Update saml auth patch

This commit is contained in:
eabdullin 2023-05-23 10:08:08 +03:00
parent 26ca1342d0
commit a67a7e01b4
2 changed files with 179 additions and 265 deletions

View File

@ -1,264 +1,178 @@
diff -crB src.orig/webfaf/config.py src/webfaf/config.py diff -aruN faf-2.6.1/src/webfaf/config.py faf-2.6.1.alma/src/webfaf/config.py
*** src.orig/webfaf/config.py 2021-02-11 11:31:10.000000000 +0200 --- faf-2.6.1/src/webfaf/config.py 2023-01-11 17:35:16
--- src/webfaf/config.py 2021-02-11 11:43:27.000000000 +0200 +++ faf-2.6.1.alma/src/webfaf/config.py 2023-05-23 09:47:50
*************** @@ -13,6 +13,7 @@
*** 10,15 ****
--- 10,16 ----
class Config:
+ SAML_CONFIG_DIR = '/etc/faf/saml'
class Config(object): DEBUG = False
+ SAML_CONFIG_DIR = '/etc/faf/saml' TESTING = False
DEBUG = False SECRET_KEY = "NOT_A_RANDOM_STRING"
TESTING = False diff -aruN faf-2.6.1/src/webfaf/login.py faf-2.6.1.alma/src/webfaf/login.py
SECRET_KEY = 'NOT_A_RANDOM_STRING' --- faf-2.6.1/src/webfaf/login.py 2023-01-11 17:35:16
diff -crB src.orig/webfaf/login.py src/webfaf/login.py +++ faf-2.6.1.alma/src/webfaf/login.py 2023-05-23 09:50:08
*** src.orig/webfaf/login.py 2021-02-11 11:31:10.000000000 +0200 @@ -1,58 +1,112 @@
--- src/webfaf/login.py 2021-02-11 11:44:30.000000000 +0200 import flask
*************** from openid_teams import teams
*** 1,56 **** from werkzeug.wrappers import Response
import flask +from urllib.parse import urlparse, urljoin
from openid_teams import teams
from werkzeug.wrappers import Response from pyfaf.storage.user import User
from webfaf.webfaf_main import db, oid, app
from pyfaf.storage.user import User from webfaf.utils import fed_raw_name
from webfaf.webfaf_main import db, oid, app +from onelogin.saml2.auth import OneLogin_Saml2_Auth
from webfaf.utils import fed_raw_name
-login = flask.Blueprint("login", __name__)
! login = flask.Blueprint("login", __name__) +login = flask.Blueprint('login', __name__)
! @login.route("/login/", methods=["GET"]) -@login.route("/login/", methods=["GET"])
! @oid.loginhandler -@oid.loginhandler
! def do_login() -> Response: -def do_login() -> Response:
! if flask.g.user is not None: - if flask.g.user is not None:
! return flask.redirect(oid.get_next_url()) - return flask.redirect(oid.get_next_url())
! +def init_saml_auth(req):
! teams_req = teams.TeamsRequest(app.config["OPENID_PRIVILEGED_TEAMS"]) + saml_config_dir = app.config['SAML_CONFIG_DIR']
! return oid.try_login("https://id.fedoraproject.org/", + return OneLogin_Saml2_Auth(req, custom_base_path=saml_config_dir)
! ask_for=["email"], extensions=[teams_req])
! - teams_req = teams.TeamsRequest(app.config["OPENID_PRIVILEGED_TEAMS"])
! - return oid.try_login("https://id.fedoraproject.org/",
! @oid.after_login - ask_for=["email"], extensions=[teams_req])
! def create_or_login(resp) -> Response:
! flask.session["openid"] = resp.identity_url +def prepare_flask_request(req):
! username = fed_raw_name(resp.identity_url) + parsed_url = urlparse(req.url)
+ return {
privileged = False + 'https': 'on' if req.scheme == 'https' else 'off',
! # "lp" is the namespace for openid-teams + 'http_host': req.host,
! if "lp" in resp.extensions and any(group in app.config["OPENID_PRIVILEGED_TEAMS"] + 'server_port': parsed_url.port,
! for group in resp.extensions["lp"].teams): + 'script_name': req.script_root + req.path,
privileged = True + 'get_data': req.args.copy(),
+ 'post_data': req.form.copy(),
user = db.session.query(User).filter(User.username == username).first() + 'query_string': req.query_string
if not user: # create + }
! user = User(username=username, mail=resp.email, privileged=privileged)
else: -@oid.after_login
! user.mail = resp.email -def create_or_login(resp) -> Response:
user.privileged = privileged - flask.session["openid"] = resp.identity_url
- username = fed_raw_name(resp.identity_url)
db.session.add(user)
db.session.commit() +@login.route('/login/', methods=('GET',))
flask.flash(u"Welcome, {0}".format(user.username)) +def do_login():
flask.g.user = user + """SSO authentication entry-point."""
+ req = prepare_flask_request(flask.request)
! if flask.request.url_root == oid.get_next_url(): + comeback_url = urljoin(flask.request.host_url, '/faf/summary')
! return flask.redirect(flask.url_for("summary.index")) + return_to = flask.request.args.get('return_to')
+ if return_to:
! return flask.redirect(oid.get_next_url()) + comeback_url += '?return_to={0}'.format(return_to)
+ auth = init_saml_auth(req)
+ return flask.redirect(auth.login(comeback_url))
@login.route("/logout/") +
def do_logout() -> Response: +
! flask.session.pop("openid", None) +@login.route('/acs/', methods=('POST',))
flask.flash(u"You were signed out") +def acs():
! return flask.redirect(oid.get_next_url()) + req = prepare_flask_request(flask.request)
--- 1,113 ---- + auth = init_saml_auth(req)
import flask + auth.process_response()
from openid_teams import teams + errors = auth.get_errors()
from werkzeug.wrappers import Response + if errors:
+ from urllib.parse import urlparse, urljoin + return flask.helpers.make_response('Error: {0}'.format(', '.join(errors)), 500)
+ elif not auth.is_authenticated():
from pyfaf.storage.user import User + return flask.helpers.make_response('Error: authentication failed', 401)
from webfaf.webfaf_main import db, oid, app + user_attrs = auth.get_attributes()
from webfaf.utils import fed_raw_name +
+ from onelogin.saml2.auth import OneLogin_Saml2_Auth privileged = False
- # "lp" is the namespace for openid-teams
! login = flask.Blueprint('login', __name__) - if "lp" in resp.extensions and any(group in app.config["OPENID_PRIVILEGED_TEAMS"]
- for group in resp.extensions["lp"].teams):
+ is_admin = False
! def init_saml_auth(req): + if any(group in user_attrs['groups'] for group in ('alma_retrace_admin', 'admins')):
! saml_config_dir = app.config['SAML_CONFIG_DIR'] privileged = True
! return OneLogin_Saml2_Auth(req, custom_base_path=saml_config_dir) + is_admin = True
!
! + email = user_attrs['email'][0]
! def prepare_flask_request(req): + username = email.split('@')[0]
! parsed_url = urlparse(req.url) user = db.session.query(User).filter(User.username == username).first()
! return { if not user: # create
! 'https': 'on' if req.scheme == 'https' else 'off', - user = User(username=username, mail=resp.email, privileged=privileged)
! 'http_host': req.host, + user = User(username=username, mail=email, privileged=privileged, admin=is_admin)
! 'server_port': parsed_url.port, else:
! 'script_name': req.script_root + req.path, - user.mail = resp.email
! 'get_data': req.args.copy(), + user.mail = email
! 'post_data': req.form.copy(), user.privileged = privileged
! 'query_string': req.query_string + user.admin = is_admin
! }
! db.session.add(user)
! db.session.commit()
! @login.route('/login/', methods=('GET',)) flask.flash(u"Welcome, {0}".format(user.username))
! def do_login(): - # This is okay: https://flask.palletsprojects.com/en/2.0.x/api/#flask.g
! """SSO authentication entry-point.""" - # pylint: disable=assigning-non-slot
! req = prepare_flask_request(flask.request) flask.g.user = user
! comeback_url = urljoin(flask.request.host_url, '/faf/summary') + flask.session['username'] = username
! return_to = flask.request.args.get('return_to')
! if return_to: - if flask.request.url_root == oid.get_next_url():
! comeback_url += '?return_to={0}'.format(return_to) - return flask.redirect(flask.url_for("summary.index"))
! auth = init_saml_auth(req) + return flask.redirect(flask.request.form['RelayState'])
! return flask.redirect(auth.login(comeback_url))
! - return flask.redirect(oid.get_next_url())
!
! @login.route('/acs/', methods=('POST',)) +@login.route('/metadata/', methods=('GET',))
! def acs(): +def metadata():
! req = prepare_flask_request(flask.request) + """
! auth = init_saml_auth(req) + Returns the Build System IDP provider metadata.
! auth.process_response()
! errors = auth.get_errors() + Returns
! if errors: + -------
! return flask.helpers.make_response('Error: {0}'.format(', '.join(errors)), 500) + flask.wrappers.Response
! elif not auth.is_authenticated(): + IDP provider metadata response.
! return flask.helpers.make_response('Error: authentication failed', 401) +
! user_attrs = auth.get_attributes() + See Also
+ --------
privileged = False + https://en.wikipedia.org/wiki/SAML_Metadata#Identity_Provider_Metadata
! is_admin = False + """
! if any(group in user_attrs['groups'] for group in ('alma_retrace_admin', 'admins')): + req = prepare_flask_request(flask.request)
privileged = True + auth = init_saml_auth(req)
+ is_admin = True + settings = auth.get_settings()
+ metadata = settings.get_sp_metadata()
+ email = user_attrs['email'][0] + errors = settings.validate_metadata(metadata)
+ username = email.split('@')[0] + if errors:
user = db.session.query(User).filter(User.username == username).first() + rsp = flask.make_response(', '.join(errors), 500)
if not user: # create + else:
! user = User(username=username, mail=email, privileged=privileged, admin=is_admin) + rsp = flask.make_response(metadata, 200)
else: + rsp.headers['Content-Type'] = 'text/xml'
! user.mail = email + return rsp
user.privileged = privileged +
+ user.admin = is_admin +
@login.route("/logout/")
db.session.add(user) def do_logout() -> Response:
db.session.commit() - flask.session.pop("openid", None)
flask.flash(u"Welcome, {0}".format(user.username)) + flask.session.pop("username", None)
flask.g.user = user flask.flash(u"You were signed out")
+ flask.session['username'] = username - return flask.redirect(oid.get_next_url())
+ return flask.redirect(flask.url_for("summary.index"))
! return flask.redirect(flask.request.form['RelayState']) diff -aruN faf-2.6.1/src/webfaf/webfaf_main.py faf-2.6.1.alma/src/webfaf/webfaf_main.py
--- faf-2.6.1/src/webfaf/webfaf_main.py 2023-01-11 17:35:16
! +++ faf-2.6.1.alma/src/webfaf/webfaf_main.py 2023-05-23 09:53:06
! @login.route('/metadata/', methods=('GET',)) @@ -170,8 +170,8 @@
! def metadata(): @app.before_request
! """ def before_request() -> None:
! Returns the Build System IDP provider metadata. flask.g.user = None
! - if "openid" in flask.session:
! Returns - username = fed_raw_name(flask.session["openid"])
! ------- + if "username" in flask.session:
! flask.wrappers.Response + username = flask.session["username"]
! IDP provider metadata response. flask.g.user = (db.session.query(User)
! .filter(User.username == username)
! See Also .first())
! -------- diff -aruN faf-2.6.1/tests/test_webfaf/test_user.py faf-2.6.1.alma/tests/test_webfaf/test_user.py
! https://en.wikipedia.org/wiki/SAML_Metadata#Identity_Provider_Metadata --- faf-2.6.1/tests/test_webfaf/test_user.py 2023-01-11 17:35:16
! """ +++ faf-2.6.1.alma/tests/test_webfaf/test_user.py 2023-05-23 09:53:28
! req = prepare_flask_request(flask.request) @@ -24,7 +24,7 @@
! auth = init_saml_auth(req) self.db.session.commit()
! settings = auth.get_settings()
! metadata = settings.get_sp_metadata() with self.app.session_transaction() as session:
! errors = settings.validate_metadata(metadata) - session['openid'] = 'faker1'
! if errors: + session['username'] = 'faker1'
! rsp = flask.make_response(', '.join(errors), 500)
! else: def test_delete_user_data(self):
! rsp = flask.make_response(metadata, 200) """
! rsp.headers['Content-Type'] = 'text/xml'
! return rsp
@login.route("/logout/")
def do_logout() -> Response:
! flask.session.pop("username", None)
flask.flash(u"You were signed out")
! return flask.redirect(flask.url_for("summary.index"))
!
diff -crB src.orig/webfaf/templates/base.html src/webfaf/templates/base.html
*** src.orig/webfaf/templates/base.html 2021-02-11 11:31:12.000000000 +0200
--- src/webfaf/templates/base.html 2021-02-11 13:09:20.000000000 +0200
***************
*** 71,77 ****
</ul>
{% if config['OPENID_ENABLED'] %}
<ul class="nav navbar-nav navbar-utility">
- <li><a href="https://apps.fedoraproject.org/notifications" class="fa fa-bell notifications" title="Notifications preferences"></a></li>
{% if g.user %}
<li class="dropdown">
<a id="user-dropdown" role="button" data-toggle="dropdown">
--- 71,76 ----
diff -crB src.orig/webfaf/webfaf_main.py src/webfaf/webfaf_main.py
*** src.orig/webfaf/webfaf_main.py 2021-02-11 11:31:15.000000000 +0200
--- src/webfaf/webfaf_main.py 2021-02-10 19:55:36.000000000 +0200
***************
*** 14,20 ****
import munch
from ratelimitingfilter import RateLimitingFilter
from werkzeug.local import LocalProxy
! from werkzeug.middleware.proxy_fix import ProxyFix
from pyfaf.storage.user import User
from pyfaf.storage import OpSysComponent, Report
--- 14,23 ----
import munch
from ratelimitingfilter import RateLimitingFilter
from werkzeug.local import LocalProxy
! try:
! from werkzeug.middleware.proxy_fix import ProxyFix
! except ModuleNotFoundError:
! from werkzeug.contrib.fixers import ProxyFix
from pyfaf.storage.user import User
from pyfaf.storage import OpSysComponent, Report
***************
*** 167,174 ****
@app.before_request
def before_request() -> None:
flask.g.user = None
! if "openid" in flask.session:
! username = fed_raw_name(flask.session["openid"])
flask.g.user = (db.session.query(User)
.filter(User.username == username)
.first())
--- 170,177 ----
@app.before_request
def before_request() -> None:
flask.g.user = None
! if "username" in flask.session:
! username = flask.session["username"]
flask.g.user = (db.session.query(User)
.filter(User.username == username)
.first())
diff -crB tests.orig/test_webfaf/test_user.py tests/test_webfaf/test_user.py
*** tests.orig/test_webfaf/test_user.py 2021-02-11 11:31:18.000000000 +0200
--- tests/test_webfaf/test_user.py 2021-02-11 11:55:50.000000000 +0200
***************
*** 24,30 ****
self.db.session.commit()
with self.app.session_transaction() as session:
! session['openid'] = 'faker1'
def test_delete_user_data(self):
"""
--- 24,30 ----
self.db.session.commit()
with self.app.session_transaction() as session:
! session['username'] = 'faker1'
def test_delete_user_data(self):
"""

View File

@ -571,7 +571,7 @@ systemd services for the Celery task queue.
%setup -q %setup -q
# AlmaLinux support patches # AlmaLinux support patches
%patch0 -p1 %patch0 -p1
%patch1 -p0 %patch1 -p1
NOCONFIGURE=1 ./autogen.sh NOCONFIGURE=1 ./autogen.sh