move everything to backend folder for migration to dashboard repository

This commit is contained in:
Maarten de Waard 2022-10-12 13:38:51 +02:00
parent af6b006409
commit 92ec7c653d
No known key found for this signature in database
GPG key ID: 1D3E893A657CC8DA
89 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,2 @@
from .users import *
from .user_service import *

View file

@ -0,0 +1,195 @@
import ory_kratos_client
from ory_kratos_client.model.submit_self_service_recovery_flow_body \
import SubmitSelfServiceRecoveryFlowBody
from ory_kratos_client.api import v0alpha2_api as kratos_api
from config import KRATOS_ADMIN_URL
from database import db
from areas.apps import App, AppRole, AppsService
from areas.roles import Role, RoleService
from helpers import KratosApi
from flask import current_app
from helpers.error_handler import KratosError
kratos_admin_api_configuration = \
ory_kratos_client.Configuration(host=KRATOS_ADMIN_URL, discard_unknown_keys=True)
KRATOS_ADMIN = \
kratos_api.V0alpha2Api(ory_kratos_client.ApiClient(kratos_admin_api_configuration))
class UserService:
@staticmethod
def get_users():
res = KratosApi.get("/admin/identities").json()
userList = []
for r in res:
userList.append(UserService.__insertAppRoleToUser(r["id"], r))
return userList
@staticmethod
def get_user(id):
res = KratosApi.get("/admin/identities/{}".format(id)).json()
return UserService.__insertAppRoleToUser(id, res)
@staticmethod
def post_user(data):
kratos_data = {
"schema_id": "default",
"traits": {
"name": data["name"],
"email": data["email"],
},
}
res = KratosApi.post("/admin/identities", kratos_data).json()
if data["app_roles"]:
app_roles = data["app_roles"]
for ar in app_roles:
app = App.query.filter_by(slug=ar["name"]).first()
app_role = AppRole(
user_id=res["id"],
role_id=ar["role_id"] if "role_id" in ar else Role.NO_ACCESS_ROLE_ID,
app_id=app.id,
)
db.session.add(app_role)
db.session.commit()
else:
all_apps = AppsService.get_all_apps()
for app in all_apps:
app_role = AppRole(
user_id=res["id"],
role_id=Role.NO_ACCESS_ROLE_ID,
app_id=app.id,
)
db.session.add(app_role)
db.session.commit()
UserService.__start_recovery_flow(data["email"])
return UserService.get_user(res["id"])
@staticmethod
def __start_recovery_flow(email):
"""
Start a Kratos recovery flow for the user's email address.
This sends out an email to the user that explains to them how they can
set their password. Make sure the user exists inside Kratos before you
use this function.
:param email: Email to send recovery link to
:type email: str
"""
api_response = KRATOS_ADMIN.initialize_self_service_recovery_flow_without_browser()
flow = api_response['id']
# Submit the recovery flow to send an email to the new user.
submit_self_service_recovery_flow_body = \
SubmitSelfServiceRecoveryFlowBody(method="link", email=email)
api_response = KRATOS_ADMIN.submit_self_service_recovery_flow(flow,
submit_self_service_recovery_flow_body=
submit_self_service_recovery_flow_body)
@staticmethod
def put_user(id, user_editing_id, data):
kratos_data = {
"schema_id": "default",
"traits": {"email": data["email"], "name": data["name"]},
}
KratosApi.put("/admin/identities/{}".format(id), kratos_data)
is_admin = RoleService.is_user_admin(user_editing_id)
if is_admin and data["app_roles"]:
app_roles = data["app_roles"]
for ar in app_roles:
app = App.query.filter_by(slug=ar["name"]).first()
app_role = AppRole.query.filter_by(
user_id=id, app_id=app.id).first()
if app_role:
app_role.role_id = ar["role_id"] if "role_id" in ar else None
db.session.commit()
else:
appRole = AppRole(
user_id=id,
role_id=ar["role_id"] if "role_id" in ar else None,
app_id=app.id,
)
db.session.add(appRole)
db.session.commit()
return UserService.get_user(id)
@staticmethod
def delete_user(id):
app_role = AppRole.query.filter_by(user_id=id).all()
for ar in app_role:
db.session.delete(ar)
db.session.commit()
@staticmethod
def post_multiple_users(data):
# check if data is array
# for every item in array call Kratos
created_users = []
existing_users = []
creation_failed_users = []
for user_data in data['users']:
user_email = user_data["email"]
if not user_email:
return
try:
UserService.post_user(user_data)
current_app.logger.info(f"Batch create user: {user_email}")
created_users.append(user_email)
except KratosError as err:
status_code = err.args[1]
if status_code == 409:
existing_users.append(user_email)
elif status_code == 400:
creation_failed_users.append(user_email)
current_app.logger.error(
f"Exception calling Kratos: {err} on creating user: {user_email} {status_code}")
except Exception as error:
current_app.logger.error(
f"Exception: {error} on creating user: {user_email}")
creation_failed_users.append(user_email)
success_response = {}
existing_response = {}
failed_response = {}
if created_users:
success_response = {"users": created_users,
"message": f"{len(created_users)} users created"}
if existing_users:
existing_response = {
"users": existing_users, "message": f"{len(existing_users)} users already exist: {', '.join(existing_users)}"}
if creation_failed_users:
failed_response = {"users": creation_failed_users,
"message": f"{len(creation_failed_users)} users failed to create: {', '.join(creation_failed_users)}"}
return {"success": success_response, "existing": existing_response, "failed": failed_response}
@staticmethod
def __insertAppRoleToUser(userId, userRes):
apps = App.query.all()
app_roles = []
for app in apps:
tmp_app_role = AppRole.query.filter_by(
user_id=userId, app_id=app.id
).first()
app_roles.append(
{
"name": app.slug,
"role_id": tmp_app_role.role_id if tmp_app_role else None,
}
)
userRes["traits"]["app_roles"] = app_roles
return userRes

