[Workavia] Brand → "Workavia Drive" + multi-tenant Company per email domain
- Replace "Twake Drive" → "Workavia Drive" in static (index.html, manifest.json), all 16 frontend locales, and shared.tsx. - Patch console/clients/remote.ts (OIDC remote) and console/web/controller.ts (local signup) to derive a per-tenant Company from the user's email domain via uuidv5, replacing the upstream all-zero hardcode.
This commit is contained in:
@@ -23,6 +23,32 @@ import { ConsoleServiceImpl } from "../service";
|
||||
import coalesce from "../../../utils/coalesce";
|
||||
import config from "config";
|
||||
import { CompanyUserRole } from "src/services/user/web/types";
|
||||
import { v5 as uuidv5 } from "uuid";
|
||||
|
||||
// [Workavia] Multi-tenant company resolution.
|
||||
//
|
||||
// Upstream tdrive hardcodes every OIDC user into the all-zero Company
|
||||
// UUID. That breaks Workavia's tenant isolation: users from different
|
||||
// customer domains (e.g. meconsult.fr, acme.com) would all land in the
|
||||
// same Company and could see each other's "Shared with me" results,
|
||||
// search each other in the share-with-user picker, etc.
|
||||
//
|
||||
// We derive a deterministic Company UUID per email domain via uuidv5
|
||||
// over a fixed namespace, and use the bare domain as the Company's
|
||||
// human name. First user from a domain bootstraps the Company; every
|
||||
// subsequent user from that domain joins it.
|
||||
//
|
||||
// Namespace UUID is stable & arbitrary — picked once and committed.
|
||||
const WORKAVIA_NAMESPACE = "1b1c8c44-3a5a-4e2c-bfa1-d3e2f1a0c0ff";
|
||||
|
||||
function workaviaCompanyForEmail(email: string): { id: string; name: string } {
|
||||
// Default fallback for malformed emails — keeps behaviour identical
|
||||
// to upstream (one shared Company) for the broken case so we don't
|
||||
// break the system on a typo.
|
||||
const at = email.lastIndexOf("@");
|
||||
const domain = at >= 0 ? email.slice(at + 1).toLowerCase() : "tdrive.local";
|
||||
return { id: uuidv5(domain, WORKAVIA_NAMESPACE), name: domain };
|
||||
}
|
||||
import Session from "../entities/session";
|
||||
export class ConsoleRemoteClient implements ConsoleServiceClient {
|
||||
version: "1";
|
||||
@@ -166,15 +192,16 @@ export class ConsoleRemoteClient implements ConsoleServiceClient {
|
||||
|
||||
await gr.services.users.save(user);
|
||||
|
||||
//For now TDrive works with only one company as we don't get it from the SSO
|
||||
let company = await gr.services.companies.getCompany({
|
||||
id: "00000000-0000-4000-0000-000000000000",
|
||||
});
|
||||
// [Workavia] Resolve the Company from the user's email domain so
|
||||
// each tenant of a Workavia install gets its own isolated Company.
|
||||
// Upstream behaviour was a single shared Company.
|
||||
const tenant = workaviaCompanyForEmail(userDTO.email);
|
||||
let company = await gr.services.companies.getCompany({ id: tenant.id });
|
||||
if (!company) {
|
||||
company = await gr.services.companies.createCompany(
|
||||
getCompanyInstance({
|
||||
id: "00000000-0000-4000-0000-000000000000",
|
||||
name: "Tdrive",
|
||||
id: tenant.id,
|
||||
name: tenant.name,
|
||||
plan: { name: "Local", limits: undefined, features: undefined },
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -23,6 +23,18 @@ import { getInstance as getCompanyInstance } from "../../../services/user/entiti
|
||||
import Workspace from "../../../services/workspaces/entities/workspace";
|
||||
import gr from "../../global-resolver";
|
||||
import { Configuration } from "../../../core/platform/framework";
|
||||
import { v5 as uuidv5 } from "uuid";
|
||||
|
||||
// [Workavia] Same multi-tenant logic as in console/clients/remote.ts —
|
||||
// derive the Company from the email domain instead of grouping every
|
||||
// user into a single Company.
|
||||
const WORKAVIA_NAMESPACE = "1b1c8c44-3a5a-4e2c-bfa1-d3e2f1a0c0ff";
|
||||
|
||||
function workaviaCompanyForEmail(email: string): { id: string; name: string } {
|
||||
const at = email.lastIndexOf("@");
|
||||
const domain = at >= 0 ? email.slice(at + 1).toLowerCase() : "tdrive.local";
|
||||
return { id: uuidv5(domain, WORKAVIA_NAMESPACE), name: domain };
|
||||
}
|
||||
import { CompanyUserRole } from "src/services/user/web/types";
|
||||
import config from "config";
|
||||
|
||||
@@ -96,15 +108,19 @@ export class ConsoleController {
|
||||
const user = await gr.services.users.create(newUser);
|
||||
await gr.services.users.setPassword({ id: user.entity.id }, request.body.password);
|
||||
|
||||
//Create a global company for all users in local mode
|
||||
const companies = await gr.services.companies.getCompanies();
|
||||
let company = companies.getEntities()?.[0];
|
||||
// [Workavia] Resolve the Company from the user's email domain
|
||||
// (same logic as the OIDC/remote path). Each tenant gets its
|
||||
// own isolated Company; first signup of a domain bootstraps it.
|
||||
const tenant = workaviaCompanyForEmail(email);
|
||||
let company = await gr.services.companies.getCompany({ id: tenant.id });
|
||||
if (!company) {
|
||||
const newCompany = getCompanyInstance({
|
||||
name: "Tdrive",
|
||||
plan: { name: "Local", limits: undefined, features: undefined },
|
||||
});
|
||||
company = await gr.services.companies.createCompany(newCompany);
|
||||
company = await gr.services.companies.createCompany(
|
||||
getCompanyInstance({
|
||||
id: tenant.id,
|
||||
name: tenant.name,
|
||||
plan: { name: "Local", limits: undefined, features: undefined },
|
||||
}),
|
||||
);
|
||||
}
|
||||
if (this.rootAdmins.includes(email)) {
|
||||
userRole = "admin";
|
||||
|
||||
Reference in New Issue
Block a user