calligraphy 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8ca4ef1090301769811a4e4e9582826338ebcc7d
4
- data.tar.gz: 6453585dca9f697419f6f5e8b4c73e2f664d5b10
3
+ metadata.gz: 3ed31ae5c1825d0054f391bd8692cde22743e792
4
+ data.tar.gz: aa169279219e512a0be3d368852a9291eb1d702b
5
5
  SHA512:
6
- metadata.gz: 5055f8ed11cda5dcc4bdaf6d1e821b989fd57f2e2869e9e316a8a9ed49e40a6da5962614dac6a8f6448d155a5de743f9ca3360aaa8700af736c7e989a8a66f91
7
- data.tar.gz: 7de86141b36bd5bef0bd915a8eed30c524568c1e409bf0ecdc1bf5d7f41a22fa887cbbc69964ef404f8f5f0de094474cb4d5eea10e68278d20d1e573e8dc90f5
6
+ metadata.gz: dce3ce028df7fde58126f2799efd90daf3609517abbbcd92e37a7fa1062f83d034cc15e67292b31d496e6e366af9580f1e55a2dbc36f40dc537db3f742b6f32a
7
+ data.tar.gz: dd3ef30ce3a48d51a871f440eff8b40974a5e9b5292f6fb5f80699d8823e8872355c3c099405378633e2daba10d232b788112c35235fbb43ce9594449883d389
data/README.md CHANGED
@@ -11,14 +11,23 @@ Calligraphy is a Web Distributed Authoring and Versioning (WebDAV) solution for
11
11
  Add the following line to your Gemfile:
12
12
 
13
13
  ```ruby
14
- gem 'calligraphy', :git => 'https://github.com/eanlain/calligraphy'
14
+ gem 'calligraphy'
15
15
  ```
16
16
 
17
- Then run `bundle install`
17
+ Then, run `bundle install`
18
18
 
19
- Next, set up a `calligraphy_resource` route in `config/routes.rb` with a `resource_class`.
19
+ Next, run the generator:
20
20
 
21
21
  ```ruby
22
+ rails generate calligraphy:install
23
+ ```
24
+
25
+ The generator will install an initializer which describes Calligraphy's configuration options.
26
+
27
+ Finally, set up a `calligraphy_resource` route in `config/routes.rb` with a `resource_class`.
28
+
29
+ ```ruby
30
+ # config/routes.rb
22
31
  calligraphy_resource :webdav, resource_class: Calligraphy::FileResource
23
32
  ```
24
33
 
@@ -37,6 +46,14 @@ The above will create a route, `/webdav` that will be able to handle the followi
37
46
  * `UNLOCK`
38
47
 
39
48
  The routes will also use the `Calligraphy::FileResource`, enabling Rails to carry out WebDAV actions on files.
