From 0f7ced8c11183e02aa5d363e9be82e330127da5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Tue, 16 Nov 2021 10:59:05 +0100 Subject: [PATCH] feat(user migration): update with proper stream and server acl, user cleanup --- .../server/scripts/duplicateUserMigration.js | 56 +++++++++++++++++-- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/packages/server/scripts/duplicateUserMigration.js b/packages/server/scripts/duplicateUserMigration.js index 9901c521b..d3d9b45d1 100644 --- a/packages/server/scripts/duplicateUserMigration.js +++ b/packages/server/scripts/duplicateUserMigration.js @@ -2,6 +2,7 @@ const appRoot = require( 'app-root-path' ) const knex = require( `${appRoot}/db/knex` ) const bcrypt = require( 'bcrypt' ) const crs = require( 'crypto-random-string' ) +const roles = require( `${appRoot}/modules/core/roles.js` ) const Users = ( ) => knex( 'users' ) const Acl = ( ) => knex( 'server_acl' ) @@ -15,10 +16,10 @@ const migrationTargets = [ [ 'file_uploads' , 'userId' ], [ 'personal_api_tokens' , 'userId' ], [ 'refresh_tokens' , 'userId' ], - [ 'server_acl' , 'userId' ], //userId is a PrimaryKey in this table, act accordingly + // [ 'server_acl' , 'userId' ], //userId is a PrimaryKey in this table, act accordingly [ 'server_apps' , 'authorId' ], [ 'server_invites' , 'inviterId' ], - [ 'stream_acl' , 'userId' ],//userId, with resourceId is a PrimaryKey in this table, act accordingly + // [ 'stream_acl' , 'userId' ],//userId, with resourceId is a PrimaryKey in this table, act accordingly [ 'stream_activity' , 'userId' ], ] @@ -32,6 +33,38 @@ const migrateColumnValue = async( tableName, columnName, oldUser, newUser ) => { } } +const serverAclMigration = async ( { lowerUser, upperUser } ) => { + const oldAcl = await knex( 'server_acl' ).where( { userId: upperUser.id } ).first() + // if the old user was admin, make the target admin too + if ( oldAcl.role === 'server:admin' ) await knex( 'server_acl' ).where( { userId: lowerUser.id } ).update( { role: 'server:admin' } ) +} + +const _migrateSingleStreamAccess = async ( { lowerUser, upperUser, upperStreamAcl } ) => { + const upperRole = roles.filter( r => r.name === upperStreamAcl.role )[0] + const lowerAcl = await knex( 'stream_acl' ).where( { userId: lowerUser.id, resourceId: upperStreamAcl.resourceId } ).first() + // see if the lowerUser has access to the stream + if ( lowerAcl ) { + // if the upper user had more access, migrate the lower user up + const lowerRole = roles.filter( r => r.name === lowerAcl.role )[0] + if ( lowerRole.weight < upperRole.weight ) + await knex( 'stream_acl' ) + .where( { userId: lowerUser.id, resourceId: upperStreamAcl.resourceId } ) + .update( { role: upperRole.name } ) + } else { + // if it didn't have access, just add it + let lowerStreamAcl = { ...upperStreamAcl } + lowerStreamAcl.userId = lowerUser.id + await knex( 'stream_acl' ).insert( lowerStreamAcl ) + } +} + +const streamAclMigration = async ( { lowerUser, upperUser } ) => { + const upperAcl = await knex( 'stream_acl' ).where( { userId: upperUser.id } ) + + await Promise.all( upperAcl.map( async upperStreamAcl => await _migrateSingleStreamAccess( { lowerUser, upperUser, upperStreamAcl } ) ) ) +} + + const createMigrations = ( { lowerUser, upperUser } ) => migrationTargets.map( ( [ tableName, columnName ] ) => { migrateColumnValue( tableName, columnName, upperUser,lowerUser ) } ) @@ -41,7 +74,8 @@ const createUser = async user => { user.id = crs( { length: 10 } ) if ( user.password ) { - user.passwordDigest = await bcrypt.hash( user.password, 10 ) + let pwdigest =await bcrypt.hash( user.password, 10 ) + user.passwordDigest = pwdigest } delete user.password @@ -64,13 +98,14 @@ const createData = async ( ) => { { email: 'fdsa@asdf.asdf', password: '12345678', name: 'Asdf Asdf' } , { email: 'Fdsa@asdf.asdf', password: '12345678', name: 'Asdf Asdf' } ] - users.map( async user => { + await Promise.all( users.map( async user => { try { - await createUser( user ) + let userId = await createUser( user ) + console.log( userId ) } catch ( err ) { console.log( err ) } - } ) + } ) ) } const getDuplicateUsers = async ( ) => { @@ -79,6 +114,10 @@ const getDuplicateUsers = async ( ) => { let lowerEmail = dup.lowered let lowerUser = await userByEmailQuery( lowerEmail ).first( ) + // if no user found migrate to a random one? + // TODO: decide 👆 + // my idea, take the first one and run with it + if ( !lowerUser ) lowerUser = await Users( ).whereRaw( 'lower(email) = lower(?)',[ lowerEmail ] ).first() let upperUser = await Users( ).whereRaw( 'lower(email) = lower(?)',[ lowerEmail ] ).whereNot( { id: lowerUser.id } ).first( ) return { lowerUser,upperUser } } ) ) @@ -89,6 +128,11 @@ const runMigrations = async ( ) => { await Promise.all( duplicateUsers.map( async userDouble => { const migrations = createMigrations( userDouble ) await Promise.all( migrations.map( async migrationStep => await migrationStep ) ) + await serverAclMigration( userDouble ) + await streamAclMigration( userDouble ) + + // remove the now defunct user + await Users( ).where( { email: userDouble.upperUser.email } ).delete( ) } ) ) }