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.
- data/.travis.yml +6 -0
- data/README.md +71 -0
- data/lib/saml.rb +30 -0
- data/lib/saml/bindings.rb +17 -0
- data/lib/saml/bindings/http_post.rb +18 -0
- data/lib/saml/bindings/http_redirect.rb +72 -0
- data/lib/saml/core/assertion.rb +45 -0
- data/lib/saml/core/attribute.rb +25 -0
- data/lib/saml/core/attribute_statement.rb +20 -0
- data/lib/saml/core/authn_request.rb +15 -0
- data/lib/saml/core/authn_statement.rb +23 -0
- data/lib/saml/core/document.rb +25 -0
- data/lib/saml/core/logout_request.rb +24 -0
- data/lib/saml/core/request_abstract.rb +42 -0
- data/lib/saml/core/response.rb +28 -0
- data/lib/saml/core/status.rb +26 -0
- data/lib/saml/core/status_response.rb +24 -0
- data/lib/saml/core/subject.rb +13 -0
- data/lib/saml/core/xml_namespaces.rb +21 -0
- data/lib/saml/metadata/document.rb +14 -0
- data/lib/saml/metadata/endpoint.rb +22 -0
- data/lib/saml/metadata/entities_descriptor.rb +32 -0
- data/lib/saml/metadata/entity_descriptor.rb +40 -0
- data/lib/saml/metadata/idp_sso_descriptor.rb +25 -0
- data/lib/saml/metadata/indexed_endpoint.rb +19 -0
- data/lib/saml/metadata/key_descriptor.rb +21 -0
- data/lib/saml/metadata/role_descriptor.rb +21 -0
- data/lib/saml/metadata/sp_sso_descriptor.rb +23 -0
- data/lib/saml/metadata/sso_descriptor.rb +21 -0
- data/lib/saml/metadata/xml_namespaces.rb +19 -0
- data/lib/saml/session.rb +22 -0
- data/lib/saml/version.rb +3 -0
- data/saml.gemspec +24 -0
- data/spec/saml/bindings/http_redirect_spec.rb +49 -0
- data/spec/saml/core/assertion_spec.rb +50 -0
- data/spec/saml/core/attribute_spec.rb +36 -0
- data/spec/saml/core/attribute_statement_spec.rb +18 -0
- data/spec/saml/core/authn_request_spec.rb +39 -0
- data/spec/saml/core/authn_statement_spec.rb +17 -0
- data/spec/saml/core/logout_request_spec.rb +36 -0
- data/spec/saml/core/request_abstract_spec.rb +55 -0
- data/spec/saml/core/response_spec.rb +32 -0
- data/spec/saml/core/status_response_spec.rb +49 -0
- data/spec/saml/core/status_spec.rb +27 -0
- data/spec/saml/core/subject_spec.rb +17 -0
- data/spec/saml/metadata/endpoint_spec.rb +17 -0
- data/spec/saml/metadata/entities_descriptor_spec.rb +18 -0
- data/spec/saml/metadata/entity_descriptor_spec.rb +9 -0
- data/spec/saml/metadata/idp_sso_descriptor_spec.rb +10 -0
- data/spec/saml/metadata/indexed_endpoint_spec.rb +20 -0
- data/spec/saml/metadata/sp_sso_descriptor_spec.rb +8 -0
- data/spec/saml/metadata/sso_descriptor_spec.rb +9 -0
- data/spec/spec_helper.rb +20 -0
- 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,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,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
|
data/lib/saml/session.rb
ADDED
@@ -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
|
data/lib/saml/version.rb
ADDED
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
|