rack-oauth2-server 1.2.2 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/CHANGELOG +14 -0
  2. data/Gemfile +3 -0
  3. data/README.rdoc +26 -7
  4. data/Rakefile +1 -1
  5. data/VERSION +1 -0
  6. data/lib/rack/oauth2/admin/css/screen.css +233 -0
  7. data/lib/rack/oauth2/admin/images/loading.gif +0 -0
  8. data/lib/rack/oauth2/admin/js/application.js +154 -0
  9. data/lib/rack/oauth2/admin/js/jquery.js +166 -0
  10. data/lib/rack/oauth2/admin/js/jquery.tmpl.js +414 -0
  11. data/lib/rack/oauth2/admin/js/sammy.js +5 -0
  12. data/lib/rack/oauth2/admin/js/sammy.json.js +5 -0
  13. data/lib/rack/oauth2/admin/js/sammy.storage.js +5 -0
  14. data/lib/rack/oauth2/admin/js/sammy.title.js +5 -0
  15. data/lib/rack/oauth2/admin/js/sammy.tmpl.js +5 -0
  16. data/lib/rack/oauth2/admin/js/underscore.js +722 -0
  17. data/lib/rack/oauth2/admin/views/client.tmpl +48 -0
  18. data/lib/rack/oauth2/admin/views/clients.tmpl +36 -0
  19. data/lib/rack/oauth2/admin/views/edit.tmpl +57 -0
  20. data/lib/rack/oauth2/admin/views/index.html +26 -0
  21. data/lib/rack/oauth2/models/access_grant.rb +6 -4
  22. data/lib/rack/oauth2/models/access_token.rb +36 -4
  23. data/lib/rack/oauth2/models/auth_request.rb +4 -3
  24. data/lib/rack/oauth2/models/client.rb +15 -2
  25. data/lib/rack/oauth2/server.rb +71 -58
  26. data/lib/rack/oauth2/server/admin.rb +216 -0
  27. data/lib/rack/oauth2/server/helper.rb +4 -4
  28. data/lib/rack/oauth2/sinatra.rb +2 -2
  29. data/rack-oauth2-server.gemspec +2 -3
  30. data/test/admin/api_test.rb +196 -0
  31. data/test/admin_test_.rb +49 -0
  32. data/test/{access_grant_test.rb → oauth/access_grant_test.rb} +1 -1
  33. data/test/{access_token_test.rb → oauth/access_token_test.rb} +83 -12
  34. data/test/{authorization_test.rb → oauth/authorization_test.rb} +1 -1
  35. data/test/rails/config/environment.rb +2 -0
  36. data/test/rails/log/test.log +72938 -0
  37. data/test/setup.rb +17 -1
  38. data/test/sinatra/my_app.rb +1 -1
  39. metadata +27 -9
  40. data/lib/rack/oauth2/server/version.rb +0 -9
