cloudkit 0.9.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.
- data/CHANGES +2 -0
- data/COPYING +20 -0
- data/README +55 -0
- data/Rakefile +35 -0
- data/TODO +22 -0
- data/cloudkit.gemspec +82 -0
- data/doc/curl.html +329 -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 +87 -0
- data/doc/main.css +151 -0
- data/doc/rest-api.html +358 -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 +10 -0
- data/examples/6.ru +10 -0
- data/examples/TOC +17 -0
- data/lib/cloudkit.rb +74 -0
- data/lib/cloudkit/flash_session.rb +22 -0
- data/lib/cloudkit/oauth_filter.rb +273 -0
- data/lib/cloudkit/oauth_store.rb +56 -0
- data/lib/cloudkit/openid_filter.rb +198 -0
- data/lib/cloudkit/openid_store.rb +101 -0
- data/lib/cloudkit/rack/builder.rb +120 -0
- data/lib/cloudkit/rack/router.rb +20 -0
- data/lib/cloudkit/request.rb +159 -0
- data/lib/cloudkit/service.rb +135 -0
- data/lib/cloudkit/store.rb +459 -0
- data/lib/cloudkit/store/adapter.rb +9 -0
- data/lib/cloudkit/store/extraction_view.rb +57 -0
- data/lib/cloudkit/store/response.rb +51 -0
- data/lib/cloudkit/store/response_helpers.rb +72 -0
- data/lib/cloudkit/store/sql_adapter.rb +36 -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/user_store.rb +44 -0
- data/lib/cloudkit/util.rb +60 -0
- data/test/ext_test.rb +57 -0
- data/test/flash_session_test.rb +22 -0
- data/test/helper.rb +50 -0
- data/test/oauth_filter_test.rb +331 -0
- data/test/oauth_store_test.rb +12 -0
- data/test/openid_filter_test.rb +54 -0
- data/test/openid_store_test.rb +12 -0
- data/test/rack_builder_test.rb +41 -0
- data/test/request_test.rb +197 -0
- data/test/service_test.rb +718 -0
- data/test/store_test.rb +99 -0
- data/test/user_store_test.rb +12 -0
- data/test/util_test.rb +13 -0
- metadata +190 -0
data/examples/1.ru
ADDED
data/examples/2.ru
ADDED
data/examples/3.ru
ADDED
data/examples/4.ru
ADDED
data/examples/5.ru
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
$:.unshift File.expand_path(File.dirname(__FILE__)) + '/../lib'
|
2
|
+
require 'cloudkit'
|
3
|
+
use Rack::Config do |env|
|
4
|
+
env['cloudkit.storage.uri'] = 'sqlite://example.db'
|
5
|
+
end
|
6
|
+
use Rack::Session::Pool
|
7
|
+
use CloudKit::OAuthFilter
|
8
|
+
use CloudKit::OpenIDFilter
|
9
|
+
use CloudKit::Service, :collections => [:notes]
|
10
|
+
run lambda{|env| [200, {}, ['HELLO']]}
|
data/examples/6.ru
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
$:.unshift File.expand_path(File.dirname(__FILE__)) + '/../lib'
|
2
|
+
require 'cloudkit'
|
3
|
+
use Rack::Config do |env|
|
4
|
+
env['cloudkit.storage.uri'] = 'mysql://root:@localhost/cloudkit_example'
|
5
|
+
end
|
6
|
+
use Rack::Session::Pool
|
7
|
+
use CloudKit::OAuthFilter
|
8
|
+
use CloudKit::OpenIDFilter
|
9
|
+
use CloudKit::Service, :collections => [:notes]
|
10
|
+
run lambda{|env| [200, {}, ['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 SQLite.
|
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. SQLite Notes - Same as #2 with a SQLite file store.
|
16
|
+
|
17
|
+
6. MySQL Notes - Same as #2 with MySQL store.
|
data/lib/cloudkit.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'erb'
|
3
|
+
require 'json'
|
4
|
+
require 'md5'
|
5
|
+
require 'openid'
|
6
|
+
gem 'sequel', '=2.6.0'
|
7
|
+
require 'sequel'
|
8
|
+
require 'time'
|
9
|
+
require 'uuid'
|
10
|
+
require 'rack'
|
11
|
+
require 'rack/config'
|
12
|
+
require 'oauth'
|
13
|
+
require 'oauth/consumer'
|
14
|
+
require 'oauth/request_proxy/rack_request'
|
15
|
+
require 'oauth/server'
|
16
|
+
require 'oauth/signature'
|
17
|
+
require 'cloudkit/util'
|
18
|
+
require 'cloudkit/store/adapter'
|
19
|
+
require 'cloudkit/store/extraction_view'
|
20
|
+
require 'cloudkit/store/response'
|
21
|
+
require 'cloudkit/store/response_helpers'
|
22
|
+
require 'cloudkit/store/sql_adapter'
|
23
|
+
require 'cloudkit/store'
|
24
|
+
require 'cloudkit/flash_session'
|
25
|
+
require 'cloudkit/oauth_filter'
|
26
|
+
require 'cloudkit/oauth_store'
|
27
|
+
require 'cloudkit/openid_filter'
|
28
|
+
require 'cloudkit/openid_store'
|
29
|
+
require 'cloudkit/rack/builder'
|
30
|
+
require 'cloudkit/rack/router'
|
31
|
+
require 'cloudkit/request'
|
32
|
+
require 'cloudkit/service'
|
33
|
+
require 'cloudkit/user_store'
|
34
|
+
|
35
|
+
class Object
|
36
|
+
|
37
|
+
# Execute a method if it exists.
|
38
|
+
def try(method) # via defunkt
|
39
|
+
send method if respond_to? method
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Hash
|
44
|
+
|
45
|
+
# For each key in 'other' that has a non-nil value, merge it into the current
|
46
|
+
# Hash.
|
47
|
+
def filter_merge!(other={})
|
48
|
+
other.each_pair{|k,v| self.merge!(k => v) if v}
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
# Change the key 'oldkey' to 'newkey'
|
53
|
+
def rekey!(oldkey, newkey)
|
54
|
+
if self[oldkey]
|
55
|
+
self[newkey] = self.delete(oldkey)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Return a new Hash, excluding the specified list of keys.
|
60
|
+
def excluding(*keys)
|
61
|
+
trimmed = self.dup
|
62
|
+
keys.each{|k| trimmed.delete(k)}
|
63
|
+
trimmed
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Array
|
68
|
+
|
69
|
+
# Return a new Array, excluding the specified list of values.
|
70
|
+
def excluding(*keys)
|
71
|
+
trimmed = self.dup
|
72
|
+
trimmed.reject{|v| keys.include?(v)}
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,22 @@
|
|
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
|
+
v = @values[k]
|
18
|
+
@values[k] = nil
|
19
|
+
v
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,273 @@
|
|
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 are 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(env[storage_uri_key])
|
35
|
+
end unless @@store
|
36
|
+
|
37
|
+
request = Request.new(env)
|
38
|
+
request.announce_auth(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(
|
86
|
+
"/cloudkit_oauth_consumers/#{request[:oauth_consumer_key]}")
|
87
|
+
unless consumer_result.status == 200
|
88
|
+
return challenge(request, 'invalid consumer')
|
89
|
+
end
|
90
|
+
|
91
|
+
consumer = consumer_result.parsed_content
|
92
|
+
signature = OAuth::Signature.build(request) { [nil, consumer['secret']] }
|
93
|
+
return challenge(request, 'invalid signature') unless signature.verify
|
94
|
+
|
95
|
+
token_id, secret = OAuth::Server.new(request.host).generate_credentials
|
96
|
+
request_token = JSON.generate(
|
97
|
+
:secret => secret,
|
98
|
+
:consumer_key => request[:oauth_consumer_key])
|
99
|
+
@@store.put(
|
100
|
+
"/cloudkit_oauth_request_tokens/#{token_id}",
|
101
|
+
:json => request_token)
|
102
|
+
[201, {}, ["oauth_token=#{token_id}&oauth_token_secret=#{secret}"]]
|
103
|
+
end
|
104
|
+
|
105
|
+
def request_authorization(request)
|
106
|
+
return login_redirect(request) unless request.current_user
|
107
|
+
|
108
|
+
request_token_result = @@store.get(
|
109
|
+
"/cloudkit_oauth_request_tokens/#{request[:oauth_token]}")
|
110
|
+
unless request_token_result.status == 200
|
111
|
+
return challenge(request, 'invalid request token')
|
112
|
+
end
|
113
|
+
|
114
|
+
request_token = request_token_result.parsed_content
|
115
|
+
erb(request, :request_authorization)
|
116
|
+
end
|
117
|
+
|
118
|
+
def authorize_request_token(request)
|
119
|
+
return login_redirect(request) unless request.current_user
|
120
|
+
|
121
|
+
request_token_response = @@store.get(
|
122
|
+
"/cloudkit_oauth_request_tokens/#{request.last_path_element}")
|
123
|
+
request_token = request_token_response.parsed_content
|
124
|
+
if request_token['authorized_at']
|
125
|
+
return challenge(request, 'invalid request token')
|
126
|
+
end
|
127
|
+
|
128
|
+
request_token['user_id'] = request.current_user
|
129
|
+
request_token['authorized_at'] = Time.now.httpdate
|
130
|
+
json = JSON.generate(request_token)
|
131
|
+
@@store.put(
|
132
|
+
"/cloudkit_oauth_request_tokens/#{request.last_path_element}",
|
133
|
+
:etag => request_token_response.etag,
|
134
|
+
:json => json)
|
135
|
+
erb(request, :authorize_request_token)
|
136
|
+
end
|
137
|
+
|
138
|
+
def deny_request_token(request)
|
139
|
+
return login_redirect(request) unless request.current_user
|
140
|
+
|
141
|
+
request_token_response = @@store.get(
|
142
|
+
"/cloudkit_oauth_request_tokens/#{request.last_path_element}")
|
143
|
+
@@store.delete(
|
144
|
+
"/cloudkit_oauth_request_tokens/#{request.last_path_element}",
|
145
|
+
:etag => request_token_response.etag)
|
146
|
+
erb(request, :request_token_denied)
|
147
|
+
end
|
148
|
+
|
149
|
+
def create_access_token(request)
|
150
|
+
return challenge(request, 'invalid nonce') unless valid_nonce?(request)
|
151
|
+
|
152
|
+
consumer_response = @@store.get(
|
153
|
+
"/cloudkit_oauth_consumers/#{request[:oauth_consumer_key]}")
|
154
|
+
unless consumer_response.status == 200
|
155
|
+
return challenge(request, 'invalid consumer')
|
156
|
+
end
|
157
|
+
|
158
|
+
consumer = consumer_response.parsed_content
|
159
|
+
request_token_response = @@store.get(
|
160
|
+
"/cloudkit_oauth_request_tokens/#{request[:oauth_token]}")
|
161
|
+
unless request_token_response.status == 200
|
162
|
+
return challenge(request, 'invalid request token')
|
163
|
+
end
|
164
|
+
|
165
|
+
request_token = request_token_response.parsed_content
|
166
|
+
unless request_token['consumer_key'] == request[:oauth_consumer_key]
|
167
|
+
return challenge(request, 'invalid consumer')
|
168
|
+
end
|
169
|
+
|
170
|
+
signature = OAuth::Signature.build(request) do
|
171
|
+
[request_token['secret'], consumer['secret']]
|
172
|
+
end
|
173
|
+
unless signature.verify
|
174
|
+
return challenge(request, 'invalid signature')
|
175
|
+
end
|
176
|
+
|
177
|
+
token_id, secret = OAuth::Server.new(request.host).generate_credentials
|
178
|
+
token_data = JSON.generate(
|
179
|
+
:secret => secret,
|
180
|
+
:consumer_key => request[:oauth_consumer_key],
|
181
|
+
:consumer_secret => consumer['secret'],
|
182
|
+
:user_id => request_token['user_id'])
|
183
|
+
@@store.put(
|
184
|
+
"/cloudkit_oauth_tokens/#{token_id}",
|
185
|
+
:json => token_data)
|
186
|
+
@@store.delete(
|
187
|
+
"/cloudkit_oauth_request_tokens/#{request[:oauth_token]}",
|
188
|
+
:etag => request_token_response.etag)
|
189
|
+
[201, {}, ["oauth_token=#{token_id}&oauth_token_secret=#{secret}"]]
|
190
|
+
end
|
191
|
+
|
192
|
+
def inject_user_or_challenge(request)
|
193
|
+
unless valid_nonce?(request)
|
194
|
+
request.current_user = ''
|
195
|
+
inject_challenge(request)
|
196
|
+
return
|
197
|
+
end
|
198
|
+
|
199
|
+
result = @@store.get("/cloudkit_oauth_tokens/#{request[:oauth_token]}")
|
200
|
+
access_token = result.parsed_content
|
201
|
+
signature = OAuth::Signature.build(request) do
|
202
|
+
[access_token['secret'], access_token['consumer_secret']]
|
203
|
+
end
|
204
|
+
if signature.verify
|
205
|
+
request.current_user = access_token['user_id']
|
206
|
+
else
|
207
|
+
request.current_user = ''
|
208
|
+
inject_challenge(request)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def valid_nonce?(request)
|
213
|
+
timestamp = request[:oauth_timestamp]
|
214
|
+
nonce = request[:oauth_nonce]
|
215
|
+
return false unless (timestamp && nonce)
|
216
|
+
|
217
|
+
uri = "/cloudkit_oauth_nonces/#{timestamp},#{nonce}"
|
218
|
+
result = @@store.put(uri, :json => '{}')
|
219
|
+
return false unless result.status == 201
|
220
|
+
|
221
|
+
true
|
222
|
+
end
|
223
|
+
|
224
|
+
def inject_challenge(request)
|
225
|
+
request.env[challenge_key] = challenge_headers(request)
|
226
|
+
end
|
227
|
+
|
228
|
+
def challenge(request, message)
|
229
|
+
[401, challenge_headers(request), [message || '']]
|
230
|
+
end
|
231
|
+
|
232
|
+
def challenge_headers(request)
|
233
|
+
{
|
234
|
+
'WWW-Authenticate' => "OAuth realm=\"http://#{request.env['HTTP_HOST']}\"",
|
235
|
+
'Link' => discovery_link(request)
|
236
|
+
}
|
237
|
+
end
|
238
|
+
|
239
|
+
def discovery_link(request)
|
240
|
+
"<#{request.scheme}://#{request.env['HTTP_HOST']}/oauth/meta>; rel=\"http://oauth.net/discovery/1.0/rel/provider\""
|
241
|
+
end
|
242
|
+
|
243
|
+
def login_redirect(request)
|
244
|
+
request.session['return_to'] = request.url if request.session
|
245
|
+
[302, {'Location' => request.login_url}, []]
|
246
|
+
end
|
247
|
+
|
248
|
+
def load_user_from_session(request)
|
249
|
+
request.current_user = request.session['user_uri'] if request.session
|
250
|
+
end
|
251
|
+
|
252
|
+
def get_meta(request)
|
253
|
+
# Expected in next OAuth Discovery Draft
|
254
|
+
erb(request, :oauth_meta)
|
255
|
+
end
|
256
|
+
|
257
|
+
def oauth_disco_draft2_xrds?(request)
|
258
|
+
# Current OAuth Discovery Draft 2 / XRDS-Simple 1.0, Section 5.1.2
|
259
|
+
request.get? &&
|
260
|
+
request.env['HTTP_ACCEPT'] &&
|
261
|
+
request.env['HTTP_ACCEPT'].match(/application\/xrds\+xml/)
|
262
|
+
end
|
263
|
+
|
264
|
+
def xrds_location(request)
|
265
|
+
# Current OAuth Discovery Draft 2 / XRDS-Simple 1.0, Section 5.1.2
|
266
|
+
[200, {'X-XRDS-Location' => "#{request.scheme}://#{request.env['HTTP_HOST']}/oauth"}, []]
|
267
|
+
end
|
268
|
+
|
269
|
+
def get_descriptor(request)
|
270
|
+
erb(request, :oauth_descriptor, {'Content-Type' => 'application/xrds+xml'})
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module CloudKit
|
2
|
+
|
3
|
+
# An OAuthStore is a thin abstraction around CloudKit::Store, providing
|
4
|
+
# consistent collection names, and allowing automatic migrations in later
|
5
|
+
# releases if needed.
|
6
|
+
class OAuthStore
|
7
|
+
@@store = nil
|
8
|
+
|
9
|
+
# Initialize a Store for use with OAuth middleware. Load the static consumer
|
10
|
+
# resource if it does not exist.
|
11
|
+
def initialize(uri=nil)
|
12
|
+
@@store = Store.new(
|
13
|
+
:collections => [
|
14
|
+
:cloudkit_oauth_nonces,
|
15
|
+
:cloudkit_oauth_tokens,
|
16
|
+
:cloudkit_oauth_request_tokens,
|
17
|
+
:cloudkit_oauth_consumers],
|
18
|
+
:adapter => SQLAdapter.new(uri)) unless @@store
|
19
|
+
load_static_consumer
|
20
|
+
end
|
21
|
+
|
22
|
+
def get(uri, options={}) #:nodoc:
|
23
|
+
@@store.get(uri, options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def post(uri, options={}) #:nodoc:
|
27
|
+
@@store.post(uri, options)
|
28
|
+
end
|
29
|
+
|
30
|
+
def put(uri, options={}) #:nodoc:
|
31
|
+
@@store.put(uri, options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def delete(uri, options={}) #:nodoc:
|
35
|
+
@@store.delete(uri, options)
|
36
|
+
end
|
37
|
+
|
38
|
+
def resolve_uris(uris) #:nodoc:
|
39
|
+
@@store.resolve_uris(uris)
|
40
|
+
end
|
41
|
+
|
42
|
+
def reset! #:nodoc:
|
43
|
+
@@store.reset!
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return the version number for this store.
|
47
|
+
def version; 1; end
|
48
|
+
|
49
|
+
# Load the static consumer entity if it does not already exist.
|
50
|
+
# See the OAuth Discovery spec for more info on static consumers.
|
51
|
+
def load_static_consumer
|
52
|
+
json = JSON.generate(:secret => '')
|
53
|
+
@@store.put('/cloudkit_oauth_consumers/cloudkitconsumer', :json => json)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|