86 lines
3.7 KiB
Java
86 lines
3.7 KiB
Java
package net.siegeln.cameleer.saas.onboarding;
|
|
|
|
import net.siegeln.cameleer.saas.account.AccountService;
|
|
import net.siegeln.cameleer.saas.identity.LogtoManagementClient;
|
|
import net.siegeln.cameleer.saas.tenant.TenantEntity;
|
|
import net.siegeln.cameleer.saas.tenant.dto.CreateTenantRequest;
|
|
import net.siegeln.cameleer.saas.vendor.VendorTenantService;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
import org.springframework.stereotype.Service;
|
|
|
|
import java.util.UUID;
|
|
|
|
/**
|
|
* Self-service onboarding: lets a newly registered user create their own trial tenant.
|
|
* Reuses VendorTenantService for the heavy lifting (Logto org, license, Docker provisioning)
|
|
* but adds the calling user as the tenant owner instead of creating a new admin user.
|
|
*/
|
|
@Service
|
|
public class OnboardingService {
|
|
|
|
private static final Logger log = LoggerFactory.getLogger(OnboardingService.class);
|
|
|
|
private final VendorTenantService vendorTenantService;
|
|
private final LogtoManagementClient logtoClient;
|
|
private final AccountService accountService;
|
|
|
|
public OnboardingService(VendorTenantService vendorTenantService,
|
|
LogtoManagementClient logtoClient,
|
|
AccountService accountService) {
|
|
this.vendorTenantService = vendorTenantService;
|
|
this.logtoClient = logtoClient;
|
|
this.accountService = accountService;
|
|
}
|
|
|
|
public TenantEntity createTrialTenant(String name, String slug, String logtoUserId) {
|
|
// Guard: check if user already has a tenant (prevent abuse)
|
|
if (logtoClient.isAvailable()) {
|
|
var orgs = logtoClient.getUserOrganizations(logtoUserId);
|
|
if (!orgs.isEmpty()) {
|
|
throw new IllegalStateException("You already have a tenant. Only one trial tenant per account.");
|
|
}
|
|
}
|
|
|
|
// Create tenant via the existing vendor flow (no admin user — we'll add the caller)
|
|
UUID actorId = resolveActorId(logtoUserId);
|
|
var request = new CreateTenantRequest(name, slug, "STARTER", null, null);
|
|
TenantEntity tenant = vendorTenantService.createAndProvision(request, actorId);
|
|
|
|
// Add the calling user to the Logto org as owner
|
|
if (tenant.getLogtoOrgId() != null && logtoClient.isAvailable()) {
|
|
try {
|
|
String ownerRoleId = logtoClient.findOrgRoleIdByName("owner");
|
|
logtoClient.addUserToOrganization(tenant.getLogtoOrgId(), logtoUserId);
|
|
if (ownerRoleId != null) {
|
|
logtoClient.assignOrganizationRole(tenant.getLogtoOrgId(), logtoUserId, ownerRoleId);
|
|
}
|
|
log.info("Added user {} as owner of tenant {}", logtoUserId, slug);
|
|
|
|
// Set display name from email if not already set (email-registered users have no name)
|
|
var profile = accountService.getProfile(logtoUserId);
|
|
if (profile.name() == null || profile.name().isBlank()) {
|
|
String email = profile.email();
|
|
if (!email.isBlank() && email.contains("@")) {
|
|
String displayName = email.substring(0, email.indexOf('@'));
|
|
accountService.updateDisplayName(logtoUserId, displayName);
|
|
log.info("Set display name '{}' for user {}", displayName, logtoUserId);
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
log.warn("Failed to add user {} to org for tenant {}: {}", logtoUserId, slug, e.getMessage());
|
|
}
|
|
}
|
|
|
|
return tenant;
|
|
}
|
|
|
|
private UUID resolveActorId(String subject) {
|
|
try {
|
|
return UUID.fromString(subject);
|
|
} catch (IllegalArgumentException e) {
|
|
return UUID.nameUUIDFromBytes(subject.getBytes());
|
|
}
|
|
}
|
|
}
|