mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-30 18:47:51 +00:00 
			
		
		
		
	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:
		| @@ -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| | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Sojan Jose
					Sojan Jose