Commit 7e0e61ea authored by Mark Chao's avatar Mark Chao

Merge branch '1007-add-ability-to-limit-the-lifetime-of-ssh-keys' into 'master'

Add a feature for setting the maximum allowable lifetime for SSH keys at an instance level

See merge request gitlab-org/gitlab!75098
parents eb3d9667 016865c2
...@@ -61,6 +61,11 @@ module ProfilesHelper ...@@ -61,6 +61,11 @@ module ProfilesHelper
def ssh_key_expires_field_description def ssh_key_expires_field_description
s_('Profiles|Key can still be used after expiration.') s_('Profiles|Key can still be used after expiration.')
end end
# Overridden in EE::ProfilesHelper#ssh_key_expiration_policy_enabled?
def ssh_key_expiration_policy_enabled?
false
end
end end
ProfilesHelper.prepend_mod ProfilesHelper.prepend_mod
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
= render_if_exists 'admin/application_settings/git_two_factor_session_expiry', form: f = render_if_exists 'admin/application_settings/git_two_factor_session_expiry', form: f
= render_if_exists 'admin/application_settings/personal_access_token_expiration_policy', form: f = render_if_exists 'admin/application_settings/personal_access_token_expiration_policy', form: f
= render_if_exists 'admin/application_settings/enforce_pat_expiration', form: f = render_if_exists 'admin/application_settings/enforce_pat_expiration', form: f
= render_if_exists 'admin/application_settings/ssh_key_expiration_policy', form: f
= render_if_exists 'admin/application_settings/enforce_ssh_key_expiration', form: f = render_if_exists 'admin/application_settings/enforce_ssh_key_expiration', form: f
.form-group .form-group
......
- max_date = ::Gitlab::CurrentSettings.max_ssh_key_lifetime_from_now.to_date if ssh_key_expiration_policy_enabled?
%div %div
= form_for [:profile, @key], html: { class: 'js-requires-input' } do |f| = form_for [:profile, @key], html: { class: 'js-requires-input' } do |f|
= form_errors(@key) = form_errors(@key)
...@@ -13,8 +14,8 @@ ...@@ -13,8 +14,8 @@
%p.form-text.text-muted= s_('Profiles|Give your individual key a title. This will be publicly visible.') %p.form-text.text-muted= s_('Profiles|Give your individual key a title. This will be publicly visible.')
.col.form-group .col.form-group
= f.label :expires_at, s_('Profiles|Expires at'), class: 'label-bold' = f.label :expires_at, s_('Profiles|Expiration date'), class: 'label-bold'
= f.date_field :expires_at, class: "form-control input-lg", min: Date.tomorrow, data: { qa_selector: 'key_expiry_date_field' } = f.date_field :expires_at, class: "form-control input-lg", min: Date.tomorrow, max: max_date, data: { qa_selector: 'key_expiry_date_field' }
%p.form-text.text-muted{ data: { qa_selector: 'key_expiry_date_field_description' } }= ssh_key_expires_field_description %p.form-text.text-muted{ data: { qa_selector: 'key_expiry_date_field_description' } }= ssh_key_expires_field_description
.js-add-ssh-key-validation-warning.hide .js-add-ssh-key-validation-warning.hide
......
# frozen_string_literal: true
class AddMaxSshKeyLifetimeToApplicationSettings < Gitlab::Database::Migration[1.0]
def change
add_column :application_settings, :max_ssh_key_lifetime, :integer
end
end
7686fd3e33b25b811aba459aba514cde8e88102277edb3be7e12378cb7e8de85
\ No newline at end of file
...@@ -10477,6 +10477,7 @@ CREATE TABLE application_settings ( ...@@ -10477,6 +10477,7 @@ CREATE TABLE application_settings (
sentry_dsn text, sentry_dsn text,
sentry_clientside_dsn text, sentry_clientside_dsn text,
sentry_environment text, sentry_environment text,
max_ssh_key_lifetime integer,
static_objects_external_storage_auth_token_encrypted text, static_objects_external_storage_auth_token_encrypted text,
CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)), CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)),
CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)), CONSTRAINT app_settings_dep_proxy_ttl_policies_worker_capacity_positive CHECK ((dependency_proxy_ttl_group_policy_worker_capacity >= 0)),
...@@ -343,6 +343,7 @@ listed in the descriptions of the relevant settings. ...@@ -343,6 +343,7 @@ listed in the descriptions of the relevant settings.
| `max_import_size` | integer | no | Maximum import size in MB. 0 for unlimited. Default = 0 (unlimited) [Modified](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50MB to 0 in GitLab 13.8. | | `max_import_size` | integer | no | Maximum import size in MB. 0 for unlimited. Default = 0 (unlimited) [Modified](https://gitlab.com/gitlab-org/gitlab/-/issues/251106) from 50MB to 0 in GitLab 13.8. |
| `max_pages_size` | integer | no | Maximum size of pages repositories in MB. | | `max_pages_size` | integer | no | Maximum size of pages repositories in MB. |
| `max_personal_access_token_lifetime` | integer | no | **(ULTIMATE SELF)** Maximum allowable lifetime for personal access tokens in days. | | `max_personal_access_token_lifetime` | integer | no | **(ULTIMATE SELF)** Maximum allowable lifetime for personal access tokens in days. |
| `max_ssh_key_lifetime` | integer | no | **(ULTIMATE SELF)** Maximum allowable lifetime for SSH keys in days. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1007) in GitLab 14.6. |
| `metrics_method_call_threshold` | integer | no | A method call is only tracked when it takes longer than the given amount of milliseconds. | | `metrics_method_call_threshold` | integer | no | A method call is only tracked when it takes longer than the given amount of milliseconds. |
| `mirror_available` | boolean | no | Allow repository mirroring to configured by project Maintainers. If disabled, only Administrators can configure repository mirroring. | | `mirror_available` | boolean | no | Allow repository mirroring to configured by project Maintainers. If disabled, only Administrators can configure repository mirroring. |
| `mirror_capacity_threshold` | integer | no | **(PREMIUM)** Minimum capacity to be available before scheduling more mirrors preemptively. | | `mirror_capacity_threshold` | integer | no | **(PREMIUM)** Minimum capacity to be available before scheduling more mirrors preemptively. |
......
...@@ -192,38 +192,45 @@ To set a limit on how long these sessions are valid: ...@@ -192,38 +192,45 @@ To set a limit on how long these sessions are valid:
1. Fill in the **Session duration for Git operations when 2FA is enabled (minutes)** field. 1. Fill in the **Session duration for Git operations when 2FA is enabled (minutes)** field.
1. Click **Save changes**. 1. Click **Save changes**.
## Limit the lifetime of personal access tokens **(ULTIMATE SELF)** ## Limit the lifetime of SSH keys **(ULTIMATE SELF)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3649) in GitLab 12.6. > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/1007) in GitLab 14.6 [with a flag](../../../administration/feature_flags.md) named `ff_limit_ssh_key_lifetime`. Disabled by default.
FLAG:
On self-managed GitLab, by default this feature is not available. To make it available,
ask an administrator to [enable the feature flag](../../../administration/feature_flags.md) named `ff_limit_ssh_key_lifetime`.
On GitLab.com, this feature is not available. The feature is not ready for production use.
Users can optionally specify a lifetime for Users can optionally specify a lifetime for
[personal access tokens](../../profile/personal_access_tokens.md). [SSH keys](../../../ssh/index.md).
This lifetime is not a requirement, and can be set to any arbitrary number of days. This lifetime is not a requirement, and can be set to any arbitrary number of days.
Personal access tokens are the only tokens needed for programmatic access to GitLab. SSH keys are user credentials to access GitLab.
However, organizations with security requirements may want to enforce more protection by However, organizations with security requirements may want to enforce more protection by
requiring the regular rotation of these tokens. requiring the regular rotation of these keys.
### Set a lifetime ### Set a lifetime
Only a GitLab administrator can set a lifetime. Leaving it empty means Only a GitLab administrator can set a lifetime. Leaving it empty means
there are no restrictions. there are no restrictions.
To set a lifetime on how long personal access tokens are valid: To set a lifetime on how long SSH keys are valid:
1. On the top bar, select **Menu > Admin**. 1. On the top bar, select **Menu > Admin**.
1. On the left sidebar, select **Settings > General**. 1. On the left sidebar, select **Settings > General**.
1. Expand the **Account and limit** section. 1. Expand the **Account and limit** section.
1. Fill in the **Maximum allowable lifetime for personal access tokens (days)** field. 1. Fill in the **Maximum allowable lifetime for SSH keys (days)** field.
1. Click **Save changes**. 1. Click **Save changes**.
Once a lifetime for personal access tokens is set, GitLab: Once a lifetime for SSH keys is set, GitLab:
- Applies the lifetime for new personal access tokens, and require users to set an expiration date - Requires users to set an expiration date that is no later than the allowed lifetime on new
and a date no later than the allowed lifetime. SSH keys.
- After three hours, revoke old tokens with no expiration date or with a lifetime longer than the - Applies the lifetime restriction to existing SSH keys. Keys with no expiry or a lifetime
allowed lifetime. Three hours is given to allow administrators to change the allowed lifetime, greater than the maximum immediately become invalid.
or remove it, before revocation takes place.
NOTE:
When a user's SSH key becomes invalid they can delete and re-add the same key again.
## Allow expired SSH keys to be used **(ULTIMATE SELF)** ## Allow expired SSH keys to be used **(ULTIMATE SELF)**
...@@ -241,6 +248,39 @@ To allow the use of expired SSH keys: ...@@ -241,6 +248,39 @@ To allow the use of expired SSH keys:
Disabling SSH key expiration immediately enables all expired SSH keys. Disabling SSH key expiration immediately enables all expired SSH keys.
## Limit the lifetime of personal access tokens **(ULTIMATE SELF)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3649) in GitLab 12.6.
Users can optionally specify a lifetime for
[personal access tokens](../../profile/personal_access_tokens.md).
This lifetime is not a requirement, and can be set to any arbitrary number of days.
Personal access tokens are the only tokens needed for programmatic access to GitLab.
However, organizations with security requirements may want to enforce more protection by
requiring the regular rotation of these tokens.
### Set a lifetime
Only a GitLab administrator can set a lifetime. Leaving it empty means
there are no restrictions.
To set a lifetime on how long personal access tokens are valid:
1. On the top bar, select **Menu > Admin**.
1. On the left sidebar, select **Settings > General**.
1. Expand the **Account and limit** section.
1. Fill in the **Maximum allowable lifetime for personal access tokens (days)** field.
1. Click **Save changes**.
Once a lifetime for personal access tokens is set, GitLab:
- Applies the lifetime for new personal access tokens, and require users to set an expiration date
and a date no later than the allowed lifetime.
- After three hours, revoke old tokens with no expiration date or with a lifetime longer than the
allowed lifetime. Three hours is given to allow administrators to change the allowed lifetime,
or remove it, before revocation takes place.
## Allow expired Personal Access Tokens to be used **(ULTIMATE SELF)** ## Allow expired Personal Access Tokens to be used **(ULTIMATE SELF)**
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214723) in GitLab 13.1. > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214723) in GitLab 13.1.
......
...@@ -58,6 +58,7 @@ module EE ...@@ -58,6 +58,7 @@ module EE
:help_text, :help_text,
:lock_memberships_to_ldap, :lock_memberships_to_ldap,
:max_personal_access_token_lifetime, :max_personal_access_token_lifetime,
:max_ssh_key_lifetime,
:pseudonymizer_enabled, :pseudonymizer_enabled,
:repository_size_limit, :repository_size_limit,
:secret_detection_token_revocation_enabled, :secret_detection_token_revocation_enabled,
......
...@@ -15,7 +15,20 @@ module EE ...@@ -15,7 +15,20 @@ module EE
def ssh_key_expires_field_description def ssh_key_expires_field_description
return super unless ::Key.expiration_enforced? return super unless ::Key.expiration_enforced?
s_('Profiles|Key will be deleted on this date.') if ssh_key_expiration_policy_enabled?
s_('Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days') % { max_ssh_key_lifetime: ::Gitlab::CurrentSettings.max_ssh_key_lifetime }
else
s_('Profiles|Key becomes invalid on this date.')
end
end
def ssh_key_expiration_policy_licensed?
License.feature_available?(:ssh_key_expiration_policy) && ::Feature.enabled?(:ff_limit_ssh_key_lifetime)
end
override :ssh_key_expiration_policy_enabled?
def ssh_key_expiration_policy_enabled?
::Gitlab::CurrentSettings.max_ssh_key_lifetime && ssh_key_expiration_policy_licensed? && ::Feature.enabled?(:ff_limit_ssh_key_lifetime)
end end
end end
end end
...@@ -116,6 +116,10 @@ module EE ...@@ -116,6 +116,10 @@ module EE
presence: true, presence: true,
numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 10080 } numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 10080 }
validates :max_ssh_key_lifetime,
allow_blank: true,
numericality: { only_integer: true, greater_than: 0, less_than_or_equal_to: 365 }
alias_attribute :delayed_project_deletion, :delayed_project_removal alias_attribute :delayed_project_deletion, :delayed_project_removal
after_commit :update_personal_access_tokens_lifetime, if: :saved_change_to_max_personal_access_token_lifetime? after_commit :update_personal_access_tokens_lifetime, if: :saved_change_to_max_personal_access_token_lifetime?
...@@ -155,6 +159,7 @@ module EE ...@@ -155,6 +159,7 @@ module EE
lock_memberships_to_ldap: false, lock_memberships_to_ldap: false,
maintenance_mode: false, maintenance_mode: false,
max_personal_access_token_lifetime: nil, max_personal_access_token_lifetime: nil,
max_ssh_key_lifetime: nil,
mirror_capacity_threshold: Settings.gitlab['mirror_capacity_threshold'], mirror_capacity_threshold: Settings.gitlab['mirror_capacity_threshold'],
mirror_max_capacity: Settings.gitlab['mirror_max_capacity'], mirror_max_capacity: Settings.gitlab['mirror_max_capacity'],
mirror_max_delay: Settings.gitlab['mirror_max_delay'], mirror_max_delay: Settings.gitlab['mirror_max_delay'],
...@@ -367,6 +372,10 @@ module EE ...@@ -367,6 +372,10 @@ module EE
max_personal_access_token_lifetime&.days&.from_now max_personal_access_token_lifetime&.days&.from_now
end end
def max_ssh_key_lifetime_from_now
max_ssh_key_lifetime&.days&.from_now
end
def compliance_frameworks=(values) def compliance_frameworks=(values)
cleaned = Array.wrap(values).reject(&:blank?).sort.uniq cleaned = Array.wrap(values).reject(&:blank?).sort.uniq
......
...@@ -5,6 +5,7 @@ module EE ...@@ -5,6 +5,7 @@ module EE
extend ActiveSupport::Concern extend ActiveSupport::Concern
include Auditable include Auditable
include ProfilesHelper
prepended do prepended do
include UsageStatistics include UsageStatistics
...@@ -13,6 +14,10 @@ module EE ...@@ -13,6 +14,10 @@ module EE
validate :expiration, if: -> { ::Key.expiration_enforced? } validate :expiration, if: -> { ::Key.expiration_enforced? }
with_options if: :ssh_key_expiration_policy_enabled? do
validate :validate_expires_at_before_max_expiry_date
end
def expiration def expiration
errors.add(:key, :expired_and_enforced, message: 'has expired and the instance administrator has enforced expiration') if expired? errors.add(:key, :expired_and_enforced, message: 'has expired and the instance administrator has enforced expiration') if expired?
end end
...@@ -26,6 +31,14 @@ module EE ...@@ -26,6 +31,14 @@ module EE
errors.map(&:type).reject { |t| t.eql?(:expired_and_enforced) }.empty? errors.map(&:type).reject { |t| t.eql?(:expired_and_enforced) }.empty?
end end
def validate_expires_at_before_max_expiry_date
return errors.add(:key, message: 'has no expiration date but an expiration date is required for SSH keys on this instance. Contact the instance administrator.') if expires_at.blank?
# when the key is not yet persisted the `created_at` field is nil
max_expiry_date = (created_at.presence || Time.current) + ::Gitlab::CurrentSettings.max_ssh_key_lifetime.days
errors.add(:key, message: 'has an invalid expiration date. Set a shorter lifetime for the key or contact the instance administrator.') if expires_at > max_expiry_date
end
end end
class_methods do class_methods do
......
...@@ -192,6 +192,7 @@ class License < ApplicationRecord ...@@ -192,6 +192,7 @@ class License < ApplicationRecord
security_dashboard security_dashboard
security_on_demand_scans security_on_demand_scans
security_orchestration_policies security_orchestration_policies
ssh_key_expiration_policy
status_page status_page
subepics subepics
threat_monitoring threat_monitoring
......
- return unless ssh_key_expiration_policy_licensed?
- form = local_assigns.fetch(:form)
.form-group
= form.label :max_ssh_key_lifetime, _('Maximum allowed lifetime for SSH keys (in days)'), class: 'label-light'
= form.number_field :max_ssh_key_lifetime, class: 'form-control gl-form-input input-xs'
%span.form-text.text-muted#max_ssh_key_lifetime= _('When enabled, SSH keys with no expiry date or an invalid expiration date are no longer accepted. Leave blank for no limit.')
---
name: ff_limit_ssh_key_lifetime
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/75098
rollout_issue_url:
milestone: '14.6'
type: development
group: group::compliance
default_enabled: false
...@@ -51,7 +51,7 @@ RSpec.describe ProfilesHelper do ...@@ -51,7 +51,7 @@ RSpec.describe ProfilesHelper do
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
where(:expiration_enforced, :result) do where(:expiration_enforced, :result) do
true | "Key will be deleted on this date." true | "Key becomes invalid on this date."
false | "Key can still be used after expiration." false | "Key can still be used after expiration."
end end
...@@ -63,4 +63,74 @@ RSpec.describe ProfilesHelper do ...@@ -63,4 +63,74 @@ RSpec.describe ProfilesHelper do
end end
end end
end end
describe '#ssh_key_expiration_policy_licensed?' do
subject { helper.ssh_key_expiration_policy_licensed? }
context 'when is not licensed' do
before do
stub_licensed_features(ssh_key_expiration_policy: false)
end
it { is_expected.to be_falsey }
end
context 'when is licensed' do
before do
stub_licensed_features(ssh_key_expiration_policy: true)
end
it { is_expected.to be_truthy }
end
end
describe '#ssh_key_expiration_policy_enabled?' do
subject { helper.ssh_key_expiration_policy_enabled? }
context 'when feature flag is enabled' do
before do
stub_feature_flags(ff_limit_ssh_key_lifetime: true)
end
context 'when is licensed and used' do
before do
stub_licensed_features(ssh_key_expiration_policy: true)
stub_application_setting(max_ssh_key_lifetime: 10)
end
it { is_expected.to be_truthy }
end
context 'when is not licensed' do
before do
stub_licensed_features(ssh_key_expiration_policy: false)
end
it { is_expected.to be_falsey }
end
context 'when is licensed but not used' do
before do
stub_licensed_features(ssh_key_expiration_policy: true)
stub_application_setting(max_ssh_key_lifetime: nil)
end
it { is_expected.to be_falsey }
end
end
context 'when feature flag is disabled' do
before do
stub_feature_flags(ff_limit_ssh_key_lifetime: false)
end
context 'when is licensed and used' do
before do
stub_licensed_features(ssh_key_expiration_policy: true)
stub_application_setting(max_ssh_key_lifetime: 10)
end
it { is_expected.to be_falsey }
end
end
end
end end
...@@ -96,6 +96,8 @@ RSpec.describe ApplicationSetting do ...@@ -96,6 +96,8 @@ RSpec.describe ApplicationSetting do
it { is_expected.not_to allow_value(0).for(:git_two_factor_session_expiry) } it { is_expected.not_to allow_value(0).for(:git_two_factor_session_expiry) }
it { is_expected.not_to allow_value(10081).for(:git_two_factor_session_expiry) } it { is_expected.not_to allow_value(10081).for(:git_two_factor_session_expiry) }
it { is_expected.to validate_numericality_of(:max_ssh_key_lifetime).is_greater_than(0).is_less_than_or_equal_to(365).allow_nil }
describe 'when additional email text is enabled' do describe 'when additional email text is enabled' do
before do before do
stub_licensed_features(email_additional_text: true) stub_licensed_features(email_additional_text: true)
...@@ -816,4 +818,36 @@ RSpec.describe ApplicationSetting do ...@@ -816,4 +818,36 @@ RSpec.describe ApplicationSetting do
expect(subject.maintenance_mode).to be false expect(subject.maintenance_mode).to be false
end end
end end
describe "#max_ssh_key_lifetime_from_now", :freeze_time do
subject { setting.max_ssh_key_lifetime_from_now }
let(:days_from_now) { nil }
before do
stub_application_setting(max_ssh_key_lifetime: days_from_now)
end
context 'when max_ssh_key_lifetime is defined' do
let(:days_from_now) { 30 }
it 'is a date time' do
expect(subject).to be_a Time
end
it 'is in the future' do
expect(subject).to be > Time.zone.now
end
it 'is in days_from_now' do
expect(subject.to_date - Date.today).to eq days_from_now
end
end
context 'when max_ssh_key_lifetime is nil' do
it 'is nil' do
expect(subject).to be_nil
end
end
end
end end
...@@ -22,6 +22,53 @@ RSpec.describe Key do ...@@ -22,6 +22,53 @@ RSpec.describe Key do
end end
end end
end end
describe '#validate_expires_at_before_max_expiry_date' do
using RSpec::Parameterized::TableSyntax
context 'for a range of key expiry combinations' do
where(:key, :max_ssh_key_lifetime, :valid) do
build(:personal_key, created_at: Time.current, expires_at: nil) | nil | true
build(:personal_key, created_at: Time.current, expires_at: 20.days.from_now) | nil | true
build(:personal_key, created_at: 1.day.ago, expires_at: 20.days.from_now) | 10 | false
build(:personal_key, created_at: 6.days.ago, expires_at: 3.days.from_now) | 10 | true
build(:personal_key, created_at: 10.days.ago, expires_at: 7.days.from_now) | 10 | false
build(:personal_key, created_at: Time.current, expires_at: nil) | 20 | false
build(:personal_key, expires_at: nil) | 30 | false
end
with_them do
before do
stub_licensed_features(ssh_key_expiration_policy: true)
stub_application_setting(max_ssh_key_lifetime: max_ssh_key_lifetime)
end
it 'checks if ssh key expiration is valid' do
expect(key.valid?).to eq(valid)
end
end
end
context 'when keys and max expiry are set' do
where(:key, :max_ssh_key_lifetime, :valid) do
build(:personal_key, created_at: Time.current, expires_at: 20.days.from_now) | 10 | false
build(:personal_key, created_at: Time.current, expires_at: 7.days.from_now) | 10 | true
end
with_them do
before do
stub_licensed_features(ssh_key_expiration_policy: true)
stub_application_setting(max_ssh_key_lifetime: max_ssh_key_lifetime)
end
it 'checks validity properly in the future too' do
# Travel to the day before the key is set to 'expire'.
# max_ssh_key_lifetime should still be enforced correctly.
travel_to(key.expires_at - 1) do
expect(key.valid?).to eq(valid)
end
end
end
end
end
end end
describe 'only_expired_and_enforced?' do describe 'only_expired_and_enforced?' do
......
...@@ -21496,6 +21496,9 @@ msgstr "" ...@@ -21496,6 +21496,9 @@ msgstr ""
msgid "Maximum allowable lifetime for personal access token (days)" msgid "Maximum allowable lifetime for personal access token (days)"
msgstr "" msgstr ""
msgid "Maximum allowed lifetime for SSH keys (in days)"
msgstr ""
msgid "Maximum artifacts size" msgid "Maximum artifacts size"
msgstr "" msgstr ""
...@@ -26705,13 +26708,13 @@ msgstr "" ...@@ -26705,13 +26708,13 @@ msgstr ""
msgid "Profiles|Enter your pronouns to let people know how to refer to you" msgid "Profiles|Enter your pronouns to let people know how to refer to you"
msgstr "" msgstr ""
msgid "Profiles|Expired key is not valid." msgid "Profiles|Expiration date"
msgstr "" msgstr ""
msgid "Profiles|Expired:" msgid "Profiles|Expired key is not valid."
msgstr "" msgstr ""
msgid "Profiles|Expires at" msgid "Profiles|Expired:"
msgstr "" msgstr ""
msgid "Profiles|Expires:" msgid "Profiles|Expires:"
...@@ -26753,13 +26756,16 @@ msgstr "" ...@@ -26753,13 +26756,16 @@ msgstr ""
msgid "Profiles|Key" msgid "Profiles|Key"
msgstr "" msgstr ""
msgid "Profiles|Key can still be used after expiration." msgid "Profiles|Key becomes invalid on this date."
msgstr "" msgstr ""
msgid "Profiles|Key usable beyond expiration date." msgid "Profiles|Key becomes invalid on this date. Maximum lifetime for SSH keys is %{max_ssh_key_lifetime} days"
msgstr "" msgstr ""
msgid "Profiles|Key will be deleted on this date." msgid "Profiles|Key can still be used after expiration."
msgstr ""
msgid "Profiles|Key usable beyond expiration date."
msgstr "" msgstr ""
msgid "Profiles|Last used:" msgid "Profiles|Last used:"
...@@ -39221,6 +39227,9 @@ msgstr "" ...@@ -39221,6 +39227,9 @@ msgstr ""
msgid "When a runner is locked, it cannot be assigned to other projects" msgid "When a runner is locked, it cannot be assigned to other projects"
msgstr "" msgstr ""
msgid "When enabled, SSH keys with no expiry date or an invalid expiration date are no longer accepted. Leave blank for no limit."
msgstr ""
msgid "When enabled, existing personal access tokens may be revoked. Leave blank for no limit." msgid "When enabled, existing personal access tokens may be revoked. Leave blank for no limit."
msgstr "" msgstr ""
......
...@@ -33,8 +33,8 @@ RSpec.describe 'profiles/keys/_form.html.haml' do ...@@ -33,8 +33,8 @@ RSpec.describe 'profiles/keys/_form.html.haml' do
end end
it 'has the expires at field', :aggregate_failures do it 'has the expires at field', :aggregate_failures do
expect(rendered).to have_field('Expires at', type: 'date') expect(rendered).to have_field('Expiration date', type: 'date')
expect(page.find_field('Expires at')['min']).to eq(l(1.day.from_now, format: "%Y-%m-%d")) expect(page.find_field('Expiration date')['min']).to eq(l(1.day.from_now, format: "%Y-%m-%d"))
expect(rendered).to have_text('Key can still be used after expiration.') expect(rendered).to have_text('Key can still be used after expiration.')
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment