addArgument('domain', InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'Domain(s) served by this tenant') ->addOption('label', 'l', InputOption::VALUE_REQUIRED, 'Display label for the tenant') ->addOption('description', 'd', InputOption::VALUE_REQUIRED, 'Tenant description') ->addOption('identifier', null, InputOption::VALUE_REQUIRED, 'Explicit tenant identifier (defaults to a generated UUID)') ->addOption('disabled', null, InputOption::VALUE_NONE, 'Create the tenant in a disabled state') ->addOption('admin-identity', null, InputOption::VALUE_REQUIRED, 'Identity for the bootstrap admin user', 'admin') ->addOption('no-admin-user', null, InputOption::VALUE_NONE, 'Do not create a bootstrap admin user (the admin role is still seeded)') ->setHelp('This command creates a new tenant with one or more domains. The tenant identifier is generated automatically unless --identifier is provided. An "admin" role with full permissions is seeded automatically, along with a bootstrap admin user unless --no-admin-user is passed.') ; } protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $identifier = $input->getOption('identifier') ?? UUID::v4(); $domains = array_values(array_unique(array_filter(array_map('trim', $input->getArgument('domain'))))); $io->title('Create Tenant'); try { if (empty($domains)) { $io->error('At least one non-empty domain is required.'); return Command::FAILURE; } // Ensure identifier is unique if ($this->tenantService->fetchById($identifier)) { $io->error("Tenant '{$identifier}' already exists."); return Command::FAILURE; } // Ensure domains are not claimed by another tenant foreach ($domains as $domain) { $existing = $this->tenantService->fetchByDomain($domain); if ($existing) { $io->error("Domain '{$domain}' is already assigned to tenant '{$existing->getIdentifier()}'."); return Command::FAILURE; } } $tenant = new TenantObject(); $tenant->setIdentifier($identifier); $tenant->setEnabled(!$input->getOption('disabled')); $tenant->setLabel($input->getOption('label') ?? $domains[0]); $tenant->setDescription($input->getOption('description') ?? ''); $tenant->setDomains(new DomainCollection($domains)); $tenant->setConfiguration(new TenantConfiguration()); $tenant = $this->tenantService->deposit($tenant); if (!$tenant) { $io->error('Failed to create tenant.'); return Command::FAILURE; } $this->logger->info('Tenant created via console', [ 'identifier' => $identifier, 'command' => $this->getName(), ]); $io->success("Tenant '{$identifier}' created successfully!"); $io->definitionList( ['Id' => $tenant->getId()], ['Identifier' => $tenant->getIdentifier()], ['Label' => $tenant->getLabel()], ['Enabled' => $tenant->getEnabled() ? 'yes' : 'no'], ['Domains' => implode(', ', $domains)], ); $role = $this->rolesStore->createRole($identifier, [ 'rid' => 'admin', 'label' => 'Administrator', 'description' => 'Full access to all tenant features', 'permissions' => ['*'], 'system' => true, ]); $this->logger->info('Default admin role seeded via console', [ 'tenant' => $identifier, 'rid' => $role['rid'] ?? null, 'command' => $this->getName(), ]); $io->text("Default role 'admin' seeded with full permissions."); if (!$input->getOption('no-admin-user')) { $adminIdentity = $input->getOption('admin-identity'); if ($this->userStore->fetchByIdentity($identifier, $adminIdentity)) { $io->warning("User '{$adminIdentity}' already exists in tenant '{$identifier}'; skipping admin user creation."); } else { $this->userStore->createUser($identifier, [ 'identity' => $adminIdentity, 'label' => 'Administrator', 'enabled' => true, 'roles' => ['admin'], 'profile' => [], 'settings' => [], 'provider' => null, 'provider_subject' => null, 'provider_managed_fields' => [], ]); $this->logger->info('Bootstrap admin user created via console', [ 'tenant' => $identifier, 'identity' => $adminIdentity, 'command' => $this->getName(), ]); $io->text("Admin user '{$adminIdentity}' created with the 'admin' role."); $io->note("Set a credential for this account using the auth provider module you have installed, e.g.: php bin/console user:password {$identifier} {$adminIdentity}"); } } return Command::SUCCESS; } catch (\Throwable $e) { $io->error('Failed to create tenant: ' . $e->getMessage()); $this->logger->error('Tenant create failed', [ 'identifier' => $identifier, 'error' => $e->getMessage(), ]); return Command::FAILURE; } } }