saml_idp_metadata 0.3.8 → 1.0.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/.circleci/config.yml +0 -1
- data/.github/workflows/ruby.yml +0 -1
- data/.rubocop_todo.yml +23 -3
- data/CHANGELOG.md +14 -0
- data/Gemfile.lock +13 -39
- data/lib/saml_idp_metadata/parser.rb +110 -42
- data/lib/saml_idp_metadata/version.rb +1 -1
- data/saml_idp_metadata.gemspec +1 -2
- metadata +3 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bda49c64b9043ebd4435d5465fd6d1b64463f9082723d8cbb22a930870d9770e
|
4
|
+
data.tar.gz: a4a9e8ec42fcfad075be611070eaf887e1e7d679c0165eac19be8141f4683775
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed85b896eb68fe0bf0eb37d2fa5814af5243dd7a91a84f1a85e7ec998b00f7a6eed996d6e82b442a1a4db950e18323c0cee07dca99a45caebf43e4981b000c6e
|
7
|
+
data.tar.gz: 6efd585b8266f52ad93a66578792dc6fdceb7d64d511fd196ff8642694ac1edce244199af302e79f129f8bec1ba22652de093b5378cbc2daa3c2a46c52131118
|
data/.circleci/config.yml
CHANGED
data/.github/workflows/ruby.yml
CHANGED
data/.rubocop_todo.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on 2025-03-29
|
3
|
+
# on 2025-03-29 09:03:43 UTC using RuboCop version 1.75.1.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
@@ -11,9 +11,29 @@
|
|
11
11
|
Metrics/AbcSize:
|
12
12
|
Max: 21
|
13
13
|
|
14
|
-
# Offense count:
|
14
|
+
# Offense count: 1
|
15
|
+
# Configuration parameters: CountComments, CountAsOne.
|
16
|
+
Metrics/ClassLength:
|
17
|
+
Max: 139
|
18
|
+
|
19
|
+
# Offense count: 1
|
20
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
21
|
+
Metrics/CyclomaticComplexity:
|
22
|
+
Max: 9
|
23
|
+
|
24
|
+
# Offense count: 1
|
25
|
+
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
26
|
+
Metrics/MethodLength:
|
27
|
+
Max: 12
|
28
|
+
|
29
|
+
# Offense count: 1
|
30
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns.
|
31
|
+
Metrics/PerceivedComplexity:
|
32
|
+
Max: 9
|
33
|
+
|
34
|
+
# Offense count: 1
|
15
35
|
# This cop supports safe autocorrection (--autocorrect).
|
16
36
|
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
|
17
37
|
# URISchemes: http, https
|
18
38
|
Layout/LineLength:
|
19
|
-
Max:
|
39
|
+
Max: 121
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [v0.3.8](https://github.com/tknzk/saml_idp_metadata/tree/v0.3.8) (2025-03-29)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/tknzk/saml_idp_metadata/compare/v0.3.7...v0.3.8)
|
6
|
+
|
7
|
+
**Merged pull requests:**
|
8
|
+
|
9
|
+
- bumpup [\#235](https://github.com/tknzk/saml_idp_metadata/pull/235) ([tknzk](https://github.com/tknzk))
|
10
|
+
- Obey rubocop [\#234](https://github.com/tknzk/saml_idp_metadata/pull/234) ([tknzk](https://github.com/tknzk))
|
11
|
+
- bundle update [\#233](https://github.com/tknzk/saml_idp_metadata/pull/233) ([tknzk](https://github.com/tknzk))
|
12
|
+
- refactor github actions [\#232](https://github.com/tknzk/saml_idp_metadata/pull/232) ([tknzk](https://github.com/tknzk))
|
13
|
+
- refactor circleci config [\#231](https://github.com/tknzk/saml_idp_metadata/pull/231) ([tknzk](https://github.com/tknzk))
|
14
|
+
- require ruby \>= 3.1 [\#230](https://github.com/tknzk/saml_idp_metadata/pull/230) ([tknzk](https://github.com/tknzk))
|
15
|
+
- bundle update [\#229](https://github.com/tknzk/saml_idp_metadata/pull/229) ([tknzk](https://github.com/tknzk))
|
16
|
+
|
3
17
|
## [v0.3.7](https://github.com/tknzk/saml_idp_metadata/tree/v0.3.7) (2025-03-21)
|
4
18
|
|
5
19
|
[Full Changelog](https://github.com/tknzk/saml_idp_metadata/compare/v0.3.6...v0.3.7)
|
data/Gemfile.lock
CHANGED
@@ -1,45 +1,22 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
saml_idp_metadata (0.
|
5
|
-
activesupport (< 8)
|
4
|
+
saml_idp_metadata (1.0.0)
|
6
5
|
rexml
|
7
6
|
|
8
7
|
GEM
|
9
8
|
remote: https://rubygems.org/
|
10
9
|
specs:
|
11
|
-
activesupport (7.2.2.1)
|
12
|
-
base64
|
13
|
-
benchmark (>= 0.3)
|
14
|
-
bigdecimal
|
15
|
-
concurrent-ruby (~> 1.0, >= 1.3.1)
|
16
|
-
connection_pool (>= 2.2.5)
|
17
|
-
drb
|
18
|
-
i18n (>= 1.6, < 2)
|
19
|
-
logger (>= 1.4.2)
|
20
|
-
minitest (>= 5.1)
|
21
|
-
securerandom (>= 0.3)
|
22
|
-
tzinfo (~> 2.0, >= 2.0.5)
|
23
10
|
ast (2.4.3)
|
24
|
-
base64 (0.2.0)
|
25
|
-
benchmark (0.4.0)
|
26
|
-
bigdecimal (3.1.9)
|
27
11
|
coderay (1.1.3)
|
28
|
-
|
29
|
-
connection_pool (2.5.0)
|
30
|
-
diff-lcs (1.6.1)
|
12
|
+
diff-lcs (1.6.2)
|
31
13
|
docile (1.4.1)
|
32
|
-
|
33
|
-
|
34
|
-
concurrent-ruby (~> 1.0)
|
35
|
-
json (2.10.2)
|
36
|
-
language_server-protocol (3.17.0.4)
|
14
|
+
json (2.12.2)
|
15
|
+
language_server-protocol (3.17.0.5)
|
37
16
|
lint_roller (1.1.0)
|
38
|
-
logger (1.7.0)
|
39
17
|
method_source (1.1.0)
|
40
|
-
|
41
|
-
|
42
|
-
parser (3.3.7.3)
|
18
|
+
parallel (1.27.0)
|
19
|
+
parser (3.3.8.0)
|
43
20
|
ast (~> 2.4.1)
|
44
21
|
racc
|
45
22
|
prism (1.4.0)
|
@@ -57,14 +34,14 @@ GEM
|
|
57
34
|
rspec-mocks (~> 3.13.0)
|
58
35
|
rspec-core (3.13.3)
|
59
36
|
rspec-support (~> 3.13.0)
|
60
|
-
rspec-expectations (3.13.
|
37
|
+
rspec-expectations (3.13.4)
|
61
38
|
diff-lcs (>= 1.2.0, < 2.0)
|
62
39
|
rspec-support (~> 3.13.0)
|
63
|
-
rspec-mocks (3.13.
|
40
|
+
rspec-mocks (3.13.4)
|
64
41
|
diff-lcs (>= 1.2.0, < 2.0)
|
65
42
|
rspec-support (~> 3.13.0)
|
66
|
-
rspec-support (3.13.
|
67
|
-
rubocop (1.75.
|
43
|
+
rspec-support (3.13.3)
|
44
|
+
rubocop (1.75.7)
|
68
45
|
json (~> 2.3)
|
69
46
|
language_server-protocol (~> 3.17.0.2)
|
70
47
|
lint_roller (~> 1.1.0)
|
@@ -72,28 +49,25 @@ GEM
|
|
72
49
|
parser (>= 3.3.0.2)
|
73
50
|
rainbow (>= 2.2.2, < 4.0)
|
74
51
|
regexp_parser (>= 2.9.3, < 3.0)
|
75
|
-
rubocop-ast (>= 1.
|
52
|
+
rubocop-ast (>= 1.44.0, < 2.0)
|
76
53
|
ruby-progressbar (~> 1.7)
|
77
54
|
unicode-display_width (>= 2.4.0, < 4.0)
|
78
|
-
rubocop-ast (1.
|
55
|
+
rubocop-ast (1.44.1)
|
79
56
|
parser (>= 3.3.7.2)
|
80
57
|
prism (~> 1.4)
|
81
58
|
rubocop-rake (0.7.1)
|
82
59
|
lint_roller (~> 1.1)
|
83
60
|
rubocop (>= 1.72.1)
|
84
|
-
rubocop-rspec (3.
|
61
|
+
rubocop-rspec (3.6.0)
|
85
62
|
lint_roller (~> 1.1)
|
86
63
|
rubocop (~> 1.72, >= 1.72.1)
|
87
64
|
ruby-progressbar (1.13.0)
|
88
|
-
securerandom (0.4.1)
|
89
65
|
simplecov (0.22.0)
|
90
66
|
docile (~> 1.1)
|
91
67
|
simplecov-html (~> 0.11)
|
92
68
|
simplecov_json_formatter (~> 0.1)
|
93
69
|
simplecov-html (0.13.1)
|
94
70
|
simplecov_json_formatter (0.1.4)
|
95
|
-
tzinfo (2.0.6)
|
96
|
-
concurrent-ruby (~> 1.0)
|
97
71
|
unicode-display_width (3.1.4)
|
98
72
|
unicode-emoji (~> 4.0, >= 4.0.4)
|
99
73
|
unicode-emoji (4.0.4)
|
@@ -1,11 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
4
|
-
require 'active_support/core_ext'
|
3
|
+
require 'rexml/document'
|
5
4
|
|
6
5
|
module SamlIdpMetadata
|
7
6
|
#
|
8
|
-
# SAML IdP metadata parser
|
7
|
+
# SAML IdP metadata parser with REXML
|
9
8
|
#
|
10
9
|
class Parser
|
11
10
|
attr_reader :xml, :xmlns, :entity_id, :sso_http_redirect_url, :sso_http_post_url, :slo_url, :nameid_format,
|
@@ -13,7 +12,7 @@ module SamlIdpMetadata
|
|
13
12
|
|
14
13
|
def initialize(xml:)
|
15
14
|
@xml = xml
|
16
|
-
@
|
15
|
+
@doc = REXML::Document.new(xml)
|
17
16
|
|
18
17
|
@xmlns = nil
|
19
18
|
@entity_id = nil
|
@@ -30,10 +29,9 @@ module SamlIdpMetadata
|
|
30
29
|
|
31
30
|
def call
|
32
31
|
@xmlns = parse_xmlns
|
33
|
-
|
34
32
|
@entity_id = parse_entity_id
|
35
33
|
@sso_http_redirect_url = parse_sso_http_redirect_url
|
36
|
-
@sso_http_post_url = parse_sso_http_post_url
|
34
|
+
@sso_http_post_url = parse_sso_http_post_url || parse_sso_http_redirect_url
|
37
35
|
@slo_url = parse_slo_url
|
38
36
|
@nameid_format = parse_nameid_format
|
39
37
|
@x509_certificate = parse_x509_certificate
|
@@ -46,7 +44,7 @@ module SamlIdpMetadata
|
|
46
44
|
end
|
47
45
|
|
48
46
|
def ensure_params?
|
49
|
-
|
47
|
+
present?(entity_id) && present?(sso_http_redirect_url) && present?(sso_http_post_url) && present?(x509_certificate)
|
50
48
|
end
|
51
49
|
|
52
50
|
def build_params
|
@@ -64,78 +62,148 @@ module SamlIdpMetadata
|
|
64
62
|
private
|
65
63
|
|
66
64
|
def entity_descriptor
|
67
|
-
|
68
|
-
|
65
|
+
# Handle EntitiesDescriptor case
|
66
|
+
if @doc.root.name == 'EntitiesDescriptor'
|
67
|
+
find_with_namespace(@doc.root.elements, 'EntityDescriptor')
|
69
68
|
else
|
70
|
-
@
|
69
|
+
@doc.root
|
71
70
|
end
|
72
71
|
end
|
73
72
|
|
74
73
|
def parse_entity_id
|
75
|
-
entity_descriptor['entityID'
|
74
|
+
entity_descriptor&.attributes&.[]('entityID')
|
76
75
|
end
|
77
76
|
|
78
77
|
def parse_xmlns
|
79
|
-
entity_descriptor
|
78
|
+
if entity_descriptor&.attributes&.[]('xmlns:md')
|
79
|
+
entity_descriptor.attributes['xmlns:md']
|
80
|
+
else
|
81
|
+
entity_descriptor&.attributes&.[]('xmlns')
|
82
|
+
end
|
80
83
|
end
|
81
84
|
|
82
|
-
def
|
83
|
-
|
85
|
+
def idp_descriptor
|
86
|
+
find_with_namespace(entity_descriptor&.elements, 'IDPSSODescriptor')
|
87
|
+
end
|
84
88
|
|
85
|
-
|
89
|
+
def parse_sso_http_redirect_url
|
90
|
+
services = find_all_with_namespace(idp_descriptor&.elements, 'SingleSignOnService')
|
91
|
+
return nil if services.empty?
|
86
92
|
|
87
|
-
|
93
|
+
# If there's only one service
|
94
|
+
return services.first.attributes['Location'] if services.size == 1
|
88
95
|
|
89
|
-
|
90
|
-
|
96
|
+
# Find service with HTTP-Redirect binding
|
97
|
+
services.each do |service|
|
98
|
+
binding = service.attributes['Binding']
|
99
|
+
return service.attributes['Location'] if binding == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
|
91
100
|
end
|
101
|
+
|
92
102
|
nil
|
93
103
|
end
|
94
104
|
|
95
105
|
def parse_sso_http_post_url
|
96
|
-
|
97
|
-
|
98
|
-
single_signon_services = entity_descriptor['IDPSSODescriptor']['SingleSignOnService']
|
106
|
+
services = find_all_with_namespace(idp_descriptor&.elements, 'SingleSignOnService')
|
107
|
+
return nil if services.empty?
|
99
108
|
|
100
|
-
|
109
|
+
# If there's only one service
|
110
|
+
return services.first.attributes['Location'] if services.size == 1
|
101
111
|
|
102
|
-
|
103
|
-
|
112
|
+
# Find service with HTTP-POST binding
|
113
|
+
services.each do |service|
|
114
|
+
binding = service.attributes['Binding']
|
115
|
+
return service.attributes['Location'] if binding == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
|
104
116
|
end
|
117
|
+
|
105
118
|
nil
|
106
119
|
end
|
107
120
|
|
108
121
|
def parse_slo_url
|
109
|
-
|
110
|
-
|
111
|
-
single_logout_services = entity_descriptor['IDPSSODescriptor']['SingleLogoutService']
|
122
|
+
services = find_all_with_namespace(idp_descriptor&.elements, 'SingleLogoutService')
|
123
|
+
return nil if services.empty?
|
112
124
|
|
113
|
-
|
125
|
+
# If there's only one service
|
126
|
+
return services.first.attributes['Location'] if services.size == 1
|
114
127
|
|
115
|
-
|
116
|
-
|
128
|
+
# Find service with HTTP-Redirect binding
|
129
|
+
services.each do |service|
|
130
|
+
binding = service.attributes['Binding']
|
131
|
+
return service.attributes['Location'] if binding == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
|
117
132
|
end
|
133
|
+
|
118
134
|
nil
|
119
135
|
end
|
120
136
|
|
121
137
|
def parse_nameid_format
|
122
|
-
|
138
|
+
formats = find_all_with_namespace(idp_descriptor&.elements, 'NameIDFormat')
|
139
|
+
return nil if formats.empty?
|
123
140
|
|
124
|
-
if
|
125
|
-
|
126
|
-
else
|
127
|
-
entity_descriptor['IDPSSODescriptor']['NameIDFormat']
|
128
|
-
end
|
141
|
+
# Return the last format if there are multiple
|
142
|
+
formats.last.text
|
129
143
|
end
|
130
144
|
|
131
145
|
def parse_x509_certificate
|
132
|
-
|
146
|
+
key_descriptors = find_all_with_namespace(idp_descriptor&.elements, 'KeyDescriptor')
|
147
|
+
return nil if key_descriptors.empty?
|
133
148
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
149
|
+
# Use the last key descriptor
|
150
|
+
key_descriptor = key_descriptors.last
|
151
|
+
|
152
|
+
# Navigate the XML structure to find the X509Certificate element
|
153
|
+
key_info = find_with_namespace(key_descriptor.elements, 'KeyInfo') ||
|
154
|
+
find_element(key_descriptor.elements, 'ds:KeyInfo')
|
155
|
+
|
156
|
+
return nil unless key_info
|
157
|
+
|
158
|
+
x509_data = find_with_namespace(key_info.elements, 'X509Data') ||
|
159
|
+
find_element(key_info.elements, 'ds:X509Data')
|
160
|
+
|
161
|
+
return nil unless x509_data
|
162
|
+
|
163
|
+
cert_element = find_with_namespace(x509_data.elements, 'X509Certificate') ||
|
164
|
+
find_element(x509_data.elements, 'ds:X509Certificate')
|
165
|
+
|
166
|
+
cert_element&.text
|
167
|
+
end
|
168
|
+
|
169
|
+
# Helper methods to handle namespace variations
|
170
|
+
def find_with_namespace(elements, name)
|
171
|
+
return nil if elements.nil?
|
172
|
+
|
173
|
+
# Try with different namespace prefixes
|
174
|
+
element = find_element(elements, name) ||
|
175
|
+
find_element(elements, "md:#{name}") ||
|
176
|
+
find_element(elements, "saml:#{name}")
|
177
|
+
|
178
|
+
element
|
179
|
+
end
|
180
|
+
|
181
|
+
def find_all_with_namespace(elements, name)
|
182
|
+
return [] if elements.nil?
|
183
|
+
|
184
|
+
# Collect elements with different namespace prefixes
|
185
|
+
result = []
|
186
|
+
result.concat(find_all_elements(elements, name))
|
187
|
+
result.concat(find_all_elements(elements, "md:#{name}"))
|
188
|
+
result.concat(find_all_elements(elements, "saml:#{name}"))
|
189
|
+
|
190
|
+
result
|
191
|
+
end
|
192
|
+
|
193
|
+
def find_element(elements, name)
|
194
|
+
return nil if elements.nil?
|
195
|
+
|
196
|
+
elements.to_a.find { |e| e.name == name }
|
197
|
+
end
|
198
|
+
|
199
|
+
def find_all_elements(elements, name)
|
200
|
+
return [] if elements.nil?
|
201
|
+
|
202
|
+
elements.to_a.select { |e| e.name == name }
|
203
|
+
end
|
204
|
+
|
205
|
+
def present?(value)
|
206
|
+
value && !value.to_s.strip.empty?
|
139
207
|
end
|
140
208
|
end
|
141
209
|
end
|
data/saml_idp_metadata.gemspec
CHANGED
@@ -6,7 +6,7 @@ require 'saml_idp_metadata/version'
|
|
6
6
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
8
|
spec.name = 'saml_idp_metadata'
|
9
|
-
spec.required_ruby_version = '>= 3.
|
9
|
+
spec.required_ruby_version = '>= 3.2', '< 4'
|
10
10
|
spec.version = SamlIdpMetadata::VERSION
|
11
11
|
spec.authors = ['tknzk']
|
12
12
|
spec.email = ['info@tknzk.dev']
|
@@ -25,7 +25,6 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
26
26
|
spec.require_paths = ['lib']
|
27
27
|
|
28
|
-
spec.add_dependency 'activesupport', '< 8'
|
29
28
|
spec.add_dependency 'rexml'
|
30
29
|
|
31
30
|
spec.add_development_dependency 'bundler', '~> 2'
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: saml_idp_metadata
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- tknzk
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-05-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: activesupport
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "<"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '8'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "<"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '8'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: rexml
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -193,7 +179,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
193
179
|
requirements:
|
194
180
|
- - ">="
|
195
181
|
- !ruby/object:Gem::Version
|
196
|
-
version: '3.
|
182
|
+
version: '3.2'
|
197
183
|
- - "<"
|
198
184
|
- !ruby/object:Gem::Version
|
199
185
|
version: '4'
|