opro 0.4.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,18 @@
1
1
  language: ruby
2
+
2
3
  rvm:
3
4
  - 1.9.3
4
- - jruby-19mode
5
+ - 2.0.0
6
+ - jruby-19mode
7
+ - ruby-head
8
+
9
+ env:
10
+ - "RAILS_VERSION=3.2.0"
11
+ - "RAILS_VERSION=3.1.0"
12
+ - "RAILS_VERSION=4.0.0.pre"
13
+ - "RAILS_VERSION=master"
14
+
15
+ matrix:
16
+ allow_failures:
17
+ - rvm: ruby-head
18
+ - env: "RAILS_VERSION=master"
@@ -1,4 +1,12 @@
1
- ## master
1
+ ## 1.0.0.pre1
2
+
3
+ ## 0.5.0
4
+
5
+ - attr_accessible removed from models, replaced by explicitly assigning attributes where needed. Allows us to use the same code for Rails 3.X and 4.0 apps.
6
+
7
+ - [#28] Set HTTP verbs to be compatiable with Rails4 (@chatgris)
8
+
9
+ - [#21] Add token_type return from TokenController (@cicloid)
2
10
 
3
11
  ## 0.4.3
4
12
  - [#20] Bugfix: expires_in not correctly recalculated after auth_grant refreshed (@nvh)
@@ -54,4 +62,4 @@
54
62
 
55
63
  ## 0.0.1
56
64
 
57
- - Initial Release
65
+ - Initial Release
data/Gemfile CHANGED
@@ -1,26 +1,44 @@
1
- source "http://rubygems.org"
1
+ source "https://rubygems.org"
2
2
 
3
- gem "activesupport" , ">= 3.1.0"
4
- gem "rails" , ">= 3.1.0"
3
+ rails_version = ENV["RAILS_VERSION"] || "default"
5
4
 
5
+ rails = case rails_version
6
+ when "master"
7
+ {github: "rails/rails"}
8
+ when "default"
9
+ ">= 3.1.0"
10
+ else
11
+ "~> #{rails_version}"
12
+ end
13
+
14
+ devise = case rails_version
15
+ when "master"
16
+ {github: "plataformatec/devise"}
17
+ when /pre/
18
+ {github: "plataformatec/devise", branch: "rails4"}
19
+ when "3.1.0", "3.2.0", "default"
20
+ "~> 2.2"
21
+ end
22
+
23
+ gem "rails", rails
6
24
 
7
25
  gem 'kramdown' # pure ruby markdown parser
8
26
 
9
27
  group :development, :test do
10
- gem 'mocha'
28
+ gem 'mocha', :require => false
11
29
  gem 'timecop'
12
30
  gem 'jeweler', "~> 1.6.4"
13
- gem "bundler", ">= 1.1.3"
14
31
 
15
32
  gem "capybara", ">= 0.4.0"
16
33
 
17
34
  gem "launchy"
18
35
 
19
36
  gem "sqlite3", :platform => [:ruby, :mswin, :mingw]
20
- gem "activerecord-jdbcsqlite3-adapter", :platform => :jruby
37
+
38
+ gem "activerecord-jdbcsqlite3-adapter", '>= 1.3.0.beta', :platform => :jruby
21
39
  gem "jdbc-sqlite3", :platform => :jruby
22
40
 
23
- gem 'devise'
41
+ gem "devise", devise if devise
24
42
  end
25
43
 
26
44
  group :test do
data/README.md CHANGED
@@ -10,6 +10,7 @@ A production ready Rails Engine that turns your app into an [OAuth2](http://oaut
10
10
  oPRO is short for (O)Auth (Pro)vider and is pronounced "oh proh". Not to be confused with [Oprah](http://www.oprah.com/index.html), who does not support or endorse this ruby gem in any way (yet).
11
11
 
12
12
  [![Build Status](https://secure.travis-ci.org/opro/opro.png)](http://travis-ci.org/opro/opro)
13
+ [![Code Climate](https://codeclimate.com/github/opro/opro.png)](https://codeclimate.com/github/opro/opro)
13
14
 
14
15
  ## Why would I use this?
15
16
 
@@ -23,19 +24,19 @@ Wouldn't it be great if we could have a token exchange where the user goes to a
23
24
  Gemfile
24
25
 
25
26
  ```ruby
26
- gem 'opro'
27
+ gem 'opro'
27
28
  ```
28
29
 
29
30
  Then run
30
31
 
31
32
  ```shell
32
- $ bundle install
33
+ $ bundle install
33
34
  ```
34
35
 
35
36
  and don't forget
36
37
 
37
38
  ```shell
38
- $ rails g opro:install
39
+ $ rails g opro:install
39
40
  ```
40
41
 
41
42
  This will put a file in `initializers/opro.rb`, generate some migrations, and add `mount_opro_oauth` to your routes.
@@ -44,8 +45,8 @@ This will put a file in `initializers/opro.rb`, generate some migrations, and ad
44
45
  Now we're ready to migrate the database
45
46
 
46
47
  ```shell
47
- $ rake db:migrate
48
- ````
48
+ $ rake db:migrate
49
+ ```
49
50
 
50
51
  This will add `Opro::Oauth::AuthGrant` and `Opro::Oauth::ClientApp` to your database. An iPhone app would need to register for a `client_id` and `client_secret` before using OAuth as a ClientApp. Once created they could get authorization from users by going through the OAuth flow, thus creating AuthGrants. In other words, a ClientApp has many users through AuthGrants.
51
52
 
@@ -54,18 +55,18 @@ This will add `Opro::Oauth::AuthGrant` and `Opro::Oauth::ClientApp` to your data
54
55
  Go to `initializers/opro.rb` and configure your app for your authentication scheme. If you're not using devise, see "Custom Auth" below.
55
56
 
56
57
  ```ruby
57
- Opro.setup do |config|
58
- config.auth_strategy = :devise
59
- end
58
+ Opro.setup do |config|
59
+ config.auth_strategy = :devise
60
+ end
60
61
  ```
61
62
 
62
63
 
63
64
  Now in your controllers you can allow OAuth access using the same syntax of the rails `before_filter`
64
65
 
65
66
  ```ruby
66
- class UsersController < ApplicationController
67
- allow_oauth! :only => [:show]
68
- end
67
+ class UsersController < ApplicationController
68
+ allow_oauth! :only => [:show]
69
+ end
69
70
  ```
70
71
 
71
72
 
@@ -73,9 +74,9 @@ You can also disallow OAuth on specific actions. Disallowing will always over-ri
73
74
 
74
75
 
75
76
  ```ruby
76
- class ProductsController < ApplicationController
77
- disallow_oauth! :only => [:create]
78
- end
77
+ class ProductsController < ApplicationController
78
+ disallow_oauth! :only => [:create]
79
+ end
79
80
  ```
80
81
 
81
82
  By default, all OAuth access is blacklisted. To whitelist all access, add `allow_oauth!` to your `ApplicationController` (this is not recommended). The best practice is to add `allow_oauth!` or `disallow_oauth` to each and every controller.
@@ -97,12 +98,13 @@ oPRO is simple by default, but easily configurable for a number of common use ca
97
98
 
98
99
  If you're not using devise, you can manually configure your own auth strategy. In the future I plan on adding more auth strategies; ping me or submit a pull request for your desired authentication scheme.
99
100
 
100
-
101
- Opro.setup do |config|
102
- config.login_method { |controller, current_user| controller.sign_in(current_user, :bypass => true) }
103
- config.logout_method { |controller, current_user| controller.sign_out(current_user) }
104
- config.authenticate_user_method { |controller| controller.authenticate_user! }
105
- end
101
+ ```ruby
102
+ Opro.setup do |config|
103
+ config.login_method { |controller, current_user| controller.sign_in(current_user, :bypass => true) }
104
+ config.logout_method { |controller, current_user| controller.sign_out(current_user) }
105
+ config.authenticate_user_method { |controller| controller.authenticate_user! }
106
+ end
107
+ ```
106
108
 
107
109
  ## Permissions
108
110
 
@@ -113,21 +115,29 @@ When a user authenticates with a client, they are automatically granting read pe
113
115
 
114
116
  To remove write permissions, comment out this line in the oPRO initializer:
115
117
 
116
- config.request_permissions = [:write]
118
+ ```ruby
119
+ config.request_permissions = [:write]
120
+ ```
117
121
 
118
122
  You can add custom permissions by adding to the array:
119
123
 
120
- config.request_permissions = [:write, :email, :picture, :whatever]
124
+ ```ruby
125
+ config.request_permissions = [:write, :email, :picture, :whatever]
126
+ ```
121
127
 
122
128
  You can then restrict access using the custom permissions by calling `require_oauth_permissions`, which takes the same arguments as `before_filter`:
123
129
 
124
- require_oauth_permissions :email, :only => :index
130
+ ```ruby
131
+ require_oauth_permissions :email, :only => :index
132
+ ```
125
133
 
126
134
  You can also skip permissions using `skip_oauth_permissions`. By default, permissions will just check to see if a client has the permission and will allow the action if it is present. If you want to implement custom permission checks, you can write custom methods using the pattern `oauth_client_can_#{permission}?`. For example, if you were restricting the `:email` permission, you would create a method:
127
135
 
128
- def oauth_client_can_email?
129
- # ...
130
- end
136
+ ```ruby
137
+ def oauth_client_can_email?
138
+ # ...
139
+ end
140
+ ```
131
141
 
132
142
  The result is expected to be true or false.
133
143
 
@@ -138,16 +148,20 @@ For added security, you can require access_tokens to be refreshed by client appl
138
148
 
139
149
  By default, refresh tokens are enabled. You can disable them in your application and set the timeout period of the tokens by adding this line to your configuration:
140
150
 
141
- config.require_refresh_within = false
151
+ ```ruby
152
+ config.require_refresh_within = false
153
+ ```
142
154
 
143
155
  # Toggling Refresh Tokens
144
156
 
145
157
  If you disable refresh tokens and then re-enable it you may have authorization grants that do not have a timeout listed, you can keep it like this or you can fix by iterating through all auth grants and setting their `access_token_expires_at` like this:
146
158
 
147
- Opro::Oauth::AuthGrant.find_each(:conditions => "access_token_expires_at is null") do |grant|
148
- grant.access_token_expires_at = Time.now + ::Opro.require_refresh_within
149
- grant.save
150
- end
159
+ ```ruby
160
+ Opro::Oauth::AuthGrant.find_each(:conditions => "access_token_expires_at is null") do |grant|
161
+ grant.access_token_expires_at = Time.now + ::Opro.require_refresh_within
162
+ grant.save
163
+ end
164
+ ```
151
165
 
152
166
  You may also need to inform clients that they need to update their credentials and start using refresh tokens.
153
167
 
@@ -155,29 +169,37 @@ You may also need to inform clients that they need to update their credentials a
155
169
 
156
170
  If a client application has a user's password and username/email, they can exchange these for a token. This is much safer than storing the username and password on a local device, but it does not offer the traditional OAuth "Flow". Because of this, all available permissions will be granted to the client application. If you want to disable this feature you can set the configuration below to false:
157
171
 
158
- config.password_exchange_enabled = true
172
+ ```ruby
173
+ config.password_exchange_enabled = true
174
+ ```
159
175
 
160
176
  If you have this feature enabled, you can further control what applications can use the feature. Some providers may wish to have "Blessed" client applications that have this ability while restricting all other clients. To accomplish this, you can create a method in your ApplicationController called `oauth_valid_password_auth?` that accepts a client_id and client_secret and returns true or false based on whether that application can use password auth:
161
177
 
162
- def oauth_valid_password_auth?(client_id, client_secret)
163
- BLESSED_APP_IDS.include?(client_id)
164
- end
178
+ ```ruby
179
+ def oauth_valid_password_auth?(client_id, client_secret)
180
+ BLESSED_APP_IDS.include?(client_id)
181
+ end
182
+ ```
165
183
 
166
184
 
167
185
  If you are using this password functionality without a supported authorization engine (like devise), you will need to add an additional method that supports validating whether or not a user's credentials are valid. The method for this is called `find_user_for_auth` and accepts a controller and the parameters. The output is expected to be a user. Add this to your config like you did to the other required methods in the "Custom Auth" section:
168
186
 
169
- config.find_user_for_auth do |controller, params|
170
- # user = User.find(params[:something])
171
- # return user.valid_password?(params[:password]) ? user : false
172
- end
187
+ ```ruby
188
+ config.find_user_for_auth do |controller, params|
189
+ # user = User.find(params[:something])
190
+ # return user.valid_password?(params[:password]) ? user : false
191
+ end
192
+ ```
173
193
 
174
194
  If you're authenticating by exchanging something other than a password (such as a facebook auth token), clients can still enable this functionality by setting `params[:grant_type] == 'password'` in their initial request. You can then use the `find_user_for_auth` method from above and implement your custom behavior. You can call `find_user_for_auth` multiple times and the application will try calling each auth method in order. It is suggested that you return from this block early if the params are missing a vital key like this:
175
195
 
176
196
 
177
- config.find_user_for_auth do |controller, params|
178
- return false if params[:fb_token].blank?
179
- User.where(:fb_token => params[:fb_token]).first
180
- end
197
+ ```ruby
198
+ config.find_user_for_auth do |controller, params|
199
+ return false if params[:fb_token].blank?
200
+ User.where(:fb_token => params[:fb_token]).first
201
+ end
202
+ ```
181
203
 
182
204
 
183
205
  ## Rate Limiting
@@ -186,15 +208,19 @@ If your API becomes a runaway success and people start abusing your API, you mig
186
208
 
187
209
  To record the number of times an application has accessed your site add this method to your ApplicationController:
188
210
 
189
- def oauth_client_record_access!(client_id, params)
190
- # implement your rate counting mechanism here
191
- end
211
+ ```ruby
212
+ def oauth_client_record_access!(client_id, params)
213
+ # implement your rate counting mechanism here
214
+ end
215
+ ```
192
216
 
193
217
  Then to let our server know if a given client has reached its limit, add the method below. The output is expected to be true if the client has gone over their limit and false if they have not:
194
218
 
195
- def oauth_client_rate_limited?(client_id, params)
196
- # implement your own custom rate limiting logic here
197
- end
219
+ ```ruby
220
+ def oauth_client_rate_limited?(client_id, params)
221
+ # implement your own custom rate limiting logic here
222
+ end
223
+ ```
198
224
 
199
225
  Rate limited clients will receive an "unsuccessful" response to any query with a message letting them know they've been rate limited. Using redis with a rotating key generator based on (hour, day, etc.) is one very common way to count accesses and implement the rate limits. Since there are so many different ways to implement this, we decided to give you a blank slate and let you implement it however you would like. The default is that apps are not rate limited, and in general unlimited API access is the way to go, but if you do find abusive behavior you can always easily add in a rate limit.
200
226
 
@@ -203,20 +229,26 @@ Rate limited clients will receive an "unsuccessful" response to any query with a
203
229
 
204
230
  By default, oPRO allows clients to send their authorization token in a header. For example, someone using an auth token of `9693accessTokena7ca570bbaf` could set the `Authorization` header in a request like this:
205
231
 
232
+ ```sh
206
233
  $ curl -H "Authorization: Bearer 9693accessTokena7ca570bbaf" "http://localhost:3000/oauth_test/show_me_the_money"
234
+ ```
207
235
 
208
236
  By default, oPRO will accept `Bearer` and `token` in the authorization header, but if your client needs to send a custom auth header, you can add a custom extra regular expression to parse and return the token. For example, if a client was setting the auth header like this:
209
237
 
238
+ ```sh
210
239
  $ curl -H "Authorization: cUsTomAuthHeader 9693accessTokena7ca570bbaf" "http://localhost:3000/oauth_test/show_me_the_money"
240
+ ```
211
241
 
212
242
  You could pull out the auth token using the regular expression `/cUsTomAuthHeader\s(.*)/`. If you're not great with regular expressions, I highly recommend using [Rubular](http://rubular.com) to test regex matches. It is very important that we are "capturing" data in between the `()` characters. The data returned inside of the parens is expected to be the auth token with no spaces or special characters such as new lines or quotes. To parse this auth header in oPRO, you can specify the `header_auth_regex` in an initializer like this:
213
243
 
214
244
 
215
- Opro.setup do |config|
216
- config.auth_strategy = :devise
245
+ ```ruby
246
+ Opro.setup do |config|
247
+ config.auth_strategy = :devise
217
248
 
218
- config.header_auth_regex = /cUsTomAuthHeader\s(.*)/
219
- end
249
+ config.header_auth_regex = /cUsTomAuthHeader\s(.*)/
250
+ end
251
+ ```
220
252
 
221
253
  Now when a client sends your custom auth header, it will be parsed correctly. Custom authorization headers should not be used for security through obscurity. They may be exposed in the docs or tests in a later iteration of oPRO. If you have strong feelings against this, then please open a pull request or send me a message stating your case.
222
254
 
@@ -230,13 +262,17 @@ Currently this is a manual process to give you control and understanding of what
230
262
  To start out overriding a controller we need to specify the new controller in your routes inside of `mount_opro_oauth`, for example if you wanted to over-ride the oauth_client_apps controller with a controller in `app/controllers/oauth/client_apps_controller.rb` you could specify it like this:
231
263
 
232
264
 
233
- mount_opro_oauth :controllers => {:oauth_client_apps => 'oauth/client_apps'}
265
+ ```ruby
266
+ mount_opro_oauth :controllers => {:oauth_client_apps => 'oauth/client_apps'}
267
+ ```
234
268
 
235
269
 
236
270
  You can see an example of [setting the routes](https://github.com/opro/opro_rails_demo/blob/master/config/routes.rb) in the [oPRO demo rails app](https://github.com/opro/opro_rails_demo). Of course you then need to create the controller, it needs to inherit from the original controller `Opro::Oauth::ClientAppController` like this:
237
271
 
238
- class Oauth::ClientAppsController < Opro::Oauth::ClientAppController
239
- end
272
+ ```ruby
273
+ class Oauth::ClientAppsController < Opro::Oauth::ClientAppController
274
+ end
275
+ ```
240
276
 
241
277
 
242
278
  You can see an example of a: [custom oPRO controller](https://github.com/opro/opro_rails_demo/blob/master/app/controllers/oauth/client_apps_controller.rb). Once you've got your controller finished, you need to specify your [custom oPRO views](https://github.com/opro/opro_rails_demo/tree/master/app/views/oauth/client_apps).
@@ -249,7 +285,9 @@ It may help to look at the [current oPRO controllers](https://github.com/opro/op
249
285
 
250
286
  If you do not wish for test, docs, or client_apps views & controllers to be available, you can skip them using `except` in your `mount_opro_oauth` like this:
251
287
 
252
- mount_opro_oauth :except => [:oauth_client_apps]
288
+ ```ruby
289
+ mount_opro_oauth :except => [:oauth_client_apps]
290
+ ```
253
291
 
254
292
  We recommend against doing this, but we aren't your mother.
255
293
 
@@ -264,4 +302,4 @@ We recommend against doing this, but we aren't your mother.
264
302
 
265
303
  If you have a question file an issue or find me on the Twitters [@schneems](http://twitter.com/schneems). Another good library for turning your app into an OAuth provider is [Doorkeeper](https://github.com/applicake/doorkeeper), if this project doesn't meet your needs let me know why and use them :)
266
304
 
267
- This project rocks and uses MIT-LICENSE.
305
+ This project rocks and uses MIT-LICENSE.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.3
1
+ 0.5.0
@@ -2,26 +2,20 @@ class Opro::Oauth::AuthController < OproController
2
2
  before_filter :opro_authenticate_user!
3
3
  before_filter :ask_user!, :only => [:create]
4
4
 
5
-
6
5
  def new
7
6
  @redirect_uri = params[:redirect_uri]
8
7
  @client_app = Opro::Oauth::ClientApp.find_by_app_id(params[:client_id])
9
8
  @scopes = scope_from_params(params)
10
9
  end
11
10
 
12
-
13
-
14
11
  # :ask_user! is called before creating a new authorization, this allows us to redirect
15
12
  def create
16
13
  # find or create an auth_grant for a given user
17
- application = Opro::Oauth::ClientApp.find_by_app_id(params[:client_id])
18
- auth_grant = Opro::Oauth::AuthGrant.where( :user_id => current_user.id, :application_id => application.id).first
19
- auth_grant ||= Opro::Oauth::AuthGrant.create(:user => current_user, :application => application)
20
-
14
+ application = Opro::Oauth::ClientApp.find_by_client_id(params[:client_id])
15
+ auth_grant = Opro::Oauth::AuthGrant.find_or_create_by_user_app(current_user, application)
21
16
 
22
17
  # add permission changes if there are any
23
- auth_grant.update_attributes(:permissions => params[:permissions]) if auth_grant.permissions != params[:permissions]
24
-
18
+ auth_grant.update_permissions(params[:permissions])
25
19
  redirect_to auth_grant.redirect_uri_for(params[:redirect_uri], params[:state])
26
20
  end
27
21
 
@@ -42,6 +36,7 @@ class Opro::Oauth::AuthController < OproController
42
36
  # if the request did not come from a form within the application, render the user form
43
37
  @redirect_uri ||= params[:redirect_uri]
44
38
  @client_app ||= Opro::Oauth::ClientApp.find_by_app_id(params[:client_id])
39
+ params.delete("action").delete("controller")
45
40
  redirect_to oauth_new_path(params)
46
41
  end
47
42
  end
@@ -52,22 +47,6 @@ class Opro::Oauth::AuthController < OproController
52
47
  Opro::Oauth::AuthGrant.where(:application_id => @client_app.id, :user_id => user.id).present?
53
48
  end
54
49
 
55
-
56
- # take params[:scope] = [:write, :read, :etc] or
57
- # take params[:scope] = "write, read, etc"
58
- # compare against available scopes ::Opro.request_permissions
59
- # return the intersecting set. or the default scope if n
60
- def scope_from_params(params)
61
- default_scope = ::Opro.request_permissions.map(&:to_s).map(&:downcase)
62
- return default_scope if params[:scope].blank?
63
-
64
- scope = params[:scope].is_a?(Array) ? params[:scope] : params[:scope].split(',')
65
- raise "Params #{params[:scope]} improperly formatted " unless scope.is_a?(Array)
66
- requested_scope = scope.map(&:downcase).map(&:strip)
67
- return requested_scope & default_scope
68
- end
69
-
70
-
71
50
  # Verifying that a post was made from our own site, indicating a user confirmed via form
72
51
  def user_authorizes_the_request?(request)
73
52
  request.post? && referrer_is_self?(request)
@@ -81,4 +60,19 @@ class Opro::Oauth::AuthController < OproController
81
60
  referrer_host == self_host
82
61
  end
83
62
 
63
+
64
+ # take params[:scope] = [:write, :read, :etc] or
65
+ # take params[:scope] = "write, read, etc"
66
+ # compare against available scopes ::Opro.request_permissions
67
+ # return the intersecting set. or the default scope
68
+ def scope_from_params(params)
69
+ return default_scope if params[:scope].blank?
70
+ scope = params[:scope].is_a?(Array) ? params[:scope] : params[:scope].split(',')
71
+ scope = scope.map(&:downcase).map(&:strip)
72
+ return requested_scope & default_scope
73
+ end
74
+
75
+ def default_scope
76
+ ::Opro.request_permissions.map(&:to_s).map(&:downcase)
77
+ end
84
78
  end