Show More
@@ -0,0 +1,39 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | ||||
|
3 | # Copyright (C) 2016-2017 RhodeCode GmbH | |||
|
4 | # | |||
|
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 | |||
|
7 | # (only), as published by the Free Software Foundation. | |||
|
8 | # | |||
|
9 | # This program is distributed in the hope that it will be useful, | |||
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
12 | # GNU General Public License for more details. | |||
|
13 | # | |||
|
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/>. | |||
|
16 | # | |||
|
17 | # This program is dual-licensed. If you wish to learn more about the | |||
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |||
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |||
|
20 | ||||
|
21 | ||||
|
22 | from rhodecode.apps._base import ADMIN_PREFIX | |||
|
23 | ||||
|
24 | ||||
|
25 | def includeme(config): | |||
|
26 | config.add_route( | |||
|
27 | name='my_account_auth_tokens', | |||
|
28 | pattern=ADMIN_PREFIX + '/my_account/auth_tokens') | |||
|
29 | config.add_route( | |||
|
30 | name='my_account_auth_tokens_add', | |||
|
31 | pattern=ADMIN_PREFIX + '/my_account/auth_tokens/new', | |||
|
32 | ) | |||
|
33 | config.add_route( | |||
|
34 | name='my_account_auth_tokens_delete', | |||
|
35 | pattern=ADMIN_PREFIX + '/my_account/auth_tokens/delete', | |||
|
36 | ) | |||
|
37 | ||||
|
38 | # Scan module for configuration decorators. | |||
|
39 | config.scan() |
@@ -0,0 +1,19 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | ||||
|
3 | # Copyright (C) 2016-2017 RhodeCode GmbH | |||
|
4 | # | |||
|
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 | |||
|
7 | # (only), as published by the Free Software Foundation. | |||
|
8 | # | |||
|
9 | # This program is distributed in the hope that it will be useful, | |||
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
12 | # GNU General Public License for more details. | |||
|
13 | # | |||
|
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/>. | |||
|
16 | # | |||
|
17 | # This program is dual-licensed. If you wish to learn more about the | |||
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |||
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
@@ -0,0 +1,111 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | ||||
|
3 | # Copyright (C) 2010-2017 RhodeCode GmbH | |||
|
4 | # | |||
|
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 | |||
|
7 | # (only), as published by the Free Software Foundation. | |||
|
8 | # | |||
|
9 | # This program is distributed in the hope that it will be useful, | |||
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
12 | # GNU General Public License for more details. | |||
|
13 | # | |||
|
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/>. | |||
|
16 | # | |||
|
17 | # This program is dual-licensed. If you wish to learn more about the | |||
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |||
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |||
|
20 | ||||
|
21 | import pytest | |||
|
22 | ||||
|
23 | from rhodecode.apps._base import ADMIN_PREFIX | |||
|
24 | from rhodecode.model.db import User | |||
|
25 | from rhodecode.tests import ( | |||
|
26 | TestController, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS, | |||
|
27 | TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, assert_session_flash) | |||
|
28 | from rhodecode.tests.fixture import Fixture | |||
|
29 | from rhodecode.tests.utils import AssertResponse | |||
|
30 | ||||
|
31 | fixture = Fixture() | |||
|
32 | ||||
|
33 | ||||
|
34 | def route_path(name, **kwargs): | |||
|
35 | return { | |||
|
36 | 'my_account_auth_tokens': | |||
|
37 | ADMIN_PREFIX + '/my_account/auth_tokens', | |||
|
38 | 'my_account_auth_tokens_add': | |||
|
39 | ADMIN_PREFIX + '/my_account/auth_tokens/new', | |||
|
40 | 'my_account_auth_tokens_delete': | |||
|
41 | ADMIN_PREFIX + '/my_account/auth_tokens/delete', | |||
|
42 | }[name].format(**kwargs) | |||
|
43 | ||||
|
44 | ||||
|
45 | class TestMyAccountAuthTokens(TestController): | |||
|
46 | ||||
|
47 | def test_my_account_auth_tokens(self): | |||
|
48 | usr = self.log_user('test_regular2', 'test12') | |||
|
49 | user = User.get(usr['user_id']) | |||
|
50 | response = self.app.get(route_path('my_account_auth_tokens')) | |||
|
51 | for token in user.auth_tokens: | |||
|
52 | response.mustcontain(token) | |||
|
53 | response.mustcontain('never') | |||
|
54 | ||||
|
55 | def test_my_account_add_auth_tokens_wrong_csrf(self, user_util): | |||
|
56 | user = user_util.create_user(password='qweqwe') | |||
|
57 | self.log_user(user.username, 'qweqwe') | |||
|
58 | ||||
|
59 | self.app.post( | |||
|
60 | route_path('my_account_auth_tokens_add'), | |||
|
61 | {'description': 'desc', 'lifetime': -1}, status=403) | |||
|
62 | ||||
|
63 | @pytest.mark.parametrize("desc, lifetime", [ | |||
|
64 | ('forever', -1), | |||
|
65 | ('5mins', 60*5), | |||
|
66 | ('30days', 60*60*24*30), | |||
|
67 | ]) | |||
|
68 | def test_my_account_add_auth_tokens(self, desc, lifetime, user_util): | |||
|
69 | user = user_util.create_user(password='qweqwe') | |||
|
70 | user_id = user.user_id | |||
|
71 | self.log_user(user.username, 'qweqwe') | |||
|
72 | ||||
|
73 | response = self.app.post( | |||
|
74 | route_path('my_account_auth_tokens_add'), | |||
|
75 | {'description': desc, 'lifetime': lifetime, | |||
|
76 | 'csrf_token': self.csrf_token}) | |||
|
77 | assert_session_flash(response, 'Auth token successfully created') | |||
|
78 | ||||
|
79 | response = response.follow() | |||
|
80 | user = User.get(user_id) | |||
|
81 | for auth_token in user.auth_tokens: | |||
|
82 | response.mustcontain(auth_token) | |||
|
83 | ||||
|
84 | def test_my_account_delete_auth_token(self, user_util): | |||
|
85 | user = user_util.create_user(password='qweqwe') | |||
|
86 | user_id = user.user_id | |||
|
87 | self.log_user(user.username, 'qweqwe') | |||
|
88 | ||||
|
89 | user = User.get(user_id) | |||
|
90 | keys = user.extra_auth_tokens | |||
|
91 | assert 2 == len(keys) | |||
|
92 | ||||
|
93 | response = self.app.post( | |||
|
94 | route_path('my_account_auth_tokens_add'), | |||
|
95 | {'description': 'desc', 'lifetime': -1, | |||
|
96 | 'csrf_token': self.csrf_token}) | |||
|
97 | assert_session_flash(response, 'Auth token successfully created') | |||
|
98 | response.follow() | |||
|
99 | ||||
|
100 | user = User.get(user_id) | |||
|
101 | keys = user.extra_auth_tokens | |||
|
102 | assert 3 == len(keys) | |||
|
103 | ||||
|
104 | response = self.app.post( | |||
|
105 | route_path('my_account_auth_tokens_delete'), | |||
|
106 | {'del_auth_token': keys[0].api_key, 'csrf_token': self.csrf_token}) | |||
|
107 | assert_session_flash(response, 'Auth token successfully deleted') | |||
|
108 | ||||
|
109 | user = User.get(user_id) | |||
|
110 | keys = user.extra_auth_tokens | |||
|
111 | assert 2 == len(keys) |
@@ -0,0 +1,111 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | ||||
|
3 | # Copyright (C) 2016-2017 RhodeCode GmbH | |||
|
4 | # | |||
|
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 | |||
|
7 | # (only), as published by the Free Software Foundation. | |||
|
8 | # | |||
|
9 | # This program is distributed in the hope that it will be useful, | |||
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
12 | # GNU General Public License for more details. | |||
|
13 | # | |||
|
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/>. | |||
|
16 | # | |||
|
17 | # This program is dual-licensed. If you wish to learn more about the | |||
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |||
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |||
|
20 | ||||
|
21 | import logging | |||
|
22 | ||||
|
23 | from pyramid.httpexceptions import HTTPFound | |||
|
24 | from pyramid.view import view_config | |||
|
25 | ||||
|
26 | from rhodecode.apps._base import BaseAppView | |||
|
27 | from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired | |||
|
28 | from rhodecode.lib.utils2 import safe_int | |||
|
29 | from rhodecode.lib import helpers as h | |||
|
30 | from rhodecode.model.auth_token import AuthTokenModel | |||
|
31 | from rhodecode.model.meta import Session | |||
|
32 | ||||
|
33 | log = logging.getLogger(__name__) | |||
|
34 | ||||
|
35 | ||||
|
36 | class MyAccountView(BaseAppView): | |||
|
37 | ||||
|
38 | def load_default_context(self): | |||
|
39 | c = self._get_local_tmpl_context() | |||
|
40 | ||||
|
41 | c.auth_user = self.request.user | |||
|
42 | c.user = c.auth_user.get_instance() | |||
|
43 | ||||
|
44 | self._register_global_c(c) | |||
|
45 | return c | |||
|
46 | ||||
|
47 | @LoginRequired() | |||
|
48 | @NotAnonymous() | |||
|
49 | @view_config( | |||
|
50 | route_name='my_account_auth_tokens', request_method='GET', | |||
|
51 | renderer='rhodecode:templates/admin/my_account/my_account.mako') | |||
|
52 | def my_account_auth_tokens(self): | |||
|
53 | _ = self.request.translate | |||
|
54 | ||||
|
55 | c = self.load_default_context() | |||
|
56 | c.active = 'auth_tokens' | |||
|
57 | ||||
|
58 | show_expired = True | |||
|
59 | ||||
|
60 | c.lifetime_values = [ | |||
|
61 | (str(-1), _('forever')), | |||
|
62 | (str(5), _('5 minutes')), | |||
|
63 | (str(60), _('1 hour')), | |||
|
64 | (str(60 * 24), _('1 day')), | |||
|
65 | (str(60 * 24 * 30), _('1 month')), | |||
|
66 | ] | |||
|
67 | c.lifetime_options = [(c.lifetime_values, _("Lifetime"))] | |||
|
68 | c.role_values = [ | |||
|
69 | (x, AuthTokenModel.cls._get_role_name(x)) | |||
|
70 | for x in AuthTokenModel.cls.ROLES] | |||
|
71 | c.role_options = [(c.role_values, _("Role"))] | |||
|
72 | c.user_auth_tokens = AuthTokenModel().get_auth_tokens( | |||
|
73 | c.user.user_id, show_expired=show_expired) | |||
|
74 | return self._get_template_context(c) | |||
|
75 | ||||
|
76 | @LoginRequired() | |||
|
77 | @NotAnonymous() | |||
|
78 | @CSRFRequired() | |||
|
79 | @view_config( | |||
|
80 | route_name='my_account_auth_tokens_add', request_method='POST') | |||
|
81 | def my_account_auth_tokens_add(self): | |||
|
82 | _ = self.request.translate | |||
|
83 | c = self.load_default_context() | |||
|
84 | ||||
|
85 | lifetime = safe_int(self.request.POST.get('lifetime'), -1) | |||
|
86 | description = self.request.POST.get('description') | |||
|
87 | role = self.request.POST.get('role') | |||
|
88 | ||||
|
89 | AuthTokenModel().create(c.user.user_id, description, lifetime, role) | |||
|
90 | Session().commit() | |||
|
91 | h.flash(_("Auth token successfully created"), category='success') | |||
|
92 | ||||
|
93 | return HTTPFound(h.route_path('my_account_auth_tokens')) | |||
|
94 | ||||
|
95 | @LoginRequired() | |||
|
96 | @NotAnonymous() | |||
|
97 | @CSRFRequired() | |||
|
98 | @view_config( | |||
|
99 | route_name='my_account_auth_tokens_delete', request_method='POST') | |||
|
100 | def my_account_auth_tokens_delete(self): | |||
|
101 | _ = self.request.translate | |||
|
102 | c = self.load_default_context() | |||
|
103 | ||||
|
104 | del_auth_token = self.request.POST.get('del_auth_token') | |||
|
105 | ||||
|
106 | if del_auth_token: | |||
|
107 | AuthTokenModel().delete(del_auth_token, c.user.user_id) | |||
|
108 | Session().commit() | |||
|
109 | h.flash(_("Auth token successfully deleted"), category='success') | |||
|
110 | ||||
|
111 | return HTTPFound(h.route_path('my_account_auth_tokens')) |
@@ -26,6 +26,10 b' from rhodecode.lib.utils2 import StrictA' | |||||
26 | log = logging.getLogger(__name__) |
|
26 | log = logging.getLogger(__name__) | |
27 |
|
27 | |||
28 |
|
28 | |||
|
29 | ADMIN_PREFIX = '/_admin' | |||
|
30 | STATIC_FILE_PREFIX = '/_static' | |||
|
31 | ||||
|
32 | ||||
29 | class TemplateArgs(StrictAttributeDict): |
|
33 | class TemplateArgs(StrictAttributeDict): | |
30 | pass |
|
34 | pass | |
31 |
|
35 | |||
@@ -41,10 +45,17 b' class BaseAppView(object):' | |||||
41 | def _get_local_tmpl_context(self): |
|
45 | def _get_local_tmpl_context(self): | |
42 | return TemplateArgs() |
|
46 | return TemplateArgs() | |
43 |
|
47 | |||
|
48 | def _register_global_c(self, tmpl_args): | |||
|
49 | """ | |||
|
50 | Registers attributes to pylons global `c` | |||
|
51 | """ | |||
|
52 | # TODO(marcink): remove once pyramid migration is finished | |||
|
53 | for k, v in tmpl_args.items(): | |||
|
54 | setattr(c, k, v) | |||
|
55 | ||||
44 | def _get_template_context(self, tmpl_args): |
|
56 | def _get_template_context(self, tmpl_args): | |
45 |
|
57 | |||
46 | for k, v in tmpl_args.items(): |
|
58 | self._register_global_c(tmpl_args) | |
47 | setattr(c, k, v) |
|
|||
48 |
|
59 | |||
49 | return { |
|
60 | return { | |
50 | 'defaults': {}, |
|
61 | 'defaults': {}, |
@@ -0,0 +1,19 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | ||||
|
3 | # Copyright (C) 2016-2017 RhodeCode GmbH | |||
|
4 | # | |||
|
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 | |||
|
7 | # (only), as published by the Free Software Foundation. | |||
|
8 | # | |||
|
9 | # This program is distributed in the hope that it will be useful, | |||
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
12 | # GNU General Public License for more details. | |||
|
13 | # | |||
|
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/>. | |||
|
16 | # | |||
|
17 | # This program is dual-licensed. If you wish to learn more about the | |||
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |||
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
@@ -286,6 +286,7 b' def includeme(config):' | |||||
286 | config.include('rhodecode.apps.channelstream') |
|
286 | config.include('rhodecode.apps.channelstream') | |
287 | config.include('rhodecode.apps.login') |
|
287 | config.include('rhodecode.apps.login') | |
288 | config.include('rhodecode.apps.user_profile') |
|
288 | config.include('rhodecode.apps.user_profile') | |
|
289 | config.include('rhodecode.apps.my_account') | |||
289 |
|
290 | |||
290 | config.include('rhodecode.tweens') |
|
291 | config.include('rhodecode.tweens') | |
291 | config.include('rhodecode.api') |
|
292 | config.include('rhodecode.api') |
@@ -543,12 +543,6 b' def make_map(config):' | |||||
543 | m.connect('my_account_emails', '/my_account/emails', |
|
543 | m.connect('my_account_emails', '/my_account/emails', | |
544 | action='my_account_emails_delete', conditions={'method': ['DELETE']}) |
|
544 | action='my_account_emails_delete', conditions={'method': ['DELETE']}) | |
545 |
|
545 | |||
546 | m.connect('my_account_auth_tokens', '/my_account/auth_tokens', |
|
|||
547 | action='my_account_auth_tokens', conditions={'method': ['GET']}) |
|
|||
548 | m.connect('my_account_auth_tokens', '/my_account/auth_tokens', |
|
|||
549 | action='my_account_auth_tokens_add', conditions={'method': ['POST']}) |
|
|||
550 | m.connect('my_account_auth_tokens', '/my_account/auth_tokens', |
|
|||
551 | action='my_account_auth_tokens_delete', conditions={'method': ['DELETE']}) |
|
|||
552 | m.connect('my_account_notifications', '/my_account/notifications', |
|
546 | m.connect('my_account_notifications', '/my_account/notifications', | |
553 | action='my_notifications', |
|
547 | action='my_notifications', | |
554 | conditions={'method': ['GET']}) |
|
548 | conditions={'method': ['GET']}) |
@@ -33,13 +33,12 b' from pylons import request, tmpl_context' | |||||
33 | from pylons.controllers.util import redirect |
|
33 | from pylons.controllers.util import redirect | |
34 | from pylons.i18n.translation import _ |
|
34 | from pylons.i18n.translation import _ | |
35 | from sqlalchemy.orm import joinedload |
|
35 | from sqlalchemy.orm import joinedload | |
36 | from webob.exc import HTTPBadGateway |
|
|||
37 |
|
36 | |||
38 | from rhodecode import forms |
|
37 | from rhodecode import forms | |
39 | from rhodecode.lib import helpers as h |
|
38 | from rhodecode.lib import helpers as h | |
40 | from rhodecode.lib import auth |
|
39 | from rhodecode.lib import auth | |
41 | from rhodecode.lib.auth import ( |
|
40 | from rhodecode.lib.auth import ( | |
42 |
LoginRequired, NotAnonymous, AuthUser |
|
41 | LoginRequired, NotAnonymous, AuthUser) | |
43 | from rhodecode.lib.base import BaseController, render |
|
42 | from rhodecode.lib.base import BaseController, render | |
44 | from rhodecode.lib.utils import jsonify |
|
43 | from rhodecode.lib.utils import jsonify | |
45 | from rhodecode.lib.utils2 import safe_int, md5, str2bool |
|
44 | from rhodecode.lib.utils2 import safe_int, md5, str2bool | |
@@ -54,7 +53,6 b' from rhodecode.model.forms import UserFo' | |||||
54 | from rhodecode.model.scm import RepoList |
|
53 | from rhodecode.model.scm import RepoList | |
55 | from rhodecode.model.user import UserModel |
|
54 | from rhodecode.model.user import UserModel | |
56 | from rhodecode.model.repo import RepoModel |
|
55 | from rhodecode.model.repo import RepoModel | |
57 | from rhodecode.model.auth_token import AuthTokenModel |
|
|||
58 | from rhodecode.model.meta import Session |
|
56 | from rhodecode.model.meta import Session | |
59 | from rhodecode.model.pull_request import PullRequestModel |
|
57 | from rhodecode.model.pull_request import PullRequestModel | |
60 | from rhodecode.model.comment import CommentsModel |
|
58 | from rhodecode.model.comment import CommentsModel | |
@@ -376,47 +374,6 b' class MyAccountController(BaseController' | |||||
376 | else: |
|
374 | else: | |
377 | return json.dumps(data) |
|
375 | return json.dumps(data) | |
378 |
|
376 | |||
379 | def my_account_auth_tokens(self): |
|
|||
380 | c.active = 'auth_tokens' |
|
|||
381 | self.__load_data() |
|
|||
382 | show_expired = True |
|
|||
383 | c.lifetime_values = [ |
|
|||
384 | (str(-1), _('forever')), |
|
|||
385 | (str(5), _('5 minutes')), |
|
|||
386 | (str(60), _('1 hour')), |
|
|||
387 | (str(60 * 24), _('1 day')), |
|
|||
388 | (str(60 * 24 * 30), _('1 month')), |
|
|||
389 | ] |
|
|||
390 | c.lifetime_options = [(c.lifetime_values, _("Lifetime"))] |
|
|||
391 | c.role_values = [(x, AuthTokenModel.cls._get_role_name(x)) |
|
|||
392 | for x in AuthTokenModel.cls.ROLES] |
|
|||
393 | c.role_options = [(c.role_values, _("Role"))] |
|
|||
394 | c.user_auth_tokens = AuthTokenModel().get_auth_tokens( |
|
|||
395 | c.rhodecode_user.user_id, show_expired=show_expired) |
|
|||
396 | return render('admin/my_account/my_account.mako') |
|
|||
397 |
|
||||
398 | @auth.CSRFRequired() |
|
|||
399 | def my_account_auth_tokens_add(self): |
|
|||
400 | lifetime = safe_int(request.POST.get('lifetime'), -1) |
|
|||
401 | description = request.POST.get('description') |
|
|||
402 | role = request.POST.get('role') |
|
|||
403 | AuthTokenModel().create(c.rhodecode_user.user_id, description, lifetime, |
|
|||
404 | role) |
|
|||
405 | Session().commit() |
|
|||
406 | h.flash(_("Auth token successfully created"), category='success') |
|
|||
407 | return redirect(url('my_account_auth_tokens')) |
|
|||
408 |
|
||||
409 | @auth.CSRFRequired() |
|
|||
410 | def my_account_auth_tokens_delete(self): |
|
|||
411 | del_auth_token = request.POST.get('del_auth_token') |
|
|||
412 |
|
||||
413 | if del_auth_token: |
|
|||
414 | AuthTokenModel().delete(del_auth_token, c.rhodecode_user.user_id) |
|
|||
415 | Session().commit() |
|
|||
416 | h.flash(_("Auth token successfully deleted"), category='success') |
|
|||
417 |
|
||||
418 | return redirect(url('my_account_auth_tokens')) |
|
|||
419 |
|
||||
420 | def my_notifications(self): |
|
377 | def my_notifications(self): | |
421 | c.active = 'notifications' |
|
378 | c.active = 'notifications' | |
422 | return render('admin/my_account/my_account.mako') |
|
379 | return render('admin/my_account/my_account.mako') |
@@ -28,7 +28,7 b'' | |||||
28 | <ul class="nav nav-pills nav-stacked"> |
|
28 | <ul class="nav nav-pills nav-stacked"> | |
29 | <li class="${'active' if c.active=='profile' or c.active=='profile_edit' else ''}"><a href="${h.url('my_account')}">${_('Profile')}</a></li> |
|
29 | <li class="${'active' if c.active=='profile' or c.active=='profile_edit' else ''}"><a href="${h.url('my_account')}">${_('Profile')}</a></li> | |
30 | <li class="${'active' if c.active=='password' else ''}"><a href="${h.url('my_account_password')}">${_('Password')}</a></li> |
|
30 | <li class="${'active' if c.active=='password' else ''}"><a href="${h.url('my_account_password')}">${_('Password')}</a></li> | |
31 |
<li class="${'active' if c.active=='auth_tokens' else ''}"><a href="${h. |
|
31 | <li class="${'active' if c.active=='auth_tokens' else ''}"><a href="${h.route_path('my_account_auth_tokens')}">${_('Auth Tokens')}</a></li> | |
32 | ## TODO: Find a better integration of oauth views into navigation. |
|
32 | ## TODO: Find a better integration of oauth views into navigation. | |
33 | <% my_account_oauth_url = h.route_path_or_none('my_account_oauth') %> |
|
33 | <% my_account_oauth_url = h.route_path_or_none('my_account_oauth') %> | |
34 | % if my_account_oauth_url: |
|
34 | % if my_account_oauth_url: |
@@ -42,7 +42,7 b'' | |||||
42 | %endif |
|
42 | %endif | |
43 | </td> |
|
43 | </td> | |
44 | <td class="td-action"> |
|
44 | <td class="td-action"> | |
45 |
${h.secure_form( |
|
45 | ${h.secure_form(h.route_path('my_account_auth_tokens_delete'), method='post')} | |
46 | ${h.hidden('del_auth_token',auth_token.api_key)} |
|
46 | ${h.hidden('del_auth_token',auth_token.api_key)} | |
47 | <button class="btn btn-link btn-danger" type="submit" |
|
47 | <button class="btn btn-link btn-danger" type="submit" | |
48 | onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.api_key}');"> |
|
48 | onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.api_key}');"> | |
@@ -58,7 +58,7 b'' | |||||
58 | </table> |
|
58 | </table> | |
59 |
|
59 | |||
60 | <div class="user_auth_tokens"> |
|
60 | <div class="user_auth_tokens"> | |
61 |
${h.secure_form( |
|
61 | ${h.secure_form(h.route_path('my_account_auth_tokens_add'), method='post')} | |
62 | <div class="form form-vertical"> |
|
62 | <div class="form form-vertical"> | |
63 | <!-- fields --> |
|
63 | <!-- fields --> | |
64 | <div class="fields"> |
|
64 | <div class="fields"> |
@@ -254,63 +254,6 b' class TestMyAccountController(TestContro' | |||||
254 | msg = h.html_escape(msg % {'username': 'test_admin'}) |
|
254 | msg = h.html_escape(msg % {'username': 'test_admin'}) | |
255 | response.mustcontain(u"%s" % msg) |
|
255 | response.mustcontain(u"%s" % msg) | |
256 |
|
256 | |||
257 | def test_my_account_auth_tokens(self): |
|
|||
258 | usr = self.log_user('test_regular2', 'test12') |
|
|||
259 | user = User.get(usr['user_id']) |
|
|||
260 | response = self.app.get(url('my_account_auth_tokens')) |
|
|||
261 | for token in user.auth_tokens: |
|
|||
262 | response.mustcontain(token) |
|
|||
263 | response.mustcontain('never') |
|
|||
264 |
|
||||
265 | @pytest.mark.parametrize("desc, lifetime", [ |
|
|||
266 | ('forever', -1), |
|
|||
267 | ('5mins', 60*5), |
|
|||
268 | ('30days', 60*60*24*30), |
|
|||
269 | ]) |
|
|||
270 | def test_my_account_add_auth_tokens(self, desc, lifetime, user_util): |
|
|||
271 | user = user_util.create_user(password='qweqwe') |
|
|||
272 | user_id = user.user_id |
|
|||
273 | self.log_user(user.username, 'qweqwe') |
|
|||
274 |
|
||||
275 | response = self.app.post(url('my_account_auth_tokens'), |
|
|||
276 | {'description': desc, 'lifetime': lifetime, |
|
|||
277 | 'csrf_token': self.csrf_token}) |
|
|||
278 | assert_session_flash(response, 'Auth token successfully created') |
|
|||
279 |
|
||||
280 | response = response.follow() |
|
|||
281 | user = User.get(user_id) |
|
|||
282 | for auth_token in user.auth_tokens: |
|
|||
283 | response.mustcontain(auth_token) |
|
|||
284 |
|
||||
285 | def test_my_account_remove_auth_token(self, user_util): |
|
|||
286 | user = user_util.create_user(password='qweqwe') |
|
|||
287 | user_id = user.user_id |
|
|||
288 | self.log_user(user.username, 'qweqwe') |
|
|||
289 |
|
||||
290 | user = User.get(user_id) |
|
|||
291 | keys = user.extra_auth_tokens |
|
|||
292 | assert 2 == len(keys) |
|
|||
293 |
|
||||
294 | response = self.app.post(url('my_account_auth_tokens'), |
|
|||
295 | {'description': 'desc', 'lifetime': -1, |
|
|||
296 | 'csrf_token': self.csrf_token}) |
|
|||
297 | assert_session_flash(response, 'Auth token successfully created') |
|
|||
298 | response.follow() |
|
|||
299 |
|
||||
300 | user = User.get(user_id) |
|
|||
301 | keys = user.extra_auth_tokens |
|
|||
302 | assert 3 == len(keys) |
|
|||
303 |
|
||||
304 | response = self.app.post( |
|
|||
305 | url('my_account_auth_tokens'), |
|
|||
306 | {'_method': 'delete', 'del_auth_token': keys[0].api_key, |
|
|||
307 | 'csrf_token': self.csrf_token}) |
|
|||
308 | assert_session_flash(response, 'Auth token successfully deleted') |
|
|||
309 |
|
||||
310 | user = User.get(user_id) |
|
|||
311 | keys = user.extra_auth_tokens |
|
|||
312 | assert 2 == len(keys) |
|
|||
313 |
|
||||
314 | def test_valid_change_password(self, user_util): |
|
257 | def test_valid_change_password(self, user_util): | |
315 | new_password = 'my_new_valid_password' |
|
258 | new_password = 'my_new_valid_password' | |
316 | user = user_util.create_user(password=self.test_user_1_password) |
|
259 | user = user_util.create_user(password=self.test_user_1_password) |
General Comments 0
You need to be logged in to leave comments.
Login now