plezi 0.8.3 → 0.8.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/Rakefile +6 -0
- data/bin/plezi +7 -6
- data/lib/plezi/handlers/controller_magic.rb +30 -11
- data/lib/plezi/oauth.rb +4 -0
- data/lib/plezi/oauth/auth_controller.rb +208 -0
- data/lib/plezi/version.rb +1 -1
- data/resources/oauth_config.rb +24 -0
- data/resources/redis_config.rb +4 -5
- data/test/plezi_tests.rb +235 -0
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5e19a46b1de10e66f183bb5e62328d68762725cd
|
4
|
+
data.tar.gz: 9e7169464ae76ac2fc86387d30d7203a093244c5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1591203078e67b70960fb20695962f3c2c7e01f5097a6272c1f7dac1d053641e943cbe808c31061c906910b42554333ed2d3f7342614172bb6036bd6031f7103
|
7
|
+
data.tar.gz: 0e5fdf7ca6e8fa261a827cec565ffaf3a1c44d526d346f84c664a9a2ee9a76ee2f473cf07274383509a91d0535a0daea6f6d7be1fac73e7802283e5bb49d98f8
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,18 @@
|
|
2
2
|
|
3
3
|
***
|
4
4
|
|
5
|
+
Change log v.0.8.4
|
6
|
+
|
7
|
+
**core routing changes**: moved the Controller's routing cache out of the global cahce store. This might provide a very slight performance increase.
|
8
|
+
|
9
|
+
**feature**: a new OAuth2 controller offers an easy support for OAuth2 login services such as facebook and google. to use this feature, require 'plezi/oauth'.
|
10
|
+
|
11
|
+
**fix**: fixed an issue where RESTful requests to `new` would be mistakenly routed to the `save` method.
|
12
|
+
|
13
|
+
**testing**: some basic testing for the RESTful Plezi framework has been implemented. Please notice that the tests WILL run the Plezi server on ports 3000 (for http) and 3030 (for https) during the test. The test will run Net::HTTP requests against the Plezi server.
|
14
|
+
|
15
|
+
***
|
16
|
+
|
5
17
|
Change log v.0.8.3
|
6
18
|
|
7
19
|
**Auto-ping feature**: WebSocket connections now automatically send a `ping` every ~45 seconds (approximately) before the websocket's connection would timeout. This auto-ping will keep the connection alive even if no data is exchanged.
|
data/Rakefile
CHANGED
data/bin/plezi
CHANGED
@@ -61,12 +61,13 @@ class AppTemplate
|
|
61
61
|
|
62
62
|
# set up config files
|
63
63
|
app_tree["config"] ||= {}
|
64
|
-
app_tree["config"]["
|
65
|
-
app_tree["config"]["
|
66
|
-
app_tree["config"]["
|
67
|
-
app_tree["config"]["
|
68
|
-
app_tree["config"]["
|
69
|
-
app_tree["config"]["
|
64
|
+
app_tree["config"]["oauth.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"oauth_config.rb"), __FILE__))
|
65
|
+
app_tree["config"]["db_active_record.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"db_ac_config.rb"), __FILE__))
|
66
|
+
app_tree["config"]["db_sequel.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"db_sequel_config.rb"), __FILE__))
|
67
|
+
app_tree["config"]["db_datamapper.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"db_dm_config.rb"), __FILE__))
|
68
|
+
app_tree["config"]["haml.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"haml_config.rb"), __FILE__))
|
69
|
+
app_tree["config"]["i18n.rb"] ||= IO.read(::File.expand_path(File.join("..", "..", "resources" ,"i18n_config.rb"), __FILE__))
|
70
|
+
app_tree["config"]["redis.rb"] ||= (IO.read(::File.expand_path(File.join("..", "..", "resources" ,"redis_config.rb"), __FILE__))).gsub('appsecret', "#{ARGV[1]}_#{SecureRandom.hex}")
|
70
71
|
|
71
72
|
#set up database stub folders
|
72
73
|
app_tree["db"] ||= {}
|
@@ -198,7 +198,7 @@ module Plezi
|
|
198
198
|
# respond to websocket special case
|
199
199
|
return :pre_connect if request['upgrade'] && request['upgrade'].to_s.downcase == 'websocket' && request['connection'].to_s.downcase == 'upgrade'
|
200
200
|
# respond to save 'new' special case
|
201
|
-
return :save if request.request_method.match(/POST|PUT|PATCH/) && params[:id].nil? || params[:id] == 'new'
|
201
|
+
return :save if request.request_method.match(/POST|PUT|PATCH/) && (params[:id].nil? || params[:id] == 'new')
|
202
202
|
# set DELETE method if simulated
|
203
203
|
request.request_method = 'DELETE' if params[:_method].to_s.downcase == 'delete'
|
204
204
|
# respond to special :id routing
|
@@ -279,19 +279,18 @@ module Plezi
|
|
279
279
|
# lists the available methods that will be exposed to HTTP requests
|
280
280
|
def available_public_methods
|
281
281
|
# set class global to improve performance while checking for supported methods
|
282
|
-
|
282
|
+
@available_public_methods ||= (available_routing_methods - [:before, :after, :save, :show, :update, :delete, :initialize, :on_message, :pre_connect, :on_connect, :on_disconnect]).to_set
|
283
283
|
end
|
284
284
|
|
285
285
|
# lists the available methods that will be exposed to the HTTP router
|
286
286
|
def available_routing_methods
|
287
|
-
|
288
|
-
Plezi.cached?(self.superclass.name + '_r&rt') ? Plezi.get_cached(self.superclass.name + "_r&rt") : Plezi.cache_data(self.superclass.name + "_r&rt", (((public_instance_methods - Object.public_instance_methods) - Plezi::ControllerMagic::InstanceMethods.instance_methods).delete_if {|m| m.to_s[0] == '_'}).to_set )
|
287
|
+
@available_routing_methods ||= ( ( (public_instance_methods - Object.public_instance_methods) - Plezi::ControllerMagic::InstanceMethods.instance_methods).delete_if {|m| m.to_s[0] == '_'}).to_set
|
289
288
|
end
|
290
289
|
|
291
290
|
# resets this controller's router, to allow for dynamic changes
|
292
291
|
def reset_routing_cache
|
293
|
-
|
294
|
-
|
292
|
+
@available_routing_methods = false
|
293
|
+
@available_public_methods = false
|
295
294
|
available_routing_methods
|
296
295
|
available_public_methods
|
297
296
|
end
|
@@ -308,6 +307,26 @@ module Plezi
|
|
308
307
|
def method_undefined(id)
|
309
308
|
reset_routing_cache
|
310
309
|
end
|
310
|
+
# # lists the available methods that will be exposed to HTTP requests
|
311
|
+
# def available_public_methods
|
312
|
+
# # set class global to improve performance while checking for supported methods
|
313
|
+
# Plezi.cached?(self.superclass.name + '_p&rt') ? Plezi.get_cached(self.superclass.name + "_p&rt") : Plezi.cache_data(self.superclass.name + "_p&rt", (available_routing_methods - [:before, :after, :save, :show, :update, :delete, :initialize, :on_message, :pre_connect, :on_connect, :on_disconnect]).to_set )
|
314
|
+
# end
|
315
|
+
|
316
|
+
# # lists the available methods that will be exposed to the HTTP router
|
317
|
+
# def available_routing_methods
|
318
|
+
# # set class global to improve performance while checking for supported methods
|
319
|
+
# Plezi.cached?(self.superclass.name + '_r&rt') ? Plezi.get_cached(self.superclass.name + "_r&rt") : Plezi.cache_data(self.superclass.name + "_r&rt", (((public_instance_methods - Object.public_instance_methods) - Plezi::ControllerMagic::InstanceMethods.instance_methods).delete_if {|m| m.to_s[0] == '_'}).to_set )
|
320
|
+
# end
|
321
|
+
|
322
|
+
# # resets this controller's router, to allow for dynamic changes
|
323
|
+
# def reset_routing_cache
|
324
|
+
# Plezi.clear_cached(self.superclass.name + '_p&rt')
|
325
|
+
# Plezi.clear_cached(self.superclass.name + '_r&rt')
|
326
|
+
# available_routing_methods
|
327
|
+
# available_public_methods
|
328
|
+
# end
|
329
|
+
|
311
330
|
|
312
331
|
# reviews the Redis connection, sets it up if it's missing and returns the Redis connection.
|
313
332
|
#
|
@@ -318,10 +337,10 @@ module Plezi
|
|
318
337
|
# ENV['PL_REDIS_URL'] = "redis://username:password@my.host:6379"
|
319
338
|
def redis_connection
|
320
339
|
# return false unless defined?(Redis) && ENV['PL_REDIS_URL']
|
321
|
-
# return
|
340
|
+
# return @redis if defined?(@redis_sub_thread) && @redis
|
322
341
|
# @@redis_uri ||= URI.parse(ENV['PL_REDIS_URL'])
|
323
|
-
#
|
324
|
-
#
|
342
|
+
# @redis ||= Redis.new(host: @@redis_uri.host, port: @@redis_uri.port, password: @@redis_uri.password)
|
343
|
+
# @redis_sub_thread = Thread.new do
|
325
344
|
# begin
|
326
345
|
# Redis.new(host: @@redis_uri.host, port: @@redis_uri.port, password: @@redis_uri.password).subscribe(redis_channel_name) do |on|
|
327
346
|
# on.message do |channel, msg|
|
@@ -335,8 +354,8 @@ module Plezi
|
|
335
354
|
# retry
|
336
355
|
# end
|
337
356
|
# end
|
338
|
-
# raise "Redis connction failed for: #{ENV['PL_REDIS_URL']}" unless
|
339
|
-
#
|
357
|
+
# raise "Redis connction failed for: #{ENV['PL_REDIS_URL']}" unless @redis
|
358
|
+
# @redis
|
340
359
|
return false unless defined?(Redis) && ENV['PL_REDIS_URL']
|
341
360
|
return Plezi.get_cached(self.superclass.name + '_b') if Plezi.cached?(self.superclass.name + '_b')
|
342
361
|
@@redis_uri ||= URI.parse(ENV['PL_REDIS_URL'])
|
data/lib/plezi/oauth.rb
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
module Plezi
|
6
|
+
|
7
|
+
#########################################
|
8
|
+
# This is a social Authentication Controller
|
9
|
+
#
|
10
|
+
# This controller currently supports:
|
11
|
+
#
|
12
|
+
# - Facebook authentication using the Graph API, v. 2.3 - [see Facebook documentation](https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.3).
|
13
|
+
# - Google authentication using the OAuth 2.0 API - [see Google documentation](https://developers.google.com/identity/protocols/OAuth2WebServer).
|
14
|
+
#
|
15
|
+
# It's also possible to manualy register any OAuth 2.0 authentication service using the `register_service` method.
|
16
|
+
#
|
17
|
+
# To use this Controller in your application, make sure the following variables are set:
|
18
|
+
# ENV['FB_APP_ID'] = {facebook_app_id}
|
19
|
+
# ENV['FB_APP_SECRET'] = {facebook_app_secret}
|
20
|
+
# ENV['GOOGLE_APP_ID'] = {google_app_id}
|
21
|
+
# ENV['GOOGLE_APP_SECRET'] = {google_app_secret}
|
22
|
+
#
|
23
|
+
# Add the following route to routes.rb file (as always, route placement order effects behavior):
|
24
|
+
#
|
25
|
+
# create_auth_shared_route do |service, remote_user_id, email, full_remote_response|
|
26
|
+
# # perform any specific app authentication logic such as saving the info.
|
27
|
+
# # return the current user or false if the callback is called with an authentication failure.
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# The `create_auth_shared_route` method is a shortcut for calling the `#shared_route` method with the relevant arguments and setting the OAuth2Ctrl callback.
|
31
|
+
#
|
32
|
+
# Use the following links for social authentication:
|
33
|
+
#
|
34
|
+
# - Facebook: "/auth/facebook?redirect_after=/foo/bar"
|
35
|
+
# - Google: "/auth/google?redirect_after=/foo/bar"
|
36
|
+
# - foo_service: "/auth/foo_service?redirect_after=/foo/bar"
|
37
|
+
#
|
38
|
+
class OAuth2Ctrl
|
39
|
+
|
40
|
+
# Sets (or gets) the callback to be called after authentication is attempeted.
|
41
|
+
#
|
42
|
+
# Accepts a block that will be called with the following parameters:
|
43
|
+
# service_name:: the name of the service. i.e. :facebook, :google, etc'.
|
44
|
+
# service_token:: the authentication token returned by the service. This token should be stored for future access
|
45
|
+
# remote_user_id:: service's user id.
|
46
|
+
# remote_user_email:: users primamry email, if registed with the service.
|
47
|
+
# remote_response:: a Hash with the complete user data response (including email and id).
|
48
|
+
#
|
49
|
+
# If the authentication fails for Facebook, the block will be called with the following values `auth_callback.call(nil, ni, {server: :responce, might_be: :empty})`
|
50
|
+
#
|
51
|
+
# The block will be run in the context of the controller and all the controller's methods will be available to it.
|
52
|
+
#
|
53
|
+
# i.e.:
|
54
|
+
# OAuth2Ctrl.auth_callback |service, service_token, id, email, res| cookies["#{service}_pl_auth_token".to_sym], cookies["#{service}_user_id".to_sym], cookies["#{service}_user_email".to_sym] = service_token, id, email }
|
55
|
+
#
|
56
|
+
# defaults to the example above, which isn't a very sercure behavior, but allows for easy testing.
|
57
|
+
def self.auth_callback &block
|
58
|
+
block_given? ? (@auth_callback = block) : ( @auth_callback ||= (Proc.new {|service, service_token, id, email, res| Plezi.info "deafult callback called for #{service}, with response: #{res.to_s}"; cookies["#{service}_pl_auth_token".to_sym], cookies["#{service}_user_id".to_sym], cookies["#{service}_user_email".to_sym] = service_token, id, email}) )
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
# Stores the registered services library
|
63
|
+
SERVICES = {}
|
64
|
+
|
65
|
+
# This method registers a social login service that conforms to the OAuth2 model.
|
66
|
+
#
|
67
|
+
# Accepts the following required parameters:
|
68
|
+
# service_name:: a Symbol naming the service. i.e. :facebook or :google .
|
69
|
+
# options:: a Hash of options, some of which are required.
|
70
|
+
#
|
71
|
+
# The options are:
|
72
|
+
# app_id:: the aplication's unique ID registered with the service. i.e. ENV[FB_APP_ID] (storing these in environment variables is safer then hardcoding them)
|
73
|
+
# app_secret:: the aplication's unique secret registered with the service.
|
74
|
+
# auth_url:: the authentication URL. This is the url to which the user is redirected. i.e.: "https://www.facebook.com/dialog/oauth"
|
75
|
+
# token_url:: the token request URL. This is the url used to switch the single-use code into a persistant authentication token. i.e.: "https://www.googleapis.com/oauth2/v3/token"
|
76
|
+
# profile_url:: the URL used to ask the service for the user's profile (the service's API url). i.e.: "https://graph.facebook.com/v2.3/me"
|
77
|
+
# scope:: a String representing the scope requested. i.e. 'email profile'.
|
78
|
+
#
|
79
|
+
# There will be an attempt to aitomatically register Facebook and Google login services under these conditions:
|
80
|
+
#
|
81
|
+
# * For Facebook: Both ENV['FB_APP_ID'] && ENV['FB_APP_SECRET'] have been defined.
|
82
|
+
# * For Google: Both ENV['GOOGLE_APP_ID'] && ENV['GOOGLE_APP_SECRET'] have been defined.
|
83
|
+
#
|
84
|
+
#
|
85
|
+
# Just for reference, at the time of this writing:
|
86
|
+
#
|
87
|
+
# * facebook auth_url: "https://www.facebook.com/dialog/oauth"
|
88
|
+
# * facebook token_url: "https://graph.facebook.com/v2.3/oauth/access_token"
|
89
|
+
# * facebook profile_url: "https://graph.facebook.com/v2.3/me"
|
90
|
+
# * google auth_url: "https://accounts.google.com/o/oauth2/auth"
|
91
|
+
# * google token_url: "https://www.googleapis.com/oauth2/v3/token"
|
92
|
+
# * google profile_url: "https://www.googleapis.com/plus/v1/people/me"
|
93
|
+
#
|
94
|
+
def self.register_service service_name, options
|
95
|
+
raise "Cannot register service, missing required information." unless service_name && options[:auth_url] && options[:token_url] && options[:profile_url] && options[:app_id] && options[:app_secret]
|
96
|
+
# for google, scope is space delimited. for facebook it's comma delimited
|
97
|
+
options[:scope] ||= 'profile, email'
|
98
|
+
SERVICES[service_name] = options
|
99
|
+
end
|
100
|
+
|
101
|
+
# Called to manually run through the authentication logic for the requested service,
|
102
|
+
# without performing any redirections.
|
103
|
+
#
|
104
|
+
# Use this method to attempt and re-login a user using an existing login token:
|
105
|
+
#
|
106
|
+
# token = 'google_token_could_be_recieved_also_from_javascript_sdk'
|
107
|
+
# OAuth2Ctrl.auth :google, token, self
|
108
|
+
#
|
109
|
+
# The method will return false if re-login fails and it will otherwise return the callback's return value.
|
110
|
+
#
|
111
|
+
# Call this method from within a controller, passing the controller (self) to the method, like so:
|
112
|
+
#
|
113
|
+
# OAuth2Ctrl.auth :facebook, token, self
|
114
|
+
#
|
115
|
+
# This is especially effective if `auth_callback` returns the user object, as it would allow to chain
|
116
|
+
# different login methods, i.e.:
|
117
|
+
#
|
118
|
+
# @user ||= app_login || OAuth2Ctrl.auth(:facebook, fb_token, self) || OAuth2Ctrl.auth(:google, google_token, self) || ....
|
119
|
+
def self.auth service_name, service_token, controller
|
120
|
+
service = SERVICES[service_name]
|
121
|
+
retrun false unless service
|
122
|
+
# auth_res = controller.cookies[c_name] ? (JSON.parse URI.parse("#{service[:profile_url]}?access_token=#{controller.cookies[c_name]}").read rescue ({}) ) : {}
|
123
|
+
# controller.cookies[c_name] = nil unless auth_res['id']
|
124
|
+
# auth_res['id'] ? controller.instance_exec( service_name, auth_res['id'], auth_res['email'], auth_res, &auth_callback) : ( controller.instance_exec( service_name, nil, nil, auth_res, &auth_callback) && false)
|
125
|
+
auth_res = {}
|
126
|
+
uri = URI.parse("#{service[:profile_url]}?access_token=#{service_token}")
|
127
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: (uri.scheme == "https"), verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http|
|
128
|
+
req = Net::HTTP::Get.new(uri)
|
129
|
+
res = http.request(req).body
|
130
|
+
auth_res = (JSON.parse res) rescue ({})
|
131
|
+
end if service_token
|
132
|
+
auth_res['id'] ? controller.instance_exec( service_name, service_token, auth_res['id'], auth_res['email'], auth_res, &auth_callback) : ( controller.instance_exec( service_name, nil, nil, nil, auth_res, &auth_callback) && false)
|
133
|
+
end
|
134
|
+
|
135
|
+
def update
|
136
|
+
service_name = params[:id].to_s.to_sym
|
137
|
+
service = SERVICES[service_name]
|
138
|
+
retrun false unless service
|
139
|
+
if params[:error]
|
140
|
+
instance_exec( service_name, nil, nil, nil, {}, &self.class.auth_callback)
|
141
|
+
return redirect_to(flash[:redirect_after])
|
142
|
+
end
|
143
|
+
unless params[:code]
|
144
|
+
flash[:redirect_after] = params[:redirect_after] || '/'
|
145
|
+
return redirect_to _auth_url_for(service_name)
|
146
|
+
end
|
147
|
+
uri = URI.parse service[:token_url]
|
148
|
+
service_token = nil
|
149
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: (uri.scheme == "https"), verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http|
|
150
|
+
req = Net::HTTP::Post.new(uri)
|
151
|
+
req.form_data = {"client_id" => service[:app_id],
|
152
|
+
"client_secret" => service[:app_secret], "code" => params[:code] ,
|
153
|
+
"grant_type" => "authorization_code","redirect_uri" => "#{request.base_url}/auth/#{service_name.to_s}"}
|
154
|
+
res = http.request(req).body
|
155
|
+
service_token = ((JSON.parse res)['access_token'] rescue nil)
|
156
|
+
end
|
157
|
+
|
158
|
+
self.class.auth service_name, service_token, self
|
159
|
+
redirect_to(flash[:redirect_after])
|
160
|
+
end
|
161
|
+
alias :show :update
|
162
|
+
|
163
|
+
protected
|
164
|
+
|
165
|
+
# returns a url for the requested service's social login.
|
166
|
+
#
|
167
|
+
# this is used internally. Normal behavior would be to set a link to '/auth/{service_name}', where correct redirection will be automatic.
|
168
|
+
#
|
169
|
+
def _auth_url_for service_name
|
170
|
+
service = SERVICES[service_name]
|
171
|
+
return nil unless service
|
172
|
+
redirect_uri = HTTP::encode "#{request.base_url}/auth/#{service_name.to_s}", :url #response_type
|
173
|
+
return "#{service[:auth_url]}?client_id=#{service[:app_id]}&redirect_uri=#{redirect_uri}&scope=#{service[:scope]}&response_type=code"
|
174
|
+
end
|
175
|
+
|
176
|
+
register_service(:facebook, app_id: ENV['FB_APP_ID'],
|
177
|
+
app_secret: ENV['FB_APP_SECRET'],
|
178
|
+
auth_url: "https://www.facebook.com/dialog/oauth",
|
179
|
+
token_url: "https://graph.facebook.com/v2.3/oauth/access_token",
|
180
|
+
profile_url: "https://graph.facebook.com/v2.3/me",
|
181
|
+
scope: "public_profile,email") if ENV['FB_APP_ID'] && ENV['FB_APP_SECRET']
|
182
|
+
register_service(:google, app_id: ENV['GOOGLE_APP_ID'],
|
183
|
+
app_secret: ENV['GOOGLE_APP_SECRET'],
|
184
|
+
auth_url: "https://accounts.google.com/o/oauth2/auth",
|
185
|
+
token_url: "https://www.googleapis.com/oauth2/v3/token",
|
186
|
+
profile_url: "https://www.googleapis.com/oauth2/v1/userinfo",
|
187
|
+
scope: "profile email") if ENV['GOOGLE_APP_ID'] && ENV['GOOGLE_APP_SECRET']
|
188
|
+
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# This method creates the OAuth2Ctrl route.
|
193
|
+
# This is actually a short-cut for:
|
194
|
+
#
|
195
|
+
# shared_route "auth/(:id)/(:code)" , Plezi::OAuth2Ctrl
|
196
|
+
# Plezi::OAuth2Ctrl.auth_callback = Proc.new {|service, user_id, user_email, service_response| ... }
|
197
|
+
#
|
198
|
+
# the `:id` parameter is used to identify the service (facebook, google. etc').
|
199
|
+
#
|
200
|
+
# The method accepts a block that will be used to set the authentication callback. See the Plezi::OAuth2Ctrl documentation for details.
|
201
|
+
#
|
202
|
+
# The method can be called only once and will self-destruct.
|
203
|
+
def create_auth_shared_route options = {}, &block
|
204
|
+
shared_route "auth/(:id)/(:code)" , Plezi::OAuth2Ctrl
|
205
|
+
undef create_auth_shared_route
|
206
|
+
Plezi::OAuth2Ctrl.auth_callback = block if block
|
207
|
+
Plezi::OAuth2Ctrl
|
208
|
+
end
|
data/lib/plezi/version.rb
CHANGED
@@ -0,0 +1,24 @@
|
|
1
|
+
###############
|
2
|
+
# # OAuth2 Config File
|
3
|
+
# # ==================
|
4
|
+
# #
|
5
|
+
# # Here you can sets the OAuth2 variables and require the OAuth2 Plezi controller.
|
6
|
+
# # (if the variables aren't set BEFORE the inclution, automatic setup will NOT take place)
|
7
|
+
# #
|
8
|
+
# # First set the variables:
|
9
|
+
# #
|
10
|
+
# ENV["FB_APP_ID"] ||= "app id"
|
11
|
+
# ENV["FB_APP_SECRET"] ||= "secret"
|
12
|
+
# ENV['GOOGLE_APP_ID'] = "app id"
|
13
|
+
# ENV['GOOGLE_APP_SECRET'] = "secret"
|
14
|
+
# #
|
15
|
+
# # Then, require the actual OAuth2 controller class (Plezi::OAuth2Ctrl).
|
16
|
+
# #
|
17
|
+
# require 'plezi/oauth'
|
18
|
+
# #
|
19
|
+
# # Last, but not least, remember to add the authentication route in the correct placement using:
|
20
|
+
# create_auth_shared_route do |service_name, remote_user_id, remote_user_email, remote_response|
|
21
|
+
# # ...callback for authentication.
|
22
|
+
# # This callback should return the app user object or false
|
23
|
+
# # This callback hass access to the magic controller methods (request, cookies, etc')
|
24
|
+
# end
|
data/resources/redis_config.rb
CHANGED
@@ -5,8 +5,7 @@ if defined? Redis
|
|
5
5
|
# ## Plezi Redis Automation
|
6
6
|
# ## ====
|
7
7
|
# ##
|
8
|
-
# ##
|
9
|
-
# ## this is less recommended then writing your own tailored solution
|
8
|
+
# ## Sets up Plezi to use Radis broadcast.
|
10
9
|
# ##
|
11
10
|
# ## If Plezi Redis Automation is enabled:
|
12
11
|
# ## Plezi creates is own listening thread for each Controller class that broadcasts using Redis.
|
@@ -15,17 +14,17 @@ if defined? Redis
|
|
15
14
|
# ## this overrides the default Controller#broadcast method which is very powerful but
|
16
15
|
# ## is limited to one process.
|
17
16
|
# ##
|
18
|
-
# ENV['PL_REDIS_URL']
|
17
|
+
# ENV['PL_REDIS_URL'] ||= ENV['REDIS_URL'] || ENV['REDISCLOUD_URL'] || ENV['REDISTOGO_URL'] || "redis://username:password@my.host:6389"
|
19
18
|
|
20
19
|
|
21
|
-
# ##
|
20
|
+
# ## OR, write your own custom Redis Automation here
|
22
21
|
# ## ====
|
23
22
|
# ##
|
24
23
|
# ## create a listening thread - rewrite the following code for your own Redis tailored solution.
|
25
24
|
# ##
|
26
25
|
# ## the following is only sample code for you to change:
|
27
26
|
# RADIS_CHANNEL = 'appsecret'
|
28
|
-
# RADIS_URI = URI.parse(ENV['REDISCLOUD_URL'] || "redis://username:password@my.host:6389")
|
27
|
+
# RADIS_URI = URI.parse(ENV['REDIS_URL'] || ENV['REDISCLOUD_URL'] || "redis://username:password@my.host:6389")
|
29
28
|
# RADIS_CONNECTION = Redis.new(host: RADIS_URI.host, port: RADIS_URI.port, password: RADIS_URI.password)
|
30
29
|
# RADIS_THREAD = Thread.new do
|
31
30
|
# Redis.new(host: RADIS_URI.host, port: RADIS_URI.port, password: RADIS_URI.password).subscribe(RADIS_CHANNEL) do |on|
|
data/test/plezi_tests.rb
ADDED
@@ -0,0 +1,235 @@
|
|
1
|
+
# I started writing tests... but I haven't finished quite yet.
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'open-uri'
|
5
|
+
require 'plezi'
|
6
|
+
|
7
|
+
class TestCtrl
|
8
|
+
|
9
|
+
# this will be called before every request.
|
10
|
+
def before
|
11
|
+
end
|
12
|
+
|
13
|
+
# this will be called after every request.
|
14
|
+
def after
|
15
|
+
end
|
16
|
+
|
17
|
+
# shouldn't be available (return 404).
|
18
|
+
def _hidden
|
19
|
+
"do you see me?"
|
20
|
+
end
|
21
|
+
def index
|
22
|
+
"test"
|
23
|
+
end
|
24
|
+
|
25
|
+
# should return a 500 internal server error message.
|
26
|
+
def fail
|
27
|
+
raise "Hell!"
|
28
|
+
end
|
29
|
+
|
30
|
+
# called when request is GET and params\[:id] == "new".
|
31
|
+
def new
|
32
|
+
"new"
|
33
|
+
end
|
34
|
+
# called when request is GET and params\[:id] exists (unless params\[:id] == "new").
|
35
|
+
def show
|
36
|
+
"show #{params[:id]}"
|
37
|
+
end
|
38
|
+
# called when request is POST / PUT and params\[:id] exists and isn't 'new'
|
39
|
+
def update
|
40
|
+
"update #{params[:id]}"
|
41
|
+
end
|
42
|
+
def delete
|
43
|
+
"delete #{params[:id]}"
|
44
|
+
end
|
45
|
+
# called when request is POST / PUT and params\[:id] is 'new'
|
46
|
+
def save
|
47
|
+
params[:data].to_s
|
48
|
+
end
|
49
|
+
def sleeper
|
50
|
+
sleep 1
|
51
|
+
"slept"
|
52
|
+
end
|
53
|
+
# should return a 404 error.
|
54
|
+
def get404
|
55
|
+
false
|
56
|
+
end
|
57
|
+
# path to test for chuncked encoding and response streaming.
|
58
|
+
def streamer
|
59
|
+
response.start_http_streaming
|
60
|
+
PL.callback(self, :_stream_out) { PL.callback(response, :finish) }
|
61
|
+
true
|
62
|
+
end
|
63
|
+
def _stream_out
|
64
|
+
response.send "streamed"
|
65
|
+
true
|
66
|
+
end
|
67
|
+
def file_test
|
68
|
+
if params[:file]
|
69
|
+
send_data params[:file][:data], type: params[:file][:type], inline: true, filename: params[:file][:filename]
|
70
|
+
return true
|
71
|
+
end
|
72
|
+
false
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
############
|
77
|
+
## WebSockets
|
78
|
+
|
79
|
+
# called once the websocket was connected
|
80
|
+
def on_connect
|
81
|
+
response << "connected"
|
82
|
+
end
|
83
|
+
|
84
|
+
# called when new Websocket data is recieved
|
85
|
+
#
|
86
|
+
# data is a string that contains binary or UTF8 (message dependent) data.
|
87
|
+
def on_message data
|
88
|
+
broadcast :_push, data
|
89
|
+
_push data
|
90
|
+
end
|
91
|
+
|
92
|
+
# called when a disconnect packet has been recieved or the connection has been cut
|
93
|
+
# (ISN'T called after a disconnect message has been sent).
|
94
|
+
def on_disconnect
|
95
|
+
end
|
96
|
+
|
97
|
+
# a demo event method that recieves a broadcast from instance siblings.
|
98
|
+
def _push data
|
99
|
+
response << data.to_s
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
module PleziTestTasks
|
104
|
+
module_function
|
105
|
+
|
106
|
+
RESULTS = {true => "passed", false => 'FAILED!'}
|
107
|
+
|
108
|
+
def run_tests
|
109
|
+
(public_methods(false)).each {|m| method(m).call if m.to_s.match /^test_/}
|
110
|
+
true
|
111
|
+
end
|
112
|
+
def test_sleep
|
113
|
+
Plezi.run_async do
|
114
|
+
begin
|
115
|
+
puts "Sleeper test: #{RESULTS[URI.parse("http://localhost:3000/sleeper").read == 'slept']}"
|
116
|
+
puts "ASync tasks test: #{RESULTS[true]}"
|
117
|
+
rescue => e
|
118
|
+
puts "Sleeper test FAILED TO RUN!!!"
|
119
|
+
puts e
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_index
|
125
|
+
begin
|
126
|
+
puts "index test: #{RESULTS[URI.parse("http://localhost:3000/").read == 'test']}"
|
127
|
+
rescue => e
|
128
|
+
puts "Index test FAILED TO RUN!!!"
|
129
|
+
puts e
|
130
|
+
end
|
131
|
+
end
|
132
|
+
def test_ssl
|
133
|
+
puts "Connection to non-ssl and unique route test: #{RESULTS[URI.parse("http://localhost:3000/ssl").read == 'false']}"
|
134
|
+
uri = URI.parse("https://localhost:3030/ssl")
|
135
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: (uri.scheme == "https"), verify_mode: OpenSSL::SSL::VERIFY_NONE) do |http|
|
136
|
+
puts "Connection to ssl and unique ssl route test: #{RESULTS[ http.request(Net::HTTP::Get.new(uri)).body == 'true' ]}"
|
137
|
+
end
|
138
|
+
rescue => e
|
139
|
+
puts "SSL Tests FAILED to complete!!!"
|
140
|
+
puts e
|
141
|
+
end
|
142
|
+
def test_new
|
143
|
+
puts "New RESTful path test: #{RESULTS[URI.parse("http://localhost:3000/new").read == 'new']}"
|
144
|
+
|
145
|
+
rescue => e
|
146
|
+
puts "New RESTful path test FAILED TO RUN!!!"
|
147
|
+
puts e
|
148
|
+
end
|
149
|
+
def test_show
|
150
|
+
puts "Show RESTful path test: #{RESULTS[URI.parse("http://localhost:3000/3").read == 'show 3']}"
|
151
|
+
|
152
|
+
rescue => e
|
153
|
+
puts "Show RESTful path test FAILED TO RUN!!!"
|
154
|
+
puts e
|
155
|
+
end
|
156
|
+
def test_update
|
157
|
+
puts "Update RESTful path test: #{RESULTS[Net::HTTP.post_form( URI.parse("http://localhost:3000/"), id: 3).body == 'update 3']}"
|
158
|
+
|
159
|
+
rescue => e
|
160
|
+
puts "Update RESTful path test FAILED TO RUN!!!"
|
161
|
+
puts e
|
162
|
+
end
|
163
|
+
def test_delete
|
164
|
+
puts "Delete RESTful path test: #{RESULTS[Net::HTTP.post_form( URI.parse("http://localhost:3000/"), id: 3, _method: :delete).body == 'delete 3']}"
|
165
|
+
|
166
|
+
rescue => e
|
167
|
+
puts "Delete RESTful path test FAILED TO RUN!!!"
|
168
|
+
puts e
|
169
|
+
end
|
170
|
+
def test_save
|
171
|
+
puts "Save RESTful path test: #{RESULTS[Net::HTTP.post_form( URI.parse("http://localhost:3000/new"), data: "passed").body == 'passed']}"
|
172
|
+
|
173
|
+
rescue => e
|
174
|
+
puts "Save RESTful path test FAILED TO RUN!!!"
|
175
|
+
puts e
|
176
|
+
end
|
177
|
+
def test_streamed
|
178
|
+
begin
|
179
|
+
puts "Streaming test: #{RESULTS[URI.parse("http://localhost:3000/streamer").read == 'streamed']}"
|
180
|
+
rescue => e
|
181
|
+
puts "Streaming test FAILED TO RUN!!!"
|
182
|
+
puts e
|
183
|
+
end
|
184
|
+
end
|
185
|
+
def test_404
|
186
|
+
puts "404 not found and router continuity tests: #{RESULTS[ Net::HTTP.get_response(URI.parse "http://localhost:3000/get404" ).code == '404' ]}"
|
187
|
+
|
188
|
+
rescue => e
|
189
|
+
puts "404 not found test FAILED TO RUN!!!"
|
190
|
+
puts e
|
191
|
+
end
|
192
|
+
def test_500
|
193
|
+
workers = Plezi::EventMachine.count_living_workers
|
194
|
+
puts "500 internal error test: #{RESULTS[ Net::HTTP.get_response(URI.parse "http://localhost:3000/fail" ).code == '500' ]}"
|
195
|
+
# cause 10 more exceptions to be raised... testing thread survival.
|
196
|
+
10.times { Net::HTTP.get_response(URI.parse "http://localhost:3000/fail" ).code }
|
197
|
+
workers_after_test = Plezi::EventMachine.count_living_workers
|
198
|
+
puts "Worker survival test: #{RESULTS[workers_after_test == workers]} (#{workers_after_test} out of #{workers})"
|
199
|
+
|
200
|
+
rescue => e
|
201
|
+
puts "404 not found test FAILED TO RUN!!!"
|
202
|
+
puts e
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
NO_PLEZI_AUTO_START = true
|
207
|
+
|
208
|
+
PL.create_logger '/dev/null'
|
209
|
+
|
210
|
+
listen port: 3000
|
211
|
+
|
212
|
+
route("/ssl") {|req, res| res << "false" }
|
213
|
+
listen port: 3030, ssl: true
|
214
|
+
route("/ssl") {|req, res| res << "true" }
|
215
|
+
|
216
|
+
shared_route '/', TestCtrl
|
217
|
+
|
218
|
+
|
219
|
+
Plezi::EventMachine.start Plezi.max_threads
|
220
|
+
|
221
|
+
shoutdown_test = false
|
222
|
+
Plezi.on_shutdown { shoutdown_test = true }
|
223
|
+
|
224
|
+
PleziTestTasks.run_tests
|
225
|
+
|
226
|
+
Plezi::EventMachine.clear_timers
|
227
|
+
|
228
|
+
Plezi::DSL.stop_services
|
229
|
+
|
230
|
+
Plezi::EventMachine.shutdown
|
231
|
+
|
232
|
+
|
233
|
+
puts "Shutdown test: #{ PleziTestTasks::RESULTS[shoutdown_test] }"
|
234
|
+
|
235
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: plezi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Boaz Segev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-06-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -74,6 +74,8 @@ files:
|
|
74
74
|
- lib/plezi/handlers/magic_helpers.rb
|
75
75
|
- lib/plezi/handlers/route.rb
|
76
76
|
- lib/plezi/handlers/stubs.rb
|
77
|
+
- lib/plezi/oauth.rb
|
78
|
+
- lib/plezi/oauth/auth_controller.rb
|
77
79
|
- lib/plezi/server/http.rb
|
78
80
|
- lib/plezi/server/http_protocol.rb
|
79
81
|
- lib/plezi/server/http_request.rb
|
@@ -102,12 +104,14 @@ files:
|
|
102
104
|
- resources/environment.rb
|
103
105
|
- resources/haml_config.rb
|
104
106
|
- resources/i18n_config.rb
|
107
|
+
- resources/oauth_config.rb
|
105
108
|
- resources/plezi_gray.png
|
106
109
|
- resources/plezi_websockets.html
|
107
110
|
- resources/rakefile
|
108
111
|
- resources/redis_config.rb
|
109
112
|
- resources/routes.rb
|
110
113
|
- resources/welcome_page.html
|
114
|
+
- test/plezi_tests.rb
|
111
115
|
- websocket chatroom.md
|
112
116
|
homepage: http://boazsegev.github.io/plezi/
|
113
117
|
licenses:
|
@@ -135,4 +139,5 @@ signing_key:
|
|
135
139
|
specification_version: 4
|
136
140
|
summary: Plezi is the native Ruby Framework for real time web-apps, with Websockets,
|
137
141
|
RESTful routing and HTTP streaming support.
|
138
|
-
test_files:
|
142
|
+
test_files:
|
143
|
+
- test/plezi_tests.rb
|