Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Critical Commanding Support For Enterprise #1627

Merged
merged 8 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions openc3-cosmos-cmd-tlm-api/app/controllers/api_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,12 @@ def handle_post(request_data, request_headers)
# see http://www.jsonrpc.org/historical/json-rpc-over-http.html#errors
if error_code
case error_code
when OpenC3::JsonRpcError::ErrorCode::INVALID_REQUEST then status = 400 # Bad request
when OpenC3::JsonRpcError::ErrorCode::AUTH_ERROR then status = 401 # Auth
when OpenC3::JsonRpcError::ErrorCode::FORBIDDEN_ERROR then status = 403 # Forbidden
when OpenC3::JsonRpcError::ErrorCode::METHOD_NOT_FOUND then status = 404 # Not found
when OpenC3::JsonRpcError::ErrorCode::HAZARDOUS_ERROR then status = 409 # Server conflict
when OpenC3::JsonRpcError::ErrorCode::INVALID_REQUEST then status = 400 # Bad request
when OpenC3::JsonRpcError::ErrorCode::AUTH_ERROR then status = 401 # Auth
when OpenC3::JsonRpcError::ErrorCode::FORBIDDEN_ERROR then status = 403 # Forbidden
when OpenC3::JsonRpcError::ErrorCode::METHOD_NOT_FOUND then status = 404 # Not found
when OpenC3::JsonRpcError::ErrorCode::HAZARDOUS_ERROR then status = 409 # Server conflict
when OpenC3::JsonRpcError::ErrorCode::CRITICAL_CMD_ERROR then status = 428 # Precondition required
else
# Also includes the following errors:
# OpenC3::JsonRpcError::ErrorCode::PARSE_ERROR
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,23 @@ def username()
end

