##// END OF EJS Templates
auth-tokens: extended views to allowed override of adding scope in EE edition.
marcink -
r1507:a630e423 default
parent child Browse files
Show More
@@ -1,111 +1,120 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-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 import logging
21 import logging
22
22
23 from pyramid.httpexceptions import HTTPFound
23 from pyramid.httpexceptions import HTTPFound
24 from pyramid.view import view_config
24 from pyramid.view import view_config
25
25
26 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
27 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
28 from rhodecode.lib.utils2 import safe_int
28 from rhodecode.lib.utils2 import safe_int
29 from rhodecode.lib import helpers as h
29 from rhodecode.lib import helpers as h
30 from rhodecode.model.auth_token import AuthTokenModel
30 from rhodecode.model.auth_token import AuthTokenModel
31 from rhodecode.model.meta import Session
31 from rhodecode.model.meta import Session
32
32
33 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
34
34
35
35
36 class MyAccountView(BaseAppView):
36 class MyAccountView(BaseAppView):
37 ALLOW_SCOPED_TOKENS = False
38 """
39 This view has alternative version inside EE, if modified please take a look
40 in there as well.
41 """
37
42
38 def load_default_context(self):
43 def load_default_context(self):
39 c = self._get_local_tmpl_context()
44 c = self._get_local_tmpl_context()
40
45
41 c.auth_user = self.request.user
46 c.auth_user = self.request.user
42 c.user = c.auth_user.get_instance()
47 c.user = c.auth_user.get_instance()
43
48 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
44 self._register_global_c(c)
49 self._register_global_c(c)
45 return c
50 return c
46
51
47 @LoginRequired()
52 @LoginRequired()
48 @NotAnonymous()
53 @NotAnonymous()
49 @view_config(
54 @view_config(
50 route_name='my_account_auth_tokens', request_method='GET',
55 route_name='my_account_auth_tokens', request_method='GET',
51 renderer='rhodecode:templates/admin/my_account/my_account.mako')
56 renderer='rhodecode:templates/admin/my_account/my_account.mako')
52 def my_account_auth_tokens(self):
57 def my_account_auth_tokens(self):
53 _ = self.request.translate
58 _ = self.request.translate
54
59
55 c = self.load_default_context()
60 c = self.load_default_context()
56 c.active = 'auth_tokens'
61 c.active = 'auth_tokens'
57
62
58 show_expired = True
59
60 c.lifetime_values = [
63 c.lifetime_values = [
61 (str(-1), _('forever')),
64 (str(-1), _('forever')),
62 (str(5), _('5 minutes')),
65 (str(5), _('5 minutes')),
63 (str(60), _('1 hour')),
66 (str(60), _('1 hour')),
64 (str(60 * 24), _('1 day')),
67 (str(60 * 24), _('1 day')),
65 (str(60 * 24 * 30), _('1 month')),
68 (str(60 * 24 * 30), _('1 month')),
66 ]
69 ]
67 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
70 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
68 c.role_values = [
71 c.role_values = [
69 (x, AuthTokenModel.cls._get_role_name(x))
72 (x, AuthTokenModel.cls._get_role_name(x))
70 for x in AuthTokenModel.cls.ROLES]
73 for x in AuthTokenModel.cls.ROLES]
71 c.role_options = [(c.role_values, _("Role"))]
74 c.role_options = [(c.role_values, _("Role"))]
72 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
75 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
73 c.user.user_id, show_expired=show_expired)
76 c.user.user_id, show_expired=True)
74 return self._get_template_context(c)
77 return self._get_template_context(c)
75
78
79 def maybe_attach_token_scope(self, token):
80 # implemented in EE edition
81 pass
82
76 @LoginRequired()
83 @LoginRequired()
77 @NotAnonymous()
84 @NotAnonymous()
78 @CSRFRequired()
85 @CSRFRequired()
79 @view_config(
86 @view_config(
80 route_name='my_account_auth_tokens_add', request_method='POST')
87 route_name='my_account_auth_tokens_add', request_method='POST')
81 def my_account_auth_tokens_add(self):
88 def my_account_auth_tokens_add(self):
82 _ = self.request.translate
89 _ = self.request.translate
83 c = self.load_default_context()
90 c = self.load_default_context()
84
91
85 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
92 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
86 description = self.request.POST.get('description')
93 description = self.request.POST.get('description')
87 role = self.request.POST.get('role')
94 role = self.request.POST.get('role')
88
95
89 AuthTokenModel().create(c.user.user_id, description, lifetime, role)
96 token = AuthTokenModel().create(
97 c.user.user_id, description, lifetime, role)
98 self.maybe_attach_token_scope(token)
90 Session().commit()
99 Session().commit()
100
91 h.flash(_("Auth token successfully created"), category='success')
101 h.flash(_("Auth token successfully created"), category='success')
92
93 return HTTPFound(h.route_path('my_account_auth_tokens'))
102 return HTTPFound(h.route_path('my_account_auth_tokens'))
94
103
95 @LoginRequired()
104 @LoginRequired()
96 @NotAnonymous()
105 @NotAnonymous()
97 @CSRFRequired()
106 @CSRFRequired()
98 @view_config(
107 @view_config(
99 route_name='my_account_auth_tokens_delete', request_method='POST')
108 route_name='my_account_auth_tokens_delete', request_method='POST')
100 def my_account_auth_tokens_delete(self):
109 def my_account_auth_tokens_delete(self):
101 _ = self.request.translate
110 _ = self.request.translate
102 c = self.load_default_context()
111 c = self.load_default_context()
103
112
104 del_auth_token = self.request.POST.get('del_auth_token')
113 del_auth_token = self.request.POST.get('del_auth_token')
105
114
106 if del_auth_token:
115 if del_auth_token:
107 AuthTokenModel().delete(del_auth_token, c.user.user_id)
116 AuthTokenModel().delete(del_auth_token, c.user.user_id)
108 Session().commit()
117 Session().commit()
109 h.flash(_("Auth token successfully deleted"), category='success')
118 h.flash(_("Auth token successfully deleted"), category='success')
110
119
111 return HTTPFound(h.route_path('my_account_auth_tokens'))
120 return HTTPFound(h.route_path('my_account_auth_tokens'))
@@ -1,95 +1,158 b''
1 <div class="panel panel-default">
1 <div class="panel panel-default">
2 <div class="panel-heading">
2 <div class="panel-heading">
3 <h3 class="panel-title">${_('Authentication Tokens')}</h3>
3 <h3 class="panel-title">${_('Authentication Tokens')}</h3>
4 </div>
4 </div>
5 <div class="panel-body">
5 <div class="panel-body">
6 <p>
6 <p>
7 ${_('Each token can have a role. Token with a role can be used only in given context, '
7 ${_('Each token can have a role. Token with a role can be used only in given context, '
8 'e.g. VCS tokens can be used together with the authtoken auth plugin for git/hg/svn operations only.')}
8 'e.g. VCS tokens can be used together with the authtoken auth plugin for git/hg/svn operations only.')}
9 ${_('Additionally scope for VCS type token can narrow the use to chosen repository.')}
10 </p>
9 </p>
11 <table class="rctable auth_tokens">
10 <table class="rctable auth_tokens">
12 %if c.user_auth_tokens:
11 %if c.user_auth_tokens:
13 <tr>
12 <tr>
14 <th>${_('Token')}</th>
13 <th>${_('Token')}</th>
15 <th>${_('Scope')}</th>
14 <th>${_('Scope')}</th>
16 <th>${_('Description')}</th>
15 <th>${_('Description')}</th>
17 <th>${_('Role')}</th>
16 <th>${_('Role')}</th>
18 <th>${_('Expiration')}</th>
17 <th>${_('Expiration')}</th>
19 <th>${_('Action')}</th>
18 <th>${_('Action')}</th>
20 </tr>
19 </tr>
21 %for auth_token in c.user_auth_tokens:
20 %for auth_token in c.user_auth_tokens:
22 <tr class="${'expired' if auth_token.expired else ''}">
21 <tr class="${'expired' if auth_token.expired else ''}">
23 <td class="truncate-wrap td-authtoken">
22 <td class="truncate-wrap td-authtoken">
24 <div class="user_auth_tokens truncate autoexpand">
23 <div class="user_auth_tokens truncate autoexpand">
25 <code>${auth_token.api_key}</code>
24 <code>${auth_token.api_key}</code>
26 </div>
25 </div>
27 </td>
26 </td>
28 <td class="td">${auth_token.scope_humanized}</td>
27 <td class="td">${auth_token.scope_humanized}</td>
29 <td class="td-wrap">${auth_token.description}</td>
28 <td class="td-wrap">${auth_token.description}</td>
30 <td class="td-tags">
29 <td class="td-tags">
31 <span class="tag disabled">${auth_token.role_humanized}</span>
30 <span class="tag disabled">${auth_token.role_humanized}</span>
32 </td>
31 </td>
33 <td class="td-exp">
32 <td class="td-exp">
34 %if auth_token.expires == -1:
33 %if auth_token.expires == -1:
35 ${_('never')}
34 ${_('never')}
36 %else:
35 %else:
37 %if auth_token.expired:
36 %if auth_token.expired:
38 <span style="text-decoration: line-through">${h.age_component(h.time_to_utcdatetime(auth_token.expires))}</span>
37 <span style="text-decoration: line-through">${h.age_component(h.time_to_utcdatetime(auth_token.expires))}</span>
39 %else:
38 %else:
40 ${h.age_component(h.time_to_utcdatetime(auth_token.expires))}
39 ${h.age_component(h.time_to_utcdatetime(auth_token.expires))}
41 %endif
40 %endif
42 %endif
41 %endif
43 </td>
42 </td>
44 <td class="td-action">
43 <td class="td-action">
45 ${h.secure_form(h.route_path('my_account_auth_tokens_delete'), method='post')}
44 ${h.secure_form(h.route_path('my_account_auth_tokens_delete'), method='post')}
46 ${h.hidden('del_auth_token',auth_token.api_key)}
45 ${h.hidden('del_auth_token',auth_token.api_key)}
47 <button class="btn btn-link btn-danger" type="submit"
46 <button class="btn btn-link btn-danger" type="submit"
48 onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.api_key}');">
47 onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.api_key}');">
49 ${_('Delete')}
48 ${_('Delete')}
50 </button>
49 </button>
51 ${h.end_form()}
50 ${h.end_form()}
52 </td>
51 </td>
53 </tr>
52 </tr>
54 %endfor
53 %endfor
55 %else:
54 %else:
56 <tr><td><div class="ip">${_('No additional auth token specified')}</div></td></tr>
55 <tr><td><div class="ip">${_('No additional auth token specified')}</div></td></tr>
57 %endif
56 %endif
58 </table>
57 </table>
59
58
60 <div class="user_auth_tokens">
59 <div class="user_auth_tokens">
61 ${h.secure_form(h.route_path('my_account_auth_tokens_add'), method='post')}
60 ${h.secure_form(h.route_path('my_account_auth_tokens_add'), method='post')}
62 <div class="form form-vertical">
61 <div class="form form-vertical">
63 <!-- fields -->
62 <!-- fields -->
64 <div class="fields">
63 <div class="fields">
65 <div class="field">
64 <div class="field">
66 <div class="label">
65 <div class="label">
67 <label for="new_email">${_('New authentication token')}:</label>
66 <label for="new_email">${_('New authentication token')}:</label>
68 </div>
67 </div>
69 <div class="input">
68 <div class="input">
70 ${h.text('description', placeholder=_('Description'))}
69 ${h.text('description', placeholder=_('Description'))}
71 ${h.select('lifetime', '', c.lifetime_options)}
70 ${h.select('lifetime', '', c.lifetime_options)}
72 ${h.select('role', '', c.role_options)}
71 ${h.select('role', '', c.role_options)}
72
73 % if c.allow_scoped_tokens:
74 ${h.hidden('scope_repo_id')}
75 % else:
76 ${h.select('scope_repo_id_disabled', '', ['Scopes available in EE edition'], disabled='disabled')}
77 % endif
73 </div>
78 </div>
79 <p class="help-block">
80 ${_('Repository scope works only with tokens with VCS type.')}
81 </p>
74 </div>
82 </div>
75 <div class="buttons">
83 <div class="buttons">
76 ${h.submit('save',_('Add'),class_="btn")}
84 ${h.submit('save',_('Add'),class_="btn")}
77 ${h.reset('reset',_('Reset'),class_="btn")}
85 ${h.reset('reset',_('Reset'),class_="btn")}
78 </div>
86 </div>
79 </div>
87 </div>
80 </div>
88 </div>
81 ${h.end_form()}
89 ${h.end_form()}
82 </div>
90 </div>
83 </div>
91 </div>
84 </div>
92 </div>
85 <script>
93 <script>
86 $(document).ready(function(){
94 $(document).ready(function(){
87 var select2Options = {
95
88 'containerCssClass': "drop-menu",
96 var select2Options = {
89 'dropdownCssClass': "drop-menu-dropdown",
97 'containerCssClass': "drop-menu",
90 'dropdownAutoWidth': true
98 'dropdownCssClass': "drop-menu-dropdown",
91 };
99 'dropdownAutoWidth': true
92 $("#lifetime").select2(select2Options);
100 };
93 $("#role").select2(select2Options);
101 $("#lifetime").select2(select2Options);
94 });
102 $("#role").select2(select2Options);
95 </script>
103
104 var repoFilter = function(data) {
105 var results = [];
106
107 if (!data.results[0]) {
108 return data
109 }
110
111 $.each(data.results[0].children, function() {
112 // replace name to ID for submision
113 this.id = this.obj.repo_id;
114 results.push(this);
115 });
116
117 data.results[0].children = results;
118 return data;
119 };
120
121 $("#scope_repo_id_disabled").select2(select2Options);
122
123 $("#scope_repo_id").select2({
124 cachedDataSource: {},
125 minimumInputLength: 2,
126 placeholder: "${_('repository scope')}",
127 dropdownAutoWidth: true,
128 containerCssClass: "drop-menu",
129 dropdownCssClass: "drop-menu-dropdown",
130 formatResult: formatResult,
131 query: $.debounce(250, function(query){
132 self = this;
133 var cacheKey = query.term;
134 var cachedData = self.cachedDataSource[cacheKey];
135
136 if (cachedData) {
137 query.callback({results: cachedData.results});
138 } else {
139 $.ajax({
140 url: "${h.url('repo_list_data')}",
141 data: {'query': query.term},
142 dataType: 'json',
143 type: 'GET',
144 success: function(data) {
145 data = repoFilter(data);
146 self.cachedDataSource[cacheKey] = data;
147 query.callback({results: data.results});
148 },
149 error: function(data, textStatus, errorThrown) {
150 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
151 }
152 })
153 }
154 })
155 });
156
157 });
158 </script>
General Comments 0
You need to be logged in to leave comments. Login now