From 73194d0e31260792bb2d8e16e45e294a2459599e Mon Sep 17 00:00:00 2001 From: Henry Avetisyan Date: Mon, 22 Jan 2024 15:39:32 -0800 Subject: [PATCH] introduce environment metadata field for domains (#2487) Signed-off-by: Henry Avetisyan Co-authored-by: Henry Avetisyan --- clients/go/zms/model.go | 66 ++++++++++++++ clients/go/zms/zms_schema.go | 1 + .../java/com/yahoo/athenz/zms/Domain.java | 13 +++ .../java/com/yahoo/athenz/zms/DomainData.java | 13 +++ .../java/com/yahoo/athenz/zms/DomainMeta.java | 13 +++ .../java/com/yahoo/athenz/zms/SubDomain.java | 13 +++ .../com/yahoo/athenz/zms/TopLevelDomain.java | 13 +++ .../java/com/yahoo/athenz/zms/UserDomain.java | 13 +++ .../java/com/yahoo/athenz/zms/ZMSSchema.java | 3 +- core/zms/src/main/rdl/Domain.tdl | 1 + .../java/com/yahoo/athenz/zms/DomainTest.java | 62 ++++++++++--- .../com/yahoo/athenz/zms/ZMSCoreTest.java | 13 ++- libs/go/zmscli/cli.go | 15 ++++ libs/go/zmscli/domain.go | 17 ++++ servers/zms/conf/zms.properties | 5 ++ .../zms/schema/updates/update-20240121.sql | 1 + servers/zms/schema/zms_server.mwb | Bin 53107 -> 53527 bytes servers/zms/schema/zms_server.sql | 3 +- .../java/com/yahoo/athenz/zms/DBService.java | 6 +- .../java/com/yahoo/athenz/zms/ZMSConsts.java | 4 + .../java/com/yahoo/athenz/zms/ZMSImpl.java | 28 +++++- .../zms/store/impl/jdbc/JDBCConnection.java | 13 +-- .../com/yahoo/athenz/zms/ZMSImplTest.java | 84 ++++++++++++++++-- .../store/impl/jdbc/JDBCConnectionTest.java | 17 +++- .../components/history/HistoryList.test.js | 4 +- .../history/RoleHistoryList.test.js | 4 +- .../__snapshots__/HistoryList.test.js.snap | 4 +- .../RoleHistoryList.test.js.snap | 4 +- 28 files changed, 393 insertions(+), 40 deletions(-) create mode 100644 servers/zms/schema/updates/update-20240121.sql 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 9861c96e0d91cad9ffd6c594ba4597fdb5667685..4c02373cc5ab7484dec864d58a72ccad308b07cd 100644 GIT binary patch literal 53527 zcma&NV{~NgxAh&X)3I%JY^Q^cI<{@w?%3)$>Dabiv29drTTlAF&;OkBj`zd!e7Q!A ztHxMWWA9zH=A7$y$^8U}Km!2-fdO%dU{!-WYv^okY~w_4 z?P@^pW^Hx)X6cA45nmbMv)L7(vU++uR=dMz-!-SMxC&g+WLKt%qPO(mpGNr}5JZil z2W}*h*7$Tm8J|xkIsTJ`g_=mP$-*!pdkbDfw10;H`ThCh-IqTjli*FhiX&qFTGuz* zmqOrGK;TXH!&60DNsRI&V&}=iTSfcz1hM=TXx5mflQlmYu@ezXjztv~e;#&9a78sH@4tRF(03AIp zZ-0DG_$@G)E^ys9ZE#;DAhHLU&?7Xb6hOr6KOZd=FU4XKRTt^Q&h}Q3_1@XS`?5$l z1TD>#Iabl}{Ic4x<>XwOqEh7B`NS*D5=pH7gQUAU<-3voi0cGcj=D)D7f1iba%78M zq@6bR#l!PX*SnqJ^UAgJD1X!06tNlRtnp7i1wpSG2xWl`DQk0Z>-XBIDy zySPad%=zI~?l$v*)90db#E)xn+_?kxF^qBDHjjr}2ge%j2;)%R`*+xrckGEY{^uKp z`R4}@@0*axN*}lSc~nQ$GSvf2W2N_<^r{d2r`d?T;c)?LoXbAhj~Cs__nre^pH{W^ z_bHI)(P!Xd`eN0`){C+EldQGq<;Q#Sz~i`h$D8TLHn?PIK~^kr@IaMu?t4w~W>;y_ zIAfR1&DkRV!#rPwoNztxwJ9cjweGAG`;Sh$hxJWgrtE2)#B0U635MciBv1=HZrMMS z>=$yc@K~1o@Ae^)O|FQ&H8r-peaD|G^~UT^H_Lxb+mmAR%W43d}f8Ujr1{}=9}#~E`4!)e#Q3D$=n5)za`%tU%!rO z+&>j)&K|!9W3hb~-bm!X50%;@~}4&|+F zrLVqP{n2o|>)W5;Uy8QpP)QdvNDb?JWZNp4AqvdZi+a!tVhcX`uGQ&@pQ$u37b$aL zKGET=p{-M{LtAw6Ok?vH#y?eqD6sb2_td(CWw zZ_rRFb9{z#^Sk88cD^-DXYPdK4lvrbwxU96Yw+@fl;b{>%wy@^&Fry}<3)el3(Lpt`CdAvuX`%cu;OG6F=Bjl9&k5Uv`(9`Q9d(u^=8bJ zLSL!+tCE799HL~k{jKJ(b}W}8qV8t)S#uJ23>YYT+%7sgJ~iRenWQjHPrnfOdM%Kw zvW~#olx}CbXlZ$nYLBltsbXpj%BeHPy{eu$p<`oOYPhyBP~%q2LujQUR;z&7AT{wT zZ!EDnP>qTXc?*_fj1r?fURFXSzz9F3Tu~;rJdgAwvCMk-nSm$oPd@yW>g9u2gU$YF zCGwKjnQRxk3IdE5vuKvKOLx1Ci;Xobede&{X=ZYlM?kV1>_S4t=_xeci&VZ|D$^a|wpbDHNX z&X}UddF7F8W)MFOh!tASc-G6`jHVFtUMm0Z@Nu&xc7ZIi6W^ zg$p;2Av3eRl#~%f9qM3n!U$DgrqY_kS!067##mUHI{T16(B&QhCPXBk%o*67pG%b3 zG>%{iVQ57pM0G$~NE`hn1pRYG37HR7z!{rzL6?YEid$a?=p87sMci8n$(65a3cU1JP&C5aCbiBs%!% zBeo6RuH00-*Wm4(nj6N2!;uoiGj2fChagDI5mmV4SNh?p&m@`cAb`bwoPfCjCNTTp zk|>z|3V*kn=MEhogT-Y7E)c{fc6agP!fE3phcHA5#hmFQm<S34z6 zG`Lw@u~6Y0nto!SohF`5+I7$D^P@$Gqa2M(gO|A7+?xD2o*X%I=nU26LXw(%gm_dK zuY8mp9y;DZN+G8Zr=$>4G|Zs&cC$_oewY-%wvMMJ4fUPNbV1(u}4;79AepqZQ zjyk8HH`s+_{!qfzTF8t+IER(!4`Be~H~T=rTmwaT6ejW0)?eI*=;;Weh)2;;OfB^T z;M*CQ@^Rm?)GfiJSu<^3r07iB#0WROj;B#b7l%wYBdHflh=m z2NAQi;qY=2r4h^E0eN=u)Q-w3!q7=%nr`?_+}UQ)JGq z7mmh$@N5*WUlw-ZNl?Gin}Vv;QgKPki#r#QvZc#eo;uz4-83GA8+g!Hd4oQ9FwtNz_13Hn`m&x>tot?4nXhW`&Ou|+M(2H) z8MVV6gI5_RP_BLq^P&gN5`NLY8IPdQ9G3v$O8vY?E#3Tm3EIu7(&fivZYs@3quIa@ zj>S^1tD>@_3tNm2?T4^19NaD)V-pwaVM2gJ2CEIh*;?p>u|6$RZ=t)zx^OK$!HQ~+ zAS?o9#4G?X$dB)FXH>;Eqgti=3e@>9ZMvsruUd$xvv{kK9n_hu(K&0@qJLj7Y`fYo znXA)f@U$kDT67Lui+I^&n`?Y#`5qy5)#9F<9x<x^ zEjC;WfETpiDTxy~O;zQbO+fYt%7=g(vw&WJ1(kN7UlM^qvdKfCHL>ON2=Y8e|0{Yb zxtxrlAjmOBW{A5{ZBk@u)yN-KzFD!1Pk$4sx*Trrb!E_feQBFpr6%((5d3gi^O~Kc z&fjr?Gu6md@HbUJn*_j)K4ozQHPn+j!R1Y7 zsizZ{nBaNzcBiU=NJldG=)=VAsBCv~2hpx*WjurEFs%%Eh%nZ=sZuinz<#fJwl2!( zr<+I15OW8~?xS_OVbAKio}<#65JpGr)TA&RFV}6*m6MAN+Tlg(pD?{ZY~&=$6ctk& z;ie_hZudBR6T~G^)XzblIZbutJn#tIAVvgaGWG_L()w>f&SVv7iRMcL+D%6G(e-)X zA)t~E)RYs$sbqYUR!(RW^f&#%JGzY2y1DW9D{K&ZgiLnxlo!s<@42XAB2I8+IEGJb zyJhsT3*Wz%;_N!3(iF zUVcdjc2BM_`Dt*EN zSR`KZ7J|faNWf2!S1(Ud$j(92q$ts3>oynp=%;I(yksV=8T?zH;!O8YkN*N zdp@sg6f$yts0wK%x7PNi4TA{DQO`5FzxRYLx9JhS9guMJROf=!BBl=J7b!vP;$&HOh zJR4YId*@zRvf8LxKz#XO-M;Eup5f#pJy)2oIH^GOz~QS+XR_TiYWxSVQx_$T2+l{) z<8jAdwP32e2rzY&*S1PK`e@BkDVpV1(V0JyPW6jvaeh!} zb(ql?Y&ij6XP&d2*=S_#Z||_K8f^IWTNG#@2ApvM#{ql!v)v4~Z)$}b>Vq6zbQ4<^Z)OlT z)g11|TjNg78zu32FV%V4Lp?CRztb{$iwws^+G?lT%~0cZdLQ*urbvW+-u1wr)wn8jnmj8wFBtX7b~ZS8JTex0!e&;7A` zBfh!*5pj>(V4GfWW)h)-s|f4_Np<*2Vgb&+3L*VuRg_4Pu||iXghxhO~Y9gmiLgaMx_3bSe`ci*Fl%?R6F{d&8W=D*q7Z8_`8K=Plv02>Bu?N$6*7Hy#aE9Zp7;z|k>nx=E zq?8`=JIa%Y?)Q+F&`qX6nINU*1(@)OnIV9WYb)B*$AyanY?RtNDfmIq zG;z4eD4;Ui!{PBJgThAC82cR==@s%)9YuhE40)z$oG_`itT3ZNka`nl0uX|svsBk~ zkGHQM^rJr!r>macOu=MS%4Syv8!BVc ze&&gp-u-Y^(nkB1FdFe}=BV@R_+I0F^Rjsyv3Y!VC-73S;$>YysTWdnhh3+c%0slI z0HC`ezu(Q_s@<*ETm125L|imsX2L$a8Xwi_8@J$6G-4xq9?a^&*Hvk6?yh*i=chn> z)vM!czp7~CDUZg8jc@X$sf^}1zcr0|I9`Qym?n61JXzcOu8o2hi|~!+e0JgJBG@tJObsqI>7bEjjyj>;L3;Y_Vv?~hjQA}L6+3}HRv;VOGkw; zw{QWO`XH;^w;zuFR7u6jFjE1Lh5@;>H2GJ>X1qO8lT0&ixp4z2oV9oAhv+deqXx}t zzPwq=gdE@*;WpMcettt02u#>e!x1Q117tmgLqu>#O1mQNKR&H17*Iqnejmo46PX2q z1kC=Vt-fsYWRKG2cZwqjfsOUGpIveBm2`vfRzC(F^I4CEIk5x)`zyaD4RT0c?>0Qk zx53c9G*SfQ5Y0`v(nZX^89U7gXb55Af@`Ipa_uE%v^aYnm3UY`;FULi60-K%Nik+v zj5XpJHts3KP^~J!=5N1~Y1&{=j_yo0OhIw7uZ?VgPRpf@1?@n@BaBby$mtm-AxVU* z%gMGliuW;1+ley1IRxfxM~*SE_D|Wpw6UBBV9a_f)%AUPNSH9G8#LI2@9~rc(p>(@ z@t9;N2Jy&(;{M`z=qPj)s?o?{zeUJMP9)fatfk01Ir3YUBLi2RQ6Tiu z%4K1jlQyU%n~_rNndr%6m_F@5>Q68#QI6~h$73(U!- zc2VF9I{lbtcctXWMMdzUVM82)U_(j!x3%=afR4Yx4>eJ*)3km`>{yvz#L}nW?9@*| zc*W-sRa1YPXaPkjfF(Oh0XYBRNan0 zdg@R-u`EiQLAN<+IAs`#?Gx=X#+djBmpMUjtFB?H3fH#0tZbiS!G^}XorwMOCE1d# z(A4@^`pJG9@DIdRn_L%T4MUhvMPIZ=8KAJBUEP}xt`_|&JhipHjnMaK*=BM?Mr4<;BIaYN!;*a*90*e)*S6U~?i9_bSe?XS>n zJ<4Zo^m^T7=R>|;+i#C8cohpMDb|TU=~(pA*prgD36*(z&?qZ1H(2k}QV+I$Y-5!Kt3);-%5R-&OsT(}e4|>ajBdL0?`cA26Nk#!NzE$L%OS z;r<5|U44KDA6uG+KaHhQVHZopM%{3Zm~ zj~+pS+>Y_8rSH!02Hm&1a_>mhv7uKn9Z&0IBQzZ+Eu;9?oYHUK3C^61?w3G6P&B8s z)|{7XU@Vj`5b}VxsU2SJD?gES(aWwniAHcac)Gxg1bK!8xJx#cD2MJojhYUw4o?>k*dzLZ zJuc)1n{*99@*5z~LW|nqisHhav#@k@8gfQBc3_aVZlF)TCx_ogN7-}=tcv#xGTgxa z629822vAn%v4TJmhgYZt?yXgL<)i?OdW#V2Pd4^|&!lf&s9SuE8;C4zghp5Yl^Xyk zPUbt7E9ssLYt*#>kn3@;owSivrFWlk z!De!Q-^-Y)?7heA-uww>SM%b>as^++c-bAW{P^HR>woeA&Hv5^;$;_|uT~^k=j>V3 z*GHO9QH{L$+)JhF3~PCpOZ8>3GZPD{uRcsWPV*p{>gc+0lM<@&ZV74f5*97-L!PL1{OiS-=bc&1$5Zk8Xz%jULlhNRWQB zMz4fnO|;5Rwxjo5(}?FDZdK|gOGm%!D2GqczYT^ycEFnME3FSLq;Uz=cpkTT(5#_s zk(dj;2hFEF;)S96%84_|S@)078{mI9@)tyfl74`ezR>A>d~hvs9TRz$#Cbb2Ik~mK zZO}aw8N}x{XYZRb$IF?;$a6XLgwd_4$dt-ZCe*3gux;&Z)A=*UkYvo)erUM8eqCey z1_~Zw9D`V0E}+$X@-qmZE<<&8vxpX`khGs(`*2Y>4Z@oBl*HU21TWBwWc;ap>0_;KQ) zkty?{=B`}DVGPEh8I! z6Jzf>eywfxq*h%a2#W6izo$6G_-mL+|B2- z<~VD4FZoJsGl*P)b$b2`!-TA4rpj1!9NK-;OhQEQ^7M9(}xYDnN;GYR0 zh!#T__#cr<`WQK!$oD8EU*>)K8kn8$N6O$&G3|Yhme|3(8u2&!e>zklNDH=yp&+$_ zz^oRPN~HLIWpC{36~wbU3A&aY7Hqx?rvvj4@7CVB%({MhI__N@-^(q~N-%^kI?g8~ z`ah5pIV~`c)Q^$NwLAL$JKIED^&UEoNkCEZT1h)&{9~I|-SM+361%rx6KGaqc#nxH zBbLOxq}B;T4UR=e?N)@6Bk}WWPjTNze(Vj0^I?>EuID)RrD11|VYDf!$HG0>La99DrI0rmTR7TIVeTV#&^21z_ zJwLnQF@6GWPbpU3Gp1!e4|sw`-XJ=(STgYJZ1`)d6OFSfm}+-rUMi;&xm4t5HnRxiW?$k zzeW}sgilJG7YQ-JEom{4;G*C-wHAHc8MN}uT$?gUGjsr_;LsC&WEp~~IzDogwLCmo zAaoPuWLQs7K0-XU?)d><;EAQ0oj^GZfm#vjxsNVmVic&Oue9K3JjzMiNerU_l}BE% z`71xEHMUk7t)rLk4sI0>;i5tHZ*rp=sd%5MHO*spf*=x@kWF7pCqQxNo zBvztwWColY+-EvH*ISHM2%b-ZxTRU!~?J#k_t{LxxAE$Kmt;#Noo zlN26;Lj`@}q?a_5?LFYM$h0n*byuG5f5h3=oXG1bQU$?4zEiM!k$K)qb91`G*ivaN z?ab?9xufQF;YCmvZ>-ojO}OWOe#3g{BT`_}ibX85$W3yi6^iyf#4rUGF(i z3J)2TEeS|H`$Ahzy7^4kVBGa{jVp&fqhuNUh0f_IHx&j=q{`g~yfCd?n# zS0=R$=&G||_`=(TE=^1juS$QFUVu-=9{@6xQ_`ee&Me_e{rJuqw&zu^wV+mS(#}a zOE{KYXrvsTFr77(nla)^9UMtt$a9js3R=p<*yJSd!$8$mj zAVZ4&m1et{7!<~{7V=LxB$4-lA4$NBk0oq@hGA!q-2~@s$~+4s|AuYIr;*z-hU3NL zaYe*)X*;H`-4(Vpn28s@a7v~YB~a94pvVbOm<_>X@cOh7L*UB+icM%ZrN-Oq7MA{p zRkXKmH-2Ew1qU{=qyBbI7bzJC9N1mx;_EIN8YUwlj(Z961%j-`DF0iBnLquH4l|kE zb9X%S;!D^0j}Ck7Ov`j_*1g$`v7OPVahMJgMq(i8%-x?}=~G#dkc1s10Xp4rb9nHdVfuQX|eZ^%8AR}3Af%kg$Bl-)<=BOYSsj5Q_I611~ zxyb&k4V+#U2wz+LnOU)^fO!c@c5yY8Bb^>WZx079XXy7E2SQay9lZoP+Y!l3Z_WqC z%AM=P+&W|Yyq3^$mH`MrPQ3n#!E;o%h(u~#kofOy0pGV@mEY8%BTX%HCxfDGCfk2Z zv(y-JY>*@e!-$3V<>1?vUjDdLhky}Wni-aVqMFuRQ?#v#>RizAjIb_rm$j|gyp;=M zAtVLY1c4$MR>CpEqe_e9;FmC_lR<{_aVk0DR-D)FGa_0nh%MI4um(l@RCX}RS`CyJ10ml^=T?Dr?^@BrwU>Sa&-G7&swL!F)>d8|Y84qe2~ zPNa=_#ak&pWH%4sQE51HFNa==Z=1l=h1pd5BwBVjnrafkJKG+3Il>Rbu4s*a<{% z|6f367BAio38IKdX=Dr4evil6C~_SC8$7ibB!8ZKw7b_9iw5x{lGl2-s;x@$$q~|} zrTLK`h#YiNnRU6Hvgqlt-PJHQEHX}DrSbQ$AwkR>Mo}P zi)(NlSe>b|?Vj69h@O7l3MZT1X5Oc|UBc;kuk6OVC$S3?ka$<`<6&w3}vu7;ES@?(wxGrlSLo4Ztv!?)Ew<^AdKYha79eC@p*C1W>d z6w`GK6D&-Mx5$Irq%PQ0sH{bTzYspTKN3HCe_tXL|nh4mHHs_Ll7ZXM6Rvo4RILv()Vs%l~GryloU7X2v}yg1V( zlQDjOSMlcBbgO8CD0fix7NG?YK| zg@%GiQzC8F2#$RggeETETSkXF zIP0^1ur-9){R+$9`Ya-opY?-Rf5zfvm~v8r@%3G~1=ES1PQ|n6GL)>-tf>-uC^ZX| zEmGb^7(1@l#ss;JR!DyLwf>)R5gqAbi^9w8gXLQ)f$_nr>fAd;S=Lv1y{G@LpE9Yl zcHZzW^+wb<_6+6TJeW7R@oDf`BDAQwb<|whdQHoZLXn@;+JhchRgF-8Jabq!y?V4V zobCG_BUY``F1A)_jVMVnZoPX07Zv zAGzUBuKwBO?fL%8h&JJ2QjSl;ePaRf{+YEW-@v%C^vl4n@Cau@RCD)QHFldDhnDcp zw#>4Y%{n?;xKNF|!KPMP7m$Ec@IIH>tdl=0u|SvC%YC53jQLh_lMk)pMfpzp zCWe?@gAc2N`%*$X^QB2E_XSziBLA)#rVXPIInoy?Liuab+0|$`t{rWmn0tLO^K5z@ zubd_18Uf};@8kw}L5<}2Gw`brAuQ^zvg}k_H2J+3X5Ey31m*r~D0JDT{5WHKp`X6e z@{rc@fqB+sZo9isX*0D##w>v#Wueetcd*AJC@2r~mYJb4IA6;~SAgv?yX$lLQ~$h^ zABg$q-h^lDG@Zi9?)>0}IBB1beUj(crwyy%6KO_G^anncbH)3wk3j98+ci<_3rx^S@-N9Eiqn#WldPefjoO12dd8Lh{$Jv_gOzew}dNtNu| zkdfH&!k|eJfr*8F@1YSPBB^EplU*nqz0t>78kr*&jAf)tLG5<7pdTcIrHF^vFdJ^i z2;O!%93tW@K?+zEW^DI-bCe7y%S9F1!}jF$NVKfG>FM)O>&ay`?d&!6uqOSs5) zawx=1Fietck0Su;-n^S^?2{^W055q&yMp?P0^tCY>NJ`nV(8Ufi)t7Oaj=2?{-~G$ z_eeZ3NNH*&LW-Q>Qhl+Sg1y>K8obMW ziq)os)bs`-tGZ|bn~J+<_X{{@4K{_}A2m8!bD2ib``|-(d-JV28LT9~{v=gTGQbnL zI(3%Fmg_Z?I)g)J__n)@$wPckBmb;W8Sq{Yb5AKA9T91)^_4XxAF1lj zRWYO&5v-+e8Q}P7HqDz=-ER9nN|el8G{Wci3#lvR#p$Ka5k-y8)#4>kYT6wq#p^bF z4%gY9Bk;qt|B>n%`~Q*Zn<=bT&BKq@Y*E%l1;!K}JG5@4tS~uLYCC*t`cd25NXdOj zv)8Ld4+ZO8PI#iaX>>aOnLqSapKHg8>UwGHHea3wS-t&E&=;o(3VOJ-N z7(xzle>0PWlzzE2_NE~;1R2?+2be1{%R`7$vp;>0{1u}GLdt7D;1sPtDQSwmF}@_B++I$>i$~7Hq?1mr|wN zVDl~2#W9qxdbEprfraWntpk7f+Uvdn*d@n2&lo<%0pAd$IM;yN`E*%gaj-zD2yRqhaVZsL+h+pETr?CjNL zZO^E+Pl$r2kwb!XT&F^rcnIaie?-b}%Gggc9&MNEheEjN`BSE6gAmQX5QXDkh++yj z%WfT_{s~baM0>Lh)wTne6vdFif7Tf(!~+vruf_3Pw*sBBsTBeCQNa>HBv6JZOvMyx zOl7}9d!otVOT&6d#?%7c|AaI8wi+^vpzq%|auf77*wb}3eUD9!H{GLRlpulGPHUeQ z_gfjt+u8dt5NV7Kt3dS+Y8n_^2*Z+JrW=6=v-6$vA8GmpQk|Iz9`I)_(zJxysE|BV z3I@xZR?ZyVnAzfty4P0eGF`u=0=c(}K)!}fnb#!j@!Mltv4GYo_)F#cFyo`8vBd{W zXsH``889_`X1W9zHY5eRM;)p+l8lDpmg;41g@zjb8Nn=-&}^W>+Dj{USSq)(i=p+E z0i_!_@s!(5X=E0Y;T{m8EPVTqo=?znIJMJRw{J!z!Vscy#R~h-AH-0(npg$0&9r~q z!HAs1i`Ppe|3WhY2c_!?#_~fRWsM2MAiGd+J#wlPO|dCWZ(ZRPjg>%q2(E^w&_Lu> zC-feX$TM5t*q^;~{q2$=yJ(Vj!MZ-ToZ@Ex>|Z)F4%wb_+ko^*)Th~-Mxt=?-8*^I zbH#d}&w|fNGp(bs;Sa_db7v+TWEpd-`E_lkMW<+nw-n$vE~oad&Gb#sogE+=*ii2$ z{j=~_v+?$=%?MA@8GlMLo>|!}%~C%`qQYUi-)*CGZw^$yjtaDUvoPK`?1HId6mWmS zn;A$sa*_IRBne??Y3$XK_27#Ko73HLGE3p_^)iC_Cqvtl`jQBu=HdxV-6KfvI^CK;o6kv^;a^` zvTM|Y?Y;e(ag^t`1-;(FzvA8`)?lS5(&_xarw0u;!$cp$Pgn?fk8nBohw;3jx>mj> z?~i6bai5hVpx^j@Vf)5Loc2xJztqH-O|SeCsohvf0zZ@uIwi^?tbk|o6Mf6Lf{?4j z$qANg^E4Y{#Jy7&I-|ap)5IlU1aC<4g1|2BMT>;R$nk0*${pAHWgi6IUx)``%zvf| zRa!6?%?SRRi?l$6AT+X)b08mS;R%T4scvxB-HZ&SczVwYsPrA{+Cgh$pYJc+ zV6ZZA4P=Lz{BS&4K_RaJs`cyjyb?mumH<+Z!jMmM?^Um9&WfeO<8pGuf=b(l79z z4^#~0W6Yq7jkjjvu#sUXXnXQ?Qu!3nBkdyt}J40=KNR1A#l?*7+vw{8}0I=`_5zxsJo>#cj8b)l{dNDlVZu&;e=v~u@8 zcGu>2E70&~*23dqA_v6y4-#dlAop<4T+ZY4aPVKwv&_v4YG-=S%%@gNjDRtQ*2-8a zLW`(ut}VcZfwPTI+v=gw%7XF|_m;{DNdn`_4N1!8SjbKehiJYM%8Hld?_w#jgR~b5 zoM!l`cAd+1shA*ydd(o&unr?7d}8czex&N(QzPjkvNPkchdC&&sYl|xRudTh?+Q3t zzl2~~e(9T<1B5ZCao(mw4QE-*LEFIIvPI)33k(Vb{1b+Ai48X|Y&pgfrJ(6qn!%eZUVx+KeXX?7y&cv}A^yeI&84ZV*wI^j`f z?{Ru`dq~KD_kQ~wnAQCSbJ)%JrIoGU0$;m{kochFOG4*h!CBBuN%>1PQ+p zUS7Z#$v9Ll80PPC=wGo-{kdHakNj4%jI`#UWOld9EP@dVrGZ1`OXg(CEV6jOMD=}+ zeAhky0xF3hmxRZF1^jqq3qwbP(VxRKlg!+N#aR0KQBWLFeMFR)&nP$Cn0K~zA`m2? z;96YEUXIUknP(H`Ua|d=r!b<+B>>ZRFqzVV(osq8J7lo8f}UEH%!%MFrUuTj+seW~ z$>aGEtJKem=HSEd12rv7&?HFcXbO|GZ#eyW2*<_68ev=t&Q3}d`68tx$`4_s2YbJd zfbHD)5EZLxJVc!|*6BtF&?G2uLR^qCgCPA#IhmJnn$ceP=(?6+UigH%C&R8%(&Q;= z9^xe>#?)HnRr_kb2#rn7-_hXdj}z6PMo^*kmCX}|kRxZ9?EoE^SjN%e0jPX}aM95L zsKRjp;yuxA<#&V;&u{kXis@h0PC#8Wn^@ew^X98xms^=f=jU(dfb>~`Nhjon^Gs5U z2IT>_6AiF`Bk<<)Kmik@i=#*Ut)e!bR?mBC~+zlmmPvGanWCCXbYG-_3Sjp2T<~(Z0$k#OsAL|>9hPD= zTp3HWv(iAweCk&7V7O=bI@VG?JwAgfrfTW=pmh3kpPgqqw8)P?L#{Tfs&1I1i^t!PEWs>?PB?1zYY2RcKd7?`G@!>eaz%7_rT|mHv6` z`I?(xT?~Q30v^O9XgN(>wlX_*rm)HeF}xULAD0PtGy_ToYS>F&v%ty;!m*-Nf(yI% z_edu}O7s+v;|eP483?IHh|oD5$};KQp&@xD2OB0$`@qP0)HOPQT@4~h$O`NFKPMcG zDkRKy_myaFVO$|AiIs#u#+-0;k+%B9Nhg5i*pm@=$QYkIQ zG-7<-c^*^-v5BSGm#Y7Bl$M;_I2T#jUXR?$$r$E0vlqcwTf&CP<%a8Y8E#j3XfyiL z(y)CNlz9(FWDd%z5td|fgt`Eh(&0s+6+KnTMO&veD&T5sui%?QnH5!1GJ&c4QKwR~ ztJ&cjHN&_Fs~|Zf1z_rw&w){jhe|+QcEp!NoR(p|qy6@GO%UnXo8wrDRpMVOViY;pnjVZkGtw-)!G1EEEY(w6Ys=qT z`)&)?PI1hwcYlS5;q0-Ie2sHCm_jo66T9zZMExsD+FS3x;eZk;{1|au7<2HA*khv> z)vY&cHFZQJm5s`)CbcBvS^}c9b|#Mkyo*qANHe|MLSwr@HDJBC;(9yZh=?jpfy>ubn zWBq`vHR6WbE5-fuNRcdXB{9HvYwKp-`R;%pzs*WJ{(ZwmiyqVW#C~C|Gxvy8_CZB? zR_1*#AIC@+*ke)HhImLwBgCzyw%LGWK8jWK_h?YpwaS!HzLBWCW;^O^MrRnX+^n=` zFQfTVCD`nN)(mJADX3T|?@mokRYdoHID5;Wxc05ps(4c_?3xwo0+54P*p7-46-m3S@e|4?uqKc~3-E)pP#&1TB(X5CCL6xiCyn|$?%*aet-;E0e2yUjrU z$*=sA;mP!0>p~WkB9f)U=aOyaF`>xWtHdy6iLfdc2p6;3pk5;#p$z^i(JrIz9rpmQ zZqW*3=1DW}xzT}-hvxQ=Fz~pOq+}+#f$h#KwzPjceZH1B25#K_ar)%GIDH6Mz4Brj zPhKT{sZyPey!CJBdc53#fBR!vqyzSErw_~DPM>R`znngU|91L7WbMwyi@}rc_PM+( z9lnlb|5r|*#jOt&^Xc9iJZsdD{KAnQ^#7h;uwgakQTeCscFFxP3BK97FqnSMb@0&; zTdaKDLlcP~MDDZ_lbpQQwAFn4SjJo2P9oja(Vp#2_;6UGogqiE^Rx9Kr0TZldUEj7 zL25AH!jex}bJiV5_G4ryW@V6C{ zarv>@4c6YxHhG#*X%CuHBMK=GYY7p@YeWicYgc^wrQA2bl(c0+HoQj!Gta)~&f1uW z=?Ot}afLv~xC~4EbxY5u-ZyjUYk)>hs+GkPAO-J|QP?*mVH~LT+Z5fjXtv?|^N|V$P2J!_`5Y7wPJmG@-WEPrjjz)9~9yJNb}l%r!jCcK%RvleM#- zI>)a~C)GQTGj!n#rH+*1BgAqbLD+j-f5zRh#EQP8r2cdL^$c3yFr$J4DZ9@|ach>bDEPzWE9b; z17v2F=;y85<$}2eDZ=-%ky05}p3;pJ;T?uWa@}4wXw@T(fd+AOR^mEl$QnE4P$cVoYwCBXP+! z-hzZgm!)lAz5oP$()OBwTRF_gYW0;aw*w0a#&2)RbzRJ0pAU)_y0gb!78o67v_c7Pv5Z+qo+6t4ILLkmZTXSuyk}LPCj=M6g10 zrc5Q0sW2IKSxyf+cQ2MEM|^WYUd?`TgjQ+o^WCV!`Nm8e(7- zVp%Dzj-+%&5|G~BJhz2A$mdrih=uh4$!Zj^Zu(8iy1`Jgmz3Dkyw&3Dh>($x7rMeQL(^EjbnGQ?X$ZLKqTdl;)MUkTWk|}iQ8FwEV;$*U~2apBN9jaT`hof z!O9&+yqyZdrU+WM(vfHCCn6E|EonNI$Mdpo!x3-h*gp9udY5Q@m@*%R-4SpbifGbi6I@#>iXC8#X=Fo4+x5Yo{LWb0-w-gf%wupZvWi0&U zmm^7XxIohhvnV~o1X9>GonL%#zYJ480F=0KEoCqbh{5S;jaS1x6w}auPO0Zq+b6IK zMeDX*!nIGZ3B9|YSg#IMEk|A7`6%u%i^~Ezo|?styJAo3gxDIL+hm=)(o=s2cz^OO zz}A}1`)KsV#AOQmEZmc9NL9_3q5(hM9r4Zn93Yc`pAc?{I4n_@ef+LEaw|+j&}0aYqBQTx;T3s)@*c{B)=P%*}%>2->Wj4Hg(UV5U1C(>)#@3SaxQ}q#S$}(_~+cz#wwymP2$+eIp~=$w{(|ZC}l+ z$BLH23b?}H)6!t63nl$uXU~QyXQx6SiS>+k~@U5~$ElrdWQBd*L(}FmV z7||qk-%?KTryQE6ND*N6HRX#RsF``}2^Wt^#K+Pks{QcPToa&a?>o<)vpkl2H}Q@B zsa|`cjqlL<-E5Bo>6Sr-FT_rl4h#VB21Ml!xW3I9z0JQw7VIcybN=kVogM78X74~f}?5=tRkaO zJqG$j3vYcqwU$Ku>|}Z#^yQTKSRAiZ{R0(1qt6a*lE%VPk3%{fTL;NX(|rkk>1tF~ z+ktx?=cH5BUBo!m=dR(k4y3&&FP+#0#^r{lDNooG;|j+CBe^%_F0-A9GX!Z%38Q@< z_suH{xGWesJ@V4^95NS2Op6~kG9eMoqkLCd2Aswb6p1yI_+i*7OEPaYEul~g5*%G7 z%`-9zT{`0NXp1enMqHwp7^uP}h>t3=g4t5R@)Jh7Vt562aB3X65aP&FF(77a5yY{C z6t6@=qES;nrfG3%v;|uWiIZg<*>OQ8l*m+}Y+Xz<_<`Ij`Y)2t zB1|MhON+Z$7s_}QeItCT2o$Cpym`Q@tai>%CB#%#g0VlQ*{6tErTt&HAmv>L{;+?A*cc?!tM(l%X!GCn#|BVM;;e+|d?-n@%eQTSuJErWF`!m%qs+Zwe!~FfusCw z-rZg3*+f#rc0GWiX^g+vS+nTEL)}&?DVlaCun_E$=2;k+Iqs~Qv$6;GUFM5oPsA+iU&I8XuG&Zh}2U)NEUA z`^p&85^8=7PF*4RbY(10>h7eyX%L4PZPB6401@TW>wH7j9dvs3T+?JvuV5~n{BJ+w zO>6#WljdUW3~u-XFHv@Qx^y%$-(UG2LrRnv$jlp4wVE%~w*4Xe8r-tIcqU4eYiJ|f zwc`(w7Q8Hty5C;M_&w(t^7R|l;8}xT2Oj&z-`b5Hx?Z;Y$+CJ128BMKYH*o}7&F)PJhcWJzjd2| zT9qVmH>YZGgC{$ynqRwA)R7PrZ6-2b89TEvZ}REYdwq`FveVZ^BARQ=)x2hMae}`6 ze-qo8*z!7Gere`q%^}Ua)qapWyX*flLg(HnhEW${}n`;9DOij8#LGg2I+f`9LqtNz8$U^-m(yo$#4Sc*uJ{pc$=I*O~%>4%yLPbK*>z z;)jqB-YI~Dr7!yFzxH9~g)fWFnj}_I6`rPGOofn`Qu!%27^6#o_^*Ov)LJK-T3Y zb~y5l4MA$8Z%VNR0p49 z6Cxs#IH}Dj_Q;kxGn%?xzOY)y#1zd41jy#OT_qe<-OjbhOdPch|CqZurCmF+;dxzI zyRE^Nl_k+s6ezI1SoXcBG+iTLF`LBEm-24HA8N1bwZRKWp_-5+3NcrFPdL~lIJkg2 z=m_*!jO7BN(i#l|m)6s}B2@nhExw0f=(aiRG8*xH&AzPh-2B{dSxBNV`{bF178%V@ zEFqChK96OFl*`Y(#6r$U=fRuk9g$VHU${pcLr4a|Id=QY3YNuOwtslKSLwPS=D)dp z1eM6io|&u;-Mm8c_J%g(FgztX>pb0jb0nyk7@MJ^|J%_1M%tlPd_=U8!ogn9Mx*w} zinF4|?3@oiE9F*(7o$cM>S=**-LLfnxXFLif_$H4pZ1#|HVBE*cn()mVYOOCXL)A0 zswUVI9EVeR%L`#jH>EKkN)x9L`S%xno*Rw`#9%5sNsOBbb6)pWWftEGQjB%LgqUj3 zb5u(8Egb2olB0Ds;l_?lJ*7~q>WH}*uxFcaTH@h%;(L7>Jc*nDP@y3VoAP9l@KAcK z6zuzSXY(YQ_s?5)cYMjh^>Mg(nE3U|CvhWvY`ij3Lrz(s27ndj>otZds&$i$ zg5Iweu2!RfF1X<02)8wgk9+JHNnCeLB%G5UX$S z$5e-B*Hgbzp^->VTc_HCm)Q*RJM8{75Cld%)8z@N3{wIa6Z0c$Rl>W{q`PHImI|v{ zaX(d~Y}HkUY0!)Vs<7-nM4}95jg{WCVpxhdjKc!dAF())X})%F$Ab~Qj8p4>=Za;u zTq9Oty|}Zgu|L(P;|tf63(DdyoM??!{1Hj!yIU z&r9BH(Vi-)<+*-ypZW)WKC`Z`@z_i-uVRG59+|@y0HEk%RL5AF64m>=m;G?LZG|A? zd?p7uJEZoi{AjjxmXtgJ{IAFj;nC~ZF9tM!!n*nw1H zRJTO4MSG8I-MXueJs+Q^jsHIk)wc75ywf*LFPt6C z6zwH>H!iHIh1Fm;Fy9bzhk6L)Dj$8y%TXA{*aIgUZ2y{)+D@?l^a9@#yG= ztv}k%+^Rarxd=sjySWIQa9>U8zfP<0TE3okieYQldrjCYe6D0f;LT;2@ zQUAjpv!!&-5hZ)&*PgGG|B|ZRUZiRP(TjD@^otj%`g2}pCR)9LlgsI1ODV1?wKJyT z&?SDPobc)avrPfzq{7%iuI71m;c8!>8O$<1kZ9HDjmfAW%<`g62LTh`SB=A?WC&B( z|9$1O1B*xZ*y=#d!><;`ZZ}3}_5oZnPWvrl9~p8jrb!|%brA5H09%_$k4js1bSWC~ zfYH?>uU$|-Cl$^F;u7NZ5VKgSGy#Kj_cWKJs{4cIvV(E-I2@tfP{svW8&zPFJp3(k zG|PcPygx6Z)Zct>dHuYuBS3!jBV9ysK1i*idE5<3rW1CXkfEK`r9=hL!x8L6$q2i1 zxBIaQdS$MTEn&Mm`qDvgeGu9(S$2`=E;>~w-@X^gJ*UP>8%N<0rm3a`CZsfp`IK2$ z?u0)Q$j~lh-48f|!4rlT|HHPKiBSVgcNdt|y#E_*BhB{*Z9`5gGwe>S4s99|f*}#| zJtc^S)2%j#n^;jGAv#K<=6%!#SK>K-kcJ2e`YZHmctx{Y^S3=wpCF~0_nNQlleq5%Dc)b~*A5)^9 z6CfUDf(NLDU{r3OK0^;&oRg?arRQ+VyEr#Q0df*e3M1c_cr0UNWlRGGIr{65(lOSc z-UY6EoE&DZc=rR@(mg+#bav`Yy*9EXegofb(;40pI?-I8#-P}5T_IL((szbWq3eXb z&p~958D1pu8R&*T`oZ>EhvloOxZG9l6vbAuEo@GFT~Wt>@J4L$MRioDVJaLn7F3AX zIo81WTl5Y27W?-kCATS1d;!V8zIbDc)^XF42OnO-d_6#-N8}r2`dPx{)W1YDREs7FBj2!Yr<3fwQ z>v2h}8z~;*NUEz%^v6f@+@KN3A*-7dydnJz@$~|=HCrE8uGh?dFiZAEyuB6s&GupCW0#YoLYyb38`tw7FVp@0 zvR+;KJ*z_5(r94S>tCb3F4tr8p4NIH6&`G12CB(jdXT}OrR``L`0P9K{ZjDOKwO8z z^J%Vz*@Vt?zx{zD6H> z1Zu4RvCabn71`$(<*x~K$j60PlqO1-?7s2v!Jb4t5qVuFhoT=viANvO_bR>8v~duk6`L6HBqul{tTv&l|Zr%5mH z9Oc}sU^GknF!^Vvu)_iY%hA}y8V3f>B?;k@v)kUJ6k3i|#EWnKO$N3?C`NP$&-RDN zNy0%7a@Gh6tI;;quZkZ7Pxxbd2p2yb$DI${=D+=MmWT`yq#qmo%DLV{sNv5K7Tuk~ zuX|XRH+P=bMoif94)pgv!INJ?hVH+v#@CF~q#HQ7)XaL8jx>z&x-#7D$T<9+JF<7{^OZEj&H$N;A|s_ zr8R0jW&>gN03ljPeFJ%oJP5kNZjCD)W7c?unJ- zEYl_=fOk*37T-OvfS49aUOb0(P|K~x;WV+>V6=a;x^Pke&LE1O zz+$YRwLwCQJ(})@t-oTq(^Nd?JhUy4_^?EzKcoeNLBXawc)mw|(bhUBVIJ0-XHc}c9OqyWQ?VG24)>L`6 z*VIwY`d^jZsPM<9OIlt?Qf~UW|E02fP!PoL$adFJyL=RDL%GdrZeu~^aB|u><8Wln zM5c)&!ukIZEgt?QTGahpw2=F6gyQxKLJ{SAz0ltX#glEzr}iSlskv-w7!I)?m^iPo zKHio?TEM-8y9QZXas&kke`F%?!2QL85Fw1~fxF0B7W4h|26W%8&HXR`f9YFG?%DIr zwDys|mNu{gop8o)HezisVOlPRUaGtMyCY7MCa1}e?{nW^wTkB}vaOvN)yNaath9v% zeZ6S7DN-?FwO+ru_o;fVqELQqJJ~wO+-o~<7ynC1`S8``4X&(;VN4n_1=THlNFl70 z6+ss|Ru@Q9ps<^Vo=~8&n!QGiqDoS0H9mQud0UUX>Tqp+S||__8kvsi?NV~-D?(7> z7di-QI@_3wfvU`JYgy#kuMXG0?*3j=aU6m?+)uB)KEw@E6bCyVeI{qqY9MrQn%-^) zh<+27Q1uS~uR`yU=7BWiFO#7jfd;h^6n1TtJ|Yy68|;jYm667Nkwi~?bRBL+6aA?ga%L0nJ*p8oiC{D~fRV$&`>U9Z_%zv=y!VLOP5Hz;2|#G|3_J+ggDiTFYofEWk*7#5RX6!sGiDPS+ygb$uqS9TP#0K%-N z-1;9+;PAkZTt`X1zs4;cCUmYElsbH2dcn9}v?eh&4xJ1Ij;!o(REC*JWUy&lz&u2B z=q5GsYfgPhU+{)trxG})f{SWc`(0GXbu*OWCw#Cl-kOK(q?kFHBRo8xs0xkjq%>S- zV^IFOLkc{5bQ;Y|CVtd#W^#K%kYo<#b4KjS$W)QhhMw^uYl(%j&;&}|p>?Go+I(A7 zurK`XUq*A4TfVu{oO#&^Yedu^`&n^UnGQv9x23**h33jv{quH#>0KGD(eG$E9q6yu zSl)kmE7?B&l%wccwu(Sn?j6=!4vWBtwo&#ec*kX8Nsf_9x$UjojVv^*=eET!&n8yP z1ZJO@*sDY3e`)%0G0xC5yhqM!83npvaqf`S`G>C~nL&~i;>C!W z*}P~(xePzB9@PJqZ($JZms3GNBHp4s6SF}Z5Vh>i6;Hq&Ra0sV!=l+wYo+Sv zA?EU}x_}_5w@Y+Zh+4=YEG=JZp>TpFILZ=4q0?^jg(|7l*{|3c6o^+bZTFB89;2u9 zJ!koLJ}UZ6Dy($O3R_6yE`FR?jizWt3KsY;$H5N1(9e@#kM8=~m1mD`qraw@XD#Ug zUyee^48#v~d3^4qyWb&Q=Czk=KZ!p9EfZ^E#2H|uq>+^g6rHt@0m6E0p?kyN&8Dl0 z+yoB)Z?QD0v5wTFQWVW<(+$=xSUhy1@T|J6U&smrAEbVn#y~7=S5f@ONsL^QO***t zhI%LRx*vI5gsrrQSF@dccP|HQ$`IbE;)`yPiFW%w@8R7)zV<^vbgBE;Mns|@jo{k&^VXH-v*Hro{^JzlUR(jlH#O^ z`j8w{!Si~&pH4{nsz`E>MdYZ1)}aV!wlqdFz*&3TjcbM41ap4C0lq=PP2pwaoNVU$ z#$HLeB2^F4l-9}?x*!go%7*$J3eTqDV%d?V8(|^9h!CqFkWbqpeHe6+3gcNtq?Wr* zT*X&tC!i&L=}fvZQuA<0S(7sNr5WlZ9j;#!7Ec&~7K=jy_A*wsHa@CKb0)E>XrGY> zt0UTAxN;0(M7y+cKP^QJ%O}-vA1}!_gIDs6q!T_@uA_G4sk`{LH?gRZpqzp`SZqex(l;+^NEG!aL zjmCV)l`=VgIuu)6Sbleu{IN~91Z+HFuz{M??i~Q-t5L5C+YV`Oz_p_9_vPnrr+E*r zzv9Oj*r1?s@Sl$i%k=oCcj`c#KJ#}rY68Ra0Bj%F#1xx&G8XP(3V|XOq6o{RQR(5_ zwxGmlKgr=Wl!{2rbD3e#O5CQb`XjkWOy~Rg23j8;`g(5L*)RgiBKK{5>cez7hD8*4 zfle09f*;=HW5rUeW?N%(gQ`Sz$tv(kOT!=_49;=P&wR8G@&lM}HwK+n zJg6q!nsoTEu2+1@+$}t)?l~=~=oKrJOp1Zzv5C7$WAAmFliVg1nIqYWaZp%FB|L)* zVqA`eo4;RQVYu+qqgAb)mnUvi{LzLXvSezYyJo$+t`qAct+9c=P*3A)vAbd3qq~Gu z9dn;u!bu)XD|enWg1ITg{$N)}F?N+c?ug-a{L7YR;biw9o;3TGC$kiX13q{*8C?jf zvFMGtXX^f^AZpNqL0f=7koAIG8y(L+CcRY;<`|_pYknAY{fDmM6Vt zk`|kWB1-L@A8si_LP{_mJRJx+&sp_iAqNMuil<6eM~YdEMVNyaBF|l@X@n?l8Mqcz zBWCHQ+-F~!rxtH%%b)gg&02Z*4=~C*l>ShOiIiqyNKFg!7~r{Vg&!3O@54&qF6-;= zJ839eC<>Hvd02pni8RIJV5Ypgvg^W=9*PdM&Go}<=!brOW~E%_v2kUNPLB*73uA1j z53q1gZYga3L~VNkv(=P8Ud{op%&d`{5tXmxwp@;gK$in|3cxOIkF|^~d;wZ5ctb!C zKU6|XX}z*PrUyMXN+9d{wl}k+=F7vLpkj(ckT30J?CbwV+x^KRZBt;M5OA_sk#*)6 z6C3PW0O28D64~qMde!~0Jn0eD{RY?q!-P{2hM^rho`TCJBZN~rp#=8quf;HedD79l z;G5L+6Ohzj`=DB(g+a~9raX@=UD~nlY&s3Dr}1=vGL^ z4!hG(lo8C`?gdH#to3%w?GJ)^l*Y27D~P6J+6QN%*SCVuWFa*#20ZPK1ySFNe#TGY z7;JrCNN%R9)_284NFY&#kESdX-Ss#?`!glVio+8YWKbC&hjE#8Lj@W5kLR;rF0jXlv z=VzfXIgm$CCt<5wk)*hR29X3IrfJ0o+27!!_-QFW`5L_%adyUgl|61J{OSN7^>RVx z3phLm%2PrgVzsz~Vbm@IZ4R{7}KaiXuz zT&CR(XCgYy*9R7VQD9V=qZ*P-svLf)G|zZrskIh@O$}r#Wsn^d2({_3SMj4NJA%At zsdL?9?L9AEO9UH{I#GzFq@K>|aU(@Qh4a^0H64<(juh06p8h8qS*!K|oeh1`db!9P z6&W>Z>MJSm0f8Kq4UMbrZx+7r-sVL1MFvI3JEgClDyx87NAvfrR+EEtQ5lr({=^t&4O%`RH18zcu!BL*gNeC@6Q8P^R=wdKV1Fu>tNFz&T$h`z_80hbig|fh5jPo z&wTYOPb(18ulkbRrwfKcpzzCG$N~ApM5|vRu)?t&{vjn9ApPnL-ixdxLtM)N{^WB# z%Qr4poX?oh#ABP%1b`shutN?=EQUe z9`Bos^g<&3j#YlJ$g$rtfvaI7lOn-YMhLen`$0k@mk_D1hNEa5Im1>6=3!QY*Ww80<}d`nfQ|sCg7qDwBh28qbed%F?myBvM5F z()J`U{j8|ODVPe&##)`??he5Z5~V}$(XzTj51=1sucsc&o*JPW$0!<0R6`HeMJ<%N zG!bqyanJ~&MWi8EtDu%#9`P=NvCxC>ja(0!=7m&M0wvs^r5y9#iXLZQ7$g-8&kgM+ zp&W)1rC8v1P)iy!ZVGXqiX>TJ%)zDt(}6I6IsyDrl9aq^y2IC))C!#Ly=Z|gwgI3V zg8;6v>4Q7mh-4G`DUwiFnAGUva-LbKtbXFGns3!X7=_TF^>6w2Olyvx=#~J<7wvaa zNZ?JFQB6}jssk)}ab_yUQVduyeDcK|&suGpWb?iSBn{eS-Duc*LQ$h#!UZSggb1)@ zZfC}x#YjR|AsT?B)Xr{f+`dFF6wTb^2Los#;gdMw3T&W1D6lI5XAGV>GI6D-@ z{I)H{q@{JYkZY*8IC7dF5`rMSxarGB3@r^baPy0NoA029n;&D!O3SJHoF^t6qK`9a@#)1x^(ggY+i4`@n z;Q_uROlZYFXNW=1ROUhp4Ug{M-9kIThLriEADv8c7_D_U=lExp^U{Lq6NOkq4GFL; zJh{0D(WS~1z)hepn!0Afn4GVsH-pG&EZWu(OeTCyJ=CccrszmDL>tnezemY_P_4EM z7X+7Uw5SS#|1xNUD(xI9?OQT%;~}or)3K@2e41dJbcDnVSR!mV^J3oEBVrUrk64u| zg%un-aNLJRw-7xr6%_^Kb!rf|tzy*jE}SWO`Pi<2a$kA!wmc!s;Hs-;LkS10`Q#jE z20vU69NDe1N))WMgk6Z2!s~iTr0VR1s$lH2Dxye&tx~BsfgwEd zJSn*8KLDQ;p<>5}sNFQJ8?1knEN5~YeQ-^bXoUt~7P@oqt2s}KY0$d1vMN$?1Usr? ze_79Fz3_lGd`ZsyoFB`M3$~8bt}SDz4IAeTRn!{aBcvna=yf+Plg+BJ&L#~gmCYX> z=2lj#rbj4fg4&M@k@A=rW{X=auv+R-N%&=Y`&kuTsdwbYezBTRa=xYIyo1gVy>+8y zClj1*6Bo5i@+rHN5W5ClXs#2IJC0iZb2HuMxo>=&7`ujJ?r{TDaKR~x_WX%j)LI5}<@gCP9P|c8a3_+wIlWV+{Qj$r0Lt^FwtXN}cvs}YevS;U( znSEDr5Oo)8*@f}C6!|(?0N06hC;VZ2)1;AyR{yI;qeqKvD*}Wjm*mtP*Ah?X?64X0 zHOHB@QCl9T2ML!4Pz%8UTE4HaN|Blr?hk7MU3=J}eDiSk&&l)^MzkL53otbypl%vA zLD?KxhvHD{pgoR2lw%a65B9ffgb^OHU{LIe5+mSrRzk_1BP8G#g#VN-R)IOyaXHX0 z@_}$o_LRmkTTTg@4lY1~5cM4sl%`&i)UKxYrY;HIEiJvdfW)bC<+WNu;rtjW$jM`y zS**$<@aDzI_j5nWO{OAZsGZdT2?Zsp;3s_~d4`M`^EW4@pHMvr6Sl6WLQB(h|DI*CqzDFzjuL7h1USKoO)cq>Qot9vPI->$k@72EEsN(X z;t~iVIOjwoG>|tt`1{$)Wstrjs;6+?T_l>R2IlVFtgf8Q9i_3TB#16Z($_SiLtD(o zwdc{95JagFpA_?aW@HckGsw?E_MQpw&2PoEvoB~}X79MX_`W3+ZtVKF+0p{Pv9<2x zP*dxN6f89g;MtYaAPFt4x_IyxT zt&M6?ZJVztq+$8I*A?jb1(egW6W%{6v4&SxH;>gw<`lvA)}@(i9W04_eHf5 z9GufTg@VhM*##N3Y(Y&)e3^&CX7tw@keEY#lpplk+B~h92){Ipnu>dINrAlD!D(%~ zuMY>Gp_c9RypQ-oBeB{kHCT`JU z4rapmct!nVu9}G@xrMxEfC@gML|&$3dW{H55x)>o;0b|NaT#TFtZFeGoJ1ThnogvS^(wkyHjB(Lm~InFAv+OZuS&VXlw#d1 z%`Ui+7)fRBlsnu{go90d3MV!Yz{ClpqPY4~QH)JNHj6@?NG}Wuge=WLuW(z zR!I=aA$3r(%ze7)TYRH5Ez_UNVPTdPeA6tO9VN1=vSNyKmyV%)0tL?jv;tg2vl$QQ zV=$+XYg=6~>@$?#3ezw+urX&IO1x^)ltS_?QcJyVQCs%=-J>TSGOk`#sna z|DEbSAg`6Av)m*cNehNi2P_5BtQT~Kt9q+{_UGQ81KmJE60S;8v%U~Z&h(vV;el*v zEAB*wXmg8DD(`0`&346;pBa8)y2uleW=mS|w@<^o~PzycrxQaUVWLiZEZ?M-)6 zpli~QAT3VYp4d$#Ete2dcO_6R(uf~0kwQ@mepTnjJc)j-;9?!-nhq8&t=_AkfLuEv zSJ3`AV_J%-*7Yw79LWA%FnVI<44Sx*5J|$+di`%dMLWoDyZrSa%EIPxMtB+8h5GmO z6do$?sD6&X_7Z>ry>tCeGG+V0WHcxx#dnc!A!~6)%>;ugWfUc!Mqe*3;p=Ieb7sA6 zsU3lkw3S$7-@lm(cV*SxG61)b3-g&wGKn=drd+>oDe&{;Xoo18MkurrkcF$zX8dr8 z=?JoB@HixgNu~G}-g{XIDFj_ltJjz2S*UH@Jx{AvgYD~S)O^#CNyK1{3F*6` zZ3%tQ3j;;b{DfhH9 zO@yLyhpGj08fQz9-6@y6sFu!!>l(vcFtoOa*%A}GhS$j8SQ+o+_8HY2&bxMEw#RUU zGrZP}FJJHqjlFl}&12Rg_R;+A5RM6!d3@hdU88KEtvh3{TrXD|A~c1*<)zr`$%g&yHxv~cShSX zH$d2E>d~pcm3Qe@CJ-4CH?+s2J}qA02tIDTY6hj>tn-#s8lq+R5OP6M9MRPu@Z1(` zb8=PhGHRzs1#@p^2?gFvq|N#X3&JCTQSRi1^ZhsSLRn&oF`$&k5@jp;;nmlv<6!VA z5LOm(8!G$MHoENKoC3B2~CXRG5=|qVifMd15LHebbua$0_hIYjO!P#XqpX$kjpQ z;5}T|Y~4+k?{m*U9bQvNnb1NWUzv$+%5C1^6xbyb+ArfB#fIz&LF;3#8YTZA6w!-j zP-+|i>g{=x>+9?k5>v5HGiRr(*0ioCy~~6wvMszzvcx&>I1?XS4pAP`QTgb4rju4Z zJ@IRbog8<4Uy$p72QdMiGMFYQ_>1otP5b|_?NCoAznkq_srCMYwqv{8`TS!l@O95x ztD{1qVc^CN=K8uF+t&mAJ1wf&iUn~YJ6qNTa9X(0n!&p^Yq8YXWlX9f&!<#1SszQ; zbT#X670b9c(XWg=+);Ls)Wq~No6VTl{Ltv@CB9xRoIDE22&t4oZ1G-|>U71Wiv)-a z+$Ss<4D4w{v+4}F)xZ%msI%^AUI&Q6@X|N}mIaLjv9ptuPbHxuI>0H8RE)SgUijQZ7uPcTIIIkFURdGae$p3NqZ@d z21HoY5a}qzK`0FV_9q=_ovCH)80e@rT&ak%SPFx%T*JxjQ`G^(BIP(BiFjy@tj=HP zbD|um8W(YWqQxLY67C3Y&G^=be`P`ss3lIP7GC{AJUOo@&3S~Iz|f|H9N#fqG|93& zQ0Rzrs&$dlLmzZSodLnbUrR;ubSm4*p2dSYO?eWH1-ROGc-TKWf$5xruRfxu()!rB*>yH|xX7 zKL8A+JK2tjVS2bk#O!?Huz7;6dPFnM3_Vtfe8kkMPA2Vu^)^RVlpX)qk)BJfo(P(C z>Zt^wzNyr$u+Ts(OKl+pDE}0o+<~AVi#L+z45xCNoRinz^5=YSR z$(+3}y>5sykr=GFSo@p4v<&EIep1&cISuE(-;?tqgJ3ssU=l4=jl6$r2%T}9`&MK% ze#Nv>TbC_zcl#Av0SjP1%g|ppgpR41Yubq5yI*S=yQ#V65UsrRXUULaX+%X9IuqS| za*gHSG-CzY{~?1U;|VnFVyPvf0AFte&uoh;jmuxSzX$FA$Fr=~ZD;U#A>j)hq$X*=K!&D_3E4-u2NI%#fJ4Wo4M30^KGB}Dioal-EW z3ab0@DBK_63YMG%E29$R7%c68XO*mq#16&8rEvNl$FjbYQ5IUYhF$+|(&(@JD{x*s z&S~-=l1E3330NfSLa)AZ%V+2+3G6WqYOD+o4@j#%oq6Yn5V1uz$2h5UGopK?X^SBN zsvq&IANXUcLIBpBvg&y>oooqDLhnsa�bbpq8ug@Yum3fdgk^zX3Vn)?!TxYM{Sz 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 delta 52088 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

Yqk&> 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 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" />