Drew Short
6 years ago
commit
d70c8b30e6
11 changed files with 332 additions and 0 deletions
-
1.gitignore
-
111app.py
-
20db.py
-
14forms.py
-
108register_new_matrix_user.py
-
4requirements.txt
-
9templates/admin.html
-
24templates/base.html
-
14templates/login.html
-
15templates/register.html
-
12todo.md
@ -0,0 +1 @@ |
|||||
|
.idea |
@ -0,0 +1,111 @@ |
|||||
|
import os |
||||
|
import uuid |
||||
|
from urllib.parse import urlparse, urljoin |
||||
|
|
||||
|
import flask |
||||
|
from flask import Flask, redirect, render_template, request |
||||
|
from flask_login import LoginManager, login_required, login_user, logout_user, UserMixin, \ |
||||
|
current_user |
||||
|
from flask_wtf import CSRFProtect |
||||
|
|
||||
|
from forms import RegistrationForm, LoginForm |
||||
|
|
||||
|
app = Flask(__name__) |
||||
|
|
||||
|
app.config.update(dict( |
||||
|
ADMIN_TOKEN=os.getenv("ADMIN_TOKEN", uuid.uuid4().__str__()), |
||||
|
SECRET_KEY=os.getenv("SECRET_KEY", "changeme"), |
||||
|
WTF_CSRF_SECRET_KEY=os.getenv("CSRF_SECRET_KEY", "csrf_changeme") |
||||
|
)) |
||||
|
|
||||
|
print("Admin Token: %s" % app.config.get("ADMIN_TOKEN")) |
||||
|
|
||||
|
csrf = CSRFProtect(app) |
||||
|
|
||||
|
login_manager = LoginManager() |
||||
|
login_manager.init_app(app) |
||||
|
login_manager.login_view = "admin_login" |
||||
|
|
||||
|
|
||||
|
class User(UserMixin): |
||||
|
username: str |
||||
|
token: str |
||||
|
authenticated: bool = False |
||||
|
|
||||
|
def __init__(self, username: str, token: str): |
||||
|
self.username = username |
||||
|
self.token = token |
||||
|
|
||||
|
def is_authenticated(self): |
||||
|
return self.authenticated |
||||
|
|
||||
|
def get_id(self): |
||||
|
return self.username |
||||
|
|
||||
|
|
||||
|
def is_safe_url(target): |
||||
|
ref_url = urlparse(request.host_url) |
||||
|
test_url = urlparse(urljoin(request.host_url, target)) |
||||
|
return test_url.scheme in ('http', 'https') and ref_url.netloc == test_url.netloc |
||||
|
|
||||
|
|
||||
|
@login_manager.user_loader |
||||
|
def load_user(user_id): |
||||
|
if user_id == "admin": |
||||
|
return User("admin", app.config.get("ADMIN_TOKEN")) |
||||
|
else: |
||||
|
return None |
||||
|
|
||||
|
|
||||
|
@app.route('/') |
||||
|
def index(): |
||||
|
return 'Hello World!' |
||||
|
|
||||
|
|
||||
|
@app.route('/register', methods=('GET', 'POST')) |
||||
|
def registration(): |
||||
|
form = RegistrationForm() |
||||
|
if form.validate_on_submit(): |
||||
|
return redirect('/success') |
||||
|
return render_template('register.html', form=form) |
||||
|
|
||||
|
|
||||
|
@app.route('/admin') |
||||
|
@login_required |
||||
|
def admin_index(): |
||||
|
return render_template('admin.html') |
||||
|
|
||||
|
|
||||
|
@app.route('/admin/login', methods=('GET', 'POST')) |
||||
|
def admin_login(): |
||||
|
form = LoginForm() |
||||
|
tmp = current_user |
||||
|
if form.validate_on_submit(): |
||||
|
user = load_user(form.username.data) |
||||
|
if user is not None: |
||||
|
if form.token.data == user.token: |
||||
|
user.authenticated = True |
||||
|
login_user(user) |
||||
|
flask.flash('Logged in successfully.') |
||||
|
next_loc = flask.request.args.get('next') |
||||
|
if not is_safe_url(next_loc): |
||||
|
return flask.abort(400) |
||||
|
else: |
||||
|
if next_loc is not None: |
||||
|
return redirect(next_loc) |
||||
|
else: |
||||
|
return redirect('/admin') |
||||
|
|
||||
|
return render_template('login.html', form=form) |
||||
|
|
||||
|
|
||||
|
@app.route("/admin/logout") |
||||
|
@login_required |
||||
|
def admin_logout(): |
||||
|
logout_user() |
||||
|
flask.flash('Logged out successfully.') |
||||
|
return redirect('/') |
||||
|
|
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
app.run() |
@ -0,0 +1,20 @@ |
|||||
|
import os |
||||
|
import sqlite3 |
||||
|
|
||||
|
from flask import g |
||||
|
|
||||
|
DATABASE = os.getenv("DATA_DIRECTORY", ".") + "/data.db" |
||||
|
|
||||
|
|
||||
|
def get_db(): |
||||
|
db = getattr(g, '_database', None) |
||||
|
if db is None: |
||||
|
db = g._database = sqlite3.connect(DATABASE) |
||||
|
return db |
||||
|
|
||||
|
|
||||
|
@app.teardown_appcontext |
||||
|
def close_connection(exception): |
||||
|
db = getattr(g, '_database', None) |
||||
|
if db is not None: |
||||
|
db.close() |
@ -0,0 +1,14 @@ |
|||||
|
from flask_wtf import FlaskForm |
||||
|
from wtforms import StringField, PasswordField |
||||
|
from wtforms.validators import DataRequired |
||||
|
|
||||
|
|
||||
|
class RegistrationForm(FlaskForm): |
||||
|
username = StringField('Username', validators=[DataRequired()]) |
||||
|
password = PasswordField('Password', validators=[DataRequired()]) |
||||
|
registration_code = StringField('Registration Code', validators=[DataRequired()]) |
||||
|
|
||||
|
|
||||
|
class LoginForm(FlaskForm): |
||||
|
username = StringField('Username', validators=[DataRequired()]) |
||||
|
token = PasswordField('Token', validators=[DataRequired()]) |
@ -0,0 +1,108 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
# Copyright 2015, 2016 OpenMarket Ltd |
||||
|
# Copyright 2018 New Vector |
||||
|
# |
||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
# you may not use this file except in compliance with the License. |
||||
|
# You may obtain a copy of the License at |
||||
|
# |
||||
|
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
# |
||||
|
# Unless required by applicable law or agreed to in writing, software |
||||
|
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
# See the License for the specific language governing permissions and |
||||
|
# limitations under the License. |
||||
|
|
||||
|
# |
||||
|
# This script has been simplified and adapted from |
||||
|
# https://raw.githubusercontent.com/matrix-org/synapse/master/synapse/_scripts/register_new_matrix_user.py |
||||
|
# |
||||
|
# The purpose is to facilitate registration using a shared_secret over the open |
||||
|
# registration that is supported in the matrix spec |
||||
|
# |
||||
|
|
||||
|
import hashlib |
||||
|
import hmac |
||||
|
import sys |
||||
|
|
||||
|
import requests as _requests |
||||
|
|
||||
|
|
||||
|
def request_registration( |
||||
|
user, |
||||
|
password, |
||||
|
server_location, |
||||
|
shared_secret, |
||||
|
admin=False, |
||||
|
user_type=None, |
||||
|
requests=_requests, |
||||
|
_print=print, |
||||
|
exit=sys.exit, |
||||
|
): |
||||
|
|
||||
|
url = "%s/_matrix/client/r0/admin/register" % (server_location,) |
||||
|
|
||||
|
# Get the nonce |
||||
|
r = requests.get(url, verify=False) |
||||
|
|
||||
|
if r.status_code is not 200: |
||||
|
_print("ERROR! Received %d %s" % (r.status_code, r.reason)) |
||||
|
if 400 <= r.status_code < 500: |
||||
|
try: |
||||
|
_print(r.json()["error"]) |
||||
|
except Exception: |
||||
|
pass |
||||
|
return exit(1) |
||||
|
|
||||
|
nonce = r.json()["nonce"] |
||||
|
|
||||
|
mac = hmac.new(key=shared_secret.encode('utf8'), digestmod=hashlib.sha1) |
||||
|
|
||||
|
mac.update(nonce.encode('utf8')) |
||||
|
mac.update(b"\x00") |
||||
|
mac.update(user.encode('utf8')) |
||||
|
mac.update(b"\x00") |
||||
|
mac.update(password.encode('utf8')) |
||||
|
mac.update(b"\x00") |
||||
|
mac.update(b"admin" if admin else b"notadmin") |
||||
|
if user_type: |
||||
|
mac.update(b"\x00") |
||||
|
mac.update(user_type.encode('utf8')) |
||||
|
|
||||
|
mac = mac.hexdigest() |
||||
|
|
||||
|
data = { |
||||
|
"nonce": nonce, |
||||
|
"username": user, |
||||
|
"password": password, |
||||
|
"mac": mac, |
||||
|
"admin": admin, |
||||
|
"user_type": user_type, |
||||
|
} |
||||
|
|
||||
|
_print("Sending registration request...") |
||||
|
r = requests.post(url, json=data, verify=False) |
||||
|
|
||||
|
if r.status_code is not 200: |
||||
|
_print("ERROR! Received %d %s" % (r.status_code, r.reason)) |
||||
|
if 400 <= r.status_code < 500: |
||||
|
try: |
||||
|
_print(r.json()["error"]) |
||||
|
except Exception: |
||||
|
pass |
||||
|
return exit(1) |
||||
|
|
||||
|
_print("Success!") |
||||
|
|
||||
|
|
||||
|
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) |
@ -0,0 +1,4 @@ |
|||||
|
flask==1.0.2 |
||||
|
flask-wtf==0.14 |
||||
|
flask-login==0.4.1 |
||||
|
requests==2.21.0 |
@ -0,0 +1,9 @@ |
|||||
|
{% extends 'base.html' %} |
||||
|
|
||||
|
{% block header %} |
||||
|
<h1>{% block title %}Admin{% endblock %}</h1> |
||||
|
{% endblock %} |
||||
|
|
||||
|
{% block content %} |
||||
|
<p>ADMIN</p> |
||||
|
{% endblock %} |
@ -0,0 +1,24 @@ |
|||||
|
<!doctype html> |
||||
|
<title>{% block title %}{% endblock %} - Matrix</title> |
||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> |
||||
|
{#<nav>#} |
||||
|
{# <h1>Flaskr</h1>#} |
||||
|
{# <ul>#} |
||||
|
{# {% if g.user %}#} |
||||
|
{# <li><span>{{ g.user['username'] }}</span>#} |
||||
|
{# <li><a href="{{ url_for('auth.logout') }}">Log Out</a>#} |
||||
|
{# {% else %}#} |
||||
|
{# <li><a href="{{ url_for('auth.register') }}">Register</a>#} |
||||
|
{# <li><a href="{{ url_for('auth.login') }}">Log In</a>#} |
||||
|
{# {% endif %}#} |
||||
|
{# </ul>#} |
||||
|
{#</nav>#} |
||||
|
<section class="content"> |
||||
|
<header> |
||||
|
{% block header %}{% endblock %} |
||||
|
</header> |
||||
|
{% for message in get_flashed_messages() %} |
||||
|
<div class="flash">{{ message }}</div> |
||||
|
{% endfor %} |
||||
|
{% block content %}{% endblock %} |
||||
|
</section> |
@ -0,0 +1,14 @@ |
|||||
|
{% extends 'base.html' %} |
||||
|
|
||||
|
{% block header %} |
||||
|
<h1>{% block title %}Login{% endblock %}</h1> |
||||
|
{% endblock %} |
||||
|
|
||||
|
{% block content %} |
||||
|
<form method="POST"> |
||||
|
{{ form.csrf_token }} |
||||
|
{{ form.username.label }} {{ form.username(size=20) }} |
||||
|
{{ form.token.label }} {{ form.token() }} |
||||
|
<input type="submit" value="Go"> |
||||
|
</form> |
||||
|
{% endblock %} |
@ -0,0 +1,15 @@ |
|||||
|
{% extends 'base.html' %} |
||||
|
|
||||
|
{% block header %} |
||||
|
<h1>{% block title %}Register{% endblock %}</h1> |
||||
|
{% endblock %} |
||||
|
|
||||
|
{% block content %} |
||||
|
<form method="POST"> |
||||
|
{{ form.csrf_token }} |
||||
|
{{ form.username.label }} {{ form.username(size=20) }} |
||||
|
{{ form.password.label }} {{ form.password() }} |
||||
|
{{ form.registration_code.label }} {{ form.registration_code() }} |
||||
|
<input type="submit" value="Go"> |
||||
|
</form> |
||||
|
{% endblock %} |
@ -0,0 +1,12 @@ |
|||||
|
# TODO |
||||
|
* Admin endpoint |
||||
|
* List existing registration codes |
||||
|
* Create new registration codes |
||||
|
* Deactivate registration codes |
||||
|
* View registrations |
||||
|
* Registration Form |
||||
|
* Fields: |
||||
|
* Desired username |
||||
|
* Password |
||||
|
* Registration Code |
||||
|
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue