songkick-oauth2-provider 0.10.2 → 0.10.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/History.txt +7 -0
  3. data/README.rdoc +18 -11
  4. data/example/README.rdoc +1 -1
  5. data/example/application.rb +9 -9
  6. data/example/schema.rb +1 -1
  7. data/example/views/authorize.erb +2 -2
  8. data/example/views/layout.erb +4 -4
  9. data/example/views/login.erb +2 -2
  10. data/example/views/new_client.erb +1 -1
  11. data/example/views/new_user.erb +1 -1
  12. data/lib/songkick/oauth2/model.rb +8 -6
  13. data/lib/songkick/oauth2/model/authorization.rb +31 -31
  14. data/lib/songkick/oauth2/model/client.rb +15 -15
  15. data/lib/songkick/oauth2/model/client_owner.rb +2 -2
  16. data/lib/songkick/oauth2/model/hashing.rb +3 -3
  17. data/lib/songkick/oauth2/model/helpers.rb +16 -0
  18. data/lib/songkick/oauth2/model/resource_owner.rb +4 -4
  19. data/lib/songkick/oauth2/provider.rb +16 -16
  20. data/lib/songkick/oauth2/provider/access_token.rb +20 -15
  21. data/lib/songkick/oauth2/provider/authorization.rb +43 -42
  22. data/lib/songkick/oauth2/provider/error.rb +4 -4
  23. data/lib/songkick/oauth2/provider/exchange.rb +46 -46
  24. data/lib/songkick/oauth2/router.rb +13 -13
  25. data/lib/songkick/oauth2/schema.rb +11 -3
  26. data/lib/songkick/oauth2/schema/20120828112156_songkick_oauth2_schema_original_schema.rb +2 -2
  27. data/lib/songkick/oauth2/schema/20121024180930_songkick_oauth2_schema_add_authorization_index.rb +3 -3
  28. data/lib/songkick/oauth2/schema/20121025180447_songkick_oauth2_schema_add_unique_indexes.rb +7 -7
  29. data/spec/request_helpers.rb +25 -21
  30. data/spec/songkick/oauth2/model/authorization_spec.rb +56 -56
  31. data/spec/songkick/oauth2/model/client_spec.rb +9 -9
  32. data/spec/songkick/oauth2/model/helpers_spec.rb +26 -0
  33. data/spec/songkick/oauth2/model/resource_owner_spec.rb +13 -13
  34. data/spec/songkick/oauth2/provider/access_token_spec.rb +32 -20
  35. data/spec/songkick/oauth2/provider/authorization_spec.rb +73 -62
  36. data/spec/songkick/oauth2/provider/exchange_spec.rb +72 -72
  37. data/spec/songkick/oauth2/provider_spec.rb +101 -101
  38. data/spec/spec_helper.rb +5 -3
  39. data/spec/test_app/helper.rb +11 -7
  40. data/spec/test_app/provider/application.rb +12 -12
  41. data/spec/test_app/provider/views/authorize.erb +2 -2
  42. metadata +71 -93
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9b0ad668ae50fbc3a9b425ec63dd98663b7cb67b
4
+ data.tar.gz: 1243ea91a6236236493d7c98fd55ef0b4be46008
5
+ SHA512:
6
+ metadata.gz: 11235193ecb2927f87b4adf3c2f70007c65f6af0367773eb705d2fb5f7e257ca31b217bce9cd2ea6693b2fee17d0c6ea60e98c0b69885853e415a5d95bc13c54
7
+ data.tar.gz: dacea0ec9c18b48a30f5a9e0f7e5706471fbf63242598efc924e9fd902fcb75c031c993f61db92513cf549edcba88f558dbe8aadef1844869e76ea5fa1987f7c
@@ -1,3 +1,10 @@
1
+ === 0.10.3 / 2017-10-24
2
+
3
+ * Add support for query strings in the client redirect uri
4
+ * Do not allow the user arg for Provider.access_token() to be nil
5
+ (Use `:implicit` for implicit user lookup)
6
+
7
+
1
8
  === 0.10.2 / 2012-10-31
