rockoauth 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/History.txt +5 -0
- data/README.rdoc +422 -0
- data/example/README.rdoc +11 -0
- data/example/application.rb +158 -0
- data/example/config.ru +3 -0
- data/example/environment.rb +11 -0
- data/example/models/connection.rb +9 -0
- data/example/models/note.rb +4 -0
- data/example/models/user.rb +5 -0
- data/example/public/style.css +78 -0
- data/example/schema.rb +22 -0
- data/example/views/authorize.erb +28 -0
- data/example/views/create_user.erb +3 -0
- data/example/views/error.erb +6 -0
- data/example/views/home.erb +24 -0
- data/example/views/layout.erb +24 -0
- data/example/views/login.erb +20 -0
- data/example/views/new_client.erb +25 -0
- data/example/views/new_user.erb +22 -0
- data/example/views/show_client.erb +15 -0
- data/lib/rockoauth/model/authorization.rb +132 -0
- data/lib/rockoauth/model/client.rb +54 -0
- data/lib/rockoauth/model/client_owner.rb +13 -0
- data/lib/rockoauth/model/hashing.rb +26 -0
- data/lib/rockoauth/model/helpers.rb +14 -0
- data/lib/rockoauth/model/resource_owner.rb +22 -0
- data/lib/rockoauth/model.rb +38 -0
- data/lib/rockoauth/provider/access_token.rb +70 -0
- data/lib/rockoauth/provider/authorization.rb +185 -0
- data/lib/rockoauth/provider/error.rb +19 -0
- data/lib/rockoauth/provider/exchange.rb +225 -0
- data/lib/rockoauth/provider.rb +133 -0
- data/lib/rockoauth/router.rb +75 -0
- data/lib/rockoauth/schema/20120828112156_rockoauth_schema_original_schema.rb +35 -0
- data/lib/rockoauth/schema/20121024180930_rockoauth_schema_add_authorization_index.rb +13 -0
- data/lib/rockoauth/schema/20121025180447_rockoauth_schema_add_unique_indexes.rb +31 -0
- data/lib/rockoauth/schema.rb +25 -0
- data/lib/rockoauth.rb +1 -0
- data/spec/factories.rb +20 -0
- data/spec/request_helpers.rb +62 -0
- data/spec/rockoauth/model/authorization_spec.rb +237 -0
- data/spec/rockoauth/model/client_spec.rb +44 -0
- data/spec/rockoauth/model/helpers_spec.rb +25 -0
- data/spec/rockoauth/model/resource_owner_spec.rb +87 -0
- data/spec/rockoauth/provider/access_token_spec.rb +138 -0
- data/spec/rockoauth/provider/authorization_spec.rb +356 -0
- data/spec/rockoauth/provider/exchange_spec.rb +361 -0
- data/spec/rockoauth/provider_spec.rb +560 -0
- data/spec/spec_helper.rb +80 -0
- data/spec/test_app/helper.rb +36 -0
- data/spec/test_app/provider/application.rb +67 -0
- data/spec/test_app/provider/views/authorize.erb +19 -0
- metadata +238 -0
@@ -0,0 +1,78 @@
|
|
1
|
+
body {
|
2
|
+
font: 16px/1.4 FreeSans, Helvetica, Arial, sans-serif;
|
3
|
+
background: #353e4b;
|
4
|
+
}
|
5
|
+
|
6
|
+
.sub {
|
7
|
+
width: 640px;
|
8
|
+
margin: 0 auto;
|
9
|
+
padding: 1em 2em;
|
10
|
+
}
|
11
|
+
|
12
|
+
.header {
|
13
|
+
text-shadow: #23282e 0px -2px 0px;
|
14
|
+
}
|
15
|
+
|
16
|
+
.header h1 {
|
17
|
+
color: #e5dec7;
|
18
|
+
font-size: 4em;
|
19
|
+
letter-spacing: -0.06em;
|
20
|
+
margin: 0;
|
21
|
+
}
|
22
|
+
|
23
|
+
.header h2 {
|
24
|
+
color: #b3a784;
|
25
|
+
font-size: 1.5em;
|
26
|
+
font-weight: normal;
|
27
|
+
letter-spacing: -0.06em;
|
28
|
+
margin: 0 0 0.5em;
|
29
|
+
}
|
30
|
+
|
31
|
+
.content .sub {
|
32
|
+
background: #fff;
|
33
|
+
color: #333;
|
34
|
+
-webkit-border-radius: 16px;
|
35
|
+
-moz-border-radius: 16px;
|
36
|
+
border-radius: 16px;
|
37
|
+
}
|
38
|
+
|
39
|
+
h3 {
|
40
|
+
color: #888;
|
41
|
+
font-size: 1.5em;
|
42
|
+
font-weight: normal;
|
43
|
+
margin: 0 0 1em;
|
44
|
+
}
|
45
|
+
|
46
|
+
fieldset {
|
47
|
+
border: none;
|
48
|
+
border-top: 1px solid #ccc;
|
49
|
+
padding: 12px 0 0 0;
|
50
|
+
margin: 12px 0 0 0;
|
51
|
+
}
|
52
|
+
|
53
|
+
table {
|
54
|
+
border-collapse: collapse;
|
55
|
+
}
|
56
|
+
|
57
|
+
table th, table td {
|
58
|
+
border-top: 1px solid #eee;
|
59
|
+
padding: 8px 16px;
|
60
|
+
}
|
61
|
+
|
62
|
+
table th {
|
63
|
+
border-right: 2px solid #ccc;
|
64
|
+
text-align: left;
|
65
|
+
}
|
66
|
+
|
67
|
+
a {
|
68
|
+
color: #9ba749;
|
69
|
+
font-weight: bold;
|
70
|
+
text-decoration: none;
|
71
|
+
}
|
72
|
+
|
73
|
+
.footer {
|
74
|
+
font-size: 0.8em;
|
75
|
+
color: #999;
|
76
|
+
text-shadow: #23282e 0px -1px 0px;
|
77
|
+
}
|
78
|
+
|
data/example/schema.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
|
4
|
+
require 'rockoauth/provider'
|
5
|
+
require 'active_record'
|
6
|
+
require File.expand_path('../models/connection', __FILE__)
|
7
|
+
|
8
|
+
ActiveRecord::Schema.define do |version|
|
9
|
+
create_table :users, :force => true do |t|
|
10
|
+
t.timestamps
|
11
|
+
t.string :username
|
12
|
+
end
|
13
|
+
|
14
|
+
create_table :notes, :force => true do |t|
|
15
|
+
t.timestamps
|
16
|
+
t.belongs_to :user
|
17
|
+
t.string :title
|
18
|
+
t.text :body
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
RockOAuth::Model::Schema.up
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<h3>Authorize OAuth client</h3>
|
2
|
+
|
3
|
+
<p>This application <b><%= @oauth2.client.name %></b> wants the following
|
4
|
+
permissions:</p>
|
5
|
+
|
6
|
+
<ul>
|
7
|
+
<% @oauth2.scopes.each do |scope| %>
|
8
|
+
<% next unless PERMISSIONS[scope] %>
|
9
|
+
<li><%= PERMISSIONS[scope] %></li>
|
10
|
+
<% end %>
|
11
|
+
</ul>
|
12
|
+
|
13
|
+
<form method="post" action="/oauth/allow">
|
14
|
+
<% @oauth2.params.each do |key, value| %>
|
15
|
+
<input type="hidden" name="<%= key %>" value="<%= value %>">
|
16
|
+
<% end %>
|
17
|
+
<input type="hidden" name="user_id" value="<%= @user.id %>">
|
18
|
+
|
19
|
+
<fieldset>
|
20
|
+
<input type="checkbox" name="allow" id="allow" value="1">
|
21
|
+
<label for="allow">Allow this application</label>
|
22
|
+
</fieldset>
|
23
|
+
|
24
|
+
<fieldset>
|
25
|
+
<input type="submit" value="Go!">
|
26
|
+
</fieldset>
|
27
|
+
</form>
|
28
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<p>Welcome to the <b>RockOAuth demo</b>. The endpoint you should direct
|
2
|
+
users to is:</p>
|
3
|
+
|
4
|
+
<pre> <%= host %>/oauth/authorize</pre>
|
5
|
+
|
6
|
+
<p>This handles both user authorization and token exchange requests. Before you
|
7
|
+
can use this though, you’ll need to register your application.</p>
|
8
|
+
|
9
|
+
<ul>
|
10
|
+
<li><a href="/oauth/apps/new">Register your application</a></li>
|
11
|
+
</ul>
|
12
|
+
|
13
|
+
<p>This application is a note-taking app. It exposes a JSON API for reading a
|
14
|
+
user’s notes, but you need an access token for this. Use the OAuth
|
15
|
+
protocol to get one, or see if you can hack in without permission!</p>
|
16
|
+
|
17
|
+
<p>The following resources are available. You’ll need to ask the user for
|
18
|
+
the <b><code>read_notes</code></b> permission scope to get at them.</p>
|
19
|
+
|
20
|
+
<ul>
|
21
|
+
<li><code>/me</code> — returns the current user’s data</li>
|
22
|
+
<li><code>/users/:username/notes</code></li>
|
23
|
+
<li><code>/users/:username/notes/:note_id</code></li>
|
24
|
+
</ul>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
5
|
+
<title>OAuth 2.0 demo</title>
|
6
|
+
<link rel="stylesheet" href="/style.css">
|
7
|
+
</head>
|
8
|
+
<body>
|
9
|
+
|
10
|
+
<div class="header"><div class="sub">
|
11
|
+
<h1>OAuth 2.0 demo</h1>
|
12
|
+
<h2>Steal my notes, why don’t you</h2>
|
13
|
+
</div></div>
|
14
|
+
|
15
|
+
<div class="content"><div class="sub">
|
16
|
+
<%= yield %>
|
17
|
+
</div></div>
|
18
|
+
|
19
|
+
<div class="footer"><div class="sub">
|
20
|
+
<p>Copyright © 2010 Songkick.com, 2014 Rocketmade.com</p>
|
21
|
+
</div></div>
|
22
|
+
|
23
|
+
</body>
|
24
|
+
</html>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<h3>Sign in</h3>
|
2
|
+
|
3
|
+
<p>Who are you? We’d ask for a password usually, but seeing as it’s
|
4
|
+
<em>you</em>…</p>
|
5
|
+
|
6
|
+
<form method="post" action="/login">
|
7
|
+
<% @oauth2.params.each do |key, value| %>
|
8
|
+
<input type="hidden" name="<%= key %>" value="<%= value %>">
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<fieldset>
|
12
|
+
<label for="username">Username</label>
|
13
|
+
<input type="text" name="username" id="username">
|
14
|
+
</fieldset>
|
15
|
+
|
16
|
+
<fieldset>
|
17
|
+
<input type="submit" value="Sign in">
|
18
|
+
</fieldset>
|
19
|
+
</form>
|
20
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<h3>Register you application</h3>
|
2
|
+
|
3
|
+
<% if @client.errors.any? %>
|
4
|
+
<ul class="errors">
|
5
|
+
<% @client.errors.full_messages.each do |message| %>
|
6
|
+
<li><%= message %></li>
|
7
|
+
<% end %>
|
8
|
+
</ul>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<form method="post" action="/oauth/apps">
|
12
|
+
<fieldset>
|
13
|
+
<label for="name">Application name</label>
|
14
|
+
<input type="text" name="name" id="name">
|
15
|
+
</fieldset>
|
16
|
+
<fieldset>
|
17
|
+
<label for="redirect_uri">Callback URI</label>
|
18
|
+
<input type="text" name="redirect_uri" id="redirect_uri">
|
19
|
+
</fieldset>
|
20
|
+
|
21
|
+
<fieldset>
|
22
|
+
<input type="submit" value="Register">
|
23
|
+
</fieldset>
|
24
|
+
</form>
|
25
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<h3>Register a User</h3>
|
2
|
+
|
3
|
+
<% if @user.errors.any? %>
|
4
|
+
<ul class="errors">
|
5
|
+
<% @user.errors.full_messages.each do |message| %>
|
6
|
+
<li><%= message %></li>
|
7
|
+
<% end %>
|
8
|
+
</ul>
|
9
|
+
<% end %>
|
10
|
+
|
11
|
+
<form method="post" action="/users/create">
|
12
|
+
<fieldset>
|
13
|
+
<label for="name">Username</label>
|
14
|
+
<input type="text" name="username" id="username">
|
15
|
+
</fieldset>
|
16
|
+
|
17
|
+
<fieldset>
|
18
|
+
<input type="submit" value="Register">
|
19
|
+
</fieldset>
|
20
|
+
</form>
|
21
|
+
|
22
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<h3>Client app: <%= @client.name %></h3>
|
2
|
+
|
3
|
+
<table>
|
4
|
+
<tbody>
|
5
|
+
<tr>
|
6
|
+
<th scope="row">client_id</th>
|
7
|
+
<td><%= @client.client_id %></td>
|
8
|
+
</tr>
|
9
|
+
<tr>
|
10
|
+
<th scope="row">client_secret</th>
|
11
|
+
<td><%= @client_secret %></td>
|
12
|
+
</tr>
|
13
|
+
</tbody>
|
14
|
+
</table>
|
15
|
+
|
@@ -0,0 +1,132 @@
|
|
1
|
+
module RockOAuth
|
2
|
+
module Model
|
3
|
+
|
4
|
+
class Authorization < ActiveRecord::Base
|
5
|
+
self.table_name = :oauth2_authorizations
|
6
|
+
|
7
|
+
belongs_to :oauth2_resource_owner, :polymorphic => true
|
8
|
+
alias :owner :oauth2_resource_owner
|
9
|
+
alias :owner= :oauth2_resource_owner=
|
10
|
+
|
11
|
+
belongs_to :client, :class_name => 'RockOAuth::Model::Client'
|
12
|
+
|
13
|
+
validates_presence_of :client, :owner
|
14
|
+
|
15
|
+
validates_uniqueness_of :code, :scope => :client_id, :allow_nil => true
|
16
|
+
validates_uniqueness_of :refresh_token_hash, :scope => :client_id, :allow_nil => true
|
17
|
+
validates_uniqueness_of :access_token_hash, :allow_nil => true
|
18
|
+
|
19
|
+
class << self
|
20
|
+
private :create, :new
|
21
|
+
end
|
22
|
+
|
23
|
+
extend Hashing
|
24
|
+
hashes_attributes :access_token, :refresh_token
|
25
|
+
|
26
|
+
def self.create_code(client)
|
27
|
+
RockOAuth.generate_id do |code|
|
28
|
+
Helpers.count(client.authorizations, :code => code).zero?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.create_access_token
|
33
|
+
RockOAuth.generate_id do |token|
|
34
|
+
hash = RockOAuth.hashify(token)
|
35
|
+
Helpers.count(self, :access_token_hash => hash).zero?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.create_refresh_token(client)
|
40
|
+
RockOAuth.generate_id do |refresh_token|
|
41
|
+
hash = RockOAuth.hashify(refresh_token)
|
42
|
+
Helpers.count(client.authorizations, :refresh_token_hash => hash).zero?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.for(owner, client, attributes = {})
|
47
|
+
return nil unless owner and client
|
48
|
+
|
49
|
+
unless client.is_a?(Client)
|
50
|
+
raise ArgumentError, "The argument should be a #{Client}, instead it was a #{client.class}"
|
51
|
+
end
|
52
|
+
|
53
|
+
instance = owner.oauth2_authorization_for(client) ||
|
54
|
+
new do |authorization|
|
55
|
+
authorization.owner = owner
|
56
|
+
authorization.client = client
|
57
|
+
end
|
58
|
+
|
59
|
+
case attributes[:response_type]
|
60
|
+
when CODE
|
61
|
+
instance.code ||= create_code(client)
|
62
|
+
when TOKEN
|
63
|
+
instance.access_token ||= create_access_token
|
64
|
+
instance.refresh_token ||= create_refresh_token(client)
|
65
|
+
when CODE_AND_TOKEN
|
66
|
+
instance.code = create_code(client)
|
67
|
+
instance.access_token ||= create_access_token
|
68
|
+
instance.refresh_token ||= create_refresh_token(client)
|
69
|
+
end
|
70
|
+
|
71
|
+
if attributes[:duration]
|
72
|
+
instance.expires_at = Time.now + attributes[:duration].to_i
|
73
|
+
else
|
74
|
+
instance.expires_at = nil
|
75
|
+
end
|
76
|
+
|
77
|
+
scopes = instance.scopes + (attributes[:scopes] || [])
|
78
|
+
scopes += attributes[:scope].split(/\s+/) if attributes[:scope]
|
79
|
+
instance.scope = scopes.empty? ? nil : scopes.entries.join(' ')
|
80
|
+
|
81
|
+
instance.save && instance
|
82
|
+
|
83
|
+
rescue Object => error
|
84
|
+
if Model.duplicate_record_error?(error)
|
85
|
+
retry
|
86
|
+
else
|
87
|
+
raise error
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def exchange!
|
92
|
+
self.code = nil
|
93
|
+
self.access_token = self.class.create_access_token
|
94
|
+
self.refresh_token = nil
|
95
|
+
save!
|
96
|
+
end
|
97
|
+
|
98
|
+
def expired?
|
99
|
+
return false unless expires_at
|
100
|
+
expires_at < Time.now
|
101
|
+
end
|
102
|
+
|
103
|
+
def expires_in
|
104
|
+
expires_at && (expires_at - Time.now).ceil
|
105
|
+
end
|
106
|
+
|
107
|
+
def generate_code
|
108
|
+
self.code ||= self.class.create_code(client)
|
109
|
+
save && code
|
110
|
+
end
|
111
|
+
|
112
|
+
def generate_access_token
|
113
|
+
self.access_token ||= self.class.create_access_token
|
114
|
+
save && access_token
|
115
|
+
end
|
116
|
+
|
117
|
+
def grants_access?(user, *scopes)
|
118
|
+
not expired? and user == owner and in_scope?(scopes)
|
119
|
+
end
|
120
|
+
|
121
|
+
def in_scope?(request_scope)
|
122
|
+
[*request_scope].all?(&scopes.method(:include?))
|
123
|
+
end
|
124
|
+
|
125
|
+
def scopes
|
126
|
+
scopes = scope ? scope.split(/\s+/) : []
|
127
|
+
Set.new(scopes)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module RockOAuth
|
2
|
+
module Model
|
3
|
+
|
4
|
+
class Client < ActiveRecord::Base
|
5
|
+
self.table_name = :oauth2_clients
|
6
|
+
|
7
|
+
belongs_to :oauth2_client_owner, :polymorphic => true
|
8
|
+
alias :owner :oauth2_client_owner
|
9
|
+
alias :owner= :oauth2_client_owner=
|
10
|
+
|
11
|
+
has_many :authorizations, :class_name => 'RockOAuth::Model::Authorization', :dependent => :destroy
|
12
|
+
|
13
|
+
validates_uniqueness_of :client_id, :name
|
14
|
+
validates_presence_of :name, :redirect_uri
|
15
|
+
validate :check_format_of_redirect_uri
|
16
|
+
|
17
|
+
before_create :generate_credentials
|
18
|
+
|
19
|
+
def self.create_client_id
|
20
|
+
RockOAuth.generate_id do |client_id|
|
21
|
+
Helpers.count(self, :client_id => client_id).zero?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_reader :client_secret
|
26
|
+
|
27
|
+
def client_secret=(secret)
|
28
|
+
@client_secret = secret
|
29
|
+
hash = BCrypt::Password.create(secret)
|
30
|
+
hash.force_encoding('UTF-8') if hash.respond_to?(:force_encoding)
|
31
|
+
self.client_secret_hash = hash
|
32
|
+
end
|
33
|
+
|
34
|
+
def valid_client_secret?(secret)
|
35
|
+
BCrypt::Password.new(client_secret_hash) == secret
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def check_format_of_redirect_uri
|
41
|
+
uri = URI.parse(redirect_uri)
|
42
|
+
errors.add(:redirect_uri, 'must be an absolute URI') unless uri.absolute?
|
43
|
+
rescue
|
44
|
+
errors.add(:redirect_uri, 'must be a URI')
|
45
|
+
end
|
46
|
+
|
47
|
+
def generate_credentials
|
48
|
+
self.client_id = self.class.create_client_id
|
49
|
+
self.client_secret = RockOAuth.random_string
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module RockOAuth
|
2
|
+
module Model
|
3
|
+
|
4
|
+
module Hashing
|
5
|
+
def hashes_attributes(*attributes)
|
6
|
+
attributes.each do |attribute|
|
7
|
+
define_method("#{attribute}=") do |value|
|
8
|
+
instance_variable_set("@#{attribute}", value)
|
9
|
+
__send__("#{attribute}_hash=", value && RockOAuth.hashify(value))
|
10
|
+
end
|
11
|
+
attr_reader attribute
|
12
|
+
end
|
13
|
+
|
14
|
+
class_eval <<-RUBY
|
15
|
+
def reload(*args)
|
16
|
+
super
|
17
|
+
#{ attributes.inspect }.each do |attribute|
|
18
|
+
instance_variable_set('@' + attribute.to_s, nil)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
RUBY
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module RockOAuth
|
2
|
+
module Model
|
3
|
+
|
4
|
+
module ResourceOwner
|
5
|
+
def self.included(klass)
|
6
|
+
klass.has_many :oauth2_authorizations,
|
7
|
+
:class_name => Authorization.name,
|
8
|
+
:as => :oauth2_resource_owner,
|
9
|
+
:dependent => :destroy
|
10
|
+
end
|
11
|
+
|
12
|
+
def grant_access!(client, options = {})
|
13
|
+
Authorization.for(self, client, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def oauth2_authorization_for(client)
|
17
|
+
oauth2_authorizations.find_by_client_id(client.id)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module RockOAuth
|
4
|
+
module Model
|
5
|
+
autoload :Helpers, ROOT + '/model/helpers'
|
6
|
+
autoload :ClientOwner, ROOT + '/model/client_owner'
|
7
|
+
autoload :ResourceOwner, ROOT + '/model/resource_owner'
|
8
|
+
autoload :Hashing, ROOT + '/model/hashing'
|
9
|
+
autoload :Authorization, ROOT + '/model/authorization'
|
10
|
+
autoload :Client, ROOT + '/model/client'
|
11
|
+
|
12
|
+
Schema = RockOAuth::Schema
|
13
|
+
|
14
|
+
DUPLICATE_RECORD_ERRORS = [
|
15
|
+
/^Mysql::Error:\s+Duplicate\s+entry\b/,
|
16
|
+
/^PG::Error:\s+ERROR:\s+duplicate\s+key\b/,
|
17
|
+
/\bConstraintException\b/
|
18
|
+
]
|
19
|
+
|
20
|
+
# ActiveRecord::RecordNotUnique was introduced in Rails 3.0 so referring
|
21
|
+
# to it while running earlier versions will raise an error. The above
|
22
|
+
# error strings should match PostgreSQL, MySQL and SQLite errors on
|
23
|
+
# Rails 2. If you're running a different adapter, add a suitable regex to
|
24
|
+
# the list:
|
25
|
+
#
|
26
|
+
# RockOAuth::Model::DUPLICATE_RECORD_ERRORS << /DB2 found a dup/
|
27
|
+
#
|
28
|
+
def self.duplicate_record_error?(error)
|
29
|
+
error.class.name == 'ActiveRecord::RecordNotUnique' or
|
30
|
+
DUPLICATE_RECORD_ERRORS.any? { |re| re =~ error.message }
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.find_access_token(access_token)
|
34
|
+
return nil if access_token.nil?
|
35
|
+
Authorization.find_by_access_token_hash(RockOAuth.hashify(access_token))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module RockOAuth
|
2
|
+
class Provider
|
3
|
+
|
4
|
+
class AccessToken
|
5
|
+
attr_reader :authorization
|
6
|
+
|
7
|
+
def initialize(resource_owner = nil, scopes = [], access_token = nil, error = nil)
|
8
|
+
@resource_owner = resource_owner
|
9
|
+
@scopes = scopes
|
10
|
+
@access_token = access_token
|
11
|
+
@error = error && INVALID_REQUEST
|
12
|
+
|
13
|
+
authorize!(access_token, error)
|
14
|
+
validate!
|
15
|
+
end
|
16
|
+
|
17
|
+
def client
|
18
|
+
valid? ? @authorization.client : nil
|
19
|
+
end
|
20
|
+
|
21
|
+
def owner
|
22
|
+
valid? ? @authorization.owner : nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def response_headers
|
26
|
+
return {} if valid?
|
27
|
+
error_message = "OAuth realm='#{ Provider.realm }'"
|
28
|
+
error_message << ", error='#{ @error }'" unless @error == ''
|
29
|
+
{'WWW-Authenticate' => error_message}
|
30
|
+
end
|
31
|
+
|
32
|
+
def response_status
|
33
|
+
case @error
|
34
|
+
when INVALID_REQUEST, INVALID_TOKEN, EXPIRED_TOKEN then 401
|
35
|
+
when INSUFFICIENT_SCOPE then 403
|
36
|
+
when '' then 401
|
37
|
+
else 200
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def valid?
|
42
|
+
@error.nil?
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def authorize!(access_token, error)
|
48
|
+
return unless @authorization = Model.find_access_token(access_token)
|
49
|
+
@authorization.update_attribute(:access_token, nil) if error
|
50
|
+
end
|
51
|
+
|
52
|
+
def validate!
|
53
|
+
return @error = '' unless @access_token
|
54
|
+
return @error = INVALID_TOKEN unless @authorization
|
55
|
+
return @error = EXPIRED_TOKEN if @authorization.expired?
|
56
|
+
return @error = INSUFFICIENT_SCOPE unless @authorization.in_scope?(@scopes)
|
57
|
+
|
58
|
+
case @resource_owner
|
59
|
+
when :implicit
|
60
|
+
# no error
|
61
|
+
when nil
|
62
|
+
@error = INVALID_TOKEN
|
63
|
+
else
|
64
|
+
@error = INSUFFICIENT_SCOPE if @authorization.owner != @resource_owner
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|