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