padrino-auth 0.0.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +34 -0
- data/EXAMPLES.md +249 -0
- data/Gemfile +2 -0
- data/LICENSE +21 -0
- data/README.md +118 -0
- data/Rakefile +12 -0
- data/lib/padrino-auth.rb +2 -0
- data/lib/padrino-auth/access.rb +148 -0
- data/lib/padrino-auth/login.rb +138 -0
- data/lib/padrino-auth/login/controller.rb +20 -0
- data/lib/padrino-auth/login/layouts/layout.slim +10 -0
- data/lib/padrino-auth/login/new.slim +35 -0
- data/lib/padrino-auth/permissions.rb +180 -0
- data/lib/padrino-auth/version.rb +5 -0
- data/padrino-auth.gemspec +29 -0
- data/test/helper.rb +68 -0
- data/test/test_padrino_access.rb +124 -0
- data/test/test_padrino_auth.rb +38 -0
- data/test/test_padrino_login.rb +81 -0
- metadata +154 -0
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
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)
|