chook 1.0.1.b2 → 1.1.0

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.
Files changed (38) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES.md +21 -0
  3. data/README.md +243 -36
  4. data/bin/chook-server +29 -1
  5. data/data/chook.conf.example +104 -0
  6. data/data/sample_handlers/RestAPIOperation.rb +12 -8
  7. data/data/sample_handlers/SmartGroupComputerMembershipChange.rb +3 -6
  8. data/data/sample_jsons/SmartGroupComputerMembershipChange.json +3 -1
  9. data/data/sample_jsons/SmartGroupMobileDeviceMembershipChange.json +3 -1
  10. data/lib/chook/configuration.rb +20 -8
  11. data/lib/chook/event/handled_event.rb +15 -12
  12. data/lib/chook/event/handled_event/handlers.rb +136 -83
  13. data/lib/chook/event/handled_event_logger.rb +86 -0
  14. data/lib/chook/event_handling.rb +1 -0
  15. data/lib/chook/foundation.rb +2 -0
  16. data/lib/chook/procs.rb +17 -1
  17. data/lib/chook/server.rb +71 -74
  18. data/lib/chook/server/log.rb +215 -0
  19. data/lib/chook/server/public/css/chook.css +125 -0
  20. data/lib/chook/server/public/imgs/ChookLogoAlMcWhiggin.png +0 -0
  21. data/lib/chook/server/public/js/chook.js +127 -0
  22. data/lib/chook/server/public/js/logstream.js +101 -0
  23. data/lib/chook/server/routes.rb +45 -0
  24. data/lib/chook/server/routes/handle_webhook_event.rb +22 -3
  25. data/lib/chook/server/routes/handlers.rb +52 -0
  26. data/lib/chook/server/routes/home.rb +34 -1
  27. data/lib/chook/server/routes/log.rb +106 -0
  28. data/lib/chook/server/views/admin.haml +11 -0
  29. data/lib/chook/server/views/bak.haml +48 -0
  30. data/lib/chook/server/views/config.haml +15 -0
  31. data/lib/chook/server/views/handlers.haml +48 -0
  32. data/lib/chook/server/views/layout.haml +39 -0
  33. data/lib/chook/server/views/logstream.haml +32 -0
  34. data/lib/chook/server/views/sketch_admin +44 -0
  35. data/lib/chook/subject.rb +1 -2
  36. data/lib/chook/subject/smart_group.rb +6 -0
  37. data/lib/chook/version.rb +1 -1
  38. metadata +73 -18
