useless 0.1.2 → 0.2.0

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