Added token attributes last_used_at and expires_at.
This commit is contained in:
parent
105fca030d
commit
fa4e953423
10 changed files with 101 additions and 15 deletions
|
@ -81,9 +81,14 @@ class Create extends App.ControllerModal
|
||||||
shown: true
|
shown: true
|
||||||
|
|
||||||
content: ->
|
content: ->
|
||||||
App.view('profile/token_access_create')(
|
content = $(App.view('profile/token_access_create')(
|
||||||
permissions: @permissions
|
permissions: @permissions
|
||||||
|
))
|
||||||
|
datepicker = App.UiElement.date.render(
|
||||||
|
name: 'expires_at',
|
||||||
)
|
)
|
||||||
|
content.find('.js-date').html(datepicker)
|
||||||
|
content
|
||||||
|
|
||||||
onSubmit: (e) =>
|
onSubmit: (e) =>
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
|
@ -16,27 +16,23 @@
|
||||||
<th><%- @T('Name') %></th>
|
<th><%- @T('Name') %></th>
|
||||||
<th><%- @T('Permission') %></th>
|
<th><%- @T('Permission') %></th>
|
||||||
<th><%- @T('Created') %></th>
|
<th><%- @T('Created') %></th>
|
||||||
<!--
|
|
||||||
<th><%- @T('Expires') %></th>
|
<th><%- @T('Expires') %></th>
|
||||||
<th><%- @T('Last used') %></th>
|
<th><%- @T('Last used') %></th>
|
||||||
-->
|
|
||||||
<th><%- @T('Delete') %></th>
|
<th><%- @T('Delete') %></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<% if _.isEmpty(@tokens): %>
|
<% if _.isEmpty(@tokens): %>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="3"><%- @T('none') %>
|
<td colspan="6"><%- @T('none') %>
|
||||||
<% else: %>
|
<% else: %>
|
||||||
<% for token in @tokens: %>
|
<% for token in @tokens: %>
|
||||||
<tr>
|
<tr>
|
||||||
<td><%= token.label %></td>
|
<td><%= token.label %></td>
|
||||||
<td><% if token.preferences && token.preferences.permission: %><%= token.preferences.permission.join(', ') %><% end %></td>
|
<td><% if token.preferences && token.preferences.permission: %><%= token.preferences.permission.join(', ') %><% end %></td>
|
||||||
<td><%- @humanTime(token.created_at) %></td>
|
<td><%- @humanTime(token.created_at) %></td>
|
||||||
<!--
|
<td><%- @Tdate(token.expires_at) %></td>
|
||||||
<td><%- @humanTime(token.expired_at) %></td>
|
|
||||||
<td><%- @humanTime(token.last_used_at) %></td>
|
<td><%- @humanTime(token.last_used_at) %></td>
|
||||||
-->
|
|
||||||
<td class="settings-list-controls">
|
<td class="settings-list-controls">
|
||||||
<div>
|
<div>
|
||||||
<a class="settings-list-control js-delete" href="#" data-token-id="<%- token.id %>" title="<%- @Ti('Delete') %>"><%- @Icon('trash') %></a>
|
<a class="settings-list-control js-delete" href="#" data-token-id="<%- token.id %>" title="<%- @Ti('Delete') %>"><%- @Icon('trash') %></a>
|
||||||
|
|
|
@ -5,6 +5,13 @@
|
||||||
<div class="controls"><input id="token-label" type="text" name="label" value="" class="form-control js-input" required></div>
|
<div class="controls"><input id="token-label" type="text" name="label" value="" class="form-control js-input" required></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="input form-group">
|
||||||
|
<div class="formGroup-label">
|
||||||
|
<label for="token-label"><%- @T('Expires') %></label>
|
||||||
|
</div>
|
||||||
|
<div class="controls js-date"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="permission form-group checkbox">
|
<div class="permission form-group checkbox">
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<% for permission in @permissions: %>
|
<% for permission in @permissions: %>
|
||||||
|
|
|
@ -260,27 +260,40 @@ class ApplicationController < ActionController::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
# check http token based authentication
|
# check http token based authentication
|
||||||
authenticate_with_http_token do |token, _options|
|
authenticate_with_http_token do |token_string, _options|
|
||||||
logger.debug "http token auth check '#{token}'"
|
logger.debug "http token auth check '#{token_string}'"
|
||||||
request.session_options[:skip] = true # do not send a session cookie
|
request.session_options[:skip] = true # do not send a session cookie
|
||||||
if Setting.get('api_token_access') == false
|
if Setting.get('api_token_access') == false
|
||||||
raise Exceptions::NotAuthorized, 'API token access disabled!'
|
raise Exceptions::NotAuthorized, 'API token access disabled!'
|
||||||
end
|
end
|
||||||
user = Token.check(
|
user = Token.check(
|
||||||
action: 'api',
|
action: 'api',
|
||||||
name: token,
|
name: token_string,
|
||||||
inactive_user: true,
|
inactive_user: true,
|
||||||
)
|
)
|
||||||
if user && auth_param[:permission]
|
if user && auth_param[:permission]
|
||||||
user = Token.check(
|
user = Token.check(
|
||||||
action: 'api',
|
action: 'api',
|
||||||
name: token,
|
name: token_string,
|
||||||
permission: auth_param[:permission],
|
permission: auth_param[:permission],
|
||||||
inactive_user: true,
|
inactive_user: true,
|
||||||
)
|
)
|
||||||
raise Exceptions::NotAuthorized, 'Not authorized (token)!' if !user
|
raise Exceptions::NotAuthorized, 'Not authorized (token)!' if !user
|
||||||
end
|
end
|
||||||
@_token_auth = token # remember for permission_check
|
|
||||||
|
if user
|
||||||
|
token = Token.find_by(name: token_string)
|
||||||
|
|
||||||
|
token.last_used_at = Time.zone.now
|
||||||
|
token.save!
|
||||||
|
|
||||||
|
if token.expires_at &&
|
||||||
|
Time.zone.today >= token.expires_at
|
||||||
|
raise Exceptions::NotAuthorized, 'Not authorized (token expired)!'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@_token_auth = token_string # remember for permission_check
|
||||||
return authentication_check_prerequesits(user, 'token_auth', auth_param) if user
|
return authentication_check_prerequesits(user, 'token_auth', auth_param) if user
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,7 @@ class UserAccessTokenController < ApplicationController
|
||||||
label: params[:label],
|
label: params[:label],
|
||||||
persistent: true,
|
persistent: true,
|
||||||
user_id: current_user.id,
|
user_id: current_user.id,
|
||||||
|
expires_at: params[:expires_at],
|
||||||
preferences: {
|
preferences: {
|
||||||
permission: params[:permission]
|
permission: params[:permission]
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,11 +40,11 @@ returns
|
||||||
|
|
||||||
check token
|
check token
|
||||||
|
|
||||||
user = Token.check(action: 'PasswordReset', name: 'TheTokenItSelf')
|
user = Token.check(action: 'PasswordReset', name: '123abc12qweads')
|
||||||
|
|
||||||
check api token with permissions
|
check api token with permissions
|
||||||
|
|
||||||
user = Token.check(action: 'api', name: 'TheTokenItSelf', permission: 'admin.session')
|
user = Token.check(action: 'api', name: '123abc12qweads', permission: 'admin.session')
|
||||||
|
|
||||||
returns
|
returns
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ returns
|
||||||
|
|
||||||
user = token.user
|
user = token.user
|
||||||
|
|
||||||
# persistent token not valid if user is inative
|
# persistent token not valid if user is inactive
|
||||||
if !data[:inactive_user]
|
if !data[:inactive_user]
|
||||||
return if token.persistent && user.active == false
|
return if token.persistent && user.active == false
|
||||||
end
|
end
|
||||||
|
|
|
@ -205,6 +205,8 @@ class CreateBase < ActiveRecord::Migration
|
||||||
t.string :action, limit: 40, null: false
|
t.string :action, limit: 40, null: false
|
||||||
t.string :label, limit: 255, null: true
|
t.string :label, limit: 255, null: true
|
||||||
t.text :preferences, limit: 500.kilobytes + 1, null: true
|
t.text :preferences, limit: 500.kilobytes + 1, null: true
|
||||||
|
t.timestamp :last_used_at, null: true
|
||||||
|
t.date :expires_at, null: true
|
||||||
t.timestamps null: false
|
t.timestamps null: false
|
||||||
end
|
end
|
||||||
add_index :tokens, :user_id
|
add_index :tokens, :user_id
|
||||||
|
|
9
db/migrate/20160830000001_token_attributes.rb
Normal file
9
db/migrate/20160830000001_token_attributes.rb
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
class TokenAttributes < ActiveRecord::Migration
|
||||||
|
def up
|
||||||
|
# return if it's a new setup
|
||||||
|
return if !Setting.find_by(name: 'system_init_done')
|
||||||
|
|
||||||
|
add_column :tokens, :last_used_at, :datetime, null: true
|
||||||
|
add_column :tokens, :expires_at, :date, null: true
|
||||||
|
end
|
||||||
|
end
|
|
@ -417,6 +417,11 @@ class PreferencesTest < TestCase
|
||||||
css: '#content .modal .js-input',
|
css: '#content .modal .js-input',
|
||||||
value: 'Some App#1',
|
value: 'Some App#1',
|
||||||
)
|
)
|
||||||
|
set(
|
||||||
|
css: '#content .modal .js-datepicker',
|
||||||
|
value: '05/15/2022',
|
||||||
|
)
|
||||||
|
sendkey(value: :tab)
|
||||||
click(css: '#content .modal input[value="ticket.agent"] ~ .label-text')
|
click(css: '#content .modal input[value="ticket.agent"] ~ .label-text')
|
||||||
click(css: '#content .modal .js-submit')
|
click(css: '#content .modal .js-submit')
|
||||||
watch_for(
|
watch_for(
|
||||||
|
@ -428,6 +433,10 @@ class PreferencesTest < TestCase
|
||||||
css: '#content .js-tokenList',
|
css: '#content .js-tokenList',
|
||||||
value: 'Some App#1'
|
value: 'Some App#1'
|
||||||
)
|
)
|
||||||
|
watch_for(
|
||||||
|
css: '#content .js-tokenList',
|
||||||
|
value: '05/15/2022'
|
||||||
|
)
|
||||||
|
|
||||||
click(css: '#content .js-create')
|
click(css: '#content .js-create')
|
||||||
watch_for(
|
watch_for(
|
||||||
|
|
|
@ -270,4 +270,48 @@ class ApiAuthControllerTest < ActionDispatch::IntegrationTest
|
||||||
assert_equal('User is inactive!', result['error'])
|
assert_equal('User is inactive!', result['error'])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test 'token auth - expired' do
|
||||||
|
|
||||||
|
Setting.set('api_token_access', true)
|
||||||
|
|
||||||
|
admin_token = Token.create(
|
||||||
|
action: 'api',
|
||||||
|
persistent: true,
|
||||||
|
user_id: @admin.id,
|
||||||
|
expires_at: Time.zone.today
|
||||||
|
)
|
||||||
|
admin_credentials = "Token token=#{admin_token.name}"
|
||||||
|
|
||||||
|
get '/api/v1/tickets', {}, @headers.merge('Authorization' => admin_credentials)
|
||||||
|
assert_response(401)
|
||||||
|
result = JSON.parse(@response.body)
|
||||||
|
assert_equal(Hash, result.class)
|
||||||
|
assert_equal('Not authorized (token expired)!', result['error'])
|
||||||
|
|
||||||
|
admin_token.reload
|
||||||
|
assert_in_delta(admin_token.last_used_at, Time.zone.now, 1.second)
|
||||||
|
end
|
||||||
|
|
||||||
|
test 'token auth - not expired' do
|
||||||
|
|
||||||
|
Setting.set('api_token_access', true)
|
||||||
|
|
||||||
|
admin_token = Token.create(
|
||||||
|
action: 'api',
|
||||||
|
persistent: true,
|
||||||
|
user_id: @admin.id,
|
||||||
|
expires_at: Time.zone.tomorrow
|
||||||
|
)
|
||||||
|
admin_credentials = "Token token=#{admin_token.name}"
|
||||||
|
|
||||||
|
get '/api/v1/tickets', {}, @headers.merge('Authorization' => admin_credentials)
|
||||||
|
assert_response(200)
|
||||||
|
result = JSON.parse(@response.body)
|
||||||
|
assert_equal(Array, result.class)
|
||||||
|
assert(result)
|
||||||
|
|
||||||
|
admin_token.reload
|
||||||
|
assert_in_delta(admin_token.last_used_at, Time.zone.now, 1.second)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue