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