Skip to content

Commit 9f035da

Browse files
committed
Warn on invalid container policy patterns.
- Treat InvalidContainerPolicyPattern as deprecation (log warn, skip grant) - Fix PolicyPatternValidations qual level 2: use validPattern.orElse(deprecatedPattern) so DB+empty schema yields deprecation instead of 'schema is empty' error - Rename ValidationError to ValidationErr with InvalidContainerPolicyPattern record - PolicyTypeFactory returns Option<PolicyType>; DesiredGrantsCompiler/PolicyGrantHashIndexer handle Option; PolicyType container types updated
1 parent f4d6664 commit 9f035da

15 files changed

Lines changed: 237 additions & 189 deletions

File tree

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package us.zoom.data.dfence.policies.factories;
22

3+
import io.vavr.Tuple2;
4+
import io.vavr.collection.Seq;
35
import io.vavr.control.Option;
46
import io.vavr.control.Try;
57
import java.util.List;
@@ -12,7 +14,7 @@
1214
import us.zoom.data.dfence.providers.snowflake.grant.builder.SnowflakeObjectType;
1315
import us.zoom.data.dfence.policies.pattern.factories.PolicyTypeFactory;
1416
import us.zoom.data.dfence.policies.pattern.models.PolicyType;
15-
import us.zoom.data.dfence.policies.pattern.models.ValidationError;
17+
import us.zoom.data.dfence.policies.pattern.models.ValidationErr;
1618
import us.zoom.data.dfence.policies.models.PolicyGrantPrivilege;
1719
import us.zoom.data.dfence.policies.models.PolicyGrant;
1820
import us.zoom.data.dfence.policies.models.PolicyPattern;
@@ -22,7 +24,7 @@
2224
@NoArgsConstructor(access = AccessLevel.PRIVATE)
2325
public final class PolicyGrantFactory {
2426

25-
public static PolicyGrant createFrom(PlaybookPrivilegeGrant grant) {
27+
public static Option<PolicyGrant> createFrom(PlaybookPrivilegeGrant grant) {
2628
return getPlaybookGrant(grant)
2729
.getOrElseThrow(
2830
err -> {
@@ -33,12 +35,12 @@ public static PolicyGrant createFrom(PlaybookPrivilegeGrant grant) {
3335
});
3436
}
3537

36-
private static Try<PolicyGrant> getPlaybookGrant(PlaybookPrivilegeGrant grant) {
38+
private static Try<Option<PolicyGrant>> getPlaybookGrant(PlaybookPrivilegeGrant grant) {
3739
return getSnowflakeObjectType(grant.objectType())
3840
.flatMap(snowflakeObjectType -> getPlaybookGrant(snowflakeObjectType, grant));
3941
}
4042

41-
private static Try<PolicyGrant> getPlaybookGrant(
43+
private static Try<Option<PolicyGrant>> getPlaybookGrant(
4244
SnowflakeObjectType snowflakeObjectType, PlaybookPrivilegeGrant grant) {
4345
return Try.of(
4446
() -> {
@@ -52,38 +54,58 @@ private static Try<PolicyGrant> getPlaybookGrant(
5254
PolicyPattern pattern = PolicyPattern.of(dbName, schName, objName);
5355
PolicyPatternOptions options =
5456
new PolicyPatternOptions(grant.includeFuture(), grant.includeAll());
55-
PolicyType policyType =
56-
createPolicyType(pattern, snowflakeObjectType, options);
57+
Option<PolicyType> policyType =
58+
createPolicyType(grant, pattern, snowflakeObjectType, options);
5759

5860
List<PolicyGrantPrivilege> privileges =
5961
grant.privileges().stream().map(PolicyGrantPrivilege::new).collect(Collectors.toList());
6062

61-
return new PolicyGrant(
62-
snowflakeObjectType, privileges, policyType, grant.enable());
63+
return policyType.map(pt -> new PolicyGrant(
64+
snowflakeObjectType, privileges, pt, grant.enable()));
6365
});
6466
}
6567

6668
private static Try<SnowflakeObjectType> getSnowflakeObjectType(String grantObjectType) {
6769
return Try.of(() -> SnowflakeObjectType.fromString(grantObjectType));
6870
}
6971

70-
private static PolicyType createPolicyType(
72+
private static Option<PolicyType> createPolicyType(
73+
PlaybookPrivilegeGrant grant,
7174
PolicyPattern pattern,
7275
SnowflakeObjectType snowflakeObjectType,
7376
PolicyPatternOptions options) {
7477
return Try.of(
7578
() -> PolicyTypeFactory.createFrom(pattern, snowflakeObjectType, options))
7679
.flatMap(
7780
validatedPattern ->
78-
validatedPattern.fold(
79-
errors ->
80-
Try.failure(
81-
new RbacDataError(
82-
"Policy pattern validation failed: "
83-
+ errors
84-
.map(ValidationError::message)
85-
.mkString("[", ", ", "]"))),
86-
Try::success))
87-
.getOrElseThrow(e -> new RbacDataError("Policy validation failed: " + e.getMessage(), e));
81+
validatedPattern.fold(errors -> handleValidationErrors(grant, errors), value -> Try.success(Option.some(value)))
82+
).getOrElseThrow(e -> new RbacDataError("Policy validation failed: " + e.getMessage(), e));
83+
}
84+
85+
private static Try<Option<PolicyType>> handleValidationErrors(
86+
PlaybookPrivilegeGrant grant,
87+
Seq<ValidationErr> validationErrors) {
88+
Tuple2<? extends Seq<ValidationErr>, ? extends Seq<ValidationErr>> byDeprecation =
89+
validationErrors.partition(err -> err instanceof ValidationErr.InvalidContainerPolicyPattern);
90+
Seq<ValidationErr> deprecationErrs = (Seq<ValidationErr>) byDeprecation._1;
91+
Seq<ValidationErr> rbacErrs = (Seq<ValidationErr>) byDeprecation._2;
92+
93+
if (!deprecationErrs.isEmpty()) {
94+
log.warn(
95+
"[DEPRECATED] Container policy pattern will be treated as an error in a future release. "
96+
+ "Migrate this grant to the required pattern (see validation message). "
97+
+ "Grant: {} | Validation: {}",
98+
grant,
99+
deprecationErrs.map(ValidationErr::message).mkString("; "));
100+
}
101+
if (!rbacErrs.isEmpty()) {
102+
return Try.failure(
103+
new RbacDataError(
104+
String.format(
105+
"Policy pattern validation failed for grant: %s, errors: %s",
106+
grant,
107+
rbacErrs.map(ValidationErr::message).mkString("[", ", ", "]"))));
108+
}
109+
return Try.success(Option.none());
88110
}
89111
}

src/main/java/us/zoom/data/dfence/policies/pattern/factories/PolicyTypeFactory.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,31 @@
44
import io.vavr.control.Validation;
55
import lombok.AccessLevel;
66
import lombok.NoArgsConstructor;
7+
import us.zoom.data.dfence.policies.pattern.models.ValidationErr;
78
import us.zoom.data.dfence.providers.snowflake.grant.builder.SnowflakeObjectType;
89
import us.zoom.data.dfence.policies.validations.PolicyPatternValidations;
910
import us.zoom.data.dfence.policies.pattern.models.PolicyType;
10-
import us.zoom.data.dfence.policies.pattern.models.ValidationError;
1111
import us.zoom.data.dfence.policies.models.PolicyPattern;
1212
import us.zoom.data.dfence.policies.models.PolicyPatternOptions;
1313

1414
@NoArgsConstructor(access = AccessLevel.PRIVATE)
1515
public final class PolicyTypeFactory {
1616

17-
public static Validation<Seq<ValidationError>, PolicyType> createFrom(
17+
public static Validation<Seq<ValidationErr>, PolicyType> createFrom(
1818
PolicyPattern pattern, SnowflakeObjectType objectType, PolicyPatternOptions options) {
1919
PolicyPatternValidations validators = new PolicyPatternValidations(pattern, objectType);
2020

21-
Validation<Seq<ValidationError>, PolicyType> validateStandardPattern =
21+
Validation<Seq<ValidationErr>, PolicyType> validateStandardPattern =
2222
lift(validators.validateStandardPattern());
23-
Validation<Seq<ValidationError>, PolicyType> validateContainerPattern =
23+
Validation<Seq<ValidationErr>, PolicyType> validateContainerPattern =
2424
lift(validators.validateContainerPattern(options));
2525

2626
return validateStandardPattern.orElse(validateContainerPattern);
2727
}
2828

2929
private static <E extends PolicyType>
30-
Validation<Seq<ValidationError>, PolicyType> lift(
31-
Validation<Seq<ValidationError>, E> validation) {
30+
Validation<Seq<ValidationErr>, PolicyType> lift(
31+
Validation<Seq<ValidationErr>, E> validation) {
3232
return validation.map(v -> (PolicyType) v);
3333
}
3434
}

src/main/java/us/zoom/data/dfence/policies/pattern/models/PolicyType.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ public String normalizeObjectName() {
9696
}
9797
}
9898
record SchemaObjectAllSchemas(
99-
String databaseName, String objectName, ContainerPolicyOptions containerPolicyOptions)
99+
String databaseName, ContainerPolicyOptions containerPolicyOptions)
100100
implements Container {
101101
@Override
102102
public SnowflakeObjectType containerObjectType() {
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package us.zoom.data.dfence.policies.pattern.models;
2+
3+
public interface ValidationErr {
4+
String message();
5+
record InvalidContainerPolicyPattern(String message) implements ValidationErr {}
6+
record Error(String message) implements ValidationErr {}
7+
}

src/main/java/us/zoom/data/dfence/policies/pattern/models/ValidationError.java

Lines changed: 0 additions & 8 deletions
This file was deleted.

src/main/java/us/zoom/data/dfence/policies/validations/BaseValidations.java

Lines changed: 49 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,88 +7,98 @@
77
import lombok.AccessLevel;
88
import lombok.NoArgsConstructor;
99
import us.zoom.data.dfence.policies.PolicyWildcards;
10-
import us.zoom.data.dfence.policies.pattern.models.ValidationError;
1110
import us.zoom.data.dfence.policies.models.PolicyPattern;
11+
import us.zoom.data.dfence.policies.pattern.models.ValidationErr;
1212
import us.zoom.data.dfence.sql.ObjectName;
1313

1414
@NoArgsConstructor(access = AccessLevel.PRIVATE)
1515
public class BaseValidations {
1616

17-
public static Validation<ValidationError, String> database(PolicyPattern pattern) {
18-
return db(pattern).validValue("database");
19-
}
17+
public static <E, A> Validation<Seq<E>, A> liftError(Validation<E, A> validation) {
18+
return validation.mapError(List::of);
19+
}
2020

21-
public static Validation<ValidationError, String> schema(PolicyPattern pattern) {
22-
return sch(pattern).validValue("schema");
21+
22+
public static Validation<ValidationErr, String> database(PolicyPattern pattern) {
23+
return db(pattern).validValue();
2324
}
2425

25-
public static Validation<ValidationError, String> object(PolicyPattern pattern) {
26-
return obj(pattern).validValue("object");
26+
public static Validation<ValidationErr, String> schema(PolicyPattern pattern) {
27+
return sch(pattern).validValue();
2728
}
2829

29-
public static <E, A> Validation<Seq<E>, A> liftError(Validation<E, A> validation) {
30-
return validation.mapError(List::of);
30+
public static Validation<ValidationErr, String> object(PolicyPattern pattern) {
31+
return obj(pattern).validValue();
3132
}
3233

3334
public static SelectedFieldForValidation db(PolicyPattern pattern) {
34-
return new SelectedFieldForValidation(pattern.dbName());
35+
return new SelectedFieldForValidation(pattern.dbName(), "database");
3536
}
3637

3738
public static SelectedFieldForValidation sch(PolicyPattern pattern) {
38-
return new SelectedFieldForValidation(pattern.schName());
39+
return new SelectedFieldForValidation(pattern.schName(), "schema");
3940
}
4041

4142
public static SelectedFieldForValidation obj(PolicyPattern pattern) {
42-
return new SelectedFieldForValidation(pattern.objName());
43+
return new SelectedFieldForValidation(pattern.objName(), "object");
4344
}
44-
public record SelectedFieldForValidation(Option<String> field) {
45+
public record SelectedFieldForValidation(Option<String> fieldValue, String fieldName) {
46+
47+
public Validation<ValidationErr, Void> anything() {
48+
return empty().orElse(wildcard()).orElse(validValueVoid());
49+
}
4550

46-
public Validation<ValidationError, String> validValue(String fieldName) {
47-
return nonEmpty(fieldName)
51+
public Validation<ValidationErr, Void> validValueVoid() {
52+
return validValue().map(v -> (Void) null);
53+
}
54+
55+
56+
public Validation<ValidationErr, String> validValue() {
57+
return nonEmpty()
4858
.flatMap(
4959
value ->
5060
PolicyWildcards.isWildcard(value)
5161
? Validation.invalid(
52-
ValidationError.of(
62+
new ValidationErr.Error(
5363
String.format(
5464
"%s value is %s, non-empty and non-wildcard value is expected.",
5565
fieldName, value)))
5666
: Validation.valid(ObjectName.normalizeObjectName(value)));
5767
}
5868

59-
public Validation<ValidationError, Void> emptyOrWildcard(String fieldName) {
60-
return empty(fieldName).orElse(wildcard(fieldName));
69+
public Validation<ValidationErr, Void> emptyOrWildcard() {
70+
return empty().orElse(wildcard());
6171
}
6272

63-
public Validation<ValidationError, Void> empty(String fieldName) {
64-
return field
65-
.<Validation<ValidationError, Void>>map(
73+
public Validation<ValidationErr, Void> empty() {
74+
return fieldValue
75+
.<Validation<ValidationErr, Void>>map(
6676
x ->
6777
Validation.invalid(
68-
ValidationError.of(
78+
new ValidationErr.Error(
6979
String.format("%s is not empty, empty is expected.", fieldName))))
7080
.getOrElse(Validation.valid(null));
7181
}
7282

73-
public Validation<ValidationError, String> nonEmpty(String fieldName) {
74-
return field
75-
.<Validation<ValidationError, String>>map(x -> Validation.valid(x.trim()))
83+
public Validation<ValidationErr, Void> wildcard() {
84+
return nonEmpty()
85+
.flatMap(
86+
value ->
87+
PolicyWildcards.isWildcard(value)
88+
? Validation.valid(null)
89+
: Validation.invalid(
90+
new ValidationErr.Error(
91+
String.format(
92+
"%s value is %s, wildcard is expected.", fieldName, value))));
93+
}
94+
95+
public Validation<ValidationErr, String> nonEmpty() {
96+
return fieldValue
97+
.<Validation<ValidationErr, String>>map(x -> Validation.valid(x.trim()))
7698
.getOrElse(
7799
Validation.invalid(
78-
ValidationError.of(
100+
new ValidationErr.Error(
79101
String.format("%s is empty, non-empty value is expected.", fieldName))));
80102
}
81-
82-
public Validation<ValidationError, Void> wildcard(String fieldName) {
83-
return nonEmpty(fieldName)
84-
.flatMap(
85-
value ->
86-
PolicyWildcards.isWildcard(value)
87-
? Validation.valid(null)
88-
: Validation.invalid(
89-
ValidationError.of(
90-
String.format(
91-
"%s value is %s, wildcard is expected.", fieldName, value))));
92-
}
93103
}
94104
}

0 commit comments

Comments
 (0)