chook 1.0.1.b1 → 1.1.5b1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES.md +56 -0
  3. data/README.md +397 -145
  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 +252 -99
  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/subject/test_subject.rb +2 -2
  45. data/lib/chook/version.rb +1 -1
  46. metadata +78 -17
@@ -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