databasedotcom-oauth2 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +81 -0
- data/MIT-LICENSE +20 -0
- data/README.md +76 -0
- data/Rakefile +12 -0
- data/databasedotcom-oauth2.gemspec +28 -0
- data/lib/databasedotcom-oauth2/version.rb +5 -0
- data/lib/databasedotcom-oauth2.rb +366 -0
- metadata +198 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
databasedotcom-oauth2 (0.0.5)
|
5
|
+
addressable
|
6
|
+
databasedotcom
|
7
|
+
oauth2
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: http://rubygems.org/
|
11
|
+
specs:
|
12
|
+
addressable (2.2.8)
|
13
|
+
crack (0.3.1)
|
14
|
+
databasedotcom (1.3.0)
|
15
|
+
json
|
16
|
+
multipart-post (~> 1.1)
|
17
|
+
diff-lcs (1.1.3)
|
18
|
+
faraday (0.8.1)
|
19
|
+
multipart-post (~> 1.1)
|
20
|
+
ffi (1.0.11)
|
21
|
+
growl (1.0.3)
|
22
|
+
guard (1.1.1)
|
23
|
+
listen (>= 0.4.2)
|
24
|
+
thor (>= 0.14.6)
|
25
|
+
guard-bundler (0.1.3)
|
26
|
+
bundler (>= 1.0.0)
|
27
|
+
guard (>= 0.2.2)
|
28
|
+
guard-rspec (1.0.0)
|
29
|
+
guard (>= 1.1)
|
30
|
+
httpauth (0.1)
|
31
|
+
json (1.7.3)
|
32
|
+
listen (0.4.4)
|
33
|
+
rb-fchange (~> 0.0.5)
|
34
|
+
rb-fsevent (~> 0.9.1)
|
35
|
+
rb-inotify (~> 0.8.8)
|
36
|
+
multi_json (1.3.6)
|
37
|
+
multipart-post (1.1.5)
|
38
|
+
oauth2 (0.7.1)
|
39
|
+
faraday (~> 0.8)
|
40
|
+
httpauth (~> 0.1)
|
41
|
+
multi_json (~> 1.0)
|
42
|
+
rack (~> 1.4)
|
43
|
+
rack (1.4.1)
|
44
|
+
rack-test (0.6.1)
|
45
|
+
rack (>= 1.0)
|
46
|
+
rb-fchange (0.0.5)
|
47
|
+
ffi
|
48
|
+
rb-fsevent (0.9.1)
|
49
|
+
rb-inotify (0.8.8)
|
50
|
+
ffi (>= 0.5.0)
|
51
|
+
rspec (2.10.0)
|
52
|
+
rspec-core (~> 2.10.0)
|
53
|
+
rspec-expectations (~> 2.10.0)
|
54
|
+
rspec-mocks (~> 2.10.0)
|
55
|
+
rspec-core (2.10.1)
|
56
|
+
rspec-expectations (2.10.0)
|
57
|
+
diff-lcs (~> 1.1.3)
|
58
|
+
rspec-mocks (2.10.1)
|
59
|
+
simplecov (0.6.4)
|
60
|
+
multi_json (~> 1.0)
|
61
|
+
simplecov-html (~> 0.5.3)
|
62
|
+
simplecov-html (0.5.3)
|
63
|
+
thor (0.15.2)
|
64
|
+
webmock (1.8.7)
|
65
|
+
addressable (>= 2.2.7)
|
66
|
+
crack (>= 0.1.7)
|
67
|
+
|
68
|
+
PLATFORMS
|
69
|
+
ruby
|
70
|
+
|
71
|
+
DEPENDENCIES
|
72
|
+
databasedotcom-oauth2!
|
73
|
+
growl
|
74
|
+
guard
|
75
|
+
guard-bundler
|
76
|
+
guard-rspec
|
77
|
+
rack-test
|
78
|
+
rb-fsevent
|
79
|
+
rspec (~> 2.7)
|
80
|
+
simplecov
|
81
|
+
webmock
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
# databasedotcom-oauth2
|
2
|
+
|
3
|
+
Rack Middleware for OAuth2 authentication against, and interaction with salesforce.com via the databasedotcom gem.
|
4
|
+
|
5
|
+
### Who's it for?
|
6
|
+
|
7
|
+
RubyDevelopers of
|
8
|
+
Whereas OmniAuth only provides authentication, this Rack Middleware assumes you lso instantiates a Databasedotcom::Client while providing an
|
9
|
+
|
10
|
+
authentication and you need to query or manipulate salesforce.com data in addition to authentication.
|
11
|
+
|
12
|
+
### Benefits:
|
13
|
+
|
14
|
+
* Hides OAuth2 hand-shake complexity against multiple salesforce.com endpoints (prod vs sandbox) including support for My Domain.
|
15
|
+
* Configurable/override-able options for scope, display, immediate
|
16
|
+
* OAuth2 Token encrypted and stored in session, supports any Rack:Session type - Cookie, Pool, etc.
|
17
|
+
* Materializes Databasedotcom::Client from token upon each request
|
18
|
+
* Databasedotcom::OAuth2::Helpers mixin provides convenience methods client, me, etc.
|
19
|
+
|
20
|
+
## Demos
|
21
|
+
|
22
|
+
<a href="https://db-oauth2-sinatra-basic.herokuapp.com" target="_blank">Sinatra Basic</a><a href="https://github.com/richardvanhook/databasedotcom-oauth2-sinatra-basic" target="_blank">(source)</a>
|
23
|
+
|
24
|
+
<a href="https://db-oauth2-sinatra-jqm.herokuapp.com" target="_blank">Sinatra showing authentication options along with JQuery Mobile</a><a href="https://github.com/richardvanhook/databasedotcom-oauth2-sinatra-jqm" target="_blank">(source)</a>
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
### Required
|
29
|
+
|
30
|
+
`:token_encryption_key` & `:endpoints` are required. databasedotcom-oauth2 encrypts oauth2 token using `:token_encryption_key` and stores it in rack.session for further use. `:endpoints` defines the server endpoints to be available; multiple can be specified but at least one is required.
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
use Databasedotcom::OAuth2::WebServerFlow,
|
34
|
+
:token_encryption_key => TOKEN_ENCRYPTION_KEY,
|
35
|
+
:endpoints => {"login.salesforce.com" => {:keys => CLIENT_ID, :secret => CLIENT_SECRET}}
|
36
|
+
```
|
37
|
+
|
38
|
+
### Multiple Endpoints
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
use Databasedotcom::OAuth2::WebServerFlow,
|
42
|
+
:endpoints => {"login.salesforce.com" => {:keys => CLIENT_ID1, :secret => CLIENT_SECRET1},
|
43
|
+
"test.salesforce.com" => {:keys => CLIENT_ID2, :secret => CLIENT_SECRET2}}
|
44
|
+
```
|
45
|
+
### Authentication Options
|
46
|
+
```ruby
|
47
|
+
use Databasedotcom::OAuth2::WebServerFlow,
|
48
|
+
:scope => "full", #default is "id api refresh_token"
|
49
|
+
:display => "touch", #default is "page"
|
50
|
+
:immediate => true #default is false
|
51
|
+
:scope_override => true, #default is false
|
52
|
+
:display_override => true, #default is false
|
53
|
+
:immediate_override => true, #default is false
|
54
|
+
```
|
55
|
+
|
56
|
+
## Parameters
|
57
|
+
|
58
|
+
### `:endpoints`
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
### `:token_encryption_key`
|
63
|
+
|
64
|
+
It's uber important that `:token_encryption_key` is sufficiently strong. To generate a sufficiently strong key, run following:
|
65
|
+
|
66
|
+
$ ruby -ropenssl -rbase64 -e "puts Base64.strict_encode64(OpenSSL::Random.random_bytes(16).to_str)"
|
67
|
+
|
68
|
+
Then, in your code, decrypt prior using:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
Base64.strict_decode64(TOKEN_ENCRYPTION_KEY)
|
72
|
+
```
|
73
|
+
|
74
|
+
## Resources
|
75
|
+
|
76
|
+
* [Article: Digging Deeper into OAuth 2.0 on Force.com](http://wiki.developerforce.com/index.php/Digging_Deeper_into_OAuth_2.0_on_Force.com)
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/databasedotcom-oauth2/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Richard Vanhook"]
|
6
|
+
gem.email = ["rvanhook@salesforce.com"]
|
7
|
+
gem.description = %q{OAuth2 Rack Middleware for database.com/salesforce.com.}
|
8
|
+
gem.summary = %q{OAuth2 Rack Middleware for database.com/salesforce.com.}
|
9
|
+
gem.homepage = "https://github.com/richardvanhook/databasedotcom-oauth2"
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "databasedotcom-oauth2"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Databasedotcom::OAuth2::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency 'addressable'
|
19
|
+
gem.add_dependency 'hashie'
|
20
|
+
gem.add_dependency 'gibberish'
|
21
|
+
gem.add_dependency 'databasedotcom'
|
22
|
+
gem.add_dependency 'oauth2'
|
23
|
+
|
24
|
+
gem.add_development_dependency 'rspec', '~> 2.7'
|
25
|
+
gem.add_development_dependency 'rack-test'
|
26
|
+
gem.add_development_dependency 'simplecov'
|
27
|
+
gem.add_development_dependency 'webmock'
|
28
|
+
end
|
@@ -0,0 +1,366 @@
|
|
1
|
+
require "cgi"
|
2
|
+
require "base64"
|
3
|
+
require "openssl"
|
4
|
+
require "addressable/uri"
|
5
|
+
require "hashie"
|
6
|
+
require "gibberish"
|
7
|
+
require "databasedotcom"
|
8
|
+
require "oauth2"
|
9
|
+
|
10
|
+
module OAuth2
|
11
|
+
class AccessToken
|
12
|
+
attr_accessor :client
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module Databasedotcom
|
17
|
+
|
18
|
+
class Client
|
19
|
+
def self.from_token(token, api_version)
|
20
|
+
client = nil
|
21
|
+
unless token.nil?
|
22
|
+
client = self.new({
|
23
|
+
:client_id => token.client.id,
|
24
|
+
:client_secret => token.client.secret,
|
25
|
+
:host => token.client.site
|
26
|
+
})
|
27
|
+
m = token["id"].match(/\/id\/([^\/]+)\/([^\/]+)$/)
|
28
|
+
client.org_id = m[1] rescue nil
|
29
|
+
client.user_id = m[2] rescue nil
|
30
|
+
client.version = api_version
|
31
|
+
client.instance_url = token.client.site
|
32
|
+
client.oauth_token = token.token
|
33
|
+
client.refresh_token = token.refresh_token
|
34
|
+
end
|
35
|
+
client
|
36
|
+
end
|
37
|
+
|
38
|
+
def org_id=(val)
|
39
|
+
@org_id = val
|
40
|
+
end
|
41
|
+
|
42
|
+
def user_id=(val)
|
43
|
+
@user_id = val
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
module OAuth2
|
49
|
+
TOKEN_KEY = "databasedotcom.token"
|
50
|
+
CLIENT_KEY = "databasedotcom.client"
|
51
|
+
|
52
|
+
module Helpers
|
53
|
+
def client
|
54
|
+
env['databasedotcom.client']
|
55
|
+
end
|
56
|
+
|
57
|
+
def token
|
58
|
+
env['databasedotcom.token']
|
59
|
+
end
|
60
|
+
|
61
|
+
def unauthenticated?
|
62
|
+
client.nil?
|
63
|
+
end
|
64
|
+
|
65
|
+
def authenticated?
|
66
|
+
!unauthenticated?
|
67
|
+
end
|
68
|
+
|
69
|
+
def me
|
70
|
+
@me ||= ::Hashie::Mash.new(Databasedotcom::Chatter::User.find(client, "me").raw_hash)
|
71
|
+
#@me.organization_id
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class WebServerFlow
|
76
|
+
|
77
|
+
def initialize(app, options = nil)
|
78
|
+
@app = app
|
79
|
+
unless options.nil?
|
80
|
+
@endpoints = self.class.sanitize_endpoints(options[:endpoints])
|
81
|
+
@token_encryption_key = options[:token_encryption_key]
|
82
|
+
@path_prefix = options[:path_prefix]
|
83
|
+
@on_failure = options[:on_failure]
|
84
|
+
@scope = options[:scope]
|
85
|
+
@display = options[:display]
|
86
|
+
@immediate = options[:immediate]
|
87
|
+
@scope_override = options[:scope_override] || false
|
88
|
+
@display_override = options[:display_override] || false
|
89
|
+
@immediate_override = options[:immediate_override] || false
|
90
|
+
@api_version = options[:api_version] || "24.0"
|
91
|
+
end
|
92
|
+
|
93
|
+
fail "\n\ndatabasedotcom-oauth2 initialization error! :endpoints parameter " \
|
94
|
+
+ "is invalid. Do something like this:\n\nuse Databasedotcom::OAuth2::Web" \
|
95
|
+
+ "ServerFlow, :endpoints => {\"login.salesforce.com\" => { :key => CLIENT" \
|
96
|
+
+ "_ID_FROM_DATABASEDOTCOM, :secret => CLIENT_SECRET_FROM_DATABASEDOTCOM }" \
|
97
|
+
+ "}\n\n" \
|
98
|
+
if !@endpoints.is_a?(Hash) || @endpoints.empty?
|
99
|
+
|
100
|
+
fail "\n\ndatabasedotcom-oauth2 initialization error! :token_encryption_key " \
|
101
|
+
+ "is invalid. Do something like this:\n\nuse Databasedotcom::OAuth2::WebS" \
|
102
|
+
+ "erverFlow, :token_encryption_key => YOUR_VERY_LONG_VERY_RANDOM_SECRET_KE" \
|
103
|
+
+ "Y_HERE\n\nTo generate a sufficiently long random key, use following comm" \
|
104
|
+
+ "and:\n\n$ ruby -ropenssl -rbase64 -e \"puts Base64.strict_encode64(OpenS" \
|
105
|
+
+ "SL::Random.random_bytes(16).to_str)\"\n\n" \
|
106
|
+
if @token_encryption_key.nil? || @token_encryption_key.size < 16
|
107
|
+
|
108
|
+
@path_prefix = "/auth/salesforce" unless @path_prefix.is_a?(String) && !@path_prefix.strip.empty?
|
109
|
+
@on_failure = nil unless @on_failure.is_a?(Proc)
|
110
|
+
end
|
111
|
+
|
112
|
+
def call(env)
|
113
|
+
dup.call!(env)
|
114
|
+
end
|
115
|
+
|
116
|
+
def call!(env)
|
117
|
+
@env = env
|
118
|
+
begin
|
119
|
+
return authorize_call if on_authorize_path?
|
120
|
+
return callback_call if on_callback_path?
|
121
|
+
materialize_token_and_client_from_session_if_present
|
122
|
+
rescue Exception => e
|
123
|
+
self.class._log_exception(e)
|
124
|
+
if @on_failure.nil?
|
125
|
+
new_path = Addressable::URI.parse(@path_prefix + "/failure")
|
126
|
+
new_path.query_values={:message => e.message, :state => request.params['state']}
|
127
|
+
return [302, {'Location' => new_path.to_s, 'Content-Type'=> 'text/html'}, []]
|
128
|
+
else
|
129
|
+
return @on_failure.call(env,e)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
@app.call(env)
|
133
|
+
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def on_authorize_path?
|
138
|
+
on_path?(@path_prefix)
|
139
|
+
end
|
140
|
+
|
141
|
+
def authorize_call
|
142
|
+
#determine endpoint via param; but if blank, use default
|
143
|
+
endpoint = request.params["endpoint"] #get endpoint from http param
|
144
|
+
keys = @endpoints[endpoint] #if endpoint not found, default will be used
|
145
|
+
endpoint = @endpoints.invert[keys] #re-lookup endpoint in case original param was bogus
|
146
|
+
mydomain = self.class.sanitize_mydomain(request.params["mydomain"])
|
147
|
+
|
148
|
+
#add endpoint to relay state so callback knows which keys to use
|
149
|
+
request.params["state"] ||= "/"
|
150
|
+
state = Addressable::URI.parse(request.params["state"])
|
151
|
+
state.query_values={} unless state.query_values
|
152
|
+
state.query_values= state.query_values.merge({:endpoint => endpoint})
|
153
|
+
|
154
|
+
#build params hash to be passed to ouath2 authorize redirect url
|
155
|
+
auth_params = {
|
156
|
+
:redirect_uri => "#{full_host}#{@path_prefix}/callback",
|
157
|
+
:state => state.to_s
|
158
|
+
}
|
159
|
+
auth_params[:scope] = @scope unless @scope.nil? || @scope.strip.empty?
|
160
|
+
auth_params[:display] = @display unless @display.nil?
|
161
|
+
auth_params[:immediate] = @immediate unless @immediate.nil?
|
162
|
+
|
163
|
+
#overrides
|
164
|
+
overrides = {}
|
165
|
+
if @scope_override
|
166
|
+
scope = (self.class.param_repeated(request.url, :scope) || []).join(" ")
|
167
|
+
overrides[:scope] = scope unless scope.nil? || scope.strip.empty?
|
168
|
+
end
|
169
|
+
overrides[:display] = request.params["display"] unless !@display_override || request.params["display"].nil?
|
170
|
+
overrides[:immediate] = request.params["immediate"] unless !@immediate_override || request.params["immediate"].nil?
|
171
|
+
auth_params.merge!(overrides)
|
172
|
+
|
173
|
+
#do redirect
|
174
|
+
redirect client(mydomain || endpoint, keys[:key], keys[:secret]).auth_code.authorize_url(auth_params)
|
175
|
+
end
|
176
|
+
|
177
|
+
def on_callback_path?
|
178
|
+
on_path?(@path_prefix + "/callback")
|
179
|
+
end
|
180
|
+
|
181
|
+
def callback_call
|
182
|
+
#check for error
|
183
|
+
callback_error = request.params["error"]
|
184
|
+
callback_error_details = request.params["error_description"]
|
185
|
+
fail "#{callback_error} #{callback_error_details}" unless callback_error.nil? || callback_error.strip.empty?
|
186
|
+
|
187
|
+
#grab authorization code
|
188
|
+
code = request.params["code"]
|
189
|
+
#grab and remove endpoint from relay state
|
190
|
+
#upon successful retrieval of token, state is url where user will be redirected to
|
191
|
+
request.params["state"] ||= "/"
|
192
|
+
state = Addressable::URI.parse(request.params["state"])
|
193
|
+
state.query_values= {} if state.query_values.nil?
|
194
|
+
state_params = state.query_values.dup
|
195
|
+
endpoint = state_params.delete("endpoint")
|
196
|
+
keys = @endpoints[endpoint]
|
197
|
+
state.query_values= state_params
|
198
|
+
state = state.to_s
|
199
|
+
state.sub!(/\?$/,"") unless state.nil?
|
200
|
+
|
201
|
+
#do callout to retrieve token
|
202
|
+
access_token = client(endpoint, keys[:key], keys[:secret]).auth_code.get_token(code,
|
203
|
+
:redirect_uri => "#{full_host}#{@path_prefix}/callback")
|
204
|
+
access_token.options[:mode] = :query
|
205
|
+
access_token.options[:param_name] = :oauth_token
|
206
|
+
access_token.options[:endpoint] = endpoint
|
207
|
+
access_token.client = nil
|
208
|
+
|
209
|
+
#populate session with serialized, encrypted token
|
210
|
+
#will be used later to materialize actual token and databasedotcom client handle
|
211
|
+
set_session_token(encrypt(access_token))
|
212
|
+
redirect state.to_str
|
213
|
+
end
|
214
|
+
|
215
|
+
def materialize_token_and_client_from_session_if_present
|
216
|
+
access_token = decrypt(session_token) unless session_token.nil? rescue nil
|
217
|
+
unless access_token.nil?
|
218
|
+
instance_url = access_token.params["instance_url"]
|
219
|
+
endpoint = access_token.options[:endpoint]
|
220
|
+
keys = @endpoints[endpoint]
|
221
|
+
access_token.client = client(instance_url, keys[:key], keys[:secret])
|
222
|
+
unless keys.nil?
|
223
|
+
@env[TOKEN_KEY] = access_token
|
224
|
+
@env[CLIENT_KEY] = ::Databasedotcom::Client.from_token(@env[TOKEN_KEY],@api_version)
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def session
|
230
|
+
@env["rack.session"] ||= {} #in case session is nil
|
231
|
+
@env["rack.session"]
|
232
|
+
end
|
233
|
+
|
234
|
+
def session_token
|
235
|
+
session[TOKEN_KEY]
|
236
|
+
end
|
237
|
+
|
238
|
+
def set_session_token(value)
|
239
|
+
session[TOKEN_KEY] = value
|
240
|
+
end
|
241
|
+
|
242
|
+
def aes
|
243
|
+
Gibberish::AES.new(@token_encryption_key)
|
244
|
+
end
|
245
|
+
|
246
|
+
def encrypt(data)
|
247
|
+
aes.encrypt(Marshal.dump(data))
|
248
|
+
end
|
249
|
+
|
250
|
+
def decrypt(data)
|
251
|
+
Marshal.load(aes.decrypt(data))
|
252
|
+
end
|
253
|
+
|
254
|
+
def on_path?(path)
|
255
|
+
current_path.casecmp(path) == 0
|
256
|
+
end
|
257
|
+
|
258
|
+
def current_path
|
259
|
+
request.path_info.downcase.sub(/\/$/,'')
|
260
|
+
end
|
261
|
+
|
262
|
+
def query_string
|
263
|
+
request.query_string.empty? ? "" : "?#{request.query_string}"
|
264
|
+
end
|
265
|
+
|
266
|
+
def request
|
267
|
+
@request ||= Rack::Request.new(@env)
|
268
|
+
end
|
269
|
+
|
270
|
+
def full_host
|
271
|
+
full_host = ENV['ORIGIN']
|
272
|
+
if full_host.nil? || full_host.strip.empty?
|
273
|
+
full_host = URI.parse(request.url.gsub(/\?.*$/,''))
|
274
|
+
full_host.path = ''
|
275
|
+
full_host.query = nil
|
276
|
+
#sometimes the url is actually showing http inside rails because the other layers (like nginx) have handled the ssl termination.
|
277
|
+
full_host.scheme = 'https' if(request.env['HTTP_X_FORWARDED_PROTO'] == 'https')
|
278
|
+
full_host = full_host.to_s
|
279
|
+
end
|
280
|
+
full_host
|
281
|
+
end
|
282
|
+
|
283
|
+
def client(site, client_id, client_secret)
|
284
|
+
::OAuth2::Client.new(
|
285
|
+
client_id,
|
286
|
+
client_secret,
|
287
|
+
:site => "https://#{self.class.parse_domain(site)}",
|
288
|
+
:authorize_url => '/services/oauth2/authorize',
|
289
|
+
:token_url => '/services/oauth2/token'
|
290
|
+
)
|
291
|
+
end
|
292
|
+
|
293
|
+
def redirect(uri)
|
294
|
+
r = Rack::Response.new
|
295
|
+
r.write("Redirecting to #{uri}...")
|
296
|
+
r.redirect(uri)
|
297
|
+
r.finish
|
298
|
+
end
|
299
|
+
|
300
|
+
class << self
|
301
|
+
|
302
|
+
def _log_exception(exception)
|
303
|
+
STDERR.puts "\n\n#{exception.class} (#{exception.message}):\n " +
|
304
|
+
exception.backtrace.join("\n ") +
|
305
|
+
"\n\n"
|
306
|
+
end
|
307
|
+
|
308
|
+
def sanitize_mydomain(mydomain)
|
309
|
+
mydomain = parse_domain(mydomain)
|
310
|
+
mydomain = nil unless mydomain.nil? || !mydomain.strip.empty?
|
311
|
+
mydomain = mydomain.split(/\.my\.salesforce\.com/).first + ".my.salesforce.com" unless mydomain.nil?
|
312
|
+
mydomain
|
313
|
+
end
|
314
|
+
|
315
|
+
def sanitize_endpoints(endpoints = nil)
|
316
|
+
endpoints = {} unless endpoints.is_a?(Hash)
|
317
|
+
endpoints = endpoints.dup
|
318
|
+
endpoints.keep_if do |key,value|
|
319
|
+
value.is_a?(Hash) &&
|
320
|
+
value.has_key?(:key) &&
|
321
|
+
value.has_key?(:secret) &&
|
322
|
+
!value[:key].nil? &&
|
323
|
+
!value[:secret].nil? &&
|
324
|
+
!value[:key].empty? &&
|
325
|
+
!value[:secret].empty?
|
326
|
+
end
|
327
|
+
#set random default if default isn't already populated
|
328
|
+
if !endpoints.empty? && endpoints.default.nil?
|
329
|
+
endpoints.default = endpoints[endpoints.keys.first]
|
330
|
+
end
|
331
|
+
endpoints
|
332
|
+
end
|
333
|
+
|
334
|
+
def parse_domain(url = nil)
|
335
|
+
unless url.nil?
|
336
|
+
url = "https://" + url if (url =~ /http[s]?:\/\//).nil?
|
337
|
+
begin
|
338
|
+
url = Addressable::URI.parse(url)
|
339
|
+
rescue Addressable::URI::InvalidURIError
|
340
|
+
url = nil
|
341
|
+
end
|
342
|
+
url = url.host unless url.nil?
|
343
|
+
url.strip! unless url.nil?
|
344
|
+
end
|
345
|
+
url = nil if url && url.strip.empty?
|
346
|
+
url
|
347
|
+
end
|
348
|
+
|
349
|
+
def param_repeated(url = nil, param_name = nil)
|
350
|
+
return_value = nil
|
351
|
+
unless url.nil? || url.strip.empty? || param_name.nil?
|
352
|
+
url = Addressable::URI.parse(url)
|
353
|
+
param_name = param_name.to_s if param_name.is_a?(Symbol)
|
354
|
+
query_values = url.query_values(:notation => :flat_array)
|
355
|
+
unless query_values.nil? || query_values.empty?
|
356
|
+
return_value = query_values.select{|param| param.is_a?(Array) && param.size >= 2 && param[0] == param_name}.collect{|param| param[1]}
|
357
|
+
end
|
358
|
+
end
|
359
|
+
return_value
|
360
|
+
end
|
361
|
+
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
end
|
366
|
+
end
|
metadata
ADDED
@@ -0,0 +1,198 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: databasedotcom-oauth2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.7
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Richard Vanhook
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-26 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: addressable
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: hashie
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: gibberish
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: databasedotcom
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: oauth2
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: rspec
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '2.7'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '2.7'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: rack-test
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: simplecov
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: webmock
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
description: OAuth2 Rack Middleware for database.com/salesforce.com.
|
159
|
+
email:
|
160
|
+
- rvanhook@salesforce.com
|
161
|
+
executables: []
|
162
|
+
extensions: []
|
163
|
+
extra_rdoc_files: []
|
164
|
+
files:
|
165
|
+
- .gitignore
|
166
|
+
- Gemfile
|
167
|
+
- Gemfile.lock
|
168
|
+
- MIT-LICENSE
|
169
|
+
- README.md
|
170
|
+
- Rakefile
|
171
|
+
- databasedotcom-oauth2.gemspec
|
172
|
+
- lib/databasedotcom-oauth2.rb
|
173
|
+
- lib/databasedotcom-oauth2/version.rb
|
174
|
+
homepage: https://github.com/richardvanhook/databasedotcom-oauth2
|
175
|
+
licenses: []
|
176
|
+
post_install_message:
|
177
|
+
rdoc_options: []
|
178
|
+
require_paths:
|
179
|
+
- lib
|
180
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
181
|
+
none: false
|
182
|
+
requirements:
|
183
|
+
- - ! '>='
|
184
|
+
- !ruby/object:Gem::Version
|
185
|
+
version: '0'
|
186
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
187
|
+
none: false
|
188
|
+
requirements:
|
189
|
+
- - ! '>='
|
190
|
+
- !ruby/object:Gem::Version
|
191
|
+
version: '0'
|
192
|
+
requirements: []
|
193
|
+
rubyforge_project:
|
194
|
+
rubygems_version: 1.8.24
|
195
|
+
signing_key:
|
196
|
+
specification_version: 3
|
197
|
+
summary: OAuth2 Rack Middleware for database.com/salesforce.com.
|
198
|
+
test_files: []
|