mdqt 0.4.0 → 0.5.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.
@@ -6,7 +6,9 @@ module MDQT
6
6
  class << self
7
7
 
8
8
  def base_url
9
+
9
10
  ENV['MDQT_SERVICE'] || ENV['MDQ_BASE_URL'] || guess_service
11
+
10
12
  end
11
13
 
12
14
  def force_hash?
@@ -16,7 +18,8 @@ module MDQT
16
18
  def cli_defaults
17
19
  {
18
20
  hash: force_hash?,
19
- cache: false
21
+ cache: true,
22
+ refresh: false
20
23
  }
21
24
  end
22
25
 
@@ -24,21 +27,39 @@ module MDQT
24
27
 
25
28
  locale = ENV['LANG']
26
29
 
27
- #STDERR.puts("Detected locale #{locale}")
30
+ service = services.find { |s| s[:locale] == locale }
31
+ #service ||= services.first
28
32
 
29
- service = case locale
30
- when 'en_GB.UTF-8'
31
- 'http://mdq.ukfederation.org.uk/'
32
- when 'en_US.UTF-8'
33
- 'http://mdq-beta.incommon.org/global'
34
- else
35
- abort "Please specify an MDQ service using --service, MDQT_SERVICE or MDQ_BASE_URL"
36
- end
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
37
40
 
38
- STDERR.puts "MDQT is assuming that you want to use #{service}\nPlease configure this using --service, MDQT_SERVICE or MDQ_BASE_URL\n\n"
41
+ end
39
42
 
40
- service
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
41
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
+ ]
42
63
  end
43
64
 
44
65
  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 CHANGED
@@ -23,14 +23,14 @@ module MDQT
23
23
  def get_responses
24
24
 
25
25
  client = MDQT::Client.new(
26
- options.service,
27
- verbose: options.verbose,
28
- explain: options.explain ? true : false,
29
- tls_risky: options.tls_risky ? true : false,
30
- cache_type: options.cache ? :file : :none,
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
31
  )
32
32
 
33
- args.empty? ? [client.get_metadata("")] : args.collect {|entity_id| client.get_metadata(entity_id)}
33
+ args.empty? ? [client.get_metadata("")] : args.collect { |entity_id| client.get_metadata(entity_id) }
34
34
 
35
35
  end
36
36
 
@@ -80,14 +80,14 @@ module MDQT
80
80
  end
81
81
 
82
82
  def output_to_stdout(results, options)
83
- results.each {|r| puts output(r)}
83
+ results.each { |r| puts output(r) }
84
84
  end
85
85
 
86
86
  def output_files(results, options)
87
87
  prepare_output_directory(options.save_to)
88
88
  results.each do |result|
89
89
  main_file = output_file_path(result.filename)
