diff --git a/server/migrations/20240327015248-add-user-role.js b/server/migrations/20240327015248-add-user-role.js new file mode 100644 index 000000000..68e246630 --- /dev/null +++ b/server/migrations/20240327015248-add-user-role.js @@ -0,0 +1,41 @@ +'use strict'; + +module.exports = { + async up (queryInterface, Sequelize) { + await queryInterface.addColumn("users", "role", { + type: Sequelize.ENUM("admin", "member", "viewer", "guest"), + allowNull: true, + }); + if (process.env.DEPLOYMENT === "hosted") { + return; + } + + await queryInterface.sequelize.transaction(async (transaction) => { + await queryInterface.sequelize.query( + `UPDATE users SET role = 'viewer' WHERE "isViewer" = true`, + { + transaction, + type: queryInterface.sequelize.QueryTypes.UPDATE, + } + ); + await queryInterface.sequelize.query( + `UPDATE users SET role = 'admin' WHERE "isAdmin" = true`, + { + transaction, + type: queryInterface.sequelize.QueryTypes.UPDATE, + } + ); + await queryInterface.sequelize.query( + `UPDATE users SET role = 'member' WHERE role IS NULL`, + { + transaction, + type: queryInterface.sequelize.QueryTypes.UPDATE, + } + ); + }); + }, + + async down (queryInterface) { + await queryInterface.removeColumn("users", "role"); + } +}; \ No newline at end of file diff --git a/server/models/User.ts b/server/models/User.ts index 529091015..da616b8d7 100644 --- a/server/models/User.ts +++ b/server/models/User.ts @@ -29,6 +29,7 @@ import { IsDate, AllowNull, AfterUpdate, + BeforeSave, } from "sequelize-typescript"; import { UserPreferenceDefaults } from "@shared/constants"; import { languages } from "@shared/i18n"; @@ -144,6 +145,10 @@ class User extends ParanoidModel< @Column isViewer: boolean; + @Default(UserRole.Member) + @Column(DataType.ENUM(...Object.values(UserRole))) + role: UserRole; + @Column(DataType.BLOB) @Encrypted get jwtSecret() { @@ -623,6 +628,20 @@ class User extends ParanoidModel< }); }; + /** + * Temporary hook to double write role while we transition to the new field. + */ + @BeforeSave + static doubleWriteRole = async (model: User) => { + if (model.isAdmin) { + model.role = UserRole.Admin; + } else if (model.isViewer) { + model.role = UserRole.Viewer; + } else { + model.role = UserRole.Member; + } + }; + @BeforeCreate static setRandomJwtSecret = (model: User) => { model.jwtSecret = crypto.randomBytes(64).toString("hex");