diff --git a/clients/go/zms/model.go b/clients/go/zms/model.go index 90d3a0b0270..ac18bc1e540 100644 --- a/clients/go/zms/model.go +++ b/clients/go/zms/model.go @@ -287,6 +287,11 @@ type DomainMeta struct { // a single value // Contacts map[SimpleName]string `json:"contacts,omitempty" rdl:"optional" yaml:",omitempty"` + + // + // domain environment e.g. production, staging, etc + // + Environment string `json:"environment" rdl:"optional" yaml:",omitempty"` } // NewDomainMeta - creates an initialized DomainMeta instance, returns a pointer to it @@ -401,6 +406,12 @@ func (self *DomainMeta) Validate() error { return fmt.Errorf("DomainMeta.productId does not contain a valid String (%v)", val.Error) } } + if self.Environment != "" { + val := rdl.Validate(ZMSSchema(), "String", self.Environment) + if !val.Valid { + return fmt.Errorf("DomainMeta.environment does not contain a valid String (%v)", val.Error) + } + } return nil } @@ -544,6 +555,11 @@ type Domain struct { // Contacts map[SimpleName]string `json:"contacts,omitempty" rdl:"optional" yaml:",omitempty"` + // + // domain environment e.g. production, staging, etc + // + Environment string `json:"environment" rdl:"optional" yaml:",omitempty"` + // // the common name to be referred to, the symbolic id. It is immutable // @@ -672,6 +688,12 @@ func (self *Domain) Validate() error { return fmt.Errorf("Domain.productId does not contain a valid String (%v)", val.Error) } } + if self.Environment != "" { + val := rdl.Validate(ZMSSchema(), "String", self.Environment) + if !val.Valid { + return fmt.Errorf("Domain.environment does not contain a valid String (%v)", val.Error) + } + } if self.Name == "" { return fmt.Errorf("Domain.name is missing but is a required field") } else { @@ -3718,6 +3740,11 @@ type TopLevelDomain struct { // Contacts map[SimpleName]string `json:"contacts,omitempty" rdl:"optional" yaml:",omitempty"` + // + // domain environment e.g. production, staging, etc + // + Environment string `json:"environment" rdl:"optional" yaml:",omitempty"` + // // name of the domain // @@ -3849,6 +3876,12 @@ func (self *TopLevelDomain) Validate() error { return fmt.Errorf("TopLevelDomain.productId does not contain a valid String (%v)", val.Error) } } + if self.Environment != "" { + val := rdl.Validate(ZMSSchema(), "String", self.Environment) + if !val.Valid { + return fmt.Errorf("TopLevelDomain.environment does not contain a valid String (%v)", val.Error) + } + } if self.Name == "" { return fmt.Errorf("TopLevelDomain.name is missing but is a required field") } else { @@ -3998,6 +4031,11 @@ type SubDomain struct { // Contacts map[SimpleName]string `json:"contacts,omitempty" rdl:"optional" yaml:",omitempty"` + // + // domain environment e.g. production, staging, etc + // + Environment string `json:"environment" rdl:"optional" yaml:",omitempty"` + // // name of the domain // @@ -4134,6 +4172,12 @@ func (self *SubDomain) Validate() error { return fmt.Errorf("SubDomain.productId does not contain a valid String (%v)", val.Error) } } + if self.Environment != "" { + val := rdl.Validate(ZMSSchema(), "String", self.Environment) + if !val.Valid { + return fmt.Errorf("SubDomain.environment does not contain a valid String (%v)", val.Error) + } + } if self.Name == "" { return fmt.Errorf("SubDomain.name is missing but is a required field") } else { @@ -4292,6 +4336,11 @@ type UserDomain struct { // Contacts map[SimpleName]string `json:"contacts,omitempty" rdl:"optional" yaml:",omitempty"` + // + // domain environment e.g. production, staging, etc + // + Environment string `json:"environment" rdl:"optional" yaml:",omitempty"` + // // user id which will be the domain name // @@ -4415,6 +4464,12 @@ func (self *UserDomain) Validate() error { return fmt.Errorf("UserDomain.productId does not contain a valid String (%v)", val.Error) } } + if self.Environment != "" { + val := rdl.Validate(ZMSSchema(), "String", self.Environment) + if !val.Valid { + return fmt.Errorf("UserDomain.environment does not contain a valid String (%v)", val.Error) + } + } if self.Name == "" { return fmt.Errorf("UserDomain.name is missing but is a required field") } else { @@ -6802,6 +6857,11 @@ type DomainData struct { // Contacts map[SimpleName]string `json:"contacts,omitempty" rdl:"optional" yaml:",omitempty"` + // + // domain environment e.g. production, staging, etc + // + Environment string `json:"environment" rdl:"optional" yaml:",omitempty"` + // // name of the domain // @@ -6965,6 +7025,12 @@ func (self *DomainData) Validate() error { return fmt.Errorf("DomainData.productId does not contain a valid String (%v)", val.Error) } } + if self.Environment != "" { + val := rdl.Validate(ZMSSchema(), "String", self.Environment) + if !val.Valid { + return fmt.Errorf("DomainData.environment does not contain a valid String (%v)", val.Error) + } + } if self.Name == "" { return fmt.Errorf("DomainData.name is missing but is a required field") } else { diff --git a/clients/go/zms/zms_schema.go b/clients/go/zms/zms_schema.go index 64311ef1813..5aafad547b3 100644 --- a/clients/go/zms/zms_schema.go +++ b/clients/go/zms/zms_schema.go @@ -166,6 +166,7 @@ func init() { tDomainMeta.Field("productId", "String", true, nil, "associated product id (system attribute - uniqueness check - if enabled)") tDomainMeta.Field("featureFlags", "Int32", true, nil, "features enabled per domain (system attribute)") tDomainMeta.MapField("contacts", "SimpleName", "String", true, "list of domain contacts (PE-Owner, Product-Owner, etc), each type can have a single value") + tDomainMeta.Field("environment", "String", true, nil, "domain environment e.g. production, staging, etc") sb.AddType(tDomainMeta.Build()) tDomain := rdl.NewStructTypeBuilder("DomainMeta", "Domain") diff --git a/core/zms/src/main/java/com/yahoo/athenz/zms/Domain.java b/core/zms/src/main/java/com/yahoo/athenz/zms/Domain.java index 0b5d7b40db1..99270e9d75a 100644 --- a/core/zms/src/main/java/com/yahoo/athenz/zms/Domain.java +++ b/core/zms/src/main/java/com/yahoo/athenz/zms/Domain.java @@ -93,6 +93,9 @@ public class Domain { @RdlOptional @JsonInclude(JsonInclude.Include.NON_EMPTY) public Map contacts; + @RdlOptional + @JsonInclude(JsonInclude.Include.NON_EMPTY) + public String environment; public String name; @RdlOptional @JsonInclude(JsonInclude.Include.NON_EMPTY) @@ -276,6 +279,13 @@ public Domain setContacts(Map contacts) { public Map getContacts() { return contacts; } + public Domain setEnvironment(String environment) { + this.environment = environment; + return this; + } + public String getEnvironment() { + return environment; + } public Domain setName(String name) { this.name = name; return this; @@ -380,6 +390,9 @@ public boolean equals(Object another) { if (contacts == null ? a.contacts != null : !contacts.equals(a.contacts)) { return false; } + if (environment == null ? a.environment != null : !environment.equals(a.environment)) { + return false; + } if (name == null ? a.name != null : !name.equals(a.name)) { return false; } diff --git a/core/zms/src/main/java/com/yahoo/athenz/zms/DomainData.java b/core/zms/src/main/java/com/yahoo/athenz/zms/DomainData.java index 12e831ddc12..dc40e128fde 100644 --- a/core/zms/src/main/java/com/yahoo/athenz/zms/DomainData.java +++ b/core/zms/src/main/java/com/yahoo/athenz/zms/DomainData.java @@ -89,6 +89,9 @@ public class DomainData { @RdlOptional @JsonInclude(JsonInclude.Include.NON_EMPTY) public Map contacts; + @RdlOptional + @JsonInclude(JsonInclude.Include.NON_EMPTY) + public String environment; public String name; public List roles; public SignedPolicies policies; @@ -272,6 +275,13 @@ public DomainData setContacts(Map contacts) { public Map getContacts() { return contacts; } + public DomainData setEnvironment(String environment) { + this.environment = environment; + return this; + } + public String getEnvironment() { + return environment; + } public DomainData setName(String name) { this.name = name; return this; @@ -404,6 +414,9 @@ public boolean equals(Object another) { if (contacts == null ? a.contacts != null : !contacts.equals(a.contacts)) { return false; } + if (environment == null ? a.environment != null : !environment.equals(a.environment)) { + return false; + } if (name == null ? a.name != null : !name.equals(a.name)) { return false; } diff --git a/core/zms/src/main/java/com/yahoo/athenz/zms/DomainMeta.java b/core/zms/src/main/java/com/yahoo/athenz/zms/DomainMeta.java index 9bf404c2189..7b930ea2245 100644 --- a/core/zms/src/main/java/com/yahoo/athenz/zms/DomainMeta.java +++ b/core/zms/src/main/java/com/yahoo/athenz/zms/DomainMeta.java @@ -89,6 +89,9 @@ public class DomainMeta { @RdlOptional @JsonInclude(JsonInclude.Include.NON_EMPTY) public Map contacts; + @RdlOptional + @JsonInclude(JsonInclude.Include.NON_EMPTY) + public String environment; public DomainMeta setDescription(String description) { this.description = description; @@ -265,6 +268,13 @@ public DomainMeta setContacts(Map contacts) { public Map getContacts() { return contacts; } + public DomainMeta setEnvironment(String environment) { + this.environment = environment; + return this; + } + public String getEnvironment() { + return environment; + } @Override public boolean equals(Object another) { @@ -348,6 +358,9 @@ public boolean equals(Object another) { if (contacts == null ? a.contacts != null : !contacts.equals(a.contacts)) { return false; } + if (environment == null ? a.environment != null : !environment.equals(a.environment)) { + return false; + } } return true; } diff --git a/core/zms/src/main/java/com/yahoo/athenz/zms/SubDomain.java b/core/zms/src/main/java/com/yahoo/athenz/zms/SubDomain.java index 325491d6d6d..7a70539ab28 100644 --- a/core/zms/src/main/java/com/yahoo/athenz/zms/SubDomain.java +++ b/core/zms/src/main/java/com/yahoo/athenz/zms/SubDomain.java @@ -89,6 +89,9 @@ public class SubDomain { @RdlOptional @JsonInclude(JsonInclude.Include.NON_EMPTY) public Map contacts; + @RdlOptional + @JsonInclude(JsonInclude.Include.NON_EMPTY) + public String environment; public String name; public List adminUsers; @RdlOptional @@ -271,6 +274,13 @@ public SubDomain setContacts(Map contacts) { public Map getContacts() { return contacts; } + public SubDomain setEnvironment(String environment) { + this.environment = environment; + return this; + } + public String getEnvironment() { + return environment; + } public SubDomain setName(String name) { this.name = name; return this; @@ -382,6 +392,9 @@ public boolean equals(Object another) { if (contacts == null ? a.contacts != null : !contacts.equals(a.contacts)) { return false; } + if (environment == null ? a.environment != null : !environment.equals(a.environment)) { + return false; + } if (name == null ? a.name != null : !name.equals(a.name)) { return false; } diff --git a/core/zms/src/main/java/com/yahoo/athenz/zms/TopLevelDomain.java b/core/zms/src/main/java/com/yahoo/athenz/zms/TopLevelDomain.java index 2bc23caf6db..cfc118e1822 100644 --- a/core/zms/src/main/java/com/yahoo/athenz/zms/TopLevelDomain.java +++ b/core/zms/src/main/java/com/yahoo/athenz/zms/TopLevelDomain.java @@ -90,6 +90,9 @@ public class TopLevelDomain { @RdlOptional @JsonInclude(JsonInclude.Include.NON_EMPTY) public Map contacts; + @RdlOptional + @JsonInclude(JsonInclude.Include.NON_EMPTY) + public String environment; public String name; public List adminUsers; @RdlOptional @@ -271,6 +274,13 @@ public TopLevelDomain setContacts(Map contacts) { public Map getContacts() { return contacts; } + public TopLevelDomain setEnvironment(String environment) { + this.environment = environment; + return this; + } + public String getEnvironment() { + return environment; + } public TopLevelDomain setName(String name) { this.name = name; return this; @@ -375,6 +385,9 @@ public boolean equals(Object another) { if (contacts == null ? a.contacts != null : !contacts.equals(a.contacts)) { return false; } + if (environment == null ? a.environment != null : !environment.equals(a.environment)) { + return false; + } if (name == null ? a.name != null : !name.equals(a.name)) { return false; } diff --git a/core/zms/src/main/java/com/yahoo/athenz/zms/UserDomain.java b/core/zms/src/main/java/com/yahoo/athenz/zms/UserDomain.java index 2511e168bcf..fbbac718c53 100644 --- a/core/zms/src/main/java/com/yahoo/athenz/zms/UserDomain.java +++ b/core/zms/src/main/java/com/yahoo/athenz/zms/UserDomain.java @@ -89,6 +89,9 @@ public class UserDomain { @RdlOptional @JsonInclude(JsonInclude.Include.NON_EMPTY) public Map contacts; + @RdlOptional + @JsonInclude(JsonInclude.Include.NON_EMPTY) + public String environment; public String name; @RdlOptional @JsonInclude(JsonInclude.Include.NON_EMPTY) @@ -269,6 +272,13 @@ public UserDomain setContacts(Map contacts) { public Map getContacts() { return contacts; } + public UserDomain setEnvironment(String environment) { + this.environment = environment; + return this; + } + public String getEnvironment() { + return environment; + } public UserDomain setName(String name) { this.name = name; return this; @@ -366,6 +376,9 @@ public boolean equals(Object another) { if (contacts == null ? a.contacts != null : !contacts.equals(a.contacts)) { return false; } + if (environment == null ? a.environment != null : !environment.equals(a.environment)) { + return false; + } if (name == null ? a.name != null : !name.equals(a.name)) { return false; } diff --git a/core/zms/src/main/java/com/yahoo/athenz/zms/ZMSSchema.java b/core/zms/src/main/java/com/yahoo/athenz/zms/ZMSSchema.java index b35a8cfdc52..ce5565a276b 100644 --- a/core/zms/src/main/java/com/yahoo/athenz/zms/ZMSSchema.java +++ b/core/zms/src/main/java/com/yahoo/athenz/zms/ZMSSchema.java @@ -141,7 +141,8 @@ private static Schema build() { .field("memberPurgeExpiryDays", "Int32", true, "purge role/group members with expiry date configured days in the past") .field("productId", "String", true, "associated product id (system attribute - uniqueness check - if enabled)") .field("featureFlags", "Int32", true, "features enabled per domain (system attribute)") - .mapField("contacts", "SimpleName", "String", true, "list of domain contacts (PE-Owner, Product-Owner, etc), each type can have a single value"); + .mapField("contacts", "SimpleName", "String", true, "list of domain contacts (PE-Owner, Product-Owner, etc), each type can have a single value") + .field("environment", "String", true, "domain environment e.g. production, staging, etc"); sb.structType("Domain", "DomainMeta") .comment("A domain is an independent partition of users, roles, and resources. Its name represents the definition of a namespace; the only way a new namespace can be created, from the top, is by creating Domains. Administration of a domain is governed by the parent domain (using reverse-DNS namespaces). The top level domains are governed by the special \"sys.auth\" domain.") diff --git a/core/zms/src/main/rdl/Domain.tdl b/core/zms/src/main/rdl/Domain.tdl index 66b1826e2e6..bbef88cb62f 100644 --- a/core/zms/src/main/rdl/Domain.tdl +++ b/core/zms/src/main/rdl/Domain.tdl @@ -31,6 +31,7 @@ type DomainMeta Struct { String productId (optional); //associated product id (system attribute - uniqueness check - if enabled) Int32 featureFlags (optional); //features enabled per domain (system attribute) Map contacts (optional); //list of domain contacts (PE-Owner, Product-Owner, etc), each type can have a single value + String environment (optional); //domain environment e.g. production, staging, etc } //A domain is an independent partition of users, roles, and resources. diff --git a/core/zms/src/test/java/com/yahoo/athenz/zms/DomainTest.java b/core/zms/src/test/java/com/yahoo/athenz/zms/DomainTest.java index a182deec9a5..c795054b2fb 100644 --- a/core/zms/src/test/java/com/yahoo/athenz/zms/DomainTest.java +++ b/core/zms/src/test/java/com/yahoo/athenz/zms/DomainTest.java @@ -85,7 +85,7 @@ public void testDomainMetaMethod() { .setAzureSubscription("azure").setGcpProject("gcp").setBusinessService("business-service") .setTags(Collections.singletonMap("tagKey", new TagValueList().setList(Collections.singletonList("tagValue")))) .setMemberPurgeExpiryDays(10).setGcpProjectNumber("1240").setProductId("abcd-1234") - .setFeatureFlags(3).setContacts(Map.of("pe-owner", "user.test")); + .setFeatureFlags(3).setContacts(Map.of("pe-owner", "user.test")).setEnvironment("production"); Validator.Result result = validator.validate(dm, "DomainMeta"); assertTrue(result.valid); @@ -116,6 +116,7 @@ public void testDomainMetaMethod() { assertEquals(dm.getProductId(), "abcd-1234"); assertEquals(dm.getFeatureFlags(), 3); assertEquals(dm.getContacts(), Map.of("pe-owner", "user.test")); + assertEquals(dm.getEnvironment(), "production"); DomainMeta dm2 = new DomainMeta().init(); dm2.setDescription("domain desc").setOrg("org:test").setEnabled(true).setAuditEnabled(false) @@ -126,11 +127,18 @@ public void testDomainMetaMethod() { .setAzureSubscription("azure").setGcpProject("gcp").setBusinessService("business-service") .setTags(Collections.singletonMap("tagKey", new TagValueList().setList(Collections.singletonList("tagValue")))) .setMemberPurgeExpiryDays(10).setGcpProjectNumber("1240").setProductId("abcd-1234") - .setFeatureFlags(3).setContacts(Map.of("pe-owner", "user.test")); + .setFeatureFlags(3).setContacts(Map.of("pe-owner", "user.test")).setEnvironment("production"); assertEquals(dm, dm2); assertEquals(dm, dm); + dm2.setEnvironment("staging"); + assertNotEquals(dm, dm2); + dm2.setEnvironment(null); + assertNotEquals(dm, dm2); + dm2.setEnvironment("production"); + assertEquals(dm, dm2); + dm2.setContacts(Map.of("product-owner", "user.test")); assertNotEquals(dm, dm2); dm2.setContacts(null); @@ -312,7 +320,7 @@ public void testTopLevelDomainMethod() { .setTags(Collections.singletonMap("tagKey", new TagValueList().setList(Collections.singletonList("tagValue")))) .setBusinessService("business-service").setMemberPurgeExpiryDays(10).setGcpProject("gcp") .setGcpProjectNumber("1242").setProductId("abcd-1234").setFeatureFlags(3) - .setContacts(Map.of("pe-owner", "user.test")); + .setContacts(Map.of("pe-owner", "user.test")).setEnvironment("production"); result = validator.validate(tld, "TopLevelDomain"); assertTrue(result.valid); @@ -346,6 +354,7 @@ public void testTopLevelDomainMethod() { assertEquals(tld.getProductId(), "abcd-1234"); assertEquals(tld.getFeatureFlags(), 3); assertEquals(tld.getContacts(), Map.of("pe-owner", "user.test")); + assertEquals(tld.getEnvironment(), "production"); TopLevelDomain tld2 = new TopLevelDomain().setDescription("domain desc").setOrg("org:test").setEnabled(true) .setAuditEnabled(false).setAccount("aws").setYpmId(10).setName("testdomain").setAdminUsers(admins) @@ -355,11 +364,18 @@ public void testTopLevelDomainMethod() { .setTags(Collections.singletonMap("tagKey", new TagValueList().setList(Collections.singletonList("tagValue")))) .setBusinessService("business-service").setMemberPurgeExpiryDays(10).setGcpProject("gcp") .setGcpProjectNumber("1242").setProductId("abcd-1234").setFeatureFlags(3) - .setContacts(Map.of("pe-owner", "user.test")); + .setContacts(Map.of("pe-owner", "user.test")).setEnvironment("production"); assertEquals(tld, tld2); assertEquals(tld, tld); + tld2.setEnvironment("staging"); + assertNotEquals(tld, tld2); + tld2.setEnvironment(null); + assertNotEquals(tld, tld2); + tld2.setEnvironment("production"); + assertEquals(tld, tld2); + tld2.setContacts(Map.of("product-owner", "user.test")); assertNotEquals(tld, tld2); tld2.setContacts(null); @@ -529,7 +545,7 @@ public void testSubDomainMethod() { .setTags(Collections.singletonMap("tagKey", new TagValueList().setList(Collections.singletonList("tagValue")))) .setBusinessService("business-service").setMemberPurgeExpiryDays(10).setGcpProject("gcp") .setGcpProjectNumber("1244").setProductId("abcd-1234").setFeatureFlags(3) - .setContacts(Map.of("pe-owner", "user.test")); + .setContacts(Map.of("pe-owner", "user.test")).setEnvironment("production"); Validator.Result result = validator.validate(sd, "SubDomain"); assertTrue(result.valid, result.error); @@ -564,6 +580,7 @@ public void testSubDomainMethod() { assertEquals(sd.getProductId(), "abcd-1234"); assertEquals(sd.getFeatureFlags(), 3); assertEquals(sd.getContacts(), Map.of("pe-owner", "user.test")); + assertEquals(sd.getEnvironment(), "production"); SubDomain sd2 = new SubDomain().setDescription("domain desc").setOrg("org:test").setEnabled(true) .setAuditEnabled(false).setAccount("aws").setYpmId(10).setName("testdomain").setAdminUsers(admins) @@ -575,12 +592,19 @@ public void testSubDomainMethod() { .setTags(Collections.singletonMap("tagKey", new TagValueList().setList(Collections.singletonList("tagValue")))) .setBusinessService("business-service").setMemberPurgeExpiryDays(10).setGcpProject("gcp") .setGcpProjectNumber("1244").setProductId("abcd-1234").setFeatureFlags(3) - .setContacts(Map.of("pe-owner", "user.test")); + .setContacts(Map.of("pe-owner", "user.test")).setEnvironment("production"); assertEquals(sd, sd2); assertEquals(sd, sd); assertNotEquals(schema, sd); + sd2.setEnvironment("staging"); + assertNotEquals(sd, sd2); + sd2.setEnvironment(null); + assertNotEquals(sd, sd2); + sd2.setEnvironment("production"); + assertEquals(sd, sd2); + sd2.setContacts(Map.of("product-owner", "user.test")); assertNotEquals(sd, sd2); sd2.setContacts(null); @@ -748,7 +772,8 @@ public void testUserDomainMethod() { .setGroupExpiryDays(50).setAzureSubscription("azure").setBusinessService("business-service") .setTags(Collections.singletonMap("tagKey", new TagValueList().setList(Collections.singletonList("tagValue")))) .setMemberPurgeExpiryDays(10).setGcpProject("gcp").setGcpProjectNumber("1246") - .setProductId("abcd-1234").setFeatureFlags(3).setContacts(Map.of("pe-owner", "user.test")); + .setProductId("abcd-1234").setFeatureFlags(3).setContacts(Map.of("pe-owner", "user.test")) + .setEnvironment("production"); Validator.Result result = validator.validate(ud, "UserDomain"); assertTrue(result.valid); @@ -781,6 +806,7 @@ public void testUserDomainMethod() { assertEquals(ud.getProductId(), "abcd-1234"); assertEquals(ud.getFeatureFlags(), 3); assertEquals(ud.getContacts(), Map.of("pe-owner", "user.test")); + assertEquals(ud.getEnvironment(), "production"); UserDomain ud2 = new UserDomain().setDescription("domain desc").setOrg("org:test").setEnabled(true) .setAuditEnabled(false).setAccount("aws").setYpmId(10).setName("testuser") @@ -791,11 +817,19 @@ public void testUserDomainMethod() { .setGroupExpiryDays(50).setAzureSubscription("azure").setBusinessService("business-service") .setTags(Collections.singletonMap("tagKey", new TagValueList().setList(Collections.singletonList("tagValue")))) .setMemberPurgeExpiryDays(10).setGcpProject("gcp").setGcpProjectNumber("1246") - .setProductId("abcd-1234").setFeatureFlags(3).setContacts(Map.of("pe-owner", "user.test")); + .setProductId("abcd-1234").setFeatureFlags(3).setContacts(Map.of("pe-owner", "user.test")) + .setEnvironment("production"); assertEquals(ud, ud2); assertEquals(ud, ud); + ud2.setEnvironment("staging"); + assertNotEquals(ud, ud2); + ud2.setEnvironment(null); + assertNotEquals(ud, ud2); + ud2.setEnvironment("production"); + assertEquals(ud, ud2); + ud2.setContacts(Map.of("product-owner", "user.test")); assertNotEquals(ud, ud2); ud2.setContacts(null); @@ -983,7 +1017,7 @@ public void testDomainMethod() { .setTags(Collections.singletonMap("tagKey", new TagValueList().setList(Collections.singletonList("tagValue")))) .setBusinessService("business-service").setMemberPurgeExpiryDays(10).setGcpProject("gcp") .setGcpProjectNumber("1237").setProductId("abcd-1234").setFeatureFlags(3) - .setContacts(Map.of("pe-owner", "user.test")); + .setContacts(Map.of("pe-owner", "user.test")).setEnvironment("production"); Validator.Result result = validator.validate(d, "Domain"); assertTrue(result.valid); @@ -1017,6 +1051,7 @@ public void testDomainMethod() { assertEquals(d.getProductId(), "abcd-1234"); assertEquals(d.getFeatureFlags(), 3); assertEquals(d.getContacts(), Map.of("pe-owner", "user.test")); + assertEquals(d.getEnvironment(), "production"); Domain d2 = new Domain(); d2.setName("test.domain").setModified(Timestamp.fromMillis(123456789123L)).setId(UUID.fromMillis(100)) @@ -1028,11 +1063,18 @@ public void testDomainMethod() { .setTags(Collections.singletonMap("tagKey", new TagValueList().setList(Collections.singletonList("tagValue")))) .setBusinessService("business-service").setMemberPurgeExpiryDays(10).setGcpProject("gcp") .setGcpProjectNumber("1237").setProductId("abcd-1234").setFeatureFlags(3) - .setContacts(Map.of("pe-owner", "user.test")); + .setContacts(Map.of("pe-owner", "user.test")).setEnvironment("production"); assertEquals(d, d2); assertEquals(d, d); + d2.setEnvironment("staging"); + assertNotEquals(d, d2); + d2.setEnvironment(null); + assertNotEquals(d, d2); + d2.setEnvironment("production"); + assertEquals(d, d2); + d2.setContacts(Map.of("product-owner", "user.test")); assertNotEquals(d, d2); d2.setContacts(null); diff --git a/core/zms/src/test/java/com/yahoo/athenz/zms/ZMSCoreTest.java b/core/zms/src/test/java/com/yahoo/athenz/zms/ZMSCoreTest.java index 15849d95184..56e9b03ff97 100644 --- a/core/zms/src/test/java/com/yahoo/athenz/zms/ZMSCoreTest.java +++ b/core/zms/src/test/java/com/yahoo/athenz/zms/ZMSCoreTest.java @@ -578,7 +578,8 @@ public void testSignedDomainsMethod() { .setUserAuthorityFilter("OnShore").setGroups(gl).setAzureSubscription("azure").setGcpProject("gcp") .setTags(Collections.singletonMap("tagKey", new TagValueList().setList(Collections.singletonList("tagValue")))) .setBusinessService("business-service").setMemberPurgeExpiryDays(10).setGcpProjectNumber("1235") - .setProductId("abcd-1234").setFeatureFlags(3).setContacts(Map.of("pe-owner", "user.test")); + .setProductId("abcd-1234").setFeatureFlags(3).setContacts(Map.of("pe-owner", "user.test")) + .setEnvironment("production"); result = validator.validate(dd, "DomainData"); assertTrue(result.valid, result.error); @@ -616,6 +617,7 @@ public void testSignedDomainsMethod() { assertEquals(dd.getProductId(), "abcd-1234"); assertEquals(dd.getFeatureFlags(), 3); assertEquals(dd.getContacts(), Map.of("pe-owner", "user.test")); + assertEquals(dd.getEnvironment(), "production"); DomainData dd2 = new DomainData().setName("test.domain").setAccount("aws").setYpmId(1).setRoles(rl) .setPolicies(sp).setServices(sil).setEntities(elist).setModified(Timestamp.fromMillis(123456789123L)) @@ -626,7 +628,7 @@ public void testSignedDomainsMethod() { .setTags(Collections.singletonMap("tagKey", new TagValueList().setList(Collections.singletonList("tagValue")))) .setBusinessService("business-service").setMemberPurgeExpiryDays(10).setGcpProject("gcp") .setGcpProjectNumber("1235").setProductId("abcd-1234").setFeatureFlags(3) - .setContacts(Map.of("pe-owner", "user.test")); + .setContacts(Map.of("pe-owner", "user.test")).setEnvironment("production"); assertEquals(dd2, dd); assertNotEquals(dd, null); @@ -641,6 +643,13 @@ public void testSignedDomainsMethod() { dd2.setGroups(gl); assertEquals(dd, dd2); + dd2.setEnvironment("staging"); + assertNotEquals(dd, dd2); + dd2.setEnvironment(null); + assertNotEquals(dd, dd2); + dd2.setEnvironment("production"); + assertEquals(dd, dd2); + dd2.setContacts(Map.of("product-owner", "user.test")); assertNotEquals(dd, dd2); dd2.setContacts(null); diff --git a/libs/go/zmscli/cli.go b/libs/go/zmscli/cli.go index 927a478bfce..859923871bf 100644 --- a/libs/go/zmscli/cli.go +++ b/libs/go/zmscli/cli.go @@ -922,6 +922,10 @@ func (cli Zms) EvalCommand(params []string) (*string, error) { if argc == 1 { return cli.SetDomainUserAuthorityFilter(dn, args[0]) } + case "set-domain-environment": + if argc == 1 { + return cli.SetDomainEnvironment(dn, args[0]) + } case "set-product-id", "set-domain-product-id": if argc == 1 { productIDString := "" @@ -1514,6 +1518,16 @@ func (cli Zms) HelpSpecificCommand(interactive bool, cmd string) string { buf.WriteString(" filter : comma separated list of user authority filters\n") buf.WriteString(" examples:\n") buf.WriteString(" " + domainExample + " set-domain-user-authority-filter OnShore-US\n") + case "set-domain-environment": + buf.WriteString(" syntax:\n") + buf.WriteString(" [-o json] " + domainParam + " set-domain-environment environment\n") + buf.WriteString(" parameters:\n") + if !interactive { + buf.WriteString(" domain : name of the domain being updated\n") + } + buf.WriteString(" environment : valid enviornment value for the domain\n") + buf.WriteString(" examples:\n") + buf.WriteString(" " + domainExample + " set-domain-environment production\n") case "set-product-id", "set-domain-product-id": buf.WriteString(" syntax:\n") buf.WriteString(" [-o json] " + domainParam + " set-product-id product-id\n") @@ -3399,6 +3413,7 @@ func (cli Zms) HelpListCommand() string { buf.WriteString(" set-domain-role-cert-expiry-mins cert-expiry-mins\n") buf.WriteString(" set-domain-token-sign-algorithm algorithm\n") buf.WriteString(" set-domain-user-authority-filter filter\n") + buf.WriteString(" set-domain-environment environment\n") buf.WriteString(" set-domain-feature-flags flags\n") buf.WriteString(" set-domain-contact type user\n") buf.WriteString(" import-domain domain [file.yaml [admin ...]] - no file means stdin\n") diff --git a/libs/go/zmscli/domain.go b/libs/go/zmscli/domain.go index 3c9e442fcae..8fd055df7cf 100644 --- a/libs/go/zmscli/domain.go +++ b/libs/go/zmscli/domain.go @@ -786,6 +786,23 @@ func (cli Zms) SetDomainUserAuthorityFilter(dn, filter string) (*string, error) return cli.dumpByFormat(message, cli.buildYAMLOutput) } +func (cli Zms) SetDomainEnvironment(dn, environment string) (*string, error) { + meta := zms.DomainMeta{ + Environment: environment, + } + err := cli.Zms.PutDomainSystemMeta(zms.DomainName(dn), "environment", cli.AuditRef, &meta) + if err != nil { + return nil, err + } + s := "[domain " + dn + " metadata successfully updated]\n" + message := SuccessMessage{ + Status: 200, + Message: s, + } + + return cli.dumpByFormat(message, cli.buildYAMLOutput) +} + func (cli Zms) SetDomainContact(dn, contactType, contactUser string) (*string, error) { domain, err := cli.Zms.GetDomain(zms.DomainName(dn)) if err != nil { diff --git a/servers/zms/conf/zms.properties b/servers/zms/conf/zms.properties index 5a672f98122..b18e23294a6 100644 --- a/servers/zms/conf/zms.properties +++ b/servers/zms/conf/zms.properties @@ -533,3 +533,8 @@ athenz.zms.no_auth_uri_list=/zms/v1/schema # deletions to the rows can be reflected in the result set. If there are # additions then the server always fetches the latest data. #athenz.zms.mysql_server_trust_roles_update_timeout=600000 + +# A comma separated list of environments that a domain is used for. +# The server will validate that the environment specified in the domain +# is one of the values specified in this list. +#athenz.zms.domain_environments=production,integration,staging,sandbox,qa,development diff --git a/servers/zms/schema/updates/update-20240121.sql b/servers/zms/schema/updates/update-20240121.sql new file mode 100644 index 00000000000..8bba9ff004f --- /dev/null +++ b/servers/zms/schema/updates/update-20240121.sql @@ -0,0 +1 @@ +ALTER TABLE `zms_server`.`domain` ADD `environment` VARCHAR(32) NOT NULL DEFAULT ''; diff --git a/servers/zms/schema/zms_server.mwb b/servers/zms/schema/zms_server.mwb index 9861c96e0d9..4c02373cc5a 100644 Binary files a/servers/zms/schema/zms_server.mwb and b/servers/zms/schema/zms_server.mwb differ diff --git a/servers/zms/schema/zms_server.sql b/servers/zms/schema/zms_server.sql index f20d83b4428..1b1fd184609 100644 --- a/servers/zms/schema/zms_server.sql +++ b/servers/zms/schema/zms_server.sql @@ -1,5 +1,5 @@ -- MySQL Script generated by MySQL Workbench --- Mon Nov 27 13:31:52 2023 +-- Sat Jan 20 17:27:52 2024 -- Model: New Model Version: 1.0 -- MySQL Workbench Forward Engineering @@ -49,6 +49,7 @@ CREATE TABLE IF NOT EXISTS `zms_server`.`domain` ( `gcp_project_number` VARCHAR(64) NOT NULL DEFAULT '', `product_id` VARCHAR(128) NOT NULL DEFAULT '', `feature_flags` INT NOT NULL DEFAULT 0, + `environment` VARCHAR(32) NOT NULL DEFAULT '', PRIMARY KEY (`domain_id`), UNIQUE INDEX `uq_name` (`name` ASC), INDEX `idx_modified` (`modified` ASC), diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/DBService.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/DBService.java index caabe572c43..aaf09999628 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/zms/DBService.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/DBService.java @@ -4183,7 +4183,8 @@ void executePutDomainMeta(ResourceContext ctx, Domain domain, DomainMeta meta, .setBusinessService(domain.getBusinessService()) .setMemberPurgeExpiryDays(domain.getMemberPurgeExpiryDays()) .setFeatureFlags(domain.getFeatureFlags()) - .setContacts(domain.getContacts()); + .setContacts(domain.getContacts()) + .setEnvironment(domain.getEnvironment()); // then we're going to apply the updated fields // from the given object @@ -4542,6 +4543,9 @@ void updateDomainMetaFields(Domain domain, DomainMeta meta) { if (meta.getContacts() != null) { domain.setContacts(meta.getContacts()); } + if (meta.getEnvironment() != null) { + domain.setEnvironment(meta.getEnvironment()); + } } boolean isDeleteSystemMetaAllowed(boolean deleteAllowed, final String oldValue, final String newValue) { diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSConsts.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSConsts.java index 6e3dcc9afea..399ee82bb27 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSConsts.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSConsts.java @@ -99,6 +99,9 @@ public final class ZMSConsts { public static final String ZMS_PROP_SERVICE_NAME_MIN_LENGTH = "athenz.zms.service_name_min_length"; public static final String ZMS_PROP_MAX_POLICY_VERSIONS = "athenz.zms.max_policy_versions"; public static final String ZMS_PROP_DOMAIN_CONTACT_TYPES = "athenz.zms.domain_contact_types"; + public static final String ZMS_PROP_DOMAIN_ENVIRONMENTS = "athenz.zms.domain_environments"; + + public static final String ZMS_DEFAULT_DOMAIN_ENVIRONMENTS = "production,integration,staging,sandbox,qa,development"; public static final String ZMS_PROP_VALIDATE_USER_MEMBERS = "athenz.zms.validate_user_members"; public static final String ZMS_PROP_VALIDATE_SERVICE_MEMBERS = "athenz.zms.validate_service_members"; @@ -275,6 +278,7 @@ public final class ZMSConsts { public static final String DB_COLUMN_MAX_MEMBERS = "max_members"; public static final String DB_COLUMN_SELF_RENEW = "self_renew"; public static final String DB_COLUMN_SELF_RENEW_MINS = "self_renew_mins"; + public static final String DB_COLUMN_ENVIRONMENT = "environment"; public static final String DB_COLUMN_SERVICE_REVIEW_DAYS = "service_review_days"; public static final String DB_COLUMN_SERVICE_EXPIRY_DAYS = "service_expiry_days"; diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSImpl.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSImpl.java index 7edd9365570..12a4b2312f2 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSImpl.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/ZMSImpl.java @@ -233,6 +233,7 @@ public class ZMSImpl implements Authorizer, KeyStore, ZMSHandler { protected ServiceProviderClient serviceProviderClient; protected Info serverInfo = null; protected Set domainContactTypes = new HashSet<>(); + protected Set domainEnvironments = new HashSet<>(); // enum to represent our access response since in some cases we want to // handle domain not founds differently instead of just returning failure @@ -975,6 +976,12 @@ void loadConfigurationSettings() { domainContactTypes.addAll(Arrays.asList(contactTypeList.split(","))); } + // get our domain classification values + + final String environments = System.getProperty(ZMSConsts.ZMS_PROP_DOMAIN_ENVIRONMENTS, + ZMSConsts.ZMS_DEFAULT_DOMAIN_ENVIRONMENTS); + domainEnvironments.addAll(Arrays.asList(environments.split(","))); + // get server region serverRegion = System.getProperty(ZMSConsts.ZMS_PROP_SERVER_REGION); @@ -1593,7 +1600,8 @@ public Domain postTopLevelDomain(ResourceContext ctx, String auditRef, TopLevelD .setBusinessService(detail.getBusinessService()) .setCertDnsDomain(detail.getCertDnsDomain()) .setFeatureFlags(detail.getFeatureFlags()) - .setContacts(detail.getContacts()); + .setContacts(detail.getContacts()) + .setEnvironment(detail.getEnvironment()); // before processing validate the fields @@ -1827,7 +1835,8 @@ public Domain postUserDomain(ResourceContext ctx, String name, String auditRef, .setSignAlgorithm(detail.getSignAlgorithm()) .setTags(detail.getTags()) .setBusinessService(detail.getBusinessService()) - .setContacts(detail.getContacts()); + .setContacts(detail.getContacts()) + .setEnvironment(detail.getEnvironment()); // before processing validate the fields @@ -1922,7 +1931,8 @@ public Domain postSubDomain(ResourceContext ctx, String parent, String auditRef, .setSignAlgorithm(detail.getSignAlgorithm()) .setTags(detail.getTags()) .setBusinessService(detail.getBusinessService()) - .setContacts(detail.getContacts()); + .setContacts(detail.getContacts()) + .setEnvironment(detail.getEnvironment()); // before processing validate the fields @@ -2273,6 +2283,12 @@ void validateDomainValues(Domain domain) { // validate the domain contacts types and names validateDomainContacts(domain.getContacts(), caller); + + // validate the domain environment if specified + + if (!StringUtil.isEmpty(domain.getEnvironment()) && !domainEnvironments.contains(domain.getEnvironment())) { + throw ZMSUtils.requestError("invalid environment for domain", caller); + } } boolean validateGcpProjectDetails(final String gcpProjectId, final String gcpProjectNumber) { @@ -2385,6 +2401,12 @@ void validateDomainMetaValues(DomainMeta meta) { // validate the domain contacts types and names validateDomainContacts(meta.getContacts(), caller); + + // validate the domain environment if specified + + if (!StringUtil.isEmpty(meta.getEnvironment()) && !domainEnvironments.contains(meta.getEnvironment())) { + throw ZMSUtils.requestError("invalid environment for domain", caller); + } } void updateDomainMetaStoreAttributeDetails(final String domainName, int metaAttribute, final Object value) { diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/store/impl/jdbc/JDBCConnection.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/store/impl/jdbc/JDBCConnection.java index f4b82d1753a..8a689abd79c 100644 --- a/servers/zms/src/main/java/com/yahoo/athenz/zms/store/impl/jdbc/JDBCConnection.java +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/store/impl/jdbc/JDBCConnection.java @@ -77,14 +77,14 @@ public class JDBCConnection implements ObjectStoreConnection { + "(name, description, org, uuid, enabled, audit_enabled, account, ypm_id, application_id, cert_dns_domain," + " member_expiry_days, token_expiry_mins, service_cert_expiry_mins, role_cert_expiry_mins, sign_algorithm," + " service_expiry_days, user_authority_filter, group_expiry_days, azure_subscription, business_service," - + " member_purge_expiry_days, gcp_project, gcp_project_number, product_id, feature_flags)" - + " VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);"; + + " member_purge_expiry_days, gcp_project, gcp_project_number, product_id, feature_flags, environment)" + + " VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);"; private static final String SQL_UPDATE_DOMAIN = "UPDATE domain " + "SET description=?, org=?, uuid=?, enabled=?, audit_enabled=?, account=?, ypm_id=?, application_id=?," + " cert_dns_domain=?, member_expiry_days=?, token_expiry_mins=?, service_cert_expiry_mins=?," + " role_cert_expiry_mins=?, sign_algorithm=?, service_expiry_days=?, user_authority_filter=?," + " group_expiry_days=?, azure_subscription=?, business_service=?, member_purge_expiry_days=?," - + " gcp_project=?, gcp_project_number=?, product_id=?, feature_flags=? WHERE name=?;"; + + " gcp_project=?, gcp_project_number=?, product_id=?, feature_flags=?, environment=? WHERE name=?;"; private static final String SQL_UPDATE_DOMAIN_MOD_TIMESTAMP = "UPDATE domain " + "SET modified=CURRENT_TIMESTAMP(3) WHERE name=?;"; private static final String SQL_GET_DOMAIN_MOD_TIMESTAMP = "SELECT modified FROM domain WHERE name=?;"; @@ -869,7 +869,8 @@ Domain saveDomainSettings(String domainName, ResultSet rs, boolean fetchAddlDeta .setUserAuthorityFilter(saveValue(rs.getString(ZMSConsts.DB_COLUMN_USER_AUTHORITY_FILTER))) .setBusinessService(saveValue(rs.getString(ZMSConsts.DB_COLUMN_BUSINESS_SERVICE))) .setMemberPurgeExpiryDays(nullIfDefaultValue(rs.getInt(ZMSConsts.DB_COLUMN_MEMBER_PURGE_EXPIRY_DAYS), 0)) - .setFeatureFlags(nullIfDefaultValue(rs.getInt(ZMSConsts.DB_COLUMN_FEATURE_FLAGS), 0)); + .setFeatureFlags(nullIfDefaultValue(rs.getInt(ZMSConsts.DB_COLUMN_FEATURE_FLAGS), 0)) + .setEnvironment(saveValue(rs.getString(ZMSConsts.DB_COLUMN_ENVIRONMENT))); if (fetchAddlDetails) { int domainId = rs.getInt(ZMSConsts.DB_COLUMN_DOMAIN_ID); domain.setTags(getDomainTags(domainId)); @@ -938,6 +939,7 @@ public boolean insertDomain(Domain domain) { ps.setString(23, processInsertValue(domain.getGcpProjectNumber())); ps.setString(24, processInsertValue(domain.getProductId())); ps.setInt(25, processInsertValue(domain.getFeatureFlags())); + ps.setString(26, processInsertValue(domain.getEnvironment())); affectedRows = executeUpdate(ps, caller); } catch (SQLException ex) { throw sqlError(ex, caller); @@ -1081,7 +1083,8 @@ public boolean updateDomain(Domain domain) { ps.setString(22, processInsertValue(domain.getGcpProjectNumber())); ps.setString(23, processInsertValue(domain.getProductId())); ps.setInt(24, processInsertValue(domain.getFeatureFlags())); - ps.setString(25, domain.getName()); + ps.setString(25, processInsertValue(domain.getEnvironment())); + ps.setString(26, domain.getName()); affectedRows = executeUpdate(ps, caller); } catch (SQLException ex) { throw sqlError(ex, caller); diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/ZMSImplTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/ZMSImplTest.java index 92690aecd9f..47a09ecfa4c 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/ZMSImplTest.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/ZMSImplTest.java @@ -22234,7 +22234,7 @@ public void testGetSignedDomainsWithMetaAttrs() { } @Test - public void testGetSignedDomainsNotModified() { + public void testGetSignedDomainsNotModified() throws InterruptedException { ZMSImpl zmsImpl = zmsTestInitializer.getZms(); RsrcCtxWrapper ctx = zmsTestInitializer.getMockDomRsrcCtx(); @@ -22249,13 +22249,15 @@ public void testGetSignedDomainsNotModified() { DomainMeta meta = zmsTestInitializer.createDomainMetaObject("Tenant Domain1", null, true, false, "12345", 0); zmsImpl.putDomainMeta(ctx, "signeddom1", auditRef, meta); - zmsImpl.privateKey = new ServerPrivateKey(Crypto.loadPrivateKey(Crypto.ybase64DecodeString(zmsTestInitializer.getPrivKey())), "0"); + zmsImpl.privateKey = new ServerPrivateKey(Crypto.loadPrivateKey( + Crypto.ybase64DecodeString(zmsTestInitializer.getPrivKey())), "0"); Authority principalAuthority = new com.yahoo.athenz.common.server.debug.DebugPrincipalAuthority(); Principal sysPrincipal = principalAuthority.authenticate("v=U1;d=sys;n=zts;s=signature", "10.11.12.13", "GET", null); ResourceContext rsrcCtx = zmsTestInitializer.createResourceContext(sysPrincipal); + Thread.sleep(1000); EntityTag eTag = new EntityTag(Timestamp.fromCurrentTime().toString()); Response response = zmsImpl.getSignedDomains(rsrcCtx, "signeddom1", null, null, Boolean.TRUE, false, eTag.getValue()); assertEquals(response.getStatus(), 304); @@ -31504,9 +31506,9 @@ public void testGcpProjectUniquenessCheck() { } @Test - public void testCreateTopLevelDomainNegativeProductId(){ + public void testCreateTopLevelDomainInvalidFields(){ - final String domainName = "negative-product-id"; + final String domainName = "invalid-fields"; TestAuditLogger alogger = new TestAuditLogger(); System.setProperty(ZMSConsts.ZMS_PROP_PRODUCT_ID_SUPPORT, "true"); @@ -31514,9 +31516,12 @@ public void testCreateTopLevelDomainNegativeProductId(){ RsrcCtxWrapper ctx = zmsTestInitializer.getMockDomRsrcCtx(); final String auditRef = zmsTestInitializer.getAuditRef(); - TopLevelDomain dom1 = zmsTestInitializer.createTopLevelDomainObject(domainName, "Test Domain1", "testOrg", "user.user1"); - dom1.setYpmId(-11001); + TopLevelDomain dom1 = zmsTestInitializer.createTopLevelDomainObject(domainName, + "Test Domain1", "testOrg", "user.user1"); + + // first try with negative product id + dom1.setYpmId(-11001); try { zmsImpl.postTopLevelDomain(ctx, auditRef, dom1); fail(); @@ -31524,6 +31529,17 @@ public void testCreateTopLevelDomainNegativeProductId(){ assertTrue(ex.getMessage().contains("Product Id must be a positive integer")); } + // try with invalid environment + + dom1.setYpmId(101999); + dom1.setEnvironment("unknown"); + try { + zmsImpl.postTopLevelDomain(ctx, auditRef, dom1); + fail(); + } catch (ResourceException ex) { + assertTrue(ex.getMessage().contains("invalid environment for domain")); + } + System.clearProperty(ZMSConsts.ZMS_PROP_PRODUCT_ID_SUPPORT); } @@ -32238,6 +32254,62 @@ public void testPutDomainMetaBusinessService() { zmsImpl.deleteTopLevelDomain(ctx, domainName, auditRef); } + @Test + public void testPutDomainMetaEnvironment() { + + ZMSImpl zmsImpl = zmsTestInitializer.getZms(); + RsrcCtxWrapper ctx = zmsTestInitializer.getMockDomRsrcCtx(); + final String auditRef = zmsTestInitializer.getAuditRef(); + + final String domainName = "athenz-domain-with-environment"; + TopLevelDomain dom1 = zmsTestInitializer.createTopLevelDomainObject(domainName, + "Test Domain1", "testOrg", zmsTestInitializer.getAdminUser()); + zmsImpl.postTopLevelDomain(ctx, auditRef, dom1); + + Domain domain = zmsImpl.getDomain(ctx, domainName); + assertNotNull(domain); + assertNull(domain.getEnvironment()); + + // set the environment + + DomainMeta dm = new DomainMeta().setEnvironment("production"); + zmsImpl.putDomainMeta(ctx, domainName, auditRef, dm); + + domain = zmsImpl.getDomain(ctx, domainName); + assertNotNull(domain); + assertEquals(domain.getEnvironment(), "production"); + + // update the environment + + dm.setEnvironment("staging"); + zmsImpl.putDomainMeta(ctx, domainName, auditRef, dm); + + domain = zmsImpl.getDomain(ctx, domainName); + assertNotNull(domain); + assertEquals(domain.getEnvironment(), "staging"); + + // set an invalid value and verify failure + + dm = new DomainMeta().setEnvironment("unknown"); + try { + zmsImpl.putDomainMeta(ctx, domainName, auditRef, dm); + fail(); + } catch (ResourceException ex) { + assertTrue(ex.getMessage().contains("invalid environment for domain")); + } + + // remove the environment + + dm = new DomainMeta().setEnvironment(""); + zmsImpl.putDomainMeta(ctx, domainName, auditRef, dm); + + domain = zmsImpl.getDomain(ctx, domainName); + assertNotNull(domain); + assertNull(domain.getEnvironment()); + + zmsImpl.deleteTopLevelDomain(ctx, domainName, auditRef); + } + @Test public void testPostDomainInvalidDomainMetaStoreValues() { diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/store/impl/jdbc/JDBCConnectionTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/store/impl/jdbc/JDBCConnectionTest.java index f1ae5ab125e..8e0bdbd9d7c 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/store/impl/jdbc/JDBCConnectionTest.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/store/impl/jdbc/JDBCConnectionTest.java @@ -84,6 +84,7 @@ public void testGetDomain() throws Exception { Mockito.doReturn("tag-key").when(mockResultSet).getString(1); Mockito.doReturn("tag-val").when(mockResultSet).getString(2); Mockito.doReturn("abcd-1234").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_PRODUCT_ID); + Mockito.doReturn("production").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_ENVIRONMENT); Mockito.doReturn(3).when(mockResultSet).getInt(ZMSConsts.DB_COLUMN_FEATURE_FLAGS); JDBCConnection jdbcConn = new JDBCConnection(mockConn, true); @@ -101,6 +102,7 @@ public void testGetDomain() throws Exception { assertEquals(domain.getProductId(), "abcd-1234"); assertEquals(domain.getTags(), Collections.singletonMap("tag-key", new TagValueList().setList(Collections.singletonList("tag-val")))); assertEquals(domain.getFeatureFlags(), 3); + assertEquals(domain.getEnvironment(), "production"); jdbcConn.close(); } @@ -128,6 +130,7 @@ public void testGetDomainWithAuditEnabled() throws Exception { Mockito.doReturn("tag-key").when(mockResultSet).getString(1); Mockito.doReturn("tag-val").when(mockResultSet).getString(2); Mockito.doReturn("").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_PRODUCT_ID); + Mockito.doReturn("").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_ENVIRONMENT); Mockito.doReturn(0).when(mockResultSet).getInt(ZMSConsts.DB_COLUMN_FEATURE_FLAGS); JDBCConnection jdbcConn = new JDBCConnection(mockConn, true); @@ -143,6 +146,7 @@ public void testGetDomainWithAuditEnabled() throws Exception { assertNull(domain.getProductId()); assertNull(domain.getFeatureFlags()); assertEquals(domain.getTags(), Collections.singletonMap("tag-key", new TagValueList().setList(Collections.singletonList("tag-val")))); + assertNull(domain.getEnvironment()); jdbcConn.close(); } @@ -412,6 +416,7 @@ public void testGetDomainAllFields() throws Exception { Mockito.doReturn("tag-key").when(mockResultSet).getString(1); Mockito.doReturn("tag-val").when(mockResultSet).getString(2); Mockito.doReturn("abcd-1234").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_PRODUCT_ID); + Mockito.doReturn("production").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_ENVIRONMENT); Mockito.doReturn(1).when(mockResultSet).getInt(ZMSConsts.DB_COLUMN_FEATURE_FLAGS); JDBCConnection jdbcConn = new JDBCConnection(mockConn, true); @@ -427,6 +432,7 @@ public void testGetDomainAllFields() throws Exception { assertEquals(domain.getProductId(), "abcd-1234"); assertEquals(domain.getTags(), Collections.singletonMap("tag-key", new TagValueList().setList(Collections.singletonList("tag-val")))); assertEquals(domain.getFeatureFlags(), 1); + assertEquals(domain.getEnvironment(), "production"); jdbcConn.close(); } @@ -643,7 +649,8 @@ public void testUpdateDomain() throws Exception { .setGcpProject("gcp") .setGcpProjectNumber("1235") .setProductId("abcd-1234") - .setFeatureFlags(3); + .setFeatureFlags(3) + .setEnvironment("production"); Mockito.doReturn(1).when(mockPrepStmt).executeUpdate(); boolean requestSuccess = jdbcConn.updateDomain(domain); @@ -673,7 +680,8 @@ public void testUpdateDomain() throws Exception { Mockito.verify(mockPrepStmt, times(1)).setString(22, "1235"); Mockito.verify(mockPrepStmt, times(1)).setString(23, "abcd-1234"); Mockito.verify(mockPrepStmt, times(1)).setInt(24, 3); - Mockito.verify(mockPrepStmt, times(1)).setString(25, "my-domain"); + Mockito.verify(mockPrepStmt, times(1)).setString(25, "production"); + Mockito.verify(mockPrepStmt, times(1)).setString(26, "my-domain"); jdbcConn.close(); } @@ -714,7 +722,8 @@ public void testUpdateDomainNullFields() throws Exception { Mockito.verify(mockPrepStmt, times(1)).setString(22, ""); Mockito.verify(mockPrepStmt, times(1)).setString(23, ""); Mockito.verify(mockPrepStmt, times(1)).setInt(24, 0); - Mockito.verify(mockPrepStmt, times(1)).setString(25, "my-domain"); + Mockito.verify(mockPrepStmt, times(1)).setString(25, ""); + Mockito.verify(mockPrepStmt, times(1)).setString(26, "my-domain"); jdbcConn.close(); } @@ -6431,6 +6440,7 @@ public void testListModifiedDomains() throws Exception { Mockito.when(mockResultSet.getString(ZMSConsts.DB_COLUMN_GCP_PROJECT_NUMBER)).thenReturn(""); Mockito.when(mockResultSet.getString(ZMSConsts.DB_COLUMN_BUSINESS_SERVICE)).thenReturn(""); Mockito.when(mockResultSet.getString(ZMSConsts.DB_COLUMN_PRODUCT_ID)).thenReturn(""); + Mockito.when(mockResultSet.getString(ZMSConsts.DB_COLUMN_ENVIRONMENT)).thenReturn(""); DomainMetaList list = jdbcConn.listModifiedDomains(1454358900); @@ -6587,6 +6597,7 @@ public void testGetAthenzDomain() throws Exception { Mockito.when(mockResultSet.getString(ZMSConsts.DB_COLUMN_GCP_PROJECT_NUMBER)).thenReturn(""); Mockito.when(mockResultSet.getString(ZMSConsts.DB_COLUMN_BUSINESS_SERVICE)).thenReturn(""); Mockito.when(mockResultSet.getString(ZMSConsts.DB_COLUMN_PRODUCT_ID)).thenReturn(""); + Mockito.when(mockResultSet.getString(ZMSConsts.DB_COLUMN_ENVIRONMENT)).thenReturn(""); AthenzDomain athenzDomain = jdbcConn.getAthenzDomain("my-domain"); assertNotNull(athenzDomain); diff --git a/ui/src/__tests__/components/history/HistoryList.test.js b/ui/src/__tests__/components/history/HistoryList.test.js index 7901aa43b10..12dc6c948c0 100644 --- a/ui/src/__tests__/components/history/HistoryList.test.js +++ b/ui/src/__tests__/components/history/HistoryList.test.js @@ -19,8 +19,8 @@ import { renderWithRedux } from '../../../tests_utils/ComponentsTestUtils'; describe('HistoryList', () => { it('should render', () => { - const startDate = '2023-01-20 07:51'; - const endDate = '2023-01-20 07:55'; + const startDate = '2024-01-20 07:51'; + const endDate = '2024-01-20 07:55'; const { getByTestId } = renderWithRedux( ); diff --git a/ui/src/__tests__/components/history/RoleHistoryList.test.js b/ui/src/__tests__/components/history/RoleHistoryList.test.js index 111856757b2..618083b4acc 100644 --- a/ui/src/__tests__/components/history/RoleHistoryList.test.js +++ b/ui/src/__tests__/components/history/RoleHistoryList.test.js @@ -19,8 +19,8 @@ import { renderWithRedux } from '../../../tests_utils/ComponentsTestUtils'; describe('RoleHistoryList', () => { it('should render', () => { - const startDate = '2023-01-20 07:51'; - const endDate = '2023-01-20 07:55'; + const startDate = '2024-01-20 07:51'; + const endDate = '2024-01-20 07:55'; const { getByTestId } = renderWithRedux( ); diff --git a/ui/src/__tests__/components/history/__snapshots__/HistoryList.test.js.snap b/ui/src/__tests__/components/history/__snapshots__/HistoryList.test.js.snap index 1f4f8c72929..b7af6833e7a 100644 --- a/ui/src/__tests__/components/history/__snapshots__/HistoryList.test.js.snap +++ b/ui/src/__tests__/components/history/__snapshots__/HistoryList.test.js.snap @@ -389,7 +389,7 @@ exports[`HistoryList should render 1`] = ` data-testid="flatPicker" placeholder="Start Date (Optional)" type="hidden" - value="2022-10-20 07:51" + value="2023-10-20 07:51" />