Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ public static List<SnowflakeGrantBuilder> containerAllGrantBuilders(
playbookPrivilegeGrant.databaseName(),
playbookPrivilegeGrant.schemaName()));
}
String objectName = String.format("%s.<%s>", containerName, objectType.getObjectType().replace(" ", "_"));
String objectName = String.format("%s.<%s>", containerName, objectType.name());
List<SnowflakeGrantModel> grants = playbookPrivilegeGrant.privileges().stream()
.map(p -> new SnowflakeGrantModel(
p,
objectType.getObjectType().replace(" ", "_"),
objectType.name(),
objectName,
"ROLE",
roleName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ public List<String> getGrantStatements() {
return List.of(String.format(
"GRANT %s ON ALL %s IN %s %s TO ROLE %s%s;",
transformPrivilege(getGrant().privilege()),
futureGrantObjectNameParts.objectType().getObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getObjectType(),
futureGrantObjectNameParts.objectType().getSqlQueryObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getSqlQueryObjectType(),
futureGrantObjectNameParts.objectName(),
getGrant().granteeName(),
withGrantOption));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ public List<String> getGrantStatements() {
return List.of(String.format(
"GRANT %s ON ALL %s IN %s %s TO ROLE %s%s;",
transformPrivilege(getGrant().privilege()),
futureGrantObjectNameParts.objectType().getObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getObjectType(),
futureGrantObjectNameParts.objectType().getSqlQueryObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getSqlQueryObjectType(),
futureGrantObjectNameParts.objectName(),
getGrant().granteeName(),
withGrantOption));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ public List<String> getGrantStatements() {
= GrantObjectNameParser.futureGrantObjectNameParts(getGrant().name());
return List.of(String.format(
"GRANT OWNERSHIP ON ALL %s IN %s %s TO ROLE %s COPY CURRENT GRANTS;",
futureGrantObjectNameParts.objectType().getObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getObjectType(),
futureGrantObjectNameParts.objectType().getSqlQueryObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getSqlQueryObjectType(),
futureGrantObjectNameParts.objectName(),
getGrant().granteeName()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ public List<String> getGrantStatements() {
return List.of(String.format(
"GRANT %s ON ALL %s IN %s %s TO ROLE %s%s;",
getGrant().privilege(),
futureGrantObjectNameParts.objectType().getObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getObjectType(),
futureGrantObjectNameParts.objectType().getSqlQueryObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getSqlQueryObjectType(),
futureGrantObjectNameParts.objectName(),
getGrant().granteeName(),
withGrantOption));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public List<String> getGrantStatements() {
return List.of(String.format(
"GRANT %s ON %s %s TO ROLE %s%s;",
transformPrivilege(grant.privilege()),
SnowflakeObjectType.fromString(grant.grantedOn()).getObjectType(),
SnowflakeObjectType.fromString(grant.grantedOn()).getSqlQueryObjectType(),
grant.getEscapedName(),
grant.granteeName(),
withGrantOption));
Expand All @@ -64,7 +64,7 @@ public List<String> getRevokeStatements() {
return List.of(String.format(
"REVOKE %s ON %s %s FROM ROLE %s;",
transformPrivilege(grant.privilege()),
SnowflakeObjectType.fromString(grant.grantedOn()).getObjectType(),
SnowflakeObjectType.fromString(grant.grantedOn()).getSqlQueryObjectType(),
grant.getEscapedName(),
grant.granteeName()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ public List<String> getGrantStatements() {
return List.of(String.format(
"GRANT %s ON FUTURE %s IN %s %s TO ROLE %s%s;",
transformPrivilege(getGrant().privilege()),
futureGrantObjectNameParts.objectType().getObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getObjectType(),
futureGrantObjectNameParts.objectType().getSqlQueryObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getSqlQueryObjectType(),
futureGrantObjectNameParts.objectName(),
getGrant().granteeName(),
withGrantOption));
Expand All @@ -49,8 +49,8 @@ public List<String> getRevokeStatements() {
return List.of(String.format(
"REVOKE %s ON FUTURE %s IN %s %s FROM ROLE %s;",
transformPrivilege(getGrant().privilege()),
futureGrantObjectNameParts.objectType().getObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getObjectType(),
futureGrantObjectNameParts.objectType().getSqlQueryObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getSqlQueryObjectType(),
futureGrantObjectNameParts.objectName(),
getGrant().granteeName()));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ public List<String> getGrantStatements() {
return List.of(String.format(
"GRANT %s ON FUTURE %s IN %s %s TO ROLE %s%s;",
transformPrivilege(getGrant().privilege()),
futureGrantObjectNameParts.objectType().getObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getObjectType(),
futureGrantObjectNameParts.objectType().getSqlQueryObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getSqlQueryObjectType(),
futureGrantObjectNameParts.objectName(),
getGrant().granteeName(),
withGrantOption));
Expand All @@ -49,8 +49,8 @@ public List<String> getRevokeStatements() {
return List.of(String.format(
"REVOKE %s ON FUTURE %s IN %s %s FROM ROLE %s;",
transformPrivilege(getGrant().privilege()),
futureGrantObjectNameParts.objectType().getObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getObjectType(),
futureGrantObjectNameParts.objectType().getSqlQueryObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getSqlQueryObjectType(),
futureGrantObjectNameParts.objectName(),
getGrant().granteeName()));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ public List<String> getGrantStatements() {
= GrantObjectNameParser.futureGrantObjectNameParts(getGrant().name());
return List.of(String.format(
"GRANT OWNERSHIP ON FUTURE %s IN %s %s TO ROLE %s COPY CURRENT GRANTS;",
futureGrantObjectNameParts.objectType().getObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getObjectType(),
futureGrantObjectNameParts.objectType().getSqlQueryObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getSqlQueryObjectType(),
futureGrantObjectNameParts.objectName(),
getGrant().granteeName()));
}
Expand All @@ -42,8 +42,8 @@ public List<String> getRevokeStatements() {
= GrantObjectNameParser.futureGrantObjectNameParts(getGrant().name());
return List.of(String.format(
"REVOKE OWNERSHIP ON FUTURE %s IN %s %s FROM ROLE %s;",
futureGrantObjectNameParts.objectType().getObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getObjectType(),
futureGrantObjectNameParts.objectType().getSqlQueryObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getSqlQueryObjectType(),
futureGrantObjectNameParts.objectName(),
getGrant().granteeName()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ public List<String> getGrantStatements() {
return List.of(String.format(
"GRANT %s ON FUTURE %s IN %s %s TO ROLE %s%s;",
getGrant().privilege(),
futureGrantObjectNameParts.objectType().getObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getObjectType(),
futureGrantObjectNameParts.objectType().getSqlQueryObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getSqlQueryObjectType(),
futureGrantObjectNameParts.objectName(),
getGrant().granteeName(),
withGrantOption));
Expand All @@ -49,8 +49,8 @@ public List<String> getRevokeStatements() {
return List.of(String.format(
"REVOKE %s ON FUTURE %s IN %s %s FROM ROLE %s;",
getGrant().privilege(),
futureGrantObjectNameParts.objectType().getObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getObjectType(),
futureGrantObjectNameParts.objectType().getSqlQueryObjectTypePlural(),
futureGrantObjectNameParts.containerObjectType().getSqlQueryObjectType(),
futureGrantObjectNameParts.objectName(),
getGrant().granteeName()));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public List<String> getGrantStatements() {
return List.of(String.format(
"GRANT %s ON %s %s TO ROLE %s%s;",
transformPrivilege(grant.privilege()),
SnowflakeObjectType.fromString(grant.grantedOn()).getObjectType(),
SnowflakeObjectType.fromString(grant.grantedOn()).getSqlQueryObjectType(),
grant.getEscapedName(),
grant.granteeName(),
withGrantOption));
Expand All @@ -65,7 +65,7 @@ public List<String> getRevokeStatements() {
return List.of(String.format(
"REVOKE %s ON %s %s FROM ROLE %s;",
transformPrivilege(grant.privilege()),
SnowflakeObjectType.fromString(grant.grantedOn()).getObjectType(),
SnowflakeObjectType.fromString(grant.grantedOn()).getSqlQueryObjectType(),
grant.getEscapedName(),
grant.granteeName()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,29 @@
import java.util.Map;
import java.util.Objects;

/**
* Snowflake object types that can be granted on (databases, schemas, tables, agents, etc.).
* <p>
* Each type has a qualification level ({@link #getQualLevel()}) indicating how many name parts
* it has: 0 = account-level, 1 = one part (e.g. database name), 2 = two parts (e.g. db.schema),
* 3 = three parts (e.g. db.schema.object). The {@link #getSqlQueryObjectType()} and {@link
* #getSqlQueryObjectTypePlural()} strings are used in SQL (e.g. "SHOW AGENTS IN DATABASE"). Use {@link
* #name()} when building grant names so desired state matches what Snowflake
* returns for the object type in SHOW GRANTS statements. {@link #name()} is also used for matching the object name as
* it appears in future grant object names such as MY_DB.MY_SCHEMA.&ltTABLE&gt..
* <p>
* {@link #getAliasFor()} is used only when building hash keys for grants (e.g. in {@code
* SnowflakeGrantBuilder.getKey()} and the revoke index). It is not user-facing and does not
* allow the user to specify a different name in the playbook. It exists to align on differences
* that come from Snowflake: the same logical object type may appear under different names in
* different Snowflake APIs (e.g. MATERIALIZED_VIEW in one place, VIEW in another). By having
* multiple enum values return the same alias, they are treated as the same when calculating
* which grants to add or remove. For playbook input names (e.g. "AGENT" instead of
* "CORTEX_AGENT"), see {@link #overrideObjectTypes}.
*
* @see #fromString(String) for resolving playbook or Snowflake strings to this enum
* @see #overrideObjectTypes for playbook input mapping (e.g. AGENT → CORTEX_AGENT)
*/
public enum SnowflakeObjectType {
ACCOUNT(0, null),
ALERT(3, null),
Expand Down Expand Up @@ -51,58 +74,94 @@ public enum SnowflakeObjectType {
IMAGE_REPOSITORY(3, null);


/** Number of name parts (0=account, 1=e.g. db, 2=e.g. db.schema, 3=e.g. db.schema.object). */
@Getter
private final Integer qualLevel;

// objectType is used for SQL statements about the object.
/** Singular form used in SQL (e.g. "TABLE" will be used in "GRANT SELECT ON TABLE TO ROLE FOO"). */
@Getter
private final String objectType;
private final String sqlQueryObjectType;

// objectTypePlural is used for SQL statements about multiple objects.
/** Plural form used in SQL (e.g. TABLES WILL BE USED IN "SHOW GRANTS ON TABLES"). */
@Getter
private final String objectTypePlural;
private final String sqlQueryObjectTypePlural;

// Alias for is used for considering grants on different object types equivalent for the sake of hashing and comparison.
/**
* When non-null, used only in hash keys so that this type and another (e.g. VIEW) are
* treated as the same when computing grants to add or remove. Aligns Snowflake variations
* (e.g. MATERIALIZED_VIEW may appear as VIEW in some output). Not user-facing; see {@link
* #overrideObjectTypes} for playbook input names. However, aliasFor does not change the
* underlying enum type. It is only changes how the objectType is used for calculating
* grants to add or remove.
*/
@Getter
private final String aliasFor;

// Main constructor with all parameters - contains the actual logic
SnowflakeObjectType(Integer qualLevel, String aliasFor, String objectType, String objectTypePlural) {
/**
* Main constructor with all parameters.
*
* @param qualLevel number of name parts (0=account, 1=e.g. db, 2=e.g. db.schema,
* 3=e.g. db.schema.object)
* @param aliasFor when non-null, value used in hash keys so this type matches another
* (e.g. MATERIALIZED_VIEW uses "VIEW"); null if no alias
* @param sqlQueryObjectType singular form for SQL (e.g. "AGENT", "TABLE"); null to infer from
* enum name (underscores to spaces)
* @param sqlQueryObjectTypePlural plural form for SQL (e.g. "AGENTS", "TABLES"); null to infer from
* objectType
*/
SnowflakeObjectType(Integer qualLevel, String aliasFor, String sqlQueryObjectType, String sqlQueryObjectTypePlural) {
this.qualLevel = qualLevel;
this.aliasFor = aliasFor;
// If objectType is provided, use it; otherwise infer from enum name
String computedObjectType = (objectType != null) ? objectType : this.name().replace("_", " ");
this.objectType = computedObjectType;
String computedObjectType = (sqlQueryObjectType != null) ? sqlQueryObjectType : this.name().replace("_", " ");
this.sqlQueryObjectType = computedObjectType;
// If objectTypePlural is provided, use it; otherwise infer from objectType
if (objectTypePlural != null) {
this.objectTypePlural = objectTypePlural;
if (sqlQueryObjectTypePlural != null) {
this.sqlQueryObjectTypePlural = sqlQueryObjectTypePlural;
} else {
// Hooked on phonics works for me.
if (computedObjectType.endsWith("Y")) {
this.objectTypePlural = computedObjectType.substring(0, computedObjectType.length() - 1) + "IES";
this.sqlQueryObjectTypePlural = computedObjectType.substring(0, computedObjectType.length() - 1) + "IES";
} else {
this.objectTypePlural = computedObjectType + "S";
this.sqlQueryObjectTypePlural = computedObjectType + "S";
}
}
this.aliasFor = Objects.requireNonNullElseGet(aliasFor, this::name);
}

// Constructor that infers objectType and objectTypePlural from enum name
/**
* Constructor that infers objectType and objectTypePlural from enum name (e.g. TABLE → "TABLE",
* "TABLES").
*
* @param qualLevel number of name parts (0=account, 1=e.g. db, 2=e.g. db.schema,
* 3=e.g. db.schema.object)
* @param aliasFor when non-null, value used in hash keys so this type matches another; null
* if no alias
*/
SnowflakeObjectType(Integer qualLevel, String aliasFor) {
this(qualLevel, aliasFor, null, null);
}

public String getAliasFor() {
if (this.aliasFor == null) {
return this.name();
}
return this.aliasFor;
}

/**
* Resolves a string (from playbook, Snowflake grant output, etc.) to this enum.
* Applies {@link #overrideObjectTypes} so that e.g. "AGENT" maps to {@link #CORTEX_AGENT}.
*
* @param objectType the object type string (case-insensitive)
* @return the corresponding SnowflakeObjectType
* @throws IllegalArgumentException if the string does not match any enum and is not in the
* override map
*/
public static SnowflakeObjectType fromString(String objectType) {
String normalizedObjectType = objectType.toUpperCase();
String overrideValue = overrideObjectTypes.get(normalizedObjectType);
return SnowflakeObjectType.valueOf(Objects.requireNonNullElse(overrideValue, normalizedObjectType));
}

/**
* Map of playbook (or Snowflake) input names to enum names. Used by {@link #fromString(String)}
* so that e.g. "AGENT" in a playbook resolves to {@link #CORTEX_AGENT}. This is what allows
* the user to specify a different name in the playbook; it is separate from {@link
* #getAliasFor()}, which is only for hash keys.
*/
public static Map<String, String> overrideObjectTypes = Map.of(
"AGENT", "CORTEX_AGENT"
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public List<String> getGrantStatements() {
}
return List.of(String.format(
"GRANT OWNERSHIP ON %s %s TO ROLE %s COPY CURRENT GRANTS;",
SnowflakeObjectType.fromString(grant.grantedOn()).getObjectType(),
SnowflakeObjectType.fromString(grant.grantedOn()).getSqlQueryObjectType(),
this.grant.getEscapedName(),
this.grant.granteeName()));
}
Expand All @@ -86,7 +86,7 @@ public List<String> getRevokeStatements() {
}
return List.of(String.format(
"GRANT OWNERSHIP ON %s %s TO ROLE SECURITYADMIN COPY CURRENT GRANTS;",
SnowflakeObjectType.fromString(grant.grantedOn()).getObjectType(),
SnowflakeObjectType.fromString(grant.grantedOn()).getSqlQueryObjectType(),
this.grant.getEscapedName()));
}

Expand Down
Loading