9d3d431416
- Added account types and privileged account types constants. - Created admin user controller for updating user security fields. - Developed role assignment controller for managing user roles. - Implemented validation middleware for request schemas. - Defined user role and auth session models for database interactions. - Created services for authentication, email notifications, and OTP handling. - Developed OAuth service for Google and Apple authentication. - Added JWT utility functions for token generation and verification. - Implemented comprehensive tests for authentication, session management, and password policies. - Created migration for updating user schema and adding new tables for auth sessions and user identities.
62 lines
4.6 KiB
JavaScript
62 lines
4.6 KiB
JavaScript
"use strict";
|
|
|
|
module.exports = {
|
|
async up(queryInterface, Sequelize) {
|
|
const userColumns = await queryInterface.describeTable("users");
|
|
await queryInterface.changeColumn("users", "accountType", {
|
|
type: Sequelize.ENUM("superadmin", "admin", "manager", "business_customer", "rider", "customer", "support_agent"),
|
|
allowNull: false, defaultValue: "customer",
|
|
});
|
|
await queryInterface.changeColumn("users", "password", { type: Sequelize.STRING, allowNull: true });
|
|
if (!userColumns.passwordChangedAt) await queryInterface.addColumn("users", "passwordChangedAt", { type: Sequelize.DATE, allowNull: true });
|
|
await queryInterface.addColumn("users", "tokenVersion", { type: Sequelize.INTEGER, allowNull: false, defaultValue: 0 });
|
|
await queryInterface.addColumn("users", "lastLoginAt", { type: Sequelize.DATE, allowNull: true });
|
|
|
|
await queryInterface.createTable("auth_sessions", {
|
|
id: { type: Sequelize.UUID, primaryKey: true }, user_id: { type: Sequelize.STRING, allowNull: false, references: { model: "users", key: "id" }, onDelete: "CASCADE" },
|
|
token_family_id: { type: Sequelize.UUID, allowNull: false }, refresh_token_hash: { type: Sequelize.STRING(64), allowNull: false },
|
|
device_name: Sequelize.STRING, user_agent: Sequelize.STRING(500), ip_address: Sequelize.STRING(64),
|
|
remember_me: { type: Sequelize.BOOLEAN, allowNull: false, defaultValue: false }, token_version: { type: Sequelize.INTEGER, allowNull: false },
|
|
last_used_at: Sequelize.DATE, expires_at: { type: Sequelize.DATE, allowNull: false }, revoked_at: Sequelize.DATE,
|
|
revoked_reason: Sequelize.STRING, replaced_by_session_id: Sequelize.UUID,
|
|
createdAt: { type: Sequelize.DATE, allowNull: false }, updatedAt: { type: Sequelize.DATE, allowNull: false },
|
|
});
|
|
await queryInterface.addIndex("auth_sessions", ["user_id"]);
|
|
await queryInterface.addIndex("auth_sessions", ["token_family_id"]);
|
|
await queryInterface.addIndex("auth_sessions", ["expires_at"]);
|
|
|
|
await queryInterface.createTable("user_identities", {
|
|
id: { type: Sequelize.UUID, primaryKey: true }, user_id: { type: Sequelize.STRING, allowNull: false, references: { model: "users", key: "id" }, onDelete: "CASCADE" },
|
|
provider: { type: Sequelize.ENUM("google", "apple"), allowNull: false }, provider_subject: { type: Sequelize.STRING, allowNull: false },
|
|
provider_email: Sequelize.STRING, createdAt: { type: Sequelize.DATE, allowNull: false }, updatedAt: { type: Sequelize.DATE, allowNull: false },
|
|
});
|
|
await queryInterface.addIndex("user_identities", ["provider", "provider_subject"], { unique: true, name: "user_identity_provider_subject_unique" });
|
|
await queryInterface.addIndex("user_identities", ["user_id"]);
|
|
|
|
await queryInterface.createTable("user_roles", {
|
|
id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true },
|
|
user_id: { type: Sequelize.STRING, allowNull: false, references: { model: "users", key: "id" }, onDelete: "CASCADE" },
|
|
role_id: { type: Sequelize.STRING, allowNull: false, references: { model: "roles", key: "role_id" }, onDelete: "CASCADE" },
|
|
createdAt: { type: Sequelize.DATE, allowNull: false }, updatedAt: { type: Sequelize.DATE, allowNull: false },
|
|
});
|
|
await queryInterface.addIndex("user_roles", ["user_id", "role_id"], { unique: true, name: "user_role_unique" });
|
|
await queryInterface.addIndex("rolePermission", ["role_id", "permission_id"], { unique: true, name: "role_permission_unique" });
|
|
await queryInterface.addIndex("userPermission", ["user_id", "permission_id"], { unique: true, name: "user_permission_unique" });
|
|
await queryInterface.addIndex("roles", ["roleName"], { unique: true, name: "role_name_unique" });
|
|
},
|
|
|
|
async down(queryInterface, Sequelize) {
|
|
await queryInterface.removeIndex("roles", "role_name_unique");
|
|
await queryInterface.removeIndex("userPermission", "user_permission_unique");
|
|
await queryInterface.removeIndex("rolePermission", "role_permission_unique");
|
|
await queryInterface.dropTable("user_roles");
|
|
await queryInterface.dropTable("user_identities");
|
|
await queryInterface.dropTable("auth_sessions");
|
|
await queryInterface.removeColumn("users", "lastLoginAt");
|
|
await queryInterface.removeColumn("users", "tokenVersion");
|
|
// passwordChangedAt may predate this migration, so down intentionally preserves it.
|
|
await queryInterface.changeColumn("users", "password", { type: Sequelize.STRING, allowNull: false });
|
|
await queryInterface.changeColumn("users", "accountType", { type: Sequelize.ENUM("business_customer", "customer"), defaultValue: "customer" });
|
|
},
|
|
};
|