#367 Add support for "kerberos_or_ssl" auth the same way as we do in ODCS.
Merged 2 months ago by jkaluza. Opened 2 months ago by jkaluza.
jkaluza/freshmaker ssl-auth  into  master

file modified
+44

@@ -88,6 +88,41 @@ 

      return user

  

  

+ @commit_on_success

+ def load_ssl_user_from_request(request):

+     """

+     Loads SSL user from current request.

+ 

+     SSL_CLIENT_VERIFY and SSL_CLIENT_S_DN needs to be set in

+     request.environ. This is set by frontend httpd mod_ssl module.

+     """

+     ssl_client_verify = request.environ.get('SSL_CLIENT_VERIFY')

+     if ssl_client_verify != 'SUCCESS':

+         raise Unauthorized('Cannot verify client: %s' % ssl_client_verify)

+ 

+     username = request.environ.get('SSL_CLIENT_S_DN')

+     if not username:

+         raise Unauthorized('Unable to get user information (DN) from client certificate')

+ 

+     user = User.find_user_by_name(username)

+     if not user:

+         user = User.create_user(username=username)

+ 

+     g.groups = []

+     g.user = user

+     return user

+ 

+ 

+ def load_krb_or_ssl_user_from_request(request):

+     """

+     Loads User using Kerberos or SSL auth.

+     """

+     if request.environ.get('REMOTE_USER'):

+         return load_krb_user_from_request(request)

+     else:

+         return load_ssl_user_from_request(request)

+ 

