##// END OF EJS Templates
another major refactoring with session management
marcink -
r1734:48d4fcf0 beta
parent child Browse files
Show More
@@ -1,167 +1,169 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.permissions
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 permissions controller for Rhodecode
7 7
8 8 :created_on: Apr 27, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28 import formencode
29 29 from formencode import htmlfill
30 30
31 31 from pylons import request, session, tmpl_context as c, url
32 32 from pylons.controllers.util import abort, redirect
33 33 from pylons.i18n.translation import _
34 34
35 35 from rhodecode.lib import helpers as h
36 36 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
37 37 from rhodecode.lib.base import BaseController, render
38 38 from rhodecode.model.forms import DefaultPermissionsForm
39 39 from rhodecode.model.permission import PermissionModel
40 40 from rhodecode.model.db import User
41 from rhodecode.model.meta import Session
41 42
42 43 log = logging.getLogger(__name__)
43 44
44 45
45 46 class PermissionsController(BaseController):
46 47 """REST Controller styled on the Atom Publishing Protocol"""
47 48 # To properly map this controller, ensure your config/routing.py
48 49 # file has a resource setup:
49 50 # map.resource('permission', 'permissions')
50 51
51 52 @LoginRequired()
52 53 @HasPermissionAllDecorator('hg.admin')
53 54 def __before__(self):
54 55 c.admin_user = session.get('admin_user')
55 56 c.admin_username = session.get('admin_username')
56 57 super(PermissionsController, self).__before__()
57 58
58 59 self.perms_choices = [('repository.none', _('None'),),
59 60 ('repository.read', _('Read'),),
60 61 ('repository.write', _('Write'),),
61 62 ('repository.admin', _('Admin'),)]
62 63 self.register_choices = [
63 64 ('hg.register.none',
64 65 _('disabled')),
65 66 ('hg.register.manual_activate',
66 67 _('allowed with manual account activation')),
67 68 ('hg.register.auto_activate',
68 69 _('allowed with automatic account activation')), ]
69 70
70 71 self.create_choices = [('hg.create.none', _('Disabled')),
71 72 ('hg.create.repository', _('Enabled'))]
72 73
73 74 def index(self, format='html'):
74 75 """GET /permissions: All items in the collection"""
75 76 # url('permissions')
76 77
77 78 def create(self):
78 79 """POST /permissions: Create a new item"""
79 80 # url('permissions')
80 81
81 82 def new(self, format='html'):
82 83 """GET /permissions/new: Form to create a new item"""
83 84 # url('new_permission')
84 85
85 86 def update(self, id):
86 87 """PUT /permissions/id: Update an existing item"""
87 88 # Forms posted to this method should contain a hidden field:
88 89 # <input type="hidden" name="_method" value="PUT" />
89 90 # Or using helpers:
90 91 # h.form(url('permission', id=ID),
91 92 # method='put')
92 93 # url('permission', id=ID)
93 94
94 95 permission_model = PermissionModel()
95 96
96 97 _form = DefaultPermissionsForm([x[0] for x in self.perms_choices],
97 98 [x[0] for x in self.register_choices],
98 99 [x[0] for x in self.create_choices])()
99 100
100 101 try:
101 102 form_result = _form.to_python(dict(request.POST))
102 103 form_result.update({'perm_user_name': id})
103 104 permission_model.update(form_result)
105 Session().commit()
104 106 h.flash(_('Default permissions updated successfully'),
105 107 category='success')
106 108
107 109 except formencode.Invalid, errors:
108 110 c.perms_choices = self.perms_choices
109 111 c.register_choices = self.register_choices
110 112 c.create_choices = self.create_choices
111 113 defaults = errors.value
112 114
113 115 return htmlfill.render(
114 116 render('admin/permissions/permissions.html'),
115 117 defaults=defaults,
116 118 errors=errors.error_dict or {},
117 119 prefix_error=False,
118 120 encoding="UTF-8")
119 121 except Exception:
120 122 log.error(traceback.format_exc())
121 123 h.flash(_('error occurred during update of permissions'),
122 124 category='error')
123 125
124 126 return redirect(url('edit_permission', id=id))
125 127
126 128 def delete(self, id):
127 129 """DELETE /permissions/id: Delete an existing item"""
128 130 # Forms posted to this method should contain a hidden field:
129 131 # <input type="hidden" name="_method" value="DELETE" />
130 132 # Or using helpers:
131 133 # h.form(url('permission', id=ID),
132 134 # method='delete')
133 135 # url('permission', id=ID)
134 136
135 137 def show(self, id, format='html'):
136 138 """GET /permissions/id: Show a specific item"""
137 139 # url('permission', id=ID)
138 140
139 141 def edit(self, id, format='html'):
140 142 """GET /permissions/id/edit: Form to edit an existing item"""
141 143 #url('edit_permission', id=ID)
142 144 c.perms_choices = self.perms_choices
143 145 c.register_choices = self.register_choices
144 146 c.create_choices = self.create_choices
145 147
146 148 if id == 'default':
147 149 default_user = User.get_by_username('default')
148 150 defaults = {'_method': 'put',
149 151 'anonymous': default_user.active}
150 152
151 153 for p in default_user.user_perms:
152 154 if p.permission.permission_name.startswith('repository.'):
153 155 defaults['default_perm'] = p.permission.permission_name
154 156
155 157 if p.permission.permission_name.startswith('hg.register.'):
156 158 defaults['default_register'] = p.permission.permission_name
157 159
158 160 if p.permission.permission_name.startswith('hg.create.'):
159 161 defaults['default_create'] = p.permission.permission_name
160 162
161 163 return htmlfill.render(
162 164 render('admin/permissions/permissions.html'),
163 165 defaults=defaults,
164 166 encoding="UTF-8",
165 167 force_defaults=True,)
166 168 else:
167 169 return redirect(url('admin_home'))
@@ -1,229 +1,230 b''
1 1 import logging
2 2 import traceback
3 3 import formencode
4 4
5 5 from formencode import htmlfill
6 6 from operator import itemgetter
7 7
8 8 from pylons import request, response, session, tmpl_context as c, url
9 9 from pylons.controllers.util import abort, redirect
10 10 from pylons.i18n.translation import _
11 11
12 12 from sqlalchemy.exc import IntegrityError
13 13
14 14 from rhodecode.lib import helpers as h
15 15 from rhodecode.lib.auth import LoginRequired, HasPermissionAnyDecorator
16 16 from rhodecode.lib.base import BaseController, render
17 17 from rhodecode.model.db import RepoGroup
18 18 from rhodecode.model.repos_group import ReposGroupModel
19 19 from rhodecode.model.forms import ReposGroupForm
20 from rhodecode.model.meta import Session
20 21
21 22 log = logging.getLogger(__name__)
22 23
23 24
24 25 class ReposGroupsController(BaseController):
25 26 """REST Controller styled on the Atom Publishing Protocol"""
26 27 # To properly map this controller, ensure your config/routing.py
27 28 # file has a resource setup:
28 29 # map.resource('repos_group', 'repos_groups')
29 30
30 31 @LoginRequired()
31 32 def __before__(self):
32 33 super(ReposGroupsController, self).__before__()
33 34
34 35 def __load_defaults(self):
35 36 c.repo_groups = RepoGroup.groups_choices()
36 37 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
37 38
38 39 def __load_data(self, group_id):
39 40 """
40 41 Load defaults settings for edit, and update
41 42
42 43 :param group_id:
43 44 """
44 45 self.__load_defaults()
45 46
46 47 repo_group = RepoGroup.get(group_id)
47 48
48 49 data = repo_group.get_dict()
49 50
50 51 data['group_name'] = repo_group.name
51 52
52 53 return data
53 54
54 55 @HasPermissionAnyDecorator('hg.admin')
55 56 def index(self, format='html'):
56 57 """GET /repos_groups: All items in the collection"""
57 58 # url('repos_groups')
58 59
59 60 sk = lambda g:g.parents[0].group_name if g.parents else g.group_name
60 61 c.groups = sorted(RepoGroup.query().all(), key=sk)
61 62 return render('admin/repos_groups/repos_groups_show.html')
62 63
63 64 @HasPermissionAnyDecorator('hg.admin')
64 65 def create(self):
65 66 """POST /repos_groups: Create a new item"""
66 67 # url('repos_groups')
67 68 self.__load_defaults()
68 repos_group_model = ReposGroupModel()
69 69 repos_group_form = ReposGroupForm(available_groups=
70 70 c.repo_groups_choices)()
71 71 try:
72 72 form_result = repos_group_form.to_python(dict(request.POST))
73 repos_group_model.create(form_result)
73 ReposGroupModel().create(form_result)
74 Session().commit()
74 75 h.flash(_('created repos group %s') \
75 76 % form_result['group_name'], category='success')
76 77 #TODO: in futureaction_logger(, '', '', '', self.sa)
77 78 except formencode.Invalid, errors:
78 79
79 80 return htmlfill.render(
80 81 render('admin/repos_groups/repos_groups_add.html'),
81 82 defaults=errors.value,
82 83 errors=errors.error_dict or {},
83 84 prefix_error=False,
84 85 encoding="UTF-8")
85 86 except Exception:
86 87 log.error(traceback.format_exc())
87 88 h.flash(_('error occurred during creation of repos group %s') \
88 89 % request.POST.get('group_name'), category='error')
89 90
90 91 return redirect(url('repos_groups'))
91 92
92 93
93 94 @HasPermissionAnyDecorator('hg.admin')
94 95 def new(self, format='html'):
95 96 """GET /repos_groups/new: Form to create a new item"""
96 97 # url('new_repos_group')
97 98 self.__load_defaults()
98 99 return render('admin/repos_groups/repos_groups_add.html')
99 100
100 101 @HasPermissionAnyDecorator('hg.admin')
101 102 def update(self, id):
102 103 """PUT /repos_groups/id: Update an existing item"""
103 104 # Forms posted to this method should contain a hidden field:
104 105 # <input type="hidden" name="_method" value="PUT" />
105 106 # Or using helpers:
106 107 # h.form(url('repos_group', id=ID),
107 108 # method='put')
108 109 # url('repos_group', id=ID)
109 110
110 111 self.__load_defaults()
111 112 c.repos_group = RepoGroup.get(id)
112 113
113 repos_group_model = ReposGroupModel()
114 114 repos_group_form = ReposGroupForm(edit=True,
115 115 old_data=c.repos_group.get_dict(),
116 116 available_groups=
117 117 c.repo_groups_choices)()
118 118 try:
119 119 form_result = repos_group_form.to_python(dict(request.POST))
120 repos_group_model.update(id, form_result)
120 ReposGroupModel().update(id, form_result)
121 Session().commit()
121 122 h.flash(_('updated repos group %s') \
122 123 % form_result['group_name'], category='success')
123 124 #TODO: in futureaction_logger(, '', '', '', self.sa)
124 125 except formencode.Invalid, errors:
125 126
126 127 return htmlfill.render(
127 128 render('admin/repos_groups/repos_groups_edit.html'),
128 129 defaults=errors.value,
129 130 errors=errors.error_dict or {},
130 131 prefix_error=False,
131 132 encoding="UTF-8")
132 133 except Exception:
133 134 log.error(traceback.format_exc())
134 135 h.flash(_('error occurred during update of repos group %s') \
135 136 % request.POST.get('group_name'), category='error')
136 137
137 138 return redirect(url('repos_groups'))
138 139
139 140
140 141 @HasPermissionAnyDecorator('hg.admin')
141 142 def delete(self, id):
142 143 """DELETE /repos_groups/id: Delete an existing item"""
143 144 # Forms posted to this method should contain a hidden field:
144 145 # <input type="hidden" name="_method" value="DELETE" />
145 146 # Or using helpers:
146 147 # h.form(url('repos_group', id=ID),
147 148 # method='delete')
148 149 # url('repos_group', id=ID)
149 150
150 repos_group_model = ReposGroupModel()
151 151 gr = RepoGroup.get(id)
152 152 repos = gr.repositories.all()
153 153 if repos:
154 154 h.flash(_('This group contains %s repositores and cannot be '
155 155 'deleted' % len(repos)),
156 156 category='error')
157 157 return redirect(url('repos_groups'))
158 158
159 159 try:
160 repos_group_model.delete(id)
160 ReposGroupModel().delete(id)
161 Session().commit()
161 162 h.flash(_('removed repos group %s' % gr.group_name), category='success')
162 163 #TODO: in future action_logger(, '', '', '', self.sa)
163 164 except IntegrityError, e:
164 165 if e.message.find('groups_group_parent_id_fkey'):
165 166 log.error(traceback.format_exc())
166 167 h.flash(_('Cannot delete this group it still contains '
167 168 'subgroups'),
168 169 category='warning')
169 170 else:
170 171 log.error(traceback.format_exc())
171 172 h.flash(_('error occurred during deletion of repos '
172 173 'group %s' % gr.group_name), category='error')
173 174
174 175 except Exception:
175 176 log.error(traceback.format_exc())
176 177 h.flash(_('error occurred during deletion of repos '
177 178 'group %s' % gr.group_name), category='error')
178 179
179 180 return redirect(url('repos_groups'))
180 181
181 182 def show_by_name(self, group_name):
182 183 id_ = RepoGroup.get_by_group_name(group_name).group_id
183 184 return self.show(id_)
184 185
185 186 def show(self, id, format='html'):
186 187 """GET /repos_groups/id: Show a specific item"""
187 188 # url('repos_group', id=ID)
188 189
189 190 c.group = RepoGroup.get(id)
190 191
191 192 if c.group:
192 193 c.group_repos = c.group.repositories.all()
193 194 else:
194 195 return redirect(url('home'))
195 196
196 197 #overwrite our cached list with current filter
197 198 gr_filter = c.group_repos
198 199 c.cached_repo_list = self.scm_model.get_repos(all_repos=gr_filter)
199 200
200 201 c.repos_list = c.cached_repo_list
201 202
202 203 c.repo_cnt = 0
203 204
204 205 c.groups = self.sa.query(RepoGroup).order_by(RepoGroup.group_name)\
205 206 .filter(RepoGroup.group_parent_id == id).all()
206 207
207 208 return render('admin/repos_groups/repos_groups.html')
208 209
209 210 @HasPermissionAnyDecorator('hg.admin')
210 211 def edit(self, id, format='html'):
211 212 """GET /repos_groups/id/edit: Form to edit an existing item"""
212 213 # url('edit_repos_group', id=ID)
213 214
214 215 id_ = int(id)
215 216
216 217 c.repos_group = RepoGroup.get(id_)
217 218 defaults = self.__load_data(id_)
218 219
219 220 # we need to exclude this group from the group list for editing
220 221 c.repo_groups = filter(lambda x:x[0] != id_, c.repo_groups)
221 222
222 223 return htmlfill.render(
223 224 render('admin/repos_groups/repos_groups_edit.html'),
224 225 defaults=defaults,
225 226 encoding="UTF-8",
226 227 force_defaults=False
227 228 )
228 229
229 230
@@ -1,367 +1,371 b''
1 1 import traceback
2 2 import logging
3 3
4 from sqlalchemy.orm.exc import NoResultFound
5
4 6 from rhodecode.controllers.api import JSONRPCController, JSONRPCError
5 7 from rhodecode.lib.auth import HasPermissionAllDecorator, \
6 8 HasPermissionAnyDecorator
9
10 from rhodecode.model.meta import Session
7 11 from rhodecode.model.scm import ScmModel
8
9 12 from rhodecode.model.db import User, UsersGroup, RepoGroup, Repository
10 13 from rhodecode.model.repo import RepoModel
11 14 from rhodecode.model.user import UserModel
12 15 from rhodecode.model.repo_permission import RepositoryPermissionModel
13 16 from rhodecode.model.users_group import UsersGroupModel
14 from rhodecode.model import users_group
15 17 from rhodecode.model.repos_group import ReposGroupModel
16 from sqlalchemy.orm.exc import NoResultFound
18
17 19
18 20 log = logging.getLogger(__name__)
19 21
20 22
21 23 class ApiController(JSONRPCController):
22 24 """
23 25 API Controller
24 26
25 27
26 28 Each method needs to have USER as argument this is then based on given
27 29 API_KEY propagated as instance of user object
28 30
29 31 Preferably this should be first argument also
30 32
31 33
32 34 Each function should also **raise** JSONRPCError for any
33 35 errors that happens
34 36
35 37 """
36 38
37 39 @HasPermissionAllDecorator('hg.admin')
38 40 def pull(self, apiuser, repo):
39 41 """
40 42 Dispatch pull action on given repo
41 43
42 44
43 45 :param user:
44 46 :param repo:
45 47 """
46 48
47 49 if Repository.is_valid(repo) is False:
48 50 raise JSONRPCError('Unknown repo "%s"' % repo)
49 51
50 52 try:
51 53 ScmModel().pull_changes(repo, self.rhodecode_user.username)
52 54 return 'Pulled from %s' % repo
53 55 except Exception:
54 56 raise JSONRPCError('Unable to pull changes from "%s"' % repo)
55 57
56 58 @HasPermissionAllDecorator('hg.admin')
57 59 def get_user(self, apiuser, username):
58 60 """"
59 61 Get a user by username
60 62
61 63 :param apiuser
62 64 :param username
63 65 """
64 66
65 67 user = User.get_by_username(username)
66 68 if not user:
67 69 return None
68 70
69 71 return dict(id=user.user_id,
70 72 username=user.username,
71 73 firstname=user.name,
72 74 lastname=user.lastname,
73 75 email=user.email,
74 76 active=user.active,
75 77 admin=user.admin,
76 78 ldap=user.ldap_dn)
77 79
78 80 @HasPermissionAllDecorator('hg.admin')
79 81 def get_users(self, apiuser):
80 82 """"
81 83 Get all users
82 84
83 85 :param apiuser
84 86 """
85 87
86 88 result = []
87 89 for user in User.getAll():
88 90 result.append(dict(id=user.user_id,
89 91 username=user.username,
90 92 firstname=user.name,
91 93 lastname=user.lastname,
92 94 email=user.email,
93 95 active=user.active,
94 96 admin=user.admin,
95 97 ldap=user.ldap_dn))
96 98 return result
97 99
98 100 @HasPermissionAllDecorator('hg.admin')
99 101 def create_user(self, apiuser, username, password, firstname,
100 102 lastname, email, active=True, admin=False, ldap_dn=None):
101 103 """
102 104 Create new user
103 105
104 106 :param apiuser:
105 107 :param username:
106 108 :param password:
107 109 :param name:
108 110 :param lastname:
109 111 :param email:
110 112 :param active:
111 113 :param admin:
112 114 :param ldap_dn:
113 115 """
114 116
115 117 if User.get_by_username(username):
116 118 raise JSONRPCError("user %s already exist" % username)
117 119
118 120 try:
119 UserModel().create_or_update(username, password, email, firstname,
121 UserModel().create_or_update(username, password, email, firstname,
120 122 lastname, active, admin, ldap_dn)
123 Session().commit()
121 124 return dict(msg='created new user %s' % username)
122 125 except Exception:
123 126 log.error(traceback.format_exc())
124 127 raise JSONRPCError('failed to create user %s' % username)
125 128
126 129 @HasPermissionAllDecorator('hg.admin')
127 130 def get_users_group(self, apiuser, group_name):
128 131 """"
129 132 Get users group by name
130 133
131 134 :param apiuser
132 135 :param group_name
133 136 """
134 137
135 138 users_group = UsersGroup.get_by_group_name(group_name)
136 139 if not users_group:
137 140 return None
138 141
139 142 members = []
140 143 for user in users_group.members:
141 144 user = user.user
142 145 members.append(dict(id=user.user_id,
143 146 username=user.username,
144 147 firstname=user.name,
145 148 lastname=user.lastname,
146 149 email=user.email,
147 150 active=user.active,
148 151 admin=user.admin,
149 152 ldap=user.ldap_dn))
150 153
151 154 return dict(id=users_group.users_group_id,
152 155 name=users_group.users_group_name,
153 156 active=users_group.users_group_active,
154 157 members=members)
155 158
156 159 @HasPermissionAllDecorator('hg.admin')
157 160 def get_users_groups(self, apiuser):
158 161 """"
159 162 Get all users groups
160 163
161 164 :param apiuser
162 165 """
163 166
164 167 result = []
165 168 for users_group in UsersGroup.getAll():
166 169 members = []
167 170 for user in users_group.members:
168 171 user = user.user
169 172 members.append(dict(id=user.user_id,
170 173 username=user.username,
171 174 firstname=user.name,
172 175 lastname=user.lastname,
173 176 email=user.email,
174 177 active=user.active,
175 178 admin=user.admin,
176 179 ldap=user.ldap_dn))
177 180
178 181 result.append(dict(id=users_group.users_group_id,
179 182 name=users_group.users_group_name,
180 183 active=users_group.users_group_active,
181 184 members=members))
182 185 return result
183 186
184 187 @HasPermissionAllDecorator('hg.admin')
185 188 def create_users_group(self, apiuser, name, active=True):
186 189 """
187 190 Creates an new usergroup
188 191
189 192 :param name:
190 193 :param active:
191 194 """
192 195
193 196 if self.get_users_group(apiuser, name):
194 197 raise JSONRPCError("users group %s already exist" % name)
195 198
196 199 try:
197 form_data = dict(users_group_name=name,
198 users_group_active=active)
199 ug = UsersGroup.create(form_data)
200 ug = UsersGroupModel().create(name=name, active=active)
201 Session().commit()
200 202 return dict(id=ug.users_group_id,
201 203 msg='created new users group %s' % name)
202 204 except Exception:
203 205 log.error(traceback.format_exc())
204 206 raise JSONRPCError('failed to create group %s' % name)
205 207
206 208 @HasPermissionAllDecorator('hg.admin')
207 209 def add_user_to_users_group(self, apiuser, group_name, user_name):
208 210 """"
209 211 Add a user to a group
210 212
211 213 :param apiuser
212 214 :param group_name
213 215 :param user_name
214 216 """
215 217
216 218 try:
217 219 users_group = UsersGroup.get_by_group_name(group_name)
218 220 if not users_group:
219 221 raise JSONRPCError('unknown users group %s' % group_name)
220 222
221 223 try:
222 224 user = User.get_by_username(user_name)
223 225 except NoResultFound:
224 226 raise JSONRPCError('unknown user %s' % user_name)
225 227
226 228 ugm = UsersGroupModel().add_user_to_group(users_group, user)
227
229 Session().commit()
228 230 return dict(id=ugm.users_group_member_id,
229 231 msg='created new users group member')
230 232 except Exception:
231 233 log.error(traceback.format_exc())
232 234 raise JSONRPCError('failed to create users group member')
233 235
234 236 @HasPermissionAnyDecorator('hg.admin')
235 237 def get_repo(self, apiuser, repo_name):
236 238 """"
237 239 Get repository by name
238 240
239 241 :param apiuser
240 242 :param repo_name
241 243 """
242 244
243 245 try:
244 246 repo = Repository.get_by_repo_name(repo_name)
245 247 except NoResultFound:
246 248 return None
247 249
248 250 members = []
249 251 for user in repo.repo_to_perm:
250 252 perm = user.permission.permission_name
251 253 user = user.user
252 254 members.append(dict(type_="user",
253 255 id=user.user_id,
254 256 username=user.username,
255 257 firstname=user.name,
256 258 lastname=user.lastname,
257 259 email=user.email,
258 260 active=user.active,
259 261 admin=user.admin,
260 262 ldap=user.ldap_dn,
261 263 permission=perm))
262 264 for users_group in repo.users_group_to_perm:
263 265 perm = users_group.permission.permission_name
264 266 users_group = users_group.users_group
265 267 members.append(dict(type_="users_group",
266 268 id=users_group.users_group_id,
267 269 name=users_group.users_group_name,
268 270 active=users_group.users_group_active,
269 271 permission=perm))
270 272
271 273 return dict(id=repo.repo_id,
272 274 name=repo.repo_name,
273 275 type=repo.repo_type,
274 276 description=repo.description,
275 277 members=members)
276 278
277 279 @HasPermissionAnyDecorator('hg.admin')
278 280 def get_repos(self, apiuser):
279 281 """"
280 282 Get all repositories
281 283
282 284 :param apiuser
283 285 """
284 286
285 287 result = []
286 288 for repository in Repository.getAll():
287 289 result.append(dict(id=repository.repo_id,
288 290 name=repository.repo_name,
289 291 type=repository.repo_type,
290 292 description=repository.description))
291 293 return result
292 294
293 295 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
294 def create_repo(self, apiuser, name, owner_name, description='',
296 def create_repo(self, apiuser, name, owner_name, description='',
295 297 repo_type='hg', private=False):
296 298 """
297 299 Create a repository
298 300
299 301 :param apiuser
300 302 :param name
301 303 :param description
302 304 :param type
303 305 :param private
304 306 :param owner_name
305 307 """
306 308
307 309 try:
308 310 try:
309 311 owner = User.get_by_username(owner_name)
310 312 except NoResultFound:
311 313 raise JSONRPCError('unknown user %s' % owner)
312 314
313 315 if self.get_repo(apiuser, name):
314 316 raise JSONRPCError("repo %s already exist" % name)
315 317
316 318 groups = name.split('/')
317 319 real_name = groups[-1]
318 320 groups = groups[:-1]
319 321 parent_id = None
320 322 for g in groups:
321 323 group = RepoGroup.get_by_group_name(g)
322 324 if not group:
323 325 group = ReposGroupModel().create(dict(group_name=g,
324 326 group_description='',
325 327 group_parent_id=parent_id))
326 328 parent_id = group.group_id
327 329
328 330 RepoModel().create(dict(repo_name=real_name,
329 331 repo_name_full=name,
330 332 description=description,
331 333 private=private,
332 334 repo_type=repo_type,
333 335 repo_group=parent_id,
334 336 clone_uri=None), owner)
337 Session().commit()
335 338 except Exception:
336 339 log.error(traceback.format_exc())
337 340 raise JSONRPCError('failed to create repository %s' % name)
338 341
339 342 @HasPermissionAnyDecorator('hg.admin')
340 343 def add_user_to_repo(self, apiuser, repo_name, user_name, perm):
341 344 """
342 345 Add permission for a user to a repository
343 346
344 347 :param apiuser
345 348 :param repo_name
346 349 :param user_name
347 350 :param perm
348 351 """
349 352
350 353 try:
351 354 try:
352 355 repo = Repository.get_by_repo_name(repo_name)
353 356 except NoResultFound:
354 357 raise JSONRPCError('unknown repository %s' % repo)
355 358
356 359 try:
357 360 user = User.get_by_username(user_name)
358 361 except NoResultFound:
359 362 raise JSONRPCError('unknown user %s' % user)
360 363
361 364 RepositoryPermissionModel()\
362 365 .update_or_delete_user_permission(repo, user, perm)
366 Session().commit()
363 367 except Exception:
364 368 log.error(traceback.format_exc())
365 369 raise JSONRPCError('failed to edit permission %(repo)s for %(user)s'
366 370 % dict(user=user_name, repo=repo_name))
367 371
@@ -1,499 +1,468 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.db_manage
4 4 ~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Database creation, and setup module for RhodeCode. Used for creation
7 7 of database as well as for migration operations
8 8
9 9 :created_on: Apr 10, 2010
10 10 :author: marcink
11 11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software: you can redistribute it and/or modify
15 15 # it under the terms of the GNU General Public License as published by
16 16 # the Free Software Foundation, either version 3 of the License, or
17 17 # (at your option) any later version.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 26
27 27 import os
28 28 import sys
29 29 import uuid
30 30 import logging
31 31 from os.path import dirname as dn, join as jn
32 32
33 33 from rhodecode import __dbversion__
34 34 from rhodecode.model import meta
35 35
36 36 from rhodecode.model.user import UserModel
37 37 from rhodecode.lib.utils import ask_ok
38 38 from rhodecode.model import init_model
39 39 from rhodecode.model.db import User, Permission, RhodeCodeUi, \
40 40 RhodeCodeSetting, UserToPerm, DbMigrateVersion
41 41
42 42 from sqlalchemy.engine import create_engine
43 43
44 44 log = logging.getLogger(__name__)
45 45
46 46
47 47 class DbManage(object):
48 48 def __init__(self, log_sql, dbconf, root, tests=False):
49 49 self.dbname = dbconf.split('/')[-1]
50 50 self.tests = tests
51 51 self.root = root
52 52 self.dburi = dbconf
53 53 self.log_sql = log_sql
54 54 self.db_exists = False
55 55 self.init_db()
56 56
57 57 def init_db(self):
58 58 engine = create_engine(self.dburi, echo=self.log_sql)
59 59 init_model(engine)
60 60 self.sa = meta.Session()
61 61
62 62 def create_tables(self, override=False):
63 63 """Create a auth database
64 64 """
65 65
66 66 log.info("Any existing database is going to be destroyed")
67 67 if self.tests:
68 68 destroy = True
69 69 else:
70 70 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
71 71 if not destroy:
72 72 sys.exit()
73 73 if destroy:
74 74 meta.Base.metadata.drop_all()
75 75
76 76 checkfirst = not override
77 77 meta.Base.metadata.create_all(checkfirst=checkfirst)
78 78 log.info('Created tables for %s', self.dbname)
79 79
80 80 def set_db_version(self):
81 try:
82 ver = DbMigrateVersion()
83 ver.version = __dbversion__
84 ver.repository_id = 'rhodecode_db_migrations'
85 ver.repository_path = 'versions'
86 self.sa.add(ver)
87 self.sa.commit()
88 except:
89 self.sa.rollback()
90 raise
81 ver = DbMigrateVersion()
82 ver.version = __dbversion__
83 ver.repository_id = 'rhodecode_db_migrations'
84 ver.repository_path = 'versions'
85 self.sa.add(ver)
91 86 log.info('db version set to: %s', __dbversion__)
92 87
93 88 def upgrade(self):
94 89 """Upgrades given database schema to given revision following
95 90 all needed steps, to perform the upgrade
96 91
97 92 """
98 93
99 94 from rhodecode.lib.dbmigrate.migrate.versioning import api
100 95 from rhodecode.lib.dbmigrate.migrate.exceptions import \
101 96 DatabaseNotControlledError
102 97
103 98 upgrade = ask_ok('You are about to perform database upgrade, make '
104 99 'sure You backed up your database before. '
105 100 'Continue ? [y/n]')
106 101 if not upgrade:
107 102 sys.exit('Nothing done')
108 103
109 104 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
110 105 'rhodecode/lib/dbmigrate')
111 106 db_uri = self.dburi
112 107
113 108 try:
114 109 curr_version = api.db_version(db_uri, repository_path)
115 110 msg = ('Found current database under version'
116 111 ' control with version %s' % curr_version)
117 112
118 113 except (RuntimeError, DatabaseNotControlledError):
119 114 curr_version = 1
120 115 msg = ('Current database is not under version control. Setting'
121 116 ' as version %s' % curr_version)
122 117 api.version_control(db_uri, repository_path, curr_version)
123 118
124 119 print (msg)
125 120
126 121 if curr_version == __dbversion__:
127 122 sys.exit('This database is already at the newest version')
128 123
129 124 #======================================================================
130 125 # UPGRADE STEPS
131 126 #======================================================================
132 127 class UpgradeSteps(object):
133 128 """Those steps follow schema versions so for example schema
134 129 for example schema with seq 002 == step_2 and so on.
135 130 """
136 131
137 132 def __init__(self, klass):
138 133 self.klass = klass
139 134
140 135 def step_0(self):
141 136 #step 0 is the schema upgrade, and than follow proper upgrades
142 137 print ('attempting to do database upgrade to version %s' \
143 138 % __dbversion__)
144 139 api.upgrade(db_uri, repository_path, __dbversion__)
145 140 print ('Schema upgrade completed')
146 141
147 142 def step_1(self):
148 143 pass
149 144
150 145 def step_2(self):
151 146 print ('Patching repo paths for newer version of RhodeCode')
152 147 self.klass.fix_repo_paths()
153 148
154 149 print ('Patching default user of RhodeCode')
155 150 self.klass.fix_default_user()
156 151
157 152 log.info('Changing ui settings')
158 153 self.klass.create_ui_settings()
159 154
160 155 def step_3(self):
161 156 print ('Adding additional settings into RhodeCode db')
162 157 self.klass.fix_settings()
163 158 print ('Adding ldap defaults')
164 159 self.klass.create_ldap_options(skip_existing=True)
165
160
166 161 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
167 162
168 163 #CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
169 164 for step in upgrade_steps:
170 165 print ('performing upgrade step %s' % step)
171 166 getattr(UpgradeSteps(self), 'step_%s' % step)()
172 167
173 168 def fix_repo_paths(self):
174 169 """Fixes a old rhodecode version path into new one without a '*'
175 170 """
176 171
177 172 paths = self.sa.query(RhodeCodeUi)\
178 173 .filter(RhodeCodeUi.ui_key == '/')\
179 174 .scalar()
180 175
181 176 paths.ui_value = paths.ui_value.replace('*', '')
182 177
183 178 try:
184 179 self.sa.add(paths)
185 180 self.sa.commit()
186 181 except:
187 182 self.sa.rollback()
188 183 raise
189 184
190 185 def fix_default_user(self):
191 186 """Fixes a old default user with some 'nicer' default values,
192 187 used mostly for anonymous access
193 188 """
194 189 def_user = self.sa.query(User)\
195 190 .filter(User.username == 'default')\
196 191 .one()
197 192
198 193 def_user.name = 'Anonymous'
199 194 def_user.lastname = 'User'
200 195 def_user.email = 'anonymous@rhodecode.org'
201 196
202 197 try:
203 198 self.sa.add(def_user)
204 199 self.sa.commit()
205 200 except:
206 201 self.sa.rollback()
207 202 raise
208 203
209 204 def fix_settings(self):
210 205 """Fixes rhodecode settings adds ga_code key for google analytics
211 206 """
212 207
213 208 hgsettings3 = RhodeCodeSetting('ga_code', '')
214 209
215 210 try:
216 211 self.sa.add(hgsettings3)
217 212 self.sa.commit()
218 213 except:
219 214 self.sa.rollback()
220 215 raise
221 216
222 217 def admin_prompt(self, second=False):
223 218 if not self.tests:
224 219 import getpass
225 220
226 221 def get_password():
227 222 password = getpass.getpass('Specify admin password '
228 223 '(min 6 chars):')
229 224 confirm = getpass.getpass('Confirm password:')
230 225
231 226 if password != confirm:
232 227 log.error('passwords mismatch')
233 228 return False
234 229 if len(password) < 6:
235 230 log.error('password is to short use at least 6 characters')
236 231 return False
237 232
238 233 return password
239 234
240 235 username = raw_input('Specify admin username:')
241 236
242 237 password = get_password()
243 238 if not password:
244 239 #second try
245 240 password = get_password()
246 241 if not password:
247 242 sys.exit()
248 243
249 244 email = raw_input('Specify admin email:')
250 245 self.create_user(username, password, email, True)
251 246 else:
252 247 log.info('creating admin and regular test users')
253 248 self.create_user('test_admin', 'test12',
254 249 'test_admin@mail.com', True)
255 250 self.create_user('test_regular', 'test12',
256 251 'test_regular@mail.com', False)
257 252 self.create_user('test_regular2', 'test12',
258 253 'test_regular2@mail.com', False)
259 254
260 255 def create_ui_settings(self):
261 256 """Creates ui settings, fills out hooks
262 257 and disables dotencode
263 258
264 259 """
265 260 #HOOKS
266 261 hooks1_key = RhodeCodeUi.HOOK_UPDATE
267 262 hooks1_ = self.sa.query(RhodeCodeUi)\
268 263 .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
269 264
270 265 hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
271 266 hooks1.ui_section = 'hooks'
272 267 hooks1.ui_key = hooks1_key
273 268 hooks1.ui_value = 'hg update >&2'
274 269 hooks1.ui_active = False
275 270
276 271 hooks2_key = RhodeCodeUi.HOOK_REPO_SIZE
277 272 hooks2_ = self.sa.query(RhodeCodeUi)\
278 273 .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
279 274
280 275 hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
281 276 hooks2.ui_section = 'hooks'
282 277 hooks2.ui_key = hooks2_key
283 278 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
284 279
285 280 hooks3 = RhodeCodeUi()
286 281 hooks3.ui_section = 'hooks'
287 282 hooks3.ui_key = RhodeCodeUi.HOOK_PUSH
288 283 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
289 284
290 285 hooks4 = RhodeCodeUi()
291 286 hooks4.ui_section = 'hooks'
292 287 hooks4.ui_key = RhodeCodeUi.HOOK_PULL
293 288 hooks4.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
294 289
295 290 # For mercurial 1.7 set backward comapatibility with format
296 291 dotencode_disable = RhodeCodeUi()
297 292 dotencode_disable.ui_section = 'format'
298 293 dotencode_disable.ui_key = 'dotencode'
299 294 dotencode_disable.ui_value = 'false'
300 295
301 296 # enable largefiles
302 dotencode_disable = RhodeCodeUi()
303 dotencode_disable.ui_section = 'extensions'
304 dotencode_disable.ui_key = 'largefiles'
305 dotencode_disable.ui_value = '1'
297 largefiles = RhodeCodeUi()
298 largefiles.ui_section = 'extensions'
299 largefiles.ui_key = 'largefiles'
300 largefiles.ui_value = '1'
306 301
307 try:
308 self.sa.add(hooks1)
309 self.sa.add(hooks2)
310 self.sa.add(hooks3)
311 self.sa.add(hooks4)
312 self.sa.add(dotencode_disable)
313 self.sa.commit()
314 except:
315 self.sa.rollback()
316 raise
302 self.sa.add(hooks1)
303 self.sa.add(hooks2)
304 self.sa.add(hooks3)
305 self.sa.add(hooks4)
306 self.sa.add(largefiles)
317 307
318 def create_ldap_options(self,skip_existing=False):
308 def create_ldap_options(self, skip_existing=False):
319 309 """Creates ldap settings"""
320 310
321 try:
322 for k, v in [('ldap_active', 'false'), ('ldap_host', ''),
323 ('ldap_port', '389'), ('ldap_tls_kind', 'PLAIN'),
324 ('ldap_tls_reqcert', ''), ('ldap_dn_user', ''),
325 ('ldap_dn_pass', ''), ('ldap_base_dn', ''),
326 ('ldap_filter', ''), ('ldap_search_scope', ''),
327 ('ldap_attr_login', ''), ('ldap_attr_firstname', ''),
328 ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]:
311 for k, v in [('ldap_active', 'false'), ('ldap_host', ''),
312 ('ldap_port', '389'), ('ldap_tls_kind', 'PLAIN'),
313 ('ldap_tls_reqcert', ''), ('ldap_dn_user', ''),
314 ('ldap_dn_pass', ''), ('ldap_base_dn', ''),
315 ('ldap_filter', ''), ('ldap_search_scope', ''),
316 ('ldap_attr_login', ''), ('ldap_attr_firstname', ''),
317 ('ldap_attr_lastname', ''), ('ldap_attr_email', '')]:
329 318
330 if skip_existing and RhodeCodeSetting.get_by_name(k) != None:
331 log.debug('Skipping option %s' % k)
332 continue
333 setting = RhodeCodeSetting(k, v)
334 self.sa.add(setting)
335 self.sa.commit()
336 except:
337 self.sa.rollback()
338 raise
319 if skip_existing and RhodeCodeSetting.get_by_name(k) != None:
320 log.debug('Skipping option %s' % k)
321 continue
322 setting = RhodeCodeSetting(k, v)
323 self.sa.add(setting)
339 324
340 325 def config_prompt(self, test_repo_path='', retries=3):
341 326 if retries == 3:
342 327 log.info('Setting up repositories config')
343 328
344 329 if not self.tests and not test_repo_path:
345 330 path = raw_input('Specify valid full path to your repositories'
346 331 ' you can change this later in application settings:')
347 332 else:
348 333 path = test_repo_path
349 334 path_ok = True
350 335
351 336 #check proper dir
352 337 if not os.path.isdir(path):
353 338 path_ok = False
354 339 log.error('Given path %s is not a valid directory', path)
355 340
356 341 #check write access
357 342 if not os.access(path, os.W_OK) and path_ok:
358 343 path_ok = False
359 344 log.error('No write permission to given path %s', path)
360 345
361 346
362 347 if retries == 0:
363 348 sys.exit('max retries reached')
364 349 if path_ok is False:
365 350 retries -= 1
366 351 return self.config_prompt(test_repo_path, retries)
367 352
368 353 return path
369 354
370 355 def create_settings(self, path):
371 356
372 357 self.create_ui_settings()
373 358
374 359 #HG UI OPTIONS
375 360 web1 = RhodeCodeUi()
376 361 web1.ui_section = 'web'
377 362 web1.ui_key = 'push_ssl'
378 363 web1.ui_value = 'false'
379 364
380 365 web2 = RhodeCodeUi()
381 366 web2.ui_section = 'web'
382 367 web2.ui_key = 'allow_archive'
383 368 web2.ui_value = 'gz zip bz2'
384 369
385 370 web3 = RhodeCodeUi()
386 371 web3.ui_section = 'web'
387 372 web3.ui_key = 'allow_push'
388 373 web3.ui_value = '*'
389 374
390 375 web4 = RhodeCodeUi()
391 376 web4.ui_section = 'web'
392 377 web4.ui_key = 'baseurl'
393 378 web4.ui_value = '/'
394 379
395 380 paths = RhodeCodeUi()
396 381 paths.ui_section = 'paths'
397 382 paths.ui_key = '/'
398 383 paths.ui_value = path
399 384
400 385 hgsettings1 = RhodeCodeSetting('realm', 'RhodeCode authentication')
401 386 hgsettings2 = RhodeCodeSetting('title', 'RhodeCode')
402 387 hgsettings3 = RhodeCodeSetting('ga_code', '')
403 388
404 try:
405 self.sa.add(web1)
406 self.sa.add(web2)
407 self.sa.add(web3)
408 self.sa.add(web4)
409 self.sa.add(paths)
410 self.sa.add(hgsettings1)
411 self.sa.add(hgsettings2)
412 self.sa.add(hgsettings3)
413
414 self.sa.commit()
415 except:
416 self.sa.rollback()
417 raise
389 self.sa.add(web1)
390 self.sa.add(web2)
391 self.sa.add(web3)
392 self.sa.add(web4)
393 self.sa.add(paths)
394 self.sa.add(hgsettings1)
395 self.sa.add(hgsettings2)
396 self.sa.add(hgsettings3)
418 397
419 398 self.create_ldap_options()
420 399
421 400 log.info('created ui config')
422 401
423 402 def create_user(self, username, password, email='', admin=False):
424 403 log.info('creating user %s', username)
425 UserModel().create_or_update(username, password, email,
426 name='RhodeCode', lastname='Admin',
404 UserModel().create_or_update(username, password, email,
405 name='RhodeCode', lastname='Admin',
427 406 active=True, admin=admin)
428 407
429 408 def create_default_user(self):
430 409 log.info('creating default user')
431 410 # create default user for handling default permissions.
432 UserModel().create_or_update(username='default',
433 password=str(uuid.uuid1())[:8],
434 email='anonymous@rhodecode.org',
411 UserModel().create_or_update(username='default',
412 password=str(uuid.uuid1())[:8],
413 email='anonymous@rhodecode.org',
435 414 name='Anonymous', lastname='User')
436
415
437 416 def create_permissions(self):
438 417 #module.(access|create|change|delete)_[name]
439 418 #module.(read|write|owner)
440 419 perms = [('repository.none', 'Repository no access'),
441 420 ('repository.read', 'Repository read access'),
442 421 ('repository.write', 'Repository write access'),
443 422 ('repository.admin', 'Repository admin access'),
444 423 ('hg.admin', 'Hg Administrator'),
445 424 ('hg.create.repository', 'Repository create'),
446 425 ('hg.create.none', 'Repository creation disabled'),
447 426 ('hg.register.none', 'Register disabled'),
448 427 ('hg.register.manual_activate', 'Register new user with '
449 428 'RhodeCode without manual'
450 429 'activation'),
451 430
452 431 ('hg.register.auto_activate', 'Register new user with '
453 432 'RhodeCode without auto '
454 433 'activation'),
455 434 ]
456 435
457 436 for p in perms:
458 437 new_perm = Permission()
459 438 new_perm.permission_name = p[0]
460 439 new_perm.permission_longname = p[1]
461 try:
462 self.sa.add(new_perm)
463 self.sa.commit()
464 except:
465 self.sa.rollback()
466 raise
440 self.sa.add(new_perm)
467 441
468 442 def populate_default_permissions(self):
469 443 log.info('creating default user permissions')
470 444
471 445 default_user = self.sa.query(User)\
472 446 .filter(User.username == 'default').scalar()
473 447
474 448 reg_perm = UserToPerm()
475 449 reg_perm.user = default_user
476 450 reg_perm.permission = self.sa.query(Permission)\
477 451 .filter(Permission.permission_name == 'hg.register.manual_activate')\
478 452 .scalar()
479 453
480 454 create_repo_perm = UserToPerm()
481 455 create_repo_perm.user = default_user
482 456 create_repo_perm.permission = self.sa.query(Permission)\
483 457 .filter(Permission.permission_name == 'hg.create.repository')\
484 458 .scalar()
485 459
486 460 default_repo_perm = UserToPerm()
487 461 default_repo_perm.user = default_user
488 462 default_repo_perm.permission = self.sa.query(Permission)\
489 463 .filter(Permission.permission_name == 'repository.read')\
490 464 .scalar()
491 465
492 try:
493 self.sa.add(reg_perm)
494 self.sa.add(create_repo_perm)
495 self.sa.add(default_repo_perm)
496 self.sa.commit()
497 except:
498 self.sa.rollback()
499 raise
466 self.sa.add(reg_perm)
467 self.sa.add(create_repo_perm)
468 self.sa.add(default_repo_perm)
@@ -1,600 +1,601 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.utils
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Utilities library for RhodeCode
7 7
8 8 :created_on: Apr 18, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import os
27 27 import logging
28 28 import datetime
29 29 import traceback
30 30 import paste
31 31 import beaker
32 32 import tarfile
33 33 import shutil
34 34 from os.path import abspath
35 35 from os.path import dirname as dn, join as jn
36 36
37 37 from paste.script.command import Command, BadCommand
38 38
39 39 from mercurial import ui, config
40 40
41 41 from webhelpers.text import collapse, remove_formatting, strip_tags
42 42
43 43 from vcs import get_backend
44 44 from vcs.backends.base import BaseChangeset
45 45 from vcs.utils.lazy import LazyProperty
46 46 from vcs.utils.helpers import get_scm
47 47 from vcs.exceptions import VCSError
48 48
49 49 from rhodecode.lib.caching_query import FromCache
50 50
51 51 from rhodecode.model import meta
52 52 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
53 53 UserLog, RepoGroup, RhodeCodeSetting
54 from rhodecode.model.meta import Session
54 55
55 56 log = logging.getLogger(__name__)
56 57
57 58
58 59 def recursive_replace(str_, replace=' '):
59 60 """Recursive replace of given sign to just one instance
60 61
61 62 :param str_: given string
62 63 :param replace: char to find and replace multiple instances
63 64
64 65 Examples::
65 66 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
66 67 'Mighty-Mighty-Bo-sstones'
67 68 """
68 69
69 70 if str_.find(replace * 2) == -1:
70 71 return str_
71 72 else:
72 73 str_ = str_.replace(replace * 2, replace)
73 74 return recursive_replace(str_, replace)
74 75
75 76
76 77 def repo_name_slug(value):
77 78 """Return slug of name of repository
78 79 This function is called on each creation/modification
79 80 of repository to prevent bad names in repo
80 81 """
81 82
82 83 slug = remove_formatting(value)
83 84 slug = strip_tags(slug)
84 85
85 86 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
86 87 slug = slug.replace(c, '-')
87 88 slug = recursive_replace(slug, '-')
88 89 slug = collapse(slug, '-')
89 90 return slug
90 91
91 92
92 93 def get_repo_slug(request):
93 94 return request.environ['pylons.routes_dict'].get('repo_name')
94 95
95 96
96 97 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
97 98 """
98 99 Action logger for various actions made by users
99 100
100 101 :param user: user that made this action, can be a unique username string or
101 102 object containing user_id attribute
102 103 :param action: action to log, should be on of predefined unique actions for
103 104 easy translations
104 105 :param repo: string name of repository or object containing repo_id,
105 106 that action was made on
106 107 :param ipaddr: optional ip address from what the action was made
107 108 :param sa: optional sqlalchemy session
108 109
109 110 """
110 111
111 112 if not sa:
112 113 sa = meta.Session()
113 114
114 115 try:
115 116 if hasattr(user, 'user_id'):
116 117 user_obj = user
117 118 elif isinstance(user, basestring):
118 119 user_obj = User.get_by_username(user)
119 120 else:
120 121 raise Exception('You have to provide user object or username')
121 122
122 123 if hasattr(repo, 'repo_id'):
123 124 repo_obj = Repository.get(repo.repo_id)
124 125 repo_name = repo_obj.repo_name
125 126 elif isinstance(repo, basestring):
126 127 repo_name = repo.lstrip('/')
127 128 repo_obj = Repository.get_by_repo_name(repo_name)
128 129 else:
129 130 raise Exception('You have to provide repository to action logger')
130 131
131 132 user_log = UserLog()
132 133 user_log.user_id = user_obj.user_id
133 134 user_log.action = action
134 135
135 136 user_log.repository_id = repo_obj.repo_id
136 137 user_log.repository_name = repo_name
137 138
138 139 user_log.action_date = datetime.datetime.now()
139 140 user_log.user_ip = ipaddr
140 141 sa.add(user_log)
141 142
142 143 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
143 144 if commit:
144 145 sa.commit()
145 146 except:
146 147 log.error(traceback.format_exc())
147 148 raise
148 149
149 150
150 151 def get_repos(path, recursive=False):
151 152 """
152 153 Scans given path for repos and return (name,(type,path)) tuple
153 154
154 155 :param path: path to scann for repositories
155 156 :param recursive: recursive search and return names with subdirs in front
156 157 """
157 158
158 159 if path.endswith(os.sep):
159 160 #remove ending slash for better results
160 161 path = path[:-1]
161 162
162 163 def _get_repos(p):
163 164 if not os.access(p, os.W_OK):
164 165 return
165 166 for dirpath in os.listdir(p):
166 167 if os.path.isfile(os.path.join(p, dirpath)):
167 168 continue
168 169 cur_path = os.path.join(p, dirpath)
169 170 try:
170 171 scm_info = get_scm(cur_path)
171 172 yield scm_info[1].split(path)[-1].lstrip(os.sep), scm_info
172 173 except VCSError:
173 174 if not recursive:
174 175 continue
175 176 #check if this dir containts other repos for recursive scan
176 177 rec_path = os.path.join(p, dirpath)
177 178 if os.path.isdir(rec_path):
178 179 for inner_scm in _get_repos(rec_path):
179 180 yield inner_scm
180 181
181 182 return _get_repos(path)
182 183
183 184
184 185 def is_valid_repo(repo_name, base_path):
185 186 """
186 187 Returns True if given path is a valid repository False otherwise
187 188 :param repo_name:
188 189 :param base_path:
189 190
190 191 :return True: if given path is a valid repository
191 192 """
192 193 full_path = os.path.join(base_path, repo_name)
193 194
194 195 try:
195 196 get_scm(full_path)
196 197 return True
197 198 except VCSError:
198 199 return False
199 200
200 201 def is_valid_repos_group(repos_group_name, base_path):
201 202 """
202 203 Returns True if given path is a repos group False otherwise
203 204
204 205 :param repo_name:
205 206 :param base_path:
206 207 """
207 208 full_path = os.path.join(base_path, repos_group_name)
208 209
209 210 # check if it's not a repo
210 211 if is_valid_repo(repos_group_name, base_path):
211 212 return False
212 213
213 214 # check if it's a valid path
214 215 if os.path.isdir(full_path):
215 216 return True
216 217
217 218 return False
218 219
219 220 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
220 221 while True:
221 222 ok = raw_input(prompt)
222 223 if ok in ('y', 'ye', 'yes'):
223 224 return True
224 225 if ok in ('n', 'no', 'nop', 'nope'):
225 226 return False
226 227 retries = retries - 1
227 228 if retries < 0:
228 229 raise IOError
229 230 print complaint
230 231
231 232 #propagated from mercurial documentation
232 233 ui_sections = ['alias', 'auth',
233 234 'decode/encode', 'defaults',
234 235 'diff', 'email',
235 236 'extensions', 'format',
236 237 'merge-patterns', 'merge-tools',
237 238 'hooks', 'http_proxy',
238 239 'smtp', 'patch',
239 240 'paths', 'profiling',
240 241 'server', 'trusted',
241 242 'ui', 'web', ]
242 243
243 244
244 245 def make_ui(read_from='file', path=None, checkpaths=True):
245 246 """A function that will read python rc files or database
246 247 and make an mercurial ui object from read options
247 248
248 249 :param path: path to mercurial config file
249 250 :param checkpaths: check the path
250 251 :param read_from: read from 'file' or 'db'
251 252 """
252 253
253 254 baseui = ui.ui()
254 255
255 256 #clean the baseui object
256 257 baseui._ocfg = config.config()
257 258 baseui._ucfg = config.config()
258 259 baseui._tcfg = config.config()
259 260
260 261 if read_from == 'file':
261 262 if not os.path.isfile(path):
262 263 log.warning('Unable to read config file %s' % path)
263 264 return False
264 265 log.debug('reading hgrc from %s', path)
265 266 cfg = config.config()
266 267 cfg.read(path)
267 268 for section in ui_sections:
268 269 for k, v in cfg.items(section):
269 270 log.debug('settings ui from file[%s]%s:%s', section, k, v)
270 271 baseui.setconfig(section, k, v)
271 272
272 273 elif read_from == 'db':
273 274 sa = meta.Session()
274 275 ret = sa.query(RhodeCodeUi)\
275 276 .options(FromCache("sql_cache_short",
276 277 "get_hg_ui_settings")).all()
277 278
278 279 hg_ui = ret
279 280 for ui_ in hg_ui:
280 281 if ui_.ui_active:
281 282 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
282 283 ui_.ui_key, ui_.ui_value)
283 284 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
284 285
285 286 meta.Session.remove()
286 287 return baseui
287 288
288 289
289 290 def set_rhodecode_config(config):
290 291 """
291 292 Updates pylons config with new settings from database
292 293
293 294 :param config:
294 295 """
295 296 hgsettings = RhodeCodeSetting.get_app_settings()
296 297
297 298 for k, v in hgsettings.items():
298 299 config[k] = v
299 300
300 301
301 302 def invalidate_cache(cache_key, *args):
302 303 """
303 304 Puts cache invalidation task into db for
304 305 further global cache invalidation
305 306 """
306 307
307 308 from rhodecode.model.scm import ScmModel
308 309
309 310 if cache_key.startswith('get_repo_cached_'):
310 311 name = cache_key.split('get_repo_cached_')[-1]
311 312 ScmModel().mark_for_invalidation(name)
312 313
313 314
314 315 class EmptyChangeset(BaseChangeset):
315 316 """
316 317 An dummy empty changeset. It's possible to pass hash when creating
317 318 an EmptyChangeset
318 319 """
319 320
320 321 def __init__(self, cs='0' * 40, repo=None, requested_revision=None, alias=None):
321 322 self._empty_cs = cs
322 323 self.revision = -1
323 324 self.message = ''
324 325 self.author = ''
325 326 self.date = ''
326 327 self.repository = repo
327 328 self.requested_revision = requested_revision
328 329 self.alias = alias
329 330
330 331 @LazyProperty
331 332 def raw_id(self):
332 333 """
333 334 Returns raw string identifying this changeset, useful for web
334 335 representation.
335 336 """
336 337
337 338 return self._empty_cs
338 339
339 340 @LazyProperty
340 341 def branch(self):
341 342 return get_backend(self.alias).DEFAULT_BRANCH_NAME
342 343
343 344 @LazyProperty
344 345 def short_id(self):
345 346 return self.raw_id[:12]
346 347
347 348 def get_file_changeset(self, path):
348 349 return self
349 350
350 351 def get_file_content(self, path):
351 352 return u''
352 353
353 354 def get_file_size(self, path):
354 355 return 0
355 356
356 357
357 358 def map_groups(groups):
358 359 """
359 360 Checks for groups existence, and creates groups structures.
360 361 It returns last group in structure
361 362
362 363 :param groups: list of groups structure
363 364 """
364 365 sa = meta.Session()
365 366
366 367 parent = None
367 368 group = None
368 369
369 370 # last element is repo in nested groups structure
370 371 groups = groups[:-1]
371 372
372 373 for lvl, group_name in enumerate(groups):
373 374 group_name = '/'.join(groups[:lvl] + [group_name])
374 375 group = sa.query(RepoGroup).filter(RepoGroup.group_name == group_name).scalar()
375 376
376 377 if group is None:
377 378 group = RepoGroup(group_name, parent)
378 379 sa.add(group)
379 380 sa.commit()
380 381 parent = group
381 382 return group
382 383
383 384
384 385 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
385 386 """
386 387 maps all repos given in initial_repo_list, non existing repositories
387 388 are created, if remove_obsolete is True it also check for db entries
388 389 that are not in initial_repo_list and removes them.
389 390
390 391 :param initial_repo_list: list of repositories found by scanning methods
391 392 :param remove_obsolete: check for obsolete entries in database
392 393 """
393 394 from rhodecode.model.repo import RepoModel
394 395 sa = meta.Session()
395 396 rm = RepoModel()
396 397 user = sa.query(User).filter(User.admin == True).first()
397 398 if user is None:
398 399 raise Exception('Missing administrative account !')
399 400 added = []
400 401
401 402 for name, repo in initial_repo_list.items():
402 403 group = map_groups(name.split(Repository.url_sep()))
403 404 if not rm.get_by_repo_name(name, cache=False):
404 405 log.info('repository %s not found creating default', name)
405 406 added.append(name)
406 407 form_data = {
407 408 'repo_name': name,
408 409 'repo_name_full': name,
409 410 'repo_type': repo.alias,
410 411 'description': repo.description \
411 412 if repo.description != 'unknown' else \
412 413 '%s repository' % name,
413 414 'private': False,
414 415 'group_id': getattr(group, 'group_id', None)
415 416 }
416 417 rm.create(form_data, user, just_db=True)
417 418 sa.commit()
418 419 removed = []
419 420 if remove_obsolete:
420 421 #remove from database those repositories that are not in the filesystem
421 422 for repo in sa.query(Repository).all():
422 423 if repo.repo_name not in initial_repo_list.keys():
423 424 removed.append(repo.repo_name)
424 425 sa.delete(repo)
425 426 sa.commit()
426 427
427 428 return added, removed
428 429
429 430 # set cache regions for beaker so celery can utilise it
430 431 def add_cache(settings):
431 432 cache_settings = {'regions': None}
432 433 for key in settings.keys():
433 434 for prefix in ['beaker.cache.', 'cache.']:
434 435 if key.startswith(prefix):
435 436 name = key.split(prefix)[1].strip()
436 437 cache_settings[name] = settings[key].strip()
437 438 if cache_settings['regions']:
438 439 for region in cache_settings['regions'].split(','):
439 440 region = region.strip()
440 441 region_settings = {}
441 442 for key, value in cache_settings.items():
442 443 if key.startswith(region):
443 444 region_settings[key.split('.')[1]] = value
444 445 region_settings['expire'] = int(region_settings.get('expire',
445 446 60))
446 447 region_settings.setdefault('lock_dir',
447 448 cache_settings.get('lock_dir'))
448 449 region_settings.setdefault('data_dir',
449 450 cache_settings.get('data_dir'))
450 451
451 452 if 'type' not in region_settings:
452 453 region_settings['type'] = cache_settings.get('type',
453 454 'memory')
454 455 beaker.cache.cache_regions[region] = region_settings
455 456
456 457
457 458 #==============================================================================
458 459 # TEST FUNCTIONS AND CREATORS
459 460 #==============================================================================
460 461 def create_test_index(repo_location, config, full_index):
461 462 """
462 463 Makes default test index
463 464
464 465 :param config: test config
465 466 :param full_index:
466 467 """
467 468
468 469 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
469 470 from rhodecode.lib.pidlock import DaemonLock, LockHeld
470 471
471 472 repo_location = repo_location
472 473
473 474 index_location = os.path.join(config['app_conf']['index_dir'])
474 475 if not os.path.exists(index_location):
475 476 os.makedirs(index_location)
476 477
477 478 try:
478 479 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
479 480 WhooshIndexingDaemon(index_location=index_location,
480 481 repo_location=repo_location)\
481 482 .run(full_index=full_index)
482 483 l.release()
483 484 except LockHeld:
484 485 pass
485 486
486 487
487 488 def create_test_env(repos_test_path, config):
488 489 """
489 490 Makes a fresh database and
490 491 install test repository into tmp dir
491 492 """
492 493 from rhodecode.lib.db_manage import DbManage
493 494 from rhodecode.tests import HG_REPO, TESTS_TMP_PATH
494 495
495 496 # PART ONE create db
496 497 dbconf = config['sqlalchemy.db1.url']
497 498 log.debug('making test db %s', dbconf)
498 499
499 500 # create test dir if it doesn't exist
500 501 if not os.path.isdir(repos_test_path):
501 502 log.debug('Creating testdir %s' % repos_test_path)
502 503 os.makedirs(repos_test_path)
503 504
504 505 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
505 506 tests=True)
506 507 dbmanage.create_tables(override=True)
507 508 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
508 509 dbmanage.create_default_user()
509 510 dbmanage.admin_prompt()
510 511 dbmanage.create_permissions()
511 512 dbmanage.populate_default_permissions()
512
513 Session().commit()
513 514 # PART TWO make test repo
514 515 log.debug('making test vcs repositories')
515 516
516 517 idx_path = config['app_conf']['index_dir']
517 518 data_path = config['app_conf']['cache_dir']
518 519
519 520 #clean index and data
520 521 if idx_path and os.path.exists(idx_path):
521 522 log.debug('remove %s' % idx_path)
522 523 shutil.rmtree(idx_path)
523 524
524 525 if data_path and os.path.exists(data_path):
525 526 log.debug('remove %s' % data_path)
526 527 shutil.rmtree(data_path)
527 528
528 529 #CREATE DEFAULT HG REPOSITORY
529 530 cur_dir = dn(dn(abspath(__file__)))
530 531 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
531 532 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
532 533 tar.close()
533 534
534 535
535 536 #==============================================================================
536 537 # PASTER COMMANDS
537 538 #==============================================================================
538 539 class BasePasterCommand(Command):
539 540 """
540 541 Abstract Base Class for paster commands.
541 542
542 543 The celery commands are somewhat aggressive about loading
543 544 celery.conf, and since our module sets the `CELERY_LOADER`
544 545 environment variable to our loader, we have to bootstrap a bit and
545 546 make sure we've had a chance to load the pylons config off of the
546 547 command line, otherwise everything fails.
547 548 """
548 549 min_args = 1
549 550 min_args_error = "Please provide a paster config file as an argument."
550 551 takes_config_file = 1
551 552 requires_config_file = True
552 553
553 554 def notify_msg(self, msg, log=False):
554 555 """Make a notification to user, additionally if logger is passed
555 556 it logs this action using given logger
556 557
557 558 :param msg: message that will be printed to user
558 559 :param log: logging instance, to use to additionally log this message
559 560
560 561 """
561 562 if log and isinstance(log, logging):
562 563 log(msg)
563 564
564 565 def run(self, args):
565 566 """
566 567 Overrides Command.run
567 568
568 569 Checks for a config file argument and loads it.
569 570 """
570 571 if len(args) < self.min_args:
571 572 raise BadCommand(
572 573 self.min_args_error % {'min_args': self.min_args,
573 574 'actual_args': len(args)})
574 575
575 576 # Decrement because we're going to lob off the first argument.
576 577 # @@ This is hacky
577 578 self.min_args -= 1
578 579 self.bootstrap_config(args[0])
579 580 self.update_parser()
580 581 return super(BasePasterCommand, self).run(args[1:])
581 582
582 583 def update_parser(self):
583 584 """
584 585 Abstract method. Allows for the class's parser to be updated
585 586 before the superclass's `run` method is called. Necessary to
586 587 allow options/arguments to be passed through to the underlying
587 588 celery command.
588 589 """
589 590 raise NotImplementedError("Abstract Method.")
590 591
591 592 def bootstrap_config(self, conf):
592 593 """
593 594 Loads the pylons configuration.
594 595 """
595 596 from pylons import config as pylonsconfig
596 597
597 598 path_to_ini_file = os.path.realpath(conf)
598 599 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
599 600 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
600 601
@@ -1,145 +1,144 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.comment
4 4 ~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 comments model for RhodeCode
7 7
8 8 :created_on: Nov 11, 2011
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28
29 29 from pylons.i18n.translation import _
30 30 from sqlalchemy.util.compat import defaultdict
31 31
32 32 from rhodecode.lib import extract_mentioned_users
33 33 from rhodecode.lib import helpers as h
34 34 from rhodecode.model import BaseModel
35 35 from rhodecode.model.db import ChangesetComment, User, Repository, Notification
36 36 from rhodecode.model.notification import NotificationModel
37 37
38 38 log = logging.getLogger(__name__)
39 39
40 40
41 41 class ChangesetCommentsModel(BaseModel):
42 42
43 43 def __get_changeset_comment(self, changeset_comment):
44 44 return self._get_instance(ChangesetComment, changeset_comment)
45 45
46 46 def _extract_mentions(self, s):
47 47 user_objects = []
48 48 for username in extract_mentioned_users(s):
49 49 user_obj = User.get_by_username(username, case_insensitive=True)
50 50 if user_obj:
51 51 user_objects.append(user_obj)
52 52 return user_objects
53 53
54 54 def create(self, text, repo_id, user_id, revision, f_path=None,
55 55 line_no=None):
56 56 """
57 57 Creates new comment for changeset
58 58
59 59 :param text:
60 60 :param repo_id:
61 61 :param user_id:
62 62 :param revision:
63 63 :param f_path:
64 64 :param line_no:
65 65 """
66 66 if text:
67 67 repo = Repository.get(repo_id)
68 68 cs = repo.scm_instance.get_changeset(revision)
69 69 desc = cs.message
70 70 author = cs.author_email
71 71 comment = ChangesetComment()
72 72 comment.repo = repo
73 73 comment.user_id = user_id
74 74 comment.revision = revision
75 75 comment.text = text
76 76 comment.f_path = f_path
77 77 comment.line_no = line_no
78 78
79 79 self.sa.add(comment)
80 80 self.sa.flush()
81 81
82 82 # make notification
83 83 line = ''
84 84 if line_no:
85 85 line = _('on line %s') % line_no
86 86 subj = h.link_to('Re commit: %(commit_desc)s %(line)s' % \
87 87 {'commit_desc':desc, 'line':line},
88 88 h.url('changeset_home', repo_name=repo.repo_name,
89 89 revision=revision,
90 90 anchor='comment-%s' % comment.comment_id,
91 91 qualified=True,
92 92 )
93 93 )
94 94 body = text
95 95 recipients = ChangesetComment.get_users(revision=revision)
96 96 # add changeset author
97 97 recipients += [User.get_by_email(author)]
98 98
99 99 NotificationModel().create(created_by=user_id, subject=subj,
100 100 body=body, recipients=recipients,
101 101 type_=Notification.TYPE_CHANGESET_COMMENT)
102 102
103 103 mention_recipients = set(self._extract_mentions(body))\
104 104 .difference(recipients)
105 105 if mention_recipients:
106 106 subj = _('[Mention]') + ' ' + subj
107 107 NotificationModel().create(created_by=user_id, subject=subj,
108 108 body=body,
109 109 recipients=mention_recipients,
110 110 type_=Notification.TYPE_CHANGESET_COMMENT)
111 111
112 self.sa.commit()
113 112 return comment
114 113
115 114 def delete(self, comment):
116 115 """
117 116 Deletes given comment
118 117
119 118 :param comment_id:
120 119 """
121 120 comment = self.__get_changeset_comment(comment)
122 121 self.sa.delete(comment)
123 122
124 123 return comment
125 124
126 125
127 126 def get_comments(self, repo_id, revision):
128 127 return ChangesetComment.query()\
129 128 .filter(ChangesetComment.repo_id == repo_id)\
130 129 .filter(ChangesetComment.revision == revision)\
131 130 .filter(ChangesetComment.line_no == None)\
132 131 .filter(ChangesetComment.f_path == None).all()
133 132
134 133 def get_inline_comments(self, repo_id, revision):
135 134 comments = self.sa.query(ChangesetComment)\
136 135 .filter(ChangesetComment.repo_id == repo_id)\
137 136 .filter(ChangesetComment.revision == revision)\
138 137 .filter(ChangesetComment.line_no != None)\
139 138 .filter(ChangesetComment.f_path != None).all()
140 139
141 140 paths = defaultdict(lambda:defaultdict(list))
142 141
143 142 for co in comments:
144 143 paths[co.f_path][co.line_no].append(co)
145 144 return paths.items()
@@ -1,117 +1,115 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.permission
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 permissions model for RhodeCode
7 7
8 8 :created_on: Aug 20, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28
29 29 from sqlalchemy.exc import DatabaseError
30 30
31 31 from rhodecode.lib.caching_query import FromCache
32 32
33 33 from rhodecode.model import BaseModel
34 34 from rhodecode.model.db import User, Permission, UserToPerm, UserRepoToPerm
35 35
36 36 log = logging.getLogger(__name__)
37 37
38 38
39 39 class PermissionModel(BaseModel):
40 40 """
41 41 Permissions model for RhodeCode
42 42 """
43 43
44 44 def get_permission(self, permission_id, cache=False):
45 45 """
46 46 Get's permissions by id
47 47
48 48 :param permission_id: id of permission to get from database
49 49 :param cache: use Cache for this query
50 50 """
51 51 perm = self.sa.query(Permission)
52 52 if cache:
53 53 perm = perm.options(FromCache("sql_cache_short",
54 54 "get_permission_%s" % permission_id))
55 55 return perm.get(permission_id)
56 56
57 57 def get_permission_by_name(self, name, cache=False):
58 58 """
59 59 Get's permissions by given name
60 60
61 61 :param name: name to fetch
62 62 :param cache: Use cache for this query
63 63 """
64 64 perm = self.sa.query(Permission)\
65 65 .filter(Permission.permission_name == name)
66 66 if cache:
67 67 perm = perm.options(FromCache("sql_cache_short",
68 68 "get_permission_%s" % name))
69 69 return perm.scalar()
70 70
71 71 def update(self, form_result):
72 72 perm_user = self.sa.query(User)\
73 .filter(User.username ==
74 form_result['perm_user_name']).scalar()
73 .filter(User.username ==
74 form_result['perm_user_name']).scalar()
75 75 u2p = self.sa.query(UserToPerm).filter(UserToPerm.user ==
76 76 perm_user).all()
77 77 if len(u2p) != 3:
78 78 raise Exception('Defined: %s should be 3 permissions for default'
79 79 ' user. This should not happen please verify'
80 80 ' your database' % len(u2p))
81 81
82 82 try:
83 83 # stage 1 change defaults
84 84 for p in u2p:
85 85 if p.permission.permission_name.startswith('repository.'):
86 86 p.permission = self.get_permission_by_name(
87 87 form_result['default_perm'])
88 88 self.sa.add(p)
89 89
90 90 if p.permission.permission_name.startswith('hg.register.'):
91 91 p.permission = self.get_permission_by_name(
92 92 form_result['default_register'])
93 93 self.sa.add(p)
94 94
95 95 if p.permission.permission_name.startswith('hg.create.'):
96 96 p.permission = self.get_permission_by_name(
97 97 form_result['default_create'])
98 98 self.sa.add(p)
99 99
100 100 #stage 2 update all default permissions for repos if checked
101 101 if form_result['overwrite_default'] == True:
102 102 for r2p in self.sa.query(UserRepoToPerm)\
103 103 .filter(UserRepoToPerm.user == perm_user).all():
104 104 r2p.permission = self.get_permission_by_name(
105 105 form_result['default_perm'])
106 106 self.sa.add(r2p)
107 107
108 108 # stage 3 set anonymous access
109 109 if perm_user.username == 'default':
110 110 perm_user.active = bool(form_result['anonymous'])
111 111 self.sa.add(perm_user)
112 112
113 self.sa.commit()
114 113 except (DatabaseError,):
115 114 log.error(traceback.format_exc())
116 self.sa.rollback()
117 115 raise
@@ -1,65 +1,63 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.users_group
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 repository permission model for RhodeCode
7 7
8 8 :created_on: Oct 1, 2011
9 9 :author: nvinot
10 10 :copyright: (C) 2011-2011 Nicolas Vinot <aeris@imirhil.fr>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 from rhodecode.model import BaseModel
28 28 from rhodecode.model.db import UserRepoToPerm, Permission
29 29
30 30 log = logging.getLogger(__name__)
31 31
32 32
33 33 class RepositoryPermissionModel(BaseModel):
34 34
35 35 def get_user_permission(self, repository, user):
36 36 return UserRepoToPerm.query() \
37 37 .filter(UserRepoToPerm.user == user) \
38 38 .filter(UserRepoToPerm.repository == repository) \
39 39 .scalar()
40 40
41 41 def update_user_permission(self, repository, user, permission):
42 42 permission = Permission.get_by_key(permission)
43 43 current = self.get_user_permission(repository, user)
44 44 if current:
45 45 if not current.permission is permission:
46 46 current.permission = permission
47 47 else:
48 48 p = UserRepoToPerm()
49 49 p.user = user
50 50 p.repository = repository
51 51 p.permission = permission
52 52 self.sa.add(p)
53 self.sa.commit()
54 53
55 54 def delete_user_permission(self, repository, user):
56 55 current = self.get_user_permission(repository, user)
57 56 if current:
58 57 self.sa.delete(current)
59 self.sa.commit()
60 58
61 59 def update_or_delete_user_permission(self, repository, user, permission):
62 60 if permission:
63 61 self.update_user_permission(repository, user, permission)
64 62 else:
65 63 self.delete_user_permission(repository, user)
@@ -1,163 +1,157 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.user_group
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 users groups model for RhodeCode
7 7
8 8 :created_on: Jan 25, 2011
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import os
27 27 import logging
28 28 import traceback
29 29 import shutil
30 30
31 31 from pylons.i18n.translation import _
32 32
33 33 from vcs.utils.lazy import LazyProperty
34 34
35 35 from rhodecode.model import BaseModel
36 36 from rhodecode.model.db import RepoGroup, RhodeCodeUi
37 37
38 38 log = logging.getLogger(__name__)
39 39
40 40
41 41 class ReposGroupModel(BaseModel):
42 42
43 43 @LazyProperty
44 44 def repos_path(self):
45 45 """
46 46 Get's the repositories root path from database
47 47 """
48 48
49 49 q = RhodeCodeUi.get_by_key('/').one()
50 50 return q.ui_value
51 51
52 52 def __create_group(self, group_name):
53 53 """
54 54 makes repositories group on filesystem
55 55
56 56 :param repo_name:
57 57 :param parent_id:
58 58 """
59 59
60 60 create_path = os.path.join(self.repos_path, group_name)
61 61 log.debug('creating new group in %s', create_path)
62 62
63 63 if os.path.isdir(create_path):
64 64 raise Exception('That directory already exists !')
65 65
66 66 os.makedirs(create_path)
67 67
68 68 def __rename_group(self, old, new):
69 69 """
70 70 Renames a group on filesystem
71 71
72 72 :param group_name:
73 73 """
74 74
75 75 if old == new:
76 76 log.debug('skipping group rename')
77 77 return
78 78
79 79 log.debug('renaming repos group from %s to %s', old, new)
80 80
81 81
82 82 old_path = os.path.join(self.repos_path, old)
83 83 new_path = os.path.join(self.repos_path, new)
84 84
85 85 log.debug('renaming repos paths from %s to %s', old_path, new_path)
86 86
87 87 if os.path.isdir(new_path):
88 88 raise Exception('Was trying to rename to already '
89 89 'existing dir %s' % new_path)
90 90 shutil.move(old_path, new_path)
91 91
92 92 def __delete_group(self, group):
93 93 """
94 94 Deletes a group from a filesystem
95 95
96 96 :param group: instance of group from database
97 97 """
98 98 paths = group.full_path.split(RepoGroup.url_sep())
99 99 paths = os.sep.join(paths)
100 100
101 101 rm_path = os.path.join(self.repos_path, paths)
102 102 if os.path.isdir(rm_path):
103 103 # delete only if that path really exists
104 104 os.rmdir(rm_path)
105 105
106 106 def create(self, form_data):
107 107 try:
108 108 new_repos_group = RepoGroup()
109 109 new_repos_group.group_description = form_data['group_description']
110 110 new_repos_group.parent_group = RepoGroup.get(form_data['group_parent_id'])
111 111 new_repos_group.group_name = new_repos_group.get_new_name(form_data['group_name'])
112 112
113 113 self.sa.add(new_repos_group)
114
114 self.sa.flush()
115 115 self.__create_group(new_repos_group.group_name)
116 116
117 self.sa.commit()
118 117 return new_repos_group
119 118 except:
120 119 log.error(traceback.format_exc())
121 self.sa.rollback()
122 120 raise
123 121
124 122 def update(self, repos_group_id, form_data):
125 123
126 124 try:
127 125 repos_group = RepoGroup.get(repos_group_id)
128 126 old_path = repos_group.full_path
129
127
130 128 # change properties
131 129 repos_group.group_description = form_data['group_description']
132 130 repos_group.parent_group = RepoGroup.get(form_data['group_parent_id'])
133 131 repos_group.group_name = repos_group.get_new_name(form_data['group_name'])
134 132
135 133 new_path = repos_group.full_path
136 134
137 135 self.sa.add(repos_group)
138 136
139 137 self.__rename_group(old_path, new_path)
140 138
141 139 # we need to get all repositories from this new group and
142 140 # rename them accordingly to new group path
143 141 for r in repos_group.repositories:
144 142 r.repo_name = r.get_new_name(r.just_name)
145 143 self.sa.add(r)
146 144
147 self.sa.commit()
148 145 return repos_group
149 146 except:
150 147 log.error(traceback.format_exc())
151 self.sa.rollback()
152 148 raise
153 149
154 150 def delete(self, users_group_id):
155 151 try:
156 152 users_group = RepoGroup.get(users_group_id)
157 153 self.sa.delete(users_group)
158 154 self.__delete_group(users_group)
159 self.sa.commit()
160 155 except:
161 156 log.error(traceback.format_exc())
162 self.sa.rollback()
163 157 raise
@@ -1,482 +1,479 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.user
4 4 ~~~~~~~~~~~~~~~~~~~~
5 5
6 6 users model for RhodeCode
7 7
8 8 :created_on: Apr 9, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28
29 29 from pylons import url
30 30 from pylons.i18n.translation import _
31 31
32 32 from rhodecode.lib import safe_unicode
33 33 from rhodecode.lib.caching_query import FromCache
34 34
35 35 from rhodecode.model import BaseModel
36 36 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
37 37 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember, \
38 38 Notification
39 39 from rhodecode.lib.exceptions import DefaultUserException, \
40 40 UserOwnsReposException
41 41
42 42 from sqlalchemy.exc import DatabaseError
43 43 from rhodecode.lib import generate_api_key
44 44 from sqlalchemy.orm import joinedload
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 48
49 49 PERM_WEIGHTS = {'repository.none': 0,
50 50 'repository.read': 1,
51 51 'repository.write': 3,
52 52 'repository.admin': 3}
53 53
54 54
55 55 class UserModel(BaseModel):
56 56
57 57 def get(self, user_id, cache=False):
58 58 user = self.sa.query(User)
59 59 if cache:
60 60 user = user.options(FromCache("sql_cache_short",
61 61 "get_user_%s" % user_id))
62 62 return user.get(user_id)
63 63
64 64 def get_by_username(self, username, cache=False, case_insensitive=False):
65 65
66 66 if case_insensitive:
67 67 user = self.sa.query(User).filter(User.username.ilike(username))
68 68 else:
69 69 user = self.sa.query(User)\
70 70 .filter(User.username == username)
71 71 if cache:
72 72 user = user.options(FromCache("sql_cache_short",
73 73 "get_user_%s" % username))
74 74 return user.scalar()
75 75
76 76 def get_by_api_key(self, api_key, cache=False):
77 77 return User.get_by_api_key(api_key, cache)
78 78
79 79 def create(self, form_data):
80 80 try:
81 81 new_user = User()
82 82 for k, v in form_data.items():
83 83 setattr(new_user, k, v)
84 84
85 85 new_user.api_key = generate_api_key(form_data['username'])
86 86 self.sa.add(new_user)
87 87 self.sa.commit()
88 88 return new_user
89 89 except:
90 90 log.error(traceback.format_exc())
91 91 self.sa.rollback()
92 92 raise
93 93
94 94
95 95 def create_or_update(self, username, password, email, name, lastname,
96 96 active=True, admin=False, ldap_dn=None):
97 97 """
98 98 Creates a new instance if not found, or updates current one
99 99
100 100 :param username:
101 101 :param password:
102 102 :param email:
103 103 :param active:
104 104 :param name:
105 105 :param lastname:
106 106 :param active:
107 107 :param admin:
108 108 :param ldap_dn:
109 109 """
110 110
111 111 from rhodecode.lib.auth import get_crypt_password
112 112
113 113 log.debug('Checking for %s account in RhodeCode database', username)
114 114 user = User.get_by_username(username, case_insensitive=True)
115 115 if user is None:
116 116 log.debug('creating new user %s', username)
117 117 new_user = User()
118 118 else:
119 119 log.debug('updating user %s', username)
120 120 new_user = user
121 121
122 122 try:
123 123 new_user.username = username
124 124 new_user.admin = admin
125 125 new_user.password = get_crypt_password(password)
126 126 new_user.api_key = generate_api_key(username)
127 127 new_user.email = email
128 128 new_user.active = active
129 129 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
130 130 new_user.name = name
131 131 new_user.lastname = lastname
132
133 132 self.sa.add(new_user)
134 self.sa.commit()
135 133 return new_user
136 134 except (DatabaseError,):
137 135 log.error(traceback.format_exc())
138 self.sa.rollback()
139 136 raise
140 137
141 138
142 139 def create_for_container_auth(self, username, attrs):
143 140 """
144 141 Creates the given user if it's not already in the database
145 142
146 143 :param username:
147 144 :param attrs:
148 145 """
149 146 if self.get_by_username(username, case_insensitive=True) is None:
150 147
151 148 # autogenerate email for container account without one
152 149 generate_email = lambda usr: '%s@container_auth.account' % usr
153 150
154 151 try:
155 152 new_user = User()
156 153 new_user.username = username
157 154 new_user.password = None
158 155 new_user.api_key = generate_api_key(username)
159 156 new_user.email = attrs['email']
160 157 new_user.active = attrs.get('active', True)
161 158 new_user.name = attrs['name'] or generate_email(username)
162 159 new_user.lastname = attrs['lastname']
163 160
164 161 self.sa.add(new_user)
165 162 self.sa.commit()
166 163 return new_user
167 164 except (DatabaseError,):
168 165 log.error(traceback.format_exc())
169 166 self.sa.rollback()
170 167 raise
171 168 log.debug('User %s already exists. Skipping creation of account'
172 169 ' for container auth.', username)
173 170 return None
174 171
175 172 def create_ldap(self, username, password, user_dn, attrs):
176 173 """
177 174 Checks if user is in database, if not creates this user marked
178 175 as ldap user
179 176
180 177 :param username:
181 178 :param password:
182 179 :param user_dn:
183 180 :param attrs:
184 181 """
185 182 from rhodecode.lib.auth import get_crypt_password
186 183 log.debug('Checking for such ldap account in RhodeCode database')
187 184 if self.get_by_username(username, case_insensitive=True) is None:
188 185
189 186 # autogenerate email for ldap account without one
190 187 generate_email = lambda usr: '%s@ldap.account' % usr
191 188
192 189 try:
193 190 new_user = User()
194 191 username = username.lower()
195 192 # add ldap account always lowercase
196 193 new_user.username = username
197 194 new_user.password = get_crypt_password(password)
198 195 new_user.api_key = generate_api_key(username)
199 196 new_user.email = attrs['email'] or generate_email(username)
200 197 new_user.active = attrs.get('active', True)
201 198 new_user.ldap_dn = safe_unicode(user_dn)
202 199 new_user.name = attrs['name']
203 200 new_user.lastname = attrs['lastname']
204 201
205 202 self.sa.add(new_user)
206 203 self.sa.commit()
207 204 return new_user
208 205 except (DatabaseError,):
209 206 log.error(traceback.format_exc())
210 207 self.sa.rollback()
211 208 raise
212 209 log.debug('this %s user exists skipping creation of ldap account',
213 210 username)
214 211 return None
215 212
216 213 def create_registration(self, form_data):
217 214 from rhodecode.model.notification import NotificationModel
218 215
219 216 try:
220 217 new_user = User()
221 218 for k, v in form_data.items():
222 219 if k != 'admin':
223 220 setattr(new_user, k, v)
224 221
225 222 self.sa.add(new_user)
226 223 self.sa.flush()
227 224
228 225 # notification to admins
229 226 subject = _('new user registration')
230 227 body = ('New user registration\n'
231 228 '---------------------\n'
232 229 '- Username: %s\n'
233 230 '- Full Name: %s\n'
234 231 '- Email: %s\n')
235 232 body = body % (new_user.username, new_user.full_name,
236 233 new_user.email)
237 234 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
238 235 kw = {'registered_user_url':edit_url}
239 236 NotificationModel().create(created_by=new_user, subject=subject,
240 237 body=body, recipients=None,
241 238 type_=Notification.TYPE_REGISTRATION,
242 239 email_kwargs=kw)
243 240
244 241 except:
245 242 log.error(traceback.format_exc())
246 243 raise
247 244
248 245 def update(self, user_id, form_data):
249 246 try:
250 247 user = self.get(user_id, cache=False)
251 248 if user.username == 'default':
252 249 raise DefaultUserException(
253 250 _("You can't Edit this user since it's"
254 251 " crucial for entire application"))
255 252
256 253 for k, v in form_data.items():
257 254 if k == 'new_password' and v != '':
258 255 user.password = v
259 256 user.api_key = generate_api_key(user.username)
260 257 else:
261 258 setattr(user, k, v)
262 259
263 260 self.sa.add(user)
264 261 self.sa.commit()
265 262 except:
266 263 log.error(traceback.format_exc())
267 264 self.sa.rollback()
268 265 raise
269 266
270 267 def update_my_account(self, user_id, form_data):
271 268 try:
272 269 user = self.get(user_id, cache=False)
273 270 if user.username == 'default':
274 271 raise DefaultUserException(
275 272 _("You can't Edit this user since it's"
276 273 " crucial for entire application"))
277 274 for k, v in form_data.items():
278 275 if k == 'new_password' and v != '':
279 276 user.password = v
280 277 user.api_key = generate_api_key(user.username)
281 278 else:
282 279 if k not in ['admin', 'active']:
283 280 setattr(user, k, v)
284 281
285 282 self.sa.add(user)
286 283 self.sa.commit()
287 284 except:
288 285 log.error(traceback.format_exc())
289 286 self.sa.rollback()
290 287 raise
291 288
292 289 def delete(self, user_id):
293 290 try:
294 291 user = self.get(user_id, cache=False)
295 292 if user.username == 'default':
296 293 raise DefaultUserException(
297 294 _("You can't remove this user since it's"
298 295 " crucial for entire application"))
299 296 if user.repositories:
300 297 raise UserOwnsReposException(_('This user still owns %s '
301 298 'repositories and cannot be '
302 299 'removed. Switch owners or '
303 300 'remove those repositories') \
304 301 % user.repositories)
305 302 self.sa.delete(user)
306 303 self.sa.commit()
307 304 except:
308 305 log.error(traceback.format_exc())
309 306 self.sa.rollback()
310 307 raise
311 308
312 309 def reset_password_link(self, data):
313 310 from rhodecode.lib.celerylib import tasks, run_task
314 311 run_task(tasks.send_password_link, data['email'])
315 312
316 313 def reset_password(self, data):
317 314 from rhodecode.lib.celerylib import tasks, run_task
318 315 run_task(tasks.reset_user_password, data['email'])
319 316
320 317 def fill_data(self, auth_user, user_id=None, api_key=None):
321 318 """
322 319 Fetches auth_user by user_id,or api_key if present.
323 320 Fills auth_user attributes with those taken from database.
324 321 Additionally set's is_authenitated if lookup fails
325 322 present in database
326 323
327 324 :param auth_user: instance of user to set attributes
328 325 :param user_id: user id to fetch by
329 326 :param api_key: api key to fetch by
330 327 """
331 328 if user_id is None and api_key is None:
332 329 raise Exception('You need to pass user_id or api_key')
333 330
334 331 try:
335 332 if api_key:
336 333 dbuser = self.get_by_api_key(api_key)
337 334 else:
338 335 dbuser = self.get(user_id)
339 336
340 337 if dbuser is not None and dbuser.active:
341 338 log.debug('filling %s data', dbuser)
342 339 for k, v in dbuser.get_dict().items():
343 340 setattr(auth_user, k, v)
344 341 else:
345 342 return False
346 343
347 344 except:
348 345 log.error(traceback.format_exc())
349 346 auth_user.is_authenticated = False
350 347 return False
351 348
352 349 return True
353 350
354 351 def fill_perms(self, user):
355 352 """
356 353 Fills user permission attribute with permissions taken from database
357 354 works for permissions given for repositories, and for permissions that
358 355 are granted to groups
359 356
360 357 :param user: user instance to fill his perms
361 358 """
362 359
363 360 user.permissions['repositories'] = {}
364 361 user.permissions['global'] = set()
365 362
366 363 #======================================================================
367 364 # fetch default permissions
368 365 #======================================================================
369 366 default_user = User.get_by_username('default', cache=True)
370 367 default_user_id = default_user.user_id
371 368
372 369 default_perms = Permission.get_default_perms(default_user_id)
373 370
374 371 if user.is_admin:
375 372 #==================================================================
376 373 # #admin have all default rights set to admin
377 374 #==================================================================
378 375 user.permissions['global'].add('hg.admin')
379 376
380 377 for perm in default_perms:
381 378 p = 'repository.admin'
382 379 user.permissions['repositories'][perm.UserRepoToPerm.
383 380 repository.repo_name] = p
384 381
385 382 else:
386 383 #==================================================================
387 384 # set default permissions
388 385 #==================================================================
389 386 uid = user.user_id
390 387
391 388 # default global
392 389 default_global_perms = self.sa.query(UserToPerm)\
393 390 .filter(UserToPerm.user_id == default_user_id)
394 391
395 392 for perm in default_global_perms:
396 393 user.permissions['global'].add(perm.permission.permission_name)
397 394
398 395 # default for repositories
399 396 for perm in default_perms:
400 397 if perm.Repository.private and not (perm.Repository.user_id ==
401 398 uid):
402 399 # disable defaults for private repos,
403 400 p = 'repository.none'
404 401 elif perm.Repository.user_id == uid:
405 402 # set admin if owner
406 403 p = 'repository.admin'
407 404 else:
408 405 p = perm.Permission.permission_name
409 406
410 407 user.permissions['repositories'][perm.UserRepoToPerm.
411 408 repository.repo_name] = p
412 409
413 410 #==================================================================
414 411 # overwrite default with user permissions if any
415 412 #==================================================================
416 413
417 414 # user global
418 415 user_perms = self.sa.query(UserToPerm)\
419 416 .options(joinedload(UserToPerm.permission))\
420 417 .filter(UserToPerm.user_id == uid).all()
421 418
422 419 for perm in user_perms:
423 420 user.permissions['global'].add(perm.permission.permission_name)
424 421
425 422 # user repositories
426 423 user_repo_perms = self.sa.query(UserRepoToPerm, Permission,
427 424 Repository)\
428 425 .join((Repository, UserRepoToPerm.repository_id ==
429 426 Repository.repo_id))\
430 427 .join((Permission, UserRepoToPerm.permission_id ==
431 428 Permission.permission_id))\
432 429 .filter(UserRepoToPerm.user_id == uid).all()
433 430
434 431 for perm in user_repo_perms:
435 432 # set admin if owner
436 433 if perm.Repository.user_id == uid:
437 434 p = 'repository.admin'
438 435 else:
439 436 p = perm.Permission.permission_name
440 437 user.permissions['repositories'][perm.UserRepoToPerm.
441 438 repository.repo_name] = p
442 439
443 440 #==================================================================
444 441 # check if user is part of groups for this repository and fill in
445 442 # (or replace with higher) permissions
446 443 #==================================================================
447 444
448 445 # users group global
449 446 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
450 447 .options(joinedload(UsersGroupToPerm.permission))\
451 448 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
452 449 UsersGroupMember.users_group_id))\
453 450 .filter(UsersGroupMember.user_id == uid).all()
454 451
455 452 for perm in user_perms_from_users_groups:
456 453 user.permissions['global'].add(perm.permission.permission_name)
457 454
458 455 # users group repositories
459 456 user_repo_perms_from_users_groups = self.sa.query(
460 457 UsersGroupRepoToPerm,
461 458 Permission, Repository,)\
462 459 .join((Repository, UsersGroupRepoToPerm.repository_id ==
463 460 Repository.repo_id))\
464 461 .join((Permission, UsersGroupRepoToPerm.permission_id ==
465 462 Permission.permission_id))\
466 463 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id ==
467 464 UsersGroupMember.users_group_id))\
468 465 .filter(UsersGroupMember.user_id == uid).all()
469 466
470 467 for perm in user_repo_perms_from_users_groups:
471 468 p = perm.Permission.permission_name
472 469 cur_perm = user.permissions['repositories'][perm.
473 470 UsersGroupRepoToPerm.
474 471 repository.repo_name]
475 472 # overwrite permission only if it's greater than permission
476 473 # given from other sources
477 474 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
478 475 user.permissions['repositories'][perm.UsersGroupRepoToPerm.
479 476 repository.repo_name] = p
480 477
481 478 return user
482 479
@@ -1,92 +1,75 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.users_group
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 users group model for RhodeCode
7 7
8 8 :created_on: Oct 1, 2011
9 9 :author: nvinot
10 10 :copyright: (C) 2011-2011 Nicolas Vinot <aeris@imirhil.fr>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28
29 29 from rhodecode.model import BaseModel
30 30 from rhodecode.model.db import UsersGroupMember, UsersGroup
31 31
32 32 log = logging.getLogger(__name__)
33 33
34 34
35 35 class UsersGroupModel(BaseModel):
36 36
37 37 def __get_users_group(self, users_group):
38 38 return self._get_instance(UsersGroup, users_group)
39 39
40 40 def get(self, users_group_id, cache=False):
41 41 return UsersGroup.get(users_group_id)
42 42
43 43 def get_by_name(self, name, cache=False, case_insensitive=False):
44 44 return UsersGroup.get_by_group_name(name, cache, case_insensitive)
45 45
46 def create(self, form_data):
47 try:
48 new_users_group = UsersGroup()
49 for k, v in form_data.items():
50 setattr(new_users_group, k, v)
51
52 self.sa.add(new_users_group)
53 self.sa.commit()
54 return new_users_group
55 except:
56 log.error(traceback.format_exc())
57 self.sa.rollback()
58 raise
59
60
61 def create_(self, name, active=True):
46 def create(self, name, active=True):
62 47 new = UsersGroup()
63 48 new.users_group_name = name
64 49 new.users_group_active = active
65 50 self.sa.add(new)
66 51 return new
67 52
68 53 def delete(self, users_group):
69 54 obj = self.__get_users_group(users_group)
70 55 self.sa.delete(obj)
71 56
72 57 def add_user_to_group(self, users_group, user):
73 58 for m in users_group.members:
74 59 u = m.user
75 60 if u.user_id == user.user_id:
76 61 return m
77 62
78 63 try:
79 64 users_group_member = UsersGroupMember()
80 65 users_group_member.user = user
81 66 users_group_member.users_group = users_group
82 67
83 68 users_group.members.append(users_group_member)
84 69 user.group_member.append(users_group_member)
85 70
86 71 self.sa.add(users_group_member)
87 self.sa.commit()
88 72 return users_group_member
89 73 except:
90 74 log.error(traceback.format_exc())
91 self.sa.rollback()
92 75 raise
@@ -1,206 +1,206 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.tests.test_hg_operations
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Test suite for making push/pull operations
7 7
8 8 :created_on: Dec 30, 2010
9 9 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 10 :license: GPLv3, see COPYING for more details.
11 11 """
12 12 # This program is free software: you can redistribute it and/or modify
13 13 # it under the terms of the GNU General Public License as published by
14 14 # the Free Software Foundation, either version 3 of the License, or
15 15 # (at your option) any later version.
16 16 #
17 17 # This program is distributed in the hope that it will be useful,
18 18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 20 # GNU General Public License for more details.
21 21 #
22 22 # You should have received a copy of the GNU General Public License
23 23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 24
25 25 import os
26 26 import sys
27 27 import shutil
28 28 import logging
29 29 from os.path import join as jn
30 30 from os.path import dirname as dn
31 31
32 32 from tempfile import _RandomNameSequence
33 33 from subprocess import Popen, PIPE
34 34
35 35 from paste.deploy import appconfig
36 36 from pylons import config
37 37 from sqlalchemy import engine_from_config
38 38
39 39 from rhodecode.lib.utils import add_cache
40 40 from rhodecode.model import init_model
41 41 from rhodecode.model import meta
42 42 from rhodecode.model.db import User, Repository
43 43 from rhodecode.lib.auth import get_crypt_password
44 44
45 45 from rhodecode.tests import TESTS_TMP_PATH, NEW_HG_REPO, HG_REPO
46 46 from rhodecode.config.environment import load_environment
47 47
48 48 rel_path = dn(dn(dn(os.path.abspath(__file__))))
49 49 conf = appconfig('config:development.ini', relative_to=rel_path)
50 50 load_environment(conf.global_conf, conf.local_conf)
51 51
52 52 add_cache(conf)
53 53
54 54 USER = 'test_admin'
55 55 PASS = 'test12'
56 HOST = '127.0.0.1:5000'
56 HOST = 'hg.local'
57 57 METHOD = 'pull'
58 58 DEBUG = True
59 59 log = logging.getLogger(__name__)
60 60
61 61
62 62 class Command(object):
63 63
64 64 def __init__(self, cwd):
65 65 self.cwd = cwd
66 66
67 67 def execute(self, cmd, *args):
68 68 """Runs command on the system with given ``args``.
69 69 """
70 70
71 71 command = cmd + ' ' + ' '.join(args)
72 72 log.debug('Executing %s' % command)
73 73 if DEBUG:
74 74 print command
75 75 p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd)
76 76 stdout, stderr = p.communicate()
77 77 if DEBUG:
78 78 print stdout, stderr
79 79 return stdout, stderr
80 80
81 81 def get_session():
82 82 engine = engine_from_config(conf, 'sqlalchemy.db1.')
83 83 init_model(engine)
84 84 sa = meta.Session()
85 85 return sa
86 86
87 87
88 88 def create_test_user(force=True):
89 89 print 'creating test user'
90 90 sa = get_session()
91 91
92 92 user = sa.query(User).filter(User.username == USER).scalar()
93 93
94 94 if force and user is not None:
95 95 print 'removing current user'
96 96 for repo in sa.query(Repository).filter(Repository.user == user).all():
97 97 sa.delete(repo)
98 98 sa.delete(user)
99 99 sa.commit()
100 100
101 101 if user is None or force:
102 102 print 'creating new one'
103 103 new_usr = User()
104 104 new_usr.username = USER
105 105 new_usr.password = get_crypt_password(PASS)
106 106 new_usr.email = 'mail@mail.com'
107 107 new_usr.name = 'test'
108 108 new_usr.lastname = 'lasttestname'
109 109 new_usr.active = True
110 110 new_usr.admin = True
111 111 sa.add(new_usr)
112 112 sa.commit()
113 113
114 114 print 'done'
115 115
116 116
117 117 def create_test_repo(force=True):
118 118 print 'creating test repo'
119 119 from rhodecode.model.repo import RepoModel
120 120 sa = get_session()
121 121
122 122 user = sa.query(User).filter(User.username == USER).scalar()
123 123 if user is None:
124 124 raise Exception('user not found')
125 125
126 126
127 127 repo = sa.query(Repository).filter(Repository.repo_name == HG_REPO).scalar()
128 128
129 129 if repo is None:
130 130 print 'repo not found creating'
131 131
132 132 form_data = {'repo_name':HG_REPO,
133 133 'repo_type':'hg',
134 134 'private':False,
135 135 'clone_uri':'' }
136 136 rm = RepoModel(sa)
137 137 rm.base_path = '/home/hg'
138 138 rm.create(form_data, user)
139 139
140 140 print 'done'
141 141
142 142 def set_anonymous_access(enable=True):
143 143 sa = get_session()
144 144 user = sa.query(User).filter(User.username == 'default').one()
145 145 user.active = enable
146 146 sa.add(user)
147 147 sa.commit()
148 148
149 149 def get_anonymous_access():
150 150 sa = get_session()
151 151 return sa.query(User).filter(User.username == 'default').one().active
152 152
153 153
154 154 #==============================================================================
155 155 # TESTS
156 156 #==============================================================================
157 157 def test_clone_with_credentials(no_errors=False, repo=HG_REPO, method=METHOD,
158 158 seq=None):
159 159 cwd = path = jn(TESTS_TMP_PATH, repo)
160 160
161 161 if seq == None:
162 162 seq = _RandomNameSequence().next()
163 163
164 164 try:
165 165 shutil.rmtree(path, ignore_errors=True)
166 166 os.makedirs(path)
167 167 #print 'made dirs %s' % jn(path)
168 168 except OSError:
169 169 raise
170 170
171 171
172 172 clone_url = 'http://%(user)s:%(pass)s@%(host)s/%(cloned_repo)s' % \
173 173 {'user':USER,
174 174 'pass':PASS,
175 175 'host':HOST,
176 176 'cloned_repo':repo, }
177 177
178 178 dest = path + seq
179 179 if method == 'pull':
180 180 stdout, stderr = Command(cwd).execute('hg', method, '--cwd', dest, clone_url)
181 181 else:
182 182 stdout, stderr = Command(cwd).execute('hg', method, clone_url, dest)
183 183
184 184 if no_errors is False:
185 185 assert """adding file changes""" in stdout, 'no messages about cloning'
186 186 assert """abort""" not in stderr , 'got error from clone'
187 187
188 188 if __name__ == '__main__':
189 189 try:
190 190 create_test_user(force=False)
191 191 seq = None
192 192 import time
193 193
194 194 if METHOD == 'pull':
195 195 seq = _RandomNameSequence().next()
196 196 test_clone_with_credentials(repo=sys.argv[1], method='clone',
197 197 seq=seq)
198 198 s = time.time()
199 199 for i in range(int(sys.argv[2])):
200 200 print 'take', i
201 201 test_clone_with_credentials(repo=sys.argv[1], method=METHOD,
202 202 seq=seq)
203 203 print 'time taken %.3f' % (time.time() - s)
204 204 except Exception, e:
205 205 raise
206 206 sys.exit('stop on %s' % e)
@@ -1,348 +1,362 b''
1 1 import os
2 2 import unittest
3 3 from rhodecode.tests import *
4 4
5 5 from rhodecode.model.repos_group import ReposGroupModel
6 6 from rhodecode.model.repo import RepoModel
7 7 from rhodecode.model.db import RepoGroup, User, Notification, UserNotification, \
8 8 UsersGroup, UsersGroupMember
9 9 from sqlalchemy.exc import IntegrityError
10 10 from rhodecode.model.user import UserModel
11 11
12 12 from rhodecode.model.meta import Session
13 13 from rhodecode.model.notification import NotificationModel
14 14 from rhodecode.model.users_group import UsersGroupModel
15 15
16 16 class TestReposGroups(unittest.TestCase):
17 17
18 18 def setUp(self):
19 19 self.g1 = self.__make_group('test1', skip_if_exists=True)
20 20 self.g2 = self.__make_group('test2', skip_if_exists=True)
21 21 self.g3 = self.__make_group('test3', skip_if_exists=True)
22 22
23 23 def tearDown(self):
24 24 print 'out'
25 25
26 26 def __check_path(self, *path):
27 27 path = [TESTS_TMP_PATH] + list(path)
28 28 path = os.path.join(*path)
29 29 return os.path.isdir(path)
30 30
31 31 def _check_folders(self):
32 32 print os.listdir(TESTS_TMP_PATH)
33 33
34 34 def __make_group(self, path, desc='desc', parent_id=None,
35 35 skip_if_exists=False):
36 36
37 37 gr = RepoGroup.get_by_group_name(path)
38 38 if gr and skip_if_exists:
39 39 return gr
40 40
41 41 form_data = dict(group_name=path,
42 42 group_description=desc,
43 43 group_parent_id=parent_id)
44 44 gr = ReposGroupModel().create(form_data)
45 Session.commit()
45 46 return gr
46 47
47 48 def __delete_group(self, id_):
48 49 ReposGroupModel().delete(id_)
49 50
50 51
51 52 def __update_group(self, id_, path, desc='desc', parent_id=None):
52 53 form_data = dict(group_name=path,
53 54 group_description=desc,
54 55 group_parent_id=parent_id)
55 56
56 57 gr = ReposGroupModel().update(id_, form_data)
57 58 return gr
58 59
59 60 def test_create_group(self):
60 61 g = self.__make_group('newGroup')
61 62 self.assertEqual(g.full_path, 'newGroup')
62 63
63 64 self.assertTrue(self.__check_path('newGroup'))
64 65
65 66
66 67 def test_create_same_name_group(self):
67 68 self.assertRaises(IntegrityError, lambda:self.__make_group('newGroup'))
68
69 Session().rollback()
69 70
70 71 def test_same_subgroup(self):
71 72 sg1 = self.__make_group('sub1', parent_id=self.g1.group_id)
72 73 self.assertEqual(sg1.parent_group, self.g1)
73 74 self.assertEqual(sg1.full_path, 'test1/sub1')
74 75 self.assertTrue(self.__check_path('test1', 'sub1'))
75 76
76 77 ssg1 = self.__make_group('subsub1', parent_id=sg1.group_id)
77 78 self.assertEqual(ssg1.parent_group, sg1)
78 79 self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
79 80 self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
80 81
81 82
82 83 def test_remove_group(self):
83 84 sg1 = self.__make_group('deleteme')
84 85 self.__delete_group(sg1.group_id)
85 86
86 87 self.assertEqual(RepoGroup.get(sg1.group_id), None)
87 88 self.assertFalse(self.__check_path('deteteme'))
88 89
89 90 sg1 = self.__make_group('deleteme', parent_id=self.g1.group_id)
90 91 self.__delete_group(sg1.group_id)
91 92
92 93 self.assertEqual(RepoGroup.get(sg1.group_id), None)
93 94 self.assertFalse(self.__check_path('test1', 'deteteme'))
94 95
95 96
96 97 def test_rename_single_group(self):
97 98 sg1 = self.__make_group('initial')
98 99
99 100 new_sg1 = self.__update_group(sg1.group_id, 'after')
100 101 self.assertTrue(self.__check_path('after'))
101 102 self.assertEqual(RepoGroup.get_by_group_name('initial'), None)
102 103
103 104
104 105 def test_update_group_parent(self):
105 106
106 107 sg1 = self.__make_group('initial', parent_id=self.g1.group_id)
107 108
108 109 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
109 110 self.assertTrue(self.__check_path('test1', 'after'))
110 111 self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None)
111 112
112 113
113 114 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
114 115 self.assertTrue(self.__check_path('test3', 'after'))
115 116 self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None)
116 117
117 118
118 119 new_sg1 = self.__update_group(sg1.group_id, 'hello')
119 120 self.assertTrue(self.__check_path('hello'))
120 121
121 122 self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1)
122 123
123 124
124 125
125 126 def test_subgrouping_with_repo(self):
126 127
127 128 g1 = self.__make_group('g1')
128 129 g2 = self.__make_group('g2')
129 130
130 131 # create new repo
131 132 form_data = dict(repo_name='john',
132 133 repo_name_full='john',
133 134 fork_name=None,
134 135 description=None,
135 136 repo_group=None,
136 137 private=False,
137 138 repo_type='hg',
138 139 clone_uri=None)
139 140 cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
140 141 r = RepoModel().create(form_data, cur_user)
141 142
142 143 self.assertEqual(r.repo_name, 'john')
143 144
144 145 # put repo into group
145 146 form_data = form_data
146 147 form_data['repo_group'] = g1.group_id
147 148 form_data['perms_new'] = []
148 149 form_data['perms_updates'] = []
149 150 RepoModel().update(r.repo_name, form_data)
150 151 self.assertEqual(r.repo_name, 'g1/john')
151 152
152 153
153 154 self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id)
154 155 self.assertTrue(self.__check_path('g2', 'g1'))
155 156
156 157 # test repo
157 158 self.assertEqual(r.repo_name, os.path.join('g2', 'g1', r.just_name))
158 159
159 160 class TestUser(unittest.TestCase):
160 161
161 162 def test_create_and_remove(self):
162 163 usr = UserModel().create_or_update(username=u'test_user', password=u'qweqwe',
163 164 email=u'u232@rhodecode.org',
164 165 name=u'u1', lastname=u'u1')
166 Session().commit()
165 167 self.assertEqual(User.get_by_username(u'test_user'), usr)
166 168
167 169 # make users group
168 users_group = UsersGroupModel().create_('some_example_group')
170 users_group = UsersGroupModel().create('some_example_group')
169 171 Session().commit()
172
170 173 UsersGroupModel().add_user_to_group(users_group, usr)
174 Session().commit()
171 175
172 176 self.assertEqual(UsersGroup.get(users_group.users_group_id), users_group)
173 177 self.assertEqual(UsersGroupMember.query().count(), 1)
174 178 UserModel().delete(usr.user_id)
179 Session().commit()
175 180
176 181 self.assertEqual(UsersGroupMember.query().all(), [])
177 182
178 183
179 184 class TestNotifications(unittest.TestCase):
180 185
181 186 def __init__(self, methodName='runTest'):
182 187 self.u1 = UserModel().create_or_update(username=u'u1',
183 188 password=u'qweqwe',
184 189 email=u'u1@rhodecode.org',
185 name=u'u1', lastname=u'u1').user_id
190 name=u'u1', lastname=u'u1')
191 Session.commit()
192 self.u1 = self.u1.user_id
193
186 194 self.u2 = UserModel().create_or_update(username=u'u2',
187 195 password=u'qweqwe',
188 196 email=u'u2@rhodecode.org',
189 name=u'u2', lastname=u'u3').user_id
197 name=u'u2', lastname=u'u3')
198 Session.commit()
199 self.u2 = self.u2.user_id
200
190 201 self.u3 = UserModel().create_or_update(username=u'u3',
191 202 password=u'qweqwe',
192 203 email=u'u3@rhodecode.org',
193 name=u'u3', lastname=u'u3').user_id
204 name=u'u3', lastname=u'u3')
205 Session.commit()
206 self.u3 = self.u3.user_id
207
194 208 super(TestNotifications, self).__init__(methodName=methodName)
195 209
196 210 def _clean_notifications(self):
197 211 for n in Notification.query().all():
198 212 Session().delete(n)
199 213
200 214 Session().commit()
201 215 self.assertEqual(Notification.query().all(), [])
202 216
203 217
204 218 def test_create_notification(self):
205 219 self.assertEqual([], Notification.query().all())
206 220 self.assertEqual([], UserNotification.query().all())
207 221
208 222 usrs = [self.u1, self.u2]
209 223 notification = NotificationModel().create(created_by=self.u1,
210 224 subject=u'subj', body=u'hi there',
211 225 recipients=usrs)
212 226 Session().commit()
213 227 u1 = User.get(self.u1)
214 228 u2 = User.get(self.u2)
215 229 u3 = User.get(self.u3)
216 230 notifications = Notification.query().all()
217 231 self.assertEqual(len(notifications), 1)
218 232
219 233 unotification = UserNotification.query()\
220 234 .filter(UserNotification.notification == notification).all()
221 235
222 236 self.assertEqual(notifications[0].recipients, [u1, u2])
223 237 self.assertEqual(notification.notification_id,
224 238 notifications[0].notification_id)
225 239 self.assertEqual(len(unotification), len(usrs))
226 240 self.assertEqual([x.user.user_id for x in unotification], usrs)
227 241
228 242 self._clean_notifications()
229 243
230 244 def test_user_notifications(self):
231 245 self.assertEqual([], Notification.query().all())
232 246 self.assertEqual([], UserNotification.query().all())
233 247
234 248 notification1 = NotificationModel().create(created_by=self.u1,
235 249 subject=u'subj', body=u'hi there1',
236 250 recipients=[self.u3])
237 251 Session().commit()
238 252 notification2 = NotificationModel().create(created_by=self.u1,
239 253 subject=u'subj', body=u'hi there2',
240 254 recipients=[self.u3])
241 255 Session().commit()
242 256 u3 = Session().query(User).get(self.u3)
243 257
244 258 self.assertEqual(sorted([x.notification for x in u3.notifications]),
245 259 sorted([notification2, notification1]))
246 260 self._clean_notifications()
247 261
248 262 def test_delete_notifications(self):
249 263 self.assertEqual([], Notification.query().all())
250 264 self.assertEqual([], UserNotification.query().all())
251 265
252 266 notification = NotificationModel().create(created_by=self.u1,
253 267 subject=u'title', body=u'hi there3',
254 268 recipients=[self.u3, self.u1, self.u2])
255 269 Session().commit()
256 270 notifications = Notification.query().all()
257 271 self.assertTrue(notification in notifications)
258 272
259 273 Notification.delete(notification.notification_id)
260 274 Session().commit()
261 275
262 276 notifications = Notification.query().all()
263 277 self.assertFalse(notification in notifications)
264 278
265 279 un = UserNotification.query().filter(UserNotification.notification
266 280 == notification).all()
267 281 self.assertEqual(un, [])
268 282
269 283 self._clean_notifications()
270 284
271 285 def test_delete_association(self):
272 286
273 287 self.assertEqual([], Notification.query().all())
274 288 self.assertEqual([], UserNotification.query().all())
275 289
276 290 notification = NotificationModel().create(created_by=self.u1,
277 291 subject=u'title', body=u'hi there3',
278 292 recipients=[self.u3, self.u1, self.u2])
279 293 Session().commit()
280 294
281 295 unotification = UserNotification.query()\
282 296 .filter(UserNotification.notification ==
283 297 notification)\
284 298 .filter(UserNotification.user_id == self.u3)\
285 299 .scalar()
286 300
287 301 self.assertEqual(unotification.user_id, self.u3)
288 302
289 303 NotificationModel().delete(self.u3,
290 304 notification.notification_id)
291 305 Session().commit()
292 306
293 307 u3notification = UserNotification.query()\
294 308 .filter(UserNotification.notification ==
295 309 notification)\
296 310 .filter(UserNotification.user_id == self.u3)\
297 311 .scalar()
298 312
299 313 self.assertEqual(u3notification, None)
300 314
301 315 # notification object is still there
302 316 self.assertEqual(Notification.query().all(), [notification])
303 317
304 318 #u1 and u2 still have assignments
305 319 u1notification = UserNotification.query()\
306 320 .filter(UserNotification.notification ==
307 321 notification)\
308 322 .filter(UserNotification.user_id == self.u1)\
309 323 .scalar()
310 324 self.assertNotEqual(u1notification, None)
311 325 u2notification = UserNotification.query()\
312 326 .filter(UserNotification.notification ==
313 327 notification)\
314 328 .filter(UserNotification.user_id == self.u2)\
315 329 .scalar()
316 330 self.assertNotEqual(u2notification, None)
317 331
318 332 self._clean_notifications()
319 333
320 334 def test_notification_counter(self):
321 335 self._clean_notifications()
322 336 self.assertEqual([], Notification.query().all())
323 337 self.assertEqual([], UserNotification.query().all())
324 338
325 339 NotificationModel().create(created_by=self.u1,
326 340 subject=u'title', body=u'hi there_delete',
327 341 recipients=[self.u3, self.u1])
328 342 Session().commit()
329 343
330 344 self.assertEqual(NotificationModel()
331 345 .get_unread_cnt_for_user(self.u1), 1)
332 346 self.assertEqual(NotificationModel()
333 347 .get_unread_cnt_for_user(self.u2), 0)
334 348 self.assertEqual(NotificationModel()
335 349 .get_unread_cnt_for_user(self.u3), 1)
336 350
337 351 notification = NotificationModel().create(created_by=self.u1,
338 352 subject=u'title', body=u'hi there3',
339 353 recipients=[self.u3, self.u1, self.u2])
340 354 Session().commit()
341 355
342 356 self.assertEqual(NotificationModel()
343 357 .get_unread_cnt_for_user(self.u1), 2)
344 358 self.assertEqual(NotificationModel()
345 359 .get_unread_cnt_for_user(self.u2), 1)
346 360 self.assertEqual(NotificationModel()
347 361 .get_unread_cnt_for_user(self.u3), 2)
348 362 self._clean_notifications()
@@ -1,49 +1,50 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.websetup
4 4 ~~~~~~~~~~~~~~~~~~
5 5
6 6 Weboperations and setup for rhodecode
7 7
8 8 :created_on: Dec 11, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import os
27 27 import logging
28 28
29 29 from rhodecode.config.environment import load_environment
30 30 from rhodecode.lib.db_manage import DbManage
31 from rhodecode.model.meta import Session
31 32
32 33
33 34 log = logging.getLogger(__name__)
34 35
35 36
36 37 def setup_app(command, conf, vars):
37 38 """Place any commands to setup rhodecode here"""
38 39 dbconf = conf['sqlalchemy.db1.url']
39 40 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=conf['here'],
40 41 tests=False)
41 42 dbmanage.create_tables(override=True)
42 43 dbmanage.set_db_version()
43 44 dbmanage.create_settings(dbmanage.config_prompt(None))
44 45 dbmanage.create_default_user()
45 46 dbmanage.admin_prompt()
46 47 dbmanage.create_permissions()
47 48 dbmanage.populate_default_permissions()
48
49 Session().commit()
49 50 load_environment(conf.global_conf, conf.local_conf, initial=True)
General Comments 0
You need to be logged in to leave comments. Login now