authkit 0.5.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +12 -0
- data/README.md +11 -3
- data/Rakefile +15 -8
- data/authkit.gemspec +2 -2
- data/lib/authkit/version.rb +1 -1
- data/lib/generators/authkit/install_generator.rb +8 -1
- data/lib/generators/authkit/templates/app/controllers/application_controller.rb +23 -20
- data/lib/generators/authkit/templates/app/controllers/auths_controller.rb +4 -4
- data/lib/generators/authkit/templates/app/controllers/email_confirmation_controller.rb +3 -6
- data/lib/generators/authkit/templates/app/controllers/password_change_controller.rb +4 -5
- data/lib/generators/authkit/templates/app/controllers/signup_controller.rb +4 -1
- data/lib/generators/authkit/templates/app/controllers/upload_controller.rb +3 -3
- data/lib/generators/authkit/templates/app/controllers/users_controller.rb +1 -3
- data/lib/generators/authkit/templates/app/forms/signup.rb +10 -2
- data/lib/generators/authkit/templates/app/models/user.rb +2 -22
- data/lib/generators/authkit/templates/app/models/user_session.rb +55 -0
- data/lib/generators/authkit/templates/app/views/password_reset/show.html.erb +0 -2
- data/lib/generators/authkit/templates/db/migrate/add_authkit_fields_to_users.rb +1 -10
- data/lib/generators/authkit/templates/db/migrate/create_auths.rb +6 -2
- data/lib/generators/authkit/templates/db/migrate/create_avatars.rb +3 -2
- data/lib/generators/authkit/templates/db/migrate/create_user_sessions.rb +27 -0
- data/lib/generators/authkit/templates/db/migrate/create_users.rb +2 -2
- data/lib/generators/authkit/templates/spec/controllers/application_controller_spec.rb +40 -47
- data/lib/generators/authkit/templates/spec/controllers/email_confirmation_controller_spec.rb +11 -11
- data/lib/generators/authkit/templates/spec/controllers/password_change_controller_spec.rb +21 -26
- data/lib/generators/authkit/templates/spec/controllers/password_reset_controller_spec.rb +11 -16
- data/lib/generators/authkit/templates/spec/controllers/sessions_controller_spec.rb +17 -23
- data/lib/generators/authkit/templates/spec/controllers/signup_controller_spec.rb +21 -29
- data/lib/generators/authkit/templates/spec/controllers/users_controller_spec.rb +14 -18
- data/lib/generators/authkit/templates/spec/factories/user_session.rb +6 -0
- data/lib/generators/authkit/templates/spec/forms/signup_spec.rb +1 -1
- data/lib/generators/authkit/templates/spec/models/user_session_spec.rb +81 -0
- data/lib/generators/authkit/templates/spec/models/user_spec.rb +18 -45
- data/lib/generators/authkit/templates/spec/support/factory_girl.rb +5 -0
- data/lib/generators/authkit/templates/spec/support/shoulda_matchers.rb +6 -0
- metadata +13 -6
@@ -0,0 +1,55 @@
|
|
1
|
+
class UserSession < ApplicationRecord
|
2
|
+
belongs_to :user
|
3
|
+
|
4
|
+
scope :active, -> { where(revoked_at: nil, logged_out_at: nil) }
|
5
|
+
|
6
|
+
validates :user, presence: true
|
7
|
+
|
8
|
+
validates :remember_token, presence: true
|
9
|
+
|
10
|
+
before_validation :set_remember_token
|
11
|
+
|
12
|
+
def active?
|
13
|
+
!logged_out? && !revoked?
|
14
|
+
end
|
15
|
+
|
16
|
+
def logged_out?
|
17
|
+
logged_out_at.present?
|
18
|
+
end
|
19
|
+
|
20
|
+
def revoked?
|
21
|
+
revoked_at.present?
|
22
|
+
end
|
23
|
+
|
24
|
+
def sudo?
|
25
|
+
sudo_enabled_at.present? && sudo_enabled_at > 1.hour.ago
|
26
|
+
end
|
27
|
+
|
28
|
+
def sudo
|
29
|
+
self.sudo_enabled_at = Time.now
|
30
|
+
save!
|
31
|
+
end
|
32
|
+
|
33
|
+
def logout
|
34
|
+
self.logged_out_at = Time.now
|
35
|
+
save!
|
36
|
+
end
|
37
|
+
|
38
|
+
def access(request, tracking=true)
|
39
|
+
self.accessed_at = Time.now
|
40
|
+
self.ip = request.remote_ip if tracking
|
41
|
+
self.user_agent = request.user_agent if tracking
|
42
|
+
save!
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# The tokens created by this method have unique indexes but collisions are very
|
48
|
+
# unlikely (1/64^32). Because of this there shouldn't be a conflict. If one occurs
|
49
|
+
# the ActiveRecord::StatementInvalid or ActiveRecord::RecordNotUnique exeception
|
50
|
+
# should bubble up.
|
51
|
+
def set_remember_token
|
52
|
+
self.remember_token = SecureRandom.urlsafe_base64(32) if self.remember_token.blank?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
@@ -1,8 +1,6 @@
|
|
1
1
|
<h1>Reset your password</h1>
|
2
2
|
|
3
3
|
<%%= form_tag(password_reset_path, method: "post") do %>
|
4
|
-
<%%= hidden_field_tag :token, params[:token] %>
|
5
|
-
|
6
4
|
<div class="field">
|
7
5
|
<%%= label_tag "email", "Email<% if username? %> or username<% end %>" %>
|
8
6
|
<%%= text_field_tag "email" %>
|
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# Add fields to the users table for managing authentication.
|
4
4
|
#
|
5
|
-
class AddAuthkitFieldsToUsers < ActiveRecord::Migration
|
5
|
+
class AddAuthkitFieldsToUsers < ActiveRecord::Migration[5.0]
|
6
6
|
def self.up
|
7
7
|
add_column :users, :email, :string, :default => "", :null => false
|
8
8
|
add_column :users, :password_digest, :string, :default => "", :null => false
|
@@ -35,10 +35,6 @@ class AddAuthkitFieldsToUsers < ActiveRecord::Migration
|
|
35
35
|
add_column :users, :reset_password_token, :string
|
36
36
|
add_column :users, :reset_password_token_created_at, :datetime
|
37
37
|
|
38
|
-
# Remember
|
39
|
-
add_column :users, :remember_token, :string
|
40
|
-
add_column :users, :remember_token_created_at, :datetime
|
41
|
-
|
42
38
|
# Confirmation
|
43
39
|
add_column :users, :confirmation_email, :string
|
44
40
|
add_column :users, :confirmation_token, :string
|
@@ -56,7 +52,6 @@ class AddAuthkitFieldsToUsers < ActiveRecord::Migration
|
|
56
52
|
add_index :users, :email, :unique => true
|
57
53
|
<% if username? %>add_index :users, :username, :unique => true
|
58
54
|
<% end %>add_index :users, :reset_password_token, :unique => true
|
59
|
-
add_index :users, :remember_token, :unique => true
|
60
55
|
add_index :users, :confirmation_token, :unique => true
|
61
56
|
add_index :users, :unlock_token, :unique => true
|
62
57
|
|
@@ -93,10 +88,6 @@ class AddAuthkitFieldsToUsers < ActiveRecord::Migration
|
|
93
88
|
drop_column :users, :reset_password_token
|
94
89
|
drop_column :users, :reset_password_created_at
|
95
90
|
|
96
|
-
# Remember
|
97
|
-
drop_column :users, :remember_token
|
98
|
-
drop_column :users, :remember_token_created_at
|
99
|
-
|
100
91
|
# Confirmation
|
101
92
|
drop_column :users, :confirmation_email
|
102
93
|
drop_column :users, :confirmation_token
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# Generated by Authkit.
|
2
2
|
#
|
3
3
|
# Create an auths table for managing OAuth authentication.
|
4
|
-
class CreateAuths < ActiveRecord::Migration
|
4
|
+
class CreateAuths < ActiveRecord::Migration[5.0]
|
5
5
|
def self.up
|
6
6
|
create_table :auths do |t|
|
7
7
|
t.integer :user_id
|
@@ -14,8 +14,12 @@ class CreateAuths < ActiveRecord::Migration
|
|
14
14
|
t.string :refresh_token
|
15
15
|
t.string :secret_token
|
16
16
|
t.text :env
|
17
|
-
|
17
|
+
|
18
|
+
t.timestamps null: false
|
18
19
|
end
|
20
|
+
|
21
|
+
add_index :auths, :user_id
|
22
|
+
add_index :auths, [:provider, :uid]
|
19
23
|
end
|
20
24
|
|
21
25
|
def self.down
|
@@ -2,7 +2,7 @@
|
|
2
2
|
#
|
3
3
|
# Create an avatars table for managing user profile images
|
4
4
|
#
|
5
|
-
class CreateAvatars < ActiveRecord::Migration
|
5
|
+
class CreateAvatars < ActiveRecord::Migration[5.0]
|
6
6
|
def self.up
|
7
7
|
create_table :avatars do |t|
|
8
8
|
t.integer :user_id
|
@@ -17,7 +17,8 @@ class CreateAvatars < ActiveRecord::Migration
|
|
17
17
|
t.datetime :attachment_updated_at
|
18
18
|
t.boolean :attachment_importing, default: false
|
19
19
|
t.datetime :attachment_uploaded_at
|
20
|
-
|
20
|
+
|
21
|
+
t.timestamps null: false
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# Generated by Authkit.
|
2
|
+
#
|
3
|
+
# Create an user_sessions table for managing user logins.
|
4
|
+
class CreateUserSessions < ActiveRecord::Migration[5.0]
|
5
|
+
def self.up
|
6
|
+
create_table :user_sessions do |t|
|
7
|
+
t.integer :user_id
|
8
|
+
t.datetime :accessed_at
|
9
|
+
t.datetime :revoked_at
|
10
|
+
t.datetime :logged_out_at
|
11
|
+
t.datetime :sudo_enabled_at
|
12
|
+
t.string :ip
|
13
|
+
t.string :user_agent
|
14
|
+
t.string :remember_token
|
15
|
+
|
16
|
+
t.timestamps null: false
|
17
|
+
end
|
18
|
+
|
19
|
+
add_index :user_sessions, :remember_token, :unique => true
|
20
|
+
add_index :user_sessions, [:accessed_at, :revoked_at, :logged_out_at], :name => 'index_user_sessions_active'
|
21
|
+
add_index :user_sessions, :user_id
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.down
|
25
|
+
drop_table :user_sessions
|
26
|
+
end
|
27
|
+
end
|
@@ -4,10 +4,10 @@
|
|
4
4
|
# the authentication are created in the AddAuthkitFieldsToUsers
|
5
5
|
# migration.
|
6
6
|
#
|
7
|
-
class CreateUsers < ActiveRecord::Migration
|
7
|
+
class CreateUsers < ActiveRecord::Migration[5.0]
|
8
8
|
def self.up
|
9
9
|
create_table :users do |t|
|
10
|
-
t.timestamps
|
10
|
+
t.timestamps null: false
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
@@ -1,16 +1,17 @@
|
|
1
1
|
require 'rails_helper'
|
2
2
|
|
3
|
-
describe ApplicationController do
|
4
|
-
let(:
|
5
|
-
let(:
|
6
|
-
let(:
|
3
|
+
RSpec.describe ApplicationController do
|
4
|
+
let(:user_session) { create(:user_session) }
|
5
|
+
let(:user) { user_session.user }
|
6
|
+
let(:logged_in_session) { { user_session_id: user_session.id } }
|
7
|
+
let(:unknown_session) { { user_session_id: user_session.id + 1000000 } }
|
7
8
|
|
8
9
|
before(:each) do
|
9
10
|
user
|
10
11
|
end
|
11
12
|
|
12
13
|
controller do
|
13
|
-
|
14
|
+
before_action :require_login, only: [:index]
|
14
15
|
|
15
16
|
def new
|
16
17
|
head :ok
|
@@ -30,46 +31,33 @@ describe ApplicationController do
|
|
30
31
|
get :new
|
31
32
|
expect(controller.send(:current_user)).to be_nil
|
32
33
|
end
|
34
|
+
end
|
33
35
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
expect(controller.send(:current_user)).to be_nil
|
36
|
+
describe "current_user_session" do
|
37
|
+
it "returns nil if there is no current user session" do
|
38
|
+
get :new
|
39
|
+
expect(controller.send(:current_user_session)).to be_nil
|
39
40
|
end
|
40
41
|
|
41
|
-
it "finds the current user in the session" do
|
42
|
-
get :new,
|
43
|
-
expect(controller.send(:
|
42
|
+
it "finds the current user session in the session" do
|
43
|
+
get :new, session: logged_in_session
|
44
|
+
expect(controller.send(:current_user_session)).to eq(user_session)
|
44
45
|
end
|
45
46
|
|
46
47
|
it "finds the current user from the remember cookie" do
|
47
|
-
|
48
|
-
user.set_remember_token
|
49
|
-
cookies.signed[:remember] = user.remember_token
|
50
|
-
get :index
|
51
|
-
expect(controller.send(:current_user)).to eq(user)
|
52
|
-
end
|
53
|
-
|
54
|
-
it "doesn't find the current user from the remember cookie if it is expired" do
|
55
|
-
# Setup expired token
|
56
|
-
user.set_remember_token
|
57
|
-
user.remember_token_created_at = 1.year.ago
|
58
|
-
user.save
|
59
|
-
|
60
|
-
cookies.signed[:remember] = user.remember_token
|
48
|
+
cookies.signed[:remember] = user_session.remember_token
|
61
49
|
get :index
|
62
|
-
expect(controller.send(:
|
50
|
+
expect(controller.send(:current_user_session)).to eq(user_session)
|
63
51
|
end
|
64
52
|
|
65
53
|
it "sets the time zone" do
|
66
54
|
expect_any_instance_of(User).to receive(:time_zone).and_return("Pacific Time (US & Canada)")
|
67
|
-
get :index,
|
55
|
+
get :index, session: logged_in_session
|
68
56
|
expect(Time.zone.name).to eq("Pacific Time (US & Canada)")
|
69
57
|
end
|
70
58
|
|
71
59
|
it "has a logged in helper method" do
|
72
|
-
get :new,
|
60
|
+
get :new, session: logged_in_session
|
73
61
|
expect(controller.send(:logged_in?)).to eq(true)
|
74
62
|
end
|
75
63
|
end
|
@@ -89,30 +77,30 @@ describe ApplicationController do
|
|
89
77
|
|
90
78
|
describe "when requiring a user" do
|
91
79
|
it "allows access if there is a user" do
|
92
|
-
get :index,
|
80
|
+
get :index, session: logged_in_session
|
93
81
|
expect(response).to be_success
|
94
82
|
end
|
95
83
|
|
96
84
|
it "stores the return path" do
|
97
|
-
get :index
|
85
|
+
get :index
|
98
86
|
expect(session[:return_url]).to eq("/anonymous")
|
99
87
|
end
|
100
88
|
|
101
89
|
describe "when responding to html" do
|
102
90
|
it "sets the flash message" do
|
103
|
-
get :index
|
91
|
+
get :index
|
104
92
|
expect(flash).to_not be_empty
|
105
93
|
end
|
106
94
|
|
107
95
|
it "redirecs the user to login" do
|
108
|
-
get :index
|
96
|
+
get :index
|
109
97
|
expect(response).to be_redirect
|
110
98
|
end
|
111
99
|
end
|
112
100
|
|
113
101
|
describe "when responding to json" do
|
114
102
|
it "returns a forbidden status" do
|
115
|
-
get :index, {format: :json}
|
103
|
+
get :index, params: { format: :json }
|
116
104
|
expect(response.code).to eq("403")
|
117
105
|
end
|
118
106
|
end
|
@@ -125,17 +113,22 @@ describe ApplicationController do
|
|
125
113
|
controller.send(:login, user)
|
126
114
|
end
|
127
115
|
|
116
|
+
it "creates a session" do
|
117
|
+
expect {
|
118
|
+
get :new
|
119
|
+
controller.send(:login, user, true)
|
120
|
+
}.to change(UserSession, :count).by(1)
|
121
|
+
end
|
122
|
+
|
128
123
|
it "remembers the user using a token and cookie" do
|
129
124
|
get :new
|
130
125
|
expect(controller).to receive(:set_remember_cookie)
|
131
|
-
expect(user).to receive(:set_remember_token)
|
132
126
|
controller.send(:login, user, true)
|
133
127
|
end
|
134
128
|
|
135
129
|
it "does not remember the user using a token and cookie when not requested" do
|
136
130
|
get :new
|
137
131
|
expect(controller).to_not receive(:set_remember_cookie)
|
138
|
-
expect(user).to_not receive(:set_remember_token)
|
139
132
|
controller.send(:login, user, false)
|
140
133
|
end
|
141
134
|
|
@@ -148,33 +141,33 @@ describe ApplicationController do
|
|
148
141
|
|
149
142
|
describe "logout" do
|
150
143
|
it "resets the session" do
|
151
|
-
get :index,
|
144
|
+
get :index, session: logged_in_session
|
152
145
|
expect(controller).to receive(:reset_session)
|
153
146
|
controller.send(:logout)
|
154
147
|
end
|
155
148
|
|
156
|
-
it "
|
157
|
-
get :index,
|
149
|
+
it "marks the user session as logged out" do
|
150
|
+
get :index, session: logged_in_session
|
158
151
|
controller.send(:logout)
|
159
|
-
expect(
|
152
|
+
expect(user_session.reload).to be_logged_out
|
160
153
|
end
|
161
154
|
|
162
|
-
it "
|
163
|
-
get :index,
|
164
|
-
expect_any_instance_of(User).to receive(:clear_remember_token).and_return(:true)
|
155
|
+
it "logs the user out" do
|
156
|
+
get :index, session: logged_in_session
|
165
157
|
controller.send(:logout)
|
158
|
+
expect(controller.send(:current_user)).to be_nil
|
166
159
|
end
|
167
160
|
end
|
168
161
|
|
169
162
|
it "sets the remember cookie" do
|
170
163
|
request.env["action_dispatch.secret_token"] = "SECRET"
|
171
164
|
get :new
|
172
|
-
controller.send(:login, user)
|
173
|
-
expect(cookies.permanent.signed[:remember]).to eq(
|
165
|
+
new_session = controller.send(:login, user, true)
|
166
|
+
expect(controller.send(:cookies).permanent.signed[:remember]).to eq(new_session.remember_token)
|
174
167
|
end
|
175
168
|
|
176
169
|
it "redirects to a stored session location if present" do
|
177
|
-
get :new, {
|
170
|
+
get :new, session: { return_url: "/return" }
|
178
171
|
expect(controller).to receive(:redirect_to).with("/return").and_return(true)
|
179
172
|
controller.send(:redirect_back_or_default)
|
180
173
|
end
|
data/lib/generators/authkit/templates/spec/controllers/email_confirmation_controller_spec.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'rails_helper'
|
2
2
|
|
3
|
-
describe EmailConfirmationController do
|
3
|
+
RSpec.describe EmailConfirmationController do
|
4
4
|
render_views
|
5
5
|
|
6
6
|
let(:user) { build(:user) }
|
@@ -9,7 +9,7 @@ describe EmailConfirmationController do
|
|
9
9
|
describe "GET 'show'" do
|
10
10
|
it "requires a login" do
|
11
11
|
allow(controller).to receive(:current_user).and_return(nil)
|
12
|
-
get 'show', token: token
|
12
|
+
get 'show', params: { token: token }
|
13
13
|
expect(response).to be_redirect
|
14
14
|
expect(flash[:error]).to_not be_empty
|
15
15
|
end
|
@@ -17,7 +17,7 @@ describe EmailConfirmationController do
|
|
17
17
|
it "requires a valid token" do
|
18
18
|
user.confirmation_token = "OTHER TOKEN"
|
19
19
|
allow(controller).to receive(:current_user).and_return(user)
|
20
|
-
get 'show', token: token
|
20
|
+
get 'show', params: { token: token }
|
21
21
|
expect(response).to be_redirect
|
22
22
|
expect(flash[:error]).to_not be_empty
|
23
23
|
end
|
@@ -26,7 +26,7 @@ describe EmailConfirmationController do
|
|
26
26
|
user.confirmation_token = token
|
27
27
|
user.confirmation_token_created_at = 4.days.ago
|
28
28
|
allow(controller).to receive(:current_user).and_return(user)
|
29
|
-
get 'show', token: token
|
29
|
+
get 'show', params: { token: token }
|
30
30
|
expect(response).to be_redirect
|
31
31
|
expect(flash[:error]).to_not be_empty
|
32
32
|
end
|
@@ -42,27 +42,27 @@ describe EmailConfirmationController do
|
|
42
42
|
describe "when the confirmation is successful" do
|
43
43
|
it "confirms the user email" do
|
44
44
|
expect(user).to receive(:email_confirmed).and_return(true)
|
45
|
-
get 'show', token: token
|
45
|
+
get 'show', params: { token: token }
|
46
46
|
end
|
47
47
|
|
48
48
|
it "does not sign the user in" do
|
49
49
|
expect(controller).to_not receive(:login)
|
50
|
-
get 'show', token: token
|
50
|
+
get 'show', params: { token: token }
|
51
51
|
end
|
52
52
|
|
53
53
|
it "sets the flash" do
|
54
|
-
get 'show', token: token
|
54
|
+
get 'show', params: { token: token }
|
55
55
|
expect(flash[:notice]).to_not be_nil
|
56
56
|
end
|
57
57
|
|
58
58
|
it "redirects the user" do
|
59
|
-
get 'show', token: token
|
59
|
+
get 'show', params: { token: token }
|
60
60
|
expect(response).to be_redirect
|
61
61
|
end
|
62
62
|
|
63
63
|
describe "from json" do
|
64
64
|
it "returns http success" do
|
65
|
-
get 'show', token: token, format: 'json'
|
65
|
+
get 'show', params: { token: token, format: 'json' }
|
66
66
|
expect(response).to be_success
|
67
67
|
end
|
68
68
|
end
|
@@ -76,7 +76,7 @@ describe EmailConfirmationController do
|
|
76
76
|
|
77
77
|
it "handles invalid confirmations" do
|
78
78
|
expect(user).to receive(:email_confirmed).and_return(false)
|
79
|
-
get 'show', token: token
|
79
|
+
get 'show', params: { token: token }
|
80
80
|
expect(flash[:error]).to_not be_empty
|
81
81
|
expect(response).to be_redirect
|
82
82
|
end
|
@@ -84,7 +84,7 @@ describe EmailConfirmationController do
|
|
84
84
|
describe "from json" do
|
85
85
|
it "returns a 422" do
|
86
86
|
expect(user).to receive(:email_confirmed).and_return(false)
|
87
|
-
get 'show', token: token, format: 'json'
|
87
|
+
get 'show', params: { token: token, format: 'json' }
|
88
88
|
expect(response.code).to eq('422')
|
89
89
|
end
|
90
90
|
end
|