View file

@ -0,0 +1,101 @@
from flask import jsonify, request
from flask_jwt_extended import get_jwt, jwt_required
from flask_cors import cross_origin
from flask_expects_json import expects_json
from areas import api_v1
from helpers import KratosApi
from helpers.auth_guard import admin_required
from .validation import schema, schema_multiple
from .user_service import UserService
@api_v1.route("/users", methods=["GET"])
@jwt_required()
@cross_origin()
@admin_required()
def get_users():
res = UserService.get_users()
return jsonify(res)
@api_v1.route("/users/<string:id>", methods=["GET"])
@jwt_required()
@cross_origin()
@admin_required()
def get_user(id):
res = UserService.get_user(id)
return jsonify(res)
@api_v1.route("/users", methods=["POST"])
@jwt_required()
@cross_origin()
@expects_json(schema)
@admin_required()
def post_user():
data = request.get_json()
res = UserService.post_user(data)
return jsonify(res)
@api_v1.route("/users/<string:id>", methods=["PUT"])
@jwt_required()
@cross_origin()
@expects_json(schema)
@admin_required()
def put_user(id):
data = request.get_json()
user_id = __get_user_id_from_jwt()
res = UserService.put_user(id, user_id, data)
return jsonify(res)
@api_v1.route("/users/<string:id>", methods=["DELETE"])
@jwt_required()
@cross_origin()
@admin_required()
def delete_user(id):
res = KratosApi.delete("/identities/{}".format(id))
if res.status_code == 204:
UserService.delete_user(id)
return jsonify(), res.status_code
return jsonify(res.json()), res.status_code
@api_v1.route("/users-batch", methods=["POST"])
@jwt_required()
@cross_origin()
@expects_json(schema_multiple)
@admin_required()
def post_multiple_users():
"""Expects an array of user JSON schema in request body."""
data = request.get_json()
res = UserService.post_multiple_users(data)
return jsonify(res)
@api_v1.route("/me", methods=["GET"])
@jwt_required()
@cross_origin()
def get_personal_info():
user_id = __get_user_id_from_jwt()
res = UserService.get_user(user_id)
return jsonify(res)
@api_v1.route("/me", methods=["PUT"])
@jwt_required()
@cross_origin()
@expects_json(schema)
def update_personal_info():
data = request.get_json()
user_id = __get_user_id_from_jwt()
res = UserService.put_user(user_id, user_id, data)
return jsonify(res)
def __get_user_id_from_jwt():
claims = get_jwt()
return claims["user_id"]

View file

@ -0,0 +1,42 @@
import re
schema = {
"type": "object",
"properties": {
"email": {
"type": "string",
"description": "Email of the user",
"pattern": r"(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])",
"minLength": 1,
},
"app_roles": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name of the app",
"minLenght": 1,
},
"role_id": {
"type": ["integer", "null"],
"description": "Role of the user",
"minimum": 1,
},
},
"required": ["name", "role_id"],
},
},
},
"required": ["email", "app_roles"],
}
schema_multiple = {
"users": {
"type": "array",
"items": {
"$ref": schema
}
}
}