chook 1.0.1.b2 → 1.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGES.md +56 -0
- data/README.md +363 -127
- data/bin/chook-server +31 -1
- data/data/chook.conf.example +183 -0
- data/data/com.pixar.chook-server.plist +20 -0
- data/data/sample_handlers/RestAPIOperation.rb +11 -11
- data/data/sample_handlers/SmartGroupComputerMembershipChange.rb +3 -6
- data/data/sample_jsons/SmartGroupComputerMembershipChange.json +3 -1
- data/data/sample_jsons/SmartGroupMobileDeviceMembershipChange.json +3 -1
- data/lib/chook/configuration.rb +27 -8
- data/lib/chook/event.rb +6 -1
- data/lib/chook/event/handled_event.rb +36 -9
- data/lib/chook/event/handled_event/handlers.rb +260 -98
- data/lib/chook/event/handled_event_logger.rb +86 -0
- data/lib/chook/event_handling.rb +1 -0
- data/lib/chook/foundation.rb +3 -0
- data/lib/chook/procs.rb +17 -1
- data/lib/chook/server.rb +73 -72
- data/lib/chook/server/auth.rb +164 -0
- data/lib/chook/server/log.rb +215 -0
- data/lib/chook/server/public/css/chook.css +133 -0
- data/lib/chook/server/public/imgs/ChookLogoAlMcWhiggin.png +0 -0
- data/lib/chook/server/public/js/chook.js +126 -0
- data/lib/chook/server/public/js/logstream.js +101 -0
- data/lib/chook/server/routes.rb +28 -0
- data/lib/chook/server/routes/handle_by_name.rb +65 -0
- data/lib/chook/server/routes/handle_webhook_event.rb +27 -3
- data/lib/chook/server/routes/handlers.rb +52 -0
- data/lib/chook/server/routes/home.rb +48 -1
- data/lib/chook/server/routes/log.rb +105 -0
- data/lib/chook/server/routes/login_logout.rb +48 -0
- data/lib/chook/server/views/admin.haml +11 -0
- data/lib/chook/server/views/bak.haml +48 -0
- data/lib/chook/server/views/config.haml +15 -0
- data/lib/chook/server/views/handlers.haml +63 -0
- data/lib/chook/server/views/layout.haml +64 -0
- data/lib/chook/server/views/logstream.haml +33 -0
- data/lib/chook/server/views/sketch_admin +44 -0
- data/lib/chook/subject.rb +13 -2
- data/lib/chook/subject/dep_device.rb +81 -0
- data/lib/chook/subject/policy_finished.rb +43 -0
- data/lib/chook/subject/smart_group.rb +6 -0
- data/lib/chook/version.rb +1 -1
- metadata +79 -19
@@ -0,0 +1,133 @@
|
|
1
|
+
@charset "UTF-8";
|
2
|
+
|
3
|
+
/* ******* General Styling ******* */
|
4
|
+
|
5
|
+
.monospaced {
|
6
|
+
font-family: "Lucida Console", Monaco, monospace;
|
7
|
+
}
|
8
|
+
|
9
|
+
|
10
|
+
/* ******* Title Header Area ******* */
|
11
|
+
|
12
|
+
.chook_title {
|
13
|
+
font-weight: bold;
|
14
|
+
font-size: 2.5em;
|
15
|
+
}
|
16
|
+
|
17
|
+
#header_version {
|
18
|
+
font-size: 0.5em;
|
19
|
+
}
|
20
|
+
|
21
|
+
#definition {
|
22
|
+
line-height: 1.6;
|
23
|
+
}
|
24
|
+
|
25
|
+
#serverstats {
|
26
|
+
vertical-align: bottom;
|
27
|
+
}
|
28
|
+
|
29
|
+
.def_pronunciation {
|
30
|
+
font-style: italic;
|
31
|
+
font-size: 1.2em;
|
32
|
+
}
|
33
|
+
|
34
|
+
.def_part_of_speech {
|
35
|
+
font-weight: bold;
|
36
|
+
}
|
37
|
+
|
38
|
+
.def_dialect {
|
39
|
+
font-style: italic;
|
40
|
+
}
|
41
|
+
|
42
|
+
.def_definition {
|
43
|
+
font-size: 1.2em;
|
44
|
+
}
|
45
|
+
|
46
|
+
#login_incorrect {
|
47
|
+
color: red;
|
48
|
+
}
|
49
|
+
|
50
|
+
/* ******* Section Label Areas ******* */
|
51
|
+
|
52
|
+
.section_label {
|
53
|
+
padding-bottom: 5px;
|
54
|
+
}
|
55
|
+
|
56
|
+
/* ******* Live Log Section ******* */
|
57
|
+
|
58
|
+
#hide_log_btn {
|
59
|
+
display: none;
|
60
|
+
}
|
61
|
+
|
62
|
+
#logbox_div {
|
63
|
+
display: none;
|
64
|
+
}
|
65
|
+
|
66
|
+
#logbox_btns {
|
67
|
+
padding-bottom: 5px;
|
68
|
+
}
|
69
|
+
|
70
|
+
#logbox {
|
71
|
+
width: 100%;
|
72
|
+
}
|
73
|
+
|
74
|
+
/* ******* Handlers Section ******* */
|
75
|
+
|
76
|
+
#hide_handlers_btn {
|
77
|
+
display: none;
|
78
|
+
}
|
79
|
+
|
80
|
+
#handlers_div {
|
81
|
+
display: none;
|
82
|
+
}
|
83
|
+
|
84
|
+
#handlers_table {
|
85
|
+
width: 100%;
|
86
|
+
border: 1px solid black;
|
87
|
+
border-collapse: collapse;
|
88
|
+
margin-top: 5px;
|
89
|
+
padding-bottom: 5px;
|
90
|
+
}
|
91
|
+
|
92
|
+
#handlers_table_header_row {
|
93
|
+
text-align: left;
|
94
|
+
border: 1px solid black;
|
95
|
+
border-collapse: collapse;
|
96
|
+
background-color: LightGrey;
|
97
|
+
padding-top: 4px;
|
98
|
+
padding-left: 3px;
|
99
|
+
}
|
100
|
+
|
101
|
+
.handlers_table_cell {
|
102
|
+
text-align: left;
|
103
|
+
border: 1px solid black;
|
104
|
+
border-collapse: collapse;
|
105
|
+
padding-top: 4px;
|
106
|
+
padding-left: 3px;
|
107
|
+
}
|
108
|
+
|
109
|
+
#handler_viewer_div {
|
110
|
+
display: none;
|
111
|
+
padding-top: 15px;
|
112
|
+
}
|
113
|
+
|
114
|
+
#handler_viewer {
|
115
|
+
width: 95%;
|
116
|
+
border: 3px solid black;
|
117
|
+
padding-top: 5px;
|
118
|
+
}
|
119
|
+
|
120
|
+
/* ******* Handlers Section ******* */
|
121
|
+
|
122
|
+
#hide_config_btn {
|
123
|
+
display: none;
|
124
|
+
}
|
125
|
+
|
126
|
+
#config_div {
|
127
|
+
display: none;
|
128
|
+
}
|
129
|
+
|
130
|
+
#config_viewer_div {
|
131
|
+
margin-top: 5px;
|
132
|
+
padding-bottom: 5px;
|
133
|
+
}
|
Binary file
|
@@ -0,0 +1,126 @@
|
|
1
|
+
// log out of basic auth by sending an incorrect name /pw
|
2
|
+
// may not work on all browsers
|
3
|
+
// see second comment at
|
4
|
+
// https://stackoverflow.com/questions/233507/how-to-log-out-user-from-web-site-using-basic-authentication#492926
|
5
|
+
|
6
|
+
function logout() {
|
7
|
+
//var logged_out_page = window.location.protocol + '//xxxx:xxxx@' + window.location.host + '/login';
|
8
|
+
|
9
|
+
var xhttp = new XMLHttpRequest();
|
10
|
+
|
11
|
+
xhttp.onreadystatechange = function() {
|
12
|
+
if (this.readyState == 4) {
|
13
|
+
window.location = "/login";
|
14
|
+
}
|
15
|
+
};
|
16
|
+
|
17
|
+
xhttp.open("GET", "/LOG_OUT:no_such_pw@logout", true);
|
18
|
+
xhttp.setRequestHeader("Authorization", "Basic " + btoa("LOG_OUT:no_such_pw"));
|
19
|
+
xhttp.send();
|
20
|
+
}
|
21
|
+
|
22
|
+
// show the logbox
|
23
|
+
function view_log() {
|
24
|
+
document.getElementById("pause_log").checked = false
|
25
|
+
document.getElementById("logbox_div").style.display = 'block';
|
26
|
+
document.getElementById("view_log_btn").style.display = 'none';
|
27
|
+
document.getElementById("hide_log_btn").style.display = 'inline';
|
28
|
+
start_log_stream();
|
29
|
+
update_logbox();
|
30
|
+
}
|
31
|
+
|
32
|
+
// hide the logbox
|
33
|
+
function hide_log() {
|
34
|
+
document.getElementById("logbox_div").style.display = 'none';
|
35
|
+
document.getElementById("view_log_btn").style.display = 'inline';
|
36
|
+
document.getElementById("hide_log_btn").style.display = 'none';
|
37
|
+
document.getElementById("pause_log").checked = true;
|
38
|
+
}
|
39
|
+
|
40
|
+
// clear the log stream
|
41
|
+
function clear_log(){
|
42
|
+
document.getElementById("logbox").value = '';
|
43
|
+
log_source = '';
|
44
|
+
}
|
45
|
+
|
46
|
+
// change the log level
|
47
|
+
function change_log_level() {
|
48
|
+
var new_level = document.getElementById("log_level_select").value
|
49
|
+
var url = "/set_log_level/" + new_level
|
50
|
+
var xhttp = new XMLHttpRequest();
|
51
|
+
xhttp.open("PUT", url, true);
|
52
|
+
xhttp.send();
|
53
|
+
}
|
54
|
+
|
55
|
+
// reload the handlers
|
56
|
+
function reload_handlers() {
|
57
|
+
var url = '/reload_handlers';
|
58
|
+
var xhttp = new XMLHttpRequest();
|
59
|
+
xhttp.onreadystatechange = function() {
|
60
|
+
if (this.readyState == 4 && this.status == 200) {
|
61
|
+
now = new Date().toLocaleString();
|
62
|
+
document.getElementById("reloaded_notification").innerHTML = 'Reloaded at ' + now;
|
63
|
+
} else {
|
64
|
+
document.getElementById("reloaded_notification").innerHTML = 'Reload Failed.';
|
65
|
+
}
|
66
|
+
};
|
67
|
+
xhttp.open("GET", url, true);
|
68
|
+
xhttp.send();
|
69
|
+
}
|
70
|
+
|
71
|
+
// show the handler area
|
72
|
+
function view_handlers() {
|
73
|
+
document.getElementById("handlers_div").style.display = 'block';
|
74
|
+
document.getElementById("view_handlers_btn").style.display = 'none';
|
75
|
+
document.getElementById("hide_handlers_btn").style.display = 'inline';
|
76
|
+
}
|
77
|
+
|
78
|
+
// hide the handler area
|
79
|
+
function hide_handlers() {
|
80
|
+
document.getElementById("handlers_div").style.display = 'none';
|
81
|
+
document.getElementById("view_handlers_btn").style.display = 'inline';
|
82
|
+
document.getElementById("hide_handlers_btn").style.display = 'none';
|
83
|
+
}
|
84
|
+
|
85
|
+
// hide the handler editor
|
86
|
+
function hide_handler_viewer() {
|
87
|
+
document.getElementById("handler_viewer_div").style.display = 'none';
|
88
|
+
}
|
89
|
+
|
90
|
+
// show the handler editor with the selected handler code
|
91
|
+
// handler = the basename of the hander fle.
|
92
|
+
function view_handler_code(handler_path, type) {
|
93
|
+
fetch_handler_code(handler_path) ;
|
94
|
+
document.getElementById("currently_viewing_filename").innerHTML = 'Viewing handler file: ' + handler_path + ' (' + type + ')';
|
95
|
+
document.getElementById("handler_viewer_div").style.display = 'block';
|
96
|
+
}
|
97
|
+
|
98
|
+
// get the code for an existing handler into the editor
|
99
|
+
function fetch_handler_code(handler) {
|
100
|
+
var viewer = document.getElementById("handler_viewer");
|
101
|
+
var url = '/handler_code?filepath=' + encodeURIComponent(handler)
|
102
|
+
var xhttp = new XMLHttpRequest();
|
103
|
+
xhttp.onreadystatechange = function() {
|
104
|
+
if (this.readyState == 4 && this.status == 200) {
|
105
|
+
viewer.value = xhttp.responseText;
|
106
|
+
} else {
|
107
|
+
viewer.value = 'ERROR: File Not Found';
|
108
|
+
}
|
109
|
+
};
|
110
|
+
xhttp.open("GET", url, true);
|
111
|
+
xhttp.send();
|
112
|
+
}
|
113
|
+
|
114
|
+
// show the config area
|
115
|
+
function view_config() {
|
116
|
+
document.getElementById("config_div").style.display = 'block';
|
117
|
+
document.getElementById("view_config_btn").style.display = 'none';
|
118
|
+
document.getElementById("hide_config_btn").style.display = 'inline';
|
119
|
+
}
|
120
|
+
|
121
|
+
// hide the config area
|
122
|
+
function hide_config() {
|
123
|
+
document.getElementById("config_div").style.display = 'none';
|
124
|
+
document.getElementById("view_config_btn").style.display = 'inline';
|
125
|
+
document.getElementById("hide_config_btn").style.display = 'none';
|
126
|
+
}
|
@@ -0,0 +1,101 @@
|
|
1
|
+
|
2
|
+
// Vars
|
3
|
+
//////////////////////////////////
|
4
|
+
|
5
|
+
// the url that will provide the stream
|
6
|
+
var log_stream_url = '/subscribe_to_log_stream';
|
7
|
+
|
8
|
+
// the EventSource that will get events from the url
|
9
|
+
var log_source
|
10
|
+
|
11
|
+
// the log box on the info page
|
12
|
+
var logbox
|
13
|
+
|
14
|
+
// the checkbox to pause updating the log box
|
15
|
+
var pause_ckbx
|
16
|
+
|
17
|
+
// data from the log gets added to this string
|
18
|
+
// but its only written to the log box if we
|
19
|
+
// aren't paused.
|
20
|
+
var log_data
|
21
|
+
|
22
|
+
var new_log_level_regex = /Log level changed, now: (.*)$/;
|
23
|
+
|
24
|
+
// update the text in the log box unless the
|
25
|
+
// pause checkbox is checked
|
26
|
+
function update_logbox() {
|
27
|
+
if (pause_ckbx.checked) { return; }
|
28
|
+
logbox.value = log_data;
|
29
|
+
logbox.scrollTop = logbox.scrollHeight;
|
30
|
+
}
|
31
|
+
|
32
|
+
// start the stream
|
33
|
+
function start_log_stream() {
|
34
|
+
// always update the log level
|
35
|
+
get_current_log_level();
|
36
|
+
// return if already started
|
37
|
+
if (typeof(log_source) != "undefined") { return; }
|
38
|
+
|
39
|
+
logbox = document.getElementById("logbox");
|
40
|
+
pause_ckbx = document.getElementById("pause_log");
|
41
|
+
|
42
|
+
log_data = logbox.value;
|
43
|
+
log_source = new EventSource(log_stream_url);
|
44
|
+
|
45
|
+
// add incoming lines of data from the server
|
46
|
+
// to the in-memory cache
|
47
|
+
log_source.onmessage = function (event) {
|
48
|
+
var msg = event.data;
|
49
|
+
log_data = log_data + msg + "\n";
|
50
|
+
update_logbox();
|
51
|
+
var match = new_log_level_regex.exec(msg);
|
52
|
+
if (match) { update_log_level_selector(match[1]); }
|
53
|
+
};
|
54
|
+
|
55
|
+
// close the streams when client pages are closed.
|
56
|
+
// The server will see that the streams are closed
|
57
|
+
// and will remove the registrations as needed.
|
58
|
+
window.onbeforeunload = function() {
|
59
|
+
log_source.close();
|
60
|
+
return null;
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
// update the selector with the current log level from the server
|
65
|
+
function get_current_log_level() {
|
66
|
+
var url = '/current_log_level';
|
67
|
+
var xhttp = new XMLHttpRequest();
|
68
|
+
|
69
|
+
xhttp.onreadystatechange = function() {
|
70
|
+
if (this.readyState == 4 && this.status == 200) {
|
71
|
+
update_log_level_selector(xhttp.responseText);
|
72
|
+
}
|
73
|
+
};
|
74
|
+
|
75
|
+
xhttp.open("GET", url, true);
|
76
|
+
xhttp.send();
|
77
|
+
}
|
78
|
+
|
79
|
+
function update_log_level_selector(new_level) {
|
80
|
+
var new_idx;
|
81
|
+
switch(new_level) {
|
82
|
+
case 'fatal':
|
83
|
+
new_idx = 0;
|
84
|
+
break;
|
85
|
+
case 'error':
|
86
|
+
new_idx = 1;
|
87
|
+
break;
|
88
|
+
case 'warn':
|
89
|
+
new_idx = 2;
|
90
|
+
break;
|
91
|
+
case 'info':
|
92
|
+
new_idx = 3;
|
93
|
+
break;
|
94
|
+
case 'debug':
|
95
|
+
new_idx = 4;
|
96
|
+
break;
|
97
|
+
default:
|
98
|
+
new_idx = null;
|
99
|
+
}
|
100
|
+
document.getElementById("log_level_select").selectedIndex = new_idx;
|
101
|
+
}
|
data/lib/chook/server/routes.rb
CHANGED
@@ -23,5 +23,33 @@
|
|
23
23
|
###
|
24
24
|
###
|
25
25
|
|
26
|
+
module Chook
|
27
|
+
|
28
|
+
# the server
|
29
|
+
class Server < Sinatra::Base
|
30
|
+
|
31
|
+
HANDLE_EVENT_ROUTE = '/handle_webhook_event'.freeze
|
32
|
+
|
33
|
+
# before do
|
34
|
+
# break if request.path_info == Chook::Server::HANDLE_EVENT_ROUTE
|
35
|
+
# # break if request.path_info == '/' && session[:authed_admin]
|
36
|
+
# # redirect '/' unless session[:authed_admin]
|
37
|
+
# end
|
38
|
+
|
39
|
+
# log errors in production (in dev, they go to stdout and the browser)
|
40
|
+
error do
|
41
|
+
logger.error "ERROR: #{env['sinatra.error'].message}"
|
42
|
+
env['sinatra.error'].backtrace.each { |l| logger.error "..#{l}" }
|
43
|
+
500
|
44
|
+
end
|
45
|
+
|
46
|
+
end # server
|
47
|
+
|
48
|
+
end # Chook
|
49
|
+
|
26
50
|
require 'chook/server/routes/home'
|
27
51
|
require 'chook/server/routes/handle_webhook_event'
|
52
|
+
require 'chook/server/routes/handle_by_name'
|
53
|
+
require 'chook/server/routes/handlers'
|
54
|
+
require 'chook/server/routes/login_logout'
|
55
|
+
require 'chook/server/routes/log'
|
@@ -0,0 +1,65 @@
|
|
1
|
+
### Copyright 2017 Pixar
|
2
|
+
|
3
|
+
###
|
4
|
+
### Licensed under the Apache License, Version 2.0 (the "Apache License")
|
5
|
+
### with the following modification; you may not use this file except in
|
6
|
+
### compliance with the Apache License and the following modification to it:
|
7
|
+
### Section 6. Trademarks. is deleted and replaced with:
|
8
|
+
###
|
9
|
+
### 6. Trademarks. This License does not grant permission to use the trade
|
10
|
+
### names, trademarks, service marks, or product names of the Licensor
|
11
|
+
### and its affiliates, except as required to comply with Section 4(c) of
|
12
|
+
### the License and to reproduce the content of the NOTICE file.
|
13
|
+
###
|
14
|
+
### You may obtain a copy of the Apache License at
|
15
|
+
###
|
16
|
+
### http://www.apache.org/licenses/LICENSE-2.0
|
17
|
+
###
|
18
|
+
### Unless required by applicable law or agreed to in writing, software
|
19
|
+
### distributed under the Apache License with the above modification is
|
20
|
+
### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
21
|
+
### KIND, either express or implied. See the Apache License for the specific
|
22
|
+
### language governing permissions and limitations under the Apache License.
|
23
|
+
###
|
24
|
+
###
|
25
|
+
|
26
|
+
module Chook
|
27
|
+
|
28
|
+
# see server.rb
|
29
|
+
class Server < Sinatra::Base
|
30
|
+
|
31
|
+
post '/handler/:handler_name' do
|
32
|
+
# enforce http basic auth if needed
|
33
|
+
protect_via_basic_auth!
|
34
|
+
|
35
|
+
# rewind to ensure read-pointer is at the start
|
36
|
+
request.body.rewind #
|
37
|
+
raw_json = request.body.read
|
38
|
+
|
39
|
+
event = Chook::HandledEvent.parse_event raw_json
|
40
|
+
|
41
|
+
if event.nil?
|
42
|
+
logger.error "Empty JSON from #{request.ip}"
|
43
|
+
result = 400
|
44
|
+
else
|
45
|
+
|
46
|
+
event.logger.debug "START From #{request.ip}, WebHook '#{event.webhook_name}' (id: #{event.webhook_id})"
|
47
|
+
event.logger.debug "Thread id: #{Thread.current.object_id}; JSON: #{raw_json}"
|
48
|
+
|
49
|
+
result = event.handle_by_name params[:handler_name]
|
50
|
+
|
51
|
+
event.logger.debug "END #{result}"
|
52
|
+
end
|
53
|
+
|
54
|
+
# this route shouldn't have a session expiration
|
55
|
+
# And when it does, the date format is wrong, and the
|
56
|
+
# JAMFSoftwareServerLog complains about it for every
|
57
|
+
# webhook sent.
|
58
|
+
env['rack.session.options'].delete :expire_after
|
59
|
+
|
60
|
+
result
|
61
|
+
end # post
|
62
|
+
|
63
|
+
end # class
|
64
|
+
|
65
|
+
end # module
|