Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
bdba6db42d
|
|||
|
4fb45c41bd
|
|||
|
a1c66152ae
|
|||
|
2a38fb14dd
|
|||
|
d86b2b58c2
|
@@ -1,9 +1,7 @@
|
||||
from flask import Flask, render_template
|
||||
from flask import Flask, render_template, g
|
||||
from werkzeug.exceptions import HTTPException
|
||||
|
||||
from flask_mobility import Mobility
|
||||
from flask_pyoidc.provider_configuration import ProviderConfiguration, ClientMetadata
|
||||
|
||||
|
||||
from . import filters
|
||||
from .lib import OIDCAuthentication
|
||||
@@ -53,9 +51,12 @@ def create_app(environment='development'):
|
||||
filters.init_app(app)
|
||||
|
||||
# Error handlers.
|
||||
|
||||
@app.errorhandler(HTTPException)
|
||||
def handle_http_error(exc):
|
||||
return render_template('error.html', error=exc), exc.code
|
||||
|
||||
@app.context_processor
|
||||
def inject_auth():
|
||||
return dict(auth=auth)
|
||||
|
||||
return app
|
||||
|
||||
17
app/lib.py
17
app/lib.py
@@ -56,11 +56,26 @@ class OIDCAuthentication(_OIDCAuth):
|
||||
userinfo = flask_session['userinfo']
|
||||
return userinfo['email'].split('@')[0]
|
||||
|
||||
@property
|
||||
def email(self) -> str:
|
||||
userinfo = flask_session['userinfo']
|
||||
return userinfo['email']
|
||||
|
||||
@property
|
||||
def login_name(self) -> str:
|
||||
userinfo = flask_session['userinfo']
|
||||
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
|
||||
def isAdmin(self) -> bool:
|
||||
userinfo = flask_session['userinfo']
|
||||
@@ -73,7 +88,7 @@ class OIDCAuthentication(_OIDCAuth):
|
||||
|
||||
if len(authorized_groups):
|
||||
log.debug(f"'{self.username}' is a member of {
|
||||
authorized_groups}")
|
||||
authorized_groups}. isAdmin == True")
|
||||
return True
|
||||
|
||||
if self.username in admin_users:
|
||||
|
||||
@@ -14,12 +14,10 @@ function renameNode(nodeId) {
|
||||
}
|
||||
|
||||
function createPKA(username) {
|
||||
console.log(username);
|
||||
var url = `${username}/pakcreate`;
|
||||
var ephemereal = $("#ephemereal").is(":checked");
|
||||
var reusable = $("#reusable").is(":checked");
|
||||
var expiration = $("#expiration").val();
|
||||
console.log(expiration);
|
||||
$.ajax({
|
||||
url: url,
|
||||
method: "POST",
|
||||
@@ -65,3 +63,29 @@ function toggleExpired(obj) {
|
||||
$(".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);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
{% if auth.isAdmin %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('main.nodes') }}">nodes</a>
|
||||
</li>
|
||||
@@ -59,6 +60,7 @@
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('main.routes') }}">routes</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item me-right">
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="jumbotron my-4">
|
||||
<div class="jumbotron jumbotron-fluid my-4">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -2,25 +2,26 @@
|
||||
|
||||
{% block content %}
|
||||
<h3>
|
||||
Welcome, {{ session.userinfo.name }}
|
||||
Welcome, {{ auth.full_name }}
|
||||
</h3>
|
||||
<hr>
|
||||
<h4>authentication info</h4>
|
||||
<div class="row data">
|
||||
<div class="col col-2">
|
||||
<strong>email</strong>
|
||||
<strong>username</strong>
|
||||
</div>
|
||||
<div class="col col-6">
|
||||
{{ session.userinfo.email }}
|
||||
<!-- {{ session.userinfo.email_verified | fancyBool | safe }} -->
|
||||
<span data-toggle="tooltip" data-placement="right" title="OIDC username: {{ auth.login_name }}">
|
||||
{{ auth.username }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row data">
|
||||
<div class="col col-2">
|
||||
<strong>username</strong>
|
||||
<strong>email</strong>
|
||||
</div>
|
||||
<div class="col col-6">
|
||||
{{ session.userinfo.preferred_username }}
|
||||
{{ auth.email }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row data">
|
||||
@@ -29,16 +30,16 @@
|
||||
</div>
|
||||
<div class="col col-6">
|
||||
<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">
|
||||
{% else %}
|
||||
<span class="badge badge-pill badge-dark">
|
||||
{% endif %}
|
||||
{{ session.userinfo.groups[0]}}
|
||||
{{ auth.groups[0]}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% for group in session.userinfo.groups[1:] |sort %}
|
||||
{% for group in auth.groups[1:] |sort %}
|
||||
<div class="row data">
|
||||
<div class="col col-2">
|
||||
|
||||
|
||||
@@ -36,6 +36,9 @@
|
||||
<div class="col col-8 float-left">
|
||||
<span data-toggle="tooltip" data-placement="right" title="{{ node.createdAt | fmt_datetime }}">
|
||||
{{ node.createdAt | htime_dt }}
|
||||
<span class="badge badge-pill badge-warning">
|
||||
{{ node.registerMethod.name }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% 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>
|
||||
<p></p>
|
||||
<table id="nodes" class="display" style="width:100%">
|
||||
|
||||
@@ -2,7 +2,7 @@ import logging
|
||||
import datetime
|
||||
import os
|
||||
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 app import auth
|
||||
|
||||
@@ -38,13 +38,10 @@ def token():
|
||||
@main_blueprint.route('/', methods=['GET', 'POST'])
|
||||
@auth.access_control('default')
|
||||
def index():
|
||||
user_session = UserSession(session)
|
||||
hs_user = user_session.userinfo['email'].split('@')[0]
|
||||
hs_user = auth.username
|
||||
userNodeList = [n for n in Node().list().nodes if n.user.name == hs_user]
|
||||
return render_template('index.html',
|
||||
userNodeList=userNodeList,
|
||||
session=user_session,
|
||||
auth=auth)
|
||||
userNodeList=userNodeList)
|
||||
|
||||
|
||||
@main_blueprint.route('/logout')
|
||||
|
||||
@@ -4,18 +4,17 @@ from flask import Blueprint, request
|
||||
from flask import redirect, url_for
|
||||
from app import auth
|
||||
|
||||
# from ..lib import login_name, username
|
||||
|
||||
from flask import jsonify
|
||||
|
||||
from hsapi_client import Node, User, Route, PreAuthKey
|
||||
from hsapi_client.preauthkeys import (v1CreatePreAuthKeyRequest,
|
||||
v1ExpirePreAuthKeyRequest)
|
||||
from hsapi_client.nodes import v1BackfillNodeIPsResponse
|
||||
|
||||
|
||||
log = logging.getLogger()
|
||||
# REST calls
|
||||
|
||||
# REST calls
|
||||
rest_blueprint = Blueprint(
|
||||
'rest', __name__, url_prefix=os.getenv('APPLICATION_ROOT', '/'))
|
||||
|
||||
@@ -30,7 +29,7 @@ def routeToggle(routeId: int):
|
||||
else:
|
||||
action = 'enabled'
|
||||
log.info(
|
||||
f"route '{route.prefix}' via '{route.node.givenName}'"
|
||||
f"route '{route.prefix}' via '{route.node.givenName}' "
|
||||
f"{action} by '{auth.username}'")
|
||||
Route().toggle(routeId)
|
||||
return redirect(request.referrer)
|
||||
@@ -106,3 +105,10 @@ def expirePKA(userName: str, key: str):
|
||||
|
||||
PreAuthKey().expire(req)
|
||||
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)
|
||||
|
||||
6
poetry.lock
generated
6
poetry.lock
generated
@@ -452,13 +452,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "hsapi-client"
|
||||
version = "0.9.6"
|
||||
version = "0.9.7"
|
||||
description = "Headscale API client"
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.11"
|
||||
files = [
|
||||
{file = "hsapi_client-0.9.6-py3-none-any.whl", hash = "sha256:441cd219a2384f66511b8cca21224171b4e6753d16d364d984eb9887aa686a6c"},
|
||||
{file = "hsapi_client-0.9.6.tar.gz", hash = "sha256:b6a4183fb9cdf95b0e864eec5b79ea18843e25379f928c4770b68e4f1ce8334b"},
|
||||
{file = "hsapi_client-0.9.7-py3-none-any.whl", hash = "sha256:6cd8ac2a787112a02d7d5d3e029ceba0749844806b20b3c27247393cccd53def"},
|
||||
{file = "hsapi_client-0.9.7.tar.gz", hash = "sha256:7a6bf7cb533a4f0431c322bc292f09559eb27b37177ea2101a6ea559dc0c9e47"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "hsman"
|
||||
version = "0.9.13"
|
||||
version = "0.9.16"
|
||||
description = "Flask Admin webui for Headscale"
|
||||
authors = ["Andrea Mistrali <andrea@mistrali.pw>"]
|
||||
license = "BSD"
|
||||
|
||||
Reference in New Issue
Block a user