Commits

Brant Young  committed 0ca96a2

New feature: associate with redmine project's repository permissions.

  • Participants
  • Parent commits 8681d26
  • Branches default

Comments (0)

Files changed (1)

File hgredmine.py

     def _send_challenge(self, req):
         req.header([('WWW-Authenticate', 'Basic realm="%s"' % self.realm)])
         raise ErrorResponse(HTTP_UNAUTHORIZED, 'List Redmine repositories is unauthorized')
-
-    def _get_member_perm(self, req, project_id):
-        """
-        Find member permissions from Redmine database.
-        
-        Redmine repository relate permissions:
-            - :manage_repository
-            - :browse_repository
-            - :view_changesets
-            - :commit_access
-        
-        @return (allow_pull, allow_push) tuple
-        """
-        user = req.env['REMOTE_USER']
-        
-        dbcur = self.db.cursor()
-        dbcur.execute('SELECT roles.permissions FROM users, projects, members, roles '
-                            'WHERE users.login=? AND projects.identifier=? '
-                            'AND projects.id = members.project_id AND users.id = members.user_id '
-                            'AND roles.id = members.role_id',
-                      (user, project_id)
-                     )
-        
-        row = dbcur.fetchone()
-        if not row:
-            # user doesn't have any permits
-            return (False, False)
-        
-        perms = row[0].splitlines()
-        
-        if '- :manage_repository' in perms or '- :commit_access' in perms:
-            return (True, True)
-        elif '- :view_changesets' in perms or '- :browse_repository' in perms:
-            return (True, False)
-        else:
-            return (False, False)
-        
-        
     
     def _user_login(self, req):
