libsaml 2.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +91 -0
- data/Rakefile +33 -0
- data/lib/saml.rb +142 -0
- data/lib/saml/artifact.rb +51 -0
- data/lib/saml/artifact_resolve.rb +10 -0
- data/lib/saml/artifact_response.rb +9 -0
- data/lib/saml/assertion.rb +67 -0
- data/lib/saml/authn_request.rb +34 -0
- data/lib/saml/base.rb +47 -0
- data/lib/saml/bindings/http_artifact.rb +44 -0
- data/lib/saml/bindings/http_post.rb +29 -0
- data/lib/saml/bindings/http_redirect.rb +100 -0
- data/lib/saml/bindings/soap.rb +31 -0
- data/lib/saml/complex_types/endpoint_type.rb +17 -0
- data/lib/saml/complex_types/indexed_endpoint_type.rb +15 -0
- data/lib/saml/complex_types/request_abstract_type.rb +57 -0
- data/lib/saml/complex_types/sso_descriptor_type.rb +48 -0
- data/lib/saml/complex_types/status_response_type.rb +29 -0
- data/lib/saml/config.rb +49 -0
- data/lib/saml/elements/attribute.rb +24 -0
- data/lib/saml/elements/attribute_statement.rb +26 -0
- data/lib/saml/elements/audience_restriction.rb +12 -0
- data/lib/saml/elements/authn_context.rb +13 -0
- data/lib/saml/elements/authn_statement.rb +25 -0
- data/lib/saml/elements/conditions.rb +24 -0
- data/lib/saml/elements/contact_person.rb +33 -0
- data/lib/saml/elements/entities_descriptor.rb +27 -0
- data/lib/saml/elements/entity_descriptor.rb +37 -0
- data/lib/saml/elements/idp_sso_descriptor.rb +23 -0
- data/lib/saml/elements/key_descriptor.rb +34 -0
- data/lib/saml/elements/key_descriptor/key_info.rb +30 -0
- data/lib/saml/elements/key_descriptor/key_info/x509_data.rb +34 -0
- data/lib/saml/elements/name_id.rb +14 -0
- data/lib/saml/elements/organization.rb +16 -0
- data/lib/saml/elements/requested_authn_context.rb +28 -0
- data/lib/saml/elements/signature.rb +33 -0
- data/lib/saml/elements/signature/canonicalization_method.rb +19 -0
- data/lib/saml/elements/signature/digest_method.rb +19 -0
- data/lib/saml/elements/signature/inclusive_namespaces.rb +20 -0
- data/lib/saml/elements/signature/key_info.rb +14 -0
- data/lib/saml/elements/signature/reference.rb +23 -0
- data/lib/saml/elements/signature/signature_method.rb +19 -0
- data/lib/saml/elements/signature/signed_info.rb +24 -0
- data/lib/saml/elements/signature/transform.rb +19 -0
- data/lib/saml/elements/signature/transforms.rb +21 -0
- data/lib/saml/elements/sp_sso_descriptor.rb +27 -0
- data/lib/saml/elements/status.rb +15 -0
- data/lib/saml/elements/status_code.rb +42 -0
- data/lib/saml/elements/sub_status_code.rb +14 -0
- data/lib/saml/elements/subject.rb +38 -0
- data/lib/saml/elements/subject_confirmation.rb +30 -0
- data/lib/saml/elements/subject_confirmation_data.rb +23 -0
- data/lib/saml/elements/subject_locality.rb +12 -0
- data/lib/saml/encoding.rb +35 -0
- data/lib/saml/logout_request.rb +10 -0
- data/lib/saml/logout_response.rb +11 -0
- data/lib/saml/provider.rb +85 -0
- data/lib/saml/provider_stores/file.rb +33 -0
- data/lib/saml/response.rb +21 -0
- data/lib/saml/util.rb +51 -0
- data/lib/saml/version.rb +3 -0
- data/lib/saml/xml_helpers.rb +34 -0
- data/lib/tasks/saml_tasks.rake +4 -0
- metadata +195 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NzdjMGJhZTcxYTc2MTUxMmNlMTdhNzEzNDlmOGI4ODEyZDQxMTBhNg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NTY3YjkxMDI3OTQyNjUyYTViZDc5MTAxZTBjYzM4ZjllOGVkYzM0Mg==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NDcwYjA1YTZmZDcyZGVhZmVmNDlhMjhmMWQ1ZDRiNGIzNzU0YzIzMTE2Y2U5
|
10
|
+
ZmUxYmUyYTBkNGVkN2ZhMTZlYjNjZjgzNjE2ZmZmOTIwMzIyNzA0MWQxMjdh
|
11
|
+
M2FkY2NjODEyYjdkMTIwNzY5NzVmYmI3MmJhZTIyMzdhZTM2MTE=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ODdjOTk5ZjJhZWZjMmFiN2M3NWFlYzA4N2Y3Njk2OWUxYTk3YzFmMGZjNTZj
|
14
|
+
Y2YwYzcwYzI2YWM0YzNhZjc4NzE4Y2UzYTRjZjBhMzZmNjQ4NjgzNWY4NDU2
|
15
|
+
Y2ZjODYxNWE4YTBkMDhmNjY5ZDJjZjQ1NGIzN2E2NzM4ZDJhNWY=
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2013 Digidentity
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
{<img src="https://travis-ci.org/digidentity/libsaml.png?branch=master" alt="Build Status" />}[https://travis-ci.org/digidentity/libsaml]
|
2
|
+
= libsaml
|
3
|
+
Libsaml is a Ruby gem to easily create SAML 2.0 messages. This gem was written because other SAML gems were missing functionality such as XML signing.
|
4
|
+
|
5
|
+
Libsaml's features include:
|
6
|
+
- Bindings: HTTP-Post, HTTP-Redirect, HTTP-Artifact, SOAP
|
7
|
+
- XML signing and verification
|
8
|
+
- Pluggable backend for providers (FileStore backend included)
|
9
|
+
|
10
|
+
Copyright Digidentity BV, released under the MIT license. This gem was written by Benoist Claassen.
|
11
|
+
|
12
|
+
= Installation
|
13
|
+
|
14
|
+
Place in your Gemfile:
|
15
|
+
gem 'libsaml', require: 'saml'
|
16
|
+
|
17
|
+
= Usage
|
18
|
+
Below follows how to configure the SAML gem in a service provider.
|
19
|
+
|
20
|
+
Store the private key in:
|
21
|
+
config/ssl/key.pem
|
22
|
+
|
23
|
+
Store the public key of the identity provider in:
|
24
|
+
config/ssl/trust-federate.cert
|
25
|
+
|
26
|
+
Add the Identity Provider web container configuration file to config/metadata/service_provider.xml.
|
27
|
+
This contains an encoded version of the public key, generate this in the ruby console by typing:
|
28
|
+
irb
|
29
|
+
require 'openssl'
|
30
|
+
require 'base64'
|
31
|
+
pem = File.open("config/ssl/trust-federate.cert").read
|
32
|
+
cert = OpenSSL::X509::Certificate.new(pem)
|
33
|
+
output = Base64.encode64(cert.to_der).gsub("\n", "")
|
34
|
+
|
35
|
+
Add the Service Provider configuration file to config/metadata/service_provider.xml:
|
36
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
37
|
+
<md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" ID="_052c51476c9560a429e1171e8c9528b96b69fb57" entityID="my:very:original:entityid">
|
38
|
+
<md:SPSSODescriptor>
|
39
|
+
<md:KeyDescriptor use="signing">
|
40
|
+
<ds:KeyInfo>
|
41
|
+
<ds:X509Data>
|
42
|
+
<ds:X509Certificate>SAME_KEY_AS_GENERATED_IN_THE_CONSOLE_BEFORE</ds:X509Certificate>
|
43
|
+
</ds:X509Data>
|
44
|
+
</ds:KeyInfo>
|
45
|
+
</md:KeyDescriptor>
|
46
|
+
<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Post" index="0" Location="http://localhost:3000/saml/receive_response" isDefault="true"/>
|
47
|
+
</md:SPSSODescriptor>
|
48
|
+
</md:EntityDescriptor>
|
49
|
+
|
50
|
+
Set up an intializer in config/initializers/saml_config.rb:
|
51
|
+
Saml.setup do |config|
|
52
|
+
config.entity_id = "my:very:original:entityid"
|
53
|
+
config.provider_store = Saml::ProviderStore::File.new("config/metadata", "config/ssl/key.pem")
|
54
|
+
# config.provider_store = SamlProvider
|
55
|
+
end
|
56
|
+
|
57
|
+
By default this will use a SamlProvider model that uses the filestore, if you want a database driven model comment out the #provider_store function in the initializer and make a model that defines #find_by_entity_id:
|
58
|
+
class SamlProvider < ActiveRecord::Base
|
59
|
+
include Saml::Provider
|
60
|
+
|
61
|
+
def self.find_by_entity_id(entity_id)
|
62
|
+
where(entity_id: entity_id).first!
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
Now you can make a SAML controller in app/controllers/saml_controller.rb:
|
68
|
+
class SamlController < ApplicationController
|
69
|
+
def request_authentication
|
70
|
+
provider = Saml.provider("my:very:original:entityid")
|
71
|
+
destination = provider.single_sign_on_service_url(Saml::ProtocolBindings::HTTP_POST)
|
72
|
+
|
73
|
+
authn_request = Saml::AuthnRequest.new(:destination => destination)
|
74
|
+
|
75
|
+
@saml_attributes = Saml::Bindings::HTTPPost.create_form_attributes(authn_request)
|
76
|
+
|
77
|
+
render text: @saml_attributes.to_yaml
|
78
|
+
end
|
79
|
+
|
80
|
+
def receive_response
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
Don't forget to define the routes in config/routes.rb:
|
85
|
+
get "/saml/request_authentication" => "saml#request_authentication"
|
86
|
+
get "/saml/receive_response" => "saml#receive_response"
|
87
|
+
|
88
|
+
= Contributing
|
89
|
+
- Fork the project
|
90
|
+
- Contribute your changes. Please make sure your changes are properly documented and covered by tests.
|
91
|
+
- Send a pull request
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'Saml'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
require "rspec/core/rake_task"
|
24
|
+
|
25
|
+
RSpec::Core::RakeTask.new(:core) do |spec|
|
26
|
+
spec.rspec_opts = ['--backtrace']
|
27
|
+
end
|
28
|
+
|
29
|
+
task :default => [:core]
|
30
|
+
|
31
|
+
|
32
|
+
Bundler::GemHelper.install_tasks
|
33
|
+
|
data/lib/saml.rb
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'active_support/all'
|
2
|
+
require 'active_model'
|
3
|
+
require 'saml/base'
|
4
|
+
require 'saml/xml_helpers'
|
5
|
+
require 'saml/encoding'
|
6
|
+
require 'saml/util'
|
7
|
+
require 'xmldsig'
|
8
|
+
require 'httpi'
|
9
|
+
|
10
|
+
module Saml
|
11
|
+
MD_NAMESPACE = 'urn:oasis:names:tc:SAML:2.0:metadata'
|
12
|
+
SAML_NAMESPACE = 'urn:oasis:names:tc:SAML:2.0:assertion'
|
13
|
+
SAMLP_NAMESPACE = 'urn:oasis:names:tc:SAML:2.0:protocol'
|
14
|
+
XML_DSIG_NAMESPACE = 'http://www.w3.org/2000/09/xmldsig#'
|
15
|
+
SAML_VERSION = '2.0'
|
16
|
+
|
17
|
+
module Errors
|
18
|
+
class SamlError < StandardError;
|
19
|
+
end
|
20
|
+
|
21
|
+
class SignatureInvalid < SamlError;
|
22
|
+
end
|
23
|
+
class InvalidProvider < SamlError;
|
24
|
+
end
|
25
|
+
class UnparseableMessage < SamlError;
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module TopLevelCodes
|
30
|
+
SUCCESS = 'urn:oasis:names:tc:SAML:2.0:status:Success'
|
31
|
+
REQUESTER = 'urn:oasis:names:tc:SAML:2.0:status:Requester'
|
32
|
+
RESPONDER = 'urn:oasis:names:tc:SAML:2.0:status:Responder'
|
33
|
+
VERSION_MISMATCH = 'urn:oasis:names:tc:SAML:2.0:status:VersionMismatch'
|
34
|
+
|
35
|
+
ALL = [SUCCESS, REQUESTER, RESPONDER, VERSION_MISMATCH]
|
36
|
+
end
|
37
|
+
|
38
|
+
module SubStatusCodes
|
39
|
+
AUTHN_FAILED = 'urn:oasis:names:tc:SAML:2.0:status:AuthnFailed'
|
40
|
+
NO_AUTHN_CONTEXT = 'urn:oasis:names:tc:SAML:2.0:status:NoAuthnContext'
|
41
|
+
PARTIAL_LOGOUT = 'urn:oasis:names:tc:SAML:2.0:status:PartialLogout'
|
42
|
+
REQUEST_DENIED = 'urn:oasis:names:tc:SAML:2.0:status:RequestDenied'
|
43
|
+
|
44
|
+
ALL = [AUTHN_FAILED, NO_AUTHN_CONTEXT, PARTIAL_LOGOUT, REQUEST_DENIED]
|
45
|
+
end
|
46
|
+
|
47
|
+
module Bindings
|
48
|
+
require 'saml/bindings/http_artifact'
|
49
|
+
require 'saml/bindings/http_redirect'
|
50
|
+
require 'saml/bindings/http_post'
|
51
|
+
require 'saml/bindings/soap'
|
52
|
+
end
|
53
|
+
|
54
|
+
module ClassRefs
|
55
|
+
PASSWORD_PROTECTED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'
|
56
|
+
MOBILE_TWO_FACTOR_CONTRACT = 'urn:oasis:names:tc:SAML:2.0:ac:classes:MobileTwoFactorContract'
|
57
|
+
MOBILE_SMARTCARD_PKI = 'urn:oasis:names:tc:SAML:2.0:ac:classes:SmartcardPKI'
|
58
|
+
|
59
|
+
ALL_CLASS_REFS = [PASSWORD_PROTECTED,
|
60
|
+
MOBILE_TWO_FACTOR_CONTRACT,
|
61
|
+
MOBILE_SMARTCARD_PKI]
|
62
|
+
ORDERED_CLASS_REFS = ALL_CLASS_REFS
|
63
|
+
end
|
64
|
+
|
65
|
+
module ComplexTypes
|
66
|
+
require 'saml/complex_types/request_abstract_type'
|
67
|
+
require 'saml/complex_types/status_response_type'
|
68
|
+
require 'saml/complex_types/endpoint_type'
|
69
|
+
require 'saml/complex_types/indexed_endpoint_type'
|
70
|
+
require 'saml/complex_types/sso_descriptor_type'
|
71
|
+
end
|
72
|
+
|
73
|
+
module Elements
|
74
|
+
require 'saml/elements/signature'
|
75
|
+
require 'saml/elements/subject_locality'
|
76
|
+
require 'saml/elements/authn_context'
|
77
|
+
require 'saml/elements/audience_restriction'
|
78
|
+
require 'saml/elements/sub_status_code'
|
79
|
+
require 'saml/elements/status_code'
|
80
|
+
require 'saml/elements/status'
|
81
|
+
require 'saml/elements/subject_confirmation_data'
|
82
|
+
require 'saml/elements/subject_confirmation'
|
83
|
+
require 'saml/elements/attribute'
|
84
|
+
require 'saml/elements/attribute_statement'
|
85
|
+
require 'saml/elements/name_id'
|
86
|
+
require 'saml/elements/subject'
|
87
|
+
require 'saml/elements/conditions'
|
88
|
+
require 'saml/elements/authn_statement'
|
89
|
+
require 'saml/elements/requested_authn_context'
|
90
|
+
require 'saml/elements/key_descriptor'
|
91
|
+
require 'saml/elements/organization'
|
92
|
+
require 'saml/elements/contact_person'
|
93
|
+
require 'saml/elements/idp_sso_descriptor'
|
94
|
+
require 'saml/elements/sp_sso_descriptor'
|
95
|
+
require 'saml/elements/entity_descriptor'
|
96
|
+
require 'saml/elements/entities_descriptor'
|
97
|
+
end
|
98
|
+
|
99
|
+
require 'saml/assertion'
|
100
|
+
require 'saml/authn_request'
|
101
|
+
require 'saml/artifact'
|
102
|
+
require 'saml/response'
|
103
|
+
require 'saml/artifact_resolve'
|
104
|
+
require 'saml/artifact_response'
|
105
|
+
require 'saml/logout_request'
|
106
|
+
require 'saml/logout_response'
|
107
|
+
require 'saml/provider'
|
108
|
+
|
109
|
+
module ProviderStores
|
110
|
+
require 'saml/provider_stores/file'
|
111
|
+
end
|
112
|
+
|
113
|
+
module ProtocolBinding
|
114
|
+
HTTP_ARTIFACT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact'
|
115
|
+
HTTP_POST = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
|
116
|
+
HTTP_REDIRECT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
|
117
|
+
SOAP = 'urn:oasis:names:tc:SAML:2.0:bindings:SOAP'
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.setup
|
121
|
+
yield Saml::Config
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.generate_id
|
125
|
+
"_#{::SecureRandom.hex(20)}"
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.provider(entity_id)
|
129
|
+
Saml::Config.provider_store.find_by_entity_id(entity_id) || raise(Saml::Errors::InvalidProvider.new)
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.parse_message(message, type)
|
133
|
+
if %w(authn_request response logout_request logout_response artifact_resolve artifact_response).include?(type.to_s)
|
134
|
+
klass = "Saml::#{type.to_s.camelize}".constantize
|
135
|
+
klass.parse(message, single: true)
|
136
|
+
else
|
137
|
+
nil
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
require 'saml/config'
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Saml
|
2
|
+
class Artifact
|
3
|
+
include ::HappyMapper
|
4
|
+
|
5
|
+
TYPE_CODE = "\000\004"
|
6
|
+
END_POINT_INDEX = "\000\000"
|
7
|
+
|
8
|
+
tag "Artifact"
|
9
|
+
namespace 'samlp'
|
10
|
+
|
11
|
+
content :artifact, String
|
12
|
+
|
13
|
+
def initialize(artifact = nil)
|
14
|
+
if artifact
|
15
|
+
@artifact = artifact
|
16
|
+
else
|
17
|
+
source_id = ::Digest::SHA1.digest(Saml::Config.entity_id)
|
18
|
+
message_handle = ::SecureRandom.random_bytes(20)
|
19
|
+
@type_code = TYPE_CODE
|
20
|
+
@endpoint_index = END_POINT_INDEX
|
21
|
+
@artifact = Saml::Encoding.encode_64 [@type_code, @endpoint_index, source_id, message_handle].join
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def type_code
|
26
|
+
decoded_value[0, 2]
|
27
|
+
end
|
28
|
+
|
29
|
+
def endpoint_index
|
30
|
+
decoded_value[2, 2]
|
31
|
+
end
|
32
|
+
|
33
|
+
def source_id
|
34
|
+
decoded_value[4, 20]
|
35
|
+
end
|
36
|
+
|
37
|
+
def message_handle
|
38
|
+
decoded_value[24, 20]
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
artifact
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def decoded_value
|
48
|
+
::Base64.decode64(artifact)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Saml
|
2
|
+
class Assertion
|
3
|
+
include Saml::Base
|
4
|
+
include Saml::XMLHelpers
|
5
|
+
|
6
|
+
register_namespace 'samlp', Saml::SAMLP_NAMESPACE
|
7
|
+
register_namespace 'saml', Saml::SAML_NAMESPACE
|
8
|
+
|
9
|
+
tag "Assertion"
|
10
|
+
namespace 'saml'
|
11
|
+
|
12
|
+
attribute :_id, String, :tag => 'ID'
|
13
|
+
attribute :version, String, :tag => "Version"
|
14
|
+
attribute :issue_instant, Time, :tag => "IssueInstant", :on_save => lambda { |val| val.utc.xmlschema }
|
15
|
+
|
16
|
+
element :issuer, String, :namespace => 'saml', :tag => "Issuer"
|
17
|
+
|
18
|
+
has_one :signature, Saml::Elements::Signature
|
19
|
+
has_one :subject, Saml::Elements::Subject
|
20
|
+
has_one :conditions, Saml::Elements::Conditions
|
21
|
+
has_many :authn_statement, Saml::Elements::AuthnStatement
|
22
|
+
has_one :attribute_statement, Saml::Elements::AttributeStatement
|
23
|
+
|
24
|
+
validates :_id, :version, :issue_instant, :issuer, :presence => true
|
25
|
+
|
26
|
+
validates :version, inclusion: %w(2.0)
|
27
|
+
validate :check_issue_instant, :if => "issue_instant.present?"
|
28
|
+
|
29
|
+
def initialize(*args)
|
30
|
+
options = args.extract_options!
|
31
|
+
@subject = Saml::Elements::Subject.new(:name_id => options.delete(:name_id),
|
32
|
+
:name_id_format => options.delete(:name_id_format),
|
33
|
+
:recipient => options.delete(:recipient),
|
34
|
+
:in_response_to => options.delete(:in_response_to))
|
35
|
+
@conditions = Saml::Elements::Conditions.new(:audience => options.delete(:audience))
|
36
|
+
@authn_statement = Saml::Elements::AuthnStatement.new(:authn_instant => Time.now,
|
37
|
+
:address => options.delete(:address),
|
38
|
+
:authn_context_class_ref => options.delete(:authn_context_class_ref),
|
39
|
+
:session_index => options.delete(:session_index))
|
40
|
+
super(*(args << options))
|
41
|
+
@_id ||= Saml.generate_id
|
42
|
+
@issue_instant ||= Time.now
|
43
|
+
@issuer ||= Saml::Config.entity_id
|
44
|
+
@version ||= Saml::SAML_VERSION
|
45
|
+
end
|
46
|
+
|
47
|
+
def add_attribute(key, value)
|
48
|
+
self.attribute_statement ||= Saml::Elements::AttributeStatement.new
|
49
|
+
self.attribute_statement.attribute ||= []
|
50
|
+
self.attribute_statement.attribute << Saml::Elements::Attribute.new(name: key, attribute_value: value)
|
51
|
+
end
|
52
|
+
|
53
|
+
def fetch_attribute(key)
|
54
|
+
return unless self.attribute_statement
|
55
|
+
return unless self.attribute_statement.attribute
|
56
|
+
attribute_statement.fetch_attribute(key)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def check_issue_instant
|
62
|
+
errors.add(:issue_instant, :too_old) if issue_instant < Time.now - Saml::Config.max_issue_instant_offset.minutes
|
63
|
+
errors.add(:issue_instant, :too_new) if issue_instant > Time.now + Saml::Config.max_issue_instant_offset.minutes
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|