diff --git a/src/lib/db/user-store.ts b/src/lib/db/user-store.ts index 18e5b4309239..04d2ab73a6d6 100644 --- a/src/lib/db/user-store.ts +++ b/src/lib/db/user-store.ts @@ -108,8 +108,15 @@ class UserStore implements IUserStore { } async insert(user: ICreateUser): Promise { + const emailHash = user.email + ? this.db.raw('md5(?)', [user.email]) + : null; const rows = await this.db(TABLE) - .insert({ ...mapUserToColumns(user), created_at: new Date() }) + .insert({ + ...mapUserToColumns(user), + email_hash: emailHash, + created_at: new Date(), + }) .returning(USER_COLUMNS); return rowToUser(rows[0]); } diff --git a/src/migrations/20241112113555-user-email-hash.js b/src/migrations/20241112113555-user-email-hash.js new file mode 100644 index 000000000000..08d04517b819 --- /dev/null +++ b/src/migrations/20241112113555-user-email-hash.js @@ -0,0 +1,18 @@ +exports.up = (db, cb) => { + db.runSql(` + ALTER TABLE users + ADD COLUMN IF NOT EXISTS email_hash VARCHAR(32); + + UPDATE users + SET email_hash = md5(email::text); + `, cb); + +}; + +exports.down = (db, cb) => { + db.runSql(` + ALTER TABLE users + DROP COLUMN IF EXISTS email_hash; + `, cb); +}; + diff --git a/src/test/e2e/api/admin/user-admin.e2e.test.ts b/src/test/e2e/api/admin/user-admin.e2e.test.ts index df670db1688f..1467fdabb574 100644 --- a/src/test/e2e/api/admin/user-admin.e2e.test.ts +++ b/src/test/e2e/api/admin/user-admin.e2e.test.ts @@ -18,6 +18,7 @@ import { randomId } from '../../../../lib/util/random-id'; import { omitKeys } from '../../../../lib/util/omit-keys'; import type { ISessionStore } from '../../../../lib/types/stores/session-store'; import type { IUnleashStores } from '../../../../lib/types'; +import { createHash } from 'crypto'; let stores: IUnleashStores; let db: ITestDb; @@ -405,3 +406,25 @@ test('Anonymises name, username and email fields if anonymiseEventLog flag is se expect(body.users[0].name).toEqual('3a8b17647@unleash.run'); expect(body.users[0].username).toEqual(''); // Not set, so anonymise should return the empty string. }); + +test('creates user with email md5 hash', async () => { + await app.request + .post('/api/admin/user-admin') + .send({ + email: `hasher@getunleash.ai`, + name: `Some Name Hash`, + rootRole: editorRole.id, + }) + .set('Content-Type', 'application/json'); + + const user = await db + .rawDatabase('users') + .where({ email: 'hasher@getunleash.ai' }) + .first(['email_hash']); + + const expectedHash = createHash('md5') + .update('hasher@getunleash.ai') + .digest('hex'); + + expect(user.email_hash).toBe(expectedHash); +});