##// END OF EJS Templates
python3: fix usage of int/long
super-admin -
r4935:18be5f3a default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,458 +1,458 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2020 RhodeCode GmbH
3 # Copyright (C) 2014-2020 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 JSON RPC utils
22 JSON RPC utils
23 """
23 """
24
24
25 import collections
25 import collections
26 import logging
26 import logging
27
27
28 from rhodecode.api.exc import JSONRPCError
28 from rhodecode.api.exc import JSONRPCError
29 from rhodecode.lib.auth import (
29 from rhodecode.lib.auth import (
30 HasPermissionAnyApi, HasRepoPermissionAnyApi, HasRepoGroupPermissionAnyApi)
30 HasPermissionAnyApi, HasRepoPermissionAnyApi, HasRepoGroupPermissionAnyApi)
31 from rhodecode.lib.utils import safe_unicode
31 from rhodecode.lib.utils import safe_unicode
32 from rhodecode.lib.vcs.exceptions import RepositoryError
32 from rhodecode.lib.vcs.exceptions import RepositoryError
33 from rhodecode.lib.view_utils import get_commit_from_ref_name
33 from rhodecode.lib.view_utils import get_commit_from_ref_name
34 from rhodecode.lib.utils2 import str2bool
34 from rhodecode.lib.utils2 import str2bool
35
35
36 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
37
37
38
38
39 class OAttr(object):
39 class OAttr(object):
40 """
40 """
41 Special Option that defines other attribute, and can default to them
41 Special Option that defines other attribute, and can default to them
42
42
43 Example::
43 Example::
44
44
45 def test(apiuser, userid=Optional(OAttr('apiuser')):
45 def test(apiuser, userid=Optional(OAttr('apiuser')):
46 user = Optional.extract(userid, evaluate_locals=local())
46 user = Optional.extract(userid, evaluate_locals=local())
47 #if we pass in userid, we get it, else it will default to apiuser
47 #if we pass in userid, we get it, else it will default to apiuser
48 #attribute
48 #attribute
49 """
49 """
50
50
51 def __init__(self, attr_name):
51 def __init__(self, attr_name):
52 self.attr_name = attr_name
52 self.attr_name = attr_name
53
53
54 def __repr__(self):
54 def __repr__(self):
55 return '<OptionalAttr:%s>' % self.attr_name
55 return '<OptionalAttr:%s>' % self.attr_name
56
56
57 def __call__(self):
57 def __call__(self):
58 return self
58 return self
59
59
60
60
61 class Optional(object):
61 class Optional(object):
62 """
62 """
63 Defines an optional parameter::
63 Defines an optional parameter::
64
64
65 param = param.getval() if isinstance(param, Optional) else param
65 param = param.getval() if isinstance(param, Optional) else param
66 param = param() if isinstance(param, Optional) else param
66 param = param() if isinstance(param, Optional) else param
67
67
68 is equivalent of::
68 is equivalent of::
69
69
70 param = Optional.extract(param)
70 param = Optional.extract(param)
71
71
72 """
72 """
73
73
74 def __init__(self, type_):
74 def __init__(self, type_):
75 self.type_ = type_
75 self.type_ = type_
76
76
77 def __repr__(self):
77 def __repr__(self):
78 return '<Optional:%s>' % self.type_.__repr__()
78 return '<Optional:%s>' % self.type_.__repr__()
79
79
80 def __call__(self):
80 def __call__(self):
81 return self.getval()
81 return self.getval()
82
82
83 def getval(self, evaluate_locals=None):
83 def getval(self, evaluate_locals=None):
84 """
84 """
85 returns value from this Optional instance
85 returns value from this Optional instance
86 """
86 """
87 if isinstance(self.type_, OAttr):
87 if isinstance(self.type_, OAttr):
88 param_name = self.type_.attr_name
88 param_name = self.type_.attr_name
89 if evaluate_locals:
89 if evaluate_locals:
90 return evaluate_locals[param_name]
90 return evaluate_locals[param_name]
91 # use params name
91 # use params name
92 return param_name
92 return param_name
93 return self.type_
93 return self.type_
94
94
95 @classmethod
95 @classmethod
96 def extract(cls, val, evaluate_locals=None, binary=None):
96 def extract(cls, val, evaluate_locals=None, binary=None):
97 """
97 """
98 Extracts value from Optional() instance
98 Extracts value from Optional() instance
99
99
100 :param val:
100 :param val:
101 :return: original value if it's not Optional instance else
101 :return: original value if it's not Optional instance else
102 value of instance
102 value of instance
103 """
103 """
104 if isinstance(val, cls):
104 if isinstance(val, cls):
105 val = val.getval(evaluate_locals)
105 val = val.getval(evaluate_locals)
106
106
107 if binary:
107 if binary:
108 val = str2bool(val)
108 val = str2bool(val)
109
109
110 return val
110 return val
111
111
112
112
113 def parse_args(cli_args, key_prefix=''):
113 def parse_args(cli_args, key_prefix=''):
114 from rhodecode.lib.utils2 import (escape_split)
114 from rhodecode.lib.utils2 import (escape_split)
115 kwargs = collections.defaultdict(dict)
115 kwargs = collections.defaultdict(dict)
116 for el in escape_split(cli_args, ','):
116 for el in escape_split(cli_args, ','):
117 kv = escape_split(el, '=', 1)
117 kv = escape_split(el, '=', 1)
118 if len(kv) == 2:
118 if len(kv) == 2:
119 k, v = kv
119 k, v = kv
120 kwargs[key_prefix + k] = v
120 kwargs[key_prefix + k] = v
121 return kwargs
121 return kwargs
122
122
123
123
124 def get_origin(obj):
124 def get_origin(obj):
125 """
125 """
126 Get origin of permission from object.
126 Get origin of permission from object.
127
127
128 :param obj:
128 :param obj:
129 """
129 """
130 origin = 'permission'
130 origin = 'permission'
131
131
132 if getattr(obj, 'owner_row', '') and getattr(obj, 'admin_row', ''):
132 if getattr(obj, 'owner_row', '') and getattr(obj, 'admin_row', ''):
133 # admin and owner case, maybe we should use dual string ?
133 # admin and owner case, maybe we should use dual string ?
134 origin = 'owner'
134 origin = 'owner'
135 elif getattr(obj, 'owner_row', ''):
135 elif getattr(obj, 'owner_row', ''):
136 origin = 'owner'
136 origin = 'owner'
137 elif getattr(obj, 'admin_row', ''):
137 elif getattr(obj, 'admin_row', ''):
138 origin = 'super-admin'
138 origin = 'super-admin'
139 return origin
139 return origin
140
140
141
141
142 def store_update(updates, attr, name):
142 def store_update(updates, attr, name):
143 """
143 """
144 Stores param in updates dict if it's not instance of Optional
144 Stores param in updates dict if it's not instance of Optional
145 allows easy updates of passed in params
145 allows easy updates of passed in params
146 """
146 """
147 if not isinstance(attr, Optional):
147 if not isinstance(attr, Optional):
148 updates[name] = attr
148 updates[name] = attr
149
149
150
150
151 def has_superadmin_permission(apiuser):
151 def has_superadmin_permission(apiuser):
152 """
152 """
153 Return True if apiuser is admin or return False
153 Return True if apiuser is admin or return False
154
154
155 :param apiuser:
155 :param apiuser:
156 """
156 """
157 if HasPermissionAnyApi('hg.admin')(user=apiuser):
157 if HasPermissionAnyApi('hg.admin')(user=apiuser):
158 return True
158 return True
159 return False
159 return False
160
160
161
161
162 def validate_repo_permissions(apiuser, repoid, repo, perms):
162 def validate_repo_permissions(apiuser, repoid, repo, perms):
163 """
163 """
164 Raise JsonRPCError if apiuser is not authorized or return True
164 Raise JsonRPCError if apiuser is not authorized or return True
165
165
166 :param apiuser:
166 :param apiuser:
167 :param repoid:
167 :param repoid:
168 :param repo:
168 :param repo:
169 :param perms:
169 :param perms:
170 """
170 """
171 if not HasRepoPermissionAnyApi(*perms)(
171 if not HasRepoPermissionAnyApi(*perms)(
172 user=apiuser, repo_name=repo.repo_name):
172 user=apiuser, repo_name=repo.repo_name):
173 raise JSONRPCError('repository `%s` does not exist' % repoid)
173 raise JSONRPCError('repository `%s` does not exist' % repoid)
174
174
175 return True
175 return True
176
176
177
177
178 def validate_repo_group_permissions(apiuser, repogroupid, repo_group, perms):
178 def validate_repo_group_permissions(apiuser, repogroupid, repo_group, perms):
179 """
179 """
180 Raise JsonRPCError if apiuser is not authorized or return True
180 Raise JsonRPCError if apiuser is not authorized or return True
181
181
182 :param apiuser:
182 :param apiuser:
183 :param repogroupid: just the id of repository group
183 :param repogroupid: just the id of repository group
184 :param repo_group: instance of repo_group
184 :param repo_group: instance of repo_group
185 :param perms:
185 :param perms:
186 """
186 """
187 if not HasRepoGroupPermissionAnyApi(*perms)(
187 if not HasRepoGroupPermissionAnyApi(*perms)(
188 user=apiuser, group_name=repo_group.group_name):
188 user=apiuser, group_name=repo_group.group_name):
189 raise JSONRPCError(
189 raise JSONRPCError(
190 'repository group `%s` does not exist' % repogroupid)
190 'repository group `%s` does not exist' % repogroupid)
191
191
192 return True
192 return True
193
193
194
194
195 def validate_set_owner_permissions(apiuser, owner):
195 def validate_set_owner_permissions(apiuser, owner):
196 if isinstance(owner, Optional):
196 if isinstance(owner, Optional):
197 owner = get_user_or_error(apiuser.user_id)
197 owner = get_user_or_error(apiuser.user_id)
198 else:
198 else:
199 if has_superadmin_permission(apiuser):
199 if has_superadmin_permission(apiuser):
200 owner = get_user_or_error(owner)
200 owner = get_user_or_error(owner)
201 else:
201 else:
202 # forbid setting owner for non-admins
202 # forbid setting owner for non-admins
203 raise JSONRPCError(
203 raise JSONRPCError(
204 'Only RhodeCode super-admin can specify `owner` param')
204 'Only RhodeCode super-admin can specify `owner` param')
205 return owner
205 return owner
206
206
207
207
208 def get_user_or_error(userid):
208 def get_user_or_error(userid):
209 """
209 """
210 Get user by id or name or return JsonRPCError if not found
210 Get user by id or name or return JsonRPCError if not found
211
211
212 :param userid:
212 :param userid:
213 """
213 """
214 from rhodecode.model.user import UserModel
214 from rhodecode.model.user import UserModel
215 user_model = UserModel()
215 user_model = UserModel()
216
216
217 if isinstance(userid, (int, long)):
217 if isinstance(userid, int):
218 try:
218 try:
219 user = user_model.get_user(userid)
219 user = user_model.get_user(userid)
220 except ValueError:
220 except ValueError:
221 user = None
221 user = None
222 else:
222 else:
223 user = user_model.get_by_username(userid)
223 user = user_model.get_by_username(userid)
224
224
225 if user is None:
225 if user is None:
226 raise JSONRPCError(
226 raise JSONRPCError(
227 'user `%s` does not exist' % (userid,))
227 'user `%s` does not exist' % (userid,))
228 return user
228 return user
229
229
230
230
231 def get_repo_or_error(repoid):
231 def get_repo_or_error(repoid):
232 """
232 """
233 Get repo by id or name or return JsonRPCError if not found
233 Get repo by id or name or return JsonRPCError if not found
234
234
235 :param repoid:
235 :param repoid:
236 """
236 """
237 from rhodecode.model.repo import RepoModel
237 from rhodecode.model.repo import RepoModel
238 repo_model = RepoModel()
238 repo_model = RepoModel()
239
239
240 if isinstance(repoid, (int, long)):
240 if isinstance(repoid, int):
241 try:
241 try:
242 repo = repo_model.get_repo(repoid)
242 repo = repo_model.get_repo(repoid)
243 except ValueError:
243 except ValueError:
244 repo = None
244 repo = None
245 else:
245 else:
246 repo = repo_model.get_by_repo_name(repoid)
246 repo = repo_model.get_by_repo_name(repoid)
247
247
248 if repo is None:
248 if repo is None:
249 raise JSONRPCError(
249 raise JSONRPCError(
250 'repository `%s` does not exist' % (repoid,))
250 'repository `%s` does not exist' % (repoid,))
251 return repo
251 return repo
252
252
253
253
254 def get_repo_group_or_error(repogroupid):
254 def get_repo_group_or_error(repogroupid):
255 """
255 """
256 Get repo group by id or name or return JsonRPCError if not found
256 Get repo group by id or name or return JsonRPCError if not found
257
257
258 :param repogroupid:
258 :param repogroupid:
259 """
259 """
260 from rhodecode.model.repo_group import RepoGroupModel
260 from rhodecode.model.repo_group import RepoGroupModel
261 repo_group_model = RepoGroupModel()
261 repo_group_model = RepoGroupModel()
262
262
263 if isinstance(repogroupid, (int, long)):
263 if isinstance(repogroupid, int):
264 try:
264 try:
265 repo_group = repo_group_model._get_repo_group(repogroupid)
265 repo_group = repo_group_model._get_repo_group(repogroupid)
266 except ValueError:
266 except ValueError:
267 repo_group = None
267 repo_group = None
268 else:
268 else:
269 repo_group = repo_group_model.get_by_group_name(repogroupid)
269 repo_group = repo_group_model.get_by_group_name(repogroupid)
270
270
271 if repo_group is None:
271 if repo_group is None:
272 raise JSONRPCError(
272 raise JSONRPCError(
273 'repository group `%s` does not exist' % (repogroupid,))
273 'repository group `%s` does not exist' % (repogroupid,))
274 return repo_group
274 return repo_group
275
275
276
276
277 def get_user_group_or_error(usergroupid):
277 def get_user_group_or_error(usergroupid):
278 """
278 """
279 Get user group by id or name or return JsonRPCError if not found
279 Get user group by id or name or return JsonRPCError if not found
280
280
281 :param usergroupid:
281 :param usergroupid:
282 """
282 """
283 from rhodecode.model.user_group import UserGroupModel
283 from rhodecode.model.user_group import UserGroupModel
284 user_group_model = UserGroupModel()
284 user_group_model = UserGroupModel()
285
285
286 if isinstance(usergroupid, (int, long)):
286 if isinstance(usergroupid, int):
287 try:
287 try:
288 user_group = user_group_model.get_group(usergroupid)
288 user_group = user_group_model.get_group(usergroupid)
289 except ValueError:
289 except ValueError:
290 user_group = None
290 user_group = None
291 else:
291 else:
292 user_group = user_group_model.get_by_name(usergroupid)
292 user_group = user_group_model.get_by_name(usergroupid)
293
293
294 if user_group is None:
294 if user_group is None:
295 raise JSONRPCError(
295 raise JSONRPCError(
296 'user group `%s` does not exist' % (usergroupid,))
296 'user group `%s` does not exist' % (usergroupid,))
297 return user_group
297 return user_group
298
298
299
299
300 def get_perm_or_error(permid, prefix=None):
300 def get_perm_or_error(permid, prefix=None):
301 """
301 """
302 Get permission by id or name or return JsonRPCError if not found
302 Get permission by id or name or return JsonRPCError if not found
303
303
304 :param permid:
304 :param permid:
305 """
305 """
306 from rhodecode.model.permission import PermissionModel
306 from rhodecode.model.permission import PermissionModel
307
307
308 perm = PermissionModel.cls.get_by_key(permid)
308 perm = PermissionModel.cls.get_by_key(permid)
309 if perm is None:
309 if perm is None:
310 msg = 'permission `{}` does not exist.'.format(permid)
310 msg = 'permission `{}` does not exist.'.format(permid)
311 if prefix:
311 if prefix:
312 msg += ' Permission should start with prefix: `{}`'.format(prefix)
312 msg += ' Permission should start with prefix: `{}`'.format(prefix)
313 raise JSONRPCError(msg)
313 raise JSONRPCError(msg)
314
314
315 if prefix:
315 if prefix:
316 if not perm.permission_name.startswith(prefix):
316 if not perm.permission_name.startswith(prefix):
317 raise JSONRPCError('permission `%s` is invalid, '
317 raise JSONRPCError('permission `%s` is invalid, '
318 'should start with %s' % (permid, prefix))
318 'should start with %s' % (permid, prefix))
319 return perm
319 return perm
320
320
321
321
322 def get_gist_or_error(gistid):
322 def get_gist_or_error(gistid):
323 """
323 """
324 Get gist by id or gist_access_id or return JsonRPCError if not found
324 Get gist by id or gist_access_id or return JsonRPCError if not found
325
325
326 :param gistid:
326 :param gistid:
327 """
327 """
328 from rhodecode.model.gist import GistModel
328 from rhodecode.model.gist import GistModel
329
329
330 gist = GistModel.cls.get_by_access_id(gistid)
330 gist = GistModel.cls.get_by_access_id(gistid)
331 if gist is None:
331 if gist is None:
332 raise JSONRPCError('gist `%s` does not exist' % (gistid,))
332 raise JSONRPCError('gist `%s` does not exist' % (gistid,))
333 return gist
333 return gist
334
334
335
335
336 def get_pull_request_or_error(pullrequestid):
336 def get_pull_request_or_error(pullrequestid):
337 """
337 """
338 Get pull request by id or return JsonRPCError if not found
338 Get pull request by id or return JsonRPCError if not found
339
339
340 :param pullrequestid:
340 :param pullrequestid:
341 """
341 """
342 from rhodecode.model.pull_request import PullRequestModel
342 from rhodecode.model.pull_request import PullRequestModel
343
343
344 try:
344 try:
345 pull_request = PullRequestModel().get(int(pullrequestid))
345 pull_request = PullRequestModel().get(int(pullrequestid))
346 except ValueError:
346 except ValueError:
347 raise JSONRPCError('pullrequestid must be an integer')
347 raise JSONRPCError('pullrequestid must be an integer')
348 if not pull_request:
348 if not pull_request:
349 raise JSONRPCError('pull request `%s` does not exist' % (
349 raise JSONRPCError('pull request `%s` does not exist' % (
350 pullrequestid,))
350 pullrequestid,))
351 return pull_request
351 return pull_request
352
352
353
353
354 def build_commit_data(rhodecode_vcs_repo, commit, detail_level):
354 def build_commit_data(rhodecode_vcs_repo, commit, detail_level):
355 commit2 = commit
355 commit2 = commit
356 commit1 = commit.first_parent
356 commit1 = commit.first_parent
357
357
358 parsed_diff = []
358 parsed_diff = []
359 if detail_level == 'extended':
359 if detail_level == 'extended':
360 for f_path in commit.added_paths:
360 for f_path in commit.added_paths:
361 parsed_diff.append(_get_commit_dict(filename=f_path, op='A'))
361 parsed_diff.append(_get_commit_dict(filename=f_path, op='A'))
362 for f_path in commit.changed_paths:
362 for f_path in commit.changed_paths:
363 parsed_diff.append(_get_commit_dict(filename=f_path, op='M'))
363 parsed_diff.append(_get_commit_dict(filename=f_path, op='M'))
364 for f_path in commit.removed_paths:
364 for f_path in commit.removed_paths:
365 parsed_diff.append(_get_commit_dict(filename=f_path, op='D'))
365 parsed_diff.append(_get_commit_dict(filename=f_path, op='D'))
366
366
367 elif detail_level == 'full':
367 elif detail_level == 'full':
368 from rhodecode.lib import diffs
368 from rhodecode.lib import diffs
369
369
370 _diff = rhodecode_vcs_repo.get_diff(commit1, commit2,)
370 _diff = rhodecode_vcs_repo.get_diff(commit1, commit2,)
371 diff_processor = diffs.DiffProcessor(_diff, format='newdiff', show_full_diff=True)
371 diff_processor = diffs.DiffProcessor(_diff, format='newdiff', show_full_diff=True)
372
372
373 for dp in diff_processor.prepare():
373 for dp in diff_processor.prepare():
374 del dp['stats']['ops']
374 del dp['stats']['ops']
375 _stats = dp['stats']
375 _stats = dp['stats']
376 parsed_diff.append(_get_commit_dict(
376 parsed_diff.append(_get_commit_dict(
377 filename=dp['filename'], op=dp['operation'],
377 filename=dp['filename'], op=dp['operation'],
378 new_revision=dp['new_revision'],
378 new_revision=dp['new_revision'],
379 old_revision=dp['old_revision'],
379 old_revision=dp['old_revision'],
380 raw_diff=dp['raw_diff'], stats=_stats))
380 raw_diff=dp['raw_diff'], stats=_stats))
381
381
382 return parsed_diff
382 return parsed_diff
383
383
384
384
385 def get_commit_or_error(ref, repo):
385 def get_commit_or_error(ref, repo):
386 try:
386 try:
387 ref_type, _, ref_hash = ref.split(':')
387 ref_type, _, ref_hash = ref.split(':')
388 except ValueError:
388 except ValueError:
389 raise JSONRPCError(
389 raise JSONRPCError(
390 'Ref `{ref}` given in a wrong format. Please check the API'
390 'Ref `{ref}` given in a wrong format. Please check the API'
391 ' documentation for more details'.format(ref=ref))
391 ' documentation for more details'.format(ref=ref))
392 try:
392 try:
393 # TODO: dan: refactor this to use repo.scm_instance().get_commit()
393 # TODO: dan: refactor this to use repo.scm_instance().get_commit()
394 # once get_commit supports ref_types
394 # once get_commit supports ref_types
395 return get_commit_from_ref_name(repo, ref_hash)
395 return get_commit_from_ref_name(repo, ref_hash)
396 except RepositoryError:
396 except RepositoryError:
397 raise JSONRPCError('Ref `{ref}` does not exist'.format(ref=ref))
397 raise JSONRPCError('Ref `{ref}` does not exist'.format(ref=ref))
398
398
399
399
400 def _get_ref_hash(repo, type_, name):
400 def _get_ref_hash(repo, type_, name):
401 vcs_repo = repo.scm_instance()
401 vcs_repo = repo.scm_instance()
402 if type_ in ['branch'] and vcs_repo.alias in ('hg', 'git'):
402 if type_ in ['branch'] and vcs_repo.alias in ('hg', 'git'):
403 return vcs_repo.branches[name]
403 return vcs_repo.branches[name]
404 elif type_ in ['bookmark', 'book'] and vcs_repo.alias == 'hg':
404 elif type_ in ['bookmark', 'book'] and vcs_repo.alias == 'hg':
405 return vcs_repo.bookmarks[name]
405 return vcs_repo.bookmarks[name]
406 else:
406 else:
407 raise ValueError()
407 raise ValueError()
408
408
409
409
410 def resolve_ref_or_error(ref, repo, allowed_ref_types=None):
410 def resolve_ref_or_error(ref, repo, allowed_ref_types=None):
411 allowed_ref_types = allowed_ref_types or ['bookmark', 'book', 'tag', 'branch']
411 allowed_ref_types = allowed_ref_types or ['bookmark', 'book', 'tag', 'branch']
412
412
413 def _parse_ref(type_, name, hash_=None):
413 def _parse_ref(type_, name, hash_=None):
414 return type_, name, hash_
414 return type_, name, hash_
415
415
416 try:
416 try:
417 ref_type, ref_name, ref_hash = _parse_ref(*ref.split(':'))
417 ref_type, ref_name, ref_hash = _parse_ref(*ref.split(':'))
418 except TypeError:
418 except TypeError:
419 raise JSONRPCError(
419 raise JSONRPCError(
420 'Ref `{ref}` given in a wrong format. Please check the API'
420 'Ref `{ref}` given in a wrong format. Please check the API'
421 ' documentation for more details'.format(ref=ref))
421 ' documentation for more details'.format(ref=ref))
422
422
423 if ref_type not in allowed_ref_types:
423 if ref_type not in allowed_ref_types:
424 raise JSONRPCError(
424 raise JSONRPCError(
425 'Ref `{ref}` type is not allowed. '
425 'Ref `{ref}` type is not allowed. '
426 'Only:{allowed_refs} are possible.'.format(
426 'Only:{allowed_refs} are possible.'.format(
427 ref=ref, allowed_refs=allowed_ref_types))
427 ref=ref, allowed_refs=allowed_ref_types))
428
428
429 try:
429 try:
430 ref_hash = ref_hash or _get_ref_hash(repo, ref_type, ref_name)
430 ref_hash = ref_hash or _get_ref_hash(repo, ref_type, ref_name)
431 except (KeyError, ValueError):
431 except (KeyError, ValueError):
432 raise JSONRPCError(
432 raise JSONRPCError(
433 'The specified value:{type}:`{name}` does not exist, or is not allowed.'.format(
433 'The specified value:{type}:`{name}` does not exist, or is not allowed.'.format(
434 type=ref_type, name=ref_name))
434 type=ref_type, name=ref_name))
435
435
436 return ':'.join([ref_type, ref_name, ref_hash])
436 return ':'.join([ref_type, ref_name, ref_hash])
437
437
438
438
439 def _get_commit_dict(
439 def _get_commit_dict(
440 filename, op, new_revision=None, old_revision=None,
440 filename, op, new_revision=None, old_revision=None,
441 raw_diff=None, stats=None):
441 raw_diff=None, stats=None):
442 if stats is None:
442 if stats is None:
443 stats = {
443 stats = {
444 "added": None,
444 "added": None,
445 "binary": None,
445 "binary": None,
446 "deleted": None
446 "deleted": None
447 }
447 }
448 return {
448 return {
449 "filename": safe_unicode(filename),
449 "filename": safe_unicode(filename),
450 "op": op,
450 "op": op,
451
451
452 # extra details
452 # extra details
453 "new_revision": new_revision,
453 "new_revision": new_revision,
454 "old_revision": old_revision,
454 "old_revision": old_revision,
455
455
456 "raw_diff": raw_diff,
456 "raw_diff": raw_diff,
457 "stats": stats
457 "stats": stats
458 }
458 }
@@ -1,818 +1,818 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 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 Authentication modules
22 Authentication modules
23 """
23 """
24 import socket
24 import socket
25 import string
25 import string
26 import colander
26 import colander
27 import copy
27 import copy
28 import logging
28 import logging
29 import time
29 import time
30 import traceback
30 import traceback
31 import warnings
31 import warnings
32 import functools
32 import functools
33
33
34 from pyramid.threadlocal import get_current_registry
34 from pyramid.threadlocal import get_current_registry
35
35
36 from rhodecode.authentication.interface import IAuthnPluginRegistry
36 from rhodecode.authentication.interface import IAuthnPluginRegistry
37 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
37 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
38 from rhodecode.lib import rc_cache
38 from rhodecode.lib import rc_cache
39 from rhodecode.lib.statsd_client import StatsdClient
39 from rhodecode.lib.statsd_client import StatsdClient
40 from rhodecode.lib.auth import PasswordGenerator, _RhodeCodeCryptoBCrypt
40 from rhodecode.lib.auth import PasswordGenerator, _RhodeCodeCryptoBCrypt
41 from rhodecode.lib.utils2 import safe_int, safe_str
41 from rhodecode.lib.utils2 import safe_int, safe_str
42 from rhodecode.lib.exceptions import (LdapConnectionError, LdapUsernameError, LdapPasswordError)
42 from rhodecode.lib.exceptions import (LdapConnectionError, LdapUsernameError, LdapPasswordError)
43 from rhodecode.model.db import User
43 from rhodecode.model.db import User
44 from rhodecode.model.meta import Session
44 from rhodecode.model.meta import Session
45 from rhodecode.model.settings import SettingsModel
45 from rhodecode.model.settings import SettingsModel
46 from rhodecode.model.user import UserModel
46 from rhodecode.model.user import UserModel
47 from rhodecode.model.user_group import UserGroupModel
47 from rhodecode.model.user_group import UserGroupModel
48
48
49
49
50 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
51
51
52 # auth types that authenticate() function can receive
52 # auth types that authenticate() function can receive
53 VCS_TYPE = 'vcs'
53 VCS_TYPE = 'vcs'
54 HTTP_TYPE = 'http'
54 HTTP_TYPE = 'http'
55
55
56 external_auth_session_key = 'rhodecode.external_auth'
56 external_auth_session_key = 'rhodecode.external_auth'
57
57
58
58
59 class hybrid_property(object):
59 class hybrid_property(object):
60 """
60 """
61 a property decorator that works both for instance and class
61 a property decorator that works both for instance and class
62 """
62 """
63 def __init__(self, fget, fset=None, fdel=None, expr=None):
63 def __init__(self, fget, fset=None, fdel=None, expr=None):
64 self.fget = fget
64 self.fget = fget
65 self.fset = fset
65 self.fset = fset
66 self.fdel = fdel
66 self.fdel = fdel
67 self.expr = expr or fget
67 self.expr = expr or fget
68 functools.update_wrapper(self, fget)
68 functools.update_wrapper(self, fget)
69
69
70 def __get__(self, instance, owner):
70 def __get__(self, instance, owner):
71 if instance is None:
71 if instance is None:
72 return self.expr(owner)
72 return self.expr(owner)
73 else:
73 else:
74 return self.fget(instance)
74 return self.fget(instance)
75
75
76 def __set__(self, instance, value):
76 def __set__(self, instance, value):
77 self.fset(instance, value)
77 self.fset(instance, value)
78
78
79 def __delete__(self, instance):
79 def __delete__(self, instance):
80 self.fdel(instance)
80 self.fdel(instance)
81
81
82
82
83 class LazyFormencode(object):
83 class LazyFormencode(object):
84 def __init__(self, formencode_obj, *args, **kwargs):
84 def __init__(self, formencode_obj, *args, **kwargs):
85 self.formencode_obj = formencode_obj
85 self.formencode_obj = formencode_obj
86 self.args = args
86 self.args = args
87 self.kwargs = kwargs
87 self.kwargs = kwargs
88
88
89 def __call__(self, *args, **kwargs):
89 def __call__(self, *args, **kwargs):
90 from inspect import isfunction
90 from inspect import isfunction
91 formencode_obj = self.formencode_obj
91 formencode_obj = self.formencode_obj
92 if isfunction(formencode_obj):
92 if isfunction(formencode_obj):
93 # case we wrap validators into functions
93 # case we wrap validators into functions
94 formencode_obj = self.formencode_obj(*args, **kwargs)
94 formencode_obj = self.formencode_obj(*args, **kwargs)
95 return formencode_obj(*self.args, **self.kwargs)
95 return formencode_obj(*self.args, **self.kwargs)
96
96
97
97
98 class RhodeCodeAuthPluginBase(object):
98 class RhodeCodeAuthPluginBase(object):
99 # UID is used to register plugin to the registry
99 # UID is used to register plugin to the registry
100 uid = None
100 uid = None
101
101
102 # cache the authentication request for N amount of seconds. Some kind
102 # cache the authentication request for N amount of seconds. Some kind
103 # of authentication methods are very heavy and it's very efficient to cache
103 # of authentication methods are very heavy and it's very efficient to cache
104 # the result of a call. If it's set to None (default) cache is off
104 # the result of a call. If it's set to None (default) cache is off
105 AUTH_CACHE_TTL = None
105 AUTH_CACHE_TTL = None
106 AUTH_CACHE = {}
106 AUTH_CACHE = {}
107
107
108 auth_func_attrs = {
108 auth_func_attrs = {
109 "username": "unique username",
109 "username": "unique username",
110 "firstname": "first name",
110 "firstname": "first name",
111 "lastname": "last name",
111 "lastname": "last name",
112 "email": "email address",
112 "email": "email address",
113 "groups": '["list", "of", "groups"]',
113 "groups": '["list", "of", "groups"]',
114 "user_group_sync":
114 "user_group_sync":
115 'True|False defines if returned user groups should be synced',
115 'True|False defines if returned user groups should be synced',
116 "extern_name": "name in external source of record",
116 "extern_name": "name in external source of record",
117 "extern_type": "type of external source of record",
117 "extern_type": "type of external source of record",
118 "admin": 'True|False defines if user should be RhodeCode super admin',
118 "admin": 'True|False defines if user should be RhodeCode super admin',
119 "active":
119 "active":
120 'True|False defines active state of user internally for RhodeCode',
120 'True|False defines active state of user internally for RhodeCode',
121 "active_from_extern":
121 "active_from_extern":
122 "True|False|None, active state from the external auth, "
122 "True|False|None, active state from the external auth, "
123 "None means use definition from RhodeCode extern_type active value"
123 "None means use definition from RhodeCode extern_type active value"
124
124
125 }
125 }
126 # set on authenticate() method and via set_auth_type func.
126 # set on authenticate() method and via set_auth_type func.
127 auth_type = None
127 auth_type = None
128
128
129 # set on authenticate() method and via set_calling_scope_repo, this is a
129 # set on authenticate() method and via set_calling_scope_repo, this is a
130 # calling scope repository when doing authentication most likely on VCS
130 # calling scope repository when doing authentication most likely on VCS
131 # operations
131 # operations
132 acl_repo_name = None
132 acl_repo_name = None
133
133
134 # List of setting names to store encrypted. Plugins may override this list
134 # List of setting names to store encrypted. Plugins may override this list
135 # to store settings encrypted.
135 # to store settings encrypted.
136 _settings_encrypted = []
136 _settings_encrypted = []
137
137
138 # Mapping of python to DB settings model types. Plugins may override or
138 # Mapping of python to DB settings model types. Plugins may override or
139 # extend this mapping.
139 # extend this mapping.
140 _settings_type_map = {
140 _settings_type_map = {
141 colander.String: 'unicode',
141 colander.String: 'unicode',
142 colander.Integer: 'int',
142 colander.Integer: 'int',
143 colander.Boolean: 'bool',
143 colander.Boolean: 'bool',
144 colander.List: 'list',
144 colander.List: 'list',
145 }
145 }
146
146
147 # list of keys in settings that are unsafe to be logged, should be passwords
147 # list of keys in settings that are unsafe to be logged, should be passwords
148 # or other crucial credentials
148 # or other crucial credentials
149 _settings_unsafe_keys = []
149 _settings_unsafe_keys = []
150
150
151 def __init__(self, plugin_id):
151 def __init__(self, plugin_id):
152 self._plugin_id = plugin_id
152 self._plugin_id = plugin_id
153
153
154 def __str__(self):
154 def __str__(self):
155 return self.get_id()
155 return self.get_id()
156
156
157 def _get_setting_full_name(self, name):
157 def _get_setting_full_name(self, name):
158 """
158 """
159 Return the full setting name used for storing values in the database.
159 Return the full setting name used for storing values in the database.
160 """
160 """
161 # TODO: johbo: Using the name here is problematic. It would be good to
161 # TODO: johbo: Using the name here is problematic. It would be good to
162 # introduce either new models in the database to hold Plugin and
162 # introduce either new models in the database to hold Plugin and
163 # PluginSetting or to use the plugin id here.
163 # PluginSetting or to use the plugin id here.
164 return 'auth_{}_{}'.format(self.name, name)
164 return 'auth_{}_{}'.format(self.name, name)
165
165
166 def _get_setting_type(self, name):
166 def _get_setting_type(self, name):
167 """
167 """
168 Return the type of a setting. This type is defined by the SettingsModel
168 Return the type of a setting. This type is defined by the SettingsModel
169 and determines how the setting is stored in DB. Optionally the suffix
169 and determines how the setting is stored in DB. Optionally the suffix
170 `.encrypted` is appended to instruct SettingsModel to store it
170 `.encrypted` is appended to instruct SettingsModel to store it
171 encrypted.
171 encrypted.
172 """
172 """
173 schema_node = self.get_settings_schema().get(name)
173 schema_node = self.get_settings_schema().get(name)
174 db_type = self._settings_type_map.get(
174 db_type = self._settings_type_map.get(
175 type(schema_node.typ), 'unicode')
175 type(schema_node.typ), 'unicode')
176 if name in self._settings_encrypted:
176 if name in self._settings_encrypted:
177 db_type = '{}.encrypted'.format(db_type)
177 db_type = '{}.encrypted'.format(db_type)
178 return db_type
178 return db_type
179
179
180 @classmethod
180 @classmethod
181 def docs(cls):
181 def docs(cls):
182 """
182 """
183 Defines documentation url which helps with plugin setup
183 Defines documentation url which helps with plugin setup
184 """
184 """
185 return ''
185 return ''
186
186
187 @classmethod
187 @classmethod
188 def icon(cls):
188 def icon(cls):
189 """
189 """
190 Defines ICON in SVG format for authentication method
190 Defines ICON in SVG format for authentication method
191 """
191 """
192 return ''
192 return ''
193
193
194 def is_enabled(self):
194 def is_enabled(self):
195 """
195 """
196 Returns true if this plugin is enabled. An enabled plugin can be
196 Returns true if this plugin is enabled. An enabled plugin can be
197 configured in the admin interface but it is not consulted during
197 configured in the admin interface but it is not consulted during
198 authentication.
198 authentication.
199 """
199 """
200 auth_plugins = SettingsModel().get_auth_plugins()
200 auth_plugins = SettingsModel().get_auth_plugins()
201 return self.get_id() in auth_plugins
201 return self.get_id() in auth_plugins
202
202
203 def is_active(self, plugin_cached_settings=None):
203 def is_active(self, plugin_cached_settings=None):
204 """
204 """
205 Returns true if the plugin is activated. An activated plugin is
205 Returns true if the plugin is activated. An activated plugin is
206 consulted during authentication, assumed it is also enabled.
206 consulted during authentication, assumed it is also enabled.
207 """
207 """
208 return self.get_setting_by_name(
208 return self.get_setting_by_name(
209 'enabled', plugin_cached_settings=plugin_cached_settings)
209 'enabled', plugin_cached_settings=plugin_cached_settings)
210
210
211 def get_id(self):
211 def get_id(self):
212 """
212 """
213 Returns the plugin id.
213 Returns the plugin id.
214 """
214 """
215 return self._plugin_id
215 return self._plugin_id
216
216
217 def get_display_name(self, load_from_settings=False):
217 def get_display_name(self, load_from_settings=False):
218 """
218 """
219 Returns a translation string for displaying purposes.
219 Returns a translation string for displaying purposes.
220 if load_from_settings is set, plugin settings can override the display name
220 if load_from_settings is set, plugin settings can override the display name
221 """
221 """
222 raise NotImplementedError('Not implemented in base class')
222 raise NotImplementedError('Not implemented in base class')
223
223
224 def get_settings_schema(self):
224 def get_settings_schema(self):
225 """
225 """
226 Returns a colander schema, representing the plugin settings.
226 Returns a colander schema, representing the plugin settings.
227 """
227 """
228 return AuthnPluginSettingsSchemaBase()
228 return AuthnPluginSettingsSchemaBase()
229
229
230 def _propagate_settings(self, raw_settings):
230 def _propagate_settings(self, raw_settings):
231 settings = {}
231 settings = {}
232 for node in self.get_settings_schema():
232 for node in self.get_settings_schema():
233 settings[node.name] = self.get_setting_by_name(
233 settings[node.name] = self.get_setting_by_name(
234 node.name, plugin_cached_settings=raw_settings)
234 node.name, plugin_cached_settings=raw_settings)
235 return settings
235 return settings
236
236
237 def get_settings(self, use_cache=True):
237 def get_settings(self, use_cache=True):
238 """
238 """
239 Returns the plugin settings as dictionary.
239 Returns the plugin settings as dictionary.
240 """
240 """
241
241
242 raw_settings = SettingsModel().get_all_settings(cache=use_cache)
242 raw_settings = SettingsModel().get_all_settings(cache=use_cache)
243 settings = self._propagate_settings(raw_settings)
243 settings = self._propagate_settings(raw_settings)
244
244
245 return settings
245 return settings
246
246
247 def get_setting_by_name(self, name, default=None, plugin_cached_settings=None):
247 def get_setting_by_name(self, name, default=None, plugin_cached_settings=None):
248 """
248 """
249 Returns a plugin setting by name.
249 Returns a plugin setting by name.
250 """
250 """
251 full_name = 'rhodecode_{}'.format(self._get_setting_full_name(name))
251 full_name = 'rhodecode_{}'.format(self._get_setting_full_name(name))
252 if plugin_cached_settings:
252 if plugin_cached_settings:
253 plugin_settings = plugin_cached_settings
253 plugin_settings = plugin_cached_settings
254 else:
254 else:
255 plugin_settings = SettingsModel().get_all_settings()
255 plugin_settings = SettingsModel().get_all_settings()
256
256
257 if full_name in plugin_settings:
257 if full_name in plugin_settings:
258 return plugin_settings[full_name]
258 return plugin_settings[full_name]
259 else:
259 else:
260 return default
260 return default
261
261
262 def create_or_update_setting(self, name, value):
262 def create_or_update_setting(self, name, value):
263 """
263 """
264 Create or update a setting for this plugin in the persistent storage.
264 Create or update a setting for this plugin in the persistent storage.
265 """
265 """
266 full_name = self._get_setting_full_name(name)
266 full_name = self._get_setting_full_name(name)
267 type_ = self._get_setting_type(name)
267 type_ = self._get_setting_type(name)
268 db_setting = SettingsModel().create_or_update_setting(
268 db_setting = SettingsModel().create_or_update_setting(
269 full_name, value, type_)
269 full_name, value, type_)
270 return db_setting.app_settings_value
270 return db_setting.app_settings_value
271
271
272 def log_safe_settings(self, settings):
272 def log_safe_settings(self, settings):
273 """
273 """
274 returns a log safe representation of settings, without any secrets
274 returns a log safe representation of settings, without any secrets
275 """
275 """
276 settings_copy = copy.deepcopy(settings)
276 settings_copy = copy.deepcopy(settings)
277 for k in self._settings_unsafe_keys:
277 for k in self._settings_unsafe_keys:
278 if k in settings_copy:
278 if k in settings_copy:
279 del settings_copy[k]
279 del settings_copy[k]
280 return settings_copy
280 return settings_copy
281
281
282 @hybrid_property
282 @hybrid_property
283 def name(self):
283 def name(self):
284 """
284 """
285 Returns the name of this authentication plugin.
285 Returns the name of this authentication plugin.
286
286
287 :returns: string
287 :returns: string
288 """
288 """
289 raise NotImplementedError("Not implemented in base class")
289 raise NotImplementedError("Not implemented in base class")
290
290
291 def get_url_slug(self):
291 def get_url_slug(self):
292 """
292 """
293 Returns a slug which should be used when constructing URLs which refer
293 Returns a slug which should be used when constructing URLs which refer
294 to this plugin. By default it returns the plugin name. If the name is
294 to this plugin. By default it returns the plugin name. If the name is
295 not suitable for using it in an URL the plugin should override this
295 not suitable for using it in an URL the plugin should override this
296 method.
296 method.
297 """
297 """
298 return self.name
298 return self.name
299
299
300 @property
300 @property
301 def is_headers_auth(self):
301 def is_headers_auth(self):
302 """
302 """
303 Returns True if this authentication plugin uses HTTP headers as
303 Returns True if this authentication plugin uses HTTP headers as
304 authentication method.
304 authentication method.
305 """
305 """
306 return False
306 return False
307
307
308 @hybrid_property
308 @hybrid_property
309 def is_container_auth(self):
309 def is_container_auth(self):
310 """
310 """
311 Deprecated method that indicates if this authentication plugin uses
311 Deprecated method that indicates if this authentication plugin uses
312 HTTP headers as authentication method.
312 HTTP headers as authentication method.
313 """
313 """
314 warnings.warn(
314 warnings.warn(
315 'Use is_headers_auth instead.', category=DeprecationWarning)
315 'Use is_headers_auth instead.', category=DeprecationWarning)
316 return self.is_headers_auth
316 return self.is_headers_auth
317
317
318 @hybrid_property
318 @hybrid_property
319 def allows_creating_users(self):
319 def allows_creating_users(self):
320 """
320 """
321 Defines if Plugin allows users to be created on-the-fly when
321 Defines if Plugin allows users to be created on-the-fly when
322 authentication is called. Controls how external plugins should behave
322 authentication is called. Controls how external plugins should behave
323 in terms if they are allowed to create new users, or not. Base plugins
323 in terms if they are allowed to create new users, or not. Base plugins
324 should not be allowed to, but External ones should be !
324 should not be allowed to, but External ones should be !
325
325
326 :return: bool
326 :return: bool
327 """
327 """
328 return False
328 return False
329
329
330 def set_auth_type(self, auth_type):
330 def set_auth_type(self, auth_type):
331 self.auth_type = auth_type
331 self.auth_type = auth_type
332
332
333 def set_calling_scope_repo(self, acl_repo_name):
333 def set_calling_scope_repo(self, acl_repo_name):
334 self.acl_repo_name = acl_repo_name
334 self.acl_repo_name = acl_repo_name
335
335
336 def allows_authentication_from(
336 def allows_authentication_from(
337 self, user, allows_non_existing_user=True,
337 self, user, allows_non_existing_user=True,
338 allowed_auth_plugins=None, allowed_auth_sources=None):
338 allowed_auth_plugins=None, allowed_auth_sources=None):
339 """
339 """
340 Checks if this authentication module should accept a request for
340 Checks if this authentication module should accept a request for
341 the current user.
341 the current user.
342
342
343 :param user: user object fetched using plugin's get_user() method.
343 :param user: user object fetched using plugin's get_user() method.
344 :param allows_non_existing_user: if True, don't allow the
344 :param allows_non_existing_user: if True, don't allow the
345 user to be empty, meaning not existing in our database
345 user to be empty, meaning not existing in our database
346 :param allowed_auth_plugins: if provided, users extern_type will be
346 :param allowed_auth_plugins: if provided, users extern_type will be
347 checked against a list of provided extern types, which are plugin
347 checked against a list of provided extern types, which are plugin
348 auth_names in the end
348 auth_names in the end
349 :param allowed_auth_sources: authentication type allowed,
349 :param allowed_auth_sources: authentication type allowed,
350 `http` or `vcs` default is both.
350 `http` or `vcs` default is both.
351 defines if plugin will accept only http authentication vcs
351 defines if plugin will accept only http authentication vcs
352 authentication(git/hg) or both
352 authentication(git/hg) or both
353 :returns: boolean
353 :returns: boolean
354 """
354 """
355 if not user and not allows_non_existing_user:
355 if not user and not allows_non_existing_user:
356 log.debug('User is empty but plugin does not allow empty users,'
356 log.debug('User is empty but plugin does not allow empty users,'
357 'not allowed to authenticate')
357 'not allowed to authenticate')
358 return False
358 return False
359
359
360 expected_auth_plugins = allowed_auth_plugins or [self.name]
360 expected_auth_plugins = allowed_auth_plugins or [self.name]
361 if user and (user.extern_type and
361 if user and (user.extern_type and
362 user.extern_type not in expected_auth_plugins):
362 user.extern_type not in expected_auth_plugins):
363 log.debug(
363 log.debug(
364 'User `%s` is bound to `%s` auth type. Plugin allows only '
364 'User `%s` is bound to `%s` auth type. Plugin allows only '
365 '%s, skipping', user, user.extern_type, expected_auth_plugins)
365 '%s, skipping', user, user.extern_type, expected_auth_plugins)
366
366
367 return False
367 return False
368
368
369 # by default accept both
369 # by default accept both
370 expected_auth_from = allowed_auth_sources or [HTTP_TYPE, VCS_TYPE]
370 expected_auth_from = allowed_auth_sources or [HTTP_TYPE, VCS_TYPE]
371 if self.auth_type not in expected_auth_from:
371 if self.auth_type not in expected_auth_from:
372 log.debug('Current auth source is %s but plugin only allows %s',
372 log.debug('Current auth source is %s but plugin only allows %s',
373 self.auth_type, expected_auth_from)
373 self.auth_type, expected_auth_from)
374 return False
374 return False
375
375
376 return True
376 return True
377
377
378 def get_user(self, username=None, **kwargs):
378 def get_user(self, username=None, **kwargs):
379 """
379 """
380 Helper method for user fetching in plugins, by default it's using
380 Helper method for user fetching in plugins, by default it's using
381 simple fetch by username, but this method can be custimized in plugins
381 simple fetch by username, but this method can be custimized in plugins
382 eg. headers auth plugin to fetch user by environ params
382 eg. headers auth plugin to fetch user by environ params
383
383
384 :param username: username if given to fetch from database
384 :param username: username if given to fetch from database
385 :param kwargs: extra arguments needed for user fetching.
385 :param kwargs: extra arguments needed for user fetching.
386 """
386 """
387 user = None
387 user = None
388 log.debug(
388 log.debug(
389 'Trying to fetch user `%s` from RhodeCode database', username)
389 'Trying to fetch user `%s` from RhodeCode database', username)
390 if username:
390 if username:
391 user = User.get_by_username(username)
391 user = User.get_by_username(username)
392 if not user:
392 if not user:
393 log.debug('User not found, fallback to fetch user in '
393 log.debug('User not found, fallback to fetch user in '
394 'case insensitive mode')
394 'case insensitive mode')
395 user = User.get_by_username(username, case_insensitive=True)
395 user = User.get_by_username(username, case_insensitive=True)
396 else:
396 else:
397 log.debug('provided username:`%s` is empty skipping...', username)
397 log.debug('provided username:`%s` is empty skipping...', username)
398 if not user:
398 if not user:
399 log.debug('User `%s` not found in database', username)
399 log.debug('User `%s` not found in database', username)
400 else:
400 else:
401 log.debug('Got DB user:%s', user)
401 log.debug('Got DB user:%s', user)
402 return user
402 return user
403
403
404 def user_activation_state(self):
404 def user_activation_state(self):
405 """
405 """
406 Defines user activation state when creating new users
406 Defines user activation state when creating new users
407
407
408 :returns: boolean
408 :returns: boolean
409 """
409 """
410 raise NotImplementedError("Not implemented in base class")
410 raise NotImplementedError("Not implemented in base class")
411
411
412 def auth(self, userobj, username, passwd, settings, **kwargs):
412 def auth(self, userobj, username, passwd, settings, **kwargs):
413 """
413 """
414 Given a user object (which may be null), username, a plaintext
414 Given a user object (which may be null), username, a plaintext
415 password, and a settings object (containing all the keys needed as
415 password, and a settings object (containing all the keys needed as
416 listed in settings()), authenticate this user's login attempt.
416 listed in settings()), authenticate this user's login attempt.
417
417
418 Return None on failure. On success, return a dictionary of the form:
418 Return None on failure. On success, return a dictionary of the form:
419
419
420 see: RhodeCodeAuthPluginBase.auth_func_attrs
420 see: RhodeCodeAuthPluginBase.auth_func_attrs
421 This is later validated for correctness
421 This is later validated for correctness
422 """
422 """
423 raise NotImplementedError("not implemented in base class")
423 raise NotImplementedError("not implemented in base class")
424
424
425 def _authenticate(self, userobj, username, passwd, settings, **kwargs):
425 def _authenticate(self, userobj, username, passwd, settings, **kwargs):
426 """
426 """
427 Wrapper to call self.auth() that validates call on it
427 Wrapper to call self.auth() that validates call on it
428
428
429 :param userobj: userobj
429 :param userobj: userobj
430 :param username: username
430 :param username: username
431 :param passwd: plaintext password
431 :param passwd: plaintext password
432 :param settings: plugin settings
432 :param settings: plugin settings
433 """
433 """
434 auth = self.auth(userobj, username, passwd, settings, **kwargs)
434 auth = self.auth(userobj, username, passwd, settings, **kwargs)
435 if auth:
435 if auth:
436 auth['_plugin'] = self.name
436 auth['_plugin'] = self.name
437 auth['_ttl_cache'] = self.get_ttl_cache(settings)
437 auth['_ttl_cache'] = self.get_ttl_cache(settings)
438 # check if hash should be migrated ?
438 # check if hash should be migrated ?
439 new_hash = auth.get('_hash_migrate')
439 new_hash = auth.get('_hash_migrate')
440 if new_hash:
440 if new_hash:
441 self._migrate_hash_to_bcrypt(username, passwd, new_hash)
441 self._migrate_hash_to_bcrypt(username, passwd, new_hash)
442 if 'user_group_sync' not in auth:
442 if 'user_group_sync' not in auth:
443 auth['user_group_sync'] = False
443 auth['user_group_sync'] = False
444 return self._validate_auth_return(auth)
444 return self._validate_auth_return(auth)
445 return auth
445 return auth
446
446
447 def _migrate_hash_to_bcrypt(self, username, password, new_hash):
447 def _migrate_hash_to_bcrypt(self, username, password, new_hash):
448 new_hash_cypher = _RhodeCodeCryptoBCrypt()
448 new_hash_cypher = _RhodeCodeCryptoBCrypt()
449 # extra checks, so make sure new hash is correct.
449 # extra checks, so make sure new hash is correct.
450 password_encoded = safe_str(password)
450 password_encoded = safe_str(password)
451 if new_hash and new_hash_cypher.hash_check(
451 if new_hash and new_hash_cypher.hash_check(
452 password_encoded, new_hash):
452 password_encoded, new_hash):
453 cur_user = User.get_by_username(username)
453 cur_user = User.get_by_username(username)
454 cur_user.password = new_hash
454 cur_user.password = new_hash
455 Session().add(cur_user)
455 Session().add(cur_user)
456 Session().flush()
456 Session().flush()
457 log.info('Migrated user %s hash to bcrypt', cur_user)
457 log.info('Migrated user %s hash to bcrypt', cur_user)
458
458
459 def _validate_auth_return(self, ret):
459 def _validate_auth_return(self, ret):
460 if not isinstance(ret, dict):
460 if not isinstance(ret, dict):
461 raise Exception('returned value from auth must be a dict')
461 raise Exception('returned value from auth must be a dict')
462 for k in self.auth_func_attrs:
462 for k in self.auth_func_attrs:
463 if k not in ret:
463 if k not in ret:
464 raise Exception('Missing %s attribute from returned data' % k)
464 raise Exception('Missing %s attribute from returned data' % k)
465 return ret
465 return ret
466
466
467 def get_ttl_cache(self, settings=None):
467 def get_ttl_cache(self, settings=None):
468 plugin_settings = settings or self.get_settings()
468 plugin_settings = settings or self.get_settings()
469 # we set default to 30, we make a compromise here,
469 # we set default to 30, we make a compromise here,
470 # performance > security, mostly due to LDAP/SVN, majority
470 # performance > security, mostly due to LDAP/SVN, majority
471 # of users pick cache_ttl to be enabled
471 # of users pick cache_ttl to be enabled
472 from rhodecode.authentication import plugin_default_auth_ttl
472 from rhodecode.authentication import plugin_default_auth_ttl
473 cache_ttl = plugin_default_auth_ttl
473 cache_ttl = plugin_default_auth_ttl
474
474
475 if isinstance(self.AUTH_CACHE_TTL, (int, long)):
475 if isinstance(self.AUTH_CACHE_TTL, int):
476 # plugin cache set inside is more important than the settings value
476 # plugin cache set inside is more important than the settings value
477 cache_ttl = self.AUTH_CACHE_TTL
477 cache_ttl = self.AUTH_CACHE_TTL
478 elif plugin_settings.get('cache_ttl'):
478 elif plugin_settings.get('cache_ttl'):
479 cache_ttl = safe_int(plugin_settings.get('cache_ttl'), 0)
479 cache_ttl = safe_int(plugin_settings.get('cache_ttl'), 0)
480
480
481 plugin_cache_active = bool(cache_ttl and cache_ttl > 0)
481 plugin_cache_active = bool(cache_ttl and cache_ttl > 0)
482 return plugin_cache_active, cache_ttl
482 return plugin_cache_active, cache_ttl
483
483
484
484
485 class RhodeCodeExternalAuthPlugin(RhodeCodeAuthPluginBase):
485 class RhodeCodeExternalAuthPlugin(RhodeCodeAuthPluginBase):
486
486
487 @hybrid_property
487 @hybrid_property
488 def allows_creating_users(self):
488 def allows_creating_users(self):
489 return True
489 return True
490
490
491 def use_fake_password(self):
491 def use_fake_password(self):
492 """
492 """
493 Return a boolean that indicates whether or not we should set the user's
493 Return a boolean that indicates whether or not we should set the user's
494 password to a random value when it is authenticated by this plugin.
494 password to a random value when it is authenticated by this plugin.
495 If your plugin provides authentication, then you will generally
495 If your plugin provides authentication, then you will generally
496 want this.
496 want this.
497
497
498 :returns: boolean
498 :returns: boolean
499 """
499 """
500 raise NotImplementedError("Not implemented in base class")
500 raise NotImplementedError("Not implemented in base class")
501
501
502 def _authenticate(self, userobj, username, passwd, settings, **kwargs):
502 def _authenticate(self, userobj, username, passwd, settings, **kwargs):
503 # at this point _authenticate calls plugin's `auth()` function
503 # at this point _authenticate calls plugin's `auth()` function
504 auth = super(RhodeCodeExternalAuthPlugin, self)._authenticate(
504 auth = super(RhodeCodeExternalAuthPlugin, self)._authenticate(
505 userobj, username, passwd, settings, **kwargs)
505 userobj, username, passwd, settings, **kwargs)
506
506
507 if auth:
507 if auth:
508 # maybe plugin will clean the username ?
508 # maybe plugin will clean the username ?
509 # we should use the return value
509 # we should use the return value
510 username = auth['username']
510 username = auth['username']
511
511
512 # if external source tells us that user is not active, we should
512 # if external source tells us that user is not active, we should
513 # skip rest of the process. This can prevent from creating users in
513 # skip rest of the process. This can prevent from creating users in
514 # RhodeCode when using external authentication, but if it's
514 # RhodeCode when using external authentication, but if it's
515 # inactive user we shouldn't create that user anyway
515 # inactive user we shouldn't create that user anyway
516 if auth['active_from_extern'] is False:
516 if auth['active_from_extern'] is False:
517 log.warning(
517 log.warning(
518 "User %s authenticated against %s, but is inactive",
518 "User %s authenticated against %s, but is inactive",
519 username, self.__module__)
519 username, self.__module__)
520 return None
520 return None
521
521
522 cur_user = User.get_by_username(username, case_insensitive=True)
522 cur_user = User.get_by_username(username, case_insensitive=True)
523 is_user_existing = cur_user is not None
523 is_user_existing = cur_user is not None
524
524
525 if is_user_existing:
525 if is_user_existing:
526 log.debug('Syncing user `%s` from '
526 log.debug('Syncing user `%s` from '
527 '`%s` plugin', username, self.name)
527 '`%s` plugin', username, self.name)
528 else:
528 else:
529 log.debug('Creating non existing user `%s` from '
529 log.debug('Creating non existing user `%s` from '
530 '`%s` plugin', username, self.name)
530 '`%s` plugin', username, self.name)
531
531
532 if self.allows_creating_users:
532 if self.allows_creating_users:
533 log.debug('Plugin `%s` allows to '
533 log.debug('Plugin `%s` allows to '
534 'create new users', self.name)
534 'create new users', self.name)
535 else:
535 else:
536 log.debug('Plugin `%s` does not allow to '
536 log.debug('Plugin `%s` does not allow to '
537 'create new users', self.name)
537 'create new users', self.name)
538
538
539 user_parameters = {
539 user_parameters = {
540 'username': username,
540 'username': username,
541 'email': auth["email"],
541 'email': auth["email"],
542 'firstname': auth["firstname"],
542 'firstname': auth["firstname"],
543 'lastname': auth["lastname"],
543 'lastname': auth["lastname"],
544 'active': auth["active"],
544 'active': auth["active"],
545 'admin': auth["admin"],
545 'admin': auth["admin"],
546 'extern_name': auth["extern_name"],
546 'extern_name': auth["extern_name"],
547 'extern_type': self.name,
547 'extern_type': self.name,
548 'plugin': self,
548 'plugin': self,
549 'allow_to_create_user': self.allows_creating_users,
549 'allow_to_create_user': self.allows_creating_users,
550 }
550 }
551
551
552 if not is_user_existing:
552 if not is_user_existing:
553 if self.use_fake_password():
553 if self.use_fake_password():
554 # Randomize the PW because we don't need it, but don't want
554 # Randomize the PW because we don't need it, but don't want
555 # them blank either
555 # them blank either
556 passwd = PasswordGenerator().gen_password(length=16)
556 passwd = PasswordGenerator().gen_password(length=16)
557 user_parameters['password'] = passwd
557 user_parameters['password'] = passwd
558 else:
558 else:
559 # Since the password is required by create_or_update method of
559 # Since the password is required by create_or_update method of
560 # UserModel, we need to set it explicitly.
560 # UserModel, we need to set it explicitly.
561 # The create_or_update method is smart and recognises the
561 # The create_or_update method is smart and recognises the
562 # password hashes as well.
562 # password hashes as well.
563 user_parameters['password'] = cur_user.password
563 user_parameters['password'] = cur_user.password
564
564
565 # we either create or update users, we also pass the flag
565 # we either create or update users, we also pass the flag
566 # that controls if this method can actually do that.
566 # that controls if this method can actually do that.
567 # raises NotAllowedToCreateUserError if it cannot, and we try to.
567 # raises NotAllowedToCreateUserError if it cannot, and we try to.
568 user = UserModel().create_or_update(**user_parameters)
568 user = UserModel().create_or_update(**user_parameters)
569 Session().flush()
569 Session().flush()
570 # enforce user is just in given groups, all of them has to be ones
570 # enforce user is just in given groups, all of them has to be ones
571 # created from plugins. We store this info in _group_data JSON
571 # created from plugins. We store this info in _group_data JSON
572 # field
572 # field
573
573
574 if auth['user_group_sync']:
574 if auth['user_group_sync']:
575 try:
575 try:
576 groups = auth['groups'] or []
576 groups = auth['groups'] or []
577 log.debug(
577 log.debug(
578 'Performing user_group sync based on set `%s` '
578 'Performing user_group sync based on set `%s` '
579 'returned by `%s` plugin', groups, self.name)
579 'returned by `%s` plugin', groups, self.name)
580 UserGroupModel().enforce_groups(user, groups, self.name)
580 UserGroupModel().enforce_groups(user, groups, self.name)
581 except Exception:
581 except Exception:
582 # for any reason group syncing fails, we should
582 # for any reason group syncing fails, we should
583 # proceed with login
583 # proceed with login
584 log.error(traceback.format_exc())
584 log.error(traceback.format_exc())
585
585
586 Session().commit()
586 Session().commit()
587 return auth
587 return auth
588
588
589
589
590 class AuthLdapBase(object):
590 class AuthLdapBase(object):
591
591
592 @classmethod
592 @classmethod
593 def _build_servers(cls, ldap_server_type, ldap_server, port, use_resolver=True):
593 def _build_servers(cls, ldap_server_type, ldap_server, port, use_resolver=True):
594
594
595 def host_resolver(host, port, full_resolve=True):
595 def host_resolver(host, port, full_resolve=True):
596 """
596 """
597 Main work for this function is to prevent ldap connection issues,
597 Main work for this function is to prevent ldap connection issues,
598 and detect them early using a "greenified" sockets
598 and detect them early using a "greenified" sockets
599 """
599 """
600 host = host.strip()
600 host = host.strip()
601 if not full_resolve:
601 if not full_resolve:
602 return '{}:{}'.format(host, port)
602 return '{}:{}'.format(host, port)
603
603
604 log.debug('LDAP: Resolving IP for LDAP host `%s`', host)
604 log.debug('LDAP: Resolving IP for LDAP host `%s`', host)
605 try:
605 try:
606 ip = socket.gethostbyname(host)
606 ip = socket.gethostbyname(host)
607 log.debug('LDAP: Got LDAP host `%s` ip %s', host, ip)
607 log.debug('LDAP: Got LDAP host `%s` ip %s', host, ip)
608 except Exception:
608 except Exception:
609 raise LdapConnectionError('Failed to resolve host: `{}`'.format(host))
609 raise LdapConnectionError('Failed to resolve host: `{}`'.format(host))
610
610
611 log.debug('LDAP: Checking if IP %s is accessible', ip)
611 log.debug('LDAP: Checking if IP %s is accessible', ip)
612 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
612 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
613 try:
613 try:
614 s.connect((ip, int(port)))
614 s.connect((ip, int(port)))
615 s.shutdown(socket.SHUT_RD)
615 s.shutdown(socket.SHUT_RD)
616 log.debug('LDAP: connection to %s successful', ip)
616 log.debug('LDAP: connection to %s successful', ip)
617 except Exception:
617 except Exception:
618 raise LdapConnectionError(
618 raise LdapConnectionError(
619 'Failed to connect to host: `{}:{}`'.format(host, port))
619 'Failed to connect to host: `{}:{}`'.format(host, port))
620
620
621 return '{}:{}'.format(host, port)
621 return '{}:{}'.format(host, port)
622
622
623 if len(ldap_server) == 1:
623 if len(ldap_server) == 1:
624 # in case of single server use resolver to detect potential
624 # in case of single server use resolver to detect potential
625 # connection issues
625 # connection issues
626 full_resolve = True
626 full_resolve = True
627 else:
627 else:
628 full_resolve = False
628 full_resolve = False
629
629
630 return ', '.join(
630 return ', '.join(
631 ["{}://{}".format(
631 ["{}://{}".format(
632 ldap_server_type,
632 ldap_server_type,
633 host_resolver(host, port, full_resolve=use_resolver and full_resolve))
633 host_resolver(host, port, full_resolve=use_resolver and full_resolve))
634 for host in ldap_server])
634 for host in ldap_server])
635
635
636 @classmethod
636 @classmethod
637 def _get_server_list(cls, servers):
637 def _get_server_list(cls, servers):
638 return map(string.strip, servers.split(','))
638 return map(string.strip, servers.split(','))
639
639
640 @classmethod
640 @classmethod
641 def get_uid(cls, username, server_addresses):
641 def get_uid(cls, username, server_addresses):
642 uid = username
642 uid = username
643 for server_addr in server_addresses:
643 for server_addr in server_addresses:
644 uid = chop_at(username, "@%s" % server_addr)
644 uid = chop_at(username, "@%s" % server_addr)
645 return uid
645 return uid
646
646
647 @classmethod
647 @classmethod
648 def validate_username(cls, username):
648 def validate_username(cls, username):
649 if "," in username:
649 if "," in username:
650 raise LdapUsernameError(
650 raise LdapUsernameError(
651 "invalid character `,` in username: `{}`".format(username))
651 "invalid character `,` in username: `{}`".format(username))
652
652
653 @classmethod
653 @classmethod
654 def validate_password(cls, username, password):
654 def validate_password(cls, username, password):
655 if not password:
655 if not password:
656 msg = "Authenticating user %s with blank password not allowed"
656 msg = "Authenticating user %s with blank password not allowed"
657 log.warning(msg, username)
657 log.warning(msg, username)
658 raise LdapPasswordError(msg)
658 raise LdapPasswordError(msg)
659
659
660
660
661 def loadplugin(plugin_id):
661 def loadplugin(plugin_id):
662 """
662 """
663 Loads and returns an instantiated authentication plugin.
663 Loads and returns an instantiated authentication plugin.
664 Returns the RhodeCodeAuthPluginBase subclass on success,
664 Returns the RhodeCodeAuthPluginBase subclass on success,
665 or None on failure.
665 or None on failure.
666 """
666 """
667 # TODO: Disusing pyramids thread locals to retrieve the registry.
667 # TODO: Disusing pyramids thread locals to retrieve the registry.
668 authn_registry = get_authn_registry()
668 authn_registry = get_authn_registry()
669 plugin = authn_registry.get_plugin(plugin_id)
669 plugin = authn_registry.get_plugin(plugin_id)
670 if plugin is None:
670 if plugin is None:
671 log.error('Authentication plugin not found: "%s"', plugin_id)
671 log.error('Authentication plugin not found: "%s"', plugin_id)
672 return plugin
672 return plugin
673
673
674
674
675 def get_authn_registry(registry=None):
675 def get_authn_registry(registry=None):
676 registry = registry or get_current_registry()
676 registry = registry or get_current_registry()
677 authn_registry = registry.queryUtility(IAuthnPluginRegistry)
677 authn_registry = registry.queryUtility(IAuthnPluginRegistry)
678 return authn_registry
678 return authn_registry
679
679
680
680
681 def authenticate(username, password, environ=None, auth_type=None,
681 def authenticate(username, password, environ=None, auth_type=None,
682 skip_missing=False, registry=None, acl_repo_name=None):
682 skip_missing=False, registry=None, acl_repo_name=None):
683 """
683 """
684 Authentication function used for access control,
684 Authentication function used for access control,
685 It tries to authenticate based on enabled authentication modules.
685 It tries to authenticate based on enabled authentication modules.
686
686
687 :param username: username can be empty for headers auth
687 :param username: username can be empty for headers auth
688 :param password: password can be empty for headers auth
688 :param password: password can be empty for headers auth
689 :param environ: environ headers passed for headers auth
689 :param environ: environ headers passed for headers auth
690 :param auth_type: type of authentication, either `HTTP_TYPE` or `VCS_TYPE`
690 :param auth_type: type of authentication, either `HTTP_TYPE` or `VCS_TYPE`
691 :param skip_missing: ignores plugins that are in db but not in environment
691 :param skip_missing: ignores plugins that are in db but not in environment
692 :returns: None if auth failed, plugin_user dict if auth is correct
692 :returns: None if auth failed, plugin_user dict if auth is correct
693 """
693 """
694 if not auth_type or auth_type not in [HTTP_TYPE, VCS_TYPE]:
694 if not auth_type or auth_type not in [HTTP_TYPE, VCS_TYPE]:
695 raise ValueError('auth type must be on of http, vcs got "%s" instead'
695 raise ValueError('auth type must be on of http, vcs got "%s" instead'
696 % auth_type)
696 % auth_type)
697 headers_only = environ and not (username and password)
697 headers_only = environ and not (username and password)
698
698
699 authn_registry = get_authn_registry(registry)
699 authn_registry = get_authn_registry(registry)
700
700
701 plugins_to_check = authn_registry.get_plugins_for_authentication()
701 plugins_to_check = authn_registry.get_plugins_for_authentication()
702 log.debug('Starting ordered authentication chain using %s plugins',
702 log.debug('Starting ordered authentication chain using %s plugins',
703 [x.name for x in plugins_to_check])
703 [x.name for x in plugins_to_check])
704 for plugin in plugins_to_check:
704 for plugin in plugins_to_check:
705 plugin.set_auth_type(auth_type)
705 plugin.set_auth_type(auth_type)
706 plugin.set_calling_scope_repo(acl_repo_name)
706 plugin.set_calling_scope_repo(acl_repo_name)
707
707
708 if headers_only and not plugin.is_headers_auth:
708 if headers_only and not plugin.is_headers_auth:
709 log.debug('Auth type is for headers only and plugin `%s` is not '
709 log.debug('Auth type is for headers only and plugin `%s` is not '
710 'headers plugin, skipping...', plugin.get_id())
710 'headers plugin, skipping...', plugin.get_id())
711 continue
711 continue
712
712
713 log.debug('Trying authentication using ** %s **', plugin.get_id())
713 log.debug('Trying authentication using ** %s **', plugin.get_id())
714
714
715 # load plugin settings from RhodeCode database
715 # load plugin settings from RhodeCode database
716 plugin_settings = plugin.get_settings()
716 plugin_settings = plugin.get_settings()
717 plugin_sanitized_settings = plugin.log_safe_settings(plugin_settings)
717 plugin_sanitized_settings = plugin.log_safe_settings(plugin_settings)
718 log.debug('Plugin `%s` settings:%s', plugin.get_id(), plugin_sanitized_settings)
718 log.debug('Plugin `%s` settings:%s', plugin.get_id(), plugin_sanitized_settings)
719
719
720 # use plugin's method of user extraction.
720 # use plugin's method of user extraction.
721 user = plugin.get_user(username, environ=environ,
721 user = plugin.get_user(username, environ=environ,
722 settings=plugin_settings)
722 settings=plugin_settings)
723 display_user = user.username if user else username
723 display_user = user.username if user else username
724 log.debug(
724 log.debug(
725 'Plugin %s extracted user is `%s`', plugin.get_id(), display_user)
725 'Plugin %s extracted user is `%s`', plugin.get_id(), display_user)
726
726
727 if not plugin.allows_authentication_from(user):
727 if not plugin.allows_authentication_from(user):
728 log.debug('Plugin %s does not accept user `%s` for authentication',
728 log.debug('Plugin %s does not accept user `%s` for authentication',
729 plugin.get_id(), display_user)
729 plugin.get_id(), display_user)
730 continue
730 continue
731 else:
731 else:
732 log.debug('Plugin %s accepted user `%s` for authentication',
732 log.debug('Plugin %s accepted user `%s` for authentication',
733 plugin.get_id(), display_user)
733 plugin.get_id(), display_user)
734
734
735 log.info('Authenticating user `%s` using %s plugin',
735 log.info('Authenticating user `%s` using %s plugin',
736 display_user, plugin.get_id())
736 display_user, plugin.get_id())
737
737
738 plugin_cache_active, cache_ttl = plugin.get_ttl_cache(plugin_settings)
738 plugin_cache_active, cache_ttl = plugin.get_ttl_cache(plugin_settings)
739
739
740 log.debug('AUTH_CACHE_TTL for plugin `%s` active: %s (TTL: %s)',
740 log.debug('AUTH_CACHE_TTL for plugin `%s` active: %s (TTL: %s)',
741 plugin.get_id(), plugin_cache_active, cache_ttl)
741 plugin.get_id(), plugin_cache_active, cache_ttl)
742
742
743 user_id = user.user_id if user else 'no-user'
743 user_id = user.user_id if user else 'no-user'
744 # don't cache for empty users
744 # don't cache for empty users
745 plugin_cache_active = plugin_cache_active and user_id
745 plugin_cache_active = plugin_cache_active and user_id
746 cache_namespace_uid = 'cache_user_auth.{}'.format(user_id)
746 cache_namespace_uid = 'cache_user_auth.{}'.format(user_id)
747 region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
747 region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
748
748
749 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid,
749 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid,
750 expiration_time=cache_ttl,
750 expiration_time=cache_ttl,
751 condition=plugin_cache_active)
751 condition=plugin_cache_active)
752 def compute_auth(
752 def compute_auth(
753 cache_name, plugin_name, username, password):
753 cache_name, plugin_name, username, password):
754
754
755 # _authenticate is a wrapper for .auth() method of plugin.
755 # _authenticate is a wrapper for .auth() method of plugin.
756 # it checks if .auth() sends proper data.
756 # it checks if .auth() sends proper data.
757 # For RhodeCodeExternalAuthPlugin it also maps users to
757 # For RhodeCodeExternalAuthPlugin it also maps users to
758 # Database and maps the attributes returned from .auth()
758 # Database and maps the attributes returned from .auth()
759 # to RhodeCode database. If this function returns data
759 # to RhodeCode database. If this function returns data
760 # then auth is correct.
760 # then auth is correct.
761 log.debug('Running plugin `%s` _authenticate method '
761 log.debug('Running plugin `%s` _authenticate method '
762 'using username and password', plugin.get_id())
762 'using username and password', plugin.get_id())
763 return plugin._authenticate(
763 return plugin._authenticate(
764 user, username, password, plugin_settings,
764 user, username, password, plugin_settings,
765 environ=environ or {})
765 environ=environ or {})
766
766
767 start = time.time()
767 start = time.time()
768 # for environ based auth, password can be empty, but then the validation is
768 # for environ based auth, password can be empty, but then the validation is
769 # on the server that fills in the env data needed for authentication
769 # on the server that fills in the env data needed for authentication
770 plugin_user = compute_auth('auth', plugin.name, username, (password or ''))
770 plugin_user = compute_auth('auth', plugin.name, username, (password or ''))
771
771
772 auth_time = time.time() - start
772 auth_time = time.time() - start
773 log.debug('Authentication for plugin `%s` completed in %.4fs, '
773 log.debug('Authentication for plugin `%s` completed in %.4fs, '
774 'expiration time of fetched cache %.1fs.',
774 'expiration time of fetched cache %.1fs.',
775 plugin.get_id(), auth_time, cache_ttl,
775 plugin.get_id(), auth_time, cache_ttl,
776 extra={"plugin": plugin.get_id(), "time": auth_time})
776 extra={"plugin": plugin.get_id(), "time": auth_time})
777
777
778 log.debug('PLUGIN USER DATA: %s', plugin_user)
778 log.debug('PLUGIN USER DATA: %s', plugin_user)
779
779
780 statsd = StatsdClient.statsd
780 statsd = StatsdClient.statsd
781
781
782 if plugin_user:
782 if plugin_user:
783 log.debug('Plugin returned proper authentication data')
783 log.debug('Plugin returned proper authentication data')
784 if statsd:
784 if statsd:
785 elapsed_time_ms = round(1000.0 * auth_time) # use ms only
785 elapsed_time_ms = round(1000.0 * auth_time) # use ms only
786 statsd.incr('rhodecode_login_success_total')
786 statsd.incr('rhodecode_login_success_total')
787 statsd.timing("rhodecode_login_timing.histogram", elapsed_time_ms,
787 statsd.timing("rhodecode_login_timing.histogram", elapsed_time_ms,
788 tags=["plugin:{}".format(plugin.get_id())],
788 tags=["plugin:{}".format(plugin.get_id())],
789 use_decimals=False
789 use_decimals=False
790 )
790 )
791 return plugin_user
791 return plugin_user
792
792
793 # we failed to Auth because .auth() method didn't return proper user
793 # we failed to Auth because .auth() method didn't return proper user
794 log.debug("User `%s` failed to authenticate against %s",
794 log.debug("User `%s` failed to authenticate against %s",
795 display_user, plugin.get_id())
795 display_user, plugin.get_id())
796 if statsd:
796 if statsd:
797 statsd.incr('rhodecode_login_fail_total')
797 statsd.incr('rhodecode_login_fail_total')
798
798
799 # case when we failed to authenticate against all defined plugins
799 # case when we failed to authenticate against all defined plugins
800 return None
800 return None
801
801
802
802
803 def chop_at(s, sub, inclusive=False):
803 def chop_at(s, sub, inclusive=False):
804 """Truncate string ``s`` at the first occurrence of ``sub``.
804 """Truncate string ``s`` at the first occurrence of ``sub``.
805
805
806 If ``inclusive`` is true, truncate just after ``sub`` rather than at it.
806 If ``inclusive`` is true, truncate just after ``sub`` rather than at it.
807
807
808 >>> chop_at("plutocratic brats", "rat")
808 >>> chop_at("plutocratic brats", "rat")
809 'plutoc'
809 'plutoc'
810 >>> chop_at("plutocratic brats", "rat", True)
810 >>> chop_at("plutocratic brats", "rat", True)
811 'plutocrat'
811 'plutocrat'
812 """
812 """
813 pos = s.find(sub)
813 pos = s.find(sub)
814 if pos == -1:
814 if pos == -1:
815 return s
815 return s
816 if inclusive:
816 if inclusive:
817 return s[:pos+len(sub)]
817 return s[:pos+len(sub)]
818 return s[:pos]
818 return s[:pos]
@@ -1,839 +1,839 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Utilities for writing code that runs on Python 2 and 3"""
2 """Utilities for writing code that runs on Python 2 and 3"""
3
3
4 # Copyright (c) 2010-2015 Benjamin Peterson
4 # Copyright (c) 2010-2015 Benjamin Peterson
5 #
5 #
6 # Permission is hereby granted, free of charge, to any person obtaining a copy
6 # Permission is hereby granted, free of charge, to any person obtaining a copy
7 # of this software and associated documentation files (the "Software"), to deal
7 # of this software and associated documentation files (the "Software"), to deal
8 # in the Software without restriction, including without limitation the rights
8 # in the Software without restriction, including without limitation the rights
9 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 # copies of the Software, and to permit persons to whom the Software is
10 # copies of the Software, and to permit persons to whom the Software is
11 # furnished to do so, subject to the following conditions:
11 # furnished to do so, subject to the following conditions:
12 #
12 #
13 # The above copyright notice and this permission notice shall be included in all
13 # The above copyright notice and this permission notice shall be included in all
14 # copies or substantial portions of the Software.
14 # copies or substantial portions of the Software.
15 #
15 #
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 # SOFTWARE.
22 # SOFTWARE.
23
23
24
24
25
25
26 import functools
26 import functools
27 import itertools
27 import itertools
28 import operator
28 import operator
29 import sys
29 import sys
30 import types
30 import types
31
31
32 __author__ = "Benjamin Peterson <benjamin@python.org>"
32 __author__ = "Benjamin Peterson <benjamin@python.org>"
33 __version__ = "1.9.0"
33 __version__ = "1.9.0"
34
34
35
35
36 # Useful for very coarse version differentiation.
36 # Useful for very coarse version differentiation.
37 PY2 = sys.version_info[0] == 2
37 PY2 = sys.version_info[0] == 2
38 PY3 = sys.version_info[0] == 3
38 PY3 = sys.version_info[0] == 3
39
39
40 if PY3:
40 if PY3:
41 string_types = str,
41 string_types = str,
42 integer_types = int,
42 integer_types = int,
43 class_types = type,
43 class_types = type,
44 text_type = str
44 text_type = str
45 binary_type = bytes
45 binary_type = bytes
46
46
47 MAXSIZE = sys.maxsize
47 MAXSIZE = sys.maxsize
48 else:
48 else:
49 string_types = str,
49 string_types = str,
50 integer_types = (int, long)
50 integer_types = int
51 class_types = (type, types.ClassType)
51 class_types = (type, types.ClassType)
52 text_type = unicode
52 text_type = unicode
53 binary_type = str
53 binary_type = str
54
54
55 if sys.platform.startswith("java"):
55 if sys.platform.startswith("java"):
56 # Jython always uses 32 bits.
56 # Jython always uses 32 bits.
57 MAXSIZE = int((1 << 31) - 1)
57 MAXSIZE = int((1 << 31) - 1)
58 else:
58 else:
59 # It's possible to have sizeof(long) != sizeof(Py_ssize_t).
59 # It's possible to have sizeof(long) != sizeof(Py_ssize_t).
60 class X(object):
60 class X(object):
61 def __len__(self):
61 def __len__(self):
62 return 1 << 31
62 return 1 << 31
63 try:
63 try:
64 len(X())
64 len(X())
65 except OverflowError:
65 except OverflowError:
66 # 32-bit
66 # 32-bit
67 MAXSIZE = int((1 << 31) - 1)
67 MAXSIZE = int((1 << 31) - 1)
68 else:
68 else:
69 # 64-bit
69 # 64-bit
70 MAXSIZE = int((1 << 63) - 1)
70 MAXSIZE = int((1 << 63) - 1)
71 del X
71 del X
72
72
73
73
74 def _add_doc(func, doc):
74 def _add_doc(func, doc):
75 """Add documentation to a function."""
75 """Add documentation to a function."""
76 func.__doc__ = doc
76 func.__doc__ = doc
77
77
78
78
79 def _import_module(name):
79 def _import_module(name):
80 """Import module, returning the module after the last dot."""
80 """Import module, returning the module after the last dot."""
81 __import__(name)
81 __import__(name)
82 return sys.modules[name]
82 return sys.modules[name]
83
83
84
84
85 class _LazyDescr(object):
85 class _LazyDescr(object):
86
86
87 def __init__(self, name):
87 def __init__(self, name):
88 self.name = name
88 self.name = name
89
89
90 def __get__(self, obj, tp):
90 def __get__(self, obj, tp):
91 result = self._resolve()
91 result = self._resolve()
92 setattr(obj, self.name, result) # Invokes __set__.
92 setattr(obj, self.name, result) # Invokes __set__.
93 try:
93 try:
94 # This is a bit ugly, but it avoids running this again by
94 # This is a bit ugly, but it avoids running this again by
95 # removing this descriptor.
95 # removing this descriptor.
96 delattr(obj.__class__, self.name)
96 delattr(obj.__class__, self.name)
97 except AttributeError:
97 except AttributeError:
98 pass
98 pass
99 return result
99 return result
100
100
101
101
102 class MovedModule(_LazyDescr):
102 class MovedModule(_LazyDescr):
103
103
104 def __init__(self, name, old, new=None):
104 def __init__(self, name, old, new=None):
105 super(MovedModule, self).__init__(name)
105 super(MovedModule, self).__init__(name)
106 if PY3:
106 if PY3:
107 if new is None:
107 if new is None:
108 new = name
108 new = name
109 self.mod = new
109 self.mod = new
110 else:
110 else:
111 self.mod = old
111 self.mod = old
112
112
113 def _resolve(self):
113 def _resolve(self):
114 return _import_module(self.mod)
114 return _import_module(self.mod)
115
115
116 def __getattr__(self, attr):
116 def __getattr__(self, attr):
117 _module = self._resolve()
117 _module = self._resolve()
118 value = getattr(_module, attr)
118 value = getattr(_module, attr)
119 setattr(self, attr, value)
119 setattr(self, attr, value)
120 return value
120 return value
121
121
122
122
123 class _LazyModule(types.ModuleType):
123 class _LazyModule(types.ModuleType):
124
124
125 def __init__(self, name):
125 def __init__(self, name):
126 super(_LazyModule, self).__init__(name)
126 super(_LazyModule, self).__init__(name)
127 self.__doc__ = self.__class__.__doc__
127 self.__doc__ = self.__class__.__doc__
128
128
129 def __dir__(self):
129 def __dir__(self):
130 attrs = ["__doc__", "__name__"]
130 attrs = ["__doc__", "__name__"]
131 attrs += [attr.name for attr in self._moved_attributes]
131 attrs += [attr.name for attr in self._moved_attributes]
132 return attrs
132 return attrs
133
133
134 # Subclasses should override this
134 # Subclasses should override this
135 _moved_attributes = []
135 _moved_attributes = []
136
136
137
137
138 class MovedAttribute(_LazyDescr):
138 class MovedAttribute(_LazyDescr):
139
139
140 def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
140 def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
141 super(MovedAttribute, self).__init__(name)
141 super(MovedAttribute, self).__init__(name)
142 if PY3:
142 if PY3:
143 if new_mod is None:
143 if new_mod is None:
144 new_mod = name
144 new_mod = name
145 self.mod = new_mod
145 self.mod = new_mod
146 if new_attr is None:
146 if new_attr is None:
147 if old_attr is None:
147 if old_attr is None:
148 new_attr = name
148 new_attr = name
149 else:
149 else:
150 new_attr = old_attr
150 new_attr = old_attr
151 self.attr = new_attr
151 self.attr = new_attr
152 else:
152 else:
153 self.mod = old_mod
153 self.mod = old_mod
154 if old_attr is None:
154 if old_attr is None:
155 old_attr = name
155 old_attr = name
156 self.attr = old_attr
156 self.attr = old_attr
157
157
158 def _resolve(self):
158 def _resolve(self):
159 module = _import_module(self.mod)
159 module = _import_module(self.mod)
160 return getattr(module, self.attr)
160 return getattr(module, self.attr)
161
161
162
162
163 class _SixMetaPathImporter(object):
163 class _SixMetaPathImporter(object):
164 """
164 """
165 A meta path importer to import six.moves and its submodules.
165 A meta path importer to import six.moves and its submodules.
166
166
167 This class implements a PEP302 finder and loader. It should be compatible
167 This class implements a PEP302 finder and loader. It should be compatible
168 with Python 2.5 and all existing versions of Python3
168 with Python 2.5 and all existing versions of Python3
169 """
169 """
170 def __init__(self, six_module_name):
170 def __init__(self, six_module_name):
171 self.name = six_module_name
171 self.name = six_module_name
172 self.known_modules = {}
172 self.known_modules = {}
173
173
174 def _add_module(self, mod, *fullnames):
174 def _add_module(self, mod, *fullnames):
175 for fullname in fullnames:
175 for fullname in fullnames:
176 self.known_modules[self.name + "." + fullname] = mod
176 self.known_modules[self.name + "." + fullname] = mod
177
177
178 def _get_module(self, fullname):
178 def _get_module(self, fullname):
179 return self.known_modules[self.name + "." + fullname]
179 return self.known_modules[self.name + "." + fullname]
180
180
181 def find_module(self, fullname, path=None):
181 def find_module(self, fullname, path=None):
182 if fullname in self.known_modules:
182 if fullname in self.known_modules:
183 return self
183 return self
184 return None
184 return None
185
185
186 def __get_module(self, fullname):
186 def __get_module(self, fullname):
187 try:
187 try:
188 return self.known_modules[fullname]
188 return self.known_modules[fullname]
189 except KeyError:
189 except KeyError:
190 raise ImportError("This loader does not know module " + fullname)
190 raise ImportError("This loader does not know module " + fullname)
191
191
192 def load_module(self, fullname):
192 def load_module(self, fullname):
193 try:
193 try:
194 # in case of a reload
194 # in case of a reload
195 return sys.modules[fullname]
195 return sys.modules[fullname]
196 except KeyError:
196 except KeyError:
197 pass
197 pass
198 mod = self.__get_module(fullname)
198 mod = self.__get_module(fullname)
199 if isinstance(mod, MovedModule):
199 if isinstance(mod, MovedModule):
200 mod = mod._resolve()
200 mod = mod._resolve()
201 else:
201 else:
202 mod.__loader__ = self
202 mod.__loader__ = self
203 sys.modules[fullname] = mod
203 sys.modules[fullname] = mod
204 return mod
204 return mod
205
205
206 def is_package(self, fullname):
206 def is_package(self, fullname):
207 """
207 """
208 Return true, if the named module is a package.
208 Return true, if the named module is a package.
209
209
210 We need this method to get correct spec objects with
210 We need this method to get correct spec objects with
211 Python 3.4 (see PEP451)
211 Python 3.4 (see PEP451)
212 """
212 """
213 return hasattr(self.__get_module(fullname), "__path__")
213 return hasattr(self.__get_module(fullname), "__path__")
214
214
215 def get_code(self, fullname):
215 def get_code(self, fullname):
216 """Return None
216 """Return None
217
217
218 Required, if is_package is implemented"""
218 Required, if is_package is implemented"""
219 self.__get_module(fullname) # eventually raises ImportError
219 self.__get_module(fullname) # eventually raises ImportError
220 return None
220 return None
221 get_source = get_code # same as get_code
221 get_source = get_code # same as get_code
222
222
223 _importer = _SixMetaPathImporter(__name__)
223 _importer = _SixMetaPathImporter(__name__)
224
224
225
225
226 class _MovedItems(_LazyModule):
226 class _MovedItems(_LazyModule):
227 """Lazy loading of moved objects"""
227 """Lazy loading of moved objects"""
228 __path__ = [] # mark as package
228 __path__ = [] # mark as package
229
229
230
230
231 _moved_attributes = [
231 _moved_attributes = [
232 MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
232 MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
233 MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
233 MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
234 MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"),
234 MovedAttribute("filterfalse", "itertools", "itertools", "ifilterfalse", "filterfalse"),
235 MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
235 MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
236 MovedAttribute("intern", "__builtin__", "sys"),
236 MovedAttribute("intern", "__builtin__", "sys"),
237 MovedAttribute("map", "itertools", "builtins", "imap", "map"),
237 MovedAttribute("map", "itertools", "builtins", "imap", "map"),
238 MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
238 MovedAttribute("range", "__builtin__", "builtins", "xrange", "range"),
239 MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
239 MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
240 MovedAttribute("reduce", "__builtin__", "functools"),
240 MovedAttribute("reduce", "__builtin__", "functools"),
241 MovedAttribute("shlex_quote", "pipes", "shlex", "quote"),
241 MovedAttribute("shlex_quote", "pipes", "shlex", "quote"),
242 MovedAttribute("StringIO", "StringIO", "io"),
242 MovedAttribute("StringIO", "StringIO", "io"),
243 MovedAttribute("UserDict", "UserDict", "collections"),
243 MovedAttribute("UserDict", "UserDict", "collections"),
244 MovedAttribute("UserList", "UserList", "collections"),
244 MovedAttribute("UserList", "UserList", "collections"),
245 MovedAttribute("UserString", "UserString", "collections"),
245 MovedAttribute("UserString", "UserString", "collections"),
246 MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
246 MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
247 MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
247 MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
248 MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
248 MovedAttribute("zip_longest", "itertools", "itertools", "izip_longest", "zip_longest"),
249
249
250 MovedModule("builtins", "__builtin__"),
250 MovedModule("builtins", "__builtin__"),
251 MovedModule("configparser", "ConfigParser"),
251 MovedModule("configparser", "ConfigParser"),
252 MovedModule("copyreg", "copy_reg"),
252 MovedModule("copyreg", "copy_reg"),
253 MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
253 MovedModule("dbm_gnu", "gdbm", "dbm.gnu"),
254 MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"),
254 MovedModule("_dummy_thread", "dummy_thread", "_dummy_thread"),
255 MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
255 MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
256 MovedModule("http_cookies", "Cookie", "http.cookies"),
256 MovedModule("http_cookies", "Cookie", "http.cookies"),
257 MovedModule("html_entities", "htmlentitydefs", "html.entities"),
257 MovedModule("html_entities", "htmlentitydefs", "html.entities"),
258 MovedModule("html_parser", "HTMLParser", "html.parser"),
258 MovedModule("html_parser", "HTMLParser", "html.parser"),
259 MovedModule("http_client", "httplib", "http.client"),
259 MovedModule("http_client", "httplib", "http.client"),
260 MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
260 MovedModule("email_mime_multipart", "email.MIMEMultipart", "email.mime.multipart"),
261 MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"),
261 MovedModule("email_mime_nonmultipart", "email.MIMENonMultipart", "email.mime.nonmultipart"),
262 MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
262 MovedModule("email_mime_text", "email.MIMEText", "email.mime.text"),
263 MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
263 MovedModule("email_mime_base", "email.MIMEBase", "email.mime.base"),
264 MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
264 MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
265 MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
265 MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
266 MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
266 MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
267 MovedModule("cPickle", "cPickle", "pickle"),
267 MovedModule("cPickle", "cPickle", "pickle"),
268 MovedModule("queue", "Queue"),
268 MovedModule("queue", "Queue"),
269 MovedModule("reprlib", "repr"),
269 MovedModule("reprlib", "repr"),
270 MovedModule("socketserver", "SocketServer"),
270 MovedModule("socketserver", "SocketServer"),
271 MovedModule("_thread", "thread", "_thread"),
271 MovedModule("_thread", "thread", "_thread"),
272 MovedModule("tkinter", "Tkinter"),
272 MovedModule("tkinter", "Tkinter"),
273 MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
273 MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
274 MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
274 MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
275 MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
275 MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
276 MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
276 MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
277 MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
277 MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
278 MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"),
278 MovedModule("tkinter_ttk", "ttk", "tkinter.ttk"),
279 MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
279 MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
280 MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
280 MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
281 MovedModule("tkinter_colorchooser", "tkColorChooser",
281 MovedModule("tkinter_colorchooser", "tkColorChooser",
282 "tkinter.colorchooser"),
282 "tkinter.colorchooser"),
283 MovedModule("tkinter_commondialog", "tkCommonDialog",
283 MovedModule("tkinter_commondialog", "tkCommonDialog",
284 "tkinter.commondialog"),
284 "tkinter.commondialog"),
285 MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
285 MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
286 MovedModule("tkinter_font", "tkFont", "tkinter.font"),
286 MovedModule("tkinter_font", "tkFont", "tkinter.font"),
287 MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
287 MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
288 MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
288 MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
289 "tkinter.simpledialog"),
289 "tkinter.simpledialog"),
290 MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"),
290 MovedModule("urllib_parse", __name__ + ".moves.urllib_parse", "urllib.parse"),
291 MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"),
291 MovedModule("urllib_error", __name__ + ".moves.urllib_error", "urllib.error"),
292 MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
292 MovedModule("urllib", __name__ + ".moves.urllib", __name__ + ".moves.urllib"),
293 MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
293 MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
294 MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
294 MovedModule("xmlrpc_client", "xmlrpclib", "xmlrpc.client"),
295 MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"),
295 MovedModule("xmlrpc_server", "SimpleXMLRPCServer", "xmlrpc.server"),
296 MovedModule("winreg", "_winreg"),
296 MovedModule("winreg", "_winreg"),
297 ]
297 ]
298 for attr in _moved_attributes:
298 for attr in _moved_attributes:
299 setattr(_MovedItems, attr.name, attr)
299 setattr(_MovedItems, attr.name, attr)
300 if isinstance(attr, MovedModule):
300 if isinstance(attr, MovedModule):
301 _importer._add_module(attr, "moves." + attr.name)
301 _importer._add_module(attr, "moves." + attr.name)
302 del attr
302 del attr
303
303
304 _MovedItems._moved_attributes = _moved_attributes
304 _MovedItems._moved_attributes = _moved_attributes
305
305
306 moves = _MovedItems(__name__ + ".moves")
306 moves = _MovedItems(__name__ + ".moves")
307 _importer._add_module(moves, "moves")
307 _importer._add_module(moves, "moves")
308
308
309
309
310 class Module_six_moves_urllib_parse(_LazyModule):
310 class Module_six_moves_urllib_parse(_LazyModule):
311 """Lazy loading of moved objects in six.moves.urllib_parse"""
311 """Lazy loading of moved objects in six.moves.urllib_parse"""
312
312
313
313
314 _urllib_parse_moved_attributes = [
314 _urllib_parse_moved_attributes = [
315 MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
315 MovedAttribute("ParseResult", "urlparse", "urllib.parse"),
316 MovedAttribute("SplitResult", "urlparse", "urllib.parse"),
316 MovedAttribute("SplitResult", "urlparse", "urllib.parse"),
317 MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
317 MovedAttribute("parse_qs", "urlparse", "urllib.parse"),
318 MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
318 MovedAttribute("parse_qsl", "urlparse", "urllib.parse"),
319 MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
319 MovedAttribute("urldefrag", "urlparse", "urllib.parse"),
320 MovedAttribute("urljoin", "urlparse", "urllib.parse"),
320 MovedAttribute("urljoin", "urlparse", "urllib.parse"),
321 MovedAttribute("urlparse", "urlparse", "urllib.parse"),
321 MovedAttribute("urlparse", "urlparse", "urllib.parse"),
322 MovedAttribute("urlsplit", "urlparse", "urllib.parse"),
322 MovedAttribute("urlsplit", "urlparse", "urllib.parse"),
323 MovedAttribute("urlunparse", "urlparse", "urllib.parse"),
323 MovedAttribute("urlunparse", "urlparse", "urllib.parse"),
324 MovedAttribute("urlunsplit", "urlparse", "urllib.parse"),
324 MovedAttribute("urlunsplit", "urlparse", "urllib.parse"),
325 MovedAttribute("quote", "urllib", "urllib.parse"),
325 MovedAttribute("quote", "urllib", "urllib.parse"),
326 MovedAttribute("quote_plus", "urllib", "urllib.parse"),
326 MovedAttribute("quote_plus", "urllib", "urllib.parse"),
327 MovedAttribute("unquote", "urllib", "urllib.parse"),
327 MovedAttribute("unquote", "urllib", "urllib.parse"),
328 MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
328 MovedAttribute("unquote_plus", "urllib", "urllib.parse"),
329 MovedAttribute("urlencode", "urllib", "urllib.parse"),
329 MovedAttribute("urlencode", "urllib", "urllib.parse"),
330 MovedAttribute("splitquery", "urllib", "urllib.parse"),
330 MovedAttribute("splitquery", "urllib", "urllib.parse"),
331 MovedAttribute("splittag", "urllib", "urllib.parse"),
331 MovedAttribute("splittag", "urllib", "urllib.parse"),
332 MovedAttribute("splituser", "urllib", "urllib.parse"),
332 MovedAttribute("splituser", "urllib", "urllib.parse"),
333 MovedAttribute("uses_fragment", "urlparse", "urllib.parse"),
333 MovedAttribute("uses_fragment", "urlparse", "urllib.parse"),
334 MovedAttribute("uses_netloc", "urlparse", "urllib.parse"),
334 MovedAttribute("uses_netloc", "urlparse", "urllib.parse"),
335 MovedAttribute("uses_params", "urlparse", "urllib.parse"),
335 MovedAttribute("uses_params", "urlparse", "urllib.parse"),
336 MovedAttribute("uses_query", "urlparse", "urllib.parse"),
336 MovedAttribute("uses_query", "urlparse", "urllib.parse"),
337 MovedAttribute("uses_relative", "urlparse", "urllib.parse"),
337 MovedAttribute("uses_relative", "urlparse", "urllib.parse"),
338 ]
338 ]
339 for attr in _urllib_parse_moved_attributes:
339 for attr in _urllib_parse_moved_attributes:
340 setattr(Module_six_moves_urllib_parse, attr.name, attr)
340 setattr(Module_six_moves_urllib_parse, attr.name, attr)
341 del attr
341 del attr
342
342
343 Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes
343 Module_six_moves_urllib_parse._moved_attributes = _urllib_parse_moved_attributes
344
344
345 _importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"),
345 _importer._add_module(Module_six_moves_urllib_parse(__name__ + ".moves.urllib_parse"),
346 "moves.urllib_parse", "moves.urllib.parse")
346 "moves.urllib_parse", "moves.urllib.parse")
347
347
348
348
349 class Module_six_moves_urllib_error(_LazyModule):
349 class Module_six_moves_urllib_error(_LazyModule):
350 """Lazy loading of moved objects in six.moves.urllib_error"""
350 """Lazy loading of moved objects in six.moves.urllib_error"""
351
351
352
352
353 _urllib_error_moved_attributes = [
353 _urllib_error_moved_attributes = [
354 MovedAttribute("URLError", "urllib2", "urllib.error"),
354 MovedAttribute("URLError", "urllib2", "urllib.error"),
355 MovedAttribute("HTTPError", "urllib2", "urllib.error"),
355 MovedAttribute("HTTPError", "urllib2", "urllib.error"),
356 MovedAttribute("ContentTooShortError", "urllib", "urllib.error"),
356 MovedAttribute("ContentTooShortError", "urllib", "urllib.error"),
357 ]
357 ]
358 for attr in _urllib_error_moved_attributes:
358 for attr in _urllib_error_moved_attributes:
359 setattr(Module_six_moves_urllib_error, attr.name, attr)
359 setattr(Module_six_moves_urllib_error, attr.name, attr)
360 del attr
360 del attr
361
361
362 Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes
362 Module_six_moves_urllib_error._moved_attributes = _urllib_error_moved_attributes
363
363
364 _importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"),
364 _importer._add_module(Module_six_moves_urllib_error(__name__ + ".moves.urllib.error"),
365 "moves.urllib_error", "moves.urllib.error")
365 "moves.urllib_error", "moves.urllib.error")
366
366
367
367
368 class Module_six_moves_urllib_request(_LazyModule):
368 class Module_six_moves_urllib_request(_LazyModule):
369 """Lazy loading of moved objects in six.moves.urllib_request"""
369 """Lazy loading of moved objects in six.moves.urllib_request"""
370
370
371
371
372 _urllib_request_moved_attributes = [
372 _urllib_request_moved_attributes = [
373 MovedAttribute("urlopen", "urllib2", "urllib.request"),
373 MovedAttribute("urlopen", "urllib2", "urllib.request"),
374 MovedAttribute("install_opener", "urllib2", "urllib.request"),
374 MovedAttribute("install_opener", "urllib2", "urllib.request"),
375 MovedAttribute("build_opener", "urllib2", "urllib.request"),
375 MovedAttribute("build_opener", "urllib2", "urllib.request"),
376 MovedAttribute("pathname2url", "urllib", "urllib.request"),
376 MovedAttribute("pathname2url", "urllib", "urllib.request"),
377 MovedAttribute("url2pathname", "urllib", "urllib.request"),
377 MovedAttribute("url2pathname", "urllib", "urllib.request"),
378 MovedAttribute("getproxies", "urllib", "urllib.request"),
378 MovedAttribute("getproxies", "urllib", "urllib.request"),
379 MovedAttribute("Request", "urllib2", "urllib.request"),
379 MovedAttribute("Request", "urllib2", "urllib.request"),
380 MovedAttribute("OpenerDirector", "urllib2", "urllib.request"),
380 MovedAttribute("OpenerDirector", "urllib2", "urllib.request"),
381 MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"),
381 MovedAttribute("HTTPDefaultErrorHandler", "urllib2", "urllib.request"),
382 MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"),
382 MovedAttribute("HTTPRedirectHandler", "urllib2", "urllib.request"),
383 MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"),
383 MovedAttribute("HTTPCookieProcessor", "urllib2", "urllib.request"),
384 MovedAttribute("ProxyHandler", "urllib2", "urllib.request"),
384 MovedAttribute("ProxyHandler", "urllib2", "urllib.request"),
385 MovedAttribute("BaseHandler", "urllib2", "urllib.request"),
385 MovedAttribute("BaseHandler", "urllib2", "urllib.request"),
386 MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"),
386 MovedAttribute("HTTPPasswordMgr", "urllib2", "urllib.request"),
387 MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"),
387 MovedAttribute("HTTPPasswordMgrWithDefaultRealm", "urllib2", "urllib.request"),
388 MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"),
388 MovedAttribute("AbstractBasicAuthHandler", "urllib2", "urllib.request"),
389 MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"),
389 MovedAttribute("HTTPBasicAuthHandler", "urllib2", "urllib.request"),
390 MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"),
390 MovedAttribute("ProxyBasicAuthHandler", "urllib2", "urllib.request"),
391 MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"),
391 MovedAttribute("AbstractDigestAuthHandler", "urllib2", "urllib.request"),
392 MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"),
392 MovedAttribute("HTTPDigestAuthHandler", "urllib2", "urllib.request"),
393 MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"),
393 MovedAttribute("ProxyDigestAuthHandler", "urllib2", "urllib.request"),
394 MovedAttribute("HTTPHandler", "urllib2", "urllib.request"),
394 MovedAttribute("HTTPHandler", "urllib2", "urllib.request"),
395 MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"),
395 MovedAttribute("HTTPSHandler", "urllib2", "urllib.request"),
396 MovedAttribute("FileHandler", "urllib2", "urllib.request"),
396 MovedAttribute("FileHandler", "urllib2", "urllib.request"),
397 MovedAttribute("FTPHandler", "urllib2", "urllib.request"),
397 MovedAttribute("FTPHandler", "urllib2", "urllib.request"),
398 MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"),
398 MovedAttribute("CacheFTPHandler", "urllib2", "urllib.request"),
399 MovedAttribute("UnknownHandler", "urllib2", "urllib.request"),
399 MovedAttribute("UnknownHandler", "urllib2", "urllib.request"),
400 MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"),
400 MovedAttribute("HTTPErrorProcessor", "urllib2", "urllib.request"),
401 MovedAttribute("urlretrieve", "urllib", "urllib.request"),
401 MovedAttribute("urlretrieve", "urllib", "urllib.request"),
402 MovedAttribute("urlcleanup", "urllib", "urllib.request"),
402 MovedAttribute("urlcleanup", "urllib", "urllib.request"),
403 MovedAttribute("URLopener", "urllib", "urllib.request"),
403 MovedAttribute("URLopener", "urllib", "urllib.request"),
404 MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
404 MovedAttribute("FancyURLopener", "urllib", "urllib.request"),
405 MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
405 MovedAttribute("proxy_bypass", "urllib", "urllib.request"),
406 ]
406 ]
407 for attr in _urllib_request_moved_attributes:
407 for attr in _urllib_request_moved_attributes:
408 setattr(Module_six_moves_urllib_request, attr.name, attr)
408 setattr(Module_six_moves_urllib_request, attr.name, attr)
409 del attr
409 del attr
410
410
411 Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes
411 Module_six_moves_urllib_request._moved_attributes = _urllib_request_moved_attributes
412
412
413 _importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"),
413 _importer._add_module(Module_six_moves_urllib_request(__name__ + ".moves.urllib.request"),
414 "moves.urllib_request", "moves.urllib.request")
414 "moves.urllib_request", "moves.urllib.request")
415
415
416
416
417 class Module_six_moves_urllib_response(_LazyModule):
417 class Module_six_moves_urllib_response(_LazyModule):
418 """Lazy loading of moved objects in six.moves.urllib_response"""
418 """Lazy loading of moved objects in six.moves.urllib_response"""
419
419
420
420
421 _urllib_response_moved_attributes = [
421 _urllib_response_moved_attributes = [
422 MovedAttribute("addbase", "urllib", "urllib.response"),
422 MovedAttribute("addbase", "urllib", "urllib.response"),
423 MovedAttribute("addclosehook", "urllib", "urllib.response"),
423 MovedAttribute("addclosehook", "urllib", "urllib.response"),
424 MovedAttribute("addinfo", "urllib", "urllib.response"),
424 MovedAttribute("addinfo", "urllib", "urllib.response"),
425 MovedAttribute("addinfourl", "urllib", "urllib.response"),
425 MovedAttribute("addinfourl", "urllib", "urllib.response"),
426 ]
426 ]
427 for attr in _urllib_response_moved_attributes:
427 for attr in _urllib_response_moved_attributes:
428 setattr(Module_six_moves_urllib_response, attr.name, attr)
428 setattr(Module_six_moves_urllib_response, attr.name, attr)
429 del attr
429 del attr
430
430
431 Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes
431 Module_six_moves_urllib_response._moved_attributes = _urllib_response_moved_attributes
432
432
433 _importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"),
433 _importer._add_module(Module_six_moves_urllib_response(__name__ + ".moves.urllib.response"),
434 "moves.urllib_response", "moves.urllib.response")
434 "moves.urllib_response", "moves.urllib.response")
435
435
436
436
437 class Module_six_moves_urllib_robotparser(_LazyModule):
437 class Module_six_moves_urllib_robotparser(_LazyModule):
438 """Lazy loading of moved objects in six.moves.urllib_robotparser"""
438 """Lazy loading of moved objects in six.moves.urllib_robotparser"""
439
439
440
440
441 _urllib_robotparser_moved_attributes = [
441 _urllib_robotparser_moved_attributes = [
442 MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
442 MovedAttribute("RobotFileParser", "robotparser", "urllib.robotparser"),
443 ]
443 ]
444 for attr in _urllib_robotparser_moved_attributes:
444 for attr in _urllib_robotparser_moved_attributes:
445 setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
445 setattr(Module_six_moves_urllib_robotparser, attr.name, attr)
446 del attr
446 del attr
447
447
448 Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes
448 Module_six_moves_urllib_robotparser._moved_attributes = _urllib_robotparser_moved_attributes
449
449
450 _importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"),
450 _importer._add_module(Module_six_moves_urllib_robotparser(__name__ + ".moves.urllib.robotparser"),
451 "moves.urllib_robotparser", "moves.urllib.robotparser")
451 "moves.urllib_robotparser", "moves.urllib.robotparser")
452
452
453
453
454 class Module_six_moves_urllib(types.ModuleType):
454 class Module_six_moves_urllib(types.ModuleType):
455 """Create a six.moves.urllib namespace that resembles the Python 3 namespace"""
455 """Create a six.moves.urllib namespace that resembles the Python 3 namespace"""
456 __path__ = [] # mark as package
456 __path__ = [] # mark as package
457 parse = _importer._get_module("moves.urllib_parse")
457 parse = _importer._get_module("moves.urllib_parse")
458 error = _importer._get_module("moves.urllib_error")
458 error = _importer._get_module("moves.urllib_error")
459 request = _importer._get_module("moves.urllib_request")
459 request = _importer._get_module("moves.urllib_request")
460 response = _importer._get_module("moves.urllib_response")
460 response = _importer._get_module("moves.urllib_response")
461 robotparser = _importer._get_module("moves.urllib_robotparser")
461 robotparser = _importer._get_module("moves.urllib_robotparser")
462
462
463 def __dir__(self):
463 def __dir__(self):
464 return ['parse', 'error', 'request', 'response', 'robotparser']
464 return ['parse', 'error', 'request', 'response', 'robotparser']
465
465
466 _importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"),
466 _importer._add_module(Module_six_moves_urllib(__name__ + ".moves.urllib"),
467 "moves.urllib")
467 "moves.urllib")
468
468
469
469
470 def add_move(move):
470 def add_move(move):
471 """Add an item to six.moves."""
471 """Add an item to six.moves."""
472 setattr(_MovedItems, move.name, move)
472 setattr(_MovedItems, move.name, move)
473
473
474
474
475 def remove_move(name):
475 def remove_move(name):
476 """Remove item from six.moves."""
476 """Remove item from six.moves."""
477 try:
477 try:
478 delattr(_MovedItems, name)
478 delattr(_MovedItems, name)
479 except AttributeError:
479 except AttributeError:
480 try:
480 try:
481 del moves.__dict__[name]
481 del moves.__dict__[name]
482 except KeyError:
482 except KeyError:
483 raise AttributeError("no such move, %r" % (name,))
483 raise AttributeError("no such move, %r" % (name,))
484
484
485
485
486 if PY3:
486 if PY3:
487 _meth_func = "__func__"
487 _meth_func = "__func__"
488 _meth_self = "__self__"
488 _meth_self = "__self__"
489
489
490 _func_closure = "__closure__"
490 _func_closure = "__closure__"
491 _func_code = "__code__"
491 _func_code = "__code__"
492 _func_defaults = "__defaults__"
492 _func_defaults = "__defaults__"
493 _func_globals = "__globals__"
493 _func_globals = "__globals__"
494 else:
494 else:
495 _meth_func = "im_func"
495 _meth_func = "im_func"
496 _meth_self = "im_self"
496 _meth_self = "im_self"
497
497
498 _func_closure = "func_closure"
498 _func_closure = "func_closure"
499 _func_code = "func_code"
499 _func_code = "func_code"
500 _func_defaults = "func_defaults"
500 _func_defaults = "func_defaults"
501 _func_globals = "func_globals"
501 _func_globals = "func_globals"
502
502
503
503
504 try:
504 try:
505 advance_iterator = next
505 advance_iterator = next
506 except NameError:
506 except NameError:
507 def advance_iterator(it):
507 def advance_iterator(it):
508 return it.next()
508 return it.next()
509 next = advance_iterator
509 next = advance_iterator
510
510
511
511
512 try:
512 try:
513 callable = callable
513 callable = callable
514 except NameError:
514 except NameError:
515 def callable(obj):
515 def callable(obj):
516 return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
516 return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
517
517
518
518
519 if PY3:
519 if PY3:
520 def get_unbound_function(unbound):
520 def get_unbound_function(unbound):
521 return unbound
521 return unbound
522
522
523 create_bound_method = types.MethodType
523 create_bound_method = types.MethodType
524
524
525 Iterator = object
525 Iterator = object
526 else:
526 else:
527 def get_unbound_function(unbound):
527 def get_unbound_function(unbound):
528 return unbound.im_func
528 return unbound.im_func
529
529
530 def create_bound_method(func, obj):
530 def create_bound_method(func, obj):
531 return types.MethodType(func, obj, obj.__class__)
531 return types.MethodType(func, obj, obj.__class__)
532
532
533 class Iterator(object):
533 class Iterator(object):
534
534
535 def next(self):
535 def next(self):
536 return type(self).__next__(self)
536 return type(self).__next__(self)
537
537
538 callable = callable
538 callable = callable
539 _add_doc(get_unbound_function,
539 _add_doc(get_unbound_function,
540 """Get the function out of a possibly unbound function""")
540 """Get the function out of a possibly unbound function""")
541
541
542
542
543 get_method_function = operator.attrgetter(_meth_func)
543 get_method_function = operator.attrgetter(_meth_func)
544 get_method_self = operator.attrgetter(_meth_self)
544 get_method_self = operator.attrgetter(_meth_self)
545 get_function_closure = operator.attrgetter(_func_closure)
545 get_function_closure = operator.attrgetter(_func_closure)
546 get_function_code = operator.attrgetter(_func_code)
546 get_function_code = operator.attrgetter(_func_code)
547 get_function_defaults = operator.attrgetter(_func_defaults)
547 get_function_defaults = operator.attrgetter(_func_defaults)
548 get_function_globals = operator.attrgetter(_func_globals)
548 get_function_globals = operator.attrgetter(_func_globals)
549
549
550
550
551 if PY3:
551 if PY3:
552 def iterkeys(d, **kw):
552 def iterkeys(d, **kw):
553 return iter(d.keys(**kw))
553 return iter(d.keys(**kw))
554
554
555 def itervalues(d, **kw):
555 def itervalues(d, **kw):
556 return iter(d.values(**kw))
556 return iter(d.values(**kw))
557
557
558 def iteritems(d, **kw):
558 def iteritems(d, **kw):
559 return iter(d.items(**kw))
559 return iter(d.items(**kw))
560
560
561 def iterlists(d, **kw):
561 def iterlists(d, **kw):
562 return iter(d.lists(**kw))
562 return iter(d.lists(**kw))
563
563
564 viewkeys = operator.methodcaller("keys")
564 viewkeys = operator.methodcaller("keys")
565
565
566 viewvalues = operator.methodcaller("values")
566 viewvalues = operator.methodcaller("values")
567
567
568 viewitems = operator.methodcaller("items")
568 viewitems = operator.methodcaller("items")
569 else:
569 else:
570 def iterkeys(d, **kw):
570 def iterkeys(d, **kw):
571 return iter(d.iterkeys(**kw))
571 return iter(d.iterkeys(**kw))
572
572
573 def itervalues(d, **kw):
573 def itervalues(d, **kw):
574 return iter(d.itervalues(**kw))
574 return iter(d.itervalues(**kw))
575
575
576 def iteritems(d, **kw):
576 def iteritems(d, **kw):
577 return iter(d.iteritems(**kw))
577 return iter(d.iteritems(**kw))
578
578
579 def iterlists(d, **kw):
579 def iterlists(d, **kw):
580 return iter(d.iterlists(**kw))
580 return iter(d.iterlists(**kw))
581
581
582 viewkeys = operator.methodcaller("viewkeys")
582 viewkeys = operator.methodcaller("viewkeys")
583
583
584 viewvalues = operator.methodcaller("viewvalues")
584 viewvalues = operator.methodcaller("viewvalues")
585
585
586 viewitems = operator.methodcaller("viewitems")
586 viewitems = operator.methodcaller("viewitems")
587
587
588 _add_doc(iterkeys, "Return an iterator over the keys of a dictionary.")
588 _add_doc(iterkeys, "Return an iterator over the keys of a dictionary.")
589 _add_doc(itervalues, "Return an iterator over the values of a dictionary.")
589 _add_doc(itervalues, "Return an iterator over the values of a dictionary.")
590 _add_doc(iteritems,
590 _add_doc(iteritems,
591 "Return an iterator over the (key, value) pairs of a dictionary.")
591 "Return an iterator over the (key, value) pairs of a dictionary.")
592 _add_doc(iterlists,
592 _add_doc(iterlists,
593 "Return an iterator over the (key, [values]) pairs of a dictionary.")
593 "Return an iterator over the (key, [values]) pairs of a dictionary.")
594
594
595
595
596 if PY3:
596 if PY3:
597 def b(s):
597 def b(s):
598 return s.encode("latin-1")
598 return s.encode("latin-1")
599 def u(s):
599 def u(s):
600 return s
600 return s
601 unichr = chr
601 unichr = chr
602 if sys.version_info[1] <= 1:
602 if sys.version_info[1] <= 1:
603 def int2byte(i):
603 def int2byte(i):
604 return bytes((i,))
604 return bytes((i,))
605 else:
605 else:
606 # This is about 2x faster than the implementation above on 3.2+
606 # This is about 2x faster than the implementation above on 3.2+
607 int2byte = operator.methodcaller("to_bytes", 1, "big")
607 int2byte = operator.methodcaller("to_bytes", 1, "big")
608 byte2int = operator.itemgetter(0)
608 byte2int = operator.itemgetter(0)
609 indexbytes = operator.getitem
609 indexbytes = operator.getitem
610 iterbytes = iter
610 iterbytes = iter
611 import io
611 import io
612 StringIO = io.StringIO
612 StringIO = io.StringIO
613 BytesIO = io.BytesIO
613 BytesIO = io.BytesIO
614 _assertCountEqual = "assertCountEqual"
614 _assertCountEqual = "assertCountEqual"
615 _assertRaisesRegex = "assertRaisesRegex"
615 _assertRaisesRegex = "assertRaisesRegex"
616 _assertRegex = "assertRegex"
616 _assertRegex = "assertRegex"
617 else:
617 else:
618 def b(s):
618 def b(s):
619 return s
619 return s
620 # Workaround for standalone backslash
620 # Workaround for standalone backslash
621 def u(s):
621 def u(s):
622 return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
622 return unicode(s.replace(r'\\', r'\\\\'), "unicode_escape")
623 unichr = unichr
623 unichr = unichr
624 int2byte = chr
624 int2byte = chr
625 def byte2int(bs):
625 def byte2int(bs):
626 return ord(bs[0])
626 return ord(bs[0])
627 def indexbytes(buf, i):
627 def indexbytes(buf, i):
628 return ord(buf[i])
628 return ord(buf[i])
629 iterbytes = functools.partial(itertools.imap, ord)
629 iterbytes = functools.partial(itertools.imap, ord)
630 from io import StringIO
630 from io import StringIO
631 StringIO = BytesIO = StringIO.StringIO
631 StringIO = BytesIO = StringIO.StringIO
632 _assertCountEqual = "assertItemsEqual"
632 _assertCountEqual = "assertItemsEqual"
633 _assertRaisesRegex = "assertRaisesRegexp"
633 _assertRaisesRegex = "assertRaisesRegexp"
634 _assertRegex = "assertRegexpMatches"
634 _assertRegex = "assertRegexpMatches"
635 _add_doc(b, """Byte literal""")
635 _add_doc(b, """Byte literal""")
636 _add_doc(u, """Text literal""")
636 _add_doc(u, """Text literal""")
637
637
638
638
639 def assertCountEqual(self, *args, **kwargs):
639 def assertCountEqual(self, *args, **kwargs):
640 return getattr(self, _assertCountEqual)(*args, **kwargs)
640 return getattr(self, _assertCountEqual)(*args, **kwargs)
641
641
642
642
643 def assertRaisesRegex(self, *args, **kwargs):
643 def assertRaisesRegex(self, *args, **kwargs):
644 return getattr(self, _assertRaisesRegex)(*args, **kwargs)
644 return getattr(self, _assertRaisesRegex)(*args, **kwargs)
645
645
646
646
647 def assertRegex(self, *args, **kwargs):
647 def assertRegex(self, *args, **kwargs):
648 return getattr(self, _assertRegex)(*args, **kwargs)
648 return getattr(self, _assertRegex)(*args, **kwargs)
649
649
650
650
651 if PY3:
651 if PY3:
652 exec_ = getattr(moves.builtins, "exec")
652 exec_ = getattr(moves.builtins, "exec")
653
653
654
654
655 def reraise(tp, value, tb=None):
655 def reraise(tp, value, tb=None):
656 if value is None:
656 if value is None:
657 value = tp()
657 value = tp()
658 if value.__traceback__ is not tb:
658 if value.__traceback__ is not tb:
659 raise value.with_traceback(tb)
659 raise value.with_traceback(tb)
660 raise value
660 raise value
661
661
662 else:
662 else:
663 def exec_(_code_, _globs_=None, _locs_=None):
663 def exec_(_code_, _globs_=None, _locs_=None):
664 """Execute code in a namespace."""
664 """Execute code in a namespace."""
665 if _globs_ is None:
665 if _globs_ is None:
666 frame = sys._getframe(1)
666 frame = sys._getframe(1)
667 _globs_ = frame.f_globals
667 _globs_ = frame.f_globals
668 if _locs_ is None:
668 if _locs_ is None:
669 _locs_ = frame.f_locals
669 _locs_ = frame.f_locals
670 del frame
670 del frame
671 elif _locs_ is None:
671 elif _locs_ is None:
672 _locs_ = _globs_
672 _locs_ = _globs_
673 exec("""exec _code_ in _globs_, _locs_""")
673 exec("""exec _code_ in _globs_, _locs_""")
674
674
675
675
676 exec_("""def reraise(tp, value, tb=None):
676 exec_("""def reraise(tp, value, tb=None):
677 raise tp, value, tb
677 raise tp, value, tb
678 """)
678 """)
679
679
680
680
681 if sys.version_info[:2] == (3, 2):
681 if sys.version_info[:2] == (3, 2):
682 exec_("""def raise_from(value, from_value):
682 exec_("""def raise_from(value, from_value):
683 if from_value is None:
683 if from_value is None:
684 raise value
684 raise value
685 raise value from from_value
685 raise value from from_value
686 """)
686 """)
687 elif sys.version_info[:2] > (3, 2):
687 elif sys.version_info[:2] > (3, 2):
688 exec_("""def raise_from(value, from_value):
688 exec_("""def raise_from(value, from_value):
689 raise value from from_value
689 raise value from from_value
690 """)
690 """)
691 else:
691 else:
692 def raise_from(value, from_value):
692 def raise_from(value, from_value):
693 raise value
693 raise value
694
694
695
695
696 print_ = getattr(moves.builtins, "print", None)
696 print_ = getattr(moves.builtins, "print", None)
697 if print_ is None:
697 if print_ is None:
698 def print_(*args, **kwargs):
698 def print_(*args, **kwargs):
699 """The new-style print function for Python 2.4 and 2.5."""
699 """The new-style print function for Python 2.4 and 2.5."""
700 fp = kwargs.pop("file", sys.stdout)
700 fp = kwargs.pop("file", sys.stdout)
701 if fp is None:
701 if fp is None:
702 return
702 return
703 def write(data):
703 def write(data):
704 if not isinstance(data, str):
704 if not isinstance(data, str):
705 data = str(data)
705 data = str(data)
706 # If the file has an encoding, encode unicode with it.
706 # If the file has an encoding, encode unicode with it.
707 if (isinstance(fp, file) and
707 if (isinstance(fp, file) and
708 isinstance(data, unicode) and
708 isinstance(data, unicode) and
709 fp.encoding is not None):
709 fp.encoding is not None):
710 errors = getattr(fp, "errors", None)
710 errors = getattr(fp, "errors", None)
711 if errors is None:
711 if errors is None:
712 errors = "strict"
712 errors = "strict"
713 data = data.encode(fp.encoding, errors)
713 data = data.encode(fp.encoding, errors)
714 fp.write(data)
714 fp.write(data)
715 want_unicode = False
715 want_unicode = False
716 sep = kwargs.pop("sep", None)
716 sep = kwargs.pop("sep", None)
717 if sep is not None:
717 if sep is not None:
718 if isinstance(sep, unicode):
718 if isinstance(sep, unicode):
719 want_unicode = True
719 want_unicode = True
720 elif not isinstance(sep, str):
720 elif not isinstance(sep, str):
721 raise TypeError("sep must be None or a string")
721 raise TypeError("sep must be None or a string")
722 end = kwargs.pop("end", None)
722 end = kwargs.pop("end", None)
723 if end is not None:
723 if end is not None:
724 if isinstance(end, unicode):
724 if isinstance(end, unicode):
725 want_unicode = True
725 want_unicode = True
726 elif not isinstance(end, str):
726 elif not isinstance(end, str):
727 raise TypeError("end must be None or a string")
727 raise TypeError("end must be None or a string")
728 if kwargs:
728 if kwargs:
729 raise TypeError("invalid keyword arguments to print()")
729 raise TypeError("invalid keyword arguments to print()")
730 if not want_unicode:
730 if not want_unicode:
731 for arg in args:
731 for arg in args:
732 if isinstance(arg, unicode):
732 if isinstance(arg, unicode):
733 want_unicode = True
733 want_unicode = True
734 break
734 break
735 if want_unicode:
735 if want_unicode:
736 newline = unicode("\n")
736 newline = unicode("\n")
737 space = unicode(" ")
737 space = unicode(" ")
738 else:
738 else:
739 newline = "\n"
739 newline = "\n"
740 space = " "
740 space = " "
741 if sep is None:
741 if sep is None:
742 sep = space
742 sep = space
743 if end is None:
743 if end is None:
744 end = newline
744 end = newline
745 for i, arg in enumerate(args):
745 for i, arg in enumerate(args):
746 if i:
746 if i:
747 write(sep)
747 write(sep)
748 write(arg)
748 write(arg)
749 write(end)
749 write(end)
750 if sys.version_info[:2] < (3, 3):
750 if sys.version_info[:2] < (3, 3):
751 _print = print_
751 _print = print_
752 def print_(*args, **kwargs):
752 def print_(*args, **kwargs):
753 fp = kwargs.get("file", sys.stdout)
753 fp = kwargs.get("file", sys.stdout)
754 flush = kwargs.pop("flush", False)
754 flush = kwargs.pop("flush", False)
755 _print(*args, **kwargs)
755 _print(*args, **kwargs)
756 if flush and fp is not None:
756 if flush and fp is not None:
757 fp.flush()
757 fp.flush()
758
758
759 _add_doc(reraise, """Reraise an exception.""")
759 _add_doc(reraise, """Reraise an exception.""")
760
760
761 if sys.version_info[0:2] < (3, 4):
761 if sys.version_info[0:2] < (3, 4):
762 def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
762 def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS,
763 updated=functools.WRAPPER_UPDATES):
763 updated=functools.WRAPPER_UPDATES):
764 def wrapper(f):
764 def wrapper(f):
765 f = functools.wraps(wrapped, assigned, updated)(f)
765 f = functools.wraps(wrapped, assigned, updated)(f)
766 f.__wrapped__ = wrapped
766 f.__wrapped__ = wrapped
767 return f
767 return f
768 return wrapper
768 return wrapper
769 else:
769 else:
770 wraps = functools.wraps
770 wraps = functools.wraps
771
771
772 def with_metaclass(meta, *bases):
772 def with_metaclass(meta, *bases):
773 """Create a base class with a metaclass."""
773 """Create a base class with a metaclass."""
774 # This requires a bit of explanation: the basic idea is to make a dummy
774 # This requires a bit of explanation: the basic idea is to make a dummy
775 # metaclass for one level of class instantiation that replaces itself with
775 # metaclass for one level of class instantiation that replaces itself with
776 # the actual metaclass.
776 # the actual metaclass.
777 class metaclass(meta):
777 class metaclass(meta):
778 def __new__(cls, name, this_bases, d):
778 def __new__(cls, name, this_bases, d):
779 return meta(name, bases, d)
779 return meta(name, bases, d)
780 return type.__new__(metaclass, 'temporary_class', (), {})
780 return type.__new__(metaclass, 'temporary_class', (), {})
781
781
782
782
783 def add_metaclass(metaclass):
783 def add_metaclass(metaclass):
784 """Class decorator for creating a class with a metaclass."""
784 """Class decorator for creating a class with a metaclass."""
785 def wrapper(cls):
785 def wrapper(cls):
786 orig_vars = cls.__dict__.copy()
786 orig_vars = cls.__dict__.copy()
787 slots = orig_vars.get('__slots__')
787 slots = orig_vars.get('__slots__')
788 if slots is not None:
788 if slots is not None:
789 if isinstance(slots, str):
789 if isinstance(slots, str):
790 slots = [slots]
790 slots = [slots]
791 for slots_var in slots:
791 for slots_var in slots:
792 orig_vars.pop(slots_var)
792 orig_vars.pop(slots_var)
793 orig_vars.pop('__dict__', None)
793 orig_vars.pop('__dict__', None)
794 orig_vars.pop('__weakref__', None)
794 orig_vars.pop('__weakref__', None)
795 return metaclass(cls.__name__, cls.__bases__, orig_vars)
795 return metaclass(cls.__name__, cls.__bases__, orig_vars)
796 return wrapper
796 return wrapper
797
797
798
798
799 def python_2_unicode_compatible(klass):
799 def python_2_unicode_compatible(klass):
800 """
800 """
801 A decorator that defines __unicode__ and __str__ methods under Python 2.
801 A decorator that defines __unicode__ and __str__ methods under Python 2.
802 Under Python 3 it does nothing.
802 Under Python 3 it does nothing.
803
803
804 To support Python 2 and 3 with a single code base, define a __str__ method
804 To support Python 2 and 3 with a single code base, define a __str__ method
805 returning text and apply this decorator to the class.
805 returning text and apply this decorator to the class.
806 """
806 """
807 if PY2:
807 if PY2:
808 if '__str__' not in klass.__dict__:
808 if '__str__' not in klass.__dict__:
809 raise ValueError("@python_2_unicode_compatible cannot be applied "
809 raise ValueError("@python_2_unicode_compatible cannot be applied "
810 "to %s because it doesn't define __str__()." %
810 "to %s because it doesn't define __str__()." %
811 klass.__name__)
811 klass.__name__)
812 klass.__unicode__ = klass.__str__
812 klass.__unicode__ = klass.__str__
813 klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
813 klass.__str__ = lambda self: self.__unicode__().encode('utf-8')
814 return klass
814 return klass
815
815
816
816
817 # Complete the moves implementation.
817 # Complete the moves implementation.
818 # This code is at the end of this module to speed up module loading.
818 # This code is at the end of this module to speed up module loading.
819 # Turn this module into a package.
819 # Turn this module into a package.
820 __path__ = [] # required for PEP 302 and PEP 451
820 __path__ = [] # required for PEP 302 and PEP 451
821 __package__ = __name__ # see PEP 366 @ReservedAssignment
821 __package__ = __name__ # see PEP 366 @ReservedAssignment
822 if globals().get("__spec__") is not None:
822 if globals().get("__spec__") is not None:
823 __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable
823 __spec__.submodule_search_locations = [] # PEP 451 @UndefinedVariable
824 # Remove other six meta path importers, since they cause problems. This can
824 # Remove other six meta path importers, since they cause problems. This can
825 # happen if six is removed from sys.modules and then reloaded. (Setuptools does
825 # happen if six is removed from sys.modules and then reloaded. (Setuptools does
826 # this for some reason.)
826 # this for some reason.)
827 if sys.meta_path:
827 if sys.meta_path:
828 for i, importer in enumerate(sys.meta_path):
828 for i, importer in enumerate(sys.meta_path):
829 # Here's some real nastiness: Another "instance" of the six module might
829 # Here's some real nastiness: Another "instance" of the six module might
830 # be floating around. Therefore, we can't use isinstance() to check for
830 # be floating around. Therefore, we can't use isinstance() to check for
831 # the six meta path importer, since the other six instance will have
831 # the six meta path importer, since the other six instance will have
832 # inserted an importer with different class.
832 # inserted an importer with different class.
833 if (type(importer).__name__ == "_SixMetaPathImporter" and
833 if (type(importer).__name__ == "_SixMetaPathImporter" and
834 importer.name == __name__):
834 importer.name == __name__):
835 del sys.meta_path[i]
835 del sys.meta_path[i]
836 break
836 break
837 del i, importer
837 del i, importer
838 # Finally, add the importer to the meta path import hook.
838 # Finally, add the importer to the meta path import hook.
839 sys.meta_path.append(_importer)
839 sys.meta_path.append(_importer)
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now