From 1b66ea3c46d7fa5aa138b8162c57f7c02e84b0c6 Mon Sep 17 00:00:00 2001 From: Alexander Borg Date: Sun, 21 Sep 2025 09:12:42 +0200 Subject: [PATCH] Fix jwt-token --- ...20250912000001-add-multi-tenant-support.js | 375 +----------------- 1 file changed, 15 insertions(+), 360 deletions(-) diff --git a/server/migrations/20250912000001-add-multi-tenant-support.js b/server/migrations/20250912000001-add-multi-tenant-support.js index 7930f49..744a782 100644 --- a/server/migrations/20250912000001-add-multi-tenant-support.js +++ b/server/migrations/20250912000001-add-multi-tenant-support.js @@ -1,380 +1,35 @@ /** * Migration: Add Multi-Tenant Support - * Adds tenant table and updates user table for multi-tenancy */ 'use strict'; module.exports = { async up(queryInterface, Sequelize) { - // Create tenants table (idempotent) - try { - await queryInterface.createTable('tenants', { - id: { - type: Sequelize.UUID, - defaultValue: Sequelize.UUIDV4, - primaryKey: true - }, - name: { - type: Sequelize.STRING, - allowNull: false, - comment: 'Human-readable tenant name' - }, - slug: { - type: Sequelize.STRING, - allowNull: false, - unique: true, - comment: 'URL-safe tenant identifier' - }, - domain: { - type: Sequelize.STRING, - allowNull: true, - comment: 'Custom domain for this tenant' - }, - subscription_type: { + const tableDescription = await queryInterface.describeTable('tenants'); + + // Add essential multi-tenant columns + if (!tableDescription.subscription_type) { + await queryInterface.addColumn('tenants', 'subscription_type', { type: Sequelize.ENUM('free', 'basic', 'premium', 'enterprise'), - defaultValue: 'basic', - comment: 'Subscription tier' - }, - is_active: { - type: Sequelize.BOOLEAN, - defaultValue: true, - comment: 'Whether tenant is active' - }, - auth_provider: { + defaultValue: 'basic' + }); + } + + if (!tableDescription.auth_provider) { + await queryInterface.addColumn('tenants', 'auth_provider', { type: Sequelize.ENUM('local', 'saml', 'oauth', 'ldap', 'custom_sso'), - defaultValue: 'local', - comment: 'Primary authentication provider' - }, - auth_config: { - type: Sequelize.JSONB, - allowNull: true, - comment: 'Authentication provider configuration' - }, - user_mapping: { - type: Sequelize.JSONB, - allowNull: true, - comment: 'User attribute mapping from external provider' - }, - role_mapping: { - type: Sequelize.JSONB, - allowNull: true, - comment: 'Role mapping from external provider to internal roles' - }, - branding: { - type: Sequelize.JSONB, - allowNull: true, - comment: 'Tenant-specific branding' - }, - features: { - type: Sequelize.JSONB, - defaultValue: { - max_devices: 10, - max_users: 5, - api_rate_limit: 1000, - data_retention_days: 90, - features: ['basic_detection', 'alerts', 'dashboard'] - }, - comment: 'Tenant feature limits and enabled features' - }, - admin_email: { - type: Sequelize.STRING, - allowNull: true, - comment: 'Primary admin email for this tenant' - }, - admin_phone: { - type: Sequelize.STRING, - allowNull: true, - comment: 'Primary admin phone for this tenant' - }, - billing_email: { - type: Sequelize.STRING, - allowNull: true - }, - payment_method_id: { - type: Sequelize.STRING, - allowNull: true, - comment: 'Payment provider customer ID' - }, - metadata: { - type: Sequelize.JSONB, - allowNull: true, - comment: 'Additional tenant metadata' - }, - created_at: { - type: Sequelize.DATE, - defaultValue: Sequelize.NOW - }, - updated_at: { - type: Sequelize.DATE, - defaultValue: Sequelize.NOW - } - }); - console.log('✅ Created tenants table'); - } catch (error) { - if (error.parent?.code === '42P07') { // Table already exists - console.log('⚠️ Tenants table already exists, skipping...'); - } else { - throw error; - } - } - - // Add indexes to tenants table (idempotent) - try { - await queryInterface.addIndex('tenants', ['slug'], { unique: true }); - console.log('✅ Added unique index on tenants.slug'); - } catch (error) { - if (error.parent?.code === '42P07') { // Index already exists - console.log('⚠️ Index tenants_slug already exists, skipping...'); - } else { - throw error; - } - } - - try { - await queryInterface.addIndex('tenants', ['domain'], { - unique: true, - where: { domain: { [Sequelize.Op.ne]: null } } + defaultValue: 'local' }); - console.log('✅ Added unique index on tenants.domain'); - } catch (error) { - if (error.parent?.code === '42P07') { // Index already exists - console.log('⚠️ Index tenants_domain already exists, skipping...'); - } else { - throw error; - } } - try { - await queryInterface.addIndex('tenants', ['is_active']); - console.log('✅ Added index on tenants.is_active'); - } catch (error) { - if (error.parent?.code === '42P07') { // Index already exists - console.log('⚠️ Index tenants_is_active already exists, skipping...'); - } else { - throw error; - } - } - - try { - await queryInterface.addIndex('tenants', ['auth_provider']); - console.log('✅ Added index on tenants.auth_provider'); - } catch (error) { - if (error.parent?.code === '42P07') { // Index already exists - console.log('⚠️ Index tenants_auth_provider already exists, skipping...'); - } else { - throw error; - } - } - - // Add tenant-related columns to users table (idempotent) - const tables = await queryInterface.showAllTables(); - - if (!tables.includes('users')) { - console.log('⚠️ Users table does not exist yet, skipping user tenant columns migration...'); - } else { - const usersTableDescription = await queryInterface.describeTable('users'); - - if (!usersTableDescription.tenant_id) { - await queryInterface.addColumn('users', 'tenant_id', { - type: Sequelize.UUID, - allowNull: true, - references: { - model: 'tenants', - key: 'id' - }, - comment: 'Tenant this user belongs to' - }); - console.log('✅ Added tenant_id column to users table'); - } else { - console.log('⚠️ Column tenant_id already exists in users table, skipping...'); - } - - if (!usersTableDescription.external_provider) { - await queryInterface.addColumn('users', 'external_provider', { - type: Sequelize.ENUM('local', 'saml', 'oauth', 'ldap', 'custom_sso'), - defaultValue: 'local', - comment: 'Authentication provider used for this user' - }); - console.log('✅ Added external_provider column to users table'); - } else { - console.log('⚠️ Column external_provider already exists in users table, skipping...'); - } - - if (!usersTableDescription.external_id) { - await queryInterface.addColumn('users', 'external_id', { - type: Sequelize.STRING, - allowNull: true, - comment: 'User ID from external authentication provider' - }); - console.log('✅ Added external_id column to users table'); - } else { - console.log('⚠️ Column external_id already exists in users table, skipping...'); - } - - // Add indexes to users table (idempotent) - try { - await queryInterface.addIndex('users', ['tenant_id']); - console.log('✅ Added index on users.tenant_id'); - } catch (error) { - if (error.parent?.code === '42P07') { // Index already exists - console.log('⚠️ Index users_tenant_id already exists, skipping...'); - } else { - throw error; - } - } - - try { - await queryInterface.addIndex('users', ['external_provider']); - console.log('✅ Added index on users.external_provider'); - } catch (error) { - if (error.parent?.code === '42P07') { // Index already exists - console.log('⚠️ Index users_external_provider already exists, skipping...'); - } else { - throw error; - } - } - - try { - await queryInterface.addIndex('users', ['external_id', 'tenant_id'], { - unique: true, - name: 'users_external_id_tenant_unique', - where: { external_id: { [Sequelize.Op.ne]: null } } - }); - console.log('✅ Added unique index on users.external_id + tenant_id'); - } catch (error) { - if (error.parent?.code === '42P07') { // Index already exists - console.log('⚠️ Index users_external_id_tenant_unique already exists, skipping...'); - } else { - throw error; - } - } - - // Create default tenant for backward compatibility (only if it doesn't exist) - const [existingTenant] = await queryInterface.sequelize.query( - 'SELECT id FROM tenants WHERE slug = \'default\' LIMIT 1', - { type: Sequelize.QueryTypes.SELECT } - ); - - let defaultTenantId; - if (!existingTenant) { - console.log('🏢 Creating default tenant...'); - defaultTenantId = await queryInterface.bulkInsert('tenants', [{ - id: Sequelize.literal('gen_random_uuid()'), - name: 'Default Organization', - slug: 'default', - subscription_type: 'enterprise', - is_active: true, - auth_provider: 'local', - features: JSON.stringify({ - max_devices: -1, - max_users: -1, - api_rate_limit: 50000, - data_retention_days: -1, - features: ['all'] - }), - created_at: new Date(), - updated_at: new Date() - }], { returning: true }); - } else { - console.log('⚠️ Default tenant already exists, skipping...'); - defaultTenantId = existingTenant.id; - } - - // Associate existing users with default tenant - await queryInterface.sequelize.query(` - UPDATE users - SET tenant_id = (SELECT id FROM tenants WHERE slug = 'default') - WHERE tenant_id IS NULL - `); - - // Add tenant_id to devices table if it exists - try { - await queryInterface.addColumn('devices', 'tenant_id', { - type: Sequelize.UUID, - allowNull: true, - references: { - model: 'tenants', - key: 'id' - }, - comment: 'Tenant this device belongs to' - }); - - await queryInterface.addIndex('devices', ['tenant_id']); - - // Associate existing devices with default tenant - await queryInterface.sequelize.query(` - UPDATE devices - SET tenant_id = (SELECT id FROM tenants WHERE slug = 'default') - WHERE tenant_id IS NULL - `); - } catch (error) { - console.log('Devices table not found or already has tenant_id column'); - } - - // Add tenant_id to alert_rules table if it exists - try { - await queryInterface.addColumn('alert_rules', 'tenant_id', { - type: Sequelize.UUID, - allowNull: true, - references: { - model: 'tenants', - key: 'id' - }, - comment: 'Tenant this alert rule belongs to' - }); - - await queryInterface.addIndex('alert_rules', ['tenant_id']); - - // Associate existing alert rules with default tenant - await queryInterface.sequelize.query(` - UPDATE alert_rules - SET tenant_id = (SELECT id FROM tenants WHERE slug = 'default') - WHERE tenant_id IS NULL - `); - } catch (error) { - console.log('Alert_rules table not found or already has tenant_id column'); - } - - } // Close the else block for users table check - - console.log('✅ Multi-tenant support added successfully'); - console.log('✅ Default tenant created for backward compatibility'); - console.log('✅ Existing data associated with default tenant'); + console.log(' Multi-tenant columns added'); }, async down(queryInterface, Sequelize) { - // Remove indexes from users table - await queryInterface.removeIndex('users', ['tenant_id']); - await queryInterface.removeIndex('users', ['external_provider']); - await queryInterface.removeIndex('users', 'users_external_id_tenant_unique'); - - // Remove columns from users table - await queryInterface.removeColumn('users', 'tenant_id'); - await queryInterface.removeColumn('users', 'external_provider'); - await queryInterface.removeColumn('users', 'external_id'); - - // Remove tenant_id from other tables - try { - await queryInterface.removeColumn('devices', 'tenant_id'); - } catch (error) { - console.log('Devices table tenant_id column not found'); - } - - try { - await queryInterface.removeColumn('alert_rules', 'tenant_id'); - } catch (error) { - console.log('Alert_rules table tenant_id column not found'); - } - - // Drop ENUMs - await queryInterface.sequelize.query('DROP TYPE IF EXISTS "enum_users_external_provider"'); + await queryInterface.removeColumn('tenants', 'auth_provider'); + await queryInterface.removeColumn('tenants', 'subscription_type'); await queryInterface.sequelize.query('DROP TYPE IF EXISTS "enum_tenants_auth_provider"'); await queryInterface.sequelize.query('DROP TYPE IF EXISTS "enum_tenants_subscription_type"'); - - // Drop tenants table - await queryInterface.dropTable('tenants'); - - console.log('✅ Multi-tenant support removed'); } };