From fa4e9534239ff00d85dcf0f5622cb2bc8b4c3623 Mon Sep 17 00:00:00 2001 From: Thorsten Eckel Date: Tue, 30 Aug 2016 16:26:27 +0200 Subject: [PATCH] Added token attributes last_used_at and expires_at. --- .../controllers/_profile/token_access.coffee | 7 ++- .../app/views/profile/token_access.jst.eco | 8 +--- .../views/profile/token_access_create.jst.eco | 7 +++ app/controllers/application_controller.rb | 23 +++++++--- .../user_access_token_controller.rb | 1 + app/models/token.rb | 6 +-- db/migrate/20120101000001_create_base.rb | 2 + db/migrate/20160830000001_token_attributes.rb | 9 ++++ test/browser/preferences_test.rb | 9 ++++ test/controllers/api_auth_controller_test.rb | 44 +++++++++++++++++++ 10 files changed, 101 insertions(+), 15 deletions(-) create mode 100644 db/migrate/20160830000001_token_attributes.rb diff --git a/app/assets/javascripts/app/controllers/_profile/token_access.coffee b/app/assets/javascripts/app/controllers/_profile/token_access.coffee index c92178ab8..378426c30 100644 --- a/app/assets/javascripts/app/controllers/_profile/token_access.coffee +++ b/app/assets/javascripts/app/controllers/_profile/token_access.coffee @@ -81,9 +81,14 @@ class Create extends App.ControllerModal shown: true content: -> - App.view('profile/token_access_create')( + content = $(App.view('profile/token_access_create')( permissions: @permissions + )) + datepicker = App.UiElement.date.render( + name: 'expires_at', ) + content.find('.js-date').html(datepicker) + content onSubmit: (e) => e.preventDefault() diff --git a/app/assets/javascripts/app/views/profile/token_access.jst.eco b/app/assets/javascripts/app/views/profile/token_access.jst.eco index 4c8cf5d01..adff82e9f 100644 --- a/app/assets/javascripts/app/views/profile/token_access.jst.eco +++ b/app/assets/javascripts/app/views/profile/token_access.jst.eco @@ -16,27 +16,23 @@ <%- @T('Name') %> <%- @T('Permission') %> <%- @T('Created') %> - <%- @T('Delete') %> <% if _.isEmpty(@tokens): %> - <%- @T('none') %> + <%- @T('none') %> <% else: %> <% for token in @tokens: %> <%= token.label %> <% if token.preferences && token.preferences.permission: %><%= token.preferences.permission.join(', ') %><% end %> <%- @humanTime(token.created_at) %> -
<%- @Icon('trash') %> diff --git a/app/assets/javascripts/app/views/profile/token_access_create.jst.eco b/app/assets/javascripts/app/views/profile/token_access_create.jst.eco index 2cb22c8e3..095abf0a7 100644 --- a/app/assets/javascripts/app/views/profile/token_access_create.jst.eco +++ b/app/assets/javascripts/app/views/profile/token_access_create.jst.eco @@ -5,6 +5,13 @@
+
+
+ +
+
+
+
<% for permission in @permissions: %> diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 2c4bad688..8db91f5ea 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -260,27 +260,40 @@ class ApplicationController < ActionController::Base end # check http token based authentication - authenticate_with_http_token do |token, _options| - logger.debug "http token auth check '#{token}'" + authenticate_with_http_token do |token_string, _options| + logger.debug "http token auth check '#{token_string}'" request.session_options[:skip] = true # do not send a session cookie if Setting.get('api_token_access') == false raise Exceptions::NotAuthorized, 'API token access disabled!' end user = Token.check( action: 'api', - name: token, + name: token_string, inactive_user: true, ) if user && auth_param[:permission] user = Token.check( action: 'api', - name: token, + name: token_string, permission: auth_param[:permission], inactive_user: true, ) raise Exceptions::NotAuthorized, 'Not authorized (token)!' if !user 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 end diff --git a/app/controllers/user_access_token_controller.rb b/app/controllers/user_access_token_controller.rb index ce965d91a..33d9e291c 100644 --- a/app/controllers/user_access_token_controller.rb +++ b/app/controllers/user_access_token_controller.rb @@ -53,6 +53,7 @@ class UserAccessTokenController < ApplicationController label: params[:label], persistent: true, user_id: current_user.id, + expires_at: params[:expires_at], preferences: { permission: params[:permission] } diff --git a/app/models/token.rb b/app/models/token.rb index fc02570ee..909f38f64 100644 --- a/app/models/token.rb +++ b/app/models/token.rb @@ -40,11 +40,11 @@ returns check token - user = Token.check(action: 'PasswordReset', name: 'TheTokenItSelf') + user = Token.check(action: 'PasswordReset', name: '123abc12qweads') 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 @@ -70,7 +70,7 @@ returns user = token.user - # persistent token not valid if user is inative + # persistent token not valid if user is inactive if !data[:inactive_user] return if token.persistent && user.active == false end diff --git a/db/migrate/20120101000001_create_base.rb b/db/migrate/20120101000001_create_base.rb index 67f458be9..586527349 100644 --- a/db/migrate/20120101000001_create_base.rb +++ b/db/migrate/20120101000001_create_base.rb @@ -205,6 +205,8 @@ class CreateBase < ActiveRecord::Migration t.string :action, limit: 40, null: false t.string :label, limit: 255, 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 end add_index :tokens, :user_id diff --git a/db/migrate/20160830000001_token_attributes.rb b/db/migrate/20160830000001_token_attributes.rb new file mode 100644 index 000000000..b29d6d135 --- /dev/null +++ b/db/migrate/20160830000001_token_attributes.rb @@ -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 diff --git a/test/browser/preferences_test.rb b/test/browser/preferences_test.rb index 9d67948bb..f6856e554 100644 --- a/test/browser/preferences_test.rb +++ b/test/browser/preferences_test.rb @@ -417,6 +417,11 @@ class PreferencesTest < TestCase css: '#content .modal .js-input', 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 .js-submit') watch_for( @@ -428,6 +433,10 @@ class PreferencesTest < TestCase css: '#content .js-tokenList', value: 'Some App#1' ) + watch_for( + css: '#content .js-tokenList', + value: '05/15/2022' + ) click(css: '#content .js-create') watch_for( diff --git a/test/controllers/api_auth_controller_test.rb b/test/controllers/api_auth_controller_test.rb index 1e69c0272..e7d274673 100644 --- a/test/controllers/api_auth_controller_test.rb +++ b/test/controllers/api_auth_controller_test.rb @@ -270,4 +270,48 @@ class ApiAuthControllerTest < ActionDispatch::IntegrationTest assert_equal('User is inactive!', result['error']) 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