changeset 6492:b5551ad26fa3

vcs: dedup auth code between Hg and Git middleware There's a lot of verbatim duplicated code in the Hg and Git VCS middleware. This attempts to deduplicate a bit of it. The _authorize function is a bit awkward, but for now the goal is simply to remove duplicated code, not improving program structure and design. As such, the code in _authorize is almost a verbatim copy of the code removed in the two controllers.
author Søren Løvborg <sorenl@unity3d.com>
date Wed, 04 Jan 2017 23:01:48 +0100
parents 701f0a3f9616
children fffb4e73700e
files kallithea/lib/base.py kallithea/lib/middleware/simplegit.py kallithea/lib/middleware/simplehg.py
diffstat 3 files changed, 81 insertions(+), 118 deletions(-) [+]
line wrap: on
line diff
--- a/kallithea/lib/base.py	Mon Jan 02 23:16:32 2017 +0100
+++ b/kallithea/lib/base.py	Wed Jan 04 23:01:48 2017 +0100
@@ -179,6 +179,9 @@
 
 
 class BaseVCSController(object):
+    """Base controller for handling Mercurial/Git protocol requests
+    (coming from a VCS client, and not a browser).
+    """
 
     def __init__(self, application, config):
         self.application = application
@@ -189,6 +192,78 @@
         self.authenticate = BasicAuth('', auth_modules.authenticate,
                                       config.get('auth_ret_code'))
 
+    def _authorize(self, environ, start_response, action, repo_name, ip_addr):
+        """Authenticate and authorize user.
+
+        Since we're dealing with a VCS client and not a browser, we only
+        support HTTP basic authentication, either directly via raw header
+        inspection, or by using container authentication to delegate the
+        authentication to the web server.
+
+        Returns (user, None) on successful authentication and authorization.
+        Returns (None, wsgi_app) to send the wsgi_app response to the client.
+        """
+        anonymous_user = User.get_default_user(cache=True)
+        user = anonymous_user
+        if anonymous_user.active:
+            # ONLY check permissions if the user is activated
+            anonymous_perm = self._check_permission(action, anonymous_user,
+                                                    repo_name, ip_addr)
+        else:
+            anonymous_perm = False
+
+        if not anonymous_user.active or not anonymous_perm:
+            if not anonymous_user.active:
+                log.debug('Anonymous access is disabled, running '
+                          'authentication')
+
+            if not anonymous_perm:
+                log.debug('Not enough credentials to access this '
+                          'repository as anonymous user')
+
+            username = None
+            #==============================================================
+            # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
+            # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
+            #==============================================================
+
+            # try to auth based on environ, container auth methods
+            log.debug('Running PRE-AUTH for container based authentication')
+            pre_auth = auth_modules.authenticate('', '', environ)
+            if pre_auth is not None and pre_auth.get('username'):
+                username = pre_auth['username']
+            log.debug('PRE-AUTH got %s as username', username)
+
+            # If not authenticated by the container, running basic auth
+            if not username:
+                self.authenticate.realm = \
+                    safe_str(self.config['realm'])
+                result = self.authenticate(environ)
+                if isinstance(result, str):
+                    paste.httpheaders.AUTH_TYPE.update(environ, 'basic')
+                    paste.httpheaders.REMOTE_USER.update(environ, result)
+                    username = result
+                else:
+                    return None, result.wsgi_application
+
+            #==============================================================
+            # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
+            #==============================================================
+            try:
+                user = User.get_by_username_or_email(username)
+                if user is None or not user.active:
+                    return None, webob.exc.HTTPForbidden()
+            except Exception:
+                log.error(traceback.format_exc())
+                return None, webob.exc.HTTPInternalServerError()
+
+            #check permissions for this repository
+            perm = self._check_permission(action, user, repo_name, ip_addr)
+            if not perm:
+                return None, webob.exc.HTTPForbidden()
+
+        return user, None
+
     def _handle_request(self, environ, start_response):
         raise NotImplementedError()
 
--- a/kallithea/lib/middleware/simplegit.py	Mon Jan 02 23:16:32 2017 +0100
+++ b/kallithea/lib/middleware/simplegit.py	Wed Jan 04 23:01:48 2017 +0100
@@ -95,64 +95,9 @@
         #======================================================================
         # CHECK PERMISSIONS
         #======================================================================