+ 

  def query_ldap_groups(uid):

      client = ldap.initialize(conf.auth_ldap_server)

      groups = client.search_s(conf.auth_ldap_group_base,

@@ -195,6 +230,15 @@ 

      elif backend == 'openidc':

          global load_openidc_user

          load_openidc_user = login_manager.request_loader(load_openidc_user)

+     elif backend == 'kerberos_or_ssl':

+         _validate_kerberos_config()

+         global load_krb_or_ssl_user_from_request

+         load_krb_or_ssl_user_from_request = login_manager.request_loader(

+             load_krb_or_ssl_user_from_request)

+     elif backend == 'ssl':

+         global load_ssl_user_from_request

+         load_ssl_user_from_request = login_manager.request_loader(

+             load_ssl_user_from_request)

      else:

          raise ValueError('Unknown backend name {0}.'.format(backend))

  

file modified
+112

@@ -33,11 +33,113 @@ 

  from freshmaker.auth import load_krb_user_from_request

  from freshmaker.auth import load_openidc_user

  from freshmaker.auth import query_ldap_groups

+ from freshmaker.auth import load_krb_or_ssl_user_from_request

+ from freshmaker.auth import load_ssl_user_from_request

  from freshmaker import app, db

  from freshmaker.models import User

  from tests.helpers import ModelsTestCase, FreshmakerTestCase

  

  

+ class TestLoadSSLUserFromRequest(ModelsTestCase):

+ 

+     def setUp(self):

+         super(TestLoadSSLUserFromRequest, self).setUp()

+ 

+         self.user = User(username='CN=tester1,L=prod,DC=example,DC=com')

+         db.session.add(self.user)

+         db.session.commit()

+ 

+     def test_create_new_user(self):

+         environ_base = {

+             'SSL_CLIENT_VERIFY': 'SUCCESS',

+             'SSL_CLIENT_S_DN': 'CN=client,L=prod,DC=example,DC=com',

+         }

+ 

+         with app.test_request_context(environ_base=environ_base):

+             load_ssl_user_from_request(flask.request)

+ 

+             expected_user = db.session.query(User).filter(

+                 User.username == 'CN=client,L=prod,DC=example,DC=com')[0]

+ 

+             self.assertEqual(expected_user.id, flask.g.user.id)

+             self.assertEqual(expected_user.username, flask.g.user.username)

+ 

+             # Ensure user's groups are set to empty list

+             self.assertEqual(0, len(flask.g.groups))

+ 

+     def test_return_existing_user(self):

+         environ_base = {

+             'SSL_CLIENT_VERIFY': 'SUCCESS',

+             'SSL_CLIENT_S_DN': self.user.username,

+         }

+ 

+         with app.test_request_context(environ_base=environ_base):

+             load_ssl_user_from_request(flask.request)

+ 

+             self.assertEqual(self.user.id, flask.g.user.id)

+             self.assertEqual(self.user.username, flask.g.user.username)

+ 

+             # Ensure user's groups are set to empty list

+             self.assertEqual(0, len(flask.g.groups))

+ 

+     def test_401_if_ssl_client_verify_not_success(self):

+         environ_base = {

+             'SSL_CLIENT_VERIFY': 'GENEROUS',

+             'SSL_CLIENT_S_DN': self.user.username,

+         }

+ 

+         with app.test_request_context(environ_base=environ_base):

+             with self.assertRaises(Unauthorized) as ctx:

+                 load_ssl_user_from_request(flask.request)

+             self.assertIn('Cannot verify client: GENEROUS',

+                           ctx.exception.description)

+ 

+     def test_401_if_cn_not_set(self):

+         environ_base = {

+             'SSL_CLIENT_VERIFY': 'SUCCESS',

+         }

+ 

+         with app.test_request_context(environ_base=environ_base):

+             with self.assertRaises(Unauthorized) as ctx:

+                 load_ssl_user_from_request(flask.request)

+             self.assertIn('Unable to get user information (DN) from client certificate',

+                           ctx.exception.description)

+ 

+ 

+ class TestLoadKrbOrSSLUserFromRequest(ModelsTestCase):

+ 

+     @patch("freshmaker.auth.load_ssl_user_from_request")

+     @patch("freshmaker.auth.load_krb_user_from_request")

+     def test_load_krb_or_ssl_user_from_request_remote_user(

+             self, load_krb_user, load_ssl_user):

+         load_krb_user.return_value = "krb_user"

+         load_ssl_user.return_value = "ssl_user"

+ 

+         environ_base = {

+             'REMOTE_USER': 'newuser@EXAMPLE.COM'

+         }

+ 

+         with app.test_request_context(environ_base=environ_base):

+             user = load_krb_or_ssl_user_from_request(flask.request)

+             self.assertEqual(user, "krb_user")

+ 

+     @patch("freshmaker.auth.load_ssl_user_from_request")

+     @patch("freshmaker.auth.load_krb_user_from_request")

+     def test_load_krb_or_ssl_user_from_request_ssl_client(

+             self, load_krb_user, load_ssl_user):

+         load_krb_user.return_value = "krb_user"

+         load_ssl_user.return_value = "ssl_user"

+ 

+         environ_base = {

+             'SSL_CLIENT_VERIFY': 'SUCCESS',

+             'SSL_CLIENT_S_DN': 'ssl_user',

+         }

+ 

+         with app.test_request_context(environ_base=environ_base):

+             user = load_krb_or_ssl_user_from_request(flask.request)

+             self.assertEqual(user, "ssl_user")

+ 

+ 

  class TestLoadKrbUserFromRequest(ModelsTestCase):

  

      @patch('freshmaker.auth.query_ldap_groups')

@@ -227,6 +329,16 @@ 

          self.login_manager.request_loader.assert_called_once_with(

              load_openidc_user)

  

+     def test_select_ssl_auth_backend(self):

+         init_auth(self.login_manager, 'ssl')

+         self.login_manager.request_loader.assert_called_once_with(

+             load_ssl_user_from_request)

+ 

+     def test_select_kerberos_or_ssl_auth_backend(self):

+         init_auth(self.login_manager, 'kerberos_or_ssl')

+         self.login_manager.request_loader.assert_called_once_with(

+             load_krb_or_ssl_user_from_request)

+ 

      def test_not_use_auth_backend(self):

          init_auth(self.login_manager, 'noauth')

          self.login_manager.request_loader.assert_not_called()

This is basically copy-paste of ODCS code which has been introduced
in https://pagure.io/odcs/pull-request/132. The code is taken
from ODCS master.

We need SSL client auth in order to allow monitoring using New Relic
which does not support keytabs.

rebased onto 6c26218

2 months ago

Pull-Request has been merged by jkaluza

2 months ago