##// END OF EJS Templates
Refactoring of model get functions
marcink -
r1530:04027bdb beta
parent child Browse files
Show More
@@ -1,167 +1,167 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.permissions
3 rhodecode.controllers.admin.permissions
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 permissions controller for Rhodecode
6 permissions controller for Rhodecode
7
7
8 :created_on: Apr 27, 2010
8 :created_on: Apr 27, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29 from formencode import htmlfill
29 from formencode import htmlfill
30
30
31 from pylons import request, session, tmpl_context as c, url
31 from pylons import request, session, tmpl_context as c, url
32 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34
34
35 from rhodecode.lib import helpers as h
35 from rhodecode.lib import helpers as h
36 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
36 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
37 from rhodecode.lib.base import BaseController, render
37 from rhodecode.lib.base import BaseController, render
38 from rhodecode.model.forms import DefaultPermissionsForm
38 from rhodecode.model.forms import DefaultPermissionsForm
39 from rhodecode.model.permission import PermissionModel
39 from rhodecode.model.permission import PermissionModel
40 from rhodecode.model.db import User
40 from rhodecode.model.db import User
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44
44
45 class PermissionsController(BaseController):
45 class PermissionsController(BaseController):
46 """REST Controller styled on the Atom Publishing Protocol"""
46 """REST Controller styled on the Atom Publishing Protocol"""
47 # To properly map this controller, ensure your config/routing.py
47 # To properly map this controller, ensure your config/routing.py
48 # file has a resource setup:
48 # file has a resource setup:
49 # map.resource('permission', 'permissions')
49 # map.resource('permission', 'permissions')
50
50
51 @LoginRequired()
51 @LoginRequired()
52 @HasPermissionAllDecorator('hg.admin')
52 @HasPermissionAllDecorator('hg.admin')
53 def __before__(self):
53 def __before__(self):
54 c.admin_user = session.get('admin_user')
54 c.admin_user = session.get('admin_user')
55 c.admin_username = session.get('admin_username')
55 c.admin_username = session.get('admin_username')
56 super(PermissionsController, self).__before__()
56 super(PermissionsController, self).__before__()
57
57
58 self.perms_choices = [('repository.none', _('None'),),
58 self.perms_choices = [('repository.none', _('None'),),
59 ('repository.read', _('Read'),),
59 ('repository.read', _('Read'),),
60 ('repository.write', _('Write'),),
60 ('repository.write', _('Write'),),
61 ('repository.admin', _('Admin'),)]
61 ('repository.admin', _('Admin'),)]
62 self.register_choices = [
62 self.register_choices = [
63 ('hg.register.none',
63 ('hg.register.none',
64 _('disabled')),
64 _('disabled')),
65 ('hg.register.manual_activate',
65 ('hg.register.manual_activate',
66 _('allowed with manual account activation')),
66 _('allowed with manual account activation')),
67 ('hg.register.auto_activate',
67 ('hg.register.auto_activate',
68 _('allowed with automatic account activation')), ]
68 _('allowed with automatic account activation')), ]
69
69
70 self.create_choices = [('hg.create.none', _('Disabled')),
70 self.create_choices = [('hg.create.none', _('Disabled')),
71 ('hg.create.repository', _('Enabled'))]
71 ('hg.create.repository', _('Enabled'))]
72
72
73 def index(self, format='html'):
73 def index(self, format='html'):
74 """GET /permissions: All items in the collection"""
74 """GET /permissions: All items in the collection"""
75 # url('permissions')
75 # url('permissions')
76
76
77 def create(self):
77 def create(self):
78 """POST /permissions: Create a new item"""
78 """POST /permissions: Create a new item"""
79 # url('permissions')
79 # url('permissions')
80
80
81 def new(self, format='html'):
81 def new(self, format='html'):
82 """GET /permissions/new: Form to create a new item"""
82 """GET /permissions/new: Form to create a new item"""
83 # url('new_permission')
83 # url('new_permission')
84
84
85 def update(self, id):
85 def update(self, id):
86 """PUT /permissions/id: Update an existing item"""
86 """PUT /permissions/id: Update an existing item"""
87 # Forms posted to this method should contain a hidden field:
87 # Forms posted to this method should contain a hidden field:
88 # <input type="hidden" name="_method" value="PUT" />
88 # <input type="hidden" name="_method" value="PUT" />
89 # Or using helpers:
89 # Or using helpers:
90 # h.form(url('permission', id=ID),
90 # h.form(url('permission', id=ID),
91 # method='put')
91 # method='put')
92 # url('permission', id=ID)
92 # url('permission', id=ID)
93
93
94 permission_model = PermissionModel()
94 permission_model = PermissionModel()
95
95
96 _form = DefaultPermissionsForm([x[0] for x in self.perms_choices],
96 _form = DefaultPermissionsForm([x[0] for x in self.perms_choices],
97 [x[0] for x in self.register_choices],
97 [x[0] for x in self.register_choices],
98 [x[0] for x in self.create_choices])()
98 [x[0] for x in self.create_choices])()
99
99
100 try:
100 try:
101 form_result = _form.to_python(dict(request.POST))
101 form_result = _form.to_python(dict(request.POST))
102 form_result.update({'perm_user_name': id})
102 form_result.update({'perm_user_name': id})
103 permission_model.update(form_result)
103 permission_model.update(form_result)
104 h.flash(_('Default permissions updated successfully'),
104 h.flash(_('Default permissions updated successfully'),
105 category='success')
105 category='success')
106
106
107 except formencode.Invalid, errors:
107 except formencode.Invalid, errors:
108 c.perms_choices = self.perms_choices
108 c.perms_choices = self.perms_choices
109 c.register_choices = self.register_choices
109 c.register_choices = self.register_choices
110 c.create_choices = self.create_choices
110 c.create_choices = self.create_choices
111 defaults = errors.value
111 defaults = errors.value
112
112
113 return htmlfill.render(
113 return htmlfill.render(
114 render('admin/permissions/permissions.html'),
114 render('admin/permissions/permissions.html'),
115 defaults=defaults,
115 defaults=defaults,
116 errors=errors.error_dict or {},
116 errors=errors.error_dict or {},
117 prefix_error=False,
117 prefix_error=False,
118 encoding="UTF-8")
118 encoding="UTF-8")
119 except Exception:
119 except Exception:
120 log.error(traceback.format_exc())
120 log.error(traceback.format_exc())
121 h.flash(_('error occurred during update of permissions'),
121 h.flash(_('error occurred during update of permissions'),
122 category='error')
122 category='error')
123
123
124 return redirect(url('edit_permission', id=id))
124 return redirect(url('edit_permission', id=id))
125
125
126 def delete(self, id):
126 def delete(self, id):
127 """DELETE /permissions/id: Delete an existing item"""
127 """DELETE /permissions/id: Delete an existing item"""
128 # Forms posted to this method should contain a hidden field:
128 # Forms posted to this method should contain a hidden field:
129 # <input type="hidden" name="_method" value="DELETE" />
129 # <input type="hidden" name="_method" value="DELETE" />
130 # Or using helpers:
130 # Or using helpers:
131 # h.form(url('permission', id=ID),
131 # h.form(url('permission', id=ID),
132 # method='delete')
132 # method='delete')
133 # url('permission', id=ID)
133 # url('permission', id=ID)
134
134
135 def show(self, id, format='html'):
135 def show(self, id, format='html'):
136 """GET /permissions/id: Show a specific item"""
136 """GET /permissions/id: Show a specific item"""
137 # url('permission', id=ID)
137 # url('permission', id=ID)
138
138
139 def edit(self, id, format='html'):
139 def edit(self, id, format='html'):
140 """GET /permissions/id/edit: Form to edit an existing item"""
140 """GET /permissions/id/edit: Form to edit an existing item"""
141 #url('edit_permission', id=ID)
141 #url('edit_permission', id=ID)
142 c.perms_choices = self.perms_choices
142 c.perms_choices = self.perms_choices
143 c.register_choices = self.register_choices
143 c.register_choices = self.register_choices
144 c.create_choices = self.create_choices
144 c.create_choices = self.create_choices
145
145
146 if id == 'default':
146 if id == 'default':
147 default_user = User.by_username('default')
147 default_user = User.get_by_username('default')
148 defaults = {'_method': 'put',
148 defaults = {'_method': 'put',
149 'anonymous': default_user.active}
149 'anonymous': default_user.active}
150
150
151 for p in default_user.user_perms:
151 for p in default_user.user_perms:
152 if p.permission.permission_name.startswith('repository.'):
152 if p.permission.permission_name.startswith('repository.'):
153 defaults['default_perm'] = p.permission.permission_name
153 defaults['default_perm'] = p.permission.permission_name
154
154
155 if p.permission.permission_name.startswith('hg.register.'):
155 if p.permission.permission_name.startswith('hg.register.'):
156 defaults['default_register'] = p.permission.permission_name
156 defaults['default_register'] = p.permission.permission_name
157
157
158 if p.permission.permission_name.startswith('hg.create.'):
158 if p.permission.permission_name.startswith('hg.create.'):
159 defaults['default_create'] = p.permission.permission_name
159 defaults['default_create'] = p.permission.permission_name
160
160
161 return htmlfill.render(
161 return htmlfill.render(
162 render('admin/permissions/permissions.html'),
162 render('admin/permissions/permissions.html'),
163 defaults=defaults,
163 defaults=defaults,
164 encoding="UTF-8",
164 encoding="UTF-8",
165 force_defaults=True,)
165 force_defaults=True,)
166 else:
166 else:
167 return redirect(url('admin_home'))
167 return redirect(url('admin_home'))
@@ -1,431 +1,431 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.repos
3 rhodecode.controllers.admin.repos
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Admin controller for RhodeCode
6 Admin controller for RhodeCode
7
7
8 :created_on: Apr 7, 2010
8 :created_on: Apr 7, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29 from operator import itemgetter
29 from operator import itemgetter
30 from formencode import htmlfill
30 from formencode import htmlfill
31
31
32 from paste.httpexceptions import HTTPInternalServerError
32 from paste.httpexceptions import HTTPInternalServerError
33 from pylons import request, response, session, tmpl_context as c, url
33 from pylons import request, response, session, tmpl_context as c, url
34 from pylons.controllers.util import abort, redirect
34 from pylons.controllers.util import abort, redirect
35 from pylons.i18n.translation import _
35 from pylons.i18n.translation import _
36
36
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 HasPermissionAnyDecorator
39 HasPermissionAnyDecorator
40 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
41 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
42 from rhodecode.lib.helpers import get_token
42 from rhodecode.lib.helpers import get_token
43 from rhodecode.model.db import User, Repository, UserFollowing, Group
43 from rhodecode.model.db import User, Repository, UserFollowing, Group
44 from rhodecode.model.forms import RepoForm
44 from rhodecode.model.forms import RepoForm
45 from rhodecode.model.scm import ScmModel
45 from rhodecode.model.scm import ScmModel
46 from rhodecode.model.repo import RepoModel
46 from rhodecode.model.repo import RepoModel
47 from sqlalchemy.exc import IntegrityError
47 from sqlalchemy.exc import IntegrityError
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51
51
52 class ReposController(BaseController):
52 class ReposController(BaseController):
53 """
53 """
54 REST Controller styled on the Atom Publishing Protocol"""
54 REST Controller styled on the Atom Publishing Protocol"""
55 # To properly map this controller, ensure your config/routing.py
55 # To properly map this controller, ensure your config/routing.py
56 # file has a resource setup:
56 # file has a resource setup:
57 # map.resource('repo', 'repos')
57 # map.resource('repo', 'repos')
58
58
59 @LoginRequired()
59 @LoginRequired()
60 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
60 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
61 def __before__(self):
61 def __before__(self):
62 c.admin_user = session.get('admin_user')
62 c.admin_user = session.get('admin_user')
63 c.admin_username = session.get('admin_username')
63 c.admin_username = session.get('admin_username')
64 super(ReposController, self).__before__()
64 super(ReposController, self).__before__()
65
65
66 def __load_defaults(self):
66 def __load_defaults(self):
67 repo_model = RepoModel()
67 repo_model = RepoModel()
68
68
69 c.repo_groups = [('', '')]
69 c.repo_groups = [('', '')]
70 parents_link = lambda k: h.literal('&raquo;'.join(
70 parents_link = lambda k: h.literal('&raquo;'.join(
71 map(lambda k: k.group_name,
71 map(lambda k: k.group_name,
72 k.parents + [k])
72 k.parents + [k])
73 )
73 )
74 )
74 )
75
75
76 c.repo_groups.extend([(x.group_id, parents_link(x)) for \
76 c.repo_groups.extend([(x.group_id, parents_link(x)) for \
77 x in self.sa.query(Group).all()])
77 x in self.sa.query(Group).all()])
78 c.repo_groups = sorted(c.repo_groups,
78 c.repo_groups = sorted(c.repo_groups,
79 key=lambda t: t[1].split('&raquo;')[0])
79 key=lambda t: t[1].split('&raquo;')[0])
80 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
80 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
81 c.users_array = repo_model.get_users_js()
81 c.users_array = repo_model.get_users_js()
82 c.users_groups_array = repo_model.get_users_groups_js()
82 c.users_groups_array = repo_model.get_users_groups_js()
83
83
84 def __load_data(self, repo_name=None):
84 def __load_data(self, repo_name=None):
85 """
85 """
86 Load defaults settings for edit, and update
86 Load defaults settings for edit, and update
87
87
88 :param repo_name:
88 :param repo_name:
89 """
89 """
90 self.__load_defaults()
90 self.__load_defaults()
91
91
92 c.repo_info = db_repo = Repository.by_repo_name(repo_name)
92 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
93 repo = scm_repo = db_repo.scm_instance
93 repo = scm_repo = db_repo.scm_instance
94
94
95 if c.repo_info is None:
95 if c.repo_info is None:
96 h.flash(_('%s repository is not mapped to db perhaps'
96 h.flash(_('%s repository is not mapped to db perhaps'
97 ' it was created or renamed from the filesystem'
97 ' it was created or renamed from the filesystem'
98 ' please run the application again'
98 ' please run the application again'
99 ' in order to rescan repositories') % repo_name,
99 ' in order to rescan repositories') % repo_name,
100 category='error')
100 category='error')
101
101
102 return redirect(url('repos'))
102 return redirect(url('repos'))
103
103
104 c.default_user_id = User.by_username('default').user_id
104 c.default_user_id = User.get_by_username('default').user_id
105 c.in_public_journal = self.sa.query(UserFollowing)\
105 c.in_public_journal = self.sa.query(UserFollowing)\
106 .filter(UserFollowing.user_id == c.default_user_id)\
106 .filter(UserFollowing.user_id == c.default_user_id)\
107 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
107 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
108
108
109 if c.repo_info.stats:
109 if c.repo_info.stats:
110 last_rev = c.repo_info.stats.stat_on_revision
110 last_rev = c.repo_info.stats.stat_on_revision
111 else:
111 else:
112 last_rev = 0
112 last_rev = 0
113 c.stats_revision = last_rev
113 c.stats_revision = last_rev
114
114
115 c.repo_last_rev = repo.count() - 1 if repo.revisions else 0
115 c.repo_last_rev = repo.count() - 1 if repo.revisions else 0
116
116
117 if last_rev == 0 or c.repo_last_rev == 0:
117 if last_rev == 0 or c.repo_last_rev == 0:
118 c.stats_percentage = 0
118 c.stats_percentage = 0
119 else:
119 else:
120 c.stats_percentage = '%.2f' % ((float((last_rev)) /
120 c.stats_percentage = '%.2f' % ((float((last_rev)) /
121 c.repo_last_rev) * 100)
121 c.repo_last_rev) * 100)
122
122
123 defaults = c.repo_info.get_dict()
123 defaults = c.repo_info.get_dict()
124 group, repo_name = c.repo_info.groups_and_repo
124 group, repo_name = c.repo_info.groups_and_repo
125 defaults['repo_name'] = repo_name
125 defaults['repo_name'] = repo_name
126 defaults['repo_group'] = getattr(group[-1] if group else None,
126 defaults['repo_group'] = getattr(group[-1] if group else None,
127 'group_id', None)
127 'group_id', None)
128
128
129 #fill owner
129 #fill owner
130 if c.repo_info.user:
130 if c.repo_info.user:
131 defaults.update({'user': c.repo_info.user.username})
131 defaults.update({'user': c.repo_info.user.username})
132 else:
132 else:
133 replacement_user = self.sa.query(User)\
133 replacement_user = self.sa.query(User)\
134 .filter(User.admin == True).first().username
134 .filter(User.admin == True).first().username
135 defaults.update({'user': replacement_user})
135 defaults.update({'user': replacement_user})
136
136
137 #fill repository users
137 #fill repository users
138 for p in c.repo_info.repo_to_perm:
138 for p in c.repo_info.repo_to_perm:
139 defaults.update({'u_perm_%s' % p.user.username:
139 defaults.update({'u_perm_%s' % p.user.username:
140 p.permission.permission_name})
140 p.permission.permission_name})
141
141
142 #fill repository groups
142 #fill repository groups
143 for p in c.repo_info.users_group_to_perm:
143 for p in c.repo_info.users_group_to_perm:
144 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
144 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
145 p.permission.permission_name})
145 p.permission.permission_name})
146
146
147 return defaults
147 return defaults
148
148
149 @HasPermissionAllDecorator('hg.admin')
149 @HasPermissionAllDecorator('hg.admin')
150 def index(self, format='html'):
150 def index(self, format='html'):
151 """GET /repos: All items in the collection"""
151 """GET /repos: All items in the collection"""
152 # url('repos')
152 # url('repos')
153
153
154 c.repos_list = ScmModel().get_repos(Repository.query()
154 c.repos_list = ScmModel().get_repos(Repository.query()
155 .order_by(Repository.repo_name)
155 .order_by(Repository.repo_name)
156 .all(), sort_key='name_sort')
156 .all(), sort_key='name_sort')
157 return render('admin/repos/repos.html')
157 return render('admin/repos/repos.html')
158
158
159 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
159 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
160 def create(self):
160 def create(self):
161 """
161 """
162 POST /repos: Create a new item"""
162 POST /repos: Create a new item"""
163 # url('repos')
163 # url('repos')
164 repo_model = RepoModel()
164 repo_model = RepoModel()
165 self.__load_defaults()
165 self.__load_defaults()
166 form_result = {}
166 form_result = {}
167 try:
167 try:
168 form_result = RepoForm(repo_groups=c.repo_groups_choices)()\
168 form_result = RepoForm(repo_groups=c.repo_groups_choices)()\
169 .to_python(dict(request.POST))
169 .to_python(dict(request.POST))
170 repo_model.create(form_result, self.rhodecode_user)
170 repo_model.create(form_result, self.rhodecode_user)
171 if form_result['clone_uri']:
171 if form_result['clone_uri']:
172 h.flash(_('created repository %s from %s') \
172 h.flash(_('created repository %s from %s') \
173 % (form_result['repo_name'], form_result['clone_uri']),
173 % (form_result['repo_name'], form_result['clone_uri']),
174 category='success')
174 category='success')
175 else:
175 else:
176 h.flash(_('created repository %s') % form_result['repo_name'],
176 h.flash(_('created repository %s') % form_result['repo_name'],
177 category='success')
177 category='success')
178
178
179 if request.POST.get('user_created'):
179 if request.POST.get('user_created'):
180 #created by regular non admin user
180 #created by regular non admin user
181 action_logger(self.rhodecode_user, 'user_created_repo',
181 action_logger(self.rhodecode_user, 'user_created_repo',
182 form_result['repo_name_full'], '', self.sa)
182 form_result['repo_name_full'], '', self.sa)
183 else:
183 else:
184 action_logger(self.rhodecode_user, 'admin_created_repo',
184 action_logger(self.rhodecode_user, 'admin_created_repo',
185 form_result['repo_name_full'], '', self.sa)
185 form_result['repo_name_full'], '', self.sa)
186
186
187 except formencode.Invalid, errors:
187 except formencode.Invalid, errors:
188
188
189 c.new_repo = errors.value['repo_name']
189 c.new_repo = errors.value['repo_name']
190
190
191 if request.POST.get('user_created'):
191 if request.POST.get('user_created'):
192 r = render('admin/repos/repo_add_create_repository.html')
192 r = render('admin/repos/repo_add_create_repository.html')
193 else:
193 else:
194 r = render('admin/repos/repo_add.html')
194 r = render('admin/repos/repo_add.html')
195
195
196 return htmlfill.render(
196 return htmlfill.render(
197 r,
197 r,
198 defaults=errors.value,
198 defaults=errors.value,
199 errors=errors.error_dict or {},
199 errors=errors.error_dict or {},
200 prefix_error=False,
200 prefix_error=False,
201 encoding="UTF-8")
201 encoding="UTF-8")
202
202
203 except Exception:
203 except Exception:
204 log.error(traceback.format_exc())
204 log.error(traceback.format_exc())
205 msg = _('error occurred during creation of repository %s') \
205 msg = _('error occurred during creation of repository %s') \
206 % form_result.get('repo_name')
206 % form_result.get('repo_name')
207 h.flash(msg, category='error')
207 h.flash(msg, category='error')
208 if request.POST.get('user_created'):
208 if request.POST.get('user_created'):
209 return redirect(url('home'))
209 return redirect(url('home'))
210 return redirect(url('repos'))
210 return redirect(url('repos'))
211
211
212 @HasPermissionAllDecorator('hg.admin')
212 @HasPermissionAllDecorator('hg.admin')
213 def new(self, format='html'):
213 def new(self, format='html'):
214 """GET /repos/new: Form to create a new item"""
214 """GET /repos/new: Form to create a new item"""
215 new_repo = request.GET.get('repo', '')
215 new_repo = request.GET.get('repo', '')
216 c.new_repo = repo_name_slug(new_repo)
216 c.new_repo = repo_name_slug(new_repo)
217 self.__load_defaults()
217 self.__load_defaults()
218 return render('admin/repos/repo_add.html')
218 return render('admin/repos/repo_add.html')
219
219
220 @HasPermissionAllDecorator('hg.admin')
220 @HasPermissionAllDecorator('hg.admin')
221 def update(self, repo_name):
221 def update(self, repo_name):
222 """
222 """
223 PUT /repos/repo_name: Update an existing item"""
223 PUT /repos/repo_name: Update an existing item"""
224 # Forms posted to this method should contain a hidden field:
224 # Forms posted to this method should contain a hidden field:
225 # <input type="hidden" name="_method" value="PUT" />
225 # <input type="hidden" name="_method" value="PUT" />
226 # Or using helpers:
226 # Or using helpers:
227 # h.form(url('repo', repo_name=ID),
227 # h.form(url('repo', repo_name=ID),
228 # method='put')
228 # method='put')
229 # url('repo', repo_name=ID)
229 # url('repo', repo_name=ID)
230 self.__load_defaults()
230 self.__load_defaults()
231 repo_model = RepoModel()
231 repo_model = RepoModel()
232 changed_name = repo_name
232 changed_name = repo_name
233 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
233 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
234 repo_groups=c.repo_groups_choices)()
234 repo_groups=c.repo_groups_choices)()
235 try:
235 try:
236 form_result = _form.to_python(dict(request.POST))
236 form_result = _form.to_python(dict(request.POST))
237 repo_model.update(repo_name, form_result)
237 repo_model.update(repo_name, form_result)
238 invalidate_cache('get_repo_cached_%s' % repo_name)
238 invalidate_cache('get_repo_cached_%s' % repo_name)
239 h.flash(_('Repository %s updated successfully' % repo_name),
239 h.flash(_('Repository %s updated successfully' % repo_name),
240 category='success')
240 category='success')
241 changed_name = form_result['repo_name_full']
241 changed_name = form_result['repo_name_full']
242 action_logger(self.rhodecode_user, 'admin_updated_repo',
242 action_logger(self.rhodecode_user, 'admin_updated_repo',
243 changed_name, '', self.sa)
243 changed_name, '', self.sa)
244
244
245 except formencode.Invalid, errors:
245 except formencode.Invalid, errors:
246 defaults = self.__load_data(repo_name)
246 defaults = self.__load_data(repo_name)
247 defaults.update(errors.value)
247 defaults.update(errors.value)
248 return htmlfill.render(
248 return htmlfill.render(
249 render('admin/repos/repo_edit.html'),
249 render('admin/repos/repo_edit.html'),
250 defaults=defaults,
250 defaults=defaults,
251 errors=errors.error_dict or {},
251 errors=errors.error_dict or {},
252 prefix_error=False,
252 prefix_error=False,
253 encoding="UTF-8")
253 encoding="UTF-8")
254
254
255 except Exception:
255 except Exception:
256 log.error(traceback.format_exc())
256 log.error(traceback.format_exc())
257 h.flash(_('error occurred during update of repository %s') \
257 h.flash(_('error occurred during update of repository %s') \
258 % repo_name, category='error')
258 % repo_name, category='error')
259 return redirect(url('edit_repo', repo_name=changed_name))
259 return redirect(url('edit_repo', repo_name=changed_name))
260
260
261 @HasPermissionAllDecorator('hg.admin')
261 @HasPermissionAllDecorator('hg.admin')
262 def delete(self, repo_name):
262 def delete(self, repo_name):
263 """
263 """
264 DELETE /repos/repo_name: Delete an existing item"""
264 DELETE /repos/repo_name: Delete an existing item"""
265 # Forms posted to this method should contain a hidden field:
265 # Forms posted to this method should contain a hidden field:
266 # <input type="hidden" name="_method" value="DELETE" />
266 # <input type="hidden" name="_method" value="DELETE" />
267 # Or using helpers:
267 # Or using helpers:
268 # h.form(url('repo', repo_name=ID),
268 # h.form(url('repo', repo_name=ID),
269 # method='delete')
269 # method='delete')
270 # url('repo', repo_name=ID)
270 # url('repo', repo_name=ID)
271
271
272 repo_model = RepoModel()
272 repo_model = RepoModel()
273 repo = repo_model.get_by_repo_name(repo_name)
273 repo = repo_model.get_by_repo_name(repo_name)
274 if not repo:
274 if not repo:
275 h.flash(_('%s repository is not mapped to db perhaps'
275 h.flash(_('%s repository is not mapped to db perhaps'
276 ' it was moved or renamed from the filesystem'
276 ' it was moved or renamed from the filesystem'
277 ' please run the application again'
277 ' please run the application again'
278 ' in order to rescan repositories') % repo_name,
278 ' in order to rescan repositories') % repo_name,
279 category='error')
279 category='error')
280
280
281 return redirect(url('repos'))
281 return redirect(url('repos'))
282 try:
282 try:
283 action_logger(self.rhodecode_user, 'admin_deleted_repo',
283 action_logger(self.rhodecode_user, 'admin_deleted_repo',
284 repo_name, '', self.sa)
284 repo_name, '', self.sa)
285 repo_model.delete(repo)
285 repo_model.delete(repo)
286 invalidate_cache('get_repo_cached_%s' % repo_name)
286 invalidate_cache('get_repo_cached_%s' % repo_name)
287 h.flash(_('deleted repository %s') % repo_name, category='success')
287 h.flash(_('deleted repository %s') % repo_name, category='success')
288
288
289 except IntegrityError, e:
289 except IntegrityError, e:
290 if e.message.find('repositories_fork_id_fkey'):
290 if e.message.find('repositories_fork_id_fkey'):
291 log.error(traceback.format_exc())
291 log.error(traceback.format_exc())
292 h.flash(_('Cannot delete %s it still contains attached '
292 h.flash(_('Cannot delete %s it still contains attached '
293 'forks') % repo_name,
293 'forks') % repo_name,
294 category='warning')
294 category='warning')
295 else:
295 else:
296 log.error(traceback.format_exc())
296 log.error(traceback.format_exc())
297 h.flash(_('An error occurred during '
297 h.flash(_('An error occurred during '
298 'deletion of %s') % repo_name,
298 'deletion of %s') % repo_name,
299 category='error')
299 category='error')
300
300
301 except Exception, e:
301 except Exception, e:
302 log.error(traceback.format_exc())
302 log.error(traceback.format_exc())
303 h.flash(_('An error occurred during deletion of %s') % repo_name,
303 h.flash(_('An error occurred during deletion of %s') % repo_name,
304 category='error')
304 category='error')
305
305
306 return redirect(url('repos'))
306 return redirect(url('repos'))
307
307
308 @HasPermissionAllDecorator('hg.admin')
308 @HasPermissionAllDecorator('hg.admin')
309 def delete_perm_user(self, repo_name):
309 def delete_perm_user(self, repo_name):
310 """
310 """
311 DELETE an existing repository permission user
311 DELETE an existing repository permission user
312
312
313 :param repo_name:
313 :param repo_name:
314 """
314 """
315
315
316 try:
316 try:
317 repo_model = RepoModel()
317 repo_model = RepoModel()
318 repo_model.delete_perm_user(request.POST, repo_name)
318 repo_model.delete_perm_user(request.POST, repo_name)
319 except Exception, e:
319 except Exception, e:
320 h.flash(_('An error occurred during deletion of repository user'),
320 h.flash(_('An error occurred during deletion of repository user'),
321 category='error')
321 category='error')
322 raise HTTPInternalServerError()
322 raise HTTPInternalServerError()
323
323
324 @HasPermissionAllDecorator('hg.admin')
324 @HasPermissionAllDecorator('hg.admin')
325 def delete_perm_users_group(self, repo_name):
325 def delete_perm_users_group(self, repo_name):
326 """
326 """
327 DELETE an existing repository permission users group
327 DELETE an existing repository permission users group
328
328
329 :param repo_name:
329 :param repo_name:
330 """
330 """
331 try:
331 try:
332 repo_model = RepoModel()
332 repo_model = RepoModel()
333 repo_model.delete_perm_users_group(request.POST, repo_name)
333 repo_model.delete_perm_users_group(request.POST, repo_name)
334 except Exception, e:
334 except Exception, e:
335 h.flash(_('An error occurred during deletion of repository'
335 h.flash(_('An error occurred during deletion of repository'
336 ' users groups'),
336 ' users groups'),
337 category='error')
337 category='error')
338 raise HTTPInternalServerError()
338 raise HTTPInternalServerError()
339
339
340 @HasPermissionAllDecorator('hg.admin')
340 @HasPermissionAllDecorator('hg.admin')
341 def repo_stats(self, repo_name):
341 def repo_stats(self, repo_name):
342 """
342 """
343 DELETE an existing repository statistics
343 DELETE an existing repository statistics
344
344
345 :param repo_name:
345 :param repo_name:
346 """
346 """
347
347
348 try:
348 try:
349 repo_model = RepoModel()
349 repo_model = RepoModel()
350 repo_model.delete_stats(repo_name)
350 repo_model.delete_stats(repo_name)
351 except Exception, e:
351 except Exception, e:
352 h.flash(_('An error occurred during deletion of repository stats'),
352 h.flash(_('An error occurred during deletion of repository stats'),
353 category='error')
353 category='error')
354 return redirect(url('edit_repo', repo_name=repo_name))
354 return redirect(url('edit_repo', repo_name=repo_name))
355
355
356 @HasPermissionAllDecorator('hg.admin')
356 @HasPermissionAllDecorator('hg.admin')
357 def repo_cache(self, repo_name):
357 def repo_cache(self, repo_name):
358 """
358 """
359 INVALIDATE existing repository cache
359 INVALIDATE existing repository cache
360
360
361 :param repo_name:
361 :param repo_name:
362 """
362 """
363
363
364 try:
364 try:
365 ScmModel().mark_for_invalidation(repo_name)
365 ScmModel().mark_for_invalidation(repo_name)
366 except Exception, e:
366 except Exception, e:
367 h.flash(_('An error occurred during cache invalidation'),
367 h.flash(_('An error occurred during cache invalidation'),
368 category='error')
368 category='error')
369 return redirect(url('edit_repo', repo_name=repo_name))
369 return redirect(url('edit_repo', repo_name=repo_name))
370
370
371 @HasPermissionAllDecorator('hg.admin')
371 @HasPermissionAllDecorator('hg.admin')
372 def repo_public_journal(self, repo_name):
372 def repo_public_journal(self, repo_name):
373 """
373 """
374 Set's this repository to be visible in public journal,
374 Set's this repository to be visible in public journal,
375 in other words assing default user to follow this repo
375 in other words assing default user to follow this repo
376
376
377 :param repo_name:
377 :param repo_name:
378 """
378 """
379
379
380 cur_token = request.POST.get('auth_token')
380 cur_token = request.POST.get('auth_token')
381 token = get_token()
381 token = get_token()
382 if cur_token == token:
382 if cur_token == token:
383 try:
383 try:
384 repo_id = Repository.by_repo_name(repo_name).repo_id
384 repo_id = Repository.get_by_repo_name(repo_name).repo_id
385 user_id = User.by_username('default').user_id
385 user_id = User.get_by_username('default').user_id
386 self.scm_model.toggle_following_repo(repo_id, user_id)
386 self.scm_model.toggle_following_repo(repo_id, user_id)
387 h.flash(_('Updated repository visibility in public journal'),
387 h.flash(_('Updated repository visibility in public journal'),
388 category='success')
388 category='success')
389 except:
389 except:
390 h.flash(_('An error occurred during setting this'
390 h.flash(_('An error occurred during setting this'
391 ' repository in public journal'),
391 ' repository in public journal'),
392 category='error')
392 category='error')
393
393
394 else:
394 else:
395 h.flash(_('Token mismatch'), category='error')
395 h.flash(_('Token mismatch'), category='error')
396 return redirect(url('edit_repo', repo_name=repo_name))
396 return redirect(url('edit_repo', repo_name=repo_name))
397
397
398 @HasPermissionAllDecorator('hg.admin')
398 @HasPermissionAllDecorator('hg.admin')
399 def repo_pull(self, repo_name):
399 def repo_pull(self, repo_name):
400 """
400 """
401 Runs task to update given repository with remote changes,
401 Runs task to update given repository with remote changes,
402 ie. make pull on remote location
402 ie. make pull on remote location
403
403
404 :param repo_name:
404 :param repo_name:
405 """
405 """
406 try:
406 try:
407 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
407 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
408 h.flash(_('Pulled from remote location'), category='success')
408 h.flash(_('Pulled from remote location'), category='success')
409 except Exception, e:
409 except Exception, e:
410 h.flash(_('An error occurred during pull from remote location'),
410 h.flash(_('An error occurred during pull from remote location'),
411 category='error')
411 category='error')
412
412
413 return redirect(url('edit_repo', repo_name=repo_name))
413 return redirect(url('edit_repo', repo_name=repo_name))
414
414
415 @HasPermissionAllDecorator('hg.admin')
415 @HasPermissionAllDecorator('hg.admin')
416 def show(self, repo_name, format='html'):
416 def show(self, repo_name, format='html'):
417 """GET /repos/repo_name: Show a specific item"""
417 """GET /repos/repo_name: Show a specific item"""
418 # url('repo', repo_name=ID)
418 # url('repo', repo_name=ID)
419
419
420 @HasPermissionAllDecorator('hg.admin')
420 @HasPermissionAllDecorator('hg.admin')
421 def edit(self, repo_name, format='html'):
421 def edit(self, repo_name, format='html'):
422 """GET /repos/repo_name/edit: Form to edit an existing item"""
422 """GET /repos/repo_name/edit: Form to edit an existing item"""
423 # url('edit_repo', repo_name=ID)
423 # url('edit_repo', repo_name=ID)
424 defaults = self.__load_data(repo_name)
424 defaults = self.__load_data(repo_name)
425
425
426 return htmlfill.render(
426 return htmlfill.render(
427 render('admin/repos/repo_edit.html'),
427 render('admin/repos/repo_edit.html'),
428 defaults=defaults,
428 defaults=defaults,
429 encoding="UTF-8",
429 encoding="UTF-8",
430 force_defaults=False
430 force_defaults=False
431 )
431 )
@@ -1,168 +1,166 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.login
3 rhodecode.controllers.login
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Login controller for rhodeocode
6 Login controller for rhodeocode
7
7
8 :created_on: Apr 22, 2010
8 :created_on: Apr 22, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import formencode
27 import formencode
28
28
29 from formencode import htmlfill
29 from formencode import htmlfill
30
30
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33 from pylons import request, response, session, tmpl_context as c, url
33 from pylons import request, response, session, tmpl_context as c, url
34
34
35 import rhodecode.lib.helpers as h
35 import rhodecode.lib.helpers as h
36 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
36 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
37 from rhodecode.lib.base import BaseController, render
37 from rhodecode.lib.base import BaseController, render
38 from rhodecode.model.db import User
38 from rhodecode.model.db import User
39 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
39 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
40 from rhodecode.model.user import UserModel
40 from rhodecode.model.user import UserModel
41
41
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45
45
46 class LoginController(BaseController):
46 class LoginController(BaseController):
47
47
48 def __before__(self):
48 def __before__(self):
49 super(LoginController, self).__before__()
49 super(LoginController, self).__before__()
50
50
51 def index(self):
51 def index(self):
52 #redirect if already logged in
52 #redirect if already logged in
53 c.came_from = request.GET.get('came_from', None)
53 c.came_from = request.GET.get('came_from', None)
54
54
55 if self.rhodecode_user.is_authenticated \
55 if self.rhodecode_user.is_authenticated \
56 and self.rhodecode_user.username != 'default':
56 and self.rhodecode_user.username != 'default':
57
57
58 return redirect(url('home'))
58 return redirect(url('home'))
59
59
60 if request.POST:
60 if request.POST:
61 #import Login Form validator class
61 #import Login Form validator class
62 login_form = LoginForm()
62 login_form = LoginForm()
63 try:
63 try:
64 c.form_result = login_form.to_python(dict(request.POST))
64 c.form_result = login_form.to_python(dict(request.POST))
65 #form checks for username/password, now we're authenticated
65 #form checks for username/password, now we're authenticated
66 username = c.form_result['username']
66 username = c.form_result['username']
67 user = User.by_username(username,
67 user = User.get_by_username(username, case_insensitive=True)
68 case_insensitive=True)
69 auth_user = AuthUser(user.user_id)
68 auth_user = AuthUser(user.user_id)
70 auth_user.set_authenticated()
69 auth_user.set_authenticated()
71 session['rhodecode_user'] = auth_user
70 session['rhodecode_user'] = auth_user
72 session.save()
71 session.save()
73
72
74 log.info('user %s is now authenticated and stored in session',
73 log.info('user %s is now authenticated and stored in session',
75 username)
74 username)
76 user.update_lastlogin()
75 user.update_lastlogin()
77
76
78 if c.came_from:
77 if c.came_from:
79 return redirect(c.came_from)
78 return redirect(c.came_from)
80 else:
79 else:
81 return redirect(url('home'))
80 return redirect(url('home'))
82
81
83 except formencode.Invalid, errors:
82 except formencode.Invalid, errors:
84 return htmlfill.render(
83 return htmlfill.render(
85 render('/login.html'),
84 render('/login.html'),
86 defaults=errors.value,
85 defaults=errors.value,
87 errors=errors.error_dict or {},
86 errors=errors.error_dict or {},
88 prefix_error=False,
87 prefix_error=False,
89 encoding="UTF-8")
88 encoding="UTF-8")
90
89
91 return render('/login.html')
90 return render('/login.html')
92
91
93 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
92 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
94 'hg.register.manual_activate')
93 'hg.register.manual_activate')
95 def register(self):
94 def register(self):
96 user_model = UserModel()
95 user_model = UserModel()
97 c.auto_active = False
96 c.auto_active = False
98 for perm in user_model.get_by_username('default',
97 for perm in User.get_by_username('default').user_perms:
99 cache=False).user_perms:
100 if perm.permission.permission_name == 'hg.register.auto_activate':
98 if perm.permission.permission_name == 'hg.register.auto_activate':
101 c.auto_active = True
99 c.auto_active = True
102 break
100 break
103
101
104 if request.POST:
102 if request.POST:
105
103
106 register_form = RegisterForm()()
104 register_form = RegisterForm()()
107 try:
105 try:
108 form_result = register_form.to_python(dict(request.POST))
106 form_result = register_form.to_python(dict(request.POST))
109 form_result['active'] = c.auto_active
107 form_result['active'] = c.auto_active
110 user_model.create_registration(form_result)
108 user_model.create_registration(form_result)
111 h.flash(_('You have successfully registered into rhodecode'),
109 h.flash(_('You have successfully registered into rhodecode'),
112 category='success')
110 category='success')
113 return redirect(url('login_home'))
111 return redirect(url('login_home'))
114
112
115 except formencode.Invalid, errors:
113 except formencode.Invalid, errors:
116 return htmlfill.render(
114 return htmlfill.render(
117 render('/register.html'),
115 render('/register.html'),
118 defaults=errors.value,
116 defaults=errors.value,
119 errors=errors.error_dict or {},
117 errors=errors.error_dict or {},
120 prefix_error=False,
118 prefix_error=False,
121 encoding="UTF-8")
119 encoding="UTF-8")
122
120
123 return render('/register.html')
121 return render('/register.html')
124
122
125 def password_reset(self):
123 def password_reset(self):
126 user_model = UserModel()
124 user_model = UserModel()
127 if request.POST:
125 if request.POST:
128
126
129 password_reset_form = PasswordResetForm()()
127 password_reset_form = PasswordResetForm()()
130 try:
128 try:
131 form_result = password_reset_form.to_python(dict(request.POST))
129 form_result = password_reset_form.to_python(dict(request.POST))
132 user_model.reset_password_link(form_result)
130 user_model.reset_password_link(form_result)
133 h.flash(_('Your password reset link was sent'),
131 h.flash(_('Your password reset link was sent'),
134 category='success')
132 category='success')
135 return redirect(url('login_home'))
133 return redirect(url('login_home'))
136
134
137 except formencode.Invalid, errors:
135 except formencode.Invalid, errors:
138 return htmlfill.render(
136 return htmlfill.render(
139 render('/password_reset.html'),
137 render('/password_reset.html'),
140 defaults=errors.value,
138 defaults=errors.value,
141 errors=errors.error_dict or {},
139 errors=errors.error_dict or {},
142 prefix_error=False,
140 prefix_error=False,
143 encoding="UTF-8")
141 encoding="UTF-8")
144
142
145 return render('/password_reset.html')
143 return render('/password_reset.html')
146
144
147 def password_reset_confirmation(self):
145 def password_reset_confirmation(self):
148
146
149 if request.GET and request.GET.get('key'):
147 if request.GET and request.GET.get('key'):
150 try:
148 try:
151 user_model = UserModel()
149 user_model = UserModel()
152 user = User.get_by_api_key(request.GET.get('key'))
150 user = User.get_by_api_key(request.GET.get('key'))
153 data = dict(email=user.email)
151 data = dict(email=user.email)
154 user_model.reset_password(data)
152 user_model.reset_password(data)
155 h.flash(_('Your password reset was successful, '
153 h.flash(_('Your password reset was successful, '
156 'new password has been sent to your email'),
154 'new password has been sent to your email'),
157 category='success')
155 category='success')
158 except Exception, e:
156 except Exception, e:
159 log.error(e)
157 log.error(e)
160 return redirect(url('reset_password'))
158 return redirect(url('reset_password'))
161
159
162 return redirect(url('login_home'))
160 return redirect(url('login_home'))
163
161
164 def logout(self):
162 def logout(self):
165 del session['rhodecode_user']
163 del session['rhodecode_user']
166 session.save()
164 session.save()
167 log.info('Logging out and setting user as Empty')
165 log.info('Logging out and setting user as Empty')
168 redirect(url('home'))
166 redirect(url('home'))
@@ -1,613 +1,612 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.auth
3 rhodecode.lib.auth
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 authentication and permission libraries
6 authentication and permission libraries
7
7
8 :created_on: Apr 4, 2010
8 :created_on: Apr 4, 2010
9 :copyright: (c) 2010 by marcink.
9 :copyright: (c) 2010 by marcink.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
11 """
11 """
12 # This program is free software: you can redistribute it and/or modify
12 # This program is free software: you can redistribute it and/or modify
13 # it under the terms of the GNU General Public License as published by
13 # it under the terms of the GNU General Public License as published by
14 # the Free Software Foundation, either version 3 of the License, or
14 # the Free Software Foundation, either version 3 of the License, or
15 # (at your option) any later version.
15 # (at your option) any later version.
16 #
16 #
17 # This program is distributed in the hope that it will be useful,
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
20 # GNU General Public License for more details.
21 #
21 #
22 # You should have received a copy of the GNU General Public License
22 # You should have received a copy of the GNU General Public License
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24
24
25 import random
25 import random
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import hashlib
28 import hashlib
29
29
30 from tempfile import _RandomNameSequence
30 from tempfile import _RandomNameSequence
31 from decorator import decorator
31 from decorator import decorator
32
32
33 from pylons import config, session, url, request
33 from pylons import config, session, url, request
34 from pylons.controllers.util import abort, redirect
34 from pylons.controllers.util import abort, redirect
35 from pylons.i18n.translation import _
35 from pylons.i18n.translation import _
36
36
37 from rhodecode import __platform__, PLATFORM_WIN, PLATFORM_OTHERS
37 from rhodecode import __platform__, PLATFORM_WIN, PLATFORM_OTHERS
38
38
39 if __platform__ in PLATFORM_WIN:
39 if __platform__ in PLATFORM_WIN:
40 from hashlib import sha256
40 from hashlib import sha256
41 if __platform__ in PLATFORM_OTHERS:
41 if __platform__ in PLATFORM_OTHERS:
42 import bcrypt
42 import bcrypt
43
43
44 from rhodecode.lib import str2bool, safe_unicode
44 from rhodecode.lib import str2bool, safe_unicode
45 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
45 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
46 from rhodecode.lib.utils import get_repo_slug
46 from rhodecode.lib.utils import get_repo_slug
47 from rhodecode.lib.auth_ldap import AuthLdap
47 from rhodecode.lib.auth_ldap import AuthLdap
48
48
49 from rhodecode.model import meta
49 from rhodecode.model import meta
50 from rhodecode.model.user import UserModel
50 from rhodecode.model.user import UserModel
51 from rhodecode.model.db import Permission, RhodeCodeSettings
51 from rhodecode.model.db import Permission, RhodeCodeSettings, User
52
52
53 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
54
54
55
55
56 class PasswordGenerator(object):
56 class PasswordGenerator(object):
57 """This is a simple class for generating password from
57 """This is a simple class for generating password from
58 different sets of characters
58 different sets of characters
59 usage:
59 usage:
60 passwd_gen = PasswordGenerator()
60 passwd_gen = PasswordGenerator()
61 #print 8-letter password containing only big and small letters
61 #print 8-letter password containing only big and small letters
62 of alphabet
62 of alphabet
63 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
63 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
64 """
64 """
65 ALPHABETS_NUM = r'''1234567890'''
65 ALPHABETS_NUM = r'''1234567890'''
66 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
66 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
67 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
67 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
68 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
68 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
69 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
69 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
70 + ALPHABETS_NUM + ALPHABETS_SPECIAL
70 + ALPHABETS_NUM + ALPHABETS_SPECIAL
71 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
71 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
72 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
72 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
73 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
73 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
74 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
74 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
75
75
76 def __init__(self, passwd=''):
76 def __init__(self, passwd=''):
77 self.passwd = passwd
77 self.passwd = passwd
78
78
79 def gen_password(self, len, type):
79 def gen_password(self, len, type):
80 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
80 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
81 return self.passwd
81 return self.passwd
82
82
83
83
84 class RhodeCodeCrypto(object):
84 class RhodeCodeCrypto(object):
85
85
86 @classmethod
86 @classmethod
87 def hash_string(cls, str_):
87 def hash_string(cls, str_):
88 """
88 """
89 Cryptographic function used for password hashing based on pybcrypt
89 Cryptographic function used for password hashing based on pybcrypt
90 or pycrypto in windows
90 or pycrypto in windows
91
91
92 :param password: password to hash
92 :param password: password to hash
93 """
93 """
94 if __platform__ in PLATFORM_WIN:
94 if __platform__ in PLATFORM_WIN:
95 return sha256(str_).hexdigest()
95 return sha256(str_).hexdigest()
96 elif __platform__ in PLATFORM_OTHERS:
96 elif __platform__ in PLATFORM_OTHERS:
97 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
97 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
98 else:
98 else:
99 raise Exception('Unknown or unsupported platform %s' \
99 raise Exception('Unknown or unsupported platform %s' \
100 % __platform__)
100 % __platform__)
101
101
102 @classmethod
102 @classmethod
103 def hash_check(cls, password, hashed):
103 def hash_check(cls, password, hashed):
104 """
104 """
105 Checks matching password with it's hashed value, runs different
105 Checks matching password with it's hashed value, runs different
106 implementation based on platform it runs on
106 implementation based on platform it runs on
107
107
108 :param password: password
108 :param password: password
109 :param hashed: password in hashed form
109 :param hashed: password in hashed form
110 """
110 """
111
111
112 if __platform__ in PLATFORM_WIN:
112 if __platform__ in PLATFORM_WIN:
113 return sha256(password).hexdigest() == hashed
113 return sha256(password).hexdigest() == hashed
114 elif __platform__ in PLATFORM_OTHERS:
114 elif __platform__ in PLATFORM_OTHERS:
115 return bcrypt.hashpw(password, hashed) == hashed
115 return bcrypt.hashpw(password, hashed) == hashed
116 else:
116 else:
117 raise Exception('Unknown or unsupported platform %s' \
117 raise Exception('Unknown or unsupported platform %s' \
118 % __platform__)
118 % __platform__)
119
119
120
120
121 def get_crypt_password(password):
121 def get_crypt_password(password):
122 return RhodeCodeCrypto.hash_string(password)
122 return RhodeCodeCrypto.hash_string(password)
123
123
124
124
125 def check_password(password, hashed):
125 def check_password(password, hashed):
126 return RhodeCodeCrypto.hash_check(password, hashed)
126 return RhodeCodeCrypto.hash_check(password, hashed)
127
127
128
128
129 def generate_api_key(username, salt=None):
129 def generate_api_key(username, salt=None):
130 if salt is None:
130 if salt is None:
131 salt = _RandomNameSequence().next()
131 salt = _RandomNameSequence().next()
132
132
133 return hashlib.sha1(username + salt).hexdigest()
133 return hashlib.sha1(username + salt).hexdigest()
134
134
135
135
136 def authfunc(environ, username, password):
136 def authfunc(environ, username, password):
137 """Dummy authentication function used in Mercurial/Git/ and access control,
137 """Dummy authentication function used in Mercurial/Git/ and access control,
138
138
139 :param environ: needed only for using in Basic auth
139 :param environ: needed only for using in Basic auth
140 """
140 """
141 return authenticate(username, password)
141 return authenticate(username, password)
142
142
143
143
144 def authenticate(username, password):
144 def authenticate(username, password):
145 """Authentication function used for access control,
145 """Authentication function used for access control,
146 firstly checks for db authentication then if ldap is enabled for ldap
146 firstly checks for db authentication then if ldap is enabled for ldap
147 authentication, also creates ldap user if not in database
147 authentication, also creates ldap user if not in database
148
148
149 :param username: username
149 :param username: username
150 :param password: password
150 :param password: password
151 """
151 """
152
152
153 user_model = UserModel()
153 user_model = UserModel()
154 user = user_model.get_by_username(username, cache=False)
154 user = User.get_by_username(username)
155
155
156 log.debug('Authenticating user using RhodeCode account')
156 log.debug('Authenticating user using RhodeCode account')
157 if user is not None and not user.ldap_dn:
157 if user is not None and not user.ldap_dn:
158 if user.active:
158 if user.active:
159 if user.username == 'default' and user.active:
159 if user.username == 'default' and user.active:
160 log.info('user %s authenticated correctly as anonymous user',
160 log.info('user %s authenticated correctly as anonymous user',
161 username)
161 username)
162 return True
162 return True
163
163
164 elif user.username == username and check_password(password,
164 elif user.username == username and check_password(password,
165 user.password):
165 user.password):
166 log.info('user %s authenticated correctly', username)
166 log.info('user %s authenticated correctly', username)
167 return True
167 return True
168 else:
168 else:
169 log.warning('user %s is disabled', username)
169 log.warning('user %s is disabled', username)
170
170
171 else:
171 else:
172 log.debug('Regular authentication failed')
172 log.debug('Regular authentication failed')
173 user_obj = user_model.get_by_username(username, cache=False,
173 user_obj = User.get_by_username(username, case_insensitive=True)
174 case_insensitive=True)
175
174
176 if user_obj is not None and not user_obj.ldap_dn:
175 if user_obj is not None and not user_obj.ldap_dn:
177 log.debug('this user already exists as non ldap')
176 log.debug('this user already exists as non ldap')
178 return False
177 return False
179
178
180 ldap_settings = RhodeCodeSettings.get_ldap_settings()
179 ldap_settings = RhodeCodeSettings.get_ldap_settings()
181 #======================================================================
180 #======================================================================
182 # FALLBACK TO LDAP AUTH IF ENABLE
181 # FALLBACK TO LDAP AUTH IF ENABLE
183 #======================================================================
182 #======================================================================
184 if str2bool(ldap_settings.get('ldap_active')):
183 if str2bool(ldap_settings.get('ldap_active')):
185 log.debug("Authenticating user using ldap")
184 log.debug("Authenticating user using ldap")
186 kwargs = {
185 kwargs = {
187 'server': ldap_settings.get('ldap_host', ''),
186 'server': ldap_settings.get('ldap_host', ''),
188 'base_dn': ldap_settings.get('ldap_base_dn', ''),
187 'base_dn': ldap_settings.get('ldap_base_dn', ''),
189 'port': ldap_settings.get('ldap_port'),
188 'port': ldap_settings.get('ldap_port'),
190 'bind_dn': ldap_settings.get('ldap_dn_user'),
189 'bind_dn': ldap_settings.get('ldap_dn_user'),
191 'bind_pass': ldap_settings.get('ldap_dn_pass'),
190 'bind_pass': ldap_settings.get('ldap_dn_pass'),
192 'tls_kind': ldap_settings.get('ldap_tls_kind'),
191 'tls_kind': ldap_settings.get('ldap_tls_kind'),
193 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
192 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
194 'ldap_filter': ldap_settings.get('ldap_filter'),
193 'ldap_filter': ldap_settings.get('ldap_filter'),
195 'search_scope': ldap_settings.get('ldap_search_scope'),
194 'search_scope': ldap_settings.get('ldap_search_scope'),
196 'attr_login': ldap_settings.get('ldap_attr_login'),
195 'attr_login': ldap_settings.get('ldap_attr_login'),
197 'ldap_version': 3,
196 'ldap_version': 3,
198 }
197 }
199 log.debug('Checking for ldap authentication')
198 log.debug('Checking for ldap authentication')
200 try:
199 try:
201 aldap = AuthLdap(**kwargs)
200 aldap = AuthLdap(**kwargs)
202 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
201 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
203 password)
202 password)
204 log.debug('Got ldap DN response %s', user_dn)
203 log.debug('Got ldap DN response %s', user_dn)
205
204
206 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
205 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
207 .get(k), [''])[0]
206 .get(k), [''])[0]
208
207
209 user_attrs = {
208 user_attrs = {
210 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
209 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
211 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
210 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
212 'email': get_ldap_attr('ldap_attr_email'),
211 'email': get_ldap_attr('ldap_attr_email'),
213 }
212 }
214
213
215 if user_model.create_ldap(username, password, user_dn,
214 if user_model.create_ldap(username, password, user_dn,
216 user_attrs):
215 user_attrs):
217 log.info('created new ldap user %s', username)
216 log.info('created new ldap user %s', username)
218
217
219 return True
218 return True
220 except (LdapUsernameError, LdapPasswordError,):
219 except (LdapUsernameError, LdapPasswordError,):
221 pass
220 pass
222 except (Exception,):
221 except (Exception,):
223 log.error(traceback.format_exc())
222 log.error(traceback.format_exc())
224 pass
223 pass
225 return False
224 return False
226
225
227
226
228 class AuthUser(object):
227 class AuthUser(object):
229 """
228 """
230 A simple object that handles all attributes of user in RhodeCode
229 A simple object that handles all attributes of user in RhodeCode
231
230
232 It does lookup based on API key,given user, or user present in session
231 It does lookup based on API key,given user, or user present in session
233 Then it fills all required information for such user. It also checks if
232 Then it fills all required information for such user. It also checks if
234 anonymous access is enabled and if so, it returns default user as logged
233 anonymous access is enabled and if so, it returns default user as logged
235 in
234 in
236 """
235 """
237
236
238 def __init__(self, user_id=None, api_key=None):
237 def __init__(self, user_id=None, api_key=None):
239
238
240 self.user_id = user_id
239 self.user_id = user_id
241 self.api_key = None
240 self.api_key = None
242
241
243 self.username = 'None'
242 self.username = 'None'
244 self.name = ''
243 self.name = ''
245 self.lastname = ''
244 self.lastname = ''
246 self.email = ''
245 self.email = ''
247 self.is_authenticated = False
246 self.is_authenticated = False
248 self.admin = False
247 self.admin = False
249 self.permissions = {}
248 self.permissions = {}
250 self._api_key = api_key
249 self._api_key = api_key
251 self.propagate_data()
250 self.propagate_data()
252
251
253 def propagate_data(self):
252 def propagate_data(self):
254 user_model = UserModel()
253 user_model = UserModel()
255 self.anonymous_user = user_model.get_by_username('default', cache=True)
254 self.anonymous_user = User.get_by_username('default')
256 if self._api_key and self._api_key != self.anonymous_user.api_key:
255 if self._api_key and self._api_key != self.anonymous_user.api_key:
257 #try go get user by api key
256 #try go get user by api key
258 log.debug('Auth User lookup by API KEY %s', self._api_key)
257 log.debug('Auth User lookup by API KEY %s', self._api_key)
259 user_model.fill_data(self, api_key=self._api_key)
258 user_model.fill_data(self, api_key=self._api_key)
260 else:
259 else:
261 log.debug('Auth User lookup by USER ID %s', self.user_id)
260 log.debug('Auth User lookup by USER ID %s', self.user_id)
262 if self.user_id is not None \
261 if self.user_id is not None \
263 and self.user_id != self.anonymous_user.user_id:
262 and self.user_id != self.anonymous_user.user_id:
264 user_model.fill_data(self, user_id=self.user_id)
263 user_model.fill_data(self, user_id=self.user_id)
265 else:
264 else:
266 if self.anonymous_user.active is True:
265 if self.anonymous_user.active is True:
267 user_model.fill_data(self,
266 user_model.fill_data(self,
268 user_id=self.anonymous_user.user_id)
267 user_id=self.anonymous_user.user_id)
269 #then we set this user is logged in
268 #then we set this user is logged in
270 self.is_authenticated = True
269 self.is_authenticated = True
271 else:
270 else:
272 self.is_authenticated = False
271 self.is_authenticated = False
273
272
274 log.debug('Auth User is now %s', self)
273 log.debug('Auth User is now %s', self)
275 user_model.fill_perms(self)
274 user_model.fill_perms(self)
276
275
277 @property
276 @property
278 def is_admin(self):
277 def is_admin(self):
279 return self.admin
278 return self.admin
280
279
281 @property
280 @property
282 def full_contact(self):
281 def full_contact(self):
283 return '%s %s <%s>' % (self.name, self.lastname, self.email)
282 return '%s %s <%s>' % (self.name, self.lastname, self.email)
284
283
285 def __repr__(self):
284 def __repr__(self):
286 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
285 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
287 self.is_authenticated)
286 self.is_authenticated)
288
287
289 def set_authenticated(self, authenticated=True):
288 def set_authenticated(self, authenticated=True):
290
289
291 if self.user_id != self.anonymous_user.user_id:
290 if self.user_id != self.anonymous_user.user_id:
292 self.is_authenticated = authenticated
291 self.is_authenticated = authenticated
293
292
294
293
295 def set_available_permissions(config):
294 def set_available_permissions(config):
296 """This function will propagate pylons globals with all available defined
295 """This function will propagate pylons globals with all available defined
297 permission given in db. We don't want to check each time from db for new
296 permission given in db. We don't want to check each time from db for new
298 permissions since adding a new permission also requires application restart
297 permissions since adding a new permission also requires application restart
299 ie. to decorate new views with the newly created permission
298 ie. to decorate new views with the newly created permission
300
299
301 :param config: current pylons config instance
300 :param config: current pylons config instance
302
301
303 """
302 """
304 log.info('getting information about all available permissions')
303 log.info('getting information about all available permissions')
305 try:
304 try:
306 sa = meta.Session()
305 sa = meta.Session()
307 all_perms = sa.query(Permission).all()
306 all_perms = sa.query(Permission).all()
308 except:
307 except:
309 pass
308 pass
310 finally:
309 finally:
311 meta.Session.remove()
310 meta.Session.remove()
312
311
313 config['available_permissions'] = [x.permission_name for x in all_perms]
312 config['available_permissions'] = [x.permission_name for x in all_perms]
314
313
315
314
316 #==============================================================================
315 #==============================================================================
317 # CHECK DECORATORS
316 # CHECK DECORATORS
318 #==============================================================================
317 #==============================================================================
319 class LoginRequired(object):
318 class LoginRequired(object):
320 """
319 """
321 Must be logged in to execute this function else
320 Must be logged in to execute this function else
322 redirect to login page
321 redirect to login page
323
322
324 :param api_access: if enabled this checks only for valid auth token
323 :param api_access: if enabled this checks only for valid auth token
325 and grants access based on valid token
324 and grants access based on valid token
326 """
325 """
327
326
328 def __init__(self, api_access=False):
327 def __init__(self, api_access=False):
329 self.api_access = api_access
328 self.api_access = api_access
330
329
331 def __call__(self, func):
330 def __call__(self, func):
332 return decorator(self.__wrapper, func)
331 return decorator(self.__wrapper, func)
333
332
334 def __wrapper(self, func, *fargs, **fkwargs):
333 def __wrapper(self, func, *fargs, **fkwargs):
335 cls = fargs[0]
334 cls = fargs[0]
336 user = cls.rhodecode_user
335 user = cls.rhodecode_user
337
336
338 api_access_ok = False
337 api_access_ok = False
339 if self.api_access:
338 if self.api_access:
340 log.debug('Checking API KEY access for %s', cls)
339 log.debug('Checking API KEY access for %s', cls)
341 if user.api_key == request.GET.get('api_key'):
340 if user.api_key == request.GET.get('api_key'):
342 api_access_ok = True
341 api_access_ok = True
343 else:
342 else:
344 log.debug("API KEY token not valid")
343 log.debug("API KEY token not valid")
345
344
346 log.debug('Checking if %s is authenticated @ %s', user.username, cls)
345 log.debug('Checking if %s is authenticated @ %s', user.username, cls)
347 if user.is_authenticated or api_access_ok:
346 if user.is_authenticated or api_access_ok:
348 log.debug('user %s is authenticated', user.username)
347 log.debug('user %s is authenticated', user.username)
349 return func(*fargs, **fkwargs)
348 return func(*fargs, **fkwargs)
350 else:
349 else:
351 log.warn('user %s NOT authenticated', user.username)
350 log.warn('user %s NOT authenticated', user.username)
352 p = url.current()
351 p = url.current()
353
352
354 log.debug('redirecting to login page with %s', p)
353 log.debug('redirecting to login page with %s', p)
355 return redirect(url('login_home', came_from=p))
354 return redirect(url('login_home', came_from=p))
356
355
357
356
358 class NotAnonymous(object):
357 class NotAnonymous(object):
359 """Must be logged in to execute this function else
358 """Must be logged in to execute this function else
360 redirect to login page"""
359 redirect to login page"""
361
360
362 def __call__(self, func):
361 def __call__(self, func):
363 return decorator(self.__wrapper, func)
362 return decorator(self.__wrapper, func)
364
363
365 def __wrapper(self, func, *fargs, **fkwargs):
364 def __wrapper(self, func, *fargs, **fkwargs):
366 cls = fargs[0]
365 cls = fargs[0]
367 self.user = cls.rhodecode_user
366 self.user = cls.rhodecode_user
368
367
369 log.debug('Checking if user is not anonymous @%s', cls)
368 log.debug('Checking if user is not anonymous @%s', cls)
370
369
371 anonymous = self.user.username == 'default'
370 anonymous = self.user.username == 'default'
372
371
373 if anonymous:
372 if anonymous:
374 p = url.current()
373 p = url.current()
375
374
376 import rhodecode.lib.helpers as h
375 import rhodecode.lib.helpers as h
377 h.flash(_('You need to be a registered user to '
376 h.flash(_('You need to be a registered user to '
378 'perform this action'),
377 'perform this action'),
379 category='warning')
378 category='warning')
380 return redirect(url('login_home', came_from=p))
379 return redirect(url('login_home', came_from=p))
381 else:
380 else:
382 return func(*fargs, **fkwargs)
381 return func(*fargs, **fkwargs)
383
382
384
383
385 class PermsDecorator(object):
384 class PermsDecorator(object):
386 """Base class for controller decorators"""
385 """Base class for controller decorators"""
387
386
388 def __init__(self, *required_perms):
387 def __init__(self, *required_perms):
389 available_perms = config['available_permissions']
388 available_perms = config['available_permissions']
390 for perm in required_perms:
389 for perm in required_perms:
391 if perm not in available_perms:
390 if perm not in available_perms:
392 raise Exception("'%s' permission is not defined" % perm)
391 raise Exception("'%s' permission is not defined" % perm)
393 self.required_perms = set(required_perms)
392 self.required_perms = set(required_perms)
394 self.user_perms = None
393 self.user_perms = None
395
394
396 def __call__(self, func):
395 def __call__(self, func):
397 return decorator(self.__wrapper, func)
396 return decorator(self.__wrapper, func)
398
397
399 def __wrapper(self, func, *fargs, **fkwargs):
398 def __wrapper(self, func, *fargs, **fkwargs):
400 cls = fargs[0]
399 cls = fargs[0]
401 self.user = cls.rhodecode_user
400 self.user = cls.rhodecode_user
402 self.user_perms = self.user.permissions
401 self.user_perms = self.user.permissions
403 log.debug('checking %s permissions %s for %s %s',
402 log.debug('checking %s permissions %s for %s %s',
404 self.__class__.__name__, self.required_perms, cls,
403 self.__class__.__name__, self.required_perms, cls,
405 self.user)
404 self.user)
406
405
407 if self.check_permissions():
406 if self.check_permissions():
408 log.debug('Permission granted for %s %s', cls, self.user)
407 log.debug('Permission granted for %s %s', cls, self.user)
409 return func(*fargs, **fkwargs)
408 return func(*fargs, **fkwargs)
410
409
411 else:
410 else:
412 log.warning('Permission denied for %s %s', cls, self.user)
411 log.warning('Permission denied for %s %s', cls, self.user)
413
412
414
413
415 anonymous = self.user.username == 'default'
414 anonymous = self.user.username == 'default'
416
415
417 if anonymous:
416 if anonymous:
418 p = url.current()
417 p = url.current()
419
418
420 import rhodecode.lib.helpers as h
419 import rhodecode.lib.helpers as h
421 h.flash(_('You need to be a signed in to '
420 h.flash(_('You need to be a signed in to '
422 'view this page'),
421 'view this page'),
423 category='warning')
422 category='warning')
424 return redirect(url('login_home', came_from=p))
423 return redirect(url('login_home', came_from=p))
425
424
426 else:
425 else:
427 #redirect with forbidden ret code
426 #redirect with forbidden ret code
428 return abort(403)
427 return abort(403)
429
428
430 def check_permissions(self):
429 def check_permissions(self):
431 """Dummy function for overriding"""
430 """Dummy function for overriding"""
432 raise Exception('You have to write this function in child class')
431 raise Exception('You have to write this function in child class')
433
432
434
433
435 class HasPermissionAllDecorator(PermsDecorator):
434 class HasPermissionAllDecorator(PermsDecorator):
436 """Checks for access permission for all given predicates. All of them
435 """Checks for access permission for all given predicates. All of them
437 have to be meet in order to fulfill the request
436 have to be meet in order to fulfill the request
438 """
437 """
439
438
440 def check_permissions(self):
439 def check_permissions(self):
441 if self.required_perms.issubset(self.user_perms.get('global')):
440 if self.required_perms.issubset(self.user_perms.get('global')):
442 return True
441 return True
443 return False
442 return False
444
443
445
444
446 class HasPermissionAnyDecorator(PermsDecorator):
445 class HasPermissionAnyDecorator(PermsDecorator):
447 """Checks for access permission for any of given predicates. In order to
446 """Checks for access permission for any of given predicates. In order to
448 fulfill the request any of predicates must be meet
447 fulfill the request any of predicates must be meet
449 """
448 """
450
449
451 def check_permissions(self):
450 def check_permissions(self):
452 if self.required_perms.intersection(self.user_perms.get('global')):
451 if self.required_perms.intersection(self.user_perms.get('global')):
453 return True
452 return True
454 return False
453 return False
455
454
456
455
457 class HasRepoPermissionAllDecorator(PermsDecorator):
456 class HasRepoPermissionAllDecorator(PermsDecorator):
458 """Checks for access permission for all given predicates for specific
457 """Checks for access permission for all given predicates for specific
459 repository. All of them have to be meet in order to fulfill the request
458 repository. All of them have to be meet in order to fulfill the request
460 """
459 """
461
460
462 def check_permissions(self):
461 def check_permissions(self):
463 repo_name = get_repo_slug(request)
462 repo_name = get_repo_slug(request)
464 try:
463 try:
465 user_perms = set([self.user_perms['repositories'][repo_name]])
464 user_perms = set([self.user_perms['repositories'][repo_name]])
466 except KeyError:
465 except KeyError:
467 return False
466 return False
468 if self.required_perms.issubset(user_perms):
467 if self.required_perms.issubset(user_perms):
469 return True
468 return True
470 return False
469 return False
471
470
472
471
473 class HasRepoPermissionAnyDecorator(PermsDecorator):
472 class HasRepoPermissionAnyDecorator(PermsDecorator):
474 """Checks for access permission for any of given predicates for specific
473 """Checks for access permission for any of given predicates for specific
475 repository. In order to fulfill the request any of predicates must be meet
474 repository. In order to fulfill the request any of predicates must be meet
476 """
475 """
477
476
478 def check_permissions(self):
477 def check_permissions(self):
479 repo_name = get_repo_slug(request)
478 repo_name = get_repo_slug(request)
480
479
481 try:
480 try:
482 user_perms = set([self.user_perms['repositories'][repo_name]])
481 user_perms = set([self.user_perms['repositories'][repo_name]])
483 except KeyError:
482 except KeyError:
484 return False
483 return False
485 if self.required_perms.intersection(user_perms):
484 if self.required_perms.intersection(user_perms):
486 return True
485 return True
487 return False
486 return False
488
487
489
488
490 #==============================================================================
489 #==============================================================================
491 # CHECK FUNCTIONS
490 # CHECK FUNCTIONS
492 #==============================================================================
491 #==============================================================================
493 class PermsFunction(object):
492 class PermsFunction(object):
494 """Base function for other check functions"""
493 """Base function for other check functions"""
495
494
496 def __init__(self, *perms):
495 def __init__(self, *perms):
497 available_perms = config['available_permissions']
496 available_perms = config['available_permissions']
498
497
499 for perm in perms:
498 for perm in perms:
500 if perm not in available_perms:
499 if perm not in available_perms:
501 raise Exception("'%s' permission in not defined" % perm)
500 raise Exception("'%s' permission in not defined" % perm)
502 self.required_perms = set(perms)
501 self.required_perms = set(perms)
503 self.user_perms = None
502 self.user_perms = None
504 self.granted_for = ''
503 self.granted_for = ''
505 self.repo_name = None
504 self.repo_name = None
506
505
507 def __call__(self, check_Location=''):
506 def __call__(self, check_Location=''):
508 user = session.get('rhodecode_user', False)
507 user = session.get('rhodecode_user', False)
509 if not user:
508 if not user:
510 return False
509 return False
511 self.user_perms = user.permissions
510 self.user_perms = user.permissions
512 self.granted_for = user
511 self.granted_for = user
513 log.debug('checking %s %s %s', self.__class__.__name__,
512 log.debug('checking %s %s %s', self.__class__.__name__,
514 self.required_perms, user)
513 self.required_perms, user)
515
514
516 if self.check_permissions():
515 if self.check_permissions():
517 log.debug('Permission granted %s @ %s', self.granted_for,
516 log.debug('Permission granted %s @ %s', self.granted_for,
518 check_Location or 'unspecified location')
517 check_Location or 'unspecified location')
519 return True
518 return True
520
519
521 else:
520 else:
522 log.warning('Permission denied for %s @ %s', self.granted_for,
521 log.warning('Permission denied for %s @ %s', self.granted_for,
523 check_Location or 'unspecified location')
522 check_Location or 'unspecified location')
524 return False
523 return False
525
524
526 def check_permissions(self):
525 def check_permissions(self):
527 """Dummy function for overriding"""
526 """Dummy function for overriding"""
528 raise Exception('You have to write this function in child class')
527 raise Exception('You have to write this function in child class')
529
528
530
529
531 class HasPermissionAll(PermsFunction):
530 class HasPermissionAll(PermsFunction):
532 def check_permissions(self):
531 def check_permissions(self):
533 if self.required_perms.issubset(self.user_perms.get('global')):
532 if self.required_perms.issubset(self.user_perms.get('global')):
534 return True
533 return True
535 return False
534 return False
536
535
537
536
538 class HasPermissionAny(PermsFunction):
537 class HasPermissionAny(PermsFunction):
539 def check_permissions(self):
538 def check_permissions(self):
540 if self.required_perms.intersection(self.user_perms.get('global')):
539 if self.required_perms.intersection(self.user_perms.get('global')):
541 return True
540 return True
542 return False
541 return False
543
542
544
543
545 class HasRepoPermissionAll(PermsFunction):
544 class HasRepoPermissionAll(PermsFunction):
546
545
547 def __call__(self, repo_name=None, check_Location=''):
546 def __call__(self, repo_name=None, check_Location=''):
548 self.repo_name = repo_name
547 self.repo_name = repo_name
549 return super(HasRepoPermissionAll, self).__call__(check_Location)
548 return super(HasRepoPermissionAll, self).__call__(check_Location)
550
549
551 def check_permissions(self):
550 def check_permissions(self):
552 if not self.repo_name:
551 if not self.repo_name:
553 self.repo_name = get_repo_slug(request)
552 self.repo_name = get_repo_slug(request)
554
553
555 try:
554 try:
556 self.user_perms = set([self.user_perms['reposit'
555 self.user_perms = set([self.user_perms['reposit'
557 'ories'][self.repo_name]])
556 'ories'][self.repo_name]])
558 except KeyError:
557 except KeyError:
559 return False
558 return False
560 self.granted_for = self.repo_name
559 self.granted_for = self.repo_name
561 if self.required_perms.issubset(self.user_perms):
560 if self.required_perms.issubset(self.user_perms):
562 return True
561 return True
563 return False
562 return False
564
563
565
564
566 class HasRepoPermissionAny(PermsFunction):
565 class HasRepoPermissionAny(PermsFunction):
567
566
568 def __call__(self, repo_name=None, check_Location=''):
567 def __call__(self, repo_name=None, check_Location=''):
569 self.repo_name = repo_name
568 self.repo_name = repo_name
570 return super(HasRepoPermissionAny, self).__call__(check_Location)
569 return super(HasRepoPermissionAny, self).__call__(check_Location)
571
570
572 def check_permissions(self):
571 def check_permissions(self):
573 if not self.repo_name:
572 if not self.repo_name:
574 self.repo_name = get_repo_slug(request)
573 self.repo_name = get_repo_slug(request)
575
574
576 try:
575 try:
577 self.user_perms = set([self.user_perms['reposi'
576 self.user_perms = set([self.user_perms['reposi'
578 'tories'][self.repo_name]])
577 'tories'][self.repo_name]])
579 except KeyError:
578 except KeyError:
580 return False
579 return False
581 self.granted_for = self.repo_name
580 self.granted_for = self.repo_name
582 if self.required_perms.intersection(self.user_perms):
581 if self.required_perms.intersection(self.user_perms):
583 return True
582 return True
584 return False
583 return False
585
584
586
585
587 #==============================================================================
586 #==============================================================================
588 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
587 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
589 #==============================================================================
588 #==============================================================================
590 class HasPermissionAnyMiddleware(object):
589 class HasPermissionAnyMiddleware(object):
591 def __init__(self, *perms):
590 def __init__(self, *perms):
592 self.required_perms = set(perms)
591 self.required_perms = set(perms)
593
592
594 def __call__(self, user, repo_name):
593 def __call__(self, user, repo_name):
595 usr = AuthUser(user.user_id)
594 usr = AuthUser(user.user_id)
596 try:
595 try:
597 self.user_perms = set([usr.permissions['repositories'][repo_name]])
596 self.user_perms = set([usr.permissions['repositories'][repo_name]])
598 except:
597 except:
599 self.user_perms = set()
598 self.user_perms = set()
600 self.granted_for = ''
599 self.granted_for = ''
601 self.username = user.username
600 self.username = user.username
602 self.repo_name = repo_name
601 self.repo_name = repo_name
603 return self.check_permissions()
602 return self.check_permissions()
604
603
605 def check_permissions(self):
604 def check_permissions(self):
606 log.debug('checking mercurial protocol '
605 log.debug('checking mercurial protocol '
607 'permissions %s for user:%s repository:%s', self.user_perms,
606 'permissions %s for user:%s repository:%s', self.user_perms,
608 self.username, self.repo_name)
607 self.username, self.repo_name)
609 if self.required_perms.intersection(self.user_perms):
608 if self.required_perms.intersection(self.user_perms):
610 log.debug('permission granted')
609 log.debug('permission granted')
611 return True
610 return True
612 log.debug('permission denied')
611 log.debug('permission denied')
613 return False
612 return False
@@ -1,82 +1,82 b''
1 """The base Controller API
1 """The base Controller API
2
2
3 Provides the BaseController class for subclassing.
3 Provides the BaseController class for subclassing.
4 """
4 """
5 import logging
5 import logging
6
6
7 from pylons import config, tmpl_context as c, request, session, url
7 from pylons import config, tmpl_context as c, request, session, url
8 from pylons.controllers import WSGIController
8 from pylons.controllers import WSGIController
9 from pylons.controllers.util import redirect
9 from pylons.controllers.util import redirect
10 from pylons.templating import render_mako as render
10 from pylons.templating import render_mako as render
11
11
12 from rhodecode import __version__
12 from rhodecode import __version__
13 from rhodecode.lib.auth import AuthUser
13 from rhodecode.lib.auth import AuthUser
14 from rhodecode.lib.utils import get_repo_slug
14 from rhodecode.lib.utils import get_repo_slug
15 from rhodecode.model import meta
15 from rhodecode.model import meta
16 from rhodecode.model.scm import ScmModel
16 from rhodecode.model.scm import ScmModel
17 from rhodecode import BACKENDS
17 from rhodecode import BACKENDS
18 from rhodecode.model.db import Repository
18 from rhodecode.model.db import Repository
19
19
20 log = logging.getLogger(__name__)
20 log = logging.getLogger(__name__)
21
21
22 class BaseController(WSGIController):
22 class BaseController(WSGIController):
23
23
24 def __before__(self):
24 def __before__(self):
25 c.rhodecode_version = __version__
25 c.rhodecode_version = __version__
26 c.rhodecode_name = config.get('rhodecode_title')
26 c.rhodecode_name = config.get('rhodecode_title')
27 c.ga_code = config.get('rhodecode_ga_code')
27 c.ga_code = config.get('rhodecode_ga_code')
28 c.repo_name = get_repo_slug(request)
28 c.repo_name = get_repo_slug(request)
29 c.backends = BACKENDS.keys()
29 c.backends = BACKENDS.keys()
30 self.cut_off_limit = int(config.get('cut_off_limit'))
30 self.cut_off_limit = int(config.get('cut_off_limit'))
31
31
32 self.sa = meta.Session()
32 self.sa = meta.Session()
33 self.scm_model = ScmModel(self.sa)
33 self.scm_model = ScmModel(self.sa)
34
34
35 #c.unread_journal = scm_model.get_unread_journal()
35 #c.unread_journal = scm_model.get_unread_journal()
36
36
37 def __call__(self, environ, start_response):
37 def __call__(self, environ, start_response):
38 """Invoke the Controller"""
38 """Invoke the Controller"""
39 # WSGIController.__call__ dispatches to the Controller method
39 # WSGIController.__call__ dispatches to the Controller method
40 # the request is routed to. This routing information is
40 # the request is routed to. This routing information is
41 # available in environ['pylons.routes_dict']
41 # available in environ['pylons.routes_dict']
42 try:
42 try:
43 # putting this here makes sure that we update permissions each time
43 # putting this here makes sure that we update permissions each time
44 api_key = request.GET.get('api_key')
44 api_key = request.GET.get('api_key')
45 user_id = getattr(session.get('rhodecode_user'), 'user_id', None)
45 user_id = getattr(session.get('rhodecode_user'), 'user_id', None)
46 self.rhodecode_user = c.rhodecode_user = AuthUser(user_id, api_key)
46 self.rhodecode_user = c.rhodecode_user = AuthUser(user_id, api_key)
47 self.rhodecode_user.set_authenticated(
47 self.rhodecode_user.set_authenticated(
48 getattr(session.get('rhodecode_user'),
48 getattr(session.get('rhodecode_user'),
49 'is_authenticated', False))
49 'is_authenticated', False))
50 session['rhodecode_user'] = self.rhodecode_user
50 session['rhodecode_user'] = self.rhodecode_user
51 session.save()
51 session.save()
52 return WSGIController.__call__(self, environ, start_response)
52 return WSGIController.__call__(self, environ, start_response)
53 finally:
53 finally:
54 meta.Session.remove()
54 meta.Session.remove()
55
55
56
56
57 class BaseRepoController(BaseController):
57 class BaseRepoController(BaseController):
58 """
58 """
59 Base class for controllers responsible for loading all needed data
59 Base class for controllers responsible for loading all needed data
60 for those controllers, loaded items are
60 for those controllers, loaded items are
61
61
62 c.rhodecode_repo: instance of scm repository (taken from cache)
62 c.rhodecode_repo: instance of scm repository (taken from cache)
63
63
64 """
64 """
65
65
66 def __before__(self):
66 def __before__(self):
67 super(BaseRepoController, self).__before__()
67 super(BaseRepoController, self).__before__()
68 if c.repo_name:
68 if c.repo_name:
69
69
70 c.rhodecode_db_repo = Repository.by_repo_name(c.repo_name)
70 c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
71 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
71 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
72
72
73 if c.rhodecode_repo is None:
73 if c.rhodecode_repo is None:
74 log.error('%s this repository is present in database but it '
74 log.error('%s this repository is present in database but it '
75 'cannot be created as an scm instance', c.repo_name)
75 'cannot be created as an scm instance', c.repo_name)
76
76
77 redirect(url('home'))
77 redirect(url('home'))
78
78
79 c.repository_followers = \
79 c.repository_followers = \
80 self.scm_model.get_followers(c.repo_name)
80 self.scm_model.get_followers(c.repo_name)
81 c.repository_forks = self.scm_model.get_forks(c.repo_name)
81 c.repository_forks = self.scm_model.get_forks(c.repo_name)
82
82
@@ -1,289 +1,289 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.middleware.simplegit
3 rhodecode.lib.middleware.simplegit
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 SimpleGit middleware for handling git protocol request (push/clone etc.)
6 SimpleGit middleware for handling git protocol request (push/clone etc.)
7 It's implemented with basic auth function
7 It's implemented with basic auth function
8
8
9 :created_on: Apr 28, 2010
9 :created_on: Apr 28, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26
26
27 import os
27 import os
28 import logging
28 import logging
29 import traceback
29 import traceback
30
30
31 from dulwich import server as dulserver
31 from dulwich import server as dulserver
32
32
33
33
34 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
34 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
35
35
36 def handle(self):
36 def handle(self):
37 write = lambda x: self.proto.write_sideband(1, x)
37 write = lambda x: self.proto.write_sideband(1, x)
38
38
39 graph_walker = dulserver.ProtocolGraphWalker(self,
39 graph_walker = dulserver.ProtocolGraphWalker(self,
40 self.repo.object_store,
40 self.repo.object_store,
41 self.repo.get_peeled)
41 self.repo.get_peeled)
42 objects_iter = self.repo.fetch_objects(
42 objects_iter = self.repo.fetch_objects(
43 graph_walker.determine_wants, graph_walker, self.progress,
43 graph_walker.determine_wants, graph_walker, self.progress,
44 get_tagged=self.get_tagged)
44 get_tagged=self.get_tagged)
45
45
46 # Do they want any objects?
46 # Do they want any objects?
47 if objects_iter is None or len(objects_iter) == 0:
47 if objects_iter is None or len(objects_iter) == 0:
48 return
48 return
49
49
50 self.progress("counting objects: %d, done.\n" % len(objects_iter))
50 self.progress("counting objects: %d, done.\n" % len(objects_iter))
51 dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
51 dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
52 objects_iter, len(objects_iter))
52 objects_iter, len(objects_iter))
53 messages = []
53 messages = []
54 messages.append('thank you for using rhodecode')
54 messages.append('thank you for using rhodecode')
55
55
56 for msg in messages:
56 for msg in messages:
57 self.progress(msg + "\n")
57 self.progress(msg + "\n")
58 # we are done
58 # we are done
59 self.proto.write("0000")
59 self.proto.write("0000")
60
60
61 dulserver.DEFAULT_HANDLERS = {
61 dulserver.DEFAULT_HANDLERS = {
62 'git-upload-pack': SimpleGitUploadPackHandler,
62 'git-upload-pack': SimpleGitUploadPackHandler,
63 'git-receive-pack': dulserver.ReceivePackHandler,
63 'git-receive-pack': dulserver.ReceivePackHandler,
64 }
64 }
65
65
66 from dulwich.repo import Repo
66 from dulwich.repo import Repo
67 from dulwich.web import HTTPGitApplication
67 from dulwich.web import HTTPGitApplication
68
68
69 from paste.auth.basic import AuthBasicAuthenticator
69 from paste.auth.basic import AuthBasicAuthenticator
70 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
70 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
71
71
72 from rhodecode.lib import safe_str
72 from rhodecode.lib import safe_str
73 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
73 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
74 from rhodecode.lib.utils import invalidate_cache, is_valid_repo
74 from rhodecode.lib.utils import invalidate_cache, is_valid_repo
75 from rhodecode.model.db import User
75 from rhodecode.model.db import User
76
76
77 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
77 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
78
78
79 log = logging.getLogger(__name__)
79 log = logging.getLogger(__name__)
80
80
81
81
82 def is_git(environ):
82 def is_git(environ):
83 """Returns True if request's target is git server.
83 """Returns True if request's target is git server.
84 ``HTTP_USER_AGENT`` would then have git client version given.
84 ``HTTP_USER_AGENT`` would then have git client version given.
85
85
86 :param environ:
86 :param environ:
87 """
87 """
88 http_user_agent = environ.get('HTTP_USER_AGENT')
88 http_user_agent = environ.get('HTTP_USER_AGENT')
89 if http_user_agent and http_user_agent.startswith('git'):
89 if http_user_agent and http_user_agent.startswith('git'):
90 return True
90 return True
91 return False
91 return False
92
92
93
93
94 class SimpleGit(object):
94 class SimpleGit(object):
95
95
96 def __init__(self, application, config):
96 def __init__(self, application, config):
97 self.application = application
97 self.application = application
98 self.config = config
98 self.config = config
99 # base path of repo locations
99 # base path of repo locations
100 self.basepath = self.config['base_path']
100 self.basepath = self.config['base_path']
101 #authenticate this mercurial request using authfunc
101 #authenticate this mercurial request using authfunc
102 self.authenticate = AuthBasicAuthenticator('', authfunc)
102 self.authenticate = AuthBasicAuthenticator('', authfunc)
103
103
104 def __call__(self, environ, start_response):
104 def __call__(self, environ, start_response):
105 if not is_git(environ):
105 if not is_git(environ):
106 return self.application(environ, start_response)
106 return self.application(environ, start_response)
107
107
108 proxy_key = 'HTTP_X_REAL_IP'
108 proxy_key = 'HTTP_X_REAL_IP'
109 def_key = 'REMOTE_ADDR'
109 def_key = 'REMOTE_ADDR'
110 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
110 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
111 username = None
111 username = None
112 # skip passing error to error controller
112 # skip passing error to error controller
113 environ['pylons.status_code_redirect'] = True
113 environ['pylons.status_code_redirect'] = True
114
114
115 #======================================================================
115 #======================================================================
116 # EXTRACT REPOSITORY NAME FROM ENV
116 # EXTRACT REPOSITORY NAME FROM ENV
117 #======================================================================
117 #======================================================================
118 try:
118 try:
119 repo_name = self.__get_repository(environ)
119 repo_name = self.__get_repository(environ)
120 log.debug('Extracted repo name is %s' % repo_name)
120 log.debug('Extracted repo name is %s' % repo_name)
121 except:
121 except:
122 return HTTPInternalServerError()(environ, start_response)
122 return HTTPInternalServerError()(environ, start_response)
123
123
124 #======================================================================
124 #======================================================================
125 # GET ACTION PULL or PUSH
125 # GET ACTION PULL or PUSH
126 #======================================================================
126 #======================================================================
127 action = self.__get_action(environ)
127 action = self.__get_action(environ)
128
128
129 #======================================================================
129 #======================================================================
130 # CHECK ANONYMOUS PERMISSION
130 # CHECK ANONYMOUS PERMISSION
131 #======================================================================
131 #======================================================================
132 if action in ['pull', 'push']:
132 if action in ['pull', 'push']:
133 anonymous_user = self.__get_user('default')
133 anonymous_user = self.__get_user('default')
134 username = anonymous_user.username
134 username = anonymous_user.username
135 anonymous_perm = self.__check_permission(action,
135 anonymous_perm = self.__check_permission(action,
136 anonymous_user,
136 anonymous_user,
137 repo_name)
137 repo_name)
138
138
139 if anonymous_perm is not True or anonymous_user.active is False:
139 if anonymous_perm is not True or anonymous_user.active is False:
140 if anonymous_perm is not True:
140 if anonymous_perm is not True:
141 log.debug('Not enough credentials to access this '
141 log.debug('Not enough credentials to access this '
142 'repository as anonymous user')
142 'repository as anonymous user')
143 if anonymous_user.active is False:
143 if anonymous_user.active is False:
144 log.debug('Anonymous access is disabled, running '
144 log.debug('Anonymous access is disabled, running '
145 'authentication')
145 'authentication')
146 #==============================================================
146 #==============================================================
147 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
147 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
148 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
148 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
149 #==============================================================
149 #==============================================================
150
150
151 if not REMOTE_USER(environ):
151 if not REMOTE_USER(environ):
152 self.authenticate.realm = \
152 self.authenticate.realm = \
153 safe_str(self.config['rhodecode_realm'])
153 safe_str(self.config['rhodecode_realm'])
154 result = self.authenticate(environ)
154 result = self.authenticate(environ)
155 if isinstance(result, str):
155 if isinstance(result, str):
156 AUTH_TYPE.update(environ, 'basic')
156 AUTH_TYPE.update(environ, 'basic')
157 REMOTE_USER.update(environ, result)
157 REMOTE_USER.update(environ, result)
158 else:
158 else:
159 return result.wsgi_application(environ, start_response)
159 return result.wsgi_application(environ, start_response)
160
160
161 #==============================================================
161 #==============================================================
162 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM
162 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM
163 # BASIC AUTH
163 # BASIC AUTH
164 #==============================================================
164 #==============================================================
165
165
166 if action in ['pull', 'push']:
166 if action in ['pull', 'push']:
167 username = REMOTE_USER(environ)
167 username = REMOTE_USER(environ)
168 try:
168 try:
169 user = self.__get_user(username)
169 user = self.__get_user(username)
170 username = user.username
170 username = user.username
171 except:
171 except:
172 log.error(traceback.format_exc())
172 log.error(traceback.format_exc())
173 return HTTPInternalServerError()(environ,
173 return HTTPInternalServerError()(environ,
174 start_response)
174 start_response)
175
175
176 #check permissions for this repository
176 #check permissions for this repository
177 perm = self.__check_permission(action, user,
177 perm = self.__check_permission(action, user,
178 repo_name)
178 repo_name)
179 if perm is not True:
179 if perm is not True:
180 return HTTPForbidden()(environ, start_response)
180 return HTTPForbidden()(environ, start_response)
181
181
182 extras = {'ip': ipaddr,
182 extras = {'ip': ipaddr,
183 'username': username,
183 'username': username,
184 'action': action,
184 'action': action,
185 'repository': repo_name}
185 'repository': repo_name}
186
186
187 #===================================================================
187 #===================================================================
188 # GIT REQUEST HANDLING
188 # GIT REQUEST HANDLING
189 #===================================================================
189 #===================================================================
190
190
191 repo_path = safe_str(os.path.join(self.basepath, repo_name))
191 repo_path = safe_str(os.path.join(self.basepath, repo_name))
192 log.debug('Repository path is %s' % repo_path)
192 log.debug('Repository path is %s' % repo_path)
193
193
194 # quick check if that dir exists...
194 # quick check if that dir exists...
195 if is_valid_repo(repo_name, self.basepath) is False:
195 if is_valid_repo(repo_name, self.basepath) is False:
196 return HTTPNotFound()(environ, start_response)
196 return HTTPNotFound()(environ, start_response)
197
197
198 try:
198 try:
199 #invalidate cache on push
199 #invalidate cache on push
200 if action == 'push':
200 if action == 'push':
201 self.__invalidate_cache(repo_name)
201 self.__invalidate_cache(repo_name)
202
202
203 app = self.__make_app(repo_name, repo_path)
203 app = self.__make_app(repo_name, repo_path)
204 return app(environ, start_response)
204 return app(environ, start_response)
205 except Exception:
205 except Exception:
206 log.error(traceback.format_exc())
206 log.error(traceback.format_exc())
207 return HTTPInternalServerError()(environ, start_response)
207 return HTTPInternalServerError()(environ, start_response)
208
208
209 def __make_app(self, repo_name, repo_path):
209 def __make_app(self, repo_name, repo_path):
210 """
210 """
211 Make an wsgi application using dulserver
211 Make an wsgi application using dulserver
212
212
213 :param repo_name: name of the repository
213 :param repo_name: name of the repository
214 :param repo_path: full path to the repository
214 :param repo_path: full path to the repository
215 """
215 """
216
216
217 _d = {'/' + repo_name: Repo(repo_path)}
217 _d = {'/' + repo_name: Repo(repo_path)}
218 backend = dulserver.DictBackend(_d)
218 backend = dulserver.DictBackend(_d)
219 gitserve = HTTPGitApplication(backend)
219 gitserve = HTTPGitApplication(backend)
220
220
221 return gitserve
221 return gitserve
222
222
223 def __check_permission(self, action, user, repo_name):
223 def __check_permission(self, action, user, repo_name):
224 """
224 """
225 Checks permissions using action (push/pull) user and repository
225 Checks permissions using action (push/pull) user and repository
226 name
226 name
227
227
228 :param action: push or pull action
228 :param action: push or pull action
229 :param user: user instance
229 :param user: user instance
230 :param repo_name: repository name
230 :param repo_name: repository name
231 """
231 """
232 if action == 'push':
232 if action == 'push':
233 if not HasPermissionAnyMiddleware('repository.write',
233 if not HasPermissionAnyMiddleware('repository.write',
234 'repository.admin')(user,
234 'repository.admin')(user,
235 repo_name):
235 repo_name):
236 return False
236 return False
237
237
238 else:
238 else:
239 #any other action need at least read permission
239 #any other action need at least read permission
240 if not HasPermissionAnyMiddleware('repository.read',
240 if not HasPermissionAnyMiddleware('repository.read',
241 'repository.write',
241 'repository.write',
242 'repository.admin')(user,
242 'repository.admin')(user,
243 repo_name):
243 repo_name):
244 return False
244 return False
245
245
246 return True
246 return True
247
247
248 def __get_repository(self, environ):
248 def __get_repository(self, environ):
249 """
249 """
250 Get's repository name out of PATH_INFO header
250 Get's repository name out of PATH_INFO header
251
251
252 :param environ: environ where PATH_INFO is stored
252 :param environ: environ where PATH_INFO is stored
253 """
253 """
254 try:
254 try:
255 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
255 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
256 if repo_name.endswith('/'):
256 if repo_name.endswith('/'):
257 repo_name = repo_name.rstrip('/')
257 repo_name = repo_name.rstrip('/')
258 except:
258 except:
259 log.error(traceback.format_exc())
259 log.error(traceback.format_exc())
260 raise
260 raise
261 repo_name = repo_name.split('/')[0]
261 repo_name = repo_name.split('/')[0]
262 return repo_name
262 return repo_name
263
263
264 def __get_user(self, username):
264 def __get_user(self, username):
265 return User.by_username(username)
265 return User.get_by_username(username)
266
266
267 def __get_action(self, environ):
267 def __get_action(self, environ):
268 """Maps git request commands into a pull or push command.
268 """Maps git request commands into a pull or push command.
269
269
270 :param environ:
270 :param environ:
271 """
271 """
272 service = environ['QUERY_STRING'].split('=')
272 service = environ['QUERY_STRING'].split('=')
273 if len(service) > 1:
273 if len(service) > 1:
274 service_cmd = service[1]
274 service_cmd = service[1]
275 mapping = {'git-receive-pack': 'push',
275 mapping = {'git-receive-pack': 'push',
276 'git-upload-pack': 'pull',
276 'git-upload-pack': 'pull',
277 }
277 }
278
278
279 return mapping.get(service_cmd,
279 return mapping.get(service_cmd,
280 service_cmd if service_cmd else 'other')
280 service_cmd if service_cmd else 'other')
281 else:
281 else:
282 return 'other'
282 return 'other'
283
283
284 def __invalidate_cache(self, repo_name):
284 def __invalidate_cache(self, repo_name):
285 """we know that some change was made to repositories and we should
285 """we know that some change was made to repositories and we should
286 invalidate the cache to see the changes right away but only for
286 invalidate the cache to see the changes right away but only for
287 push requests"""
287 push requests"""
288 invalidate_cache('get_repo_cached_%s' % repo_name)
288 invalidate_cache('get_repo_cached_%s' % repo_name)
289
289
@@ -1,288 +1,288 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.middleware.simplehg
3 rhodecode.lib.middleware.simplehg
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 SimpleHG middleware for handling mercurial protocol request
6 SimpleHG middleware for handling mercurial protocol request
7 (push/clone etc.). It's implemented with basic auth function
7 (push/clone etc.). It's implemented with basic auth function
8
8
9 :created_on: Apr 28, 2010
9 :created_on: Apr 28, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26
26
27 import os
27 import os
28 import logging
28 import logging
29 import traceback
29 import traceback
30
30
31 from mercurial.error import RepoError
31 from mercurial.error import RepoError
32 from mercurial.hgweb import hgweb_mod
32 from mercurial.hgweb import hgweb_mod
33
33
34 from paste.auth.basic import AuthBasicAuthenticator
34 from paste.auth.basic import AuthBasicAuthenticator
35 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
35 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
36
36
37 from rhodecode.lib import safe_str
37 from rhodecode.lib import safe_str
38 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
38 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
39 from rhodecode.lib.utils import make_ui, invalidate_cache, \
39 from rhodecode.lib.utils import make_ui, invalidate_cache, \
40 is_valid_repo, ui_sections
40 is_valid_repo, ui_sections
41 from rhodecode.model.db import User
41 from rhodecode.model.db import User
42
42
43 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
43 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
44
44
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
46
46
47
47
48 def is_mercurial(environ):
48 def is_mercurial(environ):
49 """Returns True if request's target is mercurial server - header
49 """Returns True if request's target is mercurial server - header
50 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
50 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
51 """
51 """
52 http_accept = environ.get('HTTP_ACCEPT')
52 http_accept = environ.get('HTTP_ACCEPT')
53 if http_accept and http_accept.startswith('application/mercurial'):
53 if http_accept and http_accept.startswith('application/mercurial'):
54 return True
54 return True
55 return False
55 return False
56
56
57
57
58 class SimpleHg(object):
58 class SimpleHg(object):
59
59
60 def __init__(self, application, config):
60 def __init__(self, application, config):
61 self.application = application
61 self.application = application
62 self.config = config
62 self.config = config
63 # base path of repo locations
63 # base path of repo locations
64 self.basepath = self.config['base_path']
64 self.basepath = self.config['base_path']
65 #authenticate this mercurial request using authfunc
65 #authenticate this mercurial request using authfunc
66 self.authenticate = AuthBasicAuthenticator('', authfunc)
66 self.authenticate = AuthBasicAuthenticator('', authfunc)
67 self.ipaddr = '0.0.0.0'
67 self.ipaddr = '0.0.0.0'
68
68
69 def __call__(self, environ, start_response):
69 def __call__(self, environ, start_response):
70 if not is_mercurial(environ):
70 if not is_mercurial(environ):
71 return self.application(environ, start_response)
71 return self.application(environ, start_response)
72
72
73 proxy_key = 'HTTP_X_REAL_IP'
73 proxy_key = 'HTTP_X_REAL_IP'
74 def_key = 'REMOTE_ADDR'
74 def_key = 'REMOTE_ADDR'
75 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
75 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
76
76
77 # skip passing error to error controller
77 # skip passing error to error controller
78 environ['pylons.status_code_redirect'] = True
78 environ['pylons.status_code_redirect'] = True
79
79
80 #======================================================================
80 #======================================================================
81 # EXTRACT REPOSITORY NAME FROM ENV
81 # EXTRACT REPOSITORY NAME FROM ENV
82 #======================================================================
82 #======================================================================
83 try:
83 try:
84 repo_name = environ['REPO_NAME'] = self.__get_repository(environ)
84 repo_name = environ['REPO_NAME'] = self.__get_repository(environ)
85 log.debug('Extracted repo name is %s' % repo_name)
85 log.debug('Extracted repo name is %s' % repo_name)
86 except:
86 except:
87 return HTTPInternalServerError()(environ, start_response)
87 return HTTPInternalServerError()(environ, start_response)
88
88
89 #======================================================================
89 #======================================================================
90 # GET ACTION PULL or PUSH
90 # GET ACTION PULL or PUSH
91 #======================================================================
91 #======================================================================
92 action = self.__get_action(environ)
92 action = self.__get_action(environ)
93
93
94 #======================================================================
94 #======================================================================
95 # CHECK ANONYMOUS PERMISSION
95 # CHECK ANONYMOUS PERMISSION
96 #======================================================================
96 #======================================================================
97 if action in ['pull', 'push']:
97 if action in ['pull', 'push']:
98 anonymous_user = self.__get_user('default')
98 anonymous_user = self.__get_user('default')
99
99
100 username = anonymous_user.username
100 username = anonymous_user.username
101 anonymous_perm = self.__check_permission(action,
101 anonymous_perm = self.__check_permission(action,
102 anonymous_user,
102 anonymous_user,
103 repo_name)
103 repo_name)
104
104
105 if anonymous_perm is not True or anonymous_user.active is False:
105 if anonymous_perm is not True or anonymous_user.active is False:
106 if anonymous_perm is not True:
106 if anonymous_perm is not True:
107 log.debug('Not enough credentials to access this '
107 log.debug('Not enough credentials to access this '
108 'repository as anonymous user')
108 'repository as anonymous user')
109 if anonymous_user.active is False:
109 if anonymous_user.active is False:
110 log.debug('Anonymous access is disabled, running '
110 log.debug('Anonymous access is disabled, running '
111 'authentication')
111 'authentication')
112 #==============================================================
112 #==============================================================
113 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
113 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
114 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
114 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
115 #==============================================================
115 #==============================================================
116
116
117 if not REMOTE_USER(environ):
117 if not REMOTE_USER(environ):
118 self.authenticate.realm = \
118 self.authenticate.realm = \
119 safe_str(self.config['rhodecode_realm'])
119 safe_str(self.config['rhodecode_realm'])
120 result = self.authenticate(environ)
120 result = self.authenticate(environ)
121 if isinstance(result, str):
121 if isinstance(result, str):
122 AUTH_TYPE.update(environ, 'basic')
122 AUTH_TYPE.update(environ, 'basic')
123 REMOTE_USER.update(environ, result)
123 REMOTE_USER.update(environ, result)
124 else:
124 else:
125 return result.wsgi_application(environ, start_response)
125 return result.wsgi_application(environ, start_response)
126
126
127 #==============================================================
127 #==============================================================
128 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM
128 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM
129 # BASIC AUTH
129 # BASIC AUTH
130 #==============================================================
130 #==============================================================
131
131
132 if action in ['pull', 'push']:
132 if action in ['pull', 'push']:
133 username = REMOTE_USER(environ)
133 username = REMOTE_USER(environ)
134 try:
134 try:
135 user = self.__get_user(username)
135 user = self.__get_user(username)
136 username = user.username
136 username = user.username
137 except:
137 except:
138 log.error(traceback.format_exc())
138 log.error(traceback.format_exc())
139 return HTTPInternalServerError()(environ,
139 return HTTPInternalServerError()(environ,
140 start_response)
140 start_response)
141
141
142 #check permissions for this repository
142 #check permissions for this repository
143 perm = self.__check_permission(action, user,
143 perm = self.__check_permission(action, user,
144 repo_name)
144 repo_name)
145 if perm is not True:
145 if perm is not True:
146 return HTTPForbidden()(environ, start_response)
146 return HTTPForbidden()(environ, start_response)
147
147
148 extras = {'ip': ipaddr,
148 extras = {'ip': ipaddr,
149 'username': username,
149 'username': username,
150 'action': action,
150 'action': action,
151 'repository': repo_name}
151 'repository': repo_name}
152
152
153 #======================================================================
153 #======================================================================
154 # MERCURIAL REQUEST HANDLING
154 # MERCURIAL REQUEST HANDLING
155 #======================================================================
155 #======================================================================
156
156
157 repo_path = safe_str(os.path.join(self.basepath, repo_name))
157 repo_path = safe_str(os.path.join(self.basepath, repo_name))
158 log.debug('Repository path is %s' % repo_path)
158 log.debug('Repository path is %s' % repo_path)
159
159
160 baseui = make_ui('db')
160 baseui = make_ui('db')
161 self.__inject_extras(repo_path, baseui, extras)
161 self.__inject_extras(repo_path, baseui, extras)
162
162
163
163
164 # quick check if that dir exists...
164 # quick check if that dir exists...
165 if is_valid_repo(repo_name, self.basepath) is False:
165 if is_valid_repo(repo_name, self.basepath) is False:
166 return HTTPNotFound()(environ, start_response)
166 return HTTPNotFound()(environ, start_response)
167
167
168 try:
168 try:
169 #invalidate cache on push
169 #invalidate cache on push
170 if action == 'push':
170 if action == 'push':
171 self.__invalidate_cache(repo_name)
171 self.__invalidate_cache(repo_name)
172
172
173 app = self.__make_app(repo_path, baseui, extras)
173 app = self.__make_app(repo_path, baseui, extras)
174 return app(environ, start_response)
174 return app(environ, start_response)
175 except RepoError, e:
175 except RepoError, e:
176 if str(e).find('not found') != -1:
176 if str(e).find('not found') != -1:
177 return HTTPNotFound()(environ, start_response)
177 return HTTPNotFound()(environ, start_response)
178 except Exception:
178 except Exception:
179 log.error(traceback.format_exc())
179 log.error(traceback.format_exc())
180 return HTTPInternalServerError()(environ, start_response)
180 return HTTPInternalServerError()(environ, start_response)
181
181
182 def __make_app(self, repo_name, baseui, extras):
182 def __make_app(self, repo_name, baseui, extras):
183 """
183 """
184 Make an wsgi application using hgweb, and inject generated baseui
184 Make an wsgi application using hgweb, and inject generated baseui
185 instance, additionally inject some extras into ui object
185 instance, additionally inject some extras into ui object
186 """
186 """
187 return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui)
187 return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui)
188
188
189
189
190 def __check_permission(self, action, user, repo_name):
190 def __check_permission(self, action, user, repo_name):
191 """
191 """
192 Checks permissions using action (push/pull) user and repository
192 Checks permissions using action (push/pull) user and repository
193 name
193 name
194
194
195 :param action: push or pull action
195 :param action: push or pull action
196 :param user: user instance
196 :param user: user instance
197 :param repo_name: repository name
197 :param repo_name: repository name
198 """
198 """
199 if action == 'push':
199 if action == 'push':
200 if not HasPermissionAnyMiddleware('repository.write',
200 if not HasPermissionAnyMiddleware('repository.write',
201 'repository.admin')(user,
201 'repository.admin')(user,
202 repo_name):
202 repo_name):
203 return False
203 return False
204
204
205 else:
205 else:
206 #any other action need at least read permission
206 #any other action need at least read permission
207 if not HasPermissionAnyMiddleware('repository.read',
207 if not HasPermissionAnyMiddleware('repository.read',
208 'repository.write',
208 'repository.write',
209 'repository.admin')(user,
209 'repository.admin')(user,
210 repo_name):
210 repo_name):
211 return False
211 return False
212
212
213 return True
213 return True
214
214
215 def __get_repository(self, environ):
215 def __get_repository(self, environ):
216 """
216 """
217 Get's repository name out of PATH_INFO header
217 Get's repository name out of PATH_INFO header
218
218
219 :param environ: environ where PATH_INFO is stored
219 :param environ: environ where PATH_INFO is stored
220 """
220 """
221 try:
221 try:
222 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
222 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
223 if repo_name.endswith('/'):
223 if repo_name.endswith('/'):
224 repo_name = repo_name.rstrip('/')
224 repo_name = repo_name.rstrip('/')
225 except:
225 except:
226 log.error(traceback.format_exc())
226 log.error(traceback.format_exc())
227 raise
227 raise
228
228
229 return repo_name
229 return repo_name
230
230
231 def __get_user(self, username):
231 def __get_user(self, username):
232 return User.by_username(username)
232 return User.get_by_username(username)
233
233
234 def __get_action(self, environ):
234 def __get_action(self, environ):
235 """
235 """
236 Maps mercurial request commands into a clone,pull or push command.
236 Maps mercurial request commands into a clone,pull or push command.
237 This should always return a valid command string
237 This should always return a valid command string
238
238
239 :param environ:
239 :param environ:
240 """
240 """
241 mapping = {'changegroup': 'pull',
241 mapping = {'changegroup': 'pull',
242 'changegroupsubset': 'pull',
242 'changegroupsubset': 'pull',
243 'stream_out': 'pull',
243 'stream_out': 'pull',
244 'listkeys': 'pull',
244 'listkeys': 'pull',
245 'unbundle': 'push',
245 'unbundle': 'push',
246 'pushkey': 'push', }
246 'pushkey': 'push', }
247 for qry in environ['QUERY_STRING'].split('&'):
247 for qry in environ['QUERY_STRING'].split('&'):
248 if qry.startswith('cmd'):
248 if qry.startswith('cmd'):
249 cmd = qry.split('=')[-1]
249 cmd = qry.split('=')[-1]
250 if cmd in mapping:
250 if cmd in mapping:
251 return mapping[cmd]
251 return mapping[cmd]
252 else:
252 else:
253 return 'pull'
253 return 'pull'
254
254
255 def __invalidate_cache(self, repo_name):
255 def __invalidate_cache(self, repo_name):
256 """we know that some change was made to repositories and we should
256 """we know that some change was made to repositories and we should
257 invalidate the cache to see the changes right away but only for
257 invalidate the cache to see the changes right away but only for
258 push requests"""
258 push requests"""
259 invalidate_cache('get_repo_cached_%s' % repo_name)
259 invalidate_cache('get_repo_cached_%s' % repo_name)
260
260
261 def __inject_extras(self, repo_path, baseui, extras={}):
261 def __inject_extras(self, repo_path, baseui, extras={}):
262 """
262 """
263 Injects some extra params into baseui instance
263 Injects some extra params into baseui instance
264
264
265 also overwrites global settings with those takes from local hgrc file
265 also overwrites global settings with those takes from local hgrc file
266
266
267 :param baseui: baseui instance
267 :param baseui: baseui instance
268 :param extras: dict with extra params to put into baseui
268 :param extras: dict with extra params to put into baseui
269 """
269 """
270
270
271 hgrc = os.path.join(repo_path, '.hg', 'hgrc')
271 hgrc = os.path.join(repo_path, '.hg', 'hgrc')
272
272
273 # make our hgweb quiet so it doesn't print output
273 # make our hgweb quiet so it doesn't print output
274 baseui.setconfig('ui', 'quiet', 'true')
274 baseui.setconfig('ui', 'quiet', 'true')
275
275
276 #inject some additional parameters that will be available in ui
276 #inject some additional parameters that will be available in ui
277 #for hooks
277 #for hooks
278 for k, v in extras.items():
278 for k, v in extras.items():
279 baseui.setconfig('rhodecode_extras', k, v)
279 baseui.setconfig('rhodecode_extras', k, v)
280
280
281 repoui = make_ui('file', hgrc, False)
281 repoui = make_ui('file', hgrc, False)
282
282
283 if repoui:
283 if repoui:
284 #overwrite our ui instance with the section from hgrc file
284 #overwrite our ui instance with the section from hgrc file
285 for section in ui_sections:
285 for section in ui_sections:
286 for k, v in repoui.configitems(section):
286 for k, v in repoui.configitems(section):
287 baseui.setconfig(section, k, v)
287 baseui.setconfig(section, k, v)
288
288
@@ -1,611 +1,611 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Utilities library for RhodeCode
6 Utilities library for RhodeCode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 import paste
30 import paste
31 import beaker
31 import beaker
32 from os.path import dirname as dn, join as jn
32 from os.path import dirname as dn, join as jn
33
33
34 from paste.script.command import Command, BadCommand
34 from paste.script.command import Command, BadCommand
35
35
36 from mercurial import ui, config
36 from mercurial import ui, config
37
37
38 from webhelpers.text import collapse, remove_formatting, strip_tags
38 from webhelpers.text import collapse, remove_formatting, strip_tags
39
39
40 from vcs import get_backend
40 from vcs import get_backend
41 from vcs.backends.base import BaseChangeset
41 from vcs.backends.base import BaseChangeset
42 from vcs.utils.lazy import LazyProperty
42 from vcs.utils.lazy import LazyProperty
43 from vcs.utils.helpers import get_scm
43 from vcs.utils.helpers import get_scm
44 from vcs.exceptions import VCSError
44 from vcs.exceptions import VCSError
45
45
46 from rhodecode.model import meta
46 from rhodecode.model import meta
47 from rhodecode.model.caching_query import FromCache
47 from rhodecode.model.caching_query import FromCache
48 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group, \
48 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group, \
49 RhodeCodeSettings
49 RhodeCodeSettings
50 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.repo import RepoModel
51
51
52 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
53
53
54
54
55 def recursive_replace(str, replace=' '):
55 def recursive_replace(str, replace=' '):
56 """Recursive replace of given sign to just one instance
56 """Recursive replace of given sign to just one instance
57
57
58 :param str: given string
58 :param str: given string
59 :param replace: char to find and replace multiple instances
59 :param replace: char to find and replace multiple instances
60
60
61 Examples::
61 Examples::
62 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
62 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
63 'Mighty-Mighty-Bo-sstones'
63 'Mighty-Mighty-Bo-sstones'
64 """
64 """
65
65
66 if str.find(replace * 2) == -1:
66 if str.find(replace * 2) == -1:
67 return str
67 return str
68 else:
68 else:
69 str = str.replace(replace * 2, replace)
69 str = str.replace(replace * 2, replace)
70 return recursive_replace(str, replace)
70 return recursive_replace(str, replace)
71
71
72
72
73 def repo_name_slug(value):
73 def repo_name_slug(value):
74 """Return slug of name of repository
74 """Return slug of name of repository
75 This function is called on each creation/modification
75 This function is called on each creation/modification
76 of repository to prevent bad names in repo
76 of repository to prevent bad names in repo
77 """
77 """
78
78
79 slug = remove_formatting(value)
79 slug = remove_formatting(value)
80 slug = strip_tags(slug)
80 slug = strip_tags(slug)
81
81
82 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
82 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
83 slug = slug.replace(c, '-')
83 slug = slug.replace(c, '-')
84 slug = recursive_replace(slug, '-')
84 slug = recursive_replace(slug, '-')
85 slug = collapse(slug, '-')
85 slug = collapse(slug, '-')
86 return slug
86 return slug
87
87
88
88
89 def get_repo_slug(request):
89 def get_repo_slug(request):
90 return request.environ['pylons.routes_dict'].get('repo_name')
90 return request.environ['pylons.routes_dict'].get('repo_name')
91
91
92
92
93 def action_logger(user, action, repo, ipaddr='', sa=None):
93 def action_logger(user, action, repo, ipaddr='', sa=None):
94 """
94 """
95 Action logger for various actions made by users
95 Action logger for various actions made by users
96
96
97 :param user: user that made this action, can be a unique username string or
97 :param user: user that made this action, can be a unique username string or
98 object containing user_id attribute
98 object containing user_id attribute
99 :param action: action to log, should be on of predefined unique actions for
99 :param action: action to log, should be on of predefined unique actions for
100 easy translations
100 easy translations
101 :param repo: string name of repository or object containing repo_id,
101 :param repo: string name of repository or object containing repo_id,
102 that action was made on
102 that action was made on
103 :param ipaddr: optional ip address from what the action was made
103 :param ipaddr: optional ip address from what the action was made
104 :param sa: optional sqlalchemy session
104 :param sa: optional sqlalchemy session
105
105
106 """
106 """
107
107
108 if not sa:
108 if not sa:
109 sa = meta.Session()
109 sa = meta.Session()
110
110
111 try:
111 try:
112 if hasattr(user, 'user_id'):
112 if hasattr(user, 'user_id'):
113 user_obj = user
113 user_obj = user
114 elif isinstance(user, basestring):
114 elif isinstance(user, basestring):
115 user_obj = User.by_username(user)
115 user_obj = User.get_by_username(user)
116 else:
116 else:
117 raise Exception('You have to provide user object or username')
117 raise Exception('You have to provide user object or username')
118
118
119 rm = RepoModel()
119 rm = RepoModel()
120 if hasattr(repo, 'repo_id'):
120 if hasattr(repo, 'repo_id'):
121 repo_obj = rm.get(repo.repo_id, cache=False)
121 repo_obj = rm.get(repo.repo_id, cache=False)
122 repo_name = repo_obj.repo_name
122 repo_name = repo_obj.repo_name
123 elif isinstance(repo, basestring):
123 elif isinstance(repo, basestring):
124 repo_name = repo.lstrip('/')
124 repo_name = repo.lstrip('/')
125 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
125 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
126 else:
126 else:
127 raise Exception('You have to provide repository to action logger')
127 raise Exception('You have to provide repository to action logger')
128
128
129 user_log = UserLog()
129 user_log = UserLog()
130 user_log.user_id = user_obj.user_id
130 user_log.user_id = user_obj.user_id
131 user_log.action = action
131 user_log.action = action
132
132
133 user_log.repository_id = repo_obj.repo_id
133 user_log.repository_id = repo_obj.repo_id
134 user_log.repository_name = repo_name
134 user_log.repository_name = repo_name
135
135
136 user_log.action_date = datetime.datetime.now()
136 user_log.action_date = datetime.datetime.now()
137 user_log.user_ip = ipaddr
137 user_log.user_ip = ipaddr
138 sa.add(user_log)
138 sa.add(user_log)
139 sa.commit()
139 sa.commit()
140
140
141 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
141 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
142 except:
142 except:
143 log.error(traceback.format_exc())
143 log.error(traceback.format_exc())
144 sa.rollback()
144 sa.rollback()
145
145
146
146
147 def get_repos(path, recursive=False):
147 def get_repos(path, recursive=False):
148 """
148 """
149 Scans given path for repos and return (name,(type,path)) tuple
149 Scans given path for repos and return (name,(type,path)) tuple
150
150
151 :param path: path to scann for repositories
151 :param path: path to scann for repositories
152 :param recursive: recursive search and return names with subdirs in front
152 :param recursive: recursive search and return names with subdirs in front
153 """
153 """
154 from vcs.utils.helpers import get_scm
154 from vcs.utils.helpers import get_scm
155 from vcs.exceptions import VCSError
155 from vcs.exceptions import VCSError
156
156
157 if path.endswith(os.sep):
157 if path.endswith(os.sep):
158 #remove ending slash for better results
158 #remove ending slash for better results
159 path = path[:-1]
159 path = path[:-1]
160
160
161 def _get_repos(p):
161 def _get_repos(p):
162 if not os.access(p, os.W_OK):
162 if not os.access(p, os.W_OK):
163 return
163 return
164 for dirpath in os.listdir(p):
164 for dirpath in os.listdir(p):
165 if os.path.isfile(os.path.join(p, dirpath)):
165 if os.path.isfile(os.path.join(p, dirpath)):
166 continue
166 continue
167 cur_path = os.path.join(p, dirpath)
167 cur_path = os.path.join(p, dirpath)
168 try:
168 try:
169 scm_info = get_scm(cur_path)
169 scm_info = get_scm(cur_path)
170 yield scm_info[1].split(path)[-1].lstrip(os.sep), scm_info
170 yield scm_info[1].split(path)[-1].lstrip(os.sep), scm_info
171 except VCSError:
171 except VCSError:
172 if not recursive:
172 if not recursive:
173 continue
173 continue
174 #check if this dir containts other repos for recursive scan
174 #check if this dir containts other repos for recursive scan
175 rec_path = os.path.join(p, dirpath)
175 rec_path = os.path.join(p, dirpath)
176 if os.path.isdir(rec_path):
176 if os.path.isdir(rec_path):
177 for inner_scm in _get_repos(rec_path):
177 for inner_scm in _get_repos(rec_path):
178 yield inner_scm
178 yield inner_scm
179
179
180 return _get_repos(path)
180 return _get_repos(path)
181
181
182
182
183 def is_valid_repo(repo_name, base_path):
183 def is_valid_repo(repo_name, base_path):
184 """
184 """
185 Returns True if given path is a valid repository False otherwise
185 Returns True if given path is a valid repository False otherwise
186 :param repo_name:
186 :param repo_name:
187 :param base_path:
187 :param base_path:
188
188
189 :return True: if given path is a valid repository
189 :return True: if given path is a valid repository
190 """
190 """
191 full_path = os.path.join(base_path, repo_name)
191 full_path = os.path.join(base_path, repo_name)
192
192
193 try:
193 try:
194 get_scm(full_path)
194 get_scm(full_path)
195 return True
195 return True
196 except VCSError:
196 except VCSError:
197 return False
197 return False
198
198
199 def is_valid_repos_group(repos_group_name, base_path):
199 def is_valid_repos_group(repos_group_name, base_path):
200 """
200 """
201 Returns True if given path is a repos group False otherwise
201 Returns True if given path is a repos group False otherwise
202
202
203 :param repo_name:
203 :param repo_name:
204 :param base_path:
204 :param base_path:
205 """
205 """
206 full_path = os.path.join(base_path, repos_group_name)
206 full_path = os.path.join(base_path, repos_group_name)
207
207
208 # check if it's not a repo
208 # check if it's not a repo
209 if is_valid_repo(repos_group_name, base_path):
209 if is_valid_repo(repos_group_name, base_path):
210 return False
210 return False
211
211
212 # check if it's a valid path
212 # check if it's a valid path
213 if os.path.isdir(full_path):
213 if os.path.isdir(full_path):
214 return True
214 return True
215
215
216 return False
216 return False
217
217
218 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
218 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
219 while True:
219 while True:
220 ok = raw_input(prompt)
220 ok = raw_input(prompt)
221 if ok in ('y', 'ye', 'yes'):
221 if ok in ('y', 'ye', 'yes'):
222 return True
222 return True
223 if ok in ('n', 'no', 'nop', 'nope'):
223 if ok in ('n', 'no', 'nop', 'nope'):
224 return False
224 return False
225 retries = retries - 1
225 retries = retries - 1
226 if retries < 0:
226 if retries < 0:
227 raise IOError
227 raise IOError
228 print complaint
228 print complaint
229
229
230 #propagated from mercurial documentation
230 #propagated from mercurial documentation
231 ui_sections = ['alias', 'auth',
231 ui_sections = ['alias', 'auth',
232 'decode/encode', 'defaults',
232 'decode/encode', 'defaults',
233 'diff', 'email',
233 'diff', 'email',
234 'extensions', 'format',
234 'extensions', 'format',
235 'merge-patterns', 'merge-tools',
235 'merge-patterns', 'merge-tools',
236 'hooks', 'http_proxy',
236 'hooks', 'http_proxy',
237 'smtp', 'patch',
237 'smtp', 'patch',
238 'paths', 'profiling',
238 'paths', 'profiling',
239 'server', 'trusted',
239 'server', 'trusted',
240 'ui', 'web', ]
240 'ui', 'web', ]
241
241
242
242
243 def make_ui(read_from='file', path=None, checkpaths=True):
243 def make_ui(read_from='file', path=None, checkpaths=True):
244 """A function that will read python rc files or database
244 """A function that will read python rc files or database
245 and make an mercurial ui object from read options
245 and make an mercurial ui object from read options
246
246
247 :param path: path to mercurial config file
247 :param path: path to mercurial config file
248 :param checkpaths: check the path
248 :param checkpaths: check the path
249 :param read_from: read from 'file' or 'db'
249 :param read_from: read from 'file' or 'db'
250 """
250 """
251
251
252 baseui = ui.ui()
252 baseui = ui.ui()
253
253
254 #clean the baseui object
254 #clean the baseui object
255 baseui._ocfg = config.config()
255 baseui._ocfg = config.config()
256 baseui._ucfg = config.config()
256 baseui._ucfg = config.config()
257 baseui._tcfg = config.config()
257 baseui._tcfg = config.config()
258
258
259 if read_from == 'file':
259 if read_from == 'file':
260 if not os.path.isfile(path):
260 if not os.path.isfile(path):
261 log.warning('Unable to read config file %s' % path)
261 log.warning('Unable to read config file %s' % path)
262 return False
262 return False
263 log.debug('reading hgrc from %s', path)
263 log.debug('reading hgrc from %s', path)
264 cfg = config.config()
264 cfg = config.config()
265 cfg.read(path)
265 cfg.read(path)
266 for section in ui_sections:
266 for section in ui_sections:
267 for k, v in cfg.items(section):
267 for k, v in cfg.items(section):
268 log.debug('settings ui from file[%s]%s:%s', section, k, v)
268 log.debug('settings ui from file[%s]%s:%s', section, k, v)
269 baseui.setconfig(section, k, v)
269 baseui.setconfig(section, k, v)
270
270
271 elif read_from == 'db':
271 elif read_from == 'db':
272 sa = meta.Session()
272 sa = meta.Session()
273 ret = sa.query(RhodeCodeUi)\
273 ret = sa.query(RhodeCodeUi)\
274 .options(FromCache("sql_cache_short",
274 .options(FromCache("sql_cache_short",
275 "get_hg_ui_settings")).all()
275 "get_hg_ui_settings")).all()
276
276
277 hg_ui = ret
277 hg_ui = ret
278 for ui_ in hg_ui:
278 for ui_ in hg_ui:
279 if ui_.ui_active:
279 if ui_.ui_active:
280 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
280 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
281 ui_.ui_key, ui_.ui_value)
281 ui_.ui_key, ui_.ui_value)
282 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
282 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
283
283
284 meta.Session.remove()
284 meta.Session.remove()
285 return baseui
285 return baseui
286
286
287
287
288 def set_rhodecode_config(config):
288 def set_rhodecode_config(config):
289 """Updates pylons config with new settings from database
289 """Updates pylons config with new settings from database
290
290
291 :param config:
291 :param config:
292 """
292 """
293 hgsettings = RhodeCodeSettings.get_app_settings()
293 hgsettings = RhodeCodeSettings.get_app_settings()
294
294
295 for k, v in hgsettings.items():
295 for k, v in hgsettings.items():
296 config[k] = v
296 config[k] = v
297
297
298
298
299 def invalidate_cache(cache_key, *args):
299 def invalidate_cache(cache_key, *args):
300 """Puts cache invalidation task into db for
300 """Puts cache invalidation task into db for
301 further global cache invalidation
301 further global cache invalidation
302 """
302 """
303
303
304 from rhodecode.model.scm import ScmModel
304 from rhodecode.model.scm import ScmModel
305
305
306 if cache_key.startswith('get_repo_cached_'):
306 if cache_key.startswith('get_repo_cached_'):
307 name = cache_key.split('get_repo_cached_')[-1]
307 name = cache_key.split('get_repo_cached_')[-1]
308 ScmModel().mark_for_invalidation(name)
308 ScmModel().mark_for_invalidation(name)
309
309
310
310
311 class EmptyChangeset(BaseChangeset):
311 class EmptyChangeset(BaseChangeset):
312 """
312 """
313 An dummy empty changeset. It's possible to pass hash when creating
313 An dummy empty changeset. It's possible to pass hash when creating
314 an EmptyChangeset
314 an EmptyChangeset
315 """
315 """
316
316
317 def __init__(self, cs='0' * 40, repo=None, requested_revision=None, alias=None):
317 def __init__(self, cs='0' * 40, repo=None, requested_revision=None, alias=None):
318 self._empty_cs = cs
318 self._empty_cs = cs
319 self.revision = -1
319 self.revision = -1
320 self.message = ''
320 self.message = ''
321 self.author = ''
321 self.author = ''
322 self.date = ''
322 self.date = ''
323 self.repository = repo
323 self.repository = repo
324 self.requested_revision = requested_revision
324 self.requested_revision = requested_revision
325 self.alias = alias
325 self.alias = alias
326
326
327 @LazyProperty
327 @LazyProperty
328 def raw_id(self):
328 def raw_id(self):
329 """Returns raw string identifying this changeset, useful for web
329 """Returns raw string identifying this changeset, useful for web
330 representation.
330 representation.
331 """
331 """
332
332
333 return self._empty_cs
333 return self._empty_cs
334
334
335 @LazyProperty
335 @LazyProperty
336 def branch(self):
336 def branch(self):
337 return get_backend(self.alias).DEFAULT_BRANCH_NAME
337 return get_backend(self.alias).DEFAULT_BRANCH_NAME
338
338
339 @LazyProperty
339 @LazyProperty
340 def short_id(self):
340 def short_id(self):
341 return self.raw_id[:12]
341 return self.raw_id[:12]
342
342
343 def get_file_changeset(self, path):
343 def get_file_changeset(self, path):
344 return self
344 return self
345
345
346 def get_file_content(self, path):
346 def get_file_content(self, path):
347 return u''
347 return u''
348
348
349 def get_file_size(self, path):
349 def get_file_size(self, path):
350 return 0
350 return 0
351
351
352
352
353 def map_groups(groups):
353 def map_groups(groups):
354 """Checks for groups existence, and creates groups structures.
354 """Checks for groups existence, and creates groups structures.
355 It returns last group in structure
355 It returns last group in structure
356
356
357 :param groups: list of groups structure
357 :param groups: list of groups structure
358 """
358 """
359 sa = meta.Session()
359 sa = meta.Session()
360
360
361 parent = None
361 parent = None
362 group = None
362 group = None
363 for lvl, group_name in enumerate(groups[:-1]):
363 for lvl, group_name in enumerate(groups[:-1]):
364 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
364 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
365
365
366 if group is None:
366 if group is None:
367 group = Group(group_name, parent)
367 group = Group(group_name, parent)
368 sa.add(group)
368 sa.add(group)
369 sa.commit()
369 sa.commit()
370
370
371 parent = group
371 parent = group
372
372
373 return group
373 return group
374
374
375
375
376 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
376 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
377 """maps all repos given in initial_repo_list, non existing repositories
377 """maps all repos given in initial_repo_list, non existing repositories
378 are created, if remove_obsolete is True it also check for db entries
378 are created, if remove_obsolete is True it also check for db entries
379 that are not in initial_repo_list and removes them.
379 that are not in initial_repo_list and removes them.
380
380
381 :param initial_repo_list: list of repositories found by scanning methods
381 :param initial_repo_list: list of repositories found by scanning methods
382 :param remove_obsolete: check for obsolete entries in database
382 :param remove_obsolete: check for obsolete entries in database
383 """
383 """
384
384
385 sa = meta.Session()
385 sa = meta.Session()
386 rm = RepoModel()
386 rm = RepoModel()
387 user = sa.query(User).filter(User.admin == True).first()
387 user = sa.query(User).filter(User.admin == True).first()
388 added = []
388 added = []
389 for name, repo in initial_repo_list.items():
389 for name, repo in initial_repo_list.items():
390 group = map_groups(name.split(os.sep))
390 group = map_groups(name.split(os.sep))
391 if not rm.get_by_repo_name(name, cache=False):
391 if not rm.get_by_repo_name(name, cache=False):
392 log.info('repository %s not found creating default', name)
392 log.info('repository %s not found creating default', name)
393 added.append(name)
393 added.append(name)
394 form_data = {
394 form_data = {
395 'repo_name': name,
395 'repo_name': name,
396 'repo_name_full': name,
396 'repo_name_full': name,
397 'repo_type': repo.alias,
397 'repo_type': repo.alias,
398 'description': repo.description \
398 'description': repo.description \
399 if repo.description != 'unknown' else \
399 if repo.description != 'unknown' else \
400 '%s repository' % name,
400 '%s repository' % name,
401 'private': False,
401 'private': False,
402 'group_id': getattr(group, 'group_id', None)
402 'group_id': getattr(group, 'group_id', None)
403 }
403 }
404 rm.create(form_data, user, just_db=True)
404 rm.create(form_data, user, just_db=True)
405
405
406 removed = []
406 removed = []
407 if remove_obsolete:
407 if remove_obsolete:
408 #remove from database those repositories that are not in the filesystem
408 #remove from database those repositories that are not in the filesystem
409 for repo in sa.query(Repository).all():
409 for repo in sa.query(Repository).all():
410 if repo.repo_name not in initial_repo_list.keys():
410 if repo.repo_name not in initial_repo_list.keys():
411 removed.append(repo.repo_name)
411 removed.append(repo.repo_name)
412 sa.delete(repo)
412 sa.delete(repo)
413 sa.commit()
413 sa.commit()
414
414
415 return added, removed
415 return added, removed
416
416
417 #set cache regions for beaker so celery can utilise it
417 #set cache regions for beaker so celery can utilise it
418 def add_cache(settings):
418 def add_cache(settings):
419 cache_settings = {'regions': None}
419 cache_settings = {'regions': None}
420 for key in settings.keys():
420 for key in settings.keys():
421 for prefix in ['beaker.cache.', 'cache.']:
421 for prefix in ['beaker.cache.', 'cache.']:
422 if key.startswith(prefix):
422 if key.startswith(prefix):
423 name = key.split(prefix)[1].strip()
423 name = key.split(prefix)[1].strip()
424 cache_settings[name] = settings[key].strip()
424 cache_settings[name] = settings[key].strip()
425 if cache_settings['regions']:
425 if cache_settings['regions']:
426 for region in cache_settings['regions'].split(','):
426 for region in cache_settings['regions'].split(','):
427 region = region.strip()
427 region = region.strip()
428 region_settings = {}
428 region_settings = {}
429 for key, value in cache_settings.items():
429 for key, value in cache_settings.items():
430 if key.startswith(region):
430 if key.startswith(region):
431 region_settings[key.split('.')[1]] = value
431 region_settings[key.split('.')[1]] = value
432 region_settings['expire'] = int(region_settings.get('expire',
432 region_settings['expire'] = int(region_settings.get('expire',
433 60))
433 60))
434 region_settings.setdefault('lock_dir',
434 region_settings.setdefault('lock_dir',
435 cache_settings.get('lock_dir'))
435 cache_settings.get('lock_dir'))
436 region_settings.setdefault('data_dir',
436 region_settings.setdefault('data_dir',
437 cache_settings.get('data_dir'))
437 cache_settings.get('data_dir'))
438
438
439 if 'type' not in region_settings:
439 if 'type' not in region_settings:
440 region_settings['type'] = cache_settings.get('type',
440 region_settings['type'] = cache_settings.get('type',
441 'memory')
441 'memory')
442 beaker.cache.cache_regions[region] = region_settings
442 beaker.cache.cache_regions[region] = region_settings
443
443
444
444
445 def get_current_revision():
445 def get_current_revision():
446 """Returns tuple of (number, id) from repository containing this package
446 """Returns tuple of (number, id) from repository containing this package
447 or None if repository could not be found.
447 or None if repository could not be found.
448 """
448 """
449
449
450 try:
450 try:
451 from vcs import get_repo
451 from vcs import get_repo
452 from vcs.utils.helpers import get_scm
452 from vcs.utils.helpers import get_scm
453 from vcs.exceptions import RepositoryError, VCSError
453 from vcs.exceptions import RepositoryError, VCSError
454 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
454 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
455 scm = get_scm(repopath)[0]
455 scm = get_scm(repopath)[0]
456 repo = get_repo(path=repopath, alias=scm)
456 repo = get_repo(path=repopath, alias=scm)
457 tip = repo.get_changeset()
457 tip = repo.get_changeset()
458 return (tip.revision, tip.short_id)
458 return (tip.revision, tip.short_id)
459 except (ImportError, RepositoryError, VCSError), err:
459 except (ImportError, RepositoryError, VCSError), err:
460 logging.debug("Cannot retrieve rhodecode's revision. Original error "
460 logging.debug("Cannot retrieve rhodecode's revision. Original error "
461 "was: %s" % err)
461 "was: %s" % err)
462 return None
462 return None
463
463
464
464
465 #==============================================================================
465 #==============================================================================
466 # TEST FUNCTIONS AND CREATORS
466 # TEST FUNCTIONS AND CREATORS
467 #==============================================================================
467 #==============================================================================
468 def create_test_index(repo_location, config, full_index):
468 def create_test_index(repo_location, config, full_index):
469 """
469 """
470 Makes default test index
470 Makes default test index
471
471
472 :param config: test config
472 :param config: test config
473 :param full_index:
473 :param full_index:
474 """
474 """
475
475
476 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
476 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
477 from rhodecode.lib.pidlock import DaemonLock, LockHeld
477 from rhodecode.lib.pidlock import DaemonLock, LockHeld
478
478
479 repo_location = repo_location
479 repo_location = repo_location
480
480
481 index_location = os.path.join(config['app_conf']['index_dir'])
481 index_location = os.path.join(config['app_conf']['index_dir'])
482 if not os.path.exists(index_location):
482 if not os.path.exists(index_location):
483 os.makedirs(index_location)
483 os.makedirs(index_location)
484
484
485 try:
485 try:
486 l = DaemonLock(file=jn(dn(index_location), 'make_index.lock'))
486 l = DaemonLock(file=jn(dn(index_location), 'make_index.lock'))
487 WhooshIndexingDaemon(index_location=index_location,
487 WhooshIndexingDaemon(index_location=index_location,
488 repo_location=repo_location)\
488 repo_location=repo_location)\
489 .run(full_index=full_index)
489 .run(full_index=full_index)
490 l.release()
490 l.release()
491 except LockHeld:
491 except LockHeld:
492 pass
492 pass
493
493
494
494
495 def create_test_env(repos_test_path, config):
495 def create_test_env(repos_test_path, config):
496 """Makes a fresh database and
496 """Makes a fresh database and
497 install test repository into tmp dir
497 install test repository into tmp dir
498 """
498 """
499 from rhodecode.lib.db_manage import DbManage
499 from rhodecode.lib.db_manage import DbManage
500 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
500 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
501 HG_FORK, GIT_FORK, TESTS_TMP_PATH
501 HG_FORK, GIT_FORK, TESTS_TMP_PATH
502 import tarfile
502 import tarfile
503 import shutil
503 import shutil
504 from os.path import abspath
504 from os.path import abspath
505
505
506 # PART ONE create db
506 # PART ONE create db
507 dbconf = config['sqlalchemy.db1.url']
507 dbconf = config['sqlalchemy.db1.url']
508 log.debug('making test db %s', dbconf)
508 log.debug('making test db %s', dbconf)
509
509
510 # create test dir if it doesn't exist
510 # create test dir if it doesn't exist
511 if not os.path.isdir(repos_test_path):
511 if not os.path.isdir(repos_test_path):
512 log.debug('Creating testdir %s' % repos_test_path)
512 log.debug('Creating testdir %s' % repos_test_path)
513 os.makedirs(repos_test_path)
513 os.makedirs(repos_test_path)
514
514
515 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
515 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
516 tests=True)
516 tests=True)
517 dbmanage.create_tables(override=True)
517 dbmanage.create_tables(override=True)
518 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
518 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
519 dbmanage.create_default_user()
519 dbmanage.create_default_user()
520 dbmanage.admin_prompt()
520 dbmanage.admin_prompt()
521 dbmanage.create_permissions()
521 dbmanage.create_permissions()
522 dbmanage.populate_default_permissions()
522 dbmanage.populate_default_permissions()
523
523
524 # PART TWO make test repo
524 # PART TWO make test repo
525 log.debug('making test vcs repositories')
525 log.debug('making test vcs repositories')
526
526
527 idx_path = config['app_conf']['index_dir']
527 idx_path = config['app_conf']['index_dir']
528 data_path = config['app_conf']['cache_dir']
528 data_path = config['app_conf']['cache_dir']
529
529
530 #clean index and data
530 #clean index and data
531 if idx_path and os.path.exists(idx_path):
531 if idx_path and os.path.exists(idx_path):
532 log.debug('remove %s' % idx_path)
532 log.debug('remove %s' % idx_path)
533 shutil.rmtree(idx_path)
533 shutil.rmtree(idx_path)
534
534
535 if data_path and os.path.exists(data_path):
535 if data_path and os.path.exists(data_path):
536 log.debug('remove %s' % data_path)
536 log.debug('remove %s' % data_path)
537 shutil.rmtree(data_path)
537 shutil.rmtree(data_path)
538
538
539 #CREATE DEFAULT HG REPOSITORY
539 #CREATE DEFAULT HG REPOSITORY
540 cur_dir = dn(dn(abspath(__file__)))
540 cur_dir = dn(dn(abspath(__file__)))
541 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
541 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
542 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
542 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
543 tar.close()
543 tar.close()
544
544
545
545
546 #==============================================================================
546 #==============================================================================
547 # PASTER COMMANDS
547 # PASTER COMMANDS
548 #==============================================================================
548 #==============================================================================
549 class BasePasterCommand(Command):
549 class BasePasterCommand(Command):
550 """
550 """
551 Abstract Base Class for paster commands.
551 Abstract Base Class for paster commands.
552
552
553 The celery commands are somewhat aggressive about loading
553 The celery commands are somewhat aggressive about loading
554 celery.conf, and since our module sets the `CELERY_LOADER`
554 celery.conf, and since our module sets the `CELERY_LOADER`
555 environment variable to our loader, we have to bootstrap a bit and
555 environment variable to our loader, we have to bootstrap a bit and
556 make sure we've had a chance to load the pylons config off of the
556 make sure we've had a chance to load the pylons config off of the
557 command line, otherwise everything fails.
557 command line, otherwise everything fails.
558 """
558 """
559 min_args = 1
559 min_args = 1
560 min_args_error = "Please provide a paster config file as an argument."
560 min_args_error = "Please provide a paster config file as an argument."
561 takes_config_file = 1
561 takes_config_file = 1
562 requires_config_file = True
562 requires_config_file = True
563
563
564 def notify_msg(self, msg, log=False):
564 def notify_msg(self, msg, log=False):
565 """Make a notification to user, additionally if logger is passed
565 """Make a notification to user, additionally if logger is passed
566 it logs this action using given logger
566 it logs this action using given logger
567
567
568 :param msg: message that will be printed to user
568 :param msg: message that will be printed to user
569 :param log: logging instance, to use to additionally log this message
569 :param log: logging instance, to use to additionally log this message
570
570
571 """
571 """
572 if log and isinstance(log, logging):
572 if log and isinstance(log, logging):
573 log(msg)
573 log(msg)
574
574
575 def run(self, args):
575 def run(self, args):
576 """
576 """
577 Overrides Command.run
577 Overrides Command.run
578
578
579 Checks for a config file argument and loads it.
579 Checks for a config file argument and loads it.
580 """
580 """
581 if len(args) < self.min_args:
581 if len(args) < self.min_args:
582 raise BadCommand(
582 raise BadCommand(
583 self.min_args_error % {'min_args': self.min_args,
583 self.min_args_error % {'min_args': self.min_args,
584 'actual_args': len(args)})
584 'actual_args': len(args)})
585
585
586 # Decrement because we're going to lob off the first argument.
586 # Decrement because we're going to lob off the first argument.
587 # @@ This is hacky
587 # @@ This is hacky
588 self.min_args -= 1
588 self.min_args -= 1
589 self.bootstrap_config(args[0])
589 self.bootstrap_config(args[0])
590 self.update_parser()
590 self.update_parser()
591 return super(BasePasterCommand, self).run(args[1:])
591 return super(BasePasterCommand, self).run(args[1:])
592
592
593 def update_parser(self):
593 def update_parser(self):
594 """
594 """
595 Abstract method. Allows for the class's parser to be updated
595 Abstract method. Allows for the class's parser to be updated
596 before the superclass's `run` method is called. Necessary to
596 before the superclass's `run` method is called. Necessary to
597 allow options/arguments to be passed through to the underlying
597 allow options/arguments to be passed through to the underlying
598 celery command.
598 celery command.
599 """
599 """
600 raise NotImplementedError("Abstract Method.")
600 raise NotImplementedError("Abstract Method.")
601
601
602 def bootstrap_config(self, conf):
602 def bootstrap_config(self, conf):
603 """
603 """
604 Loads the pylons configuration.
604 Loads the pylons configuration.
605 """
605 """
606 from pylons import config as pylonsconfig
606 from pylons import config as pylonsconfig
607
607
608 path_to_ini_file = os.path.realpath(conf)
608 path_to_ini_file = os.path.realpath(conf)
609 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
609 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
610 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
610 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
611
611
@@ -1,980 +1,979 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 from datetime import date
30 from datetime import date
31
31
32 from sqlalchemy import *
32 from sqlalchemy import *
33 from sqlalchemy.exc import DatabaseError
33 from sqlalchemy.exc import DatabaseError
34 from sqlalchemy.orm import relationship, backref, joinedload, class_mapper
34 from sqlalchemy.orm import relationship, backref, joinedload, class_mapper
35 from sqlalchemy.orm.interfaces import MapperExtension
35 from sqlalchemy.orm.interfaces import MapperExtension
36
36
37 from beaker.cache import cache_region, region_invalidate
37 from beaker.cache import cache_region, region_invalidate
38
38
39 from vcs import get_backend
39 from vcs import get_backend
40 from vcs.utils.helpers import get_scm
40 from vcs.utils.helpers import get_scm
41 from vcs.exceptions import VCSError
41 from vcs.exceptions import VCSError
42 from vcs.utils.lazy import LazyProperty
42 from vcs.utils.lazy import LazyProperty
43
43
44 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \
44 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, \
45 generate_api_key
45 generate_api_key
46 from rhodecode.lib.exceptions import UsersGroupsAssignedException
46 from rhodecode.lib.exceptions import UsersGroupsAssignedException
47 from rhodecode.lib.compat import json
47 from rhodecode.lib.compat import json
48
48
49 from rhodecode.model.meta import Base, Session
49 from rhodecode.model.meta import Base, Session
50 from rhodecode.model.caching_query import FromCache
50 from rhodecode.model.caching_query import FromCache
51
51
52 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
53
53
54 #==============================================================================
54 #==============================================================================
55 # BASE CLASSES
55 # BASE CLASSES
56 #==============================================================================
56 #==============================================================================
57
57
58 class ModelSerializer(json.JSONEncoder):
58 class ModelSerializer(json.JSONEncoder):
59 """
59 """
60 Simple Serializer for JSON,
60 Simple Serializer for JSON,
61
61
62 usage::
62 usage::
63
63
64 to make object customized for serialization implement a __json__
64 to make object customized for serialization implement a __json__
65 method that will return a dict for serialization into json
65 method that will return a dict for serialization into json
66
66
67 example::
67 example::
68
68
69 class Task(object):
69 class Task(object):
70
70
71 def __init__(self, name, value):
71 def __init__(self, name, value):
72 self.name = name
72 self.name = name
73 self.value = value
73 self.value = value
74
74
75 def __json__(self):
75 def __json__(self):
76 return dict(name=self.name,
76 return dict(name=self.name,
77 value=self.value)
77 value=self.value)
78
78
79 """
79 """
80
80
81 def default(self, obj):
81 def default(self, obj):
82
82
83 if hasattr(obj, '__json__'):
83 if hasattr(obj, '__json__'):
84 return obj.__json__()
84 return obj.__json__()
85 else:
85 else:
86 return json.JSONEncoder.default(self, obj)
86 return json.JSONEncoder.default(self, obj)
87
87
88 class BaseModel(object):
88 class BaseModel(object):
89 """Base Model for all classess
89 """Base Model for all classess
90
90
91 """
91 """
92
92
93 @classmethod
93 @classmethod
94 def _get_keys(cls):
94 def _get_keys(cls):
95 """return column names for this model """
95 """return column names for this model """
96 return class_mapper(cls).c.keys()
96 return class_mapper(cls).c.keys()
97
97
98 def get_dict(self):
98 def get_dict(self):
99 """return dict with keys and values corresponding
99 """return dict with keys and values corresponding
100 to this model data """
100 to this model data """
101
101
102 d = {}
102 d = {}
103 for k in self._get_keys():
103 for k in self._get_keys():
104 d[k] = getattr(self, k)
104 d[k] = getattr(self, k)
105 return d
105 return d
106
106
107 def get_appstruct(self):
107 def get_appstruct(self):
108 """return list with keys and values tupples corresponding
108 """return list with keys and values tupples corresponding
109 to this model data """
109 to this model data """
110
110
111 l = []
111 l = []
112 for k in self._get_keys():
112 for k in self._get_keys():
113 l.append((k, getattr(self, k),))
113 l.append((k, getattr(self, k),))
114 return l
114 return l
115
115
116 def populate_obj(self, populate_dict):
116 def populate_obj(self, populate_dict):
117 """populate model with data from given populate_dict"""
117 """populate model with data from given populate_dict"""
118
118
119 for k in self._get_keys():
119 for k in self._get_keys():
120 if k in populate_dict:
120 if k in populate_dict:
121 setattr(self, k, populate_dict[k])
121 setattr(self, k, populate_dict[k])
122
122
123 @classmethod
123 @classmethod
124 def query(cls):
124 def query(cls):
125 return Session.query(cls)
125 return Session.query(cls)
126
126
127 @classmethod
127 @classmethod
128 def get(cls, id_):
128 def get(cls, id_):
129 return Session.query(cls).get(id_)
129 return Session.query(cls).get(id_)
130
130
131 @classmethod
131 @classmethod
132 def delete(cls, id_):
132 def delete(cls, id_):
133 obj = Session.query(cls).get(id_)
133 obj = Session.query(cls).get(id_)
134 Session.delete(obj)
134 Session.delete(obj)
135 Session.commit()
135 Session.commit()
136
136
137
137
138 class RhodeCodeSettings(Base, BaseModel):
138 class RhodeCodeSettings(Base, BaseModel):
139 __tablename__ = 'rhodecode_settings'
139 __tablename__ = 'rhodecode_settings'
140 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
140 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
141 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
141 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
142 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
142 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
143 app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
143 app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
144
144
145 def __init__(self, k='', v=''):
145 def __init__(self, k='', v=''):
146 self.app_settings_name = k
146 self.app_settings_name = k
147 self.app_settings_value = v
147 self.app_settings_value = v
148
148
149 def __repr__(self):
149 def __repr__(self):
150 return "<%s('%s:%s')>" % (self.__class__.__name__,
150 return "<%s('%s:%s')>" % (self.__class__.__name__,
151 self.app_settings_name, self.app_settings_value)
151 self.app_settings_name, self.app_settings_value)
152
152
153
153
154 @classmethod
154 @classmethod
155 def get_by_name(cls, ldap_key):
155 def get_by_name(cls, ldap_key):
156 return Session.query(cls)\
156 return Session.query(cls)\
157 .filter(cls.app_settings_name == ldap_key).scalar()
157 .filter(cls.app_settings_name == ldap_key).scalar()
158
158
159 @classmethod
159 @classmethod
160 def get_app_settings(cls, cache=False):
160 def get_app_settings(cls, cache=False):
161
161
162 ret = Session.query(cls)
162 ret = Session.query(cls)
163
163
164 if cache:
164 if cache:
165 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
165 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
166
166
167 if not ret:
167 if not ret:
168 raise Exception('Could not get application settings !')
168 raise Exception('Could not get application settings !')
169 settings = {}
169 settings = {}
170 for each in ret:
170 for each in ret:
171 settings['rhodecode_' + each.app_settings_name] = \
171 settings['rhodecode_' + each.app_settings_name] = \
172 each.app_settings_value
172 each.app_settings_value
173
173
174 return settings
174 return settings
175
175
176 @classmethod
176 @classmethod
177 def get_ldap_settings(cls, cache=False):
177 def get_ldap_settings(cls, cache=False):
178 ret = Session.query(cls)\
178 ret = Session.query(cls)\
179 .filter(cls.app_settings_name.startswith('ldap_'))\
179 .filter(cls.app_settings_name.startswith('ldap_'))\
180 .all()
180 .all()
181 fd = {}
181 fd = {}
182 for row in ret:
182 for row in ret:
183 fd.update({row.app_settings_name:row.app_settings_value})
183 fd.update({row.app_settings_name:row.app_settings_value})
184
184
185 fd.update({'ldap_active':str2bool(fd.get('ldap_active'))})
185 fd.update({'ldap_active':str2bool(fd.get('ldap_active'))})
186
186
187 return fd
187 return fd
188
188
189
189
190 class RhodeCodeUi(Base, BaseModel):
190 class RhodeCodeUi(Base, BaseModel):
191 __tablename__ = 'rhodecode_ui'
191 __tablename__ = 'rhodecode_ui'
192 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
192 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
193
193
194 HOOK_UPDATE = 'changegroup.update'
194 HOOK_UPDATE = 'changegroup.update'
195 HOOK_REPO_SIZE = 'changegroup.repo_size'
195 HOOK_REPO_SIZE = 'changegroup.repo_size'
196 HOOK_PUSH = 'pretxnchangegroup.push_logger'
196 HOOK_PUSH = 'pretxnchangegroup.push_logger'
197 HOOK_PULL = 'preoutgoing.pull_logger'
197 HOOK_PULL = 'preoutgoing.pull_logger'
198
198
199 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
199 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
200 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
200 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
201 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
201 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
202 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
202 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
203 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
203 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
204
204
205
205
206 @classmethod
206 @classmethod
207 def get_by_key(cls, key):
207 def get_by_key(cls, key):
208 return Session.query(cls).filter(cls.ui_key == key)
208 return Session.query(cls).filter(cls.ui_key == key)
209
209
210
210
211 @classmethod
211 @classmethod
212 def get_builtin_hooks(cls):
212 def get_builtin_hooks(cls):
213 q = cls.query()
213 q = cls.query()
214 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
214 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
215 cls.HOOK_REPO_SIZE,
215 cls.HOOK_REPO_SIZE,
216 cls.HOOK_PUSH, cls.HOOK_PULL]))
216 cls.HOOK_PUSH, cls.HOOK_PULL]))
217 return q.all()
217 return q.all()
218
218
219 @classmethod
219 @classmethod
220 def get_custom_hooks(cls):
220 def get_custom_hooks(cls):
221 q = cls.query()
221 q = cls.query()
222 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
222 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
223 cls.HOOK_REPO_SIZE,
223 cls.HOOK_REPO_SIZE,
224 cls.HOOK_PUSH, cls.HOOK_PULL]))
224 cls.HOOK_PUSH, cls.HOOK_PULL]))
225 q = q.filter(cls.ui_section == 'hooks')
225 q = q.filter(cls.ui_section == 'hooks')
226 return q.all()
226 return q.all()
227
227
228 @classmethod
228 @classmethod
229 def create_or_update_hook(cls, key, val):
229 def create_or_update_hook(cls, key, val):
230 new_ui = cls.get_by_key(key).scalar() or cls()
230 new_ui = cls.get_by_key(key).scalar() or cls()
231 new_ui.ui_section = 'hooks'
231 new_ui.ui_section = 'hooks'
232 new_ui.ui_active = True
232 new_ui.ui_active = True
233 new_ui.ui_key = key
233 new_ui.ui_key = key
234 new_ui.ui_value = val
234 new_ui.ui_value = val
235
235
236 Session.add(new_ui)
236 Session.add(new_ui)
237 Session.commit()
237 Session.commit()
238
238
239
239
240 class User(Base, BaseModel):
240 class User(Base, BaseModel):
241 __tablename__ = 'users'
241 __tablename__ = 'users'
242 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
242 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
243 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
243 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
244 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
244 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
245 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
245 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
246 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
246 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
247 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
247 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
248 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
248 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
249 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
249 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
250 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
250 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
251 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
251 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
252 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
252 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
253 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
253 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
254
254
255 user_log = relationship('UserLog', cascade='all')
255 user_log = relationship('UserLog', cascade='all')
256 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
256 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
257
257
258 repositories = relationship('Repository')
258 repositories = relationship('Repository')
259 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
259 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
260 repo_to_perm = relationship('RepoToPerm', primaryjoin='RepoToPerm.user_id==User.user_id', cascade='all')
260 repo_to_perm = relationship('RepoToPerm', primaryjoin='RepoToPerm.user_id==User.user_id', cascade='all')
261
261
262 group_member = relationship('UsersGroupMember', cascade='all')
262 group_member = relationship('UsersGroupMember', cascade='all')
263
263
264 @property
264 @property
265 def full_contact(self):
265 def full_contact(self):
266 return '%s %s <%s>' % (self.name, self.lastname, self.email)
266 return '%s %s <%s>' % (self.name, self.lastname, self.email)
267
267
268 @property
268 @property
269 def short_contact(self):
269 def short_contact(self):
270 return '%s %s' % (self.name, self.lastname)
270 return '%s %s' % (self.name, self.lastname)
271
271
272 @property
272 @property
273 def is_admin(self):
273 def is_admin(self):
274 return self.admin
274 return self.admin
275
275
276 def __repr__(self):
276 def __repr__(self):
277 try:
277 try:
278 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
278 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
279 self.user_id, self.username)
279 self.user_id, self.username)
280 except:
280 except:
281 return self.__class__.__name__
281 return self.__class__.__name__
282
282
283 @classmethod
283 @classmethod
284 def by_username(cls, username, case_insensitive=False):
284 def get_by_username(cls, username, case_insensitive=False):
285 if case_insensitive:
285 if case_insensitive:
286 return Session.query(cls).filter(cls.username.like(username)).one()
286 return Session.query(cls).filter(cls.username.like(username)).scalar()
287 else:
287 else:
288 return Session.query(cls).filter(cls.username == username).one()
288 return Session.query(cls).filter(cls.username == username).scalar()
289
289
290 @classmethod
290 @classmethod
291 def get_by_api_key(cls, api_key):
291 def get_by_api_key(cls, api_key):
292 return Session.query(cls).filter(cls.api_key == api_key).one()
292 return Session.query(cls).filter(cls.api_key == api_key).one()
293
293
294
295 def update_lastlogin(self):
294 def update_lastlogin(self):
296 """Update user lastlogin"""
295 """Update user lastlogin"""
297
296
298 self.last_login = datetime.datetime.now()
297 self.last_login = datetime.datetime.now()
299 Session.add(self)
298 Session.add(self)
300 Session.commit()
299 Session.commit()
301 log.debug('updated user %s lastlogin', self.username)
300 log.debug('updated user %s lastlogin', self.username)
302
301
303 @classmethod
302 @classmethod
304 def create(cls, form_data):
303 def create(cls, form_data):
305 from rhodecode.lib.auth import get_crypt_password
304 from rhodecode.lib.auth import get_crypt_password
306
305
307 try:
306 try:
308 new_user = cls()
307 new_user = cls()
309 for k, v in form_data.items():
308 for k, v in form_data.items():
310 if k == 'password':
309 if k == 'password':
311 v = get_crypt_password(v)
310 v = get_crypt_password(v)
312 setattr(new_user, k, v)
311 setattr(new_user, k, v)
313
312
314 new_user.api_key = generate_api_key(form_data['username'])
313 new_user.api_key = generate_api_key(form_data['username'])
315 Session.add(new_user)
314 Session.add(new_user)
316 Session.commit()
315 Session.commit()
317 return new_user
316 return new_user
318 except:
317 except:
319 log.error(traceback.format_exc())
318 log.error(traceback.format_exc())
320 Session.rollback()
319 Session.rollback()
321 raise
320 raise
322
321
323 class UserLog(Base, BaseModel):
322 class UserLog(Base, BaseModel):
324 __tablename__ = 'user_logs'
323 __tablename__ = 'user_logs'
325 __table_args__ = {'extend_existing':True}
324 __table_args__ = {'extend_existing':True}
326 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
325 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
327 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
326 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
328 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
327 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
329 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
328 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
329 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
332 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
331 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
333
332
334 @property
333 @property
335 def action_as_day(self):
334 def action_as_day(self):
336 return date(*self.action_date.timetuple()[:3])
335 return date(*self.action_date.timetuple()[:3])
337
336
338 user = relationship('User')
337 user = relationship('User')
339 repository = relationship('Repository')
338 repository = relationship('Repository')
340
339
341
340
342 class UsersGroup(Base, BaseModel):
341 class UsersGroup(Base, BaseModel):
343 __tablename__ = 'users_groups'
342 __tablename__ = 'users_groups'
344 __table_args__ = {'extend_existing':True}
343 __table_args__ = {'extend_existing':True}
345
344
346 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
345 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
347 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
346 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
348 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
347 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
349
348
350 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
349 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
351
350
352 def __repr__(self):
351 def __repr__(self):
353 return '<userGroup(%s)>' % (self.users_group_name)
352 return '<userGroup(%s)>' % (self.users_group_name)
354
353
355 @classmethod
354 @classmethod
356 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
355 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
357 if case_insensitive:
356 if case_insensitive:
358 gr = Session.query(cls)\
357 gr = Session.query(cls)\
359 .filter(cls.users_group_name.ilike(group_name))
358 .filter(cls.users_group_name.ilike(group_name))
360 else:
359 else:
361 gr = Session.query(UsersGroup)\
360 gr = Session.query(UsersGroup)\
362 .filter(UsersGroup.users_group_name == group_name)
361 .filter(UsersGroup.users_group_name == group_name)
363 if cache:
362 if cache:
364 gr = gr.options(FromCache("sql_cache_short",
363 gr = gr.options(FromCache("sql_cache_short",
365 "get_user_%s" % group_name))
364 "get_user_%s" % group_name))
366 return gr.scalar()
365 return gr.scalar()
367
366
368
367
369 @classmethod
368 @classmethod
370 def get(cls, users_group_id, cache=False):
369 def get(cls, users_group_id, cache=False):
371 users_group = Session.query(cls)
370 users_group = Session.query(cls)
372 if cache:
371 if cache:
373 users_group = users_group.options(FromCache("sql_cache_short",
372 users_group = users_group.options(FromCache("sql_cache_short",
374 "get_users_group_%s" % users_group_id))
373 "get_users_group_%s" % users_group_id))
375 return users_group.get(users_group_id)
374 return users_group.get(users_group_id)
376
375
377 @classmethod
376 @classmethod
378 def create(cls, form_data):
377 def create(cls, form_data):
379 try:
378 try:
380 new_users_group = cls()
379 new_users_group = cls()
381 for k, v in form_data.items():
380 for k, v in form_data.items():
382 setattr(new_users_group, k, v)
381 setattr(new_users_group, k, v)
383
382
384 Session.add(new_users_group)
383 Session.add(new_users_group)
385 Session.commit()
384 Session.commit()
386 return new_users_group
385 return new_users_group
387 except:
386 except:
388 log.error(traceback.format_exc())
387 log.error(traceback.format_exc())
389 Session.rollback()
388 Session.rollback()
390 raise
389 raise
391
390
392 @classmethod
391 @classmethod
393 def update(cls, users_group_id, form_data):
392 def update(cls, users_group_id, form_data):
394
393
395 try:
394 try:
396 users_group = cls.get(users_group_id, cache=False)
395 users_group = cls.get(users_group_id, cache=False)
397
396
398 for k, v in form_data.items():
397 for k, v in form_data.items():
399 if k == 'users_group_members':
398 if k == 'users_group_members':
400 users_group.members = []
399 users_group.members = []
401 Session.flush()
400 Session.flush()
402 members_list = []
401 members_list = []
403 if v:
402 if v:
404 for u_id in set(v):
403 for u_id in set(v):
405 members_list.append(UsersGroupMember(
404 members_list.append(UsersGroupMember(
406 users_group_id,
405 users_group_id,
407 u_id))
406 u_id))
408 setattr(users_group, 'members', members_list)
407 setattr(users_group, 'members', members_list)
409 setattr(users_group, k, v)
408 setattr(users_group, k, v)
410
409
411 Session.add(users_group)
410 Session.add(users_group)
412 Session.commit()
411 Session.commit()
413 except:
412 except:
414 log.error(traceback.format_exc())
413 log.error(traceback.format_exc())
415 Session.rollback()
414 Session.rollback()
416 raise
415 raise
417
416
418 @classmethod
417 @classmethod
419 def delete(cls, users_group_id):
418 def delete(cls, users_group_id):
420 try:
419 try:
421
420
422 # check if this group is not assigned to repo
421 # check if this group is not assigned to repo
423 assigned_groups = UsersGroupRepoToPerm.query()\
422 assigned_groups = UsersGroupRepoToPerm.query()\
424 .filter(UsersGroupRepoToPerm.users_group_id ==
423 .filter(UsersGroupRepoToPerm.users_group_id ==
425 users_group_id).all()
424 users_group_id).all()
426
425
427 if assigned_groups:
426 if assigned_groups:
428 raise UsersGroupsAssignedException('Group assigned to %s' %
427 raise UsersGroupsAssignedException('Group assigned to %s' %
429 assigned_groups)
428 assigned_groups)
430
429
431 users_group = cls.get(users_group_id, cache=False)
430 users_group = cls.get(users_group_id, cache=False)
432 Session.delete(users_group)
431 Session.delete(users_group)
433 Session.commit()
432 Session.commit()
434 except:
433 except:
435 log.error(traceback.format_exc())
434 log.error(traceback.format_exc())
436 Session.rollback()
435 Session.rollback()
437 raise
436 raise
438
437
439
438
440 class UsersGroupMember(Base, BaseModel):
439 class UsersGroupMember(Base, BaseModel):
441 __tablename__ = 'users_groups_members'
440 __tablename__ = 'users_groups_members'
442 __table_args__ = {'extend_existing':True}
441 __table_args__ = {'extend_existing':True}
443
442
444 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
443 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
445 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
444 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
446 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
445 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
447
446
448 user = relationship('User', lazy='joined')
447 user = relationship('User', lazy='joined')
449 users_group = relationship('UsersGroup')
448 users_group = relationship('UsersGroup')
450
449
451 def __init__(self, gr_id='', u_id=''):
450 def __init__(self, gr_id='', u_id=''):
452 self.users_group_id = gr_id
451 self.users_group_id = gr_id
453 self.user_id = u_id
452 self.user_id = u_id
454
453
455 class Repository(Base, BaseModel):
454 class Repository(Base, BaseModel):
456 __tablename__ = 'repositories'
455 __tablename__ = 'repositories'
457 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
456 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
458
457
459 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
458 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
460 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
459 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
461 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
460 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
462 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
461 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
463 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
462 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
464 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
463 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
465 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
464 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
466 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
465 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
467 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
466 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
468 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
467 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
469
468
470 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
469 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
471 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
470 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
472
471
473
472
474 user = relationship('User')
473 user = relationship('User')
475 fork = relationship('Repository', remote_side=repo_id)
474 fork = relationship('Repository', remote_side=repo_id)
476 group = relationship('Group')
475 group = relationship('Group')
477 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
476 repo_to_perm = relationship('RepoToPerm', cascade='all', order_by='RepoToPerm.repo_to_perm_id')
478 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
477 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
479 stats = relationship('Statistics', cascade='all', uselist=False)
478 stats = relationship('Statistics', cascade='all', uselist=False)
480
479
481 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
480 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
482
481
483 logs = relationship('UserLog', cascade='all')
482 logs = relationship('UserLog', cascade='all')
484
483
485 def __repr__(self):
484 def __repr__(self):
486 return "<%s('%s:%s')>" % (self.__class__.__name__,
485 return "<%s('%s:%s')>" % (self.__class__.__name__,
487 self.repo_id, self.repo_name)
486 self.repo_id, self.repo_name)
488
487
489 @classmethod
488 @classmethod
490 def by_repo_name(cls, repo_name):
489 def get_by_repo_name(cls, repo_name):
491 q = Session.query(cls).filter(cls.repo_name == repo_name)
490 q = Session.query(cls).filter(cls.repo_name == repo_name)
492
491
493 q = q.options(joinedload(Repository.fork))\
492 q = q.options(joinedload(Repository.fork))\
494 .options(joinedload(Repository.user))\
493 .options(joinedload(Repository.user))\
495 .options(joinedload(Repository.group))\
494 .options(joinedload(Repository.group))\
496
495
497 return q.one()
496 return q.one()
498
497
499 @classmethod
498 @classmethod
500 def get_repo_forks(cls, repo_id):
499 def get_repo_forks(cls, repo_id):
501 return Session.query(cls).filter(Repository.fork_id == repo_id)
500 return Session.query(cls).filter(Repository.fork_id == repo_id)
502
501
503 @classmethod
502 @classmethod
504 def base_path(cls):
503 def base_path(cls):
505 """
504 """
506 Returns base path when all repos are stored
505 Returns base path when all repos are stored
507
506
508 :param cls:
507 :param cls:
509 """
508 """
510 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
509 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
511 q.options(FromCache("sql_cache_short", "repository_repo_path"))
510 q.options(FromCache("sql_cache_short", "repository_repo_path"))
512 return q.one().ui_value
511 return q.one().ui_value
513
512
514 @property
513 @property
515 def just_name(self):
514 def just_name(self):
516 return self.repo_name.split(os.sep)[-1]
515 return self.repo_name.split(os.sep)[-1]
517
516
518 @property
517 @property
519 def groups_with_parents(self):
518 def groups_with_parents(self):
520 groups = []
519 groups = []
521 if self.group is None:
520 if self.group is None:
522 return groups
521 return groups
523
522
524 cur_gr = self.group
523 cur_gr = self.group
525 groups.insert(0, cur_gr)
524 groups.insert(0, cur_gr)
526 while 1:
525 while 1:
527 gr = getattr(cur_gr, 'parent_group', None)
526 gr = getattr(cur_gr, 'parent_group', None)
528 cur_gr = cur_gr.parent_group
527 cur_gr = cur_gr.parent_group
529 if gr is None:
528 if gr is None:
530 break
529 break
531 groups.insert(0, gr)
530 groups.insert(0, gr)
532
531
533 return groups
532 return groups
534
533
535 @property
534 @property
536 def groups_and_repo(self):
535 def groups_and_repo(self):
537 return self.groups_with_parents, self.just_name
536 return self.groups_with_parents, self.just_name
538
537
539 @LazyProperty
538 @LazyProperty
540 def repo_path(self):
539 def repo_path(self):
541 """
540 """
542 Returns base full path for that repository means where it actually
541 Returns base full path for that repository means where it actually
543 exists on a filesystem
542 exists on a filesystem
544 """
543 """
545 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
544 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
546 q.options(FromCache("sql_cache_short", "repository_repo_path"))
545 q.options(FromCache("sql_cache_short", "repository_repo_path"))
547 return q.one().ui_value
546 return q.one().ui_value
548
547
549 @property
548 @property
550 def repo_full_path(self):
549 def repo_full_path(self):
551 p = [self.repo_path]
550 p = [self.repo_path]
552 # we need to split the name by / since this is how we store the
551 # we need to split the name by / since this is how we store the
553 # names in the database, but that eventually needs to be converted
552 # names in the database, but that eventually needs to be converted
554 # into a valid system path
553 # into a valid system path
555 p += self.repo_name.split('/')
554 p += self.repo_name.split('/')
556 return os.path.join(*p)
555 return os.path.join(*p)
557
556
558 @property
557 @property
559 def _ui(self):
558 def _ui(self):
560 """
559 """
561 Creates an db based ui object for this repository
560 Creates an db based ui object for this repository
562 """
561 """
563 from mercurial import ui
562 from mercurial import ui
564 from mercurial import config
563 from mercurial import config
565 baseui = ui.ui()
564 baseui = ui.ui()
566
565
567 #clean the baseui object
566 #clean the baseui object
568 baseui._ocfg = config.config()
567 baseui._ocfg = config.config()
569 baseui._ucfg = config.config()
568 baseui._ucfg = config.config()
570 baseui._tcfg = config.config()
569 baseui._tcfg = config.config()
571
570
572
571
573 ret = Session.query(RhodeCodeUi)\
572 ret = Session.query(RhodeCodeUi)\
574 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
573 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
575
574
576 hg_ui = ret
575 hg_ui = ret
577 for ui_ in hg_ui:
576 for ui_ in hg_ui:
578 if ui_.ui_active:
577 if ui_.ui_active:
579 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
578 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
580 ui_.ui_key, ui_.ui_value)
579 ui_.ui_key, ui_.ui_value)
581 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
580 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
582
581
583 return baseui
582 return baseui
584
583
585 @classmethod
584 @classmethod
586 def is_valid(cls, repo_name):
585 def is_valid(cls, repo_name):
587 """
586 """
588 returns True if given repo name is a valid filesystem repository
587 returns True if given repo name is a valid filesystem repository
589
588
590 @param cls:
589 @param cls:
591 @param repo_name:
590 @param repo_name:
592 """
591 """
593 from rhodecode.lib.utils import is_valid_repo
592 from rhodecode.lib.utils import is_valid_repo
594
593
595 return is_valid_repo(repo_name, cls.base_path())
594 return is_valid_repo(repo_name, cls.base_path())
596
595
597
596
598 #==========================================================================
597 #==========================================================================
599 # SCM PROPERTIES
598 # SCM PROPERTIES
600 #==========================================================================
599 #==========================================================================
601
600
602 def get_changeset(self, rev):
601 def get_changeset(self, rev):
603 return get_changeset_safe(self.scm_instance, rev)
602 return get_changeset_safe(self.scm_instance, rev)
604
603
605 @property
604 @property
606 def tip(self):
605 def tip(self):
607 return self.get_changeset('tip')
606 return self.get_changeset('tip')
608
607
609 @property
608 @property
610 def author(self):
609 def author(self):
611 return self.tip.author
610 return self.tip.author
612
611
613 @property
612 @property
614 def last_change(self):
613 def last_change(self):
615 return self.scm_instance.last_change
614 return self.scm_instance.last_change
616
615
617 #==========================================================================
616 #==========================================================================
618 # SCM CACHE INSTANCE
617 # SCM CACHE INSTANCE
619 #==========================================================================
618 #==========================================================================
620
619
621 @property
620 @property
622 def invalidate(self):
621 def invalidate(self):
623 """
622 """
624 Returns Invalidation object if this repo should be invalidated
623 Returns Invalidation object if this repo should be invalidated
625 None otherwise. `cache_active = False` means that this cache
624 None otherwise. `cache_active = False` means that this cache
626 state is not valid and needs to be invalidated
625 state is not valid and needs to be invalidated
627 """
626 """
628 return Session.query(CacheInvalidation)\
627 return Session.query(CacheInvalidation)\
629 .filter(CacheInvalidation.cache_key == self.repo_name)\
628 .filter(CacheInvalidation.cache_key == self.repo_name)\
630 .filter(CacheInvalidation.cache_active == False)\
629 .filter(CacheInvalidation.cache_active == False)\
631 .scalar()
630 .scalar()
632
631
633 def set_invalidate(self):
632 def set_invalidate(self):
634 """
633 """
635 set a cache for invalidation for this instance
634 set a cache for invalidation for this instance
636 """
635 """
637 inv = Session.query(CacheInvalidation)\
636 inv = Session.query(CacheInvalidation)\
638 .filter(CacheInvalidation.cache_key == self.repo_name)\
637 .filter(CacheInvalidation.cache_key == self.repo_name)\
639 .scalar()
638 .scalar()
640
639
641 if inv is None:
640 if inv is None:
642 inv = CacheInvalidation(self.repo_name)
641 inv = CacheInvalidation(self.repo_name)
643 inv.cache_active = True
642 inv.cache_active = True
644 Session.add(inv)
643 Session.add(inv)
645 Session.commit()
644 Session.commit()
646
645
647 @LazyProperty
646 @LazyProperty
648 def scm_instance(self):
647 def scm_instance(self):
649 return self.__get_instance()
648 return self.__get_instance()
650
649
651 @property
650 @property
652 def scm_instance_cached(self):
651 def scm_instance_cached(self):
653 @cache_region('long_term')
652 @cache_region('long_term')
654 def _c(repo_name):
653 def _c(repo_name):
655 return self.__get_instance()
654 return self.__get_instance()
656
655
657 # TODO: remove this trick when beaker 1.6 is released
656 # TODO: remove this trick when beaker 1.6 is released
658 # and have fixed this issue with not supporting unicode keys
657 # and have fixed this issue with not supporting unicode keys
659 rn = safe_str(self.repo_name)
658 rn = safe_str(self.repo_name)
660
659
661 inv = self.invalidate
660 inv = self.invalidate
662 if inv is not None:
661 if inv is not None:
663 region_invalidate(_c, None, rn)
662 region_invalidate(_c, None, rn)
664 # update our cache
663 # update our cache
665 inv.cache_active = True
664 inv.cache_active = True
666 Session.add(inv)
665 Session.add(inv)
667 Session.commit()
666 Session.commit()
668
667
669 return _c(rn)
668 return _c(rn)
670
669
671 def __get_instance(self):
670 def __get_instance(self):
672
671
673 repo_full_path = self.repo_full_path
672 repo_full_path = self.repo_full_path
674
673
675 try:
674 try:
676 alias = get_scm(repo_full_path)[0]
675 alias = get_scm(repo_full_path)[0]
677 log.debug('Creating instance of %s repository', alias)
676 log.debug('Creating instance of %s repository', alias)
678 backend = get_backend(alias)
677 backend = get_backend(alias)
679 except VCSError:
678 except VCSError:
680 log.error(traceback.format_exc())
679 log.error(traceback.format_exc())
681 log.error('Perhaps this repository is in db and not in '
680 log.error('Perhaps this repository is in db and not in '
682 'filesystem run rescan repositories with '
681 'filesystem run rescan repositories with '
683 '"destroy old data " option from admin panel')
682 '"destroy old data " option from admin panel')
684 return
683 return
685
684
686 if alias == 'hg':
685 if alias == 'hg':
687
686
688 repo = backend(safe_str(repo_full_path), create=False,
687 repo = backend(safe_str(repo_full_path), create=False,
689 baseui=self._ui)
688 baseui=self._ui)
690 #skip hidden web repository
689 #skip hidden web repository
691 if repo._get_hidden():
690 if repo._get_hidden():
692 return
691 return
693 else:
692 else:
694 repo = backend(repo_full_path, create=False)
693 repo = backend(repo_full_path, create=False)
695
694
696 return repo
695 return repo
697
696
698
697
699 class Group(Base, BaseModel):
698 class Group(Base, BaseModel):
700 __tablename__ = 'groups'
699 __tablename__ = 'groups'
701 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
700 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
702 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
701 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
703 __mapper_args__ = {'order_by':'group_name'}
702 __mapper_args__ = {'order_by':'group_name'}
704
703
705 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
704 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
706 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
705 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
707 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
706 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
708 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
707 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
709
708
710 parent_group = relationship('Group', remote_side=group_id)
709 parent_group = relationship('Group', remote_side=group_id)
711
710
712
711
713 def __init__(self, group_name='', parent_group=None):
712 def __init__(self, group_name='', parent_group=None):
714 self.group_name = group_name
713 self.group_name = group_name
715 self.parent_group = parent_group
714 self.parent_group = parent_group
716
715
717 def __repr__(self):
716 def __repr__(self):
718 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
717 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
719 self.group_name)
718 self.group_name)
720
719
721 @classmethod
720 @classmethod
722 def url_sep(cls):
721 def url_sep(cls):
723 return '/'
722 return '/'
724
723
725 @property
724 @property
726 def parents(self):
725 def parents(self):
727 parents_recursion_limit = 5
726 parents_recursion_limit = 5
728 groups = []
727 groups = []
729 if self.parent_group is None:
728 if self.parent_group is None:
730 return groups
729 return groups
731 cur_gr = self.parent_group
730 cur_gr = self.parent_group
732 groups.insert(0, cur_gr)
731 groups.insert(0, cur_gr)
733 cnt = 0
732 cnt = 0
734 while 1:
733 while 1:
735 cnt += 1
734 cnt += 1
736 gr = getattr(cur_gr, 'parent_group', None)
735 gr = getattr(cur_gr, 'parent_group', None)
737 cur_gr = cur_gr.parent_group
736 cur_gr = cur_gr.parent_group
738 if gr is None:
737 if gr is None:
739 break
738 break
740 if cnt == parents_recursion_limit:
739 if cnt == parents_recursion_limit:
741 # this will prevent accidental infinit loops
740 # this will prevent accidental infinit loops
742 log.error('group nested more than %s' %
741 log.error('group nested more than %s' %
743 parents_recursion_limit)
742 parents_recursion_limit)
744 break
743 break
745
744
746 groups.insert(0, gr)
745 groups.insert(0, gr)
747 return groups
746 return groups
748
747
749 @property
748 @property
750 def children(self):
749 def children(self):
751 return Session.query(Group).filter(Group.parent_group == self)
750 return Session.query(Group).filter(Group.parent_group == self)
752
751
753 @property
752 @property
754 def full_path(self):
753 def full_path(self):
755 return Group.url_sep().join([g.group_name for g in self.parents] +
754 return Group.url_sep().join([g.group_name for g in self.parents] +
756 [self.group_name])
755 [self.group_name])
757
756
758 @property
757 @property
759 def repositories(self):
758 def repositories(self):
760 return Session.query(Repository).filter(Repository.group == self)
759 return Session.query(Repository).filter(Repository.group == self)
761
760
762 @property
761 @property
763 def repositories_recursive_count(self):
762 def repositories_recursive_count(self):
764 cnt = self.repositories.count()
763 cnt = self.repositories.count()
765
764
766 def children_count(group):
765 def children_count(group):
767 cnt = 0
766 cnt = 0
768 for child in group.children:
767 for child in group.children:
769 cnt += child.repositories.count()
768 cnt += child.repositories.count()
770 cnt += children_count(child)
769 cnt += children_count(child)
771 return cnt
770 return cnt
772
771
773 return cnt + children_count(self)
772 return cnt + children_count(self)
774
773
775 class Permission(Base, BaseModel):
774 class Permission(Base, BaseModel):
776 __tablename__ = 'permissions'
775 __tablename__ = 'permissions'
777 __table_args__ = {'extend_existing':True}
776 __table_args__ = {'extend_existing':True}
778 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
777 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
779 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
778 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
780 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
779 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
781
780
782 def __repr__(self):
781 def __repr__(self):
783 return "<%s('%s:%s')>" % (self.__class__.__name__,
782 return "<%s('%s:%s')>" % (self.__class__.__name__,
784 self.permission_id, self.permission_name)
783 self.permission_id, self.permission_name)
785
784
786 @classmethod
785 @classmethod
787 def get_by_key(cls, key):
786 def get_by_key(cls, key):
788 return Session.query(cls).filter(cls.permission_name == key).scalar()
787 return Session.query(cls).filter(cls.permission_name == key).scalar()
789
788
790 class RepoToPerm(Base, BaseModel):
789 class RepoToPerm(Base, BaseModel):
791 __tablename__ = 'repo_to_perm'
790 __tablename__ = 'repo_to_perm'
792 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
791 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
793 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
792 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
794 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
793 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
795 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
794 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
796 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
795 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
797
796
798 user = relationship('User')
797 user = relationship('User')
799 permission = relationship('Permission')
798 permission = relationship('Permission')
800 repository = relationship('Repository')
799 repository = relationship('Repository')
801
800
802 class UserToPerm(Base, BaseModel):
801 class UserToPerm(Base, BaseModel):
803 __tablename__ = 'user_to_perm'
802 __tablename__ = 'user_to_perm'
804 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
803 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
805 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
804 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
806 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
805 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
807 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
806 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
808
807
809 user = relationship('User')
808 user = relationship('User')
810 permission = relationship('Permission')
809 permission = relationship('Permission')
811
810
812 @classmethod
811 @classmethod
813 def has_perm(cls, user_id, perm):
812 def has_perm(cls, user_id, perm):
814 if not isinstance(perm, Permission):
813 if not isinstance(perm, Permission):
815 raise Exception('perm needs to be an instance of Permission class')
814 raise Exception('perm needs to be an instance of Permission class')
816
815
817 return Session.query(cls).filter(cls.user_id == user_id)\
816 return Session.query(cls).filter(cls.user_id == user_id)\
818 .filter(cls.permission == perm).scalar() is not None
817 .filter(cls.permission == perm).scalar() is not None
819
818
820 @classmethod
819 @classmethod
821 def grant_perm(cls, user_id, perm):
820 def grant_perm(cls, user_id, perm):
822 if not isinstance(perm, Permission):
821 if not isinstance(perm, Permission):
823 raise Exception('perm needs to be an instance of Permission class')
822 raise Exception('perm needs to be an instance of Permission class')
824
823
825 new = cls()
824 new = cls()
826 new.user_id = user_id
825 new.user_id = user_id
827 new.permission = perm
826 new.permission = perm
828 try:
827 try:
829 Session.add(new)
828 Session.add(new)
830 Session.commit()
829 Session.commit()
831 except:
830 except:
832 Session.rollback()
831 Session.rollback()
833
832
834
833
835 @classmethod
834 @classmethod
836 def revoke_perm(cls, user_id, perm):
835 def revoke_perm(cls, user_id, perm):
837 if not isinstance(perm, Permission):
836 if not isinstance(perm, Permission):
838 raise Exception('perm needs to be an instance of Permission class')
837 raise Exception('perm needs to be an instance of Permission class')
839
838
840 try:
839 try:
841 Session.query(cls).filter(cls.user_id == user_id)\
840 Session.query(cls).filter(cls.user_id == user_id)\
842 .filter(cls.permission == perm).delete()
841 .filter(cls.permission == perm).delete()
843 Session.commit()
842 Session.commit()
844 except:
843 except:
845 Session.rollback()
844 Session.rollback()
846
845
847 class UsersGroupRepoToPerm(Base, BaseModel):
846 class UsersGroupRepoToPerm(Base, BaseModel):
848 __tablename__ = 'users_group_repo_to_perm'
847 __tablename__ = 'users_group_repo_to_perm'
849 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
848 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
850 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
849 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
851 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
850 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
852 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
851 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
853 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
852 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
854
853
855 users_group = relationship('UsersGroup')
854 users_group = relationship('UsersGroup')
856 permission = relationship('Permission')
855 permission = relationship('Permission')
857 repository = relationship('Repository')
856 repository = relationship('Repository')
858
857
859 def __repr__(self):
858 def __repr__(self):
860 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
859 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
861
860
862 class UsersGroupToPerm(Base, BaseModel):
861 class UsersGroupToPerm(Base, BaseModel):
863 __tablename__ = 'users_group_to_perm'
862 __tablename__ = 'users_group_to_perm'
864 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
863 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
865 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
864 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
866 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
865 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
867
866
868 users_group = relationship('UsersGroup')
867 users_group = relationship('UsersGroup')
869 permission = relationship('Permission')
868 permission = relationship('Permission')
870
869
871
870
872 @classmethod
871 @classmethod
873 def has_perm(cls, users_group_id, perm):
872 def has_perm(cls, users_group_id, perm):
874 if not isinstance(perm, Permission):
873 if not isinstance(perm, Permission):
875 raise Exception('perm needs to be an instance of Permission class')
874 raise Exception('perm needs to be an instance of Permission class')
876
875
877 return Session.query(cls).filter(cls.users_group_id ==
876 return Session.query(cls).filter(cls.users_group_id ==
878 users_group_id)\
877 users_group_id)\
879 .filter(cls.permission == perm)\
878 .filter(cls.permission == perm)\
880 .scalar() is not None
879 .scalar() is not None
881
880
882 @classmethod
881 @classmethod
883 def grant_perm(cls, users_group_id, perm):
882 def grant_perm(cls, users_group_id, perm):
884 if not isinstance(perm, Permission):
883 if not isinstance(perm, Permission):
885 raise Exception('perm needs to be an instance of Permission class')
884 raise Exception('perm needs to be an instance of Permission class')
886
885
887 new = cls()
886 new = cls()
888 new.users_group_id = users_group_id
887 new.users_group_id = users_group_id
889 new.permission = perm
888 new.permission = perm
890 try:
889 try:
891 Session.add(new)
890 Session.add(new)
892 Session.commit()
891 Session.commit()
893 except:
892 except:
894 Session.rollback()
893 Session.rollback()
895
894
896
895
897 @classmethod
896 @classmethod
898 def revoke_perm(cls, users_group_id, perm):
897 def revoke_perm(cls, users_group_id, perm):
899 if not isinstance(perm, Permission):
898 if not isinstance(perm, Permission):
900 raise Exception('perm needs to be an instance of Permission class')
899 raise Exception('perm needs to be an instance of Permission class')
901
900
902 try:
901 try:
903 Session.query(cls).filter(cls.users_group_id == users_group_id)\
902 Session.query(cls).filter(cls.users_group_id == users_group_id)\
904 .filter(cls.permission == perm).delete()
903 .filter(cls.permission == perm).delete()
905 Session.commit()
904 Session.commit()
906 except:
905 except:
907 Session.rollback()
906 Session.rollback()
908
907
909
908
910 class GroupToPerm(Base, BaseModel):
909 class GroupToPerm(Base, BaseModel):
911 __tablename__ = 'group_to_perm'
910 __tablename__ = 'group_to_perm'
912 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
911 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
913
912
914 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
913 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
915 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
914 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
916 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
915 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
917 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
916 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
918
917
919 user = relationship('User')
918 user = relationship('User')
920 permission = relationship('Permission')
919 permission = relationship('Permission')
921 group = relationship('Group')
920 group = relationship('Group')
922
921
923 class Statistics(Base, BaseModel):
922 class Statistics(Base, BaseModel):
924 __tablename__ = 'statistics'
923 __tablename__ = 'statistics'
925 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
924 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
926 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
925 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
927 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
926 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
928 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
927 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
929 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
928 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
930 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
929 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
931 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
930 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
932
931
933 repository = relationship('Repository', single_parent=True)
932 repository = relationship('Repository', single_parent=True)
934
933
935 class UserFollowing(Base, BaseModel):
934 class UserFollowing(Base, BaseModel):
936 __tablename__ = 'user_followings'
935 __tablename__ = 'user_followings'
937 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
936 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
938 UniqueConstraint('user_id', 'follows_user_id')
937 UniqueConstraint('user_id', 'follows_user_id')
939 , {'extend_existing':True})
938 , {'extend_existing':True})
940
939
941 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
940 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
942 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
941 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
943 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
942 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
944 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
943 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
945 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
944 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
946
945
947 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
946 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
948
947
949 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
948 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
950 follows_repository = relationship('Repository', order_by='Repository.repo_name')
949 follows_repository = relationship('Repository', order_by='Repository.repo_name')
951
950
952
951
953 @classmethod
952 @classmethod
954 def get_repo_followers(cls, repo_id):
953 def get_repo_followers(cls, repo_id):
955 return Session.query(cls).filter(cls.follows_repo_id == repo_id)
954 return Session.query(cls).filter(cls.follows_repo_id == repo_id)
956
955
957 class CacheInvalidation(Base, BaseModel):
956 class CacheInvalidation(Base, BaseModel):
958 __tablename__ = 'cache_invalidation'
957 __tablename__ = 'cache_invalidation'
959 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
958 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
960 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
959 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
961 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
960 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
962 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
961 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
963 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
962 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
964
963
965
964
966 def __init__(self, cache_key, cache_args=''):
965 def __init__(self, cache_key, cache_args=''):
967 self.cache_key = cache_key
966 self.cache_key = cache_key
968 self.cache_args = cache_args
967 self.cache_args = cache_args
969 self.cache_active = False
968 self.cache_active = False
970
969
971 def __repr__(self):
970 def __repr__(self):
972 return "<%s('%s:%s')>" % (self.__class__.__name__,
971 return "<%s('%s:%s')>" % (self.__class__.__name__,
973 self.cache_id, self.cache_key)
972 self.cache_id, self.cache_key)
974
973
975 class DbMigrateVersion(Base, BaseModel):
974 class DbMigrateVersion(Base, BaseModel):
976 __tablename__ = 'db_migrate_version'
975 __tablename__ = 'db_migrate_version'
977 __table_args__ = {'extend_existing':True}
976 __table_args__ = {'extend_existing':True}
978 repository_id = Column('repository_id', String(250), primary_key=True)
977 repository_id = Column('repository_id', String(250), primary_key=True)
979 repository_path = Column('repository_path', Text)
978 repository_path = Column('repository_path', Text)
980 version = Column('version', Integer)
979 version = Column('version', Integer)
@@ -1,681 +1,680 b''
1 """ this is forms validation classes
1 """ this is forms validation classes
2 http://formencode.org/module-formencode.validators.html
2 http://formencode.org/module-formencode.validators.html
3 for list off all availible validators
3 for list off all availible validators
4
4
5 we can create our own validators
5 we can create our own validators
6
6
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 pre_validators [] These validators will be applied before the schema
8 pre_validators [] These validators will be applied before the schema
9 chained_validators [] These validators will be applied after the schema
9 chained_validators [] These validators will be applied after the schema
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14
14
15
15
16 <name> = formencode.validators.<name of validator>
16 <name> = formencode.validators.<name of validator>
17 <name> must equal form name
17 <name> must equal form name
18 list=[1,2,3,4,5]
18 list=[1,2,3,4,5]
19 for SELECT use formencode.All(OneOf(list), Int())
19 for SELECT use formencode.All(OneOf(list), Int())
20
20
21 """
21 """
22 import os
22 import os
23 import re
23 import re
24 import logging
24 import logging
25 import traceback
25 import traceback
26
26
27 import formencode
27 import formencode
28 from formencode import All
28 from formencode import All
29 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
29 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
30 Email, Bool, StringBoolean, Set
30 Email, Bool, StringBoolean, Set
31
31
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33 from webhelpers.pylonslib.secure_form import authentication_token
33 from webhelpers.pylonslib.secure_form import authentication_token
34
34
35 from rhodecode.lib.utils import repo_name_slug
35 from rhodecode.lib.utils import repo_name_slug
36 from rhodecode.lib.auth import authenticate, get_crypt_password
36 from rhodecode.lib.auth import authenticate, get_crypt_password
37 from rhodecode.lib.exceptions import LdapImportError
37 from rhodecode.lib.exceptions import LdapImportError
38 from rhodecode.model.user import UserModel
38 from rhodecode.model.user import UserModel
39 from rhodecode.model.repo import RepoModel
39 from rhodecode.model.repo import RepoModel
40 from rhodecode.model.db import User, UsersGroup, Group
40 from rhodecode.model.db import User, UsersGroup, Group
41 from rhodecode import BACKENDS
41 from rhodecode import BACKENDS
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45 #this is needed to translate the messages using _() in validators
45 #this is needed to translate the messages using _() in validators
46 class State_obj(object):
46 class State_obj(object):
47 _ = staticmethod(_)
47 _ = staticmethod(_)
48
48
49 #==============================================================================
49 #==============================================================================
50 # VALIDATORS
50 # VALIDATORS
51 #==============================================================================
51 #==============================================================================
52 class ValidAuthToken(formencode.validators.FancyValidator):
52 class ValidAuthToken(formencode.validators.FancyValidator):
53 messages = {'invalid_token':_('Token mismatch')}
53 messages = {'invalid_token':_('Token mismatch')}
54
54
55 def validate_python(self, value, state):
55 def validate_python(self, value, state):
56
56
57 if value != authentication_token():
57 if value != authentication_token():
58 raise formencode.Invalid(self.message('invalid_token', state,
58 raise formencode.Invalid(self.message('invalid_token', state,
59 search_number=value), value, state)
59 search_number=value), value, state)
60
60
61 def ValidUsername(edit, old_data):
61 def ValidUsername(edit, old_data):
62 class _ValidUsername(formencode.validators.FancyValidator):
62 class _ValidUsername(formencode.validators.FancyValidator):
63
63
64 def validate_python(self, value, state):
64 def validate_python(self, value, state):
65 if value in ['default', 'new_user']:
65 if value in ['default', 'new_user']:
66 raise formencode.Invalid(_('Invalid username'), value, state)
66 raise formencode.Invalid(_('Invalid username'), value, state)
67 #check if user is unique
67 #check if user is unique
68 old_un = None
68 old_un = None
69 if edit:
69 if edit:
70 old_un = UserModel().get(old_data.get('user_id')).username
70 old_un = UserModel().get(old_data.get('user_id')).username
71
71
72 if old_un != value or not edit:
72 if old_un != value or not edit:
73 if UserModel().get_by_username(value, cache=False,
73 if User.get_by_username(value, case_insensitive=True):
74 case_insensitive=True):
75 raise formencode.Invalid(_('This username already '
74 raise formencode.Invalid(_('This username already '
76 'exists') , value, state)
75 'exists') , value, state)
77
76
78 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
77 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
79 raise formencode.Invalid(_('Username may only contain '
78 raise formencode.Invalid(_('Username may only contain '
80 'alphanumeric characters '
79 'alphanumeric characters '
81 'underscores, periods or dashes '
80 'underscores, periods or dashes '
82 'and must begin with alphanumeric '
81 'and must begin with alphanumeric '
83 'character'), value, state)
82 'character'), value, state)
84
83
85 return _ValidUsername
84 return _ValidUsername
86
85
87
86
88 def ValidUsersGroup(edit, old_data):
87 def ValidUsersGroup(edit, old_data):
89
88
90 class _ValidUsersGroup(formencode.validators.FancyValidator):
89 class _ValidUsersGroup(formencode.validators.FancyValidator):
91
90
92 def validate_python(self, value, state):
91 def validate_python(self, value, state):
93 if value in ['default']:
92 if value in ['default']:
94 raise formencode.Invalid(_('Invalid group name'), value, state)
93 raise formencode.Invalid(_('Invalid group name'), value, state)
95 #check if group is unique
94 #check if group is unique
96 old_ugname = None
95 old_ugname = None
97 if edit:
96 if edit:
98 old_ugname = UsersGroup.get(
97 old_ugname = UsersGroup.get(
99 old_data.get('users_group_id')).users_group_name
98 old_data.get('users_group_id')).users_group_name
100
99
101 if old_ugname != value or not edit:
100 if old_ugname != value or not edit:
102 if UsersGroup.get_by_group_name(value, cache=False,
101 if UsersGroup.get_by_group_name(value, cache=False,
103 case_insensitive=True):
102 case_insensitive=True):
104 raise formencode.Invalid(_('This users group '
103 raise formencode.Invalid(_('This users group '
105 'already exists') , value,
104 'already exists') , value,
106 state)
105 state)
107
106
108
107
109 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
108 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
110 raise formencode.Invalid(_('Group name may only contain '
109 raise formencode.Invalid(_('Group name may only contain '
111 'alphanumeric characters '
110 'alphanumeric characters '
112 'underscores, periods or dashes '
111 'underscores, periods or dashes '
113 'and must begin with alphanumeric '
112 'and must begin with alphanumeric '
114 'character'), value, state)
113 'character'), value, state)
115
114
116 return _ValidUsersGroup
115 return _ValidUsersGroup
117
116
118
117
119 def ValidReposGroup(edit, old_data):
118 def ValidReposGroup(edit, old_data):
120 class _ValidReposGroup(formencode.validators.FancyValidator):
119 class _ValidReposGroup(formencode.validators.FancyValidator):
121
120
122 def validate_python(self, value, state):
121 def validate_python(self, value, state):
123 #TODO WRITE VALIDATIONS
122 #TODO WRITE VALIDATIONS
124 group_name = value.get('group_name')
123 group_name = value.get('group_name')
125 group_parent_id = int(value.get('group_parent_id') or - 1)
124 group_parent_id = int(value.get('group_parent_id') or - 1)
126
125
127 # slugify repo group just in case :)
126 # slugify repo group just in case :)
128 slug = repo_name_slug(group_name)
127 slug = repo_name_slug(group_name)
129
128
130 # check for parent of self
129 # check for parent of self
131 if edit and old_data['group_id'] == group_parent_id:
130 if edit and old_data['group_id'] == group_parent_id:
132 e_dict = {'group_parent_id':_('Cannot assign this group '
131 e_dict = {'group_parent_id':_('Cannot assign this group '
133 'as parent')}
132 'as parent')}
134 raise formencode.Invalid('', value, state,
133 raise formencode.Invalid('', value, state,
135 error_dict=e_dict)
134 error_dict=e_dict)
136
135
137 old_gname = None
136 old_gname = None
138 if edit:
137 if edit:
139 old_gname = Group.get(
138 old_gname = Group.get(
140 old_data.get('group_id')).group_name
139 old_data.get('group_id')).group_name
141
140
142 if old_gname != group_name or not edit:
141 if old_gname != group_name or not edit:
143 # check filesystem
142 # check filesystem
144 gr = Group.query().filter(Group.group_name == slug)\
143 gr = Group.query().filter(Group.group_name == slug)\
145 .filter(Group.group_parent_id == group_parent_id).scalar()
144 .filter(Group.group_parent_id == group_parent_id).scalar()
146
145
147 if gr:
146 if gr:
148 e_dict = {'group_name':_('This group already exists')}
147 e_dict = {'group_name':_('This group already exists')}
149 raise formencode.Invalid('', value, state,
148 raise formencode.Invalid('', value, state,
150 error_dict=e_dict)
149 error_dict=e_dict)
151
150
152 return _ValidReposGroup
151 return _ValidReposGroup
153
152
154 class ValidPassword(formencode.validators.FancyValidator):
153 class ValidPassword(formencode.validators.FancyValidator):
155
154
156 def to_python(self, value, state):
155 def to_python(self, value, state):
157
156
158 if value:
157 if value:
159
158
160 if value.get('password'):
159 if value.get('password'):
161 try:
160 try:
162 value['password'] = get_crypt_password(value['password'])
161 value['password'] = get_crypt_password(value['password'])
163 except UnicodeEncodeError:
162 except UnicodeEncodeError:
164 e_dict = {'password':_('Invalid characters in password')}
163 e_dict = {'password':_('Invalid characters in password')}
165 raise formencode.Invalid('', value, state, error_dict=e_dict)
164 raise formencode.Invalid('', value, state, error_dict=e_dict)
166
165
167 if value.get('password_confirmation'):
166 if value.get('password_confirmation'):
168 try:
167 try:
169 value['password_confirmation'] = \
168 value['password_confirmation'] = \
170 get_crypt_password(value['password_confirmation'])
169 get_crypt_password(value['password_confirmation'])
171 except UnicodeEncodeError:
170 except UnicodeEncodeError:
172 e_dict = {'password_confirmation':_('Invalid characters in password')}
171 e_dict = {'password_confirmation':_('Invalid characters in password')}
173 raise formencode.Invalid('', value, state, error_dict=e_dict)
172 raise formencode.Invalid('', value, state, error_dict=e_dict)
174
173
175 if value.get('new_password'):
174 if value.get('new_password'):
176 try:
175 try:
177 value['new_password'] = \
176 value['new_password'] = \
178 get_crypt_password(value['new_password'])
177 get_crypt_password(value['new_password'])
179 except UnicodeEncodeError:
178 except UnicodeEncodeError:
180 e_dict = {'new_password':_('Invalid characters in password')}
179 e_dict = {'new_password':_('Invalid characters in password')}
181 raise formencode.Invalid('', value, state, error_dict=e_dict)
180 raise formencode.Invalid('', value, state, error_dict=e_dict)
182
181
183 return value
182 return value
184
183
185 class ValidPasswordsMatch(formencode.validators.FancyValidator):
184 class ValidPasswordsMatch(formencode.validators.FancyValidator):
186
185
187 def validate_python(self, value, state):
186 def validate_python(self, value, state):
188
187
189 if value['password'] != value['password_confirmation']:
188 if value['password'] != value['password_confirmation']:
190 e_dict = {'password_confirmation':
189 e_dict = {'password_confirmation':
191 _('Passwords do not match')}
190 _('Passwords do not match')}
192 raise formencode.Invalid('', value, state, error_dict=e_dict)
191 raise formencode.Invalid('', value, state, error_dict=e_dict)
193
192
194 class ValidAuth(formencode.validators.FancyValidator):
193 class ValidAuth(formencode.validators.FancyValidator):
195 messages = {
194 messages = {
196 'invalid_password':_('invalid password'),
195 'invalid_password':_('invalid password'),
197 'invalid_login':_('invalid user name'),
196 'invalid_login':_('invalid user name'),
198 'disabled_account':_('Your account is disabled')
197 'disabled_account':_('Your account is disabled')
199
198
200 }
199 }
201 #error mapping
200 #error mapping
202 e_dict = {'username':messages['invalid_login'],
201 e_dict = {'username':messages['invalid_login'],
203 'password':messages['invalid_password']}
202 'password':messages['invalid_password']}
204 e_dict_disable = {'username':messages['disabled_account']}
203 e_dict_disable = {'username':messages['disabled_account']}
205
204
206 def validate_python(self, value, state):
205 def validate_python(self, value, state):
207 password = value['password']
206 password = value['password']
208 username = value['username']
207 username = value['username']
209 user = UserModel().get_by_username(username)
208 user = User.get_by_username(username)
210
209
211 if authenticate(username, password):
210 if authenticate(username, password):
212 return value
211 return value
213 else:
212 else:
214 if user and user.active is False:
213 if user and user.active is False:
215 log.warning('user %s is disabled', username)
214 log.warning('user %s is disabled', username)
216 raise formencode.Invalid(self.message('disabled_account',
215 raise formencode.Invalid(self.message('disabled_account',
217 state=State_obj),
216 state=State_obj),
218 value, state,
217 value, state,
219 error_dict=self.e_dict_disable)
218 error_dict=self.e_dict_disable)
220 else:
219 else:
221 log.warning('user %s not authenticated', username)
220 log.warning('user %s not authenticated', username)
222 raise formencode.Invalid(self.message('invalid_password',
221 raise formencode.Invalid(self.message('invalid_password',
223 state=State_obj), value, state,
222 state=State_obj), value, state,
224 error_dict=self.e_dict)
223 error_dict=self.e_dict)
225
224
226 class ValidRepoUser(formencode.validators.FancyValidator):
225 class ValidRepoUser(formencode.validators.FancyValidator):
227
226
228 def to_python(self, value, state):
227 def to_python(self, value, state):
229 try:
228 try:
230 User.query().filter(User.active == True)\
229 User.query().filter(User.active == True)\
231 .filter(User.username == value).one()
230 .filter(User.username == value).one()
232 except Exception:
231 except Exception:
233 raise formencode.Invalid(_('This username is not valid'),
232 raise formencode.Invalid(_('This username is not valid'),
234 value, state)
233 value, state)
235 return value
234 return value
236
235
237 def ValidRepoName(edit, old_data):
236 def ValidRepoName(edit, old_data):
238 class _ValidRepoName(formencode.validators.FancyValidator):
237 class _ValidRepoName(formencode.validators.FancyValidator):
239 def to_python(self, value, state):
238 def to_python(self, value, state):
240
239
241 repo_name = value.get('repo_name')
240 repo_name = value.get('repo_name')
242
241
243 slug = repo_name_slug(repo_name)
242 slug = repo_name_slug(repo_name)
244 if slug in ['_admin', '']:
243 if slug in ['_admin', '']:
245 e_dict = {'repo_name': _('This repository name is disallowed')}
244 e_dict = {'repo_name': _('This repository name is disallowed')}
246 raise formencode.Invalid('', value, state, error_dict=e_dict)
245 raise formencode.Invalid('', value, state, error_dict=e_dict)
247
246
248
247
249 if value.get('repo_group'):
248 if value.get('repo_group'):
250 gr = Group.get(value.get('repo_group'))
249 gr = Group.get(value.get('repo_group'))
251 group_path = gr.full_path
250 group_path = gr.full_path
252 # value needs to be aware of group name in order to check
251 # value needs to be aware of group name in order to check
253 # db key This is an actuall just the name to store in the
252 # db key This is an actuall just the name to store in the
254 # database
253 # database
255 repo_name_full = group_path + Group.url_sep() + repo_name
254 repo_name_full = group_path + Group.url_sep() + repo_name
256 else:
255 else:
257 group_path = ''
256 group_path = ''
258 repo_name_full = repo_name
257 repo_name_full = repo_name
259
258
260
259
261 value['repo_name_full'] = repo_name_full
260 value['repo_name_full'] = repo_name_full
262 if old_data.get('repo_name') != repo_name_full or not edit:
261 if old_data.get('repo_name') != repo_name_full or not edit:
263
262
264 if group_path != '':
263 if group_path != '':
265 if RepoModel().get_by_repo_name(repo_name_full,):
264 if RepoModel().get_by_repo_name(repo_name_full,):
266 e_dict = {'repo_name':_('This repository already '
265 e_dict = {'repo_name':_('This repository already '
267 'exists in group "%s"') %
266 'exists in group "%s"') %
268 gr.group_name}
267 gr.group_name}
269 raise formencode.Invalid('', value, state,
268 raise formencode.Invalid('', value, state,
270 error_dict=e_dict)
269 error_dict=e_dict)
271
270
272 else:
271 else:
273 if RepoModel().get_by_repo_name(repo_name_full):
272 if RepoModel().get_by_repo_name(repo_name_full):
274 e_dict = {'repo_name':_('This repository '
273 e_dict = {'repo_name':_('This repository '
275 'already exists')}
274 'already exists')}
276 raise formencode.Invalid('', value, state,
275 raise formencode.Invalid('', value, state,
277 error_dict=e_dict)
276 error_dict=e_dict)
278 return value
277 return value
279
278
280
279
281 return _ValidRepoName
280 return _ValidRepoName
282
281
283 def ValidForkName():
282 def ValidForkName():
284 class _ValidForkName(formencode.validators.FancyValidator):
283 class _ValidForkName(formencode.validators.FancyValidator):
285 def to_python(self, value, state):
284 def to_python(self, value, state):
286
285
287 repo_name = value.get('fork_name')
286 repo_name = value.get('fork_name')
288
287
289 slug = repo_name_slug(repo_name)
288 slug = repo_name_slug(repo_name)
290 if slug in ['_admin', '']:
289 if slug in ['_admin', '']:
291 e_dict = {'repo_name': _('This repository name is disallowed')}
290 e_dict = {'repo_name': _('This repository name is disallowed')}
292 raise formencode.Invalid('', value, state, error_dict=e_dict)
291 raise formencode.Invalid('', value, state, error_dict=e_dict)
293
292
294 if RepoModel().get_by_repo_name(repo_name):
293 if RepoModel().get_by_repo_name(repo_name):
295 e_dict = {'fork_name':_('This repository '
294 e_dict = {'fork_name':_('This repository '
296 'already exists')}
295 'already exists')}
297 raise formencode.Invalid('', value, state,
296 raise formencode.Invalid('', value, state,
298 error_dict=e_dict)
297 error_dict=e_dict)
299 return value
298 return value
300 return _ValidForkName
299 return _ValidForkName
301
300
302
301
303 def SlugifyName():
302 def SlugifyName():
304 class _SlugifyName(formencode.validators.FancyValidator):
303 class _SlugifyName(formencode.validators.FancyValidator):
305
304
306 def to_python(self, value, state):
305 def to_python(self, value, state):
307 return repo_name_slug(value)
306 return repo_name_slug(value)
308
307
309 return _SlugifyName
308 return _SlugifyName
310
309
311 def ValidCloneUri():
310 def ValidCloneUri():
312 from mercurial.httprepo import httprepository, httpsrepository
311 from mercurial.httprepo import httprepository, httpsrepository
313 from rhodecode.lib.utils import make_ui
312 from rhodecode.lib.utils import make_ui
314
313
315 class _ValidCloneUri(formencode.validators.FancyValidator):
314 class _ValidCloneUri(formencode.validators.FancyValidator):
316
315
317 def to_python(self, value, state):
316 def to_python(self, value, state):
318 if not value:
317 if not value:
319 pass
318 pass
320 elif value.startswith('https'):
319 elif value.startswith('https'):
321 try:
320 try:
322 httpsrepository(make_ui('db'), value).capabilities
321 httpsrepository(make_ui('db'), value).capabilities
323 except Exception, e:
322 except Exception, e:
324 log.error(traceback.format_exc())
323 log.error(traceback.format_exc())
325 raise formencode.Invalid(_('invalid clone url'), value,
324 raise formencode.Invalid(_('invalid clone url'), value,
326 state)
325 state)
327 elif value.startswith('http'):
326 elif value.startswith('http'):
328 try:
327 try:
329 httprepository(make_ui('db'), value).capabilities
328 httprepository(make_ui('db'), value).capabilities
330 except Exception, e:
329 except Exception, e:
331 log.error(traceback.format_exc())
330 log.error(traceback.format_exc())
332 raise formencode.Invalid(_('invalid clone url'), value,
331 raise formencode.Invalid(_('invalid clone url'), value,
333 state)
332 state)
334 else:
333 else:
335 raise formencode.Invalid(_('Invalid clone url, provide a '
334 raise formencode.Invalid(_('Invalid clone url, provide a '
336 'valid clone http\s url'), value,
335 'valid clone http\s url'), value,
337 state)
336 state)
338 return value
337 return value
339
338
340 return _ValidCloneUri
339 return _ValidCloneUri
341
340
342 def ValidForkType(old_data):
341 def ValidForkType(old_data):
343 class _ValidForkType(formencode.validators.FancyValidator):
342 class _ValidForkType(formencode.validators.FancyValidator):
344
343
345 def to_python(self, value, state):
344 def to_python(self, value, state):
346 if old_data['repo_type'] != value:
345 if old_data['repo_type'] != value:
347 raise formencode.Invalid(_('Fork have to be the same '
346 raise formencode.Invalid(_('Fork have to be the same '
348 'type as original'), value, state)
347 'type as original'), value, state)
349
348
350 return value
349 return value
351 return _ValidForkType
350 return _ValidForkType
352
351
353 class ValidPerms(formencode.validators.FancyValidator):
352 class ValidPerms(formencode.validators.FancyValidator):
354 messages = {'perm_new_member_name':_('This username or users group name'
353 messages = {'perm_new_member_name':_('This username or users group name'
355 ' is not valid')}
354 ' is not valid')}
356
355
357 def to_python(self, value, state):
356 def to_python(self, value, state):
358 perms_update = []
357 perms_update = []
359 perms_new = []
358 perms_new = []
360 #build a list of permission to update and new permission to create
359 #build a list of permission to update and new permission to create
361 for k, v in value.items():
360 for k, v in value.items():
362 #means new added member to permissions
361 #means new added member to permissions
363 if k.startswith('perm_new_member'):
362 if k.startswith('perm_new_member'):
364 new_perm = value.get('perm_new_member', False)
363 new_perm = value.get('perm_new_member', False)
365 new_member = value.get('perm_new_member_name', False)
364 new_member = value.get('perm_new_member_name', False)
366 new_type = value.get('perm_new_member_type')
365 new_type = value.get('perm_new_member_type')
367
366
368 if new_member and new_perm:
367 if new_member and new_perm:
369 if (new_member, new_perm, new_type) not in perms_new:
368 if (new_member, new_perm, new_type) not in perms_new:
370 perms_new.append((new_member, new_perm, new_type))
369 perms_new.append((new_member, new_perm, new_type))
371 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
370 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
372 member = k[7:]
371 member = k[7:]
373 t = {'u':'user',
372 t = {'u':'user',
374 'g':'users_group'}[k[0]]
373 'g':'users_group'}[k[0]]
375 if member == 'default':
374 if member == 'default':
376 if value['private']:
375 if value['private']:
377 #set none for default when updating to private repo
376 #set none for default when updating to private repo
378 v = 'repository.none'
377 v = 'repository.none'
379 perms_update.append((member, v, t))
378 perms_update.append((member, v, t))
380
379
381 value['perms_updates'] = perms_update
380 value['perms_updates'] = perms_update
382 value['perms_new'] = perms_new
381 value['perms_new'] = perms_new
383
382
384 #update permissions
383 #update permissions
385 for k, v, t in perms_new:
384 for k, v, t in perms_new:
386 try:
385 try:
387 if t is 'user':
386 if t is 'user':
388 self.user_db = User.query()\
387 self.user_db = User.query()\
389 .filter(User.active == True)\
388 .filter(User.active == True)\
390 .filter(User.username == k).one()
389 .filter(User.username == k).one()
391 if t is 'users_group':
390 if t is 'users_group':
392 self.user_db = UsersGroup.query()\
391 self.user_db = UsersGroup.query()\
393 .filter(UsersGroup.users_group_active == True)\
392 .filter(UsersGroup.users_group_active == True)\
394 .filter(UsersGroup.users_group_name == k).one()
393 .filter(UsersGroup.users_group_name == k).one()
395
394
396 except Exception:
395 except Exception:
397 msg = self.message('perm_new_member_name',
396 msg = self.message('perm_new_member_name',
398 state=State_obj)
397 state=State_obj)
399 raise formencode.Invalid(msg, value, state,
398 raise formencode.Invalid(msg, value, state,
400 error_dict={'perm_new_member_name':msg})
399 error_dict={'perm_new_member_name':msg})
401 return value
400 return value
402
401
403 class ValidSettings(formencode.validators.FancyValidator):
402 class ValidSettings(formencode.validators.FancyValidator):
404
403
405 def to_python(self, value, state):
404 def to_python(self, value, state):
406 #settings form can't edit user
405 #settings form can't edit user
407 if value.has_key('user'):
406 if value.has_key('user'):
408 del['value']['user']
407 del['value']['user']
409
408
410 return value
409 return value
411
410
412 class ValidPath(formencode.validators.FancyValidator):
411 class ValidPath(formencode.validators.FancyValidator):
413 def to_python(self, value, state):
412 def to_python(self, value, state):
414
413
415 if not os.path.isdir(value):
414 if not os.path.isdir(value):
416 msg = _('This is not a valid path')
415 msg = _('This is not a valid path')
417 raise formencode.Invalid(msg, value, state,
416 raise formencode.Invalid(msg, value, state,
418 error_dict={'paths_root_path':msg})
417 error_dict={'paths_root_path':msg})
419 return value
418 return value
420
419
421 def UniqSystemEmail(old_data):
420 def UniqSystemEmail(old_data):
422 class _UniqSystemEmail(formencode.validators.FancyValidator):
421 class _UniqSystemEmail(formencode.validators.FancyValidator):
423 def to_python(self, value, state):
422 def to_python(self, value, state):
424 value = value.lower()
423 value = value.lower()
425 if old_data.get('email') != value:
424 if old_data.get('email') != value:
426 user = User.query().filter(User.email == value).scalar()
425 user = User.query().filter(User.email == value).scalar()
427 if user:
426 if user:
428 raise formencode.Invalid(
427 raise formencode.Invalid(
429 _("This e-mail address is already taken"),
428 _("This e-mail address is already taken"),
430 value, state)
429 value, state)
431 return value
430 return value
432
431
433 return _UniqSystemEmail
432 return _UniqSystemEmail
434
433
435 class ValidSystemEmail(formencode.validators.FancyValidator):
434 class ValidSystemEmail(formencode.validators.FancyValidator):
436 def to_python(self, value, state):
435 def to_python(self, value, state):
437 value = value.lower()
436 value = value.lower()
438 user = User.query().filter(User.email == value).scalar()
437 user = User.query().filter(User.email == value).scalar()
439 if user is None:
438 if user is None:
440 raise formencode.Invalid(_("This e-mail address doesn't exist.") ,
439 raise formencode.Invalid(_("This e-mail address doesn't exist.") ,
441 value, state)
440 value, state)
442
441
443 return value
442 return value
444
443
445 class LdapLibValidator(formencode.validators.FancyValidator):
444 class LdapLibValidator(formencode.validators.FancyValidator):
446
445
447 def to_python(self, value, state):
446 def to_python(self, value, state):
448
447
449 try:
448 try:
450 import ldap
449 import ldap
451 except ImportError:
450 except ImportError:
452 raise LdapImportError
451 raise LdapImportError
453 return value
452 return value
454
453
455 class AttrLoginValidator(formencode.validators.FancyValidator):
454 class AttrLoginValidator(formencode.validators.FancyValidator):
456
455
457 def to_python(self, value, state):
456 def to_python(self, value, state):
458
457
459 if not value or not isinstance(value, (str, unicode)):
458 if not value or not isinstance(value, (str, unicode)):
460 raise formencode.Invalid(_("The LDAP Login attribute of the CN "
459 raise formencode.Invalid(_("The LDAP Login attribute of the CN "
461 "must be specified - this is the name "
460 "must be specified - this is the name "
462 "of the attribute that is equivalent "
461 "of the attribute that is equivalent "
463 "to 'username'"),
462 "to 'username'"),
464 value, state)
463 value, state)
465
464
466 return value
465 return value
467
466
468 #===============================================================================
467 #===============================================================================
469 # FORMS
468 # FORMS
470 #===============================================================================
469 #===============================================================================
471 class LoginForm(formencode.Schema):
470 class LoginForm(formencode.Schema):
472 allow_extra_fields = True
471 allow_extra_fields = True
473 filter_extra_fields = True
472 filter_extra_fields = True
474 username = UnicodeString(
473 username = UnicodeString(
475 strip=True,
474 strip=True,
476 min=1,
475 min=1,
477 not_empty=True,
476 not_empty=True,
478 messages={
477 messages={
479 'empty':_('Please enter a login'),
478 'empty':_('Please enter a login'),
480 'tooShort':_('Enter a value %(min)i characters long or more')}
479 'tooShort':_('Enter a value %(min)i characters long or more')}
481 )
480 )
482
481
483 password = UnicodeString(
482 password = UnicodeString(
484 strip=True,
483 strip=True,
485 min=3,
484 min=3,
486 not_empty=True,
485 not_empty=True,
487 messages={
486 messages={
488 'empty':_('Please enter a password'),
487 'empty':_('Please enter a password'),
489 'tooShort':_('Enter %(min)i characters or more')}
488 'tooShort':_('Enter %(min)i characters or more')}
490 )
489 )
491
490
492
491
493 #chained validators have access to all data
492 #chained validators have access to all data
494 chained_validators = [ValidAuth]
493 chained_validators = [ValidAuth]
495
494
496 def UserForm(edit=False, old_data={}):
495 def UserForm(edit=False, old_data={}):
497 class _UserForm(formencode.Schema):
496 class _UserForm(formencode.Schema):
498 allow_extra_fields = True
497 allow_extra_fields = True
499 filter_extra_fields = True
498 filter_extra_fields = True
500 username = All(UnicodeString(strip=True, min=1, not_empty=True),
499 username = All(UnicodeString(strip=True, min=1, not_empty=True),
501 ValidUsername(edit, old_data))
500 ValidUsername(edit, old_data))
502 if edit:
501 if edit:
503 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
502 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
504 admin = StringBoolean(if_missing=False)
503 admin = StringBoolean(if_missing=False)
505 else:
504 else:
506 password = All(UnicodeString(strip=True, min=6, not_empty=True))
505 password = All(UnicodeString(strip=True, min=6, not_empty=True))
507 active = StringBoolean(if_missing=False)
506 active = StringBoolean(if_missing=False)
508 name = UnicodeString(strip=True, min=1, not_empty=True)
507 name = UnicodeString(strip=True, min=1, not_empty=True)
509 lastname = UnicodeString(strip=True, min=1, not_empty=True)
508 lastname = UnicodeString(strip=True, min=1, not_empty=True)
510 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
509 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
511
510
512 chained_validators = [ValidPassword]
511 chained_validators = [ValidPassword]
513
512
514 return _UserForm
513 return _UserForm
515
514
516
515
517 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
516 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
518 class _UsersGroupForm(formencode.Schema):
517 class _UsersGroupForm(formencode.Schema):
519 allow_extra_fields = True
518 allow_extra_fields = True
520 filter_extra_fields = True
519 filter_extra_fields = True
521
520
522 users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
521 users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
523 ValidUsersGroup(edit, old_data))
522 ValidUsersGroup(edit, old_data))
524
523
525 users_group_active = StringBoolean(if_missing=False)
524 users_group_active = StringBoolean(if_missing=False)
526
525
527 if edit:
526 if edit:
528 users_group_members = OneOf(available_members, hideList=False,
527 users_group_members = OneOf(available_members, hideList=False,
529 testValueList=True,
528 testValueList=True,
530 if_missing=None, not_empty=False)
529 if_missing=None, not_empty=False)
531
530
532 return _UsersGroupForm
531 return _UsersGroupForm
533
532
534 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
533 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
535 class _ReposGroupForm(formencode.Schema):
534 class _ReposGroupForm(formencode.Schema):
536 allow_extra_fields = True
535 allow_extra_fields = True
537 filter_extra_fields = True
536 filter_extra_fields = True
538
537
539 group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
538 group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
540 SlugifyName())
539 SlugifyName())
541 group_description = UnicodeString(strip=True, min=1,
540 group_description = UnicodeString(strip=True, min=1,
542 not_empty=True)
541 not_empty=True)
543 group_parent_id = OneOf(available_groups, hideList=False,
542 group_parent_id = OneOf(available_groups, hideList=False,
544 testValueList=True,
543 testValueList=True,
545 if_missing=None, not_empty=False)
544 if_missing=None, not_empty=False)
546
545
547 chained_validators = [ValidReposGroup(edit, old_data)]
546 chained_validators = [ValidReposGroup(edit, old_data)]
548
547
549 return _ReposGroupForm
548 return _ReposGroupForm
550
549
551 def RegisterForm(edit=False, old_data={}):
550 def RegisterForm(edit=False, old_data={}):
552 class _RegisterForm(formencode.Schema):
551 class _RegisterForm(formencode.Schema):
553 allow_extra_fields = True
552 allow_extra_fields = True
554 filter_extra_fields = True
553 filter_extra_fields = True
555 username = All(ValidUsername(edit, old_data),
554 username = All(ValidUsername(edit, old_data),
556 UnicodeString(strip=True, min=1, not_empty=True))
555 UnicodeString(strip=True, min=1, not_empty=True))
557 password = All(UnicodeString(strip=True, min=6, not_empty=True))
556 password = All(UnicodeString(strip=True, min=6, not_empty=True))
558 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
557 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
559 active = StringBoolean(if_missing=False)
558 active = StringBoolean(if_missing=False)
560 name = UnicodeString(strip=True, min=1, not_empty=True)
559 name = UnicodeString(strip=True, min=1, not_empty=True)
561 lastname = UnicodeString(strip=True, min=1, not_empty=True)
560 lastname = UnicodeString(strip=True, min=1, not_empty=True)
562 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
561 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
563
562
564 chained_validators = [ValidPasswordsMatch, ValidPassword]
563 chained_validators = [ValidPasswordsMatch, ValidPassword]
565
564
566 return _RegisterForm
565 return _RegisterForm
567
566
568 def PasswordResetForm():
567 def PasswordResetForm():
569 class _PasswordResetForm(formencode.Schema):
568 class _PasswordResetForm(formencode.Schema):
570 allow_extra_fields = True
569 allow_extra_fields = True
571 filter_extra_fields = True
570 filter_extra_fields = True
572 email = All(ValidSystemEmail(), Email(not_empty=True))
571 email = All(ValidSystemEmail(), Email(not_empty=True))
573 return _PasswordResetForm
572 return _PasswordResetForm
574
573
575 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
574 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
576 repo_groups=[]):
575 repo_groups=[]):
577 class _RepoForm(formencode.Schema):
576 class _RepoForm(formencode.Schema):
578 allow_extra_fields = True
577 allow_extra_fields = True
579 filter_extra_fields = False
578 filter_extra_fields = False
580 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
579 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
581 SlugifyName())
580 SlugifyName())
582 clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False),
581 clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False),
583 ValidCloneUri()())
582 ValidCloneUri()())
584 repo_group = OneOf(repo_groups, hideList=True)
583 repo_group = OneOf(repo_groups, hideList=True)
585 repo_type = OneOf(supported_backends)
584 repo_type = OneOf(supported_backends)
586 description = UnicodeString(strip=True, min=1, not_empty=True)
585 description = UnicodeString(strip=True, min=1, not_empty=True)
587 private = StringBoolean(if_missing=False)
586 private = StringBoolean(if_missing=False)
588 enable_statistics = StringBoolean(if_missing=False)
587 enable_statistics = StringBoolean(if_missing=False)
589 enable_downloads = StringBoolean(if_missing=False)
588 enable_downloads = StringBoolean(if_missing=False)
590
589
591 if edit:
590 if edit:
592 #this is repo owner
591 #this is repo owner
593 user = All(UnicodeString(not_empty=True), ValidRepoUser)
592 user = All(UnicodeString(not_empty=True), ValidRepoUser)
594
593
595 chained_validators = [ValidRepoName(edit, old_data), ValidPerms]
594 chained_validators = [ValidRepoName(edit, old_data), ValidPerms]
596 return _RepoForm
595 return _RepoForm
597
596
598 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
597 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
599 class _RepoForkForm(formencode.Schema):
598 class _RepoForkForm(formencode.Schema):
600 allow_extra_fields = True
599 allow_extra_fields = True
601 filter_extra_fields = False
600 filter_extra_fields = False
602 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True),
601 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True),
603 SlugifyName())
602 SlugifyName())
604 description = UnicodeString(strip=True, min=1, not_empty=True)
603 description = UnicodeString(strip=True, min=1, not_empty=True)
605 private = StringBoolean(if_missing=False)
604 private = StringBoolean(if_missing=False)
606 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
605 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
607
606
608 chained_validators = [ValidForkName()]
607 chained_validators = [ValidForkName()]
609
608
610 return _RepoForkForm
609 return _RepoForkForm
611
610
612 def RepoSettingsForm(edit=False, old_data={}):
611 def RepoSettingsForm(edit=False, old_data={}):
613 class _RepoForm(formencode.Schema):
612 class _RepoForm(formencode.Schema):
614 allow_extra_fields = True
613 allow_extra_fields = True
615 filter_extra_fields = False
614 filter_extra_fields = False
616 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
615 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
617 SlugifyName())
616 SlugifyName())
618 description = UnicodeString(strip=True, min=1, not_empty=True)
617 description = UnicodeString(strip=True, min=1, not_empty=True)
619 private = StringBoolean(if_missing=False)
618 private = StringBoolean(if_missing=False)
620
619
621 chained_validators = [ValidRepoName(edit, old_data), ValidPerms, ValidSettings]
620 chained_validators = [ValidRepoName(edit, old_data), ValidPerms, ValidSettings]
622 return _RepoForm
621 return _RepoForm
623
622
624
623
625 def ApplicationSettingsForm():
624 def ApplicationSettingsForm():
626 class _ApplicationSettingsForm(formencode.Schema):
625 class _ApplicationSettingsForm(formencode.Schema):
627 allow_extra_fields = True
626 allow_extra_fields = True
628 filter_extra_fields = False
627 filter_extra_fields = False
629 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
628 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
630 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
629 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
631 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
630 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
632
631
633 return _ApplicationSettingsForm
632 return _ApplicationSettingsForm
634
633
635 def ApplicationUiSettingsForm():
634 def ApplicationUiSettingsForm():
636 class _ApplicationUiSettingsForm(formencode.Schema):
635 class _ApplicationUiSettingsForm(formencode.Schema):
637 allow_extra_fields = True
636 allow_extra_fields = True
638 filter_extra_fields = False
637 filter_extra_fields = False
639 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
638 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
640 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
639 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
641 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
640 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
642 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
641 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
643 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
642 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
644 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
643 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
645
644
646 return _ApplicationUiSettingsForm
645 return _ApplicationUiSettingsForm
647
646
648 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
647 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
649 class _DefaultPermissionsForm(formencode.Schema):
648 class _DefaultPermissionsForm(formencode.Schema):
650 allow_extra_fields = True
649 allow_extra_fields = True
651 filter_extra_fields = True
650 filter_extra_fields = True
652 overwrite_default = StringBoolean(if_missing=False)
651 overwrite_default = StringBoolean(if_missing=False)
653 anonymous = OneOf(['True', 'False'], if_missing=False)
652 anonymous = OneOf(['True', 'False'], if_missing=False)
654 default_perm = OneOf(perms_choices)
653 default_perm = OneOf(perms_choices)
655 default_register = OneOf(register_choices)
654 default_register = OneOf(register_choices)
656 default_create = OneOf(create_choices)
655 default_create = OneOf(create_choices)
657
656
658 return _DefaultPermissionsForm
657 return _DefaultPermissionsForm
659
658
660
659
661 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices):
660 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices):
662 class _LdapSettingsForm(formencode.Schema):
661 class _LdapSettingsForm(formencode.Schema):
663 allow_extra_fields = True
662 allow_extra_fields = True
664 filter_extra_fields = True
663 filter_extra_fields = True
665 pre_validators = [LdapLibValidator]
664 pre_validators = [LdapLibValidator]
666 ldap_active = StringBoolean(if_missing=False)
665 ldap_active = StringBoolean(if_missing=False)
667 ldap_host = UnicodeString(strip=True,)
666 ldap_host = UnicodeString(strip=True,)
668 ldap_port = Number(strip=True,)
667 ldap_port = Number(strip=True,)
669 ldap_tls_kind = OneOf(tls_kind_choices)
668 ldap_tls_kind = OneOf(tls_kind_choices)
670 ldap_tls_reqcert = OneOf(tls_reqcert_choices)
669 ldap_tls_reqcert = OneOf(tls_reqcert_choices)
671 ldap_dn_user = UnicodeString(strip=True,)
670 ldap_dn_user = UnicodeString(strip=True,)
672 ldap_dn_pass = UnicodeString(strip=True,)
671 ldap_dn_pass = UnicodeString(strip=True,)
673 ldap_base_dn = UnicodeString(strip=True,)
672 ldap_base_dn = UnicodeString(strip=True,)
674 ldap_filter = UnicodeString(strip=True,)
673 ldap_filter = UnicodeString(strip=True,)
675 ldap_search_scope = OneOf(search_scope_choices)
674 ldap_search_scope = OneOf(search_scope_choices)
676 ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
675 ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
677 ldap_attr_firstname = UnicodeString(strip=True,)
676 ldap_attr_firstname = UnicodeString(strip=True,)
678 ldap_attr_lastname = UnicodeString(strip=True,)
677 ldap_attr_lastname = UnicodeString(strip=True,)
679 ldap_attr_email = UnicodeString(strip=True,)
678 ldap_attr_email = UnicodeString(strip=True,)
680
679
681 return _LdapSettingsForm
680 return _LdapSettingsForm
@@ -1,361 +1,359 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.repo
3 rhodecode.model.repo
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 Repository model for rhodecode
6 Repository model for rhodecode
7
7
8 :created_on: Jun 5, 2010
8 :created_on: Jun 5, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import os
25 import os
26 import shutil
26 import shutil
27 import logging
27 import logging
28 import traceback
28 import traceback
29 from datetime import datetime
29 from datetime import datetime
30
30
31 from sqlalchemy.orm import joinedload, make_transient
31 from sqlalchemy.orm import joinedload, make_transient
32
32
33 from vcs.utils.lazy import LazyProperty
33 from vcs.utils.lazy import LazyProperty
34 from vcs.backends import get_backend
34 from vcs.backends import get_backend
35
35
36 from rhodecode.lib import safe_str
36 from rhodecode.lib import safe_str
37
37
38 from rhodecode.model import BaseModel
38 from rhodecode.model import BaseModel
39 from rhodecode.model.caching_query import FromCache
39 from rhodecode.model.caching_query import FromCache
40 from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
40 from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
41 Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, Group
41 Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, Group
42 from rhodecode.model.user import UserModel
42 from rhodecode.model.user import UserModel
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46
46
47 class RepoModel(BaseModel):
47 class RepoModel(BaseModel):
48
48
49 @LazyProperty
49 @LazyProperty
50 def repos_path(self):
50 def repos_path(self):
51 """Get's the repositories root path from database
51 """Get's the repositories root path from database
52 """
52 """
53
53
54 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
54 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
55 return q.ui_value
55 return q.ui_value
56
56
57 def get(self, repo_id, cache=False):
57 def get(self, repo_id, cache=False):
58 repo = self.sa.query(Repository)\
58 repo = self.sa.query(Repository)\
59 .filter(Repository.repo_id == repo_id)
59 .filter(Repository.repo_id == repo_id)
60
60
61 if cache:
61 if cache:
62 repo = repo.options(FromCache("sql_cache_short",
62 repo = repo.options(FromCache("sql_cache_short",
63 "get_repo_%s" % repo_id))
63 "get_repo_%s" % repo_id))
64 return repo.scalar()
64 return repo.scalar()
65
65
66 def get_by_repo_name(self, repo_name, cache=False):
66 def get_by_repo_name(self, repo_name, cache=False):
67 repo = self.sa.query(Repository)\
67 repo = self.sa.query(Repository)\
68 .filter(Repository.repo_name == repo_name)
68 .filter(Repository.repo_name == repo_name)
69
69
70 if cache:
70 if cache:
71 repo = repo.options(FromCache("sql_cache_short",
71 repo = repo.options(FromCache("sql_cache_short",
72 "get_repo_%s" % repo_name))
72 "get_repo_%s" % repo_name))
73 return repo.scalar()
73 return repo.scalar()
74
74
75
75
76 def get_users_js(self):
76 def get_users_js(self):
77
77
78 users = self.sa.query(User).filter(User.active == True).all()
78 users = self.sa.query(User).filter(User.active == True).all()
79 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
79 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
80 users_array = '[%s]' % '\n'.join([u_tmpl % (u.user_id, u.name,
80 users_array = '[%s]' % '\n'.join([u_tmpl % (u.user_id, u.name,
81 u.lastname, u.username)
81 u.lastname, u.username)
82 for u in users])
82 for u in users])
83 return users_array
83 return users_array
84
84
85 def get_users_groups_js(self):
85 def get_users_groups_js(self):
86 users_groups = self.sa.query(UsersGroup)\
86 users_groups = self.sa.query(UsersGroup)\
87 .filter(UsersGroup.users_group_active == True).all()
87 .filter(UsersGroup.users_group_active == True).all()
88
88
89 g_tmpl = '''{id:%s, grname:"%s",grmembers:"%s"},'''
89 g_tmpl = '''{id:%s, grname:"%s",grmembers:"%s"},'''
90
90
91 users_groups_array = '[%s]' % '\n'.join([g_tmpl % \
91 users_groups_array = '[%s]' % '\n'.join([g_tmpl % \
92 (gr.users_group_id, gr.users_group_name,
92 (gr.users_group_id, gr.users_group_name,
93 len(gr.members))
93 len(gr.members))
94 for gr in users_groups])
94 for gr in users_groups])
95 return users_groups_array
95 return users_groups_array
96
96
97 def update(self, repo_name, form_data):
97 def update(self, repo_name, form_data):
98 try:
98 try:
99 cur_repo = self.get_by_repo_name(repo_name, cache=False)
99 cur_repo = self.get_by_repo_name(repo_name, cache=False)
100
100
101 #update permissions
101 #update permissions
102 for member, perm, member_type in form_data['perms_updates']:
102 for member, perm, member_type in form_data['perms_updates']:
103 if member_type == 'user':
103 if member_type == 'user':
104 r2p = self.sa.query(RepoToPerm)\
104 r2p = self.sa.query(RepoToPerm)\
105 .filter(RepoToPerm.user == User.by_username(member))\
105 .filter(RepoToPerm.user == User.get_by_username(member))\
106 .filter(RepoToPerm.repository == cur_repo)\
106 .filter(RepoToPerm.repository == cur_repo)\
107 .one()
107 .one()
108
108
109 r2p.permission = self.sa.query(Permission)\
109 r2p.permission = self.sa.query(Permission)\
110 .filter(Permission.permission_name ==
110 .filter(Permission.permission_name ==
111 perm).scalar()
111 perm).scalar()
112 self.sa.add(r2p)
112 self.sa.add(r2p)
113 else:
113 else:
114 g2p = self.sa.query(UsersGroupRepoToPerm)\
114 g2p = self.sa.query(UsersGroupRepoToPerm)\
115 .filter(UsersGroupRepoToPerm.users_group ==
115 .filter(UsersGroupRepoToPerm.users_group ==
116 UsersGroup.get_by_group_name(member))\
116 UsersGroup.get_by_group_name(member))\
117 .filter(UsersGroupRepoToPerm.repository ==
117 .filter(UsersGroupRepoToPerm.repository ==
118 cur_repo).one()
118 cur_repo).one()
119
119
120 g2p.permission = self.sa.query(Permission)\
120 g2p.permission = self.sa.query(Permission)\
121 .filter(Permission.permission_name ==
121 .filter(Permission.permission_name ==
122 perm).scalar()
122 perm).scalar()
123 self.sa.add(g2p)
123 self.sa.add(g2p)
124
124
125 #set new permissions
125 #set new permissions
126 for member, perm, member_type in form_data['perms_new']:
126 for member, perm, member_type in form_data['perms_new']:
127 if member_type == 'user':
127 if member_type == 'user':
128 r2p = RepoToPerm()
128 r2p = RepoToPerm()
129 r2p.repository = cur_repo
129 r2p.repository = cur_repo
130 r2p.user = User.by_username(member)
130 r2p.user = User.get_by_username(member)
131
131
132 r2p.permission = self.sa.query(Permission)\
132 r2p.permission = self.sa.query(Permission)\
133 .filter(Permission.
133 .filter(Permission.
134 permission_name == perm)\
134 permission_name == perm)\
135 .scalar()
135 .scalar()
136 self.sa.add(r2p)
136 self.sa.add(r2p)
137 else:
137 else:
138 g2p = UsersGroupRepoToPerm()
138 g2p = UsersGroupRepoToPerm()
139 g2p.repository = cur_repo
139 g2p.repository = cur_repo
140 g2p.users_group = UsersGroup.get_by_group_name(member)
140 g2p.users_group = UsersGroup.get_by_group_name(member)
141 g2p.permission = self.sa.query(Permission)\
141 g2p.permission = self.sa.query(Permission)\
142 .filter(Permission.
142 .filter(Permission.
143 permission_name == perm)\
143 permission_name == perm)\
144 .scalar()
144 .scalar()
145 self.sa.add(g2p)
145 self.sa.add(g2p)
146
146
147 #update current repo
147 #update current repo
148 for k, v in form_data.items():
148 for k, v in form_data.items():
149 if k == 'user':
149 if k == 'user':
150 cur_repo.user = User.by_username(v)
150 cur_repo.user = User.get_by_username(v)
151 elif k == 'repo_name':
151 elif k == 'repo_name':
152 cur_repo.repo_name = form_data['repo_name_full']
152 cur_repo.repo_name = form_data['repo_name_full']
153 elif k == 'repo_group':
153 elif k == 'repo_group':
154 cur_repo.group_id = v
154 cur_repo.group_id = v
155
155
156 else:
156 else:
157 setattr(cur_repo, k, v)
157 setattr(cur_repo, k, v)
158
158
159 self.sa.add(cur_repo)
159 self.sa.add(cur_repo)
160
160
161 if repo_name != form_data['repo_name_full']:
161 if repo_name != form_data['repo_name_full']:
162 # rename repository
162 # rename repository
163 self.__rename_repo(old=repo_name,
163 self.__rename_repo(old=repo_name,
164 new=form_data['repo_name_full'])
164 new=form_data['repo_name_full'])
165
165
166 self.sa.commit()
166 self.sa.commit()
167 except:
167 except:
168 log.error(traceback.format_exc())
168 log.error(traceback.format_exc())
169 self.sa.rollback()
169 self.sa.rollback()
170 raise
170 raise
171
171
172 def create(self, form_data, cur_user, just_db=False, fork=False):
172 def create(self, form_data, cur_user, just_db=False, fork=False):
173
173
174 try:
174 try:
175 if fork:
175 if fork:
176 repo_name = form_data['fork_name']
176 repo_name = form_data['fork_name']
177 org_name = form_data['repo_name']
177 org_name = form_data['repo_name']
178 org_full_name = org_name
178 org_full_name = org_name
179
179
180 else:
180 else:
181 org_name = repo_name = form_data['repo_name']
181 org_name = repo_name = form_data['repo_name']
182 repo_name_full = form_data['repo_name_full']
182 repo_name_full = form_data['repo_name_full']
183
183
184 new_repo = Repository()
184 new_repo = Repository()
185 new_repo.enable_statistics = False
185 new_repo.enable_statistics = False
186 for k, v in form_data.items():
186 for k, v in form_data.items():
187 if k == 'repo_name':
187 if k == 'repo_name':
188 if fork:
188 if fork:
189 v = repo_name
189 v = repo_name
190 else:
190 else:
191 v = repo_name_full
191 v = repo_name_full
192 if k == 'repo_group':
192 if k == 'repo_group':
193 k = 'group_id'
193 k = 'group_id'
194
194
195 if k == 'description':
195 if k == 'description':
196 v = v or repo_name
196 v = v or repo_name
197
197
198 setattr(new_repo, k, v)
198 setattr(new_repo, k, v)
199
199
200 if fork:
200 if fork:
201 parent_repo = self.sa.query(Repository)\
201 parent_repo = self.sa.query(Repository)\
202 .filter(Repository.repo_name == org_full_name).one()
202 .filter(Repository.repo_name == org_full_name).one()
203 new_repo.fork = parent_repo
203 new_repo.fork = parent_repo
204
204
205 new_repo.user_id = cur_user.user_id
205 new_repo.user_id = cur_user.user_id
206 self.sa.add(new_repo)
206 self.sa.add(new_repo)
207
207
208 #create default permission
208 #create default permission
209 repo_to_perm = RepoToPerm()
209 repo_to_perm = RepoToPerm()
210 default = 'repository.read'
210 default = 'repository.read'
211 for p in UserModel(self.sa).get_by_username('default',
211 for p in User.get_by_username('default').user_perms:
212 cache=False).user_perms:
213 if p.permission.permission_name.startswith('repository.'):
212 if p.permission.permission_name.startswith('repository.'):
214 default = p.permission.permission_name
213 default = p.permission.permission_name
215 break
214 break
216
215
217 default_perm = 'repository.none' if form_data['private'] else default
216 default_perm = 'repository.none' if form_data['private'] else default
218
217
219 repo_to_perm.permission_id = self.sa.query(Permission)\
218 repo_to_perm.permission_id = self.sa.query(Permission)\
220 .filter(Permission.permission_name == default_perm)\
219 .filter(Permission.permission_name == default_perm)\
221 .one().permission_id
220 .one().permission_id
222
221
223 repo_to_perm.repository = new_repo
222 repo_to_perm.repository = new_repo
224 repo_to_perm.user_id = UserModel(self.sa)\
223 repo_to_perm.user_id = User.get_by_username('default').user_id
225 .get_by_username('default', cache=False).user_id
226
224
227 self.sa.add(repo_to_perm)
225 self.sa.add(repo_to_perm)
228
226
229 if not just_db:
227 if not just_db:
230 self.__create_repo(repo_name, form_data['repo_type'],
228 self.__create_repo(repo_name, form_data['repo_type'],
231 form_data['repo_group'],
229 form_data['repo_group'],
232 form_data['clone_uri'])
230 form_data['clone_uri'])
233
231
234 self.sa.commit()
232 self.sa.commit()
235
233
236 #now automatically start following this repository as owner
234 #now automatically start following this repository as owner
237 from rhodecode.model.scm import ScmModel
235 from rhodecode.model.scm import ScmModel
238 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
236 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
239 cur_user.user_id)
237 cur_user.user_id)
240
238
241 except:
239 except:
242 log.error(traceback.format_exc())
240 log.error(traceback.format_exc())
243 self.sa.rollback()
241 self.sa.rollback()
244 raise
242 raise
245
243
246 def create_fork(self, form_data, cur_user):
244 def create_fork(self, form_data, cur_user):
247 from rhodecode.lib.celerylib import tasks, run_task
245 from rhodecode.lib.celerylib import tasks, run_task
248 run_task(tasks.create_repo_fork, form_data, cur_user)
246 run_task(tasks.create_repo_fork, form_data, cur_user)
249
247
250 def delete(self, repo):
248 def delete(self, repo):
251 try:
249 try:
252 self.sa.delete(repo)
250 self.sa.delete(repo)
253 self.__delete_repo(repo)
251 self.__delete_repo(repo)
254 self.sa.commit()
252 self.sa.commit()
255 except:
253 except:
256 log.error(traceback.format_exc())
254 log.error(traceback.format_exc())
257 self.sa.rollback()
255 self.sa.rollback()
258 raise
256 raise
259
257
260 def delete_perm_user(self, form_data, repo_name):
258 def delete_perm_user(self, form_data, repo_name):
261 try:
259 try:
262 self.sa.query(RepoToPerm)\
260 self.sa.query(RepoToPerm)\
263 .filter(RepoToPerm.repository \
261 .filter(RepoToPerm.repository \
264 == self.get_by_repo_name(repo_name))\
262 == self.get_by_repo_name(repo_name))\
265 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
263 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
266 self.sa.commit()
264 self.sa.commit()
267 except:
265 except:
268 log.error(traceback.format_exc())
266 log.error(traceback.format_exc())
269 self.sa.rollback()
267 self.sa.rollback()
270 raise
268 raise
271
269
272 def delete_perm_users_group(self, form_data, repo_name):
270 def delete_perm_users_group(self, form_data, repo_name):
273 try:
271 try:
274 self.sa.query(UsersGroupRepoToPerm)\
272 self.sa.query(UsersGroupRepoToPerm)\
275 .filter(UsersGroupRepoToPerm.repository \
273 .filter(UsersGroupRepoToPerm.repository \
276 == self.get_by_repo_name(repo_name))\
274 == self.get_by_repo_name(repo_name))\
277 .filter(UsersGroupRepoToPerm.users_group_id \
275 .filter(UsersGroupRepoToPerm.users_group_id \
278 == form_data['users_group_id']).delete()
276 == form_data['users_group_id']).delete()
279 self.sa.commit()
277 self.sa.commit()
280 except:
278 except:
281 log.error(traceback.format_exc())
279 log.error(traceback.format_exc())
282 self.sa.rollback()
280 self.sa.rollback()
283 raise
281 raise
284
282
285 def delete_stats(self, repo_name):
283 def delete_stats(self, repo_name):
286 try:
284 try:
287 self.sa.query(Statistics)\
285 self.sa.query(Statistics)\
288 .filter(Statistics.repository == \
286 .filter(Statistics.repository == \
289 self.get_by_repo_name(repo_name)).delete()
287 self.get_by_repo_name(repo_name)).delete()
290 self.sa.commit()
288 self.sa.commit()
291 except:
289 except:
292 log.error(traceback.format_exc())
290 log.error(traceback.format_exc())
293 self.sa.rollback()
291 self.sa.rollback()
294 raise
292 raise
295
293
296 def __create_repo(self, repo_name, alias, new_parent_id, clone_uri=False):
294 def __create_repo(self, repo_name, alias, new_parent_id, clone_uri=False):
297 """
295 """
298 makes repository on filesystem. It's group aware means it'll create
296 makes repository on filesystem. It's group aware means it'll create
299 a repository within a group, and alter the paths accordingly of
297 a repository within a group, and alter the paths accordingly of
300 group location
298 group location
301
299
302 :param repo_name:
300 :param repo_name:
303 :param alias:
301 :param alias:
304 :param parent_id:
302 :param parent_id:
305 :param clone_uri:
303 :param clone_uri:
306 """
304 """
307 from rhodecode.lib.utils import is_valid_repo
305 from rhodecode.lib.utils import is_valid_repo
308
306
309 if new_parent_id:
307 if new_parent_id:
310 paths = Group.get(new_parent_id).full_path.split(Group.url_sep())
308 paths = Group.get(new_parent_id).full_path.split(Group.url_sep())
311 new_parent_path = os.sep.join(paths)
309 new_parent_path = os.sep.join(paths)
312 else:
310 else:
313 new_parent_path = ''
311 new_parent_path = ''
314
312
315 repo_path = os.path.join(*map(lambda x:safe_str(x),
313 repo_path = os.path.join(*map(lambda x:safe_str(x),
316 [self.repos_path, new_parent_path, repo_name]))
314 [self.repos_path, new_parent_path, repo_name]))
317
315
318 if is_valid_repo(repo_path, self.repos_path) is False:
316 if is_valid_repo(repo_path, self.repos_path) is False:
319 log.info('creating repo %s in %s @ %s', repo_name, repo_path,
317 log.info('creating repo %s in %s @ %s', repo_name, repo_path,
320 clone_uri)
318 clone_uri)
321 backend = get_backend(alias)
319 backend = get_backend(alias)
322
320
323 backend(repo_path, create=True, src_url=clone_uri)
321 backend(repo_path, create=True, src_url=clone_uri)
324
322
325
323
326 def __rename_repo(self, old, new):
324 def __rename_repo(self, old, new):
327 """
325 """
328 renames repository on filesystem
326 renames repository on filesystem
329
327
330 :param old: old name
328 :param old: old name
331 :param new: new name
329 :param new: new name
332 """
330 """
333 log.info('renaming repo from %s to %s', old, new)
331 log.info('renaming repo from %s to %s', old, new)
334
332
335 old_path = os.path.join(self.repos_path, old)
333 old_path = os.path.join(self.repos_path, old)
336 new_path = os.path.join(self.repos_path, new)
334 new_path = os.path.join(self.repos_path, new)
337 if os.path.isdir(new_path):
335 if os.path.isdir(new_path):
338 raise Exception('Was trying to rename to already existing dir %s' \
336 raise Exception('Was trying to rename to already existing dir %s' \
339 % new_path)
337 % new_path)
340 shutil.move(old_path, new_path)
338 shutil.move(old_path, new_path)
341
339
342 def __delete_repo(self, repo):
340 def __delete_repo(self, repo):
343 """
341 """
344 removes repo from filesystem, the removal is acctually made by
342 removes repo from filesystem, the removal is acctually made by
345 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
343 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
346 repository is no longer valid for rhodecode, can be undeleted later on
344 repository is no longer valid for rhodecode, can be undeleted later on
347 by reverting the renames on this repository
345 by reverting the renames on this repository
348
346
349 :param repo: repo object
347 :param repo: repo object
350 """
348 """
351 rm_path = os.path.join(self.repos_path, repo.repo_name)
349 rm_path = os.path.join(self.repos_path, repo.repo_name)
352 log.info("Removing %s", rm_path)
350 log.info("Removing %s", rm_path)
353 #disable hg/git
351 #disable hg/git
354 alias = repo.repo_type
352 alias = repo.repo_type
355 shutil.move(os.path.join(rm_path, '.%s' % alias),
353 shutil.move(os.path.join(rm_path, '.%s' % alias),
356 os.path.join(rm_path, 'rm__.%s' % alias))
354 os.path.join(rm_path, 'rm__.%s' % alias))
357 #disable repo
355 #disable repo
358 shutil.move(rm_path, os.path.join(self.repos_path, 'rm__%s__%s' \
356 shutil.move(rm_path, os.path.join(self.repos_path, 'rm__%s__%s' \
359 % (datetime.today()\
357 % (datetime.today()\
360 .strftime('%Y%m%d_%H%M%S_%f'),
358 .strftime('%Y%m%d_%H%M%S_%f'),
361 repo.repo_name)))
359 repo.repo_name)))
@@ -1,410 +1,408 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.scm
3 rhodecode.model.scm
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Scm model for RhodeCode
6 Scm model for RhodeCode
7
7
8 :created_on: Apr 9, 2010
8 :created_on: Apr 9, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import os
26 import time
25 import time
27 import traceback
26 import traceback
28 import logging
27 import logging
29
28
30 from sqlalchemy.exc import DatabaseError
29 from sqlalchemy.exc import DatabaseError
31
30
32 from vcs import get_backend
31 from vcs import get_backend
33 from vcs.exceptions import RepositoryError
32 from vcs.exceptions import RepositoryError
34 from vcs.utils.lazy import LazyProperty
33 from vcs.utils.lazy import LazyProperty
35 from vcs.nodes import FileNode
34 from vcs.nodes import FileNode
36
35
37 from rhodecode import BACKENDS
36 from rhodecode import BACKENDS
38 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
39 from rhodecode.lib import safe_str
38 from rhodecode.lib import safe_str
40 from rhodecode.lib.auth import HasRepoPermissionAny
39 from rhodecode.lib.auth import HasRepoPermissionAny
41 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
40 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
42 action_logger, EmptyChangeset
41 action_logger, EmptyChangeset
43 from rhodecode.model import BaseModel
42 from rhodecode.model import BaseModel
44 from rhodecode.model.user import UserModel
45 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
43 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
46 UserFollowing, UserLog
44 UserFollowing, UserLog, User
47
45
48 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
49
47
50
48
51 class UserTemp(object):
49 class UserTemp(object):
52 def __init__(self, user_id):
50 def __init__(self, user_id):
53 self.user_id = user_id
51 self.user_id = user_id
54
52
55 def __repr__(self):
53 def __repr__(self):
56 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
54 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
57
55
58
56
59 class RepoTemp(object):
57 class RepoTemp(object):
60 def __init__(self, repo_id):
58 def __init__(self, repo_id):
61 self.repo_id = repo_id
59 self.repo_id = repo_id
62
60
63 def __repr__(self):
61 def __repr__(self):
64 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
62 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
65
63
66 class CachedRepoList(object):
64 class CachedRepoList(object):
67
65
68 def __init__(self, db_repo_list, repos_path, order_by=None):
66 def __init__(self, db_repo_list, repos_path, order_by=None):
69 self.db_repo_list = db_repo_list
67 self.db_repo_list = db_repo_list
70 self.repos_path = repos_path
68 self.repos_path = repos_path
71 self.order_by = order_by
69 self.order_by = order_by
72 self.reversed = (order_by or '').startswith('-')
70 self.reversed = (order_by or '').startswith('-')
73
71
74 def __len__(self):
72 def __len__(self):
75 return len(self.db_repo_list)
73 return len(self.db_repo_list)
76
74
77 def __repr__(self):
75 def __repr__(self):
78 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
76 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
79
77
80 def __iter__(self):
78 def __iter__(self):
81 for dbr in self.db_repo_list:
79 for dbr in self.db_repo_list:
82
80
83 scmr = dbr.scm_instance_cached
81 scmr = dbr.scm_instance_cached
84
82
85 # check permission at this level
83 # check permission at this level
86 if not HasRepoPermissionAny('repository.read', 'repository.write',
84 if not HasRepoPermissionAny('repository.read', 'repository.write',
87 'repository.admin')(dbr.repo_name,
85 'repository.admin')(dbr.repo_name,
88 'get repo check'):
86 'get repo check'):
89 continue
87 continue
90
88
91 if scmr is None:
89 if scmr is None:
92 log.error('%s this repository is present in database but it '
90 log.error('%s this repository is present in database but it '
93 'cannot be created as an scm instance',
91 'cannot be created as an scm instance',
94 dbr.repo_name)
92 dbr.repo_name)
95 continue
93 continue
96
94
97 last_change = scmr.last_change
95 last_change = scmr.last_change
98 tip = h.get_changeset_safe(scmr, 'tip')
96 tip = h.get_changeset_safe(scmr, 'tip')
99
97
100 tmp_d = {}
98 tmp_d = {}
101 tmp_d['name'] = dbr.repo_name
99 tmp_d['name'] = dbr.repo_name
102 tmp_d['name_sort'] = tmp_d['name'].lower()
100 tmp_d['name_sort'] = tmp_d['name'].lower()
103 tmp_d['description'] = dbr.description
101 tmp_d['description'] = dbr.description
104 tmp_d['description_sort'] = tmp_d['description']
102 tmp_d['description_sort'] = tmp_d['description']
105 tmp_d['last_change'] = last_change
103 tmp_d['last_change'] = last_change
106 tmp_d['last_change_sort'] = time.mktime(last_change \
104 tmp_d['last_change_sort'] = time.mktime(last_change \
107 .timetuple())
105 .timetuple())
108 tmp_d['tip'] = tip.raw_id
106 tmp_d['tip'] = tip.raw_id
109 tmp_d['tip_sort'] = tip.revision
107 tmp_d['tip_sort'] = tip.revision
110 tmp_d['rev'] = tip.revision
108 tmp_d['rev'] = tip.revision
111 tmp_d['contact'] = dbr.user.full_contact
109 tmp_d['contact'] = dbr.user.full_contact
112 tmp_d['contact_sort'] = tmp_d['contact']
110 tmp_d['contact_sort'] = tmp_d['contact']
113 tmp_d['owner_sort'] = tmp_d['contact']
111 tmp_d['owner_sort'] = tmp_d['contact']
114 tmp_d['repo_archives'] = list(scmr._get_archives())
112 tmp_d['repo_archives'] = list(scmr._get_archives())
115 tmp_d['last_msg'] = tip.message
113 tmp_d['last_msg'] = tip.message
116 tmp_d['author'] = tip.author
114 tmp_d['author'] = tip.author
117 tmp_d['dbrepo'] = dbr.get_dict()
115 tmp_d['dbrepo'] = dbr.get_dict()
118 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork \
116 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork \
119 else {}
117 else {}
120 yield tmp_d
118 yield tmp_d
121
119
122 class ScmModel(BaseModel):
120 class ScmModel(BaseModel):
123 """Generic Scm Model
121 """Generic Scm Model
124 """
122 """
125
123
126 @LazyProperty
124 @LazyProperty
127 def repos_path(self):
125 def repos_path(self):
128 """Get's the repositories root path from database
126 """Get's the repositories root path from database
129 """
127 """
130
128
131 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
129 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
132
130
133 return q.ui_value
131 return q.ui_value
134
132
135 def repo_scan(self, repos_path=None):
133 def repo_scan(self, repos_path=None):
136 """Listing of repositories in given path. This path should not be a
134 """Listing of repositories in given path. This path should not be a
137 repository itself. Return a dictionary of repository objects
135 repository itself. Return a dictionary of repository objects
138
136
139 :param repos_path: path to directory containing repositories
137 :param repos_path: path to directory containing repositories
140 """
138 """
141
139
142 log.info('scanning for repositories in %s', repos_path)
140 log.info('scanning for repositories in %s', repos_path)
143
141
144 if repos_path is None:
142 if repos_path is None:
145 repos_path = self.repos_path
143 repos_path = self.repos_path
146
144
147 baseui = make_ui('db')
145 baseui = make_ui('db')
148 repos_list = {}
146 repos_list = {}
149
147
150 for name, path in get_filesystem_repos(repos_path, recursive=True):
148 for name, path in get_filesystem_repos(repos_path, recursive=True):
151 try:
149 try:
152 if name in repos_list:
150 if name in repos_list:
153 raise RepositoryError('Duplicate repository name %s '
151 raise RepositoryError('Duplicate repository name %s '
154 'found in %s' % (name, path))
152 'found in %s' % (name, path))
155 else:
153 else:
156
154
157 klass = get_backend(path[0])
155 klass = get_backend(path[0])
158
156
159 if path[0] == 'hg' and path[0] in BACKENDS.keys():
157 if path[0] == 'hg' and path[0] in BACKENDS.keys():
160
158
161 # for mercurial we need to have an str path
159 # for mercurial we need to have an str path
162 repos_list[name] = klass(safe_str(path[1]),
160 repos_list[name] = klass(safe_str(path[1]),
163 baseui=baseui)
161 baseui=baseui)
164
162
165 if path[0] == 'git' and path[0] in BACKENDS.keys():
163 if path[0] == 'git' and path[0] in BACKENDS.keys():
166 repos_list[name] = klass(path[1])
164 repos_list[name] = klass(path[1])
167 except OSError:
165 except OSError:
168 continue
166 continue
169
167
170 return repos_list
168 return repos_list
171
169
172 def get_repos(self, all_repos=None, sort_key=None):
170 def get_repos(self, all_repos=None, sort_key=None):
173 """
171 """
174 Get all repos from db and for each repo create it's
172 Get all repos from db and for each repo create it's
175 backend instance and fill that backed with information from database
173 backend instance and fill that backed with information from database
176
174
177 :param all_repos: list of repository names as strings
175 :param all_repos: list of repository names as strings
178 give specific repositories list, good for filtering
176 give specific repositories list, good for filtering
179 """
177 """
180 if all_repos is None:
178 if all_repos is None:
181 all_repos = self.sa.query(Repository)\
179 all_repos = self.sa.query(Repository)\
182 .filter(Repository.group_id == None)\
180 .filter(Repository.group_id == None)\
183 .order_by(Repository.repo_name).all()
181 .order_by(Repository.repo_name).all()
184
182
185 repo_iter = CachedRepoList(all_repos, repos_path=self.repos_path,
183 repo_iter = CachedRepoList(all_repos, repos_path=self.repos_path,
186 order_by=sort_key)
184 order_by=sort_key)
187
185
188 return repo_iter
186 return repo_iter
189
187
190 def mark_for_invalidation(self, repo_name):
188 def mark_for_invalidation(self, repo_name):
191 """Puts cache invalidation task into db for
189 """Puts cache invalidation task into db for
192 further global cache invalidation
190 further global cache invalidation
193
191
194 :param repo_name: this repo that should invalidation take place
192 :param repo_name: this repo that should invalidation take place
195 """
193 """
196
194
197 log.debug('marking %s for invalidation', repo_name)
195 log.debug('marking %s for invalidation', repo_name)
198 cache = self.sa.query(CacheInvalidation)\
196 cache = self.sa.query(CacheInvalidation)\
199 .filter(CacheInvalidation.cache_key == repo_name).scalar()
197 .filter(CacheInvalidation.cache_key == repo_name).scalar()
200
198
201 if cache:
199 if cache:
202 # mark this cache as inactive
200 # mark this cache as inactive
203 cache.cache_active = False
201 cache.cache_active = False
204 else:
202 else:
205 log.debug('cache key not found in invalidation db -> creating one')
203 log.debug('cache key not found in invalidation db -> creating one')
206 cache = CacheInvalidation(repo_name)
204 cache = CacheInvalidation(repo_name)
207
205
208 try:
206 try:
209 self.sa.add(cache)
207 self.sa.add(cache)
210 self.sa.commit()
208 self.sa.commit()
211 except (DatabaseError,):
209 except (DatabaseError,):
212 log.error(traceback.format_exc())
210 log.error(traceback.format_exc())
213 self.sa.rollback()
211 self.sa.rollback()
214
212
215 def toggle_following_repo(self, follow_repo_id, user_id):
213 def toggle_following_repo(self, follow_repo_id, user_id):
216
214
217 f = self.sa.query(UserFollowing)\
215 f = self.sa.query(UserFollowing)\
218 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
216 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
219 .filter(UserFollowing.user_id == user_id).scalar()
217 .filter(UserFollowing.user_id == user_id).scalar()
220
218
221 if f is not None:
219 if f is not None:
222
220
223 try:
221 try:
224 self.sa.delete(f)
222 self.sa.delete(f)
225 self.sa.commit()
223 self.sa.commit()
226 action_logger(UserTemp(user_id),
224 action_logger(UserTemp(user_id),
227 'stopped_following_repo',
225 'stopped_following_repo',
228 RepoTemp(follow_repo_id))
226 RepoTemp(follow_repo_id))
229 return
227 return
230 except:
228 except:
231 log.error(traceback.format_exc())
229 log.error(traceback.format_exc())
232 self.sa.rollback()
230 self.sa.rollback()
233 raise
231 raise
234
232
235 try:
233 try:
236 f = UserFollowing()
234 f = UserFollowing()
237 f.user_id = user_id
235 f.user_id = user_id
238 f.follows_repo_id = follow_repo_id
236 f.follows_repo_id = follow_repo_id
239 self.sa.add(f)
237 self.sa.add(f)
240 self.sa.commit()
238 self.sa.commit()
241 action_logger(UserTemp(user_id),
239 action_logger(UserTemp(user_id),
242 'started_following_repo',
240 'started_following_repo',
243 RepoTemp(follow_repo_id))
241 RepoTemp(follow_repo_id))
244 except:
242 except:
245 log.error(traceback.format_exc())
243 log.error(traceback.format_exc())
246 self.sa.rollback()
244 self.sa.rollback()
247 raise
245 raise
248
246
249 def toggle_following_user(self, follow_user_id, user_id):
247 def toggle_following_user(self, follow_user_id, user_id):
250 f = self.sa.query(UserFollowing)\
248 f = self.sa.query(UserFollowing)\
251 .filter(UserFollowing.follows_user_id == follow_user_id)\
249 .filter(UserFollowing.follows_user_id == follow_user_id)\
252 .filter(UserFollowing.user_id == user_id).scalar()
250 .filter(UserFollowing.user_id == user_id).scalar()
253
251
254 if f is not None:
252 if f is not None:
255 try:
253 try:
256 self.sa.delete(f)
254 self.sa.delete(f)
257 self.sa.commit()
255 self.sa.commit()
258 return
256 return
259 except:
257 except:
260 log.error(traceback.format_exc())
258 log.error(traceback.format_exc())
261 self.sa.rollback()
259 self.sa.rollback()
262 raise
260 raise
263
261
264 try:
262 try:
265 f = UserFollowing()
263 f = UserFollowing()
266 f.user_id = user_id
264 f.user_id = user_id
267 f.follows_user_id = follow_user_id
265 f.follows_user_id = follow_user_id
268 self.sa.add(f)
266 self.sa.add(f)
269 self.sa.commit()
267 self.sa.commit()
270 except:
268 except:
271 log.error(traceback.format_exc())
269 log.error(traceback.format_exc())
272 self.sa.rollback()
270 self.sa.rollback()
273 raise
271 raise
274
272
275 def is_following_repo(self, repo_name, user_id, cache=False):
273 def is_following_repo(self, repo_name, user_id, cache=False):
276 r = self.sa.query(Repository)\
274 r = self.sa.query(Repository)\
277 .filter(Repository.repo_name == repo_name).scalar()
275 .filter(Repository.repo_name == repo_name).scalar()
278
276
279 f = self.sa.query(UserFollowing)\
277 f = self.sa.query(UserFollowing)\
280 .filter(UserFollowing.follows_repository == r)\
278 .filter(UserFollowing.follows_repository == r)\
281 .filter(UserFollowing.user_id == user_id).scalar()
279 .filter(UserFollowing.user_id == user_id).scalar()
282
280
283 return f is not None
281 return f is not None
284
282
285 def is_following_user(self, username, user_id, cache=False):
283 def is_following_user(self, username, user_id, cache=False):
286 u = UserModel(self.sa).get_by_username(username)
284 u = User.get_by_username(username)
287
285
288 f = self.sa.query(UserFollowing)\
286 f = self.sa.query(UserFollowing)\
289 .filter(UserFollowing.follows_user == u)\
287 .filter(UserFollowing.follows_user == u)\
290 .filter(UserFollowing.user_id == user_id).scalar()
288 .filter(UserFollowing.user_id == user_id).scalar()
291
289
292 return f is not None
290 return f is not None
293
291
294 def get_followers(self, repo_id):
292 def get_followers(self, repo_id):
295 if not isinstance(repo_id, int):
293 if not isinstance(repo_id, int):
296 repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id')
294 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
297
295
298 return self.sa.query(UserFollowing)\
296 return self.sa.query(UserFollowing)\
299 .filter(UserFollowing.follows_repo_id == repo_id).count()
297 .filter(UserFollowing.follows_repo_id == repo_id).count()
300
298
301 def get_forks(self, repo_id):
299 def get_forks(self, repo_id):
302 if not isinstance(repo_id, int):
300 if not isinstance(repo_id, int):
303 repo_id = getattr(Repository.by_repo_name(repo_id), 'repo_id')
301 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
304
302
305 return self.sa.query(Repository)\
303 return self.sa.query(Repository)\
306 .filter(Repository.fork_id == repo_id).count()
304 .filter(Repository.fork_id == repo_id).count()
307
305
308 def pull_changes(self, repo_name, username):
306 def pull_changes(self, repo_name, username):
309 dbrepo = Repository.by_repo_name(repo_name)
307 dbrepo = Repository.get_by_repo_name(repo_name)
310 clone_uri = dbrepo.clone_uri
308 clone_uri = dbrepo.clone_uri
311 if not clone_uri:
309 if not clone_uri:
312 raise Exception("This repository doesn't have a clone uri")
310 raise Exception("This repository doesn't have a clone uri")
313
311
314 repo = dbrepo.scm_instance
312 repo = dbrepo.scm_instance
315 try:
313 try:
316 extras = {'ip': '',
314 extras = {'ip': '',
317 'username': username,
315 'username': username,
318 'action': 'push_remote',
316 'action': 'push_remote',
319 'repository': repo_name}
317 'repository': repo_name}
320
318
321 #inject ui extra param to log this action via push logger
319 #inject ui extra param to log this action via push logger
322 for k, v in extras.items():
320 for k, v in extras.items():
323 repo._repo.ui.setconfig('rhodecode_extras', k, v)
321 repo._repo.ui.setconfig('rhodecode_extras', k, v)
324
322
325 repo.pull(clone_uri)
323 repo.pull(clone_uri)
326 self.mark_for_invalidation(repo_name)
324 self.mark_for_invalidation(repo_name)
327 except:
325 except:
328 log.error(traceback.format_exc())
326 log.error(traceback.format_exc())
329 raise
327 raise
330
328
331 def commit_change(self, repo, repo_name, cs, user, author, message, content,
329 def commit_change(self, repo, repo_name, cs, user, author, message, content,
332 f_path):
330 f_path):
333
331
334 if repo.alias == 'hg':
332 if repo.alias == 'hg':
335 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
333 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
336 elif repo.alias == 'git':
334 elif repo.alias == 'git':
337 from vcs.backends.git import GitInMemoryChangeset as IMC
335 from vcs.backends.git import GitInMemoryChangeset as IMC
338
336
339 # decoding here will force that we have proper encoded values
337 # decoding here will force that we have proper encoded values
340 # in any other case this will throw exceptions and deny commit
338 # in any other case this will throw exceptions and deny commit
341 content = safe_str(content)
339 content = safe_str(content)
342 message = safe_str(message)
340 message = safe_str(message)
343 path = safe_str(f_path)
341 path = safe_str(f_path)
344 author = safe_str(author)
342 author = safe_str(author)
345 m = IMC(repo)
343 m = IMC(repo)
346 m.change(FileNode(path, content))
344 m.change(FileNode(path, content))
347 tip = m.commit(message=message,
345 tip = m.commit(message=message,
348 author=author,
346 author=author,
349 parents=[cs], branch=cs.branch)
347 parents=[cs], branch=cs.branch)
350
348
351 new_cs = tip.short_id
349 new_cs = tip.short_id
352 action = 'push_local:%s' % new_cs
350 action = 'push_local:%s' % new_cs
353
351
354 action_logger(user, action, repo_name)
352 action_logger(user, action, repo_name)
355
353
356 self.mark_for_invalidation(repo_name)
354 self.mark_for_invalidation(repo_name)
357
355
358 def create_node(self, repo, repo_name, cs, user, author, message, content,
356 def create_node(self, repo, repo_name, cs, user, author, message, content,
359 f_path):
357 f_path):
360 if repo.alias == 'hg':
358 if repo.alias == 'hg':
361 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
359 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
362 elif repo.alias == 'git':
360 elif repo.alias == 'git':
363 from vcs.backends.git import GitInMemoryChangeset as IMC
361 from vcs.backends.git import GitInMemoryChangeset as IMC
364 # decoding here will force that we have proper encoded values
362 # decoding here will force that we have proper encoded values
365 # in any other case this will throw exceptions and deny commit
363 # in any other case this will throw exceptions and deny commit
366
364
367 if isinstance(content,(basestring,)):
365 if isinstance(content, (basestring,)):
368 content = safe_str(content)
366 content = safe_str(content)
369 elif isinstance(content,file):
367 elif isinstance(content, file):
370 content = content.read()
368 content = content.read()
371
369
372 message = safe_str(message)
370 message = safe_str(message)
373 path = safe_str(f_path)
371 path = safe_str(f_path)
374 author = safe_str(author)
372 author = safe_str(author)
375 m = IMC(repo)
373 m = IMC(repo)
376
374
377 if isinstance(cs, EmptyChangeset):
375 if isinstance(cs, EmptyChangeset):
378 # Emptychangeset means we we're editing empty repository
376 # Emptychangeset means we we're editing empty repository
379 parents = None
377 parents = None
380 else:
378 else:
381 parents = [cs]
379 parents = [cs]
382
380
383 m.add(FileNode(path, content=content))
381 m.add(FileNode(path, content=content))
384 tip = m.commit(message=message,
382 tip = m.commit(message=message,
385 author=author,
383 author=author,
386 parents=parents, branch=cs.branch)
384 parents=parents, branch=cs.branch)
387 new_cs = tip.short_id
385 new_cs = tip.short_id
388 action = 'push_local:%s' % new_cs
386 action = 'push_local:%s' % new_cs
389
387
390 action_logger(user, action, repo_name)
388 action_logger(user, action, repo_name)
391
389
392 self.mark_for_invalidation(repo_name)
390 self.mark_for_invalidation(repo_name)
393
391
394
392
395 def get_unread_journal(self):
393 def get_unread_journal(self):
396 return self.sa.query(UserLog).count()
394 return self.sa.query(UserLog).count()
397
395
398 def _should_invalidate(self, repo_name):
396 def _should_invalidate(self, repo_name):
399 """Looks up database for invalidation signals for this repo_name
397 """Looks up database for invalidation signals for this repo_name
400
398
401 :param repo_name:
399 :param repo_name:
402 """
400 """
403
401
404 ret = self.sa.query(CacheInvalidation)\
402 ret = self.sa.query(CacheInvalidation)\
405 .filter(CacheInvalidation.cache_key == repo_name)\
403 .filter(CacheInvalidation.cache_key == repo_name)\
406 .filter(CacheInvalidation.cache_active == False)\
404 .filter(CacheInvalidation.cache_active == False)\
407 .scalar()
405 .scalar()
408
406
409 return ret
407 return ret
410
408
@@ -1,261 +1,261 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 from rhodecode.tests import *
2 from rhodecode.tests import *
3 from rhodecode.model.db import User
3 from rhodecode.model.db import User
4 from rhodecode.lib import generate_api_key
4 from rhodecode.lib import generate_api_key
5 from rhodecode.lib.auth import check_password
5 from rhodecode.lib.auth import check_password
6
6
7
7
8 class TestLoginController(TestController):
8 class TestLoginController(TestController):
9
9
10 def test_index(self):
10 def test_index(self):
11 response = self.app.get(url(controller='login', action='index'))
11 response = self.app.get(url(controller='login', action='index'))
12 self.assertEqual(response.status, '200 OK')
12 self.assertEqual(response.status, '200 OK')
13 # Test response...
13 # Test response...
14
14
15 def test_login_admin_ok(self):
15 def test_login_admin_ok(self):
16 response = self.app.post(url(controller='login', action='index'),
16 response = self.app.post(url(controller='login', action='index'),
17 {'username':'test_admin',
17 {'username':'test_admin',
18 'password':'test12'})
18 'password':'test12'})
19 self.assertEqual(response.status, '302 Found')
19 self.assertEqual(response.status, '302 Found')
20 self.assertEqual(response.session['rhodecode_user'].username ,
20 self.assertEqual(response.session['rhodecode_user'].username ,
21 'test_admin')
21 'test_admin')
22 response = response.follow()
22 response = response.follow()
23 self.assertTrue('%s repository' % HG_REPO in response.body)
23 self.assertTrue('%s repository' % HG_REPO in response.body)
24
24
25 def test_login_regular_ok(self):
25 def test_login_regular_ok(self):
26 response = self.app.post(url(controller='login', action='index'),
26 response = self.app.post(url(controller='login', action='index'),
27 {'username':'test_regular',
27 {'username':'test_regular',
28 'password':'test12'})
28 'password':'test12'})
29
29
30 self.assertEqual(response.status, '302 Found')
30 self.assertEqual(response.status, '302 Found')
31 self.assertEqual(response.session['rhodecode_user'].username ,
31 self.assertEqual(response.session['rhodecode_user'].username ,
32 'test_regular')
32 'test_regular')
33 response = response.follow()
33 response = response.follow()
34 self.assertTrue('%s repository' % HG_REPO in response.body)
34 self.assertTrue('%s repository' % HG_REPO in response.body)
35 self.assertTrue('<a title="Admin" href="/_admin">' not in response.body)
35 self.assertTrue('<a title="Admin" href="/_admin">' not in response.body)
36
36
37 def test_login_ok_came_from(self):
37 def test_login_ok_came_from(self):
38 test_came_from = '/_admin/users'
38 test_came_from = '/_admin/users'
39 response = self.app.post(url(controller='login', action='index',
39 response = self.app.post(url(controller='login', action='index',
40 came_from=test_came_from),
40 came_from=test_came_from),
41 {'username':'test_admin',
41 {'username':'test_admin',
42 'password':'test12'})
42 'password':'test12'})
43 self.assertEqual(response.status, '302 Found')
43 self.assertEqual(response.status, '302 Found')
44 response = response.follow()
44 response = response.follow()
45
45
46 self.assertEqual(response.status, '200 OK')
46 self.assertEqual(response.status, '200 OK')
47 self.assertTrue('Users administration' in response.body)
47 self.assertTrue('Users administration' in response.body)
48
48
49
49
50 def test_login_short_password(self):
50 def test_login_short_password(self):
51 response = self.app.post(url(controller='login', action='index'),
51 response = self.app.post(url(controller='login', action='index'),
52 {'username':'test_admin',
52 {'username':'test_admin',
53 'password':'as'})
53 'password':'as'})
54 self.assertEqual(response.status, '200 OK')
54 self.assertEqual(response.status, '200 OK')
55
55
56 self.assertTrue('Enter 3 characters or more' in response.body)
56 self.assertTrue('Enter 3 characters or more' in response.body)
57
57
58 def test_login_wrong_username_password(self):
58 def test_login_wrong_username_password(self):
59 response = self.app.post(url(controller='login', action='index'),
59 response = self.app.post(url(controller='login', action='index'),
60 {'username':'error',
60 {'username':'error',
61 'password':'test12'})
61 'password':'test12'})
62 self.assertEqual(response.status , '200 OK')
62 self.assertEqual(response.status , '200 OK')
63
63
64 self.assertTrue('invalid user name' in response.body)
64 self.assertTrue('invalid user name' in response.body)
65 self.assertTrue('invalid password' in response.body)
65 self.assertTrue('invalid password' in response.body)
66
66
67 #==========================================================================
67 #==========================================================================
68 # REGISTRATIONS
68 # REGISTRATIONS
69 #==========================================================================
69 #==========================================================================
70 def test_register(self):
70 def test_register(self):
71 response = self.app.get(url(controller='login', action='register'))
71 response = self.app.get(url(controller='login', action='register'))
72 self.assertTrue('Sign Up to RhodeCode' in response.body)
72 self.assertTrue('Sign Up to RhodeCode' in response.body)
73
73
74 def test_register_err_same_username(self):
74 def test_register_err_same_username(self):
75 response = self.app.post(url(controller='login', action='register'),
75 response = self.app.post(url(controller='login', action='register'),
76 {'username':'test_admin',
76 {'username':'test_admin',
77 'password':'test12',
77 'password':'test12',
78 'password_confirmation':'test12',
78 'password_confirmation':'test12',
79 'email':'goodmail@domain.com',
79 'email':'goodmail@domain.com',
80 'name':'test',
80 'name':'test',
81 'lastname':'test'})
81 'lastname':'test'})
82
82
83 self.assertEqual(response.status , '200 OK')
83 self.assertEqual(response.status , '200 OK')
84 self.assertTrue('This username already exists' in response.body)
84 self.assertTrue('This username already exists' in response.body)
85
85
86 def test_register_err_same_email(self):
86 def test_register_err_same_email(self):
87 response = self.app.post(url(controller='login', action='register'),
87 response = self.app.post(url(controller='login', action='register'),
88 {'username':'test_admin_0',
88 {'username':'test_admin_0',
89 'password':'test12',
89 'password':'test12',
90 'password_confirmation':'test12',
90 'password_confirmation':'test12',
91 'email':'test_admin@mail.com',
91 'email':'test_admin@mail.com',
92 'name':'test',
92 'name':'test',
93 'lastname':'test'})
93 'lastname':'test'})
94
94
95 self.assertEqual(response.status , '200 OK')
95 self.assertEqual(response.status , '200 OK')
96 assert 'This e-mail address is already taken' in response.body
96 assert 'This e-mail address is already taken' in response.body
97
97
98 def test_register_err_same_email_case_sensitive(self):
98 def test_register_err_same_email_case_sensitive(self):
99 response = self.app.post(url(controller='login', action='register'),
99 response = self.app.post(url(controller='login', action='register'),
100 {'username':'test_admin_1',
100 {'username':'test_admin_1',
101 'password':'test12',
101 'password':'test12',
102 'password_confirmation':'test12',
102 'password_confirmation':'test12',
103 'email':'TesT_Admin@mail.COM',
103 'email':'TesT_Admin@mail.COM',
104 'name':'test',
104 'name':'test',
105 'lastname':'test'})
105 'lastname':'test'})
106 self.assertEqual(response.status , '200 OK')
106 self.assertEqual(response.status , '200 OK')
107 assert 'This e-mail address is already taken' in response.body
107 assert 'This e-mail address is already taken' in response.body
108
108
109 def test_register_err_wrong_data(self):
109 def test_register_err_wrong_data(self):
110 response = self.app.post(url(controller='login', action='register'),
110 response = self.app.post(url(controller='login', action='register'),
111 {'username':'xs',
111 {'username':'xs',
112 'password':'test',
112 'password':'test',
113 'password_confirmation':'test',
113 'password_confirmation':'test',
114 'email':'goodmailm',
114 'email':'goodmailm',
115 'name':'test',
115 'name':'test',
116 'lastname':'test'})
116 'lastname':'test'})
117 self.assertEqual(response.status , '200 OK')
117 self.assertEqual(response.status , '200 OK')
118 assert 'An email address must contain a single @' in response.body
118 assert 'An email address must contain a single @' in response.body
119 assert 'Enter a value 6 characters long or more' in response.body
119 assert 'Enter a value 6 characters long or more' in response.body
120
120
121
121
122 def test_register_err_username(self):
122 def test_register_err_username(self):
123 response = self.app.post(url(controller='login', action='register'),
123 response = self.app.post(url(controller='login', action='register'),
124 {'username':'error user',
124 {'username':'error user',
125 'password':'test12',
125 'password':'test12',
126 'password_confirmation':'test12',
126 'password_confirmation':'test12',
127 'email':'goodmailm',
127 'email':'goodmailm',
128 'name':'test',
128 'name':'test',
129 'lastname':'test'})
129 'lastname':'test'})
130
130
131 self.assertEqual(response.status , '200 OK')
131 self.assertEqual(response.status , '200 OK')
132 assert 'An email address must contain a single @' in response.body
132 assert 'An email address must contain a single @' in response.body
133 assert ('Username may only contain '
133 assert ('Username may only contain '
134 'alphanumeric characters underscores, '
134 'alphanumeric characters underscores, '
135 'periods or dashes and must begin with '
135 'periods or dashes and must begin with '
136 'alphanumeric character') in response.body
136 'alphanumeric character') in response.body
137
137
138 def test_register_err_case_sensitive(self):
138 def test_register_err_case_sensitive(self):
139 response = self.app.post(url(controller='login', action='register'),
139 response = self.app.post(url(controller='login', action='register'),
140 {'username':'Test_Admin',
140 {'username':'Test_Admin',
141 'password':'test12',
141 'password':'test12',
142 'password_confirmation':'test12',
142 'password_confirmation':'test12',
143 'email':'goodmailm',
143 'email':'goodmailm',
144 'name':'test',
144 'name':'test',
145 'lastname':'test'})
145 'lastname':'test'})
146
146
147 self.assertEqual(response.status , '200 OK')
147 self.assertEqual(response.status , '200 OK')
148 assert 'An email address must contain a single @' in response.body
148 assert 'An email address must contain a single @' in response.body
149 assert 'This username already exists' in response.body
149 assert 'This username already exists' in response.body
150
150
151
151
152
152
153 def test_register_special_chars(self):
153 def test_register_special_chars(self):
154 response = self.app.post(url(controller='login', action='register'),
154 response = self.app.post(url(controller='login', action='register'),
155 {'username':'xxxaxn',
155 {'username':'xxxaxn',
156 'password':'Δ…Δ‡ΕΊΕΌΔ…Ε›Ε›Ε›Ε›',
156 'password':'Δ…Δ‡ΕΊΕΌΔ…Ε›Ε›Ε›Ε›',
157 'password_confirmation':'Δ…Δ‡ΕΊΕΌΔ…Ε›Ε›Ε›Ε›',
157 'password_confirmation':'Δ…Δ‡ΕΊΕΌΔ…Ε›Ε›Ε›Ε›',
158 'email':'goodmailm@test.plx',
158 'email':'goodmailm@test.plx',
159 'name':'test',
159 'name':'test',
160 'lastname':'test'})
160 'lastname':'test'})
161
161
162 self.assertEqual(response.status , '200 OK')
162 self.assertEqual(response.status , '200 OK')
163 assert 'Invalid characters in password' in response.body
163 assert 'Invalid characters in password' in response.body
164
164
165
165
166 def test_register_password_mismatch(self):
166 def test_register_password_mismatch(self):
167 response = self.app.post(url(controller='login', action='register'),
167 response = self.app.post(url(controller='login', action='register'),
168 {'username':'xs',
168 {'username':'xs',
169 'password':'123qwe',
169 'password':'123qwe',
170 'password_confirmation':'qwe123',
170 'password_confirmation':'qwe123',
171 'email':'goodmailm@test.plxa',
171 'email':'goodmailm@test.plxa',
172 'name':'test',
172 'name':'test',
173 'lastname':'test'})
173 'lastname':'test'})
174
174
175 self.assertEqual(response.status , '200 OK')
175 self.assertEqual(response.status , '200 OK')
176 assert 'Passwords do not match' in response.body
176 assert 'Passwords do not match' in response.body
177
177
178 def test_register_ok(self):
178 def test_register_ok(self):
179 username = 'test_regular4'
179 username = 'test_regular4'
180 password = 'qweqwe'
180 password = 'qweqwe'
181 email = 'marcin@test.com'
181 email = 'marcin@test.com'
182 name = 'testname'
182 name = 'testname'
183 lastname = 'testlastname'
183 lastname = 'testlastname'
184
184
185 response = self.app.post(url(controller='login', action='register'),
185 response = self.app.post(url(controller='login', action='register'),
186 {'username':username,
186 {'username':username,
187 'password':password,
187 'password':password,
188 'password_confirmation':password,
188 'password_confirmation':password,
189 'email':email,
189 'email':email,
190 'name':name,
190 'name':name,
191 'lastname':lastname})
191 'lastname':lastname})
192 self.assertEqual(response.status , '302 Found')
192 self.assertEqual(response.status , '302 Found')
193 assert 'You have successfully registered into rhodecode' in response.session['flash'][0], 'No flash message about user registration'
193 assert 'You have successfully registered into rhodecode' in response.session['flash'][0], 'No flash message about user registration'
194
194
195 ret = self.sa.query(User).filter(User.username == 'test_regular4').one()
195 ret = self.sa.query(User).filter(User.username == 'test_regular4').one()
196 assert ret.username == username , 'field mismatch %s %s' % (ret.username, username)
196 assert ret.username == username , 'field mismatch %s %s' % (ret.username, username)
197 assert check_password(password, ret.password) == True , 'password mismatch'
197 assert check_password(password, ret.password) == True , 'password mismatch'
198 assert ret.email == email , 'field mismatch %s %s' % (ret.email, email)
198 assert ret.email == email , 'field mismatch %s %s' % (ret.email, email)
199 assert ret.name == name , 'field mismatch %s %s' % (ret.name, name)
199 assert ret.name == name , 'field mismatch %s %s' % (ret.name, name)
200 assert ret.lastname == lastname , 'field mismatch %s %s' % (ret.lastname, lastname)
200 assert ret.lastname == lastname , 'field mismatch %s %s' % (ret.lastname, lastname)
201
201
202
202
203 def test_forgot_password_wrong_mail(self):
203 def test_forgot_password_wrong_mail(self):
204 response = self.app.post(url(controller='login', action='password_reset'),
204 response = self.app.post(url(controller='login', action='password_reset'),
205 {'email':'marcin@wrongmail.org', })
205 {'email':'marcin@wrongmail.org', })
206
206
207 assert "This e-mail address doesn't exist" in response.body, 'Missing error message about wrong email'
207 assert "This e-mail address doesn't exist" in response.body, 'Missing error message about wrong email'
208
208
209 def test_forgot_password(self):
209 def test_forgot_password(self):
210 response = self.app.get(url(controller='login',
210 response = self.app.get(url(controller='login',
211 action='password_reset'))
211 action='password_reset'))
212 self.assertEqual(response.status , '200 OK')
212 self.assertEqual(response.status , '200 OK')
213
213
214 username = 'test_password_reset_1'
214 username = 'test_password_reset_1'
215 password = 'qweqwe'
215 password = 'qweqwe'
216 email = 'marcin@python-works.com'
216 email = 'marcin@python-works.com'
217 name = 'passwd'
217 name = 'passwd'
218 lastname = 'reset'
218 lastname = 'reset'
219
219
220 new = User()
220 new = User()
221 new.username = username
221 new.username = username
222 new.password = password
222 new.password = password
223 new.email = email
223 new.email = email
224 new.name = name
224 new.name = name
225 new.lastname = lastname
225 new.lastname = lastname
226 new.api_key = generate_api_key(username)
226 new.api_key = generate_api_key(username)
227 self.sa.add(new)
227 self.sa.add(new)
228 self.sa.commit()
228 self.sa.commit()
229
229
230 response = self.app.post(url(controller='login',
230 response = self.app.post(url(controller='login',
231 action='password_reset'),
231 action='password_reset'),
232 {'email':email, })
232 {'email':email, })
233
233
234 self.checkSessionFlash(response, 'Your password reset link was sent')
234 self.checkSessionFlash(response, 'Your password reset link was sent')
235
235
236 response = response.follow()
236 response = response.follow()
237
237
238 # BAD KEY
238 # BAD KEY
239
239
240 key = "bad"
240 key = "bad"
241 response = self.app.get(url(controller='login',
241 response = self.app.get(url(controller='login',
242 action='password_reset_confirmation',
242 action='password_reset_confirmation',
243 key=key))
243 key=key))
244 self.assertEqual(response.status, '302 Found')
244 self.assertEqual(response.status, '302 Found')
245 self.assertTrue(response.location.endswith(url('reset_password')))
245 self.assertTrue(response.location.endswith(url('reset_password')))
246
246
247 # GOOD KEY
247 # GOOD KEY
248
248
249 key = User.by_username(username).api_key
249 key = User.get_by_username(username).api_key
250
250
251 response = self.app.get(url(controller='login',
251 response = self.app.get(url(controller='login',
252 action='password_reset_confirmation',
252 action='password_reset_confirmation',
253 key=key))
253 key=key))
254 self.assertEqual(response.status, '302 Found')
254 self.assertEqual(response.status, '302 Found')
255 self.assertTrue(response.location.endswith(url('login_home')))
255 self.assertTrue(response.location.endswith(url('login_home')))
256
256
257 self.checkSessionFlash(response,
257 self.checkSessionFlash(response,
258 ('Your password reset was successful, '
258 ('Your password reset was successful, '
259 'new password has been sent to your email'))
259 'new password has been sent to your email'))
260
260
261 response = response.follow()
261 response = response.follow()
@@ -1,47 +1,47 b''
1 from rhodecode.tests import *
1 from rhodecode.tests import *
2 from rhodecode.model.db import Repository
2 from rhodecode.model.db import Repository
3 from rhodecode.lib.utils import invalidate_cache
3 from rhodecode.lib.utils import invalidate_cache
4
4
5 class TestSummaryController(TestController):
5 class TestSummaryController(TestController):
6
6
7 def test_index(self):
7 def test_index(self):
8 self.log_user()
8 self.log_user()
9 response = self.app.get(url(controller='summary',
9 response = self.app.get(url(controller='summary',
10 action='index', repo_name=HG_REPO))
10 action='index', repo_name=HG_REPO))
11
11
12 #repo type
12 #repo type
13 self.assertTrue("""<img style="margin-bottom:2px" class="icon" """
13 self.assertTrue("""<img style="margin-bottom:2px" class="icon" """
14 """title="Mercurial repository" alt="Mercurial """
14 """title="Mercurial repository" alt="Mercurial """
15 """repository" src="/images/icons/hgicon.png"/>"""
15 """repository" src="/images/icons/hgicon.png"/>"""
16 in response.body)
16 in response.body)
17 self.assertTrue("""<img style="margin-bottom:2px" class="icon" """
17 self.assertTrue("""<img style="margin-bottom:2px" class="icon" """
18 """title="public repository" alt="public """
18 """title="public repository" alt="public """
19 """repository" src="/images/icons/lock_open.png"/>"""
19 """repository" src="/images/icons/lock_open.png"/>"""
20 in response.body)
20 in response.body)
21
21
22 #codes stats
22 #codes stats
23 self._enable_stats()
23 self._enable_stats()
24
24
25
25
26 invalidate_cache('get_repo_cached_%s' % HG_REPO)
26 invalidate_cache('get_repo_cached_%s' % HG_REPO)
27 response = self.app.get(url(controller='summary', action='index',
27 response = self.app.get(url(controller='summary', action='index',
28 repo_name=HG_REPO))
28 repo_name=HG_REPO))
29
29
30 self.assertTrue("""var data = {"py": {"count": 42, "desc": """
30 self.assertTrue("""var data = {"py": {"count": 42, "desc": """
31 """["Python"]}, "rst": {"count": 11, "desc": """
31 """["Python"]}, "rst": {"count": 11, "desc": """
32 """["Rst"]}, "sh": {"count": 2, "desc": ["Bash"]}, """
32 """["Rst"]}, "sh": {"count": 2, "desc": ["Bash"]}, """
33 """"makefile": {"count": 1, "desc": ["Makefile", """
33 """"makefile": {"count": 1, "desc": ["Makefile", """
34 """"Makefile"]}, "cfg": {"count": 1, "desc": ["Ini"]},"""
34 """"Makefile"]}, "cfg": {"count": 1, "desc": ["Ini"]},"""
35 """ "css": {"count": 1, "desc": ["Css"]}, "bat": """
35 """ "css": {"count": 1, "desc": ["Css"]}, "bat": """
36 """{"count": 1, "desc": ["Batch"]}};"""
36 """{"count": 1, "desc": ["Batch"]}};"""
37 in response.body)
37 in response.body)
38
38
39 # clone url...
39 # clone url...
40 self.assertTrue("""<input type="text" id="clone_url" readonly="readonly" value="hg clone http://test_admin@localhost:80/%s" size="70"/>""" % HG_REPO in response.body)
40 self.assertTrue("""<input type="text" id="clone_url" readonly="readonly" value="hg clone http://test_admin@localhost:80/%s" size="70"/>""" % HG_REPO in response.body)
41
41
42
42
43 def _enable_stats(self):
43 def _enable_stats(self):
44 r = Repository.by_repo_name(HG_REPO)
44 r = Repository.get_by_repo_name(HG_REPO)
45 r.enable_statistics = True
45 r.enable_statistics = True
46 self.sa.add(r)
46 self.sa.add(r)
47 self.sa.commit()
47 self.sa.commit()
@@ -1,400 +1,400 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.tests.test_hg_operations
3 rhodecode.tests.test_hg_operations
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Test suite for making push/pull operations
6 Test suite for making push/pull operations
7
7
8 :created_on: Dec 30, 2010
8 :created_on: Dec 30, 2010
9 :copyright: (c) 2010 by marcink.
9 :copyright: (c) 2010 by marcink.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
11 """
11 """
12
12
13 import os
13 import os
14 import time
14 import time
15 import sys
15 import sys
16 import shutil
16 import shutil
17 import logging
17 import logging
18
18
19 from os.path import join as jn
19 from os.path import join as jn
20 from os.path import dirname as dn
20 from os.path import dirname as dn
21
21
22 from tempfile import _RandomNameSequence
22 from tempfile import _RandomNameSequence
23 from subprocess import Popen, PIPE
23 from subprocess import Popen, PIPE
24
24
25 from paste.deploy import appconfig
25 from paste.deploy import appconfig
26 from pylons import config
26 from pylons import config
27 from sqlalchemy import engine_from_config
27 from sqlalchemy import engine_from_config
28
28
29 from rhodecode.lib.utils import add_cache
29 from rhodecode.lib.utils import add_cache
30 from rhodecode.model import init_model
30 from rhodecode.model import init_model
31 from rhodecode.model import meta
31 from rhodecode.model import meta
32 from rhodecode.model.db import User, Repository, UserLog
32 from rhodecode.model.db import User, Repository, UserLog
33 from rhodecode.lib.auth import get_crypt_password
33 from rhodecode.lib.auth import get_crypt_password
34
34
35 from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO
35 from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO
36 from rhodecode.config.environment import load_environment
36 from rhodecode.config.environment import load_environment
37
37
38 rel_path = dn(dn(dn(os.path.abspath(__file__))))
38 rel_path = dn(dn(dn(os.path.abspath(__file__))))
39 conf = appconfig('config:development.ini', relative_to=rel_path)
39 conf = appconfig('config:development.ini', relative_to=rel_path)
40 load_environment(conf.global_conf, conf.local_conf)
40 load_environment(conf.global_conf, conf.local_conf)
41
41
42 add_cache(conf)
42 add_cache(conf)
43
43
44 USER = 'test_admin'
44 USER = 'test_admin'
45 PASS = 'test12'
45 PASS = 'test12'
46 HOST = '127.0.0.1:5000'
46 HOST = '127.0.0.1:5000'
47 DEBUG = bool(int(sys.argv[1]))
47 DEBUG = True if sys.argv[1:] else False
48 print 'DEBUG:', DEBUG
48 print 'DEBUG:', DEBUG
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51
51
52 class Command(object):
52 class Command(object):
53
53
54 def __init__(self, cwd):
54 def __init__(self, cwd):
55 self.cwd = cwd
55 self.cwd = cwd
56
56
57 def execute(self, cmd, *args):
57 def execute(self, cmd, *args):
58 """Runs command on the system with given ``args``.
58 """Runs command on the system with given ``args``.
59 """
59 """
60
60
61 command = cmd + ' ' + ' '.join(args)
61 command = cmd + ' ' + ' '.join(args)
62 log.debug('Executing %s' % command)
62 log.debug('Executing %s' % command)
63 if DEBUG:
63 if DEBUG:
64 print command
64 print command
65 p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd)
65 p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd)
66 stdout, stderr = p.communicate()
66 stdout, stderr = p.communicate()
67 if DEBUG:
67 if DEBUG:
68 print stdout, stderr
68 print stdout, stderr
69 return stdout, stderr
69 return stdout, stderr
70
70
71
71
72 def test_wrapp(func):
72 def test_wrapp(func):
73
73
74 def __wrapp(*args, **kwargs):
74 def __wrapp(*args, **kwargs):
75 print '###%s###' % func.__name__
75 print '>>>%s' % func.__name__
76 try:
76 try:
77 res = func(*args, **kwargs)
77 res = func(*args, **kwargs)
78 except Exception, e:
78 except Exception, e:
79 print ('###############\n-'
79 print ('###############\n-'
80 '--%s failed %s--\n'
80 '--%s failed %s--\n'
81 '###############\n' % (func.__name__, e))
81 '###############\n' % (func.__name__, e))
82 sys.exit()
82 sys.exit()
83 print '++OK++'
83 print '++OK++'
84 return res
84 return res
85 return __wrapp
85 return __wrapp
86
86
87 def get_session():
87 def get_session():
88 engine = engine_from_config(conf, 'sqlalchemy.db1.')
88 engine = engine_from_config(conf, 'sqlalchemy.db1.')
89 init_model(engine)
89 init_model(engine)
90 sa = meta.Session
90 sa = meta.Session
91 return sa
91 return sa
92
92
93
93
94 def create_test_user(force=True):
94 def create_test_user(force=True):
95 print '\tcreating test user'
95 print '\tcreating test user'
96 sa = get_session()
96 sa = get_session()
97
97
98 user = sa.query(User).filter(User.username == USER).scalar()
98 user = sa.query(User).filter(User.username == USER).scalar()
99
99
100 if force and user is not None:
100 if force and user is not None:
101 print '\tremoving current user'
101 print '\tremoving current user'
102 for repo in sa.query(Repository).filter(Repository.user == user).all():
102 for repo in sa.query(Repository).filter(Repository.user == user).all():
103 sa.delete(repo)
103 sa.delete(repo)
104 sa.delete(user)
104 sa.delete(user)
105 sa.commit()
105 sa.commit()
106
106
107 if user is None or force:
107 if user is None or force:
108 print '\tcreating new one'
108 print '\tcreating new one'
109 new_usr = User()
109 new_usr = User()
110 new_usr.username = USER
110 new_usr.username = USER
111 new_usr.password = get_crypt_password(PASS)
111 new_usr.password = get_crypt_password(PASS)
112 new_usr.email = 'mail@mail.com'
112 new_usr.email = 'mail@mail.com'
113 new_usr.name = 'test'
113 new_usr.name = 'test'
114 new_usr.lastname = 'lasttestname'
114 new_usr.lastname = 'lasttestname'
115 new_usr.active = True
115 new_usr.active = True
116 new_usr.admin = True
116 new_usr.admin = True
117 sa.add(new_usr)
117 sa.add(new_usr)
118 sa.commit()
118 sa.commit()
119
119
120 print '\tdone'
120 print '\tdone'
121
121
122
122
123 def create_test_repo(force=True):
123 def create_test_repo(force=True):
124 from rhodecode.model.repo import RepoModel
124 from rhodecode.model.repo import RepoModel
125 sa = get_session()
125 sa = get_session()
126
126
127 user = sa.query(User).filter(User.username == USER).scalar()
127 user = sa.query(User).filter(User.username == USER).scalar()
128 if user is None:
128 if user is None:
129 raise Exception('user not found')
129 raise Exception('user not found')
130
130
131
131
132 repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar()
132 repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar()
133
133
134 if repo is None:
134 if repo is None:
135 print '\trepo not found creating'
135 print '\trepo not found creating'
136
136
137 form_data = {'repo_name':HG_REPO,
137 form_data = {'repo_name':HG_REPO,
138 'repo_type':'hg',
138 'repo_type':'hg',
139 'private':False,
139 'private':False,
140 'clone_uri':'' }
140 'clone_uri':'' }
141 rm = RepoModel(sa)
141 rm = RepoModel(sa)
142 rm.base_path = '/home/hg'
142 rm.base_path = '/home/hg'
143 rm.create(form_data, user)
143 rm.create(form_data, user)
144
144
145
145
146 def set_anonymous_access(enable=True):
146 def set_anonymous_access(enable=True):
147 sa = get_session()
147 sa = get_session()
148 user = sa.query(User).filter(User.username == 'default').one()
148 user = sa.query(User).filter(User.username == 'default').one()
149 sa.expire(user)
149 sa.expire(user)
150 user.active = enable
150 user.active = enable
151 sa.add(user)
151 sa.add(user)
152 sa.commit()
152 sa.commit()
153 sa.remove()
153 sa.remove()
154 import time;time.sleep(3)
154 import time;time.sleep(3)
155 print '\tanonymous access is now:', enable
155 print '\tanonymous access is now:', enable
156
156
157
157
158 def get_anonymous_access():
158 def get_anonymous_access():
159 sa = get_session()
159 sa = get_session()
160 obj1 = sa.query(User).filter(User.username == 'default').one()
160 obj1 = sa.query(User).filter(User.username == 'default').one()
161 sa.expire(obj1)
161 sa.expire(obj1)
162 return obj1.active
162 return obj1.active
163
163
164
164
165 #==============================================================================
165 #==============================================================================
166 # TESTS
166 # TESTS
167 #==============================================================================
167 #==============================================================================
168 @test_wrapp
168 @test_wrapp
169 def test_clone_with_credentials(no_errors=False):
169 def test_clone_with_credentials(no_errors=False):
170 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
170 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
171
171
172 try:
172 try:
173 shutil.rmtree(path, ignore_errors=True)
173 shutil.rmtree(path, ignore_errors=True)
174 os.makedirs(path)
174 os.makedirs(path)
175 #print 'made dirs %s' % jn(path)
175 #print 'made dirs %s' % jn(path)
176 except OSError:
176 except OSError:
177 raise
177 raise
178
178
179 print '\tchecking if anonymous access is enabled'
179 print '\tchecking if anonymous access is enabled'
180 anonymous_access = get_anonymous_access()
180 anonymous_access = get_anonymous_access()
181 if anonymous_access:
181 if anonymous_access:
182 print '\tenabled, disabling it '
182 print '\tenabled, disabling it '
183 set_anonymous_access(enable=False)
183 set_anonymous_access(enable=False)
184 time.sleep(1)
184 time.sleep(1)
185
185
186 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
186 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
187 {'user':USER,
187 {'user':USER,
188 'pass':PASS,
188 'pass':PASS,
189 'host':HOST,
189 'host':HOST,
190 'cloned_repo':HG_REPO,
190 'cloned_repo':HG_REPO,
191 'dest':path}
191 'dest':path}
192
192
193 stdout, stderr = Command(cwd).execute('hg clone', clone_url)
193 stdout, stderr = Command(cwd).execute('hg clone', clone_url)
194
194
195 if no_errors is False:
195 if no_errors is False:
196 assert """adding file changes""" in stdout, 'no messages about cloning'
196 assert """adding file changes""" in stdout, 'no messages about cloning'
197 assert """abort""" not in stderr , 'got error from clone'
197 assert """abort""" not in stderr , 'got error from clone'
198
198
199
199
200 @test_wrapp
200 @test_wrapp
201 def test_clone_anonymous():
201 def test_clone_anonymous():
202 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
202 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
203
203
204 try:
204 try:
205 shutil.rmtree(path, ignore_errors=True)
205 shutil.rmtree(path, ignore_errors=True)
206 os.makedirs(path)
206 os.makedirs(path)
207 #print 'made dirs %s' % jn(path)
207 #print 'made dirs %s' % jn(path)
208 except OSError:
208 except OSError:
209 raise
209 raise
210
210
211
211
212 print '\tchecking if anonymous access is enabled'
212 print '\tchecking if anonymous access is enabled'
213 anonymous_access = get_anonymous_access()
213 anonymous_access = get_anonymous_access()
214 if not anonymous_access:
214 if not anonymous_access:
215 print '\tnot enabled, enabling it '
215 print '\tnot enabled, enabling it '
216 set_anonymous_access(enable=True)
216 set_anonymous_access(enable=True)
217 time.sleep(1)
217 time.sleep(1)
218
218
219 clone_url = 'http://%(host)s/%(cloned_repo)s %(dest)s' % \
219 clone_url = 'http://%(host)s/%(cloned_repo)s %(dest)s' % \
220 {'user':USER,
220 {'user':USER,
221 'pass':PASS,
221 'pass':PASS,
222 'host':HOST,
222 'host':HOST,
223 'cloned_repo':HG_REPO,
223 'cloned_repo':HG_REPO,
224 'dest':path}
224 'dest':path}
225
225
226 stdout, stderr = Command(cwd).execute('hg clone', clone_url)
226 stdout, stderr = Command(cwd).execute('hg clone', clone_url)
227
227
228 assert """adding file changes""" in stdout, 'no messages about cloning'
228 assert """adding file changes""" in stdout, 'no messages about cloning'
229 assert """abort""" not in stderr , 'got error from clone'
229 assert """abort""" not in stderr , 'got error from clone'
230
230
231 #disable if it was enabled
231 #disable if it was enabled
232 if not anonymous_access:
232 if not anonymous_access:
233 print '\tdisabling anonymous access'
233 print '\tdisabling anonymous access'
234 set_anonymous_access(enable=False)
234 set_anonymous_access(enable=False)
235
235
236 @test_wrapp
236 @test_wrapp
237 def test_clone_wrong_credentials():
237 def test_clone_wrong_credentials():
238 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
238 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
239
239
240 try:
240 try:
241 shutil.rmtree(path, ignore_errors=True)
241 shutil.rmtree(path, ignore_errors=True)
242 os.makedirs(path)
242 os.makedirs(path)
243 #print 'made dirs %s' % jn(path)
243 #print 'made dirs %s' % jn(path)
244 except OSError:
244 except OSError:
245 raise
245 raise
246
246
247 print '\tchecking if anonymous access is enabled'
247 print '\tchecking if anonymous access is enabled'
248 anonymous_access = get_anonymous_access()
248 anonymous_access = get_anonymous_access()
249 if anonymous_access:
249 if anonymous_access:
250 print '\tenabled, disabling it '
250 print '\tenabled, disabling it '
251 set_anonymous_access(enable=False)
251 set_anonymous_access(enable=False)
252
252
253 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
253 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s %(dest)s' % \
254 {'user':USER + 'error',
254 {'user':USER + 'error',
255 'pass':PASS,
255 'pass':PASS,
256 'host':HOST,
256 'host':HOST,
257 'cloned_repo':HG_REPO,
257 'cloned_repo':HG_REPO,
258 'dest':path}
258 'dest':path}
259
259
260 stdout, stderr = Command(cwd).execute('hg clone', clone_url)
260 stdout, stderr = Command(cwd).execute('hg clone', clone_url)
261
261
262 if not """abort: authorization failed""" in stderr:
262 if not """abort: authorization failed""" in stderr:
263 raise Exception('Failure')
263 raise Exception('Failure')
264
264
265 @test_wrapp
265 @test_wrapp
266 def test_pull():
266 def test_pull():
267 pass
267 pass
268
268
269 @test_wrapp
269 @test_wrapp
270 def test_push_modify_file(f_name='setup.py'):
270 def test_push_modify_file(f_name='setup.py'):
271 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
271 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
272 modified_file = jn(TESTS_TMP_PATH, HG_REPO, f_name)
272 modified_file = jn(TESTS_TMP_PATH, HG_REPO, f_name)
273 for i in xrange(5):
273 for i in xrange(5):
274 cmd = """echo 'added_line%s' >> %s""" % (i, modified_file)
274 cmd = """echo 'added_line%s' >> %s""" % (i, modified_file)
275 Command(cwd).execute(cmd)
275 Command(cwd).execute(cmd)
276
276
277 cmd = """hg ci -m 'changed file %s' %s """ % (i, modified_file)
277 cmd = """hg ci -m 'changed file %s' %s """ % (i, modified_file)
278 Command(cwd).execute(cmd)
278 Command(cwd).execute(cmd)
279
279
280 Command(cwd).execute('hg push %s' % jn(TESTS_TMP_PATH, HG_REPO))
280 Command(cwd).execute('hg push %s' % jn(TESTS_TMP_PATH, HG_REPO))
281
281
282 @test_wrapp
282 @test_wrapp
283 def test_push_new_file(commits=15, with_clone=True):
283 def test_push_new_file(commits=15, with_clone=True):
284
284
285 if with_clone:
285 if with_clone:
286 test_clone_with_credentials(no_errors=True)
286 test_clone_with_credentials(no_errors=True)
287
287
288 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
288 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
289 added_file = jn(path, '%ssetupΔ…ΕΌΕΊΔ‡.py' % _RandomNameSequence().next())
289 added_file = jn(path, '%ssetupΔ…ΕΌΕΊΔ‡.py' % _RandomNameSequence().next())
290
290
291 Command(cwd).execute('touch %s' % added_file)
291 Command(cwd).execute('touch %s' % added_file)
292
292
293 Command(cwd).execute('hg add %s' % added_file)
293 Command(cwd).execute('hg add %s' % added_file)
294
294
295 for i in xrange(commits):
295 for i in xrange(commits):
296 cmd = """echo 'added_line%s' >> %s""" % (i, added_file)
296 cmd = """echo 'added_line%s' >> %s""" % (i, added_file)
297 Command(cwd).execute(cmd)
297 Command(cwd).execute(cmd)
298
298
299 cmd = """hg ci -m 'commited new %s' -u '%s' %s """ % (i,
299 cmd = """hg ci -m 'commited new %s' -u '%s' %s """ % (i,
300 'Marcin KuΕΊminski <marcin@python-blog.com>',
300 'Marcin KuΕΊminski <marcin@python-blog.com>',
301 added_file)
301 added_file)
302 Command(cwd).execute(cmd)
302 Command(cwd).execute(cmd)
303
303
304 push_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
304 push_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
305 {'user':USER,
305 {'user':USER,
306 'pass':PASS,
306 'pass':PASS,
307 'host':HOST,
307 'host':HOST,
308 'cloned_repo':HG_REPO,
308 'cloned_repo':HG_REPO,
309 'dest':jn(TESTS_TMP_PATH, HG_REPO)}
309 'dest':jn(TESTS_TMP_PATH, HG_REPO)}
310
310
311 Command(cwd).execute('hg push --verbose --debug %s' % push_url)
311 Command(cwd).execute('hg push --verbose --debug %s' % push_url)
312
312
313 @test_wrapp
313 @test_wrapp
314 def test_push_wrong_credentials():
314 def test_push_wrong_credentials():
315 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
315 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
316 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
316 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
317 {'user':USER + 'xxx',
317 {'user':USER + 'xxx',
318 'pass':PASS,
318 'pass':PASS,
319 'host':HOST,
319 'host':HOST,
320 'cloned_repo':HG_REPO,
320 'cloned_repo':HG_REPO,
321 'dest':jn(TESTS_TMP_PATH, HG_REPO)}
321 'dest':jn(TESTS_TMP_PATH, HG_REPO)}
322
322
323 modified_file = jn(TESTS_TMP_PATH, HG_REPO, 'setup.py')
323 modified_file = jn(TESTS_TMP_PATH, HG_REPO, 'setup.py')
324 for i in xrange(5):
324 for i in xrange(5):
325 cmd = """echo 'added_line%s' >> %s""" % (i, modified_file)
325 cmd = """echo 'added_line%s' >> %s""" % (i, modified_file)
326 Command(cwd).execute(cmd)
326 Command(cwd).execute(cmd)
327
327
328 cmd = """hg ci -m 'commited %s' %s """ % (i, modified_file)
328 cmd = """hg ci -m 'commited %s' %s """ % (i, modified_file)
329 Command(cwd).execute(cmd)
329 Command(cwd).execute(cmd)
330
330
331 Command(cwd).execute('hg push %s' % clone_url)
331 Command(cwd).execute('hg push %s' % clone_url)
332
332
333 @test_wrapp
333 @test_wrapp
334 def test_push_wrong_path():
334 def test_push_wrong_path():
335 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
335 cwd = path = jn(TESTS_TMP_PATH, HG_REPO)
336 added_file = jn(path, 'somefile.py')
336 added_file = jn(path, 'somefile.py')
337
337
338 try:
338 try:
339 shutil.rmtree(path, ignore_errors=True)
339 shutil.rmtree(path, ignore_errors=True)
340 os.makedirs(path)
340 os.makedirs(path)
341 print '\tmade dirs %s' % jn(path)
341 print '\tmade dirs %s' % jn(path)
342 except OSError:
342 except OSError:
343 raise
343 raise
344
344
345 Command(cwd).execute("""echo '' > %s""" % added_file)
345 Command(cwd).execute("""echo '' > %s""" % added_file)
346 Command(cwd).execute("""hg init %s""" % path)
346 Command(cwd).execute("""hg init %s""" % path)
347 Command(cwd).execute("""hg add %s""" % added_file)
347 Command(cwd).execute("""hg add %s""" % added_file)
348
348
349 for i in xrange(2):
349 for i in xrange(2):
350 cmd = """echo 'added_line%s' >> %s""" % (i, added_file)
350 cmd = """echo 'added_line%s' >> %s""" % (i, added_file)
351 Command(cwd).execute(cmd)
351 Command(cwd).execute(cmd)
352
352
353 cmd = """hg ci -m 'commited new %s' %s """ % (i, added_file)
353 cmd = """hg ci -m 'commited new %s' %s """ % (i, added_file)
354 Command(cwd).execute(cmd)
354 Command(cwd).execute(cmd)
355
355
356 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
356 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
357 {'user':USER,
357 {'user':USER,
358 'pass':PASS,
358 'pass':PASS,
359 'host':HOST,
359 'host':HOST,
360 'cloned_repo':HG_REPO + '_error',
360 'cloned_repo':HG_REPO + '_error',
361 'dest':jn(TESTS_TMP_PATH, HG_REPO)}
361 'dest':jn(TESTS_TMP_PATH, HG_REPO)}
362
362
363 stdout, stderr = Command(cwd).execute('hg push %s' % clone_url)
363 stdout, stderr = Command(cwd).execute('hg push %s' % clone_url)
364 if not """abort: HTTP Error 403: Forbidden""" in stderr:
364 if not """abort: HTTP Error 403: Forbidden""" in stderr:
365 raise Exception('Failure')
365 raise Exception('Failure')
366
366
367 @test_wrapp
367 @test_wrapp
368 def get_logs():
368 def get_logs():
369 sa = get_session()
369 sa = get_session()
370 return len(sa.query(UserLog).all())
370 return len(sa.query(UserLog).all())
371
371
372 @test_wrapp
372 @test_wrapp
373 def test_logs(initial):
373 def test_logs(initial):
374 sa = get_session()
374 sa = get_session()
375 logs = sa.query(UserLog).all()
375 logs = sa.query(UserLog).all()
376 operations = 7
376 operations = 7
377 if initial + operations != len(logs):
377 if initial + operations != len(logs):
378 raise Exception("missing number of logs %s vs %s" % (initial, len(logs)))
378 raise Exception("missing number of logs %s vs %s" % (initial, len(logs)))
379
379
380
380
381 if __name__ == '__main__':
381 if __name__ == '__main__':
382 create_test_user(force=False)
382 create_test_user(force=False)
383 create_test_repo()
383 create_test_repo()
384
384
385 initial_logs = get_logs()
385 initial_logs = get_logs()
386
386
387 # test_push_modify_file()
387 # test_push_modify_file()
388 test_clone_with_credentials()
388 test_clone_with_credentials()
389 test_clone_wrong_credentials()
389 test_clone_wrong_credentials()
390
390
391
391
392 test_push_new_file(commits=2, with_clone=True)
392 test_push_new_file(commits=2, with_clone=True)
393
393
394 test_clone_anonymous()
394 test_clone_anonymous()
395 test_push_wrong_path()
395 test_push_wrong_path()
396
396
397
397
398 test_push_wrong_credentials()
398 test_push_wrong_credentials()
399
399
400 test_logs(initial_logs)
400 test_logs(initial_logs)
@@ -1,219 +1,221 b''
1 ################################################################################
1 ################################################################################
2 ################################################################################
2 ################################################################################
3 # RhodeCode - Pylons environment configuration #
3 # RhodeCode - Pylons environment configuration #
4 # #
4 # #
5 # The %(here)s variable will be replaced with the parent directory of this file#
5 # The %(here)s variable will be replaced with the parent directory of this file#
6 ################################################################################
6 ################################################################################
7
7
8 [DEFAULT]
8 [DEFAULT]
9 debug = true
9 debug = true
10 pdebug = false
10 pdebug = false
11 ################################################################################
11 ################################################################################
12 ## Uncomment and replace with the address which should receive ##
12 ## Uncomment and replace with the address which should receive ##
13 ## any error reports after application crash ##
13 ## any error reports after application crash ##
14 ## Additionally those settings will be used by RhodeCode mailing system ##
14 ## Additionally those settings will be used by RhodeCode mailing system ##
15 ################################################################################
15 ################################################################################
16 #email_to = admin@localhost
16 #email_to = admin@localhost
17 #error_email_from = paste_error@localhost
17 #error_email_from = paste_error@localhost
18 #app_email_from = rhodecode-noreply@localhost
18 #app_email_from = rhodecode-noreply@localhost
19 #error_message =
19 #error_message =
20
20
21 #smtp_server = mail.server.com
21 #smtp_server = mail.server.com
22 #smtp_username =
22 #smtp_username =
23 #smtp_password =
23 #smtp_password =
24 #smtp_port =
24 #smtp_port =
25 #smtp_use_tls = false
25 #smtp_use_tls = false
26 #smtp_use_ssl = true
26
27
27 [server:main]
28 [server:main]
28 ##nr of threads to spawn
29 ##nr of threads to spawn
29 threadpool_workers = 5
30 threadpool_workers = 5
30
31
31 ##max request before thread respawn
32 ##max request before thread respawn
32 threadpool_max_requests = 2
33 threadpool_max_requests = 2
33
34
34 ##option to use threads of process
35 ##option to use threads of process
35 use_threadpool = true
36 use_threadpool = true
36
37
37 use = egg:Paste#http
38 use = egg:Paste#http
38 host = 127.0.0.1
39 host = 127.0.0.1
39 port = 5000
40 port = 5000
40
41
41 [app:main]
42 [app:main]
42 use = egg:rhodecode
43 use = egg:rhodecode
43 full_stack = true
44 full_stack = true
44 static_files = true
45 static_files = true
45 lang=en
46 lang=en
46 cache_dir = /tmp/data
47 cache_dir = /tmp/data
47 index_dir = /tmp/index
48 index_dir = /tmp/index
48 app_instance_uuid = develop-test
49 app_instance_uuid = develop-test
49 cut_off_limit = 256000
50 cut_off_limit = 256000
50 force_https = false
51 force_https = false
51 commit_parse_limit = 25
52 commit_parse_limit = 25
53 use_gravatar = true
52
54
53 ####################################
55 ####################################
54 ### CELERY CONFIG ####
56 ### CELERY CONFIG ####
55 ####################################
57 ####################################
56 use_celery = false
58 use_celery = false
57 broker.host = localhost
59 broker.host = localhost
58 broker.vhost = rabbitmqhost
60 broker.vhost = rabbitmqhost
59 broker.port = 5672
61 broker.port = 5672
60 broker.user = rabbitmq
62 broker.user = rabbitmq
61 broker.password = qweqwe
63 broker.password = qweqwe
62
64
63 celery.imports = rhodecode.lib.celerylib.tasks
65 celery.imports = rhodecode.lib.celerylib.tasks
64
66
65 celery.result.backend = amqp
67 celery.result.backend = amqp
66 celery.result.dburi = amqp://
68 celery.result.dburi = amqp://
67 celery.result.serialier = json
69 celery.result.serialier = json
68
70
69 #celery.send.task.error.emails = true
71 #celery.send.task.error.emails = true
70 #celery.amqp.task.result.expires = 18000
72 #celery.amqp.task.result.expires = 18000
71
73
72 celeryd.concurrency = 2
74 celeryd.concurrency = 2
73 #celeryd.log.file = celeryd.log
75 #celeryd.log.file = celeryd.log
74 celeryd.log.level = debug
76 celeryd.log.level = debug
75 celeryd.max.tasks.per.child = 1
77 celeryd.max.tasks.per.child = 1
76
78
77 #tasks will never be sent to the queue, but executed locally instead.
79 #tasks will never be sent to the queue, but executed locally instead.
78 celery.always.eager = false
80 celery.always.eager = false
79
81
80 ####################################
82 ####################################
81 ### BEAKER CACHE ####
83 ### BEAKER CACHE ####
82 ####################################
84 ####################################
83 beaker.cache.data_dir=/tmp/data/cache/data
85 beaker.cache.data_dir=/tmp/data/cache/data
84 beaker.cache.lock_dir=/tmp/data/cache/lock
86 beaker.cache.lock_dir=/tmp/data/cache/lock
85 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
87 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
86
88
87 beaker.cache.super_short_term.type=memory
89 beaker.cache.super_short_term.type=memory
88 beaker.cache.super_short_term.expire=10
90 beaker.cache.super_short_term.expire=10
89
91
90 beaker.cache.short_term.type=memory
92 beaker.cache.short_term.type=memory
91 beaker.cache.short_term.expire=60
93 beaker.cache.short_term.expire=60
92
94
93 beaker.cache.long_term.type=memory
95 beaker.cache.long_term.type=memory
94 beaker.cache.long_term.expire=36000
96 beaker.cache.long_term.expire=36000
95
97
96
97 beaker.cache.sql_cache_short.type=memory
98 beaker.cache.sql_cache_short.type=memory
98 beaker.cache.sql_cache_short.expire=10
99 beaker.cache.sql_cache_short.expire=10
99
100
100 beaker.cache.sql_cache_med.type=memory
101 beaker.cache.sql_cache_med.type=memory
101 beaker.cache.sql_cache_med.expire=360
102 beaker.cache.sql_cache_med.expire=360
102
103
103 beaker.cache.sql_cache_long.type=file
104 beaker.cache.sql_cache_long.type=file
104 beaker.cache.sql_cache_long.expire=3600
105 beaker.cache.sql_cache_long.expire=3600
105
106
106 ####################################
107 ####################################
107 ### BEAKER SESSION ####
108 ### BEAKER SESSION ####
108 ####################################
109 ####################################
109 ## Type of storage used for the session, current types are
110 ## Type of storage used for the session, current types are
110 ## dbm, file, memcached, database, and memory.
111 ## dbm, file, memcached, database, and memory.
111 ## The storage uses the Container API
112 ## The storage uses the Container API
112 ##that is also used by the cache system.
113 ##that is also used by the cache system.
113 beaker.session.type = file
114 beaker.session.type = file
114
115
115 beaker.session.key = rhodecode
116 beaker.session.key = rhodecode
116 beaker.session.secret = g654dcno0-9873jhgfreyu
117 beaker.session.secret = g654dcno0-9873jhgfreyu
117 beaker.session.timeout = 36000
118 beaker.session.timeout = 36000
118
119
119 ##auto save the session to not to use .save()
120 ##auto save the session to not to use .save()
120 beaker.session.auto = False
121 beaker.session.auto = False
121
122
122 ##true exire at browser close
123 ##true exire at browser close
123 #beaker.session.cookie_expires = 3600
124 #beaker.session.cookie_expires = 3600
124
125
125
126
126 ################################################################################
127 ################################################################################
127 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
128 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
128 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
129 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
129 ## execute malicious code after an exception is raised. ##
130 ## execute malicious code after an exception is raised. ##
130 ################################################################################
131 ################################################################################
131 #set debug = false
132 #set debug = false
132
133
133 ##################################
134 ##################################
134 ### LOGVIEW CONFIG ###
135 ### LOGVIEW CONFIG ###
135 ##################################
136 ##################################
136 logview.sqlalchemy = #faa
137 logview.sqlalchemy = #faa
137 logview.pylons.templating = #bfb
138 logview.pylons.templating = #bfb
138 logview.pylons.util = #eee
139 logview.pylons.util = #eee
139
140
140 #########################################################
141 #########################################################
141 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
142 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
142 #########################################################
143 #########################################################
143 sqlalchemy.db1.url = sqlite:///%(here)s/test.db
144 sqlalchemy.db1.url = sqlite:///%(here)s/test.db
144 #sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode_tests
145 #sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode_tests
145 #sqlalchemy.db1.echo = False
146 #sqlalchemy.db1.echo = False
146 #sqlalchemy.db1.pool_recycle = 3600
147 #sqlalchemy.db1.pool_recycle = 3600
147 sqlalchemy.convert_unicode = true
148 sqlalchemy.convert_unicode = true
148
149
149 ################################
150 ################################
150 ### LOGGING CONFIGURATION ####
151 ### LOGGING CONFIGURATION ####
151 ################################
152 ################################
152 [loggers]
153 [loggers]
153 keys = root, routes, rhodecode, sqlalchemy,beaker,templates
154 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
154
155
155 [handlers]
156 [handlers]
156 keys = console
157 keys = console
157
158
158 [formatters]
159 [formatters]
159 keys = generic,color_formatter
160 keys = generic, color_formatter
160
161
161 #############
162 #############
162 ## LOGGERS ##
163 ## LOGGERS ##
163 #############
164 #############
164 [logger_root]
165 [logger_root]
165 level = ERROR
166 level = ERROR
166 handlers = console
167 handlers = console
167
168
168 [logger_routes]
169 [logger_routes]
169 level = ERROR
170 level = ERROR
170 handlers = console
171 handlers =
171 qualname = routes.middleware
172 qualname = routes.middleware
172 # "level = DEBUG" logs the route matched and routing variables.
173 # "level = DEBUG" logs the route matched and routing variables.
174 propagate = 1
173
175
174 [logger_beaker]
176 [logger_beaker]
175 level = DEBUG
177 level = DEBUG
176 handlers =
178 handlers =
177 qualname = beaker.container
179 qualname = beaker.container
178 propagate = 1
180 propagate = 1
179
181
180 [logger_templates]
182 [logger_templates]
181 level = INFO
183 level = INFO
182 handlers =
184 handlers =
183 qualname = pylons.templating
185 qualname = pylons.templating
184 propagate = 1
186 propagate = 1
185
187
186 [logger_rhodecode]
188 [logger_rhodecode]
187 level = ERROR
189 level = ERROR
188 handlers = console
190 handlers =
189 qualname = rhodecode
191 qualname = rhodecode
190 propagate = 0
192 propagate = 1
191
193
192 [logger_sqlalchemy]
194 [logger_sqlalchemy]
193 level = ERROR
195 level = ERROR
194 handlers = console
196 handlers = console
195 qualname = sqlalchemy.engine
197 qualname = sqlalchemy.engine
196 propagate = 0
198 propagate = 0
197
199
198 ##############
200 ##############
199 ## HANDLERS ##
201 ## HANDLERS ##
200 ##############
202 ##############
201
203
202 [handler_console]
204 [handler_console]
203 class = StreamHandler
205 class = StreamHandler
204 args = (sys.stderr,)
206 args = (sys.stderr,)
205 level = NOTSET
207 level = NOTSET
206 formatter = color_formatter
208 formatter = generic
207
209
208 ################
210 ################
209 ## FORMATTERS ##
211 ## FORMATTERS ##
210 ################
212 ################
211
213
212 [formatter_generic]
214 [formatter_generic]
213 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
215 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
214 datefmt = %Y-%m-%d %H:%M:%S
216 datefmt = %Y-%m-%d %H:%M:%S
215
217
216 [formatter_color_formatter]
218 [formatter_color_formatter]
217 class=rhodecode.lib.colored_formatter.ColorFormatter
219 class=rhodecode.lib.colored_formatter.ColorFormatter
218 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
220 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
219 datefmt = %Y-%m-%d %H:%M:%S No newline at end of file
221 datefmt = %Y-%m-%d %H:%M:%S
General Comments 0
You need to be logged in to leave comments. Login now