11 Commits

16 changed files with 141 additions and 88 deletions

View File

@@ -1,9 +1,7 @@
from flask import Flask, render_template from flask import Flask, render_template, g
from werkzeug.exceptions import HTTPException from werkzeug.exceptions import HTTPException
from flask_mobility import Mobility from flask_mobility import Mobility
from flask_pyoidc.provider_configuration import ProviderConfiguration, ClientMetadata
from . import filters from . import filters
from .lib import OIDCAuthentication from .lib import OIDCAuthentication
@@ -53,9 +51,12 @@ def create_app(environment='development'):
filters.init_app(app) filters.init_app(app)
# Error handlers. # Error handlers.
@app.errorhandler(HTTPException) @app.errorhandler(HTTPException)
def handle_http_error(exc): def handle_http_error(exc):
return render_template('error.html', error=exc), exc.code return render_template('error.html', error=exc), exc.code
@app.context_processor
def inject_auth():
return dict(auth=auth)
return app return app

View File

@@ -56,11 +56,26 @@ class OIDCAuthentication(_OIDCAuth):
userinfo = flask_session['userinfo'] userinfo = flask_session['userinfo']
return userinfo['email'].split('@')[0] return userinfo['email'].split('@')[0]
@property
def email(self) -> str:
userinfo = flask_session['userinfo']
return userinfo['email']
@property @property
def login_name(self) -> str: def login_name(self) -> str:
userinfo = flask_session['userinfo'] userinfo = flask_session['userinfo']
return userinfo.get('preferred_username', self.username) return userinfo.get('preferred_username', self.username)
@property
def full_name(self) -> str:
userinfo = flask_session['userinfo']
return userinfo.get('name')
@property
def groups(self) -> list:
userinfo = flask_session['userinfo']
return userinfo.get('groups')
@property @property
def isAdmin(self) -> bool: def isAdmin(self) -> bool:
userinfo = flask_session['userinfo'] userinfo = flask_session['userinfo']
@@ -73,7 +88,7 @@ class OIDCAuthentication(_OIDCAuth):
if len(authorized_groups): if len(authorized_groups):
log.debug(f"'{self.username}' is a member of { log.debug(f"'{self.username}' is a member of {
authorized_groups}") authorized_groups}. isAdmin == True")
return True return True
if self.username in admin_users: if self.username in admin_users:

View File

View File

