Show More
@@ -209,18 +209,18 b' def request_view(request):' | |||||
209 | request.rpc_user = auth_u |
|
209 | request.rpc_user = auth_u | |
210 |
|
210 | |||
211 | # now check if token is valid for API |
|
211 | # now check if token is valid for API | |
212 | role = UserApiKeys.ROLE_API |
|
212 | auth_token = request.rpc_api_key | |
213 | extra_auth_tokens = [ |
|
213 | token_match = api_user.authenticate_by_token( | |
214 | x.api_key for x in User.extra_valid_auth_tokens(api_user, role=role)] |
|
214 | auth_token, roles=[UserApiKeys.ROLE_API], include_builtin_token=True) | |
215 | active_tokens = [api_user.api_key] + extra_auth_tokens |
|
215 | invalid_token = not token_match | |
216 |
|
216 | |||
217 |
log.debug('Checking if API |
|
217 | log.debug('Checking if API KEY is valid with proper role') | |
218 | if request.rpc_api_key not in active_tokens: |
|
218 | if invalid_token: | |
219 | return jsonrpc_error( |
|
219 | return jsonrpc_error( | |
220 | request, retid=request.rpc_id, |
|
220 | request, retid=request.rpc_id, | |
221 | message='API KEY has bad role for an API call') |
|
221 | message='API KEY invalid or, has bad role for an API call') | |
222 |
|
222 | |||
223 |
except Exception |
|
223 | except Exception: | |
224 | log.exception('Error on API AUTH') |
|
224 | log.exception('Error on API AUTH') | |
225 | return jsonrpc_error( |
|
225 | return jsonrpc_error( | |
226 | request, retid=request.rpc_id, message='Invalid API KEY') |
|
226 | request, retid=request.rpc_id, message='Invalid API KEY') |
@@ -122,10 +122,10 b' class RhodeCodeAuthPlugin(RhodeCodeAuthP' | |||||
122 |
|
122 | |||
123 | log.debug('Authenticating user with args %s', user_attrs) |
|
123 | log.debug('Authenticating user with args %s', user_attrs) | |
124 | if userobj.active: |
|
124 | if userobj.active: | |
125 | role = UserApiKeys.ROLE_VCS |
|
125 | token_match = userobj.authenticate_by_token( | |
126 | active_tokens = [x.api_key for x in |
|
126 | password, roles=[UserApiKeys.ROLE_VCS]) | |
127 | User.extra_valid_auth_tokens(userobj, role=role)] |
|
127 | ||
128 |
if userobj.username == username and |
|
128 | if userobj.username == username and token_match: | |
129 | log.info( |
|
129 | log.info( | |
130 | 'user `%s` successfully authenticated via %s', |
|
130 | 'user `%s` successfully authenticated via %s', | |
131 | user_attrs['username'], self.name) |
|
131 | user_attrs['username'], self.name) |
@@ -28,12 +28,11 b' import pytz' | |||||
28 | from pylons import url, response, tmpl_context as c |
|
28 | from pylons import url, response, tmpl_context as c | |
29 | from pylons.i18n.translation import _ |
|
29 | from pylons.i18n.translation import _ | |
30 |
|
30 | |||
31 |
from beaker.cache import cache_region |
|
31 | from beaker.cache import cache_region | |
32 | from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed |
|
32 | from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed | |
33 |
|
33 | |||
34 | from rhodecode.model.db import CacheKey |
|
34 | from rhodecode.model.db import CacheKey, UserApiKeys | |
35 | from rhodecode.lib import helpers as h |
|
35 | from rhodecode.lib import helpers as h | |
36 | from rhodecode.lib import caches |
|
|||
37 | from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator |
|
36 | from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator | |
38 | from rhodecode.lib.base import BaseRepoController |
|
37 | from rhodecode.lib.base import BaseRepoController | |
39 | from rhodecode.lib.diffs import DiffProcessor, LimitedDiffContainer |
|
38 | from rhodecode.lib.diffs import DiffProcessor, LimitedDiffContainer | |
@@ -62,7 +61,7 b' class FeedController(BaseRepoController)' | |||||
62 | safe_int(config.get('rss_cut_off_limit', 32 * 1024)), |
|
61 | safe_int(config.get('rss_cut_off_limit', 32 * 1024)), | |
63 | } |
|
62 | } | |
64 |
|
63 | |||
65 |
@LoginRequired(auth_token_access= |
|
64 | @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED]) | |
66 | def __before__(self): |
|
65 | def __before__(self): | |
67 | super(FeedController, self).__before__() |
|
66 | super(FeedController, self).__before__() | |
68 | config = self._get_config() |
|
67 | config = self._get_config() |
@@ -35,7 +35,7 b' from pylons import request, tmpl_context' | |||||
35 | from pylons.i18n.translation import _ |
|
35 | from pylons.i18n.translation import _ | |
36 |
|
36 | |||
37 | from rhodecode.controllers.admin.admin import _journal_filter |
|
37 | from rhodecode.controllers.admin.admin import _journal_filter | |
38 | from rhodecode.model.db import UserLog, UserFollowing, User |
|
38 | from rhodecode.model.db import UserLog, UserFollowing, User, UserApiKeys | |
39 | from rhodecode.model.meta import Session |
|
39 | from rhodecode.model.meta import Session | |
40 | import rhodecode.lib.helpers as h |
|
40 | import rhodecode.lib.helpers as h | |
41 | from rhodecode.lib.helpers import Page |
|
41 | from rhodecode.lib.helpers import Page | |
@@ -211,7 +211,7 b' class JournalController(BaseController):' | |||||
211 |
|
211 | |||
212 | return render('journal/journal.mako') |
|
212 | return render('journal/journal.mako') | |
213 |
|
213 | |||
214 |
@LoginRequired(auth_token_access= |
|
214 | @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED]) | |
215 | @NotAnonymous() |
|
215 | @NotAnonymous() | |
216 | def journal_atom(self): |
|
216 | def journal_atom(self): | |
217 | """ |
|
217 | """ | |
@@ -223,7 +223,7 b' class JournalController(BaseController):' | |||||
223 | .all() |
|
223 | .all() | |
224 | return self._atom_feed(following, public=False) |
|
224 | return self._atom_feed(following, public=False) | |
225 |
|
225 | |||
226 |
@LoginRequired(auth_token_access= |
|
226 | @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED]) | |
227 | @NotAnonymous() |
|
227 | @NotAnonymous() | |
228 | def journal_rss(self): |
|
228 | def journal_rss(self): | |
229 | """ |
|
229 | """ | |
@@ -281,7 +281,7 b' class JournalController(BaseController):' | |||||
281 | return c.journal_data |
|
281 | return c.journal_data | |
282 | return render('journal/public_journal.mako') |
|
282 | return render('journal/public_journal.mako') | |
283 |
|
283 | |||
284 |
@LoginRequired(auth_token_access= |
|
284 | @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED]) | |
285 | def public_journal_atom(self): |
|
285 | def public_journal_atom(self): | |
286 | """ |
|
286 | """ | |
287 | Produce an atom-1.0 feed via feedgenerator module |
|
287 | Produce an atom-1.0 feed via feedgenerator module | |
@@ -293,7 +293,7 b' class JournalController(BaseController):' | |||||
293 |
|
293 | |||
294 | return self._atom_feed(c.following) |
|
294 | return self._atom_feed(c.following) | |
295 |
|
295 | |||
296 |
@LoginRequired(auth_token_access= |
|
296 | @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED]) | |
297 | def public_journal_rss(self): |
|
297 | def public_journal_rss(self): | |
298 | """ |
|
298 | """ | |
299 | Produce an rss2 feed via feedgenerator module |
|
299 | Produce an rss2 feed via feedgenerator module |
@@ -99,6 +99,7 b' class PasswordGenerator(object):' | |||||
99 |
|
99 | |||
100 |
|
100 | |||
101 | class _RhodeCodeCryptoBase(object): |
|
101 | class _RhodeCodeCryptoBase(object): | |
|
102 | ENC_PREF = None | |||
102 |
|
103 | |||
103 | def hash_create(self, str_): |
|
104 | def hash_create(self, str_): | |
104 | """ |
|
105 | """ | |
@@ -139,6 +140,7 b' class _RhodeCodeCryptoBase(object):' | |||||
139 |
|
140 | |||
140 |
|
141 | |||
141 | class _RhodeCodeCryptoBCrypt(_RhodeCodeCryptoBase): |
|
142 | class _RhodeCodeCryptoBCrypt(_RhodeCodeCryptoBase): | |
|
143 | ENC_PREF = '$2a$10' | |||
142 |
|
144 | |||
143 | def hash_create(self, str_): |
|
145 | def hash_create(self, str_): | |
144 | self._assert_bytes(str_) |
|
146 | self._assert_bytes(str_) | |
@@ -194,6 +196,7 b' class _RhodeCodeCryptoBCrypt(_RhodeCodeC' | |||||
194 |
|
196 | |||
195 |
|
197 | |||
196 | class _RhodeCodeCryptoSha256(_RhodeCodeCryptoBase): |
|
198 | class _RhodeCodeCryptoSha256(_RhodeCodeCryptoBase): | |
|
199 | ENC_PREF = '_' | |||
197 |
|
200 | |||
198 | def hash_create(self, str_): |
|
201 | def hash_create(self, str_): | |
199 | self._assert_bytes(str_) |
|
202 | self._assert_bytes(str_) | |
@@ -211,6 +214,7 b' class _RhodeCodeCryptoSha256(_RhodeCodeC' | |||||
211 |
|
214 | |||
212 |
|
215 | |||
213 | class _RhodeCodeCryptoMd5(_RhodeCodeCryptoBase): |
|
216 | class _RhodeCodeCryptoMd5(_RhodeCodeCryptoBase): | |
|
217 | ENC_PREF = '_' | |||
214 |
|
218 | |||
215 | def hash_create(self, str_): |
|
219 | def hash_create(self, str_): | |
216 | self._assert_bytes(str_) |
|
220 | self._assert_bytes(str_) | |
@@ -831,10 +835,6 b' class AuthUser(object):' | |||||
831 | self._permissions_scoped_cache[cache_key] = res |
|
835 | self._permissions_scoped_cache[cache_key] = res | |
832 | return self._permissions_scoped_cache[cache_key] |
|
836 | return self._permissions_scoped_cache[cache_key] | |
833 |
|
837 | |||
834 | @property |
|
|||
835 | def auth_tokens(self): |
|
|||
836 | return self.get_auth_tokens() |
|
|||
837 |
|
||||
838 | def get_instance(self): |
|
838 | def get_instance(self): | |
839 | return User.get(self.user_id) |
|
839 | return User.get(self.user_id) | |
840 |
|
840 | |||
@@ -925,16 +925,6 b' class AuthUser(object):' | |||||
925 | log.debug('PERMISSION tree computed %s' % (result_repr,)) |
|
925 | log.debug('PERMISSION tree computed %s' % (result_repr,)) | |
926 | return result |
|
926 | return result | |
927 |
|
927 | |||
928 | def get_auth_tokens(self): |
|
|||
929 | auth_tokens = [self.api_key] |
|
|||
930 | for api_key in UserApiKeys.query()\ |
|
|||
931 | .filter(UserApiKeys.user_id == self.user_id)\ |
|
|||
932 | .filter(or_(UserApiKeys.expires == -1, |
|
|||
933 | UserApiKeys.expires >= time.time())).all(): |
|
|||
934 | auth_tokens.append(api_key.api_key) |
|
|||
935 |
|
||||
936 | return auth_tokens |
|
|||
937 |
|
||||
938 | @property |
|
928 | @property | |
939 | def is_default(self): |
|
929 | def is_default(self): | |
940 | return self.username == User.DEFAULT_USER |
|
930 | return self.username == User.DEFAULT_USER | |
@@ -1171,7 +1161,7 b' class LoginRequired(object):' | |||||
1171 | :param api_access: if enabled this checks only for valid auth token |
|
1161 | :param api_access: if enabled this checks only for valid auth token | |
1172 | and grants access based on valid token |
|
1162 | and grants access based on valid token | |
1173 | """ |
|
1163 | """ | |
1174 |
def __init__(self, auth_token_access= |
|
1164 | def __init__(self, auth_token_access=None): | |
1175 | self.auth_token_access = auth_token_access |
|
1165 | self.auth_token_access = auth_token_access | |
1176 |
|
1166 | |||
1177 | def __call__(self, func): |
|
1167 | def __call__(self, func): | |
@@ -1191,7 +1181,7 b' class LoginRequired(object):' | |||||
1191 | ip_access_valid = False |
|
1181 | ip_access_valid = False | |
1192 |
|
1182 | |||
1193 | # check if we used an APIKEY and it's a valid one |
|
1183 | # check if we used an APIKEY and it's a valid one | |
1194 | # defined whitelist of controllers which API access will be enabled |
|
1184 | # defined white-list of controllers which API access will be enabled | |
1195 | _auth_token = request.GET.get( |
|
1185 | _auth_token = request.GET.get( | |
1196 | 'auth_token', '') or request.GET.get('api_key', '') |
|
1186 | 'auth_token', '') or request.GET.get('api_key', '') | |
1197 | auth_token_access_valid = allowed_auth_token_access( |
|
1187 | auth_token_access_valid = allowed_auth_token_access( | |
@@ -1200,8 +1190,20 b' class LoginRequired(object):' | |||||
1200 | # explicit controller is enabled or API is in our whitelist |
|
1190 | # explicit controller is enabled or API is in our whitelist | |
1201 | if self.auth_token_access or auth_token_access_valid: |
|
1191 | if self.auth_token_access or auth_token_access_valid: | |
1202 | log.debug('Checking AUTH TOKEN access for %s' % (cls,)) |
|
1192 | log.debug('Checking AUTH TOKEN access for %s' % (cls,)) | |
|
1193 | db_user = user.get_instance() | |||
1203 |
|
1194 | |||
1204 | if _auth_token and _auth_token in user.auth_tokens: |
|
1195 | if db_user: | |
|
1196 | if self.auth_token_access: | |||
|
1197 | roles = self.auth_token_access | |||
|
1198 | else: | |||
|
1199 | roles = [UserApiKeys.ROLE_HTTP] | |||
|
1200 | token_match = db_user.authenticate_by_token( | |||
|
1201 | _auth_token, roles=roles, include_builtin_token=True) | |||
|
1202 | else: | |||
|
1203 | log.debug('Unable to fetch db instance for auth user: %s', user) | |||
|
1204 | token_match = False | |||
|
1205 | ||||
|
1206 | if _auth_token and token_match: | |||
1205 | auth_token_access_valid = True |
|
1207 | auth_token_access_valid = True | |
1206 | log.debug('AUTH TOKEN ****%s is VALID' % (_auth_token[-4:],)) |
|
1208 | log.debug('AUTH TOKEN ****%s is VALID' % (_auth_token[-4:],)) | |
1207 | else: |
|
1209 | else: |
@@ -581,15 +581,16 b' class User(Base, BaseModel):' | |||||
581 |
|
581 | |||
582 | @property |
|
582 | @property | |
583 | def feed_token(self): |
|
583 | def feed_token(self): | |
|
584 | return self.get_feed_token() | |||
|
585 | ||||
|
586 | def get_feed_token(self): | |||
584 | feed_tokens = UserApiKeys.query()\ |
|
587 | feed_tokens = UserApiKeys.query()\ | |
585 | .filter(UserApiKeys.user == self)\ |
|
588 | .filter(UserApiKeys.user == self)\ | |
586 | .filter(UserApiKeys.role == UserApiKeys.ROLE_FEED)\ |
|
589 | .filter(UserApiKeys.role == UserApiKeys.ROLE_FEED)\ | |
587 | .all() |
|
590 | .all() | |
588 | if feed_tokens: |
|
591 | if feed_tokens: | |
589 | return feed_tokens[0].api_key |
|
592 | return feed_tokens[0].api_key | |
590 | else: |
|
593 | return 'NO_FEED_TOKEN_AVAILABLE' | |
591 | # use the main token so we don't end up with nothing... |
|
|||
592 | return self.api_key |
|
|||
593 |
|
594 | |||
594 | @classmethod |
|
595 | @classmethod | |
595 | def extra_valid_auth_tokens(cls, user, role=None): |
|
596 | def extra_valid_auth_tokens(cls, user, role=None): | |
@@ -601,11 +602,57 b' class User(Base, BaseModel):' | |||||
601 | UserApiKeys.role == UserApiKeys.ROLE_ALL)) |
|
602 | UserApiKeys.role == UserApiKeys.ROLE_ALL)) | |
602 | return tokens.all() |
|
603 | return tokens.all() | |
603 |
|
604 | |||
|
605 | def authenticate_by_token(self, auth_token, roles=None, | |||
|
606 | include_builtin_token=False): | |||
|
607 | from rhodecode.lib import auth | |||
|
608 | ||||
|
609 | log.debug('Trying to authenticate user: %s via auth-token, ' | |||
|
610 | 'and roles: %s', self, roles) | |||
|
611 | ||||
|
612 | if not auth_token: | |||
|
613 | return False | |||
|
614 | ||||
|
615 | crypto_backend = auth.crypto_backend() | |||
|
616 | ||||
|
617 | roles = (roles or []) + [UserApiKeys.ROLE_ALL] | |||
|
618 | tokens_q = UserApiKeys.query()\ | |||
|
619 | .filter(UserApiKeys.user_id == self.user_id)\ | |||
|
620 | .filter(or_(UserApiKeys.expires == -1, | |||
|
621 | UserApiKeys.expires >= time.time())) | |||
|
622 | ||||
|
623 | tokens_q = tokens_q.filter(UserApiKeys.role.in_(roles)) | |||
|
624 | ||||
|
625 | maybe_builtin = [] | |||
|
626 | if include_builtin_token: | |||
|
627 | maybe_builtin = [AttributeDict({'api_key': self.api_key})] | |||
|
628 | ||||
|
629 | plain_tokens = [] | |||
|
630 | hash_tokens = [] | |||
|
631 | ||||
|
632 | for token in tokens_q.all() + maybe_builtin: | |||
|
633 | if token.api_key.startswith(crypto_backend.ENC_PREF): | |||
|
634 | hash_tokens.append(token.api_key) | |||
|
635 | else: | |||
|
636 | plain_tokens.append(token.api_key) | |||
|
637 | ||||
|
638 | is_plain_match = auth_token in plain_tokens | |||
|
639 | if is_plain_match: | |||
|
640 | return True | |||
|
641 | ||||
|
642 | for hashed in hash_tokens: | |||
|
643 | # marcink: this is expensive to calculate, but the most secure | |||
|
644 | match = crypto_backend.hash_check(auth_token, hashed) | |||
|
645 | if match: | |||
|
646 | return True | |||
|
647 | ||||
|
648 | return False | |||
|
649 | ||||
604 | @property |
|
650 | @property | |
605 | def builtin_token_roles(self): |
|
651 | def builtin_token_roles(self): | |
606 | return map(UserApiKeys._get_role_name, [ |
|
652 | roles = [ | |
607 | UserApiKeys.ROLE_API, UserApiKeys.ROLE_FEED, UserApiKeys.ROLE_HTTP |
|
653 | UserApiKeys.ROLE_API, UserApiKeys.ROLE_FEED, UserApiKeys.ROLE_HTTP | |
608 |
] |
|
654 | ] | |
|
655 | return map(UserApiKeys._get_role_name, roles) | |||
609 |
|
656 | |||
610 | @property |
|
657 | @property | |
611 | def ip_addresses(self): |
|
658 | def ip_addresses(self): |
@@ -17,7 +17,7 b'' | |||||
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 | from rhodecode.model.auth_token import AuthTokenModel | ||
21 | from rhodecode.model.db import User |
|
21 | from rhodecode.model.db import User | |
22 | from rhodecode.tests import * |
|
22 | from rhodecode.tests import * | |
23 |
|
23 | |||
@@ -32,15 +32,36 b' class TestFeedController(TestController)' | |||||
32 | assert response.content_type == "application/rss+xml" |
|
32 | assert response.content_type == "application/rss+xml" | |
33 | assert """<rss version="2.0">""" in response |
|
33 | assert """<rss version="2.0">""" in response | |
34 |
|
34 | |||
35 | def test_rss_with_auth_token(self, backend): |
|
35 | def test_rss_with_auth_token(self, backend, user_admin): | |
36 |
auth_token = |
|
36 | auth_token = user_admin.feed_token | |
37 | assert auth_token != '' |
|
37 | assert auth_token != '' | |
38 |
response = self.app.get( |
|
38 | response = self.app.get( | |
39 | repo_name=backend.repo_name, auth_token=auth_token)) |
|
39 | url(controller='feed', action='rss', | |
|
40 | repo_name=backend.repo_name, auth_token=auth_token, | |||
|
41 | status=200)) | |||
40 |
|
42 | |||
41 | assert response.content_type == "application/rss+xml" |
|
43 | assert response.content_type == "application/rss+xml" | |
42 | assert """<rss version="2.0">""" in response |
|
44 | assert """<rss version="2.0">""" in response | |
43 |
|
45 | |||
|
46 | def test_rss_with_auth_token_of_wrong_type(self, backend, user_util): | |||
|
47 | user = user_util.create_user() | |||
|
48 | auth_token = AuthTokenModel().create( | |||
|
49 | user.user_id, 'test-token', -1, AuthTokenModel.cls.ROLE_API) | |||
|
50 | auth_token = auth_token.api_key | |||
|
51 | ||||
|
52 | self.app.get( | |||
|
53 | url(controller='feed', action='rss', | |||
|
54 | repo_name=backend.repo_name, auth_token=auth_token), | |||
|
55 | status=302) | |||
|
56 | ||||
|
57 | auth_token = AuthTokenModel().create( | |||
|
58 | user.user_id, 'test-token', -1, AuthTokenModel.cls.ROLE_FEED) | |||
|
59 | auth_token = auth_token.api_key | |||
|
60 | self.app.get( | |||
|
61 | url(controller='feed', action='rss', | |||
|
62 | repo_name=backend.repo_name, auth_token=auth_token), | |||
|
63 | status=200) | |||
|
64 | ||||
44 | def test_atom(self, backend): |
|
65 | def test_atom(self, backend): | |
45 | self.log_user() |
|
66 | self.log_user() | |
46 | response = self.app.get(url(controller='feed', action='atom', |
|
67 | response = self.app.get(url(controller='feed', action='atom', |
@@ -443,14 +443,15 b' class TestLoginController:' | |||||
443 | ('fake_number', '123456'), |
|
443 | ('fake_number', '123456'), | |
444 | ('proper_auth_token', None) |
|
444 | ('proper_auth_token', None) | |
445 | ]) |
|
445 | ]) | |
446 |
def test_access_not_whitelisted_page_via_auth_token( |
|
446 | def test_access_not_whitelisted_page_via_auth_token( | |
447 | auth_token): |
|
447 | self, test_name, auth_token, user_admin): | |
|
448 | ||||
448 | whitelist = self._get_api_whitelist([]) |
|
449 | whitelist = self._get_api_whitelist([]) | |
449 | with mock.patch.dict('rhodecode.CONFIG', whitelist): |
|
450 | with mock.patch.dict('rhodecode.CONFIG', whitelist): | |
450 | assert [] == whitelist['api_access_controllers_whitelist'] |
|
451 | assert [] == whitelist['api_access_controllers_whitelist'] | |
451 | if test_name == 'proper_auth_token': |
|
452 | if test_name == 'proper_auth_token': | |
452 | # use builtin if api_key is None |
|
453 | # use builtin if api_key is None | |
453 |
auth_token = |
|
454 | auth_token = user_admin.api_key | |
454 |
|
455 | |||
455 | with fixture.anon_access(False): |
|
456 | with fixture.anon_access(False): | |
456 | self.app.get(url(controller='changeset', |
|
457 | self.app.get(url(controller='changeset', | |
@@ -465,15 +466,17 b' class TestLoginController:' | |||||
465 | ('fake_number', '123456', 302), |
|
466 | ('fake_number', '123456', 302), | |
466 | ('proper_auth_token', None, 200) |
|
467 | ('proper_auth_token', None, 200) | |
467 | ]) |
|
468 | ]) | |
468 |
def test_access_whitelisted_page_via_auth_token( |
|
469 | def test_access_whitelisted_page_via_auth_token( | |
469 | auth_token, code): |
|
470 | self, test_name, auth_token, code, user_admin): | |
470 | whitelist = self._get_api_whitelist( |
|
471 | ||
471 |
|
|
472 | whitelist_entry = ['ChangesetController:changeset_raw'] | |
|
473 | whitelist = self._get_api_whitelist(whitelist_entry) | |||
|
474 | ||||
472 | with mock.patch.dict('rhodecode.CONFIG', whitelist): |
|
475 | with mock.patch.dict('rhodecode.CONFIG', whitelist): | |
473 | assert ['ChangesetController:changeset_raw'] == \ |
|
476 | assert whitelist_entry == whitelist['api_access_controllers_whitelist'] | |
474 | whitelist['api_access_controllers_whitelist'] |
|
477 | ||
475 | if test_name == 'proper_auth_token': |
|
478 | if test_name == 'proper_auth_token': | |
476 |
auth_token = |
|
479 | auth_token = user_admin.api_key | |
477 |
|
480 | |||
478 | with fixture.anon_access(False): |
|
481 | with fixture.anon_access(False): | |
479 | self.app.get(url(controller='changeset', |
|
482 | self.app.get(url(controller='changeset', |
@@ -26,6 +26,7 b' from mock import patch' | |||||
26 |
|
26 | |||
27 | from rhodecode.lib import auth |
|
27 | from rhodecode.lib import auth | |
28 | from rhodecode.lib.utils2 import md5 |
|
28 | from rhodecode.lib.utils2 import md5 | |
|
29 | from rhodecode.model.auth_token import AuthTokenModel | |||
29 | from rhodecode.model.db import User |
|
30 | from rhodecode.model.db import User | |
30 | from rhodecode.model.repo import RepoModel |
|
31 | from rhodecode.model.repo import RepoModel | |
31 | from rhodecode.model.user import UserModel |
|
32 | from rhodecode.model.user import UserModel | |
@@ -580,3 +581,28 b' class TestGenerateAuthToken(object):' | |||||
580 | result = auth.generate_auth_token(user_name) |
|
581 | result = auth.generate_auth_token(user_name) | |
581 | expected_result = sha1(user_name + random_salt).hexdigest() |
|
582 | expected_result = sha1(user_name + random_salt).hexdigest() | |
582 | assert result == expected_result |
|
583 | assert result == expected_result | |
|
584 | ||||
|
585 | ||||
|
586 | @pytest.mark.parametrize("test_token, test_roles, auth_result, expected_tokens", [ | |||
|
587 | ('', None, False, | |||
|
588 | []), | |||
|
589 | ('wrongtoken', None, False, | |||
|
590 | []), | |||
|
591 | ('abracadabra_vcs', [AuthTokenModel.cls.ROLE_API], False, | |||
|
592 | [('abracadabra_api', AuthTokenModel.cls.ROLE_API, -1)]), | |||
|
593 | ('abracadabra_api', [AuthTokenModel.cls.ROLE_API], True, | |||
|
594 | [('abracadabra_api', AuthTokenModel.cls.ROLE_API, -1)]), | |||
|
595 | ('abracadabra_api', [AuthTokenModel.cls.ROLE_API], True, | |||
|
596 | [('abracadabra_api', AuthTokenModel.cls.ROLE_API, -1), | |||
|
597 | ('abracadabra_http', AuthTokenModel.cls.ROLE_HTTP, -1)]), | |||
|
598 | ]) | |||
|
599 | def test_auth_by_token(test_token, test_roles, auth_result, expected_tokens, | |||
|
600 | user_util): | |||
|
601 | user = user_util.create_user() | |||
|
602 | user_id = user.user_id | |||
|
603 | for token, role, expires in expected_tokens: | |||
|
604 | new_token = AuthTokenModel().create(user_id, 'test-token', expires, role) | |||
|
605 | new_token.api_key = token # inject known name for testing... | |||
|
606 | ||||
|
607 | assert auth_result == user.authenticate_by_token( | |||
|
608 | test_token, roles=test_roles, include_builtin_token=True) |
General Comments 0
You need to be logged in to leave comments.
Login now