dor-rights-auth 1.0.1 → 1.0.2
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/lib/dor/rights/auth.rb +1 -0
- data/lib/dor/rights_auth.rb +77 -94
- metadata +6 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 40f40de949255d9644bb67069e570735acfa4b5b
|
|
4
|
+
data.tar.gz: 7691335eecd9d5cf68b6fa70609c782c7114f566
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f71d100e9c23464dc50014d8749589f8c69ca46137eb60cfb82cbf5b3caa9cad0c5b99130a5bad2133c5e45d735c54ff3b6203f092105ecf1335c8da6960a770
|
|
7
|
+
data.tar.gz: 9278835bc26a8774acfccc5e41f8cb9cfc58750832db255f84444e1f3180fa31fb0401775dab9f57ecbbead7ec1798b58eda873f694d98384b52f2123c2a44b7
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'dor/rights_auth'
|
data/lib/dor/rights_auth.rb
CHANGED
|
@@ -2,9 +2,8 @@ require 'nokogiri'
|
|
|
2
2
|
require 'time'
|
|
3
3
|
|
|
4
4
|
# We handle the ugly stuff, so you don't have to.
|
|
5
|
-
|
|
6
5
|
module Dor
|
|
7
|
-
|
|
6
|
+
#
|
|
8
7
|
# The Individual Right
|
|
9
8
|
Rights = Struct.new(:value, :rule)
|
|
10
9
|
|
|
@@ -44,7 +43,7 @@ module Dor
|
|
|
44
43
|
|
|
45
44
|
# Returns true if the object is under embargo.
|
|
46
45
|
# @return [Boolean]
|
|
47
|
-
def embargoed?
|
|
46
|
+
def embargoed? # rubocop:disable Style/TrivialAccessors
|
|
48
47
|
@embargoed
|
|
49
48
|
end
|
|
50
49
|
|
|
@@ -57,7 +56,7 @@ module Dor
|
|
|
57
56
|
alias_method :public_unrestricted?, :world_unrestricted?
|
|
58
57
|
|
|
59
58
|
def readable?
|
|
60
|
-
public_unrestricted? || stanford_only_unrestricted? # TODO stanford_only or public with rule, figure out if this is still a legit method
|
|
59
|
+
public_unrestricted? || stanford_only_unrestricted? # TODO: stanford_only or public with rule, figure out if this is still a legit method
|
|
61
60
|
end
|
|
62
61
|
|
|
63
62
|
# Returns true if the object is stanford-only readable AND has no rule attribute
|
|
@@ -70,7 +69,7 @@ module Dor
|
|
|
70
69
|
# @param [String] agent_name Name of the agent that wants to access this object
|
|
71
70
|
# @return [Boolean]
|
|
72
71
|
def agent_unrestricted?(agent_name)
|
|
73
|
-
return false unless @obj_lvl.agent.
|
|
72
|
+
return false unless @obj_lvl.agent.key? agent_name
|
|
74
73
|
@obj_lvl.agent[agent_name].value && @obj_lvl.agent[agent_name].rule.nil?
|
|
75
74
|
end
|
|
76
75
|
|
|
@@ -82,7 +81,7 @@ module Dor
|
|
|
82
81
|
# @param [String] file_name Name of the file that is tested for stanford_only rights
|
|
83
82
|
# @return [Boolean]
|
|
84
83
|
def stanford_only_unrestricted_file?(file_name)
|
|
85
|
-
return stanford_only_unrestricted? if
|
|
84
|
+
return stanford_only_unrestricted? if @file[file_name].nil? || @file[file_name].group[:stanford].nil?
|
|
86
85
|
|
|
87
86
|
@file[file_name].group[:stanford].value && @file[file_name].group[:stanford].rule.nil?
|
|
88
87
|
end
|
|
@@ -93,7 +92,7 @@ module Dor
|
|
|
93
92
|
# @param [String] file_name Name of file that is tested for world rights
|
|
94
93
|
# @return [Boolean]
|
|
95
94
|
def world_unrestricted_file?(file_name)
|
|
96
|
-
return world_unrestricted? if
|
|
95
|
+
return world_unrestricted? if @file[file_name].nil? || @file[file_name].world.nil?
|
|
97
96
|
|
|
98
97
|
@file[file_name].world.value && @file[file_name].world.rule.nil?
|
|
99
98
|
end
|
|
@@ -123,7 +122,7 @@ module Dor
|
|
|
123
122
|
# agent_exists, agent_rule = rights.agent_rights('someapp')
|
|
124
123
|
# @note should be called after doing a check for world_unrestricted?
|
|
125
124
|
def agent_rights(agent_name)
|
|
126
|
-
return [false, nil] if
|
|
125
|
+
return [false, nil] if @obj_lvl.agent[agent_name].nil?
|
|
127
126
|
[@obj_lvl.agent[agent_name].value, @obj_lvl.agent[agent_name].rule]
|
|
128
127
|
end
|
|
129
128
|
|
|
@@ -135,7 +134,7 @@ module Dor
|
|
|
135
134
|
# @example Using multiple variable assignment to read both array elements
|
|
136
135
|
# world_exists, world_rule = rights.world_rights_for_file('somefile')
|
|
137
136
|
def world_rights_for_file(file_name)
|
|
138
|
-
return world_rights if
|
|
137
|
+
return world_rights if @file[file_name].nil? || @file[file_name].world.nil?
|
|
139
138
|
|
|
140
139
|
[@file[file_name].world.value, @file[file_name].world.rule]
|
|
141
140
|
end
|
|
@@ -148,7 +147,7 @@ module Dor
|
|
|
148
147
|
# @example Using multiple variable assignment to read both array elements
|
|
149
148
|
# su_only_exists, su_only_rule = rights.stanford_only_rights_for_file('somefile')
|
|
150
149
|
def stanford_only_rights_for_file(file_name)
|
|
151
|
-
return stanford_only_rights if
|
|
150
|
+
return stanford_only_rights if @file[file_name].nil? || @file[file_name].group[:stanford].nil?
|
|
152
151
|
|
|
153
152
|
[@file[file_name].group[:stanford].value, @file[file_name].group[:stanford].rule]
|
|
154
153
|
end
|
|
@@ -161,87 +160,81 @@ module Dor
|
|
|
161
160
|
# @example Using multiple variable assignment to read both array elements
|
|
162
161
|
# agent_exists, agent_rule = rights.agent_rights_for_file('filex', 'someapp')
|
|
163
162
|
def agent_rights_for_file(file_name, agent_name)
|
|
164
|
-
return agent_rights(agent_name) if
|
|
163
|
+
return agent_rights(agent_name) if @file[file_name].nil? # look at object level agent rights if the file-name is not stored
|
|
165
164
|
|
|
166
|
-
return [false, nil] if
|
|
165
|
+
return [false, nil] if @file[file_name].agent[agent_name].nil? # file rules exist, but not for this agent
|
|
167
166
|
|
|
168
167
|
[@file[file_name].agent[agent_name].value, @file[file_name].agent[agent_name].rule]
|
|
169
168
|
end
|
|
170
169
|
|
|
171
170
|
# Check formedness of rightsMetadata -- to be replaced with XSD once formalized, one fine day
|
|
172
|
-
# @param [Nokogiri::XML] the rightsMetadata document
|
|
171
|
+
# @param [Nokogiri::XML] doc the rightsMetadata document
|
|
173
172
|
# @return [Array] list of things that are wrong with it
|
|
174
|
-
def
|
|
175
|
-
if
|
|
176
|
-
return ["no_rightsMetadata"]
|
|
177
|
-
end
|
|
173
|
+
def self.validate_lite(doc)
|
|
174
|
+
return ['no_rightsMetadata'] if doc.nil? || doc.at_xpath('//rightsMetadata').nil?
|
|
178
175
|
errors = []
|
|
179
176
|
maindiscover = doc.at_xpath("//rightsMetadata/access[@type='discover' and not(file)]")
|
|
180
177
|
mainread = doc.at_xpath("//rightsMetadata/access[@type='read' and not(file)]")
|
|
181
178
|
|
|
182
179
|
if maindiscover.nil?
|
|
183
|
-
errors.push
|
|
184
|
-
elsif maindiscover.at_xpath(
|
|
185
|
-
errors.push
|
|
186
|
-
elsif
|
|
187
|
-
errors.push
|
|
180
|
+
errors.push 'no_discover_access', 'no_discover_machine'
|
|
181
|
+
elsif maindiscover.at_xpath('./machine').nil?
|
|
182
|
+
errors.push 'no_discover_machine'
|
|
183
|
+
elsif maindiscover.at_xpath('./machine/world[not(@rule)]').nil? && maindiscover.at_xpath('./machine/none').nil?
|
|
184
|
+
errors.push 'discover_machine_unrecognized'
|
|
188
185
|
end
|
|
189
186
|
if mainread.nil?
|
|
190
|
-
errors.push
|
|
191
|
-
elsif mainread.at_xpath(
|
|
192
|
-
errors.push
|
|
193
|
-
|
|
194
|
-
|
|
187
|
+
errors.push 'no_read_access', 'no_read_machine'
|
|
188
|
+
elsif mainread.at_xpath('./machine').nil?
|
|
189
|
+
errors.push 'no_read_machine'
|
|
190
|
+
# else
|
|
191
|
+
# TODO: deeper read validation?
|
|
195
192
|
end
|
|
196
193
|
|
|
197
194
|
errors
|
|
198
195
|
end
|
|
199
196
|
|
|
200
197
|
# Assemble various characterizing terms for index from XML
|
|
201
|
-
# @param [Nokogiri::XML] the rightsMetadata document
|
|
202
|
-
# @return [Array] Strings of interest to the Solr index
|
|
203
|
-
def
|
|
198
|
+
# @param [Nokogiri::XML] doc the rightsMetadata document
|
|
199
|
+
# @return [Array<String>] Strings of interest to the Solr index
|
|
200
|
+
def self.extract_index_terms(doc)
|
|
204
201
|
terms = []
|
|
205
202
|
machine = doc.at_xpath("//rightsMetadata/access[@type='read' and not(file)]/machine")
|
|
206
|
-
terms.push
|
|
207
|
-
terms.push
|
|
203
|
+
terms.push 'none_discover' if doc.at_xpath("//rightsMetadata/access[@type='discover']/machine/none")
|
|
204
|
+
terms.push 'world_discover' if doc.at_xpath("//rightsMetadata/access[@type='discover']/machine/world[not(@rule)]")
|
|
208
205
|
return terms if machine.nil?
|
|
209
206
|
|
|
210
|
-
terms.push
|
|
211
|
-
terms.push
|
|
207
|
+
terms.push 'has_group_rights' if machine.at_xpath('./group')
|
|
208
|
+
terms.push 'has_rule' if machine.at_xpath('.//@rule')
|
|
212
209
|
|
|
213
|
-
if
|
|
214
|
-
terms.push
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
terms.push "group|#{machine.at_xpath("./group").value.downcase}"
|
|
210
|
+
if machine.at_xpath("./group[#{CONTAINS_STANFORD_XPATH}]")
|
|
211
|
+
terms.push 'group|stanford'
|
|
212
|
+
terms.push 'group|stanford_with_rule' if machine.at_xpath("./group[@rule and #{CONTAINS_STANFORD_XPATH}]")
|
|
213
|
+
elsif machine.at_xpath('./group')
|
|
214
|
+
terms.push "group|#{machine.at_xpath('./group').value.downcase}"
|
|
219
215
|
end
|
|
220
216
|
|
|
221
|
-
if
|
|
222
|
-
terms.push
|
|
223
|
-
elsif
|
|
224
|
-
terms.push
|
|
225
|
-
|
|
226
|
-
terms.push "world|#{machine.at_xpath("./world/@rule").value.downcase}"
|
|
217
|
+
if machine.at_xpath('./none')
|
|
218
|
+
terms.push 'none_read'
|
|
219
|
+
elsif machine.at_xpath('./world')
|
|
220
|
+
terms.push 'world_read'
|
|
221
|
+
terms.push "world|#{machine.at_xpath('./world/@rule').value.downcase}" if machine.at_xpath('./world/@rule')
|
|
227
222
|
end
|
|
228
223
|
|
|
229
224
|
# now some statistical generation
|
|
230
|
-
names = machine.element_children.collect
|
|
231
|
-
kidcount = names.each_with_object(Hash.new(0)){ |word,counts| counts[word] += 1 }
|
|
232
|
-
countphrase = kidcount.sort.collect{|k,v| "#{k}#{v}"}.join(
|
|
233
|
-
terms.push
|
|
225
|
+
names = machine.element_children.collect(&:name)
|
|
226
|
+
kidcount = names.each_with_object(Hash.new(0)) { |word, counts| counts[word] += 1 }
|
|
227
|
+
countphrase = kidcount.sort.collect { |k, v| "#{k}#{v}" }.join('|')
|
|
228
|
+
terms.push 'profile:' + countphrase unless countphrase.empty?
|
|
234
229
|
|
|
235
230
|
filemachines = doc.xpath("//rightsMetadata/access[@type='read' and file]/machine")
|
|
236
231
|
unless filemachines.empty?
|
|
237
|
-
terms.push
|
|
232
|
+
terms.push 'has_file_rights', "file_rights_count|#{filemachines.count}"
|
|
238
233
|
counts = Hash.new(0)
|
|
239
234
|
filemachines.each { |filemachine|
|
|
240
|
-
filemachine.element_children.each { |node|
|
|
241
|
-
counts[node.name] += 1
|
|
242
|
-
}
|
|
235
|
+
filemachine.element_children.each { |node| counts[node.name] += 1 }
|
|
243
236
|
}
|
|
244
|
-
counts.each { |k,v|
|
|
237
|
+
counts.each { |k, v|
|
|
245
238
|
terms.push "file_has_#{k}", "file_rights_for_#{k}|#{v}"
|
|
246
239
|
}
|
|
247
240
|
end
|
|
@@ -250,14 +243,14 @@ module Dor
|
|
|
250
243
|
end
|
|
251
244
|
|
|
252
245
|
# Give the index what it needs
|
|
253
|
-
# @param [Nokogiri::XML] the rightsMetadata document
|
|
254
|
-
# @return [Hash] Strings of interest to the Solr index, including:
|
|
246
|
+
# @param [Nokogiri::XML] doc the rightsMetadata document
|
|
247
|
+
# @return [Hash{Symbol => Object}] Strings of interest to the Solr index, including:
|
|
255
248
|
# {
|
|
256
249
|
# :primary => '...', # string of foremost rights category, if determinable
|
|
257
250
|
# :errors => [...], # known error cases
|
|
258
251
|
# :terms => [...] # array of non-error characterizations and stats strings
|
|
259
252
|
# }
|
|
260
|
-
def
|
|
253
|
+
def self.extract_access_rights(doc)
|
|
261
254
|
errors = validate_lite(doc)
|
|
262
255
|
stuff = {
|
|
263
256
|
:primary => nil,
|
|
@@ -265,65 +258,55 @@ module Dor
|
|
|
265
258
|
:terms => []
|
|
266
259
|
}
|
|
267
260
|
|
|
268
|
-
if
|
|
261
|
+
if errors.include? 'no_rightsMetadata'
|
|
269
262
|
stuff[:primary] = 'dark'
|
|
270
|
-
return stuff
|
|
263
|
+
return stuff # short circuit if no metadata -- no point going on
|
|
271
264
|
end
|
|
272
265
|
|
|
273
266
|
stuff[:terms] = extract_index_terms(doc)
|
|
267
|
+
has_rule = stuff[:terms].include? 'has_rule'
|
|
274
268
|
|
|
275
|
-
if
|
|
269
|
+
if stuff[:terms].include?('none_discover')
|
|
276
270
|
stuff[:primary] = 'dark'
|
|
277
|
-
elsif
|
|
271
|
+
elsif errors.include?('no_discover_access') || errors.include?('no_discover_machine')
|
|
278
272
|
stuff[:primary] = 'dark'
|
|
279
|
-
elsif
|
|
273
|
+
elsif errors.include?('no_read_machine') || stuff[:terms].include?('none_read')
|
|
280
274
|
stuff[:primary] = 'citation'
|
|
281
|
-
elsif
|
|
282
|
-
stuff[:primary] = 'world'
|
|
283
|
-
elsif
|
|
284
|
-
stuff[:primary] = 'stanford'
|
|
285
|
-
|
|
286
|
-
stuff[:primary] = '
|
|
275
|
+
elsif stuff[:terms].include? 'world_read'
|
|
276
|
+
stuff[:primary] = has_rule ? 'world_qualified' : 'world'
|
|
277
|
+
elsif stuff[:terms].include? 'group|stanford'
|
|
278
|
+
stuff[:primary] = has_rule ? 'stanford_qualified' : 'stanford'
|
|
279
|
+
else # should never happen, but we might as well note it if it does
|
|
280
|
+
stuff[:primary] = has_rule ? 'UNKNOWN_qualified' : 'UNKNOWN'
|
|
287
281
|
end
|
|
288
282
|
stuff
|
|
289
283
|
end
|
|
290
284
|
|
|
291
|
-
|
|
292
285
|
# Create a Dor::RightsAuth object from xml
|
|
293
286
|
# @param [String|Nokogiri::XML::Document] xml rightsMetadata xml that will be parsed to build a RightsAuth object
|
|
294
287
|
# @param [Boolean] forindex, flag for requesting index_elements be parsed
|
|
295
288
|
# @return [Dor::RightsAuth] object created after parsing rightsMetadata xml
|
|
296
|
-
def
|
|
289
|
+
def self.parse(xml, forindex = false)
|
|
297
290
|
rights = Dor::RightsAuth.new
|
|
298
291
|
rights.obj_lvl = EntityRights.new
|
|
299
292
|
rights.obj_lvl.world = Rights.new
|
|
300
293
|
|
|
301
294
|
doc = xml.is_a?(Nokogiri::XML::Document) ? xml.clone : Nokogiri::XML(xml)
|
|
302
|
-
if
|
|
295
|
+
if doc.at_xpath("//rightsMetadata/access[@type='read' and not(file)]/machine/world")
|
|
303
296
|
rights.obj_lvl.world.value = true
|
|
304
297
|
rule = doc.at_xpath("//rightsMetadata/access[@type='read' and not(file)]/machine/world/@rule")
|
|
305
|
-
rights.obj_lvl.world.rule = rule.value if
|
|
298
|
+
rights.obj_lvl.world.rule = rule.value if rule
|
|
306
299
|
else
|
|
307
300
|
rights.obj_lvl.world.value = false
|
|
308
301
|
end
|
|
309
302
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
# rights[:readable] = true
|
|
313
|
-
# else
|
|
314
|
-
# rights[:readable] = false
|
|
315
|
-
# end
|
|
316
|
-
|
|
317
|
-
rights.obj_lvl.group = { :stanford => Rights.new }
|
|
318
|
-
|
|
319
|
-
if (forindex)
|
|
320
|
-
rights.index_elements = extract_access_rights(doc)
|
|
321
|
-
end
|
|
303
|
+
rights.obj_lvl.group = { :stanford => Rights.new }
|
|
304
|
+
rights.index_elements = extract_access_rights(doc) if forindex
|
|
322
305
|
|
|
323
|
-
if
|
|
306
|
+
if doc.at_xpath("//rightsMetadata/access[@type='read' and not(file)]/machine/group[#{CONTAINS_STANFORD_XPATH}]")
|
|
324
307
|
rights.obj_lvl.group[:stanford].value = true
|
|
325
308
|
rule = doc.at_xpath("//rightsMetadata/access[@type='read' and not(file)]/machine/group[#{CONTAINS_STANFORD_XPATH}]/@rule")
|
|
326
|
-
rights.obj_lvl.group[:stanford].rule = rule.value if
|
|
309
|
+
rights.obj_lvl.group[:stanford].rule = rule.value if rule
|
|
327
310
|
else
|
|
328
311
|
rights.obj_lvl.group[:stanford].value = false
|
|
329
312
|
end
|
|
@@ -339,9 +322,9 @@ module Dor
|
|
|
339
322
|
# Initialze embargo_status to false
|
|
340
323
|
rights.embargoed = false
|
|
341
324
|
embargo_node = doc.at_xpath("//rightsMetadata/access[@type='read']/machine/embargoReleaseDate")
|
|
342
|
-
if
|
|
325
|
+
if embargo_node
|
|
343
326
|
embargo_dt = Time.parse(embargo_node.content)
|
|
344
|
-
rights.embargoed = true if
|
|
327
|
+
rights.embargoed = true if embargo_dt > Time.now
|
|
345
328
|
end
|
|
346
329
|
|
|
347
330
|
access_with_files = doc.xpath("//rightsMetadata/access[@type='read' and file]")
|
|
@@ -351,21 +334,21 @@ module Dor
|
|
|
351
334
|
if access_node.at_xpath("machine/group[#{CONTAINS_STANFORD_XPATH}]")
|
|
352
335
|
stanford_access.value = true
|
|
353
336
|
rule = access_node.at_xpath("machine/group[#{CONTAINS_STANFORD_XPATH}]/@rule")
|
|
354
|
-
stanford_access.rule = rule.value if
|
|
337
|
+
stanford_access.rule = rule.value if rule
|
|
355
338
|
else
|
|
356
339
|
stanford_access.value = false
|
|
357
340
|
end
|
|
358
341
|
|
|
359
|
-
if access_node.at_xpath(
|
|
342
|
+
if access_node.at_xpath('machine/world')
|
|
360
343
|
world_access.value = true
|
|
361
|
-
rule = access_node.at_xpath(
|
|
362
|
-
world_access.rule = rule.value if
|
|
344
|
+
rule = access_node.at_xpath('machine/world/@rule')
|
|
345
|
+
world_access.rule = rule.value if rule
|
|
363
346
|
else
|
|
364
347
|
world_access.value = false
|
|
365
348
|
end
|
|
366
349
|
|
|
367
350
|
file_agents = {}
|
|
368
|
-
access_node.xpath(
|
|
351
|
+
access_node.xpath('machine/agent').each do |node|
|
|
369
352
|
r = Rights.new
|
|
370
353
|
r.value = true
|
|
371
354
|
r.rule = node['rule']
|
metadata
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dor-rights-auth
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Willy Mene
|
|
8
|
+
- Joe Atzberger
|
|
8
9
|
autorequire:
|
|
9
10
|
bindir: bin
|
|
10
11
|
cert_chain: []
|
|
11
|
-
date: 2015-
|
|
12
|
+
date: 2015-12-11 00:00:00.000000000 Z
|
|
12
13
|
dependencies:
|
|
13
14
|
- !ruby/object:Gem::Dependency
|
|
14
15
|
name: nokogiri
|
|
@@ -69,10 +70,12 @@ dependencies:
|
|
|
69
70
|
description: Parses rightsMetadata xml into a useable object
|
|
70
71
|
email:
|
|
71
72
|
- wmene@stanford.edu
|
|
73
|
+
- atz@stanford.edu
|
|
72
74
|
executables: []
|
|
73
75
|
extensions: []
|
|
74
76
|
extra_rdoc_files: []
|
|
75
77
|
files:
|
|
78
|
+
- lib/dor/rights/auth.rb
|
|
76
79
|
- lib/dor/rights_auth.rb
|
|
77
80
|
- lib/dor/xsd/druid.xsd
|
|
78
81
|
- lib/dor/xsd/druidlist.xsd
|
|
@@ -100,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
100
103
|
version: 1.3.6
|
|
101
104
|
requirements: []
|
|
102
105
|
rubyforge_project:
|
|
103
|
-
rubygems_version: 2.
|
|
106
|
+
rubygems_version: 2.4.6
|
|
104
107
|
signing_key:
|
|
105
108
|
specification_version: 4
|
|
106
109
|
summary: Parses rightsMetadata xml into a useable object
|