useless 0.1.2 → 0.2.0
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/lib/useless.rb +11 -1
- data/lib/useless/rack.rb +18 -26
- data/lib/useless/rack/authentication/access_token.rb +39 -0
- data/lib/useless/rack/authentication/query_string.rb +19 -0
- data/lib/useless/rack/authentication/request_header.rb +19 -0
- data/lib/useless/rack/exceptions.rb +28 -0
- data/lib/useless/rack/files.rb +37 -0
- data/lib/useless/rack/fs.rb +17 -0
- data/lib/useless/rack/mongo.rb +16 -0
- data/lib/useless/version.rb +1 -1
- data/spec/spec_helper.rb +4 -3
- data/spec/useless/fs_spec.rb +1 -0
- data/spec/useless/mongo_spec.rb +1 -0
- data/spec/useless/rack/{middleware/authentication → authentication}/query_string_spec.rb +8 -7
- data/spec/useless/rack/{middleware/authentication → authentication}/request_header_spec.rb +8 -7
- data/spec/useless/rack/exceptions_spec.rb +31 -0
- data/spec/useless/rack/files_spec.rb +49 -0
- data/spec/useless/rack/fs_spec.rb +18 -0
- data/spec/useless/rack/mongo_spec.rb +18 -0
- data/spec/useless/rack_spec.rb +77 -0
- data/useless.gemspec +7 -7
- metadata +49 -50
- data/assets/DOC.html +0 -39
- data/assets/application.css +0 -134
- data/lib/useless/logger.rb +0 -48
- data/lib/useless/rack/base.rb +0 -26
- data/lib/useless/rack/base/files.rb +0 -33
- data/lib/useless/rack/middleware/assets.rb +0 -38
- data/lib/useless/rack/middleware/authentication/access_token.rb +0 -41
- data/lib/useless/rack/middleware/authentication/query_string.rb +0 -21
- data/lib/useless/rack/middleware/authentication/request_header.rb +0 -21
- data/lib/useless/rack/middleware/exceptions.rb +0 -41
- data/lib/useless/rack/middleware/fs.rb +0 -17
- data/lib/useless/rack/middleware/mongo.rb +0 -18
- data/spec/useless/logger_spec.rb +0 -87
- data/spec/useless/rack/base/files_spec.rb +0 -42
- data/spec/useless/rack/middleware/exceptions_spec.rb +0 -47
data/lib/useless.rb
CHANGED
data/lib/useless/rack.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'low/rack/default'
|
2
|
+
require 'low/rack/exceptions'
|
1
3
|
require 'low/rack/log_level'
|
2
4
|
require 'low/rack/rack_errors'
|
3
5
|
require 'low/rack/request_id'
|
@@ -5,29 +7,17 @@ require 'low/rack/request_logger'
|
|
5
7
|
require 'low/rack/subdomain_map'
|
6
8
|
require 'low/scoped_logger'
|
7
9
|
|
8
|
-
require 'useless/
|
9
|
-
require 'useless/
|
10
|
-
|
11
|
-
require 'useless/rack/
|
12
|
-
require 'useless/rack/
|
13
|
-
require 'useless/rack/middleware/mongo'
|
14
|
-
require 'useless/rack/middleware/fs'
|
15
|
-
require 'useless/rack/middleware/authentication/query_string'
|
16
|
-
require 'useless/rack/middleware/authentication/request_header'
|
17
|
-
require 'useless/rack/base'
|
10
|
+
require 'useless/rack/exceptions'
|
11
|
+
require 'useless/rack/fs'
|
12
|
+
require 'useless/rack/mongo'
|
13
|
+
require 'useless/rack/authentication/query_string'
|
14
|
+
require 'useless/rack/authentication/request_header'
|
18
15
|
|
19
16
|
module Useless
|
20
17
|
class Rack
|
21
18
|
|
22
|
-
def
|
23
|
-
@
|
24
|
-
end
|
25
|
-
|
26
|
-
def self.fs
|
27
|
-
@fs ||= Useless::FS.new(mongo)
|
28
|
-
end
|
29
|
-
|
30
|
-
def initialize(map = nil, &block)
|
19
|
+
def initialize(base = nil, map = nil, &block)
|
20
|
+
@base = base
|
31
21
|
@map = map || {}
|
32
22
|
instance_eval &block if block_given?
|
33
23
|
end
|
@@ -38,22 +28,24 @@ module Useless
|
|
38
28
|
end
|
39
29
|
|
40
30
|
def call(env)
|
31
|
+
base = @base
|
41
32
|
map = @map
|
42
33
|
|
43
34
|
::Rack::Builder.app do
|
44
|
-
use
|
35
|
+
use Low::Rack::Default
|
36
|
+
use Low::Rack::Exceptions
|
45
37
|
use Low::Rack::RequestId
|
46
38
|
use Low::Rack::RackErrors
|
47
39
|
use Low::Rack::LogLevel
|
48
40
|
use Low::Rack::RequestLogger, key: 'useless.logger'
|
49
41
|
use ::Rack::CommonLogger
|
50
|
-
use
|
51
|
-
use
|
52
|
-
use
|
53
|
-
use
|
54
|
-
use
|
42
|
+
use Useless::Rack::Mongo
|
43
|
+
use Useless::Rack::FS
|
44
|
+
use Useless::Rack::Authentication::QueryString
|
45
|
+
use Useless::Rack::Authentication::RequestHeader
|
46
|
+
use Useless::Rack::Exceptions
|
55
47
|
|
56
|
-
run Low::Rack::SubdomainMap.new(
|
48
|
+
run Low::Rack::SubdomainMap.new(base, map)
|
57
49
|
end.call(env)
|
58
50
|
end
|
59
51
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Useless
|
2
|
+
class Rack
|
3
|
+
module Authentication
|
4
|
+
# The `Authentication::AccessToken` module defines the behavior for access-
|
5
|
+
# token-based authentication middleware. The middlewares are responsible
|
6
|
+
# only for providing the access token via the `#access_token_for_env`
|
7
|
+
# method.
|
8
|
+
module AccessToken
|
9
|
+
def initialize(app)
|
10
|
+
@app = app
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
# If we don't already have a user set in the environment,
|
15
|
+
unless env['useless.user']
|
16
|
+
# check to see if an access token was specified.
|
17
|
+
if access_token = access_token_for_env(env)
|
18
|
+
# If so, and a corresponding user can be found,
|
19
|
+
if user = env['useless.mongo']['users'].find_one('access_token' => access_token)
|
20
|
+
# set 'useless.user' in the environment.
|
21
|
+
env['useless.user'] = user
|
22
|
+
else
|
23
|
+
# Otherwise, return a 401 Unauthorized.
|
24
|
+
return [401, {'Content-Type' => 'text/plain'}, ["Invalid access token: #{access_token}"]]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
@app.call(env)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def access_token_for_env(env)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'useless/rack/authentication/access_token'
|
2
|
+
|
3
|
+
module Useless
|
4
|
+
class Rack
|
5
|
+
module Authentication
|
6
|
+
# The `Authentication::QueryString` middleware attempt to retrieve the
|
7
|
+
# access token from the query string.
|
8
|
+
class QueryString
|
9
|
+
include AccessToken
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def access_token_for_env(env)
|
14
|
+
::Rack::Utils.parse_query(env['QUERY_STRING'])['access_token']
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'useless/rack/authentication/access_token'
|
2
|
+
|
3
|
+
module Useless
|
4
|
+
class Rack
|
5
|
+
module Authentication
|
6
|
+
# The `Authentication::RequestHeader` middleware attempts to retrieve the
|
7
|
+
# access token from the `Authorization` request header.
|
8
|
+
class RequestHeader
|
9
|
+
include AccessToken
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def access_token_for_env(env)
|
14
|
+
env['HTTP_AUTHORIZATION']
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Useless
|
2
|
+
class Rack
|
3
|
+
# `Rack::Exceptions` sets 'low.show_exceptions' to true if the request
|
4
|
+
# has been authenticated as an admin user.
|
5
|
+
class Exceptions
|
6
|
+
def initialize(app)
|
7
|
+
@app = app
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
# If low.show_exceptions has yet to be set,
|
12
|
+
unless env['low.show_exceptions']
|
13
|
+
|
14
|
+
# set it to true if
|
15
|
+
env['low.show_exceptions'] =
|
16
|
+
|
17
|
+
# the request has been authenticated,
|
18
|
+
(!env['useless.user'].nil? and
|
19
|
+
|
20
|
+
# and the user is an admin.
|
21
|
+
(env['useless.user']['admin'] == true))
|
22
|
+
end
|
23
|
+
|
24
|
+
@app.call(env)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'useless/fs'
|
2
|
+
|
3
|
+
module Useless
|
4
|
+
class Rack
|
5
|
+
# `Rack::Files` retrieves files from Useless::FS and serves them up.
|
6
|
+
class Files
|
7
|
+
|
8
|
+
def initialize(app)
|
9
|
+
@app = app
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
if env['PATH_INFO'] =~ /^\/file/
|
14
|
+
begin
|
15
|
+
# The file ID is everything after the second '/' in the path
|
16
|
+
id = env['PATH_INFO'].split('/')[2]
|
17
|
+
|
18
|
+
# Retrieve the file from FS
|
19
|
+
file = env['useless.fs'].get(BSON::ObjectId(id))
|
20
|
+
|
21
|
+
# and serve it up with the associated content type
|
22
|
+
return [200, {'Content-Type' => file.content_type}, file.read]
|
23
|
+
|
24
|
+
# Two things can go wrong, and they'll both raise an error:
|
25
|
+
# * the specified ID is not a valid object ID
|
26
|
+
# * there is no file corresponding to the ID in the FS
|
27
|
+
rescue BSON::InvalidObjectId, Useless::FS::FileNotFound
|
28
|
+
[404, {'Content-Type' => 'text/plain'}, "File not found: #{id}"]
|
29
|
+
end
|
30
|
+
else
|
31
|
+
@app.call(env)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'useless'
|
2
|
+
|
3
|
+
module Useless
|
4
|
+
class Rack
|
5
|
+
# `Rack::FS` adds the Useless.fs instance to the env as 'useless.fs'.
|
6
|
+
class FS
|
7
|
+
def initialize(app)
|
8
|
+
@app = app
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
env['useless.fs'] = Useless.fs
|
13
|
+
@app.call(env)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Useless
|
2
|
+
class Rack
|
3
|
+
# `Rack::Mongo` adds the Useless.mongo instance to the env as
|
4
|
+
# 'useless.mongo'.
|
5
|
+
class Mongo
|
6
|
+
def initialize(app)
|
7
|
+
@app = app
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
env['useless.mongo'] = Useless.mongo
|
12
|
+
@app.call(env)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/useless/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
ENV['RACK_ENV'] = 'test'
|
2
|
-
|
2
|
+
|
3
|
+
require File.dirname(__FILE__) + '/../lib/useless'
|
3
4
|
|
4
5
|
require 'rack/mock'
|
5
6
|
require 'rack/test'
|
@@ -8,7 +9,7 @@ RSpec.configure do |config|
|
|
8
9
|
config.order = :rand
|
9
10
|
|
10
11
|
def clean_database
|
11
|
-
Useless
|
12
|
+
Useless.mongo.db.collections.each do |collection|
|
12
13
|
if !(collection.name =~ /^system\./)
|
13
14
|
collection.drop
|
14
15
|
end
|
@@ -22,7 +23,7 @@ RSpec.configure do |config|
|
|
22
23
|
|
23
24
|
# After dropping collections, we need to force a new connection on the
|
24
25
|
# next request.
|
25
|
-
Useless
|
26
|
+
Useless.mongo.reset_connection!
|
26
27
|
end
|
27
28
|
|
28
29
|
def asset_path(path)
|
data/spec/useless/fs_spec.rb
CHANGED
data/spec/useless/mongo_spec.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
require File.dirname(__FILE__) + '
|
2
|
-
require 'useless/rack/middleware/authentication/query_string'
|
1
|
+
require File.dirname(__FILE__) + '/../../../spec_helper'
|
3
2
|
|
4
|
-
|
3
|
+
require 'useless/rack/authentication/query_string'
|
4
|
+
|
5
|
+
describe Useless::Rack::Authentication::QueryString do
|
5
6
|
def authenticated_app
|
6
7
|
app = lambda do |env|
|
7
8
|
body = env['useless.user'] ?
|
@@ -11,12 +12,12 @@ describe Useless::Rack::Middleware::Authentication::QueryString do
|
|
11
12
|
[200, {'Content-Type' => 'text/plain'}, [body]]
|
12
13
|
end
|
13
14
|
|
14
|
-
query_string_app = Useless::Rack::
|
15
|
-
Useless::Rack::
|
15
|
+
query_string_app = Useless::Rack::Authentication::QueryString.new(app)
|
16
|
+
Useless::Rack::Mongo.new(query_string_app)
|
16
17
|
end
|
17
18
|
|
18
19
|
it 'should do nothing if `useless.user` is already set' do
|
19
|
-
Useless
|
20
|
+
Useless.mongo['users'].insert({'handle' => 'dph', 'access_token' => 'def456'})
|
20
21
|
res = Rack::MockRequest.new(authenticated_app).get('/resource?access_token=def456',
|
21
22
|
'useless.user' => {'handle' => 'khy', 'access_token' => 'abc123'})
|
22
23
|
res.should be_ok
|
@@ -38,7 +39,7 @@ describe Useless::Rack::Middleware::Authentication::QueryString do
|
|
38
39
|
|
39
40
|
it 'should set `useless.user` in the environment if a user can be found for
|
40
41
|
the specified access token' do
|
41
|
-
Useless
|
42
|
+
Useless.mongo['users'].insert({'handle' => 'khy', 'access_token' => 'abc123'})
|
42
43
|
res = Rack::MockRequest.new(authenticated_app).get('/resource?access_token=abc123')
|
43
44
|
res.should be_ok
|
44
45
|
res.body.should == 'user authenticated: khy'
|
@@ -1,7 +1,8 @@
|
|
1
|
-
require File.dirname(__FILE__) + '
|
2
|
-
require 'useless/rack/middleware/authentication/request_header'
|
1
|
+
require File.dirname(__FILE__) + '/../../../spec_helper'
|
3
2
|
|
4
|
-
|
3
|
+
require 'useless/rack/authentication/request_header'
|
4
|
+
|
5
|
+
describe Useless::Rack::Authentication::RequestHeader do
|
5
6
|
def authenticated_app
|
6
7
|
app = lambda do |env|
|
7
8
|
body = env['useless.user'] ?
|
@@ -11,12 +12,12 @@ describe Useless::Rack::Middleware::Authentication::RequestHeader do
|
|
11
12
|
[200, {'Content-Type' => 'text/plain'}, [body]]
|
12
13
|
end
|
13
14
|
|
14
|
-
request_header_app = Useless::Rack::
|
15
|
-
Useless::Rack::
|
15
|
+
request_header_app = Useless::Rack::Authentication::RequestHeader.new(app)
|
16
|
+
Useless::Rack::Mongo.new(request_header_app)
|
16
17
|
end
|
17
18
|
|
18
19
|
it 'should do nothing if `useless.user` is already set' do
|
19
|
-
Useless
|
20
|
+
Useless.mongo['users'].insert({'handle' => 'dph', 'access_token' => 'def456'})
|
20
21
|
res = Rack::MockRequest.new(authenticated_app).get('/resource',
|
21
22
|
'Authorization' => 'def456',
|
22
23
|
'useless.user' => {'handle' => 'khy', 'access_token' => 'abc123'})
|
@@ -39,7 +40,7 @@ describe Useless::Rack::Middleware::Authentication::RequestHeader do
|
|
39
40
|
|
40
41
|
it 'should set `useless.user` in the environment if a user can be found for
|
41
42
|
the specified access token' do
|
42
|
-
Useless
|
43
|
+
Useless.mongo['users'].insert({'handle' => 'khy', 'access_token' => 'abc123'})
|
43
44
|
res = Rack::MockRequest.new(authenticated_app).get('/resource', 'HTTP_AUTHORIZATION' => 'abc123')
|
44
45
|
res.should be_ok
|
45
46
|
res.body.should == 'user authenticated: khy'
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
|
3
|
+
require 'useless/rack/exceptions'
|
4
|
+
|
5
|
+
describe Useless::Rack::Exceptions do
|
6
|
+
|
7
|
+
def app
|
8
|
+
base = lambda do |env|
|
9
|
+
[200, {'Content-Type' => 'text/plain'}, [env['low.show_exceptions']]]
|
10
|
+
end
|
11
|
+
|
12
|
+
Useless::Rack::Exceptions.new(base)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should set low.show_exceptions to false if the request is not authenticated' do
|
16
|
+
res = Rack::MockRequest.new(app).get('http://useless.info')
|
17
|
+
res.body.should == 'false'
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should set low.show_exceptions to true if the request is authenticated by an admin user' do
|
21
|
+
res = Rack::MockRequest.new(app).get('http://useless.info',
|
22
|
+
'useless.user' => {'handle' => 'khy', 'admin' => true})
|
23
|
+
res.body.should == 'true'
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should set low.show_exceptions to false if request is authenticated by an non-admin user' do
|
27
|
+
res = Rack::MockRequest.new(app).get('http://useless.info',
|
28
|
+
'useless.user' => {'handle' => 'khy'})
|
29
|
+
res.body.should == 'false'
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
|
3
|
+
require 'useless/rack'
|
4
|
+
require 'useless/rack/files'
|
5
|
+
|
6
|
+
describe Useless::Rack::Files do
|
7
|
+
include Rack::Test::Methods
|
8
|
+
|
9
|
+
def app
|
10
|
+
@app ||= begin
|
11
|
+
base = -> env { [200, {'Content-Type' => 'text/plain'}, ['base']] }
|
12
|
+
|
13
|
+
subdomain = ::Rack::Builder.app do
|
14
|
+
use Useless::Rack::Files
|
15
|
+
|
16
|
+
run -> env do
|
17
|
+
[200, { 'Content-Type' => 'text/plain' }, ['whatever']]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Useless::Rack.new base do
|
22
|
+
map 'subdomain' => subdomain
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should return a file if one exists for the specified ID' do
|
28
|
+
file = File.open(asset_path('muffin-milk.jpg'))
|
29
|
+
id = Useless.fs.put(file)
|
30
|
+
|
31
|
+
get "http://subdomain.useless.io/files/#{id}"
|
32
|
+
last_response.should be_ok
|
33
|
+
|
34
|
+
file.rewind
|
35
|
+
blob = file.read
|
36
|
+
blob.force_encoding('BINARY')
|
37
|
+
blob.should == last_response.body
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should return a 404 if the file does not exits' do
|
41
|
+
get "http://subdomain.useless.io/files/#{BSON::ObjectId.new}"
|
42
|
+
last_response.should be_not_found
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should return a 404 if the specified ID is invalid' do
|
46
|
+
get 'http://subdomain.useless.io/files/invalid-id'
|
47
|
+
last_response.should be_not_found
|
48
|
+
end
|
49
|
+
end
|