@@ -14,12 +14,10 @@ function renameNode(nodeId) {
} }
function createPKA(username) { function createPKA(username) {
console.log(username);
var url = `${username}/pakcreate`; var url = `${username}/pakcreate`;
var ephemereal = $("#ephemereal").is(":checked"); var ephemereal = $("#ephemereal").is(":checked");
var reusable = $("#reusable").is(":checked"); var reusable = $("#reusable").is(":checked");
var expiration = $("#expiration").val(); var expiration = $("#expiration").val();
console.log(expiration);
$.ajax({ $.ajax({
url: url, url: url,
method: "POST", method: "POST",
@@ -65,3 +63,29 @@ function toggleExpired(obj) {
$(".pka-expired").addClass("pka-hide"); $(".pka-expired").addClass("pka-hide");
} }
} }
function backfillips(obj) {
var url = "backfillips";
var button = $(obj);
var original = button.html();
$.ajax({
url: url,
method: "POST",
dataType: "json",
contentType: "application/json; charset=utf-8",
xhrFields: {
withCredentials: true,
},
data: {},
success: function (data) {
if (data.length) {
button.html("Updated");
} else {
button.html("Done");
}
setTimeout(function () {
button.html(original);
}, 500);
},
});
}

View File

@@ -50,6 +50,7 @@
<div class="collapse navbar-collapse" id="navbarNav"> <div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav mr-auto"> <ul class="navbar-nav mr-auto">
{% if auth.isAdmin %}
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{ url_for('main.nodes') }}">nodes</a> <a class="nav-link" href="{{ url_for('main.nodes') }}">nodes</a>
</li> </li>
@@ -59,6 +60,7 @@
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{ url_for('main.routes') }}">routes</a> <a class="nav-link" href="{{ url_for('main.routes') }}">routes</a>
</li> </li>
{% endif %}
</ul> </ul>
<ul class="navbar-nav"> <ul class="navbar-nav">
<li class="nav-item me-right"> <li class="nav-item me-right">

View File

@@ -1,10 +1,11 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<div class="jumbotron my-4"> <div class="jumbotron jumbotron-fluid my-4">
<div class="text-center"> <div class="text-center">
<h1>{{ '%s - %s' % (error.code, error.name) }}</h1> <h1>Oops, something went wrong</h1>
<h1>{{ '%s - %s' % (error.code, error.name) }}</h2>
<p>{{ error.description }}.</p> <p>{{ error.description }}.</p>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@@ -2,25 +2,26 @@
{% block content %} {% block content %}
<h3> <h3>
Welcome, {{ session.userinfo.name }} Welcome, {{ auth.full_name }}
</h3> </h3>
<hr> <hr>
<h4>authentication info</h4> <h4>authentication info</h4>
<div class="row data"> <div class="row data">
<div class="col col-2"> <div class="col col-2">
<strong>email</strong> <strong>username</strong>
</div> </div>
<div class="col col-6"> <div class="col col-6">
{{ session.userinfo.email }} <span data-toggle="tooltip" data-placement="right" title="OIDC username: {{ auth.login_name }}">
<!-- {{ session.userinfo.email_verified | fancyBool | safe }} --> {{ auth.username }}
</span>
</div> </div>
</div> </div>
<div class="row data"> <div class="row data">
<div class="col col-2"> <div class="col col-2">
<strong>username</strong> <strong>email</strong>
</div> </div>
<div class="col col-6"> <div class="col col-6">
{{ session.userinfo.preferred_username }} {{ auth.email }}
</div> </div>
</div> </div>
<div class="row data"> <div class="row data">
@@ -29,16 +30,16 @@
</div> </div>
<div class="col col-6"> <div class="col col-6">
<i class="fas fa-angle-right"></i> <i class="fas fa-angle-right"></i>
{% if session.userinfo.groups[0] in config['ADMIN_GROUPS'] %} {% if auth.groups[0] in config['ADMIN_GROUPS'] %}
<span class="badge badge-pill badge-warning"> <span class="badge badge-pill badge-warning">
{% else %} {% else %}
<span class="badge badge-pill badge-dark"> <span class="badge badge-pill badge-dark">
{% endif %} {% endif %}
{{ session.userinfo.groups[0]}} {{ auth.groups[0]}}
</span> </span>
</div> </div>
</div> </div>
{% for group in session.userinfo.groups[1:] |sort %} {% for group in auth.groups[1:] |sort %}
<div class="row data"> <div class="row data">
<div class="col col-2"> <div class="col col-2">
&nbsp; &nbsp;

View File

@@ -36,6 +36,9 @@
<div class="col col-8 float-left"> <div class="col col-8 float-left">
<span data-toggle="tooltip" data-placement="right" title="{{ node.createdAt | fmt_datetime }}"> <span data-toggle="tooltip" data-placement="right" title="{{ node.createdAt | fmt_datetime }}">
{{ node.createdAt | htime_dt }} {{ node.createdAt | htime_dt }}
<span class="badge badge-pill badge-warning">
{{ node.registerMethod.name }}
</span>
</span> </span>
</div> </div>
</div> </div>

View File

@@ -1,7 +1,16 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<h3>nodes</h3> <div class="row data justify-content-between">
<div class="col col-4">
<h3>nodes</h3>
</div>
<div class="col col-2">
<span data-toggle="tooltip" data-placement="right" title="Recheck all IP addresses of all nodes">
<button type="button" class="btn btn-outline-primary btn-sm" onClick="backfillips(this);">Backfill IP addresses</button>
</span>
</div>
</div>
<hr> <hr>
<p></p> <p></p>
<table id="nodes" class="display" style="width:100%"> <table id="nodes" class="display" style="width:100%">

View File

@@ -17,7 +17,7 @@
{% for user in users %} {% for user in users %}
<tr> <tr>
<td> <td>
<a class="plain" href="{{ url_for('main.user', userName=user.name)}}"> <a class="plain" href="{{ url_for('main.user', userName=user.name) }}">
{{user.name}} {{user.name}}
</a> </a>
</td> </td>
@@ -31,7 +31,7 @@
</td> </td>
<td class="no-sort"> <td class="no-sort">
<span data-toggle="tooltip" data-placement="right" title="delete"> <span data-toggle="tooltip" data-placement="right" title="delete">
<a class="nodeco" href="/user/{{user.name}}/delete"> <a class="nodeco" href="{{ url_for('rest.deleteUser', userName=user.name) }}">
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
</a> </a>
</span> </span>

View File

@@ -2,7 +2,7 @@ import logging
import datetime import datetime
import os import os
from flask import current_app from flask import current_app
from flask import render_template, Blueprint, request from flask import render_template, Blueprint
from flask import redirect, session, url_for from flask import redirect, session, url_for
from app import auth from app import auth
@@ -38,13 +38,10 @@ def token():
@main_blueprint.route('/', methods=['GET', 'POST']) @main_blueprint.route('/', methods=['GET', 'POST'])
@auth.access_control('default') @auth.access_control('default')
def index(): def index():
user_session = UserSession(session) hs_user = auth.username
hs_user = user_session.userinfo['email'].split('@')[0]
userNodeList = [n for n in Node().list().nodes if n.user.name == hs_user] userNodeList = [n for n in Node().list().nodes if n.user.name == hs_user]
return render_template('index.html', return render_template('index.html',
userNodeList=userNodeList, userNodeList=userNodeList)
session=user_session,
auth=auth)
@main_blueprint.route('/logout') @main_blueprint.route('/logout')

View File

@@ -4,18 +4,17 @@ from flask import Blueprint, request
from flask import redirect, url_for from flask import redirect, url_for
from app import auth from app import auth
# from ..lib import login_name, username
from flask import jsonify from flask import jsonify
from hsapi_client import Node, User, Route, PreAuthKey from hsapi_client import Node, User, Route, PreAuthKey
from hsapi_client.preauthkeys import (v1CreatePreAuthKeyRequest, from hsapi_client.preauthkeys import (v1CreatePreAuthKeyRequest,
v1ExpirePreAuthKeyRequest) v1ExpirePreAuthKeyRequest)
from hsapi_client.nodes import v1BackfillNodeIPsResponse
log = logging.getLogger() log = logging.getLogger()
# REST calls
# REST calls
rest_blueprint = Blueprint( rest_blueprint = Blueprint(
'rest', __name__, url_prefix=os.getenv('APPLICATION_ROOT', '/')) 'rest', __name__, url_prefix=os.getenv('APPLICATION_ROOT', '/'))
@@ -23,19 +22,16 @@ rest_blueprint = Blueprint(
@rest_blueprint.route('/routeToggle/<int:routeId>', methods=['GET']) @rest_blueprint.route('/routeToggle/<int:routeId>', methods=['GET'])
@auth.authorize_admins('default') @auth.authorize_admins('default')
def routeToggle(routeId: int): def routeToggle(routeId: int):
routes = Route().list() route = Route().get(routeId)
route = [r for r in routes.routes if r.id == routeId]
if route: if route:
route = route[0]
if route.enabled: if route.enabled:
action = 'disabled' action = 'disabled'
Route().disable(routeId)
else: else:
Route().enable(routeId)
action = 'enabled' action = 'enabled'
log.info( log.info(
f"route '{route.prefix}' via '{route.node.givenName}'" f"route '{route.prefix}' via '{route.node.givenName}' "
f"{action} by '{auth.username}'") f"{action} by '{auth.username}'")
Route().toggle(routeId)
return redirect(request.referrer) return redirect(request.referrer)
@@ -67,8 +63,11 @@ def deleteNode(nodeId: int):
@rest_blueprint.route('/node/<int:nodeId>/rename/<newName>', methods=['GET']) @rest_blueprint.route('/node/<int:nodeId>/rename/<newName>', methods=['GET'])
@auth.authorize_admins('default') @auth.access_control('default')
def renameNode(nodeId: int, newName: str): def renameNode(nodeId: int, newName: str):
node = Node().get(nodeId)
if not auth.userOrAdmin(node.user.name):
return auth.unathorized
Node().rename(nodeId, newName) Node().rename(nodeId, newName)
return jsonify(dict(newName=newName)) return jsonify(dict(newName=newName))
@@ -106,3 +105,10 @@ def expirePKA(userName: str, key: str):
PreAuthKey().expire(req) PreAuthKey().expire(req)
return redirect(url_for('main.user', userName=userName)) return redirect(url_for('main.user', userName=userName))
@rest_blueprint.route('/backfillips', methods=['POST'])
@auth.authorize_admins('default')
def backfillips():
response = Node().backfillips()
return jsonify(response.changes)

View File

@@ -11,7 +11,7 @@ preload_app = True
# logconfig = "app/logging/production.ini" # logconfig = "app/logging/production.ini"
logconfig = "app/logging/production.ini" logconfig = "app/logging/production.ini"
# access_log_format = "%(h)s %(l)s %(t)s %(r)s %(s)s %(b)s %(f)s %(a)s" access_log_format = "%(h)s %({x-forwarded-for}i)s %(t)s %(r)s %(s)s %(b)s %(L)s"
# Log to stdout. # Log to stdout.
accesslog = "-" accesslog = "-"
errorlog = "-" errorlog = "-"

87
poetry.lock generated
View File

@@ -241,43 +241,38 @@ files = [
[[package]] [[package]]
name = "cryptography" name = "cryptography"
version = "42.0.8" version = "43.0.0"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e"}, {file = "cryptography-43.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:64c3f16e2a4fc51c0d06af28441881f98c5d91009b8caaff40cf3548089e9c74"},
{file = "cryptography-42.0.8-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d"}, {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3dcdedae5c7710b9f97ac6bba7e1052b95c7083c9d0e9df96e02a1932e777895"},
{file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902"}, {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d9a1eca329405219b605fac09ecfc09ac09e595d6def650a437523fcd08dd22"},
{file = "cryptography-42.0.8-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801"}, {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ea9e57f8ea880eeea38ab5abf9fbe39f923544d7884228ec67d666abd60f5a47"},
{file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949"}, {file = "cryptography-43.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9a8d6802e0825767476f62aafed40532bd435e8a5f7d23bd8b4f5fd04cc80ecf"},
{file = "cryptography-42.0.8-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9"}, {file = "cryptography-43.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:cc70b4b581f28d0a254d006f26949245e3657d40d8857066c2ae22a61222ef55"},
{file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583"}, {file = "cryptography-43.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:4a997df8c1c2aae1e1e5ac49c2e4f610ad037fc5a3aadc7b64e39dea42249431"},
{file = "cryptography-42.0.8-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7"}, {file = "cryptography-43.0.0-cp37-abi3-win32.whl", hash = "sha256:6e2b11c55d260d03a8cf29ac9b5e0608d35f08077d8c087be96287f43af3ccdc"},
{file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b"}, {file = "cryptography-43.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:31e44a986ceccec3d0498e16f3d27b2ee5fdf69ce2ab89b52eaad1d2f33d8778"},
{file = "cryptography-42.0.8-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7"}, {file = "cryptography-43.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:7b3f5fe74a5ca32d4d0f302ffe6680fcc5c28f8ef0dc0ae8f40c0f3a1b4fca66"},
{file = "cryptography-42.0.8-cp37-abi3-win32.whl", hash = "sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2"}, {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac1955ce000cb29ab40def14fd1bbfa7af2017cca696ee696925615cafd0dce5"},
{file = "cryptography-42.0.8-cp37-abi3-win_amd64.whl", hash = "sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba"}, {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:299d3da8e00b7e2b54bb02ef58d73cd5f55fb31f33ebbf33bd00d9aa6807df7e"},
{file = "cryptography-42.0.8-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28"}, {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ee0c405832ade84d4de74b9029bedb7b31200600fa524d218fc29bfa371e97f5"},
{file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e"}, {file = "cryptography-43.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cb013933d4c127349b3948aa8aaf2f12c0353ad0eccd715ca789c8a0f671646f"},
{file = "cryptography-42.0.8-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70"}, {file = "cryptography-43.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fdcb265de28585de5b859ae13e3846a8e805268a823a12a4da2597f1f5afc9f0"},
{file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c"}, {file = "cryptography-43.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2905ccf93a8a2a416f3ec01b1a7911c3fe4073ef35640e7ee5296754e30b762b"},
{file = "cryptography-42.0.8-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7"}, {file = "cryptography-43.0.0-cp39-abi3-win32.whl", hash = "sha256:47ca71115e545954e6c1d207dd13461ab81f4eccfcb1345eac874828b5e3eaaf"},
{file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e"}, {file = "cryptography-43.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:0663585d02f76929792470451a5ba64424acc3cd5227b03921dab0e2f27b1709"},
{file = "cryptography-42.0.8-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961"}, {file = "cryptography-43.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2c6d112bf61c5ef44042c253e4859b3cbbb50df2f78fa8fae6747a7814484a70"},
{file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1"}, {file = "cryptography-43.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:844b6d608374e7d08f4f6e6f9f7b951f9256db41421917dfb2d003dde4cd6b66"},
{file = "cryptography-42.0.8-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14"}, {file = "cryptography-43.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:51956cf8730665e2bdf8ddb8da0056f699c1a5715648c1b0144670c1ba00b48f"},
{file = "cryptography-42.0.8-cp39-abi3-win32.whl", hash = "sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c"}, {file = "cryptography-43.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:aae4d918f6b180a8ab8bf6511a419473d107df4dbb4225c7b48c5c9602c38c7f"},
{file = "cryptography-42.0.8-cp39-abi3-win_amd64.whl", hash = "sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a"}, {file = "cryptography-43.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:232ce02943a579095a339ac4b390fbbe97f5b5d5d107f8a08260ea2768be8cc2"},
{file = "cryptography-42.0.8-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe"}, {file = "cryptography-43.0.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5bcb8a5620008a8034d39bce21dc3e23735dfdb6a33a06974739bfa04f853947"},
{file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c"}, {file = "cryptography-43.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:08a24a7070b2b6804c1940ff0f910ff728932a9d0e80e7814234269f9d46d069"},
{file = "cryptography-42.0.8-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71"}, {file = "cryptography-43.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e9c5266c432a1e23738d178e51c2c7a5e2ddf790f248be939448c0ba2021f9d1"},
{file = "cryptography-42.0.8-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d"}, {file = "cryptography-43.0.0.tar.gz", hash = "sha256:b88075ada2d51aa9f18283532c9f60e72170041bba88d7f37e49cbb10275299e"},
{file = "cryptography-42.0.8-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c"},
{file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842"},
{file = "cryptography-42.0.8-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648"},
{file = "cryptography-42.0.8-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad"},
{file = "cryptography-42.0.8.tar.gz", hash = "sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2"},
] ]
[package.dependencies] [package.dependencies]
@@ -290,7 +285,7 @@ nox = ["nox"]
pep8test = ["check-sdist", "click", "mypy", "ruff"] pep8test = ["check-sdist", "click", "mypy", "ruff"]
sdist = ["build"] sdist = ["build"]
ssh = ["bcrypt (>=3.1.5)"] ssh = ["bcrypt (>=3.1.5)"]
test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test = ["certifi", "cryptography-vectors (==43.0.0)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
test-randomorder = ["pytest-randomly"] test-randomorder = ["pytest-randomly"]
[[package]] [[package]]
@@ -457,13 +452,13 @@ files = [
[[package]] [[package]]
name = "hsapi-client" name = "hsapi-client"
version = "0.9.3" version = "0.9.7"
description = "Headscale API client" description = "Headscale API client"
optional = false optional = false
python-versions = "<4.0,>=3.11" python-versions = "<4.0,>=3.11"
files = [ files = [
{file = "hsapi_client-0.9.3-py3-none-any.whl", hash = "sha256:75bf3e5f35a857c36f49560ba8d70243b3dc66138f50dbf2b862ae240a68b9ab"}, {file = "hsapi_client-0.9.7-py3-none-any.whl", hash = "sha256:6cd8ac2a787112a02d7d5d3e029ceba0749844806b20b3c27247393cccd53def"},
{file = "hsapi_client-0.9.3.tar.gz", hash = "sha256:58e1494608e17b224d27ca7fa004219cc1a1f7926c677e16fc21774e9502ed25"}, {file = "hsapi_client-0.9.7.tar.gz", hash = "sha256:7a6bf7cb533a4f0431c322bc292f09559eb27b37177ea2101a6ea559dc0c9e47"},
] ]
[package.dependencies] [package.dependencies]
@@ -473,13 +468,13 @@ requests = ">=2.32.3,<3.0.0"
[[package]] [[package]]
name = "humanize" name = "humanize"
version = "4.9.0" version = "4.10.0"
description = "Python humanize utilities" description = "Python humanize utilities"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "humanize-4.9.0-py3-none-any.whl", hash = "sha256:ce284a76d5b1377fd8836733b983bfb0b76f1aa1c090de2566fcf008d7f6ab16"}, {file = "humanize-4.10.0-py3-none-any.whl", hash = "sha256:39e7ccb96923e732b5c2e27aeaa3b10a8dfeeba3eb965ba7b74a3eb0e30040a6"},
{file = "humanize-4.9.0.tar.gz", hash = "sha256:582a265c931c683a7e9b8ed9559089dea7edcf6cc95be39a3cbc2c5d5ac2bcfa"}, {file = "humanize-4.10.0.tar.gz", hash = "sha256:06b6eb0293e4b85e8d385397c5868926820db32b9b654b932f57fa41c23c9978"},
] ]
[package.extras] [package.extras]
@@ -793,13 +788,13 @@ files = [
[[package]] [[package]]
name = "pure-eval" name = "pure-eval"
version = "0.2.2" version = "0.2.3"
description = "Safely evaluate AST nodes without side effects" description = "Safely evaluate AST nodes without side effects"
optional = false optional = false
python-versions = "*" python-versions = "*"
files = [ files = [
{file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"},
{file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"},
] ]
[package.extras] [package.extras]
@@ -1139,13 +1134,13 @@ zstd = ["zstandard (>=0.18.0)"]
[[package]] [[package]]
name = "uvicorn" name = "uvicorn"
version = "0.30.1" version = "0.30.3"
description = "The lightning-fast ASGI server." description = "The lightning-fast ASGI server."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "uvicorn-0.30.1-py3-none-any.whl", hash = "sha256:cd17daa7f3b9d7a24de3617820e634d0933b69eed8e33a516071174427238c81"}, {file = "uvicorn-0.30.3-py3-none-any.whl", hash = "sha256:94a3608da0e530cea8f69683aa4126364ac18e3826b6630d1a65f4638aade503"},
{file = "uvicorn-0.30.1.tar.gz", hash = "sha256:d46cd8e0fd80240baffbcd9ec1012a712938754afcf81bce56c024c1656aece8"}, {file = "uvicorn-0.30.3.tar.gz", hash = "sha256:0d114d0831ff1adbf231d358cbf42f17333413042552a624ea6a9b4c33dcfd81"},
] ]
[package.dependencies] [package.dependencies]

View File

@@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "hsman" name = "hsman"
version = "0.9.8" version = "0.9.16"
description = "Flask Admin webui for Headscale" description = "Flask Admin webui for Headscale"
authors = ["Andrea Mistrali <andrea@mistrali.pw>"] authors = ["Andrea Mistrali <andrea@mistrali.pw>"]
license = "BSD" license = "BSD"

View File

@@ -1,6 +1,5 @@
from app import create_app from app import create_app
from app import lib from app import lib
from app import models
import logging import logging
import logging.config import logging.config
import os import os
@@ -20,7 +19,7 @@ log.debug(f"Running in web mode: {lib.webMode()}")
def get_context(): def get_context():
# flask cli context setup # flask cli context setup
"""Objects exposed here will be automatically available from the shell.""" """Objects exposed here will be automatically available from the shell."""
return dict(app=app, models=models) return dict(app=app)
if __name__ == '__main__': if __name__ == '__main__':