signet-rails 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +24 -0
- data/lib/signet/rails/builder.rb +36 -13
- data/lib/signet/rails/factory.rb +15 -9
- data/lib/signet/rails/handler.rb +45 -29
- data/lib/signet/rails/version.rb +1 -1
- data/lib/signet/rails/wrappers/active_record.rb +12 -12
- metadata +1 -1
data/README.md
CHANGED
@@ -28,6 +28,30 @@ $ gem install signet-rails
|
|
28
28
|
|
29
29
|
TODO: Write usage instructions here
|
30
30
|
|
31
|
+
## TODO
|
32
|
+
|
33
|
+
A list of items still todo or work in progress
|
34
|
+
|
35
|
+
0. Give an example usage
|
36
|
+
* Example of setting default options
|
37
|
+
* Example of setting per provider options
|
38
|
+
* Clearly document default `extract\_from\_env`
|
39
|
+
* Clearly document default `extract\_by\_oauth\_id`
|
40
|
+
1. Is there a better (Rails) way to creating per-request instances of signet OAuth clients?
|
41
|
+
2. Currently use the env variable to store the handlers and references to the instances... is this thread safe (probably) and the best way (probably not)?
|
42
|
+
3. Better way of sourcing the Google default authorization_uri and token_credential_uri? From signet directly?
|
43
|
+
4. Clear definition of the Signet options and the `signet-rails` options
|
44
|
+
5. More Rails-esque way of getting the `rack.session` in `extract\_from\_env`?
|
45
|
+
6. Better way of loading persistance wrappers in builder?
|
46
|
+
7. Check to see whether we have all required signet options at the end of Builder.provder?
|
47
|
+
8. Sort out `approval\_prompt` vs 'prompt'
|
48
|
+
9. Better `auth\_options` split at the end of Builder.provider?
|
49
|
+
10. Avoid having to dup options the whole time: fix signet?
|
50
|
+
11. Refactor Handler.handle code... messy
|
51
|
+
12. Document handling of callback in Rails
|
52
|
+
13. Error handling...
|
53
|
+
14. Document the various `env` values that can/will be set and when (e.g. `signet.XXX.persistance\_obj` on `auth\_callback`)
|
54
|
+
|
31
55
|
## Contributing
|
32
56
|
|
33
57
|
1. Fork it
|
data/lib/signet/rails/builder.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'signet/rails'
|
2
|
-
require '
|
2
|
+
require 'active_support/core_ext/string'
|
3
3
|
|
4
4
|
module Signet
|
5
5
|
module Rails
|
@@ -7,7 +7,7 @@ module Signet
|
|
7
7
|
@@default_options = {}
|
8
8
|
|
9
9
|
def self.set_default_options opts = {}
|
10
|
-
# normalize to symbol
|
10
|
+
# normalize to symbol hash
|
11
11
|
n_opts = opts.inject({}) { |memo,(k,v)| memo[k.to_sym] = v; memo }
|
12
12
|
@@default_options = n_opts
|
13
13
|
end
|
@@ -17,17 +17,31 @@ module Signet
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def provider(opts = {}, &block)
|
20
|
-
# normalize to symbol
|
20
|
+
# normalize to symbol hash
|
21
21
|
n_opts = opts.inject({}) { |memo,(k,v)| memo[k.to_sym] = v; memo }
|
22
|
+
|
23
|
+
# use the default_options as a base... then merge these changes on top
|
22
24
|
combined_options = @@default_options.merge(n_opts)
|
23
25
|
|
24
26
|
# now set some defaults if they aren't already set
|
25
|
-
|
27
|
+
|
28
|
+
combined_options[:persist_attrs] ||= [:refresh_token, :access_token, :expires_in, :issued_at]
|
26
29
|
combined_options[:name] ||= :google
|
30
|
+
|
31
|
+
# TODO: see https://developers.google.com/accounts/docs/OAuth2Login#authenticationuriparameters
|
27
32
|
combined_options[:approval_prompt] ||= 'auto'
|
28
|
-
|
29
|
-
|
30
|
-
combined_options[:
|
33
|
+
|
34
|
+
# unless specified, we need to set this at request-time because we need the env to get server etc
|
35
|
+
# combined_options[:redirect_uri] = ??? need env
|
36
|
+
|
37
|
+
# TODO: better way of sourcing these defaults... from signet?
|
38
|
+
combined_options[:authorization_uri] ||= 'https://accounts.google.com/o/oauth2/auth'
|
39
|
+
combined_options[:token_credential_uri] ||= 'https://accounts.google.com/o/oauth2/token'
|
40
|
+
|
41
|
+
# whether we handle the persistance of the auth callback or simply pass-through
|
42
|
+
combined_options[:handle_auth_callback] ||= true
|
43
|
+
|
44
|
+
# method to get the persistance object when creating a client via the factory
|
31
45
|
combined_options[:extract_from_env] ||= lambda do |env, client|
|
32
46
|
u = nil
|
33
47
|
session = env['rack.session']
|
@@ -39,7 +53,8 @@ module Signet
|
|
39
53
|
end
|
40
54
|
u
|
41
55
|
end
|
42
|
-
|
56
|
+
|
57
|
+
# when on an auth_callback, how do we get the persistance object from the id?
|
43
58
|
combined_options[:extract_by_oauth_id] ||= lambda do |id, client|
|
44
59
|
u = nil
|
45
60
|
begin
|
@@ -51,19 +66,27 @@ module Signet
|
|
51
66
|
u
|
52
67
|
end
|
53
68
|
|
54
|
-
|
69
|
+
combined_options[:persistance_wrapper] ||= :active_record
|
70
|
+
persistance_wrapper = lambda do |meth|
|
55
71
|
lambda do |context, client|
|
56
72
|
y = meth.call context, client
|
57
|
-
|
73
|
+
klass_str = combined_options[:persistance_wrapper].to_s
|
74
|
+
require "signet/rails/wrappers/#{klass_str}"
|
75
|
+
w = "Signet::Rails::Wrappers::#{klass_str.camelize}".constantize.new y, client
|
58
76
|
end
|
59
77
|
end
|
60
78
|
|
61
|
-
combined_options[:extract_by_oauth_id] =
|
62
|
-
combined_options[:extract_from_env] =
|
79
|
+
combined_options[:extract_by_oauth_id] = persistance_wrapper.call combined_options[:extract_by_oauth_id]
|
80
|
+
combined_options[:extract_from_env] = persistance_wrapper.call combined_options[:extract_from_env]
|
63
81
|
|
64
82
|
# TODO: check here we have the basics?
|
83
|
+
|
84
|
+
# TODO: better auth_options split?
|
85
|
+
auth_option_keys = [:prompt, :redirect_uri, :access_type, :approval_prompt, :client_id]
|
86
|
+
base_options = combined_options
|
87
|
+
auth_options = base_options.select { |k,v| auth_option_keys.include? k }
|
65
88
|
|
66
|
-
use Signet::Rails::Handler,
|
89
|
+
use Signet::Rails::Handler, base_options, auth_options, &block
|
67
90
|
end
|
68
91
|
|
69
92
|
def call(env)
|
data/lib/signet/rails/factory.rb
CHANGED
@@ -5,19 +5,25 @@ module Signet
|
|
5
5
|
|
6
6
|
class Factory
|
7
7
|
def self.create_from_env name, env, opt_hsh = {load_token: true}
|
8
|
-
# rework this to use singleton?
|
9
|
-
handler = env["signet.#{name.to_s}"]
|
10
8
|
|
11
|
-
|
9
|
+
# TODO: thread safe? best approach? Other uses below
|
10
|
+
client = env["signet.#{name.to_s}.instance"]
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
return client if !!client
|
13
|
+
|
14
|
+
# TODO: again, not pretty...
|
15
|
+
handler = env["signet.#{name.to_s}"]
|
16
16
|
|
17
|
-
|
18
|
-
end
|
17
|
+
client = Signet::OAuth2::Client.new handler.options
|
19
18
|
|
20
|
-
|
19
|
+
if opt_hsh[:load_token]
|
20
|
+
obj = handler.options[:extract_from_env].call env, client
|
21
|
+
handler.load_token_state obj, client
|
22
|
+
|
23
|
+
env["signet.#{name.to_s}.instance"] = obj
|
24
|
+
end
|
25
|
+
|
26
|
+
client
|
21
27
|
end
|
22
28
|
end
|
23
29
|
end
|
data/lib/signet/rails/handler.rb
CHANGED
@@ -5,52 +5,69 @@ require 'rack/utils'
|
|
5
5
|
module Signet
|
6
6
|
module Rails
|
7
7
|
class Handler
|
8
|
-
def initialize(app, opts = {}, &block)
|
8
|
+
def initialize(app, opts = {}, auth_opts = {}, &block)
|
9
9
|
@app = app
|
10
10
|
@options = opts
|
11
|
+
@auth_options = auth_opts
|
11
12
|
end
|
12
13
|
|
13
14
|
def options
|
14
|
-
|
15
|
+
# TODO: this is because signet doesn't dup what we pass in....
|
16
|
+
@options.dup
|
17
|
+
end
|
18
|
+
|
19
|
+
def auth_options env
|
20
|
+
# TODO: this is because signet doesn't dup what we pass in....
|
21
|
+
ret = @auth_options.dup
|
22
|
+
unless ret.include? :redirect_uri
|
23
|
+
req = Rack::Request.new env
|
24
|
+
scheme = req.ssl? ? 'https' : 'http'
|
25
|
+
ret[:redirect_uri] = "#{scheme}://#{req.host_with_port}/signet/#{options[:name]}/auth_callback"
|
26
|
+
end
|
27
|
+
ret
|
15
28
|
end
|
16
29
|
|
17
30
|
def handle(env)
|
18
31
|
|
19
|
-
|
20
|
-
|
21
|
-
r = Rack::Response.new
|
32
|
+
# set these to 'handle' the request
|
33
|
+
status = headers = body = nil
|
22
34
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
35
|
+
# TODO: better way than a gross if elsif block?
|
36
|
+
if "/signet/#{options[:name]}/auth" == env['PATH_INFO'] && 'GET' == env['REQUEST_METHOD']
|
37
|
+
|
38
|
+
# we are looking to auth... so nothing to load
|
39
|
+
client = Factory.create_from_env options[:name], env, load_token: false
|
40
|
+
|
41
|
+
r = Rack::Response.new
|
42
|
+
redirect_uri = client.authorization_uri(auth_options(env)).to_s
|
27
43
|
r.redirect(redirect_uri)
|
28
|
-
r.finish
|
29
|
-
elsif "/signet/#{
|
30
|
-
client = Factory.create_from_env
|
44
|
+
status, headers, body = r.finish
|
45
|
+
elsif "/signet/#{options[:name]}/auth_callback" == env['PATH_INFO'] && 'GET' == env['REQUEST_METHOD']
|
46
|
+
client = Factory.create_from_env options[:name], env, load_token: false
|
31
47
|
query_string_params = Rack::Utils.parse_query(env['QUERY_STRING'])
|
32
48
|
client.code = query_string_params['code']
|
33
|
-
|
49
|
+
client.redirect_uri = auth_options(env)[:redirect_uri]
|
50
|
+
client.fetch_access_token!
|
34
51
|
|
35
|
-
if
|
36
|
-
#
|
37
|
-
|
52
|
+
if options[:handle_auth_callback]
|
53
|
+
# TODO: remove
|
54
|
+
puts '******************************************'
|
55
|
+
puts client.decoded_id_token.inspect
|
56
|
+
|
57
|
+
obj = options[:extract_by_oauth_id].call client.decoded_id_token['id'], client
|
38
58
|
persist_token_state obj, client
|
39
59
|
obj.persist
|
40
|
-
env["signet.#{options[:name]}.
|
41
|
-
|
60
|
+
env["signet.#{options[:name]}.persistance_obj"] = obj.obj
|
42
61
|
else
|
43
|
-
|
44
|
-
env["signet.#{options[:name]}.client"] = client
|
45
|
-
|
62
|
+
env["signet.#{options[:name]}.auth_client"] = client
|
46
63
|
end
|
47
|
-
|
48
|
-
[nil,nil,nil]
|
49
64
|
end
|
65
|
+
|
66
|
+
[status, headers, body]
|
50
67
|
end
|
51
68
|
|
52
69
|
def persist_token_state wrapper, client
|
53
|
-
for i in
|
70
|
+
for i in options[:persist_attrs]
|
54
71
|
if client.respond_to?(i) && wrapper.obj.respond_to?(i.to_s+'=')
|
55
72
|
wrapper.obj.method(i.to_s+'=').call(client.method(i).call)
|
56
73
|
end
|
@@ -58,7 +75,7 @@ module Signet
|
|
58
75
|
end
|
59
76
|
|
60
77
|
def load_token_state wrapper, client
|
61
|
-
for i in
|
78
|
+
for i in options[:persist_attrs]
|
62
79
|
if wrapper.obj.respond_to?(i) && client.respond_to?(i.to_s+'=')
|
63
80
|
client.method(i.to_s+'=').call(wrapper.obj.method(i).call)
|
64
81
|
end
|
@@ -67,15 +84,15 @@ module Signet
|
|
67
84
|
|
68
85
|
def call(env)
|
69
86
|
# rework this to use singleton?
|
70
|
-
env["signet.#{
|
87
|
+
env["signet.#{options[:name]}"] = self
|
71
88
|
|
72
89
|
status, headers, body = handle(env)
|
73
|
-
|
90
|
+
|
74
91
|
unless status
|
75
92
|
|
76
93
|
status, headers, body = @app.call(env)
|
77
94
|
|
78
|
-
obj = env["signet.#{
|
95
|
+
obj = env["signet.#{options[:name]}.instance"]
|
79
96
|
if !!obj
|
80
97
|
obj.persist
|
81
98
|
end
|
@@ -84,7 +101,6 @@ module Signet
|
|
84
101
|
|
85
102
|
[status, headers, body]
|
86
103
|
end
|
87
|
-
|
88
104
|
end
|
89
105
|
end
|
90
106
|
end
|
data/lib/signet/rails/version.rb
CHANGED
@@ -2,20 +2,20 @@ module Signet
|
|
2
2
|
module Rails
|
3
3
|
module Wrappers
|
4
4
|
class ActiveRecord
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
def initialize obj, client
|
6
|
+
@obj = obj
|
7
|
+
@client = client
|
8
|
+
end
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
def obj
|
11
|
+
@obj
|
12
|
+
end
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
def persist
|
15
|
+
if @obj.changed?
|
16
|
+
@obj.save
|
17
|
+
end
|
18
|
+
end
|
19
19
|
end
|
20
20
|
end
|
21
21
|
end
|