##// END OF EJS Templates
users: moved get_users from RepoModel to UserModel.
marcink -
r1677:f2c95b1d default
parent child Browse files
Show More
@@ -1,236 +1,236 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import re
21 import re
22 import logging
22 import logging
23
23
24 from pyramid.view import view_config
24 from pyramid.view import view_config
25
25
26 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib.auth import LoginRequired, NotAnonymous
28 from rhodecode.lib.auth import LoginRequired, NotAnonymous
29 from rhodecode.lib.index import searcher_from_config
29 from rhodecode.lib.index import searcher_from_config
30 from rhodecode.lib.utils2 import safe_unicode, str2bool
30 from rhodecode.lib.utils2 import safe_unicode, str2bool
31 from rhodecode.model.db import func, Repository, RepoGroup
31 from rhodecode.model.db import func, Repository, RepoGroup
32 from rhodecode.model.repo import RepoModel
32 from rhodecode.model.repo import RepoModel
33 from rhodecode.model.scm import ScmModel
33 from rhodecode.model.scm import ScmModel
34 from rhodecode.model.user import UserModel
34 from rhodecode.model.user_group import UserGroupModel
35 from rhodecode.model.user_group import UserGroupModel
35
36
36 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
37
38
38
39
39 class HomeView(BaseAppView):
40 class HomeView(BaseAppView):
40
41
41 def load_default_context(self):
42 def load_default_context(self):
42 c = self._get_local_tmpl_context()
43 c = self._get_local_tmpl_context()
43 c.user = c.auth_user.get_instance()
44 c.user = c.auth_user.get_instance()
44 self._register_global_c(c)
45 self._register_global_c(c)
45 return c
46 return c
46
47
47 @LoginRequired()
48 @LoginRequired()
48 @view_config(
49 @view_config(
49 route_name='user_autocomplete_data', request_method='GET',
50 route_name='user_autocomplete_data', request_method='GET',
50 renderer='json_ext', xhr=True)
51 renderer='json_ext', xhr=True)
51 def user_autocomplete_data(self):
52 def user_autocomplete_data(self):
52 query = self.request.GET.get('query')
53 query = self.request.GET.get('query')
53 active = str2bool(self.request.GET.get('active') or True)
54 active = str2bool(self.request.GET.get('active') or True)
54 include_groups = str2bool(self.request.GET.get('user_groups'))
55 include_groups = str2bool(self.request.GET.get('user_groups'))
55
56
56 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
57 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
57 query, active, include_groups)
58 query, active, include_groups)
58
59
59 repo_model = RepoModel()
60 _users = UserModel().get_users(
60 _users = repo_model.get_users(
61 name_contains=query, only_active=active)
61 name_contains=query, only_active=active)
62
62
63 if include_groups:
63 if include_groups:
64 # extend with user groups
64 # extend with user groups
65 _user_groups = UserGroupModel().get_user_groups(
65 _user_groups = UserGroupModel().get_user_groups(
66 name_contains=query, only_active=active)
66 name_contains=query, only_active=active)
67 _users = _users + _user_groups
67 _users = _users + _user_groups
68
68
69 return {'suggestions': _users}
69 return {'suggestions': _users}
70
70
71 @LoginRequired()
71 @LoginRequired()
72 @NotAnonymous()
72 @NotAnonymous()
73 @view_config(
73 @view_config(
74 route_name='user_group_autocomplete_data', request_method='GET',
74 route_name='user_group_autocomplete_data', request_method='GET',
75 renderer='json_ext', xhr=True)
75 renderer='json_ext', xhr=True)
76 def user_group_autocomplete_data(self):
76 def user_group_autocomplete_data(self):
77 query = self.request.GET.get('query')
77 query = self.request.GET.get('query')
78 active = str2bool(self.request.GET.get('active') or True)
78 active = str2bool(self.request.GET.get('active') or True)
79 log.debug('generating user group list, query:%s, active:%s',
79 log.debug('generating user group list, query:%s, active:%s',
80 query, active)
80 query, active)
81
81
82 _user_groups = UserGroupModel().get_user_groups(
82 _user_groups = UserGroupModel().get_user_groups(
83 name_contains=query, only_active=active)
83 name_contains=query, only_active=active)
84 _user_groups = _user_groups
84 _user_groups = _user_groups
85
85
86 return {'suggestions': _user_groups}
86 return {'suggestions': _user_groups}
87
87
88 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
88 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
89 query = Repository.query()\
89 query = Repository.query()\
90 .order_by(func.length(Repository.repo_name))\
90 .order_by(func.length(Repository.repo_name))\
91 .order_by(Repository.repo_name)
91 .order_by(Repository.repo_name)
92
92
93 if repo_type:
93 if repo_type:
94 query = query.filter(Repository.repo_type == repo_type)
94 query = query.filter(Repository.repo_type == repo_type)
95
95
96 if name_contains:
96 if name_contains:
97 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
97 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
98 query = query.filter(
98 query = query.filter(
99 Repository.repo_name.ilike(ilike_expression))
99 Repository.repo_name.ilike(ilike_expression))
100 query = query.limit(limit)
100 query = query.limit(limit)
101
101
102 all_repos = query.all()
102 all_repos = query.all()
103 # permission checks are inside this function
103 # permission checks are inside this function
104 repo_iter = ScmModel().get_repos(all_repos)
104 repo_iter = ScmModel().get_repos(all_repos)
105 return [
105 return [
106 {
106 {
107 'id': obj['name'],
107 'id': obj['name'],
108 'text': obj['name'],
108 'text': obj['name'],
109 'type': 'repo',
109 'type': 'repo',
110 'obj': obj['dbrepo'],
110 'obj': obj['dbrepo'],
111 'url': h.url('summary_home', repo_name=obj['name'])
111 'url': h.url('summary_home', repo_name=obj['name'])
112 }
112 }
113 for obj in repo_iter]
113 for obj in repo_iter]
114
114
115 def _get_repo_group_list(self, name_contains=None, limit=20):
115 def _get_repo_group_list(self, name_contains=None, limit=20):
116 query = RepoGroup.query()\
116 query = RepoGroup.query()\
117 .order_by(func.length(RepoGroup.group_name))\
117 .order_by(func.length(RepoGroup.group_name))\
118 .order_by(RepoGroup.group_name)
118 .order_by(RepoGroup.group_name)
119
119
120 if name_contains:
120 if name_contains:
121 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
121 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
122 query = query.filter(
122 query = query.filter(
123 RepoGroup.group_name.ilike(ilike_expression))
123 RepoGroup.group_name.ilike(ilike_expression))
124 query = query.limit(limit)
124 query = query.limit(limit)
125
125
126 all_groups = query.all()
126 all_groups = query.all()
127 repo_groups_iter = ScmModel().get_repo_groups(all_groups)
127 repo_groups_iter = ScmModel().get_repo_groups(all_groups)
128 return [
128 return [
129 {
129 {
130 'id': obj.group_name,
130 'id': obj.group_name,
131 'text': obj.group_name,
131 'text': obj.group_name,
132 'type': 'group',
132 'type': 'group',
133 'obj': {},
133 'obj': {},
134 'url': h.url('repo_group_home', group_name=obj.group_name)
134 'url': h.url('repo_group_home', group_name=obj.group_name)
135 }
135 }
136 for obj in repo_groups_iter]
136 for obj in repo_groups_iter]
137
137
138 def _get_hash_commit_list(self, auth_user, hash_starts_with=None):
138 def _get_hash_commit_list(self, auth_user, hash_starts_with=None):
139 if not hash_starts_with or len(hash_starts_with) < 3:
139 if not hash_starts_with or len(hash_starts_with) < 3:
140 return []
140 return []
141
141
142 commit_hashes = re.compile('([0-9a-f]{2,40})').findall(hash_starts_with)
142 commit_hashes = re.compile('([0-9a-f]{2,40})').findall(hash_starts_with)
143
143
144 if len(commit_hashes) != 1:
144 if len(commit_hashes) != 1:
145 return []
145 return []
146
146
147 commit_hash_prefix = commit_hashes[0]
147 commit_hash_prefix = commit_hashes[0]
148
148
149 searcher = searcher_from_config(self.request.registry.settings)
149 searcher = searcher_from_config(self.request.registry.settings)
150 result = searcher.search(
150 result = searcher.search(
151 'commit_id:%s*' % commit_hash_prefix, 'commit', auth_user,
151 'commit_id:%s*' % commit_hash_prefix, 'commit', auth_user,
152 raise_on_exc=False)
152 raise_on_exc=False)
153
153
154 return [
154 return [
155 {
155 {
156 'id': entry['commit_id'],
156 'id': entry['commit_id'],
157 'text': entry['commit_id'],
157 'text': entry['commit_id'],
158 'type': 'commit',
158 'type': 'commit',
159 'obj': {'repo': entry['repository']},
159 'obj': {'repo': entry['repository']},
160 'url': h.url('changeset_home',
160 'url': h.url('changeset_home',
161 repo_name=entry['repository'],
161 repo_name=entry['repository'],
162 revision=entry['commit_id'])
162 revision=entry['commit_id'])
163 }
163 }
164 for entry in result['results']]
164 for entry in result['results']]
165
165
166 @LoginRequired()
166 @LoginRequired()
167 @view_config(
167 @view_config(
168 route_name='repo_list_data', request_method='GET',
168 route_name='repo_list_data', request_method='GET',
169 renderer='json_ext', xhr=True)
169 renderer='json_ext', xhr=True)
170 def repo_list_data(self):
170 def repo_list_data(self):
171 _ = self.request.translate
171 _ = self.request.translate
172
172
173 query = self.request.GET.get('query')
173 query = self.request.GET.get('query')
174 repo_type = self.request.GET.get('repo_type')
174 repo_type = self.request.GET.get('repo_type')
175 log.debug('generating repo list, query:%s, repo_type:%s',
175 log.debug('generating repo list, query:%s, repo_type:%s',
176 query, repo_type)
176 query, repo_type)
177
177
178 res = []
178 res = []
179 repos = self._get_repo_list(query, repo_type=repo_type)
179 repos = self._get_repo_list(query, repo_type=repo_type)
180 if repos:
180 if repos:
181 res.append({
181 res.append({
182 'text': _('Repositories'),
182 'text': _('Repositories'),
183 'children': repos
183 'children': repos
184 })
184 })
185
185
186 data = {
186 data = {
187 'more': False,
187 'more': False,
188 'results': res
188 'results': res
189 }
189 }
190 return data
190 return data
191
191
192 @LoginRequired()
192 @LoginRequired()
193 @view_config(
193 @view_config(
194 route_name='goto_switcher_data', request_method='GET',
194 route_name='goto_switcher_data', request_method='GET',
195 renderer='json_ext', xhr=True)
195 renderer='json_ext', xhr=True)
196 def goto_switcher_data(self):
196 def goto_switcher_data(self):
197 c = self.load_default_context()
197 c = self.load_default_context()
198
198
199 _ = self.request.translate
199 _ = self.request.translate
200
200
201 query = self.request.GET.get('query')
201 query = self.request.GET.get('query')
202 log.debug('generating goto switcher list, query %s', query)
202 log.debug('generating goto switcher list, query %s', query)
203
203
204 res = []
204 res = []
205 repo_groups = self._get_repo_group_list(query)
205 repo_groups = self._get_repo_group_list(query)
206 if repo_groups:
206 if repo_groups:
207 res.append({
207 res.append({
208 'text': _('Groups'),
208 'text': _('Groups'),
209 'children': repo_groups
209 'children': repo_groups
210 })
210 })
211
211
212 repos = self._get_repo_list(query)
212 repos = self._get_repo_list(query)
213 if repos:
213 if repos:
214 res.append({
214 res.append({
215 'text': _('Repositories'),
215 'text': _('Repositories'),
216 'children': repos
216 'children': repos
217 })
217 })
218
218
219 commits = self._get_hash_commit_list(c.auth_user, query)
219 commits = self._get_hash_commit_list(c.auth_user, query)
220 if commits:
220 if commits:
221 unique_repos = {}
221 unique_repos = {}
222 for commit in commits:
222 for commit in commits:
223 unique_repos.setdefault(commit['obj']['repo'], []
223 unique_repos.setdefault(commit['obj']['repo'], []
224 ).append(commit)
224 ).append(commit)
225
225
226 for repo in unique_repos:
226 for repo in unique_repos:
227 res.append({
227 res.append({
228 'text': _('Commits in %(repo)s') % {'repo': repo},
228 'text': _('Commits in %(repo)s') % {'repo': repo},
229 'children': unique_repos[repo]
229 'children': unique_repos[repo]
230 })
230 })
231
231
232 data = {
232 data = {
233 'more': False,
233 'more': False,
234 'results': res
234 'results': res
235 }
235 }
236 return data
236 return data
@@ -1,1036 +1,999 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Repository model for rhodecode
22 Repository model for rhodecode
23 """
23 """
24
24
25 import logging
25 import logging
26 import os
26 import os
27 import re
27 import re
28 import shutil
28 import shutil
29 import time
29 import time
30 import traceback
30 import traceback
31 from datetime import datetime, timedelta
31 from datetime import datetime, timedelta
32
32
33 from sqlalchemy.sql.expression import true, or_
34 from zope.cachedescriptors.property import Lazy as LazyProperty
33 from zope.cachedescriptors.property import Lazy as LazyProperty
35
34
36 from rhodecode import events
35 from rhodecode import events
37 from rhodecode.lib import helpers as h
36 from rhodecode.lib import helpers as h
38 from rhodecode.lib.auth import HasUserGroupPermissionAny
37 from rhodecode.lib.auth import HasUserGroupPermissionAny
39 from rhodecode.lib.caching_query import FromCache
38 from rhodecode.lib.caching_query import FromCache
40 from rhodecode.lib.exceptions import AttachedForksError
39 from rhodecode.lib.exceptions import AttachedForksError
41 from rhodecode.lib.hooks_base import log_delete_repository
40 from rhodecode.lib.hooks_base import log_delete_repository
42 from rhodecode.lib.utils import make_db_config
41 from rhodecode.lib.utils import make_db_config
43 from rhodecode.lib.utils2 import (
42 from rhodecode.lib.utils2 import (
44 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
43 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
45 get_current_rhodecode_user, safe_int, datetime_to_time, action_logger_generic)
44 get_current_rhodecode_user, safe_int, datetime_to_time, action_logger_generic)
46 from rhodecode.lib.vcs.backends import get_backend
45 from rhodecode.lib.vcs.backends import get_backend
47 from rhodecode.model import BaseModel
46 from rhodecode.model import BaseModel
48 from rhodecode.model.db import (
47 from rhodecode.model.db import (
49 Repository, UserRepoToPerm, UserGroupRepoToPerm, UserRepoGroupToPerm,
48 Repository, UserRepoToPerm, UserGroupRepoToPerm, UserRepoGroupToPerm,
50 UserGroupRepoGroupToPerm, User, Permission, Statistics, UserGroup,
49 UserGroupRepoGroupToPerm, User, Permission, Statistics, UserGroup,
51 RepoGroup, RepositoryField)
50 RepoGroup, RepositoryField)
52
51
53 from rhodecode.model.settings import VcsSettingsModel
52 from rhodecode.model.settings import VcsSettingsModel
54
53
55
54
56 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
57
56
58
57
59 class RepoModel(BaseModel):
58 class RepoModel(BaseModel):
60
59
61 cls = Repository
60 cls = Repository
62
61
63 def _get_user_group(self, users_group):
62 def _get_user_group(self, users_group):
64 return self._get_instance(UserGroup, users_group,
63 return self._get_instance(UserGroup, users_group,
65 callback=UserGroup.get_by_group_name)
64 callback=UserGroup.get_by_group_name)
66
65
67 def _get_repo_group(self, repo_group):
66 def _get_repo_group(self, repo_group):
68 return self._get_instance(RepoGroup, repo_group,
67 return self._get_instance(RepoGroup, repo_group,
69 callback=RepoGroup.get_by_group_name)
68 callback=RepoGroup.get_by_group_name)
70
69
71 def _create_default_perms(self, repository, private):
70 def _create_default_perms(self, repository, private):
72 # create default permission
71 # create default permission
73 default = 'repository.read'
72 default = 'repository.read'
74 def_user = User.get_default_user()
73 def_user = User.get_default_user()
75 for p in def_user.user_perms:
74 for p in def_user.user_perms:
76 if p.permission.permission_name.startswith('repository.'):
75 if p.permission.permission_name.startswith('repository.'):
77 default = p.permission.permission_name
76 default = p.permission.permission_name
78 break
77 break
79
78
80 default_perm = 'repository.none' if private else default
79 default_perm = 'repository.none' if private else default
81
80
82 repo_to_perm = UserRepoToPerm()
81 repo_to_perm = UserRepoToPerm()
83 repo_to_perm.permission = Permission.get_by_key(default_perm)
82 repo_to_perm.permission = Permission.get_by_key(default_perm)
84
83
85 repo_to_perm.repository = repository
84 repo_to_perm.repository = repository
86 repo_to_perm.user_id = def_user.user_id
85 repo_to_perm.user_id = def_user.user_id
87
86
88 return repo_to_perm
87 return repo_to_perm
89
88
90 @LazyProperty
89 @LazyProperty
91 def repos_path(self):
90 def repos_path(self):
92 """
91 """
93 Gets the repositories root path from database
92 Gets the repositories root path from database
94 """
93 """
95 settings_model = VcsSettingsModel(sa=self.sa)
94 settings_model = VcsSettingsModel(sa=self.sa)
96 return settings_model.get_repos_location()
95 return settings_model.get_repos_location()
97
96
98 def get(self, repo_id, cache=False):
97 def get(self, repo_id, cache=False):
99 repo = self.sa.query(Repository) \
98 repo = self.sa.query(Repository) \
100 .filter(Repository.repo_id == repo_id)
99 .filter(Repository.repo_id == repo_id)
101
100
102 if cache:
101 if cache:
103 repo = repo.options(FromCache("sql_cache_short",
102 repo = repo.options(FromCache("sql_cache_short",
104 "get_repo_%s" % repo_id))
103 "get_repo_%s" % repo_id))
105 return repo.scalar()
104 return repo.scalar()
106
105
107 def get_repo(self, repository):
106 def get_repo(self, repository):
108 return self._get_repo(repository)
107 return self._get_repo(repository)
109
108
110 def get_by_repo_name(self, repo_name, cache=False):
109 def get_by_repo_name(self, repo_name, cache=False):
111 repo = self.sa.query(Repository) \
110 repo = self.sa.query(Repository) \
112 .filter(Repository.repo_name == repo_name)
111 .filter(Repository.repo_name == repo_name)
113
112
114 if cache:
113 if cache:
115 repo = repo.options(FromCache("sql_cache_short",
114 repo = repo.options(FromCache("sql_cache_short",
116 "get_repo_%s" % repo_name))
115 "get_repo_%s" % repo_name))
117 return repo.scalar()
116 return repo.scalar()
118
117
119 def _extract_id_from_repo_name(self, repo_name):
118 def _extract_id_from_repo_name(self, repo_name):
120 if repo_name.startswith('/'):
119 if repo_name.startswith('/'):
121 repo_name = repo_name.lstrip('/')
120 repo_name = repo_name.lstrip('/')
122 by_id_match = re.match(r'^_(\d{1,})', repo_name)
121 by_id_match = re.match(r'^_(\d{1,})', repo_name)
123 if by_id_match:
122 if by_id_match:
124 return by_id_match.groups()[0]
123 return by_id_match.groups()[0]
125
124
126 def get_repo_by_id(self, repo_name):
125 def get_repo_by_id(self, repo_name):
127 """
126 """
128 Extracts repo_name by id from special urls.
127 Extracts repo_name by id from special urls.
129 Example url is _11/repo_name
128 Example url is _11/repo_name
130
129
131 :param repo_name:
130 :param repo_name:
132 :return: repo object if matched else None
131 :return: repo object if matched else None
133 """
132 """
134 try:
133 try:
135 _repo_id = self._extract_id_from_repo_name(repo_name)
134 _repo_id = self._extract_id_from_repo_name(repo_name)
136 if _repo_id:
135 if _repo_id:
137 return self.get(_repo_id)
136 return self.get(_repo_id)
138 except Exception:
137 except Exception:
139 log.exception('Failed to extract repo_name from URL')
138 log.exception('Failed to extract repo_name from URL')
140
139
141 return None
140 return None
142
141
143 def get_repos_for_root(self, root, traverse=False):
142 def get_repos_for_root(self, root, traverse=False):
144 if traverse:
143 if traverse:
145 like_expression = u'{}%'.format(safe_unicode(root))
144 like_expression = u'{}%'.format(safe_unicode(root))
146 repos = Repository.query().filter(
145 repos = Repository.query().filter(
147 Repository.repo_name.like(like_expression)).all()
146 Repository.repo_name.like(like_expression)).all()
148 else:
147 else:
149 if root and not isinstance(root, RepoGroup):
148 if root and not isinstance(root, RepoGroup):
150 raise ValueError(
149 raise ValueError(
151 'Root must be an instance '
150 'Root must be an instance '
152 'of RepoGroup, got:{} instead'.format(type(root)))
151 'of RepoGroup, got:{} instead'.format(type(root)))
153 repos = Repository.query().filter(Repository.group == root).all()
152 repos = Repository.query().filter(Repository.group == root).all()
154 return repos
153 return repos
155
154
156 def get_url(self, repo):
155 def get_url(self, repo):
157 return h.url('summary_home', repo_name=safe_str(repo.repo_name),
156 return h.url('summary_home', repo_name=safe_str(repo.repo_name),
158 qualified=True)
157 qualified=True)
159
158
160 def get_users(self, name_contains=None, limit=20, only_active=True):
161
162 # TODO: mikhail: move this method to the UserModel.
163 query = self.sa.query(User)
164 if only_active:
165 query = query.filter(User.active == true())
166
167 if name_contains:
168 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
169 query = query.filter(
170 or_(
171 User.name.ilike(ilike_expression),
172 User.lastname.ilike(ilike_expression),
173 User.username.ilike(ilike_expression)
174 )
175 )
176 query = query.limit(limit)
177 users = query.all()
178
179 _users = [
180 {
181 'id': user.user_id,
182 'first_name': user.name,
183 'last_name': user.lastname,
184 'username': user.username,
185 'email': user.email,
186 'icon_link': h.gravatar_url(user.email, 30),
187 'value_display': h.person(user),
188 'value': user.username,
189 'value_type': 'user',
190 'active': user.active,
191 }
192 for user in users
193 ]
194 return _users
195
196 @classmethod
159 @classmethod
197 def update_repoinfo(cls, repositories=None):
160 def update_repoinfo(cls, repositories=None):
198 if not repositories:
161 if not repositories:
199 repositories = Repository.getAll()
162 repositories = Repository.getAll()
200 for repo in repositories:
163 for repo in repositories:
201 repo.update_commit_cache()
164 repo.update_commit_cache()
202
165
203 def get_repos_as_dict(self, repo_list=None, admin=False,
166 def get_repos_as_dict(self, repo_list=None, admin=False,
204 super_user_actions=False):
167 super_user_actions=False):
205
168
206 from rhodecode.lib.utils import PartialRenderer
169 from rhodecode.lib.utils import PartialRenderer
207 _render = PartialRenderer('data_table/_dt_elements.mako')
170 _render = PartialRenderer('data_table/_dt_elements.mako')
208 c = _render.c
171 c = _render.c
209
172
210 def quick_menu(repo_name):
173 def quick_menu(repo_name):
211 return _render('quick_menu', repo_name)
174 return _render('quick_menu', repo_name)
212
175
213 def repo_lnk(name, rtype, rstate, private, fork_of):
176 def repo_lnk(name, rtype, rstate, private, fork_of):
214 return _render('repo_name', name, rtype, rstate, private, fork_of,
177 return _render('repo_name', name, rtype, rstate, private, fork_of,
215 short_name=not admin, admin=False)
178 short_name=not admin, admin=False)
216
179
217 def last_change(last_change):
180 def last_change(last_change):
218 if admin and isinstance(last_change, datetime) and not last_change.tzinfo:
181 if admin and isinstance(last_change, datetime) and not last_change.tzinfo:
219 last_change = last_change + timedelta(seconds=
182 last_change = last_change + timedelta(seconds=
220 (datetime.now() - datetime.utcnow()).seconds)
183 (datetime.now() - datetime.utcnow()).seconds)
221 return _render("last_change", last_change)
184 return _render("last_change", last_change)
222
185
223 def rss_lnk(repo_name):
186 def rss_lnk(repo_name):
224 return _render("rss", repo_name)
187 return _render("rss", repo_name)
225
188
226 def atom_lnk(repo_name):
189 def atom_lnk(repo_name):
227 return _render("atom", repo_name)
190 return _render("atom", repo_name)
228
191
229 def last_rev(repo_name, cs_cache):
192 def last_rev(repo_name, cs_cache):
230 return _render('revision', repo_name, cs_cache.get('revision'),
193 return _render('revision', repo_name, cs_cache.get('revision'),
231 cs_cache.get('raw_id'), cs_cache.get('author'),
194 cs_cache.get('raw_id'), cs_cache.get('author'),
232 cs_cache.get('message'))
195 cs_cache.get('message'))
233
196
234 def desc(desc):
197 def desc(desc):
235 if c.visual.stylify_metatags:
198 if c.visual.stylify_metatags:
236 desc = h.urlify_text(h.escaped_stylize(desc))
199 desc = h.urlify_text(h.escaped_stylize(desc))
237 else:
200 else:
238 desc = h.urlify_text(h.html_escape(desc))
201 desc = h.urlify_text(h.html_escape(desc))
239
202
240 return _render('repo_desc', desc)
203 return _render('repo_desc', desc)
241
204
242 def state(repo_state):
205 def state(repo_state):
243 return _render("repo_state", repo_state)
206 return _render("repo_state", repo_state)
244
207
245 def repo_actions(repo_name):
208 def repo_actions(repo_name):
246 return _render('repo_actions', repo_name, super_user_actions)
209 return _render('repo_actions', repo_name, super_user_actions)
247
210
248 def user_profile(username):
211 def user_profile(username):
249 return _render('user_profile', username)
212 return _render('user_profile', username)
250
213
251 repos_data = []
214 repos_data = []
252 for repo in repo_list:
215 for repo in repo_list:
253 cs_cache = repo.changeset_cache
216 cs_cache = repo.changeset_cache
254 row = {
217 row = {
255 "menu": quick_menu(repo.repo_name),
218 "menu": quick_menu(repo.repo_name),
256
219
257 "name": repo_lnk(repo.repo_name, repo.repo_type,
220 "name": repo_lnk(repo.repo_name, repo.repo_type,
258 repo.repo_state, repo.private, repo.fork),
221 repo.repo_state, repo.private, repo.fork),
259 "name_raw": repo.repo_name.lower(),
222 "name_raw": repo.repo_name.lower(),
260
223
261 "last_change": last_change(repo.last_db_change),
224 "last_change": last_change(repo.last_db_change),
262 "last_change_raw": datetime_to_time(repo.last_db_change),
225 "last_change_raw": datetime_to_time(repo.last_db_change),
263
226
264 "last_changeset": last_rev(repo.repo_name, cs_cache),
227 "last_changeset": last_rev(repo.repo_name, cs_cache),
265 "last_changeset_raw": cs_cache.get('revision'),
228 "last_changeset_raw": cs_cache.get('revision'),
266
229
267 "desc": desc(repo.description),
230 "desc": desc(repo.description),
268 "owner": user_profile(repo.user.username),
231 "owner": user_profile(repo.user.username),
269
232
270 "state": state(repo.repo_state),
233 "state": state(repo.repo_state),
271 "rss": rss_lnk(repo.repo_name),
234 "rss": rss_lnk(repo.repo_name),
272
235
273 "atom": atom_lnk(repo.repo_name),
236 "atom": atom_lnk(repo.repo_name),
274 }
237 }
275 if admin:
238 if admin:
276 row.update({
239 row.update({
277 "action": repo_actions(repo.repo_name),
240 "action": repo_actions(repo.repo_name),
278 })
241 })
279 repos_data.append(row)
242 repos_data.append(row)
280
243
281 return repos_data
244 return repos_data
282
245
283 def _get_defaults(self, repo_name):
246 def _get_defaults(self, repo_name):
284 """
247 """
285 Gets information about repository, and returns a dict for
248 Gets information about repository, and returns a dict for
286 usage in forms
249 usage in forms
287
250
288 :param repo_name:
251 :param repo_name:
289 """
252 """
290
253
291 repo_info = Repository.get_by_repo_name(repo_name)
254 repo_info = Repository.get_by_repo_name(repo_name)
292
255
293 if repo_info is None:
256 if repo_info is None:
294 return None
257 return None
295
258
296 defaults = repo_info.get_dict()
259 defaults = repo_info.get_dict()
297 defaults['repo_name'] = repo_info.just_name
260 defaults['repo_name'] = repo_info.just_name
298
261
299 groups = repo_info.groups_with_parents
262 groups = repo_info.groups_with_parents
300 parent_group = groups[-1] if groups else None
263 parent_group = groups[-1] if groups else None
301
264
302 # we use -1 as this is how in HTML, we mark an empty group
265 # we use -1 as this is how in HTML, we mark an empty group
303 defaults['repo_group'] = getattr(parent_group, 'group_id', -1)
266 defaults['repo_group'] = getattr(parent_group, 'group_id', -1)
304
267
305 keys_to_process = (
268 keys_to_process = (
306 {'k': 'repo_type', 'strip': False},
269 {'k': 'repo_type', 'strip': False},
307 {'k': 'repo_enable_downloads', 'strip': True},
270 {'k': 'repo_enable_downloads', 'strip': True},
308 {'k': 'repo_description', 'strip': True},
271 {'k': 'repo_description', 'strip': True},
309 {'k': 'repo_enable_locking', 'strip': True},
272 {'k': 'repo_enable_locking', 'strip': True},
310 {'k': 'repo_landing_rev', 'strip': True},
273 {'k': 'repo_landing_rev', 'strip': True},
311 {'k': 'clone_uri', 'strip': False},
274 {'k': 'clone_uri', 'strip': False},
312 {'k': 'repo_private', 'strip': True},
275 {'k': 'repo_private', 'strip': True},
313 {'k': 'repo_enable_statistics', 'strip': True}
276 {'k': 'repo_enable_statistics', 'strip': True}
314 )
277 )
315
278
316 for item in keys_to_process:
279 for item in keys_to_process:
317 attr = item['k']
280 attr = item['k']
318 if item['strip']:
281 if item['strip']:
319 attr = remove_prefix(item['k'], 'repo_')
282 attr = remove_prefix(item['k'], 'repo_')
320
283
321 val = defaults[attr]
284 val = defaults[attr]
322 if item['k'] == 'repo_landing_rev':
285 if item['k'] == 'repo_landing_rev':
323 val = ':'.join(defaults[attr])
286 val = ':'.join(defaults[attr])
324 defaults[item['k']] = val
287 defaults[item['k']] = val
325 if item['k'] == 'clone_uri':
288 if item['k'] == 'clone_uri':
326 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
289 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
327
290
328 # fill owner
291 # fill owner
329 if repo_info.user:
292 if repo_info.user:
330 defaults.update({'user': repo_info.user.username})
293 defaults.update({'user': repo_info.user.username})
331 else:
294 else:
332 replacement_user = User.get_first_super_admin().username
295 replacement_user = User.get_first_super_admin().username
333 defaults.update({'user': replacement_user})
296 defaults.update({'user': replacement_user})
334
297
335 # fill repository users
298 # fill repository users
336 for p in repo_info.repo_to_perm:
299 for p in repo_info.repo_to_perm:
337 defaults.update({'u_perm_%s' % p.user.user_id:
300 defaults.update({'u_perm_%s' % p.user.user_id:
338 p.permission.permission_name})
301 p.permission.permission_name})
339
302
340 # fill repository groups
303 # fill repository groups
341 for p in repo_info.users_group_to_perm:
304 for p in repo_info.users_group_to_perm:
342 defaults.update({'g_perm_%s' % p.users_group.users_group_id:
305 defaults.update({'g_perm_%s' % p.users_group.users_group_id:
343 p.permission.permission_name})
306 p.permission.permission_name})
344
307
345 return defaults
308 return defaults
346
309
347 def update(self, repo, **kwargs):
310 def update(self, repo, **kwargs):
348 try:
311 try:
349 cur_repo = self._get_repo(repo)
312 cur_repo = self._get_repo(repo)
350 source_repo_name = cur_repo.repo_name
313 source_repo_name = cur_repo.repo_name
351 if 'user' in kwargs:
314 if 'user' in kwargs:
352 cur_repo.user = User.get_by_username(kwargs['user'])
315 cur_repo.user = User.get_by_username(kwargs['user'])
353
316
354 if 'repo_group' in kwargs:
317 if 'repo_group' in kwargs:
355 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
318 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
356 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
319 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
357
320
358 update_keys = [
321 update_keys = [
359 (1, 'repo_description'),
322 (1, 'repo_description'),
360 (1, 'repo_landing_rev'),
323 (1, 'repo_landing_rev'),
361 (1, 'repo_private'),
324 (1, 'repo_private'),
362 (1, 'repo_enable_downloads'),
325 (1, 'repo_enable_downloads'),
363 (1, 'repo_enable_locking'),
326 (1, 'repo_enable_locking'),
364 (1, 'repo_enable_statistics'),
327 (1, 'repo_enable_statistics'),
365 (0, 'clone_uri'),
328 (0, 'clone_uri'),
366 (0, 'fork_id')
329 (0, 'fork_id')
367 ]
330 ]
368 for strip, k in update_keys:
331 for strip, k in update_keys:
369 if k in kwargs:
332 if k in kwargs:
370 val = kwargs[k]
333 val = kwargs[k]
371 if strip:
334 if strip:
372 k = remove_prefix(k, 'repo_')
335 k = remove_prefix(k, 'repo_')
373 if k == 'clone_uri':
336 if k == 'clone_uri':
374 from rhodecode.model.validators import Missing
337 from rhodecode.model.validators import Missing
375 _change = kwargs.get('clone_uri_change')
338 _change = kwargs.get('clone_uri_change')
376 if _change in [Missing, 'OLD']:
339 if _change in [Missing, 'OLD']:
377 # we don't change the value, so use original one
340 # we don't change the value, so use original one
378 val = cur_repo.clone_uri
341 val = cur_repo.clone_uri
379
342
380 setattr(cur_repo, k, val)
343 setattr(cur_repo, k, val)
381
344
382 new_name = cur_repo.get_new_name(kwargs['repo_name'])
345 new_name = cur_repo.get_new_name(kwargs['repo_name'])
383 cur_repo.repo_name = new_name
346 cur_repo.repo_name = new_name
384
347
385 # if private flag is set, reset default permission to NONE
348 # if private flag is set, reset default permission to NONE
386 if kwargs.get('repo_private'):
349 if kwargs.get('repo_private'):
387 EMPTY_PERM = 'repository.none'
350 EMPTY_PERM = 'repository.none'
388 RepoModel().grant_user_permission(
351 RepoModel().grant_user_permission(
389 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
352 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
390 )
353 )
391
354
392 # handle extra fields
355 # handle extra fields
393 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX),
356 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX),
394 kwargs):
357 kwargs):
395 k = RepositoryField.un_prefix_key(field)
358 k = RepositoryField.un_prefix_key(field)
396 ex_field = RepositoryField.get_by_key_name(
359 ex_field = RepositoryField.get_by_key_name(
397 key=k, repo=cur_repo)
360 key=k, repo=cur_repo)
398 if ex_field:
361 if ex_field:
399 ex_field.field_value = kwargs[field]
362 ex_field.field_value = kwargs[field]
400 self.sa.add(ex_field)
363 self.sa.add(ex_field)
401 self.sa.add(cur_repo)
364 self.sa.add(cur_repo)
402
365
403 if source_repo_name != new_name:
366 if source_repo_name != new_name:
404 # rename repository
367 # rename repository
405 self._rename_filesystem_repo(
368 self._rename_filesystem_repo(
406 old=source_repo_name, new=new_name)
369 old=source_repo_name, new=new_name)
407
370
408 return cur_repo
371 return cur_repo
409 except Exception:
372 except Exception:
410 log.error(traceback.format_exc())
373 log.error(traceback.format_exc())
411 raise
374 raise
412
375
413 def _create_repo(self, repo_name, repo_type, description, owner,
376 def _create_repo(self, repo_name, repo_type, description, owner,
414 private=False, clone_uri=None, repo_group=None,
377 private=False, clone_uri=None, repo_group=None,
415 landing_rev='rev:tip', fork_of=None,
378 landing_rev='rev:tip', fork_of=None,
416 copy_fork_permissions=False, enable_statistics=False,
379 copy_fork_permissions=False, enable_statistics=False,
417 enable_locking=False, enable_downloads=False,
380 enable_locking=False, enable_downloads=False,
418 copy_group_permissions=False,
381 copy_group_permissions=False,
419 state=Repository.STATE_PENDING):
382 state=Repository.STATE_PENDING):
420 """
383 """
421 Create repository inside database with PENDING state, this should be
384 Create repository inside database with PENDING state, this should be
422 only executed by create() repo. With exception of importing existing
385 only executed by create() repo. With exception of importing existing
423 repos
386 repos
424 """
387 """
425 from rhodecode.model.scm import ScmModel
388 from rhodecode.model.scm import ScmModel
426
389
427 owner = self._get_user(owner)
390 owner = self._get_user(owner)
428 fork_of = self._get_repo(fork_of)
391 fork_of = self._get_repo(fork_of)
429 repo_group = self._get_repo_group(safe_int(repo_group))
392 repo_group = self._get_repo_group(safe_int(repo_group))
430
393
431 try:
394 try:
432 repo_name = safe_unicode(repo_name)
395 repo_name = safe_unicode(repo_name)
433 description = safe_unicode(description)
396 description = safe_unicode(description)
434 # repo name is just a name of repository
397 # repo name is just a name of repository
435 # while repo_name_full is a full qualified name that is combined
398 # while repo_name_full is a full qualified name that is combined
436 # with name and path of group
399 # with name and path of group
437 repo_name_full = repo_name
400 repo_name_full = repo_name
438 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
401 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
439
402
440 new_repo = Repository()
403 new_repo = Repository()
441 new_repo.repo_state = state
404 new_repo.repo_state = state
442 new_repo.enable_statistics = False
405 new_repo.enable_statistics = False
443 new_repo.repo_name = repo_name_full
406 new_repo.repo_name = repo_name_full
444 new_repo.repo_type = repo_type
407 new_repo.repo_type = repo_type
445 new_repo.user = owner
408 new_repo.user = owner
446 new_repo.group = repo_group
409 new_repo.group = repo_group
447 new_repo.description = description or repo_name
410 new_repo.description = description or repo_name
448 new_repo.private = private
411 new_repo.private = private
449 new_repo.clone_uri = clone_uri
412 new_repo.clone_uri = clone_uri
450 new_repo.landing_rev = landing_rev
413 new_repo.landing_rev = landing_rev
451
414
452 new_repo.enable_statistics = enable_statistics
415 new_repo.enable_statistics = enable_statistics
453 new_repo.enable_locking = enable_locking
416 new_repo.enable_locking = enable_locking
454 new_repo.enable_downloads = enable_downloads
417 new_repo.enable_downloads = enable_downloads
455
418
456 if repo_group:
419 if repo_group:
457 new_repo.enable_locking = repo_group.enable_locking
420 new_repo.enable_locking = repo_group.enable_locking
458
421
459 if fork_of:
422 if fork_of:
460 parent_repo = fork_of
423 parent_repo = fork_of
461 new_repo.fork = parent_repo
424 new_repo.fork = parent_repo
462
425
463 events.trigger(events.RepoPreCreateEvent(new_repo))
426 events.trigger(events.RepoPreCreateEvent(new_repo))
464
427
465 self.sa.add(new_repo)
428 self.sa.add(new_repo)
466
429
467 EMPTY_PERM = 'repository.none'
430 EMPTY_PERM = 'repository.none'
468 if fork_of and copy_fork_permissions:
431 if fork_of and copy_fork_permissions:
469 repo = fork_of
432 repo = fork_of
470 user_perms = UserRepoToPerm.query() \
433 user_perms = UserRepoToPerm.query() \
471 .filter(UserRepoToPerm.repository == repo).all()
434 .filter(UserRepoToPerm.repository == repo).all()
472 group_perms = UserGroupRepoToPerm.query() \
435 group_perms = UserGroupRepoToPerm.query() \
473 .filter(UserGroupRepoToPerm.repository == repo).all()
436 .filter(UserGroupRepoToPerm.repository == repo).all()
474
437
475 for perm in user_perms:
438 for perm in user_perms:
476 UserRepoToPerm.create(
439 UserRepoToPerm.create(
477 perm.user, new_repo, perm.permission)
440 perm.user, new_repo, perm.permission)
478
441
479 for perm in group_perms:
442 for perm in group_perms:
480 UserGroupRepoToPerm.create(
443 UserGroupRepoToPerm.create(
481 perm.users_group, new_repo, perm.permission)
444 perm.users_group, new_repo, perm.permission)
482 # in case we copy permissions and also set this repo to private
445 # in case we copy permissions and also set this repo to private
483 # override the default user permission to make it a private
446 # override the default user permission to make it a private
484 # repo
447 # repo
485 if private:
448 if private:
486 RepoModel(self.sa).grant_user_permission(
449 RepoModel(self.sa).grant_user_permission(
487 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
450 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
488
451
489 elif repo_group and copy_group_permissions:
452 elif repo_group and copy_group_permissions:
490 user_perms = UserRepoGroupToPerm.query() \
453 user_perms = UserRepoGroupToPerm.query() \
491 .filter(UserRepoGroupToPerm.group == repo_group).all()
454 .filter(UserRepoGroupToPerm.group == repo_group).all()
492
455
493 group_perms = UserGroupRepoGroupToPerm.query() \
456 group_perms = UserGroupRepoGroupToPerm.query() \
494 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
457 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
495
458
496 for perm in user_perms:
459 for perm in user_perms:
497 perm_name = perm.permission.permission_name.replace(
460 perm_name = perm.permission.permission_name.replace(
498 'group.', 'repository.')
461 'group.', 'repository.')
499 perm_obj = Permission.get_by_key(perm_name)
462 perm_obj = Permission.get_by_key(perm_name)
500 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
463 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
501
464
502 for perm in group_perms:
465 for perm in group_perms:
503 perm_name = perm.permission.permission_name.replace(
466 perm_name = perm.permission.permission_name.replace(
504 'group.', 'repository.')
467 'group.', 'repository.')
505 perm_obj = Permission.get_by_key(perm_name)
468 perm_obj = Permission.get_by_key(perm_name)
506 UserGroupRepoToPerm.create(
469 UserGroupRepoToPerm.create(
507 perm.users_group, new_repo, perm_obj)
470 perm.users_group, new_repo, perm_obj)
508
471
509 if private:
472 if private:
510 RepoModel(self.sa).grant_user_permission(
473 RepoModel(self.sa).grant_user_permission(
511 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
474 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
512
475
513 else:
476 else:
514 perm_obj = self._create_default_perms(new_repo, private)
477 perm_obj = self._create_default_perms(new_repo, private)
515 self.sa.add(perm_obj)
478 self.sa.add(perm_obj)
516
479
517 # now automatically start following this repository as owner
480 # now automatically start following this repository as owner
518 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
481 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
519 owner.user_id)
482 owner.user_id)
520
483
521 # we need to flush here, in order to check if database won't
484 # we need to flush here, in order to check if database won't
522 # throw any exceptions, create filesystem dirs at the very end
485 # throw any exceptions, create filesystem dirs at the very end
523 self.sa.flush()
486 self.sa.flush()
524 events.trigger(events.RepoCreateEvent(new_repo))
487 events.trigger(events.RepoCreateEvent(new_repo))
525 return new_repo
488 return new_repo
526
489
527 except Exception:
490 except Exception:
528 log.error(traceback.format_exc())
491 log.error(traceback.format_exc())
529 raise
492 raise
530
493
531 def create(self, form_data, cur_user):
494 def create(self, form_data, cur_user):
532 """
495 """
533 Create repository using celery tasks
496 Create repository using celery tasks
534
497
535 :param form_data:
498 :param form_data:
536 :param cur_user:
499 :param cur_user:
537 """
500 """
538 from rhodecode.lib.celerylib import tasks, run_task
501 from rhodecode.lib.celerylib import tasks, run_task
539 return run_task(tasks.create_repo, form_data, cur_user)
502 return run_task(tasks.create_repo, form_data, cur_user)
540
503
541 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
504 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
542 perm_deletions=None, check_perms=True,
505 perm_deletions=None, check_perms=True,
543 cur_user=None):
506 cur_user=None):
544 if not perm_additions:
507 if not perm_additions:
545 perm_additions = []
508 perm_additions = []
546 if not perm_updates:
509 if not perm_updates:
547 perm_updates = []
510 perm_updates = []
548 if not perm_deletions:
511 if not perm_deletions:
549 perm_deletions = []
512 perm_deletions = []
550
513
551 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
514 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
552
515
553 # update permissions
516 # update permissions
554 for member_id, perm, member_type in perm_updates:
517 for member_id, perm, member_type in perm_updates:
555 member_id = int(member_id)
518 member_id = int(member_id)
556 if member_type == 'user':
519 if member_type == 'user':
557 # this updates also current one if found
520 # this updates also current one if found
558 self.grant_user_permission(
521 self.grant_user_permission(
559 repo=repo, user=member_id, perm=perm)
522 repo=repo, user=member_id, perm=perm)
560 else: # set for user group
523 else: # set for user group
561 # check if we have permissions to alter this usergroup
524 # check if we have permissions to alter this usergroup
562 member_name = UserGroup.get(member_id).users_group_name
525 member_name = UserGroup.get(member_id).users_group_name
563 if not check_perms or HasUserGroupPermissionAny(
526 if not check_perms or HasUserGroupPermissionAny(
564 *req_perms)(member_name, user=cur_user):
527 *req_perms)(member_name, user=cur_user):
565 self.grant_user_group_permission(
528 self.grant_user_group_permission(
566 repo=repo, group_name=member_id, perm=perm)
529 repo=repo, group_name=member_id, perm=perm)
567
530
568 # set new permissions
531 # set new permissions
569 for member_id, perm, member_type in perm_additions:
532 for member_id, perm, member_type in perm_additions:
570 member_id = int(member_id)
533 member_id = int(member_id)
571 if member_type == 'user':
534 if member_type == 'user':
572 self.grant_user_permission(
535 self.grant_user_permission(
573 repo=repo, user=member_id, perm=perm)
536 repo=repo, user=member_id, perm=perm)
574 else: # set for user group
537 else: # set for user group
575 # check if we have permissions to alter this usergroup
538 # check if we have permissions to alter this usergroup
576 member_name = UserGroup.get(member_id).users_group_name
539 member_name = UserGroup.get(member_id).users_group_name
577 if not check_perms or HasUserGroupPermissionAny(
540 if not check_perms or HasUserGroupPermissionAny(
578 *req_perms)(member_name, user=cur_user):
541 *req_perms)(member_name, user=cur_user):
579 self.grant_user_group_permission(
542 self.grant_user_group_permission(
580 repo=repo, group_name=member_id, perm=perm)
543 repo=repo, group_name=member_id, perm=perm)
581
544
582 # delete permissions
545 # delete permissions
583 for member_id, perm, member_type in perm_deletions:
546 for member_id, perm, member_type in perm_deletions:
584 member_id = int(member_id)
547 member_id = int(member_id)
585 if member_type == 'user':
548 if member_type == 'user':
586 self.revoke_user_permission(repo=repo, user=member_id)
549 self.revoke_user_permission(repo=repo, user=member_id)
587 else: # set for user group
550 else: # set for user group
588 # check if we have permissions to alter this usergroup
551 # check if we have permissions to alter this usergroup
589 member_name = UserGroup.get(member_id).users_group_name
552 member_name = UserGroup.get(member_id).users_group_name
590 if not check_perms or HasUserGroupPermissionAny(
553 if not check_perms or HasUserGroupPermissionAny(
591 *req_perms)(member_name, user=cur_user):
554 *req_perms)(member_name, user=cur_user):
592 self.revoke_user_group_permission(
555 self.revoke_user_group_permission(
593 repo=repo, group_name=member_id)
556 repo=repo, group_name=member_id)
594
557
595 def create_fork(self, form_data, cur_user):
558 def create_fork(self, form_data, cur_user):
596 """
559 """
597 Simple wrapper into executing celery task for fork creation
560 Simple wrapper into executing celery task for fork creation
598
561
599 :param form_data:
562 :param form_data:
600 :param cur_user:
563 :param cur_user:
601 """
564 """
602 from rhodecode.lib.celerylib import tasks, run_task
565 from rhodecode.lib.celerylib import tasks, run_task
603 return run_task(tasks.create_repo_fork, form_data, cur_user)
566 return run_task(tasks.create_repo_fork, form_data, cur_user)
604
567
605 def delete(self, repo, forks=None, fs_remove=True, cur_user=None):
568 def delete(self, repo, forks=None, fs_remove=True, cur_user=None):
606 """
569 """
607 Delete given repository, forks parameter defines what do do with
570 Delete given repository, forks parameter defines what do do with
608 attached forks. Throws AttachedForksError if deleted repo has attached
571 attached forks. Throws AttachedForksError if deleted repo has attached
609 forks
572 forks
610
573
611 :param repo:
574 :param repo:
612 :param forks: str 'delete' or 'detach'
575 :param forks: str 'delete' or 'detach'
613 :param fs_remove: remove(archive) repo from filesystem
576 :param fs_remove: remove(archive) repo from filesystem
614 """
577 """
615 if not cur_user:
578 if not cur_user:
616 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
579 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
617 repo = self._get_repo(repo)
580 repo = self._get_repo(repo)
618 if repo:
581 if repo:
619 if forks == 'detach':
582 if forks == 'detach':
620 for r in repo.forks:
583 for r in repo.forks:
621 r.fork = None
584 r.fork = None
622 self.sa.add(r)
585 self.sa.add(r)
623 elif forks == 'delete':
586 elif forks == 'delete':
624 for r in repo.forks:
587 for r in repo.forks:
625 self.delete(r, forks='delete')
588 self.delete(r, forks='delete')
626 elif [f for f in repo.forks]:
589 elif [f for f in repo.forks]:
627 raise AttachedForksError()
590 raise AttachedForksError()
628
591
629 old_repo_dict = repo.get_dict()
592 old_repo_dict = repo.get_dict()
630 events.trigger(events.RepoPreDeleteEvent(repo))
593 events.trigger(events.RepoPreDeleteEvent(repo))
631 try:
594 try:
632 self.sa.delete(repo)
595 self.sa.delete(repo)
633 if fs_remove:
596 if fs_remove:
634 self._delete_filesystem_repo(repo)
597 self._delete_filesystem_repo(repo)
635 else:
598 else:
636 log.debug('skipping removal from filesystem')
599 log.debug('skipping removal from filesystem')
637 old_repo_dict.update({
600 old_repo_dict.update({
638 'deleted_by': cur_user,
601 'deleted_by': cur_user,
639 'deleted_on': time.time(),
602 'deleted_on': time.time(),
640 })
603 })
641 log_delete_repository(**old_repo_dict)
604 log_delete_repository(**old_repo_dict)
642 events.trigger(events.RepoDeleteEvent(repo))
605 events.trigger(events.RepoDeleteEvent(repo))
643 except Exception:
606 except Exception:
644 log.error(traceback.format_exc())
607 log.error(traceback.format_exc())
645 raise
608 raise
646
609
647 def grant_user_permission(self, repo, user, perm):
610 def grant_user_permission(self, repo, user, perm):
648 """
611 """
649 Grant permission for user on given repository, or update existing one
612 Grant permission for user on given repository, or update existing one
650 if found
613 if found
651
614
652 :param repo: Instance of Repository, repository_id, or repository name
615 :param repo: Instance of Repository, repository_id, or repository name
653 :param user: Instance of User, user_id or username
616 :param user: Instance of User, user_id or username
654 :param perm: Instance of Permission, or permission_name
617 :param perm: Instance of Permission, or permission_name
655 """
618 """
656 user = self._get_user(user)
619 user = self._get_user(user)
657 repo = self._get_repo(repo)
620 repo = self._get_repo(repo)
658 permission = self._get_perm(perm)
621 permission = self._get_perm(perm)
659
622
660 # check if we have that permission already
623 # check if we have that permission already
661 obj = self.sa.query(UserRepoToPerm) \
624 obj = self.sa.query(UserRepoToPerm) \
662 .filter(UserRepoToPerm.user == user) \
625 .filter(UserRepoToPerm.user == user) \
663 .filter(UserRepoToPerm.repository == repo) \
626 .filter(UserRepoToPerm.repository == repo) \
664 .scalar()
627 .scalar()
665 if obj is None:
628 if obj is None:
666 # create new !
629 # create new !
667 obj = UserRepoToPerm()
630 obj = UserRepoToPerm()
668 obj.repository = repo
631 obj.repository = repo
669 obj.user = user
632 obj.user = user
670 obj.permission = permission
633 obj.permission = permission
671 self.sa.add(obj)
634 self.sa.add(obj)
672 log.debug('Granted perm %s to %s on %s', perm, user, repo)
635 log.debug('Granted perm %s to %s on %s', perm, user, repo)
673 action_logger_generic(
636 action_logger_generic(
674 'granted permission: {} to user: {} on repo: {}'.format(
637 'granted permission: {} to user: {} on repo: {}'.format(
675 perm, user, repo), namespace='security.repo')
638 perm, user, repo), namespace='security.repo')
676 return obj
639 return obj
677
640
678 def revoke_user_permission(self, repo, user):
641 def revoke_user_permission(self, repo, user):
679 """
642 """
680 Revoke permission for user on given repository
643 Revoke permission for user on given repository
681
644
682 :param repo: Instance of Repository, repository_id, or repository name
645 :param repo: Instance of Repository, repository_id, or repository name
683 :param user: Instance of User, user_id or username
646 :param user: Instance of User, user_id or username
684 """
647 """
685
648
686 user = self._get_user(user)
649 user = self._get_user(user)
687 repo = self._get_repo(repo)
650 repo = self._get_repo(repo)
688
651
689 obj = self.sa.query(UserRepoToPerm) \
652 obj = self.sa.query(UserRepoToPerm) \
690 .filter(UserRepoToPerm.repository == repo) \
653 .filter(UserRepoToPerm.repository == repo) \
691 .filter(UserRepoToPerm.user == user) \
654 .filter(UserRepoToPerm.user == user) \
692 .scalar()
655 .scalar()
693 if obj:
656 if obj:
694 self.sa.delete(obj)
657 self.sa.delete(obj)
695 log.debug('Revoked perm on %s on %s', repo, user)
658 log.debug('Revoked perm on %s on %s', repo, user)
696 action_logger_generic(
659 action_logger_generic(
697 'revoked permission from user: {} on repo: {}'.format(
660 'revoked permission from user: {} on repo: {}'.format(
698 user, repo), namespace='security.repo')
661 user, repo), namespace='security.repo')
699
662
700 def grant_user_group_permission(self, repo, group_name, perm):
663 def grant_user_group_permission(self, repo, group_name, perm):
701 """
664 """
702 Grant permission for user group on given repository, or update
665 Grant permission for user group on given repository, or update
703 existing one if found
666 existing one if found
704
667
705 :param repo: Instance of Repository, repository_id, or repository name
668 :param repo: Instance of Repository, repository_id, or repository name
706 :param group_name: Instance of UserGroup, users_group_id,
669 :param group_name: Instance of UserGroup, users_group_id,
707 or user group name
670 or user group name
708 :param perm: Instance of Permission, or permission_name
671 :param perm: Instance of Permission, or permission_name
709 """
672 """
710 repo = self._get_repo(repo)
673 repo = self._get_repo(repo)
711 group_name = self._get_user_group(group_name)
674 group_name = self._get_user_group(group_name)
712 permission = self._get_perm(perm)
675 permission = self._get_perm(perm)
713
676
714 # check if we have that permission already
677 # check if we have that permission already
715 obj = self.sa.query(UserGroupRepoToPerm) \
678 obj = self.sa.query(UserGroupRepoToPerm) \
716 .filter(UserGroupRepoToPerm.users_group == group_name) \
679 .filter(UserGroupRepoToPerm.users_group == group_name) \
717 .filter(UserGroupRepoToPerm.repository == repo) \
680 .filter(UserGroupRepoToPerm.repository == repo) \
718 .scalar()
681 .scalar()
719
682
720 if obj is None:
683 if obj is None:
721 # create new
684 # create new
722 obj = UserGroupRepoToPerm()
685 obj = UserGroupRepoToPerm()
723
686
724 obj.repository = repo
687 obj.repository = repo
725 obj.users_group = group_name
688 obj.users_group = group_name
726 obj.permission = permission
689 obj.permission = permission
727 self.sa.add(obj)
690 self.sa.add(obj)
728 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
691 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
729 action_logger_generic(
692 action_logger_generic(
730 'granted permission: {} to usergroup: {} on repo: {}'.format(
693 'granted permission: {} to usergroup: {} on repo: {}'.format(
731 perm, group_name, repo), namespace='security.repo')
694 perm, group_name, repo), namespace='security.repo')
732
695
733 return obj
696 return obj
734
697
735 def revoke_user_group_permission(self, repo, group_name):
698 def revoke_user_group_permission(self, repo, group_name):
736 """
699 """
737 Revoke permission for user group on given repository
700 Revoke permission for user group on given repository
738
701
739 :param repo: Instance of Repository, repository_id, or repository name
702 :param repo: Instance of Repository, repository_id, or repository name
740 :param group_name: Instance of UserGroup, users_group_id,
703 :param group_name: Instance of UserGroup, users_group_id,
741 or user group name
704 or user group name
742 """
705 """
743 repo = self._get_repo(repo)
706 repo = self._get_repo(repo)
744 group_name = self._get_user_group(group_name)
707 group_name = self._get_user_group(group_name)
745
708
746 obj = self.sa.query(UserGroupRepoToPerm) \
709 obj = self.sa.query(UserGroupRepoToPerm) \
747 .filter(UserGroupRepoToPerm.repository == repo) \
710 .filter(UserGroupRepoToPerm.repository == repo) \
748 .filter(UserGroupRepoToPerm.users_group == group_name) \
711 .filter(UserGroupRepoToPerm.users_group == group_name) \
749 .scalar()
712 .scalar()
750 if obj:
713 if obj:
751 self.sa.delete(obj)
714 self.sa.delete(obj)
752 log.debug('Revoked perm to %s on %s', repo, group_name)
715 log.debug('Revoked perm to %s on %s', repo, group_name)
753 action_logger_generic(
716 action_logger_generic(
754 'revoked permission from usergroup: {} on repo: {}'.format(
717 'revoked permission from usergroup: {} on repo: {}'.format(
755 group_name, repo), namespace='security.repo')
718 group_name, repo), namespace='security.repo')
756
719
757 def delete_stats(self, repo_name):
720 def delete_stats(self, repo_name):
758 """
721 """
759 removes stats for given repo
722 removes stats for given repo
760
723
761 :param repo_name:
724 :param repo_name:
762 """
725 """
763 repo = self._get_repo(repo_name)
726 repo = self._get_repo(repo_name)
764 try:
727 try:
765 obj = self.sa.query(Statistics) \
728 obj = self.sa.query(Statistics) \
766 .filter(Statistics.repository == repo).scalar()
729 .filter(Statistics.repository == repo).scalar()
767 if obj:
730 if obj:
768 self.sa.delete(obj)
731 self.sa.delete(obj)
769 except Exception:
732 except Exception:
770 log.error(traceback.format_exc())
733 log.error(traceback.format_exc())
771 raise
734 raise
772
735
773 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
736 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
774 field_type='str', field_desc=''):
737 field_type='str', field_desc=''):
775
738
776 repo = self._get_repo(repo_name)
739 repo = self._get_repo(repo_name)
777
740
778 new_field = RepositoryField()
741 new_field = RepositoryField()
779 new_field.repository = repo
742 new_field.repository = repo
780 new_field.field_key = field_key
743 new_field.field_key = field_key
781 new_field.field_type = field_type # python type
744 new_field.field_type = field_type # python type
782 new_field.field_value = field_value
745 new_field.field_value = field_value
783 new_field.field_desc = field_desc
746 new_field.field_desc = field_desc
784 new_field.field_label = field_label
747 new_field.field_label = field_label
785 self.sa.add(new_field)
748 self.sa.add(new_field)
786 return new_field
749 return new_field
787
750
788 def delete_repo_field(self, repo_name, field_key):
751 def delete_repo_field(self, repo_name, field_key):
789 repo = self._get_repo(repo_name)
752 repo = self._get_repo(repo_name)
790 field = RepositoryField.get_by_key_name(field_key, repo)
753 field = RepositoryField.get_by_key_name(field_key, repo)
791 if field:
754 if field:
792 self.sa.delete(field)
755 self.sa.delete(field)
793
756
794 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
757 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
795 clone_uri=None, repo_store_location=None,
758 clone_uri=None, repo_store_location=None,
796 use_global_config=False):
759 use_global_config=False):
797 """
760 """
798 makes repository on filesystem. It's group aware means it'll create
761 makes repository on filesystem. It's group aware means it'll create
799 a repository within a group, and alter the paths accordingly of
762 a repository within a group, and alter the paths accordingly of
800 group location
763 group location
801
764
802 :param repo_name:
765 :param repo_name:
803 :param alias:
766 :param alias:
804 :param parent:
767 :param parent:
805 :param clone_uri:
768 :param clone_uri:
806 :param repo_store_location:
769 :param repo_store_location:
807 """
770 """
808 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
771 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
809 from rhodecode.model.scm import ScmModel
772 from rhodecode.model.scm import ScmModel
810
773
811 if Repository.NAME_SEP in repo_name:
774 if Repository.NAME_SEP in repo_name:
812 raise ValueError(
775 raise ValueError(
813 'repo_name must not contain groups got `%s`' % repo_name)
776 'repo_name must not contain groups got `%s`' % repo_name)
814
777
815 if isinstance(repo_group, RepoGroup):
778 if isinstance(repo_group, RepoGroup):
816 new_parent_path = os.sep.join(repo_group.full_path_splitted)
779 new_parent_path = os.sep.join(repo_group.full_path_splitted)
817 else:
780 else:
818 new_parent_path = repo_group or ''
781 new_parent_path = repo_group or ''
819
782
820 if repo_store_location:
783 if repo_store_location:
821 _paths = [repo_store_location]
784 _paths = [repo_store_location]
822 else:
785 else:
823 _paths = [self.repos_path, new_parent_path, repo_name]
786 _paths = [self.repos_path, new_parent_path, repo_name]
824 # we need to make it str for mercurial
787 # we need to make it str for mercurial
825 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
788 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
826
789
827 # check if this path is not a repository
790 # check if this path is not a repository
828 if is_valid_repo(repo_path, self.repos_path):
791 if is_valid_repo(repo_path, self.repos_path):
829 raise Exception('This path %s is a valid repository' % repo_path)
792 raise Exception('This path %s is a valid repository' % repo_path)
830
793
831 # check if this path is a group
794 # check if this path is a group
832 if is_valid_repo_group(repo_path, self.repos_path):
795 if is_valid_repo_group(repo_path, self.repos_path):
833 raise Exception('This path %s is a valid group' % repo_path)
796 raise Exception('This path %s is a valid group' % repo_path)
834
797
835 log.info('creating repo %s in %s from url: `%s`',
798 log.info('creating repo %s in %s from url: `%s`',
836 repo_name, safe_unicode(repo_path),
799 repo_name, safe_unicode(repo_path),
837 obfuscate_url_pw(clone_uri))
800 obfuscate_url_pw(clone_uri))
838
801
839 backend = get_backend(repo_type)
802 backend = get_backend(repo_type)
840
803
841 config_repo = None if use_global_config else repo_name
804 config_repo = None if use_global_config else repo_name
842 if config_repo and new_parent_path:
805 if config_repo and new_parent_path:
843 config_repo = Repository.NAME_SEP.join(
806 config_repo = Repository.NAME_SEP.join(
844 (new_parent_path, config_repo))
807 (new_parent_path, config_repo))
845 config = make_db_config(clear_session=False, repo=config_repo)
808 config = make_db_config(clear_session=False, repo=config_repo)
846 config.set('extensions', 'largefiles', '')
809 config.set('extensions', 'largefiles', '')
847
810
848 # patch and reset hooks section of UI config to not run any
811 # patch and reset hooks section of UI config to not run any
849 # hooks on creating remote repo
812 # hooks on creating remote repo
850 config.clear_section('hooks')
813 config.clear_section('hooks')
851
814
852 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
815 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
853 if repo_type == 'git':
816 if repo_type == 'git':
854 repo = backend(
817 repo = backend(
855 repo_path, config=config, create=True, src_url=clone_uri,
818 repo_path, config=config, create=True, src_url=clone_uri,
856 bare=True)
819 bare=True)
857 else:
820 else:
858 repo = backend(
821 repo = backend(
859 repo_path, config=config, create=True, src_url=clone_uri)
822 repo_path, config=config, create=True, src_url=clone_uri)
860
823
861 ScmModel().install_hooks(repo, repo_type=repo_type)
824 ScmModel().install_hooks(repo, repo_type=repo_type)
862
825
863 log.debug('Created repo %s with %s backend',
826 log.debug('Created repo %s with %s backend',
864 safe_unicode(repo_name), safe_unicode(repo_type))
827 safe_unicode(repo_name), safe_unicode(repo_type))
865 return repo
828 return repo
866
829
867 def _rename_filesystem_repo(self, old, new):
830 def _rename_filesystem_repo(self, old, new):
868 """
831 """
869 renames repository on filesystem
832 renames repository on filesystem
870
833
871 :param old: old name
834 :param old: old name
872 :param new: new name
835 :param new: new name
873 """
836 """
874 log.info('renaming repo from %s to %s', old, new)
837 log.info('renaming repo from %s to %s', old, new)
875
838
876 old_path = os.path.join(self.repos_path, old)
839 old_path = os.path.join(self.repos_path, old)
877 new_path = os.path.join(self.repos_path, new)
840 new_path = os.path.join(self.repos_path, new)
878 if os.path.isdir(new_path):
841 if os.path.isdir(new_path):
879 raise Exception(
842 raise Exception(
880 'Was trying to rename to already existing dir %s' % new_path
843 'Was trying to rename to already existing dir %s' % new_path
881 )
844 )
882 shutil.move(old_path, new_path)
845 shutil.move(old_path, new_path)
883
846
884 def _delete_filesystem_repo(self, repo):
847 def _delete_filesystem_repo(self, repo):
885 """
848 """
886 removes repo from filesystem, the removal is acctually made by
849 removes repo from filesystem, the removal is acctually made by
887 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
850 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
888 repository is no longer valid for rhodecode, can be undeleted later on
851 repository is no longer valid for rhodecode, can be undeleted later on
889 by reverting the renames on this repository
852 by reverting the renames on this repository
890
853
891 :param repo: repo object
854 :param repo: repo object
892 """
855 """
893 rm_path = os.path.join(self.repos_path, repo.repo_name)
856 rm_path = os.path.join(self.repos_path, repo.repo_name)
894 repo_group = repo.group
857 repo_group = repo.group
895 log.info("Removing repository %s", rm_path)
858 log.info("Removing repository %s", rm_path)
896 # disable hg/git internal that it doesn't get detected as repo
859 # disable hg/git internal that it doesn't get detected as repo
897 alias = repo.repo_type
860 alias = repo.repo_type
898
861
899 config = make_db_config(clear_session=False)
862 config = make_db_config(clear_session=False)
900 config.set('extensions', 'largefiles', '')
863 config.set('extensions', 'largefiles', '')
901 bare = getattr(repo.scm_instance(config=config), 'bare', False)
864 bare = getattr(repo.scm_instance(config=config), 'bare', False)
902
865
903 # skip this for bare git repos
866 # skip this for bare git repos
904 if not bare:
867 if not bare:
905 # disable VCS repo
868 # disable VCS repo
906 vcs_path = os.path.join(rm_path, '.%s' % alias)
869 vcs_path = os.path.join(rm_path, '.%s' % alias)
907 if os.path.exists(vcs_path):
870 if os.path.exists(vcs_path):
908 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
871 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
909
872
910 _now = datetime.now()
873 _now = datetime.now()
911 _ms = str(_now.microsecond).rjust(6, '0')
874 _ms = str(_now.microsecond).rjust(6, '0')
912 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
875 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
913 repo.just_name)
876 repo.just_name)
914 if repo_group:
877 if repo_group:
915 # if repository is in group, prefix the removal path with the group
878 # if repository is in group, prefix the removal path with the group
916 args = repo_group.full_path_splitted + [_d]
879 args = repo_group.full_path_splitted + [_d]
917 _d = os.path.join(*args)
880 _d = os.path.join(*args)
918
881
919 if os.path.isdir(rm_path):
882 if os.path.isdir(rm_path):
920 shutil.move(rm_path, os.path.join(self.repos_path, _d))
883 shutil.move(rm_path, os.path.join(self.repos_path, _d))
921
884
922
885
923 class ReadmeFinder:
886 class ReadmeFinder:
924 """
887 """
925 Utility which knows how to find a readme for a specific commit.
888 Utility which knows how to find a readme for a specific commit.
926
889
927 The main idea is that this is a configurable algorithm. When creating an
890 The main idea is that this is a configurable algorithm. When creating an
928 instance you can define parameters, currently only the `default_renderer`.
891 instance you can define parameters, currently only the `default_renderer`.
929 Based on this configuration the method :meth:`search` behaves slightly
892 Based on this configuration the method :meth:`search` behaves slightly
930 different.
893 different.
931 """
894 """
932
895
933 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
896 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
934 path_re = re.compile(r'^docs?', re.IGNORECASE)
897 path_re = re.compile(r'^docs?', re.IGNORECASE)
935
898
936 default_priorities = {
899 default_priorities = {
937 None: 0,
900 None: 0,
938 '.text': 2,
901 '.text': 2,
939 '.txt': 3,
902 '.txt': 3,
940 '.rst': 1,
903 '.rst': 1,
941 '.rest': 2,
904 '.rest': 2,
942 '.md': 1,
905 '.md': 1,
943 '.mkdn': 2,
906 '.mkdn': 2,
944 '.mdown': 3,
907 '.mdown': 3,
945 '.markdown': 4,
908 '.markdown': 4,
946 }
909 }
947
910
948 path_priority = {
911 path_priority = {
949 'doc': 0,
912 'doc': 0,
950 'docs': 1,
913 'docs': 1,
951 }
914 }
952
915
953 FALLBACK_PRIORITY = 99
916 FALLBACK_PRIORITY = 99
954
917
955 RENDERER_TO_EXTENSION = {
918 RENDERER_TO_EXTENSION = {
956 'rst': ['.rst', '.rest'],
919 'rst': ['.rst', '.rest'],
957 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
920 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
958 }
921 }
959
922
960 def __init__(self, default_renderer=None):
923 def __init__(self, default_renderer=None):
961 self._default_renderer = default_renderer
924 self._default_renderer = default_renderer
962 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
925 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
963 default_renderer, [])
926 default_renderer, [])
964
927
965 def search(self, commit, path='/'):
928 def search(self, commit, path='/'):
966 """
929 """
967 Find a readme in the given `commit`.
930 Find a readme in the given `commit`.
968 """
931 """
969 nodes = commit.get_nodes(path)
932 nodes = commit.get_nodes(path)
970 matches = self._match_readmes(nodes)
933 matches = self._match_readmes(nodes)
971 matches = self._sort_according_to_priority(matches)
934 matches = self._sort_according_to_priority(matches)
972 if matches:
935 if matches:
973 return matches[0].node
936 return matches[0].node
974
937
975 paths = self._match_paths(nodes)
938 paths = self._match_paths(nodes)
976 paths = self._sort_paths_according_to_priority(paths)
939 paths = self._sort_paths_according_to_priority(paths)
977 for path in paths:
940 for path in paths:
978 match = self.search(commit, path=path)
941 match = self.search(commit, path=path)
979 if match:
942 if match:
980 return match
943 return match
981
944
982 return None
945 return None
983
946
984 def _match_readmes(self, nodes):
947 def _match_readmes(self, nodes):
985 for node in nodes:
948 for node in nodes:
986 if not node.is_file():
949 if not node.is_file():
987 continue
950 continue
988 path = node.path.rsplit('/', 1)[-1]
951 path = node.path.rsplit('/', 1)[-1]
989 match = self.readme_re.match(path)
952 match = self.readme_re.match(path)
990 if match:
953 if match:
991 extension = match.group(1)
954 extension = match.group(1)
992 yield ReadmeMatch(node, match, self._priority(extension))
955 yield ReadmeMatch(node, match, self._priority(extension))
993
956
994 def _match_paths(self, nodes):
957 def _match_paths(self, nodes):
995 for node in nodes:
958 for node in nodes:
996 if not node.is_dir():
959 if not node.is_dir():
997 continue
960 continue
998 match = self.path_re.match(node.path)
961 match = self.path_re.match(node.path)
999 if match:
962 if match:
1000 yield node.path
963 yield node.path
1001
964
1002 def _priority(self, extension):
965 def _priority(self, extension):
1003 renderer_priority = (
966 renderer_priority = (
1004 0 if extension in self._renderer_extensions else 1)
967 0 if extension in self._renderer_extensions else 1)
1005 extension_priority = self.default_priorities.get(
968 extension_priority = self.default_priorities.get(
1006 extension, self.FALLBACK_PRIORITY)
969 extension, self.FALLBACK_PRIORITY)
1007 return (renderer_priority, extension_priority)
970 return (renderer_priority, extension_priority)
1008
971
1009 def _sort_according_to_priority(self, matches):
972 def _sort_according_to_priority(self, matches):
1010
973
1011 def priority_and_path(match):
974 def priority_and_path(match):
1012 return (match.priority, match.path)
975 return (match.priority, match.path)
1013
976
1014 return sorted(matches, key=priority_and_path)
977 return sorted(matches, key=priority_and_path)
1015
978
1016 def _sort_paths_according_to_priority(self, paths):
979 def _sort_paths_according_to_priority(self, paths):
1017
980
1018 def priority_and_path(path):
981 def priority_and_path(path):
1019 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
982 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
1020
983
1021 return sorted(paths, key=priority_and_path)
984 return sorted(paths, key=priority_and_path)
1022
985
1023
986
1024 class ReadmeMatch:
987 class ReadmeMatch:
1025
988
1026 def __init__(self, node, match, priority):
989 def __init__(self, node, match, priority):
1027 self.node = node
990 self.node = node
1028 self._match = match
991 self._match = match
1029 self.priority = priority
992 self.priority = priority
1030
993
1031 @property
994 @property
1032 def path(self):
995 def path(self):
1033 return self.node.path
996 return self.node.path
1034
997
1035 def __repr__(self):
998 def __repr__(self):
1036 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
999 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
@@ -1,861 +1,897 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 users model for RhodeCode
22 users model for RhodeCode
23 """
23 """
24
24
25 import logging
25 import logging
26 import traceback
26 import traceback
27
27
28 import datetime
28 import datetime
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30
30
31 import ipaddress
31 import ipaddress
32 from sqlalchemy.exc import DatabaseError
32 from sqlalchemy.exc import DatabaseError
33 from sqlalchemy.sql.expression import true, false
33 from sqlalchemy.sql.expression import true, false
34
34
35 from rhodecode import events
35 from rhodecode import events
36 from rhodecode.lib.user_log_filter import user_log_filter
36 from rhodecode.lib.user_log_filter import user_log_filter
37 from rhodecode.lib.utils2 import (
37 from rhodecode.lib.utils2 import (
38 safe_unicode, get_current_rhodecode_user, action_logger_generic,
38 safe_unicode, get_current_rhodecode_user, action_logger_generic,
39 AttributeDict, str2bool)
39 AttributeDict, str2bool)
40 from rhodecode.lib.caching_query import FromCache
40 from rhodecode.lib.caching_query import FromCache
41 from rhodecode.model import BaseModel
41 from rhodecode.model import BaseModel
42 from rhodecode.model.auth_token import AuthTokenModel
42 from rhodecode.model.auth_token import AuthTokenModel
43 from rhodecode.model.db import (
43 from rhodecode.model.db import (
44 or_, joinedload, User, UserToPerm, UserEmailMap, UserIpMap, UserLog)
44 or_, joinedload, User, UserToPerm, UserEmailMap, UserIpMap, UserLog)
45 from rhodecode.lib.exceptions import (
45 from rhodecode.lib.exceptions import (
46 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
46 DefaultUserException, UserOwnsReposException, UserOwnsRepoGroupsException,
47 UserOwnsUserGroupsException, NotAllowedToCreateUserError)
47 UserOwnsUserGroupsException, NotAllowedToCreateUserError)
48 from rhodecode.model.meta import Session
48 from rhodecode.model.meta import Session
49 from rhodecode.model.repo_group import RepoGroupModel
49 from rhodecode.model.repo_group import RepoGroupModel
50
50
51
51
52 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
53
53
54
54
55 class UserModel(BaseModel):
55 class UserModel(BaseModel):
56 cls = User
56 cls = User
57
57
58 def get(self, user_id, cache=False):
58 def get(self, user_id, cache=False):
59 user = self.sa.query(User)
59 user = self.sa.query(User)
60 if cache:
60 if cache:
61 user = user.options(FromCache("sql_cache_short",
61 user = user.options(FromCache("sql_cache_short",
62 "get_user_%s" % user_id))
62 "get_user_%s" % user_id))
63 return user.get(user_id)
63 return user.get(user_id)
64
64
65 def get_user(self, user):
65 def get_user(self, user):
66 return self._get_user(user)
66 return self._get_user(user)
67
67
68 def get_users(self, name_contains=None, limit=20, only_active=True):
69 import rhodecode.lib.helpers as h
70
71 query = self.sa.query(User)
72 if only_active:
73 query = query.filter(User.active == true())
74
75 if name_contains:
76 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
77 query = query.filter(
78 or_(
79 User.name.ilike(ilike_expression),
80 User.lastname.ilike(ilike_expression),
81 User.username.ilike(ilike_expression)
82 )
83 )
84 query = query.limit(limit)
85 users = query.all()
86
87 _users = [
88 {
89 'id': user.user_id,
90 'first_name': user.name,
91 'last_name': user.lastname,
92 'username': user.username,
93 'email': user.email,
94 'icon_link': h.gravatar_url(user.email, 30),
95 'value_display': h.person(user),
96 'value': user.username,
97 'value_type': 'user',
98 'active': user.active,
99 }
100 for user in users
101 ]
102 return _users
103
68 def get_by_username(self, username, cache=False, case_insensitive=False):
104 def get_by_username(self, username, cache=False, case_insensitive=False):
69
105
70 if case_insensitive:
106 if case_insensitive:
71 user = self.sa.query(User).filter(User.username.ilike(username))
107 user = self.sa.query(User).filter(User.username.ilike(username))
72 else:
108 else:
73 user = self.sa.query(User)\
109 user = self.sa.query(User)\
74 .filter(User.username == username)
110 .filter(User.username == username)
75 if cache:
111 if cache:
76 user = user.options(FromCache("sql_cache_short",
112 user = user.options(FromCache("sql_cache_short",
77 "get_user_%s" % username))
113 "get_user_%s" % username))
78 return user.scalar()
114 return user.scalar()
79
115
80 def get_by_email(self, email, cache=False, case_insensitive=False):
116 def get_by_email(self, email, cache=False, case_insensitive=False):
81 return User.get_by_email(email, case_insensitive, cache)
117 return User.get_by_email(email, case_insensitive, cache)
82
118
83 def get_by_auth_token(self, auth_token, cache=False):
119 def get_by_auth_token(self, auth_token, cache=False):
84 return User.get_by_auth_token(auth_token, cache)
120 return User.get_by_auth_token(auth_token, cache)
85
121
86 def get_active_user_count(self, cache=False):
122 def get_active_user_count(self, cache=False):
87 return User.query().filter(
123 return User.query().filter(
88 User.active == True).filter(
124 User.active == True).filter(
89 User.username != User.DEFAULT_USER).count()
125 User.username != User.DEFAULT_USER).count()
90
126
91 def create(self, form_data, cur_user=None):
127 def create(self, form_data, cur_user=None):
92 if not cur_user:
128 if not cur_user:
93 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
129 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
94
130
95 user_data = {
131 user_data = {
96 'username': form_data['username'],
132 'username': form_data['username'],
97 'password': form_data['password'],
133 'password': form_data['password'],
98 'email': form_data['email'],
134 'email': form_data['email'],
99 'firstname': form_data['firstname'],
135 'firstname': form_data['firstname'],
100 'lastname': form_data['lastname'],
136 'lastname': form_data['lastname'],
101 'active': form_data['active'],
137 'active': form_data['active'],
102 'extern_type': form_data['extern_type'],
138 'extern_type': form_data['extern_type'],
103 'extern_name': form_data['extern_name'],
139 'extern_name': form_data['extern_name'],
104 'admin': False,
140 'admin': False,
105 'cur_user': cur_user
141 'cur_user': cur_user
106 }
142 }
107
143
108 if 'create_repo_group' in form_data:
144 if 'create_repo_group' in form_data:
109 user_data['create_repo_group'] = str2bool(
145 user_data['create_repo_group'] = str2bool(
110 form_data.get('create_repo_group'))
146 form_data.get('create_repo_group'))
111
147
112 try:
148 try:
113 if form_data.get('password_change'):
149 if form_data.get('password_change'):
114 user_data['force_password_change'] = True
150 user_data['force_password_change'] = True
115 return UserModel().create_or_update(**user_data)
151 return UserModel().create_or_update(**user_data)
116 except Exception:
152 except Exception:
117 log.error(traceback.format_exc())
153 log.error(traceback.format_exc())
118 raise
154 raise
119
155
120 def update_user(self, user, skip_attrs=None, **kwargs):
156 def update_user(self, user, skip_attrs=None, **kwargs):
121 from rhodecode.lib.auth import get_crypt_password
157 from rhodecode.lib.auth import get_crypt_password
122
158
123 user = self._get_user(user)
159 user = self._get_user(user)
124 if user.username == User.DEFAULT_USER:
160 if user.username == User.DEFAULT_USER:
125 raise DefaultUserException(
161 raise DefaultUserException(
126 _("You can't Edit this user since it's"
162 _("You can't Edit this user since it's"
127 " crucial for entire application"))
163 " crucial for entire application"))
128
164
129 # first store only defaults
165 # first store only defaults
130 user_attrs = {
166 user_attrs = {
131 'updating_user_id': user.user_id,
167 'updating_user_id': user.user_id,
132 'username': user.username,
168 'username': user.username,
133 'password': user.password,
169 'password': user.password,
134 'email': user.email,
170 'email': user.email,
135 'firstname': user.name,
171 'firstname': user.name,
136 'lastname': user.lastname,
172 'lastname': user.lastname,
137 'active': user.active,
173 'active': user.active,
138 'admin': user.admin,
174 'admin': user.admin,
139 'extern_name': user.extern_name,
175 'extern_name': user.extern_name,
140 'extern_type': user.extern_type,
176 'extern_type': user.extern_type,
141 'language': user.user_data.get('language')
177 'language': user.user_data.get('language')
142 }
178 }
143
179
144 # in case there's new_password, that comes from form, use it to
180 # in case there's new_password, that comes from form, use it to
145 # store password
181 # store password
146 if kwargs.get('new_password'):
182 if kwargs.get('new_password'):
147 kwargs['password'] = kwargs['new_password']
183 kwargs['password'] = kwargs['new_password']
148
184
149 # cleanups, my_account password change form
185 # cleanups, my_account password change form
150 kwargs.pop('current_password', None)
186 kwargs.pop('current_password', None)
151 kwargs.pop('new_password', None)
187 kwargs.pop('new_password', None)
152
188
153 # cleanups, user edit password change form
189 # cleanups, user edit password change form
154 kwargs.pop('password_confirmation', None)
190 kwargs.pop('password_confirmation', None)
155 kwargs.pop('password_change', None)
191 kwargs.pop('password_change', None)
156
192
157 # create repo group on user creation
193 # create repo group on user creation
158 kwargs.pop('create_repo_group', None)
194 kwargs.pop('create_repo_group', None)
159
195
160 # legacy forms send name, which is the firstname
196 # legacy forms send name, which is the firstname
161 firstname = kwargs.pop('name', None)
197 firstname = kwargs.pop('name', None)
162 if firstname:
198 if firstname:
163 kwargs['firstname'] = firstname
199 kwargs['firstname'] = firstname
164
200
165 for k, v in kwargs.items():
201 for k, v in kwargs.items():
166 # skip if we don't want to update this
202 # skip if we don't want to update this
167 if skip_attrs and k in skip_attrs:
203 if skip_attrs and k in skip_attrs:
168 continue
204 continue
169
205
170 user_attrs[k] = v
206 user_attrs[k] = v
171
207
172 try:
208 try:
173 return self.create_or_update(**user_attrs)
209 return self.create_or_update(**user_attrs)
174 except Exception:
210 except Exception:
175 log.error(traceback.format_exc())
211 log.error(traceback.format_exc())
176 raise
212 raise
177
213
178 def create_or_update(
214 def create_or_update(
179 self, username, password, email, firstname='', lastname='',
215 self, username, password, email, firstname='', lastname='',
180 active=True, admin=False, extern_type=None, extern_name=None,
216 active=True, admin=False, extern_type=None, extern_name=None,
181 cur_user=None, plugin=None, force_password_change=False,
217 cur_user=None, plugin=None, force_password_change=False,
182 allow_to_create_user=True, create_repo_group=None,
218 allow_to_create_user=True, create_repo_group=None,
183 updating_user_id=None, language=None, strict_creation_check=True):
219 updating_user_id=None, language=None, strict_creation_check=True):
184 """
220 """
185 Creates a new instance if not found, or updates current one
221 Creates a new instance if not found, or updates current one
186
222
187 :param username:
223 :param username:
188 :param password:
224 :param password:
189 :param email:
225 :param email:
190 :param firstname:
226 :param firstname:
191 :param lastname:
227 :param lastname:
192 :param active:
228 :param active:
193 :param admin:
229 :param admin:
194 :param extern_type:
230 :param extern_type:
195 :param extern_name:
231 :param extern_name:
196 :param cur_user:
232 :param cur_user:
197 :param plugin: optional plugin this method was called from
233 :param plugin: optional plugin this method was called from
198 :param force_password_change: toggles new or existing user flag
234 :param force_password_change: toggles new or existing user flag
199 for password change
235 for password change
200 :param allow_to_create_user: Defines if the method can actually create
236 :param allow_to_create_user: Defines if the method can actually create
201 new users
237 new users
202 :param create_repo_group: Defines if the method should also
238 :param create_repo_group: Defines if the method should also
203 create an repo group with user name, and owner
239 create an repo group with user name, and owner
204 :param updating_user_id: if we set it up this is the user we want to
240 :param updating_user_id: if we set it up this is the user we want to
205 update this allows to editing username.
241 update this allows to editing username.
206 :param language: language of user from interface.
242 :param language: language of user from interface.
207
243
208 :returns: new User object with injected `is_new_user` attribute.
244 :returns: new User object with injected `is_new_user` attribute.
209 """
245 """
210 if not cur_user:
246 if not cur_user:
211 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
247 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
212
248
213 from rhodecode.lib.auth import (
249 from rhodecode.lib.auth import (
214 get_crypt_password, check_password, generate_auth_token)
250 get_crypt_password, check_password, generate_auth_token)
215 from rhodecode.lib.hooks_base import (
251 from rhodecode.lib.hooks_base import (
216 log_create_user, check_allowed_create_user)
252 log_create_user, check_allowed_create_user)
217
253
218 def _password_change(new_user, password):
254 def _password_change(new_user, password):
219 # empty password
255 # empty password
220 if not new_user.password:
256 if not new_user.password:
221 return False
257 return False
222
258
223 # password check is only needed for RhodeCode internal auth calls
259 # password check is only needed for RhodeCode internal auth calls
224 # in case it's a plugin we don't care
260 # in case it's a plugin we don't care
225 if not plugin:
261 if not plugin:
226
262
227 # first check if we gave crypted password back, and if it
263 # first check if we gave crypted password back, and if it
228 # matches it's not password change
264 # matches it's not password change
229 if new_user.password == password:
265 if new_user.password == password:
230 return False
266 return False
231
267
232 password_match = check_password(password, new_user.password)
268 password_match = check_password(password, new_user.password)
233 if not password_match:
269 if not password_match:
234 return True
270 return True
235
271
236 return False
272 return False
237
273
238 # read settings on default personal repo group creation
274 # read settings on default personal repo group creation
239 if create_repo_group is None:
275 if create_repo_group is None:
240 default_create_repo_group = RepoGroupModel()\
276 default_create_repo_group = RepoGroupModel()\
241 .get_default_create_personal_repo_group()
277 .get_default_create_personal_repo_group()
242 create_repo_group = default_create_repo_group
278 create_repo_group = default_create_repo_group
243
279
244 user_data = {
280 user_data = {
245 'username': username,
281 'username': username,
246 'password': password,
282 'password': password,
247 'email': email,
283 'email': email,
248 'firstname': firstname,
284 'firstname': firstname,
249 'lastname': lastname,
285 'lastname': lastname,
250 'active': active,
286 'active': active,
251 'admin': admin
287 'admin': admin
252 }
288 }
253
289
254 if updating_user_id:
290 if updating_user_id:
255 log.debug('Checking for existing account in RhodeCode '
291 log.debug('Checking for existing account in RhodeCode '
256 'database with user_id `%s` ' % (updating_user_id,))
292 'database with user_id `%s` ' % (updating_user_id,))
257 user = User.get(updating_user_id)
293 user = User.get(updating_user_id)
258 else:
294 else:
259 log.debug('Checking for existing account in RhodeCode '
295 log.debug('Checking for existing account in RhodeCode '
260 'database with username `%s` ' % (username,))
296 'database with username `%s` ' % (username,))
261 user = User.get_by_username(username, case_insensitive=True)
297 user = User.get_by_username(username, case_insensitive=True)
262
298
263 if user is None:
299 if user is None:
264 # we check internal flag if this method is actually allowed to
300 # we check internal flag if this method is actually allowed to
265 # create new user
301 # create new user
266 if not allow_to_create_user:
302 if not allow_to_create_user:
267 msg = ('Method wants to create new user, but it is not '
303 msg = ('Method wants to create new user, but it is not '
268 'allowed to do so')
304 'allowed to do so')
269 log.warning(msg)
305 log.warning(msg)
270 raise NotAllowedToCreateUserError(msg)
306 raise NotAllowedToCreateUserError(msg)
271
307
272 log.debug('Creating new user %s', username)
308 log.debug('Creating new user %s', username)
273
309
274 # only if we create user that is active
310 # only if we create user that is active
275 new_active_user = active
311 new_active_user = active
276 if new_active_user and strict_creation_check:
312 if new_active_user and strict_creation_check:
277 # raises UserCreationError if it's not allowed for any reason to
313 # raises UserCreationError if it's not allowed for any reason to
278 # create new active user, this also executes pre-create hooks
314 # create new active user, this also executes pre-create hooks
279 check_allowed_create_user(user_data, cur_user, strict_check=True)
315 check_allowed_create_user(user_data, cur_user, strict_check=True)
280 events.trigger(events.UserPreCreate(user_data))
316 events.trigger(events.UserPreCreate(user_data))
281 new_user = User()
317 new_user = User()
282 edit = False
318 edit = False
283 else:
319 else:
284 log.debug('updating user %s', username)
320 log.debug('updating user %s', username)
285 events.trigger(events.UserPreUpdate(user, user_data))
321 events.trigger(events.UserPreUpdate(user, user_data))
286 new_user = user
322 new_user = user
287 edit = True
323 edit = True
288
324
289 # we're not allowed to edit default user
325 # we're not allowed to edit default user
290 if user.username == User.DEFAULT_USER:
326 if user.username == User.DEFAULT_USER:
291 raise DefaultUserException(
327 raise DefaultUserException(
292 _("You can't edit this user (`%(username)s`) since it's "
328 _("You can't edit this user (`%(username)s`) since it's "
293 "crucial for entire application") % {'username': user.username})
329 "crucial for entire application") % {'username': user.username})
294
330
295 # inject special attribute that will tell us if User is new or old
331 # inject special attribute that will tell us if User is new or old
296 new_user.is_new_user = not edit
332 new_user.is_new_user = not edit
297 # for users that didn's specify auth type, we use RhodeCode built in
333 # for users that didn's specify auth type, we use RhodeCode built in
298 from rhodecode.authentication.plugins import auth_rhodecode
334 from rhodecode.authentication.plugins import auth_rhodecode
299 extern_name = extern_name or auth_rhodecode.RhodeCodeAuthPlugin.name
335 extern_name = extern_name or auth_rhodecode.RhodeCodeAuthPlugin.name
300 extern_type = extern_type or auth_rhodecode.RhodeCodeAuthPlugin.name
336 extern_type = extern_type or auth_rhodecode.RhodeCodeAuthPlugin.name
301
337
302 try:
338 try:
303 new_user.username = username
339 new_user.username = username
304 new_user.admin = admin
340 new_user.admin = admin
305 new_user.email = email
341 new_user.email = email
306 new_user.active = active
342 new_user.active = active
307 new_user.extern_name = safe_unicode(extern_name)
343 new_user.extern_name = safe_unicode(extern_name)
308 new_user.extern_type = safe_unicode(extern_type)
344 new_user.extern_type = safe_unicode(extern_type)
309 new_user.name = firstname
345 new_user.name = firstname
310 new_user.lastname = lastname
346 new_user.lastname = lastname
311
347
312 # set password only if creating an user or password is changed
348 # set password only if creating an user or password is changed
313 if not edit or _password_change(new_user, password):
349 if not edit or _password_change(new_user, password):
314 reason = 'new password' if edit else 'new user'
350 reason = 'new password' if edit else 'new user'
315 log.debug('Updating password reason=>%s', reason)
351 log.debug('Updating password reason=>%s', reason)
316 new_user.password = get_crypt_password(password) if password else None
352 new_user.password = get_crypt_password(password) if password else None
317
353
318 if force_password_change:
354 if force_password_change:
319 new_user.update_userdata(force_password_change=True)
355 new_user.update_userdata(force_password_change=True)
320 if language:
356 if language:
321 new_user.update_userdata(language=language)
357 new_user.update_userdata(language=language)
322 new_user.update_userdata(notification_status=True)
358 new_user.update_userdata(notification_status=True)
323
359
324 self.sa.add(new_user)
360 self.sa.add(new_user)
325
361
326 if not edit and create_repo_group:
362 if not edit and create_repo_group:
327 RepoGroupModel().create_personal_repo_group(
363 RepoGroupModel().create_personal_repo_group(
328 new_user, commit_early=False)
364 new_user, commit_early=False)
329
365
330 if not edit:
366 if not edit:
331 # add the RSS token
367 # add the RSS token
332 AuthTokenModel().create(username,
368 AuthTokenModel().create(username,
333 description='Generated feed token',
369 description='Generated feed token',
334 role=AuthTokenModel.cls.ROLE_FEED)
370 role=AuthTokenModel.cls.ROLE_FEED)
335 log_create_user(created_by=cur_user, **new_user.get_dict())
371 log_create_user(created_by=cur_user, **new_user.get_dict())
336 events.trigger(events.UserPostCreate(user_data))
372 events.trigger(events.UserPostCreate(user_data))
337 return new_user
373 return new_user
338 except (DatabaseError,):
374 except (DatabaseError,):
339 log.error(traceback.format_exc())
375 log.error(traceback.format_exc())
340 raise
376 raise
341
377
342 def create_registration(self, form_data):
378 def create_registration(self, form_data):
343 from rhodecode.model.notification import NotificationModel
379 from rhodecode.model.notification import NotificationModel
344 from rhodecode.model.notification import EmailNotificationModel
380 from rhodecode.model.notification import EmailNotificationModel
345
381
346 try:
382 try:
347 form_data['admin'] = False
383 form_data['admin'] = False
348 form_data['extern_name'] = 'rhodecode'
384 form_data['extern_name'] = 'rhodecode'
349 form_data['extern_type'] = 'rhodecode'
385 form_data['extern_type'] = 'rhodecode'
350 new_user = self.create(form_data)
386 new_user = self.create(form_data)
351
387
352 self.sa.add(new_user)
388 self.sa.add(new_user)
353 self.sa.flush()
389 self.sa.flush()
354
390
355 user_data = new_user.get_dict()
391 user_data = new_user.get_dict()
356 kwargs = {
392 kwargs = {
357 # use SQLALCHEMY safe dump of user data
393 # use SQLALCHEMY safe dump of user data
358 'user': AttributeDict(user_data),
394 'user': AttributeDict(user_data),
359 'date': datetime.datetime.now()
395 'date': datetime.datetime.now()
360 }
396 }
361 notification_type = EmailNotificationModel.TYPE_REGISTRATION
397 notification_type = EmailNotificationModel.TYPE_REGISTRATION
362 # pre-generate the subject for notification itself
398 # pre-generate the subject for notification itself
363 (subject,
399 (subject,
364 _h, _e, # we don't care about those
400 _h, _e, # we don't care about those
365 body_plaintext) = EmailNotificationModel().render_email(
401 body_plaintext) = EmailNotificationModel().render_email(
366 notification_type, **kwargs)
402 notification_type, **kwargs)
367
403
368 # create notification objects, and emails
404 # create notification objects, and emails
369 NotificationModel().create(
405 NotificationModel().create(
370 created_by=new_user,
406 created_by=new_user,
371 notification_subject=subject,
407 notification_subject=subject,
372 notification_body=body_plaintext,
408 notification_body=body_plaintext,
373 notification_type=notification_type,
409 notification_type=notification_type,
374 recipients=None, # all admins
410 recipients=None, # all admins
375 email_kwargs=kwargs,
411 email_kwargs=kwargs,
376 )
412 )
377
413
378 return new_user
414 return new_user
379 except Exception:
415 except Exception:
380 log.error(traceback.format_exc())
416 log.error(traceback.format_exc())
381 raise
417 raise
382
418
383 def _handle_user_repos(self, username, repositories, handle_mode=None):
419 def _handle_user_repos(self, username, repositories, handle_mode=None):
384 _superadmin = self.cls.get_first_super_admin()
420 _superadmin = self.cls.get_first_super_admin()
385 left_overs = True
421 left_overs = True
386
422
387 from rhodecode.model.repo import RepoModel
423 from rhodecode.model.repo import RepoModel
388
424
389 if handle_mode == 'detach':
425 if handle_mode == 'detach':
390 for obj in repositories:
426 for obj in repositories:
391 obj.user = _superadmin
427 obj.user = _superadmin
392 # set description we know why we super admin now owns
428 # set description we know why we super admin now owns
393 # additional repositories that were orphaned !
429 # additional repositories that were orphaned !
394 obj.description += ' \n::detached repository from deleted user: %s' % (username,)
430 obj.description += ' \n::detached repository from deleted user: %s' % (username,)
395 self.sa.add(obj)
431 self.sa.add(obj)
396 left_overs = False
432 left_overs = False
397 elif handle_mode == 'delete':
433 elif handle_mode == 'delete':
398 for obj in repositories:
434 for obj in repositories:
399 RepoModel().delete(obj, forks='detach')
435 RepoModel().delete(obj, forks='detach')
400 left_overs = False
436 left_overs = False
401
437
402 # if nothing is done we have left overs left
438 # if nothing is done we have left overs left
403 return left_overs
439 return left_overs
404
440
405 def _handle_user_repo_groups(self, username, repository_groups,
441 def _handle_user_repo_groups(self, username, repository_groups,
406 handle_mode=None):
442 handle_mode=None):
407 _superadmin = self.cls.get_first_super_admin()
443 _superadmin = self.cls.get_first_super_admin()
408 left_overs = True
444 left_overs = True
409
445
410 from rhodecode.model.repo_group import RepoGroupModel
446 from rhodecode.model.repo_group import RepoGroupModel
411
447
412 if handle_mode == 'detach':
448 if handle_mode == 'detach':
413 for r in repository_groups:
449 for r in repository_groups:
414 r.user = _superadmin
450 r.user = _superadmin
415 # set description we know why we super admin now owns
451 # set description we know why we super admin now owns
416 # additional repositories that were orphaned !
452 # additional repositories that were orphaned !
417 r.group_description += ' \n::detached repository group from deleted user: %s' % (username,)
453 r.group_description += ' \n::detached repository group from deleted user: %s' % (username,)
418 self.sa.add(r)
454 self.sa.add(r)
419 left_overs = False
455 left_overs = False
420 elif handle_mode == 'delete':
456 elif handle_mode == 'delete':
421 for r in repository_groups:
457 for r in repository_groups:
422 RepoGroupModel().delete(r)
458 RepoGroupModel().delete(r)
423 left_overs = False
459 left_overs = False
424
460
425 # if nothing is done we have left overs left
461 # if nothing is done we have left overs left
426 return left_overs
462 return left_overs
427
463
428 def _handle_user_user_groups(self, username, user_groups, handle_mode=None):
464 def _handle_user_user_groups(self, username, user_groups, handle_mode=None):
429 _superadmin = self.cls.get_first_super_admin()
465 _superadmin = self.cls.get_first_super_admin()
430 left_overs = True
466 left_overs = True
431
467
432 from rhodecode.model.user_group import UserGroupModel
468 from rhodecode.model.user_group import UserGroupModel
433
469
434 if handle_mode == 'detach':
470 if handle_mode == 'detach':
435 for r in user_groups:
471 for r in user_groups:
436 for user_user_group_to_perm in r.user_user_group_to_perm:
472 for user_user_group_to_perm in r.user_user_group_to_perm:
437 if user_user_group_to_perm.user.username == username:
473 if user_user_group_to_perm.user.username == username:
438 user_user_group_to_perm.user = _superadmin
474 user_user_group_to_perm.user = _superadmin
439 r.user = _superadmin
475 r.user = _superadmin
440 # set description we know why we super admin now owns
476 # set description we know why we super admin now owns
441 # additional repositories that were orphaned !
477 # additional repositories that were orphaned !
442 r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,)
478 r.user_group_description += ' \n::detached user group from deleted user: %s' % (username,)
443 self.sa.add(r)
479 self.sa.add(r)
444 left_overs = False
480 left_overs = False
445 elif handle_mode == 'delete':
481 elif handle_mode == 'delete':
446 for r in user_groups:
482 for r in user_groups:
447 UserGroupModel().delete(r)
483 UserGroupModel().delete(r)
448 left_overs = False
484 left_overs = False
449
485
450 # if nothing is done we have left overs left
486 # if nothing is done we have left overs left
451 return left_overs
487 return left_overs
452
488
453 def delete(self, user, cur_user=None, handle_repos=None,
489 def delete(self, user, cur_user=None, handle_repos=None,
454 handle_repo_groups=None, handle_user_groups=None):
490 handle_repo_groups=None, handle_user_groups=None):
455 if not cur_user:
491 if not cur_user:
456 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
492 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
457 user = self._get_user(user)
493 user = self._get_user(user)
458
494
459 try:
495 try:
460 if user.username == User.DEFAULT_USER:
496 if user.username == User.DEFAULT_USER:
461 raise DefaultUserException(
497 raise DefaultUserException(
462 _(u"You can't remove this user since it's"
498 _(u"You can't remove this user since it's"
463 u" crucial for entire application"))
499 u" crucial for entire application"))
464
500
465 left_overs = self._handle_user_repos(
501 left_overs = self._handle_user_repos(
466 user.username, user.repositories, handle_repos)
502 user.username, user.repositories, handle_repos)
467 if left_overs and user.repositories:
503 if left_overs and user.repositories:
468 repos = [x.repo_name for x in user.repositories]
504 repos = [x.repo_name for x in user.repositories]
469 raise UserOwnsReposException(
505 raise UserOwnsReposException(
470 _(u'user "%s" still owns %s repositories and cannot be '
506 _(u'user "%s" still owns %s repositories and cannot be '
471 u'removed. Switch owners or remove those repositories:%s')
507 u'removed. Switch owners or remove those repositories:%s')
472 % (user.username, len(repos), ', '.join(repos)))
508 % (user.username, len(repos), ', '.join(repos)))
473
509
474 left_overs = self._handle_user_repo_groups(
510 left_overs = self._handle_user_repo_groups(
475 user.username, user.repository_groups, handle_repo_groups)
511 user.username, user.repository_groups, handle_repo_groups)
476 if left_overs and user.repository_groups:
512 if left_overs and user.repository_groups:
477 repo_groups = [x.group_name for x in user.repository_groups]
513 repo_groups = [x.group_name for x in user.repository_groups]
478 raise UserOwnsRepoGroupsException(
514 raise UserOwnsRepoGroupsException(
479 _(u'user "%s" still owns %s repository groups and cannot be '
515 _(u'user "%s" still owns %s repository groups and cannot be '
480 u'removed. Switch owners or remove those repository groups:%s')
516 u'removed. Switch owners or remove those repository groups:%s')
481 % (user.username, len(repo_groups), ', '.join(repo_groups)))
517 % (user.username, len(repo_groups), ', '.join(repo_groups)))
482
518
483 left_overs = self._handle_user_user_groups(
519 left_overs = self._handle_user_user_groups(
484 user.username, user.user_groups, handle_user_groups)
520 user.username, user.user_groups, handle_user_groups)
485 if left_overs and user.user_groups:
521 if left_overs and user.user_groups:
486 user_groups = [x.users_group_name for x in user.user_groups]
522 user_groups = [x.users_group_name for x in user.user_groups]
487 raise UserOwnsUserGroupsException(
523 raise UserOwnsUserGroupsException(
488 _(u'user "%s" still owns %s user groups and cannot be '
524 _(u'user "%s" still owns %s user groups and cannot be '
489 u'removed. Switch owners or remove those user groups:%s')
525 u'removed. Switch owners or remove those user groups:%s')
490 % (user.username, len(user_groups), ', '.join(user_groups)))
526 % (user.username, len(user_groups), ', '.join(user_groups)))
491
527
492 # we might change the user data with detach/delete, make sure
528 # we might change the user data with detach/delete, make sure
493 # the object is marked as expired before actually deleting !
529 # the object is marked as expired before actually deleting !
494 self.sa.expire(user)
530 self.sa.expire(user)
495 self.sa.delete(user)
531 self.sa.delete(user)
496 from rhodecode.lib.hooks_base import log_delete_user
532 from rhodecode.lib.hooks_base import log_delete_user
497 log_delete_user(deleted_by=cur_user, **user.get_dict())
533 log_delete_user(deleted_by=cur_user, **user.get_dict())
498 except Exception:
534 except Exception:
499 log.error(traceback.format_exc())
535 log.error(traceback.format_exc())
500 raise
536 raise
501
537
502 def reset_password_link(self, data, pwd_reset_url):
538 def reset_password_link(self, data, pwd_reset_url):
503 from rhodecode.lib.celerylib import tasks, run_task
539 from rhodecode.lib.celerylib import tasks, run_task
504 from rhodecode.model.notification import EmailNotificationModel
540 from rhodecode.model.notification import EmailNotificationModel
505 user_email = data['email']
541 user_email = data['email']
506 try:
542 try:
507 user = User.get_by_email(user_email)
543 user = User.get_by_email(user_email)
508 if user:
544 if user:
509 log.debug('password reset user found %s', user)
545 log.debug('password reset user found %s', user)
510
546
511 email_kwargs = {
547 email_kwargs = {
512 'password_reset_url': pwd_reset_url,
548 'password_reset_url': pwd_reset_url,
513 'user': user,
549 'user': user,
514 'email': user_email,
550 'email': user_email,
515 'date': datetime.datetime.now()
551 'date': datetime.datetime.now()
516 }
552 }
517
553
518 (subject, headers, email_body,
554 (subject, headers, email_body,
519 email_body_plaintext) = EmailNotificationModel().render_email(
555 email_body_plaintext) = EmailNotificationModel().render_email(
520 EmailNotificationModel.TYPE_PASSWORD_RESET, **email_kwargs)
556 EmailNotificationModel.TYPE_PASSWORD_RESET, **email_kwargs)
521
557
522 recipients = [user_email]
558 recipients = [user_email]
523
559
524 action_logger_generic(
560 action_logger_generic(
525 'sending password reset email to user: {}'.format(
561 'sending password reset email to user: {}'.format(
526 user), namespace='security.password_reset')
562 user), namespace='security.password_reset')
527
563
528 run_task(tasks.send_email, recipients, subject,
564 run_task(tasks.send_email, recipients, subject,
529 email_body_plaintext, email_body)
565 email_body_plaintext, email_body)
530
566
531 else:
567 else:
532 log.debug("password reset email %s not found", user_email)
568 log.debug("password reset email %s not found", user_email)
533 except Exception:
569 except Exception:
534 log.error(traceback.format_exc())
570 log.error(traceback.format_exc())
535 return False
571 return False
536
572
537 return True
573 return True
538
574
539 def reset_password(self, data):
575 def reset_password(self, data):
540 from rhodecode.lib.celerylib import tasks, run_task
576 from rhodecode.lib.celerylib import tasks, run_task
541 from rhodecode.model.notification import EmailNotificationModel
577 from rhodecode.model.notification import EmailNotificationModel
542 from rhodecode.lib import auth
578 from rhodecode.lib import auth
543 user_email = data['email']
579 user_email = data['email']
544 pre_db = True
580 pre_db = True
545 try:
581 try:
546 user = User.get_by_email(user_email)
582 user = User.get_by_email(user_email)
547 new_passwd = auth.PasswordGenerator().gen_password(
583 new_passwd = auth.PasswordGenerator().gen_password(
548 12, auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
584 12, auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
549 if user:
585 if user:
550 user.password = auth.get_crypt_password(new_passwd)
586 user.password = auth.get_crypt_password(new_passwd)
551 # also force this user to reset his password !
587 # also force this user to reset his password !
552 user.update_userdata(force_password_change=True)
588 user.update_userdata(force_password_change=True)
553
589
554 Session().add(user)
590 Session().add(user)
555
591
556 # now delete the token in question
592 # now delete the token in question
557 UserApiKeys = AuthTokenModel.cls
593 UserApiKeys = AuthTokenModel.cls
558 UserApiKeys().query().filter(
594 UserApiKeys().query().filter(
559 UserApiKeys.api_key == data['token']).delete()
595 UserApiKeys.api_key == data['token']).delete()
560
596
561 Session().commit()
597 Session().commit()
562 log.info('successfully reset password for `%s`', user_email)
598 log.info('successfully reset password for `%s`', user_email)
563
599
564 if new_passwd is None:
600 if new_passwd is None:
565 raise Exception('unable to generate new password')
601 raise Exception('unable to generate new password')
566
602
567 pre_db = False
603 pre_db = False
568
604
569 email_kwargs = {
605 email_kwargs = {
570 'new_password': new_passwd,
606 'new_password': new_passwd,
571 'user': user,
607 'user': user,
572 'email': user_email,
608 'email': user_email,
573 'date': datetime.datetime.now()
609 'date': datetime.datetime.now()
574 }
610 }
575
611
576 (subject, headers, email_body,
612 (subject, headers, email_body,
577 email_body_plaintext) = EmailNotificationModel().render_email(
613 email_body_plaintext) = EmailNotificationModel().render_email(
578 EmailNotificationModel.TYPE_PASSWORD_RESET_CONFIRMATION,
614 EmailNotificationModel.TYPE_PASSWORD_RESET_CONFIRMATION,
579 **email_kwargs)
615 **email_kwargs)
580
616
581 recipients = [user_email]
617 recipients = [user_email]
582
618
583 action_logger_generic(
619 action_logger_generic(
584 'sent new password to user: {} with email: {}'.format(
620 'sent new password to user: {} with email: {}'.format(
585 user, user_email), namespace='security.password_reset')
621 user, user_email), namespace='security.password_reset')
586
622
587 run_task(tasks.send_email, recipients, subject,
623 run_task(tasks.send_email, recipients, subject,
588 email_body_plaintext, email_body)
624 email_body_plaintext, email_body)
589
625
590 except Exception:
626 except Exception:
591 log.error('Failed to update user password')
627 log.error('Failed to update user password')
592 log.error(traceback.format_exc())
628 log.error(traceback.format_exc())
593 if pre_db:
629 if pre_db:
594 # we rollback only if local db stuff fails. If it goes into
630 # we rollback only if local db stuff fails. If it goes into
595 # run_task, we're pass rollback state this wouldn't work then
631 # run_task, we're pass rollback state this wouldn't work then
596 Session().rollback()
632 Session().rollback()
597
633
598 return True
634 return True
599
635
600 def fill_data(self, auth_user, user_id=None, api_key=None, username=None):
636 def fill_data(self, auth_user, user_id=None, api_key=None, username=None):
601 """
637 """
602 Fetches auth_user by user_id,or api_key if present.
638 Fetches auth_user by user_id,or api_key if present.
603 Fills auth_user attributes with those taken from database.
639 Fills auth_user attributes with those taken from database.
604 Additionally set's is_authenitated if lookup fails
640 Additionally set's is_authenitated if lookup fails
605 present in database
641 present in database
606
642
607 :param auth_user: instance of user to set attributes
643 :param auth_user: instance of user to set attributes
608 :param user_id: user id to fetch by
644 :param user_id: user id to fetch by
609 :param api_key: api key to fetch by
645 :param api_key: api key to fetch by
610 :param username: username to fetch by
646 :param username: username to fetch by
611 """
647 """
612 if user_id is None and api_key is None and username is None:
648 if user_id is None and api_key is None and username is None:
613 raise Exception('You need to pass user_id, api_key or username')
649 raise Exception('You need to pass user_id, api_key or username')
614
650
615 log.debug(
651 log.debug(
616 'doing fill data based on: user_id:%s api_key:%s username:%s',
652 'doing fill data based on: user_id:%s api_key:%s username:%s',
617 user_id, api_key, username)
653 user_id, api_key, username)
618 try:
654 try:
619 dbuser = None
655 dbuser = None
620 if user_id:
656 if user_id:
621 dbuser = self.get(user_id)
657 dbuser = self.get(user_id)
622 elif api_key:
658 elif api_key:
623 dbuser = self.get_by_auth_token(api_key)
659 dbuser = self.get_by_auth_token(api_key)
624 elif username:
660 elif username:
625 dbuser = self.get_by_username(username)
661 dbuser = self.get_by_username(username)
626
662
627 if not dbuser:
663 if not dbuser:
628 log.warning(
664 log.warning(
629 'Unable to lookup user by id:%s api_key:%s username:%s',
665 'Unable to lookup user by id:%s api_key:%s username:%s',
630 user_id, api_key, username)
666 user_id, api_key, username)
631 return False
667 return False
632 if not dbuser.active:
668 if not dbuser.active:
633 log.debug('User `%s` is inactive, skipping fill data', username)
669 log.debug('User `%s` is inactive, skipping fill data', username)
634 return False
670 return False
635
671
636 log.debug('filling user:%s data', dbuser)
672 log.debug('filling user:%s data', dbuser)
637
673
638 # TODO: johbo: Think about this and find a clean solution
674 # TODO: johbo: Think about this and find a clean solution
639 user_data = dbuser.get_dict()
675 user_data = dbuser.get_dict()
640 user_data.update(dbuser.get_api_data(include_secrets=True))
676 user_data.update(dbuser.get_api_data(include_secrets=True))
641
677
642 for k, v in user_data.iteritems():
678 for k, v in user_data.iteritems():
643 # properties of auth user we dont update
679 # properties of auth user we dont update
644 if k not in ['auth_tokens', 'permissions']:
680 if k not in ['auth_tokens', 'permissions']:
645 setattr(auth_user, k, v)
681 setattr(auth_user, k, v)
646
682
647 # few extras
683 # few extras
648 setattr(auth_user, 'feed_token', dbuser.feed_token)
684 setattr(auth_user, 'feed_token', dbuser.feed_token)
649 except Exception:
685 except Exception:
650 log.error(traceback.format_exc())
686 log.error(traceback.format_exc())
651 auth_user.is_authenticated = False
687 auth_user.is_authenticated = False
652 return False
688 return False
653
689
654 return True
690 return True
655
691
656 def has_perm(self, user, perm):
692 def has_perm(self, user, perm):
657 perm = self._get_perm(perm)
693 perm = self._get_perm(perm)
658 user = self._get_user(user)
694 user = self._get_user(user)
659
695
660 return UserToPerm.query().filter(UserToPerm.user == user)\
696 return UserToPerm.query().filter(UserToPerm.user == user)\
661 .filter(UserToPerm.permission == perm).scalar() is not None
697 .filter(UserToPerm.permission == perm).scalar() is not None
662
698
663 def grant_perm(self, user, perm):
699 def grant_perm(self, user, perm):
664 """
700 """
665 Grant user global permissions
701 Grant user global permissions
666
702
667 :param user:
703 :param user:
668 :param perm:
704 :param perm:
669 """
705 """
670 user = self._get_user(user)
706 user = self._get_user(user)
671 perm = self._get_perm(perm)
707 perm = self._get_perm(perm)
672 # if this permission is already granted skip it
708 # if this permission is already granted skip it
673 _perm = UserToPerm.query()\
709 _perm = UserToPerm.query()\
674 .filter(UserToPerm.user == user)\
710 .filter(UserToPerm.user == user)\
675 .filter(UserToPerm.permission == perm)\
711 .filter(UserToPerm.permission == perm)\
676 .scalar()
712 .scalar()
677 if _perm:
713 if _perm:
678 return
714 return
679 new = UserToPerm()
715 new = UserToPerm()
680 new.user = user
716 new.user = user
681 new.permission = perm
717 new.permission = perm
682 self.sa.add(new)
718 self.sa.add(new)
683 return new
719 return new
684
720
685 def revoke_perm(self, user, perm):
721 def revoke_perm(self, user, perm):
686 """
722 """
687 Revoke users global permissions
723 Revoke users global permissions
688
724
689 :param user:
725 :param user:
690 :param perm:
726 :param perm:
691 """
727 """
692 user = self._get_user(user)
728 user = self._get_user(user)
693 perm = self._get_perm(perm)
729 perm = self._get_perm(perm)
694
730
695 obj = UserToPerm.query()\
731 obj = UserToPerm.query()\
696 .filter(UserToPerm.user == user)\
732 .filter(UserToPerm.user == user)\
697 .filter(UserToPerm.permission == perm)\
733 .filter(UserToPerm.permission == perm)\
698 .scalar()
734 .scalar()
699 if obj:
735 if obj:
700 self.sa.delete(obj)
736 self.sa.delete(obj)
701
737
702 def add_extra_email(self, user, email):
738 def add_extra_email(self, user, email):
703 """
739 """
704 Adds email address to UserEmailMap
740 Adds email address to UserEmailMap
705
741
706 :param user:
742 :param user:
707 :param email:
743 :param email:
708 """
744 """
709 from rhodecode.model import forms
745 from rhodecode.model import forms
710 form = forms.UserExtraEmailForm()()
746 form = forms.UserExtraEmailForm()()
711 data = form.to_python({'email': email})
747 data = form.to_python({'email': email})
712 user = self._get_user(user)
748 user = self._get_user(user)
713
749
714 obj = UserEmailMap()
750 obj = UserEmailMap()
715 obj.user = user
751 obj.user = user
716 obj.email = data['email']
752 obj.email = data['email']
717 self.sa.add(obj)
753 self.sa.add(obj)
718 return obj
754 return obj
719
755
720 def delete_extra_email(self, user, email_id):
756 def delete_extra_email(self, user, email_id):
721 """
757 """
722 Removes email address from UserEmailMap
758 Removes email address from UserEmailMap
723
759
724 :param user:
760 :param user:
725 :param email_id:
761 :param email_id:
726 """
762 """
727 user = self._get_user(user)
763 user = self._get_user(user)
728 obj = UserEmailMap.query().get(email_id)
764 obj = UserEmailMap.query().get(email_id)
729 if obj:
765 if obj:
730 self.sa.delete(obj)
766 self.sa.delete(obj)
731
767
732 def parse_ip_range(self, ip_range):
768 def parse_ip_range(self, ip_range):
733 ip_list = []
769 ip_list = []
734 def make_unique(value):
770 def make_unique(value):
735 seen = []
771 seen = []
736 return [c for c in value if not (c in seen or seen.append(c))]
772 return [c for c in value if not (c in seen or seen.append(c))]
737
773
738 # firsts split by commas
774 # firsts split by commas
739 for ip_range in ip_range.split(','):
775 for ip_range in ip_range.split(','):
740 if not ip_range:
776 if not ip_range:
741 continue
777 continue
742 ip_range = ip_range.strip()
778 ip_range = ip_range.strip()
743 if '-' in ip_range:
779 if '-' in ip_range:
744 start_ip, end_ip = ip_range.split('-', 1)
780 start_ip, end_ip = ip_range.split('-', 1)
745 start_ip = ipaddress.ip_address(start_ip.strip())
781 start_ip = ipaddress.ip_address(start_ip.strip())
746 end_ip = ipaddress.ip_address(end_ip.strip())
782 end_ip = ipaddress.ip_address(end_ip.strip())
747 parsed_ip_range = []
783 parsed_ip_range = []
748
784
749 for index in xrange(int(start_ip), int(end_ip) + 1):
785 for index in xrange(int(start_ip), int(end_ip) + 1):
750 new_ip = ipaddress.ip_address(index)
786 new_ip = ipaddress.ip_address(index)
751 parsed_ip_range.append(str(new_ip))
787 parsed_ip_range.append(str(new_ip))
752 ip_list.extend(parsed_ip_range)
788 ip_list.extend(parsed_ip_range)
753 else:
789 else:
754 ip_list.append(ip_range)
790 ip_list.append(ip_range)
755
791
756 return make_unique(ip_list)
792 return make_unique(ip_list)
757
793
758 def add_extra_ip(self, user, ip, description=None):
794 def add_extra_ip(self, user, ip, description=None):
759 """
795 """
760 Adds ip address to UserIpMap
796 Adds ip address to UserIpMap
761
797
762 :param user:
798 :param user:
763 :param ip:
799 :param ip:
764 """
800 """
765 from rhodecode.model import forms
801 from rhodecode.model import forms
766 form = forms.UserExtraIpForm()()
802 form = forms.UserExtraIpForm()()
767 data = form.to_python({'ip': ip})
803 data = form.to_python({'ip': ip})
768 user = self._get_user(user)
804 user = self._get_user(user)
769
805
770 obj = UserIpMap()
806 obj = UserIpMap()
771 obj.user = user
807 obj.user = user
772 obj.ip_addr = data['ip']
808 obj.ip_addr = data['ip']
773 obj.description = description
809 obj.description = description
774 self.sa.add(obj)
810 self.sa.add(obj)
775 return obj
811 return obj
776
812
777 def delete_extra_ip(self, user, ip_id):
813 def delete_extra_ip(self, user, ip_id):
778 """
814 """
779 Removes ip address from UserIpMap
815 Removes ip address from UserIpMap
780
816
781 :param user:
817 :param user:
782 :param ip_id:
818 :param ip_id:
783 """
819 """
784 user = self._get_user(user)
820 user = self._get_user(user)
785 obj = UserIpMap.query().get(ip_id)
821 obj = UserIpMap.query().get(ip_id)
786 if obj:
822 if obj:
787 self.sa.delete(obj)
823 self.sa.delete(obj)
788
824
789 def get_accounts_in_creation_order(self, current_user=None):
825 def get_accounts_in_creation_order(self, current_user=None):
790 """
826 """
791 Get accounts in order of creation for deactivation for license limits
827 Get accounts in order of creation for deactivation for license limits
792
828
793 pick currently logged in user, and append to the list in position 0
829 pick currently logged in user, and append to the list in position 0
794 pick all super-admins in order of creation date and add it to the list
830 pick all super-admins in order of creation date and add it to the list
795 pick all other accounts in order of creation and add it to the list.
831 pick all other accounts in order of creation and add it to the list.
796
832
797 Based on that list, the last accounts can be disabled as they are
833 Based on that list, the last accounts can be disabled as they are
798 created at the end and don't include any of the super admins as well
834 created at the end and don't include any of the super admins as well
799 as the current user.
835 as the current user.
800
836
801 :param current_user: optionally current user running this operation
837 :param current_user: optionally current user running this operation
802 """
838 """
803
839
804 if not current_user:
840 if not current_user:
805 current_user = get_current_rhodecode_user()
841 current_user = get_current_rhodecode_user()
806 active_super_admins = [
842 active_super_admins = [
807 x.user_id for x in User.query()
843 x.user_id for x in User.query()
808 .filter(User.user_id != current_user.user_id)
844 .filter(User.user_id != current_user.user_id)
809 .filter(User.active == true())
845 .filter(User.active == true())
810 .filter(User.admin == true())
846 .filter(User.admin == true())
811 .order_by(User.created_on.asc())]
847 .order_by(User.created_on.asc())]
812
848
813 active_regular_users = [
849 active_regular_users = [
814 x.user_id for x in User.query()
850 x.user_id for x in User.query()
815 .filter(User.user_id != current_user.user_id)
851 .filter(User.user_id != current_user.user_id)
816 .filter(User.active == true())
852 .filter(User.active == true())
817 .filter(User.admin == false())
853 .filter(User.admin == false())
818 .order_by(User.created_on.asc())]
854 .order_by(User.created_on.asc())]
819
855
820 list_of_accounts = [current_user.user_id]
856 list_of_accounts = [current_user.user_id]
821 list_of_accounts += active_super_admins
857 list_of_accounts += active_super_admins
822 list_of_accounts += active_regular_users
858 list_of_accounts += active_regular_users
823
859
824 return list_of_accounts
860 return list_of_accounts
825
861
826 def deactivate_last_users(self, expected_users):
862 def deactivate_last_users(self, expected_users):
827 """
863 """
828 Deactivate accounts that are over the license limits.
864 Deactivate accounts that are over the license limits.
829 Algorithm of which accounts to disabled is based on the formula:
865 Algorithm of which accounts to disabled is based on the formula:
830
866
831 Get current user, then super admins in creation order, then regular
867 Get current user, then super admins in creation order, then regular
832 active users in creation order.
868 active users in creation order.
833
869
834 Using that list we mark all accounts from the end of it as inactive.
870 Using that list we mark all accounts from the end of it as inactive.
835 This way we block only latest created accounts.
871 This way we block only latest created accounts.
836
872
837 :param expected_users: list of users in special order, we deactivate
873 :param expected_users: list of users in special order, we deactivate
838 the end N ammoun of users from that list
874 the end N ammoun of users from that list
839 """
875 """
840
876
841 list_of_accounts = self.get_accounts_in_creation_order()
877 list_of_accounts = self.get_accounts_in_creation_order()
842
878
843 for acc_id in list_of_accounts[expected_users + 1:]:
879 for acc_id in list_of_accounts[expected_users + 1:]:
844 user = User.get(acc_id)
880 user = User.get(acc_id)
845 log.info('Deactivating account %s for license unlock', user)
881 log.info('Deactivating account %s for license unlock', user)
846 user.active = False
882 user.active = False
847 Session().add(user)
883 Session().add(user)
848 Session().commit()
884 Session().commit()
849
885
850 return
886 return
851
887
852 def get_user_log(self, user, filter_term):
888 def get_user_log(self, user, filter_term):
853 user_log = UserLog.query()\
889 user_log = UserLog.query()\
854 .filter(or_(UserLog.user_id == user.user_id,
890 .filter(or_(UserLog.user_id == user.user_id,
855 UserLog.username == user.username))\
891 UserLog.username == user.username))\
856 .options(joinedload(UserLog.user))\
892 .options(joinedload(UserLog.user))\
857 .options(joinedload(UserLog.repository))\
893 .options(joinedload(UserLog.repository))\
858 .order_by(UserLog.action_date.desc())
894 .order_by(UserLog.action_date.desc())
859
895
860 user_log = user_log_filter(user_log, filter_term)
896 user_log = user_log_filter(user_log, filter_term)
861 return user_log
897 return user_log
@@ -1,252 +1,171 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import tempfile
21 import tempfile
22
22
23 import mock
23 import mock
24 import pytest
24 import pytest
25
25
26 from rhodecode.lib.exceptions import AttachedForksError
26 from rhodecode.lib.exceptions import AttachedForksError
27 from rhodecode.lib.utils import make_db_config
27 from rhodecode.lib.utils import make_db_config
28 from rhodecode.model.db import Repository
28 from rhodecode.model.db import Repository
29 from rhodecode.model.meta import Session
29 from rhodecode.model.meta import Session
30 from rhodecode.model.repo import RepoModel
30 from rhodecode.model.repo import RepoModel
31 from rhodecode.model.scm import ScmModel
31 from rhodecode.model.scm import ScmModel
32
32
33
33
34 class TestRepoModel(object):
34 class TestRepoModel(object):
35
35
36 def test_remove_repo(self, backend):
36 def test_remove_repo(self, backend):
37 repo = backend.create_repo()
37 repo = backend.create_repo()
38 Session().commit()
38 Session().commit()
39 RepoModel().delete(repo=repo)
39 RepoModel().delete(repo=repo)
40 Session().commit()
40 Session().commit()
41
41
42 repos = ScmModel().repo_scan()
42 repos = ScmModel().repo_scan()
43
43
44 assert Repository.get_by_repo_name(repo_name=backend.repo_name) is None
44 assert Repository.get_by_repo_name(repo_name=backend.repo_name) is None
45 assert repo.repo_name not in repos
45 assert repo.repo_name not in repos
46
46
47 def test_remove_repo_raises_exc_when_attached_forks(self, backend):
47 def test_remove_repo_raises_exc_when_attached_forks(self, backend):
48 repo = backend.create_repo()
48 repo = backend.create_repo()
49 Session().commit()
49 Session().commit()
50 backend.create_fork()
50 backend.create_fork()
51 Session().commit()
51 Session().commit()
52
52
53 with pytest.raises(AttachedForksError):
53 with pytest.raises(AttachedForksError):
54 RepoModel().delete(repo=repo)
54 RepoModel().delete(repo=repo)
55
55
56 def test_remove_repo_delete_forks(self, backend):
56 def test_remove_repo_delete_forks(self, backend):
57 repo = backend.create_repo()
57 repo = backend.create_repo()
58 Session().commit()
58 Session().commit()
59
59
60 fork = backend.create_fork()
60 fork = backend.create_fork()
61 Session().commit()
61 Session().commit()
62
62
63 fork_of_fork = backend.create_fork()
63 fork_of_fork = backend.create_fork()
64 Session().commit()
64 Session().commit()
65
65
66 RepoModel().delete(repo=repo, forks='delete')
66 RepoModel().delete(repo=repo, forks='delete')
67 Session().commit()
67 Session().commit()
68
68
69 assert Repository.get_by_repo_name(repo_name=repo.repo_name) is None
69 assert Repository.get_by_repo_name(repo_name=repo.repo_name) is None
70 assert Repository.get_by_repo_name(repo_name=fork.repo_name) is None
70 assert Repository.get_by_repo_name(repo_name=fork.repo_name) is None
71 assert (
71 assert (
72 Repository.get_by_repo_name(repo_name=fork_of_fork.repo_name)
72 Repository.get_by_repo_name(repo_name=fork_of_fork.repo_name)
73 is None)
73 is None)
74
74
75 def test_remove_repo_detach_forks(self, backend):
75 def test_remove_repo_detach_forks(self, backend):
76 repo = backend.create_repo()
76 repo = backend.create_repo()
77 Session().commit()
77 Session().commit()
78
78
79 fork = backend.create_fork()
79 fork = backend.create_fork()
80 Session().commit()
80 Session().commit()
81
81
82 fork_of_fork = backend.create_fork()
82 fork_of_fork = backend.create_fork()
83 Session().commit()
83 Session().commit()
84
84
85 RepoModel().delete(repo=repo, forks='detach')
85 RepoModel().delete(repo=repo, forks='detach')
86 Session().commit()
86 Session().commit()
87
87
88 assert Repository.get_by_repo_name(repo_name=repo.repo_name) is None
88 assert Repository.get_by_repo_name(repo_name=repo.repo_name) is None
89 assert (
89 assert (
90 Repository.get_by_repo_name(repo_name=fork.repo_name) is not None)
90 Repository.get_by_repo_name(repo_name=fork.repo_name) is not None)
91 assert (
91 assert (
92 Repository.get_by_repo_name(repo_name=fork_of_fork.repo_name)
92 Repository.get_by_repo_name(repo_name=fork_of_fork.repo_name)
93 is not None)
93 is not None)
94
94
95 @pytest.mark.parametrize("filename, expected", [
95 @pytest.mark.parametrize("filename, expected", [
96 ("README", True),
96 ("README", True),
97 ("README.rst", False),
97 ("README.rst", False),
98 ])
98 ])
99 def test_filenode_is_link(self, vcsbackend, filename, expected):
99 def test_filenode_is_link(self, vcsbackend, filename, expected):
100 repo = vcsbackend.repo
100 repo = vcsbackend.repo
101 assert repo.get_commit().is_link(filename) is expected
101 assert repo.get_commit().is_link(filename) is expected
102
102
103 def test_get_commit(self, backend):
103 def test_get_commit(self, backend):
104 backend.repo.get_commit()
104 backend.repo.get_commit()
105
105
106 def test_get_changeset_is_deprecated(self, backend):
106 def test_get_changeset_is_deprecated(self, backend):
107 repo = backend.repo
107 repo = backend.repo
108 pytest.deprecated_call(repo.get_changeset)
108 pytest.deprecated_call(repo.get_changeset)
109
109
110 def test_clone_url_encrypted_value(self, backend):
110 def test_clone_url_encrypted_value(self, backend):
111 repo = backend.create_repo()
111 repo = backend.create_repo()
112 Session().commit()
112 Session().commit()
113
113
114 repo.clone_url = 'https://marcink:qweqwe@code.rhodecode.com'
114 repo.clone_url = 'https://marcink:qweqwe@code.rhodecode.com'
115 Session().add(repo)
115 Session().add(repo)
116 Session().commit()
116 Session().commit()
117
117
118 assert repo.clone_url == 'https://marcink:qweqwe@code.rhodecode.com'
118 assert repo.clone_url == 'https://marcink:qweqwe@code.rhodecode.com'
119
119
120 @pytest.mark.backends("git", "svn")
120 @pytest.mark.backends("git", "svn")
121 def test_create_filesystem_repo_installs_hooks(self, tmpdir, backend):
121 def test_create_filesystem_repo_installs_hooks(self, tmpdir, backend):
122 hook_methods = {
122 hook_methods = {
123 'git': 'install_git_hook',
123 'git': 'install_git_hook',
124 'svn': 'install_svn_hooks'
124 'svn': 'install_svn_hooks'
125 }
125 }
126 repo = backend.create_repo()
126 repo = backend.create_repo()
127 repo_name = repo.repo_name
127 repo_name = repo.repo_name
128 model = RepoModel()
128 model = RepoModel()
129 repo_location = tempfile.mkdtemp()
129 repo_location = tempfile.mkdtemp()
130 model.repos_path = repo_location
130 model.repos_path = repo_location
131 method = hook_methods[backend.alias]
131 method = hook_methods[backend.alias]
132 with mock.patch.object(ScmModel, method) as hooks_mock:
132 with mock.patch.object(ScmModel, method) as hooks_mock:
133 model._create_filesystem_repo(
133 model._create_filesystem_repo(
134 repo_name, backend.alias, repo_group='', clone_uri=None)
134 repo_name, backend.alias, repo_group='', clone_uri=None)
135 assert hooks_mock.call_count == 1
135 assert hooks_mock.call_count == 1
136 hook_args, hook_kwargs = hooks_mock.call_args
136 hook_args, hook_kwargs = hooks_mock.call_args
137 assert hook_args[0].name == repo_name
137 assert hook_args[0].name == repo_name
138
138
139 @pytest.mark.parametrize("use_global_config, repo_name_passed", [
139 @pytest.mark.parametrize("use_global_config, repo_name_passed", [
140 (True, False),
140 (True, False),
141 (False, True)
141 (False, True)
142 ])
142 ])
143 def test_per_repo_config_is_generated_during_filesystem_repo_creation(
143 def test_per_repo_config_is_generated_during_filesystem_repo_creation(
144 self, tmpdir, backend, use_global_config, repo_name_passed):
144 self, tmpdir, backend, use_global_config, repo_name_passed):
145 repo_name = 'test-{}-repo-{}'.format(backend.alias, use_global_config)
145 repo_name = 'test-{}-repo-{}'.format(backend.alias, use_global_config)
146 config = make_db_config()
146 config = make_db_config()
147 model = RepoModel()
147 model = RepoModel()
148 with mock.patch('rhodecode.model.repo.make_db_config') as config_mock:
148 with mock.patch('rhodecode.model.repo.make_db_config') as config_mock:
149 config_mock.return_value = config
149 config_mock.return_value = config
150 model._create_filesystem_repo(
150 model._create_filesystem_repo(
151 repo_name, backend.alias, repo_group='', clone_uri=None,
151 repo_name, backend.alias, repo_group='', clone_uri=None,
152 use_global_config=use_global_config)
152 use_global_config=use_global_config)
153 expected_repo_name = repo_name if repo_name_passed else None
153 expected_repo_name = repo_name if repo_name_passed else None
154 expected_call = mock.call(clear_session=False, repo=expected_repo_name)
154 expected_call = mock.call(clear_session=False, repo=expected_repo_name)
155 assert expected_call in config_mock.call_args_list
155 assert expected_call in config_mock.call_args_list
156
156
157 def test_update_commit_cache_with_config(serf, backend):
157 def test_update_commit_cache_with_config(serf, backend):
158 repo = backend.create_repo()
158 repo = backend.create_repo()
159 with mock.patch('rhodecode.model.db.Repository.scm_instance') as scm:
159 with mock.patch('rhodecode.model.db.Repository.scm_instance') as scm:
160 scm_instance = mock.Mock()
160 scm_instance = mock.Mock()
161 scm_instance.get_commit.return_value = {
161 scm_instance.get_commit.return_value = {
162 'raw_id': 40*'0',
162 'raw_id': 40*'0',
163 'revision': 1
163 'revision': 1
164 }
164 }
165 scm.return_value = scm_instance
165 scm.return_value = scm_instance
166 repo.update_commit_cache()
166 repo.update_commit_cache()
167 scm.assert_called_with(cache=False, config=None)
167 scm.assert_called_with(cache=False, config=None)
168 config = {'test': 'config'}
168 config = {'test': 'config'}
169 repo.update_commit_cache(config=config)
169 repo.update_commit_cache(config=config)
170 scm.assert_called_with(
170 scm.assert_called_with(
171 cache=False, config=config)
171 cache=False, config=config)
172
173
174 class TestGetUsers(object):
175 def test_returns_active_users(self, backend, user_util):
176 for i in range(4):
177 is_active = i % 2 == 0
178 user_util.create_user(active=is_active, lastname='Fake user')
179
180 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
181 users = RepoModel().get_users()
182 fake_users = [u for u in users if u['last_name'] == 'Fake user']
183 assert len(fake_users) == 2
184
185 expected_keys = (
186 'id', 'first_name', 'last_name', 'username', 'icon_link',
187 'value_display', 'value', 'value_type')
188 for user in users:
189 assert user['value_type'] is 'user'
190 for key in expected_keys:
191 assert key in user
192
193 def test_returns_user_filtered_by_last_name(self, backend, user_util):
194 keywords = ('aBc', u'ΓΌnicode')
195 for keyword in keywords:
196 for i in range(2):
197 user_util.create_user(
198 active=True, lastname=u'Fake {} user'.format(keyword))
199
200 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
201 keyword = keywords[1].lower()
202 users = RepoModel().get_users(name_contains=keyword)
203
204 fake_users = [u for u in users if u['last_name'].startswith('Fake')]
205 assert len(fake_users) == 2
206 for user in fake_users:
207 assert user['last_name'] == safe_unicode('Fake ΓΌnicode user')
208
209 def test_returns_user_filtered_by_first_name(self, backend, user_util):
210 created_users = []
211 keywords = ('aBc', u'ΓΌnicode')
212 for keyword in keywords:
213 for i in range(2):
214 created_users.append(user_util.create_user(
215 active=True, lastname='Fake user',
216 firstname=u'Fake {} user'.format(keyword)))
217
218 keyword = keywords[1].lower()
219 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
220 users = RepoModel().get_users(name_contains=keyword)
221
222 fake_users = [u for u in users if u['last_name'].startswith('Fake')]
223 assert len(fake_users) == 2
224 for user in fake_users:
225 assert user['first_name'] == safe_unicode('Fake ΓΌnicode user')
226
227 def test_returns_user_filtered_by_username(self, backend, user_util):
228 created_users = []
229 for i in range(5):
230 created_users.append(user_util.create_user(
231 active=True, lastname='Fake user'))
232
233 user_filter = created_users[-1].username[-2:]
234 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
235 users = RepoModel().get_users(name_contains=user_filter)
236
237 fake_users = [u for u in users if u['last_name'].startswith('Fake')]
238 assert len(fake_users) == 1
239 assert fake_users[0]['username'] == created_users[-1].username
240
241 def test_returns_limited_user_list(self, backend, user_util):
242 created_users = []
243 for i in range(5):
244 created_users.append(user_util.create_user(
245 active=True, lastname='Fake user'))
246
247 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
248 users = RepoModel().get_users(name_contains='Fake', limit=3)
249
250 fake_users = [u for u in users if u['last_name'].startswith('Fake')]
251 assert len(fake_users) == 3
252
@@ -1,242 +1,323 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import pytest
21 import pytest
22 from sqlalchemy.sql.expression import true
22 import mock
23
23
24 from rhodecode.model.db import User, UserGroup, UserGroupMember, UserEmailMap,\
24 from rhodecode.lib.utils2 import safe_unicode
25 Permission, UserIpMap
25 from rhodecode.model.db import (
26 true, User, UserGroup, UserGroupMember, UserEmailMap, Permission, UserIpMap)
26 from rhodecode.model.meta import Session
27 from rhodecode.model.meta import Session
27 from rhodecode.model.user import UserModel
28 from rhodecode.model.user import UserModel
28 from rhodecode.model.user_group import UserGroupModel
29 from rhodecode.model.user_group import UserGroupModel
29 from rhodecode.model.repo import RepoModel
30 from rhodecode.model.repo import RepoModel
30 from rhodecode.model.repo_group import RepoGroupModel
31 from rhodecode.model.repo_group import RepoGroupModel
31 from rhodecode.tests.fixture import Fixture
32 from rhodecode.tests.fixture import Fixture
32
33
33 fixture = Fixture()
34 fixture = Fixture()
34
35
35
36
37 class TestGetUsers(object):
38 def test_returns_active_users(self, backend, user_util):
39 for i in range(4):
40 is_active = i % 2 == 0
41 user_util.create_user(active=is_active, lastname='Fake user')
42
43 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
44 users = UserModel().get_users()
45 fake_users = [u for u in users if u['last_name'] == 'Fake user']
46 assert len(fake_users) == 2
47
48 expected_keys = (
49 'id', 'first_name', 'last_name', 'username', 'icon_link',
50 'value_display', 'value', 'value_type')
51 for user in users:
52 assert user['value_type'] is 'user'
53 for key in expected_keys:
54 assert key in user
55
56 def test_returns_user_filtered_by_last_name(self, backend, user_util):
57 keywords = ('aBc', u'ΓΌnicode')
58 for keyword in keywords:
59 for i in range(2):
60 user_util.create_user(
61 active=True, lastname=u'Fake {} user'.format(keyword))
62
63 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
64 keyword = keywords[1].lower()
65 users = UserModel().get_users(name_contains=keyword)
66
67 fake_users = [u for u in users if u['last_name'].startswith('Fake')]
68 assert len(fake_users) == 2
69 for user in fake_users:
70 assert user['last_name'] == safe_unicode('Fake ΓΌnicode user')
71
72 def test_returns_user_filtered_by_first_name(self, backend, user_util):
73 created_users = []
74 keywords = ('aBc', u'ΓΌnicode')
75 for keyword in keywords:
76 for i in range(2):
77 created_users.append(user_util.create_user(
78 active=True, lastname='Fake user',
79 firstname=u'Fake {} user'.format(keyword)))
80
81 keyword = keywords[1].lower()
82 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
83 users = UserModel().get_users(name_contains=keyword)
84
85 fake_users = [u for u in users if u['last_name'].startswith('Fake')]
86 assert len(fake_users) == 2
87 for user in fake_users:
88 assert user['first_name'] == safe_unicode('Fake ΓΌnicode user')
89
90 def test_returns_user_filtered_by_username(self, backend, user_util):
91 created_users = []
92 for i in range(5):
93 created_users.append(user_util.create_user(
94 active=True, lastname='Fake user'))
95
96 user_filter = created_users[-1].username[-2:]
97 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
98 users = UserModel().get_users(name_contains=user_filter)
99
100 fake_users = [u for u in users if u['last_name'].startswith('Fake')]
101 assert len(fake_users) == 1
102 assert fake_users[0]['username'] == created_users[-1].username
103
104 def test_returns_limited_user_list(self, backend, user_util):
105 created_users = []
106 for i in range(5):
107 created_users.append(user_util.create_user(
108 active=True, lastname='Fake user'))
109
110 with mock.patch('rhodecode.lib.helpers.gravatar_url'):
111 users = UserModel().get_users(name_contains='Fake', limit=3)
112
113 fake_users = [u for u in users if u['last_name'].startswith('Fake')]
114 assert len(fake_users) == 3
115
116
36 @pytest.fixture
117 @pytest.fixture
37 def test_user(request, pylonsapp):
118 def test_user(request, pylonsapp):
38 usr = UserModel().create_or_update(
119 usr = UserModel().create_or_update(
39 username=u'test_user',
120 username=u'test_user',
40 password=u'qweqwe',
121 password=u'qweqwe',
41 email=u'main_email@rhodecode.org',
122 email=u'main_email@rhodecode.org',
42 firstname=u'u1', lastname=u'u1')
123 firstname=u'u1', lastname=u'u1')
43 Session().commit()
124 Session().commit()
44 assert User.get_by_username(u'test_user') == usr
125 assert User.get_by_username(u'test_user') == usr
45
126
46 @request.addfinalizer
127 @request.addfinalizer
47 def cleanup():
128 def cleanup():
48 if UserModel().get_user(usr.user_id) is None:
129 if UserModel().get_user(usr.user_id) is None:
49 return
130 return
50
131
51 perm = Permission.query().all()
132 perm = Permission.query().all()
52 for p in perm:
133 for p in perm:
53 UserModel().revoke_perm(usr, p)
134 UserModel().revoke_perm(usr, p)
54
135
55 UserModel().delete(usr.user_id)
136 UserModel().delete(usr.user_id)
56 Session().commit()
137 Session().commit()
57
138
58 return usr
139 return usr
59
140
60
141
61 def test_create_and_remove(test_user):
142 def test_create_and_remove(test_user):
62 usr = test_user
143 usr = test_user
63
144
64 # make user group
145 # make user group
65 user_group = fixture.create_user_group('some_example_group')
146 user_group = fixture.create_user_group('some_example_group')
66 Session().commit()
147 Session().commit()
67
148
68 UserGroupModel().add_user_to_group(user_group, usr)
149 UserGroupModel().add_user_to_group(user_group, usr)
69 Session().commit()
150 Session().commit()
70
151
71 assert UserGroup.get(user_group.users_group_id) == user_group
152 assert UserGroup.get(user_group.users_group_id) == user_group
72 assert UserGroupMember.query().count() == 1
153 assert UserGroupMember.query().count() == 1
73 UserModel().delete(usr.user_id)
154 UserModel().delete(usr.user_id)
74 Session().commit()
155 Session().commit()
75
156
76 assert UserGroupMember.query().all() == []
157 assert UserGroupMember.query().all() == []
77
158
78
159
79 def test_additonal_email_as_main(test_user):
160 def test_additonal_email_as_main(test_user):
80 with pytest.raises(AttributeError):
161 with pytest.raises(AttributeError):
81 m = UserEmailMap()
162 m = UserEmailMap()
82 m.email = test_user.email
163 m.email = test_user.email
83 m.user = test_user
164 m.user = test_user
84 Session().add(m)
165 Session().add(m)
85 Session().commit()
166 Session().commit()
86
167
87
168
88 def test_extra_email_map(test_user):
169 def test_extra_email_map(test_user):
89
170
90 m = UserEmailMap()
171 m = UserEmailMap()
91 m.email = u'main_email2@rhodecode.org'
172 m.email = u'main_email2@rhodecode.org'
92 m.user = test_user
173 m.user = test_user
93 Session().add(m)
174 Session().add(m)
94 Session().commit()
175 Session().commit()
95
176
96 u = User.get_by_email(email='main_email@rhodecode.org')
177 u = User.get_by_email(email='main_email@rhodecode.org')
97 assert test_user.user_id == u.user_id
178 assert test_user.user_id == u.user_id
98 assert test_user.username == u.username
179 assert test_user.username == u.username
99
180
100 u = User.get_by_email(email='main_email2@rhodecode.org')
181 u = User.get_by_email(email='main_email2@rhodecode.org')
101 assert test_user.user_id == u.user_id
182 assert test_user.user_id == u.user_id
102 assert test_user.username == u.username
183 assert test_user.username == u.username
103 u = User.get_by_email(email='main_email3@rhodecode.org')
184 u = User.get_by_email(email='main_email3@rhodecode.org')
104 assert u is None
185 assert u is None
105
186
106
187
107 def test_get_api_data_replaces_secret_data_by_default(test_user):
188 def test_get_api_data_replaces_secret_data_by_default(test_user):
108 api_data = test_user.get_api_data()
189 api_data = test_user.get_api_data()
109 api_key_length = 40
190 api_key_length = 40
110 expected_replacement = '*' * api_key_length
191 expected_replacement = '*' * api_key_length
111
192
112 for key in api_data['api_keys']:
193 for key in api_data['api_keys']:
113 assert key == expected_replacement
194 assert key == expected_replacement
114
195
115
196
116 def test_get_api_data_includes_secret_data_if_activated(test_user):
197 def test_get_api_data_includes_secret_data_if_activated(test_user):
117 api_data = test_user.get_api_data(include_secrets=True)
198 api_data = test_user.get_api_data(include_secrets=True)
118 assert api_data['api_keys'] == test_user.auth_tokens
199 assert api_data['api_keys'] == test_user.auth_tokens
119
200
120
201
121 def test_add_perm(test_user):
202 def test_add_perm(test_user):
122 perm = Permission.query().all()[0]
203 perm = Permission.query().all()[0]
123 UserModel().grant_perm(test_user, perm)
204 UserModel().grant_perm(test_user, perm)
124 Session().commit()
205 Session().commit()
125 assert UserModel().has_perm(test_user, perm)
206 assert UserModel().has_perm(test_user, perm)
126
207
127
208
128 def test_has_perm(test_user):
209 def test_has_perm(test_user):
129 perm = Permission.query().all()
210 perm = Permission.query().all()
130 for p in perm:
211 for p in perm:
131 assert not UserModel().has_perm(test_user, p)
212 assert not UserModel().has_perm(test_user, p)
132
213
133
214
134 def test_revoke_perm(test_user):
215 def test_revoke_perm(test_user):
135 perm = Permission.query().all()[0]
216 perm = Permission.query().all()[0]
136 UserModel().grant_perm(test_user, perm)
217 UserModel().grant_perm(test_user, perm)
137 Session().commit()
218 Session().commit()
138 assert UserModel().has_perm(test_user, perm)
219 assert UserModel().has_perm(test_user, perm)
139
220
140 # revoke
221 # revoke
141 UserModel().revoke_perm(test_user, perm)
222 UserModel().revoke_perm(test_user, perm)
142 Session().commit()
223 Session().commit()
143 assert not UserModel().has_perm(test_user, perm)
224 assert not UserModel().has_perm(test_user, perm)
144
225
145
226
146 @pytest.mark.parametrize("ip_range, expected, expect_errors", [
227 @pytest.mark.parametrize("ip_range, expected, expect_errors", [
147 ('', [], False),
228 ('', [], False),
148 ('127.0.0.1', ['127.0.0.1'], False),
229 ('127.0.0.1', ['127.0.0.1'], False),
149 ('127.0.0.1,127.0.0.2', ['127.0.0.1', '127.0.0.2'], False),
230 ('127.0.0.1,127.0.0.2', ['127.0.0.1', '127.0.0.2'], False),
150 ('127.0.0.1 , 127.0.0.2', ['127.0.0.1', '127.0.0.2'], False),
231 ('127.0.0.1 , 127.0.0.2', ['127.0.0.1', '127.0.0.2'], False),
151 (
232 (
152 '127.0.0.1,172.172.172.0,127.0.0.2',
233 '127.0.0.1,172.172.172.0,127.0.0.2',
153 ['127.0.0.1', '172.172.172.0', '127.0.0.2'], False),
234 ['127.0.0.1', '172.172.172.0', '127.0.0.2'], False),
154 (
235 (
155 '127.0.0.1-127.0.0.5',
236 '127.0.0.1-127.0.0.5',
156 ['127.0.0.1', '127.0.0.2', '127.0.0.3', '127.0.0.4', '127.0.0.5'],
237 ['127.0.0.1', '127.0.0.2', '127.0.0.3', '127.0.0.4', '127.0.0.5'],
157 False),
238 False),
158 (
239 (
159 '127.0.0.1 - 127.0.0.5',
240 '127.0.0.1 - 127.0.0.5',
160 ['127.0.0.1', '127.0.0.2', '127.0.0.3', '127.0.0.4', '127.0.0.5'],
241 ['127.0.0.1', '127.0.0.2', '127.0.0.3', '127.0.0.4', '127.0.0.5'],
161 False
242 False
162 ),
243 ),
163 ('-', [], True),
244 ('-', [], True),
164 ('127.0.0.1-32', [], True),
245 ('127.0.0.1-32', [], True),
165 (
246 (
166 '127.0.0.1,127.0.0.1,127.0.0.1,127.0.0.1-127.0.0.2,127.0.0.2',
247 '127.0.0.1,127.0.0.1,127.0.0.1,127.0.0.1-127.0.0.2,127.0.0.2',
167 ['127.0.0.1', '127.0.0.2'], False),
248 ['127.0.0.1', '127.0.0.2'], False),
168 (
249 (
169 '127.0.0.1-127.0.0.2,127.0.0.4-127.0.0.6,',
250 '127.0.0.1-127.0.0.2,127.0.0.4-127.0.0.6,',
170 ['127.0.0.1', '127.0.0.2', '127.0.0.4', '127.0.0.5', '127.0.0.6'],
251 ['127.0.0.1', '127.0.0.2', '127.0.0.4', '127.0.0.5', '127.0.0.6'],
171 False
252 False
172 ),
253 ),
173 (
254 (
174 '127.0.0.1-127.0.0.2,127.0.0.1-127.0.0.6,',
255 '127.0.0.1-127.0.0.2,127.0.0.1-127.0.0.6,',
175 ['127.0.0.1', '127.0.0.2', '127.0.0.3', '127.0.0.4', '127.0.0.5',
256 ['127.0.0.1', '127.0.0.2', '127.0.0.3', '127.0.0.4', '127.0.0.5',
176 '127.0.0.6'],
257 '127.0.0.6'],
177 False
258 False
178 ),
259 ),
179 ])
260 ])
180 def test_ip_range_generator(ip_range, expected, expect_errors):
261 def test_ip_range_generator(ip_range, expected, expect_errors):
181 func = UserModel().parse_ip_range
262 func = UserModel().parse_ip_range
182 if expect_errors:
263 if expect_errors:
183 pytest.raises(Exception, func, ip_range)
264 pytest.raises(Exception, func, ip_range)
184 else:
265 else:
185 parsed_list = func(ip_range)
266 parsed_list = func(ip_range)
186 assert parsed_list == expected
267 assert parsed_list == expected
187
268
188
269
189 def test_user_delete_cascades_ip_whitelist(test_user):
270 def test_user_delete_cascades_ip_whitelist(test_user):
190 sample_ip = '1.1.1.1'
271 sample_ip = '1.1.1.1'
191 uid_map = UserIpMap(user_id=test_user.user_id, ip_addr=sample_ip)
272 uid_map = UserIpMap(user_id=test_user.user_id, ip_addr=sample_ip)
192 Session().add(uid_map)
273 Session().add(uid_map)
193 Session().delete(test_user)
274 Session().delete(test_user)
194 try:
275 try:
195 Session().flush()
276 Session().flush()
196 finally:
277 finally:
197 Session().rollback()
278 Session().rollback()
198
279
199
280
200 def test_account_for_deactivation_generation(test_user):
281 def test_account_for_deactivation_generation(test_user):
201 accounts = UserModel().get_accounts_in_creation_order(
282 accounts = UserModel().get_accounts_in_creation_order(
202 current_user=test_user)
283 current_user=test_user)
203 # current user should be #1 in the list
284 # current user should be #1 in the list
204 assert accounts[0] == test_user.user_id
285 assert accounts[0] == test_user.user_id
205 active_users = User.query().filter(User.active == true()).count()
286 active_users = User.query().filter(User.active == true()).count()
206 assert active_users == len(accounts)
287 assert active_users == len(accounts)
207
288
208
289
209 def test_user_delete_cascades_permissions_on_repo(backend, test_user):
290 def test_user_delete_cascades_permissions_on_repo(backend, test_user):
210 test_repo = backend.create_repo()
291 test_repo = backend.create_repo()
211 RepoModel().grant_user_permission(
292 RepoModel().grant_user_permission(
212 test_repo, test_user, 'repository.write')
293 test_repo, test_user, 'repository.write')
213 Session().commit()
294 Session().commit()
214
295
215 assert test_user.repo_to_perm
296 assert test_user.repo_to_perm
216
297
217 UserModel().delete(test_user)
298 UserModel().delete(test_user)
218 Session().commit()
299 Session().commit()
219
300
220
301
221 def test_user_delete_cascades_permissions_on_repo_group(
302 def test_user_delete_cascades_permissions_on_repo_group(
222 test_repo_group, test_user):
303 test_repo_group, test_user):
223 RepoGroupModel().grant_user_permission(
304 RepoGroupModel().grant_user_permission(
224 test_repo_group, test_user, 'group.write')
305 test_repo_group, test_user, 'group.write')
225 Session().commit()
306 Session().commit()
226
307
227 assert test_user.repo_group_to_perm
308 assert test_user.repo_group_to_perm
228
309
229 Session().delete(test_user)
310 Session().delete(test_user)
230 Session().commit()
311 Session().commit()
231
312
232
313
233 def test_user_delete_cascades_permissions_on_user_group(
314 def test_user_delete_cascades_permissions_on_user_group(
234 test_user_group, test_user):
315 test_user_group, test_user):
235 UserGroupModel().grant_user_permission(
316 UserGroupModel().grant_user_permission(
236 test_user_group, test_user, 'usergroup.write')
317 test_user_group, test_user, 'usergroup.write')
237 Session().commit()
318 Session().commit()
238
319
239 assert test_user.user_group_to_perm
320 assert test_user.user_group_to_perm
240
321
241 Session().delete(test_user)
322 Session().delete(test_user)
242 Session().commit()
323 Session().commit()
General Comments 0
You need to be logged in to leave comments. Login now