saml_idp 0.9.0 → 0.14.0
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.
- checksums.yaml +4 -4
- data/README.md +39 -45
- data/lib/saml_idp.rb +2 -1
- data/lib/saml_idp/assertion_builder.rb +28 -3
- data/lib/saml_idp/configurator.rb +4 -1
- data/lib/saml_idp/controller.rb +11 -9
- data/lib/saml_idp/encryptor.rb +0 -1
- data/lib/saml_idp/fingerprint.rb +19 -0
- data/lib/saml_idp/incoming_metadata.rb +13 -0
- data/lib/saml_idp/metadata_builder.rb +23 -8
- data/lib/saml_idp/persisted_metadata.rb +4 -0
- data/lib/saml_idp/request.rb +9 -3
- data/lib/saml_idp/response_builder.rb +19 -5
- data/lib/saml_idp/saml_response.rb +37 -16
- data/lib/saml_idp/service_provider.rb +1 -6
- data/lib/saml_idp/signable.rb +1 -2
- data/lib/saml_idp/version.rb +1 -1
- data/saml_idp.gemspec +8 -8
- data/spec/lib/saml_idp/assertion_builder_spec.rb +73 -0
- data/spec/lib/saml_idp/configurator_spec.rb +1 -0
- data/spec/lib/saml_idp/controller_spec.rb +24 -0
- data/spec/lib/saml_idp/fingerprint_spec.rb +14 -0
- data/spec/lib/saml_idp/incoming_metadata_spec.rb +15 -1
- data/spec/lib/saml_idp/metadata_builder_spec.rb +23 -0
- data/spec/lib/saml_idp/response_builder_spec.rb +3 -1
- data/spec/lib/saml_idp/saml_response_spec.rb +25 -2
- data/spec/rails_app/app/controllers/saml_controller.rb +1 -5
- data/spec/rails_app/app/controllers/saml_idp_controller.rb +47 -8
- data/{app → spec/rails_app/app}/views/saml_idp/idp/new.html.erb +1 -5
- data/{app → spec/rails_app/app}/views/saml_idp/idp/saml_post.html.erb +1 -1
- data/spec/rails_app/config/environments/development.rb +2 -0
- data/spec/spec_helper.rb +20 -1
- data/spec/support/certificates/sp_cert_req.csr +12 -0
- data/spec/support/certificates/sp_private_key.pem +16 -0
- data/spec/support/certificates/sp_x509_cert.crt +18 -0
- data/spec/support/saml_request_macros.rb +62 -3
- data/spec/support/security_helpers.rb +10 -0
- metadata +51 -28
- data/app/controllers/saml_idp/idp_controller.rb +0 -59
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 847e2cafec28e67417685e6ba1173aa88ac489172e0e31fa51f2bbab37ef5d10
|
4
|
+
data.tar.gz: 8d9ace44b46770b3ca7481461bd16fce9dfc0e2b9925a4a2b5460a780cfddce3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6aa29d58babddddc81037fe220276241a355d81a15acb3639a9cc6df935f531bf1fe53e3c5599d49640080776c357ebf6348559ac034313ad64f82f80a7824a0
|
7
|
+
data.tar.gz: 9f5b83fd6459476d81c2dc2d3f0e79c401ba01474856f201c5a32615da8e22e4c416d220ef82985205f69f1d87a560a80c3bbae552a39ec4bdb49cc0a1baafa0
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Ruby SAML Identity Provider (IdP)
|
2
2
|
|
3
|
-
Forked from https://github.com/lawrencepit/ruby-saml-idp
|
3
|
+
Forked from <https://github.com/lawrencepit/ruby-saml-idp>
|
4
4
|
|
5
5
|
[](https://travis-ci.org/saml-idp/saml_idp)
|
6
6
|
[](http://badge.fury.io/rb/saml_idp)
|
@@ -13,13 +13,15 @@ protocol. It provides a means for managing authentication requests and confirmat
|
|
13
13
|
This was originally setup by @lawrencepit to test SAML Clients. I took it closer to a real
|
14
14
|
SAML IDP implementation.
|
15
15
|
|
16
|
-
|
16
|
+
## Installation and Usage
|
17
17
|
|
18
18
|
Add this to your Gemfile:
|
19
19
|
|
20
|
+
```ruby
|
20
21
|
gem 'saml_idp'
|
22
|
+
```
|
21
23
|
|
22
|
-
|
24
|
+
### Not using rails?
|
23
25
|
|
24
26
|
Include `SamlIdp::Controller` and see the examples that use rails. It should be straightforward for you.
|
25
27
|
|
@@ -27,50 +29,32 @@ Basically you call `decode_request(params[:SAMLRequest])` on an incoming request
|
|
27
29
|
`saml_acs_url` to determine the source for which you need to authenticate a user. How you authenticate
|
28
30
|
a user is entirely up to you.
|
29
31
|
|
30
|
-
Once a user has successfully authenticated on your system send the Service Provider a
|
32
|
+
Once a user has successfully authenticated on your system send the Service Provider a SAMLResponse by
|
31
33
|
posting to `saml_acs_url` the parameter `SAMLResponse` with the return value from a call to
|
32
34
|
`encode_response(user_email)`.
|
33
35
|
|
34
|
-
|
36
|
+
### Using rails?
|
35
37
|
|
36
|
-
|
38
|
+
Check out our Wiki page for Rails integration
|
39
|
+
[Rails Integration guide](https://github.com/saml-idp/saml_idp/wiki/Rails_Integration)
|
37
40
|
|
38
|
-
|
39
|
-
get '/saml/auth' => 'saml_idp#new'
|
40
|
-
get '/saml/metadata' => 'saml_idp#show'
|
41
|
-
post '/saml/auth' => 'saml_idp#create'
|
42
|
-
match '/saml/logout' => 'saml_idp#logout', via: [:get, :post, :delete]
|
43
|
-
```
|
41
|
+
### Configuration
|
44
42
|
|
45
|
-
|
43
|
+
#### Signed assertions and Signed Response
|
46
44
|
|
47
|
-
|
48
|
-
class SamlIdpController < SamlIdp::IdpController
|
49
|
-
def idp_authenticate(email, password) # not using params intentionally
|
50
|
-
user = User.by_email(email).first
|
51
|
-
user && user.valid_password?(password) ? user : nil
|
52
|
-
end
|
53
|
-
private :idp_authenticate
|
54
|
-
|
55
|
-
def idp_make_saml_response(found_user) # not using params intentionally
|
56
|
-
# NOTE encryption is optional
|
57
|
-
encode_response found_user, encryption: {
|
58
|
-
cert: saml_request.service_provider.cert,
|
59
|
-
block_encryption: 'aes256-cbc',
|
60
|
-
key_transport: 'rsa-oaep-mgf1p'
|
61
|
-
}
|
62
|
-
end
|
63
|
-
private :idp_make_saml_response
|
45
|
+
By default SAML Assertion will be signed with an algorithm which defined to `config.algorithm`. Because SAML assertions contain secure information used for authentication such as NameID.
|
64
46
|
|
65
|
-
|
66
|
-
|
67
|
-
user.logout
|
68
|
-
end
|
69
|
-
private :idp_logout
|
70
|
-
end
|
71
|
-
```
|
47
|
+
Signing SAML Response is optional, but some security perspective SP services might require Response message itself must be signed.
|
48
|
+
For that, you can enable it with `config.signed_message` option. [More about SAML spec](https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf#page=68)
|
72
49
|
|
73
|
-
|
50
|
+
#### Signing algorithm
|
51
|
+
|
52
|
+
Following algorithms you can set in your response signing algorithm
|
53
|
+
:sha1 - RSA-SHA1 default value but not recommended to production environment
|
54
|
+
Highly recommended to use one of following algorithm, suit with your computing power.
|
55
|
+
:sha256 - RSA-SHA256
|
56
|
+
:sha384 - RSA-SHA384
|
57
|
+
:sha512 - RSA-SHA512
|
74
58
|
|
75
59
|
Be sure to load a file like this during your app initialization:
|
76
60
|
|
@@ -91,16 +75,17 @@ KEY DATA
|
|
91
75
|
CERT
|
92
76
|
|
93
77
|
# config.password = "secret_key_password"
|
94
|
-
# config.algorithm = :sha256
|
78
|
+
# config.algorithm = :sha256 # Default: sha1 only for development.
|
95
79
|
# config.organization_name = "Your Organization"
|
96
80
|
# config.organization_url = "http://example.com"
|
97
81
|
# config.base_saml_location = "#{base}/saml"
|
98
|
-
# config.reference_id_generator # Default: -> {
|
82
|
+
# config.reference_id_generator # Default: -> { SecureRandom.uuid }
|
99
83
|
# config.single_logout_service_post_location = "#{base}/saml/logout"
|
100
84
|
# config.single_logout_service_redirect_location = "#{base}/saml/logout"
|
101
85
|
# config.attribute_service_location = "#{base}/saml/attributes"
|
102
86
|
# config.single_service_post_location = "#{base}/saml/auth"
|
103
87
|
# config.session_expiry = 86400 # Default: 0 which means never
|
88
|
+
# config.signed_message = true # Default: false which means unsigned SAML Response
|
104
89
|
|
105
90
|
# Principal (e.g. User) is passed in when you `encode_response`
|
106
91
|
#
|
@@ -213,7 +198,7 @@ CERT
|
|
213
198
|
end
|
214
199
|
```
|
215
200
|
|
216
|
-
|
201
|
+
## Keys and Secrets
|
217
202
|
|
218
203
|
To generate the SAML Response it uses a default X.509 certificate and secret key... which isn't so secret.
|
219
204
|
You can find them in `SamlIdp::Default`. The X.509 certificate is valid until year 2032.
|
@@ -224,22 +209,31 @@ and `SamlIdp.config.secret_key` properties.
|
|
224
209
|
|
225
210
|
The fingerprint to use, if you use the default X.509 certificate of this gem, is:
|
226
211
|
|
212
|
+
```bash
|
213
|
+
9E:65:2E:03:06:8D:80:F2:86:C7:6C:77:A1:D9:14:97:0A:4D:F4:4D
|
227
214
|
```
|
228
|
-
|
215
|
+
|
216
|
+
## Fingerprint
|
217
|
+
|
218
|
+
The gem provides an helper to generate a fingerprint for a X.509 certificate.
|
219
|
+
The second parameter is optional and default to your configuration `SamlIdp.config.algorithm`
|
220
|
+
|
221
|
+
```ruby
|
222
|
+
Fingerprint.certificate_digest(x509_cert, :sha512)
|
229
223
|
```
|
230
224
|
|
231
|
-
|
225
|
+
## Service Providers
|
232
226
|
|
233
227
|
To act as a Service Provider which generates SAML Requests and can react to SAML Responses use the
|
234
228
|
excellent [ruby-saml](https://github.com/onelogin/ruby-saml) gem.
|
235
229
|
|
236
|
-
|
230
|
+
## Author
|
237
231
|
|
238
232
|
Jon Phenow, jon@jphenow.com, jphenow.com, @jphenow
|
239
233
|
|
240
234
|
Lawrence Pit, lawrence.pit@gmail.com, lawrencepit.com, @lawrencepit
|
241
235
|
|
242
|
-
|
236
|
+
## Copyright
|
243
237
|
|
244
238
|
Copyright (c) 2012 Sport Ngin.
|
245
239
|
Portions Copyright (c) 2010 OneLogin, LLC
|
data/lib/saml_idp.rb
CHANGED
@@ -8,7 +8,8 @@ module SamlIdp
|
|
8
8
|
require 'saml_idp/default'
|
9
9
|
require 'saml_idp/metadata_builder'
|
10
10
|
require 'saml_idp/version'
|
11
|
-
require 'saml_idp/
|
11
|
+
require 'saml_idp/fingerprint'
|
12
|
+
require 'saml_idp/engine' if defined?(::Rails)
|
12
13
|
|
13
14
|
def self.config
|
14
15
|
@config ||= SamlIdp::Configurator.new
|
@@ -16,10 +16,26 @@ module SamlIdp
|
|
16
16
|
attr_accessor :expiry
|
17
17
|
attr_accessor :encryption_opts
|
18
18
|
attr_accessor :session_expiry
|
19
|
+
attr_accessor :name_id_formats_opts
|
20
|
+
attr_accessor :asserted_attributes_opts
|
19
21
|
|
20
22
|
delegate :config, to: :SamlIdp
|
21
23
|
|
22
|
-
def initialize(
|
24
|
+
def initialize(
|
25
|
+
reference_id,
|
26
|
+
issuer_uri,
|
27
|
+
principal,
|
28
|
+
audience_uri,
|
29
|
+
saml_request_id,
|
30
|
+
saml_acs_url,
|
31
|
+
raw_algorithm,
|
32
|
+
authn_context_classref,
|
33
|
+
expiry=60*60,
|
34
|
+
encryption_opts=nil,
|
35
|
+
session_expiry=nil,
|
36
|
+
name_id_formats_opts = nil,
|
37
|
+
asserted_attributes_opts = nil
|
38
|
+
)
|
23
39
|
self.reference_id = reference_id
|
24
40
|
self.issuer_uri = issuer_uri
|
25
41
|
self.principal = principal
|
@@ -31,6 +47,8 @@ module SamlIdp
|
|
31
47
|
self.expiry = expiry
|
32
48
|
self.encryption_opts = encryption_opts
|
33
49
|
self.session_expiry = session_expiry.nil? ? config.session_expiry : session_expiry
|
50
|
+
self.name_id_formats_opts = name_id_formats_opts
|
51
|
+
self.asserted_attributes_opts = asserted_attributes_opts
|
34
52
|
end
|
35
53
|
|
36
54
|
def fresh
|
@@ -98,7 +116,9 @@ module SamlIdp
|
|
98
116
|
end
|
99
117
|
|
100
118
|
def asserted_attributes
|
101
|
-
if
|
119
|
+
if asserted_attributes_opts.present? && !asserted_attributes_opts.empty?
|
120
|
+
asserted_attributes_opts
|
121
|
+
elsif principal.respond_to?(:asserted_attributes)
|
102
122
|
principal.send(:asserted_attributes)
|
103
123
|
elsif !config.attributes.nil? && !config.attributes.empty?
|
104
124
|
config.attributes
|
@@ -139,10 +159,15 @@ module SamlIdp
|
|
139
159
|
private :name_id_getter
|
140
160
|
|
141
161
|
def name_id_format
|
142
|
-
@name_id_format ||= NameIdFormatter.new(
|
162
|
+
@name_id_format ||= NameIdFormatter.new(name_id_formats).chosen
|
143
163
|
end
|
144
164
|
private :name_id_format
|
145
165
|
|
166
|
+
def name_id_formats
|
167
|
+
@name_id_formats ||= (name_id_formats_opts || config.name_id.formats)
|
168
|
+
end
|
169
|
+
private :name_id_formats
|
170
|
+
|
146
171
|
def reference_string
|
147
172
|
"_#{reference_id}"
|
148
173
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require 'ostruct'
|
3
|
+
require 'securerandom'
|
4
|
+
|
3
5
|
module SamlIdp
|
4
6
|
class Configurator
|
5
7
|
attr_accessor :x509_certificate
|
@@ -13,6 +15,7 @@ module SamlIdp
|
|
13
15
|
attr_accessor :reference_id_generator
|
14
16
|
attr_accessor :attribute_service_location
|
15
17
|
attr_accessor :single_service_post_location
|
18
|
+
attr_accessor :single_service_redirect_location
|
16
19
|
attr_accessor :single_logout_service_post_location
|
17
20
|
attr_accessor :single_logout_service_redirect_location
|
18
21
|
attr_accessor :attributes
|
@@ -24,7 +27,7 @@ module SamlIdp
|
|
24
27
|
self.x509_certificate = Default::X509_CERTIFICATE
|
25
28
|
self.secret_key = Default::SECRET_KEY
|
26
29
|
self.algorithm = :sha1
|
27
|
-
self.reference_id_generator = ->() {
|
30
|
+
self.reference_id_generator = ->() { SecureRandom.uuid }
|
28
31
|
self.service_provider = OpenStruct.new
|
29
32
|
self.service_provider.finder = ->(_) { Default::SERVICE_PROVIDER }
|
30
33
|
self.service_provider.metadata_persister = ->(id, settings) { }
|
data/lib/saml_idp/controller.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
require 'openssl'
|
3
3
|
require 'base64'
|
4
4
|
require 'time'
|
5
|
-
require '
|
5
|
+
require 'securerandom'
|
6
6
|
require 'saml_idp/request'
|
7
7
|
require 'saml_idp/logout_response_builder'
|
8
8
|
module SamlIdp
|
@@ -37,11 +37,7 @@ module SamlIdp
|
|
37
37
|
decode_request(raw_saml_request)
|
38
38
|
return true if valid_saml_request?
|
39
39
|
if defined?(::Rails)
|
40
|
-
|
41
|
-
head :forbidden
|
42
|
-
else
|
43
|
-
render nothing: true, status: :forbidden
|
44
|
-
end
|
40
|
+
head :forbidden
|
45
41
|
end
|
46
42
|
false
|
47
43
|
end
|
@@ -64,6 +60,9 @@ module SamlIdp
|
|
64
60
|
expiry = opts[:expiry] || 60*60
|
65
61
|
session_expiry = opts[:session_expiry]
|
66
62
|
encryption_opts = opts[:encryption] || nil
|
63
|
+
signed_message_opts = opts[:signed_message] || false
|
64
|
+
name_id_formats_opts = opts[:name_id_formats] || nil
|
65
|
+
asserted_attributes_opts = opts[:attributes] || nil
|
67
66
|
|
68
67
|
SamlResponse.new(
|
69
68
|
reference_id,
|
@@ -77,7 +76,10 @@ module SamlIdp
|
|
77
76
|
my_authn_context_classref,
|
78
77
|
expiry,
|
79
78
|
encryption_opts,
|
80
|
-
session_expiry
|
79
|
+
session_expiry,
|
80
|
+
signed_message_opts,
|
81
|
+
name_id_formats_opts,
|
82
|
+
asserted_attributes_opts
|
81
83
|
).build
|
82
84
|
end
|
83
85
|
|
@@ -124,11 +126,11 @@ module SamlIdp
|
|
124
126
|
end
|
125
127
|
|
126
128
|
def get_saml_response_id
|
127
|
-
|
129
|
+
SecureRandom.uuid
|
128
130
|
end
|
129
131
|
|
130
132
|
def get_saml_reference_id
|
131
|
-
|
133
|
+
SecureRandom.uuid
|
132
134
|
end
|
133
135
|
|
134
136
|
def default_algorithm
|
data/lib/saml_idp/encryptor.rb
CHANGED
@@ -61,7 +61,6 @@ module SamlIdp
|
|
61
61
|
key_info.EncryptedKey Id: 'EK', xmlns: 'http://www.w3.org/2001/04/xmlenc#' do |enc_key|
|
62
62
|
enc_key.EncryptionMethod Algorithm: key_transport_ns
|
63
63
|
enc_key.tag! 'ds:KeyInfo', 'xmlns:ds' => 'http://www.w3.org/2000/09/xmldsig#' do |key_info2|
|
64
|
-
key_info2.tag! 'ds:KeyName'
|
65
64
|
key_info2.tag! 'ds:X509Data' do |x509_data|
|
66
65
|
x509_data.tag! 'ds:X509Certificate' do |x509_cert|
|
67
66
|
x509_cert << cert.to_s.gsub(/-+(BEGIN|END) CERTIFICATE-+/, '')
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module SamlIdp
|
2
|
+
module Fingerprint
|
3
|
+
def self.certificate_digest(cert, sha_size = nil)
|
4
|
+
sha_size ||= SamlIdp.config.algorithm
|
5
|
+
digest_sha_class(sha_size).hexdigest(OpenSSL::X509::Certificate.new(cert).to_der).scan(/../).join(':')
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.digest_sha_class(sha_size)
|
9
|
+
case sha_size
|
10
|
+
when :sha256
|
11
|
+
Digest::SHA256
|
12
|
+
when :sha512
|
13
|
+
Digest::SHA512
|
14
|
+
else
|
15
|
+
raise ArgumentError, "Unsupported sha size parameter: #{sha_size}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -34,6 +34,19 @@ module SamlIdp
|
|
34
34
|
end
|
35
35
|
hashable :sign_assertions
|
36
36
|
|
37
|
+
def sign_authn_request
|
38
|
+
doc = xpath(
|
39
|
+
"//md:SPSSODescriptor",
|
40
|
+
ds: signature_namespace,
|
41
|
+
md: metadata_namespace
|
42
|
+
).first
|
43
|
+
if (doc && !doc['AuthnRequestsSigned'].nil?)
|
44
|
+
return doc['AuthnRequestsSigned'].strip.downcase == 'true'
|
45
|
+
end
|
46
|
+
return false
|
47
|
+
end
|
48
|
+
hashable :sign_authn_request
|
49
|
+
|
37
50
|
def display_name
|
38
51
|
role_descriptor_document.present? ? role_descriptor_document["ServiceDisplayName"] : ""
|
39
52
|
end
|
@@ -24,13 +24,15 @@ module SamlIdp
|
|
24
24
|
|
25
25
|
entity.IDPSSODescriptor protocolSupportEnumeration: protocol_enumeration do |descriptor|
|
26
26
|
build_key_descriptor descriptor
|
27
|
-
descriptor
|
28
|
-
|
29
|
-
|
30
|
-
|
27
|
+
build_endpoint descriptor, [
|
28
|
+
{ tag: 'SingleLogoutService', url: single_logout_service_post_location, bind: 'HTTP-POST' },
|
29
|
+
{ tag: 'SingleLogoutService', url: single_logout_service_redirect_location, bind: 'HTTP-Redirect'}
|
30
|
+
]
|
31
31
|
build_name_id_formats descriptor
|
32
|
-
descriptor
|
33
|
-
|
32
|
+
build_endpoint descriptor, [
|
33
|
+
{ tag: 'SingleSignOnService', url: single_service_post_location, bind: 'HTTP-POST' },
|
34
|
+
{ tag: 'SingleSignOnService', url: single_service_redirect_location, bind: 'HTTP-Redirect'}
|
35
|
+
]
|
34
36
|
build_attribute descriptor
|
35
37
|
end
|
36
38
|
|
@@ -38,8 +40,9 @@ module SamlIdp
|
|
38
40
|
build_key_descriptor authority_descriptor
|
39
41
|
build_organization authority_descriptor
|
40
42
|
build_contact authority_descriptor
|
41
|
-
authority_descriptor
|
42
|
-
|
43
|
+
build_endpoint authority_descriptor, [
|
44
|
+
{ tag: 'AttributeService', url: attribute_service_location, bind: 'HTTP-Redirect' }
|
45
|
+
]
|
43
46
|
build_name_id_formats authority_descriptor
|
44
47
|
build_attribute authority_descriptor
|
45
48
|
end
|
@@ -69,6 +72,17 @@ module SamlIdp
|
|
69
72
|
end
|
70
73
|
private :build_name_id_formats
|
71
74
|
|
75
|
+
def build_endpoint(el, end_points)
|
76
|
+
end_points.each do |ep|
|
77
|
+
next unless ep[:url].present?
|
78
|
+
|
79
|
+
el.tag! ep[:tag],
|
80
|
+
Binding: "urn:oasis:names:tc:SAML:2.0:bindings:#{ep[:bind]}",
|
81
|
+
Location: ep[:url]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
private :build_endpoint
|
85
|
+
|
72
86
|
def build_attribute(el)
|
73
87
|
attributes.each do |attribute|
|
74
88
|
el.tag! "saml:Attribute",
|
@@ -151,6 +165,7 @@ module SamlIdp
|
|
151
165
|
organization_url
|
152
166
|
attribute_service_location
|
153
167
|
single_service_post_location
|
168
|
+
single_service_redirect_location
|
154
169
|
single_logout_service_post_location
|
155
170
|
single_logout_service_redirect_location
|
156
171
|
technical_contact
|