# Authorize and rescue the possible exceptions
# @return [Boolean] true if authorize successful
def authorization(permission, target_name: nil)
# @return [Boolean or User] User if authorize successful
def authorization(permission, target_name: nil, perform_render: true)
begin
authorize(
return authorize(
permission: permission,
target_name: target_name,
manual: request.headers['HTTP_MANUAL'],
scope: params[:scope],
token: request.headers['HTTP_AUTHORIZATION'],
)
rescue OpenC3::AuthError => e
render(json: { status: 'error', message: e.message }, status: 401) and
return false
render(json: { status: 'error', message: e.message }, status: 401) if perform_render
return false
rescue OpenC3::ForbiddenError => e
render(json: { status: 'error', message: e.message }, status: 403) and
return false
render(json: { status: 'error', message: e.message }, status: 403) if perform_render
return false
end
true
end

def sanitize_params(param_list, require_params: true, allow_forward_slash: false)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# encoding: ascii-8bit

# Copyright 2024 OpenC3, Inc.
# All Rights Reserved.
#
# This program is free software; you can modify and/or redistribute it
# under the terms of the GNU Affero General Public License
# as published by the Free Software Foundation; version 3 with
# attribution addendums as found in the LICENSE.txt
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.

# This file may also be used under the terms of a commercial license
# if purchased from OpenC3, Inc.

begin
require 'openc3-enterprise/controllers/critical_cmd_controller'
rescue LoadError
class CriticalCmdController < ModelController
end
end
8 changes: 6 additions & 2 deletions openc3-cosmos-cmd-tlm-api/app/models/messages_thread.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@ def initialize(
@redis_offset = nil # Redis offset to transition from files
@scope = scope
@thread_mode = :SETUP
@topics = ["#{scope}__openc3_log_messages"]
@topics = ["#{scope}__openc3_log_messages", "#{scope}__openc3_ephemeral_messages"]

offsets = nil
offsets = [start_offset] if start_offset
# $ means only new messages for the ephemeral topic
offsets = [start_offset, "$"] if start_offset
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does the "$" do? comment?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added comment.

super(@topics, channel, history_count, max_batch_size, offsets: offsets)
end

Expand Down Expand Up @@ -89,6 +90,7 @@ def setup_thread_body
offset = ((@start_time + delta) / 1_000_000).to_s + "-0"
# OpenC3::Logger.debug "stream from Redis offset:#{offset} redis_time:#{redis_time} delta:#{delta}"
@offsets[@offset_index_by_topic[@topics[0]]] = offset
@offsets[@offset_index_by_topic[@topics[1]]] = "$" # Only new ephemeral messages
@thread_mode = :STREAM
end
end
Expand All @@ -99,6 +101,7 @@ def setup_thread_body
else
unless @offsets
thread_setup() # From TopicsThread
@offsets[@offset_index_by_topic[@topics[1]]] = "$" # Only new ephemeral messages
end
@thread_mode = :STREAM
end
Expand Down Expand Up @@ -145,6 +148,7 @@ def file_thread_body
else
@offsets[@offset_index_by_topic[@topics[0]]] = "0-0"
end
@offsets[@offset_index_by_topic[@topics[1]]] = "$" # Only new ephemeral messages
@thread_mode = :STREAM
end

Expand Down
2 changes: 2 additions & 0 deletions openc3-cosmos-cmd-tlm-api/app/models/topics_thread.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ def transmit_results(results, force: false)

def thread_setup
@topics.each do |topic|
topic_split = topic.split('__')
next if topic_split[1] == 'openc3_ephemeral_messages' # No history
results = OpenC3::Topic.xrevrange(topic, '+', '-', count: [1, @history_count].max) # Always get at least 1, because 0 breaks redis-rb
batch = []
results.reverse_each do |msg_id, msg_hash|
Expand Down
5 changes: 5 additions & 0 deletions openc3-cosmos-cmd-tlm-api/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -218,5 +218,10 @@
post '/cmdauth/release', to: 'cmd_authority#release'
post '/cmdauth/take-all', to: 'cmd_authority#take_all'
post '/cmdauth/release-all', to: 'cmd_authority#release_all'

get '/criticalcmd/status/:id', to: 'critical_cmd#status'
post '/criticalcmd/approve/:id', to: 'critical_cmd#approve'
post '/criticalcmd/reject/:id', to: 'critical_cmd#reject'
get '/criticalcmd/canapprove/:id', to: 'critical_cmd#canapprove'
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,12 @@
:type="'cmd'"
v-model="viewDetails"
/>
<critical-cmd-dialog
:uuid="criticalCmdUuid"
:cmdString="criticalCmdString"
:cmdUser="criticalCmdUser"
v-model="displayCriticalCmd"
/>

<v-dialog v-model="displayErrorDialog" max-width="600">
<v-card>
Expand Down Expand Up @@ -251,6 +257,7 @@ import CommandParameterEditor from '@/tools/CommandSender/CommandParameterEditor
import Utilities from '@/tools/CommandSender/utilities'
import { OpenC3Api } from '@openc3/tool-common/src/services/openc3-api'
import DetailsDialog from '@openc3/tool-common/src/components/DetailsDialog'
import CriticalCmdDialog from '@openc3/tool-common/src/components/CriticalCmdDialog'
import TopBar from '@openc3/tool-common/src/components/TopBar'
import Openc3Screen from '@openc3/tool-common/src/components/Openc3Screen'
import 'sprintf-js'
Expand All @@ -259,6 +266,7 @@ export default {
mixins: [Utilities],
components: {
DetailsDialog,
CriticalCmdDialog,
TargetPacketItemChooser,
CommandParameterEditor,
TopBar,
Expand Down Expand Up @@ -297,6 +305,10 @@ export default {
displaySendHazardous: false,
displayErrorDialog: false,
displaySendRaw: false,
displayCriticalCmd: false,
criticalTargetName: null,
criticalCmdName: null,
criticalUuid: null,
sendDisabled: false,
api: null,
viewDetails: false,
Expand Down Expand Up @@ -667,13 +679,16 @@ export default {
targetName,
commandName,
paramList,
{
'Ignore-Errors': '428',
},
)
} else {
cmd = 'cmd_raw'
obs = this.api.cmd_raw(targetName, commandName, paramList, {
// This request could be denied due to out of range but since
// we're explicitly handling it we don't want the interceptor to fire
'Ignore-Errors': '500',
'Ignore-Errors': '428 500',
})
}
} else {
Expand All @@ -683,13 +698,16 @@ export default {
targetName,
commandName,
paramList,
{
'Ignore-Errors': '428',
},
)
} else {
cmd = 'cmd'
obs = this.api.cmd(targetName, commandName, paramList, {
// This request could be denied due to out of range but since
// we're explicitly handling it we don't want the interceptor to fire
'Ignore-Errors': '500',
'Ignore-Errors': '428 500',
})
}
}
Expand Down Expand Up @@ -733,6 +751,9 @@ export default {
this.lastTargetName,
this.lastCommandName,
this.lastParamList,
{
'Ignore-Errors': '428',
},
)
} else {
cmd = 'cmd_raw'
Expand All @@ -743,7 +764,7 @@ export default {
{
// This request could be denied due to out of range but since
// we're explicitly handling it we don't want the interceptor to fire
'Ignore-Errors': '500',
'Ignore-Errors': '428 500',
},
)
}
Expand All @@ -754,6 +775,9 @@ export default {
this.lastTargetName,
this.lastCommandName,
this.lastParamList,
{
'Ignore-Errors': '428',
},
)
} else {
cmd = 'cmd'
Expand All @@ -764,7 +788,7 @@ export default {
{
// This request could be denied due to out of range but since
// we're explicitly handling it we don't want the interceptor to fire
'Ignore-Errors': '500',
'Ignore-Errors': '428 500',
},
)
}
Expand Down Expand Up @@ -869,8 +893,20 @@ export default {
this.status += ': '
this.status += error.message
}
if (this.status.includes('CriticalCmdError')) {
this.status = `Critical Command Queued For Approval`
}
if (showDialog) {
this.displayErrorDialog = true
if (error.message.includes('CriticalCmdError')) {
this.criticalCmdUuid = error.object.data.instance_variables['@uuid']
this.criticalCmdString =
error.object.data.instance_variables['@cmd_string']
this.criticalCmdUser =
error.object.data.instance_variables['@username']
this.displayCriticalCmd = true
} else {
this.displayErrorDialog = true
}
}
},

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,14 @@
:text="suiteError"
:width="1000"
/>
<critical-cmd-dialog
:uuid="criticalCmdUuid"
:cmdString="criticalCmdString"
:cmdUser="criticalCmdUser"
:persistent="true"
v-model="displayCriticalCmd"
@status="promptDialogCallback"
/>
<v-bottom-sheet v-model="showScripts">
<v-sheet class="pb-11 pt-5 px-5">
<running-scripts
Expand Down Expand Up @@ -408,6 +416,7 @@ import { Multipane, MultipaneResizer } from 'vue-multipane'
import FileOpenSaveDialog from '@openc3/tool-common/src/components/FileOpenSaveDialog'
import EnvironmentDialog from '@openc3/tool-common/src/components/EnvironmentDialog'
import SimpleTextDialog from '@openc3/tool-common/src/components/SimpleTextDialog'
import CriticalCmdDialog from '@openc3/tool-common/src/components/CriticalCmdDialog'
import TopBar from '@openc3/tool-common/src/components/TopBar'
import { OpenC3Api } from '@openc3/tool-common/src/services/openc3-api'
import { fileIcon } from '@openc3/tool-common/src/tools/base/util/fileIcon'
Expand Down Expand Up @@ -459,6 +468,7 @@ export default {
SuiteRunner,
RunningScripts,
ScriptLogMessages,
CriticalCmdDialog,
},
data() {
return {
Expand Down Expand Up @@ -583,6 +593,10 @@ export default {
idCounter: 0,
updateCounter: 0,
recent: [],
criticalCmdUuid: null,
criticalCmdString: null,
criticalCmdUser: null,
displayCriticalCmd: false,
}
},
computed: {
Expand Down Expand Up @@ -935,7 +949,10 @@ export default {
this.executeUser = true
} else {
await Api.get(`/openc3-api/roles/${role}`).then((response) => {
if (response.data.permissions !== undefined) {
if (
response.data !== null &&
response.data.permissions !== undefined
) {
if (
response.data.permissions.some(
(i) => i.permission == 'script_edit',
Expand Down Expand Up @@ -1914,6 +1931,12 @@ export default {
this.prompt.callback = this.promptDialogCallback
this.prompt.show = true
break
case 'prompt_for_critical_cmd':
this.criticalCmdUuid = data.args[0]
this.criticalCmdString = data.args[5]
this.criticalCmdUser = data.args[1]
this.displayCriticalCmd = true
break
case 'prompt':
if (data.kwargs && data.kwargs.informative) {
this.prompt.subtitle = data.kwargs.informative
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@
border-color: var(--button-color-border-primary-default) !important;
color: var(--color-text-interactive-default) !important;
}
.theme--dark.v-btn--outlined.v-btn--disabled {
border-color: var(--button-color-border-primary-default) !important;
color: var(--color-text-interactive-default) !important;
opacity: 0.4 !important;
}
.theme--dark.v-btn--outlined:not(
.v-btn--icon,
.v-btn--round,
Expand Down
Loading
Loading