saml 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. data/.travis.yml +6 -0
  2. data/README.md +71 -0
  3. data/lib/saml.rb +30 -0
  4. data/lib/saml/bindings.rb +17 -0
  5. data/lib/saml/bindings/http_post.rb +18 -0
  6. data/lib/saml/bindings/http_redirect.rb +72 -0
  7. data/lib/saml/core/assertion.rb +45 -0
  8. data/lib/saml/core/attribute.rb +25 -0
  9. data/lib/saml/core/attribute_statement.rb +20 -0
  10. data/lib/saml/core/authn_request.rb +15 -0
  11. data/lib/saml/core/authn_statement.rb +23 -0
  12. data/lib/saml/core/document.rb +25 -0
  13. data/lib/saml/core/logout_request.rb +24 -0
  14. data/lib/saml/core/request_abstract.rb +42 -0
  15. data/lib/saml/core/response.rb +28 -0
  16. data/lib/saml/core/status.rb +26 -0
  17. data/lib/saml/core/status_response.rb +24 -0
  18. data/lib/saml/core/subject.rb +13 -0
  19. data/lib/saml/core/xml_namespaces.rb +21 -0
  20. data/lib/saml/metadata/document.rb +14 -0
  21. data/lib/saml/metadata/endpoint.rb +22 -0
  22. data/lib/saml/metadata/entities_descriptor.rb +32 -0
  23. data/lib/saml/metadata/entity_descriptor.rb +40 -0
  24. data/lib/saml/metadata/idp_sso_descriptor.rb +25 -0
  25. data/lib/saml/metadata/indexed_endpoint.rb +19 -0
  26. data/lib/saml/metadata/key_descriptor.rb +21 -0
  27. data/lib/saml/metadata/role_descriptor.rb +21 -0
  28. data/lib/saml/metadata/sp_sso_descriptor.rb +23 -0
  29. data/lib/saml/metadata/sso_descriptor.rb +21 -0
  30. data/lib/saml/metadata/xml_namespaces.rb +19 -0
  31. data/lib/saml/session.rb +22 -0
  32. data/lib/saml/version.rb +3 -0
  33. data/saml.gemspec +24 -0
  34. data/spec/saml/bindings/http_redirect_spec.rb +49 -0
  35. data/spec/saml/core/assertion_spec.rb +50 -0
  36. data/spec/saml/core/attribute_spec.rb +36 -0
  37. data/spec/saml/core/attribute_statement_spec.rb +18 -0
  38. data/spec/saml/core/authn_request_spec.rb +39 -0
  39. data/spec/saml/core/authn_statement_spec.rb +17 -0
  40. data/spec/saml/core/logout_request_spec.rb +36 -0
  41. data/spec/saml/core/request_abstract_spec.rb +55 -0
  42. data/spec/saml/core/response_spec.rb +32 -0
  43. data/spec/saml/core/status_response_spec.rb +49 -0
  44. data/spec/saml/core/status_spec.rb +27 -0
  45. data/spec/saml/core/subject_spec.rb +17 -0
  46. data/spec/saml/metadata/endpoint_spec.rb +17 -0
  47. data/spec/saml/metadata/entities_descriptor_spec.rb +18 -0
  48. data/spec/saml/metadata/entity_descriptor_spec.rb +9 -0
  49. data/spec/saml/metadata/idp_sso_descriptor_spec.rb +10 -0
  50. data/spec/saml/metadata/indexed_endpoint_spec.rb +20 -0
  51. data/spec/saml/metadata/sp_sso_descriptor_spec.rb +8 -0
  52. data/spec/saml/metadata/sso_descriptor_spec.rb +9 -0
  53. data/spec/spec_helper.rb +20 -0
  54. metadata +131 -0
