Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions example/lib/demo_content.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import 'dart:developer';

import 'package:flutter/material.dart';
import 'package:flutter_policy_engine/flutter_policy_engine.dart';

class DemoContent extends StatefulWidget {
const DemoContent({super.key});

@override
State<DemoContent> createState() => _DemoContentState();
}

class _DemoContentState extends State<DemoContent> {
String _currentRole = 'guest';

@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Rol actual: $_currentRole',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 16),

// Selector de rol
Row(
children: [
_buildRoleButton('guest'),
const SizedBox(width: 8),
_buildRoleButton('user'),
const SizedBox(width: 8),
_buildRoleButton('admin'),
],
),

const SizedBox(height: 32),

// Ejemplos de PolicyWidget
_buildPolicyExample('LoginPage', 'Página de Login'),
_buildPolicyExample('Dashboard', 'Dashboard'),
_buildPolicyExample('UserManagement', 'Gestión de Usuarios'),
_buildPolicyExample('Settings', 'Configuración'),

const SizedBox(height: 32),
],
),
);
}

Widget _buildRoleButton(String role) {
return ElevatedButton(
onPressed: () {
setState(() {
_currentRole = role;
});
},
style: ElevatedButton.styleFrom(
backgroundColor: _currentRole == role ? Colors.blue : Colors.grey,
),
child: Text(role),
);
}

Widget _buildPolicyExample(String content, String displayName) {
return Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('$displayName:'),
const SizedBox(height: 4),
PolicyWidget(
role: _currentRole,
content: content,
fallback: Card(
color: Colors.red[100],
child: Padding(
padding: const EdgeInsets.all(16),
child: Text('Acceso denegado para $displayName'),
),
),
onAccessDenied: () {
log('Acceso denegado para $_currentRole a $content');
},
child: Card(
color: Colors.green[100],
child: Padding(
padding: const EdgeInsets.all(16),
child: Text('Acceso permitido a $displayName'),
),
),
),
],
),
);
}
}
68 changes: 1 addition & 67 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import 'dart:developer';

import 'package:flutter/material.dart';
import 'package:flutter_policy_engine/flutter_policy_engine.dart';
import 'package:flutter_policy_engine_example/policy_engine_demo.dart';

void main() {
runApp(const MyApp());
Expand All @@ -22,67 +20,3 @@ class MyApp extends StatelessWidget {
);
}
}

class PolicyEngineDemo extends StatefulWidget {
const PolicyEngineDemo({super.key});

@override
State<StatefulWidget> createState() {
return _PolicyEngineDemoState();
}
}

class _PolicyEngineDemoState extends State<PolicyEngineDemo> {
late PolicyManager policyManager;
bool _isInitialized = false;

@override
void initState() {
super.initState();
_initializePolicyManager();
}

Future<void> _initializePolicyManager() async {
policyManager = PolicyManager();
final policies = {
"admin": ["LoginPage", "Dashboard", "UserManagement", "Settings"],
"user": ["LoginPage", "Dashboard"],
"guest": ["LoginPage"]
};
try {
await policyManager.initialize(policies);
setState(() {
_isInitialized = true;
});
} catch (e) {
log(e.toString());
setState(() {
_isInitialized = false;
});
}
}

@override
Widget build(BuildContext context) {
if (!_isInitialized) {
return Scaffold(
appBar: AppBar(
title: const Text('Policy Engine Demo'),
),
body: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('Loading policies...'),
],
),
),
);
}
return const Center(
child: Text('Policies loaded'),
);
}
}
75 changes: 75 additions & 0 deletions example/lib/policy_engine_demo.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import 'dart:developer';

import 'package:flutter/material.dart';
import 'package:flutter_policy_engine/flutter_policy_engine.dart';
import 'package:flutter_policy_engine_example/demo_content.dart';

class PolicyEngineDemo extends StatefulWidget {
const PolicyEngineDemo({super.key});

@override
State<StatefulWidget> createState() {
return _PolicyEngineDemoState();
}
}

class _PolicyEngineDemoState extends State<PolicyEngineDemo> {
late PolicyManager policyManager;
bool _isInitialized = false;

@override
void initState() {
super.initState();
_initializePolicyManager();
}

Future<void> _initializePolicyManager() async {
policyManager = PolicyManager();
final policies = {
"admin": ["LoginPage", "Dashboard", "UserManagement", "Settings"],
"user": ["LoginPage", "Dashboard"],
"guest": ["LoginPage"]
};
try {
await policyManager.initialize(policies);
setState(() {
_isInitialized = true;
});
} catch (e) {
log(e.toString());
setState(() {
_isInitialized = false;
});
}
}

@override
Widget build(BuildContext context) {
if (!_isInitialized) {
return Scaffold(
appBar: AppBar(
title: const Text('Policy Engine Demo'),
),
body: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('Loading policies...'),
],
),
),
);
}
return PolicyProvider(
policyManager: policyManager,
child: Scaffold(
appBar: AppBar(
title: const Text('Policy Engine Demo'),
),
body: const DemoContent(),
),
);
}
}
2 changes: 2 additions & 0 deletions lib/flutter_policy_engine.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
library flutter_policy_engine;

export 'src/core/policy_manager.dart';
export 'src/widgets/policy_widget.dart';
export 'src/core/policy_provider.dart';
97 changes: 53 additions & 44 deletions lib/src/core/policy_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -86,54 +86,43 @@ class PolicyManager extends ChangeNotifier {
final key = entry.key;
final value = entry.value;

try {
if (value == null) {
LogHandler.warning(
'Skipping null policy value',
context: {'role': key},
operation: 'policy_validation_skip',
);
continue;
}

if (value is! List) {
LogHandler.warning(
'Skipping invalid policy value type',
context: {
'role': key,
'expected_type': 'List',
'actual_type': value.runtimeType.toString(),
},
operation: 'policy_validation_skip',
);
continue;
}

if (value.any((item) => item is! String)) {
LogHandler.warning(
'Skipping policy with non-string content items',
context: {'role': key},
operation: 'policy_validation_skip',
);
continue;
}

// Create the policy and add to valid policies
final policy = Policy(
roleName: key,
allowedContent: value.cast<String>(),
if (value == null) {
LogHandler.warning(
'Skipping null policy value',
context: {'role': key},
operation: 'policy_validation_skip',
);
continue;
}

if (value is! List) {
LogHandler.warning(
'Skipping invalid policy value type',
context: {
'role': key,
'expected_type': 'List',
'actual_type': value.runtimeType.toString(),
},
operation: 'policy_validation_skip',
);
validPolicies[key] = policy.toJson();
} catch (e, stackTrace) {
LogHandler.error(
'Failed to process policy',
error: e,
stackTrace: stackTrace,
continue;
}

if (value.any((item) => item is! String)) {
LogHandler.warning(
'Skipping policy with non-string content items',
context: {'role': key},
operation: 'policy_processing_error',
operation: 'policy_validation_skip',
);
// Continue with other policies
continue;
}

// Create the policy and add to valid policies
final policy = Policy(
roleName: key,
allowedContent: value.cast<String>(),
);
validPolicies[key] = policy.toJson();
}

_policies = JsonHandler.parseMap(
Expand Down Expand Up @@ -185,4 +174,24 @@ class PolicyManager extends ChangeNotifier {
rethrow;
}
}

/// Checks if the specified [role] has access to the given [content].
///
/// Returns `true` if the policy manager is initialized and the evaluator
/// determines that the [role] is permitted to access the [content].
/// Returns `false` if the policy manager is not initialized, the evaluator
/// is not set, or if access is denied.
///
/// Logs an error if called before initialization or if the evaluator is missing.
bool hasAccess(String role, String content) {
if (!_isInitialized || _evaluator == null) {
LogHandler.error(
'Policy manager not initialized or evaluator not set',
context: {'role': role, 'content': content},
operation: 'policy_manager_access_check',
);
return false;
}
return _evaluator!.evaluate(role, content);
}
}
Loading