jko_api 0.1.3 → 0.2.4

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: 3711a73e0bd2f3bb5186bf24f11d4530b9bbeb6c
4
- data.tar.gz: e3a28ae1fc62c36cf5a3b77ee0f6820873fb9a75
3
+ metadata.gz: 2b71ffbde46caf94241d8813b48dc53c2b737f77
4
+ data.tar.gz: 06f6e116abebe5c5efcae4614a8e8c49178e69b5
5
5
  SHA512:
6
- metadata.gz: c6adaaad9e11e56f1971934b7522dae6cc7376e18ddae3b3358202ee73dc85452327d5eab3a62c594e05f525ced2320ff1df5b1acbcf03b8e8ce0faedcbadc6a
7
- data.tar.gz: 26a75d36b762ffa83d8f74f93374b5a843f73fdc9d1c80e3f969afdd68722d2055e9d527e239b722f70477304680f2de002490f19c041bace08d668cd4816bc3
6
+ metadata.gz: 3b0e444f0a11c75fedbd4e3d9865582b60179ad78f75f2213248619b2c3b17d9ac42a63e77429a6d79d3f2dba23f28372b86a9bfc1c91f59102f6b392ccc0061
7
+ data.tar.gz: 75ced80d1207ab1c3457d602a515e8d169b4a30572ee1cad16636ec888c479e24a6bef1f483cd9a4d811043fc666cfe4b127c0390ec512a0a7a7a6e8f18a6ef4
data/Gemfile CHANGED
@@ -1,4 +1,5 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ gem 'warden-oauth2-strategies', github: 'airservice/warden-oauth2', require: false
3
4
  # Specify your gem's dependencies in jko_api.gemspec
4
5
  gemspec
data/README.md CHANGED
@@ -6,7 +6,11 @@ This gem lets you make a single change and reversion an API without copying over
6
6
 
7
7
  ## Props
8
8
 
