feat(api): users and groups actions
parent
c2fc854f16
commit
e9a6c7108d
|
@ -3,7 +3,6 @@ Flask-Login==0.5.0
|
|||
eventlet==0.33.0
|
||||
Flask-SocketIO==5.1.0
|
||||
bcrypt==3.2.0
|
||||
|
||||
diceware==0.9.6
|
||||
mysql-connector-python==8.0.25
|
||||
psycopg2==2.8.6
|
||||
|
@ -13,5 +12,5 @@ urllib3==1.26.6
|
|||
schema==0.7.5
|
||||
Werkzeug~=2.0.0
|
||||
python-jose==3.3.0
|
||||
# Unused yet
|
||||
#flask-oidc==1.4.0
|
||||
Cerberus==1.3.4
|
||||
PyYAML==6.0
|
||||
|
|
|
@ -108,4 +108,4 @@ def send_custom(path):
|
|||
"""
|
||||
Import all views
|
||||
"""
|
||||
from .views import ApiViews, InternalViews, LoginViews, WebViews
|
||||
from .views import ApiViews, AppViews, LoginViews, WebViews, WpViews
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
# Copyright 2017 the Isard-vdi project authors:
|
||||
# Josep Maria Viñolas Auquer
|
||||
# Alberto Larraz Dalmases
|
||||
# License: AGPLv3
|
||||
|
||||
import json
|
||||
import logging as log
|
||||
import os
|
||||
import traceback
|
||||
from functools import wraps
|
||||
|
||||
from flask import request
|
||||
from jose import jwt
|
||||
|
||||
from admin import app
|
||||
|
||||
from ..lib.api_exceptions import Error
|
||||
|
||||
|
||||
def get_header_jwt_payload():
|
||||
return get_token_payload(get_token_auth_header())
|
||||
|
||||
|
||||
def get_token_header(header):
|
||||
"""Obtains the Access Token from the a Header"""
|
||||
auth = request.headers.get(header, None)
|
||||
if not auth:
|
||||
raise Error(
|
||||
"unauthorized",
|
||||
"Authorization header is expected",
|
||||
traceback.format_stack(),
|
||||
)
|
||||
|
||||
parts = auth.split()
|
||||
if parts[0].lower() != "bearer":
|
||||
raise Error(
|
||||
"unauthorized",
|
||||
"Authorization header must start with Bearer",
|
||||
traceback.format_stack(),
|
||||
)
|
||||
|
||||
elif len(parts) == 1:
|
||||
raise Error("bad_request", "Token not found")
|
||||
elif len(parts) > 2:
|
||||
raise Error(
|
||||
"unauthorized",
|
||||
"Authorization header must be Bearer token",
|
||||
traceback.format_stack(),
|
||||
)
|
||||
|
||||
return parts[1] # Token
|
||||
|
||||
|
||||
def get_token_auth_header():
|
||||
return get_token_header("Authorization")
|
||||
|
||||
|
||||
def get_token_payload(token):
|
||||
try:
|
||||
claims = jwt.get_unverified_claims(token)
|
||||
secret = app.config["API_SECRET"]
|
||||
|
||||
except:
|
||||
log.warning("JWT token with invalid parameters. Can not parse it.")
|
||||
raise Error(
|
||||
"unauthorized",
|
||||
"Unable to parse authentication parameters token.",
|
||||
traceback.format_stack(),
|
||||
)
|
||||
|
||||
try:
|
||||
payload = jwt.decode(
|
||||
token,
|
||||
secret,
|
||||
algorithms=["HS256"],
|
||||
options=dict(verify_aud=False, verify_sub=False, verify_exp=True),
|
||||
)
|
||||
except jwt.ExpiredSignatureError:
|
||||
log.info("Token expired")
|
||||
raise Error("unauthorized", "Token is expired", traceback.format_stack())
|
||||
|
||||
except jwt.JWTClaimsError:
|
||||
raise Error(
|
||||
"unauthorized",
|
||||
"Incorrect claims, please check the audience and issuer",
|
||||
traceback.format_stack(),
|
||||
)
|
||||
except Exception:
|
||||
raise Error(
|
||||
"unauthorized",
|
||||
"Unable to parse authentication token.",
|
||||
traceback.format_stack(),
|
||||
)
|
||||
if payload.get("data", False):
|
||||
return payload["data"]
|
||||
return payload
|
|
@ -30,6 +30,7 @@ options.num = 3
|
|||
|
||||
import secrets
|
||||
|
||||
from .api_exceptions import Error
|
||||
from .events import Events
|
||||
from .exceptions import UserExists, UserNotFound
|
||||
from .helpers import (
|
||||
|
@ -466,6 +467,10 @@ class Admin:
|
|||
def _get_roles(self):
|
||||
return filter_roles_listofdicts(self.keycloak.get_roles())
|
||||
|
||||
def get_group_by_name(self, group_name):
|
||||
group = [g for g in self.internal["groups"] if g["name"] == group_name]
|
||||
return group[0] if len(group) else False
|
||||
|
||||
def get_keycloak_groups(self):
|
||||
log.warning("Loading keycloak groups...")
|
||||
return self.keycloak.get_groups()
|
||||
|
@ -1812,6 +1817,7 @@ class Admin:
|
|||
log.error(traceback.format_exc())
|
||||
|
||||
self.resync_data()
|
||||
return uid
|
||||
|
||||
def add_group(self, g):
|
||||
# TODO: Check if exists
|
||||
|
@ -1830,6 +1836,7 @@ class Admin:
|
|||
self.moodle.add_system_cohort(new_path, description=g["description"])
|
||||
self.nextcloud.add_group(new_path)
|
||||
self.resync_data()
|
||||
return new_path
|
||||
|
||||
def delete_group_by_id(self, group_id):
|
||||
ev = Events("Deleting group", "Deleting from keycloak")
|
||||
|
@ -1843,6 +1850,7 @@ class Admin:
|
|||
+ str(group_id)
|
||||
+ " as it does not exist!"
|
||||
)
|
||||
raise Error("not_found", "Group " + group_id + " not found.")
|
||||
|
||||
# {'id': '966ad67c-499a-4f56-bd1d-283691cde0e7', 'name': 'asdgfewfwe', 'path': '/asdgfewfwe', 'attributes': {}, 'realmRoles': [], 'clientRoles': {}, 'subGroups': [], 'access': {'view': True, 'manage': True, 'manageMembership': True}}
|
||||
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
import inspect
|
||||
import json
|
||||
import logging as log
|
||||
import os
|
||||
import traceback
|
||||
|
||||
from flask import jsonify, request
|
||||
|
||||
from admin import app
|
||||
|
||||
content_type = {"Content-Type": "application/json"}
|
||||
ex = {
|
||||
"bad_request": {
|
||||
"error": {
|
||||
"error": "bad_request",
|
||||
"msg": "Bad request",
|
||||
},
|
||||
"status_code": 400,
|
||||
},
|
||||
"unauthorized": {
|
||||
"error": {
|
||||
"error": "unauthorized",
|
||||
"msg": "Unauthorized",
|
||||
},
|
||||
"status_code": 401,
|
||||
},
|
||||
"forbidden": {
|
||||
"error": {
|
||||
"error": "forbidden",
|
||||
"msg": "Forbidden",
|
||||
},
|
||||
"status_code": 403,
|
||||
},
|
||||
"not_found": {
|
||||
"error": {
|
||||
"error": "not_found",
|
||||
"msg": "Not found",
|
||||
},
|
||||
"status_code": 404,
|
||||
},
|
||||
"conflict": {
|
||||
"error": {
|
||||
"error": "conflict",
|
||||
"msg": "Conflict",
|
||||
},
|
||||
"status_code": 409,
|
||||
},
|
||||
"internal_server": {
|
||||
"error": {
|
||||
"error": "internal_server",
|
||||
"msg": "Internal server error",
|
||||
},
|
||||
"status_code": 500,
|
||||
},
|
||||
"gateway_timeout": {
|
||||
"error": {
|
||||
"error": "gateway_timeout",
|
||||
"msg": "Gateway timeout",
|
||||
},
|
||||
"status_code": 504,
|
||||
},
|
||||
"precondition_required": {
|
||||
"error": {
|
||||
"error": "precondition_required",
|
||||
"msg": "Precondition required",
|
||||
},
|
||||
"status_code": 428,
|
||||
},
|
||||
"insufficient_storage": {
|
||||
"error": {
|
||||
"error": "insufficient_storage",
|
||||
"msg": "Insufficient storage",
|
||||
},
|
||||
"status_code": 507,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class Error(Exception):
|
||||
def __init__(self, error="bad_request", description="", debug="", data=None):
|
||||
self.error = ex[error]["error"].copy()
|
||||
self.error["function"] = (
|
||||
inspect.stack()[1][1].split(os.sep)[-1]
|
||||
+ ":"
|
||||
+ str(inspect.stack()[1][2])
|
||||
+ ":"
|
||||
+ inspect.stack()[1][3]
|
||||
)
|
||||
self.error["function_call"] = (
|
||||
inspect.stack()[2][1].split(os.sep)[-1]
|
||||
+ ":"
|
||||
+ str(inspect.stack()[2][2])
|
||||
+ ":"
|
||||
+ inspect.stack()[2][3]
|
||||
)
|
||||
self.error["description"] = str(description)
|
||||
self.error["debug"] = "{}\n\r{}{}".format(
|
||||
"----------- DEBUG START -------------",
|
||||
debug,
|
||||
"----------- DEBUG STOP -------------",
|
||||
)
|
||||
self.error["request"] = (
|
||||
"{}\n{}\r\n{}\r\n\r\n{}{}".format(
|
||||
"----------- REQUEST START -----------",
|
||||
request.method + " " + request.url,
|
||||
"\r\n".join("{}: {}".format(k, v) for k, v in request.headers.items()),
|
||||
request.body if hasattr(request, "body") else "",
|
||||
"----------- REQUEST STOP -----------",
|
||||
)
|
||||
if request
|
||||
else ""
|
||||
)
|
||||
self.error["data"] = (
|
||||
"{}\n{}\n{}".format(
|
||||
"----------- DATA START -----------",
|
||||
json.dumps(data, indent=2),
|
||||
"----------- DATA STOP -----------",
|
||||
)
|
||||
if data
|
||||
else ""
|
||||
)
|
||||
self.status_code = ex[error]["status_code"]
|
||||
self.content_type = content_type
|
||||
log.debug(
|
||||
"%s - %s - [%s -> %s]\r\n%s\r\n%s\r\n%s"
|
||||
% (
|
||||
error,
|
||||
str(description),
|
||||
self.error["function_call"],
|
||||
self.error["function"],
|
||||
self.error["debug"],
|
||||
self.error["request"],
|
||||
self.error["data"],
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@app.errorhandler(Error)
|
||||
def handle_user_error(ex):
|
||||
response = jsonify(ex.error)
|
||||
response.status_code = ex.status_code
|
||||
response.headers = {"content-type": content_type}
|
||||
return response
|
|
@ -6,9 +6,45 @@ import os
|
|||
import sys
|
||||
import traceback
|
||||
|
||||
import yaml
|
||||
from cerberus import Validator, rules_set_registry, schema_registry
|
||||
|
||||
from admin import app
|
||||
|
||||
|
||||
class AdminValidator(Validator):
|
||||
None
|
||||
# def _normalize_default_setter_genid(self, document):
|
||||
# return _parse_string(document["name"])
|
||||
|
||||
# def _normalize_default_setter_genidlower(self, document):
|
||||
# return _parse_string(document["name"]).lower()
|
||||
|
||||
# def _normalize_default_setter_gengroupid(self, document):
|
||||
# return _parse_string(
|
||||
# document["parent_category"] + "-" + document["uid"]
|
||||
# ).lower()
|
||||
|
||||
|
||||
def load_validators(purge_unknown=True):
|
||||
validators = {}
|
||||
schema_path = os.path.join(app.root_path, "schemas")
|
||||
for schema_filename in os.listdir(schema_path):
|
||||
try:
|
||||
with open(os.path.join(schema_path, schema_filename)) as file:
|
||||
schema_yml = file.read()
|
||||
schema = yaml.load(schema_yml, Loader=yaml.FullLoader)
|
||||
validators[schema_filename.split(".")[0]] = AdminValidator(
|
||||
schema, purge_unknown=purge_unknown
|
||||
)
|
||||
except IsADirectoryError:
|
||||
None
|
||||
return validators
|
||||
|
||||
|
||||
app.validators = load_validators()
|
||||
|
||||
|
||||
class loadConfig:
|
||||
def __init__(self, app=None):
|
||||
try:
|
||||
|
@ -34,9 +70,7 @@ class loadConfig:
|
|||
app.config.setdefault(
|
||||
"VERIFY", True if os.environ["VERIFY"] == "true" else False
|
||||
)
|
||||
app.config.setdefault(
|
||||
"API_SECRET", os.environ.get("API_SECRET")
|
||||
)
|
||||
app.config.setdefault("API_SECRET", os.environ.get("API_SECRET"))
|
||||
except Exception as e:
|
||||
log.error(traceback.format_exc())
|
||||
raise
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
name:
|
||||
required: true
|
||||
type: string
|
||||
description:
|
||||
required: false
|
||||
type: string
|
||||
default: "Api created"
|
||||
parent:
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
|
@ -0,0 +1,29 @@
|
|||
username:
|
||||
required: true
|
||||
type: string
|
||||
first:
|
||||
required: true
|
||||
type: string
|
||||
last:
|
||||
required: true
|
||||
type: string
|
||||
email:
|
||||
required: true
|
||||
type: string
|
||||
password:
|
||||
required: true
|
||||
type: string
|
||||
quota:
|
||||
required: true
|
||||
type: string
|
||||
enabled:
|
||||
required: true
|
||||
type: boolean
|
||||
role:
|
||||
required: true
|
||||
type: string
|
||||
empty: false
|
||||
groups:
|
||||
required: true
|
||||
type: list
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
first:
|
||||
required: false
|
||||
type: string
|
||||
last:
|
||||
required: false
|
||||
type: string
|
||||
email:
|
||||
required: false
|
||||
type: string
|
||||
password:
|
||||
required: false
|
||||
type: string
|
||||
password_temporary:
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
quota:
|
||||
required: false
|
||||
type: string
|
||||
enabled:
|
||||
required: false
|
||||
type: boolean
|
||||
role:
|
||||
required: false
|
||||
type: string
|
||||
empty: false
|
||||
groups:
|
||||
required: false
|
||||
type: list
|
||||
|
|
@ -1,545 +1,298 @@
|
|||
#!flask/bin/python
|
||||
# coding=utf-8
|
||||
import concurrent.futures
|
||||
import json
|
||||
import logging as log
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import sys
|
||||
|
||||
# import Queue
|
||||
import threading
|
||||
import time
|
||||
import traceback
|
||||
from uuid import uuid4
|
||||
|
||||
from flask import Response, jsonify, redirect, render_template, request, url_for
|
||||
from flask_login import current_user, login_required
|
||||
from flask import request
|
||||
|
||||
from admin import app
|
||||
|
||||
from ..lib.helpers import system_group
|
||||
from .decorators import is_admin
|
||||
|
||||
threads = {"external": None}
|
||||
# q = Queue.Queue()
|
||||
|
||||
from keycloak.exceptions import KeycloakGetError
|
||||
|
||||
from ..lib.dashboard import Dashboard
|
||||
from ..lib.exceptions import UserExists, UserNotFound
|
||||
|
||||
dashboard = Dashboard()
|
||||
from ..lib.api_exceptions import Error
|
||||
from .decorators import has_token
|
||||
|
||||
|
||||
@app.route("/sysadmin/api/resync")
|
||||
@app.route("/api/resync")
|
||||
@login_required
|
||||
def resync():
|
||||
return (
|
||||
json.dumps(app.admin.resync_data()),
|
||||
200,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
## LISTS
|
||||
@app.route("/ddapi/users", methods=["GET"])
|
||||
@has_token
|
||||
def ddapi_users():
|
||||
if request.method == "GET":
|
||||
sorted_users = sorted(app.admin.get_mix_users(), key=lambda k: k["username"])
|
||||
users = []
|
||||
for user in sorted_users:
|
||||
users.append(user_parser(user))
|
||||
return json.dumps(users), 200, {"Content-Type": "application/json"}
|
||||
|
||||
|
||||
@app.route("/api/users", methods=["GET", "PUT"])
|
||||
@app.route("/api/users/<provider>", methods=["POST", "PUT", "GET", "DELETE"])
|
||||
@login_required
|
||||
def users(provider=False):
|
||||
if request.method == "DELETE":
|
||||
if current_user.role != "admin":
|
||||
return json.dumps({}), 301, {"Content-Type": "application/json"}
|
||||
if provider == "keycloak":
|
||||
return (
|
||||
json.dumps(app.admin.delete_keycloak_users()),
|
||||
200,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
if provider == "nextcloud":
|
||||
return (
|
||||
json.dumps(app.admin.delete_nextcloud_users()),
|
||||
200,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
if provider == "moodle":
|
||||
return (
|
||||
json.dumps(app.admin.delete_moodle_users()),
|
||||
200,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
@app.route("/ddapi/users/filter", methods=["POST"])
|
||||
@has_token
|
||||
def ddapi_users_search():
|
||||
if request.method == "POST":
|
||||
if current_user.role != "admin":
|
||||
return json.dumps({}), 301, {"Content-Type": "application/json"}
|
||||
if provider == "moodle":
|
||||
return (
|
||||
json.dumps(app.admin.sync_to_moodle()),
|
||||
200,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
if provider == "nextcloud":
|
||||
return (
|
||||
json.dumps(app.admin.sync_to_nextcloud()),
|
||||
200,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
if request.method == "PUT" and not provider:
|
||||
if current_user.role != "admin":
|
||||
return json.dumps({}), 301, {"Content-Type": "application/json"}
|
||||
data = request.get_json(force=True)
|
||||
if not data.get("text"):
|
||||
raise Error("bad_request", "Incorrect data requested.")
|
||||
users = app.admin.get_mix_users()
|
||||
result = [user_parser(user) for user in filter_users(users, data["text"])]
|
||||
sorted_result = sorted(result, key=lambda k: k["id"])
|
||||
return json.dumps(sorted_result), 200, {"Content-Type": "application/json"}
|
||||
|
||||
if "external" in threads.keys():
|
||||
if threads["external"] is not None and threads["external"].is_alive():
|
||||
return (
|
||||
json.dumps(
|
||||
{"msg": "Precondition failed: already working with users"}
|
||||
),
|
||||
412,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
else:
|
||||
threads["external"] = None
|
||||
try:
|
||||
threads["external"] = threading.Thread(
|
||||
target=app.admin.update_users_from_keycloak, args=()
|
||||
)
|
||||
threads["external"].start()
|
||||
return json.dumps({}), 200, {"Content-Type": "application/json"}
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
return (
|
||||
json.dumps({"msg": "Add user error."}),
|
||||
500,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
|
||||
# return json.dumps(app.admin.update_users_from_keycloak()), 200, {'Content-Type': 'application/json'}
|
||||
@app.route("/ddapi/groups", methods=["GET"])
|
||||
@has_token
|
||||
def ddapi_groups():
|
||||
if request.method == "GET":
|
||||
sorted_groups = sorted(app.admin.get_mix_groups(), key=lambda k: k["name"])
|
||||
groups = []
|
||||
for group in sorted_groups:
|
||||
groups.append(group_parser(group))
|
||||
return json.dumps(groups), 200, {"Content-Type": "application/json"}
|
||||
|
||||
users = app.admin.get_mix_users()
|
||||
if current_user.role != "admin":
|
||||
for user in users:
|
||||
user["keycloak_groups"] = [
|
||||
g for g in user["keycloak_groups"] if not system_group(g)
|
||||
|
||||
@app.route("/ddapi/group/users", methods=["POST"])
|
||||
@has_token
|
||||
def ddapi_group_users():
|
||||
if request.method == "POST":
|
||||
data = request.get_json(force=True)
|
||||
sorted_users = sorted(app.admin.get_mix_users(), key=lambda k: k["username"])
|
||||
if data.get("id"):
|
||||
group_users = [
|
||||
user_parser(user)
|
||||
for user in sorted_users
|
||||
if data.get("id") in user["keycloak_groups"]
|
||||
]
|
||||
return json.dumps(users), 200, {"Content-Type": "application/json"}
|
||||
|
||||
|
||||
@app.route("/api/users_bulk/<action>", methods=["PUT"])
|
||||
@login_required
|
||||
def users_bulk(action):
|
||||
data = request.get_json(force=True)
|
||||
if request.method == "PUT":
|
||||
if action == "enable":
|
||||
if "external" in threads.keys():
|
||||
if threads["external"] is not None and threads["external"].is_alive():
|
||||
return (
|
||||
json.dumps(
|
||||
{"msg": "Precondition failed: already operating users"}
|
||||
),
|
||||
412,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
else:
|
||||
threads["external"] = None
|
||||
elif data.get("path"):
|
||||
try:
|
||||
threads["external"] = threading.Thread(
|
||||
target=app.admin.enable_users, args=(data,)
|
||||
)
|
||||
threads["external"].start()
|
||||
return json.dumps({}), 200, {"Content-Type": "application/json"}
|
||||
name = [
|
||||
g["name"]
|
||||
for g in app.admin.get_mix_groups()
|
||||
if g["path"] == data.get("path")
|
||||
][0]
|
||||
group_users = [
|
||||
user_parser(user)
|
||||
for user in sorted_users
|
||||
if name in user["keycloak_groups"]
|
||||
]
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
return (
|
||||
json.dumps({"msg": "Enable users error."}),
|
||||
500,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
if action == "disable":
|
||||
if "external" in threads.keys():
|
||||
if threads["external"] is not None and threads["external"].is_alive():
|
||||
return (
|
||||
json.dumps(
|
||||
{"msg": "Precondition failed: already operating users"}
|
||||
),
|
||||
412,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
else:
|
||||
threads["external"] = None
|
||||
raise Error("not_found", "Group path not found in system")
|
||||
elif data.get("keycloak_id"):
|
||||
try:
|
||||
threads["external"] = threading.Thread(
|
||||
target=app.admin.disable_users, args=(data,)
|
||||
)
|
||||
threads["external"].start()
|
||||
return json.dumps({}), 200, {"Content-Type": "application/json"}
|
||||
name = [
|
||||
g["name"]
|
||||
for g in app.admin.get_mix_groups()
|
||||
if g["id"] == data.get("keycloak_id")
|
||||
][0]
|
||||
group_users = [
|
||||
user_parser(user)
|
||||
for user in sorted_users
|
||||
if name in user["keycloak_groups"]
|
||||
]
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
return (
|
||||
json.dumps({"msg": "Disabling users error."}),
|
||||
500,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
if action == "delete":
|
||||
if "external" in threads.keys():
|
||||
if threads["external"] is not None and threads["external"].is_alive():
|
||||
return (
|
||||
json.dumps(
|
||||
{"msg": "Precondition failed: already operating users"}
|
||||
),
|
||||
412,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
else:
|
||||
threads["external"] = None
|
||||
try:
|
||||
threads["external"] = threading.Thread(
|
||||
target=app.admin.delete_users, args=(data,)
|
||||
)
|
||||
threads["external"].start()
|
||||
return json.dumps({}), 200, {"Content-Type": "application/json"}
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
return (
|
||||
json.dumps({"msg": "Deleting users error."}),
|
||||
500,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
return json.dumps({}), 405, {"Content-Type": "application/json"}
|
||||
|
||||
|
||||
# Update pwd
|
||||
@app.route("/api/user_password", methods=["GET"])
|
||||
@app.route("/api/user_password/<userid>", methods=["PUT"])
|
||||
@login_required
|
||||
def user_password(userid=False):
|
||||
if request.method == "GET":
|
||||
return (
|
||||
json.dumps(app.admin.get_dice_pwd()),
|
||||
200,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
if request.method == "PUT":
|
||||
data = request.get_json(force=True)
|
||||
password = data["password"]
|
||||
temporary = data.get("temporary", True)
|
||||
try:
|
||||
res = app.admin.user_update_password(userid, password, temporary)
|
||||
return json.dumps({}), 200, {"Content-Type": "application/json"}
|
||||
except KeycloakGetError as e:
|
||||
log.error(e.error_message.decode("utf-8"))
|
||||
return (
|
||||
json.dumps({"msg": "Update password error."}),
|
||||
500,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
|
||||
return json.dumps({}), 405, {"Content-Type": "application/json"}
|
||||
|
||||
|
||||
# User
|
||||
@app.route("/api/user", methods=["POST"])
|
||||
@app.route("/api/user/<userid>", methods=["PUT", "GET", "DELETE"])
|
||||
@login_required
|
||||
def user(userid=None):
|
||||
if request.method == "DELETE":
|
||||
app.admin.delete_user(userid)
|
||||
return json.dumps({}), 200, {"Content-Type": "application/json"}
|
||||
if request.method == "POST":
|
||||
data = request.get_json(force=True)
|
||||
if app.admin.get_user_username(data["username"]):
|
||||
return (
|
||||
json.dumps({"msg": "Add user error: already exists."}),
|
||||
409,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
data["enabled"] = True if data.get("enabled", False) else False
|
||||
data["quota"] = data["quota"] if data["quota"] != "false" else False
|
||||
data["groups"] = data["groups"] if data.get("groups", False) else []
|
||||
if "external" in threads.keys():
|
||||
if threads["external"] is not None and threads["external"].is_alive():
|
||||
return (
|
||||
json.dumps({"msg": "Precondition failed: already adding users"}),
|
||||
412,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
else:
|
||||
threads["external"] = None
|
||||
try:
|
||||
threads["external"] = threading.Thread(
|
||||
target=app.admin.add_user, args=(data,)
|
||||
)
|
||||
threads["external"].start()
|
||||
return json.dumps({}), 200, {"Content-Type": "application/json"}
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
return (
|
||||
json.dumps({"msg": "Add user error."}),
|
||||
500,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
|
||||
if request.method == "PUT":
|
||||
data = request.get_json(force=True)
|
||||
data["enabled"] = True if data.get("enabled", False) else False
|
||||
data["groups"] = data["groups"] if data.get("groups", False) else []
|
||||
data["roles"] = [data.pop("role-keycloak")]
|
||||
try:
|
||||
app.admin.user_update(data)
|
||||
return json.dumps({}), 200, {"Content-Type": "application/json"}
|
||||
except UserNotFound:
|
||||
return (
|
||||
json.dumps({"msg": "User not found."}),
|
||||
404,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
if request.method == "DELETE":
|
||||
pass
|
||||
if request.method == "GET":
|
||||
user = app.admin.get_user(userid)
|
||||
if not user:
|
||||
return (
|
||||
json.dumps({"msg": "User not found."}),
|
||||
404,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
return json.dumps(user), 200, {"Content-Type": "application/json"}
|
||||
|
||||
|
||||
@app.route("/api/roles")
|
||||
@login_required
|
||||
def roles():
|
||||
sorted_roles = sorted(app.admin.get_roles(), key=lambda k: k["name"])
|
||||
if current_user.role != "admin":
|
||||
sorted_roles = [sr for sr in sorted_roles if sr["name"] != "admin"]
|
||||
return json.dumps(sorted_roles), 200, {"Content-Type": "application/json"}
|
||||
|
||||
|
||||
@app.route("/api/group", methods=["POST", "DELETE"])
|
||||
@app.route("/api/group/<group_id>", methods=["PUT", "GET", "DELETE"])
|
||||
@login_required
|
||||
def group(group_id=False):
|
||||
if request.method == "POST":
|
||||
data = request.get_json(force=True)
|
||||
data["parent"] = data["parent"] if data["parent"] != "" else None
|
||||
return (
|
||||
json.dumps(app.admin.add_group(data)),
|
||||
200,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
if request.method == "DELETE":
|
||||
try:
|
||||
data = request.get_json(force=True)
|
||||
except:
|
||||
data = False
|
||||
|
||||
if data:
|
||||
res = app.admin.delete_group_by_path(data["path"])
|
||||
raise Error("not_found", "Group keycloak_id not found in system")
|
||||
else:
|
||||
res = app.admin.delete_group_by_id(group_id)
|
||||
return json.dumps(res), 200, {"Content-Type": "application/json"}
|
||||
raise Error("bad_request", "Incorrect data requested.")
|
||||
return json.dumps(group_users), 200, {"Content-Type": "application/json"}
|
||||
|
||||
|
||||
@app.route("/api/groups")
|
||||
@app.route("/api/groups/<provider>", methods=["POST", "PUT", "GET", "DELETE"])
|
||||
@login_required
|
||||
def groups(provider=False):
|
||||
@app.route("/ddapi/roles", methods=["GET"])
|
||||
@has_token
|
||||
def ddapi_roles():
|
||||
if request.method == "GET":
|
||||
sorted_groups = sorted(app.admin.get_mix_groups(), key=lambda k: str(k["name"]))
|
||||
if current_user.role != "admin":
|
||||
## internal groups should be avoided as are assigned with the role
|
||||
sorted_groups = [sg for sg in sorted_groups if not system_group(sg["name"])]
|
||||
else:
|
||||
sorted_groups = [sg for sg in sorted_groups]
|
||||
return json.dumps(sorted_groups), 200, {"Content-Type": "application/json"}
|
||||
if request.method == "DELETE":
|
||||
if provider == "keycloak":
|
||||
return (
|
||||
json.dumps(app.admin.delete_keycloak_groups()),
|
||||
200,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
|
||||
|
||||
### SYSADM USERS ONLY
|
||||
|
||||
|
||||
@app.route("/api/external", methods=["POST", "PUT", "GET", "DELETE"])
|
||||
@login_required
|
||||
def external():
|
||||
if "external" in threads.keys():
|
||||
if threads["external"] is not None and threads["external"].is_alive():
|
||||
return json.dumps({}), 301, {"Content-Type": "application/json"}
|
||||
else:
|
||||
threads["external"] = None
|
||||
|
||||
if request.method == "POST":
|
||||
data = request.get_json(force=True)
|
||||
if data["format"] == "json-ga":
|
||||
threads["external"] = threading.Thread(
|
||||
target=app.admin.upload_json_ga, args=(data,)
|
||||
)
|
||||
threads["external"].start()
|
||||
return json.dumps({}), 200, {"Content-Type": "application/json"}
|
||||
if data["format"] == "csv-ug":
|
||||
valid = check_upload_errors(data)
|
||||
if valid["pass"]:
|
||||
threads["external"] = threading.Thread(
|
||||
target=app.admin.upload_csv_ug, args=(data,)
|
||||
)
|
||||
threads["external"].start()
|
||||
return json.dumps({}), 200, {"Content-Type": "application/json"}
|
||||
else:
|
||||
return json.dumps(valid), 422, {"Content-Type": "application/json"}
|
||||
if request.method == "PUT":
|
||||
data = request.get_json(force=True)
|
||||
threads["external"] = threading.Thread(
|
||||
target=app.admin.sync_external, args=(data,)
|
||||
)
|
||||
threads["external"].start()
|
||||
return json.dumps({}), 200, {"Content-Type": "application/json"}
|
||||
if request.method == "DELETE":
|
||||
print("RESET")
|
||||
app.admin.reset_external()
|
||||
return json.dumps({}), 200, {"Content-Type": "application/json"}
|
||||
return json.dumps({}), 500, {"Content-Type": "application/json"}
|
||||
|
||||
|
||||
@app.route("/api/external/users")
|
||||
@login_required
|
||||
def external_users_list():
|
||||
while threads["external"] is not None and threads["external"].is_alive():
|
||||
time.sleep(0.5)
|
||||
return (
|
||||
json.dumps(app.admin.get_external_users()),
|
||||
200,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
|
||||
|
||||
@app.route("/api/external/groups")
|
||||
@login_required
|
||||
def external_groups_list():
|
||||
while threads["external"] is not None and threads["external"].is_alive():
|
||||
time.sleep(0.5)
|
||||
return (
|
||||
json.dumps(app.admin.get_external_groups()),
|
||||
200,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
|
||||
|
||||
@app.route("/api/external/roles", methods=["PUT"])
|
||||
@login_required
|
||||
def external_roles():
|
||||
if request.method == "PUT":
|
||||
return (
|
||||
json.dumps(app.admin.external_roleassign(request.get_json(force=True))),
|
||||
200,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
|
||||
|
||||
def check_upload_errors(data):
|
||||
email_regex = r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b"
|
||||
for u in data["data"]:
|
||||
try:
|
||||
user_groups = [g.strip() for g in u["groups"].split(",")]
|
||||
except:
|
||||
resp = {
|
||||
"pass": False,
|
||||
"msg": "User " + u["username"] + " has invalid groups: " + u["groups"],
|
||||
}
|
||||
log.error(resp)
|
||||
return resp
|
||||
|
||||
if not re.fullmatch(email_regex, u["email"]):
|
||||
resp = {
|
||||
"pass": False,
|
||||
"msg": "User " + u["username"] + " has invalid email: " + u["email"],
|
||||
}
|
||||
log.error(resp)
|
||||
return resp
|
||||
|
||||
if u["role"] not in ["admin", "manager", "teacher", "student"]:
|
||||
if u["role"] == "":
|
||||
resp = {
|
||||
"pass": False,
|
||||
"msg": "User " + u["username"] + " has no role assigned!",
|
||||
roles = []
|
||||
for role in sorted(app.admin.get_roles(), key=lambda k: k["name"]):
|
||||
log.error(role)
|
||||
roles.append(
|
||||
{
|
||||
"keycloak_id": role["id"],
|
||||
"id": role["name"],
|
||||
"name": role["name"],
|
||||
"description": role.get("description", ""),
|
||||
}
|
||||
log.error(resp)
|
||||
return resp
|
||||
resp = {
|
||||
"pass": False,
|
||||
"msg": "User " + u["username"] + " has invalid role: " + u["role"],
|
||||
}
|
||||
log.error(resp)
|
||||
return resp
|
||||
return {"pass": True, "msg": ""}
|
||||
|
||||
|
||||
@app.route("/api/dashboard/<item>", methods=["PUT"])
|
||||
# @login_required
|
||||
def dashboard_put(item):
|
||||
if item == "colours":
|
||||
try:
|
||||
data = request.get_json(force=True)
|
||||
dashboard.update_colours(data)
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
return json.dumps({"colours": data}), 200, {"Content-Type": "application/json"}
|
||||
if item == "menu":
|
||||
try:
|
||||
data = request.get_json(force=True)
|
||||
dashboard.update_menu(data)
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
return json.dumps(data), 200, {"Content-Type": "application/json"}
|
||||
if item == "logo":
|
||||
dashboard.update_logo(request.files["croppedImage"])
|
||||
return json.dumps({}), 200, {"Content-Type": "application/json"}
|
||||
if item == "background":
|
||||
dashboard.update_background(request.files["croppedImage"])
|
||||
return json.dumps({}), 200, {"Content-Type": "application/json"}
|
||||
return (
|
||||
json.dumps(
|
||||
{
|
||||
"error": "update_error",
|
||||
"msg": "Error updating item " + item + "\n" + traceback.format_exc(),
|
||||
}
|
||||
),
|
||||
500,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
|
||||
|
||||
@app.route("/api/legal/<item>", methods=["GET", "POST"])
|
||||
# @login_required
|
||||
def legal_put(item):
|
||||
if request.method == "GET":
|
||||
if item == "legal":
|
||||
lang = request.args.get("lang")
|
||||
return (
|
||||
json.dumps({"html": "<b>Legal</b><br>This works! in lang: " + lang}),
|
||||
200,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
# if item == "privacy":
|
||||
# return json.dumps({ "html": "<b>Privacy policy</b><br>This works!"}), 200, {'Content-Type': 'application/json'}
|
||||
return json.dumps(roles), 200, {"Content-Type": "application/json"}
|
||||
|
||||
|
||||
@app.route("/ddapi/role/users", methods=["POST"])
|
||||
@has_token
|
||||
def ddapi_role_users():
|
||||
if request.method == "POST":
|
||||
if item == "legal":
|
||||
data = None
|
||||
data = request.get_json(force=True)
|
||||
sorted_users = sorted(app.admin.get_mix_users(), key=lambda k: k["username"])
|
||||
if data.get("id", data.get("name")):
|
||||
role_users = [
|
||||
user_parser(user)
|
||||
for user in sorted_users
|
||||
if data.get("id", data.get("name")) in user["roles"]
|
||||
]
|
||||
elif data.get("keycloak_id"):
|
||||
try:
|
||||
data = request.json
|
||||
html = data["html"]
|
||||
lang = data["lang"]
|
||||
id = [
|
||||
r["id"]
|
||||
for r in app.admin.get_roles()
|
||||
if r["id"] == data.get("keycloak_id")
|
||||
][0]
|
||||
role_users = [
|
||||
user_parser(user) for user in sorted_users if id in user["roles"]
|
||||
]
|
||||
except:
|
||||
log.error(traceback.format_exc())
|
||||
return json.dumps(data), 200, {"Content-Type": "application/json"}
|
||||
# if item == "privacy":
|
||||
# data = None
|
||||
# try:
|
||||
# data = request.json
|
||||
# html = data["html"]
|
||||
# lang = data["lang"]
|
||||
# except:
|
||||
# log.error(traceback.format_exc())
|
||||
# return json.dumps(data), 200, {'Content-Type': 'application/json'}
|
||||
raise Error("not_found", "Role keycloak_id not found in system")
|
||||
else:
|
||||
raise Error("bad_request", "Incorrect data requested.")
|
||||
return json.dumps(role_users), 200, {"Content-Type": "application/json"}
|
||||
|
||||
|
||||
## INDIVIDUAL ACTIONS
|
||||
@app.route("/ddapi/user", methods=["POST"])
|
||||
@app.route("/ddapi/user/<user_ddid>", methods=["PUT", "GET", "DELETE"])
|
||||
@has_token
|
||||
def ddapi_user(user_ddid=None):
|
||||
if request.method == "GET":
|
||||
user = app.admin.get_user_username(user_ddid)
|
||||
if not user:
|
||||
raise Error("not_found", "User id not found")
|
||||
return json.dumps(user_parser(user)), 200, {"Content-Type": "application/json"}
|
||||
if request.method == "DELETE":
|
||||
user = app.admin.get_user_username(user_ddid)
|
||||
if not user:
|
||||
raise Error("not_found", "User id not found")
|
||||
app.admin.delete_user(user["id"])
|
||||
return json.dumps({}), 200, {"Content-Type": "application/json"}
|
||||
if request.method == "POST":
|
||||
data = request.get_json(force=True)
|
||||
if not app.validators["user"].validate(data):
|
||||
raise Error(
|
||||
"bad_request",
|
||||
"Data validation for user failed: ",
|
||||
+str(app.validators["user"].errors),
|
||||
traceback.format_exc(),
|
||||
)
|
||||
|
||||
if app.admin.get_user_username(data["username"]):
|
||||
raise Error("conflict", "User id already exists")
|
||||
keycloak_id = app.admin.add_user(data)
|
||||
return (
|
||||
json.dumps({"keycloak_id": keycloak_id}),
|
||||
200,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
|
||||
if request.method == "PUT":
|
||||
user = app.admin.get_user_username(user_ddid)
|
||||
if not user:
|
||||
raise Error("not_found", "User id not found")
|
||||
data = request.get_json(force=True)
|
||||
if not app.validators["user_update"].validate(data):
|
||||
raise Error(
|
||||
"bad_request",
|
||||
"Data validation for user failed: "
|
||||
+ str(app.validators["user_update"].errors),
|
||||
traceback.format_exc(),
|
||||
)
|
||||
data = {**user, **data}
|
||||
data = app.validators["user_update"].normalized(data)
|
||||
data = {**data, **{"username": user_ddid}}
|
||||
data["roles"] = [data.pop("role")]
|
||||
data["firstname"] = data.pop("first")
|
||||
data["lastname"] = data.pop("last")
|
||||
app.admin.user_update(data)
|
||||
if data.get("password"):
|
||||
app.admin.user_update_password(
|
||||
user["id"], data["password"], data["password_temporary"]
|
||||
)
|
||||
return json.dumps({}), 200, {"Content-Type": "application/json"}
|
||||
|
||||
|
||||
@app.route("/ddapi/username/<old_user_ddid>/<new_user_did>", methods=["PUT"])
|
||||
@has_token
|
||||
def ddapi_username(old_user_ddid, new_user_did):
|
||||
user = app.admin.get_user_username(user_ddid)
|
||||
if not user:
|
||||
raise Error("not_found", "User id not found")
|
||||
# user = app.admin.update_user_username(old_user_ddid,new_user_did)
|
||||
return json.dumps("Not implemented yet!"), 419, {"Content-Type": "application/json"}
|
||||
|
||||
|
||||
@app.route("/ddapi/group", methods=["POST"])
|
||||
@app.route("/ddapi/group/<id>", methods=["GET", "POST", "DELETE"])
|
||||
# @app.route("/api/group/<group_id>", methods=["PUT", "GET", "DELETE"])
|
||||
@has_token
|
||||
def ddapi_group(id=None):
|
||||
if request.method == "GET":
|
||||
group = app.admin.get_group_by_name(id)
|
||||
if not group:
|
||||
Error("not found", "Group id not found")
|
||||
return (
|
||||
json.dumps(group_parser(group)),
|
||||
200,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
if request.method == "POST":
|
||||
data = request.get_json(force=True)
|
||||
if not app.validators["group"].validate(data):
|
||||
raise Error(
|
||||
"bad_request",
|
||||
"Data validation for group failed: "
|
||||
+ str(app.validators["group"].errors),
|
||||
traceback.format_exc(),
|
||||
)
|
||||
data = app.validators["group"].normalized(data)
|
||||
data["parent"] = data["parent"] if data["parent"] != "" else None
|
||||
|
||||
if app.admin.get_group_by_name(id):
|
||||
raise Error("conflict", "Group id already exists")
|
||||
|
||||
path = app.admin.add_group(data)
|
||||
# log.error(path)
|
||||
# keycloak_id = app.admin.get_group_by_name(id)["id"]
|
||||
# log.error()
|
||||
return (
|
||||
json.dumps({"keycloak_id": None}),
|
||||
200,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
if request.method == "DELETE":
|
||||
group = app.admin.get_group_by_name(id)
|
||||
if not group:
|
||||
raise Error("not_found", "Group id not found")
|
||||
app.admin.delete_group_by_id(group["id"])
|
||||
return json.dumps({}), 200, {"Content-Type": "application/json"}
|
||||
|
||||
|
||||
def user_parser(user):
|
||||
return {
|
||||
"keycloak_id": user["id"],
|
||||
"id": user["username"],
|
||||
"username": user["username"],
|
||||
"enabled": user["enabled"],
|
||||
"first": user["first"],
|
||||
"last": user["last"],
|
||||
"role": user["roles"][0] if len(user["roles"]) else None,
|
||||
"email": user["email"],
|
||||
"groups": user.get("groups", user["keycloak_groups"]),
|
||||
"quota": user["quota"],
|
||||
"quota_used_bytes": user["quota_used_bytes"],
|
||||
}
|
||||
|
||||
|
||||
def group_parser(group):
|
||||
return {
|
||||
"keycloak_id": group["id"],
|
||||
"id": group["name"],
|
||||
"name": group["name"].split(".")[-1],
|
||||
"path": group["path"],
|
||||
"description": group.get("description", ""),
|
||||
}
|
||||
|
||||
|
||||
def filter_users(users, text):
|
||||
return [
|
||||
user
|
||||
for user in users
|
||||
if text in user["username"]
|
||||
or text in user["first"]
|
||||
or text in user["last"]
|
||||
or text in user["email"]
|
||||
]
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import json
|
||||
import logging as log
|
||||
import os
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
|
@ -11,12 +12,11 @@ from flask import request
|
|||
|
||||
from admin import app
|
||||
|
||||
from .decorators import is_internal, is_internal_or_has_token
|
||||
from .decorators import is_internal
|
||||
|
||||
import socket
|
||||
|
||||
@app.route("/api/internal/users", methods=["GET"])
|
||||
@is_internal_or_has_token
|
||||
@is_internal
|
||||
def internal_users():
|
||||
log.error(socket.gethostbyname("isard-apps-wordpress"))
|
||||
if request.method == "GET":
|
||||
|
@ -31,7 +31,7 @@ def internal_users():
|
|||
|
||||
|
||||
@app.route("/api/internal/users/filter", methods=["POST"])
|
||||
@is_internal_or_has_token
|
||||
@is_internal
|
||||
def internal_users_search():
|
||||
if request.method == "POST":
|
||||
data = request.get_json(force=True)
|
||||
|
@ -42,7 +42,7 @@ def internal_users_search():
|
|||
|
||||
|
||||
@app.route("/api/internal/groups", methods=["GET"])
|
||||
@is_internal_or_has_token
|
||||
@is_internal
|
||||
def internal_groups():
|
||||
if request.method == "GET":
|
||||
sorted_groups = sorted(app.admin.get_mix_groups(), key=lambda k: k["name"])
|
||||
|
@ -61,7 +61,7 @@ def internal_groups():
|
|||
|
||||
|
||||
@app.route("/api/internal/group/users", methods=["POST"])
|
||||
@is_internal_or_has_token
|
||||
@is_internal
|
||||
def internal_group_users():
|
||||
if request.method == "POST":
|
||||
data = request.get_json(force=True)
|
||||
|
@ -80,7 +80,7 @@ def internal_group_users():
|
|||
|
||||
|
||||
@app.route("/api/internal/roles", methods=["GET"])
|
||||
@is_internal_or_has_token
|
||||
@is_internal
|
||||
def internal_roles():
|
||||
if request.method == "GET":
|
||||
roles = []
|
||||
|
@ -98,7 +98,7 @@ def internal_roles():
|
|||
|
||||
|
||||
@app.route("/api/internal/role/users", methods=["POST"])
|
||||
@is_internal_or_has_token
|
||||
@is_internal
|
||||
def internal_role_users():
|
||||
if request.method == "POST":
|
||||
data = request.get_json(force=True)
|
|
@ -1,15 +1,17 @@
|
|||
#!flask/bin/python
|
||||
# coding=utf-8
|
||||
|
||||
import json
|
||||
import logging as log
|
||||
import os
|
||||
import socket
|
||||
from functools import wraps
|
||||
import json
|
||||
import os
|
||||
from jose import jwt
|
||||
from ..auth.tokens import get_header_jwt_payload
|
||||
|
||||
from flask import redirect, request, url_for
|
||||
from flask_login import current_user, logout_user
|
||||
from jose import jwt
|
||||
|
||||
from ..auth.tokens import get_header_jwt_payload
|
||||
|
||||
|
||||
def is_admin(fn):
|
||||
|
@ -34,22 +36,29 @@ def is_internal(fn):
|
|||
## but we should check if it is internal net and not haproxy
|
||||
if socket.gethostbyname("isard-apps-wordpress") == remote_addr:
|
||||
return fn(*args, **kwargs)
|
||||
logout_user()
|
||||
return redirect(url_for("login"))
|
||||
return (
|
||||
json.dumps(
|
||||
{
|
||||
"error": "unauthorized",
|
||||
"msg": "Unauthorized access",
|
||||
}
|
||||
),
|
||||
401,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
|
||||
return decorated_view
|
||||
|
||||
|
||||
def has_token(fn):
|
||||
@wraps(fn)
|
||||
def decorated(*args, **kwargs):
|
||||
payload = get_header_jwt_payload()
|
||||
# if payload.get("role_id") != "admin":
|
||||
# maintenance()
|
||||
kwargs["payload"] = payload
|
||||
return fn(*args, **kwargs)
|
||||
|
||||
return decorated
|
||||
|
||||
|
||||
def is_internal_or_has_token(fn):
|
||||
@wraps(fn)
|
||||
def decorated_view(*args, **kwargs):
|
||||
|
@ -58,32 +67,22 @@ def is_internal_or_has_token(fn):
|
|||
if "X-Forwarded-For" in request.headers
|
||||
else request.remote_addr.split(",")[0]
|
||||
)
|
||||
## Now only checks if it is wordpress container,
|
||||
## but we should check if it is internal net and not haproxy
|
||||
valid_jwt = False
|
||||
try:
|
||||
payload = get_header_jwt_payload()
|
||||
valid_jwt = True
|
||||
except:
|
||||
valid_jwt = False
|
||||
if valid_jwt:
|
||||
return fn(*args, **kwargs)
|
||||
else:
|
||||
return (
|
||||
json.dumps(
|
||||
{
|
||||
"error": "unauthorized",
|
||||
"msg": "Unauthorized access",
|
||||
}
|
||||
),
|
||||
401,
|
||||
{"Content-Type": "application/json"},
|
||||
)
|
||||
payload = get_header_jwt_payload()
|
||||
|
||||
if socket.gethostbyname("isard-apps-wordpress") == remote_addr:
|
||||
return fn(*args, **kwargs)
|
||||
else:
|
||||
logout_user()
|
||||
return redirect(url_for("login"))
|
||||
payload = get_header_jwt_payload()
|
||||
return fn(*args, **kwargs)
|
||||
|
||||
return decorated_view
|
||||
return decorated_view
|
||||
|
||||
|
||||
def login_or_token(fn):
|
||||
@wraps(fn)
|
||||
def decorated_view(*args, **kwargs):
|
||||
if current_user.is_authenticated:
|
||||
return fn(*args, **kwargs)
|
||||
payload = get_header_jwt_payload()
|
||||
return fn(*args, **kwargs)
|
||||
|
||||
return decorated_view
|
||||
|
|
|
@ -41,9 +41,9 @@ if __name__ == "__main__":
|
|||
app,
|
||||
host="0.0.0.0",
|
||||
port=9000,
|
||||
debug=False,
|
||||
# ssl_context="adhoc",
|
||||
# async_mode="threading",
|
||||
) # , logger=logger, engineio_logger=engineio_logger)
|
||||
debug=True,
|
||||
)
|
||||
# ssl_context="adhoc",
|
||||
# async_mode="threading",
|
||||
# ) # , logger=logger, engineio_logger=engineio_logger)
|
||||
# , cors_allowed_origins="*"
|
||||
# /usr/lib/python3.8/site-packages/certifi
|
||||
|
|
Loading…
Reference in New Issue