xolo-server 1.0.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.
- checksums.yaml +7 -0
- data/LICENSE.txt +177 -0
- data/README.md +7 -0
- data/bin/xoloserver +106 -0
- data/data/com.pixar.xoloserver.plist +29 -0
- data/data/uninstall-pkgs-by-id.zsh +103 -0
- data/lib/xolo/server/app.rb +133 -0
- data/lib/xolo/server/command_line.rb +216 -0
- data/lib/xolo/server/configuration.rb +739 -0
- data/lib/xolo/server/constants.rb +70 -0
- data/lib/xolo/server/helpers/auth.rb +257 -0
- data/lib/xolo/server/helpers/client_data.rb +415 -0
- data/lib/xolo/server/helpers/file_transfers.rb +265 -0
- data/lib/xolo/server/helpers/jamf_pro.rb +156 -0
- data/lib/xolo/server/helpers/log.rb +97 -0
- data/lib/xolo/server/helpers/maintenance.rb +401 -0
- data/lib/xolo/server/helpers/notification.rb +145 -0
- data/lib/xolo/server/helpers/pkg_signing.rb +141 -0
- data/lib/xolo/server/helpers/progress_streaming.rb +252 -0
- data/lib/xolo/server/helpers/title_editor.rb +92 -0
- data/lib/xolo/server/helpers/titles.rb +145 -0
- data/lib/xolo/server/helpers/versions.rb +160 -0
- data/lib/xolo/server/log.rb +286 -0
- data/lib/xolo/server/mixins/changelog.rb +315 -0
- data/lib/xolo/server/mixins/title_jamf_access.rb +1668 -0
- data/lib/xolo/server/mixins/title_ted_access.rb +519 -0
- data/lib/xolo/server/mixins/version_jamf_access.rb +1541 -0
- data/lib/xolo/server/mixins/version_ted_access.rb +373 -0
- data/lib/xolo/server/object_locks.rb +102 -0
- data/lib/xolo/server/routes/auth.rb +89 -0
- data/lib/xolo/server/routes/jamf_pro.rb +89 -0
- data/lib/xolo/server/routes/maint.rb +174 -0
- data/lib/xolo/server/routes/title_editor.rb +71 -0
- data/lib/xolo/server/routes/titles.rb +285 -0
- data/lib/xolo/server/routes/uploads.rb +93 -0
- data/lib/xolo/server/routes/versions.rb +261 -0
- data/lib/xolo/server/routes.rb +168 -0
- data/lib/xolo/server/title.rb +1143 -0
- data/lib/xolo/server/version.rb +902 -0
- data/lib/xolo/server.rb +205 -0
- data/lib/xolo-server.rb +8 -0
- metadata +243 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Copyright 2025 Pixar
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the terms set forth in the LICENSE.txt file available at
|
|
4
|
+
# at the root of this project.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
# frozen_string_literal: true
|
|
8
|
+
|
|
9
|
+
# main module
|
|
10
|
+
module Xolo
|
|
11
|
+
|
|
12
|
+
# Server Module
|
|
13
|
+
module Server
|
|
14
|
+
|
|
15
|
+
module Routes
|
|
16
|
+
|
|
17
|
+
module Maint
|
|
18
|
+
|
|
19
|
+
# This is how we 'mix in' modules to Sinatra servers:
|
|
20
|
+
# We make them extentions here with
|
|
21
|
+
# extend Sinatra::Extension (from sinatra-contrib)
|
|
22
|
+
# and then 'register' them in the server with
|
|
23
|
+
# register Xolo::Server::<Module>
|
|
24
|
+
# Doing it this way allows us to split the code into a logical
|
|
25
|
+
# file structure, without re-opening the Sinatra::Base server app,
|
|
26
|
+
extend Sinatra::Extension
|
|
27
|
+
|
|
28
|
+
# when this module is included
|
|
29
|
+
def self.included(includer)
|
|
30
|
+
Xolo.verbose_include includer, self
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# when this module is extended
|
|
34
|
+
def self.extended(extender)
|
|
35
|
+
Xolo.verbose_extend extender, self
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Threads
|
|
39
|
+
##########
|
|
40
|
+
get '/maint/threads' do
|
|
41
|
+
body Xolo::Server.thread_info
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# State
|
|
45
|
+
##########
|
|
46
|
+
get '/maint/state' do
|
|
47
|
+
require 'concurrent/version'
|
|
48
|
+
uptime_secs = (Time.now - Xolo::Server.start_time).to_i
|
|
49
|
+
|
|
50
|
+
state = {
|
|
51
|
+
start_time: Xolo::Server.start_time,
|
|
52
|
+
uptime: uptime_secs.pix_humanize_secs,
|
|
53
|
+
uptime_secs: uptime_secs,
|
|
54
|
+
app_env: Xolo::Server.app_env,
|
|
55
|
+
data_dir: Xolo::Server::DATA_DIR,
|
|
56
|
+
log_file: Xolo::Server::Log::LOG_FILE,
|
|
57
|
+
log_level: Xolo::Server::Log::LEVELS[Xolo::Server.logger.level],
|
|
58
|
+
ruby_version: RUBY_VERSION,
|
|
59
|
+
gems: {
|
|
60
|
+
xolo_version: Xolo::VERSION,
|
|
61
|
+
ruby_jss_version: Jamf::VERSION,
|
|
62
|
+
windoo_version: Windoo::VERSION,
|
|
63
|
+
sinatra_version: Sinatra::VERSION,
|
|
64
|
+
thin_version: Thin::VERSION::STRING,
|
|
65
|
+
concurrent_ruby_version: Concurrent::VERSION,
|
|
66
|
+
faraday_version: Faraday::VERSION
|
|
67
|
+
},
|
|
68
|
+
config: Xolo::Server.config.to_h_private
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if params[:extended]
|
|
72
|
+
state[:gem_path] = Gem.paths.path
|
|
73
|
+
state[:load_path] = $LOAD_PATH
|
|
74
|
+
state[:object_locks] = Xolo::Server.object_locks
|
|
75
|
+
state[:pkg_deletion_pool] = Xolo::Server::Version.pkg_deletion_pool_info
|
|
76
|
+
state[:threads] = Xolo::Server.thread_info
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
body state
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# run the cleanup process from the internal timer task
|
|
83
|
+
# The before filter will ensure the request came from the server itself.
|
|
84
|
+
# with a valid internal auth token.
|
|
85
|
+
################
|
|
86
|
+
post '/maint/cleanup-internal' do
|
|
87
|
+
log_info 'Starting internal cleanup'
|
|
88
|
+
session[:admin] = 'Automated Cleanup'
|
|
89
|
+
|
|
90
|
+
thr = Thread.new { run_cleanup }
|
|
91
|
+
thr.name = 'Internal Cleanup Thread'
|
|
92
|
+
result = { result: 'Internal Cleanup Underway' }
|
|
93
|
+
body result
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# run the cleanup process manually from a server admin via xadm
|
|
97
|
+
################
|
|
98
|
+
post '/maint/cleanup' do
|
|
99
|
+
log_info "Starting manual server cleanup by #{session[:admin]}"
|
|
100
|
+
|
|
101
|
+
session[:admin] = "Cleanup by #{session[:admin]}"
|
|
102
|
+
thr = Thread.new { run_cleanup }
|
|
103
|
+
thr.name = 'Manual Cleanup Thread'
|
|
104
|
+
result = { result: 'Manual Cleanup Underway' }
|
|
105
|
+
body result
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# force an update of the client data
|
|
109
|
+
################
|
|
110
|
+
post '/maint/update-client-data' do
|
|
111
|
+
log_info "Force update of client-data by #{session[:admin]}"
|
|
112
|
+
log_debug "Gem Paths: #{Gem.paths.inspect}"
|
|
113
|
+
|
|
114
|
+
thr = Thread.new { update_client_data }
|
|
115
|
+
thr.name = 'Manual Client Data Update Thread'
|
|
116
|
+
result = { result: 'Client Data Update underway' }
|
|
117
|
+
body result
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# force log rotation
|
|
121
|
+
################
|
|
122
|
+
post '/maint/rotate-logs' do
|
|
123
|
+
log_info "Force log rotation by #{session[:admin]}"
|
|
124
|
+
|
|
125
|
+
thr = Thread.new { Xolo::Server::Log.rotate_logs force: true }
|
|
126
|
+
thr.name = 'Manual Log Rotation Thread'
|
|
127
|
+
result = { result: 'Log rotation underway' }
|
|
128
|
+
body result
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# set the log level
|
|
132
|
+
################
|
|
133
|
+
post '/maint/set-log-level' do
|
|
134
|
+
request.body.rewind
|
|
135
|
+
payload = parse_json request.body.read
|
|
136
|
+
level = payload[:level]
|
|
137
|
+
|
|
138
|
+
log_info "Setting log level to #{level} by #{session[:admin]}"
|
|
139
|
+
Xolo::Server.set_log_level level, admin: session[:admin]
|
|
140
|
+
|
|
141
|
+
result = { result: "Log level set to #{level}" }
|
|
142
|
+
body result
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Shutdown the server gracefully
|
|
146
|
+
# stop accepting new requests
|
|
147
|
+
# wait for all queues and threads to finish, including:
|
|
148
|
+
# the cleanup timer task & mutex
|
|
149
|
+
# the log rotation timer task & mutex
|
|
150
|
+
# the pkg deletion pool
|
|
151
|
+
# the object locks
|
|
152
|
+
# the progress streams, including this one, which will be the last thing to finish
|
|
153
|
+
################
|
|
154
|
+
post '/maint/shutdown-server' do
|
|
155
|
+
request.body.rewind
|
|
156
|
+
payload = parse_json request.body.read
|
|
157
|
+
restart = payload[:restart]
|
|
158
|
+
|
|
159
|
+
# for streamed responses, the with_streaming block must
|
|
160
|
+
# be the last thing in the route
|
|
161
|
+
with_streaming do
|
|
162
|
+
# give the stream a chance to get started
|
|
163
|
+
sleep 2
|
|
164
|
+
shutdown_server restart
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
end # module maint
|
|
169
|
+
|
|
170
|
+
end # Routes
|
|
171
|
+
|
|
172
|
+
end # Server
|
|
173
|
+
|
|
174
|
+
end # module Xolo
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Copyright 2025 Pixar
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the terms set forth in the LICENSE.txt file available at
|
|
4
|
+
# at the root of this project.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
# frozen_string_literal: true
|
|
8
|
+
|
|
9
|
+
# main module
|
|
10
|
+
module Xolo
|
|
11
|
+
|
|
12
|
+
# Server Module
|
|
13
|
+
module Server
|
|
14
|
+
|
|
15
|
+
module Routes
|
|
16
|
+
|
|
17
|
+
# See comments for Xolo::Server::Helpers::TitleEditor
|
|
18
|
+
#
|
|
19
|
+
module TitleEditor
|
|
20
|
+
|
|
21
|
+
# This is how we 'mix in' modules to Sinatra servers
|
|
22
|
+
# for route definitions and similar things
|
|
23
|
+
#
|
|
24
|
+
# (things to be 'included' for use in route and view processing
|
|
25
|
+
# are mixed in by delcaring them to be helpers)
|
|
26
|
+
#
|
|
27
|
+
# We make them extentions here with
|
|
28
|
+
# extend Sinatra::Extension (from sinatra-contrib)
|
|
29
|
+
# and then 'register' them in the server with
|
|
30
|
+
# register Xolo::Server::<Module>
|
|
31
|
+
# Doing it this way allows us to split the code into a logical
|
|
32
|
+
# file structure, without re-opening the Sinatra::Base server app,
|
|
33
|
+
# and let xeitwork do the requiring of those files
|
|
34
|
+
extend Sinatra::Extension
|
|
35
|
+
|
|
36
|
+
# Module methods
|
|
37
|
+
#
|
|
38
|
+
##############################
|
|
39
|
+
##############################
|
|
40
|
+
|
|
41
|
+
# when this module is included
|
|
42
|
+
def self.included(includer)
|
|
43
|
+
Xolo.verbose_include includer, self
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# when this module is extended
|
|
47
|
+
def self.extended(extender)
|
|
48
|
+
Xolo.verbose_extend extender, self
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Routes
|
|
52
|
+
#
|
|
53
|
+
##############################
|
|
54
|
+
##############################
|
|
55
|
+
|
|
56
|
+
###############
|
|
57
|
+
get '/title-editor/titles' do
|
|
58
|
+
log_debug "Fetching Title Editor titles for #{session[:admin]}"
|
|
59
|
+
wcnx = ted_cnx
|
|
60
|
+
body Windoo::SoftwareTitle.all(cnx: wcnx).map { |t| t[:id] }.sort
|
|
61
|
+
ensure
|
|
62
|
+
wcnx&.disconnect
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end # Module
|
|
66
|
+
|
|
67
|
+
end # Routes
|
|
68
|
+
|
|
69
|
+
end # Server
|
|
70
|
+
|
|
71
|
+
end # module Xolo
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# Copyright 2025 Pixar
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the terms set forth in the LICENSE.txt file available at
|
|
4
|
+
# at the root of this project.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
# frozen_string_literal: true
|
|
8
|
+
|
|
9
|
+
# main module
|
|
10
|
+
module Xolo
|
|
11
|
+
|
|
12
|
+
# Server Module
|
|
13
|
+
module Server
|
|
14
|
+
|
|
15
|
+
module Routes
|
|
16
|
+
|
|
17
|
+
module Titles
|
|
18
|
+
|
|
19
|
+
# This is how we 'mix in' modules to Sinatra servers
|
|
20
|
+
# for route definitions and similar things
|
|
21
|
+
#
|
|
22
|
+
# (things to be 'included' for use in route and view processing
|
|
23
|
+
# are mixed in by delcaring them to be helpers)
|
|
24
|
+
#
|
|
25
|
+
# We make them extentions here with
|
|
26
|
+
# extend Sinatra::Extension (from sinatra-contrib)
|
|
27
|
+
# and then 'register' them in the server with
|
|
28
|
+
# register Xolo::Server::<Module>
|
|
29
|
+
# Doing it this way allows us to split the code into a logical
|
|
30
|
+
# file structure, without re-opening the Sinatra::Base server app
|
|
31
|
+
extend Sinatra::Extension
|
|
32
|
+
|
|
33
|
+
# when this module is included
|
|
34
|
+
def self.included(includer)
|
|
35
|
+
Xolo.verbose_include includer, self
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Create a new title from the body content of the request
|
|
39
|
+
#
|
|
40
|
+
# @return [Hash] A response hash
|
|
41
|
+
#################################
|
|
42
|
+
post '/titles' do
|
|
43
|
+
request.body.rewind
|
|
44
|
+
data = parse_json(request.body.read)
|
|
45
|
+
log_debug "Incoming new title data: #{data}"
|
|
46
|
+
|
|
47
|
+
title = instantiate_title data
|
|
48
|
+
halt_on_existing_title title.title
|
|
49
|
+
|
|
50
|
+
log_info "Admin #{session[:admin]} is creating title '#{title.title}'"
|
|
51
|
+
with_streaming do
|
|
52
|
+
title.create
|
|
53
|
+
# we don't need to update client data when titles are created
|
|
54
|
+
# because they don't have any versions yet, so there's nothing a
|
|
55
|
+
# client can do with them.
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# get an array of titles
|
|
60
|
+
# @return [Array<Hash>] the data for existing titles
|
|
61
|
+
#################################
|
|
62
|
+
get '/titles' do
|
|
63
|
+
log_debug "Admin #{session[:admin]} is fetching all titles"
|
|
64
|
+
body all_title_objects.map(&:to_h)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# get all the data for a single title
|
|
68
|
+
# @return [Hash] The data for this title
|
|
69
|
+
#################################
|
|
70
|
+
get '/titles/:title' do
|
|
71
|
+
log_debug "Admin #{session[:admin]} is fetching title '#{params[:title]}'"
|
|
72
|
+
halt_on_missing_title params[:title]
|
|
73
|
+
|
|
74
|
+
title = instantiate_title params[:title]
|
|
75
|
+
|
|
76
|
+
body title.to_h
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Update a title by
|
|
80
|
+
# replacing the data for an existing title with the content of the request
|
|
81
|
+
# @return [Hash] A response hash
|
|
82
|
+
#################################
|
|
83
|
+
put '/titles/:title' do
|
|
84
|
+
halt_on_missing_title params[:title]
|
|
85
|
+
halt_on_locked_title params[:title]
|
|
86
|
+
|
|
87
|
+
title = instantiate_title params[:title]
|
|
88
|
+
|
|
89
|
+
request.body.rewind
|
|
90
|
+
new_data = parse_json(request.body.read)
|
|
91
|
+
log_debug "Incoming update title data: #{new_data}"
|
|
92
|
+
|
|
93
|
+
log_info "Admin #{session[:admin]} is updating title '#{params[:title]}'"
|
|
94
|
+
with_streaming do
|
|
95
|
+
title.update new_data
|
|
96
|
+
update_client_data
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Release a version of a title
|
|
101
|
+
#
|
|
102
|
+
# @return [Hash] A response hash
|
|
103
|
+
#################################
|
|
104
|
+
patch '/titles/:title/release/:version' do
|
|
105
|
+
log_info "Admin #{session[:admin]} is releasing version #{params[:version]} of title '#{params[:title]}' via PATCH"
|
|
106
|
+
|
|
107
|
+
halt_on_missing_title params[:title]
|
|
108
|
+
halt_on_missing_version params[:title], params[:version]
|
|
109
|
+
halt_on_locked_title params[:title]
|
|
110
|
+
halt_on_locked_version params[:title], params[:version]
|
|
111
|
+
|
|
112
|
+
title = instantiate_title params[:title]
|
|
113
|
+
|
|
114
|
+
if title.released_version == params[:version]
|
|
115
|
+
msg = "Version '#{params[:version]}' of title '#{params[:title]}' is already released"
|
|
116
|
+
log_debug "ERROR: #{msg}"
|
|
117
|
+
halt 409, { status: 409, error: msg }
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
vers_to_release = params[:version]
|
|
121
|
+
|
|
122
|
+
with_streaming do
|
|
123
|
+
title.release vers_to_release
|
|
124
|
+
update_client_data
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# run 'repair' on a title
|
|
129
|
+
# to do all versions, body should be JSON { "repair_versions": true }
|
|
130
|
+
######################
|
|
131
|
+
post '/titles/:title/repair' do
|
|
132
|
+
request.body.rewind
|
|
133
|
+
data = request.body.read.strip
|
|
134
|
+
do_versions = data.pix_empty? ? false : parse_json(data)[:repair_versions]
|
|
135
|
+
|
|
136
|
+
addendum = do_versions ? ' and all versions' : ' (title only)'
|
|
137
|
+
log_debug "Admin #{session[:admin]} is repairing title '#{params[:title]}'#{addendum}"
|
|
138
|
+
|
|
139
|
+
halt_on_missing_title params[:title]
|
|
140
|
+
halt_on_locked_title params[:title]
|
|
141
|
+
title = instantiate_title params[:title]
|
|
142
|
+
|
|
143
|
+
with_streaming do
|
|
144
|
+
title.repair repair_versions: do_versions
|
|
145
|
+
update_client_data
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Delete an existing title
|
|
150
|
+
# @return [Hash] A response hash
|
|
151
|
+
#################################
|
|
152
|
+
delete '/titles/:title' do
|
|
153
|
+
halt_on_missing_title params[:title]
|
|
154
|
+
|
|
155
|
+
title = instantiate_title params[:title]
|
|
156
|
+
|
|
157
|
+
with_streaming do
|
|
158
|
+
title.delete
|
|
159
|
+
update_client_data
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Handle upload for self-service icon for a title
|
|
164
|
+
#
|
|
165
|
+
# @return [Hash] A response hash
|
|
166
|
+
#################################
|
|
167
|
+
post '/titles/:title/ssvc-icon' do
|
|
168
|
+
process_incoming_ssvc_icon
|
|
169
|
+
body({ result: :uploaded })
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Return the members of the 'frozen' static group for a title
|
|
173
|
+
#
|
|
174
|
+
# @return [Hash{String => String}] computer name => user name
|
|
175
|
+
#################################
|
|
176
|
+
get '/titles/:title/frozen' do
|
|
177
|
+
log_debug "Admin #{session[:admin]} is fetching frozen computers for title '#{params[:title]}'"
|
|
178
|
+
halt_on_missing_title params[:title]
|
|
179
|
+
title = instantiate_title params[:title]
|
|
180
|
+
body title.frozen_computers
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# add one or more computers to the 'frozen' static group for a title
|
|
184
|
+
# Body should be an array of computer names
|
|
185
|
+
#
|
|
186
|
+
# @return [Hash] A response hash
|
|
187
|
+
#################################
|
|
188
|
+
put '/titles/:title/freeze' do
|
|
189
|
+
halt_on_missing_title params[:title]
|
|
190
|
+
|
|
191
|
+
request.body.rewind
|
|
192
|
+
data = parse_json(request.body.read)
|
|
193
|
+
|
|
194
|
+
comps_to_freeze = expand_freeze_thaw_targets(targets: data[:targets], users: data[:users])
|
|
195
|
+
|
|
196
|
+
log_debug "Target computers to freeze for title #{params[:title]}: #{comps_to_freeze}"
|
|
197
|
+
title = instantiate_title params[:title]
|
|
198
|
+
|
|
199
|
+
result = title.freeze_or_thaw_computers(action: :freeze, computers: comps_to_freeze)
|
|
200
|
+
body result
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# remove one or more computers from the 'frozen' static group for a title
|
|
204
|
+
# Body should be an array of computer names
|
|
205
|
+
#
|
|
206
|
+
# If any computer name is 'clear_all' then all frozen computers will be thawed
|
|
207
|
+
#
|
|
208
|
+
# @return [Hash] A response hash
|
|
209
|
+
#################################
|
|
210
|
+
put '/titles/:title/thaw' do
|
|
211
|
+
halt_on_missing_title params[:title]
|
|
212
|
+
|
|
213
|
+
request.body.rewind
|
|
214
|
+
data = parse_json(request.body.read)
|
|
215
|
+
|
|
216
|
+
comps_to_thaw = expand_freeze_thaw_targets(targets: data[:targets], users: data[:users])
|
|
217
|
+
|
|
218
|
+
log_debug "Incoming computers to thaw for title #{params[:title]}: #{comps_to_thaw}"
|
|
219
|
+
title = instantiate_title params[:title]
|
|
220
|
+
|
|
221
|
+
result = title.freeze_or_thaw_computers(action: :thaw, computers: comps_to_thaw)
|
|
222
|
+
body result
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Return info about all the computers with a given title installed
|
|
226
|
+
#
|
|
227
|
+
# @return [Array<Hash>] The data for all computers with the given title
|
|
228
|
+
#################################
|
|
229
|
+
get '/titles/:title/patch_report' do
|
|
230
|
+
log_debug "Admin #{session[:admin]} is fetching patch report for title '#{params[:title]}'"
|
|
231
|
+
halt_on_missing_title params[:title]
|
|
232
|
+
title = instantiate_title params[:title]
|
|
233
|
+
|
|
234
|
+
body title.patch_report
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Return URLs for all the UI pages for a title
|
|
238
|
+
#
|
|
239
|
+
# @return [Hash] The URLs for all the UI pages for a title
|
|
240
|
+
#################################
|
|
241
|
+
get '/titles/:title/urls' do
|
|
242
|
+
log_debug "Admin #{session[:admin]} is fetching GUI URLS for title '#{params[:title]}'"
|
|
243
|
+
halt_on_missing_title params[:title]
|
|
244
|
+
title = instantiate_title params[:title]
|
|
245
|
+
data = {
|
|
246
|
+
ted_title_url: title.ted_title_url,
|
|
247
|
+
jamf_installed_group_url: title.jamf_installed_group_url,
|
|
248
|
+
jamf_frozen_group_url: title.jamf_frozen_group_url
|
|
249
|
+
}
|
|
250
|
+
data[:jamf_manual_install_released_policy_url] = title.jamf_manual_install_released_policy_url if title.jamf_manual_install_released_policy_exist?
|
|
251
|
+
|
|
252
|
+
if title.uninstallable?
|
|
253
|
+
data[:jamf_uninstall_script_url] = title.jamf_uninstall_script_url
|
|
254
|
+
data[:jamf_uninstall_policy_url] = title.jamf_uninstall_policy_url
|
|
255
|
+
data[:jamf_expire_policy_url] = title.jamf_expire_policy_url if title.expiration
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
if title.jamf_ted_title_active?
|
|
259
|
+
data[:jamf_patch_title_url] = title.jamf_patch_title_url unless title.versions.empty?
|
|
260
|
+
data[:jamf_patch_ea_url] = title.jamf_patch_ea_url if title.version_script
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
data[:jamf_normal_ea_url] = title.jamf_normal_ea_url if title.version_script
|
|
264
|
+
|
|
265
|
+
body data
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Return the changelog for a title
|
|
269
|
+
#
|
|
270
|
+
# @return [Array<Hash>] The changelog for a title
|
|
271
|
+
#################################
|
|
272
|
+
get '/titles/:title/changelog' do
|
|
273
|
+
log_debug "Admin #{session[:admin]} is fetching the change log for title '#{params[:title]}'"
|
|
274
|
+
halt_on_missing_title params[:title]
|
|
275
|
+
title = instantiate_title params[:title]
|
|
276
|
+
body title.changelog
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
end # Titles
|
|
280
|
+
|
|
281
|
+
end # Routes
|
|
282
|
+
|
|
283
|
+
end # Server
|
|
284
|
+
|
|
285
|
+
end # module Xolo
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Copyright 2025 Pixar
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the terms set forth in the LICENSE.txt file available at
|
|
4
|
+
# at the root of this project.
|
|
5
|
+
#
|
|
6
|
+
|
|
7
|
+
# frozen_string_literal: true
|
|
8
|
+
|
|
9
|
+
# main module
|
|
10
|
+
module Xolo
|
|
11
|
+
|
|
12
|
+
# Server Module
|
|
13
|
+
module Server
|
|
14
|
+
|
|
15
|
+
module Routes
|
|
16
|
+
|
|
17
|
+
module Uploads
|
|
18
|
+
|
|
19
|
+
# This is how we 'extend' modules to Sinatra servers
|
|
20
|
+
# for route definitions and similar things
|
|
21
|
+
#
|
|
22
|
+
# (things to be 'included' for use in route and view processing
|
|
23
|
+
# are mixed in by delcaring them to be helpers)
|
|
24
|
+
#
|
|
25
|
+
# We make them extentions here with
|
|
26
|
+
# extend Sinatra::Extension (from sinatra-contrib)
|
|
27
|
+
# and then 'register' them in the server with
|
|
28
|
+
# register Xolo::Server::<Module>
|
|
29
|
+
# Doing it this way allows us to split the code into a logical
|
|
30
|
+
# file structure, without re-opening the Sinatra::Base server app,
|
|
31
|
+
# and let xeitwork do the requiring of those files
|
|
32
|
+
extend Sinatra::Extension
|
|
33
|
+
|
|
34
|
+
# Module methods
|
|
35
|
+
##############################
|
|
36
|
+
##############################
|
|
37
|
+
|
|
38
|
+
# when this module is included
|
|
39
|
+
def self.included(includer)
|
|
40
|
+
Xolo.verbose_include includer, self
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# when this module is extended
|
|
44
|
+
def self.extended(extender)
|
|
45
|
+
Xolo.verbose_extend extender, self
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# This hash contains upload progress stream urls
|
|
49
|
+
# for file uploads, keyed by session[:xolo_id]
|
|
50
|
+
# Individual sessions.
|
|
51
|
+
# Should be accessible from anywhere via
|
|
52
|
+
# Xolo::Server::App.file_upload_progress_files
|
|
53
|
+
# or Xolo::Server::Routes::Uploads.file_upload_progress_files
|
|
54
|
+
#
|
|
55
|
+
# @return [Hash {String => String}]
|
|
56
|
+
##########################
|
|
57
|
+
def file_upload_progress_files
|
|
58
|
+
@file_upload_progress_files ||= {}
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Routes
|
|
62
|
+
##############################
|
|
63
|
+
##############################
|
|
64
|
+
|
|
65
|
+
# # param with the uploaded file must be :file
|
|
66
|
+
# ######################
|
|
67
|
+
# post '/upload/ssvc-icon/:title' do
|
|
68
|
+
# process_incoming_ssvc_icon
|
|
69
|
+
# body({ result: :uploaded })
|
|
70
|
+
# end
|
|
71
|
+
|
|
72
|
+
# # param with the uploaded file must be :file
|
|
73
|
+
# ######################
|
|
74
|
+
# post '/upload/pkg/:title/:version' do
|
|
75
|
+
# process_incoming_pkg
|
|
76
|
+
# body({ result: :uploaded })
|
|
77
|
+
# end
|
|
78
|
+
|
|
79
|
+
# param with the uploaded file must be :file
|
|
80
|
+
######################
|
|
81
|
+
post '/upload/test' do
|
|
82
|
+
with_streaming do
|
|
83
|
+
process_incoming_testfile
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
end # Module
|
|
88
|
+
|
|
89
|
+
end # Routes
|
|
90
|
+
|
|
91
|
+
end # Server
|
|
92
|
+
|
|
93
|
+
end # module Xolo
|