Commit 89571f3f authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch 'jej/group-saml-metadata-common-changes' into 'master'

Extract SAML refactoring from metadata MR

See merge request gitlab-org/gitlab-ee!7997
parents 653f7a91 50431f66
...@@ -8,48 +8,68 @@ class SamlProvider < ActiveRecord::Base ...@@ -8,48 +8,68 @@ class SamlProvider < ActiveRecord::Base
after_initialize :set_defaults, if: :new_record? after_initialize :set_defaults, if: :new_record?
NAME_IDENTIFIER_FORMAT = 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified'.freeze delegate :assertion_consumer_service_url, :issuer, :name_identifier_format, to: :defaults
def assertion_consumer_service_url
"#{full_group_path}/-/saml/callback"
end
def issuer
full_group_path
end
def name_identifier_format
NAME_IDENTIFIER_FORMAT
end
def certificate_fingerprint=(value) def certificate_fingerprint=(value)
super(strip_left_to_right_chars(value)) super(strip_left_to_right_chars(value))
end end
def settings def settings
{ defaults.to_h.merge(
assertion_consumer_service_url: assertion_consumer_service_url,
issuer: issuer,
idp_cert_fingerprint: certificate_fingerprint, idp_cert_fingerprint: certificate_fingerprint,
idp_sso_target_url: sso_url, idp_sso_target_url: sso_url
name_identifier_format: name_identifier_format )
}
end end
private def defaults
@defaults ||= DefaultOptions.new(group.full_path)
end
class DefaultOptions
NAME_IDENTIFIER_FORMAT = 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified'.freeze
def initialize(group_path)
@group_path = group_path
end
def full_group_path def name_identifier_format
"#{host}/groups/#{group.full_path}" NAME_IDENTIFIER_FORMAT
end
def full_group_path
"#{host}/groups/#{@group_path}"
end
def issuer
full_group_path
end
def assertion_consumer_service_url
"#{full_group_path}/-/saml/callback"
end
def to_h
{
assertion_consumer_service_url: assertion_consumer_service_url,
issuer: issuer,
name_identifier_format: name_identifier_format,
idp_sso_target_url_runtime_params: { redirect_to: :RelayState }
}
end
private
def host
@host ||= Gitlab.config.gitlab.url
end
end end
private
def set_defaults def set_defaults
self.enabled = true self.enabled = true
end end
def host
@host ||= Gitlab.config.gitlab.url
end
def strip_left_to_right_chars(input) def strip_left_to_right_chars(input)
input&.gsub(/[\u200E]/, '') input&.gsub(/[\u200E]/, '')
end end
......
...@@ -4,26 +4,26 @@ module Gitlab ...@@ -4,26 +4,26 @@ module Gitlab
class DynamicSettings class DynamicSettings
include Enumerable include Enumerable
delegate :each, :keys, :[], to: :settings delegate :each, :keys, :[], to: :to_h
def initialize(saml_provider) def initialize(group)
@saml_provider = saml_provider @group = group
end end
def settings def to_h
@settings ||= configured_settings.merge(default_settings) return {} unless @group
configured_settings || default_settings
end end
private private
def configured_settings def configured_settings
@saml_provider&.settings || {} @configured_settings ||= @group.saml_provider&.settings
end end
def default_settings def default_settings
{ @default_settings ||= SamlProvider::DefaultOptions.new(@group.full_path).to_h
idp_sso_target_url_runtime_params: { redirect_to: :RelayState }
}
end end
end end
end end
......
...@@ -33,9 +33,11 @@ module Gitlab ...@@ -33,9 +33,11 @@ module Gitlab
path.match(path_regex).try(:[], :group) path.match(path_regex).try(:[], :group)
end end
def path_from_params def params
params = Rack::Request.new(env).params @params ||= Rack::Request.new(env).params
end
def path_from_params
params['group_path'] params['group_path']
end end
end end
......
...@@ -5,18 +5,13 @@ module OmniAuth ...@@ -5,18 +5,13 @@ module OmniAuth
option :callback_path, ->(env) { callback?(env) } option :callback_path, ->(env) { callback?(env) }
def setup_phase def setup_phase
require_saml_provider
# Set devise scope for custom callback URL # Set devise scope for custom callback URL
env["devise.mapping"] = Devise.mappings[:user] env["devise.mapping"] = Devise.mappings[:user]
group_lookup = Gitlab::Auth::GroupSaml::GroupLookup.new(env) settings = Gitlab::Auth::GroupSaml::DynamicSettings.new(group_lookup.group).to_h
env['omniauth.strategy'].options.merge!(settings)
unless group_lookup.group_saml_enabled?
self.class.invalid_group!(group_lookup.path)
end
saml_provider = group_lookup.saml_provider
dynamic_settings = Gitlab::Auth::GroupSaml::DynamicSettings.new(saml_provider)
env['omniauth.strategy'].options.merge!(dynamic_settings.settings)
super super
end end
...@@ -34,6 +29,18 @@ module OmniAuth ...@@ -34,6 +29,18 @@ module OmniAuth
def self.callback?(env) def self.callback?(env)
env['PATH_INFO'] =~ Gitlab::PathRegex.saml_callback_regex env['PATH_INFO'] =~ Gitlab::PathRegex.saml_callback_regex
end end
private
def group_lookup
@group_lookup ||= Gitlab::Auth::GroupSaml::GroupLookup.new(env)
end
def require_saml_provider
unless group_lookup.group_saml_enabled?
self.class.invalid_group!(group_lookup.path)
end
end
end end
end end
end end
...@@ -2,13 +2,18 @@ require 'spec_helper' ...@@ -2,13 +2,18 @@ require 'spec_helper'
describe Gitlab::Auth::GroupSaml::DynamicSettings do describe Gitlab::Auth::GroupSaml::DynamicSettings do
let(:saml_provider) { create(:saml_provider) } let(:saml_provider) { create(:saml_provider) }
let(:group) { saml_provider.group }
subject { described_class.new(saml_provider) } subject { described_class.new(group) }
it 'behaves like an enumerator for settings' do it 'exposes a settings hash' do
expect(subject.to_h).to be_a(Hash) expect(subject.to_h).to be_a(Hash)
end end
it 'behaves like an enumerator for settings' do
expect(subject.to_a).to be_a(Array)
end
it 'configures requests to transfrom redirect_to to RelayState' do it 'configures requests to transfrom redirect_to to RelayState' do
expect(subject[:idp_sso_target_url_runtime_params]).to eq( redirect_to: :RelayState ) expect(subject[:idp_sso_target_url_runtime_params]).to eq( redirect_to: :RelayState )
end end
...@@ -34,4 +39,25 @@ describe Gitlab::Auth::GroupSaml::DynamicSettings do ...@@ -34,4 +39,25 @@ describe Gitlab::Auth::GroupSaml::DynamicSettings do
expect(subject.keys).to include(:name_identifier_format) expect(subject.keys).to include(:name_identifier_format)
end end
end end
describe 'sets default settings without saml_provider' do
let(:group) { create(:group) }
specify 'assertion_consumer_service_url' do
expect(subject.keys).to include(:assertion_consumer_service_url)
end
specify 'issuer' do
expect(subject.keys).to include(:issuer)
end
specify 'name_identifier_format' do
expect(subject.keys).to include(:name_identifier_format)
end
it 'excludes configured keys' do
expect(subject.keys).not_to include(:idp_cert_fingerprint)
expect(subject.keys).not_to include(:idp_sso_target_url)
end
end
end end
...@@ -50,6 +50,13 @@ describe OmniAuth::Strategies::GroupSaml, type: :strategy do ...@@ -50,6 +50,13 @@ describe OmniAuth::Strategies::GroupSaml, type: :strategy do
expect(auth_hash[:info]['email']).to eq("user@example.com") expect(auth_hash[:info]['email']).to eq("user@example.com")
end end
it 'sets omniauth setings from configured settings' do
post "/groups/my-group/-/saml/callback", SAMLResponse: saml_response
options = last_request.env['omniauth.strategy'].options
expect(options['idp_cert_fingerprint']).to eq fingerprint
end
end end
context 'with invalid SAMLResponse' do context 'with invalid SAMLResponse' do
......
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