refinerycms-authentication 0.9.9.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/app/controllers/admin/users_controller.rb +90 -0
- data/app/controllers/passwords_controller.rb +43 -0
- data/app/controllers/registrations_controller.rb +67 -0
- data/app/controllers/sessions_controller.rb +23 -0
- data/app/helpers/sessions_helper.rb +2 -0
- data/app/helpers/users_helper.rb +2 -0
- data/app/mailers/user_mailer.rb +20 -0
- data/app/models/role.rb +16 -0
- data/app/models/roles_users.rb +6 -0
- data/app/models/user.rb +60 -0
- data/app/models/user_plugin.rb +5 -0
- data/app/views/admin/users/_form.html.erb +92 -0
- data/app/views/admin/users/_user.html.erb +19 -0
- data/app/views/admin/users/_users.html.erb +4 -0
- data/app/views/admin/users/edit.html.erb +1 -0
- data/app/views/admin/users/index.html.erb +12 -0
- data/app/views/admin/users/new.html.erb +1 -0
- data/app/views/layouts/login.html.erb +21 -0
- data/app/views/passwords/edit.html.erb +31 -0
- data/app/views/passwords/new.html.erb +18 -0
- data/app/views/registrations/new.html.erb +41 -0
- data/app/views/sessions/new.html.erb +29 -0
- data/app/views/user_mailer/reset_notification.html.erb +12 -0
- data/app/views/user_mailer/reset_notification.text.plain.erb +7 -0
- data/config/locales/cs.yml +75 -0
- data/config/locales/da.yml +72 -0
- data/config/locales/de.yml +72 -0
- data/config/locales/el.yml +72 -0
- data/config/locales/en.yml +72 -0
- data/config/locales/es.yml +100 -0
- data/config/locales/fr.yml +72 -0
- data/config/locales/it.yml +97 -0
- data/config/locales/lolcat.yml +55 -0
- data/config/locales/lt.yml +55 -0
- data/config/locales/lv.yml +72 -0
- data/config/locales/nb.yml +72 -0
- data/config/locales/nl.yml +70 -0
- data/config/locales/pl.yml +100 -0
- data/config/locales/pt-BR.yml +68 -0
- data/config/locales/rs.yml +72 -0
- data/config/locales/ru.yml +97 -0
- data/config/locales/sl.yml +61 -0
- data/config/locales/sv.yml +64 -0
- data/config/locales/vi.yml +72 -0
- data/config/locales/zh-CN.yml +72 -0
- data/config/locales/zh-TW.yml +72 -0
- data/config/routes.rb +31 -0
- data/db/migrate/20100913234705_create_refinerycms_authentication_schema.rb +43 -0
- data/db/migrate/20100929035252_add_missing_indexes_to_roles_users.rb +11 -0
- data/db/migrate/20101206013505_change_to_devise_users_table.rb +27 -0
- data/db/migrate/20110106184757_add_remember_created_at_to_users.rb +9 -0
- data/features/lost_password.feature +49 -0
- data/features/manage_users.feature +61 -0
- data/features/step_definitions/lost_password.rb +8 -0
- data/features/step_definitions/user_steps.rb +36 -0
- data/features/support/factories.rb +18 -0
- data/features/support/paths.rb +24 -0
- data/lib/authenticated_system.rb +29 -0
- data/lib/gemspec.rb +34 -0
- data/lib/generators/refinerycms_authentication_generator.rb +8 -0
- data/lib/refinerycms-authentication.rb +47 -0
- data/license.md +21 -0
- data/readme.md +17 -0
- data/refinerycms-authentication.gemspec +112 -0
- data/spec/models/user_spec.rb +159 -0
- metadata +144 -0
@@ -0,0 +1,90 @@
|
|
1
|
+
module Admin
|
2
|
+
class UsersController < Admin::BaseController
|
3
|
+
|
4
|
+
crudify :user, :order => 'username ASC', :title_attribute => 'username'
|
5
|
+
|
6
|
+
before_filter :load_available_plugins_and_roles, :only => [:new, :create, :edit, :update]
|
7
|
+
|
8
|
+
def index
|
9
|
+
search_all_users if searching?
|
10
|
+
paginate_all_users
|
11
|
+
|
12
|
+
render :partial => 'users' if request.xhr?
|
13
|
+
end
|
14
|
+
|
15
|
+
def new
|
16
|
+
@user = User.new
|
17
|
+
@selected_plugin_names = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def create
|
21
|
+
@user = User.new(params[:user])
|
22
|
+
@selected_plugin_names = params[:user][:plugins] || []
|
23
|
+
@selected_role_names = params[:user][:roles] || []
|
24
|
+
|
25
|
+
if @user.save
|
26
|
+
@user.plugins = @selected_plugin_names
|
27
|
+
# if the user is a superuser and can assign roles according to this site's
|
28
|
+
# settings then the roles are set with the POST data.
|
29
|
+
unless current_user.has_role?(:superuser) and RefinerySetting.find_or_set(:superuser_can_assign_roles, false)
|
30
|
+
@user.add_role(:refinery)
|
31
|
+
else
|
32
|
+
@user.roles = @selected_role_names.collect{|r| Role[r.downcase.to_sym]}
|
33
|
+
end
|
34
|
+
|
35
|
+
redirect_to(admin_users_url, :notice => t('created', :what => @user.username, :scope => 'refinery.crudify'))
|
36
|
+
else
|
37
|
+
render :action => 'new'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def edit
|
42
|
+
@user = User.find params[:id]
|
43
|
+
@selected_plugin_names = @user.plugins.collect{|p| p.name}
|
44
|
+
end
|
45
|
+
|
46
|
+
def update
|
47
|
+
# Store what the user selected.
|
48
|
+
@selected_role_names = params[:user].delete(:roles) || []
|
49
|
+
unless current_user.has_role?(:superuser) and RefinerySetting.find_or_set(:superuser_can_assign_roles, false)
|
50
|
+
@selected_role_names = @user.roles.collect{|r| r.title}
|
51
|
+
end
|
52
|
+
@selected_plugin_names = params[:user][:plugins]
|
53
|
+
|
54
|
+
# Prevent the current user from locking themselves out of the User manager
|
55
|
+
if current_user.id == @user.id and (params[:user][:plugins].exclude?("refinery_users") || @selected_role_names.map(&:downcase).exclude?("refinery"))
|
56
|
+
flash.now[:error] = t('cannot_remove_user_plugin_from_current_user', :scope => 'admin.users.update')
|
57
|
+
render :action => "edit"
|
58
|
+
else
|
59
|
+
# Store the current plugins and roles for this user.
|
60
|
+
@previously_selected_plugin_names = @user.plugins.collect{|p| p.name}
|
61
|
+
@previously_selected_roles = @user.roles
|
62
|
+
@user.roles = @selected_role_names.collect{|r| Role[r.downcase.to_sym]}
|
63
|
+
if params[:user][:password].blank? and params[:user][:password_confirmation].blank?
|
64
|
+
params[:user].delete(:password)
|
65
|
+
params[:user].delete(:password_confirmation)
|
66
|
+
end
|
67
|
+
|
68
|
+
if @user.update_attributes(params[:user])
|
69
|
+
redirect_to admin_users_url, :notice => t('updated', :what => @user.username, :scope => 'refinery.crudify')
|
70
|
+
else
|
71
|
+
@user.plugins = @previously_selected_plugin_names
|
72
|
+
@user.roles = @previously_selected_roles
|
73
|
+
@user.save
|
74
|
+
render :action => 'edit'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
|
81
|
+
def load_available_plugins_and_roles
|
82
|
+
@available_plugins = ::Refinery::Plugins.registered.in_menu.collect{|a|
|
83
|
+
{:name => a.name, :title => a.title}
|
84
|
+
}.sort_by {|a| a[:title]}
|
85
|
+
|
86
|
+
@available_roles = Role.all
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class PasswordsController < ::Devise::PasswordsController
|
2
|
+
layout 'login'
|
3
|
+
|
4
|
+
# Rather than overriding devise, it seems better to just apply the notice here.
|
5
|
+
after_filter :give_notice, :only => [:update]
|
6
|
+
def give_notice
|
7
|
+
unless %w(notice error alert).include?(flash.keys.map(&:to_s)) or @user.errors.any?
|
8
|
+
flash[:notice] = t('successful', :scope => 'users.reset', :email => @user.email)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
protected :give_notice
|
12
|
+
|
13
|
+
# GET /registrations/password/edit?reset_password_token=abcdef
|
14
|
+
def edit
|
15
|
+
if params[:reset_password_token] and (@user = User.find_by_reset_password_token(params[:reset_password_token])).present?
|
16
|
+
render_with_scope :edit
|
17
|
+
else
|
18
|
+
redirect_to(new_user_password_url, :flash => ({
|
19
|
+
:error => t('code_invalid', :scope => 'users.reset')
|
20
|
+
}))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# POST /registrations/password
|
25
|
+
def create
|
26
|
+
if params[:user].present? and (email = params[:user][:email]).present? and
|
27
|
+
(user = User.find_by_email(email)).present?
|
28
|
+
|
29
|
+
# Call devise reset function.
|
30
|
+
user.send(:generate_reset_password_token!)
|
31
|
+
UserMailer.reset_notification(user, request).deliver
|
32
|
+
redirect_to new_user_session_path, :notice => t('email_reset_sent', :scope => 'users.forgot') and return
|
33
|
+
else
|
34
|
+
@user = User.new(params[:user])
|
35
|
+
flash.now[:error] = if (email = params[:user][:email]).blank?
|
36
|
+
t('blank_email', :scope => 'users.forgot')
|
37
|
+
else
|
38
|
+
t('email_not_associated_with_account_html', :email => email, :scope => 'users.forgot').html_safe
|
39
|
+
end
|
40
|
+
render_with_scope :new
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
class RegistrationsController < ::Devise::RegistrationsController
|
2
|
+
|
3
|
+
# Protect these actions behind an admin login
|
4
|
+
before_filter :redirect?, :only => [:new, :create]
|
5
|
+
|
6
|
+
layout 'login'
|
7
|
+
|
8
|
+
def new
|
9
|
+
@user = User.new
|
10
|
+
end
|
11
|
+
|
12
|
+
# This method should only be used to create the first Refinery user.
|
13
|
+
def create
|
14
|
+
@user = User.new(params[:user])
|
15
|
+
@selected_plugin_titles = params[:user][:plugins] || []
|
16
|
+
|
17
|
+
@user.save if @user.valid?
|
18
|
+
|
19
|
+
if @user.errors.empty?
|
20
|
+
@user.add_role(:refinery)
|
21
|
+
@user.plugins = @selected_plugin_titles
|
22
|
+
@user.save
|
23
|
+
if Role[:refinery].users.count == 1
|
24
|
+
# this is the superuser if this user is the only user.
|
25
|
+
@user.add_role(:superuser)
|
26
|
+
@user.save
|
27
|
+
|
28
|
+
# set this user as the recipient of inquiry notifications, if we're using that engine.
|
29
|
+
if defined?(InquirySetting) and
|
30
|
+
(notification_recipients = InquirySetting.find_or_create_by_name("Notification Recipients")).present?
|
31
|
+
notification_recipients.update_attributes({
|
32
|
+
:value => @user.email,
|
33
|
+
:destroyable => false
|
34
|
+
})
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
flash[:message] = "<h2>#{t('welcome', :scope => 'users.create', :who => @user.username).gsub(/\.$/, '')}.</h2>".html_safe
|
39
|
+
|
40
|
+
site_name_setting = RefinerySetting.find_or_create_by_name('site_name', :value => "Company Name")
|
41
|
+
if site_name_setting.value.to_s =~ /^(|Company\ Name)$/ or Role[:refinery].users.count == 1
|
42
|
+
flash[:message] << "<p>#{t('setup_website_name_html', :scope => 'users',
|
43
|
+
:link => edit_admin_refinery_setting_url(site_name_setting, :dialog => true),
|
44
|
+
:title => t('edit', :scope => 'admin.refinery_settings'))}</p>".html_safe
|
45
|
+
end
|
46
|
+
sign_in(@user)
|
47
|
+
redirect_back_or_default(admin_root_url)
|
48
|
+
else
|
49
|
+
render :action => 'new'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
protected
|
54
|
+
|
55
|
+
def redirect?
|
56
|
+
if refinery_user?
|
57
|
+
redirect_to admin_users_url
|
58
|
+
elsif refinery_users_exist?
|
59
|
+
redirect_to new_user_session_path
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def refinery_users_exist?
|
64
|
+
Role[:refinery].users.any?
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class SessionsController < ::Devise::SessionsController
|
2
|
+
layout 'login'
|
3
|
+
|
4
|
+
before_filter :clear_unauthenticated_flash, :only => [:new]
|
5
|
+
|
6
|
+
def create
|
7
|
+
super
|
8
|
+
rescue BCrypt::Errors::InvalidSalt
|
9
|
+
flash[:error] = t('password_encryption', :scope => 'users.forgot')
|
10
|
+
redirect_to new_user_password_path
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
# We don't like this alert.
|
15
|
+
def clear_unauthenticated_flash
|
16
|
+
if flash.keys.include?(:alert) and flash.values.any?{|v|
|
17
|
+
['unauthenticated', t('unauthenticated', :scope => 'devise.failure')].include?(v)
|
18
|
+
}
|
19
|
+
flash.delete(:alert)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class UserMailer < ActionMailer::Base
|
2
|
+
|
3
|
+
def reset_notification(user, request)
|
4
|
+
@user = user
|
5
|
+
@url = edit_user_password_url(:host => request.host_with_port,
|
6
|
+
:reset_password_token => @user.reset_password_token)
|
7
|
+
|
8
|
+
domain = request.domain(RefinerySetting.find_or_set(:tld_length, 1))
|
9
|
+
|
10
|
+
mail(:to => user.email,
|
11
|
+
:subject => t('subject', :scope => 'user_mailer.reset_notification'),
|
12
|
+
:from => "\"#{RefinerySetting[:site_name]}\" <no-reply@#{domain}>")
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def url_prefix(request)
|
18
|
+
"#{request.protocol}#{request.host_with_port}"
|
19
|
+
end
|
20
|
+
end
|
data/app/models/role.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
class Role < ActiveRecord::Base
|
2
|
+
|
3
|
+
has_and_belongs_to_many :users
|
4
|
+
|
5
|
+
before_validation :camelize_title
|
6
|
+
validates :title, :uniqueness => true
|
7
|
+
|
8
|
+
def camelize_title(role_title = self.title)
|
9
|
+
self.title = role_title.to_s.camelize
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.[](title)
|
13
|
+
find_or_create_by_title(title.to_s.camelize)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
data/app/models/user.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'devise'
|
2
|
+
|
3
|
+
class User < ActiveRecord::Base
|
4
|
+
has_and_belongs_to_many :roles
|
5
|
+
has_many :plugins, :class_name => "UserPlugin", :order => "position ASC", :dependent => :destroy
|
6
|
+
has_friendly_id :username, :use_slug => true
|
7
|
+
|
8
|
+
# Include default devise modules. Others available are:
|
9
|
+
# :token_authenticatable, :confirmable, :lockable and :timeoutable
|
10
|
+
devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable
|
11
|
+
|
12
|
+
# Setup accessible (or protected) attributes for your model
|
13
|
+
# :login is a virtual attribute for authenticating by either username or email
|
14
|
+
# This is in addition to a real persisted field like 'username'
|
15
|
+
attr_accessor :login
|
16
|
+
attr_accessible :email, :password, :password_confirmation, :remember_me, :username, :plugins, :login
|
17
|
+
|
18
|
+
validates :username, :presence => true, :uniqueness => true
|
19
|
+
|
20
|
+
class << self
|
21
|
+
# Find user by email or username.
|
22
|
+
# https://github.com/plataformatec/devise/wiki/How-To:-Allow-users-to-sign_in-using-their-username-or-email-address
|
23
|
+
def find_for_database_authentication(conditions)
|
24
|
+
value = conditions[authentication_keys.first]
|
25
|
+
where(["username = :value OR email = :value", { :value => value }]).first
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def plugins=(plugin_names)
|
30
|
+
if persisted? # don't add plugins when the user_id is nil.
|
31
|
+
UserPlugin.delete_all(:user_id => id)
|
32
|
+
|
33
|
+
plugin_names.each_with_index do |plugin_name, index|
|
34
|
+
plugins.create(:name => plugin_name, :position => index) if plugin_name.is_a?(String)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def authorized_plugins
|
40
|
+
plugins.collect { |p| p.name } | Refinery::Plugins.always_allowed.names
|
41
|
+
end
|
42
|
+
|
43
|
+
def can_delete?(user_to_delete = self)
|
44
|
+
user_to_delete.persisted? and
|
45
|
+
!user_to_delete.has_role?(:superuser) and
|
46
|
+
Role[:refinery].users.count > 1 and
|
47
|
+
id != user_to_delete.id
|
48
|
+
end
|
49
|
+
|
50
|
+
def add_role(title)
|
51
|
+
raise ArgumentException, "Role should be the title of the role not a role object." if title.is_a?(Role)
|
52
|
+
roles << Role[title] unless has_role?(title)
|
53
|
+
end
|
54
|
+
|
55
|
+
def has_role?(title)
|
56
|
+
raise ArgumentException, "Role should be the title of the role not a role object." if title.is_a?(Role)
|
57
|
+
(role = Role.find_by_title(title.to_s.camelize)).present? and roles.collect{|r| r.id}.include?(role.id)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
<%= form_for [:admin, @user] do |f| %>
|
2
|
+
|
3
|
+
<%= render :partial => "/shared/admin/error_messages",
|
4
|
+
:locals => {
|
5
|
+
:object => @user,
|
6
|
+
:include_object_name => true
|
7
|
+
} %>
|
8
|
+
|
9
|
+
<div class='field'>
|
10
|
+
<%= f.label :username %>
|
11
|
+
<%= f.text_field :username %>
|
12
|
+
</div>
|
13
|
+
<div class='field'>
|
14
|
+
<%= f.label :email %>
|
15
|
+
<%= f.text_field :email %>
|
16
|
+
</div>
|
17
|
+
<div class='field'>
|
18
|
+
<%= f.label :password %>
|
19
|
+
<%= f.password_field :password, :autocomplete => 'off' %>
|
20
|
+
<%= "<br /><span class='preview'>#{t('.blank_password_keeps_current')}</span>".html_safe if @user.persisted? %>
|
21
|
+
</div>
|
22
|
+
<div class='field'>
|
23
|
+
<%= f.label :password_confirmation %>
|
24
|
+
<%= f.password_field :password_confirmation, :autocomplete => 'off' %>
|
25
|
+
</div>
|
26
|
+
<div class='field plugin_access'>
|
27
|
+
<span class='label_with_help'>
|
28
|
+
<%= f.label :plugin_access, t('.plugin_access'), :class => "title_label" %>
|
29
|
+
<%= link_to "(#{t('.enable_all')})", "", :id => "user_plugins_enable_all" %>
|
30
|
+
</span>
|
31
|
+
<ul id='plugins' class='checkboxes'>
|
32
|
+
<% @available_plugins.each do |plugin| -%>
|
33
|
+
<% if Refinery::Plugins.always_allowed.names.include?(plugin[:name]) or
|
34
|
+
(plugin[:name] == 'refinery_users' and @user.id == current_user.id) %>
|
35
|
+
<%= hidden_field_tag 'user[plugins][]', plugin[:name],
|
36
|
+
:id => "plugins_#{plugin[:name]}" %>
|
37
|
+
<% else %>
|
38
|
+
<li>
|
39
|
+
<%= check_box_tag 'user[plugins][]', plugin[:name],
|
40
|
+
@selected_plugin_names.include?(plugin[:name]),
|
41
|
+
:id => "plugins_#{plugin[:name]}" %>
|
42
|
+
<%= f.label 'user[plugins][]',
|
43
|
+
t('title', :scope => "plugins.#{plugin[:name].downcase}", :default => plugin[:title]),
|
44
|
+
:class => "stripped",
|
45
|
+
:for => "plugins_#{plugin[:name]}" %>
|
46
|
+
</li>
|
47
|
+
<% end %>
|
48
|
+
<% end %>
|
49
|
+
</ul>
|
50
|
+
</div>
|
51
|
+
|
52
|
+
<% if current_user.has_role?(:superuser) and RefinerySetting.find_or_set(:superuser_can_assign_roles, false) %>
|
53
|
+
<div class='field role_access'>
|
54
|
+
<span class='label_with_help'>
|
55
|
+
<%= f.label :role_access, t('.role_access'), :class => "title_label" %>
|
56
|
+
</span>
|
57
|
+
<ul id='roles' class='checkboxes'>
|
58
|
+
<% @available_roles.each do |role|
|
59
|
+
downcased_title = (title = role[:title]).downcase -%>
|
60
|
+
<li>
|
61
|
+
<%= check_box_tag 'user[roles][]', downcased_title, @user.has_role?(title),
|
62
|
+
:id => "roles_#{downcased_title}" %>
|
63
|
+
<%= f.label 'user[roles][]',
|
64
|
+
t(downcased_title, :scope => 'roles', :default => title),
|
65
|
+
:class => 'stripped',
|
66
|
+
:for => "roles_#{downcased_title}" %>
|
67
|
+
</li>
|
68
|
+
<% end %>
|
69
|
+
</ul>
|
70
|
+
</div>
|
71
|
+
<% end %>
|
72
|
+
|
73
|
+
<%= render :partial => "/shared/admin/form_actions",
|
74
|
+
:locals => {
|
75
|
+
:f => f,
|
76
|
+
:continue_editing => false,
|
77
|
+
:hide_delete => !current_user.can_delete?(@user),
|
78
|
+
:delete_title => t('delete', :scope => 'admin.users'),
|
79
|
+
:delete_confirmation => t('message', :scope => 'shared.admin.delete', :title => @user.username)
|
80
|
+
} %>
|
81
|
+
<% end %>
|
82
|
+
|
83
|
+
<% content_for :javascripts do %>
|
84
|
+
<script>
|
85
|
+
$(document).ready(function() {
|
86
|
+
$('#user_plugins_enable_all').click(function(e, a) {
|
87
|
+
$('div.field.plugin_access ul#plugins li input:checkbox').attr('checked', true);
|
88
|
+
e.preventDefault();
|
89
|
+
});
|
90
|
+
});
|
91
|
+
</script>
|
92
|
+
<% end %>
|