aaf-mdqt 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/codeql-analysis.yml +70 -0
- data/.github/workflows/ruby.yml +41 -0
- data/.gitignore +25 -0
- data/.rspec +2 -0
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +296 -0
- data/.ruby-version +1 -0
- data/.tool-versions +1 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +168 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +21 -0
- data/Makefile +4 -0
- data/README.md +268 -0
- data/Rakefile +5 -0
- data/aaf-mdqt.gemspec +46 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/cucumber.yml +2 -0
- data/exe/mdqt +174 -0
- data/lib/mdqt/cli/base.rb +190 -0
- data/lib/mdqt/cli/cache_control.rb +25 -0
- data/lib/mdqt/cli/check.rb +78 -0
- data/lib/mdqt/cli/compliance.rb +0 -0
- data/lib/mdqt/cli/defaults.rb +70 -0
- data/lib/mdqt/cli/entities.rb +47 -0
- data/lib/mdqt/cli/exists.rb +0 -0
- data/lib/mdqt/cli/get.rb +130 -0
- data/lib/mdqt/cli/list.rb +65 -0
- data/lib/mdqt/cli/ln.rb +81 -0
- data/lib/mdqt/cli/ls.rb +54 -0
- data/lib/mdqt/cli/rename.rb +75 -0
- data/lib/mdqt/cli/reset.rb +27 -0
- data/lib/mdqt/cli/services.rb +25 -0
- data/lib/mdqt/cli/transform.rb +33 -0
- data/lib/mdqt/cli/url.rb +37 -0
- data/lib/mdqt/cli/version.rb +17 -0
- data/lib/mdqt/cli.rb +24 -0
- data/lib/mdqt/client/identifier_utils.rb +51 -0
- data/lib/mdqt/client/metadata_file.rb +144 -0
- data/lib/mdqt/client/metadata_response.rb +182 -0
- data/lib/mdqt/client/metadata_service.rb +194 -0
- data/lib/mdqt/client/metadata_validator.rb +81 -0
- data/lib/mdqt/client.rb +83 -0
- data/lib/mdqt/schema/MetadataExchange.xsd +112 -0
- data/lib/mdqt/schema/mdqt_check_schema.xsd +5 -0
- data/lib/mdqt/schema/oasis-200401-wss-wssecurity-secext-1.0.xsd +195 -0
- data/lib/mdqt/schema/oasis-200401-wss-wssecurity-utility-1.0.xsd +108 -0
- data/lib/mdqt/schema/saml-schema-assertion-2.0.xsd +283 -0
- data/lib/mdqt/schema/saml-schema-metadata-2.0.xsd +337 -0
- data/lib/mdqt/schema/ws-addr.xsd +137 -0
- data/lib/mdqt/schema/ws-authorization.xsd +145 -0
- data/lib/mdqt/schema/ws-federation.xsd +471 -0
- data/lib/mdqt/schema/ws-securitypolicy-1.2.xsd +1205 -0
- data/lib/mdqt/schema/xenc-schema.xsd +136 -0
- data/lib/mdqt/schema/xml.xsd +287 -0
- data/lib/mdqt/schema/xmldsig-core-schema.xsd +309 -0
- data/lib/mdqt/version.rb +3 -0
- data/lib/mdqt.rb +5 -0
- data/lib/tasks/cucumber.rake +8 -0
- data/lib/tasks/spec.rake +5 -0
- data/lib/tasks/tests.rake +6 -0
- data/lib/tasks/yard.rake +6 -0
- metadata +332 -0
@@ -0,0 +1,190 @@
|
|
1
|
+
module MDQT
|
2
|
+
|
3
|
+
class CLI
|
4
|
+
|
5
|
+
class Base
|
6
|
+
|
7
|
+
require 'mdqt/cli'
|
8
|
+
require 'pastel'
|
9
|
+
require 'pathname'
|
10
|
+
|
11
|
+
def self.run(args, options)
|
12
|
+
|
13
|
+
check_requirements(options)
|
14
|
+
introduce(options)
|
15
|
+
|
16
|
+
self.new(args, options).run
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.check_requirements(options)
|
20
|
+
|
21
|
+
unless options.service == :not_required
|
22
|
+
abort "No MDQ service URL has been specified. Please use --service, MDQT_SERVICE or MDQ_BASE_URL" unless service_url(options).to_s.start_with?("http")
|
23
|
+
end
|
24
|
+
|
25
|
+
if options.save_to
|
26
|
+
dir = options.save_to
|
27
|
+
begin
|
28
|
+
FileUtils.mkdir_p(dir) unless File.exist?(dir)
|
29
|
+
rescue
|
30
|
+
abort "Error: Directory #{dir} did not exist, and we can't create it"
|
31
|
+
end
|
32
|
+
abort "Error: '#{dir}' is not a writable directory!" if (File.directory?(dir) && !File.writable?(dir))
|
33
|
+
abort "Error: '#{dir}' is not a directory!" unless File.directory?(dir)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.introduce(options)
|
39
|
+
if options.verbose
|
40
|
+
STDERR.puts "MDQT version #{MDQT::VERSION}"
|
41
|
+
STDERR.puts "Using #{service_url(options)}" unless options.service == :not_required
|
42
|
+
STDERR.puts "Caching is #{MDQT::CLI::CacheControl.caching_on?(options) ? 'on' : 'off'}"
|
43
|
+
STDERR.print "XML validation is #{MDQT::Client.verification_available? ? 'available' : 'not available'}"
|
44
|
+
STDERR.puts " #{options.validate ? "and active" : "but inactive"} for this request" if MDQT::Client.verification_available?
|
45
|
+
STDERR.print "Signature verification is #{MDQT::Client.verification_available? ? 'available' : 'not available'}"
|
46
|
+
STDERR.puts " #{options.verify_with ? "and active" : "but inactive"} for this request" if MDQT::Client.verification_available?
|
47
|
+
STDERR.puts "Output directory for saved files is: #{File.absolute_path(options.save_to)}" if options.save_to
|
48
|
+
STDERR.puts("Warning! TLS certificate verification has been disabled!") if options.tls_risky
|
49
|
+
STDERR.puts
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize(cli_args, options)
|
54
|
+
piped_input = get_stdin
|
55
|
+
@args = cli_args.concat(piped_input)
|
56
|
+
@options = options
|
57
|
+
end
|
58
|
+
|
59
|
+
def get_stdin
|
60
|
+
return $stdin.readlines.map(&:split).flatten.map(&:strip) if pipeable?
|
61
|
+
[]
|
62
|
+
end
|
63
|
+
|
64
|
+
def pipeable?
|
65
|
+
return false if ENV["MDQT_STDIN"].to_s.strip.downcase == "off" # Workaround Aruba testing weirdness?
|
66
|
+
!STDIN.tty? && !$stdin.closed? && $stdin.stat.pipe?
|
67
|
+
end
|
68
|
+
|
69
|
+
def args
|
70
|
+
@args
|
71
|
+
end
|
72
|
+
|
73
|
+
def options=(new_options)
|
74
|
+
@options = new_options
|
75
|
+
end
|
76
|
+
|
77
|
+
def options
|
78
|
+
@options
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.service_url(options)
|
82
|
+
|
83
|
+
return nil if options.service == :not_required
|
84
|
+
|
85
|
+
choice = options.service.to_s.strip
|
86
|
+
|
87
|
+
if choice.downcase.start_with? "http"
|
88
|
+
normalize_base_url(choice)
|
89
|
+
else
|
90
|
+
Defaults.lookup_service_alias(choice)
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
def service_url(options)
|
96
|
+
self.class.service_url(options)
|
97
|
+
end
|
98
|
+
|
99
|
+
def output(response)
|
100
|
+
if response.ok?
|
101
|
+
yay response.message
|
102
|
+
hey explain(response) if options.explain
|
103
|
+
trailer = response.data[-1] == "\n" ? "" : "\n"
|
104
|
+
response.data + trailer
|
105
|
+
else
|
106
|
+
hey response.message
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
def explain(response)
|
112
|
+
unless response.explanation.empty?
|
113
|
+
require 'terminal-table'
|
114
|
+
misc_rows = [['URL', response.explanation[:url]], ["Method", response.explanation[:method]], ['Status', response.explanation[:status]]]
|
115
|
+
req_header_rows = response.explanation[:request_headers].map { |k, v| ['C', k, v] }
|
116
|
+
resp_header_rows = response.explanation[:response_headers].map { |k, v| ['S', k, v] }
|
117
|
+
|
118
|
+
btw Terminal::Table.new :title => "HTTP Misc", :rows => misc_rows
|
119
|
+
btw Terminal::Table.new :title => "Client Request Headers", :headings => ['C/S', 'Header', 'Value'], :rows => req_header_rows
|
120
|
+
btw Terminal::Table.new :title => "Server Response Headers", :headings => ['C/S', 'Header', 'Value'], :rows => resp_header_rows
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def advise_on_xml_signing_support
|
126
|
+
hey "XML signature verification and XML validation are not available. Install the 'xmldsig' gem if you can." unless MDQT::Client.verification_available?
|
127
|
+
end
|
128
|
+
|
129
|
+
def extract_certificate_paths(cert_paths = options.verify_with)
|
130
|
+
cert_paths.collect do |cert_path|
|
131
|
+
begin
|
132
|
+
halt! "Cannot read certificate at '#{cert_path}'!" unless File.readable?(cert_path)
|
133
|
+
halt! "File at '#{cert_path}' does not seem to be a PEM format certificate" unless IO.binread(cert_path).include?("-----BEGIN CERTIFICATE-----")
|
134
|
+
cert_path
|
135
|
+
rescue
|
136
|
+
halt! "Unable to validate the certificate at '#{cert_path}'"
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def colour_shell?
|
142
|
+
TTY::Color.color?
|
143
|
+
end
|
144
|
+
|
145
|
+
def pastel
|
146
|
+
@pastel ||= Pastel.new
|
147
|
+
end
|
148
|
+
|
149
|
+
def say(text)
|
150
|
+
STDOUT.puts(text)
|
151
|
+
end
|
152
|
+
|
153
|
+
def hey(comment)
|
154
|
+
STDERR.puts(comment)
|
155
|
+
end
|
156
|
+
|
157
|
+
def btw(comment)
|
158
|
+
STDERR.puts(comment) if options.verbose
|
159
|
+
end
|
160
|
+
|
161
|
+
def yay(comment)
|
162
|
+
btw pastel.green(comment)
|
163
|
+
end
|
164
|
+
|
165
|
+
def halt!(comment)
|
166
|
+
abort pastel.red("Error: #{comment}")
|
167
|
+
end
|
168
|
+
|
169
|
+
def run
|
170
|
+
halt! "No action has been defined for this command!"
|
171
|
+
end
|
172
|
+
|
173
|
+
private
|
174
|
+
|
175
|
+
## Base URLs should end with a "/", it might be easier to just add one rather than raise an error
|
176
|
+
def self.normalize_base_url(url)
|
177
|
+
if url.end_with?('/')
|
178
|
+
url
|
179
|
+
else
|
180
|
+
"#{url}/"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
#
|
189
|
+
end
|
190
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module MDQT
|
2
|
+
class CLI
|
3
|
+
|
4
|
+
class CacheControl
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def caching_on?(options)
|
9
|
+
return false if cache_type(options) == :none
|
10
|
+
true
|
11
|
+
end
|
12
|
+
|
13
|
+
def cache_type(options)
|
14
|
+
return :none if options.refresh
|
15
|
+
return :memcache if options.cache && options.memcache
|
16
|
+
return :file if options.cache
|
17
|
+
:none
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module MDQT
|
2
|
+
|
3
|
+
class CLI
|
4
|
+
|
5
|
+
require 'mdqt/cli/base'
|
6
|
+
|
7
|
+
class Check < Base
|
8
|
+
|
9
|
+
def run
|
10
|
+
|
11
|
+
options.validate = true
|
12
|
+
|
13
|
+
advise_on_xml_signing_support
|
14
|
+
halt!("Cannot check a metadata file without XML support: please install additional gems") unless MDQT::Client.verification_available?
|
15
|
+
|
16
|
+
client = MDQT::Client.new(
|
17
|
+
service_url(options),
|
18
|
+
verbose: options.verbose,
|
19
|
+
explain: options.explain ? true : false,
|
20
|
+
)
|
21
|
+
|
22
|
+
cert_paths = options.verify_with ? extract_certificate_paths(options.verify_with) : []
|
23
|
+
|
24
|
+
args.each do |filename|
|
25
|
+
|
26
|
+
filename = File.absolute_path(filename)
|
27
|
+
file = client.open_metadata(filename)
|
28
|
+
|
29
|
+
halt!("Cannot access file #{filename}") unless file.readable?
|
30
|
+
|
31
|
+
halt!("XML validation failed for #{filename}:\n#{file.validation_error}") unless file.valid?
|
32
|
+
btw "File #{filename} is valid SAML Metadata XML"
|
33
|
+
|
34
|
+
if options.verify_with
|
35
|
+
halt! "XML in #{filename} is not signed, cannot verify!" unless file.signed?
|
36
|
+
halt! "The signed XML for #{filename} cannot be verified using #{cert_paths.to_sentence}" unless file.verified_signature?(cert_paths)
|
37
|
+
btw "Signed XML for #{filename} has been verified using '#{cert_paths.to_sentence}'"
|
38
|
+
end
|
39
|
+
|
40
|
+
yay "#{filename} OK"
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
def verify_results(results)
|
46
|
+
|
47
|
+
# if options.validate
|
48
|
+
# results.each do |result|
|
49
|
+
# next unless result.ok?
|
50
|
+
# halt! "The data for #{result.identifier} is not valid when checked against schema:\n#{result.validation_error}" unless result.valid?
|
51
|
+
# btw "Data for #{result.identifier.empty? ? 'aggregate' : result.identifier } has been validated against schema" ## FIXME - needs constistent #label maybe?
|
52
|
+
# end
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# return results unless options.verify_with
|
56
|
+
#
|
57
|
+
# cert_paths = extract_certificate_paths(options.verify_with)
|
58
|
+
#
|
59
|
+
# results.each do |result|
|
60
|
+
# next unless result.ok?
|
61
|
+
# halt! "Data from #{options.service} is not signed, cannot verify!" unless result.signed?
|
62
|
+
# halt! "The data for #{result.identifier} cannot be verified using #{cert_paths.to_sentence}" unless result.verified_signature?(cert_paths)
|
63
|
+
# btw "Data for #{result.identifier.empty? ? 'aggregate' : result.identifier } has been verified using '#{cert_paths.to_sentence}'" ## FIXME - needs constistent #label maybe?
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# results
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
|
File without changes
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module MDQT
|
2
|
+
class CLI
|
3
|
+
|
4
|
+
class Defaults
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def base_url
|
9
|
+
|
10
|
+
ENV['MDQT_SERVICE'] || ENV['MDQ_BASE_URL'] || guess_service
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
def force_hash?
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
def cli_defaults
|
19
|
+
{
|
20
|
+
hash: force_hash?,
|
21
|
+
cache: true,
|
22
|
+
refresh: false
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def guess_service
|
27
|
+
|
28
|
+
locale = ENV['LANG']
|
29
|
+
|
30
|
+
service = services.find { |s| s[:locale] == locale }
|
31
|
+
#service ||= services.first
|
32
|
+
|
33
|
+
if service
|
34
|
+
url = service[:url]
|
35
|
+
STDERR.puts "MDQT is assuming that you want to use #{url}\nPlease configure this using --service, MDQT_SERVICE or MDQ_BASE_URL\n\n"
|
36
|
+
url
|
37
|
+
else
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
def lookup_service_alias(srv_alias)
|
44
|
+
service = services.find { |s| s[:alias].to_s.downcase.to_sym == srv_alias.to_s.downcase.to_sym }
|
45
|
+
service ? service[:url] : nil
|
46
|
+
end
|
47
|
+
|
48
|
+
def services
|
49
|
+
[
|
50
|
+
{ alias: "ukamf",
|
51
|
+
locale: "en_GB.UTF-8",
|
52
|
+
url: "http://mdq.ukfederation.org.uk/"
|
53
|
+
},
|
54
|
+
{ alias: "incommon",
|
55
|
+
locale: "en_US.UTF-8",
|
56
|
+
url: "https://mdq.incommon.org/"
|
57
|
+
},
|
58
|
+
{ alias: "dfn",
|
59
|
+
locale: "de_utf8",
|
60
|
+
url: "https://mdq.aai.dfn.de/"
|
61
|
+
},
|
62
|
+
]
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module MDQT
|
2
|
+
|
3
|
+
class CLI
|
4
|
+
|
5
|
+
require 'mdqt/cli/base'
|
6
|
+
|
7
|
+
class Entities < Base
|
8
|
+
|
9
|
+
def run
|
10
|
+
|
11
|
+
options.validate = true
|
12
|
+
|
13
|
+
advise_on_xml_signing_support
|
14
|
+
halt!("Cannot check a metadata file without XML support: please install additional gems") unless MDQT::Client.verification_available?
|
15
|
+
|
16
|
+
client = MDQT::Client.new(
|
17
|
+
service_url(options),
|
18
|
+
verbose: options.verbose,
|
19
|
+
explain: options.explain ? true : false,
|
20
|
+
)
|
21
|
+
|
22
|
+
args.each do |filename|
|
23
|
+
|
24
|
+
file = client.open_metadata(filename)
|
25
|
+
|
26
|
+
halt!("Cannot access file #{filename}") unless file.readable?
|
27
|
+
|
28
|
+
halt!("XML validation failed for #{filename}:\n#{file.validation_error}") unless file.valid?
|
29
|
+
|
30
|
+
file.entity_ids.each do |id|
|
31
|
+
id = options.sha1 ? [id, MDQT::Client::IdentifierUtils.transform_uri(id)].join(" ") : id
|
32
|
+
say(id)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
|
File without changes
|
data/lib/mdqt/cli/get.rb
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
module MDQT
|
2
|
+
|
3
|
+
class CLI
|
4
|
+
|
5
|
+
require 'mdqt/cli/base'
|
6
|
+
|
7
|
+
class Get < Base
|
8
|
+
|
9
|
+
def run
|
10
|
+
|
11
|
+
aggregate_confirmation_check!
|
12
|
+
|
13
|
+
advise_on_xml_signing_support
|
14
|
+
|
15
|
+
results = verify_results(get_responses)
|
16
|
+
|
17
|
+
#results = MetadataAggregator.aggregate_responses(results) if options.aggregate
|
18
|
+
|
19
|
+
output_metadata(results, options)
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_responses
|
24
|
+
|
25
|
+
client = MDQT::Client.new(
|
26
|
+
service_url(options),
|
27
|
+
verbose: options.verbose,
|
28
|
+
explain: options.explain ? true : false,
|
29
|
+
tls_risky: options.tls_risky ? true : false,
|
30
|
+
cache_type: MDQT::CLI::CacheControl.cache_type(options),
|
31
|
+
)
|
32
|
+
|
33
|
+
args.empty? ? [client.get_metadata("")] : args.collect { |entity_id| client.get_metadata(entity_id) }
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
def verify_results(results)
|
38
|
+
|
39
|
+
if options.validate
|
40
|
+
results.each do |result|
|
41
|
+
next unless result.ok?
|
42
|
+
halt! "The data for #{result.identifier} is not valid when checked against schema:\n#{result.validation_error}" unless result.valid?
|
43
|
+
btw "Data for #{result.identifier.empty? ? 'aggregate' : result.identifier } has been validated against schema" ## FIXME - needs constistent #label maybe?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
return results unless options.verify_with
|
48
|
+
|
49
|
+
cert_paths = extract_certificate_paths(options.verify_with)
|
50
|
+
|
51
|
+
results.each do |result|
|
52
|
+
next unless result.ok?
|
53
|
+
halt! "Data from #{options.service} is not signed, cannot verify!" unless result.signed?
|
54
|
+
halt! "The data for #{result.identifier} cannot be verified using #{cert_paths.to_sentence}" unless result.verified_signature?(cert_paths)
|
55
|
+
btw "Data for #{result.identifier.empty? ? 'aggregate' : result.identifier } has been verified using '#{cert_paths.to_sentence}'" ## FIXME - needs constistent #label maybe?
|
56
|
+
end
|
57
|
+
|
58
|
+
results
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
def output_metadata(results, options)
|
63
|
+
case action(results, options)
|
64
|
+
when :save_files
|
65
|
+
output_files(results, options)
|
66
|
+
when :print_to_stdout
|
67
|
+
output_to_stdout(results, options)
|
68
|
+
else
|
69
|
+
halt! "Can't determine output type"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def action(results, options)
|
74
|
+
case
|
75
|
+
when options.save_to
|
76
|
+
:save_files
|
77
|
+
else
|
78
|
+
:print_to_stdout
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def output_to_stdout(results, options)
|
83
|
+
results.each { |r| puts output(r) }
|
84
|
+
end
|
85
|
+
|
86
|
+
def output_files(results, options)
|
87
|
+
pwd = Pathname.getwd
|
88
|
+
prepare_output_directory(options.save_to)
|
89
|
+
results.each do |result|
|
90
|
+
main_file = output_file_path(result.filename)
|
91
|
+
open(main_file, 'w') { |f|
|
92
|
+
f.puts result.data
|
93
|
+
}
|
94
|
+
|
95
|
+
if options.list
|
96
|
+
puts Pathname.new(main_file).relative_path_from(pwd)
|
97
|
+
end
|
98
|
+
|
99
|
+
yay "Created #{main_file}"
|
100
|
+
|
101
|
+
# if options.link_id
|
102
|
+
# ["{sha1}#{result.filename.gsub(".xml", "")}"].each do |altname|
|
103
|
+
# full_alias = output_file_path(altname)
|
104
|
+
# FileUtils.ln_sf(main_file, full_alias)
|
105
|
+
# yay "Linked alias #{altname} -> #{main_file}"
|
106
|
+
# end
|
107
|
+
# end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def output_file_path(filename)
|
115
|
+
File.absolute_path(File.join([options.save_to, filename]))
|
116
|
+
end
|
117
|
+
|
118
|
+
def prepare_output_directory(directory)
|
119
|
+
FileUtils.mkdir_p(directory)
|
120
|
+
end
|
121
|
+
|
122
|
+
def aggregate_confirmation_check!
|
123
|
+
halt!("Please specify --all if you wish to request all entities from #{options.service}") if args.empty? && !options.all
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module MDQT
|
2
|
+
|
3
|
+
class CLI
|
4
|
+
|
5
|
+
require 'mdqt/cli/base'
|
6
|
+
|
7
|
+
class List < Base
|
8
|
+
|
9
|
+
def run
|
10
|
+
|
11
|
+
options.validate = true
|
12
|
+
|
13
|
+
advise_on_xml_signing_support
|
14
|
+
|
15
|
+
halt!("Cannot check a metadata file without XML support: please install additional gems") unless MDQT::Client.verification_available?
|
16
|
+
|
17
|
+
response = get_response
|
18
|
+
result = verify_result(response)
|
19
|
+
|
20
|
+
puts result.entity_ids
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_response
|
25
|
+
|
26
|
+
client = MDQT::Client.new(
|
27
|
+
service_url(options),
|
28
|
+
verbose: options.verbose,
|
29
|
+
explain: options.explain ? true : false,
|
30
|
+
tls_risky: options.tls_risky ? true : false,
|
31
|
+
cache_type: MDQT::CLI::CacheControl.cache_type(options),
|
32
|
+
)
|
33
|
+
|
34
|
+
client.get_metadata("")
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def verify_result(result)
|
39
|
+
|
40
|
+
if options.validate
|
41
|
+
halt! "The data for #{result.identifier} is not valid when checked against schema:\n#{result.validation_error}" unless result.valid?
|
42
|
+
btw "Data for #{result.identifier.empty? ? 'aggregate' : result.identifier } has been validated against schema" ## FIXME - needs constistent #label maybe?
|
43
|
+
end
|
44
|
+
|
45
|
+
return result unless options.verify_with
|
46
|
+
|
47
|
+
cert_paths = extract_certificate_paths(options.verify_with)
|
48
|
+
|
49
|
+
halt! "Data from #{options.service} is not signed, cannot verify!" unless result.signed?
|
50
|
+
halt! "The data for #{result.identifier} cannot be verified using #{cert_paths.to_sentence}" unless result.verified_signature?(cert_paths)
|
51
|
+
btw "Data for #{result.identifier.empty? ? 'aggregate' : result.identifier } has been verified using '#{cert_paths.to_sentence}'" ## FIXME - needs constistent #label maybe?
|
52
|
+
|
53
|
+
result
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
|
data/lib/mdqt/cli/ln.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
module MDQT
|
2
|
+
|
3
|
+
class CLI
|
4
|
+
|
5
|
+
require 'mdqt/cli/base'
|
6
|
+
|
7
|
+
class Ln < Base
|
8
|
+
|
9
|
+
def run
|
10
|
+
|
11
|
+
options.validate = true
|
12
|
+
|
13
|
+
advise_on_xml_signing_support
|
14
|
+
halt!("Cannot check a metadata file without XML support: please install additional gems") unless MDQT::Client.verification_available?
|
15
|
+
|
16
|
+
client = MDQT::Client.new(
|
17
|
+
options.service,
|
18
|
+
verbose: options.verbose,
|
19
|
+
explain: options.explain ? true : false,
|
20
|
+
)
|
21
|
+
|
22
|
+
halt!("Please specify a file to link to!") if args.empty?
|
23
|
+
|
24
|
+
args.each do |filename|
|
25
|
+
|
26
|
+
next if File.symlink?(filename)
|
27
|
+
|
28
|
+
file = client.open_metadata(filename)
|
29
|
+
|
30
|
+
halt!("Cannot access file #{filename}") unless file.readable?
|
31
|
+
halt!("File #{filename} is a metadata aggregate, cannot create entityID hashed link!") if file.aggregate?
|
32
|
+
halt!("XML validation failed for #{filename}:\n#{file.validation_error}") unless file.valid?
|
33
|
+
|
34
|
+
halt!("Cannot find entityID for #{filename}") unless file.entity_id
|
35
|
+
|
36
|
+
linkname = file.linkname
|
37
|
+
|
38
|
+
if filename == linkname
|
39
|
+
if options.force
|
40
|
+
hey("Warning: Cannot link file to itself, skipping! #{filename}")
|
41
|
+
next
|
42
|
+
else
|
43
|
+
halt!("Cannot link file to itself! #{filename}")
|
44
|
+
next
|
45
|
+
end
|
46
|
+
btw("Cannot link file to itself! #{filename}")
|
47
|
+
end
|
48
|
+
|
49
|
+
if file.turd?
|
50
|
+
hey "Warning: will not process backup/turd files"
|
51
|
+
next
|
52
|
+
end
|
53
|
+
|
54
|
+
message = ""
|
55
|
+
|
56
|
+
if File.exist?(linkname)
|
57
|
+
if options.force
|
58
|
+
File.delete(linkname)
|
59
|
+
else
|
60
|
+
old_target = File.readlink(linkname)
|
61
|
+
message = old_target == filename ? "File exists" : "Conflicts with #{filename}"
|
62
|
+
halt!("#{linkname} -> #{old_target} [#{file.entity_id}] #{message}. Use --force to override")
|
63
|
+
next
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
File.symlink(filename, linkname)
|
68
|
+
hey("#{linkname} -> #{filename} [#{file.entity_id}] #{message}") if options.verbose
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
|