cloudkit-jruby 0.11.2
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.
- data/CHANGES +47 -0
- data/COPYING +20 -0
- data/README +84 -0
- data/Rakefile +42 -0
- data/TODO +21 -0
- data/cloudkit.gemspec +89 -0
- data/doc/curl.html +388 -0
- data/doc/images/example-code.gif +0 -0
- data/doc/images/json-title.gif +0 -0
- data/doc/images/oauth-discovery-logo.gif +0 -0
- data/doc/images/openid-logo.gif +0 -0
- data/doc/index.html +90 -0
- data/doc/main.css +151 -0
- data/doc/rest-api.html +467 -0
- data/examples/1.ru +3 -0
- data/examples/2.ru +3 -0
- data/examples/3.ru +6 -0
- data/examples/4.ru +5 -0
- data/examples/5.ru +9 -0
- data/examples/6.ru +11 -0
- data/examples/TOC +17 -0
- data/lib/cloudkit.rb +92 -0
- data/lib/cloudkit/constants.rb +34 -0
- data/lib/cloudkit/exceptions.rb +10 -0
- data/lib/cloudkit/flash_session.rb +20 -0
- data/lib/cloudkit/oauth_filter.rb +266 -0
- data/lib/cloudkit/oauth_store.rb +48 -0
- data/lib/cloudkit/openid_filter.rb +236 -0
- data/lib/cloudkit/openid_store.rb +100 -0
- data/lib/cloudkit/rack/builder.rb +120 -0
- data/lib/cloudkit/rack/router.rb +20 -0
- data/lib/cloudkit/request.rb +177 -0
- data/lib/cloudkit/service.rb +162 -0
- data/lib/cloudkit/store.rb +349 -0
- data/lib/cloudkit/store/memory_table.rb +99 -0
- data/lib/cloudkit/store/resource.rb +269 -0
- data/lib/cloudkit/store/response.rb +52 -0
- data/lib/cloudkit/store/response_helpers.rb +84 -0
- data/lib/cloudkit/templates/authorize_request_token.erb +19 -0
- data/lib/cloudkit/templates/oauth_descriptor.erb +43 -0
- data/lib/cloudkit/templates/oauth_meta.erb +8 -0
- data/lib/cloudkit/templates/openid_login.erb +31 -0
- data/lib/cloudkit/templates/request_authorization.erb +23 -0
- data/lib/cloudkit/templates/request_token_denied.erb +18 -0
- data/lib/cloudkit/uri.rb +88 -0
- data/lib/cloudkit/user_store.rb +37 -0
- data/lib/cloudkit/util.rb +25 -0
- data/spec/ext_spec.rb +76 -0
- data/spec/flash_session_spec.rb +20 -0
- data/spec/memory_table_spec.rb +86 -0
- data/spec/oauth_filter_spec.rb +326 -0
- data/spec/oauth_store_spec.rb +10 -0
- data/spec/openid_filter_spec.rb +81 -0
- data/spec/openid_store_spec.rb +101 -0
- data/spec/rack_builder_spec.rb +39 -0
- data/spec/request_spec.rb +191 -0
- data/spec/resource_spec.rb +310 -0
- data/spec/service_spec.rb +1039 -0
- data/spec/spec_helper.rb +32 -0
- data/spec/store_spec.rb +10 -0
- data/spec/uri_spec.rb +93 -0
- data/spec/user_store_spec.rb +10 -0
- data/spec/util_spec.rb +11 -0
- metadata +180 -0
data/examples/1.ru
ADDED
data/examples/2.ru
ADDED
data/examples/3.ru
ADDED
@@ -0,0 +1,6 @@
|
|
1
|
+
$:.unshift File.expand_path(File.dirname(__FILE__)) + '/../lib'
|
2
|
+
require 'cloudkit'
|
3
|
+
use Rack::Session::Pool
|
4
|
+
use CloudKit::OpenIDFilter
|
5
|
+
use CloudKit::Service, :collections => [:notes]
|
6
|
+
run lambda{|env| [200, {'Content-Type' => 'text/html', 'Content-Length' => '5'}, ['HELLO']]}
|
data/examples/4.ru
ADDED
data/examples/5.ru
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
$:.unshift File.expand_path(File.dirname(__FILE__)) + '/../lib'
|
2
|
+
require 'cloudkit'
|
3
|
+
require 'rufus/tokyo' # gem install rufus-tokyo
|
4
|
+
CloudKit.setup_storage_adapter(Rufus::Tokyo::Table.new('cloudkit.tdb'))
|
5
|
+
use Rack::Session::Pool
|
6
|
+
use CloudKit::OAuthFilter
|
7
|
+
use CloudKit::OpenIDFilter
|
8
|
+
use CloudKit::Service, :collections => [:notes]
|
9
|
+
run lambda{|env| [200, {'Content-Type' => 'text/html', 'Content-Length' => '5'}, ['HELLO']]}
|
data/examples/6.ru
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
$:.unshift File.expand_path(File.dirname(__FILE__)) + '/../lib'
|
2
|
+
require 'cloudkit'
|
3
|
+
require 'rufus/tokyo/tyrant' # gem install rufus-tokyo
|
4
|
+
# start Tokyo Tyrant with a table store...
|
5
|
+
# ttserver data.tct
|
6
|
+
CloudKit.setup_storage_adapter(Rufus::Tokyo::TyrantTable.new('127.0.0.1', 1978))
|
7
|
+
use Rack::Session::Pool
|
8
|
+
use CloudKit::OAuthFilter
|
9
|
+
use CloudKit::OpenIDFilter
|
10
|
+
use CloudKit::Service, :collections => [:notes]
|
11
|
+
run lambda{|env| [200, {'Content-Type' => 'text/html', 'Content-Length' => '5'}, ['HELLO']]}
|
data/examples/TOC
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
Index of Examples
|
2
|
+
-----------------
|
3
|
+
|
4
|
+
When using the gem version of CloudKit, the first line of each example can be
|
5
|
+
removed.
|
6
|
+
|
7
|
+
1. Expose Notes - Mount a JSON "notes" API. Uses in-memory store.
|
8
|
+
|
9
|
+
2. Contain Notes - Same as #1, adding OpenID and OAuth.
|
10
|
+
|
11
|
+
3. Notes with OpenID - Same as #1 using only OpenID.
|
12
|
+
|
13
|
+
4. Notes with OAuth - Same as #1 using only OAuth.
|
14
|
+
|
15
|
+
5. Tokyo Notes - Same as #2 with a Tokyo Cabinet Table store.
|
16
|
+
|
17
|
+
6. Tyrant Notes - Same as #2 with a Tokyo Tyrant Table store.
|
data/lib/cloudkit.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'erb'
|
3
|
+
require 'json'
|
4
|
+
require 'digest/md5'
|
5
|
+
require 'openid'
|
6
|
+
require 'time'
|
7
|
+
require 'uuid'
|
8
|
+
require 'rack'
|
9
|
+
require 'oauth'
|
10
|
+
require 'oauth/consumer'
|
11
|
+
require 'oauth/request_proxy/rack_request'
|
12
|
+
require 'oauth/server'
|
13
|
+
require 'oauth/signature'
|
14
|
+
require 'cloudkit/constants'
|
15
|
+
require 'cloudkit/exceptions'
|
16
|
+
require 'cloudkit/util'
|
17
|
+
require 'cloudkit/uri'
|
18
|
+
require 'cloudkit/store/memory_table'
|
19
|
+
require 'cloudkit/store/resource'
|
20
|
+
require 'cloudkit/store/response'
|
21
|
+
require 'cloudkit/store/response_helpers'
|
22
|
+
require 'cloudkit/store'
|
23
|
+
require 'cloudkit/flash_session'
|
24
|
+
require 'cloudkit/oauth_filter'
|
25
|
+
require 'cloudkit/oauth_store'
|
26
|
+
require 'cloudkit/openid_filter'
|
27
|
+
require 'cloudkit/openid_store'
|
28
|
+
require 'cloudkit/rack/builder'
|
29
|
+
require 'cloudkit/rack/router'
|
30
|
+
require 'cloudkit/request'
|
31
|
+
require 'cloudkit/service'
|
32
|
+
require 'cloudkit/user_store'
|
33
|
+
|
34
|
+
include CloudKit::Constants
|
35
|
+
|
36
|
+
module CloudKit
|
37
|
+
VERSION = '0.11.2'
|
38
|
+
|
39
|
+
# Sets up the storage adapter. Defaults to development-time
|
40
|
+
# CloudKit::MemoryTable. Also supports Rufus Tokyo Table instances. See the
|
41
|
+
# examples directory for Cabinet and Tyrant Table examples.
|
42
|
+
def self.setup_storage_adapter(adapter_instance=nil)
|
43
|
+
@storage_adapter = adapter_instance || CloudKit::MemoryTable.new
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return the shared storage adapter.
|
47
|
+
def self.storage_adapter
|
48
|
+
@storage_adapter
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class Object
|
53
|
+
|
54
|
+
# Execute a method if it exists.
|
55
|
+
def try(method) # via defunkt
|
56
|
+
send method if respond_to? method
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class Hash
|
61
|
+
|
62
|
+
# For each key in 'other' that has a non-nil value, merge it into the current
|
63
|
+
# Hash.
|
64
|
+
def filter_merge!(other={})
|
65
|
+
other.each_pair{|k,v| self.merge!(k => v) unless v.nil?}
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
# Change the key 'oldkey' to 'newkey'
|
70
|
+
def rekey!(oldkey, newkey)
|
71
|
+
if self.has_key? oldkey
|
72
|
+
self[newkey] = self.delete(oldkey)
|
73
|
+
end
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
|
77
|
+
# Return a new Hash, excluding the specified list of keys.
|
78
|
+
def excluding(*keys)
|
79
|
+
trimmed = self.dup
|
80
|
+
keys.each{|k| trimmed.delete(k)}
|
81
|
+
trimmed
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class Array
|
86
|
+
|
87
|
+
# Return a new Array, excluding the specified list of values.
|
88
|
+
def excluding(*keys)
|
89
|
+
trimmed = self.dup
|
90
|
+
trimmed - keys
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module CloudKit
|
2
|
+
module Constants
|
3
|
+
|
4
|
+
# The key used to store the authenticated user.
|
5
|
+
CLOUDKIT_AUTH_KEY = 'cloudkit.user'.freeze
|
6
|
+
|
7
|
+
# The key used to indicate the presence of auth in a stack.
|
8
|
+
CLOUDKIT_AUTH_PRESENCE = 'cloudkit.auth'.freeze
|
9
|
+
|
10
|
+
# The key used to store auth challenge headers shared between
|
11
|
+
# OpenID and OAuth middleware.
|
12
|
+
CLOUDKIT_AUTH_CHALLENGE = 'cloudkit.challenge'.freeze
|
13
|
+
|
14
|
+
# The 'via' key used to announce and track upstream middleware.
|
15
|
+
CLOUDKIT_VIA = 'cloudkit.via'.freeze
|
16
|
+
|
17
|
+
# The key used to store the 'flash' in the session.
|
18
|
+
CLOUDKIT_FLASH = 'cloudkit.flash'.freeze
|
19
|
+
|
20
|
+
# The 'via' key for the OAuth filter.
|
21
|
+
CLOUDKIT_OAUTH_FILTER_KEY = 'cloudkit.filter.oauth'.freeze
|
22
|
+
|
23
|
+
# The 'via' key for the OpenID filter.
|
24
|
+
CLOUDKIT_OPENID_FILTER_KEY = 'cloudkit.filter.openid'.freeze
|
25
|
+
|
26
|
+
# The key for the login URL used in OpenID and OAuth middleware
|
27
|
+
# components.
|
28
|
+
CLOUDKIT_LOGIN_URL = 'cloudkit.filter.openid.url.login'.freeze
|
29
|
+
|
30
|
+
# The key for the logout URL used in OpenID and OAuth middleware
|
31
|
+
# components.
|
32
|
+
CLOUDKIT_LOGOUT_URL = 'cloudkit.filter.openid.url.logout'.freeze
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module CloudKit
|
2
|
+
|
3
|
+
# HistoricalIntegrityViolation exceptions are raised when an attempt is made
|
4
|
+
# to modify an archived or deleted version of a resource.
|
5
|
+
class HistoricalIntegrityViolation < Exception; end
|
6
|
+
|
7
|
+
# InvalidURIFormat exceptions are raised during attempts to get or generate
|
8
|
+
# cannonical URIs from non-collection or non-resource URIs.
|
9
|
+
class InvalidURIFormat < Exception; end
|
10
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module CloudKit
|
2
|
+
|
3
|
+
# FlashSessions are hashes that forget their contents after the first access.
|
4
|
+
# Useful for session-based messaging.
|
5
|
+
class FlashSession
|
6
|
+
def initialize
|
7
|
+
@values = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
# Set the value for a key.
|
11
|
+
def []=(k, v)
|
12
|
+
@values[k] = v
|
13
|
+
end
|
14
|
+
|
15
|
+
# Access a value, then forget it.
|
16
|
+
def [](k)
|
17
|
+
@values.delete(k)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,266 @@
|
|
1
|
+
module CloudKit
|
2
|
+
|
3
|
+
# An OAuthFilter provides both OAuth 1.0 support, plus OAuth Discovery.
|
4
|
+
#
|
5
|
+
# Responds to the following URIs as part of the OAuth 1.0 "dance":
|
6
|
+
#
|
7
|
+
# /oauth/request_tokens
|
8
|
+
# /oauth/authorization
|
9
|
+
# /oauth/authorized_request_tokens/{id}
|
10
|
+
# /oauth/access_tokens
|
11
|
+
#
|
12
|
+
# Responds to the following URIs as part of OAuth Discovery:
|
13
|
+
# /oauth
|
14
|
+
# /oauth/meta
|
15
|
+
#
|
16
|
+
# See also:
|
17
|
+
# - {OAuth Core 1.0}[http://oauth.net/core/1.0]
|
18
|
+
# - {OAuth Discovery}[http://oauth.net/discovery]
|
19
|
+
# - Thread[http://groups.google.com/group/oauth/browse_thread/thread/29a1b550396f63cf] covering /oauth and /oauth/meta URIs.
|
20
|
+
#
|
21
|
+
class OAuthFilter
|
22
|
+
include Util
|
23
|
+
|
24
|
+
@@lock = Mutex.new
|
25
|
+
@@store = nil
|
26
|
+
|
27
|
+
def initialize(app, options={})
|
28
|
+
@app = app
|
29
|
+
@options = options
|
30
|
+
end
|
31
|
+
|
32
|
+
def call(env)
|
33
|
+
@@lock.synchronize do
|
34
|
+
@@store = OAuthStore.new
|
35
|
+
end unless @@store
|
36
|
+
|
37
|
+
request = Request.new(env)
|
38
|
+
request.announce_auth(CLOUDKIT_OAUTH_FILTER_KEY)
|
39
|
+
return xrds_location(request) if oauth_disco_draft2_xrds?(request)
|
40
|
+
return @app.call(env) if request.path_info == '/'
|
41
|
+
|
42
|
+
load_user_from_session(request)
|
43
|
+
|
44
|
+
begin
|
45
|
+
case request
|
46
|
+
when r(:get, '/oauth/meta')
|
47
|
+
get_meta(request)
|
48
|
+
when r(:post, '/oauth/request_tokens', ['oauth_consumer_key'])
|
49
|
+
create_request_token(request)
|
50
|
+
when r(:get, '/oauth/authorization', ['oauth_token'])
|
51
|
+
request_authorization(request)
|
52
|
+
when r(:put, '/oauth/authorized_request_tokens/:id', ['submit' => 'Approve'])
|
53
|
+
# Temporarily relying on a button value until pluggable templates are
|
54
|
+
# introduced in 1.0.
|
55
|
+
authorize_request_token(request)
|
56
|
+
when r(:put, '/oauth/authorized_request_tokens/:id', ['submit' => 'Deny'])
|
57
|
+
# See previous comment.
|
58
|
+
deny_request_token(request)
|
59
|
+
when r(:post, '/oauth/authorized_request_tokens/:id', [{'_method' => 'PUT'}])
|
60
|
+
authorize_request_token(request)
|
61
|
+
when r(:post, '/oauth/access_tokens')
|
62
|
+
create_access_token(request)
|
63
|
+
when r(:get, '/oauth')
|
64
|
+
get_descriptor(request)
|
65
|
+
else
|
66
|
+
inject_user_or_challenge(request)
|
67
|
+
@app.call(env)
|
68
|
+
end
|
69
|
+
rescue OAuth::Signature::UnknownSignatureMethod
|
70
|
+
# The OAuth spec suggests a 400 status, but serving a 401 with the
|
71
|
+
# meta/challenge info seems more appropriate as the OAuth metadata
|
72
|
+
# specifies the supported signature methods, giving the user agent an
|
73
|
+
# opportunity to fix the error.
|
74
|
+
return challenge(request, 'unknown signature method')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def store; @@store; end
|
79
|
+
|
80
|
+
protected
|
81
|
+
|
82
|
+
def create_request_token(request)
|
83
|
+
return challenge(request, 'invalid nonce') unless valid_nonce?(request)
|
84
|
+
|
85
|
+
consumer_result = @@store.get("/cloudkit_oauth_consumers/#{request[:oauth_consumer_key]}")
|
86
|
+
unless consumer_result.status == 200
|
87
|
+
return challenge(request, 'invalid consumer')
|
88
|
+
end
|
89
|
+
|
90
|
+
consumer = consumer_result.parsed_content
|
91
|
+
signature = OAuth::Signature.build(request) { [nil, consumer['secret']] }
|
92
|
+
return challenge(request, 'invalid signature') unless signature.verify
|
93
|
+
|
94
|
+
token_id, secret = OAuth::Server.new(request.host).generate_credentials
|
95
|
+
request_token = JSON.generate(
|
96
|
+
:secret => secret,
|
97
|
+
:consumer_key => request[:oauth_consumer_key])
|
98
|
+
@@store.put(
|
99
|
+
"/cloudkit_oauth_request_tokens/#{token_id}",
|
100
|
+
:json => request_token)
|
101
|
+
Rack::Response.new("oauth_token=#{token_id}&oauth_token_secret=#{secret}", 201).finish
|
102
|
+
end
|
103
|
+
|
104
|
+
def request_authorization(request)
|
105
|
+
return login_redirect(request) unless request.current_user
|
106
|
+
|
107
|
+
request_token_result = @@store.get("/cloudkit_oauth_request_tokens/#{request[:oauth_token]}")
|
108
|
+
unless request_token_result.status == 200
|
109
|
+
return challenge(request, 'invalid request token')
|
110
|
+
end
|
111
|
+
|
112
|
+
request_token = request_token_result.parsed_content
|
113
|
+
erb(request, :request_authorization)
|
114
|
+
end
|
115
|
+
|
116
|
+
def authorize_request_token(request)
|
117
|
+
return login_redirect(request) unless request.current_user
|
118
|
+
|
119
|
+
request_token_response = @@store.get("/cloudkit_oauth_request_tokens/#{request.last_path_element}")
|
120
|
+
request_token = request_token_response.parsed_content
|
121
|
+
if request_token['authorized_at']
|
122
|
+
return challenge(request, 'invalid request token')
|
123
|
+
end
|
124
|
+
|
125
|
+
request_token['user_id'] = request.current_user
|
126
|
+
request_token['authorized_at'] = Time.now.httpdate
|
127
|
+
json = JSON.generate(request_token)
|
128
|
+
@@store.put(
|
129
|
+
"/cloudkit_oauth_request_tokens/#{request.last_path_element}",
|
130
|
+
:etag => request_token_response.etag,
|
131
|
+
:json => json)
|
132
|
+
erb(request, :authorize_request_token)
|
133
|
+
end
|
134
|
+
|
135
|
+
def deny_request_token(request)
|
136
|
+
return login_redirect(request) unless request.current_user
|
137
|
+
|
138
|
+
request_token_response = @@store.get("/cloudkit_oauth_request_tokens/#{request.last_path_element}")
|
139
|
+
@@store.delete(
|
140
|
+
"/cloudkit_oauth_request_tokens/#{request.last_path_element}",
|
141
|
+
:etag => request_token_response.etag)
|
142
|
+
erb(request, :request_token_denied)
|
143
|
+
end
|
144
|
+
|
145
|
+
def create_access_token(request)
|
146
|
+
return challenge(request, 'invalid nonce') unless valid_nonce?(request)
|
147
|
+
|
148
|
+
consumer_response = @@store.get("/cloudkit_oauth_consumers/#{request[:oauth_consumer_key]}")
|
149
|
+
unless consumer_response.status == 200
|
150
|
+
return challenge(request, 'invalid consumer')
|
151
|
+
end
|
152
|
+
|
153
|
+
consumer = consumer_response.parsed_content
|
154
|
+
request_token_response = @@store.get("/cloudkit_oauth_request_tokens/#{request[:oauth_token]}")
|
155
|
+
unless request_token_response.status == 200
|
156
|
+
return challenge(request, 'invalid request token')
|
157
|
+
end
|
158
|
+
|
159
|
+
request_token = request_token_response.parsed_content
|
160
|
+
unless request_token['consumer_key'] == request[:oauth_consumer_key]
|
161
|
+
return challenge(request, 'invalid consumer')
|
162
|
+
end
|
163
|
+
|
164
|
+
signature = OAuth::Signature.build(request) do
|
165
|
+
[request_token['secret'], consumer['secret']]
|
166
|
+
end
|
167
|
+
unless signature.verify
|
168
|
+
return challenge(request, 'invalid signature')
|
169
|
+
end
|
170
|
+
|
171
|
+
token_id, secret = OAuth::Server.new(request.host).generate_credentials
|
172
|
+
token_data = JSON.generate(
|
173
|
+
:secret => secret,
|
174
|
+
:consumer_key => request[:oauth_consumer_key],
|
175
|
+
:consumer_secret => consumer['secret'],
|
176
|
+
:user_id => request_token['user_id'])
|
177
|
+
@@store.put("/cloudkit_oauth_tokens/#{token_id}", :json => token_data)
|
178
|
+
@@store.delete(
|
179
|
+
"/cloudkit_oauth_request_tokens/#{request[:oauth_token]}",
|
180
|
+
:etag => request_token_response.etag)
|
181
|
+
Rack::Response.new("oauth_token=#{token_id}&oauth_token_secret=#{secret}", 201).finish
|
182
|
+
end
|
183
|
+
|
184
|
+
def inject_user_or_challenge(request)
|
185
|
+
unless valid_nonce?(request)
|
186
|
+
request.current_user = ''
|
187
|
+
inject_challenge(request)
|
188
|
+
return
|
189
|
+
end
|
190
|
+
|
191
|
+
result = @@store.get("/cloudkit_oauth_tokens/#{request[:oauth_token]}")
|
192
|
+
access_token = result.parsed_content
|
193
|
+
signature = OAuth::Signature.build(request) do
|
194
|
+
[access_token['secret'], access_token['consumer_secret']]
|
195
|
+
end
|
196
|
+
if signature.verify
|
197
|
+
request.current_user = access_token['user_id']
|
198
|
+
else
|
199
|
+
request.current_user = ''
|
200
|
+
inject_challenge(request)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def valid_nonce?(request)
|
205
|
+
timestamp = request[:oauth_timestamp]
|
206
|
+
nonce = request[:oauth_nonce]
|
207
|
+
return false unless (timestamp && nonce)
|
208
|
+
|
209
|
+
uri = "/cloudkit_oauth_nonces/#{timestamp},#{nonce}"
|
210
|
+
result = @@store.put(uri, :json => '{}')
|
211
|
+
return false unless result.status == 201
|
212
|
+
|
213
|
+
true
|
214
|
+
end
|
215
|
+
|
216
|
+
def inject_challenge(request)
|
217
|
+
request.env[CLOUDKIT_AUTH_CHALLENGE] = challenge_headers(request)
|
218
|
+
end
|
219
|
+
|
220
|
+
def challenge(request, message='')
|
221
|
+
Rack::Response.new(message, 401, challenge_headers(request)).finish
|
222
|
+
end
|
223
|
+
|
224
|
+
def challenge_headers(request)
|
225
|
+
{
|
226
|
+
'WWW-Authenticate' => "OAuth realm=\"http://#{request.env['HTTP_HOST']}\"",
|
227
|
+
'Link' => discovery_link(request),
|
228
|
+
'Content-Type' => 'application/json'
|
229
|
+
}
|
230
|
+
end
|
231
|
+
|
232
|
+
def discovery_link(request)
|
233
|
+
"<#{request.scheme}://#{request.env['HTTP_HOST']}/oauth/meta>; rel=\"http://oauth.net/discovery/1.0/rel/provider\""
|
234
|
+
end
|
235
|
+
|
236
|
+
def login_redirect(request)
|
237
|
+
request.session['return_to'] = request.url if request.session
|
238
|
+
Rack::Response.new([], 302, {'Location' => request.login_url}).finish
|
239
|
+
end
|
240
|
+
|
241
|
+
def load_user_from_session(request)
|
242
|
+
request.current_user = request.session['user_uri'] if request.session
|
243
|
+
end
|
244
|
+
|
245
|
+
def get_meta(request)
|
246
|
+
# Expected in next OAuth Discovery Draft
|
247
|
+
erb(request, :oauth_meta)
|
248
|
+
end
|
249
|
+
|
250
|
+
def oauth_disco_draft2_xrds?(request)
|
251
|
+
# Current OAuth Discovery Draft 2 / XRDS-Simple 1.0, Section 5.1.2
|
252
|
+
request.get? &&
|
253
|
+
request.env['HTTP_ACCEPT'] &&
|
254
|
+
request.env['HTTP_ACCEPT'].match(/application\/xrds\+xml/)
|
255
|
+
end
|
256
|
+
|
257
|
+
def xrds_location(request)
|
258
|
+
# Current OAuth Discovery Draft 2 / XRDS-Simple 1.0, Section 5.1.2
|
259
|
+
Rack::Response.new([], 200, {'X-XRDS-Location' => "#{request.scheme}://#{request.env['HTTP_HOST']}/oauth"}).finish
|
260
|
+
end
|
261
|
+
|
262
|
+
def get_descriptor(request)
|
263
|
+
erb(request, :oauth_descriptor, {'Content-Type' => 'application/xrds+xml'})
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|