2019-07-07 11:18:12 +05:30
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2016-06-22 15:30:34 +05:30
|
|
|
require 'spec_helper'
|
|
|
|
|
2020-07-28 23:09:34 +05:30
|
|
|
RSpec.describe PersonalAccessToken do
|
2018-03-17 18:26:18 +05:30
|
|
|
subject { described_class }
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
describe '.build' do
|
|
|
|
let(:personal_access_token) { build(:personal_access_token) }
|
|
|
|
let(:invalid_personal_access_token) { build(:personal_access_token, :invalid) }
|
|
|
|
|
|
|
|
it 'is a valid personal access token' do
|
|
|
|
expect(personal_access_token).to be_valid
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'ensures that the token is generated' do
|
|
|
|
invalid_personal_access_token.save!
|
|
|
|
|
|
|
|
expect(invalid_personal_access_token).to be_valid
|
|
|
|
expect(invalid_personal_access_token.token).not_to be_nil
|
2016-06-22 15:30:34 +05:30
|
|
|
end
|
2017-08-17 22:00:37 +05:30
|
|
|
end
|
|
|
|
|
2020-01-01 13:55:28 +05:30
|
|
|
describe 'scopes' do
|
2022-04-04 11:22:00 +05:30
|
|
|
describe '.project_access_tokens' do
|
|
|
|
let_it_be(:user) { create(:user, :project_bot) }
|
|
|
|
let_it_be(:project_member) { create(:project_member, user: user) }
|
|
|
|
let_it_be(:project_access_token) { create(:personal_access_token, user: user) }
|
|
|
|
|
|
|
|
subject { described_class.project_access_token }
|
|
|
|
|
|
|
|
it { is_expected.to contain_exactly(project_access_token) }
|
|
|
|
end
|
|
|
|
|
2022-05-07 20:08:51 +05:30
|
|
|
describe '.owner_is_human' do
|
|
|
|
let_it_be(:user) { create(:user, :project_bot) }
|
|
|
|
let_it_be(:project_member) { create(:project_member, user: user) }
|
|
|
|
let_it_be(:personal_access_token) { create(:personal_access_token) }
|
|
|
|
let_it_be(:project_access_token) { create(:personal_access_token, user: user) }
|
|
|
|
|
|
|
|
subject { described_class.owner_is_human }
|
|
|
|
|
|
|
|
it { is_expected.to contain_exactly(personal_access_token) }
|
|
|
|
end
|
|
|
|
|
2020-01-01 13:55:28 +05:30
|
|
|
describe '.for_user' do
|
|
|
|
it 'returns personal access tokens of specified user only' do
|
|
|
|
user_1 = create(:user)
|
|
|
|
token_of_user_1 = create(:personal_access_token, user: user_1)
|
|
|
|
create_list(:personal_access_token, 2)
|
|
|
|
|
|
|
|
expect(described_class.for_user(user_1)).to contain_exactly(token_of_user_1)
|
|
|
|
end
|
|
|
|
end
|
2021-01-29 00:20:46 +05:30
|
|
|
|
|
|
|
describe '.for_users' do
|
|
|
|
it 'returns personal access tokens for the specified users only' do
|
|
|
|
user_1 = create(:user)
|
|
|
|
user_2 = create(:user)
|
|
|
|
token_of_user_1 = create(:personal_access_token, user: user_1)
|
|
|
|
token_of_user_2 = create(:personal_access_token, user: user_2)
|
|
|
|
create_list(:personal_access_token, 3)
|
|
|
|
|
|
|
|
expect(described_class.for_users([user_1, user_2])).to contain_exactly(token_of_user_1, token_of_user_2)
|
|
|
|
end
|
|
|
|
end
|
2022-10-11 01:57:18 +05:30
|
|
|
|
|
|
|
describe '.created_before' do
|
|
|
|
let(:last_used_at) { 1.month.ago.beginning_of_hour }
|
|
|
|
let!(:new_used_token) do
|
|
|
|
create(:personal_access_token,
|
|
|
|
created_at: last_used_at + 1.minute,
|
|
|
|
last_used_at: last_used_at + 1.minute
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
let!(:old_unused_token) do
|
|
|
|
create(:personal_access_token,
|
|
|
|
created_at: last_used_at - 1.minute
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
let!(:old_formerly_used_token) do
|
|
|
|
create(:personal_access_token,
|
|
|
|
created_at: last_used_at - 1.minute,
|
|
|
|
last_used_at: last_used_at - 1.minute
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
let!(:old_still_used_token) do
|
|
|
|
create(:personal_access_token,
|
|
|
|
created_at: last_used_at - 1.minute,
|
|
|
|
last_used_at: 1.minute.ago
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
subject { described_class.created_before(last_used_at) }
|
|
|
|
|
|
|
|
it do
|
|
|
|
is_expected.to contain_exactly(
|
|
|
|
old_unused_token,
|
|
|
|
old_formerly_used_token,
|
|
|
|
old_still_used_token
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-11-25 23:54:43 +05:30
|
|
|
describe '.last_used_before' do
|
|
|
|
context 'last_used_*' do
|
|
|
|
let_it_be(:date) { DateTime.new(2022, 01, 01) }
|
2023-01-13 00:05:48 +05:30
|
|
|
let_it_be(:token) { create(:personal_access_token, last_used_at: date) }
|
2022-11-25 23:54:43 +05:30
|
|
|
# This token should never occur in the following tests and indicates that filtering was done correctly with it
|
|
|
|
let_it_be(:never_used_token) { create(:personal_access_token) }
|
|
|
|
|
|
|
|
describe '.last_used_before' do
|
|
|
|
it 'returns personal access tokens used before the specified date only' do
|
|
|
|
expect(described_class.last_used_before(date + 1)).to contain_exactly(token)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'does not return token that is last_used_at after given date' do
|
|
|
|
expect(described_class.last_used_before(date + 1)).not_to contain_exactly(never_used_token)
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '.last_used_after' do
|
|
|
|
it 'returns personal access tokens used after the specified date only' do
|
|
|
|
expect(described_class.last_used_after(date - 1)).to contain_exactly(token)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-10-11 01:57:18 +05:30
|
|
|
describe '.last_used_before_or_unused' do
|
|
|
|
let(:last_used_at) { 1.month.ago.beginning_of_hour }
|
|
|
|
let!(:unused_token) { create(:personal_access_token) }
|
|
|
|
let!(:used_token) do
|
|
|
|
create(:personal_access_token,
|
|
|
|
created_at: last_used_at + 1.minute,
|
|
|
|
last_used_at: last_used_at + 1.minute
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
let!(:old_unused_token) do
|
|
|
|
create(:personal_access_token,
|
|
|
|
created_at: last_used_at - 1.minute
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
let!(:old_formerly_used_token) do
|
|
|
|
create(:personal_access_token,
|
|
|
|
created_at: last_used_at - 1.minute,
|
|
|
|
last_used_at: last_used_at - 1.minute
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
let!(:old_still_used_token) do
|
|
|
|
create(:personal_access_token,
|
|
|
|
created_at: last_used_at - 1.minute,
|
|
|
|
last_used_at: 1.minute.ago
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
subject { described_class.last_used_before_or_unused(last_used_at) }
|
|
|
|
|
|
|
|
it { is_expected.to contain_exactly(old_unused_token, old_formerly_used_token) }
|
|
|
|
end
|
2020-01-01 13:55:28 +05:30
|
|
|
end
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
describe ".active?" do
|
|
|
|
let(:active_personal_access_token) { build(:personal_access_token) }
|
|
|
|
let(:revoked_personal_access_token) { build(:personal_access_token, :revoked) }
|
|
|
|
let(:expired_personal_access_token) { build(:personal_access_token, :expired) }
|
|
|
|
|
|
|
|
it "returns false if the personal_access_token is revoked" do
|
|
|
|
expect(revoked_personal_access_token).not_to be_active
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns false if the personal_access_token is expired" do
|
|
|
|
expect(expired_personal_access_token).not_to be_active
|
|
|
|
end
|
|
|
|
|
|
|
|
it "returns true if the personal_access_token is not revoked and not expired" do
|
|
|
|
expect(active_personal_access_token).to be_active
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-09-10 17:25:29 +05:30
|
|
|
describe 'revoke!' do
|
|
|
|
let(:active_personal_access_token) { create(:personal_access_token) }
|
|
|
|
|
|
|
|
it 'revokes the token' do
|
|
|
|
active_personal_access_token.revoke!
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
expect(active_personal_access_token).to be_revoked
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
context "validations" do
|
|
|
|
let(:personal_access_token) { build(:personal_access_token) }
|
|
|
|
|
|
|
|
it "requires at least one scope" do
|
|
|
|
personal_access_token.scopes = []
|
|
|
|
|
|
|
|
expect(personal_access_token).not_to be_valid
|
|
|
|
expect(personal_access_token.errors[:scopes].first).to eq "can't be blank"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "allows creating a token with API scopes" do
|
|
|
|
personal_access_token.scopes = [:api, :read_user]
|
|
|
|
|
|
|
|
expect(personal_access_token).to be_valid
|
|
|
|
end
|
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
context 'when registry is disabled' do
|
|
|
|
before do
|
|
|
|
stub_container_registry_config(enabled: false)
|
|
|
|
end
|
2017-09-10 17:25:29 +05:30
|
|
|
|
2018-03-17 18:26:18 +05:30
|
|
|
it "rejects creating a token with read_registry scope" do
|
|
|
|
personal_access_token.scopes = [:read_registry]
|
|
|
|
|
|
|
|
expect(personal_access_token).not_to be_valid
|
|
|
|
expect(personal_access_token.errors[:scopes].first).to eq "can only contain available scopes"
|
|
|
|
end
|
|
|
|
|
|
|
|
it "allows revoking a token with read_registry scope" do
|
|
|
|
personal_access_token.scopes = [:read_registry]
|
|
|
|
|
|
|
|
personal_access_token.revoke!
|
|
|
|
|
|
|
|
expect(personal_access_token).to be_revoked
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when registry is enabled' do
|
|
|
|
before do
|
|
|
|
stub_container_registry_config(enabled: true)
|
|
|
|
end
|
|
|
|
|
|
|
|
it "allows creating a token with read_registry scope" do
|
|
|
|
personal_access_token.scopes = [:read_registry]
|
|
|
|
|
|
|
|
expect(personal_access_token).to be_valid
|
|
|
|
end
|
2017-09-10 17:25:29 +05:30
|
|
|
end
|
|
|
|
|
|
|
|
it "rejects creating a token with unavailable scopes" do
|
2017-08-17 22:00:37 +05:30
|
|
|
personal_access_token.scopes = [:openid, :api]
|
2016-06-22 15:30:34 +05:30
|
|
|
|
2017-08-17 22:00:37 +05:30
|
|
|
expect(personal_access_token).not_to be_valid
|
2017-09-10 17:25:29 +05:30
|
|
|
expect(personal_access_token.errors[:scopes].first).to eq "can only contain available scopes"
|
2016-06-22 15:30:34 +05:30
|
|
|
end
|
|
|
|
end
|
2020-01-01 13:55:28 +05:30
|
|
|
|
|
|
|
describe 'scopes' do
|
2022-08-27 11:52:29 +05:30
|
|
|
describe '.active' do
|
|
|
|
let_it_be(:revoked_token) { create(:personal_access_token, :revoked) }
|
|
|
|
let_it_be(:not_revoked_false_token) { create(:personal_access_token, revoked: false) }
|
|
|
|
let_it_be(:not_revoked_nil_token) { create(:personal_access_token, revoked: nil) }
|
|
|
|
let_it_be(:expired_token) { create(:personal_access_token, :expired) }
|
|
|
|
let_it_be(:not_expired_token) { create(:personal_access_token) }
|
|
|
|
let_it_be(:never_expires_token) { create(:personal_access_token, expires_at: nil) }
|
|
|
|
|
|
|
|
it 'includes non-revoked and non-expired tokens' do
|
|
|
|
expect(described_class.active)
|
|
|
|
.to match_array([not_revoked_false_token, not_revoked_nil_token, not_expired_token, never_expires_token])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-01-01 13:55:28 +05:30
|
|
|
describe '.expiring_and_not_notified' do
|
|
|
|
let_it_be(:expired_token) { create(:personal_access_token, expires_at: 2.days.ago) }
|
|
|
|
let_it_be(:revoked_token) { create(:personal_access_token, revoked: true) }
|
|
|
|
let_it_be(:valid_token_and_notified) { create(:personal_access_token, expires_at: 2.days.from_now, expire_notification_delivered: true) }
|
|
|
|
let_it_be(:valid_token) { create(:personal_access_token, expires_at: 2.days.from_now) }
|
2020-07-28 23:09:34 +05:30
|
|
|
let_it_be(:long_expiry_token) { create(:personal_access_token, expires_at: '999999-12-31'.to_date) }
|
2020-01-01 13:55:28 +05:30
|
|
|
|
|
|
|
context 'in one day' do
|
|
|
|
it "doesn't have any tokens" do
|
|
|
|
expect(described_class.expiring_and_not_notified(1.day.from_now)).to be_empty
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'in three days' do
|
|
|
|
it 'only includes a valid token' do
|
|
|
|
expect(described_class.expiring_and_not_notified(3.days.from_now)).to contain_exactly(valid_token)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2020-06-23 00:09:42 +05:30
|
|
|
|
2020-10-24 23:57:45 +05:30
|
|
|
describe '.expired_today_and_not_notified' do
|
|
|
|
let_it_be(:active) { create(:personal_access_token) }
|
|
|
|
let_it_be(:expired_yesterday) { create(:personal_access_token, expires_at: Date.yesterday) }
|
|
|
|
let_it_be(:revoked_token) { create(:personal_access_token, expires_at: Date.current, revoked: true) }
|
|
|
|
let_it_be(:expired_today) { create(:personal_access_token, expires_at: Date.current) }
|
|
|
|
let_it_be(:expired_today_and_notified) { create(:personal_access_token, expires_at: Date.current, after_expiry_notification_delivered: true) }
|
|
|
|
|
|
|
|
it 'returns tokens that have expired today' do
|
|
|
|
expect(described_class.expired_today_and_not_notified).to contain_exactly(expired_today)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-06-23 00:09:42 +05:30
|
|
|
describe '.without_impersonation' do
|
|
|
|
let_it_be(:impersonation_token) { create(:personal_access_token, :impersonation) }
|
|
|
|
let_it_be(:personal_access_token) { create(:personal_access_token) }
|
|
|
|
|
|
|
|
it 'returns only non-impersonation tokens' do
|
|
|
|
expect(described_class.without_impersonation).to contain_exactly(personal_access_token)
|
|
|
|
end
|
|
|
|
end
|
2020-07-28 23:09:34 +05:30
|
|
|
|
|
|
|
describe 'revoke scopes' do
|
|
|
|
let_it_be(:revoked_token) { create(:personal_access_token, :revoked) }
|
|
|
|
let_it_be(:non_revoked_token) { create(:personal_access_token, revoked: false) }
|
|
|
|
let_it_be(:non_revoked_token2) { create(:personal_access_token, revoked: nil) }
|
|
|
|
|
|
|
|
describe '.revoked' do
|
|
|
|
it { expect(described_class.revoked).to contain_exactly(revoked_token) }
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '.not_revoked' do
|
|
|
|
it { expect(described_class.not_revoked).to contain_exactly(non_revoked_token, non_revoked_token2) }
|
|
|
|
end
|
|
|
|
end
|
2020-01-01 13:55:28 +05:30
|
|
|
end
|
2020-05-24 23:13:21 +05:30
|
|
|
|
|
|
|
describe '.simple_sorts' do
|
2020-07-28 23:09:34 +05:30
|
|
|
it 'includes overridden keys' do
|
2023-01-13 00:05:48 +05:30
|
|
|
expect(described_class.simple_sorts.keys).to include(*%w(expires_at_asc_id_desc))
|
2020-05-24 23:13:21 +05:30
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe 'ordering by expires_at' do
|
|
|
|
let_it_be(:earlier_token) { create(:personal_access_token, expires_at: 2.days.ago) }
|
|
|
|
let_it_be(:later_token) { create(:personal_access_token, expires_at: 1.day.ago) }
|
|
|
|
|
2022-08-27 11:52:29 +05:30
|
|
|
describe '.order_expires_at_asc_id_desc' do
|
|
|
|
let_it_be(:earlier_token_2) { create(:personal_access_token, expires_at: 2.days.ago) }
|
|
|
|
|
|
|
|
it 'returns ordered list in combination of expires_at ascending and id descending' do
|
|
|
|
expect(described_class.order_expires_at_asc_id_desc).to eq [earlier_token_2, earlier_token, later_token]
|
|
|
|
end
|
|
|
|
end
|
2020-05-24 23:13:21 +05:30
|
|
|
end
|
2016-06-22 15:30:34 +05:30
|
|
|
end
|