chore(seed): bake live test connections into init.sql + helpdesk migration

Fresh installs now come up with working dev test connections for
Microsoft SSO and Business Central without manual configuration:

- init.sql appends the live breadcrumb + icoreon tenants (idempotent
  by uuid), Felix as a 6th user with all five roles, the live
  departments, plus tenant_auth_microsoft (SSO enabled for
  breadcrumb), tenant_auth_ldap stubs, and the global Microsoft
  shared-app credentials. The MusterMandant demo seed is preserved
  as tenant 1.
- A new helpdesk migration 011 seeds the per-tenant BC connection
  rows in helpdesk_tenant_settings and the helpdesk.bc_* fallback
  settings — module-owned data stays in module migrations.
- .env.example sets APP_CRYPTO_KEY so the AES-256-GCM blobs in the
  seed (Microsoft client secret, BC basic auth passwords) actually
  decrypt out-of-the-box. Verified by decrypting all four blobs
  end-to-end through Crypto::decryptString.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-29 08:27:47 +02:00
parent a5213977ca
commit f36b169f67
3 changed files with 253 additions and 1 deletions

View File

@@ -9,7 +9,7 @@ APP_STORAGE_PATH=./storage
APP_LOCALE=de APP_LOCALE=de
APP_LOCALES=de,en APP_LOCALES=de,en
APP_I18N_DOMAIN=default APP_I18N_DOMAIN=default
APP_CRYPTO_KEY= APP_CRYPTO_KEY=9792a9ce0def59c034a651f2f6f26f3dcda2f78c1e3513e67be4da06afc24680
SESSION_NAME=CoreCore SESSION_NAME=CoreCore
TENANT_SCOPE_STRICT=true TENANT_SCOPE_STRICT=true

View File

@@ -1749,3 +1749,204 @@ CROSS JOIN (
) n ) n
WHERE u.email = 'demo@user.com' WHERE u.email = 'demo@user.com'
AND NOT EXISTS (SELECT 1 FROM user_notifications un WHERE un.recipient_user_id = u.id); 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`);

View File

@@ -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`);