@@ -0,0 +1,125 @@
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
+ .def_pronunciation {
26
+ font-style: italic;
27
+ font-size: 1.2em;
28
+ }
29
+
30
+ .def_part_of_speech {
31
+ font-weight: bold;
32
+ }
33
+
34
+ .def_dialect {
35
+ font-style: italic;
36
+ }
37
+
38
+ .def_definition {
39
+ font-size: 1.2em;
40
+ }
41
+
42
+ /* ******* Section Label Areas ******* */
43
+
44
+ .section_label {
45
+ padding-bottom: 5px;
46
+ }
47
+
48
+ /* ******* Live Log Section ******* */
49
+
50
+ #hide_log_btn {
51
+ display: none;
52
+ }
53
+
54
+ #logbox_div {
55
+ display: none;
56
+ }
57
+
58
+ #logbox_btns {
59
+ padding-bottom: 5px;
60
+ }
61
+
62
+ #logbox {
63
+ width: 100%;
64
+ }
65
+
66
+ /* ******* Handlers Section ******* */
67
+
68
+ #hide_handlers_btn {
69
+ display: none;
70
+ }
71
+
72
+ #handlers_div {
73
+ display: none;
74
+ }
75
+
76
+ #handlers_table {
77
+ width: 100%;
78
+ border: 1px solid black;
79
+ border-collapse: collapse;
80
+ margin-top: 5px;
81
+ padding-bottom: 5px;
82
+ }
83
+
84
+ #handlers_table_header_row {
85
+ text-align: left;
86
+ border: 1px solid black;
87
+ border-collapse: collapse;
88
+ background-color: LightGrey;
89
+ padding-top: 4px;
90
+ padding-left: 3px;
91
+ }
92
+
93
+ .handlers_table_cell {
94
+ text-align: left;
95
+ border: 1px solid black;
96
+ border-collapse: collapse;
97
+ padding-top: 4px;
98
+ padding-left: 3px;
99
+ }
100
+
101
+ #handler_viewer_div {
102
+ display: none;
103
+ padding-top: 15px;
104
+ }
105
+
106
+ #handler_viewer {
107
+ width: 95%;
108
+ border: 3px solid black;
109
+ padding-top: 5px;
110
+ }
111
+
112
+ /* ******* Handlers Section ******* */
113
+
114
+ #hide_config_btn {
115
+ display: none;
116
+ }
117
+
118
+ #config_div {
119
+ display: none;
120
+ }
121
+
122
+ #config_viewer_div {
123
+ margin-top: 5px;
124
+ padding-bottom: 5px;
125
+ }
@@ -0,0 +1,127 @@
1
+
2
+ // show the logbox
3
+ function view_log() {
4
+ document.getElementById("pause_log").checked = false
5
+ document.getElementById("logbox_div").style.display = 'block';
6
+ document.getElementById("view_log_btn").style.display = 'none';
7
+ document.getElementById("hide_log_btn").style.display = 'inline';
8
+ start_log_stream();
9
+ update_logbox();
10
+ }
11
+
12
+ // hide the logbox
13
+ function hide_log() {
14
+ document.getElementById("logbox_div").style.display = 'none';
15
+ document.getElementById("view_log_btn").style.display = 'inline';
16
+ document.getElementById("hide_log_btn").style.display = 'none';
17
+ document.getElementById("pause_log").checked = true;
18
+ }
19
+
20
+ // clear the log stream
21
+ function clear_log(){
22
+ document.getElementById("logbox").value = '';
23
+ log_source = '';
24
+ }
25
+
26
+ // change the log level
27
+ function change_log_level() {
28
+ var new_level = document.getElementById("log_level_select").value
29
+ var url = "/set_log_level/" + new_level
30
+ var xhttp = new XMLHttpRequest();
31
+ xhttp.open("PUT", url, true);
32
+ xhttp.send();
33
+ }
34
+
35
+ // reload the handlers
36
+ function reload_handlers() {
37
+ var url = '/reload_handlers';
38
+ var xhttp = new XMLHttpRequest();
39
+ xhttp.onreadystatechange = function() {
40
+ if (this.readyState == 4 && this.status == 200) {
41
+ now = new Date().toLocaleString();
42
+ document.getElementById("reloaded_notification").innerHTML = 'Reloaded at ' + now;
43
+ } else {
44
+ document.getElementById("reloaded_notification").innerHTML = 'Reload Failed.';
45
+ }
46
+ };
47
+ xhttp.open("GET", url, true);
48
+ xhttp.send();
49
+ }
50
+
51
+ // show the handler area
52
+ function view_handlers() {
53
+ document.getElementById("handlers_div").style.display = 'block';
54
+ document.getElementById("view_handlers_btn").style.display = 'none';
55
+ document.getElementById("hide_handlers_btn").style.display = 'inline';
56
+ }
57
+
58
+ // hide the handler area
59
+ function hide_handlers() {
60
+ document.getElementById("handlers_div").style.display = 'none';
61
+ document.getElementById("view_handlers_btn").style.display = 'inline';
62
+ document.getElementById("hide_handlers_btn").style.display = 'none';
63
+ }
64
+
65
+ // hide the handler editor
66
+ function hide_handler_viewer() {
67
+ document.getElementById("handler_viewer_div").style.display = 'none';
68
+ }
69
+
70
+ // show the handler editor with the selected handler code
71
+ // handler = the path to the hander fle.
72
+ function edit_handler(handler, type) {
73
+ var code = '';
74
+ var editing_filename = handler;
75
+
76
+ // new handler
77
+ if (handler == 'new_handler') {
78
+ editing_filename = new_handler_filename();
79
+ if (editing_filename == 'Name Already Taken') {
80
+ code = editing_filename;
81
+ }
82
+ document.getElementById("handler_viewer").value = code;
83
+
84
+ if (document.getElementById("add_handler_external_radio").checked) {
85
+ type = 'external';
86
+ } else {
87
+ type = 'internal';
88
+ }
89
+
90
+ // existing handler
91
+ } else {
92
+ fetch_handler_code(handler) ;
93
+ }
94
+ var now_editing = editing_filename + ' (' + type + ')'
95
+ document.getElementById("currently_viewing_filename").innerHTML = now_editing;
96
+ document.getElementById("handler_viewer_div").style.display = 'block';
97
+ }
98
+
99
+ // get the code for an existing handler into the editor
100
+ function fetch_handler_code(handler) {
101
+ var editor = document.getElementById("handler_viewer");
102
+ var url = '/handler_code/' + handler
103
+ var xhttp = new XMLHttpRequest();
104
+ xhttp.onreadystatechange = function() {
105
+ if (this.readyState == 4 && this.status == 200) {
106
+ editor.value = xhttp.responseText;
107
+ } else {
108
+ editor.value = 'ERROR: File Not Found';
109
+ }
110
+ };
111
+ xhttp.open("GET", url, true);
112
+ xhttp.send();
113
+ }
114
+
115
+ // show the config area
116
+ function view_config() {
117
+ document.getElementById("config_div").style.display = 'block';
118
+ document.getElementById("view_config_btn").style.display = 'none';
119
+ document.getElementById("hide_config_btn").style.display = 'inline';
120
+ }
121
+
122
+ // hide the config area
123
+ function hide_config() {
124
+ document.getElementById("config_div").style.display = 'none';
125
+ document.getElementById("view_config_btn").style.display = 'inline';
126
+ document.getElementById("hide_config_btn").style.display = 'none';
127
+ }
@@ -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
+ }
@@ -23,5 +23,50 @@
23
23
  ###
