saml 0.1

Sign up to get free protection for your applications and to get access to all the features.
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