@@ -0,0 +1,48 @@
1
+ <div class="client">
2
+ <div class="details">
3
+ <a href="${link}" class="name">{{if imageUrl}}<img src="${imageUrl}">{{/if}} ${displayName}</a>
4
+ <a href="#/client/${id}/edit" rel="edit">Edit</a>
5
+ {{if !revoked}}
6
+ <a href="${revoke}" data-method="post" data-confirm="There is no undo. Are you really really sure?" rel="revoke">Revoke</a>
7
+ {{/if}}
8
+ <div class="meta">
9
+ Created {{html $.shortdate(revoked)}}
10
+ {{if revoked}}Revoked {{html $.shortdate(revoked)}}{{/if}}
11
+ </div>
12
+ </div>
13
+ <ul class="badges">
14
+ <li title="Access tokens granted, lifetime total"><big>${$.thousands(tokens.total)}</big><small>Granted</small></li>
15
+ <li title="Access tokens granted, last 7 days"><big>${$.thousands(tokens.week)}</big><small>This Week</small></li>
16
+ <li title="Access tokens revoked, last 7 days"><big>${$.thousands(tokens.revoked)}</big><small>Revoked (Week)</small></li>
17
+ </ul>
18
+ <table class="tokens">
19
+ <thead>
20
+ <th>Token</th>
21
+ <th>Identity</th>
22
+ <th>Scope</th>
23
+ <th>Created</th>
24
+ <th>Revoked</th>
25
+ </thead>
26
+ <tbody>
27
+ {{each tokens.list}}
28
+ <tr>
29
+ <td class="token">${token}</td>
30
+ <td class="identity">${identity}</td>
31
+ <td class="scope">${scope}</td>
32
+ <td class="created">{{html $.shortdate(created)}}</td>
33
+ <td class="revoke">
34
+ {{if revoked}}
35
+ {{html $.shortdate(revoked)}}
36
+ {{else}}
37
+ <a href="${revoke}" data-method="post" data-confirm="Are you sure?" rel="revoke">Revoke</a>
38
+ {{/if}}
39
+ </td>
40
+ </tr>
41
+ {{/each}}
42
+ </tbody>
43
+ </table>
44
+ <div class="pagination">
45
+ {{if tokens.previous}}<a href="#/client/${id}/${tokens.page - 1}" rel="previous">Previous</a>{{/if}}
46
+ {{if tokens.next}}<a href="#/client/${id}/${tokens.page + 1}" rel="next">Next</a>{{/if}}
47
+ </div>
48
+ </div>
@@ -0,0 +1,36 @@
1
+ <div class="client">
2
+ <a href="#/new" style="float:left">Add New Client</a>
3
+ <ul class="badges">
4
+ <li title="Access tokens granted, lifetime total"><big>${$.thousands(tokens.total)}</big><small>Granted</small></li>
5
+ <li title="Access tokens granted, last 7 days"><big>${$.thousands(tokens.week)}</big><small>This Week</small></li>
6
+ <li title="Access tokens revoked, last 7 days"><big>${$.thousands(tokens.revoked)}</big><small>Revoked (Week)</small></li>
7
+ </ul>
8
+ <table class="clients">
9
+ <thead>
10
+ <th>Application</th>
11
+ <th>ID/Secret</th>
12
+ <th>Created</th>
13
+ <th>Revoked</th>
14
+ </thead>
15
+ {{each clients}}
16
+ <tr class="${revoked ? "revoked" : "active"}">
17
+ <td class="name">
18
+ <a href="#/client/${id}">
19
+ {{if imageUrl}}<img src="${imageUrl}">{{/if}}
20
+ ${displayName}
21
+ </a>
22
+ </td>
23
+ <td class="secrets">
24
+ <a href="" rel="toggle">Reveal</a>
25
+ <dl>
26
+ <dt>ID</dt><dd>${id}</dd>
27
+ <dt>Secret</dt><dd>${secret}</dd>
28
+ <dt>Redirect</dt><dd>${redirectUri}</dd>
29
+ </dl>
30
+ </td>
31
+ <td class="created">{{html $.shortdate(created)}}</td>
32
+ <td class="revoke">{{if revoked}}{{html $.shortdate(revoked)}}{{/if}}</td>
33
+ </tr>
34
+ {{/each}}
35
+ </table>
36
+ </div>
@@ -0,0 +1,57 @@
1
+ {{if id}}<form action="#/client/${id}" method="put" class="client edit">
2
+ {{else}}<form action="#/clients" method="post" class="client new">{{/if}}
3
+ <img id="image">
4
+ <label>Display Name
5
+ <input type="text" name="displayName" value="${displayName}" size="30" autofocus>
6
+ <p class="hint">This is the application name that users see when asked to authorize.</p>
7
+ </label>
8
+ <label>Site URL
9
+ <input type="text" name="link" value="${link}" size="30">
10
+ <p class="hint">This is a link to the application's site.</p>
11
+ </label>
12
+ <label>Image URL
13
+ <input type="text" name="imageUrl" value="${imageUrl}" size="30">
14
+ <p class="hint">This is a link to the application's icon (48x48).</p>
15
+ </label>
16
+ <label>Redirect URI
17
+ <input type="text" name="redirectUri" value="${redirectUri}" size="30">
18
+ <p class="hint">Users redirected back to this URL on successful authorization.</p>
19
+ </label>
20
+ {{if id}}<button>Save Changes</button>
21
+ {{else}}<button>Create Client</button>{{/if}}
22
+ </form>
23
+ <script type="text/javascript">
24
+ $(function() {
25
+ var image = $("#image");
26
+ image.load(function() {
27
+ image.show().removeClass("loading");
28
+ }).error(function() {
29
+ if (image.attr("src"))
30
+ image.removeClass("loading");
31
+ });
32
+
33
+ var imageUrl = $("input[name=imageUrl]");
34
+ imageUrl.change(function() {
35
+ var url = $(this).val().trim();
36
+ if (url == "") {
37
+ image.hide();
38
+ } else {
39
+ image.attr("src", "admin/images/loading.gif").show().addClass("loading");
40
+ setTimeout(function() { image.attr("src", url); }, 10);
41
+ }
42
+ }).trigger("change");
43
+
44
+ $("input[name=link]").change(function() {
45
+ if (imageUrl.val().trim() == "") {
46
+ $("#image").show().addClass("loading").attr("src", null);
47
+ var image = new Image();
48
+ image.src = $(this).val().trim().replace(/^(https?:\/\/)(.+?)(\/.*|$)/, "$1$2/favicon.ico");
49
+ image.onload = function() {
50
+ if (imageUrl.val().trim() == "") {
51
+ imageUrl.val(image.src).trigger("change");
52
+ }
53
+ }
54
+ }
55
+ });
56
+ })
57
+ </script>
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>OAuth Console</title>
5
+ <link href="admin/css/screen.css" media="screen, projection" rel="stylesheet" type="text/css">
6
+ <script src="admin/js/jquery.js" type="text/javascript"></script>
7
+ <script src="admin/js/jquery.tmpl.js" type="text/javascript"></script>
8
+ <script src="admin/js/sammy.js" type="text/javascript"></script>
9
+ <script src="admin/js/sammy.tmpl.js" type="text/javascript"></script>
10
+ <script src="admin/js/sammy.json.js" type="text/javascript"></script>
11
+ <script src="admin/js/sammy.storage.js" type="text/javascript"></script>
12
+ <script src="admin/js/sammy.title.js" type="text/javascript"></script>
13
+ <script src="admin/js/underscore.js" type="text/javascript"></script>
14
+ <script src="admin/js/application.js" type="text/javascript"></script>
15
+ </head>
16
+ <body>
17
+ <div id="notice" style="display:none"></div>
18
+ <div id="header"><a href="#/" class="title">OAuth Console</a></div>
19
+ <div id="main"></div>
20
+ <script>
21
+ var loading = new Image();
22
+ loading.src = "admin/images/loading.gif";
23
+ $(function() { Sammy("#main").run("#/"); });
24
+ </script>
25
+ </body>
26
+ </html>
@@ -13,8 +13,9 @@ module Rack
13
13
 
14
14
  # Create a new access grant.
15
15
  def create(identity, scope, client_id, redirect_uri)
16
- fields = { :_id=>Server.secure_random, :identity=>identity.to_s, :scope=>scope, :client_id=>BSON::ObjectId(client_id.to_s),
17
- :redirect_uri=>redirect_uri, :created_at=>Time.now.utc, :granted_at=>nil, :access_token=>nil, :revoked=>nil }
16
+ fields = { :_id=>Server.secure_random, :identity=>identity.to_s, :scope=>scope,
17
+ :client_id=>BSON::ObjectId(client_id.to_s), :redirect_uri=>redirect_uri,
18
+ :created_at=>Time.now.utc.to_i, :granted_at=>nil, :access_token=>nil, :revoked=>nil }
18
19
  collection.insert fields
19
20
  Server.new_instance self, fields
20
21
  end
@@ -54,7 +55,7 @@ module Rack
54
55
  raise InvalidGrantError if self.access_token || self.revoked
55
56
  access_token = AccessToken.get_token_for(identity, scope, client_id)
56
57
  self.access_token = access_token.token
57
- self.granted_at = Time.now.utc
58
+ self.granted_at = Time.now.utc.to_i
58
59
  self.class.collection.update({ :_id=>code, :access_token=>nil, :revoked=>nil }, { :$set=>{ :granted_at=>granted_at, :access_token=>access_token.token } }, :safe=>true)
59
60
  reload = self.class.collection.find_one({ :_id=>code, :revoked=>nil }, { :fields=>%w{access_token} })
