module Chatops::Controller
Public Instance Methods
execute_chatop()
click to toggle source
# File lib/chatops/controller.rb, line 45 def execute_chatop # This needs to exist for route declarations, but we'll be overriding # things in #process to make a method the action. end
list()
click to toggle source
# File lib/chatops/controller.rb, line 18 def list chatops = self.class.chatops chatops.each { |name, hash| hash[:path] = name } render :json => { namespace: self.class.chatops_namespace, help: self.class.chatops_help, error_response: self.class.chatops_error_response, methods: chatops, version: "3" } end
process(*args)
click to toggle source
Calls superclass method
# File lib/chatops/controller.rb, line 29 def process(*args) setup_params! if params[:chatop].present? params[:action] = params[:chatop] args[0] = params[:action] unless self.respond_to?(params[:chatop].to_sym) raise AbstractController::ActionNotFound end end super(*args) rescue AbstractController::ActionNotFound return jsonrpc_method_not_found end
Protected Instance Methods
chatop_names()
click to toggle source
# File lib/chatops/controller.rb, line 200 def chatop_names self.class.chatops.keys end
chatops_test_auth?()
click to toggle source
# File lib/chatops/controller.rb, line 180 def chatops_test_auth? Rails.env.test? && request.env["CHATOPS_TESTING_AUTH"] end
ensure_chatops_authenticated()
click to toggle source
# File lib/chatops/controller.rb, line 120 def ensure_chatops_authenticated body = request.raw_post || "" signature_string = [@chatops_url, @chatops_nonce, @chatops_timestamp, body].join("\n") # We return this just to aid client debugging. response.headers["Chatops-Signature-String"] = Base64.strict_encode64(signature_string) raise ConfigurationError.new("You need to add a client's public key in .pem format via #{Chatops.public_key_env_var_name}") unless Chatops.public_key.present? if signature_valid?(Chatops.public_key, @chatops_signature, signature_string) || signature_valid?(Chatops.alt_public_key, @chatops_signature, signature_string) return true end return jsonrpc_error(-32800, 403, "Not authorized") end
ensure_method_exists()
click to toggle source
# File lib/chatops/controller.rb, line 196 def ensure_method_exists return jsonrpc_method_not_found unless (chatop_names + [:list]).include?(params[:action].to_sym) end
ensure_user_given()
click to toggle source
# File lib/chatops/controller.rb, line 114 def ensure_user_given return true unless chatop_names.include?(params[:action].to_sym) return true if params[:user].present? jsonrpc_invalid_params("A username must be supplied as 'user'") end
ensure_valid_chatops_nonce()
click to toggle source
# File lib/chatops/controller.rb, line 143 def ensure_valid_chatops_nonce @chatops_nonce = request.headers["Chatops-Nonce"] return jsonrpc_error(-32801, 403, "A Chatops-Nonce header is required") unless @chatops_nonce.present? end
ensure_valid_chatops_signature()
click to toggle source
# File lib/chatops/controller.rb, line 148 def ensure_valid_chatops_signature signature_header = request.headers["Chatops-Signature"] begin # "Chatops-Signature: Signature keyid=foo,signature=abc123" => { "keyid"" => "foo", "signature" => "abc123" } signature_items = signature_header.split(" ", 2)[1].split(",").map { |item| item.split("=", 2) }.to_h @chatops_signature = signature_items["signature"] rescue NoMethodError # The signature header munging, if something's amiss, can produce a `nil` that raises a # no method error. We'll just carry on; the nil signature will raise below end unless @chatops_signature.present? return jsonrpc_error(-32802, 403, "Failed to parse signature header") end end
ensure_valid_chatops_timestamp()
click to toggle source
# File lib/chatops/controller.rb, line 165 def ensure_valid_chatops_timestamp @chatops_timestamp = request.headers["Chatops-Timestamp"] time = Time.iso8601(@chatops_timestamp) if !(time > 1.minute.ago && time < 1.minute.from_now) return jsonrpc_error(-32803, 403, "Chatops timestamp not within 1 minute of server time: #{@chatops_timestamp} vs #{Time.now.utc.iso8601}") end rescue ArgumentError, TypeError # time parsing or missing can raise these return jsonrpc_error(-32804, 403, "Invalid Chatops-Timestamp: #{@chatops_timestamp}") end
ensure_valid_chatops_url()
click to toggle source
# File lib/chatops/controller.rb, line 133 def ensure_valid_chatops_url unless Chatops.auth_base_url.present? raise ConfigurationError.new("You need to set the server's base URL to authenticate chatops RPC via #{Chatops.auth_base_url_env_var_name}") end if Chatops.auth_base_url[-1] == "/" raise ConfigurationError.new("Don't include a trailing slash in #{Chatops.auth_base_url_env_var_name}; the rails path will be appended and it must match exactly.") end @chatops_url = Chatops.auth_base_url + request.path end
json_body()
click to toggle source
# File lib/chatops/controller.rb, line 67 def json_body hash = {} if request.content_type =~ %r/\Aapplication\/json\Z/i hash = ActiveSupport::JSON.decode(request.raw_post) || {} end hash.with_indifferent_access end
jsonrpc_error(number, http_status, message)
click to toggle source
# File lib/chatops/controller.rb, line 103 def jsonrpc_error(number, http_status, message) jsonrpc_response({ :error => { :code => number, :message => message.to_s } }, http_status) end
jsonrpc_invalid_params(message)
click to toggle source
# File lib/chatops/controller.rb, line 97 def jsonrpc_invalid_params(message) message ||= "Invalid parameters" jsonrpc_error(-32602, 400, message.to_s) end
Also aliased as: jsonrpc_failure
jsonrpc_invalid_request()
click to toggle source
# File lib/chatops/controller.rb, line 89 def jsonrpc_invalid_request jsonrpc_error(-32600, 400, "Invalid request") end
jsonrpc_method_not_found()
click to toggle source
# File lib/chatops/controller.rb, line 93 def jsonrpc_method_not_found jsonrpc_error(-32601, 404, "Method not found") end
jsonrpc_params()
click to toggle source
# File lib/chatops/controller.rb, line 63 def jsonrpc_params @jsonrpc_params ||= ActionController::Parameters.new end
jsonrpc_parse_error()
click to toggle source
# File lib/chatops/controller.rb, line 85 def jsonrpc_parse_error jsonrpc_error(-32700, 500, "Parse error") end
jsonrpc_response(hash, http_status = nil)
click to toggle source
# File lib/chatops/controller.rb, line 107 def jsonrpc_response(hash, http_status = nil) http_status ||= 200 render :status => http_status, :json => { :jsonrpc => "2.0", :id => params[:id] }.merge(hash) end
jsonrpc_success(message, options: {})
click to toggle source
`options` supports any of the optional fields documented in the [protocol](../../docs/protocol-description.md).
# File lib/chatops/controller.rb, line 77 def jsonrpc_success(message, options: {}) response = { :result => message.to_s } # do not allow options to override message options.delete(:result) jsonrpc_response response.merge(options) end
Also aliased as: chatop_send
request_is_chatop?()
click to toggle source
# File lib/chatops/controller.rb, line 176 def request_is_chatop? (chatop_names + [:list]).include?(params[:action].to_sym) end
setup_params!()
click to toggle source
# File lib/chatops/controller.rb, line 52 def setup_params! json_body.each do |key, value| next if params.has_key? key params[key] = value end @jsonrpc_params = params.delete(:params) if params.has_key? :params self.params = params.permit(:action, :chatop, :controller, :id, :mention_slug, :message_id, :method, :room_id, :user) end
should_authenticate_chatops?()
click to toggle source
# File lib/chatops/controller.rb, line 184 def should_authenticate_chatops? request_is_chatop? && !chatops_test_auth? end
signature_valid?(key_string, signature, signature_string)
click to toggle source
# File lib/chatops/controller.rb, line 188 def signature_valid?(key_string, signature, signature_string) return false unless key_string.present? digest = OpenSSL::Digest::SHA256.new decoded_signature = Base64.decode64(signature) public_key = OpenSSL::PKey::RSA.new(key_string) public_key.verify(digest, decoded_signature, signature_string) end