diff --git a/profiles/__init__.py b/profiles/__init__.py index 5e8c66b..b3b833b 100644 --- a/profiles/__init__.py +++ b/profiles/__init__.py @@ -1,15 +1,17 @@ import os +import csh_ldap +import ldap import sentry_sdk + +from flask import Flask, flash, jsonify, redirect, render_template, request +from flask_pyoidc.flask_pyoidc import OIDCAuthentication +from flask_pyoidc.provider_configuration import ProviderConfiguration +from flask_sqlalchemy import SQLAlchemy +from flask_uploads import IMAGES, UploadSet, configure_uploads from sentry_sdk.integrations.flask import FlaskIntegration from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration from sqlalchemy.exc import SQLAlchemyError -import ldap -import csh_ldap -from flask import Flask, render_template, jsonify, request, redirect, flash -from flask_sqlalchemy import SQLAlchemy -from flask_pyoidc.flask_pyoidc import OIDCAuthentication -from flask_uploads import UploadSet, configure_uploads, IMAGES app = Flask(__name__) @@ -19,46 +21,44 @@ else: app.config.from_pyfile(os.path.join(os.getcwd(), "config.env.py")) -auth = OIDCAuthentication(app, issuer=app.config["OIDC_ISSUER"], - client_registration_info=app.config["OIDC_CLIENT_CONFIG"]) +auth = OIDCAuthentication( + { + "default": ProviderConfiguration( + issuer=app.config["OIDC_ISSUER"], + client_registration_info=app.config["OIDC_CLIENT_CONFIG"], + ) + }, + app, +) # Sentry # pylint: disable=abstract-class-instantiated sentry_sdk.init( - dsn=app.config['SENTRY_DSN'], - integrations=[FlaskIntegration(), SqlalchemyIntegration()] + dsn=app.config["SENTRY_DSN"], + integrations=[FlaskIntegration(), SqlalchemyIntegration()], ) # LDAP -_ldap = csh_ldap.CSHLDAP(app.config['LDAP_BIND_DN'], app.config['LDAP_BIND_PASS']) +_ldap = csh_ldap.CSHLDAP(app.config["LDAP_BIND_DN"], app.config["LDAP_BIND_PASS"]) -photos = UploadSet('photos', IMAGES) +photos = UploadSet("photos", IMAGES) -app.config['UPLOADED_PHOTOS_DEST'] = 'static/img' +app.config["UPLOADED_PHOTOS_DEST"] = "static/img" configure_uploads(app, photos) # Import ldap model after instantiating object # pylint: disable=wrong-import-position +from profiles.ldap import (BadQueryError, _ldap_get_group_members, + get_gravatar, get_image, ldap_get_active_members, + ldap_get_all_members, ldap_get_current_students, + ldap_get_eboard, ldap_get_group_desc, + ldap_get_groups, ldap_get_intro_members, + ldap_get_member, ldap_get_onfloor_members, + ldap_get_year, ldap_is_active, ldap_is_rtp, + ldap_search_members, ldap_update_profile, + proxy_image) from profiles.utils import before_request, get_member_info, process_image -from profiles.ldap import(ldap_update_profile, - ldap_get_member, - ldap_is_active, - _ldap_get_group_members, - ldap_get_active_members, - ldap_get_intro_members, - ldap_get_onfloor_members, - ldap_get_current_students, - ldap_get_all_members, - ldap_get_groups, - ldap_get_group_desc, - ldap_get_eboard, - ldap_search_members, - ldap_get_year, - ldap_is_rtp, - get_image, - get_gravatar, - proxy_image, - BadQueryError) +# pylint: enable=wrong-import-position @app.route("/", methods=["GET"]) @@ -72,16 +72,14 @@ def home(info=None): @auth.oidc_auth @before_request def user(uid=None, info=None): - return render_template("profile.html", - info=info, - member_info=get_member_info(uid)) + return render_template("profile.html", info=info, member_info=get_member_info(uid)) @app.route("/results", methods=["POST"]) @auth.oidc_auth @before_request def results(): - searched = request.form['query'] + searched = request.form["query"] return redirect(f"/search/{searched}", 302) @@ -94,10 +92,9 @@ def search(searched=None, info=None): members = ldap_search_members(searched) if len(members) == 1: return redirect("/user/" + members[0].uid, 302) - return render_template("listing.html", - info=info, - title="Search Results: "+searched, - members=members) + return render_template( + "listing.html", info=info, title="Search Results: " + searched, members=members + ) @app.route("/group/<_group>", methods=["GET"]) @@ -107,47 +104,47 @@ def group(_group=None, info=None): group_desc = ldap_get_group_desc(_group) if _group == "eboard": - return render_template("listing.html", - info=info, - title=group_desc, - members=ldap_get_eboard()) + return render_template( + "listing.html", info=info, title=group_desc, members=ldap_get_eboard() + ) - return render_template("listing.html", - info=info, - title=group_desc, - members=_ldap_get_group_members(_group)) + return render_template( + "listing.html", + info=info, + title=group_desc, + members=_ldap_get_group_members(_group), + ) @app.route("/year/<_year>", methods=["GET"]) @auth.oidc_auth @before_request def year(_year=None, info=None): - return render_template("listing.html", - info=info, - title="Year: "+_year, - members=ldap_get_year(_year)) + return render_template( + "listing.html", info=info, title="Year: " + _year, members=ldap_get_year(_year) + ) @app.route("/update", methods=["POST"]) @auth.oidc_auth @before_request def update(info=None): - if 'photo' in request.form: - process_image(request.form['photo'][22:], info['uid']) + if "photo" in request.form: + process_image(request.form["photo"][22:], info["uid"]) get_image.cache_clear() - ldap_update_profile(request.json, info['uid']) + ldap_update_profile(request.json, info["uid"]) return jsonify({"success": True}), 200 -@app.route('/upload', methods=['POST']) +@app.route("/upload", methods=["POST"]) @auth.oidc_auth @before_request def upload(info=None): - if 'photo' in request.form: - process_image(request.form['photo'][22:], info['uid']) + if "photo" in request.form: + process_image(request.form["photo"][22:], info["uid"]) get_image.cache_clear() - return redirect('/', 302) + return redirect("/", 302) @app.route("/logout") @@ -161,11 +158,11 @@ def image(uid): return get_image(uid) -@app.route('/clearcache') +@app.route("/clearcache") @auth.oidc_auth @before_request def clear_cache(info=None): - if not ldap_is_rtp(info['user_obj']): + if not ldap_is_rtp(info["user_obj"]): return redirect("/") ldap_get_active_members.cache_clear() @@ -182,7 +179,7 @@ def clear_cache(info=None): get_gravatar.cache_clear() proxy_image.cache_clear() - flash('Cache cleared!') + flash("Cache cleared!") return redirect(request.referrer, 302) @@ -195,7 +192,7 @@ def handle_internal_error(e): raise e.original_exception -@app.route('/api/v1/healthcheck', methods=["GET"]) +@app.route("/api/v1/healthcheck", methods=["GET"]) def health_check(): # Return codes # 200 - Success diff --git a/requirements.in b/requirements.in index 9822a31..ae817b2 100644 --- a/requirements.in +++ b/requirements.in @@ -3,7 +3,7 @@ ddtrace~=1.1.4 Flask==1.1.4 Flask-Migrate==3.1.0 flask-optimize==0.2.9.8 -Flask-pyoidc==1.3.0 +Flask-pyoidc==3.14.3 Flask-SQLAlchemy==2.5.1 gunicorn==22.0.0 pip-tools~=6.6.2 diff --git a/requirements.txt b/requirements.txt index 4eddee7..344d465 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,16 +4,14 @@ # # pip-compile requirements.in # -alabaster==0.7.12 - # via oic alembic==1.7.7 # via flask-migrate +annotated-types==0.7.0 + # via pydantic astroid==3.2.4 # via pylint attrs==21.4.0 # via ddtrace -beaker==1.11.0 - # via oic blinker==1.4 # via sentry-sdk certifi==2024.7.4 @@ -29,13 +27,15 @@ click==7.1.2 # flask # pip-tools cryptography==43.0.1 - # via pyopenssl + # via oic csh-ldap==2.4.0 # via -r requirements.in ddsketch==2.0.3 # via ddtrace ddtrace==1.1.4 # via -r requirements.in +defusedxml==0.7.1 + # via oic dill==0.3.5.1 # via pylint dnspython==2.6.1 @@ -53,7 +53,7 @@ flask-migrate==3.1.0 # via -r requirements.in flask-optimize==0.2.9.8 # via -r requirements.in -flask-pyoidc==1.3.0 +flask-pyoidc==3.14.3 # via -r requirements.in flask-sqlalchemy==2.5.1 # via @@ -62,9 +62,7 @@ flask-sqlalchemy==2.5.1 flask-uploads @ git+https://github.com/maxcountryman/flask-uploads@master # via -r requirements.in future==0.18.3 - # via - # oic - # pyjwkest + # via pyjwkest greenlet==2.0.2 # via sqlalchemy gunicorn==22.0.0 @@ -73,6 +71,8 @@ htmlmin==0.1.12 # via flask-optimize idna==3.7 # via requests +importlib-resources==6.4.5 + # via flask-pyoidc isort==5.10.1 # via pylint itsdangerous==1.1.0 @@ -90,7 +90,7 @@ markupsafe==2.1.2 # mako mccabe==0.7.0 # via pylint -oic==0.11.0.1 +oic==1.6.1 # via flask-pyoidc packaging==21.3 # via @@ -120,6 +120,12 @@ pycryptodomex==3.19.1 # via # oic # pyjwkest +pydantic==2.9.2 + # via pydantic-settings +pydantic-core==2.23.4 + # via pydantic +pydantic-settings==2.5.2 + # via oic pygravatar==0.0.6 # via -r requirements.in pyjwkest==1.4.2 @@ -128,15 +134,16 @@ pylint==3.2.7 # via -r requirements.in pymysql==1.1.1 # via -r requirements.in -pyopenssl==22.0.0 - # via oic pyparsing==3.0.9 # via packaging +python-dotenv==1.0.1 + # via pydantic-settings python-ldap==3.4.0 # via csh-ldap requests==2.32.2 # via # -r requirements.in + # flask-pyoidc # oic # pyjwkest sentry-sdk[flask]==1.5.12 @@ -145,7 +152,6 @@ six==1.16.0 # via # ddsketch # ddtrace - # oic # pyjwkest sqlalchemy==1.4.36 # via @@ -164,6 +170,8 @@ tomlkit==0.13.2 typing-extensions==4.12.2 # via # astroid + # pydantic + # pydantic-core # pylint urllib3==1.26.19 # via @@ -175,6 +183,8 @@ werkzeug==1.0.1 # flask wheel==0.38.1 # via pip-tools +zipp==3.20.2 + # via importlib-resources # The following packages are considered to be unsafe in a requirements file: # pip