Rubocop WebDavRequestsController

Abstracts select methods from WebDavRequestsController to WebDavMethods
and WebDavPreconditions modules.
This commit is contained in:
Brandon Robins
2017-12-28 17:18:39 -06:00
parent 4d532e2c34
commit e32189716d
4 changed files with 264 additions and 193 deletions

View File

@@ -1,4 +1,6 @@
require 'calligraphy/rails/mapper'
require 'calligraphy/rails/web_dav_methods'
require 'calligraphy/rails/web_dav_preconditions'
require 'calligraphy/rails/web_dav_requests_controller'
require 'calligraphy/xml/builder'

View File

@@ -0,0 +1,67 @@
# frozen_string_literal: true
module Calligraphy
module Rails
# Provides methods to direct the execution of WebDAV actions.
module WebDavMethods
private
def web_dav_request
{
headers: request.headers,
request: request,
resource: @resource,
response: response
}
end
def options
response.headers['DAV'] = @resource.dav_compliance
:ok
end
def get(head: false)
fresh_when(@resource, etag: @resource.etag) if @resource.readable?
Calligraphy::Get.new(web_dav_request).execute(head: head)
end
def put
Calligraphy::Put.new(web_dav_request).execute
end
def delete
Calligraphy::Delete.new(web_dav_request).execute
end
def copy
Calligraphy::Copy.new(web_dav_request).execute
end
def move
Calligraphy::Move.new(web_dav_request).execute
end
def mkcol
Calligraphy::Mkcol.new(web_dav_request).execute
end
def propfind
Calligraphy::Propfind.new(web_dav_request).execute
end
def proppatch
Calligraphy::Proppatch.new(web_dav_request).execute
end
def lock
Calligraphy::Lock.new(web_dav_request).execute
end
def unlock
Calligraphy::Unlock.new(web_dav_request).execute
end
end
end
end

View File

@@ -0,0 +1,114 @@
# frozen_string_literal: true
module Calligraphy
module Rails
# Provides methods to handle checking and validating WebDAV request
# preconditions.
module WebDavPreconditions
private
def check_preconditions
return true unless request.headers['If'].present?
evaluate_if_header
end
def evaluate_if_header
conditions_met = false
condition_lists = if_conditions
condition_lists.each do |list|
conditions = parse_preconditions list
conditions_met = evaluate_preconditions conditions
break if conditions_met
end
conditions_met
end
def if_conditions
if request.headers['If'][0] == '<'
request.headers['If'].split Calligraphy::TAGGED_LIST_REGEX
else
request.headers['If'].split Calligraphy::UNTAGGAGED_LIST_REGEX
end
end
def parse_preconditions(list)
conditions = conditions_hash
conditions[:dav_no_lock] = match_dav_no_lock list
conditions[:resource] = scan_for_resource list
conditions[:lock_token] = scan_for_lock_token list
conditions[:etag] = scan_for_etag list
conditions
end
def conditions_hash
{
dav_no_lock: nil,
etag: nil,
lock_token: nil,
resource: nil
}
end
def match_dav_no_lock(list)
return nil unless list =~ Calligraphy::DAV_NO_LOCK_REGEX
list =~ Calligraphy::DAV_NOT_NO_LOCK_REGEX ? nil : true
end
def scan_for_resource(list)
return nil unless list =~ Calligraphy::RESOURCE_REGEX
list.scan(Calligraphy::RESOURCE_REGEX).flatten[0]
end
def scan_for_lock_token(list)
return nil unless list =~ Calligraphy::LOCK_TOKEN_REGEX
list.scan(Calligraphy::LOCK_TOKEN_REGEX).flatten[0]
end
def scan_for_etag(list)
return nil unless list =~ Calligraphy::ETAG_IF_REGEX
list.scan(Calligraphy::ETAG_IF_REGEX).flatten[0]
end
def evaluate_preconditions(conditions)
conditions_met = true
if conditions[:etag]
conditions_met = false unless evaluate_etag_condition conditions
end
conditions_met = false if conditions[:dav_no_lock]
conditions_met
end
def target_resource(conditions)
if conditions[:resource]
@resource_class.new(
resource: conditions[:resource],
mount: @resource.mount_point
)
else
@resource
end
end
def evaluate_etag_condition(conditions)
validators = [@resource.etag, '']
validate_etag validators, conditions[:etag]
end
def validate_etag(etag_validators, validate_against)
cache_key = ActiveSupport::Cache.expand_cache_key etag_validators
validate_against == "W/\"#{Digest::MD5.hexdigest(cache_key)}\""
end
end
end
end

View File

