Browse Source

Remove active admin (#611)

* Remove stuff

* Remove ActiveAdmin from Gem file

* Remove routes and table

* Rubocop

* Remove active admin from webpack config

* Remove JQuery from webpack

* Remove remaining spec references

* Remove js packages

* Schema
pull/619/head
baarkerlounger 3 years ago committed by baarkerlounger
parent
commit
fc3ada63f7
  1. 2
      Gemfile
  2. 44
      Gemfile.lock
  3. 37
      app/admin/admin_users.rb
  4. 20
      app/admin/case_logs.rb
  5. 32
      app/admin/dashboard.rb
  6. 31
      app/admin/organisations.rb
  7. 42
      app/admin/users.rb
  8. 0
      app/concerns/.keep
  9. 15
      app/concerns/admin/paper_trail.rb
  10. 6
      app/controllers/auth/passwords_controller.rb
  11. 20
      app/controllers/auth/sessions_controller.rb
  12. 7
      app/frontend/active_admin.js
  13. 17
      app/frontend/styles/active_admin.scss
  14. 36
      app/models/admin_user.rb
  15. 339
      config/initializers/active_admin.rb
  16. 25
      config/routes.rb
  17. 36
      db/migrate/20220525130518_drop_admin_users.rb
  18. 31
      db/schema.rb
  19. 1
      package.json
  20. 78
      spec/controllers/admin/admin_users_controller_spec.rb
  21. 92
      spec/controllers/admin/case_logs_controller_spec.rb
  22. 44
      spec/controllers/admin/dashboard_controller_spec.rb
  23. 78
      spec/controllers/admin/organisations_controller_spec.rb
  24. 97
      spec/controllers/admin/users_controller_spec.rb
  25. 162
      spec/features/admin_panel_spec.rb
  26. 50
      spec/features/auth/user_lockout_spec.rb
  27. 84
      spec/models/admin_user_spec.rb
  28. 72
      spec/requests/auth/passwords_controller_spec.rb
  29. 41
      spec/requests/rack_attack_spec.rb
  30. 10
      webpack.config.js
  31. 26
      yarn.lock

2
Gemfile

@ -27,8 +27,6 @@ gem "govuk_markdown"
gem "notifications-ruby-client" gem "notifications-ruby-client"
# A modest javascript framework for the html you already have # A modest javascript framework for the html you already have
gem "stimulus-rails" gem "stimulus-rails"
# Administration framework
gem "activeadmin"
# Admin charts # Admin charts
gem "chartkick" gem "chartkick"
# Spreadsheet parsing # Spreadsheet parsing

44
Gemfile.lock

@ -69,15 +69,6 @@ GEM
erubi (~> 1.4) erubi (~> 1.4)
rails-dom-testing (~> 2.0) rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0)
activeadmin (2.12.0)
arbre (~> 1.2, >= 1.2.1)
formtastic (>= 3.1, < 5.0)
formtastic_i18n (~> 0.4)
inherited_resources (~> 1.7)
jquery-rails (~> 4.2)
kaminari (~> 1.0, >= 1.2.1)
railties (>= 6.0, < 7.1)
ransack (>= 2.1.1, < 4)
activejob (7.0.2.4) activejob (7.0.2.4)
activesupport (= 7.0.2.4) activesupport (= 7.0.2.4)
globalid (>= 0.3.6) globalid (>= 0.3.6)
@ -100,9 +91,6 @@ GEM
tzinfo (~> 2.0) tzinfo (~> 2.0)
addressable (2.8.0) addressable (2.8.0)
public_suffix (>= 2.0.2, < 5.0) public_suffix (>= 2.0.2, < 5.0)
arbre (1.5.0)
activesupport (>= 3.0.0, < 7.1)
ruby2_keywords (>= 0.0.2, < 1.0)
ast (2.4.2) ast (2.4.2)
aws-eventstream (1.2.0) aws-eventstream (1.2.0)
aws-partitions (1.584.0) aws-partitions (1.584.0)
@ -183,9 +171,6 @@ GEM
factory_bot (~> 6.2.0) factory_bot (~> 6.2.0)
railties (>= 5.0.0) railties (>= 5.0.0)
ffi (1.15.5) ffi (1.15.5)
formtastic (4.0.0)
actionpack (>= 5.2.0)
formtastic_i18n (0.7.0)
globalid (1.0.0) globalid (1.0.0)
activesupport (>= 5.0) activesupport (>= 5.0)
govuk-components (3.0.3) govuk-components (3.0.3)
@ -200,41 +185,17 @@ GEM
govuk_markdown (1.0.0) govuk_markdown (1.0.0)
activesupport activesupport
redcarpet redcarpet
has_scope (0.8.0)
actionpack (>= 5.2)
activesupport (>= 5.2)
hashdiff (1.0.1) hashdiff (1.0.1)
html_tokenizer (0.0.7) html_tokenizer (0.0.7)
i18n (1.10.0) i18n (1.10.0)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
inherited_resources (1.13.1)
actionpack (>= 5.2, < 7.1)
has_scope (~> 0.6)
railties (>= 5.2, < 7.1)
responders (>= 2, < 4)
iniparse (1.5.0) iniparse (1.5.0)
jmespath (1.6.1) jmespath (1.6.1)
jquery-rails (4.4.0)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
jsbundling-rails (1.0.2) jsbundling-rails (1.0.2)
railties (>= 6.0.0) railties (>= 6.0.0)
json-schema (3.0.0) json-schema (3.0.0)
addressable (>= 2.8) addressable (>= 2.8)
jwt (2.3.0) jwt (2.3.0)
kaminari (1.2.2)
activesupport (>= 4.1.0)
kaminari-actionview (= 1.2.2)
kaminari-activerecord (= 1.2.2)
kaminari-core (= 1.2.2)
kaminari-actionview (1.2.2)
actionview
kaminari-core (= 1.2.2)
kaminari-activerecord (1.2.2)
activerecord
kaminari-core (= 1.2.2)
kaminari-core (1.2.2)
listen (3.7.1) listen (3.7.1)
rb-fsevent (~> 0.10, >= 0.10.3) rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10) rb-inotify (~> 0.9, >= 0.9.10)
@ -342,10 +303,6 @@ GEM
rainbow (3.1.1) rainbow (3.1.1)
rake (13.0.6) rake (13.0.6)
randexp (0.1.7) randexp (0.1.7)
ransack (3.1.0)
activerecord (>= 6.0.4)
activesupport (>= 6.0.4)
i18n
rb-fsevent (0.11.1) rb-fsevent (0.11.1)
rb-inotify (0.10.1) rb-inotify (0.10.1)
ffi (~> 1.0) ffi (~> 1.0)
@ -468,7 +425,6 @@ PLATFORMS
x86_64-linux x86_64-linux
DEPENDENCIES DEPENDENCIES
activeadmin
aws-sdk-s3 aws-sdk-s3
bootsnap (>= 1.4.4) bootsnap (>= 1.4.4)
bundler-audit bundler-audit

37
app/admin/admin_users.rb

