2022-02-19 22:16:13 +00:00
|
|
|
from authlib.integrations.base_client.errors import MissingTokenError
|
2020-06-01 21:43:10 +00:00
|
|
|
from flask import Blueprint, redirect, request, url_for, render_template
|
|
|
|
from flask import current_app, session
|
|
|
|
from flask import jsonify
|
2022-02-19 22:16:13 +00:00
|
|
|
from flask.typing import ResponseReturnValue
|
2020-06-01 21:43:10 +00:00
|
|
|
from flask_login import current_user, logout_user
|
|
|
|
from oauthlib.oauth2.rfc6749.errors import TokenExpiredError
|
2022-04-08 19:29:23 +00:00
|
|
|
from authlib.integrations.base_client.errors import InvalidTokenError
|
2023-03-17 07:52:33 +00:00
|
|
|
from ory_hydra_client.api.o_auth_2 import list_o_auth_2_clients, get_o_auth_2_client, set_o_auth_2_client, create_o_auth_2_client
|
|
|
|
from ory_hydra_client.models import OAuth20Client, GenericError
|
2023-03-18 11:00:40 +00:00
|
|
|
from typing import Optional, List
|
2022-07-15 08:53:06 +00:00
|
|
|
from collections.abc import Iterable
|
2023-03-18 11:00:40 +00:00
|
|
|
from http import HTTPStatus
|
|
|
|
import httpx
|
2022-02-19 09:21:00 +00:00
|
|
|
import logging
|
|
|
|
|
2022-06-17 11:38:49 +00:00
|
|
|
from ..model import db, User
|
2022-02-19 22:16:13 +00:00
|
|
|
from .oauth2 import redirect_login, oauth2
|
2022-02-19 09:21:00 +00:00
|
|
|
from ..form.admin import OAuth2ClientForm
|
2022-02-19 22:16:13 +00:00
|
|
|
from ..hydra import hydra_service
|
2020-06-01 21:43:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
admin_views = Blueprint('admin', __name__, url_prefix='/admin')
|
2022-02-19 09:21:00 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
2020-06-01 21:43:10 +00:00
|
|
|
|
|
|
|
|
2022-02-19 22:16:13 +00:00
|
|
|
def before_request() -> Optional[ResponseReturnValue]:
|
2020-06-01 21:43:10 +00:00
|
|
|
try:
|
2022-02-19 22:16:13 +00:00
|
|
|
resp = oauth2.custom.get('/userinfo')
|
2020-06-01 21:43:10 +00:00
|
|
|
data = resp.json()
|
2022-02-06 22:57:01 +00:00
|
|
|
if not current_user.is_authenticated or resp.status_code != 200:
|
2020-06-02 17:09:32 +00:00
|
|
|
return redirect_login()
|
2022-02-06 22:57:01 +00:00
|
|
|
if 'groups' not in data or 'admin' not in data['groups']:
|
2020-06-01 21:43:10 +00:00
|
|
|
return 'Not an admin', 403
|
2022-04-08 19:29:23 +00:00
|
|
|
except (MissingTokenError, InvalidTokenError):
|
2020-06-02 17:09:32 +00:00
|
|
|
return redirect_login()
|
2022-02-19 22:16:13 +00:00
|
|
|
return None
|
2020-06-01 21:43:10 +00:00
|
|
|
|
|
|
|
|
|
|
|
admin_views.before_request(before_request)
|
|
|
|
|
|
|
|
|
|
|
|
@admin_views.route('/', methods=['GET', 'POST'])
|
2022-02-20 15:53:24 +00:00
|
|
|
async def index() -> ResponseReturnValue:
|
2020-06-01 21:43:10 +00:00
|
|
|
return render_template('admin/index.html.j2')
|
|
|
|
|
|
|
|
|
|
|
|
@admin_views.route('/user', methods=['GET'])
|
2022-02-20 15:53:24 +00:00
|
|
|
async def users():
|
2022-07-15 08:53:06 +00:00
|
|
|
users = User.query.all() # type: Iterable[User]
|
2020-06-01 21:43:10 +00:00
|
|
|
return render_template('admin/users.html.j2', users=users)
|
|
|
|
|
|
|
|
|
|
|
|
@admin_views.route('/registrations', methods=['GET'])
|
2022-02-19 22:16:13 +00:00
|
|
|
def registrations() -> ResponseReturnValue:
|
2022-07-15 08:53:06 +00:00
|
|
|
users = User.query.filter_by(enabled=False).all() # type: Iterable[User]
|
2020-06-01 21:43:10 +00:00
|
|
|
return render_template('admin/registrations.html.j2', users=users)
|
|
|
|
|
|
|
|
|
|
|
|
@admin_views.route('/registration/<registration_id>', methods=['DELETE'])
|
2022-02-19 22:16:13 +00:00
|
|
|
def registration_delete(registration_id) -> ResponseReturnValue:
|
2022-07-15 08:53:06 +00:00
|
|
|
user = User.query.get(registration_id) # type: Optional[User]
|
2022-06-17 11:38:49 +00:00
|
|
|
if user is None:
|
2020-06-01 21:43:10 +00:00
|
|
|
return jsonify({}), 404
|
2022-06-17 11:38:49 +00:00
|
|
|
db.session.delete(user)
|
2020-06-01 21:43:10 +00:00
|
|
|
db.session.commit()
|
|
|
|
return jsonify({})
|
|
|
|
|
|
|
|
|
|
|
|
@admin_views.route('/registration/<registration_id>', methods=['PUT'])
|
2022-02-19 22:16:13 +00:00
|
|
|
def registration_accept(registration_id) -> ResponseReturnValue:
|
2022-07-15 08:53:06 +00:00
|
|
|
user = User.query.get(registration_id) # type: Optional[User]
|
|
|
|
if user is None:
|
|
|
|
return jsonify({'message':'user not found'}), 404
|
2022-06-17 11:38:49 +00:00
|
|
|
user.enabled = True
|
2020-06-01 21:43:10 +00:00
|
|
|
db.session.commit()
|
|
|
|
return jsonify({})
|
2022-02-19 09:21:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
@admin_views.route('/clients')
|
2022-02-20 15:53:24 +00:00
|
|
|
async def clients() -> ResponseReturnValue:
|
2023-03-18 11:00:40 +00:00
|
|
|
response = await list_o_auth_2_clients.asyncio_detailed(_client=hydra_service.hydra_client)
|
|
|
|
clients = response.parsed
|
|
|
|
if clients is None:
|
|
|
|
logger.error(f"could not fetch client list response {response}")
|
|
|
|
return 'internal error', 500
|
2022-02-19 09:21:00 +00:00
|
|
|
return render_template('admin/clients.html.j2', clients=clients)
|
|
|
|
|
|
|
|
@admin_views.route('/client/<client_id>', methods=['GET', 'POST'])
|
2022-02-20 15:53:24 +00:00
|
|
|
async def client(client_id: str) -> ResponseReturnValue:
|
2022-02-19 09:21:00 +00:00
|
|
|
|
2022-02-20 15:53:24 +00:00
|
|
|
client = await get_o_auth_2_client.asyncio(client_id, _client=hydra_service.hydra_client)
|
2022-02-19 22:16:13 +00:00
|
|
|
if client is None or isinstance( client, GenericError):
|
2022-02-19 09:21:00 +00:00
|
|
|
logger.error(f"oauth2 client not found with id: '{client_id}'")
|
|
|
|
return 'client not found', 404
|
|
|
|
|
|
|
|
form = OAuth2ClientForm(obj=client)
|
|
|
|
if form.validate_on_submit():
|
|
|
|
form.populate_obj(client)
|
|
|
|
|
2023-03-17 07:52:33 +00:00
|
|
|
client = await set_o_auth_2_client.asyncio(id=client_id ,json_body=client, _client=hydra_service.hydra_client)
|
2022-02-19 22:16:13 +00:00
|
|
|
if client is None or isinstance(client, GenericError):
|
2022-02-19 09:21:00 +00:00
|
|
|
logger.error(f"oauth2 client update failed: '{client_id}'")
|
|
|
|
return 'client update failed', 500
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return render_template('admin/client.html.j2', client=client, form=form)
|
|
|
|
|
|
|
|
|
|
|
|
@admin_views.route('/client_new', methods=['GET','POST'])
|
2022-02-20 15:53:24 +00:00
|
|
|
async def client_new() -> ResponseReturnValue:
|
2022-02-19 09:21:00 +00:00
|
|
|
|
2023-03-17 07:52:33 +00:00
|
|
|
client = OAuth20Client()
|
2022-02-19 09:21:00 +00:00
|
|
|
|
|
|
|
form = OAuth2ClientForm()
|
|
|
|
if form.validate_on_submit():
|
|
|
|
form.populate_obj(client)
|
|
|
|
|
2022-02-20 15:53:24 +00:00
|
|
|
resp_client = await create_o_auth_2_client.asyncio(json_body=client, _client=hydra_service.hydra_client)
|
2022-02-19 22:16:13 +00:00
|
|
|
if resp_client is None:
|
2023-04-15 13:52:08 +00:00
|
|
|
logger.error(f"oauth2 client created failed: '{client.client_id}'")
|
2022-02-19 09:21:00 +00:00
|
|
|
return 'internal error', 500
|
|
|
|
return redirect(url_for('.client', client_id=client.client_id))
|
|
|
|
|
|
|
|
|
|
|
|
return render_template('admin/client.html.j2', client=client, form=form)
|