Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ source 'https://rubygems.org'
# Specify your gem's dependencies in devise_saml_authenticatable.gemspec
gemspec

gem 'ruby-saml', git: 'https://github.com/SAML-Toolkits/ruby-saml.git', branch: 'v2.x'

group :test do
gem 'rake'
gem 'rspec', '~> 3.0'
gem 'rails', '~> 8.0.0'
gem 'rails', '~> 8.0'
gem 'rspec-rails'
gem 'sqlite3', '~> 2.6.0'
gem 'capybara'
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ In `config/initializers/devise.rb`:
# header that matches the ID of the SAML request. (Default is false)
# config.saml_validate_in_response_to = false

# Configure with your SAML settings (see ruby-saml's README for more information: https://github.com/onelogin/ruby-saml).
# Configure with your SAML settings (see ruby-saml's README for more information: https://github.com/SAML-Toolkits/ruby-saml).
config.saml_configure do |settings|
settings.assertion_consumer_service_url = "http://localhost:3000/users/saml/auth"
settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
Expand All @@ -159,7 +159,7 @@ In `config/initializers/devise.rb`:
settings.idp_slo_service_url = "http://localhost/simplesaml/www/saml2/idp/SingleLogoutService.php"
settings.idp_sso_service_url = "http://localhost/simplesaml/www/saml2/idp/SSOService.php"
settings.idp_cert_fingerprint = "00:A1:2B:3C:44:55:6F:A7:88:CC:DD:EE:22:33:44:55:D6:77:8F:99"
settings.idp_cert_fingerprint_algorithm = "http://www.w3.org/2000/09/xmldsig#sha1"
settings.idp_cert_fingerprint_algorithm = RubySaml::XML::SHA256
end
end
```
Expand Down
10 changes: 5 additions & 5 deletions app/controllers/devise/saml_sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class Devise::SamlSessionsController < Devise::SessionsController

def new
idp_entity_id = get_idp_entity_id(params)
auth_request = OneLogin::RubySaml::Authrequest.new
auth_request = ::RubySaml::Authrequest.new
auth_params = { RelayState: relay_state } if relay_state
action = auth_request.create(saml_config(idp_entity_id, request), auth_params || {})
session[:saml_transaction_id] = auth_request.request_id if auth_request.respond_to?(:request_id)
Expand All @@ -17,7 +17,7 @@ def new

def metadata
idp_entity_id = params[:idp_entity_id]
meta = OneLogin::RubySaml::Metadata.new
meta = ::RubySaml::Metadata.new
render xml: meta.generate(saml_config(idp_entity_id, request))
end

Expand All @@ -27,7 +27,7 @@ def idp_sign_out
session[Devise.saml_session_index_key] = nil

saml_config = saml_config(get_idp_entity_id(params), request)
logout_request = OneLogin::RubySaml::SloLogoutrequest.new(params[:SAMLRequest], settings: saml_config)
logout_request = ::RubySaml::SloLogoutrequest.new(params[:SAMLRequest], settings: saml_config)
redirect_to generate_idp_logout_response(saml_config, logout_request.id), allow_other_host: true
elsif params[:SAMLResponse]
# Currently Devise handles the session invalidation when the request is made.
Expand Down Expand Up @@ -64,7 +64,7 @@ def store_info_for_sp_initiated_logout
# Override devise to send user to IdP logout for SLO
def after_sign_out_path_for(_)
idp_entity_id = get_idp_entity_id(params)
logout_request = OneLogin::RubySaml::Logoutrequest.new
logout_request = ::RubySaml::Logoutrequest.new
saml_settings = saml_config(idp_entity_id, request).dup

# Add attributes to saml_settings which will later be used to create the SP
Expand Down Expand Up @@ -93,6 +93,6 @@ def generate_idp_logout_response(saml_config, logout_request_id)
params = {}
params[:RelayState] = relay_state if relay_state

OneLogin::RubySaml::SloLogoutresponse.new.create(saml_config, logout_request_id, nil, params)
::RubySaml::SloLogoutresponse.new.create(saml_config, logout_request_id, nil, params)
end
end
3 changes: 2 additions & 1 deletion devise_saml_authenticatable.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ Gem::Specification.new do |gem|
gem.required_ruby_version = ">= 2.6.0"

gem.add_dependency("devise","> 2.0.0")
gem.add_dependency("ruby-saml","~> 1.18")

gem.add_dependency("ruby-saml")
end
4 changes: 3 additions & 1 deletion lib/devise_saml_authenticatable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
require "devise_saml_authenticatable/default_attribute_map_resolver"
require "devise_saml_authenticatable/default_idp_entity_id_reader"

require "ruby-saml"

begin
Rails::Engine
rescue
Expand Down Expand Up @@ -103,7 +105,7 @@ module Devise
@@allowed_clock_drift_in_seconds

mattr_accessor :saml_config
@@saml_config = OneLogin::RubySaml::Settings.new
@@saml_config = ::RubySaml::Settings.new
def self.saml_configure
yield saml_config
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ module DeviseSamlAuthenticatable
class DefaultIdpEntityIdReader
def self.entity_id(params)
if params[:SAMLRequest]
OneLogin::RubySaml::SloLogoutrequest.new(
::RubySaml::SloLogoutrequest.new(
params[:SAMLRequest],
settings: Devise.saml_config,
allowed_clock_drift: Devise.allowed_clock_drift_in_seconds,
).issuer
elsif params[:SAMLResponse]
OneLogin::RubySaml::Response.new(
::RubySaml::Response.new(
params[:SAMLResponse],
settings: Devise.saml_config,
allowed_clock_drift: Devise.allowed_clock_drift_in_seconds,
Expand Down
2 changes: 1 addition & 1 deletion lib/devise_saml_authenticatable/saml_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def file_based_config
idp_config_path = "#{Rails.root}/config/idp.yml"

if File.exist?(idp_config_path)
@file_based_config ||= OneLogin::RubySaml::Settings.new(YAML.load(File.read(idp_config_path))[Rails.env])
@file_based_config ||= ::RubySaml::Settings.new(YAML.load(File.read(idp_config_path))[Rails.env])
end
end

Expand Down
4 changes: 2 additions & 2 deletions lib/devise_saml_authenticatable/strategy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class SamlAuthenticatable < Authenticatable
include DeviseSamlAuthenticatable::SamlConfig
def valid?
if params[:SAMLResponse]
OneLogin::RubySaml::Response.new(
::RubySaml::Response.new(
params[:SAMLResponse],
response_options,
)
Expand Down Expand Up @@ -35,7 +35,7 @@ def store?

private
def parse_saml_response
@response = OneLogin::RubySaml::Response.new(
@response = ::RubySaml::Response.new(
params[:SAMLResponse],
response_options,
)
Expand Down
20 changes: 10 additions & 10 deletions spec/controllers/devise/saml_sessions_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def destroy

it 'stores saml_transaction_id in the session' do
do_get
if OneLogin::RubySaml::Authrequest.public_instance_methods.include?(:request_id)
if ::RubySaml::Authrequest.public_instance_methods.include?(:request_id)
expect(session[:saml_transaction_id]).to be_present
end
end
Expand All @@ -95,7 +95,7 @@ def destroy

it 'stores saml_transaction_id in the session' do
do_get
if OneLogin::RubySaml::Authrequest.public_instance_methods.include?(:request_id)
if ::RubySaml::Authrequest.public_instance_methods.include?(:request_id)
expect(session[:saml_transaction_id]).to be_present
end
end
Expand Down Expand Up @@ -153,8 +153,8 @@ def self.entity_id(params)
get :metadata

# Remove ID that can vary across requests
expected_metadata = OneLogin::RubySaml::Metadata.new.generate(saml_config)
metadata_pattern = Regexp.escape(expected_metadata).gsub(/ ID='[^']+'/, " ID='[\\w-]+'")
expected_metadata = ::RubySaml::Metadata.new.generate(saml_config)
metadata_pattern = Regexp.escape(expected_metadata).gsub(/ ID="[^"]+"/, ' ID="[\w-]+"')
expect(response.body).to match(Regexp.new(metadata_pattern))
end
end
Expand All @@ -176,8 +176,8 @@ def self.entity_id(params)
get :metadata

# Remove ID that can vary across requests
expected_metadata = OneLogin::RubySaml::Metadata.new.generate(saml_config)
metadata_pattern = Regexp.escape(expected_metadata).gsub(/ ID='[^']+'/, " ID='[\\w-]+'")
expected_metadata = ::RubySaml::Metadata.new.generate(saml_config)
metadata_pattern = Regexp.escape(expected_metadata).gsub(/ ID="[^"]+"/, ' ID="[\w-]+"')
expect(response.body).to match(Regexp.new(metadata_pattern))
end
end
Expand All @@ -197,7 +197,7 @@ def all_signed_out?

shared_examples 'not create SP initiated logout request' do
it do
expect(OneLogin::RubySaml::Logoutrequest).not_to receive(:new)
expect(::RubySaml::Logoutrequest).not_to receive(:new)
subject
end
end
Expand Down Expand Up @@ -261,7 +261,7 @@ def all_signed_out?
session[Devise.saml_session_index_key] = 'sessionindex'

actual_settings = nil
expect_any_instance_of(OneLogin::RubySaml::Logoutrequest).to receive(:create) do |_, settings|
expect_any_instance_of(::RubySaml::Logoutrequest).to receive(:create) do |_, settings|
actual_settings = settings
'http://localhost:8009/saml/logout'
end
Expand Down Expand Up @@ -319,7 +319,7 @@ def self.entity_id(params)
let(:saml_response) { double(:slo_logoutresponse) }
let(:response_url) { 'http://localhost/logout_response' }
before do
allow(OneLogin::RubySaml::SloLogoutresponse).to receive(:new).and_return(saml_response)
allow(::RubySaml::SloLogoutresponse).to receive(:new).and_return(saml_response)
allow(saml_response).to receive(:create).and_return(response_url)
end

Expand Down Expand Up @@ -369,7 +369,7 @@ def self.entity_id(params)
end
let(:name_id) { '12312312' }
before do
allow(OneLogin::RubySaml::SloLogoutrequest).to receive(:new).and_return(saml_request)
allow(::RubySaml::SloLogoutrequest).to receive(:new).and_return(saml_request)
session[Devise.saml_session_index_key] = 'sessionindex'
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
let(:logger) { instance_double("Logger", info: nil) }
let(:rails_root) { Pathname.new("tmp") }

let(:saml_response) { instance_double("OneLogin::RubySaml::Response") }
let(:saml_response) { instance_double("::RubySaml::Response") }
let(:file_contents) {
<<YAML
---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
let(:params) { {SAMLRequest: "logout request"} }
let(:slo_logout_request) { double('slo_logout_request', issuer: 'meow')}
before do
allow(OneLogin::RubySaml::SloLogoutrequest).to receive(:new).and_return(slo_logout_request)
allow(::RubySaml::SloLogoutrequest).to receive(:new).and_return(slo_logout_request)
end

it "uses an OneLogin::RubySaml::SloLogoutrequest to get the idp_entity_id" do
expect(OneLogin::RubySaml::SloLogoutrequest).to receive(:new).with("logout request", hash_including)
it "uses an RubySaml::SloLogoutrequest to get the idp_entity_id" do
expect(::RubySaml::SloLogoutrequest).to receive(:new).with("logout request", hash_including)
expect(described_class.entity_id(params)).to eq("meow")
end

Expand All @@ -20,7 +20,7 @@
end

it "allows the configured clock drift" do
expect(OneLogin::RubySaml::SloLogoutrequest).to receive(:new).with("logout request", hash_including(allowed_clock_drift: 30))
expect(::RubySaml::SloLogoutrequest).to receive(:new).with("logout request", hash_including(allowed_clock_drift: 30))
expect(described_class.entity_id(params)).to eq("meow")
end
end
Expand All @@ -30,11 +30,11 @@
let(:params) { {SAMLResponse: "auth response"} }
let(:response) { double('response', issuers: ['meow'] )}
before do
allow(OneLogin::RubySaml::Response).to receive(:new).and_return(response)
allow(::RubySaml::Response).to receive(:new).and_return(response)
end

it "uses an OneLogin::RubySaml::Response to get the idp_entity_id" do
expect(OneLogin::RubySaml::Response).to receive(:new).with("auth response", hash_including)
it "uses an RubySaml::Response to get the idp_entity_id" do
expect(::RubySaml::Response).to receive(:new).with("auth response", hash_including)
expect(described_class.entity_id(params)).to eq("meow")
end

Expand All @@ -44,7 +44,7 @@
end

it "allows the configured clock drift" do
expect(OneLogin::RubySaml::Response).to receive(:new).with("auth response", hash_including(allowed_clock_drift: 30))
expect(::RubySaml::Response).to receive(:new).with("auth response", hash_including(allowed_clock_drift: 30))
expect(described_class.entity_id(params)).to eq("meow")
end
end
Expand Down
6 changes: 3 additions & 3 deletions spec/devise_saml_authenticatable/model_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def attribute_map
let(:attributemap) { attribute_map_resolver.new(nil).attribute_map }
let(:response) { double(:response, attributes: attributes, name_id: name_id) }
let(:attributes) {
OneLogin::RubySaml::Attributes.new(
::RubySaml::Attributes.new(
'saml-email-format' => ['user@example.com'],
'saml-name-format' => ['A User'],
)
Expand All @@ -74,7 +74,7 @@ def attribute_map
end

context "when configured to use the subject" do
let(:attributes) { OneLogin::RubySaml::Attributes.new('saml-name-format' => ['A User']) }
let(:attributes) { ::RubySaml::Attributes.new('saml-name-format' => ['A User']) }
let(:name_id) { 'user@example.com' }

before do
Expand Down Expand Up @@ -298,7 +298,7 @@ def attribute_map
end

context "when using default user key" do
let(:attributes) { OneLogin::RubySaml::Attributes.new('saml-email-format' => ['UPPER@example.com']) }
let(:attributes) { ::RubySaml::Attributes.new('saml-email-format' => ['UPPER@example.com']) }

include_examples "correct downcasing"
end
Expand Down
29 changes: 16 additions & 13 deletions spec/devise_saml_authenticatable/saml_config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@

it "is the global devise SAML config" do
Devise.saml_configure do |settings|
settings.assertion_consumer_logout_service_binding = 'test'
settings.single_logout_service_binding = 'test'
end
expect(saml_config).to be(Devise.saml_config)
expect(saml_config.assertion_consumer_logout_service_binding).to eq('test')
expect(saml_config.single_logout_service_binding).to eq('test')
end

context "when the idp_providers_adapter key exists" do
Expand Down Expand Up @@ -93,7 +93,7 @@ def self.settings(idp_entity_id)
}, else_do: proc {
expect(saml_config.idp_sso_target_url).to eq('idp_sso_url')
})
expect(saml_config.class).to eq OneLogin::RubySaml::Settings
expect(saml_config.class).to eq ::RubySaml::Settings
end
end

Expand All @@ -106,7 +106,7 @@ def self.settings(idp_entity_id)
}, else_do: proc {
expect(saml_config.idp_sso_target_url).to eq('idp_sso_url_other')
})
expect(saml_config.class).to eq OneLogin::RubySaml::Settings
expect(saml_config.class).to eq ::RubySaml::Settings
end
end
end
Expand All @@ -117,17 +117,18 @@ def self.settings(idp_entity_id)
yaml = <<-IDP
---
environment:
assertion_consumer_logout_service_binding: assertion_consumer_logout_service_binding
assertion_consumer_logout_service_url: assertion_consumer_logout_service_url
single_logout_service_binding: single_logout_service_binding
single_logout_service_url: single_logout_service_url
assertion_consumer_service_binding: assertion_consumer_service_binding
assertion_consumer_service_url: assertion_consumer_service_url
attributes_index: attributes_index
authn_context: authn_context
authn_context_comparison: authn_context_comparison
authn_context_decl_ref: authn_context_decl_ref
certificate: certificate
compress_request: compress_request
compress_response: compress_response
# compress_request and compress_response are deprecated and no longer functional in ruby-saml 2.x
# compress_request: compress_request
# compress_response: compress_response
double_quote_xml_attribute_values: double_quote_xml_attribute_values
force_authn: force_authn
idp_cert: idp_cert
Expand Down Expand Up @@ -161,18 +162,20 @@ def self.settings(idp_entity_id)
end

it "uses that file's contents" do
expect(saml_config.assertion_consumer_logout_service_binding).to eq('assertion_consumer_logout_service_binding')
expect(saml_config.assertion_consumer_logout_service_url).to eq('assertion_consumer_logout_service_url')
expect(saml_config.single_logout_service_binding).to eq('single_logout_service_binding')
expect(saml_config.single_logout_service_url).to eq('single_logout_service_url')
expect(saml_config.assertion_consumer_service_binding).to eq('assertion_consumer_service_binding')
expect(saml_config.assertion_consumer_service_url).to eq('assertion_consumer_service_url')
expect(saml_config.attributes_index).to eq('attributes_index')
expect(saml_config.authn_context).to eq('authn_context')
expect(saml_config.authn_context_comparison).to eq('authn_context_comparison')
expect(saml_config.authn_context_decl_ref).to eq('authn_context_decl_ref')
expect(saml_config.certificate).to eq('certificate')
expect(saml_config.compress_request).to eq('compress_request')
expect(saml_config.compress_response).to eq('compress_response')
expect(saml_config.double_quote_xml_attribute_values).to eq('double_quote_xml_attribute_values')
# compress_request and compress_response are deprecated and no longer functional in ruby-saml 2.x
# expect(saml_config.compress_request).to eq('compress_request')
# expect(saml_config.compress_response).to eq('compress_response')
# double_quote_xml_attribute_values is deprecated in ruby-saml 2.x and always returns true
expect(saml_config.double_quote_xml_attribute_values).to eq(true)
expect(saml_config.force_authn).to eq('force_authn')
expect(saml_config.idp_cert).to eq('idp_cert')
expect(saml_config.idp_cert_fingerprint).to eq('idp_cert_fingerprint')
Expand Down
Loading
Loading