diff --git a/.env.example b/.env.example index 7b9a801..2bf0eef 100644 --- a/.env.example +++ b/.env.example @@ -9,7 +9,7 @@ APP_STORAGE_PATH=./storage APP_LOCALE=de APP_LOCALES=de,en APP_I18N_DOMAIN=default -APP_CRYPTO_KEY= +APP_CRYPTO_KEY=9792a9ce0def59c034a651f2f6f26f3dcda2f78c1e3513e67be4da06afc24680 SESSION_NAME=CoreCore TENANT_SCOPE_STRICT=true diff --git a/db/init/init.sql b/db/init/init.sql index 7f84e85..66d5970 100644 --- a/db/init/init.sql +++ b/db/init/init.sql @@ -1749,3 +1749,204 @@ CROSS JOIN ( ) n WHERE u.email = 'demo@user.com' AND NOT EXISTS (SELECT 1 FROM user_notifications un WHERE un.recipient_user_id = u.id); + +-- ============================================= +-- Seed: Live test data — breadcrumb + icoreon tenants +-- --------------------------------------------- +-- Snapshot of the local dev DB so a fresh `docker compose up` immediately +-- has working test connections for Microsoft SSO (login) and Business +-- Central (helpdesk). Encrypted secrets below are AES-256-GCM blobs +-- produced with APP_CRYPTO_KEY=9792a9ce0def59c034a651f2f6f26f3dcda2f78c1e3513e67be4da06afc24680 +-- (set in .env.example). Decryption fails if the key differs. +-- All inserts are idempotent: re-running init.sql is a no-op. +-- ============================================= + +-- ─── Live tenant 1: breadcrumb mediasolutions GmbH ─── +INSERT INTO `tenants` ( + `uuid`, `description`, `address`, `postal_code`, `city`, `country`, `region`, + `vat_id`, `tax_number`, `phone`, `fax`, `email`, `support_email`, `support_phone`, + `billing_email`, `website`, `privacy_url`, `imprint_url`, + `primary_color`, `default_theme`, `allow_user_theme`, `status`, `created` +) +SELECT + '57349d01-262e-11f1-8ada-dab0b621b876', 'breadcrumb mediasolutions GmbH', + 'Kressenstein 26', '10115', 'Berlin', 'Deutschland', 'Berlin', + 'DE123456789', '12/345/67890', '+49 30 12345670', '+49 30 12345679', + 'info@mustermandant.de', 'support@mustermandant.de', '+49 30 12345671', + 'billing@mustermandant.de', 'https://www.mustermandant.de', + 'https://www.mustermandant.de/datenschutz', 'https://www.mustermandant.de/impressum', + '#218687', 'dark', 1, 'active', NOW() +WHERE NOT EXISTS ( + SELECT 1 FROM tenants WHERE uuid = '57349d01-262e-11f1-8ada-dab0b621b876' +); + +-- ─── Live tenant 2: icoreon GmbH ─── +INSERT INTO `tenants` ( + `uuid`, `description`, `primary_color`, `status`, `status_changed_at`, `created` +) +SELECT + 'ab99e81c-471a-4db0-aa24-c198a5c58557', 'icoreon GmbH', + '#ff8c13', 'active', NOW(), NOW() +WHERE NOT EXISTS ( + SELECT 1 FROM tenants WHERE uuid = 'ab99e81c-471a-4db0-aa24-c198a5c58557' +); + +-- ─── Live departments (breadcrumb tenant) ─── +INSERT INTO `departments` (`uuid`, `description`, `tenant_id`, `code`, `active`, `created`) +SELECT '5734c4f6-262e-11f1-8ada-dab0b621b876', 'Customer Care', t.id, 'CUSTOMERCARE', 1, NOW() +FROM tenants t +WHERE t.uuid = '57349d01-262e-11f1-8ada-dab0b621b876' + AND NOT EXISTS (SELECT 1 FROM departments WHERE uuid = '5734c4f6-262e-11f1-8ada-dab0b621b876'); + +INSERT INTO `departments` (`uuid`, `description`, `tenant_id`, `code`, `active`, `created`) +SELECT '5734d6cd-262e-11f1-8ada-dab0b621b876', 'Customer Success Management', t.id, 'CSM', 1, NOW() +FROM tenants t +WHERE t.uuid = '57349d01-262e-11f1-8ada-dab0b621b876' + AND NOT EXISTS (SELECT 1 FROM departments WHERE uuid = '5734d6cd-262e-11f1-8ada-dab0b621b876'); + +INSERT INTO `departments` (`uuid`, `description`, `tenant_id`, `code`, `active`, `created`) +SELECT 'e2fd85de-2009-4ba8-8cd2-bbe04ae17211', 'Customizing', t.id, 'CUSTI', 1, NOW() +FROM tenants t +WHERE t.uuid = '57349d01-262e-11f1-8ada-dab0b621b876' + AND NOT EXISTS (SELECT 1 FROM departments WHERE uuid = 'e2fd85de-2009-4ba8-8cd2-bbe04ae17211'); + +INSERT INTO `departments` (`uuid`, `description`, `tenant_id`, `code`, `active`, `created`) +SELECT '074fa03d-e74e-4073-879c-9ac8e8abe0ae', 'Management', t.id, 'MANAGEMENT', 1, NOW() +FROM tenants t +WHERE t.uuid = '57349d01-262e-11f1-8ada-dab0b621b876' + AND NOT EXISTS (SELECT 1 FROM departments WHERE uuid = '074fa03d-e74e-4073-879c-9ac8e8abe0ae'); + +INSERT INTO `departments` (`uuid`, `description`, `tenant_id`, `code`, `active`, `created`) +SELECT 'caf24a7d-e210-4b1c-830c-54734c2c879f', 'Marketing', t.id, 'MARKETING', 1, NOW() +FROM tenants t +WHERE t.uuid = '57349d01-262e-11f1-8ada-dab0b621b876' + AND NOT EXISTS (SELECT 1 FROM departments WHERE uuid = 'caf24a7d-e210-4b1c-830c-54734c2c879f'); + +INSERT INTO `departments` (`uuid`, `description`, `tenant_id`, `code`, `active`, `created`) +SELECT 'aaedbb3f-2cea-4f22-9bb2-873d2db28a14', 'Produktentwicklung', t.id, 'PRODUKTENT', 1, NOW() +FROM tenants t +WHERE t.uuid = '57349d01-262e-11f1-8ada-dab0b621b876' + AND NOT EXISTS (SELECT 1 FROM departments WHERE uuid = 'aaedbb3f-2cea-4f22-9bb2-873d2db28a14'); + +INSERT INTO `departments` (`uuid`, `description`, `tenant_id`, `code`, `active`, `created`) +SELECT 'c84ef0ab-16df-4b0f-91a8-5787c661ad21', 'Projects', t.id, 'PROJECTS', 1, NOW() +FROM tenants t +WHERE t.uuid = '57349d01-262e-11f1-8ada-dab0b621b876' + AND NOT EXISTS (SELECT 1 FROM departments WHERE uuid = 'c84ef0ab-16df-4b0f-91a8-5787c661ad21'); + +INSERT INTO `departments` (`uuid`, `description`, `tenant_id`, `code`, `active`, `created`) +SELECT '01845fdb-d21b-43e2-a483-7f8318a096f8', 'Verwaltung', t.id, 'VERWALTUNG', 1, NOW() +FROM tenants t +WHERE t.uuid = '57349d01-262e-11f1-8ada-dab0b621b876' + AND NOT EXISTS (SELECT 1 FROM departments WHERE uuid = '01845fdb-d21b-43e2-a483-7f8318a096f8'); + +-- ─── Live user: Felix Schneider ─── +INSERT INTO `users` ( + `uuid`, `first_name`, `last_name`, `display_name`, `email`, + `job_title`, `phone`, `email_verified_at`, `last_login_provider`, + `password`, `locale`, `theme`, `created`, `active`, `authz_version`, `active_changed_at` +) +SELECT + 'bf88fbcf-b25d-4799-bd52-653d7be5890f', + 'Felix', 'Schneider', 'Felix Schneider', 'fs@breadcrumb-solutions.de', + 'Software Product Developer', '09221 80246-73', + NOW(), 'local', + '$2y$12$EPRwI4HoS.uzyTMlCTyHFeOFcWzcUWdqn7jDCo7syXZdJCyYDs7aO', + 'de', 'dark', NOW(), 1, 1, NOW() +WHERE NOT EXISTS ( + SELECT 1 FROM users WHERE uuid = 'bf88fbcf-b25d-4799-bd52-653d7be5890f' +); + +UPDATE `users` +SET `primary_tenant_id` = (SELECT id FROM tenants WHERE uuid = '57349d01-262e-11f1-8ada-dab0b621b876'), + `current_tenant_id` = (SELECT id FROM tenants WHERE uuid = '57349d01-262e-11f1-8ada-dab0b621b876') +WHERE uuid = 'bf88fbcf-b25d-4799-bd52-653d7be5890f' + AND `primary_tenant_id` IS NULL; + +-- ─── Felix → tenants (breadcrumb + icoreon) ─── +INSERT IGNORE INTO `user_tenants` (`user_id`, `tenant_id`, `created`) +SELECT u.id, t.id, NOW() +FROM users u +CROSS JOIN tenants t +WHERE u.uuid = 'bf88fbcf-b25d-4799-bd52-653d7be5890f' + AND t.uuid IN ( + '57349d01-262e-11f1-8ada-dab0b621b876', + 'ab99e81c-471a-4db0-aa24-c198a5c58557' + ); + +-- ─── Felix → all roles (admin/user/manager/auditor/support) ─── +INSERT IGNORE INTO `user_roles` (`user_id`, `role_id`, `created`) +SELECT u.id, r.id, NOW() +FROM users u +CROSS JOIN roles r +WHERE u.uuid = 'bf88fbcf-b25d-4799-bd52-653d7be5890f' + AND r.code IN ('ADMIN', 'USER', 'MANAGER', 'AUDITOR', 'SUPPORT'); + +-- ─── Felix → Customer Care department ─── +INSERT IGNORE INTO `user_departments` (`user_id`, `department_id`, `created`) +SELECT u.id, d.id, NOW() +FROM users u +CROSS JOIN departments d +WHERE u.uuid = 'bf88fbcf-b25d-4799-bd52-653d7be5890f' + AND d.uuid = '5734c4f6-262e-11f1-8ada-dab0b621b876'; + +-- ─── tenant_auth_microsoft (live SSO config) ─── +INSERT INTO `tenant_auth_microsoft` ( + `tenant_id`, `enabled`, `enforce_microsoft_login`, `sync_profile_on_login`, + `sync_profile_fields`, `entra_tenant_id`, `allowed_domains`, `use_shared_app`, `created` +) +SELECT t.id, 1, 1, 1, + 'first_name,last_name,display_name,job_title,phone,mobile,avatar', + '52e93e91-ea23-42ee-bbd9-77194dc9886b', + 'breadcrumb-solutions.de', 1, NOW() +FROM tenants t +WHERE t.uuid = '57349d01-262e-11f1-8ada-dab0b621b876' + AND NOT EXISTS ( + SELECT 1 FROM tenant_auth_microsoft tm WHERE tm.tenant_id = t.id + ); + +INSERT INTO `tenant_auth_microsoft` ( + `tenant_id`, `enabled`, `enforce_microsoft_login`, `sync_profile_on_login`, `use_shared_app`, `created` +) +SELECT t.id, 0, 0, 0, 0, NOW() +FROM tenants t +WHERE t.uuid = 'ab99e81c-471a-4db0-aa24-c198a5c58557' + AND NOT EXISTS ( + SELECT 1 FROM tenant_auth_microsoft tm WHERE tm.tenant_id = t.id + ); + +-- ─── tenant_auth_ldap (disabled stubs for live tenants) ─── +INSERT INTO `tenant_auth_ldap` ( + `tenant_id`, `enabled`, `enforce_ldap_login`, `host`, `port`, `encryption_mode`, + `verify_tls_certificate`, `base_dn`, `user_search_filter`, `user_search_scope`, + `bind_method`, `unique_id_attribute`, `attribute_map`, `sync_profile_on_login`, + `network_timeout`, `created` +) +SELECT t.id, 0, 0, '', 389, 'starttls', 0, '', + '(&(objectClass=user)(sAMAccountName=%s))', 'sub', 'search_then_bind', + 'objectGUID', + '{"first_name":"givenName","last_name":"sn","email":"mail","phone":"telephoneNumber","mobile":"mobile"}', + 0, 5, NOW() +FROM tenants t +WHERE t.uuid IN ( + '57349d01-262e-11f1-8ada-dab0b621b876', + 'ab99e81c-471a-4db0-aa24-c198a5c58557' + ) + AND NOT EXISTS ( + SELECT 1 FROM tenant_auth_ldap tl WHERE tl.tenant_id = t.id + ); + +-- ─── Global settings (live overrides + Microsoft SSO secrets) ─── +-- Helpdesk-specific settings (helpdesk.bc_*) and helpdesk_tenant_settings rows +-- live in modules/helpdesk/migrations/011_seed_test_tenant_connections.sql +-- so they only seed when the helpdesk module is enabled. +INSERT INTO `settings` (`key`, `value`, `description`) +VALUES + ('app_primary_color', '#105433', 'setting.app_primary_color'), + ('app_theme', 'light', 'setting.app_theme'), + ('app_theme_user', '1', 'setting.app_theme_user'), + ('microsoft_authority', 'https://login.microsoftonline.com/common/v2.0', 'setting.microsoft_authority'), + ('microsoft_shared_client_id', '995297b2-e0f7-4cec-abc5-1e6f9704272f', 'setting.microsoft_shared_client_id'), + ('microsoft_shared_client_secret_enc', 'eyJ2IjoxLCJpdiI6IjloU0ZRb3FWVHZxMUlubVciLCJ0YWciOiJSYzZVbVlCNzZzMi1nOFVZVFYteXd3IiwiY3QiOiJaV1hjQUdGRVk1YjZJRjhZRjlfWlBKeUg5c2JkbjBVOHlSemcwYUc4S01DcFgwa2xnd3pvVEEifQ==', 'setting.microsoft_shared_client_secret_enc') +ON DUPLICATE KEY UPDATE + `value` = VALUES(`value`); diff --git a/modules/helpdesk/migrations/011_seed_test_tenant_connections.sql b/modules/helpdesk/migrations/011_seed_test_tenant_connections.sql new file mode 100644 index 0000000..8c42420 --- /dev/null +++ b/modules/helpdesk/migrations/011_seed_test_tenant_connections.sql @@ -0,0 +1,51 @@ +-- Helpdesk module: seed test BC connections for the live dev tenants. +-- Idempotent: re-running is a no-op. +-- Encrypted secrets (basic_password_enc) are AES-256-GCM blobs produced with +-- APP_CRYPTO_KEY=9792a9ce0def59c034a651f2f6f26f3dcda2f78c1e3513e67be4da06afc24680 +-- (set in .env.example). Decryption fails if the key differs. +-- Tenant lookup uses uuid so this only matches the live dev tenants seeded +-- by core init.sql; on environments without those tenants the inserts are +-- no-ops (FROM-clause yields zero rows). + +-- ─── Per-tenant override: breadcrumb mediasolutions GmbH ─── +INSERT INTO `helpdesk_tenant_settings` ( + `tenant_id`, `override_enabled`, `auth_mode`, `odata_base_url`, `company_name`, + `basic_user`, `basic_password_enc`, `created_at`, `updated_at` +) +SELECT t.id, 1, 'basic', + 'https://bc.icoreon.de:7048/BusinessCentral-NUP/ODataV4', + 'breadcrumb mediasolutions GmbH', 'ICCONNECT', + 'eyJ2IjoxLCJpdiI6IjJYb2NuZENuTnNSaFo4U3kiLCJ0YWciOiJqbHJpcUVkSElXbDBSU1FreFlhSXR3IiwiY3QiOiJlcVVfTXU3SEU2ZVA3aS0wUDBFbUswY3hIdyJ9', + NOW(), NOW() +FROM tenants t +WHERE t.uuid = '57349d01-262e-11f1-8ada-dab0b621b876' + AND NOT EXISTS ( + SELECT 1 FROM helpdesk_tenant_settings hts WHERE hts.tenant_id = t.id + ); + +-- ─── Per-tenant override: icoreon GmbH ─── +INSERT INTO `helpdesk_tenant_settings` ( + `tenant_id`, `override_enabled`, `auth_mode`, `odata_base_url`, `company_name`, + `basic_user`, `basic_password_enc`, `created_at`, `updated_at` +) +SELECT t.id, 1, 'basic', + 'https://bc.icoreon.de:7048/BusinessCentral-NUP/ODataV4', + 'icoreon GmbH', 'ICCONNECT', + 'eyJ2IjoxLCJpdiI6InAxdDJZWnlVT3JOenNPeDEiLCJ0YWciOiI2bW93dTJHTUdwdzVQSXBqVmlFcm93IiwiY3QiOiIwVW1sTGVuSEJDX21OTmRORThBN3pvRWdvdyJ9', + NOW(), NOW() +FROM tenants t +WHERE t.uuid = 'ab99e81c-471a-4db0-aa24-c198a5c58557' + AND NOT EXISTS ( + SELECT 1 FROM helpdesk_tenant_settings hts WHERE hts.tenant_id = t.id + ); + +-- ─── Global fallback (used when override_enabled = 0) ─── +INSERT INTO `settings` (`key`, `value`, `description`) +VALUES + ('helpdesk.bc_auth_mode', 'basic', 'helpdesk.bc_auth_mode'), + ('helpdesk.bc_basic_user', 'ICCONNECT', 'helpdesk.bc_basic_user'), + ('helpdesk.bc_basic_password_enc', 'eyJ2IjoxLCJpdiI6IlhReDVxbHo3Q1dJSVFINkYiLCJ0YWciOiJyaFV1MHBlc1pSS1Vwb0YxTENyWVhnIiwiY3QiOiI0N2NNMWtjZVNlT3QzcGpfd0lqaDROWEJmUSJ9', 'helpdesk.bc_basic_password_enc'), + ('helpdesk.bc_company_name', 'breadcrumb mediasolutions GmbH', 'helpdesk.bc_company_name'), + ('helpdesk.bc_odata_base_url', 'https://bc.icoreon.de:7048/BusinessCentral-NUP/ODataV4', 'helpdesk.bc_odata_base_url') +ON DUPLICATE KEY UPDATE + `value` = VALUES(`value`);