feat: Unify user and super admin credentials (#3830)
Fixes: #3061, #3489
This commit is contained in:
parent
23965fbaa3
commit
34e8ad9dc5
34 changed files with 303 additions and 233 deletions
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
class AccountBuilder
|
class AccountBuilder
|
||||||
include CustomExceptions::Account
|
include CustomExceptions::Account
|
||||||
pattr_initialize [:account_name!, :email!, :confirmed, :user, :user_full_name, :user_password]
|
pattr_initialize [:account_name!, :email!, :confirmed, :user, :user_full_name, :user_password, :super_admin]
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
if @user.nil?
|
if @user.nil?
|
||||||
|
@ -65,6 +65,7 @@ class AccountBuilder
|
||||||
password: user_password,
|
password: user_password,
|
||||||
password_confirmation: user_password,
|
password_confirmation: user_password,
|
||||||
name: @user_full_name)
|
name: @user_full_name)
|
||||||
|
@user.type = 'SuperAdmin' if @super_admin
|
||||||
@user.confirm if @confirmed
|
@user.confirm if @confirmed
|
||||||
@user.save!
|
@user.save!
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,6 +10,7 @@ class Installation::OnboardingController < ApplicationController
|
||||||
user_full_name: onboarding_params.dig(:user, :name),
|
user_full_name: onboarding_params.dig(:user, :name),
|
||||||
email: onboarding_params.dig(:user, :email),
|
email: onboarding_params.dig(:user, :email),
|
||||||
user_password: params.dig(:user, :password),
|
user_password: params.dig(:user, :password),
|
||||||
|
super_admin: true,
|
||||||
confirmed: true
|
confirmed: true
|
||||||
).perform
|
).perform
|
||||||
rescue StandardError => e
|
rescue StandardError => e
|
||||||
|
|
|
@ -8,7 +8,7 @@ class SuperAdmin::Devise::SessionsController < Devise::SessionsController
|
||||||
def create
|
def create
|
||||||
redirect_to(super_admin_session_path, flash: { error: @error_message }) && return unless valid_credentials?
|
redirect_to(super_admin_session_path, flash: { error: @error_message }) && return unless valid_credentials?
|
||||||
|
|
||||||
sign_in(@super_admin, scope: :super_admin)
|
sign_in(:super_admin, @super_admin)
|
||||||
flash.discard
|
flash.discard
|
||||||
redirect_to super_admin_users_path
|
redirect_to super_admin_users_path
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
class SuperAdmin::SuperAdminsController < SuperAdmin::ApplicationController
|
|
||||||
# Overwrite any of the RESTful controller actions to implement custom behavior
|
|
||||||
# For example, you may want to send an email after a foo is updated.
|
|
||||||
#
|
|
||||||
# def update
|
|
||||||
# super
|
|
||||||
# send_foo_updated_email(requested_resource)
|
|
||||||
# end
|
|
||||||
|
|
||||||
# Override this method to specify custom lookup behavior.
|
|
||||||
# This will be used to set the resource for the `show`, `edit`, and `update`
|
|
||||||
# actions.
|
|
||||||
#
|
|
||||||
# def find_resource(param)
|
|
||||||
# Foo.find_by!(slug: param)
|
|
||||||
# end
|
|
||||||
|
|
||||||
# The result of this lookup will be available as `requested_resource`
|
|
||||||
|
|
||||||
# Override this if you have certain roles that require a subset
|
|
||||||
# this will be used to set the records shown on the `index` action.
|
|
||||||
#
|
|
||||||
# def scoped_resource
|
|
||||||
# if current_user.super_admin?
|
|
||||||
# resource_class
|
|
||||||
# else
|
|
||||||
# resource_class.with_less_stuff
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
|
|
||||||
# Override `resource_params` if you want to transform the submitted
|
|
||||||
# data before it's persisted. For example, the following would turn all
|
|
||||||
# empty values into nil values. It uses other APIs such as `resource_class`
|
|
||||||
# and `dashboard`:
|
|
||||||
#
|
|
||||||
# def resource_params
|
|
||||||
# params.require(resource_class.model_name.param_key).
|
|
||||||
# permit(dashboard.permitted_attributes).
|
|
||||||
# transform_values { |value| value == "" ? nil : value }
|
|
||||||
# end
|
|
||||||
|
|
||||||
# See https://administrate-prototype.herokuapp.com/customizing_controller_actions
|
|
||||||
# for more information
|
|
||||||
end
|
|
|
@ -33,12 +33,15 @@ class SuperAdmin::UsersController < SuperAdmin::ApplicationController
|
||||||
# empty values into nil values. It uses other APIs such as `resource_class`
|
# empty values into nil values. It uses other APIs such as `resource_class`
|
||||||
# and `dashboard`:
|
# and `dashboard`:
|
||||||
#
|
#
|
||||||
# def resource_params
|
def resource_params
|
||||||
# params.require(resource_class.model_name.param_key).
|
permitted_params = super
|
||||||
# permit(dashboard.permitted_attributes).
|
permitted_params.delete(:password) if permitted_params[:password].blank?
|
||||||
# transform_values { |value| value == "" ? nil : value }
|
permitted_params
|
||||||
# end
|
end
|
||||||
|
|
||||||
# See https://administrate-prototype.herokuapp.com/customizing_controller_actions
|
# See https://administrate-prototype.herokuapp.com/customizing_controller_actions
|
||||||
# for more information
|
# for more information
|
||||||
|
def find_resource(param)
|
||||||
|
super.becomes(User)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
require 'administrate/base_dashboard'
|
|
||||||
|
|
||||||
class SuperAdminDashboard < Administrate::BaseDashboard
|
|
||||||
# ATTRIBUTE_TYPES
|
|
||||||
# a hash that describes the type of each of the model's fields.
|
|
||||||
#
|
|
||||||
# Each different type represents an Administrate::Field object,
|
|
||||||
# which determines how the attribute is displayed
|
|
||||||
# on pages throughout the dashboard.
|
|
||||||
ATTRIBUTE_TYPES = {
|
|
||||||
id: Field::Number,
|
|
||||||
email: Field::String,
|
|
||||||
password: Field::Password,
|
|
||||||
remember_created_at: Field::DateTime,
|
|
||||||
sign_in_count: Field::Number,
|
|
||||||
current_sign_in_at: Field::DateTime,
|
|
||||||
last_sign_in_at: Field::DateTime,
|
|
||||||
current_sign_in_ip: Field::String.with_options(searchable: false),
|
|
||||||
last_sign_in_ip: Field::String.with_options(searchable: false),
|
|
||||||
created_at: Field::DateTime,
|
|
||||||
updated_at: Field::DateTime
|
|
||||||
}.freeze
|
|
||||||
|
|
||||||
# COLLECTION_ATTRIBUTES
|
|
||||||
# an array of attributes that will be displayed on the model's index page.
|
|
||||||
#
|
|
||||||
# By default, it's limited to four items to reduce clutter on index pages.
|
|
||||||
# Feel free to add, remove, or rearrange items.
|
|
||||||
COLLECTION_ATTRIBUTES = %i[
|
|
||||||
id
|
|
||||||
email
|
|
||||||
].freeze
|
|
||||||
|
|
||||||
# SHOW_PAGE_ATTRIBUTES
|
|
||||||
# an array of attributes that will be displayed on the model's show page.
|
|
||||||
SHOW_PAGE_ATTRIBUTES = %i[
|
|
||||||
id
|
|
||||||
email
|
|
||||||
remember_created_at
|
|
||||||
sign_in_count
|
|
||||||
current_sign_in_at
|
|
||||||
last_sign_in_at
|
|
||||||
current_sign_in_ip
|
|
||||||
last_sign_in_ip
|
|
||||||
created_at
|
|
||||||
updated_at
|
|
||||||
].freeze
|
|
||||||
|
|
||||||
# FORM_ATTRIBUTES
|
|
||||||
# an array of attributes that will be displayed
|
|
||||||
# on the model's form (`new` and `edit`) pages.
|
|
||||||
FORM_ATTRIBUTES = %i[
|
|
||||||
email
|
|
||||||
password
|
|
||||||
].freeze
|
|
||||||
|
|
||||||
# COLLECTION_FILTERS
|
|
||||||
# a hash that defines filters that can be used while searching via the search
|
|
||||||
# field of the dashboard.
|
|
||||||
#
|
|
||||||
# For example to add an option to search for open resources by typing "open:"
|
|
||||||
# in the search field:
|
|
||||||
#
|
|
||||||
# COLLECTION_FILTERS = {
|
|
||||||
# open: ->(resources) { resources.where(open: true) }
|
|
||||||
# }.freeze
|
|
||||||
COLLECTION_FILTERS = {}.freeze
|
|
||||||
|
|
||||||
# Overwrite this method to customize how super admins are displayed
|
|
||||||
# across all pages of the admin dashboard.
|
|
||||||
#
|
|
||||||
# def display_resource(super_admin)
|
|
||||||
# "SuperAdmin ##{super_admin.id}"
|
|
||||||
# end
|
|
||||||
end
|
|
|
@ -30,6 +30,7 @@ class UserDashboard < Administrate::BaseDashboard
|
||||||
created_at: Field::DateTime,
|
created_at: Field::DateTime,
|
||||||
updated_at: Field::DateTime,
|
updated_at: Field::DateTime,
|
||||||
pubsub_token: Field::String,
|
pubsub_token: Field::String,
|
||||||
|
type: Field::Select.with_options(collection: [nil, 'SuperAdmin']),
|
||||||
accounts: CountField
|
accounts: CountField
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
|
@ -44,6 +45,7 @@ class UserDashboard < Administrate::BaseDashboard
|
||||||
name
|
name
|
||||||
email
|
email
|
||||||
accounts
|
accounts
|
||||||
|
type
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
# SHOW_PAGE_ATTRIBUTES
|
# SHOW_PAGE_ATTRIBUTES
|
||||||
|
@ -53,10 +55,12 @@ class UserDashboard < Administrate::BaseDashboard
|
||||||
avatar_url
|
avatar_url
|
||||||
unconfirmed_email
|
unconfirmed_email
|
||||||
name
|
name
|
||||||
|
type
|
||||||
display_name
|
display_name
|
||||||
email
|
email
|
||||||
created_at
|
created_at
|
||||||
updated_at
|
updated_at
|
||||||
|
confirmed_at
|
||||||
account_users
|
account_users
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
|
@ -68,6 +72,8 @@ class UserDashboard < Administrate::BaseDashboard
|
||||||
display_name
|
display_name
|
||||||
email
|
email
|
||||||
password
|
password
|
||||||
|
confirmed_at
|
||||||
|
type
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
# COLLECTION_FILTERS
|
# COLLECTION_FILTERS
|
||||||
|
|
|
@ -12,7 +12,7 @@ module Avatarable
|
||||||
def avatar_url
|
def avatar_url
|
||||||
return url_for(avatar.representation(resize: '250x250')) if avatar.attached? && avatar.representable?
|
return url_for(avatar.representation(resize: '250x250')) if avatar.attached? && avatar.representable?
|
||||||
|
|
||||||
if [User, Contact].include?(self.class) && email.present?
|
if [SuperAdmin, User, Contact].include?(self.class) && email.present?
|
||||||
hash = Digest::MD5.hexdigest(email)
|
hash = Digest::MD5.hexdigest(email)
|
||||||
return "https://www.gravatar.com/avatar/#{hash}?d=404"
|
return "https://www.gravatar.com/avatar/#{hash}?d=404"
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,25 +1,41 @@
|
||||||
# == Schema Information
|
# == Schema Information
|
||||||
#
|
#
|
||||||
# Table name: super_admins
|
# Table name: users
|
||||||
#
|
#
|
||||||
# id :bigint not null, primary key
|
# id :integer not null, primary key
|
||||||
# current_sign_in_at :datetime
|
# availability :integer default("online")
|
||||||
# current_sign_in_ip :inet
|
# confirmation_sent_at :datetime
|
||||||
# email :string default(""), not null
|
# confirmation_token :string
|
||||||
# encrypted_password :string default(""), not null
|
# confirmed_at :datetime
|
||||||
# last_sign_in_at :datetime
|
# current_sign_in_at :datetime
|
||||||
# last_sign_in_ip :inet
|
# current_sign_in_ip :string
|
||||||
# remember_created_at :datetime
|
# custom_attributes :jsonb
|
||||||
# sign_in_count :integer default(0), not null
|
# display_name :string
|
||||||
# created_at :datetime not null
|
# email :string
|
||||||
# updated_at :datetime not null
|
# encrypted_password :string default(""), not null
|
||||||
|
# last_sign_in_at :datetime
|
||||||
|
# last_sign_in_ip :string
|
||||||
|
# name :string not null
|
||||||
|
# provider :string default("email"), not null
|
||||||
|
# pubsub_token :string
|
||||||
|
# remember_created_at :datetime
|
||||||
|
# reset_password_sent_at :datetime
|
||||||
|
# reset_password_token :string
|
||||||
|
# sign_in_count :integer default(0), not null
|
||||||
|
# tokens :json
|
||||||
|
# type :string
|
||||||
|
# ui_settings :jsonb
|
||||||
|
# uid :string default(""), not null
|
||||||
|
# unconfirmed_email :string
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
#
|
#
|
||||||
# Indexes
|
# Indexes
|
||||||
#
|
#
|
||||||
# index_super_admins_on_email (email) UNIQUE
|
# index_users_on_email (email)
|
||||||
|
# index_users_on_pubsub_token (pubsub_token) UNIQUE
|
||||||
|
# index_users_on_reset_password_token (reset_password_token) UNIQUE
|
||||||
|
# index_users_on_uid_and_provider (uid,provider) UNIQUE
|
||||||
#
|
#
|
||||||
class SuperAdmin < ApplicationRecord
|
class SuperAdmin < User
|
||||||
# Include default devise modules. Others available are:
|
|
||||||
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
|
|
||||||
devise :database_authenticatable, :trackable, :rememberable, :validatable, :password_has_required_content
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
# reset_password_token :string
|
# reset_password_token :string
|
||||||
# sign_in_count :integer default(0), not null
|
# sign_in_count :integer default(0), not null
|
||||||
# tokens :json
|
# tokens :json
|
||||||
|
# type :string
|
||||||
# ui_settings :jsonb
|
# ui_settings :jsonb
|
||||||
# uid :string default(""), not null
|
# uid :string default(""), not null
|
||||||
# unconfirmed_email :string
|
# unconfirmed_email :string
|
||||||
|
|
26
app/views/fields/belongs_to/_form.html.erb
Normal file
26
app/views/fields/belongs_to/_form.html.erb
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<%#
|
||||||
|
# BelongsTo Form Partial
|
||||||
|
|
||||||
|
This partial renders an input element for belongs_to relationships.
|
||||||
|
By default, the input is a collection select box
|
||||||
|
that displays all possible records to associate with.
|
||||||
|
|
||||||
|
## Local variables:
|
||||||
|
|
||||||
|
- `f`:
|
||||||
|
A Rails form generator, used to help create the appropriate input fields.
|
||||||
|
- `field`:
|
||||||
|
An instance of [Administrate::Field::BelongsTo][1].
|
||||||
|
Contains helper methods for displaying a collection select box.
|
||||||
|
|
||||||
|
[1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/BelongsTo
|
||||||
|
%>
|
||||||
|
|
||||||
|
<div class="field-unit__label">
|
||||||
|
<%= f.label field.permitted_attribute %>
|
||||||
|
</div>
|
||||||
|
<div class="field-unit__field">
|
||||||
|
<%= f.select(field.permitted_attribute,
|
||||||
|
options_for_select(field.associated_resource_options, field.selected_option),
|
||||||
|
include_blank: field.include_blank_option) %>
|
||||||
|
</div>
|
32
app/views/fields/belongs_to/_index.html.erb
Normal file
32
app/views/fields/belongs_to/_index.html.erb
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
<%#
|
||||||
|
# BelongsTo Index Partial
|
||||||
|
|
||||||
|
This partial renders a belongs_to relationship,
|
||||||
|
to be displayed on a resource's index page.
|
||||||
|
|
||||||
|
By default, the relationship is rendered as a link to the associated object.
|
||||||
|
|
||||||
|
## Local variables:
|
||||||
|
|
||||||
|
- `field`:
|
||||||
|
An instance of [Administrate::Field::BelongsTo][1].
|
||||||
|
A wrapper around the belongs_to relationship pulled from the database.
|
||||||
|
|
||||||
|
[1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/BelongsTo
|
||||||
|
%>
|
||||||
|
|
||||||
|
<% if field.data %>
|
||||||
|
<% if field.data.is_a? User %>
|
||||||
|
<%= link_to(
|
||||||
|
field.display_associated_resource,
|
||||||
|
super_admin_user_path(field.data),
|
||||||
|
) %>
|
||||||
|
<% elsif valid_action?(:show, field.associated_class) %>
|
||||||
|
<%= link_to(
|
||||||
|
field.display_associated_resource,
|
||||||
|
[namespace, field.data],
|
||||||
|
) %>
|
||||||
|
<% else %>
|
||||||
|
<%= field.display_associated_resource %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
27
app/views/fields/belongs_to/_show.html.erb
Normal file
27
app/views/fields/belongs_to/_show.html.erb
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<%#
|
||||||
|
# BelongsTo Show Partial
|
||||||
|
|
||||||
|
This partial renders a belongs_to relationship,
|
||||||
|
to be displayed on a resource's show page.
|
||||||
|
|
||||||
|
By default, the relationship is rendered as a link to the associated object.
|
||||||
|
|
||||||
|
## Local variables:
|
||||||
|
|
||||||
|
- `field`:
|
||||||
|
An instance of [Administrate::Field::BelongsTo][1].
|
||||||
|
A wrapper around the belongs_to relationship pulled from the database.
|
||||||
|
|
||||||
|
[1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/BelongsTo
|
||||||
|
%>
|
||||||
|
|
||||||
|
<% if field.data %>
|
||||||
|
<% if valid_action?(:show, field.associated_class) %>
|
||||||
|
<%= link_to(
|
||||||
|
field.display_associated_resource,
|
||||||
|
[namespace, field.data],
|
||||||
|
) %>
|
||||||
|
<% else %>
|
||||||
|
<%= field.display_associated_resource %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
29
app/views/fields/polymorphic/_form.html.erb
Normal file
29
app/views/fields/polymorphic/_form.html.erb
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<%#
|
||||||
|
# Polymorphic Form Partial
|
||||||
|
|
||||||
|
This partial renders an input element for polymorphic relationships.
|
||||||
|
|
||||||
|
## Local variables:
|
||||||
|
|
||||||
|
- `f`:
|
||||||
|
A Rails form generator, used to help create the appropriate input fields.
|
||||||
|
- `field`:
|
||||||
|
An instance of [Administrate::Field::Polymorphic][1].
|
||||||
|
A wrapper around the polymorphic belongs_to relationship
|
||||||
|
pulled from the database.
|
||||||
|
|
||||||
|
[1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Polymorphic
|
||||||
|
%>
|
||||||
|
|
||||||
|
<%= f.fields_for field.attribute do |pf| %>
|
||||||
|
<div class="field-unit__label">
|
||||||
|
<%= pf.label :value, field.name.humanize %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field-unit__field">
|
||||||
|
<%= pf.hidden_field(:type, value: field.class.name) %>
|
||||||
|
<%= pf.select(:value) do %>
|
||||||
|
<%= grouped_options_for_select(field.associated_resource_grouped_options, field.selected_global_id, prompt: true) %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
31
app/views/fields/polymorphic/_index.html.erb
Normal file
31
app/views/fields/polymorphic/_index.html.erb
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<%#
|
||||||
|
# Polymorphic Index Partial
|
||||||
|
|
||||||
|
This partial renders a polymorphic relationship,
|
||||||
|
to be displayed on a resource's index page.
|
||||||
|
|
||||||
|
By default, the relationship is rendered as a link to the associated object.
|
||||||
|
|
||||||
|
## Local variables:
|
||||||
|
|
||||||
|
- `field`:
|
||||||
|
An instance of [Administrate::Field::Polymorphic][1].
|
||||||
|
A wrapper around the polymorphic belongs_to relationship
|
||||||
|
pulled from the database.
|
||||||
|
|
||||||
|
[1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Polymorphic
|
||||||
|
%>
|
||||||
|
|
||||||
|
<% if field.data %>
|
||||||
|
<% if field.data.is_a? User %>
|
||||||
|
<%= link_to(
|
||||||
|
"User ##{field.data.id}",
|
||||||
|
super_admin_user_path(field.data)
|
||||||
|
) %>
|
||||||
|
<% else %>
|
||||||
|
<%= link_to(
|
||||||
|
field.display_associated_resource,
|
||||||
|
[namespace, field.data]
|
||||||
|
) %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
28
app/views/fields/polymorphic/_show.html.erb
Normal file
28
app/views/fields/polymorphic/_show.html.erb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<%#
|
||||||
|
# Polymorphic Show Partial
|
||||||
|
|
||||||
|
This partial renders a polymorphic relationship,
|
||||||
|
to be displayed on a resource's show page.
|
||||||
|
|
||||||
|
By default, the relationship is rendered as a link to the associated object.
|
||||||
|
|
||||||
|
## Local variables:
|
||||||
|
|
||||||
|
- `field`:
|
||||||
|
An instance of [Administrate::Field::Polymorphic][1].
|
||||||
|
A wrapper around the polymorphic belongs_to relationship
|
||||||
|
pulled from the database.
|
||||||
|
|
||||||
|
[1]: http://www.rubydoc.info/gems/administrate/Administrate/Field/Polymorphic
|
||||||
|
%>
|
||||||
|
|
||||||
|
<% if field.data %>
|
||||||
|
<% if valid_action?(:show, field.data.class) %>
|
||||||
|
<%= link_to(
|
||||||
|
field.display_associated_resource,
|
||||||
|
[namespace, field.data],
|
||||||
|
) %>
|
||||||
|
<% else %>
|
||||||
|
<%= field.display_associated_resource %>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
|
@ -56,9 +56,14 @@ as defined by the routes in the `admin/` namespace
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="logout">
|
<ul class="logout">
|
||||||
|
|
||||||
<li class="navigation__link">
|
<li class="navigation__link">
|
||||||
<i class="ion ion-log-out"></i>
|
<i class="ion ion-log-out"></i>
|
||||||
<%= link_to "Logout", super_admin_logout_url %>
|
<%= link_to "Logout", super_admin_logout_url %>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="navigation__link">
|
||||||
|
<i class="ion ion-android-contacts"></i>
|
||||||
|
<%= link_to "Agent Dashboard", '/' %>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -57,13 +57,13 @@ to display a collection of resources in an HTML table.
|
||||||
<tr class="js-table-row"
|
<tr class="js-table-row"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
<% if valid_action? :show, collection_presenter.resource_name %>
|
<% if valid_action? :show, collection_presenter.resource_name %>
|
||||||
<%= %(role=link data-url=#{polymorphic_path([namespace, resource])}) %>
|
<%= %(role=link data-url=#{polymorphic_path([namespace, resource.becomes(User)])}) %>
|
||||||
<% end %>
|
<% end %>
|
||||||
>
|
>
|
||||||
<% collection_presenter.attributes_for(resource).each do |attribute| %>
|
<% collection_presenter.attributes_for(resource).each do |attribute| %>
|
||||||
<td class="cell-data cell-data--<%= attribute.html_class %>">
|
<td class="cell-data cell-data--<%= attribute.html_class %>">
|
||||||
<% if show_action? :show, resource -%>
|
<% if show_action? :show, resource -%>
|
||||||
<a href="<%= polymorphic_path([namespace, resource]) -%>"
|
<a href="<%= polymorphic_path([namespace, resource.becomes(User)]) -%>"
|
||||||
class="action-show"
|
class="action-show"
|
||||||
>
|
>
|
||||||
<%= render_field attribute %>
|
<%= render_field attribute %>
|
||||||
|
@ -75,7 +75,7 @@ to display a collection of resources in an HTML table.
|
||||||
<% if valid_action? :edit, collection_presenter.resource_name %>
|
<% if valid_action? :edit, collection_presenter.resource_name %>
|
||||||
<td><%= link_to(
|
<td><%= link_to(
|
||||||
t("administrate.actions.edit"),
|
t("administrate.actions.edit"),
|
||||||
[:edit, namespace, resource],
|
[:edit, namespace, resource.becomes(User)],
|
||||||
class: "action-edit",
|
class: "action-edit",
|
||||||
) if show_action? :edit, resource%></td>
|
) if show_action? :edit, resource%></td>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
@ -83,7 +83,7 @@ to display a collection of resources in an HTML table.
|
||||||
<% if valid_action? :destroy, collection_presenter.resource_name %>
|
<% if valid_action? :destroy, collection_presenter.resource_name %>
|
||||||
<td><%= link_to(
|
<td><%= link_to(
|
||||||
t("administrate.actions.destroy"),
|
t("administrate.actions.destroy"),
|
||||||
[namespace, resource],
|
[namespace, resource.becomes(User)],
|
||||||
class: "text-color-red",
|
class: "text-color-red",
|
||||||
method: :delete,
|
method: :delete,
|
||||||
data: { confirm: t("administrate.actions.confirm") }
|
data: { confirm: t("administrate.actions.confirm") }
|
||||||
|
|
|
@ -72,6 +72,15 @@ class Rack::Attack
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
throttle('super_admin_login/email', limit: 20, period: 5.minutes) do |req|
|
||||||
|
if req.path == '/super_admin/sign_in' && req.post?
|
||||||
|
# NOTE: This line used to throw ArgumentError /rails/action_mailbox/sendgrid/inbound_emails : invalid byte sequence in UTF-8
|
||||||
|
# Hence placed in the if block
|
||||||
|
email = req.params['email'].presence || ActionDispatch::Request.new(req.env).params['email'].presence
|
||||||
|
email.to_s.downcase.gsub(/\s+/, '')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
throttle('reset_password/email', limit: 5, period: 1.hour) do |req|
|
throttle('reset_password/email', limit: 5, period: 1.hour) do |req|
|
||||||
if req.path == '/auth/password' && req.post?
|
if req.path == '/auth/password' && req.post?
|
||||||
email = req.params['email'].presence || ActionDispatch::Request.new(req.env).params['email'].presence
|
email = req.params['email'].presence || ActionDispatch::Request.new(req.env).params['email'].presence
|
||||||
|
|
|
@ -303,7 +303,6 @@ Rails.application.routes.draw do
|
||||||
# order of resources affect the order of sidebar navigation in super admin
|
# order of resources affect the order of sidebar navigation in super admin
|
||||||
resources :accounts
|
resources :accounts
|
||||||
resources :users, only: [:index, :new, :create, :show, :edit, :update]
|
resources :users, only: [:index, :new, :create, :show, :edit, :update]
|
||||||
resources :super_admins
|
|
||||||
resources :access_tokens, only: [:index, :show]
|
resources :access_tokens, only: [:index, :show]
|
||||||
resources :installation_configs, only: [:index, :new, :create, :show, :edit, :update]
|
resources :installation_configs, only: [:index, :new, :create, :show, :edit, :update]
|
||||||
|
|
||||||
|
|
42
db/migrate/20220121055444_add_type_to_users.rb
Normal file
42
db/migrate/20220121055444_add_type_to_users.rb
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
class AddTypeToUsers < ActiveRecord::Migration[6.1]
|
||||||
|
def change
|
||||||
|
add_column :users, :type, :string
|
||||||
|
migrate_existing_super_admins
|
||||||
|
|
||||||
|
drop_table :super_admins do |t|
|
||||||
|
t.string :email, null: false, default: ''
|
||||||
|
t.string :encrypted_password, null: false, default: ''
|
||||||
|
t.datetime :remember_created_at
|
||||||
|
t.integer :sign_in_count, default: 0, null: false
|
||||||
|
t.datetime :current_sign_in_at
|
||||||
|
t.datetime :last_sign_in_at
|
||||||
|
t.inet :current_sign_in_ip
|
||||||
|
t.inet :last_sign_in_ip
|
||||||
|
t.timestamps null: false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def old_super_admins
|
||||||
|
ActiveRecord::Base.connection.execute('SELECT * from super_admins').to_a
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_user_account_for_super_admin(super_admin)
|
||||||
|
u = User.new(email: super_admin['email'], name: "SuperUser #{super_admin['id']}", encrypted_password: super_admin['encrypted_password'],
|
||||||
|
confirmed_at: DateTime.now, type: 'SuperAdmin')
|
||||||
|
u.save(validate: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def migrate_existing_super_admins
|
||||||
|
old_super_admins.each do |super_admin|
|
||||||
|
user = User.find_by(email: super_admin['email'])
|
||||||
|
if user.present?
|
||||||
|
user.update(type: 'SuperAdmin')
|
||||||
|
else
|
||||||
|
Rails.logger.debug { "User with email #{super_admin['email']} not found" }
|
||||||
|
create_user_account_for_super_admin(super_admin)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 2022_01_11_223630) do
|
ActiveRecord::Schema.define(version: 2022_01_21_055444) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "pg_stat_statements"
|
enable_extension "pg_stat_statements"
|
||||||
|
@ -745,6 +745,7 @@ ActiveRecord::Schema.define(version: 2022_01_11_223630) do
|
||||||
t.integer "availability", default: 0
|
t.integer "availability", default: 0
|
||||||
t.jsonb "ui_settings", default: {}
|
t.jsonb "ui_settings", default: {}
|
||||||
t.jsonb "custom_attributes", default: {}
|
t.jsonb "custom_attributes", default: {}
|
||||||
|
t.string "type"
|
||||||
t.index ["email"], name: "index_users_on_email"
|
t.index ["email"], name: "index_users_on_email"
|
||||||
t.index ["pubsub_token"], name: "index_users_on_pubsub_token", unique: true
|
t.index ["pubsub_token"], name: "index_users_on_pubsub_token", unique: true
|
||||||
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
|
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
|
||||||
|
|
|
@ -10,7 +10,6 @@ end
|
||||||
|
|
||||||
## Seeds for Local Development
|
## Seeds for Local Development
|
||||||
unless Rails.env.production?
|
unless Rails.env.production?
|
||||||
SuperAdmin.create!(email: 'john@acme.inc', password: 'Password1!')
|
|
||||||
|
|
||||||
account = Account.create!(
|
account = Account.create!(
|
||||||
name: 'Acme Inc'
|
name: 'Acme Inc'
|
||||||
|
@ -20,7 +19,7 @@ unless Rails.env.production?
|
||||||
name: 'Acme Org'
|
name: 'Acme Org'
|
||||||
)
|
)
|
||||||
|
|
||||||
user = User.new(name: 'John', email: 'john@acme.inc', password: 'Password1!')
|
user = User.new(name: 'John', email: 'john@acme.inc', password: 'Password1!', type: 'SuperAdmin')
|
||||||
user.skip_confirmation!
|
user.skip_confirmation!
|
||||||
user.save!
|
user.save!
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ RSpec.describe 'Super Admin access tokens API', type: :request do
|
||||||
|
|
||||||
context 'when it is an authenticated super admin' do
|
context 'when it is an authenticated super admin' do
|
||||||
it 'shows the list of access tokens' do
|
it 'shows the list of access tokens' do
|
||||||
sign_in super_admin
|
sign_in(super_admin, scope: :super_admin)
|
||||||
get '/super_admin/access_tokens'
|
get '/super_admin/access_tokens'
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
expect(response.body).to include(platform_app.access_token.token)
|
expect(response.body).to include(platform_app.access_token.token)
|
||||||
|
|
|
@ -13,7 +13,7 @@ RSpec.describe 'Super Admin Account Users API', type: :request do
|
||||||
|
|
||||||
context 'when it is an authenticated super admin' do
|
context 'when it is an authenticated super admin' do
|
||||||
it 'shows the account user create page' do
|
it 'shows the account user create page' do
|
||||||
sign_in super_admin
|
sign_in(super_admin, scope: :super_admin)
|
||||||
get '/super_admin/account_users/new'
|
get '/super_admin/account_users/new'
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,7 +15,7 @@ RSpec.describe 'Super Admin accounts API', type: :request do
|
||||||
let!(:account) { create(:account) }
|
let!(:account) { create(:account) }
|
||||||
|
|
||||||
it 'shows the list of accounts' do
|
it 'shows the list of accounts' do
|
||||||
sign_in super_admin
|
sign_in(super_admin, scope: :super_admin)
|
||||||
get '/super_admin/accounts'
|
get '/super_admin/accounts'
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
expect(response.body).to include('New account')
|
expect(response.body).to include('New account')
|
||||||
|
|
|
@ -15,7 +15,7 @@ RSpec.describe 'Super Admin agent-bots API', type: :request do
|
||||||
let!(:agent_bot) { create(:agent_bot) }
|
let!(:agent_bot) { create(:agent_bot) }
|
||||||
|
|
||||||
it 'shows the list of users' do
|
it 'shows the list of users' do
|
||||||
sign_in super_admin
|
sign_in(super_admin, scope: :super_admin)
|
||||||
get '/super_admin/agent_bots'
|
get '/super_admin/agent_bots'
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
expect(response.body).to include(agent_bot.name)
|
expect(response.body).to include(agent_bot.name)
|
||||||
|
|
|
@ -15,7 +15,7 @@ RSpec.describe 'Super Admin Application Config API', type: :request do
|
||||||
let!(:config) { create(:installation_config, { name: 'FB_APP_ID', value: 'TESTVALUE' }) }
|
let!(:config) { create(:installation_config, { name: 'FB_APP_ID', value: 'TESTVALUE' }) }
|
||||||
|
|
||||||
it 'shows the app_config page' do
|
it 'shows the app_config page' do
|
||||||
sign_in super_admin
|
sign_in(super_admin, scope: :super_admin)
|
||||||
get '/super_admin/app_config'
|
get '/super_admin/app_config'
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
expect(response.body).to include(config.name)
|
expect(response.body).to include(config.name)
|
||||||
|
@ -33,7 +33,7 @@ RSpec.describe 'Super Admin Application Config API', type: :request do
|
||||||
|
|
||||||
context 'when it is an aunthenticated super admin' do
|
context 'when it is an aunthenticated super admin' do
|
||||||
it 'shows the app_config page' do
|
it 'shows the app_config page' do
|
||||||
sign_in super_admin
|
sign_in(super_admin, scope: :super_admin)
|
||||||
post '/super_admin/app_config', params: { app_config: { TESTKEY: 'TESTVALUE' } }
|
post '/super_admin/app_config', params: { app_config: { TESTKEY: 'TESTVALUE' } }
|
||||||
|
|
||||||
expect(response.status).to eq(302)
|
expect(response.status).to eq(302)
|
||||||
|
|
|
@ -19,20 +19,20 @@ RSpec.describe 'Super Admin Installation Config API', type: :request do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'shows the installation_configs create page' do
|
it 'shows the installation_configs create page' do
|
||||||
sign_in super_admin
|
sign_in(super_admin, scope: :super_admin)
|
||||||
get '/super_admin/installation_configs/new'
|
get '/super_admin/installation_configs/new'
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'shows the installation_configs edit page' do
|
it 'shows the installation_configs edit page' do
|
||||||
sign_in super_admin
|
sign_in(super_admin, scope: :super_admin)
|
||||||
editable_config = InstallationConfig.editable.first
|
editable_config = InstallationConfig.editable.first
|
||||||
get "/super_admin/installation_configs/#{editable_config.id}/edit"
|
get "/super_admin/installation_configs/#{editable_config.id}/edit"
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'shows the installation_configs list page' do
|
it 'shows the installation_configs list page' do
|
||||||
sign_in super_admin
|
sign_in(super_admin, scope: :super_admin)
|
||||||
get '/super_admin/installation_configs'
|
get '/super_admin/installation_configs'
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
expect(response.body).to include(config.name)
|
expect(response.body).to include(config.name)
|
||||||
|
|
|
@ -15,7 +15,7 @@ RSpec.describe 'Super Admin platform app API', type: :request do
|
||||||
let!(:platform_app) { create(:platform_app) }
|
let!(:platform_app) { create(:platform_app) }
|
||||||
|
|
||||||
it 'shows the list of users' do
|
it 'shows the list of users' do
|
||||||
sign_in super_admin
|
sign_in(super_admin, scope: :super_admin)
|
||||||
get '/super_admin/platform_apps'
|
get '/super_admin/platform_apps'
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
expect(response.body).to include(platform_app.name)
|
expect(response.body).to include(platform_app.name)
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe 'Super Admin super admins API', type: :request do
|
|
||||||
let(:super_admin) { create(:super_admin) }
|
|
||||||
|
|
||||||
describe 'GET /super_admin/users' do
|
|
||||||
context 'when it is an unauthenticated super admin' do
|
|
||||||
it 'returns unauthorized' do
|
|
||||||
get '/super_admin/super_admins'
|
|
||||||
expect(response).to have_http_status(:redirect)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'when it is an authenticated super admin' do
|
|
||||||
it 'shows the list of super admins' do
|
|
||||||
sign_in super_admin
|
|
||||||
get '/super_admin/super_admins'
|
|
||||||
expect(response).to have_http_status(:success)
|
|
||||||
expect(response.body).to include('New super admin')
|
|
||||||
expect(response.body).to include(super_admin.email)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -15,7 +15,7 @@ RSpec.describe 'Super Admin Users API', type: :request do
|
||||||
let!(:user) { create(:user) }
|
let!(:user) { create(:user) }
|
||||||
|
|
||||||
it 'shows the list of users' do
|
it 'shows the list of users' do
|
||||||
sign_in super_admin
|
sign_in(super_admin, scope: :super_admin)
|
||||||
get '/super_admin/users'
|
get '/super_admin/users'
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
expect(response.body).to include('New user')
|
expect(response.body).to include('New user')
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe 'Super Admin', type: :request do
|
|
||||||
let(:super_admin) { create(:super_admin) }
|
|
||||||
|
|
||||||
describe 'request to /super_admin' do
|
|
||||||
context 'when the super admin is unauthenticated' do
|
|
||||||
it 'redirects to signin page' do
|
|
||||||
get '/super_admin/'
|
|
||||||
expect(response).to have_http_status(:redirect)
|
|
||||||
expect(response.body).to include('sign_in')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'signs super admin in and out' do
|
|
||||||
sign_in super_admin
|
|
||||||
get '/super_admin'
|
|
||||||
expect(response).to have_http_status(:success)
|
|
||||||
expect(response.body).to include('Dashboard')
|
|
||||||
|
|
||||||
sign_out super_admin
|
|
||||||
get '/super_admin'
|
|
||||||
expect(response).to have_http_status(:redirect)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'request to /super_admin/sidekiq' do
|
|
||||||
context 'when the super admin is unauthenticated' do
|
|
||||||
it 'redirects to signin page' do
|
|
||||||
get '/monitoring/sidekiq'
|
|
||||||
expect(response).to have_http_status(:not_found)
|
|
||||||
expect(response.body).to include('sign_in')
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'signs super admin in and out' do
|
|
||||||
sign_in super_admin
|
|
||||||
get '/monitoring/sidekiq'
|
|
||||||
expect(response).to have_http_status(:success)
|
|
||||||
|
|
||||||
sign_out super_admin
|
|
||||||
get '/monitoring/sidekiq'
|
|
||||||
expect(response).to have_http_status(:not_found)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,6 +1,9 @@
|
||||||
FactoryBot.define do
|
FactoryBot.define do
|
||||||
factory :super_admin do
|
factory :super_admin do
|
||||||
|
name { Faker::Name.name }
|
||||||
email { "admin@#{SecureRandom.uuid}.com" }
|
email { "admin@#{SecureRandom.uuid}.com" }
|
||||||
password { 'Password1!' }
|
password { 'Password1!' }
|
||||||
|
type { 'SuperAdmin' }
|
||||||
|
confirmed_at { Time.zone.now }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue