authkit 0.5.0 → 0.7.0
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 +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
|