Browse Source

Attempting to create a working docker deploy

master
Drew Short 5 years ago
parent
commit
08ef5005d4
  1. 11
      .dockerignore
  2. 7
      .gitignore
  3. 18
      Dockerfile
  4. 83
      app.py
  5. 6
      db.py
  6. 14
      docker-compose.yml
  7. 7
      example.env
  8. 44
      register_new_matrix_user.py
  9. 3
      templates/base.html

11
.dockerignore

@ -0,0 +1,11 @@
*.env
*.conf
.git
.gitignore
*.db
Dockerfile
docker-compose.yml
tmp

7
.gitignore

@ -1,3 +1,6 @@
.idea
__pycache__
tmp
*.db
.idea
*.db
.env

18
Dockerfile

@ -0,0 +1,18 @@
FROM python:3.7
MAINTAINER Drew Short <warrick@sothr.com>
RUN mkdir /srv/portal /srv/portal/data
ENV APP_DATA_DIRECTORY=/srv/portal/data
VOLUME /srv/portal/data
WORKDIR /srv/portal
CMD ["gunicorn", "-b", "0.0.0.0:8080", "app:app"]
COPY . /srv/portal
RUN cd /srv/portal \
&& pip install -r requirements.txt \
&& pip install gunicorn

83
app.py