@ -1,37 +0,0 @@
ActiveAdmin.register AdminUser do
permit_params :email, :phone, :password, :password_confirmation
controller do
def update_resource(object, attributes)
update_method = attributes.first[:password].present? ? :update : :update_without_password
object.send(update_method, *attributes)
end
end
index do
selectable_column
id_column
column :email
column "Phone Number", :phone
column :current_sign_in_at
column :sign_in_count
column :created_at
actions
end
filter :email
filter :phone
filter :current_sign_in_at
filter :sign_in_count
filter :created_at
form do |f|
f.inputs do
f.input :email
f.input :phone
f.input :password
f.input :password_confirmation
end
f.actions
end
end

20
app/admin/case_logs.rb

@ -1,20 +0,0 @@
ActiveAdmin.register CaseLog do
# See permitted parameters documentation:
# https://github.com/activeadmin/activeadmin/blob/master/docs/2-resource-customization.md#setting-up-strong-parameters
permit_params do
CaseLog.editable_fields
end
index do
selectable_column
id_column
column :created_at
column :updated_at
column :status
column :tenant_code
column :postcode_full
column :owning_organisation
column :managing_organisation
actions
end
end

32
app/admin/dashboard.rb

@ -1,32 +0,0 @@
ActiveAdmin.register_page "Dashboard" do
menu priority: 1, label: proc { I18n.t("active_admin.dashboard") }
content title: proc { I18n.t("active_admin.dashboard") } do
columns do
column do
panel "Recent logs" do
table_for CaseLog.order(updated_at: :desc).limit(10) do
column :id
column :created_at
column :updated_at
column :status
column :tenant_code
column :postcode_full
end
end
end
column do
panel "Total logs in progress" do
para CaseLog.in_progress.size
end
panel "Total logs completed" do
para CaseLog.completed.size
end
panel "Total logs completed" do
pie_chart CaseLog.group(:status).size
end
end
end
end
end

31
app/admin/organisations.rb

@ -1,31 +0,0 @@
ActiveAdmin.register Organisation do
permit_params do
permitted = %i[name
phone
provider_type
address_line1
address_line2
postcode
local_authorities
holds_own_stock
other_stock_owners
managing_agents]
permitted
end
index do
selectable_column
id_column
column :name
column "Org type", :provider_type
column "Address Line 1", :address_line1
column "Address Line 2", :address_line2
column :postcode
column "Phone Number", :phone
column :local_authorities
column :holds_own_stock
column :other_stock_owners
column :managing_agents
actions
end
end

42
app/admin/users.rb

@ -1,42 +0,0 @@
ActiveAdmin.register User do
permit_params :name, :email, :password, :password_confirmation, :organisation_id, :role
controller do
def update_resource(object, attributes)
update_method = attributes.first[:password].present? ? :update : :update_without_password
object.send(update_method, *attributes)
end
end
index do
selectable_column
id_column
column :name
column :email
column :organisation
column(:role) { |u| u.role.to_s.humanize }
column :current_sign_in_at
column :sign_in_count
column :created_at
actions
end
filter :email
filter :name
filter :organisation
filter :current_sign_in_at
filter :sign_in_count
filter :created_at
form do |f|
f.inputs do
f.input :name
f.input :email
f.input :password
f.input :password_confirmation
f.input :organisation
f.input :role
end
f.actions
end
end

0
app/concerns/.keep

15
app/concerns/admin/paper_trail.rb

@ -1,15 +0,0 @@
module Admin
module PaperTrail
extend ActiveSupport::Concern
included do
before_action :set_paper_trail_whodunnit
end
protected
def user_for_paper_trail
current_admin_user
end
end
end

6
app/controllers/auth/passwords_controller.rb

@ -62,12 +62,8 @@ protected
resource.need_two_factor_authentication?(request) ? :updated_2FA : :updated resource.need_two_factor_authentication?(request) ? :updated_2FA : :updated
end end
def resource_class_name
resource_class.name.underscore
end
def after_sending_reset_password_instructions_path_for(_resource) def after_sending_reset_password_instructions_path_for(_resource)
account_password_reset_confirmation_path(email: params.dig(resource_class_name, "email")) account_password_reset_confirmation_path(email: params.dig("user", "email"))
end end
def after_resetting_password_path_for(resource) def after_resetting_password_path_for(resource)

20
app/controllers/auth/sessions_controller.rb

@ -2,13 +2,13 @@ class Auth::SessionsController < Devise::SessionsController
include Helpers::Email include Helpers::Email
def create def create
self.resource = resource_class.new self.resource = User.new
if params.dig(resource_class_name, "email").empty? if params.dig("user", "email").empty?
resource.errors.add :email, "Enter an email address" resource.errors.add :email, "Enter an email address"
elsif !email_valid?(params.dig(resource_class_name, "email")) elsif !email_valid?(params.dig("user", "email"))
resource.errors.add :email, "Enter an email address in the correct format, like name@example.com" resource.errors.add :email, "Enter an email address in the correct format, like name@example.com"
end end
if params.dig(resource_class_name, "password").empty? if params.dig("user", "password").empty?
resource.errors.add :password, "Enter a password" resource.errors.add :password, "Enter a password"
end end
if resource.errors.present? if resource.errors.present?
@ -20,19 +20,11 @@ class Auth::SessionsController < Devise::SessionsController
private private
def resource_class
request.path.include?("admin") ? AdminUser : User
end
def resource_class_name
resource_class.name.underscore
end
def after_sign_in_path_for(resource) def after_sign_in_path_for(resource)
if resource.need_two_factor_authentication?(request) if resource.need_two_factor_authentication?(request)
send("#{resource_name}_two_factor_authentication_path") user_two_factor_authentication_path
else else
params.dig(resource_class_name, "start").present? ? case_logs_path : super params.dig("user", "start").present? ? case_logs_path : super
end end
end end
end end

7
app/frontend/active_admin.js

@ -1,7 +0,0 @@
// Load Active Admin's styles into Webpacker,
// see `active_admin.scss` for customization.
import "./styles/active_admin.scss";
import "@activeadmin/activeadmin";
import "chartkick/chart.js"

17
app/frontend/styles/active_admin.scss

@ -1,17 +0,0 @@
// Sass variable overrides must be declared before loading up Active Admin's styles.
//
// To view the variables that Active Admin provides, take a look at
// `app/assets/stylesheets/active_admin/mixins/_variables.scss` in the
// Active Admin source.
//
// For example, to change the sidebar width:
// $sidebar-width: 242px;
// Active Admin's got SASS!
@import "@activeadmin/activeadmin/src/scss/mixins";
@import "@activeadmin/activeadmin/src/scss/base";
// Overriding any non-variable Sass must be done after the fact.
// For example, to change the default status-tag color:
//
// .status_tag { background: #6090DB; }

36
app/models/admin_user.rb

