Commit 2061aa39 authored by Andreas Brandl's avatar Andreas Brandl

Merge branch 'if-57131-external_auth_to_ce-ee' into 'master'

Move "Authorize project access with external service" to Core

See merge request gitlab-org/gitlab-ee!10437
parents fd00a835 980b8292
...@@ -67,6 +67,10 @@ ...@@ -67,6 +67,10 @@
} }
} }
.classification-label {
background-color: $red-500;
}
.toggle-wrapper { .toggle-wrapper {
margin-top: 5px; margin-top: 5px;
} }
......
...@@ -124,7 +124,9 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -124,7 +124,9 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
end end
def visible_application_setting_attributes def visible_application_setting_attributes
ApplicationSettingsHelper.visible_attributes + [ [
*::ApplicationSettingsHelper.visible_attributes,
*::ApplicationSettingsHelper.external_authorization_service_attributes,
:domain_blacklist_file, :domain_blacklist_file,
disabled_oauth_sign_in_sources: [], disabled_oauth_sign_in_sources: [],
import_sources: [], import_sources: [],
......
# frozen_string_literal: true # frozen_string_literal: true
module ProjectUnauthorized module ProjectUnauthorized
extend ActiveSupport::Concern
# EE would override this
def project_unauthorized_proc def project_unauthorized_proc
# no-op lambda do |project|
if project
label = project.external_authorization_classification_label
rejection_reason = nil
unless ::Gitlab::ExternalAuthorization.access_allowed?(current_user, label)
rejection_reason = ::Gitlab::ExternalAuthorization.rejection_reason(current_user, label)
rejection_reason ||= _('External authorization denied access to this project')
end end
end
ProjectUnauthorized.prepend(EE::ProjectUnauthorized) if rejection_reason
access_denied!(rejection_reason)
end
end
end
end
end
...@@ -345,6 +345,7 @@ class ProjectsController < Projects::ApplicationController ...@@ -345,6 +345,7 @@ class ProjectsController < Projects::ApplicationController
:container_registry_enabled, :container_registry_enabled,
:default_branch, :default_branch,
:description, :description,
:external_authorization_classification_label,
:import_url, :import_url,
:issues_tracker, :issues_tracker,
:issues_tracker_id, :issues_tracker_id,
......
...@@ -119,6 +119,39 @@ module ApplicationSettingsHelper ...@@ -119,6 +119,39 @@ module ApplicationSettingsHelper
options_for_select(options, selected) options_for_select(options, selected)
end end
def external_authorization_description
_("If enabled, access to projects will be validated on an external service"\
" using their classification label.")
end
def external_authorization_timeout_help_text
_("Time in seconds GitLab will wait for a response from the external "\
"service. When the service does not respond in time, access will be "\
"denied.")
end
def external_authorization_url_help_text
_("When leaving the URL blank, classification labels can still be "\
"specified without disabling cross project features or performing "\
"external authorization checks.")
end
def external_authorization_client_certificate_help_text
_("The X509 Certificate to use when mutual TLS is required to communicate "\
"with the external authorization service. If left blank, the server "\
"certificate is still validated when accessing over HTTPS.")
end
def external_authorization_client_key_help_text
_("The private key to use when a client certificate is provided. This value "\
"is encrypted at rest.")
end
def external_authorization_client_pass_help_text
_("The passphrase required to decrypt the private key. This is optional "\
"and the value is encrypted at rest.")
end
def visible_attributes def visible_attributes
[ [
:admin_notification_email, :admin_notification_email,
...@@ -238,6 +271,18 @@ module ApplicationSettingsHelper ...@@ -238,6 +271,18 @@ module ApplicationSettingsHelper
] ]
end end
def external_authorization_service_attributes
[
:external_auth_client_cert,
:external_auth_client_key,
:external_auth_client_key_pass,
:external_authorization_service_default_label,
:external_authorization_service_enabled,
:external_authorization_service_timeout,
:external_authorization_service_url
]
end
def expanded_by_default? def expanded_by_default?
Rails.env.test? Rails.env.test?
end end
......
...@@ -305,6 +305,16 @@ module ProjectsHelper ...@@ -305,6 +305,16 @@ module ProjectsHelper
@path.present? @path.present?
end end
def external_classification_label_help_message
default_label = ::Gitlab::CurrentSettings.current_application_settings
.external_authorization_service_default_label
s_(
"ExternalAuthorizationService|When no classification label is set the "\
"default label `%{default_label}` will be used."
) % { default_label: default_label }
end
private private
def get_project_nav_tabs(project, current_user) def get_project_nav_tabs(project, current_user)
......
...@@ -213,6 +213,40 @@ class ApplicationSetting < ApplicationRecord ...@@ -213,6 +213,40 @@ class ApplicationSetting < ApplicationRecord
validate :terms_exist, if: :enforce_terms? validate :terms_exist, if: :enforce_terms?
validates :external_authorization_service_default_label,
presence: true,
if: :external_authorization_service_enabled
validates :external_authorization_service_url,
url: true, allow_blank: true,
if: :external_authorization_service_enabled
validates :external_authorization_service_timeout,
numericality: { greater_than: 0, less_than_or_equal_to: 10 },
if: :external_authorization_service_enabled
validates :external_auth_client_key,
presence: true,
if: -> (setting) { setting.external_auth_client_cert.present? }
validates_with X509CertificateCredentialsValidator,
certificate: :external_auth_client_cert,
pkey: :external_auth_client_key,
pass: :external_auth_client_key_pass,
if: -> (setting) { setting.external_auth_client_cert.present? }
attr_encrypted :external_auth_client_key,
mode: :per_attribute_iv,
key: Settings.attr_encrypted_db_key_base_truncated,
algorithm: 'aes-256-gcm',
encode: true
attr_encrypted :external_auth_client_key_pass,
mode: :per_attribute_iv,
key: Settings.attr_encrypted_db_key_base_truncated,
algorithm: 'aes-256-gcm',
encode: true
before_validation :ensure_uuid! before_validation :ensure_uuid!
before_validation :strip_sentry_values before_validation :strip_sentry_values
......
...@@ -208,7 +208,13 @@ class Issue < ApplicationRecord ...@@ -208,7 +208,13 @@ class Issue < ApplicationRecord
def visible_to_user?(user = nil) def visible_to_user?(user = nil)
return false unless project && project.feature_available?(:issues, user) return false unless project && project.feature_available?(:issues, user)
user ? readable_by?(user) : publicly_visible? return publicly_visible? unless user
return false unless readable_by?(user)
user.full_private_access? ||
::Gitlab::ExternalAuthorization.access_allowed?(
user, project.external_authorization_classification_label)
end end
def check_for_spam? def check_for_spam?
...@@ -276,7 +282,7 @@ class Issue < ApplicationRecord ...@@ -276,7 +282,7 @@ class Issue < ApplicationRecord
# Returns `true` if this Issue is visible to everybody. # Returns `true` if this Issue is visible to everybody.
def publicly_visible? def publicly_visible?
project.public? && !confidential? project.public? && !confidential? && !::Gitlab::ExternalAuthorization.enabled?
end end
def expire_etag_cache def expire_etag_cache
......
...@@ -2066,6 +2066,11 @@ class Project < ApplicationRecord ...@@ -2066,6 +2066,11 @@ class Project < ApplicationRecord
fetch_branch_allows_collaboration(user, branch_name) fetch_branch_allows_collaboration(user, branch_name)
end end
def external_authorization_classification_label
super || ::Gitlab::CurrentSettings.current_application_settings
.external_authorization_service_default_label
end
def licensed_features def licensed_features
[] []
end end
......
...@@ -22,7 +22,14 @@ class BasePolicy < DeclarativePolicy::Base ...@@ -22,7 +22,14 @@ class BasePolicy < DeclarativePolicy::Base
Gitlab::CurrentSettings.current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC) Gitlab::CurrentSettings.current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC)
end end
# This is prevented in some cases in `gitlab-ee` condition(:external_authorization_enabled, scope: :global, score: 0) do
::Gitlab::ExternalAuthorization.perform_check?
end
rule { external_authorization_enabled & ~full_private_access }.policy do
prevent :read_cross_project
end
rule { default }.enable :read_cross_project rule { default }.enable :read_cross_project
end end
......
...@@ -89,6 +89,15 @@ class ProjectPolicy < BasePolicy ...@@ -89,6 +89,15 @@ class ProjectPolicy < BasePolicy
::Gitlab::CurrentSettings.current_application_settings.mirror_available ::Gitlab::CurrentSettings.current_application_settings.mirror_available
end end
with_scope :subject
condition(:classification_label_authorized, score: 32) do
::Gitlab::ExternalAuthorization.access_allowed?(
@user,
@subject.external_authorization_classification_label,
@subject.full_path
)
end
# We aren't checking `:read_issue` or `:read_merge_request` in this case # We aren't checking `:read_issue` or `:read_merge_request` in this case
# because it could be possible for a user to see an issuable-iid # because it could be possible for a user to see an issuable-iid
# (`:read_issue_iid` or `:read_merge_request_iid`) but then wouldn't be # (`:read_issue_iid` or `:read_merge_request_iid`) but then wouldn't be
...@@ -417,6 +426,25 @@ class ProjectPolicy < BasePolicy ...@@ -417,6 +426,25 @@ class ProjectPolicy < BasePolicy
rule { ~can_have_multiple_clusters & has_clusters }.prevent :add_cluster rule { ~can_have_multiple_clusters & has_clusters }.prevent :add_cluster
rule { ~can?(:read_cross_project) & ~classification_label_authorized }.policy do
# Preventing access here still allows the projects to be listed. Listing
# projects doesn't check the `:read_project` ability. But instead counts
# on the `project_authorizations` table.
#
# All other actions should explicitly check read project, which would
# trigger the `classification_label_authorized` condition.
#
# `:read_project_for_iids` is not prevented by this condition, as it is
# used for cross-project reference checks.
prevent :guest_access
prevent :public_access
prevent :public_user_access
prevent :reporter_access
prevent :developer_access
prevent :maintainer_access
prevent :owner_access
end
private private
def team_member? def team_member?
......
...@@ -2,9 +2,17 @@ ...@@ -2,9 +2,17 @@
module ApplicationSettings module ApplicationSettings
class UpdateService < ApplicationSettings::BaseService class UpdateService < ApplicationSettings::BaseService
include ValidatesClassificationLabel
attr_reader :params, :application_setting attr_reader :params, :application_setting
def execute def execute
validate_classification_label(application_setting, :external_authorization_service_default_label)
if application_setting.errors.any?
return false
end
update_terms(@params.delete(:terms)) update_terms(@params.delete(:terms))
if params.key?(:performance_bar_allowed_group_path) if params.key?(:performance_bar_allowed_group_path)
......
...@@ -2,14 +2,14 @@ ...@@ -2,14 +2,14 @@
module ValidatesClassificationLabel module ValidatesClassificationLabel
def validate_classification_label(record, attribute_name) def validate_classification_label(record, attribute_name)
return unless EE::Gitlab::ExternalAuthorization.enabled? return unless ::Gitlab::ExternalAuthorization.enabled?
return unless classification_label_change?(record, attribute_name) return unless classification_label_change?(record, attribute_name)
new_label = params[attribute_name].presence new_label = params[attribute_name].presence
new_label ||= ::Gitlab::CurrentSettings.current_application_settings new_label ||= ::Gitlab::CurrentSettings.current_application_settings
.external_authorization_service_default_label .external_authorization_service_default_label
unless EE::Gitlab::ExternalAuthorization.access_allowed?(current_user, new_label) unless ::Gitlab::ExternalAuthorization.access_allowed?(current_user, new_label)
reason = rejection_reason_for_label(new_label) reason = rejection_reason_for_label(new_label)
message = s_('ClassificationLabelUnavailable|is unavailable: %{reason}') % { reason: reason } message = s_('ClassificationLabelUnavailable|is unavailable: %{reason}') % { reason: reason }
record.errors.add(attribute_name, message) record.errors.add(attribute_name, message)
...@@ -17,7 +17,7 @@ module ValidatesClassificationLabel ...@@ -17,7 +17,7 @@ module ValidatesClassificationLabel
end end
def rejection_reason_for_label(label) def rejection_reason_for_label(label)
reason_from_service = EE::Gitlab::ExternalAuthorization.rejection_reason(current_user, label).presence reason_from_service = ::Gitlab::ExternalAuthorization.rejection_reason(current_user, label).presence
reason_from_service || _("Access to '%{classification_label}' not allowed") % { classification_label: label } reason_from_service || _("Access to '%{classification_label}' not allowed") % { classification_label: label }
end end
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
module Projects module Projects
class CreateService < BaseService class CreateService < BaseService
include ValidatesClassificationLabel
def initialize(user, params) def initialize(user, params)
@current_user, @params = user, params.dup @current_user, @params = user, params.dup
@skip_wiki = @params.delete(:skip_wiki) @skip_wiki = @params.delete(:skip_wiki)
...@@ -45,6 +47,8 @@ module Projects ...@@ -45,6 +47,8 @@ module Projects
relations_block&.call(@project) relations_block&.call(@project)
yield(@project) if block_given? yield(@project) if block_given?
validate_classification_label(@project, :external_authorization_classification_label)
# If the block added errors, don't try to save the project # If the block added errors, don't try to save the project
return @project if @project.errors.any? return @project if @project.errors.any?
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
module Projects module Projects
class UpdateService < BaseService class UpdateService < BaseService
include UpdateVisibilityLevel include UpdateVisibilityLevel
include ValidatesClassificationLabel
ValidationError = Class.new(StandardError) ValidationError = Class.new(StandardError)
...@@ -14,6 +15,8 @@ module Projects ...@@ -14,6 +15,8 @@ module Projects
yield if block_given? yield if block_given?
validate_classification_label(project, :external_authorization_classification_label)
# If the block added errors, don't try to save the project # If the block added errors, don't try to save the project
return update_failed! if project.errors.any? return update_failed! if project.errors.any?
......
- return unless License.feature_available?(:external_authorization_service)
%section.settings.as-external-auth.no-animate#js-external-auth-settings{ class: ('expanded' if expanded) } %section.settings.as-external-auth.no-animate#js-external-auth-settings{ class: ('expanded' if expanded) }
.settings-header .settings-header
%h4 %h4
......
...@@ -68,7 +68,7 @@ ...@@ -68,7 +68,7 @@
.settings-content .settings-content
= render 'terms' = render 'terms'
= render_if_exists 'admin/application_settings/external_authorization_service_form', expanded: expanded_by_default? = render 'admin/application_settings/external_authorization_service_form', expanded: expanded_by_default?
%section.settings.as-terminal.no-animate#js-terminal-settings{ class: ('expanded' if expanded_by_default?) } %section.settings.as-terminal.no-animate#js-terminal-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header .settings-header
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
= render "layouts/header/ee_license_banner" = render "layouts/header/ee_license_banner"
= render "layouts/broadcast" = render "layouts/broadcast"
= render "layouts/header/read_only_banner" = render "layouts/header/read_only_banner"
= render "layouts/nav/ee/classification_level_banner" = render "layouts/nav/classification_level_banner"
= yield :flash_message = yield :flash_message
= render "shared/ping_consent" = render "shared/ping_consent"
- unless @hide_breadcrumbs - unless @hide_breadcrumbs
......
- if EE::Gitlab::ExternalAuthorization.enabled? && @project - if ::Gitlab::ExternalAuthorization.enabled? && @project
= content_for :header_content do = content_for :header_content do
%span.badge.color-label.classification-label.has-tooltip{ title: s_('ExternalAuthorizationService|Classification label') } %span.badge.color-label.classification-label.has-tooltip{ title: s_('ExternalAuthorizationService|Classification label') }
= sprite_icon('lock-open', size: 8, css_class: 'inline') = sprite_icon('lock-open', size: 8, css_class: 'inline')
......
- if ::EE::Gitlab::ExternalAuthorization.enabled? - if ::Gitlab::ExternalAuthorization.enabled?
.form-group .form-group
= f.label :external_authorization_classification_label, class: 'label-bold' do = f.label :external_authorization_classification_label, class: 'label-bold' do
= s_('ExternalAuthorizationService|Classification Label') = s_('ExternalAuthorizationService|Classification Label')
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
%span.light (optional) %span.light (optional)
= f.text_area :description, class: "form-control", rows: 3, maxlength: 250 = f.text_area :description, class: "form-control", rows: 3, maxlength: 250
= render_if_exists 'projects/classification_policy_settings', f: f = render 'projects/classification_policy_settings', f: f
= render_if_exists 'shared/repository_size_limit_setting', form: f, type: :project = render_if_exists 'shared/repository_size_limit_setting', form: f, type: :project
......
---
title: Move "Authorize project access with external service" to Core
merge_request: 26823
author:
type: changed
...@@ -51,10 +51,6 @@ ...@@ -51,10 +51,6 @@
} }
} }
.classification-label {
background-color: $red-500;
}
.load-wrapper { .load-wrapper {
position: absolute; position: absolute;
background: $black-transparent; background: $black-transparent;
......
# frozen_string_literal: true
module EE
module ProjectUnauthorized
extend ::Gitlab::Utils::Override
override :project_unauthorized_proc
def project_unauthorized_proc
lambda do |project|
if project
label = project.external_authorization_classification_label
rejection_reason = nil
unless EE::Gitlab::ExternalAuthorization.access_allowed?(current_user, label)
rejection_reason = EE::Gitlab::ExternalAuthorization.rejection_reason(current_user, label)
rejection_reason ||= _('External authorization denied access to this project')
end
if rejection_reason
access_denied!(rejection_reason)
end
end
end
end
end
end
...@@ -10,8 +10,8 @@ module EE ...@@ -10,8 +10,8 @@ module EE
attrs += EE::ApplicationSettingsHelper.repository_mirror_attributes attrs += EE::ApplicationSettingsHelper.repository_mirror_attributes
end end
if License.feature_available?(:external_authorization_service) if License.feature_available?(:project_creation_level)
attrs += EE::ApplicationSettingsHelper.external_authorization_service_attributes attrs << :default_project_creation
end end
if License.feature_available?(:custom_project_templates) if License.feature_available?(:custom_project_templates)
......
...@@ -37,7 +37,6 @@ module EE ...@@ -37,7 +37,6 @@ module EE
repository_size_limit repository_size_limit
reset_approvals_on_push reset_approvals_on_push
service_desk_enabled service_desk_enabled
external_authorization_classification_label
ci_cd_only ci_cd_only
use_custom_template use_custom_template
packages_enabled packages_enabled
......
...@@ -4,39 +4,6 @@ module EE ...@@ -4,39 +4,6 @@ module EE
module ApplicationSettingsHelper module ApplicationSettingsHelper
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
def external_authorization_description
_("If enabled, access to projects will be validated on an external service"\
" using their classification label.")
end
def external_authorization_timeout_help_text
_("Time in seconds GitLab will wait for a response from the external "\
"service. When the service does not respond in time, access will be "\
"denied.")
end
def external_authorization_url_help_text
_("When leaving the URL blank, classification labels can still be "\
"specified without disabling cross project features or performing "\
"external authorization checks.")
end
def external_authorization_client_certificate_help_text
_("The X509 Certificate to use when mutual TLS is required to communicate "\
"with the external authorization service. If left blank, the server "\
"certificate is still validated when accessing over HTTPS.")
end
def external_authorization_client_key_help_text
_("The private key to use when a client certificate is provided. This value "\
"is encrypted at rest.")
end
def external_authorization_client_pass_help_text
_("The passphrase required to decrypt the private key. This is optional "\
"and the value is encrypted at rest.")
end
def pseudonymizer_enabled_help_text def pseudonymizer_enabled_help_text
_("Enable Pseudonymizer data collection") _("Enable Pseudonymizer data collection")
end end
...@@ -102,20 +69,8 @@ module EE ...@@ -102,20 +69,8 @@ module EE
] ]
end end
def self.external_authorization_service_attributes
[
:external_auth_client_cert,
:external_auth_client_key,
:external_auth_client_key_pass,
:external_authorization_service_default_label,
:external_authorization_service_enabled,
:external_authorization_service_timeout,
:external_authorization_service_url
]
end
def self.possible_licensed_attributes def self.possible_licensed_attributes
repository_mirror_attributes + external_authorization_service_attributes + %i[ repository_mirror_attributes + %i[
email_additional_text email_additional_text
file_template_project_id file_template_project_id
] ]
......
...@@ -111,16 +111,6 @@ module EE ...@@ -111,16 +111,6 @@ module EE
can?(current_user, :"change_#{rule}", @project) can?(current_user, :"change_#{rule}", @project)
end end
def external_classification_label_help_message
default_label = ::Gitlab::CurrentSettings.current_application_settings
.external_authorization_service_default_label
s_(
"ExternalAuthorizationService|When no classification label is set the "\
"default label `%{default_label}` will be used."
) % { default_label: default_label }
end
def ci_cd_projects_available? def ci_cd_projects_available?
::License.feature_available?(:ci_cd_projects) && import_sources_enabled? ::License.feature_available?(:ci_cd_projects) && import_sources_enabled?
end end
......
...@@ -60,43 +60,9 @@ module EE ...@@ -60,43 +60,9 @@ module EE
presence: true, presence: true,
if: :snowplow_enabled if: :snowplow_enabled
validates :external_authorization_service_default_label,
presence: true,
if: :external_authorization_service_enabled?
validates :external_authorization_service_url,
url: true, allow_blank: true,
if: :external_authorization_service_enabled?
validates :external_authorization_service_timeout,
numericality: { greater_than: 0, less_than_or_equal_to: 10 },
if: :external_authorization_service_enabled?
validates :external_auth_client_key,
presence: true,
if: -> (setting) { setting.external_auth_client_cert.present? }
validates :geo_node_allowed_ips, length: { maximum: 255 }, presence: true validates :geo_node_allowed_ips, length: { maximum: 255 }, presence: true
validate :check_geo_node_allowed_ips validate :check_geo_node_allowed_ips
validates_with X509CertificateCredentialsValidator,
certificate: :external_auth_client_cert,
pkey: :external_auth_client_key,
pass: :external_auth_client_key_pass,
if: -> (setting) { setting.external_auth_client_cert.present? }
attr_encrypted :external_auth_client_key,
mode: :per_attribute_iv,
key: Settings.attr_encrypted_db_key_base_truncated,
algorithm: 'aes-256-gcm',
encode: true
attr_encrypted :external_auth_client_key_pass,
mode: :per_attribute_iv,
key: Settings.attr_encrypted_db_key_base_truncated,
algorithm: 'aes-256-gcm',
encode: true
end end
class_methods do class_methods do
...@@ -238,12 +204,6 @@ module EE ...@@ -238,12 +204,6 @@ module EE
EMAIL_ADDITIONAL_TEXT_CHARACTER_LIMIT EMAIL_ADDITIONAL_TEXT_CHARACTER_LIMIT
end end
def external_authorization_service_enabled
License.feature_available?(:external_authorization_service) && super
end
alias_method :external_authorization_service_enabled?,
:external_authorization_service_enabled
def custom_project_templates_enabled? def custom_project_templates_enabled?
License.feature_available?(:custom_project_templates) License.feature_available?(:custom_project_templates)
end end
......
...@@ -56,23 +56,6 @@ module EE ...@@ -56,23 +56,6 @@ module EE
super if supports_weight? super if supports_weight?
end end
# The functionality here is duplicated from the `IssuePolicy` and the
# `EE::IssuePolicy` for better performace
#
# Make sure to keep this in sync with the policies.
override :readable_by?
def readable_by?(user)
return super if user.full_private_access?
super && ::EE::Gitlab::ExternalAuthorization
.access_allowed?(user, project.external_authorization_classification_label)
end
override :publicly_visible?
def publicly_visible?
super && !::EE::Gitlab::ExternalAuthorization.enabled?
end
def supports_weight? def supports_weight?
project&.feature_available?(:issue_weights) project&.feature_available?(:issue_weights)
end end
......
...@@ -477,13 +477,6 @@ module EE ...@@ -477,13 +477,6 @@ module EE
::Gitlab::CurrentSettings.mirror_available ::Gitlab::CurrentSettings.mirror_available
end end
def external_authorization_classification_label
return unless License.feature_available?(:external_authorization_service)
super || ::Gitlab::CurrentSettings.current_application_settings
.external_authorization_service_default_label
end
override :licensed_features override :licensed_features
def licensed_features def licensed_features
return super unless License.current return super unless License.current
......
...@@ -66,7 +66,6 @@ class License < ApplicationRecord ...@@ -66,7 +66,6 @@ class License < ApplicationRecord
variable_environment_scope variable_environment_scope
reject_unsigned_commits reject_unsigned_commits
commit_committer_check commit_committer_check
external_authorization_service
ci_cd_projects ci_cd_projects
protected_environments protected_environments
custom_project_templates custom_project_templates
...@@ -179,7 +178,6 @@ class License < ApplicationRecord ...@@ -179,7 +178,6 @@ class License < ApplicationRecord
multiple_ldap_servers multiple_ldap_servers
object_storage object_storage
repository_size_limit repository_size_limit
external_authorization_service
custom_project_templates custom_project_templates
].freeze ].freeze
......
...@@ -5,14 +5,6 @@ module EE ...@@ -5,14 +5,6 @@ module EE
extend ActiveSupport::Concern extend ActiveSupport::Concern
prepended do prepended do
condition(:external_authorization_enabled, scope: :global, score: 0) do
::EE::Gitlab::ExternalAuthorization.perform_check?
end
rule { external_authorization_enabled & ~admin & ~auditor }.policy do
prevent :read_cross_project
end
with_scope :user with_scope :user
condition(:auditor, score: 0) { @user&.auditor? } condition(:auditor, score: 0) { @user&.auditor? }
......
...@@ -45,6 +45,7 @@ module EE ...@@ -45,6 +45,7 @@ module EE
prevent :create_epic prevent :create_epic
prevent :admin_epic prevent :admin_epic
prevent :update_epic prevent :update_epic
prevent :destroy_epic
end end
rule { auditor }.enable :read_group rule { auditor }.enable :read_group
......
...@@ -30,15 +30,6 @@ module EE ...@@ -30,15 +30,6 @@ module EE
with_scope :subject with_scope :subject
condition(:packages_disabled) { !@subject.packages_enabled } condition(:packages_disabled) { !@subject.packages_enabled }
with_scope :subject
condition(:classification_label_authorized, score: 32) do
EE::Gitlab::ExternalAuthorization.access_allowed?(
@user,
@subject.external_authorization_classification_label,
@subject.full_path
)
end
with_scope :global with_scope :global
condition(:is_development) { Rails.env.development? } condition(:is_development) { Rails.env.development? }
...@@ -191,25 +182,6 @@ module EE ...@@ -191,25 +182,6 @@ module EE
rule { owner | reporter }.enable :build_read_project rule { owner | reporter }.enable :build_read_project
rule { ~can?(:read_cross_project) & ~classification_label_authorized }.policy do
# Preventing access here still allows the projects to be listed. Listing
# projects doesn't check the `:read_project` ability. But instead counts
# on the `project_authorizations` table.
#
# All other actions should explicitly check read project, which would
# trigger the `classification_label_authorized` condition.
#
# `:read_project_for_iids` is not prevented by this condition, as it is
# used for cross-project reference checks.
prevent :guest_access
prevent :public_access
prevent :public_user_access
prevent :reporter_access
prevent :developer_access
prevent :maintainer_access
prevent :owner_access
end
rule { archived }.policy do rule { archived }.policy do
READONLY_FEATURES_WHEN_ARCHIVED.each do |feature| READONLY_FEATURES_WHEN_ARCHIVED.each do |feature|
prevent(*::ProjectPolicy.create_update_admin_destroy(feature)) prevent(*::ProjectPolicy.create_update_admin_destroy(feature))
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
module EE module EE
module ApplicationSettings module ApplicationSettings
module UpdateService module UpdateService
include ValidatesClassificationLabel
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
extend ActiveSupport::Concern extend ActiveSupport::Concern
...@@ -13,12 +12,6 @@ module EE ...@@ -13,12 +12,6 @@ module EE
limit = params.delete(:repository_size_limit) limit = params.delete(:repository_size_limit)
application_setting.repository_size_limit = ::Gitlab::Utils.try_megabytes_to_bytes(limit) if limit application_setting.repository_size_limit = ::Gitlab::Utils.try_megabytes_to_bytes(limit) if limit
validate_classification_label(application_setting, :external_authorization_service_default_label)
if application_setting.errors.any?
return false
end
super super
end end
end end
......
...@@ -4,7 +4,6 @@ module EE ...@@ -4,7 +4,6 @@ module EE
module Projects module Projects
module CreateService module CreateService
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
include ValidatesClassificationLabel
override :execute override :execute
def execute def execute
...@@ -25,7 +24,6 @@ module EE ...@@ -25,7 +24,6 @@ module EE
project.mirror_user_id = mirror_user_id project.mirror_user_id = mirror_user_id
end end
validate_classification_label(project, :external_authorization_classification_label)
validate_namespace_used_with_template(project, group_with_project_templates_id) validate_namespace_used_with_template(project, group_with_project_templates_id)
end end
......
...@@ -4,7 +4,6 @@ module EE ...@@ -4,7 +4,6 @@ module EE
module Projects module Projects
module UpdateService module UpdateService
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
include ValidatesClassificationLabel
include CleanupApprovers include CleanupApprovers
override :execute override :execute
...@@ -26,8 +25,6 @@ module EE ...@@ -26,8 +25,6 @@ module EE
if changing_storage_size? if changing_storage_size?
project.change_repository_storage(params.delete(:repository_storage)) project.change_repository_storage(params.delete(:repository_storage))
end end
validate_classification_label(project, :external_authorization_classification_label)
end end
if result[:status] == :success if result[:status] == :success
......
...@@ -41,8 +41,6 @@ module EE ...@@ -41,8 +41,6 @@ module EE
expose :mirror_trigger_builds, if: ->(project, _) { project.mirror? } expose :mirror_trigger_builds, if: ->(project, _) { project.mirror? }
expose :only_mirror_protected_branches, if: ->(project, _) { project.mirror? } expose :only_mirror_protected_branches, if: ->(project, _) { project.mirror? }
expose :mirror_overwrites_diverged_branches, if: ->(project, _) { project.mirror? } expose :mirror_overwrites_diverged_branches, if: ->(project, _) { project.mirror? }
expose :external_authorization_classification_label,
if: ->(_, _) { License.feature_available?(:external_authorization_service) }
expose :packages_enabled, if: ->(project, _) { project.feature_available?(:packages) } expose :packages_enabled, if: ->(project, _) { project.feature_available?(:packages) }
end end
end end
...@@ -158,9 +156,6 @@ module EE ...@@ -158,9 +156,6 @@ module EE
expose(*EE::ApplicationSettingsHelper.repository_mirror_attributes, if: ->(_instance, _options) do expose(*EE::ApplicationSettingsHelper.repository_mirror_attributes, if: ->(_instance, _options) do
::License.feature_available?(:repository_mirrors) ::License.feature_available?(:repository_mirrors)
end) end)
expose(*EE::ApplicationSettingsHelper.external_authorization_service_attributes, if: ->(_instance, _options) do
::License.feature_available?(:external_authorization_service)
end)
expose :email_additional_text, if: ->(_instance, _opts) { ::License.feature_available?(:email_additional_text) } expose :email_additional_text, if: ->(_instance, _opts) { ::License.feature_available?(:email_additional_text) }
expose :file_template_project_id, if: ->(_instance, _opts) { ::License.feature_available?(:custom_file_templates) } expose :file_template_project_id, if: ->(_instance, _opts) { ::License.feature_available?(:custom_file_templates) }
end end
......
...@@ -16,12 +16,6 @@ module EE ...@@ -16,12 +16,6 @@ module EE
attrs = attrs.except(*::EE::ApplicationSettingsHelper.repository_mirror_attributes) attrs = attrs.except(*::EE::ApplicationSettingsHelper.repository_mirror_attributes)
end end
unless ::License.feature_available?(:external_authorization_service)
attrs = attrs.except(
*::EE::ApplicationSettingsHelper.external_authorization_service_attributes
)
end
unless ::License.feature_available?(:email_additional_text) unless ::License.feature_available?(:email_additional_text)
attrs = attrs.except(:email_additional_text) attrs = attrs.except(:email_additional_text)
end end
......
# frozen_string_literal: true
module EE
module Gitlab
module ExternalAuthorization
extend Config
RequestFailed = Class.new(StandardError)
def self.access_allowed?(user, label, project_path = nil)
return true unless perform_check?
return false unless user
access_for_user_to_label(user, label, project_path).has_access?
end
def self.rejection_reason(user, label)
return unless enabled?
return unless user
access_for_user_to_label(user, label, nil).reason
end
def self.access_for_user_to_label(user, label, project_path)
if RequestStore.active?
RequestStore.fetch("external_authorisation:user-#{user.id}:label-#{label}") do
load_access(user, label, project_path)
end
else
load_access(user, label, project_path)
end
end
def self.load_access(user, label, project_path)
access = EE::Gitlab::ExternalAuthorization::Access.new(user, label).load!
::EE::Gitlab::ExternalAuthorization::Logger.log_access(access, project_path)
access
end
end
end
end
# frozen_string_literal: true
module EE
module Gitlab
module ExternalAuthorization
class Access
attr_reader :user,
:reason,
:loaded_at,
:label,
:load_type
def initialize(user, label)
@user, @label = user, label
end
def loaded?
loaded_at && (loaded_at > Cache::VALIDITY_TIME.ago)
end
def has_access?
@access
end
def load!
load_from_cache
load_from_service unless loaded?
self
end
private
def load_from_cache
@load_type = :cache
@access, @reason, @loaded_at = cache.load
end
def load_from_service
@load_type = :request
response = Client.new(@user, @label).request_access
@access = response.successful?
@reason = response.reason
@loaded_at = Time.now
cache.store(@access, @reason, @loaded_at) if response.valid?
rescue EE::Gitlab::ExternalAuthorization::RequestFailed => e
@access = false
@reason = e.message
@loaded_at = Time.now
end
def cache
@cache ||= Cache.new(@user, @label)
end
end
end
end
end
# frozen_string_literal: true
module EE
module Gitlab
module ExternalAuthorization
class Cache
VALIDITY_TIME = 6.hours
def initialize(user, label)
@user, @label = user, label
end
def load
@access, @reason, @refreshed_at = ::Gitlab::Redis::Cache.with do |redis|
redis.hmget(cache_key, :access, :reason, :refreshed_at)
end
[access, reason, refreshed_at]
end
def store(new_access, new_reason, new_refreshed_at)
::Gitlab::Redis::Cache.with do |redis|
redis.pipelined do
redis.mapped_hmset(
cache_key,
{
access: new_access.to_s,
reason: new_reason.to_s,
refreshed_at: new_refreshed_at.to_s
}
)
redis.expire(cache_key, VALIDITY_TIME)
end
end
end
private
def access
::Gitlab::Utils.to_boolean(@access)
end
def reason
# `nil` if the cached value was an empty string
return unless @reason.present?
@reason
end
def refreshed_at
# Don't try to parse a time if there was no cache
return unless @refreshed_at.present?
Time.parse(@refreshed_at)
end
def cache_key
"external_authorization:user-#{@user.id}:label-#{@label}"
end
end
end
end
end
# frozen_string_literal: true
Excon.defaults[:ssl_verify_peer] = false
module EE
module Gitlab
module ExternalAuthorization
class Client
include Config
REQUEST_HEADERS = {
'Content-Type' => 'application/json',
'Accept' => 'application/json'
}.freeze
def initialize(user, label)
@user, @label = user, label
end
def request_access
response = Excon.post(
service_url,
post_params
)
EE::Gitlab::ExternalAuthorization::Response.new(response)
rescue Excon::Error => e
raise EE::Gitlab::ExternalAuthorization::RequestFailed.new(e)
end
private
def post_params
params = { headers: REQUEST_HEADERS,
body: body.to_json,
connect_timeout: timeout,
read_timeout: timeout,
write_timeout: timeout }
if has_tls?
params[:client_cert_data] = client_cert
params[:client_key_data] = client_key
params[:client_key_pass] = client_key_pass
end
params
end
def body
@body ||= begin
body = {
user_identifier: @user.email,
project_classification_label: @label
}
if @user.ldap_identity
body[:user_ldap_dn] = @user.ldap_identity.extern_uid
end
body
end
end
end
end
end
end
# frozen_string_literal: true
module EE
module Gitlab
module ExternalAuthorization
module Config
extend self
def timeout
application_settings.external_authorization_service_timeout
end
def service_url
application_settings.external_authorization_service_url
end
def enabled?
application_settings.external_authorization_service_enabled?
end
def perform_check?
enabled? && service_url.present?
end
def client_cert
application_settings.external_auth_client_cert
end
def client_key
application_settings.external_auth_client_key
end
def client_key_pass
application_settings.external_auth_client_key_pass
end
def has_tls?
client_cert.present? && client_key.present?
end
private
def application_settings
::Gitlab::CurrentSettings.current_application_settings
end
end
end
end
end
# frozen_string_literal: true
module EE
module Gitlab
module ExternalAuthorization
class Logger < ::Gitlab::Logger
def self.log_access(access, project_path)
status = access.has_access? ? "GRANTED" : "DENIED"
message = ["#{status} #{access.user.email} access to '#{access.label}'"]
message << "(#{project_path})" if project_path.present?
message << "- #{access.load_type} #{access.loaded_at}" if access.load_type == :cache
info(message.join(' '))
end
def self.file_name_noext
'external-policy-access-control'
end
end
end
end
end
# frozen_string_literal: true
module EE
module Gitlab
module ExternalAuthorization
class Response
include ::Gitlab::Utils::StrongMemoize
def initialize(excon_response)
@excon_response = excon_response
end
def valid?
@excon_response && [200, 401, 403].include?(@excon_response.status)
end
def successful?
valid? && @excon_response.status == 200
end
def reason
parsed_response['reason'] if parsed_response
end
private
def parsed_response
strong_memoize(:parsed_response) { parse_response! }
end
def parse_response!
JSON.parse(@excon_response.body)
rescue JSON::JSONError
# The JSON response is optional, so don't fail when it's missing
nil
end
end
end
end
end
...@@ -81,23 +81,6 @@ describe Admin::ApplicationSettingsController do ...@@ -81,23 +81,6 @@ describe Admin::ApplicationSettingsController do
it_behaves_like 'settings for licensed features' it_behaves_like 'settings for licensed features'
end end
context 'external policy classification settings' do
let(:settings) do
{
external_authorization_service_enabled: true,
external_authorization_service_url: 'https://custom.service/',
external_authorization_service_default_label: 'default',
external_authorization_service_timeout: 3,
external_auth_client_cert: File.read('ee/spec/fixtures/passphrase_x509_certificate.crt'),
external_auth_client_key: File.read('ee/spec/fixtures/passphrase_x509_certificate_pk.key'),
external_auth_client_key_pass: "5iveL!fe"
}
end
let(:feature) { :external_authorization_service }
it_behaves_like 'settings for licensed features'
end
context 'additional email footer' do context 'additional email footer' do
let(:settings) { { email_additional_text: 'scary legal footer' } } let(:settings) { { email_additional_text: 'scary legal footer' } }
let(:feature) { :email_additional_text } let(:feature) { :email_additional_text }
......
...@@ -89,26 +89,6 @@ describe Boards::IssuesController do ...@@ -89,26 +89,6 @@ describe Boards::IssuesController do
end end
end end
context 'with external authorization' do
before do
sign_in(user)
enable_external_authorization_service_check
end
it 'returns a 403 for group boards' do
get :index, params: { board_id: board }
expect(response).to have_gitlab_http_status(403)
end
it 'is successful for project boards' do
project_board = create(:board, project: project_1)
list_issues(user: user, board: project_board)
expect(response).to have_gitlab_http_status(200)
end
end
def list_issues(user:, board:, list: nil) def list_issues(user:, board:, list: nil)
sign_in(user) sign_in(user)
......
require 'spec_helper'
describe Dashboard::GroupsController do
include ExternalAuthorizationServiceHelpers
before do
sign_in create(:user)
end
describe '#index' do
it 'works when the external authorization service is enabled' do
enable_external_authorization_service_check
get :index
expect(response).to have_gitlab_http_status(200)
end
end
end
require 'spec_helper'
describe Dashboard::LabelsController do
before do
sign_in create(:user)
end
describe '#index' do
subject { get :index, format: :json }
it_behaves_like 'disabled when using an external authorization service'
end
end
require 'spec_helper'
describe Dashboard::MilestonesController do
before do
sign_in create(:user)
end
describe '#index' do
subject { get :index }
it_behaves_like 'disabled when using an external authorization service'
end
end
require 'spec_helper'
describe Dashboard::ProjectsController do
include ExternalAuthorizationServiceHelpers
before do
sign_in create(:user)
end
describe '#index' do
subject { get :index }
it 'works when the external authorization service is enabled' do
enable_external_authorization_service_check
get :index
expect(response).to have_gitlab_http_status(200)
end
end
end
require 'spec_helper'
describe Dashboard::TodosController do
before do
sign_in create(:user)
end
describe '#index' do
subject { get :index }
it_behaves_like 'disabled when using an external authorization service'
end
end
require 'spec_helper'
describe Groups::AvatarsController do
include ExternalAuthorizationServiceHelpers
let(:user) { create(:user) }
let(:group) { create(:group, avatar: fixture_file_upload('spec/fixtures/dk.png', 'image/png')) }
before do
group.add_owner(user)
sign_in(user)
end
it 'works when external authorization service is enabled' do
enable_external_authorization_service_check
delete :destroy, params: { group_id: group }
expect(response).to have_gitlab_http_status(302)
end
end
...@@ -43,10 +43,6 @@ describe Groups::BoardsController do ...@@ -43,10 +43,6 @@ describe Groups::BoardsController do
end end
end end
it_behaves_like 'disabled when using an external authorization service' do
subject { list_boards }
end
it_behaves_like 'redirects to last visited board' do it_behaves_like 'redirects to last visited board' do
let(:parent) { group } let(:parent) { group }
end end
......
require 'spec_helper'
describe Groups::ChildrenController do
include ExternalAuthorizationServiceHelpers
let(:user) { create(:user) }
let(:group) { create(:group) }
before do
group.add_owner(user)
sign_in(user)
end
it 'works when external authorization service is enabled' do
enable_external_authorization_service_check
get :index, params: { group_id: group }, format: :json
expect(response).to have_gitlab_http_status(200)
end
end
...@@ -12,26 +12,7 @@ describe Groups::GroupMembersController do ...@@ -12,26 +12,7 @@ describe Groups::GroupMembersController do
sign_in(user) sign_in(user)
end end
context 'with external authorization enabled' do
before do
enable_external_authorization_service_check
end
describe 'GET #index' do
it 'is successful' do
get :index, params: { group_id: group }
expect(response).to have_gitlab_http_status(200)
end
end
describe 'POST #create' do describe 'POST #create' do
it 'is successful' do
post :create, params: { group_id: group, users: user, access_level: Gitlab::Access::GUEST }
expect(response).to have_gitlab_http_status(302)
end
it 'creates an audit event' do it 'creates an audit event' do
expect do expect do
post :create, params: { group_id: group, post :create, params: { group_id: group,
...@@ -41,56 +22,7 @@ describe Groups::GroupMembersController do ...@@ -41,56 +22,7 @@ describe Groups::GroupMembersController do
end end
end end
describe 'PUT #update' do
it 'is successful' do
put :update,
params: {
group_member: { access_level: Gitlab::Access::GUEST },
group_id: group,
id: membership
},
format: :js
expect(response).to have_gitlab_http_status(200)
end
end
describe 'DELETE #destroy' do
it 'is successful' do
delete :destroy, params: { group_id: group, id: membership }
expect(response).to have_gitlab_http_status(302)
end
end
describe 'POST #destroy' do
it 'is successful' do
sign_in(create(:user))
post :request_access, params: { group_id: group }
expect(response).to have_gitlab_http_status(302)
end
end
describe 'POST #approve_request_access' do
it 'is successful' do
access_request = create(:group_member, :access_request, group: group)
post :approve_access_request, params: { group_id: group, id: access_request }
expect(response).to have_gitlab_http_status(302)
end
end
describe 'DELETE #leave' do describe 'DELETE #leave' do
it 'is successful' do
group.add_owner(create(:user))
delete :leave, params: { group_id: group }
expect(response).to have_gitlab_http_status(302)
end
context 'when member is not an owner' do context 'when member is not an owner' do
it 'creates an audit event' do it 'creates an audit event' do
developer = create(:user) developer = create(:user)
...@@ -118,12 +50,9 @@ describe Groups::GroupMembersController do ...@@ -118,12 +50,9 @@ describe Groups::GroupMembersController do
end end
end end
describe 'POST #resend_invite' do context 'with external authorization enabled' do
it 'is successful' do before do
post :resend_invite, params: { group_id: group, id: membership } enable_external_authorization_service_check
expect(response).to have_gitlab_http_status(302)
end
end end
describe 'POST #override' do describe 'POST #override' do
......
...@@ -13,6 +13,7 @@ describe GroupsController do ...@@ -13,6 +13,7 @@ describe GroupsController do
describe 'external authorization' do describe 'external authorization' do
before do before do
group.add_owner(user) group.add_owner(user)
sign_in(user)
end end
context 'with external authorization service enabled' do context 'with external authorization service enabled' do
...@@ -20,60 +21,7 @@ describe GroupsController do ...@@ -20,60 +21,7 @@ describe GroupsController do
enable_external_authorization_service_check enable_external_authorization_service_check
end end
describe 'GET #show' do
it 'is successful' do
get :show, params: { id: group.to_param }
expect(response).to have_gitlab_http_status(200)
end
it 'does not allow other formats' do
get :show, params: { id: group.to_param }, format: :atom
expect(response).to have_gitlab_http_status(403)
end
end
describe 'GET #edit' do
it 'is successful' do
get :edit, params: { id: group.to_param }
expect(response).to have_gitlab_http_status(200)
end
end
describe 'GET #new' do
it 'is successful' do
get :new
expect(response).to have_gitlab_http_status(200)
end
end
describe 'GET #index' do
it 'is successful' do
get :index
# Redirects to the dashboard
expect(response).to have_gitlab_http_status(302)
end
end
describe 'POST #create' do
it 'creates a group' do
expect do
post :create, params: { group: { name: 'a name', path: 'a-name' } }
end.to change { Group.count }.by(1)
end
end
describe 'PUT #update' do describe 'PUT #update' do
it 'updates a group' do
expect do
put :update, params: { id: group.to_param, group: { name: 'world' } }
end.to change { group.reload.name }
end
context 'no license' do context 'no license' do
it 'does not update the file_template_project_id successfully' do it 'does not update the file_template_project_id successfully' do
project = create(:project, group: group) project = create(:project, group: group)
...@@ -108,32 +56,6 @@ describe GroupsController do ...@@ -108,32 +56,6 @@ describe GroupsController do
end end
end end
end end
describe 'DELETE #destroy' do
it 'deletes the group' do
delete :destroy, params: { id: group.to_param }
expect(response).to have_gitlab_http_status(302)
end
end
end
describe 'GET #activity' do
subject { get :activity, params: { id: group.to_param } }
it_behaves_like 'disabled when using an external authorization service'
end
describe 'GET #issues' do
subject { get :issues, params: { id: group.to_param } }
it_behaves_like 'disabled when using an external authorization service'
end
describe 'GET #merge_requests' do
subject { get :merge_requests, params: { id: group.to_param } }
it_behaves_like 'disabled when using an external authorization service'
end end
end end
......
require 'spec_helper'
describe Groups::LabelsController do
let(:group) { create(:group) }
let(:user) { create(:user) }
before do
group.add_owner(user)
sign_in(user)
end
describe 'GET #index' do
subject { get :index, params: { group_id: group.to_param } }
it_behaves_like 'disabled when using an external authorization service'
end
end
require 'spec_helper'
describe Groups::MilestonesController do
let(:group) { create(:group) }
let(:user) { create(:user) }
before do
group.add_owner(user)
sign_in(user)
end
describe 'GET #index' do
subject { get :index, params: { group_id: group.to_param } }
it_behaves_like 'disabled when using an external authorization service'
end
end
require 'spec_helper'
describe Groups::Settings::CiCdController do
include ExternalAuthorizationServiceHelpers
let(:group) { create(:group) }
let(:user) { create(:user) }
before do
group.add_owner(user)
sign_in(user)
end
context 'with external authorization enabled' do
before do
enable_external_authorization_service_check
end
describe 'GET #show' do
it 'renders show with 200 status code' do
get :show, params: { group_id: group }
expect(response).to have_gitlab_http_status(200)
end
end
end
end
require 'spec_helper'
describe Groups::VariablesController do
include ExternalAuthorizationServiceHelpers
let(:user) { create(:user) }
let(:group) { create(:group) }
let(:variable) { create(:ci_group_variable, group: group) }
before do
group.add_owner(user)
sign_in(user)
end
context 'with external authorization enabled' do
before do
enable_external_authorization_service_check
end
describe 'GET #show' do
let!(:variable) { create(:ci_group_variable, group: group) }
it 'is successful' do
get :show, params: { group_id: group }, format: :json
expect(response).to have_gitlab_http_status(200)
end
end
describe 'PATCH #update' do
let!(:variable) { create(:ci_group_variable, group: group) }
let(:owner) { group }
it 'is successful' do
patch :update,
params: {
group_id: group,
variables_attributes: [{ id: variable.id, key: 'hello' }]
},
format: :json
expect(response).to have_gitlab_http_status(200)
end
end
end
end
require 'spec_helper' require 'spec_helper'
describe Projects::BoardsController do describe Projects::BoardsController do
include Rails.application.routes.url_helpers
let(:project) { create(:project) } let(:project) { create(:project) }
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
project.add_maintainer(user) project.add_maintainer(user)
allow(Ability).to receive(:allowed?).and_call_original
sign_in(user) sign_in(user)
end end
...@@ -25,10 +22,6 @@ describe Projects::BoardsController do ...@@ -25,10 +22,6 @@ describe Projects::BoardsController do
expect(parsed_response.length).to eq 2 expect(parsed_response.length).to eq 2
end end
it_behaves_like 'unauthorized when external service denies access' do
subject { list_boards }
end
it_behaves_like 'redirects to last visited board' do it_behaves_like 'redirects to last visited board' do
let(:parent) { project } let(:parent) { project }
end end
......
require 'spec_helper' require 'spec_helper'
describe Projects::IssuesController do describe Projects::IssuesController do
include Rails.application.routes.url_helpers
let(:namespace) { create(:group, :public) } let(:namespace) { create(:group, :public) }
let(:project) { create(:project_empty_repo, :public, namespace: namespace) } let(:project) { create(:project_empty_repo, :public, namespace: namespace) }
let(:user) { create(:user) } let(:user) { create(:user) }
describe 'GET #index' do
before do
sign_in user
project.add_developer(user)
end
it_behaves_like 'unauthorized when external service denies access' do
subject { get :index, params: { namespace_id: project.namespace, project_id: project } }
end
end
describe 'POST export_csv' do describe 'POST export_csv' do
let(:viewer) { user } let(:viewer) { user }
let(:issue) { create(:issue, project: project) } let(:issue) { create(:issue, project: project) }
......
require 'spec_helper' require 'spec_helper'
describe ProjectsController do describe ProjectsController do
include ExternalAuthorizationServiceHelpers
let(:project) { create(:project) } let(:project) { create(:project) }
let(:user) { create(:user) } let(:user) { create(:user) }
...@@ -279,29 +277,5 @@ describe ProjectsController do ...@@ -279,29 +277,5 @@ describe ProjectsController do
end end
end end
end end
it_behaves_like 'unauthorized when external service denies access' do
subject do
put :update,
params: {
namespace_id: project.namespace,
id: project,
project: { description: 'Hello world' }
}
project.reload
end
it 'updates when the service allows access' do
external_service_allow_access(user, project)
expect { subject }.to change(project, :description)
end
it 'does not update when the service rejects access' do
external_service_deny_access(user, project)
expect { subject }.not_to change(project, :description)
end
end
end end
end end
require 'spec_helper'
describe SearchController do
include ExternalAuthorizationServiceHelpers
let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) }
let(:note) { create(:note_on_issue, project: project) }
before do
sign_in(user)
end
context 'with external authorization service enabled' do
before do
enable_external_authorization_service_check
end
describe 'GET #show' do
it 'renders a 403 when no project is given' do
get :show, params: { scope: 'notes', search: note.note }
expect(response).to have_gitlab_http_status(403)
end
it 'renders a 200 when a project was set' do
get :show, params: { project_id: project.id, scope: 'notes', search: note.note }
expect(response).to have_gitlab_http_status(200)
end
end
describe 'GET #autocomplete' do
it 'renders a 403 when no project is given' do
get :autocomplete, params: { term: 'hello' }
expect(response).to have_gitlab_http_status(403)
end
it 'renders a 200 when a project was set' do
get :autocomplete, params: { project_id: project.id, term: 'hello' }
expect(response).to have_gitlab_http_status(200)
end
end
end
end
require 'spec_helper'
describe UsersController do
let(:user) { create(:user) }
before do
sign_in(user)
end
describe 'GET #snippets' do
subject { get :snippets, params: { username: user.username } }
it_behaves_like 'disabled when using an external authorization service'
end
describe 'GET #calendar_activities' do
subject { get :calendar_activities, params: { username: user.username } }
it_behaves_like 'disabled when using an external authorization service'
end
end
...@@ -11,46 +11,12 @@ describe 'The group page' do ...@@ -11,46 +11,12 @@ describe 'The group page' do
group.add_owner(user) group.add_owner(user)
end end
def expect_all_sidebar_links
within('.nav-sidebar') do
expect(page).to have_link('Overview')
expect(page).to have_link('Details')
expect(page).to have_link('Activity')
expect(page).to have_link('Contribution Analytics')
expect(page).to have_link('Issues')
expect(page).to have_link('Merge Requests')
expect(page).to have_link('Members')
end
end
describe 'The sidebar' do describe 'The sidebar' do
it 'has all the expected links' do it 'shows the link to contribution analytics' do
visit group_path(group)
expect_all_sidebar_links
end
it 'shows all project features when policy control is enabled' do
stub_ee_application_setting(external_authorization_service_enabled: true)
visit group_path(group)
expect_all_sidebar_links
end
it 'hides some links when an external authorization service configured with an url' do
enable_external_authorization_service_check
visit group_path(group) visit group_path(group)
within('.nav-sidebar') do within('.nav-sidebar') do
expect(page).to have_link('Overview') expect(page).to have_link('Contribution Analytics')
expect(page).to have_link('Details')
expect(page).not_to have_link('Activity')
expect(page).not_to have_link('Contribution Analytics')
expect(page).not_to have_link('Issues')
expect(page).not_to have_link('Merge Requests')
expect(page).to have_link('Members')
end end
end end
......
require 'spec_helper'
describe 'User page' do
include ExternalAuthorizationServiceHelpers
let(:user) { create(:user) }
it 'shows the most recent activity' do
visit(user_path(user))
expect(page).to have_content('Most Recent Activity')
end
describe 'when external authorization is enabled' do
before do
enable_external_authorization_service_check
end
it 'hides the most recent activity' do
visit(user_path(user))
expect(page).not_to have_content('Most Recent Activity')
end
end
end
require 'spec_helper' require 'spec_helper'
describe IssuesFinder do describe IssuesFinder do
it_behaves_like 'a finder with external authorization service' do
let!(:subject) { create(:issue, project: project) }
let(:project_params) { { project_id: project.id } }
end
describe '#execute' do describe '#execute' do
include_context 'IssuesFinder context' include_context 'IssuesFinder context'
include_context 'IssuesFinder#execute context' include_context 'IssuesFinder#execute context'
......
require 'spec_helper'
describe LabelsFinder do
it_behaves_like 'a finder with external authorization service' do
let!(:subject) { create(:label, project: project) }
let(:project_params) { { project_id: project.id } }
end
end
require 'spec_helper' require 'spec_helper'
describe MergeRequestsFinder do describe MergeRequestsFinder do
it_behaves_like 'a finder with external authorization service' do
let!(:subject) { create(:merge_request, source_project: project) }
let(:project_params) { { project_id: project.id } }
end
describe '#execute' do describe '#execute' do
include_context 'MergeRequestsFinder multiple projects with merge requests context' include_context 'MergeRequestsFinder multiple projects with merge requests context'
......
require 'spec_helper' require 'spec_helper'
describe SnippetsFinder do describe SnippetsFinder do
include ExternalAuthorizationServiceHelpers
it_behaves_like 'a finder with external authorization service' do
let!(:subject) { create(:project_snippet, project: project) }
let(:project_params) { { project: project } }
end
context 'external authorization service enabled' do
let(:user) { create(:user) }
let(:project) { create(:project) }
let!(:snippet) { create(:project_snippet, :public, project: project) }
before do
project.add_maintainer(user)
end
it 'includes the result if the external service allows access' do
external_service_allow_access(user, project)
results = described_class.new(user, project: project).execute
expect(results).to contain_exactly(snippet)
end
it 'does not include any results if the external service denies access' do
external_service_deny_access(user, project)
results = described_class.new(user, project: project).execute
expect(results).to be_empty
end
end
context 'filter by project' do context 'filter by project' do
set(:user) { create(:user) } set(:user) { create(:user) }
set(:group) { create(:group, :public) } set(:group) { create(:group, :public) }
......
require 'spec_helper'
describe TodosFinder do
it_behaves_like 'a finder with external authorization service' do
let!(:subject) { create(:todo, project: project, user: user) }
let(:project_params) { { project_id: project.id } }
end
end
...@@ -36,53 +36,6 @@ describe ApplicationSetting do ...@@ -36,53 +36,6 @@ describe ApplicationSetting do
it { is_expected.not_to allow_value("a" * (subject.email_additional_text_character_limit + 1)).for(:email_additional_text) } it { is_expected.not_to allow_value("a" * (subject.email_additional_text_character_limit + 1)).for(:email_additional_text) }
end end
describe 'when external authorization service is enabled' do
before do
stub_licensed_features(external_authorization_service: true)
setting.external_authorization_service_enabled = true
end
it { is_expected.not_to allow_value('not a URL').for(:external_authorization_service_url) }
it { is_expected.to allow_value('https://example.com').for(:external_authorization_service_url) }
it { is_expected.to allow_value('').for(:external_authorization_service_url) }
it { is_expected.not_to allow_value(nil).for(:external_authorization_service_default_label) }
it { is_expected.not_to allow_value(11).for(:external_authorization_service_timeout) }
it { is_expected.not_to allow_value(0).for(:external_authorization_service_timeout) }
it { is_expected.not_to allow_value('not a certificate').for(:external_auth_client_cert) }
it { is_expected.to allow_value('').for(:external_auth_client_cert) }
it { is_expected.to allow_value('').for(:external_auth_client_key) }
context 'when setting a valid client certificate for external authorization' do
let(:certificate_data) { File.read('ee/spec/fixtures/passphrase_x509_certificate.crt') }
before do
setting.external_auth_client_cert = certificate_data
end
it 'requires a valid client key when a certificate is set' do
expect(setting).not_to allow_value('fefefe').for(:external_auth_client_key)
end
it 'requires a matching certificate' do
other_private_key = File.read('ee/spec/fixtures/x509_certificate_pk.key')
expect(setting).not_to allow_value(other_private_key).for(:external_auth_client_key)
end
it 'the credentials are valid when the private key can be read and matches the certificate' do
tls_attributes = [:external_auth_client_key_pass,
:external_auth_client_key,
:external_auth_client_cert]
setting.external_auth_client_key = File.read('ee/spec/fixtures/passphrase_x509_certificate_pk.key')
setting.external_auth_client_key_pass = '5iveL!fe'
setting.validate
expect(setting.errors).not_to include(*tls_attributes)
end
end
end
context 'when validating allowed_ips' do context 'when validating allowed_ips' do
where(:allowed_ips, :is_valid) do where(:allowed_ips, :is_valid) do
"192.1.1.1" | true "192.1.1.1" | true
......
require 'spec_helper' require 'spec_helper'
describe EE::ProtectedRefAccess do describe EE::ProtectedRefAccess do
include ExternalAuthorizationServiceHelpers
included_in_classes = [ProtectedBranch::MergeAccessLevel, included_in_classes = [ProtectedBranch::MergeAccessLevel,
ProtectedBranch::PushAccessLevel, ProtectedBranch::PushAccessLevel,
ProtectedTag::CreateAccessLevel] ProtectedTag::CreateAccessLevel]
...@@ -109,20 +108,4 @@ describe EE::ProtectedRefAccess do ...@@ -109,20 +108,4 @@ describe EE::ProtectedRefAccess do
end end
end end
end end
describe '#check_access' do
subject(:protected_ref_access) do
create(:protected_branch, :maintainers_can_push).push_access_levels.first
end
let(:project) { protected_ref_access.project }
it 'is false if external authorization denies access' do
maintainer = create(:user)
project.add_maintainer(maintainer)
external_service_deny_access(maintainer, project)
expect(protected_ref_access.check_access(maintainer)).to be_falsey
end
end
end end
require 'spec_helper' require 'spec_helper'
describe Issue do describe Issue do
using RSpec::Parameterized::TableSyntax
include ExternalAuthorizationServiceHelpers include ExternalAuthorizationServiceHelpers
using RSpec::Parameterized::TableSyntax
describe 'validations' do describe 'validations' do
subject { build(:issue) } subject { build(:issue) }
...@@ -131,50 +132,6 @@ describe Issue do ...@@ -131,50 +132,6 @@ describe Issue do
end end
end end
context 'when an external authentication service' do
before do
enable_external_authorization_service_check
end
describe '#publicly_visible?' do
it 'is `false` when an external authorization service is enabled' do
issue = build(:issue, project: build(:project, :public))
expect(issue).not_to be_publicly_visible
end
end
describe '#readable_by?' do
it 'checks the external service to determine if an issue is readable by a user' do
project = build(:project, :public,
external_authorization_classification_label: 'a-label')
issue = build(:issue, project: project)
user = build(:user)
expect(EE::Gitlab::ExternalAuthorization).to receive(:access_allowed?).with(user, 'a-label') { false }
expect(issue.readable_by?(user)).to be_falsy
end
it 'does not check the external webservice for admins' do
issue = build(:issue)
user = build(:admin)
expect(EE::Gitlab::ExternalAuthorization).not_to receive(:access_allowed?)
issue.readable_by?(user)
end
it 'does not check the external webservice for auditors' do
issue = build(:issue)
user = build(:auditor)
expect(EE::Gitlab::ExternalAuthorization).not_to receive(:access_allowed?)
issue.readable_by?(user)
end
end
end
describe 'relative positioning with group boards' do describe 'relative positioning with group boards' do
let(:group) { create(:group) } let(:group) { create(:group) }
let!(:board) { create(:board, group: group) } let!(:board) { create(:board, group: group) }
...@@ -380,4 +337,21 @@ describe Issue do ...@@ -380,4 +337,21 @@ describe Issue do
end end
end end
end end
context 'when an external authentication service' do
before do
enable_external_authorization_service_check
end
describe '#visible_to_user?' do
it 'does not check the external webservice for auditors' do
issue = build(:issue)
user = build(:auditor)
expect(::Gitlab::ExternalAuthorization).not_to receive(:access_allowed?)
issue.visible_to_user?(user)
end
end
end
end end
...@@ -4,7 +4,6 @@ require 'spec_helper' ...@@ -4,7 +4,6 @@ require 'spec_helper'
describe Project do describe Project do
include ProjectForksHelper include ProjectForksHelper
include ExternalAuthorizationServiceHelpers
include ::EE::GeoHelpers include ::EE::GeoHelpers
using RSpec::Parameterized::TableSyntax using RSpec::Parameterized::TableSyntax
...@@ -1242,43 +1241,6 @@ describe Project do ...@@ -1242,43 +1241,6 @@ describe Project do
end end
end end
describe '#external_authorization_classification_label' do
it 'falls back to the default when none is configured' do
enable_external_authorization_service_check
expect(build(:project).external_authorization_classification_label)
.to eq('default_label')
end
it 'returns `nil` if the feature is disabled' do
stub_licensed_features(external_authorization_service: false)
project = build(:project,
external_authorization_classification_label: 'hello')
expect(project.external_authorization_classification_label)
.to eq(nil)
end
it 'returns the classification label if it was configured on the project' do
enable_external_authorization_service_check
project = build(:project,
external_authorization_classification_label: 'hello')
expect(project.external_authorization_classification_label)
.to eq('hello')
end
it 'does not break when not stubbing the license check' do
enable_external_authorization_service_check
enable_namespace_license_check!
project = build(:project)
expect { project.external_authorization_classification_label }.not_to raise_error
end
end
describe '#licensed_features' do describe '#licensed_features' do
let(:plan_license) { :free } let(:plan_license) { :free }
let(:global_license) { create(:license) } let(:global_license) { create(:license) }
......
...@@ -3,25 +3,12 @@ require 'spec_helper' ...@@ -3,25 +3,12 @@ require 'spec_helper'
describe BasePolicy do describe BasePolicy do
include ExternalAuthorizationServiceHelpers include ExternalAuthorizationServiceHelpers
let(:current_user) { create(:user) }
let(:user) { create(:user) }
subject { described_class.new(current_user, [user]) }
describe 'read cross project' do describe 'read cross project' do
it { is_expected.to be_allowed(:read_cross_project) }
context 'when an external authorization service is enabled' do context 'when an external authorization service is enabled' do
before do before do
enable_external_authorization_service_check enable_external_authorization_service_check
end end
it { is_expected.not_to be_allowed(:read_cross_project) }
it 'allows admins' do
expect(described_class.new(build(:admin), nil)).to be_allowed(:read_cross_project)
end
it 'allows auditors' do it 'allows auditors' do
expect(described_class.new(build(:auditor), nil)).to be_allowed(:read_cross_project) expect(described_class.new(build(:auditor), nil)).to be_allowed(:read_cross_project)
end end
......
...@@ -143,7 +143,7 @@ describe EpicPolicy do ...@@ -143,7 +143,7 @@ describe EpicPolicy do
end end
it 'does not call external authorization service' do it 'does not call external authorization service' do
expect(EE::Gitlab::ExternalAuthorization).not_to receive(:access_allowed?) expect(::Gitlab::ExternalAuthorization).not_to receive(:access_allowed?)
subject subject
end end
......
require 'spec_helper'
describe IssuePolicy do
include ExternalAuthorizationServiceHelpers
context 'with external authorization enabled' do
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
let(:issue) { create(:issue, project: project) }
let(:policies) { described_class.new(user, issue) }
before do
enable_external_authorization_service_check
end
it 'can read the issue iid without accessing the external service' do
expect(EE::Gitlab::ExternalAuthorization).not_to receive(:access_allowed?)
expect(policies).to be_allowed(:read_issue_iid)
end
end
end
require 'spec_helper' require 'spec_helper'
describe MergeRequestPolicy do describe MergeRequestPolicy do
include ExternalAuthorizationServiceHelpers
include ProjectForksHelper include ProjectForksHelper
let(:guest) { create(:user) } let(:guest) { create(:user) }
...@@ -111,21 +110,4 @@ describe MergeRequestPolicy do ...@@ -111,21 +110,4 @@ describe MergeRequestPolicy do
end end
end end
end end
context 'with external authorization enabled' do
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
let(:merge_request) { create(:merge_request, source_project: project) }
let(:policies) { described_class.new(user, merge_request) }
before do
enable_external_authorization_service_check
end
it 'can read the issue iid without accessing the external service' do
expect(EE::Gitlab::ExternalAuthorization).not_to receive(:access_allowed?)
expect(policies).to be_allowed(:read_merge_request_iid)
end
end
end end
...@@ -190,61 +190,17 @@ describe ProjectPolicy do ...@@ -190,61 +190,17 @@ describe ProjectPolicy do
end end
context 'reading a project' do context 'reading a project' do
it 'allows access when a user has read access to the repo' do
expect(described_class.new(owner, project)).to be_allowed(:read_project)
expect(described_class.new(developer, project)).to be_allowed(:read_project)
expect(described_class.new(admin, project)).to be_allowed(:read_project)
end
it 'never checks the external service' do
expect(EE::Gitlab::ExternalAuthorization).not_to receive(:access_allowed?)
expect(described_class.new(owner, project)).to be_allowed(:read_project)
end
context 'with an external authorization service' do context 'with an external authorization service' do
before do before do
enable_external_authorization_service_check enable_external_authorization_service_check
end end
it 'allows access when the external service allows it' do
external_service_allow_access(owner, project)
external_service_allow_access(developer, project)
expect(described_class.new(owner, project)).to be_allowed(:read_project)
expect(described_class.new(developer, project)).to be_allowed(:read_project)
end
it 'does not check the external service for admins and allows access' do
expect(EE::Gitlab::ExternalAuthorization).not_to receive(:access_allowed?)
expect(described_class.new(admin, project)).to be_allowed(:read_project)
end
it 'allows auditors' do it 'allows auditors' do
stub_licensed_features(auditor_user: true) stub_licensed_features(auditor_user: true)
auditor = create(:user, :auditor) auditor = create(:user, :auditor)
expect(described_class.new(auditor, project)).to be_allowed(:read_project) expect(described_class.new(auditor, project)).to be_allowed(:read_project)
end end
it 'prevents all but seeing a public project in a list when access is denied' do
[developer, owner, build(:user), nil].each do |user|
external_service_deny_access(user, project)
policy = described_class.new(user, project)
expect(policy).not_to be_allowed(:read_project)
expect(policy).not_to be_allowed(:owner_access)
expect(policy).not_to be_allowed(:change_namespace)
end
end
it 'passes the full path to external authorization for logging purposes' do
expect(EE::Gitlab::ExternalAuthorization)
.to receive(:access_allowed?).with(owner, 'default_label', project.full_path).and_call_original
described_class.new(owner, project).allowed?(:read_project)
end
end end
end end
......
require 'spec_helper' require 'spec_helper'
describe API::Projects do describe API::Projects do
include ExternalAuthorizationServiceHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project, namespace: user.namespace) } let(:project) { create(:project, namespace: user.namespace) }
describe 'GET /projects' do describe 'GET /projects' do
it 'does not break on license checks' do it 'does not break on license checks' do
stub_licensed_features(external_authorization_service: true)
enable_namespace_license_check! enable_namespace_license_check!
create(:project, :private, namespace: user.namespace) create(:project, :private, namespace: user.namespace)
...@@ -91,20 +88,6 @@ describe API::Projects do ...@@ -91,20 +88,6 @@ describe API::Projects do
describe 'PUT /projects/:id' do describe 'PUT /projects/:id' do
let(:project) { create(:project, namespace: user.namespace) } let(:project) { create(:project, namespace: user.namespace) }
context 'when updating external classification' do
before do
enable_external_authorization_service_check
end
it 'updates the classification label' do
put(api("/projects/#{project.id}", user), params: { external_authorization_classification_label: 'new label' })
expect(response).to have_gitlab_http_status(200)
expect(project.reload.external_authorization_classification_label).to eq('new label')
end
end
context 'when updating repository storage' do context 'when updating repository storage' do
let(:unknown_storage) { 'new-storage' } let(:unknown_storage) { 'new-storage' }
let(:new_project) { create(:project, :repository, namespace: user.namespace) } let(:new_project) { create(:project, :repository, namespace: user.namespace) }
...@@ -307,51 +290,6 @@ describe API::Projects do ...@@ -307,51 +290,6 @@ describe API::Projects do
end end
describe 'GET /projects/:id' do describe 'GET /projects/:id' do
context 'with external authorization' do
let(:project) do
create(:project,
namespace: user.namespace,
external_authorization_classification_label: 'the-label')
end
context 'when the user has access to the project' do
before do
external_service_allow_access(user, project)
end
it 'includes the label in the response' do
get api("/projects/#{project.id}", user)
expect(response).to have_gitlab_http_status(200)
expect(json_response['external_authorization_classification_label']).to eq('the-label')
end
end
context 'when the external service denies access' do
before do
external_service_deny_access(user, project)
end
it 'returns a 404' do
get api("/projects/#{project.id}", user)
expect(response).to have_gitlab_http_status(404)
end
end
context 'it does not return the label when the feature is not available' do
before do
stub_licensed_features(external_authorization_service: false)
end
it 'does not include the label in the response' do
get api("/projects/#{project.id}", user)
expect(response).to have_gitlab_http_status(200)
expect(json_response['external_authorization_classification_label']).to be_nil
end
end
describe 'packages_enabled attribute' do describe 'packages_enabled attribute' do
it 'exposed when the feature is available' do it 'exposed when the feature is available' do
stub_licensed_features(packages: true) stub_licensed_features(packages: true)
...@@ -370,5 +308,4 @@ describe API::Projects do ...@@ -370,5 +308,4 @@ describe API::Projects do
end end
end end
end end
end
end end
...@@ -95,23 +95,6 @@ describe API::Settings, 'EE Settings' do ...@@ -95,23 +95,6 @@ describe API::Settings, 'EE Settings' do
it_behaves_like 'settings for licensed features' it_behaves_like 'settings for licensed features'
end end
context 'external policy classification settings' do
let(:settings) do
{
external_authorization_service_enabled: true,
external_authorization_service_url: 'https://custom.service/',
external_authorization_service_default_label: 'default',
external_authorization_service_timeout: 9.99,
external_auth_client_cert: File.read('ee/spec/fixtures/passphrase_x509_certificate.crt'),
external_auth_client_key: File.read('ee/spec/fixtures/passphrase_x509_certificate_pk.key'),
external_auth_client_key_pass: "5iveL!fe"
}
end
let(:feature) { :external_authorization_service }
it_behaves_like 'settings for licensed features'
end
context 'custom email footer' do context 'custom email footer' do
let(:settings) { { email_additional_text: 'this is a scary legal footer' } } let(:settings) { { email_additional_text: 'this is a scary legal footer' } }
let(:feature) { :email_additional_text } let(:feature) { :email_additional_text }
......
require 'spec_helper'
describe GroupChildEntity do
include ExternalAuthorizationServiceHelpers
let(:user) { create(:user) }
let(:request) { double('request') }
let(:entity) { described_class.new(object, request: request) }
subject(:json) { entity.as_json }
before do
allow(request).to receive(:current_user).and_return(user)
end
describe 'for a project with external authorization enabled' do
let(:object) do
create(:project, :with_avatar,
description: 'Awesomeness')
end
before do
enable_external_authorization_service_check
object.add_maintainer(user)
end
it 'does not hit the external authorization service' do
expect(EE::Gitlab::ExternalAuthorization).not_to receive(:access_allowed?)
expect(json[:can_edit]).to eq(false)
end
end
end
require 'spec_helper' require 'spec_helper'
describe ApplicationSettings::UpdateService do describe ApplicationSettings::UpdateService do
include ExternalAuthorizationServiceHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:setting) { ApplicationSetting.create_from_defaults } let(:setting) { ApplicationSetting.create_from_defaults }
let(:service) { described_class.new(setting, user, opts) } let(:service) { described_class.new(setting, user, opts) }
...@@ -64,37 +62,4 @@ describe ApplicationSettings::UpdateService do ...@@ -64,37 +62,4 @@ describe ApplicationSettings::UpdateService do
end end
end end
end end
context 'when external authorization is enabled' do
before do
enable_external_authorization_service_check
end
it 'does not save the settings with an error if the service denies access' do
expect(EE::Gitlab::ExternalAuthorization)
.to receive(:access_allowed?).with(user, 'new-label') { false }
described_class.new(setting, user, { external_authorization_service_default_label: 'new-label' }).execute
expect(setting.errors[:external_authorization_service_default_label]).to be_present
end
it 'saves the setting when the user has access to the label' do
expect(EE::Gitlab::ExternalAuthorization)
.to receive(:access_allowed?).with(user, 'new-label') { true }
described_class.new(setting, user, { external_authorization_service_default_label: 'new-label' }).execute
# Read the attribute directly to avoid the stub from
# `enable_external_authorization_service_check`
expect(setting[:external_authorization_service_default_label]).to eq('new-label')
end
it 'does not validate the label if it was not passed' do
expect(EE::Gitlab::ExternalAuthorization)
.not_to receive(:access_allowed?)
described_class.new(setting, user, { home_page_url: 'http://foo.bar' }).execute
end
end
end end
...@@ -2,46 +2,8 @@ require 'spec_helper' ...@@ -2,46 +2,8 @@ require 'spec_helper'
describe EE::NotificationService, :mailer do describe EE::NotificationService, :mailer do
include NotificationHelpers include NotificationHelpers
include ExternalAuthorizationServiceHelpers
let(:subject) { NotificationService.new }
context 'with external authentication service' do
let(:issue) { create(:issue) }
let(:project) { issue.project }
let(:note) { create(:note, noteable: issue, project: project) }
let(:member) { create(:user) }
before do
project.add_maintainer(member)
member.global_notification_setting.update!(level: :watch)
end
it 'sends email when the service is not enabled' do
expect(Notify).to receive(:new_issue_email).with(member.id, issue.id, nil).and_call_original
subject.new_issue(issue, member)
end
context 'when the service is enabled' do
before do
enable_external_authorization_service_check
end
it 'does not send an email' do
expect(Notify).not_to receive(:new_issue_email)
subject.new_issue(issue, member)
end
it 'still delivers email to admins' do
member.update!(admin: true)
expect(Notify).to receive(:new_issue_email).with(member.id, issue.id, nil).and_call_original let(:subject) { NotificationService.new }
subject.new_issue(issue, member)
end
end
end
context 'service desk issues' do context 'service desk issues' do
before do before do
......
require 'spec_helper' require 'spec_helper'
describe Projects::CreateService, '#execute' do describe Projects::CreateService, '#execute' do
include ExternalAuthorizationServiceHelpers
let(:user) { create :user } let(:user) { create :user }
let(:opts) do let(:opts) do
{ {
...@@ -260,42 +258,6 @@ describe Projects::CreateService, '#execute' do ...@@ -260,42 +258,6 @@ describe Projects::CreateService, '#execute' do
end end
end end
context 'with external authorization enabled' do
before do
enable_external_authorization_service_check
end
it 'does not save the project with an error if the service denies access' do
expect(EE::Gitlab::ExternalAuthorization)
.to receive(:access_allowed?).with(user, 'new-label', any_args) { false }
project = create_project(user, opts.merge({ external_authorization_classification_label: 'new-label' }))
expect(project.errors[:external_authorization_classification_label]).to be_present
expect(project).not_to be_persisted
end
it 'saves the project when the user has access to the label' do
expect(EE::Gitlab::ExternalAuthorization)
.to receive(:access_allowed?).with(user, 'new-label', any_args) { true }
project = create_project(user, opts.merge({ external_authorization_classification_label: 'new-label' }))
expect(project).to be_persisted
expect(project.external_authorization_classification_label).to eq('new-label')
end
it 'does not save the project when the user has no access to the default label and no label is provided' do
expect(EE::Gitlab::ExternalAuthorization)
.to receive(:access_allowed?).with(user, 'default_label', any_args) { false }
project = create_project(user, opts)
expect(project.errors[:external_authorization_classification_label]).to be_present
expect(project).not_to be_persisted
end
end
def create_project(user, opts) def create_project(user, opts)
described_class.new(user, opts).execute described_class.new(user, opts).execute
end end
......
...@@ -2,7 +2,6 @@ require 'spec_helper' ...@@ -2,7 +2,6 @@ require 'spec_helper'
describe Projects::UpdateService, '#execute' do describe Projects::UpdateService, '#execute' do
include EE::GeoHelpers include EE::GeoHelpers
include ExternalAuthorizationServiceHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project, :repository, creator: user, namespace: user.namespace) } let(:project) { create(:project, :repository, creator: user, namespace: user.namespace) }
...@@ -178,46 +177,6 @@ describe Projects::UpdateService, '#execute' do ...@@ -178,46 +177,6 @@ describe Projects::UpdateService, '#execute' do
end end
end end
context 'with external authorization enabled' do
before do
enable_external_authorization_service_check
end
it 'does not save the project with an error if the service denies access' do
expect(EE::Gitlab::ExternalAuthorization)
.to receive(:access_allowed?).with(user, 'new-label') { false }
result = update_project(project, user, { external_authorization_classification_label: 'new-label' })
expect(result[:message]).to be_present
expect(result[:status]).to eq(:error)
end
it 'saves the new label if the service allows access' do
expect(EE::Gitlab::ExternalAuthorization)
.to receive(:access_allowed?).with(user, 'new-label') { true }
result = update_project(project, user, { external_authorization_classification_label: 'new-label' })
expect(result[:status]).to eq(:success)
expect(project.reload.external_authorization_classification_label).to eq('new-label')
end
it 'checks the default label when the classification label was cleared' do
expect(EE::Gitlab::ExternalAuthorization)
.to receive(:access_allowed?).with(user, 'default_label') { true }
update_project(project, user, { external_authorization_classification_label: '' })
end
it 'does not check the label when it does not change' do
expect(EE::Gitlab::ExternalAuthorization)
.not_to receive(:access_allowed?)
update_project(project, user, { name: 'New name' })
end
end
context 'with approval_rules' do context 'with approval_rules' do
context 'when approval_rules is disabled' do context 'when approval_rules is disabled' do
it "updates approval_rules' approvals_required" do it "updates approval_rules' approvals_required" do
......
...@@ -277,6 +277,7 @@ module API ...@@ -277,6 +277,7 @@ module API
expose :statistics, using: 'API::Entities::ProjectStatistics', if: -> (project, options) { expose :statistics, using: 'API::Entities::ProjectStatistics', if: -> (project, options) {
options[:statistics] && Ability.allowed?(options[:current_user], :read_statistics, project) options[:statistics] && Ability.allowed?(options[:current_user], :read_statistics, project)
} }
expose :external_authorization_classification_label
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def self.preload_relation(projects_relation, options = {}) def self.preload_relation(projects_relation, options = {})
...@@ -1120,6 +1121,8 @@ module API ...@@ -1120,6 +1121,8 @@ module API
expose(:default_snippet_visibility) { |setting, _options| Gitlab::VisibilityLevel.string_level(setting.default_snippet_visibility) } expose(:default_snippet_visibility) { |setting, _options| Gitlab::VisibilityLevel.string_level(setting.default_snippet_visibility) }
expose(:default_group_visibility) { |setting, _options| Gitlab::VisibilityLevel.string_level(setting.default_group_visibility) } expose(:default_group_visibility) { |setting, _options| Gitlab::VisibilityLevel.string_level(setting.default_group_visibility) }
expose(*::ApplicationSettingsHelper.external_authorization_service_attributes)
# support legacy names, can be removed in v5 # support legacy names, can be removed in v5
expose :password_authentication_enabled_for_web, as: :password_authentication_enabled expose :password_authentication_enabled_for_web, as: :password_authentication_enabled
expose :password_authentication_enabled_for_web, as: :signin_enabled expose :password_authentication_enabled_for_web, as: :signin_enabled
......
...@@ -29,13 +29,13 @@ module API ...@@ -29,13 +29,13 @@ module API
optional :printing_merge_request_link_enabled, type: Boolean, desc: 'Show link to create/view merge request when pushing from the command line' optional :printing_merge_request_link_enabled, type: Boolean, desc: 'Show link to create/view merge request when pushing from the command line'
optional :merge_method, type: String, values: %w(ff rebase_merge merge), desc: 'The merge method used when merging merge requests' optional :merge_method, type: String, values: %w(ff rebase_merge merge), desc: 'The merge method used when merging merge requests'
optional :initialize_with_readme, type: Boolean, desc: "Initialize a project with a README.md" optional :initialize_with_readme, type: Boolean, desc: "Initialize a project with a README.md"
optional :external_authorization_classification_label, type: String, desc: 'The classification label for the project'
end end
if Gitlab.ee? if Gitlab.ee?
params :optional_project_params_ee do params :optional_project_params_ee do
optional :repository_storage, type: String, desc: 'Which storage shard the repository is on. Available only to admins' optional :repository_storage, type: String, desc: 'Which storage shard the repository is on. Available only to admins'
optional :approvals_before_merge, type: Integer, desc: 'How many approvers should approve merge request by default' optional :approvals_before_merge, type: Integer, desc: 'How many approvers should approve merge request by default'
optional :external_authorization_classification_label, type: String, desc: 'The classification label for the project'
optional :mirror, type: Boolean, desc: 'Enables pull mirroring in a project' optional :mirror, type: Boolean, desc: 'Enables pull mirroring in a project'
optional :mirror_trigger_builds, type: Boolean, desc: 'Pull mirroring triggers builds' optional :mirror_trigger_builds, type: Boolean, desc: 'Pull mirroring triggers builds'
end end
...@@ -72,7 +72,8 @@ module API ...@@ -72,7 +72,8 @@ module API
:tag_list, :tag_list,
:visibility, :visibility,
:wiki_enabled, :wiki_enabled,
:avatar :avatar,
:external_authorization_classification_label
] ]
end end
end end
......
...@@ -168,7 +168,9 @@ module API ...@@ -168,7 +168,9 @@ module API
optional :usage_ping_enabled, type: Boolean, desc: 'Every week GitLab will report license usage back to GitLab, Inc.' optional :usage_ping_enabled, type: Boolean, desc: 'Every week GitLab will report license usage back to GitLab, Inc.'
end end
optional_attributes = ::ApplicationSettingsHelper.visible_attributes << :performance_bar_allowed_group_id optional_attributes = [*::ApplicationSettingsHelper.visible_attributes,
*::ApplicationSettingsHelper.external_authorization_service_attributes,
:performance_bar_allowed_group_id]
if Gitlab.ee? if Gitlab.ee?
optional_attributes += EE::ApplicationSettingsHelper.possible_licensed_attributes optional_attributes += EE::ApplicationSettingsHelper.possible_licensed_attributes
......
# frozen_string_literal: true
module Gitlab
module ExternalAuthorization
extend ExternalAuthorization::Config
RequestFailed = Class.new(StandardError)
def self.access_allowed?(user, label, project_path = nil)
return true unless perform_check?
return false unless user
access_for_user_to_label(user, label, project_path).has_access?
end
def self.rejection_reason(user, label)
return unless enabled?
return unless user
access_for_user_to_label(user, label, nil).reason
end
def self.access_for_user_to_label(user, label, project_path)
if RequestStore.active?
RequestStore.fetch("external_authorisation:user-#{user.id}:label-#{label}") do
load_access(user, label, project_path)
end
else
load_access(user, label, project_path)
end
end
def self.load_access(user, label, project_path)
access = ::Gitlab::ExternalAuthorization::Access.new(user, label).load!
::Gitlab::ExternalAuthorization::Logger.log_access(access, project_path)
access
end
end
end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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