@ -1,12 +1,12 @@
import logging import logging
import os import os
import uuid import uuid
from logging.config import dictConfig
from urllib.parse import urlparse, urljoin from urllib.parse import urlparse, urljoin
import flask import flask
from flask import Flask, redirect, render_template, request, g, flash
from flask_login import LoginManager, login_required, login_user, logout_user, UserMixin, \
current_user
from flask import Flask, redirect, render_template, request, g, flash, url_for
from flask_login import LoginManager, login_required, login_user, logout_user, UserMixin
from flask_wtf import CSRFProtect from flask_wtf import CSRFProtect
from db import get_db, get_registration_codes, add_registration_code, \ from db import get_db, get_registration_codes, add_registration_code, \
@ -14,15 +14,36 @@ from db import get_db, get_registration_codes, add_registration_code, \
add_registered_user add_registered_user
from forms import RegistrationForm, LoginForm, RegistrationCodeForm, \ from forms import RegistrationForm, LoginForm, RegistrationCodeForm, \
ExpireRegistrationCodeForm ExpireRegistrationCodeForm
from register_new_matrix_user import register_new_user
csrf = CSRFProtect() csrf = CSRFProtect()
login_manager = LoginManager() login_manager = LoginManager()
dictConfig({
'version': 1,
'disable_existing_loggers': False,
'formatters': {'default': {
'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s',
}},
'handlers': {
'wsgi': {
'class': 'logging.StreamHandler',
'stream': 'ext://flask.logging.wsgi_errors_stream',
'formatter': 'default'
}
},
'root': {
'level': 'INFO',
'handlers': ['wsgi']
}
})
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def init_db(flask_app): def init_db(flask_app):
with flask_app.app_context(): with flask_app.app_context():
log.info("Initializing DB")
db = get_db() db = get_db()
with flask_app.open_resource('schema.sql', mode='r') as f: with flask_app.open_resource('schema.sql', mode='r') as f:
db.cursor().executescript(f.read()) db.cursor().executescript(f.read())
@ -34,12 +55,16 @@ def create_app():
flask_app = Flask(__name__) flask_app = Flask(__name__)
flask_app.config.update(dict( flask_app.config.update(dict(
APPLICATION_ROOT=os.getenv("APPLICATION_ROOT", "/"),
ADMIN_TOKEN=os.getenv("ADMIN_TOKEN", uuid.uuid4().__str__()), ADMIN_TOKEN=os.getenv("ADMIN_TOKEN", uuid.uuid4().__str__()),
SECRET_KEY=os.getenv("SECRET_KEY", "changeme"), SECRET_KEY=os.getenv("SECRET_KEY", "changeme"),
WTF_CSRF_SECRET_KEY=os.getenv("CSRF_SECRET_KEY", "csrf_changeme")
WTF_CSRF_SECRET_KEY=os.getenv("CSRF_SECRET_KEY", "csrf_changeme"),
MATRIX_HOMESERVER=os.getenv("MATRIX_HOMESERVER"),
MATRIX_SHARED_SECRET=os.getenv("MATRIX_SHARED_SECRET"),
REGISTRATION_SUCCESS_REDIRECT=os.getenv("REGISTRATION_SUCCESS_REDIRECT")
)) ))
print("Admin Token: %s" % flask_app.config.get("ADMIN_TOKEN"))
log.info("Admin Token: %s" % flask_app.config.get("ADMIN_TOKEN"))
csrf.init_app(flask_app) csrf.init_app(flask_app)
@ -48,11 +73,15 @@ def create_app():
init_db(flask_app) init_db(flask_app)
log.info("Application ready")
return flask_app return flask_app
app = create_app() app = create_app()
log.info("Bound reverse proxy wsgi app")
def flash_form_errors(form): def flash_form_errors(form):
if hasattr(form, 'errors') and len(form.errors) > 0: if hasattr(form, 'errors') and len(form.errors) > 0:
@ -82,6 +111,13 @@ def is_safe_url(target):
return test_url.scheme in ('http', 'https') and ref_url.netloc == test_url.netloc return test_url.scheme in ('http', 'https') and ref_url.netloc == test_url.netloc
def get_successful_registration_redirect():
target = app.config.get('REGISTRATION_SUCCESS_REDIRECT')
if target is None or not target.startswith('http'):
return url_for('index', _external=True)
return target
@login_manager.user_loader @login_manager.user_loader
def load_user(user_id): def load_user(user_id):
if user_id == "admin": if user_id == "admin":
@ -94,15 +130,33 @@ def load_user(user_id):
@app.route('/') @app.route('/')
def index(): def index():
return redirect('/register')
return redirect(url_for('registration'))
@app.route('/register', methods=('GET', 'POST')) @app.route('/register', methods=('GET', 'POST'))
def registration(): def registration():
form = RegistrationForm() form = RegistrationForm()
if form.validate_on_submit(): if form.validate_on_submit():
add_registered_user(form.registration_code.data, form.username.data)
return redirect('/success')
if app.config.get("MATRIX_HOMESERVER") is None:
flash("Matrix Homeserver Currently Unavailable. Please Try Again Later!")
return render_template('register.html', form=form)
else:
if app.config.get("MATRIX_SHARED_SECRET") is None:
flash("Registration Configuration Is Invalid. Contact Administrator!")
return render_template('register.html', form=form)
else:
successful = register_new_user(
form.username.data,
form.password.data,
app.config.get("MATRIX_HOMESERVER"),
app.config.get("MATRIX_SHARED_SECRET")
)
if successful:
add_registered_user(form.registration_code.data, form.username.data)
return redirect(get_successful_registration_redirect())
else:
flash("Registration Failure. Contact Administrator!")
return render_template('register.html', form=form)
flash_form_errors(form) flash_form_errors(form)
@ -131,11 +185,11 @@ def admin_add_registration_code():
expiration_time = form.expiration_time.data expiration_time = form.expiration_time.data
max_usages = form.max_usages.data max_usages = form.max_usages.data
add_registration_code(expiration_time, max_usages) add_registration_code(expiration_time, max_usages)
redirect('/admin')
redirect(url_for('admin_index', _external=True))
flash_form_errors(form) flash_form_errors(form)
return redirect('/admin')
return redirect(url_for('admin_index', _external=True))
@app.route('/admin/expire_registration_code', methods=['POST']) @app.route('/admin/expire_registration_code', methods=['POST'])
@ -147,17 +201,16 @@ def admin_expire_registration_code():
expire_registration_code(form.registration_code.data) expire_registration_code(form.registration_code.data)
elif form.delete.data: elif form.delete.data:
delete_registration_code(form.registration_code.data) delete_registration_code(form.registration_code.data)
redirect('/admin')
redirect(url_for('admin_index', _external=True))
flash_form_errors(form) flash_form_errors(form)
return redirect('/admin')
return redirect(url_for('admin_index', _external=True))
@app.route('/admin/login', methods=('GET', 'POST')) @app.route('/admin/login', methods=('GET', 'POST'))
def admin_login(): def admin_login():
form = LoginForm() form = LoginForm()
tmp = current_user
if form.validate_on_submit(): if form.validate_on_submit():
user = load_user(form.username.data) user = load_user(form.username.data)
if user is not None: if user is not None:
@ -172,7 +225,7 @@ def admin_login():
if next_loc is not None: if next_loc is not None:
return redirect(next_loc) return redirect(next_loc)
else: else:
return redirect('/admin')
return redirect(url_for('admin_index', _external=True))
flash_form_errors(form) flash_form_errors(form)
@ -184,7 +237,7 @@ def admin_login():
def admin_logout(): def admin_logout():
logout_user() logout_user()
flask.flash('Logged out successfully.') flask.flash('Logged out successfully.')
return redirect('/')
return redirect(url_for('index', _external=True))
@app.teardown_appcontext @app.teardown_appcontext

6
db.py

