mongrel_crypted_download 0.4
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.
- data/COPYING +19 -0
- data/LICENSE +19 -0
- data/README +69 -0
- data/Rakefile +38 -0
- data/lib/mongrel_crypted_download/init.rb +62 -0
- data/resources/defaults.yaml +2 -0
- data/tools/rakehelp.rb +105 -0
- metadata +77 -0
data/COPYING
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2008 Andriy Bazyuta
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the "Software"),
|
|
5
|
+
to deal in the Software without restriction, including without limitation
|
|
6
|
+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
7
|
+
and/or sell copies of the Software, and to permit persons to whom the
|
|
8
|
+
Software is furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included
|
|
11
|
+
in all copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
14
|
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
data/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright (c) 2008 Andriy Bazyuta
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the "Software"),
|
|
5
|
+
to deal in the Software without restriction, including without limitation
|
|
6
|
+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
7
|
+
and/or sell copies of the Software, and to permit persons to whom the
|
|
8
|
+
Software is furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included
|
|
11
|
+
in all copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
14
|
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
data/README
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
== Mongrel_crypted_download GemPlugin
|
|
2
|
+
IMPORTANT!!
|
|
3
|
+
Required encrypted_strings plugin http://agilewebdevelopment.com/plugins/encrypted_strings
|
|
4
|
+
|
|
5
|
+
The need to send secured files in a fast and reliable way is common.
|
|
6
|
+
|
|
7
|
+
Sending a file from inside of a web application can be slow
|
|
8
|
+
and also utilizes an entire application thread/process until the user
|
|
9
|
+
is done downloading the file. Also is crypt path by encrypted_strings plugin http://agilewebdevelopment.com/plugins/encrypted_strings
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
<uri-prefix> is a directory that does not exist in the directory structure of the application but does
|
|
14
|
+
exist in the directory structure of the server.
|
|
15
|
+
example: /download_file
|
|
16
|
+
|
|
17
|
+
<relative-path> is the crypted by encrypted_strings plugin path to the file.
|
|
18
|
+
example: /public/attachments/
|
|
19
|
+
|
|
20
|
+
<file-name> is the name of file without path.
|
|
21
|
+
|
|
22
|
+
<timestamp> is the number of seconds since epoch until the time when this download expires
|
|
23
|
+
example (in ruby on rails): 1.minute.from_now.to_i.to_s
|
|
24
|
+
|
|
25
|
+
<token> is the SHA1 hash of the concatenation of the following items:
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
To use the plugin you need to do the following:
|
|
29
|
+
|
|
30
|
+
1) setup the handler within a configuration script and pass in the secret string.
|
|
31
|
+
|
|
32
|
+
example configuration script:
|
|
33
|
+
|
|
34
|
+
uri "/download_file", :handler => plugin('/handlers/crypteddownload')
|
|
35
|
+
|
|
36
|
+
2) In your application, form a secured URI by creating the proper parameters and
|
|
37
|
+
perform an SHA1 hash of the parameters to create the proper token
|
|
38
|
+
|
|
39
|
+
example code (ruby on rails):
|
|
40
|
+
|
|
41
|
+
@track = Track.find(params[:id])
|
|
42
|
+
@attachment = Attachment.find(params[:id])
|
|
43
|
+
|
|
44
|
+
url = CryptedDownload.generate(@attachment.filename, "/public"+@attachment.public_filename.gsub(/#{@attachment.filename}/, ''), "/download_files", request)
|
|
45
|
+
|
|
46
|
+
redirect_to url
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
3) Start mongel by passing in the location of the configuration script from step 1 with the -S command
|
|
50
|
+
line switch
|
|
51
|
+
|
|
52
|
+
example:
|
|
53
|
+
|
|
54
|
+
mongrel_rails start -S config/mongrel_crypted_download.conf
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
Error messages
|
|
58
|
+
|
|
59
|
+
If any of the parameters in the URI or the secret_string are missing
|
|
60
|
+
the handler returns a 500 Application Error.
|
|
61
|
+
|
|
62
|
+
If the token passed in as a parameter does not match the token generated
|
|
63
|
+
by the handler (if someone tries to guess the token) the handler returns
|
|
64
|
+
a 403 Forbidden error.
|
|
65
|
+
|
|
66
|
+
If the timestamp is earlier than the current server time, meaning that the file is
|
|
67
|
+
no longer a valid download then the handler returns a 408 Request Time-out Error.
|
|
68
|
+
This error is not technically correct but it makes the most sense in the context of
|
|
69
|
+
the handler.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require 'rake'
|
|
2
|
+
require 'rake/testtask'
|
|
3
|
+
require 'rake/clean'
|
|
4
|
+
require 'rake/gempackagetask'
|
|
5
|
+
require 'rake/rdoctask'
|
|
6
|
+
require 'tools/rakehelp'
|
|
7
|
+
require 'fileutils'
|
|
8
|
+
include FileUtils
|
|
9
|
+
|
|
10
|
+
setup_tests
|
|
11
|
+
setup_clean ["pkg", "lib/*.bundle", "*.gem", ".config"]
|
|
12
|
+
|
|
13
|
+
setup_rdoc ['README', 'LICENSE', 'COPYING', 'lib/**/*.rb', 'doc/**/*.rdoc']
|
|
14
|
+
|
|
15
|
+
desc "Does a full compile, test run"
|
|
16
|
+
task :default => [:test, :package]
|
|
17
|
+
|
|
18
|
+
version="0.4"
|
|
19
|
+
name="mongrel_crypted_download"
|
|
20
|
+
|
|
21
|
+
setup_gem(name, version) do |spec|
|
|
22
|
+
spec.summary = "Mongrel Crypted Download Plugin"
|
|
23
|
+
spec.description = spec.summary
|
|
24
|
+
spec.author="Andriy Bazyuta"
|
|
25
|
+
spec.add_dependency('gem_plugin', '>= 0.2.1')
|
|
26
|
+
spec.add_dependency('mongrel', '>= 0.3.13')
|
|
27
|
+
spec.files += Dir.glob("resources/**/*")
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
task :install => [:test, :package] do
|
|
32
|
+
sh %{sudo gem install pkg/#{name}-#{version}.gem}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
task :uninstall => [:clean] do
|
|
36
|
+
sh %{sudo gem uninstall #{name}}
|
|
37
|
+
end
|
|
38
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require 'gem_plugin'
|
|
2
|
+
require 'mongrel'
|
|
3
|
+
require 'digest/sha1'
|
|
4
|
+
|
|
5
|
+
class CryptedDownload < GemPlugin::Plugin "/handlers"
|
|
6
|
+
include Mongrel::HttpHandlerPlugin
|
|
7
|
+
|
|
8
|
+
def process(request, response)
|
|
9
|
+
query = Mongrel::HttpRequest.query_parse(request.params['QUERY_STRING'])
|
|
10
|
+
cookies = Mongrel::HttpRequest.query_parse(request.params['HTTP_COOKIE'])
|
|
11
|
+
secret_string = cookies.keys.first
|
|
12
|
+
|
|
13
|
+
query['path'] = CGI::unescape(query['path'])
|
|
14
|
+
|
|
15
|
+
if secret_string.nil? or query['token'].nil? or query['timestamp'].nil? or query['path'].nil?
|
|
16
|
+
response.start(500){}
|
|
17
|
+
elsif query['timestamp'].to_i < Time.now.to_i
|
|
18
|
+
response.start(408){}
|
|
19
|
+
elsif query['token'] == Digest::SHA1.hexdigest("#{secret_string}#{query['path']}#{query['timestamp']}").to_s
|
|
20
|
+
send_file(File.expand_path("." + query['path'].decrypt(:symmetric, :key => secret_string) + query['file-name']), response)
|
|
21
|
+
else
|
|
22
|
+
response.start(403){}
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.generate(file_name, path, uri_prefix, request)
|
|
27
|
+
secret_string = request.cookies.keys.first
|
|
28
|
+
|
|
29
|
+
path = path.encrypt(:symmetric, :key => secret_string)
|
|
30
|
+
timestamp = 1.minute.from_now.to_i.to_s
|
|
31
|
+
token = Digest::SHA1.hexdigest(secret_string + path + timestamp)
|
|
32
|
+
path = CGI::escape(path)
|
|
33
|
+
|
|
34
|
+
return "#{uri_prefix}/?token=#{token}&path=#{path}&file-name=#{file_name}×tamp=#{timestamp}"
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
# Sends the contents of a file back to the user.
|
|
43
|
+
def send_file(path, response)
|
|
44
|
+
# first we setup the headers and status then we do a very fast send on the socket directly
|
|
45
|
+
file_status = File.stat(path)
|
|
46
|
+
|
|
47
|
+
response.status = 200
|
|
48
|
+
# Set the last modified times as well and etag for all files
|
|
49
|
+
response.header[Mongrel::Const::LAST_MODIFIED] = file_status.mtime.httpdate
|
|
50
|
+
# Calculated the same as apache, not sure how well the works on win32
|
|
51
|
+
response.header[Mongrel::Const::ETAG] = Mongrel::Const::ETAG_FORMAT % [file_status.mtime.to_i, file_status.size, file_status.ino]
|
|
52
|
+
#set the content type to something generic for now
|
|
53
|
+
response.header[Mongrel::Const::CONTENT_TYPE] = @default_content_type
|
|
54
|
+
#set the content disposition and filename
|
|
55
|
+
response.header['Content-Disposition'] = "attachment; filename=\"#{File.basename(path)}\""
|
|
56
|
+
|
|
57
|
+
# send a status with out content length
|
|
58
|
+
response.send_status(file_status.size)
|
|
59
|
+
response.send_header
|
|
60
|
+
response.send_file(path)
|
|
61
|
+
end
|
|
62
|
+
end
|
data/tools/rakehelp.rb
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
|
|
2
|
+
def make(makedir)
|
|
3
|
+
Dir.chdir(makedir) do
|
|
4
|
+
sh(PLATFORM =~ /win32/ ? 'nmake' : 'make')
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def extconf(dir)
|
|
10
|
+
Dir.chdir(dir) do ruby "extconf.rb" end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def setup_tests
|
|
15
|
+
Rake::TestTask.new do |t|
|
|
16
|
+
t.libs << "test"
|
|
17
|
+
t.test_files = FileList['test/test*.rb']
|
|
18
|
+
t.verbose = true
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def setup_clean otherfiles
|
|
24
|
+
files = ['build/*', '**/*.o', '**/*.so', '**/*.a', 'lib/*-*', '**/*.log'] + otherfiles
|
|
25
|
+
CLEAN.include(files)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def setup_rdoc files
|
|
30
|
+
Rake::RDocTask.new do |rdoc|
|
|
31
|
+
rdoc.rdoc_dir = 'doc/rdoc'
|
|
32
|
+
rdoc.options << '--line-numbers'
|
|
33
|
+
rdoc.rdoc_files.add(files)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def setup_extension(dir, extension)
|
|
39
|
+
ext = "ext/#{dir}"
|
|
40
|
+
ext_so = "#{ext}/#{extension}.#{Config::CONFIG['DLEXT']}"
|
|
41
|
+
ext_files = FileList[
|
|
42
|
+
"#{ext}/*.c",
|
|
43
|
+
"#{ext}/*.h",
|
|
44
|
+
"#{ext}/extconf.rb",
|
|
45
|
+
"#{ext}/Makefile",
|
|
46
|
+
"lib"
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
task "lib" do
|
|
50
|
+
directory "lib"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
desc "Builds just the #{extension} extension"
|
|
54
|
+
task extension.to_sym => ["#{ext}/Makefile", ext_so ]
|
|
55
|
+
|
|
56
|
+
file "#{ext}/Makefile" => ["#{ext}/extconf.rb"] do
|
|
57
|
+
extconf "#{ext}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
file ext_so => ext_files do
|
|
61
|
+
make "#{ext}"
|
|
62
|
+
cp ext_so, "lib"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def base_gem_spec(pkg_name, pkg_version)
|
|
68
|
+
pkg_version = pkg_version
|
|
69
|
+
pkg_name = pkg_name
|
|
70
|
+
pkg_file_name = "#{pkg_name}-#{pkg_version}"
|
|
71
|
+
Gem::Specification.new do |s|
|
|
72
|
+
s.name = pkg_name
|
|
73
|
+
s.version = pkg_version
|
|
74
|
+
s.platform = Gem::Platform::RUBY
|
|
75
|
+
s.has_rdoc = true
|
|
76
|
+
s.extra_rdoc_files = [ "README" ]
|
|
77
|
+
|
|
78
|
+
s.files = %w(COPYING LICENSE README Rakefile) +
|
|
79
|
+
Dir.glob("{bin,doc/rdoc,test,lib}/**/*") +
|
|
80
|
+
Dir.glob("ext/**/*.{h,c,rb}") +
|
|
81
|
+
Dir.glob("examples/**/*.rb") +
|
|
82
|
+
Dir.glob("tools/*.rb")
|
|
83
|
+
|
|
84
|
+
s.require_path = "lib"
|
|
85
|
+
s.extensions = FileList["ext/**/extconf.rb"].to_a
|
|
86
|
+
s.bindir = "bin"
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def setup_gem(pkg_name, pkg_version)
|
|
91
|
+
spec = base_gem_spec(pkg_name, pkg_version)
|
|
92
|
+
yield spec if block_given?
|
|
93
|
+
|
|
94
|
+
Rake::GemPackageTask.new(spec) do |p|
|
|
95
|
+
p.gem_spec = spec
|
|
96
|
+
p.need_tar = true
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def setup_win32_gem(pkg_name, pkg_version)
|
|
101
|
+
spec = base_gem_spec(pkg_name, pkg_version)
|
|
102
|
+
yield spec if block_given?
|
|
103
|
+
|
|
104
|
+
Gem::Builder.new(spec).build
|
|
105
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: mongrel_crypted_download
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: "0.4"
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Andriy Bazyuta
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2008-06-19 00:00:00 +03:00
|
|
13
|
+
default_executable:
|
|
14
|
+
dependencies:
|
|
15
|
+
- !ruby/object:Gem::Dependency
|
|
16
|
+
name: gem_plugin
|
|
17
|
+
version_requirement:
|
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
19
|
+
requirements:
|
|
20
|
+
- - ">="
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: 0.2.1
|
|
23
|
+
version:
|
|
24
|
+
- !ruby/object:Gem::Dependency
|
|
25
|
+
name: mongrel
|
|
26
|
+
version_requirement:
|
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
28
|
+
requirements:
|
|
29
|
+
- - ">="
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: 0.3.13
|
|
32
|
+
version:
|
|
33
|
+
description: Mongrel Crypted Download Plugin
|
|
34
|
+
email:
|
|
35
|
+
executables: []
|
|
36
|
+
|
|
37
|
+
extensions: []
|
|
38
|
+
|
|
39
|
+
extra_rdoc_files:
|
|
40
|
+
- README
|
|
41
|
+
files:
|
|
42
|
+
- COPYING
|
|
43
|
+
- LICENSE
|
|
44
|
+
- README
|
|
45
|
+
- Rakefile
|
|
46
|
+
- lib/mongrel_crypted_download
|
|
47
|
+
- lib/mongrel_crypted_download/init.rb
|
|
48
|
+
- tools/rakehelp.rb
|
|
49
|
+
- resources/defaults.yaml
|
|
50
|
+
has_rdoc: true
|
|
51
|
+
homepage:
|
|
52
|
+
post_install_message:
|
|
53
|
+
rdoc_options: []
|
|
54
|
+
|
|
55
|
+
require_paths:
|
|
56
|
+
- lib
|
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: "0"
|
|
62
|
+
version:
|
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: "0"
|
|
68
|
+
version:
|
|
69
|
+
requirements: []
|
|
70
|
+
|
|
71
|
+
rubyforge_project:
|
|
72
|
+
rubygems_version: 0.9.5
|
|
73
|
+
signing_key:
|
|
74
|
+
specification_version: 2
|
|
75
|
+
summary: Mongrel Crypted Download Plugin
|
|
76
|
+
test_files: []
|
|
77
|
+
|