From 6ccc445484a7faa7220262f297ee94c1295aa015 Mon Sep 17 00:00:00 2001 From: hamaron Date: Thu, 11 Jan 2018 16:03:06 -0800 Subject: [PATCH 1/2] Added options to allow IPD to dynamically change values of Name ID and attributes when creating a SAML response. --- lib/saml_idp/assertion_builder.rb | 31 ++++++++- lib/saml_idp/controller.rb | 6 +- lib/saml_idp/saml_response.rb | 37 +++++++---- spec/lib/saml_idp/assertion_builder_spec.rb | 73 +++++++++++++++++++++ 4 files changed, 129 insertions(+), 18 deletions(-) diff --git a/lib/saml_idp/assertion_builder.rb b/lib/saml_idp/assertion_builder.rb index 39e43e08..8e0170fa 100644 --- a/lib/saml_idp/assertion_builder.rb +++ b/lib/saml_idp/assertion_builder.rb @@ -16,10 +16,26 @@ class AssertionBuilder attr_accessor :expiry attr_accessor :encryption_opts attr_accessor :session_expiry + attr_accessor :name_id_formats_opts + attr_accessor :asserted_attributes_opts delegate :config, to: :SamlIdp - def initialize(reference_id, issuer_uri, principal, audience_uri, saml_request_id, saml_acs_url, raw_algorithm, authn_context_classref, expiry=60*60, encryption_opts=nil, session_expiry=nil) + def initialize( + reference_id, + issuer_uri, + principal, + audience_uri, + saml_request_id, + saml_acs_url, + raw_algorithm, + authn_context_classref, + expiry=60*60, + encryption_opts=nil, + session_expiry=nil, + name_id_formats_opts = nil, + asserted_attributes_opts = nil + ) self.reference_id = reference_id self.issuer_uri = issuer_uri self.principal = principal @@ -31,6 +47,8 @@ def initialize(reference_id, issuer_uri, principal, audience_uri, saml_request_i self.expiry = expiry self.encryption_opts = encryption_opts self.session_expiry = session_expiry.nil? ? config.session_expiry : session_expiry + self.name_id_formats_opts = name_id_formats_opts + self.asserted_attributes_opts = asserted_attributes_opts end def fresh @@ -98,7 +116,9 @@ def encrypt(opts = {}) end def asserted_attributes - if principal.respond_to?(:asserted_attributes) + if asserted_attributes_opts.present? && !asserted_attributes_opts.empty? + asserted_attributes_opts + elsif principal.respond_to?(:asserted_attributes) principal.send(:asserted_attributes) elsif !config.attributes.nil? && !config.attributes.empty? config.attributes @@ -139,10 +159,15 @@ def name_id_getter private :name_id_getter def name_id_format - @name_id_format ||= NameIdFormatter.new(config.name_id.formats).chosen + @name_id_format ||= NameIdFormatter.new(name_id_formats).chosen end private :name_id_format + def name_id_formats + @name_id_formats ||= (name_id_formats_opts || config.name_id.formats) + end + private :name_id_formats + def reference_string "_#{reference_id}" end diff --git a/lib/saml_idp/controller.rb b/lib/saml_idp/controller.rb index e0ecb1e8..d62ae512 100644 --- a/lib/saml_idp/controller.rb +++ b/lib/saml_idp/controller.rb @@ -62,6 +62,8 @@ def encode_authn_response(principal, opts = {}) expiry = opts[:expiry] || 60*60 session_expiry = opts[:session_expiry] encryption_opts = opts[:encryption] || nil + name_id_formats_opts = opts[:name_id_formats] || nil + asserted_attributes_opts = opts[:attributes] || nil SamlResponse.new( reference_id, @@ -75,7 +77,9 @@ def encode_authn_response(principal, opts = {}) my_authn_context_classref, expiry, encryption_opts, - session_expiry + session_expiry, + name_id_formats_opts, + asserted_attributes_opts ).build end diff --git a/lib/saml_idp/saml_response.rb b/lib/saml_idp/saml_response.rb index ceac0ac1..8350371f 100644 --- a/lib/saml_idp/saml_response.rb +++ b/lib/saml_idp/saml_response.rb @@ -17,20 +17,25 @@ class SamlResponse attr_accessor :expiry attr_accessor :encryption_opts attr_accessor :session_expiry + attr_accessor :name_id_formats_opts + attr_accessor :asserted_attributes_opts - def initialize(reference_id, - response_id, - issuer_uri, - principal, - audience_uri, - saml_request_id, - saml_acs_url, - algorithm, - authn_context_classref, - expiry=60*60, - encryption_opts=nil, - session_expiry=0 - ) + def initialize( + reference_id, + response_id, + issuer_uri, + principal, + audience_uri, + saml_request_id, + saml_acs_url, + algorithm, + authn_context_classref, + expiry=60*60, + encryption_opts=nil, + session_expiry=0, + name_id_formats_opts = nil, + asserted_attributes_opts = nil + ) self.reference_id = reference_id self.response_id = response_id self.issuer_uri = issuer_uri @@ -45,6 +50,8 @@ def initialize(reference_id, self.expiry = expiry self.encryption_opts = encryption_opts self.session_expiry = session_expiry + self.name_id_formats_opts = name_id_formats_opts + self.asserted_attributes_opts = asserted_attributes_opts end def build @@ -76,7 +83,9 @@ def assertion_builder authn_context_classref, expiry, encryption_opts, - session_expiry + session_expiry, + name_id_formats_opts, + asserted_attributes_opts end private :assertion_builder end diff --git a/spec/lib/saml_idp/assertion_builder_spec.rb b/spec/lib/saml_idp/assertion_builder_spec.rb index 298b959a..8f679083 100644 --- a/spec/lib/saml_idp/assertion_builder_spec.rb +++ b/spec/lib/saml_idp/assertion_builder_spec.rb @@ -19,6 +19,9 @@ module SamlIdp key_transport: 'rsa-oaep-mgf1p', } end + let(:session_expiry) { nil } + let(:name_id_formats_opt) { nil } + let(:asserted_attributes_opt) { nil } subject { described_class.new( reference_id, issuer_uri, @@ -103,6 +106,76 @@ module SamlIdp expect(encrypted_xml).to_not match(audience_uri) end + describe "with name_id_formats_opt" do + let(:name_id_formats_opt) { + { + persistent: -> (principal) { + principal.unique_identifier + } + } + } + it "delegates name_id_formats to opts" do + UserWithUniqueId = Struct.new(:unique_identifier, :email, :asserted_attributes) + principal = UserWithUniqueId.new('unique_identifier_123456', 'foo@example.com', { emailAddress: { getter: :email } }) + builder = described_class.new( + reference_id, + issuer_uri, + principal, + audience_uri, + saml_request_id, + saml_acs_url, + algorithm, + authn_context_classref, + expiry, + encryption_opts, + session_expiry, + name_id_formats_opt, + asserted_attributes_opt + ) + Timecop.travel(Time.zone.local(2010, 6, 1, 13, 0, 0)) do + expect(builder.raw).to eq("http://sportngin.comunique_identifier_123456http://example.comurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordfoo@example.com") + end + end + end + + describe "with asserted_attributes_opt" do + let(:asserted_attributes_opt) { + { + 'GivenName' => { + getter: :first_name + }, + 'SurName' => { + getter: -> (principal) { + principal.last_name + } + } + } + } + + it "delegates asserted_attributes to opts" do + UserWithName = Struct.new(:email, :first_name, :last_name) + principal = UserWithName.new('foo@example.com', 'George', 'Washington') + builder = described_class.new( + reference_id, + issuer_uri, + principal, + audience_uri, + saml_request_id, + saml_acs_url, + algorithm, + authn_context_classref, + expiry, + encryption_opts, + session_expiry, + name_id_formats_opt, + asserted_attributes_opt + ) + Timecop.travel(Time.zone.local(2010, 6, 1, 13, 0, 0)) do + expect(builder.raw).to eq("http://sportngin.comfoo@example.comhttp://example.comurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordGeorgeWashington") + end + end + end + describe "with custom session_expiry configuration" do let(:config) { SamlIdp::Configurator.new } before do From bb69e8c23f88d3bcd30b474cf789971c0a1288d1 Mon Sep 17 00:00:00 2001 From: tsogbadrakh Date: Sun, 6 Jun 2021 16:05:40 +0900 Subject: [PATCH 2/2] Add default value for signed message option --- .gitignore | 1 + lib/saml_idp/saml_response.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 99af2fe2..845e2c16 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ /Gemfile.lock /gemfiles/*.gemfile.lock /.byebug_history +/vendor \ No newline at end of file diff --git a/lib/saml_idp/saml_response.rb b/lib/saml_idp/saml_response.rb index 247638c5..6f389071 100644 --- a/lib/saml_idp/saml_response.rb +++ b/lib/saml_idp/saml_response.rb @@ -34,7 +34,7 @@ def initialize( expiry=60*60, encryption_opts=nil, session_expiry=0, - signed_message_opts, + signed_message_opts=false, name_id_formats_opts = nil, asserted_attributes_opts = nil )