Skip to content

Commit 7278732

Browse files
feat(policy_manager): enhance policy initialization with validation and logging
1 parent 30d3a8b commit 7278732

File tree

2 files changed

+157
-15
lines changed

2 files changed

+157
-15
lines changed

lib/src/core/policy_manager.dart

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,113 @@ class PolicyManager extends ChangeNotifier {
253253
try {
254254
final assetHandler = ExternalAssetHandler(assetPath: assetPath);
255255
final jsonPolicies = await assetHandler.loadAssets();
256-
await initialize(jsonPolicies);
256+
257+
try {
258+
LogHandler.info(
259+
'Initializing policy manager with assets',
260+
context: {
261+
'policy_count': jsonPolicies.length,
262+
'policy_keys': jsonPolicies.keys.take(5).toList(),
263+
},
264+
operation: 'policy_manager_initialize',
265+
);
266+
267+
// Create a map of valid policies, skipping invalid ones
268+
final validPolicies = <String, Map<String, dynamic>>{};
269+
270+
for (final entry in jsonPolicies.entries) {
271+
final key = entry.key;
272+
final value = entry.value;
273+
274+
if (value == null) {
275+
LogHandler.warning(
276+
'Skipping null policy value',
277+
context: {'role': key},
278+
operation: 'policy_validation_skip',
279+
);
280+
continue;
281+
}
282+
283+
if (value is! Map<String, dynamic>) {
284+
LogHandler.warning(
285+
'Skipping invalid policy value type',
286+
context: {
287+
'role': key,
288+
'expected_type': 'Map<String, dynamic>',
289+
'actual_type': value.runtimeType.toString(),
290+
},
291+
operation: 'policy_validation_skip',
292+
);
293+
continue;
294+
}
295+
296+
// Add key as role name to value if not already present
297+
if (!value.containsKey('roleName')) {
298+
value['roleName'] = key;
299+
}
300+
301+
try {
302+
// Create the policy and add to valid policies
303+
final role = Role.fromJson(value);
304+
validPolicies[key] = role.toJson();
305+
} catch (e) {
306+
LogHandler.warning(
307+
'Skipping policy with invalid structure',
308+
context: {'role': key, 'error': e.toString()},
309+
operation: 'policy_validation_skip',
310+
);
311+
continue;
312+
}
313+
}
314+
315+
_roles = JsonHandler.parseMap(
316+
validPolicies,
317+
(json) => Role.fromJson(json),
318+
context: 'policy_manager',
319+
allowPartialSuccess: true,
320+
);
321+
322+
// Only create evaluator if we have at least some policies
323+
if (_roles.isNotEmpty) {
324+
_evaluator = RoleEvaluator(_roles);
325+
await _storage.savePolicies(_roles);
326+
_isInitialized = true;
327+
328+
LogHandler.info(
329+
'Policy manager initialized successfully',
330+
context: {
331+
'loaded_policies': _roles.length,
332+
'total_policies': jsonPolicies.length,
333+
},
334+
operation: 'policy_manager_initialized',
335+
);
336+
} else {
337+
LogHandler.warning(
338+
'Policy manager initialized with no valid policies',
339+
context: {
340+
'total_policies': jsonPolicies.length,
341+
},
342+
operation: 'policy_manager_empty',
343+
);
344+
// Still mark as initialized but with empty policies
345+
_isInitialized = true;
346+
}
347+
348+
notifyListeners();
349+
} catch (e, stackTrace) {
350+
LogHandler.error(
351+
'Failed to initialize policy manager',
352+
error: e,
353+
stackTrace: stackTrace,
354+
context: {
355+
'policy_count': jsonPolicies.length,
356+
},
357+
operation: 'policy_manager_initialize_error',
358+
);
359+
360+
// Re-throw to allow caller to handle the error
361+
rethrow;
362+
}
257363
} catch (e, stackTrace) {
258364
LogHandler.error(
259365
'Failed to initialize policy manager from JSON assets',

test/core/policy_manager_test.dart

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -457,8 +457,12 @@ void main() {
457457
// Mock the rootBundle to return valid JSON
458458
const validJson = '''
459459
{
460-
"admin": ["read", "write", "delete"],
461-
"user": ["read"]
460+
"admin": {
461+
"allowedContent": ["read", "write", "delete"]
462+
},
463+
"user": {
464+
"allowedContent": ["read"]
465+
}
462466
}
463467
''';
464468

@@ -471,6 +475,7 @@ void main() {
471475
await policyManager
472476
.initializeFromJsonAssets('assets/policies/test.json');
473477

478+
// The method should initialize successfully with valid JSON
474479
expect(policyManager.isInitialized, isTrue);
475480
expect(policyManager.roles.length, equals(2));
476481
expect(policyManager.roles['admin'], isA<Role>());
@@ -528,9 +533,16 @@ void main() {
528533
// Mock the rootBundle to return JSON with malformed policy data
529534
const malformedJson = '''
530535
{
531-
"admin": "not_a_list",
536+
"admin": {
537+
"allowedContent": "not_a_list"
538+
},
532539
"user": null,
533-
"guest": ["read"]
540+
"guest": {
541+
"allowedContent": ["read"]
542+
},
543+
"invalid_role": {
544+
"allowedContent": [123, "read"]
545+
}
534546
}
535547
''';
536548

@@ -547,8 +559,11 @@ void main() {
547559
expect(policyManager.isInitialized, isTrue);
548560
expect(policyManager.roles.length, equals(1));
549561
expect(policyManager.roles['guest'], isNotNull);
562+
expect(policyManager.roles['guest']!.name, equals('guest'));
563+
expect(policyManager.roles['guest']!.allowedContent, contains('read'));
550564
expect(policyManager.roles['admin'], isNull);
551565
expect(policyManager.roles['user'], isNull);
566+
expect(policyManager.roles['invalid_role'], isNull);
552567

553568
// Clean up mock message handler
554569
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
@@ -580,7 +595,9 @@ void main() {
580595
// Mock the rootBundle to return JSON with single policy
581596
const singlePolicyJson = '''
582597
{
583-
"admin": ["read", "write"]
598+
"admin": {
599+
"allowedContent": ["read", "write"]
600+
}
584601
}
585602
''';
586603

@@ -608,7 +625,9 @@ void main() {
608625
// Create a large JSON with many policies
609626
final largeJson = <String, dynamic>{};
610627
for (int i = 0; i < 100; i++) {
611-
largeJson['role_$i'] = ['read', 'write'];
628+
largeJson['role_$i'] = {
629+
'allowedContent': ['read', 'write']
630+
};
612631
}
613632

614633
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
@@ -632,8 +651,12 @@ void main() {
632651
// Mock the rootBundle to return JSON with empty content arrays
633652
const emptyContentJson = '''
634653
{
635-
"admin": [],
636-
"user": ["read"]
654+
"admin": {
655+
"allowedContent": []
656+
},
657+
"user": {
658+
"allowedContent": ["read"]
659+
}
637660
}
638661
''';
639662

@@ -661,7 +684,9 @@ void main() {
661684
// Mock the rootBundle to return JSON with duplicate content
662685
const duplicateContentJson = '''
663686
{
664-
"admin": ["read", "read", "write", "write"]
687+
"admin": {
688+
"allowedContent": ["read", "read", "write", "write"]
689+
}
665690
}
666691
''';
667692

@@ -698,8 +723,12 @@ void main() {
698723
// Mock the rootBundle to return valid JSON
699724
const validJson = '''
700725
{
701-
"admin": ["read", "write"],
702-
"user": ["read"]
726+
"admin": {
727+
"allowedContent": ["read", "write"]
728+
},
729+
"user": {
730+
"allowedContent": ["read"]
731+
}
703732
}
704733
''';
705734

@@ -732,7 +761,9 @@ void main() {
732761
// Mock the rootBundle to return valid JSON
733762
const validJson = '''
734763
{
735-
"admin": ["read", "write"]
764+
"admin": {
765+
"allowedContent": ["read", "write"]
766+
}
736767
}
737768
''';
738769

@@ -772,7 +803,9 @@ void main() {
772803
// Mock the rootBundle to return valid JSON
773804
const validJson = '''
774805
{
775-
"admin": ["read", "write"]
806+
"admin": {
807+
"allowedContent": ["read", "write"]
808+
}
776809
}
777810
''';
778811

@@ -786,6 +819,7 @@ void main() {
786819
.initializeFromJsonAssets('assets/policies/test.json');
787820

788821
// Check that the expected policy is present in storage
822+
expect(freshPolicyManager.isInitialized, isTrue);
789823
expect(freshStorage.storedPolicies['admin'], isA<Role>());
790824
expect(
791825
(freshStorage.storedPolicies['admin'] as Role).allowedContent,
@@ -805,7 +839,9 @@ void main() {
805839
// Mock the rootBundle to return valid JSON
806840
const validJson = '''
807841
{
808-
"admin": ["read", "write"]
842+
"admin": {
843+
"allowedContent": ["read", "write"]
844+
}
809845
}
810846
''';
811847

0 commit comments

Comments
 (0)