@ -1,3 +1,4 @@
import logging
import os import os
import sqlite3 import sqlite3
import uuid import uuid
@ -8,7 +9,7 @@ from flask import g
from models import RegistrationCode, RegisteredUser from models import RegistrationCode, RegisteredUser
DATABASE = os.getenv("DATA_DIRECTORY", ".") + "/data.db"
DATABASE = os.getenv("APP_DATA_DIRECTORY", ".") + "/data.db"
REGISTRATION_CODE_INSERT_SQL = """INSERT INTO REGISTRATION_CODE_INSERT_SQL = """INSERT INTO
@ -24,10 +25,13 @@ registered_users(registrationCode, username, registeredTime)
VALUES(?, ?, ?) VALUES(?, ?, ?)
""" """
log = logging.getLogger(__name__)
def get_db(): def get_db():
db = getattr(g, '_database', None) db = getattr(g, '_database', None)
if db is None: if db is None:
log.info("Using database at: %s" % DATABASE)
db = g._database = sqlite3.connect(DATABASE) db = g._database = sqlite3.connect(DATABASE)
return db return db

14
docker-compose.yml

@ -0,0 +1,14 @@
version: "2"
services:
portal:
build: .
environment:
- ADMIN_TOKEN
- SECRET_KEY
- WTF_CSRF_SECRET_KEY
- MATRIX_HOMESERVER
- MATRIX_SHARED_SECRET
- DATA_DIRECTORY=/srv/portal/data
volumes:
- ${DATA_DIR}:/srv/portal/data:Z

7
example.env

@ -0,0 +1,7 @@
ADMIN_TOKEN=changeme
SECRET_KEY=changeme_too
WTF_CSRF_SECRET_KEY=csrf_changeme_also
MATRIX_HOMESERVER=your_matrix_homeserver
MATRIX_SHARED_SECRET=the_shared_registration_secret
DATA_DIRECTORY=/srv/portal/data
REGISTRATION_SUCCESS_REDIRECT=/success

44
register_new_matrix_user.py

@ -24,11 +24,14 @@
import hashlib import hashlib
import hmac import hmac
import sys
import logging
import requests as _requests import requests as _requests
log = logging.getLogger(__name__)
def request_registration( def request_registration(
user, user,
password, password,
@ -36,9 +39,7 @@ def request_registration(
shared_secret, shared_secret,
admin=False, admin=False,
user_type=None, user_type=None,
requests=_requests,
_print=print,
exit=sys.exit,
requests=_requests
): ):
url = "%s/_matrix/client/r0/admin/register" % (server_location,) url = "%s/_matrix/client/r0/admin/register" % (server_location,)
@ -47,13 +48,13 @@ def request_registration(
r = requests.get(url, verify=False) r = requests.get(url, verify=False)
if r.status_code is not 200: if r.status_code is not 200:
_print("ERROR! Received %d %s" % (r.status_code, r.reason))
log.error("ERROR! Received %d %s" % (r.status_code, r.reason))
if 400 <= r.status_code < 500: if 400 <= r.status_code < 500:
try: try:
_print(r.json()["error"])
except Exception:
pass
return exit(1)
log.error(r.json()["error"])
except Exception as e:
log.error(e)
return False
nonce = r.json()["nonce"] nonce = r.json()["nonce"]
@ -81,28 +82,19 @@ def request_registration(
"user_type": user_type, "user_type": user_type,
} }
_print("Sending registration request...")
log.debug("Sending registration request...")
r = requests.post(url, json=data, verify=False) r = requests.post(url, json=data, verify=False)
if r.status_code is not 200: if r.status_code is not 200:
_print("ERROR! Received %d %s" % (r.status_code, r.reason))
log.error("ERROR! Received %d %s" % (r.status_code, r.reason))
if 400 <= r.status_code < 500: if 400 <= r.status_code < 500:
try: try:
_print(r.json()["error"])
except Exception:
pass
return exit(1)
_print("Success!")
log.error(r.json()["error"])
except Exception as e:
log.error(e)
return False
return True
def register_new_user(user, password, server_location, shared_secret): def register_new_user(user, password, server_location, shared_secret):
if not user:
print("Invalid user name")
sys.exit(1)
if not password:
print("Invalid user name")
sys.exit(1)
request_registration(user, password, server_location, shared_secret, False, None)
return request_registration(user, password, server_location, shared_secret, False, None)

3
templates/base.html

@ -6,8 +6,9 @@
<ul> <ul>
{% if g.user %} {% if g.user %}
<li><span>{{ g.user['username'] }}</span> <li><span>{{ g.user['username'] }}</span>
<li><a href="{{ url_for('admin_index') }}">Admin Panel</a>
<li><a href="{{ url_for('admin_logout') }}">Log Out</a> <li><a href="{{ url_for('admin_logout') }}">Log Out</a>
{% else %}
{% else %}
<li><a href="{{ url_for('registration') }}">Register</a> <li><a href="{{ url_for('registration') }}">Register</a>
<li><a href="{{ url_for('admin_login') }}">Log In</a> <li><a href="{{ url_for('admin_login') }}">Log In</a>
{% endif %} {% endif %}

Loading…
Cancel
Save