diff --git a/Dockerfile b/Dockerfile index ad6f018..769e8c6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,6 +4,7 @@ LABEL maintainer="Dan Muckerman " WORKDIR /project ADD . /project +RUN rm /project/.envrc RUN rm -rf /project/env RUN pip install -r requirements.txt diff --git a/app.py b/app.py index cb8eaa2..d38a0bc 100644 --- a/app.py +++ b/app.py @@ -1,14 +1,33 @@ +import ldap as l +from ldap3 import Server, Connection, ALL, MODIFY_REPLACE from flask import Flask, g, request, session, redirect, url_for, render_template -from flask_bootstrap import Bootstrap +from flask_bs4 import Bootstrap +from flask_simpleldap import LDAP import yaml import datetime as dt import pytz +import os +import sqlite3 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) + eastern = pytz.timezone('US/Eastern') with open('config/config.yaml') as f: @@ -18,24 +37,40 @@ search = yaml_data['search'] account_url = yaml_data['accounts']['account_url'] description = yaml_data['description'] +game_description = yaml_data['game_description'] countdown_data = None if yaml_data['countdown']['active'] == True: countdown_data = yaml_data['countdown'] - print(countdown_data) final_countdown_data = None final_time = None if yaml_data['final_countdown']['active'] == True: final_countdown_data = yaml_data['final_countdown'] final_time = eastern.localize(dt.datetime.strptime(final_countdown_data['timestamp'], '%B %d %Y %H:%M:%S%z').replace(tzinfo=None)) - print(final_countdown_data) apps = [] for itm in yaml_data['apps'].items(): apps.append(itm[1]) +games = [] +for itm in yaml_data['games'].items(): + games.append(itm[1]) + + +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('/') def index(): current_time = eastern.localize(dt.datetime.now()) @@ -47,5 +82,104 @@ def index(): return render_template('index.j2', apps = apps, search = search, account_url = account_url, description = description) +@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'), + } + + current_time = eastern.localize(dt.datetime.now()) + if final_countdown_data != None: + if (final_time - current_time).days > -1: + return render_template('final_countdown.j2', final_countdown = final_countdown_data) + if countdown_data != None: + 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()) + return render_template('games.j2', apps = games, search = search, account_url = account_url, description = game_description) + + +@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('/games') + return render_template('login.j2') + + +@ldap.login_required +@app.route('/add', methods=['POST']) +def add_game(): + if request.method == 'POST': + game_title = request.form['game_title'] + game_link = request.form['game_link'] + conn = sqlite3.connect('config/games_in_progress.db') + c = conn.cursor() + + if game_title is not None and len(game_title) > 0 and game_link is not None and len(game_link) > 0: + c.execute("INSERT INTO games (user_id, game_title, game_link) VALUES (?, ?, ?)", (session['user_id'], game_title, game_link,)) + conn.commit() + conn.close() + + return 'Success' + conn.commit() + conn.close() + return 'Error' + + +@ldap.login_required +@app.route('/delete', methods=['POST']) +def delete_game(): + if request.method == 'POST': + game_id = request.form['game_id'] + conn = sqlite3.connect('config/games_in_progress.db') + c = conn.cursor() + + if game_id is not None and len(game_id) > 0: + c.execute("DELETE FROM games WHERE id=? AND user_id=?", (game_id, session['user_id'],)) + conn.commit() + conn.close() + + return 'Success' + conn.commit() + conn.close() + return 'Error' + + +def generate_game_list(): + conn = sqlite3.connect('config/games_in_progress.db') + c = conn.cursor() + + if 'user_id' in session: + c.execute('SELECT * FROM games WHERE user_id=?', (session['user_id'], )) + rows = c.fetchall() + conn.close() + + return rows + + conn.close() + return [] + + +@app.route('/logout') +def logout(): + session.pop('user_id', None) + return redirect(url_for('game')) + + if __name__ == '__main__': app.run(extra_files="config/config.yaml") diff --git a/config/config.yaml b/config/config.yaml index 8e30841..6b0e092 100644 --- a/config/config.yaml +++ b/config/config.yaml @@ -6,6 +6,7 @@ accounts: account_url: 'https://account.technicalincompetence.club' description: "It turns out Technical Incompetence's amazingness is just too great for this lame internet - we need a new one to maximize our full potential. Welcome to the Technical Incompetence internet. Population - US." +game_description: "We're all gamers at heart, so welcome to the Valhalla of self hosted gaming. You name it, we have it. Unless we don't. We're gamers, not wizards." countdown: active: False @@ -22,6 +23,11 @@ apps: description: "
The moment you're all been waiting for... Zulip, our chat lord and savior is finally here. Third only to iMessage and AOL - prepare for a superior chatting experience. The move from Slack has never been so sweet. Trust us... you won't be disappointed. There's even a mobile app!" link: "https://zillow.technicalincompetence.club" image: "zulip.png" + rocketchat: + name: "Rocketchat" + description: "
Stay in touch with your friends, family, and people you've never even met with Technical Incompetence's amazing chat service. Experience the joy's of late-1990s AIM all over again." + link: "https://rocketchat.technicalincompetence.club" + image: "rocketchat.png" cloud: name: "Nextcloud" description: "
What would Technical Incompetence be without the ability to give up ownership of all your data and store it on our servers. Don't worry though, our servers will probably never crash or lose data so all you care about is safe with us." @@ -35,7 +41,7 @@ apps: jellyfin: name: "Jellyfin" description: "
Angry about the StReAmInG WaRs? Well our Jellyfin instance has you covered. Listen to all your embarassing weeb music, or watch your barely-not-porn weeb shows and movies. We're not judging you." - link: "http://octotep.com:8096" + link: "https://jellyfin.octotep.com" image: "jellyfin.png" rss: name: "FreshRSS" @@ -47,4 +53,25 @@ apps: description: "
Have you ever felt self conscious about the links you send to people? We feel you. That's why we're proud to introduce our brand new link shortening service! For all those rickrolls you're still sending in current year." link: "https://links.technicalincompetence.club" image: "links.png" + wiki: + name: "Bookstack" + description: "
We did it, we finally got a wiki. All of our history is held here, both recorded and not recorded history, the travels of the drewniverse, and a full explanation of the drewian calendar." + link: "https://wiki.technicalincompetence.club" + image: "wiki.png" +games: + gameboy: + name: "NewGamePlus" + description: "
Too many games, not enough time to play them? Don't worry, we're also slowly sinking into the existential tar pit that is realizing the things you once loved to do fit less and less into your adult life even though they are the very things that hold back the tide of weapons grade ennui that threatens to consume your every waking moment in a boiling pot that will eventually one day bubble over into an anomic explosion of grief, pain, and depression. So that's why we made it easy to keep track of your backlog, checkout NewGamePlus today!" + link: "https://technicalincompetence.games" + image: "gameboy.png" + terraformingmars: + name: "Terraforming Mars" + description: "
Experience the glory of \"cooperative\" gameplay through corporate simulation!" + link: "https://terraforming-mars.flamwenco.com" + image: "mars.png" + minecraft: + name: "Minecraft" + description: "
Remember playing Minecraft in high school? Or that one month on PS4? Or that one Bedrock server we used for a month?

