Skip to content

Commit 6490000

Browse files
Copilotnomeguy
andcommitted
Add Flutter example and update README with JSON ABAC documentation
Co-authored-by: nomeguy <85475922+nomeguy@users.noreply.github.com>
1 parent f2e3054 commit 6490000

2 files changed

Lines changed: 327 additions & 0 deletions

File tree

README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,67 @@ https://casbin.org/docs/tutorials
128128

129129
See [Policy management APIs](#policy-management) for more usage.
130130

131+
## ABAC with JSON strings
132+
133+
Dart-Casbin supports using JSON strings as subjects for Attribute-Based Access Control (ABAC), making it easy to implement ABAC in Flutter and Dart applications without creating custom classes.
134+
135+
```dart
136+
import 'package:casbin/casbin.dart';
137+
import 'dart:convert';
138+
139+
// Define an ABAC model
140+
final model = Model()..loadModelFromText('''
141+
[request_definition]
142+
r = sub, obj, act
143+
144+
[policy_definition]
145+
p = sub, obj, act, condition
146+
147+
[policy_effect]
148+
e = some(where (p.eft == allow))
149+
150+
[matchers]
151+
m = eval(p.condition) && r.obj == p.obj && r.act == p.act
152+
''');
153+
154+
// Create enforcer
155+
final enforcer = Enforcer.initWithModelAndAdapter(model);
156+
157+
// Add policy with attribute-based condition
158+
enforcer.addPolicy([
159+
'{"age": 18}',
160+
'/data1',
161+
'read',
162+
'r.sub.age >= 18 && r.sub.age < 60'
163+
]);
164+
165+
// Check access using JSON string
166+
String userJson = jsonEncode({"age": 25});
167+
bool allowed = enforcer.enforce([userJson, '/data1', 'read']);
168+
// Returns: true (access granted)
169+
```
170+
171+
You can also use multiple attributes:
172+
173+
```dart
174+
// Add policy with multiple attributes
175+
enforcer.addPolicy([
176+
'{"role": "admin"}',
177+
'/admin',
178+
'write',
179+
'r.sub.role == "admin" && r.sub.age >= 21'
180+
]);
181+
182+
// Check access with multiple user attributes
183+
String adminJson = jsonEncode({"role": "admin", "age": 25});
184+
bool allowed = enforcer.enforce([adminJson, '/admin', 'write']);
185+
// Returns: true
186+
```
187+
188+
For complete examples, see:
189+
- [example/abac_json_example.dart](example/abac_json_example.dart) - Basic ABAC with JSON
190+
- [example/flutter_abac_example.dart](example/flutter_abac_example.dart) - Flutter-specific examples
191+
131192
## Policy management
132193

133194
Casbin provides two sets of APIs to manage permissions:

example/flutter_abac_example.dart

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
// Copyright 2018-2021 The Casbin Authors. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
/// This example demonstrates how to use ABAC with JSON strings in Flutter.
16+
///
17+
/// This approach allows you to pass user attributes directly as JSON without
18+
/// creating custom classes, making it ideal for Flutter applications that
19+
/// receive user data from APIs or other sources.
20+
///
21+
/// To use this in a Flutter app:
22+
/// 1. Add casbin to your pubspec.yaml: `casbin: ^0.1.0`
23+
/// 2. Import this code into your Flutter widget
24+
/// 3. Call the checkAccess() method when you need to verify permissions
25+
///
26+
/// Example Flutter widget code:
27+
/// ```dart
28+
/// import 'package:flutter/material.dart';
29+
/// import 'package:casbin/casbin.dart';
30+
/// import 'dart:convert';
31+
///
32+
/// class ABACScreen extends StatefulWidget {
33+
/// @override
34+
/// _ABACScreenState createState() => _ABACScreenState();
35+
/// }
36+
///
37+
/// class _ABACScreenState extends State<ABACScreen> {
38+
/// final TextEditingController ageController = TextEditingController();
39+
/// String resultMessage = "";
40+
///
41+
/// Future<void> checkAccess() async {
42+
/// int? age = int.tryParse(ageController.text);
43+
/// if (age == null) {
44+
/// setState(() {
45+
/// resultMessage = "❌ Please enter a valid age.";
46+
/// });
47+
/// return;
48+
/// }
49+
///
50+
/// // Create model
51+
/// final model = Model()..loadModelFromText(getModel());
52+
/// final enforcer = Enforcer.initWithModelAndAdapter(model);
53+
///
54+
/// // Add policy
55+
/// enforcer.addPolicy([
56+
/// '{"age": 18}',
57+
/// '/data1',
58+
/// 'read',
59+
/// 'r.sub.age >= 18 && r.sub.age < 60'
60+
/// ]);
61+
///
62+
/// try {
63+
/// // Create JSON string with user attributes
64+
/// String subJson = jsonEncode({"age": age});
65+
///
66+
/// // Enforce the policy
67+
/// bool result = enforcer.enforce([subJson, '/data1', 'read']);
68+
///
69+
/// setState(() {
70+
/// resultMessage = result ? "✅ Access Granted" : "❌ Access Denied";
71+
/// });
72+
/// } catch (e) {
73+
/// debugPrint("⚠️ Error: $e");
74+
/// setState(() {
75+
/// resultMessage = "⚠️ Error: ${e.toString()}";
76+
/// });
77+
/// }
78+
/// }
79+
///
80+
/// @override
81+
/// Widget build(BuildContext context) {
82+
/// return Scaffold(
83+
/// appBar: AppBar(title: Text("Casbin ABAC Demo")),
84+
/// body: Padding(
85+
/// padding: EdgeInsets.all(20),
86+
/// child: Column(
87+
/// mainAxisAlignment: MainAxisAlignment.center,
88+
/// children: [
89+
/// TextField(
90+
/// controller: ageController,
91+
/// keyboardType: TextInputType.number,
92+
/// decoration: InputDecoration(
93+
/// labelText: "Age",
94+
/// border: OutlineInputBorder()
95+
/// ),
96+
/// ),
97+
/// SizedBox(height: 20),
98+
/// ElevatedButton(
99+
/// onPressed: checkAccess,
100+
/// child: Text("Check Access"),
101+
/// ),
102+
/// SizedBox(height: 20),
103+
/// SelectableText(
104+
/// resultMessage,
105+
/// style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)
106+
/// ),
107+
/// ],
108+
/// ),
109+
/// ),
110+
/// );
111+
/// }
112+
/// }
113+
///
114+
/// String getModel() {
115+
/// return '''
116+
/// [request_definition]
117+
/// r = sub, obj, act
118+
///
119+
/// [policy_definition]
120+
/// p = sub, obj, act, condition
121+
///
122+
/// [policy_effect]
123+
/// e = some(where (p.eft == allow))
124+
///
125+
/// [matchers]
126+
/// m = eval(p.condition) && r.obj == p.obj && r.act == p.act
127+
/// ''';
128+
/// }
129+
/// ```
130+
///
131+
/// Key points:
132+
/// - Use jsonEncode() to convert Dart maps to JSON strings
133+
/// - Pass the JSON string directly to enforcer.enforce()
134+
/// - The JSON attributes (like "age") can be accessed in policy conditions using dot notation (r.sub.age)
135+
/// - No need to create custom classes that implement AbacClass
136+
///
137+
library abac_flutter_example;
138+
139+
import 'package:casbin/casbin.dart';
140+
import 'dart:convert';
141+
142+
/// Simulates a Flutter app's access control check
143+
void simulateFlutterAccessCheck(int userAge, String resource, String action) {
144+
print('\n--- Simulating Flutter Access Check ---');
145+
print('User Age: $userAge');
146+
print('Resource: $resource');
147+
print('Action: $action');
148+
149+
// Create the model
150+
final model = Model()..loadModelFromText('''
151+
[request_definition]
152+
r = sub, obj, act
153+
154+
[policy_definition]
155+
p = sub, obj, act, condition
156+
157+
[policy_effect]
158+
e = some(where (p.eft == allow))
159+
160+
[matchers]
161+
m = eval(p.condition) && r.obj == p.obj && r.act == p.act
162+
''');
163+
164+
// Create enforcer
165+
final enforcer = Enforcer.initWithModelAndAdapter(model);
166+
167+
// Add policy (this would typically come from a database or config file)
168+
enforcer.addPolicy([
169+
'{"age": 18}',
170+
'/data1',
171+
'read',
172+
'r.sub.age >= 18 && r.sub.age < 60'
173+
]);
174+
175+
// Create JSON string from user attributes
176+
// In a real Flutter app, this data might come from an API response
177+
String subJson = jsonEncode({"age": userAge});
178+
179+
// Check access
180+
try {
181+
bool hasAccess = enforcer.enforce([subJson, resource, action]);
182+
print('Result: ${hasAccess ? "✅ Access Granted" : "❌ Access Denied"}');
183+
} catch (e) {
184+
print('⚠️ Error: $e');
185+
}
186+
}
187+
188+
/// Example with multiple user attributes
189+
void simulateComplexFlutterAccessCheck(
190+
String userName,
191+
int userAge,
192+
String userRole,
193+
String resource,
194+
String action,
195+
) {
196+
print('\n--- Simulating Complex Flutter Access Check ---');
197+
print('User: $userName');
198+
print('Age: $userAge');
199+
print('Role: $userRole');
200+
print('Resource: $resource');
201+
print('Action: $action');
202+
203+
final model = Model()..loadModelFromText('''
204+
[request_definition]
205+
r = sub, obj, act
206+
207+
[policy_definition]
208+
p = sub, obj, act, condition
209+
210+
[policy_effect]
211+
e = some(where (p.eft == allow))
212+
213+
[matchers]
214+
m = eval(p.condition) && r.obj == p.obj && r.act == p.act
215+
''');
216+
217+
final enforcer = Enforcer.initWithModelAndAdapter(model);
218+
219+
// Add policies with multiple attribute conditions
220+
enforcer.addPolicy([
221+
'{"name": "user", "age": 18, "role": "user"}',
222+
'/admin',
223+
'write',
224+
'r.sub.role == "admin" && r.sub.age >= 21'
225+
]);
226+
227+
enforcer.addPolicy([
228+
'{"name": "user", "age": 18, "role": "user"}',
229+
'/data',
230+
'read',
231+
'r.sub.age >= 18'
232+
]);
233+
234+
// In a Flutter app, this data typically comes from user authentication
235+
String subJson = jsonEncode({
236+
"name": userName,
237+
"age": userAge,
238+
"role": userRole,
239+
});
240+
241+
try {
242+
bool hasAccess = enforcer.enforce([subJson, resource, action]);
243+
print('Result: ${hasAccess ? "✅ Access Granted" : "❌ Access Denied"}');
244+
} catch (e) {
245+
print('⚠️ Error: $e');
246+
}
247+
}
248+
249+
void main() {
250+
print('=== Flutter ABAC Examples with JSON Strings ===');
251+
252+
// Simple examples
253+
simulateFlutterAccessCheck(25, '/data1', 'read');
254+
simulateFlutterAccessCheck(16, '/data1', 'read');
255+
simulateFlutterAccessCheck(65, '/data1', 'read');
256+
257+
// Complex examples with multiple attributes
258+
simulateComplexFlutterAccessCheck('Alice', 25, 'admin', '/admin', 'write');
259+
simulateComplexFlutterAccessCheck('Bob', 20, 'admin', '/admin', 'write');
260+
simulateComplexFlutterAccessCheck('Charlie', 25, 'user', '/admin', 'write');
261+
simulateComplexFlutterAccessCheck('David', 19, 'user', '/data', 'read');
262+
263+
print('\n=== Examples Complete ===');
264+
print('\nNote: This demonstrates the same functionality described in the GitHub issue.');
265+
print('Users can now pass JSON strings directly to Casbin without creating custom classes!');
266+
}

0 commit comments

Comments
 (0)