diff --git a/spec/factories/token.rb b/spec/factories/token.rb index 9d8f47def..7700886fa 100644 --- a/spec/factories/token.rb +++ b/spec/factories/token.rb @@ -1,9 +1,16 @@ FactoryBot.define do - factory :token do + factory :token, aliases: %i[token_api api_token] do user - end + action { 'api' } + persistent { true } - factory :token_password_reset, parent: :token do - action 'PasswordReset' + factory :token_password_reset, aliases: %i[password_reset_token] do + action { 'PasswordReset' } + end + + factory :token_ical, aliases: %i[ical_token] do + action { 'iCal' } + persistent { true } + end end end diff --git a/spec/models/token_spec.rb b/spec/models/token_spec.rb new file mode 100644 index 000000000..24ab6f504 --- /dev/null +++ b/spec/models/token_spec.rb @@ -0,0 +1,140 @@ +require 'rails_helper' + +RSpec.describe Token, type: :model do + subject(:token) { create(:password_reset_token) } + + describe '.check' do + context 'with name and action matching existing token' do + it 'returns the token’s user' do + expect(Token.check(action: token.action, name: token.name)).to eq(token.user) + end + end + + context 'with invalid name' do + it 'returns nil' do + expect(Token.check(action: token.action, name: '1NV4L1D')).to be(nil) + end + end + + context 'with invalid action' do + it 'returns nil' do + expect(Token.check(action: 'PasswordReset_NotExisting', name: token.name)).to be(nil) + end + end + + describe 'persistence handling' do + context 'for persistent token' do + subject(:token) { create(:ical_token, persistent: true, created_at: created_at) } + + context 'at any time' do + let(:created_at) { 1.month.ago } + + it 'returns the token’s user' do + expect(Token.check(action: token.action, name: token.name)).to eq(token.user) + end + + it 'does not delete the token' do + token # create token + + expect { Token.check(action: token.action, name: token.name) } + .not_to change { Token.count } + end + end + end + + context 'for non-persistent token' do + subject(:token) { create(:password_reset_token, persistent: false, created_at: created_at) } + + context 'less than one day after creation' do + let(:created_at) { 1.day.ago + 5 } + + it 'returns the token’s user' do + expect(Token.check(action: token.action, name: token.name)).to eq(token.user) + end + + it 'does not delete the token' do + token # create token + + expect { Token.check(action: token.action, name: token.name) } + .not_to change { Token.count } + end + end + + context 'at least one day after creation' do + let(:created_at) { 1.day.ago } + + it 'returns nil' do + expect(Token.check(action: token.action, name: token.name)).to be(nil) + end + + it 'deletes the token' do + token # create token + + expect { Token.check(action: token.action, name: token.name) } + .to change { Token.count }.by(-1) + end + end + end + end + + describe 'permission matching' do + subject(:token) { create(:api_token, user: agent, preferences: preferences) } + let(:agent) { create(:agent_user) } + let(:preferences) { { permission: %w[admin ticket.agent] } } # agent has no access to admin.* + + context 'with a permission shared by both token.user and token.preferences' do + it 'returns token.user' do + expect(Token.check(action: token.action, name: token.name, permission: 'ticket.agent')).to eq(agent) + end + end + + context 'with the child of a permission shared by both token.user and token.preferences' do + it 'returns token.user' do + expect(Token.check(action: token.action, name: token.name, permission: 'ticket.agent.foo')).to eq(agent) + end + end + + context 'with the parent of a permission shared by both token.user and token.preferences' do + it 'returns nil' do + expect(Token.check(action: token.action, name: token.name, permission: 'ticket')).to be(nil) + end + end + + context 'with a permission in token.preferences, but not on token.user' do + it 'returns nil' do + expect(Token.check(action: token.action, name: token.name, permission: 'admin')).to be(nil) + end + end + + context 'with a permission not in token.preferences, but on token.user' do + it 'returns nil' do + expect(Token.check(action: token.action, name: token.name, permission: 'cti.agent')).to be(nil) + end + end + + context 'with non-existent permission' do + it 'returns nil' do + expect(Token.check(action: token.action, name: token.name, permission: 'foo')).to be(nil) + end + end + + context 'with multiple permissions, where at least one is shared by both token.user and token.preferences' do + it 'returns token.user' do + expect(Token.check(action: token.action, name: token.name, permission: %w[foo ticket.agent])).to eq(agent) + end + end + end + end + + describe 'Attributes:' do + describe '#persistent' do + context 'when not set on creation' do + subject(:token) { Token.create(action: 'foo', user_id: User.first.id) } + + it 'defaults to nil' do + expect(token.persistent).to be(nil) + end + end + end + end +end diff --git a/test/unit/token_test.rb b/test/unit/token_test.rb deleted file mode 100644 index b3889a0b2..000000000 --- a/test/unit/token_test.rb +++ /dev/null @@ -1,140 +0,0 @@ -require 'test_helper' - -class TokenTest < ActiveSupport::TestCase - test 'token' do - - groups = Group.all - roles = Role.where(name: 'Agent') - agent1 = User.create_or_update( - login: 'token-agent1@example.com', - firstname: 'Token', - lastname: 'Agent1', - email: 'token-agent1@example.com', - password: 'agentpw', - active: true, - roles: roles, - groups: groups, - updated_by_id: 1, - created_by_id: 1, - ) - - # invalid token - user = Token.check( - action: 'PasswordReset', - name: '1NV4L1D', - ) - assert_not(user) - - # generate fresh token - token = Token.create( - action: 'PasswordReset', - user_id: agent1.id, - ) - assert(token) - assert_nil(token.persistent) - user = Token.check( - action: 'PasswordReset_NotExisting', - name: token.name, - ) - assert_not(user) - user = Token.check( - action: 'PasswordReset', - name: token.name, - ) - assert(user) - assert_equal('Token', user.firstname) - assert_equal('Agent1', user.lastname) - assert_equal('token-agent1@example.com', user.email) - - # two days but not persistent - token = Token.create( - action: 'PasswordReset', - user_id: agent1.id, - created_at: 2.days.ago, - persistent: false, - ) - user = Token.check( - action: 'PasswordReset', - name: token.name, - ) - assert_not(user) - - # two days but persistent - token = Token.create( - action: 'iCal', - user_id: agent1.id, - created_at: 2.days.ago, - persistent: true, - ) - user = Token.check( - action: 'iCal', - name: token.name, - ) - assert(user) - assert_equal('Token', user.firstname) - assert_equal('Agent1', user.lastname) - assert_equal('token-agent1@example.com', user.email) - - # api token with permissions - token = Token.create( - action: 'api', - label: 'some label', - persistent: true, - user_id: agent1.id, - preferences: { - permission: ['admin', 'ticket.agent'], # agent has no access to admin.* - } - ) - user = Token.check( - action: 'api', - name: token.name, - permission: 'admin.session', - ) - assert_not(user) - user = Token.check( - action: 'api', - name: token.name, - permission: 'admin', - ) - assert_not(user) - user = Token.check( - action: 'api', - name: token.name, - permission: 'ticket', - ) - assert_not(user) - user = Token.check( - action: 'api', - name: token.name, - permission: 'ticket.agent.sub', - ) - assert(user) - user = Token.check( - action: 'api', - name: token.name, - permission: 'admin_not_extisting', - ) - assert_not(user) - user = Token.check( - action: 'api', - name: token.name, - permission: 'ticket.agent', - ) - assert(user) - assert_equal('Token', user.firstname) - assert_equal('Agent1', user.lastname) - assert_equal('token-agent1@example.com', user.email) - - user = Token.check( - action: 'api', - name: token.name, - permission: ['ticket.agent', 'not_existing'], - ) - assert(user) - assert_equal('Token', user.firstname) - assert_equal('Agent1', user.lastname) - assert_equal('token-agent1@example.com', user.email) - - end - -end