pillowfort 0.1.1 → 0.1.2

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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +0 -1
  3. data/app/models/pillowfort/concerns/model_authentication.rb +7 -3
  4. data/lib/pillowfort/version.rb +1 -1
  5. data/spec/dummy/README.rdoc +28 -0
  6. data/spec/dummy/Rakefile +6 -0
  7. data/spec/dummy/app/assets/javascripts/accounts.js +2 -0
  8. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  9. data/spec/dummy/app/assets/stylesheets/accounts.css +4 -0
  10. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  11. data/spec/dummy/app/controllers/accounts_controller.rb +13 -0
  12. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  13. data/spec/dummy/app/helpers/accounts_helper.rb +2 -0
  14. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  15. data/spec/dummy/app/models/account.rb +3 -0
  16. data/spec/dummy/app/views/accounts/index.html.erb +2 -0
  17. data/spec/dummy/app/views/accounts/show.html.erb +2 -0
  18. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  19. data/spec/dummy/bin/bundle +3 -0
  20. data/spec/dummy/bin/rails +4 -0
  21. data/spec/dummy/bin/rake +4 -0
  22. data/spec/dummy/bin/setup +29 -0
  23. data/spec/dummy/config.ru +4 -0
  24. data/spec/dummy/config/application.rb +31 -0
  25. data/spec/dummy/config/boot.rb +5 -0
  26. data/spec/dummy/config/database.yml +17 -0
  27. data/spec/dummy/config/environment.rb +5 -0
  28. data/spec/dummy/config/environments/test.rb +42 -0
  29. data/spec/dummy/config/initializers/assets.rb +11 -0
  30. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  31. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  32. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  33. data/spec/dummy/config/initializers/inflections.rb +16 -0
  34. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  35. data/spec/dummy/config/initializers/session_store.rb +3 -0
  36. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  37. data/spec/dummy/config/locales/en.yml +23 -0
  38. data/spec/dummy/config/routes.rb +60 -0
  39. data/spec/dummy/config/secrets.yml +14 -0
  40. data/spec/dummy/db/migrate/20150127045508_create_accounts.rb +12 -0
  41. data/spec/dummy/db/schema.rb +25 -0
  42. data/spec/dummy/db/test.sqlite3 +0 -0
  43. data/spec/dummy/log/development.log +0 -0
  44. data/spec/dummy/log/test.log +2087 -0
  45. data/spec/dummy/public/404.html +67 -0
  46. data/spec/dummy/public/422.html +67 -0
  47. data/spec/dummy/public/500.html +66 -0
  48. data/spec/dummy/public/favicon.ico +0 -0
  49. data/spec/dummy/spec/controllers/accounts_controller_spec.rb +52 -0
  50. data/spec/dummy/spec/factories/accounts.rb +10 -0
  51. data/spec/dummy/spec/models/account_spec.rb +276 -0
  52. data/spec/dummy/spec/rails_helper.rb +52 -0
  53. data/spec/dummy/spec/spec_helper.rb +85 -0
  54. data/spec/dummy/spec/support/helpers/authentication_helper.rb +15 -0
  55. metadata +103 -3
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/404.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>The page you were looking for doesn't exist.</h1>
62
+ <p>You may have mistyped the address or the page may have moved.</p>
63
+ </div>
64
+ <p>If you are the application owner check the logs for more information.</p>
65
+ </div>
66
+ </body>
67
+ </html>
@@ -0,0 +1,67 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/422.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>The change you wanted was rejected.</h1>
62
+ <p>Maybe you tried to change something you didn't have access to.</p>
63
+ </div>
64
+ <p>If you are the application owner check the logs for more information.</p>
65
+ </div>
66
+ </body>
67
+ </html>
@@ -0,0 +1,66 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <style>
7
+ body {
8
+ background-color: #EFEFEF;
9
+ color: #2E2F30;
10
+ text-align: center;
11
+ font-family: arial, sans-serif;
12
+ margin: 0;
13
+ }
14
+
15
+ div.dialog {
16
+ width: 95%;
17
+ max-width: 33em;
18
+ margin: 4em auto 0;
19
+ }
20
+
21
+ div.dialog > div {
22
+ border: 1px solid #CCC;
23
+ border-right-color: #999;
24
+ border-left-color: #999;
25
+ border-bottom-color: #BBB;
26
+ border-top: #B00100 solid 4px;
27
+ border-top-left-radius: 9px;
28
+ border-top-right-radius: 9px;
29
+ background-color: white;
30
+ padding: 7px 12% 0;
31
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
32
+ }
33
+
34
+ h1 {
35
+ font-size: 100%;
36
+ color: #730E15;
37
+ line-height: 1.5em;
38
+ }
39
+
40
+ div.dialog > p {
41
+ margin: 0 0 1em;
42
+ padding: 1em;
43
+ background-color: #F7F7F7;
44
+ border: 1px solid #CCC;
45
+ border-right-color: #999;
46
+ border-left-color: #999;
47
+ border-bottom-color: #999;
48
+ border-bottom-left-radius: 4px;
49
+ border-bottom-right-radius: 4px;
50
+ border-top-color: #DADADA;
51
+ color: #666;
52
+ box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
53
+ }
54
+ </style>
55
+ </head>
56
+
57
+ <body>
58
+ <!-- This file lives in public/500.html -->
59
+ <div class="dialog">
60
+ <div>
61
+ <h1>We're sorry, but something went wrong.</h1>
62
+ </div>
63
+ <p>If you are the application owner check the logs for more information.</p>
64
+ </div>
65
+ </body>
66
+ </html>
File without changes
@@ -0,0 +1,52 @@
1
+ require 'rails_helper'
2
+
3
+ describe AccountsController, :type => :controller do
4
+ describe 'its response' do
5
+ subject { response }
6
+
7
+ context 'when authenticated' do
8
+ let(:account) { FactoryGirl.create :account }
9
+ before { authenticate_with account }
10
+
11
+ describe 'unprotected #index' do
12
+ before { get :index }
13
+ it { should have_http_status :success }
14
+ end
15
+
16
+ describe 'protected #show' do
17
+ before { get :show, id: 1 }
18
+ it { should have_http_status :success }
19
+ end
20
+ end
21
+
22
+ context 'when not authenticated' do
23
+ describe 'unprotected #index' do
24
+ before { get :index }
25
+ it { should have_http_status :success }
26
+ end
27
+
28
+ describe 'protected #show' do
29
+ before { get :show, id: 1 }
30
+ it { should have_http_status :unauthorized }
31
+ end
32
+ end
33
+ end
34
+
35
+ describe 'its methods' do
36
+ describe '#current_account' do
37
+ it { should respond_to(:current_account) }
38
+
39
+ context 'when authenticated' do
40
+ let(:account) { FactoryGirl.create :account }
41
+ before do
42
+ authenticate_with account
43
+ get :show, id: 1
44
+ end
45
+
46
+ it 'should return the account when current_account is called' do
47
+ expect(subject.current_account).to eq(account)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,10 @@
1
+ FactoryGirl.define do
2
+ sequence :email do |n|
3
+ "foo.bar.#{n}@baz.org"
4
+ end
5
+
6
+ factory :account do
7
+ email
8
+ password { "SuperSafe123" }
9
+ end
10
+ end
@@ -0,0 +1,276 @@
1
+ require 'rails_helper'
2
+
3
+ # ------------------------------------------------------------------------------
4
+ # Shared Examples
5
+ # ------------------------------------------------------------------------------
6
+
7
+ RSpec.shared_examples 'an auth token resetter' do
8
+ describe 'its affect on the auth_token' do
9
+ subject { account.auth_token }
10
+
11
+ describe 'before the call' do
12
+ it { should eq(auth_token) }
13
+ end
14
+
15
+ describe 'after the call' do
16
+ before { call_the_method }
17
+ it { should_not eq(auth_token) }
18
+ end
19
+ end
20
+
21
+ describe 'its affect on the auth_token_expires_at' do
22
+ subject { account.auth_token_expires_at }
23
+
24
+ describe 'before the call' do
25
+ it { should eq(auth_token_expires_at) }
26
+ end
27
+
28
+ describe 'after the call' do
29
+ before { call_the_method }
30
+ it { should be > auth_token_expires_at }
31
+ end
32
+ end
33
+ end
34
+
35
+ # ------------------------------------------------------------------------------
36
+ # The Spec!
37
+ # ------------------------------------------------------------------------------
38
+
39
+ RSpec.describe Account, :type => :model do
40
+
41
+ describe 'its validations' do
42
+ before { account.save }
43
+ subject { account.errors.messages }
44
+
45
+ describe 'email validations' do
46
+ let(:account) { FactoryGirl.build(:account, email: email) }
47
+
48
+ context 'presence_of' do
49
+ let(:email) { nil }
50
+
51
+ it { should include(email: ["can't be blank"]) }
52
+ end
53
+
54
+ context 'uniqueness' do
55
+ let(:email) { 'foobar@baz.com' }
56
+ let(:dup_account) { FactoryGirl.build(:account, email: email) }
57
+ before { dup_account.save }
58
+ subject { dup_account.errors.messages}
59
+
60
+ it { should include(email: ["has already been taken"]) }
61
+ end
62
+ end
63
+
64
+ describe 'password validations' do
65
+ let(:account) { FactoryGirl.build(:account, password: password) }
66
+
67
+ context 'presence_of' do
68
+ let(:password) { nil }
69
+
70
+ it { should include(password: [/can't be blank/, /is too short/]) }
71
+ end
72
+
73
+ context 'length of' do
74
+ context "when it's too short" do
75
+ let(:password) { "x"*3 }
76
+
77
+ it { should include(password: [/is too short/])}
78
+ end
79
+
80
+ context "when it's too long" do
81
+ let(:password) { "x"*80 }
82
+
83
+ it { should include(password: [/is too long/])}
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ describe 'the instance methods' do
90
+ let(:account) {
91
+ FactoryGirl.create :account,
92
+ auth_token: auth_token,
93
+ auth_token_expires_at: auth_token_expires_at
94
+ }
95
+
96
+ let(:auth_token) { 'abc123def456' }
97
+ let(:auth_token_expires_at) { 1.day.from_now }
98
+
99
+ describe '#ensure_auth_token' do
100
+ subject { account.auth_token }
101
+ before { account.ensure_auth_token }
102
+
103
+ context 'when the token is nil' do
104
+ let(:auth_token) { nil }
105
+ it { should_not be_nil }
106
+ end
107
+
108
+ context 'when the token is not nil' do
109
+ let(:auth_token) { 'deadbeef' }
110
+ it { should eq('deadbeef') }
111
+ end
112
+ end
113
+
114
+ describe '#reset_auth_token' do
115
+ let(:call_the_method) { account.reset_auth_token }
116
+ it_behaves_like 'an auth token resetter'
117
+
118
+ describe 'its persistence' do
119
+ subject { account }
120
+ after { call_the_method }
121
+ it { should_not receive(:save) }
122
+ end
123
+ end
124
+
125
+ describe '#reset_auth_token!' do
126
+ let(:call_the_method) { account.reset_auth_token! }
127
+ it_behaves_like 'an auth token resetter'
128
+
129
+ describe 'its persistence' do
130
+ subject { account }
131
+ after { call_the_method }
132
+ it { should receive(:save) }
133
+ end
134
+ end
135
+
136
+ describe '#token_expired?' do
137
+ subject { account.token_expired? }
138
+
139
+ context 'when the token expiration is in the future' do
140
+ let(:auth_token_expires_at) { 1.minute.from_now }
141
+ it { should be_falsey }
142
+ end
143
+
144
+ context 'when the token expiration is in the past' do
145
+ let(:auth_token_expires_at) { 1.minute.ago }
146
+ it { should be_truthy }
147
+ end
148
+ end
149
+
150
+ describe '#password=' do
151
+ let!(:current_password) { account.password.to_s }
152
+ subject { account.password.to_s }
153
+
154
+ describe 'before the call' do
155
+ it { should == (current_password) }
156
+ end
157
+
158
+ describe 'after the call' do
159
+ before { account.password = 'fudge_knuckles_45' }
160
+ it { should_not eq(current_password) }
161
+ end
162
+ end
163
+ end
164
+
165
+ describe 'the class methods' do
166
+ let(:email) { 'foobar@baz.com' }
167
+ let(:token) { 'deadbeef' }
168
+ let(:password) { 'admin4lolz' }
169
+ let(:auth_token_expires_at) { 1.day.from_now }
170
+
171
+ let!(:account) {
172
+ FactoryGirl.create :account,
173
+ email: email,
174
+ auth_token: token,
175
+ password: password,
176
+ auth_token_expires_at: auth_token_expires_at
177
+ }
178
+
179
+ describe '.authenticate_securely' do
180
+ let(:email_param) { email }
181
+ let(:token_param) { token }
182
+ let(:block) { ->(resource) {} }
183
+
184
+ subject { Account.authenticate_securely(email_param, token_param, &block) }
185
+
186
+ context 'when email is nil' do
187
+ let(:email_param) { nil }
188
+ it { should be_falsey }
189
+ end
190
+
191
+ context 'when token is nil' do
192
+ let(:token_param) { nil }
193
+ it { should be_falsey }
194
+ end
195
+
196
+ context 'when email and token are provided' do
197
+
198
+ context 'email case-sensitivity' do
199
+ describe 'when an uppercased email address is provided' do
200
+ let(:email_param) { email.upcase }
201
+
202
+ it 'should yield the matched account' do
203
+ expect { |b| Account.authenticate_securely(email_param, token_param, &b) }.to yield_with_args(account)
204
+ end
205
+ end
206
+
207
+ describe 'when a downcased email address is provided' do
208
+ let(:email_param) { email.downcase }
209
+
210
+ it 'should yield the matched account' do
211
+ expect { |b| Account.authenticate_securely(email_param, token_param, &b) }.to yield_with_args(account)
212
+ end
213
+ end
214
+ end
215
+
216
+ context 'when the resource is located' do
217
+
218
+ context 'when the auth_token is expired' do
219
+ let(:auth_token_expires_at) { 1.week.ago }
220
+
221
+ it 'should reset the account auth_token' do
222
+ allow(Account).to receive(:find_by_email_case_insensitive) { account }
223
+ expect(account).to receive(:reset_auth_token!)
224
+ subject
225
+ end
226
+
227
+ it { should be_falsey }
228
+ end
229
+
230
+ context 'when the auth_token is current' do
231
+
232
+ context 'when the auth_token matches' do
233
+ it 'should yield the matched account' do
234
+ expect { |b| Account.authenticate_securely(email_param, token_param, &b) }.to yield_with_args(account)
235
+ end
236
+ end
237
+
238
+ context 'when the auth_token does not match' do
239
+ it { should be_falsey }
240
+ end
241
+ end
242
+ end
243
+
244
+ context 'when the resource is not located' do
245
+ it { should be_falsey }
246
+ end
247
+
248
+ end
249
+ end
250
+
251
+ describe '.find_and_authenticate' do
252
+ let(:email_param) { email }
253
+ let(:password_param) { password }
254
+
255
+ subject { Account.find_and_authenticate(email_param, password_param) }
256
+
257
+
258
+ context 'when the resource is located' do
259
+
260
+ context 'when the password matches' do
261
+ it { should eq(account) }
262
+ end
263
+
264
+ context 'when the password does not match' do
265
+ let(:password_param) { "#{password}_bad" }
266
+ it { should be_falsey }
267
+ end
268
+ end
269
+
270
+ context 'when the resource is not located' do
271
+ let(:email_param) { "#{email}_evil" }
272
+ it { should be_falsey }
273
+ end
274
+ end
275
+ end
276
+ end