banacle 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +81 -0
- data/LICENSE +22 -0
- data/README.md +53 -0
- data/Rakefile +6 -0
- data/banacle.gemspec +34 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/config.ru +3 -0
- data/docs/demo1.png +0 -0
- data/docs/demo2.png +0 -0
- data/docs/nacl.png +0 -0
- data/lib/banacle/app.rb +21 -0
- data/lib/banacle/aws_wrapper/error.rb +5 -0
- data/lib/banacle/aws_wrapper/nacl.rb +127 -0
- data/lib/banacle/aws_wrapper/result.rb +6 -0
- data/lib/banacle/aws_wrapper/vpc.rb +51 -0
- data/lib/banacle/handler.rb +61 -0
- data/lib/banacle/interactive_message/parser.rb +24 -0
- data/lib/banacle/interactive_message/renderer.rb +138 -0
- data/lib/banacle/slack.rb +191 -0
- data/lib/banacle/slack_validator.rb +32 -0
- data/lib/banacle/slash_command/builder.rb +100 -0
- data/lib/banacle/slash_command/command.rb +75 -0
- data/lib/banacle/slash_command/error.rb +6 -0
- data/lib/banacle/slash_command/parser.rb +42 -0
- data/lib/banacle/slash_command/renderer.rb +56 -0
- data/lib/banacle/version.rb +3 -0
- data/lib/banacle.rb +2 -0
- metadata +188 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 62eb7e77755ab01a7f402528bd3d4740bf57849519fecdcf9fcb2d2360c369aa
|
4
|
+
data.tar.gz: afa51477a9e6f8d19669b766fecc2a2b0e09e709247ef03da9ca96ecd19fc4e3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 99a11e4010d7663b2d991cca2a461bcbd2722f5897e5f9e4a199ab04ec353c1b38688400f4d4fdd260b68f6e4c315f0fc5cb0925cecf1bde79c9dcc73d7b2cab
|
7
|
+
data.tar.gz: 1ee71620bf8b1c67bbda63b9680585f62b526bc79d62f52474a059b2985e1f83dddb6f92ed5071e73728993c7264fb9b4b69bf011efd7fd631c8eb49f0ef7d8e
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
banacle (0.1.1)
|
5
|
+
aws-sdk-ec2
|
6
|
+
sinatra
|
7
|
+
unicorn
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
aws-eventstream (1.0.1)
|
13
|
+
aws-partitions (1.127.0)
|
14
|
+
aws-sdk-core (3.44.1)
|
15
|
+
aws-eventstream (~> 1.0)
|
16
|
+
aws-partitions (~> 1.0)
|
17
|
+
aws-sigv4 (~> 1.0)
|
18
|
+
jmespath (~> 1.0)
|
19
|
+
aws-sdk-ec2 (1.65.0)
|
20
|
+
aws-sdk-core (~> 3, >= 3.39.0)
|
21
|
+
aws-sigv4 (~> 1.0)
|
22
|
+
aws-sigv4 (1.0.3)
|
23
|
+
backports (3.11.4)
|
24
|
+
coderay (1.1.2)
|
25
|
+
diff-lcs (1.3)
|
26
|
+
jmespath (1.4.0)
|
27
|
+
kgio (2.11.2)
|
28
|
+
method_source (0.9.2)
|
29
|
+
multi_json (1.13.1)
|
30
|
+
mustermann (1.0.3)
|
31
|
+
pry (0.12.2)
|
32
|
+
coderay (~> 1.1.0)
|
33
|
+
method_source (~> 0.9.0)
|
34
|
+
rack (2.0.6)
|
35
|
+
rack-protection (2.0.5)
|
36
|
+
rack
|
37
|
+
raindrops (0.19.0)
|
38
|
+
rake (10.5.0)
|
39
|
+
rspec (3.8.0)
|
40
|
+
rspec-core (~> 3.8.0)
|
41
|
+
rspec-expectations (~> 3.8.0)
|
42
|
+
rspec-mocks (~> 3.8.0)
|
43
|
+
rspec-core (3.8.0)
|
44
|
+
rspec-support (~> 3.8.0)
|
45
|
+
rspec-expectations (3.8.2)
|
46
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
47
|
+
rspec-support (~> 3.8.0)
|
48
|
+
rspec-mocks (3.8.0)
|
49
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
50
|
+
rspec-support (~> 3.8.0)
|
51
|
+
rspec-support (3.8.0)
|
52
|
+
sinatra (2.0.5)
|
53
|
+
mustermann (~> 1.0)
|
54
|
+
rack (~> 2.0)
|
55
|
+
rack-protection (= 2.0.5)
|
56
|
+
tilt (~> 2.0)
|
57
|
+
sinatra-contrib (2.0.5)
|
58
|
+
backports (>= 2.8.2)
|
59
|
+
multi_json
|
60
|
+
mustermann (~> 1.0)
|
61
|
+
rack-protection (= 2.0.5)
|
62
|
+
sinatra (= 2.0.5)
|
63
|
+
tilt (>= 1.3, < 3)
|
64
|
+
tilt (2.0.9)
|
65
|
+
unicorn (5.4.1)
|
66
|
+
kgio (~> 2.6)
|
67
|
+
raindrops (~> 0.7)
|
68
|
+
|
69
|
+
PLATFORMS
|
70
|
+
ruby
|
71
|
+
|
72
|
+
DEPENDENCIES
|
73
|
+
banacle!
|
74
|
+
bundler (~> 1.16)
|
75
|
+
pry
|
76
|
+
rake (~> 10.0)
|
77
|
+
rspec (~> 3.0)
|
78
|
+
sinatra-contrib
|
79
|
+
|
80
|
+
BUNDLED WITH
|
81
|
+
1.16.2
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2018 Takuya Kosugiyama
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
|
data/README.md
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# Banacle: Create or delete DENY NACL entries on AWS VPC as ChatOps (Slack Slash Command)
|
2
|
+
|
3
|
+
## Installation
|
4
|
+
|
5
|
+
Add this line to your application's Gemfile:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
gem 'banacle'
|
9
|
+
```
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
```
|
14
|
+
$ bundle
|
15
|
+
```
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
```
|
20
|
+
$ gem install banacle
|
21
|
+
```
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
Banacle is supposed to be run as a Sinatra server. You can run it simply by `rackup` command. Banacle has two endpoints for Slack as follows:
|
26
|
+
|
27
|
+
- `/slack/command`: handle Slash Command
|
28
|
+
- `/slack/message`: handle Interactive Message
|
29
|
+
|
30
|
+
### Example: ban 1.2.3.4 from my VPC
|
31
|
+
|
32
|
+
Execute an command that create a DENY NACL entry for 1.2.3.4 on a VPC named "test" in ap-northeast-1.
|
33
|
+
|
34
|
+
![](./docs/demo1.png)
|
35
|
+
|
36
|
+
Then an approval request appears. Someone else can review the request and decide to approve or reject it. The requester can cancel the request. In this case, the request looks good so the reviewer clicks "Approve" button.
|
37
|
+
|
38
|
+
![](./docs/demo2.png)
|
39
|
+
|
40
|
+
After approving the request, Banacle executes creating the NACL entry on the target VPC through the AWS API as follows.
|
41
|
+
|
42
|
+
![](./docs/nacl.png)
|
43
|
+
|
44
|
+
|
45
|
+
## Development
|
46
|
+
|
47
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
48
|
+
|
49
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
50
|
+
|
51
|
+
## Contributing
|
52
|
+
|
53
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/itkq/banacle.
|
data/Rakefile
ADDED
data/banacle.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "banacle/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "banacle"
|
8
|
+
spec.version = Banacle::VERSION
|
9
|
+
spec.authors = ["Takuya Kosugiyama"]
|
10
|
+
spec.email = ["re@itkq.jp"]
|
11
|
+
|
12
|
+
spec.summary = %q{Create or delete DENY NACL entries on AWS VPC as ChatOps (Slack Slash Command)}
|
13
|
+
spec.description = %q{Create or delete DENY NACL entries on AWS VPC as ChatOps (Slack Slash Command)}
|
14
|
+
spec.homepage = "https://github.com/itkq/banacle"
|
15
|
+
|
16
|
+
# Specify which files should be added to the gem when it is released.
|
17
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
18
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
19
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
20
|
+
end
|
21
|
+
spec.bindir = "exe"
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
+
spec.require_paths = ["lib"]
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
26
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
27
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
28
|
+
spec.add_development_dependency "sinatra-contrib"
|
29
|
+
spec.add_development_dependency "pry"
|
30
|
+
|
31
|
+
spec.add_dependency "sinatra"
|
32
|
+
spec.add_dependency "unicorn"
|
33
|
+
spec.add_dependency "aws-sdk-ec2"
|
34
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "banacle"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/config.ru
ADDED
data/docs/demo1.png
ADDED
Binary file
|
data/docs/demo2.png
ADDED
Binary file
|
data/docs/nacl.png
ADDED
Binary file
|
data/lib/banacle/app.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'sinatra/reloader'
|
3
|
+
require 'banacle/handler'
|
4
|
+
|
5
|
+
module Banacle
|
6
|
+
class App < Sinatra::Base
|
7
|
+
configure :development do
|
8
|
+
register Sinatra::Reloader
|
9
|
+
end
|
10
|
+
|
11
|
+
post '/slack/command' do
|
12
|
+
content_type :json
|
13
|
+
Handler.handle_slash_command(request)
|
14
|
+
end
|
15
|
+
|
16
|
+
post '/slack/message' do
|
17
|
+
content_type :json
|
18
|
+
Handler.handle_interactive_message(request)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'aws-sdk-ec2'
|
2
|
+
require 'banacle/aws_wrapper/error'
|
3
|
+
require 'banacle/aws_wrapper/result'
|
4
|
+
|
5
|
+
module Banacle
|
6
|
+
module AwsWrapper
|
7
|
+
class Nacl
|
8
|
+
class EntryDuplicatedError < AwsWrapper::Error; end
|
9
|
+
class EntryNotFoundError < AwsWrapper::Error; end
|
10
|
+
|
11
|
+
DEFAULT_RULE_NUMBER = 100
|
12
|
+
|
13
|
+
def self.create_network_acl_ingress_entries(region:, vpc_id:, cidr_blocks:)
|
14
|
+
new(region, vpc_id, cidr_blocks).create_network_acl_ingress_entries
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.delete_network_acl_entries(region:, vpc_id:, cidr_blocks:)
|
18
|
+
new(region, vpc_id, cidr_blocks).delete_network_acl_entries
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(region, vpc_id, cidr_blocks)
|
22
|
+
@region = region
|
23
|
+
@vpc_id = vpc_id
|
24
|
+
@cidr_blocks = cidr_blocks
|
25
|
+
@rule_numbers = ingress_rules.map(&:rule_number).sort
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :action, :region, :vpc_id, :cidr_blocks
|
29
|
+
attr_accessor :rule_numbers
|
30
|
+
|
31
|
+
def create_network_acl_ingress_entries
|
32
|
+
cidr_blocks.map do |cidr_block|
|
33
|
+
result = begin
|
34
|
+
create_network_acl_ingress_entry(cidr_block)
|
35
|
+
AwsWrapper::Result.new(status: true)
|
36
|
+
rescue AwsWrapper::Error => e
|
37
|
+
AwsWrapper::Result.new(status: false, error: e)
|
38
|
+
end
|
39
|
+
[cidr_block, result]
|
40
|
+
end.to_h
|
41
|
+
end
|
42
|
+
|
43
|
+
def delete_network_acl_entries
|
44
|
+
cidr_blocks.map do |cidr_block|
|
45
|
+
result = begin
|
46
|
+
delete_network_acl_entry(cidr_block)
|
47
|
+
AwsWrapper::Result.new(status: true)
|
48
|
+
rescue AwsWrapper::Error => e
|
49
|
+
AwsWrapper::Result.new(status: false, error: e)
|
50
|
+
end
|
51
|
+
[cidr_block, result]
|
52
|
+
end.to_h
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def create_network_acl_ingress_entry(cidr_block)
|
58
|
+
duplicated_rule = ingress_rules.select { |e|
|
59
|
+
e.cidr_block == cidr_block
|
60
|
+
}.first
|
61
|
+
|
62
|
+
if duplicated_rule
|
63
|
+
raise EntryDuplicatedError.new("entry already exists (rule_number: #{duplicated_rule.rule_number})")
|
64
|
+
end
|
65
|
+
|
66
|
+
next_min_rule_number = nil
|
67
|
+
(0..rule_numbers.size - 1).each do |i|
|
68
|
+
if rule_numbers[i + 1] - rule_numbers[i] > 1
|
69
|
+
next_min_rule_number = rule_numbers[i] + 1
|
70
|
+
break
|
71
|
+
end
|
72
|
+
end
|
73
|
+
next_min_rule_number = DEFAULT_RULE_NUMBER unless next_min_rule_number
|
74
|
+
|
75
|
+
ec2.create_network_acl_entry(
|
76
|
+
cidr_block: cidr_block,
|
77
|
+
egress: false,
|
78
|
+
network_acl_id: network_acl_id,
|
79
|
+
protocol: "-1", # all protocols
|
80
|
+
rule_action: "deny",
|
81
|
+
rule_number: next_min_rule_number,
|
82
|
+
)
|
83
|
+
|
84
|
+
add_rule_number(next_min_rule_number)
|
85
|
+
end
|
86
|
+
|
87
|
+
def delete_network_acl_entry(cidr_block)
|
88
|
+
target = ingress_rules.select { |e| !e.egress && e.cidr_block == cidr_block }.first
|
89
|
+
if target
|
90
|
+
ec2.delete_network_acl_entry(
|
91
|
+
egress: false,
|
92
|
+
network_acl_id: network_acl_id,
|
93
|
+
rule_number: target.rule_number,
|
94
|
+
)
|
95
|
+
else
|
96
|
+
raise EntryDuplicatedError.new("not found")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def add_rule_number(num)
|
101
|
+
rule_numbers << num
|
102
|
+
rule_numbers.sort!
|
103
|
+
num
|
104
|
+
end
|
105
|
+
|
106
|
+
def network_acl_id
|
107
|
+
acl.network_acl_id
|
108
|
+
end
|
109
|
+
|
110
|
+
def ingress_rules
|
111
|
+
@ingress_rules ||= acl.entries.select { |e| !e.egress }
|
112
|
+
end
|
113
|
+
|
114
|
+
def acl
|
115
|
+
@acl ||= ec2.describe_network_acls(
|
116
|
+
filters: [
|
117
|
+
{ name: 'vpc-id', values: [vpc_id] },
|
118
|
+
],
|
119
|
+
).network_acls.first
|
120
|
+
end
|
121
|
+
|
122
|
+
def ec2
|
123
|
+
@ec2 ||= ::Aws::EC2::Client.new(region: region)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'aws-sdk-ec2'
|
2
|
+
require 'banacle/aws_wrapper/error'
|
3
|
+
require 'banacle/aws_wrapper/result'
|
4
|
+
|
5
|
+
module Banacle
|
6
|
+
module AwsWrapper
|
7
|
+
class Vpc
|
8
|
+
class InvalidRegionError < AwsWrapper::Error; end
|
9
|
+
|
10
|
+
def self.resolve_vpc_id(region, vpc_id_or_name)
|
11
|
+
new(region, vpc_id_or_name).resolve_vpc_id
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(region, vpc_id_or_name)
|
15
|
+
@region = region
|
16
|
+
@vpc_id_or_name = vpc_id_or_name
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :region, :vpc_id_or_name
|
20
|
+
|
21
|
+
def resolve_vpc_id
|
22
|
+
begin
|
23
|
+
vpc_list = ec2.describe_vpcs.each.flat_map(&:vpcs).map do |vpc|
|
24
|
+
name_tag = vpc.tags.find { |t| t.key == "Name" }
|
25
|
+
[
|
26
|
+
name_tag.value,
|
27
|
+
vpc.vpc_id,
|
28
|
+
]
|
29
|
+
end.sort_by { |e| e[0] }.to_h
|
30
|
+
rescue Aws::Errors::NoSuchEndpointError
|
31
|
+
raise InvalidRegionError.new("region: #{region} is invalid")
|
32
|
+
end
|
33
|
+
|
34
|
+
vpc_id = nil
|
35
|
+
if vpc_list.values.include?(vpc_id_or_name)
|
36
|
+
vpc_id = vpc_id_or_name
|
37
|
+
else
|
38
|
+
vpc_id = vpc_list[vpc_id_or_name]
|
39
|
+
end
|
40
|
+
|
41
|
+
vpc_id
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def ec2
|
47
|
+
@ec2 ||= ::Aws::EC2::Client.new(region: region)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'banacle/slash_command/error'
|
2
|
+
require 'banacle/slash_command/parser'
|
3
|
+
require 'banacle/slash_command/renderer'
|
4
|
+
|
5
|
+
require 'banacle/interactive_message/parser'
|
6
|
+
require 'banacle/interactive_message/renderer'
|
7
|
+
|
8
|
+
require 'banacle/slack_validator'
|
9
|
+
|
10
|
+
module Banacle
|
11
|
+
class Handler
|
12
|
+
def self.handle_slash_command(request)
|
13
|
+
new(request).handle_slash_command
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.handle_interactive_message(request)
|
17
|
+
new(request).handle_interactive_message
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(request)
|
21
|
+
@request = request
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :request
|
25
|
+
|
26
|
+
def handle_slash_command
|
27
|
+
unless skip_validation? || SlackValidator.valid_signature?(request)
|
28
|
+
return [401, {}, "invalid request"]
|
29
|
+
end
|
30
|
+
|
31
|
+
begin
|
32
|
+
command = SlashCommand::Parser.parse(request_text)
|
33
|
+
rescue SlashCommand::Error => e
|
34
|
+
return SlashCommand::Renderer.render_error(e)
|
35
|
+
end
|
36
|
+
|
37
|
+
SlashCommand::Renderer.render(request.params, command)
|
38
|
+
end
|
39
|
+
|
40
|
+
def handle_interactive_message
|
41
|
+
unless skip_validation? || SlackValidator.valid_signature?(request)
|
42
|
+
return [401, {}, "invalid request"]
|
43
|
+
end
|
44
|
+
|
45
|
+
command = InteractiveMessage::Parser.parse(JSON.parse(request_payload))
|
46
|
+
InteractiveMessage::Renderer.render(request.params, command)
|
47
|
+
end
|
48
|
+
|
49
|
+
def request_text
|
50
|
+
request.params["text"]
|
51
|
+
end
|
52
|
+
|
53
|
+
def request_payload
|
54
|
+
request.params["payload"]
|
55
|
+
end
|
56
|
+
|
57
|
+
def skip_validation?
|
58
|
+
request.params["skip_validation"] || ENV["BANACLE_SKIP_VALIDATION"]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'banacle/slash_command/command'
|
2
|
+
|
3
|
+
module Banacle
|
4
|
+
module InteractiveMessage
|
5
|
+
class Parser
|
6
|
+
def self.parse(payload)
|
7
|
+
new.parse(payload)
|
8
|
+
end
|
9
|
+
|
10
|
+
def parse(payload)
|
11
|
+
original_text = payload["original_message"]["text"]
|
12
|
+
original_json = JSON.parse(
|
13
|
+
original_text.match(command_json_regex)[1].strip, symbolize_names: true,
|
14
|
+
)
|
15
|
+
command = SlashCommand::Command.new(**original_json)
|
16
|
+
end
|
17
|
+
|
18
|
+
# TODO: sync slash_command/renderer
|
19
|
+
def command_json_regex
|
20
|
+
/```([^`]+)```/.freeze
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|