Add multi-tenancy support for SAML
This commit is contained in:
committed by
Javi Martín
parent
5726bcef07
commit
c9bf7797a0
@@ -16,6 +16,11 @@ module OmniauthTenantSetup
|
|||||||
oauth2(env, secrets.wordpress_oauth2_key, secrets.wordpress_oauth2_secret)
|
oauth2(env, secrets.wordpress_oauth2_key, secrets.wordpress_oauth2_secret)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def saml(env)
|
||||||
|
saml_auth(env, secrets.saml_sp_entity_id,
|
||||||
|
secrets.saml_idp_metadata_url, secrets.saml_idp_sso_service_url)
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def oauth(env, key, secret)
|
def oauth(env, key, secret)
|
||||||
@@ -32,6 +37,24 @@ module OmniauthTenantSetup
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def saml_auth(env, sp_entity_id, idp_metadata_url, idp_sso_service_url)
|
||||||
|
unless Tenant.default?
|
||||||
|
strategy = env["omniauth.strategy"]
|
||||||
|
|
||||||
|
strategy.options[:sp_entity_id] = sp_entity_id if sp_entity_id.present?
|
||||||
|
strategy.options[:idp_metadata_url] = idp_metadata_url if idp_metadata_url.present?
|
||||||
|
strategy.options[:idp_sso_service_url] = idp_sso_service_url if idp_sso_service_url.present?
|
||||||
|
|
||||||
|
if strategy.options[:issuer].present? && sp_entity_id.present?
|
||||||
|
strategy.options[:issuer] = sp_entity_id
|
||||||
|
end
|
||||||
|
|
||||||
|
if strategy.options[:idp_metadata].present? && idp_metadata_url.present?
|
||||||
|
strategy.options[:idp_metadata] = idp_metadata_url
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def secrets
|
def secrets
|
||||||
Tenant.current_secrets
|
Tenant.current_secrets
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -294,7 +294,7 @@ Devise.setup do |config|
|
|||||||
saml_settings[:sp_entity_id] = Rails.application.secrets.saml_sp_entity_id
|
saml_settings[:sp_entity_id] = Rails.application.secrets.saml_sp_entity_id
|
||||||
saml_settings[:allowed_clock_drift] = 1.minute
|
saml_settings[:allowed_clock_drift] = 1.minute
|
||||||
end
|
end
|
||||||
config.omniauth :saml, saml_settings
|
config.omniauth :saml, saml_settings.merge(setup: ->(env) { OmniauthTenantSetup.saml(env) })
|
||||||
|
|
||||||
# ==> Warden configuration
|
# ==> Warden configuration
|
||||||
# If you want to use other strategies, that are not supported by Devise, or
|
# If you want to use other strategies, that are not supported by Devise, or
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ staging:
|
|||||||
# secret_key: my_secret_value
|
# secret_key: my_secret_value
|
||||||
#
|
#
|
||||||
# Currently you can overwrite SMTP, SMS, manager, microsoft API,
|
# Currently you can overwrite SMTP, SMS, manager, microsoft API,
|
||||||
# HTTP basic, twitter, facebook, google, wordpress and security settings.
|
# HTTP basic, twitter, facebook, google, wordpress, SAML and security settings.
|
||||||
twitter_key: ""
|
twitter_key: ""
|
||||||
twitter_secret: ""
|
twitter_secret: ""
|
||||||
facebook_key: ""
|
facebook_key: ""
|
||||||
@@ -140,7 +140,7 @@ preproduction:
|
|||||||
# secret_key: my_secret_value
|
# secret_key: my_secret_value
|
||||||
#
|
#
|
||||||
# Currently you can overwrite SMTP, SMS, manager, microsoft API,
|
# Currently you can overwrite SMTP, SMS, manager, microsoft API,
|
||||||
# HTTP basic, twitter, facebook, google, wordpress and security settings.
|
# HTTP basic, twitter, facebook, google, wordpress, SAML and security settings.
|
||||||
twitter_key: ""
|
twitter_key: ""
|
||||||
twitter_secret: ""
|
twitter_secret: ""
|
||||||
facebook_key: ""
|
facebook_key: ""
|
||||||
@@ -198,7 +198,7 @@ production:
|
|||||||
# secret_key: my_secret_value
|
# secret_key: my_secret_value
|
||||||
#
|
#
|
||||||
# Currently you can overwrite SMTP, SMS, manager, microsoft API,
|
# Currently you can overwrite SMTP, SMS, manager, microsoft API,
|
||||||
# HTTP basic, twitter, facebook, google, wordpress and security settings.
|
# HTTP basic, twitter, facebook, google, wordpress, SAML and security settings.
|
||||||
twitter_key: ""
|
twitter_key: ""
|
||||||
twitter_secret: ""
|
twitter_secret: ""
|
||||||
facebook_key: ""
|
facebook_key: ""
|
||||||
|
|||||||
87
spec/lib/omniauth_tenant_setup_spec.rb
Normal file
87
spec/lib/omniauth_tenant_setup_spec.rb
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
require "rails_helper"
|
||||||
|
|
||||||
|
describe OmniauthTenantSetup do
|
||||||
|
describe "#saml" do
|
||||||
|
it "uses different secrets for different tenants" do
|
||||||
|
create(:tenant, schema: "mars")
|
||||||
|
create(:tenant, schema: "venus")
|
||||||
|
|
||||||
|
stub_secrets(
|
||||||
|
saml_sp_entity_id: "https://default.consul.dev/saml/metadata",
|
||||||
|
saml_idp_metadata_url: "https://default-idp.example.com/metadata",
|
||||||
|
saml_idp_sso_service_url: "https://default-idp.example.com/sso",
|
||||||
|
tenants: {
|
||||||
|
mars: {
|
||||||
|
saml_sp_entity_id: "https://mars.consul.dev/saml/metadata",
|
||||||
|
saml_idp_metadata_url: "https://mars-idp.example.com/metadata",
|
||||||
|
saml_idp_sso_service_url: "https://mars-idp.example.com/sso"
|
||||||
|
},
|
||||||
|
venus: {
|
||||||
|
saml_sp_entity_id: "https://venus.consul.dev/saml/metadata",
|
||||||
|
saml_idp_metadata_url: "https://venus-idp.example.com/metadata",
|
||||||
|
saml_idp_sso_service_url: "https://venus-idp.example.com/sso"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Tenant.switch("mars") do
|
||||||
|
mars_env = {
|
||||||
|
"omniauth.strategy" => double(options: {}),
|
||||||
|
"HTTP_HOST" => "mars.consul.dev"
|
||||||
|
}
|
||||||
|
|
||||||
|
OmniauthTenantSetup.saml(mars_env)
|
||||||
|
mars_strategy_options = mars_env["omniauth.strategy"].options
|
||||||
|
|
||||||
|
expect(mars_strategy_options[:sp_entity_id]).to eq "https://mars.consul.dev/saml/metadata"
|
||||||
|
expect(mars_strategy_options[:idp_metadata_url]).to eq "https://mars-idp.example.com/metadata"
|
||||||
|
expect(mars_strategy_options[:idp_sso_service_url]).to eq "https://mars-idp.example.com/sso"
|
||||||
|
end
|
||||||
|
|
||||||
|
Tenant.switch("venus") do
|
||||||
|
venus_env = {
|
||||||
|
"omniauth.strategy" => double(options: {}),
|
||||||
|
"HTTP_HOST" => "venus.consul.dev"
|
||||||
|
}
|
||||||
|
|
||||||
|
OmniauthTenantSetup.saml(venus_env)
|
||||||
|
venus_strategy_options = venus_env["omniauth.strategy"].options
|
||||||
|
|
||||||
|
expect(venus_strategy_options[:sp_entity_id]).to eq "https://venus.consul.dev/saml/metadata"
|
||||||
|
expect(venus_strategy_options[:idp_metadata_url]).to eq "https://venus-idp.example.com/metadata"
|
||||||
|
expect(venus_strategy_options[:idp_sso_service_url]).to eq "https://venus-idp.example.com/sso"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it "uses default secrets for non-overridden tenant" do
|
||||||
|
create(:tenant, schema: "earth")
|
||||||
|
|
||||||
|
stub_secrets(
|
||||||
|
saml_sp_entity_id: "https://default.consul.dev/saml/metadata",
|
||||||
|
saml_idp_metadata_url: "https://default-idp.example.com/metadata",
|
||||||
|
saml_idp_sso_service_url: "https://default-idp.example.com/sso",
|
||||||
|
tenants: {
|
||||||
|
mars: {
|
||||||
|
saml_sp_entity_id: "https://mars.consul.dev/saml/metadata",
|
||||||
|
saml_idp_metadata_url: "https://mars-idp.example.com/metadata",
|
||||||
|
saml_idp_sso_service_url: "https://mars-idp.example.com/sso"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
Tenant.switch("earth") do
|
||||||
|
earth_env = {
|
||||||
|
"omniauth.strategy" => double(options: {}),
|
||||||
|
"HTTP_HOST" => "earth.consul.dev"
|
||||||
|
}
|
||||||
|
|
||||||
|
OmniauthTenantSetup.saml(earth_env)
|
||||||
|
earth_strategy_options = earth_env["omniauth.strategy"].options
|
||||||
|
|
||||||
|
expect(earth_strategy_options[:sp_entity_id]).to eq "https://default.consul.dev/saml/metadata"
|
||||||
|
expect(earth_strategy_options[:idp_metadata_url]).to eq "https://default-idp.example.com/metadata"
|
||||||
|
expect(earth_strategy_options[:idp_sso_service_url]).to eq "https://default-idp.example.com/sso"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -602,6 +602,48 @@ describe "Users" do
|
|||||||
|
|
||||||
expect(page).to have_field "Email", with: "tester@consul.dev"
|
expect(page).to have_field "Email", with: "tester@consul.dev"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
scenario "SAML user from one tenant cannot sign in to another tenant", :seed_tenants do
|
||||||
|
%w[mars venus].each do |schema|
|
||||||
|
create(:tenant, schema: schema)
|
||||||
|
Tenant.switch(schema) { Setting["feature.saml_login"] = true }
|
||||||
|
end
|
||||||
|
|
||||||
|
Tenant.switch("mars") do
|
||||||
|
mars_user = create(:user, username: "marsuser", email: "mars@consul.dev")
|
||||||
|
create(:identity, uid: "mars-saml-123", provider: "saml", user: mars_user)
|
||||||
|
end
|
||||||
|
|
||||||
|
mars_saml_hash = {
|
||||||
|
provider: "saml",
|
||||||
|
uid: "mars-saml-123",
|
||||||
|
info: {
|
||||||
|
name: "marsuser",
|
||||||
|
email: "mars@consul.dev"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OmniAuth.config.add_mock(:saml, mars_saml_hash)
|
||||||
|
|
||||||
|
with_subdomain("mars") do
|
||||||
|
visit new_user_session_path
|
||||||
|
click_button "Sign in with SAML"
|
||||||
|
|
||||||
|
expect(page).to have_content "Successfully identified as Saml"
|
||||||
|
|
||||||
|
within("#notice") { click_button "Close" }
|
||||||
|
click_link "My account"
|
||||||
|
|
||||||
|
expect(page).to have_field "Username", with: "marsuser"
|
||||||
|
end
|
||||||
|
|
||||||
|
with_subdomain("venus") do
|
||||||
|
visit new_user_session_path
|
||||||
|
click_button "Sign in with SAML"
|
||||||
|
|
||||||
|
expect(page).to have_content "To continue, please click on the confirmation " \
|
||||||
|
"link that we have sent you via email"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user