-
Notifications
You must be signed in to change notification settings - Fork 2
/
app.rb
171 lines (130 loc) · 4.04 KB
/
app.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#!/usr/bin/env ruby
# Coding: UTF-8
require "ipaddr"
class PatliteArgumentError < ArgumentError; end
configure do
set :haml, format: :html5
allow_hosts = ENV["ALLOW_HOSTS"]&.split(?,) || %w(127.0.0.0/8 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16)
set :allowed_hosts, allow_hosts.map{|x| IPAddr.new(x) }
set :allowed_commands, %w(alert clear status test) # doclear dotest
set :allowed_alert_options, %i(r y g z sec)
set :allowed_clear_options, %i(p z)
set :patlite_host, ENV["PATLITE_HOST"]
set :patlite_ruser, ENV["PATLITE_RUSER"] || "patlite"
end
helpers do
def patlite_command(command, options, dry_run: false)
if not settings.allowed_commands.include?(command)
raise "Invalid command: #{command}"
end
options_str = ""
error_keys = []
if command == "alert"
default_options = { r: 9, y: 9, g: 9, z: 9, sec: 0 }
matcher = {
r: /^[01239]$/,
y: /^[01239]$/,
g: /^[01239]$/,
z: /^[012349]$/,
sec: /^[0-9]{,2}$/
}
options = default_options.merge(options).inject(Hash.new) do |result, (key, value)|
if not matcher[key] === value.to_s
error_keys << key
else
result[key] = value.to_s
end
result
end
options_str = "%s%s%s00%s %s" % options.values_at(*settings.allowed_alert_options)
elsif command == "clear"
matcher = {
p: /^[01]$/,
z: /^[01]$/,
}
options = options.inject(Hash.new) do |result, (key, value)|
if not matcher[key] === value.to_s
error_keys << key
else
result[key] = value.to_s
end
result
end
options_str = options.select{|key, value| value == 1}.keys.map{|x| "-#{x}" }.join(?\s)
end
if not error_keys.empty?
raise PatliteArgumentError, "%s %s invalid" % [error_keys.join(?\s), error_keys.size == 1 ? "is" : "are"]
end
return if dry_run
command_str = "rsh -l #{settings.patlite_ruser} #{settings.patlite_host} #{command} #{options_str}"
logger.info "Cmd: #{command_str}"
result = `#{command_str}`
end
def protected!(user, pass)
unless authorized?(user, pass)
response['WWW-Authenticate'] = %(Basic realm="Restricted Area")
throw(:halt, [401, "Not authorized\n"])
end
end
def authorized?(user, pass)
@auth ||= Rack::Auth::Basic::Request.new(request.env)
@auth.provided? && @auth.basic? && @auth.credentials && @auth.credentials == [user, pass]
end
end
get "/?" do
@lamp_options = %i(r y g)
@buzzer_option = :z
@sec_option = :sec
haml :index
end
before "/webhook" do
halt 403 if not settings.allowed_hosts.any?{|range| range === request.ip }
end
post "/webhook" do
protected!("user", "password")
request.body.rewind
params = JSON.parse(request.body.read)
logger.info "Received webhook: `#{params}`"
case params["event"]
when "sample"
patlite_command("test")
when "alert"
status = params.dig("alert", "status")
case status
when "critical"
patlite_command("alert", r: 1, z: 1, sec: 2)
when "warning"
patlite_command("alert", y: 1, z: 1, sec: 2)
when "ok"
patlite_command("alert", g: 1, z: 1, sec: 2)
end
end
end
before %r{/cmd/(?<command>.+)} do
if not settings.allowed_commands.include?(params[:command])
not_found
end
end
get %r{/cmd/(?<command>.+)} do
command = params[:command]
options = settings.allowed_alert_options.inject(Hash.new) do |hash, key|
hash[key] = params[key] if not params[key].nil?
hash
end
begin
patlite_command(command, options, dry_run: true)
rescue PatliteArgumentError => e
result = { status: "error", command: command, parameter: options}
logger.error result
halt 412, result.to_json
end
logger.info "Execute: command: #{command}, options: #{options}"
patlite_result = patlite_command(command, options)
result = { status: "success", command: command, patlite_result: patlite_result }
logger.info result
result.to_json
end
not_found do
content_type :json
{ error: "not found" }.to_json
end