Feature: Introduce Super Admins (#705)
* Feature: Introduce Super Admins - added new devise model for super user - added administrate gem - sample dashboards for users and accounts Co-authored-by: Pranav Raj Sreepuram <pranavrajs@gmail.com>
This commit is contained in:
parent
8859880e55
commit
c74b5c21d7
37 changed files with 964 additions and 35 deletions
|
@ -73,10 +73,6 @@ RAILS_LOG_TO_STDOUT=true
|
|||
LOG_LEVEL=info
|
||||
LOG_SIZE=500
|
||||
|
||||
# Credentials to access sidekiq dashboard in production
|
||||
SIDEKIQ_AUTH_USERNAME=
|
||||
SIDEKIQ_AUTH_PASSWORD=
|
||||
|
||||
### This environment variables are only required if you are setting up social media channels
|
||||
#facebook
|
||||
FB_VERIFY_TOKEN=
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -49,6 +49,8 @@ gem 'devise_token_auth'
|
|||
# authorization
|
||||
gem 'jwt'
|
||||
gem 'pundit'
|
||||
# super admin
|
||||
gem 'administrate'
|
||||
|
||||
##--- gems for pubsub service ---##
|
||||
# https://karolgalanciak.com/blog/2019/11/30/from-activerecord-callbacks-to-publish-slash-subscribe-pattern-and-event-driven-design/
|
||||
|
|
32
Gemfile.lock
32
Gemfile.lock
|
@ -84,11 +84,24 @@ GEM
|
|||
activerecord (>= 5.0, < 6.1)
|
||||
addressable (2.7.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
administrate (0.13.0)
|
||||
actionpack (>= 4.2)
|
||||
actionview (>= 4.2)
|
||||
activerecord (>= 4.2)
|
||||
autoprefixer-rails (>= 6.0)
|
||||
datetime_picker_rails (~> 0.0.7)
|
||||
jquery-rails (>= 4.0)
|
||||
kaminari (>= 1.0)
|
||||
momentjs-rails (~> 2.8)
|
||||
sassc-rails (~> 2.1)
|
||||
selectize-rails (~> 0.6)
|
||||
annotate (3.1.1)
|
||||
activerecord (>= 3.2, < 7.0)
|
||||
rake (>= 10.4, < 14.0)
|
||||
ast (2.4.0)
|
||||
attr_extras (6.2.3)
|
||||
autoprefixer-rails (9.7.6)
|
||||
execjs
|
||||
aws-eventstream (1.1.0)
|
||||
aws-partitions (1.310.0)
|
||||
aws-sdk-core (3.94.1)
|
||||
|
@ -141,6 +154,8 @@ GEM
|
|||
concurrent-ruby (1.1.6)
|
||||
connection_pool (2.2.2)
|
||||
crass (1.0.6)
|
||||
datetime_picker_rails (0.0.7)
|
||||
momentjs-rails (>= 2.8.1)
|
||||
declarative (0.0.10)
|
||||
declarative-option (0.1.0)
|
||||
descendants_tracker (0.0.4)
|
||||
|
@ -235,6 +250,10 @@ GEM
|
|||
jbuilder (2.10.0)
|
||||
activesupport (>= 5.0.0)
|
||||
jmespath (1.4.0)
|
||||
jquery-rails (4.3.5)
|
||||
rails-dom-testing (>= 1, < 3)
|
||||
railties (>= 4.2.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
json (2.3.0)
|
||||
json_pure (2.3.0)
|
||||
jwt (2.2.1)
|
||||
|
@ -278,6 +297,8 @@ GEM
|
|||
mini_mime (1.0.2)
|
||||
mini_portile2 (2.4.0)
|
||||
minitest (5.14.0)
|
||||
momentjs-rails (2.20.1)
|
||||
railties (>= 3.1)
|
||||
msgpack (1.3.3)
|
||||
multi_json (1.14.1)
|
||||
multi_xml (0.6.0)
|
||||
|
@ -406,6 +427,14 @@ GEM
|
|||
sass-listen (4.0.0)
|
||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||
rb-inotify (~> 0.9, >= 0.9.7)
|
||||
sassc (2.3.0)
|
||||
ffi (~> 1.9)
|
||||
sassc-rails (2.1.2)
|
||||
railties (>= 4.0.0)
|
||||
sassc (>= 2.0)
|
||||
sprockets (> 3.0)
|
||||
sprockets-rails
|
||||
tilt
|
||||
scout_apm (2.6.7)
|
||||
parser
|
||||
scss_lint (0.59.0)
|
||||
|
@ -413,6 +442,7 @@ GEM
|
|||
seed_dump (3.3.1)
|
||||
activerecord (>= 4)
|
||||
activesupport (>= 4)
|
||||
selectize-rails (0.12.6)
|
||||
semantic_range (2.3.0)
|
||||
sentry-raven (3.0.0)
|
||||
faraday (>= 1.0)
|
||||
|
@ -451,6 +481,7 @@ GEM
|
|||
telephone_number (1.4.6)
|
||||
thor (0.20.3)
|
||||
thread_safe (0.3.6)
|
||||
tilt (2.0.10)
|
||||
time_diff (0.3.0)
|
||||
activesupport
|
||||
i18n
|
||||
|
@ -505,6 +536,7 @@ PLATFORMS
|
|||
DEPENDENCIES
|
||||
action-cable-testing
|
||||
acts-as-taggable-on
|
||||
administrate
|
||||
annotate
|
||||
attr_extras
|
||||
aws-sdk-s3
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
//= link_tree ../images
|
||||
//= link administrate/application.css
|
||||
//= link administrate/application.js
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
class AccountBuilder
|
||||
include CustomExceptions::Account
|
||||
pattr_initialize [:account_name!, :email!]
|
||||
pattr_initialize [:account_name!, :email!, :confirmed!]
|
||||
|
||||
def perform
|
||||
validate_email
|
||||
|
@ -46,6 +46,7 @@ class AccountBuilder
|
|||
password: password,
|
||||
password_confirmation: password,
|
||||
name: email_to_name(@email))
|
||||
@user.confirm if @confirmed
|
||||
if @user.save!
|
||||
link_user_to_account(@user, @account)
|
||||
@user
|
||||
|
|
|
@ -16,7 +16,8 @@ class Api::V1::Accounts::AccountsController < Api::BaseController
|
|||
def create
|
||||
@user = AccountBuilder.new(
|
||||
account_name: account_params[:account_name],
|
||||
email: account_params[:email]
|
||||
email: account_params[:email],
|
||||
confirmed: confirmed?
|
||||
).perform
|
||||
if @user
|
||||
send_auth_headers(@user)
|
||||
|
@ -40,6 +41,10 @@ class Api::V1::Accounts::AccountsController < Api::BaseController
|
|||
authorize(Account)
|
||||
end
|
||||
|
||||
def confirmed?
|
||||
super_admin? && params[:confirmed]
|
||||
end
|
||||
|
||||
def fetch_account
|
||||
@account = current_user.accounts.find(params[:id])
|
||||
end
|
||||
|
|
|
@ -4,17 +4,25 @@ module AccessTokenAuthHelper
|
|||
'api/v1/accounts/conversations/messages' => ['create']
|
||||
}.freeze
|
||||
|
||||
def authenticate_access_token!
|
||||
def ensure_access_token
|
||||
token = request.headers[:api_access_token] || request.headers[:HTTP_API_ACCESS_TOKEN]
|
||||
access_token = AccessToken.find_by(token: token)
|
||||
render_unauthorized('Invalid Access Token') && return unless access_token
|
||||
@access_token = AccessToken.find_by(token: token) if token.present?
|
||||
end
|
||||
|
||||
token_owner = access_token.owner
|
||||
@resource = token_owner
|
||||
def authenticate_access_token!
|
||||
ensure_access_token
|
||||
render_unauthorized('Invalid Access Token') && return if @access_token.blank?
|
||||
|
||||
@resource = @access_token.owner
|
||||
end
|
||||
|
||||
def super_admin?
|
||||
@resource.present? && @resource.is_a?(SuperAdmin)
|
||||
end
|
||||
|
||||
def validate_bot_access_token!
|
||||
return if current_user.is_a?(User)
|
||||
return if super_admin?
|
||||
return if agent_bot_accessible?
|
||||
|
||||
render_unauthorized('Access to this endpoint is not authorized for bots')
|
||||
|
|
44
app/controllers/super_admin/access_tokens_controller.rb
Normal file
44
app/controllers/super_admin/access_tokens_controller.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
class SuperAdmin::AccessTokensController < 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
|
44
app/controllers/super_admin/accounts_controller.rb
Normal file
44
app/controllers/super_admin/accounts_controller.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
class SuperAdmin::AccountsController < 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
|
16
app/controllers/super_admin/application_controller.rb
Normal file
16
app/controllers/super_admin/application_controller.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
# All Administrate controllers inherit from this
|
||||
# `Administrate::ApplicationController`, making it the ideal place to put
|
||||
# authentication logic or other before_actions.
|
||||
#
|
||||
# If you want to add pagination or other controller-level concerns,
|
||||
# you're free to overwrite the RESTful controller actions.
|
||||
class SuperAdmin::ApplicationController < Administrate::ApplicationController
|
||||
# authenticiation done via devise : SuperAdmin Model
|
||||
before_action :authenticate_super_admin!
|
||||
|
||||
# Override this value to specify the number of elements to display at a time
|
||||
# on index pages. Defaults to 20.
|
||||
# def records_per_page
|
||||
# params[:per_page] || 20
|
||||
# end
|
||||
end
|
28
app/controllers/super_admin/devise/sessions_controller.rb
Normal file
28
app/controllers/super_admin/devise/sessions_controller.rb
Normal file
|
@ -0,0 +1,28 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class SuperAdmin::Devise::SessionsController < Devise::SessionsController
|
||||
def new
|
||||
self.resource = resource_class.new(sign_in_params)
|
||||
end
|
||||
|
||||
def create
|
||||
return unless valid_credentials?
|
||||
|
||||
sign_in(@super_admin, scope: :super_admin)
|
||||
flash.discard
|
||||
redirect_to super_admin_users_path
|
||||
end
|
||||
|
||||
def destroy
|
||||
sign_out
|
||||
flash.discard
|
||||
redirect_to '/'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def valid_credentials?
|
||||
@super_admin = SuperAdmin.find_by!(email: params[:super_admin][:email])
|
||||
@super_admin.valid_password?(params[:super_admin][:password])
|
||||
end
|
||||
end
|
44
app/controllers/super_admin/super_admins_controller.rb
Normal file
44
app/controllers/super_admin/super_admins_controller.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
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
|
44
app/controllers/super_admin/users_controller.rb
Normal file
44
app/controllers/super_admin/users_controller.rb
Normal file
|
@ -0,0 +1,44 @@
|
|||
class SuperAdmin::UsersController < 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
|
66
app/dashboards/access_token_dashboard.rb
Normal file
66
app/dashboards/access_token_dashboard.rb
Normal file
|
@ -0,0 +1,66 @@
|
|||
require 'administrate/base_dashboard'
|
||||
|
||||
class AccessTokenDashboard < 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 = {
|
||||
owner: Field::Polymorphic,
|
||||
id: Field::Number,
|
||||
token: Field::String,
|
||||
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[
|
||||
owner
|
||||
id
|
||||
token
|
||||
created_at
|
||||
].freeze
|
||||
|
||||
# SHOW_PAGE_ATTRIBUTES
|
||||
# an array of attributes that will be displayed on the model's show page.
|
||||
SHOW_PAGE_ATTRIBUTES = %i[
|
||||
owner
|
||||
id
|
||||
token
|
||||
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[
|
||||
owner
|
||||
token
|
||||
].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 access tokens are displayed
|
||||
# across all pages of the admin dashboard.
|
||||
#
|
||||
# def display_resource(access_token)
|
||||
# "AccessToken ##{access_token.id}"
|
||||
# end
|
||||
end
|
64
app/dashboards/account_dashboard.rb
Normal file
64
app/dashboards/account_dashboard.rb
Normal file
|
@ -0,0 +1,64 @@
|
|||
require 'administrate/base_dashboard'
|
||||
|
||||
class AccountDashboard < 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,
|
||||
name: Field::String,
|
||||
created_at: Field::DateTime,
|
||||
updated_at: Field::DateTime,
|
||||
locale: Field::String.with_options(searchable: false)
|
||||
}.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[
|
||||
name
|
||||
locale
|
||||
].freeze
|
||||
|
||||
# SHOW_PAGE_ATTRIBUTES
|
||||
# an array of attributes that will be displayed on the model's show page.
|
||||
SHOW_PAGE_ATTRIBUTES = %i[
|
||||
id
|
||||
name
|
||||
created_at
|
||||
updated_at
|
||||
locale
|
||||
].freeze
|
||||
|
||||
# FORM_ATTRIBUTES
|
||||
# an array of attributes that will be displayed
|
||||
# on the model's form (`new` and `edit`) pages.
|
||||
FORM_ATTRIBUTES = %i[
|
||||
name
|
||||
locale
|
||||
].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 accounts are displayed
|
||||
# across all pages of the admin dashboard.
|
||||
#
|
||||
# def display_resource(account)
|
||||
# "Account ##{account.id}"
|
||||
# end
|
||||
end
|
81
app/dashboards/super_admin_dashboard.rb
Normal file
81
app/dashboards/super_admin_dashboard.rb
Normal file
|
@ -0,0 +1,81 @@
|
|||
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,
|
||||
access_token: Field::HasOne,
|
||||
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
|
||||
access_token
|
||||
].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
|
||||
remember_created_at
|
||||
sign_in_count
|
||||
current_sign_in_at
|
||||
last_sign_in_at
|
||||
current_sign_in_ip
|
||||
last_sign_in_ip
|
||||
].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
|
88
app/dashboards/user_dashboard.rb
Normal file
88
app/dashboards/user_dashboard.rb
Normal file
|
@ -0,0 +1,88 @@
|
|||
require 'administrate/base_dashboard'
|
||||
|
||||
class UserDashboard < 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 = {
|
||||
account_users: Field::HasMany,
|
||||
accounts: Field::HasMany,
|
||||
invitees: Field::HasMany.with_options(class_name: 'User'),
|
||||
id: Field::Number,
|
||||
provider: Field::String,
|
||||
uid: Field::String,
|
||||
reset_password_token: Field::String,
|
||||
reset_password_sent_at: Field::DateTime,
|
||||
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,
|
||||
last_sign_in_ip: Field::String,
|
||||
confirmation_token: Field::String,
|
||||
confirmed_at: Field::DateTime,
|
||||
confirmation_sent_at: Field::DateTime,
|
||||
unconfirmed_email: Field::String,
|
||||
name: Field::String,
|
||||
nickname: Field::String,
|
||||
email: Field::String,
|
||||
tokens: Field::String.with_options(searchable: false),
|
||||
created_at: Field::DateTime,
|
||||
updated_at: Field::DateTime,
|
||||
pubsub_token: Field::String
|
||||
}.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[
|
||||
name
|
||||
email
|
||||
].freeze
|
||||
|
||||
# SHOW_PAGE_ATTRIBUTES
|
||||
# an array of attributes that will be displayed on the model's show page.
|
||||
SHOW_PAGE_ATTRIBUTES = %i[
|
||||
accounts
|
||||
id
|
||||
unconfirmed_email
|
||||
name
|
||||
nickname
|
||||
email
|
||||
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[
|
||||
name
|
||||
nickname
|
||||
email
|
||||
].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 users are displayed
|
||||
# across all pages of the admin dashboard.
|
||||
#
|
||||
# def display_resource(user)
|
||||
# "User ##{user.id}"
|
||||
# end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
@import '../variables';
|
||||
|
||||
.superadmin-body {
|
||||
background: $color-background;
|
||||
}
|
13
app/javascript/dashboard/assets/scss/super_admin/pages.scss
Normal file
13
app/javascript/dashboard/assets/scss/super_admin/pages.scss
Normal file
|
@ -0,0 +1,13 @@
|
|||
@import 'shared/assets/fonts/inter';
|
||||
@import '../variables';
|
||||
|
||||
body {
|
||||
background-color: $color-background;
|
||||
font-family: Inter;
|
||||
}
|
||||
|
||||
.button {
|
||||
background-color: $color-woot;
|
||||
border-radius: 1px solid $color-woot;
|
||||
color: $color-white;
|
||||
}
|
2
app/javascript/packs/superadmin.js
Normal file
2
app/javascript/packs/superadmin.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
import '../dashboard/assets/scss/app.scss';
|
||||
import '../dashboard/assets/scss/super_admin/index.scss';
|
1
app/javascript/packs/superadmin_pages.js
Normal file
1
app/javascript/packs/superadmin_pages.js
Normal file
|
@ -0,0 +1 @@
|
|||
import '../dashboard/assets/scss/super_admin/pages.scss';
|
27
app/models/super_admin.rb
Normal file
27
app/models/super_admin.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
# == Schema Information
|
||||
#
|
||||
# Table name: super_admins
|
||||
#
|
||||
# id :bigint not null, primary key
|
||||
# current_sign_in_at :datetime
|
||||
# current_sign_in_ip :inet
|
||||
# email :string default(""), not null
|
||||
# encrypted_password :string default(""), not null
|
||||
# last_sign_in_at :datetime
|
||||
# last_sign_in_ip :inet
|
||||
# remember_created_at :datetime
|
||||
# sign_in_count :integer default(0), not null
|
||||
# created_at :datetime not null
|
||||
# updated_at :datetime not null
|
||||
#
|
||||
# Indexes
|
||||
#
|
||||
# index_super_admins_on_email (email) UNIQUE
|
||||
#
|
||||
class SuperAdmin < ApplicationRecord
|
||||
# Include default devise modules. Others available are:
|
||||
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
|
||||
devise :database_authenticatable, :trackable, :rememberable, :validatable
|
||||
|
||||
include AccessTokenable
|
||||
end
|
27
app/views/super_admin/application/_navigation.html.erb
Normal file
27
app/views/super_admin/application/_navigation.html.erb
Normal file
|
@ -0,0 +1,27 @@
|
|||
<%#
|
||||
# Navigation
|
||||
|
||||
This partial is used to display the navigation in Administrate.
|
||||
By default, the navigation contains navigation links
|
||||
for all resources in the admin dashboard,
|
||||
as defined by the routes in the `admin/` namespace
|
||||
%>
|
||||
|
||||
<%= javascript_pack_tag 'superadmin_pages' %>
|
||||
<%= stylesheet_pack_tag 'superadmin_pages' %>
|
||||
|
||||
|
||||
<nav class="navigation" role="navigation">
|
||||
<%= link_to "Back to app", root_url, class: "button button--alt" %>
|
||||
<%= link_to "Logout", super_admin_logout_url , class: "button button--alt" %>
|
||||
|
||||
<% Administrate::Namespace.new(namespace).resources.each do |resource| %>
|
||||
<%= link_to(
|
||||
display_resource_name(resource),
|
||||
[namespace, resource_index_route_key(resource)],
|
||||
class: "navigation__link navigation__link--#{nav_link_state(resource)}"
|
||||
) if valid_action? :index, resource %>
|
||||
<% end %>
|
||||
|
||||
<%= link_to "Sidekiq", sidekiq_web_url , class: "button" %>
|
||||
</nav>
|
43
app/views/super_admin/devise/sessions/new.html.erb
Normal file
43
app/views/super_admin/devise/sessions/new.html.erb
Normal file
|
@ -0,0 +1,43 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>SuperAdmin | Chatwoot</title>
|
||||
<%= javascript_pack_tag 'superadmin' %>
|
||||
<%= stylesheet_pack_tag 'superadmin' %>
|
||||
</head>
|
||||
<body data-gr-c-s-loaded="true">
|
||||
<div id="app" class="superadmin-body app-wrapper app-root">
|
||||
<div class="medium column login">
|
||||
<div class="text-center medium-12 login__hero align-self-top">
|
||||
<h2 class="hero__title">
|
||||
Howdy, admin 👋
|
||||
</h2>
|
||||
</div>
|
||||
<div class="row align-center">
|
||||
<div class="small-12 medium-4 column">
|
||||
<%= form_for(resource, as: resource_name, url: '/super_admin/sign_in', html: { class: 'login-box column align-self-top'}) do |f| %>
|
||||
<div class="column log-in-form">
|
||||
<label>
|
||||
Email
|
||||
<%= f.email_field :email, autofocus: true, autocomplete: "email", placeholder: "Email eg: someone@example.com" %>
|
||||
</label>
|
||||
<label>
|
||||
Password
|
||||
<%= f.password_field :password, autocomplete: "current-password", placeholder: "Password" %>
|
||||
</label>
|
||||
<p>
|
||||
<%= f.check_box :remember_me %> Remember me
|
||||
</p>
|
||||
<button type="submit" class="button nice large expanded">
|
||||
Login
|
||||
</button>
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="column text-center sigin__footer">
|
||||
© Chatwoot
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -6,7 +6,7 @@ Devise.setup do |config|
|
|||
# confirmation, reset password and unlock tokens in the database.
|
||||
# Devise will use the `secret_key_base` as its `secret_key`
|
||||
# by default. You can change it below and use your own secret key.
|
||||
# config.secret_key = 'dff4665a082305d28b485d1d763d0d3e52e2577220eaa551836862a3dbca1aade309fe7ceed35180ac494cbc27bd2f5f84d45e4d19530598d1bd899dcbb115e1'
|
||||
# config.secret_key = 'dff4665a082305d28b485d1d763d0d3e52e2577220eaa551836862a3dbca1aade309fe7ceed35180ac494cbc27bd2f5f84d45e1'
|
||||
|
||||
# ==> Mailer Configuration
|
||||
# Configure the e-mail address which will be shown in Devise::Mailer,
|
||||
|
@ -220,15 +220,15 @@ Devise.setup do |config|
|
|||
# Turn scoped views on. Before rendering "sessions/new", it will first check for
|
||||
# "users/sessions/new". It's turned off by default because it's slower if you
|
||||
# are using only default views.
|
||||
# config.scoped_views = false
|
||||
config.scoped_views = true
|
||||
|
||||
# Configure the default scope given to Warden. By default it's the first
|
||||
# devise role declared in your routes (usually :user).
|
||||
# config.default_scope = :user
|
||||
config.default_scope = :user
|
||||
|
||||
# Set this configuration to false if you want /users/sign_out to sign out
|
||||
# only the current scope. By default, Devise signs out all scopes.
|
||||
# config.sign_out_all_scopes = true
|
||||
config.sign_out_all_scopes = true
|
||||
|
||||
# ==> Navigation configuration
|
||||
# Lists the formats that should be treated as navigational. Formats like
|
||||
|
|
|
@ -168,20 +168,20 @@ Rails.application.routes.draw do
|
|||
# Internal Monitoring Routes
|
||||
require 'sidekiq/web'
|
||||
|
||||
scope :monitoring do
|
||||
# Sidekiq should use basic auth in production environment
|
||||
if Rails.env.production?
|
||||
Sidekiq::Web.use Rack::Auth::Basic do |username, password|
|
||||
ENV['SIDEKIQ_AUTH_USERNAME'] &&
|
||||
ENV['SIDEKIQ_AUTH_PASSWORD'] &&
|
||||
ActiveSupport::SecurityUtils.secure_compare(::Digest::SHA256.hexdigest(username),
|
||||
::Digest::SHA256.hexdigest(ENV['SIDEKIQ_AUTH_USERNAME'])) &&
|
||||
ActiveSupport::SecurityUtils.secure_compare(::Digest::SHA256.hexdigest(password),
|
||||
::Digest::SHA256.hexdigest(ENV['SIDEKIQ_AUTH_PASSWORD']))
|
||||
end
|
||||
end
|
||||
devise_for :super_admins, path: 'super_admin', controllers: { sessions: 'super_admin/devise/sessions' }
|
||||
devise_scope :super_admin do
|
||||
get 'super_admin/logout', to: 'super_admin/devise/sessions#destroy'
|
||||
namespace :super_admin do
|
||||
resources :users
|
||||
resources :accounts
|
||||
resources :super_admins
|
||||
resources :access_tokens
|
||||
|
||||
mount Sidekiq::Web, at: '/sidekiq'
|
||||
root to: 'users#index'
|
||||
end
|
||||
authenticated :super_admin do
|
||||
mount Sidekiq::Web => '/monitoring/sidekiq'
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------
|
||||
|
|
45
db/migrate/20200410145519_devise_create_super_admins.rb
Normal file
45
db/migrate/20200410145519_devise_create_super_admins.rb
Normal file
|
@ -0,0 +1,45 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class DeviseCreateSuperAdmins < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
return if ActiveRecord::Base.connection.table_exists? 'super_admins'
|
||||
|
||||
create_table :super_admins do |t|
|
||||
## Database authenticatable
|
||||
t.string :email, null: false, default: ''
|
||||
t.string :encrypted_password, null: false, default: ''
|
||||
|
||||
## Recoverable
|
||||
# t.string :reset_password_token
|
||||
# t.datetime :reset_password_sent_at
|
||||
|
||||
## Rememberable
|
||||
t.datetime :remember_created_at
|
||||
|
||||
## Trackable
|
||||
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
|
||||
|
||||
## Confirmable
|
||||
# t.string :confirmation_token
|
||||
# t.datetime :confirmed_at
|
||||
# t.datetime :confirmation_sent_at
|
||||
# t.string :unconfirmed_email # Only if using reconfirmable
|
||||
|
||||
## Lockable
|
||||
# t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
|
||||
# t.string :unlock_token # Only if unlock strategy is :email or :both
|
||||
# t.datetime :locked_at
|
||||
|
||||
t.timestamps null: false
|
||||
end
|
||||
|
||||
add_index :super_admins, :email, unique: true
|
||||
# add_index :super_admins, :reset_password_token, unique: true
|
||||
# add_index :super_admins, :confirmation_token, unique: true
|
||||
# add_index :super_admins, :unlock_token, unique: true
|
||||
end
|
||||
end
|
14
db/schema.rb
14
db/schema.rb
|
@ -327,6 +327,20 @@ ActiveRecord::Schema.define(version: 2020_05_10_112339) do
|
|||
t.boolean "payment_source_added", default: false
|
||||
end
|
||||
|
||||
create_table "super_admins", force: :cascade do |t|
|
||||
t.string "email", default: "", null: false
|
||||
t.string "encrypted_password", default: "", null: false
|
||||
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.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.index ["email"], name: "index_super_admins_on_email", unique: true
|
||||
end
|
||||
|
||||
create_table "taggings", id: :serial, force: :cascade do |t|
|
||||
t.integer "tag_id"
|
||||
t.string "taggable_type"
|
||||
|
|
|
@ -2,9 +2,10 @@ require 'rails_helper'
|
|||
|
||||
RSpec.describe 'Accounts API', type: :request do
|
||||
describe 'POST /api/v1/accounts' do
|
||||
let(:email) { Faker::Internet.email }
|
||||
|
||||
context 'when posting to accounts with correct parameters' do
|
||||
let(:account_builder) { double }
|
||||
let(:email) { Faker::Internet.email }
|
||||
let(:account) { create(:account) }
|
||||
let(:user) { create(:user, email: email, account: account) }
|
||||
|
||||
|
@ -22,7 +23,7 @@ RSpec.describe 'Accounts API', type: :request do
|
|||
params: params,
|
||||
as: :json
|
||||
|
||||
expect(AccountBuilder).to have_received(:new).with(params)
|
||||
expect(AccountBuilder).to have_received(:new).with(params.merge(confirmed: false))
|
||||
expect(account_builder).to have_received(:perform)
|
||||
expect(response.headers.keys).to include('access-token', 'token-type', 'client', 'expiry', 'uid')
|
||||
end
|
||||
|
@ -36,16 +37,45 @@ RSpec.describe 'Accounts API', type: :request do
|
|||
params: params,
|
||||
as: :json
|
||||
|
||||
expect(AccountBuilder).to have_received(:new).with(params)
|
||||
expect(AccountBuilder).to have_received(:new).with(params.merge(confirmed: false))
|
||||
expect(account_builder).to have_received(:perform)
|
||||
expect(response).to have_http_status(:forbidden)
|
||||
expect(response.body).to eq({ message: I18n.t('errors.signup.failed') }.to_json)
|
||||
end
|
||||
|
||||
it 'ignores confirmed param when called with out super admin token' do
|
||||
allow(account_builder).to receive(:perform).and_return(nil)
|
||||
|
||||
params = { account_name: 'test', email: email, confirmed: true }
|
||||
|
||||
post api_v1_accounts_url,
|
||||
params: params,
|
||||
as: :json
|
||||
|
||||
expect(AccountBuilder).to have_received(:new).with(params.merge(confirmed: false))
|
||||
expect(account_builder).to have_received(:perform)
|
||||
expect(response).to have_http_status(:forbidden)
|
||||
expect(response.body).to eq({ message: I18n.t('errors.signup.failed') }.to_json)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when ENABLE_ACCOUNT_SIGNUP env variable is set to false' do
|
||||
let(:email) { Faker::Internet.email }
|
||||
context 'when called with super admin token' do
|
||||
let(:super_admin) { create(:super_admin) }
|
||||
|
||||
it 'calls account builder with confirmed true when confirmed param is passed' do
|
||||
params = { account_name: 'test', email: email, confirmed: true }
|
||||
|
||||
post api_v1_accounts_url,
|
||||
params: params,
|
||||
headers: { api_access_token: super_admin.access_token.token },
|
||||
as: :json
|
||||
|
||||
expect(User.find_by(email: email).confirmed?).to eq(true)
|
||||
expect(response.headers.keys).to include('access-token', 'token-type', 'client', 'expiry', 'uid')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when ENABLE_ACCOUNT_SIGNUP env variable is set to false' do
|
||||
it 'responds 404 on requests' do
|
||||
params = { account_name: 'test', email: email }
|
||||
ENV['ENABLE_ACCOUNT_SIGNUP'] = 'false'
|
||||
|
@ -60,8 +90,6 @@ RSpec.describe 'Accounts API', type: :request do
|
|||
end
|
||||
|
||||
context 'when ENABLE_ACCOUNT_SIGNUP env variable is set to api_only' do
|
||||
let(:email) { Faker::Internet.email }
|
||||
|
||||
it 'does not respond 404 on requests' do
|
||||
params = { account_name: 'test', email: email }
|
||||
ENV['ENABLE_ACCOUNT_SIGNUP'] = 'api_only'
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Super Admin access tokens API', type: :request do
|
||||
let(:super_admin) { create(:super_admin) }
|
||||
|
||||
describe 'GET /super_admin/access_tokens' do
|
||||
context 'when it is an unauthenticated super admin' do
|
||||
it 'returns unauthorized' do
|
||||
get '/super_admin/'
|
||||
expect(response).to have_http_status(:redirect)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is an authenticated super admin' do
|
||||
it 'shows the list of access tokens' do
|
||||
sign_in super_admin
|
||||
get '/super_admin/access_tokens'
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response.body).to include('New access token')
|
||||
expect(response.body).to include(super_admin.access_token.token)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
26
spec/controllers/super_admin/accounts_controller_spec.rb
Normal file
26
spec/controllers/super_admin/accounts_controller_spec.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Super Admin accounts API', type: :request do
|
||||
let(:super_admin) { create(:super_admin) }
|
||||
|
||||
describe 'GET /super_admin/accounts' do
|
||||
context 'when it is an unauthenticated user' do
|
||||
it 'returns unauthorized' do
|
||||
get '/super_admin/accounts'
|
||||
expect(response).to have_http_status(:redirect)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is an authenticated user' do
|
||||
let!(:account) { create(:account) }
|
||||
|
||||
it 'shows the list of accounts' do
|
||||
sign_in super_admin
|
||||
get '/super_admin/accounts'
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response.body).to include('New account')
|
||||
expect(response.body).to include(account.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
24
spec/controllers/super_admin/super_admins_controller_spec.rb
Normal file
24
spec/controllers/super_admin/super_admins_controller_spec.rb
Normal file
|
@ -0,0 +1,24 @@
|
|||
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
|
26
spec/controllers/super_admin/users_controller_spec.rb
Normal file
26
spec/controllers/super_admin/users_controller_spec.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Super Admin Users 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/'
|
||||
expect(response).to have_http_status(:redirect)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when it is an authenticated super admin' do
|
||||
let!(:user) { create(:user) }
|
||||
|
||||
it 'shows the list of users' do
|
||||
sign_in super_admin
|
||||
get '/super_admin'
|
||||
expect(response).to have_http_status(:success)
|
||||
expect(response.body).to include('New user')
|
||||
expect(response.body).to include(user.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
46
spec/controllers/super_admin_controller_spec.rb
Normal file
46
spec/controllers/super_admin_controller_spec.rb
Normal file
|
@ -0,0 +1,46 @@
|
|||
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('New user')
|
||||
|
||||
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
|
6
spec/factories/super_admins.rb
Normal file
6
spec/factories/super_admins.rb
Normal file
|
@ -0,0 +1,6 @@
|
|||
FactoryBot.define do
|
||||
factory :super_admin do
|
||||
email { "admin@#{SecureRandom.uuid}.com" }
|
||||
password { 'password' }
|
||||
end
|
||||
end
|
5
spec/models/super_admin_spec.rb
Normal file
5
spec/models/super_admin_spec.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe SuperAdmin, type: :model do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
|
@ -61,6 +61,8 @@ RSpec.configure do |config|
|
|||
config.filter_rails_from_backtrace!
|
||||
# arbitrary gems may also be filtered via:
|
||||
# config.filter_gems_from_backtrace("gem name")
|
||||
|
||||
config.include Devise::Test::IntegrationHelpers, type: :request
|
||||
end
|
||||
|
||||
Shoulda::Matchers.configure do |config|
|
||||
|
|
Loading…
Reference in a new issue