123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467 |
- # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
- require 'rails_helper'
- RSpec.describe 'Api Auth', type: :request do
- around do |example|
- orig = ActionController::Base.allow_forgery_protection
- begin
- ActionController::Base.allow_forgery_protection = true
- example.run
- ensure
- ActionController::Base.allow_forgery_protection = orig
- end
- end
- let(:admin) do
- create(:admin)
- end
- let(:agent) do
- create(:agent)
- end
- let(:customer) do
- create(:customer)
- end
- before do
- stub_const('Auth::BRUTE_FORCE_SLEEP', 0)
- end
- describe 'request handling' do
- it 'does basic auth - admin' do
- Setting.set('api_password_access', false)
- authenticated_as(admin)
- get '/api/v1/sessions', params: {}, as: :json
- expect(response).to have_http_status(:forbidden)
- expect(response.header).not_to be_key('Access-Control-Allow-Origin')
- expect(json_response).to be_a(Hash)
- expect(json_response['error']).to eq('API password access disabled!')
- Setting.set('api_password_access', true)
- get '/api/v1/sessions', params: {}, as: :json
- expect(response).to have_http_status(:ok)
- expect(response.header['Access-Control-Allow-Origin']).to eq('*')
- expect(response.header['Cache-Control']).to eq('max-age=0, private, must-revalidate')
- expect(json_response).to be_a(Hash)
- expect(json_response).to be_truthy
- end
- it 'does basic auth - agent' do
- Setting.set('api_password_access', false)
- authenticated_as(agent)
- get '/api/v1/tickets', params: {}, as: :json
- expect(response).to have_http_status(:forbidden)
- expect(response.header).not_to be_key('Access-Control-Allow-Origin')
- expect(json_response).to be_a(Hash)
- expect(json_response['error']).to eq('API password access disabled!')
- Setting.set('api_password_access', true)
- get '/api/v1/tickets', params: {}, as: :json
- expect(response).to have_http_status(:ok)
- expect(response.header['Access-Control-Allow-Origin']).to eq('*')
- expect(response.header['Cache-Control']).to eq('max-age=0, private, must-revalidate')
- expect(json_response).to be_a(Array)
- expect(json_response).to be_truthy
- end
- it 'does basic auth - customer' do
- Setting.set('api_password_access', false)
- authenticated_as(customer)
- get '/api/v1/tickets', params: {}, as: :json
- expect(response).to have_http_status(:forbidden)
- expect(response.header).not_to be_key('Access-Control-Allow-Origin')
- expect(json_response).to be_a(Hash)
- expect(json_response['error']).to eq('API password access disabled!')
- Setting.set('api_password_access', true)
- get '/api/v1/tickets', params: {}, as: :json
- expect(response).to have_http_status(:ok)
- expect(response.header['Access-Control-Allow-Origin']).to eq('*')
- expect(response.header['Cache-Control']).to eq('max-age=0, private, must-revalidate')
- expect(json_response).to be_a(Array)
- expect(json_response).to be_truthy
- end
- context 'when using BasicAuth with TwoFactor' do
- let!(:two_factor_pref) { create(:user_two_factor_preference, :authenticator_app, user: admin) }
- it 'rejects the log-in' do
- two_factor_pref
- authenticated_as(admin)
- Setting.set('api_password_access', true)
- get '/api/v1/sessions', params: {}, as: :json
- expect(response).to have_http_status(:unauthorized)
- end
- end
- it 'does token auth - admin', last_admin_check: false do
- admin_token = create(
- :token,
- action: 'api',
- persistent: true,
- user_id: admin.id,
- preferences: {
- permission: ['admin.session'],
- },
- )
- authenticated_as(admin, token: admin_token)
- Setting.set('api_token_access', false)
- get '/api/v1/sessions', params: {}, as: :json
- expect(response).to have_http_status(:forbidden)
- expect(response.header).not_to be_key('Access-Control-Allow-Origin')
- expect(json_response).to be_a(Hash)
- expect(json_response['error']).to eq('API token access disabled!')
- Setting.set('api_token_access', true)
- get '/api/v1/sessions', params: {}, as: :json
- expect(response).to have_http_status(:ok)
- expect(response.header['Access-Control-Allow-Origin']).to eq('*')
- expect(response.header['Cache-Control']).to eq('max-age=0, private, must-revalidate')
- expect(json_response).to be_a(Hash)
- expect(json_response).to be_truthy
- admin_token.preferences[:permission] = ['admin.session_not_existing']
- admin_token.save!
- get '/api/v1/sessions', params: {}, as: :json
- expect(response).to have_http_status(:forbidden)
- expect(json_response).to be_a(Hash)
- expect(json_response['error']).to eq('Not authorized (token)!')
- admin_token.preferences[:permission] = []
- admin_token.save!
- get '/api/v1/sessions', params: {}, as: :json
- expect(response).to have_http_status(:forbidden)
- expect(json_response).to be_a(Hash)
- expect(json_response['error']).to eq('Not authorized (token)!')
- admin.active = false
- admin.save!
- get '/api/v1/sessions', params: {}, as: :json
- expect(response).to have_http_status(:unauthorized)
- expect(json_response).to be_a(Hash)
- expect(json_response['error']).to eq('Login failed. Have you double-checked your credentials and completed the email verification step?')
- admin_token.preferences[:permission] = ['admin.session']
- admin_token.save!
- get '/api/v1/sessions', params: {}, as: :json
- expect(response).to have_http_status(:unauthorized)
- expect(json_response).to be_a(Hash)
- expect(json_response['error']).to eq('Login failed. Have you double-checked your credentials and completed the email verification step?')
- admin.active = true
- admin.save!
- get '/api/v1/sessions', params: {}, as: :json
- expect(response).to have_http_status(:ok)
- expect(json_response).to be_a(Hash)
- expect(json_response).to be_truthy
- get '/api/v1/roles', params: {}, as: :json
- expect(response).to have_http_status(:forbidden)
- expect(json_response).to be_a(Hash)
- expect(json_response['error']).to eq('Not authorized (token)!')
- admin_token.preferences[:permission] = ['admin.session_not_existing', 'admin.role']
- admin_token.save!
- get '/api/v1/roles', params: {}, as: :json
- expect(response).to have_http_status(:ok)
- expect(json_response).to be_a(Array)
- expect(json_response).to be_truthy
- admin_token.preferences[:permission] = ['ticket.agent']
- admin_token.save!
- get '/api/v1/organizations', params: {}, as: :json
- expect(response).to have_http_status(:ok)
- expect(json_response).to be_a(Array)
- expect(json_response).to be_truthy
- name = "some org name #{SecureRandom.uuid}"
- post '/api/v1/organizations', params: { name: name }, as: :json
- expect(response).to have_http_status(:created)
- expect(json_response).to be_a(Hash)
- expect(json_response['name']).to eq(name)
- expect(json_response).to be_truthy
- name = "some org name #{SecureRandom.uuid} - 2"
- put "/api/v1/organizations/#{json_response['id']}", params: { name: name }, as: :json
- expect(response).to have_http_status(:ok)
- expect(json_response).to be_a(Hash)
- expect(json_response['name']).to eq(name)
- expect(json_response).to be_truthy
- admin_token.preferences[:permission] = ['admin.organization']
- admin_token.save!
- get '/api/v1/organizations', params: {}, as: :json
- expect(response).to have_http_status(:ok)
- expect(json_response).to be_a(Array)
- expect(json_response).to be_truthy
- name = "some org name #{SecureRandom.uuid}"
- post '/api/v1/organizations', params: { name: name }, as: :json
- expect(response).to have_http_status(:created)
- expect(json_response).to be_a(Hash)
- expect(json_response['name']).to eq(name)
- expect(json_response).to be_truthy
- name = "some org name #{SecureRandom.uuid} - 2"
- put "/api/v1/organizations/#{json_response['id']}", params: { name: name }, as: :json
- expect(response).to have_http_status(:ok)
- expect(json_response).to be_a(Hash)
- expect(json_response['name']).to eq(name)
- expect(json_response).to be_truthy
- admin_token.preferences[:permission] = ['admin']
- admin_token.save!
- get '/api/v1/organizations', params: {}, as: :json
- expect(response).to have_http_status(:ok)
- expect(json_response).to be_a(Array)
- expect(json_response).to be_truthy
- name = "some org name #{SecureRandom.uuid}"
- post '/api/v1/organizations', params: { name: name }, as: :json
- expect(response).to have_http_status(:created)
- expect(json_response).to be_a(Hash)
- expect(json_response['name']).to eq(name)
- expect(json_response).to be_truthy
- name = "some org name #{SecureRandom.uuid} - 2"
- put "/api/v1/organizations/#{json_response['id']}", params: { name: name }, as: :json
- expect(response).to have_http_status(:ok)
- expect(json_response).to be_a(Hash)
- expect(json_response['name']).to eq(name)
- expect(json_response).to be_truthy
- end
- it 'does token auth - agent' do
- agent_token = create(
- :token,
- action: 'api',
- persistent: true,
- user_id: agent.id,
- )
- authenticated_as(agent, token: agent_token)
- Setting.set('api_token_access', false)
- get '/api/v1/tickets', params: {}, as: :json
- expect(response).to have_http_status(:forbidden)
- expect(response.header).not_to be_key('Access-Control-Allow-Origin')
- expect(json_response).to be_a(Hash)
- expect(json_response['error']).to eq('API token access disabled!')
- Setting.set('api_token_access', true)
- get '/api/v1/tickets', params: {}, as: :json
- expect(response).to have_http_status(:ok)
- expect(response.header['Access-Control-Allow-Origin']).to eq('*')
- expect(response.header['Cache-Control']).to eq('max-age=0, private, must-revalidate')
- expect(json_response).to be_a(Array)
- expect(json_response).to be_truthy
- get '/api/v1/organizations', params: {}, as: :json
- expect(response).to have_http_status(:ok)
- expect(json_response).to be_a(Array)
- expect(json_response).to be_truthy
- name = "some org name #{SecureRandom.uuid}"
- post '/api/v1/organizations', params: { name: name }, as: :json
- expect(response).to have_http_status(:forbidden)
- end
- it 'does token auth - customer' do
- customer_token = create(
- :token,
- action: 'api',
- persistent: true,
- user_id: customer.id,
- )
- authenticated_as(customer, token: customer_token)
- Setting.set('api_token_access', false)
- get '/api/v1/tickets', params: {}, as: :json
- expect(response).to have_http_status(:forbidden)
- expect(response.header).not_to be_key('Access-Control-Allow-Origin')
- expect(json_response).to be_a(Hash)
- expect(json_response['error']).to eq('API token access disabled!')
- Setting.set('api_token_access', true)
- get '/api/v1/tickets', params: {}, as: :json
- expect(response.header['Access-Control-Allow-Origin']).to eq('*')
- expect(response.header['Cache-Control']).to eq('max-age=0, private, must-revalidate')
- expect(response).to have_http_status(:ok)
- expect(json_response).to be_a(Array)
- expect(json_response).to be_truthy
- get '/api/v1/organizations', params: {}, as: :json
- expect(response).to have_http_status(:ok)
- expect(json_response).to be_a(Array)
- expect(json_response).to be_truthy
- name = "some org name #{SecureRandom.uuid}"
- post '/api/v1/organizations', params: { name: name }, as: :json
- expect(response).to have_http_status(:forbidden)
- end
- it 'does token auth - invalid user - admin', last_admin_check: false do
- admin_token = create(
- :token,
- action: 'api',
- persistent: true,
- user_id: admin.id,
- )
- authenticated_as(admin, token: admin_token)
- admin.active = false
- admin.save!
- Setting.set('api_token_access', false)
- get '/api/v1/sessions', params: {}, as: :json
- expect(response).to have_http_status(:forbidden)
- expect(response.header).not_to be_key('Access-Control-Allow-Origin')
- expect(json_response).to be_a(Hash)
- expect(json_response['error']).to eq('API token access disabled!')
- Setting.set('api_token_access', true)
- get '/api/v1/sessions', params: {}, as: :json
- expect(response).to have_http_status(:unauthorized)
- expect(response.header).not_to be_key('Access-Control-Allow-Origin')
- expect(json_response).to be_a(Hash)
- expect(json_response['error']).to eq('Login failed. Have you double-checked your credentials and completed the email verification step?')
- end
- it 'does token auth - expired' do
- Setting.set('api_token_access', true)
- admin_token = create(
- :token,
- action: 'api',
- persistent: true,
- user_id: admin.id,
- expires_at: Time.zone.today
- )
- authenticated_as(admin, token: admin_token)
- get '/api/v1/tickets', params: {}, as: :json
- expect(response).to have_http_status(:unauthorized)
- expect(response.header).not_to be_key('Access-Control-Allow-Origin')
- expect(json_response).to be_a(Hash)
- expect(json_response['error']).to eq('Not authorized (token expired)!')
- admin_token.reload
- expect(admin_token.last_used_at).to be_within(1.second).of(Time.zone.now)
- end
- it 'does token auth - not expired' do
- Setting.set('api_token_access', true)
- admin_token = create(
- :token,
- action: 'api',
- persistent: true,
- user_id: admin.id,
- expires_at: Time.zone.tomorrow
- )
- authenticated_as(admin, token: admin_token)
- get '/api/v1/tickets', params: {}, as: :json
- expect(response).to have_http_status(:ok)
- expect(response.header['Access-Control-Allow-Origin']).to eq('*')
- expect(response.header['Cache-Control']).to eq('max-age=0, private, must-revalidate')
- expect(json_response).to be_a(Array)
- expect(json_response).to be_truthy
- admin_token.reload
- expect(admin_token.last_used_at).to be_within(1.second).of(Time.zone.now)
- end
- it 'does session auth - admin' do
- admin = create(:admin)
- post '/api/v1/signshow', params: {}, as: :json
- token = response.headers['CSRF-TOKEN']
- post '/api/v1/signin', params: { username: admin.login, password: admin.password, fingerprint: '123456789' }, headers: { 'X-CSRF-Token' => token }
- expect(response.header).not_to be_key('Access-Control-Allow-Origin')
- expect(response).to have_http_status(:created)
- get '/api/v1/sessions', params: {}
- expect(response).to have_http_status(:ok)
- expect(response.header).not_to be_key('Access-Control-Allow-Origin')
- expect(json_response).to be_a(Hash)
- expect(json_response).to be_truthy
- end
- context 'when using session auth with TwoFactor' do
- let(:admin) { create(:admin) }
- let(:two_factor_method) { nil }
- let(:two_factor_payload) { nil }
- let(:code) { two_factor_pref.configuration[:code] }
- let!(:two_factor_pref) { create(:user_two_factor_preference, :authenticator_app, user: admin) }
- before do
- post '/api/v1/signshow', params: {}, as: :json
- token = response.headers['CSRF-TOKEN']
- post '/api/v1/signin', params: { username: admin.login, password: admin.password, two_factor_method: two_factor_method, two_factor_payload: two_factor_payload, fingerprint: '123456789' }, headers: { 'X-CSRF-Token' => token }
- end
- context 'without two factor token' do
- it 'rejects the log-in' do
- expect(response).to have_http_status(:unprocessable_entity)
- end
- end
- context 'with wrong two factor token' do
- let(:two_factor_payload) { 'wrong' }
- let(:two_factor_method) { 'authenticator_app' }
- it 'rejects the log-in' do
- expect(response).to have_http_status(:unauthorized)
- end
- end
- context 'with correct two factor token' do
- let(:two_factor_payload) { code }
- let(:two_factor_method) { 'authenticator_app' }
- it 'accepts the log-in' do
- expect(response).to have_http_status(:created)
- end
- end
- end
- it 'does session auth - admin - only with valid CSRF token' do
- create(:admin, login: 'api-admin@example.com', password: 'adminpw')
- post '/api/v1/signin', params: { username: 'api-admin@example.com', password: 'adminpw', fingerprint: '123456789' }
- expect(response).to have_http_status(:unauthorized)
- end
- end
- end
|