-        anonymous_user = User.get_default_user(cache=True)
-        user = anonymous_user
-        if anonymous_user.active:
-            # ONLY check permissions if the user is activated
-            anonymous_perm = self._check_permission(action, anonymous_user,
-                                                    repo_name, ip_addr)
-        else:
-            anonymous_perm = False
-
-        if not anonymous_user.active or not anonymous_perm:
-            if not anonymous_user.active:
-                log.debug('Anonymous access is disabled, running '
-                          'authentication')
-
-            if not anonymous_perm:
-                log.debug('Not enough credentials to access this '
-                          'repository as anonymous user')
-
-            username = None
-            #==============================================================
-            # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
-            # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
-            #==============================================================
-
-            # try to auth based on environ, container auth methods
-            log.debug('Running PRE-AUTH for container based authentication')
-            pre_auth = auth_modules.authenticate('', '', environ)
-            if pre_auth is not None and pre_auth.get('username'):
-                username = pre_auth['username']
-            log.debug('PRE-AUTH got %s as username', username)
-
-            # If not authenticated by the container, running basic auth
-            if not username:
-                self.authenticate.realm = \
-                    safe_str(self.config['realm'])
-                result = self.authenticate(environ)
-                if isinstance(result, str):
-                    AUTH_TYPE.update(environ, 'basic')
-                    REMOTE_USER.update(environ, result)
-                    username = result
-                else:
-                    return result.wsgi_application(environ, start_response)
-
-            #==============================================================
-            # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
-            #==============================================================
-            try:
-                user = User.get_by_username_or_email(username)
-                if user is None or not user.active:
-                    return HTTPForbidden()(environ, start_response)
-            except Exception:
-                log.error(traceback.format_exc())
-                return HTTPInternalServerError()(environ, start_response)
-
-            #check permissions for this repository
-            perm = self._check_permission(action, user, repo_name, ip_addr)
-            if not perm:
-                return HTTPForbidden()(environ, start_response)
+        user, response_app = self._authorize(environ, start_response, action, repo_name, ip_addr)
+        if response_app is not None:
+            return response_app(environ, start_response)
 
         # extras are injected into UI object and later available
         # in hooks executed by kallithea
--- a/kallithea/lib/middleware/simplehg.py	Mon Jan 02 23:16:32 2017 +0100
+++ b/kallithea/lib/middleware/simplehg.py	Wed Jan 04 23:01:48 2017 +0100
@@ -32,7 +32,6 @@
 import logging
 import traceback
 
-from paste.httpheaders import REMOTE_USER, AUTH_TYPE
 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError, \
     HTTPNotAcceptable, HTTPBadRequest
 from kallithea.model.db import User
@@ -43,7 +42,6 @@
 from kallithea.lib.utils import make_ui, is_valid_repo, ui_sections
 from kallithea.lib.vcs.utils.hgcompat import RepoError, hgweb_mod
 from kallithea.lib.exceptions import HTTPLockedRC
-from kallithea.lib import auth_modules
 
 log = logging.getLogger(__name__)
 
@@ -103,64 +101,9 @@
         #======================================================================
         # CHECK PERMISSIONS
         #======================================================================
-        anonymous_user = User.get_default_user(cache=True)
-        user = anonymous_user
-        if anonymous_user.active:
-            # ONLY check permissions if the user is activated
-            anonymous_perm = self._check_permission(action, anonymous_user,
-                                                    repo_name, ip_addr)
-        else:
-            anonymous_perm = False
-
-        if not anonymous_user.active or not anonymous_perm:
-            if not anonymous_user.active:
-                log.debug('Anonymous access is disabled, running '
-                          'authentication')
-
-            if not anonymous_perm:
-                log.debug('Not enough credentials to access this '
-                          'repository as anonymous user')
-
-            username = None
-            #==============================================================
-            # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
-            # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
-            #==============================================================
-
-            # try to auth based on environ, container auth methods
-            log.debug('Running PRE-AUTH for container based authentication')
-            pre_auth = auth_modules.authenticate('', '', environ)
-            if pre_auth is not None and pre_auth.get('username'):
-                username = pre_auth['username']
-            log.debug('PRE-AUTH got %s as username', username)
-
-            # If not authenticated by the container, running basic auth
-            if not username:
-                self.authenticate.realm = \
-                    safe_str(self.config['realm'])
-                result = self.authenticate(environ)
-                if isinstance(result, str):
-                    AUTH_TYPE.update(environ, 'basic')
-                    REMOTE_USER.update(environ, result)
-                    username = result
-                else:
-                    return result.wsgi_application(environ, start_response)
-
-            #==============================================================
-            # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME
-            #==============================================================
-            try:
-                user = User.get_by_username_or_email(username)
-                if user is None or not user.active:
-                    return HTTPForbidden()(environ, start_response)
-            except Exception:
-                log.error(traceback.format_exc())
-                return HTTPInternalServerError()(environ, start_response)
-
-            #check permissions for this repository
-            perm = self._check_permission(action, user, repo_name, ip_addr)
-            if not perm:
-                return HTTPForbidden()(environ, start_response)
+        user, response_app = self._authorize(environ, start_response, action, repo_name, ip_addr)
+        if response_app is not None:
+            return response_app(environ, start_response)
 
         # extras are injected into mercurial UI object and later available
         # in hg hooks executed by kallithea