shield 0.0.0 → 0.0.1
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.markdown +33 -2
- data/lib/shield.rb +3 -14
- data/lib/shield/helpers.rb +26 -7
- data/lib/shield/model.rb +35 -0
- data/test/helper.rb +0 -6
- data/test/model_test.rb +45 -0
- data/test/shield_test.rb +114 -1
- data/test/sinatra_test.rb +43 -29
- metadata +8 -69
- data/README.rb +0 -73
- data/lib/shield/login.rb +0 -38
- data/lib/shield/template/basic_user.rb +0 -37
- data/lib/shield/template/flexi_user.rb +0 -22
- data/lib/shield/template/user.rb +0 -32
- data/test/basic_user_test.rb +0 -37
- data/test/flexi_user_test.rb +0 -40
- data/test/login_middleware_test.rb +0 -59
- data/test/login_rack_mounting_test.rb +0 -63
- data/test/mounted_middleware_test.rb +0 -96
- data/test/user_test.rb +0 -33
data/README.markdown
CHANGED
@@ -1,5 +1,36 @@
|
|
1
1
|
# Shield
|
2
2
|
|
3
|
-
|
3
|
+
Shield
|
4
4
|
|
5
|
-
|
5
|
+
_n. A solid piece of metal code used to protect your application._
|
6
|
+
|
7
|
+
## Why another authentication library?
|
8
|
+
|
9
|
+
1. Because most of the other libraries are too huge.
|
10
|
+
2. Extending other libraries is a pain.
|
11
|
+
3. Writing code is fun :-)
|
12
|
+
|
13
|
+
## Description of Shield
|
14
|
+
|
15
|
+
1. Simple
|
16
|
+
2. Doesn't get in the way
|
17
|
+
3. Extensible (see [shield-contrib][shield-contrib]).
|
18
|
+
|
19
|
+
## Getting started
|
20
|
+
|
21
|
+
The fastest way to get started is by using one of the drop-in solutions
|
22
|
+
in [shield-contrib][shield-contrib].
|
23
|
+
|
24
|
+
## Tutorials
|
25
|
+
|
26
|
+
You can learn more by reading through some of our tutorials:
|
27
|
+
|
28
|
+
1. [Sinatra & OHM][sin-ohm]
|
29
|
+
2. [Sinatra & Sequel][sin-sequel]
|
30
|
+
|
31
|
+
|
32
|
+
[sin]: http://sinatrarb.com
|
33
|
+
[ohm]: http://ohm.keyvalue.org
|
34
|
+
[shield-contrib]: http://github.com/cyx/shield-contrib
|
35
|
+
[sin-ohm]: http://cyx.github.com/shield/sinatra-ohm.html
|
36
|
+
[sin-sequel]: http://cyx.github.com/shield/sinatra-sequel.html
|
data/lib/shield.rb
CHANGED
@@ -1,18 +1,7 @@
|
|
1
1
|
module Shield
|
2
|
-
VERSION = "0.0.
|
2
|
+
VERSION = "0.0.1"
|
3
3
|
|
4
|
-
autoload :BasicUser, "shield/template/basic_user"
|
5
|
-
autoload :User, "shield/template/user"
|
6
|
-
autoload :FlexiUser, "shield/template/flexi_user"
|
7
4
|
autoload :Password, "shield/password"
|
8
|
-
autoload :Login, "shield/login"
|
9
5
|
autoload :Helpers, "shield/helpers"
|
10
|
-
|
11
|
-
|
12
|
-
app.helpers Helpers
|
13
|
-
|
14
|
-
app.use Login do |m|
|
15
|
-
m.settings.set :views, app.views if app.views
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
6
|
+
autoload :Model, "shield/model"
|
7
|
+
end
|
data/lib/shield/helpers.rb
CHANGED
@@ -1,22 +1,41 @@
|
|
1
1
|
module Shield
|
2
2
|
module Helpers
|
3
|
-
def ensure_authenticated
|
4
|
-
return if
|
3
|
+
def ensure_authenticated(model)
|
4
|
+
return if authenticated(model)
|
5
5
|
|
6
6
|
session[:return_to] = request.fullpath
|
7
|
-
|
7
|
+
redirect_to_login
|
8
8
|
end
|
9
9
|
|
10
|
-
def
|
11
|
-
|
10
|
+
def authenticated(model)
|
11
|
+
@_authenticated ||= {}
|
12
|
+
@_authenticated[model] ||= model[session[model.to_s]]
|
12
13
|
end
|
13
14
|
|
14
|
-
def
|
15
|
-
|
15
|
+
def redirect_to_login
|
16
|
+
redirect "/login"
|
16
17
|
end
|
17
18
|
|
18
19
|
def redirect_to_stored(default = "/")
|
19
20
|
redirect(session.delete(:return_to) || default)
|
20
21
|
end
|
22
|
+
|
23
|
+
def login(model, username, password)
|
24
|
+
instance = model.authenticate(username, password)
|
25
|
+
|
26
|
+
if instance
|
27
|
+
session[model.to_s] = instance.id
|
28
|
+
return true
|
29
|
+
else
|
30
|
+
return false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def logout(model)
|
35
|
+
session.delete(model.to_s)
|
36
|
+
session.delete(:return_to)
|
37
|
+
|
38
|
+
@_authenticated.delete(model) if defined?(@_authenticated)
|
39
|
+
end
|
21
40
|
end
|
22
41
|
end
|
data/lib/shield/model.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
module Shield
|
2
|
+
module Model
|
3
|
+
def authenticate(username, password)
|
4
|
+
user = fetch(username)
|
5
|
+
|
6
|
+
if user and is_valid_password?(user, password)
|
7
|
+
return user
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def fetch(login)
|
12
|
+
raise FetchMissing
|
13
|
+
end
|
14
|
+
|
15
|
+
def is_valid_password?(user, password)
|
16
|
+
Shield::Password.check(password, user.crypted_password)
|
17
|
+
end
|
18
|
+
|
19
|
+
class FetchMissing < Class.new(StandardError)
|
20
|
+
def message
|
21
|
+
%{
|
22
|
+
!! You need to implement `fetch`.
|
23
|
+
Below is a quick example implementation (in Ohm):
|
24
|
+
|
25
|
+
def fetch(email)
|
26
|
+
find(:email => email).first
|
27
|
+
end
|
28
|
+
|
29
|
+
For more example implementations, check out
|
30
|
+
http://github.com/cyx/shield-contrib
|
31
|
+
}.gsub(/^ {10}/, "")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/test/helper.rb
CHANGED
data/test/model_test.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
class User < Struct.new(:crypted_password)
|
4
|
+
extend Shield::Model
|
5
|
+
end
|
6
|
+
|
7
|
+
test "fetch" do
|
8
|
+
ex = nil
|
9
|
+
|
10
|
+
begin
|
11
|
+
User.fetch("quentin")
|
12
|
+
rescue Exception => e
|
13
|
+
ex = e
|
14
|
+
end
|
15
|
+
|
16
|
+
assert ex.kind_of?(Shield::Model::FetchMissing)
|
17
|
+
assert Shield::Model::FetchMissing.new.message == ex.message
|
18
|
+
end
|
19
|
+
|
20
|
+
test "is_valid_password?" do
|
21
|
+
user = User.new(Shield::Password.encrypt("password"))
|
22
|
+
|
23
|
+
assert User.is_valid_password?(user, "password")
|
24
|
+
assert ! User.is_valid_password?(user, "password1")
|
25
|
+
end
|
26
|
+
|
27
|
+
class User
|
28
|
+
class << self
|
29
|
+
attr_accessor :fetched
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.fetch(username)
|
33
|
+
return fetched if username == "quentin"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
test "authenticate" do
|
38
|
+
user = User.new(Shield::Password.encrypt("pass"))
|
39
|
+
|
40
|
+
User.fetched = user
|
41
|
+
|
42
|
+
assert user == User.authenticate("quentin", "pass")
|
43
|
+
assert nil == User.authenticate("unknown", "pass")
|
44
|
+
assert nil == User.authenticate("quentin", "wrongpass")
|
45
|
+
end
|
data/test/shield_test.rb
CHANGED
@@ -1 +1,114 @@
|
|
1
|
-
require File.expand_path("helper", File.dirname(__FILE__))
|
1
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
class User < Struct.new(:id)
|
4
|
+
extend Shield::Model
|
5
|
+
|
6
|
+
def self.[](id)
|
7
|
+
User.new(1) unless id.to_s.empty?
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.authenticate(username, password)
|
11
|
+
User.new(1001) if username == "quentin" && password == "password"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Context
|
16
|
+
def initialize(path)
|
17
|
+
@path = path
|
18
|
+
end
|
19
|
+
|
20
|
+
def session
|
21
|
+
@session ||= {}
|
22
|
+
end
|
23
|
+
|
24
|
+
class Request < Struct.new(:fullpath)
|
25
|
+
end
|
26
|
+
|
27
|
+
def request
|
28
|
+
Request.new(@path)
|
29
|
+
end
|
30
|
+
|
31
|
+
def redirect(redirect = nil)
|
32
|
+
@redirect = redirect if redirect
|
33
|
+
@redirect
|
34
|
+
end
|
35
|
+
|
36
|
+
include Shield::Helpers
|
37
|
+
end
|
38
|
+
|
39
|
+
setup do
|
40
|
+
Context.new("/events/1")
|
41
|
+
end
|
42
|
+
|
43
|
+
test "ensure_authenticated when logged out" do |context|
|
44
|
+
context.ensure_authenticated(User)
|
45
|
+
assert "/events/1" == context.session[:return_to]
|
46
|
+
assert "/login" == context.redirect
|
47
|
+
end
|
48
|
+
|
49
|
+
test "ensure_authenticated when logged in" do |context|
|
50
|
+
context.session["User"] = 1
|
51
|
+
assert nil == context.ensure_authenticated(User)
|
52
|
+
assert nil == context.redirect
|
53
|
+
assert nil == context.session[:return_to]
|
54
|
+
end
|
55
|
+
|
56
|
+
class Admin < Struct.new(:id)
|
57
|
+
def self.[](id)
|
58
|
+
new(id) unless id.to_s.empty?
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
test "authenticated" do |context|
|
63
|
+
context.session["User"] = 1
|
64
|
+
|
65
|
+
assert User.new(1) == context.authenticated(User)
|
66
|
+
assert nil == context.authenticated(Admin)
|
67
|
+
end
|
68
|
+
|
69
|
+
test "caches authenticated in @_authenticated" do |context|
|
70
|
+
context.session["User"] = 1
|
71
|
+
context.authenticated(User)
|
72
|
+
|
73
|
+
assert User.new(1) == context.instance_variable_get(:@_authenticated)[User]
|
74
|
+
end
|
75
|
+
|
76
|
+
test "redirect to stored when :return_to is set" do |context|
|
77
|
+
context.session[:return_to] = "/private"
|
78
|
+
context.redirect_to_stored
|
79
|
+
|
80
|
+
assert "/private" == context.redirect
|
81
|
+
assert nil == context.session[:return_to]
|
82
|
+
end
|
83
|
+
|
84
|
+
test "redirect to stored when no return to" do |context|
|
85
|
+
context.redirect_to_stored
|
86
|
+
assert "/" == context.redirect
|
87
|
+
|
88
|
+
context.redirect_to_stored("/custom")
|
89
|
+
assert "/custom" == context.redirect
|
90
|
+
end
|
91
|
+
|
92
|
+
test "login success" do |context|
|
93
|
+
assert context.login(User, "quentin", "password")
|
94
|
+
assert 1001 == context.session["User"]
|
95
|
+
end
|
96
|
+
|
97
|
+
test "login failure" do |context|
|
98
|
+
assert false == context.login(User, "wrong", "creds")
|
99
|
+
assert nil == context.session["User"]
|
100
|
+
end
|
101
|
+
|
102
|
+
test "logout" do |context|
|
103
|
+
context.session["User"] = 1001
|
104
|
+
context.session[:return_to] = "/foo"
|
105
|
+
|
106
|
+
# Now let's make it memoize the User
|
107
|
+
context.authenticated(User)
|
108
|
+
|
109
|
+
context.logout(User)
|
110
|
+
|
111
|
+
assert nil == context.session["User"]
|
112
|
+
assert nil == context.session[:return_to]
|
113
|
+
assert nil == context.authenticated(User)
|
114
|
+
end
|
data/test/sinatra_test.rb
CHANGED
@@ -1,21 +1,47 @@
|
|
1
1
|
require File.expand_path("helper", File.dirname(__FILE__))
|
2
2
|
|
3
|
-
class User <
|
3
|
+
class User < Struct.new(:id)
|
4
|
+
extend Shield::Model
|
5
|
+
|
6
|
+
def self.[](id)
|
7
|
+
User.new(1) unless id.to_s.empty?
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.authenticate(username, password)
|
11
|
+
User.new(1001) if username == "quentin" && password == "password"
|
12
|
+
end
|
4
13
|
end
|
5
14
|
|
6
15
|
class SinatraApp < Sinatra::Base
|
7
16
|
enable :sessions
|
8
|
-
|
17
|
+
helpers Shield::Helpers
|
9
18
|
|
10
19
|
get "/public" do
|
11
20
|
"Public"
|
12
21
|
end
|
13
22
|
|
14
23
|
get "/private" do
|
15
|
-
ensure_authenticated
|
24
|
+
ensure_authenticated(User)
|
16
25
|
|
17
26
|
"Private"
|
18
27
|
end
|
28
|
+
|
29
|
+
get "/login" do
|
30
|
+
"Login"
|
31
|
+
end
|
32
|
+
|
33
|
+
post "/login" do
|
34
|
+
if login(User, params[:login], params[:password])
|
35
|
+
redirect_to_stored
|
36
|
+
else
|
37
|
+
redirect_to_login
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
get "/logout" do
|
42
|
+
logout(User)
|
43
|
+
redirect "/"
|
44
|
+
end
|
19
45
|
end
|
20
46
|
|
21
47
|
scope do
|
@@ -24,9 +50,7 @@ scope do
|
|
24
50
|
end
|
25
51
|
|
26
52
|
setup do
|
27
|
-
|
28
|
-
:password => "password",
|
29
|
-
:password_confirmation => "password")
|
53
|
+
clear_cookies
|
30
54
|
end
|
31
55
|
|
32
56
|
test "public" do
|
@@ -34,40 +58,30 @@ scope do
|
|
34
58
|
assert "Public" == last_response.body
|
35
59
|
end
|
36
60
|
|
37
|
-
test "
|
61
|
+
test "successful logging in" do
|
38
62
|
get "/private"
|
39
63
|
assert_redirected_to "/login"
|
40
64
|
assert "/private" == session[:return_to]
|
41
65
|
|
42
|
-
post "/login", :login => "quentin
|
66
|
+
post "/login", :login => "quentin", :password => "password"
|
43
67
|
assert_redirected_to "/private"
|
44
|
-
end
|
45
68
|
|
46
|
-
|
47
|
-
get "/login"
|
48
|
-
|
49
|
-
doc = Nokogiri(%{<div>#{last_response.body}</div>})
|
50
|
-
|
51
|
-
assert 2 == doc.search("form > fieldset > label > input").size
|
52
|
-
assert 1 == doc.search("form > fieldset > button").size
|
69
|
+
assert 1001 == session["User"]
|
53
70
|
end
|
54
|
-
end
|
55
|
-
|
56
|
-
class LoginCustomized < Sinatra::Base
|
57
|
-
enable :sessions
|
58
|
-
set :views, File.join(File.dirname(__FILE__), "fixtures", "views")
|
59
71
|
|
60
|
-
|
61
|
-
|
72
|
+
test "failed login" do
|
73
|
+
post "/login", :login => "q", :password => "p"
|
74
|
+
assert_redirected_to "/login"
|
62
75
|
|
63
|
-
|
64
|
-
def app
|
65
|
-
LoginCustomized.new
|
76
|
+
assert nil == session["User"]
|
66
77
|
end
|
67
78
|
|
68
|
-
test "
|
69
|
-
|
79
|
+
test "logging out" do
|
80
|
+
post "/login", :login => "quentin", :password => "password"
|
81
|
+
|
82
|
+
get "/logout"
|
70
83
|
|
71
|
-
assert
|
84
|
+
assert nil == session["User"]
|
85
|
+
assert nil == session[:return_to]
|
72
86
|
end
|
73
87
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Michel Martens
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2010-10-
|
19
|
+
date: 2010-10-21 00:00:00 +08:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -46,7 +46,7 @@ dependencies:
|
|
46
46
|
type: :development
|
47
47
|
version_requirements: *id002
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
|
-
name:
|
49
|
+
name: rack-test
|
50
50
|
prerelease: false
|
51
51
|
requirement: &id003 !ruby/object:Gem::Requirement
|
52
52
|
none: false
|
@@ -58,59 +58,7 @@ dependencies:
|
|
58
58
|
version: "0"
|
59
59
|
type: :development
|
60
60
|
version_requirements: *id003
|
61
|
-
|
62
|
-
name: rack-test
|
63
|
-
prerelease: false
|
64
|
-
requirement: &id004 !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
|
-
requirements:
|
67
|
-
- - ">="
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
segments:
|
70
|
-
- 0
|
71
|
-
version: "0"
|
72
|
-
type: :development
|
73
|
-
version_requirements: *id004
|
74
|
-
- !ruby/object:Gem::Dependency
|
75
|
-
name: ohm
|
76
|
-
prerelease: false
|
77
|
-
requirement: &id005 !ruby/object:Gem::Requirement
|
78
|
-
none: false
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
segments:
|
83
|
-
- 0
|
84
|
-
version: "0"
|
85
|
-
type: :development
|
86
|
-
version_requirements: *id005
|
87
|
-
- !ruby/object:Gem::Dependency
|
88
|
-
name: ohm-contrib
|
89
|
-
prerelease: false
|
90
|
-
requirement: &id006 !ruby/object:Gem::Requirement
|
91
|
-
none: false
|
92
|
-
requirements:
|
93
|
-
- - ">="
|
94
|
-
- !ruby/object:Gem::Version
|
95
|
-
segments:
|
96
|
-
- 0
|
97
|
-
version: "0"
|
98
|
-
type: :development
|
99
|
-
version_requirements: *id006
|
100
|
-
- !ruby/object:Gem::Dependency
|
101
|
-
name: nokogiri
|
102
|
-
prerelease: false
|
103
|
-
requirement: &id007 !ruby/object:Gem::Requirement
|
104
|
-
none: false
|
105
|
-
requirements:
|
106
|
-
- - ">="
|
107
|
-
- !ruby/object:Gem::Version
|
108
|
-
segments:
|
109
|
-
- 0
|
110
|
-
version: "0"
|
111
|
-
type: :development
|
112
|
-
version_requirements: *id007
|
113
|
-
description: "\n Gets you 80-90% of the way regarding your authentication\n requirements. Provides convenience helper functions which you can\n use with your favorite web framework of choice.\n "
|
61
|
+
description: "\n Provides all the protocol you need in order to do authentication on\n your rack application. The implementation specifics can be found in\n http://github.com/cyx/shield-contrib\n "
|
114
62
|
email:
|
115
63
|
- michel@soveran.com
|
116
64
|
- djanowski@dimaion.com
|
@@ -123,26 +71,17 @@ extra_rdoc_files: []
|
|
123
71
|
|
124
72
|
files:
|
125
73
|
- lib/shield/helpers.rb
|
126
|
-
- lib/shield/
|
74
|
+
- lib/shield/model.rb
|
127
75
|
- lib/shield/password.rb
|
128
|
-
- lib/shield/template/basic_user.rb
|
129
|
-
- lib/shield/template/flexi_user.rb
|
130
|
-
- lib/shield/template/user.rb
|
131
76
|
- lib/shield.rb
|
132
77
|
- README.markdown
|
133
|
-
- README.rb
|
134
78
|
- LICENSE
|
135
79
|
- Rakefile
|
136
|
-
- test/basic_user_test.rb
|
137
|
-
- test/flexi_user_test.rb
|
138
80
|
- test/helper.rb
|
139
|
-
- test/
|
140
|
-
- test/login_rack_mounting_test.rb
|
141
|
-
- test/mounted_middleware_test.rb
|
81
|
+
- test/model_test.rb
|
142
82
|
- test/password_hash_test.rb
|
143
83
|
- test/shield_test.rb
|
144
84
|
- test/sinatra_test.rb
|
145
|
-
- test/user_test.rb
|
146
85
|
has_rdoc: true
|
147
86
|
homepage: http://github.com/cyx/shield
|
148
87
|
licenses: []
|
@@ -174,6 +113,6 @@ rubyforge_project: shield
|
|
174
113
|
rubygems_version: 1.3.7
|
175
114
|
signing_key:
|
176
115
|
specification_version: 3
|
177
|
-
summary:
|
116
|
+
summary: Generic authentication protocol for rack applications.
|
178
117
|
test_files: []
|
179
118
|
|
data/README.rb
DELETED
@@ -1,73 +0,0 @@
|
|
1
|
-
## Shield
|
2
|
-
|
3
|
-
# A simple authentication framework for use with a basic Rack application.
|
4
|
-
# It doesn't try to be generic and instead has the following assertions:
|
5
|
-
|
6
|
-
# 1. You use [Ohm][ohm] for your persistence layer.
|
7
|
-
# 2. That your app has some form of a Helper plugin system.
|
8
|
-
|
9
|
-
## Usage
|
10
|
-
|
11
|
-
# The fastest way to get started with `Shield` is to use it as a [Sinatra][sin]
|
12
|
-
# extension on top of your main application.
|
13
|
-
|
14
|
-
# We simply have to require sinatra, shield, and [Ohm][ohm].
|
15
|
-
require "sinatra/base"
|
16
|
-
require "shield"
|
17
|
-
require "ohm"
|
18
|
-
|
19
|
-
# We then define a `User` class on the top-level namespace:
|
20
|
-
class User < Shield::User
|
21
|
-
end
|
22
|
-
|
23
|
-
# Our sinatra application can be a classic style, but for this example,
|
24
|
-
# we'll use the more modular style extending from `Sinatra::Base`.
|
25
|
-
class App < Sinatra::Base
|
26
|
-
# One very very important detail to remember is that you need to
|
27
|
-
# have session support for all of Shield to work.
|
28
|
-
enable :sessions
|
29
|
-
|
30
|
-
# Now for the main highlight: This line simply does two things:
|
31
|
-
#
|
32
|
-
# 1. It adds `Shield::Helpers` to your Sinatra app's helpers.
|
33
|
-
# 2. It adds `Shield::Login` as a middleware for your application.
|
34
|
-
#
|
35
|
-
register Shield
|
36
|
-
|
37
|
-
# This is a normal Sinatra route. No authentication is needed here.
|
38
|
-
get "/public" do
|
39
|
-
"Public"
|
40
|
-
end
|
41
|
-
|
42
|
-
# This is a private Sinatra route, which we enforce using
|
43
|
-
# `ensure_authenticated`. This method comes from `Shield::Helpers` along
|
44
|
-
# with some other helper methods:
|
45
|
-
#
|
46
|
-
# 1. `current_user` - ain't this the standard nowadays?
|
47
|
-
# 2. `logged_in?` - simple sugar to verify if the user is logged in.
|
48
|
-
# 3. `ensure_authenticated` - as shown here.
|
49
|
-
# 4. `redirect_to_stored` - redirects to the previous url the user was
|
50
|
-
# trying to access prior to authenticating.
|
51
|
-
get "/private" do
|
52
|
-
ensure_authenticated
|
53
|
-
|
54
|
-
"Private"
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
# If this file was run directly i.e. ruby README.rb.
|
59
|
-
if __FILE__ == $0
|
60
|
-
# For the purposes of this quick script, let's create a User. You'll use that
|
61
|
-
# to quickly verify that you can indeed login to the application.
|
62
|
-
if User.all.size == 0
|
63
|
-
User.create(:email => "quentin@test.com",
|
64
|
-
:password => "password",
|
65
|
-
:password_confirmation => "password")
|
66
|
-
end
|
67
|
-
|
68
|
-
# Let Sinatra take the stage.
|
69
|
-
App.run!
|
70
|
-
end
|
71
|
-
|
72
|
-
# [ohm]: http://ohm.keyvalue.org
|
73
|
-
# [sin]: http://sinatrarb.com
|
data/lib/shield/login.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
require "sinatra/base"
|
2
|
-
|
3
|
-
module Shield
|
4
|
-
class Login < Sinatra::Base
|
5
|
-
enable :sessions
|
6
|
-
helpers Helpers
|
7
|
-
|
8
|
-
set :views, File.join(File.dirname(__FILE__), "..", "..", "views")
|
9
|
-
|
10
|
-
set :auth_success_message, "You have successfully logged in."
|
11
|
-
set :auth_failure_message, "Wrong Username and/or Password combination."
|
12
|
-
|
13
|
-
get "/login/?" do
|
14
|
-
haml :login
|
15
|
-
end
|
16
|
-
|
17
|
-
post "/login/?" do
|
18
|
-
user = ::User.authenticate(params[:login], params[:password])
|
19
|
-
|
20
|
-
if user
|
21
|
-
session[:success] = settings.auth_success_message
|
22
|
-
session[:user] = user.id
|
23
|
-
|
24
|
-
redirect_to_stored
|
25
|
-
else
|
26
|
-
session[:error] = settings.auth_failure_message
|
27
|
-
redirect "/login"
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
get "/logout/?" do
|
32
|
-
session.delete(:user)
|
33
|
-
session.delete(:return_to)
|
34
|
-
|
35
|
-
redirect "/"
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
module Shield
|
2
|
-
class BasicUser < Ohm::Model
|
3
|
-
Unimplemented = Class.new(StandardError)
|
4
|
-
|
5
|
-
def self.inherited(model)
|
6
|
-
model.attribute :crypted_password
|
7
|
-
end
|
8
|
-
|
9
|
-
# TODO : change this if Ohm decides on implementing subclassing.
|
10
|
-
def self._copy_attributes_indices_counters(model)
|
11
|
-
attributes.each { |att| model.attribute(att) }
|
12
|
-
indices.each { |att| model.index(att) }
|
13
|
-
counters.each { |att| model.counter(att) }
|
14
|
-
end
|
15
|
-
|
16
|
-
attr_reader :password, :password_confirmation
|
17
|
-
attr_writer :password_confirmation
|
18
|
-
|
19
|
-
def self.authenticate(login, password)
|
20
|
-
user = find_by_login(login)
|
21
|
-
|
22
|
-
if user && Shield::Password.check(password, user.crypted_password)
|
23
|
-
return user
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.find_by_login(login)
|
28
|
-
raise Unimplemented
|
29
|
-
end
|
30
|
-
|
31
|
-
def password=(password)
|
32
|
-
write_local :crypted_password, Shield::Password.encrypt(password)
|
33
|
-
|
34
|
-
@password = password
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
module Shield
|
2
|
-
class FlexiUser < User
|
3
|
-
def self.inherited(model)
|
4
|
-
model.attribute :username
|
5
|
-
model.index :username
|
6
|
-
|
7
|
-
_copy_attributes_indices_counters(model)
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.find_by_login(login)
|
11
|
-
super or find(:username => login).first
|
12
|
-
end
|
13
|
-
|
14
|
-
def validate
|
15
|
-
super
|
16
|
-
|
17
|
-
assert_present(:username) &&
|
18
|
-
assert_format(:username, /\A[a-z][a-z0-9\-\_\.]{2,}\z/) &&
|
19
|
-
assert_unique(:username)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
data/lib/shield/template/user.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
require "ohm/contrib"
|
2
|
-
|
3
|
-
module Shield
|
4
|
-
class User < BasicUser
|
5
|
-
include Ohm::WebValidations
|
6
|
-
|
7
|
-
def self.inherited(model)
|
8
|
-
model.attribute :email
|
9
|
-
model.index :email
|
10
|
-
|
11
|
-
_copy_attributes_indices_counters(model)
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.find_by_login(login)
|
15
|
-
find(:email => login).first
|
16
|
-
end
|
17
|
-
|
18
|
-
def validate
|
19
|
-
super
|
20
|
-
|
21
|
-
assert_present(:email) && assert_email(:email) && assert_unique(:email)
|
22
|
-
|
23
|
-
if new?
|
24
|
-
assert_present :password
|
25
|
-
end
|
26
|
-
|
27
|
-
unless password.to_s.empty?
|
28
|
-
assert password == password_confirmation, [:password, :not_confirmed]
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
data/test/basic_user_test.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
require File.expand_path("helper", File.dirname(__FILE__))
|
2
|
-
|
3
|
-
class BasicUser < Shield::BasicUser
|
4
|
-
end
|
5
|
-
|
6
|
-
setup do
|
7
|
-
BasicUser.create(:password => "password")
|
8
|
-
end
|
9
|
-
|
10
|
-
test "allows password checks at the minimum" do |u|
|
11
|
-
assert Shield::Password.check("password", u.crypted_password)
|
12
|
-
end
|
13
|
-
|
14
|
-
test "no find_by_login" do
|
15
|
-
assert_raise Shield::BasicUser::Unimplemented do
|
16
|
-
BasicUser.authenticate("quentin", "password")
|
17
|
-
end
|
18
|
-
|
19
|
-
assert_raise Shield::BasicUser::Unimplemented do
|
20
|
-
BasicUser.find_by_login("quentin")
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
test "has password confirmation accesors" do |u|
|
25
|
-
u.password_confirmation = "pass"
|
26
|
-
|
27
|
-
assert "pass" == u.password_confirmation
|
28
|
-
end
|
29
|
-
|
30
|
-
test "writes the new crypted password on set" do
|
31
|
-
u = BasicUser.new(:password => "mypass")
|
32
|
-
assert Shield::Password.check("mypass", u.crypted_password)
|
33
|
-
|
34
|
-
u.save
|
35
|
-
u = BasicUser[u.id]
|
36
|
-
assert Shield::Password.check("mypass", u.crypted_password)
|
37
|
-
end
|
data/test/flexi_user_test.rb
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
require File.expand_path("helper", File.dirname(__FILE__))
|
2
|
-
|
3
|
-
class FlexiUser < Shield::FlexiUser
|
4
|
-
end
|
5
|
-
|
6
|
-
setup do
|
7
|
-
FlexiUser.create(:email => "quentin@test.com",
|
8
|
-
:username => "quentin",
|
9
|
-
:password => "password",
|
10
|
-
:password_confirmation => "password")
|
11
|
-
end
|
12
|
-
|
13
|
-
test "find_by_login" do |u|
|
14
|
-
assert u == FlexiUser.find_by_login("quentin@test.com")
|
15
|
-
assert u == FlexiUser.find_by_login("quentin")
|
16
|
-
end
|
17
|
-
|
18
|
-
test "authenticate" do |u|
|
19
|
-
assert u == FlexiUser.authenticate("quentin", "password")
|
20
|
-
assert u == FlexiUser.authenticate("quentin@test.com", "password")
|
21
|
-
end
|
22
|
-
|
23
|
-
test "username validation" do |u|
|
24
|
-
u.username = nil
|
25
|
-
assert ! u.valid?
|
26
|
-
assert u.errors.include?([:username, :not_present])
|
27
|
-
|
28
|
-
["1foo", "fo", "foo^", "foo&", "foo#"].each do |username|
|
29
|
-
u.username = username
|
30
|
-
assert ! u.valid?
|
31
|
-
assert u.errors.include?([:username, :format])
|
32
|
-
end
|
33
|
-
|
34
|
-
u.username = "foo"
|
35
|
-
assert u.valid?
|
36
|
-
|
37
|
-
newuser = FlexiUser.new(:username => "quentin")
|
38
|
-
assert ! newuser.valid?
|
39
|
-
assert newuser.errors.include?([:username, :not_unique])
|
40
|
-
end
|
@@ -1,59 +0,0 @@
|
|
1
|
-
require File.expand_path("helper", File.dirname(__FILE__))
|
2
|
-
|
3
|
-
class User < Shield::User
|
4
|
-
end
|
5
|
-
|
6
|
-
class Main < Sinatra::Base
|
7
|
-
enable :sessions
|
8
|
-
helpers Shield::Helpers
|
9
|
-
|
10
|
-
get "/public" do
|
11
|
-
"Public"
|
12
|
-
end
|
13
|
-
|
14
|
-
get "/private" do
|
15
|
-
ensure_authenticated
|
16
|
-
|
17
|
-
"Private"
|
18
|
-
end
|
19
|
-
|
20
|
-
use Shield::Login do |login|
|
21
|
-
login.settings.auth_success_message = "Booya!"
|
22
|
-
login.settings.auth_failure_message = "BOOM!"
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
scope do
|
27
|
-
def app
|
28
|
-
Main.new
|
29
|
-
end
|
30
|
-
|
31
|
-
setup do
|
32
|
-
clear_cookies
|
33
|
-
|
34
|
-
User.create(:email => "quentin@test.com",
|
35
|
-
:password => "password",
|
36
|
-
:password_confirmation => "password")
|
37
|
-
end
|
38
|
-
|
39
|
-
test "public" do
|
40
|
-
get "/public"
|
41
|
-
assert "Public" == last_response.body
|
42
|
-
end
|
43
|
-
|
44
|
-
test "logging in" do
|
45
|
-
post "/login", :login => "quentin@test.com", :password => "password"
|
46
|
-
|
47
|
-
get "/private"
|
48
|
-
assert "Private" == last_response.body
|
49
|
-
assert "Booya!" == session[:success]
|
50
|
-
end
|
51
|
-
|
52
|
-
test "login, logout, login failure" do
|
53
|
-
post "/login", :login => "quentin@test.com", :password => "password"
|
54
|
-
get "/logout"
|
55
|
-
|
56
|
-
post "/login"
|
57
|
-
assert "BOOM!" == session[:error]
|
58
|
-
end
|
59
|
-
end
|
@@ -1,63 +0,0 @@
|
|
1
|
-
require File.expand_path("helper", File.dirname(__FILE__))
|
2
|
-
|
3
|
-
class User < Shield::User
|
4
|
-
end
|
5
|
-
|
6
|
-
class Main < Sinatra::Base
|
7
|
-
enable :sessions
|
8
|
-
helpers Shield::Helpers
|
9
|
-
|
10
|
-
get "/public" do
|
11
|
-
"Public"
|
12
|
-
end
|
13
|
-
|
14
|
-
get "/private" do
|
15
|
-
ensure_authenticated
|
16
|
-
|
17
|
-
"Private"
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
$app = Rack::Builder.app {
|
22
|
-
use Shield::Login
|
23
|
-
|
24
|
-
map "/" do
|
25
|
-
run Main
|
26
|
-
end
|
27
|
-
}
|
28
|
-
|
29
|
-
scope do
|
30
|
-
def app
|
31
|
-
$app
|
32
|
-
end
|
33
|
-
|
34
|
-
setup do
|
35
|
-
clear_cookies
|
36
|
-
|
37
|
-
User.create(:email => "quentin@test.com",
|
38
|
-
:password => "password",
|
39
|
-
:password_confirmation => "password")
|
40
|
-
end
|
41
|
-
|
42
|
-
test "public" do
|
43
|
-
get "/public"
|
44
|
-
|
45
|
-
assert "Public" == last_response.body
|
46
|
-
end
|
47
|
-
|
48
|
-
test "logging in" do
|
49
|
-
post "/login", :login => "quentin@test.com",
|
50
|
-
:password => "password"
|
51
|
-
|
52
|
-
get "/private"
|
53
|
-
assert "Private" == last_response.body
|
54
|
-
end
|
55
|
-
|
56
|
-
test "being redirected and then logging in" do
|
57
|
-
get "/private"
|
58
|
-
assert_redirected_to "/login"
|
59
|
-
|
60
|
-
post "/login", :login => "quentin@test.com", :password => "password"
|
61
|
-
assert_redirected_to "/private"
|
62
|
-
end
|
63
|
-
end
|
@@ -1,96 +0,0 @@
|
|
1
|
-
require File.expand_path("helper", File.dirname(__FILE__))
|
2
|
-
|
3
|
-
class User < Shield::User
|
4
|
-
end
|
5
|
-
|
6
|
-
class App < Sinatra::Base
|
7
|
-
enable :sessions
|
8
|
-
|
9
|
-
helpers Shield::Helpers
|
10
|
-
end
|
11
|
-
|
12
|
-
class Main < App
|
13
|
-
get "/public" do
|
14
|
-
"Public"
|
15
|
-
end
|
16
|
-
|
17
|
-
get "/private" do
|
18
|
-
ensure_authenticated
|
19
|
-
|
20
|
-
"Private"
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
class Admin < App
|
25
|
-
before do
|
26
|
-
ensure_authenticated unless request.fullpath == "/admin/login"
|
27
|
-
end
|
28
|
-
|
29
|
-
get "/events" do
|
30
|
-
"Events"
|
31
|
-
end
|
32
|
-
|
33
|
-
get "/sponsors" do
|
34
|
-
"Sponsors"
|
35
|
-
end
|
36
|
-
|
37
|
-
post "/login" do
|
38
|
-
user = User.authenticate(params[:login], params[:password])
|
39
|
-
|
40
|
-
if user
|
41
|
-
session[:success] = "Success"
|
42
|
-
session[:user] = user.id
|
43
|
-
|
44
|
-
redirect_to_stored
|
45
|
-
else
|
46
|
-
session[:error] = "Failure"
|
47
|
-
redirect "/login"
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
$app = Rack::Builder.app {
|
53
|
-
map "/" do
|
54
|
-
run Main
|
55
|
-
end
|
56
|
-
|
57
|
-
map "/admin" do
|
58
|
-
run Admin
|
59
|
-
end
|
60
|
-
}
|
61
|
-
|
62
|
-
scope do
|
63
|
-
def app
|
64
|
-
$app
|
65
|
-
end
|
66
|
-
|
67
|
-
setup do
|
68
|
-
User.create(:email => "quentin@test.com",
|
69
|
-
:password => "password",
|
70
|
-
:password_confirmation => "password")
|
71
|
-
end
|
72
|
-
|
73
|
-
test "public" do
|
74
|
-
get "/public"
|
75
|
-
|
76
|
-
assert "Public" == last_response.body
|
77
|
-
end
|
78
|
-
|
79
|
-
test "all admin routes" do
|
80
|
-
get "/admin/events"
|
81
|
-
assert_redirected_to "/login"
|
82
|
-
assert "/admin/events" == session[:return_to]
|
83
|
-
|
84
|
-
get "/admin/sponsors"
|
85
|
-
assert_redirected_to "/login"
|
86
|
-
assert "/admin/sponsors" == session[:return_to]
|
87
|
-
end
|
88
|
-
|
89
|
-
test "single sign on" do
|
90
|
-
post "/admin/login", :login => "quentin@test.com",
|
91
|
-
:password => "password"
|
92
|
-
|
93
|
-
get "/private"
|
94
|
-
assert "Private" == last_response.body
|
95
|
-
end
|
96
|
-
end
|
data/test/user_test.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
require File.expand_path("helper", File.dirname(__FILE__))
|
2
|
-
|
3
|
-
class User < Shield::User
|
4
|
-
end
|
5
|
-
|
6
|
-
setup do
|
7
|
-
User.create(:email => "quentin@email.com", :password => "password",
|
8
|
-
:password_confirmation => "password")
|
9
|
-
end
|
10
|
-
|
11
|
-
test "authentication default" do |u|
|
12
|
-
assert u == User.authenticate("quentin@email.com", "password")
|
13
|
-
|
14
|
-
assert nil == User.authenticate("quentin@email.com", "pass")
|
15
|
-
assert nil == User.authenticate("quentin@email.co.uk", "password")
|
16
|
-
end
|
17
|
-
|
18
|
-
test "email validation" do |u|
|
19
|
-
u.email = nil
|
20
|
-
assert ! u.valid?
|
21
|
-
assert u.errors.include?([:email, :not_present])
|
22
|
-
|
23
|
-
u.email = "foobar"
|
24
|
-
assert ! u.valid?
|
25
|
-
assert u.errors.include?([:email, :not_email])
|
26
|
-
|
27
|
-
u.email = "foo@bar.com"
|
28
|
-
u.save
|
29
|
-
|
30
|
-
foo = User.new(:email => "foo@bar.com")
|
31
|
-
assert ! foo.valid?
|
32
|
-
assert foo.errors.include?([:email, :not_unique])
|
33
|
-
end
|