padrino-auth 0.0.12

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.
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