Add support for digest authentication
This commit is contained in:
21
Gemfile.lock
21
Gemfile.lock
@@ -1,8 +1,8 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
calligraphy (0.1.0)
|
||||
rails (>= 5.0)
|
||||
calligraphy (0.2.0)
|
||||
rails (~> 5.0)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
@@ -48,6 +48,7 @@ GEM
|
||||
builder (3.2.3)
|
||||
concurrent-ruby (1.0.5)
|
||||
crass (1.0.3)
|
||||
diff-lcs (1.3)
|
||||
erubi (1.7.0)
|
||||
globalid (0.4.1)
|
||||
activesupport (>= 4.2.0)
|
||||
@@ -66,7 +67,7 @@ GEM
|
||||
nokogiri (1.8.1)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
rack (2.0.3)
|
||||
rack-test (0.8.0)
|
||||
rack-test (0.8.2)
|
||||
rack (>= 1.0, < 3)
|
||||
rails (5.1.4)
|
||||
actioncable (= 5.1.4)
|
||||
@@ -92,6 +93,19 @@ GEM
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
rake (12.3.0)
|
||||
rspec (3.7.0)
|
||||
rspec-core (~> 3.7.0)
|
||||
rspec-expectations (~> 3.7.0)
|
||||
rspec-mocks (~> 3.7.0)
|
||||
rspec-core (3.7.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-expectations (3.7.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-mocks (3.7.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.7.0)
|
||||
rspec-support (3.7.0)
|
||||
sprockets (3.7.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
rack (> 1, < 3)
|
||||
@@ -112,6 +126,7 @@ PLATFORMS
|
||||
|
||||
DEPENDENCIES
|
||||
calligraphy!
|
||||
rspec
|
||||
|
||||
BUNDLED WITH
|
||||
1.16.0
|
||||
|
||||
@@ -4,14 +4,14 @@ Calligraphy is a Web Distributed Authoring and Versioning (WebDAV) solution for
|
||||
|
||||
* Provides a framework for handling WebDAV requests (e.g. `PROPFIND`, `PROPPATCH`)
|
||||
* Allows you to extend WedDAV functionality to any type of resource
|
||||
* Passes 103/104 of the [Litmus](https://github.com/tolsen/litmus) tests (using `Calligraphy::FileResource`)
|
||||
* Passes all of the [Litmus](https://github.com/eanlain/litmus) tests (using `Calligraphy::FileResource` and digest authentication)
|
||||
|
||||
## Getting Started
|
||||
|
||||
Add the following line to your Gemfile:
|
||||
|
||||
```ruby
|
||||
gem 'calligraphy'
|
||||
gem 'calligraphy', :git => 'https://github.com/eanlain/calligraphy'
|
||||
```
|
||||
|
||||
Then run `bundle install`
|
||||
|
||||
@@ -17,4 +17,6 @@ Gem::Specification.new do |s|
|
||||
s.test_files = Dir['spec/**/*']
|
||||
|
||||
s.add_dependency 'rails', '~> 5.0'
|
||||
|
||||
s.add_development_dependency 'rspec'
|
||||
end
|
||||
|
||||
@@ -38,6 +38,12 @@ module Calligraphy
|
||||
options head get put delete copy move mkcol propfind proppatch lock unlock
|
||||
)
|
||||
|
||||
mattr_accessor :digest_password_procedure
|
||||
@@digest_password_procedure = Proc.new { |x| 'changeme!' }
|
||||
|
||||
mattr_accessor :enable_digest_authentication
|
||||
@@enable_digest_authentication = false
|
||||
|
||||
mattr_accessor :lock_timeout_period
|
||||
@@lock_timeout_period = 24 * 60 * 60
|
||||
|
||||
@@ -45,4 +51,8 @@ module Calligraphy
|
||||
@@web_dav_actions = %i(
|
||||
options get put delete copy move mkcol propfind proppatch lock unlock
|
||||
)
|
||||
|
||||
def self.configure
|
||||
yield self
|
||||
end
|
||||
end
|
||||
|
||||
@@ -309,6 +309,7 @@ module Calligraphy
|
||||
|
||||
def create_lock(properties, depth)
|
||||
@store.transaction do
|
||||
@store[:lockcreator] = client_nonce
|
||||
@store[:lockdiscovery] = [] unless @store[:lockdiscovery].is_a? Array
|
||||
@store[:lockdepth] = depth
|
||||
|
||||
@@ -366,6 +367,7 @@ module Calligraphy
|
||||
|
||||
def locking_ancestor?(ancestor_path, ancestors, headers=nil)
|
||||
ancestor_store_path = "#{ancestor_path}/#{ancestors[-1]}.pstore"
|
||||
check_lock_creator = Calligraphy.enable_digest_authentication
|
||||
blocking_lock = false
|
||||
unlockable = true
|
||||
|
||||
@@ -381,6 +383,10 @@ module Calligraphy
|
||||
ancestor_store[:lockdiscovery]
|
||||
end
|
||||
|
||||
ancestor_lock_creator = ancestor_store.transaction(true) do
|
||||
ancestor_store[:lockcreator]
|
||||
end if check_lock_creator
|
||||
|
||||
blocking_lock = obj_exists_and_is_not_type? obj: ancestor_lock, type: []
|
||||
|
||||
if blocking_lock
|
||||
@@ -392,7 +398,8 @@ module Calligraphy
|
||||
.each { |x| x }
|
||||
.map { |k, v| k[:locktoken].children[0].text }
|
||||
|
||||
unlockable = ancestor_lock_tokens.include? token
|
||||
unlockable = ancestor_lock_tokens.include?(token) ||
|
||||
(check_lock_creator && (ancestor_lock_creator == client_nonce))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -463,6 +470,8 @@ module Calligraphy
|
||||
|
||||
def remove_lock(token)
|
||||
@store.transaction do
|
||||
@store.delete :lockcreator
|
||||
|
||||
if @store[:lockdiscovery].length == 1
|
||||
@store.delete :lockdiscovery
|
||||
else
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
module Calligraphy::Rails
|
||||
class WebDavRequestsController < ActionController::Base
|
||||
before_action :verify_resource_scope
|
||||
before_action :authenticate_with_digest_authentiation
|
||||
before_action :set_resource
|
||||
|
||||
def invoke_method
|
||||
@@ -10,6 +11,8 @@ module Calligraphy::Rails
|
||||
if method == 'head'
|
||||
status = get head: true
|
||||
elsif Calligraphy.allowed_methods.include? method
|
||||
set_resource_client_nonce(method) if Calligraphy.enable_digest_authentication
|
||||
|
||||
status, body = send method
|
||||
else
|
||||
status = :method_not_allowed
|
||||
@@ -27,6 +30,14 @@ module Calligraphy::Rails
|
||||
head :forbidden if params[:resource].include? '..'
|
||||
end
|
||||
|
||||
def authenticate_with_digest_authentiation
|
||||
if Calligraphy.enable_digest_authentication
|
||||
authenticate_or_request_with_http_digest do |username|
|
||||
Calligraphy.digest_password_procedure.call(username)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def set_resource
|
||||
resource_id = if params[:format]
|
||||
[params[:resource], params[:format]].join '.'
|
||||
@@ -182,5 +193,16 @@ module Calligraphy::Rails
|
||||
render body: body, status: status
|
||||
end
|
||||
end
|
||||
|
||||
def set_resource_client_nonce(method)
|
||||
@resource.client_nonce = get_client_nonce
|
||||
end
|
||||
|
||||
def get_client_nonce
|
||||
auth_header = request.headers["HTTP_AUTHORIZATION"]
|
||||
|
||||
auth = ::ActionController::HttpAuthentication::Digest.decode_credentials auth_header
|
||||
auth[:cnonce]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module Calligraphy
|
||||
class Resource
|
||||
attr_accessor :contents, :updated_at
|
||||
attr_accessor :client_nonce, :contents, :updated_at
|
||||
attr_reader :full_request_path, :mount_point, :request_body, :request_path, :root_dir
|
||||
|
||||
def initialize(resource: nil, req: nil, mount: nil, root_dir: nil)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module Calligraphy
|
||||
VERSION = '0.1.0'
|
||||
VERSION = '0.2.0'
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user