@@ -0,0 +1,49 @@
1
+ # HTTP Redirect Binding
2
+ #
3
+ # SAML v2.0 Bindings
4
+ #
5
+ # Section 3.4
6
+ #
7
+ # http://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf
8
+ #
9
+
10
+ require 'spec_helper'
11
+
12
+ module SAML
13
+ module Bindings
14
+ describe HTTPRedirect do
15
+
16
+ let :endpoint do
17
+ endpoint = double('Endpoint')
18
+ endpoint.stub(:location).and_return('')
19
+ endpoint
20
+ end
21
+
22
+ let(:rake) { double('Rake') }
23
+
24
+ describe "#send_request" do
25
+ it "should base64 and url encode the SAMLRequest query parameter" do
26
+ sr = Core::AuthnRequest.new
27
+ rake.should_receive(:redirect).with(/\?SAMLRequest=[A-za-z0-9%]+/)
28
+
29
+ subject.build_request(rake, endpoint, sr)
30
+ end
31
+
32
+ context "with relay_state parameter" do
33
+ let(:saml_request) { double('SAMLRequest').as_null_object }
34
+
35
+ it "should accept a relay_state and URL encode it" do
36
+ rake.should_receive(:redirect).with(/RelayState=A\+relay\+state/)
37
+ subject.build_request(rake, endpoint, saml_request, 'A relay state')
38
+ end
39
+
40
+ it "should not accept relay_state longer than 80 bytes" do
41
+ expect {
42
+ subject.build_request(rake, endpoint, saml_request, 'x'*81)
43
+ }.to raise_error(ArgumentError)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,50 @@
1
+ # Assertion
2
+ #
3
+ # SAML v2.0 Core
4
+ #
5
+ # Section 2.3.3
6
+ #
7
+ # http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
8
+ #
9
+
10
+ require "spec_helper"
11
+
12
+ module SAML
13
+ module Core
14
+ describe Assertion do
15
+
16
+ describe "#from_xml" do
17
+
18
+ def xml(str='')
19
+ Document.new(<<EOT).root
20
+ <s:Assertion xmlns:s='urn:oasis:names:tc:SAML:2.0:assertion' ID="1" Version="2.0" IssueInstant="2012-01-01T07:00:00Z">
21
+ <s:Issuer>Me</s:Issuer>
22
+ #{str}
23
+ </s:Assertion>
24
+ EOT
25
+ end
26
+
27
+ context "Minimal valid XML" do
28
+
29
+ subject do
30
+ Assertion.from_xml(xml)
31
+ end
32
+
33
+ its(:id) { should == "1" }
34
+ its(:version) { should == "2.0" }
35
+ its(:issue_instant) { should == "2012-01-01T07:00:00Z" }
36
+ end
37
+
38
+ context "With AttributeStatement" do
39
+ it do
40
+ a = Assertion.from_xml(xml('<s:AttributeStatement><s:Attribute Name="Foo"><s:AttributeValue>Bar</s:AttributeValue></s:Attribute></s:AttributeStatement>'))
41
+ a.attribute_statement.attributes.first.name.should == "Foo"
42
+ end
43
+ end
44
+
45
+ it "should fail when required fields are missing"
46
+ end
47
+ end
48
+ end
49
+ end
50
+
@@ -0,0 +1,36 @@
1
+ # Attribute
2
+ #
3
+ # SAML v2.0 Core
4
+ #
5
+ # Section 2.7.3.1
6
+ #
7
+ # http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
8
+ #
9
+
10
+ require 'spec_helper'
11
+
12
+ module SAML
13
+ module Core
14
+ describe Attribute do
15
+
16
+ describe "#from_xml" do
17
+
18
+ context "Minimal valid XML" do
19
+
20
+ subject do
21
+ r = Attribute.from_xml(Document.new(<<EOT).root)
22
+ <s:Attribute xmlns:s='urn:oasis:names:tc:SAML:2.0:assertion' Name="Foo">
23
+ <s:AttributeValue>Hei</s:AttributeValue>
24
+ </s:Attribute>
25
+ EOT
26
+ end
27
+
28
+ its(:name) { should == "Foo" }
29
+ its(:name_format) { should == "urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified" }
30
+ its(:attribute_values) { subject.first.should =~ /Hei/ }
31
+
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,18 @@
1
+ # AttributeStatement
2
+ #
3
+ # SAML v2.0 Core
4
+ #
5
+ # Section 2.7.3
6
+ #
7
+ # http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
8
+ #
9
+
10
+ require "spec_helper"
11
+
12
+ module SAML
13
+ module Core
14
+ describe AttributeStatement do
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,39 @@
1
+ # AuthnRequest
2
+ #
3
+ # SAML v2.0 Core
4
+ #
5
+ # Section 3.4.1
6
+ #
7
+ # http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
8
+ #
9
+
10
+ require 'spec_helper'
11
+
12
+ module SAML
13
+ module Core
14
+ describe AuthnRequest do
15
+
16
+ it "should extend RequestAbstract" do
17
+ AuthnRequest.new.should be_kind_of(RequestAbstract)
18
+ end
19
+
20
+ describe "#to_xml" do
21
+ context "attributes" do
22
+ subject { AuthnRequest.new.to_xml.root.attributes }
23
+ it { should include('Version') }
24
+ it { should include('ID') }
25
+ it { should include('IssueInstant') }
26
+ end
27
+
28
+ context "with Issuer" do
29
+ it "should have an Issuer element in the xml" do
30
+ r = AuthnRequest.new
31
+ r.issuer = "me"
32
+ xml = r.to_xml.root
33
+ xml.get_elements('saml:Issuer').first.text.should == "me"
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,17 @@
1
+ # AuthnStatement
2
+ #
3
+ # SAML v2.0 Core
4
+ #
5
+ # Section 2.7.2
6
+ #
7
+ # http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
8
+ #
9
+
10
+ require 'spec_helper'
11
+
12
+ module SAML
13
+ module Core
14
+ describe AuthnStatement do
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,36 @@
1
+ # LogoutRequest
2
+ #
3
+ # SAML v2.0 Core
4
+ #
5
+ # Section 3.7.1
6
+ #
7
+ # http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
8
+ #
9
+
10
+ require 'spec_helper'
11
+
12
+ module SAML
13
+ module Core
14
+ describe LogoutRequest do
15
+
16
+ it "should extend RequestAbstract" do
17
+ AuthnRequest.new.should be_kind_of(RequestAbstract)
18
+ end
19
+
20
+ %w(NotOnOrAfter Reason BaseID/EncryptedID SessionIndex).each do |attr|
21
+ it "should not support optional attribute/element #{attr}"
22
+ end
23
+
24
+ describe "#to_xml" do
25
+ subject do
26
+ r = LogoutRequest.new
27
+ r.name_id = "test@example.com"
28
+ r.to_xml.root
29
+ end
30
+
31
+ it { should have(1).elements }
32
+ its(:name) { should == "LogoutRequest" }
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,55 @@
1
+ # RequestAbstract
2
+ #
3
+ # SAML v2.0 Core
4
+ #
5
+ # Section 3.2.1
6
+ #
7
+ # http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
8
+ #
9
+
10
+ require 'spec_helper'
11
+
12
+ module SAML
13
+ module Core
14
+ describe RequestAbstract do
15
+
16
+ it "should have a ID conforming to the xs:ID data type" do
17
+ subject.id.should match(/[A-Za-z0-9-]+/)
18
+ end
19
+
20
+ its(:version) { should == "2.0" }
21
+
22
+ describe "#issue_instant" do
23
+ it "should have an issue instant formated as %Y-%m-%dT%H:%M:%SZ" do
24
+ subject.issue_instant.should match(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z/)
25
+ end
26
+
27
+ it "should be set to now" do
28
+ t = double(Time)
29
+ t.should_receive(:now).and_return(Time.now)
30
+ RequestAbstract.new(t)
31
+ end
32
+ end
33
+
34
+ describe "#issuer" do
35
+ it do
36
+ subject.issuer = 'test'
37
+ end
38
+
39
+ it "should have an issuer element in the xml when issuer is set" do
40
+ subject.issuer = 'test'
41
+ subject.to_xml.should match_xpath('//saml:Issuer')
42
+ end
43
+
44
+ it "should not have an issuer element in the xml when issuer is not set" do
45
+ subject.to_xml.should_not match_xpath('//saml:Issuer')
46
+ end
47
+ end
48
+
49
+ %w(Destination Consent Signature Extensions).each do |attr|
50
+ it "should not support #{attr}"
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,32 @@
1
+ # Response
2
+ #
3
+ # SAML v2.0 Core
4
+ #
5
+ # Section 3.3.3
6
+ #
7
+ # http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
8
+ #
9
+
10
+ require 'spec_helper'
11
+
12
+ module SAML
13
+ module Core
14
+ describe Response do
15
+
16
+ it { should be_kind_of(StatusResponse) }
17
+
18
+ describe "#from_xml" do
19
+
20
+ it do
21
+ xml = Document.new(<<EOT).root
22
+ <sp:StatusResponse xmlns:sp='urn:oasis:names:tc:SAML:2.0:protocol' xmlns:s='urn:oasis:names:tc:SAML:2.0:assertion' ID="1" Version="2.0">
23
+ <sp:Status><sp:StatusCode Value="Jolly good"/></sp:Status>
24
+ <s:Assertion ID="1" Version="2.0" IssueInstant="right now"></s:Assertion>
25
+ </sp:StatusResponse>
26
+ EOT
27
+ r = Response.from_xml(xml)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,49 @@
1
+ # StatusResponse
2
+ #
3
+ # SAML v2.0 Core
4
+ #
5
+ # Section 3.2.2
6
+ #
7
+ # http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
8
+ #
9
+
10
+ require 'spec_helper'
11
+
12
+ module SAML
13
+ module Core
14
+ describe StatusResponse do
15
+
16
+ #ID [Required]
17
+ #InResponseTo [Optional]
18
+ #Version [Required]
19
+ #IssueInstant [Required]
20
+ #Destination [Optional]
21
+ #Consent [Optional]
22
+ #<saml:Issuer> [Optional]
23
+ #<ds:Signature> [Optional]
24
+ #<Extensions> [Optional]
25
+ #<Status> [Required]
26
+
27
+ describe "#from_xml" do
28
+
29
+ context "Minimal valid XML" do
30
+
31
+ subject do
32
+ xml = Document.new(<<EOT)
33
+ <sp:StatusResponse xmlns:sp='urn:oasis:names:tc:SAML:2.0:protocol' ID="1" Version="2.0">
34
+ <sp:Status><sp:StatusCode Value="Jolly good"/></sp:Status>
35
+ </sp:StatusResponse>
36
+ EOT
37
+ StatusResponse.from_xml(xml.root)
38
+ end
39
+
40
+ its(:id) { should == "1" }
41
+ its(:version) { should == "2.0" }
42
+ its(:status) { subject.status_code.should == "Jolly good" }
43
+ end
44
+
45
+ it "should fail when required fields are missing"
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,27 @@
1
+ # Status
2
+ #
3
+ # SAML v2.0 Core
4
+ #
5
+ # Section 3.2.2.1
6
+ #
7
+ # http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
8
+ #
9
+
10
+ require 'spec_helper'
11
+
12
+ module SAML
13
+ module Core
14
+ describe Status do
15
+ it "should only allow the following top-level status codes"
16
+ # urn:oasis:names:tc:SAML:2.0:status:Success
17
+ # The request succeeded. Additional information MAY be returned in the <StatusMessage> and/or <StatusDetail> elements.
18
+ # urn:oasis:names:tc:SAML:2.0:status:Requester
19
+ # The request could not be performed due to an error on the part of the requester.
20
+ # urn:oasis:names:tc:SAML:2.0:status:Responder
21
+ # The request could not be performed due to an error on the part of the SAML responder or SAML authority.
22
+ # urn:oasis:names:tc:SAML:2.0:status:VersionMismatch
23
+ # The SAML responder could not process the request because the version of the request message was incorrect.
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,17 @@
1
+ # Subject
2
+ #
3
+ # SAML v2.0 Core
4
+ #
5
+ # Section 2.4.1
6
+ #
7
+ # http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
8
+ #
9
+
10
+ require 'spec_helper'
11
+
12
+ module SAML
13
+ module Core
14
+ describe Subject do
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # Status
2
+ #
3
+ # SAML v2.0 Metadata
4
+ #
5
+ # Section 2.2.2
6
+ #
7
+ # http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf
8
+ #
9
+
10
+ require 'spec_helper'
11
+
12
+ module SAML
13
+ module Metadata
14
+ describe Endpoint do
15
+ end
16
+ end
17
+ end