12 KiB
🎯 Multi-Client Access Implementation Summary
Tanggal: 30 September 2025
📊 Executive Summary
Sistem telah diupgrade dari single-client tenant isolation menjadi hierarchical multi-tenancy dengan fitur:
✅ Parent-Child Client Hierarchy - Client bisa memiliki sub-clients (unlimited depth)
✅ Multi-Client User Access - User bisa mengakses multiple clients sekaligus
✅ Super Admin Support - Platform admin dengan akses ke semua clients
✅ Granular Access Control - Read, Write, Admin, Owner levels
✅ Backward Compatible - Kode lama tetap berfungsi
🎯 Problem yang Dipecahkan
Before (Kondisi Lama)
❌ 1 User = 1 Client saja (rigid)
❌ Tidak ada hierarchy client
❌ Tidak ada super admin
❌ Sulit monitor multiple clients
After (Kondisi Baru)
✅ 1 User = Multiple Clients (flexible)
✅ Parent Client → Sub Clients (unlimited depth)
✅ Super Admin bisa akses semua
✅ Manager bisa monitor beberapa client sekaligus
📦 File-File yang Dibuat/Dimodifikasi
✨ Entity Changes
1. app/database/entity/clients.go (MODIFIED)
+ ClientType // 'parent_client', 'sub_client', 'standalone'
+ ParentClientId // UUID reference to parent
+ ParentClient // Relationship
+ SubClients // Array of children
+ Settings // JSONB custom config
+ MaxUsers // Limit untuk sub-clients
+ MaxStorage // Storage limit
2. app/database/entity/users.entity.go (MODIFIED)
+ IsSuperAdmin // Platform super admin flag
+ ClientAccesses // Many-to-Many relationship
3. app/database/entity/user_client_access.entity.go (NEW ⭐)
// Many-to-Many table untuk User ↔ Client access
- UserId
- ClientId
- AccessType (read/write/admin/owner)
- CanManage
- CanDelegate
- IncludeSubClients (auto-access sub-clients)
🔧 Middleware & Utilities
4. app/middleware/client_v2.middleware.go (NEW ⭐)
Enhanced middleware dengan support:
- Super admin detection
- Multi-client access retrieval
- Current client context
- Backward compatible dengan X-Client-Key header
5. utils/client/client_hierarchy.go (NEW ⭐)
Helper functions:
GetAccessibleClientIDs()- Get all accessible clientsGetSubClientIDs()- Recursive sub-client retrievalHasAccessToClient()- Access validationGetClientHierarchy()- Parent-child structureIsParentClient()- Check if has children
6. utils/middleware/client_utils_v2.go (NEW ⭐)
Query utilities:
AddMultiClientFilter()- Auto filter by accessible clientsSetCurrentClientID()- Set client on createValidateMultiClientAccess()- Resource access checkFilterByCurrentClient()- Filter by active client only
📚 Documentation
7. docs/MULTI_CLIENT_ACCESS_GUIDE.md (NEW ⭐)
Comprehensive guide:
- Architecture overview
- Database schema
- Migration steps
- Code examples
- Use cases
- Troubleshooting
8. docs/migrations/001_add_multi_client_support.sql (NEW ⭐)
SQL migration script:
- ALTER TABLE statements
- CREATE TABLE user_client_access
- Indexes & constraints
- Helper functions (PostgreSQL)
- Views for reporting
- Rollback script
9. docs/examples/articles_controller_example.go (NEW ⭐)
Real-world examples:
- GetAll dengan multi-client filter
- GetOne dengan access validation
- Create dengan target client selection
- Super admin dashboard
- Grant access endpoint
- Client hierarchy endpoints
🗄️ Database Migration
10. app/database/index.database.go (MODIFIED)
+ entity.UserClientAccess{} // Added to Models()
🚀 How to Deploy
Step 1: Backup Database
pg_dump netidhub_db > backup_before_multi_client.sql
Step 2: Run Application (Auto-migrate)
# Pastikan config.toml: migrate = true
go run main.go
GORM AutoMigrate akan otomatis:
- Tambah columns di
clientstable - Tambah column di
userstable - Create
user_client_accesstable
Step 3: Run SQL Migration (Optional)
# Untuk functions, views, dan indexes tambahan
psql -U netidhub_user -d netidhub_db -f docs/migrations/001_add_multi_client_support.sql
Step 4: Migrate Data
-- Set existing clients sebagai 'standalone'
UPDATE clients SET client_type = 'standalone';
-- Create super admin
UPDATE users SET is_super_admin = true WHERE id = 1;
Step 5: Update Code (Bertahap)
Option A: Full Migration
// Di config/webserver/webserver.config.go
app.Use(middleware.ClientMiddlewareV2(db.DB))
Option B: Gradual Migration
// Keep old middleware
app.Use(middleware.ClientMiddleware(db.DB))
// New routes use V2
apiV2 := app.Group("/api/v2")
apiV2.Use(middleware.ClientMiddlewareV2(db.DB))
💻 Usage Examples
Example 1: Setup Parent-Child Clients
// Parent Client (Polda Metro)
parentClient := entity.Clients{
ID: uuid.New(),
Name: "Polda Metro Jaya",
ClientType: "parent_client",
}
db.Create(&parentClient)
// Sub Client (Polres)
subClient := entity.Clients{
ID: uuid.New(),
Name: "Polres Jakarta Pusat",
ClientType: "sub_client",
ParentClientId: &parentClient.ID,
}
db.Create(&subClient)
Example 2: Grant Multi-Client Access
// Manager bisa akses 3 Polres
access := entity.UserClientAccess{
UserId: managerUserId,
ClientId: parentClientId,
AccessType: "admin",
IncludeSubClients: &trueVal, // Auto-access semua sub-clients
CanManage: &trueVal,
}
db.Create(&access)
Example 3: Repository dengan Multi-Client Filter
func (r *ArticleRepository) GetAll(c *fiber.Ctx) ([]entity.Articles, error) {
query := r.DB.Model(&entity.Articles{})
// One line - auto filter by accessible clients!
query = middlewareUtils.AddMultiClientFilter(query, c)
var articles []entity.Articles
query.Find(&articles)
return articles, nil
}
Example 4: Check Access
func (ctrl *Controller) GetArticle(c *fiber.Ctx) error {
userId := c.Locals(customMiddleware.UserIDContextKey).(uint)
isSuperAdmin := customMiddleware.IsSuperAdmin(c)
hasAccess, _ := clientUtils.HasAccessToClient(
db, userId, article.ClientId, isSuperAdmin,
)
if !hasAccess {
return c.Status(403).JSON(fiber.Map{"error": "Access denied"})
}
}
🎨 Use Case Scenarios
Scenario 1: Regional Manager
Kebutuhan: Manager regional monitor 5 Polres
Solusi:
// Grant access ke parent dengan IncludeSubClients
UserClientAccess{
UserId: regionalManagerId,
ClientId: parentPolresId,
IncludeSubClients: true, // ✅ Auto-access semua sub-Polres
}
Scenario 2: Super Admin Dashboard
Kebutuhan: Admin platform lihat semua data
Solusi:
// Set is_super_admin = true
users.IsSuperAdmin = true
// Di controller
if customMiddleware.IsSuperAdmin(c) {
// No filtering - get ALL data
}
Scenario 3: Multi-Organization User
Kebutuhan: User kerja di 2 organisasi berbeda
Solusi:
// Grant access ke 2 clients
UserClientAccess{ UserId: 123, ClientId: orgA }
UserClientAccess{ UserId: 123, ClientId: orgB }
// User bisa switch via header: X-Client-Key
⚠️ Breaking Changes
NONE! 🎉
Implementasi ini BACKWARD COMPATIBLE:
✅ Existing ClientId di Users tetap berfungsi
✅ Middleware lama (ClientMiddleware) masih bisa dipakai
✅ Repository dengan single-client filter masih work
✅ Header X-Client-Key masih didukung
Migration bisa dilakukan bertahap per module.
🔒 Security Considerations
- ✅ Super admin flag protected - Cannot be set via API
- ✅ Access validation - Always check before showing data
- ✅ Delegation control - Only users with
CanDelegatecan grant access - ✅ Audit trail - Track who granted access (GrantedById)
- ✅ Cascade rules - Proper foreign key constraints
📈 Performance Impact
Optimizations Implemented:
- ✅ Database indexes on
parent_client_id,user_id,client_id - ✅ Unique constraint on
(user_id, client_id) - ✅ Efficient recursive queries dengan PostgreSQL CTEs
- ✅ Preload relationships untuk avoid N+1 queries
Recommendations:
- 🔸 Cache accessible client IDs di Redis (per user)
- 🔸 Pagination untuk multi-client data
- 🔸 Database read replicas untuk super admin queries
🧪 Testing Checklist
- Create parent client
- Create sub-client under parent
- Grant multi-client access to user
- Test
IncludeSubClients= true - Test super admin access all clients
- Test single-client user (backward compat)
- Test access validation on resources
- Test client hierarchy retrieval
- Test switching between clients
- Load test multi-client queries
📞 Next Steps
Recommended Actions:
-
Test di Development Environment
# Run migration go run main.go # Verify tables psql -c "\d user_client_access" -
Create Sample Data
-- Parent client INSERT INTO clients (id, name, client_type) VALUES (uuid_generate_v4(), 'Parent', 'parent_client'); -- Super admin UPDATE users SET is_super_admin = true WHERE id = 1; -
Update 1-2 Modules sebagai Pilot
- Update Articles module dulu
- Test thoroughly
- Rollout to other modules
-
Monitor Performance
- Check query performance
- Monitor database load
- Optimize if needed
-
User Training
- Explain new multi-client concept
- Show how to switch clients
- Train admin on granting access
📊 Architecture Diagram
┌─────────────────────────────────────────────┐
│ SUPER ADMIN (Platform) │
│ Access: ALL clients, ALL data │
└──────────────────┬──────────────────────────┘
│
┌──────────┴──────────┬──────────┐
│ │ │
┌───────▼────────┐ ┌────────▼─────┐ ┌─▼────────┐
│ PARENT CLIENT A│ │PARENT CLIENT B│ │STANDALONE│
│ Manager User 1 │ │Manager User 2 │ │ CLIENT │
│ (Multi-access) │ │(Multi-access) │ │ │
└───────┬────────┘ └────────┬─────┘ └──────────┘
│ │
┌────┴────┬────┐ ┌────┴────┐
│ │ │ │ │
┌──▼──┐ ┌──▼─┐ ┌▼───┐ ┌▼───┐ ┌─▼──┐
│SUB 1│ │SUB2│ │SUB3│ │SUB1│ │SUB2│
│Users│ │User│ │User│ │User│ │User│
└─────┘ └────┘ └────┘ └────┘ └────┘
✅ Conclusion
Implementasi multi-client access ini memberikan flexibility yang sangat besar untuk:
- 🎯 Scalability - Support unlimited clients & hierarchy
- 🎯 Flexibility - User bisa manage multiple clients
- 🎯 Security - Granular access control
- 🎯 Usability - Easy untuk monitor & manage
Status: ✅ Ready for Testing & Deployment
📚 References
- 📖
docs/MULTI_CLIENT_ACCESS_GUIDE.md- Comprehensive guide - 📖
docs/migrations/001_add_multi_client_support.sql- SQL migration - 📖
docs/examples/articles_controller_example.go- Code examples
Prepared by: AI Assistant
Date: September 30, 2025
Version: 1.0