Skip to content

Commit

Permalink
feat: Add package:cedar (#63)
Browse files Browse the repository at this point in the history
Adds `package:cedar` which includes Dart representations of the Cedar policy language and shared interfaces which will be implemented in `package:cedar_ffi`.
  • Loading branch information
dnys1 authored Mar 9, 2024
1 parent ce97ec3 commit a12ae42
Show file tree
Hide file tree
Showing 22 changed files with 4,117 additions and 0 deletions.
7 changes: 7 additions & 0 deletions packages/cedar/.gitignore
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions packages/cedar/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 0.1.0

- Initial version.
7 changes: 7 additions & 0 deletions packages/cedar/README.md
Original file line number Diff line number Diff line change
@@ -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`.
1 change: 1 addition & 0 deletions packages/cedar/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include: package:lints/recommended.yaml
26 changes: 26 additions & 0 deletions packages/cedar/lib/cedar.dart
Original file line number Diff line number Diff line change
@@ -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';
54 changes: 54 additions & 0 deletions packages/cedar/lib/src/ast/cedar_entity.dart
Original file line number Diff line number Diff line change
@@ -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<CedarEntity, CedarEntityBuilder> {
factory CedarEntity({
required CedarEntityId id,
List<CedarEntityId> parents = const [],
Map<String, CedarValueJson> attributes = const {},
}) {
return _$CedarEntity._(
id: id,
parents: parents.build(),
attributes: attributes.build(),
);
}

factory CedarEntity.build([
void Function(CedarEntityBuilder) updates,
]) = _$CedarEntity;

factory CedarEntity.fromJson(Map<String, Object?> json) => CedarEntity(
id: CedarEntityId.fromJson(json['uid'] as Map<String, Object?>),
parents: (json['parents'] as List<Object?>)
.map((e) => CedarEntityId.fromJson(e as Map<String, Object?>))
.toList(),
attributes: (json['attrs'] as Map<Object?, Object?>)
.cast<String, Object?>()
.map((key, value) => MapEntry(key, CedarValueJson.fromJson(value))),
);

const CedarEntity._();

CedarEntityId get id;
BuiltList<CedarEntityId> get parents;
BuiltMap<String, CedarValueJson> get attributes;

Map<String, Object?> toJson() => {
'uid': id.toJson(),
'parents': parents.map((e) => e.toJson()).toList(),
'attrs': attributes
.map((key, value) => MapEntry(key, value.toJson()))
.asMap(),
};

static Serializer<CedarEntity> get serializer => _$cedarEntitySerializer;
}
201 changes: 201 additions & 0 deletions packages/cedar/lib/src/ast/cedar_entity.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 68 additions & 0 deletions packages/cedar/lib/src/ast/cedar_entity_id.dart
Original file line number Diff line number Diff line change
@@ -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<CedarEntityId, CedarEntityIdBuilder> {
factory CedarEntityId(String type, String id) =>
_$CedarEntityId._(type: type, id: id);

factory CedarEntityId.build([
void Function(CedarEntityIdBuilder) updates,
]) = _$CedarEntityId;

factory CedarEntityId.fromJson(Map<String, Object?> 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<String, Object?> toJson() => {
'type': type,
'id': id,
};

static Serializer<CedarEntityId> get serializer => _$cedarEntityIdSerializer;
}
Loading

0 comments on commit a12ae42

Please sign in to comment.