# frozen_string_literal: true module Motor module Configs class SyncMiddleware ACCESS_KEY = ENV.fetch('MOTOR_SYNC_API_KEY', '') KeyNotSpecified = Class.new(StandardError) NotAuthenticated = Class.new(StandardError) def initialize(app) @app = app end def call(env) if env['PATH_INFO'] == Motor::Configs::SYNC_API_PATH authenticate!(env['QUERY_STRING']) case env['REQUEST_METHOD'] when 'GET' respond_with_configs when 'POST' sync_configs(env['rack.input'].read) else @app.call(env) end else @app.call(env) end rescue NotAuthenticated [403, {}, ['Invalid synchronization API key']] rescue KeyNotSpecified [404, {}, ['Set `MOTOR_SYNC_API_KEY` environment variable in order to sync configs']] end private def authenticate!(query_string) raise KeyNotSpecified if ACCESS_KEY.blank? raise NotAuthenticated if query_string.blank? is_token_valid = ActiveSupport::SecurityUtils.secure_compare( Digest::SHA256.hexdigest(query_string), Digest::SHA256.hexdigest("token=#{ACCESS_KEY}") ) raise NotAuthenticated unless is_token_valid end def respond_with_configs [ 200, { 'Content-Type' => 'application/json' }, [Motor::Configs::BuildConfigsHash.call.to_json] ] rescue StandardError => e [500, {}, [e.message]] end def sync_configs(body) configs_hash = JSON.parse(body) Motor::Configs::SyncFromHash.call(configs_hash) [200, {}, []] rescue StandardError => e [500, {}, [e.message]] end end end end