49
+ Additionally, when using the `FileResource` class the resource root path (the location you'd like to serve files from) with the `resource_root_path` key:
50
+
51
+ ```ruby
52
+ # config/routes.rb
53
+ calligraphy_resource :webdav, resource_class: Calligraphy::FileResource, resource_root_path: '/home/webdav'
54
+ ```
55
+
56
+ This will serve local files from `/home/webdav` on the server through the `/webdav` endpoint.
40
57
 
41
58
  ## Extensibility
42
59
 
@@ -1,61 +1,85 @@
1
1
  module ActionDispatch::Routing
2
2
  class Mapper
3
3
  module HttpHelpers
4
+ # Define a Calligraphy route that only recognizes HTTP COPY.
5
+ # copy 'bacon', to: 'food#bacon'
4
6
  def copy(*args, &block)
5
7
  args = set_web_dav_args args
6
8
  map_method :copy, args, &block
7
9
  end
8
10
 
11
+ # Define a Calligraphy route that only recognizes HTTP HEAD.
12
+ # head 'bacon', to: 'food#bacon'
9
13
  def head(*args, &block)
10
14
  args = set_web_dav_args args
11
15
  map_method :head, args, &block
12
16
  end
13
17
 
18
+ # Define a Calligraphy route that only recognizes HTTP LOCK.
19
+ # lock 'bacon', to: 'food#bacon'
14
20
  def lock(*args, &block)
15
21
  args = set_web_dav_args args
16
22
  map_method :lock, args, &block
17
23
  end
18
24
 
25
+ # Define a Calligraphy route that only recognizes HTTP MKCOL.
26
+ # mkcol 'bacon', to: 'food#bacon'
19
27
  def mkcol(*args, &block)
20
28
  args = set_web_dav_args args
21
29
  map_method :mkcol, args, &block
22
30
  end
23
31
 
32
+ # Define a Calligraphy route that only recognizes HTTP MOVE.
33
+ # move 'bacon', to: 'food#bacon'
24
34
  def move(*args, &block)
25
35
  args = set_web_dav_args args
26
36
  map_method :move, args, &block
27
37
  end
28
38
 
39
+ # Define a Calligraphy route that only recognizes HTTP OPTIONS.
40
+ # options 'bacon', to: 'food#bacon'
29
41
  def options(*args, &block)
30
42
  args = set_web_dav_args args
31
43
  map_method :options, args, &block
32
44
  end
33
45
 
46
+ # Define a Calligraphy route that only recognizes HTTP PROPFIND.
47
+ # propfind 'bacon', to: 'food#bacon'
34
48
  def propfind(*args, &block)
35
49
  args = set_web_dav_args args
36
50
  map_method :propfind, args, &block
37
51
  end
38
52
 
53
+ # Define a Calligraphy route that only recognizes HTTP PROPPATCH.
54
+ # proppatch 'bacon', to: 'food#bacon'
39
55
  def proppatch(*args, &block)
40
56
  args = set_web_dav_args args
41
57
  map_method :proppatch, args, &block
42
58
  end
43
59
 
60
+ # Define a Calligraphy route that only recognizes HTTP UNLOCK.
61
+ # unlock 'bacon', to: 'food#bacon'
44
62
  def unlock(*args, &block)
45
63
  args = set_web_dav_args args
46
64
  map_method :unlock, args, &block
47
65
  end
48
66
 
67
+ # Define a Calligraphy route that only recognizes HTTP DELETE.
68
+ # web_dav_delete 'broccoli', to: 'food#broccoli'
49
69
  def web_dav_delete(*args, &block)
50
70
  args = set_web_dav_args args
51
71
  map_method :delete, args, &block
52
72
  end
53
73
 
74
+ # Define a Calligraphy route that only recognizes HTTP GET.
75
+ # web_dav_get 'bacon', to: 'food#bacon'
54
76
  def web_dav_get(*args, &block)
55
77
  args = set_web_dav_args args
56
78
  map_method :get, args, &block
57
79
  end
58
80
 
81
+ # Define a Calligraphy route that only recognizes HTTP PUT.
82
+ # web_dav_put 'bacon', to: 'food#bacon'
59
83
  def web_dav_put(*args, &block)
60
84
  args = set_web_dav_args args
61
85
  map_method :put, args, &block
@@ -64,9 +88,10 @@ module ActionDispatch::Routing
64
88
  private
65
89
 
66
90
  def set_web_dav_args(args)
67
- options = {}
68
- options[:controller] = 'calligraphy/rails/web_dav_requests'
69
- options[:action] = 'invoke_method'
91
+ options = {
92
+ controller: 'calligraphy/rails/web_dav_requests',
93
+ action: 'invoke_method'
94
+ }
70
95
  [args[0], options]
71
96
  end
72
97
  end
@@ -84,6 +109,26 @@ module ActionDispatch::Routing
84
109
  end
85
110
  end
86
111
 
112
+ # With Calligraphy, a resourceful route provides mappings between WebDAV
113
+ # HTTP verbs and URLs and WebDAV controller actions. A single entry in
114
+ # the routing file, such as:
115
+ #
116
+ # calligraphy_resource :photos
117
+ #
118
+ # creates eleven different routes in your application, all mapping to the
119
+ # WebDavRequests controller:
120
+ #
121
+ # OPTIONS /photos/*resource
122
+ # GET /photos/*resource
123
+ # PUT /photos/*resource
124
+ # DELETE /photos/*resource
125
+ # COPY /photos/*resource
126
+ # MOVE /photos/*resource
127
+ # MKCOL /photos/*resource
128
+ # PROPFIND /photos/*resource
129
+ # PROPPATCH /photos/*resource
130
+ # LOCK /photos/*resource
131
+ # UNLOCK /photos/*resource
87
132
  def calligraphy_resource(*resources, &block)
88
133
  options = resources.extract_options!.dup
89
134
 
@@ -94,6 +139,7 @@ module ActionDispatch::Routing
94
139
  with_scope_level(:resource) do
95
140
  options = apply_action_options options
96
141
  singleton_resoure = ActionDispatch::Routing::Mapper::SingletonResource
142
+
97
143
  resource_scope(singleton_resoure.new resources.pop, api_only?, @scope[:shallow], options) do
98
144
  yield if block_given?
99
145
 
@@ -108,6 +154,9 @@ module ActionDispatch::Routing
108
154
 
109
155
  def set_mappings_for_web_dav_resources
110
156
  parent_resource.web_dav_actions.each do |action|
157
+ # Rails already defines GET, PUT, and DELETE actions which we don't
158
+ # want to override. Instead, we map WebDAV GET, PUT, and DELETE
159
+ # HTTP actions to 'web_dav_' prefixed methods.
111
160
  if [:get, :put, :delete].include? action
112
161
  send "web_dav_#{action.to_s}", '*resource'
113
162
  else
@@ -4,13 +4,16 @@ module Calligraphy::Rails
4
4
  before_action :authenticate_with_digest_authentiation
5
5
  before_action :set_resource
6
6
 
7
+ # Entry-point for all WebDAV requests. Handles checking and validating
8
+ # preconditions, directing of requests to the proper WebDAV action
9
+ # method, and composing responses to send back to the client.
7
10
  def invoke_method
8
11
  method = request.request_method.downcase
9
12
 
10
13
  if check_preconditions
11
14
  if method == 'head'
12
15
  status = get head: true
13
- elsif Calligraphy.allowed_methods.include? method
16
+ elsif Calligraphy.allowed_http_methods.include? method
14
17
  set_resource_client_nonce(method) if Calligraphy.enable_digest_authentication
15
18
 
16
19
  status, body = send method
@@ -27,14 +30,16 @@ module Calligraphy::Rails
27
30
  private
28
31
 
29
32
  def verify_resource_scope
30
- head :forbidden if params[:resource].include? '..'
33
+ head :forbidden if %w(. ..).any? { |seg| params[:resource].include? seg }
31
34
  end
32
35
 
33
36
  def authenticate_with_digest_authentiation
34
- if Calligraphy.enable_digest_authentication
35
- authenticate_or_request_with_http_digest do |username|
36
- Calligraphy.digest_password_procedure.call(username)
37
- end
37
+ return unless Calligraphy.enable_digest_authentication
38
+
39
+ realm = Calligraphy.http_authentication_realm
40
+
41
+ authenticate_or_request_with_http_digest(realm) do |username|
42
+ Calligraphy.digest_password_procedure.call(username)
38
43
  end
39
44
  end
40
45
 
@@ -58,8 +63,8 @@ module Calligraphy::Rails
58
63
 
59
64
  def evaluate_if_header
60
65
  conditions_met = false
61
-
62
66
  condition_lists = get_if_conditions
67
+
63
68
  condition_lists.each do |list|
64
69
  conditions = parse_preconditions list
65
70
 
@@ -102,7 +102,7 @@ module Calligraphy
102
102
  File.exist? @src_path
103
103
  end
104
104
 
105
- def lock(nodes, depth='infinity')
105
+ def lock(nodes, depth=INFINITY)
106
106
  properties = {}
107
107
 
108
108
  nodes.each do |node|
@@ -375,17 +375,19 @@ module Calligraphy
375
375
 
376
376
  if File.exist? ancestor_store_path
377
377
  ancestor_store = PStore.new ancestor_store_path
378
- ancestor_lock_depth = ancestor_store.transaction(true) do
379
- ancestor_store[:lockdepth]
380
- end
381
378
 
382
- ancestor_lock = ancestor_store.transaction(true) do
383
- ancestor_store[:lockdiscovery]
384
- end
379
+ ancestor_lock = nil
380
+ ancestor_lock_creator = nil
381
+ ancestor_lock_depth = nil
385
382
 
386
- ancestor_lock_creator = ancestor_store.transaction(true) do
387
- ancestor_store[:lockcreator]
388
- end if check_lock_creator
383
+ ancestor_store.transaction(true) do
384
+ ancestor_lock = ancestor_store[:lockdiscovery]
385
+ ancestor_lock_depth = ancestor_store[:lockdepth]
386
+
387
+ if check_lock_creator
388
+ ancestor_lock_creator = ancestor_store[:lockcreator]
389
+ end
390
+ end
389
391
 
390
392
  blocking_lock = obj_exists_and_is_not_type? obj: ancestor_lock, type: []
391
393
 
@@ -422,14 +424,22 @@ module Calligraphy
422
424
  prop.content = @stats[:created_at]
423
425
  when 'displayname'
424
426
  prop.content = @name
427
+ when 'getcontentlanguage'
428
+ prop.content = nil
425
429
  when 'getcontentlength'
426
430
  prop.content = @stats[:size]
431
+ when 'getcontenttype'
432
+ prop.content = nil
433
+ when 'getetag'
434
+ prop.content = nil
427
435
  when 'getlastmodified'
428
436
  prop.content = @updated_at
429
- when 'resourcetype'
430
- prop.content = 'collection'
431
437
  when 'lockdiscovery'
432
438
  return get_lock_info
439
+ when 'resourcetype'
440
+ prop.content = 'collection'
441
+ when 'supportedlock'
442
+ prop.content = nil
433
443
  else
434
444
  return get_custom_property prop.name
435
445
  end
@@ -456,7 +466,7 @@ module Calligraphy
456
466
 
457
467
  if File.exist? ancestor_store_path
458
468
  ancestor_store = PStore.new ancestor_store_path
459
- ancestor_lock = ancestor_store.transaction(true) do
469
+ ancestor_lock = ancestor_store.transaction do
460
470
  ancestor_store[:lockdiscovery][-1][:timeout] = timeout_node
461
471
  ancestor_store[:lockdiscovery]
462
472
  end
@@ -42,7 +42,7 @@ module Calligraphy
42
42
  raise NotImplemented
43
43
  end
44
44
 
45
- def lock(nodes, depth='infinity')
45
+ def lock(nodes, depth=INFINITY)
46
46
  raise NotImplemented
47
47
  end
48
48
 
@@ -1,3 +1,3 @@
1
1
  module Calligraphy
2
- VERSION = '0.2.0'
2
+ VERSION = '0.2.1'
3
3
  end
@@ -5,6 +5,11 @@ module Calligraphy::XML
5
5
  getcontenttype getetag getlastmodified href lockdiscovery lockscope
6
6
  locktype owner write
7
7
  )