60
61
  raise InvalidGrantError unless reload && reload["access_token"] == access_token.token
@@ -62,7 +63,8 @@ module Rack
62
63
  end
63
64
 
64
65
  def revoke!
65
- self.class.collection.update({ :_id=>code, :revoked=>nil }, { :$set=>{ :revoked=>Time.now.utc } })
66
+ self.revoked = Time.now.utc.to_i
67
+ self.class.collection.update({ :_id=>code, :revoked=>nil }, { :$set=>{ :revoked=>revoked } })
66
68
  end
67
69
 
68
70
  Server.create_indexes do
@@ -8,6 +8,7 @@ module Rack
8
8
  # and scope. It may be revoked, or expire after a certain period.
9
9
  class AccessToken
10
10
  class << self
11
+
11
12
  # Find AccessToken from token. Does not return revoked tokens.
12
13
  def from_token(token)
13
14
  Server.new_instance self, collection.find_one({ :_id=>token, :revoked=>nil })
@@ -16,9 +17,11 @@ module Rack
16
17
  # Get an access token (create new one if necessary).
17
18
  def get_token_for(identity, scope, client_id)
18
19
  scope = scope.split.sort.join(" ") # Make sure always in same order.
19
- unless token = collection.find_one({ :identity=>identity.to_s, :scope=>scope, :client_id=>BSON::ObjectId(client_id.to_s) })
20
- token = { :_id=>Server.secure_random, :identity=>identity.to_s, :scope=>scope, :client_id=>BSON::ObjectId(client_id.to_s),
21
- :created_at=>Time.now.utc, :expires_at=>nil, :revoked=>nil }
20
+ client_id = BSON::ObjectId(client_id.to_s)
21
+ unless token = collection.find_one({ :identity=>identity.to_s, :scope=>scope, :client_id=>client_id })
22
+ token = { :_id=>Server.secure_random, :identity=>identity.to_s, :scope=>scope,
23
+ :client_id=>client_id, :created_at=>Time.now.utc.to_i,
24
+ :expires_at=>nil, :revoked=>nil }
22
25
  collection.insert token
23
26
  end
24
27
  Server.new_instance self, token
@@ -29,6 +32,35 @@ module Rack
29
32
  collection.find({ :identity=>identity }).map { |fields| Server.new_instance self, fields }
30
33
  end
31
34
 
35
+ # Returns all access tokens for a given client, Use limit and offset
36
+ # to return a subset of tokens, sorted by creation date.
37
+ def for_client(client_id, offset = 0, limit = 100)
38
+ client_id = BSON::ObjectId(client_id.to_s)
39
+ collection.find({ :client_id=>client_id }, { :sort=>[[:created_at, Mongo::ASCENDING]], :skip=>offset, :limit=>limit }).
40
+ map { |token| Server.new_instance self, token }
41
+ end
42
+
43
+ # Returns count of access tokens.
44
+ #
45
+ # @param [Hash] filter Count only a subset of access tokens
46
+ # @option filter [Integer] days Only count that many days (since now)
47
+ # @option filter [Boolean] revoked Only count revoked (true) or non-revoked (false) tokens; count all tokens if nil
48
+ # @option filter [String, ObjectId] client_id Only tokens grant to this client
49
+ def count(filter = {})
50
+ select = {}
51
+ if filter[:days]
52
+ now = Time.now.utc.to_i
53
+ select[:created_at] = { :$gt=>now - filter[:days] * 86400, :$lte=>now }
54
+ end
55
+ if filter.has_key?(:revoked)
56
+ select[:revoked] = filter[:revoked] ? { :$ne=>nil } : { :$eq=>nil }
57
+ end
58
+ if filter[:client_id]
59
+ select[:client_id] = BSON::ObjectId(filter[:client_id].to_s)
60
+ end
61
+ collection.find(select).count
62
+ end
63
+
32
64
  def collection
33
65
  Server.database["oauth2.access_tokens"]
34
66
  end
@@ -52,7 +84,7 @@ module Rack
52
84
 
53
85
  # Revokes this access token.
54
86
  def revoke!
55
- self.revoked = Time.now.utc
87
+ self.revoked = Time.now.utc.to_i
56
88
  AccessToken.collection.update({ :_id=>token }, { :$set=>{ :revoked=>revoked } })
