rockoauth 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|