cedar_policy 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7410b46f2bfc9344de867185521852b7f95d4511e5b903dd8e451844e0e5b07e
4
- data.tar.gz: 462c41840176d812a3caa1814eb156ade79833e3dfde0607a1459cb574bd0a1b
3
+ metadata.gz: 3d74502a574ecb798f539deef9534d297f8f60802fc1614a2ba61a0b04c381e8
4
+ data.tar.gz: 052ea0f3383e332bcd94931ff7f5c57f45aa2ffbd126e3e8628574e20dd39d98
5
5
  SHA512:
6
- metadata.gz: c996c04f26847edea288d1aba4f995ab880c825009f197d9f88139065f63f4aa5fc4f62ddd8f86961418bfbcc344b41496a8ffd9d54b33da38f5de86ee0533d9
7
- data.tar.gz: 2a99f4e6026e3bf6f020128f00a0d9a856f75fce16299a0cd486f834c867dd9e392c54328d3527a951fb064924c793d09969d7e4296b3d348e5085ff7bd08224
6
+ metadata.gz: 1a1311adf918c900f7baac517ed7942377cbe8ded2e44c7228926d1e84db87a10bf81b474cc68d9555f4b4db5a754ab7e29c41df508c06e3878f2daab3126adb
7
+ data.tar.gz: 7c038820cfb7c60d0730ab7d8d10f43acce0afcf10db84e97b3c4e76c8f49b6d469cfdf66c37edbd4531bc8ace9ffd66deaa146d9afa5550b87894be2263875a
data/Cargo.lock CHANGED
@@ -170,7 +170,9 @@ name = "cedar_policy"
170
170
  version = "0.1.0"
171
171
  dependencies = [
172
172
  "cedar-policy",
173
+ "cedar-policy-core",
173
174
  "magnus",
175
+ "serde_magnus",
174
176
  ]
175
177
 
176
178
  [[package]]
@@ -400,9 +402,9 @@ dependencies = [
400
402
 
401
403
  [[package]]
402
404
  name = "indexmap"
403
- version = "2.3.0"
405
+ version = "2.4.0"
404
406
  source = "registry+https://github.com/rust-lang/crates.io-index"
405
- checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0"
407
+ checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
406
408
  dependencies = [
407
409
  "equivalent",
408
410
  "hashbrown 0.14.5",
@@ -435,9 +437,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
435
437
 
436
438
  [[package]]
437
439
  name = "js-sys"
438
- version = "0.3.69"
440
+ version = "0.3.70"
439
441
  source = "registry+https://github.com/rust-lang/crates.io-index"
440
- checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d"
442
+ checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
441
443
  dependencies = [
442
444
  "wasm-bindgen",
443
445
  ]
@@ -529,9 +531,9 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
529
531
 
530
532
  [[package]]
531
533
  name = "magnus"
532
- version = "0.7.1"
534
+ version = "0.6.4"
533
535
  source = "registry+https://github.com/rust-lang/crates.io-index"
534
- checksum = "3d87ae53030f3a22e83879e666cb94e58a7bdf31706878a0ba48752994146dab"
536
+ checksum = "b1597ef40aa8c36be098249e82c9a20cf7199278ac1c1a1a995eeead6a184479"
535
537
  dependencies = [
536
538
  "magnus-macros",
537
539
  "rb-sys",
@@ -659,7 +661,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
659
661
  checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
660
662
  dependencies = [
661
663
  "fixedbitset",
662
- "indexmap 2.3.0",
664
+ "indexmap 2.4.0",
663
665
  ]
664
666
 
665
667
  [[package]]
@@ -865,18 +867,18 @@ checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4"
865
867
 
866
868
  [[package]]
867
869
  name = "serde"
868
- version = "1.0.206"
870
+ version = "1.0.207"
869
871
  source = "registry+https://github.com/rust-lang/crates.io-index"
870
- checksum = "5b3e4cd94123dd520a128bcd11e34d9e9e423e7e3e50425cb1b4b1e3549d0284"
872
+ checksum = "5665e14a49a4ea1b91029ba7d3bca9f299e1f7cfa194388ccc20f14743e784f2"
871
873
  dependencies = [
872
874
  "serde_derive",
873
875
  ]
874
876
 
875
877
  [[package]]
876
878
  name = "serde_derive"
877
- version = "1.0.206"
879
+ version = "1.0.207"
878
880
  source = "registry+https://github.com/rust-lang/crates.io-index"
879
- checksum = "fabfb6138d2383ea8208cf98ccf69cdfb1aff4088460681d84189aa259762f97"
881
+ checksum = "6aea2634c86b0e8ef2cfdc0c340baede54ec27b1e46febd7f80dffb2aa44a00e"
880
882
  dependencies = [
881
883
  "proc-macro2",
882
884
  "quote",
@@ -885,17 +887,28 @@ dependencies = [
885
887
 
886
888
  [[package]]
887
889
  name = "serde_json"
888
- version = "1.0.122"
890
+ version = "1.0.124"
889
891
  source = "registry+https://github.com/rust-lang/crates.io-index"
890
- checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da"
892
+ checksum = "66ad62847a56b3dba58cc891acd13884b9c61138d330c0d7b6181713d4fce38d"
891
893
  dependencies = [
892
- "indexmap 2.3.0",
894
+ "indexmap 2.4.0",
893
895
  "itoa",
894
896
  "memchr",
895
897
  "ryu",
896
898
  "serde",
897
899
  ]
898
900
 
901
+ [[package]]
902
+ name = "serde_magnus"
903
+ version = "0.8.1"
904
+ source = "registry+https://github.com/rust-lang/crates.io-index"
905
+ checksum = "76c20da583b5e1016e9199ef5f3260f7a8d1b253307d232600f6b12737262dbd"
906
+ dependencies = [
907
+ "magnus",
908
+ "serde",
909
+ "tap",
910
+ ]
911
+
899
912
  [[package]]
900
913
  name = "serde_with"
901
914
  version = "3.9.0"
@@ -906,7 +919,7 @@ dependencies = [
906
919
  "chrono",
907
920
  "hex",
908
921
  "indexmap 1.9.3",
909
- "indexmap 2.3.0",
922
+ "indexmap 2.4.0",
910
923
  "serde",
911
924
  "serde_derive",
912
925
  "serde_json",
@@ -1002,6 +1015,12 @@ dependencies = [
1002
1015
  "unicode-ident",
1003
1016
  ]
1004
1017
 
1018
+ [[package]]
1019
+ name = "tap"
1020
+ version = "1.0.1"
1021
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1022
+ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
1023
+
1005
1024
  [[package]]
1006
1025
  name = "term"
1007
1026
  version = "0.7.0"
@@ -1149,19 +1168,20 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
1149
1168
 
1150
1169
  [[package]]
1151
1170
  name = "wasm-bindgen"
1152
- version = "0.2.92"
1171
+ version = "0.2.93"
1153
1172
  source = "registry+https://github.com/rust-lang/crates.io-index"
1154
- checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
1173
+ checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
1155
1174
  dependencies = [
1156
1175
  "cfg-if",
1176
+ "once_cell",
1157
1177
  "wasm-bindgen-macro",
1158
1178
  ]
1159
1179
 
1160
1180
  [[package]]
1161
1181
  name = "wasm-bindgen-backend"
1162
- version = "0.2.92"
1182
+ version = "0.2.93"
1163
1183
  source = "registry+https://github.com/rust-lang/crates.io-index"
1164
- checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da"
1184
+ checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
1165
1185
  dependencies = [
1166
1186
  "bumpalo",
1167
1187
  "log",
@@ -1174,9 +1194,9 @@ dependencies = [
1174
1194
 
1175
1195
  [[package]]
1176
1196
  name = "wasm-bindgen-macro"
1177
- version = "0.2.92"
1197
+ version = "0.2.93"
1178
1198
  source = "registry+https://github.com/rust-lang/crates.io-index"
1179
- checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726"
1199
+ checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
1180
1200
  dependencies = [
1181
1201
  "quote",
1182
1202
  "wasm-bindgen-macro-support",
@@ -1184,9 +1204,9 @@ dependencies = [
1184
1204
 
1185
1205
  [[package]]
1186
1206
  name = "wasm-bindgen-macro-support"
1187
- version = "0.2.92"
1207
+ version = "0.2.93"
1188
1208
  source = "registry+https://github.com/rust-lang/crates.io-index"
1189
- checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
1209
+ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
1190
1210
  dependencies = [
1191
1211
  "proc-macro2",
1192
1212
  "quote",
@@ -1197,9 +1217,9 @@ dependencies = [
1197
1217
 
1198
1218
  [[package]]
1199
1219
  name = "wasm-bindgen-shared"
1200
- version = "0.2.92"
1220
+ version = "0.2.93"
1201
1221
  source = "registry+https://github.com/rust-lang/crates.io-index"
1202
- checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96"
1222
+ checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
1203
1223
 
1204
1224
  [[package]]
1205
1225
  name = "winapi"
data/README.md CHANGED
@@ -26,34 +26,35 @@ policy = <<~POLICY
26
26
  resource
27
27
  );
28
28
  POLICY
29
- policy_set = CedarPolicy::PolicySet.from_str(policy)
29
+ policy_set = CedarPolicy::PolicySet.new(policy)
30
30
 
31
- principal = CedarPolicy::EntityUid.new("AdminUser", "1")
31
+ principal = CedarPolicy::EntityUid.new("User", "1")
32
32
  action = CedarPolicy::EntityUid.new("Action", "view")
33
33
  resource = CedarPolicy::EntityUid.new("Image", "1")
34
+ ctx = CedarPolicy::Context.new
34
35
 
35
- request = CedarPolicy::Request.new(principal, action, resource)
36
+ request = CedarPolicy::Request.new(principal, action, resource, ctx)
36
37
 
37
- entities_json = <<~JSON
38
- [
39
- {
40
- "uid": { "type": "AdminUser", "id": "1" },
41
- "attrs": {},
42
- "parents": []
43
- }
44
- ]
45
- JSON
46
- entities = CedarPolicy::Entities.from_json(entities_json)
38
+ entities = CedarPolicy::Entities.new([
39
+ CedarPolicy::Entity.new(
40
+ CedarPolicy::EntityUid.new("User", "1"),
41
+ { role: "admin" }
42
+ )
43
+ ])
47
44
 
48
45
  authorizer = CedarPolicy::Authorizer.new
49
-
50
- authorized = authorizer.authorize?(request, policy_set, entities) # => true
46
+ authorizer.authorize?(request, policy_set, entities) # => true
51
47
 
52
48
  response = authorizer.authorize(request, policy_set, entities)
53
- response.decision # => CedarPolicy::Decision.allow
49
+ response.decision # => CedarPolicy::Decision::ALLOW
54
50
  ```
55
51
 
56
- > Currently, the API design is including too many low-level details. We are working on a more user-friendly API.
52
+ ## Roadmap
53
+
54
+ * [ ] Add DSL to improve developer experience
55
+ * [ ] Diagnostics return with response
56
+ * [ ] Validator support
57
+ * [ ] Schema support
57
58
 
58
59
  ## Development
59
60
 
@@ -11,4 +11,6 @@ crate-type = ["cdylib"]
11
11
 
12
12
  [dependencies]
13
13
  cedar-policy = "3.2.4"
14
- magnus = { version = "0.7.1" }
14
+ cedar-policy-core = "3.2.4"
15
+ magnus = "0.6.2"
16
+ serde_magnus = "0.8.1"
@@ -1,7 +1,9 @@
1
1
  use cedar_policy::{Authorizer, Decision};
2
2
  use magnus::{function, method, Error, Module, Object, RModule, Ruby};
3
3
 
4
- use crate::{entities::REntities, policy_set::RPolicySet, request::RRequest, response::RResponse};
4
+ use crate::{
5
+ entities::EntitiesWrapper, policy_set::RPolicySet, request::RRequest, response::RResponse,
6
+ };
5
7
 
6
8
  #[magnus::wrap(class = "CedarPolicy::Authorizer")]
7
9
  pub struct RAuthorizer(Authorizer);
@@ -11,18 +13,23 @@ impl RAuthorizer {
11
13
  RAuthorizer(Authorizer::new())
12
14
  }
13
15
 
14
- fn is_authorized(&self, request: &RRequest, policy: &RPolicySet, entities: &REntities) -> bool {
15
- let response = self
16
- .0
17
- .is_authorized(request.into(), &policy.into(), &entities.into());
18
- response.decision() == Decision::Allow
16
+ fn is_authorized(
17
+ &self,
18
+ request: &RRequest,
19
+ policy: &RPolicySet,
20
+ entities: EntitiesWrapper,
21
+ ) -> bool {
22
+ self.0
23
+ .is_authorized(request.into(), &policy.into(), &entities.into())
24
+ .decision()
25
+ == Decision::Allow
19
26
  }
20
27
 
21
28
  fn authorize(
22
29
  &self,
23
30
  request: &RRequest,
24
31
  policy: &RPolicySet,
25
- entities: &REntities,
32
+ entities: EntitiesWrapper,
26
33
  ) -> RResponse {
27
34
  self.0
28
35
  .is_authorized(request.into(), &policy.into(), &entities.into())
@@ -0,0 +1,51 @@
1
+ use cedar_policy::Context;
2
+ use cedar_policy_core::jsonvalue::JsonValueWithNoDuplicateKeys;
3
+ use magnus::{
4
+ value::{Lazy, ReprValue},
5
+ Error, Module, RClass, Ruby, TryConvert, Value,
6
+ };
7
+ use serde_magnus::deserialize;
8
+
9
+ use crate::CEDAR_POLICY;
10
+
11
+ static CONTEXT: Lazy<RClass> = Lazy::new(|ruby| {
12
+ ruby.get_inner(&CEDAR_POLICY)
13
+ .define_class("Context", ruby.class_object())
14
+ .unwrap()
15
+ });
16
+
17
+ pub struct ContextWrapper(Context);
18
+
19
+ impl From<ContextWrapper> for Context {
20
+ fn from(value: ContextWrapper) -> Self {
21
+ value.0
22
+ }
23
+ }
24
+
25
+ impl TryConvert for ContextWrapper {
26
+ fn try_convert(value: Value) -> Result<Self, Error> {
27
+ let handle = Ruby::get_with(value);
28
+ match value.respond_to("to_hash", false) {
29
+ Ok(true) => {
30
+ let value: Value = value.funcall_public("to_hash", ())?;
31
+ let value: JsonValueWithNoDuplicateKeys = deserialize(value)?;
32
+ Ok(Self(Context::from_json_value(value.into(), None).map_err(
33
+ |e| Error::new(handle.exception_runtime_error(), e.to_string()),
34
+ )?))
35
+ }
36
+ Err(e) => Err(Error::new(handle.exception_runtime_error(), e.to_string())),
37
+ _ => Err(Error::new(
38
+ handle.exception_arg_error(),
39
+ format!("no implicit conversion of {} into Context", unsafe {
40
+ value.classname()
41
+ }),
42
+ ))?,
43
+ }
44
+ }
45
+ }
46
+
47
+ pub fn init(ruby: &Ruby) -> Result<(), Error> {
48
+ Lazy::force(&CONTEXT, ruby);
49
+
50
+ Ok(())
51
+ }
@@ -1,19 +1,27 @@
1
1
  use cedar_policy::Decision;
2
2
  use magnus::{
3
- function, method, typed_data::IsEql, value::ReprValue, Class, Error, Module, Object, RModule,
4
- Ruby, TryConvert, Value,
3
+ method,
4
+ typed_data::IsEql,
5
+ value::{Lazy, ReprValue},
6
+ Class, Error, IntoValue, Module, RClass, Ruby, TryConvert, Value,
5
7
  };
6
8
 
7
- #[magnus::wrap(class = "CedarPolicy::Decision")]
8
- pub struct RDecision(Decision);
9
+ use crate::CEDAR_POLICY;
9
10
 
10
- fn allow() -> RDecision {
11
- RDecision(Decision::Allow)
12
- }
11
+ static DECISION: Lazy<RClass> = Lazy::new(|ruby| {
12
+ ruby.get_inner(&CEDAR_POLICY)
13
+ .define_class("Decision", ruby.class_object())
14
+ .unwrap()
15
+ });
13
16
 
14
- fn deny() -> RDecision {
15
- RDecision(Decision::Deny)
16
- }
17
+ pub static DECISION_ALLOW: Lazy<Value> =
18
+ Lazy::new(|ruby| RDecision(Decision::Allow).into_value_with(ruby));
19
+
20
+ pub static DECISION_DENY: Lazy<Value> =
21
+ Lazy::new(|ruby| RDecision(Decision::Deny).into_value_with(ruby));
22
+
23
+ #[magnus::wrap(class = "CedarPolicy::Decision")]
24
+ pub struct RDecision(Decision);
17
25
 
18
26
  impl IsEql for RDecision {
19
27
  fn is_eql(&self, other: Value) -> bool {
@@ -42,12 +50,15 @@ impl From<Decision> for RDecision {
42
50
  }
43
51
  }
44
52
 
45
- pub fn init(ruby: &Ruby, module: &RModule) -> Result<(), Error> {
46
- let class = module.define_class("Decision", ruby.class_object())?;
53
+ pub fn init(ruby: &Ruby) -> Result<(), Error> {
54
+ let class = ruby.get_inner(&DECISION);
55
+ let allow = ruby.get_inner(&DECISION_ALLOW);
56
+ let deny = ruby.get_inner(&DECISION_DENY);
57
+
47
58
  class.undef_default_alloc_func();
48
59
 
49
- class.define_singleton_method("allow", function!(allow, 0))?;
50
- class.define_singleton_method("deny", function!(deny, 0))?;
60
+ class.const_set("ALLOW", allow)?;
61
+ class.const_set("DENY", deny)?;
51
62
 
52
63
  class.define_method("==", method!(<RDecision as IsEql>::is_eql, 1))?;
53
64
  class.define_method("eql?", method!(<RDecision as IsEql>::is_eql, 1))?;
@@ -1,37 +1,51 @@
1
1
  use cedar_policy::Entities;
2
- use magnus::{function, method, Error, Module, Object, RModule, Ruby};
2
+ use cedar_policy_core::jsonvalue::JsonValueWithNoDuplicateKeys;
3
+ use magnus::{
4
+ value::{Lazy, ReprValue},
5
+ Error, Module, RClass, Ruby, TryConvert, Value,
6
+ };
7
+ use serde_magnus::deserialize;
3
8
 
4
- use crate::{entity::REntity, entity_uid::REntityUid, error::ENTITIES_ERROR};
9
+ use crate::CEDAR_POLICY;
5
10
 
6
- #[magnus::wrap(class = "CedarPolicy::Entities")]
7
- pub struct REntities(Entities);
11
+ static ENTITIES: Lazy<RClass> = Lazy::new(|ruby| {
12
+ ruby.get_inner(&CEDAR_POLICY)
13
+ .define_class("Entities", ruby.class_object())
14
+ .unwrap()
15
+ });
8
16
 
9
- impl REntities {
10
- fn new() -> Self {
11
- Self(Entities::empty())
12
- }
13
-
14
- fn from_json(ruby: &Ruby, json: String) -> Result<Self, Error> {
15
- let entities = Entities::from_json_str(&json, None)
16
- .map_err(|error| Error::new(ruby.get_inner(&ENTITIES_ERROR), error.to_string()))?;
17
- Ok(Self(entities))
18
- }
17
+ pub struct EntitiesWrapper(Entities);
19
18
 
20
- fn get(&self, uid: &REntityUid) -> Option<REntity> {
21
- self.0.get(&uid.into()).map(|entity| entity.into())
19
+ impl From<EntitiesWrapper> for Entities {
20
+ fn from(value: EntitiesWrapper) -> Self {
21
+ value.0
22
22
  }
23
23
  }
24
24
 
25
- impl From<&REntities> for Entities {
26
- fn from(entities: &REntities) -> Self {
27
- entities.0.clone()
25
+ impl TryConvert for EntitiesWrapper {
26
+ fn try_convert(value: Value) -> Result<Self, Error> {
27
+ let handle = Ruby::get_with(value);
28
+ match value.respond_to("to_ary", false) {
29
+ Ok(true) => {
30
+ let value: Value = value.funcall_public("to_ary", ())?;
31
+ let value: JsonValueWithNoDuplicateKeys = deserialize(value)?;
32
+ Ok(Self(
33
+ Entities::from_json_value(value.into(), None)
34
+ .map_err(|e| Error::new(handle.exception_arg_error(), e.to_string()))?,
35
+ ))
36
+ }
37
+ Err(e) => Err(Error::new(handle.exception_arg_error(), e.to_string())),
38
+ _ => Err(Error::new(
39
+ handle.exception_arg_error(),
40
+ format!("no implicit conversion of {} into Entities", unsafe {
41
+ value.classname()
42
+ }),
43
+ ))?,
44
+ }
28
45
  }
29
46
  }
30
47
 
31
- pub fn init(ruby: &Ruby, module: &RModule) -> Result<(), Error> {
32
- let class = module.define_class("Entities", ruby.class_object())?;
33
- class.define_singleton_method("new", function!(REntities::new, 0))?;
34
- class.define_singleton_method("from_json", function!(REntities::from_json, 1))?;
35
- class.define_method("get", method!(REntities::get, 1))?;
48
+ pub fn init(ruby: &Ruby) -> Result<(), Error> {
49
+ Lazy::force(&ENTITIES, ruby);
36
50
  Ok(())
37
51
  }
@@ -1,54 +1,71 @@
1
- use std::str::FromStr;
1
+ use cedar_policy::EntityUid;
2
+ use cedar_policy_core::jsonvalue::JsonValueWithNoDuplicateKeys;
3
+ use magnus::{
4
+ value::{Lazy, ReprValue},
5
+ Class, Error, IntoValue, Module, RClass, Ruby, TryConvert, Value,
6
+ };
7
+ use serde_magnus::deserialize;
2
8
 
3
- use cedar_policy::{EntityId, EntityTypeName, EntityUid};
4
- use magnus::{function, method, Error, Module, Object, RModule, Ruby};
9
+ use crate::CEDAR_POLICY;
5
10
 
6
- #[magnus::wrap(class = "CedarPolicy::EntityUid")]
7
- pub struct REntityUid(EntityUid);
11
+ static ENTITY_UID: Lazy<RClass> = Lazy::new(|ruby| {
12
+ ruby.get_inner(&CEDAR_POLICY)
13
+ .define_class("EntityUid", ruby.class_object())
14
+ .unwrap()
15
+ });
8
16
 
9
- impl REntityUid {
10
- fn new(ruby: &Ruby, entity_type: String, id: String) -> Result<Self, Error> {
11
- let id = EntityId::from_str(&id)
12
- .map_err(|e| Error::new(ruby.exception_arg_error(), e.to_string()))?;
13
- let entity_type = EntityTypeName::from_str(&entity_type)
14
- .map_err(|e| Error::new(ruby.exception_arg_error(), e.to_string()))?;
17
+ pub struct EntityUidWrapper(EntityUid);
15
18
 
16
- return Ok(Self(EntityUid::from_type_name_and_id(entity_type, id)));
17
- }
18
-
19
- fn eq(&self, other: &REntityUid) -> bool {
20
- self.0.eq(&other.0)
19
+ impl EntityUidWrapper {
20
+ pub fn new(uid: EntityUid) -> Self {
21
+ Self(uid)
21
22
  }
23
+ }
22
24
 
23
- fn to_s(&self) -> String {
24
- self.0.to_string()
25
+ impl From<EntityUidWrapper> for EntityUid {
26
+ fn from(value: EntityUidWrapper) -> EntityUid {
27
+ value.0
25
28
  }
26
29
  }
27
30
 
28
- impl From<EntityUid> for REntityUid {
29
- fn from(uid: EntityUid) -> Self {
30
- Self(uid)
31
+ impl IntoValue for EntityUidWrapper {
32
+ fn into_value_with(self, handle: &Ruby) -> Value {
33
+ let type_name = self.0.type_name().to_string();
34
+ let id = self.0.id().to_string();
35
+ let class = handle.get_inner(&ENTITY_UID);
36
+
37
+ return class.new_instance((type_name, id)).unwrap().into();
31
38
  }
32
39
  }
33
40
 
34
- impl From<&EntityUid> for REntityUid {
35
- fn from(uid: &EntityUid) -> Self {
36
- Self(uid.clone())
41
+ impl TryConvert for EntityUidWrapper {
42
+ fn try_convert(value: Value) -> Result<Self, magnus::Error> {
43
+ let handle = Ruby::get_with(value);
44
+ match value.respond_to("to_hash", false) {
45
+ Ok(true) => {
46
+ let value: Value = value.funcall_public("to_hash", ())?;
47
+ let value: JsonValueWithNoDuplicateKeys = deserialize(value)?;
48
+ Ok(Self(EntityUid::from_json(value.into()).map_err(|e| {
49
+ Error::new(handle.exception_runtime_error(), e.to_string())
50
+ })?))
51
+ }
52
+ Err(e) => Err(Error::new(handle.exception_runtime_error(), e.to_string())),
53
+ _ => Err(Error::new(
54
+ handle.exception_arg_error(),
55
+ format!("no implicit conversion of {} into EntityUid", unsafe {
56
+ value.classname()
57
+ }),
58
+ ))?,
59
+ }
37
60
  }
38
61
  }
39
62
 
40
- impl From<&REntityUid> for EntityUid {
41
- fn from(wrapper: &REntityUid) -> Self {
42
- wrapper.0.clone()
43
- }
63
+ pub fn to_euid_value(euid: &EntityUid) -> Value {
64
+ EntityUidWrapper::new(euid.clone()).into_value_with(&Ruby::get().unwrap())
44
65
  }
45
66
 
46
- pub fn init(ruby: &Ruby, module: &RModule) -> Result<(), Error> {
47
- let class = module.define_class("EntityUid", ruby.class_object())?;
48
- class.define_singleton_method("new", function!(REntityUid::new, 2))?;
49
- class.define_method("==", method!(REntityUid::eq, 1))?;
50
- class.define_method("to_s", method!(REntityUid::to_s, 0))?;
51
- class.define_method("inspect", method!(REntityUid::to_s, 0))?;
67
+ pub fn init(ruby: &Ruby) -> Result<(), Error> {
68
+ Lazy::force(&ENTITY_UID, ruby);
52
69
 
53
70
  Ok(())
54
71
  }
@@ -1,9 +1,9 @@
1
1
  use magnus::{value::Lazy, Error, RModule, Ruby};
2
2
 
3
3
  mod authorizer;
4
+ mod context;
4
5
  mod decision;
5
6
  mod entities;
6
- mod entity;
7
7
  mod entity_uid;
8
8
  mod error;
9
9
  mod policy_set;
@@ -17,14 +17,14 @@ fn init(ruby: &Ruby) -> Result<(), Error> {
17
17
  let module = ruby.get_inner(&CEDAR_POLICY);
18
18
 
19
19
  error::init(ruby)?;
20
+ entity_uid::init(ruby)?;
21
+ entities::init(ruby)?;
22
+ decision::init(ruby)?;
23
+ context::init(ruby)?;
20
24
  authorizer::init(ruby, &module)?;
21
- entity_uid::init(ruby, &module)?;
22
- entities::init(ruby, &module)?;
23
- entity::init(ruby, &module)?;
24
25
  request::init(ruby, &module)?;
25
26
  response::init(ruby, &module)?;
26
27
  policy_set::init(ruby, &module)?;
27
- decision::init(ruby, &module)?;
28
28
 
29
29
  Ok(())
30
30
  }
@@ -1,7 +1,7 @@
1
1
  use std::str::FromStr;
2
2
 
3
- use cedar_policy::PolicySet;
4
- use magnus::{function, method, Error, Module, Object, RModule, Ruby};
3
+ use cedar_policy::{ParseErrors, PolicySet};
4
+ use magnus::{function, method, scan_args::scan_args, Error, Module, Object, RModule, Ruby, Value};
5
5
 
6
6
  use crate::error::PARSE_ERROR;
7
7
 
@@ -9,14 +9,15 @@ use crate::error::PARSE_ERROR;
9
9
  pub struct RPolicySet(PolicySet);
10
10
 
11
11
  impl RPolicySet {
12
- fn new() -> Self {
13
- Self(PolicySet::new())
14
- }
15
-
16
- fn from_str(ruby: &Ruby, policy: String) -> Result<Self, Error> {
17
- let policy = PolicySet::from_str(&policy)
18
- .map_err(|e| Error::new(ruby.get_inner(&PARSE_ERROR), e.to_string()))?;
19
- Ok(Self(policy))
12
+ fn new(ruby: &Ruby, args: &[Value]) -> Result<Self, Error> {
13
+ let args = scan_args::<(), _, (), (), (), ()>(args)?;
14
+ let (policy,): (Option<String>,) = args.optional;
15
+
16
+ match policy {
17
+ Some(policy) => Self::from_str(&policy)
18
+ .map_err(|e| Error::new(ruby.get_inner(&PARSE_ERROR), e.to_string())),
19
+ None => Ok(Self(PolicySet::new())),
20
+ }
20
21
  }
21
22
 
22
23
  fn is_empty(&self) -> bool {
@@ -30,10 +31,17 @@ impl From<&RPolicySet> for PolicySet {
30
31
  }
31
32
  }
32
33
 
34
+ impl FromStr for RPolicySet {
35
+ type Err = ParseErrors;
36
+
37
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
38
+ Ok(Self(PolicySet::from_str(s)?))
39
+ }
40
+ }
41
+
33
42
  pub fn init(ruby: &Ruby, module: &RModule) -> Result<(), Error> {
34
43
  let class = module.define_class("PolicySet", ruby.class_object())?;
35
- class.define_singleton_method("new", function!(RPolicySet::new, 0))?;
36
- class.define_singleton_method("from_str", function!(RPolicySet::from_str, 1))?;
44
+ class.define_singleton_method("new", function!(RPolicySet::new, -1))?;
37
45
  class.define_method("empty?", method!(RPolicySet::is_empty, 0))?;
38
46
 
39
47
  Ok(())
@@ -1,40 +1,45 @@
1
- use cedar_policy::{Context, Request};
2
- use magnus::{function, method, Error, Module, Object, RModule, Ruby};
1
+ use cedar_policy::Request;
2
+ use magnus::{function, method, Error, Module, Object, RModule, Ruby, Value};
3
3
  use std::convert::Into;
4
4
 
5
- use crate::entity_uid::REntityUid;
5
+ use crate::{
6
+ context::ContextWrapper,
7
+ entity_uid::{to_euid_value, EntityUidWrapper},
8
+ };
6
9
 
7
10
  #[magnus::wrap(class = "CedarPolicy::Request")]
8
11
  pub struct RRequest(Request);
9
12
 
10
13
  impl RRequest {
11
14
  fn new(
12
- principal: Option<&REntityUid>,
13
- action: Option<&REntityUid>,
14
- resource: Option<&REntityUid>,
15
- ) -> Self {
16
- Self(
15
+ ruby: &Ruby,
16
+ principal: Option<EntityUidWrapper>,
17
+ action: Option<EntityUidWrapper>,
18
+ resource: Option<EntityUidWrapper>,
19
+ context: ContextWrapper,
20
+ ) -> Result<Self, Error> {
21
+ Ok(Self(
17
22
  Request::new(
18
- principal.map(&Into::into),
19
- action.map(&Into::into),
20
- resource.map(&Into::into),
21
- Context::empty(),
23
+ principal.map(|p| p.into()),
24
+ action.map(|a| a.into()),
25
+ resource.map(|r| r.into()),
26
+ context.into(),
22
27
  None,
23
28
  )
24
- .unwrap(),
25
- )
29
+ .map_err(|e| Error::new(ruby.exception_runtime_error(), e.to_string()))?,
30
+ ))
26
31
  }
27
32
 
28
- fn principal(&self) -> Option<REntityUid> {
29
- self.0.principal().map(&Into::into)
33
+ fn principal(&self) -> Option<Value> {
34
+ self.0.principal().map(&to_euid_value)
30
35
  }
31
36
 
32
- fn action(&self) -> Option<REntityUid> {
33
- self.0.action().map(&Into::into)
37
+ fn action(&self) -> Option<Value> {
38
+ self.0.action().map(&to_euid_value)
34
39
  }
35
40
 
36
- fn resource(&self) -> Option<REntityUid> {
37
- self.0.resource().map(&Into::into)
41
+ fn resource(&self) -> Option<Value> {
42
+ self.0.resource().map(&to_euid_value)
38
43
  }
39
44
  }
40
45
 
@@ -46,7 +51,7 @@ impl<'a> From<&'a RRequest> for &'a Request {
46
51
 
47
52
  pub fn init(ruby: &Ruby, module: &RModule) -> Result<(), Error> {
48
53
  let class = module.define_class("Request", ruby.class_object())?;
49
- class.define_singleton_method("new", function!(RRequest::new, 3))?;
54
+ class.define_singleton_method("new", function!(RRequest::new, 4))?;
50
55
  class.define_method("principal", method!(RRequest::principal, 0))?;
51
56
  class.define_method("action", method!(RRequest::action, 0))?;
52
57
  class.define_method("resource", method!(RRequest::resource, 0))?;
@@ -7,7 +7,7 @@ use crate::decision::RDecision;
7
7
  pub struct RResponse(Response);
8
8
 
9
9
  impl RResponse {
10
- fn decision(&self) -> RDecision {
10
+ pub fn decision(&self) -> RDecision {
11
11
  self.0.decision().into()
12
12
  }
13
13
  }
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CedarPolicy
4
+ # :nodoc:
5
+ class Context
6
+ def initialize(context = {})
7
+ @context = context
8
+ end
9
+
10
+ def to_hash
11
+ CedarPolicy.deep_serialize(@context)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CedarPolicy
4
+ # :nodoc:
5
+ class Entities
6
+ def initialize(entities = [])
7
+ @entities = Set.new(entities)
8
+ end
9
+
10
+ def to_ary
11
+ @entities.map { |entity| CedarPolicy.deep_serialize(entity) }
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CedarPolicy
4
+ # :nodoc:
5
+ class Entity
6
+ attr_reader :uid, :attrs, :parents
7
+
8
+ def initialize(uid, attrs = {}, parents = [])
9
+ raise ArgumentError unless uid.is_a?(EntityUid)
10
+
11
+ @uid = uid
12
+ @attrs = attrs
13
+ @parents = Set.new(parents)
14
+ end
15
+
16
+ def ==(other)
17
+ hahs == other.hash
18
+ end
19
+
20
+ def hash
21
+ [self.class, @uid].hash
22
+ end
23
+
24
+ def to_hash
25
+ {
26
+ uid: @uid,
27
+ attrs: @attrs,
28
+ parents: @parents.to_a
29
+ }
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CedarPolicy
4
+ # :nodoc:
5
+ class EntityUid
6
+ attr_reader :type_name, :id
7
+
8
+ def initialize(type_name, id)
9
+ @type_name = type_name.to_s
10
+ @id = id.to_s
11
+ end
12
+
13
+ def ==(other)
14
+ hash == other.hash
15
+ end
16
+
17
+ def hash
18
+ [self.class, @type_name, @id].hash
19
+ end
20
+
21
+ def to_str
22
+ "#{@type_name}::#{@id.inspect}"
23
+ end
24
+ alias to_s to_str
25
+ alias inspect to_str
26
+
27
+ def to_hash
28
+ { type: @type_name, id: @id }
29
+ end
30
+ end
31
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CedarPolicy
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/cedar_policy.rb CHANGED
@@ -1,9 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "json"
4
+ require "set"
5
+
3
6
  require_relative "cedar_policy/version"
4
7
  require_relative "cedar_policy/cedar_policy"
8
+ require_relative "cedar_policy/entity_uid"
9
+ require_relative "cedar_policy/entity"
10
+ require_relative "cedar_policy/entities"
11
+ require_relative "cedar_policy/context"
5
12
 
13
+ # :nodoc:
6
14
  module CedarPolicy
7
15
  class Error < StandardError; end
8
- # Your code goes here...
16
+
17
+ def self.deep_serialize(input)
18
+ input.to_hash.each_with_object({}) do |(key, value), output|
19
+ output[key.to_sym] =
20
+ case value
21
+ when ->(h) { h.respond_to?(:to_hash) } then deep_serialize(value)
22
+ when Array
23
+ value.map { |item| item.respond_to?(:to_hash) ? deep_serialize(item) : item }
24
+ else
25
+ value
26
+ end
27
+ end
28
+ end
9
29
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cedar_policy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aotokitsuruya
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-08-13 00:00:00.000000000 Z
11
+ date: 2024-08-14 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Ruby bindings for Cedar policy evaluation engine.
14
14
  email:
@@ -29,9 +29,9 @@ files:
29
29
  - ext/cedar_policy/Cargo.toml
30
30
  - ext/cedar_policy/extconf.rb
31
31
  - ext/cedar_policy/src/authorizer.rs
32
+ - ext/cedar_policy/src/context.rs
32
33
  - ext/cedar_policy/src/decision.rs
33
34
  - ext/cedar_policy/src/entities.rs
34
- - ext/cedar_policy/src/entity.rs
35
35
  - ext/cedar_policy/src/entity_uid.rs
36
36
  - ext/cedar_policy/src/error.rs
37
37
  - ext/cedar_policy/src/lib.rs
@@ -39,6 +39,10 @@ files:
39
39
  - ext/cedar_policy/src/request.rs
40
40
  - ext/cedar_policy/src/response.rs
41
41
  - lib/cedar_policy.rb
42
+ - lib/cedar_policy/context.rb
43
+ - lib/cedar_policy/entities.rb
44
+ - lib/cedar_policy/entity.rb
45
+ - lib/cedar_policy/entity_uid.rb
42
46
  - lib/cedar_policy/version.rb
43
47
  - sig/cedar_policy.rbs
44
48
  homepage: https://github.com/elct9620/cedar-policy-rb
@@ -1,70 +0,0 @@
1
- use std::{
2
- collections::{HashMap, HashSet},
3
- str::FromStr,
4
- };
5
-
6
- use cedar_policy::{Entity, RestrictedExpression};
7
- use magnus::{
8
- function, method, scan_args::scan_args, Error, Module, Object, RHash, RModule, Ruby, Value,
9
- };
10
-
11
- use crate::{entity_uid::REntityUid, error::PARSE_ERROR};
12
-
13
- #[magnus::wrap(class = "CedarPolicy::Entity")]
14
- pub struct REntity(Entity);
15
-
16
- impl REntity {
17
- fn new(ruby: &Ruby, args: &[Value]) -> Result<REntity, Error> {
18
- let args = scan_args::<_, _, (), (), (), ()>(args)?;
19
- let (uid,): (&REntityUid,) = args.required;
20
- let (attrs,): (Option<RHash>,) = args.optional;
21
-
22
- match attrs {
23
- Some(attrs) => Ok(Self(
24
- Entity::new(uid.into(), try_convert_attrs(ruby, &attrs)?, HashSet::new())
25
- .map_err(|e| Error::new(ruby.get_inner(&PARSE_ERROR), e.to_string()))?,
26
- )),
27
- None => Ok(Self(Entity::with_uid(uid.into()))),
28
- }
29
- }
30
-
31
- fn uid(&self) -> REntityUid {
32
- self.0.uid().into()
33
- }
34
- }
35
-
36
- impl From<&Entity> for REntity {
37
- fn from(entity: &Entity) -> Self {
38
- Self(entity.clone())
39
- }
40
- }
41
-
42
- fn to_attr(
43
- ruby: &Ruby,
44
- key: String,
45
- value: String,
46
- ) -> Result<(String, RestrictedExpression), Error> {
47
- Ok((
48
- key,
49
- RestrictedExpression::from_str(&value)
50
- .map_err(|e| Error::new(ruby.get_inner(&PARSE_ERROR), e.to_string()))?,
51
- ))
52
- }
53
-
54
- fn try_convert_attrs(
55
- ruby: &Ruby,
56
- attrs: &RHash,
57
- ) -> Result<HashMap<String, RestrictedExpression>, Error> {
58
- Ok(attrs
59
- .to_hash_map::<String, String>()?
60
- .iter()
61
- .map(|(k, v)| to_attr(ruby, k.to_string(), v.to_string()))
62
- .collect::<Result<HashMap<String, RestrictedExpression>, Error>>()?)
63
- }
64
-
65
- pub fn init(ruby: &Ruby, module: &RModule) -> Result<(), Error> {
66
- let class = module.define_class("Entity", ruby.class_object())?;
67
- class.define_singleton_method("new", function!(REntity::new, -1))?;
68
- class.define_method("uid", method!(REntity::uid, 0))?;
69
- Ok(())
70
- }