##// END OF EJS Templates
#49 Enabled anonymous access push and pull commands
marcink -
r674:99875a8f beta
parent child Browse files
Show More
@@ -1,120 +1,126 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # summary controller for pylons
3 # summary controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 18, 2010
21 Created on April 18, 2010
22 summary controller for pylons
22 summary controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from pylons import tmpl_context as c, request, url
25 from pylons import tmpl_context as c, request, url
26 from vcs.exceptions import ChangesetError
26 from vcs.exceptions import ChangesetError
27 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
27 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
28 from rhodecode.lib.base import BaseController, render
28 from rhodecode.lib.base import BaseController, render
29 from rhodecode.lib.utils import OrderedDict, EmptyChangeset
29 from rhodecode.lib.utils import OrderedDict, EmptyChangeset
30 from rhodecode.model.hg import HgModel
30 from rhodecode.model.hg import HgModel
31 from rhodecode.model.db import Statistics
31 from rhodecode.model.db import Statistics
32 from webhelpers.paginate import Page
32 from webhelpers.paginate import Page
33 from rhodecode.lib.celerylib import run_task
33 from rhodecode.lib.celerylib import run_task
34 from rhodecode.lib.celerylib.tasks import get_commits_stats
34 from rhodecode.lib.celerylib.tasks import get_commits_stats
35 from datetime import datetime, timedelta
35 from datetime import datetime, timedelta
36 from time import mktime
36 from time import mktime
37 import calendar
37 import calendar
38 import logging
38 import logging
39 try:
39 try:
40 import json
40 import json
41 except ImportError:
41 except ImportError:
42 #python 2.5 compatibility
42 #python 2.5 compatibility
43 import simplejson as json
43 import simplejson as json
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46 class SummaryController(BaseController):
46 class SummaryController(BaseController):
47
47
48 @LoginRequired()
48 @LoginRequired()
49 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
49 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
50 'repository.admin')
50 'repository.admin')
51 def __before__(self):
51 def __before__(self):
52 super(SummaryController, self).__before__()
52 super(SummaryController, self).__before__()
53
53
54 def index(self):
54 def index(self):
55 hg_model = HgModel()
55 hg_model = HgModel()
56 c.repo_info = hg_model.get_repo(c.repo_name)
56 c.repo_info = hg_model.get_repo(c.repo_name)
57 def url_generator(**kw):
57 def url_generator(**kw):
58 return url('shortlog_home', repo_name=c.repo_name, **kw)
58 return url('shortlog_home', repo_name=c.repo_name, **kw)
59
59
60 c.repo_changesets = Page(c.repo_info, page=1, items_per_page=10,
60 c.repo_changesets = Page(c.repo_info, page=1, items_per_page=10,
61 url=url_generator)
61 url=url_generator)
62
62
63 e = request.environ
63 e = request.environ
64
64
65 uri = u'%(protocol)s://%(user)s@%(host)s%(prefix)s/%(repo_name)s' % {
65 if self.rhodecode_user.username == 'default':
66 password = ':default'
67 else:
68 password = ''
69
70 uri = u'%(protocol)s://%(user)s%(password)s@%(host)s%(prefix)s/%(repo_name)s' % {
66 'protocol': e.get('wsgi.url_scheme'),
71 'protocol': e.get('wsgi.url_scheme'),
67 'user':str(c.rhodecode_user.username),
72 'user':str(c.rhodecode_user.username),
73 'password':password,
68 'host':e.get('HTTP_HOST'),
74 'host':e.get('HTTP_HOST'),
69 'prefix':e.get('SCRIPT_NAME'),
75 'prefix':e.get('SCRIPT_NAME'),
70 'repo_name':c.repo_name, }
76 'repo_name':c.repo_name, }
71 c.clone_repo_url = uri
77 c.clone_repo_url = uri
72 c.repo_tags = OrderedDict()
78 c.repo_tags = OrderedDict()
73 for name, hash in c.repo_info.tags.items()[:10]:
79 for name, hash in c.repo_info.tags.items()[:10]:
74 try:
80 try:
75 c.repo_tags[name] = c.repo_info.get_changeset(hash)
81 c.repo_tags[name] = c.repo_info.get_changeset(hash)
76 except ChangesetError:
82 except ChangesetError:
77 c.repo_tags[name] = EmptyChangeset(hash)
83 c.repo_tags[name] = EmptyChangeset(hash)
78
84
79 c.repo_branches = OrderedDict()
85 c.repo_branches = OrderedDict()
80 for name, hash in c.repo_info.branches.items()[:10]:
86 for name, hash in c.repo_info.branches.items()[:10]:
81 try:
87 try:
82 c.repo_branches[name] = c.repo_info.get_changeset(hash)
88 c.repo_branches[name] = c.repo_info.get_changeset(hash)
83 except ChangesetError:
89 except ChangesetError:
84 c.repo_branches[name] = EmptyChangeset(hash)
90 c.repo_branches[name] = EmptyChangeset(hash)
85
91
86 td = datetime.today() + timedelta(days=1)
92 td = datetime.today() + timedelta(days=1)
87 y, m, d = td.year, td.month, td.day
93 y, m, d = td.year, td.month, td.day
88
94
89 ts_min_y = mktime((y - 1, (td - timedelta(days=calendar.mdays[m])).month,
95 ts_min_y = mktime((y - 1, (td - timedelta(days=calendar.mdays[m])).month,
90 d, 0, 0, 0, 0, 0, 0,))
96 d, 0, 0, 0, 0, 0, 0,))
91 ts_min_m = mktime((y, (td - timedelta(days=calendar.mdays[m])).month,
97 ts_min_m = mktime((y, (td - timedelta(days=calendar.mdays[m])).month,
92 d, 0, 0, 0, 0, 0, 0,))
98 d, 0, 0, 0, 0, 0, 0,))
93
99
94 ts_max_y = mktime((y, m, d, 0, 0, 0, 0, 0, 0,))
100 ts_max_y = mktime((y, m, d, 0, 0, 0, 0, 0, 0,))
95
101
96 run_task(get_commits_stats, c.repo_info.name, ts_min_y, ts_max_y)
102 run_task(get_commits_stats, c.repo_info.name, ts_min_y, ts_max_y)
97 c.ts_min = ts_min_m
103 c.ts_min = ts_min_m
98 c.ts_max = ts_max_y
104 c.ts_max = ts_max_y
99
105
100 stats = self.sa.query(Statistics)\
106 stats = self.sa.query(Statistics)\
101 .filter(Statistics.repository == c.repo_info.dbrepo)\
107 .filter(Statistics.repository == c.repo_info.dbrepo)\
102 .scalar()
108 .scalar()
103
109
104
110
105 if stats and stats.languages:
111 if stats and stats.languages:
106 lang_stats = json.loads(stats.languages)
112 lang_stats = json.loads(stats.languages)
107 c.commit_data = stats.commit_activity
113 c.commit_data = stats.commit_activity
108 c.overview_data = stats.commit_activity_combined
114 c.overview_data = stats.commit_activity_combined
109 c.trending_languages = json.dumps(OrderedDict(
115 c.trending_languages = json.dumps(OrderedDict(
110 sorted(lang_stats.items(), reverse=True,
116 sorted(lang_stats.items(), reverse=True,
111 key=lambda k: k[1])[:2]
117 key=lambda k: k[1])[:2]
112 )
118 )
113 )
119 )
114 else:
120 else:
115 c.commit_data = json.dumps({})
121 c.commit_data = json.dumps({})
116 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 0] ])
122 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 0] ])
117 c.trending_languages = json.dumps({})
123 c.trending_languages = json.dumps({})
118
124
119 return render('summary/summary.html')
125 return render('summary/summary.html')
120
126
@@ -1,475 +1,480 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # authentication and permission libraries
3 # authentication and permission libraries
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 4, 2010
21 Created on April 4, 2010
22
22
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from pylons import config, session, url, request
25 from pylons import config, session, url, request
26 from pylons.controllers.util import abort, redirect
26 from pylons.controllers.util import abort, redirect
27 from rhodecode.lib.utils import get_repo_slug
27 from rhodecode.lib.utils import get_repo_slug
28 from rhodecode.model import meta
28 from rhodecode.model import meta
29 from rhodecode.model.user import UserModel
29 from rhodecode.model.user import UserModel
30 from rhodecode.model.caching_query import FromCache
30 from rhodecode.model.caching_query import FromCache
31 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
31 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
32 UserToPerm
32 UserToPerm
33 import bcrypt
33 import bcrypt
34 from decorator import decorator
34 from decorator import decorator
35 import logging
35 import logging
36 import random
36 import random
37
37
38 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
39
39
40 class PasswordGenerator(object):
40 class PasswordGenerator(object):
41 """This is a simple class for generating password from
41 """This is a simple class for generating password from
42 different sets of characters
42 different sets of characters
43 usage:
43 usage:
44 passwd_gen = PasswordGenerator()
44 passwd_gen = PasswordGenerator()
45 #print 8-letter password containing only big and small letters of alphabet
45 #print 8-letter password containing only big and small letters of alphabet
46 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
46 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
47 """
47 """
48 ALPHABETS_NUM = r'''1234567890'''#[0]
48 ALPHABETS_NUM = r'''1234567890'''#[0]
49 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
49 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
50 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
50 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
51 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
51 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
52 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
52 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
53 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
53 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
54 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
54 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
55 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
55 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
56 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
56 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
57
57
58 def __init__(self, passwd=''):
58 def __init__(self, passwd=''):
59 self.passwd = passwd
59 self.passwd = passwd
60
60
61 def gen_password(self, len, type):
61 def gen_password(self, len, type):
62 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
62 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
63 return self.passwd
63 return self.passwd
64
64
65
65
66 def get_crypt_password(password):
66 def get_crypt_password(password):
67 """Cryptographic function used for password hashing based on sha1
67 """Cryptographic function used for password hashing based on sha1
68 :param password: password to hash
68 :param password: password to hash
69 """
69 """
70 return bcrypt.hashpw(password, bcrypt.gensalt(10))
70 return bcrypt.hashpw(password, bcrypt.gensalt(10))
71
71
72 def check_password(password, hashed):
72 def check_password(password, hashed):
73 return bcrypt.hashpw(password, hashed) == hashed
73 return bcrypt.hashpw(password, hashed) == hashed
74
74
75 def authfunc(environ, username, password):
75 def authfunc(environ, username, password):
76 user = UserModel().get_by_username(username, cache=False)
76 user = UserModel().get_by_username(username, cache=False)
77
77
78 if user:
78 if user:
79 if user.active:
79 if user.active:
80 if user.username == username and check_password(password, user.password):
80
81 if user.username == 'default' and user.active:
82 log.info('user %s authenticated correctly', username)
83 return True
84
85 elif user.username == username and check_password(password, user.password):
81 log.info('user %s authenticated correctly', username)
86 log.info('user %s authenticated correctly', username)
82 return True
87 return True
83 else:
88 else:
84 log.error('user %s is disabled', username)
89 log.error('user %s is disabled', username)
85
90
86 return False
91 return False
87
92
88 class AuthUser(object):
93 class AuthUser(object):
89 """
94 """
90 A simple object that handles a mercurial username for authentication
95 A simple object that handles a mercurial username for authentication
91 """
96 """
92 def __init__(self):
97 def __init__(self):
93 self.username = 'None'
98 self.username = 'None'
94 self.name = ''
99 self.name = ''
95 self.lastname = ''
100 self.lastname = ''
96 self.email = ''
101 self.email = ''
97 self.user_id = None
102 self.user_id = None
98 self.is_authenticated = False
103 self.is_authenticated = False
99 self.is_admin = False
104 self.is_admin = False
100 self.permissions = {}
105 self.permissions = {}
101
106
102 def __repr__(self):
107 def __repr__(self):
103 return "<AuthUser('id:%s:%s')>" % (self.user_id, self.username)
108 return "<AuthUser('id:%s:%s')>" % (self.user_id, self.username)
104
109
105 def set_available_permissions(config):
110 def set_available_permissions(config):
106 """
111 """
107 This function will propagate pylons globals with all available defined
112 This function will propagate pylons globals with all available defined
108 permission given in db. We don't wannt to check each time from db for new
113 permission given in db. We don't wannt to check each time from db for new
109 permissions since adding a new permission also requires application restart
114 permissions since adding a new permission also requires application restart
110 ie. to decorate new views with the newly created permission
115 ie. to decorate new views with the newly created permission
111 :param config:
116 :param config:
112 """
117 """
113 log.info('getting information about all available permissions')
118 log.info('getting information about all available permissions')
114 try:
119 try:
115 sa = meta.Session()
120 sa = meta.Session()
116 all_perms = sa.query(Permission).all()
121 all_perms = sa.query(Permission).all()
117 except:
122 except:
118 pass
123 pass
119 finally:
124 finally:
120 meta.Session.remove()
125 meta.Session.remove()
121
126
122 config['available_permissions'] = [x.permission_name for x in all_perms]
127 config['available_permissions'] = [x.permission_name for x in all_perms]
123
128
124 def set_base_path(config):
129 def set_base_path(config):
125 config['base_path'] = config['pylons.app_globals'].base_path
130 config['base_path'] = config['pylons.app_globals'].base_path
126
131
127
132
128 def fill_perms(user):
133 def fill_perms(user):
129 """
134 """
130 Fills user permission attribute with permissions taken from database
135 Fills user permission attribute with permissions taken from database
131 :param user:
136 :param user:
132 """
137 """
133
138
134 sa = meta.Session()
139 sa = meta.Session()
135 user.permissions['repositories'] = {}
140 user.permissions['repositories'] = {}
136 user.permissions['global'] = set()
141 user.permissions['global'] = set()
137
142
138 #===========================================================================
143 #===========================================================================
139 # fetch default permissions
144 # fetch default permissions
140 #===========================================================================
145 #===========================================================================
141 default_user = UserModel(sa).get_by_username('default', cache=True)
146 default_user = UserModel(sa).get_by_username('default', cache=True)
142
147
143 default_perms = sa.query(RepoToPerm, Repository, Permission)\
148 default_perms = sa.query(RepoToPerm, Repository, Permission)\
144 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
149 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
145 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
150 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
146 .filter(RepoToPerm.user == default_user).all()
151 .filter(RepoToPerm.user == default_user).all()
147
152
148 if user.is_admin:
153 if user.is_admin:
149 #=======================================================================
154 #=======================================================================
150 # #admin have all default rights set to admin
155 # #admin have all default rights set to admin
151 #=======================================================================
156 #=======================================================================
152 user.permissions['global'].add('hg.admin')
157 user.permissions['global'].add('hg.admin')
153
158
154 for perm in default_perms:
159 for perm in default_perms:
155 p = 'repository.admin'
160 p = 'repository.admin'
156 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
161 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
157
162
158 else:
163 else:
159 #=======================================================================
164 #=======================================================================
160 # set default permissions
165 # set default permissions
161 #=======================================================================
166 #=======================================================================
162
167
163 #default global
168 #default global
164 default_global_perms = sa.query(UserToPerm)\
169 default_global_perms = sa.query(UserToPerm)\
165 .filter(UserToPerm.user == sa.query(User).filter(User.username ==
170 .filter(UserToPerm.user == sa.query(User).filter(User.username ==
166 'default').one())
171 'default').one())
167
172
168 for perm in default_global_perms:
173 for perm in default_global_perms:
169 user.permissions['global'].add(perm.permission.permission_name)
174 user.permissions['global'].add(perm.permission.permission_name)
170
175
171 #default repositories
176 #default repositories
172 for perm in default_perms:
177 for perm in default_perms:
173 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
178 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
174 #disable defaults for private repos,
179 #disable defaults for private repos,
175 p = 'repository.none'
180 p = 'repository.none'
176 elif perm.Repository.user_id == user.user_id:
181 elif perm.Repository.user_id == user.user_id:
177 #set admin if owner
182 #set admin if owner
178 p = 'repository.admin'
183 p = 'repository.admin'
179 else:
184 else:
180 p = perm.Permission.permission_name
185 p = perm.Permission.permission_name
181
186
182 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
187 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
183
188
184 #=======================================================================
189 #=======================================================================
185 # #overwrite default with user permissions if any
190 # #overwrite default with user permissions if any
186 #=======================================================================
191 #=======================================================================
187 user_perms = sa.query(RepoToPerm, Permission, Repository)\
192 user_perms = sa.query(RepoToPerm, Permission, Repository)\
188 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
193 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
189 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
194 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
190 .filter(RepoToPerm.user_id == user.user_id).all()
195 .filter(RepoToPerm.user_id == user.user_id).all()
191
196
192 for perm in user_perms:
197 for perm in user_perms:
193 if perm.Repository.user_id == user.user_id:#set admin if owner
198 if perm.Repository.user_id == user.user_id:#set admin if owner
194 p = 'repository.admin'
199 p = 'repository.admin'
195 else:
200 else:
196 p = perm.Permission.permission_name
201 p = perm.Permission.permission_name
197 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
202 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
198 meta.Session.remove()
203 meta.Session.remove()
199 return user
204 return user
200
205
201 def get_user(session):
206 def get_user(session):
202 """
207 """
203 Gets user from session, and wraps permissions into user
208 Gets user from session, and wraps permissions into user
204 :param session:
209 :param session:
205 """
210 """
206 user = session.get('rhodecode_user', AuthUser())
211 user = session.get('rhodecode_user', AuthUser())
207
212
208
213
209 #if the user is not logged in we check for anonymous access
214 #if the user is not logged in we check for anonymous access
210 #if user is logged and it's a default user check if we still have anonymous
215 #if user is logged and it's a default user check if we still have anonymous
211 #access enabled
216 #access enabled
212 if user.user_id is None or user.username == 'default':
217 if user.user_id is None or user.username == 'default':
213 anonymous_user = UserModel().get_by_username('default', cache=True)
218 anonymous_user = UserModel().get_by_username('default', cache=True)
214 if anonymous_user.active is True:
219 if anonymous_user.active is True:
215 #then we set this user is logged in
220 #then we set this user is logged in
216 user.is_authenticated = True
221 user.is_authenticated = True
217 else:
222 else:
218 user.is_authenticated = False
223 user.is_authenticated = False
219
224
220 if user.is_authenticated:
225 if user.is_authenticated:
221 user = UserModel().fill_data(user)
226 user = UserModel().fill_data(user)
222
227
223 user = fill_perms(user)
228 user = fill_perms(user)
224 session['rhodecode_user'] = user
229 session['rhodecode_user'] = user
225 session.save()
230 session.save()
226 return user
231 return user
227
232
228 #===============================================================================
233 #===============================================================================
229 # CHECK DECORATORS
234 # CHECK DECORATORS
230 #===============================================================================
235 #===============================================================================
231 class LoginRequired(object):
236 class LoginRequired(object):
232 """Must be logged in to execute this function else redirect to login page"""
237 """Must be logged in to execute this function else redirect to login page"""
233
238
234 def __call__(self, func):
239 def __call__(self, func):
235 return decorator(self.__wrapper, func)
240 return decorator(self.__wrapper, func)
236
241
237 def __wrapper(self, func, *fargs, **fkwargs):
242 def __wrapper(self, func, *fargs, **fkwargs):
238 user = session.get('rhodecode_user', AuthUser())
243 user = session.get('rhodecode_user', AuthUser())
239 log.debug('Checking login required for user:%s', user.username)
244 log.debug('Checking login required for user:%s', user.username)
240 if user.is_authenticated:
245 if user.is_authenticated:
241 log.debug('user %s is authenticated', user.username)
246 log.debug('user %s is authenticated', user.username)
242 return func(*fargs, **fkwargs)
247 return func(*fargs, **fkwargs)
243 else:
248 else:
244 log.warn('user %s not authenticated', user.username)
249 log.warn('user %s not authenticated', user.username)
245
250
246 p = ''
251 p = ''
247 if request.environ.get('SCRIPT_NAME') != '/':
252 if request.environ.get('SCRIPT_NAME') != '/':
248 p += request.environ.get('SCRIPT_NAME')
253 p += request.environ.get('SCRIPT_NAME')
249
254
250 p += request.environ.get('PATH_INFO')
255 p += request.environ.get('PATH_INFO')
251 if request.environ.get('QUERY_STRING'):
256 if request.environ.get('QUERY_STRING'):
252 p += '?' + request.environ.get('QUERY_STRING')
257 p += '?' + request.environ.get('QUERY_STRING')
253
258
254 log.debug('redirecting to login page with %s', p)
259 log.debug('redirecting to login page with %s', p)
255 return redirect(url('login_home', came_from=p))
260 return redirect(url('login_home', came_from=p))
256
261
257 class PermsDecorator(object):
262 class PermsDecorator(object):
258 """Base class for decorators"""
263 """Base class for decorators"""
259
264
260 def __init__(self, *required_perms):
265 def __init__(self, *required_perms):
261 available_perms = config['available_permissions']
266 available_perms = config['available_permissions']
262 for perm in required_perms:
267 for perm in required_perms:
263 if perm not in available_perms:
268 if perm not in available_perms:
264 raise Exception("'%s' permission is not defined" % perm)
269 raise Exception("'%s' permission is not defined" % perm)
265 self.required_perms = set(required_perms)
270 self.required_perms = set(required_perms)
266 self.user_perms = None
271 self.user_perms = None
267
272
268 def __call__(self, func):
273 def __call__(self, func):
269 return decorator(self.__wrapper, func)
274 return decorator(self.__wrapper, func)
270
275
271
276
272 def __wrapper(self, func, *fargs, **fkwargs):
277 def __wrapper(self, func, *fargs, **fkwargs):
273 # _wrapper.__name__ = func.__name__
278 # _wrapper.__name__ = func.__name__
274 # _wrapper.__dict__.update(func.__dict__)
279 # _wrapper.__dict__.update(func.__dict__)
275 # _wrapper.__doc__ = func.__doc__
280 # _wrapper.__doc__ = func.__doc__
276 self.user = session.get('rhodecode_user', AuthUser())
281 self.user = session.get('rhodecode_user', AuthUser())
277 self.user_perms = self.user.permissions
282 self.user_perms = self.user.permissions
278 log.debug('checking %s permissions %s for %s %s',
283 log.debug('checking %s permissions %s for %s %s',
279 self.__class__.__name__, self.required_perms, func.__name__,
284 self.__class__.__name__, self.required_perms, func.__name__,
280 self.user)
285 self.user)
281
286
282 if self.check_permissions():
287 if self.check_permissions():
283 log.debug('Permission granted for %s %s', func.__name__, self.user)
288 log.debug('Permission granted for %s %s', func.__name__, self.user)
284
289
285 return func(*fargs, **fkwargs)
290 return func(*fargs, **fkwargs)
286
291
287 else:
292 else:
288 log.warning('Permission denied for %s %s', func.__name__, self.user)
293 log.warning('Permission denied for %s %s', func.__name__, self.user)
289 #redirect with forbidden ret code
294 #redirect with forbidden ret code
290 return abort(403)
295 return abort(403)
291
296
292
297
293
298
294 def check_permissions(self):
299 def check_permissions(self):
295 """Dummy function for overriding"""
300 """Dummy function for overriding"""
296 raise Exception('You have to write this function in child class')
301 raise Exception('You have to write this function in child class')
297
302
298 class HasPermissionAllDecorator(PermsDecorator):
303 class HasPermissionAllDecorator(PermsDecorator):
299 """Checks for access permission for all given predicates. All of them
304 """Checks for access permission for all given predicates. All of them
300 have to be meet in order to fulfill the request
305 have to be meet in order to fulfill the request
301 """
306 """
302
307
303 def check_permissions(self):
308 def check_permissions(self):
304 if self.required_perms.issubset(self.user_perms.get('global')):
309 if self.required_perms.issubset(self.user_perms.get('global')):
305 return True
310 return True
306 return False
311 return False
307
312
308
313
309 class HasPermissionAnyDecorator(PermsDecorator):
314 class HasPermissionAnyDecorator(PermsDecorator):
310 """Checks for access permission for any of given predicates. In order to
315 """Checks for access permission for any of given predicates. In order to
311 fulfill the request any of predicates must be meet
316 fulfill the request any of predicates must be meet
312 """
317 """
313
318
314 def check_permissions(self):
319 def check_permissions(self):
315 if self.required_perms.intersection(self.user_perms.get('global')):
320 if self.required_perms.intersection(self.user_perms.get('global')):
316 return True
321 return True
317 return False
322 return False
318
323
319 class HasRepoPermissionAllDecorator(PermsDecorator):
324 class HasRepoPermissionAllDecorator(PermsDecorator):
320 """Checks for access permission for all given predicates for specific
325 """Checks for access permission for all given predicates for specific
321 repository. All of them have to be meet in order to fulfill the request
326 repository. All of them have to be meet in order to fulfill the request
322 """
327 """
323
328
324 def check_permissions(self):
329 def check_permissions(self):
325 repo_name = get_repo_slug(request)
330 repo_name = get_repo_slug(request)
326 try:
331 try:
327 user_perms = set([self.user_perms['repositories'][repo_name]])
332 user_perms = set([self.user_perms['repositories'][repo_name]])
328 except KeyError:
333 except KeyError:
329 return False
334 return False
330 if self.required_perms.issubset(user_perms):
335 if self.required_perms.issubset(user_perms):
331 return True
336 return True
332 return False
337 return False
333
338
334
339
335 class HasRepoPermissionAnyDecorator(PermsDecorator):
340 class HasRepoPermissionAnyDecorator(PermsDecorator):
336 """Checks for access permission for any of given predicates for specific
341 """Checks for access permission for any of given predicates for specific
337 repository. In order to fulfill the request any of predicates must be meet
342 repository. In order to fulfill the request any of predicates must be meet
338 """
343 """
339
344
340 def check_permissions(self):
345 def check_permissions(self):
341 repo_name = get_repo_slug(request)
346 repo_name = get_repo_slug(request)
342
347
343 try:
348 try:
344 user_perms = set([self.user_perms['repositories'][repo_name]])
349 user_perms = set([self.user_perms['repositories'][repo_name]])
345 except KeyError:
350 except KeyError:
346 return False
351 return False
347 if self.required_perms.intersection(user_perms):
352 if self.required_perms.intersection(user_perms):
348 return True
353 return True
349 return False
354 return False
350 #===============================================================================
355 #===============================================================================
351 # CHECK FUNCTIONS
356 # CHECK FUNCTIONS
352 #===============================================================================
357 #===============================================================================
353
358
354 class PermsFunction(object):
359 class PermsFunction(object):
355 """Base function for other check functions"""
360 """Base function for other check functions"""
356
361
357 def __init__(self, *perms):
362 def __init__(self, *perms):
358 available_perms = config['available_permissions']
363 available_perms = config['available_permissions']
359
364
360 for perm in perms:
365 for perm in perms:
361 if perm not in available_perms:
366 if perm not in available_perms:
362 raise Exception("'%s' permission in not defined" % perm)
367 raise Exception("'%s' permission in not defined" % perm)
363 self.required_perms = set(perms)
368 self.required_perms = set(perms)
364 self.user_perms = None
369 self.user_perms = None
365 self.granted_for = ''
370 self.granted_for = ''
366 self.repo_name = None
371 self.repo_name = None
367
372
368 def __call__(self, check_Location=''):
373 def __call__(self, check_Location=''):
369 user = session.get('rhodecode_user', False)
374 user = session.get('rhodecode_user', False)
370 if not user:
375 if not user:
371 return False
376 return False
372 self.user_perms = user.permissions
377 self.user_perms = user.permissions
373 self.granted_for = user.username
378 self.granted_for = user.username
374 log.debug('checking %s %s %s', self.__class__.__name__,
379 log.debug('checking %s %s %s', self.__class__.__name__,
375 self.required_perms, user)
380 self.required_perms, user)
376
381
377 if self.check_permissions():
382 if self.check_permissions():
378 log.debug('Permission granted for %s @ %s %s', self.granted_for,
383 log.debug('Permission granted for %s @ %s %s', self.granted_for,
379 check_Location, user)
384 check_Location, user)
380 return True
385 return True
381
386
382 else:
387 else:
383 log.warning('Permission denied for %s @ %s %s', self.granted_for,
388 log.warning('Permission denied for %s @ %s %s', self.granted_for,
384 check_Location, user)
389 check_Location, user)
385 return False
390 return False
386
391
387 def check_permissions(self):
392 def check_permissions(self):
388 """Dummy function for overriding"""
393 """Dummy function for overriding"""
389 raise Exception('You have to write this function in child class')
394 raise Exception('You have to write this function in child class')
390
395
391 class HasPermissionAll(PermsFunction):
396 class HasPermissionAll(PermsFunction):
392 def check_permissions(self):
397 def check_permissions(self):
393 if self.required_perms.issubset(self.user_perms.get('global')):
398 if self.required_perms.issubset(self.user_perms.get('global')):
394 return True
399 return True
395 return False
400 return False
396
401
397 class HasPermissionAny(PermsFunction):
402 class HasPermissionAny(PermsFunction):
398 def check_permissions(self):
403 def check_permissions(self):
399 if self.required_perms.intersection(self.user_perms.get('global')):
404 if self.required_perms.intersection(self.user_perms.get('global')):
400 return True
405 return True
401 return False
406 return False
402
407
403 class HasRepoPermissionAll(PermsFunction):
408 class HasRepoPermissionAll(PermsFunction):
404
409
405 def __call__(self, repo_name=None, check_Location=''):
410 def __call__(self, repo_name=None, check_Location=''):
406 self.repo_name = repo_name
411 self.repo_name = repo_name
407 return super(HasRepoPermissionAll, self).__call__(check_Location)
412 return super(HasRepoPermissionAll, self).__call__(check_Location)
408
413
409 def check_permissions(self):
414 def check_permissions(self):
410 if not self.repo_name:
415 if not self.repo_name:
411 self.repo_name = get_repo_slug(request)
416 self.repo_name = get_repo_slug(request)
412
417
413 try:
418 try:
414 self.user_perms = set([self.user_perms['repositories']\
419 self.user_perms = set([self.user_perms['repositories']\
415 [self.repo_name]])
420 [self.repo_name]])
416 except KeyError:
421 except KeyError:
417 return False
422 return False
418 self.granted_for = self.repo_name
423 self.granted_for = self.repo_name
419 if self.required_perms.issubset(self.user_perms):
424 if self.required_perms.issubset(self.user_perms):
420 return True
425 return True
421 return False
426 return False
422
427
423 class HasRepoPermissionAny(PermsFunction):
428 class HasRepoPermissionAny(PermsFunction):
424
429
425 def __call__(self, repo_name=None, check_Location=''):
430 def __call__(self, repo_name=None, check_Location=''):
426 self.repo_name = repo_name
431 self.repo_name = repo_name
427 return super(HasRepoPermissionAny, self).__call__(check_Location)
432 return super(HasRepoPermissionAny, self).__call__(check_Location)
428
433
429 def check_permissions(self):
434 def check_permissions(self):
430 if not self.repo_name:
435 if not self.repo_name:
431 self.repo_name = get_repo_slug(request)
436 self.repo_name = get_repo_slug(request)
432
437
433 try:
438 try:
434 self.user_perms = set([self.user_perms['repositories']\
439 self.user_perms = set([self.user_perms['repositories']\
435 [self.repo_name]])
440 [self.repo_name]])
436 except KeyError:
441 except KeyError:
437 return False
442 return False
438 self.granted_for = self.repo_name
443 self.granted_for = self.repo_name
439 if self.required_perms.intersection(self.user_perms):
444 if self.required_perms.intersection(self.user_perms):
440 return True
445 return True
441 return False
446 return False
442
447
443 #===============================================================================
448 #===============================================================================
444 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
449 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
445 #===============================================================================
450 #===============================================================================
446
451
447 class HasPermissionAnyMiddleware(object):
452 class HasPermissionAnyMiddleware(object):
448 def __init__(self, *perms):
453 def __init__(self, *perms):
449 self.required_perms = set(perms)
454 self.required_perms = set(perms)
450
455
451 def __call__(self, user, repo_name):
456 def __call__(self, user, repo_name):
452 usr = AuthUser()
457 usr = AuthUser()
453 usr.user_id = user.user_id
458 usr.user_id = user.user_id
454 usr.username = user.username
459 usr.username = user.username
455 usr.is_admin = user.admin
460 usr.is_admin = user.admin
456
461
457 try:
462 try:
458 self.user_perms = set([fill_perms(usr)\
463 self.user_perms = set([fill_perms(usr)\
459 .permissions['repositories'][repo_name]])
464 .permissions['repositories'][repo_name]])
460 except:
465 except:
461 self.user_perms = set()
466 self.user_perms = set()
462 self.granted_for = ''
467 self.granted_for = ''
463 self.username = user.username
468 self.username = user.username
464 self.repo_name = repo_name
469 self.repo_name = repo_name
465 return self.check_permissions()
470 return self.check_permissions()
466
471
467 def check_permissions(self):
472 def check_permissions(self):
468 log.debug('checking mercurial protocol '
473 log.debug('checking mercurial protocol '
469 'permissions for user:%s repository:%s',
474 'permissions for user:%s repository:%s',
470 self.username, self.repo_name)
475 self.username, self.repo_name)
471 if self.required_perms.intersection(self.user_perms):
476 if self.required_perms.intersection(self.user_perms):
472 log.debug('permission granted')
477 log.debug('permission granted')
473 return True
478 return True
474 log.debug('permission denied')
479 log.debug('permission denied')
475 return False
480 return False
@@ -1,239 +1,239 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # middleware to handle mercurial api calls
3 # middleware to handle mercurial api calls
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on 2010-04-28
21 Created on 2010-04-28
22
22
23 @author: marcink
23 @author: marcink
24 SimpleHG middleware for handling mercurial protocol request (push/clone etc.)
24 SimpleHG middleware for handling mercurial protocol request (push/clone etc.)
25 It's implemented with basic auth function
25 It's implemented with basic auth function
26 """
26 """
27 from itertools import chain
27 from itertools import chain
28 from mercurial.error import RepoError
28 from mercurial.error import RepoError
29 from mercurial.hgweb import hgweb
29 from mercurial.hgweb import hgweb
30 from mercurial.hgweb.request import wsgiapplication
30 from mercurial.hgweb.request import wsgiapplication
31 from paste.auth.basic import AuthBasicAuthenticator
31 from paste.auth.basic import AuthBasicAuthenticator
32 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
32 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
33 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
33 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
34 from rhodecode.lib.utils import is_mercurial, make_ui, invalidate_cache, \
34 from rhodecode.lib.utils import is_mercurial, make_ui, invalidate_cache, \
35 check_repo_fast, ui_sections
35 check_repo_fast, ui_sections
36 from rhodecode.model.user import UserModel
36 from rhodecode.model.user import UserModel
37 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
37 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
38 import logging
38 import logging
39 import os
39 import os
40 import traceback
40 import traceback
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44 class SimpleHg(object):
44 class SimpleHg(object):
45
45
46 def __init__(self, application, config):
46 def __init__(self, application, config):
47 self.application = application
47 self.application = application
48 self.config = config
48 self.config = config
49 #authenticate this mercurial request using
49 #authenticate this mercurial request using authfunc
50 self.authenticate = AuthBasicAuthenticator('', authfunc)
50 self.authenticate = AuthBasicAuthenticator('', authfunc)
51 self.ipaddr = '0.0.0.0'
51 self.ipaddr = '0.0.0.0'
52 self.repository = None
52 self.repository = None
53 self.username = None
53 self.username = None
54 self.action = None
54 self.action = None
55
55
56 def __call__(self, environ, start_response):
56 def __call__(self, environ, start_response):
57 if not is_mercurial(environ):
57 if not is_mercurial(environ):
58 return self.application(environ, start_response)
58 return self.application(environ, start_response)
59
59
60 proxy_key = 'HTTP_X_REAL_IP'
60 proxy_key = 'HTTP_X_REAL_IP'
61 def_key = 'REMOTE_ADDR'
61 def_key = 'REMOTE_ADDR'
62 self.ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
62 self.ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
63
63
64 #===================================================================
64 #===================================================================
65 # AUTHENTICATE THIS MERCURIAL REQUEST
65 # AUTHENTICATE THIS MERCURIAL REQUEST
66 #===================================================================
66 #===================================================================
67 username = REMOTE_USER(environ)
67 username = REMOTE_USER(environ)
68
68
69 if not username:
69 if not username:
70 self.authenticate.realm = self.config['rhodecode_realm']
70 self.authenticate.realm = self.config['rhodecode_realm']
71 result = self.authenticate(environ)
71 result = self.authenticate(environ)
72 if isinstance(result, str):
72 if isinstance(result, str):
73 AUTH_TYPE.update(environ, 'basic')
73 AUTH_TYPE.update(environ, 'basic')
74 REMOTE_USER.update(environ, result)
74 REMOTE_USER.update(environ, result)
75 else:
75 else:
76 return result.wsgi_application(environ, start_response)
76 return result.wsgi_application(environ, start_response)
77
77
78 #=======================================================================
78 #=======================================================================
79 # GET REPOSITORY
79 # GET REPOSITORY
80 #=======================================================================
80 #=======================================================================
81 try:
81 try:
82 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
82 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
83 if repo_name.endswith('/'):
83 if repo_name.endswith('/'):
84 repo_name = repo_name.rstrip('/')
84 repo_name = repo_name.rstrip('/')
85 self.repository = repo_name
85 self.repository = repo_name
86 except:
86 except:
87 log.error(traceback.format_exc())
87 log.error(traceback.format_exc())
88 return HTTPInternalServerError()(environ, start_response)
88 return HTTPInternalServerError()(environ, start_response)
89
89
90 #===================================================================
90 #===================================================================
91 # CHECK PERMISSIONS FOR THIS REQUEST
91 # CHECK PERMISSIONS FOR THIS REQUEST
92 #===================================================================
92 #===================================================================
93 self.action = self.__get_action(environ)
93 self.action = self.__get_action(environ)
94 if self.action:
94 if self.action:
95 username = self.__get_environ_user(environ)
95 username = self.__get_environ_user(environ)
96 try:
96 try:
97 user = self.__get_user(username)
97 user = self.__get_user(username)
98 self.username = user.username
98 self.username = user.username
99 except:
99 except:
100 log.error(traceback.format_exc())
100 log.error(traceback.format_exc())
101 return HTTPInternalServerError()(environ, start_response)
101 return HTTPInternalServerError()(environ, start_response)
102
102
103 #check permissions for this repository
103 #check permissions for this repository
104 if self.action == 'push':
104 if self.action == 'push':
105 if not HasPermissionAnyMiddleware('repository.write',
105 if not HasPermissionAnyMiddleware('repository.write',
106 'repository.admin')\
106 'repository.admin')\
107 (user, repo_name):
107 (user, repo_name):
108 return HTTPForbidden()(environ, start_response)
108 return HTTPForbidden()(environ, start_response)
109
109
110 else:
110 else:
111 #any other action need at least read permission
111 #any other action need at least read permission
112 if not HasPermissionAnyMiddleware('repository.read',
112 if not HasPermissionAnyMiddleware('repository.read',
113 'repository.write',
113 'repository.write',
114 'repository.admin')\
114 'repository.admin')\
115 (user, repo_name):
115 (user, repo_name):
116 return HTTPForbidden()(environ, start_response)
116 return HTTPForbidden()(environ, start_response)
117
117
118 self.extras = {'ip':self.ipaddr,
118 self.extras = {'ip':self.ipaddr,
119 'username':self.username,
119 'username':self.username,
120 'action':self.action,
120 'action':self.action,
121 'repository':self.repository}
121 'repository':self.repository}
122
122
123 #===================================================================
123 #===================================================================
124 # MERCURIAL REQUEST HANDLING
124 # MERCURIAL REQUEST HANDLING
125 #===================================================================
125 #===================================================================
126 environ['PATH_INFO'] = '/'#since we wrap into hgweb, reset the path
126 environ['PATH_INFO'] = '/'#since we wrap into hgweb, reset the path
127 self.baseui = make_ui('db')
127 self.baseui = make_ui('db')
128 self.basepath = self.config['base_path']
128 self.basepath = self.config['base_path']
129 self.repo_path = os.path.join(self.basepath, repo_name)
129 self.repo_path = os.path.join(self.basepath, repo_name)
130
130
131 #quick check if that dir exists...
131 #quick check if that dir exists...
132 if check_repo_fast(repo_name, self.basepath):
132 if check_repo_fast(repo_name, self.basepath):
133 return HTTPNotFound()(environ, start_response)
133 return HTTPNotFound()(environ, start_response)
134 try:
134 try:
135 app = wsgiapplication(self.__make_app)
135 app = wsgiapplication(self.__make_app)
136 except RepoError, e:
136 except RepoError, e:
137 if str(e).find('not found') != -1:
137 if str(e).find('not found') != -1:
138 return HTTPNotFound()(environ, start_response)
138 return HTTPNotFound()(environ, start_response)
139 except Exception:
139 except Exception:
140 log.error(traceback.format_exc())
140 log.error(traceback.format_exc())
141 return HTTPInternalServerError()(environ, start_response)
141 return HTTPInternalServerError()(environ, start_response)
142
142
143 #invalidate cache on push
143 #invalidate cache on push
144 if self.action == 'push':
144 if self.action == 'push':
145 self.__invalidate_cache(repo_name)
145 self.__invalidate_cache(repo_name)
146 messages = []
146 messages = []
147 messages.append('thank you for using rhodecode')
147 messages.append('thank you for using rhodecode')
148
148
149 return self.msg_wrapper(app, environ, start_response, messages)
149 return self.msg_wrapper(app, environ, start_response, messages)
150 else:
150 else:
151 return app(environ, start_response)
151 return app(environ, start_response)
152
152
153
153
154 def msg_wrapper(self, app, environ, start_response, messages=[]):
154 def msg_wrapper(self, app, environ, start_response, messages=[]):
155 """
155 """
156 Wrapper for custom messages that come out of mercurial respond messages
156 Wrapper for custom messages that come out of mercurial respond messages
157 is a list of messages that the user will see at the end of response
157 is a list of messages that the user will see at the end of response
158 from merurial protocol actions that involves remote answers
158 from merurial protocol actions that involves remote answers
159 :param app:
159 :param app:
160 :param environ:
160 :param environ:
161 :param start_response:
161 :param start_response:
162 """
162 """
163 def custom_messages(msg_list):
163 def custom_messages(msg_list):
164 for msg in msg_list:
164 for msg in msg_list:
165 yield msg + '\n'
165 yield msg + '\n'
166 org_response = app(environ, start_response)
166 org_response = app(environ, start_response)
167 return chain(org_response, custom_messages(messages))
167 return chain(org_response, custom_messages(messages))
168
168
169 def __make_app(self):
169 def __make_app(self):
170 hgserve = hgweb(str(self.repo_path), baseui=self.baseui)
170 hgserve = hgweb(str(self.repo_path), baseui=self.baseui)
171 return self.__load_web_settings(hgserve, self.extras)
171 return self.__load_web_settings(hgserve, self.extras)
172
172
173 def __get_environ_user(self, environ):
173 def __get_environ_user(self, environ):
174 return environ.get('REMOTE_USER')
174 return environ.get('REMOTE_USER')
175
175
176 def __get_user(self, username):
176 def __get_user(self, username):
177 return UserModel().get_by_username(username, cache=True)
177 return UserModel().get_by_username(username, cache=True)
178
178
179 def __get_action(self, environ):
179 def __get_action(self, environ):
180 """
180 """
181 Maps mercurial request commands into a clone,pull or push command.
181 Maps mercurial request commands into a clone,pull or push command.
182 This should always return a valid command string
182 This should always return a valid command string
183 :param environ:
183 :param environ:
184 """
184 """
185 mapping = {'changegroup': 'pull',
185 mapping = {'changegroup': 'pull',
186 'changegroupsubset': 'pull',
186 'changegroupsubset': 'pull',
187 'stream_out': 'pull',
187 'stream_out': 'pull',
188 'listkeys': 'pull',
188 'listkeys': 'pull',
189 'unbundle': 'push',
189 'unbundle': 'push',
190 'pushkey': 'push', }
190 'pushkey': 'push', }
191 for qry in environ['QUERY_STRING'].split('&'):
191 for qry in environ['QUERY_STRING'].split('&'):
192 if qry.startswith('cmd'):
192 if qry.startswith('cmd'):
193 cmd = qry.split('=')[-1]
193 cmd = qry.split('=')[-1]
194 if mapping.has_key(cmd):
194 if mapping.has_key(cmd):
195 return mapping[cmd]
195 return mapping[cmd]
196 else:
196 else:
197 return cmd
197 return cmd
198
198
199 def __invalidate_cache(self, repo_name):
199 def __invalidate_cache(self, repo_name):
200 """we know that some change was made to repositories and we should
200 """we know that some change was made to repositories and we should
201 invalidate the cache to see the changes right away but only for
201 invalidate the cache to see the changes right away but only for
202 push requests"""
202 push requests"""
203 invalidate_cache('get_repo_cached_%s' % repo_name)
203 invalidate_cache('get_repo_cached_%s' % repo_name)
204
204
205
205
206 def __load_web_settings(self, hgserve, extras={}):
206 def __load_web_settings(self, hgserve, extras={}):
207 #set the global ui for hgserve instance passed
207 #set the global ui for hgserve instance passed
208 hgserve.repo.ui = self.baseui
208 hgserve.repo.ui = self.baseui
209
209
210 hgrc = os.path.join(self.repo_path, '.hg', 'hgrc')
210 hgrc = os.path.join(self.repo_path, '.hg', 'hgrc')
211
211
212 #inject some additional parameters that will be available in ui
212 #inject some additional parameters that will be available in ui
213 #for hooks
213 #for hooks
214 for k, v in extras.items():
214 for k, v in extras.items():
215 hgserve.repo.ui.setconfig('rhodecode_extras', k, v)
215 hgserve.repo.ui.setconfig('rhodecode_extras', k, v)
216
216
217 repoui = make_ui('file', hgrc, False)
217 repoui = make_ui('file', hgrc, False)
218
218
219 if repoui:
219 if repoui:
220 #overwrite our ui instance with the section from hgrc file
220 #overwrite our ui instance with the section from hgrc file
221 for section in ui_sections:
221 for section in ui_sections:
222 for k, v in repoui.configitems(section):
222 for k, v in repoui.configitems(section):
223 hgserve.repo.ui.setconfig(section, k, v)
223 hgserve.repo.ui.setconfig(section, k, v)
224
224
225 return hgserve
225 return hgserve
226
226
227
227
228
228
229
229
230
230
231
231
232
232
233
233
234
234
235
235
236
236
237
237
238
238
239
239
General Comments 0
You need to be logged in to leave comments. Login now