saml_idp 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4692b2d2c5266c2db128e4942daddd534c7e22efe32f1f45b02776db2eb8b607
4
- data.tar.gz: cc0b169ea2d024b91590e270c6a6cbe742a661280651917147eb0401f821c6a8
3
+ metadata.gz: 1a69c8d8c945c47e87cb526d4dfcf5f30c2f687d03b33e99013eef63acbe4377
4
+ data.tar.gz: 4933af3eb5cb686111307cd86d074ab45cc879a253f7a0833aaaa3262add74db
5
5
  SHA512:
6
- metadata.gz: 0d1eaa0e214b1c2cb17970987fc0956c991e33831a05f7bf40936180f6a4fc2a22d5539051b456f7a920a49b7cbd98e83296ae55858025351e9fb693a6f6d595
7
- data.tar.gz: 587c1ca1bc298dc8381bd50e49bd3b361fd011b4ae7509107f2336ec2b9cbc30bd625915a76d5833ebbe7c36eb9181acbee2f1f8b60dae0cb71f27a0436a9fec
6
+ metadata.gz: 61d5e37801af0e41b71cdf0bdbac6cac61e0b86969de75c27519b74ede55e3b9c2ec7c2dfc8bff9ef82ec1fa1a870c9483fe31e65e17d645fdf6cab5205ac2d1
7
+ data.tar.gz: a1594a0f1da0694a93c478259dcece4d38c30863564aa653b585bdae9ef6834699b784ff151b7569e7cccee22b41e0b5a37f0a5151a89b664feda92005f3f21f
data/README.md CHANGED
@@ -72,6 +72,22 @@ end
72
72
 
73
73
  ## Configuration
74
74
 
75
+ #### Signed assertions and Signed Response
76
+
77
+ By default SAML Assertion will be signed with an algorithm which defined to `config.algorithm`. Because SAML assertions contain secure information used for authentication such as NameID.
78
+
79
+ Signing SAML Response is optional, but some security perspective SP services might require Response message itself must be signed.
80
+ For that, you can enable it with `config.signed_message` option. [More about SAML spec](https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=68)
81
+
82
+ #### Signing algorithm
83
+
84
+ Following algorithms you can set in your response signing algorithm
85
+ :sha1 - RSA-SHA1 default value but not recommended to production environment
86
+ Highly recommended to use one of following algorithm, suit with your computing power.
87
+ :sha256 - RSA-SHA256
88
+ :sha384 - RSA-SHA384
89
+ :sha512 - RSA-SHA512
90
+
75
91
  Be sure to load a file like this during your app initialization:
76
92
 
