Multi-Tenancy
صُمّم Ithbat لتطبيقات SaaS متعددة الـ Tenants وهوية المؤسسات. كل Tenant هو نطاق هوية معزول تمامًا -- بمستخدميه وأدواره ومجموعاته وإعداداته وهويته البصرية وسجلات التدقيق الخاصة به. لا تُشارَك سجلات المستخدمين أو الـ Session Tokens بين الـ Tenants.
نظرة عامة على البنية
graph TD
subgraph Global Platform
PlatformAdmin[Platform Admin]
BillingService[Billing Service]
end
subgraph Tenant A
UsersA[Users]
RolesA[Roles & Permissions]
BrandingA[Custom Branding]
SAMLA[SAML Config]
AuditA[Audit Logs]
end
subgraph Tenant B
UsersB[Users]
RolesB[Roles & Permissions]
BrandingB[Custom Branding]
SAMLB[SAML Config]
AuditB[Audit Logs]
end
PlatformAdmin --> |manages| TenantA
PlatformAdmin --> |manages| TenantB
BillingService --> TenantA
BillingService --> TenantB
عزل البيانات
يستخدم Ithbat Row-Level Security (RLS) في PostgreSQL لفرض عزل الـ Tenants على مستوى قاعدة البيانات. يُعيَّن الـ Tenant ID الحالي في الـ PostgreSQL Session عبر (SET LOCAL app.current_tenant_id = '...') قبل كل Query، وتضمن سياسات الـ RLS أن الـ Queries ترجع فقط صفوف الـ Tenant المعني. حتى لو سمح خطأ في التطبيق بـ Cross-Tenant Query، تفرض طبقة قاعدة البيانات العزل.
عملاء خطة Enterprise يحصلون على Database Instance منفصل بالكامل.
إنشاء Tenant
Self-Service Registration
POST /api/v1/tenants/register
Content-Type: application/json
{
"name": "Acme Corp",
"slug": "acme",
"adminEmail": "[email protected]",
"adminFirstName": "Omar",
"adminLastName": "Hassan",
"plan": "growth",
"region": "ksa"
}
الـ slug يصبح الـ Subdomain للـ Tenant: acme.ithbat.io. يجب أن يكون فريدًا، lowercase، حروف وأرقام وشرطات فقط.
بعد التسجيل، يتلقى المسؤول بريد دعوة لإعداد حسابه.
إنشاء Tenant عبر الـ Platform API
يمكن لمسؤولي المنصة إنشاء Tenants مباشرة:
POST /api/v1/tenants
Authorization: Bearer {platform_admin_token}
Content-Type: application/json
{
"name": "Acme Corp",
"slug": "acme",
"plan": "enterprise",
"region": "ksa"
}
Subdomain Routing
يحصل كل Tenant على Subdomain بصيغة {slug}.ithbat.io. صفحات المصادقة المُستضافة (تسجيل الدخول، التسجيل، MFA، إعادة تعيين كلمة المرور) تُقدَّم على هذا الـ Subdomain مع الهوية البصرية الخاصة بالـ Tenant.
| URL | الوظيفة |
|---|---|
acme.ithbat.io | صفحة تسجيل دخول الـ Tenant |
acme.ithbat.io/register | التسجيل الذاتي |
acme.ithbat.io/forgot-password | إعادة تعيين كلمة المرور |
يحدد الـ Subdomain سياق الـ Tenant تلقائيًا -- لا حاجة لتمرير Tenant ID في الـ URL لصفحات المصادقة المُستضافة.
Custom Domains
عملاء خطتي Growth وEnterprise يمكنهم استخدام نطاقهم الخاص لواجهة المصادقة المُستضافة.
الخطوة 1 -- إضافة Custom Domain
POST /api/v1/tenant/custom-domain
Authorization: Bearer {token}
X-Tenant-ID: {tenant_id}
Content-Type: application/json
{
"domain": "auth.acme.com"
}
يتضمن الـ Response سجل DNS Verification:
{
"domain": "auth.acme.com",
"verificationToken": "ithbat-verify=abc123xyz",
"verificationRecord": {
"type": "TXT",
"name": "_ithbat-challenge.auth.acme.com",
"value": "abc123xyz"
},
"status": "pending"
}
الخطوة 2 -- إضافة DNS Records
عند الـ DNS Provider، أضف:
| النوع | الاسم | القيمة |
|---|---|---|
TXT | _ithbat-challenge.auth.acme.com | abc123xyz |
CNAME | auth.acme.com | acme.ithbat.io |
الـ DNS Propagation عادة يستغرق 5 إلى 30 دقيقة.
الخطوة 3 -- التحقق من النطاق
POST /api/v1/tenant/custom-domain/verify
Authorization: Bearer {token}
X-Tenant-ID: {tenant_id}
يتحقق Ithbat من الـ TXT Record. عند النجاح، يُفعَّل النطاق وتُصدر شهادة TLS تلقائيًا.
جلب حالة الـ Custom Domain
GET /api/v1/tenant/custom-domain
Authorization: Bearer {token}
X-Tenant-ID: {tenant_id}
إزالة الـ Custom Domain
DELETE /api/v1/tenant/custom-domain
Authorization: Bearer {token}
X-Tenant-ID: {tenant_id}
Tenant Branding
خصّص واجهة الـ Tenant المُستضافة بشعارك وألوانك وFavicon.
PATCH /api/v1/tenant/settings/branding
Authorization: Bearer {token}
X-Tenant-ID: {tenant_id}
Content-Type: application/json
{
"logoUrl": "https://cdn.acme.com/logo.svg",
"faviconUrl": "https://cdn.acme.com/favicon.ico",
"primaryColor": "#0B7285",
"accentColor": "#C8943F",
"backgroundColor": "#F7FAFB",
"companyName": "Acme Corp"
}
تُطبَّق الهوية البصرية فورًا على صفحات المصادقة المُستضافة.
Data Residency
يوفر Ithbat ثلاث مناطق نشر. عند إنشاء Tenant، يحدد حقل region مكان تخزين ومعالجة البيانات. لا تعبر البيانات حدود المناطق.
| رمز المنطقة | الموقع | البنية التحتية |
|---|---|---|
global | الاتحاد الأوروبي (فرانكفورت) | CranL Global |
ksa | الرياض، السعودية | CranL KSA -- البيانات تبقى داخل المملكة |
egypt | القاهرة، مصر | CranL Egypt |
المناطق المتاحة:
GET /api/v1/regions
[
{ "code": "global", "name": "Global (EU)", "available": true },
{ "code": "ksa", "name": "Saudi Arabia (KSA)", "available": true },
{ "code": "egypt", "name": "Egypt", "available": true }
]
منطقتا السعودية ومصر تعملان على بنية CranL التحتية وتلبيان متطلبات Data Residency المحلية. للعملاء الخاضعين لـ PDPL (السعودية) أو قوانين حماية البيانات المصرية، استخدم منطقة ksa أو egypt.
إعدادات الـ Tenant
جلب الإعدادات
GET /api/v1/tenant/settings
Authorization: Bearer {token}
X-Tenant-ID: {tenant_id}
تحديث الإعدادات العامة
PATCH /api/v1/tenant/settings/general
Authorization: Bearer {token}
X-Tenant-ID: {tenant_id}
Content-Type: application/json
{
"name": "Acme Corp",
"defaultLanguage": "en",
"allowRegistration": true,
"requireEmailVerification": true,
"sessionTimeout": 3600
}
تحديث الإعدادات الأمنية
PATCH /api/v1/tenant/settings/security
Authorization: Bearer {token}
X-Tenant-ID: {tenant_id}
Content-Type: application/json
{
"requireMfa": false,
"allowedAuthMethods": ["password", "saml", "google"],
"ipWhitelistEnabled": false
}
المؤسسات داخل الـ Tenant
المؤسسات هي وحدات فرعية داخل الـ Tenant -- أقسام أو عملاء أو فروع إقليمية. يمكن أن يضم الـ Tenant مؤسسات متعددة، ويمكن للمستخدمين الانتماء لمؤسسات متعددة بأدوار مختلفة.
إنشاء مؤسسة
POST /api/v1/organizations
Authorization: Bearer {token}
X-Tenant-ID: {tenant_id}
Content-Type: application/json
{
"name": "Engineering",
"description": "Product engineering team"
}
إضافة مستخدم لمؤسسة
POST /api/v1/organizations/{orgId}/members
Authorization: Bearer {token}
Content-Type: application/json
{
"userId": "3fa85f64-...",
"roles": ["role-id-1"]
}
سرد المؤسسات
GET /api/v1/organizations
Authorization: Bearer {token}
X-Tenant-ID: {tenant_id}
Tenant Lifecycle
إيقاف Tenant
يمنع الإيقاف جميع عمليات المصادقة لمستخدمي الـ Tenant. البيانات تُحفظ كما هي.
POST /api/v1/tenants/{id}/suspend
Authorization: Bearer {platform_admin_token}
إعادة تفعيل Tenant
POST /api/v1/tenants/{id}/reactivate
Authorization: Bearer {platform_admin_token}
تحديث Tenant Metadata
أرفق Metadata بصيغة Key-Value بالـ Tenant:
PATCH /api/v1/tenants/{id}/metadata
Authorization: Bearer {platform_admin_token}
Content-Type: application/json
{
"salesforceId": "0015000000XYZ",
"contractEndDate": "2027-12-31"
}
حذف Tenant
الحذف نهائي ولا رجعة فيه. جميع المستخدمين والـ Sessions وسجلات التدقيق والإعدادات تُحذف نهائيًا.
DELETE /api/v1/tenants/{id}
Authorization: Bearer {platform_admin_token}
Tenant API Reference
| Method | Endpoint | الصلاحية |
|---|---|---|
POST | /api/v1/tenants/register | عام |
GET | /api/v1/tenants/current | tenant:read |
GET | /api/v1/tenants/{id}/stats | tenant:read |
GET | /api/v1/tenants | tenant:write |
POST | /api/v1/tenants | Platform Admin |
PUT | /api/v1/tenants/{id} | tenant:write |
DELETE | /api/v1/tenants/{id} | Platform Admin |
POST | /api/v1/tenants/{id}/suspend | Platform Admin |
POST | /api/v1/tenants/{id}/reactivate | Platform Admin |
PATCH | /api/v1/tenants/{id}/metadata | tenant:write |
GET | /api/v1/tenant/settings | settings:read |
PATCH | /api/v1/tenant/settings/general | settings:write |
PATCH | /api/v1/tenant/settings/branding | settings:write |
PATCH | /api/v1/tenant/settings/security | settings:write |
POST | /api/v1/tenant/custom-domain | settings:write |
POST | /api/v1/tenant/custom-domain/verify | settings:write |
DELETE | /api/v1/tenant/custom-domain | settings:write |
الخطوات التالية
- RBAC والصلاحيات -- إعداد الأدوار والصلاحيات داخل الـ Tenant
- SAML 2.0 -- إعداد SSO للمؤسسات
- الفوترة -- إدارة اشتراك وخطة الـ Tenant