##// END OF EJS Templates
middleware: move handling of permanent repo URLs to separate middleware...
Mads Kiilerich -
r7620:ce2a4ef8 default
parent child Browse files
Show More
@@ -0,0 +1,38 b''
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
6 #
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
11 #
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 """
15 kallithea.lib.middleware.permanent_repo_url
16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17
18 middleware to handle permanent repo URLs, replacing PATH_INFO '/_123/yada' with
19 '/name/of/repo/yada' after looking 123 up in the database.
20 """
21
22
23 from kallithea.lib.utils import safe_str, fix_repo_id_name
24
25
26 class PermanentRepoUrl(object):
27
28 def __init__(self, app, config):
29 self.application = app
30 self.config = config
31
32 def __call__(self, environ, start_response):
33 path_info = environ['PATH_INFO']
34 if path_info.startswith('/'): # it must
35 path_info = '/' + safe_str(fix_repo_id_name(path_info[1:]))
36 environ['PATH_INFO'] = path_info
37
38 return self.application(environ, start_response)
@@ -30,6 +30,7 b' from alembic.migration import MigrationC'
30 from sqlalchemy import create_engine
30 from sqlalchemy import create_engine
31 import mercurial
31 import mercurial
32
32
33 from kallithea.lib.middleware.permanent_repo_url import PermanentRepoUrl
33 from kallithea.lib.middleware.https_fixup import HttpsFixup
34 from kallithea.lib.middleware.https_fixup import HttpsFixup
34 from kallithea.lib.middleware.simplegit import SimpleGit
35 from kallithea.lib.middleware.simplegit import SimpleGit
35 from kallithea.lib.middleware.simplehg import SimpleHg
36 from kallithea.lib.middleware.simplehg import SimpleHg
@@ -208,6 +209,8 b' def setup_application(app):'
208 # Enable https redirects based on HTTP_X_URL_SCHEME set by proxy
209 # Enable https redirects based on HTTP_X_URL_SCHEME set by proxy
209 if any(asbool(config.get(x)) for x in ['https_fixup', 'force_https', 'use_htsts']):
210 if any(asbool(config.get(x)) for x in ['https_fixup', 'force_https', 'use_htsts']):
210 app = HttpsFixup(app, config)
211 app = HttpsFixup(app, config)
212
213 app = PermanentRepoUrl(app, config)
211 return app
214 return app
212
215
213
216
@@ -33,28 +33,18 b' def make_map(config):'
33 rmap.minimization = False
33 rmap.minimization = False
34 rmap.explicit = False
34 rmap.explicit = False
35
35
36 from kallithea.lib.utils import (is_valid_repo, is_valid_repo_group,
36 from kallithea.lib.utils import is_valid_repo, is_valid_repo_group
37 get_repo_by_id)
38
37
39 def check_repo(environ, match_dict):
38 def check_repo(environ, match_dict):
40 """
39 """
41 check for valid repository for proper 404 handling
40 Check for valid repository for proper 404 handling.
42
41 Also, a bit of side effect modifying match_dict ...
43 :param environ:
44 :param match_dict:
45 """
42 """
46 repo_name = match_dict.get('repo_name')
47
48 if match_dict.get('f_path'):
43 if match_dict.get('f_path'):
49 # fix for multiple initial slashes that causes errors
44 # fix for multiple initial slashes that causes errors
50 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
45 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
51
46
52 by_id_match = get_repo_by_id(repo_name)
47 return is_valid_repo(match_dict['repo_name'], config['base_path'])
53 if by_id_match:
54 repo_name = by_id_match
55 match_dict['repo_name'] = repo_name
56
57 return is_valid_repo(repo_name, config['base_path'])
58
48
59 def check_group(environ, match_dict):
49 def check_group(environ, match_dict):
60 """
50 """
@@ -266,23 +266,6 b' class BaseVCSController(object):'
266 def _handle_request(self, environ, start_response):
266 def _handle_request(self, environ, start_response):
267 raise NotImplementedError()
267 raise NotImplementedError()
268
268
269 def _get_by_id(self, repo_name):
270 """
271 Gets a special pattern _<ID> from clone url and tries to replace it
272 with a repository_name for support of _<ID> permanent URLs
273
274 :param repo_name:
275 """
276
277 data = repo_name.split('/')
278 if len(data) >= 2:
279 from kallithea.lib.utils import get_repo_by_id
280 by_id_match = get_repo_by_id(repo_name)
281 if by_id_match:
282 data[1] = safe_str(by_id_match)
283
284 return '/'.join(data)
285
286 def _check_permission(self, action, authuser, repo_name):
269 def _check_permission(self, action, authuser, repo_name):
287 """
270 """
288 Checks permissions using action (push/pull) user and repository
271 Checks permissions using action (push/pull) user and repository
@@ -141,14 +141,11 b' class SimpleGit(BaseVCSController):'
141 :param environ: environ where PATH_INFO is stored
141 :param environ: environ where PATH_INFO is stored
142 """
142 """
143 try:
143 try:
144 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
144 return GIT_PROTO_PAT.match(environ['PATH_INFO']).group(1)
145 repo_name = GIT_PROTO_PAT.match(environ['PATH_INFO']).group(1)
146 except Exception:
145 except Exception:
147 log.error(traceback.format_exc())
146 log.error(traceback.format_exc())
148 raise
147 raise
149
148
150 return repo_name
151
152 def __get_action(self, environ):
149 def __get_action(self, environ):
153 """
150 """
154 Maps Git request commands into a pull or push command.
151 Maps Git request commands into a pull or push command.
@@ -167,16 +167,13 b' class SimpleHg(BaseVCSController):'
167 :param environ: environ where PATH_INFO is stored
167 :param environ: environ where PATH_INFO is stored
168 """
168 """
169 try:
169 try:
170 environ['PATH_INFO'] = self._get_by_id(environ['PATH_INFO'])
170 path_info = environ['PATH_INFO']
171 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
171 if path_info.startswith('/'):
172 if repo_name.endswith('/'):
172 return path_info[1:].rstrip('/')
173 repo_name = repo_name.rstrip('/')
174 except Exception:
173 except Exception:
175 log.error(traceback.format_exc())
174 log.error(traceback.format_exc())
176 raise
175 raise
177
176
178 return repo_name
179
180 def __get_action(self, environ):
177 def __get_action(self, environ):
181 """
178 """
182 Maps Mercurial request commands into 'pull' or 'push'.
179 Maps Mercurial request commands into 'pull' or 'push'.
@@ -78,29 +78,35 b' def get_user_group_slug(request):'
78 return None
78 return None
79
79
80
80
81 def _extract_id_from_repo_name(repo_name):
81 def _get_permanent_id(s):
82 if repo_name.startswith('/'):
82 """Helper for decoding stable URLs with repo ID. For a string like '_123'
83 repo_name = repo_name.lstrip('/')
83 return 123.
84 by_id_match = re.match(r'^_(\d{1,})', repo_name)
84 """
85 if by_id_match:
85 by_id_match = re.match(r'^_(\d+)$', s)
86 return by_id_match.groups()[0]
86 if by_id_match is None:
87 return None
88 return int(by_id_match.group(1))
87
89
88
90
89 def get_repo_by_id(repo_name):
91 def fix_repo_id_name(path):
90 """
92 """
91 Extracts repo_name by id from special urls. Example url is _11/repo_name
93 Rewrite repo_name for _<ID> permanent URLs.
92
94
93 :param repo_name:
95 Given a path, if the first path element is like _<ID>, return the path with
94 :return: repo_name if matched else None
96 this part expanded to the corresponding full repo name, else return the
97 provided path.
95 """
98 """
96 _repo_id = _extract_id_from_repo_name(repo_name)
99 first, rest = path, ''
97 if _repo_id:
100 if '/' in path:
101 first, rest_ = path.split('/', 1)
102 rest = '/' + rest_
103 repo_id = _get_permanent_id(first)
104 if repo_id is not None:
98 from kallithea.model.db import Repository
105 from kallithea.model.db import Repository
99 repo = Repository.get(_repo_id)
106 repo = Repository.get(repo_id)
100 if repo:
107 if repo is not None:
101 # TODO: return repo instead of reponame? or would that be a layering violation?
108 return repo.repo_name + rest
102 return repo.repo_name
109 return path
103 return None
104
110
105
111
106 def action_logger(user, action, repo, ipaddr='', commit=False):
112 def action_logger(user, action, repo, ipaddr='', commit=False):
@@ -532,26 +532,32 b' class TestLibs(TestController):'
532
532
533 @parametrize('test,expected', [
533 @parametrize('test,expected', [
534 ("", None),
534 ("", None),
535 ("/_2", '2'),
535 ("/_2", None),
536 ("_2", '2'),
536 ("_2", 2),
537 ("/_2/", '2'),
537 ("_2/", None),
538 ("_2/", '2'),
538 ])
539
539 def test_get_permanent_id(self, test, expected):
540 ("/_21", '21'),
540 from kallithea.lib.utils import _get_permanent_id
541 ("_21", '21'),
541 extracted = _get_permanent_id(test)
542 ("/_21/", '21'),
542 assert extracted == expected, 'url:%s, got:`%s` expected: `%s`' % (test, _test, expected)
543 ("_21/", '21'),
544
543
545 ("/_21/foobar", '21'),
544 @parametrize('test,expected', [
546 ("_21/121", '21'),
545 ("", ""),
547 ("/_21/_12", '21'),
546 ("/", "/"),
548 ("_21/prefix/foo", '21'),
547 ("/_ID", '/_ID'),
548 ("ID", "ID"),
549 ("_ID", 'NAME'),
550 ("_ID/", 'NAME/'),
551 ("_ID/1/2", 'NAME/1/2'),
552 ("_IDa", '_IDa'),
549 ])
553 ])
550 def test_get_repo_by_id(self, test, expected):
554 def test_fix_repo_id_name(self, test, expected):
551 from kallithea.lib.utils import _extract_id_from_repo_name
555 repo = Repository.get_by_repo_name(HG_REPO)
552 _test = _extract_id_from_repo_name(test)
556 test = test.replace('ID', str(repo.repo_id))
553 assert _test == expected, 'url:%s, got:`%s` expected: `%s`' % (test, _test, expected)
557 expected = expected.replace('NAME', repo.repo_name).replace('ID', str(repo.repo_id))
554
558 from kallithea.lib.utils import fix_repo_id_name
559 replaced = fix_repo_id_name(test)
560 assert replaced == expected, 'url:%s, got:`%s` expected: `%s`' % (test, replaced, expected)
555
561
556 @parametrize('canonical,test,expected', [
562 @parametrize('canonical,test,expected', [
557 ('http://www.example.org/', '/abc/xyz', 'http://www.example.org/abc/xyz'),
563 ('http://www.example.org/', '/abc/xyz', 'http://www.example.org/abc/xyz'),
General Comments 0
You need to be logged in to leave comments. Login now