##// END OF EJS Templates
auth-tokens: add scope and show consitent token UI for my account and admin.
marcink -
r1480:a9c54e36 default
parent child Browse files
Show More

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

@@ -1,97 +1,98 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2013-2017 RhodeCode GmbH
3 # Copyright (C) 2013-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 tokens model for RhodeCode
22 authentication tokens model for RhodeCode
23 """
23 """
24
24
25 import time
25 import time
26 import logging
26 import logging
27 import traceback
27 import traceback
28 from sqlalchemy import or_
28 from sqlalchemy import or_
29
29
30 from rhodecode.model import BaseModel
30 from rhodecode.model import BaseModel
31 from rhodecode.model.db import UserApiKeys
31 from rhodecode.model.db import UserApiKeys
32 from rhodecode.model.meta import Session
32 from rhodecode.model.meta import Session
33
33
34 log = logging.getLogger(__name__)
34 log = logging.getLogger(__name__)
35
35
36
36
37 class AuthTokenModel(BaseModel):
37 class AuthTokenModel(BaseModel):
38 cls = UserApiKeys
38 cls = UserApiKeys
39
39
40 def create(self, user, description, lifetime=-1, role=UserApiKeys.ROLE_ALL):
40 def create(self, user, description, lifetime=-1, role=UserApiKeys.ROLE_ALL):
41 """
41 """
42 :param user: user or user_id
42 :param user: user or user_id
43 :param description: description of ApiKey
43 :param description: description of ApiKey
44 :param lifetime: expiration time in minutes
44 :param lifetime: expiration time in minutes
45 :param role: role for the apikey
45 :param role: role for the apikey
46 """
46 """
47 from rhodecode.lib.auth import generate_auth_token
47 from rhodecode.lib.auth import generate_auth_token
48
48
49 user = self._get_user(user)
49 user = self._get_user(user)
50
50
51 new_auth_token = UserApiKeys()
51 new_auth_token = UserApiKeys()
52 new_auth_token.api_key = generate_auth_token(user.username)
52 new_auth_token.api_key = generate_auth_token(user.username)
53 new_auth_token.user_id = user.user_id
53 new_auth_token.user_id = user.user_id
54 new_auth_token.description = description
54 new_auth_token.description = description
55 new_auth_token.role = role
55 new_auth_token.role = role
56 new_auth_token.expires = time.time() + (lifetime * 60) if lifetime != -1 else -1
56 new_auth_token.expires = time.time() + (lifetime * 60) \
57 if lifetime != -1 else -1
57 Session().add(new_auth_token)
58 Session().add(new_auth_token)
58
59
59 return new_auth_token
60 return new_auth_token
60
61
61 def delete(self, api_key, user=None):
62 def delete(self, api_key, user=None):
62 """
63 """
63 Deletes given api_key, if user is set it also filters the object for
64 Deletes given api_key, if user is set it also filters the object for
64 deletion by given user.
65 deletion by given user.
65 """
66 """
66 api_key = UserApiKeys.query().filter(UserApiKeys.api_key == api_key)
67 api_key = UserApiKeys.query().filter(UserApiKeys.api_key == api_key)
67
68
68 if user:
69 if user:
69 user = self._get_user(user)
70 user = self._get_user(user)
70 api_key = api_key.filter(UserApiKeys.user_id == user.user_id)
71 api_key = api_key.filter(UserApiKeys.user_id == user.user_id)
71
72
72 api_key = api_key.scalar()
73 api_key = api_key.scalar()
73 try:
74 try:
74 Session().delete(api_key)
75 Session().delete(api_key)
75 except Exception:
76 except Exception:
76 log.error(traceback.format_exc())
77 log.error(traceback.format_exc())
77 raise
78 raise
78
79
79 def get_auth_tokens(self, user, show_expired=True):
80 def get_auth_tokens(self, user, show_expired=True):
80 user = self._get_user(user)
81 user = self._get_user(user)
81 user_auth_tokens = UserApiKeys.query()\
82 user_auth_tokens = UserApiKeys.query()\
82 .filter(UserApiKeys.user_id == user.user_id)
83 .filter(UserApiKeys.user_id == user.user_id)
83 if not show_expired:
84 if not show_expired:
84 user_auth_tokens = user_auth_tokens\
85 user_auth_tokens = user_auth_tokens\
85 .filter(or_(UserApiKeys.expires == -1,
86 .filter(or_(UserApiKeys.expires == -1,
86 UserApiKeys.expires >= time.time()))
87 UserApiKeys.expires >= time.time()))
87 return user_auth_tokens
88 return user_auth_tokens
88
89
89 def get_auth_token(self, auth_token):
90 def get_auth_token(self, auth_token):
90 auth_token = UserApiKeys.query().filter(
91 auth_token = UserApiKeys.query().filter(
91 UserApiKeys.api_key == auth_token)
92 UserApiKeys.api_key == auth_token)
92 auth_token = auth_token \
93 auth_token = auth_token \
93 .filter(or_(UserApiKeys.expires == -1,
94 .filter(or_(UserApiKeys.expires == -1,
94 UserApiKeys.expires >= time.time()))\
95 UserApiKeys.expires >= time.time()))\
95 .first()
96 .first()
96
97
97 return auth_token
98 return auth_token
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,80 +1,95 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. VCS tokens can be used together with the authtoken auth plugin for git/hg/svn operations.')}
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.')}
9 ${_('Additionally scope for VCS type token can narrow the use to chosen repository.')}
8 </p>
10 </p>
9 <table class="rctable auth_tokens">
11 <table class="rctable auth_tokens">
10 %if c.user_auth_tokens:
12 %if c.user_auth_tokens:
13 <tr>
14 <th>${_('Token')}</th>
15 <th>${_('Scope')}</th>
16 <th>${_('Description')}</th>
17 <th>${_('Role')}</th>
18 <th>${_('Expiration')}</th>
19 <th>${_('Action')}</th>
20 </tr>
11 %for auth_token in c.user_auth_tokens:
21 %for auth_token in c.user_auth_tokens:
12 <tr class="${'expired' if auth_token.expired else ''}">
22 <tr class="${'expired' if auth_token.expired else ''}">
13 <td class="truncate-wrap td-authtoken"><div class="user_auth_tokens truncate autoexpand"><code>${auth_token.api_key}</code></div></td>
23 <td class="truncate-wrap td-authtoken">
24 <div class="user_auth_tokens truncate autoexpand">
25 <code>${auth_token.api_key}</code>
26 </div>
27 </td>
28 <td class="td">${auth_token.scope_humanized}</td>
14 <td class="td-wrap">${auth_token.description}</td>
29 <td class="td-wrap">${auth_token.description}</td>
15 <td class="td-tags">
30 <td class="td-tags">
16 <span class="tag disabled">${auth_token.role_humanized}</span>
31 <span class="tag disabled">${auth_token.role_humanized}</span>
17 </td>
32 </td>
18 <td class="td-exp">
33 <td class="td-exp">
19 %if auth_token.expires == -1:
34 %if auth_token.expires == -1:
20 ${_('expires')}: ${_('never')}
35 ${_('never')}
21 %else:
36 %else:
22 %if auth_token.expired:
37 %if auth_token.expired:
23 ${_('expired')}: ${h.age_component(h.time_to_utcdatetime(auth_token.expires))}
38 <span style="text-decoration: line-through">${h.age_component(h.time_to_utcdatetime(auth_token.expires))}</span>
24 %else:
39 %else:
25 ${_('expires')}: ${h.age_component(h.time_to_utcdatetime(auth_token.expires))}
40 ${h.age_component(h.time_to_utcdatetime(auth_token.expires))}
26 %endif
41 %endif
27 %endif
42 %endif
28 </td>
43 </td>
29 <td class="td-action">
44 <td class="td-action">
30 ${h.secure_form(url('my_account_auth_tokens'),method='delete')}
45 ${h.secure_form(url('my_account_auth_tokens'),method='delete')}
31 ${h.hidden('del_auth_token',auth_token.api_key)}
46 ${h.hidden('del_auth_token',auth_token.api_key)}
32 <button class="btn btn-link btn-danger" type="submit"
47 <button class="btn btn-link btn-danger" type="submit"
33 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}');">
34 ${_('Delete')}
49 ${_('Delete')}
35 </button>
50 </button>
36 ${h.end_form()}
51 ${h.end_form()}
37 </td>
52 </td>
38 </tr>
53 </tr>
39 %endfor
54 %endfor
40 %else:
55 %else:
41 <tr><td><div class="ip">${_('No additional auth token specified')}</div></td></tr>
56 <tr><td><div class="ip">${_('No additional auth token specified')}</div></td></tr>
42 %endif
57 %endif
43 </table>
58 </table>
44
59
45 <div class="user_auth_tokens">
60 <div class="user_auth_tokens">
46 ${h.secure_form(url('my_account_auth_tokens'), method='post')}
61 ${h.secure_form(url('my_account_auth_tokens'), method='post')}
47 <div class="form form-vertical">
62 <div class="form form-vertical">
48 <!-- fields -->
63 <!-- fields -->
49 <div class="fields">
64 <div class="fields">
50 <div class="field">
65 <div class="field">
51 <div class="label">
66 <div class="label">
52 <label for="new_email">${_('New authentication token')}:</label>
67 <label for="new_email">${_('New authentication token')}:</label>
53 </div>
68 </div>
54 <div class="input">
69 <div class="input">
55 ${h.text('description', placeholder=_('Description'))}
70 ${h.text('description', placeholder=_('Description'))}
56 ${h.select('lifetime', '', c.lifetime_options)}
71 ${h.select('lifetime', '', c.lifetime_options)}
57 ${h.select('role', '', c.role_options)}
72 ${h.select('role', '', c.role_options)}
58 </div>
73 </div>
59 </div>
74 </div>
60 <div class="buttons">
75 <div class="buttons">
61 ${h.submit('save',_('Add'),class_="btn")}
76 ${h.submit('save',_('Add'),class_="btn")}
62 ${h.reset('reset',_('Reset'),class_="btn")}
77 ${h.reset('reset',_('Reset'),class_="btn")}
63 </div>
78 </div>
64 </div>
79 </div>
65 </div>
80 </div>
66 ${h.end_form()}
81 ${h.end_form()}
67 </div>
82 </div>
68 </div>
83 </div>
69 </div>
84 </div>
70 <script>
85 <script>
71 $(document).ready(function(){
86 $(document).ready(function(){
72 var select2Options = {
87 var select2Options = {
73 'containerCssClass': "drop-menu",
88 'containerCssClass': "drop-menu",
74 'dropdownCssClass': "drop-menu-dropdown",
89 'dropdownCssClass': "drop-menu-dropdown",
75 'dropdownAutoWidth': true
90 'dropdownAutoWidth': true
76 };
91 };
77 $("#lifetime").select2(select2Options);
92 $("#lifetime").select2(select2Options);
78 $("#role").select2(select2Options);
93 $("#role").select2(select2Options);
79 });
94 });
80 </script>
95 </script>
@@ -1,83 +1,97 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 Access Tokens')}</h3>
3 <h3 class="panel-title">${_('Authentication Access Tokens')}</h3>
4 </div>
4 </div>
5 <div class="panel-body">
5 <div class="panel-body">
6 <div class="apikeys_wrap">
6 <div class="apikeys_wrap">
7 <p>
8 ${_('Each token can have a role. Token with a role can be used only in given context, '
9 'e.g. VCS tokens can be used together with the authtoken auth plugin for git/hg/svn operations only.')}
10 ${_('Additionally scope for VCS type token can narrow the use to chosen repository.')}
11 </p>
7 <table class="rctable auth_tokens">
12 <table class="rctable auth_tokens">
13 <tr>
14 <th>${_('Token')}</th>
15 <th>${_('Scope')}</th>
16 <th>${_('Description')}</th>
17 <th>${_('Role')}</th>
18 <th>${_('Expiration')}</th>
19 <th>${_('Action')}</th>
20 </tr>
8 %if c.user_auth_tokens:
21 %if c.user_auth_tokens:
9 %for auth_token in c.user_auth_tokens:
22 %for auth_token in c.user_auth_tokens:
10 <tr class="${'expired' if auth_token.expired else ''}">
23 <tr class="${'expired' if auth_token.expired else ''}">
11 <td class="truncate-wrap td-authtoken"><div class="user_auth_tokens truncate autoexpand"><code>${auth_token.api_key}</code></div></td>
24 <td class="truncate-wrap td-authtoken"><div class="user_auth_tokens truncate autoexpand"><code>${auth_token.api_key}</code></div></td>
25 <td class="td">${auth_token.scope_humanized}</td>
12 <td class="td-wrap">${auth_token.description}</td>
26 <td class="td-wrap">${auth_token.description}</td>
13 <td class="td-tags">
27 <td class="td-tags">
14 <span class="tag">${auth_token.role_humanized}</span>
28 <span class="tag">${auth_token.role_humanized}</span>
15 </td>
29 </td>
16 <td class="td-exp">
30 <td class="td-exp">
17 %if auth_token.expires == -1:
31 %if auth_token.expires == -1:
18 ${_('expires')}: ${_('never')}
32 ${_('never')}
19 %else:
33 %else:
20 %if auth_token.expired:
34 %if auth_token.expired:
21 ${_('expired')}: ${h.age_component(h.time_to_utcdatetime(auth_token.expires))}
35 <span style="text-decoration: line-through">${h.age_component(h.time_to_utcdatetime(auth_token.expires))}</span>
22 %else:
36 %else:
23 ${_('expires')}: ${h.age_component(h.time_to_utcdatetime(auth_token.expires))}
37 ${h.age_component(h.time_to_utcdatetime(auth_token.expires))}
24 %endif
38 %endif
25 %endif
39 %endif
26 </td>
40 </td>
27 <td>
41 <td>
28 ${h.secure_form(url('edit_user_auth_tokens', user_id=c.user.user_id),method='delete')}
42 ${h.secure_form(url('edit_user_auth_tokens', user_id=c.user.user_id),method='delete')}
29 ${h.hidden('del_auth_token',auth_token.api_key)}
43 ${h.hidden('del_auth_token',auth_token.api_key)}
30 <button class="btn btn-link btn-danger" type="submit"
44 <button class="btn btn-link btn-danger" type="submit"
31 onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.api_key}');">
45 onclick="return confirm('${_('Confirm to remove this auth token: %s') % auth_token.api_key}');">
32 ${_('Delete')}
46 ${_('Delete')}
33 </button>
47 </button>
34 ${h.end_form()}
48 ${h.end_form()}
35 </td>
49 </td>
36 </tr>
50 </tr>
37 %endfor
51 %endfor
38 %else:
52 %else:
39 <tr><td><div class="ip">${_('No additional auth tokens specified')}</div></td></tr>
53 <tr><td><div class="ip">${_('No additional auth tokens specified')}</div></td></tr>
40 %endif
54 %endif
41 </table>
55 </table>
42 </div>
56 </div>
43
57
44 <div class="user_auth_tokens">
58 <div class="user_auth_tokens">
45 ${h.secure_form(url('edit_user_auth_tokens', user_id=c.user.user_id), method='put')}
59 ${h.secure_form(url('edit_user_auth_tokens', user_id=c.user.user_id), method='put')}
46 <div class="form form-vertical">
60 <div class="form form-vertical">
47 <!-- fields -->
61 <!-- fields -->
48 <div class="fields">
62 <div class="fields">
49 <div class="field">
63 <div class="field">
50 <div class="label">
64 <div class="label">
51 <label for="new_email">${_('New auth token')}:</label>
65 <label for="new_email">${_('New authentication token')}:</label>
52 </div>
66 </div>
53 <div class="input">
67 <div class="input">
54 ${h.text('description', class_='medium', placeholder=_('Description'))}
68 ${h.text('description', class_='medium', placeholder=_('Description'))}
55 ${h.select('lifetime', '', c.lifetime_options)}
69 ${h.select('lifetime', '', c.lifetime_options)}
56 ${h.select('role', '', c.role_options)}
70 ${h.select('role', '', c.role_options)}
57 </div>
71 </div>
58 </div>
72 </div>
59 <div class="buttons">
73 <div class="buttons">
60 ${h.submit('save',_('Add'),class_="btn btn-small")}
74 ${h.submit('save',_('Add'),class_="btn btn-small")}
61 ${h.reset('reset',_('Reset'),class_="btn btn-small")}
75 ${h.reset('reset',_('Reset'),class_="btn btn-small")}
62 </div>
76 </div>
63 </div>
77 </div>
64 </div>
78 </div>
65 ${h.end_form()}
79 ${h.end_form()}
66 </div>
80 </div>
67 </div>
81 </div>
68 </div>
82 </div>
69
83
70 <script>
84 <script>
71 $(document).ready(function(){
85 $(document).ready(function(){
72 $("#lifetime").select2({
86 $("#lifetime").select2({
73 'containerCssClass': "drop-menu",
87 'containerCssClass': "drop-menu",
74 'dropdownCssClass': "drop-menu-dropdown",
88 'dropdownCssClass': "drop-menu-dropdown",
75 'dropdownAutoWidth': true
89 'dropdownAutoWidth': true
76 });
90 });
77 $("#role").select2({
91 $("#role").select2({
78 'containerCssClass': "drop-menu",
92 'containerCssClass': "drop-menu",
79 'dropdownCssClass': "drop-menu-dropdown",
93 'dropdownCssClass': "drop-menu-dropdown",
80 'dropdownAutoWidth': true
94 'dropdownAutoWidth': true
81 });
95 });
82 })
96 })
83 </script>
97 </script>
General Comments 0
You need to be logged in to leave comments. Login now