@ -1,36 +0,0 @@
class AdminUser < ApplicationRecord
# Include default devise modules. Others available are:
# :confirmable, :timeoutable, :omniauthable
devise :two_factor_authenticatable, :database_authenticatable, :recoverable,
:rememberable, :validatable, :trackable, :lockable
has_one_time_password(encrypted: true)
has_paper_trail ignore: %w[last_sign_in_at
current_sign_in_at
current_sign_in_ip
last_sign_in_ip
failed_attempts
unlock_token
locked_at
reset_password_token
reset_password_sent_at
remember_created_at
sign_in_count
updated_at]
validates :phone, presence: true, numericality: true
MFA_TEMPLATE_ID = "6bdf5ee1-8e01-4be1-b1f9-747061d8a24c".freeze
RESET_PASSWORD_TEMPLATE_ID = "fbb2d415-b9b1-4507-ba0a-6e542fa3504d".freeze
def send_two_factor_authentication_code(code)
template_id = MFA_TEMPLATE_ID
personalisation = { otp: code }
DeviseNotifyMailer.new.send_email(email, template_id, personalisation)
end
def reset_password_notify_template
RESET_PASSWORD_TEMPLATE_ID
end
end

339
config/initializers/active_admin.rb

@ -1,339 +0,0 @@
ActiveAdmin.setup do |config|
# == Site Title
#
# Set the title that is displayed on the main layout
# for each of the active admin pages.
#
config.site_title = "DLUHC CORE"
# Set the link url for the title. For example, to take
# users to your main site. Defaults to no link.
#
# config.site_title_link = "/"
# Set an optional image to be displayed for the header
# instead of a string (overrides :site_title)
#
# Note: Aim for an image that's 21px high so it fits in the header.
#
# config.site_title_image = "logo.png"
# == Default Namespace
#
# Set the default namespace each administration resource
# will be added to.
#
# eg:
# config.default_namespace = :hello_world
#
# This will create resources in the HelloWorld module and
# will namespace routes to /hello_world/*
#
# To set no namespace by default, use:
# config.default_namespace = false
#
# Default:
# config.default_namespace = :admin
#
# You can customize the settings for each namespace by using
# a namespace block. For example, to change the site title
# within a namespace:
#
# config.namespace :admin do |admin|
# admin.site_title = "Custom Admin Title"
# end
#
# This will ONLY change the title for the admin section. Other
# namespaces will continue to use the main "site_title" configuration.
# == User Authentication
#
# Active Admin will automatically call an authentication
# method in a before filter of all controller actions to
# ensure that there is a currently logged in admin user.
#
# This setting changes the method which Active Admin calls
# within the application controller.
config.authentication_method = :authenticate_admin_user!
# == User Authorization
#
# Active Admin will automatically call an authorization
# method in a before filter of all controller actions to
# ensure that there is a user with proper rights. You can use
# CanCanAdapter or make your own. Please refer to documentation.
# config.authorization_adapter = ActiveAdmin::CanCanAdapter
# In case you prefer Pundit over other solutions you can here pass
# the name of default policy class. This policy will be used in every
# case when Pundit is unable to find suitable policy.
# config.pundit_default_policy = "MyDefaultPunditPolicy"
# If you wish to maintain a separate set of Pundit policies for admin
# resources, you may set a namespace here that Pundit will search
# within when looking for a resource's policy.
# config.pundit_policy_namespace = :admin
# You can customize your CanCan Ability class name here.
# config.cancan_ability_class = "Ability"
# You can specify a method to be called on unauthorized access.
# This is necessary in order to prevent a redirect loop which happens
# because, by default, user gets redirected to Dashboard. If user
# doesn't have access to Dashboard, he'll end up in a redirect loop.
# Method provided here should be defined in application_controller.rb.
# config.on_unauthorized_access = :access_denied
# == Current User
#
# Active Admin will associate actions with the current
# user performing them.
#
# This setting changes the method which Active Admin calls
# (within the application controller) to return the currently logged in user.
config.current_user_method = :current_admin_user
# == Logging Out
#
# Active Admin displays a logout link on each screen. These
# settings configure the location and method used for the link.
#
# This setting changes the path where the link points to. If it's
# a string, the strings is used as the path. If it's a Symbol, we
# will call the method to return the path.
#
# Default:
config.logout_link_path = :destroy_admin_user_session_path
# This setting changes the http method used when rendering the
# link. For example :get, :delete, :put, etc..
#
# Default:
# config.logout_link_method = :get
# == Root
#
# Set the action to call for the root path. You can set different
# roots for each namespace.
#
# Default:
# config.root_to = 'dashboard#index'
# == Admin Comments
#
# This allows your users to comment on any resource registered with Active Admin.
#
# You can completely disable comments:
config.comments = false
#
# You can change the name under which comments are registered:
# config.comments_registration_name = 'AdminComment'
#
# You can change the order for the comments and you can change the column
# to be used for ordering:
# config.comments_order = 'created_at ASC'
#
# You can disable the menu item for the comments index page:
# config.comments_menu = false
#
# You can customize the comment menu:
# config.comments_menu = { parent: 'Admin', priority: 1 }
# == Batch Actions
#
# Enable and disable Batch Actions
#
config.batch_actions = true
# == Controller Filters
#
# You can add before, after and around filters to all of your
# Active Admin resources and pages from here.
#
# config.before_action :do_something_awesome
# == Attribute Filters
#
# You can exclude possibly sensitive model attributes from being displayed,
# added to forms, or exported by default by ActiveAdmin
#
config.filter_attributes = %i[encrypted_password password password_confirmation]
# == Localize Date/Time Format
#
# Set the localize format to display dates and times.
# To understand how to localize your app with I18n, read more at
# https://guides.rubyonrails.org/i18n.html
#
# You can run `bin/rails runner 'puts I18n.t("date.formats")'` to see the
# available formats in your application.
#
config.localize_format = :long
# == Setting a Favicon
#
# config.favicon = 'favicon.ico'
# == Meta Tags
#
# Add additional meta tags to the head element of active admin pages.
#
# Add tags to all pages logged in users see:
# config.meta_tags = { author: 'My Company' }
# By default, sign up/sign in/recover password pages are excluded
# from showing up in search engine results by adding a robots meta
# tag. You can reset the hash of meta tags included in logged out
# pages:
# config.meta_tags_for_logged_out_pages = {}
# == Removing Breadcrumbs
#
# Breadcrumbs are enabled by default. You can customize them for individual
# resources or you can disable them globally from here.
#
# config.breadcrumb = false
# == Create Another Checkbox
#
# Create another checkbox is disabled by default. You can customize it for individual
# resources or you can enable them globally from here.
#
# config.create_another = true
# == Register Stylesheets & Javascripts
#
# We recommend using the built in Active Admin layout and loading
# up your own stylesheets / javascripts to customize the look
# and feel.
#
# To load a stylesheet:
# config.register_stylesheet 'my_stylesheet.css'
#
# You can provide an options hash for more control, which is passed along to stylesheet_link_tag():
# config.register_stylesheet 'my_print_stylesheet.css', media: :print
#
# To load a javascript file:
# config.register_javascript 'my_javascript.js'
# == CSV options
#
# Set the CSV builder separator
# config.csv_options = { col_sep: ';' }
#
# Force the use of quotes
# config.csv_options = { force_quotes: true }
# == Menu System
#
# You can add a navigation menu to be used in your application, or configure a provided menu
#
# To change the default utility navigation to show a link to your website & a logout btn
#
# config.namespace :admin do |admin|
# admin.build_menu :utility_navigation do |menu|
# menu.add label: "My Great Website", url: "http://www.mygreatwebsite.com", html_options: { target: :blank }
# admin.add_logout_button_to_menu menu
# end
# end
#
# If you wanted to add a static menu item to the default menu provided:
#
# config.namespace :admin do |admin|
# admin.build_menu :default do |menu|
# menu.add label: "My Great Website", url: "http://www.mygreatwebsite.com", html_options: { target: :blank }
# end
# end
# == Download Links
#
# You can disable download links on resource listing pages,
# or customize the formats shown per namespace/globally
#
# To disable/customize for the :admin namespace:
#
# config.namespace :admin do |admin|
#
# # Disable the links entirely
# admin.download_links = false
#
# # Only show XML & PDF options
# admin.download_links = [:xml, :pdf]
#
# # Enable/disable the links based on block
# # (for example, with cancan)
# admin.download_links = proc { can?(:view_download_links) }
#
# end
# == Pagination
#
# Pagination is enabled by default for all resources.
# You can control the default per page count for all resources here.
#
# config.default_per_page = 30
#
# You can control the max per page count too.
#
# config.max_per_page = 10_000
# == Filters
#
# By default the index screen includes a "Filters" sidebar on the right
# hand side with a filter for each attribute of the registered model.
# You can enable or disable them for all resources here.
#
# config.filters = true
#
# By default the filters include associations in a select, which means
# that every record will be loaded for each association (up
# to the value of config.maximum_association_filter_arity).
# You can enabled or disable the inclusion
# of those filters by default here.
#
# config.include_default_association_filters = true
# config.maximum_association_filter_arity = 256 # default value of :unlimited will change to 256 in a future version
# config.filter_columns_for_large_association = [
# :display_name,
# :full_name,
# :name,
# :username,
# :login,
# :title,
# :email,
# ]
# config.filter_method_for_large_association = '_starts_with'
# == Head
#
# You can add your own content to the site head like analytics. Make sure
# you only pass content you trust.
#
# config.head = ''.html_safe
# == Footer
#
# By default, the footer shows the current Active Admin version. You can
# override the content of the footer here.
#
# config.footer = 'my custom footer text'
# == Sorting
#
# By default ActiveAdmin::OrderClause is used for sorting logic
# You can inherit it with own class and inject it for all resources
#
# config.order_clause = MyOrderClause
# == Webpacker
#
# By default, Active Admin uses Sprocket's asset pipeline.
# You can switch to using Webpacker here.
#
# config.use_webpacker = true
end
Rails.application.config.after_initialize do
ActiveAdmin::BaseController.include Admin::PaperTrail
end

