##// END OF EJS Templates
api-utils: added helper to validate repository-group access permissions.
marcink -
r1148:6c251832 default
parent child Browse files
Show More
@@ -1,376 +1,407 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2014-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 JSON RPC utils
23 23 """
24 24
25 25 import collections
26 26 import logging
27 27
28 28 from rhodecode.api.exc import JSONRPCError
29 from rhodecode.lib.auth import HasPermissionAnyApi, HasRepoPermissionAnyApi
29 from rhodecode.lib.auth import HasPermissionAnyApi, HasRepoPermissionAnyApi, \
30 HasRepoGroupPermissionAnyApi
30 31 from rhodecode.lib.utils import safe_unicode
31 32 from rhodecode.controllers.utils import get_commit_from_ref_name
32 33 from rhodecode.lib.vcs.exceptions import RepositoryError
33 34
34 35 log = logging.getLogger(__name__)
35 36
36 37
37 38 class OAttr(object):
38 39 """
39 40 Special Option that defines other attribute, and can default to them
40 41
41 42 Example::
42 43
43 44 def test(apiuser, userid=Optional(OAttr('apiuser')):
44 45 user = Optional.extract(userid, evaluate_locals=local())
45 46 #if we pass in userid, we get it, else it will default to apiuser
46 47 #attribute
47 48 """
48 49
49 50 def __init__(self, attr_name):
50 51 self.attr_name = attr_name
51 52
52 53 def __repr__(self):
53 54 return '<OptionalAttr:%s>' % self.attr_name
54 55
55 56 def __call__(self):
56 57 return self
57 58
58 59
59 60 class Optional(object):
60 61 """
61 62 Defines an optional parameter::
62 63
63 64 param = param.getval() if isinstance(param, Optional) else param
64 65 param = param() if isinstance(param, Optional) else param
65 66
66 67 is equivalent of::
67 68
68 69 param = Optional.extract(param)
69 70
70 71 """
71 72
72 73 def __init__(self, type_):
73 74 self.type_ = type_
74 75
75 76 def __repr__(self):
76 77 return '<Optional:%s>' % self.type_.__repr__()
77 78
78 79 def __call__(self):
79 80 return self.getval()
80 81
81 82 def getval(self, evaluate_locals=None):
82 83 """
83 84 returns value from this Optional instance
84 85 """
85 86 if isinstance(self.type_, OAttr):
86 87 param_name = self.type_.attr_name
87 88 if evaluate_locals:
88 89 return evaluate_locals[param_name]
89 90 # use params name
90 91 return param_name
91 92 return self.type_
92 93
93 94 @classmethod
94 95 def extract(cls, val, evaluate_locals=None):
95 96 """
96 97 Extracts value from Optional() instance
97 98
98 99 :param val:
99 100 :return: original value if it's not Optional instance else
100 101 value of instance
101 102 """
102 103 if isinstance(val, cls):
103 104 return val.getval(evaluate_locals)
104 105 return val
105 106
106 107
107 108 def parse_args(cli_args, key_prefix=''):
108 109 from rhodecode.lib.utils2 import (escape_split)
109 110 kwargs = collections.defaultdict(dict)
110 111 for el in escape_split(cli_args, ','):
111 112 kv = escape_split(el, '=', 1)
112 113 if len(kv) == 2:
113 114 k, v = kv
114 115 kwargs[key_prefix + k] = v
115 116 return kwargs
116 117
117 118
118 119 def get_origin(obj):
119 120 """
120 121 Get origin of permission from object.
121 122
122 123 :param obj:
123 124 """
124 125 origin = 'permission'
125 126
126 127 if getattr(obj, 'owner_row', '') and getattr(obj, 'admin_row', ''):
127 128 # admin and owner case, maybe we should use dual string ?
128 129 origin = 'owner'
129 130 elif getattr(obj, 'owner_row', ''):
130 131 origin = 'owner'
131 132 elif getattr(obj, 'admin_row', ''):
132 133 origin = 'super-admin'
133 134 return origin
134 135
135 136
136 137 def store_update(updates, attr, name):
137 138 """
138 139 Stores param in updates dict if it's not instance of Optional
139 140 allows easy updates of passed in params
140 141 """
141 142 if not isinstance(attr, Optional):
142 143 updates[name] = attr
143 144
144 145
145 146 def has_superadmin_permission(apiuser):
146 147 """
147 148 Return True if apiuser is admin or return False
148 149
149 150 :param apiuser:
150 151 """
151 152 if HasPermissionAnyApi('hg.admin')(user=apiuser):
152 153 return True
153 154 return False
154 155
155 156
156 157 def has_repo_permissions(apiuser, repoid, repo, perms):
157 158 """
158 159 Raise JsonRPCError if apiuser is not authorized or return True
159 160
160 161 :param apiuser:
161 162 :param repoid:
162 163 :param repo:
163 164 :param perms:
164 165 """
165 166 if not HasRepoPermissionAnyApi(*perms)(
166 167 user=apiuser, repo_name=repo.repo_name):
167 168 raise JSONRPCError(
168 169 'repository `%s` does not exist' % repoid)
169 170
170 171 return True
171 172
172 173
174 def validate_repo_group_permissions(apiuser, repogroupid, repo_group, perms):
175 """
176 Raise JsonRPCError if apiuser is not authorized or return True
177
178 :param apiuser:
179 :param repogroupid: just the id of repository group
180 :param repo_group: instance of repo_group
181 :param perms:
182 """
183 if not HasRepoGroupPermissionAnyApi(*perms)(
184 user=apiuser, group_name=repo_group.group_name):
185 raise JSONRPCError(
186 'repository group `%s` does not exist' % repogroupid)
187
188 return True
189
190
191 def has_set_owner_permissions(apiuser, owner):
192 if isinstance(owner, Optional):
193 owner = get_user_or_error(apiuser.user_id)
194 else:
195 if has_superadmin_permission(apiuser):
196 owner = get_user_or_error(owner)
197 else:
198 # forbid setting owner for non-admins
199 raise JSONRPCError(
200 'Only RhodeCode super-admin can specify `owner` param')
201 return owner
202
203
173 204 def get_user_or_error(userid):
174 205 """
175 206 Get user by id or name or return JsonRPCError if not found
176 207
177 208 :param userid:
178 209 """
179 210 from rhodecode.model.user import UserModel
180 211
181 212 user_model = UserModel()
182 213 try:
183 214 user = user_model.get_user(int(userid))
184 215 except ValueError:
185 216 user = user_model.get_by_username(userid)
186 217
187 218 if user is None:
188 219 raise JSONRPCError("user `%s` does not exist" % (userid,))
189 220 return user
190 221
191 222
192 223 def get_repo_or_error(repoid):
193 224 """
194 225 Get repo by id or name or return JsonRPCError if not found
195 226
196 227 :param repoid:
197 228 """
198 229 from rhodecode.model.repo import RepoModel
199 230
200 231 repo = RepoModel().get_repo(repoid)
201 232 if repo is None:
202 233 raise JSONRPCError('repository `%s` does not exist' % (repoid,))
203 234 return repo
204 235
205 236
206 237 def get_repo_group_or_error(repogroupid):
207 238 """
208 239 Get repo group by id or name or return JsonRPCError if not found
209 240
210 241 :param repogroupid:
211 242 """
212 243 from rhodecode.model.repo_group import RepoGroupModel
213 244
214 245 repo_group = RepoGroupModel()._get_repo_group(repogroupid)
215 246 if repo_group is None:
216 247 raise JSONRPCError(
217 248 'repository group `%s` does not exist' % (repogroupid,))
218 249 return repo_group
219 250
220 251
221 252 def get_user_group_or_error(usergroupid):
222 253 """
223 254 Get user group by id or name or return JsonRPCError if not found
224 255
225 256 :param usergroupid:
226 257 """
227 258 from rhodecode.model.user_group import UserGroupModel
228 259
229 260 user_group = UserGroupModel().get_group(usergroupid)
230 261 if user_group is None:
231 262 raise JSONRPCError('user group `%s` does not exist' % (usergroupid,))
232 263 return user_group
233 264
234 265
235 266 def get_perm_or_error(permid, prefix=None):
236 267 """
237 268 Get permission by id or name or return JsonRPCError if not found
238 269
239 270 :param permid:
240 271 """
241 272 from rhodecode.model.permission import PermissionModel
242 273
243 274 perm = PermissionModel.cls.get_by_key(permid)
244 275 if perm is None:
245 276 raise JSONRPCError('permission `%s` does not exist' % (permid,))
246 277 if prefix:
247 278 if not perm.permission_name.startswith(prefix):
248 279 raise JSONRPCError('permission `%s` is invalid, '
249 280 'should start with %s' % (permid, prefix))
250 281 return perm
251 282
252 283
253 284 def get_gist_or_error(gistid):
254 285 """
255 286 Get gist by id or gist_access_id or return JsonRPCError if not found
256 287
257 288 :param gistid:
258 289 """
259 290 from rhodecode.model.gist import GistModel
260 291
261 292 gist = GistModel.cls.get_by_access_id(gistid)
262 293 if gist is None:
263 294 raise JSONRPCError('gist `%s` does not exist' % (gistid,))
264 295 return gist
265 296
266 297
267 298 def get_pull_request_or_error(pullrequestid):
268 299 """
269 300 Get pull request by id or return JsonRPCError if not found
270 301
271 302 :param pullrequestid:
272 303 """
273 304 from rhodecode.model.pull_request import PullRequestModel
274 305
275 306 try:
276 307 pull_request = PullRequestModel().get(int(pullrequestid))
277 308 except ValueError:
278 309 raise JSONRPCError('pullrequestid must be an integer')
279 310 if not pull_request:
280 311 raise JSONRPCError('pull request `%s` does not exist' % (
281 312 pullrequestid,))
282 313 return pull_request
283 314
284 315
285 316 def build_commit_data(commit, detail_level):
286 317 parsed_diff = []
287 318 if detail_level == 'extended':
288 319 for f in commit.added:
289 320 parsed_diff.append(_get_commit_dict(filename=f.path, op='A'))
290 321 for f in commit.changed:
291 322 parsed_diff.append(_get_commit_dict(filename=f.path, op='M'))
292 323 for f in commit.removed:
293 324 parsed_diff.append(_get_commit_dict(filename=f.path, op='D'))
294 325
295 326 elif detail_level == 'full':
296 327 from rhodecode.lib.diffs import DiffProcessor
297 328 diff_processor = DiffProcessor(commit.diff())
298 329 for dp in diff_processor.prepare():
299 330 del dp['stats']['ops']
300 331 _stats = dp['stats']
301 332 parsed_diff.append(_get_commit_dict(
302 333 filename=dp['filename'], op=dp['operation'],
303 334 new_revision=dp['new_revision'],
304 335 old_revision=dp['old_revision'],
305 336 raw_diff=dp['raw_diff'], stats=_stats))
306 337
307 338 return parsed_diff
308 339
309 340
310 341 def get_commit_or_error(ref, repo):
311 342 try:
312 343 ref_type, _, ref_hash = ref.split(':')
313 344 except ValueError:
314 345 raise JSONRPCError(
315 346 'Ref `{ref}` given in a wrong format. Please check the API'
316 347 ' documentation for more details'.format(ref=ref))
317 348 try:
318 349 # TODO: dan: refactor this to use repo.scm_instance().get_commit()
319 350 # once get_commit supports ref_types
320 351 return get_commit_from_ref_name(repo, ref_hash)
321 352 except RepositoryError:
322 353 raise JSONRPCError('Ref `{ref}` does not exist'.format(ref=ref))
323 354
324 355
325 356 def resolve_ref_or_error(ref, repo):
326 357 def _parse_ref(type_, name, hash_=None):
327 358 return type_, name, hash_
328 359
329 360 try:
330 361 ref_type, ref_name, ref_hash = _parse_ref(*ref.split(':'))
331 362 except TypeError:
332 363 raise JSONRPCError(
333 364 'Ref `{ref}` given in a wrong format. Please check the API'
334 365 ' documentation for more details'.format(ref=ref))
335 366
336 367 try:
337 368 ref_hash = ref_hash or _get_ref_hash(repo, ref_type, ref_name)
338 369 except (KeyError, ValueError):
339 370 raise JSONRPCError(
340 371 'The specified {type} `{name}` does not exist'.format(
341 372 type=ref_type, name=ref_name))
342 373
343 374 return ':'.join([ref_type, ref_name, ref_hash])
344 375
345 376
346 377 def _get_commit_dict(
347 378 filename, op, new_revision=None, old_revision=None,
348 379 raw_diff=None, stats=None):
349 380 if stats is None:
350 381 stats = {
351 382 "added": None,
352 383 "binary": None,
353 384 "deleted": None
354 385 }
355 386 return {
356 387 "filename": safe_unicode(filename),
357 388 "op": op,
358 389
359 390 # extra details
360 391 "new_revision": new_revision,
361 392 "old_revision": old_revision,
362 393
363 394 "raw_diff": raw_diff,
364 395 "stats": stats
365 396 }
366 397
367 398
368 399 # TODO: mikhail: Think about moving this function to some library
369 400 def _get_ref_hash(repo, type_, name):
370 401 vcs_repo = repo.scm_instance()
371 402 if type_ == 'branch' and vcs_repo.alias in ('hg', 'git'):
372 403 return vcs_repo.branches[name]
373 404 elif type_ == 'bookmark' and vcs_repo.alias == 'hg':
374 405 return vcs_repo.bookmarks[name]
375 406 else:
376 407 raise ValueError()
General Comments 0
You need to be logged in to leave comments. Login now