diff --git a/backend/src/main/java/ca/bc/gov/app/ApplicationConstant.java b/backend/src/main/java/ca/bc/gov/app/ApplicationConstant.java index d2a81a69e3..b7afc8c697 100644 --- a/backend/src/main/java/ca/bc/gov/app/ApplicationConstant.java +++ b/backend/src/main/java/ca/bc/gov/app/ApplicationConstant.java @@ -46,11 +46,11 @@ public final class ApplicationConstant { btc.business_type_code as business_type, sd.incorporation_number, sd.client_number, - sd.organization_name, + pgp_sym_decrypt(sd.organization_name, current_setting('cryptic.key')) as organization_name, ctc.client_type_code as client_type, ctc.description as client_type_desc, sd.good_standing_ind as good_standing, - sd.birthdate, + pgp_sym_decrypt(sd.birthdate, current_setting('cryptic.key'))::date as birthdate, dc.district_code as district, dc.district_code || ' - ' || dc.description as district_desc FROM nrfc.submission s @@ -73,10 +73,10 @@ public final class ApplicationConstant { ROW_NUMBER() OVER (order by sc.submission_contact_id ) AS index, sc.contact_type_code, ctc.description as contact_desc, - sc.first_name, - sc.last_name, - sc.business_phone_number, - sc.email_address, + pgp_sym_decrypt(sc.first_name, current_setting('cryptic.key')) as first_name, + pgp_sym_decrypt(sc.last_name, current_setting('cryptic.key')) as last_name, + pgp_sym_decrypt(sc.business_phone_number, current_setting('cryptic.key')) as business_phone_number, + pgp_sym_decrypt(sc.email_address, current_setting('cryptic.key')) as email_address, ( select STRING_AGG(sl.location_name,', ') as locations from nrfc.submission_location sl @@ -94,13 +94,13 @@ select STRING_AGG(sl.location_name,', ') as locations public static final String SUBMISSION_LOCATION_QUERY = """ SELECT ROW_NUMBER() OVER (order by sl.submission_location_id ) AS index, - sl.street_address, + pgp_sym_decrypt(sl.street_address, current_setting('cryptic.key')) as street_address, sl.country_code, cc.description as country_desc, sl.province_code, pc.description as province_desc, - sl.city_name, - sl.postal_code, + pgp_sym_decrypt(sl.city_name, current_setting('cryptic.key')) as city_name, + pgp_sym_decrypt(sl.postal_code, current_setting('cryptic.key')) as postal_code, sl.location_name FROM nrfc.submission_location sl left join nrfc.country_code cc on cc.country_code = sl.country_code diff --git a/backend/src/main/java/ca/bc/gov/app/converters/SubmissionContactEntityConverter.java b/backend/src/main/java/ca/bc/gov/app/converters/SubmissionContactEntityConverter.java new file mode 100644 index 0000000000..a1349f801c --- /dev/null +++ b/backend/src/main/java/ca/bc/gov/app/converters/SubmissionContactEntityConverter.java @@ -0,0 +1,103 @@ +package ca.bc.gov.app.converters; + +import ca.bc.gov.app.entity.client.SubmissionContactEntity; +import ca.bc.gov.app.util.DatabaseCryptoUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.reactivestreams.Publisher; +import org.springframework.data.r2dbc.mapping.event.AfterConvertCallback; +import org.springframework.data.r2dbc.mapping.event.BeforeConvertCallback; +import org.springframework.data.relational.core.sql.SqlIdentifier; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +@Slf4j +public class SubmissionContactEntityConverter implements + BeforeConvertCallback, + AfterConvertCallback { + + private final DatabaseCryptoUtil cryptoUtil; + + @Override + public Publisher onAfterConvert( + SubmissionContactEntity entity, + SqlIdentifier table + ) { + return cryptoUtil + .decryptAs(entity.getOriginalFirstName(), String.class) + .map(entity::withFirstName) + .defaultIfEmpty(entity) + .flatMap(updatedEntity -> + cryptoUtil + .decryptAs(updatedEntity.getOriginalLastName(), String.class) + .map(updatedEntity::withLastName) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil + .decryptAs(updatedEntity.getOriginalBusinessPhoneNumber(), String.class) + .map(updatedEntity::withBusinessPhoneNumber) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil + .decryptAs(updatedEntity.getOriginalSecondaryPhoneNumber(), String.class) + .map(updatedEntity::withSecondaryPhoneNumber) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil + .decryptAs(updatedEntity.getOriginalFaxNumber(), String.class) + .map(updatedEntity::withFaxNumber) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil + .decryptAs(updatedEntity.getOriginalEmailAddress(), String.class) + .map(updatedEntity::withEmailAddress) + .defaultIfEmpty(updatedEntity) + ); + } + + @Override + public Publisher onBeforeConvert( + SubmissionContactEntity entity, + SqlIdentifier table + ) { + return cryptoUtil + .encryptFromString(entity.getFirstName()) + .map(entity::withOriginalFirstName) + .defaultIfEmpty(entity) + .flatMap(updatedEntity -> + cryptoUtil + .encryptFromString(updatedEntity.getLastName()) + .map(updatedEntity::withOriginalLastName) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil + .encryptFromString(updatedEntity.getBusinessPhoneNumber()) + .map(updatedEntity::withOriginalBusinessPhoneNumber) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil + .encryptFromString(updatedEntity.getSecondaryPhoneNumber()) + .map(updatedEntity::withOriginalSecondaryPhoneNumber) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil + .encryptFromString(updatedEntity.getFaxNumber()) + .map(updatedEntity::withOriginalFaxNumber) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil + .encryptFromString(updatedEntity.getEmailAddress()) + .map(updatedEntity::withOriginalEmailAddress) + .defaultIfEmpty(updatedEntity) + ); + } +} diff --git a/backend/src/main/java/ca/bc/gov/app/converters/SubmissionDetailEntityConverter.java b/backend/src/main/java/ca/bc/gov/app/converters/SubmissionDetailEntityConverter.java new file mode 100644 index 0000000000..fcec039225 --- /dev/null +++ b/backend/src/main/java/ca/bc/gov/app/converters/SubmissionDetailEntityConverter.java @@ -0,0 +1,94 @@ +package ca.bc.gov.app.converters; + +import ca.bc.gov.app.entity.client.SubmissionDetailEntity; +import ca.bc.gov.app.util.DatabaseCryptoUtil; +import java.time.LocalDate; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.reactivestreams.Publisher; +import org.springframework.data.r2dbc.mapping.event.AfterConvertCallback; +import org.springframework.data.r2dbc.mapping.event.BeforeConvertCallback; +import org.springframework.data.relational.core.sql.SqlIdentifier; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +@Slf4j +public class SubmissionDetailEntityConverter implements + BeforeConvertCallback, + AfterConvertCallback { + + private final DatabaseCryptoUtil cryptoUtil; + + @Override + public Publisher onAfterConvert( + SubmissionDetailEntity entity, + SqlIdentifier table + ) { + return cryptoUtil + .decryptAs(entity.getOriginalOrganizationName(), String.class) + .map(entity::withOrganizationName) + .defaultIfEmpty(entity) + .flatMap(updatedEntity -> + cryptoUtil.decryptAs(updatedEntity.getOriginalClientIdentification(), String.class) + .map(updatedEntity::withClientIdentification) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil.decryptAs(updatedEntity.getOriginalLastName(), String.class) + .map(updatedEntity::withLastName) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil.decryptAs(updatedEntity.getOriginalMiddleName(), String.class) + .map(updatedEntity::withMiddleName) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil.decryptAs(updatedEntity.getOriginalFirstName(), String.class) + .map(updatedEntity::withFirstName) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil.decryptAs(updatedEntity.getOriginalBirthdate(), LocalDate.class) + .map(updatedEntity::withBirthdate) + .defaultIfEmpty(updatedEntity) + ); + } + + @Override + public Publisher onBeforeConvert( + SubmissionDetailEntity entity, + SqlIdentifier table + ) { + return cryptoUtil + .encryptFromString(entity.getOrganizationName()) + .map(entity::withOriginalOrganizationName) + .defaultIfEmpty(entity) + .flatMap(updatedEntity -> + cryptoUtil.encryptFromString(updatedEntity.getClientIdentification()) + .map(updatedEntity::withOriginalClientIdentification) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil.encryptFromString(updatedEntity.getLastName()) + .map(updatedEntity::withOriginalLastName) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil.encryptFromString(updatedEntity.getMiddleName()) + .map(updatedEntity::withOriginalMiddleName) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil.encryptFromString(updatedEntity.getFirstName()) + .map(updatedEntity::withOriginalFirstName) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil.encryptFromLocalDate(updatedEntity.getBirthdate()) + .map(updatedEntity::withOriginalBirthdate) + .defaultIfEmpty(updatedEntity) + ); + } +} diff --git a/backend/src/main/java/ca/bc/gov/app/converters/SubmissionLocationEntityConverter.java b/backend/src/main/java/ca/bc/gov/app/converters/SubmissionLocationEntityConverter.java new file mode 100644 index 0000000000..bf030d4cde --- /dev/null +++ b/backend/src/main/java/ca/bc/gov/app/converters/SubmissionLocationEntityConverter.java @@ -0,0 +1,127 @@ +package ca.bc.gov.app.converters; + +import ca.bc.gov.app.entity.client.SubmissionLocationEntity; +import ca.bc.gov.app.util.DatabaseCryptoUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.reactivestreams.Publisher; +import org.springframework.data.r2dbc.mapping.event.AfterConvertCallback; +import org.springframework.data.r2dbc.mapping.event.BeforeConvertCallback; +import org.springframework.data.relational.core.sql.SqlIdentifier; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +@Slf4j +public class SubmissionLocationEntityConverter implements + BeforeConvertCallback, + AfterConvertCallback +{ + + private final DatabaseCryptoUtil cryptoUtil; + + @Override + public Publisher onAfterConvert( + SubmissionLocationEntity entity, + SqlIdentifier table + ) { + return cryptoUtil + .decryptAs(entity.getOriginalStreetAddress(), String.class) + .map(entity::withStreetAddress) + .defaultIfEmpty(entity) + .flatMap(updatedEntity -> + cryptoUtil.decryptAs(updatedEntity.getOriginalComplementaryAddress1(), String.class) + .map(updatedEntity::withComplementaryAddress1) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil.decryptAs(updatedEntity.getOriginalComplementaryAddress2(), String.class) + .map(updatedEntity::withComplementaryAddress2) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil.decryptAs(updatedEntity.getOriginalCityName(), String.class) + .map(updatedEntity::withCityName) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil.decryptAs(updatedEntity.getOriginalPostalCode(), String.class) + .map(updatedEntity::withPostalCode) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil + .decryptAs(updatedEntity.getOriginalBusinessPhoneNumber(), String.class) + .map(updatedEntity::withBusinessPhoneNumber) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil + .decryptAs(updatedEntity.getOriginalSecondaryPhoneNumber(), String.class) + .map(updatedEntity::withSecondaryPhoneNumber) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil + .decryptAs(updatedEntity.getOriginalFaxNumber(), String.class) + .map(updatedEntity::withFaxNumber) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil + .decryptAs(updatedEntity.getOriginalEmailAddress(), String.class) + .map(updatedEntity::withEmailAddress) + .defaultIfEmpty(updatedEntity) + ); + } + + @Override + public Publisher onBeforeConvert(SubmissionLocationEntity entity, + SqlIdentifier table) { + return + cryptoUtil + .encryptFromString(entity.getStreetAddress()) + .map(entity::withOriginalStreetAddress) + .defaultIfEmpty(entity) + .flatMap(updatedEntity -> + cryptoUtil.encryptFromString(updatedEntity.getComplementaryAddress1()) + .map(updatedEntity::withOriginalComplementaryAddress1) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil.encryptFromString(updatedEntity.getComplementaryAddress2()) + .map(updatedEntity::withOriginalComplementaryAddress2) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil.encryptFromString(updatedEntity.getCityName()) + .map(updatedEntity::withOriginalCityName) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil.encryptFromString(updatedEntity.getPostalCode()) + .map(updatedEntity::withOriginalPostalCode) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil.encryptFromString(updatedEntity.getBusinessPhoneNumber()) + .map(updatedEntity::withOriginalBusinessPhoneNumber) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil.encryptFromString(updatedEntity.getSecondaryPhoneNumber()) + .map(updatedEntity::withOriginalSecondaryPhoneNumber) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil.encryptFromString(updatedEntity.getFaxNumber()) + .map(updatedEntity::withOriginalFaxNumber) + .defaultIfEmpty(updatedEntity) + ) + .flatMap(updatedEntity -> + cryptoUtil.encryptFromString(updatedEntity.getEmailAddress()) + .map(updatedEntity::withOriginalEmailAddress) + .defaultIfEmpty(updatedEntity) + ); + } +} diff --git a/backend/src/main/java/ca/bc/gov/app/entity/client/SubmissionContactEntity.java b/backend/src/main/java/ca/bc/gov/app/entity/client/SubmissionContactEntity.java index 8e4707abe5..15af1a364f 100644 --- a/backend/src/main/java/ca/bc/gov/app/entity/client/SubmissionContactEntity.java +++ b/backend/src/main/java/ca/bc/gov/app/entity/client/SubmissionContactEntity.java @@ -1,6 +1,7 @@ package ca.bc.gov.app.entity.client; import ca.bc.gov.app.ApplicationConstant; +import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.AllArgsConstructor; @@ -9,6 +10,7 @@ import lombok.NoArgsConstructor; import lombok.With; import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.Transient; import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Table; @@ -35,28 +37,52 @@ public class SubmissionContactEntity { @Column("first_name") @Size(min = 1, max = 30) + @JsonIgnore + private byte[] originalFirstName; + + @Transient private String firstName; @Column("last_name") @Size(min = 1, max = 30) + @JsonIgnore + private byte[] originalLastName; + + @Transient private String lastName; @Column("business_phone_number") @NotNull @Size(min = 5, max = 14) + @JsonIgnore + private byte[] originalBusinessPhoneNumber; + + @Transient private String businessPhoneNumber; @Column("secondary_phone_number") @Size(max = 14) + @JsonIgnore + private byte[] originalSecondaryPhoneNumber; + + @Transient private String secondaryPhoneNumber; @Column("fax_number") @Size(max = 14) + @JsonIgnore + private byte[] originalFaxNumber; + + @Transient private String faxNumber; @Column("email_address") @NotNull @Size(min = 5, max = 100) + @JsonIgnore + private byte[] originalEmailAddress; + + @Transient private String emailAddress; @Column("idp_user_id") diff --git a/backend/src/main/java/ca/bc/gov/app/entity/client/SubmissionDetailEntity.java b/backend/src/main/java/ca/bc/gov/app/entity/client/SubmissionDetailEntity.java index e4b3b52d55..f39bff5ee2 100644 --- a/backend/src/main/java/ca/bc/gov/app/entity/client/SubmissionDetailEntity.java +++ b/backend/src/main/java/ca/bc/gov/app/entity/client/SubmissionDetailEntity.java @@ -1,6 +1,7 @@ package ca.bc.gov.app.entity.client; import ca.bc.gov.app.ApplicationConstant; +import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import java.time.LocalDate; @@ -10,6 +11,7 @@ import lombok.NoArgsConstructor; import lombok.With; import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.Transient; import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Table; @@ -37,9 +39,13 @@ public class SubmissionDetailEntity { @Size(min = 1, max = 13) private String registrationNumber; + @Transient + private String organizationName; + @Column("organization_name") @Size(min = 3, max = 60) - private String organizationName; + @JsonIgnore + private byte[] originalOrganizationName; @Column("business_type_code") @NotNull @@ -54,8 +60,12 @@ public class SubmissionDetailEntity { @Column("good_standing_ind") @Size(min = 1, max = 1) private String goodStandingInd; - + @Column("birthdate") + @JsonIgnore + private byte[] originalBirthdate; + + @Transient private LocalDate birthdate; @Column("district_code") @@ -76,14 +86,26 @@ public class SubmissionDetailEntity { @Column("first_name") @Size(max = 30) + @JsonIgnore + private byte[] originalFirstName; + + @Transient private String firstName; @Column("middle_name") @Size(max = 30) + @JsonIgnore + private byte[] originalMiddleName; + + @Transient private String middleName; @Column("last_name") @Size(max = 30) + @JsonIgnore + private byte[] originalLastName; + + @Transient private String lastName; @Column("notes") @@ -96,6 +118,10 @@ public class SubmissionDetailEntity { @Column("client_identification") @Size(max = 40) + @JsonIgnore + private byte[] originalClientIdentification; + + @Transient private String clientIdentification; @Column("identification_country_code") diff --git a/backend/src/main/java/ca/bc/gov/app/entity/client/SubmissionLocationEntity.java b/backend/src/main/java/ca/bc/gov/app/entity/client/SubmissionLocationEntity.java index 4c4d66732c..f1209a29e6 100644 --- a/backend/src/main/java/ca/bc/gov/app/entity/client/SubmissionLocationEntity.java +++ b/backend/src/main/java/ca/bc/gov/app/entity/client/SubmissionLocationEntity.java @@ -1,6 +1,7 @@ package ca.bc.gov.app.entity.client; import ca.bc.gov.app.ApplicationConstant; +import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.AllArgsConstructor; @@ -9,6 +10,7 @@ import lombok.NoArgsConstructor; import lombok.With; import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.Transient; import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Table; @@ -31,6 +33,10 @@ public class SubmissionLocationEntity { @Column("street_address") @NotNull @Size(min = 3, max = 40) + @JsonIgnore + private byte[] originalStreetAddress; + + @Transient private String streetAddress; @Column("country_code") @@ -46,11 +52,19 @@ public class SubmissionLocationEntity { @Column("city_name") @NotNull @Size(min = 2, max = 30) + @JsonIgnore + private byte[] originalCityName; + + @Transient private String cityName; @Column("postal_code") @NotNull @Size(min = 5, max = 10) + @JsonIgnore + private byte[] originalPostalCode; + + @Transient private String postalCode; @Column("location_name") @@ -61,18 +75,35 @@ public class SubmissionLocationEntity { @Column("business_phone_number") @NotNull @Size(min = 5, max = 14) + + @JsonIgnore + private byte[] originalBusinessPhoneNumber; + + @Transient private String businessPhoneNumber; @Column("secondary_phone_number") @Size(max = 14) + @JsonIgnore + private byte[] originalSecondaryPhoneNumber; + + @Transient private String secondaryPhoneNumber; @Column("fax_number") @Size(max = 14) + @JsonIgnore + private byte[] originalFaxNumber; + + @Transient private String faxNumber; @Column("email_address") @Size(min = 5, max = 100) + @JsonIgnore + private byte[] originalEmailAddress; + + @Transient private String emailAddress; @Column("notes") @@ -81,10 +112,18 @@ public class SubmissionLocationEntity { @Column("complementary_address_1") @Size(max = 40) + @JsonIgnore + private byte[] originalComplementaryAddress1; + + @Transient private String complementaryAddress1; @Column("complementary_address_2") @Size(max = 40) + @JsonIgnore + private byte[] originalComplementaryAddress2; + + @Transient private String complementaryAddress2; } diff --git a/backend/src/main/java/ca/bc/gov/app/util/DatabaseCryptoUtil.java b/backend/src/main/java/ca/bc/gov/app/util/DatabaseCryptoUtil.java new file mode 100644 index 0000000000..31e7e19715 --- /dev/null +++ b/backend/src/main/java/ca/bc/gov/app/util/DatabaseCryptoUtil.java @@ -0,0 +1,85 @@ +package ca.bc.gov.app.util; + +import io.r2dbc.postgresql.codec.Json; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.Objects; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.r2dbc.core.DatabaseClient; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Mono; + +@Component +@RequiredArgsConstructor +public class DatabaseCryptoUtil { + + private final DatabaseClient databaseClient; + + /** + * Decrypts the provided byte array into an object of the specified class type. This method uses + * the database to perform the decryption using the `pgp_sym_decrypt` function. If the provided + * byte array is null, it returns an empty Mono. + * + * @param The type of the object to be returned after decryption. + * @param data The byte array to be decrypted. + * @param clazz The class type of the object to be returned after decryption. + * @return A Mono emitting the decrypted object of the specified class type, or an empty Mono if + * the input data is null. + */ + public Mono decryptAs(byte[] data, Class clazz) { + if (Objects.isNull(data)) { + return Mono.empty(); + } + return databaseClient.sql(generateDecryptQuery(clazz)) + .bind("data", data) + .map((row, rowMetadata) -> row.get("decrypted_data", clazz)) + .first(); + } + + /** + * Encrypts the provided string into a byte array. This method uses the database to perform the + * encryption using the `pgp_sym_encrypt` function. If the provided string is null, it returns an + * empty Mono. + * + * @param data The string to be encrypted. + * @return A Mono emitting the encrypted byte array, or an empty Mono if the input string is null. + */ + public Mono encryptFromString(String data) { + if (Objects.isNull(data)) { + return Mono.empty(); + } + return databaseClient.sql( + "SELECT pgp_sym_encrypt(:data, current_setting('cryptic.key')) AS encrypted_data") + .bind("data", data).map((row, rowMetadata) -> row.get("encrypted_data", byte[].class)) + .first(); + } + + /** + * Encrypts the provided LocalDate into a byte array. This method converts the LocalDate to a + * string formatted as ISO_DATE and then encrypts it using the `encryptFromString` method. If the + * provided LocalDate is null, it returns an empty Mono. + * + * @param data The LocalDate to be encrypted. + * @return A Mono emitting the encrypted byte array, or an empty Mono if the input LocalDate is + * null. + */ + public Mono encryptFromLocalDate(LocalDate data) { + if (Objects.isNull(data)) { + return Mono.empty(); + } + return encryptFromString(data.format(DateTimeFormatter.ISO_DATE)); + } + + private String generateDecryptQuery(Class clazz){ + String decryptQuery = "SELECT pgp_sym_decrypt(:data, current_setting('cryptic.key'))%s AS decrypted_data"; + if(clazz.equals(Json.class)){ + return String.format(decryptQuery, "::jsonb"); + } + if(clazz.equals(LocalDate.class)){ + return String.format(decryptQuery, "::date"); + } + return String.format(decryptQuery, StringUtils.EMPTY); + } + +} \ No newline at end of file diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index 86551d2adf..cb667e7f7e 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -11,7 +11,7 @@ spring: jwt: jwk-set-uri: ${JWK_SET_URI:https://cognito-idp.${ca.bc.gov.nrs.security.region}.amazonaws.com/${ca.bc.gov.nrs.security.user-pool}/.well-known/jwks.json} r2dbc: - url: r2dbc:postgresql://${ca.bc.gov.nrs.postgres.host}/${ca.bc.gov.nrs.postgres.database} + url: r2dbc:postgresql://${ca.bc.gov.nrs.postgres.host}/${ca.bc.gov.nrs.postgres.database}?options=cryptic.key=${ca.bc.gov.nrs.postgres.cryptic-key} username: ${ca.bc.gov.nrs.postgres.username} password: ${ca.bc.gov.nrs.postgres.password} pool: @@ -30,7 +30,7 @@ spring: baseline-on-migrate: true user: ${ca.bc.gov.nrs.postgres.username} password: ${ca.bc.gov.nrs.postgres.password} - url: jdbc:postgresql://${ca.bc.gov.nrs.postgres.host}/${ca.bc.gov.nrs.postgres.database} + url: jdbc:postgresql://${ca.bc.gov.nrs.postgres.host}/${ca.bc.gov.nrs.postgres.database}?options=-c%20cryptic.key=${ca.bc.gov.nrs.postgres.cryptic-key} http: encoding: charset: UTF-8 @@ -123,6 +123,7 @@ ca: host: ${POSTGRESQL_HOST:localhost}:5432 username: ${POSTGRESQL_USER:user} password: ${POSTGRESQL_PASSWORD:passwd} + cryptic-key: ${POSTGRESQL_KEY:saltandpepper} legacy: url: ${LEGACY_URL:http://127.0.0.1:9019} processor: diff --git a/backend/src/main/resources/db/migration/V10__data_encryption.sql b/backend/src/main/resources/db/migration/V10__data_encryption.sql new file mode 100644 index 0000000000..e28e9688c6 --- /dev/null +++ b/backend/src/main/resources/db/migration/V10__data_encryption.sql @@ -0,0 +1,113 @@ +CREATE EXTENSION IF NOT EXISTS pgcrypto; + +ALTER TABLE nrfc.email_log +ADD COLUMN IF NOT EXISTS email_address_t BYTEA NULL, +ADD COLUMN IF NOT EXISTS email_variables_t BYTEA NULL; + +UPDATE nrfc.email_log SET email_address_t = pgp_sym_encrypt(email_address, current_setting('cryptic.key')); +UPDATE nrfc.email_log SET email_variables_t = pgp_sym_encrypt(email_variables::text, current_setting('cryptic.key')); + +ALTER TABLE nrfc.email_log DROP COLUMN IF EXISTS email_address; +ALTER TABLE nrfc.email_log DROP COLUMN IF EXISTS email_variables; + +ALTER TABLE nrfc.email_log RENAME COLUMN email_address_t TO email_address; +ALTER TABLE nrfc.email_log RENAME COLUMN email_variables_t TO email_variables; + +ALTER TABLE nrfc.submission_contact +ADD COLUMN IF NOT EXISTS first_name_t BYTEA NULL, +ADD COLUMN IF NOT EXISTS last_name_t BYTEA NULL, +ADD COLUMN IF NOT EXISTS business_phone_number_t BYTEA NULL, +ADD COLUMN IF NOT EXISTS secondary_phone_number_t BYTEA NULL, +ADD COLUMN IF NOT EXISTS fax_number_t BYTEA NULL, +ADD COLUMN IF NOT EXISTS email_address_t BYTEA NULL; + +UPDATE nrfc.submission_contact SET first_name_t = pgp_sym_encrypt(first_name,current_setting('cryptic.key')); +UPDATE nrfc.submission_contact SET last_name_t = pgp_sym_encrypt(last_name,current_setting('cryptic.key')); +UPDATE nrfc.submission_contact SET business_phone_number_t = pgp_sym_encrypt(business_phone_number,current_setting('cryptic.key')); +UPDATE nrfc.submission_contact SET secondary_phone_number_t = pgp_sym_encrypt(secondary_phone_number,current_setting('cryptic.key')); +UPDATE nrfc.submission_contact SET fax_number_t = pgp_sym_encrypt(fax_number,current_setting('cryptic.key')); +UPDATE nrfc.submission_contact SET email_address_t = pgp_sym_encrypt(email_address,current_setting('cryptic.key')); + +ALTER TABLE nrfc.submission_contact DROP COLUMN IF EXISTS first_name; +ALTER TABLE nrfc.submission_contact DROP COLUMN IF EXISTS last_name; +ALTER TABLE nrfc.submission_contact DROP COLUMN IF EXISTS business_phone_number; +ALTER TABLE nrfc.submission_contact DROP COLUMN IF EXISTS secondary_phone_number; +ALTER TABLE nrfc.submission_contact DROP COLUMN IF EXISTS fax_number; +ALTER TABLE nrfc.submission_contact DROP COLUMN IF EXISTS email_address; + +ALTER TABLE nrfc.submission_contact RENAME COLUMN first_name_t to first_name; +ALTER TABLE nrfc.submission_contact RENAME COLUMN last_name_t to last_name; +ALTER TABLE nrfc.submission_contact RENAME COLUMN business_phone_number_t to business_phone_number; +ALTER TABLE nrfc.submission_contact RENAME COLUMN secondary_phone_number_t to secondary_phone_number; +ALTER TABLE nrfc.submission_contact RENAME COLUMN fax_number_t to fax_number; +ALTER TABLE nrfc.submission_contact RENAME COLUMN email_address_t to email_address; + +ALTER TABLE nrfc.submission_detail +ADD COLUMN IF NOT EXISTS birthdate_t BYTEA NULL, +ADD COLUMN IF NOT EXISTS organization_name_t BYTEA NULL, +ADD COLUMN IF NOT EXISTS first_name_t BYTEA NULL, +ADD COLUMN IF NOT EXISTS middle_name_t BYTEA NULL, +ADD COLUMN IF NOT EXISTS last_name_t BYTEA NULL, +ADD COLUMN IF NOT EXISTS client_identification_t BYTEA NULL; + +UPDATE nrfc.submission_detail SET birthdate_t = pgp_sym_encrypt(birthdate::text,current_setting('cryptic.key')); +UPDATE nrfc.submission_detail SET organization_name_t = pgp_sym_encrypt(organization_name,current_setting('cryptic.key')); +UPDATE nrfc.submission_detail SET first_name_t = pgp_sym_encrypt(first_name,current_setting('cryptic.key')); +UPDATE nrfc.submission_detail SET middle_name_t = pgp_sym_encrypt(middle_name,current_setting('cryptic.key')); +UPDATE nrfc.submission_detail SET last_name_t = pgp_sym_encrypt(last_name,current_setting('cryptic.key')); +UPDATE nrfc.submission_detail SET client_identification_t = pgp_sym_encrypt(client_identification,current_setting('cryptic.key')); + +ALTER TABLE nrfc.submission_detail DROP COLUMN IF EXISTS birthdate; +ALTER TABLE nrfc.submission_detail DROP COLUMN IF EXISTS organization_name; +ALTER TABLE nrfc.submission_detail DROP COLUMN IF EXISTS first_name; +ALTER TABLE nrfc.submission_detail DROP COLUMN IF EXISTS middle_name; +ALTER TABLE nrfc.submission_detail DROP COLUMN IF EXISTS last_name; +ALTER TABLE nrfc.submission_detail DROP COLUMN IF EXISTS client_identification; + +ALTER TABLE nrfc.submission_detail RENAME COLUMN birthdate_t TO birthdate; +ALTER TABLE nrfc.submission_detail RENAME COLUMN organization_name_t TO organization_name; +ALTER TABLE nrfc.submission_detail RENAME COLUMN first_name_t TO first_name; +ALTER TABLE nrfc.submission_detail RENAME COLUMN middle_name_t TO middle_name; +ALTER TABLE nrfc.submission_detail RENAME COLUMN last_name_t TO last_name; +ALTER TABLE nrfc.submission_detail RENAME COLUMN client_identification_t TO client_identification; + +ALTER TABLE nrfc.submission_location +ADD COLUMN IF NOT EXISTS street_address_t BYTEA NULL, +ADD COLUMN IF NOT EXISTS city_name_t BYTEA NULL, +ADD COLUMN IF NOT EXISTS postal_code_t BYTEA NULL, +ADD COLUMN IF NOT EXISTS business_phone_number_t BYTEA NULL, +ADD COLUMN IF NOT EXISTS secondary_phone_number_t BYTEA NULL, +ADD COLUMN IF NOT EXISTS fax_number_t BYTEA NULL, +ADD COLUMN IF NOT EXISTS email_address_t BYTEA NULL, +ADD COLUMN IF NOT EXISTS complementary_address_1_t BYTEA NULL, +ADD COLUMN IF NOT EXISTS complementary_address_2_t BYTEA NULL; + +UPDATE nrfc.submission_location SET street_address_t = pgp_sym_encrypt(street_address, current_setting('cryptic.key')); +UPDATE nrfc.submission_location SET city_name_t = pgp_sym_encrypt(city_name, current_setting('cryptic.key')); +UPDATE nrfc.submission_location SET postal_code_t = pgp_sym_encrypt(postal_code, current_setting('cryptic.key')); +UPDATE nrfc.submission_location SET business_phone_number_t = pgp_sym_encrypt(business_phone_number, current_setting('cryptic.key')); +UPDATE nrfc.submission_location SET secondary_phone_number_t = pgp_sym_encrypt(secondary_phone_number, current_setting('cryptic.key')); +UPDATE nrfc.submission_location SET fax_number_t = pgp_sym_encrypt(fax_number, current_setting('cryptic.key')); +UPDATE nrfc.submission_location SET email_address_t = pgp_sym_encrypt(email_address, current_setting('cryptic.key')); +UPDATE nrfc.submission_location SET complementary_address_1_t = pgp_sym_encrypt(complementary_address_1, current_setting('cryptic.key')); +UPDATE nrfc.submission_location SET complementary_address_2_t = pgp_sym_encrypt(complementary_address_2, current_setting('cryptic.key')); + +ALTER TABLE nrfc.submission_location DROP COLUMN IF EXISTS street_address; +ALTER TABLE nrfc.submission_location DROP COLUMN IF EXISTS city_name; +ALTER TABLE nrfc.submission_location DROP COLUMN IF EXISTS postal_code; +ALTER TABLE nrfc.submission_location DROP COLUMN IF EXISTS business_phone_number; +ALTER TABLE nrfc.submission_location DROP COLUMN IF EXISTS secondary_phone_number; +ALTER TABLE nrfc.submission_location DROP COLUMN IF EXISTS fax_number; +ALTER TABLE nrfc.submission_location DROP COLUMN IF EXISTS email_address; +ALTER TABLE nrfc.submission_location DROP COLUMN IF EXISTS complementary_address_1; +ALTER TABLE nrfc.submission_location DROP COLUMN IF EXISTS complementary_address_2; + +ALTER TABLE nrfc.submission_location RENAME COLUMN street_address_t TO street_address; +ALTER TABLE nrfc.submission_location RENAME COLUMN city_name_t TO city_name; +ALTER TABLE nrfc.submission_location RENAME COLUMN postal_code_t TO postal_code; +ALTER TABLE nrfc.submission_location RENAME COLUMN business_phone_number_t TO business_phone_number; +ALTER TABLE nrfc.submission_location RENAME COLUMN secondary_phone_number_t TO secondary_phone_number; +ALTER TABLE nrfc.submission_location RENAME COLUMN fax_number_t TO fax_number; +ALTER TABLE nrfc.submission_location RENAME COLUMN email_address_t TO email_address; +ALTER TABLE nrfc.submission_location RENAME COLUMN complementary_address_1_t TO complementary_address_1; +ALTER TABLE nrfc.submission_location RENAME COLUMN complementary_address_2_t TO complementary_address_2; diff --git a/backend/src/test/resources/db/migration/R__Test_Data.sql b/backend/src/test/resources/db/migration/R__Test_Data.sql deleted file mode 100644 index b868b36885..0000000000 --- a/backend/src/test/resources/db/migration/R__Test_Data.sql +++ /dev/null @@ -1,49 +0,0 @@ -insert into nrfc.submission_status_code (submission_status_code, description, effective_date, create_user) values ('P', 'In Progress', current_timestamp, 'mariamar') on conflict (submission_status_code) do nothing; -insert into nrfc.submission_status_code (submission_status_code, description, effective_date, create_user) values ('A', 'Approved', current_timestamp, 'mariamar') on conflict (submission_status_code) do nothing; -insert into nrfc.submission_status_code (submission_status_code, description, effective_date, create_user) values ('R', 'Rejected', current_timestamp, 'mariamar') on conflict (submission_status_code) do nothing; -insert into nrfc.submission_status_code (submission_status_code, description, effective_date, create_user) values ('D', 'Deleted', current_timestamp, 'mariamar') on conflict (submission_status_code) do nothing; -insert into nrfc.submission_status_code (submission_status_code, description, effective_date, create_user) values ('S', 'Submitted', current_timestamp, 'mariamar') on conflict (submission_status_code) do nothing; - -insert into nrfc.client_type_code (client_type_code, description, effective_date, create_user) values ('A', 'Association', current_timestamp, 'mariamar') on conflict (client_type_code) do nothing; -insert into nrfc.client_type_code (client_type_code, description, effective_date, create_user) values ('B', 'First Nation Band', current_timestamp, 'mariamar') on conflict (client_type_code) do nothing; -insert into nrfc.client_type_code (client_type_code, description, effective_date, create_user) values ('C', 'Corporation', current_timestamp, 'mariamar') on conflict (client_type_code) do nothing; -insert into nrfc.client_type_code (client_type_code, description, effective_date, create_user) values ('F', 'Ministry of Forests and Range', current_timestamp, 'mariamar') on conflict (client_type_code) do nothing; -insert into nrfc.client_type_code (client_type_code, description, effective_date, create_user) values ('G', 'Government', current_timestamp, 'mariamar') on conflict (client_type_code) do nothing; -insert into nrfc.client_type_code (client_type_code, description, effective_date, create_user) values ('I', 'Individual', current_timestamp, 'mariamar') on conflict (client_type_code) do nothing; -insert into nrfc.client_type_code (client_type_code, description, effective_date, create_user) values ('L', 'Limited Partnership', current_timestamp, 'mariamar') on conflict (client_type_code) do nothing; -insert into nrfc.client_type_code (client_type_code, description, effective_date, create_user) values ('P', 'General Partnership', current_timestamp, 'mariamar') on conflict (client_type_code) do nothing; -insert into nrfc.client_type_code (client_type_code, description, effective_date, create_user) values ('R', 'First Nation Group', current_timestamp, 'mariamar') on conflict (client_type_code) do nothing; -insert into nrfc.client_type_code (client_type_code, description, effective_date, create_user) values ('S', 'Society', current_timestamp, 'mariamar') on conflict (client_type_code) do nothing; -insert into nrfc.client_type_code (client_type_code, description, effective_date, create_user) values ('T', 'First Nation Tribal Council', current_timestamp, 'mariamar') on conflict (client_type_code) do nothing; -insert into nrfc.client_type_code (client_type_code, description, effective_date, create_user) values ('U', 'Unregistered Company', current_timestamp, 'mariamar') on conflict (client_type_code) do nothing; - -update nrfc.client_type_code set effective_date = '1970-01-01 00:00:00.000'; -update nrfc.district_code set email_address = 'mail@mail.ca'; - --- Test case data Review new client submission - -insert into nrfc.submission values (365, 'R', 'RNC', current_timestamp, current_timestamp, 'BCEIDBUSINESS\\UAT', ' BCEIDBUSINESS\\UAT') on conflict do nothing; -insert into nrfc.submission_contact - (submission_contact_id, submission_id, contact_type_code, first_name, last_name, business_phone_number, email_address, idp_user_id) values - (365, 365, 'DI', 'Load', 'NRS', '(777) 777-7777', 'uattestingmail@uat.testing.lo', 'BCEIDBUSINESS\\UAT') on conflict do nothing; -insert into nrfc.submission_detail values (365, 365, NULL, 'R', 'BC0000006', 'HYPERION CORP', 'C', 'Y', NULL, 'DCR') on conflict do nothing; -insert into nrfc.submission_location - (submission_location_id, submission_id, street_address, country_code, province_code, city_name, postal_code, location_name) values - (365, 365, '712 Canyon View Dr', 'US', 'KS', 'Lansing', '66043-6271', 'Mailing address') on conflict do nothing; -insert into nrfc.submission_location_contact_xref values (365, 365) on conflict do nothing; -insert into nrfc.submission_matching_detail values (365, 365, '{"contact": "00000000,00000000,00000001,00000001,00000001", "registrationNumber": "00000002,00000002"}', 'N', ' already has one. The number is: tyututu. Be sure to keep it for your records.', '2024-04-16 16:00:06.678395', 'idir\\ottomated', true) on conflict do nothing; - --- Test case data Staff submitted data - Individual - -insert into nrfc.submission values (364, 'A', 'SSD', current_timestamp, current_timestamp, 'BCEIDBUSINESS\\UAT', ' BCEIDBUSINESS\\UAT') on conflict do nothing; -insert into nrfc.submission_contact - (submission_contact_id, submission_id, contact_type_code, first_name, last_name, business_phone_number, email_address, idp_user_id) values - (364, 364, 'DI', 'Jhon', 'Wick', '(999) 888-7766', 'jhonwick@uat.testing.lo', 'BCEIDBUSINESS\\UAT') on conflict do nothing; -insert into nrfc.submission_detail - (submission_detail_id, submission_id, client_number, business_type_code, incorporation_number, organization_name, client_type_code, good_standing_ind, birthdate, district_code, work_safe_bc_number, doing_business_as, client_acronym, first_name, middle_name, last_name, notes, identification_type_code, client_identification, identification_country_code, identification_province_code) values - (364, 364, NULL, 'U', NULL, 'ROLAND SOLDIER', 'I', 'Y', '1972-12-04', 'DCR', NULL, NULL, NULL,'ROLAND', NULL,'SOLDIER','GOOD AT SUPPORT','CDDL','9999911','CA','BC') on conflict do nothing; -insert into nrfc.submission_location - (submission_location_id, submission_id, street_address, country_code, province_code, city_name, postal_code, location_name) values - (364, 364, '999 Canyon View Dr', 'US', 'CO', 'Denver', '66043-6271', 'Mailing address') on conflict do nothing; -insert into nrfc.submission_location_contact_xref values (364, 364) on conflict do nothing; -insert into nrfc.submission_matching_detail values (364, 364, '{}', 'N', NULL, '2024-04-16 16:00:06.678395', 'idir\\ottomated', true) on conflict do nothing; diff --git a/backend/src/test/resources/db/migration/V9.1__Test_Data.sql b/backend/src/test/resources/db/migration/V9.1__Test_Data.sql new file mode 100644 index 0000000000..89d8e97bdc --- /dev/null +++ b/backend/src/test/resources/db/migration/V9.1__Test_Data.sql @@ -0,0 +1,64 @@ +update nrfc.client_type_code set effective_date = '1970-01-01 00:00:00.000'; +update nrfc.district_code set email_address = 'mail@mail.ca'; + +-- Test case data Review new client submission + +insert into nrfc.submission +values + (365, 'R', 'RNC', current_timestamp, current_timestamp, 'BCEIDBUSINESS\\UAT', ' BCEIDBUSINESS\\UAT') on conflict do nothing; +insert into nrfc.submission_contact (submission_contact_id, submission_id, contact_type_code, first_name, last_name, business_phone_number, email_address, idp_user_id) + +values + (365, 365, 'DI', 'Load', 'NRS', '(777) 777-7777', 'uattestingmail@uat.testing.lo', 'BCEIDBUSINESS\\UAT') on conflict do nothing; + +insert into nrfc.submission_detail +values + (365, 365, NULL, 'R', 'BC0000006', 'HYPERION CORP', 'C', 'Y', NULL, 'DCR') on conflict do nothing; + +insert into nrfc.submission_location (submission_location_id, submission_id, street_address, country_code, province_code, city_name, postal_code, location_name) +values + (365, 365, '712 Canyon View Dr', 'US', 'KS', 'Lansing', '66043-6271', 'Mailing address') on conflict do nothing; + +insert into nrfc.submission_location_contact_xref +values + (365, 365) on conflict do nothing; + +insert into nrfc.submission_matching_detail +values + (365, 365, '{"contact": "00000000,00000000,00000001,00000001,00000001", "registrationNumber": "00000002,00000002"}', 'N', ' already has one. The number is: tyututu. Be sure to keep it for your records.', '2024-04-16 16:00:06.678395', 'idir\\ottomated', true) on conflict do nothing; + +-- Test case data Staff submitted data - Individual + +insert into nrfc.submission +values + (364, 'A', 'SSD', current_timestamp, current_timestamp, 'BCEIDBUSINESS\\UAT', ' BCEIDBUSINESS\\UAT') on conflict do nothing; + +insert into nrfc.submission_contact (submission_contact_id, submission_id, contact_type_code, first_name, last_name, business_phone_number, email_address, idp_user_id) +values + (364, 364, 'DI', 'Jhon', 'Wick', '(999) 888-7766', 'jhonwick@uat.testing.lo', 'BCEIDBUSINESS\\UAT') on conflict do nothing; + +insert into nrfc.submission_detail (submission_detail_id, submission_id, client_number, business_type_code, incorporation_number, organization_name, client_type_code, good_standing_ind, birthdate, district_code, work_safe_bc_number, doing_business_as, client_acronym, first_name, middle_name, last_name, notes, identification_type_code, client_identification, identification_country_code, identification_province_code) +values + (364, 364, NULL, 'U', NULL, 'ROLAND SOLDIER', 'I', 'Y', '1972-12-04', 'DCR', NULL, NULL, NULL,'ROLAND', NULL,'SOLDIER','GOOD AT SUPPORT','CDDL','9999911','CA','BC') on conflict do nothing; + +insert into nrfc.submission_location (submission_location_id, submission_id, street_address, country_code, province_code, city_name, postal_code, location_name) +values + (364, 364, '999 Canyon View Dr', 'US', 'CO', 'Denver', '66043-6271', 'Mailing address') on conflict do nothing; + +insert into nrfc.submission_location_contact_xref +values + (364, 364) on conflict do nothing; + +insert into nrfc.submission_matching_detail +values + (364, 364, '{}', 'N', NULL, '2024-04-16 16:00:06.678395', 'idir\\ottomated', true) on conflict do nothing; + + +-- Email log data + +insert into nrfc.email_log (create_timestamp, email_address, email_id, email_sent_ind, email_subject, email_variables, exception_message, template_name, update_timestamp) +values +('2024-12-03 06:34:35.753139', 'mail@mail.ca', '94cd9b2e-bb7d-4eb3-be15-37eaedb8d1f7', 'Y', '[UAT] Client number application received', '{"business":{"name":"01-DEV, LOAD","notes":"","district":"UAT","lastName":"","birthdate":"1990-01-01","firstName":"","legalType":"SP","clientType":"USP","middleName":"","businessType":"U","goodStanding":"Y","clientAcronym":"","doingBusinessAs":"","workSafeBcNumber":"","identificationType":"","registrationNumber":"","clientIdentification":"","identificationCountry":"","identificationProvince":""},"frontend":"https://nr-forest-client-00-frontend.uat.gov.bc.ca","userName":"LOAD 01-DEV","address.[0]":{"city":"Victoria","name":"Mailing address","notes":"","address":"2975 Jutland Rd","country":"Canada","province":"British Columbia","faxNumber":"","postalCode":"V8T5J9","emailAddress":"","businessPhoneNumber":"","secondaryPhoneNumber":"","complementaryAddressOne":"","complementaryAddressTwo":""},"contact.[0]":{"name":"John Wick","email":"jwick@thecontinental.ca","phone":"2501234568","lastName":"Wick","faxNumber":"","firstName":"John","secondaryPhoneNumber":""},"districtName":"UAT District","districtEmail":"alliance@mail.ca"}', NULL, 'registration', '2024-12-03 06:34:35.753139'), +('2024-12-03 06:34:35.753139', 'mail@mail.ca', '2b3d2b63-a536-492a-b9d7-da2660d8ba68', 'Y', '[UAT] Client number application received', '{"business":{"name":"HYPERION CORP","notes":"","district":"UAT","lastName":"","birthdate":"1990-01-01","firstName":"","legalType":"SP","clientType":"RSP","middleName":"","businessType":"R","goodStanding":"Y","clientAcronym":"","doingBusinessAs":"HYPERION CORP","workSafeBcNumber":"","identificationType":"","registrationNumber":"FM0403309","clientIdentification":"","identificationCountry":"","identificationProvince":""},"frontend":"https://nr-forest-client-00-frontend.uat.gov.bc.ca","userName":"LOAD 01-DEV","address.[0]":{"city":"Campbell River","name":"Mailing address","notes":"","address":"823 Georgia DR","country":"Canada","province":"British Columbia","faxNumber":"","postalCode":"V9H1S2","emailAddress":"","businessPhoneNumber":"","secondaryPhoneNumber":"","complementaryAddressOne":"","complementaryAddressTwo":""},"contact.[0]":{"name":"LOAD 01-DEV","email":"notanemail@mail.ca","phone":"2255522552","lastName":"01-DEV","faxNumber":"","firstName":"LOAD","secondaryPhoneNumber":""},"contact.[1]":{"name":"JOHN WICK","email":"jwicker@mail.ca","phone":"7787787778","lastName":"WICK","faxNumber":"","firstName":"JOHN","secondaryPhoneNumber":""},"districtName":"UAT District","districtEmail":"alliance@mail.ca"}', NULL, 'registration', '2024-12-03 06:34:35.753139'), +('2024-12-03 06:34:35.753139', 'mail@mail.ca', '914e1e8d-2849-45a9-bba3-6f638398ad4c', 'Y', '[UAT] Client number application approved', '{"business":{"name":"01-DEV, LOAD","clientNumber":"00000172","districtName":"UAT District","districtEmail":"alliance@mail.ca"},"frontend":"https://nr-forest-client-00-frontend.uat.gov.bc.ca","userName":"LOAD 01-DEV"}', NULL, 'approval', '2024-12-03 06:34:35.753139'), +('2024-12-03 06:34:35.753139', 'mail@mail.ca', 'b1602808-49f2-4eb4-b701-2fa6097b13e9', 'Y', '[UAT] HYPERION CORP requires review', '{"business":{"name":"HYPERION CORP","districtName":"UAT District"},"frontend":"https://nr-forest-client-00-frontend.uat.gov.bc.ca","userName":"JOHN WICK","submission":2}', NULL, 'revision', '2024-12-03 06:34:35.753139') on conflict do nothing; diff --git a/database/Dockerfile b/database/Dockerfile index 92e08bf1b7..7df1a5fef1 100644 --- a/database/Dockerfile +++ b/database/Dockerfile @@ -1,6 +1,9 @@ FROM postgres:13 +# Enable the pgcrypto extension +RUN echo "CREATE EXTENSION IF NOT EXISTS pgcrypto;" >> /docker-entrypoint-initdb.d/init.sql + HEALTHCHECK --interval=35s --timeout=4s CMD pg_isready -d db_prod - + # Non-privileged user USER postgres diff --git a/database/openshift.backup.yml b/database/openshift.backup.yml index 1513bab2b0..ede4c44d29 100644 --- a/database/openshift.backup.yml +++ b/database/openshift.backup.yml @@ -95,7 +95,7 @@ objects: spec: containers: - name: ${NAME}-${ZONE}-${COMPONENT} - image: ${REGISTRY}/${ORG}/${NAME}/${COMPONENT}:${TAG} + image: postgres:15 command: ["/bin/sh", "-c"] args: - | @@ -190,7 +190,7 @@ objects: spec: containers: - name: ${NAME}-${ZONE}-${COMPONENT}-restore - image: ${REGISTRY}/${ORG}/${NAME}/${COMPONENT}:${TAG} + image: postgres:15 command: ["/bin/sh", "-c"] args: - |