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.
Files changed (45) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES.md +56 -0
  3. data/README.md +363 -127
  4. data/bin/chook-server +31 -1
  5. data/data/chook.conf.example +183 -0
  6. data/data/com.pixar.chook-server.plist +20 -0
  7. data/data/sample_handlers/RestAPIOperation.rb +11 -11
  8. data/data/sample_handlers/SmartGroupComputerMembershipChange.rb +3 -6
  9. data/data/sample_jsons/SmartGroupComputerMembershipChange.json +3 -1
  10. data/data/sample_jsons/SmartGroupMobileDeviceMembershipChange.json +3 -1
  11. data/lib/chook/configuration.rb +27 -8
  12. data/lib/chook/event.rb +6 -1
  13. data/lib/chook/event/handled_event.rb +36 -9
  14. data/lib/chook/event/handled_event/handlers.rb +260 -98
  15. data/lib/chook/event/handled_event_logger.rb +86 -0
  16. data/lib/chook/event_handling.rb +1 -0
  17. data/lib/chook/foundation.rb +3 -0
  18. data/lib/chook/procs.rb +17 -1
  19. data/lib/chook/server.rb +73 -72
  20. data/lib/chook/server/auth.rb +164 -0
  21. data/lib/chook/server/log.rb +215 -0
  22. data/lib/chook/server/public/css/chook.css +133 -0
  23. data/lib/chook/server/public/imgs/ChookLogoAlMcWhiggin.png +0 -0
  24. data/lib/chook/server/public/js/chook.js +126 -0
  25. data/lib/chook/server/public/js/logstream.js +101 -0
  26. data/lib/chook/server/routes.rb +28 -0
  27. data/lib/chook/server/routes/handle_by_name.rb +65 -0
  28. data/lib/chook/server/routes/handle_webhook_event.rb +27 -3
  29. data/lib/chook/server/routes/handlers.rb +52 -0
  30. data/lib/chook/server/routes/home.rb +48 -1
  31. data/lib/chook/server/routes/log.rb +105 -0
  32. data/lib/chook/server/routes/login_logout.rb +48 -0
  33. data/lib/chook/server/views/admin.haml +11 -0
  34. data/lib/chook/server/views/bak.haml +48 -0
  35. data/lib/chook/server/views/config.haml +15 -0
  36. data/lib/chook/server/views/handlers.haml +63 -0
  37. data/lib/chook/server/views/layout.haml +64 -0
  38. data/lib/chook/server/views/logstream.haml +33 -0
  39. data/lib/chook/server/views/sketch_admin +44 -0
  40. data/lib/chook/subject.rb +13 -2
  41. data/lib/chook/subject/dep_device.rb +81 -0
  42. data/lib/chook/subject/policy_finished.rb +43 -0
  43. data/lib/chook/subject/smart_group.rb +6 -0
  44. data/lib/chook/version.rb +1 -1
  45. metadata +79 -19
@@ -29,9 +29,33 @@ 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
+ 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
+ if event.nil?
41
+ logger.error "Empty JSON from #{request.ip}"
42
+ result = 400
43
+ else
44
+
45
+ event.logger.debug "START From #{request.ip}, WebHook '#{event.webhook_name}' (id: #{event.webhook_id})"
46
+ event.logger.debug "Thread id: #{Thread.current.object_id}; JSON: #{raw_json}"
47
+
48
+ result = event.handle
49
+
50
+ event.logger.debug "END #{result}"
51
+ end
52
+
53
+ # this route shouldn't have a session expiration
54
+ # And when it does, the date format is wrong, and the
55
+ # JAMFSoftwareServerLog complains about it for every
56
+ # webhook sent.
57
+ env['rack.session.options'].delete :expire_after
58
+ result
35
59
  end # post
36
60
 
37
61
  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