Me neither, but join us in Java Edition at: mc.flamwenco.com" + link: "#" + image: "minecraft.png" \ No newline at end of file diff --git a/config/games_in_progress.db b/config/games_in_progress.db new file mode 100644 index 0000000..9e45393 Binary files /dev/null and b/config/games_in_progress.db differ diff --git a/requirements.txt b/requirements.txt index e60fcb6..d610db7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,8 @@ Flask==1.1.2 -Flask-Bootstrap4==4.0.2 +Flask-BS4==4.5.2.0 PyYAML==5.3.1 -pytz \ No newline at end of file +pytz +Flask-Login==0.5.0 +Flask-SimpleLDAP==1.4.0 +python-ldap==3.2.0 +ldap3==2.7 \ No newline at end of file diff --git a/static/favicon.png b/static/favicon.png new file mode 100644 index 0000000..a32cf54 Binary files /dev/null and b/static/favicon.png differ diff --git a/static/images/gameboy.png b/static/images/gameboy.png new file mode 100644 index 0000000..891c006 Binary files /dev/null and b/static/images/gameboy.png differ diff --git a/static/images/mars.png b/static/images/mars.png new file mode 100644 index 0000000..aaf0a20 Binary files /dev/null and b/static/images/mars.png differ diff --git a/static/images/minecraft.png b/static/images/minecraft.png new file mode 100644 index 0000000..a95b435 Binary files /dev/null and b/static/images/minecraft.png differ diff --git a/static/style.css b/static/style.css index 953cc38..b15e3bf 100644 --- a/static/style.css +++ b/static/style.css @@ -13,6 +13,24 @@ background-color: #f8f9fa!important; } +#game-table { + border-radius: .25rem !important; + border-collapse: separate !important; + border-spacing: 0 !important; + border: 1px solid rgba(0,0,0,.125) !important; +} + +#game-table thead { + color: #495057; + background-color: #e9ecef; + border-color: #dee2e6; +} + +.game-link { + color: #000; +} + + @media (prefers-color-scheme: dark) { body { background-color: #111 !important; @@ -79,6 +97,25 @@ color: white !important; } + #game-table { + border-radius: 5px !important; + border-collapse: separate !important; + border-spacing: 0 !important; + background-color: #000 !important; + border: 1px solid #6c757d !important; + color: #fff !important; + } + + #game-table thead { + color: #fff; + background-color: #343a40; + border-color: #454d55; + } + + .game-link { + color: #fff; + } + @media (prefers-reduced-motion: reduce) { .form-control { transition: none; diff --git a/templates/card_list.j2 b/templates/card_list.j2 new file mode 100644 index 0000000..40adb4f --- /dev/null +++ b/templates/card_list.j2 @@ -0,0 +1,36 @@ +
+
+
+
Games In Progress
+ {% if user %} + + + + + + + + + {% if game_list is defined and account_url != [] %} + {% for game in game_list %} + + + + + {% endfor %} + {% endif %} + +
GameAdd
{{ game[2] }}Delete
+
+ Log out +