25
config/routes.rb

@ -1,28 +1,5 @@
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
Rails.application.routes.draw do Rails.application.routes.draw do
devise_for :admin_users, {
path: :admin,
controllers: {
sessions: "auth/sessions",
passwords: "auth/passwords",
unlocks: "active_admin/devise/unlocks",
registrations: "active_admin/devise/registrations",
confirmations: "active_admin/devise/confirmations",
two_factor_authentication: "auth/two_factor_authentication",
},
path_names: {
sign_in: "sign-in",
sign_out: "sign-out",
two_factor_authentication: "two-factor-authentication",
two_factor_authentication_resend_code: "resend-code",
},
sign_out_via: %i[get],
}
devise_scope :admin_user do
get "admin/two-factor-authentication/resend", to: "auth/two_factor_authentication#show_resend", as: "admin_user_two_factor_authentication_resend"
end
devise_for :users, { devise_for :users, {
path: :account, path: :account,
controllers: { controllers: {
@ -48,8 +25,6 @@ Rails.application.routes.draw do
get "/health", to: ->(_) { [204, {}, [nil]] } get "/health", to: ->(_) { [204, {}, [nil]] }
ActiveAdmin.routes(self)
root to: "start#index" root to: "start#index"
get "/accessibility-statement", to: "content#accessibility_statement" get "/accessibility-statement", to: "content#accessibility_statement"

36
db/migrate/20220525130518_drop_admin_users.rb

@ -0,0 +1,36 @@
class DropAdminUsers < ActiveRecord::Migration[7.0]
def up
drop_table :admin_users
end
def down
create_table "admin_users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at", precision: nil
t.datetime "remember_created_at", precision: nil
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "second_factor_attempts_count", default: 0
t.string "encrypted_otp_secret_key"
t.string "encrypted_otp_secret_key_iv"
t.string "encrypted_otp_secret_key_salt"
t.string "direct_otp"
t.datetime "direct_otp_sent_at", precision: nil
t.datetime "totp_timestamp", precision: nil
t.string "phone"
t.string "name"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at", precision: nil
t.datetime "last_sign_in_at", precision: nil
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.integer "failed_attempts", default: 0
t.string "unlock_token"
t.datetime "locked_at", precision: nil
t.index %w[encrypted_otp_secret_key], name: "index_admin_users_on_encrypted_otp_secret_key", unique: true
t.index %w[unlock_token], name: "index_admin_users_on_unlock_token", unique: true
end
end
end

31
db/schema.rb

@ -10,39 +10,10 @@
# #
# 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[7.0].define(version: 2022_05_23_150557) do ActiveRecord::Schema[7.0].define(version: 2022_05_25_130518) 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 "plpgsql" enable_extension "plpgsql"
create_table "admin_users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at", precision: nil
t.datetime "remember_created_at", precision: nil
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "second_factor_attempts_count", default: 0
t.string "encrypted_otp_secret_key"
t.string "encrypted_otp_secret_key_iv"
t.string "encrypted_otp_secret_key_salt"
t.string "direct_otp"
t.datetime "direct_otp_sent_at", precision: nil
t.datetime "totp_timestamp", precision: nil
t.string "phone"
t.string "name"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at", precision: nil
t.datetime "last_sign_in_at", precision: nil
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.integer "failed_attempts", default: 0
t.string "unlock_token"
t.datetime "locked_at", precision: nil
t.index ["encrypted_otp_secret_key"], name: "index_admin_users_on_encrypted_otp_secret_key", unique: true
t.index ["unlock_token"], name: "index_admin_users_on_unlock_token", unique: true
end
create_table "case_logs", force: :cascade do |t| create_table "case_logs", force: :cascade do |t|
t.integer "status", default: 0 t.integer "status", default: 0
t.datetime "created_at", null: false t.datetime "created_at", null: false

1
package.json

@ -5,7 +5,6 @@
"node": "^16.0.0" "node": "^16.0.0"
}, },
"dependencies": { "dependencies": {
"@activeadmin/activeadmin": "^2.11.0",
"@babel/core": "^7.17.7", "@babel/core": "^7.17.7",
"@babel/plugin-transform-runtime": "^7.17.0", "@babel/plugin-transform-runtime": "^7.17.0",
"@babel/preset-env": "^7.16.11", "@babel/preset-env": "^7.16.11",

78
spec/controllers/admin/admin_users_controller_spec.rb

@ -1,78 +0,0 @@
require "rails_helper"
require_relative "../../support/devise"
describe Admin::AdminUsersController, type: :controller do
render_views
let(:page) { Capybara::Node::Simple.new(response.body) }
let(:resource_title) { "Admin Users" }
let(:valid_session) { {} }
let(:signed_in_admin_user) { FactoryBot.create(:admin_user) }
before do
sign_in signed_in_admin_user
end
describe "Get admin users" do
before do
get :index, session: valid_session
end
it "returns a table of admin users" do
expect(page).to have_content(resource_title)
expect(page).to have_table("index_table_admin_users")
expect(page).to have_link(AdminUser.first.id.to_s)
end
end
describe "Create admin users" do
let(:params) { { admin_user: { email: "test2@example.com", password: "pAssword1", phone: "07566126368" } } }
it "creates a new admin user" do
expect { post :create, session: valid_session, params: }.to change(AdminUser, :count).by(1)
end
it "tracks who created the record" do
post(:create, session: valid_session, params:)
created_id = response.location.match(/[0-9]+/)[0]
whodunnit_actor = AdminUser.find_by(id: created_id).versions.last.actor
expect(whodunnit_actor).to be_a(AdminUser)
expect(whodunnit_actor.id).to eq(signed_in_admin_user.id)
end
end
describe "Update admin users" do
context "when viewing the form" do
before do
get :edit, session: valid_session, params: { id: AdminUser.first.id }
end
it "shows the correct fields" do
expect(page).to have_field("admin_user_email")
expect(page).to have_field("admin_user_password")
expect(page).to have_field("admin_user_password_confirmation")
end
end
context "when updating an admin user" do
let(:admin_user) { FactoryBot.create(:admin_user) }
let(:email) { "new_email@example.com" }
let(:params) { { id: admin_user.id, admin_user: { email: } } }
before do
patch :update, session: valid_session, params:
end
it "updates the user without needing to input a password" do
admin_user.reload
expect(admin_user.email).to eq(email)
end
it "tracks who updated the record" do
admin_user.reload
whodunnit_actor = admin_user.versions.last.actor
expect(whodunnit_actor).to be_a(AdminUser)
expect(whodunnit_actor.id).to eq(signed_in_admin_user.id)
end
end
end
end

92
spec/controllers/admin/case_logs_controller_spec.rb

@ -1,92 +0,0 @@
require "rails_helper"
require_relative "../../support/devise"
describe Admin::CaseLogsController, type: :controller do
before do
sign_in admin_user
end
render_views
let(:page) { Capybara::Node::Simple.new(response.body) }
let(:resource_title) { "Logs" }
let(:valid_session) { {} }
let(:admin_user) { FactoryBot.create(:admin_user) }
let(:user) { FactoryBot.create(:user) }
describe "Get case logs" do
let!(:case_log) { FactoryBot.create(:case_log, :in_progress) }
before do
get :index, session: valid_session
end
it "returns a table of case logs" do
expect(page).to have_content(resource_title)
expect(page).to have_table("index_table_case_logs")
expect(page).to have_link(case_log.id.to_s)
expect(page).to have_link(case_log.owning_organisation.name.to_s)
end
end
describe "Create case logs" do
let(:owning_organisation) { FactoryBot.create(:organisation) }
let(:managing_organisation) { owning_organisation }
let(:params) do
{
"case_log": {
"owning_organisation_id": owning_organisation.id,
"managing_organisation_id": managing_organisation.id,
"created_by_id": user.id,
},
}
end
it "creates a new case log" do
expect { post :create, session: valid_session, params: }.to change(CaseLog, :count).by(1)
end
it "tracks who created the record" do
post(:create, session: valid_session, params:)
created_id = response.location.match(/[0-9]+/)[0]
whodunnit_actor = CaseLog.find_by(id: created_id).versions.last.actor
expect(whodunnit_actor).to be_a(AdminUser)
expect(whodunnit_actor.id).to eq(admin_user.id)
end
end
describe "Update case log" do
let!(:case_log) { FactoryBot.create(:case_log, :in_progress) }
context "when viewing the edit form" do
before do
get :edit, session: valid_session, params: { id: case_log.id }
end
it "has the correct fields" do
expect(page).to have_field("case_log_age1")
expect(page).to have_field("case_log_tenant_code")
end
end
context "when updating the case_log" do
let(:tenant_code) { "New tenant code by Admin" }
let(:params) { { id: case_log.id, case_log: { tenant_code: } } }
before do
patch :update, session: valid_session, params:
end
it "updates the case log" do
case_log.reload
expect(case_log.tenant_code).to eq(tenant_code)
end
it "tracks who updated the record" do
case_log.reload
whodunnit_actor = case_log.versions.last.actor
expect(whodunnit_actor).to be_a(AdminUser)
expect(whodunnit_actor.id).to eq(admin_user.id)
end
end
end
end

44
spec/controllers/admin/dashboard_controller_spec.rb

@ -1,44 +0,0 @@
require "rails_helper"
require_relative "../../support/devise"
describe Admin::DashboardController, type: :controller do
before do
sign_in admin_user
end
render_views
let(:page) { Capybara::Node::Simple.new(response.body) }
let(:resource_title) { "Dashboard" }
let(:valid_session) { {} }
let(:admin_user) { FactoryBot.create(:admin_user) }
describe "Get case logs" do
before do
2.times { |_| FactoryBot.create(:case_log, :in_progress) }
FactoryBot.create(:case_log, :completed)
get :index, session: valid_session
end
it "returns a dashboard page" do
expect(page).to have_content(resource_title)
end
it "returns a panel of recent case logs" do
expect(page).to have_xpath("//div[contains(@class, 'panel') and contains(//h3, 'Recent logs')]")
end
it "returns a panel of in progress case logs" do
panel_xpath = "//div[@class='panel' and .//h3[contains(., 'Total logs in progress')]]"
panel_content_xpath = "#{panel_xpath}//div[@class='panel_contents' and .//p[contains(., 2)]]"
expect(page).to have_xpath(panel_xpath)
expect(page).to have_xpath(panel_content_xpath)
end
it "returns a panel of completed case logs" do
panel_xpath = "//div[@class='panel' and .//h3[contains(., 'Total logs completed')]]"
panel_content_xpath = "#{panel_xpath}//div[@class='panel_contents' and .//p[contains(., 1)]]"
expect(page).to have_xpath(panel_xpath)
expect(page).to have_xpath(panel_content_xpath)
end
end
end

78
spec/controllers/admin/organisations_controller_spec.rb

@ -1,78 +0,0 @@
require "rails_helper"
require_relative "../../support/devise"
describe Admin::OrganisationsController, type: :controller do
render_views
let(:page) { Capybara::Node::Simple.new(response.body) }
let(:resource_title) { "Organisations" }
let(:valid_session) { {} }
let!(:organisation) { FactoryBot.create(:organisation) }
let!(:admin_user) { FactoryBot.create(:admin_user) }
before do
sign_in admin_user
end
describe "Organisations" do
before do
get :index, session: valid_session
end
it "returns a table of admin users" do
expect(page).to have_content(resource_title)
expect(page).to have_table("index_table_organisations")
expect(page).to have_link(organisation.id.to_s)
end
end
describe "Create organisation" do
let(:params) { { organisation: { name: "DLUHC", provider_type: "LA" } } }
it "creates a organisation" do
expect { post :create, session: valid_session, params: }.to change(Organisation, :count).by(1)
end
it "tracks who created the record" do
post(:create, session: valid_session, params:)
created_id = response.location.match(/[0-9]+/)[0]
whodunnit_actor = Organisation.find_by(id: created_id).versions.last.actor
expect(whodunnit_actor).to be_a(AdminUser)
expect(whodunnit_actor.id).to eq(admin_user.id)
end
end
describe "Update organisation" do
context "when viewing the edit form" do
before do
get :edit, session: valid_session, params: { id: organisation.id }
end
it "has the correct fields" do
expect(page).to have_field("organisation_name")
expect(page).to have_field("organisation_provider_type")
expect(page).to have_field("organisation_phone")
end
end
context "when updating the organisation" do
let(:name) { "New Org Name by Admin" }
let(:params) { { id: organisation.id, organisation: { name: } } }
before do
patch :update, session: valid_session, params:
end
it "updates the organisation" do
organisation.reload
expect(organisation.name).to eq(name)
end
it "tracks who updated the record" do
organisation.reload
whodunnit_actor = organisation.versions.last.actor
expect(whodunnit_actor).to be_a(AdminUser)
expect(whodunnit_actor.id).to eq(admin_user.id)
end
end
end
end

97
spec/controllers/admin/users_controller_spec.rb

@ -1,97 +0,0 @@
require "rails_helper"
require_relative "../../support/devise"
describe Admin::UsersController, type: :controller do
render_views
let!(:user) { FactoryBot.create(:user) }
let(:organisation) { FactoryBot.create(:organisation) }
let(:page) { Capybara::Node::Simple.new(response.body) }
let(:resource_title) { "Users" }
let(:valid_session) { {} }
let!(:admin_user) { FactoryBot.create(:admin_user) }
let(:notify_client) { instance_double(Notifications::Client) }
let(:devise_notify_mailer) { DeviseNotifyMailer.new }
before do
allow(DeviseNotifyMailer).to receive(:new).and_return(devise_notify_mailer)
allow(devise_notify_mailer).to receive(:notify_client).and_return(notify_client)
allow(notify_client).to receive(:send_email).and_return(true)
sign_in admin_user
end
describe "Get users" do
before do
get :index, session: valid_session
end
it "returns a table of users" do
expect(page).to have_content(resource_title)
expect(page).to have_table("index_table_users")
expect(page).to have_link(user.id.to_s)
end
end
describe "Create users" do
let(:params) do
{
user: {
email: "somethin5@example.com",
name: "Jane",
password: "pAssword1",
organisation_id: organisation.id,
role: "data_coordinator",
},
}
end
it "creates a new user" do
expect { post :create, session: valid_session, params: }.to change(User, :count).by(1)
end
it "tracks who created the record" do
post(:create, session: valid_session, params:)
created_id = response.location.match(/[0-9]+/)[0]
whodunnit_actor = User.find_by(id: created_id).versions.last.actor
expect(whodunnit_actor).to be_a(AdminUser)
expect(whodunnit_actor.id).to eq(admin_user.id)
end
end
describe "Update users" do
context "when viewing the edit form" do
before do
get :edit, session: valid_session, params: { id: user.id }
end
it "has the correct fields" do
expect(page).to have_field("user_email")
expect(page).to have_field("user_name")
expect(page).to have_field("user_organisation_id")
expect(page).to have_field("user_role")
expect(page).to have_field("user_password")
expect(page).to have_field("user_password_confirmation")
end
end
context "when updating the user" do
let(:name) { "Pete" }
let(:params) { { id: user.id, user: { name: } } }
before do
patch :update, session: valid_session, params:
end
it "updates the user without needing to input a password" do
user.reload
expect(user.name).to eq(name)
end
it "tracks who updated the record" do
user.reload
whodunnit_actor = user.versions.last.actor
expect(whodunnit_actor).to be_a(AdminUser)
expect(whodunnit_actor.id).to eq(admin_user.id)
end
end
end
end

162
spec/features/admin_panel_spec.rb

@ -1,162 +0,0 @@
require "rails_helper"
RSpec.describe "Admin Panel" do
let!(:admin) { FactoryBot.create(:admin_user) }
let(:devise_notify_mailer) { DeviseNotifyMailer.new }
let(:notify_client) { instance_double(Notifications::Client) }
let(:mfa_template_id) { AdminUser::MFA_TEMPLATE_ID }
let(:otp) { "999111" }
before do
allow(DeviseNotifyMailer).to receive(:new).and_return(devise_notify_mailer)
allow(devise_notify_mailer).to receive(:notify_client).and_return(notify_client)
allow(notify_client).to receive(:send_email).and_return(true)
end
it "shows the admin sign in page" do
visit("/admin")
expect(page).to have_current_path("/admin/sign-in")
expect(page).to have_content("Sign in to your CORE administration account")
end
context "with a valid 2FA code" do
before do
allow(SecureRandom).to receive(:random_number).and_return(otp)
visit("/admin")
fill_in("admin_user[email]", with: admin.email)
fill_in("admin_user[password]", with: admin.password)
end
it "authenticates successfully" do
expect(notify_client).to receive(:send_email).with(
{
email_address: admin.email,
template_id: mfa_template_id,
personalisation: { otp: },
},
)
click_button("Sign in")
fill_in("code", with: otp)
click_button("Submit")
expect(page).to have_content("Dashboard")
expect(page).to have_content(I18n.t("devise.two_factor_authentication.success"))
end
context "but it is more than 15 minutes old" do
it "does not authenticate successfully" do
click_button("Sign in")
admin.update!(direct_otp_sent_at: 16.minutes.ago)
fill_in("code", with: otp)
click_button("Submit")
expect(page).to have_content("Check your email")
expect(page).to have_http_status(:unprocessable_entity)
expect(page).to have_title("Error")
expect(page).to have_selector("#error-summary-title")
end
end
end
context "with an invalid 2FA code" do
it "does not authenticate successfully" do
visit("/admin")
fill_in("admin_user[email]", with: admin.email)
fill_in("admin_user[password]", with: admin.password)
click_button("Sign in")
fill_in("code", with: otp)
click_button("Submit")
expect(page).to have_content("Check your email")
expect(page).to have_http_status(:unprocessable_entity)
expect(page).to have_title("Error")
expect(page).to have_selector("#error-summary-title")
end
end
context "when the 2FA code needs to be resent" do
before do
visit("/admin")
fill_in("admin_user[email]", with: admin.email)
fill_in("admin_user[password]", with: admin.password)
click_button("Sign in")
end
it "displays the resend view" do
click_link("Not received an email?")
expect(page).to have_button("Resend security code")
end
it "send a new OTP code and redirects back to the 2FA view" do
click_link("Not received an email?")
expect { click_button("Resend security code") }.to(change { admin.reload.direct_otp })
expect(page).to have_current_path("/admin/two-factor-authentication")
end
end
context "when logging out and in again" do
before do
allow(SecureRandom).to receive(:random_number).and_return(otp)
end
it "requires the 2FA code on each login" do
visit("/admin")
fill_in("admin_user[email]", with: admin.email)
fill_in("admin_user[password]", with: admin.password)
click_button("Sign in")
fill_in("code", with: otp)
click_button("Submit")
click_link("Logout")
visit("/admin")
fill_in("admin_user[email]", with: admin.email)
fill_in("admin_user[password]", with: admin.password)
click_button("Sign in")
expect(page).to have_content("Check your email")
end
end
context "when the admin has forgotten their password" do
let!(:admin_user) { FactoryBot.create(:admin_user, last_sign_in_at: Time.zone.now) }
let(:reset_password_token) { "MCDH5y6Km-U7CFPgAMVS" }
before do
allow(Devise.token_generator).to receive(:generate).and_return(reset_password_token)
end
it " is redirected to the reset password page when they click the reset password link" do
visit("/admin")
click_link("reset your password")
expect(page).to have_current_path("/admin/password/new")
end
it " is shown an error message if they submit without entering an email address" do
visit("/admin/password/new")
click_button("Send email")
expect(page).to have_selector("#error-summary-title")
expect(page).to have_selector("#user-email-field-error")
expect(page).to have_title("Error")
end
it " is redirected to admin login page after reset email is sent" do
visit("/admin/password/new")
fill_in("admin_user[email]", with: admin_user.email)
click_button("Send email")
expect(page).to have_content("Check your email")
end
it " is sent a reset password email via Notify" do
expect(notify_client).to receive(:send_email).with(
{
email_address: admin_user.email,
template_id: admin_user.reset_password_notify_template,
personalisation: {
name: admin_user.email,
email: admin_user.email,
organisation: "",
link: "http://localhost:3000/admin/password/edit?reset_password_token=#{reset_password_token}",
},
},
)
visit("/admin/password/new")
fill_in("admin_user[email]", with: admin_user.email)
click_button("Send email")
end
end
end

50
spec/features/auth/user_lockout_spec.rb

@ -2,7 +2,6 @@ require "rails_helper"
RSpec.describe "User Lockout" do RSpec.describe "User Lockout" do
let(:user) { FactoryBot.create(:user) } let(:user) { FactoryBot.create(:user) }
let(:admin) { FactoryBot.create(:admin_user) }
let(:max_login_attempts) { Devise.maximum_attempts } let(:max_login_attempts) { Devise.maximum_attempts }
let(:max_2fa_attempts) { Devise.max_login_attempts } let(:max_2fa_attempts) { Devise.max_login_attempts }
let(:notify_client) { instance_double(Notifications::Client) } let(:notify_client) { instance_double(Notifications::Client) }
@ -26,53 +25,4 @@ RSpec.describe "User Lockout" do
expect(page).to have_content(I18n.t("devise.failure.locked")) expect(page).to have_content(I18n.t("devise.failure.locked"))
end end
end end
context "when login-in with the wrong admin password up to a maximum number of attempts" do
before do
visit("/admin/sign-in")
max_login_attempts.times do
fill_in("admin_user[email]", with: admin.email)
fill_in("admin_user[password]", with: "wrong_password")
click_button("Sign in")
end
end
it "locks the admin account" do
visit("/admin/sign-in")
fill_in("admin_user[email]", with: admin.email)
fill_in("admin_user[password]", with: admin.password)
click_button("Sign in")
expect(page).to have_http_status(:unprocessable_entity)
expect(page).to have_content(I18n.t("devise.failure.locked"))
end
end
context "when login-in with the right admin password and incorrect 2FA token up to a maximum number of attempts" do
let(:devise_notify_mailer) { DeviseNotifyMailer.new }
before do
allow(DeviseNotifyMailer).to receive(:new).and_return(devise_notify_mailer)
allow(devise_notify_mailer).to receive(:notify_client).and_return(notify_client)
allow(notify_client).to receive(:send_email).and_return(true)
visit("/admin/sign-in")
fill_in("admin_user[email]", with: admin.email)
fill_in("admin_user[password]", with: admin.password)
click_button("Sign in")
max_2fa_attempts.times do
fill_in("code", with: "random")
click_button("Submit")
end
end
it "locks the admin account" do
visit("/admin/sign-in")
fill_in("admin_user[email]", with: admin.email)
fill_in("admin_user[password]", with: admin.password)
click_button("Sign in")
expect(page).to have_http_status(:unprocessable_entity)
expect(page).to have_content(I18n.t("devise.two_factor_authentication.account_locked"))
end
end
end end

84
spec/models/admin_user_spec.rb

@ -1,84 +0,0 @@
require "rails_helper"
RSpec.describe AdminUser, type: :model do
describe "#new" do
it "requires a phone number" do
expect {
described_class.create!(
email: "admin_test@example.com",
password: "password123",
)
}.to raise_error(ActiveRecord::RecordInvalid)
end
it "requires a numerical phone number" do
expect {
described_class.create!(
email: "admin_test@example.com",
password: "password123",
phone: "string",
)
}.to raise_error(ActiveRecord::RecordInvalid)
end
it "requires an email" do
expect {
described_class.create!(
password: "password123",
phone: "075752137",
)
}.to raise_error(ActiveRecord::RecordInvalid)
end
it "requires a password" do
expect {
described_class.create!(
email: "admin_test@example.com",
phone: "075752137",
)
}.to raise_error(ActiveRecord::RecordInvalid)
end
it "can be created" do
expect {
described_class.create!(
email: "admin_test@example.com",
password: "password123",
phone: "075752137",
)
}.to change(described_class, :count).by(1)
end
end
describe "paper trail" do
let(:admin_user) { FactoryBot.create(:admin_user) }
it "creates a record of changes to a log" do
expect { admin_user.update!(phone: "09673867853") }.to change(admin_user.versions, :count).by(1)
end
it "allows case logs to be restored to a previous version" do
admin_user.update!(phone: "09673867853")
expect(admin_user.paper_trail.previous_version.phone).to eq("07563867654")
end
it "signing in does not create a new version" do
expect {
admin_user.update!(
last_sign_in_at: Time.zone.now,
current_sign_in_at: Time.zone.now,
current_sign_in_ip: "127.0.0.1",
last_sign_in_ip: "127.0.0.1",
failed_attempts: 3,
unlock_token: "dummy",
locked_at: Time.zone.now,
reset_password_token: "dummy",
reset_password_sent_at: Time.zone.now,
remember_created_at: Time.zone.now,
sign_in_count: 5,
updated_at: Time.zone.now,
)
}.not_to change(admin_user.versions, :count)
end
end
end

72
spec/requests/auth/passwords_controller_spec.rb

@ -73,78 +73,6 @@ RSpec.describe Auth::PasswordsController, type: :request do
end end
end end
context "when an admin user" do
let(:admin_user) { FactoryBot.create(:admin_user) }
describe "reset password" do
let(:new_value) { "new-password" }
before do
allow(DeviseNotifyMailer).to receive(:notify_client).and_return(notify_client)
allow(notify_client).to receive(:send_email).and_return(true)
end
it "renders the user edit password view" do
_raw, enc = Devise.token_generator.generate(AdminUser, :reset_password_token)
get "/admin/password/edit?reset_password_token=#{enc}"
expect(page).to have_css("h1", text: I18n.t("user.reset_password"))
end
context "when passwords entered don't match" do
let(:raw) { admin_user.send_reset_password_instructions }
let(:params) do
{
id: admin_user.id,
admin_user: {
password: new_value,
password_confirmation: "something_else",
reset_password_token: raw,
},
}
end
it "shows an error" do
put "/admin/password", headers: headers, params: params
expect(response).to have_http_status(:unprocessable_entity)
expect(page).to have_content("doesn’t match new password")
end
end
context "when passwords is reset" do
let(:raw) { admin_user.send_reset_password_instructions }
let(:params) do
{
id: admin_user.id,
admin_user: {
password: new_value,
password_confirmation: new_value,
reset_password_token: raw,
},
}
end
it "updates the password" do
expect {
put "/admin/password", headers: headers, params: params
admin_user.reload
}.to change(admin_user, :encrypted_password)
end
it "sends you to the 2FA page and does not allow bypassing 2FA code" do
put "/admin/password", headers: headers, params: params
expect(response).to redirect_to("/admin/two-factor-authentication")
get "/admin/case_logs", headers: headers
expect(response).to redirect_to("/admin/two-factor-authentication")
end
it "triggers an email" do
expect(notify_client).to receive(:send_email)
put "/admin/password", headers:, params:
end
end
end
end
context "when a customer support user" do context "when a customer support user" do
let(:support_user) { FactoryBot.create(:user, :support) } let(:support_user) { FactoryBot.create(:user, :support) }

41
spec/requests/rack_attack_spec.rb

@ -12,11 +12,8 @@ describe "Rack::Attack" do
let(:devise_notify_mailer) { DeviseNotifyMailer.new } let(:devise_notify_mailer) { DeviseNotifyMailer.new }
let(:params) { { user: { email: } } } let(:params) { { user: { email: } } }
let(:admin_params) { { admin_user: { email: admin_email } } }
let(:user) { FactoryBot.create(:user) } let(:user) { FactoryBot.create(:user) }
let(:admin_user) { FactoryBot.create(:admin_user) }
let(:email) { user.email } let(:email) { user.email }
let(:admin_email) { admin_user.email }
before do before do
Rack::Attack.enabled = true Rack::Attack.enabled = true
@ -40,15 +37,6 @@ describe "Rack::Attack" do
last_response = response last_response = response
expect(last_response.status).to eq(200) expect(last_response.status).to eq(200)
end end
it "does not throttle for an admin user" do
under_limit.times do
post "/admin/password", params: admin_params
follow_redirect!
end
last_response = response
expect(last_response.status).to eq(200)
end
end end
context "when the number of requests is at the throttle limit" do context "when the number of requests is at the throttle limit" do
@ -60,26 +48,6 @@ describe "Rack::Attack" do
last_response = response last_response = response
expect(last_response.status).to eq(200) expect(last_response.status).to eq(200)
end end
it "does not throttle for an admin user" do
limit.times do
post "/admin/password", params: admin_params
follow_redirect!
end
last_response = response
expect(last_response.status).to eq(200)
end
it "does not throttle if both endpoints are hit" do
limit.times do
post "/account/password", params: params
follow_redirect!
post "/admin/password", params: admin_params
follow_redirect!
end
last_response = response
expect(last_response.status).to eq(200)
end
end end
context "when the number of requests is over the throttle limit" do context "when the number of requests is over the throttle limit" do
@ -91,15 +59,6 @@ describe "Rack::Attack" do
last_response = response last_response = response
expect(last_response.status).to eq(429) expect(last_response.status).to eq(429)
end end
it "throttles for an admin user" do
over_limit.times do
post "/admin/password", params: admin_params
follow_redirect!
end
last_response = response
expect(last_response.status).to eq(429)
end
end end
end end
end end

10
webpack.config.js

@ -13,10 +13,6 @@ module.exports = {
entry: { entry: {
application: [ application: [
"./app/frontend/application.js", "./app/frontend/application.js",
],
active_admin: [
'./app/frontend/active_admin.js',
'./app/frontend/styles/active_admin.scss'
] ]
}, },
module: { module: {
@ -68,12 +64,6 @@ module.exports = {
{ from: "app/frontend/vendor/outerHTML.js", to: "vendor" }, { from: "app/frontend/vendor/outerHTML.js", to: "vendor" },
{ from: "app/frontend/vendor/polyfill-output-value.js", to: "vendor" } { from: "app/frontend/vendor/polyfill-output-value.js", to: "vendor" }
], ],
}),
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
jquery: 'jquery',
'window.jQuery': 'jquery'
}) })
] ]
} }

