Select Git revision
Bjarke Madsen authored
user.py 4.94 KiB
import logging
from typing import Any, Union
from flask import Blueprint, jsonify, request
from flask_login import current_user, AnonymousUserMixin, login_required # type: ignore
from sqlalchemy import select
from compendium_v2.auth.session_management import admin_required
from compendium_v2.db import db
from compendium_v2.db.auth_model import User, ROLES
from compendium_v2.db.model import NREN
from compendium_v2.routes import common
routes = Blueprint('user', __name__)
logger = logging.getLogger(__name__)
USER_RESPONSE_SCHEMA = {
'$schema': 'http://json-schema.org/draft-07/schema#',
'definitions': {
'permissions': {
'type': 'object',
'properties': {
'admin': {'type': 'boolean'},
'active': {'type': 'boolean'},
},
'required': ['admin', 'active'],
'additionalProperties': False
},
'user': {
'type': 'object',
'properties': {
'name': {'type': ['string', 'null']},
'email': {'type': ['string', 'null']},
'permissions': {'$ref': '#/definitions/permissions'},
'id': {'type': 'uuid'},
'role': {'type': 'string'},
'oidc_sub': {'type': 'string'},
'nrens': {
'type': 'array',
'items': {'type': 'string'}
}
},
'required': ['permissions'],
'additionalProperties': False
}
},
'type': 'array',
'items': {'$ref': '#/definitions/charging'}
}
def _extract_user(user: Union[User, AnonymousUserMixin]):
if isinstance(user, AnonymousUserMixin):
return {
'permissions': {
'admin': False,
'active': False,
}
}
return {
'name': user.fullname,
'email': user.email,
'permissions': {
'admin': user.roles == ROLES.admin,
'active': user.active,
},
'id': user.id,
'role': user.roles.value,
'oidc_sub': user.oidc_sub,
'nrens': [nren.name for nren in user.nrens]
}
@routes.route('/', methods=['GET'])
@common.require_accepts_json
def current_user_view() -> Any:
"""
handler for /api/user/ requests
response will be formatted as:
.. asjson::
compendium_v2.routes.user.USER_RESPONSE_SCHEMA
:return:
"""
return jsonify(_extract_user(current_user))
@routes.route('/list', methods=['GET'])
@common.require_accepts_json
@login_required
@admin_required
def all_users_view() -> Any:
# TODO schema and docstring
entries = [_extract_user(user) for user in db.session.scalars(
select(User).order_by(User.email)).unique()]
return jsonify(entries)
@routes.route('/', methods=['PUT'])
@common.require_accepts_json
@login_required
@admin_required
def update_user_view() -> Any:
"""
Handler for updating user information via PUT request.
Request data should be in JSON format with the fields you want to update.
The response will be formatted the same way as in the current_user_view.
Example JSON request data:
{
"name": "Updated Name",
"email": "updated@example.com",
"roles": ["role1", "role2"],
"active": False
}
:return:
"""
def _update_user_data(user: User, update_data: dict):
new_roles = update_data.get('roles', user.roles.value)
if new_roles != user.roles.value:
if user == current_user:
return jsonify({'success': False, 'message': 'Cannot change your own role.'}), 400
user.roles = new_roles
if 'active' in update_data:
_active = bool(update_data['active'])
if _active != user.active:
if user == current_user:
return jsonify({'success': False, 'message': 'Cannot deactivate yourself.'}), 400
user.active = _active
nrens = update_data.get('nrens', None)
if nrens is not None:
new_nrens = None
try:
new_nrens = db.session.scalars(select(NREN).filter(NREN.id.in_(update_data['nrens']))).all()
user.nrens = [nren for nren in new_nrens]
db.session.commit()
except Exception:
return jsonify({'success': False, 'message': 'No valid NREN IDs provided.'}), 400
return jsonify({'success': True, 'message': 'User updated successfully'})
body = request.get_json()
if not body:
return jsonify({"success": False, 'message': 'Invalid request'}), 400
user_id = body.get("id")
if not user_id:
return jsonify({"success": False, 'message': 'No user ID provided in the request data.'}), 400
user = db.session.execute(select(User).filter_by(id=user_id)).scalar()
if not user:
return jsonify({"success": False, 'message': 'User not found.'}), 404
return _update_user_data(user, body)