signet-rails 0.0.1 → 0.0.2
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/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
|