-
Notifications
You must be signed in to change notification settings - Fork 1
Encryption of sensitive data #194
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
junaidferoz
wants to merge
39
commits into
dev
Choose a base branch
from
feat-156-pgcrypto_encryption
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 32 commits
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
c49c087
feat: add pgcrypto user table encryption migration
junaidferoz 7ea39b5
fix(db): COALESCE user view insert booleans and require DB_ENCRYPTION…
junaidferoz 698a7f4
Merge branch 'dev' into feat-156-pgcrypto_encryption
junaidferoz 1d7c03e
fix: remove old implementation of encryption
karimouf 0228f6c
feat: add encryption functionalities
karimouf fbad854
feat: add hooks for automatic encryption/decryption handling
karimouf e62f502
feat: add fields to be encrypted
karimouf 07dc3f2
feat: add db encryption key to the env file
karimouf 7674fa8
feat: add migration to encrypt user data
karimouf 873ef11
Merge branch 'dev' into feat-156-pgcrypto_encryption
karimouf 168a0c9
feat: removing extra role button
karimouf 11f43e5
fix: update salt with encryption key
karimouf a261817
feat: add before bulk create hook
karimouf 95c46ac
fix: remove wrong naming
karimouf b119ead
fix: remove wrong naming
karimouf 980387a
Merge branch 'dev' into feat-156-pgcrypto_encryption
karimouf bc4902d
feat: backup command
karimouf 67d7251
feat: initialize new encryption key each server restart
karimouf b2d778b
fix: add a down method for the encrypted fields
karimouf 770bc09
fix
karimouf 12d5eb2
fix: remove encrption key from env file
karimouf 8355e11
feat: add enable encryption env variable to enable/disable encryption
karimouf 444aadc
feat: add script to change encryption key
karimouf 8250438
feat: change db encryption state when the .env encryption state is ch…
karimouf b4e5a7e
feat: generate new key if no new key is provided
karimouf 67d5eaa
Merge branch 'dev' into feat-156-pgcrypto_encryption
bingobongomann 8cf9586
feat: enable decrypting before back up
karimouf 681110b
Merge branch 'feat-156-pgcrypto_encryption' of https://github.com/UKP…
karimouf f693c3e
refactor: db is cloned and then decrypted
karimouf 0c94ad0
Merge branch 'dev' into feat-156-pgcrypto_encryption
karimouf 1029133
fix: fix function docstring
karimouf bc1772c
feat: unique fields have an addition column in order to preserve the …
karimouf ea05cc7
feat: add error message handling to handle unique constraint
karimouf 199ec00
fix: add guard for mapping empty arrays
karimouf 73f5397
fic: add warning when changing encryption env variable
karimouf afb47ac
Merge branch 'dev' into feat-156-pgcrypto_encryption
karimouf 8c5a123
Merge branch 'dev' into feat-156-pgcrypto_encryption
karimouf faf1de9
refactor: move encryption in helper folder
karimouf 303ff11
Merge branch 'dev' into feat-156-pgcrypto_encryption
karimouf File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
|
karimouf marked this conversation as resolved.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
167 changes: 167 additions & 0 deletions
167
backend/db/migrations/20260612100001-encrypt-user-fields.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| 'use strict'; | ||
|
|
||
| /** | ||
| * Encrypt existing plaintext values in user fields: firstName, lastName, email, initialPassword. | ||
| * Also populates emailHash from the newly encrypted email value. | ||
| * | ||
| * Requires DB_ENCRYPTION_KEY to be set in the environment. | ||
| * Skips rows where the field already appears encrypted (safe to re-run). | ||
| */ | ||
|
|
||
| const { encrypt, getKey, initializeEncryptionKey, decrypt } = require('../../utils/encryption'); | ||
|
|
||
| module.exports = { | ||
| async up(queryInterface) { | ||
|
karimouf marked this conversation as resolved.
|
||
| const isEncryptionEnabled = process.env.ENCRYPTION_ENABLED === 'true'; | ||
| if (!isEncryptionEnabled) { | ||
| return; | ||
| } | ||
| initializeEncryptionKey(); | ||
| const encryptionKey = getKey(); | ||
| if (!encryptionKey) { | ||
| throw new Error( | ||
| 'DB_ENCRYPTION_KEY must be set before running the user encryption data migration' | ||
| ); | ||
| } | ||
|
|
||
| const transaction = await queryInterface.sequelize.transaction(); | ||
| try { | ||
| const users = await queryInterface.sequelize.query( | ||
| `SELECT id, "firstName", "lastName", email, "initialPassword", "twoFactorOtp", "totpSecret", "orcidId", "ldapUsername", "samlNameId", "salt" FROM "user"`, | ||
| { type: queryInterface.sequelize.QueryTypes.SELECT, transaction } | ||
| ); | ||
|
|
||
| for (const user of users) { | ||
| const updates = {}; | ||
|
|
||
| if (user.firstName) { | ||
| updates.firstName = encrypt(user.firstName); | ||
| } | ||
| if (user.lastName) { | ||
| updates.lastName = encrypt(user.lastName); | ||
| } | ||
| if (user.email) { | ||
| const encryptedEmail = encrypt(user.email); | ||
| updates.email = encryptedEmail; | ||
| } | ||
| if (user.initialPassword) { | ||
| updates.initialPassword = encrypt(user.initialPassword); | ||
| } | ||
| if (user.twoFactorOtp) { | ||
| updates.twoFactorOtp = encrypt(user.twoFactorOtp); | ||
| } | ||
| if (user.totpSecret) { | ||
| updates.totpSecret = encrypt(user.totpSecret); | ||
| } | ||
| if (user.orcidId) { | ||
| updates.orcidId = encrypt(user.orcidId); | ||
| } | ||
| if (user.ldapUsername) { | ||
| updates.ldapUsername = encrypt(user.ldapUsername); | ||
| } | ||
| if (user.samlNameId) { | ||
| updates.samlNameId = encrypt(user.samlNameId); | ||
| } | ||
| if (user.salt) { | ||
| updates.salt = encrypt(user.salt); | ||
| } | ||
|
|
||
| if (Object.keys(updates).length > 0) { | ||
| const setClauses = Object.keys(updates) | ||
| .map(col => `"${col}" = :${col}`) | ||
| .join(', '); | ||
|
|
||
| await queryInterface.sequelize.query( | ||
| `UPDATE "user" SET ${setClauses} WHERE id = :id`, | ||
| { | ||
| replacements: { ...updates, id: user.id }, | ||
| transaction, | ||
| } | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| await transaction.commit(); | ||
| } catch (err) { | ||
| await transaction.rollback(); | ||
| throw err; | ||
| } | ||
| }, | ||
|
|
||
| async down(queryInterface) { | ||
| const isEncryptionEnabled = process.env.ENCRYPTION_ENABLED === 'true'; | ||
| if (!isEncryptionEnabled) { | ||
| return; | ||
| } | ||
| initializeEncryptionKey(); | ||
| const encryptionKey = getKey(); | ||
| if (!encryptionKey) { | ||
| throw new Error( | ||
| 'DB_ENCRYPTION_KEY must be set before running the user encryption data migration' | ||
| ); | ||
| } | ||
|
|
||
| const transaction = await queryInterface.sequelize.transaction(); | ||
| try { | ||
| const users = await queryInterface.sequelize.query( | ||
| `SELECT id, "firstName", "lastName", email, "initialPassword", "twoFactorOtp", "totpSecret", "orcidId", "ldapUsername", "samlNameId", "salt" FROM "user"`, | ||
| { type: queryInterface.sequelize.QueryTypes.SELECT, transaction } | ||
| ); | ||
|
|
||
| for (const user of users) { | ||
| const updates = {}; | ||
|
|
||
| if (user.firstName) { | ||
| updates.firstName = decrypt(user.firstName); | ||
| } | ||
| if (user.lastName) { | ||
| updates.lastName = decrypt(user.lastName); | ||
| } | ||
| if (user.email) { | ||
| const decryptedEmail = decrypt(user.email); | ||
| updates.email = decryptedEmail; | ||
| } | ||
| if (user.initialPassword) { | ||
| updates.initialPassword = decrypt(user.initialPassword); | ||
| } | ||
| if (user.twoFactorOtp) { | ||
| updates.twoFactorOtp = decrypt(user.twoFactorOtp); | ||
| } | ||
| if (user.totpSecret) { | ||
| updates.totpSecret = decrypt(user.totpSecret); | ||
| } | ||
| if (user.orcidId) { | ||
| updates.orcidId = decrypt(user.orcidId); | ||
| } | ||
| if (user.ldapUsername) { | ||
| updates.ldapUsername = decrypt(user.ldapUsername); | ||
| } | ||
| if (user.samlNameId) { | ||
| updates.samlNameId = decrypt(user.samlNameId); | ||
| } | ||
| if (user.salt) { | ||
| updates.salt = decrypt(user.salt); | ||
| } | ||
|
|
||
| if (Object.keys(updates).length > 0) { | ||
| const setClauses = Object.keys(updates) | ||
| .map(col => `"${col}" = :${col}`) | ||
| .join(', '); | ||
|
|
||
| await queryInterface.sequelize.query( | ||
| `UPDATE "user" SET ${setClauses} WHERE id = :id`, | ||
| { | ||
| replacements: { ...updates, id: user.id }, | ||
| transaction, | ||
| } | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| await transaction.commit(); | ||
| } catch (err) { | ||
| await transaction.rollback(); | ||
| throw err; | ||
| } | ||
| }, | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.