From 2bd5e8893518da4f575a352af8d075631f2f4532 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Feb 2022 03:12:59 +0100 Subject: [PATCH 01/24] combine login_users and state_users into state users --- .../migrations/0017-combine_user_tables.ts | 155 ++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 database/migrations/0017-combine_user_tables.ts diff --git a/database/migrations/0017-combine_user_tables.ts b/database/migrations/0017-combine_user_tables.ts new file mode 100644 index 000000000..fe4e0ea4e --- /dev/null +++ b/database/migrations/0017-combine_user_tables.ts @@ -0,0 +1,155 @@ +/* MIGRATION TO COMBINE ALL USER TABLES + * + * This migration combines the tables `login_users` + * and `login_user_backups` into the `state_users` + * table. + */ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + // Drop column `group_id` since it contains uniform data which is not the same as the uniform data + // on login_users. Since we do not need this data anyway, we sjust throw it away. + await queryFn('ALTER TABLE `state_users` DROP COLUMN `group_id`;') + + // Remove the unique constraint from the pubkey + // TODO - check for code impact + await queryFn('ALTER TABLE `state_users` DROP INDEX `public_key`;') + + // Allow NULL on the `state_users` pubkey like it is allowed on `login_users` + // TODO remove the random key shenanigans + await queryFn('ALTER TABLE `state_users` MODIFY COLUMN `public_key` binary(32) DEFAULT NULL;') + + // instead use a unique constraint for the email like on `login_users` + // therefore do not allow null on `email` anymore + await queryFn( + 'ALTER TABLE `state_users` MODIFY COLUMN `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL;', + ) + // TODO - check for code impact + await queryFn('ALTER TABLE `state_users` ADD CONSTRAINT `email` UNIQUE KEY (`email`);') + + // Create `login_user_id` column - to store the login_users.id field to not break references. + // TODO - what happens when we create a new user - how do we generate the login_user_id? + await queryFn( + 'ALTER TABLE `state_users` ADD COLUMN `login_user_id` int(10) unsigned DEFAULT NULL AFTER `id`;', + ) + + // Create missing data columns for the data stored in `login_users` + await queryFn( + "ALTER TABLE `state_users` ADD COLUMN `description` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT '' AFTER `disabled`;", + ) + await queryFn( + 'ALTER TABLE `state_users` ADD COLUMN `password` bigint(20) unsigned DEFAULT 0 AFTER `description`;', + ) + await queryFn( + 'ALTER TABLE `state_users` ADD COLUMN `privkey` binary(80) DEFAULT NULL AFTER `public_key`;', + ) + await queryFn( + 'ALTER TABLE `state_users` ADD COLUMN `email_hash` binary(32) DEFAULT NULL AFTER `password`;', + ) + await queryFn( + 'ALTER TABLE `state_users` ADD COLUMN `created` datetime NOT NULL DEFAULT current_timestamp() AFTER `email_hash`;', + ) + await queryFn( + 'ALTER TABLE `state_users` ADD COLUMN `email_checked` tinyint(4) NOT NULL DEFAULT 0 AFTER `created`;', + ) + await queryFn( + 'ALTER TABLE `state_users` ADD COLUMN `passphrase_shown` tinyint(4) NOT NULL DEFAULT 0 AFTER `email_checked`;', + ) + await queryFn( + "ALTER TABLE `state_users` ADD COLUMN `language` varchar(4) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'de' AFTER `passphrase_shown`;", + ) + await queryFn( + 'ALTER TABLE `state_users` ADD COLUMN `publisher_id` int(11) DEFAULT 0 AFTER `language`;', + ) + + // Move data from `login_users` to the newly modified `state_users` table. + // The following rules for overwriting data applies: + // email is the matching criteria + // public_key is overwritten by `login_users`.`pubkey` (we have validated the passphrases here) (2 keys differ) + // first_name is more accurate on `state_users` and stays unchanged (1 users with different first_* & last_name) + // last_name is more accurate on `state_users` and stays unchanged (1 users with different first_* & last_name) + // username does not contain any relevant data, either NULL or '' and therefore we do not change anything here + // disabled does not differ, we can omit it + await queryFn(` + UPDATE state_users + LEFT JOIN login_users ON state_users.email = login_users.email + SET state_users.public_key = login_users.pubkey, + state_users.login_user_id = login_users.id, + state_users.description = login_users.description, + state_users.password = login_users.password, + state_users.privkey = login_users.privkey, + state_users.email_hash = login_users.email_hash, + state_users.created = login_users.created, + state_users.email_checked = login_users.email_checked, + state_users.passphrase_shown = login_users.passphrase_shown, + state_users.language = login_users.language, + state_users.publisher_id = login_users.publisher_id + ; + `) + + // Drop `login_users` table + await queryFn('DROP TABLE `login_users`;') +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn(` + CREATE TABLE \`login_users\` ( + \`id\` int(10) unsigned NOT NULL AUTO_INCREMENT, + \`email\` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL, + \`first_name\` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL, + \`last_name\` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '', + \`username\` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '', + \`description\` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT '', + \`password\` bigint(20) unsigned DEFAULT 0, + \`pubkey\` binary(32) DEFAULT NULL, + \`privkey\` binary(80) DEFAULT NULL, + \`email_hash\` binary(32) DEFAULT NULL, + \`created\` datetime NOT NULL DEFAULT current_timestamp(), + \`email_checked\` tinyint(4) NOT NULL DEFAULT 0, + \`passphrase_shown\` tinyint(4) NOT NULL DEFAULT 0, + \`language\` varchar(4) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'de', + \`disabled\` tinyint(4) DEFAULT 0, + \`group_id\` int(10) unsigned DEFAULT 0, + \`publisher_id\` int(11) DEFAULT 0, + PRIMARY KEY (\`id\`), + UNIQUE KEY \`email\` (\`email\`) + ) ENGINE=InnoDB AUTO_INCREMENT=2363 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + `) + await queryFn(` + INSERT INTO login_users + ( id, email, first_name, last_name, username, + description, password, pubkey, privkey, email_hash, + created, email_checked, passphrase_shown, language, + disabled, group_id, publisher_id ) + ( SELECT login_user_id AS id, email, first_name, + last_name, username, description, password, + public_key AS pubkey, privkey, email_hash, + created, email_checked, passphrase_shown, + language, disabled, '1' AS group_id, + publisher_id + FROM state_users ) + ; + `) + await queryFn('ALTER TABLE `state_users` DROP COLUMN `publisher_id`;') + await queryFn('ALTER TABLE `state_users` DROP COLUMN `language`;') + await queryFn('ALTER TABLE `state_users` DROP COLUMN `passphrase_shown`;') + await queryFn('ALTER TABLE `state_users` DROP COLUMN `email_checked`;') + await queryFn('ALTER TABLE `state_users` DROP COLUMN `created`;') + await queryFn('ALTER TABLE `state_users` DROP COLUMN `email_hash`;') + await queryFn('ALTER TABLE `state_users` DROP COLUMN `privkey`;') + await queryFn('ALTER TABLE `state_users` DROP COLUMN `password`;') + await queryFn('ALTER TABLE `state_users` DROP COLUMN `description`;') + await queryFn('ALTER TABLE `state_users` DROP COLUMN `login_user_id`;') + await queryFn('ALTER TABLE `state_users` DROP INDEX `email`;') + await queryFn( + 'ALTER TABLE `state_users` MODIFY COLUMN `email` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL;', + ) + // Note: if the public_key is NULL, we need to set a random key in order to meet the constraint + await queryFn( + 'UPDATE `state_users` SET public_key = UNHEX(SHA1(RAND())) WHERE public_key IS NULL;', + ) + await queryFn('ALTER TABLE `state_users` MODIFY COLUMN `public_key` binary(32) NOT NULL;') + await queryFn('ALTER TABLE `state_users` ADD CONSTRAINT `public_key` UNIQUE KEY (`public_key`);') + await queryFn( + 'ALTER TABLE `state_users` ADD COLUMN `group_id` int(10) unsigned NOT NULL DEFAULT 0 AFTER index_id;', + ) +} From 2cc823646a1ee11ab6bb44acc61c83d57b144cf3 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Feb 2022 03:59:32 +0100 Subject: [PATCH 02/24] entity definition for server_users (user) removed loginuser entity --- .../entity/0017-combine_user_tables/User.ts | 74 +++++++++++++++++++ database/entity/LoginUser.ts | 1 - database/entity/User.ts | 2 +- 3 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 database/entity/0017-combine_user_tables/User.ts delete mode 100644 database/entity/LoginUser.ts diff --git a/database/entity/0017-combine_user_tables/User.ts b/database/entity/0017-combine_user_tables/User.ts new file mode 100644 index 000000000..a9bf29d24 --- /dev/null +++ b/database/entity/0017-combine_user_tables/User.ts @@ -0,0 +1,74 @@ +import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm' +import { UserSetting } from '../UserSetting' + +@Entity('state_users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) +export class User extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ name: 'login_user_id', default: null, unsigned: true }) + loginUserId: number + + @Column({ name: 'index_id', type: 'smallint', default: 0, nullable: false }) + indexId: number + + @Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true }) + pubKey: Buffer + + @Column({ name: 'privkey', type: 'binary', length: 80, default: null, nullable: true }) + privKey: Buffer + + @Column({ length: 255, unique: true, nullable: false, collation: 'utf8mb4_unicode_ci' }) + email: string + + @Column({ + name: 'first_name', + length: 255, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + firstName: string + + @Column({ + name: 'last_name', + length: 255, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + lastName: string + + @Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' }) + username: string + + @Column({ type: 'bool', default: false }) + disabled: boolean + + @Column({ type: 'mediumtext', default: '', collation: 'utf8mb4_unicode_ci', nullable: true }) + description: string + + @Column({ type: 'bigint', default: 0, unsigned: true }) + password: BigInt + + @Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true }) + emailHash: Buffer + + @Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false }) + createdAt: Date + + @Column({ name: 'email_checked', type: 'bool', nullable: false, default: false }) + emailChecked: boolean + + @Column({ name: 'passphrase_shown', type: 'bool', nullable: false, default: false }) + passphraseShown: boolean + + @Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false }) + language: string + + @Column({ name: 'publisher_id', default: 0 }) + publisherId: number + + @OneToMany(() => UserSetting, (userSetting) => userSetting.user) + settings: UserSetting[] +} diff --git a/database/entity/LoginUser.ts b/database/entity/LoginUser.ts deleted file mode 100644 index b22e1137f..000000000 --- a/database/entity/LoginUser.ts +++ /dev/null @@ -1 +0,0 @@ -export { LoginUser } from './0006-login_users_collation/LoginUser' diff --git a/database/entity/User.ts b/database/entity/User.ts index b20e934f1..3cf3e6c46 100644 --- a/database/entity/User.ts +++ b/database/entity/User.ts @@ -1 +1 @@ -export { User } from './0002-add_settings/User' +export { User } from './0017-combine_user_tables/User' From 7fd65ae318ebc49f90be74c87b270b165167ed45 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Feb 2022 04:00:09 +0100 Subject: [PATCH 03/24] updated old entity definitions to be more correct --- database/entity/0001-init_db/User.ts | 6 +++--- database/entity/0002-add_settings/User.ts | 6 +++--- .../entity/0003-login_server_tables/LoginUser.ts | 12 ++++++------ .../entity/0006-login_users_collation/LoginUser.ts | 12 ++++++------ 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/database/entity/0001-init_db/User.ts b/database/entity/0001-init_db/User.ts index b349e2584..be2c4c5ad 100644 --- a/database/entity/0001-init_db/User.ts +++ b/database/entity/0001-init_db/User.ts @@ -7,13 +7,13 @@ export class User extends BaseEntity { @PrimaryGeneratedColumn('increment', { unsigned: true }) id: number - @Column({ name: 'index_id', default: 0 }) + @Column({ name: 'index_id', type: 'smallint', default: 0, nullable: false }) indexId: number @Column({ name: 'group_id', default: 0, unsigned: true }) groupId: number - @Column({ type: 'binary', length: 32, name: 'public_key' }) + @Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true }) pubkey: Buffer @Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' }) @@ -40,7 +40,7 @@ export class User extends BaseEntity { @Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' }) username: string - @Column() + @Column({ type: 'bool', default: false }) disabled: boolean @OneToOne(() => Balance, (balance) => balance.user) diff --git a/database/entity/0002-add_settings/User.ts b/database/entity/0002-add_settings/User.ts index 40f5d400a..a756cbbd5 100644 --- a/database/entity/0002-add_settings/User.ts +++ b/database/entity/0002-add_settings/User.ts @@ -7,13 +7,13 @@ export class User extends BaseEntity { @PrimaryGeneratedColumn('increment', { unsigned: true }) id: number - @Column({ name: 'index_id', default: 0 }) + @Column({ name: 'index_id', type: 'smallint', default: 0, nullable: false }) indexId: number @Column({ name: 'group_id', default: 0, unsigned: true }) groupId: number - @Column({ type: 'binary', length: 32, name: 'public_key' }) + @Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true }) pubkey: Buffer @Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' }) @@ -40,7 +40,7 @@ export class User extends BaseEntity { @Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' }) username: string - @Column() + @Column({ type: 'bool', default: false }) disabled: boolean @OneToMany(() => UserSetting, (userSetting) => userSetting.user) diff --git a/database/entity/0003-login_server_tables/LoginUser.ts b/database/entity/0003-login_server_tables/LoginUser.ts index 07816254f..cacc27be1 100644 --- a/database/entity/0003-login_server_tables/LoginUser.ts +++ b/database/entity/0003-login_server_tables/LoginUser.ts @@ -19,7 +19,7 @@ export class LoginUser extends BaseEntity { @Column({ length: 255, default: '' }) username: string - @Column({ default: '', nullable: true }) + @Column({ type: 'mediumtext', default: '', nullable: true }) description: string @Column({ type: 'bigint', default: 0, unsigned: true }) @@ -34,19 +34,19 @@ export class LoginUser extends BaseEntity { @Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true }) emailHash: Buffer - @Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP' }) + @Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false }) createdAt: Date - @Column({ name: 'email_checked', default: 0 }) + @Column({ name: 'email_checked', type: 'bool', nullable: false, default: false }) emailChecked: boolean - @Column({ name: 'passphrase_shown', default: 0 }) + @Column({ name: 'passphrase_shown', type: 'bool', nullable: false, default: false }) passphraseShown: boolean - @Column({ length: 4, default: 'de' }) + @Column({ length: 4, default: 'de', nullable: false }) language: string - @Column({ default: 0 }) + @Column({ type: 'bool', default: false }) disabled: boolean @Column({ name: 'group_id', default: 0, unsigned: true }) diff --git a/database/entity/0006-login_users_collation/LoginUser.ts b/database/entity/0006-login_users_collation/LoginUser.ts index e12c82e27..f29a46ffe 100644 --- a/database/entity/0006-login_users_collation/LoginUser.ts +++ b/database/entity/0006-login_users_collation/LoginUser.ts @@ -19,7 +19,7 @@ export class LoginUser extends BaseEntity { @Column({ length: 255, default: '', collation: 'utf8mb4_unicode_ci' }) username: string - @Column({ default: '', collation: 'utf8mb4_unicode_ci', nullable: true }) + @Column({ type: 'mediumtext', default: '', collation: 'utf8mb4_unicode_ci', nullable: true }) description: string @Column({ type: 'bigint', default: 0, unsigned: true }) @@ -34,19 +34,19 @@ export class LoginUser extends BaseEntity { @Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true }) emailHash: Buffer - @Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP' }) + @Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false }) createdAt: Date - @Column({ name: 'email_checked', default: 0 }) + @Column({ name: 'email_checked', type: 'bool', nullable: false, default: false }) emailChecked: boolean - @Column({ name: 'passphrase_shown', default: 0 }) + @Column({ name: 'passphrase_shown', type: 'bool', nullable: false, default: false }) passphraseShown: boolean - @Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci' }) + @Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false }) language: string - @Column({ default: 0 }) + @Column({ type: 'bool', default: false }) disabled: boolean @Column({ name: 'group_id', default: 0, unsigned: true }) From d7cd4d32c02754a0ad25ccb6a117aa5240d60b22 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Feb 2022 04:12:25 +0100 Subject: [PATCH 04/24] corrected entity includes to allow compilation of the old removed interlinked entities (login_users) --- .../entity/0003-login_server_tables/LoginUser.ts | 2 +- .../0003-login_server_tables/LoginUserBackup.ts | 2 +- .../0006-login_users_collation/LoginUser.ts | 2 +- .../0017-combine_user_tables/LoginUserBackup.ts | 16 ++++++++++++++++ database/entity/LoginUserBackup.ts | 2 +- database/entity/index.ts | 2 -- 6 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 database/entity/0017-combine_user_tables/LoginUserBackup.ts diff --git a/database/entity/0003-login_server_tables/LoginUser.ts b/database/entity/0003-login_server_tables/LoginUser.ts index cacc27be1..a3a83f450 100644 --- a/database/entity/0003-login_server_tables/LoginUser.ts +++ b/database/entity/0003-login_server_tables/LoginUser.ts @@ -1,5 +1,5 @@ import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm' -import { LoginUserBackup } from '../LoginUserBackup' +import { LoginUserBackup } from './LoginUserBackup' // Moriz: I do not like the idea of having two user tables @Entity('login_users') diff --git a/database/entity/0003-login_server_tables/LoginUserBackup.ts b/database/entity/0003-login_server_tables/LoginUserBackup.ts index 7152e12e5..39f5e0db5 100644 --- a/database/entity/0003-login_server_tables/LoginUserBackup.ts +++ b/database/entity/0003-login_server_tables/LoginUserBackup.ts @@ -1,5 +1,5 @@ import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, JoinColumn, OneToOne } from 'typeorm' -import { LoginUser } from '../LoginUser' +import { LoginUser } from './LoginUser' @Entity('login_user_backups') export class LoginUserBackup extends BaseEntity { diff --git a/database/entity/0006-login_users_collation/LoginUser.ts b/database/entity/0006-login_users_collation/LoginUser.ts index f29a46ffe..fdb17f4ad 100644 --- a/database/entity/0006-login_users_collation/LoginUser.ts +++ b/database/entity/0006-login_users_collation/LoginUser.ts @@ -1,5 +1,5 @@ import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm' -import { LoginUserBackup } from '../LoginUserBackup' +import { LoginUserBackup } from '../0003-login_server_tables/LoginUserBackup' // Moriz: I do not like the idea of having two user tables @Entity('login_users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) diff --git a/database/entity/0017-combine_user_tables/LoginUserBackup.ts b/database/entity/0017-combine_user_tables/LoginUserBackup.ts new file mode 100644 index 000000000..456f9d8f7 --- /dev/null +++ b/database/entity/0017-combine_user_tables/LoginUserBackup.ts @@ -0,0 +1,16 @@ +import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, JoinColumn, OneToOne } from 'typeorm' + +@Entity('login_user_backups') +export class LoginUserBackup extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ type: 'text', name: 'passphrase', nullable: false }) + passphrase: string + + @Column({ name: 'user_id', nullable: false }) + userId: number + + @Column({ name: 'mnemonic_type', default: -1 }) + mnemonicType: number +} diff --git a/database/entity/LoginUserBackup.ts b/database/entity/LoginUserBackup.ts index 23d2c9271..7a3031a33 100644 --- a/database/entity/LoginUserBackup.ts +++ b/database/entity/LoginUserBackup.ts @@ -1 +1 @@ -export { LoginUserBackup } from './0003-login_server_tables/LoginUserBackup' +export { LoginUserBackup } from './0017-combine_user_tables/LoginUserBackup' diff --git a/database/entity/index.ts b/database/entity/index.ts index cd1dd4e21..d64c66d51 100644 --- a/database/entity/index.ts +++ b/database/entity/index.ts @@ -1,7 +1,6 @@ import { Balance } from './Balance' import { LoginElopageBuys } from './LoginElopageBuys' import { LoginEmailOptIn } from './LoginEmailOptIn' -import { LoginUser } from './LoginUser' import { LoginUserBackup } from './LoginUserBackup' import { Migration } from './Migration' import { ServerUser } from './ServerUser' @@ -18,7 +17,6 @@ export const entities = [ Balance, LoginElopageBuys, LoginEmailOptIn, - LoginUser, LoginUserBackup, Migration, ServerUser, From fa7c7826721f2e971d9ac3cdc475a617305c5765 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Feb 2022 04:43:30 +0100 Subject: [PATCH 05/24] fixed seed (untested, just building) --- database/src/factories/login-user.factory.ts | 30 -------------- database/src/factories/user.factory.ts | 16 ++++++-- database/src/interface/UserContext.ts | 13 +----- database/src/interface/UserInterface.ts | 3 +- database/src/seeds/helpers/user-helpers.ts | 41 ++++--------------- database/src/seeds/users/bibi-bloxberg.ts | 1 + database/src/seeds/users/bob-baumeister.ts | 1 + .../src/seeds/users/garrick-ollivander.ts | 1 + database/src/seeds/users/peter-lustig.ts | 1 + .../src/seeds/users/raeuber-hotzenplotz.ts | 1 + 10 files changed, 30 insertions(+), 78 deletions(-) delete mode 100644 database/src/factories/login-user.factory.ts diff --git a/database/src/factories/login-user.factory.ts b/database/src/factories/login-user.factory.ts deleted file mode 100644 index b3c0312f3..000000000 --- a/database/src/factories/login-user.factory.ts +++ /dev/null @@ -1,30 +0,0 @@ -import Faker from 'faker' -import { define } from 'typeorm-seeding' -import { LoginUser } from '../../entity/LoginUser' -import { randomBytes } from 'crypto' -import { LoginUserContext } from '../interface/UserContext' - -define(LoginUser, (faker: typeof Faker, context?: LoginUserContext) => { - if (!context) context = {} - - const user = new LoginUser() - user.email = context.email ? context.email : faker.internet.email() - user.firstName = context.firstName ? context.firstName : faker.name.firstName() - user.lastName = context.lastName ? context.lastName : faker.name.lastName() - user.username = context.username ? context.username : faker.internet.userName() - user.description = context.description ? context.description : faker.random.words(4) - // TODO Create real password and keys/hash - user.password = context.password ? context.password : BigInt(0) - user.pubKey = context.pubKey ? context.pubKey : randomBytes(32) - user.privKey = context.privKey ? context.privKey : randomBytes(80) - user.emailHash = context.emailHash ? context.emailHash : randomBytes(32) - user.createdAt = context.createdAt ? context.createdAt : faker.date.recent() - user.emailChecked = context.emailChecked === undefined ? false : context.emailChecked - user.passphraseShown = context.passphraseShown ? context.passphraseShown : false - user.language = context.language ? context.language : 'en' - user.disabled = context.disabled ? context.disabled : false - user.groupId = context.groupId ? context.groupId : 1 - user.publisherId = context.publisherId ? context.publisherId : 0 - - return user -}) diff --git a/database/src/factories/user.factory.ts b/database/src/factories/user.factory.ts index 1f684f23f..338a854f1 100644 --- a/database/src/factories/user.factory.ts +++ b/database/src/factories/user.factory.ts @@ -1,21 +1,31 @@ import Faker from 'faker' import { define } from 'typeorm-seeding' import { User } from '../../entity/User' -import { randomBytes } from 'crypto' +import { randomBytes, randomInt } from 'crypto' import { UserContext } from '../interface/UserContext' define(User, (faker: typeof Faker, context?: UserContext) => { if (!context) context = {} const user = new User() - user.pubkey = context.pubkey ? context.pubkey : randomBytes(32) + user.pubKey = context.pubKey ? context.pubKey : randomBytes(32) user.email = context.email ? context.email : faker.internet.email() user.firstName = context.firstName ? context.firstName : faker.name.firstName() user.lastName = context.lastName ? context.lastName : faker.name.lastName() user.username = context.username ? context.username : faker.internet.userName() user.disabled = context.disabled ? context.disabled : false - user.groupId = 0 + user.loginUserId = context.loginUserId ? context.loginUserId : randomInt(999999) user.indexId = 0 + user.description = context.description ? context.description : faker.random.words(4) + // TODO Create real password and keys/hash + user.password = context.password ? context.password : BigInt(0) + user.privKey = context.privKey ? context.privKey : randomBytes(80) + user.emailHash = context.emailHash ? context.emailHash : randomBytes(32) + user.createdAt = context.createdAt ? context.createdAt : faker.date.recent() + user.emailChecked = context.emailChecked === undefined ? false : context.emailChecked + user.passphraseShown = context.passphraseShown ? context.passphraseShown : false + user.language = context.language ? context.language : 'en' + user.publisherId = context.publisherId ? context.publisherId : 0 return user }) diff --git a/database/src/interface/UserContext.ts b/database/src/interface/UserContext.ts index eb4323aee..a23a50109 100644 --- a/database/src/interface/UserContext.ts +++ b/database/src/interface/UserContext.ts @@ -1,28 +1,19 @@ export interface UserContext { - pubkey?: Buffer + loginUserId?: number + pubKey?: Buffer email?: string firstName?: string lastName?: string username?: string disabled?: boolean -} - -export interface LoginUserContext { - email?: string - firstName?: string - lastName?: string - username?: string description?: string password?: BigInt - pubKey?: Buffer privKey?: Buffer emailHash?: Buffer createdAt?: Date emailChecked?: boolean passphraseShown?: boolean language?: string - disabled?: boolean - groupId?: number publisherId?: number } diff --git a/database/src/interface/UserInterface.ts b/database/src/interface/UserInterface.ts index 63804af6b..235dd2dd0 100644 --- a/database/src/interface/UserInterface.ts +++ b/database/src/interface/UserInterface.ts @@ -1,5 +1,6 @@ export interface UserInterface { - // from login user (contains state user) + // from user + loginUserId?: number email?: string firstName?: string lastName?: string diff --git a/database/src/seeds/helpers/user-helpers.ts b/database/src/seeds/helpers/user-helpers.ts index f205ccb00..2d0b7788b 100644 --- a/database/src/seeds/helpers/user-helpers.ts +++ b/database/src/seeds/helpers/user-helpers.ts @@ -1,10 +1,4 @@ -import { - UserContext, - LoginUserContext, - LoginUserBackupContext, - ServerUserContext, - LoginUserRolesContext, -} from '../../interface/UserContext' +import { UserContext, LoginUserBackupContext, ServerUserContext } from '../../interface/UserContext' import { BalanceContext, TransactionContext, @@ -13,7 +7,6 @@ import { } from '../../interface/TransactionContext' import { UserInterface } from '../../interface/UserInterface' import { User } from '../../../entity/User' -import { LoginUser } from '../../../entity/LoginUser' import { LoginUserBackup } from '../../../entity/LoginUserBackup' import { ServerUser } from '../../../entity/ServerUser' import { Balance } from '../../../entity/Balance' @@ -24,9 +17,9 @@ import { Factory } from 'typeorm-seeding' export const userSeeder = async (factory: Factory, userData: UserInterface): Promise => { const user = await factory(User)(createUserContext(userData)).create() - if (!userData.email) userData.email = user.email - const loginUser = await factory(LoginUser)(createLoginUserContext(userData)).create() - await factory(LoginUserBackup)(createLoginUserBackupContext(userData, loginUser)).create() + await factory(LoginUserBackup)( + createLoginUserBackupContext(userData, (user).loginUserId), + ).create() if (userData.isAdmin) { await factory(ServerUser)(createServerUserContext(userData)).create() @@ -49,44 +42,33 @@ export const userSeeder = async (factory: Factory, userData: UserInterface): Pro const createUserContext = (context: UserInterface): UserContext => { return { - pubkey: context.pubKey, + pubKey: context.pubKey, email: context.email, + loginUserId: context.loginUserId, firstName: context.firstName, lastName: context.lastName, username: context.username, disabled: context.disabled, - } -} - -const createLoginUserContext = (context: UserInterface): LoginUserContext => { - return { - email: context.email, - firstName: context.firstName, - lastName: context.lastName, - username: context.username, description: context.description, password: context.password, - pubKey: context.pubKey, privKey: context.privKey, emailHash: context.emailHash, createdAt: context.createdAt, emailChecked: context.emailChecked, passphraseShown: context.passphraseShown, language: context.language, - disabled: context.disabled, - groupId: context.groupId, publisherId: context.publisherId, } } const createLoginUserBackupContext = ( context: UserInterface, - loginUser: LoginUser, + loginUserId: number, ): LoginUserBackupContext => { return { passphrase: context.passphrase, mnemonicType: context.mnemonicType, - userId: loginUser.id, + userId: loginUserId, } } @@ -103,13 +85,6 @@ const createServerUserContext = (context: UserInterface): ServerUserContext => { } } -const createLoginUserRolesContext = (loginUser: LoginUser): LoginUserRolesContext => { - return { - userId: loginUser.id, - roleId: 1, - } -} - const createBalanceContext = (context: UserInterface, user: User): BalanceContext => { return { modified: context.balanceModified, diff --git a/database/src/seeds/users/bibi-bloxberg.ts b/database/src/seeds/users/bibi-bloxberg.ts index 221349cb7..86fae0300 100644 --- a/database/src/seeds/users/bibi-bloxberg.ts +++ b/database/src/seeds/users/bibi-bloxberg.ts @@ -1,4 +1,5 @@ export const bibiBloxberg = { + loginUserId: 1, email: 'bibi@bloxberg.de', firstName: 'Bibi', lastName: 'Bloxberg', diff --git a/database/src/seeds/users/bob-baumeister.ts b/database/src/seeds/users/bob-baumeister.ts index 013636079..a42993a1d 100644 --- a/database/src/seeds/users/bob-baumeister.ts +++ b/database/src/seeds/users/bob-baumeister.ts @@ -1,4 +1,5 @@ export const bobBaumeister = { + loginUserId: 2, email: 'bob@baumeister.de', firstName: 'Bob', lastName: 'der Baumeister', diff --git a/database/src/seeds/users/garrick-ollivander.ts b/database/src/seeds/users/garrick-ollivander.ts index 1c7bbb9fc..d84cd02db 100644 --- a/database/src/seeds/users/garrick-ollivander.ts +++ b/database/src/seeds/users/garrick-ollivander.ts @@ -1,4 +1,5 @@ export const garrickOllivander = { + loginUserId: 3, email: 'garrick@ollivander.com', firstName: 'Garrick', lastName: 'Ollivander', diff --git a/database/src/seeds/users/peter-lustig.ts b/database/src/seeds/users/peter-lustig.ts index c96b28a65..96b44cda4 100644 --- a/database/src/seeds/users/peter-lustig.ts +++ b/database/src/seeds/users/peter-lustig.ts @@ -1,4 +1,5 @@ export const peterLustig = { + loginUserId: 4, email: 'peter@lustig.de', firstName: 'Peter', lastName: 'Lustig', diff --git a/database/src/seeds/users/raeuber-hotzenplotz.ts b/database/src/seeds/users/raeuber-hotzenplotz.ts index c1f31b490..0df964167 100644 --- a/database/src/seeds/users/raeuber-hotzenplotz.ts +++ b/database/src/seeds/users/raeuber-hotzenplotz.ts @@ -1,4 +1,5 @@ export const raeuberHotzenplotz = { + loginUserId: 5, email: 'raeuber@hotzenplotz.de', firstName: 'Räuber', lastName: 'Hotzenplotz', From 7baf05cf14cca1408aff23fa168fcf849ca78c4a Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Feb 2022 04:46:04 +0100 Subject: [PATCH 06/24] corrected field pubKey rename --- backend/src/graphql/resolver/TransactionResolver.ts | 4 ++-- backend/src/graphql/resolver/UserResolver.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 61f590123..554be7db6 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -364,7 +364,7 @@ export class TransactionResolver { // validate sender user (logged in) const userRepository = getCustomRepository(UserRepository) const senderUser = await userRepository.findByPubkeyHex(context.pubKey) - if (senderUser.pubkey.length !== 32) { + if (senderUser.pubKey.length !== 32) { throw new Error('invalid sender public key') } if (!hasUserAmount(senderUser, amount)) { @@ -454,7 +454,7 @@ export class TransactionResolver { const transactionSendCoin = new dbTransactionSendCoin() transactionSendCoin.transactionId = transaction.id transactionSendCoin.userId = senderUser.id - transactionSendCoin.senderPublic = senderUser.pubkey + transactionSendCoin.senderPublic = senderUser.pubKey transactionSendCoin.recipiantUserId = recipiantUser.id transactionSendCoin.recipiantPublic = Buffer.from(recipiantPublicKey, 'hex') transactionSendCoin.amount = centAmount diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 14a56b60b..4c901c02a 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -232,7 +232,7 @@ export class UserResolver { user.lastName = userEntity.lastName user.username = userEntity.username user.description = loginUser.description - user.pubkey = userEntity.pubkey.toString('hex') + user.pubkey = userEntity.pubKey.toString('hex') user.language = loginUser.language // Elopage Status & Stored PublisherId @@ -293,7 +293,7 @@ export class UserResolver { userEntity.lastName = loginUser.lastName userEntity.username = loginUser.username userEntity.email = loginUser.email - userEntity.pubkey = loginUser.pubKey + userEntity.pubKey = loginUser.pubKey userRepository.save(userEntity).catch(() => { throw new Error('error by save userEntity') @@ -437,7 +437,7 @@ export class UserResolver { dbUser.lastName = lastName dbUser.username = username // TODO this field has no null allowed unlike the loginServer table - dbUser.pubkey = Buffer.from(randomBytes(32)) // Buffer.alloc(32, 0) default to 0000... + dbUser.pubKey = Buffer.from(randomBytes(32)) // Buffer.alloc(32, 0) default to 0000... // dbUser.pubkey = keyPair[0] await queryRunner.manager.save(dbUser).catch((er) => { @@ -620,7 +620,7 @@ export class UserResolver { loginUser.password = passwordHash[0].readBigUInt64LE() // using the shorthash loginUser.pubKey = keyPair[0] loginUser.privKey = encryptedPrivkey - dbUser.pubkey = keyPair[0] + dbUser.pubKey = keyPair[0] const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() From a334c916fd1bd769898eca25b710355b33266a7d Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Feb 2022 05:26:55 +0100 Subject: [PATCH 07/24] fixed business logic with only one user --- backend/src/graphql/resolver/AdminResolver.ts | 5 +- .../graphql/resolver/TransactionResolver.ts | 8 +- backend/src/graphql/resolver/UserResolver.ts | 216 ++++++------------ backend/src/typeorm/repository/LoginUser.ts | 24 -- backend/src/webhook/elopage.ts | 5 +- 5 files changed, 79 insertions(+), 179 deletions(-) delete mode 100644 backend/src/typeorm/repository/LoginUser.ts diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 4204c6c2f..2c8cbfe27 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -21,8 +21,8 @@ import { UserTransaction } from '@entity/UserTransaction' import { UserTransactionRepository } from '../../typeorm/repository/UserTransaction' import { BalanceRepository } from '../../typeorm/repository/Balance' import { calculateDecay } from '../../util/decay' -import { LoginUserRepository } from '../../typeorm/repository/LoginUser' import { AdminPendingCreation } from '@entity/AdminPendingCreation' +import { User as dbUser } from '@entity/User' @Resolver() export class AdminResolver { @@ -378,7 +378,6 @@ function isCreationValid(creations: number[], amount: number, creationDate: Date } async function hasActivatedEmail(email: string): Promise { - const repository = getCustomRepository(LoginUserRepository) - const user = await repository.findByEmail(email) + const user = await dbUser.findOne({ email }) return user ? user.emailChecked : false } diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 554be7db6..4bf4e7a17 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -33,7 +33,6 @@ import { calculateDecay, calculateDecayWithInterval } from '../../util/decay' import { TransactionTypeId } from '../enum/TransactionTypeId' import { TransactionType } from '../enum/TransactionType' import { hasUserAmount, isHexPublicKey } from '../../util/validate' -import { LoginUserRepository } from '../../typeorm/repository/LoginUser' import { RIGHTS } from '../../auth/RIGHTS' // Helper function @@ -290,14 +289,13 @@ async function addUserTransaction( } async function getPublicKey(email: string): Promise { - const loginUserRepository = getCustomRepository(LoginUserRepository) - const loginUser = await loginUserRepository.findOne({ email: email }) + const user = await dbUser.findOne({ email: email }) // User not found - if (!loginUser) { + if (!user) { return null } - return loginUser.pubKey.toString('hex') + return user.pubKey.toString('hex') } @Resolver() diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 4c901c02a..9fab8493a 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -14,10 +14,8 @@ import UnsecureLoginArgs from '../arg/UnsecureLoginArgs' import UpdateUserInfosArgs from '../arg/UpdateUserInfosArgs' import { klicktippNewsletterStateMiddleware } from '../../middleware/klicktippMiddleware' import { UserSettingRepository } from '../../typeorm/repository/UserSettingRepository' -import { LoginUserRepository } from '../../typeorm/repository/LoginUser' import { Setting } from '../enum/Setting' import { UserRepository } from '../../typeorm/repository/User' -import { LoginUser } from '@entity/LoginUser' import { LoginUserBackup } from '@entity/LoginUserBackup' import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' import { sendResetPasswordEmail } from '../../mailer/sendResetPasswordEmail' @@ -27,7 +25,7 @@ import { klicktippSignIn } from '../../apis/KlicktippController' import { RIGHTS } from '../../auth/RIGHTS' import { ServerUserRepository } from '../../typeorm/repository/ServerUser' import { ROLE_ADMIN } from '../../auth/ROLES' -import { randomBytes } from 'crypto' +import { randomBytes, randomInt } from 'crypto' const EMAIL_OPT_IN_RESET_PASSWORD = 2 const EMAIL_OPT_IN_REGISTER = 1 @@ -186,10 +184,10 @@ const createEmailOptIn = async ( return emailOptIn } -const getOptInCode = async (loginUser: LoginUser): Promise => { +const getOptInCode = async (loginUserId: number): Promise => { const loginEmailOptInRepository = await getRepository(LoginEmailOptIn) let optInCode = await loginEmailOptInRepository.findOne({ - userId: loginUser.id, + userId: loginUserId, emailOptInTypeId: EMAIL_OPT_IN_RESET_PASSWORD, }) @@ -207,7 +205,7 @@ const getOptInCode = async (loginUser: LoginUser): Promise => { } else { optInCode = new LoginEmailOptIn() optInCode.verificationCode = random(64) - optInCode.userId = loginUser.id + optInCode.userId = loginUserId optInCode.emailOptInTypeId = EMAIL_OPT_IN_RESET_PASSWORD } await loginEmailOptInRepository.save(optInCode) @@ -223,17 +221,15 @@ export class UserResolver { // TODO refactor and do not have duplicate code with login(see below) const userRepository = getCustomRepository(UserRepository) const userEntity = await userRepository.findByPubkeyHex(context.pubKey) - const loginUserRepository = getCustomRepository(LoginUserRepository) - const loginUser = await loginUserRepository.findByEmail(userEntity.email) const user = new User() user.id = userEntity.id user.email = userEntity.email user.firstName = userEntity.firstName user.lastName = userEntity.lastName user.username = userEntity.username - user.description = loginUser.description + user.description = userEntity.description user.pubkey = userEntity.pubKey.toString('hex') - user.language = loginUser.language + user.language = userEntity.language // Elopage Status & Stored PublisherId user.hasElopage = await this.hasElopage(context) @@ -259,76 +255,50 @@ export class UserResolver { @Ctx() context: any, ): Promise { email = email.trim().toLowerCase() - const loginUserRepository = getCustomRepository(LoginUserRepository) - const loginUser = await loginUserRepository.findByEmail(email).catch(() => { + const dbUser = await DbUser.findOneOrFail({ email }).catch(() => { throw new Error('No user with this credentials') }) - if (!loginUser.emailChecked) { + if (!dbUser.emailChecked) { throw new Error('User email not validated') } - if (loginUser.password === BigInt(0)) { + if (dbUser.password === BigInt(0)) { // TODO we want to catch this on the frontend and ask the user to check his emails or resend code throw new Error('User has no password set yet') } - if (!loginUser.pubKey || !loginUser.privKey) { + if (!dbUser.pubKey || !dbUser.privKey) { // TODO we want to catch this on the frontend and ask the user to check his emails or resend code throw new Error('User has no private or publicKey') } const passwordHash = SecretKeyCryptographyCreateKey(email, password) // return short and long hash - const loginUserPassword = BigInt(loginUser.password.toString()) + const loginUserPassword = BigInt(dbUser.password.toString()) if (loginUserPassword !== passwordHash[0].readBigUInt64LE()) { throw new Error('No user with this credentials') } - // TODO: If user has no pubKey Create it again and update user. - - const userRepository = getCustomRepository(UserRepository) - let userEntity: void | DbUser - const loginUserPubKey = loginUser.pubKey - const loginUserPubKeyString = loginUserPubKey.toString('hex') - userEntity = await userRepository.findByPubkeyHex(loginUserPubKeyString).catch(() => { - // User not stored in state_users - // TODO: Check with production data - email is unique which can cause problems - userEntity = new DbUser() - userEntity.firstName = loginUser.firstName - userEntity.lastName = loginUser.lastName - userEntity.username = loginUser.username - userEntity.email = loginUser.email - userEntity.pubKey = loginUser.pubKey - - userRepository.save(userEntity).catch(() => { - throw new Error('error by save userEntity') - }) - }) - if (!userEntity) { - throw new Error('error with cannot happen') - } const user = new User() - user.id = userEntity.id + user.id = dbUser.id user.email = email - user.firstName = loginUser.firstName - user.lastName = loginUser.lastName - user.username = loginUser.username - user.description = loginUser.description - user.pubkey = loginUserPubKeyString - user.language = loginUser.language + user.firstName = dbUser.firstName + user.lastName = dbUser.lastName + user.username = dbUser.username + user.description = dbUser.description + user.pubkey = dbUser.pubKey.toString('hex') + user.language = dbUser.language // Elopage Status & Stored PublisherId - user.hasElopage = await this.hasElopage({ pubKey: loginUserPubKeyString }) + user.hasElopage = await this.hasElopage({ pubKey: dbUser.pubKey.toString('hex') }) if (!user.hasElopage && publisherId) { user.publisherId = publisherId // TODO: Check if we can use updateUserInfos // await this.updateUserInfos({ publisherId }, { pubKey: loginUser.pubKey }) - const loginUserRepository = getCustomRepository(LoginUserRepository) - const loginUser = await loginUserRepository.findOneOrFail({ email: userEntity.email }) - loginUser.publisherId = publisherId - loginUserRepository.save(loginUser) + dbUser.publisherId = publisherId + DbUser.save(dbUser) } // coinAnimation const userSettingRepository = getCustomRepository(UserSettingRepository) const coinanimation = await userSettingRepository - .readBoolean(userEntity.id, Setting.COIN_ANIMATION) + .readBoolean(dbUser.id, Setting.COIN_ANIMATION) .catch((error) => { throw new Error(error) }) @@ -341,7 +311,7 @@ export class UserResolver { context.setHeaders.push({ key: 'token', - value: encode(loginUser.pubKey), + value: encode(dbUser.pubKey), }) return user @@ -393,18 +363,22 @@ export class UserResolver { // const encryptedPrivkey = SecretKeyCryptographyEncrypt(keyPair[1], passwordHash[1]) const emailHash = getEmailHash(email) - // Table: login_users - const loginUser = new LoginUser() - loginUser.email = email - loginUser.firstName = firstName - loginUser.lastName = lastName - loginUser.username = username - loginUser.description = '' + // Table: state_users + const dbUser = new DbUser() + dbUser.email = email + dbUser.firstName = firstName + dbUser.lastName = lastName + dbUser.username = username + dbUser.description = '' + dbUser.emailHash = emailHash + dbUser.language = language + dbUser.publisherId = publisherId + // TODO this is a refactor artifact and must be removed quickly + dbUser.loginUserId = randomInt(9999999999) + // TODO this field has no null allowed unlike the loginServer table + // dbUser.pubKey = Buffer.from(randomBytes(32)) // Buffer.alloc(32, 0) default to 0000... + // dbUser.pubkey = keyPair[0] // loginUser.password = passwordHash[0].readBigUInt64LE() // using the shorthash - loginUser.emailHash = emailHash - loginUser.language = language - loginUser.groupId = 1 - loginUser.publisherId = publisherId // loginUser.pubKey = keyPair[0] // loginUser.privKey = encryptedPrivkey @@ -412,15 +386,15 @@ export class UserResolver { await queryRunner.connect() await queryRunner.startTransaction('READ UNCOMMITTED') try { - const { id: loginUserId } = await queryRunner.manager.save(loginUser).catch((error) => { + await queryRunner.manager.save(dbUser).catch((error) => { // eslint-disable-next-line no-console - console.log('insert LoginUser failed', error) - throw new Error('insert user failed') + console.log('Error while saving dbUser', error) + throw new Error('error saving user') }) // Table: login_user_backups const loginUserBackup = new LoginUserBackup() - loginUserBackup.userId = loginUserId + loginUserBackup.userId = dbUser.loginUserId loginUserBackup.passphrase = passphrase.join(' ') // login server saves trailing space loginUserBackup.mnemonicType = 2 // ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER; @@ -430,25 +404,9 @@ export class UserResolver { throw new Error('insert user backup failed') }) - // Table: state_users - const dbUser = new DbUser() - dbUser.email = email - dbUser.firstName = firstName - dbUser.lastName = lastName - dbUser.username = username - // TODO this field has no null allowed unlike the loginServer table - dbUser.pubKey = Buffer.from(randomBytes(32)) // Buffer.alloc(32, 0) default to 0000... - // dbUser.pubkey = keyPair[0] - - await queryRunner.manager.save(dbUser).catch((er) => { - // eslint-disable-next-line no-console - console.log('Error while saving dbUser', er) - throw new Error('error saving user') - }) - // Store EmailOptIn in DB // TODO: this has duplicate code with sendResetPasswordEmail - const emailOptIn = await createEmailOptIn(loginUserId, queryRunner) + const emailOptIn = await createEmailOptIn(dbUser.loginUserId, queryRunner) const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( /{code}/g, @@ -480,15 +438,14 @@ export class UserResolver { @Authorized([RIGHTS.SEND_ACTIVATION_EMAIL]) @Mutation(() => Boolean) async sendActivationEmail(@Arg('email') email: string): Promise { - const loginUserRepository = getCustomRepository(LoginUserRepository) - const loginUser = await loginUserRepository.findOneOrFail({ email: email }) + const user = await DbUser.findOneOrFail({ email: email }) const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() await queryRunner.startTransaction('READ UNCOMMITTED') try { - const emailOptIn = await createEmailOptIn(loginUser.id, queryRunner) + const emailOptIn = await createEmailOptIn(user.loginUserId, queryRunner) const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( /{code}/g, @@ -497,8 +454,8 @@ export class UserResolver { const emailSent = await sendAccountActivationEmail({ link: activationLink, - firstName: loginUser.firstName, - lastName: loginUser.lastName, + firstName: user.firstName, + lastName: user.lastName, email, }) @@ -522,10 +479,9 @@ export class UserResolver { async sendResetPasswordEmail(@Arg('email') email: string): Promise { // TODO: this has duplicate code with createUser - const loginUserRepository = await getCustomRepository(LoginUserRepository) - const loginUser = await loginUserRepository.findOneOrFail({ email }) + const user = await DbUser.findOneOrFail({ email }) - const optInCode = await getOptInCode(loginUser) + const optInCode = await getOptInCode(user.loginUserId) const link = CONFIG.EMAIL_LINK_SETPASSWORD.replace( /{code}/g, @@ -534,8 +490,8 @@ export class UserResolver { const emailSent = await sendResetPasswordEmail({ link, - firstName: loginUser.firstName, - lastName: loginUser.lastName, + firstName: user.firstName, + lastName: user.lastName, email, }) @@ -575,28 +531,19 @@ export class UserResolver { throw new Error('Code is older than 10 minutes') } - // load loginUser - const loginUserRepository = await getCustomRepository(LoginUserRepository) - const loginUser = await loginUserRepository - .findOneOrFail({ id: optInCode.userId }) - .catch(() => { - throw new Error('Could not find corresponding Login User') - }) - // load user - const dbUserRepository = await getCustomRepository(UserRepository) - const dbUser = await dbUserRepository.findOneOrFail({ email: loginUser.email }).catch(() => { - throw new Error('Could not find corresponding User') + const user = await DbUser.findOneOrFail({ loginUserId: optInCode.userId }).catch(() => { + throw new Error('Could not find corresponding Login User') }) const loginUserBackupRepository = await getRepository(LoginUserBackup) - let loginUserBackup = await loginUserBackupRepository.findOne({ userId: loginUser.id }) + let loginUserBackup = await loginUserBackupRepository.findOne({ userId: user.loginUserId }) // Generate Passphrase if needed if (!loginUserBackup) { const passphrase = PassphraseGenerate() loginUserBackup = new LoginUserBackup() - loginUserBackup.userId = loginUser.id + loginUserBackup.userId = user.loginUserId loginUserBackup.passphrase = passphrase.join(' ') // login server saves trailing space loginUserBackup.mnemonicType = 2 // ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER; loginUserBackupRepository.save(loginUserBackup) @@ -611,29 +558,23 @@ export class UserResolver { } // Activate EMail - loginUser.emailChecked = true + user.emailChecked = true // Update Password - const passwordHash = SecretKeyCryptographyCreateKey(loginUser.email, password) // return short and long hash + const passwordHash = SecretKeyCryptographyCreateKey(user.email, password) // return short and long hash const keyPair = KeyPairEd25519Create(passphrase) // return pub, priv Key const encryptedPrivkey = SecretKeyCryptographyEncrypt(keyPair[1], passwordHash[1]) - loginUser.password = passwordHash[0].readBigUInt64LE() // using the shorthash - loginUser.pubKey = keyPair[0] - loginUser.privKey = encryptedPrivkey - dbUser.pubKey = keyPair[0] + user.password = passwordHash[0].readBigUInt64LE() // using the shorthash + user.pubKey = keyPair[0] + user.privKey = encryptedPrivkey const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() await queryRunner.startTransaction('READ UNCOMMITTED') try { - // Save loginUser - await queryRunner.manager.save(loginUser).catch((error) => { - throw new Error('error saving loginUser: ' + error) - }) - // Save user - await queryRunner.manager.save(dbUser).catch((error) => { + await queryRunner.manager.save(user).catch((error) => { throw new Error('error saving user: ' + error) }) @@ -654,12 +595,7 @@ export class UserResolver { // TODO do we always signUp the user? How to handle things with old users? if (optInCode.emailOptInTypeId === EMAIL_OPT_IN_REGISTER) { try { - await klicktippSignIn( - loginUser.email, - loginUser.language, - loginUser.firstName, - loginUser.lastName, - ) + await klicktippSignIn(user.email, user.language, user.firstName, user.lastName) } catch { // TODO is this a problem? // eslint-disable-next-line no-console @@ -689,8 +625,6 @@ export class UserResolver { ): Promise { const userRepository = getCustomRepository(UserRepository) const userEntity = await userRepository.findByPubkeyHex(context.pubKey) - const loginUserRepository = getCustomRepository(LoginUserRepository) - const loginUser = await loginUserRepository.findOneOrFail({ email: userEntity.email }) if (username) { throw new Error('change username currently not supported!') @@ -704,46 +638,44 @@ export class UserResolver { } if (firstName) { - loginUser.firstName = firstName userEntity.firstName = firstName } if (lastName) { - loginUser.lastName = lastName userEntity.lastName = lastName } if (description) { - loginUser.description = description + userEntity.description = description } if (language) { if (!isLanguage(language)) { throw new Error(`"${language}" isn't a valid language`) } - loginUser.language = language + userEntity.language = language } if (password && passwordNew) { // TODO: This had some error cases defined - like missing private key. This is no longer checked. - const oldPasswordHash = SecretKeyCryptographyCreateKey(loginUser.email, password) - if (BigInt(loginUser.password.toString()) !== oldPasswordHash[0].readBigUInt64LE()) { + const oldPasswordHash = SecretKeyCryptographyCreateKey(userEntity.email, password) + if (BigInt(userEntity.password.toString()) !== oldPasswordHash[0].readBigUInt64LE()) { throw new Error(`Old password is invalid`) } - const privKey = SecretKeyCryptographyDecrypt(loginUser.privKey, oldPasswordHash[1]) + const privKey = SecretKeyCryptographyDecrypt(userEntity.privKey, oldPasswordHash[1]) - const newPasswordHash = SecretKeyCryptographyCreateKey(loginUser.email, passwordNew) // return short and long hash + const newPasswordHash = SecretKeyCryptographyCreateKey(userEntity.email, passwordNew) // return short and long hash const encryptedPrivkey = SecretKeyCryptographyEncrypt(privKey, newPasswordHash[1]) // Save new password hash and newly encrypted private key - loginUser.password = newPasswordHash[0].readBigUInt64LE() - loginUser.privKey = encryptedPrivkey + userEntity.password = newPasswordHash[0].readBigUInt64LE() + userEntity.privKey = encryptedPrivkey } // Save publisherId only if Elopage is not yet registered if (publisherId && !(await this.hasElopage(context))) { - loginUser.publisherId = publisherId + userEntity.publisherId = publisherId } const queryRunner = getConnection().createQueryRunner() @@ -760,10 +692,6 @@ export class UserResolver { }) } - await queryRunner.manager.save(loginUser).catch((error) => { - throw new Error('error saving loginUser: ' + error) - }) - await queryRunner.manager.save(userEntity).catch((error) => { throw new Error('error saving user: ' + error) }) @@ -793,7 +721,7 @@ export class UserResolver { throw new Error(`Username must be at minimum ${MIN_CHARACTERS_USERNAME} characters long.`) } - const usersFound = await LoginUser.count({ username }) + const usersFound = await DbUser.count({ username }) // Username already present? if (usersFound !== 0) { diff --git a/backend/src/typeorm/repository/LoginUser.ts b/backend/src/typeorm/repository/LoginUser.ts deleted file mode 100644 index efe6f5428..000000000 --- a/backend/src/typeorm/repository/LoginUser.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { EntityRepository, Repository } from '@dbTools/typeorm' -import { LoginUser } from '@entity/LoginUser' - -@EntityRepository(LoginUser) -export class LoginUserRepository extends Repository { - async findByEmail(email: string): Promise { - return this.createQueryBuilder('loginUser') - .where('loginUser.email = :email', { email }) - .getOneOrFail() - } - - async findBySearchCriteria(searchCriteria: string): Promise { - return await this.createQueryBuilder('user') - .where( - 'user.firstName like :name or user.lastName like :lastName or user.email like :email', - { - name: `%${searchCriteria}%`, - lastName: `%${searchCriteria}%`, - email: `%${searchCriteria}%`, - }, - ) - .getMany() - } -} diff --git a/backend/src/webhook/elopage.ts b/backend/src/webhook/elopage.ts index 9c1aadd2e..0b392abb1 100644 --- a/backend/src/webhook/elopage.ts +++ b/backend/src/webhook/elopage.ts @@ -31,7 +31,7 @@ import { LoginElopageBuys } from '@entity/LoginElopageBuys' import { getCustomRepository } from '@dbTools/typeorm' import { UserResolver } from '../graphql/resolver/UserResolver' import { LoginElopageBuysRepository } from '../typeorm/repository/LoginElopageBuys' -import { LoginUserRepository } from '../typeorm/repository/LoginUser' +import { User as dbUser } from '@entity/User' export const elopageWebhook = async (req: any, res: any): Promise => { // eslint-disable-next-line no-console @@ -114,8 +114,7 @@ export const elopageWebhook = async (req: any, res: any): Promise => { } // Do we already have such a user? - const loginUserRepository = await getCustomRepository(LoginUserRepository) - if ((await loginUserRepository.count({ email })) !== 0) { + if ((await dbUser.count({ email })) !== 0) { // eslint-disable-next-line no-console console.log(`Did not create User - already exists with email: ${email}`) return From 7987fbbba19637d23c908915e9129b08dba72c13 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Feb 2022 19:49:39 +0100 Subject: [PATCH 08/24] some comments --- database/migrations/0017-combine_user_tables.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/database/migrations/0017-combine_user_tables.ts b/database/migrations/0017-combine_user_tables.ts index fe4e0ea4e..d4a0f7c0c 100644 --- a/database/migrations/0017-combine_user_tables.ts +++ b/database/migrations/0017-combine_user_tables.ts @@ -1,8 +1,7 @@ -/* MIGRATION TO COMBINE ALL USER TABLES +/* MIGRATION TO COMBINE LOGIN_USERS WITH STATE_USERS TABLE * - * This migration combines the tables `login_users` - * and `login_user_backups` into the `state_users` - * table. + * This migration combines the table `login_users` with + * the `state_users` table, where the later is the target. */ export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { From 3d226d10681bf8a40af39df274a5cd70a7630ad4 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Feb 2022 19:50:01 +0100 Subject: [PATCH 09/24] migration to combine `login_user_backups` with `state_users` --- ...mbine_login_user_backups_and_user_table.ts | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 database/migrations/0018-combine_login_user_backups_and_user_table.ts diff --git a/database/migrations/0018-combine_login_user_backups_and_user_table.ts b/database/migrations/0018-combine_login_user_backups_and_user_table.ts new file mode 100644 index 000000000..9cc1853dd --- /dev/null +++ b/database/migrations/0018-combine_login_user_backups_and_user_table.ts @@ -0,0 +1,49 @@ +/* MIGRATION TO COMBINE LOGIN_BACKUP_USERS TABLE WITH STATE_USERS + * + * This migration combines the table `login_user_backups` into + * the `state_users` table, where the later is the target. + */ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + // We only keep the passphrase, the mnemonic type is a constant, + // since every passphrase was converted to mnemonic type 2 + // TODO there is now default NULL instead of NOT NULL - check code impact + await queryFn( + 'ALTER TABLE `state_users` ADD COLUMN `passphrase` text DEFAULT NULL AFTER `publisher_id`;', + ) + + // Move data from `login_user_backups` to the newly modified `state_users` table. + await queryFn(` + UPDATE state_users + LEFT JOIN login_user_backups ON state_users.login_user_id = login_user_backups.user_id + SET state_users.passphrase = login_user_backups.passphrase + WHERE login_user_backups.passphrase IS NOT NULL + ; + `) + + // Drop `login_user_backups` table + await queryFn('DROP TABLE `login_user_backups`;') +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn(` + CREATE TABLE \`login_user_backups\` ( + \`id\` int(10) unsigned NOT NULL AUTO_INCREMENT, + \`user_id\` int(11) NOT NULL, + \`passphrase\` text NOT NULL, + \`mnemonic_type\` int(11) DEFAULT -1, + PRIMARY KEY (\`id\`) + ) ENGINE=InnoDB AUTO_INCREMENT=1862 DEFAULT CHARSET=utf8mb4; + `) + await queryFn(` + INSERT INTO login_user_backups + ( user_id, passphrase, mnemonic_type ) + ( SELECT login_user_id AS user_id, + passphrase, + '2' as mnemonic_type + FROM state_users + WHERE passphrase IS NOT NULL ) + ; + `) + await queryFn('ALTER TABLE `state_users` DROP COLUMN `passphrase`;') +} From b23553c6f230e22477d05d27fbff5ac8ec3339f1 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Feb 2022 20:15:09 +0100 Subject: [PATCH 10/24] adjusted entities --- .../User.ts | 83 +++++++++++++++++++ database/entity/LoginUserBackup.ts | 1 - database/entity/User.ts | 2 +- database/entity/index.ts | 2 - 4 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 database/entity/0018-combine_login_user_backups_and_user_table/User.ts delete mode 100644 database/entity/LoginUserBackup.ts diff --git a/database/entity/0018-combine_login_user_backups_and_user_table/User.ts b/database/entity/0018-combine_login_user_backups_and_user_table/User.ts new file mode 100644 index 000000000..2ae351e47 --- /dev/null +++ b/database/entity/0018-combine_login_user_backups_and_user_table/User.ts @@ -0,0 +1,83 @@ +import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm' +import { UserSetting } from '../UserSetting' + +@Entity('state_users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) +export class User extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ name: 'login_user_id', default: null, unsigned: true }) + loginUserId: number + + @Column({ name: 'index_id', type: 'smallint', default: 0, nullable: false }) + indexId: number + + @Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true }) + pubKey: Buffer + + @Column({ name: 'privkey', type: 'binary', length: 80, default: null, nullable: true }) + privKey: Buffer + + @Column({ length: 255, unique: true, nullable: false, collation: 'utf8mb4_unicode_ci' }) + email: string + + @Column({ + name: 'first_name', + length: 255, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + firstName: string + + @Column({ + name: 'last_name', + length: 255, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + lastName: string + + @Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' }) + username: string + + @Column({ type: 'bool', default: false }) + disabled: boolean + + @Column({ type: 'mediumtext', default: '', collation: 'utf8mb4_unicode_ci', nullable: true }) + description: string + + @Column({ type: 'bigint', default: 0, unsigned: true }) + password: BigInt + + @Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true }) + emailHash: Buffer + + @Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false }) + createdAt: Date + + @Column({ name: 'email_checked', type: 'bool', nullable: false, default: false }) + emailChecked: boolean + + @Column({ name: 'passphrase_shown', type: 'bool', nullable: false, default: false }) + passphraseShown: boolean + + @Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false }) + language: string + + @Column({ name: 'publisher_id', default: 0 }) + publisherId: number + + @Column({ + type: 'text', + name: 'passphrase', + collation: 'utf8mb4_unicode_ci', + nullable: true, + default: null, + }) + passphrase: string + + @OneToMany(() => UserSetting, (userSetting) => userSetting.user) + settings: UserSetting[] +} diff --git a/database/entity/LoginUserBackup.ts b/database/entity/LoginUserBackup.ts deleted file mode 100644 index 7a3031a33..000000000 --- a/database/entity/LoginUserBackup.ts +++ /dev/null @@ -1 +0,0 @@ -export { LoginUserBackup } from './0017-combine_user_tables/LoginUserBackup' diff --git a/database/entity/User.ts b/database/entity/User.ts index 3cf3e6c46..0e61cec1a 100644 --- a/database/entity/User.ts +++ b/database/entity/User.ts @@ -1 +1 @@ -export { User } from './0017-combine_user_tables/User' +export { User } from './0018-combine_login_user_backups_and_user_table/User' diff --git a/database/entity/index.ts b/database/entity/index.ts index d64c66d51..37fe6eb55 100644 --- a/database/entity/index.ts +++ b/database/entity/index.ts @@ -1,7 +1,6 @@ import { Balance } from './Balance' import { LoginElopageBuys } from './LoginElopageBuys' import { LoginEmailOptIn } from './LoginEmailOptIn' -import { LoginUserBackup } from './LoginUserBackup' import { Migration } from './Migration' import { ServerUser } from './ServerUser' import { Transaction } from './Transaction' @@ -17,7 +16,6 @@ export const entities = [ Balance, LoginElopageBuys, LoginEmailOptIn, - LoginUserBackup, Migration, ServerUser, Transaction, From 22b873c22d0c65bd4439b89ac6c2df9a4ca49df6 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Feb 2022 20:15:17 +0100 Subject: [PATCH 11/24] removed unused stuff --- database/entity/0017-combine_user_tables/LoginUserBackup.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/entity/0017-combine_user_tables/LoginUserBackup.ts b/database/entity/0017-combine_user_tables/LoginUserBackup.ts index 456f9d8f7..7aa69a021 100644 --- a/database/entity/0017-combine_user_tables/LoginUserBackup.ts +++ b/database/entity/0017-combine_user_tables/LoginUserBackup.ts @@ -1,4 +1,4 @@ -import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, JoinColumn, OneToOne } from 'typeorm' +import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm' @Entity('login_user_backups') export class LoginUserBackup extends BaseEntity { From d7f06dad121c95023a27da5cb3dabc5b1440bc0c Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Feb 2022 20:15:49 +0100 Subject: [PATCH 12/24] adjusted seed --- database/src/factories/user.factory.ts | 1 + database/src/interface/UserContext.ts | 10 ---------- database/src/interface/UserInterface.ts | 2 -- database/src/seeds/helpers/user-helpers.ts | 17 +---------------- 4 files changed, 2 insertions(+), 28 deletions(-) diff --git a/database/src/factories/user.factory.ts b/database/src/factories/user.factory.ts index 338a854f1..898a07dbc 100644 --- a/database/src/factories/user.factory.ts +++ b/database/src/factories/user.factory.ts @@ -26,6 +26,7 @@ define(User, (faker: typeof Faker, context?: UserContext) => { user.passphraseShown = context.passphraseShown ? context.passphraseShown : false user.language = context.language ? context.language : 'en' user.publisherId = context.publisherId ? context.publisherId : 0 + user.passphrase = context.passphrase ? context.passphrase : faker.random.words(24) return user }) diff --git a/database/src/interface/UserContext.ts b/database/src/interface/UserContext.ts index a23a50109..6760de552 100644 --- a/database/src/interface/UserContext.ts +++ b/database/src/interface/UserContext.ts @@ -15,12 +15,7 @@ export interface UserContext { passphraseShown?: boolean language?: string publisherId?: number -} - -export interface LoginUserBackupContext { - userId?: number passphrase?: string - mnemonicType?: number } export interface ServerUserContext { @@ -33,8 +28,3 @@ export interface ServerUserContext { created?: Date modified?: Date } - -export interface LoginUserRolesContext { - userId?: number - roleId?: number -} diff --git a/database/src/interface/UserInterface.ts b/database/src/interface/UserInterface.ts index 235dd2dd0..6e5c2303a 100644 --- a/database/src/interface/UserInterface.ts +++ b/database/src/interface/UserInterface.ts @@ -17,9 +17,7 @@ export interface UserInterface { disabled?: boolean groupId?: number publisherId?: number - // from login user backup passphrase?: string - mnemonicType?: number // from server user serverUserPassword?: string role?: string diff --git a/database/src/seeds/helpers/user-helpers.ts b/database/src/seeds/helpers/user-helpers.ts index 2d0b7788b..7061163fc 100644 --- a/database/src/seeds/helpers/user-helpers.ts +++ b/database/src/seeds/helpers/user-helpers.ts @@ -1,4 +1,4 @@ -import { UserContext, LoginUserBackupContext, ServerUserContext } from '../../interface/UserContext' +import { UserContext, ServerUserContext } from '../../interface/UserContext' import { BalanceContext, TransactionContext, @@ -7,7 +7,6 @@ import { } from '../../interface/TransactionContext' import { UserInterface } from '../../interface/UserInterface' import { User } from '../../../entity/User' -import { LoginUserBackup } from '../../../entity/LoginUserBackup' import { ServerUser } from '../../../entity/ServerUser' import { Balance } from '../../../entity/Balance' import { Transaction } from '../../../entity/Transaction' @@ -17,9 +16,6 @@ import { Factory } from 'typeorm-seeding' export const userSeeder = async (factory: Factory, userData: UserInterface): Promise => { const user = await factory(User)(createUserContext(userData)).create() - await factory(LoginUserBackup)( - createLoginUserBackupContext(userData, (user).loginUserId), - ).create() if (userData.isAdmin) { await factory(ServerUser)(createServerUserContext(userData)).create() @@ -61,17 +57,6 @@ const createUserContext = (context: UserInterface): UserContext => { } } -const createLoginUserBackupContext = ( - context: UserInterface, - loginUserId: number, -): LoginUserBackupContext => { - return { - passphrase: context.passphrase, - mnemonicType: context.mnemonicType, - userId: loginUserId, - } -} - const createServerUserContext = (context: UserInterface): ServerUserContext => { return { role: context.role, From d61b26ac653f95ca7750007a6a71070ecaaac782 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Feb 2022 20:16:00 +0100 Subject: [PATCH 13/24] deleted used factory --- .../src/factories/login-user-backup.factory.ts | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 database/src/factories/login-user-backup.factory.ts diff --git a/database/src/factories/login-user-backup.factory.ts b/database/src/factories/login-user-backup.factory.ts deleted file mode 100644 index c4ae18a77..000000000 --- a/database/src/factories/login-user-backup.factory.ts +++ /dev/null @@ -1,18 +0,0 @@ -import Faker from 'faker' -import { define } from 'typeorm-seeding' -import { LoginUserBackup } from '../../entity/LoginUserBackup' -import { LoginUserBackupContext } from '../interface/UserContext' - -define(LoginUserBackup, (faker: typeof Faker, context?: LoginUserBackupContext) => { - if (!context || !context.userId) { - throw new Error('LoginUserBackup: No userId present!') - } - - const userBackup = new LoginUserBackup() - // TODO: Get the real passphrase - userBackup.passphrase = context.passphrase ? context.passphrase : faker.random.words(24) - userBackup.mnemonicType = context.mnemonicType ? context.mnemonicType : 2 - userBackup.userId = context.userId - - return userBackup -}) From da349230c841b33ad04a186a0f3282fff487ce74 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Feb 2022 20:25:09 +0100 Subject: [PATCH 14/24] removed LoginBackupUsers entity & references from backend resolvers --- backend/src/graphql/resolver/UserResolver.ts | 28 ++++--------------- .../src/typeorm/repository/LoginUserBackup.ts | 5 ---- 2 files changed, 5 insertions(+), 28 deletions(-) delete mode 100644 backend/src/typeorm/repository/LoginUserBackup.ts diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 9fab8493a..b055afb97 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -16,7 +16,6 @@ import { klicktippNewsletterStateMiddleware } from '../../middleware/klicktippMi import { UserSettingRepository } from '../../typeorm/repository/UserSettingRepository' import { Setting } from '../enum/Setting' import { UserRepository } from '../../typeorm/repository/User' -import { LoginUserBackup } from '@entity/LoginUserBackup' import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' import { sendResetPasswordEmail } from '../../mailer/sendResetPasswordEmail' import { sendAccountActivationEmail } from '../../mailer/sendAccountActivationEmail' @@ -25,7 +24,7 @@ import { klicktippSignIn } from '../../apis/KlicktippController' import { RIGHTS } from '../../auth/RIGHTS' import { ServerUserRepository } from '../../typeorm/repository/ServerUser' import { ROLE_ADMIN } from '../../auth/ROLES' -import { randomBytes, randomInt } from 'crypto' +import { randomInt } from 'crypto' const EMAIL_OPT_IN_RESET_PASSWORD = 2 const EMAIL_OPT_IN_REGISTER = 1 @@ -373,6 +372,7 @@ export class UserResolver { dbUser.emailHash = emailHash dbUser.language = language dbUser.publisherId = publisherId + dbUser.passphrase = passphrase.join(' ') // TODO this is a refactor artifact and must be removed quickly dbUser.loginUserId = randomInt(9999999999) // TODO this field has no null allowed unlike the loginServer table @@ -392,18 +392,6 @@ export class UserResolver { throw new Error('error saving user') }) - // Table: login_user_backups - const loginUserBackup = new LoginUserBackup() - loginUserBackup.userId = dbUser.loginUserId - loginUserBackup.passphrase = passphrase.join(' ') // login server saves trailing space - loginUserBackup.mnemonicType = 2 // ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER; - - await queryRunner.manager.save(loginUserBackup).catch((error) => { - // eslint-disable-next-line no-console - console.log('insert LoginUserBackup failed', error) - throw new Error('insert user backup failed') - }) - // Store EmailOptIn in DB // TODO: this has duplicate code with sendResetPasswordEmail const emailOptIn = await createEmailOptIn(dbUser.loginUserId, queryRunner) @@ -536,20 +524,14 @@ export class UserResolver { throw new Error('Could not find corresponding Login User') }) - const loginUserBackupRepository = await getRepository(LoginUserBackup) - let loginUserBackup = await loginUserBackupRepository.findOne({ userId: user.loginUserId }) // Generate Passphrase if needed - if (!loginUserBackup) { + if (!user.passphrase) { const passphrase = PassphraseGenerate() - loginUserBackup = new LoginUserBackup() - loginUserBackup.userId = user.loginUserId - loginUserBackup.passphrase = passphrase.join(' ') // login server saves trailing space - loginUserBackup.mnemonicType = 2 // ServerConfig::MNEMONIC_BIP0039_SORTED_ORDER; - loginUserBackupRepository.save(loginUserBackup) + user.passphrase = passphrase.join(' ') } - const passphrase = loginUserBackup.passphrase.split(' ') + const passphrase = user.passphrase.split(' ') if (passphrase.length < PHRASE_WORD_COUNT) { // TODO if this can happen we cannot recover from that // this seem to be good on production data, if we dont diff --git a/backend/src/typeorm/repository/LoginUserBackup.ts b/backend/src/typeorm/repository/LoginUserBackup.ts deleted file mode 100644 index a54b1e8af..000000000 --- a/backend/src/typeorm/repository/LoginUserBackup.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { EntityRepository, Repository } from '@dbTools/typeorm' -import { LoginUserBackup } from '@entity/LoginUserBackup' - -@EntityRepository(LoginUserBackup) -export class LoginUserBackupRepository extends Repository {} From 6500901e2fe26f06a0a9cad6f3af8dd9caab4ef9 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Feb 2022 20:25:56 +0100 Subject: [PATCH 15/24] lint fix --- backend/src/graphql/resolver/UserResolver.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index b055afb97..b6b37efc4 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -524,7 +524,6 @@ export class UserResolver { throw new Error('Could not find corresponding Login User') }) - // Generate Passphrase if needed if (!user.passphrase) { const passphrase = PassphraseGenerate() From 48ecadf6b59bc77a9e6d4fa26fffb0d814d2b204 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Feb 2022 21:21:44 +0100 Subject: [PATCH 16/24] migration to only use state_user.id not the old login_user.id --- ...eplace_login_user_id_with_state_user_id.ts | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 database/migrations/0019-replace_login_user_id_with_state_user_id.ts diff --git a/database/migrations/0019-replace_login_user_id_with_state_user_id.ts b/database/migrations/0019-replace_login_user_id_with_state_user_id.ts new file mode 100644 index 000000000..719c05443 --- /dev/null +++ b/database/migrations/0019-replace_login_user_id_with_state_user_id.ts @@ -0,0 +1,57 @@ +/* MIGRATION TO REPLACE LOGIN_USER_ID WITH STATE_USER_ID + * + * This migration replaces the `login_user_id with` the + * `state_user.id` and removes corresponding columns. + * The table affected is `login_email_opt_in` + */ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + // Delete email opt in codes which can not be linked to an user + await queryFn(` + DELETE FROM \`login_email_opt_in\` + WHERE user_id NOT IN + ( SELECT login_user_id FROM state_users ) + `) + + // Replace user_id in `login_email_opt_in` + await queryFn(` + UPDATE login_email_opt_in + LEFT JOIN state_users ON state_users.login_user_id = login_email_opt_in.user_id + SET login_email_opt_in.user_id = state_users.id; + `) + + // Remove the column `login_user_id` from `state_users` + await queryFn('ALTER TABLE `state_users` DROP COLUMN `login_user_id`;') +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn( + 'ALTER TABLE `state_users` ADD COLUMN `login_user_id` int(10) unsigned DEFAULT NULL AFTER `id`;', + ) + // Instead of generating new `login_user_id`'s we just use the id of state user. + // This way we do not need to alter the `user_id`'s of `login_email_opt_in` table + // at all when migrating down. + // This is possible since there are no old `login_user.id` referenced anymore and + // we can freely choose them + await queryFn('UPDATE `state_users` SET login_user_id = id') + + // Insert back broken data, since we generate new `user_id`'s the old data might be now + // linked to existing accounts. To prevent that all invalid `user_id`'s are now negative. + // This renders them invalid while still keeping the original value + await queryFn(` + INSERT INTO login_email_opt_in + (id, user_id, verification_code, email_opt_in_type_id, created, resend_count, updated) + VALUES + ('38','-41','7544440030630126261','0','2019-11-09 13:58:21','0','2020-07-17 13:58:29'), + ('1262','-1185','2702555860489093775','3','2020-10-17 00:57:29','0','2020-10-17 00:57:29'), + ('1431','-1319','9846213635571107141','3','2020-12-29 00:07:32','0','2020-12-29 00:07:32'), + ('1548','-1185','1009203004512986277','1','2021-01-26 01:07:29','0','2021-01-26 01:07:29'), + ('1549','-1185','2144334450300724903','1','2021-01-26 01:07:32','0','2021-01-26 01:07:32'), + ('1683','-1525','14803676216828342915','3','2021-03-10 08:39:39','0','2021-03-10 08:39:39'), + ('1899','-1663','16616172057370363741','3','2021-04-12 14:49:18','0','2021-04-12 14:49:18'), + ('2168','-1865','13129474130315401087','3','2021-07-08 11:58:54','0','2021-07-08 11:58:54'), + ('2274','-1935','5775135935896874129','3','2021-08-24 11:40:04','0','2021-08-24 11:40:04'), + ('2318','-1967','5713731625139303791','3','2021-09-06 21:38:30','0','2021-09-06 21:38:30'), + ('2762','-2263','6997866521554931275','1','2021-12-25 11:44:30','0','2021-12-25 11:44:30'); + `) +} From 31f9ac59e312ac2be52fe99414e24e2f26f091d8 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Feb 2022 21:22:08 +0100 Subject: [PATCH 17/24] entities --- .../User.ts | 80 +++++++++++++++++++ database/entity/User.ts | 2 +- 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 database/entity/0019-replace_login_user_id_with_state_user_id/User.ts diff --git a/database/entity/0019-replace_login_user_id_with_state_user_id/User.ts b/database/entity/0019-replace_login_user_id_with_state_user_id/User.ts new file mode 100644 index 000000000..b469a55a7 --- /dev/null +++ b/database/entity/0019-replace_login_user_id_with_state_user_id/User.ts @@ -0,0 +1,80 @@ +import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm' +import { UserSetting } from '../UserSetting' + +@Entity('state_users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) +export class User extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ name: 'index_id', type: 'smallint', default: 0, nullable: false }) + indexId: number + + @Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true }) + pubKey: Buffer + + @Column({ name: 'privkey', type: 'binary', length: 80, default: null, nullable: true }) + privKey: Buffer + + @Column({ length: 255, unique: true, nullable: false, collation: 'utf8mb4_unicode_ci' }) + email: string + + @Column({ + name: 'first_name', + length: 255, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + firstName: string + + @Column({ + name: 'last_name', + length: 255, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + lastName: string + + @Column({ length: 255, nullable: true, default: null, collation: 'utf8mb4_unicode_ci' }) + username: string + + @Column({ type: 'bool', default: false }) + disabled: boolean + + @Column({ type: 'mediumtext', default: '', collation: 'utf8mb4_unicode_ci', nullable: true }) + description: string + + @Column({ type: 'bigint', default: 0, unsigned: true }) + password: BigInt + + @Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true }) + emailHash: Buffer + + @Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false }) + createdAt: Date + + @Column({ name: 'email_checked', type: 'bool', nullable: false, default: false }) + emailChecked: boolean + + @Column({ name: 'passphrase_shown', type: 'bool', nullable: false, default: false }) + passphraseShown: boolean + + @Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false }) + language: string + + @Column({ name: 'publisher_id', default: 0 }) + publisherId: number + + @Column({ + type: 'text', + name: 'passphrase', + collation: 'utf8mb4_unicode_ci', + nullable: true, + default: null, + }) + passphrase: string + + @OneToMany(() => UserSetting, (userSetting) => userSetting.user) + settings: UserSetting[] +} diff --git a/database/entity/User.ts b/database/entity/User.ts index 0e61cec1a..6dcdfed68 100644 --- a/database/entity/User.ts +++ b/database/entity/User.ts @@ -1 +1 @@ -export { User } from './0018-combine_login_user_backups_and_user_table/User' +export { User } from './0019-replace_login_user_id_with_state_user_id/User' From 6a03b2d34df4ffdf6aa04e2a8f420c44daa92b3d Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Feb 2022 21:22:32 +0100 Subject: [PATCH 18/24] adjusted seed & factories --- database/src/factories/user.factory.ts | 1 - database/src/interface/UserContext.ts | 1 - database/src/interface/UserInterface.ts | 1 - database/src/seeds/helpers/user-helpers.ts | 1 - 4 files changed, 4 deletions(-) diff --git a/database/src/factories/user.factory.ts b/database/src/factories/user.factory.ts index 898a07dbc..966e5ffc8 100644 --- a/database/src/factories/user.factory.ts +++ b/database/src/factories/user.factory.ts @@ -14,7 +14,6 @@ define(User, (faker: typeof Faker, context?: UserContext) => { user.lastName = context.lastName ? context.lastName : faker.name.lastName() user.username = context.username ? context.username : faker.internet.userName() user.disabled = context.disabled ? context.disabled : false - user.loginUserId = context.loginUserId ? context.loginUserId : randomInt(999999) user.indexId = 0 user.description = context.description ? context.description : faker.random.words(4) // TODO Create real password and keys/hash diff --git a/database/src/interface/UserContext.ts b/database/src/interface/UserContext.ts index 6760de552..0fa1a61b5 100644 --- a/database/src/interface/UserContext.ts +++ b/database/src/interface/UserContext.ts @@ -1,5 +1,4 @@ export interface UserContext { - loginUserId?: number pubKey?: Buffer email?: string firstName?: string diff --git a/database/src/interface/UserInterface.ts b/database/src/interface/UserInterface.ts index 6e5c2303a..2e20b857f 100644 --- a/database/src/interface/UserInterface.ts +++ b/database/src/interface/UserInterface.ts @@ -1,6 +1,5 @@ export interface UserInterface { // from user - loginUserId?: number email?: string firstName?: string lastName?: string diff --git a/database/src/seeds/helpers/user-helpers.ts b/database/src/seeds/helpers/user-helpers.ts index 7061163fc..55ab40e9d 100644 --- a/database/src/seeds/helpers/user-helpers.ts +++ b/database/src/seeds/helpers/user-helpers.ts @@ -40,7 +40,6 @@ const createUserContext = (context: UserInterface): UserContext => { return { pubKey: context.pubKey, email: context.email, - loginUserId: context.loginUserId, firstName: context.firstName, lastName: context.lastName, username: context.username, From 17e2e28aece0af3871509995d82824903f869166 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Feb 2022 21:30:07 +0100 Subject: [PATCH 19/24] fixed backend implementation --- backend/src/graphql/resolver/UserResolver.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index b6b37efc4..d7ecfa797 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -24,7 +24,6 @@ import { klicktippSignIn } from '../../apis/KlicktippController' import { RIGHTS } from '../../auth/RIGHTS' import { ServerUserRepository } from '../../typeorm/repository/ServerUser' import { ROLE_ADMIN } from '../../auth/ROLES' -import { randomInt } from 'crypto' const EMAIL_OPT_IN_RESET_PASSWORD = 2 const EMAIL_OPT_IN_REGISTER = 1 @@ -373,8 +372,6 @@ export class UserResolver { dbUser.language = language dbUser.publisherId = publisherId dbUser.passphrase = passphrase.join(' ') - // TODO this is a refactor artifact and must be removed quickly - dbUser.loginUserId = randomInt(9999999999) // TODO this field has no null allowed unlike the loginServer table // dbUser.pubKey = Buffer.from(randomBytes(32)) // Buffer.alloc(32, 0) default to 0000... // dbUser.pubkey = keyPair[0] @@ -394,7 +391,7 @@ export class UserResolver { // Store EmailOptIn in DB // TODO: this has duplicate code with sendResetPasswordEmail - const emailOptIn = await createEmailOptIn(dbUser.loginUserId, queryRunner) + const emailOptIn = await createEmailOptIn(dbUser.id, queryRunner) const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( /{code}/g, @@ -433,7 +430,7 @@ export class UserResolver { await queryRunner.startTransaction('READ UNCOMMITTED') try { - const emailOptIn = await createEmailOptIn(user.loginUserId, queryRunner) + const emailOptIn = await createEmailOptIn(user.id, queryRunner) const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( /{code}/g, @@ -469,7 +466,7 @@ export class UserResolver { const user = await DbUser.findOneOrFail({ email }) - const optInCode = await getOptInCode(user.loginUserId) + const optInCode = await getOptInCode(user.id) const link = CONFIG.EMAIL_LINK_SETPASSWORD.replace( /{code}/g, @@ -520,7 +517,7 @@ export class UserResolver { } // load user - const user = await DbUser.findOneOrFail({ loginUserId: optInCode.userId }).catch(() => { + const user = await DbUser.findOneOrFail({ id: optInCode.userId }).catch(() => { throw new Error('Could not find corresponding Login User') }) From ff77ffc82c13c9243cbea005045637ae80012f84 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Feb 2022 21:41:14 +0100 Subject: [PATCH 20/24] adjusted db version --- backend/src/config/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 99859b252..f9707c711 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -4,7 +4,7 @@ import dotenv from 'dotenv' dotenv.config() const constants = { - DB_VERSION: '0016-transaction_signatures', + DB_VERSION: '0019-replace_login_user_id_with_state_user_id', } const server = { From b54ae481620672da8c51d66a49e68f8917f542f8 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Thu, 3 Feb 2022 21:45:45 +0100 Subject: [PATCH 21/24] fixed query --- backend/src/typeorm/repository/User.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/typeorm/repository/User.ts b/backend/src/typeorm/repository/User.ts index fa7115429..59d6ff465 100644 --- a/backend/src/typeorm/repository/User.ts +++ b/backend/src/typeorm/repository/User.ts @@ -5,7 +5,7 @@ import { User } from '@entity/User' export class UserRepository extends Repository { async findByPubkeyHex(pubkeyHex: string): Promise { return this.createQueryBuilder('user') - .where('hex(user.pubkey) = :pubkeyHex', { pubkeyHex }) + .where('hex(user.pubKey) = :pubkeyHex', { pubkeyHex }) .getOneOrFail() } From 7ba01d3f0618e754d065bf52a29bf9e3a32a4bd1 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 4 Feb 2022 13:41:24 +0100 Subject: [PATCH 22/24] removed todo no longer applying --- database/migrations/0017-combine_user_tables.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/database/migrations/0017-combine_user_tables.ts b/database/migrations/0017-combine_user_tables.ts index d4a0f7c0c..023c85fb9 100644 --- a/database/migrations/0017-combine_user_tables.ts +++ b/database/migrations/0017-combine_user_tables.ts @@ -14,7 +14,6 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis await queryFn('ALTER TABLE `state_users` DROP INDEX `public_key`;') // Allow NULL on the `state_users` pubkey like it is allowed on `login_users` - // TODO remove the random key shenanigans await queryFn('ALTER TABLE `state_users` MODIFY COLUMN `public_key` binary(32) DEFAULT NULL;') // instead use a unique constraint for the email like on `login_users` From 1025ba433648fcc553950e3c3e0320fbd9de9924 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 4 Feb 2022 13:47:57 +0100 Subject: [PATCH 23/24] removed loginUserId --- database/src/seeds/users/bibi-bloxberg.ts | 1 - database/src/seeds/users/bob-baumeister.ts | 1 - database/src/seeds/users/garrick-ollivander.ts | 1 - database/src/seeds/users/peter-lustig.ts | 1 - database/src/seeds/users/raeuber-hotzenplotz.ts | 1 - 5 files changed, 5 deletions(-) diff --git a/database/src/seeds/users/bibi-bloxberg.ts b/database/src/seeds/users/bibi-bloxberg.ts index 86fae0300..221349cb7 100644 --- a/database/src/seeds/users/bibi-bloxberg.ts +++ b/database/src/seeds/users/bibi-bloxberg.ts @@ -1,5 +1,4 @@ export const bibiBloxberg = { - loginUserId: 1, email: 'bibi@bloxberg.de', firstName: 'Bibi', lastName: 'Bloxberg', diff --git a/database/src/seeds/users/bob-baumeister.ts b/database/src/seeds/users/bob-baumeister.ts index a42993a1d..013636079 100644 --- a/database/src/seeds/users/bob-baumeister.ts +++ b/database/src/seeds/users/bob-baumeister.ts @@ -1,5 +1,4 @@ export const bobBaumeister = { - loginUserId: 2, email: 'bob@baumeister.de', firstName: 'Bob', lastName: 'der Baumeister', diff --git a/database/src/seeds/users/garrick-ollivander.ts b/database/src/seeds/users/garrick-ollivander.ts index d84cd02db..1c7bbb9fc 100644 --- a/database/src/seeds/users/garrick-ollivander.ts +++ b/database/src/seeds/users/garrick-ollivander.ts @@ -1,5 +1,4 @@ export const garrickOllivander = { - loginUserId: 3, email: 'garrick@ollivander.com', firstName: 'Garrick', lastName: 'Ollivander', diff --git a/database/src/seeds/users/peter-lustig.ts b/database/src/seeds/users/peter-lustig.ts index 96b44cda4..c96b28a65 100644 --- a/database/src/seeds/users/peter-lustig.ts +++ b/database/src/seeds/users/peter-lustig.ts @@ -1,5 +1,4 @@ export const peterLustig = { - loginUserId: 4, email: 'peter@lustig.de', firstName: 'Peter', lastName: 'Lustig', diff --git a/database/src/seeds/users/raeuber-hotzenplotz.ts b/database/src/seeds/users/raeuber-hotzenplotz.ts index 0df964167..c1f31b490 100644 --- a/database/src/seeds/users/raeuber-hotzenplotz.ts +++ b/database/src/seeds/users/raeuber-hotzenplotz.ts @@ -1,5 +1,4 @@ export const raeuberHotzenplotz = { - loginUserId: 5, email: 'raeuber@hotzenplotz.de', firstName: 'Räuber', lastName: 'Hotzenplotz', From 72af3ebdbc499c74ac0ba913f364c466e5222a18 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 4 Feb 2022 13:53:28 +0100 Subject: [PATCH 24/24] checked an removed todos where possible --- database/migrations/0017-combine_user_tables.ts | 3 --- .../0018-combine_login_user_backups_and_user_table.ts | 1 - 2 files changed, 4 deletions(-) diff --git a/database/migrations/0017-combine_user_tables.ts b/database/migrations/0017-combine_user_tables.ts index 023c85fb9..04be53615 100644 --- a/database/migrations/0017-combine_user_tables.ts +++ b/database/migrations/0017-combine_user_tables.ts @@ -10,7 +10,6 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis await queryFn('ALTER TABLE `state_users` DROP COLUMN `group_id`;') // Remove the unique constraint from the pubkey - // TODO - check for code impact await queryFn('ALTER TABLE `state_users` DROP INDEX `public_key`;') // Allow NULL on the `state_users` pubkey like it is allowed on `login_users` @@ -21,11 +20,9 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis await queryFn( 'ALTER TABLE `state_users` MODIFY COLUMN `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL;', ) - // TODO - check for code impact await queryFn('ALTER TABLE `state_users` ADD CONSTRAINT `email` UNIQUE KEY (`email`);') // Create `login_user_id` column - to store the login_users.id field to not break references. - // TODO - what happens when we create a new user - how do we generate the login_user_id? await queryFn( 'ALTER TABLE `state_users` ADD COLUMN `login_user_id` int(10) unsigned DEFAULT NULL AFTER `id`;', ) diff --git a/database/migrations/0018-combine_login_user_backups_and_user_table.ts b/database/migrations/0018-combine_login_user_backups_and_user_table.ts index 9cc1853dd..2141017bd 100644 --- a/database/migrations/0018-combine_login_user_backups_and_user_table.ts +++ b/database/migrations/0018-combine_login_user_backups_and_user_table.ts @@ -7,7 +7,6 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { // We only keep the passphrase, the mnemonic type is a constant, // since every passphrase was converted to mnemonic type 2 - // TODO there is now default NULL instead of NOT NULL - check code impact await queryFn( 'ALTER TABLE `state_users` ADD COLUMN `passphrase` text DEFAULT NULL AFTER `publisher_id`;', )