updating hasura actions to all be camelCase

This commit is contained in:
its-a-feature
2026-05-01 09:12:38 -05:00
parent 40abc91f46
commit ab57e34cbd
13 changed files with 108 additions and 127 deletions
@@ -35,7 +35,7 @@ mutation hideCallback ($callback_display_id: Int!, $active: Boolean!){
export const removeEdgeMutation = gql`
mutation removeEdgeMutation ($edge_id: Int!){
callbackgraphedge_remove(edge_id: $edge_id) {
callbackgraphedgeRemove(edge_id: $edge_id) {
status
error
}
@@ -44,7 +44,7 @@ mutation removeEdgeMutation ($edge_id: Int!){
export const addEdgeMutation = gql`
mutation addEdgeMutation ($source_id: Int!, $destination_id: Int!, $c2profile: String!){
callbackgraphedge_add(c2profile: $c2profile, destination_id: $destination_id, source_id: $source_id) {
callbackgraphedgeAdd(c2profile: $c2profile, destination_id: $destination_id, source_id: $source_id) {
status
error
}
@@ -202,10 +202,10 @@ export function CallbacksTabsTaskingInputPreMemo(props){
const lastValueTypedBeforeDynamicParamsRef = React.useRef("");
const [getDynamicParams] = useMutation(getDynamicQueryParams, {
onCompleted: (data) => {
if(data.dynamic_query_function.status === "success"){
if(data.dynamicQueryFunction.status === "success"){
try{
if(data.dynamic_query_function.choices && data.dynamic_query_function.choices.length > 0){
const choices = data.dynamic_query_function.choices.filter( c => {
if(data.dynamicQueryFunction.choices && data.dynamicQueryFunction.choices.length > 0){
const choices = data.dynamicQueryFunction.choices.filter( c => {
if(c.toLowerCase().includes(lastValueTypedBeforeDynamicParamsRef.current.toLowerCase())){
return c;
}
@@ -239,7 +239,7 @@ export function CallbacksTabsTaskingInputPreMemo(props){
}
}else{
snackActions.warning(data.dynamic_query_function.error, snackMessageStyles);
snackActions.warning(data.dynamicQueryFunction.error, snackMessageStyles);
}
setBackdropOpen(false);
},
@@ -44,7 +44,7 @@ import PlayCircleFilledTwoToneIcon from '@mui/icons-material/PlayCircleFilledTwo
const ReissueTaskMutationGQL = gql`
mutation reissueTaskMutation($task_id: Int!){
reissue_task(task_id: $task_id){
reissueTask(task_id: $task_id){
status
error
}
@@ -52,7 +52,7 @@ mutation reissueTaskMutation($task_id: Int!){
`;
const ReissueTaskHandlerMutationGQL = gql`
mutation reissueTaskHandlerMutation($task_id: Int!){
reissue_task_handler(task_id: $task_id){
reissueTaskHandler(task_id: $task_id){
status
error
}
@@ -226,10 +226,10 @@ const SideDisplayGeneric = ({toggleViewBrowserScript, toggleSelectAllOutput,
};
const [reissueTask] = useMutation(ReissueTaskMutationGQL, {
onCompleted: data => {
if(data.reissue_task.status === "success"){
if(data.reissueTask.status === "success"){
snackActions.success("Successfully re-issued task to Mythic");
}else{
snackActions.error("Failed to re-issue task to Mythic: " + data.reissue_task.error);
snackActions.error("Failed to re-issue task to Mythic: " + data.reissueTask.error);
}
},
onError: data => {
@@ -239,10 +239,10 @@ const SideDisplayGeneric = ({toggleViewBrowserScript, toggleSelectAllOutput,
});
const [reissueTaskHandler] = useMutation(ReissueTaskHandlerMutationGQL, {
onCompleted: data => {
if(data.reissue_task_handler.status === "success"){
if(data.reissueTaskHandler.status === "success"){
snackActions.success("Successfully resubmitted task for handling");
}else{
snackActions.warning("Failed to resubmit task for handling: " + data.reissue_task_handler.error);
snackActions.warning("Failed to resubmit task for handling: " + data.reissueTaskHandler.error);
}
},
@@ -494,10 +494,10 @@ const SpeedDialDisplayGeneric = ({toggleViewBrowserScript, toggleSelectAllOutput
};
const [reissueTask] = useMutation(ReissueTaskMutationGQL, {
onCompleted: data => {
if(data.reissue_task.status === "success"){
if(data.reissueTask.status === "success"){
snackActions.success("Successfully re-issued task to Mythic");
}else{
snackActions.error("Failed to re-issue task to Mythic: " + data.reissue_task.error);
snackActions.error("Failed to re-issue task to Mythic: " + data.reissueTask.error);
}
},
onError: data => {
@@ -507,10 +507,10 @@ const SpeedDialDisplayGeneric = ({toggleViewBrowserScript, toggleSelectAllOutput
});
const [reissueTaskHandler] = useMutation(ReissueTaskHandlerMutationGQL, {
onCompleted: data => {
if(data.reissue_task_handler.status === "success"){
if(data.reissueTaskHandler.status === "success"){
snackActions.success("Successfully resubmitted task for handling");
}else{
snackActions.warning("Failed to resubmit task for handling: " + data.reissue_task_handler.error);
snackActions.warning("Failed to resubmit task for handling: " + data.reissueTaskHandler.error);
}
},
@@ -763,4 +763,4 @@ const SpeedDialDisplayGeneric = ({toggleViewBrowserScript, toggleSelectAllOutput
</React.Fragment>
)
}
}
@@ -31,7 +31,7 @@ import CloudUploadTwoToneIcon from '@mui/icons-material/CloudUploadTwoTone';
export const getDynamicQueryParamsString = `
mutation getDynamicParamsMutation($callback: Int!, $command: String!, $payload_type: String!, $parameter_name: String!, $other_parameters: jsonb){
dynamic_query_function(callback: $callback, command: $command, payload_type: $payload_type, parameter_name: $parameter_name, other_parameters: $other_parameters){
dynamicQueryFunction(callback: $callback, command: $command, payload_type: $payload_type, parameter_name: $parameter_name, other_parameters: $other_parameters){
status
error
choices
@@ -44,7 +44,7 @@ ${getDynamicQueryParamsString}
`;
const parseTypedArrayMutation = gql`
mutation parseTypedArrayMutation($callback: Int!, $command: String!, $payload_type: String!, $parameter_name: String!, $input_array: [String!]!){
typedarray_parse_function(callback: $callback, command: $command, payload_type: $payload_type, parameter_name: $parameter_name, input_array: $input_array){
typedarrayParseFunction(callback: $callback, command: $command, payload_type: $payload_type, parameter_name: $parameter_name, input_array: $input_array){
status
error
typed_array
@@ -125,19 +125,19 @@ export function TaskParametersDialogRow(props){
const updateToLatestCredential = React.useRef(false);
const [getDynamicParams] = useMutation(getDynamicQueryParams, {
onCompleted: (data) => {
if(data.dynamic_query_function.status === "success"){
if(data.dynamicQueryFunction.status === "success"){
try{
let choicesInUse = [];
if (data.dynamic_query_function.complex_choices !== null &&
data.dynamic_query_function.complex_choices !== undefined &&
data.dynamic_query_function.complex_choices.length > 0) {
if (data.dynamicQueryFunction.complex_choices !== null &&
data.dynamicQueryFunction.complex_choices !== undefined &&
data.dynamicQueryFunction.complex_choices.length > 0) {
usingDynamicParamComplexChoices.current = true;
setChoiceOptions([...data.dynamic_query_function.complex_choices]);
choicesInUse = [...data.dynamic_query_function.complex_choices];
setChoiceOptions([...data.dynamicQueryFunction.complex_choices]);
choicesInUse = [...data.dynamicQueryFunction.complex_choices];
} else {
usingDynamicParamComplexChoices.current = false;
setChoiceOptions([...data.dynamic_query_function.choices]);
choicesInUse = [...data.dynamic_query_function.choices];
setChoiceOptions([...data.dynamicQueryFunction.choices]);
choicesInUse = [...data.dynamicQueryFunction.choices];
}
usingDynamicParamChoices.current = true;
if(props.type === "ChooseOne"){
@@ -220,7 +220,7 @@ export function TaskParametersDialogRow(props){
}
}else{
snackActions.warning(data.dynamic_query_function.error);
snackActions.warning(data.dynamicQueryFunction.error);
}
setBackdropOpen(false);
},
@@ -232,9 +232,9 @@ export function TaskParametersDialogRow(props){
});
const [parseTypedArray] = useMutation(parseTypedArrayMutation, {
onCompleted: (data) => {
if(data.typedarray_parse_function.status === "success"){
if(data.typedarrayParseFunction.status === "success"){
try{
let newTypedArrayValue = [...data.typedarray_parse_function.typed_array.reduce( (prev, cur) => {
let newTypedArrayValue = [...data.typedarrayParseFunction.typed_array.reduce( (prev, cur) => {
if(cur){
return [...prev, cur];
}
@@ -250,7 +250,7 @@ export function TaskParametersDialogRow(props){
}
}else{
snackActions.warning(data.typedarray_parse_function.error);
snackActions.warning(data.typedarrayParseFunction.error);
}
setBackdropOpen(false);
},
@@ -1582,12 +1582,12 @@ function EventDetailsPayloadsTable({payloads, deletePayload}){
const [downloadBulk] = useMutation(downloadBulkQuery, {
onCompleted: (data) => {
snackActions.dismiss();
if(data.download_bulk.status === "success"){
if(data.downloadBulk.status === "success"){
snackActions.success(<SnackMessage
file_id={data.download_bulk.file_id}
/>, {toastId: data.download_bulk.file_id, autoClose: false, closeOnClick: false});
file_id={data.downloadBulk.file_id}
/>, {toastId: data.downloadBulk.file_id, autoClose: false, closeOnClick: false});
}else{
snackActions.error(data.download_bulk.error);
snackActions.error(data.downloadBulk.error);
}
},
onError: (data) => {
@@ -1721,12 +1721,12 @@ function EventDetailsFilesTable({files}){
const [downloadBulk] = useMutation(downloadBulkQuery, {
onCompleted: (data) => {
snackActions.dismiss();
if(data.download_bulk.status === "success"){
if(data.downloadBulk.status === "success"){
snackActions.success(<SnackMessage
file_id={data.download_bulk.file_id}
/>, {toastId: data.download_bulk.file_id, autoClose: false, closeOnClick: false});
file_id={data.downloadBulk.file_id}
/>, {toastId: data.downloadBulk.file_id, autoClose: false, closeOnClick: false});
}else{
snackActions.error(data.download_bulk.error);
snackActions.error(data.downloadBulk.error);
}
},
onError: (data) => {
@@ -1796,4 +1796,4 @@ function EventDetailsFilesTable({files}){
</Table>
</>
)
}
}
@@ -71,7 +71,7 @@ mutation deleteSavedInstance($name: String!, $c2_profile_id: Int!){
`;
const createInstanceMutation = gql`
mutation createNewC2Instance($instance_name: String!, $c2_instance: String!, $c2profile_id: Int!){
create_c2_instance(c2_instance: $c2_instance, instance_name: $instance_name, c2profile_id: $c2profile_id){
createC2Instance(c2_instance: $c2_instance, instance_name: $instance_name, c2profile_id: $c2profile_id){
status
error
}
@@ -79,7 +79,7 @@ mutation createNewC2Instance($instance_name: String!, $c2_instance: String!, $c2
`;
const importInstanceMutation = gql`
mutation importNewC2Instance($c2_instance: jsonb!, $instance_name: String!, $c2profile_name: String!){
import_c2_instance(
importC2Instance(
c2_instance: $c2_instance,
instance_name: $instance_name
c2profile_name: $c2profile_name
@@ -207,10 +207,10 @@ export function C2ProfileSavedInstancesDialog(props) {
});
const [createInstance] = useMutation(createInstanceMutation, {
onCompleted: (data) => {
if(data.create_c2_instance.status === "success"){
if(data.createC2Instance.status === "success"){
snackActions.success("Successfully created instance");
}else{
snackActions.error("Failed to create instance: " + data.create_c2_instance.error);
snackActions.error("Failed to create instance: " + data.createC2Instance.error);
}
props.onClose();
@@ -221,10 +221,10 @@ export function C2ProfileSavedInstancesDialog(props) {
});
const [importInstance] = useMutation(importInstanceMutation, {
onCompleted: (data) => {
if(data.import_c2_instance.status === "success"){
if(data.importC2Instance.status === "success"){
snackActions.success("Successfully imported instance")
} else {
snackActions.error(data.import_c2_instance.error);
snackActions.error(data.importC2Instance.error);
}
},
onError: (error) => {
@@ -433,4 +433,3 @@ export function C2ProfileSavedInstancesDialog(props) {
</React.Fragment>
);
}
@@ -9,7 +9,7 @@ import {ResponseDisplayPlaintext} from "../Callbacks/ResponseDisplayPlaintext";
const checkPayloadConfigurationQuery = gql`
query checkPayloadConfigurationQuery($uuid: String!) {
config_check(uuid: $uuid) {
configCheck(uuid: $uuid) {
status
error
output
@@ -22,11 +22,11 @@ export function PayloadConfigCheckDialog(props) {
const { loading, error } = useQuery(checkPayloadConfigurationQuery, {
variables: {uuid: props.uuid},
onCompleted: data => {
if(data.config_check.status === "success"){
setMessage(data.config_check.output);
if(data.configCheck.status === "success"){
setMessage(data.configCheck.output);
}else{
snackActions.warning(data.config_check.error);
setMessage("Error!\n" + data.config_check.error);
snackActions.warning(data.configCheck.error);
setMessage("Error!\n" + data.configCheck.error);
}
},
@@ -60,4 +60,3 @@ export function PayloadConfigCheckDialog(props) {
</React.Fragment>
);
}
@@ -9,7 +9,7 @@ import {ResponseDisplayPlaintext} from "../Callbacks/ResponseDisplayPlaintext";
const generateRedirectRulesMutation = gql`
query generateRedirectRulesMutation($uuid: String!) {
redirect_rules(uuid: $uuid) {
redirectRules(uuid: $uuid) {
status
error
output
@@ -22,11 +22,11 @@ export function PayloadRedirectRulesDialog(props) {
const { loading, error } = useQuery(generateRedirectRulesMutation, {
variables: {uuid: props.uuid},
onCompleted: data => {
if(data.redirect_rules.status === "success"){
setMessage(data.redirect_rules.output);
if(data.redirectRules.status === "success"){
setMessage(data.redirectRules.output);
}else{
snackActions.warning(data.redirect_rules.error);
setMessage("Error!\n" + data.redirect_rules.error);
snackActions.warning(data.redirectRules.error);
setMessage("Error!\n" + data.redirectRules.error);
}
},
@@ -60,4 +60,3 @@ export function PayloadRedirectRulesDialog(props) {
</React.Fragment>
);
}
@@ -50,7 +50,7 @@ import {Dropdown, DropdownMenuItem, DropdownNestedMenuItem} from "../../MythicCo
const rebuildPayloadMutation = gql`
mutation triggerRebuildMutation($uuid: String!) {
rebuild_payload(uuid: $uuid) {
rebuildPayload(uuid: $uuid) {
status
error
uuid
@@ -86,10 +86,10 @@ export function PayloadsTableRow(props){
const dropdownAnchorRef = useRef(null);
const [triggerRebuild] = useMutation(rebuildPayloadMutation, {
onCompleted: (data) => {
if(data.rebuild_payload.status === "success"){
if(data.rebuildPayload.status === "success"){
snackActions.success("Successfully triggered rebuild");
} else {
snackActions.error("Failed to build:\n" + data.rebuild_payload.error);
snackActions.error("Failed to build:\n" + data.rebuildPayload.error);
}
},
@@ -515,4 +515,3 @@ export function PayloadsTableRow(props){
) : null
)
}
@@ -38,7 +38,7 @@ import {FileDownloadLinkWithAuth} from "../../utilities/FileDownloadWithAuth";
export const downloadBulkQuery = gql`
mutation downloadBulkMutation($files: [String!]!){
download_bulk(files: $files){
downloadBulk(files: $files){
status
error
file_id
@@ -144,12 +144,12 @@ export function FileMetaDownloadTable(props){
const [downloadBulk] = useMutation(downloadBulkQuery, {
onCompleted: (data) => {
snackActions.dismiss();
if(data.download_bulk.status === "success"){
if(data.downloadBulk.status === "success"){
snackActions.success(<SnackMessage
file_id={data.download_bulk.file_id}
/>, {toastId: data.download_bulk.file_id, autoClose: false, closeOnClick: false});
file_id={data.downloadBulk.file_id}
/>, {toastId: data.downloadBulk.file_id, autoClose: false, closeOnClick: false});
}else{
snackActions.error(data.download_bulk.error);
snackActions.error(data.downloadBulk.error);
}
},
onError: (data) => {
@@ -535,8 +535,8 @@ export function FileMetaUploadTable(props){
const [downloadBulk] = useMutation(downloadBulkQuery, {
onCompleted: (data) => {
snackActions.dismiss();
if(data.download_bulk.status === "success"){
snackActions.success(<MythicSnackDownload title="Download Zip File" file_id={data.download_bulk.file_id} />, {toastId: data.download_bulk.file_id, autoClose: false, closeOnClick: false});
if(data.downloadBulk.status === "success"){
snackActions.success(<MythicSnackDownload title="Download Zip File" file_id={data.downloadBulk.file_id} />, {toastId: data.downloadBulk.file_id, autoClose: false, closeOnClick: false});
}else{
snackActions.error(data.error);
}
@@ -1179,10 +1179,10 @@ export function FileMetaEventingWorkflowsTable(props){
const [downloadBulk] = useMutation(downloadBulkQuery, {
onCompleted: (data) => {
snackActions.dismiss();
if(data.download_bulk.status === "success"){
snackActions.success(<MythicSnackDownload title="Download Zip File" file_id={data.download_bulk.file_id} />, {toastId: data.download_bulk.file_id, autoClose: false, closeOnClick: false});
if(data.downloadBulk.status === "success"){
snackActions.success(<MythicSnackDownload title="Download Zip File" file_id={data.downloadBulk.file_id} />, {toastId: data.downloadBulk.file_id, autoClose: false, closeOnClick: false});
}else{
snackActions.error(data.download_bulk.error);
snackActions.error(data.downloadBulk.error);
}
},
onError: (data) => {
@@ -1466,4 +1466,4 @@ function FileMetaEventingWorkflowsTableRow(props){
) : null }
</React.Fragment>
)
}
}
+12 -13
View File
@@ -32,7 +32,7 @@ type Query {
}
type Mutation {
callbackgraphedge_add(
callbackgraphedgeAdd(
source_id: Int!
destination_id: Int!
c2profile: String!
@@ -40,13 +40,13 @@ type Mutation {
}
type Mutation {
callbackgraphedge_remove(
callbackgraphedgeRemove(
edge_id: Int!
): CallbackgraphedgeRemoveOutput
}
type Query {
config_check(
configCheck(
uuid: String!
): ConfigCheckOutput
}
@@ -207,7 +207,7 @@ type Mutation {
}
type Mutation {
create_c2_instance(
createC2Instance(
c2_instance: String!
instance_name: String!
c2profile_id: Int!
@@ -263,7 +263,7 @@ type Mutation {
}
type Mutation {
download_bulk(
downloadBulk(
files: [String!]!
): BulkDownloadOutput
}
@@ -277,7 +277,7 @@ type Mutation {
}
type Mutation {
dynamic_query_function(
dynamicQueryFunction(
command: String!
parameter_name: String!
payload_type: String!
@@ -428,7 +428,7 @@ type Mutation {
}
type Mutation {
import_c2_instance(
importC2Instance(
c2_instance: jsonb!
instance_name: String!
c2profile_name: String!
@@ -446,25 +446,25 @@ type Mutation {
}
type Mutation {
rebuild_payload(
rebuildPayload(
uuid: String!
): rebuildOutput
}
type Query {
redirect_rules(
redirectRules(
uuid: String!
): redirectOutput
}
type Mutation {
reissue_task(
reissueTask(
task_id: Int!
): ReissueTaskOutput
}
type Mutation {
reissue_task_handler(
reissueTaskHandler(
task_id: Int!
): ReissueTaskHandlerOutput
}
@@ -511,7 +511,7 @@ type Mutation {
}
type Mutation {
typedarray_parse_function(
typedarrayParseFunction(
command: String!
parameter_name: String!
payload_type: String!
@@ -1324,4 +1324,3 @@ type custombrowserExportFunctionOutput {
status: String!
error: String
}
+12 -12
View File
@@ -57,7 +57,7 @@ actions:
- role: mythic_admin
- role: developer
comment: Using the UUID of a payload, ask's that payload's C2 Profiles to generate a sample message of what traffic would look like
- name: callbackgraphedge_add
- name: callbackgraphedgeAdd
definition:
kind: synchronous
handler: '{{MYTHIC_ACTIONS_URL_BASE}}/callbackgraphedge_add_webhook'
@@ -68,7 +68,7 @@ actions:
- role: mythic_admin
- role: developer
comment: Adds an active graph edge between the two callbacks specified by their ID via the C2 Profile specified by its Name
- name: callbackgraphedge_remove
- name: callbackgraphedgeRemove
definition:
kind: synchronous
handler: '{{MYTHIC_ACTIONS_URL_BASE}}/callbackgraphedge_remove_webhook'
@@ -79,7 +79,7 @@ actions:
- role: mythic_admin
- role: developer
comment: Removes an active graph edge specified by its ID
- name: config_check
- name: configCheck
definition:
kind: ""
handler: '{{MYTHIC_ACTIONS_URL_BASE}}/config_check_webhook'
@@ -292,7 +292,7 @@ actions:
- role: operation_admin
- role: operator
comment: Creates a new task in Mythic for the specified callback_display_id or callback_display_ids
- name: create_c2_instance
- name: createC2Instance
definition:
kind: synchronous
handler: '{{MYTHIC_ACTIONS_URL_BASE}}/create_c2parameter_instance_webhook'
@@ -377,7 +377,7 @@ actions:
permissions:
- role: mythic_admin
comment: Delete the specified tasks and callbacks by their display IDs
- name: download_bulk
- name: downloadBulk
definition:
kind: synchronous
handler: '{{MYTHIC_ACTIONS_URL_BASE}}/download_bulk_webhook'
@@ -398,7 +398,7 @@ actions:
- role: operation_admin
- role: mythic_admin
- role: developer
- name: dynamic_query_function
- name: dynamicQueryFunction
definition:
kind: synchronous
handler: '{{MYTHIC_ACTIONS_URL_BASE}}/dynamic_query_webhook'
@@ -633,7 +633,7 @@ actions:
- role: operation_admin
- role: mythic_admin
- role: developer
- name: import_c2_instance
- name: importC2Instance
definition:
kind: synchronous
handler: '{{MYTHIC_ACTIONS_URL_BASE}}/import_c2parameter_instance_webhook'
@@ -665,7 +665,7 @@ actions:
- role: operation_admin
- role: mythic_admin
- role: developer
- name: rebuild_payload
- name: rebuildPayload
definition:
kind: synchronous
handler: '{{MYTHIC_ACTIONS_URL_BASE}}/rebuild_webhook'
@@ -675,7 +675,7 @@ actions:
- role: operation_admin
- role: mythic_admin
- role: developer
- name: redirect_rules
- name: redirectRules
definition:
kind: ""
handler: '{{MYTHIC_ACTIONS_URL_BASE}}/redirect_rules_webhook'
@@ -686,7 +686,7 @@ actions:
- role: operation_admin
- role: mythic_admin
- role: developer
- name: reissue_task
- name: reissueTask
definition:
kind: synchronous
handler: '{{MYTHIC_ACTIONS_URL_BASE}}/reissue_task_webhook'
@@ -696,7 +696,7 @@ actions:
- role: operation_admin
- role: mythic_admin
- role: developer
- name: reissue_task_handler
- name: reissueTaskHandler
definition:
kind: synchronous
handler: '{{MYTHIC_ACTIONS_URL_BASE}}/reissue_task_handler_webhook'
@@ -769,7 +769,7 @@ actions:
- role: operation_admin
- role: mythic_admin
- role: developer
- name: typedarray_parse_function
- name: typedarrayParseFunction
definition:
kind: synchronous
handler: '{{MYTHIC_ACTIONS_URL_BASE}}/typedarray_parse_webhook'
+14 -28
View File
@@ -20,7 +20,6 @@ var (
ErrBearerInvalidValue = errors.New("Authorization bearer value is invalid")
ErrMissingAuthorizationBearerToken = errors.New("Missing Authorization Bearer token")
ErrMissingAuthorizationHeader = errors.New("Missing Authorization header")
ErrLegacyAPITokenJWTDeprecated = errors.New("Legacy JWT API tokens are no longer accepted. Generate and use a new API token.")
)
const (
@@ -31,9 +30,7 @@ const (
)
var (
// LegacyAPITokenJWTCutoff // int64(time.Now().Unix()) for a specific date via iat
LegacyAPITokenJWTCutoff = int64(1776211200) // April 15, 2026
SQLGetIDForActiveToken = `SELECT
SQLGetIDForActiveToken = `SELECT
apitokens.id, apitokens.operator_id, apitokens.name, apitokens.active, apitokens.token_value, apitokens.deleted,
apitokens.eventstepinstance_id, apitokens.token_type, apitokens.scopes,
operator.username "operator.username",
@@ -86,21 +83,17 @@ func validateAndSetAPITokenContext(c *gin.Context, tokenString string) error {
return err
}
if !databaseApiToken.Active {
go func(token databaseStructs.Apitokens) {
go rabbitmq.SendAllOperationsMessage(fmt.Sprintf("Deactivated APIToken, %s, for user %s (%s) attempted to be used",
token.Name, token.Operator.Username, token.Operator.AccountType),
int(token.Operator.CurrentOperationID.Int64), token.Name+fmt.Sprintf("%d", token.ID)+token.Operator.Username,
database.MESSAGE_LEVEL_API, true)
}(databaseApiToken)
go rabbitmq.SendAllOperationsMessage(fmt.Sprintf("Deactivated APIToken, %s, for user %s (%s) attempted to be used",
databaseApiToken.Name, databaseApiToken.Operator.Username, databaseApiToken.Operator.AccountType),
int(databaseApiToken.Operator.CurrentOperationID.Int64), databaseApiToken.Name+fmt.Sprintf("%d", databaseApiToken.ID)+databaseApiToken.Operator.Username,
database.MESSAGE_LEVEL_API, true)
return errors.New("Deactivated APIToken attempted to be used")
}
if databaseApiToken.Deleted {
go func(token databaseStructs.Apitokens) {
go rabbitmq.SendAllOperationsMessage(fmt.Sprintf("Deleted APIToken, \"%s\", for user %s (%s) attempted to be used",
token.Name, token.Operator.Username, token.Operator.AccountType),
int(token.Operator.CurrentOperationID.Int64), token.Name+fmt.Sprintf("%d", token.ID)+token.Operator.Username,
database.MESSAGE_LEVEL_API, true)
}(databaseApiToken)
go rabbitmq.SendAllOperationsMessage(fmt.Sprintf("Deleted APIToken, \"%s\", for user %s (%s) attempted to be used",
databaseApiToken.Name, databaseApiToken.Operator.Username, databaseApiToken.Operator.AccountType),
int(databaseApiToken.Operator.CurrentOperationID.Int64), databaseApiToken.Name+fmt.Sprintf("%d", databaseApiToken.ID)+databaseApiToken.Operator.Username,
database.MESSAGE_LEVEL_API, true)
return errors.New("Deleted APIToken attempted to be used")
}
source := c.GetHeader("MythicSource")
@@ -134,10 +127,10 @@ func GetClaims(c *gin.Context) (*mythicjwt.CustomClaims, error) {
return nil, err
}
if looksLikeOpaqueAPIToken(tokenString) {
apiTokenErr := validateAndSetAPITokenContext(c, tokenString)
if apiTokenErr != nil {
logging.LogError(err, "failed to parse jwt")
return nil, apiTokenErr
err = validateAndSetAPITokenContext(c, tokenString)
if err != nil {
logging.LogError(err, "failed to validate and set apitoken")
return nil, err
}
if existingClaims, exists := c.Get(ContextKeyClaims); exists {
if typedClaims, ok := existingClaims.(*mythicjwt.CustomClaims); ok {
@@ -155,16 +148,9 @@ func GetClaims(c *gin.Context) (*mythicjwt.CustomClaims, error) {
return utils.MythicConfig.JWTSecret, nil
})
if err != nil {
logging.LogError(err, "failed to parse jwt")
logging.LogError(err, "failed to parse and validate jwt")
return nil, err
}
if claims.IssuedAt < LegacyAPITokenJWTCutoff {
logging.LogError(ErrLegacyAPITokenJWTDeprecated,
"auth_method", claims.AuthMethod,
"issued_at", claims.IssuedAt,
"cutoff", LegacyAPITokenJWTCutoff)
return nil, ErrLegacyAPITokenJWTDeprecated
}
c.Set(ContextKeyUserID, claims.UserID)
c.Set(ContextKeyClaims, &claims)
return &claims, nil