refinerycms-authentication 2.0.10 → 2.1.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/app/controllers/refinery/admin/users_controller.rb +82 -54
- data/app/controllers/refinery/passwords_controller.rb +1 -1
- data/app/controllers/refinery/users_controller.rb +1 -1
- data/app/models/refinery/user.rb +28 -9
- data/app/views/refinery/admin/users/_actions.html.erb +3 -0
- data/app/views/refinery/admin/users/_form.html.erb +6 -4
- data/app/views/refinery/admin/users/_records.html.erb +7 -0
- data/app/views/refinery/passwords/edit.html.erb +1 -1
- data/app/views/refinery/users/new.html.erb +1 -1
- data/config/locales/bg.yml +1 -1
- data/config/locales/cs.yml +3 -3
- data/config/locales/da.yml +1 -1
- data/config/locales/de.yml +1 -1
- data/config/locales/el.yml +1 -1
- data/config/locales/en.yml +3 -2
- data/config/locales/es.yml +1 -1
- data/config/locales/fi.yml +1 -1
- data/config/locales/fr.yml +1 -1
- data/config/locales/hu.yml +72 -0
- data/config/locales/it.yml +1 -1
- data/config/locales/ja.yml +1 -1
- data/config/locales/ko.yml +2 -2
- data/config/locales/lt.yml +1 -1
- data/config/locales/lv.yml +1 -1
- data/config/locales/nb.yml +1 -1
- data/config/locales/nl.yml +37 -35
- data/config/locales/pl.yml +7 -3
- data/config/locales/pt-BR.yml +1 -1
- data/config/locales/pt.yml +72 -0
- data/config/locales/rs.yml +1 -1
- data/config/locales/ru.yml +1 -1
- data/config/locales/sk.yml +9 -9
- data/config/locales/sl.yml +1 -1
- data/config/locales/sv.yml +1 -1
- data/config/locales/tr.yml +72 -0
- data/config/locales/uk.yml +98 -0
- data/config/locales/vi.yml +1 -1
- data/config/locales/zh-CN.yml +5 -5
- data/config/locales/zh-TW.yml +1 -1
- data/config/routes.rb +9 -11
- data/db/migrate/20120301234455_add_slug_to_refinery_users.rb +7 -0
- data/lib/refinery/authenticated_system.rb +1 -1
- data/lib/refinery/authentication.rb +1 -0
- data/lib/refinery/authentication/devise.rb +0 -7
- data/lib/refinery/authentication/engine.rb +3 -4
- data/refinerycms-authentication.gemspec +4 -4
- data/spec/controllers/refinery/admin/users_controller_spec.rb +14 -5
- data/spec/factories/user.rb +2 -2
- data/spec/{requests → features}/refinery/admin/users_spec.rb +10 -10
- data/spec/{requests → features}/refinery/passwords_spec.rb +1 -1
- data/spec/{requests → features}/refinery/sessions_spec.rb +12 -11
- data/spec/models/refinery/user_spec.rb +57 -10
- metadata +29 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e10d50b46b093530d9a2411b4ed0ad58fef167e9
|
4
|
+
data.tar.gz: 75c0cb210cc8b10a3d610440b0e230592a738203
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5df94292c59ba79fc7fb1b1d2434c3d3c46d739d7e7e9ee50d0de7080a187b1c5743918f7bd47eefbc0c209f0b1dd6b4238f7749974f5bb0668e0b983420ffa6
|
7
|
+
data.tar.gz: 3b2665aa68871310a69e79e2d047317e486a2880df406342acc1ff051395eb16eb90d6d98e299f54bc572c29e995565296d0def2746121c9b90287d58d47d87b
|
@@ -7,7 +7,10 @@ module Refinery
|
|
7
7
|
:title_attribute => 'username',
|
8
8
|
:xhr_paging => true
|
9
9
|
|
10
|
-
before_filter :
|
10
|
+
before_filter :find_available_plugins, :find_available_roles,
|
11
|
+
:only => [:new, :create, :edit, :update]
|
12
|
+
before_filter :redirect_unless_user_editable!, :only => [:edit, :update]
|
13
|
+
before_filter :exclude_password_assignment_when_blank!, :only => :update
|
11
14
|
|
12
15
|
def new
|
13
16
|
@user = Refinery::User.new
|
@@ -15,92 +18,117 @@ module Refinery
|
|
15
18
|
end
|
16
19
|
|
17
20
|
def create
|
18
|
-
@user = Refinery::User.new
|
21
|
+
@user = Refinery::User.new params[:user].except(:roles)
|
19
22
|
@selected_plugin_names = params[:user][:plugins] || []
|
20
23
|
@selected_role_names = params[:user][:roles] || []
|
21
24
|
|
22
25
|
if @user.save
|
23
|
-
|
24
|
-
# if the user is a superuser and can assign roles according to this site's
|
25
|
-
# settings then the roles are set with the POST data.
|
26
|
-
unless current_refinery_user.has_role?(:superuser) and Refinery::Authentication.superuser_can_assign_roles
|
27
|
-
@user.add_role(:refinery)
|
28
|
-
else
|
29
|
-
@user.roles = @selected_role_names.collect { |r| Refinery::Role[r.downcase.to_sym] }
|
30
|
-
end
|
31
|
-
|
32
|
-
redirect_to refinery.admin_users_path,
|
33
|
-
:notice => t('created', :what => @user.username, :scope => 'refinery.crudify')
|
26
|
+
create_successful
|
34
27
|
else
|
35
|
-
|
28
|
+
create_failed
|
36
29
|
end
|
37
30
|
end
|
38
31
|
|
39
32
|
def edit
|
40
|
-
|
41
|
-
|
42
|
-
@selected_plugin_names = @user.plugins.collect(&:name)
|
33
|
+
@selected_plugin_names = find_user.plugins.map(&:name)
|
43
34
|
end
|
44
35
|
|
45
36
|
def update
|
46
|
-
redirect_unless_user_editable!
|
47
|
-
|
48
37
|
# Store what the user selected.
|
49
38
|
@selected_role_names = params[:user].delete(:roles) || []
|
50
|
-
|
51
|
-
@selected_role_names = @user.roles.collect(&:title)
|
52
|
-
end
|
39
|
+
@selected_role_names = @user.roles.select(:title).map(&:title) unless user_can_assign_roles?
|
53
40
|
@selected_plugin_names = params[:user][:plugins]
|
54
41
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
42
|
+
if user_is_locking_themselves_out?
|
43
|
+
flash.now[:error] = t('lockout_prevented', :scope => 'refinery.admin.users.update')
|
44
|
+
render :edit and return
|
45
|
+
end
|
46
|
+
|
47
|
+
store_user_memento
|
48
|
+
|
49
|
+
@user.roles = @selected_role_names.map { |r| Refinery::Role[r.downcase] }
|
50
|
+
if @user.update_attributes params[:user]
|
51
|
+
update_successful
|
59
52
|
else
|
60
|
-
|
61
|
-
@previously_selected_plugin_names = @user.plugins.collect(&:name)
|
62
|
-
@previously_selected_roles = @user.roles
|
63
|
-
@user.roles = @selected_role_names.collect { |r| Refinery::Role[r.downcase.to_sym] }
|
64
|
-
if params[:user][:password].blank? and params[:user][:password_confirmation].blank?
|
65
|
-
params[:user].except!(:password, :password_confirmation)
|
66
|
-
end
|
67
|
-
|
68
|
-
if @user.update_attributes(params[:user])
|
69
|
-
redirect_to refinery.admin_users_path,
|
70
|
-
:notice => t('updated', :what => @user.username, :scope => 'refinery.crudify')
|
71
|
-
else
|
72
|
-
@user.plugins = @previously_selected_plugin_names
|
73
|
-
@user.roles = @previously_selected_roles
|
74
|
-
@user.save
|
75
|
-
render :edit
|
76
|
-
end
|
53
|
+
update_failed
|
77
54
|
end
|
78
55
|
end
|
79
56
|
|
80
|
-
|
57
|
+
protected
|
58
|
+
def create_successful
|
59
|
+
@user.plugins = @selected_plugin_names
|
81
60
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
61
|
+
# if the user is a superuser and can assign roles according to this site's
|
62
|
+
# settings then the roles are set with the POST data.
|
63
|
+
if user_can_assign_roles?
|
64
|
+
@user.roles = @selected_role_names.map { |r| Refinery::Role[r.downcase] }
|
65
|
+
else
|
66
|
+
@user.add_role :refinery
|
87
67
|
end
|
68
|
+
|
69
|
+
redirect_to refinery.admin_users_path,
|
70
|
+
:notice => t('created', :what => @user.username, :scope => 'refinery.crudify')
|
71
|
+
end
|
72
|
+
|
73
|
+
def create_failed
|
74
|
+
render :action => 'new'
|
88
75
|
end
|
89
|
-
alias_method_chain :find_user, :slug
|
90
76
|
|
91
|
-
def
|
92
|
-
|
77
|
+
def update_successful
|
78
|
+
redirect_to refinery.admin_users_path,
|
79
|
+
:notice => t('updated', :what => @user.username, :scope => 'refinery.crudify')
|
80
|
+
end
|
81
|
+
|
82
|
+
def update_failed
|
83
|
+
user_memento_rollback!
|
84
|
+
|
85
|
+
render :edit
|
86
|
+
end
|
87
|
+
|
88
|
+
def find_available_plugins
|
89
|
+
@available_plugins = Refinery::Plugins.registered.in_menu.map { |a|
|
93
90
|
{ :name => a.name, :title => a.title }
|
94
91
|
}.sort_by { |a| a[:title] }
|
92
|
+
end
|
95
93
|
|
94
|
+
def find_available_roles
|
96
95
|
@available_roles = Refinery::Role.all
|
97
96
|
end
|
98
97
|
|
99
98
|
def redirect_unless_user_editable!
|
100
|
-
unless current_refinery_user.can_edit?
|
101
|
-
|
99
|
+
redirect_to refinery.admin_users_path unless current_refinery_user.can_edit? find_user
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
def exclude_password_assignment_when_blank!
|
104
|
+
if params[:user][:password].blank? && params[:user][:password_confirmation].blank?
|
105
|
+
params[:user].except!(:password, :password_confirmation)
|
102
106
|
end
|
103
107
|
end
|
108
|
+
|
109
|
+
def user_can_assign_roles?
|
110
|
+
Refinery::Authentication.superuser_can_assign_roles &&
|
111
|
+
current_refinery_user.has_role?(:superuser)
|
112
|
+
end
|
113
|
+
|
114
|
+
def user_is_locking_themselves_out?
|
115
|
+
return false if current_refinery_user.id != @user.id || @selected_plugin_names.blank?
|
116
|
+
|
117
|
+
@selected_plugin_names.exclude?('refinery_users') || # removing user plugin access
|
118
|
+
@selected_role_names.map(&:downcase).exclude?('refinery') # Or we're removing the refinery role
|
119
|
+
end
|
120
|
+
|
121
|
+
def store_user_memento
|
122
|
+
# Store the current plugins and roles for this user.
|
123
|
+
@previously_selected_plugin_names = @user.plugins.map(&:name)
|
124
|
+
@previously_selected_roles = @user.roles
|
125
|
+
end
|
126
|
+
|
127
|
+
def user_memento_rollback!
|
128
|
+
@user.plugins = @previously_selected_plugin_names
|
129
|
+
@user.roles = @previously_selected_roles
|
130
|
+
@user.save
|
131
|
+
end
|
104
132
|
end
|
105
133
|
end
|
106
134
|
end
|
@@ -36,7 +36,7 @@ module Refinery
|
|
36
36
|
# Call devise reset function.
|
37
37
|
user.send(:generate_reset_password_token!)
|
38
38
|
UserMailer.reset_notification(user, request).deliver
|
39
|
-
redirect_to refinery.
|
39
|
+
redirect_to refinery.login_path,
|
40
40
|
:notice => t('email_reset_sent', :scope => 'refinery.users.forgot')
|
41
41
|
else
|
42
42
|
flash.now[:error] = if (email = params[:refinery_user][:email]).blank?
|
data/app/models/refinery/user.rb
CHANGED
@@ -8,7 +8,7 @@ module Refinery
|
|
8
8
|
has_and_belongs_to_many :roles, :join_table => :refinery_roles_users
|
9
9
|
|
10
10
|
has_many :plugins, :class_name => "UserPlugin", :order => "position ASC", :dependent => :destroy
|
11
|
-
friendly_id :username
|
11
|
+
friendly_id :username, :use => [:slugged]
|
12
12
|
|
13
13
|
# Include default devise modules. Others available are:
|
14
14
|
# :token_authenticatable, :confirmable, :lockable and :timeoutable
|
@@ -24,7 +24,7 @@ module Refinery
|
|
24
24
|
attr_accessible :email, :password, :password_confirmation, :remember_me, :username, :plugins, :login
|
25
25
|
|
26
26
|
validates :username, :presence => true, :uniqueness => true
|
27
|
-
before_validation :downcase_username
|
27
|
+
before_validation :downcase_username, :strip_username
|
28
28
|
|
29
29
|
class << self
|
30
30
|
# Find user by email or username.
|
@@ -36,11 +36,28 @@ module Refinery
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def plugins=(plugin_names)
|
39
|
-
|
40
|
-
UserPlugin.delete_all(:user_id => id)
|
39
|
+
return unless persisted?
|
41
40
|
|
41
|
+
plugin_names = plugin_names.dup
|
42
|
+
plugin_names.reject! { |plugin_name| !plugin_name.is_a?(String) }
|
43
|
+
|
44
|
+
if plugins.empty?
|
42
45
|
plugin_names.each_with_index do |plugin_name, index|
|
43
|
-
plugins.create(:name => plugin_name, :position => index)
|
46
|
+
plugins.create(:name => plugin_name, :position => index)
|
47
|
+
end
|
48
|
+
else
|
49
|
+
assigned_plugins = plugins.all
|
50
|
+
assigned_plugins.each do |assigned_plugin|
|
51
|
+
if plugin_names.include?(assigned_plugin.name)
|
52
|
+
plugin_names.delete(assigned_plugin.name)
|
53
|
+
else
|
54
|
+
assigned_plugin.destroy
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
plugin_names.each do |plugin_name|
|
59
|
+
plugins.create(:name => plugin_name,
|
60
|
+
:position => plugins.select(:position).map{|p| p.position.to_i}.max + 1)
|
44
61
|
end
|
45
62
|
end
|
46
63
|
end
|
@@ -90,10 +107,6 @@ module Refinery
|
|
90
107
|
username.to_s
|
91
108
|
end
|
92
109
|
|
93
|
-
def to_param
|
94
|
-
to_s.parameterize
|
95
|
-
end
|
96
|
-
|
97
110
|
private
|
98
111
|
# To ensure uniqueness without case sensitivity we first downcase the username.
|
99
112
|
# We do this here and not in SQL is that it will otherwise bypass indexes using LOWER:
|
@@ -102,5 +115,11 @@ module Refinery
|
|
102
115
|
self.username = self.username.downcase if self.username?
|
103
116
|
end
|
104
117
|
|
118
|
+
# To ensure that we aren't creating "admin" and "admin " as the same thing.
|
119
|
+
# Also ensures that "admin user" and "admin user" are the same thing.
|
120
|
+
def strip_username
|
121
|
+
self.username = self.username.strip.gsub(/\ {2,}/, ' ') if self.username?
|
122
|
+
end
|
123
|
+
|
105
124
|
end
|
106
125
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
<%= form_for [refinery, :admin, @user] do |f| %>
|
2
2
|
|
3
|
-
<%= render '/refinery/admin/error_messages',
|
3
|
+
<%= render '/refinery/admin/error_messages',
|
4
|
+
:object => @user,
|
5
|
+
:include_object_name => true %>
|
4
6
|
|
5
7
|
<div class='field'>
|
6
8
|
<%= f.label :username %>
|
@@ -14,7 +16,7 @@
|
|
14
16
|
<%= f.label :password %>
|
15
17
|
<%= f.password_field :password, :autocomplete => 'off' %>
|
16
18
|
<% if @user.persisted? %>
|
17
|
-
|
19
|
+
<br>
|
18
20
|
<%= content_tag(:span, t('.blank_password_keeps_current')) %>
|
19
21
|
<% end %>
|
20
22
|
</div>
|
@@ -30,7 +32,7 @@
|
|
30
32
|
<ul id='plugins' class='checkboxes'>
|
31
33
|
<% @available_plugins.each do |plugin| -%>
|
32
34
|
<% if Refinery::Plugins.always_allowed.names.include?(plugin[:name]) or
|
33
|
-
(plugin[:name] == 'refinery_users'
|
35
|
+
(plugin[:name] == 'refinery_users' && @user.id == current_refinery_user.id) %>
|
34
36
|
<%= hidden_field_tag 'user[plugins][]', plugin[:name],
|
35
37
|
:id => "plugins_#{plugin[:name]}" %>
|
36
38
|
<% else %>
|
@@ -48,7 +50,7 @@
|
|
48
50
|
</ul>
|
49
51
|
</div>
|
50
52
|
|
51
|
-
<% if current_refinery_user.has_role?(:superuser)
|
53
|
+
<% if current_refinery_user.has_role?(:superuser) && Refinery::Authentication.superuser_can_assign_roles %>
|
52
54
|
<div class='field role_access'>
|
53
55
|
<span class='label_with_help'>
|
54
56
|
<%= f.label :role_access, t('.role_access'), :class => "title_label" %>
|
@@ -1,3 +1,10 @@
|
|
1
|
+
<%= render 'refinery/admin/search_header', :url => refinery.admin_users_path %>
|
2
|
+
<% if @users.any? %>
|
1
3
|
<div class='pagination_container'>
|
2
4
|
<%= render 'users' %>
|
3
5
|
</div>
|
6
|
+
<% else %>
|
7
|
+
<p>
|
8
|
+
<%= t('no_results', :scope => 'refinery.admin.search') %>
|
9
|
+
</p>
|
10
|
+
<% end %>
|
@@ -20,7 +20,7 @@
|
|
20
20
|
<%= render '/refinery/admin/form_actions', :f => f,
|
21
21
|
:continue_editing => false,
|
22
22
|
:submit_button_text => t('reset_password', :scope => 'refinery.users.reset'),
|
23
|
-
:cancel_url => refinery.
|
23
|
+
:cancel_url => refinery.login_path,
|
24
24
|
:cancel_title => nil,
|
25
25
|
:hide_delete => true -%>
|
26
26
|
<% end -%>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<% content_for :header, t('there_are_no_users', :scope => 'refinery.welcome') %>
|
2
2
|
|
3
|
-
<%= form_for :user, :url => refinery.
|
3
|
+
<%= form_for :user, :url => refinery.signup_path do |f| -%>
|
4
4
|
|
5
5
|
<%= render '/refinery/admin/error_messages', :object => @user, :include_object_name => true %>
|
6
6
|
|
data/config/locales/bg.yml
CHANGED
@@ -9,7 +9,7 @@ bg:
|
|
9
9
|
delete: Изтриване на този потребител завинаги
|
10
10
|
edit: Редактиране на този потребител
|
11
11
|
update:
|
12
|
-
|
12
|
+
lockout_prevented: Не можете да премахнете добавката "Потребители" чрез потребителя, с който сте влезли в момента в системата.
|
13
13
|
form:
|
14
14
|
blank_password_keeps_current: При празно поле текущата парола ще бъде запазена
|
15
15
|
plugin_access: Достъп до добавки
|
data/config/locales/cs.yml
CHANGED
@@ -9,7 +9,7 @@ cs:
|
|
9
9
|
delete: Smazat tohoto uživatele
|
10
10
|
edit: Editovat tohoto uživatele
|
11
11
|
update:
|
12
|
-
|
12
|
+
lockout_prevented: Nemůžete odstranit 'Users' plugin z aktuálně přihlášeného konta.
|
13
13
|
form:
|
14
14
|
blank_password_keeps_current: Pokud necháte toto pole prázné bude zachováno stávající heslo
|
15
15
|
plugin_access: Přístup k pluginům
|
@@ -23,7 +23,7 @@ cs:
|
|
23
23
|
sessions:
|
24
24
|
new:
|
25
25
|
hello_please_sign_in: Prosím přihlašte se
|
26
|
-
sign_in:
|
26
|
+
sign_in: Přihlásit se
|
27
27
|
forgot_password: Zapomenuté heslo
|
28
28
|
users:
|
29
29
|
new:
|
@@ -67,7 +67,7 @@ cs:
|
|
67
67
|
refinery/user: uživatel
|
68
68
|
attributes:
|
69
69
|
refinery/user:
|
70
|
-
login:
|
70
|
+
login: Uživatelské jméno nebo email
|
71
71
|
email: Email
|
72
72
|
username: Uživatelské jméno
|
73
73
|
password: Heslo
|
data/config/locales/da.yml
CHANGED
@@ -9,7 +9,7 @@ da:
|
|
9
9
|
delete: Slet bruger
|
10
10
|
edit: Redigér bruger
|
11
11
|
update:
|
12
|
-
|
12
|
+
lockout_prevented: "Du kan ikke deaktivere 'Brugere' for en bruger, der er logget på."
|
13
13
|
form:
|
14
14
|
blank_password_keeps_current: 'Hvis du ikke indtaster noget, beholdes den nuværende adgangskode'
|
15
15
|
plugin_access: Plugin adgang
|
data/config/locales/de.yml
CHANGED
@@ -9,7 +9,7 @@ de:
|
|
9
9
|
delete: Diesen Benutzer für immer löschen
|
10
10
|
edit: Diesen Benutzer bearbeiten
|
11
11
|
update:
|
12
|
-
|
12
|
+
lockout_prevented: Sie können die Erweiterung 'Benutzer' nicht vom aktuellen Konto entfernen.
|
13
13
|
form:
|
14
14
|
blank_password_keeps_current: 'Wird das Passwort leer gelassen, wird das aktuelle Passwort beibehalten'
|
15
15
|
plugin_access: Zugriff auf Erweiterungen
|
data/config/locales/el.yml
CHANGED
@@ -9,7 +9,7 @@ el:
|
|
9
9
|
delete: Διαγραφή χρήστη
|
10
10
|
edit: Επεξεργασία χρήστη
|
11
11
|
update:
|
12
|
-
|
12
|
+
lockout_prevented: Δεν μπορείτε να διαγράψετε το 'Users' plugin για αυτόν τον λογαριασμό.
|
13
13
|
form:
|
14
14
|
blank_password_keeps_current: Αφήνοντας κενό τον κωδικό δε θα άλλαξει
|
15
15
|
plugin_access: Πρόσβαση Plugin
|
data/config/locales/en.yml
CHANGED
@@ -9,7 +9,7 @@ en:
|
|
9
9
|
delete: Remove this user forever
|
10
10
|
edit: Edit this user
|
11
11
|
update:
|
12
|
-
|
12
|
+
lockout_prevented: You cannot remove the 'Users' plugin from the currently logged in account.
|
13
13
|
form:
|
14
14
|
blank_password_keeps_current: Leaving password blank keeps the current password
|
15
15
|
plugin_access: Plugin access
|
@@ -57,6 +57,7 @@ en:
|
|
57
57
|
failure:
|
58
58
|
unauthenticated: You need to sign in before continuing.
|
59
59
|
invalid: "Sorry, your login or password was incorrect."
|
60
|
+
not_found_in_database: "Sorry, your login or password was incorrect."
|
60
61
|
sessions:
|
61
62
|
signed_in: Signed in successfully.
|
62
63
|
activerecord:
|
@@ -64,7 +65,7 @@ en:
|
|
64
65
|
refinery/user: user
|
65
66
|
attributes:
|
66
67
|
refinery/user:
|
67
|
-
login:
|
68
|
+
login: Username or email
|
68
69
|
username: Username
|
69
70
|
password: Password
|
70
71
|
password_confirmation: Password confirmation
|