2
9
 
3
10
  * Stop Model::Authorization#scope being set to an empty string and returned as
@@ -17,6 +17,11 @@ framework-specific request objects, though you can pass those in and their
17
17
  It stores the clients and authorizations using ActiveRecord.
18
18
 
19
19
 
20
+ === Installation
21
+
22
+ gem install songkick-oauth2-provider
23
+
24
+
20
25
  === A note on versioning
21
26
 
22
27
  This library is based on draft-10[http://tools.ietf.org/html/draft-ietf-oauth-v2-10],
@@ -108,6 +113,8 @@ the gem's migrations that have not yet been applied to your database.
108
113
  -> 0.0009s
109
114
  ...
110
115
 
116
+ To rollback migrations, use <tt>Songkick::OAuth2::Model::Schema.rollback</tt>.
117
+
111
118
 
112
119
  === Model Mixins
113
120
 
@@ -196,14 +203,14 @@ the client:
196
203
  __send__ method, '/oauth/authorize' do
197
204
  @owner = User.find_by_id(session[:user_id])
198
205
  @oauth2 = Songkick::OAuth2::Provider.parse(@owner, env)
199
-
206
+
200
207
  if @oauth2.redirect?
201
208
  redirect @oauth2.redirect_uri, @oauth2.response_status
202
209
  end
203
-
210
+
204
211
  headers @oauth2.response_headers
205
212
  status @oauth2.response_status
206
-
213
+
207
214
  if body = @oauth2.response_body
208
215
  body
209
216
  elsif @oauth2.valid?
@@ -228,7 +235,7 @@ You may also want to use scopes to provide granular access to your domain using
228
235
  asked for so you can display them to the user:
229
236
 
230
237
  <p>The application <%= @oauth2.client.name %> wants the following permissions:</p>
231
-
238
+
232
239
  <ul>
233
240
  <% @oauth2.scopes.each do |scope| %>
234
241
  <li><%= PERMISSION_UI_STRINGS[scope] %></li>
@@ -251,7 +258,7 @@ user checks a box before posting a form to indicate their intent:
251
258
  post '/oauth/allow' do
252
259
  @user = User.find_by_id(session[:user_id])
253
260
  @auth = Songkick::OAuth2::Provider::Authorization.new(@user, params)
254
-
261
+
255
262
  if params['allow'] == '1'
256
263
  @auth.grant_access!
257
264
  else
@@ -311,10 +318,10 @@ requested scopes.
311
318
  Songkick::OAuth2::Provider.handle_assertions 'https://graph.facebook.com/me' do |client, assertion, scopes|
312
319
  facebook = URI.parse('https://graph.facebook.com/me?access_token=' + assertion)
313
320
  response = Net::HTTP.get_response(facebook)
314
-
321
+
315
322
  user_data = JSON.parse(response.body)
316
323
  account = User.from_facebook_data(user_data)
317
-
324
+
318
325
  account.grant_access!(client, :scopes => scopes)
319
326
  end
320
327
 
@@ -337,10 +344,10 @@ simple, for example a call to get a user's notes:
337
344
  get '/user/:username/notes' do
338
345
  user = User.find_by_username(params[:username])
339
346
  token = Songkick::OAuth2::Provider.access_token(user, ['read_notes'], env)
340
-
347
+
341
348
  headers token.response_headers
342
349
  status token.response_status
343
-
350
+
344
351
  if token.valid?
345
352
  JSON.unparse('notes' => user.notes)
346
353
  else
@@ -357,10 +364,10 @@ determine whether to serve the request or not.
357
364
 
358
365
  It is also common to provide a dynamic resource for getting some basic data
359
366
  about a user by supplying their access token. This can be done by passing
360
- <tt>nil</tt> as the resource owner:
367
+ <tt>:implicit</tt> as the resource owner:
361
368
 
362
369
  get '/me' do
363
- token = Songkick::OAuth2::Provider.access_token(nil, [], env)
370
+ token = Songkick::OAuth2::Provider.access_token(:implicit, [], env)
364
371
  if token.valid?
365
372
  JSON.unparse('username' => token.owner.username)
366
373
  else
@@ -4,7 +4,7 @@ To get up and running:
4
4
 
5
5
  # in parent directory
6
6
  bundle install
7
-
7
+
8
8
  cd example/
9
9
  ruby schema.rb
10
10
  rackup config.ru
@@ -65,14 +65,14 @@ end
65
65
  __send__ method, '/oauth/authorize' do
66
66
  @user = User.find_by_id(session[:user_id])
67
67
  @oauth2 = Songkick::OAuth2::Provider.parse(@user, env)
68
-
68
+
69
69
  if @oauth2.redirect?
70
70
  redirect @oauth2.redirect_uri, @oauth2.response_status
71
71
  end
72
-
72
+
73
73
  headers @oauth2.response_headers
74
74
  status @oauth2.response_status
75
-
75
+
76
76
  if body = @oauth2.response_body
77
77
  body
78
78
  elsif @oauth2.valid?
@@ -105,10 +105,10 @@ end
105
105
  # Domain API
106
106
 
107
107
  get '/me' do
108
- authorization = Songkick::OAuth2::Provider.access_token(nil, [], env)
108
+ authorization = Songkick::OAuth2::Provider.access_token(:implicit, [], env)
109
109
  headers authorization.response_headers
110
110
  status authorization.response_status
111
-
111
+
112
112
  if authorization.valid?
113
113
  user = authorization.owner
114
114
  JSON.unparse('username' => user.username)
@@ -141,15 +141,15 @@ helpers do
141
141
  def verify_access(scope)
142
142
  user = User.find_by_username(params[:username])
143
143
  token = Songkick::OAuth2::Provider.access_token(user, [scope.to_s], env)
144
-
144
+
145
145
  headers token.response_headers
146
146
  status token.response_status
147
-
147
+
148
148
  return ERROR_RESPONSE unless token.valid?
149
-
149
+
150
150
  yield user
151
151
  end
152
-
152
+
153
153
  #================================================================
154
154
  # Return the full app domain
155
155
  def host
@@ -10,7 +10,7 @@ ActiveRecord::Schema.define do |version|
10
10
  t.timestamps
11
11
  t.string :username
12
12
  end
13
-
13
+
14
14
  create_table :notes, :force => true do |t|
15
15
  t.timestamps
16
16
  t.belongs_to :user
@@ -15,12 +15,12 @@
15
15
  <input type="hidden" name="<%= key %>" value="<%= value %>">
16
16
  <% end %>
17
17
  <input type="hidden" name="user_id" value="<%= @user.id %>">
18
-
18
+
19
19
  <fieldset>
20
20
  <input type="checkbox" name="allow" id="allow" value="1">
21
21
  <label for="allow">Allow this application</label>
22
22
  </fieldset>
23
-
23
+
24
24
  <fieldset>
25
25
  <input type="submit" value="Go!">
26
26
  </fieldset>
@@ -6,20 +6,20 @@
6
6
  <link rel="stylesheet" href="/style.css">
7
7
  </head>
8
8
  <body>
9
-
9
+
10
10
  <div class="header"><div class="sub">
11
11
  <h1>OAuth 2.0 demo</h1>
12
12
  <h2>Steal my notes, why don&rsquo;t you</h2>
13
13
  </div></div>
14
-
14
+
15
15
  <div class="content"><div class="sub">
16
16
  <%= yield %>
17
17
  </div></div>
18
-
18
+
19
19
  <div class="footer"><div class="sub">
20
20
  <p>Copyright &copy; 2010 Songkick.com</p>
21
21
  </div></div>
22
-
22
+
23
23
  </body>
24
24
  </html>
25
25
 
@@ -7,12 +7,12 @@
7
7
  <% @oauth2.params.each do |key, value| %>
8
8
  <input type="hidden" name="<%= key %>" value="<%= value %>">
9
9
  <% end %>
10
-
10
+
11
11
  <fieldset>
12
12
  <label for="username">Username</label>
13
13
  <input type="text" name="username" id="username">
14
14
  </fieldset>
15
-
15
+
16
16
  <fieldset>
17
17
  <input type="submit" value="Sign in">
18
18
  </fieldset>
@@ -17,7 +17,7 @@
17
17
  <label for="redirect_uri">Callback URI</label>
18
18
  <input type="text" name="redirect_uri" id="redirect_uri">
19
19
  </fieldset>
20
-
20
+
21
21
  <fieldset>
22
22
  <input type="submit" value="Register">
23
23
  </fieldset>
@@ -13,7 +13,7 @@
13
13
  <label for="name">Username</label>
14
14
  <input type="text" name="username" id="username">
15
15
  </fieldset>
16
-
16
+
17
17
  <fieldset>
18
18
  <input type="submit" value="Register">
19
19
  </fieldset>
@@ -3,34 +3,36 @@ require 'active_record'
3
3
  module Songkick
4
4
  module OAuth2
5
5
  module Model
6
+ autoload :Helpers, ROOT + '/oauth2/model/helpers'
6
7
  autoload :ClientOwner, ROOT + '/oauth2/model/client_owner'
7
8
  autoload :ResourceOwner, ROOT + '/oauth2/model/resource_owner'
8
9
  autoload :Hashing, ROOT + '/oauth2/model/hashing'
9
10
  autoload :Authorization, ROOT + '/oauth2/model/authorization'
10
11
  autoload :Client, ROOT + '/oauth2/model/client'
11
-
12
+
12
13
  Schema = Songkick::OAuth2::Schema
13
-
14
+
14
15
  DUPLICATE_RECORD_ERRORS = [
15
16
  /^Mysql::Error:\s+Duplicate\s+entry\b/,
16
17
  /^PG::Error:\s+ERROR:\s+duplicate\s+key\b/,
17
18
  /\bConstraintException\b/
18
19
  ]
19
-
20
+
20
21
  # ActiveRecord::RecordNotUnique was introduced in Rails 3.0 so referring
21
22
  # to it while running earlier versions will raise an error. The above
22
23
  # error strings should match PostgreSQL, MySQL and SQLite errors on
23
24
  # Rails 2. If you're running a different adapter, add a suitable regex to
24
25
  # the list:
25
- #
26
+ #
26
27
  # Songkick::OAuth2::Model::DUPLICATE_RECORD_ERRORS << /DB2 found a dup/
27
- #
28
+ #
28
29
  def self.duplicate_record_error?(error)
29
30
  error.class.name == 'ActiveRecord::RecordNotUnique' or
30
31
  DUPLICATE_RECORD_ERRORS.any? { |re| re =~ error.message }
31
32
  end
32
-
33
+
33
34
  def self.find_access_token(access_token)
35
+ return nil if access_token.nil?
34
36
  Authorization.find_by_access_token_hash(Songkick::OAuth2.hashify(access_token))
35
37
  end
36
38
  end
@@ -1,64 +1,64 @@
1
1
  module Songkick
2
2
  module OAuth2
3
3
  module Model
4
-
4
+
5
5
  class Authorization < ActiveRecord::Base
6
6
  self.table_name = :oauth2_authorizations
7
-
7
+
8
8
  belongs_to :oauth2_resource_owner, :polymorphic => true
9
9
  alias :owner :oauth2_resource_owner
10
10
  alias :owner= :oauth2_resource_owner=
11
-
11
+
12
12
  belongs_to :client, :class_name => 'Songkick::OAuth2::Model::Client'
13
-
13
+
14
14
  validates_presence_of :client, :owner
15
-
15
+
16
16
  validates_uniqueness_of :code, :scope => :client_id, :allow_nil => true
17
17
  validates_uniqueness_of :refresh_token_hash, :scope => :client_id, :allow_nil => true
18
18
  validates_uniqueness_of :access_token_hash, :allow_nil => true
19
-
19
+
20
20
  attr_accessible nil
21
-
21
+
22
22
  class << self
23
23
  private :create, :new
24
24
  end
25
-
25
+
26
26
  extend Hashing
27
27
  hashes_attributes :access_token, :refresh_token
28
-
28
+
29
29
  def self.create_code(client)
30
30
  Songkick::OAuth2.generate_id do |code|
31
- client.authorizations.count(:conditions => {:code => code}).zero?
31
+ Helpers.count(client.authorizations, :code => code).zero?
32
32
  end
33
33
  end
34
-
34
+
35
35
  def self.create_access_token
36
36
  Songkick::OAuth2.generate_id do |token|
37
37
  hash = Songkick::OAuth2.hashify(token)
38
- count(:conditions => {:access_token_hash => hash}).zero?
38
+ Helpers.count(self, :access_token_hash => hash).zero?
39
39
  end
40
40
  end
41
-
41
+
42
42
  def self.create_refresh_token(client)
43
43
  Songkick::OAuth2.generate_id do |refresh_token|
44
44
  hash = Songkick::OAuth2.hashify(refresh_token)
45
- client.authorizations.count(:conditions => {:refresh_token_hash => hash}).zero?
45
+ Helpers.count(client.authorizations, :refresh_token_hash => hash).zero?
46
46
  end
47
47
  end
48
-
48
+
49
49
  def self.for(owner, client, attributes = {})
50
50
  return nil unless owner and client
51
-
51
+
52
52
  unless client.is_a?(Client)
53
53
  raise ArgumentError, "The argument should be a #{Client}, instead it was a #{client.class}"
54
54
  end
55
-
55
+
56
56
  instance = owner.oauth2_authorization_for(client) ||
57
57
  new do |authorization|
58
58
  authorization.owner = owner
59
59
  authorization.client = client
60
60
  end
61
-
61
+
62
62
  case attributes[:response_type]
63
63
  when CODE
64
64
  instance.code ||= create_code(client)
@@ -70,19 +70,19 @@ module Songkick
70
70
  instance.access_token ||= create_access_token
71
71
  instance.refresh_token ||= create_refresh_token(client)
72
72
  end
73
-
73
+
74
74
  if attributes[:duration]
75
75
  instance.expires_at = Time.now + attributes[:duration].to_i
76
76
  else
77
77
  instance.expires_at = nil
78
78
  end
79
-
79
+
80
80
  scopes = instance.scopes + (attributes[:scopes] || [])
81
81
  scopes += attributes[:scope].split(/\s+/) if attributes[:scope]
82
82
  instance.scope = scopes.empty? ? nil : scopes.entries.join(' ')
83
-
83
+
84
84
  instance.save && instance
85
-
85
+
86
86
  rescue Object => error
87
87
  if Model.duplicate_record_error?(error)
88
88
  retry
@@ -90,47 +90,47 @@ module Songkick
90
90
  raise error
91
91
  end
92
92
  end
93
-
93
+
94
94
  def exchange!
95
95
  self.code = nil
96
96
  self.access_token = self.class.create_access_token
97
97
  self.refresh_token = nil
98
98
  save!
99
99
  end
100
-
100
+
101
101
  def expired?
102
102
  return false unless expires_at
103
103
  expires_at < Time.now
104
104
  end
105
-
105
+
106
106
  def expires_in
107
107
  expires_at && (expires_at - Time.now).ceil
108
108
  end
109
-
109
+
110
110
  def generate_code
111
111
  self.code ||= self.class.create_code(client)
112
112
  save && code
113
113
  end
114
-
114
+
115
115
  def generate_access_token
116
116
  self.access_token ||= self.class.create_access_token
117
117
  save && access_token
118
118
  end
119
-
119
+
120
120
  def grants_access?(user, *scopes)
121
121
  not expired? and user == owner and in_scope?(scopes)
122
122
  end
123
-
123
+
124
124
  def in_scope?(request_scope)
125
125
  [*request_scope].all?(&scopes.method(:include?))
126
126
  end
127
-
127
+
128
128
  def scopes
129
129
  scopes = scope ? scope.split(/\s+/) : []
130
130
  Set.new(scopes)
131
131
  end
132
132
  end
133
-
133
+
134
134
  end
135
135
  end
136
136
  end