Merge pull request #6010 from Anamika1608/saml
Add support for SAML authentication
This commit is contained in:
1
Gemfile
1
Gemfile
@@ -38,6 +38,7 @@ gem "omniauth", "~> 2.1.3"
|
||||
gem "omniauth-facebook", "~> 10.0.0"
|
||||
gem "omniauth-google-oauth2", "~> 1.2.1"
|
||||
gem "omniauth-rails_csrf_protection", "~> 1.0.2"
|
||||
gem "omniauth-saml", "~> 2.2.4"
|
||||
gem "omniauth-twitter", "~> 1.4.0"
|
||||
gem "paranoia", "~> 3.0.1"
|
||||
gem "pg", "~> 1.5.9"
|
||||
|
||||
@@ -440,6 +440,9 @@ GEM
|
||||
omniauth-rails_csrf_protection (1.0.2)
|
||||
actionpack (>= 4.2)
|
||||
omniauth (~> 2.0)
|
||||
omniauth-saml (2.2.4)
|
||||
omniauth (~> 2.1)
|
||||
ruby-saml (~> 1.18)
|
||||
omniauth-twitter (1.4.0)
|
||||
omniauth-oauth (~> 1.1)
|
||||
rack
|
||||
@@ -609,6 +612,9 @@ GEM
|
||||
rubocop-rspec (~> 3, >= 3.0.1)
|
||||
ruby-progressbar (1.13.0)
|
||||
ruby-rc4 (0.1.5)
|
||||
ruby-saml (1.18.0)
|
||||
nokogiri (>= 1.13.10)
|
||||
rexml
|
||||
ruby-vips (2.2.3)
|
||||
ffi (~> 1.12)
|
||||
logger
|
||||
@@ -797,6 +803,7 @@ DEPENDENCIES
|
||||
omniauth-facebook (~> 10.0.0)
|
||||
omniauth-google-oauth2 (~> 1.2.1)
|
||||
omniauth-rails_csrf_protection (~> 1.0.2)
|
||||
omniauth-saml (~> 2.2.4)
|
||||
omniauth-twitter (~> 1.4.0)
|
||||
paranoia (~> 3.0.1)
|
||||
pdf-reader (~> 2.14.1)
|
||||
|
||||
@@ -1392,7 +1392,8 @@ table {
|
||||
.button.button-twitter,
|
||||
.button.button-facebook,
|
||||
.button.button-google,
|
||||
.button.button-wordpress {
|
||||
.button.button-wordpress,
|
||||
.button.button-saml {
|
||||
color: inherit;
|
||||
font-weight: bold;
|
||||
|
||||
@@ -1442,6 +1443,17 @@ table {
|
||||
}
|
||||
}
|
||||
|
||||
.button.button-saml {
|
||||
@include has-fa-icon(lock, solid);
|
||||
background: #eafee9;
|
||||
border-left: 3px solid #3b9857;
|
||||
|
||||
&::before {
|
||||
color: #3b9857;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 14. Verification
|
||||
// ----------------
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ class Admin::Settings::FeaturesTabComponent < ApplicationComponent
|
||||
feature.featured_proposals
|
||||
feature.facebook_login
|
||||
feature.google_login
|
||||
feature.saml_login
|
||||
feature.twitter_login
|
||||
feature.wordpress_login
|
||||
feature.signature_sheets
|
||||
|
||||
@@ -16,7 +16,8 @@ class Devise::OmniauthFormComponent < ApplicationComponent
|
||||
(:twitter if feature?(:twitter_login)),
|
||||
(:facebook if feature?(:facebook_login)),
|
||||
(:google_oauth2 if feature?(:google_login)),
|
||||
(:wordpress_oauth2 if feature?(:wordpress_login))
|
||||
(:wordpress_oauth2 if feature?(:wordpress_login)),
|
||||
(:saml if feature?(:saml_login))
|
||||
].compact
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
||||
skip_before_action :verify_authenticity_token, only: :saml
|
||||
|
||||
def twitter
|
||||
sign_in_with :twitter_login, :twitter
|
||||
end
|
||||
@@ -15,6 +17,10 @@ class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
||||
sign_in_with :wordpress_login, :wordpress_oauth2
|
||||
end
|
||||
|
||||
def saml
|
||||
sign_in_with :saml_login, :saml
|
||||
end
|
||||
|
||||
def after_sign_in_path_for(resource)
|
||||
if resource.registering_with_oauth
|
||||
finish_signup_path
|
||||
|
||||
@@ -16,6 +16,11 @@ module OmniauthTenantSetup
|
||||
oauth2(env, secrets.wordpress_oauth2_key, secrets.wordpress_oauth2_secret)
|
||||
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
|
||||
|
||||
def oauth(env, key, secret)
|
||||
@@ -32,6 +37,24 @@ module OmniauthTenantSetup
|
||||
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
|
||||
Tenant.current_secrets
|
||||
end
|
||||
|
||||
@@ -85,6 +85,7 @@ class Setting < ApplicationRecord
|
||||
"feature.remote_census": nil,
|
||||
"feature.valuation_comment_notification": true,
|
||||
"feature.graphql_api": true,
|
||||
"feature.saml_login": false,
|
||||
"feature.sdg": true,
|
||||
"feature.machine_learning": false,
|
||||
"feature.remove_investments_supports": true,
|
||||
|
||||
@@ -286,6 +286,15 @@ Devise.setup do |config|
|
||||
Rails.application.secrets.wordpress_oauth2_secret,
|
||||
client_options: { site: Rails.application.secrets.wordpress_oauth2_site },
|
||||
setup: ->(env) { OmniauthTenantSetup.wordpress_oauth2(env) }
|
||||
saml_settings = {}
|
||||
if Rails.application.secrets.saml_idp_metadata_url.present?
|
||||
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
|
||||
saml_settings = idp_metadata_parser.parse_remote_to_hash(Rails.application.secrets.saml_idp_metadata_url)
|
||||
saml_settings[:idp_sso_service_url] = Rails.application.secrets.saml_idp_sso_service_url
|
||||
saml_settings[:sp_entity_id] = Rails.application.secrets.saml_sp_entity_id
|
||||
saml_settings[:allowed_clock_drift] = 1.minute
|
||||
end
|
||||
config.omniauth :saml, saml_settings.merge(setup: ->(env) { OmniauthTenantSetup.saml(env) })
|
||||
|
||||
# ==> Warden configuration
|
||||
# If you want to use other strategies, that are not supported by Devise, or
|
||||
|
||||
@@ -278,6 +278,10 @@ en:
|
||||
info:
|
||||
sign_in: "Sign in with:"
|
||||
sign_up: "Sign up with:"
|
||||
saml:
|
||||
sign_in: Sign in with SAML
|
||||
sign_up: Sign up with SAML
|
||||
name: SAML
|
||||
or_fill: "Or fill the following form:"
|
||||
proposals:
|
||||
create:
|
||||
|
||||
@@ -91,6 +91,8 @@ en:
|
||||
google_login_description: "Allow users to sign up with their Google Account"
|
||||
wordpress_login: "Wordpress login"
|
||||
wordpress_login_description: "Allow users to sign up with their Wordpress Account"
|
||||
saml_login: "SAML login"
|
||||
saml_login_description: "Allow users to sign up with SAML"
|
||||
featured_proposals: "Featured proposals"
|
||||
featured_proposals_description: "Shows featured proposals on index proposals page"
|
||||
signature_sheets: "Signature sheets"
|
||||
|
||||
@@ -275,6 +275,10 @@ es:
|
||||
sign_in: Entra con Twitter
|
||||
sign_up: Regístrate con Twitter
|
||||
name: Twitter
|
||||
saml:
|
||||
sign_in: Entra con SAML
|
||||
sign_up: Regístrate con SAML
|
||||
name: SAML
|
||||
info:
|
||||
sign_in: "Entra con:"
|
||||
sign_up: "Regístrate con:"
|
||||
|
||||
@@ -91,6 +91,8 @@ es:
|
||||
google_login_description: "Permitir que los usuarios se registren con su cuenta de Google"
|
||||
wordpress_login: "Registro con Wordpress"
|
||||
wordpress_login_description: "Permitir que los usuarios se registren con su cuenta de Wordpress"
|
||||
saml_login: "Registro con SAML"
|
||||
saml_login_description: "Permitir que los usuarios se registren usando SAML"
|
||||
featured_proposals: "Propuestas destacadas"
|
||||
featured_proposals_description: "Muestra propuestas destacadas en la página principal de propuestas"
|
||||
signature_sheets: "Hojas de firmas"
|
||||
|
||||
@@ -81,7 +81,7 @@ staging:
|
||||
# secret_key: my_secret_value
|
||||
#
|
||||
# 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_secret: ""
|
||||
facebook_key: ""
|
||||
@@ -91,6 +91,9 @@ staging:
|
||||
wordpress_oauth2_key: ""
|
||||
wordpress_oauth2_secret: ""
|
||||
wordpress_oauth2_site: ""
|
||||
saml_sp_entity_id: ""
|
||||
saml_idp_metadata_url: ""
|
||||
saml_idp_sso_service_url: ""
|
||||
<<: *maps
|
||||
<<: *apis
|
||||
|
||||
@@ -137,7 +140,7 @@ preproduction:
|
||||
# secret_key: my_secret_value
|
||||
#
|
||||
# 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_secret: ""
|
||||
facebook_key: ""
|
||||
@@ -147,6 +150,9 @@ preproduction:
|
||||
wordpress_oauth2_key: ""
|
||||
wordpress_oauth2_secret: ""
|
||||
wordpress_oauth2_site: ""
|
||||
saml_sp_entity_id: ""
|
||||
saml_idp_metadata_url: ""
|
||||
saml_idp_sso_service_url: ""
|
||||
<<: *maps
|
||||
<<: *apis
|
||||
|
||||
@@ -192,7 +198,7 @@ production:
|
||||
# secret_key: my_secret_value
|
||||
#
|
||||
# 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_secret: ""
|
||||
facebook_key: ""
|
||||
@@ -202,5 +208,8 @@ production:
|
||||
wordpress_oauth2_key: ""
|
||||
wordpress_oauth2_secret: ""
|
||||
wordpress_oauth2_site: ""
|
||||
saml_sp_entity_id: ""
|
||||
saml_idp_metadata_url: ""
|
||||
saml_idp_sso_service_url: ""
|
||||
<<: *maps
|
||||
<<: *apis
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
* [Tests](customization/tests.md)
|
||||
|
||||
* [Technical Features](features/features.md)
|
||||
* [OAuth](features/oauth.md)
|
||||
* [Authentication with external services (OAuth)](features/oauth.md)
|
||||
* [GraphQL](features/graphql.md)
|
||||
* [Debates and proposals recommendations](features/recommendations.md)
|
||||
* [Configure Census Connection](features/census_configuration.md)
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
# OAuth
|
||||
# Authentication with external services (OAuth)
|
||||
|
||||
You can configure authentication services with external OAuth providers, right now Twitter, Facebook, Google and Wordpress are supported.
|
||||
You can configure authentication services with external OAuth providers. Right now, Twitter, Facebook, Google, Wordpress and SAML are supported.
|
||||
|
||||
## 1. Create an App on the platform
|
||||
|
||||
For each platform, go to their developers section and follow their guides to create an app.
|
||||
For Twitter, Facebook, Google and Wordpress, go to their developers section and follow their guides to create an app. For SAML, you'll have to configure an Identity Provider (IdP).
|
||||
|
||||
## 2. Set the authentication URL of your Consul Democracy installation
|
||||
|
||||
They'll ask you for the authentication URL of your Consul Democracy installation, and as you can see running `rails routes |grep omniauth` at your Consul Democracy repo locally:
|
||||
They'll ask you for the authentication URL of your Consul Democracy installation, and as you can see running `rails routes | grep omniauth` at your Consul Democracy repo locally:
|
||||
|
||||
```bash
|
||||
user_twitter_omniauth_authorize GET|POST /users/auth/twitter(.:format) users/omniauth_callbacks#passthru
|
||||
user_twitter_omniauth_callback GET|POST /users/auth/twitter/callback(.:format) users/omniauth_callbacks#twitter
|
||||
user_facebook_omniauth_authorize GET|POST /users/auth/facebook(.:format) users/omniauth_callbacks#passthru
|
||||
user_facebook_omniauth_callback GET|POST /users/auth/facebook/callback(.:format) users/omniauth_callbacks#facebook
|
||||
user_google_oauth2_omniauth_authorize GET|POST /users/auth/google_oauth2(.:format) users omniauth_callbacks#passthru
|
||||
user_google_oauth2_omniauth_callback GET|POST /users/auth/google_oauth2/callback(.:format) users/omniauth_callbacks#google_oauth2
|
||||
user_wordpress_oauth2_omniauth_authorize GET|POST /users/auth/wordpress_oauth2(.:format) users/omniauth_callbacks#passthru
|
||||
user_wordpress_oauth2_omniauth_callback GET|POST /users/auth/wordpress_oauth2/callback(.:format) users/omniauth_callbacks#wordpress_oauth2
|
||||
user_twitter_omniauth_authorize GET|POST /users/auth/twitter(.:format) users/omniauth_callbacks#passthru
|
||||
user_twitter_omniauth_callback GET|POST /users/auth/twitter/callback(.:format) users/omniauth_callbacks#twitter
|
||||
user_facebook_omniauth_authorize GET|POST /users/auth/facebook(.:format) users/omniauth_callbacks#passthru
|
||||
user_facebook_omniauth_callback GET|POST /users/auth/facebook/callback(.:format) users/omniauth_callbacks#facebook
|
||||
user_google_oauth2_omniauth_authorize GET|POST /users/auth/google_oauth2(.:format) users/omniauth_callbacks#passthru
|
||||
user_google_oauth2_omniauth_callback GET|POST /users/auth/google_oauth2/callback(.:format) users/omniauth_callbacks#google_oauth2
|
||||
user_wordpress_oauth2_omniauth_authorize GET|POST /users/auth/wordpress_oauth2(.:format) users/omniauth_callbacks#passthru
|
||||
user_wordpress_oauth2_omniauth_callback GET|POST /users/auth/wordpress_oauth2/callback(.:format) users/omniauth_callbacks#wordpress_oauth2
|
||||
user_saml_omniauth_authorize GET|POST /users/auth/saml(.:format) users/omniauth_callbacks#passthru
|
||||
user_saml_omniauth_callback GET|POST /users/auth/saml/callback(.:format) users/omniauth_callbacks#saml
|
||||
```
|
||||
|
||||
So for example the URL for Facebook application would be `yourdomain.com/users/auth/facebook/callback`.
|
||||
@@ -37,4 +39,7 @@ When you complete the application registration you'll get a *key* and *secret* v
|
||||
wordpress_oauth2_key: ""
|
||||
wordpress_oauth2_secret: ""
|
||||
wordpress_oauth2_site: ""
|
||||
saml_sp_entity_id: "https://yoursp.org/entityid"
|
||||
saml_idp_metadata_url: "https://youridp.org/api/saml/metadata"
|
||||
saml_idp_sso_service_url: "https://youridp.org/api/saml/sso"
|
||||
```
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
* [Tests](customization/tests.md)
|
||||
|
||||
* [Funcionalidades Técnicas](features/features.md)
|
||||
* [OAuth](features/oauth.md)
|
||||
* [Autenticación con servicios externos (OAuth)](features/oauth.md)
|
||||
* [GraphQL](features/graphql.md)
|
||||
* [Recomendaciones de debates y propuestas](features/recommendations.md)
|
||||
* [Configurar conexión con el Censo](features/census_configuration.md)
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
# OAuth
|
||||
# Autenticación con servicios externos (OAuth)
|
||||
|
||||
Puedes configurar la autenticación con servicios externos usando OAuth, actualmente se pueden utilizar Twitter, Facebook, Google y Wordpress.
|
||||
Puedes configurar la autenticación con servicios externos usando OAuth. Actualmente, se pueden utilizar Twitter, Facebook, Google, Wordpress y SAML.
|
||||
|
||||
## 1. Crea una aplicación en la plataforma
|
||||
|
||||
Para cada plataforma, sigue las instrucciones en la sección de desarrolladores de su página web.
|
||||
Para Twitter, Facebook, Google y Wordpress, sigue las instrucciones en la sección de desarrolladores de su página web. Para SAML, tendrás que configurar tu propio proveedor de identidad (IdP).
|
||||
|
||||
## 2. Establece la URL de autenticación de tu instalación de Consul Democracy
|
||||
|
||||
Te preguntarán por la URL de autenticación de tu instalación de Consul Democracy, y como podrás comprobar corriendo la tarea `rails routes |grep omniauth` en tu repositorio local:
|
||||
Te preguntarán por la URL de autenticación de tu instalación de Consul Democracy, y como podrás comprobar corriendo la tarea `rails routes | grep omniauth` en tu repositorio local:
|
||||
|
||||
```bash
|
||||
user_twitter_omniauth_authorize GET|POST /users/auth/twitter(.:format) users/omniauth_callbacks#passthru
|
||||
user_twitter_omniauth_callback GET|POST /users/auth/twitter/callback(.:format) users/omniauth_callbacks#twitter
|
||||
user_facebook_omniauth_authorize GET|POST /users/auth/facebook(.:format) users/omniauth_callbacks#passthru
|
||||
user_facebook_omniauth_callback GET|POST /users/auth/facebook/callback(.:format) users/omniauth_callbacks#facebook
|
||||
user_google_oauth2_omniauth_authorize GET|POST /users/auth/google_oauth2(.:format) users omniauth_callbacks#passthru
|
||||
user_google_oauth2_omniauth_callback GET|POST /users/auth/google_oauth2/callback(.:format) users/omniauth_callbacks#google_oauth2
|
||||
user_wordpress_oauth2_omniauth_authorize GET|POST /users/auth/wordpress_oauth2(.:format) users/omniauth_callbacks#passthru
|
||||
user_wordpress_oauth2_omniauth_callback GET|POST /users/auth/wordpress_oauth2/callback(.:format) users/omniauth_callbacks#wordpress_oauth2
|
||||
user_twitter_omniauth_authorize GET|POST /users/auth/twitter(.:format) users/omniauth_callbacks#passthru
|
||||
user_twitter_omniauth_callback GET|POST /users/auth/twitter/callback(.:format) users/omniauth_callbacks#twitter
|
||||
user_facebook_omniauth_authorize GET|POST /users/auth/facebook(.:format) users/omniauth_callbacks#passthru
|
||||
user_facebook_omniauth_callback GET|POST /users/auth/facebook/callback(.:format) users/omniauth_callbacks#facebook
|
||||
user_google_oauth2_omniauth_authorize GET|POST /users/auth/google_oauth2(.:format) users/omniauth_callbacks#passthru
|
||||
user_google_oauth2_omniauth_callback GET|POST /users/auth/google_oauth2/callback(.:format) users/omniauth_callbacks#google_oauth2
|
||||
user_wordpress_oauth2_omniauth_authorize GET|POST /users/auth/wordpress_oauth2(.:format) users/omniauth_callbacks#passthru
|
||||
user_wordpress_oauth2_omniauth_callback GET|POST /users/auth/wordpress_oauth2/callback(.:format) users/omniauth_callbacks#wordpress_oauth2
|
||||
user_saml_omniauth_authorize GET|POST /users/auth/saml(.:format) users/omniauth_callbacks#passthru
|
||||
user_saml_omniauth_callback GET|POST /users/auth/saml/callback(.:format) users/omniauth_callbacks#saml
|
||||
```
|
||||
|
||||
Por ejemplo para Facebook la URL sería `yourdomain.com/users/auth/facebook/callback`.
|
||||
@@ -37,4 +39,7 @@ Cuando completes el registro de la aplicación en su plataforma te darán un *ke
|
||||
wordpress_oauth2_key: ""
|
||||
wordpress_oauth2_secret: ""
|
||||
wordpress_oauth2_site: ""
|
||||
saml_sp_entity_id: "https://tusp.org/entityid"
|
||||
saml_idp_metadata_url: "https://tuidp.org/api/saml/metadata"
|
||||
saml_idp_sso_service_url: "https://tuidp.org/api/saml/sso"
|
||||
```
|
||||
|
||||
@@ -9,6 +9,7 @@ describe Devise::OmniauthFormComponent do
|
||||
Setting["feature.twitter_login"] = false
|
||||
Setting["feature.google_login"] = false
|
||||
Setting["feature.wordpress_login"] = false
|
||||
Setting["feature.saml_login"] = false
|
||||
end
|
||||
|
||||
it "is not rendered when all authentications are disabled" do
|
||||
@@ -52,5 +53,14 @@ describe Devise::OmniauthFormComponent do
|
||||
expect(page).to have_button "Wordpress"
|
||||
expect(page).to have_button count: 1
|
||||
end
|
||||
|
||||
it "renders the SAML link when the feature is enabled" do
|
||||
Setting["feature.saml_login"] = true
|
||||
|
||||
render_inline component
|
||||
|
||||
expect(page).to have_button "SAML"
|
||||
expect(page).to have_button count: 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
@@ -488,6 +488,163 @@ describe "Users" do
|
||||
expect(page).to have_field "Email", with: "manuela@consul.dev"
|
||||
end
|
||||
end
|
||||
|
||||
context "Saml" do
|
||||
before { Setting["feature.saml_login"] = true }
|
||||
|
||||
let(:saml_hash_with_email) do
|
||||
{
|
||||
provider: "saml",
|
||||
uid: "ext-tester",
|
||||
info: {
|
||||
name: "samltester",
|
||||
email: "tester@consul.dev"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
let(:saml_hash_with_verified_email) do
|
||||
{
|
||||
provider: "saml",
|
||||
uid: "ext-tester",
|
||||
info: {
|
||||
name: "samltester",
|
||||
email: "tester@consul.dev",
|
||||
verified: "1"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
scenario "Sign up with a confirmed email" do
|
||||
OmniAuth.config.add_mock(:saml, saml_hash_with_verified_email)
|
||||
|
||||
visit new_user_registration_path
|
||||
click_button "Sign up with SAML"
|
||||
|
||||
expect(page).to have_content "Successfully identified as Saml"
|
||||
expect_to_be_signed_in
|
||||
|
||||
within("#notice") { click_button "Close" }
|
||||
click_link "My account"
|
||||
|
||||
expect(page).to have_field "Username", with: "samltester"
|
||||
|
||||
click_link "Change my login details"
|
||||
|
||||
expect(page).to have_field "Email", with: "tester@consul.dev"
|
||||
end
|
||||
|
||||
scenario "Sign up with an unconfirmed email" do
|
||||
OmniAuth.config.add_mock(:saml, saml_hash_with_email)
|
||||
|
||||
visit new_user_registration_path
|
||||
click_button "Sign up with SAML"
|
||||
|
||||
expect(page).to have_content "To continue, please click on the confirmation " \
|
||||
"link that we have sent you via email"
|
||||
|
||||
confirm_email
|
||||
expect(page).to have_content "Your account has been confirmed"
|
||||
expect(page).to have_current_path new_user_session_path
|
||||
|
||||
click_button "Sign in with SAML"
|
||||
|
||||
expect(page).to have_content "Successfully identified as Saml"
|
||||
expect_to_be_signed_in
|
||||
|
||||
within("#notice") { click_button "Close" }
|
||||
click_link "My account"
|
||||
|
||||
expect(page).to have_field "Username", with: "samltester"
|
||||
|
||||
click_link "Change my login details"
|
||||
|
||||
expect(page).to have_field "Email", with: "tester@consul.dev"
|
||||
end
|
||||
|
||||
scenario "Sign in with a user with a SAML identity" do
|
||||
user = create(:user, username: "samltester", email: "tester@consul.dev", password: "My123456")
|
||||
create(:identity, uid: "ext-tester", provider: "saml", user: user)
|
||||
OmniAuth.config.add_mock(:saml, { provider: "saml", uid: "ext-tester" })
|
||||
|
||||
visit new_user_session_path
|
||||
click_button "Sign in with SAML"
|
||||
|
||||
expect(page).to have_content "Successfully identified as Saml"
|
||||
expect_to_be_signed_in
|
||||
|
||||
within("#notice") { click_button "Close" }
|
||||
click_link "My account"
|
||||
|
||||
expect(page).to have_field "Username", with: "samltester"
|
||||
|
||||
click_link "Change my login details"
|
||||
|
||||
expect(page).to have_field "Email", with: "tester@consul.dev"
|
||||
end
|
||||
|
||||
scenario "Sign in with a user without a SAML identity keeps the username" do
|
||||
create(:user, username: "tester", email: "tester@consul.dev", password: "My123456")
|
||||
OmniAuth.config.add_mock(:saml, saml_hash_with_verified_email)
|
||||
|
||||
visit new_user_session_path
|
||||
click_button "Sign in with SAML"
|
||||
|
||||
expect(page).to have_content "Successfully identified as Saml"
|
||||
expect_to_be_signed_in
|
||||
|
||||
within("#notice") { click_button "Close" }
|
||||
click_link "My account"
|
||||
|
||||
expect(page).to have_field "Username", with: "tester"
|
||||
|
||||
click_link "Change my login details"
|
||||
|
||||
expect(page).to have_field "Email", with: "tester@consul.dev"
|
||||
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
|
||||
|
||||
scenario "Sign out" do
|
||||
|
||||
Reference in New Issue
Block a user