Gitlab Issue Fr013 Licensing Framework
Problem Statement
Saat ini TessaERP belum memiliki mekanisme licensing yang terstandarisasi untuk membatasi penggunaan aplikasi berdasarkan entitlement pelanggan.
Kondisi saat ini menyebabkan:
- Tidak ada mekanisme resmi untuk membedakan pelanggan Trial, Subscription, maupun Perpetual License
- Tidak ada validasi lisensi yang dapat mencegah modifikasi file lisensi oleh pelanggan
- ERP belum memiliki abstraction layer untuk membaca entitlement pelanggan
- Belum tersedia fondasi untuk feature licensing, user limitation, subscription expiry, maupun module entitlement
- Deployment On-Premise maupun SaaS belum memiliki proses validasi lisensi yang konsisten
Sebagai bagian dari transformasi ERP menjadi platform SaaS dan commercial product, diperlukan fondasi licensing yang sederhana namun extensible.
Goals
Mengimplementasikan fondasi Licensing Framework pada TessaERP dengan pendekatan:
- License file berbasis JSON
- License file ditandatangani menggunakan Private Key
- License Service melakukan verifikasi menggunakan Public Key
- ERP tidak membaca license file secara langsung
- ERP mengakses entitlement melalui abstraction
ILicenseProvider - License generation masih dilakukan secara manual melalui backend/admin tool
- Menyediakan fondasi untuk feature licensing dan subscription validation pada phase berikutnya
Tujuan utama:
- Menjamin integritas license file
- Memisahkan licensing concern dari business module ERP
- Menyediakan extensible license schema untuk kebutuhan masa depan
- Menyiapkan pondasi untuk SaaS maupun On-Premise deployment
Scope of Works
Backend
1. License Domain Model
Implementasikan struktur license payload yang extensible.
{
"licenseId": "LIC-2026-0001",
"tenantId": "TENANT001",
"edition": "Professional",
"subscription": {
"type": "annual",
"expiresAt": "2027-12-31"
},
"features": {
"sales": true,
"purchase": true,
"inventory": true,
"finance": false
},
"limits": {
"users": 50
},
"constraints": {
"machineFingerprint": null
}
}
| Section | Purpose |
|---|---|
licenseId | Unique License Identifier |
tenantId | Tenant / Customer Identifier — diverifikasi terhadap env var TENANT_ID/ENTITY_ID (EID) |
edition | Product Edition |
subscription | Subscription Information |
features | Feature Entitlement |
limits | User / Resource Limitation |
constraints | Future Restriction Support (machineFingerprint: Phase 2) |
2. Cryptographic Signing Framework
Implementasi mekanisme signing menggunakan asymmetric cryptography.
Portal / License Generator memiliki: Private Key
License Service memiliki: Public Key
License Payload
↓ Sign using Private Key
↓ license.lic
↓ Verify using Public Key
Requirements:
- Gunakan RSA minimal 2048-bit atau ekuivalen
- Signature disimpan bersama payload
- Verifikasi dilakukan sebelum license digunakan
- Tidak boleh menggunakan shared secret / symmetric key
3. License File Format
{
"payload": { ... },
"signature": "BASE64_SIGNATURE"
}
Validation Rules:
License dianggap valid jika:
- Payload dapat dibaca
- Signature valid
- License belum expired
tenantIdcocok dengan env varTENANT_ID/ENTITY_ID (EID)
License dianggap invalid jika:
- Payload dimodifikasi
- Signature tidak valid
- Signature tidak ditemukan
- Format file tidak sesuai
tenantIdtidak cocok dengan env varTENANT_ID/ENTITY_ID (EID)
4. License Verification Service
public interface ILicenseVerificationService
{
LicenseValidationResult Validate();
}
public class LicenseValidationResult
{
public bool IsValid { get; set; }
public string Reason { get; set; }
public DateTime? ExpirationDate { get; set; }
}
Verification Process pada startup:
Load license.lic
↓ Verify Signature
↓ Validate tenantId vs TENANT_ID/ENTITY_ID (EID) env var
↓ Validate Expiry
↓ Cache License Information
Error Reason Messages:
| Condition | Reason |
|---|---|
| File not found | "License file not found. Please upload a valid license." |
| Signature invalid | "License file is corrupted or has been tampered. Request a new license from your vendor." |
| Tenant mismatch | "This license was issued for a different tenant. Contact your vendor." |
| Expired | "Your license expired on {date}. Please renew your subscription." |
5. License Provider Abstraction
ERP tidak boleh membaca license file secara langsung.
public interface ILicenseProvider
{
bool IsValid();
bool HasFeature(string featureCode);
int GetLimit(string limitCode);
LicenseInfo GetLicenseInfo();
}
Example Usage:
if (_licenseProvider.HasFeature("finance"))
{
ShowFinanceMenu();
}
ERP module hanya menggunakan interface tersebut. ERP tidak mengetahui cara license dibuat, ditandatangani, lokasi file, maupun mekanisme verifikasi.
6. License Bootstrap Validation
Pada startup:
Application Start
↓ Validate License
↓ License Valid?
YES → Continue Startup
NO → Show License Error with Go To Upload License View/Page
Jika lisensi tidak valid:
- Redirect semua request ke
/license(upload page) - Simpan log error
- Blok akses ERP
Implementasi interceptor di middleware level (bukan per-page):
app.Use(async (context, next) =>
{
var license = context.RequestServices.GetRequiredService<ILicenseProvider>();
if (!license.IsValid() && !context.Request.Path.StartsWithSegments("/license"))
{
context.Response.Redirect("/license");
return;
}
await next();
});
7. License Upload Page (Invalid License UI)
Saat license invalid, tampilkan halaman upload.
Upload Flow:
User uploads license.lic
↓ Parse file
↓ Verify signature (public key)
↓ Validate tenantId vs TENANT_ID/ENTITY_ID (EID) env var
↓ Validate expiry
↓ All valid?
YES → Save license.lic → Show restart instruction
NO → Show specific error reason
Post-upload Success Message:
✅ License file uploaded successfully.
Please restart the application server to apply the new license
8. Manual License Generator
Implementasikan utility/backend tool untuk generate license secara manual.
Input:
| Field | Mandatory |
|---|---|
| Tenant ID | Yes |
| Edition | Yes |
| Subscription Type | Yes |
| Expiry Date | Yes |
| Features | No |
| Limits | No |
Output: license.lic
Generate Payload
↓ Sign using Private Key
↓ Export license.lic
File kemudian dikirim secara manual ke customer atau deployment environment.
9. Configuration
| Key | Description |
|---|---|
Licensing:LicenseFilePath | Lokasi file license |
Licensing:PublicKeyPath | Lokasi public key |
Licensing:LicenseValidationEnabled | Enable / Disable Validation |
{
"Licensing": {
"LicenseFilePath": "./license/license.lic",
"PublicKeyPath": "./license/public.pem",
"LicenseValidationEnabled": true
}
}
Business Rules
- Setiap ERP instance hanya menggunakan satu file license aktif
- License wajib diverifikasi saat startup
tenantIdpada license wajib cocok dengan env varTENANT_ID/ENTITY_ID (EID)- ERP tidak boleh membaca payload secara langsung tanpa melalui License Provider
- Signature verification wajib berhasil sebelum entitlement digunakan
- License yang expired dianggap invalid
- Public Key hanya digunakan untuk verification
- Private Key tidak boleh tersedia pada deployment ERP customer
Out of Scope (Phase 1)
- Online Activation
- Machine Fingerprint Enforcement (reserved in schema, enforced in Phase 2)
- Hot Reload License (penggantian license memerlukan restart)
- Grace Period for Expired License
- Concurrent User Enforcement
- Feature Menu Hiding based on entitlement
- Subscription Auto Renewal
- License Revocation Server
- Cloud License Validation
- License Management Portal UI
- Automatic License Distribution
- Per User Licensing
- Per Branch Licensing
- Usage Based Licensing
- License Service as Separate Microservice
Acceptance Criteria
- License payload dapat di-generate secara manual
- License dapat ditandatangani menggunakan Private Key
- License dapat diverifikasi menggunakan Public Key
- Modifikasi payload menyebabkan verifikasi gagal
- Pesan error menampilkan reason spesifik (expired / signature invalid / tenant mismatch / file missing)
- License dengan
tenantIdberbeda ditolak - License expiry tervalidasi dengan benar
- Halaman upload license ditampilkan ketika license invalid
- Terdapat validasi license file pada View upload license (expired / signature invalid / tenant mismatch)
- Setelah upload berhasil, instruksi restart server aplikasi ditampilkan
- Private Key tidak tersedia pada environment customer
- Public Key digunakan hanya untuk verification
- Dapat memvalidasi feature licensing dan limit enforcement (hanya tampilkan feature/menu module yang eligible)
- Dapat memvalidasi limit enforcement (tidak bisa membuat user lebih dari limit)
References
- Multitenancy Roadmap
- Licensing Service Architecture
- GitLab Branch — fr013
- Licensing Framework Dev Doc