+
+ {% else %} +
+ Log in +

+
+ {% endif %} +
+
+
diff --git a/templates/games.j2 b/templates/games.j2 new file mode 100644 index 0000000..7282ded --- /dev/null +++ b/templates/games.j2 @@ -0,0 +1,150 @@ +{% extends "bootstrap/base.html" %} +{% block title %}Technical Incompetence - Home{% endblock %} + +{% block styles %} +{{super()}} + + +{% if countdown is defined %} + +{% endif %} +{% endblock %} + +{% block navbar %} + +{% endblock %} + +{% block content %} +
+ {% if search['active'] == True %} +
+ +
+

+ {% endif %} + {% if countdown is defined %} + {% include "countdown.j2" %} + {% endif %} +

{{ description }}

+
+ {% for app in apps %} + {% include "card.j2" %} + {% endfor %} +
+
+ {% include "card_list.j2" %} +
+
+
+ {% include "new_game_modal.j2" %} +{% endblock %} + +{% block scripts %} +{{ super () }} +{% if countdown is defined %} + + +{% endif %} + +{% endblock %} \ No newline at end of file diff --git a/templates/index.j2 b/templates/index.j2 index 209c6cc..df51cbe 100644 --- a/templates/index.j2 +++ b/templates/index.j2 @@ -3,6 +3,7 @@ {% block styles %} {{super()}} + {% if countdown is defined %} @@ -12,11 +13,25 @@ {% block navbar %} {% endblock %} diff --git a/templates/login.j2 b/templates/login.j2 new file mode 100644 index 0000000..d4935b3 --- /dev/null +++ b/templates/login.j2 @@ -0,0 +1,37 @@ +{% extends "bootstrap/base.html" %} +{% block title %}Manage your Technical Incompetence account{% 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 diff --git a/templates/new_game_modal.j2 b/templates/new_game_modal.j2 new file mode 100644 index 0000000..f074709 --- /dev/null +++ b/templates/new_game_modal.j2 @@ -0,0 +1,28 @@ +