opro 0.2.1.pre → 0.3.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +4 -2
- data/README.md +48 -1
- data/VERSION +1 -1
- data/app/controllers/opro/oauth/token_controller.rb +2 -2
- data/app/models/opro/oauth/auth_grant.rb +1 -1
- data/app/views/opro/oauth/docs/markdown/curl.md.erb +16 -2
- data/app/views/opro/oauth/docs/markdown/oauth.md.erb +9 -1
- data/app/views/opro/oauth/docs/markdown/quick_start.md.erb +5 -0
- data/lib/generators/templates/opro.rb +1 -1
- data/lib/opro.rb +27 -8
- data/lib/opro/controllers/application_controller_helper.rb +13 -2
- data/lib/opro/controllers/concerns/error_messages.rb +1 -1
- data/lib/opro/controllers/concerns/permissions.rb +2 -0
- data/opro.gemspec +2 -2
- data/test/integration/action_dispatch/oauth_flow_test.rb +15 -0
- data/test/integration/action_dispatch/password_token_test.rb +20 -0
- metadata +29 -29
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
-
## 0.
|
1
|
+
## 0.3.0
|
2
2
|
|
3
3
|
- Properly set attr_accessible for those apps that are requiring all attributes to be whitelisted.
|
4
|
-
|
4
|
+
- Allow access_token to be passed in header `curl -H "Authorization: token iAmAOaUthToken" http://localhost:3000`
|
5
|
+
- Default `config.allow_password_exchange' to true
|
6
|
+
- Allow multiple `find_user_for_auth` calls in setup to allow custom finders for facebook, etc.
|
5
7
|
|
6
8
|
## 0.2.0
|
7
9
|
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@ A production ready Rails Engine that turns your app into an [Oauth2](http://oaut
|
|
7
7
|
|
8
8
|
## Why would I use this?
|
9
9
|
|
10
|
-
Lets say you've built a Rails app, awesome. Now you want to build a mobile app on say, the iPhone
|
10
|
+
Lets say you've built a Rails app, awesome. Now you want to build a mobile app on say, the iPhone... cool. You start throwing around `#to_json` like nobody's business, but then you realize you need to authenticate users somehow. "Basic Auth!!", you exclaim, but then you realize that's not the most secure solution. You also realize that some users already signed up with Facebook & Twitter so they don't have a username/password combo. What ever shall you do?
|
11
11
|
|
12
12
|
Wouldn't it be great if we could have a token exchange where the user goes to a mobile web view and grants permission, and then we return back an auth token just like the big boys (Facebook, Twitter, *cough* Foursquare *cough*). With Opro, we can add this functionality pretty easily. We'll use your existing authentication strategy and provide some end integration points for your clients to use out of the box.
|
13
13
|
|
@@ -81,6 +81,12 @@ That should be all you need to do to get setup, congrats you're now able to auth
|
|
81
81
|
|
82
82
|
Opro comes with built in documentation, so if you start your server you can view them at [http://localhost:3000/oauth_docs](http://localhost:3000/oauth_docs). Or you can [view the guide](http://opro-demo.herokuapp.com/oauth_docs) on the example app. This guide will walk you through creating your first OAuth client application, giving access to that app as a logged in user, getting an access token for that user, and using that token to access the server as an authenticated user!
|
83
83
|
|
84
|
+
|
85
|
+
# Advanced Setup
|
86
|
+
|
87
|
+
Opro is simple by default, but easily configurable for a number of common use cases. Check out the options below.
|
88
|
+
|
89
|
+
|
84
90
|
## Custom Auth
|
85
91
|
|
86
92
|
If you're not using devise you can manually configure your own auth strategy. In the future I plan on adding more auth strategies, ping me or submit a pull request for your desired authentication scheme.
|
@@ -120,6 +126,47 @@ You can also skip permissions using `skip_oauth_permissions`. By default permiss
|
|
120
126
|
|
121
127
|
The result is expected to be true or false.
|
122
128
|
|
129
|
+
|
130
|
+
## Refresh Tokens
|
131
|
+
|
132
|
+
For added security you can require access_tokens be refreshed by client applications. This will help to mitigate risk of a leaked access_token, and enable an all around more secure system. This security comes at a price however, since implemeting the refresh_token functionality in a client can be quite difficult.
|
133
|
+
|
134
|
+
By default refresh tokens are disabled, you can enable them in your application and set the timeout period of the tokens by adding this line to your configuration.
|
135
|
+
|
136
|
+
|
137
|
+
config.require_refresh_within = 1.month
|
138
|
+
|
139
|
+
|
140
|
+
## Password Token Exchange
|
141
|
+
|
142
|
+
If a client application has a user's password and username/email they can exchange these for a token. This is much safer than storing username and password on a local device, but does not offer the traditional OAuth "Flow". Because of this all available permissions will be granted to the client application. If you want to disable this feature you can set the configuration below to false:
|
143
|
+
|
144
|
+
config.allow_password_exchange = true
|
145
|
+
|
146
|
+
If you have this feature enabled you can further control what applications can use the feature. Some providers may wish to have "Blessed" client applications that have this ability while restricting all other clients. To accomplish this you can create a method in your ApplicationController called `oauth_valid_password_auth?` that accepts a client_id and client_secret, and returns a true or false based on whether that application can use password auth
|
147
|
+
|
148
|
+
def oauth_valid_password_auth?(client_id, client_secret)
|
149
|
+
BLESSED_APP_IDS.include?(client_id)
|
150
|
+
end
|
151
|
+
|
152
|
+
|
153
|
+
If you are using this password functionality without a supported authorization engine (like devise), you will need to add an additional method that supports validating whether or not a user's credentials are valid. The method for this is called `find_user_for_auth` and accepts a controller and the parameters. The output is expected to be a user. Add this to your config like you did to the other required methods in the Custom Auth section.
|
154
|
+
|
155
|
+
config.find_user_for_auth do |controller, params|
|
156
|
+
# user = User.find(params[:something])
|
157
|
+
# return user.valid_password?(params[:password]) ? user : false
|
158
|
+
end
|
159
|
+
|
160
|
+
If you're authenticating exchanging something other than a password (such as a facebook auth token) client's can still enable this functionality by setting `params[:auth_grant] == 'password'` in their initial request. You can then use `find_user_for_auth` method from above and implement your custom behavior. You can call `find_user_for_auth` multiple times and the application will try calling each auth method in order. It is suggested that you return from this block early if the params are missing a vital key like this:
|
161
|
+
|
162
|
+
|
163
|
+
config.find_user_for_auth do |controller, params|
|
164
|
+
return false if params[:fb_token].blank?
|
165
|
+
User.where(:fb_token => params[:fb_token]).first
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
|
123
170
|
## Assumptions
|
124
171
|
|
125
172
|
* You have a user model and that is what your authenticating
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0.pre
|
@@ -20,8 +20,8 @@ class Opro::Oauth::TokenController < OproController
|
|
20
20
|
auth_grant = Opro::Oauth::AuthGrant.auth_with_code!(params[:code], application.id)
|
21
21
|
elsif params[:refresh_token]
|
22
22
|
auth_grant = Opro::Oauth::AuthGrant.refresh_tokens!(params[:refresh_token], application.id)
|
23
|
-
elsif params[:password] ||
|
24
|
-
user = ::Opro.
|
23
|
+
elsif params[:password] || params[:auth_grant] == "password"
|
24
|
+
user = ::Opro.find_user_for_all_auths!(self, params) if Opro.password_exchange_enabled? && oauth_valid_password_auth?(params[:client_id], params[:client_secret])
|
25
25
|
auth_grant = Opro::Oauth::AuthGrant.auth_with_user!(user, application.id) if user.present?
|
26
26
|
end
|
27
27
|
|
@@ -17,7 +17,7 @@ class Opro::Oauth::AuthGrant < ActiveRecord::Base
|
|
17
17
|
attr_accessible :code, :access_token, :refresh_token, :access_token_expires_at, :permissions, :user_id, :user, :application_id, :application
|
18
18
|
|
19
19
|
def can?(value)
|
20
|
-
permissions[value
|
20
|
+
HashWithIndifferentAccess.new(permissions)[value]
|
21
21
|
end
|
22
22
|
|
23
23
|
def expired?
|
@@ -6,6 +6,10 @@
|
|
6
6
|
|
7
7
|
With curl we're able to arbitrarily add parameters to our requests and to send using arbitrary HTTP status codes (GET/POST/DELETE) that are difficult to simulate in the browser. If you need to `POST` data to a url doing so with curl is much easier than constructing a form for testing.
|
8
8
|
|
9
|
+
# Hurl
|
10
|
+
|
11
|
+
[Hurl](http://hurl.it/) is an open sourced browser based `curl` implementation. If you're going to do quite a few curl requests, using it can be easier than the command line.
|
12
|
+
|
9
13
|
## How do I use it?
|
10
14
|
|
11
15
|
On the command line you should be able to get get help by typing `man curl` if your system supports man pages. Below are some simple and common use cases
|
@@ -32,11 +36,21 @@ You can ask for the headers of a request by adding the `-I` flag to a curl comma
|
|
32
36
|
X-Frame-Options: SAMEORIGIN
|
33
37
|
Transfer-Encoding: chunked
|
34
38
|
|
39
|
+
### Set Headers
|
40
|
+
|
41
|
+
You can set a request header by using `-H` for example if you wanted to send an access_token in a header you could issue this request (assuming your access token is '9693accessTokena7ca570bbaf')
|
42
|
+
|
43
|
+
$ curl -H "Authorization: token 9693accessTokena7ca570bbaf" "http://localhost:3000/oauth_test/show_me_the_money"
|
44
|
+
|
45
|
+
|
46
|
+
### HTTP Verb
|
47
|
+
|
48
|
+
You can specify the type of request you make in curl (GET, POST, PUT, DELETE, etc.) by specifying `-X`. For example if you wanted to POST to the /products url at localhost:3000/products you could do so like this:
|
49
|
+
|
50
|
+
$ curl -X POST http://localhost:3000
|
35
51
|
|
36
52
|
|
37
|
-
# Hurl
|
38
53
|
|
39
|
-
[Hurl](http://hurl.it/) is an open sourced browser based `curl` implementation. If you're going to do quite a few curl requests, using it can be easier than the command line.
|
40
54
|
|
41
55
|
|
42
56
|
|
@@ -9,6 +9,14 @@ OAuth is a secure way to grant authorization without having to transfer password
|
|
9
9
|
|
10
10
|
The flow is simple, it is started when a user clicks on an authorization button, they are then directed to the OAuth provider's website, such as Facebook. They are then prompted to confirm with the OAuth provider that they are who they say they are by logging in. The user is then given the opportunity to grant authorization to the OAuth client (where the request was initiated, such as the iPhone). After returning to the client, a code is sent that can be exchanged for a secure token. This secure token can be used to authenticate as the user. This way an iPhone client can ask for personalized content to show to the user, such as a friend list, or messages. This is the mechanism that drives most of the web.
|
11
11
|
|
12
|
+
|
13
|
+
## Watch "OAuth: A Tale of 2 Servers"
|
14
|
+
|
15
|
+
This video covers a typical flow an OAuth client follows with an OAuth provider for obtaining and using an access_token.
|
16
|
+
|
17
|
+
|
18
|
+
<object width="560" height="315"><param name="movie" value="http://www.youtube.com/v/tFYrq3d54Dc?version=3&hl=en_US&rel=0"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/tFYrq3d54Dc?version=3&hl=en_US&rel=0" type="application/x-shockwave-flash" width="560" height="315" allowscriptaccess="always" allowfullscreen="true"></embed></object>
|
19
|
+
|
12
20
|
## Not just Mobile
|
13
21
|
|
14
22
|
Client and server side web applications can use this type of authorization to add features to their service such as posting things to a timeline, or adding personalization.
|
@@ -16,7 +24,7 @@ Client and server side web applications can use this type of authorization to ad
|
|
16
24
|
|
17
25
|
## Alternatives
|
18
26
|
|
19
|
-
OAuth is simple in concept, but can be tricky to implement right. Many services also support basic auth. With basic auth you send a user's username and password along with every request. While this is fairly simple it means that the client application has access to your password, which is not very secure.
|
27
|
+
OAuth is simple in concept, but can be tricky to implement right. Many services also support basic auth. With basic auth you send a user's username and password along with every request. While this is fairly simple it means that the client application has access to your password, which is not very secure.
|
20
28
|
|
21
29
|
|
22
30
|
## Clients
|
@@ -63,6 +63,11 @@ Try it out for yourself open up a browser and go to
|
|
63
63
|
|
64
64
|
You should see a successful result ( again don't forget to replace the example access token with yours ). Not all urls will support OAuth authentication.
|
65
65
|
|
66
|
+
You can also use a header to pass the oauth token
|
67
|
+
|
68
|
+
<%= %Q{ curl -H "Authorization: token 9693accessTokena7ca570bbaf" "#{request.base_url}/oauth_test/show_me_the_money" } %>
|
69
|
+
|
70
|
+
|
66
71
|
## Security
|
67
72
|
|
68
73
|
Don't share your client application's secret or any user's access_token with unknown or untrusted parties. Always use https when available and don't write any of these values to your application's logs.
|
@@ -21,6 +21,6 @@ Opro.setup do |config|
|
|
21
21
|
# password, etc. for an access token.
|
22
22
|
# Caution: This bypasses the traditional OAuth flow
|
23
23
|
# as a result users cannot opt out of client permissions, all permissions are granted
|
24
|
-
config.allow_password_exchange =
|
24
|
+
config.allow_password_exchange = true
|
25
25
|
|
26
26
|
end
|
data/lib/opro.rb
CHANGED
@@ -27,14 +27,12 @@ module Opro
|
|
27
27
|
authenticate_user_method { |controller| controller.authenticate_user! }
|
28
28
|
|
29
29
|
find_user_for_auth do |controller, params|
|
30
|
+
return false if params[:password].blank?
|
30
31
|
find_params = params.each_with_object({}) {|(key,value), hash| hash[key] = value if Devise.authentication_keys.include?(key.to_sym) }
|
31
|
-
user = User.where(find_params).first
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
return_user = false
|
36
|
-
end
|
37
|
-
return_user
|
32
|
+
user = User.where(find_params).first if find_params.present?
|
33
|
+
return false unless user.present?
|
34
|
+
return false unless user.valid_password?(params[:password])
|
35
|
+
user
|
38
36
|
end
|
39
37
|
else
|
40
38
|
# nothing
|
@@ -107,9 +105,30 @@ module Opro
|
|
107
105
|
end
|
108
106
|
end
|
109
107
|
|
108
|
+
# calls all of the different auths made available,
|
109
|
+
def self.find_user_for_all_auths!(controller, params)
|
110
|
+
@user = false
|
111
|
+
find_user_for_auth.each do |auth_block|
|
112
|
+
break if @user.present?
|
113
|
+
@user = auth_block.call(controller, params)
|
114
|
+
end
|
115
|
+
@user
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
# Grossssss, don't use, needed to support `return` from the blocks provided to `find_user_for_auth`
|
120
|
+
def self.convert_to_lambda &block
|
121
|
+
obj = Object.new
|
122
|
+
obj.define_singleton_method(:_, &block)
|
123
|
+
return obj.method(:_).to_proc
|
124
|
+
end
|
125
|
+
|
126
|
+
# holds an Array of authentication blocks is called by find_user_for_all_auths! in token controller
|
127
|
+
# can be used for finding users using multiple methods (password, facebook, twitter, etc.)
|
110
128
|
def self.find_user_for_auth(&block)
|
111
129
|
if block.present?
|
112
|
-
@find_for_authentication
|
130
|
+
@find_for_authentication ||= []
|
131
|
+
@find_for_authentication << convert_to_lambda(&block)
|
113
132
|
else
|
114
133
|
@find_for_authentication or raise 'find_for_authentication not set, please specify Opro auth_strategy'
|
115
134
|
end
|
@@ -53,8 +53,19 @@ module Opro
|
|
53
53
|
@use_oauth = true
|
54
54
|
end
|
55
55
|
|
56
|
+
def oauth_access_token
|
57
|
+
params[:access_token] || oauth_access_token_from_header
|
58
|
+
end
|
59
|
+
|
60
|
+
def oauth_access_token_from_header
|
61
|
+
auth_header = request.env["HTTP_AUTHORIZATION"]||""
|
62
|
+
match = auth_header.match(/^token\s(.*)/)
|
63
|
+
return match[1] if match.present?
|
64
|
+
false
|
65
|
+
end
|
66
|
+
|
56
67
|
def oauth?
|
57
|
-
allow_oauth? &&
|
68
|
+
allow_oauth? && oauth_access_token.present?
|
58
69
|
end
|
59
70
|
|
60
71
|
# Override with custom logic to exclude or allow applications from exchanging
|
@@ -64,7 +75,7 @@ module Opro
|
|
64
75
|
end
|
65
76
|
|
66
77
|
def oauth_access_grant
|
67
|
-
@oauth_access_grant ||= Opro::Oauth::AuthGrant.find_for_token(
|
78
|
+
@oauth_access_grant ||= Opro::Oauth::AuthGrant.find_for_token(oauth_access_token)
|
68
79
|
end
|
69
80
|
|
70
81
|
def oauth_client_app
|
@@ -3,7 +3,7 @@ module Opro::Controllers::Concerns::ErrorMessages
|
|
3
3
|
|
4
4
|
def generate_oauth_error_message!
|
5
5
|
msg = ""
|
6
|
-
msg << ' - No OAuth Token Provided!' if
|
6
|
+
msg << ' - No OAuth Token Provided!' if oauth_access_token.blank?
|
7
7
|
msg << ' - Allow OAuth set to false!' if allow_oauth? == false
|
8
8
|
msg << ' - OAuth user not found!' if oauth_user.blank?
|
9
9
|
msg = generate_oauth_permissions_error_message!(msg)
|
@@ -31,6 +31,7 @@ module Opro::Controllers::Concerns::Permissions
|
|
31
31
|
# oauth_client_can_:method? so to over-write a default check for
|
32
32
|
# :write permission, you would need to define oauth_client_can_write?
|
33
33
|
def oauth_client_has_permissions?
|
34
|
+
return false unless oauth_access_grant.present?
|
34
35
|
permissions_valid_array = []
|
35
36
|
oauth_required_permissions.each do |permission|
|
36
37
|
permissions_valid_array << oauth_client_has_permission?(permission)
|
@@ -53,6 +54,7 @@ module Opro::Controllers::Concerns::Permissions
|
|
53
54
|
# Returns boolean
|
54
55
|
# if client has been granted write permissions or request is a 'GET' returns true
|
55
56
|
def oauth_client_can_write?
|
57
|
+
return false unless oauth_access_grant.present?
|
56
58
|
return true if env['REQUEST_METHOD'] == 'GET'
|
57
59
|
return true if oauth_access_grant.can?(:write)
|
58
60
|
false
|
data/opro.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "opro"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.3.0.pre"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["schneems"]
|
12
|
-
s.date = "2012-06-
|
12
|
+
s.date = "2012-06-30"
|
13
13
|
s.description = " Enable OAuth clients (iphone, android, web sites, etc.) to access and use your Rails application, what you do with it is up to you"
|
14
14
|
s.email = "richard.schneeman@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -30,5 +30,20 @@ class OauthTokenTest < ActionDispatch::IntegrationTest
|
|
30
30
|
assert json_hash["refresh_token"], auth_grant.refresh_token
|
31
31
|
end
|
32
32
|
|
33
|
+
|
34
|
+
test 'header authorization token' do
|
35
|
+
auth_grant = create_auth_grant_for_user(@user)
|
36
|
+
auth_grant.update_attributes(:permissions => {:write => true})
|
37
|
+
|
38
|
+
# curl -H "Authorization: token OAUTH-TOKEN" http://localhost:3000
|
39
|
+
# sets request.env["HTTP_AUTHORIZATION"] to "token OAUTH-TOKEN"
|
40
|
+
access_token = auth_grant.access_token
|
41
|
+
|
42
|
+
headers = {"HTTP_AUTHORIZATION" => "token #{access_token}"}
|
43
|
+
post oauth_tests_path, {}, headers
|
44
|
+
|
45
|
+
assert_equal 200, status
|
46
|
+
end
|
47
|
+
|
33
48
|
end
|
34
49
|
|
@@ -57,5 +57,25 @@ class PasswordTokenTest < ActionDispatch::IntegrationTest
|
|
57
57
|
assert json_hash['access_token'].blank?
|
58
58
|
end
|
59
59
|
|
60
|
+
|
61
|
+
test "Allow multiple definitions of find_user_for_auth (no password)" do
|
62
|
+
Opro.setup do |config|
|
63
|
+
config.find_user_for_auth do |controller, params|
|
64
|
+
return false if params[:special_key].blank?
|
65
|
+
user = User.last if params[:special_key] == "fooBarzyrhaz"
|
66
|
+
user
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
params = {:client_id => @client_app.client_id ,
|
71
|
+
:client_secret => @client_app.client_secret,
|
72
|
+
:special_key => "fooBarzyrhaz",
|
73
|
+
:auth_grant => 'password' }
|
74
|
+
|
75
|
+
post oauth_token_path(params)
|
76
|
+
json_hash = JSON.parse(response.body)
|
77
|
+
assert json_hash['access_token'].present?
|
78
|
+
end
|
79
|
+
|
60
80
|
end
|
61
81
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: opro
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0.pre
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-06-
|
12
|
+
date: 2012-06-30 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
16
|
-
requirement: &
|
16
|
+
requirement: &70196426967100 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: 3.1.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70196426967100
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rails
|
27
|
-
requirement: &
|
27
|
+
requirement: &70196426966240 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 3.1.0
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70196426966240
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: bluecloth
|
38
|
-
requirement: &
|
38
|
+
requirement: &70196426965380 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *70196426965380
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: mocha
|
49
|
-
requirement: &
|
49
|
+
requirement: &70196426964780 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: '0'
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *70196426964780
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: timecop
|
60
|
-
requirement: &
|
60
|
+
requirement: &70196426964140 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,10 +65,10 @@ dependencies:
|
|
65
65
|
version: '0'
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *70196426964140
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: jeweler
|
71
|
-
requirement: &
|
71
|
+
requirement: &70196426963520 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ~>
|
@@ -76,10 +76,10 @@ dependencies:
|
|
76
76
|
version: 1.6.4
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *70196426963520
|
80
80
|
- !ruby/object:Gem::Dependency
|
81
81
|
name: bundler
|
82
|
-
requirement: &
|
82
|
+
requirement: &70196426962980 !ruby/object:Gem::Requirement
|
83
83
|
none: false
|
84
84
|
requirements:
|
85
85
|
- - ! '>='
|
@@ -87,10 +87,10 @@ dependencies:
|
|
87
87
|
version: 1.1.3
|
88
88
|
type: :development
|
89
89
|
prerelease: false
|
90
|
-
version_requirements: *
|
90
|
+
version_requirements: *70196426962980
|
91
91
|
- !ruby/object:Gem::Dependency
|
92
92
|
name: capybara
|
93
|
-
requirement: &
|
93
|
+
requirement: &70196426962460 !ruby/object:Gem::Requirement
|
94
94
|
none: false
|
95
95
|
requirements:
|
96
96
|
- - ! '>='
|
@@ -98,10 +98,10 @@ dependencies:
|
|
98
98
|
version: 0.4.0
|
99
99
|
type: :development
|
100
100
|
prerelease: false
|
101
|
-
version_requirements: *
|
101
|
+
version_requirements: *70196426962460
|
102
102
|
- !ruby/object:Gem::Dependency
|
103
103
|
name: sqlite3
|
104
|
-
requirement: &
|
104
|
+
requirement: &70196426961860 !ruby/object:Gem::Requirement
|
105
105
|
none: false
|
106
106
|
requirements:
|
107
107
|
- - ! '>='
|
@@ -109,10 +109,10 @@ dependencies:
|
|
109
109
|
version: '0'
|
110
110
|
type: :development
|
111
111
|
prerelease: false
|
112
|
-
version_requirements: *
|
112
|
+
version_requirements: *70196426961860
|
113
113
|
- !ruby/object:Gem::Dependency
|
114
114
|
name: launchy
|
115
|
-
requirement: &
|
115
|
+
requirement: &70196426961260 !ruby/object:Gem::Requirement
|
116
116
|
none: false
|
117
117
|
requirements:
|
118
118
|
- - ! '>='
|
@@ -120,10 +120,10 @@ dependencies:
|
|
120
120
|
version: '0'
|
121
121
|
type: :development
|
122
122
|
prerelease: false
|
123
|
-
version_requirements: *
|
123
|
+
version_requirements: *70196426961260
|
124
124
|
- !ruby/object:Gem::Dependency
|
125
125
|
name: devise
|
126
|
-
requirement: &
|
126
|
+
requirement: &70196426960660 !ruby/object:Gem::Requirement
|
127
127
|
none: false
|
128
128
|
requirements:
|
129
129
|
- - ! '>='
|
@@ -131,10 +131,10 @@ dependencies:
|
|
131
131
|
version: '0'
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
|
-
version_requirements: *
|
134
|
+
version_requirements: *70196426960660
|
135
135
|
- !ruby/object:Gem::Dependency
|
136
136
|
name: rcov
|
137
|
-
requirement: &
|
137
|
+
requirement: &70196426960060 !ruby/object:Gem::Requirement
|
138
138
|
none: false
|
139
139
|
requirements:
|
140
140
|
- - ! '>='
|
@@ -142,10 +142,10 @@ dependencies:
|
|
142
142
|
version: '0'
|
143
143
|
type: :development
|
144
144
|
prerelease: false
|
145
|
-
version_requirements: *
|
145
|
+
version_requirements: *70196426960060
|
146
146
|
- !ruby/object:Gem::Dependency
|
147
147
|
name: simplecov
|
148
|
-
requirement: &
|
148
|
+
requirement: &70196426959380 !ruby/object:Gem::Requirement
|
149
149
|
none: false
|
150
150
|
requirements:
|
151
151
|
- - ! '>='
|
@@ -153,7 +153,7 @@ dependencies:
|
|
153
153
|
version: '0'
|
154
154
|
type: :development
|
155
155
|
prerelease: false
|
156
|
-
version_requirements: *
|
156
|
+
version_requirements: *70196426959380
|
157
157
|
description: ! ' Enable OAuth clients (iphone, android, web sites, etc.) to access
|
158
158
|
and use your Rails application, what you do with it is up to you'
|
159
159
|
email: richard.schneeman@gmail.com
|
@@ -275,7 +275,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
275
275
|
version: '0'
|
276
276
|
segments:
|
277
277
|
- 0
|
278
|
-
hash:
|
278
|
+
hash: 2196355823502899805
|
279
279
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
280
280
|
none: false
|
281
281
|
requirements:
|