57
89
  end
58
90
 
@@ -18,7 +18,8 @@ module Rack
18
18
  # and any state value to pass back in that redirect.
19
19
  def create(client_id, scope, redirect_uri, response_type, state)
20
20
  fields = { :client_id=>BSON::ObjectId(client_id.to_s), :scope=>scope, :redirect_uri=>redirect_uri, :state=>state,
21
- :response_type=>response_type, :created_at=>Time.now.utc, :grant_code=>nil, :authorized_at=>nil, :revoked=>nil }
21
+ :response_type=>response_type, :created_at=>Time.now.utc.to_i, :grant_code=>nil,
22
+ :authorized_at=>nil, :revoked=>nil }
22
23
  fields[:_id] = collection.insert(fields)
23
24
  Server.new_instance self, fields
24
25
  end
@@ -56,7 +57,7 @@ module Rack
56
57
  def grant!(identity)
57
58
  raise ArgumentError, "Must supply a identity" unless identity
58
59
  return if revoked
59
- self.authorized_at = Time.now.utc
60
+ self.authorized_at = Time.now.utc.to_i
60
61
  if response_type == "code" # Requested authorization code
61
62
  access_grant = AccessGrant.create(identity, scope, client_id, redirect_uri)
62
63
  self.grant_code = access_grant.code
@@ -71,7 +72,7 @@ module Rack
71
72
 
72
73
  # Deny access.
73
74
  def deny!
74
- self.authorized_at = Time.now.utc
75
+ self.authorized_at = Time.now.utc.to_i
75
76
  self.class.collection.update({ :_id=>id }, { :$set=>{ :authorized_at=>authorized_at } })
76
77
  end
77
78
 
@@ -25,7 +25,8 @@ module Rack
25
25
  def create(args)
26
26
  redirect_uri = Server::Utils.parse_redirect_uri(args[:redirect_uri]).to_s if args[:redirect_uri]
27
27
  fields = { :secret=>Server.secure_random, :display_name=>args[:display_name], :link=>args[:link],
28
- :image_url=>args[:image_url], :redirect_uri=>redirect_uri, :created_at=>Time.now.utc, :revoked=>nil }
28
+ :image_url=>args[:image_url], :redirect_uri=>redirect_uri, :created_at=>Time.now.utc.to_i,
29
+ :revoked=>nil }
29
30
  fields[:_id] = collection.insert(fields)
30
31
  Server.new_instance self, fields
31
32
  end
@@ -38,6 +39,12 @@ module Rack
38
39
  Server.new_instance self, collection.find_one({ :display_name=>field }) || collection.find_one({ :link=>field })
39
40
  end
40
41
 
42
+ # Returns all the clients in the database, sorted alphabetically.
43
+ def all
44
+ collection.find({}, { :sort=>[[:display_name, Mongo::ASCENDING]] }).
45
+ map { |fields| Server.new_instance self, fields }
46
+ end
47
+
41
48
  def collection
42
49
  Server.database["oauth2.clients"]
43
50
  end
@@ -65,13 +72,19 @@ module Rack
65
72
  # Revoke all authorization requests, access grants and access tokens for
66
73
  # this client. Ward off the evil.
67
74
  def revoke!
68
- self.revoked = Time.now.utc
75
+ self.revoked = Time.now.utc.to_i
69
76
  Client.collection.update({ :_id=>id }, { :$set=>{ :revoked=>revoked } })
70
77
  AuthRequest.collection.update({ :client_id=>id }, { :$set=>{ :revoked=>revoked } })
71
78
  AccessGrant.collection.update({ :client_id=>id }, { :$set=>{ :revoked=>revoked } })
72
79
  AccessToken.collection.update({ :client_id=>id }, { :$set=>{ :revoked=>revoked } })
73
80
  end
74
81
 
82
+ def update(args)
83
+ fields = [:display_name, :link, :image_url].inject({}) { |h,k| v = args[k]; h[k] = v if v; h }
84
+ fields[:redirect_uri] = Server::Utils.parse_redirect_uri(args[:redirect_uri]).to_s if args[:redirect_uri]
85
+ self.class.collection.update({ :_id=>id }, { :$set=>fields })
86
+ end
87
+
75
88
  Server.create_indexes do