8
+ DAV_PROPERTIES = %w(
9
+ creationdate displayname getcontentlanguage getcontentlength
10
+ getcontenttype getetag getlastmodified lockdiscovery
11
+ resourcetype supportedlock
12
+ )
8
13
 
9
14
  attr_reader :dav_ns, :default_ns, :server_protocol
10
15
 
@@ -12,9 +12,8 @@ module Calligraphy::XML
12
12
  end
13
13
 
14
14
  if node.children&.length > 0
15
- @children = []
16
- node.children.each do |child|
17
- @children.push Calligraphy::XML::Node.new child
15
+ @children = node.children.map do |child|
16
+ Calligraphy::XML::Node.new child
18
17
  end
19
18
  end
20
19
  end
data/lib/calligraphy.rb CHANGED
@@ -7,8 +7,8 @@ require 'calligraphy/xml/node'
7
7
  require 'calligraphy/xml/utils'
8
8
 
9
9
  require 'calligraphy/utils'
10
- require 'calligraphy/resource'
11
- require 'calligraphy/file_resource'
10
+ require 'calligraphy/resource/resource'
11
+ require 'calligraphy/resource/file_resource'
12
12
 
13
13
  require 'calligraphy/web_dav_request'
14
14
  require 'calligraphy/copy'
@@ -23,35 +23,55 @@ require 'calligraphy/put'
23
23
  require 'calligraphy/unlock'