9
- 99% of this gem was written by [Justin Ko](https://github.com/justinko). Since he's lazy and won't make a gem from it, I'm doing it for him :stuck_out_tongue: That's why the gem is named after him.
9
+ 90% of the code was originally written by [Justin Ko](https://github.com/justinko). Since he's lazy and won't make a gem from it, I'm doing it for him :stuck_out_tongue: That's why the gem is named after him. I've since modified some of the stuff, and will continue to make it easier to use and more configurable.
10
+
11
+ ## Requirements
12
+
13
+ Rails 5 and Ruby 2.3
10
14
 
11
15
  ## Installation
12
16
 
@@ -31,9 +35,19 @@ In your `config/initializers/jko_api.rb` add
31
35
  ```ruby
32
36
  JkoApi.configure do |c|
33
37
  # This is the default. You can override this if you need a different controller
34
- # c.base_controller = Api::ApplicationController
38
+ # c.base_controller = ApiApplicationController
39
+
35
40
  # This is the folder name where all the api controllers would go
36
41
  # c.api_namespace = 'api'
42
+
43
+ # If you want to use your own authentication stuff, leave this as default
44
+ # c.use_authentication = false
45
+
46
+ # The built in authentication is Warden::OAuth2 with a Bearer strategy. Currently no other option exists
47
+ # c.strategy = :bearer
48
+
49
+ # If you use the authentication, then you will need to make some model, and set this value. More notes below.
50
+ # c.token_model = SomeModelYouCreated
37
51
  end
38
52
  ```
39
53
 
@@ -53,10 +67,10 @@ JkoApi.routes self do
53
67
  end
54
68
  ```
55
69
 
56
- Place your version 1 controller code in `app/controllers/api/v1`. Controllers should all inherit from `Api::ApplicationController`, or something that inherits from that.
70
+ Place your version 1 controller code in `app/controllers/api/v1`. Controllers should all inherit from `ApiApplicationController`, or something that inherits from that.
57
71
 
58
72
  ```ruby
59
- class Api::V1::BarsController < Api::ApplicationController
73
+ class Api::V1::BarsController < ApiApplicationController
60
74
  def index
61
75
  # do stuff
62
76
  end
@@ -80,7 +94,7 @@ end
80
94
  We can still do this though
81
95
 
82
96
  ```ruby
83
- class Api::V3::BarsController < Api::ApplicationController
97
+ class Api::V3::BarsController < ApiApplicationController
84
98
  def index
85
99
  # do different stuff
86
100
  end
@@ -89,6 +103,63 @@ end
89
103
 
90
104
  You can test this all by booting up a simple rails app, then do `curl -H "Accept: application/vnd.api.v1+json" http://localhost:3000/bars`
91
105
 
106
+ The `Accept` header is required to make the calls.
107
+
108
+ ## Authentication
109
+ If your API calls need to be authenticated by some API key, then you will need to configure a few more things.
110
+
111
+ ```ruby
112
+ JkoApi.configure do |c|
113
+ c.use_authentication = true
114
+ c.token_model = MyCustomTokenVerifier
115
+ end
116
+ ```
117
+
118
+ Your `MyCustomTokenVerifier` must be a ruby object that responds to a `locate` class method, and take a single string argument for the api token that needs to be authenticated. The method should return the token string if it's valid, and nil if it's not. You can [read more here](https://github.com/opperator/warden-oauth2#access-token).
119
+
120
+ This `MyCustomTokenVerifier` could look a few different ways. One way would be to make this a rails model like
121
+
122
+ ```ruby
123
+ class MyCustomTokenVerifier < ApplicationModel
124
+
125
+ def self.locate(token_string)
126
+ find_by(api_token: token_string)
127
+ end
128
+
129
+ end
130
+ ```
131
+
132
+ Another way would be a simple class that verifies against some configuration option like
133
+
134
+ ```ruby
135
+ class MyCustomTokenVerifier
136
+
137
+ def self.locate(token_string)
138
+ if token_string == Rails.configuration.x.my_custom_api_token
139
+ token_string
140
+ else
141
+ nil
142
+ end
143
+ end
144
+ end
145
+ ```
146
+
147
+ ## Testing
148
+ If you're trying to test controllers, keep in mind that Rails doesn't process the middleware stack for controller tests. This means that the `JkoApi::Middleware` as well as the `JkoApi::Strategies` modules won't exist, and neither will `warden`. You can [read up more here](https://github.com/hassox/warden/issues/117).
149
+
150
+ My recommendation for testing api endpoints is doing an integration test. With RSpec, just create your `spec/requests` folder, and add your spec for the endpoint you want to test.
151
+
152
+ If you need access to a mock warden object, you can add
153
+
154
+ ```ruby
155
+ require 'jko_api/test_helpers'
156
+ include JkoApi::TestHelpers
157
+ ```
158
+
159
+ ## Debugging notes
160
+
161
+ If you have a route that matches one of the API routes, and it's listed first, then Rails will match that route first. This might not be what you expected, so put your `JkoApi.routes` near the top to ensure that gets hit first.
162
+
92
163
 
93
164
  ## Contributing
94
165
 
@@ -0,0 +1,3 @@
1
+ class ApiApplicationController < ActionController::Base
2
+ include JkoApi::Controller
3
+ end
@@ -0,0 +1,7 @@
1
+ JkoApi.auth_setup do
2
+ Warden::OAuth2.configure do |config|
3
+ if JkoApi.configuration.strategy.bearer?
4
+ config.token_model = JkoApi.configuration.token_model
5
+ end
6
+ end
7
+ end
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.email = ["jeremywoertink@gmail.com"]
11
11
  spec.summary = %q{A Rails API gem}
12
12
  spec.description = %q{Some Rails API code written by JustinKo and ported to a badly written gem}
13
- spec.homepage = ""
13
+ spec.homepage = "https://github.com/jwoertink/jko_api"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.7"
22
22
  spec.add_development_dependency "rake", "~> 10.0"
23
- spec.add_dependency "responders", "2.1.0"
24
- spec.add_dependency "rails", ">= 4.2.0", "< 5"
23
+ spec.add_dependency "responders", "2.2.0"
24
+ spec.add_dependency "rails", ">= 5.0.0.beta3", "< 5.1"
25
+ spec.add_dependency "warden-oauth2"
25
26
  end
@@ -2,7 +2,9 @@ require "active_support/all"
2
2
  require "active_model/railtie"
3
3
  require "action_controller/railtie"
4
4
  require "responders"
5
+ require "warden-oauth2"
5
6
  require "jko_api/version"
7
+ require "jko_api/strategies"
6
8
  require "jko_api/class_descendants_builder"
7
9
  require "jko_api/constraints"
8
10
  require "jko_api/controller"
@@ -20,6 +22,7 @@ module JkoApi
20
22
  ACCEPT_HEADER_REGEX = /\Aapplication\/vnd\.api(\.v([0-9]))?\+json\z/
21
23
 
22
24
  mattr_accessor :configuration, instance_accessor: false
25
+ mattr_accessor :auth_initializer, instance_accessor: false
23
26
  mattr_reader :current_version_number, instance_reader: false
24
27
 
25
28
  def self.configure
@@ -30,7 +33,8 @@ module JkoApi
30
33
 
31
34
  def self.setup(base_controller)
32
35
  Util.stupid_hack!
33
- ClassDescendantsBuilder.build base_controller, level: max_version_number
36
+ ClassDescendantsBuilder.build(base_controller, upto: max_version_number)
37
+ @@auth_initializer.call
34
38
  end
35
39
 
36
40
  def self.activated?
@@ -66,4 +70,8 @@ module JkoApi
66
70
  JkoApi.versions(context, &block)
67
71
  end
68
72
  end
73
+
74
+ def self.auth_setup(&block)
75
+ self.auth_initializer = block
76
+ end
69
77
  end
@@ -2,45 +2,49 @@ module JkoApi
2
2
  class ClassDescendantsBuilder
3
3
  LEVEL_REGEX = /\d+/
4
4
 
5
- def self.build(base_class, level:)
5
+ def self.build(base_class, upto:)
6
6
  base_class.descendants.each do |descendant|
7
- new(descendant, level).build
7
+ new(descendant, upto).build
8
8
  end
9
9
  end
10
10
 
11
- def initialize(descendant, level)
12
- @descendant, @level = descendant, level
11
+ attr_reader :descendant, :upto
12
+
13
+ def initialize(descendant, upto)
14
+ @descendant, @upto = descendant, upto
13
15
  end
14
16
 
15
17
  def build
16
- initial_level.upto(@level - 1) do |level|
17
- build_descendant(level) unless descendant_defined?(level)
18
+ initial_level.upto(upto) do |level|
19
+ unless Module.qualified_const_defined?(swap_level(level.next))
20
+ build_descendant level
21
+ end
18
22
  end
19
23
  end
20
24
 
21
25
  private
22
26
 
23
- def descendant_defined?(level)
24
- !!swap_level(level.next).safe_constantize
25
- end
26
-
27
27
  def build_descendant(level)
28
- namespace(level.next).constantize.const_set(
28
+ namespace(level.next).const_set(
29
29
  swap_level(level.next).demodulize,
30
30
  Class.new(swap_level(level).constantize)
31
31
  )
32
32
  end
33
33
 
34
34
  def namespace(level)
35
- swap_level(level).deconstantize.presence || 'Object'
35
+ deconstantized = swap_level(level).deconstantize
36
+ unless Module.qualified_const_defined?(deconstantized)
37
+ Module.qualified_const_set deconstantized, Module.new
38
+ end
39
+ deconstantized.constantize
36
40
  end
37
41
 
38
42
  def swap_level(level)
39
- @descendant.name.sub LEVEL_REGEX, level.to_s
43
+ descendant.name.sub LEVEL_REGEX, level.to_s
40
44
  end
41
45
 
42
46
  def initial_level
43
- @descendant.name[LEVEL_REGEX].to_i
47
+ descendant.name[LEVEL_REGEX].to_i
44
48
  end
45
49
  end
46
50
  end
@@ -1,11 +1,21 @@
1
1
  module JkoApi
2
2
  class Configuration
3
3
 
4
- attr_accessor :base_controller, :api_namespace
4
+ attr_accessor :base_controller, :api_namespace, :use_authentication, :token_model
5
5
 
6
6
  def initialize
7
- @base_controller = Api::ApplicationController
7
+ @base_controller = ApiApplicationController
8
8
  @api_namespace = 'api'
9
+ @use_authentication = false
10
+ @strategy = :bearer
11
+ end
12
+
13
+ def authenticate?
14
+ use_authentication
15
+ end
16
+
17
+ def strategy
18
+ @strategy.to_s.inquiry
9
19
  end
10
20
 
11
21
  end
@@ -4,7 +4,12 @@ module JkoApi
4
4
 
5
5
  included do
6
6
  include JkoApi::ControllerHelpers
7
- skip_authentication # TODO: make this configurable
7
+
8
+ if JkoApi.configuration&.use_authentication
9
+ prepend_before_action :authenticate!
10
+ else
11
+ skip_authentication
12
+ end
8
13
 
9
14
  self.responder = JkoApi::Responder
10
15
  respond_to :json
@@ -15,5 +20,14 @@ module JkoApi
15
20
  def render_json_errors(status, message = status)
16
21
  render json: JkoApi::RequestError[status, message], status: status
17
22
  end
23
+
24
+ def warden
25
+ request.env['warden']
26
+ end
27
+
28
+ def authenticate!
29
+ token = warden.authenticate!
30
+ self.authenticated = !!token
31
+ end
18
32
  end
19
33
  end
@@ -5,7 +5,11 @@ module JkoApi
5
5
  included do
6
6
  class_attribute :authenticated
7
7
  self.authenticated = false
8
- append_before_action { raise 'setup authentication' unless authenticated }
8
+ append_before_action do
9
+ unless authenticated
10
+ render(json: {error: 'Authentication Failed'}, status: 401)
11
+ end
12
+ end
9
13
  end
10
14
 
11
15
  class_methods do
@@ -3,7 +3,12 @@ require 'rails/railtie'
3
3
  module JkoApi
4
4
  class Engine < ::Rails::Engine
5
5
  initializer "jko_api.configure_rails_initialization" do |app|
6
- app.middleware.use JkoApi::Middleware
6
+ app.middleware.use JkoApi::Middleware, only: proc { |env|
7
+ env['HTTP_ACCEPT'] && env['HTTP_ACCEPT'].include?('application/vnd.api') }
8
+
9
+ if JkoApi.configuration&.authenticate?
10
+ app.middleware.use "JkoApi::Strategies::#{JkoApi.configuration.strategy.to_s.capitalize}".constantize, only: proc { |env| env['HTTP_ACCEPT'] && env['HTTP_ACCEPT'].include?('application/vnd.api') }
11
+ end
7
12
  end
8
13
  end
9
14
  end
@@ -1,14 +1,17 @@
1
1
  module JkoApi
2
2
  class Middleware
3
- def initialize(app)
3
+ def initialize(app, options={})
4
4
  @app = app
5
+ @only = options[:only]
5
6
  end
6
7
 
7
8
  def call(env)
8
- if version_number = extract_version_number(env)
9
- ::JkoApi.current_version_number = version_number
10
- else
11
- ::JkoApi.reset
9
+ if @only && @only.call(env)
10
+ if version_number = extract_version_number(env)
11
+ ::JkoApi.current_version_number = version_number
12
+ else
13
+ ::JkoApi.reset
14
+ end
12
15
  end
13
16
  @app.call env
14
17
  end
@@ -0,0 +1 @@
1
+ require 'jko_api/strategies/bearer'
@@ -0,0 +1,26 @@
1
+ module JkoApi
2
+ module Strategies
3
+ class Bearer
4
+
5
+ def initialize(app, options ={})
6
+ @app = app
7
+ @only = options[:only]
8
+ @mgr = Warden::Manager.new(@app, options) do |config|
9
+ config.strategies.add :bearer, Warden::OAuth2::Strategies::Bearer
10
+ config.default_strategies :bearer
11
+ config.failure_app = Warden::OAuth2::FailureApp
12
+ end
13
+
14
+ @mgr
15
+ end
16
+
17
+ def call(env)
18
+ if @only && @only.call(env)
19
+ @mgr.call(env)
20
+ else
21
+ @app.call(env)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,9 @@
1
+ module JkoApi
2
+ module TestHelpers
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ include Warden::Test::Mock
7
+ end
8
+ end
9
+ end
@@ -1,3 +1,3 @@
1
1
  module JkoApi
2
- VERSION = "0.1.3"
2
+ VERSION = "0.2.4"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jko_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Woertink
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-17 00:00:00.000000000 Z
11
+ date: 2016-07-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -44,34 +44,48 @@ dependencies:
44
44
  requirements:
45
45
  - - '='
46
46
  - !ruby/object:Gem::Version
47
- version: 2.1.0
47
+ version: 2.2.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - '='
53
53
  - !ruby/object:Gem::Version
54
- version: 2.1.0
54
+ version: 2.2.0
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rails
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: 4.2.0
61
+ version: 5.0.0.beta3
62
62
  - - "<"
63
63
  - !ruby/object:Gem::Version
64
- version: '5'
64
+ version: '5.1'
65
65
  type: :runtime
66
66
  prerelease: false
67
67
  version_requirements: !ruby/object:Gem::Requirement
68
68
  requirements:
69
69
  - - ">="
70
70
  - !ruby/object:Gem::Version
71
- version: 4.2.0
71
+ version: 5.0.0.beta3
72
72
  - - "<"
73
73
  - !ruby/object:Gem::Version
74
- version: '5'
74
+ version: '5.1'
75
+ - !ruby/object:Gem::Dependency
76
+ name: warden-oauth2
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
75
89
  description: Some Rails API code written by JustinKo and ported to a badly written
76
90
  gem
77
91
  email:
@@ -85,7 +99,8 @@ files:
85
99
  - LICENSE.txt
86
100
  - README.md
87
101
  - Rakefile
88
- - app/controllers/api/application_controller.rb
102
+ - app/controllers/api_application_controller.rb
103
+ - config/initializers/warden.rb
89
104
  - jko_api.gemspec
90
105
  - lib/jko_api.rb
91
106
  - lib/jko_api/class_descendants_builder.rb
@@ -97,10 +112,13 @@ files:
97
112
  - lib/jko_api/middleware.rb
98
113
  - lib/jko_api/request_error.rb
99
114
  - lib/jko_api/responder.rb
115
+ - lib/jko_api/strategies.rb
116
+ - lib/jko_api/strategies/bearer.rb
117
+ - lib/jko_api/test_helpers.rb
100
118
  - lib/jko_api/util.rb
101
119
  - lib/jko_api/version.rb
102
120
  - lib/jko_api/versioning.rb
103
- homepage: ''
121
+ homepage: https://github.com/jwoertink/jko_api
104
122
  licenses:
105
123
  - MIT
106
124
  metadata: {}
@@ -120,7 +138,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
120
138
  version: '0'
121
139
  requirements: []
122
140
  rubyforge_project:
123
- rubygems_version: 2.4.3
141
+ rubygems_version: 2.5.1
124
142
  signing_key:
125
143
  specification_version: 4
126
144
  summary: A Rails API gem
@@ -1,3 +0,0 @@
1
- class Api::ApplicationController < ActionController::Base
2
- include JkoApi::Controller
3
- end