+ logger.info 'Reloading handlers'
34
+ Chook::HandledEvent::Handlers.load_handlers reload: true
35
+ 'Handlers reloaded'
36
+ end # get /
37
+
38
+ # used by javascript to fetch the content of a handler file
39
+ get '/handler_code' do
40
+ file = Pathname.new params[:filepath]
41
+
42
+ # only if its a known handler path
43
+ if Chook::HandledEvent::Handlers.all_handler_paths.include?(file) && file.file?
44
+ body file.read
45
+ else
46
+ 404
47
+ end
48
+ end
49
+
50
+ end # class
51
+
52
+ end # module
@@ -29,7 +29,54 @@ module Chook
29
29
  class Server < Sinatra::Base
30
30
 
31
31
  get '/' do
32
- body "Hello, this is Chook, a Jamf Pro WebHook handling service from Pixar Animation Studios!\n"
32
+
33
+ # a list of current handlers for the admin page
34
+ @handlers_for_admin_page = []
35
+
36
+ Chook::HandledEvent::Handlers.handlers.keys.sort.each do |eventname|
37
+ Chook::HandledEvent::Handlers.handlers[eventname].each do |handler|
38
+ if handler.is_a? Pathname
39
+ file = handler
40
+ type = :external
41
+ else
42
+ file = handler.handler_file
43
+ type = :internal
44
+ end # if else
45
+ @handlers_for_admin_page << { event: eventname, file: file, type: type }
46
+ end # handlers each
47
+ end # Handlers.handlers.each
48
+
49
+ # a list of current named handlers for the admin page
50
+ @named_handlers_for_admin_page = []
51
+
52
+ Chook::HandledEvent::Handlers.named_handlers.each do |name, handler|
53
+ if handler.is_a? Pathname
54
+ file = handler
55
+ type = :external
56
+ else
57
+ file = handler.handler_file
58
+ type = :internal
59
+ end # if else
60
+ @named_handlers_for_admin_page << { file: file, type: type }
61
+ end # handlers each
62
+
63
+
64
+ # the current config, for the admin page
65
+ @config_text =
66
+ if Chook::Configuration::DEFAULT_CONF_FILE.file?
67
+ @config_src = Chook::Configuration::DEFAULT_CONF_FILE.to_s
68
+ Chook::Configuration::DEFAULT_CONF_FILE.read
69
+
70
+ elsif Chook::Configuration::SAMPLE_CONF_FILE.file?
71
+ @config_src = "Using default values, showing sample config file at #{Chook::Configuration::SAMPLE_CONF_FILE}"
72
+ Chook::Configuration::SAMPLE_CONF_FILE.read
73
+
74
+ else
75
+ @config_src = "No #{Chook::Configuration::DEFAULT_CONF_FILE} or sample config file found."
76
+ @config_src
77
+ end
78
+
79
+ haml :admin
33
80
  end # get /
34
81
 
35
82
  end # class
