Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
36e64e679d
commit
3bbc597f60
16 changed files with 207 additions and 90 deletions
|
@ -140,6 +140,7 @@ export default {
|
|||
|
||||
<template #cell(action)="{ item: { revokePath } }">
|
||||
<gl-button
|
||||
v-if="revokePath"
|
||||
category="tertiary"
|
||||
:aria-label="$options.i18n.revokeButton"
|
||||
:data-confirm="modalMessage"
|
||||
|
|
|
@ -14,6 +14,7 @@ module Environments
|
|||
|
||||
def execute
|
||||
environments = project.environments
|
||||
environments = by_type(environments)
|
||||
environments = by_name(environments)
|
||||
environments = by_search(environments)
|
||||
environments = by_ids(environments)
|
||||
|
@ -24,6 +25,12 @@ module Environments
|
|||
|
||||
private
|
||||
|
||||
def by_type(environments)
|
||||
return environments unless params[:type].present?
|
||||
|
||||
environments.for_type(params[:type])
|
||||
end
|
||||
|
||||
def by_name(environments)
|
||||
if params[:name].present?
|
||||
environments.for_name(params[:name])
|
||||
|
|
|
@ -89,6 +89,7 @@ class Environment < ApplicationRecord
|
|||
|
||||
scope :for_project, -> (project) { where(project_id: project) }
|
||||
scope :for_tier, -> (tier) { where(tier: tier).where.not(tier: nil) }
|
||||
scope :for_type, -> (type) { where(environment_type: type) }
|
||||
scope :unfoldered, -> { where(environment_type: nil) }
|
||||
scope :with_rank, -> do
|
||||
select('environments.*, rank() OVER (PARTITION BY project_id ORDER BY id DESC)')
|
||||
|
|
|
@ -24,6 +24,7 @@ class PersonalAccessToken < ApplicationRecord
|
|||
scope :expiring_and_not_notified, ->(date) { where(["revoked = false AND expire_notification_delivered = false AND expires_at >= CURRENT_DATE AND expires_at <= ?", date]) }
|
||||
scope :expired_today_and_not_notified, -> { where(["revoked = false AND expires_at = CURRENT_DATE AND after_expiry_notification_delivered = false"]) }
|
||||
scope :inactive, -> { where("revoked = true OR expires_at < CURRENT_DATE") }
|
||||
scope :created_before, -> (date) { where("personal_access_tokens.created_at < :date", date: date) }
|
||||
scope :last_used_before, -> (date) { where("personal_access_tokens.created_at < :date AND (last_used_at < :date OR last_used_at IS NULL)", date: date) }
|
||||
scope :with_impersonation, -> { where(impersonation: true) }
|
||||
scope :without_impersonation, -> { where(impersonation: false) }
|
||||
|
|
|
@ -218,6 +218,28 @@ instance_of_object.method(:foo).source_location
|
|||
project.method(:private?).source_location
|
||||
```
|
||||
|
||||
## Limiting output
|
||||
|
||||
Adding a semicolon(`;`) and a follow-up statement at the end of a statement prevents the default implicit return output. This can be used if you are already explicitly printing details and potentially have a lot of return output:
|
||||
|
||||
```ruby
|
||||
puts ActiveRecord::Base.descendants; :ok
|
||||
Project.select(&:pages_deployed?).each {|p| puts p.pages_url }; true
|
||||
```
|
||||
|
||||
## Get or store the result of last operation
|
||||
|
||||
Underscore(`_`) represents the implicit return of the previous statement. You can use this to quickly assign a variable from the output of the previous command:
|
||||
|
||||
```ruby
|
||||
Project.last
|
||||
# => #<Project id:2537 root/discard>>
|
||||
project = _
|
||||
# => #<Project id:2537 root/discard>>
|
||||
project.id
|
||||
# => 2537
|
||||
```
|
||||
|
||||
## Active Record objects
|
||||
|
||||
### Looking up database-persisted objects
|
||||
|
@ -347,6 +369,15 @@ D, [2020-03-05T17:18:30.406047 #910] DEBUG -- : User Load (2.6ms) SELECT "use
|
|||
For more on different ways to retrieve data from the database using Active
|
||||
Record, please see the [Active Record Query Interface documentation](https://guides.rubyonrails.org/active_record_querying.html).
|
||||
|
||||
## Query the database using an Active Record model
|
||||
|
||||
```ruby
|
||||
m = Model.where('attribute like ?', 'ex%')
|
||||
|
||||
# for example to query the projects
|
||||
projects = Project.where('path like ?', 'Oumua%')
|
||||
```
|
||||
|
||||
### Modifying Active Record objects
|
||||
|
||||
In the previous section, we learned about retrieving database records using
|
||||
|
|
|
@ -62,28 +62,6 @@ Notify.test_email(e, "Test email for #{n}", 'Test email').deliver_now
|
|||
Notify.test_email(u.email, "Test email for #{u.name}", 'Test email').deliver_now
|
||||
```
|
||||
|
||||
## Limiting output
|
||||
|
||||
Adding a semicolon(`;`) and a follow-up statement at the end of a statement prevents the default implicit return output. This can be used if you are already explicitly printing details and potentially have a lot of return output:
|
||||
|
||||
```ruby
|
||||
puts ActiveRecord::Base.descendants; :ok
|
||||
Project.select(&:pages_deployed?).each {|p| puts p.pages_url }; true
|
||||
```
|
||||
|
||||
## Get or store the result of last operation
|
||||
|
||||
Underscore(`_`) represents the implicit return of the previous statement. You can use this to quickly assign a variable from the output of the previous command:
|
||||
|
||||
```ruby
|
||||
Project.last
|
||||
# => #<Project id:2537 root/discard>>
|
||||
project = _
|
||||
# => #<Project id:2537 root/discard>>
|
||||
project.id
|
||||
# => 2537
|
||||
```
|
||||
|
||||
## Open object in `irb`
|
||||
|
||||
Sometimes it is easier to go through a method if you are in the context of the object. You can shim into the namespace of `Object` to let you open `irb` in the context of any object:
|
||||
|
@ -99,15 +77,6 @@ irb(#<Project>)> web_url
|
|||
# => "https://gitlab-example/root/discard"
|
||||
```
|
||||
|
||||
## Query the database using an ActiveRecord Model
|
||||
|
||||
```ruby
|
||||
m = Model.where('attribute like ?', 'ex%')
|
||||
|
||||
# for example to query the projects
|
||||
projects = Project.where('path like ?', 'Oumua%')
|
||||
```
|
||||
|
||||
## View all keys in cache
|
||||
|
||||
```ruby
|
||||
|
|
|
@ -6,25 +6,33 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
|
||||
# Get started with GitLab application security **(ULTIMATE)**
|
||||
|
||||
Complete the following steps to get the most from GitLab application security tools.
|
||||
The following steps will help you get the most from GitLab application security tools. These steps are a recommended order of operations. You can choose to implement capabilities in a different order or omit features that do not apply to your specific needs.
|
||||
|
||||
1. Enable [Secret Detection](secret_detection/index.md) scanning for your default branch.
|
||||
1. Enable [Dependency Scanning](dependency_scanning/index.md) for your default branch so you can start identifying existing
|
||||
1. Enable [Secret Detection](secret_detection/index.md) and [Dependency Scanning](dependency_scanning/index.md)
|
||||
to identify any leaked secrets and vulnerable packages in your codebase.
|
||||
|
||||
- For all security scanners, enable them by updating your `[.gitlab-ci.yml](../../ci/yaml/gitlab_ci_yaml.md)` directly on your `default` branch. This creates a baseline scan of your `default` branch, which is necessary for
|
||||
feature branch scans to be compared against. This allows [merge requests](../project/merge_requests/index.md)
|
||||
to display only newly-introduced vulnerabilities. Otherwise, merge requests will display every
|
||||
vulnerability in the branch, regardless of whether it was introduced by a change in the branch.
|
||||
- If you are after simplicity, enable only Secret Detection first. It only has one analyzer,
|
||||
no build requirements, and relatively simple findings: is this a secret or not?
|
||||
- It is good practice to enable Dependency Scanning early so you can start identifying existing
|
||||
vulnerable packages in your codebase.
|
||||
1. Add security scans to feature branch pipelines. The same scans should be enabled as are running
|
||||
on your default branch. Subsequent scans will show only new vulnerabilities by comparing the feature branch to the default branch results.
|
||||
1. Let your team get comfortable with [vulnerability reports](vulnerability_report/index.md) and
|
||||
establish a vulnerability triage workflow.
|
||||
1. Consider creating [labels](../project/labels.md) and [issue boards](../project/issue_board.md) to
|
||||
help manage issues created from vulnerabilities. Issue boards allow all stakeholders to have a
|
||||
common view of all issues.
|
||||
common view of all issues and track remediation progress.
|
||||
1. Use [scheduled pipelines](../../ci/pipelines/schedules.md#scheduled-pipelines) to regularly scan important branches such as `default` or those used for maintenance releases.
|
||||
- Running regular dependency and [container scans](container_scanning/index.md) will surface newly-discovered vulnerabilities that already exist in your repository.
|
||||
- Scheduled scans are most useful for projects or important branches with low development activity where pipeline scans are infrequent.
|
||||
1. Create a [scan result policy](policies/index.md) to limit new vulnerabilities from being merged
|
||||
into your default branch.
|
||||
into your `default` branch.
|
||||
1. Monitor the [Security Dashboard](security_dashboard/index.md) trends to gauge success in
|
||||
remediating existing vulnerabilities and preventing the introduction of new ones.
|
||||
1. Enable other scan types such as [SAST](sast/index.md), [DAST](dast/index.md),
|
||||
[Fuzz testing](coverage_fuzzing/index.md), or [Container Scanning](container_scanning/index.md).
|
||||
Be sure to add the same scan types to both feature pipelines and default branch pipelines.
|
||||
1. Use [Compliance Pipelines](../../user/project/settings/index.md#compliance-pipeline-configuration)
|
||||
or [Scan Execution Policies](policies/scan-execution-policies.md) to enforce required scan types
|
||||
and ensure separation of duties between security and engineering.
|
||||
|
|
|
@ -2,31 +2,23 @@
|
|||
|
||||
module Gitlab
|
||||
module Cleanup
|
||||
# Unused active Personal Access Tokens pose a risk to organizations
|
||||
# in that they may have been, or may be, leaked to unauthorized
|
||||
# individuals. They are likely providing little / no current value
|
||||
# because they are not actively being used, and should therefore be
|
||||
# proactively revoked.
|
||||
class UnusedPersonalAccessTokens
|
||||
# By default tokens that haven't been used for over 1 year will
|
||||
# be revoked
|
||||
class PersonalAccessTokens
|
||||
# By default tokens that haven't been used for over 1 year will be revoked
|
||||
DEFAULT_TIME_PERIOD = 1.year
|
||||
# To prevent inadvertently revoking actively used tokens, we
|
||||
# provide a minimum time
|
||||
# To prevent inadvertently revoking all tokens, we provide a minimum time
|
||||
MINIMUM_TIME_PERIOD = 1.day
|
||||
|
||||
attr_reader :logger, :last_used_before, :revocation_time, :group
|
||||
attr_reader :logger, :cut_off_date, :revocation_time, :group
|
||||
|
||||
def initialize(last_used_before: DEFAULT_TIME_PERIOD.ago.beginning_of_day, logger: nil, group_full_path:)
|
||||
# binding.pry
|
||||
# Ensure last_used_before is a Time and far enough in the past
|
||||
@last_used_before = last_used_before
|
||||
def initialize(cut_off_date: DEFAULT_TIME_PERIOD.ago.beginning_of_day, logger: nil, group_full_path:)
|
||||
@cut_off_date = cut_off_date
|
||||
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
@group = Group.find_by_full_path(group_full_path)
|
||||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
raise "Group with full_path #{group_full_path} not found" unless @group
|
||||
raise "Invalid time: #{@last_used_before}" unless @last_used_before <= MINIMUM_TIME_PERIOD.ago
|
||||
raise "Invalid time: #{@cut_off_date}" unless @cut_off_date <= MINIMUM_TIME_PERIOD.ago
|
||||
|
||||
# Use a static revocation time to make correlation of revoked
|
||||
# tokens easier, should it be needed.
|
||||
|
@ -36,27 +28,21 @@ module Gitlab
|
|||
raise "Invalid logger: #{@logger}" unless @logger.respond_to?(:info) && @logger.respond_to?(:warn)
|
||||
end
|
||||
|
||||
# Revokes unused personal access tokens.
|
||||
# A dry run is performed by default, logging what would be
|
||||
# revoked. Pass `dry_run: false` explicitly to revoke tokens.
|
||||
def run!(dry_run: true)
|
||||
def run!(dry_run: true, revoke_active_tokens: false)
|
||||
# rubocop:disable Rails/Output
|
||||
if dry_run
|
||||
puts "Dry running. No changes will be made"
|
||||
elsif revoke_active_tokens
|
||||
puts "Revoking used and unused access tokens created before #{cut_off_date}..."
|
||||
else
|
||||
puts "Revoking access tokens from before #{last_used_before}..."
|
||||
puts "Revoking access tokens last used and created before #{cut_off_date}..."
|
||||
end
|
||||
# rubocop:enable Rails/Output
|
||||
|
||||
logger.info(
|
||||
dry_run: dry_run,
|
||||
group_full_path: group.full_path,
|
||||
message: "Looking for Personal Access Tokens " \
|
||||
"last used before #{last_used_before}..."
|
||||
)
|
||||
tokens_to_revoke = revocable_tokens(revoke_active_tokens)
|
||||
|
||||
# rubocop:disable Cop/InBatches
|
||||
revocable_tokens.in_batches do |access_tokens|
|
||||
tokens_to_revoke.in_batches do |access_tokens|
|
||||
revoke_batch(access_tokens, dry_run)
|
||||
end
|
||||
# rubocop:enable Cop/InBatches
|
||||
|
@ -64,12 +50,20 @@ module Gitlab
|
|||
|
||||
private
|
||||
|
||||
def revocable_tokens
|
||||
PersonalAccessToken
|
||||
.active
|
||||
.owner_is_human
|
||||
.last_used_before(last_used_before)
|
||||
.for_users(group.users)
|
||||
def revocable_tokens(revoke_active_tokens)
|
||||
if revoke_active_tokens
|
||||
PersonalAccessToken
|
||||
.active
|
||||
.owner_is_human
|
||||
.created_before(cut_off_date)
|
||||
.for_users(group.users)
|
||||
else
|
||||
PersonalAccessToken
|
||||
.active
|
||||
.owner_is_human
|
||||
.last_used_before(cut_off_date)
|
||||
.for_users(group.users)
|
||||
end
|
||||
end
|
||||
|
||||
def revoke_batch(access_tokens, dry_run)
|
|
@ -11576,6 +11576,9 @@ msgstr ""
|
|||
msgid "CycleAnalytics|Average time to completion"
|
||||
msgstr ""
|
||||
|
||||
msgid "CycleAnalytics|Average time to completion (days)"
|
||||
msgstr ""
|
||||
|
||||
msgid "CycleAnalytics|Change Failure Rate"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@ RSpec.describe Environments::EnvironmentsFinder do
|
|||
let_it_be(:project) { create(:project, :repository) }
|
||||
let_it_be(:user) { project.creator }
|
||||
let_it_be(:environment) { create(:environment, :available, project: project) }
|
||||
let_it_be(:environment_stopped) { create(:environment, :stopped, name: 'test2', project: project) }
|
||||
let_it_be(:environment_available) { create(:environment, :available, name: 'test3', project: project) }
|
||||
let_it_be(:environment_stopped) { create(:environment, :stopped, name: 'test/test2', project: project) }
|
||||
let_it_be(:environment_available) { create(:environment, :available, name: 'test/test3', project: project) }
|
||||
|
||||
before do
|
||||
project.add_maintainer(user)
|
||||
|
@ -65,5 +65,11 @@ RSpec.describe Environments::EnvironmentsFinder do
|
|||
expect(result).to contain_exactly(environment_available)
|
||||
end
|
||||
end
|
||||
|
||||
it 'filters environments by type' do
|
||||
result = described_class.new(project, user, type: 'test').execute
|
||||
|
||||
expect(result).to contain_exactly(environment_stopped, environment_available)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -190,6 +190,21 @@ describe('~/access_tokens/components/access_token_table_app', () => {
|
|||
expect(button.props('category')).toBe('tertiary');
|
||||
});
|
||||
|
||||
describe('revoke path', () => {
|
||||
beforeEach(() => {
|
||||
createComponent({ showRole: true });
|
||||
});
|
||||
|
||||
it.each([{ revoke_path: null }, { revoke_path: undefined }])(
|
||||
'with %p, does not show revoke button',
|
||||
async (input) => {
|
||||
await triggerSuccess(defaultActiveAccessTokens.map((data) => ({ ...data, ...input })));
|
||||
|
||||
expect(findCells().at(6).findComponent(GlButton).exists()).toBe(false);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('sorts rows alphabetically', async () => {
|
||||
createComponent({ showRole: true });
|
||||
await triggerSuccess();
|
||||
|
|
|
@ -151,7 +151,7 @@ RSpec.describe ErrorTracking::SentryClient::Issue do
|
|||
|
||||
context 'with older sentry versions where keys are not present' do
|
||||
let(:sentry_api_response) do
|
||||
issues_sample_response[0...1].map do |issue|
|
||||
issues_sample_response.first(1).map do |issue|
|
||||
issue[:project].delete(:id)
|
||||
issue
|
||||
end
|
||||
|
@ -167,7 +167,7 @@ RSpec.describe ErrorTracking::SentryClient::Issue do
|
|||
|
||||
context 'when essential keys are missing in API response' do
|
||||
let(:sentry_api_response) do
|
||||
issues_sample_response[0...1].map do |issue|
|
||||
issues_sample_response.first(1).map do |issue|
|
||||
issue.except(:id)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -38,7 +38,7 @@ RSpec.describe ErrorTracking::SentryClient::Projects do
|
|||
|
||||
context 'essential keys missing in API response' do
|
||||
let(:sentry_api_response) do
|
||||
projects_sample_response[0...1].map do |project|
|
||||
projects_sample_response.first(1).map do |project|
|
||||
project.except(:slug)
|
||||
end
|
||||
end
|
||||
|
@ -50,7 +50,7 @@ RSpec.describe ErrorTracking::SentryClient::Projects do
|
|||
|
||||
context 'optional keys missing in sentry response' do
|
||||
let(:sentry_api_response) do
|
||||
projects_sample_response[0...1].map do |project|
|
||||
projects_sample_response.first(1).map do |project|
|
||||
project[:organization].delete(:id)
|
||||
project.delete(:id)
|
||||
project.except(:status)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::Cleanup::UnusedPersonalAccessTokens do
|
||||
RSpec.describe Gitlab::Cleanup::PersonalAccessTokens do
|
||||
let_it_be(:group) { create(:group) }
|
||||
let_it_be(:subgroup) { create(:group, parent: group) }
|
||||
let_it_be(:project_bot) { create(:user, :project_bot) }
|
||||
|
@ -16,6 +16,10 @@ RSpec.describe Gitlab::Cleanup::UnusedPersonalAccessTokens do
|
|||
create(:personal_access_token, created_at: last_used_at - 1.minute)
|
||||
end
|
||||
|
||||
let!(:old_actively_used_token) do
|
||||
create(:personal_access_token, created_at: last_used_at - 1.minute, last_used_at: 1.day.ago)
|
||||
end
|
||||
|
||||
let!(:old_unused_token_for_non_group_member) do
|
||||
create(:personal_access_token, created_at: last_used_at - 1.minute)
|
||||
end
|
||||
|
@ -37,6 +41,7 @@ RSpec.describe Gitlab::Cleanup::UnusedPersonalAccessTokens do
|
|||
|
||||
before do
|
||||
group.add_member(old_formerly_used_token.user, Gitlab::Access::DEVELOPER)
|
||||
group.add_member(old_actively_used_token.user, Gitlab::Access::DEVELOPER)
|
||||
group.add_member(unused_token.user, Gitlab::Access::DEVELOPER)
|
||||
group.add_member(old_unused_token.user, Gitlab::Access::DEVELOPER)
|
||||
group.add_member(project_bot, Gitlab::Access::MAINTAINER)
|
||||
|
@ -47,7 +52,7 @@ RSpec.describe Gitlab::Cleanup::UnusedPersonalAccessTokens do
|
|||
subject do
|
||||
described_class.new(
|
||||
logger: logger,
|
||||
last_used_before: last_used_at,
|
||||
cut_off_date: last_used_at,
|
||||
group_full_path: group_full_path
|
||||
)
|
||||
end
|
||||
|
@ -76,16 +81,42 @@ RSpec.describe Gitlab::Cleanup::UnusedPersonalAccessTokens do
|
|||
context 'in a real run' do
|
||||
let(:args) { { dry_run: false } }
|
||||
|
||||
it 'revokes only revocable tokens' do
|
||||
subject.run!(**args)
|
||||
context 'when revoking unused tokens' do
|
||||
it 'revokes human-owned tokens created and last used over 1 year ago' do
|
||||
subject.run!(**args)
|
||||
|
||||
expect(PersonalAccessToken.active).to contain_exactly(
|
||||
unused_token,
|
||||
old_unused_project_access_token,
|
||||
old_unused_token_for_non_group_member,
|
||||
old_unused_token_for_subgroup_member
|
||||
)
|
||||
expect(PersonalAccessToken.revoked).to contain_exactly(old_unused_token, old_formerly_used_token)
|
||||
expect(PersonalAccessToken.active).to contain_exactly(
|
||||
unused_token,
|
||||
old_actively_used_token,
|
||||
old_unused_project_access_token,
|
||||
old_unused_token_for_non_group_member,
|
||||
old_unused_token_for_subgroup_member
|
||||
)
|
||||
expect(PersonalAccessToken.revoked).to contain_exactly(
|
||||
old_unused_token,
|
||||
old_formerly_used_token
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when revoking used and unused tokens' do
|
||||
let(:args) { { dry_run: false, revoke_active_tokens: true } }
|
||||
|
||||
it 'revokes human-owned tokens created over 1 year ago' do
|
||||
subject.run!(**args)
|
||||
|
||||
expect(PersonalAccessToken.active).to contain_exactly(
|
||||
unused_token,
|
||||
old_unused_project_access_token,
|
||||
old_unused_token_for_non_group_member,
|
||||
old_unused_token_for_subgroup_member
|
||||
)
|
||||
expect(PersonalAccessToken.revoked).to contain_exactly(
|
||||
old_unused_token,
|
||||
old_actively_used_token,
|
||||
old_formerly_used_token
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
it 'updates updated_at' do
|
|
@ -318,6 +318,16 @@ RSpec.describe Environment, :use_clean_rails_memory_store_caching do
|
|||
end
|
||||
end
|
||||
|
||||
describe '.for_type' do
|
||||
it 'filters by type' do
|
||||
create(:environment)
|
||||
create(:environment, name: 'type1/prod')
|
||||
env = create(:environment, name: 'type2/prod')
|
||||
|
||||
expect(described_class.for_type('type2')).to contain_exactly(env)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#guess_tier' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
|
|
|
@ -65,6 +65,46 @@ RSpec.describe PersonalAccessToken do
|
|||
end
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
describe '.last_used_before' do
|
||||
let(:last_used_at) { 1.month.ago.beginning_of_hour }
|
||||
let!(:unused_token) { create(:personal_access_token) }
|
||||
|
|
Loading…
Reference in a new issue