Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 80 additions & 6 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,18 @@
import logging
import sys
import subprocess
from flask import Flask, render_template, request, redirect, url_for, flash, current_app
from flask import (
Flask,
render_template,
request,
redirect,
url_for,
flash,
current_app,
make_response,
g,
abort,
)
from flask_restx import Api
from werkzeug.middleware.proxy_fix import ProxyFix
from flask_minify import minify
Expand All @@ -17,7 +28,11 @@
from zoneforge.api.authentication import api as ns_auth
from zoneforge.api.authentication import LoginResource, SignupResource
from zoneforge.api.rbac import api as ns_rbac
from zoneforge.api.idm import api as ns_idm
from zoneforge.db import db
from zoneforge.api import app_release_access
from zoneforge.api.rbac import RoleResource, GroupResource
from zoneforge.api.idm import UserResource


def get_logging_conf() -> dict:
Expand Down Expand Up @@ -78,10 +93,17 @@ def create_app():

minify(app=app, html=True, js=True, cssless=True, static=True)
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1, x_prefix=1)

# Validate current user in template
app.context_processor(
lambda _=None: {"current_user": getattr(g, "current_user", None)}
)

# API Setup
api = Api(app, prefix="/api", doc="/api", validate=True)

@app.route("/", methods=["GET"])
# @app_release_access() # Without decorator the nav don't appear
def home():
zf_zone = DnsZone()
try:
Expand Down Expand Up @@ -140,33 +162,85 @@ def login():
login_response = LoginResource().post()

if login_response[1] != 200:
flash(login_response[0])
flash(login_response[0]["message"], "error")

return render_template("login.html.j2")

return redirect(url_for("home"))
response = make_response(redirect(url_for("home")))
response.set_cookie(
"access_token",
login_response[0]["token"],
httponly=True,
)
response.set_cookie(
"refresh_token",
login_response[0]["refresh_token"],
httponly=True,
)
return response
return render_template("login.html.j2")

@app.route("/signup", methods=["GET", "POST"])
def signup():
if request.method == "POST":
signup_response = SignupResource().post()

flash(signup_response[0])

if signup_response[1] != 200:

flash(signup_response[0]["message"], "error")
return render_template("signup.html.j2")

flash(signup_response[0]["message"], "success")
return redirect(url_for("login"))
return render_template("signup.html.j2")

@app.route("/logout", methods=["POST"])
def logout():
response = make_response(redirect(url_for("login")))
response.delete_cookie("access_token")
response.delete_cookie("refresh_token")
return response

@app.route("/access/<string:access_name>", methods=["GET"])
@app_release_access("adm")
def access(access_name):
user_sort = request.args.get("sort", "")
user_sort_order = request.args.get("sort_order", "desc")

if access_name not in ("users", "groups", "roles"):
return abort(404)

users = groups = roles = [{}]

if access_name == "users":
users = UserResource().get()

if access_name in ("users", "groups"):
groups = GroupResource().get()

if access_name in ("groups", "roles"):
roles = RoleResource().get()

access = {
**users[0],
**groups[0],
**roles[0],
}

return render_template(
"access.html.j2",
access_name=access_name,
access=access,
record_sort=user_sort,
record_sort_order=user_sort_order,
)

api.add_namespace(ns_status)
api.add_namespace(ns_zone)
api.add_namespace(ns_record)
api.add_namespace(ns_types)
api.add_namespace(ns_auth)
api.add_namespace(ns_rbac)
api.add_namespace(ns_idm)

return app

Expand Down
217 changes: 217 additions & 0 deletions static/css/access.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
.manager {
width: 920px;
justify-self: center;
}

/* Header */
.header-row {
flex-direction: column;
padding: 0;
gap: 1rem;
}

.create-new-access {
display: flex;
padding: 0.5rem;
}

.header-items {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}

/* Nav */
.header-nav {
display: flex;
align-items: center;
gap: .5rem;
margin: 0;
list-style-type: none;
color: #333333a6;
padding: 0.5rem 0;
}

.header-nav > li {
display: flex;
}

.header-nav li:hover {
transform: scale(1.2);
}

.header-nav > li > a {
text-decoration: none;
color: inherit;
font-size: 1rem;
padding: .2rem .5rem;
cursor: pointer;
}

.access-selected {
color: #4CAF50;
}

/* Create new record */

/* Toggle new record */
.new-access-content {
display: none;
}

.new-access-active {
display: flex;
}

.new-access {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: .5rem;
margin-bottom: .5rem;
}

.input-fields {
display: flex;
}

.input-fields > div,
.select-content {
display: flex;
flex-direction: column;
position: relative;
margin-right: 20px;
}

.input-fields > div label {
font-size: 1rem;
margin-bottom: .2rem;
font-weight: 600;
}

#password-content {
display: flex;
padding: .2rem 0;
height: 16px;
gap: .2rem;
}

#allow-generate-password + label {
font-size: .8rem;
margin: 0;
line-height: 1.1rem;
}

#allow-generate-password {
align-self: center;
height: 15px;
width: 15px;
margin: 0;
}

#display-selected-roles > span {
padding: .2rem;
font-size: .8rem;
}

.relative-select,
#display-selected-roles {
position: relative;
}

.input-fields > div > input,
select,
#display-selected-roles {
min-height: 20px;
min-width: 160px;
background: #f4f4f9;
outline: none;
border: 1px solid #aaa;
border-radius: 4px;
padding: .3rem;
}

#access-options {
min-height: 20px;
box-sizing: content-box;
background-color: #f4f4f9;
overflow: auto;
width: 100%;
position: absolute;
}

.input-fields input::placeholder,
#display-selected-roles {
line-height: 1.25rem;
font-size: .9rem;
font-weight: 600;
color: #aaaaaaf8;
}

.input-fields select,
select > option {
font-weight: 600;
cursor: pointer;
color: #333;
}

.select-content select:focus {
z-index: 1;
}

/* Toggle multi select */
.access-options-inactive {
display: none;
height: 150px;
}

.access-options-active {
display: flex;
}

.drop-select {
position: absolute;
right: -15px;
bottom: 6px;
cursor: pointer;
user-select: none;
}

.actions {
align-items: center;
}

.create {
height: 32px;
}

.overflow-multi-role {
overflow-x: auto;
max-width: 400px;
}

.multi-role {
padding: .4rem;
background-color: #ccc;
border-radius: 4px;
margin-right: 2px;
color: #333;
font-weight: 100;
}

/* Style to scroll */
::-webkit-scrollbar {
width: 5px;
height: 5px;
}

#display-selected-roles::-webkit-scrollbar {
height: 3px;
}

::-webkit-scrollbar-thumb {
background: #aaa;
border-radius: 10px;
}
Loading