padrino-auth 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fe0f85bb7353604b8ba83cc1f2065702a18249a6
4
+ data.tar.gz: c6e23562acdc4d84ad7df25f53551fc673bd8bd1
5
+ SHA512:
6
+ metadata.gz: 7fbe645faf1515806edb29a26d5dad59130fce3a291c666bffdc9585e0aa941e4d324b26fa4be930f2ca4cac1f2c3ba2386138474c8641fe585d07dcae1e2840
7
+ data.tar.gz: e79a584a29ea547eb66b78afb232add8adb5e8f0560983f5cef18cd3fc2483eeed21488a2df4b370dd5ce52f5643c41713b004c8ce37b999a26f5ae3dd9c122e
data/.gitignore ADDED
@@ -0,0 +1,34 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /lib/bundler/man/
26
+
27
+ # for a library or gem, you might want to ignore these files since the code is
28
+ # intended to run in multiple environments; otherwise, check them in:
29
+ Gemfile.lock
30
+ # .ruby-version
31
+ # .ruby-gemset
32
+
33
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
34
+ .rvmrc
data/EXAMPLES.md ADDED
@@ -0,0 +1,249 @@
1
+ ### Authentication and authorization example
2
+
3
+ ```
4
+ # sketchup some database model
5
+ module Character
6
+ extend self
7
+
8
+ # db model must have authenticate method which should response with credentials object on
9
+ # the calls of { :email => 'a@b', :password => 'abc' } to authenticate by email and password
10
+ # or { :id => 42 } to restore credentials from id saved in session or another persistance storage
11
+ def authenticate(credentials)
12
+ case
13
+ when credentials[:email] && credentials[:password]
14
+ target = all.find{ |resource| resource.id.to_s == credentials[:email] }
15
+ (target && target.name.gsub(/[^A-Z]/,'') == credentials[:password]) ? target : nil
16
+ when credentials.has_key?(:id)
17
+ all.find{ |resource| resource.id == credentials[:id] }
18
+ else
19
+ false
20
+ end
21
+ end
22
+
23
+ # example collection of users
24
+ def all
25
+ @all = [
26
+ OpenStruct.new(:id => :bender, :name => 'Bender Bending Rodriguez', :role => :robots ),
27
+ OpenStruct.new(:id => :leela, :name => 'Turanga Leela', :role => :mutants ),
28
+ OpenStruct.new(:id => :fry, :name => 'Philip J. Fry', :role => :humans ),
29
+ OpenStruct.new(:id => :ami, :name => 'Amy Wong', :role => :humans ),
30
+ OpenStruct.new(:id => :zoidberg, :name => 'Dr. John A. Zoidberg', :role => :lobsters),
31
+ ]
32
+ end
33
+ end
34
+
35
+ module Example; end
36
+ # define an application class
37
+ class Example::App < Padrino::Application
38
+ set :login_model, :character
39
+
40
+ register Padrino::Rendering
41
+ register Padrino::Helpers
42
+ register Padrino::Login
43
+ register Padrino::Access
44
+ enable :sessions
45
+
46
+ set_access :*, :allow => :index
47
+ set_access :humans, :allow => :restricted
48
+
49
+ get(:index){ 'index' }
50
+ get(:restricted){ 'secret' }
51
+ end
52
+ ```
53
+
54
+ After rackup we have:
55
+
56
+ http://localhost:9292/ gets index
57
+ http://localhost:9292/restricted gets us redirected to '/login'
58
+
59
+ If we fill the form with 'leela' and her password 'TL' we get logged in and
60
+ redirected to '/restricted' location we tried to visit earlier, but Leela has
61
+ no access to it and we get 403 status.
62
+
63
+ http://localhost:9292/login now if we visit '/login' again and ented 'ami'
64
+ and his password 'AW' we can get to '/restricted' location.
65
+
66
+ http://localhost:9292/restricted now gets 'secret' content
67
+
68
+ ### Authorization-only example
69
+
70
+ ```ruby
71
+ # sketchup some database model
72
+ module Character
73
+ extend self
74
+
75
+ # db model must have authenticate method which should response with credentials object on
76
+ # the calls of { :email => 'a@b', :password => 'abc' } to authenticate by email and password
77
+ # or { :id => 42 } to restore credentials from id saved in session or another persistance storage
78
+ def authenticate(credentials)
79
+ case
80
+ when credentials[:email] && credentials[:password]
81
+ target = all.find{ |resource| resource.id.to_s == credentials[:email] }
82
+ target.name.gsub(/[^A-Z]/,'') == credentials[:password] ? target : nil
83
+ when credentials.has_key?(:id)
84
+ all.find{ |resource| resource.id == credentials[:id] }
85
+ else
86
+ false
87
+ end
88
+ end
89
+
90
+ # example collection of users
91
+ def all
92
+ @all = [
93
+ OpenStruct.new(:id => :bender, :name => 'Bender Bending Rodriguez', :role => :robots ),
94
+ OpenStruct.new(:id => :leela, :name => 'Turanga Leela', :role => :mutants ),
95
+ OpenStruct.new(:id => :fry, :name => 'Philip J. Fry', :role => :humans ),
96
+ OpenStruct.new(:id => :ami, :name => 'Amy Wong', :role => :humans ),
97
+ OpenStruct.new(:id => :zoidberg, :name => 'Dr. John A. Zoidberg', :role => :lobsters),
98
+ ]
99
+ end
100
+ end
101
+
102
+ module Example; end
103
+ # define an application class
104
+ class Example::App < Padrino::Application
105
+ register Padrino::Access
106
+
107
+ # authorization module has no built-in persistance storage, so we have to implement it:
108
+ enable :sessions
109
+ helpers do
110
+ def credentials
111
+ puts settings.permissions.inspect
112
+ @visitor ||= Character.authenticate(:id => session[:visitor_id])
113
+ end
114
+ def credentials=(user)
115
+ @visitor = user
116
+ session[:visitor_id] = @visitor ? @visitor.id : nil
117
+ end
118
+ end
119
+
120
+ # simple authentication controller
121
+ get(:login, :with => :id) do
122
+ # this is an example, do not authenticate by user id in real apps
123
+ self.credentials = Character.authenticate(:id => params[:id].to_sym)
124
+ end
125
+
126
+ # allow everyone to visit '/login'
127
+ set_access :*, :allow => :login
128
+
129
+ # example action
130
+ get(:index){ 'foo' }
131
+
132
+ # robots are allowed to bend
133
+ set_access :robots, :allow => :bend
134
+ get(:bend){ 'bend' }
135
+
136
+ # humans and robots are allowed to live on surface
137
+ controller :surface do
138
+ set_access :humans, :robots
139
+ get(:live) { 'live on the surface' }
140
+ end
141
+
142
+ # mutants are allowed to live on surface, humans are allowed to visit
143
+ controller :sewers do
144
+ set_access :mutants
145
+ set_access :humans, :allow => :visit
146
+ get(:live) { 'live in the sewers' }
147
+ get(:visit) { 'visit the sewers' }
148
+ end
149
+ end
150
+ ```
151
+
152
+ That's it, rackup it and see what's up:
153
+
154
+ http://localhost:9292/ => 403 shows Forbidden
155
+ http://localhost:9292/login/leela => 200 logs Leela in
156
+
157
+ Now we can see that Leela is allowed to live in sewers:
158
+
159
+ http://localhost:9292/sewers/live => 200 live in sewers
160
+
161
+ Now check if robots can bend and humans to visit sewers:
162
+
163
+ http://localhost:9292/login/fry
164
+ http://localhost:9292/sewers/live
165
+ http://localhost:9292/sewers/visit
166
+
167
+ http://localhost:9292/login/bender
168
+ http://localhost:9292/bend
169
+ http://localhost:9292/stop_partying
170
+
171
+ ### Authentication-only example
172
+
173
+ ```ruby
174
+ # sketchup some database model
175
+ module Character
176
+ extend self
177
+
178
+ # db model must have authenticate method which should response with credentials object on
179
+ # the calls of { :email => 'a@b', :password => 'abc' } to authenticate by email and password
180
+ # or { :id => 42 } to restore credentials from id saved in session or another persistance storage
181
+ def authenticate(credentials)
182
+ case
183
+ when credentials[:email] && credentials[:password]
184
+ target = all.find{ |resource| resource.id.to_s == credentials[:email] }
185
+ (target && target.name.gsub(/[^A-Z]/,'') == credentials[:password]) ? target : nil
186
+ when credentials.has_key?(:id)
187
+ all.find{ |resource| resource.id == credentials[:id] }
188
+ else
189
+ false
190
+ end
191
+ end
192
+
193
+ # example collection of users
194
+ def all
195
+ @all = [
196
+ OpenStruct.new(:id => :bender, :name => 'Bender Bending Rodriguez', :role => :robots ),
197
+ OpenStruct.new(:id => :leela, :name => 'Turanga Leela', :role => :mutants ),
198
+ OpenStruct.new(:id => :fry, :name => 'Philip J. Fry', :role => :humans ),
199
+ OpenStruct.new(:id => :ami, :name => 'Amy Wong', :role => :humans ),
200
+ OpenStruct.new(:id => :zoidberg, :name => 'Dr. John A. Zoidberg', :role => :lobsters),
201
+ ]
202
+ end
203
+ end
204
+
205
+ module Example; end
206
+ # define an application class
207
+ class Example::App < Padrino::Application
208
+ set :login_model, :character
209
+
210
+ register Padrino::Rendering
211
+ register Padrino::Helpers
212
+ register Padrino::Login
213
+ enable :sessions
214
+
215
+ get(:index){ 'index' }
216
+ get(:restricted){ 'secret' }
217
+
218
+ # if we plan to add custom authorization we need to define #authorized? helper
219
+ helpers do
220
+ def authorized?
221
+ restricted = ['/restricted'].include?(request.env['PATH_INFO'])
222
+ if credentials
223
+ case
224
+ when credentials.id == :bender
225
+ true
226
+ else
227
+ !restricted
228
+ end
229
+ else
230
+ !restricted
231
+ end
232
+ end
233
+ end
234
+ end
235
+ ```
236
+
237
+ After rackup we have:
238
+
239
+ http://localhost:9292/ gets index
240
+ http://localhost:9292/restricted gets us redirected to '/login'
241
+
242
+ If we fill the form with 'leela' and her password 'TL' we get logged in and
243
+ redirected to '/restricted' location we tried to visit earlier, but Leela has
244
+ no access to it and we get 403 status.
245
+
246
+ http://localhost:9292/login now if we visit '/login' again and ented 'bender'
247
+ and his password 'BBR' we can get to '/restricted' location.
248
+
249
+ http://localhost:9292/restricted now gets 'secret' content
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Igor Bochkariov
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,118 @@
1
+ ## Authorization and authentication modules for Padrino framework
2
+
3
+ ### Overview
4
+
5
+ This padrino-auth provides the means to authenticate and authorize users.
6
+ These modules are designed to be independent but compatible with each other.
7
+
8
+ ### Padrino::Login, an authentication module for Padrino
9
+
10
+ Authentication means identifying the user by comparing provided parameters
11
+ (usually login or email and password) with the credentials stored in the
12
+ application database and selecting the matched one. This module provides
13
+ a simple login form and related helpers methods including saving and
14
+ restoring user location.
15
+
16
+ #### Usage
17
+
18
+ ##### Holding a session
19
+
20
+ Make sure you have sessions enabled: `enable :sessions`.
21
+
22
+ If you use a different persistence storage you will have to make #session hash
23
+ available to call in your app. `Padrino::Login` stores credentials `id` in
24
+ session[settings.session_key]. You can customize session_key by calling
25
+ `set :session_key, :current_credentials_id`. Default session_key name is
26
+ `"_login_#{app.app_name}"`.
27
+
28
+ ##### Taming a model
29
+
30
+ By default `Padrino::Login` uses credentials model name `Account`. You can
31
+ customize it by setting `set :login_model, :user`.
32
+
33
+ Usually a set of credentials are stored in application database. To access
34
+ stored credentials `Padrino::Login` uses `Account.authenticate` class method.
35
+ The Account model must provide this class method in at least two forms:
36
+ authenticate with email/password, or with id. `Account.authenticate` is called
37
+
38
+ * with hash like `{ :email => 'user@example.com', :password => 'mypass' }`
39
+ to authenticate by email and password in response to user request
40
+ * with hash `{ :id => 42 }` to restore credentials from session
41
+
42
+ ##### Accessing credentials (optional)
43
+
44
+ To be able to access current credentials of the signed user your app will
45
+ have to provide application helpers. By default `Padrino::Login` adds simple
46
+ reader and writer with names `credentials` and `credentials=(user)`. You can
47
+ customize helper name by setting `set :credentials_accessor, :visitor` and you
48
+ can override default accessor by defining your own reader and writer helpers
49
+ with the said names.
50
+
51
+ * the reader is called before checking if the user is signed in
52
+ * the writer is called after authenticating user's credentials or restoring
53
+ it from session
54
+
55
+ ##### Bypassing authentication (optional)
56
+
57
+ By default this option is disabled. To enable it you can call
58
+ `enable :login_bypass`.
59
+
60
+ In development environment it sometimes is convenient to be able to bypass
61
+ authentication. If you do this you also have to extend your model
62
+ `Account.authenticate` class method to be able to return default credentials in
63
+ response to hash `{ :bypass => true }`. This way if the user authenticates with
64
+ parameter `bypass` she will be assigned the credentials returned by you model
65
+ and redirected to the stored location.
66
+
67
+ ##### Customizing the login process (optional)
68
+
69
+ By default `Padrino::Login` registers a simple login controller for your app
70
+ and binds it to `/login`.
71
+
72
+ To customize this url it you can call `set :login_url, '/signin'`. Also it's
73
+ possible to disable registering the default controller by calling
74
+ `disable :login_controller`. If you do so you should provide a controller for
75
+ your custom login url which on request of authentication will call
76
+ `#authenticate` helper. If the result is true it should call
77
+ `#restore_location`, else it should show an error. Or you can do whatever you
78
+ like, it's your controller after all.
79
+
80
+ ##### Synergizing with Padrino::Access and other things (info)
81
+
82
+ `Padrino::Login` tries to inform `Padrino::Access` that `/login` url should be
83
+ accessible for unauthenticated users by setting default
84
+ `set(:login_permissions) { set_access(:*, :allow => :*, :with => :login) }`.
85
+ Yes, it's a Proc and `Padrino::Access` tries to call it when registers itself.
86
+
87
+ If you name your controller another way you must redefine this. If you use
88
+ another authorization solution you also should configure it to allow visiting
89
+ `/login` url without having to authenticate.
90
+
91
+ ##### Redirecting the user (info)
92
+
93
+ To authenticate users `Padrino::Login` defines Sinatra `before_filter` which
94
+ checks things and acts accordingly.
95
+
96
+ The first thing is if the user is already logged in. It uses the credentials
97
+ reader we mentioned before, or tries to restore credentials from session.
98
+
99
+ The second thing is if the user is authorized to look at the requested page.
100
+ To do so `Padrino::Login` calls `#authorized?` helper and checks it's bollean
101
+ result. If this helper does not exist then your app is considered not requiring
102
+ authorization. If it exists and responds with `true` then `before_filter`
103
+ passes. If the helper exists and returns `false` then the user's location
104
+ is saved and the user herself is redirected to login url.
105
+
106
+ `#authorized?` helper should be defined in your app if you want access control.
107
+
108
+ ##### Finally registering
109
+
110
+ Call `register Padrino::Login` and you are ready to roll.
111
+
112
+ ### Padrino::Access, an authorization module for Padrino
113
+
114
+ https://github.com/padrino/padrino-framework/wiki/Padrino-authorization-module
115
+
116
+ ### Examples
117
+
118
+ [EXAMPLES.md](EXAMPLES.md)
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ RAKE_ROOT = __FILE__
2
+
3
+ require 'rubygems'
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new(:test) do |test|
7
+ test.libs << 'test'
8
+ test.test_files = Dir['test/**/test_*.rb']
9
+ test.verbose = true
10
+ end
11
+
12
+ task :default => :test