Add support for allprop and propname

This commit is contained in:
Brandon Robins
2018-01-30 22:59:55 -06:00
parent 13b5dbc989
commit 9f21799206
4 changed files with 203 additions and 37 deletions

View File

@@ -6,9 +6,9 @@ module Calligraphy
# Resource responsible for writing and deleting directories and files to disk. # Resource responsible for writing and deleting directories and files to disk.
class FileResource < Resource class FileResource < Resource
DAV_PROPERTY_METHODS = %w[ DAV_PROPERTY_METHODS = %w[
creationdate displayname getcontentlanguage getcontentlength allprop creationdate displayname getcontentlanguage getcontentlength
getcontenttype getetag getlastmodified lockdiscovery resourcetype getcontenttype getetag getlastmodified lockdiscovery propname
supportedlock resourcetype supportedlock
].freeze ].freeze
include Calligraphy::Utils include Calligraphy::Utils
@@ -174,15 +174,7 @@ module Calligraphy
def propfind(nodes) def propfind(nodes)
properties = { found: [], not_found: [] } properties = { found: [], not_found: [] }
nodes.each do |node| find_properties_from_xml_elements nodes, properties
node.children.each do |prop|
next unless prop.is_a? Nokogiri::XML::Element
value = get_property prop
update_found_properties properties, prop, value
end
end
properties[:found] = properties[:found].uniq.flatten if properties[:found] properties[:found] = properties[:found].uniq.flatten if properties[:found]
properties properties
@@ -507,6 +499,30 @@ module Calligraphy
(lock_info[:check_creator] && (lock_info[:creator] == client_nonce)) (lock_info[:check_creator] && (lock_info[:creator] == client_nonce))
end end
def find_properties_from_xml_elements(nodes, properties)
nodes.each do |node|
next unless node.is_a? Nokogiri::XML::Element
if node.children.length.positive?
find_properties_from_property_nodes node, properties
else
value = get_property node
update_found_properties properties, node, value
end
end
end
def find_properties_from_property_nodes(node, properties)
node.children.each do |prop|
next unless prop.is_a? Nokogiri::XML::Element
value = get_property prop
update_found_properties properties, prop, value
end
end
def ancestor_lock_tokens(lock_info) def ancestor_lock_tokens(lock_info)
lock_info[:lock].each { |x| x }.map { |k| k[:locktoken].children[0].text } lock_info[:lock].each { |x| x }.map { |k| k[:locktoken].children[0].text }
end end
@@ -521,55 +537,94 @@ module Calligraphy
def get_property(prop) def get_property(prop)
case prop.name case prop.name
when *DAV_PROPERTY_METHODS when *DAV_PROPERTY_METHODS
prop.content = send prop.name send prop.name, prop
prop
else else
get_custom_property prop.name, deserialize: true get_custom_property prop.name, deserialize: true
end end
end end
def creationdate def allprop(_prop)
@stats[:created_at] get_custom_property nil, deserialize: true
{}.tap do |properties|
@store_properties.each_value do |node|
next unless node.is_a? Nokogiri::XML::Element
properties[node.name.to_sym] = node
end
end
end end
def displayname def creationdate(prop)
get_custom_property(:displayname) || @name prop.content = @stats[:created_at]
prop
end end
def getcontentlanguage def displayname(prop)
get_custom_property :contentlanguage prop.content = get_custom_property(:displayname) || @name
prop
end end
def getcontentlength def getcontentlanguage(prop)
@stats[:size] prop.content = get_custom_property :contentlanguage
prop
end end
def getcontenttype def getcontentlength(prop)
get_custom_property :contenttype prop.content = @stats[:size]
prop
end end
def getetag def getcontenttype(prop)
prop.content = get_custom_property :contenttype
prop
end
def getetag(prop)
cache_key = ActiveSupport::Cache.expand_cache_key [@resource.etag, ''] cache_key = ActiveSupport::Cache.expand_cache_key [@resource.etag, '']
"W/\"#{Digest::MD5.hexdigest(cache_key)}\""
prop.content = "W/\"#{Digest::MD5.hexdigest(cache_key)}\""
prop
end end
def getlastmodified def getlastmodified(prop)
@updated_at prop.content = @updated_at
prop
end end
def lockdiscovery # def include(prop)
fetch_lock_info # # TODO: Implement
# prop
# end
def lockdiscovery(prop)
prop.content = fetch_lock_info
prop
end end
def resourcetype def propname(_prop)
'collection' if collection? get_custom_property nil, deserialize: true
{}.tap do |properties|
@store_properties.each_value do |node|
next unless node.is_a? Nokogiri::XML::Element
properties[node.name.to_sym] = xml_node node.name
end
end
end end
def supportedlock def resourcetype(prop)
prop.content = 'collection' if collection?
prop
end
def supportedlock(prop)
exclusive_write = lockentry_hash('exclusive', 'write') exclusive_write = lockentry_hash('exclusive', 'write')
shared_write = lockentry_hash('shared', 'write') shared_write = lockentry_hash('shared', 'write')
JSON.generate [exclusive_write, shared_write] prop.content = JSON.generate [exclusive_write, shared_write]
prop
end end
def get_custom_property(prop, deserialize: false) def get_custom_property(prop, deserialize: false)
@@ -609,11 +664,10 @@ module Calligraphy
def add_properties(node, actions) def add_properties(node, actions)
node.children.each do |prop| node.children.each do |prop|
prop.children.each do |property| prop.children.each do |property|
next unless node.is_a? Nokogiri::XML::Element next unless property.is_a? Nokogiri::XML::Element
prop_sym = property.name.to_sym prop_sym = property.name.to_sym
store_property_node property.clone.serialize, prop_sym
store_property_node property.serialize, prop_sym
actions[:set].push property actions[:set].push property
end end

