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,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