77
93
  ```ruby
@@ -91,7 +107,7 @@ KEY DATA
91
107
  CERT
92
108
 
93
109
  # config.password = "secret_key_password"
94
- # config.algorithm = :sha256
110
+ # config.algorithm = :sha256 # Default: sha1 only for development.
95
111
  # config.organization_name = "Your Organization"
96
112
  # config.organization_url = "http://example.com"
97
113
  # config.base_saml_location = "#{base}/saml"
@@ -101,6 +117,7 @@ CERT
101
117
  # config.attribute_service_location = "#{base}/saml/attributes"
102
118
  # config.single_service_post_location = "#{base}/saml/auth"
103
119
  # config.session_expiry = 86400 # Default: 0 which means never
120
+ # config.signed_message = true # Default: false which means unsigned SAML Response
104
121
 
105
122
  # Principal (e.g. User) is passed in when you `encode_response`
106
123
  #
@@ -64,6 +64,7 @@ module SamlIdp
64
64
  expiry = opts[:expiry] || 60*60
65
65
  session_expiry = opts[:session_expiry]
66
66
  encryption_opts = opts[:encryption] || nil
67
+ signed_message_opts = opts[:signed_message] || false
67
68
 
68
69
  SamlResponse.new(
69
70
  reference_id,
@@ -77,7 +78,8 @@ module SamlIdp
77
78
  my_authn_context_classref,
78
79
  expiry,
79
80
  encryption_opts,
80
- session_expiry
81
+ session_expiry,
82
+ signed_message_opts
81
83
  ).build
82
84
  end
83
85
 
@@ -106,6 +106,7 @@ module SamlIdp
106
106
  end
107
107
 
108
108
  if !service_provider.acceptable_response_hosts.include?(response_host)
109
+ log "#{service_provider.acceptable_response_hosts} compare to #{response_host}"
109
110
  log "No acceptable AssertionConsumerServiceURL, either configure them via config.service_provider.response_hosts or match to your metadata_url host"
110
111
  return false
111
112
  end
@@ -1,32 +1,45 @@
1
1
  require 'builder'
2
+ require 'saml_idp/algorithmable'
3
+ require 'saml_idp/signable'
2
4
  module SamlIdp
3
5
  class ResponseBuilder
6
+ include Algorithmable
7
+ include Signable
4
8
  attr_accessor :response_id
5
9
  attr_accessor :issuer_uri
6
10
  attr_accessor :saml_acs_url
7
11
  attr_accessor :saml_request_id
8
12
  attr_accessor :assertion_and_signature
13
+ attr_accessor :raw_algorithm
9
14
 
10
- def initialize(response_id, issuer_uri, saml_acs_url, saml_request_id, assertion_and_signature)
15
+ alias_method :reference_id, :response_id
16
+
17
+ def initialize(response_id, issuer_uri, saml_acs_url, saml_request_id, assertion_and_signature, raw_algorithm)
11
18
  self.response_id = response_id
12
19
  self.issuer_uri = issuer_uri
13
20
  self.saml_acs_url = saml_acs_url
14
21
  self.saml_request_id = saml_request_id
15
22
  self.assertion_and_signature = assertion_and_signature
23
+ self.raw_algorithm = raw_algorithm
16
24
  end
17
25
 
18
- def encoded
19
- @encoded ||= encode
26
+ def encoded(signed_message: false)
27
+ @encoded ||= signed_message ? encode_signed_message : encode_raw_message
20
28
  end
21
29
 
22
30
  def raw
23
31
  build
24
32
  end
25
33
 
26
- def encode
34
+ def encode_raw_message
27
35
  Base64.strict_encode64(raw)
28
36
  end
29
- private :encode
37
+ private :encode_raw_message
38
+
39
+ def encode_signed_message
40
+ Base64.strict_encode64(signed)
41
+ end
42
+ private :encode_signed_message
30
43
 
31
44
  def build
32
45
  resp_options = {}
@@ -41,6 +54,7 @@ module SamlIdp
41
54
  builder = Builder::XmlMarkup.new
42
55
  builder.tag! "samlp:Response", resp_options do |response|
43
56
  response.Issuer issuer_uri, xmlns: Saml::XML::Namespaces::ASSERTION
57
+ sign response
44
58
  response.tag! "samlp:Status" do |status|
45
59
  status.tag! "samlp:StatusCode", Value: Saml::XML::Namespaces::Statuses::SUCCESS
46
60
  end
@@ -17,6 +17,7 @@ module SamlIdp
17
17
  attr_accessor :expiry
18
18
  attr_accessor :encryption_opts
19
19
  attr_accessor :session_expiry
20
+ attr_accessor :signed_message_opts
20
21
 
21
22
  def initialize(reference_id,
22
23
  response_id,
@@ -29,7 +30,8 @@ module SamlIdp
29
30
  authn_context_classref,
30
31
  expiry=60*60,
31
32
  encryption_opts=nil,
32
- session_expiry=0
33
+ session_expiry=0,
34
+ signed_message_opts
33
35
  )
34
36
  self.reference_id = reference_id
35
37
  self.response_id = response_id
@@ -45,10 +47,11 @@ module SamlIdp
45
47
  self.expiry = expiry
46
48
  self.encryption_opts = encryption_opts
47
49
  self.session_expiry = session_expiry
50
+ self.signed_message_opts = signed_message_opts
48
51
  end
49
52
 
50
53
  def build
51
- @built ||= response_builder.encoded
54
+ @built ||= encoded_message
52
55
  end
53
56
 
54
57
  def signed_assertion
@@ -60,8 +63,17 @@ module SamlIdp
60
63
  end
61
64
  private :signed_assertion
62
65
 
66
+ def encoded_message
67
+ if signed_message_opts
68
+ response_builder.encoded(signed_message: true)
69
+ else
70
+ response_builder.encoded(signed_message: false)
71
+ end
72
+ end
73
+ private :encoded_message
74
+
63
75
  def response_builder
64
- ResponseBuilder.new(response_id, issuer_uri, saml_acs_url, saml_request_id, signed_assertion)
76
+ ResponseBuilder.new(response_id, issuer_uri, saml_acs_url, saml_request_id, signed_assertion, algorithm)
65
77
  end
66
78
  private :response_builder
67
79
 
@@ -108,8 +108,7 @@ module SamlIdp
108
108
  canon_algorithm = Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
109
109
  canon_hashed_element = noko_raw.canonicalize(canon_algorithm, inclusive_namespaces)
110
110
  digest_algorithm = get_algorithm
111
-
112
- hash = digest_algorithm.digest(canon_hashed_element)
111
+ hash = digest_algorithm.digest(canon_hashed_element)
113
112
  Base64.strict_encode64(hash).gsub(/\n/, '')
114
113
  end
115
114
  private :digest
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module SamlIdp
3
- VERSION = '0.9.0'
3
+ VERSION = '0.10.0'
4
4
  end
@@ -58,5 +58,5 @@ section of the README.
58
58
  s.add_development_dependency('timecop', '>= 0.8')
59
59
  s.add_development_dependency('xmlenc', '>= 0.6.4')
60
60
  s.add_development_dependency('appraisal')
61
+ s.add_development_dependency('byebug')
61
62
  end
62
-
@@ -6,12 +6,14 @@ module SamlIdp
6
6
  let(:saml_acs_url) { "http://sportngin.com" }
7
7
  let(:saml_request_id) { "134" }
8
8
  let(:assertion_and_signature) { "<Assertion xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"_abc\" IssueInstant=\"2013-07-31T05:00:00Z\" Version=\"2.0\"><Issuer>http://sportngin.com</Issuer><signature>stuff</signature><Subject><NameID Format=\"urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress\">jon.phenow@sportngin.com</NameID><SubjectConfirmation Method=\"urn:oasis:names:tc:SAML:2.0:cm:bearer\"><SubjectConfirmationData InResponseTo=\"123\" NotOnOrAfter=\"2013-07-31T05:03:00Z\" Recipient=\"http://saml.acs.url\"/></SubjectConfirmation></Subject><Conditions NotBefore=\"2013-07-31T04:59:55Z\" NotOnOrAfter=\"2013-07-31T06:00:00Z\"><AudienceRestriction><Audience>http://example.com</Audience></AudienceRestriction></Conditions><AttributeStatement><Attribute Name=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress\"><AttributeValue>jon.phenow@sportngin.com</AttributeValue></Attribute></AttributeStatement><AuthnStatment AuthnInstant=\"2013-07-31T05:00:00Z\" SessionIndex=\"_abc\"><AuthnContext><AuthnContextClassRef>urn:federation:authentication:windows</AuthnContextClassRef></AuthnContext></AuthnStatment></Assertion>" }
9
+ let(:algorithm) { :sha256 }
9
10
  subject { described_class.new(
10
11
  response_id,
11
12
  issuer_uri,
12
13
  saml_acs_url,
13
14
  saml_request_id,
14
- assertion_and_signature
15
+ assertion_and_signature,
16
+ algorithm
15
17
  ) }
16
18
 
17
19
  before do
@@ -24,6 +24,8 @@ module SamlIdp
24
24
  key_transport: 'rsa-oaep-mgf1p',
25
25
  }
26
26
  end
27
+ let(:signed_response_opts) { true }
28
+ let(:unsigned_response_opts) { false }
27
29
  let(:subject_encrypted) { described_class.new(reference_id,
28
30
  response_id,
29
31
  issuer_uri,
@@ -35,7 +37,8 @@ module SamlIdp
35
37
  authn_context_classref,
36
38
  expiry,
37
39
  encryption_opts,
38
- session_expiry
40
+ session_expiry,
41
+ unsigned_response_opts
39
42
  )
40
43
  }
41
44
 
@@ -50,7 +53,8 @@ module SamlIdp
50
53
  authn_context_classref,
51
54
  expiry,
52
55
  nil,
53
- session_expiry
56
+ session_expiry,
57
+ signed_response_opts
54
58
  )
55
59
  }
56
60
 
@@ -77,6 +81,25 @@ module SamlIdp
77
81
  expect(saml_resp.is_valid?).to eq(true)
78
82
  end
79
83
 
84
+ it "will build signed valid response" do
85
+ expect { subject.build }.not_to raise_error
86
+ signed_encoded_xml = subject.build
87
+ resp_settings = saml_settings(saml_acs_url)
88
+ resp_settings.private_key = Default::SECRET_KEY
89
+ resp_settings.issuer = audience_uri
90
+ saml_resp = OneLogin::RubySaml::Response.new(signed_encoded_xml, settings: resp_settings)
91
+ expect(
92
+ Nokogiri::XML(saml_resp.response).at_xpath(
93
+ "//p:Response//ds:Signature",
94
+ {
95
+ "p" => "urn:oasis:names:tc:SAML:2.0:protocol",
96
+ "ds" => "http://www.w3.org/2000/09/xmldsig#"
97
+ }
98
+ )).to be_present
99
+ expect(saml_resp.send(:validate_signature)).to eq(true)
100
+ expect(saml_resp.is_valid?).to eq(true)
101
+ end
102
+
80
103
  it "sets session expiration" do
81
104
  saml_resp = OneLogin::RubySaml::Response.new(subject.build)
82
105
  expect(saml_resp.session_expires_at).to eq Time.local(1990, "jan", 2).iso8601
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: saml_idp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Phenow
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-21 00:00:00.000000000 Z
11
+ date: 2020-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -206,6 +206,20 @@ dependencies:
206
206
  - - ">="
207
207
  - !ruby/object:Gem::Version
208
208
  version: '0'
209
+ - !ruby/object:Gem::Dependency
210
+ name: byebug
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ version: '0'
216
+ type: :development
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
209
223
  description: SAML IdP (Identity Provider) Library for Ruby
210
224
  email: jon.phenow@sportngin.com
211
225
  executables: []
@@ -347,7 +361,7 @@ metadata:
347
361
  homepage_uri: https://github.com/saml-idp/saml_idp
348
362
  source_code_uri: https://github.com/saml-idp/saml_idp
349
363
  bug_tracker_uri: https://github.com/saml-idp/saml_idp/issues
350
- documentation_uri: http://rdoc.info/gems/saml_idp/0.9.0
364
+ documentation_uri: http://rdoc.info/gems/saml_idp/0.10.0
351
365
  post_install_message: |
352
366
  If you're just recently updating saml_idp - please be aware we've changed the default
353
367
  certificate. See the PR and a description of why we've done this here:
@@ -378,8 +392,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
378
392
  - !ruby/object:Gem::Version
379
393
  version: '0'
380
394
  requirements: []
381
- rubyforge_project:
382
- rubygems_version: 2.7.6
395
+ rubygems_version: 3.1.2
383
396
  signing_key:
384
397
  specification_version: 4
385
398
  summary: SAML Indentity Provider for Ruby