Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion .devcontainer/chefs_local/local.sample.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@
},
"encryption": {
"proxy": "352f7c24819086bf3df5a38c1a40586045f73e0007440c9d27d59ee8560e3fe7"
}
},
"submissionTokenKey": "REPLACE_ME_WITH_32_PLUS_RANDOM_BYTES_e.g._openssl_rand_-hex_32"
},
"eventStreamService": {
"servers": "localhost:4222,localhost:4223,localhost:4224",
Expand Down
17 changes: 15 additions & 2 deletions .github/actions/deploy-to-environment/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,21 @@ runs:

- name: Deploy App Secret
shell: bash
run: >-
oc get --namespace ${{ inputs.namespace_prefix }}-${{ inputs.namespace_environment }} secret chefs-${{ inputs.job_name }}-secret || oc process --namespace ${{ inputs.namespace_prefix }}-${{ inputs.namespace_environment }} -f openshift/app.secret.yaml -p INSTANCE=${{ inputs.job_name }} | oc create --namespace ${{ inputs.namespace_prefix }}-${{ inputs.namespace_environment }} -f -
env:
NAMESPACE: ${{ inputs.namespace_prefix }}-${{ inputs.namespace_environment }}
JOB_NAME: ${{ inputs.job_name }}
run: |
set -euo pipefail

SECRET_NAME="chefs-${JOB_NAME}-secret"
if ! oc get --namespace "${NAMESPACE}" secret "${SECRET_NAME}" >/dev/null 2>&1; then
oc process --namespace "${NAMESPACE}" -f openshift/app.secret.yaml -p INSTANCE="${JOB_NAME}" | oc create --namespace "${NAMESPACE}" -f -
fi

SUBMISSION_TOKEN_KEY="$(oc get --namespace "${NAMESPACE}" secret "${SECRET_NAME}" -o jsonpath='{.data.submissiontokenkey}' | base64 -d || true)"
if [[ ${#SUBMISSION_TOKEN_KEY} -lt 32 ]]; then
oc patch --namespace "${NAMESPACE}" secret "${SECRET_NAME}" --type merge -p "{\"stringData\":{\"submissiontokenkey\":\"$(openssl rand -hex 32)\"}}"
fi

- name: Deploy app ConfigMaps
shell: bash
Expand Down
4 changes: 3 additions & 1 deletion app/config/custom-environment-variables.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@
"port": "SERVER_PORT",
"encryption": {
"proxy": "SERVER_ENCRYPTION_PROXY"
}
},
"submissionTokenKey": "SUBMISSION_TOKEN_KEY",
"submissionTokenTtlMinutes": "SUBMISSION_TOKEN_TTL_MINUTES"
},
"eventStreamService": {
"servers": "EVENTSTREAMSERVICE_SERVERS",
Expand Down
3 changes: 2 additions & 1 deletion app/config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@
},
"encryption": {
"proxy": "352f7c24819086bf3df5a38c1a40586045f73e0007440c9d27d59ee8560e3fe7"
}
},
"submissionTokenTtlMinutes": "15"
},
"eventStreamService": {
"servers": "localhost:4222,localhost:4223,localhost:4224",
Expand Down
3 changes: 2 additions & 1 deletion app/config/test.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
"oidc": {
"clientSecret": "password"
},
"logLevel": "silent"
"logLevel": "silent",
"submissionTokenKey": "test-only-submission-token-key-32-bytes-min-do-not-use-anywhere-else"
},
"serviceClient": {
"commonServices": {
Expand Down
3 changes: 3 additions & 0 deletions app/frontend/src/components/designer/FormDesigner.vue
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,9 @@ async function schemaCreateNew() {
enableSubmitterRevision: form.value.enableSubmitterRevision,
showAssigneeInSubmissionsTable: form.value.showAssigneeInSubmissionsTable,
showSubmissionConfirmation: form.value.showSubmissionConfirmation,
enableSubmitterEmailReceipt: form.value.enableSubmitterEmailReceipt,
enableSubmissionUrlSharing: form.value.enableSubmissionUrlSharing,
hideSubmissionContentOnSuccess: form.value.hideSubmissionContentOnSuccess,
submissionReceivedEmails: form.value.submissionReceivedEmails,
submissionPackageSettings: form.value.submissionPackageSettings,
reminder_enabled: false,
Expand Down
187 changes: 104 additions & 83 deletions app/frontend/src/components/designer/FormViewer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const { t, locale } = useI18n({ useScope: 'global' });

const router = useRouter();

const emit = defineEmits(['submission-updated']);
const emit = defineEmits(['submission-updated', 'access-denied']);

const properties = defineProps({
displayTitle: {
Expand Down Expand Up @@ -365,18 +365,34 @@ async function getFormData() {
permissions.value = permRes.data[0] ? permRes.data[0].permissions : [];
}
} catch (error) {
notificationStore.addNotification({
text: t('trans.formViewer.getUsersSubmissionsErrMsg'),
consoleError: t('trans.formViewer.getUsersSubmissionsConsoleErrMsg', {
submissionId: properties.submissionId,
error: error,
}),
});
handleGetFormDataError(error);
} finally {
loadingSubmission.value = false;
}
}

// Sharing-off + 401 is the "forwarded success URL, viewer isn't on the form
// team" case. Success.vue listens on `access-denied` and falls back to the
// static confirmation block; suppressing the notification (and the follow-on
// calls) avoids a burst of misleading errors for what is really a known
// "you can't view this submission" state.
function handleGetFormDataError(error) {
if (
error.response?.status === 401 &&
formStore.form.enableSubmissionUrlSharing === false
) {
emit('access-denied');
return;
}
notificationStore.addNotification({
text: t('trans.formViewer.getUsersSubmissionsErrMsg'),
consoleError: t('trans.formViewer.getUsersSubmissionsConsoleErrMsg', {
submissionId: properties.submissionId,
error: error,
}),
});
}

async function setProxyHeaders() {
try {
let response = await formService.getProxyHeaders({
Expand All @@ -397,87 +413,92 @@ async function setProxyHeaders() {
// Get the form definition/schema
async function getFormSchema() {
try {
let response = undefined;
if (properties.versionId) {
versionIdToSubmitTo.value = properties.versionId;
// If getting for a specific older version of the form
response = await formService.readVersion(
properties.formId,
properties.versionId
);
if (!response.data || !response.data.schema) {
throw new Error(
t('trans.formViewer.readVersionErrMsg', {
versionId: properties.versionId,
})
);
}
form.value = response.data;
version.value = response.data.version;
formSchema.value = response.data.schema;
} else if (properties.draftId) {
// If getting for a specific draft version of the form for preview
response = await formService.readDraft(
properties.formId,
properties.draftId
);
if (!response.data || !response.data.schema) {
throw new Error(
t('trans.formViewer.readDraftErrMsg', {
draftId: properties.draftId,
})
);
}
form.value = response.data;
formSchema.value = response.data.schema;
} else {
// If getting the HEAD form version (IE making a new submission)
response = await formService.readPublished(properties.formId);
if (
!response ||
!response.data ||
!response.data.versions ||
!response.data.versions[0]
) {
router.push({
name: 'Alert',
query: {
text: t('trans.formViewer.alertRouteMsg'),
type: 'info',
},
});
return;
}
form.value = response.data;
version.value = response.data.versions[0].version;
versionIdToSubmitTo.value = response.data.versions[0].id;
formSchema.value = response.data.versions[0].schema;

if (response.data.schedule && response.data.schedule.expire) {
let formScheduleStatus = response.data.schedule;
isFormScheduleExpired.value = formScheduleStatus.expire;
isLateSubmissionAllowed.value = formScheduleStatus.allowLateSubmissions;
}
await loadFormByVersion();
return;
}
} catch (error) {
if (authenticated.value) {
// if 401 error, the user is not authorized to view the form
if (error.response && error.response.status === 401) {
isAuthorized.value = false;
} else {
// throw a generic error message
notificationStore.addNotification({
text: t('trans.formViewer.fecthingFormErrMsg'),
consoleError: t('trans.formViewer.fecthingFormConsoleErrMsg', {
versionId: properties.versionId,
error: error,
}),
});
}
if (properties.draftId) {
await loadFormByDraft();
return;
}
await loadPublishedForm();
} catch (error) {
handleGetFormSchemaError(error);
}
}

async function loadFormByVersion() {
versionIdToSubmitTo.value = properties.versionId;
const response = await formService.readVersion(
properties.formId,
properties.versionId
);
if (!response.data || !response.data.schema) {
throw new Error(
t('trans.formViewer.readVersionErrMsg', {
versionId: properties.versionId,
})
);
}
form.value = response.data;
version.value = response.data.version;
formSchema.value = response.data.schema;
}

async function loadFormByDraft() {
const response = await formService.readDraft(
properties.formId,
properties.draftId
);
if (!response.data || !response.data.schema) {
throw new Error(
t('trans.formViewer.readDraftErrMsg', {
draftId: properties.draftId,
})
);
}
form.value = response.data;
formSchema.value = response.data.schema;
}

async function loadPublishedForm() {
const response = await formService.readPublished(properties.formId);
if (!response?.data?.versions?.[0]) {
router.push({
name: 'Alert',
query: {
text: t('trans.formViewer.alertRouteMsg'),
type: 'info',
},
});
return;
}
form.value = response.data;
version.value = response.data.versions[0].version;
versionIdToSubmitTo.value = response.data.versions[0].id;
formSchema.value = response.data.versions[0].schema;
if (response.data.schedule?.expire) {
isFormScheduleExpired.value = response.data.schedule.expire;
isLateSubmissionAllowed.value = response.data.schedule.allowLateSubmissions;
}
}

function handleGetFormSchemaError(error) {
// Silent for anonymous viewers (public forms rendered without auth).
if (!authenticated.value) return;
if (error.response?.status === 401) {
isAuthorized.value = false;
return;
}
notificationStore.addNotification({
text: t('trans.formViewer.fecthingFormErrMsg'),
consoleError: t('trans.formViewer.fecthingFormConsoleErrMsg', {
versionId: properties.versionId,
error: error,
}),
});
}

function isProcessingMultiUpload(e) {
block.value = e;
}
Expand Down
Loading
Loading