##// END OF EJS Templates
auth: don't expose full set of permissions into channelstream payload....
ergo -
r2157:aefa7aac default
parent child Browse files
Show More
@@ -1,175 +1,176 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Channel Stream controller for rhodecode
22 Channel Stream controller for rhodecode
23
23
24 :created_on: Oct 10, 2015
24 :created_on: Oct 10, 2015
25 :author: marcinl
25 :author: marcinl
26 :copyright: (c) 2013-2015 RhodeCode GmbH.
26 :copyright: (c) 2013-2015 RhodeCode GmbH.
27 :license: Commercial License, see LICENSE for more details.
27 :license: Commercial License, see LICENSE for more details.
28 """
28 """
29
29
30 import logging
30 import logging
31 import uuid
31 import uuid
32
32
33 from pyramid.view import view_config
33 from pyramid.view import view_config
34 from webob.exc import HTTPBadRequest, HTTPForbidden, HTTPBadGateway
34 from webob.exc import HTTPBadRequest, HTTPForbidden, HTTPBadGateway
35
35
36 from rhodecode.lib.channelstream import (
36 from rhodecode.lib.channelstream import (
37 channelstream_request,
37 channelstream_request,
38 ChannelstreamConnectionException,
38 ChannelstreamConnectionException,
39 ChannelstreamPermissionException,
39 ChannelstreamPermissionException,
40 check_channel_permissions,
40 check_channel_permissions,
41 get_connection_validators,
41 get_connection_validators,
42 get_user_data,
42 get_user_data,
43 parse_channels_info,
43 parse_channels_info,
44 update_history_from_logs,
44 update_history_from_logs,
45 STATE_PUBLIC_KEYS)
45 STATE_PUBLIC_KEYS)
46 from rhodecode.lib.auth import NotAnonymous
46 from rhodecode.lib.auth import NotAnonymous
47
47
48 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
49
49
50
50
51 class ChannelstreamView(object):
51 class ChannelstreamView(object):
52 def __init__(self, context, request):
52 def __init__(self, context, request):
53 self.context = context
53 self.context = context
54 self.request = request
54 self.request = request
55
55
56 # Some of the decorators rely on this attribute to be present
56 # Some of the decorators rely on this attribute to be present
57 # on the class of the decorated method.
57 # on the class of the decorated method.
58 self._rhodecode_user = request.user
58 self._rhodecode_user = request.user
59 registry = request.registry
59 registry = request.registry
60 self.channelstream_config = registry.rhodecode_plugins['channelstream']
60 self.channelstream_config = registry.rhodecode_plugins['channelstream']
61 if not self.channelstream_config.get('enabled'):
61 if not self.channelstream_config.get('enabled'):
62 log.error('Channelstream plugin is disabled')
62 log.error('Channelstream plugin is disabled')
63 raise HTTPBadRequest()
63 raise HTTPBadRequest()
64
64
65 @NotAnonymous()
65 @NotAnonymous()
66 @view_config(route_name='channelstream_connect', renderer='json')
66 @view_config(route_name='channelstream_connect', renderer='json')
67 def connect(self):
67 def connect(self):
68 """ handle authorization of users trying to connect """
68 """ handle authorization of users trying to connect """
69 try:
69 try:
70 json_body = self.request.json_body
70 json_body = self.request.json_body
71 except Exception:
71 except Exception:
72 log.exception('Failed to decode json from request')
72 log.exception('Failed to decode json from request')
73 raise HTTPBadRequest()
73 raise HTTPBadRequest()
74
74 try:
75 try:
75 channels = check_channel_permissions(
76 channels = check_channel_permissions(
76 json_body.get('channels'),
77 json_body.get('channels'),
77 get_connection_validators(self.request.registry))
78 get_connection_validators(self.request.registry))
78 except ChannelstreamPermissionException:
79 except ChannelstreamPermissionException:
79 log.error('Incorrect permissions for requested channels')
80 log.error('Incorrect permissions for requested channels')
80 raise HTTPForbidden()
81 raise HTTPForbidden()
81
82
82 user = self._rhodecode_user
83 user = self._rhodecode_user
83 if user.user_id:
84 if user.user_id:
84 user_data = get_user_data(user.user_id)
85 user_data = get_user_data(user.user_id)
85 else:
86 else:
86 user_data = {
87 user_data = {
87 'id': None,
88 'id': None,
88 'username': None,
89 'username': None,
89 'first_name': None,
90 'first_name': None,
90 'last_name': None,
91 'last_name': None,
91 'icon_link': None,
92 'icon_link': None,
92 'display_name': None,
93 'display_name': None,
93 'display_link': None,
94 'display_link': None,
94 }
95 }
95 user_data['permissions'] = self._rhodecode_user.permissions
96 user_data['permissions'] = self._rhodecode_user.permissions_safe
96 payload = {
97 payload = {
97 'username': user.username,
98 'username': user.username,
98 'user_state': user_data,
99 'user_state': user_data,
99 'conn_id': str(uuid.uuid4()),
100 'conn_id': str(uuid.uuid4()),
100 'channels': channels,
101 'channels': channels,
101 'channel_configs': {},
102 'channel_configs': {},
102 'state_public_keys': STATE_PUBLIC_KEYS,
103 'state_public_keys': STATE_PUBLIC_KEYS,
103 'info': {
104 'info': {
104 'exclude_channels': ['broadcast']
105 'exclude_channels': ['broadcast']
105 }
106 }
106 }
107 }
107 filtered_channels = [channel for channel in channels
108 filtered_channels = [channel for channel in channels
108 if channel != 'broadcast']
109 if channel != 'broadcast']
109 for channel in filtered_channels:
110 for channel in filtered_channels:
110 payload['channel_configs'][channel] = {
111 payload['channel_configs'][channel] = {
111 'notify_presence': True,
112 'notify_presence': True,
112 'history_size': 100,
113 'history_size': 100,
113 'store_history': True,
114 'store_history': True,
114 'broadcast_presence_with_user_lists': True
115 'broadcast_presence_with_user_lists': True
115 }
116 }
116 # connect user to server
117 # connect user to server
117 try:
118 try:
118 connect_result = channelstream_request(self.channelstream_config,
119 connect_result = channelstream_request(self.channelstream_config,
119 payload, '/connect')
120 payload, '/connect')
120 except ChannelstreamConnectionException:
121 except ChannelstreamConnectionException:
121 log.exception('Channelstream service is down')
122 log.exception('Channelstream service is down')
122 return HTTPBadGateway()
123 return HTTPBadGateway()
123
124
124 connect_result['channels'] = channels
125 connect_result['channels'] = channels
125 connect_result['channels_info'] = parse_channels_info(
126 connect_result['channels_info'] = parse_channels_info(
126 connect_result['channels_info'],
127 connect_result['channels_info'],
127 include_channel_info=filtered_channels)
128 include_channel_info=filtered_channels)
128 update_history_from_logs(self.channelstream_config,
129 update_history_from_logs(self.channelstream_config,
129 filtered_channels, connect_result)
130 filtered_channels, connect_result)
130 return connect_result
131 return connect_result
131
132
132 @NotAnonymous()
133 @NotAnonymous()
133 @view_config(route_name='channelstream_subscribe', renderer='json')
134 @view_config(route_name='channelstream_subscribe', renderer='json')
134 def subscribe(self):
135 def subscribe(self):
135 """ can be used to subscribe specific connection to other channels """
136 """ can be used to subscribe specific connection to other channels """
136 try:
137 try:
137 json_body = self.request.json_body
138 json_body = self.request.json_body
138 except Exception:
139 except Exception:
139 log.exception('Failed to decode json from request')
140 log.exception('Failed to decode json from request')
140 raise HTTPBadRequest()
141 raise HTTPBadRequest()
141 try:
142 try:
142 channels = check_channel_permissions(
143 channels = check_channel_permissions(
143 json_body.get('channels'),
144 json_body.get('channels'),
144 get_connection_validators(self.request.registry))
145 get_connection_validators(self.request.registry))
145 except ChannelstreamPermissionException:
146 except ChannelstreamPermissionException:
146 log.error('Incorrect permissions for requested channels')
147 log.error('Incorrect permissions for requested channels')
147 raise HTTPForbidden()
148 raise HTTPForbidden()
148 payload = {'conn_id': json_body.get('conn_id', ''),
149 payload = {'conn_id': json_body.get('conn_id', ''),
149 'channels': channels,
150 'channels': channels,
150 'channel_configs': {},
151 'channel_configs': {},
151 'info': {
152 'info': {
152 'exclude_channels': ['broadcast']}
153 'exclude_channels': ['broadcast']}
153 }
154 }
154 filtered_channels = [chan for chan in channels if chan != 'broadcast']
155 filtered_channels = [chan for chan in channels if chan != 'broadcast']
155 for channel in filtered_channels:
156 for channel in filtered_channels:
156 payload['channel_configs'][channel] = {
157 payload['channel_configs'][channel] = {
157 'notify_presence': True,
158 'notify_presence': True,
158 'history_size': 100,
159 'history_size': 100,
159 'store_history': True,
160 'store_history': True,
160 'broadcast_presence_with_user_lists': True
161 'broadcast_presence_with_user_lists': True
161 }
162 }
162 try:
163 try:
163 connect_result = channelstream_request(
164 connect_result = channelstream_request(
164 self.channelstream_config, payload, '/subscribe')
165 self.channelstream_config, payload, '/subscribe')
165 except ChannelstreamConnectionException:
166 except ChannelstreamConnectionException:
166 log.exception('Channelstream service is down')
167 log.exception('Channelstream service is down')
167 return HTTPBadGateway()
168 return HTTPBadGateway()
168 # include_channel_info will limit history only to new channel
169 # include_channel_info will limit history only to new channel
169 # to not overwrite histories on other channels in client
170 # to not overwrite histories on other channels in client
170 connect_result['channels_info'] = parse_channels_info(
171 connect_result['channels_info'] = parse_channels_info(
171 connect_result['channels_info'],
172 connect_result['channels_info'],
172 include_channel_info=filtered_channels)
173 include_channel_info=filtered_channels)
173 update_history_from_logs(self.channelstream_config,
174 update_history_from_logs(self.channelstream_config,
174 filtered_channels, connect_result)
175 filtered_channels, connect_result)
175 return connect_result
176 return connect_result
@@ -1,2174 +1,2192 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 authentication and permission libraries
22 authentication and permission libraries
23 """
23 """
24
24
25 import os
25 import os
26 import inspect
26 import inspect
27 import collections
27 import collections
28 import fnmatch
28 import fnmatch
29 import hashlib
29 import hashlib
30 import itertools
30 import itertools
31 import logging
31 import logging
32 import random
32 import random
33 import traceback
33 import traceback
34 from functools import wraps
34 from functools import wraps
35
35
36 import ipaddress
36 import ipaddress
37 from beaker.cache import cache_region
37 from beaker.cache import cache_region
38 from pyramid.httpexceptions import HTTPForbidden, HTTPFound, HTTPNotFound
38 from pyramid.httpexceptions import HTTPForbidden, HTTPFound, HTTPNotFound
39 from pylons.i18n.translation import _
39 from pylons.i18n.translation import _
40 # NOTE(marcink): this has to be removed only after pyramid migration,
40 # NOTE(marcink): this has to be removed only after pyramid migration,
41 # replace with _ = request.translate
41 # replace with _ = request.translate
42 from sqlalchemy.orm.exc import ObjectDeletedError
42 from sqlalchemy.orm.exc import ObjectDeletedError
43 from sqlalchemy.orm import joinedload
43 from sqlalchemy.orm import joinedload
44 from zope.cachedescriptors.property import Lazy as LazyProperty
44 from zope.cachedescriptors.property import Lazy as LazyProperty
45
45
46 import rhodecode
46 import rhodecode
47 from rhodecode.model import meta
47 from rhodecode.model import meta
48 from rhodecode.model.meta import Session
48 from rhodecode.model.meta import Session
49 from rhodecode.model.user import UserModel
49 from rhodecode.model.user import UserModel
50 from rhodecode.model.db import (
50 from rhodecode.model.db import (
51 User, Repository, Permission, UserToPerm, UserGroupToPerm, UserGroupMember,
51 User, Repository, Permission, UserToPerm, UserGroupToPerm, UserGroupMember,
52 UserIpMap, UserApiKeys, RepoGroup, UserGroup)
52 UserIpMap, UserApiKeys, RepoGroup, UserGroup)
53 from rhodecode.lib import caches
53 from rhodecode.lib import caches
54 from rhodecode.lib.utils2 import safe_unicode, aslist, safe_str, md5
54 from rhodecode.lib.utils2 import safe_unicode, aslist, safe_str, md5
55 from rhodecode.lib.utils import (
55 from rhodecode.lib.utils import (
56 get_repo_slug, get_repo_group_slug, get_user_group_slug)
56 get_repo_slug, get_repo_group_slug, get_user_group_slug)
57 from rhodecode.lib.caching_query import FromCache
57 from rhodecode.lib.caching_query import FromCache
58
58
59
59
60 if rhodecode.is_unix:
60 if rhodecode.is_unix:
61 import bcrypt
61 import bcrypt
62
62
63 log = logging.getLogger(__name__)
63 log = logging.getLogger(__name__)
64
64
65 csrf_token_key = "csrf_token"
65 csrf_token_key = "csrf_token"
66
66
67
67
68 class PasswordGenerator(object):
68 class PasswordGenerator(object):
69 """
69 """
70 This is a simple class for generating password from different sets of
70 This is a simple class for generating password from different sets of
71 characters
71 characters
72 usage::
72 usage::
73
73
74 passwd_gen = PasswordGenerator()
74 passwd_gen = PasswordGenerator()
75 #print 8-letter password containing only big and small letters
75 #print 8-letter password containing only big and small letters
76 of alphabet
76 of alphabet
77 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
77 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
78 """
78 """
79 ALPHABETS_NUM = r'''1234567890'''
79 ALPHABETS_NUM = r'''1234567890'''
80 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
80 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
81 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
81 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
82 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
82 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
83 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
83 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
84 + ALPHABETS_NUM + ALPHABETS_SPECIAL
84 + ALPHABETS_NUM + ALPHABETS_SPECIAL
85 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
85 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
86 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
86 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
87 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
87 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
88 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
88 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
89
89
90 def __init__(self, passwd=''):
90 def __init__(self, passwd=''):
91 self.passwd = passwd
91 self.passwd = passwd
92
92
93 def gen_password(self, length, type_=None):
93 def gen_password(self, length, type_=None):
94 if type_ is None:
94 if type_ is None:
95 type_ = self.ALPHABETS_FULL
95 type_ = self.ALPHABETS_FULL
96 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
96 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
97 return self.passwd
97 return self.passwd
98
98
99
99
100 class _RhodeCodeCryptoBase(object):
100 class _RhodeCodeCryptoBase(object):
101 ENC_PREF = None
101 ENC_PREF = None
102
102
103 def hash_create(self, str_):
103 def hash_create(self, str_):
104 """
104 """
105 hash the string using
105 hash the string using
106
106
107 :param str_: password to hash
107 :param str_: password to hash
108 """
108 """
109 raise NotImplementedError
109 raise NotImplementedError
110
110
111 def hash_check_with_upgrade(self, password, hashed):
111 def hash_check_with_upgrade(self, password, hashed):
112 """
112 """
113 Returns tuple in which first element is boolean that states that
113 Returns tuple in which first element is boolean that states that
114 given password matches it's hashed version, and the second is new hash
114 given password matches it's hashed version, and the second is new hash
115 of the password, in case this password should be migrated to new
115 of the password, in case this password should be migrated to new
116 cipher.
116 cipher.
117 """
117 """
118 checked_hash = self.hash_check(password, hashed)
118 checked_hash = self.hash_check(password, hashed)
119 return checked_hash, None
119 return checked_hash, None
120
120
121 def hash_check(self, password, hashed):
121 def hash_check(self, password, hashed):
122 """
122 """
123 Checks matching password with it's hashed value.
123 Checks matching password with it's hashed value.
124
124
125 :param password: password
125 :param password: password
126 :param hashed: password in hashed form
126 :param hashed: password in hashed form
127 """
127 """
128 raise NotImplementedError
128 raise NotImplementedError
129
129
130 def _assert_bytes(self, value):
130 def _assert_bytes(self, value):
131 """
131 """
132 Passing in an `unicode` object can lead to hard to detect issues
132 Passing in an `unicode` object can lead to hard to detect issues
133 if passwords contain non-ascii characters. Doing a type check
133 if passwords contain non-ascii characters. Doing a type check
134 during runtime, so that such mistakes are detected early on.
134 during runtime, so that such mistakes are detected early on.
135 """
135 """
136 if not isinstance(value, str):
136 if not isinstance(value, str):
137 raise TypeError(
137 raise TypeError(
138 "Bytestring required as input, got %r." % (value, ))
138 "Bytestring required as input, got %r." % (value, ))
139
139
140
140
141 class _RhodeCodeCryptoBCrypt(_RhodeCodeCryptoBase):
141 class _RhodeCodeCryptoBCrypt(_RhodeCodeCryptoBase):
142 ENC_PREF = ('$2a$10', '$2b$10')
142 ENC_PREF = ('$2a$10', '$2b$10')
143
143
144 def hash_create(self, str_):
144 def hash_create(self, str_):
145 self._assert_bytes(str_)
145 self._assert_bytes(str_)
146 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
146 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
147
147
148 def hash_check_with_upgrade(self, password, hashed):
148 def hash_check_with_upgrade(self, password, hashed):
149 """
149 """
150 Returns tuple in which first element is boolean that states that
150 Returns tuple in which first element is boolean that states that
151 given password matches it's hashed version, and the second is new hash
151 given password matches it's hashed version, and the second is new hash
152 of the password, in case this password should be migrated to new
152 of the password, in case this password should be migrated to new
153 cipher.
153 cipher.
154
154
155 This implements special upgrade logic which works like that:
155 This implements special upgrade logic which works like that:
156 - check if the given password == bcrypted hash, if yes then we
156 - check if the given password == bcrypted hash, if yes then we
157 properly used password and it was already in bcrypt. Proceed
157 properly used password and it was already in bcrypt. Proceed
158 without any changes
158 without any changes
159 - if bcrypt hash check is not working try with sha256. If hash compare
159 - if bcrypt hash check is not working try with sha256. If hash compare
160 is ok, it means we using correct but old hashed password. indicate
160 is ok, it means we using correct but old hashed password. indicate
161 hash change and proceed
161 hash change and proceed
162 """
162 """
163
163
164 new_hash = None
164 new_hash = None
165
165
166 # regular pw check
166 # regular pw check
167 password_match_bcrypt = self.hash_check(password, hashed)
167 password_match_bcrypt = self.hash_check(password, hashed)
168
168
169 # now we want to know if the password was maybe from sha256
169 # now we want to know if the password was maybe from sha256
170 # basically calling _RhodeCodeCryptoSha256().hash_check()
170 # basically calling _RhodeCodeCryptoSha256().hash_check()
171 if not password_match_bcrypt:
171 if not password_match_bcrypt:
172 if _RhodeCodeCryptoSha256().hash_check(password, hashed):
172 if _RhodeCodeCryptoSha256().hash_check(password, hashed):
173 new_hash = self.hash_create(password) # make new bcrypt hash
173 new_hash = self.hash_create(password) # make new bcrypt hash
174 password_match_bcrypt = True
174 password_match_bcrypt = True
175
175
176 return password_match_bcrypt, new_hash
176 return password_match_bcrypt, new_hash
177
177
178 def hash_check(self, password, hashed):
178 def hash_check(self, password, hashed):
179 """
179 """
180 Checks matching password with it's hashed value.
180 Checks matching password with it's hashed value.
181
181
182 :param password: password
182 :param password: password
183 :param hashed: password in hashed form
183 :param hashed: password in hashed form
184 """
184 """
185 self._assert_bytes(password)
185 self._assert_bytes(password)
186 try:
186 try:
187 return bcrypt.hashpw(password, hashed) == hashed
187 return bcrypt.hashpw(password, hashed) == hashed
188 except ValueError as e:
188 except ValueError as e:
189 # we're having a invalid salt here probably, we should not crash
189 # we're having a invalid salt here probably, we should not crash
190 # just return with False as it would be a wrong password.
190 # just return with False as it would be a wrong password.
191 log.debug('Failed to check password hash using bcrypt %s',
191 log.debug('Failed to check password hash using bcrypt %s',
192 safe_str(e))
192 safe_str(e))
193
193
194 return False
194 return False
195
195
196
196
197 class _RhodeCodeCryptoSha256(_RhodeCodeCryptoBase):
197 class _RhodeCodeCryptoSha256(_RhodeCodeCryptoBase):
198 ENC_PREF = '_'
198 ENC_PREF = '_'
199
199
200 def hash_create(self, str_):
200 def hash_create(self, str_):
201 self._assert_bytes(str_)
201 self._assert_bytes(str_)
202 return hashlib.sha256(str_).hexdigest()
202 return hashlib.sha256(str_).hexdigest()
203
203
204 def hash_check(self, password, hashed):
204 def hash_check(self, password, hashed):
205 """
205 """
206 Checks matching password with it's hashed value.
206 Checks matching password with it's hashed value.
207
207
208 :param password: password
208 :param password: password
209 :param hashed: password in hashed form
209 :param hashed: password in hashed form
210 """
210 """
211 self._assert_bytes(password)
211 self._assert_bytes(password)
212 return hashlib.sha256(password).hexdigest() == hashed
212 return hashlib.sha256(password).hexdigest() == hashed
213
213
214
214
215 class _RhodeCodeCryptoMd5(_RhodeCodeCryptoBase):
215 class _RhodeCodeCryptoMd5(_RhodeCodeCryptoBase):
216 ENC_PREF = '_'
216 ENC_PREF = '_'
217
217
218 def hash_create(self, str_):
218 def hash_create(self, str_):
219 self._assert_bytes(str_)
219 self._assert_bytes(str_)
220 return hashlib.md5(str_).hexdigest()
220 return hashlib.md5(str_).hexdigest()
221
221
222 def hash_check(self, password, hashed):
222 def hash_check(self, password, hashed):
223 """
223 """
224 Checks matching password with it's hashed value.
224 Checks matching password with it's hashed value.
225
225
226 :param password: password
226 :param password: password
227 :param hashed: password in hashed form
227 :param hashed: password in hashed form
228 """
228 """
229 self._assert_bytes(password)
229 self._assert_bytes(password)
230 return hashlib.md5(password).hexdigest() == hashed
230 return hashlib.md5(password).hexdigest() == hashed
231
231
232
232
233 def crypto_backend():
233 def crypto_backend():
234 """
234 """
235 Return the matching crypto backend.
235 Return the matching crypto backend.
236
236
237 Selection is based on if we run tests or not, we pick md5 backend to run
237 Selection is based on if we run tests or not, we pick md5 backend to run
238 tests faster since BCRYPT is expensive to calculate
238 tests faster since BCRYPT is expensive to calculate
239 """
239 """
240 if rhodecode.is_test:
240 if rhodecode.is_test:
241 RhodeCodeCrypto = _RhodeCodeCryptoMd5()
241 RhodeCodeCrypto = _RhodeCodeCryptoMd5()
242 else:
242 else:
243 RhodeCodeCrypto = _RhodeCodeCryptoBCrypt()
243 RhodeCodeCrypto = _RhodeCodeCryptoBCrypt()
244
244
245 return RhodeCodeCrypto
245 return RhodeCodeCrypto
246
246
247
247
248 def get_crypt_password(password):
248 def get_crypt_password(password):
249 """
249 """
250 Create the hash of `password` with the active crypto backend.
250 Create the hash of `password` with the active crypto backend.
251
251
252 :param password: The cleartext password.
252 :param password: The cleartext password.
253 :type password: unicode
253 :type password: unicode
254 """
254 """
255 password = safe_str(password)
255 password = safe_str(password)
256 return crypto_backend().hash_create(password)
256 return crypto_backend().hash_create(password)
257
257
258
258
259 def check_password(password, hashed):
259 def check_password(password, hashed):
260 """
260 """
261 Check if the value in `password` matches the hash in `hashed`.
261 Check if the value in `password` matches the hash in `hashed`.
262
262
263 :param password: The cleartext password.
263 :param password: The cleartext password.
264 :type password: unicode
264 :type password: unicode
265
265
266 :param hashed: The expected hashed version of the password.
266 :param hashed: The expected hashed version of the password.
267 :type hashed: The hash has to be passed in in text representation.
267 :type hashed: The hash has to be passed in in text representation.
268 """
268 """
269 password = safe_str(password)
269 password = safe_str(password)
270 return crypto_backend().hash_check(password, hashed)
270 return crypto_backend().hash_check(password, hashed)
271
271
272
272
273 def generate_auth_token(data, salt=None):
273 def generate_auth_token(data, salt=None):
274 """
274 """
275 Generates API KEY from given string
275 Generates API KEY from given string
276 """
276 """
277
277
278 if salt is None:
278 if salt is None:
279 salt = os.urandom(16)
279 salt = os.urandom(16)
280 return hashlib.sha1(safe_str(data) + salt).hexdigest()
280 return hashlib.sha1(safe_str(data) + salt).hexdigest()
281
281
282
282
283 def get_came_from(request):
283 def get_came_from(request):
284 """
284 """
285 get query_string+path from request sanitized after removing auth_token
285 get query_string+path from request sanitized after removing auth_token
286 """
286 """
287 _req = request
287 _req = request
288
288
289 path = _req.path
289 path = _req.path
290 if 'auth_token' in _req.GET:
290 if 'auth_token' in _req.GET:
291 # sanitize the request and remove auth_token for redirection
291 # sanitize the request and remove auth_token for redirection
292 _req.GET.pop('auth_token')
292 _req.GET.pop('auth_token')
293 qs = _req.query_string
293 qs = _req.query_string
294 if qs:
294 if qs:
295 path += '?' + qs
295 path += '?' + qs
296
296
297 return path
297 return path
298
298
299
299
300 class CookieStoreWrapper(object):
300 class CookieStoreWrapper(object):
301
301
302 def __init__(self, cookie_store):
302 def __init__(self, cookie_store):
303 self.cookie_store = cookie_store
303 self.cookie_store = cookie_store
304
304
305 def __repr__(self):
305 def __repr__(self):
306 return 'CookieStore<%s>' % (self.cookie_store)
306 return 'CookieStore<%s>' % (self.cookie_store)
307
307
308 def get(self, key, other=None):
308 def get(self, key, other=None):
309 if isinstance(self.cookie_store, dict):
309 if isinstance(self.cookie_store, dict):
310 return self.cookie_store.get(key, other)
310 return self.cookie_store.get(key, other)
311 elif isinstance(self.cookie_store, AuthUser):
311 elif isinstance(self.cookie_store, AuthUser):
312 return self.cookie_store.__dict__.get(key, other)
312 return self.cookie_store.__dict__.get(key, other)
313
313
314
314
315 def _cached_perms_data(user_id, scope, user_is_admin,
315 def _cached_perms_data(user_id, scope, user_is_admin,
316 user_inherit_default_permissions, explicit, algo,
316 user_inherit_default_permissions, explicit, algo,
317 calculate_super_admin):
317 calculate_super_admin):
318
318
319 permissions = PermissionCalculator(
319 permissions = PermissionCalculator(
320 user_id, scope, user_is_admin, user_inherit_default_permissions,
320 user_id, scope, user_is_admin, user_inherit_default_permissions,
321 explicit, algo, calculate_super_admin)
321 explicit, algo, calculate_super_admin)
322 return permissions.calculate()
322 return permissions.calculate()
323
323
324
324
325 class PermOrigin(object):
325 class PermOrigin(object):
326 SUPER_ADMIN = 'superadmin'
326 SUPER_ADMIN = 'superadmin'
327
327
328 REPO_USER = 'user:%s'
328 REPO_USER = 'user:%s'
329 REPO_USERGROUP = 'usergroup:%s'
329 REPO_USERGROUP = 'usergroup:%s'
330 REPO_OWNER = 'repo.owner'
330 REPO_OWNER = 'repo.owner'
331 REPO_DEFAULT = 'repo.default'
331 REPO_DEFAULT = 'repo.default'
332 REPO_DEFAULT_NO_INHERIT = 'repo.default.no.inherit'
332 REPO_DEFAULT_NO_INHERIT = 'repo.default.no.inherit'
333 REPO_PRIVATE = 'repo.private'
333 REPO_PRIVATE = 'repo.private'
334
334
335 REPOGROUP_USER = 'user:%s'
335 REPOGROUP_USER = 'user:%s'
336 REPOGROUP_USERGROUP = 'usergroup:%s'
336 REPOGROUP_USERGROUP = 'usergroup:%s'
337 REPOGROUP_OWNER = 'group.owner'
337 REPOGROUP_OWNER = 'group.owner'
338 REPOGROUP_DEFAULT = 'group.default'
338 REPOGROUP_DEFAULT = 'group.default'
339 REPOGROUP_DEFAULT_NO_INHERIT = 'group.default.no.inherit'
339 REPOGROUP_DEFAULT_NO_INHERIT = 'group.default.no.inherit'
340
340
341 USERGROUP_USER = 'user:%s'
341 USERGROUP_USER = 'user:%s'
342 USERGROUP_USERGROUP = 'usergroup:%s'
342 USERGROUP_USERGROUP = 'usergroup:%s'
343 USERGROUP_OWNER = 'usergroup.owner'
343 USERGROUP_OWNER = 'usergroup.owner'
344 USERGROUP_DEFAULT = 'usergroup.default'
344 USERGROUP_DEFAULT = 'usergroup.default'
345 USERGROUP_DEFAULT_NO_INHERIT = 'usergroup.default.no.inherit'
345 USERGROUP_DEFAULT_NO_INHERIT = 'usergroup.default.no.inherit'
346
346
347
347
348 class PermOriginDict(dict):
348 class PermOriginDict(dict):
349 """
349 """
350 A special dict used for tracking permissions along with their origins.
350 A special dict used for tracking permissions along with their origins.
351
351
352 `__setitem__` has been overridden to expect a tuple(perm, origin)
352 `__setitem__` has been overridden to expect a tuple(perm, origin)
353 `__getitem__` will return only the perm
353 `__getitem__` will return only the perm
354 `.perm_origin_stack` will return the stack of (perm, origin) set per key
354 `.perm_origin_stack` will return the stack of (perm, origin) set per key
355
355
356 >>> perms = PermOriginDict()
356 >>> perms = PermOriginDict()
357 >>> perms['resource'] = 'read', 'default'
357 >>> perms['resource'] = 'read', 'default'
358 >>> perms['resource']
358 >>> perms['resource']
359 'read'
359 'read'
360 >>> perms['resource'] = 'write', 'admin'
360 >>> perms['resource'] = 'write', 'admin'
361 >>> perms['resource']
361 >>> perms['resource']
362 'write'
362 'write'
363 >>> perms.perm_origin_stack
363 >>> perms.perm_origin_stack
364 {'resource': [('read', 'default'), ('write', 'admin')]}
364 {'resource': [('read', 'default'), ('write', 'admin')]}
365 """
365 """
366
366
367 def __init__(self, *args, **kw):
367 def __init__(self, *args, **kw):
368 dict.__init__(self, *args, **kw)
368 dict.__init__(self, *args, **kw)
369 self.perm_origin_stack = collections.OrderedDict()
369 self.perm_origin_stack = collections.OrderedDict()
370
370
371 def __setitem__(self, key, (perm, origin)):
371 def __setitem__(self, key, (perm, origin)):
372 self.perm_origin_stack.setdefault(key, []).append((perm, origin))
372 self.perm_origin_stack.setdefault(key, []).append((perm, origin))
373 dict.__setitem__(self, key, perm)
373 dict.__setitem__(self, key, perm)
374
374
375
375
376 class PermissionCalculator(object):
376 class PermissionCalculator(object):
377
377
378 def __init__(
378 def __init__(
379 self, user_id, scope, user_is_admin,
379 self, user_id, scope, user_is_admin,
380 user_inherit_default_permissions, explicit, algo,
380 user_inherit_default_permissions, explicit, algo,
381 calculate_super_admin=False):
381 calculate_super_admin=False):
382
382
383 self.user_id = user_id
383 self.user_id = user_id
384 self.user_is_admin = user_is_admin
384 self.user_is_admin = user_is_admin
385 self.inherit_default_permissions = user_inherit_default_permissions
385 self.inherit_default_permissions = user_inherit_default_permissions
386 self.explicit = explicit
386 self.explicit = explicit
387 self.algo = algo
387 self.algo = algo
388 self.calculate_super_admin = calculate_super_admin
388 self.calculate_super_admin = calculate_super_admin
389
389
390 scope = scope or {}
390 scope = scope or {}
391 self.scope_repo_id = scope.get('repo_id')
391 self.scope_repo_id = scope.get('repo_id')
392 self.scope_repo_group_id = scope.get('repo_group_id')
392 self.scope_repo_group_id = scope.get('repo_group_id')
393 self.scope_user_group_id = scope.get('user_group_id')
393 self.scope_user_group_id = scope.get('user_group_id')
394
394
395 self.default_user_id = User.get_default_user(cache=True).user_id
395 self.default_user_id = User.get_default_user(cache=True).user_id
396
396
397 self.permissions_repositories = PermOriginDict()
397 self.permissions_repositories = PermOriginDict()
398 self.permissions_repository_groups = PermOriginDict()
398 self.permissions_repository_groups = PermOriginDict()
399 self.permissions_user_groups = PermOriginDict()
399 self.permissions_user_groups = PermOriginDict()
400 self.permissions_global = set()
400 self.permissions_global = set()
401
401
402 self.default_repo_perms = Permission.get_default_repo_perms(
402 self.default_repo_perms = Permission.get_default_repo_perms(
403 self.default_user_id, self.scope_repo_id)
403 self.default_user_id, self.scope_repo_id)
404 self.default_repo_groups_perms = Permission.get_default_group_perms(
404 self.default_repo_groups_perms = Permission.get_default_group_perms(
405 self.default_user_id, self.scope_repo_group_id)
405 self.default_user_id, self.scope_repo_group_id)
406 self.default_user_group_perms = \
406 self.default_user_group_perms = \
407 Permission.get_default_user_group_perms(
407 Permission.get_default_user_group_perms(
408 self.default_user_id, self.scope_user_group_id)
408 self.default_user_id, self.scope_user_group_id)
409
409
410 def calculate(self):
410 def calculate(self):
411 if self.user_is_admin and not self.calculate_super_admin:
411 if self.user_is_admin and not self.calculate_super_admin:
412 return self._admin_permissions()
412 return self._admin_permissions()
413
413
414 self._calculate_global_default_permissions()
414 self._calculate_global_default_permissions()
415 self._calculate_global_permissions()
415 self._calculate_global_permissions()
416 self._calculate_default_permissions()
416 self._calculate_default_permissions()
417 self._calculate_repository_permissions()
417 self._calculate_repository_permissions()
418 self._calculate_repository_group_permissions()
418 self._calculate_repository_group_permissions()
419 self._calculate_user_group_permissions()
419 self._calculate_user_group_permissions()
420 return self._permission_structure()
420 return self._permission_structure()
421
421
422 def _admin_permissions(self):
422 def _admin_permissions(self):
423 """
423 """
424 admin user have all default rights for repositories
424 admin user have all default rights for repositories
425 and groups set to admin
425 and groups set to admin
426 """
426 """
427 self.permissions_global.add('hg.admin')
427 self.permissions_global.add('hg.admin')
428 self.permissions_global.add('hg.create.write_on_repogroup.true')
428 self.permissions_global.add('hg.create.write_on_repogroup.true')
429
429
430 # repositories
430 # repositories
431 for perm in self.default_repo_perms:
431 for perm in self.default_repo_perms:
432 r_k = perm.UserRepoToPerm.repository.repo_name
432 r_k = perm.UserRepoToPerm.repository.repo_name
433 p = 'repository.admin'
433 p = 'repository.admin'
434 self.permissions_repositories[r_k] = p, PermOrigin.SUPER_ADMIN
434 self.permissions_repositories[r_k] = p, PermOrigin.SUPER_ADMIN
435
435
436 # repository groups
436 # repository groups
437 for perm in self.default_repo_groups_perms:
437 for perm in self.default_repo_groups_perms:
438 rg_k = perm.UserRepoGroupToPerm.group.group_name
438 rg_k = perm.UserRepoGroupToPerm.group.group_name
439 p = 'group.admin'
439 p = 'group.admin'
440 self.permissions_repository_groups[rg_k] = p, PermOrigin.SUPER_ADMIN
440 self.permissions_repository_groups[rg_k] = p, PermOrigin.SUPER_ADMIN
441
441
442 # user groups
442 # user groups
443 for perm in self.default_user_group_perms:
443 for perm in self.default_user_group_perms:
444 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
444 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
445 p = 'usergroup.admin'
445 p = 'usergroup.admin'
446 self.permissions_user_groups[u_k] = p, PermOrigin.SUPER_ADMIN
446 self.permissions_user_groups[u_k] = p, PermOrigin.SUPER_ADMIN
447
447
448 return self._permission_structure()
448 return self._permission_structure()
449
449
450 def _calculate_global_default_permissions(self):
450 def _calculate_global_default_permissions(self):
451 """
451 """
452 global permissions taken from the default user
452 global permissions taken from the default user
453 """
453 """
454 default_global_perms = UserToPerm.query()\
454 default_global_perms = UserToPerm.query()\
455 .filter(UserToPerm.user_id == self.default_user_id)\
455 .filter(UserToPerm.user_id == self.default_user_id)\
456 .options(joinedload(UserToPerm.permission))
456 .options(joinedload(UserToPerm.permission))
457
457
458 for perm in default_global_perms:
458 for perm in default_global_perms:
459 self.permissions_global.add(perm.permission.permission_name)
459 self.permissions_global.add(perm.permission.permission_name)
460
460
461 if self.user_is_admin:
461 if self.user_is_admin:
462 self.permissions_global.add('hg.admin')
462 self.permissions_global.add('hg.admin')
463 self.permissions_global.add('hg.create.write_on_repogroup.true')
463 self.permissions_global.add('hg.create.write_on_repogroup.true')
464
464
465 def _calculate_global_permissions(self):
465 def _calculate_global_permissions(self):
466 """
466 """
467 Set global system permissions with user permissions or permissions
467 Set global system permissions with user permissions or permissions
468 taken from the user groups of the current user.
468 taken from the user groups of the current user.
469
469
470 The permissions include repo creating, repo group creating, forking
470 The permissions include repo creating, repo group creating, forking
471 etc.
471 etc.
472 """
472 """
473
473
474 # now we read the defined permissions and overwrite what we have set
474 # now we read the defined permissions and overwrite what we have set
475 # before those can be configured from groups or users explicitly.
475 # before those can be configured from groups or users explicitly.
476
476
477 # TODO: johbo: This seems to be out of sync, find out the reason
477 # TODO: johbo: This seems to be out of sync, find out the reason
478 # for the comment below and update it.
478 # for the comment below and update it.
479
479
480 # In case we want to extend this list we should be always in sync with
480 # In case we want to extend this list we should be always in sync with
481 # User.DEFAULT_USER_PERMISSIONS definitions
481 # User.DEFAULT_USER_PERMISSIONS definitions
482 _configurable = frozenset([
482 _configurable = frozenset([
483 'hg.fork.none', 'hg.fork.repository',
483 'hg.fork.none', 'hg.fork.repository',
484 'hg.create.none', 'hg.create.repository',
484 'hg.create.none', 'hg.create.repository',
485 'hg.usergroup.create.false', 'hg.usergroup.create.true',
485 'hg.usergroup.create.false', 'hg.usergroup.create.true',
486 'hg.repogroup.create.false', 'hg.repogroup.create.true',
486 'hg.repogroup.create.false', 'hg.repogroup.create.true',
487 'hg.create.write_on_repogroup.false',
487 'hg.create.write_on_repogroup.false',
488 'hg.create.write_on_repogroup.true',
488 'hg.create.write_on_repogroup.true',
489 'hg.inherit_default_perms.false', 'hg.inherit_default_perms.true'
489 'hg.inherit_default_perms.false', 'hg.inherit_default_perms.true'
490 ])
490 ])
491
491
492 # USER GROUPS comes first user group global permissions
492 # USER GROUPS comes first user group global permissions
493 user_perms_from_users_groups = Session().query(UserGroupToPerm)\
493 user_perms_from_users_groups = Session().query(UserGroupToPerm)\
494 .options(joinedload(UserGroupToPerm.permission))\
494 .options(joinedload(UserGroupToPerm.permission))\
495 .join((UserGroupMember, UserGroupToPerm.users_group_id ==
495 .join((UserGroupMember, UserGroupToPerm.users_group_id ==
496 UserGroupMember.users_group_id))\
496 UserGroupMember.users_group_id))\
497 .filter(UserGroupMember.user_id == self.user_id)\
497 .filter(UserGroupMember.user_id == self.user_id)\
498 .order_by(UserGroupToPerm.users_group_id)\
498 .order_by(UserGroupToPerm.users_group_id)\
499 .all()
499 .all()
500
500
501 # need to group here by groups since user can be in more than
501 # need to group here by groups since user can be in more than
502 # one group, so we get all groups
502 # one group, so we get all groups
503 _explicit_grouped_perms = [
503 _explicit_grouped_perms = [
504 [x, list(y)] for x, y in
504 [x, list(y)] for x, y in
505 itertools.groupby(user_perms_from_users_groups,
505 itertools.groupby(user_perms_from_users_groups,
506 lambda _x: _x.users_group)]
506 lambda _x: _x.users_group)]
507
507
508 for gr, perms in _explicit_grouped_perms:
508 for gr, perms in _explicit_grouped_perms:
509 # since user can be in multiple groups iterate over them and
509 # since user can be in multiple groups iterate over them and
510 # select the lowest permissions first (more explicit)
510 # select the lowest permissions first (more explicit)
511 # TODO: marcink: do this^^
511 # TODO: marcink: do this^^
512
512
513 # group doesn't inherit default permissions so we actually set them
513 # group doesn't inherit default permissions so we actually set them
514 if not gr.inherit_default_permissions:
514 if not gr.inherit_default_permissions:
515 # NEED TO IGNORE all previously set configurable permissions
515 # NEED TO IGNORE all previously set configurable permissions
516 # and replace them with explicitly set from this user
516 # and replace them with explicitly set from this user
517 # group permissions
517 # group permissions
518 self.permissions_global = self.permissions_global.difference(
518 self.permissions_global = self.permissions_global.difference(
519 _configurable)
519 _configurable)
520 for perm in perms:
520 for perm in perms:
521 self.permissions_global.add(perm.permission.permission_name)
521 self.permissions_global.add(perm.permission.permission_name)
522
522
523 # user explicit global permissions
523 # user explicit global permissions
524 user_perms = Session().query(UserToPerm)\
524 user_perms = Session().query(UserToPerm)\
525 .options(joinedload(UserToPerm.permission))\
525 .options(joinedload(UserToPerm.permission))\
526 .filter(UserToPerm.user_id == self.user_id).all()
526 .filter(UserToPerm.user_id == self.user_id).all()
527
527
528 if not self.inherit_default_permissions:
528 if not self.inherit_default_permissions:
529 # NEED TO IGNORE all configurable permissions and
529 # NEED TO IGNORE all configurable permissions and
530 # replace them with explicitly set from this user permissions
530 # replace them with explicitly set from this user permissions
531 self.permissions_global = self.permissions_global.difference(
531 self.permissions_global = self.permissions_global.difference(
532 _configurable)
532 _configurable)
533 for perm in user_perms:
533 for perm in user_perms:
534 self.permissions_global.add(perm.permission.permission_name)
534 self.permissions_global.add(perm.permission.permission_name)
535
535
536 def _calculate_default_permissions(self):
536 def _calculate_default_permissions(self):
537 """
537 """
538 Set default user permissions for repositories, repository groups
538 Set default user permissions for repositories, repository groups
539 taken from the default user.
539 taken from the default user.
540
540
541 Calculate inheritance of object permissions based on what we have now
541 Calculate inheritance of object permissions based on what we have now
542 in GLOBAL permissions. We check if .false is in GLOBAL since this is
542 in GLOBAL permissions. We check if .false is in GLOBAL since this is
543 explicitly set. Inherit is the opposite of .false being there.
543 explicitly set. Inherit is the opposite of .false being there.
544
544
545 .. note::
545 .. note::
546
546
547 the syntax is little bit odd but what we need to check here is
547 the syntax is little bit odd but what we need to check here is
548 the opposite of .false permission being in the list so even for
548 the opposite of .false permission being in the list so even for
549 inconsistent state when both .true/.false is there
549 inconsistent state when both .true/.false is there
550 .false is more important
550 .false is more important
551
551
552 """
552 """
553 user_inherit_object_permissions = not ('hg.inherit_default_perms.false'
553 user_inherit_object_permissions = not ('hg.inherit_default_perms.false'
554 in self.permissions_global)
554 in self.permissions_global)
555
555
556 # defaults for repositories, taken from `default` user permissions
556 # defaults for repositories, taken from `default` user permissions
557 # on given repo
557 # on given repo
558 for perm in self.default_repo_perms:
558 for perm in self.default_repo_perms:
559 r_k = perm.UserRepoToPerm.repository.repo_name
559 r_k = perm.UserRepoToPerm.repository.repo_name
560 p = perm.Permission.permission_name
560 p = perm.Permission.permission_name
561 o = PermOrigin.REPO_DEFAULT
561 o = PermOrigin.REPO_DEFAULT
562 self.permissions_repositories[r_k] = p, o
562 self.permissions_repositories[r_k] = p, o
563
563
564 # if we decide this user isn't inheriting permissions from
564 # if we decide this user isn't inheriting permissions from
565 # default user we set him to .none so only explicit
565 # default user we set him to .none so only explicit
566 # permissions work
566 # permissions work
567 if not user_inherit_object_permissions:
567 if not user_inherit_object_permissions:
568 p = 'repository.none'
568 p = 'repository.none'
569 o = PermOrigin.REPO_DEFAULT_NO_INHERIT
569 o = PermOrigin.REPO_DEFAULT_NO_INHERIT
570 self.permissions_repositories[r_k] = p, o
570 self.permissions_repositories[r_k] = p, o
571
571
572 if perm.Repository.private and not (
572 if perm.Repository.private and not (
573 perm.Repository.user_id == self.user_id):
573 perm.Repository.user_id == self.user_id):
574 # disable defaults for private repos,
574 # disable defaults for private repos,
575 p = 'repository.none'
575 p = 'repository.none'
576 o = PermOrigin.REPO_PRIVATE
576 o = PermOrigin.REPO_PRIVATE
577 self.permissions_repositories[r_k] = p, o
577 self.permissions_repositories[r_k] = p, o
578
578
579 elif perm.Repository.user_id == self.user_id:
579 elif perm.Repository.user_id == self.user_id:
580 # set admin if owner
580 # set admin if owner
581 p = 'repository.admin'
581 p = 'repository.admin'
582 o = PermOrigin.REPO_OWNER
582 o = PermOrigin.REPO_OWNER
583 self.permissions_repositories[r_k] = p, o
583 self.permissions_repositories[r_k] = p, o
584
584
585 if self.user_is_admin:
585 if self.user_is_admin:
586 p = 'repository.admin'
586 p = 'repository.admin'
587 o = PermOrigin.SUPER_ADMIN
587 o = PermOrigin.SUPER_ADMIN
588 self.permissions_repositories[r_k] = p, o
588 self.permissions_repositories[r_k] = p, o
589
589
590 # defaults for repository groups taken from `default` user permission
590 # defaults for repository groups taken from `default` user permission
591 # on given group
591 # on given group
592 for perm in self.default_repo_groups_perms:
592 for perm in self.default_repo_groups_perms:
593 rg_k = perm.UserRepoGroupToPerm.group.group_name
593 rg_k = perm.UserRepoGroupToPerm.group.group_name
594 p = perm.Permission.permission_name
594 p = perm.Permission.permission_name
595 o = PermOrigin.REPOGROUP_DEFAULT
595 o = PermOrigin.REPOGROUP_DEFAULT
596 self.permissions_repository_groups[rg_k] = p, o
596 self.permissions_repository_groups[rg_k] = p, o
597
597
598 # if we decide this user isn't inheriting permissions from default
598 # if we decide this user isn't inheriting permissions from default
599 # user we set him to .none so only explicit permissions work
599 # user we set him to .none so only explicit permissions work
600 if not user_inherit_object_permissions:
600 if not user_inherit_object_permissions:
601 p = 'group.none'
601 p = 'group.none'
602 o = PermOrigin.REPOGROUP_DEFAULT_NO_INHERIT
602 o = PermOrigin.REPOGROUP_DEFAULT_NO_INHERIT
603 self.permissions_repository_groups[rg_k] = p, o
603 self.permissions_repository_groups[rg_k] = p, o
604
604
605 if perm.RepoGroup.user_id == self.user_id:
605 if perm.RepoGroup.user_id == self.user_id:
606 # set admin if owner
606 # set admin if owner
607 p = 'group.admin'
607 p = 'group.admin'
608 o = PermOrigin.REPOGROUP_OWNER
608 o = PermOrigin.REPOGROUP_OWNER
609 self.permissions_repository_groups[rg_k] = p, o
609 self.permissions_repository_groups[rg_k] = p, o
610
610
611 if self.user_is_admin:
611 if self.user_is_admin:
612 p = 'group.admin'
612 p = 'group.admin'
613 o = PermOrigin.SUPER_ADMIN
613 o = PermOrigin.SUPER_ADMIN
614 self.permissions_repository_groups[rg_k] = p, o
614 self.permissions_repository_groups[rg_k] = p, o
615
615
616 # defaults for user groups taken from `default` user permission
616 # defaults for user groups taken from `default` user permission
617 # on given user group
617 # on given user group
618 for perm in self.default_user_group_perms:
618 for perm in self.default_user_group_perms:
619 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
619 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
620 p = perm.Permission.permission_name
620 p = perm.Permission.permission_name
621 o = PermOrigin.USERGROUP_DEFAULT
621 o = PermOrigin.USERGROUP_DEFAULT
622 self.permissions_user_groups[u_k] = p, o
622 self.permissions_user_groups[u_k] = p, o
623
623
624 # if we decide this user isn't inheriting permissions from default
624 # if we decide this user isn't inheriting permissions from default
625 # user we set him to .none so only explicit permissions work
625 # user we set him to .none so only explicit permissions work
626 if not user_inherit_object_permissions:
626 if not user_inherit_object_permissions:
627 p = 'usergroup.none'
627 p = 'usergroup.none'
628 o = PermOrigin.USERGROUP_DEFAULT_NO_INHERIT
628 o = PermOrigin.USERGROUP_DEFAULT_NO_INHERIT
629 self.permissions_user_groups[u_k] = p, o
629 self.permissions_user_groups[u_k] = p, o
630
630
631 if perm.UserGroup.user_id == self.user_id:
631 if perm.UserGroup.user_id == self.user_id:
632 # set admin if owner
632 # set admin if owner
633 p = 'usergroup.admin'
633 p = 'usergroup.admin'
634 o = PermOrigin.USERGROUP_OWNER
634 o = PermOrigin.USERGROUP_OWNER
635 self.permissions_user_groups[u_k] = p, o
635 self.permissions_user_groups[u_k] = p, o
636
636
637 if self.user_is_admin:
637 if self.user_is_admin:
638 p = 'usergroup.admin'
638 p = 'usergroup.admin'
639 o = PermOrigin.SUPER_ADMIN
639 o = PermOrigin.SUPER_ADMIN
640 self.permissions_user_groups[u_k] = p, o
640 self.permissions_user_groups[u_k] = p, o
641
641
642 def _calculate_repository_permissions(self):
642 def _calculate_repository_permissions(self):
643 """
643 """
644 Repository permissions for the current user.
644 Repository permissions for the current user.
645
645
646 Check if the user is part of user groups for this repository and
646 Check if the user is part of user groups for this repository and
647 fill in the permission from it. `_choose_permission` decides of which
647 fill in the permission from it. `_choose_permission` decides of which
648 permission should be selected based on selected method.
648 permission should be selected based on selected method.
649 """
649 """
650
650
651 # user group for repositories permissions
651 # user group for repositories permissions
652 user_repo_perms_from_user_group = Permission\
652 user_repo_perms_from_user_group = Permission\
653 .get_default_repo_perms_from_user_group(
653 .get_default_repo_perms_from_user_group(
654 self.user_id, self.scope_repo_id)
654 self.user_id, self.scope_repo_id)
655
655
656 multiple_counter = collections.defaultdict(int)
656 multiple_counter = collections.defaultdict(int)
657 for perm in user_repo_perms_from_user_group:
657 for perm in user_repo_perms_from_user_group:
658 r_k = perm.UserGroupRepoToPerm.repository.repo_name
658 r_k = perm.UserGroupRepoToPerm.repository.repo_name
659 multiple_counter[r_k] += 1
659 multiple_counter[r_k] += 1
660 p = perm.Permission.permission_name
660 p = perm.Permission.permission_name
661 o = PermOrigin.REPO_USERGROUP % perm.UserGroupRepoToPerm\
661 o = PermOrigin.REPO_USERGROUP % perm.UserGroupRepoToPerm\
662 .users_group.users_group_name
662 .users_group.users_group_name
663
663
664 if multiple_counter[r_k] > 1:
664 if multiple_counter[r_k] > 1:
665 cur_perm = self.permissions_repositories[r_k]
665 cur_perm = self.permissions_repositories[r_k]
666 p = self._choose_permission(p, cur_perm)
666 p = self._choose_permission(p, cur_perm)
667
667
668 self.permissions_repositories[r_k] = p, o
668 self.permissions_repositories[r_k] = p, o
669
669
670 if perm.Repository.user_id == self.user_id:
670 if perm.Repository.user_id == self.user_id:
671 # set admin if owner
671 # set admin if owner
672 p = 'repository.admin'
672 p = 'repository.admin'
673 o = PermOrigin.REPO_OWNER
673 o = PermOrigin.REPO_OWNER
674 self.permissions_repositories[r_k] = p, o
674 self.permissions_repositories[r_k] = p, o
675
675
676 if self.user_is_admin:
676 if self.user_is_admin:
677 p = 'repository.admin'
677 p = 'repository.admin'
678 o = PermOrigin.SUPER_ADMIN
678 o = PermOrigin.SUPER_ADMIN
679 self.permissions_repositories[r_k] = p, o
679 self.permissions_repositories[r_k] = p, o
680
680
681 # user explicit permissions for repositories, overrides any specified
681 # user explicit permissions for repositories, overrides any specified
682 # by the group permission
682 # by the group permission
683 user_repo_perms = Permission.get_default_repo_perms(
683 user_repo_perms = Permission.get_default_repo_perms(
684 self.user_id, self.scope_repo_id)
684 self.user_id, self.scope_repo_id)
685 for perm in user_repo_perms:
685 for perm in user_repo_perms:
686 r_k = perm.UserRepoToPerm.repository.repo_name
686 r_k = perm.UserRepoToPerm.repository.repo_name
687 p = perm.Permission.permission_name
687 p = perm.Permission.permission_name
688 o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
688 o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
689
689
690 if not self.explicit:
690 if not self.explicit:
691 cur_perm = self.permissions_repositories.get(
691 cur_perm = self.permissions_repositories.get(
692 r_k, 'repository.none')
692 r_k, 'repository.none')
693 p = self._choose_permission(p, cur_perm)
693 p = self._choose_permission(p, cur_perm)
694
694
695 self.permissions_repositories[r_k] = p, o
695 self.permissions_repositories[r_k] = p, o
696
696
697 if perm.Repository.user_id == self.user_id:
697 if perm.Repository.user_id == self.user_id:
698 # set admin if owner
698 # set admin if owner
699 p = 'repository.admin'
699 p = 'repository.admin'
700 o = PermOrigin.REPO_OWNER
700 o = PermOrigin.REPO_OWNER
701 self.permissions_repositories[r_k] = p, o
701 self.permissions_repositories[r_k] = p, o
702
702
703 if self.user_is_admin:
703 if self.user_is_admin:
704 p = 'repository.admin'
704 p = 'repository.admin'
705 o = PermOrigin.SUPER_ADMIN
705 o = PermOrigin.SUPER_ADMIN
706 self.permissions_repositories[r_k] = p, o
706 self.permissions_repositories[r_k] = p, o
707
707
708 def _calculate_repository_group_permissions(self):
708 def _calculate_repository_group_permissions(self):
709 """
709 """
710 Repository group permissions for the current user.
710 Repository group permissions for the current user.
711
711
712 Check if the user is part of user groups for repository groups and
712 Check if the user is part of user groups for repository groups and
713 fill in the permissions from it. `_choose_permission` decides of which
713 fill in the permissions from it. `_choose_permission` decides of which
714 permission should be selected based on selected method.
714 permission should be selected based on selected method.
715 """
715 """
716 # user group for repo groups permissions
716 # user group for repo groups permissions
717 user_repo_group_perms_from_user_group = Permission\
717 user_repo_group_perms_from_user_group = Permission\
718 .get_default_group_perms_from_user_group(
718 .get_default_group_perms_from_user_group(
719 self.user_id, self.scope_repo_group_id)
719 self.user_id, self.scope_repo_group_id)
720
720
721 multiple_counter = collections.defaultdict(int)
721 multiple_counter = collections.defaultdict(int)
722 for perm in user_repo_group_perms_from_user_group:
722 for perm in user_repo_group_perms_from_user_group:
723 rg_k = perm.UserGroupRepoGroupToPerm.group.group_name
723 rg_k = perm.UserGroupRepoGroupToPerm.group.group_name
724 multiple_counter[rg_k] += 1
724 multiple_counter[rg_k] += 1
725 o = PermOrigin.REPOGROUP_USERGROUP % perm.UserGroupRepoGroupToPerm\
725 o = PermOrigin.REPOGROUP_USERGROUP % perm.UserGroupRepoGroupToPerm\
726 .users_group.users_group_name
726 .users_group.users_group_name
727 p = perm.Permission.permission_name
727 p = perm.Permission.permission_name
728
728
729 if multiple_counter[rg_k] > 1:
729 if multiple_counter[rg_k] > 1:
730 cur_perm = self.permissions_repository_groups[rg_k]
730 cur_perm = self.permissions_repository_groups[rg_k]
731 p = self._choose_permission(p, cur_perm)
731 p = self._choose_permission(p, cur_perm)
732 self.permissions_repository_groups[rg_k] = p, o
732 self.permissions_repository_groups[rg_k] = p, o
733
733
734 if perm.RepoGroup.user_id == self.user_id:
734 if perm.RepoGroup.user_id == self.user_id:
735 # set admin if owner, even for member of other user group
735 # set admin if owner, even for member of other user group
736 p = 'group.admin'
736 p = 'group.admin'
737 o = PermOrigin.REPOGROUP_OWNER
737 o = PermOrigin.REPOGROUP_OWNER
738 self.permissions_repository_groups[rg_k] = p, o
738 self.permissions_repository_groups[rg_k] = p, o
739
739
740 if self.user_is_admin:
740 if self.user_is_admin:
741 p = 'group.admin'
741 p = 'group.admin'
742 o = PermOrigin.SUPER_ADMIN
742 o = PermOrigin.SUPER_ADMIN
743 self.permissions_repository_groups[rg_k] = p, o
743 self.permissions_repository_groups[rg_k] = p, o
744
744
745 # user explicit permissions for repository groups
745 # user explicit permissions for repository groups
746 user_repo_groups_perms = Permission.get_default_group_perms(
746 user_repo_groups_perms = Permission.get_default_group_perms(
747 self.user_id, self.scope_repo_group_id)
747 self.user_id, self.scope_repo_group_id)
748 for perm in user_repo_groups_perms:
748 for perm in user_repo_groups_perms:
749 rg_k = perm.UserRepoGroupToPerm.group.group_name
749 rg_k = perm.UserRepoGroupToPerm.group.group_name
750 o = PermOrigin.REPOGROUP_USER % perm.UserRepoGroupToPerm\
750 o = PermOrigin.REPOGROUP_USER % perm.UserRepoGroupToPerm\
751 .user.username
751 .user.username
752 p = perm.Permission.permission_name
752 p = perm.Permission.permission_name
753
753
754 if not self.explicit:
754 if not self.explicit:
755 cur_perm = self.permissions_repository_groups.get(
755 cur_perm = self.permissions_repository_groups.get(
756 rg_k, 'group.none')
756 rg_k, 'group.none')
757 p = self._choose_permission(p, cur_perm)
757 p = self._choose_permission(p, cur_perm)
758
758
759 self.permissions_repository_groups[rg_k] = p, o
759 self.permissions_repository_groups[rg_k] = p, o
760
760
761 if perm.RepoGroup.user_id == self.user_id:
761 if perm.RepoGroup.user_id == self.user_id:
762 # set admin if owner
762 # set admin if owner
763 p = 'group.admin'
763 p = 'group.admin'
764 o = PermOrigin.REPOGROUP_OWNER
764 o = PermOrigin.REPOGROUP_OWNER
765 self.permissions_repository_groups[rg_k] = p, o
765 self.permissions_repository_groups[rg_k] = p, o
766
766
767 if self.user_is_admin:
767 if self.user_is_admin:
768 p = 'group.admin'
768 p = 'group.admin'
769 o = PermOrigin.SUPER_ADMIN
769 o = PermOrigin.SUPER_ADMIN
770 self.permissions_repository_groups[rg_k] = p, o
770 self.permissions_repository_groups[rg_k] = p, o
771
771
772 def _calculate_user_group_permissions(self):
772 def _calculate_user_group_permissions(self):
773 """
773 """
774 User group permissions for the current user.
774 User group permissions for the current user.
775 """
775 """
776 # user group for user group permissions
776 # user group for user group permissions
777 user_group_from_user_group = Permission\
777 user_group_from_user_group = Permission\
778 .get_default_user_group_perms_from_user_group(
778 .get_default_user_group_perms_from_user_group(
779 self.user_id, self.scope_user_group_id)
779 self.user_id, self.scope_user_group_id)
780
780
781 multiple_counter = collections.defaultdict(int)
781 multiple_counter = collections.defaultdict(int)
782 for perm in user_group_from_user_group:
782 for perm in user_group_from_user_group:
783 ug_k = perm.UserGroupUserGroupToPerm\
783 ug_k = perm.UserGroupUserGroupToPerm\
784 .target_user_group.users_group_name
784 .target_user_group.users_group_name
785 multiple_counter[ug_k] += 1
785 multiple_counter[ug_k] += 1
786 o = PermOrigin.USERGROUP_USERGROUP % perm.UserGroupUserGroupToPerm\
786 o = PermOrigin.USERGROUP_USERGROUP % perm.UserGroupUserGroupToPerm\
787 .user_group.users_group_name
787 .user_group.users_group_name
788 p = perm.Permission.permission_name
788 p = perm.Permission.permission_name
789
789
790 if multiple_counter[ug_k] > 1:
790 if multiple_counter[ug_k] > 1:
791 cur_perm = self.permissions_user_groups[ug_k]
791 cur_perm = self.permissions_user_groups[ug_k]
792 p = self._choose_permission(p, cur_perm)
792 p = self._choose_permission(p, cur_perm)
793
793
794 self.permissions_user_groups[ug_k] = p, o
794 self.permissions_user_groups[ug_k] = p, o
795
795
796 if perm.UserGroup.user_id == self.user_id:
796 if perm.UserGroup.user_id == self.user_id:
797 # set admin if owner, even for member of other user group
797 # set admin if owner, even for member of other user group
798 p = 'usergroup.admin'
798 p = 'usergroup.admin'
799 o = PermOrigin.USERGROUP_OWNER
799 o = PermOrigin.USERGROUP_OWNER
800 self.permissions_user_groups[ug_k] = p, o
800 self.permissions_user_groups[ug_k] = p, o
801
801
802 if self.user_is_admin:
802 if self.user_is_admin:
803 p = 'usergroup.admin'
803 p = 'usergroup.admin'
804 o = PermOrigin.SUPER_ADMIN
804 o = PermOrigin.SUPER_ADMIN
805 self.permissions_user_groups[ug_k] = p, o
805 self.permissions_user_groups[ug_k] = p, o
806
806
807 # user explicit permission for user groups
807 # user explicit permission for user groups
808 user_user_groups_perms = Permission.get_default_user_group_perms(
808 user_user_groups_perms = Permission.get_default_user_group_perms(
809 self.user_id, self.scope_user_group_id)
809 self.user_id, self.scope_user_group_id)
810 for perm in user_user_groups_perms:
810 for perm in user_user_groups_perms:
811 ug_k = perm.UserUserGroupToPerm.user_group.users_group_name
811 ug_k = perm.UserUserGroupToPerm.user_group.users_group_name
812 o = PermOrigin.USERGROUP_USER % perm.UserUserGroupToPerm\
812 o = PermOrigin.USERGROUP_USER % perm.UserUserGroupToPerm\
813 .user.username
813 .user.username
814 p = perm.Permission.permission_name
814 p = perm.Permission.permission_name
815
815
816 if not self.explicit:
816 if not self.explicit:
817 cur_perm = self.permissions_user_groups.get(
817 cur_perm = self.permissions_user_groups.get(
818 ug_k, 'usergroup.none')
818 ug_k, 'usergroup.none')
819 p = self._choose_permission(p, cur_perm)
819 p = self._choose_permission(p, cur_perm)
820
820
821 self.permissions_user_groups[ug_k] = p, o
821 self.permissions_user_groups[ug_k] = p, o
822
822
823 if perm.UserGroup.user_id == self.user_id:
823 if perm.UserGroup.user_id == self.user_id:
824 # set admin if owner
824 # set admin if owner
825 p = 'usergroup.admin'
825 p = 'usergroup.admin'
826 o = PermOrigin.USERGROUP_OWNER
826 o = PermOrigin.USERGROUP_OWNER
827 self.permissions_user_groups[ug_k] = p, o
827 self.permissions_user_groups[ug_k] = p, o
828
828
829 if self.user_is_admin:
829 if self.user_is_admin:
830 p = 'usergroup.admin'
830 p = 'usergroup.admin'
831 o = PermOrigin.SUPER_ADMIN
831 o = PermOrigin.SUPER_ADMIN
832 self.permissions_user_groups[ug_k] = p, o
832 self.permissions_user_groups[ug_k] = p, o
833
833
834 def _choose_permission(self, new_perm, cur_perm):
834 def _choose_permission(self, new_perm, cur_perm):
835 new_perm_val = Permission.PERM_WEIGHTS[new_perm]
835 new_perm_val = Permission.PERM_WEIGHTS[new_perm]
836 cur_perm_val = Permission.PERM_WEIGHTS[cur_perm]
836 cur_perm_val = Permission.PERM_WEIGHTS[cur_perm]
837 if self.algo == 'higherwin':
837 if self.algo == 'higherwin':
838 if new_perm_val > cur_perm_val:
838 if new_perm_val > cur_perm_val:
839 return new_perm
839 return new_perm
840 return cur_perm
840 return cur_perm
841 elif self.algo == 'lowerwin':
841 elif self.algo == 'lowerwin':
842 if new_perm_val < cur_perm_val:
842 if new_perm_val < cur_perm_val:
843 return new_perm
843 return new_perm
844 return cur_perm
844 return cur_perm
845
845
846 def _permission_structure(self):
846 def _permission_structure(self):
847 return {
847 return {
848 'global': self.permissions_global,
848 'global': self.permissions_global,
849 'repositories': self.permissions_repositories,
849 'repositories': self.permissions_repositories,
850 'repositories_groups': self.permissions_repository_groups,
850 'repositories_groups': self.permissions_repository_groups,
851 'user_groups': self.permissions_user_groups,
851 'user_groups': self.permissions_user_groups,
852 }
852 }
853
853
854
854
855 def allowed_auth_token_access(view_name, auth_token, whitelist=None):
855 def allowed_auth_token_access(view_name, auth_token, whitelist=None):
856 """
856 """
857 Check if given controller_name is in whitelist of auth token access
857 Check if given controller_name is in whitelist of auth token access
858 """
858 """
859 if not whitelist:
859 if not whitelist:
860 from rhodecode import CONFIG
860 from rhodecode import CONFIG
861 whitelist = aslist(
861 whitelist = aslist(
862 CONFIG.get('api_access_controllers_whitelist'), sep=',')
862 CONFIG.get('api_access_controllers_whitelist'), sep=',')
863 # backward compat translation
863 # backward compat translation
864 compat = {
864 compat = {
865 # old controller, new VIEW
865 # old controller, new VIEW
866 'ChangesetController:*': 'RepoCommitsView:*',
866 'ChangesetController:*': 'RepoCommitsView:*',
867 'ChangesetController:changeset_patch': 'RepoCommitsView:repo_commit_patch',
867 'ChangesetController:changeset_patch': 'RepoCommitsView:repo_commit_patch',
868 'ChangesetController:changeset_raw': 'RepoCommitsView:repo_commit_raw',
868 'ChangesetController:changeset_raw': 'RepoCommitsView:repo_commit_raw',
869 'FilesController:raw': 'RepoCommitsView:repo_commit_raw',
869 'FilesController:raw': 'RepoCommitsView:repo_commit_raw',
870 'FilesController:archivefile': 'RepoFilesView:repo_archivefile',
870 'FilesController:archivefile': 'RepoFilesView:repo_archivefile',
871 'GistsController:*': 'GistView:*',
871 'GistsController:*': 'GistView:*',
872 }
872 }
873
873
874 log.debug(
874 log.debug(
875 'Allowed views for AUTH TOKEN access: %s' % (whitelist,))
875 'Allowed views for AUTH TOKEN access: %s' % (whitelist,))
876 auth_token_access_valid = False
876 auth_token_access_valid = False
877
877
878 for entry in whitelist:
878 for entry in whitelist:
879 token_match = True
879 token_match = True
880 if entry in compat:
880 if entry in compat:
881 # translate from old Controllers to Pyramid Views
881 # translate from old Controllers to Pyramid Views
882 entry = compat[entry]
882 entry = compat[entry]
883
883
884 if '@' in entry:
884 if '@' in entry:
885 # specific AuthToken
885 # specific AuthToken
886 entry, allowed_token = entry.split('@', 1)
886 entry, allowed_token = entry.split('@', 1)
887 token_match = auth_token == allowed_token
887 token_match = auth_token == allowed_token
888
888
889 if fnmatch.fnmatch(view_name, entry) and token_match:
889 if fnmatch.fnmatch(view_name, entry) and token_match:
890 auth_token_access_valid = True
890 auth_token_access_valid = True
891 break
891 break
892
892
893 if auth_token_access_valid:
893 if auth_token_access_valid:
894 log.debug('view: `%s` matches entry in whitelist: %s'
894 log.debug('view: `%s` matches entry in whitelist: %s'
895 % (view_name, whitelist))
895 % (view_name, whitelist))
896 else:
896 else:
897 msg = ('view: `%s` does *NOT* match any entry in whitelist: %s'
897 msg = ('view: `%s` does *NOT* match any entry in whitelist: %s'
898 % (view_name, whitelist))
898 % (view_name, whitelist))
899 if auth_token:
899 if auth_token:
900 # if we use auth token key and don't have access it's a warning
900 # if we use auth token key and don't have access it's a warning
901 log.warning(msg)
901 log.warning(msg)
902 else:
902 else:
903 log.debug(msg)
903 log.debug(msg)
904
904
905 return auth_token_access_valid
905 return auth_token_access_valid
906
906
907
907
908 class AuthUser(object):
908 class AuthUser(object):
909 """
909 """
910 A simple object that handles all attributes of user in RhodeCode
910 A simple object that handles all attributes of user in RhodeCode
911
911
912 It does lookup based on API key,given user, or user present in session
912 It does lookup based on API key,given user, or user present in session
913 Then it fills all required information for such user. It also checks if
913 Then it fills all required information for such user. It also checks if
914 anonymous access is enabled and if so, it returns default user as logged in
914 anonymous access is enabled and if so, it returns default user as logged in
915 """
915 """
916 GLOBAL_PERMS = [x[0] for x in Permission.PERMS]
916 GLOBAL_PERMS = [x[0] for x in Permission.PERMS]
917
917
918 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
918 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
919
919
920 self.user_id = user_id
920 self.user_id = user_id
921 self._api_key = api_key
921 self._api_key = api_key
922
922
923 self.api_key = None
923 self.api_key = None
924 self.feed_token = ''
924 self.feed_token = ''
925 self.username = username
925 self.username = username
926 self.ip_addr = ip_addr
926 self.ip_addr = ip_addr
927 self.name = ''
927 self.name = ''
928 self.lastname = ''
928 self.lastname = ''
929 self.first_name = ''
929 self.first_name = ''
930 self.last_name = ''
930 self.last_name = ''
931 self.email = ''
931 self.email = ''
932 self.is_authenticated = False
932 self.is_authenticated = False
933 self.admin = False
933 self.admin = False
934 self.inherit_default_permissions = False
934 self.inherit_default_permissions = False
935 self.password = ''
935 self.password = ''
936
936
937 self.anonymous_user = None # propagated on propagate_data
937 self.anonymous_user = None # propagated on propagate_data
938 self.propagate_data()
938 self.propagate_data()
939 self._instance = None
939 self._instance = None
940 self._permissions_scoped_cache = {} # used to bind scoped calculation
940 self._permissions_scoped_cache = {} # used to bind scoped calculation
941
941
942 @LazyProperty
942 @LazyProperty
943 def permissions(self):
943 def permissions(self):
944 return self.get_perms(user=self, cache=False)
944 return self.get_perms(user=self, cache=False)
945
945
946 @LazyProperty
946 @LazyProperty
947 def permissions_safe(self):
948 """
949 Filtered permissions excluding not allowed repositories
950 """
951 perms = self.get_perms(user=self, cache=False)
952
953 perms['repositories'] = {
954 k: v for k, v in perms['repositories'].iteritems()
955 if v != 'repository.none'}
956 perms['repositories_groups'] = {
957 k: v for k, v in perms['repositories_groups'].iteritems()
958 if v != 'group.none'}
959 perms['user_groups'] = {
960 k: v for k, v in perms['user_groups'].iteritems()
961 if v != 'usergroup.none'}
962 return perms
963
964 @LazyProperty
947 def permissions_full_details(self):
965 def permissions_full_details(self):
948 return self.get_perms(
966 return self.get_perms(
949 user=self, cache=False, calculate_super_admin=True)
967 user=self, cache=False, calculate_super_admin=True)
950
968
951 def permissions_with_scope(self, scope):
969 def permissions_with_scope(self, scope):
952 """
970 """
953 Call the get_perms function with scoped data. The scope in that function
971 Call the get_perms function with scoped data. The scope in that function
954 narrows the SQL calls to the given ID of objects resulting in fetching
972 narrows the SQL calls to the given ID of objects resulting in fetching
955 Just particular permission we want to obtain. If scope is an empty dict
973 Just particular permission we want to obtain. If scope is an empty dict
956 then it basically narrows the scope to GLOBAL permissions only.
974 then it basically narrows the scope to GLOBAL permissions only.
957
975
958 :param scope: dict
976 :param scope: dict
959 """
977 """
960 if 'repo_name' in scope:
978 if 'repo_name' in scope:
961 obj = Repository.get_by_repo_name(scope['repo_name'])
979 obj = Repository.get_by_repo_name(scope['repo_name'])
962 if obj:
980 if obj:
963 scope['repo_id'] = obj.repo_id
981 scope['repo_id'] = obj.repo_id
964 _scope = {
982 _scope = {
965 'repo_id': -1,
983 'repo_id': -1,
966 'user_group_id': -1,
984 'user_group_id': -1,
967 'repo_group_id': -1,
985 'repo_group_id': -1,
968 }
986 }
969 _scope.update(scope)
987 _scope.update(scope)
970 cache_key = "_".join(map(safe_str, reduce(lambda a, b: a+b,
988 cache_key = "_".join(map(safe_str, reduce(lambda a, b: a+b,
971 _scope.items())))
989 _scope.items())))
972 if cache_key not in self._permissions_scoped_cache:
990 if cache_key not in self._permissions_scoped_cache:
973 # store in cache to mimic how the @LazyProperty works,
991 # store in cache to mimic how the @LazyProperty works,
974 # the difference here is that we use the unique key calculated
992 # the difference here is that we use the unique key calculated
975 # from params and values
993 # from params and values
976 res = self.get_perms(user=self, cache=False, scope=_scope)
994 res = self.get_perms(user=self, cache=False, scope=_scope)
977 self._permissions_scoped_cache[cache_key] = res
995 self._permissions_scoped_cache[cache_key] = res
978 return self._permissions_scoped_cache[cache_key]
996 return self._permissions_scoped_cache[cache_key]
979
997
980 def get_instance(self):
998 def get_instance(self):
981 return User.get(self.user_id)
999 return User.get(self.user_id)
982
1000
983 def update_lastactivity(self):
1001 def update_lastactivity(self):
984 if self.user_id:
1002 if self.user_id:
985 User.get(self.user_id).update_lastactivity()
1003 User.get(self.user_id).update_lastactivity()
986
1004
987 def propagate_data(self):
1005 def propagate_data(self):
988 """
1006 """
989 Fills in user data and propagates values to this instance. Maps fetched
1007 Fills in user data and propagates values to this instance. Maps fetched
990 user attributes to this class instance attributes
1008 user attributes to this class instance attributes
991 """
1009 """
992 log.debug('AuthUser: starting data propagation for new potential user')
1010 log.debug('AuthUser: starting data propagation for new potential user')
993 user_model = UserModel()
1011 user_model = UserModel()
994 anon_user = self.anonymous_user = User.get_default_user(cache=True)
1012 anon_user = self.anonymous_user = User.get_default_user(cache=True)
995 is_user_loaded = False
1013 is_user_loaded = False
996
1014
997 # lookup by userid
1015 # lookup by userid
998 if self.user_id is not None and self.user_id != anon_user.user_id:
1016 if self.user_id is not None and self.user_id != anon_user.user_id:
999 log.debug('Trying Auth User lookup by USER ID: `%s`', self.user_id)
1017 log.debug('Trying Auth User lookup by USER ID: `%s`', self.user_id)
1000 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
1018 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
1001
1019
1002 # try go get user by api key
1020 # try go get user by api key
1003 elif self._api_key and self._api_key != anon_user.api_key:
1021 elif self._api_key and self._api_key != anon_user.api_key:
1004 log.debug('Trying Auth User lookup by API KEY: `%s`', self._api_key)
1022 log.debug('Trying Auth User lookup by API KEY: `%s`', self._api_key)
1005 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
1023 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
1006
1024
1007 # lookup by username
1025 # lookup by username
1008 elif self.username:
1026 elif self.username:
1009 log.debug('Trying Auth User lookup by USER NAME: `%s`', self.username)
1027 log.debug('Trying Auth User lookup by USER NAME: `%s`', self.username)
1010 is_user_loaded = user_model.fill_data(self, username=self.username)
1028 is_user_loaded = user_model.fill_data(self, username=self.username)
1011 else:
1029 else:
1012 log.debug('No data in %s that could been used to log in', self)
1030 log.debug('No data in %s that could been used to log in', self)
1013
1031
1014 if not is_user_loaded:
1032 if not is_user_loaded:
1015 log.debug(
1033 log.debug(
1016 'Failed to load user. Fallback to default user %s', anon_user)
1034 'Failed to load user. Fallback to default user %s', anon_user)
1017 # if we cannot authenticate user try anonymous
1035 # if we cannot authenticate user try anonymous
1018 if anon_user.active:
1036 if anon_user.active:
1019 log.debug('default user is active, using it as a session user')
1037 log.debug('default user is active, using it as a session user')
1020 user_model.fill_data(self, user_id=anon_user.user_id)
1038 user_model.fill_data(self, user_id=anon_user.user_id)
1021 # then we set this user is logged in
1039 # then we set this user is logged in
1022 self.is_authenticated = True
1040 self.is_authenticated = True
1023 else:
1041 else:
1024 log.debug('default user is NOT active')
1042 log.debug('default user is NOT active')
1025 # in case of disabled anonymous user we reset some of the
1043 # in case of disabled anonymous user we reset some of the
1026 # parameters so such user is "corrupted", skipping the fill_data
1044 # parameters so such user is "corrupted", skipping the fill_data
1027 for attr in ['user_id', 'username', 'admin', 'active']:
1045 for attr in ['user_id', 'username', 'admin', 'active']:
1028 setattr(self, attr, None)
1046 setattr(self, attr, None)
1029 self.is_authenticated = False
1047 self.is_authenticated = False
1030
1048
1031 if not self.username:
1049 if not self.username:
1032 self.username = 'None'
1050 self.username = 'None'
1033
1051
1034 log.debug('AuthUser: propagated user is now %s', self)
1052 log.debug('AuthUser: propagated user is now %s', self)
1035
1053
1036 def get_perms(self, user, scope=None, explicit=True, algo='higherwin',
1054 def get_perms(self, user, scope=None, explicit=True, algo='higherwin',
1037 calculate_super_admin=False, cache=False):
1055 calculate_super_admin=False, cache=False):
1038 """
1056 """
1039 Fills user permission attribute with permissions taken from database
1057 Fills user permission attribute with permissions taken from database
1040 works for permissions given for repositories, and for permissions that
1058 works for permissions given for repositories, and for permissions that
1041 are granted to groups
1059 are granted to groups
1042
1060
1043 :param user: instance of User object from database
1061 :param user: instance of User object from database
1044 :param explicit: In case there are permissions both for user and a group
1062 :param explicit: In case there are permissions both for user and a group
1045 that user is part of, explicit flag will defiine if user will
1063 that user is part of, explicit flag will defiine if user will
1046 explicitly override permissions from group, if it's False it will
1064 explicitly override permissions from group, if it's False it will
1047 make decision based on the algo
1065 make decision based on the algo
1048 :param algo: algorithm to decide what permission should be choose if
1066 :param algo: algorithm to decide what permission should be choose if
1049 it's multiple defined, eg user in two different groups. It also
1067 it's multiple defined, eg user in two different groups. It also
1050 decides if explicit flag is turned off how to specify the permission
1068 decides if explicit flag is turned off how to specify the permission
1051 for case when user is in a group + have defined separate permission
1069 for case when user is in a group + have defined separate permission
1052 """
1070 """
1053 user_id = user.user_id
1071 user_id = user.user_id
1054 user_is_admin = user.is_admin
1072 user_is_admin = user.is_admin
1055
1073
1056 # inheritance of global permissions like create repo/fork repo etc
1074 # inheritance of global permissions like create repo/fork repo etc
1057 user_inherit_default_permissions = user.inherit_default_permissions
1075 user_inherit_default_permissions = user.inherit_default_permissions
1058
1076
1059 log.debug('Computing PERMISSION tree for scope %s' % (scope, ))
1077 log.debug('Computing PERMISSION tree for scope %s' % (scope, ))
1060 compute = caches.conditional_cache(
1078 compute = caches.conditional_cache(
1061 'short_term', 'cache_desc',
1079 'short_term', 'cache_desc',
1062 condition=cache, func=_cached_perms_data)
1080 condition=cache, func=_cached_perms_data)
1063 result = compute(user_id, scope, user_is_admin,
1081 result = compute(user_id, scope, user_is_admin,
1064 user_inherit_default_permissions, explicit, algo,
1082 user_inherit_default_permissions, explicit, algo,
1065 calculate_super_admin)
1083 calculate_super_admin)
1066
1084
1067 result_repr = []
1085 result_repr = []
1068 for k in result:
1086 for k in result:
1069 result_repr.append((k, len(result[k])))
1087 result_repr.append((k, len(result[k])))
1070
1088
1071 log.debug('PERMISSION tree computed %s' % (result_repr,))
1089 log.debug('PERMISSION tree computed %s' % (result_repr,))
1072 return result
1090 return result
1073
1091
1074 @property
1092 @property
1075 def is_default(self):
1093 def is_default(self):
1076 return self.username == User.DEFAULT_USER
1094 return self.username == User.DEFAULT_USER
1077
1095
1078 @property
1096 @property
1079 def is_admin(self):
1097 def is_admin(self):
1080 return self.admin
1098 return self.admin
1081
1099
1082 @property
1100 @property
1083 def is_user_object(self):
1101 def is_user_object(self):
1084 return self.user_id is not None
1102 return self.user_id is not None
1085
1103
1086 @property
1104 @property
1087 def repositories_admin(self):
1105 def repositories_admin(self):
1088 """
1106 """
1089 Returns list of repositories you're an admin of
1107 Returns list of repositories you're an admin of
1090 """
1108 """
1091 return [
1109 return [
1092 x[0] for x in self.permissions['repositories'].iteritems()
1110 x[0] for x in self.permissions['repositories'].iteritems()
1093 if x[1] == 'repository.admin']
1111 if x[1] == 'repository.admin']
1094
1112
1095 @property
1113 @property
1096 def repository_groups_admin(self):
1114 def repository_groups_admin(self):
1097 """
1115 """
1098 Returns list of repository groups you're an admin of
1116 Returns list of repository groups you're an admin of
1099 """
1117 """
1100 return [
1118 return [
1101 x[0] for x in self.permissions['repositories_groups'].iteritems()
1119 x[0] for x in self.permissions['repositories_groups'].iteritems()
1102 if x[1] == 'group.admin']
1120 if x[1] == 'group.admin']
1103
1121
1104 @property
1122 @property
1105 def user_groups_admin(self):
1123 def user_groups_admin(self):
1106 """
1124 """
1107 Returns list of user groups you're an admin of
1125 Returns list of user groups you're an admin of
1108 """
1126 """
1109 return [
1127 return [
1110 x[0] for x in self.permissions['user_groups'].iteritems()
1128 x[0] for x in self.permissions['user_groups'].iteritems()
1111 if x[1] == 'usergroup.admin']
1129 if x[1] == 'usergroup.admin']
1112
1130
1113 def repo_acl_ids(self, perms=None, name_filter=None, cache=False):
1131 def repo_acl_ids(self, perms=None, name_filter=None, cache=False):
1114 """
1132 """
1115 Returns list of repository ids that user have access to based on given
1133 Returns list of repository ids that user have access to based on given
1116 perms. The cache flag should be only used in cases that are used for
1134 perms. The cache flag should be only used in cases that are used for
1117 display purposes, NOT IN ANY CASE for permission checks.
1135 display purposes, NOT IN ANY CASE for permission checks.
1118 """
1136 """
1119 from rhodecode.model.scm import RepoList
1137 from rhodecode.model.scm import RepoList
1120 if not perms:
1138 if not perms:
1121 perms = [
1139 perms = [
1122 'repository.read', 'repository.write', 'repository.admin']
1140 'repository.read', 'repository.write', 'repository.admin']
1123
1141
1124 def _cached_repo_acl(user_id, perm_def, name_filter):
1142 def _cached_repo_acl(user_id, perm_def, name_filter):
1125 qry = Repository.query()
1143 qry = Repository.query()
1126 if name_filter:
1144 if name_filter:
1127 ilike_expression = u'%{}%'.format(safe_unicode(name_filter))
1145 ilike_expression = u'%{}%'.format(safe_unicode(name_filter))
1128 qry = qry.filter(
1146 qry = qry.filter(
1129 Repository.repo_name.ilike(ilike_expression))
1147 Repository.repo_name.ilike(ilike_expression))
1130
1148
1131 return [x.repo_id for x in
1149 return [x.repo_id for x in
1132 RepoList(qry, perm_set=perm_def)]
1150 RepoList(qry, perm_set=perm_def)]
1133
1151
1134 compute = caches.conditional_cache(
1152 compute = caches.conditional_cache(
1135 'long_term', 'repo_acl_ids',
1153 'long_term', 'repo_acl_ids',
1136 condition=cache, func=_cached_repo_acl)
1154 condition=cache, func=_cached_repo_acl)
1137 return compute(self.user_id, perms, name_filter)
1155 return compute(self.user_id, perms, name_filter)
1138
1156
1139 def repo_group_acl_ids(self, perms=None, name_filter=None, cache=False):
1157 def repo_group_acl_ids(self, perms=None, name_filter=None, cache=False):
1140 """
1158 """
1141 Returns list of repository group ids that user have access to based on given
1159 Returns list of repository group ids that user have access to based on given
1142 perms. The cache flag should be only used in cases that are used for
1160 perms. The cache flag should be only used in cases that are used for
1143 display purposes, NOT IN ANY CASE for permission checks.
1161 display purposes, NOT IN ANY CASE for permission checks.
1144 """
1162 """
1145 from rhodecode.model.scm import RepoGroupList
1163 from rhodecode.model.scm import RepoGroupList
1146 if not perms:
1164 if not perms:
1147 perms = [
1165 perms = [
1148 'group.read', 'group.write', 'group.admin']
1166 'group.read', 'group.write', 'group.admin']
1149
1167
1150 def _cached_repo_group_acl(user_id, perm_def, name_filter):
1168 def _cached_repo_group_acl(user_id, perm_def, name_filter):
1151 qry = RepoGroup.query()
1169 qry = RepoGroup.query()
1152 if name_filter:
1170 if name_filter:
1153 ilike_expression = u'%{}%'.format(safe_unicode(name_filter))
1171 ilike_expression = u'%{}%'.format(safe_unicode(name_filter))
1154 qry = qry.filter(
1172 qry = qry.filter(
1155 RepoGroup.group_name.ilike(ilike_expression))
1173 RepoGroup.group_name.ilike(ilike_expression))
1156
1174
1157 return [x.group_id for x in
1175 return [x.group_id for x in
1158 RepoGroupList(qry, perm_set=perm_def)]
1176 RepoGroupList(qry, perm_set=perm_def)]
1159
1177
1160 compute = caches.conditional_cache(
1178 compute = caches.conditional_cache(
1161 'long_term', 'repo_group_acl_ids',
1179 'long_term', 'repo_group_acl_ids',
1162 condition=cache, func=_cached_repo_group_acl)
1180 condition=cache, func=_cached_repo_group_acl)
1163 return compute(self.user_id, perms, name_filter)
1181 return compute(self.user_id, perms, name_filter)
1164
1182
1165 def user_group_acl_ids(self, perms=None, name_filter=None, cache=False):
1183 def user_group_acl_ids(self, perms=None, name_filter=None, cache=False):
1166 """
1184 """
1167 Returns list of user group ids that user have access to based on given
1185 Returns list of user group ids that user have access to based on given
1168 perms. The cache flag should be only used in cases that are used for
1186 perms. The cache flag should be only used in cases that are used for
1169 display purposes, NOT IN ANY CASE for permission checks.
1187 display purposes, NOT IN ANY CASE for permission checks.
1170 """
1188 """
1171 from rhodecode.model.scm import UserGroupList
1189 from rhodecode.model.scm import UserGroupList
1172 if not perms:
1190 if not perms:
1173 perms = [
1191 perms = [
1174 'usergroup.read', 'usergroup.write', 'usergroup.admin']
1192 'usergroup.read', 'usergroup.write', 'usergroup.admin']
1175
1193
1176 def _cached_user_group_acl(user_id, perm_def, name_filter):
1194 def _cached_user_group_acl(user_id, perm_def, name_filter):
1177 qry = UserGroup.query()
1195 qry = UserGroup.query()
1178 if name_filter:
1196 if name_filter:
1179 ilike_expression = u'%{}%'.format(safe_unicode(name_filter))
1197 ilike_expression = u'%{}%'.format(safe_unicode(name_filter))
1180 qry = qry.filter(
1198 qry = qry.filter(
1181 UserGroup.users_group_name.ilike(ilike_expression))
1199 UserGroup.users_group_name.ilike(ilike_expression))
1182
1200
1183 return [x.users_group_id for x in
1201 return [x.users_group_id for x in
1184 UserGroupList(qry, perm_set=perm_def)]
1202 UserGroupList(qry, perm_set=perm_def)]
1185
1203
1186 compute = caches.conditional_cache(
1204 compute = caches.conditional_cache(
1187 'long_term', 'user_group_acl_ids',
1205 'long_term', 'user_group_acl_ids',
1188 condition=cache, func=_cached_user_group_acl)
1206 condition=cache, func=_cached_user_group_acl)
1189 return compute(self.user_id, perms, name_filter)
1207 return compute(self.user_id, perms, name_filter)
1190
1208
1191 @property
1209 @property
1192 def ip_allowed(self):
1210 def ip_allowed(self):
1193 """
1211 """
1194 Checks if ip_addr used in constructor is allowed from defined list of
1212 Checks if ip_addr used in constructor is allowed from defined list of
1195 allowed ip_addresses for user
1213 allowed ip_addresses for user
1196
1214
1197 :returns: boolean, True if ip is in allowed ip range
1215 :returns: boolean, True if ip is in allowed ip range
1198 """
1216 """
1199 # check IP
1217 # check IP
1200 inherit = self.inherit_default_permissions
1218 inherit = self.inherit_default_permissions
1201 return AuthUser.check_ip_allowed(self.user_id, self.ip_addr,
1219 return AuthUser.check_ip_allowed(self.user_id, self.ip_addr,
1202 inherit_from_default=inherit)
1220 inherit_from_default=inherit)
1203 @property
1221 @property
1204 def personal_repo_group(self):
1222 def personal_repo_group(self):
1205 return RepoGroup.get_user_personal_repo_group(self.user_id)
1223 return RepoGroup.get_user_personal_repo_group(self.user_id)
1206
1224
1207 @classmethod
1225 @classmethod
1208 def check_ip_allowed(cls, user_id, ip_addr, inherit_from_default):
1226 def check_ip_allowed(cls, user_id, ip_addr, inherit_from_default):
1209 allowed_ips = AuthUser.get_allowed_ips(
1227 allowed_ips = AuthUser.get_allowed_ips(
1210 user_id, cache=True, inherit_from_default=inherit_from_default)
1228 user_id, cache=True, inherit_from_default=inherit_from_default)
1211 if check_ip_access(source_ip=ip_addr, allowed_ips=allowed_ips):
1229 if check_ip_access(source_ip=ip_addr, allowed_ips=allowed_ips):
1212 log.debug('IP:%s is in range of %s' % (ip_addr, allowed_ips))
1230 log.debug('IP:%s is in range of %s' % (ip_addr, allowed_ips))
1213 return True
1231 return True
1214 else:
1232 else:
1215 log.info('Access for IP:%s forbidden, '
1233 log.info('Access for IP:%s forbidden, '
1216 'not in %s' % (ip_addr, allowed_ips))
1234 'not in %s' % (ip_addr, allowed_ips))
1217 return False
1235 return False
1218
1236
1219 def __repr__(self):
1237 def __repr__(self):
1220 return "<AuthUser('id:%s[%s] ip:%s auth:%s')>"\
1238 return "<AuthUser('id:%s[%s] ip:%s auth:%s')>"\
1221 % (self.user_id, self.username, self.ip_addr, self.is_authenticated)
1239 % (self.user_id, self.username, self.ip_addr, self.is_authenticated)
1222
1240
1223 def set_authenticated(self, authenticated=True):
1241 def set_authenticated(self, authenticated=True):
1224 if self.user_id != self.anonymous_user.user_id:
1242 if self.user_id != self.anonymous_user.user_id:
1225 self.is_authenticated = authenticated
1243 self.is_authenticated = authenticated
1226
1244
1227 def get_cookie_store(self):
1245 def get_cookie_store(self):
1228 return {
1246 return {
1229 'username': self.username,
1247 'username': self.username,
1230 'password': md5(self.password),
1248 'password': md5(self.password),
1231 'user_id': self.user_id,
1249 'user_id': self.user_id,
1232 'is_authenticated': self.is_authenticated
1250 'is_authenticated': self.is_authenticated
1233 }
1251 }
1234
1252
1235 @classmethod
1253 @classmethod
1236 def from_cookie_store(cls, cookie_store):
1254 def from_cookie_store(cls, cookie_store):
1237 """
1255 """
1238 Creates AuthUser from a cookie store
1256 Creates AuthUser from a cookie store
1239
1257
1240 :param cls:
1258 :param cls:
1241 :param cookie_store:
1259 :param cookie_store:
1242 """
1260 """
1243 user_id = cookie_store.get('user_id')
1261 user_id = cookie_store.get('user_id')
1244 username = cookie_store.get('username')
1262 username = cookie_store.get('username')
1245 api_key = cookie_store.get('api_key')
1263 api_key = cookie_store.get('api_key')
1246 return AuthUser(user_id, api_key, username)
1264 return AuthUser(user_id, api_key, username)
1247
1265
1248 @classmethod
1266 @classmethod
1249 def get_allowed_ips(cls, user_id, cache=False, inherit_from_default=False):
1267 def get_allowed_ips(cls, user_id, cache=False, inherit_from_default=False):
1250 _set = set()
1268 _set = set()
1251
1269
1252 if inherit_from_default:
1270 if inherit_from_default:
1253 default_ips = UserIpMap.query().filter(
1271 default_ips = UserIpMap.query().filter(
1254 UserIpMap.user == User.get_default_user(cache=True))
1272 UserIpMap.user == User.get_default_user(cache=True))
1255 if cache:
1273 if cache:
1256 default_ips = default_ips.options(
1274 default_ips = default_ips.options(
1257 FromCache("sql_cache_short", "get_user_ips_default"))
1275 FromCache("sql_cache_short", "get_user_ips_default"))
1258
1276
1259 # populate from default user
1277 # populate from default user
1260 for ip in default_ips:
1278 for ip in default_ips:
1261 try:
1279 try:
1262 _set.add(ip.ip_addr)
1280 _set.add(ip.ip_addr)
1263 except ObjectDeletedError:
1281 except ObjectDeletedError:
1264 # since we use heavy caching sometimes it happens that
1282 # since we use heavy caching sometimes it happens that
1265 # we get deleted objects here, we just skip them
1283 # we get deleted objects here, we just skip them
1266 pass
1284 pass
1267
1285
1268 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
1286 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
1269 if cache:
1287 if cache:
1270 user_ips = user_ips.options(
1288 user_ips = user_ips.options(
1271 FromCache("sql_cache_short", "get_user_ips_%s" % user_id))
1289 FromCache("sql_cache_short", "get_user_ips_%s" % user_id))
1272
1290
1273 for ip in user_ips:
1291 for ip in user_ips:
1274 try:
1292 try:
1275 _set.add(ip.ip_addr)
1293 _set.add(ip.ip_addr)
1276 except ObjectDeletedError:
1294 except ObjectDeletedError:
1277 # since we use heavy caching sometimes it happens that we get
1295 # since we use heavy caching sometimes it happens that we get
1278 # deleted objects here, we just skip them
1296 # deleted objects here, we just skip them
1279 pass
1297 pass
1280 return _set or set(['0.0.0.0/0', '::/0'])
1298 return _set or set(['0.0.0.0/0', '::/0'])
1281
1299
1282
1300
1283 def set_available_permissions(settings):
1301 def set_available_permissions(settings):
1284 """
1302 """
1285 This function will propagate pyramid settings with all available defined
1303 This function will propagate pyramid settings with all available defined
1286 permission given in db. We don't want to check each time from db for new
1304 permission given in db. We don't want to check each time from db for new
1287 permissions since adding a new permission also requires application restart
1305 permissions since adding a new permission also requires application restart
1288 ie. to decorate new views with the newly created permission
1306 ie. to decorate new views with the newly created permission
1289
1307
1290 :param settings: current pyramid registry.settings
1308 :param settings: current pyramid registry.settings
1291
1309
1292 """
1310 """
1293 log.debug('auth: getting information about all available permissions')
1311 log.debug('auth: getting information about all available permissions')
1294 try:
1312 try:
1295 sa = meta.Session
1313 sa = meta.Session
1296 all_perms = sa.query(Permission).all()
1314 all_perms = sa.query(Permission).all()
1297 settings.setdefault('available_permissions',
1315 settings.setdefault('available_permissions',
1298 [x.permission_name for x in all_perms])
1316 [x.permission_name for x in all_perms])
1299 log.debug('auth: set available permissions')
1317 log.debug('auth: set available permissions')
1300 except Exception:
1318 except Exception:
1301 log.exception('Failed to fetch permissions from the database.')
1319 log.exception('Failed to fetch permissions from the database.')
1302 raise
1320 raise
1303
1321
1304
1322
1305 def get_csrf_token(session, force_new=False, save_if_missing=True):
1323 def get_csrf_token(session, force_new=False, save_if_missing=True):
1306 """
1324 """
1307 Return the current authentication token, creating one if one doesn't
1325 Return the current authentication token, creating one if one doesn't
1308 already exist and the save_if_missing flag is present.
1326 already exist and the save_if_missing flag is present.
1309
1327
1310 :param session: pass in the pylons session, else we use the global ones
1328 :param session: pass in the pylons session, else we use the global ones
1311 :param force_new: force to re-generate the token and store it in session
1329 :param force_new: force to re-generate the token and store it in session
1312 :param save_if_missing: save the newly generated token if it's missing in
1330 :param save_if_missing: save the newly generated token if it's missing in
1313 session
1331 session
1314 """
1332 """
1315 # NOTE(marcink): probably should be replaced with below one from pyramid 1.9
1333 # NOTE(marcink): probably should be replaced with below one from pyramid 1.9
1316 # from pyramid.csrf import get_csrf_token
1334 # from pyramid.csrf import get_csrf_token
1317
1335
1318 if (csrf_token_key not in session and save_if_missing) or force_new:
1336 if (csrf_token_key not in session and save_if_missing) or force_new:
1319 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
1337 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
1320 session[csrf_token_key] = token
1338 session[csrf_token_key] = token
1321 if hasattr(session, 'save'):
1339 if hasattr(session, 'save'):
1322 session.save()
1340 session.save()
1323 return session.get(csrf_token_key)
1341 return session.get(csrf_token_key)
1324
1342
1325
1343
1326 def get_request(perm_class_instance):
1344 def get_request(perm_class_instance):
1327 from pyramid.threadlocal import get_current_request
1345 from pyramid.threadlocal import get_current_request
1328 pyramid_request = get_current_request()
1346 pyramid_request = get_current_request()
1329 if not pyramid_request:
1347 if not pyramid_request:
1330 # return global request of pylons in case pyramid isn't available
1348 # return global request of pylons in case pyramid isn't available
1331 # NOTE(marcink): this should be removed after migration to pyramid
1349 # NOTE(marcink): this should be removed after migration to pyramid
1332 from pylons import request
1350 from pylons import request
1333 return request
1351 return request
1334 return pyramid_request
1352 return pyramid_request
1335
1353
1336
1354
1337 # CHECK DECORATORS
1355 # CHECK DECORATORS
1338 class CSRFRequired(object):
1356 class CSRFRequired(object):
1339 """
1357 """
1340 Decorator for authenticating a form
1358 Decorator for authenticating a form
1341
1359
1342 This decorator uses an authorization token stored in the client's
1360 This decorator uses an authorization token stored in the client's
1343 session for prevention of certain Cross-site request forgery (CSRF)
1361 session for prevention of certain Cross-site request forgery (CSRF)
1344 attacks (See
1362 attacks (See
1345 http://en.wikipedia.org/wiki/Cross-site_request_forgery for more
1363 http://en.wikipedia.org/wiki/Cross-site_request_forgery for more
1346 information).
1364 information).
1347
1365
1348 For use with the ``webhelpers.secure_form`` helper functions.
1366 For use with the ``webhelpers.secure_form`` helper functions.
1349
1367
1350 """
1368 """
1351 def __init__(self, token=csrf_token_key, header='X-CSRF-Token',
1369 def __init__(self, token=csrf_token_key, header='X-CSRF-Token',
1352 except_methods=None):
1370 except_methods=None):
1353 self.token = token
1371 self.token = token
1354 self.header = header
1372 self.header = header
1355 self.except_methods = except_methods or []
1373 self.except_methods = except_methods or []
1356
1374
1357 def __call__(self, func):
1375 def __call__(self, func):
1358 return get_cython_compat_decorator(self.__wrapper, func)
1376 return get_cython_compat_decorator(self.__wrapper, func)
1359
1377
1360 def _get_csrf(self, _request):
1378 def _get_csrf(self, _request):
1361 return _request.POST.get(self.token, _request.headers.get(self.header))
1379 return _request.POST.get(self.token, _request.headers.get(self.header))
1362
1380
1363 def check_csrf(self, _request, cur_token):
1381 def check_csrf(self, _request, cur_token):
1364 supplied_token = self._get_csrf(_request)
1382 supplied_token = self._get_csrf(_request)
1365 return supplied_token and supplied_token == cur_token
1383 return supplied_token and supplied_token == cur_token
1366
1384
1367 def _get_request(self):
1385 def _get_request(self):
1368 return get_request(self)
1386 return get_request(self)
1369
1387
1370 def __wrapper(self, func, *fargs, **fkwargs):
1388 def __wrapper(self, func, *fargs, **fkwargs):
1371 request = self._get_request()
1389 request = self._get_request()
1372
1390
1373 if request.method in self.except_methods:
1391 if request.method in self.except_methods:
1374 return func(*fargs, **fkwargs)
1392 return func(*fargs, **fkwargs)
1375
1393
1376 cur_token = get_csrf_token(request.session, save_if_missing=False)
1394 cur_token = get_csrf_token(request.session, save_if_missing=False)
1377 if self.check_csrf(request, cur_token):
1395 if self.check_csrf(request, cur_token):
1378 if request.POST.get(self.token):
1396 if request.POST.get(self.token):
1379 del request.POST[self.token]
1397 del request.POST[self.token]
1380 return func(*fargs, **fkwargs)
1398 return func(*fargs, **fkwargs)
1381 else:
1399 else:
1382 reason = 'token-missing'
1400 reason = 'token-missing'
1383 supplied_token = self._get_csrf(request)
1401 supplied_token = self._get_csrf(request)
1384 if supplied_token and cur_token != supplied_token:
1402 if supplied_token and cur_token != supplied_token:
1385 reason = 'token-mismatch [%s:%s]' % (
1403 reason = 'token-mismatch [%s:%s]' % (
1386 cur_token or ''[:6], supplied_token or ''[:6])
1404 cur_token or ''[:6], supplied_token or ''[:6])
1387
1405
1388 csrf_message = \
1406 csrf_message = \
1389 ("Cross-site request forgery detected, request denied. See "
1407 ("Cross-site request forgery detected, request denied. See "
1390 "http://en.wikipedia.org/wiki/Cross-site_request_forgery for "
1408 "http://en.wikipedia.org/wiki/Cross-site_request_forgery for "
1391 "more information.")
1409 "more information.")
1392 log.warn('Cross-site request forgery detected, request %r DENIED: %s '
1410 log.warn('Cross-site request forgery detected, request %r DENIED: %s '
1393 'REMOTE_ADDR:%s, HEADERS:%s' % (
1411 'REMOTE_ADDR:%s, HEADERS:%s' % (
1394 request, reason, request.remote_addr, request.headers))
1412 request, reason, request.remote_addr, request.headers))
1395
1413
1396 raise HTTPForbidden(explanation=csrf_message)
1414 raise HTTPForbidden(explanation=csrf_message)
1397
1415
1398
1416
1399 class LoginRequired(object):
1417 class LoginRequired(object):
1400 """
1418 """
1401 Must be logged in to execute this function else
1419 Must be logged in to execute this function else
1402 redirect to login page
1420 redirect to login page
1403
1421
1404 :param api_access: if enabled this checks only for valid auth token
1422 :param api_access: if enabled this checks only for valid auth token
1405 and grants access based on valid token
1423 and grants access based on valid token
1406 """
1424 """
1407 def __init__(self, auth_token_access=None):
1425 def __init__(self, auth_token_access=None):
1408 self.auth_token_access = auth_token_access
1426 self.auth_token_access = auth_token_access
1409
1427
1410 def __call__(self, func):
1428 def __call__(self, func):
1411 return get_cython_compat_decorator(self.__wrapper, func)
1429 return get_cython_compat_decorator(self.__wrapper, func)
1412
1430
1413 def _get_request(self):
1431 def _get_request(self):
1414 return get_request(self)
1432 return get_request(self)
1415
1433
1416 def __wrapper(self, func, *fargs, **fkwargs):
1434 def __wrapper(self, func, *fargs, **fkwargs):
1417 from rhodecode.lib import helpers as h
1435 from rhodecode.lib import helpers as h
1418 cls = fargs[0]
1436 cls = fargs[0]
1419 user = cls._rhodecode_user
1437 user = cls._rhodecode_user
1420 request = self._get_request()
1438 request = self._get_request()
1421
1439
1422 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
1440 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
1423 log.debug('Starting login restriction checks for user: %s' % (user,))
1441 log.debug('Starting login restriction checks for user: %s' % (user,))
1424 # check if our IP is allowed
1442 # check if our IP is allowed
1425 ip_access_valid = True
1443 ip_access_valid = True
1426 if not user.ip_allowed:
1444 if not user.ip_allowed:
1427 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr,))),
1445 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr,))),
1428 category='warning')
1446 category='warning')
1429 ip_access_valid = False
1447 ip_access_valid = False
1430
1448
1431 # check if we used an APIKEY and it's a valid one
1449 # check if we used an APIKEY and it's a valid one
1432 # defined white-list of controllers which API access will be enabled
1450 # defined white-list of controllers which API access will be enabled
1433 _auth_token = request.GET.get(
1451 _auth_token = request.GET.get(
1434 'auth_token', '') or request.GET.get('api_key', '')
1452 'auth_token', '') or request.GET.get('api_key', '')
1435 auth_token_access_valid = allowed_auth_token_access(
1453 auth_token_access_valid = allowed_auth_token_access(
1436 loc, auth_token=_auth_token)
1454 loc, auth_token=_auth_token)
1437
1455
1438 # explicit controller is enabled or API is in our whitelist
1456 # explicit controller is enabled or API is in our whitelist
1439 if self.auth_token_access or auth_token_access_valid:
1457 if self.auth_token_access or auth_token_access_valid:
1440 log.debug('Checking AUTH TOKEN access for %s' % (cls,))
1458 log.debug('Checking AUTH TOKEN access for %s' % (cls,))
1441 db_user = user.get_instance()
1459 db_user = user.get_instance()
1442
1460
1443 if db_user:
1461 if db_user:
1444 if self.auth_token_access:
1462 if self.auth_token_access:
1445 roles = self.auth_token_access
1463 roles = self.auth_token_access
1446 else:
1464 else:
1447 roles = [UserApiKeys.ROLE_HTTP]
1465 roles = [UserApiKeys.ROLE_HTTP]
1448 token_match = db_user.authenticate_by_token(
1466 token_match = db_user.authenticate_by_token(
1449 _auth_token, roles=roles)
1467 _auth_token, roles=roles)
1450 else:
1468 else:
1451 log.debug('Unable to fetch db instance for auth user: %s', user)
1469 log.debug('Unable to fetch db instance for auth user: %s', user)
1452 token_match = False
1470 token_match = False
1453
1471
1454 if _auth_token and token_match:
1472 if _auth_token and token_match:
1455 auth_token_access_valid = True
1473 auth_token_access_valid = True
1456 log.debug('AUTH TOKEN ****%s is VALID' % (_auth_token[-4:],))
1474 log.debug('AUTH TOKEN ****%s is VALID' % (_auth_token[-4:],))
1457 else:
1475 else:
1458 auth_token_access_valid = False
1476 auth_token_access_valid = False
1459 if not _auth_token:
1477 if not _auth_token:
1460 log.debug("AUTH TOKEN *NOT* present in request")
1478 log.debug("AUTH TOKEN *NOT* present in request")
1461 else:
1479 else:
1462 log.warning(
1480 log.warning(
1463 "AUTH TOKEN ****%s *NOT* valid" % _auth_token[-4:])
1481 "AUTH TOKEN ****%s *NOT* valid" % _auth_token[-4:])
1464
1482
1465 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
1483 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
1466 reason = 'RHODECODE_AUTH' if user.is_authenticated \
1484 reason = 'RHODECODE_AUTH' if user.is_authenticated \
1467 else 'AUTH_TOKEN_AUTH'
1485 else 'AUTH_TOKEN_AUTH'
1468
1486
1469 if ip_access_valid and (
1487 if ip_access_valid and (
1470 user.is_authenticated or auth_token_access_valid):
1488 user.is_authenticated or auth_token_access_valid):
1471 log.info(
1489 log.info(
1472 'user %s authenticating with:%s IS authenticated on func %s'
1490 'user %s authenticating with:%s IS authenticated on func %s'
1473 % (user, reason, loc))
1491 % (user, reason, loc))
1474
1492
1475 # update user data to check last activity
1493 # update user data to check last activity
1476 user.update_lastactivity()
1494 user.update_lastactivity()
1477 Session().commit()
1495 Session().commit()
1478 return func(*fargs, **fkwargs)
1496 return func(*fargs, **fkwargs)
1479 else:
1497 else:
1480 log.warning(
1498 log.warning(
1481 'user %s authenticating with:%s NOT authenticated on '
1499 'user %s authenticating with:%s NOT authenticated on '
1482 'func: %s: IP_ACCESS:%s AUTH_TOKEN_ACCESS:%s'
1500 'func: %s: IP_ACCESS:%s AUTH_TOKEN_ACCESS:%s'
1483 % (user, reason, loc, ip_access_valid,
1501 % (user, reason, loc, ip_access_valid,
1484 auth_token_access_valid))
1502 auth_token_access_valid))
1485 # we preserve the get PARAM
1503 # we preserve the get PARAM
1486 came_from = get_came_from(request)
1504 came_from = get_came_from(request)
1487
1505
1488 log.debug('redirecting to login page with %s' % (came_from,))
1506 log.debug('redirecting to login page with %s' % (came_from,))
1489 raise HTTPFound(
1507 raise HTTPFound(
1490 h.route_path('login', _query={'came_from': came_from}))
1508 h.route_path('login', _query={'came_from': came_from}))
1491
1509
1492
1510
1493 class NotAnonymous(object):
1511 class NotAnonymous(object):
1494 """
1512 """
1495 Must be logged in to execute this function else
1513 Must be logged in to execute this function else
1496 redirect to login page
1514 redirect to login page
1497 """
1515 """
1498
1516
1499 def __call__(self, func):
1517 def __call__(self, func):
1500 return get_cython_compat_decorator(self.__wrapper, func)
1518 return get_cython_compat_decorator(self.__wrapper, func)
1501
1519
1502 def _get_request(self):
1520 def _get_request(self):
1503 return get_request(self)
1521 return get_request(self)
1504
1522
1505 def __wrapper(self, func, *fargs, **fkwargs):
1523 def __wrapper(self, func, *fargs, **fkwargs):
1506 import rhodecode.lib.helpers as h
1524 import rhodecode.lib.helpers as h
1507 cls = fargs[0]
1525 cls = fargs[0]
1508 self.user = cls._rhodecode_user
1526 self.user = cls._rhodecode_user
1509 request = self._get_request()
1527 request = self._get_request()
1510
1528
1511 log.debug('Checking if user is not anonymous @%s' % cls)
1529 log.debug('Checking if user is not anonymous @%s' % cls)
1512
1530
1513 anonymous = self.user.username == User.DEFAULT_USER
1531 anonymous = self.user.username == User.DEFAULT_USER
1514
1532
1515 if anonymous:
1533 if anonymous:
1516 came_from = get_came_from(request)
1534 came_from = get_came_from(request)
1517 h.flash(_('You need to be a registered user to '
1535 h.flash(_('You need to be a registered user to '
1518 'perform this action'),
1536 'perform this action'),
1519 category='warning')
1537 category='warning')
1520 raise HTTPFound(
1538 raise HTTPFound(
1521 h.route_path('login', _query={'came_from': came_from}))
1539 h.route_path('login', _query={'came_from': came_from}))
1522 else:
1540 else:
1523 return func(*fargs, **fkwargs)
1541 return func(*fargs, **fkwargs)
1524
1542
1525
1543
1526 class PermsDecorator(object):
1544 class PermsDecorator(object):
1527 """
1545 """
1528 Base class for controller decorators, we extract the current user from
1546 Base class for controller decorators, we extract the current user from
1529 the class itself, which has it stored in base controllers
1547 the class itself, which has it stored in base controllers
1530 """
1548 """
1531
1549
1532 def __init__(self, *required_perms):
1550 def __init__(self, *required_perms):
1533 self.required_perms = set(required_perms)
1551 self.required_perms = set(required_perms)
1534
1552
1535 def __call__(self, func):
1553 def __call__(self, func):
1536 return get_cython_compat_decorator(self.__wrapper, func)
1554 return get_cython_compat_decorator(self.__wrapper, func)
1537
1555
1538 def _get_request(self):
1556 def _get_request(self):
1539 return get_request(self)
1557 return get_request(self)
1540
1558
1541 def __wrapper(self, func, *fargs, **fkwargs):
1559 def __wrapper(self, func, *fargs, **fkwargs):
1542 import rhodecode.lib.helpers as h
1560 import rhodecode.lib.helpers as h
1543 cls = fargs[0]
1561 cls = fargs[0]
1544 _user = cls._rhodecode_user
1562 _user = cls._rhodecode_user
1545
1563
1546 log.debug('checking %s permissions %s for %s %s',
1564 log.debug('checking %s permissions %s for %s %s',
1547 self.__class__.__name__, self.required_perms, cls, _user)
1565 self.__class__.__name__, self.required_perms, cls, _user)
1548
1566
1549 if self.check_permissions(_user):
1567 if self.check_permissions(_user):
1550 log.debug('Permission granted for %s %s', cls, _user)
1568 log.debug('Permission granted for %s %s', cls, _user)
1551 return func(*fargs, **fkwargs)
1569 return func(*fargs, **fkwargs)
1552
1570
1553 else:
1571 else:
1554 log.debug('Permission denied for %s %s', cls, _user)
1572 log.debug('Permission denied for %s %s', cls, _user)
1555 anonymous = _user.username == User.DEFAULT_USER
1573 anonymous = _user.username == User.DEFAULT_USER
1556
1574
1557 if anonymous:
1575 if anonymous:
1558 came_from = get_came_from(self._get_request())
1576 came_from = get_came_from(self._get_request())
1559 h.flash(_('You need to be signed in to view this page'),
1577 h.flash(_('You need to be signed in to view this page'),
1560 category='warning')
1578 category='warning')
1561 raise HTTPFound(
1579 raise HTTPFound(
1562 h.route_path('login', _query={'came_from': came_from}))
1580 h.route_path('login', _query={'came_from': came_from}))
1563
1581
1564 else:
1582 else:
1565 # redirect with 404 to prevent resource discovery
1583 # redirect with 404 to prevent resource discovery
1566 raise HTTPNotFound()
1584 raise HTTPNotFound()
1567
1585
1568 def check_permissions(self, user):
1586 def check_permissions(self, user):
1569 """Dummy function for overriding"""
1587 """Dummy function for overriding"""
1570 raise NotImplementedError(
1588 raise NotImplementedError(
1571 'You have to write this function in child class')
1589 'You have to write this function in child class')
1572
1590
1573
1591
1574 class HasPermissionAllDecorator(PermsDecorator):
1592 class HasPermissionAllDecorator(PermsDecorator):
1575 """
1593 """
1576 Checks for access permission for all given predicates. All of them
1594 Checks for access permission for all given predicates. All of them
1577 have to be meet in order to fulfill the request
1595 have to be meet in order to fulfill the request
1578 """
1596 """
1579
1597
1580 def check_permissions(self, user):
1598 def check_permissions(self, user):
1581 perms = user.permissions_with_scope({})
1599 perms = user.permissions_with_scope({})
1582 if self.required_perms.issubset(perms['global']):
1600 if self.required_perms.issubset(perms['global']):
1583 return True
1601 return True
1584 return False
1602 return False
1585
1603
1586
1604
1587 class HasPermissionAnyDecorator(PermsDecorator):
1605 class HasPermissionAnyDecorator(PermsDecorator):
1588 """
1606 """
1589 Checks for access permission for any of given predicates. In order to
1607 Checks for access permission for any of given predicates. In order to
1590 fulfill the request any of predicates must be meet
1608 fulfill the request any of predicates must be meet
1591 """
1609 """
1592
1610
1593 def check_permissions(self, user):
1611 def check_permissions(self, user):
1594 perms = user.permissions_with_scope({})
1612 perms = user.permissions_with_scope({})
1595 if self.required_perms.intersection(perms['global']):
1613 if self.required_perms.intersection(perms['global']):
1596 return True
1614 return True
1597 return False
1615 return False
1598
1616
1599
1617
1600 class HasRepoPermissionAllDecorator(PermsDecorator):
1618 class HasRepoPermissionAllDecorator(PermsDecorator):
1601 """
1619 """
1602 Checks for access permission for all given predicates for specific
1620 Checks for access permission for all given predicates for specific
1603 repository. All of them have to be meet in order to fulfill the request
1621 repository. All of them have to be meet in order to fulfill the request
1604 """
1622 """
1605 def _get_repo_name(self):
1623 def _get_repo_name(self):
1606 _request = self._get_request()
1624 _request = self._get_request()
1607 return get_repo_slug(_request)
1625 return get_repo_slug(_request)
1608
1626
1609 def check_permissions(self, user):
1627 def check_permissions(self, user):
1610 perms = user.permissions
1628 perms = user.permissions
1611 repo_name = self._get_repo_name()
1629 repo_name = self._get_repo_name()
1612
1630
1613 try:
1631 try:
1614 user_perms = set([perms['repositories'][repo_name]])
1632 user_perms = set([perms['repositories'][repo_name]])
1615 except KeyError:
1633 except KeyError:
1616 log.debug('cannot locate repo with name: `%s` in permissions defs',
1634 log.debug('cannot locate repo with name: `%s` in permissions defs',
1617 repo_name)
1635 repo_name)
1618 return False
1636 return False
1619
1637
1620 log.debug('checking `%s` permissions for repo `%s`',
1638 log.debug('checking `%s` permissions for repo `%s`',
1621 user_perms, repo_name)
1639 user_perms, repo_name)
1622 if self.required_perms.issubset(user_perms):
1640 if self.required_perms.issubset(user_perms):
1623 return True
1641 return True
1624 return False
1642 return False
1625
1643
1626
1644
1627 class HasRepoPermissionAnyDecorator(PermsDecorator):
1645 class HasRepoPermissionAnyDecorator(PermsDecorator):
1628 """
1646 """
1629 Checks for access permission for any of given predicates for specific
1647 Checks for access permission for any of given predicates for specific
1630 repository. In order to fulfill the request any of predicates must be meet
1648 repository. In order to fulfill the request any of predicates must be meet
1631 """
1649 """
1632 def _get_repo_name(self):
1650 def _get_repo_name(self):
1633 _request = self._get_request()
1651 _request = self._get_request()
1634 return get_repo_slug(_request)
1652 return get_repo_slug(_request)
1635
1653
1636 def check_permissions(self, user):
1654 def check_permissions(self, user):
1637 perms = user.permissions
1655 perms = user.permissions
1638 repo_name = self._get_repo_name()
1656 repo_name = self._get_repo_name()
1639
1657
1640 try:
1658 try:
1641 user_perms = set([perms['repositories'][repo_name]])
1659 user_perms = set([perms['repositories'][repo_name]])
1642 except KeyError:
1660 except KeyError:
1643 log.debug(
1661 log.debug(
1644 'cannot locate repo with name: `%s` in permissions defs',
1662 'cannot locate repo with name: `%s` in permissions defs',
1645 repo_name)
1663 repo_name)
1646 return False
1664 return False
1647
1665
1648 log.debug('checking `%s` permissions for repo `%s`',
1666 log.debug('checking `%s` permissions for repo `%s`',
1649 user_perms, repo_name)
1667 user_perms, repo_name)
1650 if self.required_perms.intersection(user_perms):
1668 if self.required_perms.intersection(user_perms):
1651 return True
1669 return True
1652 return False
1670 return False
1653
1671
1654
1672
1655 class HasRepoGroupPermissionAllDecorator(PermsDecorator):
1673 class HasRepoGroupPermissionAllDecorator(PermsDecorator):
1656 """
1674 """
1657 Checks for access permission for all given predicates for specific
1675 Checks for access permission for all given predicates for specific
1658 repository group. All of them have to be meet in order to
1676 repository group. All of them have to be meet in order to
1659 fulfill the request
1677 fulfill the request
1660 """
1678 """
1661 def _get_repo_group_name(self):
1679 def _get_repo_group_name(self):
1662 _request = self._get_request()
1680 _request = self._get_request()
1663 return get_repo_group_slug(_request)
1681 return get_repo_group_slug(_request)
1664
1682
1665 def check_permissions(self, user):
1683 def check_permissions(self, user):
1666 perms = user.permissions
1684 perms = user.permissions
1667 group_name = self._get_repo_group_name()
1685 group_name = self._get_repo_group_name()
1668 try:
1686 try:
1669 user_perms = set([perms['repositories_groups'][group_name]])
1687 user_perms = set([perms['repositories_groups'][group_name]])
1670 except KeyError:
1688 except KeyError:
1671 log.debug(
1689 log.debug(
1672 'cannot locate repo group with name: `%s` in permissions defs',
1690 'cannot locate repo group with name: `%s` in permissions defs',
1673 group_name)
1691 group_name)
1674 return False
1692 return False
1675
1693
1676 log.debug('checking `%s` permissions for repo group `%s`',
1694 log.debug('checking `%s` permissions for repo group `%s`',
1677 user_perms, group_name)
1695 user_perms, group_name)
1678 if self.required_perms.issubset(user_perms):
1696 if self.required_perms.issubset(user_perms):
1679 return True
1697 return True
1680 return False
1698 return False
1681
1699
1682
1700
1683 class HasRepoGroupPermissionAnyDecorator(PermsDecorator):
1701 class HasRepoGroupPermissionAnyDecorator(PermsDecorator):
1684 """
1702 """
1685 Checks for access permission for any of given predicates for specific
1703 Checks for access permission for any of given predicates for specific
1686 repository group. In order to fulfill the request any
1704 repository group. In order to fulfill the request any
1687 of predicates must be met
1705 of predicates must be met
1688 """
1706 """
1689 def _get_repo_group_name(self):
1707 def _get_repo_group_name(self):
1690 _request = self._get_request()
1708 _request = self._get_request()
1691 return get_repo_group_slug(_request)
1709 return get_repo_group_slug(_request)
1692
1710
1693 def check_permissions(self, user):
1711 def check_permissions(self, user):
1694 perms = user.permissions
1712 perms = user.permissions
1695 group_name = self._get_repo_group_name()
1713 group_name = self._get_repo_group_name()
1696
1714
1697 try:
1715 try:
1698 user_perms = set([perms['repositories_groups'][group_name]])
1716 user_perms = set([perms['repositories_groups'][group_name]])
1699 except KeyError:
1717 except KeyError:
1700 log.debug(
1718 log.debug(
1701 'cannot locate repo group with name: `%s` in permissions defs',
1719 'cannot locate repo group with name: `%s` in permissions defs',
1702 group_name)
1720 group_name)
1703 return False
1721 return False
1704
1722
1705 log.debug('checking `%s` permissions for repo group `%s`',
1723 log.debug('checking `%s` permissions for repo group `%s`',
1706 user_perms, group_name)
1724 user_perms, group_name)
1707 if self.required_perms.intersection(user_perms):
1725 if self.required_perms.intersection(user_perms):
1708 return True
1726 return True
1709 return False
1727 return False
1710
1728
1711
1729
1712 class HasUserGroupPermissionAllDecorator(PermsDecorator):
1730 class HasUserGroupPermissionAllDecorator(PermsDecorator):
1713 """
1731 """
1714 Checks for access permission for all given predicates for specific
1732 Checks for access permission for all given predicates for specific
1715 user group. All of them have to be meet in order to fulfill the request
1733 user group. All of them have to be meet in order to fulfill the request
1716 """
1734 """
1717 def _get_user_group_name(self):
1735 def _get_user_group_name(self):
1718 _request = self._get_request()
1736 _request = self._get_request()
1719 return get_user_group_slug(_request)
1737 return get_user_group_slug(_request)
1720
1738
1721 def check_permissions(self, user):
1739 def check_permissions(self, user):
1722 perms = user.permissions
1740 perms = user.permissions
1723 group_name = self._get_user_group_name()
1741 group_name = self._get_user_group_name()
1724 try:
1742 try:
1725 user_perms = set([perms['user_groups'][group_name]])
1743 user_perms = set([perms['user_groups'][group_name]])
1726 except KeyError:
1744 except KeyError:
1727 return False
1745 return False
1728
1746
1729 if self.required_perms.issubset(user_perms):
1747 if self.required_perms.issubset(user_perms):
1730 return True
1748 return True
1731 return False
1749 return False
1732
1750
1733
1751
1734 class HasUserGroupPermissionAnyDecorator(PermsDecorator):
1752 class HasUserGroupPermissionAnyDecorator(PermsDecorator):
1735 """
1753 """
1736 Checks for access permission for any of given predicates for specific
1754 Checks for access permission for any of given predicates for specific
1737 user group. In order to fulfill the request any of predicates must be meet
1755 user group. In order to fulfill the request any of predicates must be meet
1738 """
1756 """
1739 def _get_user_group_name(self):
1757 def _get_user_group_name(self):
1740 _request = self._get_request()
1758 _request = self._get_request()
1741 return get_user_group_slug(_request)
1759 return get_user_group_slug(_request)
1742
1760
1743 def check_permissions(self, user):
1761 def check_permissions(self, user):
1744 perms = user.permissions
1762 perms = user.permissions
1745 group_name = self._get_user_group_name()
1763 group_name = self._get_user_group_name()
1746 try:
1764 try:
1747 user_perms = set([perms['user_groups'][group_name]])
1765 user_perms = set([perms['user_groups'][group_name]])
1748 except KeyError:
1766 except KeyError:
1749 return False
1767 return False
1750
1768
1751 if self.required_perms.intersection(user_perms):
1769 if self.required_perms.intersection(user_perms):
1752 return True
1770 return True
1753 return False
1771 return False
1754
1772
1755
1773
1756 # CHECK FUNCTIONS
1774 # CHECK FUNCTIONS
1757 class PermsFunction(object):
1775 class PermsFunction(object):
1758 """Base function for other check functions"""
1776 """Base function for other check functions"""
1759
1777
1760 def __init__(self, *perms):
1778 def __init__(self, *perms):
1761 self.required_perms = set(perms)
1779 self.required_perms = set(perms)
1762 self.repo_name = None
1780 self.repo_name = None
1763 self.repo_group_name = None
1781 self.repo_group_name = None
1764 self.user_group_name = None
1782 self.user_group_name = None
1765
1783
1766 def __bool__(self):
1784 def __bool__(self):
1767 frame = inspect.currentframe()
1785 frame = inspect.currentframe()
1768 stack_trace = traceback.format_stack(frame)
1786 stack_trace = traceback.format_stack(frame)
1769 log.error('Checking bool value on a class instance of perm '
1787 log.error('Checking bool value on a class instance of perm '
1770 'function is not allowed: %s' % ''.join(stack_trace))
1788 'function is not allowed: %s' % ''.join(stack_trace))
1771 # rather than throwing errors, here we always return False so if by
1789 # rather than throwing errors, here we always return False so if by
1772 # accident someone checks truth for just an instance it will always end
1790 # accident someone checks truth for just an instance it will always end
1773 # up in returning False
1791 # up in returning False
1774 return False
1792 return False
1775 __nonzero__ = __bool__
1793 __nonzero__ = __bool__
1776
1794
1777 def __call__(self, check_location='', user=None):
1795 def __call__(self, check_location='', user=None):
1778 if not user:
1796 if not user:
1779 log.debug('Using user attribute from global request')
1797 log.debug('Using user attribute from global request')
1780 # TODO: remove this someday,put as user as attribute here
1798 # TODO: remove this someday,put as user as attribute here
1781 request = self._get_request()
1799 request = self._get_request()
1782 user = request.user
1800 user = request.user
1783
1801
1784 # init auth user if not already given
1802 # init auth user if not already given
1785 if not isinstance(user, AuthUser):
1803 if not isinstance(user, AuthUser):
1786 log.debug('Wrapping user %s into AuthUser', user)
1804 log.debug('Wrapping user %s into AuthUser', user)
1787 user = AuthUser(user.user_id)
1805 user = AuthUser(user.user_id)
1788
1806
1789 cls_name = self.__class__.__name__
1807 cls_name = self.__class__.__name__
1790 check_scope = self._get_check_scope(cls_name)
1808 check_scope = self._get_check_scope(cls_name)
1791 check_location = check_location or 'unspecified location'
1809 check_location = check_location or 'unspecified location'
1792
1810
1793 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
1811 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
1794 self.required_perms, user, check_scope, check_location)
1812 self.required_perms, user, check_scope, check_location)
1795 if not user:
1813 if not user:
1796 log.warning('Empty user given for permission check')
1814 log.warning('Empty user given for permission check')
1797 return False
1815 return False
1798
1816
1799 if self.check_permissions(user):
1817 if self.check_permissions(user):
1800 log.debug('Permission to repo:`%s` GRANTED for user:`%s` @ %s',
1818 log.debug('Permission to repo:`%s` GRANTED for user:`%s` @ %s',
1801 check_scope, user, check_location)
1819 check_scope, user, check_location)
1802 return True
1820 return True
1803
1821
1804 else:
1822 else:
1805 log.debug('Permission to repo:`%s` DENIED for user:`%s` @ %s',
1823 log.debug('Permission to repo:`%s` DENIED for user:`%s` @ %s',
1806 check_scope, user, check_location)
1824 check_scope, user, check_location)
1807 return False
1825 return False
1808
1826
1809 def _get_request(self):
1827 def _get_request(self):
1810 return get_request(self)
1828 return get_request(self)
1811
1829
1812 def _get_check_scope(self, cls_name):
1830 def _get_check_scope(self, cls_name):
1813 return {
1831 return {
1814 'HasPermissionAll': 'GLOBAL',
1832 'HasPermissionAll': 'GLOBAL',
1815 'HasPermissionAny': 'GLOBAL',
1833 'HasPermissionAny': 'GLOBAL',
1816 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
1834 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
1817 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
1835 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
1818 'HasRepoGroupPermissionAll': 'repo_group:%s' % self.repo_group_name,
1836 'HasRepoGroupPermissionAll': 'repo_group:%s' % self.repo_group_name,
1819 'HasRepoGroupPermissionAny': 'repo_group:%s' % self.repo_group_name,
1837 'HasRepoGroupPermissionAny': 'repo_group:%s' % self.repo_group_name,
1820 'HasUserGroupPermissionAll': 'user_group:%s' % self.user_group_name,
1838 'HasUserGroupPermissionAll': 'user_group:%s' % self.user_group_name,
1821 'HasUserGroupPermissionAny': 'user_group:%s' % self.user_group_name,
1839 'HasUserGroupPermissionAny': 'user_group:%s' % self.user_group_name,
1822 }.get(cls_name, '?:%s' % cls_name)
1840 }.get(cls_name, '?:%s' % cls_name)
1823
1841
1824 def check_permissions(self, user):
1842 def check_permissions(self, user):
1825 """Dummy function for overriding"""
1843 """Dummy function for overriding"""
1826 raise Exception('You have to write this function in child class')
1844 raise Exception('You have to write this function in child class')
1827
1845
1828
1846
1829 class HasPermissionAll(PermsFunction):
1847 class HasPermissionAll(PermsFunction):
1830 def check_permissions(self, user):
1848 def check_permissions(self, user):
1831 perms = user.permissions_with_scope({})
1849 perms = user.permissions_with_scope({})
1832 if self.required_perms.issubset(perms.get('global')):
1850 if self.required_perms.issubset(perms.get('global')):
1833 return True
1851 return True
1834 return False
1852 return False
1835
1853
1836
1854
1837 class HasPermissionAny(PermsFunction):
1855 class HasPermissionAny(PermsFunction):
1838 def check_permissions(self, user):
1856 def check_permissions(self, user):
1839 perms = user.permissions_with_scope({})
1857 perms = user.permissions_with_scope({})
1840 if self.required_perms.intersection(perms.get('global')):
1858 if self.required_perms.intersection(perms.get('global')):
1841 return True
1859 return True
1842 return False
1860 return False
1843
1861
1844
1862
1845 class HasRepoPermissionAll(PermsFunction):
1863 class HasRepoPermissionAll(PermsFunction):
1846 def __call__(self, repo_name=None, check_location='', user=None):
1864 def __call__(self, repo_name=None, check_location='', user=None):
1847 self.repo_name = repo_name
1865 self.repo_name = repo_name
1848 return super(HasRepoPermissionAll, self).__call__(check_location, user)
1866 return super(HasRepoPermissionAll, self).__call__(check_location, user)
1849
1867
1850 def _get_repo_name(self):
1868 def _get_repo_name(self):
1851 if not self.repo_name:
1869 if not self.repo_name:
1852 _request = self._get_request()
1870 _request = self._get_request()
1853 self.repo_name = get_repo_slug(_request)
1871 self.repo_name = get_repo_slug(_request)
1854 return self.repo_name
1872 return self.repo_name
1855
1873
1856 def check_permissions(self, user):
1874 def check_permissions(self, user):
1857 self.repo_name = self._get_repo_name()
1875 self.repo_name = self._get_repo_name()
1858 perms = user.permissions
1876 perms = user.permissions
1859 try:
1877 try:
1860 user_perms = set([perms['repositories'][self.repo_name]])
1878 user_perms = set([perms['repositories'][self.repo_name]])
1861 except KeyError:
1879 except KeyError:
1862 return False
1880 return False
1863 if self.required_perms.issubset(user_perms):
1881 if self.required_perms.issubset(user_perms):
1864 return True
1882 return True
1865 return False
1883 return False
1866
1884
1867
1885
1868 class HasRepoPermissionAny(PermsFunction):
1886 class HasRepoPermissionAny(PermsFunction):
1869 def __call__(self, repo_name=None, check_location='', user=None):
1887 def __call__(self, repo_name=None, check_location='', user=None):
1870 self.repo_name = repo_name
1888 self.repo_name = repo_name
1871 return super(HasRepoPermissionAny, self).__call__(check_location, user)
1889 return super(HasRepoPermissionAny, self).__call__(check_location, user)
1872
1890
1873 def _get_repo_name(self):
1891 def _get_repo_name(self):
1874 if not self.repo_name:
1892 if not self.repo_name:
1875 _request = self._get_request()
1893 _request = self._get_request()
1876 self.repo_name = get_repo_slug(_request)
1894 self.repo_name = get_repo_slug(_request)
1877 return self.repo_name
1895 return self.repo_name
1878
1896
1879 def check_permissions(self, user):
1897 def check_permissions(self, user):
1880 self.repo_name = self._get_repo_name()
1898 self.repo_name = self._get_repo_name()
1881 perms = user.permissions
1899 perms = user.permissions
1882 try:
1900 try:
1883 user_perms = set([perms['repositories'][self.repo_name]])
1901 user_perms = set([perms['repositories'][self.repo_name]])
1884 except KeyError:
1902 except KeyError:
1885 return False
1903 return False
1886 if self.required_perms.intersection(user_perms):
1904 if self.required_perms.intersection(user_perms):
1887 return True
1905 return True
1888 return False
1906 return False
1889
1907
1890
1908
1891 class HasRepoGroupPermissionAny(PermsFunction):
1909 class HasRepoGroupPermissionAny(PermsFunction):
1892 def __call__(self, group_name=None, check_location='', user=None):
1910 def __call__(self, group_name=None, check_location='', user=None):
1893 self.repo_group_name = group_name
1911 self.repo_group_name = group_name
1894 return super(HasRepoGroupPermissionAny, self).__call__(
1912 return super(HasRepoGroupPermissionAny, self).__call__(
1895 check_location, user)
1913 check_location, user)
1896
1914
1897 def check_permissions(self, user):
1915 def check_permissions(self, user):
1898 perms = user.permissions
1916 perms = user.permissions
1899 try:
1917 try:
1900 user_perms = set(
1918 user_perms = set(
1901 [perms['repositories_groups'][self.repo_group_name]])
1919 [perms['repositories_groups'][self.repo_group_name]])
1902 except KeyError:
1920 except KeyError:
1903 return False
1921 return False
1904 if self.required_perms.intersection(user_perms):
1922 if self.required_perms.intersection(user_perms):
1905 return True
1923 return True
1906 return False
1924 return False
1907
1925
1908
1926
1909 class HasRepoGroupPermissionAll(PermsFunction):
1927 class HasRepoGroupPermissionAll(PermsFunction):
1910 def __call__(self, group_name=None, check_location='', user=None):
1928 def __call__(self, group_name=None, check_location='', user=None):
1911 self.repo_group_name = group_name
1929 self.repo_group_name = group_name
1912 return super(HasRepoGroupPermissionAll, self).__call__(
1930 return super(HasRepoGroupPermissionAll, self).__call__(
1913 check_location, user)
1931 check_location, user)
1914
1932
1915 def check_permissions(self, user):
1933 def check_permissions(self, user):
1916 perms = user.permissions
1934 perms = user.permissions
1917 try:
1935 try:
1918 user_perms = set(
1936 user_perms = set(
1919 [perms['repositories_groups'][self.repo_group_name]])
1937 [perms['repositories_groups'][self.repo_group_name]])
1920 except KeyError:
1938 except KeyError:
1921 return False
1939 return False
1922 if self.required_perms.issubset(user_perms):
1940 if self.required_perms.issubset(user_perms):
1923 return True
1941 return True
1924 return False
1942 return False
1925
1943
1926
1944
1927 class HasUserGroupPermissionAny(PermsFunction):
1945 class HasUserGroupPermissionAny(PermsFunction):
1928 def __call__(self, user_group_name=None, check_location='', user=None):
1946 def __call__(self, user_group_name=None, check_location='', user=None):
1929 self.user_group_name = user_group_name
1947 self.user_group_name = user_group_name
1930 return super(HasUserGroupPermissionAny, self).__call__(
1948 return super(HasUserGroupPermissionAny, self).__call__(
1931 check_location, user)
1949 check_location, user)
1932
1950
1933 def check_permissions(self, user):
1951 def check_permissions(self, user):
1934 perms = user.permissions
1952 perms = user.permissions
1935 try:
1953 try:
1936 user_perms = set([perms['user_groups'][self.user_group_name]])
1954 user_perms = set([perms['user_groups'][self.user_group_name]])
1937 except KeyError:
1955 except KeyError:
1938 return False
1956 return False
1939 if self.required_perms.intersection(user_perms):
1957 if self.required_perms.intersection(user_perms):
1940 return True
1958 return True
1941 return False
1959 return False
1942
1960
1943
1961
1944 class HasUserGroupPermissionAll(PermsFunction):
1962 class HasUserGroupPermissionAll(PermsFunction):
1945 def __call__(self, user_group_name=None, check_location='', user=None):
1963 def __call__(self, user_group_name=None, check_location='', user=None):
1946 self.user_group_name = user_group_name
1964 self.user_group_name = user_group_name
1947 return super(HasUserGroupPermissionAll, self).__call__(
1965 return super(HasUserGroupPermissionAll, self).__call__(
1948 check_location, user)
1966 check_location, user)
1949
1967
1950 def check_permissions(self, user):
1968 def check_permissions(self, user):
1951 perms = user.permissions
1969 perms = user.permissions
1952 try:
1970 try:
1953 user_perms = set([perms['user_groups'][self.user_group_name]])
1971 user_perms = set([perms['user_groups'][self.user_group_name]])
1954 except KeyError:
1972 except KeyError:
1955 return False
1973 return False
1956 if self.required_perms.issubset(user_perms):
1974 if self.required_perms.issubset(user_perms):
1957 return True
1975 return True
1958 return False
1976 return False
1959
1977
1960
1978
1961 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
1979 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
1962 class HasPermissionAnyMiddleware(object):
1980 class HasPermissionAnyMiddleware(object):
1963 def __init__(self, *perms):
1981 def __init__(self, *perms):
1964 self.required_perms = set(perms)
1982 self.required_perms = set(perms)
1965
1983
1966 def __call__(self, user, repo_name):
1984 def __call__(self, user, repo_name):
1967 # repo_name MUST be unicode, since we handle keys in permission
1985 # repo_name MUST be unicode, since we handle keys in permission
1968 # dict by unicode
1986 # dict by unicode
1969 repo_name = safe_unicode(repo_name)
1987 repo_name = safe_unicode(repo_name)
1970 user = AuthUser(user.user_id)
1988 user = AuthUser(user.user_id)
1971 log.debug(
1989 log.debug(
1972 'Checking VCS protocol permissions %s for user:%s repo:`%s`',
1990 'Checking VCS protocol permissions %s for user:%s repo:`%s`',
1973 self.required_perms, user, repo_name)
1991 self.required_perms, user, repo_name)
1974
1992
1975 if self.check_permissions(user, repo_name):
1993 if self.check_permissions(user, repo_name):
1976 log.debug('Permission to repo:`%s` GRANTED for user:%s @ %s',
1994 log.debug('Permission to repo:`%s` GRANTED for user:%s @ %s',
1977 repo_name, user, 'PermissionMiddleware')
1995 repo_name, user, 'PermissionMiddleware')
1978 return True
1996 return True
1979
1997
1980 else:
1998 else:
1981 log.debug('Permission to repo:`%s` DENIED for user:%s @ %s',
1999 log.debug('Permission to repo:`%s` DENIED for user:%s @ %s',
1982 repo_name, user, 'PermissionMiddleware')
2000 repo_name, user, 'PermissionMiddleware')
1983 return False
2001 return False
1984
2002
1985 def check_permissions(self, user, repo_name):
2003 def check_permissions(self, user, repo_name):
1986 perms = user.permissions_with_scope({'repo_name': repo_name})
2004 perms = user.permissions_with_scope({'repo_name': repo_name})
1987
2005
1988 try:
2006 try:
1989 user_perms = set([perms['repositories'][repo_name]])
2007 user_perms = set([perms['repositories'][repo_name]])
1990 except Exception:
2008 except Exception:
1991 log.exception('Error while accessing user permissions')
2009 log.exception('Error while accessing user permissions')
1992 return False
2010 return False
1993
2011
1994 if self.required_perms.intersection(user_perms):
2012 if self.required_perms.intersection(user_perms):
1995 return True
2013 return True
1996 return False
2014 return False
1997
2015
1998
2016
1999 # SPECIAL VERSION TO HANDLE API AUTH
2017 # SPECIAL VERSION TO HANDLE API AUTH
2000 class _BaseApiPerm(object):
2018 class _BaseApiPerm(object):
2001 def __init__(self, *perms):
2019 def __init__(self, *perms):
2002 self.required_perms = set(perms)
2020 self.required_perms = set(perms)
2003
2021
2004 def __call__(self, check_location=None, user=None, repo_name=None,
2022 def __call__(self, check_location=None, user=None, repo_name=None,
2005 group_name=None, user_group_name=None):
2023 group_name=None, user_group_name=None):
2006 cls_name = self.__class__.__name__
2024 cls_name = self.__class__.__name__
2007 check_scope = 'global:%s' % (self.required_perms,)
2025 check_scope = 'global:%s' % (self.required_perms,)
2008 if repo_name:
2026 if repo_name:
2009 check_scope += ', repo_name:%s' % (repo_name,)
2027 check_scope += ', repo_name:%s' % (repo_name,)
2010
2028
2011 if group_name:
2029 if group_name:
2012 check_scope += ', repo_group_name:%s' % (group_name,)
2030 check_scope += ', repo_group_name:%s' % (group_name,)
2013
2031
2014 if user_group_name:
2032 if user_group_name:
2015 check_scope += ', user_group_name:%s' % (user_group_name,)
2033 check_scope += ', user_group_name:%s' % (user_group_name,)
2016
2034
2017 log.debug(
2035 log.debug(
2018 'checking cls:%s %s %s @ %s'
2036 'checking cls:%s %s %s @ %s'
2019 % (cls_name, self.required_perms, check_scope, check_location))
2037 % (cls_name, self.required_perms, check_scope, check_location))
2020 if not user:
2038 if not user:
2021 log.debug('Empty User passed into arguments')
2039 log.debug('Empty User passed into arguments')
2022 return False
2040 return False
2023
2041
2024 # process user
2042 # process user
2025 if not isinstance(user, AuthUser):
2043 if not isinstance(user, AuthUser):
2026 user = AuthUser(user.user_id)
2044 user = AuthUser(user.user_id)
2027 if not check_location:
2045 if not check_location:
2028 check_location = 'unspecified'
2046 check_location = 'unspecified'
2029 if self.check_permissions(user.permissions, repo_name, group_name,
2047 if self.check_permissions(user.permissions, repo_name, group_name,
2030 user_group_name):
2048 user_group_name):
2031 log.debug('Permission to repo:`%s` GRANTED for user:`%s` @ %s',
2049 log.debug('Permission to repo:`%s` GRANTED for user:`%s` @ %s',
2032 check_scope, user, check_location)
2050 check_scope, user, check_location)
2033 return True
2051 return True
2034
2052
2035 else:
2053 else:
2036 log.debug('Permission to repo:`%s` DENIED for user:`%s` @ %s',
2054 log.debug('Permission to repo:`%s` DENIED for user:`%s` @ %s',
2037 check_scope, user, check_location)
2055 check_scope, user, check_location)
2038 return False
2056 return False
2039
2057
2040 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2058 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2041 user_group_name=None):
2059 user_group_name=None):
2042 """
2060 """
2043 implement in child class should return True if permissions are ok,
2061 implement in child class should return True if permissions are ok,
2044 False otherwise
2062 False otherwise
2045
2063
2046 :param perm_defs: dict with permission definitions
2064 :param perm_defs: dict with permission definitions
2047 :param repo_name: repo name
2065 :param repo_name: repo name
2048 """
2066 """
2049 raise NotImplementedError()
2067 raise NotImplementedError()
2050
2068
2051
2069
2052 class HasPermissionAllApi(_BaseApiPerm):
2070 class HasPermissionAllApi(_BaseApiPerm):
2053 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2071 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2054 user_group_name=None):
2072 user_group_name=None):
2055 if self.required_perms.issubset(perm_defs.get('global')):
2073 if self.required_perms.issubset(perm_defs.get('global')):
2056 return True
2074 return True
2057 return False
2075 return False
2058
2076
2059
2077
2060 class HasPermissionAnyApi(_BaseApiPerm):
2078 class HasPermissionAnyApi(_BaseApiPerm):
2061 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2079 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2062 user_group_name=None):
2080 user_group_name=None):
2063 if self.required_perms.intersection(perm_defs.get('global')):
2081 if self.required_perms.intersection(perm_defs.get('global')):
2064 return True
2082 return True
2065 return False
2083 return False
2066
2084
2067
2085
2068 class HasRepoPermissionAllApi(_BaseApiPerm):
2086 class HasRepoPermissionAllApi(_BaseApiPerm):
2069 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2087 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2070 user_group_name=None):
2088 user_group_name=None):
2071 try:
2089 try:
2072 _user_perms = set([perm_defs['repositories'][repo_name]])
2090 _user_perms = set([perm_defs['repositories'][repo_name]])
2073 except KeyError:
2091 except KeyError:
2074 log.warning(traceback.format_exc())
2092 log.warning(traceback.format_exc())
2075 return False
2093 return False
2076 if self.required_perms.issubset(_user_perms):
2094 if self.required_perms.issubset(_user_perms):
2077 return True
2095 return True
2078 return False
2096 return False
2079
2097
2080
2098
2081 class HasRepoPermissionAnyApi(_BaseApiPerm):
2099 class HasRepoPermissionAnyApi(_BaseApiPerm):
2082 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2100 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2083 user_group_name=None):
2101 user_group_name=None):
2084 try:
2102 try:
2085 _user_perms = set([perm_defs['repositories'][repo_name]])
2103 _user_perms = set([perm_defs['repositories'][repo_name]])
2086 except KeyError:
2104 except KeyError:
2087 log.warning(traceback.format_exc())
2105 log.warning(traceback.format_exc())
2088 return False
2106 return False
2089 if self.required_perms.intersection(_user_perms):
2107 if self.required_perms.intersection(_user_perms):
2090 return True
2108 return True
2091 return False
2109 return False
2092
2110
2093
2111
2094 class HasRepoGroupPermissionAnyApi(_BaseApiPerm):
2112 class HasRepoGroupPermissionAnyApi(_BaseApiPerm):
2095 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2113 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2096 user_group_name=None):
2114 user_group_name=None):
2097 try:
2115 try:
2098 _user_perms = set([perm_defs['repositories_groups'][group_name]])
2116 _user_perms = set([perm_defs['repositories_groups'][group_name]])
2099 except KeyError:
2117 except KeyError:
2100 log.warning(traceback.format_exc())
2118 log.warning(traceback.format_exc())
2101 return False
2119 return False
2102 if self.required_perms.intersection(_user_perms):
2120 if self.required_perms.intersection(_user_perms):
2103 return True
2121 return True
2104 return False
2122 return False
2105
2123
2106
2124
2107 class HasRepoGroupPermissionAllApi(_BaseApiPerm):
2125 class HasRepoGroupPermissionAllApi(_BaseApiPerm):
2108 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2126 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2109 user_group_name=None):
2127 user_group_name=None):
2110 try:
2128 try:
2111 _user_perms = set([perm_defs['repositories_groups'][group_name]])
2129 _user_perms = set([perm_defs['repositories_groups'][group_name]])
2112 except KeyError:
2130 except KeyError:
2113 log.warning(traceback.format_exc())
2131 log.warning(traceback.format_exc())
2114 return False
2132 return False
2115 if self.required_perms.issubset(_user_perms):
2133 if self.required_perms.issubset(_user_perms):
2116 return True
2134 return True
2117 return False
2135 return False
2118
2136
2119
2137
2120 class HasUserGroupPermissionAnyApi(_BaseApiPerm):
2138 class HasUserGroupPermissionAnyApi(_BaseApiPerm):
2121 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2139 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2122 user_group_name=None):
2140 user_group_name=None):
2123 try:
2141 try:
2124 _user_perms = set([perm_defs['user_groups'][user_group_name]])
2142 _user_perms = set([perm_defs['user_groups'][user_group_name]])
2125 except KeyError:
2143 except KeyError:
2126 log.warning(traceback.format_exc())
2144 log.warning(traceback.format_exc())
2127 return False
2145 return False
2128 if self.required_perms.intersection(_user_perms):
2146 if self.required_perms.intersection(_user_perms):
2129 return True
2147 return True
2130 return False
2148 return False
2131
2149
2132
2150
2133 def check_ip_access(source_ip, allowed_ips=None):
2151 def check_ip_access(source_ip, allowed_ips=None):
2134 """
2152 """
2135 Checks if source_ip is a subnet of any of allowed_ips.
2153 Checks if source_ip is a subnet of any of allowed_ips.
2136
2154
2137 :param source_ip:
2155 :param source_ip:
2138 :param allowed_ips: list of allowed ips together with mask
2156 :param allowed_ips: list of allowed ips together with mask
2139 """
2157 """
2140 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
2158 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
2141 source_ip_address = ipaddress.ip_address(safe_unicode(source_ip))
2159 source_ip_address = ipaddress.ip_address(safe_unicode(source_ip))
2142 if isinstance(allowed_ips, (tuple, list, set)):
2160 if isinstance(allowed_ips, (tuple, list, set)):
2143 for ip in allowed_ips:
2161 for ip in allowed_ips:
2144 ip = safe_unicode(ip)
2162 ip = safe_unicode(ip)
2145 try:
2163 try:
2146 network_address = ipaddress.ip_network(ip, strict=False)
2164 network_address = ipaddress.ip_network(ip, strict=False)
2147 if source_ip_address in network_address:
2165 if source_ip_address in network_address:
2148 log.debug('IP %s is network %s' %
2166 log.debug('IP %s is network %s' %
2149 (source_ip_address, network_address))
2167 (source_ip_address, network_address))
2150 return True
2168 return True
2151 # for any case we cannot determine the IP, don't crash just
2169 # for any case we cannot determine the IP, don't crash just
2152 # skip it and log as error, we want to say forbidden still when
2170 # skip it and log as error, we want to say forbidden still when
2153 # sending bad IP
2171 # sending bad IP
2154 except Exception:
2172 except Exception:
2155 log.error(traceback.format_exc())
2173 log.error(traceback.format_exc())
2156 continue
2174 continue
2157 return False
2175 return False
2158
2176
2159
2177
2160 def get_cython_compat_decorator(wrapper, func):
2178 def get_cython_compat_decorator(wrapper, func):
2161 """
2179 """
2162 Creates a cython compatible decorator. The previously used
2180 Creates a cython compatible decorator. The previously used
2163 decorator.decorator() function seems to be incompatible with cython.
2181 decorator.decorator() function seems to be incompatible with cython.
2164
2182
2165 :param wrapper: __wrapper method of the decorator class
2183 :param wrapper: __wrapper method of the decorator class
2166 :param func: decorated function
2184 :param func: decorated function
2167 """
2185 """
2168 @wraps(func)
2186 @wraps(func)
2169 def local_wrapper(*args, **kwds):
2187 def local_wrapper(*args, **kwds):
2170 return wrapper(func, *args, **kwds)
2188 return wrapper(func, *args, **kwds)
2171 local_wrapper.__wrapped__ = func
2189 local_wrapper.__wrapped__ = func
2172 return local_wrapper
2190 return local_wrapper
2173
2191
2174
2192
General Comments 0
You need to be logged in to leave comments. Login now