diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index edb9a2053b12220acb2200cd50534dce60a27f81..d6f729891883a8411c65b45f992337ed7117fdd0 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -791,6 +791,10 @@ def kroki_format_supported?(diagram_type) ::AsciidoctorExtensions::Kroki::SUPPORTED_DIAGRAM_NAMES.include?(diagram_type) end + def personal_access_tokens_disabled? + false + end + private def parsed_grafana_url diff --git a/doc/user/group/settings/group_access_tokens.md b/doc/user/group/settings/group_access_tokens.md index a22658bd60292a3502137067cc01af7e66da06a4..64cbe68a9d45604a3c4e81415b21b46b64eef3fd 100644 --- a/doc/user/group/settings/group_access_tokens.md +++ b/doc/user/group/settings/group_access_tokens.md @@ -48,6 +48,9 @@ You cannot use group access tokens to create other group, project, or personal a Group access tokens inherit the [default prefix setting](../../admin_area/settings/account_and_limit_settings.md#personal-access-token-prefix) configured for personal access tokens. +NOTE: +Group access tokens are not FIPS compliant and creation and use are disabled when [FIPS mode](../../../development/fips_compliance.md) is enabled. + ## Create a group access token using UI > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/214045) in GitLab 14.7. diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md index 17c5e1dbfa7819e6bf9928018b8c02c69c3d2fec..29dce202a3764e292454d9e4982478dd7cc22a2c 100644 --- a/doc/user/profile/personal_access_tokens.md +++ b/doc/user/profile/personal_access_tokens.md @@ -45,6 +45,9 @@ For examples of how you can use a personal access token to authenticate with the Alternately, GitLab administrators can use the API to create [impersonation tokens](../../api/index.md#impersonation-tokens). Use impersonation tokens to automate authentication as a specific user. +NOTE: +Personal access tokens are not FIPS compliant and creation and use are disabled when [FIPS mode](../../development/fips_compliance.md) is enabled. + ## Create a personal access token > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/348660) in GitLab 15.3, default expiration of 30 days is populated in the UI. diff --git a/doc/user/project/settings/project_access_tokens.md b/doc/user/project/settings/project_access_tokens.md index 16a884270ffb75accb3a83192ba3882ccd5ee3e2..c1652251b9f6d458913688769549892ca3151b0d 100644 --- a/doc/user/project/settings/project_access_tokens.md +++ b/doc/user/project/settings/project_access_tokens.md @@ -48,6 +48,9 @@ You cannot use project access tokens to create other group, project, or personal Project access tokens inherit the [default prefix setting](../../admin_area/settings/account_and_limit_settings.md#personal-access-token-prefix) configured for personal access tokens. +NOTE: +Project access tokens are not FIPS compliant and creation and use are disabled when [FIPS mode](../../../development/fips_compliance.md) is enabled. + ## Create a project access token > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89114) in GitLab 15.1, Owners can select Owner role for project access tokens. diff --git a/ee/app/models/ee/application_setting.rb b/ee/app/models/ee/application_setting.rb index 124c649dd8ad6311ee5dc0f824a61bf6b1008916..5c119468006eb75fb85bba327d2e0ae0a15572c1 100644 --- a/ee/app/models/ee/application_setting.rb +++ b/ee/app/models/ee/application_setting.rb @@ -415,6 +415,16 @@ def should_apply_user_signup_cap? ::Gitlab::CurrentSettings.new_user_signups_cap.present? end + override :personal_access_tokens_disabled? + def personal_access_tokens_disabled? + License.feature_available?(:fips_disable_personal_access_tokens) && ::Gitlab::FIPS.enabled? + end + + def disable_feed_token + personal_access_tokens_disabled? || read_attribute(:disable_feed_token) + end + alias_method :disable_feed_token?, :disable_feed_token + private def elasticsearch_limited_project_exists?(project) diff --git a/ee/app/models/ee/personal_access_token.rb b/ee/app/models/ee/personal_access_token.rb index 528798296709022cb11c3a38c4f516d6d5c49627..d7b4abb491eab7f7b98e01ecbcf7f9a7a508141e 100644 --- a/ee/app/models/ee/personal_access_token.rb +++ b/ee/app/models/ee/personal_access_token.rb @@ -27,6 +27,8 @@ module PersonalAccessToken end class_methods do + extend ::Gitlab::Utils::Override + def pluck_names pluck(:name) end @@ -39,6 +41,12 @@ def with_invalid_expires_at(max_lifetime, limit = 1_000) ] ) end + + # Disable lookup by token (token auth) when PATs disabled (FIPS) + override :find_by_token + def find_by_token(token) + super unless ::Gitlab::CurrentSettings.personal_access_tokens_disabled? + end end override :revoke! diff --git a/ee/app/models/gitlab_subscriptions/features.rb b/ee/app/models/gitlab_subscriptions/features.rb index b8bc869ce3a3fadc3c44ebe18b8e7062d418b1bd..caa780ad6c5d7ecfcf5a66cd8fcf78da9c2049bf 100644 --- a/ee/app/models/gitlab_subscriptions/features.rb +++ b/ee/app/models/gitlab_subscriptions/features.rb @@ -106,6 +106,7 @@ class Features feature_flags_related_issues feature_flags_code_references file_locks + fips_disable_personal_access_tokens geo generic_alert_fingerprinting git_two_factor_enforcement diff --git a/ee/spec/lib/ee/gitlab/auth/auth_finders_spec.rb b/ee/spec/lib/ee/gitlab/auth/auth_finders_spec.rb index 8987c8ac4995db941f799a90fe172465b26ad90e..160c0faf537df64762c5b94feddfdc46dae4044e 100644 --- a/ee/spec/lib/ee/gitlab/auth/auth_finders_spec.rb +++ b/ee/spec/lib/ee/gitlab/auth/auth_finders_spec.rb @@ -2,11 +2,11 @@ require 'spec_helper' -RSpec.describe EE::Gitlab::Auth::AuthFinders do +RSpec.describe Gitlab::Auth::AuthFinders do include described_class include ::EE::GeoHelpers - let(:current_request) { ActionDispatch::Request.new(env) } + let(:request) { ActionDispatch::Request.new(env) } let(:env) do { 'rack.input' => '' @@ -31,7 +31,7 @@ stub_current_geo_node(primary) env['SCRIPT_NAME'] = path - current_request.headers['Authorization'] = authorization_header + request.headers['Authorization'] = authorization_header end it { is_expected.to eq(user) } @@ -106,4 +106,81 @@ end end end + + describe '#find_user_from_bearer_token' do + context 'with a personal access token' do + before do + env[described_class::PRIVATE_TOKEN_HEADER] = create(:personal_access_token, user: user).token + end + + it 'returns user' do + expect(find_user_from_bearer_token).to eq user + end + + context 'when FIPS mode is enabled', :fips_mode do + before do + stub_licensed_features(fips_disable_personal_access_tokens: true) + end + + it 'raises unauthorized error' do + expect { find_user_from_bearer_token }.to raise_error(Gitlab::Auth::UnauthorizedError) + end + end + end + end + + describe '#find_user_from_access_token' do + before do + env[described_class::PRIVATE_TOKEN_HEADER] = create(:personal_access_token, user: user).token + end + + context 'when validate_access_token! returns valid' do + it 'returns user' do + expect(find_user_from_access_token).to eq user + end + + context 'when FIPS mode is enabled', :fips_mode do + before do + stub_licensed_features(fips_disable_personal_access_tokens: true) + end + + it 'raised unauthorized error' do + expect { find_user_from_access_token }.to raise_error(Gitlab::Auth::UnauthorizedError) + end + end + end + end + + describe '#find_user_from_feed_token' do + context 'when the request format is atom' do + before do + env['SCRIPT_NAME'] = 'url.atom' + env['HTTP_ACCEPT'] = 'application/atom+xml' + end + + context 'when feed_token param is provided' do + context 'when the feed token is valid' do + before do + request.update_param(:feed_token, user.feed_token) + end + + context 'when FIPS mode is enabled', :fips_mode do + it 'returns user' do + expect(find_user_from_feed_token(:rss)).to eq user + end + + context 'when fips_disable_personal_access_tokens feature is licensed' do + before do + stub_licensed_features(fips_disable_personal_access_tokens: true) + end + + it 'returns nil' do + expect(find_user_from_feed_token(:rss)).to be_nil + end + end + end + end + end + end + end end diff --git a/ee/spec/lib/gitlab/auth_spec.rb b/ee/spec/lib/gitlab/auth_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..9ba1a9eeae0a8e77744545a9e6978885f025f63f --- /dev/null +++ b/ee/spec/lib/gitlab/auth_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Auth, :use_clean_rails_memory_store_caching do + let_it_be(:project) { create(:project) } + + let(:auth_failure) { { actor: nil, project: nil, type: nil, authentication_abilities: nil } } + let(:gl_auth) { described_class } + + context 'when FIPS mode is enabled', :fips_mode do + before do + stub_licensed_features(fips_disable_personal_access_tokens: true) + end + + it 'fails authentication when using personal access tokens' do + personal_access_token = create(:personal_access_token, scopes: ['api']) + + expect(gl_auth.find_for_git_client('', personal_access_token.token, project: nil, ip: 'ip')) + .to have_attributes(auth_failure) + end + + it 'fails authentication when using impersonation tokens' do + impersonation_token = create(:personal_access_token, :impersonation, scopes: ['api']) + + expect(gl_auth.find_for_git_client('', impersonation_token.token, project: nil, ip: 'ip')) + .to have_attributes(auth_failure) + end + + it 'fails authentication when using a resource access token' do + project_bot_user = create(:user, :project_bot) + access_token = create(:personal_access_token, user: project_bot_user) + + expect(gl_auth.find_for_git_client(project_bot_user.username, access_token.token, project: project, ip: 'ip')) + .to have_attributes(auth_failure) + end + end +end diff --git a/ee/spec/models/application_setting_spec.rb b/ee/spec/models/application_setting_spec.rb index fa54271a255eda9a7f7724a2a3abc6e20498d924..c4f7d980956067defa293c30cf311cfa4b14d60b 100644 --- a/ee/spec/models/application_setting_spec.rb +++ b/ee/spec/models/application_setting_spec.rb @@ -961,4 +961,50 @@ def expect_is_es_licensed it { is_expected.to allow_value(true).for(:delayed_project_removal) } end end + + describe '#personal_access_tokens_disabled?' do + subject { setting.personal_access_tokens_disabled? } + + context 'when the licensed feature is not available' do + before do + stub_licensed_features(fips_disable_personal_access_tokens: false) + end + + it { is_expected.to eq(false) } + end + + context 'when the licensed feature is available' do + before do + stub_licensed_features(fips_disable_personal_access_tokens: true) + end + + context 'when FIPS mode is disabled', fips_mode: false do + it { is_expected.to eq(false) } + end + + context 'when FIPS mode is enabled', :fips_mode do + it { is_expected.to eq(true) } + end + end + end + + describe '#disable_feed_token' do + subject { setting.disable_feed_token } + + before do + setting.update!(disable_feed_token: false) + end + + context 'when personal access tokens are disabled', :fips_mode do + before do + stub_licensed_features(fips_disable_personal_access_tokens: true) + end + + it { is_expected.to eq(true) } + end + + context 'when personal access tokens are enabled' do + it { is_expected.to eq(false) } + end + end end diff --git a/ee/spec/models/ee/personal_access_token_spec.rb b/ee/spec/models/ee/personal_access_token_spec.rb index 7fb6c783a6eaf3c197efc3ee4e399c6b362c585e..8b8356b6e56819d26c8498e13eb44302e6583cb5 100644 --- a/ee/spec/models/ee/personal_access_token_spec.rb +++ b/ee/spec/models/ee/personal_access_token_spec.rb @@ -209,6 +209,24 @@ end end + describe '.find_by_token' do + let!(:token) { create(:personal_access_token) } + + it 'finds the token' do + expect(described_class.find_by_token(token.token)).to eq(token) + end + + context 'when FIPS mode is enabled', :fips_mode do + before do + stub_licensed_features(fips_disable_personal_access_tokens: true) + end + + it 'does not find the token' do + expect(described_class.find_by_token(token.token)).to be_nil + end + end + end + shared_context 'write to cache' do let_it_be(:pat) { create(:personal_access_token) } let_it_be(:cache_keys) { %w(token_expired_rotation token_expiring_rotation) } diff --git a/spec/lib/gitlab/auth/auth_finders_spec.rb b/spec/lib/gitlab/auth/auth_finders_spec.rb index d0b44135a2f0f97623e968808f868e3604553e58..e2226952d15a570ff59c970788b2e3de71ee4a0f 100644 --- a/spec/lib/gitlab/auth/auth_finders_spec.rb +++ b/spec/lib/gitlab/auth/auth_finders_spec.rb @@ -188,7 +188,7 @@ def set_bearer_token(token) end it 'returns nil if valid feed_token and disabled' do - stub_application_setting(disable_feed_token: true) + allow(Gitlab::CurrentSettings).to receive_messages(disable_feed_token: true) set_param(:feed_token, user.feed_token) expect(find_user_from_feed_token(:rss)).to be_nil diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index b5f153e7add5a66246e5966984b7afeaaa2a5683..db64e8439a5d9a7c2736af84e9f65101b092ab91 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -1453,4 +1453,10 @@ def expect_invalid expect(setting.personal_access_token_prefix).to eql('glpat-') end end + + describe '.personal_access_tokens_disabled?' do + it 'is false' do + expect(setting.personal_access_tokens_disabled?).to eq(false) + end + end end