calligraphy 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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: