Skip to content

Commit 2ea9d97

Browse files
refactor(policy): consolidate policy management and evaluation and enhancing PolicyManager
1 parent b05031c commit 2ea9d97

14 files changed

+1158
-102
lines changed

lib/core/interfaces/i_policy_evaluator.dart

Lines changed: 0 additions & 6 deletions
This file was deleted.

lib/core/interfaces/i_policy_storage.dart

Lines changed: 0 additions & 8 deletions
This file was deleted.

lib/core/policy_manager.dart

Lines changed: 0 additions & 21 deletions
This file was deleted.

lib/core/role_evaluator.dart

Lines changed: 0 additions & 16 deletions
This file was deleted.

lib/models/policy.dart

Lines changed: 0 additions & 43 deletions
This file was deleted.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import 'package:meta/meta.dart';
2+
3+
/// Abstract interface for evaluating policies based on role and content.
4+
///
5+
/// This interface defines the contract for policy evaluation implementations
6+
/// that determine whether a given role has permission to access specific content.
7+
/// Implementations should provide the logic for checking role-based access control
8+
/// according to defined policy rules.
9+
///
10+
/// Example usage:
11+
/// ```dart
12+
/// class SimplePolicyEvaluator implements IPolicyEvaluator {
13+
/// @override
14+
/// bool evaluate(String roleName, String content) {
15+
/// // Implementation logic here
16+
/// return true;
17+
/// }
18+
/// }
19+
/// ```
20+
@immutable
21+
abstract class IPolicyEvaluator {
22+
/// Evaluates whether a role has permission to access specific content.
23+
///
24+
/// This method performs the core policy evaluation logic by checking
25+
/// if the specified role has the necessary permissions to access the
26+
/// given content according to the defined policy rules.
27+
///
28+
/// [roleName] The name of the role to evaluate permissions for.
29+
/// Must not be null or empty.
30+
///
31+
/// [content] The content identifier or path to check access for.
32+
/// Must not be null or empty.
33+
///
34+
/// Returns `true` if the role has permission to access the content,
35+
/// `false` otherwise.
36+
///
37+
/// Throws:
38+
/// - [ArgumentError] if [roleName] or [content] is null or empty
39+
/// - [StateError] if the policy evaluator is not properly initialized
40+
bool evaluate(String roleName, String content);
41+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import 'package:meta/meta.dart';
2+
3+
/// Abstract interface for policy storage operations.
4+
///
5+
/// This interface defines the contract for storing and retrieving policy data
6+
/// in the Flutter Policy Engine. Implementations can provide different storage
7+
/// backends such as local file storage, secure storage, or remote storage.
8+
///
9+
/// All methods are asynchronous to support various storage mechanisms that
10+
/// may require I/O operations.
11+
@immutable
12+
abstract class IPolicyStorage {
13+
/// Loads all policies from storage.
14+
///
15+
/// Returns a [Map<String, dynamic>] containing the policy data where:
16+
/// - Keys represent policy identifiers
17+
/// - Values represent the policy configuration and rules
18+
///
19+
/// Throws a [StateError] if the storage is not accessible or corrupted.
20+
/// Returns an empty map if no policies are stored.
21+
Future<Map<String, dynamic>> loadPolicies();
22+
23+
/// Saves policies to storage.
24+
///
25+
/// [policies] should be a [Map<String, dynamic>] where:
26+
/// - Keys represent policy identifiers
27+
/// - Values represent the policy configuration and rules
28+
///
29+
/// This method will overwrite any existing policies in storage.
30+
/// Throws a [StateError] if the storage is not writable or if the data
31+
/// format is invalid.
32+
Future<void> savePolicies(Map<String, dynamic> policies);
33+
34+
/// Removes all policies from storage.
35+
///
36+
/// This operation is irreversible and will permanently delete all stored
37+
/// policy data. Use with caution.
38+
///
39+
/// Throws a [StateError] if the storage is not accessible or if the
40+
/// clear operation fails.
41+
Future<void> clearPolicies();
42+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import 'package:flutter_policy_engine/src/core/interfaces/i_policy_storage.dart';
2+
3+
/// An in-memory implementation of [IPolicyStorage] that stores policies
4+
/// in a Map during the application's runtime.
5+
///
6+
/// This storage implementation is suitable for:
7+
/// - Testing scenarios where persistence is not required
8+
/// - Temporary policy storage during development
9+
/// - Cases where policies are loaded at startup and don't need to persist
10+
/// between application sessions
11+
///
12+
/// **Note:** All data stored in this implementation will be lost when the
13+
/// application is terminated or the object is garbage collected.
14+
class MemoryPolicyStorage implements IPolicyStorage {
15+
/// Internal storage for policies using a Map with String keys and dynamic values.
16+
///
17+
/// The Map structure allows for flexible policy storage where keys represent
18+
/// policy identifiers and values contain the policy data.
19+
Map<String, dynamic> _policies = {};
20+
21+
/// Loads all stored policies from memory.
22+
///
23+
/// Returns a copy of the internal policies map to prevent external
24+
/// modifications from affecting the internal state.
25+
///
26+
/// **Returns:** A [Future] that completes with a [Map<String, dynamic>]
27+
/// containing all stored policies.
28+
///
29+
/// **Example:**
30+
/// ```dart
31+
/// final storage = MemoryPolicyStorage();
32+
/// final policies = await storage.loadPolicies();
33+
/// print('Loaded ${policies.length} policies');
34+
/// ```
35+
@override
36+
Future<Map<String, dynamic>> loadPolicies() async {
37+
return Map.from(_policies);
38+
}
39+
40+
/// Saves the provided policies to memory storage.
41+
///
42+
/// Creates a deep copy of the input policies to ensure the internal
43+
/// storage is not affected by subsequent modifications to the input map.
44+
///
45+
/// **Parameters:**
46+
/// - [policies]: A [Map<String, dynamic>] containing the policies to store.
47+
/// Keys should be policy identifiers and values should contain policy data.
48+
///
49+
/// **Example:**
50+
/// ```dart
51+
/// final storage = MemoryPolicyStorage();
52+
/// final policies = {'user_policy': {'role': 'admin'}};
53+
/// await storage.savePolicies(policies);
54+
/// ```
55+
@override
56+
Future<void> savePolicies(Map<String, dynamic> policies) async {
57+
_policies = Map.from(policies);
58+
}
59+
60+
/// Removes all stored policies from memory.
61+
///
62+
/// This operation is immediate and irreversible. All policy data
63+
/// will be permanently lost after calling this method.
64+
///
65+
/// **Example:**
66+
/// ```dart
67+
/// final storage = MemoryPolicyStorage();
68+
/// await storage.clearPolicies();
69+
/// // All policies have been removed
70+
/// ```
71+
@override
72+
Future<void> clearPolicies() async {
73+
_policies.clear();
74+
}
75+
}

lib/src/core/policy_manager.dart

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import 'package:flutter/foundation.dart';
2+
import 'package:flutter_policy_engine/src/core/interfaces/i_policy_evaluator.dart';
3+
import 'package:flutter_policy_engine/src/core/interfaces/i_policy_storage.dart';
4+
import 'package:flutter_policy_engine/src/core/memory_policy_storage.dart';
5+
import 'package:flutter_policy_engine/src/core/role_evaluator.dart';
6+
import 'package:flutter_policy_engine/src/models/policy.dart';
7+
import 'package:flutter_policy_engine/src/utils/json_handler.dart';
8+
import 'package:flutter_policy_engine/src/utils/log_handler.dart';
9+
10+
/// Manages policy lifecycle and provides centralized access to policy operations.
11+
///
12+
/// The [PolicyManager] serves as the main entry point for policy-related operations,
13+
/// coordinating between storage, evaluation, and policy state management. It extends
14+
/// [ChangeNotifier] to notify listeners of policy state changes.
15+
///
16+
/// Example usage:
17+
/// ```dart
18+
/// final policyManager = PolicyManager(
19+
/// storage: MyPolicyStorage(),
20+
/// evaluator: MyPolicyEvaluator(),
21+
/// );
22+
///
23+
/// await policyManager.initialize(policyJsonData);
24+
/// ```
25+
class PolicyManager extends ChangeNotifier {
26+
/// Creates a new [PolicyManager] instance.
27+
///
28+
/// [storage] is responsible for persisting and retrieving policy data.
29+
/// [evaluator] handles policy evaluation logic and decision making.
30+
PolicyManager({
31+
IPolicyStorage? storage,
32+
IPolicyEvaluator? evaluator,
33+
}) : _storage = storage ?? MemoryPolicyStorage(),
34+
_evaluator = evaluator;
35+
36+
/// The storage implementation for policy persistence.
37+
final IPolicyStorage _storage;
38+
39+
/// The evaluator implementation for policy decision making.
40+
IPolicyEvaluator? _evaluator;
41+
42+
/// Internal cache of loaded policies, keyed by policy identifier.
43+
Map<String, Policy> _policies = {};
44+
45+
/// Indicates whether the policy manager has been initialized with policy data.
46+
bool _isInitialized = false;
47+
48+
/// Returns whether the policy manager has been initialized.
49+
///
50+
/// Returns `true` if [initialize] has been called successfully, `false` otherwise.
51+
bool get isInitialized => _isInitialized;
52+
53+
/// Returns an unmodifiable view of all loaded policies.
54+
///
55+
/// The returned map cannot be modified directly. Use [initialize] to update
56+
/// the policy collection.
57+
Map<String, Policy> get policies => Map.unmodifiable(_policies);
58+
59+
/// Initializes the policy manager with policy data from JSON.
60+
///
61+
/// Parses the provided [jsonPolicies] and loads them into the internal cache.
62+
/// This method should be called before using any policy-related functionality.
63+
///
64+
/// [jsonPolicies] should be a map where keys are policy identifiers and values
65+
/// are JSON representations of [Policy] objects.
66+
///
67+
/// Throws:
68+
/// - [JsonParseException] if policy parsing fails completely
69+
/// - [FormatException] if the JSON data is malformed
70+
/// - [ArgumentError] if policy parsing fails
71+
Future<void> initialize(Map<String, dynamic> jsonPolicies) async {
72+
try {
73+
LogHandler.info(
74+
'Initializing policy manager',
75+
context: {
76+
'policy_count': jsonPolicies.length,
77+
'policy_keys': jsonPolicies.keys.take(5).toList(),
78+
},
79+
operation: 'policy_manager_initialize',
80+
);
81+
82+
_policies = JsonHandler.parseMap(
83+
jsonPolicies,
84+
(json) => Policy.fromJson(json),
85+
context: 'policy_manager',
86+
allowPartialSuccess:
87+
true, // Allow partial success for graceful degradation
88+
);
89+
90+
// Only create evaluator if we have at least some policies
91+
if (_policies.isNotEmpty) {
92+
_evaluator = RoleEvaluator(_policies);
93+
await _storage.savePolicies(_policies);
94+
_isInitialized = true;
95+
96+
LogHandler.info(
97+
'Policy manager initialized successfully',
98+
context: {
99+
'loaded_policies': _policies.length,
100+
'total_policies': jsonPolicies.length,
101+
},
102+
operation: 'policy_manager_initialized',
103+
);
104+
} else {
105+
LogHandler.warning(
106+
'Policy manager initialized with no valid policies',
107+
context: {
108+
'total_policies': jsonPolicies.length,
109+
},
110+
operation: 'policy_manager_empty',
111+
);
112+
// Still mark as initialized but with empty policies
113+
_isInitialized = true;
114+
}
115+
116+
notifyListeners();
117+
} catch (e, stackTrace) {
118+
LogHandler.error(
119+
'Failed to initialize policy manager',
120+
error: e,
121+
stackTrace: stackTrace,
122+
context: {
123+
'policy_count': jsonPolicies.length,
124+
},
125+
operation: 'policy_manager_initialize_error',
126+
);
127+
128+
// Re-throw to allow caller to handle the error
129+
rethrow;
130+
}
131+
}
132+
}

0 commit comments

Comments
 (0)