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

View File

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