Documentation Index Fetch the complete documentation index at: https://docs-mstore.faisalaffan.com/llms.txt
Use this file to discover all available pages before exploring further.
๐ฑ Flutter RBAC Enterprise v4 (L0โL4)
Blueprint arsitektur Flutter mobile-first ERP untuk sistem MStore yang scalable dari warung (L0) hingga public company (L4) .
Berbasis prinsip Security โ Efficiency โ Ease โ Style .
๐ฏ Prinsip Desain Utama
RBAC Contextual Role menentukan apa yang terlihat & dapat diakses , bukan struktur folder.
Feature-First Modular Struktur berbasis domain (finance, hr, inventory), bukan per role.
Tenant & Entity Isolation L3+ siap multi-entity, tiap entitas terpisah context.
Global Policy Integration L4 integrasi OPA, Casbin, IAM, dan regional policy (GDPR/SOX).
๐งฉ Struktur Folder (7 Domain Architecture)
lib/
โโโ app/
โ โโโ main.dart
โ โโโ routes.dart
โ โโโ app_provider.dart
โ โโโ rbac/
โ โ โโโ rbac_context.dart # Global RBAC provider
โ โ โโโ rbac_guard.dart # RoleGuard untuk navigasi
โ โ โโโ rbac_policy.dart # Static & dynamic rule loader
โ โ โโโ rbac_registry.dart # Sinkronisasi ke backend
โ โโโ config/
โ โ โโโ env.dart
โ โ โโโ constants.dart
โ โ โโโ menu_registry.yaml # Backend-driven menu (L4)
โ โโโ localization/
โ โโโ * .arb # i18n (GDPR, SOX notices)
โ
โโโ core/
โ โโโ theme/
โ โโโ services/ # API, Storage, Logger
โ โ โโโ audit_service.dart # UI audit trail
โ โ โโโ rbac_service.dart # RBAC API integration
โ โโโ utils/
โ โโโ widgets/
โ โโโ constants.dart
โ
โโโ features/
โ โโโ core/ # Auth, Profile, Settings
โ โโโ ops/ # POS, Sales, Inventory, Procurement
โ โโโ support/ # HR, Finance, CRM, Marketing, CS
โ โโโ governance/ # Audit, Policy, SoD
โ โโโ analytics/ # Dashboard, KPI, BI, Forecast
โ โโโ holding/ # Multi-Entity, Consolidation
โ โโโ global/ # IAM, SecurityOps, ESG, CSR
โ
โโโ shared/
โโโ components/
โโโ mixins/
โโโ extensions/
โโโ styles/
โ๏ธ RBACContext v2 (Multi-Level Aware)
import 'package:flutter/foundation.dart' ;
/// RBAC Context yang mendukung multi-level (L0-L4)
class RBACContext extends ChangeNotifier {
final String userId;
final String role;
final String businessLevel; // L0-L4
final String ? entityId; // Untuk Holding (L3+)
final String ? region; // Untuk Global (L4)
final List < String > permissions;
final Map < String , dynamic > ? metadata;
RBACContext ({
required this .userId,
required this .role,
required this .businessLevel,
this .entityId,
this .region,
required this .permissions,
this .metadata,
});
/// Check apakah user punya permission tertentu
bool can ( String code) => permissions. contains (code);
/// Check apakah business level minimal tertentu
bool isAtLeast ( String level) {
const levels = [ 'L0' , 'L1' , 'L2' , 'L3' , 'L4' ];
return levels. indexOf (businessLevel) >= levels. indexOf (level);
}
/// Check apakah role cocok dengan salah satu dari list
bool hasRole ( List < String > roles) => roles. contains (role);
/// Get permission metadata
Map < String , dynamic > ? getPermissionMeta ( String code) {
return metadata ? [ 'permissions' ] ? [code];
}
}
Usage :
// Di app level
ChangeNotifierProvider < RBACContext >(
create : (_) => RBACContext (
userId : user.id,
role : user.role,
businessLevel : merchant.businessLevel,
permissions : user.permissions,
),
);
// Di widget
final rbac = context. watch < RBACContext >();
if (rbac. can ( 'FIN_REPORT_VIEW' )) {
// Show finance report button
}
import 'package:flutter/material.dart' ;
import 'package:provider/provider.dart' ;
/// Widget guard untuk mengontrol akses berdasarkan role
class RoleGuard extends StatelessWidget {
final List < String > allowedRoles;
final Widget child;
final Widget ? fallback;
const RoleGuard ({
Key ? key,
required this .allowedRoles,
required this .child,
this .fallback,
}) : super (key : key);
@override
Widget build ( BuildContext context) {
final rbac = context. watch < RBACContext >();
if ( ! rbac. hasRole (allowedRoles)) {
return fallback ??
const Scaffold (
body : Center (
child : Column (
mainAxisAlignment : MainAxisAlignment .center,
children : [
Icon ( Icons .lock, size : 64 , color : Colors .grey),
SizedBox (height : 16 ),
Text ( '๐ซ Akses Ditolak' ),
Text ( 'Anda tidak memiliki izin untuk halaman ini' ),
],
),
),
);
}
return child;
}
}
Usage :
// Di routes
GoRoute (
path : '/finance/reports' ,
builder : (context, state) => RoleGuard (
allowedRoles : [ 'FIN-MGR' , 'ACC-MGR' , 'AUD' ],
child : FinanceReportPage (),
),
);
๐ RBAC Policy Loader
import 'dart:convert' ;
import 'package:flutter/services.dart' ;
/// Loader untuk RBAC policy dari YAML atau API
class RBACPolicy {
final Map < String , List < String >> rolePermissions;
final Map < String , dynamic > sodRules;
RBACPolicy ({
required this .rolePermissions,
required this .sodRules,
});
/// Load dari local YAML
static Future < RBACPolicy > fromAsset ( String path) async {
final yamlString = await rootBundle. loadString (path);
final data = jsonDecode (yamlString); // Use yaml package in production
return RBACPolicy (
rolePermissions : Map < String , List < String >>. from (
data[ 'role_permissions' ]. map (
(k, v) => MapEntry (k, List < String >. from (v)),
),
),
sodRules : data[ 'sod_rules' ],
);
}
/// Load dari backend API
static Future < RBACPolicy > fromAPI ( String endpoint) async {
// Implementation dengan http client
// final response = await http.get(Uri.parse(endpoint));
// return RBACPolicy.fromJson(jsonDecode(response.body));
throw UnimplementedError ();
}
/// Check permission untuk role tertentu
bool roleHasPermission ( String role, String permission) {
return rolePermissions[role] ? . contains (permission) ?? false ;
}
/// Check SoD violation
bool checkSoDViolation ( List < String > userRoles) {
final violations = sodRules[ 'violations' ] as List ;
for ( var rule in violations) {
final conflictRoles = List < String >. from (rule[ 'roles' ]);
if (userRoles. toSet (). containsAll (conflictRoles. toSet ())) {
return true ; // Ada violation
}
}
return false ;
}
}
File : assets/config/menu_registry.yaml
menus :
- code : DASHBOARD
label : Dashboard
icon : home
path : /dashboard
roles : [ L0_OWNER , L1_OWN-MGR , L2_OWN-MGR ]
level : L0
- code : FIN_REPORT_PL
label : Laporan Laba Rugi
icon : chart-line
path : /finance/report/pl
roles : [ FIN-MGR , OWN-MGR , AUD ]
level : L1
- code : HR_EMPLOYEE_LIST
label : Data Karyawan
icon : users
path : /hr/employee
roles : [ HR-MGR , HR-OPS , OWN-MGR ]
level : L1
- code : INV_PRODUCT_LIST
label : Daftar Produk
icon : box
path : /inventory/products
roles : [ INV-MGR , INV-OPS , L0_OWNER ]
level : L0
- code : APPROVAL_QUEUE
label : Antrian Persetujuan
icon : check-circle
path : /approval/queue
roles : [ APV-L1 , APV-L2 , OWN-MGR ]
level : L2
- code : AUDIT_LOGS
label : Audit Logs
icon : file-text
path : /audit/logs
roles : [ AUD , AUD-GRP , ADM-SYS ]
level : L2
Loader :
class MenuRegistry {
static Future < List < MenuItem >> loadMenus ( RBACContext rbac) async {
final yamlString = await rootBundle. loadString (
'assets/config/menu_registry.yaml'
);
final data = jsonDecode (yamlString);
final menus = (data[ 'menus' ] as List )
. map ((m) => MenuItem . fromMap (m))
. where ((m) => m.roles. contains (rbac.role))
. where ((m) => _isLevelAllowed (m.level, rbac.businessLevel))
. toList ();
return menus;
}
static bool _isLevelAllowed ( String menuLevel, String userLevel) {
const levels = [ 'L0' , 'L1' , 'L2' , 'L3' , 'L4' ];
return levels. indexOf (userLevel) >= levels. indexOf (menuLevel);
}
}
class MenuItem {
final String code;
final String label;
final String icon;
final String path;
final List < String > roles;
final String level;
MenuItem ({
required this .code,
required this .label,
required this .icon,
required this .path,
required this .roles,
required this .level,
});
factory MenuItem . fromMap ( Map < String , dynamic > map) {
return MenuItem (
code : map[ 'code' ],
label : map[ 'label' ],
icon : map[ 'icon' ],
path : map[ 'path' ],
roles : List < String >. from (map[ 'roles' ]),
level : map[ 'level' ],
);
}
}
๐ง SoD Enforcement (L2+)
class SoDValidator {
final RBACPolicy policy;
SoDValidator ( this .policy);
/// Validate SoD sebelum assign role
ValidationResult validate ( List < String > userRoles, String newRole) {
final allRoles = [...userRoles, newRole];
if (policy. checkSoDViolation (allRoles)) {
return ValidationResult (
isValid : false ,
message : 'SoD Violation: Role $ newRole tidak bisa dikombinasi dengan role yang sudah ada' ,
severity : 'critical' ,
);
}
return ValidationResult (isValid : true );
}
}
class ValidationResult {
final bool isValid;
final String ? message;
final String ? severity;
ValidationResult ({
required this .isValid,
this .message,
this .severity,
});
}
๐ข EntityScope (L3+ Holding)
class EntityScope extends InheritedWidget {
final String entityId;
final String entityName;
const EntityScope ({
Key ? key,
required this .entityId,
required this .entityName,
required Widget child,
}) : super (key : key, child : child);
static EntityScope of ( BuildContext context) {
final scope = context. dependOnInheritedWidgetOfExactType < EntityScope >();
assert (scope != null , 'EntityScope not found in context' );
return scope ! ;
}
@override
bool updateShouldNotify ( EntityScope oldWidget) {
return entityId != oldWidget.entityId;
}
}
Usage :
EntityScope (
entityId : "ENT-01" ,
entityName : "PT Musholla Store Jakarta" ,
child : FinanceDashboard (),
);
// Di child widget
final entity = EntityScope . of (context);
print ( 'Current entity: ${ entity . entityName } ' );
๐ Audit Trail Service
import 'package:http/http.dart' as http;
import 'dart:convert' ;
class AuditService {
final String baseUrl;
AuditService ( this .baseUrl);
/// Log user action untuk audit trail
Future < void > logUserAction (
String event, {
Map < String , dynamic > ? meta,
}) async {
try {
await http. post (
Uri . parse ( ' $ baseUrl /v2/audit/ui' ),
headers : { 'Content-Type' : 'application/json' },
body : jsonEncode ({
'event' : event,
'timestamp' : DateTime . now (). toIso8601String (),
'meta' : meta,
}),
);
} catch (e) {
// Log error tapi jangan block UI
print ( 'Audit log failed: $ e ' );
}
}
}
Usage :
// Di button handler
onPressed : () {
auditService. logUserAction (
'CREATE_INVOICE' ,
meta : {
'amount' : 200000 ,
'customer_id' : 'CUST-001' ,
},
);
createInvoice ();
}
๐ Global Compliance (L4)
IAM Integration
class IAMService {
Future < AuthResult > loginWithOAuth2 ( String provider) async {
// OAuth2 / OIDC implementation
// Keycloak, Auth0, Azure AD
throw UnimplementedError ();
}
Future < void > refreshToken () async {
// Token refresh dari /v2/core/auth/refresh
throw UnimplementedError ();
}
Future < List < String >> syncRoles () async {
// Role sync dari /v2/core/roles
throw UnimplementedError ();
}
}
Regional Policy
File : lib/app/localization/app_en.arb
{
"gdpr_warning" : "Your data is processed in accordance with GDPR policy." ,
"sox_warning" : "Audit active: All actions are recorded (SOX-404)." ,
"iso_compliance" : "This system is ISO 27001 compliant."
}
๐งช Testing Blueprint
import 'package:flutter_test/flutter_test.dart' ;
void main () {
group ( 'RBACContext Tests' , () {
test ( 'Role FIN-MGR can access finance reports' , () {
final ctx = RBACContext (
userId : '123' ,
role : 'FIN-MGR' ,
businessLevel : 'L2' ,
permissions : [ 'FIN_REPORT_PL' , 'FIN_REPORT_VIEW' ],
);
expect (ctx. can ( 'FIN_REPORT_PL' ), true );
expect (ctx. can ( 'HR_EMPLOYEE_VIEW' ), false );
});
test ( 'L2 business level includes L0 and L1 features' , () {
final ctx = RBACContext (
userId : '123' ,
role : 'OWN-MGR' ,
businessLevel : 'L2' ,
permissions : [],
);
expect (ctx. isAtLeast ( 'L0' ), true );
expect (ctx. isAtLeast ( 'L1' ), true );
expect (ctx. isAtLeast ( 'L2' ), true );
expect (ctx. isAtLeast ( 'L3' ), false );
});
});
group ( 'SoD Validation Tests' , () {
test ( 'Detect SoD violation' , () {
final policy = RBACPolicy (
rolePermissions : {},
sodRules : {
'violations' : [
{ 'roles' : [ 'FIN-AP' , 'ACC-MGR' ]}
]
},
);
final validator = SoDValidator (policy);
final result = validator. validate ([ 'FIN-AP' ], 'ACC-MGR' );
expect (result.isValid, false );
});
});
}
๐ Feature Matrix by Level
Feature L0 L1 L2 L3 L4 Implementation
Role-based UI โ
โ
โ
โ
โ
RBACContext SoD Enforcement โ โ ๏ธ โ
โ
โ
SoDValidator Multi-Entity โ โ โ โ
โ
EntityScope Dynamic Menu โ ๏ธ โ
โ
โ
โ
MenuRegistry Audit Trail โ โ ๏ธ โ
โ
โ
AuditService IAM/SSO โ โ โ โ
โ
IAMService Policy Sync โ โ โ ๏ธ โ
โ
RBACPolicy.fromAPI i18n Regional โ โ โ โ ๏ธ โ
.arb files Offline Support โ
โ
โ
โ โ Hive/Drift
๐ฆ Dependencies
dependencies :
flutter :
sdk : flutter
# State Management
provider : ^6.1.1
riverpod : ^2.4.9 # Alternative
# Routing
go_router : ^13.0.0
# Data Models
freezed_annotation : ^2.4.1
json_annotation : ^4.8.1
# Networking
http : ^1.2.0
dio : ^5.4.0
# Local Storage
hive : ^2.2.3
hive_flutter : ^1.1.0
drift : ^2.14.1
# Localization
flutter_localizations :
sdk : flutter
intl : ^0.19.0
# Utils
yaml : ^3.1.2
dev_dependencies :
flutter_test :
sdk : flutter
# Code Generation
build_runner : ^2.4.7
freezed : ^2.4.6
json_serializable : ^6.7.1
# Testing
mockito : ^5.4.4
integration_test :
sdk : flutter
โ
Implementation Checklist
Phase 1: Core RBAC (Week 1-2)
Phase 3: Audit & Compliance (Week 4)
Phase 4: L3+ Features (Week 5-6)
RBAC Architecture Multi-level RBAC (L0-L4)
UI/UX by Role UI specifications per role
Bruno API Collections API endpoints by role
Casbin Integration Policy enforcement
Audit Logs Audit trail system
Global Compliance SOX, ISO, GDPR
๐ฏ Success Metrics
Metric Target Measurement
Code Coverage >80% Unit + Widget tests SoD Compliance 100% No violations in production Audit Trail 100% All critical actions logged Menu Load Time < 100ms Dynamic menu rendering Role Check Performance < 10ms Permission validation Build Size Impact < 500KB Additional code overhead
๐ฅ Best Practices
Always use RBACContext untuk permission checks
Never hardcode role checks di UI code
Use RoleGuard untuk semua protected routes
Log critical actions dengan AuditService
Validate SoD sebelum role assignment
Test across all levels (L0-L4)
Keep policy sync dengan backend
Document permission codes di constants
Status : โ
Production-Ready Blueprint
Last Updated : November 5, 2025
Version : 4.0.0