databasedotcom-oauth2 0.1.7
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/.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: []
|