Add comments

This commit is contained in:
Brandon Robins
2017-12-17 12:56:24 -06:00
parent 25101301cf
commit 560c1ef994
3 changed files with 88 additions and 15 deletions

View File

@@ -23,35 +23,54 @@ require 'calligraphy/put'
require 'calligraphy/unlock' require 'calligraphy/unlock'
module Calligraphy module Calligraphy
# Constants used throughout Calligraphy.
DAV_NS = 'DAV:' DAV_NS = 'DAV:'
DAV_NO_LOCK_REGEX = /DAV:no-lock/i DAV_NO_LOCK_REGEX = /DAV:no-lock/i
DAV_NOT_NO_LOCK_REGEX = /Not\s+<DAV:no-lock>/i DAV_NOT_NO_LOCK_REGEX = /Not\s+<DAV:no-lock>/i
ETAG_IF_REGEX = /\[(.+?)\]/ ETAG_IF_REGEX = /\[(.+?)\]/
INFINITY = 1.0 / 0.0 unless defined? INFINITY
LOCK_TOKEN_ANGLE_REGEX = /[<>]/ LOCK_TOKEN_ANGLE_REGEX = /[<>]/
LOCK_TOKEN_REGEX = /<(urn:uuid:.+?)>/ LOCK_TOKEN_REGEX = /<(urn:uuid:.+?)>/
RESOURCE_REGEX = /^<+(.+?)>\s/ RESOURCE_REGEX = /^<+(.+?)>\s/
TAGGED_LIST_REGEX = /\)\s</ TAGGED_LIST_REGEX = /\)\s</
UNTAGGAGED_LIST_REGEX = /\)\s\(/ UNTAGGAGED_LIST_REGEX = /\)\s\(/
mattr_accessor :allowed_methods # HTTP methods allowed by the WebDavRequestsController.
@@allowed_methods = %w( mattr_accessor :allowed_http_methods
options head get put delete copy move mkcol propfind proppatch lock unlock @@allowed_http_methods = %w(
options head get put delete copy
move mkcol propfind proppatch lock unlock
) )
# Proc responsible for returning the user's password, API key,
# or HA1 digest hash so that Rails can check user credentials.
# Should be overridden to handle your particular application's
# user and/or authentication setup.
mattr_accessor :digest_password_procedure mattr_accessor :digest_password_procedure
@@digest_password_procedure = Proc.new { |x| 'changeme!' } @@digest_password_procedure = Proc.new { |username| 'changeme!' }
# If Digest Authentication is enabled by default.
mattr_accessor :enable_digest_authentication mattr_accessor :enable_digest_authentication
@@enable_digest_authentication = false @@enable_digest_authentication = false
# The realm used in HTTP Basic Authentication.
mattr_accessor :http_authentication_realm
@@http_authentication_realm = 'Application'
# Maximum lock lifetime in seconds.
mattr_accessor :lock_timeout_period mattr_accessor :lock_timeout_period
@@lock_timeout_period = 24 * 60 * 60 @@lock_timeout_period = 24 * 60 * 60
# The HTTP actions Calligraphy is responsible for handling.
mattr_accessor :web_dav_actions mattr_accessor :web_dav_actions
@@web_dav_actions = %i( @@web_dav_actions = %i(
options get put delete copy move mkcol propfind proppatch lock unlock options get put delete copy move
mkcol propfind proppatch lock unlock
) )
# Default way to set up Calligraphy.
# Run `rails generate calligraphy_install` to generate a
# fresh initializer with all configuration values.
def self.configure def self.configure
yield self yield self
end end

View File

@@ -1,61 +1,85 @@
module ActionDispatch::Routing module ActionDispatch::Routing
class Mapper class Mapper
module HttpHelpers module HttpHelpers
# Define a Calligraphy route that only recognizes HTTP COPY.
# copy 'bacon', to: 'food#bacon'
def copy(*args, &block) def copy(*args, &block)
args = set_web_dav_args args args = set_web_dav_args args
map_method :copy, args, &block map_method :copy, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP HEAD.
# head 'bacon', to: 'food#bacon'
def head(*args, &block) def head(*args, &block)
args = set_web_dav_args args args = set_web_dav_args args
map_method :head, args, &block map_method :head, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP LOCK.
# lock 'bacon', to: 'food#bacon'
def lock(*args, &block) def lock(*args, &block)
args = set_web_dav_args args args = set_web_dav_args args
map_method :lock, args, &block map_method :lock, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP MKCOL.
# mkcol 'bacon', to: 'food#bacon'
def mkcol(*args, &block) def mkcol(*args, &block)
args = set_web_dav_args args args = set_web_dav_args args
map_method :mkcol, args, &block map_method :mkcol, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP MOVE.
# move 'bacon', to: 'food#bacon'
def move(*args, &block) def move(*args, &block)
args = set_web_dav_args args args = set_web_dav_args args
map_method :move, args, &block map_method :move, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP OPTIONS.
# options 'bacon', to: 'food#bacon'
def options(*args, &block) def options(*args, &block)
args = set_web_dav_args args args = set_web_dav_args args
map_method :options, args, &block map_method :options, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP PROPFIND.
# propfind 'bacon', to: 'food#bacon'
def propfind(*args, &block) def propfind(*args, &block)
args = set_web_dav_args args args = set_web_dav_args args
map_method :propfind, args, &block map_method :propfind, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP PROPPATCH.
# proppatch 'bacon', to: 'food#bacon'
def proppatch(*args, &block) def proppatch(*args, &block)
args = set_web_dav_args args args = set_web_dav_args args
map_method :proppatch, args, &block map_method :proppatch, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP UNLOCK.
# unlock 'bacon', to: 'food#bacon'
def unlock(*args, &block) def unlock(*args, &block)
args = set_web_dav_args args args = set_web_dav_args args
map_method :unlock, args, &block map_method :unlock, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP DELETE.
# web_dav_delete 'broccoli', to: 'food#broccoli'
def web_dav_delete(*args, &block) def web_dav_delete(*args, &block)
args = set_web_dav_args args args = set_web_dav_args args
map_method :delete, args, &block map_method :delete, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP GET.
# web_dav_get 'bacon', to: 'food#bacon'
def web_dav_get(*args, &block) def web_dav_get(*args, &block)
args = set_web_dav_args args args = set_web_dav_args args
map_method :get, args, &block map_method :get, args, &block
end end
# Define a Calligraphy route that only recognizes HTTP PUT.
# web_dav_put 'bacon', to: 'food#bacon'
def web_dav_put(*args, &block) def web_dav_put(*args, &block)
args = set_web_dav_args args args = set_web_dav_args args
map_method :put, args, &block map_method :put, args, &block
@@ -64,9 +88,10 @@ module ActionDispatch::Routing
private private
def set_web_dav_args(args) def set_web_dav_args(args)
options = {} options = {
options[:controller] = 'calligraphy/rails/web_dav_requests' controller: 'calligraphy/rails/web_dav_requests',
options[:action] = 'invoke_method' action: 'invoke_method'
}
[args[0], options] [args[0], options]
end end
end end
@@ -84,6 +109,26 @@ module ActionDispatch::Routing
end end
end end
# With Calligraphy, a resourceful route provides mappings between WebDAV
# HTTP verbs and URLs and WebDAV controller actions. A single entry in
# the routing file, such as:
#
# calligraphy_resource :photos
#
# creates eleven different routes in your application, all mapping to the
# WebDavRequests controller:
#
# OPTIONS /photos/*resource
# GET /photos/*resource
# PUT /photos/*resource
# DELETE /photos/*resource
# COPY /photos/*resource
# MOVE /photos/*resource
# MKCOL /photos/*resource
# PROPFIND /photos/*resource
# PROPPATCH /photos/*resource
# LOCK /photos/*resource
# UNLOCK /photos/*resource
def calligraphy_resource(*resources, &block) def calligraphy_resource(*resources, &block)
options = resources.extract_options!.dup options = resources.extract_options!.dup
@@ -94,6 +139,7 @@ module ActionDispatch::Routing
with_scope_level(:resource) do with_scope_level(:resource) do
options = apply_action_options options options = apply_action_options options
singleton_resoure = ActionDispatch::Routing::Mapper::SingletonResource singleton_resoure = ActionDispatch::Routing::Mapper::SingletonResource
resource_scope(singleton_resoure.new resources.pop, api_only?, @scope[:shallow], options) do resource_scope(singleton_resoure.new resources.pop, api_only?, @scope[:shallow], options) do
yield if block_given? yield if block_given?
@@ -108,6 +154,9 @@ module ActionDispatch::Routing
def set_mappings_for_web_dav_resources def set_mappings_for_web_dav_resources
parent_resource.web_dav_actions.each do |action| parent_resource.web_dav_actions.each do |action|
# Rails already defines GET, PUT, and DELETE actions which we don't
# want to override. Instead, we map WebDAV GET, PUT, and DELETE
# HTTP actions to 'web_dav_' prefixed methods.
if [:get, :put, :delete].include? action if [:get, :put, :delete].include? action
send "web_dav_#{action.to_s}", '*resource' send "web_dav_#{action.to_s}", '*resource'
else else

View File

@@ -4,13 +4,16 @@ module Calligraphy::Rails
before_action :authenticate_with_digest_authentiation before_action :authenticate_with_digest_authentiation
before_action :set_resource before_action :set_resource
# Entry-point for all WebDAV requests. Handles checking and validating
# preconditions, directing of requests to the proper WebDAV action
# method, and composing responses to send back to the client.
def invoke_method def invoke_method
method = request.request_method.downcase method = request.request_method.downcase
if check_preconditions if check_preconditions
if method == 'head' if method == 'head'
status = get head: true status = get head: true
elsif Calligraphy.allowed_methods.include? method elsif Calligraphy.allowed_http_methods.include? method
set_resource_client_nonce(method) if Calligraphy.enable_digest_authentication set_resource_client_nonce(method) if Calligraphy.enable_digest_authentication
status, body = send method status, body = send method
@@ -27,14 +30,16 @@ module Calligraphy::Rails
private private
def verify_resource_scope def verify_resource_scope
head :forbidden if params[:resource].include? '..' head :forbidden if %w(. ..).any? { |seg| params[:resource].include? seg }
end end
def authenticate_with_digest_authentiation def authenticate_with_digest_authentiation
if Calligraphy.enable_digest_authentication return unless Calligraphy.enable_digest_authentication
authenticate_or_request_with_http_digest do |username|
Calligraphy.digest_password_procedure.call(username) realm = Calligraphy.http_authentication_realm
end
authenticate_or_request_with_http_digest(realm) do |username|
Calligraphy.digest_password_procedure.call(username)
end end
end end
@@ -58,8 +63,8 @@ module Calligraphy::Rails
def evaluate_if_header def evaluate_if_header
conditions_met = false conditions_met = false
condition_lists = get_if_conditions condition_lists = get_if_conditions
condition_lists.each do |list| condition_lists.each do |list|
conditions = parse_preconditions list conditions = parse_preconditions list