@@ -0,0 +1,105 @@
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
+ # External Handlers can use this route to make log entries.
32
+ #
33
+ # The request body must be a JSON object (Hash) wth 2 keys 'level' and 'message'
34
+ # where both values are strings
35
+ #
36
+ # Here's an example with curl, split to multi-line for clarity:
37
+ #
38
+ # curl -H "Content-Type: application/json" \
39
+ # -X POST \
40
+ # --data '{"level":"debug", "message":"It Worked"}' \
41
+ # https://user:passwd@chookserver.myorg.org:443/log
42
+ #
43
+ post '/log' do
44
+ protect_via_basic_auth!
45
+
46
+ request.body.rewind # in case someone already read it
47
+ raw = request.body.read
48
+
49
+ begin
50
+ logentry = JSON.parse raw, symbolize_names: true
51
+ raise if logentry[:level].to_s.empty? || logentry[:message].to_s.empty?
52
+ rescue
53
+ Chook::Server::Log.logger.error "Malformed log entry JSON from #{request.ip}: #{raw}"
54
+ halt 409, "Malformed log entry JSON: #{raw}"
55
+ end
56
+
57
+ level = logentry[:level].to_sym
58
+ level = :unknown unless Chook::Server::Log::LOG_LEVELS.key? level
59
+ Chook::Server::Log.logger.send level, "ExternalEntry: #{logentry[:message]}"
60
+
61
+ { result: 'logged', level: level }.to_json
62
+ end # post /
63
+
64
+ # AJAXy access to a log stream
65
+ # When an admin displays the log on the chook admin/home page,
66
+ # the page's javascript starts the stream as an EventSource
67
+ # from this url.
68
+ #
69
+ # The innards are taken almost verbatim from the Sinatra README
70
+ # docs.
71
+ #
72
+ # See also logstream.js and views/admin.haml
73
+ #
74
+ #
75
+ get '/subscribe_to_log_stream', provides: 'text/event-stream' do
76
+ content_type 'text/event-stream'
77
+ cache_control 'no-cache'
78
+
79
+ # register a client's interest in server events
80
+ stream(:keep_open) do |outbound_stream|
81
+ # add this connection to the array of streams
82
+ Chook::Server::Log.log_streams[outbound_stream] = request.ip
83
+ logger.debug "Added log stream for #{request.ip}"
84
+ # purge dead connections
85
+ Chook::Server::Log.clean_log_streams
86
+ end # stream
87
+ end
88
+
89
+ # set the log level via the admin page.
90
+ put '/set_log_level/:level' do
91
+ level = params[:level].to_sym
92
+ level = :unknown unless Chook::Server::Log::LOG_LEVELS.key? level
93
+ Chook.logger.level = level
94
+ Chook.logger.unknown "Log level changed, now: #{level}"
95
+ { result: 'level changed', level: level }.to_json
96
+ end
97
+
98
+ # get the log level via the admin page.
99
+ get '/current_log_level' do
100
+ Chook::Server::Log::LOG_LEVELS.invert[Chook.logger.level].to_s
101
+ end
102
+
103
+ end # class
104
+
105
+ end # module
@@ -0,0 +1,48 @@
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 '/logout' do
33
+ session[:authed_admin] = nil
34
+ session[:auth_failed] = nil
35
+ redirect '/'
36
+ end # get /
37
+
38
+ # reload the handlers
39
+ post '/login' do
40
+ Chook.logger.debug "Attempting to log in #{params[:username]}"
41
+ session[:auth_failed] = !authenticate_admin(params[:username], params[:password])
42
+ redirect '/'
43
+ end # get /
44
+
45
+
46
+ end # class
47
+
48
+ end # module
@@ -0,0 +1,11 @@
1
+ %hr/
2
+ %hr/
3
+ = haml :logstream
4
+
5
+ %hr/
6
+ %hr/
7
+ = haml :handlers
8
+
9
+ %hr/
10
+ %hr/
11
+ = haml :config
@@ -0,0 +1,48 @@
1
+ %hr/
2
+ .section_label#log_label
3
+ The Live Chook Log
4
+ &nbsp;&nbsp;&nbsp;&nbsp;
5
+ %button#view_log_btn{ type: 'button', onClick: 'view_log();', title: 'view the live Chook log' }
6
+ View
7
+ %button#hide_log_btn{ type: 'button', onClick: 'hide_log();', title: 'hide the live Chook log' }
8
+ Hide
9
+ &nbsp;&nbsp;&nbsp;&nbsp; Log Level:
10
+ %select#log_level_select{ onchange: 'change_log_level();' }
11
+ %option{ value: 'fatal', selected: Chook.logger.level == Logger::FATAL } fatal
12
+ %option{ value: 'error', selected: Chook.logger.level == Logger::ERROR } error
13
+ %option{ value: 'warn', selected: Chook.logger.level == Logger::WARN } warn
14
+ %option{ value: 'info', selected: Chook.logger.level == Logger::INFO } info
15
+ %option{ value: 'debug', selected: Chook.logger.level == Logger::DEBUG } debug
16
+
17
+ #logbox_div
18
+ #logbox_btns
19
+ %input#pause_log{ type: 'checkbox', checked: false, onclick: 'update_logbox();' }
20
+ Pause
21
+ &nbsp;&nbsp;&nbsp;&nbsp;
22
+ %button#clear_log_btn{ type: 'button', onClick: 'clear_log();', title: 'clear the live Chook log' }
23
+ Clear
24
+
25
+ // Log Level: [popup list] (set)
26
+ %textarea.monospaced#logbox{ readonly: true, rows: 20, cols: 150 }
27
+
28
+ %hr/
29
+ .section_label#handlers_label
30
+ Current Webhook Handlers (#{@handlers_for_admin_page.size})
31
+ &nbsp;&nbsp;&nbsp;&nbsp;
32
+
33
+ %button#view_handlers_btn{ type: 'button', onClick: 'view_handlers();', title: 'view the handler list' }
34
+ View
35
+ %button#hide_handlers_btn{ type: 'button', onClick: 'hide_handlers();', title: 'hide the handler list' }
36
+ Hide
37
+ &nbsp;&nbsp;&nbsp;&nbsp;
38
+
39
+ Handler Directory:
40
+ %span.monospaced
41
+ = Chook.config.handler_dir.to_s
42
+
43
+ #handlers_div
44
+ %table#handlers_table
45
+ %tr
46
+ %th Event
47
+ %th Handler Type
48
+ %th File Name
@@ -0,0 +1,15 @@
1
+ .section_label#config_label
2
+ %button#view_config_btn{ type: 'button', onClick: 'view_config();', title: 'view the config file' }
3
+ View
4
+ %button#hide_config_btn{ type: 'button', onClick: 'hide_config();', title: 'hide the config file' }
5
+ Hide
6
+ &nbsp;&nbsp;&nbsp;&nbsp;
7
+
8
+ Configuration
9
+
10
+ #config_div
11
+ Config file:
12
+ %span.monospaced= @config_src
13
+
14
+ #config_viewer_div
15
+ %textarea.monospaced#config_box{ readonly: true, rows: 20, cols: 150 }= @config_text
@@ -0,0 +1,63 @@
1
+ .section_label#handlers_label
2
+ %button#view_handlers_btn{ type: 'button', onClick: 'view_handlers();', title: 'view the handler list' }
3
+ View
4
+ %button#hide_handlers_btn{ type: 'button', onClick: 'hide_handlers();', title: 'hide the handler list' }
5
+ Hide
6
+ &nbsp;&nbsp;&nbsp;&nbsp;
7
+
8
+ Current Webhook Handlers (#{@handlers_for_admin_page.size + @named_handlers_for_admin_page.size})
9
+
10
+ %button#reload_all_handlers_btn{ type: 'button', onClick: 'reload_handlers();', title: 'reload all handlers' }
11
+ Reload All
12
+ &nbsp;&nbsp;
13
+ %span#reloaded_notification
14
+
15
+ #handlers_div
16
+ General Handler Directory:
17
+ %span.monospaced= Chook.config.handler_dir.to_s
18
+ &nbsp;&nbsp;&nbsp;&nbsp;
19
+
20
+ %table#handlers_table
21
+ %tr#handlers_table_header_row
22
+ %th.handlers_table_cell File Name
23
+ %th.handlers_table_cell{ width: '10%' } Handler Type
24
+ %th.handlers_table_cell Actions
25
+
26
+ - @handlers_for_admin_page.each do |hndlr_info|
27
+ %tr
28
+ %td.handlers_table_cell= hndlr_info[:file].basename.to_s
29
+ %td.handlers_table_cell= hndlr_info[:type].to_s
30
+ %td.handlers_table_cell
31
+ %button.edit_handler_btn{ type: 'button', onClick: "view_handler_code('#{hndlr_info[:file]}', '#{hndlr_info[:type]}');", title: 'View this handler' }
32
+ View
33
+ %br
34
+ %br
35
+ Named Handler Directory:
36
+ %span.monospaced= Chook.config.handler_dir.to_s + "/#{Chook::HandledEvent::Handlers::NAMED_HANDLER_SUBDIR}"
37
+ &nbsp;&nbsp;&nbsp;&nbsp;
38
+
39
+ %table#handlers_table
40
+ %tr#handlers_table_header_row
41
+ %th.handlers_table_cell File Name
42
+ %th.handlers_table_cell{ width: '10%' } Handler Type
43
+ %th.handlers_table_cell Actions
44
+
45
+ - @named_handlers_for_admin_page.each do |hndlr_info|
46
+ %tr
47
+ %td.handlers_table_cell= hndlr_info[:file].basename.to_s
48
+ %td.handlers_table_cell= hndlr_info[:type].to_s
49
+ %td.handlers_table_cell
50
+ %button.edit_handler_btn{ type: 'button', onClick: "view_handler_code('#{hndlr_info[:file]}', '#{hndlr_info[:type]}');", title: 'View this handler' }
51
+ View
52
+
53
+ #handler_viewer_div
54
+ %input#currently_viewing_handler_file{ name: 'currently_viewing_handler_file', type: :hidden }
55
+ %input#currently_editing_handler_type{ name: 'currently_editing_handler_type', type: :hidden }
56
+ #currently_viewing_handler_label
57
+ %button#hide_handler_viewer_btn{ type: 'button', onClick: 'hide_handler_viewer();', title: 'hide the handler editor' }
58
+ Hide
59
+ &nbsp;&nbsp;&nbsp;&nbsp;
60
+ %span.monospaced#currently_viewing_filename -nothing-
61
+
62
+
63
+ %textarea.monospaced#handler_viewer{ rows: 35 , readonly: true }
@@ -0,0 +1,64 @@
1
+ !!!
2
+ %html{ lang: 'en' }
3
+
4
+ %head
5
+
6
+ %meta{ charset: 'UTF-8' }
7
+ %title
8
+ Chook
9
+
10
+ / CSS
11
+ %link{ href: '/css/chook.css', rel: 'stylesheet' }
12
+
13
+ / JavaScript
14
+ %script{ type: 'text/javascript', language: 'javascript', src: '/js/chook.js' }
15
+ %script{ type: 'text/javascript', language: 'javascript', src: '/js/logstream.js' }
16
+
17
+ %body
18
+ / Top
19
+ #pageheader
20
+ %table{ width: '100%' }
21
+ %tr
22
+ %td{ valign: 'bottom', width: '120' }
23
+ #header_logo
24
+ %img{ alt: '', height: '120', src: '/imgs/ChookLogoAlMcWhiggin.png', width: '120' }
25
+ #header_version
26
+ v#{Chook::VERSION}
27
+ %td
28
+ #definition
29
+ %span.chook_title Chook
30
+ %br/
31
+ %span.def_pronunciation /tʃʊk/ (also chookie /ˈtʃʊki/ )
32
+ %br/
33
+ %span.def_part_of_speech noun
34
+ %span.def_dialect Australian/NZ informal
35
+ %br/
36
+ %span.def_definition a chicken or fowl
37
+ %td#serverstats{ valign: 'bottom' }
38
+ Server started: #{Chook::Server.starttime.strftime '%Y-%m-%d %H:%M:%S'}
39
+ %br/
40
+ (#{Chook::Server.uptime})
41
+
42
+ %hr/
43
+ - if Chook.config.admin_user
44
+ #login_logout_div
45
+
46
+ - if session[:authed_admin]
47
+ %a{ href: '/logout' }
48
+ %button#logout_btn{ type: 'button' } Log Out
49
+ = yield
50
+
51
+ - else
52
+ %form#login_form{ name: 'login_form', method: 'POST', action: '/login' }
53
+ - if Chook.config.admin_user == Chook::Server::Auth::USE_JAMF_ADMIN_USER
54
+ Jamf Pro
55
+ Username:
56
+ %input#username{ type: :text, name: 'username' }
57
+ Password:
58
+ %input#password{ type: :password, name: 'password' }
59
+ %input#login_btn{ type: :submit, value: 'Log In' }
60
+ - if session[:auth_failed]
61
+ %span#login_incorrect &nbsp;&nbsp;&nbsp; Login Incorrect!
62
+
63
+ - else
64
+ = yield