90
- open(main_file, 'w') {|f|
90
+ open(main_file, 'w') { |f|
91
91
  f.puts result.data
92
92
  }
93
93
  yay "Created #{main_file}"
@@ -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
+
@@ -0,0 +1,74 @@
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
+ args.each do |filename|
23
+
24
+ next if File.symlink?(filename)
25
+
26
+ file = client.open_metadata(filename)
27
+
28
+ halt!("Cannot access file #{filename}") unless file.readable?
29
+ halt!("File #{filename} is a metadata aggregate, cannot create entityID hashed link!") if file.aggregate?
30
+ halt!("XML validation failed for #{filename}:\n#{file.validation_error}") unless file.valid?
31
+
32
+ halt!("Cannot find entityID for #{filename}") unless file.entity_id
33
+
34
+ linkname = file.linkname
35
+
36
+ if filename == linkname
37
+ if options.force
38
+ hey("Warning: Cannot link file to itself, skipping! #{filename}")
39
+ next
40
+ else
41
+ halt!("Cannot link file to itself! #{filename}")
42
+ next
43
+ end
44
+ btw("Cannot link file to itself! #{filename}")
45
+ end
46
+
47
+ message = ""
48
+
49
+ if File.exists?(linkname)
50
+ if options.force
51
+ File.delete(linkname)
52
+ else
53
+ old_target = File.readlink(linkname)
54
+ message = old_target == filename ? "File exists" : "Conflicts with #{filename}"
55
+ halt!("#{linkname} -> #{old_target} [#{file.entity_id}] #{message}. Use --force to override")
56
+ next
57
+ end
58
+ end
59
+
60
+ File.symlink(filename, linkname)
61
+ hey("#{linkname} -> #{filename} [#{file.entity_id}] #{message}") if options.verbose
62
+ end
63
+
64
+ end
65
+
66
+ end
67
+
68
+ private
69
+
70
+ end
71
+
72
+ end
73
+
74
+
@@ -0,0 +1,52 @@
1
+ module MDQT
2
+
3
+ class CLI
4
+
5
+ require 'mdqt/cli/base'
6
+
7
+ class Ls < 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
+ results = []
23
+
24
+ args.each do |filename|
25
+
26
+ file = client.open_metadata(filename)
27
+
28
+ halt!("Cannot access file #{filename}") unless file.readable?
29
+
30
+ #halt!("File #{filename} is a metadata aggregate, cannot create entityID hashed link!") if file.aggregate?
31
+ next 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
+ results << {id: file.entity_id, type: file.type, filename: file.basename}
37
+
38
+ end
39
+
40
+ results.sort_by { | r | [r[:id], r[:type]] }.each {|r| puts "#{r[:id]}, #{r[:type]}, #{r[:filename]}" }
41
+
42
+ end
43
+
44
+ end
45
+
46
+ private
47
+
48
+ end
49
+
50
+ end
51
+
52
+
@@ -0,0 +1,60 @@
1
+ module MDQT
2
+
3
+ class CLI
4
+
5
+ require 'mdqt/cli/base'
6
+
7
+ class Rename < 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
+ args.each do |filename|
23
+
24
+ file = client.open_metadata(filename)
25
+
26
+ halt!("Cannot access file #{filename}") unless file.readable?
27
+ halt!("File #{filename} is a metadata aggregate, cannot rename to hashed entityID!") if file.aggregate?
28
+ halt!("XML validation failed for #{filename}:\n#{file.validation_error}") unless file.valid?
29
+
30
+ halt!("Cannot find entityID for #{filename}") unless file.entity_id
31
+
32
+ newname = file.linkname # Using the same name as the link, not super-obvious
33
+ next if filename == newname
34
+
35
+ message = ""
36
+
37
+ if File.exists?(newname)
38
+ if options.force
39
+ File.delete(newname)
40
+ else
41
+ halt!("Cannot rename #{filename} to #{newname} - File exists! Use --force to override")
42
+ next
43
+ end
44
+ end
45
+
46
+ File.rename(filename, newname)
47
+ hey("#{filename} renamed to #{newname} [#{file.entity_id}] #{message}") if options.verbose
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+
54
+ private
55
+
56
+ end
57
+
58
+ end
59
+
60
+
@@ -0,0 +1,25 @@
1
+ module MDQT
2
+
3
+ class CLI
4
+
5
+ require 'mdqt/cli/base'
6
+
7
+ class Services < Base
8
+
9
+ def run
10
+
11
+ puts "\nKnown services:"
12
+ puts
13
+ MDQT::CLI::Defaults.services.each do |service|
14
+ puts "#{service[:alias]}: #{service[:url]}"
15
+ end
16
+ puts
17
+ puts "Specify these in commands using the --service option or MDQ_BASE_URL environment variable"
18
+ puts
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+ end
25
+
@@ -0,0 +1,37 @@
1
+ module MDQT
2
+
3
+ class CLI
4
+
5
+ require 'mdqt/cli/base'
6
+ require 'uri'
7
+
8
+ class URL < Base
9
+
10
+ def run
11
+
12
+ mds = MDQT::Client::MetadataService.new(service_url(options),
13
+ verbose:false,
14
+ cache_type: :none,
15
+ explain: false,
16
+ tls_cert_check: false)
17
+
18
+ if args.empty?
19
+ puts service_url(options)
20
+ else
21
+ args.each do |arg|
22
+ puts build_url(mds, arg)
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ def build_url(mds, entity_id)
29
+
30
+ URI.join(service_url(options), "/entities/#{mds.prepare_id(entity_id)}")
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+ end
37
+
data/lib/mdqt/cli.rb CHANGED
@@ -4,13 +4,20 @@ module MDQT
4
4
  require 'mdqt/client'
5
5
 
6
6
  require 'mdqt/cli/defaults'
7
+ require 'mdqt/cli/cache_control'
7
8
 
8
9
  require 'mdqt/cli/get'
9
10
  require 'mdqt/cli/reset'
10
11
  require 'mdqt/cli/version'
11
12
  require 'mdqt/cli/transform'
12
13
  require 'mdqt/cli/check'
13
-
14
+ require 'mdqt/cli/entities'
15
+ require 'mdqt/cli/ln'
16
+ require 'mdqt/cli/ls'
17
+ require 'mdqt/cli/list'
18
+ require 'mdqt/cli/services'
19
+ require 'mdqt/cli/rename'
20
+ require 'mdqt/cli/url'
14
21
 
15
22
  end
16
23
 
@@ -6,8 +6,7 @@ module MDQT
6
6
  require 'digest'
7
7
 
8
8
  def initialize(filename, options = {})
9
- @filename = filename
10
- @identifier = nil
9
+ @filename = File.absolute_path(filename)
11
10
  @data = nil
12
11
  @expires = nil
13
12
  @etag = nil
@@ -19,8 +18,21 @@ module MDQT
19
18
  @filename
20
19
  end
21
20
 
21
+ def basename
22
+ File.basename(filename)
23
+ end
24
+
22
25
  def identifier
23
- @identifier
26
+ entity_id
27
+ end
28
+
29
+ def entity_id
30
+ raise "Incorrect metadata file type - aggregate" if aggregate?
31
+ @entity_id ||= extract_entity_id
32
+ end
33
+
34
+ def entity_ids
35
+ @entity_ids ||= extract_entity_ids
24
36
  end
25
37
 
26
38
  def data
@@ -32,7 +44,11 @@ module MDQT
32
44
  end
33
45
 
34
46
  def type
35
- @type
47
+ @type ||= calculate_type
48
+ end
49
+
50
+ def aggregate?
51
+ type == :aggregate
36
52
  end
37
53
 
38
54
  def expires
@@ -75,13 +91,46 @@ module MDQT
75
91
  @filename = "aggregate-#{Digest::SHA1.hexdigest(@service)}.xml"
76
92
  else
77
93
  @filename ||= identifier.start_with?("{sha1}") ?
78
- "#{@identifier.gsub("{sha1}","")}.xml" :
79
- "#{Digest::SHA1.hexdigest(@identifier)}.xml"
94
+ "#{@identifier.gsub("{sha1}", "")}.xml" :
95
+ "#{Digest::SHA1.hexdigest(@identifier)}.xml"
96
+ end
97
+ end
98
+
99
+ def linkname
100
+ if aggregate?
101
+ raise "Not supported for aggregates"
102
+ else
103
+ "#{Digest::SHA1.hexdigest(entity_id)}.xml"
80
104
  end
81
105
  end
82
106
 
107
+ def symlink?
108
+ File.symlink?(filename)
109
+ end
110
+
83
111
  private
84
112
 
113
+ def calculate_type
114
+ return :aggregate if data.include?("<EntitiesDescriptor")
115
+ if data.include?("EntityDescriptor")
116
+ return :alias if symlink?
117
+ return :entity
118
+ end
119
+ :unknown
120
+ end
121
+
122
+ def xml_doc
123
+ Nokogiri::XML.parse(data).remove_namespaces!
124
+ end
125
+
126
+ def extract_entity_id
127
+ xml_doc.xpath("/EntityDescriptor/@entityID").text
128
+ end
129
+
130
+ def extract_entity_ids
131
+ xml_doc.xpath("//EntityDescriptor/@entityID").map(&:text)
132
+ end
133
+
85
134
  end
86
135
 
87
136
  end
@@ -4,15 +4,17 @@ module MDQT
4
4
  class MetadataResponse
5
5
 
6
6
  require 'digest'
7
+ require 'uri'
7
8
 
8
9
  def initialize(identifier, service, http_response, options = {})
9
10
 
10
11
  @requested_identitier = identifier
11
- @identifier = URI.decode(identifier)
12
+ @identifier = URI.decode_www_form_component(identifier)
12
13
  @service = service
13
14
  @code = http_response.status || 500
14
15
  @data = http_response.body || ""
15
- @type = http_response.headers['Content-Type']
16
+ @type = nil
17
+ @content_type = http_response.headers['Content-Type']
16
18
  @expires = http_response.headers['Expires']
17
19
  @etag = http_response.headers['ETag']
18
20
  @last_modified = http_response.headers['Last-Modified']
@@ -33,10 +35,21 @@ module MDQT
33
35
  @identifier
34
36
  end
35
37
 
38
+
36
39
  def requested_identifier
37
40
  @identifier
38
41
  end
39
42
 
43
+ def entity_id
44
+ raise "Incorrect metadata file type - aggregate" if aggregate?
45
+ @entity_id ||= extract_entity_id
46
+ end
47
+
48
+ def entity_ids
49
+ @entity_ids ||= extract_entity_ids
50
+ end
51
+
52
+
40
53
  def service
41
54
  @service
42
55
  end
@@ -50,7 +63,11 @@ module MDQT
50
63
  end
51
64
 
52
65
  def type
53
- @type
66
+ @type ||= calculate_type
67
+ end
68
+
69
+ def content_type
70
+ @content_type
54
71
  end
55
72
 
56
73
  def expires
@@ -135,6 +152,28 @@ module MDQT
135
152
 
136
153
  private
137
154
 
155
+ def calculate_type
156
+
157
+ return :html if data[0,1000].include?('<!doctype html>')
158
+ return :aggregate if data[0,5000].include?("EntitiesDescriptor")
159
+ if data[0,5000].include?("EntityDescriptor")
160
+ return :alias if symlink?
161
+ return :entity
162
+ end
163
+ :unknown
164
+ end
165
+
166
+ def xml_doc
167
+ Nokogiri::XML.parse(data).remove_namespaces!
168
+ end
169
+
170
+ def extract_entity_id
171
+ xml_doc.xpath("/EntityDescriptor/@entityID").text
172
+ end
173
+
174
+ def extract_entity_ids
175
+ xml_doc.xpath("//EntityDescriptor/@entityID").map(&:text)
176
+ end
138
177
 
139
178
  end
140
179