# frozen_string_literal: true # # The System configuration. Various configuration items that can be updated/defined at run time # # Use of this class allows you to simply ask for the configuration parameter directly without # first having to get an instance of it. # # SystemConfiguration.queue_impl #=> 'RedisQueue' # # This method only is allowed for accessors, you should NEVER set values on the SystemConfiguration # unless you are updating via the Admin or Stack UI, or during testing to setup a specific configuration # for that. # module CoreSystemConfiguration extend ActiveSupport::Concern def self.included(base) base.class_eval do # # Fields # field :environment, type: String, default: 'test' # SMTP configuration field :default_email, type: String, default: 'support@app47.com' field :support_email, type: String, default: 'support@app47.com' field :smtp_name, type: String field :smtp_address, type: String field :smtp_domain, type: String field :smtp_port, type: Integer, default: 587 field :smtp_user_name, type: String field :smtp_password, type: String field :smtp_enable_starttls_auto, type: Boolean field :mailgun_api_key, type: String # Twillio support field :twilio_account_id, type: String field :twilio_auth_token, type: String field :twilio_phone_number, type: String # URLs field :base_url, type: String field :cdn_url, type: String # Slack support field :slack_api_url, type: String field :slack_support_channel, type: String, default: 'support' field :slack_sales_channel, type: String, default: 'sales' # Time Zone Support field :default_time_zone, type: String, default: 'US/Eastern' # AWS field :aws_region, type: String field :aws_access_key, type: String field :aws_access_secret, type: String field :aws_auto_scaling_group_name, type: String # Zendesk field :zendesk_token, type: String field :zendesk_base_url, type: String, default: 'https://app47.zendesk.com' field :zendesk_documentation_path, type: String, default: 'hc' field :zendesk_support_path, type: String, default: 'hc/en-us/requests' field :zendesk_updates_path, type: String, default: 'hc' # Switchboard field :switchboard_base_url, type: String, default: 'https://switchboard.app47.com' field :switchboard_stack_id, type: String field :switchboard_stack_api_token, type: String # # Validations # validates :environment, presence: true, uniqueness: true validates :slack_support_channel, presence: true validates :slack_sales_channel, presence: true validates :default_time_zone, presence: true end base.extend ClassMethods end module ClassMethods # # Fetch the system configuration based on environment name. First try the rails cache # if not there, then go to the database. # # Also, if an argument error is thrown, then just fetch from the database. # def configuration cache_key = "SystemConfiguration::#{Rails.env}" begin config = Rails.cache.fetch(cache_key, expires_in: 90.seconds) do SystemConfiguration.find_by(environment: Rails.env) end rescue StandardError # This seems to happen in testing relative to Country, when it does, remove # ourselves from the cache and return the DB entry. Rails.cache.delete cache_key config = SystemConfiguration.find_or_create_by!(environment: Rails.env) end config.nil? ? SystemConfiguration.create(environment: Rails.env) : config end def smtp_configuration output = {} config = configuration fields = %w[name address domain port user_name password enable_starttls_auto] fields.each do |field| field_name = "smtp_#{field}".to_sym output[field.to_sym] = config.send(field_name) end output end # # NOTE: Currently ignored Codacy issue: When using 'method_missing', fall back on 'super' # # rubocop:disable Style/MethodMissingSuper def method_missing(method, *args, &block) configuration.send method, *args end def respond_to?(method_name, _include_private = false) SystemConfiguration.fields.include?(method_name) end # rubocop:enable Style/MethodMissingSuper end # # Make sure the password doesn't get blanked out on an update # def secure_fields super + %i[smtp_password aws_access_secret mailgun_api_key switchboard_stack_api_token twilio_auth_token zendesk_token] end # # Determine if SMTP is configured # def smtp_configured? smtp_name.present? && smtp_address.present? && smtp_domain.present? end # # Determine if mailgun is configured # def mailgun_configured? smtp_configured? && mailgun_api_key.present? end # # Determine if AWS is configured # def aws_configured? [aws_region.present?, aws_access_key.present?, aws_access_secret.present?].all? end # # Determine if auto scaling group is configured # def aws_auto_scaling_configured? aws_configured? && aws_auto_scaling_group_name.present? end # # AWS client. # def aws_ec2_client return nil unless aws_configured? Aws::EC2::Client.new region: aws_region, credentials: Aws::Credentials.new(aws_access_key, aws_access_secret) end # # Return the zendesk documentation URL # def zendesk_documentation_url(user = nil) zendesk_url(forward_to: zendesk_documentation_path, user: user) end # # Return the zendesk support URL # def zendesk_requests_url(user = nil) zendesk_url(forward_to: zendesk_support_path, user: user) end # # Return the zendesk support URL # def zendesk_new_request_url(user = nil) zendesk_url(forward_to: "#{zendesk_support_path}/new", user: user) end # # Return the zendesk update URL # def zendesk_updates_url(user = nil) zendesk_url(forward_to: zendesk_updates_path, user: user) end # # Generate a Zendesk URL # # If a user is passed in and Zendesk is configured then return a JWT enabled URL for # SSO authentication to Zendesk. # def zendesk_url(forward_to: zendesk_documentation_path, user: nil) config = SystemConfiguration.configuration path = if config.zendesk_configured? && user.present? time_now = Time.now.to_i jti = "#{time_now}/#{rand(36 ** 64).to_s(36)}" payload = { jwt: JWT.encode({ iat: time_now, # Seconds since epoch, determine when this token is stale jti: jti, # Unique token identifier, helps prevent replay attacks name: user.name, email: user.email }, config.zendesk_token), return_to: CGI.escape([config.zendesk_base_url, forward_to].join('/')) } ['access/jwt', payload.to_query].join('?') else forward_to end [config.zendesk_base_url, path].join('/') end # # Is zendesk configured? # def zendesk_configured? [zendesk_token.present?, zendesk_base_url.present?, zendesk_documentation_path.present?, zendesk_support_path.present?].all? end # # Public: Determine if switchboard is configured # # Examples # # switchboard_configured? # # => true || false # def switchboard_configured? [switchboard_base_url.present?, switchboard_stack_api_token.present?, switchboard_stack_id.present?].all? end # # Determine if twillio is configured at a system configuration # # Examples # # switchboard_configured? # # => true || false # def twilio_configured? [twilio_account_id.present?, twilio_auth_token.present?, twilio_phone_number.present?].all? end # # Determine if Slack is configured # # Examples # # switchboard_configured? # # => true || false # def slack_configured? slack_api_url.present? end private # # Clear the cache when the object is saved # def clear_cache Rails.cache.delete "SystemConfiguration::#{Rails.env}" end end