Browse Source

Merge pull request #6333 from nocodb/fix/super-access

fix: super access
pull/6334/head
Pranav C 1 year ago committed by GitHub
parent
commit
729408ab7a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      packages/nc-gui/middleware/auth.global.ts
  2. 267
      packages/nocodb/src/helpers/initAdminFromEnv.ts
  3. 7
      packages/nocodb/src/middlewares/extract-ids/extract-ids.middleware.ts
  4. 2
      packages/nocodb/src/models/User.ts

4
packages/nc-gui/middleware/auth.global.ts

@ -85,7 +85,7 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
} }
} else { } else {
/** If page is limited to certain users verify the user have the roles */ /** 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.") message.error("You don't have enough permission to access the page.")
return navigateTo('/') return navigateTo('/')
} }
@ -94,7 +94,7 @@ export default defineNuxtRouteMiddleware(async (to, from) => {
if (to.params.projectId && from.params.projectId !== to.params.projectId) { if (to.params.projectId && from.params.projectId !== to.params.projectId) {
const user = await api.auth.me({ project_id: to.params.projectId as string }) 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.") message.error("You don't have enough permission to access the project.")
return navigateTo('/') return navigateTo('/')

267
packages/nocodb/src/helpers/initAdminFromEnv.ts

@ -62,7 +62,7 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) {
salt, salt,
); );
const email_verification_token = uuidv4(); const email_verification_token = uuidv4();
const roles = 'user,super'; const roles = 'org-level-creator,super';
// if super admin not present // if super admin not present
if (await User.isFirst(ncMeta)) { if (await User.isFirst(ncMeta)) {
@ -78,6 +78,7 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) {
salt, salt,
password, password,
email_verification_token, email_verification_token,
token_version: randomTokenString(),
roles, roles,
}, },
ncMeta, ncMeta,
@ -89,122 +90,153 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) {
salt, salt,
); );
const email_verification_token = uuidv4(); const email_verification_token = uuidv4();
const superUser = await ncMeta.metaGet2(null, null, MetaTable.USERS, { // TODO improve this
roles: 'user,super', const superUsers = await ncMeta.metaList2(null, null, MetaTable.USERS);
});
if (!superUser?.id) { let superUserPresent = false;
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}`,
);
// Update email and password of super admin account for (const user of superUsers) {
await User.update( if (!user.roles?.includes('super')) continue;
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,
});
await User.insert( superUserPresent = true;
{
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
// check user account already present with the new admin email if (email !== user.email) {
const existingUserWithNewEmail = await User.getByEmail(email, ncMeta); // update admin email and password and migrate projects
// if user already present and associated with some project
if (existingUserWithNewEmail?.id) { // check user account already present with the new admin email
// get all project access belongs to the existing account const existingUserWithNewEmail = await User.getByEmail(
// and migrate to the admin account email,
const existingUserProjects = await ncMeta.metaList2( ncMeta,
null,
null,
MetaTable.PROJECT_USERS,
{
condition: { fk_user_id: existingUserWithNewEmail.id },
},
); );
for (const existingUserProject of existingUserProjects) { if (existingUserWithNewEmail?.id) {
const userProject = await ProjectUser.get( // get all project access belongs to the existing account
existingUserProject.project_id, // and migrate to the admin account
superUser.id, const existingUserProjects = await ncMeta.metaList2(
ncMeta, null,
null,
MetaTable.PROJECT_USERS,
{
condition: { fk_user_id: existingUserWithNewEmail.id },
},
); );
// if admin user already have access to the project for (const existingUserProject of existingUserProjects) {
// then update role based on the highest access level const userProject = await ProjectUser.get(
if (userProject) { existingUserProject.project_id,
if ( user.id,
rolesLevel[userProject.roles] > ncMeta,
rolesLevel[existingUserProject.roles] );
) {
await ProjectUser.update( // if admin user already have access to the project
userProject.project_id, // then update role based on the highest access level
superUser.id, if (userProject) {
existingUserProject.roles, 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, ncMeta,
); );
} }
} else { // delete the old project access entry from DB
// if super doesn't have access then add the access await ProjectUser.delete(
await ProjectUser.insert( existingUserProject.project_id,
{ existingUserProject.fk_user_id,
...existingUserProject,
fk_user_id: superUser.id,
},
ncMeta, ncMeta,
); );
} }
// delete the old project access entry from DB
await ProjectUser.delete( // delete existing user
existingUserProject.project_id, await ncMeta.metaDelete(
existingUserProject.fk_user_id, 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, ncMeta,
); );
} }
} else {
// delete existing user const newPasswordHash = await promisify(bcrypt.hash)(
await ncMeta.metaDelete( process.env.NC_ADMIN_PASSWORD,
null, user.salt,
null,
MetaTable.USERS,
existingUserWithNewEmail.id,
); );
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 // clear cache
await NocoCache.delAll( await NocoCache.delAll(
CacheScope.USER, CacheScope.USER,
@ -217,9 +249,9 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) {
`${CacheScope.USER}:${existingUserWithNewEmail.email}`, `${CacheScope.USER}:${existingUserWithNewEmail.email}`,
); );
// Update email and password of super admin account // Update password and roles of existing user
await User.update( await User.update(
superUser.id, existingUserWithNewEmail.id,
{ {
salt, salt,
email, email,
@ -227,52 +259,37 @@ export default async function initAdminFromEnv(_ncMeta = Noco.ncMeta) {
email_verification_token, email_verification_token,
token_version: randomTokenString(), token_version: randomTokenString(),
refresh_token: null, refresh_token: null,
roles,
}, },
ncMeta, ncMeta,
); );
} else { } else {
// if email's are not different update the password and hash // no super user present and no user present with the new admin email
await User.update( T.emit('evt', {
superUser.id, evt_type: 'project:invite',
{ count: 1,
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,
);
if (newPasswordHash !== superUser.password) { await User.insert(
// if email's are same and passwords are different
// then update the password and token version
await User.update(
superUser.id,
{ {
email,
salt, salt,
password, password,
email_verification_token, email_verification_token,
token_version: randomTokenString(), token_version: randomTokenString(),
refresh_token: null, roles,
}, },
ncMeta, ncMeta,
); );
} }
} }
} }
await ncMeta.commit(); await ncMeta.commit();
} catch (e) { } catch (e) {
console.log('Error occurred while updating/creating admin user'); console.log('Error occurred while updating/creating admin user');
console.log(e);
await ncMeta.rollback(e); await ncMeta.rollback(e);
throw e;
} }
} }
} }

7
packages/nocodb/src/middlewares/extract-ids/extract-ids.middleware.ts

@ -220,6 +220,13 @@ export class AclMiddleware implements NestInterceptor {
NcError.forbidden('Unauthorized access'); 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<string, boolean> = extractRolesObj(userScopeRole); const roles: Record<string, boolean> = extractRolesObj(userScopeRole);
if (req?.user?.is_api_token && blockApiTokenAccess) { if (req?.user?.is_api_token && blockApiTokenAccess) {

2
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 // check if the target email addr is in use or not
const targetUser = await this.getByEmail(updateObj.email, ncMeta); const targetUser = await this.getByEmail(updateObj.email, ncMeta);
if (targetUser.id !== id) { if (targetUser && targetUser.id !== id) {
NcError.badRequest('email is in use'); NcError.badRequest('email is in use');
} }
} else { } else {

Loading…
Cancel
Save