26
yarn.lock

@ -2,15 +2,6 @@
# yarn lockfile v1 # yarn lockfile v1
"@activeadmin/activeadmin@^2.11.0":
version "2.12.0"
resolved "https://registry.yarnpkg.com/@activeadmin/activeadmin/-/activeadmin-2.12.0.tgz#f0f69fbc5828a538802512884304744e3d94a619"
integrity sha512-2paDrb1ZGy9T1W+LETV0nje/LmCqhqDZSEXtOKvRiC9zGMVh6IwjUD82C1ISfi5jg3VIrMihQ9tECIAS9d1f9w==
dependencies:
jquery "^3.4.1"
jquery-ui "^1.12.1"
jquery-ujs "^1.2.2"
"@ampproject/remapping@^2.1.0": "@ampproject/remapping@^2.1.0":
version "2.2.0" version "2.2.0"
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d"
@ -2249,23 +2240,6 @@ jest-worker@^27.4.5:
merge-stream "^2.0.0" merge-stream "^2.0.0"
supports-color "^8.0.0" supports-color "^8.0.0"
jquery-ui@^1.12.1:
version "1.13.1"
resolved "https://registry.yarnpkg.com/jquery-ui/-/jquery-ui-1.13.1.tgz#d0b7a42e73a04c31bb5706adf86f6f8942f64eaa"
integrity sha512-2VlU59N5P4HaumDK1Z3XEVjSvegFbEOQRgpHUBaB2Ak98Axl3hFhJ6RFcNQNuk9SfL6WxIbuLst8dW/U56NSiA==
dependencies:
jquery ">=1.8.0 <4.0.0"
jquery-ujs@^1.2.2:
version "1.2.3"
resolved "https://registry.yarnpkg.com/jquery-ujs/-/jquery-ujs-1.2.3.tgz#dcac6026ab7268e5ee41faf9d31c997cd4ddd603"
integrity sha512-59wvfx5vcCTHMeQT1/OwFiAj+UffLIwjRIoXdpO7Z7BCFGepzq9T9oLVeoItjTqjoXfUrHJvV7QU6pUR+UzOoA==
"jquery@>=1.8.0 <4.0.0", jquery@^3.4.1:
version "3.6.0"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.0.tgz#c72a09f15c1bdce142f49dbf1170bdf8adac2470"
integrity sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==
js-tokens@^4.0.0: js-tokens@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"

Loading…
Cancel
Save