76
89
  # For quickly returning clients sorted by display name, or finding
77
90
  # client from a URL.
@@ -2,7 +2,6 @@ require "rack/oauth2/models"
2
2
  require "rack/oauth2/server/errors"
3
3
  require "rack/oauth2/server/utils"
4
4
  require "rack/oauth2/server/helper"
5
- require "rack/oauth2/server/version"
6
5
 
7
6
 
8
7
  module Rack
@@ -11,6 +10,9 @@ module Rack
11
10
  # Implements an OAuth 2 Authorization Server, based on http://tools.ietf.org/html/draft-ietf-oauth-v2-10
12
11
  class Server
13
12
 
13
+ # Same as gem version number.
14
+ VERSION = IO.read(::File.expand_path("../../../VERSION", ::File.dirname(__FILE__)))
15
+
14
16
  class << self
15
17
  # Return AuthRequest from authorization request handle.
16
18
  def get_auth_request(authorization)
@@ -46,13 +48,17 @@ module Rack
46
48
  # - :authorize_path -- Path for requesting end-user authorization. By
47
49
  # convention defaults to /oauth/authorize.
48
50
  # - :database -- Mongo::DB instance.
51
+ # - :host -- Only check requests sent to this host.
52
+ # - :path -- Only check requests for resources under this path.
53
+ # - :param_authentication -- If true, supports authentication using
54
+ # query/form parameters.
49
55
  # - :realm -- Authorization realm that will show up in 401 responses.
50
56
  # Defaults to use the request host name.
51
57
  # - :scopes -- Array listing all supported scopes, e.g. %w{read write}.
52
58
  # - :logger -- The logger to use. Under Rails, defaults to use the Rails
53
59
  # logger. Will use Rack::Logger if available.
54
60
  Options = Struct.new(:access_token_path, :authenticator, :authorization_types,
55
- :authorize_path, :database, :realm, :scopes, :logger)
61
+ :authorize_path, :database, :host, :param_authentication, :path, :realm, :scopes, :logger)
56
62
 
57
63
  def initialize(app, options = Options.new, &authenticator)
58
64
  @app = app
@@ -61,76 +67,83 @@ module Rack
61
67
  @options.access_token_path ||= "/oauth/access_token"
62
68
  @options.authorize_path ||= "/oauth/authorize"
63
69
  @options.authorization_types ||= %w{code token}
70
+ @options.param_authentication = false
64
71
  end
65
72
 
66
73
  # @see Options
67
74
  attr_reader :options
68
75
 
69
76
  def call(env)
70
- # Use options.database if specified.
71
- org_database, Server.database = Server.database, options.database || Server.database
72
- logger = options.logger || env["rack.logger"]
73
77
  request = OAuthRequest.new(env)
78
+ return @app.call(env) if options.host && options.host != request.host
79
+ return @app.call(env) if options.path && request.path.index(options.path) != 0
74
80
 
75
- # 3. Obtaining End-User Authorization
76
- # Flow starts here.
77
- return request_authorization(request, logger) if request.path == options.authorize_path
78
- # 4. Obtaining an Access Token
79
- return respond_with_access_token(request, logger) if request.path == options.access_token_path
80
-
81
- # 5. Accessing a Protected Resource
82
- if request.authorization
83
- # 5.1.1. The Authorization Request Header Field
84
- token = request.credentials if request.oauth?
85
- else
86
- # 5.1.2. URI Query Parameter
87
- # 5.1.3. Form-Encoded Body Parameter
88
- token = request.GET["oauth_token"] || request.POST["oauth_token"]
89
- end
81
+ begin
82
+ # Use options.database if specified.
83
+ org_database, Server.database = Server.database, options.database || Server.database
84
+ logger = options.logger || env["rack.logger"]
90
85
 
91
- if token
92
- begin
93
- access_token = AccessToken.from_token(token)
94
- raise InvalidTokenError if access_token.nil? || access_token.revoked
95
- raise ExpiredTokenError if access_token.expires_at && access_token.expires_at <= Time.now.utc
96
- request.env["oauth.access_token"] = token
97
- request.env["oauth.identity"] = access_token.identity
98
- logger.info "Authorized #{access_token.identity}" if logger
99
- rescue Error=>error
100
- # 5.2. The WWW-Authenticate Response Header Field
101
- logger.info "HTTP authorization failed #{error.code}" if logger
102
- return unauthorized(request, error)
103
- rescue =>ex
104
- logger.info "HTTP authorization failed #{ex.message}" if logger
105
- return unauthorized(request)
86
+ # 3. Obtaining End-User Authorization
87
+ # Flow starts here.
88
+ return request_authorization(request, logger) if request.path == options.authorize_path
89
+ # 4. Obtaining an Access Token
90
+ return respond_with_access_token(request, logger) if request.path == options.access_token_path
91
+
92
+ # 5. Accessing a Protected Resource
93
+ if request.authorization
94
+ # 5.1.1. The Authorization Request Header Field
95
+ token = request.credentials if request.oauth?
96
+ elsif options.param_authentication && !request.GET["oauth_verifier"] # Ignore OAuth 1.0 callbacks
97
+ # 5.1.2. URI Query Parameter
98
+ # 5.1.3. Form-Encoded Body Parameter
99
+ token = request.GET["oauth_token"] || request.POST["oauth_token"]
106
100
  end
107
101
 
108
- # We expect application to use 403 if request has insufficient scope,
109
- # and return appropriate WWW-Authenticate header.
110
- response = @app.call(env)
111
- if response[0] == 403
112
- scope = response[1]["oauth.no_scope"] || ""
113
- scope = scope.join(" ") if scope.respond_to?(:join)
114
- challenge = 'OAuth realm="%s", error="insufficient_scope", scope="%s"' % [(options.realm || request.host), scope]
115
- return [403, { "WWW-Authenticate"=>challenge }, []]
116
- else
117
- return response
118
- end
119
- else
120
- response = @app.call(env)
121
- if response[1] && response[1]["oauth.no_access"]
122
- # OAuth access required.
123
- return unauthorized(request)
124
- elsif response[1] && response[1]["oauth.authorization"]
125
- # 3. Obtaining End-User Authorization
126
- # Flow ends here.
127
- return authorization_response(response, logger)
102
+ if token
103
+ begin
104
+ access_token = AccessToken.from_token(token)
105
+ raise InvalidTokenError if access_token.nil? || access_token.revoked
106
+ raise ExpiredTokenError if access_token.expires_at && access_token.expires_at <= Time.now.utc
107
+ request.env["oauth.access_token"] = token
108
+ request.env["oauth.identity"] = access_token.identity
109
+ logger.info "Authorized #{access_token.identity}" if logger
110
+ rescue Error=>error
111
+ # 5.2. The WWW-Authenticate Response Header Field
112
+ logger.info "HTTP authorization failed #{error.code}" if logger
113
+ return unauthorized(request, error)
114
+ rescue =>ex
115
+ logger.info "HTTP authorization failed #{ex.message}" if logger
116
+ return unauthorized(request)
117
+ end
118
+
119
+ # We expect application to use 403 if request has insufficient scope,
120
+ # and return appropriate WWW-Authenticate header.
121
+ response = @app.call(env)
122
+ if response[0] == 403
123
+ scope = response[1]["oauth.no_scope"] || ""
124
+ scope = scope.join(" ") if scope.respond_to?(:join)
125
+ challenge = 'OAuth realm="%s", error="insufficient_scope", scope="%s"' % [(options.realm || request.host), scope]
126
+ response[1]["WWW-Authenticate"] = challenge
127
+ return response
128
+ else
129
+ return response
130
+ end
128
131
  else
129
- return response
132
+ response = @app.call(env)
133
+ if response[1] && response[1].delete("oauth.no_access")
134
+ # OAuth access required.
135
+ return unauthorized(request)
136
+ elsif response[1] && response[1]["oauth.authorization"]
137
+ # 3. Obtaining End-User Authorization
138
+ # Flow ends here.
139
+ return authorization_response(response, logger)
140
+ else
141
+ return response
142
+ end
130
143
  end
144
+ ensure
145
+ Server.database = org_database
131
146
  end
132
- ensure
133
- Server.database = org_database
134
147
  end
135
148
 
136
149
  protected