+        req.env['REMOTE_USER'] = None
+        
         header = req.env.get('HTTP_AUTHORIZATION')
         if not header or not header.startswith('Basic'):
             return False
         
         @return True / False
         """
-        self._user_login(req)
         return req.env.get('REMOTE_USER_ADMIN', 'f') == 't'
         
-    def _is_public_repo(self, project_id):
-        dbcur = self.db.cursor()
-        dbcur.execute('SELECT projects.is_public FROM projects '
-                            'WHERE projects.identifier=?',
-                      (project_id, )
-                     )
-        
-        row = dbcur.fetchone()
-        if not row:
-            return False
-            
-        return row[0] == 't'
-        
-    def _set_perms(self, req, project_id):
-        allow_read = ''
-        allow_push = ''
-        deny_read = ''
-        deny_push = ''
-        
-        is_public = self._is_public_repo(project_id)
-        
-        if not self._user_login(req): # anonymous user
-            if is_public:
-                allow_read = '*'
-                deny_push = '*'
-            else:
-                allow_read = '*'
-                deny_push = '*'
-                
-                self._send_challenge(req)
-        else: # Redmine member
-            # check perm from redmine database
-            perm = self._get_member_perm(req, project_id)
-            
-            if perm == (False, False):
-                allow_read = '*'
-                deny_push = '*'
-                self._send_challenge(req)
-            elif perm == (True, False):
-                allow_read = allow_push = req.env['REMOTE_USER']
-            elif perm == (False, False):
-                if is_public:
-                    allow_read = '*'
-                    deny_push = '*'
-                else:
-                    allow_read = '*'
-                    deny_push = '*'
-        
     def _setup_repo(self, repo, project_id):
         dbcur = self.db.cursor()
         dbcur.execute('SELECT projects.name, projects.description FROM projects '
                 tmpl = self.templater(req)
                 ctype = tmpl('mimetype', encoding=encoding.encoding)
                 ctype = templater.stringify(ctype)
-
+                
                 # a static file
                 if virtual.startswith('static/') or 'static' in req.form:
                     if virtual.startswith('static/'):
                         fname = req.form['static'][0]
                     static = templater.templatepath('static')
                     return (staticfile(static, fname, req),)
-
+                
+                self._user_login(req)
+                
                 # top-level index
-                elif not virtual:
+                if not virtual:
                     # only administrators can list repositories
                     if self._is_admin(req):
                         req.respond(HTTP_OK, ctype)
                 repos = dict(self.repos)
                 real = repos.get(project_id)
                 
-                if real:
-                    self._set_perms(req, project_id)
-                    
+                if real:                    
                     req.env['REPO_NAME'] = project_id
                     try:
                         repo = hg.repository(self.ui, real)
                         self._setup_repo(repo, project_id)
-                        return hgweb(repo).run_wsgi(req)
+                        return HgwebRedmine(self.db, self.realm, repo).run_wsgi(req)
                     except IOError, inst:
                         msg = inst.strerror
                         raise ErrorResponse(HTTP_SERVER_ERROR, msg)
             self.db = None
             tmpl = None
 
+class HgwebRedmine(hgweb):
+    def __init__(self, dbconn, realm, repo, name=None):
+        self.db = dbconn
+        self.realm = realm
+        
+        hgweb.__init__(self, repo, name)
+    
+    def _send_challenge(self, req, msg):
+        req.header([('WWW-Authenticate', 'Basic realm="%s"' % self.realm)])
+        raise ErrorResponse(HTTP_UNAUTHORIZED, msg)
+        
+    def _get_perms(self, user, project_id):
+        """
+        Find member permissions from Redmine database.
+        
+        Redmine repository relate permissions:
+            - :manage_repository
+            - :browse_repository
+            - :view_changesets
+            - :commit_access
+        
+        @return (allow_read, allow_push) tuple
+        """
+        is_public = self._is_public_repo(project_id)
+        
+        if not user: # anonymous user
+            if is_public:
+                return (True, False)
+            
+            return (False, False)
+        
+        # Redmine member
+        
+        dbcur = self.db.cursor()
+        dbcur.execute('SELECT roles.permissions FROM users, projects, members, roles '
+                            'WHERE users.login=? AND projects.identifier=? '
+                            'AND projects.id = members.project_id AND users.id = members.user_id '
+                            'AND roles.id = members.role_id',
+                      (user, project_id)
+                     )
+        
+        row = dbcur.fetchone()
+        if not row:
+            # user doesn't have any permits
+            return (False, False)
+        
+        perms = row[0].splitlines()
+        
+        if '- :manage_repository' in perms or '- :commit_access' in perms:
+            return (True, True)
+        elif '- :view_changesets' in perms or '- :browse_repository' in perms:
+            return (True, False)
+        else:
+            return (False, False)
+            
+    def _is_public_repo(self, project_id):        
+        dbcur = self.db.cursor()
+        dbcur.execute('SELECT projects.is_public FROM projects '
+                            'WHERE projects.identifier=?',
+                      (project_id, )
+                     )
+        
+        row = dbcur.fetchone()
+        if not row:
+            return False
+            
+        return row[0] == 't'
+    
+    def check_perm(self, req, op):
+        '''Check permission for operation based on request data (including
+        authentication info). Return if op allowed, else raise an ErrorResponse
+        exception.'''
+        
+        user = req.env.get('REMOTE_USER')
+        project_id = req.env.get('REPO_NAME')
+        
+        allow_read, allow_push = self._get_perms(user, project_id)
+        
+        if not allow_read:
+            self._send_challenge(req, 'read not authorized')
+        
+        if op == 'pull' and not self.allowpull:
+            self._send_challenge(req, 'pull not authorized')
+        elif op == 'pull' or op is None: # op is None for interface requests
+            return
+
+        # enforce that you can only push using POST requests
+        if req.env['REQUEST_METHOD'] != 'POST':
+            msg = 'push requires POST request'
+            raise ErrorResponse(HTTP_METHOD_NOT_ALLOWED, msg)
+
+        # require ssl by default for pushing, auth info cannot be sniffed
+        # and replayed
+        scheme = req.env.get('wsgi.url_scheme')
+        if self.configbool('web', 'push_ssl', True) and scheme != 'https':
+            raise ErrorResponse(HTTP_OK, 'ssl required')
+        
+        if not allow_push:
+            self._send_challenge(req, 'push not authorized')
+
+
 def connect(dsn):
     """
     Connect to database parsing dsn.