@@ -1,5 +1,12 @@
module Calligraphy::Rails
# frozen_string_literal: true
module Calligraphy
module Rails
# Controller for all WebDAV requests.
class WebDavRequestsController < ActionController::Base
include Calligraphy::Rails::WebDavMethods
include Calligraphy::Rails::WebDavPreconditions
before_action :verify_resource_scope
before_action :authenticate_with_digest_authentiation
before_action :set_resource
@@ -8,34 +15,27 @@ module Calligraphy::Rails
# preconditions, directing of requests to the proper WebDAV action
# method, and composing responses to send back to the client.
def invoke_method
method = request.request_method.downcase
if check_preconditions
if method == 'head'
status = get head: true
elsif Calligraphy.allowed_http_methods.include? method
set_resource_client_nonce(method) if Calligraphy.enable_digest_authentication
status, body = send method
else
status = :method_not_allowed
unless check_preconditions
return send_response(status: :precondition_failed)
end
method = request.request_method.downcase
status, body = make_request method
send_response status: status, body: body
else
send_response status: :precondition_failed
end
end
private
# Prevent any request with `.` or `..` as part of the resource ID.
def verify_resource_scope
head :forbidden if %w(. ..).any? { |seg| params[:resource].include? seg }
# Prevent any request with `.` or `..` as part of the resource.
head :forbidden if %w[. ..].any? do |seg|
params[:resource].include? seg
end
end
def authenticate_with_digest_authentiation
return unless Calligraphy.enable_digest_authentication
return unless digest_enabled?
realm = Calligraphy.http_authentication_realm
@@ -44,168 +44,55 @@ module Calligraphy::Rails
end
end
def digest_enabled?
Calligraphy.enable_digest_authentication
end
def set_resource
resource_id = if params[:format]
@resource_class = params[:resource_class] || Calligraphy::Resource
@resource_root_path = params[:resource_root_path]
@resource = @resource_class.new(
resource: resource_id,
req: request,
root_dir: @resource_root_path
)
end
def resource_id
if params[:format]
[params[:resource], params[:format]].join '.'
else
params[:resource]
end
@resource_class = params[:resource_class] || Calligraphy::Resource
@resource_root_path = params[:resource_root_path]
@resource = @resource_class.new resource: resource_id, req: request, root_dir: @resource_root_path
end
def check_preconditions
return true unless request.headers['If'].present?
def make_request(method)
if method == 'head'
status = get head: true
elsif Calligraphy.allowed_http_methods.include? method
resource_client_nonce(method) if digest_enabled?
evaluate_if_header
end
def evaluate_if_header
conditions_met = false
condition_lists = get_if_conditions
condition_lists.each do |list|
conditions = parse_preconditions list
conditions_met = evaluate_preconditions conditions
break if conditions_met
end
conditions_met
end
def get_if_conditions
lists = if request.headers['If'][0] == '<'
request.headers['If'].split Calligraphy::TAGGED_LIST_REGEX
status, body = send method
else
request.headers['If'].split Calligraphy::UNTAGGAGED_LIST_REGEX
status = :method_not_allowed
end
lists
[status, body]
end
def parse_preconditions(list)
conditions = { dav_no_lock: nil, etag: nil, lock_token: nil, resource: nil }
conditions[:dav_no_lock] = if list =~ Calligraphy::DAV_NO_LOCK_REGEX
list =~ Calligraphy::DAV_NOT_NO_LOCK_REGEX ? nil : true
def resource_client_nonce(_method)
@resource.client_nonce = client_nonce
end
if list =~ Calligraphy::RESOURCE_REGEX
conditions[:resource] = list.scan(Calligraphy::RESOURCE_REGEX).flatten[0]
end
if list =~ Calligraphy::LOCK_TOKEN_REGEX
conditions[:lock_token] = list.scan(Calligraphy::LOCK_TOKEN_REGEX).flatten[0]
end
if list =~ Calligraphy::ETAG_IF_REGEX
conditions[:etag] = list.scan(Calligraphy::ETAG_IF_REGEX).flatten[0]
end
conditions
end
def evaluate_preconditions(conditions)
conditions_met = true
target = if conditions[:resource]
@resource_class.new(
resource: conditions[:resource],
mount: @resource.mount_point
)
else
@resource
end
if conditions[:lock_token]
if target.locked?
conditions_met = false unless target.lock_tokens&.include? conditions[:lock_token]
else
conditions_met = false if target.locked_to_user? request.headers
end
end
if conditions[:etag]
validators = [@resource.etag, '']
conditions_met = false unless validate_etag validators, conditions[:etag]
end
conditions_met = false if conditions[:dav_no_lock]
conditions_met
end
def validate_etag(etag_validators, validate_against)
cache_key = ActiveSupport::Cache.expand_cache_key etag_validators
"W/\"#{Digest::MD5.hexdigest(cache_key)}\"" == validate_against
end
def web_dav_request
{ headers: request.headers, request: request, resource: @resource, response: response }
end
def set_resource_client_nonce(method)
@resource.client_nonce = get_client_nonce
end
def get_client_nonce
def client_nonce
auth_header = request.headers['HTTP_AUTHORIZATION']
digest = ::ActionController::HttpAuthentication::Digest
auth = ::ActionController::HttpAuthentication::Digest.decode_credentials auth_header
auth = digest.decode_credentials auth_header
auth[:cnonce]
end
def options
response.headers['DAV'] = @resource.dav_compliance
:ok
end
def get(head: false)
fresh_when(@resource, etag: @resource.etag) if @resource.readable?
Calligraphy::Get.new(web_dav_request).request(head: head)
end
def put
Calligraphy::Put.new(web_dav_request).request
end
def delete
Calligraphy::Delete.new(web_dav_request).request
end
def copy
Calligraphy::Copy.new(web_dav_request).request
end
def move
Calligraphy::Move.new(web_dav_request).request
end
def mkcol
Calligraphy::Mkcol.new(web_dav_request).request
end
def propfind
Calligraphy::Propfind.new(web_dav_request).request
end
def proppatch
Calligraphy::Proppatch.new(web_dav_request).request
end
def lock
Calligraphy::Lock.new(web_dav_request).request
end
def unlock
Calligraphy::Unlock.new(web_dav_request).request
end
def send_response(status:, body: nil)
if body.nil?
head status
@@ -215,3 +102,4 @@ module Calligraphy::Rails
end
end
end
end