///|
fn decode_attribute_map(
value : Json,
field~ : String,
) -> Map[String, AttributeValue] raise PolicyParseError {
let object = expect_object(value, field~)
let attributes : Map[String, AttributeValue] = {}
for name, item in object {
if name == "" {
raise InvalidPolicy(field + " contains an empty attribute name")
}
attributes[name] = decode_attribute_value(item, field=field + "." + name)
}
attributes
}
///|
fn decode_optional_attributes(
object : Map[String, Json],
field : String,
) -> Map[String, AttributeValue] raise PolicyParseError {
match object.get(field) {
Some(value) => decode_attribute_map(value, field~)
None => {}
}
}
///|
/// Decode a JSON authorization request. The required keys are `subject`,
/// `action`, and `resource`. The optional `subject_attributes`,
/// `resource_attributes`, and `context` maps contain scalar values.
pub fn request_from_json(source : String) -> Request raise PolicyParseError {
let root = @json.parse(source) catch {
error => raise InvalidJson(error.to_string())
}
decode_request(root)
}
///|
fn decode_request(root : Json) -> Request raise PolicyParseError {
let object = expect_object(root, field="request")
let subject = expect_string(
required_field(object, "subject"),
field="subject",
)
let action = expect_string(required_field(object, "action"), field="action")
let resource = expect_string(
required_field(object, "resource"),
field="resource",
)
if subject == "" || action == "" || resource == "" {
raise InvalidPolicy("subject, action, and resource must not be empty")
}
Request(
subject~,
action~,
resource~,
subject_attributes=decode_optional_attributes(object, "subject_attributes"),
resource_attributes=decode_optional_attributes(
object, "resource_attributes",
),
context=decode_optional_attributes(object, "context"),
)
}
///|
/// Decode a JSON array of requests for batch checks or policy regression tests.
pub fn requests_from_json(
source : String,
) -> Array[Request] raise PolicyParseError {
let root = @json.parse(source) catch {
error => raise InvalidJson(error.to_string())
}
let entries = expect_array(root, field="requests")
let requests : Array[Request] = []
for entry in entries {
requests.push(decode_request(entry))
}
requests
}
///|
fn attribute_map_to_json(attributes : Map[String, AttributeValue]) -> Json {
let object : Map[String, Json] = {}
for key, value in attributes {
object[key] = attribute_value_to_json(value)
}
Json::object(object)
}
///|
/// Serialize a request into the same format accepted by `request_from_json`.
pub fn Request::to_json(self : Request) -> Json {
Json::object({
"subject": Json::string(self.subject),
"action": Json::string(self.action),
"resource": Json::string(self.resource),
"subject_attributes": attribute_map_to_json(self.subject_attributes),
"resource_attributes": attribute_map_to_json(self.resource_attributes),
"context": attribute_map_to_json(self.context),
})
}
///|
fn DecisionReason::as_string(self : DecisionReason) -> String {
match self {
Allowed => "allowed"
ExplicitDeny => "explicit_deny"
NoMatchingRule => "no_matching_rule"
}
}
///|
/// Serialize a decision as stable, machine-readable JSON.
pub fn Decision::to_json(self : Decision) -> Json {
Json::object({
"allowed": Json::boolean(self.allowed),
"reason": Json::string(self.reason.as_string()),
"trace": Json::array([ for item in self.trace => Json::string(item) ]),
})
}