accounts 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/.gitignore +2 -0
- data/.rvmrc +53 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +167 -0
- data/README.mkd +105 -0
- data/Rakefile +24 -0
- data/accounts.gemspec +52 -0
- data/demo/views/change_email.haml +14 -0
- data/demo/views/change_password.haml +36 -0
- data/demo/views/forgot_password.haml +21 -0
- data/demo/views/logon.haml +24 -0
- data/demo/views/register.haml +15 -0
- data/demo/web_app.rb +99 -0
- data/features/change-email.feature +51 -0
- data/features/change-password.feature +89 -0
- data/features/register.feature +42 -0
- data/features/step_definitions/steps.rb +102 -0
- data/features/step_definitions/web_steps.rb +228 -0
- data/features/support/env.rb +18 -0
- data/features/support/paths.rb +29 -0
- data/features/support/unused.rb +29 -0
- data/lib/accounts.rb +79 -0
- data/lib/accounts/configure.rb +122 -0
- data/lib/accounts/helpers.rb +90 -0
- data/lib/accounts/model.rb +126 -0
- data/lib/accounts/version.rb +5 -0
- data/spec/user_model_spec.rb +105 -0
- metadata +263 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
!!!
|
2
|
+
%html{html_attrs}
|
3
|
+
/ Copyright Westside Consulting LLC, Ann Arbor, MI, USA, 2012
|
4
|
+
|
5
|
+
%body.forgot_password
|
6
|
+
|
7
|
+
%h1 Forgot my password
|
8
|
+
|
9
|
+
%form.forgot_password(action="/forgot-password" method="POST")
|
10
|
+
.label_input
|
11
|
+
%label{:for=>"email"} E-mail
|
12
|
+
%input{:name=>"email", :id=>"email", :type=>"text"}
|
13
|
+
.button
|
14
|
+
%button{:type => "submit", } Submit
|
15
|
+
|
16
|
+
%p
|
17
|
+
We will send an e-mail to you with a one-time link that will
|
18
|
+
take you to a page that will allow you to change your password.
|
19
|
+
|
20
|
+
%p
|
21
|
+
Meanwhile, if you remember your password, it will still work until you change it.
|
@@ -0,0 +1,24 @@
|
|
1
|
+
!!!
|
2
|
+
|
3
|
+
%html{html_attrs}
|
4
|
+
/ Copyright Westside Consulting LLC, Ann Arbor, MI, USA, 2012
|
5
|
+
|
6
|
+
%body
|
7
|
+
|
8
|
+
%h1 Sign in
|
9
|
+
|
10
|
+
%form#login_form(action="logon" method="POST")
|
11
|
+
.label_input
|
12
|
+
%label{:for=>"email"} E-mail
|
13
|
+
%input{:name=>"email", :id=>"email", :type=>"text", :value=>@email}
|
14
|
+
.label_input
|
15
|
+
%label{:for=>"password"} Password
|
16
|
+
%input{:name=>"password", :id=>"password", :type=>"password"}
|
17
|
+
.button
|
18
|
+
%button{:type => "submit"} Submit
|
19
|
+
|
20
|
+
.reset_password
|
21
|
+
%a{:href=>"/forgot-password"} I forgot my password.
|
22
|
+
|
23
|
+
.register
|
24
|
+
%a{:href=>"/register"} Register!
|
@@ -0,0 +1,15 @@
|
|
1
|
+
!!!
|
2
|
+
|
3
|
+
%html{html_attrs}
|
4
|
+
/ Copyright Westside Consulting LLC, Ann Arbor, MI, USA, 2012
|
5
|
+
|
6
|
+
%body
|
7
|
+
|
8
|
+
%h1 Register
|
9
|
+
|
10
|
+
%form#login_form{:action => '/register', :method => 'post'}
|
11
|
+
.label_input
|
12
|
+
%label{:for=>"email"} E-mail
|
13
|
+
%input{:name=>"email", :id=>"email", :type=>"text"}
|
14
|
+
.button
|
15
|
+
%button{:type => "submit"} Submit
|
data/demo/web_app.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# Copyright Westside Consulting LLC, Ann Arbor, MI, USA, 2012
|
2
|
+
|
3
|
+
require 'pp'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'bundler/setup'
|
6
|
+
#Bundler.require(:default, :test, :development) # didn't work
|
7
|
+
|
8
|
+
# development
|
9
|
+
require 'mail-store-agent'
|
10
|
+
require 'mail-single_file_delivery'
|
11
|
+
require 'haml'
|
12
|
+
|
13
|
+
# runtime
|
14
|
+
require 'rack'
|
15
|
+
require 'sinatra'
|
16
|
+
require 'thin'
|
17
|
+
require 'data_mapper'
|
18
|
+
require 'dm-types'
|
19
|
+
require 'dm-timestamps'
|
20
|
+
require 'dm-postgres-adapter'
|
21
|
+
require 'mail'
|
22
|
+
require 'logger'
|
23
|
+
require 'accounts'
|
24
|
+
|
25
|
+
class MyWebApp < Sinatra::Base
|
26
|
+
use ::Accounts::Server;
|
27
|
+
|
28
|
+
DataMapper.auto_migrate! # empty database
|
29
|
+
STDERR.puts "WARNING: called DataMapper.auto_migrate! to clear database"
|
30
|
+
|
31
|
+
enable :logging
|
32
|
+
|
33
|
+
# Web app class must define this.
|
34
|
+
# In a production environment you might want to replace this with something like Rack::Session::Pool
|
35
|
+
enable :sessions
|
36
|
+
|
37
|
+
not_found do
|
38
|
+
%Q{Page not found. Go to <a href="home">home page</a>.}
|
39
|
+
end
|
40
|
+
|
41
|
+
error 403 do
|
42
|
+
"Access denied"
|
43
|
+
end
|
44
|
+
|
45
|
+
get '/' do
|
46
|
+
%Q{Welcome visitor! Please <a href="/logon">log on</a>.}
|
47
|
+
end
|
48
|
+
|
49
|
+
get '/welcome' do
|
50
|
+
account = Accounts::Account.get(session[:account_id]) or return 403
|
51
|
+
"Welcome #{account.email}!"
|
52
|
+
end
|
53
|
+
|
54
|
+
get '/logon' do
|
55
|
+
@email = params[:email] || ''
|
56
|
+
haml :logon
|
57
|
+
end
|
58
|
+
|
59
|
+
get '/logout' do
|
60
|
+
session[:account_id] = nil
|
61
|
+
redirect to('/logon')
|
62
|
+
end
|
63
|
+
|
64
|
+
get '/register' do
|
65
|
+
haml :register
|
66
|
+
end
|
67
|
+
|
68
|
+
get '/forgot-password' do
|
69
|
+
haml :forgot_password
|
70
|
+
end
|
71
|
+
|
72
|
+
get '/change-password' do
|
73
|
+
return 403 unless session[:account_id]
|
74
|
+
haml :change_password
|
75
|
+
end
|
76
|
+
|
77
|
+
get '/change-email' do
|
78
|
+
return 403 unless session[:account_id]
|
79
|
+
haml :change_email
|
80
|
+
end
|
81
|
+
|
82
|
+
if app_file == $0
|
83
|
+
STDERR.puts "Running standalone"
|
84
|
+
# Run as stand-along web app
|
85
|
+
Mail.defaults do
|
86
|
+
delivery_method Mail::SingleFileDelivery::Agent, :filename => '/tmp/mail-test-fifo'
|
87
|
+
end
|
88
|
+
require 'sinatra/reloader'
|
89
|
+
register Sinatra::Reloader
|
90
|
+
enable :reloader
|
91
|
+
run!
|
92
|
+
else
|
93
|
+
# Probably running under Cucumber
|
94
|
+
Mail.defaults do
|
95
|
+
delivery_method(:test)
|
96
|
+
Mail::TestMailer.deliveries = MailStoreAgent.new
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Copyright Westside Consulting LLC, Ann Arbor, MI, USA, 2012
|
2
|
+
|
3
|
+
@change-email
|
4
|
+
Feature: Users can change their e-mails
|
5
|
+
|
6
|
+
@email-taken
|
7
|
+
Scenario: Alice cannot change her e-mail to one that is already taken
|
8
|
+
Given I register "alice@wunder.land" with password "caterpillar"
|
9
|
+
And I register "caterpillar@wunder.land" with password "caterpillar"
|
10
|
+
And "alice@wunder.land" is authenticated with password "caterpillar"
|
11
|
+
When Alice visits "/change-email"
|
12
|
+
And she fills in "email" with "caterpillar@wunder.land"
|
13
|
+
And she presses "Submit"
|
14
|
+
Then she should see "caterpillar@wunder.land is already taken"
|
15
|
+
|
16
|
+
@request-change-email
|
17
|
+
Scenario: Alice requests to change email to an available email
|
18
|
+
Given "alice@wunder.land" is registered with password "caterpillar"
|
19
|
+
And "alice@wunder.land" is authenticated with password "caterpillar"
|
20
|
+
When she visits "/change-email"
|
21
|
+
And she fills in "email" with "alice@looking.glass"
|
22
|
+
And she presses "Submit"
|
23
|
+
Then she should see "Check your e-mail"
|
24
|
+
And "alice@wunder.land" opens an email containing "You have requested to change your e-mail to alice@looking.glass"
|
25
|
+
And "alice@looking.glass" should receive an email
|
26
|
+
|
27
|
+
Scenario: Alice can still log on with her previous e-mail
|
28
|
+
Given "alice@wunder.land" is registered with password "caterpillar"
|
29
|
+
When she visits "/logon"
|
30
|
+
And she fills in "email" with "alice@wunder.land"
|
31
|
+
And she fills in "password" with "caterpillar"
|
32
|
+
And she presses "Submit"
|
33
|
+
Then she should be on "/welcome"
|
34
|
+
And she should see "Welcome alice@wunder.land"
|
35
|
+
|
36
|
+
Scenario: Alice follows confirmation link
|
37
|
+
Given "alice@looking.glass" opens an email containing "/response-token/"
|
38
|
+
When she visits link from email
|
39
|
+
And "email" form-input should contain "alice@looking.glass"
|
40
|
+
And she fills in "password" with "caterpillar"
|
41
|
+
And she presses "Submit"
|
42
|
+
Then she should be on "/welcome"
|
43
|
+
And she should see "Welcome alice@looking.glass"
|
44
|
+
|
45
|
+
Scenario: Alice can not log on with her previous e-mail
|
46
|
+
Given "alice@looking.glass" is registered with password "caterpillar"
|
47
|
+
When she visits "/logon"
|
48
|
+
And she fills in "email" with "alice@wunder.land"
|
49
|
+
And she fills in "password" with "caterpillar"
|
50
|
+
And she presses "Submit"
|
51
|
+
Then she should not see "Welcome alice@wunder.land"
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# Copyright Westside Consulting LLC, Ann Arbor, MI, USA, 2012
|
2
|
+
|
3
|
+
@change-password
|
4
|
+
Feature: Users can change their passwords
|
5
|
+
|
6
|
+
@registers-confirms
|
7
|
+
Scenario: Alice requests to register
|
8
|
+
Given I have unregistered "alice@wunder.land"
|
9
|
+
When she visits "/register"
|
10
|
+
And she fills in "email" with "alice@wunder.land"
|
11
|
+
And she presses "Submit"
|
12
|
+
Then she should see "Check your e-mail"
|
13
|
+
|
14
|
+
Scenario: Alice changes her password
|
15
|
+
Given "alice@wunder.land" opens an email containing "/response-token/"
|
16
|
+
Then she visits link from email
|
17
|
+
And she should see "Change Password"
|
18
|
+
And she fills in "password" with "caterpillar"
|
19
|
+
And she fills in "password2" with "caterpillar"
|
20
|
+
And she presses "Submit"
|
21
|
+
Then she should see "You have changed your password."
|
22
|
+
And "alice@wunder.land" should receive an email
|
23
|
+
And "admin@accounts.test" should receive an email
|
24
|
+
And "admin@accounts.test" opens an email containing "alice@wunder.land has registered and confirmed"
|
25
|
+
|
26
|
+
Scenario: Alice can log on with password "caterpillar"
|
27
|
+
Given "alice@wunder.land" opens an email containing "The password for alice@wunder.land has changed."
|
28
|
+
When she visits "/logon"
|
29
|
+
And she fills in "email" with "alice@wunder.land"
|
30
|
+
And she fills in "password" with "caterpillar"
|
31
|
+
And she presses "Submit"
|
32
|
+
Then she should be on "/welcome"
|
33
|
+
And she should see "Welcome alice@wunder.land"
|
34
|
+
|
35
|
+
Scenario: Alice can not log on with password "alice"
|
36
|
+
When she visits "/logon"
|
37
|
+
And she fills in "email" with "alice@wunder.land"
|
38
|
+
And she fills in "password" with "alice"
|
39
|
+
And she presses "Submit"
|
40
|
+
Then she should be on "/logon"
|
41
|
+
And she should not see "Welcome alice@wunder.land"
|
42
|
+
|
43
|
+
Scenario: Alice attempts to visit restricted page without first authenticating
|
44
|
+
Given Alice has logged out
|
45
|
+
When she visits "/welcome"
|
46
|
+
And she should not see "Welcome alice@wunder.land"
|
47
|
+
|
48
|
+
Scenario: Alice changes her password again
|
49
|
+
Given Alice visits "/logon"
|
50
|
+
And she fills in "email" with "alice@wunder.land"
|
51
|
+
And she fills in "password" with "caterpillar"
|
52
|
+
And she presses "Submit"
|
53
|
+
And she visits "/change-password"
|
54
|
+
Then she fills in "password" with "whiterabbit"
|
55
|
+
And she fills in "password2" with "whiterabbit"
|
56
|
+
And she presses "Submit"
|
57
|
+
Then she should see "You have changed your password."
|
58
|
+
|
59
|
+
Scenario: Alice tries to log on with her defunct password
|
60
|
+
Given "alice@wunder.land" opens an email containing "The password for alice@wunder.land has changed."
|
61
|
+
When she visits "/logon"
|
62
|
+
And she fills in "email" with "alice@wunder.land"
|
63
|
+
And she fills in "password" with "caterpillar"
|
64
|
+
And she presses "Submit"
|
65
|
+
Then she should not see "Welcome alice@wunder.land"
|
66
|
+
|
67
|
+
Scenario: Only authenticated users can change their password
|
68
|
+
Given I have unregistered "dormouse@alice.com"
|
69
|
+
When he visits "/change-password"
|
70
|
+
Then he should not see "Change Password"
|
71
|
+
|
72
|
+
Scenario: Alice forgets her password
|
73
|
+
Given Alice visits "/forgot-password"
|
74
|
+
When she fills in "email" with "alice@wunder.land"
|
75
|
+
And she presses "Submit"
|
76
|
+
Then she should see "Check your e-mail"
|
77
|
+
And "alice@wunder.land" should receive an email
|
78
|
+
|
79
|
+
Scenario: Alice can reset her password again
|
80
|
+
Given "alice@wunder.land" opens an email containing "/response-token/"
|
81
|
+
When she visits link from email
|
82
|
+
Then alice should see "Change Password"
|
83
|
+
#But she remembers it again and goes to tea with Caterpillar. :)
|
84
|
+
|
85
|
+
Scenario: Unknown user attempts to reset password
|
86
|
+
Given MadHatter visits "/forgot-password"
|
87
|
+
When he fills in "email" with "madhatter@wunder.land"
|
88
|
+
And he presses "Submit"
|
89
|
+
Then he should see "madhatter@wunder.land does not match any account"
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# Copyright Westside Consulting LLC, Ann Arbor, MI, USA, 2012
|
2
|
+
|
3
|
+
@register
|
4
|
+
Feature: Visitors can register
|
5
|
+
|
6
|
+
Scenario: Unregistered user requests to register
|
7
|
+
Given I have unregistered "alice@wunder.land"
|
8
|
+
When she visits "/register"
|
9
|
+
And she fills in "email" with "alice@wunder.land"
|
10
|
+
And she presses "Submit"
|
11
|
+
Then she should see "Check your e-mail"
|
12
|
+
And "alice@wunder.land" should receive an email
|
13
|
+
|
14
|
+
Scenario: Unconfirmed user requests to register again will get another e-mail
|
15
|
+
Given "alice@wunder.land" is registered
|
16
|
+
When she visits "/register"
|
17
|
+
And she fills in "email" with "alice@wunder.land"
|
18
|
+
And she presses "Submit"
|
19
|
+
Then page has content "Check your e-mail"
|
20
|
+
And "alice@wunder.land" should receive an email
|
21
|
+
|
22
|
+
Scenario: Confirm registration with stale link doesn't do anything
|
23
|
+
Given "alice@wunder.land" opens an email containing "/response-token/"
|
24
|
+
When she visits link from email
|
25
|
+
Then Alice should see "Page not found"
|
26
|
+
And "alice@wunder.land" is not confirmed
|
27
|
+
|
28
|
+
Scenario: Confirm registration with active link is successful
|
29
|
+
Given "alice@wunder.land" opens an email containing "/response-token/"
|
30
|
+
When she visits link from email
|
31
|
+
Then alice should see "Change Password"
|
32
|
+
And "admin@accounts.test" should receive an email
|
33
|
+
And "admin@accounts.test" opens an email containing "alice@wunder.land has registered and confirmed"
|
34
|
+
And "alice@wunder.land" is confirmed
|
35
|
+
|
36
|
+
Scenario: Confirmed user requests to register again will fail
|
37
|
+
Given "alice@wunder.land" is registered
|
38
|
+
When I visits "/register"
|
39
|
+
And I fills in "email" with "alice@wunder.land"
|
40
|
+
And I presses "Submit"
|
41
|
+
Then page has content "alice@wunder.land is already registered"
|
42
|
+
And "alice@wunder.land" should not receive an email
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# Copyright Westside Consulting LLC, Ann Arbor, MI, USA, 2012
|
2
|
+
|
3
|
+
When /^page has "([^"]*)"$/ do |arg1|
|
4
|
+
#STDERR.puts page.body
|
5
|
+
page.body.should have_selector(arg1)
|
6
|
+
end
|
7
|
+
|
8
|
+
When /^page has content "([^"]*)"$/ do |arg1|
|
9
|
+
#STDERR.puts page.body
|
10
|
+
page.body.should have_content(arg1)
|
11
|
+
end
|
12
|
+
|
13
|
+
When /^"([^"]*)" is suspended$/ do |arg1|
|
14
|
+
pending # express the regexp above with the code you wish you had
|
15
|
+
end
|
16
|
+
|
17
|
+
When /^\w+ visits link from email$/ do
|
18
|
+
@new_mail.should_not be_nil
|
19
|
+
@new_mail.body.to_s =~ /(http\S+)/
|
20
|
+
link = $1
|
21
|
+
link.should_not be_nil
|
22
|
+
#STDERR.puts "link = #{link}"
|
23
|
+
visit link
|
24
|
+
end
|
25
|
+
|
26
|
+
When /^"([^"]*)" is (not )?confirmed$/ do |arg1, bool|
|
27
|
+
account = Accounts::Account.first( :email => arg1 )
|
28
|
+
should_be_confirmed = !bool
|
29
|
+
|
30
|
+
if (should_be_confirmed) then
|
31
|
+
account.should_not be_nil
|
32
|
+
account.status.should include :email_confirmed
|
33
|
+
else
|
34
|
+
account.status.should_not include :email_confirmed if account
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
When /^\w+ visits? "([^"]*)"$/ do |arg1|
|
39
|
+
visit arg1
|
40
|
+
#save_and_open_page
|
41
|
+
end
|
42
|
+
|
43
|
+
When /^I should see raw html: "([^"]*)"$/ do |arg1|
|
44
|
+
page.html.should match /#{arg1}/
|
45
|
+
end
|
46
|
+
|
47
|
+
When /^I have unregistered "([^"]*)"$/ do |arg1|
|
48
|
+
account = Accounts::Account.all( :email => arg1 )
|
49
|
+
account.destroy if account
|
50
|
+
Accounts::Account.all( :email => arg1 ).should have(0).items
|
51
|
+
end
|
52
|
+
|
53
|
+
When /^"([^"]*)" is (not )?registered$/ do |arg1, bool|
|
54
|
+
Accounts::Account.all( :email => arg1 ).should have(bool ? 0 : 1).items
|
55
|
+
end
|
56
|
+
|
57
|
+
When /^"([^"]*)" opens an email containing "([^"]*)"$/ do |arg1, arg2|
|
58
|
+
Mail::TestMailer.deliveries.accounts.should include(arg1)
|
59
|
+
@new_mail = Mail::TestMailer.deliveries.get(arg1)
|
60
|
+
@new_mail.should_not be_nil
|
61
|
+
@new_mail.body.should match /#{arg2}/
|
62
|
+
end
|
63
|
+
|
64
|
+
When /^"([^"]*)" should receive an email$/ do |arg1|
|
65
|
+
Mail::TestMailer.deliveries.accounts.should include(arg1)
|
66
|
+
Mail::TestMailer.deliveries.peek(arg1).should_not be_nil
|
67
|
+
end
|
68
|
+
|
69
|
+
When /^"([^"]*)" should not receive an email$/ do |arg1|
|
70
|
+
msg = Mail::TestMailer.deliveries.peek(arg1)
|
71
|
+
msg.should be_nil
|
72
|
+
end
|
73
|
+
|
74
|
+
When /^(?:\S+ )(?:is|has) logged out$/ do
|
75
|
+
visit '/logout'
|
76
|
+
end
|
77
|
+
|
78
|
+
When /^I register "([^"]*)" with password "([^"]*)"$/ do |arg1, arg2|
|
79
|
+
account = Accounts::Account.create ({ :email => arg1 })
|
80
|
+
account.set_password arg2
|
81
|
+
end
|
82
|
+
|
83
|
+
When /^"([^"]*)" is registered with password "([^"]*)"$/ do |arg1, arg2|
|
84
|
+
account = Accounts::Account.first ({ :email => arg1 })
|
85
|
+
account.should_not be_nil
|
86
|
+
account.confirm_password(arg2).should be_true
|
87
|
+
end
|
88
|
+
|
89
|
+
When /^"([^"]*)" is authenticated with password "([^"]*)"$/ do |arg1, arg2|
|
90
|
+
visit '/logon'
|
91
|
+
current_path.should be == '/logon'
|
92
|
+
fill_in('email', :with => arg1)
|
93
|
+
fill_in('password', :with => arg2)
|
94
|
+
click_button("Submit")
|
95
|
+
current_path.should be == '/welcome'
|
96
|
+
page.body.should match "Welcome #{arg1}"
|
97
|
+
end
|
98
|
+
|
99
|
+
Then /^"([^"]*)" form\-input should contain "([^"]*)"$/ do |arg1, arg2|
|
100
|
+
find("input[@name=#{arg1}]").value.should be == arg2
|
101
|
+
end
|
102
|
+
|