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.
Files changed (37) hide show
  1. data/lib/useless.rb +11 -1
  2. data/lib/useless/rack.rb +18 -26
  3. data/lib/useless/rack/authentication/access_token.rb +39 -0
  4. data/lib/useless/rack/authentication/query_string.rb +19 -0
  5. data/lib/useless/rack/authentication/request_header.rb +19 -0
  6. data/lib/useless/rack/exceptions.rb +28 -0
  7. data/lib/useless/rack/files.rb +37 -0
  8. data/lib/useless/rack/fs.rb +17 -0
  9. data/lib/useless/rack/mongo.rb +16 -0
  10. data/lib/useless/version.rb +1 -1
  11. data/spec/spec_helper.rb +4 -3
  12. data/spec/useless/fs_spec.rb +1 -0
  13. data/spec/useless/mongo_spec.rb +1 -0
  14. data/spec/useless/rack/{middleware/authentication → authentication}/query_string_spec.rb +8 -7
  15. data/spec/useless/rack/{middleware/authentication → authentication}/request_header_spec.rb +8 -7
  16. data/spec/useless/rack/exceptions_spec.rb +31 -0
  17. data/spec/useless/rack/files_spec.rb +49 -0
  18. data/spec/useless/rack/fs_spec.rb +18 -0
  19. data/spec/useless/rack/mongo_spec.rb +18 -0
  20. data/spec/useless/rack_spec.rb +77 -0
  21. data/useless.gemspec +7 -7
  22. metadata +49 -50
  23. data/assets/DOC.html +0 -39
  24. data/assets/application.css +0 -134
  25. data/lib/useless/logger.rb +0 -48
  26. data/lib/useless/rack/base.rb +0 -26
  27. data/lib/useless/rack/base/files.rb +0 -33
  28. data/lib/useless/rack/middleware/assets.rb +0 -38
  29. data/lib/useless/rack/middleware/authentication/access_token.rb +0 -41
  30. data/lib/useless/rack/middleware/authentication/query_string.rb +0 -21
  31. data/lib/useless/rack/middleware/authentication/request_header.rb +0 -21
  32. data/lib/useless/rack/middleware/exceptions.rb +0 -41
  33. data/lib/useless/rack/middleware/fs.rb +0 -17
  34. data/lib/useless/rack/middleware/mongo.rb +0 -18
  35. data/spec/useless/logger_spec.rb +0 -87
  36. data/spec/useless/rack/base/files_spec.rb +0 -42
  37. data/spec/useless/rack/middleware/exceptions_spec.rb +0 -47
@@ -1,4 +1,14 @@
1
- $:.push File.expand_path('../lib', __FILE__)
1
+ require 'useless/fs'
2
+ require 'useless/mongo'
2
3
 
3
4
  module Useless
5
+
6
+ def self.mongo
7
+ @mongo ||= Useless::Mongo.for_env
8
+ end
9
+
10
+ def self.fs
11
+ @fs ||= Useless::FS.new(mongo)
12
+ end
13
+
4
14
  end
@@ -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/fs'
9
- require 'useless/mongo'
10
-
11
- require 'useless/rack/middleware/exceptions'
12
- require 'useless/rack/middleware/assets'
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 self.mongo
23
- @mongo ||= Useless::Mongo.for_env
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 Middleware::Exceptions
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 Middleware::Assets
51
- use Middleware::Mongo
52
- use Middleware::FS
53
- use Middleware::Authentication::QueryString
54
- use Middleware::Authentication::RequestHeader
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(Base.new, map)
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
@@ -1,3 +1,3 @@
1
1
  module Useless
2
- VERSION = '0.1.2'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -1,5 +1,6 @@
1
1
  ENV['RACK_ENV'] = 'test'
2
- require File.dirname(__FILE__) + '/../lib/useless/rack'
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::Rack.mongo.db.collections.each do |collection|
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::Rack.mongo.reset_connection!
26
+ Useless.mongo.reset_connection!
26
27
  end
27
28
 
28
29
  def asset_path(path)
@@ -1,4 +1,5 @@
1
1
  require File.dirname(__FILE__) + '/../spec_helper'
2
+
2
3
  require 'useless/fs'
3
4
 
4
5
  describe Useless::FS do
@@ -1,4 +1,5 @@
1
1
  require File.dirname(__FILE__) + '/../spec_helper'
2
+
2
3
  require 'useless/mongo'
3
4
 
4
5
  describe Useless::Mongo do
@@ -1,7 +1,8 @@
1
- require File.dirname(__FILE__) + '/../../../../spec_helper'
2
- require 'useless/rack/middleware/authentication/query_string'
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
3
2
 
4
- describe Useless::Rack::Middleware::Authentication::QueryString do
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::Middleware::Authentication::QueryString.new(app)
15
- Useless::Rack::Middleware::Mongo.new(query_string_app)
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::Rack.mongo['users'].insert({'handle' => 'dph', 'access_token' => 'def456'})
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::Rack.mongo['users'].insert({'handle' => 'khy', 'access_token' => 'abc123'})
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__) + '/../../../../spec_helper'
2
- require 'useless/rack/middleware/authentication/request_header'
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
3
2
 
4
- describe Useless::Rack::Middleware::Authentication::RequestHeader do
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::Middleware::Authentication::RequestHeader.new(app)
15
- Useless::Rack::Middleware::Mongo.new(request_header_app)
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::Rack.mongo['users'].insert({'handle' => 'dph', 'access_token' => 'def456'})
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::Rack.mongo['users'].insert({'handle' => 'khy', 'access_token' => 'abc123'})
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