tryd to fix a oid connect related bug, but there is still a BIG problem

This commit is contained in:
2020-07-24 16:45:18 +02:00
parent 1d4c4c8ec2
commit de398d189a
7 changed files with 86 additions and 30 deletions

View File

@@ -5,11 +5,12 @@
import logging import logging
import ssl import ssl
import sys import sys
import threading
from jinja2.exceptions import TemplateNotFound from jinja2.exceptions import TemplateNotFound
from backend import app, db from backend import app, db
from backend.cron import get_default_scheduler, add_default_jobs from backend.cron import get_default_scheduler, add_default_jobs, async_permanent_cron_recorder_checker
from backend.models import * from backend.models import *
from backend.models import room_model, recorder_model, RecorderCommand, Recorder from backend.models import room_model, recorder_model, RecorderCommand, Recorder
from backend.recorder_adapters import get_defined_recorder_adapters from backend.recorder_adapters import get_defined_recorder_adapters
@@ -17,6 +18,23 @@ from backend.tools.model_updater import update_recorder_models_database, create_
from backend.websocket.base import WebSocketBase from backend.websocket.base import WebSocketBase
def _start_initial_recorder_state_update(run_in_thread=True):
if run_in_thread:
thread = threading.Thread(target=async_permanent_cron_recorder_checker.check_object_state, args=())
thread.start()
else:
async_permanent_cron_recorder_checker.check_object_state() # initial check of all recorders
def _create_and_start_default_scheduler():
print("Starting Scheduler")
scheduler = get_default_scheduler()
add_default_jobs(scheduler)
scheduler.start()
return scheduler
def main(): def main():
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
@@ -44,10 +62,8 @@ def main():
except Exception as e: except Exception as e:
logging.critical(e) logging.critical(e)
print("Starting Scheduler") scheduler = _create_and_start_default_scheduler()
scheduler = get_default_scheduler() #_start_initial_recorder_state_update(run_in_thread=False)
add_default_jobs(scheduler)
scheduler.start()
wsb = WebSocketBase() wsb = WebSocketBase()
print("running websocket...(replaces normal app.run()") print("running websocket...(replaces normal app.run()")

View File

