in sources/src/main/java/com/google/solutions/jitaccess/catalog/policy/PolicyDocument.java [721:839]
public record ConstraintElement(
@JsonProperty("type") String type,
@JsonProperty("name") String name,
@JsonProperty("displayName") String displayName,
@JsonProperty("min") String expiryMinDuration,
@JsonProperty("max") String expiryMaxDuration,
@JsonProperty("expression") String celExpression,
@JsonProperty("variables") List<CelVariableElement> celVariables
) {
static ConstraintElement toYaml(@NotNull Constraint constraint) {
if (constraint instanceof ExpiryConstraint expiryConstraint) {
return new ConstraintElement(
"expiry",
null,
null,
expiryConstraint.minDuration().toString(),
expiryConstraint.maxDuration().toString(),
null,
null);
}
else if (constraint instanceof CelConstraint celConstraint) {
return new ConstraintElement(
"expression",
celConstraint.name(),
celConstraint.displayName(),
null,
null,
celConstraint.expression(),
celConstraint.variables()
.stream()
.map(CelVariableElement::toYaml)
.toList());
}
else {
throw new UnsupportedOperationException("The constraint type is not supported");
}
}
@NotNull Optional<Constraint> toPolicy(@NotNull IssueCollection issues) {
return Optional
.ofNullable(switch (Strings.nullToEmpty(this.type).trim().toLowerCase()) {
case "expiry" -> {
if (!Strings.isNullOrEmpty(this.celExpression) || this.celVariables != null) {
issues.error(
Issue.Code.CONSTRAINT_INVALID_EXPIRY,
"Expiry constraints must not specify an expression",
this.type);
}
try {
yield new ExpiryConstraint(
Duration.parse(this.expiryMinDuration),
Duration.parse(this.expiryMaxDuration));
}
catch (Exception e) {
issues.error(
Issue.Code.CONSTRAINT_INVALID_EXPIRY,
"The format of the duration '%s - %s' is invalid: %s",
this.expiryMinDuration,
this.expiryMaxDuration,
e.getMessage());
yield null;
}
}
case "expression" -> {
if (this.expiryMinDuration != null || this.expiryMaxDuration != null) {
issues.error(
Issue.Code.CONSTRAINT_INVALID_EXPRESSION,
"Expression constraints must not specify an expiry",
this.type);
}
var variables = Coalesce.emptyIfNull(this.celVariables)
.stream()
.map(v -> v.toPolicy(issues))
.toList();
if (variables.stream().allMatch(Optional::isPresent)) {
try {
var constraint = new CelConstraint(
this.name,
this.displayName,
variables.stream().map(Optional::get).toList(),
this.celExpression);
for (var issue : constraint.lint()) {
issues.error(
Issue.Code.CONSTRAINT_INVALID_EXPRESSION,
"The constraint '%s' uses an invalid CEL expression: %s",
this.name,
issue.getMessage());
}
yield constraint;
}
catch (Exception e) {
issues.error(
Issue.Code.CONSTRAINT_INVALID_EXPRESSION,
"The CEL constraint is invalid: %s",
e.getMessage());
yield null;
}
}
else {
yield null;
}
}
default -> {
issues.error(
Issue.Code.CONSTRAINT_INVALID_TYPE,
"The constraint type '%s' is invalid",
this.type);
yield null;
}
});
}
}