441 lines
12 KiB
Markdown
441 lines
12 KiB
Markdown
# 🎯 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)
|
|
```go
|
|
+ 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)
|
|
```go
|
|
+ IsSuperAdmin // Platform super admin flag
|
|
+ ClientAccesses // Many-to-Many relationship
|
|
```
|
|
|
|
#### 3. **app/database/entity/user_client_access.entity.go** (NEW ⭐)
|
|
```go
|
|
// 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 clients
|
|
- `GetSubClientIDs()` - Recursive sub-client retrieval
|
|
- `HasAccessToClient()` - Access validation
|
|
- `GetClientHierarchy()` - Parent-child structure
|
|
- `IsParentClient()` - Check if has children
|
|
|
|
#### 6. **utils/middleware/client_utils_v2.go** (NEW ⭐)
|
|
Query utilities:
|
|
- `AddMultiClientFilter()` - Auto filter by accessible clients
|
|
- `SetCurrentClientID()` - Set client on create
|
|
- `ValidateMultiClientAccess()` - Resource access check
|
|
- `FilterByCurrentClient()` - 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)
|
|
```go
|
|
+ entity.UserClientAccess{} // Added to Models()
|
|
```
|
|
|
|
---
|
|
|
|
## 🚀 How to Deploy
|
|
|
|
### Step 1: Backup Database
|
|
```bash
|
|
pg_dump netidhub_db > backup_before_multi_client.sql
|
|
```
|
|
|
|
### Step 2: Run Application (Auto-migrate)
|
|
```bash
|
|
# Pastikan config.toml: migrate = true
|
|
go run main.go
|
|
```
|
|
|
|
GORM AutoMigrate akan otomatis:
|
|
- Tambah columns di `clients` table
|
|
- Tambah column di `users` table
|
|
- Create `user_client_access` table
|
|
|
|
### Step 3: Run SQL Migration (Optional)
|
|
```bash
|
|
# 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
|
|
```sql
|
|
-- 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**
|
|
```go
|
|
// Di config/webserver/webserver.config.go
|
|
app.Use(middleware.ClientMiddlewareV2(db.DB))
|
|
```
|
|
|
|
**Option B: Gradual Migration**
|
|
```go
|
|
// 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
|
|
|
|
```go
|
|
// 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
|
|
|
|
```go
|
|
// 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
|
|
|
|
```go
|
|
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
|
|
|
|
```go
|
|
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:**
|
|
```go
|
|
// 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:**
|
|
```go
|
|
// 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:**
|
|
```go
|
|
// 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
|
|
|
|
1. ✅ **Super admin flag protected** - Cannot be set via API
|
|
2. ✅ **Access validation** - Always check before showing data
|
|
3. ✅ **Delegation control** - Only users with `CanDelegate` can grant access
|
|
4. ✅ **Audit trail** - Track who granted access (GrantedById)
|
|
5. ✅ **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:
|
|
|
|
1. **Test di Development Environment**
|
|
```bash
|
|
# Run migration
|
|
go run main.go
|
|
|
|
# Verify tables
|
|
psql -c "\d user_client_access"
|
|
```
|
|
|
|
2. **Create Sample Data**
|
|
```sql
|
|
-- 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;
|
|
```
|
|
|
|
3. **Update 1-2 Modules sebagai Pilot**
|
|
- Update Articles module dulu
|
|
- Test thoroughly
|
|
- Rollout to other modules
|
|
|
|
4. **Monitor Performance**
|
|
- Check query performance
|
|
- Monitor database load
|
|
- Optimize if needed
|
|
|
|
5. **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
|