From eb8aeadbe3653f5232dafa8853433b6a07a62471 Mon Sep 17 00:00:00 2001 From: Dillon Nys Date: Sat, 9 Mar 2024 10:20:06 -0800 Subject: [PATCH] feat: Add `package:cedar` Adds `package:cedar` which includes Dart representations of the Cedar policy language and shared interfaces which will be implemented in `package:cedar_ffi`. --- packages/cedar/.gitignore | 7 + packages/cedar/CHANGELOG.md | 3 + packages/cedar/README.md | 7 + packages/cedar/analysis_options.yaml | 1 + packages/cedar/lib/cedar.dart | 26 + packages/cedar/lib/src/ast/cedar_entity.dart | 54 + .../cedar/lib/src/ast/cedar_entity.g.dart | 201 +++ .../cedar/lib/src/ast/cedar_entity_id.dart | 68 + .../cedar/lib/src/ast/cedar_entity_id.g.dart | 145 ++ packages/cedar/lib/src/ast/cedar_schema.dart | 495 +++++++ .../cedar_authorization_request.dart | 26 + .../cedar_authorization_response.dart | 37 + .../src/authorization/cedar_authorizer.dart | 8 + .../cedar/lib/src/policy/cedar_policy.dart | 420 ++++++ .../cedar/lib/src/policy/cedar_policy.g.dart | 1170 +++++++++++++++++ .../lib/src/policy/cedar_policy_set.dart | 40 + .../lib/src/policy/cedar_policy_set.g.dart | 155 +++ packages/cedar/lib/src/policy/json_expr.dart | 1161 ++++++++++++++++ packages/cedar/lib/src/serializers.dart | 21 + packages/cedar/lib/src/serializers.g.dart | 45 + packages/cedar/lib/src/util.dart | 8 + packages/cedar/pubspec.yaml | 19 + 22 files changed, 4117 insertions(+) create mode 100644 packages/cedar/.gitignore create mode 100644 packages/cedar/CHANGELOG.md create mode 100644 packages/cedar/README.md create mode 100644 packages/cedar/analysis_options.yaml create mode 100644 packages/cedar/lib/cedar.dart create mode 100644 packages/cedar/lib/src/ast/cedar_entity.dart create mode 100644 packages/cedar/lib/src/ast/cedar_entity.g.dart create mode 100644 packages/cedar/lib/src/ast/cedar_entity_id.dart create mode 100644 packages/cedar/lib/src/ast/cedar_entity_id.g.dart create mode 100644 packages/cedar/lib/src/ast/cedar_schema.dart create mode 100644 packages/cedar/lib/src/authorization/cedar_authorization_request.dart create mode 100644 packages/cedar/lib/src/authorization/cedar_authorization_response.dart create mode 100644 packages/cedar/lib/src/authorization/cedar_authorizer.dart create mode 100644 packages/cedar/lib/src/policy/cedar_policy.dart create mode 100644 packages/cedar/lib/src/policy/cedar_policy.g.dart create mode 100644 packages/cedar/lib/src/policy/cedar_policy_set.dart create mode 100644 packages/cedar/lib/src/policy/cedar_policy_set.g.dart create mode 100644 packages/cedar/lib/src/policy/json_expr.dart create mode 100644 packages/cedar/lib/src/serializers.dart create mode 100644 packages/cedar/lib/src/serializers.g.dart create mode 100644 packages/cedar/lib/src/util.dart create mode 100644 packages/cedar/pubspec.yaml diff --git a/packages/cedar/.gitignore b/packages/cedar/.gitignore new file mode 100644 index 00000000..3cceda55 --- /dev/null +++ b/packages/cedar/.gitignore @@ -0,0 +1,7 @@ +# https://dart.dev/guides/libraries/private-files +# Created by `dart pub` +.dart_tool/ + +# Avoid committing pubspec.lock for library packages; see +# https://dart.dev/guides/libraries/private-files#pubspeclock. +pubspec.lock diff --git a/packages/cedar/CHANGELOG.md b/packages/cedar/CHANGELOG.md new file mode 100644 index 00000000..a0712a79 --- /dev/null +++ b/packages/cedar/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.1.0 + +- Initial version. diff --git a/packages/cedar/README.md b/packages/cedar/README.md new file mode 100644 index 00000000..2ccdb3d2 --- /dev/null +++ b/packages/cedar/README.md @@ -0,0 +1,7 @@ +# cedar + +Core types and interfaces of the [Cedar](https://www.cedarpolicy.com/en) policy language in Dart. + +Cedar is a policy language for defining and enforcing access control policies in a declarative way. It is used in [Celest](https://celest.dev) for ensuring that only authorized users meeting certain criteria can access your backend. + +The actual implementation of the Cedar engine is in [cedar_ffi](https://pub.dev/packages/cedar_ffi), which uses FFI and Dart's native assets feature to bind to the Rust implementation of Cedar. This package only holds the Dart AST representation and is separate from `package:cedar_ffi` so that the types can be used without bundling the native assets of `package:cedar_ffi`. diff --git a/packages/cedar/analysis_options.yaml b/packages/cedar/analysis_options.yaml new file mode 100644 index 00000000..572dd239 --- /dev/null +++ b/packages/cedar/analysis_options.yaml @@ -0,0 +1 @@ +include: package:lints/recommended.yaml diff --git a/packages/cedar/lib/cedar.dart b/packages/cedar/lib/cedar.dart new file mode 100644 index 00000000..25306717 --- /dev/null +++ b/packages/cedar/lib/cedar.dart @@ -0,0 +1,26 @@ +/// Core types and interfaces of the [Cedar](https://www.cedarpolicy.com/en) +/// policy language in Dart. +/// +/// Cedar is a policy language for defining and enforcing access control +/// policies in a declarative way. It is used in [Celest](https://celest.dev) +/// for ensuring that only authorized users meeting certain criteria can access +/// your backend. +/// +/// The actual implementation of the Cedar engine is in +/// [cedar_ffi](https://pub.dev/packages/cedar_ffi), which uses FFI and Dart's +/// native assets feature to bind to the Rust implementation of Cedar. This +/// package only holds the Dart AST representation and is separate from +/// `package:cedar_ffi` so that the types can be used without bundling the +/// native assets of `package:cedar_ffi`. +library; + +export 'src/ast/cedar_entity.dart'; +export 'src/ast/cedar_entity_id.dart'; +export 'src/ast/cedar_schema.dart'; +export 'src/authorization/cedar_authorization_request.dart'; +export 'src/authorization/cedar_authorization_response.dart'; +export 'src/authorization/cedar_authorizer.dart'; +export 'src/policy/cedar_policy.dart'; +export 'src/policy/cedar_policy_set.dart'; +export 'src/policy/json_expr.dart'; +export 'src/serializers.dart'; diff --git a/packages/cedar/lib/src/ast/cedar_entity.dart b/packages/cedar/lib/src/ast/cedar_entity.dart new file mode 100644 index 00000000..b3271f03 --- /dev/null +++ b/packages/cedar/lib/src/ast/cedar_entity.dart @@ -0,0 +1,54 @@ +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/built_value.dart'; +import 'package:built_value/serializer.dart'; +import 'package:cedar/src/ast/cedar_entity_id.dart'; +import 'package:cedar/src/policy/json_expr.dart'; + +part 'cedar_entity.g.dart'; + +/// Dart representation of a Cedar [entity](https://docs.cedarpolicy.com/policies/syntax-entity.html). +/// +/// Conforms to the entity [JSON format](https://docs.cedarpolicy.com/auth/entities-syntax.html#entities). +abstract class CedarEntity implements Built { + factory CedarEntity({ + required CedarEntityId id, + List parents = const [], + Map attributes = const {}, + }) { + return _$CedarEntity._( + id: id, + parents: parents.build(), + attributes: attributes.build(), + ); + } + + factory CedarEntity.build([ + void Function(CedarEntityBuilder) updates, + ]) = _$CedarEntity; + + factory CedarEntity.fromJson(Map json) => CedarEntity( + id: CedarEntityId.fromJson(json['uid'] as Map), + parents: (json['parents'] as List) + .map((e) => CedarEntityId.fromJson(e as Map)) + .toList(), + attributes: (json['attrs'] as Map) + .cast() + .map((key, value) => MapEntry(key, CedarValueJson.fromJson(value))), + ); + + const CedarEntity._(); + + CedarEntityId get id; + BuiltList get parents; + BuiltMap get attributes; + + Map toJson() => { + 'uid': id.toJson(), + 'parents': parents.map((e) => e.toJson()).toList(), + 'attrs': attributes + .map((key, value) => MapEntry(key, value.toJson())) + .asMap(), + }; + + static Serializer get serializer => _$cedarEntitySerializer; +} diff --git a/packages/cedar/lib/src/ast/cedar_entity.g.dart b/packages/cedar/lib/src/ast/cedar_entity.g.dart new file mode 100644 index 00000000..16c92876 --- /dev/null +++ b/packages/cedar/lib/src/ast/cedar_entity.g.dart @@ -0,0 +1,201 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'cedar_entity.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +Serializer _$cedarEntitySerializer = new _$CedarEntitySerializer(); + +class _$CedarEntitySerializer implements StructuredSerializer { + @override + final Iterable types = const [CedarEntity, _$CedarEntity]; + @override + final String wireName = 'CedarEntity'; + + @override + Iterable serialize(Serializers serializers, CedarEntity object, + {FullType specifiedType = FullType.unspecified}) { + final result = [ + 'id', + serializers.serialize(object.id, + specifiedType: const FullType(CedarEntityId)), + 'parents', + serializers.serialize(object.parents, + specifiedType: + const FullType(BuiltList, const [const FullType(CedarEntityId)])), + 'attributes', + serializers.serialize(object.attributes, + specifiedType: const FullType(BuiltMap, + const [const FullType(String), const FullType(CedarValueJson)])), + ]; + + return result; + } + + @override + CedarEntity deserialize(Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = new CedarEntityBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'id': + result.id.replace(serializers.deserialize(value, + specifiedType: const FullType(CedarEntityId))! as CedarEntityId); + break; + case 'parents': + result.parents.replace(serializers.deserialize(value, + specifiedType: const FullType( + BuiltList, const [const FullType(CedarEntityId)]))! + as BuiltList); + break; + case 'attributes': + result.attributes.replace(serializers.deserialize(value, + specifiedType: const FullType(BuiltMap, const [ + const FullType(String), + const FullType(CedarValueJson) + ]))!); + break; + } + } + + return result.build(); + } +} + +class _$CedarEntity extends CedarEntity { + @override + final CedarEntityId id; + @override + final BuiltList parents; + @override + final BuiltMap attributes; + + factory _$CedarEntity([void Function(CedarEntityBuilder)? updates]) => + (new CedarEntityBuilder()..update(updates))._build(); + + _$CedarEntity._( + {required this.id, required this.parents, required this.attributes}) + : super._() { + BuiltValueNullFieldError.checkNotNull(id, r'CedarEntity', 'id'); + BuiltValueNullFieldError.checkNotNull(parents, r'CedarEntity', 'parents'); + BuiltValueNullFieldError.checkNotNull( + attributes, r'CedarEntity', 'attributes'); + } + + @override + CedarEntity rebuild(void Function(CedarEntityBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + CedarEntityBuilder toBuilder() => new CedarEntityBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is CedarEntity && + id == other.id && + parents == other.parents && + attributes == other.attributes; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, id.hashCode); + _$hash = $jc(_$hash, parents.hashCode); + _$hash = $jc(_$hash, attributes.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'CedarEntity') + ..add('id', id) + ..add('parents', parents) + ..add('attributes', attributes)) + .toString(); + } +} + +class CedarEntityBuilder implements Builder { + _$CedarEntity? _$v; + + CedarEntityIdBuilder? _id; + CedarEntityIdBuilder get id => _$this._id ??= new CedarEntityIdBuilder(); + set id(CedarEntityIdBuilder? id) => _$this._id = id; + + ListBuilder? _parents; + ListBuilder get parents => + _$this._parents ??= new ListBuilder(); + set parents(ListBuilder? parents) => _$this._parents = parents; + + MapBuilder? _attributes; + MapBuilder get attributes => + _$this._attributes ??= new MapBuilder(); + set attributes(MapBuilder? attributes) => + _$this._attributes = attributes; + + CedarEntityBuilder(); + + CedarEntityBuilder get _$this { + final $v = _$v; + if ($v != null) { + _id = $v.id.toBuilder(); + _parents = $v.parents.toBuilder(); + _attributes = $v.attributes.toBuilder(); + _$v = null; + } + return this; + } + + @override + void replace(CedarEntity other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$CedarEntity; + } + + @override + void update(void Function(CedarEntityBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + CedarEntity build() => _build(); + + _$CedarEntity _build() { + _$CedarEntity _$result; + try { + _$result = _$v ?? + new _$CedarEntity._( + id: id.build(), + parents: parents.build(), + attributes: attributes.build()); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'id'; + id.build(); + _$failedField = 'parents'; + parents.build(); + _$failedField = 'attributes'; + attributes.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'CedarEntity', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/packages/cedar/lib/src/ast/cedar_entity_id.dart b/packages/cedar/lib/src/ast/cedar_entity_id.dart new file mode 100644 index 00000000..1cecc9f0 --- /dev/null +++ b/packages/cedar/lib/src/ast/cedar_entity_id.dart @@ -0,0 +1,68 @@ +import 'package:built_value/built_value.dart'; +import 'package:built_value/serializer.dart'; + +part 'cedar_entity_id.g.dart'; + +abstract class CedarEntityId + implements Built { + factory CedarEntityId(String type, String id) => + _$CedarEntityId._(type: type, id: id); + + factory CedarEntityId.build([ + void Function(CedarEntityIdBuilder) updates, + ]) = _$CedarEntityId; + + factory CedarEntityId.fromJson(Map json) { + switch (json) { + case {'type': final String type, 'id': final String id} || + {'__entity': {'type': final String type, 'id': final String id}}: + return CedarEntityId(type, id); + default: + throw FormatException('Invalid entity ID JSON: $json'); + } + } + + const CedarEntityId._(); + + String get type; + String get id; + + /// Returns a normalized version of this entity ID. + /// + /// Cedar prohibits whitespace in entity IDs, so this method removes all + /// whitespace from the [type] and [id]. + /// + /// See Cedar [RFC 9](https://github.com/cedar-policy/rfcs/blob/main/text/0009-disallow-whitespace-in-entityuid.md) + /// for more information. + CedarEntityId get normalized => CedarEntityId( + type, + String.fromCharCodes( + id.runes.expand((char) { + return switch (char) { + 0 => '\\0'.codeUnits, + 0x9 => '\\t'.codeUnits, + 0xa => '\\n'.codeUnits, + 0xd => '\\r'.codeUnits, + 0x22 => '\\"'.codeUnits, + 0x27 => "\\'".codeUnits, + < 0x20 || + 0x7f || // Delete + 0x96 || // Non-breaking space + > 0xffff => + '\\u{${char.toRadixString(16)}}'.codeUnits, + _ => [char], + }; + }), + ), + ); + + @override + String toString() => '$type::"$id"'; + + Map toJson() => { + 'type': type, + 'id': id, + }; + + static Serializer get serializer => _$cedarEntityIdSerializer; +} diff --git a/packages/cedar/lib/src/ast/cedar_entity_id.g.dart b/packages/cedar/lib/src/ast/cedar_entity_id.g.dart new file mode 100644 index 00000000..0e6e3615 --- /dev/null +++ b/packages/cedar/lib/src/ast/cedar_entity_id.g.dart @@ -0,0 +1,145 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'cedar_entity_id.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +Serializer _$cedarEntityIdSerializer = + new _$CedarEntityIdSerializer(); + +class _$CedarEntityIdSerializer implements StructuredSerializer { + @override + final Iterable types = const [CedarEntityId, _$CedarEntityId]; + @override + final String wireName = 'CedarEntityId'; + + @override + Iterable serialize(Serializers serializers, CedarEntityId object, + {FullType specifiedType = FullType.unspecified}) { + final result = [ + 'type', + serializers.serialize(object.type, specifiedType: const FullType(String)), + 'id', + serializers.serialize(object.id, specifiedType: const FullType(String)), + ]; + + return result; + } + + @override + CedarEntityId deserialize( + Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = new CedarEntityIdBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'type': + result.type = serializers.deserialize(value, + specifiedType: const FullType(String))! as String; + break; + case 'id': + result.id = serializers.deserialize(value, + specifiedType: const FullType(String))! as String; + break; + } + } + + return result.build(); + } +} + +class _$CedarEntityId extends CedarEntityId { + @override + final String type; + @override + final String id; + + factory _$CedarEntityId([void Function(CedarEntityIdBuilder)? updates]) => + (new CedarEntityIdBuilder()..update(updates))._build(); + + _$CedarEntityId._({required this.type, required this.id}) : super._() { + BuiltValueNullFieldError.checkNotNull(type, r'CedarEntityId', 'type'); + BuiltValueNullFieldError.checkNotNull(id, r'CedarEntityId', 'id'); + } + + @override + CedarEntityId rebuild(void Function(CedarEntityIdBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + CedarEntityIdBuilder toBuilder() => new CedarEntityIdBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is CedarEntityId && type == other.type && id == other.id; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, type.hashCode); + _$hash = $jc(_$hash, id.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } +} + +class CedarEntityIdBuilder + implements Builder { + _$CedarEntityId? _$v; + + String? _type; + String? get type => _$this._type; + set type(String? type) => _$this._type = type; + + String? _id; + String? get id => _$this._id; + set id(String? id) => _$this._id = id; + + CedarEntityIdBuilder(); + + CedarEntityIdBuilder get _$this { + final $v = _$v; + if ($v != null) { + _type = $v.type; + _id = $v.id; + _$v = null; + } + return this; + } + + @override + void replace(CedarEntityId other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$CedarEntityId; + } + + @override + void update(void Function(CedarEntityIdBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + CedarEntityId build() => _build(); + + _$CedarEntityId _build() { + final _$result = _$v ?? + new _$CedarEntityId._( + type: BuiltValueNullFieldError.checkNotNull( + type, r'CedarEntityId', 'type'), + id: BuiltValueNullFieldError.checkNotNull( + id, r'CedarEntityId', 'id')); + replace(_$result); + return _$result; + } +} + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/packages/cedar/lib/src/ast/cedar_schema.dart b/packages/cedar/lib/src/ast/cedar_schema.dart new file mode 100644 index 00000000..3c7afa4f --- /dev/null +++ b/packages/cedar/lib/src/ast/cedar_schema.dart @@ -0,0 +1,495 @@ +import 'package:cedar/src/ast/cedar_entity_id.dart'; +import 'package:cedar/src/util.dart'; + +/// Dart representation of a Cedar [schema](https://docs.cedarpolicy.com/schema/schema.html). +final class CedarSchema { + CedarSchema({ + Map? namespaces, + }) : _namespaces = namespaces ?? {}; + + final Map _namespaces; + + factory CedarSchema.fromJson(Map json) { + return CedarSchema( + namespaces: json.map( + (name, json) => MapEntry( + name, + CedarNamespace.fromJson(json as Map), + ), + ), + ); + } + + CedarNamespace? getNamespace(String name) { + return _namespaces[name]; + } + + void updateNamespace( + String name, CedarNamespace Function(CedarNamespace) updates) { + _namespaces.update( + name, + (value) => updates(value), + ifAbsent: () => updates(CedarNamespace()), + ); + } + + Map toJson() => _namespaces.map( + (name, namespace) => MapEntry(name, namespace.toJson()), + ); +} + +final class CedarNamespace { + CedarNamespace({ + Map? entityTypes, + Map? actionTypes, + Map? commonTypes, + }) : _entityTypes = entityTypes, + _actionTypes = actionTypes, + _commonTypes = commonTypes; + + factory CedarNamespace.fromJson(Map json) { + return CedarNamespace( + entityTypes: (json['entityTypes'] as Map?) + ?.cast() + .map( + (name, json) => MapEntry( + name, + CedarEntitySchema.fromJson( + (json as Map).cast()), + ), + ), + actionTypes: (json['actions'] as Map?) + ?.cast() + .map( + (name, json) => MapEntry( + name, + CedarActionSchema.fromJson( + (json as Map).cast()), + ), + ), + commonTypes: (json['commonTypes'] as Map?) + ?.cast() + .map( + (name, json) => MapEntry( + name, + CedarType.fromJson((json as Map).cast()), + ), + ), + ); + } + + Map? _entityTypes; + Map? _actionTypes; + Map? _commonTypes; + + void addEntitySchema(String name, CedarEntitySchema entityType) { + (_entityTypes ??= {}).update( + name, + (value) => throw StateError('Entity type "$name" already exists'), + ifAbsent: () => entityType, + ); + } + + void addActionSchema(String name, CedarActionSchema actionType) { + (_actionTypes ??= {}).update( + name, + (value) => throw StateError('Action type "$name" already exists'), + ifAbsent: () => actionType, + ); + } + + void addCommonType(String name, CedarTypeDefinition type) { + (_commonTypes ??= {}).update( + name, + (value) => throw StateError('Common type "$name" already exists'), + ifAbsent: () => type, + ); + } + + Map toJson() => { + if (_entityTypes != null) + 'entityTypes': _entityTypes!.map( + (name, entityType) => MapEntry(name, entityType.toJson()), + ), + if (_actionTypes != null) + 'actions': _actionTypes!.map( + (name, actionType) => MapEntry(name, actionType.toJson()), + ), + if (_commonTypes != null) + 'commonTypes': _commonTypes!.map( + (name, type) => MapEntry(name, type.toJson()), + ), + }; +} + +final class CedarEntitySchema { + const CedarEntitySchema({ + this.memberOfTypes, + this.shape, + }); + + factory CedarEntitySchema.fromJson(Map json) { + return CedarEntitySchema( + memberOfTypes: + (json['memberOfTypes'] as List?)?.cast().toList(), + shape: (json['shape'] as Map?)?.let(CedarType.fromJson), + ); + } + + final List? memberOfTypes; + final CedarType? shape; + + Map toJson() => { + if (memberOfTypes != null) 'memberOfTypes': memberOfTypes, + if (shape != null) 'shape': shape!.toJson(), + }; +} + +sealed class CedarType { + factory CedarType.fromJson(Map json) { + switch (json) { + case {'type': 'Boolean'}: + return CedarBooleanType( + required: json['required'] as bool?, + ); + case {'type': 'String'}: + return CedarStringType( + required: json['required'] as bool?, + ); + case {'type': 'Long'}: + return CedarLongType( + required: json['required'] as bool?, + ); + case {'type': 'Set'}: + return CedarSetType( + elementType: CedarType.fromJson( + json['element'] as Map, + ), + required: json['required'] as bool?, + ); + case {'type': 'Record'}: + return CedarRecordType( + attributes: (json['attributes'] as Map) + .cast() + .map( + (name, json) => MapEntry( + name, + CedarType.fromJson(json as Map), + ), + ), + required: json['required'] as bool?, + additionalAttributes: json['additionalAttributes'] as bool?, + ); + case {'type': 'Entity'}: + return CedarEntityType( + entityName: json['name'] as String, + required: json['required'] as bool?, + ); + case {'type': 'Extension', 'name': 'ipaddr'}: + return CedarIpAddressType( + required: json['required'] as bool?, + ); + case {'type': 'Extension', 'name': 'decimal'}: + return CedarDecimalType( + required: json['required'] as bool?, + ); + case {'type': final String type}: + if (json.keys.length > 1) { + throw ArgumentError.value(json, 'json', 'Invalid Cedar type'); + } + return CedarTypeReference(type: type); + default: + throw ArgumentError.value(json, 'json', 'Invalid Cedar type'); + } + } + + const factory CedarType.boolean({ + bool required, + }) = CedarBooleanType; + + const factory CedarType.string({ + bool required, + }) = CedarStringType; + + const factory CedarType.long({ + bool required, + }) = CedarLongType; + + const factory CedarType.set({ + required CedarType elementType, + bool required, + }) = CedarSetType; + + const factory CedarType.record({ + required Map attributes, + bool required, + }) = CedarRecordType; + + const factory CedarType.entity({ + required String entityName, + bool required, + }) = CedarEntityType; + + const factory CedarType.ipAddress({ + bool required, + }) = CedarIpAddressType; + + const factory CedarType.decimal({ + bool required, + }) = CedarDecimalType; + + const factory CedarType.reference({ + required String type, + }) = CedarTypeReference; + + Map toJson(); +} + +final class CedarTypeReference implements CedarType { + const CedarTypeReference({ + required this.type, + }); + + factory CedarTypeReference.fromJson(Map json) { + return CedarTypeReference(type: json['type'] as String); + } + + final String type; + + @override + Map toJson() => { + 'type': type, + }; +} + +sealed class CedarTypeDefinition implements CedarType { + const CedarTypeDefinition({ + this.required, + }); + + /// Whether a value of this type is required. + /// + /// Defaults to `true`. + final bool? required; + + const factory CedarTypeDefinition.boolean({ + bool required, + }) = CedarBooleanType; + + const factory CedarTypeDefinition.string({ + bool required, + }) = CedarStringType; + + const factory CedarTypeDefinition.long({ + bool required, + }) = CedarLongType; + + const factory CedarTypeDefinition.set({ + required CedarType elementType, + bool required, + }) = CedarSetType; + + const factory CedarTypeDefinition.record({ + required Map attributes, + bool required, + }) = CedarRecordType; + + const factory CedarTypeDefinition.entity({ + required String entityName, + bool required, + }) = CedarEntityType; + + const factory CedarTypeDefinition.ipAddress({ + bool required, + }) = CedarIpAddressType; + + const factory CedarTypeDefinition.decimal({ + bool required, + }) = CedarDecimalType; +} + +final class CedarBooleanType extends CedarTypeDefinition { + const CedarBooleanType({ + super.required, + }); + + @override + Map toJson() => { + 'type': 'Boolean', + if (required != null) 'required': required, + }; +} + +final class CedarStringType extends CedarTypeDefinition { + const CedarStringType({ + super.required, + }); + + @override + Map toJson() => { + 'type': 'String', + if (required != null) 'required': required, + }; +} + +final class CedarLongType extends CedarTypeDefinition { + const CedarLongType({ + super.required, + }); + + @override + Map toJson() => { + 'type': 'Long', + if (required != null) 'required': required, + }; +} + +final class CedarSetType extends CedarTypeDefinition { + const CedarSetType({ + required this.elementType, + super.required, + }); + + /// The type of the elements in the set. + final CedarType elementType; + + @override + Map toJson() => { + 'type': 'Set', + if (required != null) 'required': required, + 'element': elementType.toJson(), + }; +} + +final class CedarRecordType extends CedarTypeDefinition { + const CedarRecordType({ + required this.attributes, + super.required, + this.additionalAttributes, + }); + + final Map attributes; + // TODO: What is this used for? + final bool? additionalAttributes; + + @override + Map toJson() => { + 'type': 'Record', + if (required != null) 'required': required, + 'attributes': attributes.map( + (name, type) => MapEntry(name, type.toJson()), + ), + if (additionalAttributes != null) + 'additionalAttributes': additionalAttributes, + }; +} + +final class CedarEntityType extends CedarTypeDefinition { + const CedarEntityType({ + required this.entityName, + super.required, + }); + + /// The namespaced name of the entity type. + final String entityName; + + @override + Map toJson() => { + 'type': 'Entity', + if (required != null) 'required': required, + 'name': entityName, + }; +} + +final class CedarIpAddressType extends CedarTypeDefinition { + const CedarIpAddressType({ + super.required, + }); + + @override + Map toJson() => { + 'type': 'Extension', + if (required != null) 'required': required, + 'name': 'ipaddr', + }; +} + +final class CedarDecimalType extends CedarTypeDefinition { + const CedarDecimalType({ + super.required, + }); + + @override + Map toJson() => { + 'type': 'Extension', + if (required != null) 'required': required, + 'name': 'decimal', + }; +} + +final class CedarActionSchema { + const CedarActionSchema({ + this.memberOf, + required this.appliesTo, + }); + + factory CedarActionSchema.fromJson(Map json) { + return CedarActionSchema( + memberOf: (json['memberOf'] as List?) + ?.map((json) => CedarEntityId.fromJson(json as Map)) + .toList(), + appliesTo: switch (json['appliesTo']) { + null => null, + final Map json => CedarActionAppliesTo.fromJson( + json.cast(), + ), + _ => throw ArgumentError.value( + json, + 'json', + 'Invalid Cedar action schema', + ), + }, + ); + } + + final List? memberOf; + final CedarActionAppliesTo? appliesTo; + + Map toJson() => { + 'memberOf': memberOf?.map((e) => e.toJson()).toList(), + 'appliesTo': appliesTo?.toJson(), + }; +} + +final class CedarActionAppliesTo { + const CedarActionAppliesTo({ + this.principalTypes, + this.resourceTypes, + this.contextType, + }); + + factory CedarActionAppliesTo.fromJson(Map json) { + return CedarActionAppliesTo( + principalTypes: (json['principalTypes'] as List?)?.cast(), + resourceTypes: (json['resourceTypes'] as List?)?.cast(), + contextType: json['context'] == null + ? null + : CedarType.fromJson( + (json['context'] as Map).cast(), + ), + ); + } + + final List? principalTypes; + final List? resourceTypes; + + /// Must be a [CedarRecordType] or a [CedarTypeReference] to a + /// [CedarRecordType]. + final CedarType? contextType; + + Map toJson() => { + 'principalTypes': principalTypes, + 'resourceTypes': resourceTypes, + if (contextType != null) 'context': contextType!.toJson(), + }; +} diff --git a/packages/cedar/lib/src/authorization/cedar_authorization_request.dart b/packages/cedar/lib/src/authorization/cedar_authorization_request.dart new file mode 100644 index 00000000..5c2655cb --- /dev/null +++ b/packages/cedar/lib/src/authorization/cedar_authorization_request.dart @@ -0,0 +1,26 @@ +import 'package:cedar/cedar.dart'; + +/// {@template cedar.cedar_authorization_request} +/// A request for authorization to a [CedarEngine]. +/// {@endtemplate} +final class CedarAuthorizationRequest { + /// {@macro cedar.cedar_authorization_request} + const CedarAuthorizationRequest({ + this.principal, + this.action, + this.resource, + this.context, + }); + + /// The principal component of the request. + final CedarEntityId? principal; + + /// The action component of the request. + final CedarEntityId? action; + + /// The resource component of the request. + final CedarEntityId? resource; + + /// The context of the request. + final Map? context; +} diff --git a/packages/cedar/lib/src/authorization/cedar_authorization_response.dart b/packages/cedar/lib/src/authorization/cedar_authorization_response.dart new file mode 100644 index 00000000..86c3f702 --- /dev/null +++ b/packages/cedar/lib/src/authorization/cedar_authorization_response.dart @@ -0,0 +1,37 @@ +import 'package:cedar/cedar.dart'; +import 'package:json_annotation/json_annotation.dart'; + +/// The decision of an authorization request. +enum CedarAuthorizationDecision { + @JsonValue('Allow') + allow, + + @JsonValue('Deny') + deny, +} + +/// {@template cedar.cedar_authorization_response} +/// The response to a [CedarAuthorizer] request. +/// {@endtemplate} +final class CedarAuthorizationResponse { + /// {@macro cedar.cedar_authorization_response} + const CedarAuthorizationResponse({ + required this.decision, + List? reasons, + List? errors, + }) : reasons = reasons ?? const [], + errorMessages = errors ?? const []; + + /// The decision of the authorization request. + final CedarAuthorizationDecision decision; + + /// The policy IDs of the policies that contributed to the decision. + /// + /// If no policies applied to the request, this will be empty. + final List reasons; + + /// Any evaluation errors which occurred during the request. + /// + /// If no errors occurred, this will be empty. + final List errorMessages; +} diff --git a/packages/cedar/lib/src/authorization/cedar_authorizer.dart b/packages/cedar/lib/src/authorization/cedar_authorizer.dart new file mode 100644 index 00000000..8eb75f55 --- /dev/null +++ b/packages/cedar/lib/src/authorization/cedar_authorizer.dart @@ -0,0 +1,8 @@ +import 'package:cedar/cedar.dart'; + +abstract interface class CedarAuthorizer { + /// Responds to an authorization [request]. + CedarAuthorizationResponse isAuthorized( + CedarAuthorizationRequest request, + ); +} diff --git a/packages/cedar/lib/src/policy/cedar_policy.dart b/packages/cedar/lib/src/policy/cedar_policy.dart new file mode 100644 index 00000000..37b2a6a2 --- /dev/null +++ b/packages/cedar/lib/src/policy/cedar_policy.dart @@ -0,0 +1,420 @@ +/// Provides builders and serializers for Cedar policies which conform to the +/// official JSON format. +/// +/// See: +/// - https://docs.cedarpolicy.com/auth/authorization.html +/// - https://docs.cedarpolicy.com/policies/json-format.html +library; + +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/built_value.dart'; +import 'package:built_value/json_object.dart'; +import 'package:built_value/serializer.dart'; +import 'package:cedar/cedar.dart'; + +part 'cedar_policy.g.dart'; + +class CedarPolicyEffect extends EnumClass { + const CedarPolicyEffect._(super.name); + + static const CedarPolicyEffect permit = _$permit; + static const CedarPolicyEffect forbid = _$forbid; + + static BuiltSet get values => _$CedarPolicyEffectValues; + static CedarPolicyEffect valueOf(String name) => + _$CedarPolicyEffectValueOf(name); + + static CedarPolicyEffect fromJson(String json) => + _$CedarPolicyEffectValueOf(json); + + String toJson() => name; + + static Serializer get serializer => + _$cedarPolicyEffectSerializer; +} + +class CedarPolicyOp extends EnumClass { + const CedarPolicyOp._(super.name); + + @BuiltValueEnumConst(wireName: 'All') + static const CedarPolicyOp all = _$all; + + @BuiltValueEnumConst(wireName: '==') + static const CedarPolicyOp equals = _$equals; + + @BuiltValueEnumConst(wireName: 'in') + static const CedarPolicyOp in$ = _$in$; + + @BuiltValueEnumConst(wireName: 'is') + static const CedarPolicyOp is$ = _$is$; + + static BuiltSet get values => _$CedarPolicyOpValues; + static CedarPolicyOp valueOf(String name) => _$CedarPolicyOpValueOf(name); + + static CedarPolicyOp fromJson(String json) => + cedarSerializers.deserializeWith(CedarPolicyOp.serializer, json)!; + + String toJson() => + cedarSerializers.serializeWith(CedarPolicyOp.serializer, this) as String; + + static Serializer get serializer => _$cedarPolicyOpSerializer; +} + +class CedarPolicyConditionKind extends EnumClass { + const CedarPolicyConditionKind._(super.name); + + static const CedarPolicyConditionKind when = _$when; + static const CedarPolicyConditionKind unless = _$unless; + + static BuiltSet get values => + _$CedarPolicyConditionKindValues; + static CedarPolicyConditionKind valueOf(String name) => + _$CedarPolicyConditionKindValueOf(name); + + static CedarPolicyConditionKind fromJson(String json) => + _$CedarPolicyConditionKindValueOf(json); + + String toJson() => name; + + static Serializer get serializer => + _$cedarPolicyConditionKindSerializer; +} + +abstract class CedarPolicy implements Built { + factory CedarPolicy({ + required CedarPolicyEffect effect, + required CedarPolicyPrincipal principal, + required CedarPolicyAction action, + required CedarPolicyResource resource, + List conditions = const [], + Map? annotations, + }) { + return _$CedarPolicy._( + effect: effect, + principal: principal, + action: action, + resource: resource, + conditions: conditions.build(), + annotations: annotations?.build(), + ); + } + + factory CedarPolicy.fromJson(Map json) { + return CedarPolicy( + effect: CedarPolicyEffect.fromJson(json['effect'] as String), + principal: CedarPolicyPrincipal.fromJson( + json['principal'] as Map, + ), + action: CedarPolicyAction.fromJson( + json['action'] as Map, + ), + resource: CedarPolicyResource.fromJson( + json['resource'] as Map, + ), + conditions: (json['conditions'] as List) + .map((c) => CedarPolicyCondition.fromJson(c as Map)) + .toList(), + annotations: (json['annotations'] as Map?)?.cast(), + ); + } + + factory CedarPolicy.build([ + void Function(CedarPolicyBuilder) updates, + ]) = _$CedarPolicy; + CedarPolicy._(); + + CedarPolicyEffect get effect; + CedarPolicyPrincipal get principal; + CedarPolicyAction get action; + CedarPolicyResource get resource; + BuiltList get conditions; + BuiltMap? get annotations; + + Map toJson() => { + 'effect': effect.toJson(), + 'principal': principal.toJson(), + 'action': action.toJson(), + 'resource': resource.toJson(), + 'conditions': conditions.map((c) => c.toJson()).toList(), + if (annotations != null) 'annotations': annotations!.toMap(), + }; + + static Serializer get serializer => _$cedarPolicySerializer; +} + +abstract class CedarPolicyPrincipal + implements Built { + factory CedarPolicyPrincipal({ + required CedarPolicyOp op, + CedarEntityId? entity, + String? entityType, + }) { + return CedarPolicyPrincipal.build((b) { + b + ..op = op + ..entityType = entityType; + if (entity != null) { + b.entity.replace(entity); + } + }); + } + + factory CedarPolicyPrincipal.fromJson(Map json) { + return CedarPolicyPrincipal( + op: CedarPolicyOp.fromJson(json['op'] as String), + entity: json['entity'] == null + ? null + : CedarEntityId.fromJson(json['entity'] as Map), + entityType: json['entity_type'] as String?, + ); + } + + factory CedarPolicyPrincipal.build([ + void Function(CedarPolicyPrincipalBuilder) updates, + ]) = _$CedarPolicyPrincipal; + CedarPolicyPrincipal._(); + + @BuiltValueHook(finalizeBuilder: true) + static void _validate(CedarPolicyPrincipalBuilder policy) { + switch (policy.op) { + case CedarPolicyOp.all: + _expectAbsent('entity', policy._entity); + _expectAbsent('entityType', policy.entityType); + case CedarPolicyOp.equals: + _expectPresent('entity', policy._entity); + _expectAbsent('entityType', policy.entityType); + case CedarPolicyOp.in$: + _expectPresent('entity', policy._entity); + _expectAbsent('entityType', policy.entityType); + case CedarPolicyOp.is$: + _expectPresent('entityType', policy.entityType); + _expectAbsent('entity', policy._entity); + default: + throw ArgumentError.value(policy.op, 'op', 'Invalid op for principal'); + } + } + + CedarPolicyOp get op; + CedarEntityId? get entity; + + @BuiltValueField(wireName: 'entity_type') + String? get entityType; + + Map toJson() => { + 'op': op.toJson(), + if (entity != null) 'entity': entity!.toJson(), + if (entityType != null) 'entity_type': entityType, + }; + + static Serializer get serializer => + _$cedarPolicyPrincipalSerializer; +} + +abstract class CedarPolicyAction + implements Built { + factory CedarPolicyAction({ + required CedarPolicyOp op, + CedarEntityId? entity, + List? entities, + }) { + return CedarPolicyAction.build((b) { + b.op = op; + if (entity != null) { + b.entity.replace(entity); + } + if (entities != null) { + b.entities.addAll(entities); + } + }); + } + + factory CedarPolicyAction.build([ + void Function(CedarPolicyActionBuilder) updates, + ]) = _$CedarPolicyAction; + CedarPolicyAction._(); + + @BuiltValueHook(finalizeBuilder: true) + static void _validate(CedarPolicyActionBuilder policy) { + void expectNoEntities() { + if (policy._entities case final entities? when entities.isNotEmpty) { + throw ArgumentError.value( + policy._entities, + 'entities', + 'Should be empty', + ); + } + } + + switch (policy.op) { + case CedarPolicyOp.all: + _expectAbsent('entity', policy._entity); + expectNoEntities(); + case CedarPolicyOp.equals: + _expectPresent('entity', policy._entity); + expectNoEntities(); + case CedarPolicyOp.in$: + if (policy._entity != null) { + expectNoEntities(); + } else if (policy._entities != null) { + _expectAbsent('entity', policy._entity); + } else { + throw ArgumentError( + 'Either entity or entities must be specified', + ); + } + case CedarPolicyOp.is$: + default: + throw ArgumentError.value(policy.op, 'op', 'Invalid op for action'); + } + } + + CedarPolicyOp get op; + CedarEntityId? get entity; + BuiltList? get entities; + + Map toJson() => { + 'op': op.toJson(), + if (entity != null) 'entity': entity!.toJson(), + if (entities != null && op == CedarPolicyOp.in$) + 'entities': entities!.map((e) => e.toJson()).toList(), + }; + + static CedarPolicyAction fromJson(Map json) { + return CedarPolicyAction( + op: CedarPolicyOp.fromJson(json['op'] as String), + entity: json['entity'] == null + ? null + : CedarEntityId.fromJson(json['entity'] as Map), + entities: (json['entities'] as List?) + ?.map((e) => CedarEntityId.fromJson(e as Map)) + .toList(), + ); + } + + static Serializer get serializer => + _$cedarPolicyActionSerializer; +} + +abstract class CedarPolicyResource + implements Built { + factory CedarPolicyResource({ + required CedarPolicyOp op, + CedarEntityId? entity, + String? entityType, + }) { + return CedarPolicyResource.build((b) { + b + ..op = op + ..entityType = entityType; + if (entity != null) { + b.entity.replace(entity); + } + }); + } + + factory CedarPolicyResource.build([ + void Function(CedarPolicyResourceBuilder) updates, + ]) = _$CedarPolicyResource; + CedarPolicyResource._(); + + @BuiltValueHook(finalizeBuilder: true) + static void _validate(CedarPolicyResourceBuilder policy) { + switch (policy.op) { + case CedarPolicyOp.all: + _expectAbsent('entity', policy._entity); + _expectAbsent('entityType', policy.entityType); + case CedarPolicyOp.equals: + _expectPresent('entity', policy._entity); + _expectAbsent('entityType', policy.entityType); + case CedarPolicyOp.in$: + _expectPresent('entity', policy._entity); + _expectAbsent('entityType', policy.entityType); + case CedarPolicyOp.is$: + _expectPresent('entityType', policy.entityType); + _expectAbsent('entity', policy._entity); + default: + throw ArgumentError.value(policy.op, 'op', 'Invalid op for resource'); + } + } + + CedarPolicyOp get op; + CedarEntityId? get entity; + + @BuiltValueField(wireName: 'entity_type') + String? get entityType; + + Map toJson() => { + 'op': op.toJson(), + if (entity != null) 'entity': entity!.toJson(), + if (entityType != null) 'entity_type': entityType, + }; + + factory CedarPolicyResource.fromJson(Map json) { + return CedarPolicyResource( + op: CedarPolicyOp.fromJson(json['op'] as String), + entity: json['entity'] == null + ? null + : CedarEntityId.fromJson(json['entity'] as Map), + entityType: json['entity_type'] as String?, + ); + } + + static Serializer get serializer => + _$cedarPolicyResourceSerializer; +} + +abstract class CedarPolicyCondition + implements Built { + factory CedarPolicyCondition({ + required CedarPolicyConditionKind kind, + required JsonExpr body, + }) { + return _$CedarPolicyCondition._( + kind: kind, + bodyJson: JsonObject(body.toJson()), + ); + } + + factory CedarPolicyCondition.build([ + void Function(CedarPolicyConditionBuilder) updates, + ]) = _$CedarPolicyCondition; + CedarPolicyCondition._(); + + CedarPolicyConditionKind get kind; + + @BuiltValueField(wireName: 'body') + JsonObject get bodyJson; + + JsonExpr get body => JsonExpr.fromJson(bodyJson.asMap.cast()); + + Map toJson() => { + 'kind': kind.toJson(), + 'body': bodyJson.asMap, + }; + + factory CedarPolicyCondition.fromJson(Map json) => + CedarPolicyCondition.build( + (b) => b + ..kind = CedarPolicyConditionKind.fromJson(json['kind'] as String) + ..bodyJson = JsonObject(json['body'] as Map), + ); + + static Serializer get serializer => + _$cedarPolicyConditionSerializer; +} + +void _expectPresent(String name, Object? value) { + if (value == null) { + throw ArgumentError.notNull(name); + } +} + +void _expectAbsent(String name, Object? value) { + if (value != null) { + throw ArgumentError.value( + value, + name, + 'Should be absent', + ); + } +} diff --git a/packages/cedar/lib/src/policy/cedar_policy.g.dart b/packages/cedar/lib/src/policy/cedar_policy.g.dart new file mode 100644 index 00000000..2766132a --- /dev/null +++ b/packages/cedar/lib/src/policy/cedar_policy.g.dart @@ -0,0 +1,1170 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'cedar_policy.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +const CedarPolicyEffect _$permit = const CedarPolicyEffect._('permit'); +const CedarPolicyEffect _$forbid = const CedarPolicyEffect._('forbid'); + +CedarPolicyEffect _$CedarPolicyEffectValueOf(String name) { + switch (name) { + case 'permit': + return _$permit; + case 'forbid': + return _$forbid; + default: + throw new ArgumentError(name); + } +} + +final BuiltSet _$CedarPolicyEffectValues = + new BuiltSet(const [ + _$permit, + _$forbid, +]); + +const CedarPolicyOp _$all = const CedarPolicyOp._('all'); +const CedarPolicyOp _$equals = const CedarPolicyOp._('equals'); +const CedarPolicyOp _$in$ = const CedarPolicyOp._('in\$'); +const CedarPolicyOp _$is$ = const CedarPolicyOp._('is\$'); + +CedarPolicyOp _$CedarPolicyOpValueOf(String name) { + switch (name) { + case 'all': + return _$all; + case 'equals': + return _$equals; + case 'in\$': + return _$in$; + case 'is\$': + return _$is$; + default: + throw new ArgumentError(name); + } +} + +final BuiltSet _$CedarPolicyOpValues = + new BuiltSet(const [ + _$all, + _$equals, + _$in$, + _$is$, +]); + +const CedarPolicyConditionKind _$when = + const CedarPolicyConditionKind._('when'); +const CedarPolicyConditionKind _$unless = + const CedarPolicyConditionKind._('unless'); + +CedarPolicyConditionKind _$CedarPolicyConditionKindValueOf(String name) { + switch (name) { + case 'when': + return _$when; + case 'unless': + return _$unless; + default: + throw new ArgumentError(name); + } +} + +final BuiltSet _$CedarPolicyConditionKindValues = + new BuiltSet(const [ + _$when, + _$unless, +]); + +Serializer _$cedarPolicyEffectSerializer = + new _$CedarPolicyEffectSerializer(); +Serializer _$cedarPolicyOpSerializer = + new _$CedarPolicyOpSerializer(); +Serializer _$cedarPolicyConditionKindSerializer = + new _$CedarPolicyConditionKindSerializer(); +Serializer _$cedarPolicySerializer = new _$CedarPolicySerializer(); +Serializer _$cedarPolicyPrincipalSerializer = + new _$CedarPolicyPrincipalSerializer(); +Serializer _$cedarPolicyActionSerializer = + new _$CedarPolicyActionSerializer(); +Serializer _$cedarPolicyResourceSerializer = + new _$CedarPolicyResourceSerializer(); +Serializer _$cedarPolicyConditionSerializer = + new _$CedarPolicyConditionSerializer(); + +class _$CedarPolicyEffectSerializer + implements PrimitiveSerializer { + @override + final Iterable types = const [CedarPolicyEffect]; + @override + final String wireName = 'CedarPolicyEffect'; + + @override + Object serialize(Serializers serializers, CedarPolicyEffect object, + {FullType specifiedType = FullType.unspecified}) => + object.name; + + @override + CedarPolicyEffect deserialize(Serializers serializers, Object serialized, + {FullType specifiedType = FullType.unspecified}) => + CedarPolicyEffect.valueOf(serialized as String); +} + +class _$CedarPolicyOpSerializer implements PrimitiveSerializer { + static const Map _toWire = const { + 'all': 'All', + 'equals': '==', + 'in\$': 'in', + 'is\$': 'is', + }; + static const Map _fromWire = const { + 'All': 'all', + '==': 'equals', + 'in': 'in\$', + 'is': 'is\$', + }; + + @override + final Iterable types = const [CedarPolicyOp]; + @override + final String wireName = 'CedarPolicyOp'; + + @override + Object serialize(Serializers serializers, CedarPolicyOp object, + {FullType specifiedType = FullType.unspecified}) => + _toWire[object.name] ?? object.name; + + @override + CedarPolicyOp deserialize(Serializers serializers, Object serialized, + {FullType specifiedType = FullType.unspecified}) => + CedarPolicyOp.valueOf( + _fromWire[serialized] ?? (serialized is String ? serialized : '')); +} + +class _$CedarPolicyConditionKindSerializer + implements PrimitiveSerializer { + @override + final Iterable types = const [CedarPolicyConditionKind]; + @override + final String wireName = 'CedarPolicyConditionKind'; + + @override + Object serialize(Serializers serializers, CedarPolicyConditionKind object, + {FullType specifiedType = FullType.unspecified}) => + object.name; + + @override + CedarPolicyConditionKind deserialize( + Serializers serializers, Object serialized, + {FullType specifiedType = FullType.unspecified}) => + CedarPolicyConditionKind.valueOf(serialized as String); +} + +class _$CedarPolicySerializer implements StructuredSerializer { + @override + final Iterable types = const [CedarPolicy, _$CedarPolicy]; + @override + final String wireName = 'CedarPolicy'; + + @override + Iterable serialize(Serializers serializers, CedarPolicy object, + {FullType specifiedType = FullType.unspecified}) { + final result = [ + 'effect', + serializers.serialize(object.effect, + specifiedType: const FullType(CedarPolicyEffect)), + 'principal', + serializers.serialize(object.principal, + specifiedType: const FullType(CedarPolicyPrincipal)), + 'action', + serializers.serialize(object.action, + specifiedType: const FullType(CedarPolicyAction)), + 'resource', + serializers.serialize(object.resource, + specifiedType: const FullType(CedarPolicyResource)), + 'conditions', + serializers.serialize(object.conditions, + specifiedType: const FullType( + BuiltList, const [const FullType(CedarPolicyCondition)])), + ]; + Object? value; + value = object.annotations; + if (value != null) { + result + ..add('annotations') + ..add(serializers.serialize(value, + specifiedType: const FullType(BuiltMap, + const [const FullType(String), const FullType(String)]))); + } + return result; + } + + @override + CedarPolicy deserialize(Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = new CedarPolicyBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'effect': + result.effect = serializers.deserialize(value, + specifiedType: const FullType(CedarPolicyEffect))! + as CedarPolicyEffect; + break; + case 'principal': + result.principal.replace(serializers.deserialize(value, + specifiedType: const FullType(CedarPolicyPrincipal))! + as CedarPolicyPrincipal); + break; + case 'action': + result.action.replace(serializers.deserialize(value, + specifiedType: const FullType(CedarPolicyAction))! + as CedarPolicyAction); + break; + case 'resource': + result.resource.replace(serializers.deserialize(value, + specifiedType: const FullType(CedarPolicyResource))! + as CedarPolicyResource); + break; + case 'conditions': + result.conditions.replace(serializers.deserialize(value, + specifiedType: const FullType( + BuiltList, const [const FullType(CedarPolicyCondition)]))! + as BuiltList); + break; + case 'annotations': + result.annotations.replace(serializers.deserialize(value, + specifiedType: const FullType(BuiltMap, + const [const FullType(String), const FullType(String)]))!); + break; + } + } + + return result.build(); + } +} + +class _$CedarPolicyPrincipalSerializer + implements StructuredSerializer { + @override + final Iterable types = const [ + CedarPolicyPrincipal, + _$CedarPolicyPrincipal + ]; + @override + final String wireName = 'CedarPolicyPrincipal'; + + @override + Iterable serialize( + Serializers serializers, CedarPolicyPrincipal object, + {FullType specifiedType = FullType.unspecified}) { + final result = [ + 'op', + serializers.serialize(object.op, + specifiedType: const FullType(CedarPolicyOp)), + ]; + Object? value; + value = object.entity; + if (value != null) { + result + ..add('entity') + ..add(serializers.serialize(value, + specifiedType: const FullType(CedarEntityId))); + } + value = object.entityType; + if (value != null) { + result + ..add('entity_type') + ..add(serializers.serialize(value, + specifiedType: const FullType(String))); + } + return result; + } + + @override + CedarPolicyPrincipal deserialize( + Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = new CedarPolicyPrincipalBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'op': + result.op = serializers.deserialize(value, + specifiedType: const FullType(CedarPolicyOp))! as CedarPolicyOp; + break; + case 'entity': + result.entity.replace(serializers.deserialize(value, + specifiedType: const FullType(CedarEntityId))! as CedarEntityId); + break; + case 'entity_type': + result.entityType = serializers.deserialize(value, + specifiedType: const FullType(String)) as String?; + break; + } + } + + return result.build(); + } +} + +class _$CedarPolicyActionSerializer + implements StructuredSerializer { + @override + final Iterable types = const [CedarPolicyAction, _$CedarPolicyAction]; + @override + final String wireName = 'CedarPolicyAction'; + + @override + Iterable serialize(Serializers serializers, CedarPolicyAction object, + {FullType specifiedType = FullType.unspecified}) { + final result = [ + 'op', + serializers.serialize(object.op, + specifiedType: const FullType(CedarPolicyOp)), + ]; + Object? value; + value = object.entity; + if (value != null) { + result + ..add('entity') + ..add(serializers.serialize(value, + specifiedType: const FullType(CedarEntityId))); + } + value = object.entities; + if (value != null) { + result + ..add('entities') + ..add(serializers.serialize(value, + specifiedType: const FullType( + BuiltList, const [const FullType(CedarEntityId)]))); + } + return result; + } + + @override + CedarPolicyAction deserialize( + Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = new CedarPolicyActionBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'op': + result.op = serializers.deserialize(value, + specifiedType: const FullType(CedarPolicyOp))! as CedarPolicyOp; + break; + case 'entity': + result.entity.replace(serializers.deserialize(value, + specifiedType: const FullType(CedarEntityId))! as CedarEntityId); + break; + case 'entities': + result.entities.replace(serializers.deserialize(value, + specifiedType: const FullType( + BuiltList, const [const FullType(CedarEntityId)]))! + as BuiltList); + break; + } + } + + return result.build(); + } +} + +class _$CedarPolicyResourceSerializer + implements StructuredSerializer { + @override + final Iterable types = const [ + CedarPolicyResource, + _$CedarPolicyResource + ]; + @override + final String wireName = 'CedarPolicyResource'; + + @override + Iterable serialize( + Serializers serializers, CedarPolicyResource object, + {FullType specifiedType = FullType.unspecified}) { + final result = [ + 'op', + serializers.serialize(object.op, + specifiedType: const FullType(CedarPolicyOp)), + ]; + Object? value; + value = object.entity; + if (value != null) { + result + ..add('entity') + ..add(serializers.serialize(value, + specifiedType: const FullType(CedarEntityId))); + } + value = object.entityType; + if (value != null) { + result + ..add('entity_type') + ..add(serializers.serialize(value, + specifiedType: const FullType(String))); + } + return result; + } + + @override + CedarPolicyResource deserialize( + Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = new CedarPolicyResourceBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'op': + result.op = serializers.deserialize(value, + specifiedType: const FullType(CedarPolicyOp))! as CedarPolicyOp; + break; + case 'entity': + result.entity.replace(serializers.deserialize(value, + specifiedType: const FullType(CedarEntityId))! as CedarEntityId); + break; + case 'entity_type': + result.entityType = serializers.deserialize(value, + specifiedType: const FullType(String)) as String?; + break; + } + } + + return result.build(); + } +} + +class _$CedarPolicyConditionSerializer + implements StructuredSerializer { + @override + final Iterable types = const [ + CedarPolicyCondition, + _$CedarPolicyCondition + ]; + @override + final String wireName = 'CedarPolicyCondition'; + + @override + Iterable serialize( + Serializers serializers, CedarPolicyCondition object, + {FullType specifiedType = FullType.unspecified}) { + final result = [ + 'kind', + serializers.serialize(object.kind, + specifiedType: const FullType(CedarPolicyConditionKind)), + 'body', + serializers.serialize(object.bodyJson, + specifiedType: const FullType(JsonObject)), + ]; + + return result; + } + + @override + CedarPolicyCondition deserialize( + Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = new CedarPolicyConditionBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'kind': + result.kind = serializers.deserialize(value, + specifiedType: const FullType(CedarPolicyConditionKind))! + as CedarPolicyConditionKind; + break; + case 'body': + result.bodyJson = serializers.deserialize(value, + specifiedType: const FullType(JsonObject))! as JsonObject; + break; + } + } + + return result.build(); + } +} + +class _$CedarPolicy extends CedarPolicy { + @override + final CedarPolicyEffect effect; + @override + final CedarPolicyPrincipal principal; + @override + final CedarPolicyAction action; + @override + final CedarPolicyResource resource; + @override + final BuiltList conditions; + @override + final BuiltMap? annotations; + + factory _$CedarPolicy([void Function(CedarPolicyBuilder)? updates]) => + (new CedarPolicyBuilder()..update(updates))._build(); + + _$CedarPolicy._( + {required this.effect, + required this.principal, + required this.action, + required this.resource, + required this.conditions, + this.annotations}) + : super._() { + BuiltValueNullFieldError.checkNotNull(effect, r'CedarPolicy', 'effect'); + BuiltValueNullFieldError.checkNotNull( + principal, r'CedarPolicy', 'principal'); + BuiltValueNullFieldError.checkNotNull(action, r'CedarPolicy', 'action'); + BuiltValueNullFieldError.checkNotNull(resource, r'CedarPolicy', 'resource'); + BuiltValueNullFieldError.checkNotNull( + conditions, r'CedarPolicy', 'conditions'); + } + + @override + CedarPolicy rebuild(void Function(CedarPolicyBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + CedarPolicyBuilder toBuilder() => new CedarPolicyBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is CedarPolicy && + effect == other.effect && + principal == other.principal && + action == other.action && + resource == other.resource && + conditions == other.conditions && + annotations == other.annotations; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, effect.hashCode); + _$hash = $jc(_$hash, principal.hashCode); + _$hash = $jc(_$hash, action.hashCode); + _$hash = $jc(_$hash, resource.hashCode); + _$hash = $jc(_$hash, conditions.hashCode); + _$hash = $jc(_$hash, annotations.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'CedarPolicy') + ..add('effect', effect) + ..add('principal', principal) + ..add('action', action) + ..add('resource', resource) + ..add('conditions', conditions) + ..add('annotations', annotations)) + .toString(); + } +} + +class CedarPolicyBuilder implements Builder { + _$CedarPolicy? _$v; + + CedarPolicyEffect? _effect; + CedarPolicyEffect? get effect => _$this._effect; + set effect(CedarPolicyEffect? effect) => _$this._effect = effect; + + CedarPolicyPrincipalBuilder? _principal; + CedarPolicyPrincipalBuilder get principal => + _$this._principal ??= new CedarPolicyPrincipalBuilder(); + set principal(CedarPolicyPrincipalBuilder? principal) => + _$this._principal = principal; + + CedarPolicyActionBuilder? _action; + CedarPolicyActionBuilder get action => + _$this._action ??= new CedarPolicyActionBuilder(); + set action(CedarPolicyActionBuilder? action) => _$this._action = action; + + CedarPolicyResourceBuilder? _resource; + CedarPolicyResourceBuilder get resource => + _$this._resource ??= new CedarPolicyResourceBuilder(); + set resource(CedarPolicyResourceBuilder? resource) => + _$this._resource = resource; + + ListBuilder? _conditions; + ListBuilder get conditions => + _$this._conditions ??= new ListBuilder(); + set conditions(ListBuilder? conditions) => + _$this._conditions = conditions; + + MapBuilder? _annotations; + MapBuilder get annotations => + _$this._annotations ??= new MapBuilder(); + set annotations(MapBuilder? annotations) => + _$this._annotations = annotations; + + CedarPolicyBuilder(); + + CedarPolicyBuilder get _$this { + final $v = _$v; + if ($v != null) { + _effect = $v.effect; + _principal = $v.principal.toBuilder(); + _action = $v.action.toBuilder(); + _resource = $v.resource.toBuilder(); + _conditions = $v.conditions.toBuilder(); + _annotations = $v.annotations?.toBuilder(); + _$v = null; + } + return this; + } + + @override + void replace(CedarPolicy other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$CedarPolicy; + } + + @override + void update(void Function(CedarPolicyBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + CedarPolicy build() => _build(); + + _$CedarPolicy _build() { + _$CedarPolicy _$result; + try { + _$result = _$v ?? + new _$CedarPolicy._( + effect: BuiltValueNullFieldError.checkNotNull( + effect, r'CedarPolicy', 'effect'), + principal: principal.build(), + action: action.build(), + resource: resource.build(), + conditions: conditions.build(), + annotations: _annotations?.build()); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'principal'; + principal.build(); + _$failedField = 'action'; + action.build(); + _$failedField = 'resource'; + resource.build(); + _$failedField = 'conditions'; + conditions.build(); + _$failedField = 'annotations'; + _annotations?.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'CedarPolicy', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +class _$CedarPolicyPrincipal extends CedarPolicyPrincipal { + @override + final CedarPolicyOp op; + @override + final CedarEntityId? entity; + @override + final String? entityType; + + factory _$CedarPolicyPrincipal( + [void Function(CedarPolicyPrincipalBuilder)? updates]) => + (new CedarPolicyPrincipalBuilder()..update(updates))._build(); + + _$CedarPolicyPrincipal._({required this.op, this.entity, this.entityType}) + : super._() { + BuiltValueNullFieldError.checkNotNull(op, r'CedarPolicyPrincipal', 'op'); + } + + @override + CedarPolicyPrincipal rebuild( + void Function(CedarPolicyPrincipalBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + CedarPolicyPrincipalBuilder toBuilder() => + new CedarPolicyPrincipalBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is CedarPolicyPrincipal && + op == other.op && + entity == other.entity && + entityType == other.entityType; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, op.hashCode); + _$hash = $jc(_$hash, entity.hashCode); + _$hash = $jc(_$hash, entityType.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'CedarPolicyPrincipal') + ..add('op', op) + ..add('entity', entity) + ..add('entityType', entityType)) + .toString(); + } +} + +class CedarPolicyPrincipalBuilder + implements Builder { + _$CedarPolicyPrincipal? _$v; + + CedarPolicyOp? _op; + CedarPolicyOp? get op => _$this._op; + set op(CedarPolicyOp? op) => _$this._op = op; + + CedarEntityIdBuilder? _entity; + CedarEntityIdBuilder get entity => + _$this._entity ??= new CedarEntityIdBuilder(); + set entity(CedarEntityIdBuilder? entity) => _$this._entity = entity; + + String? _entityType; + String? get entityType => _$this._entityType; + set entityType(String? entityType) => _$this._entityType = entityType; + + CedarPolicyPrincipalBuilder(); + + CedarPolicyPrincipalBuilder get _$this { + final $v = _$v; + if ($v != null) { + _op = $v.op; + _entity = $v.entity?.toBuilder(); + _entityType = $v.entityType; + _$v = null; + } + return this; + } + + @override + void replace(CedarPolicyPrincipal other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$CedarPolicyPrincipal; + } + + @override + void update(void Function(CedarPolicyPrincipalBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + CedarPolicyPrincipal build() => _build(); + + _$CedarPolicyPrincipal _build() { + CedarPolicyPrincipal._validate(this); + _$CedarPolicyPrincipal _$result; + try { + _$result = _$v ?? + new _$CedarPolicyPrincipal._( + op: BuiltValueNullFieldError.checkNotNull( + op, r'CedarPolicyPrincipal', 'op'), + entity: _entity?.build(), + entityType: entityType); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'entity'; + _entity?.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'CedarPolicyPrincipal', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +class _$CedarPolicyAction extends CedarPolicyAction { + @override + final CedarPolicyOp op; + @override + final CedarEntityId? entity; + @override + final BuiltList? entities; + + factory _$CedarPolicyAction( + [void Function(CedarPolicyActionBuilder)? updates]) => + (new CedarPolicyActionBuilder()..update(updates))._build(); + + _$CedarPolicyAction._({required this.op, this.entity, this.entities}) + : super._() { + BuiltValueNullFieldError.checkNotNull(op, r'CedarPolicyAction', 'op'); + } + + @override + CedarPolicyAction rebuild(void Function(CedarPolicyActionBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + CedarPolicyActionBuilder toBuilder() => + new CedarPolicyActionBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is CedarPolicyAction && + op == other.op && + entity == other.entity && + entities == other.entities; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, op.hashCode); + _$hash = $jc(_$hash, entity.hashCode); + _$hash = $jc(_$hash, entities.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'CedarPolicyAction') + ..add('op', op) + ..add('entity', entity) + ..add('entities', entities)) + .toString(); + } +} + +class CedarPolicyActionBuilder + implements Builder { + _$CedarPolicyAction? _$v; + + CedarPolicyOp? _op; + CedarPolicyOp? get op => _$this._op; + set op(CedarPolicyOp? op) => _$this._op = op; + + CedarEntityIdBuilder? _entity; + CedarEntityIdBuilder get entity => + _$this._entity ??= new CedarEntityIdBuilder(); + set entity(CedarEntityIdBuilder? entity) => _$this._entity = entity; + + ListBuilder? _entities; + ListBuilder get entities => + _$this._entities ??= new ListBuilder(); + set entities(ListBuilder? entities) => + _$this._entities = entities; + + CedarPolicyActionBuilder(); + + CedarPolicyActionBuilder get _$this { + final $v = _$v; + if ($v != null) { + _op = $v.op; + _entity = $v.entity?.toBuilder(); + _entities = $v.entities?.toBuilder(); + _$v = null; + } + return this; + } + + @override + void replace(CedarPolicyAction other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$CedarPolicyAction; + } + + @override + void update(void Function(CedarPolicyActionBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + CedarPolicyAction build() => _build(); + + _$CedarPolicyAction _build() { + CedarPolicyAction._validate(this); + _$CedarPolicyAction _$result; + try { + _$result = _$v ?? + new _$CedarPolicyAction._( + op: BuiltValueNullFieldError.checkNotNull( + op, r'CedarPolicyAction', 'op'), + entity: _entity?.build(), + entities: _entities?.build()); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'entity'; + _entity?.build(); + _$failedField = 'entities'; + _entities?.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'CedarPolicyAction', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +class _$CedarPolicyResource extends CedarPolicyResource { + @override + final CedarPolicyOp op; + @override + final CedarEntityId? entity; + @override + final String? entityType; + + factory _$CedarPolicyResource( + [void Function(CedarPolicyResourceBuilder)? updates]) => + (new CedarPolicyResourceBuilder()..update(updates))._build(); + + _$CedarPolicyResource._({required this.op, this.entity, this.entityType}) + : super._() { + BuiltValueNullFieldError.checkNotNull(op, r'CedarPolicyResource', 'op'); + } + + @override + CedarPolicyResource rebuild( + void Function(CedarPolicyResourceBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + CedarPolicyResourceBuilder toBuilder() => + new CedarPolicyResourceBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is CedarPolicyResource && + op == other.op && + entity == other.entity && + entityType == other.entityType; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, op.hashCode); + _$hash = $jc(_$hash, entity.hashCode); + _$hash = $jc(_$hash, entityType.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'CedarPolicyResource') + ..add('op', op) + ..add('entity', entity) + ..add('entityType', entityType)) + .toString(); + } +} + +class CedarPolicyResourceBuilder + implements Builder { + _$CedarPolicyResource? _$v; + + CedarPolicyOp? _op; + CedarPolicyOp? get op => _$this._op; + set op(CedarPolicyOp? op) => _$this._op = op; + + CedarEntityIdBuilder? _entity; + CedarEntityIdBuilder get entity => + _$this._entity ??= new CedarEntityIdBuilder(); + set entity(CedarEntityIdBuilder? entity) => _$this._entity = entity; + + String? _entityType; + String? get entityType => _$this._entityType; + set entityType(String? entityType) => _$this._entityType = entityType; + + CedarPolicyResourceBuilder(); + + CedarPolicyResourceBuilder get _$this { + final $v = _$v; + if ($v != null) { + _op = $v.op; + _entity = $v.entity?.toBuilder(); + _entityType = $v.entityType; + _$v = null; + } + return this; + } + + @override + void replace(CedarPolicyResource other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$CedarPolicyResource; + } + + @override + void update(void Function(CedarPolicyResourceBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + CedarPolicyResource build() => _build(); + + _$CedarPolicyResource _build() { + CedarPolicyResource._validate(this); + _$CedarPolicyResource _$result; + try { + _$result = _$v ?? + new _$CedarPolicyResource._( + op: BuiltValueNullFieldError.checkNotNull( + op, r'CedarPolicyResource', 'op'), + entity: _entity?.build(), + entityType: entityType); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'entity'; + _entity?.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'CedarPolicyResource', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +class _$CedarPolicyCondition extends CedarPolicyCondition { + @override + final CedarPolicyConditionKind kind; + @override + final JsonObject bodyJson; + + factory _$CedarPolicyCondition( + [void Function(CedarPolicyConditionBuilder)? updates]) => + (new CedarPolicyConditionBuilder()..update(updates))._build(); + + _$CedarPolicyCondition._({required this.kind, required this.bodyJson}) + : super._() { + BuiltValueNullFieldError.checkNotNull( + kind, r'CedarPolicyCondition', 'kind'); + BuiltValueNullFieldError.checkNotNull( + bodyJson, r'CedarPolicyCondition', 'bodyJson'); + } + + @override + CedarPolicyCondition rebuild( + void Function(CedarPolicyConditionBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + CedarPolicyConditionBuilder toBuilder() => + new CedarPolicyConditionBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is CedarPolicyCondition && + kind == other.kind && + bodyJson == other.bodyJson; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, kind.hashCode); + _$hash = $jc(_$hash, bodyJson.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'CedarPolicyCondition') + ..add('kind', kind) + ..add('bodyJson', bodyJson)) + .toString(); + } +} + +class CedarPolicyConditionBuilder + implements Builder { + _$CedarPolicyCondition? _$v; + + CedarPolicyConditionKind? _kind; + CedarPolicyConditionKind? get kind => _$this._kind; + set kind(CedarPolicyConditionKind? kind) => _$this._kind = kind; + + JsonObject? _bodyJson; + JsonObject? get bodyJson => _$this._bodyJson; + set bodyJson(JsonObject? bodyJson) => _$this._bodyJson = bodyJson; + + CedarPolicyConditionBuilder(); + + CedarPolicyConditionBuilder get _$this { + final $v = _$v; + if ($v != null) { + _kind = $v.kind; + _bodyJson = $v.bodyJson; + _$v = null; + } + return this; + } + + @override + void replace(CedarPolicyCondition other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$CedarPolicyCondition; + } + + @override + void update(void Function(CedarPolicyConditionBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + CedarPolicyCondition build() => _build(); + + _$CedarPolicyCondition _build() { + final _$result = _$v ?? + new _$CedarPolicyCondition._( + kind: BuiltValueNullFieldError.checkNotNull( + kind, r'CedarPolicyCondition', 'kind'), + bodyJson: BuiltValueNullFieldError.checkNotNull( + bodyJson, r'CedarPolicyCondition', 'bodyJson')); + replace(_$result); + return _$result; + } +} + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/packages/cedar/lib/src/policy/cedar_policy_set.dart b/packages/cedar/lib/src/policy/cedar_policy_set.dart new file mode 100644 index 00000000..c1bcdbaa --- /dev/null +++ b/packages/cedar/lib/src/policy/cedar_policy_set.dart @@ -0,0 +1,40 @@ +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/built_value.dart'; +import 'package:built_value/serializer.dart'; +import 'package:cedar/cedar.dart'; + +part 'cedar_policy_set.g.dart'; + +/// A collection of Cedar policies. +abstract class CedarPolicySet + implements Built { + factory CedarPolicySet({ + Map policies = const {}, + }) { + return _$CedarPolicySet._(policies: policies.build()); + } + + factory CedarPolicySet.fromJson(Map json) { + return CedarPolicySet( + policies: { + for (final MapEntry(key: id, value: json) in json.entries) + id: CedarPolicy.fromJson(json as Map), + }, + ); + } + + factory CedarPolicySet.build([ + void Function(CedarPolicySetBuilder) updates, + ]) = _$CedarPolicySet; + + const CedarPolicySet._(); + + BuiltMap get policies; + // TODO(dnys1): Templates + + Map toJson() => + policies.asMap().map((key, value) => MapEntry(key, value.toJson())); + + static Serializer get serializer => + _$cedarPolicySetSerializer; +} diff --git a/packages/cedar/lib/src/policy/cedar_policy_set.g.dart b/packages/cedar/lib/src/policy/cedar_policy_set.g.dart new file mode 100644 index 00000000..8f6fac8f --- /dev/null +++ b/packages/cedar/lib/src/policy/cedar_policy_set.g.dart @@ -0,0 +1,155 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'cedar_policy_set.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +Serializer _$cedarPolicySetSerializer = + new _$CedarPolicySetSerializer(); + +class _$CedarPolicySetSerializer + implements StructuredSerializer { + @override + final Iterable types = const [CedarPolicySet, _$CedarPolicySet]; + @override + final String wireName = 'CedarPolicySet'; + + @override + Iterable serialize(Serializers serializers, CedarPolicySet object, + {FullType specifiedType = FullType.unspecified}) { + final result = [ + 'policies', + serializers.serialize(object.policies, + specifiedType: const FullType(BuiltMap, + const [const FullType(String), const FullType(CedarPolicy)])), + ]; + + return result; + } + + @override + CedarPolicySet deserialize( + Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = new CedarPolicySetBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'policies': + result.policies.replace(serializers.deserialize(value, + specifiedType: const FullType(BuiltMap, const [ + const FullType(String), + const FullType(CedarPolicy) + ]))!); + break; + } + } + + return result.build(); + } +} + +class _$CedarPolicySet extends CedarPolicySet { + @override + final BuiltMap policies; + + factory _$CedarPolicySet([void Function(CedarPolicySetBuilder)? updates]) => + (new CedarPolicySetBuilder()..update(updates))._build(); + + _$CedarPolicySet._({required this.policies}) : super._() { + BuiltValueNullFieldError.checkNotNull( + policies, r'CedarPolicySet', 'policies'); + } + + @override + CedarPolicySet rebuild(void Function(CedarPolicySetBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + CedarPolicySetBuilder toBuilder() => + new CedarPolicySetBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is CedarPolicySet && policies == other.policies; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, policies.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'CedarPolicySet') + ..add('policies', policies)) + .toString(); + } +} + +class CedarPolicySetBuilder + implements Builder { + _$CedarPolicySet? _$v; + + MapBuilder? _policies; + MapBuilder get policies => + _$this._policies ??= new MapBuilder(); + set policies(MapBuilder? policies) => + _$this._policies = policies; + + CedarPolicySetBuilder(); + + CedarPolicySetBuilder get _$this { + final $v = _$v; + if ($v != null) { + _policies = $v.policies.toBuilder(); + _$v = null; + } + return this; + } + + @override + void replace(CedarPolicySet other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$CedarPolicySet; + } + + @override + void update(void Function(CedarPolicySetBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + CedarPolicySet build() => _build(); + + _$CedarPolicySet _build() { + _$CedarPolicySet _$result; + try { + _$result = _$v ?? new _$CedarPolicySet._(policies: policies.build()); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'policies'; + policies.build(); + } catch (e) { + throw new BuiltValueNestedFieldError( + r'CedarPolicySet', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/packages/cedar/lib/src/policy/json_expr.dart b/packages/cedar/lib/src/policy/json_expr.dart new file mode 100644 index 00000000..c0338b76 --- /dev/null +++ b/packages/cedar/lib/src/policy/json_expr.dart @@ -0,0 +1,1161 @@ +/// Dart representation of the JSON expression language used in Cedar +/// policies. +/// +/// See: https://docs.cedarpolicy.com/policies/json-format.html#JsonExpr-objects +library; + +import 'dart:convert'; + +import 'package:cedar/src/ast/cedar_entity_id.dart'; +import 'package:collection/collection.dart'; +import 'package:meta/meta.dart'; + +sealed class JsonExprOp { + factory JsonExprOp.fromJson(String json) => switch (json) { + 'Value' => JsonExprOpCode.value, + 'Var' => JsonExprOpCode.variable, + 'Slot' => JsonExprOpCode.slot, + 'Unknown' => JsonExprOpCode.unknown, + '!' => JsonExprOpCode.not, + 'neg' => JsonExprOpCode.neg, + '==' => JsonExprOpCode.equals, + '!=' => JsonExprOpCode.notEquals, + 'in' => JsonExprOpCode.in$, + '<' => JsonExprOpCode.lessThan, + '<=' => JsonExprOpCode.lessThanOrEquals, + '>' => JsonExprOpCode.greaterThan, + '>=' => JsonExprOpCode.greaterThanOrEquals, + '&&' => JsonExprOpCode.and, + '||' => JsonExprOpCode.or, + '+' => JsonExprOpCode.plus, + '-' => JsonExprOpCode.minus, + '*' => JsonExprOpCode.times, + 'contains' => JsonExprOpCode.contains, + 'containsAll' => JsonExprOpCode.containsAll, + 'containsAny' => JsonExprOpCode.containsAny, + '.' => JsonExprOpCode.getAttribute, + 'has' => JsonExprOpCode.hasAttribute, + 'like' => JsonExprOpCode.like, + 'is' => JsonExprOpCode.is$, + 'if-then-else' => JsonExprOpCode.ifThenElse, + 'Set' => JsonExprOpCode.set, + 'Record' => JsonExprOpCode.record, + _ => JsonExprOpFunc(json), + }; + + String toJson(); +} + +final class JsonExprOpFunc implements JsonExprOp { + const JsonExprOpFunc(this.name); + + final String name; + + @override + String toJson() => name; + + @override + bool operator ==(Object other) => + identical(this, other) || other is JsonExprOpFunc && name == other.name; + + @override + int get hashCode => name.hashCode; +} + +enum JsonExprOpCode implements JsonExprOp { + value, + variable, + slot, + unknown, + not, + neg, + equals, + notEquals, + in$, + lessThan, + lessThanOrEquals, + greaterThan, + greaterThanOrEquals, + and, + or, + plus, + minus, + times, + contains, + containsAll, + containsAny, + getAttribute, + hasAttribute, + like, + is$, + ifThenElse, + set, + record; + + @override + String toJson() => switch (this) { + value => 'Value', + variable => 'Var', + slot => 'Slot', + unknown => 'Unknown', + not => '!', + neg => 'neg', + equals => '==', + notEquals => '!=', + in$ => 'in', + lessThan => '<', + lessThanOrEquals => '<=', + greaterThan => '>', + greaterThanOrEquals => '>=', + and => '&&', + or => '||', + plus => '+', + minus => '-', + times => '*', + contains => 'contains', + containsAll => 'containsAll', + containsAny => 'containsAny', + getAttribute => '.', + hasAttribute => 'has', + like => 'like', + is$ => 'is', + ifThenElse => 'if-then-else', + set => 'Set', + record => 'Record', + }; +} + +sealed class JsonExpr { + const JsonExpr(); + + factory JsonExpr.fromJson(Map json) { + if (json.keys.length != 1) { + throw FormatException('Expected exactly one key in JSON expression'); + } + final MapEntry(:key, :value) = json.entries.first; + final op = JsonExprOp.fromJson(key); + return switch (op) { + JsonExprOpCode.value => JsonExprValue.fromJson(value), + JsonExprOpCode.variable => JsonExprVariable.fromJson(value as String), + JsonExprOpCode.slot => JsonExprSlot.fromJson(value as String), + JsonExprOpCode.unknown => + JsonExprUnknown.fromJson(value as Map), + JsonExprOpCode.not => JsonExprNot.fromJson(value as Map), + JsonExprOpCode.neg => JsonExprNeg.fromJson(value as Map), + JsonExprOpCode.equals => + JsonExprEquals.fromJson(value as Map), + JsonExprOpCode.notEquals => + JsonExprNotEquals.fromJson(value as Map), + JsonExprOpCode.in$ => JsonExprIn.fromJson(value as Map), + JsonExprOpCode.lessThan => + JsonExprLessThan.fromJson(value as Map), + JsonExprOpCode.lessThanOrEquals => + JsonExprLessThanOrEquals.fromJson(value as Map), + JsonExprOpCode.greaterThan => + JsonExprGreaterThan.fromJson(value as Map), + JsonExprOpCode.greaterThanOrEquals => + JsonExprGreaterThanOrEquals.fromJson(value as Map), + JsonExprOpCode.and => JsonExprAnd.fromJson(value as Map), + JsonExprOpCode.or => JsonExprOr.fromJson(value as Map), + JsonExprOpCode.plus => + JsonExprPlus.fromJson(value as Map), + JsonExprOpCode.minus => + JsonExprMinus.fromJson(value as Map), + JsonExprOpCode.times => + JsonExprTimes.fromJson(value as Map), + JsonExprOpCode.contains => + JsonExprContains.fromJson(value as Map), + JsonExprOpCode.containsAll => + JsonExprContainsAll.fromJson(value as Map), + JsonExprOpCode.containsAny => + JsonExprContainsAny.fromJson(value as Map), + JsonExprOpCode.getAttribute => + JsonExprGetAttribute.fromJson(value as Map), + JsonExprOpCode.hasAttribute => + JsonExprHasAttribute.fromJson(value as Map), + JsonExprOpCode.like => + JsonExprLike.fromJson(value as Map), + JsonExprOpCode.is$ => JsonExprIs.fromJson(value as Map), + JsonExprOpCode.ifThenElse => + JsonExprIfThenElse.fromJson(value as Map), + JsonExprOpCode.set => JsonExprSet.fromJson(value as List), + JsonExprOpCode.record => + JsonExprRecord.fromJson(value as Map), + final JsonExprOpFunc op => JsonExprFuncCall( + op, + (value as List) + .map((el) => JsonExpr.fromJson(el as Map)) + .toList(), + ), + }; + } + + const factory JsonExpr.value(CedarValueJson value) = JsonExprValue; + + const factory JsonExpr.variable(CedarVariable variable) = JsonExprVariable; + + const factory JsonExpr.slot(CedarSlotId slotId) = JsonExprSlot; + + const factory JsonExpr.unknown(String name) = JsonExprUnknown; + + const factory JsonExpr.not(JsonExpr arg) = JsonExprNot; + + const factory JsonExpr.neg(JsonExpr arg) = JsonExprNeg; + + const factory JsonExpr.equals(JsonExpr left, JsonExpr right) = JsonExprEquals; + + const factory JsonExpr.notEquals(JsonExpr left, JsonExpr right) = + JsonExprNotEquals; + + const factory JsonExpr.in$(JsonExpr left, JsonExpr right) = JsonExprIn; + + const factory JsonExpr.lessThan(JsonExpr left, JsonExpr right) = + JsonExprLessThan; + + const factory JsonExpr.lessThanOrEquals(JsonExpr left, JsonExpr right) = + JsonExprLessThanOrEquals; + + const factory JsonExpr.greaterThan(JsonExpr left, JsonExpr right) = + JsonExprGreaterThan; + + const factory JsonExpr.greaterThanOrEquals(JsonExpr left, JsonExpr right) = + JsonExprGreaterThanOrEquals; + + const factory JsonExpr.and(JsonExpr left, JsonExpr right) = JsonExprAnd; + + const factory JsonExpr.or(JsonExpr left, JsonExpr right) = JsonExprOr; + + const factory JsonExpr.plus(JsonExpr left, JsonExpr right) = JsonExprPlus; + + const factory JsonExpr.minus(JsonExpr left, JsonExpr right) = JsonExprMinus; + + const factory JsonExpr.times(JsonExpr left, JsonExpr right) = JsonExprTimes; + + const factory JsonExpr.contains(JsonExpr left, JsonExpr right) = + JsonExprContains; + + const factory JsonExpr.containsAll(JsonExpr left, JsonExpr right) = + JsonExprContainsAll; + + const factory JsonExpr.containsAny(JsonExpr left, JsonExpr right) = + JsonExprContainsAny; + + const factory JsonExpr.getAttribute(JsonExpr left, String attr) = + JsonExprGetAttribute; + + const factory JsonExpr.hasAttribute(JsonExpr left, String attr) = + JsonExprHasAttribute; + + const factory JsonExpr.like(JsonExpr left, String pattern) = JsonExprLike; + + const factory JsonExpr.is$(JsonExpr left, String entityType, + [JsonExpr? inExpr]) = JsonExprIs; + + const factory JsonExpr.ifThenElse({ + required JsonExpr cond, + required JsonExpr then, + required JsonExpr else$, + }) = JsonExprIfThenElse; + + const factory JsonExpr.set(List expressions) = JsonExprSet; + + const factory JsonExpr.record(Map attributes) = + JsonExprRecord; + + const factory JsonExpr.funcCall(JsonExprOpFunc fn, List args) = + JsonExprFuncCall; + + JsonExprOp get op; + + Object? valueToJson(); + + @nonVirtual + Map toJson() => { + op.toJson(): valueToJson(), + }; + + @override + String toString() => _prettyJson(toJson()); +} + +final class JsonExprFuncCall extends JsonExpr { + const JsonExprFuncCall(this.fn, this.args); + + final JsonExprOpFunc fn; + final List args; + + @override + JsonExprOpFunc get op => fn; + + @override + List> valueToJson() => + args.map((arg) => arg.toJson()).toList(); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is JsonExprFuncCall && + op == other.op && + const ListEquality().equals(args, other.args); + + @override + int get hashCode => Object.hash(op, op, args); +} + +sealed class CedarValueJson { + const CedarValueJson(); + + factory CedarValueJson.fromJson(Object? json) { + return switch (json) { + {'__entity': _} => CedarValueEntity.fromJson(json), + {'__extn': _} => CedarValueExtension.fromJson(json), + final bool json => CedarValueBool.fromJson(json), + final num json => CedarValueLong.fromJson(json.toInt()), + final String json => CedarValueString.fromJson(json), + final List json => CedarValueSet.fromJson(json), + final Map json => CedarValueRecord.fromJson(json.cast()), + _ => throw FormatException('Invalid Cedar JSON value: $json'), + }; + } + + const factory CedarValueJson.entity(CedarEntityId entityId) = + CedarValueEntity; + + const factory CedarValueJson.extension(CedarExtensionInvocation extension) = + CedarValueExtension; + + const factory CedarValueJson.bool(bool value) = CedarValueBool; + + const factory CedarValueJson.long(int value) = CedarValueLong; + + const factory CedarValueJson.string(String value) = CedarValueString; + + const factory CedarValueJson.set(List elements) = + CedarValueSet; + + const factory CedarValueJson.record(Map attributes) = + CedarValueRecord; + + Object? toJson(); + + @override + String toString() => _prettyJson(toJson()); +} + +final class CedarValueEntity extends CedarValueJson { + const CedarValueEntity(this.entityId); + + factory CedarValueEntity.fromJson(Map json) { + return CedarValueEntity( + CedarEntityId.fromJson(json['__entity'] as Map), + ); + } + + final CedarEntityId entityId; + + @override + Map toJson() => { + '__entity': entityId.toJson(), + }; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is CedarValueEntity && entityId == other.entityId; + + @override + int get hashCode => entityId.hashCode; +} + +final class CedarValueExtension extends CedarValueJson { + const CedarValueExtension(this.extension); + + factory CedarValueExtension.fromJson(Map json) { + return CedarValueExtension( + CedarExtensionInvocation.fromJson(json['__extn'] as Map), + ); + } + + final CedarExtensionInvocation extension; + + @override + Map toJson() => { + '__extn': extension.toJson(), + }; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is CedarValueExtension && extension == other.extension; + + @override + int get hashCode => extension.hashCode; +} + +final class CedarExtensionInvocation { + const CedarExtensionInvocation({ + required this.fn, + required this.arg, + }); + + factory CedarExtensionInvocation.fromJson(Map json) { + return CedarExtensionInvocation( + fn: json['fn'] as String, + arg: CedarValueJson.fromJson(json['arg']), + ); + } + + final String fn; + final CedarValueJson arg; + + Map toJson() => { + 'fn': fn, + 'arg': arg.toJson(), + }; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is CedarExtensionInvocation && fn == other.fn && arg == other.arg; + + @override + int get hashCode => Object.hash(fn, arg); +} + +final class CedarValueBool extends CedarValueJson { + const CedarValueBool(this.value); + + factory CedarValueBool.fromJson(bool json) { + return CedarValueBool(json); + } + + final bool value; + + @override + bool toJson() => value; + + @override + bool operator ==(Object other) => + identical(this, other) || other is CedarValueBool && value == other.value; + + @override + int get hashCode => value.hashCode; +} + +final class CedarValueLong extends CedarValueJson { + const CedarValueLong(this.value); + + factory CedarValueLong.fromJson(int json) { + return CedarValueLong(json); + } + + final int value; + + @override + int toJson() => value; + + @override + bool operator ==(Object other) => + identical(this, other) || other is CedarValueLong && value == other.value; + + @override + int get hashCode => value.hashCode; +} + +final class CedarValueString extends CedarValueJson { + const CedarValueString(this.value); + + factory CedarValueString.fromJson(String json) { + return CedarValueString(json); + } + + final String value; + + @override + String toJson() => value; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is CedarValueString && value == other.value; + + @override + int get hashCode => value.hashCode; +} + +final class CedarValueSet extends CedarValueJson { + const CedarValueSet(this.elements); + + factory CedarValueSet.fromJson(List json) { + return CedarValueSet([ + for (final element in json) CedarValueJson.fromJson(element), + ]); + } + + final List elements; + + @override + List toJson() => [ + for (final element in elements) element.toJson(), + ]; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is CedarValueSet && + const UnorderedIterableEquality().equals(elements, other.elements); + + @override + int get hashCode => Object.hashAllUnordered(elements); +} + +final class CedarValueRecord extends CedarValueJson { + const CedarValueRecord(this.attributes); + + factory CedarValueRecord.fromJson(Map json) { + return CedarValueRecord({ + for (final entry in json.entries) + entry.key: CedarValueJson.fromJson(entry.value) + }); + } + + final Map attributes; + + @override + Map toJson() => { + for (final entry in attributes.entries) entry.key: entry.value.toJson(), + }; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is CedarValueRecord && + const MapEquality().equals(attributes, other.attributes); + + @override + int get hashCode => const MapEquality().hash(attributes); +} + +final class JsonExprValue extends JsonExpr { + const JsonExprValue(this.value); + + factory JsonExprValue.fromJson(Object? json) { + return JsonExprValue(CedarValueJson.fromJson(json)); + } + + final CedarValueJson value; + + @override + JsonExprOpCode get op => JsonExprOpCode.value; + + @override + Object? valueToJson() => value.toJson(); + + @override + bool operator ==(Object other) => + identical(this, other) || other is JsonExprValue && value == other.value; + + @override + int get hashCode => Object.hash(op, value); +} + +enum CedarVariable { principal, action, resource, context } + +final class JsonExprVariable extends JsonExpr { + const JsonExprVariable(this.variable); + + factory JsonExprVariable.fromJson(String json) { + return JsonExprVariable(CedarVariable.values.byName(json)); + } + + final CedarVariable variable; + + @override + JsonExprOpCode get op => JsonExprOpCode.variable; + + @override + String valueToJson() => variable.name; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is JsonExprVariable && variable == other.variable; + + @override + int get hashCode => Object.hash(op, variable); +} + +enum CedarSlotId { principal, resource } + +final class JsonExprSlot extends JsonExpr { + const JsonExprSlot(this.slotId); + + factory JsonExprSlot.fromJson(String json) { + return JsonExprSlot(CedarSlotId.values.byName(json.substring(1))); + } + + final CedarSlotId slotId; + + @override + JsonExprOpCode get op => JsonExprOpCode.slot; + + @override + String valueToJson() => '?${slotId.name}'; + + @override + bool operator ==(Object other) => + identical(this, other) || other is JsonExprSlot && slotId == other.slotId; + + @override + int get hashCode => Object.hash(op, slotId); +} + +final class JsonExprUnknown extends JsonExpr { + const JsonExprUnknown(this.name); + + factory JsonExprUnknown.fromJson(Map json) { + return JsonExprUnknown(json['name'] as String); + } + + final String name; + + @override + JsonExprOpCode get op => JsonExprOpCode.unknown; + + @override + Map valueToJson() => { + 'name': name, + }; + + @override + bool operator ==(Object other) => + identical(this, other) || other is JsonExprUnknown && name == other.name; + + @override + int get hashCode => Object.hash(op, name); +} + +final class JsonExprNot extends JsonExpr { + const JsonExprNot(this.arg); + + factory JsonExprNot.fromJson(Map json) { + return JsonExprNot( + JsonExpr.fromJson(json['arg'] as Map), + ); + } + + final JsonExpr arg; + + @override + JsonExprOpCode get op => JsonExprOpCode.not; + + @override + Map valueToJson() => { + 'arg': arg.toJson(), + }; + + @override + bool operator ==(Object other) => + identical(this, other) || other is JsonExprNot && arg == other.arg; + + @override + int get hashCode => Object.hash(op, arg); +} + +final class JsonExprNeg extends JsonExpr { + const JsonExprNeg(this.arg); + + factory JsonExprNeg.fromJson(Map json) { + return JsonExprNeg( + JsonExpr.fromJson(json['arg'] as Map), + ); + } + + final JsonExpr arg; + + @override + JsonExprOpCode get op => JsonExprOpCode.neg; + + @override + Map valueToJson() => { + 'arg': arg.toJson(), + }; + + @override + bool operator ==(Object other) => + identical(this, other) || other is JsonExprNeg && arg == other.arg; + + @override + int get hashCode => Object.hash(op, arg); +} + +sealed class JsonBinaryExpr extends JsonExpr { + const JsonBinaryExpr(this.left, this.right); + + final JsonExpr left; + final JsonExpr right; + + @nonVirtual + @override + Map valueToJson() => { + 'left': left.toJson(), + 'right': right.toJson(), + }; + + @override + bool operator ==(Object other) => + identical(this, other) && + other is JsonBinaryExpr && + op == other.op && + left == other.left && + right == other.right; + + @override + int get hashCode => Object.hash(op, left, right); +} + +final class JsonExprEquals extends JsonBinaryExpr { + const JsonExprEquals(super.left, super.right); + + factory JsonExprEquals.fromJson(Map json) { + return JsonExprEquals( + JsonExpr.fromJson(json['left'] as Map), + JsonExpr.fromJson(json['right'] as Map), + ); + } + + @override + JsonExprOpCode get op => JsonExprOpCode.equals; +} + +final class JsonExprNotEquals extends JsonBinaryExpr { + const JsonExprNotEquals(super.left, super.right); + + factory JsonExprNotEquals.fromJson(Map json) { + return JsonExprNotEquals( + JsonExpr.fromJson(json['left'] as Map), + JsonExpr.fromJson(json['right'] as Map), + ); + } + + @override + JsonExprOpCode get op => JsonExprOpCode.notEquals; +} + +final class JsonExprIn extends JsonBinaryExpr { + const JsonExprIn(super.left, super.right); + + factory JsonExprIn.fromJson(Map json) { + return JsonExprIn( + JsonExpr.fromJson(json['left'] as Map), + JsonExpr.fromJson(json['right'] as Map), + ); + } + + @override + JsonExprOpCode get op => JsonExprOpCode.in$; +} + +final class JsonExprLessThan extends JsonBinaryExpr { + const JsonExprLessThan(super.left, super.right); + + factory JsonExprLessThan.fromJson(Map json) { + return JsonExprLessThan( + JsonExpr.fromJson(json['left'] as Map), + JsonExpr.fromJson(json['right'] as Map), + ); + } + + @override + JsonExprOpCode get op => JsonExprOpCode.lessThan; +} + +final class JsonExprLessThanOrEquals extends JsonBinaryExpr { + const JsonExprLessThanOrEquals(super.left, super.right); + + factory JsonExprLessThanOrEquals.fromJson(Map json) { + return JsonExprLessThanOrEquals( + JsonExpr.fromJson(json['left'] as Map), + JsonExpr.fromJson(json['right'] as Map), + ); + } + + @override + JsonExprOpCode get op => JsonExprOpCode.lessThanOrEquals; +} + +final class JsonExprGreaterThan extends JsonBinaryExpr { + const JsonExprGreaterThan(super.left, super.right); + + factory JsonExprGreaterThan.fromJson(Map json) { + return JsonExprGreaterThan( + JsonExpr.fromJson(json['left'] as Map), + JsonExpr.fromJson(json['right'] as Map), + ); + } + + @override + JsonExprOpCode get op => JsonExprOpCode.greaterThan; +} + +final class JsonExprGreaterThanOrEquals extends JsonBinaryExpr { + const JsonExprGreaterThanOrEquals(super.left, super.right); + + factory JsonExprGreaterThanOrEquals.fromJson(Map json) { + return JsonExprGreaterThanOrEquals( + JsonExpr.fromJson(json['left'] as Map), + JsonExpr.fromJson(json['right'] as Map), + ); + } + + @override + JsonExprOpCode get op => JsonExprOpCode.greaterThanOrEquals; +} + +final class JsonExprAnd extends JsonBinaryExpr { + const JsonExprAnd(super.left, super.right); + + factory JsonExprAnd.fromJson(Map json) { + return JsonExprAnd( + JsonExpr.fromJson(json['left'] as Map), + JsonExpr.fromJson(json['right'] as Map), + ); + } + + @override + JsonExprOpCode get op => JsonExprOpCode.and; +} + +final class JsonExprOr extends JsonBinaryExpr { + const JsonExprOr(super.left, super.right); + + factory JsonExprOr.fromJson(Map json) { + return JsonExprOr( + JsonExpr.fromJson(json['left'] as Map), + JsonExpr.fromJson(json['right'] as Map), + ); + } + + @override + JsonExprOpCode get op => JsonExprOpCode.or; +} + +final class JsonExprPlus extends JsonBinaryExpr { + const JsonExprPlus(super.left, super.right); + + factory JsonExprPlus.fromJson(Map json) { + return JsonExprPlus( + JsonExpr.fromJson(json['left'] as Map), + JsonExpr.fromJson(json['right'] as Map), + ); + } + + @override + JsonExprOpCode get op => JsonExprOpCode.plus; +} + +final class JsonExprMinus extends JsonBinaryExpr { + const JsonExprMinus(super.left, super.right); + + factory JsonExprMinus.fromJson(Map json) { + return JsonExprMinus( + JsonExpr.fromJson(json['left'] as Map), + JsonExpr.fromJson(json['right'] as Map), + ); + } + + @override + JsonExprOpCode get op => JsonExprOpCode.minus; +} + +final class JsonExprTimes extends JsonBinaryExpr { + const JsonExprTimes(super.left, super.right); + + factory JsonExprTimes.fromJson(Map json) { + return JsonExprTimes( + JsonExpr.fromJson(json['left'] as Map), + JsonExpr.fromJson(json['right'] as Map), + ); + } + + @override + JsonExprOpCode get op => JsonExprOpCode.times; +} + +final class JsonExprContains extends JsonBinaryExpr { + const JsonExprContains(super.left, super.right); + + factory JsonExprContains.fromJson(Map json) { + return JsonExprContains( + JsonExpr.fromJson(json['left'] as Map), + JsonExpr.fromJson(json['right'] as Map), + ); + } + + @override + JsonExprOpCode get op => JsonExprOpCode.contains; +} + +final class JsonExprContainsAll extends JsonBinaryExpr { + const JsonExprContainsAll(super.left, super.right); + + factory JsonExprContainsAll.fromJson(Map json) { + return JsonExprContainsAll( + JsonExpr.fromJson(json['left'] as Map), + JsonExpr.fromJson(json['right'] as Map), + ); + } + + @override + JsonExprOpCode get op => JsonExprOpCode.containsAll; +} + +final class JsonExprContainsAny extends JsonBinaryExpr { + const JsonExprContainsAny(super.left, super.right); + + factory JsonExprContainsAny.fromJson(Map json) { + return JsonExprContainsAny( + JsonExpr.fromJson(json['left'] as Map), + JsonExpr.fromJson(json['right'] as Map), + ); + } + + @override + JsonExprOpCode get op => JsonExprOpCode.containsAny; +} + +final class JsonExprGetAttribute extends JsonExpr { + const JsonExprGetAttribute(this.left, this.attr); + + factory JsonExprGetAttribute.fromJson(Map json) { + return JsonExprGetAttribute( + JsonExpr.fromJson(json['left'] as Map), + json['attr'] as String, + ); + } + + final JsonExpr left; + final String attr; + + @override + JsonExprOpCode get op => JsonExprOpCode.getAttribute; + + @override + Map valueToJson() => { + 'left': left.toJson(), + 'attr': attr, + }; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is JsonExprGetAttribute && left == other.left && attr == other.attr; + + @override + int get hashCode => Object.hash(op, left, attr); +} + +final class JsonExprHasAttribute extends JsonExpr { + const JsonExprHasAttribute(this.left, this.attr); + + factory JsonExprHasAttribute.fromJson(Map json) { + return JsonExprHasAttribute( + JsonExpr.fromJson(json['left'] as Map), + json['attr'] as String, + ); + } + + final JsonExpr left; + final String attr; + + @override + JsonExprOpCode get op => JsonExprOpCode.hasAttribute; + + @override + Map valueToJson() => { + 'left': left.toJson(), + 'attr': attr, + }; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is JsonExprHasAttribute && left == other.left && attr == other.attr; + + @override + int get hashCode => Object.hash(op, left, attr); +} + +final class JsonExprLike extends JsonExpr { + const JsonExprLike(this.left, this.pattern); + + factory JsonExprLike.fromJson(Map json) { + return JsonExprLike( + JsonExpr.fromJson(json['left'] as Map), + json['pattern'] as String, + ); + } + + final JsonExpr left; + final String pattern; + + @override + JsonExprOpCode get op => JsonExprOpCode.like; + + @override + Map valueToJson() => { + 'left': left.toJson(), + 'pattern': pattern, + }; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is JsonExprLike && left == other.left && pattern == other.pattern; + + @override + int get hashCode => Object.hash(op, left, pattern); +} + +final class JsonExprIs extends JsonExpr { + const JsonExprIs(this.left, this.entityType, [this.inExpr]); + + factory JsonExprIs.fromJson(Map json) { + return JsonExprIs( + JsonExpr.fromJson(json['left'] as Map), + json['entity_type'] as String, + json['in'] != null + ? JsonExpr.fromJson(json['in'] as Map) + : null, + ); + } + + final JsonExpr left; + final String entityType; + final JsonExpr? inExpr; + + @override + JsonExprOpCode get op => JsonExprOpCode.is$; + + @override + Map valueToJson() => { + 'left': left.toJson(), + 'entity_type': entityType, + if (inExpr case final inExpr?) 'in': inExpr.toJson(), + }; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is JsonExprIs && + left == other.left && + entityType == other.entityType && + inExpr == other.inExpr; + + @override + int get hashCode => Object.hash(op, left, entityType, inExpr); +} + +final class JsonExprIfThenElse extends JsonExpr { + const JsonExprIfThenElse({ + required this.cond, + required this.then, + required this.else$, + }); + + factory JsonExprIfThenElse.fromJson(Map json) { + return JsonExprIfThenElse( + cond: JsonExpr.fromJson(json['if'] as Map), + then: JsonExpr.fromJson(json['then'] as Map), + else$: JsonExpr.fromJson(json['else'] as Map), + ); + } + + final JsonExpr cond; + final JsonExpr then; + final JsonExpr else$; + + @override + JsonExprOpCode get op => JsonExprOpCode.ifThenElse; + + @override + Map valueToJson() => { + 'if': cond.toJson(), + 'then': then.toJson(), + 'else': else$.toJson(), + }; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is JsonExprIfThenElse && + cond == other.cond && + then == other.then && + else$ == other.else$; + + @override + int get hashCode => Object.hash(op, cond, then, else$); +} + +final class JsonExprSet extends JsonExpr { + const JsonExprSet(this.expressions); + + factory JsonExprSet.fromJson(List json) { + return JsonExprSet([ + for (final expression in json) + JsonExpr.fromJson(expression as Map) + ]); + } + + final List expressions; + + @override + JsonExprOpCode get op => JsonExprOpCode.set; + + @override + List valueToJson() => [ + for (final expression in expressions) expression.toJson(), + ]; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is JsonExprSet && + const UnorderedIterableEquality() + .equals(expressions, other.expressions); + + @override + int get hashCode => Object.hashAllUnordered(expressions); +} + +final class JsonExprRecord extends JsonExpr { + const JsonExprRecord(this.attributes); + + factory JsonExprRecord.fromJson(Map json) { + return JsonExprRecord({ + for (final entry in json.entries) + entry.key: JsonExpr.fromJson(entry.value as Map) + }); + } + + final Map attributes; + + @override + JsonExprOpCode get op => JsonExprOpCode.record; + + @override + Map valueToJson() => { + for (final entry in attributes.entries) entry.key: entry.value.toJson(), + }; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is JsonExprRecord && + const MapEquality().equals(attributes, other.attributes); + + @override + int get hashCode => const MapEquality().hash(attributes); +} + +String _prettyJson(Object? o) => const JsonEncoder.withIndent(' ').convert(o); diff --git a/packages/cedar/lib/src/serializers.dart b/packages/cedar/lib/src/serializers.dart new file mode 100644 index 00000000..6da3e6f9 --- /dev/null +++ b/packages/cedar/lib/src/serializers.dart @@ -0,0 +1,21 @@ +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/serializer.dart'; +import 'package:cedar/cedar.dart'; + +part 'serializers.g.dart'; + +/// `package:built_value` serializers for Cedar types. +@SerializersFor([ + CedarEntityId, + CedarEntity, + CedarPolicySet, + CedarPolicy, + CedarPolicyEffect, + CedarPolicyOp, + CedarPolicyConditionKind, + CedarPolicyPrincipal, + CedarPolicyAction, + CedarPolicyResource, + CedarPolicyCondition, +]) +final Serializers cedarSerializers = _$cedarSerializers; diff --git a/packages/cedar/lib/src/serializers.g.dart b/packages/cedar/lib/src/serializers.g.dart new file mode 100644 index 00000000..b682b045 --- /dev/null +++ b/packages/cedar/lib/src/serializers.g.dart @@ -0,0 +1,45 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'serializers.dart'; + +// ************************************************************************** +// BuiltValueGenerator +// ************************************************************************** + +Serializers _$cedarSerializers = (new Serializers().toBuilder() + ..add(CedarEntity.serializer) + ..add(CedarEntityId.serializer) + ..add(CedarPolicy.serializer) + ..add(CedarPolicyAction.serializer) + ..add(CedarPolicyCondition.serializer) + ..add(CedarPolicyConditionKind.serializer) + ..add(CedarPolicyEffect.serializer) + ..add(CedarPolicyOp.serializer) + ..add(CedarPolicyPrincipal.serializer) + ..add(CedarPolicyResource.serializer) + ..add(CedarPolicySet.serializer) + ..addBuilderFactory( + const FullType(BuiltList, const [const FullType(CedarEntityId)]), + () => new ListBuilder()) + ..addBuilderFactory( + const FullType(BuiltList, const [const FullType(CedarEntityId)]), + () => new ListBuilder()) + ..addBuilderFactory( + const FullType(BuiltMap, + const [const FullType(String), const FullType(CedarValueJson)]), + () => new MapBuilder()) + ..addBuilderFactory( + const FullType( + BuiltList, const [const FullType(CedarPolicyCondition)]), + () => new ListBuilder()) + ..addBuilderFactory( + const FullType( + BuiltMap, const [const FullType(String), const FullType(String)]), + () => new MapBuilder()) + ..addBuilderFactory( + const FullType(BuiltMap, + const [const FullType(String), const FullType(CedarPolicy)]), + () => new MapBuilder())) + .build(); + +// ignore_for_file: deprecated_member_use_from_same_package,type=lint diff --git a/packages/cedar/lib/src/util.dart b/packages/cedar/lib/src/util.dart new file mode 100644 index 00000000..15b5740d --- /dev/null +++ b/packages/cedar/lib/src/util.dart @@ -0,0 +1,8 @@ +extension Let on T? { + R? let(R Function(T) f) { + if (this case final this_?) { + return f(this_); + } + return null; + } +} diff --git a/packages/cedar/pubspec.yaml b/packages/cedar/pubspec.yaml new file mode 100644 index 00000000..4d3d0fa0 --- /dev/null +++ b/packages/cedar/pubspec.yaml @@ -0,0 +1,19 @@ +name: cedar +description: Core types and interfaces of the Cedar policy language in Dart. +version: 0.1.0 + +environment: + sdk: ^3.3.0 + +dependencies: + built_collection: ^5.1.1 + built_value: ^8.9.1 + collection: ^1.18.0 + json_annotation: ^4.8.1 + meta: ^1.11.0 + +dev_dependencies: + build_runner: ^2.4.8 + built_value_generator: ^8.9.1 + lints: ^3.0.0 + test: ^1.24.0