@@ -7,6 +7,7 @@ Login through API does not start a new session, but instead returns JWT.
""" """
import base64 import base64
import json import json
import logging
from pprint import pprint from pprint import pprint
import flask import flask
@@ -29,6 +30,8 @@ from backend.api import auth_api_bp, auth_api_providers_ns, auth_api_register_ns
from backend.auth import AUTH_PROVIDERS, oidc_auth from backend.auth import AUTH_PROVIDERS, oidc_auth
from backend.models.user_model import User, Group, BlacklistToken from backend.models.user_model import User, Group, BlacklistToken
logger = logging.getLogger("lrc.api.auth")
@auth_api_bp.route('/providers', methods=('GET',)) @auth_api_bp.route('/providers', methods=('GET',))
def get_auth_providers(): def get_auth_providers():
@@ -92,6 +95,7 @@ def login():
} }
return jsonify(token), 200 return jsonify(token), 200
# Endpoint for revoking the current users access token # Endpoint for revoking the current users access token
@auth_api_bp.route('/logout', methods=['GET', 'DELETE']) @auth_api_bp.route('/logout', methods=['GET', 'DELETE'])
@jwt_required @jwt_required
@@ -135,7 +139,7 @@ def create_or_retrieve_user_from_userinfo(userinfo):
user = User.get_by_identifier(email) user = User.get_by_identifier(email)
if user is not None: if user is not None:
app.logger.info("user found -> update user") logger.info("user found -> update user")
pprint(user.to_dict()) pprint(user.to_dict())
user.first_name = userinfo.get("given_name", "") user.first_name = userinfo.get("given_name", "")
user.last_name = userinfo.get("family_name", "") user.last_name = userinfo.get("family_name", "")
@@ -148,7 +152,7 @@ def create_or_retrieve_user_from_userinfo(userinfo):
last_name=userinfo.get("family_name", ""), external_user=True, last_name=userinfo.get("family_name", ""), external_user=True,
groups=user_groups) groups=user_groups)
app.logger.info("creating new user") logger.info("creating new user")
db.session.add(user) db.session.add(user)
db.session.commit() db.session.commit()
@@ -159,8 +163,11 @@ def create_or_retrieve_user_from_userinfo(userinfo):
@auth_api_bp.route('/oidc/<redirect_url>', methods=['GET']) @auth_api_bp.route('/oidc/<redirect_url>', methods=['GET'])
@oidc_auth.oidc_auth() @oidc_auth.oidc_auth()
def oidc(redirect_url=None): def oidc(redirect_url=None):
logger.debug("oidc auth endpoint:")
return "fuck!"
user = create_or_retrieve_user_from_userinfo(flask.session['userinfo']) user = create_or_retrieve_user_from_userinfo(flask.session['userinfo'])
if user is None: if user is None:
logger.error("Could not authenticate: could not find or create user.")
return "Could not authenticate: could not find or create user.", 401 return "Could not authenticate: could not find or create user.", 401
if current_app.config.get("AUTH_RETURN_EXTERNAL_JWT", False): if current_app.config.get("AUTH_RETURN_EXTERNAL_JWT", False):
token = jwt.encode(flask.session['id_token'], current_app.config['SECRET_KEY']) token = jwt.encode(flask.session['id_token'], current_app.config['SECRET_KEY'])
@@ -169,13 +176,13 @@ def oidc(redirect_url=None):
'access_token': create_access_token(identity=user, fresh=True), 'access_token': create_access_token(identity=user, fresh=True),
'refresh_token': create_refresh_token(identity=user) 'refresh_token': create_refresh_token(identity=user)
}) })
logger.info("Token: {}".format(token))
if redirect_url is None: if redirect_url is None:
redirect_url = request.headers.get("Referer") redirect_url = request.headers.get("Referer")
if redirect_url is None: if redirect_url is None:
redirect_url = request.args.get('redirect_url') redirect_url = request.args.get('redirect_url')
if redirect_url is None: if redirect_url is None:
redirect_url = "/" redirect_url = "/"
app.logger.info("Token: {}".format(token))
response = make_response(redirect(redirect_url)) response = make_response(redirect(redirect_url))
response.set_cookie('tokens', base64.b64encode(token.encode('utf-8'))) response.set_cookie('tokens', base64.b64encode(token.encode('utf-8')))
return response return response
@@ -189,8 +196,7 @@ def refresh():
as we do not actually verify a password in this endpoint.""" as we do not actually verify a password in this endpoint."""
jwt_identity = get_jwt_identity() jwt_identity = get_jwt_identity()
user = User.get_by_identifier(jwt_identity) user = User.get_by_identifier(jwt_identity)
app.logger.info("Refreshing token for " + str(user)) logger.info("Refreshing token for " + str(user))
new_token = create_access_token(identity=user, fresh=False) new_token = create_access_token(identity=user, fresh=False)
ret = {'access_token': new_token} ret = {'access_token': new_token}
return jsonify(ret), 200 return jsonify(ret), 200

View File

@@ -15,7 +15,6 @@ from backend.models.user_model import User
from . import auth_bp from . import auth_bp
from .oidc_config import PROVIDER_NAME, OIDC_PROVIDERS from .oidc_config import PROVIDER_NAME, OIDC_PROVIDERS
OIDCAuthentication.oidc_auth_orig = OIDCAuthentication.oidc_auth OIDCAuthentication.oidc_auth_orig = OIDCAuthentication.oidc_auth
OIDCAuthentication.oidc_logout_orig = OIDCAuthentication.oidc_logout OIDCAuthentication.oidc_logout_orig = OIDCAuthentication.oidc_logout
@@ -46,6 +45,7 @@ def create_or_retrieve_user_from_userinfo(userinfo):
if user is not None: if user is not None:
app.logger.info("user found") app.logger.info("user found")
app.logger.debug(f"user found: {email}")
user.last_seen = datetime.utcnow() user.last_seen = datetime.utcnow()
# TODO: update user! # TODO: update user!
db.session.commit() db.session.commit()
@@ -54,21 +54,24 @@ def create_or_retrieve_user_from_userinfo(userinfo):
user = User(email=email, first_name=userinfo.get("given_name", ""), user = User(email=email, first_name=userinfo.get("given_name", ""),
last_name=userinfo.get("family_name", "")) last_name=userinfo.get("family_name", ""))
app.logger.info("creating new user") app.logger.info(f"creating new user: {email}")
db.session.add(user) db.session.add(user)
db.session.commit() db.session.commit()
return user return user
@auth_bp.route('/oidc', methods=['GET']) @auth_bp.route('/oidc', methods=['GET'])
@oidc_auth.oidc_auth() @oidc_auth.oidc_auth()
def oidc(): def oidc():
user_session = UserSession(flask.session) user_session = UserSession(flask.session)
app.logger.info(user_session.userinfo) app.logger.info(user_session.userinfo)
user = create_or_retrieve_user_from_userinfo(user_session.userinfo) user = create_or_retrieve_user_from_userinfo(user_session.userinfo)
if user is None:
return ''
login_user(user) login_user(user)
app.logger.info(f"logged in user: {str(user)}")
app.logger.debug(f"id token: {str(user_session.id_token)}")
return jsonify(id_token=user_session.id_token, return jsonify(id_token=user_session.id_token,
access_token=flask.session['access_token'], access_token=flask.session['access_token'],
userinfo=user_session.userinfo) userinfo=user_session.userinfo)
@@ -78,3 +81,9 @@ def oidc():
def oidc_logout(): def oidc_logout():
oidc_auth.oidc_logout() oidc_auth.oidc_logout()
return redirect('/') return redirect('/')
@oidc_auth.error_view
def error(error=None, error_description=None):
app.logger.error(f"Something wwent wrong with OIDC auth error: {error}, message: {error_description}")
return jsonify({'error': error, 'message': error_description})

