openlogic-saml-sp 3.1.3 → 4.0.0.alpha.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/README.md +1 -1
- data/Rakefile +10 -7
- data/VERSION +1 -1
- data/lib/saml-sp.rb +21 -0
- data/lib/saml2/artifact_resolver.rb +2 -3
- data/lib/saml2/assertion.rb +12 -1
- data/lib/saml2/authn_request.rb +26 -0
- data/lib/saml2/issuer.rb +35 -0
- data/lib/saml2.rb +2 -0
- data/lib/saml_sp/config.rb +22 -0
- data/spec/saml2/artifact_resolver_spec.rb +2 -0
- data/spec/saml2/assertion_spec.rb +3 -0
- data/spec/saml_sp/config_spec.rb +44 -0
- metadata +45 -8
data/README.md
CHANGED
data/Rakefile
CHANGED
@@ -3,12 +3,15 @@ begin
|
|
3
3
|
Jeweler::Tasks.new do |gemspec|
|
4
4
|
gemspec.name = 'openlogic-saml-sp'
|
5
5
|
gemspec.summary = 'SAML 2.0 SSO Sevice Provider Library'
|
6
|
-
gemspec.email = 'gbettridge@openlogic.com'
|
7
|
-
gemspec.authors = ["OpenLogic", "Peter Williams","Glen Aultman-Bettridge"]
|
6
|
+
gemspec.email = ['gbettridge@openlogic.com', 'todd.thomas@openlogic.com']
|
7
|
+
gemspec.authors = ["OpenLogic", "Peter Williams", "Glen Aultman-Bettridge", "Todd Thomas"]
|
8
|
+
gemspec.homepage = 'https://github.com/openlogic/saml-sp'
|
8
9
|
gemspec.add_dependency 'nokogiri'
|
10
|
+
gemspec.add_dependency 'signed_xml', '~> 1.0'
|
9
11
|
gemspec.add_dependency 'openlogic-resourceful'
|
10
12
|
gemspec.add_dependency 'uuidtools'
|
11
|
-
gemspec.add_development_dependency 'rspec'
|
13
|
+
gemspec.add_development_dependency 'rspec', '~> 2.12'
|
14
|
+
gemspec.add_development_dependency 'fakeweb'
|
12
15
|
gemspec.files = FileList["[A-Z]*", "{bin,generators,lib,test,spec,rails}/**/*"]
|
13
16
|
end
|
14
17
|
Jeweler::GemcutterTasks.new
|
@@ -16,10 +19,10 @@ rescue LoadError
|
|
16
19
|
puts "Jeweler not available. Install it with: gem install jeweler"
|
17
20
|
end
|
18
21
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
22
|
+
require "rspec/core/rake_task"
|
23
|
+
RSpec::Core::RakeTask.new(:spec)
|
24
|
+
|
25
|
+
task :default => :spec
|
23
26
|
# EOF
|
24
27
|
|
25
28
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
4.0.0.alpha.1
|
data/lib/saml-sp.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'digest'
|
1
2
|
require 'logger'
|
2
3
|
|
3
4
|
module SamlSp
|
@@ -57,6 +58,26 @@ module SamlSp
|
|
57
58
|
end
|
58
59
|
end
|
59
60
|
|
61
|
+
CertificateStore = Class.new(Hash) do
|
62
|
+
include Logging
|
63
|
+
|
64
|
+
def load_certificates(glob)
|
65
|
+
logger.info "loading certificates from #{glob}"
|
66
|
+
Dir[glob].each do |file|
|
67
|
+
begin
|
68
|
+
next unless File.file?(file)
|
69
|
+
logger.info "reading #{file}"
|
70
|
+
cert = OpenSSL::X509::Certificate.new(File.read(file))
|
71
|
+
fingerprint = Digest::SHA1.hexdigest(cert.to_der)
|
72
|
+
self[fingerprint] = cert
|
73
|
+
logger.info "loaded certificate #{cert.inspect} with fingerprint #{fingerprint}"
|
74
|
+
rescue StandardError => e
|
75
|
+
logger.warn "unable to read X.509 cert from #{file}: #{e.message}"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end.new
|
80
|
+
|
60
81
|
autoload :Config, 'saml_sp/config'
|
61
82
|
end # module SamlSp
|
62
83
|
|
@@ -23,7 +23,7 @@ module Saml2
|
|
23
23
|
|
24
24
|
# Initialize and register a new artifact resolver.
|
25
25
|
#
|
26
|
-
# @param [string] source_id An
|
26
|
+
# @param [string] source_id An opacque identifier used by the IDP
|
27
27
|
# to identify artifact that can be resolved by this service.
|
28
28
|
#
|
29
29
|
# @param [string] resolution_service_uri The URI that will resolve
|
@@ -80,12 +80,11 @@ module Saml2
|
|
80
80
|
# response do not match the idp_id for this source.
|
81
81
|
def resolve(artifact)
|
82
82
|
soap_body = request_document_for(artifact)
|
83
|
-
logger.
|
83
|
+
logger.debug{"ArtifactResolve request body:\n#{soap_body.gsub(/^/, "\t")}"}
|
84
84
|
resp = http.resource(resolution_service_uri).post(soap_body,
|
85
85
|
'Accept' => 'application/soap+xml',
|
86
86
|
'Content-Type' => 'application/soap+xml')
|
87
87
|
|
88
|
-
logger.info{"ArtifactResolve response body:\n#{resp.gsub(/^/, "\t")}"}
|
89
88
|
doc = Nokogiri::XML.parse(resp.body)
|
90
89
|
assert_successful_response(doc)
|
91
90
|
|
data/lib/saml2/assertion.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'nokogiri'
|
2
2
|
require 'saml2/artifact_resolver'
|
3
|
+
require 'signed_xml'
|
3
4
|
|
4
5
|
module Saml2
|
5
6
|
class InvalidAssertionError < ArgumentError
|
@@ -60,8 +61,13 @@ module Saml2
|
|
60
61
|
else
|
61
62
|
Nokogiri::XML.parse(xml_assertion)
|
62
63
|
end
|
63
|
-
logger.
|
64
|
+
logger.info {"Parsing assertion: \n" + doc.to_xml(:indent => 2).gsub(/^/, "\t")}
|
64
65
|
|
66
|
+
# We can't use the helpful #issuer_from until the 'asrt' namespace is defined,
|
67
|
+
# but we can't add that definition without breaking signature verification.
|
68
|
+
# This is sad.
|
69
|
+
issuer = doc.at_xpath('//saml2:Assertion/saml2:Issuer', saml2: "urn:oasis:names:tc:SAML:2.0:assertion").text.strip
|
70
|
+
verify(doc) if Saml2::Issuer(issuer).verify_signatures?
|
65
71
|
|
66
72
|
doc.root.add_namespace_definition('asrt', 'urn:oasis:names:tc:SAML:2.0:assertion')
|
67
73
|
|
@@ -86,6 +92,11 @@ module Saml2
|
|
86
92
|
attributes[attr_name.to_s]
|
87
93
|
end
|
88
94
|
|
95
|
+
def self.verify(doc)
|
96
|
+
signed_doc = SignedXml::Document(doc)
|
97
|
+
raise "SAML assertion failed verification" unless signed_doc.is_verified?(SamlSp::CertificateStore)
|
98
|
+
end
|
99
|
+
|
89
100
|
protected
|
90
101
|
|
91
102
|
attr_reader :attributes
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Saml2
|
2
|
+
class AuthnRequest
|
3
|
+
attr_reader :issuer, :id, :issue_instant, :assertion_consumer_service_url
|
4
|
+
|
5
|
+
def initialize(attrs = {})
|
6
|
+
@issuer = attrs.fetch(:issuer)
|
7
|
+
@id = SecureRandom.uuid
|
8
|
+
@issue_instant = Time.now.utc.strftime('%FT%TZ')
|
9
|
+
@assertion_consumer_service_url = attrs[:assertion_consumer_service_url]
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_xml
|
13
|
+
Nokogiri::XML::Builder.new do |xml|
|
14
|
+
xml.AuthnRequest('xmlns:samlp' => 'urn:oasis:names:tc:SAML:2.0:protocol',
|
15
|
+
'xmlns:saml' => 'urn:oasis:names:tc:SAML:2.0:assertion',
|
16
|
+
'ID' => id,
|
17
|
+
'Version' => '2.0',
|
18
|
+
'IssueInstant' => issue_instant,
|
19
|
+
'AssertionConsumerServiceURL' => assertion_consumer_service_url) do
|
20
|
+
xml.parent.namespace = xml.parent.namespace_definitions.find {|ns| ns.prefix == 'samlp'} # Necessary to output namespace prefix on root element.
|
21
|
+
xml['saml'].Issuer issuer
|
22
|
+
end
|
23
|
+
end.to_xml
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/saml2/issuer.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module Saml2
|
2
|
+
NoSuchIssuerError = Class.new(StandardError)
|
3
|
+
|
4
|
+
class Issuer
|
5
|
+
attr_reader :id, :verify_signatures
|
6
|
+
|
7
|
+
def initialize(id, verify_signatures)
|
8
|
+
@id = id
|
9
|
+
@verify_signatures = verify_signatures
|
10
|
+
|
11
|
+
IssuerRegistry.register self
|
12
|
+
end
|
13
|
+
|
14
|
+
def verify_signatures?
|
15
|
+
verify_signatures
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.Issuer(id)
|
20
|
+
if IssuerRegistry.has_key? id
|
21
|
+
IssuerRegistry[id]
|
22
|
+
else
|
23
|
+
raise NoSuchIssuerError, "No Issuer registered with id [#{id}]"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
IssuerRegistry = Class.new(Hash) do
|
28
|
+
include SamlSp::Logging
|
29
|
+
|
30
|
+
def register(issuer)
|
31
|
+
self[issuer.id] = issuer
|
32
|
+
logger.info "saml-sp: #{issuer.inspect}' registered"
|
33
|
+
end
|
34
|
+
end.new
|
35
|
+
end
|
data/lib/saml2.rb
CHANGED
data/lib/saml_sp/config.rb
CHANGED
@@ -60,6 +60,11 @@ METHOD
|
|
60
60
|
dsl = ResolutionSerivceConfig.new
|
61
61
|
dsl.interpret(blk)
|
62
62
|
end
|
63
|
+
|
64
|
+
def issuer(&blk)
|
65
|
+
dsl = IssuerConfig.new
|
66
|
+
dsl.interpret(blk)
|
67
|
+
end
|
63
68
|
end
|
64
69
|
|
65
70
|
|
@@ -111,6 +116,23 @@ METHOD
|
|
111
116
|
(@realm || @promiscuous) && @user_id && @password
|
112
117
|
end
|
113
118
|
end
|
119
|
+
|
120
|
+
class IssuerConfig < ConfigBlock
|
121
|
+
config_item :id
|
122
|
+
config_item :verify_signatures
|
123
|
+
|
124
|
+
def interpret(blk, filename = nil)
|
125
|
+
super
|
126
|
+
|
127
|
+
raise ConfigurationError, "Incomplete Issuer configuration" unless valid?
|
128
|
+
|
129
|
+
Saml2::Issuer.new(@id, @verify_signatures)
|
130
|
+
end
|
131
|
+
|
132
|
+
def valid?
|
133
|
+
@id && !@verify_signatures.nil?
|
134
|
+
end
|
135
|
+
end
|
114
136
|
end
|
115
137
|
|
116
138
|
# Copyright (c) 2010 OpenLogic
|
@@ -22,6 +22,8 @@ describe Saml2::ArtifactResolver do
|
|
22
22
|
before do
|
23
23
|
@resolver = Saml2::ArtifactResolver.new('a-source-id', 'https://idp.invalid/resolution-service', 'http://idp.invalid', 'http://sp.invalid')
|
24
24
|
@resolver.basic_auth_credentials('myuserid', 'mypasswd', 'myrealm')
|
25
|
+
# register issuer which doesn't require verification
|
26
|
+
Saml2::Issuer.new(@resolver.idp_id, false)
|
25
27
|
|
26
28
|
@artifact = Saml2::Type4Artifact.new(0, '01234567890123456789', 'abcdefghijklmnopqrst')
|
27
29
|
FakeWeb.register_uri(:post, 'https://idp.invalid/resolution-service', :body => SUCCESSFUL_SAML_RESP)
|
@@ -146,6 +146,9 @@ describe Saml2::Assertion do
|
|
146
146
|
</SOAP-ENV:Body>
|
147
147
|
</SOAP-ENV:Envelope>
|
148
148
|
XML
|
149
|
+
|
150
|
+
# register issuer which doesn't require verification
|
151
|
+
Saml2::Issuer.new('https://idp.invalid', false)
|
149
152
|
end
|
150
153
|
|
151
154
|
def self.it_should_extract(prop, expected_value)
|
data/spec/saml_sp/config_spec.rb
CHANGED
@@ -201,6 +201,30 @@ describe SamlSp::Config do
|
|
201
201
|
end
|
202
202
|
end
|
203
203
|
|
204
|
+
describe "valid issuer description" do
|
205
|
+
before do
|
206
|
+
@dsl = SamlSp::Config.new
|
207
|
+
@issuer = @dsl.interpret(<<-CONFIG)
|
208
|
+
issuer {
|
209
|
+
id "http://sso.example.org"
|
210
|
+
verify_signatures true
|
211
|
+
}
|
212
|
+
CONFIG
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should build an issuer" do
|
216
|
+
@issuer.should be_kind_of(Saml2::Issuer)
|
217
|
+
end
|
218
|
+
|
219
|
+
it "should build an issuer with correct id" do
|
220
|
+
@issuer.id.should == 'http://sso.example.org'
|
221
|
+
end
|
222
|
+
|
223
|
+
it "should build an issuer with correct verify_signatures setting" do
|
224
|
+
@issuer.verify_signatures?.should be true
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
204
228
|
it "should raise error on missing source_id" do
|
205
229
|
lambda {
|
206
230
|
@dsl.interpret(<<-CONFIG)
|
@@ -290,6 +314,26 @@ describe SamlSp::Config do
|
|
290
314
|
}.should raise_error SamlSp::ConfigurationError
|
291
315
|
end
|
292
316
|
|
317
|
+
it "should raise error on missing issuer id" do
|
318
|
+
lambda {
|
319
|
+
@dsl.interpret(<<-CONFIG)
|
320
|
+
issuer {
|
321
|
+
verify_signatures true
|
322
|
+
}
|
323
|
+
CONFIG
|
324
|
+
}.should raise_error SamlSp::ConfigurationError
|
325
|
+
end
|
326
|
+
|
327
|
+
it "should raise error on missing verify_signatures setting" do
|
328
|
+
lambda {
|
329
|
+
@dsl.interpret(<<-CONFIG)
|
330
|
+
issuer {
|
331
|
+
id "http://sso.example.org"
|
332
|
+
}
|
333
|
+
CONFIG
|
334
|
+
}.should raise_error SamlSp::ConfigurationError
|
335
|
+
end
|
336
|
+
|
293
337
|
end
|
294
338
|
|
295
339
|
|
metadata
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: openlogic-saml-sp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
5
|
-
prerelease:
|
4
|
+
version: 4.0.0.alpha.1
|
5
|
+
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- OpenLogic
|
9
9
|
- Peter Williams
|
10
10
|
- Glen Aultman-Bettridge
|
11
|
+
- Todd Thomas
|
11
12
|
autorequire:
|
12
13
|
bindir: bin
|
13
14
|
cert_chain: []
|
14
|
-
date:
|
15
|
+
date: 2013-04-17 00:00:00.000000000 Z
|
15
16
|
dependencies:
|
16
17
|
- !ruby/object:Gem::Dependency
|
17
18
|
name: nokogiri
|
@@ -29,6 +30,22 @@ dependencies:
|
|
29
30
|
- - ! '>='
|
30
31
|
- !ruby/object:Gem::Version
|
31
32
|
version: '0'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: signed_xml
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
none: false
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
type: :runtime
|
42
|
+
prerelease: false
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ~>
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '1.0'
|
32
49
|
- !ruby/object:Gem::Dependency
|
33
50
|
name: openlogic-resourceful
|
34
51
|
requirement: !ruby/object:Gem::Requirement
|
@@ -63,6 +80,22 @@ dependencies:
|
|
63
80
|
version: '0'
|
64
81
|
- !ruby/object:Gem::Dependency
|
65
82
|
name: rspec
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ~>
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '2.12'
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
none: false
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.12'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: fakeweb
|
66
99
|
requirement: !ruby/object:Gem::Requirement
|
67
100
|
none: false
|
68
101
|
requirements:
|
@@ -78,7 +111,9 @@ dependencies:
|
|
78
111
|
- !ruby/object:Gem::Version
|
79
112
|
version: '0'
|
80
113
|
description:
|
81
|
-
email:
|
114
|
+
email:
|
115
|
+
- gbettridge@openlogic.com
|
116
|
+
- todd.thomas@openlogic.com
|
82
117
|
executables: []
|
83
118
|
extensions: []
|
84
119
|
extra_rdoc_files:
|
@@ -94,6 +129,8 @@ files:
|
|
94
129
|
- lib/saml2.rb
|
95
130
|
- lib/saml2/artifact_resolver.rb
|
96
131
|
- lib/saml2/assertion.rb
|
132
|
+
- lib/saml2/authn_request.rb
|
133
|
+
- lib/saml2/issuer.rb
|
97
134
|
- lib/saml2/type4_artifact.rb
|
98
135
|
- lib/saml2/unexpected_type_code_error.rb
|
99
136
|
- lib/saml_sp/config.rb
|
@@ -103,7 +140,7 @@ files:
|
|
103
140
|
- spec/saml2/type4_artifact_spec.rb
|
104
141
|
- spec/saml_sp/config_spec.rb
|
105
142
|
- spec/spec_helper.rb
|
106
|
-
homepage:
|
143
|
+
homepage: https://github.com/openlogic/saml-sp
|
107
144
|
licenses: []
|
108
145
|
post_install_message:
|
109
146
|
rdoc_options: []
|
@@ -118,12 +155,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
118
155
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
156
|
none: false
|
120
157
|
requirements:
|
121
|
-
- - ! '
|
158
|
+
- - ! '>'
|
122
159
|
- !ruby/object:Gem::Version
|
123
|
-
version:
|
160
|
+
version: 1.3.1
|
124
161
|
requirements: []
|
125
162
|
rubyforge_project:
|
126
|
-
rubygems_version: 1.8.
|
163
|
+
rubygems_version: 1.8.25
|
127
164
|
signing_key:
|
128
165
|
specification_version: 3
|
129
166
|
summary: SAML 2.0 SSO Sevice Provider Library
|