Added token attributes last_used_at and expires_at.

This commit is contained in:
Thorsten Eckel 2016-08-30 16:26:27 +02:00
parent 105fca030d
commit fa4e953423
10 changed files with 101 additions and 15 deletions

View file

@ -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()

View file

@ -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>

View file

@ -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: %>

View file

@ -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

View file

@ -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]
} }

View file

@ -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

View file

@ -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

View 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

View file

@ -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(

View file

@ -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