View File

@@ -0,0 +1,59 @@
# frozen_string_literal: false
require 'rails_helper'
require 'support/request_helpers'
require 'support/examples/propfind'
require 'support/examples/proppatch'
RSpec.describe 'PROPFIND', type: :request do
before(:all) do
tmp_dir = Rails.root.join('../../tmp').to_path
Dir.mkdir tmp_dir unless File.exists? tmp_dir
webdav_dir = Rails.root.join('../../tmp/webdav').to_path
FileUtils.rm_r webdav_dir if File.exists? webdav_dir
Dir.mkdir webdav_dir
end
before(:each) do
allow(Calligraphy).to receive(:enable_digest_authentication)
.and_return(false)
end
context 'with xml defintiion' do
before(:each) do
put '/webdav/bar.html', headers: {
RAW_POST_DATA: 'hello world'
}
proppatch '/webdav/bar.html', headers: {
RAW_POST_DATA: Support::Examples::Proppatch.rfc4918_9_2_2
}
end
describe 'allprop' do
it 'returns all property names and values' do
propfind '/webdav/bar.html', headers: {
RAW_POST_DATA: Support::Examples::Propfind.allprop
}
expect(response.status).to eq(207)
expect(response.body).to include('Authors')
expect(response.body).to include('Author>')
expect(response.body).to include('Jim')
expect(response.body).to include('Roy')
end
end
describe 'propname' do
it 'returns all property names' do
propfind '/webdav/bar.html', headers: {
RAW_POST_DATA: Support::Examples::Propfind.propname
}
expect(response.status).to eq(207)
expect(response.body).to include('Authors/')
expect(response.body).to_not include('Author/')
end
end
end
end

View File

@@ -0,0 +1,25 @@
# frozen_string_literal: false
module Support
module Examples
module Propfind
def self.allprop
<<~XML
<?xml version="1.0" encoding="utf-8" ?>
<D:propfind xmlns:D="DAV:">
<D:allprop/>
</D:propfind>
XML
end
def self.propname
<<~XML
<?xml version="1.0" encoding="utf-8" ?>
<D:propfind xmlns:D="DAV:">
<D:propname/>
</D:propfind>
XML
end
end
end
end

View File

@@ -0,0 +1,28 @@
# frozen_string_literal: false
module Support
module Examples
module Proppatch
# RFC4918: 9.2.2
def self.rfc4918_9_2_2
<<~XML
<?xml version="1.0" encoding="utf-8" ?>
<D:propertyupdate xmlns:D="DAV:"
xmlns:Z="http://ns.example.com/standards/z39.50/">
<D:set>
<D:prop>
<Z:Authors>
<Z:Author>Jim Whitehead</Z:Author>
<Z:Author>Roy Fielding</Z:Author>
</Z:Authors>
</D:prop>
</D:set>
<D:remove>
<D:prop><Z:Copyright-Owner/></D:prop>
</D:remove>
</D:propertyupdate>
XML
end
end
end
end