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,28 @@
1
+ require 'xml_signature'
2
+
3
+ module SAML
4
+ module Core
5
+ class Response < StatusResponse
6
+
7
+ attr_reader :assertions
8
+
9
+ def self.from_xml(xml); new.from_xml(xml); end
10
+
11
+ def from_xml(xml)
12
+ super(xml)
13
+ @xml = xml
14
+
15
+ @assertions = xml.get_elements('saml:Assertion').map do |a|
16
+ Assertion.from_xml(a)
17
+ end
18
+
19
+ self
20
+ end
21
+
22
+ def valid?(expected_certificate)
23
+ XMLSignature.new(@xml).verify(expected_certificate)
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,26 @@
1
+ module SAML
2
+ module Core
3
+ class Status
4
+
5
+ attr_reader :status_code
6
+ # FIXME attr_reader :status_message
7
+ # FIXME attr_reader :status_detail
8
+
9
+ def self.from_xml(xml); new.from_xml(xml); end
10
+
11
+ def from_xml(xml)
12
+ @status_code = REXML::XPath.first(
13
+ xml,
14
+ "//samlp:Status/samlp:StatusCode/@Value",
15
+ { 'samlp' => 'urn:oasis:names:tc:SAML:2.0:protocol'}
16
+ ).value
17
+ self
18
+ end
19
+
20
+ def success?
21
+ @status_code == 'urn:oasis:names:tc:SAML:2.0:status:Success'
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ module SAML
2
+ module Core
3
+ class StatusResponse
4
+
5
+ attr_reader :id
6
+ attr_reader :version
7
+ attr_reader :status
8
+
9
+ def self.from_xml(xml); new.from_xml(xml); end
10
+
11
+ def from_xml(xml)
12
+ @id = xml.attributes["ID"]
13
+ @version = xml.attributes["Version"]
14
+ @status = Status.from_xml(xml.get_elements("samlp:Status").first)
15
+ self
16
+ end
17
+
18
+ def success?
19
+ status.success?
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,13 @@
1
+ module SAML
2
+ module Core
3
+ class Subject
4
+
5
+ attr_reader :name_id
6
+
7
+ def from_xml(xml)
8
+ @name_id = xml.get_elements('saml:NameID').text
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ # XML Namespaces
2
+ #
3
+ # SAML v2.0 Core
4
+ #
5
+ # Section 1.2 (and 1.1)
6
+ #
7
+ # http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
8
+ #
9
+
10
+ module SAML
11
+ module Core
12
+
13
+ XMLNamespaces = {
14
+ :saml => 'urn:oasis:names:tc:SAML:2.0:assertion',
15
+ :samlp => 'urn:oasis:names:tc:SAML:2.0:protocol',
16
+ :ds => 'http://www.w3.org/2000/09/xmldsig#',
17
+ :xenc => 'http://www.w3.org/2001/04/xmlenc#',
18
+ }
19
+
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+ require 'rexml/document'
2
+
3
+ module SAML
4
+ module Metadata
5
+ class Document < REXML::Document
6
+
7
+ def initialize(*args)
8
+ super(*args)
9
+ XMLNamespaces.each {|k,v| add_namespace(k, v)}
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,22 @@
1
+ require 'uri'
2
+
3
+ module SAML
4
+ module Metadata
5
+ class Endpoint
6
+
7
+ attr_reader :binding
8
+ attr_reader :location
9
+ attr_reader :response_location
10
+
11
+ def self.from_xml(xml); new.from_xml(xml); end
12
+
13
+ def from_xml(xml)
14
+ @binding = xml.attributes['Binding']
15
+ @location = URI(xml.attributes['Location'])
16
+ @response_location = xml.attributes['ResponseLocation'] && URI(xml.attributes['ResponseLocation'])
17
+ self
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,32 @@
1
+ module SAML
2
+ module Metadata
3
+ class EntitiesDescriptor
4
+
5
+ attr_reader :entity_descriptors
6
+
7
+ def self.from_xml(xml)
8
+ allocate.from_xml(xml)
9
+ end
10
+
11
+ def from_xml(xml)
12
+ @entity_descriptors = xml.get_elements('md:EntityDescriptor').map do |ed_node|
13
+ EntityDescriptor.from_xml(ed_node)
14
+ end
15
+ self
16
+ end
17
+
18
+ def sp
19
+ @entity_descriptors.each do |entity|
20
+ return entity if entity.sp?
21
+ end
22
+ end
23
+
24
+ def idp
25
+ @entity_descriptors.each do |entity|
26
+ return entity if entity.idp?
27
+ end
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,40 @@
1
+ module SAML
2
+ module Metadata
3
+ class EntityDescriptor
4
+
5
+ attr_accessor :entity_id
6
+
7
+ def self.from_xml(xml)
8
+ allocate.from_xml(xml)
9
+ end
10
+
11
+ def from_xml(xml)
12
+ @entity_id = xml.attributes['entityID']
13
+ @sp_sso_descriptors = xml.get_elements('md:SPSSODescriptor').map do |elem|
14
+ SPSSODescriptor.from_xml(elem)
15
+ end
16
+ @idp_sso_descriptors = xml.get_elements('md:IDPSSODescriptor').map do |elem|
17
+ IDPSSODescriptor.from_xml(elem)
18
+ end
19
+ self
20
+ end
21
+
22
+ def sp_sso_descriptors
23
+ @sp_sso_descriptors.clone
24
+ end
25
+
26
+ def idp_sso_descriptors
27
+ @idp_sso_descriptors.clone
28
+ end
29
+
30
+ def sp?
31
+ not @sp_sso_descriptors.empty?
32
+ end
33
+
34
+ def idp?
35
+ not @idp_sso_descriptors.empty?
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,25 @@
1
+ module SAML
2
+ module Metadata
3
+ class IDPSSODescriptor < SSODescriptor
4
+
5
+ def self.from_xml(xml); new.from_xml(xml); end
6
+
7
+ def from_xml(xml)
8
+ super(xml)
9
+ @single_signon_services = xml.get_elements('md:SingleSignOnService').map do |elem|
10
+ Endpoint.from_xml(elem)
11
+ end
12
+ self
13
+ end
14
+
15
+ def want_authn_requests_signed?
16
+ false
17
+ end
18
+
19
+ def single_signon_services
20
+ @single_signon_services.clone
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ module SAML
2
+ module Metadata
3
+ class IndexedEndpoint < Endpoint
4
+
5
+ attr_reader :index
6
+ attr_reader :is_default
7
+
8
+ def self.from_xml(xml); new.from_xml(xml); end
9
+
10
+ def from_xml(xml)
11
+ super(xml)
12
+ @index = xml.attributes['Index']
13
+ @is_default = xml.attributes['IsDefault']
14
+ self
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ module SAML
2
+ module Metadata
3
+ class KeyDescriptor
4
+
5
+ attr_accessor :use
6
+ attr_accessor :key_info
7
+ attr_accessor :x509_certificate
8
+
9
+ def self.from_xml(xml); new.from_xml(xml); end
10
+
11
+ def from_xml(xml)
12
+ @use = xml.attributes['use']
13
+ puts xml.to_s(2)
14
+ @x509_certificate = REXML::XPath.first(xml, '//ds:X509Certificate').text
15
+
16
+ self
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module SAML
2
+ module Metadata
3
+ class RoleDescriptor
4
+
5
+ attr_accessor :protocol_support_enumeration
6
+ attr_accessor :key_descriptors
7
+
8
+ def from_xml(xml)
9
+ @key_descriptors = xml.get_elements('md:KeyDescriptor').map do |kd_node|
10
+ KeyDescriptor.from_xml(kd_node)
11
+ end
12
+ self
13
+ end
14
+
15
+ def signing_key_descriptor
16
+ @key_descriptors.find {|kd| kd.use == 'signing' }
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ module SAML
2
+ module Metadata
3
+ class SPSSODescriptor < SSODescriptor
4
+
5
+ def self.from_xml(xml); new.from_xml(xml); end
6
+
7
+ def from_xml(xml)
8
+ super(xml)
9
+
10
+ @assertion_consumer_services = xml.get_elements('md:AssertionConsumerService').map do |elem|
11
+ IndexedEndpoint.from_xml(elem)
12
+ end
13
+
14
+ self
15
+ end
16
+
17
+ def assertion_consumer_services
18
+ @assertion_consumer_services.clone
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ require 'saml/metadata/role_descriptor'
2
+
3
+ module SAML
4
+ module Metadata
5
+ class SSODescriptor < RoleDescriptor
6
+
7
+ def from_xml(xml)
8
+ super(xml)
9
+ @single_logout_services = xml.get_elements('md:SingleLogoutService').map do |elem|
10
+ Endpoint.from_xml(elem)
11
+ end
12
+ self
13
+ end
14
+
15
+ def single_logout_services
16
+ @single_logout_services.clone
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ # XML Namespaces
2
+ #
3
+ # SAML v2.0 Metadata
4
+ #
5
+ # Section FIXME
6
+ #
7
+ # http://docs.oasis-open.org/security/saml/v2.0/saml-metadata-2.0-os.pdf
8
+ #
9
+
10
+ module SAML
11
+ module Metadata
12
+
13
+ XMLNamespaces = {
14
+ :md => 'urn:oasis:names:tc:SAML:2.0:metadata',
15
+ :ds => 'http://www.w3.org/2000/09/xmldsig#',
16
+ }
17
+
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ module SAML
2
+
3
+ class Session
4
+
5
+ attr_reader :id
6
+
7
+ def initialize(id)
8
+ @id = id
9
+ @participants = []
10
+ end
11
+
12
+ def add_participant(service_provider)
13
+ @participants << service_provider
14
+ end
15
+
16
+ def logout_participants
17
+ @participants.each { |p| p.logout(@id) }
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,3 @@
1
+ module SAML
2
+ VERSION = "0.1"
3
+ end
data/saml.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "saml/version"
4
+
5
+ development_files = %w(.gitignore Guardfile Gemfile)
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "saml"
9
+ s.version = SAML::VERSION
10
+ s.author = "Kjell-Magne Øierud"
11
+ s.email = ["kjellm@oierud.net"]
12
+ s.homepage = "https://github.com/kjellm/skeleton"
13
+ s.license = "MIT"
14
+ s.summary = %q{Partial implementation of the SAML Standard}
15
+ s.description = %q{Partial implementation of the SAML Standard}
16
+
17
+ s.files = `git ls-files`.split("\n") - development_files
18
+ s.require_paths = ["lib"]
19
+
20
+ s.required_ruby_version = '>= 1.8.7'
21
+
22
+ s.add_runtime_dependency "uuid"
23
+ s.add_runtime_dependency "xml_signature"
24
+ end