Binary file not shown.

View File

@@ -172,4 +172,4 @@ async_permanent_cron_recorder_checker = StateChecker(
for r in Recorder.get_all(): for r in Recorder.get_all():
async_permanent_cron_recorder_checker.add_object_to_state_check(r.id) async_permanent_cron_recorder_checker.add_object_to_state_check(r.id)
async_permanent_cron_recorder_checker.check_object_state() # initial check of all recorders

View File

@@ -1,4 +1,7 @@
from backend import LrcException from backend import LrcException
import logging
logger = logging.getLogger("lrc.tools.exception_decorator")
def exception_decorator(*exceptions): def exception_decorator(*exceptions):
@@ -8,6 +11,9 @@ def exception_decorator(*exceptions):
ret = func(*args, **kwargs) ret = func(*args, **kwargs)
return ret return ret
except exceptions as e: except exceptions as e:
logger.error(str(e))
raise LrcException(e) raise LrcException(e)
return new_func return new_func
return decorator return decorator

View File

@@ -11,6 +11,7 @@ from typing import Union
import requests import requests
from requests.auth import HTTPBasicAuth from requests.auth import HTTPBasicAuth
from requests.exceptions import ConnectionError
from multiprocessing.pool import ThreadPool from multiprocessing.pool import ThreadPool
from multiprocessing.context import TimeoutError from multiprocessing.context import TimeoutError
@@ -26,6 +27,8 @@ from backend.recorder_adapters.extron_smp import SMP35x
from backend.tools.recorder_streams_sanity_checks import check_frame_is_valid, check_if_audio_is_valid from backend.tools.recorder_streams_sanity_checks import check_frame_is_valid, check_if_audio_is_valid
from backend.tools.send_mail import send_error_mail, get_smtp_error_handler from backend.tools.send_mail import send_error_mail, get_smtp_error_handler
from backend.tools.exception_decorator import exception_decorator
logger = logging.getLogger("lrc.tools.simple_state_checker") logger = logging.getLogger("lrc.tools.simple_state_checker")
smtp_error_handler = get_smtp_error_handler(subject="Errors have been detected while checking recorder states!") smtp_error_handler = get_smtp_error_handler(subject="Errors have been detected while checking recorder states!")
@@ -50,6 +53,7 @@ agent_states_lock = threading.RLock()
agent_states = {} agent_states = {}
@exception_decorator(ConnectionError)
def get_service_url(service_type: str): def get_service_url(service_type: str):
if service_type in config['service_urls']: if service_type in config['service_urls']:
return config['service_urls'][service_type] return config['service_urls'][service_type]
@@ -64,19 +68,23 @@ def get_service_url(service_type: str):
return None return None
@exception_decorator(ConnectionError)
def get_calender(rec_id): def get_calender(rec_id):
params = {'agentid': rec_id} params = {'agentid': rec_id}
url = get_service_url('org.opencastproject.scheduler') + "/calendars" url = get_service_url('org.opencastproject.scheduler') + "/calendars"
res = session.get(url, params=params) res = session.get(url, params=params)
if res.ok: if res.ok:
return Calendar(res.text) return Calendar(res.text)
raise LrcException(res.text, res.status_code)
@exception_decorator(ConnectionError)
def get_capture_agents(): def get_capture_agents():
url = get_service_url("org.opencastproject.capture.admin") + "/agents.json" url = get_service_url("org.opencastproject.capture.admin") + "/agents.json"
res = session.get(url) res = session.get(url)
if res.ok: if res.ok:
return res.json()["agents"]["agent"] return res.json()["agents"]["agent"]
raise LrcException(res.text, res.status_code)
def get_recorder_details_old(): def get_recorder_details_old():
@@ -117,19 +125,25 @@ def get_recorder_adapter(recorder_info: Union[dict, Recorder]) -> RecorderAdapte
type = recorder_info.get("type") type = recorder_info.get("type")
except KeyError: except KeyError:
type = RecorderModel.get_by_id(recorder_info.get('recorder_model_id')).model_name type = RecorderModel.get_by_id(recorder_info.get('recorder_model_id')).model_name
try:
if "SMP" in type: if "SMP" in type:
rec = SMP35x(recorder_info.get('ip'), recorder_info.get('password')) rec = SMP35x(recorder_info.get('ip'), recorder_info.get('password'))
else: else:
rec = Epiphan(recorder_info.get('ip'), recorder_info.get("username"), recorder_info.get("password")) rec = Epiphan(recorder_info.get('ip'), recorder_info.get("username"), recorder_info.get("password"))
return rec return rec
except LrcException:
raise
def check_stream_sanity(recorder_agent: Union[Recorder, dict], recorder_adapter: RecorderAdapter = None): def check_stream_sanity(recorder_agent: Union[Recorder, dict], recorder_adapter: RecorderAdapter = None):
try:
if recorder_adapter is None: if recorder_adapter is None:
recorder_info = get_recorder_by_name(recorder_agent.get('name')) recorder_info = get_recorder_by_name(recorder_agent.get('name'))
recorder_adapter = get_recorder_adapter(recorder_info) recorder_adapter = get_recorder_adapter(recorder_info)
if not recorder_adapter.is_recording(): if not recorder_adapter.is_recording():
return True, "not recording, so there is no stream!", recorder_agent.get('name') return True, "not recording, so there is no stream!", recorder_agent.get('name')
except LrcException:
return False, "Could not determine if recorder is recording!", recorder_agent.get('name')
if recorder_agent.get('archive_stream1') is None and recorder_agent.get( if recorder_agent.get('archive_stream1') is None and recorder_agent.get(
'archive_stream2') is None: # fall back to default names and rtsp 'archive_stream2') is None: # fall back to default names and rtsp
archive_stream_1_url = "rtsp://{}/{}".format(recorder_adapter.address, Config.DEFAULT_ARCHIVE_STREAM_1_NAME) archive_stream_1_url = "rtsp://{}/{}".format(recorder_adapter.address, Config.DEFAULT_ARCHIVE_STREAM_1_NAME)
@@ -186,7 +200,12 @@ def check_capture_agent_state(recorder_agent: Union[Recorder, dict]):
return True, "Recorder is in offline / maintenance mode", recorder_agent.get('name') return True, "Recorder is in offline / maintenance mode", recorder_agent.get('name')
agent_state_error_msg = None agent_state_error_msg = None
logger.debug("Checking Agent {}".format(recorder_agent.get('name'))) logger.debug("Checking Agent {}".format(recorder_agent.get('name')))
try:
c = get_calender(recorder_agent.get('name')) c = get_calender(recorder_agent.get('name'))
except LrcException:
error_msg = "Could not get calender of recorder agent: {}!".format(recorder_agent.get('name'))
logger.fatal(error_msg)
return False, error_msg, recorder_agent.get('name')
is_recording_in_calendar = len(list(c.timeline.now())) >= 1 is_recording_in_calendar = len(list(c.timeline.now())) >= 1
if is_recording_in_calendar: if is_recording_in_calendar:
logger.info("{} has entry in Calender and should therefore be recording... checking now!".format( logger.info("{} has entry in Calender and should therefore be recording... checking now!".format(