firestore_access_policy 0.3.1
firestore_access_policy: ^0.3.1 copied to clipboard
Declarative Firestore access policies in Dart — generate security rules, tests, and optional client guards from a single CRUD + membership model.
example/firestore_access_policy_example.dart
import 'package:firestore_access_policy/firestore_access_policy.dart';
/// Example: generate rules and write to a **separate** file (never overwrites
/// hand-written `firestore.rules` unless you pass [RulesWriteIfExists.overwrite]).
Future<void> main() async {
const generation = RulesGeneration();
final file = FirestoreRulesFile(
headerComment: RulesFileDefaults.firestoreHeaderComment,
helpers: [
...MemberDiffPatterns.standardListMemberHelpers(),
ParentResourcePatterns.groupMemberByIdHelper(),
],
policies: [
AccessPolicy(
path: ResourcePath.parse('lists/{listId}'),
description: 'Collaborative lists',
rules: {
PolicyAction.read: [
PolicyRule(
And([
Authenticated(),
ParentResourcePatterns.listMemberOrParentGroup(),
]),
),
],
PolicyAction.update: [
PolicyRule(
And([
InMapKeys('members'),
CallHelper('listUpdateKeepsField'),
MemberDiffPatterns.allowedMemberMapUpdate(),
]),
),
],
PolicyAction.delete: [
PolicyRule(AuthUidEqualsField('createdBy')),
],
},
),
],
);
// Safe default: fails if firestore.generated.rules already exists.
final result = await generation.writeFirestore(
file,
const RulesOutputTarget(path: 'firestore.generated.rules'),
);
// ignore: avoid_print
print(result.wasWritten
? 'Wrote ${result.path}'
: 'Skipped ${result.path} (already exists)');
final testSource = const RulesTestGenerator().generate(
packageName: 'my_app',
policies: file.policies,
cases: [
RulesTestCase(
description: 'list creator can delete',
path: 'lists/abc',
action: PolicyAction.delete,
expectAllowed: true,
authUid: 'creatorUid',
),
],
);
await const RulesFileWriter().writeString(
testSource,
const RulesOutputTarget(
path: 'test/generated_rules_test.dart',
ifExists: RulesWriteIfExists.overwrite,
),
);
}