Module: Sinatra::Slack::InstanceHelpers

Defined in:
lib/sinatra/slack/instance_helpers.rb

Overview

Instance level helper methods

Instance Method Summary collapse

Instance Method Details

#actionObject



16
17
18
# File 'lib/sinatra/slack/instance_helpers.rb', line 16

def action
  @action ||= Helpers::ActionRequest.parse(params)
end

#authorized?Boolean

Checks for Slack defined HTTP headers and computes the request signature (HMAC). If provided signature is the same as the computed one, the request is valid.

Go to this page for the verification process: api.slack.com/docs/verifying-requests-from-slack

Returns:

  • (Boolean)


62
63
64
65
66
67
68
# File 'lib/sinatra/slack/instance_helpers.rb', line 62

def authorized?
  logger.warn 'Missing Slack signing token' unless settings.slack_secret
  return true unless settings.slack_secret

  valid_headers? &&
    compute_signature(settings.slack_secret) == slack_signature
end

#channelObject



20
21
22
# File 'lib/sinatra/slack/instance_helpers.rb', line 20

def channel
  @channel ||= Helpers::Channel.parse(params)
end

#commandObject



12
13
14
# File 'lib/sinatra/slack/instance_helpers.rb', line 12

def command
  @command ||= Helpers::CommandRequest.new(params)
end

#compute_signature(secret) ⇒ Object



87
88
89
90
91
92
93
94
# File 'lib/sinatra/slack/instance_helpers.rb', line 87

def compute_signature(secret)
  # in case someone already read it
  request.body.rewind

  # From Slack API docs, the "v0" is always fixed for now
  sig_basestring = "v0:#{slack_timestamp}:#{request.body.read}"
  "v0=#{hmac_signed(sig_basestring, secret)}"
end

#handle_request(request_handler:, request_params:, quick_reply: '...') ⇒ Object



36
37
38
39
40
41
42
43
44
45
# File 'lib/sinatra/slack/instance_helpers.rb', line 36

def handle_request(request_handler:, request_params:, quick_reply: '...')
  EM.defer do
    handle_with_rescue do
      deferred_message = request_handler.bind(self).call(*request_params)
      channel.send(deferred_message)
    end
  end

  body quick_reply
end

#handle_with_rescueObject



47
48
49
50
51
52
53
54
# File 'lib/sinatra/slack/instance_helpers.rb', line 47

def handle_with_rescue
  return unless block_given?

  yield
rescue StandardError => ex
  logger.error ex.full_message
  channel.send(slack_error_notification)
end

#hmac_signed(to_sign, hmac_key) ⇒ Object



96
97
98
99
# File 'lib/sinatra/slack/instance_helpers.rb', line 96

def hmac_signed(to_sign, hmac_key)
  sha256 = OpenSSL::Digest.new('sha256')
  OpenSSL::HMAC.hexdigest(sha256, hmac_key, to_sign)
end

#slack_error_notificationObject



30
31
32
33
34
# File 'lib/sinatra/slack/instance_helpers.rb', line 30

def slack_error_notification
  slack_response '' do |r|
    r.text = 'Ups, something went wrong'
  end
end

#slack_response(callback_id) {|s_resp| ... } ⇒ Object

Yields:

  • (s_resp)


24
25
26
27
28
# File 'lib/sinatra/slack/instance_helpers.rb', line 24

def slack_response(callback_id)
  s_resp = Helpers::SlackResponse.new(callback_id)
  yield s_resp if block_given?
  s_resp
end

#slack_signatureObject

Helper methods for Slack request validation



71
72
73
# File 'lib/sinatra/slack/instance_helpers.rb', line 71

def slack_signature
  @slack_signature ||= env['HTTP_X_SLACK_SIGNATURE']
end

#slack_timestampObject



75
76
77
# File 'lib/sinatra/slack/instance_helpers.rb', line 75

def slack_timestamp
  @slack_timestamp ||= env['HTTP_X_SLACK_REQUEST_TIMESTAMP']
end

#valid_headers?Boolean

Returns:

  • (Boolean)


79
80
81
82
83
84
85
# File 'lib/sinatra/slack/instance_helpers.rb', line 79

def valid_headers?
  return false unless slack_signature || slack_timestamp

  # The request timestamp is more than five minutes from local time.
  # It could be a replay attack, so let's ignore it.
  (Time.now.to_i - slack_timestamp.to_i).abs <= 60 * 5
end