diff --git a/packages/nc-gui/middleware/auth.global.ts b/packages/nc-gui/middleware/auth.global.ts index 571e094baf..64702addf7 100644 --- a/packages/nc-gui/middleware/auth.global.ts +++ b/packages/nc-gui/middleware/auth.global.ts @@ -85,7 +85,7 @@ export default defineNuxtRouteMiddleware(async (to, from) => { } } else { /** If page is limited to certain users verify the user have the roles */ - if (to.meta.allowedRoles && to.meta.allowedRoles.every((role) => !allRoles.value[role])) { + if (to.meta.allowedRoles && to.meta.allowedRoles.every((role) => !allRoles.value?.[role])) { message.error("You don't have enough permission to access the page.") return navigateTo('/') } @@ -94,7 +94,7 @@ export default defineNuxtRouteMiddleware(async (to, from) => { if (to.params.projectId && from.params.projectId !== to.params.projectId) { const user = await api.auth.me({ project_id: to.params.projectId as string }) - if (user?.roles?.user) { + if (user?.roles?.guest) { message.error("You don't have enough permission to access the project.") return navigateTo('/') diff --git a/packages/nocodb/src/helpers/initAdminFromEnv.ts b/packages/nocodb/src/helpers/initAdminFromEnv.ts index d203896270..4b71632eef 100644 --- a/packages/nocodb/src/helpers/initAdminFromEnv.ts +++ b/packages/nocodb/src/helpers/initAdminFromEnv.ts @@ -62,7 +62,7 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) { salt, ); const email_verification_token = uuidv4(); - const roles = 'user,super'; + const roles = 'org-level-creator,super'; // if super admin not present if (await User.isFirst(ncMeta)) { @@ -78,6 +78,7 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) { salt, password, email_verification_token, + token_version: randomTokenString(), roles, }, ncMeta, @@ -89,122 +90,153 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) { salt, ); const email_verification_token = uuidv4(); - const superUser = await ncMeta.metaGet2(null, null, MetaTable.USERS, { - roles: 'user,super', - }); + // TODO improve this + const superUsers = await ncMeta.metaList2(null, null, MetaTable.USERS); - if (!superUser?.id) { - const existingUserWithNewEmail = await User.getByEmail(email, ncMeta); - if (existingUserWithNewEmail?.id) { - // clear cache - await NocoCache.delAll( - CacheScope.USER, - `${existingUserWithNewEmail.email}___*`, - ); - await NocoCache.del( - `${CacheScope.USER}:${existingUserWithNewEmail.id}`, - ); - await NocoCache.del( - `${CacheScope.USER}:${existingUserWithNewEmail.email}`, - ); + let superUserPresent = false; - // Update email and password of super admin account - await User.update( - existingUserWithNewEmail.id, - { - salt, - email, - password, - email_verification_token, - token_version: randomTokenString(), - refresh_token: null, - roles, - }, - ncMeta, - ); - } else { - T.emit('evt', { - evt_type: 'project:invite', - count: 1, - }); + for (const user of superUsers) { + if (!user.roles?.includes('super')) continue; - await User.insert( - { - email, - salt, - password, - email_verification_token, - roles, - }, - ncMeta, - ); - } - } else if (email !== superUser.email) { - // update admin email and password and migrate projects - // if user already present and associated with some project + superUserPresent = true; - // check user account already present with the new admin email - const existingUserWithNewEmail = await User.getByEmail(email, ncMeta); + if (email !== user.email) { + // update admin email and password and migrate projects + // if user already present and associated with some project - if (existingUserWithNewEmail?.id) { - // get all project access belongs to the existing account - // and migrate to the admin account - const existingUserProjects = await ncMeta.metaList2( - null, - null, - MetaTable.PROJECT_USERS, - { - condition: { fk_user_id: existingUserWithNewEmail.id }, - }, + // check user account already present with the new admin email + const existingUserWithNewEmail = await User.getByEmail( + email, + ncMeta, ); - for (const existingUserProject of existingUserProjects) { - const userProject = await ProjectUser.get( - existingUserProject.project_id, - superUser.id, - ncMeta, + if (existingUserWithNewEmail?.id) { + // get all project access belongs to the existing account + // and migrate to the admin account + const existingUserProjects = await ncMeta.metaList2( + null, + null, + MetaTable.PROJECT_USERS, + { + condition: { fk_user_id: existingUserWithNewEmail.id }, + }, ); - // if admin user already have access to the project - // then update role based on the highest access level - if (userProject) { - if ( - rolesLevel[userProject.roles] > - rolesLevel[existingUserProject.roles] - ) { - await ProjectUser.update( - userProject.project_id, - superUser.id, - existingUserProject.roles, + for (const existingUserProject of existingUserProjects) { + const userProject = await ProjectUser.get( + existingUserProject.project_id, + user.id, + ncMeta, + ); + + // if admin user already have access to the project + // then update role based on the highest access level + if (userProject) { + if ( + rolesLevel[userProject.roles] > + rolesLevel[existingUserProject.roles] + ) { + await ProjectUser.update( + userProject.project_id, + user.id, + existingUserProject.roles, + ncMeta, + ); + } + } else { + // if super doesn't have access then add the access + await ProjectUser.insert( + { + ...existingUserProject, + fk_user_id: user.id, + }, ncMeta, ); } - } else { - // if super doesn't have access then add the access - await ProjectUser.insert( - { - ...existingUserProject, - fk_user_id: superUser.id, - }, + // delete the old project access entry from DB + await ProjectUser.delete( + existingUserProject.project_id, + existingUserProject.fk_user_id, ncMeta, ); } - // delete the old project access entry from DB - await ProjectUser.delete( - existingUserProject.project_id, - existingUserProject.fk_user_id, + + // delete existing user + await ncMeta.metaDelete( + null, + null, + MetaTable.USERS, + existingUserWithNewEmail.id, + ); + + // clear cache + await NocoCache.delAll( + CacheScope.USER, + `${existingUserWithNewEmail.email}___*`, + ); + await NocoCache.del( + `${CacheScope.USER}:${existingUserWithNewEmail.id}`, + ); + await NocoCache.del( + `${CacheScope.USER}:${existingUserWithNewEmail.email}`, + ); + + // Update email and password of super admin account + await User.update( + user.id, + { + salt, + email, + password, + email_verification_token, + token_version: randomTokenString(), + refresh_token: null, + }, + ncMeta, + ); + } else { + // if no user present with the new admin email update the email and password + await User.update( + user.id, + { + salt, + email, + password, + email_verification_token, + token_version: randomTokenString(), + refresh_token: null, + }, ncMeta, ); } - - // delete existing user - await ncMeta.metaDelete( - null, - null, - MetaTable.USERS, - existingUserWithNewEmail.id, + } else { + const newPasswordHash = await promisify(bcrypt.hash)( + process.env.NC_ADMIN_PASSWORD, + user.salt, ); + if (newPasswordHash !== user.password) { + // if email's are same and passwords are different + // then update the password and token version + await User.update( + user.id, + { + salt, + password, + email_verification_token, + token_version: randomTokenString(), + refresh_token: null, + }, + ncMeta, + ); + } + } + } + + if (!superUserPresent) { + // check user account already present with the new admin email + const existingUserWithNewEmail = await User.getByEmail(email, ncMeta); + if (existingUserWithNewEmail?.id) { // clear cache await NocoCache.delAll( CacheScope.USER, @@ -217,9 +249,9 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) { `${CacheScope.USER}:${existingUserWithNewEmail.email}`, ); - // Update email and password of super admin account + // Update password and roles of existing user await User.update( - superUser.id, + existingUserWithNewEmail.id, { salt, email, @@ -227,52 +259,37 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) { email_verification_token, token_version: randomTokenString(), refresh_token: null, + roles, }, ncMeta, ); } else { - // if email's are not different update the password and hash - await User.update( - superUser.id, - { - salt, - email, - password, - email_verification_token, - token_version: randomTokenString(), - refresh_token: null, - }, - ncMeta, - ); - } - } else { - const newPasswordHash = await promisify(bcrypt.hash)( - process.env.NC_ADMIN_PASSWORD, - superUser.salt, - ); + // no super user present and no user present with the new admin email + T.emit('evt', { + evt_type: 'project:invite', + count: 1, + }); - if (newPasswordHash !== superUser.password) { - // if email's are same and passwords are different - // then update the password and token version - await User.update( - superUser.id, + await User.insert( { + email, salt, password, email_verification_token, token_version: randomTokenString(), - refresh_token: null, + roles, }, ncMeta, ); } } } + await ncMeta.commit(); } catch (e) { console.log('Error occurred while updating/creating admin user'); - console.log(e); await ncMeta.rollback(e); + throw e; } } } diff --git a/packages/nocodb/src/middlewares/extract-ids/extract-ids.middleware.ts b/packages/nocodb/src/middlewares/extract-ids/extract-ids.middleware.ts index d5ed6a9c33..e158b16065 100644 --- a/packages/nocodb/src/middlewares/extract-ids/extract-ids.middleware.ts +++ b/packages/nocodb/src/middlewares/extract-ids/extract-ids.middleware.ts @@ -220,6 +220,13 @@ export class AclMiddleware implements NestInterceptor { NcError.forbidden('Unauthorized access'); } + // assign owner role to super admin for all projects + if (userScopeRole === OrgUserRoles.SUPER_ADMIN) { + req.user.project_roles = { + [ProjectRoles.OWNER]: true, + }; + } + const roles: Record = extractRolesObj(userScopeRole); if (req?.user?.is_api_token && blockApiTokenAccess) { diff --git a/packages/nocodb/src/models/User.ts b/packages/nocodb/src/models/User.ts index 9bd97b3fe5..d8e4eafd0f 100644 --- a/packages/nocodb/src/models/User.ts +++ b/packages/nocodb/src/models/User.ts @@ -87,7 +87,7 @@ export default class User implements UserType { // check if the target email addr is in use or not const targetUser = await this.getByEmail(updateObj.email, ncMeta); - if (targetUser.id !== id) { + if (targetUser && targetUser.id !== id) { NcError.badRequest('email is in use'); } } else {