24
24
 
25
25
  module Calligraphy
26
+ # Constants used throughout Calligraphy.
26
27
  DAV_NS = 'DAV:'
27
28
  DAV_NO_LOCK_REGEX = /DAV:no-lock/i
28
29
  DAV_NOT_NO_LOCK_REGEX = /Not\s+<DAV:no-lock>/i
29
30
  ETAG_IF_REGEX = /\[(.+?)\]/
31
+ INFINITY = 1.0 / 0.0 unless defined? INFINITY
30
32
  LOCK_TOKEN_ANGLE_REGEX = /[<>]/
31
33
  LOCK_TOKEN_REGEX = /<(urn:uuid:.+?)>/
32
34
  RESOURCE_REGEX = /^<+(.+?)>\s/
33
35
  TAGGED_LIST_REGEX = /\)\s</
34
36
  UNTAGGAGED_LIST_REGEX = /\)\s\(/
35
37
 
36
- mattr_accessor :allowed_methods
37
- @@allowed_methods = %w(
38
- options head get put delete copy move mkcol propfind proppatch lock unlock
38
+ # HTTP methods allowed by the WebDavRequests controller.
39
+ mattr_accessor :allowed_http_methods
40
+ @@allowed_http_methods = %w(
41
+ options get put delete copy move
42
+ mkcol propfind proppatch lock unlock
39
43
  )
40
44
 
45
+ # Proc responsible for returning the user's password, API key,
46
+ # or HA1 digest hash so that Rails can check user credentials.
47
+ # Should be overridden to handle your particular application's
48
+ # user and/or authentication setup.
41
49
  mattr_accessor :digest_password_procedure
42
- @@digest_password_procedure = Proc.new { |x| 'changeme!' }
50
+ @@digest_password_procedure = Proc.new { |username| 'changeme!' }
43
51
 
52
+ # If Digest Authentication is enabled by default.
44
53
  mattr_accessor :enable_digest_authentication
45
54
  @@enable_digest_authentication = false
46
55
 
56
+ # The realm used in HTTP Digest Authentication.
57
+ mattr_accessor :http_authentication_realm
58
+ @@http_authentication_realm = 'Application'
59
+
60
+ # Maximum lock lifetime in seconds.
47
61
  mattr_accessor :lock_timeout_period
48
- @@lock_timeout_period = 24 * 60 * 60
62
+ @@lock_timeout_period = 86400
49
63
 
64
+ # The HTTP actions Calligraphy uses to create mappings between WebDAV
65
+ # HTTP verbs and URLs and WebDAV controller actions.
50
66
  mattr_accessor :web_dav_actions
51
67
  @@web_dav_actions = %i(
52
- options get put delete copy move mkcol propfind proppatch lock unlock
68
+ options get put delete copy move
69
+ mkcol propfind proppatch lock unlock
53
70
  )
54
71
 
72
+ # Default way to set up Calligraphy.
73
+ # Run `rails generate calligraphy:install` to generate a
74
+ # fresh initializer with all configuration values.
55
75
  def self.configure
56
76
  yield self
57
77
  end
@@ -0,0 +1,15 @@
1
+ require 'rails/generators/base'
2
+
3
+ module Calligraphy
4
+ module Generators
5
+ class InstallGenerator < ::Rails::Generators::Base
6
+ source_root File.expand_path('../../templates', __FILE__)
7
+
8
+ desc 'Creates a Calligraphy initializer for your application'
9
+
10
+ def copy_initializer
11
+ template 'calligraphy.rb', 'config/initializers/calligraphy.rb'
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,47 @@
1
+ Calligraphy.configure do |config|
2
+ # The HTTP actions Calligraphy uses to create mappings between WebDAV
3
+ # HTTP verbs and URLs and WebDAV controller actions.
4
+ # config.web_dav_actions = [
5
+ # :options, :get, :put, :delete, :copy, :move,
6
+ # :mkcol, :propfind, :proppatch, :lock, :unlock
7
+ # ]
8
+
9
+ # HTTP methods allowed by the WebDavRequests controller.
10
+ # Before responding to a WebDav request, the WebDavRequests controller
11
+ # checks this list to determine if it is allowed to make the request.
12
+ # If a method is disallowed, the controller will respond by sending an
13
+ # HTTP 405 (Method Not Allowed) response.
14
+ # config.allowed_http_methods = %w(
15
+ # options get put delete copy move
16
+ # mkcol propfind proppatch lock unlock
17
+ # )
18
+
19
+ # If Digest Authentication is enabled by default. False by default.
20
+ # config.enable_digest_authentication = false
21
+
22
+ # Proc responsible for returning the user's password, API key,
23
+ # or HA1 digest hash so that Rails can check user credentials.
24
+ # Should be configured to handle your particular application's
25
+ # user and/or authentication setup.
26
+ #
27
+ # For example, in an API setup where an email/API key are sent with the
28
+ # request, in lieu of a username/password, the digest_password_procedure
29
+ # would be defined as:
30
+ #
31
+ # config.digest_password_procedure = Proc.new do |email|
32
+ # u = User.find_by(email: email)
33
+ # u.authentication_token
34
+ # end
35
+ #
36
+ # Digest Authentication would need to be enabled for this proc to
37
+ # actually be called.
38
+ # config.digest_password_procedure = Proc.new do |username|
39
+ # 'changeme!'
40
+ # end
41
+
42
+ # The realm used in HTTP Digest Authentication. 'Application' by default.
43
+ # config.http_authentication_realm = 'Application'
44
+
45
+ # Maximum lock lifetime in seconds. 86400 by default.
46
+ # config.lock_timeout_period = 86400
47
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: calligraphy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brandon Robins
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-15 00:00:00.000000000 Z
11
+ date: 2017-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -49,7 +49,6 @@ files:
49
49
  - lib/calligraphy.rb
50
50
  - lib/calligraphy/copy.rb
51
51
  - lib/calligraphy/delete.rb
52
- - lib/calligraphy/file_resource.rb
53
52
  - lib/calligraphy/get.rb
54
53
  - lib/calligraphy/lock.rb
55
54
  - lib/calligraphy/mkcol.rb
@@ -59,7 +58,8 @@ files:
59
58
  - lib/calligraphy/put.rb
60
59
  - lib/calligraphy/rails/mapper.rb
61
60
  - lib/calligraphy/rails/web_dav_requests_controller.rb
62
- - lib/calligraphy/resource.rb
61
+ - lib/calligraphy/resource/file_resource.rb
62
+ - lib/calligraphy/resource/resource.rb
63
63
  - lib/calligraphy/unlock.rb
64
64
  - lib/calligraphy/utils.rb
65
65
  - lib/calligraphy/version.rb
@@ -68,6 +68,8 @@ files:
68
68
  - lib/calligraphy/xml/namespace.rb
69
69
  - lib/calligraphy/xml/node.rb
70
70
  - lib/calligraphy/xml/utils.rb
71
+ - lib/generators/calligraphy/install_generator.rb
72
+ - lib/generators/templates/calligraphy.rb
71
73
  - spec/spec_helper.rb
72
74
  homepage: http://www.github.com/eanlain/calligraphy
73
75
  licenses: