Skip to content

Commit 6c66cc7

Browse files
jhs-pandaNicholasMy
authored andcommitted
Disallow creating or updating scheduler if invalid (autolab#2214)
* Add checks for existing and valid Action when creating new scheduler or updating current scheduler. * Fix formatting. * Only allow for creating new schedulers and updating current schedulers if visual run under action does not return an error. * Correct logic of returning error. * Addressed comments in review. * Added additional error checking suggested by CodeRabbit. (cherry picked from commit 2dc3ce9)
1 parent dad3e2b commit 6c66cc7

File tree

1 file changed

+110
-49
lines changed

1 file changed

+110
-49
lines changed

app/controllers/schedulers_controller.rb

Lines changed: 110 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,23 @@ def new; end
2323
action_auth_level :create, :administrator
2424
def create
2525
@scheduler = @course.scheduler.new(scheduler_params)
26-
if @scheduler.save
27-
flash[:success] = "Scheduler created!"
28-
redirect_to(course_schedulers_path(@course))
29-
else
30-
flash[:error] = "Scheduler create failed. Please check all fields."
31-
redirect_to(new_course_scheduler_path(@course))
26+
action_path = Rails.root.join(scheduler_params[:action]).to_path
27+
# Check if the action file exists, is readable, and compiles
28+
if validate_compile_action_file(action_path)
29+
if @scheduler.save
30+
# Ensure visual run is successful
31+
if run_visual_scheduler(@scheduler)
32+
flash[:success] = "Scheduler created and executed successfully!"
33+
redirect_to(course_schedulers_path(@course)) and return
34+
else
35+
@scheduler.destroy
36+
flash[:error] = "Scheduler creation failed during execution."
37+
end
38+
else
39+
flash[:error] = "Scheduler create failed. Please check all fields."
40+
end
3241
end
42+
redirect_to(new_course_scheduler_path(@course))
3343
end
3444

3545
action_auth_level :edit, :administrator
@@ -44,56 +54,32 @@ def run
4454

4555
action_auth_level :visual_run, :administrator
4656
def visual_run
47-
action = Scheduler.find(params[:scheduler_id])
48-
# https://stackoverflow.com/a/1076445
49-
read, write = IO.pipe
50-
@log = "Executing #{Rails.root.join(action.action)}\n"
51-
begin
52-
pid = fork do
53-
read.close
54-
mod_name = Rails.root.join(action.action).to_path
55-
fork_log = ""
56-
begin
57-
require mod_name
58-
output = Updater.update(action.course)
59-
if output.respond_to?(:to_str)
60-
fork_log << "----- Script Output -----\n"
61-
fork_log << output
62-
fork_log << "\n----- End Script Output -----"
63-
end
64-
rescue ScriptError, StandardError => e
65-
fork_log << "----- Script Error Output -----\n"
66-
fork_log << "Error in '#{@course.name}' updater: #{e.message}\n"
67-
fork_log << e.backtrace.join("\n\t")
68-
fork_log << "\n---- End Script Error Output -----"
69-
end
70-
write.print fork_log
71-
end
72-
73-
write.close
74-
result = read.read
75-
Process.wait2(pid)
76-
@log << result
77-
rescue StandardError => e
78-
@log << "----- Error Output -----\n"
79-
@log << "Error in '#{@course.name}' updater: #{e.message}\n"
80-
@log << e.backtrace.join("\n\t")
81-
@log << "\n---- End Error Output -----"
82-
end
83-
@log << "\nCompleted running action."
57+
@scheduler = Scheduler.find(params[:scheduler_id])
58+
@log = execute_action(@scheduler)
8459
render partial: "visual_test"
8560
end
8661

8762
action_auth_level :update, :administrator
8863
def update
8964
@scheduler = Scheduler.find_by(id: params[:id])
90-
if @scheduler&.update(scheduler_params)
91-
flash[:success] = "Scheduler updated."
92-
redirect_to(course_schedulers_path(@course))
93-
else
94-
flash[:error] = "Scheduler update failed! Please check your fields."
95-
redirect_to(edit_course_scheduler_path(@course, @scheduler))
65+
action_path = Rails.root.join(scheduler_params[:action]).to_path
66+
# Check if the action file exists, is readable, and compiles
67+
if validate_compile_action_file(action_path)
68+
previous_state = @scheduler.attributes
69+
if @scheduler.update(scheduler_params)
70+
# Ensure visual run is successful
71+
if run_visual_scheduler(@scheduler)
72+
flash[:success] = "Scheduler updated and executed successfully!"
73+
redirect_to(course_schedulers_path(@course)) and return
74+
else
75+
@scheduler.update(previous_state) # If error, revert to previous state.
76+
flash[:error] = "Scheduler update failed during execution. Reverted to previous state."
77+
end
78+
else
79+
flash[:error] = "Scheduler update failed! Please check your fields."
80+
end
9681
end
82+
redirect_to(edit_course_scheduler_path(@course, @scheduler))
9783
end
9884

9985
action_auth_level :destroy, :administrator
@@ -119,4 +105,79 @@ def set_manage_scheduler_breadcrumb
119105

120106
@breadcrumbs << (view_context.link_to "Manage Schedulers", course_schedulers_path(@course))
121107
end
108+
109+
def validate_compile_action_file(action_path)
110+
# Check if the action file exists and is readable
111+
unless File.exist?(action_path) && File.readable?(action_path)
112+
flash[:error] = "Scheduler update failed. Action file does not exist or is
113+
not readable at #{action_path}."
114+
return false
115+
end
116+
117+
# compile action file to check for syntax errors
118+
begin
119+
RubyVM::InstructionSequence.compile(File.read(action_path))
120+
rescue SyntaxError => e
121+
flash[:error] = "Syntax error in action file: #{e.message}"
122+
return false
123+
rescue StandardError => e
124+
flash[:error] = "Error validating action file: #{e.message}"
125+
return false
126+
end
127+
128+
true
129+
end
130+
131+
def run_visual_scheduler(scheduler)
132+
log = execute_action(scheduler)
133+
# Ensure visual run is successful or return error
134+
if log.include?("Error")
135+
flash[:error] = "Scheduler execution failed."
136+
false
137+
else
138+
flash[:success] = "Scheduler executed successfully!"
139+
true
140+
end
141+
end
142+
143+
def execute_action(scheduler)
144+
action_path = Rails.root.join(scheduler.action).to_path
145+
# https://stackoverflow.com/a/1076445
146+
read, write = IO.pipe
147+
log = "Executing #{action_path}\n"
148+
begin
149+
pid = fork do
150+
read.close
151+
mod_name = action_path
152+
fork_log = ""
153+
begin
154+
require mod_name
155+
output = Updater.update(scheduler.course)
156+
if output.respond_to?(:to_str)
157+
fork_log << "----- Script Output -----\n"
158+
fork_log << output
159+
fork_log << "\n----- End Script Output -----"
160+
end
161+
rescue ScriptError, StandardError => e
162+
fork_log << "----- Script Error Output -----\n"
163+
fork_log << "Error in '#{scheduler.course.name}' updater: #{e.message}\n"
164+
fork_log << e.backtrace.join("\n\t")
165+
fork_log << "\n---- End Script Error Output -----"
166+
end
167+
write.print fork_log
168+
end
169+
170+
write.close
171+
result = read.read
172+
Process.wait2(pid)
173+
log << result
174+
rescue StandardError => e
175+
log << "----- Error Output -----\n"
176+
log << "Error during execution: #{e.message}\n"
177+
log << e.backtrace.join("\n\t")
178+
log << "\n---- End Error Output -----"
179+
end
180+
log << "\nCompleted running action."
181+
log
182+
end
122183
end

0 commit comments

Comments
 (0)