24
24
  ###
25
25
 
26
+ module Chook
27
+
28
+ # the server
29
+ class Server < Sinatra::Base
30
+
31
+ # These two helpers let us decude which routes need
32
+ # http basic auth and which don't
33
+ #
34
+ # To protect a route, put `protected!` as the
35
+ # first line of code in the route.
36
+ #
37
+ # See http://sinatrarb.com/faq.html#auth
38
+ #
39
+ helpers do
40
+ def protected!
41
+ # don't protect if user isn't defined
42
+ return unless Chook.config.webhooks_user
43
+
44
+ return if authorized?
45
+ headers['WWW-Authenticate'] = 'Basic realm="Restricted Area"'
46
+ halt 401, "Not authorized\n"
47
+ end
48
+
49
+ def authorized?
50
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
51
+ @auth.provided? && \
52
+ @auth.basic? && \
53
+ @auth.credentials && \
54
+ @auth.credentials == [Chook.config.webhooks_user, Chook::Server.webhooks_user_pw]
55
+ end
56
+ end
57
+
58
+ # log errors in production (in dev, they go to stdout and the browser)
59
+ error do
60
+ logger.error "ERROR: #{env['sinatra.error'].message}"
61
+ env['sinatra.error'].backtrace.each { |l| logger.error "..#{l}" }
62
+ 500
63
+ end
64
+
65
+ end # server
66
+
67
+ end # Chook
68
+
26
69
  require 'chook/server/routes/home'
27
70
  require 'chook/server/routes/handle_webhook_event'
71
+ require 'chook/server/routes/handlers'
72
+ require 'chook/server/routes/log'
@@ -29,9 +29,28 @@ module Chook
29
29
  class Server < Sinatra::Base
30
30
 
31
31
  post '/handle_webhook_event' do
32
- request.body.rewind # in case someone already read it
33
- event = Chook::HandledEvent.parse_event request.body.read
34
- event.handle
32
+ # enforce http basic auth if needed
33
+ protected!
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
+ if event.nil?
41
+ logger.error "Empty JSON from #{request.ip}"
42
+ result = 400
43
+ else
44
+
45
+ event.logger.info "START From #{request.ip}, WebHook '#{event.webhook_name}' (id: #{event.webhook_id})"
46
+
47
+ event.logger.debug "JSON: #{raw_json}"
48
+
49
+ result = event.handle
50
+
51
+ event.logger.info "END #{result}"
52
+ end
53
+ result
35
54
  end # post
36
55
 
37
56
  end # class
@@ -0,0 +1,52 @@
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
+ # reload the handlers
32
+ get '/reload_handlers' do
33
+ protected!
34
+ logger.info 'Reloading handlers'
35
+ Chook::HandledEvent::Handlers.load_handlers reload: true
36
+ 'Handlers reloaded'
37
+ end # get /
38
+
39
+ # used by javascript to fetch the content of a handler
40
+ get '/handler_code/:file' do
41
+ protected!
42
+ file = Chook.config.handler_dir + params[:file]
43
+ if file.file?
44
+ body file.read
45
+ else
46
+ 404
47
+ end
48
+ end
49
+
50
+ end # class
51
+
52
+ end # module