Skip to content

Commit

Permalink
feat: CE-518-Prevent-user-from-editing-closed-complaints (#882)
Browse files Browse the repository at this point in the history
Co-authored-by: dmitri-korin-bcps <[email protected]>
  • Loading branch information
dk-bcps and dk-bcps authored Jan 21, 2025
1 parent 5003168 commit 2887d2b
Show file tree
Hide file tree
Showing 30 changed files with 219 additions and 75 deletions.
99 changes: 48 additions & 51 deletions .github/workflows/.dbdeployer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ on:
inputs: ### Required
directory:
description: Crunchy Chart directory
default: 'charts/crunchy'
default: "charts/crunchy"
required: false
type: string
oc_server:
default: https://api.silver.devops.gov.bc.ca:6443
description: 'OpenShift server'
description: "OpenShift server"
required: false
type: string
environment:
Expand All @@ -21,29 +21,29 @@ on:
description: Cluster environment name, should be dev,test,prod
required: false
type: string
default: 'dev'
default: "dev"
s3_enabled:
description: Enable S3 backups
required: false
default: true
type: boolean
values:
description: 'Values file'
default: 'values.yaml'
description: "Values file"
default: "values.yaml"
required: false
type: string
app_values:
description: 'App specific values file which is present inside charts/app'
default: 'values.yaml'
description: "App specific values file which is present inside charts/app"
default: "values.yaml"
required: false
type: string
enabled:
description: 'Enable the deployment of the crunchy database, easy switch to turn it on/off'
description: "Enable the deployment of the crunchy database, easy switch to turn it on/off"
default: true
required: false
type: boolean
timeout-minutes:
description: 'Timeout minutes'
description: "Timeout minutes"
default: 20
required: false
type: number
Expand All @@ -52,8 +52,8 @@ on:
required: false
type: string
params:
description: 'Extra parameters to pass to helm upgrade'
default: ''
description: "Extra parameters to pass to helm upgrade"
default: ""
required: false
type: string
secrets:
Expand Down Expand Up @@ -146,47 +146,44 @@ jobs:
shell: bash
if: github.event_name == 'pull_request'
run: |
echo 'Adding PR specific user to Crunchy DB'
NEW_USER='{"databases":["app-${{ github.event.number }}"],"name":"app-${{ github.event.number }}"}'
CURRENT_USERS=$(oc get PostgresCluster/postgres-crunchy-${{ inputs.cluster_environment }} -o json | jq '.spec.users')
echo "${CURRENT_USERS}"
echo 'Adding PR specific user to Crunchy DB'
NEW_USER='{"databases":["app-${{ github.event.number }}"],"name":"app-${{ github.event.number }}"}'
CURRENT_USERS=$(oc get PostgresCluster/postgres-crunchy-${{ inputs.cluster_environment }} -o json | jq '.spec.users')
echo "${CURRENT_USERS}"
# check if current_users already contains the new_user
if echo "${CURRENT_USERS}" | jq -e ".[] | select(.name == \"app-${{ github.event.number }}\")" > /dev/null; then
echo "User already exists"
exit 0
fi
UPDATED_USERS=$(echo "$CURRENT_USERS" | jq --argjson NEW_USER "$NEW_USER" '. + [$NEW_USER]')
echo "$UPDATED_USERS"
PATCH_JSON=$(jq -n --argjson users "$UPDATED_USERS" '{"spec": {"users": $users}}')
echo "$PATCH_JSON"
oc patch PostgresCluster/postgres-crunchy-${{ inputs.cluster_environment }} --type=merge -p "${PATCH_JSON}"
# wait for sometime as it takes time to create the user, query the secret and check if it is created, otherwise wait in a loop for 10 rounds
for i in {1..10}; do
if oc get secret postgres-crunchy-${{ inputs.cluster_environment }}-pguser-app-${{ github.event.number }} -o jsonpath='{.metadata.name}' > /dev/null; then
echo "Secret created"
break
else
echo "Secret not created, waiting for 60 seconds"
sleep 60
fi
done
# check if current_users already contains the new_user
if echo "${CURRENT_USERS}" | jq -e ".[] | select(.name == \"app-${{ github.event.number }}\")" > /dev/null; then
echo "User already exists"
exit 0
fi
# Add public schema and grant to PR user
# get primary crunchy pod and remove the role and db
CRUNCHY_PG_PRIMARY_POD_NAME=$(oc get pods -l postgres-operator.crunchydata.com/role=master -o json | jq -r '.items[0].metadata.name')
echo "${CRUNCHY_PG_PRIMARY_POD_NAME}"
oc exec "${CRUNCHY_PG_PRIMARY_POD_NAME}" -- psql -d "app-${{ github.event.number }}" -c "CREATE SCHEMA IF NOT EXISTS public;"
oc exec "${CRUNCHY_PG_PRIMARY_POD_NAME}" -- psql -d "app-${{ github.event.number }}" -c "GRANT ALL PRIVILEGES ON SCHEMA public TO \"app-${{ github.event.number }}\";"
oc exec "${CRUNCHY_PG_PRIMARY_POD_NAME}" -- psql -d "app-${{ github.event.number }}" -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO \"app-${{ github.event.number }}\";"
oc exec "${CRUNCHY_PG_PRIMARY_POD_NAME}" -- psql -d "app-${{ github.event.number }}" -c "GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO \"app-${{ github.event.number }}\";"
oc exec "${CRUNCHY_PG_PRIMARY_POD_NAME}" -- psql -d "app-${{ github.event.number }}" -c "GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public TO \"app-${{ github.event.number }}\";"
oc exec "${CRUNCHY_PG_PRIMARY_POD_NAME}" -- psql -d "app-${{ github.event.number }}" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON TABLES TO \"app-${{ github.event.number }}\";"
oc exec "${CRUNCHY_PG_PRIMARY_POD_NAME}" -- psql -d "app-${{ github.event.number }}" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON SEQUENCES TO \"app-${{ github.event.number }}\";"
oc exec "${CRUNCHY_PG_PRIMARY_POD_NAME}" -- psql -d "app-${{ github.event.number }}" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON FUNCTIONS TO \"app-${{ github.event.number }}\";"
# TODO: remove these
UPDATED_USERS=$(echo "$CURRENT_USERS" | jq --argjson NEW_USER "$NEW_USER" '. + [$NEW_USER]')
echo "$UPDATED_USERS"
PATCH_JSON=$(jq -n --argjson users "$UPDATED_USERS" '{"spec": {"users": $users}}')
echo "$PATCH_JSON"
oc patch PostgresCluster/postgres-crunchy-${{ inputs.cluster_environment }} --type=merge -p "${PATCH_JSON}"
# wait for sometime as it takes time to create the user, query the secret and check if it is created, otherwise wait in a loop for 10 rounds
for i in {1..10}; do
if oc get secret postgres-crunchy-${{ inputs.cluster_environment }}-pguser-app-${{ github.event.number }} -o jsonpath='{.metadata.name}' > /dev/null; then
echo "Secret created"
break
else
echo "Secret not created, waiting for 60 seconds"
sleep 60
fi
done
# Add public schema and grant to PR user
# get primary crunchy pod and remove the role and db
CRUNCHY_PG_PRIMARY_POD_NAME=$(oc get pods -l postgres-operator.crunchydata.com/role=master -o json | jq -r '.items[0].metadata.name')
echo "${CRUNCHY_PG_PRIMARY_POD_NAME}"
oc exec "${CRUNCHY_PG_PRIMARY_POD_NAME}" -- psql -d "app-${{ github.event.number }}" -c "CREATE SCHEMA IF NOT EXISTS public;"
oc exec "${CRUNCHY_PG_PRIMARY_POD_NAME}" -- psql -d "app-${{ github.event.number }}" -c "GRANT ALL PRIVILEGES ON SCHEMA public TO \"app-${{ github.event.number }}\";"
oc exec "${CRUNCHY_PG_PRIMARY_POD_NAME}" -- psql -d "app-${{ github.event.number }}" -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO \"app-${{ github.event.number }}\";"
oc exec "${CRUNCHY_PG_PRIMARY_POD_NAME}" -- psql -d "app-${{ github.event.number }}" -c "GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO \"app-${{ github.event.number }}\";"
oc exec "${CRUNCHY_PG_PRIMARY_POD_NAME}" -- psql -d "app-${{ github.event.number }}" -c "GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public TO \"app-${{ github.event.number }}\";"
oc exec "${CRUNCHY_PG_PRIMARY_POD_NAME}" -- psql -d "app-${{ github.event.number }}" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON TABLES TO \"app-${{ github.event.number }}\";"
oc exec "${CRUNCHY_PG_PRIMARY_POD_NAME}" -- psql -d "app-${{ github.event.number }}" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON SEQUENCES TO \"app-${{ github.event.number }}\";"
oc exec "${CRUNCHY_PG_PRIMARY_POD_NAME}" -- psql -d "app-${{ github.event.number }}" -c "ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON FUNCTIONS TO \"app-${{ github.event.number }}\";"
# TODO: remove these
26 changes: 13 additions & 13 deletions .github/workflows/pr-close.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
oc_token: ${{ secrets.OC_TOKEN }}
with:
cleanup: label

cleanup-pvcs:
name: Cleanup Project PVCs
runs-on: ubuntu-22.04
Expand Down Expand Up @@ -60,32 +60,32 @@ jobs:
oc project ${{ secrets.OC_NAMESPACE }} # Safeguard!
- run: |
# check if postgres-crunchy exists or else exit
oc get PostgresCluster/postgres-crunchy || exit 0
oc get PostgresCluster/postgres-crunchy-dev || exit 0
# Remove the user from the crunchy cluster yaml and apply the changes
USER_TO_REMOVE='{"databases":["app-${{ github.event.number }}"],"name":"app-${{ github.event.number }}"}'
echo 'getting current users from crunchy'
CURRENT_USERS=$(oc get PostgresCluster/postgres-crunchy -o json | jq '.spec.users')
CURRENT_USERS=$(oc get PostgresCluster/postgres-crunchy-dev -o json | jq '.spec.users')
echo "${CURRENT_USERS}"
# Remove the user from the list,
UPDATED_USERS=$(echo "$CURRENT_USERS" | jq --argjson user "$USER_TO_REMOVE" 'map(select(. != $user))')
PATCH_JSON=$(jq -n --argjson users "$UPDATED_USERS" '{"spec": {"users": $users}}')
oc patch PostgresCluster/postgres-crunchy --type=merge -p "$PATCH_JSON"
oc patch PostgresCluster/postgres-crunchy-dev --type=merge -p "$PATCH_JSON"
# get primary crunchy pod and remove the role and db
CRUNCHY_PG_PRIMARY_POD_NAME=$(oc get pods -l postgres-operator.crunchydata.com/role=master -o json | jq -r '.items[0].metadata.name')
echo "${CRUNCHY_PG_PRIMARY_POD_NAME}"
# Terminate all connections to the database before trying terminate
oc exec "${CRUNCHY_PG_PRIMARY_POD_NAME}" -- psql -c "SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = 'app-${{ github.event.number }}' AND pid <> pg_backend_pid();"
# Drop the database and role
oc exec "${CRUNCHY_PG_PRIMARY_POD_NAME}" -- psql -c "DROP DATABASE \"app-${{ github.event.number }}\" --cascade"
oc exec "${CRUNCHY_PG_PRIMARY_POD_NAME}" -- psql -c "DROP ROLE \"app-${{ github.event.number }}\" --cascade"
echo "Database and Role for PR is cleaned."
exit 0
exit 0
1 change: 0 additions & 1 deletion backend/dataloader/bulk-data-loader.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Instruction for running: from backend directory: node dataloader/bulk-data-loader.js
// Ensure parameters at the bottom of this file are updated as required

require('dotenv').config();
const faker = require('faker');
const db = require('pg-promise')();
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/app/common/validation-date-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ interface ValidationDatePickerProps {
id: string;
classNamePrefix: string;
errMsg: string;
isDisabled?: boolean;
}

export const ValidationDatePicker: FC<ValidationDatePickerProps> = ({
Expand All @@ -23,6 +24,7 @@ export const ValidationDatePicker: FC<ValidationDatePickerProps> = ({
id,
classNamePrefix,
errMsg,
isDisabled,
}) => {
const handleDateChange = (date: Date) => {
onChange(date);
Expand All @@ -48,6 +50,7 @@ export const ValidationDatePicker: FC<ValidationDatePickerProps> = ({
autoComplete="false"
monthsShown={2}
showPreviousMonths
disabled={isDisabled}
/>
</div>
<div className={calculatedClass}>{errMsg}</div>
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/app/common/validation-textarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface ValidationTextAreaProps {
rows: number;
maxLength?: number;
placeholderText?: string;
disabled?: boolean;
}

export const ValidationTextArea: FC<ValidationTextAreaProps> = ({
Expand All @@ -20,6 +21,7 @@ export const ValidationTextArea: FC<ValidationTextAreaProps> = ({
rows,
maxLength,
placeholderText,
disabled,
}) => {
const errClass = errMsg === "" ? "" : "error-message";
const calulatedClass = errMsg === "" ? className : className + " error-border";
Expand All @@ -34,6 +36,7 @@ export const ValidationTextArea: FC<ValidationTextAreaProps> = ({
onChange={(e) => onChange(e.target.value)}
maxLength={maxLength}
placeholder={placeholderText}
disabled={disabled}
/>
</div>
<div className={errClass}>{errMsg}</div>
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/app/components/common/attachment-upload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { BsPlus } from "react-icons/bs";

type Props = {
onFileSelect: (selectedFile: FileList) => void;
disabled?: boolean | null;
};

export const AttachmentUpload: FC<Props> = ({ onFileSelect }) => {
export const AttachmentUpload: FC<Props> = ({ onFileSelect, disabled }) => {
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
if (event.target.files) {
onFileSelect(event.target.files);
Expand Down Expand Up @@ -40,6 +41,8 @@ export const AttachmentUpload: FC<Props> = ({ onFileSelect }) => {
className="comp-attachment-upload-btn"
tabIndex={0}
onClick={handleDivClick}
disabled={disabled ?? false}
style={disabled ?? false ? { cursor: "default" } : {}}
>
<div className="upload-icon">
<BsPlus />
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/app/components/common/attachments-carousel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Props = {
onFileDeleted?: (attachments: COMSObject) => void;
onSlideCountChange?: (count: number) => void;
setCancelPendingUpload?: (isCancelUpload: boolean) => void | null;
disabled?: boolean | null;
};

export const AttachmentsCarousel: FC<Props> = ({
Expand All @@ -33,6 +34,7 @@ export const AttachmentsCarousel: FC<Props> = ({
onFileDeleted,
onSlideCountChange,
setCancelPendingUpload,
disabled,
}) => {
const dispatch = useAppDispatch();

Expand Down Expand Up @@ -207,7 +209,12 @@ export const AttachmentsCarousel: FC<Props> = ({
className="comp-carousel"
>
<Slider className="coms-slider">
{allowUpload && <AttachmentUpload onFileSelect={onFileSelect} />}
{allowUpload && (
<AttachmentUpload
onFileSelect={onFileSelect}
disabled={disabled}
/>
)}
{slides?.map((item, index) => (
<AttachmentSlide
key={item.name}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
getLinkedComplaints,
selectLinkedComplaints,
setLinkedComplaints,
selectComplaintViewMode,
} from "@store/reducers/complaints";
import DatePicker from "react-datepicker";
import Select from "react-select";
Expand Down Expand Up @@ -89,6 +90,7 @@ export const ComplaintDetailsEdit: FC = () => {
const data = useAppSelector(selectComplaint);
const privacyDropdown = useAppSelector(selectPrivacyDropdown);
const enablePrivacyFeature = useAppSelector(isFeatureActive(FEATURE_TYPES.PRIV_REQ));
const isReadOnly = useAppSelector(selectComplaintViewMode);

const {
details,
Expand Down Expand Up @@ -741,6 +743,7 @@ export const ComplaintDetailsEdit: FC = () => {
size="sm"
id="details-screen-edit-button"
onClick={editButtonClick}
disabled={isReadOnly}
>
<i className="bi bi-pencil"></i>
<span>Edit Complaint</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { FC } from "react";
import { Link } from "react-router-dom";
import COMPLAINT_TYPES, { complaintTypeToName } from "@apptypes/app/complaint-types";
import { useAppDispatch, useAppSelector } from "@hooks/hooks";
import { selectComplaintHeader } from "@store/reducers/complaints";
import { selectComplaintHeader, selectComplaintViewMode } from "@store/reducers/complaints";
import { applyStatusClass, formatDate, formatTime, getAvatarInitials } from "@common/methods";

import { Badge, Button, Dropdown } from "react-bootstrap";
Expand Down Expand Up @@ -46,6 +46,7 @@ export const ComplaintHeader: FC<ComplaintHeaderProps> = ({
girType,
} = useAppSelector(selectComplaintHeader(complaintType));
const showExperimentalFeature = useAppSelector(isFeatureActive(FEATURE_TYPES.EXPERIMENTAL_FEATURE));
const isReadOnly = useAppSelector(selectComplaintViewMode);

const dispatch = useAppDispatch();
const assignText = officerAssigned === "Not Assigned" ? "Assign" : "Reassign";
Expand Down Expand Up @@ -175,6 +176,7 @@ export const ComplaintHeader: FC<ComplaintHeaderProps> = ({
<Dropdown.Item
as="button"
onClick={openQuickCloseModal}
disabled={isReadOnly}
>
<i className="bi bi-journal-x"></i>
<span>Quick close</span>
Expand All @@ -183,6 +185,7 @@ export const ComplaintHeader: FC<ComplaintHeaderProps> = ({
<Dropdown.Item
as="button"
onClick={openAsignOfficerModal}
disabled={isReadOnly}
>
<i className="bi bi-person-plus"></i>
<span>{assignText}</span>
Expand Down Expand Up @@ -211,6 +214,7 @@ export const ComplaintHeader: FC<ComplaintHeaderProps> = ({
title="Quick close"
variant="outline-light"
onClick={openQuickCloseModal}
disabled={isReadOnly}
>
<i className="bi bi-journal-x"></i>
<span>Quick close</span>
Expand All @@ -221,6 +225,7 @@ export const ComplaintHeader: FC<ComplaintHeaderProps> = ({
title="Assign to officer"
variant="outline-light"
onClick={openAsignOfficerModal}
disabled={isReadOnly}
>
<i className="bi bi-person-plus"></i>
<span>{assignText}</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export const ComplaintActionItems: FC<Props> = ({
<Dropdown.Item
id="update-assignee-menu-item"
onClick={openAssignOfficerModal}
disabled={complaint_status === "CLOSED"}
>
<i
className="bi bi-person-up"
Expand Down
Loading

0 comments on commit 2887d2b

Please sign in to comment.