diff --git a/clients/go/zms/model.go b/clients/go/zms/model.go index d4aeedd2d73..5e11527b13b 100644 --- a/clients/go/zms/model.go +++ b/clients/go/zms/model.go @@ -1466,6 +1466,11 @@ type RoleMeta struct { // ownership information for the role (read-only attribute) // ResourceOwnership *ResourceRoleOwnership `json:"resourceOwnership,omitempty" rdl:"optional" yaml:",omitempty"` + + // + // membership filtered based on configured principal domains + // + PrincipalDomainFilter string `json:"principalDomainFilter" rdl:"optional" yaml:",omitempty"` } // NewRoleMeta - creates an initialized RoleMeta instance, returns a pointer to it @@ -1525,6 +1530,12 @@ func (self *RoleMeta) Validate() error { return fmt.Errorf("RoleMeta.description does not contain a valid String (%v)", val.Error) } } + if self.PrincipalDomainFilter != "" { + val := rdl.Validate(ZMSSchema(), "String", self.PrincipalDomainFilter) + if !val.Valid { + return fmt.Errorf("RoleMeta.principalDomainFilter does not contain a valid String (%v)", val.Error) + } + } return nil } @@ -1658,6 +1669,11 @@ type Role struct { // ResourceOwnership *ResourceRoleOwnership `json:"resourceOwnership,omitempty" rdl:"optional" yaml:",omitempty"` + // + // membership filtered based on configured principal domains + // + PrincipalDomainFilter string `json:"principalDomainFilter" rdl:"optional" yaml:",omitempty"` + // // name of the role // @@ -1747,6 +1763,12 @@ func (self *Role) Validate() error { return fmt.Errorf("Role.description does not contain a valid String (%v)", val.Error) } } + if self.PrincipalDomainFilter != "" { + val := rdl.Validate(ZMSSchema(), "String", self.PrincipalDomainFilter) + if !val.Valid { + return fmt.Errorf("Role.principalDomainFilter does not contain a valid String (%v)", val.Error) + } + } if self.Name == "" { return fmt.Errorf("Role.name is missing but is a required field") } else { @@ -5905,6 +5927,11 @@ type GroupMeta struct { // ownership information for the group (read-only attribute) // ResourceOwnership *ResourceGroupOwnership `json:"resourceOwnership,omitempty" rdl:"optional" yaml:",omitempty"` + + // + // membership filtered based on configured principal domains + // + PrincipalDomainFilter string `json:"principalDomainFilter" rdl:"optional" yaml:",omitempty"` } // NewGroupMeta - creates an initialized GroupMeta instance, returns a pointer to it @@ -5952,6 +5979,12 @@ func (self *GroupMeta) Validate() error { return fmt.Errorf("GroupMeta.userAuthorityExpiration does not contain a valid String (%v)", val.Error) } } + if self.PrincipalDomainFilter != "" { + val := rdl.Validate(ZMSSchema(), "String", self.PrincipalDomainFilter) + if !val.Valid { + return fmt.Errorf("GroupMeta.principalDomainFilter does not contain a valid String (%v)", val.Error) + } + } return nil } @@ -6039,6 +6072,11 @@ type Group struct { // ResourceOwnership *ResourceGroupOwnership `json:"resourceOwnership,omitempty" rdl:"optional" yaml:",omitempty"` + // + // membership filtered based on configured principal domains + // + PrincipalDomainFilter string `json:"principalDomainFilter" rdl:"optional" yaml:",omitempty"` + // // name of the group // @@ -6105,6 +6143,12 @@ func (self *Group) Validate() error { return fmt.Errorf("Group.userAuthorityExpiration does not contain a valid String (%v)", val.Error) } } + if self.PrincipalDomainFilter != "" { + val := rdl.Validate(ZMSSchema(), "String", self.PrincipalDomainFilter) + if !val.Valid { + return fmt.Errorf("Group.principalDomainFilter does not contain a valid String (%v)", val.Error) + } + } if self.Name == "" { return fmt.Errorf("Group.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 a0431166b81..d1f77e928c7 100644 --- a/clients/go/zms/zms_schema.go +++ b/clients/go/zms/zms_schema.go @@ -270,6 +270,7 @@ func init() { tRoleMeta.Field("selfRenewMins", "Int32", true, nil, "Number of minutes members can renew their membership if self review option is enabled") tRoleMeta.Field("maxMembers", "Int32", true, nil, "Maximum number of members allowed in the group") tRoleMeta.Field("resourceOwnership", "ResourceRoleOwnership", true, nil, "ownership information for the role (read-only attribute)") + tRoleMeta.Field("principalDomainFilter", "String", true, nil, "membership filtered based on configured principal domains") sb.AddType(tRoleMeta.Build()) tRole := rdl.NewStructTypeBuilder("RoleMeta", "Role") @@ -638,6 +639,7 @@ func init() { tGroupMeta.Field("selfRenewMins", "Int32", true, nil, "Number of minutes members can renew their membership if self review option is enabled") tGroupMeta.Field("maxMembers", "Int32", true, nil, "Maximum number of members allowed in the group") tGroupMeta.Field("resourceOwnership", "ResourceGroupOwnership", true, nil, "ownership information for the group (read-only attribute)") + tGroupMeta.Field("principalDomainFilter", "String", true, nil, "membership filtered based on configured principal domains") sb.AddType(tGroupMeta.Build()) tGroup := rdl.NewStructTypeBuilder("GroupMeta", "Group") diff --git a/core/zms/src/main/java/com/yahoo/athenz/zms/Group.java b/core/zms/src/main/java/com/yahoo/athenz/zms/Group.java index 817dcb39e24..33ec9b2031e 100644 --- a/core/zms/src/main/java/com/yahoo/athenz/zms/Group.java +++ b/core/zms/src/main/java/com/yahoo/athenz/zms/Group.java @@ -59,6 +59,9 @@ public class Group { @RdlOptional @JsonInclude(JsonInclude.Include.NON_EMPTY) public ResourceGroupOwnership resourceOwnership; + @RdlOptional + @JsonInclude(JsonInclude.Include.NON_NULL) + public String principalDomainFilter; public String name; @RdlOptional @JsonInclude(JsonInclude.Include.NON_EMPTY) @@ -175,6 +178,13 @@ public Group setResourceOwnership(ResourceGroupOwnership resourceOwnership) { public ResourceGroupOwnership getResourceOwnership() { return resourceOwnership; } + public Group setPrincipalDomainFilter(String principalDomainFilter) { + this.principalDomainFilter = principalDomainFilter; + return this; + } + public String getPrincipalDomainFilter() { + return principalDomainFilter; + } public Group setName(String name) { this.name = name; return this; @@ -256,6 +266,9 @@ public boolean equals(Object another) { if (resourceOwnership == null ? a.resourceOwnership != null : !resourceOwnership.equals(a.resourceOwnership)) { return false; } + if (principalDomainFilter == null ? a.principalDomainFilter != null : !principalDomainFilter.equals(a.principalDomainFilter)) { + 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/GroupMeta.java b/core/zms/src/main/java/com/yahoo/athenz/zms/GroupMeta.java index 10790807549..14c9f2c8c5d 100644 --- a/core/zms/src/main/java/com/yahoo/athenz/zms/GroupMeta.java +++ b/core/zms/src/main/java/com/yahoo/athenz/zms/GroupMeta.java @@ -59,6 +59,9 @@ public class GroupMeta { @RdlOptional @JsonInclude(JsonInclude.Include.NON_EMPTY) public ResourceGroupOwnership resourceOwnership; + @RdlOptional + @JsonInclude(JsonInclude.Include.NON_NULL) + public String principalDomainFilter; public GroupMeta setSelfServe(Boolean selfServe) { this.selfServe = selfServe; @@ -165,6 +168,13 @@ public GroupMeta setResourceOwnership(ResourceGroupOwnership resourceOwnership) public ResourceGroupOwnership getResourceOwnership() { return resourceOwnership; } + public GroupMeta setPrincipalDomainFilter(String principalDomainFilter) { + this.principalDomainFilter = principalDomainFilter; + return this; + } + public String getPrincipalDomainFilter() { + return principalDomainFilter; + } @Override public boolean equals(Object another) { @@ -218,6 +228,9 @@ public boolean equals(Object another) { if (resourceOwnership == null ? a.resourceOwnership != null : !resourceOwnership.equals(a.resourceOwnership)) { return false; } + if (principalDomainFilter == null ? a.principalDomainFilter != null : !principalDomainFilter.equals(a.principalDomainFilter)) { + return false; + } } return true; } diff --git a/core/zms/src/main/java/com/yahoo/athenz/zms/Role.java b/core/zms/src/main/java/com/yahoo/athenz/zms/Role.java index 55025b29fd9..bcba698423d 100644 --- a/core/zms/src/main/java/com/yahoo/athenz/zms/Role.java +++ b/core/zms/src/main/java/com/yahoo/athenz/zms/Role.java @@ -89,6 +89,9 @@ public class Role { @RdlOptional @JsonInclude(JsonInclude.Include.NON_EMPTY) public ResourceRoleOwnership resourceOwnership; + @RdlOptional + @JsonInclude(JsonInclude.Include.NON_NULL) + public String principalDomainFilter; public String name; @RdlOptional @JsonInclude(JsonInclude.Include.NON_EMPTY) @@ -267,6 +270,13 @@ public Role setResourceOwnership(ResourceRoleOwnership resourceOwnership) { public ResourceRoleOwnership getResourceOwnership() { return resourceOwnership; } + public Role setPrincipalDomainFilter(String principalDomainFilter) { + this.principalDomainFilter = principalDomainFilter; + return this; + } + public String getPrincipalDomainFilter() { + return principalDomainFilter; + } public Role setName(String name) { this.name = name; return this; @@ -386,6 +396,9 @@ public boolean equals(Object another) { if (resourceOwnership == null ? a.resourceOwnership != null : !resourceOwnership.equals(a.resourceOwnership)) { return false; } + if (principalDomainFilter == null ? a.principalDomainFilter != null : !principalDomainFilter.equals(a.principalDomainFilter)) { + 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/RoleMeta.java b/core/zms/src/main/java/com/yahoo/athenz/zms/RoleMeta.java index 380ed5da9f2..7b753d4afab 100644 --- a/core/zms/src/main/java/com/yahoo/athenz/zms/RoleMeta.java +++ b/core/zms/src/main/java/com/yahoo/athenz/zms/RoleMeta.java @@ -83,6 +83,9 @@ public class RoleMeta { @RdlOptional @JsonInclude(JsonInclude.Include.NON_EMPTY) public ResourceRoleOwnership resourceOwnership; + @RdlOptional + @JsonInclude(JsonInclude.Include.NON_NULL) + public String principalDomainFilter; public RoleMeta setSelfServe(Boolean selfServe) { this.selfServe = selfServe; @@ -245,6 +248,13 @@ public RoleMeta setResourceOwnership(ResourceRoleOwnership resourceOwnership) { public ResourceRoleOwnership getResourceOwnership() { return resourceOwnership; } + public RoleMeta setPrincipalDomainFilter(String principalDomainFilter) { + this.principalDomainFilter = principalDomainFilter; + return this; + } + public String getPrincipalDomainFilter() { + return principalDomainFilter; + } @Override public boolean equals(Object another) { @@ -322,6 +332,9 @@ public boolean equals(Object another) { if (resourceOwnership == null ? a.resourceOwnership != null : !resourceOwnership.equals(a.resourceOwnership)) { return false; } + if (principalDomainFilter == null ? a.principalDomainFilter != null : !principalDomainFilter.equals(a.principalDomainFilter)) { + return false; + } } return true; } 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 a7cd85af099..a9300d9f520 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 @@ -234,7 +234,8 @@ private static Schema build() { .field("selfRenew", "Bool", true, "Flag indicates whether to allow expired members to renew their membership") .field("selfRenewMins", "Int32", true, "Number of minutes members can renew their membership if self review option is enabled") .field("maxMembers", "Int32", true, "Maximum number of members allowed in the group") - .field("resourceOwnership", "ResourceRoleOwnership", true, "ownership information for the role (read-only attribute)"); + .field("resourceOwnership", "ResourceRoleOwnership", true, "ownership information for the role (read-only attribute)") + .field("principalDomainFilter", "String", true, "membership filtered based on configured principal domains"); sb.structType("Role", "RoleMeta") .comment("The representation for a Role with set of members. The members (Array) field is deprecated and not used in role objects since it incorrectly lists all the members in the role without taking into account if the member is expired or possibly disabled. Thus, using this attribute will result in incorrect authorization checks by the client and, thus, it's no longer being populated. All applications must use the roleMembers field and take into account all the attributes of the member.") @@ -553,7 +554,8 @@ private static Schema build() { .field("selfRenew", "Bool", true, "Flag indicates whether to allow expired members to renew their membership") .field("selfRenewMins", "Int32", true, "Number of minutes members can renew their membership if self review option is enabled") .field("maxMembers", "Int32", true, "Maximum number of members allowed in the group") - .field("resourceOwnership", "ResourceGroupOwnership", true, "ownership information for the group (read-only attribute)"); + .field("resourceOwnership", "ResourceGroupOwnership", true, "ownership information for the group (read-only attribute)") + .field("principalDomainFilter", "String", true, "membership filtered based on configured principal domains"); sb.structType("Group", "GroupMeta") .comment("The representation for a Group with set of members.") diff --git a/core/zms/src/main/rdl/Group.tdl b/core/zms/src/main/rdl/Group.tdl index fa0ebeacd9c..994c0ad7f1f 100644 --- a/core/zms/src/main/rdl/Group.tdl +++ b/core/zms/src/main/rdl/Group.tdl @@ -67,6 +67,7 @@ type GroupMeta Struct { Int32 selfRenewMins (optional); //Number of minutes members can renew their membership if self review option is enabled Int32 maxMembers (optional); //Maximum number of members allowed in the group ResourceGroupOwnership resourceOwnership (optional); //ownership information for the group (read-only attribute) + String principalDomainFilter (optional, x_allowempty="true"); //membership filtered based on configured principal domains } //The representation for a Group with set of members. diff --git a/core/zms/src/main/rdl/Role.tdl b/core/zms/src/main/rdl/Role.tdl index 3d2a735e9ea..d0191a7a4ba 100644 --- a/core/zms/src/main/rdl/Role.tdl +++ b/core/zms/src/main/rdl/Role.tdl @@ -66,6 +66,7 @@ type RoleMeta Struct { Int32 selfRenewMins (optional); //Number of minutes members can renew their membership if self review option is enabled Int32 maxMembers (optional); //Maximum number of members allowed in the group ResourceRoleOwnership resourceOwnership (optional); //ownership information for the role (read-only attribute) + String principalDomainFilter (optional, x_allowempty="true"); //membership filtered based on configured principal domains } //The representation for a Role with set of members. diff --git a/core/zms/src/test/java/com/yahoo/athenz/zms/GroupTest.java b/core/zms/src/test/java/com/yahoo/athenz/zms/GroupTest.java index bb4b8033d37..10fea7ae9b2 100644 --- a/core/zms/src/test/java/com/yahoo/athenz/zms/GroupTest.java +++ b/core/zms/src/test/java/com/yahoo/athenz/zms/GroupTest.java @@ -62,7 +62,8 @@ public void testGroupsMethod() { .setSelfRenew(true) .setSelfRenewMins(180) .setMaxMembers(5) - .setResourceOwnership(new ResourceGroupOwnership().setMetaOwner("TF")); + .setResourceOwnership(new ResourceGroupOwnership().setMetaOwner("TF")) + .setPrincipalDomainFilter("user,+unix.test,-home"); Group r2 = new Group() .setName("sys.auth:group.admin") @@ -83,7 +84,8 @@ public void testGroupsMethod() { .setSelfRenew(true) .setSelfRenewMins(180) .setMaxMembers(5) - .setResourceOwnership(new ResourceGroupOwnership().setMetaOwner("TF")); + .setResourceOwnership(new ResourceGroupOwnership().setMetaOwner("TF")) + .setPrincipalDomainFilter("user,+unix.test,-home"); assertEquals(r, r2); assertEquals(r, r); @@ -107,6 +109,7 @@ public void testGroupsMethod() { assertEquals(r.getSelfRenew(), Boolean.TRUE); assertEquals(r.getMaxMembers(), 5); assertEquals(r.getResourceOwnership(), new ResourceGroupOwnership().setMetaOwner("TF")); + assertEquals(r.getPrincipalDomainFilter(), "user,+unix.test,-home"); r2.setLastReviewedDate(Timestamp.fromMillis(123456789124L)); assertNotEquals(r, r2); @@ -206,6 +209,13 @@ public void testGroupsMethod() { r2.setResourceOwnership(new ResourceGroupOwnership().setMetaOwner("TF")); assertEquals(r, r2); + r2.setPrincipalDomainFilter("user"); + assertNotEquals(r, r2); + r2.setPrincipalDomainFilter(null); + assertNotEquals(r, r2); + r2.setPrincipalDomainFilter("user,+unix.test,-home"); + assertEquals(r, r2); + r2.setAuditLog(null); assertNotEquals(r, r2); r2.setGroupMembers(null); @@ -539,7 +549,8 @@ public void testGroupMetaMethod() { .setSelfRenew(true) .setSelfRenewMins(180) .setMaxMembers(5) - .setResourceOwnership(new ResourceGroupOwnership().setMetaOwner("TF")); + .setResourceOwnership(new ResourceGroupOwnership().setMetaOwner("TF")) + .setPrincipalDomainFilter("user,+unix.test,-home"); assertFalse(gm1.getSelfServe()); assertEquals(gm1.getNotifyRoles(), "role1,domain:role.role2"); @@ -556,6 +567,7 @@ public void testGroupMetaMethod() { assertEquals(gm1.getSelfRenew(), Boolean.TRUE); assertEquals(gm1.getMaxMembers(), 5); assertEquals(gm1.getResourceOwnership(), new ResourceGroupOwnership().setMetaOwner("TF")); + assertEquals(gm1.getPrincipalDomainFilter(), "user,+unix.test,-home"); GroupMeta gm2 = new GroupMeta() .setSelfServe(false) @@ -572,7 +584,8 @@ public void testGroupMetaMethod() { .setSelfRenew(true) .setSelfRenewMins(180) .setMaxMembers(5) - .setResourceOwnership(new ResourceGroupOwnership().setMetaOwner("TF")); + .setResourceOwnership(new ResourceGroupOwnership().setMetaOwner("TF")) + .setPrincipalDomainFilter("user,+unix.test,-home"); assertEquals(gm1, gm2); assertEquals(gm1, gm1); @@ -684,6 +697,13 @@ public void testGroupMetaMethod() { gm2.setResourceOwnership(new ResourceGroupOwnership().setMetaOwner("TF")); assertEquals(gm2, gm1); + gm2.setPrincipalDomainFilter("user"); + assertNotEquals(gm2, gm1); + gm2.setPrincipalDomainFilter(null); + assertNotEquals(gm2, gm1); + gm2.setPrincipalDomainFilter("user,+unix.test,-home"); + assertEquals(gm2, gm1); + Schema schema = ZMSSchema.instance(); Validator validator = new Validator(schema); Result result = validator.validate(gm1, "GroupMeta"); diff --git a/core/zms/src/test/java/com/yahoo/athenz/zms/RoleTest.java b/core/zms/src/test/java/com/yahoo/athenz/zms/RoleTest.java index 74ba394cc9f..65e7fb11a8e 100644 --- a/core/zms/src/test/java/com/yahoo/athenz/zms/RoleTest.java +++ b/core/zms/src/test/java/com/yahoo/athenz/zms/RoleTest.java @@ -55,7 +55,8 @@ public void testRoleMetaMethod() { .setSelfRenew(true) .setSelfRenewMins(180) .setMaxMembers(5) - .setResourceOwnership(new ResourceRoleOwnership().setMetaOwner("TF")); + .setResourceOwnership(new ResourceRoleOwnership().setMetaOwner("TF")) + .setPrincipalDomainFilter("user,+unix.test,-home"); assertFalse(rm1.getSelfServe()); assertEquals(rm1.getMemberExpiryDays(), Integer.valueOf(30)); @@ -80,6 +81,7 @@ public void testRoleMetaMethod() { assertEquals(rm1.getSelfRenew(), Boolean.TRUE); assertEquals(rm1.getMaxMembers(), 5); assertEquals(rm1.getResourceOwnership(), new ResourceRoleOwnership().setMetaOwner("TF")); + assertEquals(rm1.getPrincipalDomainFilter(), "user,+unix.test,-home"); RoleMeta rm2 = new RoleMeta() .setMemberExpiryDays(30) @@ -104,7 +106,8 @@ public void testRoleMetaMethod() { .setSelfRenew(true) .setSelfRenewMins(180) .setMaxMembers(5) - .setResourceOwnership(new ResourceRoleOwnership().setMetaOwner("TF")); + .setResourceOwnership(new ResourceRoleOwnership().setMetaOwner("TF")) + .setPrincipalDomainFilter("user,+unix.test,-home"); assertEquals(rm1, rm2); assertEquals(rm1, rm1); @@ -272,6 +275,13 @@ public void testRoleMetaMethod() { rm2.setResourceOwnership(new ResourceRoleOwnership().setMetaOwner("TF")); assertEquals(rm2, rm1); + rm2.setPrincipalDomainFilter("user"); + assertNotEquals(rm2, rm1); + rm2.setPrincipalDomainFilter(null); + assertNotEquals(rm2, rm1); + rm2.setPrincipalDomainFilter("user,+unix.test,-home"); + assertEquals(rm2, rm1); + Schema schema = ZMSSchema.instance(); Validator validator = new Validator(schema); Validator.Result result = validator.validate(rm1, "RoleMeta"); @@ -322,7 +332,8 @@ public void testRolesMethod() { .setSelfRenew(true) .setSelfRenewMins(180) .setMaxMembers(5) - .setResourceOwnership(new ResourceRoleOwnership().setMetaOwner("TF")); + .setResourceOwnership(new ResourceRoleOwnership().setMetaOwner("TF")) + .setPrincipalDomainFilter("user,+unix.test,-home"); assertEquals(r.getName(), "sys.auth:role.admin"); assertEquals(r.getModified(), Timestamp.fromMillis(123456789123L)); @@ -353,6 +364,7 @@ public void testRolesMethod() { assertEquals(r.getSelfRenewMins(), 180); assertEquals(r.getMaxMembers(), 5); assertEquals(r.getResourceOwnership(), new ResourceRoleOwnership().setMetaOwner("TF")); + assertEquals(r.getPrincipalDomainFilter(), "user,+unix.test,-home"); Role r2 = new Role() .setName("sys.auth:role.admin") @@ -383,7 +395,8 @@ public void testRolesMethod() { .setSelfRenew(true) .setSelfRenewMins(180) .setMaxMembers(5) - .setResourceOwnership(new ResourceRoleOwnership().setMetaOwner("TF")); + .setResourceOwnership(new ResourceRoleOwnership().setMetaOwner("TF")) + .setPrincipalDomainFilter("user,+unix.test,-home"); assertEquals(r, r2); assertEquals(r, r); @@ -542,6 +555,13 @@ public void testRolesMethod() { r2.setResourceOwnership(new ResourceRoleOwnership().setMetaOwner("TF")); assertEquals(r2, r); + r2.setPrincipalDomainFilter("user"); + assertNotEquals(r2, r); + r2.setPrincipalDomainFilter(null); + assertNotEquals(r2, r); + r2.setPrincipalDomainFilter("user,+unix.test,-home"); + assertEquals(r2, r); + r2.setAuditLog(null); assertNotEquals(r, r2); r2.setTrust(null); diff --git a/libs/go/zmscli/cli.go b/libs/go/zmscli/cli.go index 91b355b6996..59e83a9a641 100644 --- a/libs/go/zmscli/cli.go +++ b/libs/go/zmscli/cli.go @@ -1003,6 +1003,10 @@ func (cli Zms) EvalCommand(params []string) (*string, error) { if argc == 2 { return cli.SetRoleResourceOwnership(dn, args[0], args[1]) } + case "set-role-principal-domain-filter": + if argc == 2 { + return cli.SetRolePrincipalDomainFilter(dn, args[0], args[1]) + } case "set-role-self-renew": if argc == 2 { selfRenew, err := strconv.ParseBool(args[1]) @@ -1165,6 +1169,10 @@ func (cli Zms) EvalCommand(params []string) (*string, error) { if argc == 2 { return cli.SetGroupResourceOwnership(dn, args[0], args[1]) } + case "set-group-principal-domain-filter": + if argc == 2 { + return cli.SetGroupPrincipalDomainFilter(dn, args[0], args[1]) + } case "set-group-self-renew": if argc == 2 { selfRenew, err := strconv.ParseBool(args[1]) @@ -3057,6 +3065,17 @@ func (cli Zms) HelpSpecificCommand(interactive bool, cmd string) string { buf.WriteString(" resource-owner : resource owner in objectowner:{owner},metaowner:{owner},membersowner:{owner} format\n") buf.WriteString(" examples:\n") buf.WriteString(" " + domainExample + " set-role-resource-ownership writers metaowner:TF,membersowner:MSD\n") + case "set-role-principal-domain-filter": + buf.WriteString(" syntax:\n") + buf.WriteString(" " + domainParam + " set-role-principal-domain-filter role principal-domain-filter\n") + buf.WriteString(" parameters:\n") + if !interactive { + buf.WriteString(" domain : name of the domain being updated\n") + } + buf.WriteString(" role : name of the role to be modified\n") + buf.WriteString(" principal-domain-filter : domain filter [+|-]{domainName}[,[+|-]{domainName}]...\n") + buf.WriteString(" examples:\n") + buf.WriteString(" " + domainExample + " set-role-principal-domain-filter writers user,+sports,-sports.dev\n") case "set-role-description": buf.WriteString(" syntax:\n") buf.WriteString(" " + domainParam + " set-role-description role \"description\"\n") @@ -3176,6 +3195,17 @@ func (cli Zms) HelpSpecificCommand(interactive bool, cmd string) string { buf.WriteString(" resource-owner : resource owner in objectowner:{owner},metaowner:{owner},membersowner:{owner} format\n") buf.WriteString(" examples:\n") buf.WriteString(" " + domainExample + " set-group-resource-ownership writers metaowner:TF,membersowner:MSD\n") + case "set-group-principal-domain-filter": + buf.WriteString(" syntax:\n") + buf.WriteString(" " + domainParam + " set-group-principal-domain-filter group principal-domain-filter\n") + buf.WriteString(" parameters:\n") + if !interactive { + buf.WriteString(" domain : name of the domain being updated\n") + } + buf.WriteString(" group : name of the group to be modified\n") + buf.WriteString(" principal-domain-filter : domain filter [+|-]{domainName}[,[+|-]{domainName}]...\n") + buf.WriteString(" examples:\n") + buf.WriteString(" " + domainExample + " set-group-principal-domain-filter writers user,+sports,-sports.dev\n") case "set-group-audit-enabled": buf.WriteString(" syntax:\n") buf.WriteString(" " + domainParam + " set-group-audit-enabled group audit-enabled\n") @@ -3614,6 +3644,7 @@ func (cli Zms) HelpListCommand() string { buf.WriteString(" set-role-user-authority-expiration regular_role attribute\n") buf.WriteString(" set-role-description regular_role description\n") buf.WriteString(" set-role-resource-ownership regular_role resource-owner\n") + buf.WriteString(" set-role-principal-domain-filter regular_role domain-filter\n") buf.WriteString(" add-role-tag regular_role tag_key tag_value [tag_value ...]\n") buf.WriteString(" delete-role-tag regular_role tag_key [tag_value]\n") buf.WriteString(" put-membership-decision regular_role user_or_service [expiration] decision\n") @@ -3644,6 +3675,7 @@ func (cli Zms) HelpListCommand() string { buf.WriteString(" set-group-user-authority-filter group attribute[,attribute...]\n") buf.WriteString(" set-group-user-authority-expiration group attribute\n") buf.WriteString(" set-group-resource-ownership group resource-owner\n") + buf.WriteString(" set-group-principal-domain-filter group domain-filter\n") buf.WriteString(" add-group-tag group tag_key tag_value [tag_value ...]\n") buf.WriteString(" delete-group-tag group tag_key [tag_value]\n") buf.WriteString(" put-group-membership-decision group user_or_service [expiration] decision\n") diff --git a/libs/go/zmscli/group.go b/libs/go/zmscli/group.go index 3628b21a86e..42f64589ea0 100644 --- a/libs/go/zmscli/group.go +++ b/libs/go/zmscli/group.go @@ -381,6 +381,7 @@ func getGroupMetaObject(group *zms.Group) zms.GroupMeta { MaxMembers: group.MaxMembers, SelfRenew: group.SelfRenew, SelfRenewMins: group.SelfRenewMins, + PrincipalDomainFilter: group.PrincipalDomainFilter, } } @@ -677,3 +678,24 @@ func (cli Zms) SetGroupResourceOwnership(dn, gn, resourceOwner string) (*string, return cli.dumpByFormat(message, cli.buildYAMLOutput) } + +func (cli Zms) SetGroupPrincipalDomainFilter(dn, gn, domainFilter string) (*string, error) { + group, err := cli.Zms.GetGroup(zms.DomainName(dn), zms.EntityName(gn), nil, nil) + if err != nil { + return nil, err + } + meta := getGroupMetaObject(group) + meta.PrincipalDomainFilter = domainFilter + + err = cli.Zms.PutGroupMeta(zms.DomainName(dn), zms.EntityName(gn), cli.AuditRef, cli.ResourceOwner, &meta) + if err != nil { + return nil, err + } + s := "[domain " + dn + " group " + gn + " principal-domain-filter attribute successfully updated]\n" + message := SuccessMessage{ + Status: 200, + Message: s, + } + + return cli.dumpByFormat(message, cli.buildYAMLOutput) +} diff --git a/libs/go/zmscli/role.go b/libs/go/zmscli/role.go index bf796a50bac..5cf22c36a22 100644 --- a/libs/go/zmscli/role.go +++ b/libs/go/zmscli/role.go @@ -478,6 +478,7 @@ func getRoleMetaObject(role *zms.Role) zms.RoleMeta { MaxMembers: role.MaxMembers, SelfRenew: role.SelfRenew, SelfRenewMins: role.SelfRenewMins, + PrincipalDomainFilter: role.PrincipalDomainFilter, } } @@ -1015,3 +1016,24 @@ func (cli Zms) SetRoleResourceOwnership(dn, rn, resourceOwner string) (*string, return cli.dumpByFormat(message, cli.buildYAMLOutput) } + +func (cli Zms) SetRolePrincipalDomainFilter(dn string, rn string, domainFilter string) (*string, error) { + role, err := cli.Zms.GetRole(zms.DomainName(dn), zms.EntityName(rn), nil, nil, nil) + if err != nil { + return nil, err + } + meta := getRoleMetaObject(role) + meta.PrincipalDomainFilter = domainFilter + + err = cli.Zms.PutRoleMeta(zms.DomainName(dn), zms.EntityName(rn), cli.AuditRef, cli.ResourceOwner, &meta) + if err != nil { + return nil, err + } + s := "[domain " + dn + " role " + rn + " principal-domain-filter attribute successfully updated]\n" + message := SuccessMessage{ + Status: 200, + Message: s, + } + + return cli.dumpByFormat(message, cli.buildYAMLOutput) +} diff --git a/servers/zms/pom.xml b/servers/zms/pom.xml index e171fc1a5d3..a94f98dd1cb 100644 --- a/servers/zms/pom.xml +++ b/servers/zms/pom.xml @@ -46,7 +46,7 @@ - 0.9813 + 0.9814 diff --git a/servers/zms/schema/updates/update-20240525.sql b/servers/zms/schema/updates/update-20240525.sql new file mode 100644 index 00000000000..8fe9be5f82a --- /dev/null +++ b/servers/zms/schema/updates/update-20240525.sql @@ -0,0 +1,2 @@ +ALTER TABLE `zms_server`.`role` ADD `principal_domain_filter` VARCHAR(1024) NOT NULL DEFAULT ''; +ALTER TABLE `zms_server`.`principal_group` ADD `principal_domain_filter` VARCHAR(1024) NOT NULL DEFAULT ''; diff --git a/servers/zms/schema/zms_server.mwb b/servers/zms/schema/zms_server.mwb index 1911dcc05ef..14ca71ead9e 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 8b4da38a772..17220b07442 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 --- Thu May 23 10:22:50 2024 +-- Fri May 24 13:53:41 2024 -- Model: New Model Version: 1.0 -- MySQL Workbench Forward Engineering @@ -98,6 +98,7 @@ CREATE TABLE IF NOT EXISTS `zms_server`.`role` ( `self_renew` TINYINT(1) NOT NULL DEFAULT 0, `self_renew_mins` INT NOT NULL DEFAULT 0, `resource_owner` VARCHAR(256) NOT NULL DEFAULT '', + `principal_domain_filter` VARCHAR(1024) NOT NULL DEFAULT '', PRIMARY KEY (`role_id`), UNIQUE INDEX `uq_domain_role` (`domain_id` ASC, `name` ASC), CONSTRAINT `fk_role_domain` @@ -408,6 +409,7 @@ CREATE TABLE IF NOT EXISTS `zms_server`.`principal_group` ( `self_renew` TINYINT(1) NOT NULL DEFAULT 0, `self_renew_mins` INT NOT NULL DEFAULT 0, `resource_owner` VARCHAR(256) NOT NULL DEFAULT '', + `principal_domain_filter` VARCHAR(1024) NOT NULL DEFAULT '', PRIMARY KEY (`group_id`), UNIQUE INDEX `uq_domain_group` (`domain_id` ASC, `name` ASC), CONSTRAINT `fk_group_domain` 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 7f4e9d1ecbc..a16c4697051 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 @@ -6162,7 +6162,8 @@ void auditLogRoleMeta(StringBuilder auditDetails, Role role, String roleName, bo .append("\", \"lastReviewedDate\": \"").append(role.getLastReviewedDate()) .append("\", \"maxMembers\": \"").append(role.getMembers()) .append("\", \"selfRenew\": \"").append(role.getSelfRenew()) - .append("\", \"selfRenewMins\": \"").append(role.getSelfRenewMins()).append("\""); + .append("\", \"selfRenewMins\": \"").append(role.getSelfRenewMins()) + .append("\", \"principalDomainFilter\": \"").append(role.getPrincipalDomainFilter()).append("\""); auditLogTags(auditDetails, role.getTags()); if (close) { auditDetails.append("}"); @@ -6182,7 +6183,8 @@ void auditLogGroupMeta(StringBuilder auditDetails, Group group, final String gro .append("\", \"lastReviewedDate\": \"").append(group.getLastReviewedDate()) .append("\", \"maxMembers\": \"").append(group.getMaxMembers()) .append("\", \"selfRenew\": \"").append(group.getSelfRenew()) - .append("\", \"selfRenewMins\": \"").append(group.getSelfRenewMins()).append("\""); + .append("\", \"selfRenewMins\": \"").append(group.getSelfRenewMins()) + .append("\", \"principalDomainFilter\": \"").append(group.getPrincipalDomainFilter()).append("\""); auditLogTags(auditDetails, group.getTags()); if (close) { auditDetails.append("}"); @@ -6613,6 +6615,9 @@ void updateRoleMetaFields(Role role, RoleMeta meta, final String caller) { if (meta.getSelfRenewMins() != null) { role.setSelfRenewMins(meta.getSelfRenewMins()); } + if (meta.getPrincipalDomainFilter() != null) { + role.setPrincipalDomainFilter(meta.getPrincipalDomainFilter()); + } role.setLastReviewedDate(objectLastReviewDate(meta.getLastReviewedDate(), role.getLastReviewedDate(), false, caller)); } @@ -6658,7 +6663,8 @@ public Role executePutRoleMeta(ResourceContext ctx, String domainName, String ro .setLastReviewedDate(originalRole.getLastReviewedDate()) .setMaxMembers(originalRole.getMaxMembers()) .setSelfRenew(originalRole.getSelfRenew()) - .setSelfRenewMins(originalRole.getSelfRenewMins()); + .setSelfRenewMins(originalRole.getSelfRenewMins()) + .setPrincipalDomainFilter(originalRole.getPrincipalDomainFilter()); // then we're going to apply the updated fields // from the given object @@ -6747,6 +6753,9 @@ void updateGroupMetaFields(Group group, GroupMeta meta, final String caller) { if (meta.getSelfRenewMins() != null) { group.setSelfRenewMins(meta.getSelfRenewMins()); } + if (meta.getPrincipalDomainFilter() != null) { + group.setPrincipalDomainFilter(meta.getPrincipalDomainFilter()); + } group.setLastReviewedDate(objectLastReviewDate(meta.getLastReviewedDate(), group.getLastReviewedDate(), false, caller)); } @@ -6783,7 +6792,8 @@ public Group executePutGroupMeta(ResourceContext ctx, final String domainName, f .setLastReviewedDate(originalGroup.getLastReviewedDate()) .setMaxMembers(originalGroup.getMaxMembers()) .setSelfRenew(originalGroup.getSelfRenew()) - .setSelfRenewMins(originalGroup.getSelfRenewMins()); + .setSelfRenewMins(originalGroup.getSelfRenewMins()) + .setPrincipalDomainFilter(originalGroup.getPrincipalDomainFilter()); // then we're going to apply the updated fields // from the given object 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 a8737222a60..d868ae09346 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 @@ -285,6 +285,7 @@ public final class ZMSConsts { public static final String DB_COLUMN_RESOURCE_OWNER = "resource_owner"; public static final String DB_COLUMN_SYSTEM_SUSPENDED = "system_suspended"; + public static final String DB_COLUMN_PRINCIPAL_DOMAIN_FILTER = "principal_domain_filter"; public static final String DB_COLUMN_SERVICE_REVIEW_DAYS = "service_review_days"; public static final String DB_COLUMN_SERVICE_EXPIRY_DAYS = "service_expiry_days"; public static final String DB_COLUMN_GROUP_EXPIRY_DAYS = "group_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 e70208d9900..a0be8c659ec 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 @@ -58,6 +58,7 @@ import com.yahoo.athenz.zms.provider.ServiceProviderManager; import com.yahoo.athenz.zms.purge.PurgeResourcesEnum; import com.yahoo.athenz.zms.store.*; +import com.yahoo.athenz.zms.utils.PrincipalDomainFilter; import com.yahoo.athenz.zms.utils.ResourceOwnership; import com.yahoo.athenz.zms.utils.ZMSUtils; import com.yahoo.rdl.UUID; @@ -1863,7 +1864,8 @@ public Domain postUserDomain(ResourceContext ctx, String name, String auditRef, // to be the home domain and the admin of the domain is the user final String userDomainAdmin = userDomainPrefix + principal.getName(); - validateRoleMemberPrincipal(userDomainAdmin, Principal.Type.USER.getValue(), null, null, null, true, caller); + validateRoleMemberPrincipal(userDomainAdmin, Principal.Type.USER.getValue(), null, null, + null, null, true, caller); List adminUsers = new ArrayList<>(); adminUsers.add(userDomainAdmin); @@ -2340,8 +2342,28 @@ void validateDomainContacts(Map contacts, final String caller) { } } + void validateDomainFilterValue(final String domainFilter, final String caller) { + + if (!StringUtil.isEmpty(domainFilter)) { + + String[] domainNames = domainFilter.split(","); + for (String domainName : domainNames) { + + // supported format is domainName,+domainName,-domainName + + if (domainName.startsWith("+") || domainName.startsWith("-")) { + domainName = domainName.substring(1); + } + validate(domainName, TYPE_DOMAIN_NAME, caller); + if (dbService.getDomain(domainName, false) == null) { + throw ZMSUtils.requestError("No such domain: " + domainName, caller); + } + } + } + } + void validateString(final String value, final String type, final String caller) { - if (value != null && !value.isEmpty()) { + if (!StringUtil.isEmpty(value)) { validate(value, type, caller); } } @@ -2640,6 +2662,8 @@ void validateRoleMetaValues(RoleMeta meta) { validateString(meta.getNotifyRoles(), TYPE_RESOURCE_NAMES, caller); validateString(meta.getUserAuthorityFilter(), TYPE_AUTHORITY_KEYWORDS, caller); validateString(meta.getUserAuthorityExpiration(), TYPE_AUTHORITY_KEYWORD, caller); + + validateDomainFilterValue(meta.getPrincipalDomainFilter(), caller); } void validateRoleValues(Role role) { @@ -2660,6 +2684,8 @@ void validateRoleValues(Role role) { validateString(role.getNotifyRoles(), TYPE_RESOURCE_NAMES, caller); validateString(role.getUserAuthorityFilter(), TYPE_AUTHORITY_KEYWORDS, caller); validateString(role.getUserAuthorityExpiration(), TYPE_AUTHORITY_KEYWORD, caller); + + validateDomainFilterValue(role.getPrincipalDomainFilter(), caller); } void validateGroupValues(Group group) { @@ -2674,6 +2700,8 @@ void validateGroupValues(Group group) { validateString(group.getNotifyRoles(), TYPE_RESOURCE_NAMES, caller); validateString(group.getUserAuthorityFilter(), TYPE_AUTHORITY_KEYWORDS, caller); validateString(group.getUserAuthorityExpiration(), TYPE_AUTHORITY_KEYWORD, caller); + + validateDomainFilterValue(group.getPrincipalDomainFilter(), caller); } void validateGroupMetaValues(GroupMeta meta) { @@ -2688,6 +2716,8 @@ void validateGroupMetaValues(GroupMeta meta) { validateString(meta.getNotifyRoles(), TYPE_RESOURCE_NAMES, caller); validateString(meta.getUserAuthorityFilter(), TYPE_AUTHORITY_KEYWORDS, caller); validateString(meta.getUserAuthorityExpiration(), TYPE_AUTHORITY_KEYWORD, caller); + + validateDomainFilterValue(meta.getPrincipalDomainFilter(), caller); } @Override @@ -3993,7 +4023,7 @@ List normalizedAdminUsers(List admins, final String domainUserAu for (String admin : normalizedAdmins) { validateRoleMemberPrincipal(admin, principalType(admin), domainUserAuthorityFilter, null, - null, disallowGroupsInAdminRole.get(), caller); + null, null, disallowGroupsInAdminRole.get(), caller); } return new ArrayList<>(normalizedAdmins); @@ -4153,7 +4183,8 @@ public Response putRole(ResourceContext ctx, String domainName, String roleName, // be specified as members. boolean disallowGroups = disallowGroupsInAdminRole.get() == Boolean.TRUE && ADMIN_ROLE_NAME.equals(roleName); - validateRoleMemberPrincipals(role, domain.getUserAuthorityFilter(), disallowGroups, caller); + PrincipalDomainFilter principalDomainFilter = new PrincipalDomainFilter(role.getPrincipalDomainFilter()); + validateRoleMemberPrincipals(role, domain.getUserAuthorityFilter(), principalDomainFilter, disallowGroups, caller); // validate role review-enabled and/or audit-enabled flags @@ -4259,8 +4290,8 @@ void validateRoleStructure(final Role role, final String domainName, final Strin } } - void validateRoleMemberPrincipals(final Role role, final String domainUserAuthorityFilter, boolean disallowGroups, - final String caller) { + void validateRoleMemberPrincipals(final Role role, final String domainUserAuthorityFilter, + PrincipalDomainFilter principalDomainFilter, boolean disallowGroups, final String caller) { // extract the user authority filter for the role @@ -4269,8 +4300,8 @@ void validateRoleMemberPrincipals(final Role role, final String domainUserAuthor for (RoleMember roleMember : role.getRoleMembers()) { validateRoleMemberPrincipal(roleMember.getMemberName(), roleMember.getPrincipalType(), - userAuthorityFilter, role.getUserAuthorityExpiration(), role.getAuditEnabled(), - disallowGroups, caller); + userAuthorityFilter, role.getUserAuthorityExpiration(), principalDomainFilter, + role.getAuditEnabled(), disallowGroups, caller); } } @@ -4394,10 +4425,25 @@ void validateGroupPrincipal(final String memberName, final String userAuthorityF } void validateRoleMemberPrincipal(final String memberName, int principalType, final String userAuthorityFilter, - final String userAuthorityExpiration, Boolean roleAuditEnabled, - boolean disallowGroups, final String caller) { + final String userAuthorityExpiration, PrincipalDomainFilter principalDomainFilter, + Boolean roleAuditEnabled, boolean disallowGroups, final String caller) { + + Principal.Type type = Principal.Type.getType(principalType); + + if (type == Principal.Type.UNKNOWN) { + throw ZMSUtils.requestError("Principal " + memberName + " is not valid", caller); + } - switch (Principal.Type.getType(principalType)) { + // if we have a principal domain filter then we need to make sure + // that the principal is within the allowed domain list + + if (principalDomainFilter != null && !principalDomainFilter.validate(memberName, type)) { + throw ZMSUtils.requestError("Principal " + memberName + " is not allowed for the role", caller); + } + + // now let's carry out further validation based on the principal type + + switch (type) { case USER: case USER_HEADLESS: @@ -4430,23 +4476,30 @@ void validateRoleMemberPrincipal(final String memberName, int principalType, fin validateGroupPrincipal(memberName, userAuthorityFilter, userAuthorityExpiration, roleAuditEnabled, caller); break; - - default: - - throw ZMSUtils.requestError("Principal " + memberName + " is not valid", caller); } } void validateGroupMemberPrincipal(final String memberName, int principalType, final String userAuthorityFilter, - final String caller) { + PrincipalDomainFilter principalDomainFilter, final String caller) { - // we do not support any type of wildcards in group members + Principal.Type type = Principal.Type.getType(principalType); - if (memberName.indexOf('*') != -1) { + // we do not support group members and any type of wildcards in group members + + if (type == Principal.Type.UNKNOWN || type == Principal.Type.GROUP || memberName.indexOf('*') != -1) { throw ZMSUtils.requestError("Principal " + memberName + " is not valid", caller); } - switch (Principal.Type.getType(principalType)) { + // if we have a principal domain filter then we need to make sure + // that the principal is within the allowed domain list + + if (principalDomainFilter != null && !principalDomainFilter.validate(memberName, type)) { + throw ZMSUtils.requestError("Principal " + memberName + " is not allowed for the group", caller); + } + + // now let's carry out further validation based on the principal type + + switch (type) { case USER: case USER_HEADLESS: @@ -4460,10 +4513,6 @@ void validateGroupMemberPrincipal(final String memberName, int principalType, fi validateServicePrincipal(memberName, caller); break; - - default: - - throw ZMSUtils.requestError("Principal " + memberName + " is not valid", caller); } } @@ -4762,8 +4811,10 @@ public Response putMembership(ResourceContext ctx, String domainName, String rol final String userAuthorityFilter = enforcedUserAuthorityFilter(role.getUserAuthorityFilter(), domain.getDomain().getUserAuthorityFilter()); boolean disallowGroups = disallowGroupsInAdminRole.get() == Boolean.TRUE && ADMIN_ROLE_NAME.equals(roleName); + PrincipalDomainFilter principalDomainFilter = new PrincipalDomainFilter(role.getPrincipalDomainFilter()); validateRoleMemberPrincipal(roleMember.getMemberName(), roleMember.getPrincipalType(), userAuthorityFilter, - role.getUserAuthorityExpiration(), role.getAuditEnabled(), disallowGroups, caller); + role.getUserAuthorityExpiration(), principalDomainFilter, role.getAuditEnabled(), + disallowGroups, caller); // authorization check which also automatically updates // the active and approved flags for the request @@ -9846,9 +9897,10 @@ public void putMembershipDecision(ResourceContext ctx, String domainName, String final String userAuthorityFilter = enforcedUserAuthorityFilter(role.getUserAuthorityFilter(), domain.getDomain().getUserAuthorityFilter()); boolean disallowGroups = disallowGroupsInAdminRole.get() == Boolean.TRUE && ADMIN_ROLE_NAME.equals(roleName); + PrincipalDomainFilter principalDomainFilter = new PrincipalDomainFilter(role.getPrincipalDomainFilter()); validateRoleMemberPrincipal(roleMember.getMemberName(), roleMember.getPrincipalType(), - userAuthorityFilter, role.getUserAuthorityExpiration(), role.getAuditEnabled(), - disallowGroups, caller); + userAuthorityFilter, role.getUserAuthorityExpiration(), principalDomainFilter, + role.getAuditEnabled(), disallowGroups, caller); } dbService.executePutMembershipDecision(ctx, domainName, roleName, roleMember, auditRef, caller); @@ -10262,7 +10314,8 @@ public Response putRoleReview(ResourceContext ctx, String domainName, String rol // validate all members specified in the review request - validateRoleMemberPrincipals(role, domain.getDomain().getUserAuthorityFilter(), false, caller); + PrincipalDomainFilter principalDomainFilter = new PrincipalDomainFilter(dbRole.getPrincipalDomainFilter()); + validateRoleMemberPrincipals(role, domain.getDomain().getUserAuthorityFilter(), principalDomainFilter, false, caller); // update role expiry based on our configurations @@ -10464,7 +10517,8 @@ void normalizeGroupMembers(Group group) { group.setGroupMembers(new ArrayList<>(normalizedMembers.values())); } - void validateGroupMemberPrincipals(final Group group, final String domainUserAuthorityFilter, final String caller) { + void validateGroupMemberPrincipals(final Group group, final String domainUserAuthorityFilter, + PrincipalDomainFilter principalDomainFilter, final String caller) { // make sure we have either one of the options enabled for verification @@ -10473,7 +10527,7 @@ void validateGroupMemberPrincipals(final Group group, final String domainUserAut for (GroupMember groupMember : group.getGroupMembers()) { validateGroupMemberPrincipal(groupMember.getMemberName(), groupMember.getPrincipalType(), - userAuthorityFilter, caller); + userAuthorityFilter, principalDomainFilter, caller); } } @@ -10576,7 +10630,8 @@ public Response putGroup(ResourceContext ctx, String domainName, String groupNam // check to see if we need to validate user and service members // and possibly user authority filter restrictions - validateGroupMemberPrincipals(group, domain.getUserAuthorityFilter(), caller); + PrincipalDomainFilter principalDomainFilter = new PrincipalDomainFilter(group.getPrincipalDomainFilter()); + validateGroupMemberPrincipals(group, domain.getUserAuthorityFilter(), principalDomainFilter, caller); // validate group review-enabled and/or audit-enabled flags @@ -10813,7 +10868,7 @@ void setGroupMemberExpiration(final AthenzDomain domain, final Group group, fina } boolean isAllowedPutGroupMembership(Principal principal, final AthenzDomain domain, final Group group, - final GroupMember groupMember) { + final GroupMember groupMember) { // first lets check if the principal has update access on the group @@ -10983,7 +11038,9 @@ public Response putGroupMembership(ResourceContext ctx, String domainName, Strin final String userAuthorityFilter = enforcedUserAuthorityFilter(group.getUserAuthorityFilter(), domain.getDomain().getUserAuthorityFilter()); - validateGroupMemberPrincipal(groupMember.getMemberName(), groupMember.getPrincipalType(), userAuthorityFilter, caller); + PrincipalDomainFilter principalDomainFilter = new PrincipalDomainFilter(group.getPrincipalDomainFilter()); + validateGroupMemberPrincipal(groupMember.getMemberName(), groupMember.getPrincipalType(), userAuthorityFilter, + principalDomainFilter, caller); // authorization check which also automatically updates // the active and approved flags for the request @@ -11333,8 +11390,8 @@ public void putGroupMembershipDecision(ResourceContext ctx, String domainName, S } // initially create the group member and only set the - // user name which is all we need in case we need to - // lookup the pending entry for review approval + // username which is all we need in case we need to + // look up the pending entry for review approval // we'll set the state and expiration after the // authorization check is successful @@ -11363,8 +11420,9 @@ public void putGroupMembershipDecision(ResourceContext ctx, String domainName, S final String userAuthorityFilter = enforcedUserAuthorityFilter(group.getUserAuthorityFilter(), domain.getDomain().getUserAuthorityFilter()); + PrincipalDomainFilter principalDomainFilter = new PrincipalDomainFilter(group.getPrincipalDomainFilter()); validateGroupMemberPrincipal(groupMember.getMemberName(), groupMember.getPrincipalType(), - userAuthorityFilter, caller); + userAuthorityFilter, principalDomainFilter, caller); } dbService.executePutGroupMembershipDecision(ctx, domainName, group, groupMember, auditRef); @@ -11437,7 +11495,8 @@ public Response putGroupReview(ResourceContext ctx, String domainName, String gr // validate all members specified in the review request - validateGroupMemberPrincipals(group, domain.getDomain().getUserAuthorityFilter(), caller); + PrincipalDomainFilter principalDomainFilter = new PrincipalDomainFilter(dbGroup.getPrincipalDomainFilter()); + validateGroupMemberPrincipals(group, domain.getDomain().getUserAuthorityFilter(), principalDomainFilter, caller); // update group expiry based on our configurations 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 151a4f948da..836183bfe55 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 @@ -116,12 +116,14 @@ public class JDBCConnection implements ObjectStoreConnection { + " member_expiry_days, token_expiry_mins, cert_expiry_mins, sign_algorithm, service_expiry_days," + " member_review_days, service_review_days, group_review_days, review_enabled, notify_roles, user_authority_filter," + " user_authority_expiration, description, group_expiry_days, delete_protection, last_reviewed_time," - + " max_members, self_renew, self_renew_mins) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);"; + + " max_members, self_renew, self_renew_mins, principal_domain_filter)" + + " VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);"; private static final String SQL_UPDATE_ROLE = "UPDATE role SET trust=?, audit_enabled=?, self_serve=?, " + "member_expiry_days=?, token_expiry_mins=?, cert_expiry_mins=?, sign_algorithm=?, " + "service_expiry_days=?, member_review_days=?, service_review_days=?, group_review_days=?, review_enabled=?, notify_roles=?, " + "user_authority_filter=?, user_authority_expiration=?, description=?, group_expiry_days=?, " - + "delete_protection=?, last_reviewed_time=?, max_members=?, self_renew=?, self_renew_mins=? WHERE role_id=?;"; + + "delete_protection=?, last_reviewed_time=?, max_members=?, self_renew=?, self_renew_mins=?, " + + "principal_domain_filter=? WHERE role_id=?;"; private static final String SQL_DELETE_ROLE = "DELETE FROM role WHERE domain_id=? AND name=?;"; private static final String SQL_UPDATE_ROLE_MOD_TIMESTAMP = "UPDATE role " + "SET modified=CURRENT_TIMESTAMP(3) WHERE role_id=?;"; @@ -411,12 +413,12 @@ public class JDBCConnection implements ObjectStoreConnection { + "WHERE domain.name=? AND principal_group.name=?;"; private static final String SQL_INSERT_GROUP = "INSERT INTO principal_group (name, domain_id, audit_enabled, self_serve, " + "review_enabled, notify_roles, user_authority_filter, user_authority_expiration, member_expiry_days, " - + "service_expiry_days, delete_protection, last_reviewed_time, max_members, self_renew, self_renew_mins) " - + "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);"; + + "service_expiry_days, delete_protection, last_reviewed_time, max_members, self_renew, self_renew_mins, " + + "principal_domain_filter) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);"; private static final String SQL_UPDATE_GROUP = "UPDATE principal_group SET audit_enabled=?, self_serve=?, " + "review_enabled=?, notify_roles=?, user_authority_filter=?, user_authority_expiration=?, " + "member_expiry_days=?, service_expiry_days=?, delete_protection=?, last_reviewed_time=?, " - + "max_members=?, self_renew=?, self_renew_mins=? WHERE group_id=?;"; + + "max_members=?, self_renew=?, self_renew_mins=?, principal_domain_filter=? WHERE group_id=?;"; private static final String SQL_GET_GROUP_ID = "SELECT group_id FROM principal_group WHERE domain_id=? AND name=?;"; private static final String SQL_DELETE_GROUP = "DELETE FROM principal_group WHERE domain_id=? AND name=?;"; private static final String SQL_UPDATE_GROUP_MOD_TIMESTAMP = "UPDATE principal_group " @@ -2048,6 +2050,7 @@ public boolean insertRole(String domainName, Role role) { ps.setInt(22, processInsertValue(role.getMaxMembers())); ps.setBoolean(23, processInsertValue(role.getSelfRenew(), false)); ps.setInt(24, processInsertValue(role.getSelfRenewMins())); + ps.setString(25, processInsertValue(role.getPrincipalDomainFilter())); affectedRows = executeUpdate(ps, caller); } catch (SQLException ex) { throw sqlError(ex, caller); @@ -2102,7 +2105,8 @@ public boolean updateRole(String domainName, Role role) { ps.setInt(20, processInsertValue(role.getMaxMembers())); ps.setBoolean(21, processInsertValue(role.getSelfRenew(), false)); ps.setInt(22, processInsertValue(role.getSelfRenewMins())); - ps.setInt(23, roleId); + ps.setString(23, processInsertValue(role.getPrincipalDomainFilter())); + ps.setInt(24, roleId); affectedRows = executeUpdate(ps, caller); } catch (SQLException ex) { throw sqlError(ex, caller); @@ -3976,7 +3980,8 @@ Role retrieveRole(ResultSet rs, final String domainName, final String roleName) .setMaxMembers(nullIfDefaultValue(rs.getInt(ZMSConsts.DB_COLUMN_MAX_MEMBERS), 0)) .setSelfRenew(nullIfDefaultValue(rs.getBoolean(ZMSConsts.DB_COLUMN_SELF_RENEW), false)) .setSelfRenewMins(nullIfDefaultValue(rs.getInt(ZMSConsts.DB_COLUMN_SELF_RENEW_MINS), 0)) - .setResourceOwnership(ResourceOwnership.getResourceRoleOwnership(rs.getString(ZMSConsts.DB_COLUMN_RESOURCE_OWNER))); + .setResourceOwnership(ResourceOwnership.getResourceRoleOwnership(rs.getString(ZMSConsts.DB_COLUMN_RESOURCE_OWNER))) + .setPrincipalDomainFilter(saveValue(rs.getString(ZMSConsts.DB_COLUMN_PRINCIPAL_DOMAIN_FILTER))); java.sql.Timestamp lastReviewedTime = rs.getTimestamp(ZMSConsts.DB_COLUMN_LAST_REVIEWED_TIME); if (lastReviewedTime != null) { role.setLastReviewedDate(Timestamp.fromMillis(lastReviewedTime.getTime())); @@ -6026,7 +6031,8 @@ Group retrieveGroup(ResultSet rs, final String domainName, final String groupNam .setMaxMembers(nullIfDefaultValue(rs.getInt(ZMSConsts.DB_COLUMN_MAX_MEMBERS), 0)) .setSelfRenew(nullIfDefaultValue(rs.getBoolean(ZMSConsts.DB_COLUMN_SELF_RENEW), false)) .setSelfRenewMins(nullIfDefaultValue(rs.getInt(ZMSConsts.DB_COLUMN_SELF_RENEW_MINS), 0)) - .setResourceOwnership(ResourceOwnership.getResourceGroupOwnership(rs.getString(ZMSConsts.DB_COLUMN_RESOURCE_OWNER))); + .setResourceOwnership(ResourceOwnership.getResourceGroupOwnership(rs.getString(ZMSConsts.DB_COLUMN_RESOURCE_OWNER))) + .setPrincipalDomainFilter(saveValue(rs.getString(ZMSConsts.DB_COLUMN_PRINCIPAL_DOMAIN_FILTER))); java.sql.Timestamp lastReviewedTime = rs.getTimestamp(ZMSConsts.DB_COLUMN_LAST_REVIEWED_TIME); if (lastReviewedTime != null) { group.setLastReviewedDate(Timestamp.fromMillis(lastReviewedTime.getTime())); @@ -6087,6 +6093,7 @@ public boolean insertGroup(String domainName, Group group) { ps.setInt(13, processInsertValue(group.getMaxMembers())); ps.setBoolean(14, processInsertValue(group.getSelfRenew(), false)); ps.setInt(15, processInsertValue(group.getSelfRenewMins())); + ps.setString(16, processInsertValue(group.getPrincipalDomainFilter())); affectedRows = executeUpdate(ps, caller); } catch (SQLException ex) { throw sqlError(ex, caller); @@ -6131,7 +6138,8 @@ public boolean updateGroup(String domainName, Group group) { ps.setInt(11, processInsertValue(group.getMaxMembers())); ps.setBoolean(12, processInsertValue(group.getSelfRenew(), false)); ps.setInt(13, processInsertValue(group.getSelfRenewMins())); - ps.setInt(14, groupId); + ps.setString(14, processInsertValue(group.getPrincipalDomainFilter())); + ps.setInt(15, groupId); affectedRows = executeUpdate(ps, caller); } catch (SQLException ex) { throw sqlError(ex, caller); diff --git a/servers/zms/src/main/java/com/yahoo/athenz/zms/utils/PrincipalDomainFilter.java b/servers/zms/src/main/java/com/yahoo/athenz/zms/utils/PrincipalDomainFilter.java new file mode 100644 index 00000000000..0416bc2e226 --- /dev/null +++ b/servers/zms/src/main/java/com/yahoo/athenz/zms/utils/PrincipalDomainFilter.java @@ -0,0 +1,139 @@ +/* + * Copyright The Athenz Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.yahoo.athenz.zms.utils; + +import com.yahoo.athenz.auth.AuthorityConsts; +import com.yahoo.athenz.auth.Principal; +import org.eclipse.jetty.util.StringUtil; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class PrincipalDomainFilter { + + Set allowedDomains; + List disallowedSubDomains; + List allowedSubDomains; + + public PrincipalDomainFilter(final String domainFilter) { + + // supported format is domainName,+domainName,-domainName + + if (StringUtil.isEmpty(domainFilter)) { + return; + } + + // for matching with domains and not substrings we're going + // to automatically add a '.' at the end of each domain name + + String[] domainNames = domainFilter.split(","); + for (String domainName : domainNames) { + if (domainName.startsWith("+")) { + if (allowedSubDomains == null) { + allowedSubDomains = new ArrayList<>(); + } + allowedSubDomains.add(domainName.substring(1) + "."); + } else if (domainName.startsWith("-")) { + if (disallowedSubDomains == null) { + disallowedSubDomains = new ArrayList<>(); + } + disallowedSubDomains.add(domainName.substring(1) + "."); + } else { + if (allowedDomains == null) { + allowedDomains = new HashSet<>(); + } + allowedDomains.add(domainName + "."); + } + } + } + + public boolean validate(final String principalName, Principal.Type type) { + + // if we have no filter then we're good + + if (allowedDomains == null && allowedSubDomains == null && disallowedSubDomains == null) { + return true; + } + + // let's first extract our domain name: special handling + // for groups while all other types are standard service names + // since we're given the principal type, it's already been + // verified that the principal name is valid so no need to + // check for error cases + + int idx = getIdx(principalName, type); + final String domainName = principalName.substring(0, idx) + "."; + + // if we have disallowed domains then we need to make sure + // that the principal domain is not in the disallowed list + + if (disallowedSubDomains != null) { + + for (String disallowedDomain : disallowedSubDomains) { + if (domainName.startsWith(disallowedDomain)) { + return false; + } + } + + // at this time we don't have any allowed list specified + // it means all other entries are allowed + + if (allowedDomains == null && allowedSubDomains == null) { + return true; + } + } + + // if we have allowed domains then we need to make sure + // that the principal domain is in the allowed list + + if (allowedDomains != null) { + if (allowedDomains.contains(domainName)) { + return true; + } + + // at this time we don't have any subdomains specified + // it means all other entries are disallowed + + if (allowedSubDomains == null) { + return false; + } + } + + // if we got here it means we have configured allowed subdomains, + // so we need to make sure the principal domain is in the allowed list + + for (String allowedDomain : allowedSubDomains) { + if (domainName.startsWith(allowedDomain)) { + return true; + } + } + + return false; + } + + private int getIdx(String principalName, Principal.Type type) { + int idx; + if (type == Principal.Type.GROUP) { + idx = principalName.indexOf(AuthorityConsts.GROUP_SEP); + } else { + idx = principalName.lastIndexOf('.'); + } + return idx; + } +} diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/DBServiceTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/DBServiceTest.java index 02bdd9b2292..04d7170dea8 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/DBServiceTest.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/DBServiceTest.java @@ -5934,7 +5934,7 @@ public void testUpdateRoleMetaFields() { public void testAuditLogRoleMeta() { StringBuilder auditDetails = new StringBuilder(); Role role = new Role().setName("dom1:role.role1").setSelfServe(true).setReviewEnabled(false) - .setSelfRenew(true).setSelfRenewMins(10); + .setSelfRenew(true).setSelfRenewMins(10).setPrincipalDomainFilter("domain1"); zms.dbService.auditLogRoleMeta(auditDetails, role, "role1", true); assertEquals(auditDetails.toString(), "{\"name\": \"role1\", \"selfServe\": \"true\", \"memberExpiryDays\": \"null\"," @@ -5944,7 +5944,8 @@ public void testAuditLogRoleMeta() { + " \"reviewEnabled\": \"false\", \"notifyRoles\": \"null\", \"signAlgorithm\": \"null\"," + " \"userAuthorityFilter\": \"null\", \"userAuthorityExpiration\": \"null\"," + " \"description\": \"null\", \"deleteProtection\": \"null\", \"lastReviewedDate\": \"null\"," - + " \"maxMembers\": \"null\", \"selfRenew\": \"true\", \"selfRenewMins\": \"10\"}"); + + " \"maxMembers\": \"null\", \"selfRenew\": \"true\", \"selfRenewMins\": \"10\"," + + " \"principalDomainFilter\": \"domain1\"}"); } @Test 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 29e73dec606..3fedfa251d7 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 @@ -2253,7 +2253,7 @@ public void testCreateRole() { + "\"signAlgorithm\": \"null\", \"userAuthorityFilter\": \"null\", " + "\"userAuthorityExpiration\": \"null\", \"description\": \"null\", " + "\"deleteProtection\": \"false\", \"lastReviewedDate\": \"null\", \"maxMembers\": \"null\", " - + "\"selfRenew\": \"null\", \"selfRenewMins\": \"null\", \"trust\": \"null\", " + + "\"selfRenew\": \"null\", \"selfRenewMins\": \"null\", \"principalDomainFilter\": \"null\", \"trust\": \"null\", " + "\"deleted-members\": [{\"member\": \"user.jane\", \"approved\": true, \"system-disabled\": 0}], " + "\"added-members\": []}"); assertTrue(index2 > index, msg); @@ -21301,6 +21301,7 @@ public void testPutRoleMeta() { rm.setMaxMembers(25); rm.setSelfRenew(true); rm.setSelfRenewMins(99); + rm.setPrincipalDomainFilter("user,sys.auth"); zmsImpl.putRoleMeta(ctx, "rolemetadom1", "role1", auditRef, null, rm); Role resRole1 = zmsImpl.getRole(ctx, "rolemetadom1", "role1", true, false, false); @@ -21320,6 +21321,7 @@ public void testPutRoleMeta() { assertTrue(resRole1.getSelfRenew()); assertEquals(resRole1.getMaxMembers(), 25); assertEquals(resRole1.getSelfRenewMins(), 99); + assertEquals(resRole1.getPrincipalDomainFilter(), "user,sys.auth"); // if we pass a null for the expiry days (e.g. old client) // then we're not going to modify the value @@ -21342,6 +21344,7 @@ public void testPutRoleMeta() { assertTrue(resRole1.getSelfRenew()); assertEquals(resRole1.getMaxMembers(), 25); assertEquals(resRole1.getSelfRenewMins(), 99); + assertEquals(resRole1.getPrincipalDomainFilter(), "user,sys.auth"); // now let's reset to 0 @@ -21357,6 +21360,7 @@ public void testPutRoleMeta() { rm3.setSelfRenewMins(0); rm3.setSelfRenew(false); rm3.setMaxMembers(0); + rm3.setPrincipalDomainFilter(""); zmsImpl.putRoleMeta(ctx, "rolemetadom1", "role1", auditRef, null, rm3); resRole1 = zmsImpl.getRole(ctx, "rolemetadom1", "role1", true, false, false); @@ -21374,6 +21378,7 @@ public void testPutRoleMeta() { assertNull(resRole1.getSelfRenew()); assertNull(resRole1.getMaxMembers()); assertNull(resRole1.getSelfRenewMins()); + assertNull(resRole1.getPrincipalDomainFilter()); // invalid negative values @@ -21452,6 +21457,16 @@ public void testPutRoleMeta() { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); } + rm4.setServiceReviewDays(10); + rm4.setPrincipalDomainFilter("some invalid domain"); + + try { + zmsImpl.putRoleMeta(ctx, "rolemetadom1", "role1", auditRef, null, rm4); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); + } + zmsImpl.deleteTopLevelDomain(ctx, "rolemetadom1", auditRef, null); } @@ -22907,7 +22922,7 @@ public void testValidateRoleMemberPrincipals() { roleMembers.add(new RoleMember().setMemberName("coretech.backend").setPrincipalType(Principal.Type.SERVICE.getValue())); Role role = new Role().setRoleMembers(roleMembers); - zmsImpl.validateRoleMemberPrincipals(role, null, false, "unittest"); + zmsImpl.validateRoleMemberPrincipals(role, null, null, false, "unittest"); // enable user authority check @@ -22923,13 +22938,13 @@ public void testValidateRoleMemberPrincipals() { roleMembers.add(new RoleMember().setMemberName("user.jane").setPrincipalType(Principal.Type.USER.getValue())); role.setRoleMembers(roleMembers); - zmsImpl.validateRoleMemberPrincipals(role, null, false, "unittest"); + zmsImpl.validateRoleMemberPrincipals(role, null, null, false, "unittest"); // add one more invalid user roleMembers.add(new RoleMember().setMemberName("user.john").setPrincipalType(Principal.Type.USER.getValue())); try { - zmsImpl.validateRoleMemberPrincipals(role, null, false, "unittest"); + zmsImpl.validateRoleMemberPrincipals(role, null, null, false, "unittest"); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); @@ -22943,7 +22958,7 @@ public void testValidateRoleMemberPrincipals() { roleMembers.add(new RoleMember().setMemberName("coretech:group.dev-team").setPrincipalType(Principal.Type.GROUP.getValue())); role.setRoleMembers(roleMembers); try { - zmsImpl.validateRoleMemberPrincipals(role, null, true, "unittest"); + zmsImpl.validateRoleMemberPrincipals(role, null, null, true, "unittest"); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); @@ -22955,7 +22970,7 @@ public void testValidateRoleMemberPrincipals() { roleMembers.add(new RoleMember().setMemberName("unknown").setPrincipalType(Principal.Type.UNKNOWN.getValue())); role.setRoleMembers(roleMembers); try { - zmsImpl.validateRoleMemberPrincipals(role, null, false, "unittest"); + zmsImpl.validateRoleMemberPrincipals(role, null, null, false, "unittest"); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); @@ -22975,13 +22990,16 @@ public void testValidateRoleMemberPrincipalUser() { // valid users no exception - zmsImpl.validateRoleMemberPrincipal("user.joe", Principal.Type.USER.getValue(), null, null, null, false, "unittest"); - zmsImpl.validateRoleMemberPrincipal("user.jane", Principal.Type.USER.getValue(), null, null, null, false, "unittest"); + zmsImpl.validateRoleMemberPrincipal("user.joe", Principal.Type.USER.getValue(), null, null, + null, null, false, "unittest"); + zmsImpl.validateRoleMemberPrincipal("user.jane", Principal.Type.USER.getValue(), null, null, + null, null, false, "unittest"); // invalid user request error try { - zmsImpl.validateRoleMemberPrincipal("user.john", Principal.Type.USER.getValue(), null, null, null, false, "unittest"); + zmsImpl.validateRoleMemberPrincipal("user.john", Principal.Type.USER.getValue(), null, null, + null, null, false, "unittest"); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); @@ -22990,31 +23008,31 @@ public void testValidateRoleMemberPrincipalUser() { // non - user principals by default are accepted zmsImpl.validateRoleMemberPrincipal("coretech.api", Principal.Type.SERVICE.getValue(), - null, null, null, false, "unittest"); + null, null, null, null, false, "unittest"); // valid employee and contractor users zmsImpl.validateRoleMemberPrincipal("user.joe", Principal.Type.USER.getValue(), "employee", - null, null, false, "unittest"); + null, null, null, false, "unittest"); zmsImpl.validateRoleMemberPrincipal("user.jane", Principal.Type.USER.getValue(), "employee", - null, null, false, "unittest"); + null, null, null, false, "unittest"); zmsImpl.validateRoleMemberPrincipal("user.jack", Principal.Type.USER.getValue(), "contractor", - null, null, false, "unittest"); + null, null, null, false, "unittest"); // valid multiple attribute users zmsImpl.validateRoleMemberPrincipal("user.joe", Principal.Type.USER.getValue(), "employee,local", - null, null, false, "unittest"); + null, null, null, false, "unittest"); zmsImpl.validateRoleMemberPrincipal("user.jane", Principal.Type.USER.getValue(), "employee,local", - null, null, false, "unittest"); + null, null, null, false, "unittest"); zmsImpl.validateRoleMemberPrincipal("user.jack", Principal.Type.USER.getValue(), "contractor,local", - null, null, false, "unittest"); + null, null, null, false, "unittest"); // invalid employee type try { zmsImpl.validateRoleMemberPrincipal("user.jack", Principal.Type.USER.getValue(), "employee", - null, null, false, "unittest"); + null, null, null, false, "unittest"); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); @@ -23024,7 +23042,7 @@ public void testValidateRoleMemberPrincipalUser() { try { zmsImpl.validateRoleMemberPrincipal("user.jack", Principal.Type.USER.getValue(), "local,employee", - null, null, false, "unittest"); + null, null, null, false, "unittest"); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); @@ -23047,15 +23065,15 @@ public void testValidateRoleMemberPrincipalService() { // wildcards are always valid with no exception zmsImpl.validateRoleMemberPrincipal("athenz.api*", Principal.Type.SERVICE.getValue(), - null, null, null, false, "unittest"); + null, null, null, null, false, "unittest"); zmsImpl.validateRoleMemberPrincipal("coretech.*", Principal.Type.SERVICE.getValue(), - null, null, null, false, "unittest"); + null, null, null, null, false, "unittest"); // should get back invalid request since service does not exist try { zmsImpl.validateRoleMemberPrincipal("coretech.api", Principal.Type.SERVICE.getValue(), "employee", - null, null, false, "unittest"); + null, null, null, false, "unittest"); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); @@ -23065,7 +23083,7 @@ public void testValidateRoleMemberPrincipalService() { try { zmsImpl.validateRoleMemberPrincipal("coretech", Principal.Type.SERVICE.getValue(), - null, null, null, false, "unittest"); + null, null, null, null, false, "unittest"); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); @@ -23088,13 +23106,13 @@ public void testValidateRoleMemberPrincipalService() { // known service - no exception zmsImpl.validateRoleMemberPrincipal("coretech.api", Principal.Type.SERVICE.getValue(), - null, null, null, false, "unittest"); + null, null, null, null, false, "unittest"); // unknown service - exception try { zmsImpl.validateRoleMemberPrincipal("coretech.backend", Principal.Type.SERVICE.getValue(), - null, null, null, false, "unittest"); + null, null, null, null, false, "unittest"); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); @@ -23111,13 +23129,13 @@ public void testValidateRoleMemberPrincipalService() { // coretech is now accepted zmsImpl.validateRoleMemberPrincipal("coretech.backend", Principal.Type.SERVICE.getValue(), - null, null, null, false, "unittest"); + null, null, null, null, false, "unittest"); // but coretech2 is rejected try { zmsImpl.validateRoleMemberPrincipal("coretech2.backend", Principal.Type.SERVICE.getValue(), - null, null, null, false, "unittest"); + null, null, null, null, false, "unittest"); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); @@ -23126,11 +23144,12 @@ public void testValidateRoleMemberPrincipalService() { // rbac.sre does not exists, but is accepted because rbac.* is included in skipDomains zmsImpl.validateRoleMemberPrincipal("rbac.sre.backend", Principal.Type.SERVICE.getValue(), - null, null, null ,false, "unittest"); + null, null, null, null ,false, "unittest"); // user principals by default are accepted - zmsImpl.validateRoleMemberPrincipal("user.john", Principal.Type.USER.getValue(), null, null, null, false, "unittest"); + zmsImpl.validateRoleMemberPrincipal("user.john", Principal.Type.USER.getValue(), null, null, + null, null, false, "unittest"); // reset our setting @@ -23152,21 +23171,23 @@ public void testValidateGroupMemberPrincipal() { // wildcards are always rejected try { - zmsImpl.validateGroupMemberPrincipal("athenz.api*", Principal.Type.SERVICE.getValue(), null, "unittest"); + zmsImpl.validateGroupMemberPrincipal("athenz.api*", Principal.Type.SERVICE.getValue(), + null, null, "unittest"); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); } try { - zmsImpl.validateGroupMemberPrincipal("athenz.api*", Principal.Type.SERVICE.getValue(), null, "unittest"); + zmsImpl.validateGroupMemberPrincipal("athenz.api*", Principal.Type.SERVICE.getValue(), + null, null, "unittest"); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); } try { - zmsImpl.validateGroupMemberPrincipal("*", Principal.Type.SERVICE.getValue(), null, "unittest"); + zmsImpl.validateGroupMemberPrincipal("*", Principal.Type.SERVICE.getValue(), null, null, "unittest"); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); @@ -23175,7 +23196,8 @@ public void testValidateGroupMemberPrincipal() { // should get back invalid request since service does not exist try { - zmsImpl.validateGroupMemberPrincipal("coretech.api", Principal.Type.SERVICE.getValue(), "employee", "unittest"); + zmsImpl.validateGroupMemberPrincipal("coretech.api", Principal.Type.SERVICE.getValue(), + "employee", null, "unittest"); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); @@ -23184,13 +23206,15 @@ public void testValidateGroupMemberPrincipal() { // invalid service request error try { - zmsImpl.validateGroupMemberPrincipal("coretech", Principal.Type.SERVICE.getValue(), null, "unittest"); + zmsImpl.validateGroupMemberPrincipal("coretech", Principal.Type.SERVICE.getValue(), null, + null, "unittest"); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); } - TopLevelDomain dom1 = zmsTestInitializer.createTopLevelDomainObject("coretech", "Test Domain1", "testorg", zmsTestInitializer.getAdminUser()); + TopLevelDomain dom1 = zmsTestInitializer.createTopLevelDomainObject("coretech", "Test Domain1", + "testorg", zmsTestInitializer.getAdminUser()); zmsImpl.postTopLevelDomain(ctx, auditRef, null, dom1); ServiceIdentity service1 = zmsTestInitializer.createServiceObject("coretech", @@ -23201,12 +23225,14 @@ public void testValidateGroupMemberPrincipal() { // known service - no exception - zmsImpl.validateGroupMemberPrincipal("coretech.api", Principal.Type.SERVICE.getValue(), null, "unittest"); + zmsImpl.validateGroupMemberPrincipal("coretech.api", Principal.Type.SERVICE.getValue(), + null, null, "unittest"); // unknown service - exception try { - zmsImpl.validateGroupMemberPrincipal("coretech.backend", Principal.Type.SERVICE.getValue(), null, "unittest"); + zmsImpl.validateGroupMemberPrincipal("coretech.backend", Principal.Type.SERVICE.getValue(), + null, null, "unittest"); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); @@ -23214,13 +23240,13 @@ public void testValidateGroupMemberPrincipal() { // known user principals are accepted - zmsImpl.validateGroupMemberPrincipal("user.joe", Principal.Type.USER.getValue(), null, "unittest"); - zmsImpl.validateGroupMemberPrincipal("user.jane", Principal.Type.USER.getValue(), null, "unittest"); + zmsImpl.validateGroupMemberPrincipal("user.joe", Principal.Type.USER.getValue(), null, null, "unittest"); + zmsImpl.validateGroupMemberPrincipal("user.jane", Principal.Type.USER.getValue(), null, null, "unittest"); // unknown users are rejected try { - zmsImpl.validateGroupMemberPrincipal("user.john", Principal.Type.USER.getValue(), null, "unittest"); + zmsImpl.validateGroupMemberPrincipal("user.john", Principal.Type.USER.getValue(), null, null, "unittest"); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); @@ -23229,7 +23255,7 @@ public void testValidateGroupMemberPrincipal() { // groups and unknown types are rejected try { - zmsImpl.validateGroupMemberPrincipal("user", Principal.Type.UNKNOWN.getValue(), null, "unittest"); + zmsImpl.validateGroupMemberPrincipal("user", Principal.Type.UNKNOWN.getValue(), null, null, "unittest"); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); @@ -23239,7 +23265,8 @@ public void testValidateGroupMemberPrincipal() { zmsImpl.putGroup(ctx, "coretech", "dev-team", auditRef, false, null, group); try { - zmsImpl.validateGroupMemberPrincipal("coretech:group.dev-team", Principal.Type.GROUP.getValue(), null, "unittest"); + zmsImpl.validateGroupMemberPrincipal("coretech:group.dev-team", Principal.Type.GROUP.getValue(), + null, null, "unittest"); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); @@ -25135,7 +25162,7 @@ public void testCreateGroup() { + "\"serviceExpiryDays\": \"null\", \"reviewEnabled\": \"false\", \"notifyRoles\": \"null\", " + "\"userAuthorityFilter\": \"null\", \"userAuthorityExpiration\": \"null\", " + "\"deleteProtection\": \"false\", \"lastReviewedDate\": \"null\", \"maxMembers\": \"null\", " - + "\"selfRenew\": \"null\", \"selfRenewMins\": \"null\", " + + "\"selfRenew\": \"null\", \"selfRenewMins\": \"null\", \"principalDomainFilter\": \"null\", " + "\"deleted-members\": [{\"member\": \"user.jane\", \"approved\": true, \"system-disabled\": 0}], " + "\"added-members\": []}"); assertTrue(index2 > index, msg); @@ -25960,7 +25987,7 @@ public void testValidateGroupMemberPrincipals() { Group group = new Group().setGroupMembers(groupMembers); try { - zmsImpl.validateGroupMemberPrincipals(group, null, "unittest"); + zmsImpl.validateGroupMemberPrincipals(group, null, null, "unittest"); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); @@ -25979,13 +26006,13 @@ public void testValidateGroupMemberPrincipals() { groupMembers.add(new GroupMember().setMemberName("sys.auth.zms").setPrincipalType(Principal.Type.SERVICE.getValue())); group.setGroupMembers(groupMembers); - zmsImpl.validateGroupMemberPrincipals(group, null, "unittest"); + zmsImpl.validateGroupMemberPrincipals(group, null, null, "unittest"); // add one more invalid user groupMembers.add(new GroupMember().setMemberName("user.john").setPrincipalType(Principal.Type.USER.getValue())); try { - zmsImpl.validateGroupMemberPrincipals(group, null, "unittest"); + zmsImpl.validateGroupMemberPrincipals(group, null, null, "unittest"); fail(); } catch (ResourceException ex) { assertEquals(ex.getCode(), ResourceException.BAD_REQUEST); @@ -27942,7 +27969,8 @@ public void testPutGroupMeta() { final String domainName = "put-group-meta"; final String groupName = "group1"; - TopLevelDomain dom1 = zmsTestInitializer.createTopLevelDomainObject(domainName, "Group Meta Test Domain1", "testOrg", zmsTestInitializer.getAdminUser()); + TopLevelDomain dom1 = zmsTestInitializer.createTopLevelDomainObject(domainName, + "Group Meta Test Domain1", "testOrg", zmsTestInitializer.getAdminUser()); zmsImpl.postTopLevelDomain(ctx, auditRef, null, dom1); Group group1 = zmsTestInitializer.createGroupObject(domainName, groupName, "user.john", "user.jane"); @@ -27976,6 +28004,7 @@ public void testPutGroupMeta() { assertNull(resGroup1.getMaxMembers()); assertNull(resGroup1.getSelfRenew()); assertNull(resGroup1.getSelfRenewMins()); + assertNull(resGroup1.getPrincipalDomainFilter()); groupMeta = new GroupMeta() .setSelfServe(true) @@ -27987,7 +28016,8 @@ public void testPutGroupMeta() { .setServiceExpiryDays(45) .setSelfRenew(true) .setSelfRenewMins(99) - .setMaxMembers(23); + .setMaxMembers(23) + .setPrincipalDomainFilter("user,sys.auth"); zmsImpl.putGroupMeta(ctx, domainName, groupName, auditRef, null, groupMeta); resGroup1 = zmsImpl.getGroup(ctx, domainName, groupName, true, false); @@ -28002,6 +28032,7 @@ public void testPutGroupMeta() { assertEquals(resGroup1.getMaxMembers(), 23); assertTrue(resGroup1.getSelfRenew()); assertEquals(resGroup1.getSelfRenewMins(), 99); + assertEquals(resGroup1.getPrincipalDomainFilter(), "user,sys.auth"); groupMeta = new GroupMeta().setNotifyRoles("role2,role3"); zmsImpl.putGroupMeta(ctx, domainName, groupName, auditRef, null, groupMeta); @@ -28013,6 +28044,7 @@ public void testPutGroupMeta() { assertEquals(resGroup1.getNotifyRoles(), "role2,role3"); assertEquals(resGroup1.getUserAuthorityExpiration(), "elevated-clearance"); assertEquals(resGroup1.getUserAuthorityFilter(), "OnShore-US"); + assertEquals(resGroup1.getPrincipalDomainFilter(), "user,sys.auth"); zmsImpl.dbService.zmsConfig.setUserAuthority(savedAuthority); zmsImpl.userAuthority = savedAuthority; diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/provider/ServiceProviderManagerTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/provider/ServiceProviderManagerTest.java index 6c131ad8c69..db099737f50 100644 --- a/servers/zms/src/test/java/com/yahoo/athenz/zms/provider/ServiceProviderManagerTest.java +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/provider/ServiceProviderManagerTest.java @@ -31,10 +31,7 @@ import org.testng.annotations.Test; import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.*; import static com.yahoo.athenz.zms.ZMSConsts.*; @@ -115,6 +112,7 @@ public void testIsServiceProvider() throws InterruptedException, ExecutionExcept } serviceProviderManager.shutdown(); + serviceProviderManager.setServiceProviders(Collections.emptyMap()); System.clearProperty(ZMS_PROP_SERVICE_PROVIDER_MANAGER_FREQUENCY_SECONDS); System.clearProperty(ZMS_PROP_SERVICE_PROVIDER_MANAGER_DOMAIN); System.clearProperty(ZMS_PROP_SERVICE_PROVIDER_MANAGER_ROLE); 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 b89dc650f32..c0dd73f7dc7 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 @@ -1027,6 +1027,7 @@ public void testGetRole() throws Exception { Mockito.doReturn("").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_USER_AUTHORITY_EXPIRATION); Mockito.doReturn("").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_USER_AUTHORITY_FILTER); Mockito.doReturn("").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_DESCRIPTION); + Mockito.doReturn("user,-home").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_PRINCIPAL_DOMAIN_FILTER); JDBCConnection jdbcConn = new JDBCConnection(mockConn, true); Role role = jdbcConn.getRole("my-domain", "role1"); @@ -1046,6 +1047,7 @@ public void testGetRole() throws Exception { assertEquals(role.getNotifyRoles(), "role1,role2"); assertTrue(role.getReviewEnabled()); assertEquals(role.getLastReviewedDate(), Timestamp.fromMillis(1454358917)); + assertEquals(role.getPrincipalDomainFilter(), "user,-home"); Mockito.verify(mockPrepStmt, times(1)).setString(1, "my-domain"); Mockito.verify(mockPrepStmt, times(1)).setString(2, "role1"); @@ -1077,6 +1079,7 @@ public void testGetRoleWithDueDates() throws Exception { Mockito.doReturn("expiry").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_USER_AUTHORITY_EXPIRATION); Mockito.doReturn("filter").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_USER_AUTHORITY_FILTER); Mockito.doReturn("description").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_DESCRIPTION); + Mockito.doReturn("user,-home").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_PRINCIPAL_DOMAIN_FILTER); JDBCConnection jdbcConn = new JDBCConnection(mockConn, true); Role role = jdbcConn.getRole("my-domain", "role1"); @@ -1093,6 +1096,7 @@ public void testGetRoleWithDueDates() throws Exception { assertEquals(role.getMemberReviewDays(), Integer.valueOf(70)); assertEquals(role.getServiceReviewDays(), Integer.valueOf(80)); assertEquals(role.getGroupReviewDays(), Integer.valueOf(90)); + assertEquals(role.getPrincipalDomainFilter(), "user,-home"); Mockito.verify(mockPrepStmt, times(1)).setString(1, "my-domain"); Mockito.verify(mockPrepStmt, times(1)).setString(2, "role1"); @@ -1117,6 +1121,7 @@ public void testGetRoleWithoutSelfServe() throws Exception { Mockito.doReturn("").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_USER_AUTHORITY_EXPIRATION); Mockito.doReturn("").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_USER_AUTHORITY_FILTER); Mockito.doReturn("").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_DESCRIPTION); + Mockito.doReturn("").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_PRINCIPAL_DOMAIN_FILTER); JDBCConnection jdbcConn = new JDBCConnection(mockConn, true); Role role = jdbcConn.getRole("my-domain", "role1"); @@ -1127,6 +1132,7 @@ public void testGetRoleWithoutSelfServe() throws Exception { assertNull(role.getUserAuthorityExpiration()); assertNull(role.getUserAuthorityFilter()); assertNull(role.getDescription()); + assertNull(role.getPrincipalDomainFilter()); Mockito.verify(mockPrepStmt, times(1)).setString(1, "my-domain"); Mockito.verify(mockPrepStmt, times(1)).setString(2, "role1"); @@ -1160,6 +1166,7 @@ public void testGetRoleTrust() throws Exception { Mockito.doReturn("").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_USER_AUTHORITY_EXPIRATION); Mockito.doReturn("").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_USER_AUTHORITY_FILTER); Mockito.doReturn("").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_DESCRIPTION); + Mockito.doReturn("").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_PRINCIPAL_DOMAIN_FILTER); JDBCConnection jdbcConn = new JDBCConnection(mockConn, true); Role role = jdbcConn.getRole("my-domain", "role1"); @@ -1168,6 +1175,7 @@ public void testGetRoleTrust() throws Exception { assertEquals("trust.domain", role.getTrust()); assertNull(role.getUserAuthorityExpiration()); assertNull(role.getUserAuthorityFilter()); + assertNull(role.getPrincipalDomainFilter()); jdbcConn.close(); } @@ -1383,7 +1391,7 @@ public void testUpdateRole() throws Exception { .setReviewEnabled(true).setNotifyRoles("role1,role2") .setUserAuthorityFilter("filter").setUserAuthorityExpiration("expiry") .setDescription("description").setLastReviewedDate(Timestamp.fromMillis(100)) - .setMaxMembers(10).setSelfRenew(true).setSelfRenewMins(99); + .setMaxMembers(10).setSelfRenew(true).setSelfRenewMins(99).setPrincipalDomainFilter("user"); Mockito.doReturn(1).when(mockPrepStmt).executeUpdate(); Mockito.when(mockResultSet.next()).thenReturn(true); @@ -1421,7 +1429,8 @@ public void testUpdateRole() throws Exception { Mockito.verify(mockPrepStmt, times(1)).setInt(20, 10); Mockito.verify(mockPrepStmt, times(1)).setBoolean(21, true); Mockito.verify(mockPrepStmt, times(1)).setInt(22, 99); - Mockito.verify(mockPrepStmt, times(1)).setInt(23, 4); + Mockito.verify(mockPrepStmt, times(1)).setString(23, "user"); + Mockito.verify(mockPrepStmt, times(1)).setInt(24, 4); jdbcConn.close(); } @@ -1470,7 +1479,8 @@ public void testUpdateRoleWithTrust() throws Exception { Mockito.verify(mockPrepStmt, times(1)).setInt(20, 0); Mockito.verify(mockPrepStmt, times(1)).setBoolean(21, false); Mockito.verify(mockPrepStmt, times(1)).setInt(22, 0); - Mockito.verify(mockPrepStmt, times(1)).setInt(23, 7); + Mockito.verify(mockPrepStmt, times(1)).setString(23, ""); + Mockito.verify(mockPrepStmt, times(1)).setInt(24, 7); jdbcConn.close(); } @@ -6598,6 +6608,7 @@ public void testGetAthenzDomain() throws Exception { 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(""); + Mockito.when(mockResultSet.getString(ZMSConsts.DB_COLUMN_PRINCIPAL_DOMAIN_FILTER)).thenReturn(""); AthenzDomain athenzDomain = jdbcConn.getAthenzDomain("my-domain"); assertNotNull(athenzDomain); @@ -9133,6 +9144,7 @@ public void testGetRoleDefaultAuditEnabledAsNull() throws Exception { Mockito.doReturn("").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_USER_AUTHORITY_EXPIRATION); Mockito.doReturn("").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_USER_AUTHORITY_FILTER); Mockito.doReturn("").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_DESCRIPTION); + Mockito.doReturn("").when(mockResultSet).getString(ZMSConsts.DB_COLUMN_PRINCIPAL_DOMAIN_FILTER); JDBCConnection jdbcConn = new JDBCConnection(mockConn, true); Role role = jdbcConn.getRole("my-domain", "role1"); @@ -9146,6 +9158,7 @@ public void testGetRoleDefaultAuditEnabledAsNull() throws Exception { assertNull(role.getUserAuthorityExpiration()); assertNull(role.getUserAuthorityFilter()); assertNull(role.getDescription()); + assertNull(role.getPrincipalDomainFilter()); Mockito.verify(mockPrepStmt, times(1)).setString(1, "my-domain"); Mockito.verify(mockPrepStmt, times(1)).setString(2, "role1"); diff --git a/servers/zms/src/test/java/com/yahoo/athenz/zms/utils/PrincipalDomainFilterTest.java b/servers/zms/src/test/java/com/yahoo/athenz/zms/utils/PrincipalDomainFilterTest.java new file mode 100644 index 00000000000..1ce2542ab20 --- /dev/null +++ b/servers/zms/src/test/java/com/yahoo/athenz/zms/utils/PrincipalDomainFilterTest.java @@ -0,0 +1,489 @@ +/* + * Copyright The Athenz Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.yahoo.athenz.zms.utils; + +import com.yahoo.athenz.auth.Principal; +import com.yahoo.athenz.zms.*; +import org.mockito.MockitoAnnotations; +import org.testng.annotations.*; + +import java.lang.reflect.Member; +import java.util.ArrayList; +import java.util.List; + +import static org.testng.Assert.*; + +public class PrincipalDomainFilterTest { + + private final ZMSTestInitializer zmsTestInitializer = new ZMSTestInitializer(); + + @BeforeClass + public void startMemoryMySQL() { + zmsTestInitializer.startMemoryMySQL(); + } + + @AfterClass + public void stopMemoryMySQL() { + zmsTestInitializer.stopMemoryMySQL(); + } + + @BeforeMethod + public void setUp() throws Exception { + MockitoAnnotations.openMocks(this); + zmsTestInitializer.setUp(); + } + + @Test + public void testPrincipalDomainFilter() { + + // empty filter has no processing and always returns true for validation + + PrincipalDomainFilter filter = new PrincipalDomainFilter(""); + assertNull(filter.allowedDomains); + assertNull(filter.disallowedSubDomains); + assertNull(filter.allowedSubDomains); + assertTrue(filter.validate(null, Principal.Type.USER)); + assertTrue(filter.validate(null, Principal.Type.GROUP)); + + filter = new PrincipalDomainFilter(null); + assertNull(filter.allowedDomains); + assertNull(filter.disallowedSubDomains); + assertNull(filter.allowedSubDomains); + assertTrue(filter.validate(null, Principal.Type.USER)); + assertTrue(filter.validate(null, Principal.Type.GROUP)); + + // now let's test some valid filters + + filter = new PrincipalDomainFilter("domain1,domain2"); + assertNotNull(filter.allowedDomains); + assertEquals(filter.allowedDomains.size(), 2); + assertTrue(filter.allowedDomains.contains("domain1.")); + assertTrue(filter.allowedDomains.contains("domain2.")); + assertNull(filter.disallowedSubDomains); + assertNull(filter.allowedSubDomains); + + filter = new PrincipalDomainFilter("domain1,domain2.api,+domain3,-domain4"); + assertNotNull(filter.allowedDomains); + assertEquals(filter.allowedDomains.size(), 2); + assertTrue(filter.allowedDomains.contains("domain1.")); + assertTrue(filter.allowedDomains.contains("domain2.api.")); + assertNotNull(filter.allowedSubDomains); + assertEquals(filter.allowedSubDomains.size(), 1); + assertTrue(filter.allowedSubDomains.contains("domain3.")); + assertNotNull(filter.disallowedSubDomains); + assertEquals(filter.disallowedSubDomains.size(), 1); + assertTrue(filter.disallowedSubDomains.contains("domain4.")); + + filter = new PrincipalDomainFilter("domain2.api,+domain1,+domain2,-domain1.api,-domain2.prod"); + assertNotNull(filter.allowedDomains); + assertEquals(filter.allowedDomains.size(), 1); + assertTrue(filter.allowedDomains.contains("domain2.api.")); + assertNotNull(filter.allowedSubDomains); + assertEquals(filter.allowedSubDomains.size(), 2); + assertTrue(filter.allowedSubDomains.contains("domain1.")); + assertTrue(filter.allowedSubDomains.contains("domain2.")); + assertNotNull(filter.disallowedSubDomains); + assertEquals(filter.disallowedSubDomains.size(), 2); + assertTrue(filter.disallowedSubDomains.contains("domain1.api.")); + assertTrue(filter.disallowedSubDomains.contains("domain2.prod.")); + } + + @DataProvider(name = "DomainFilterData") + public static Object[][] domainFilterData() { + return new Object[][] { + { "user", "user.joe", Principal.Type.USER, true }, + { "user", "sports.api", Principal.Type.SERVICE, false }, + { "user", "athenz:group.dev-team", Principal.Type.GROUP, false }, + { "-home", "user.joe", Principal.Type.USER, true }, + { "-home", "sports.api", Principal.Type.SERVICE, true }, + { "-home", "athenz:group.dev-team", Principal.Type.GROUP, true }, + { "-home", "home.api", Principal.Type.SERVICE, false }, + { "-home", "home.prod.api", Principal.Type.SERVICE, false }, + { "+sports.prod", "user.joe", Principal.Type.USER, false }, + { "+sports.prod", "sports.api", Principal.Type.SERVICE, false }, + { "+sports.prod", "athenz:group.dev-team", Principal.Type.GROUP, false }, + { "+sports.prod", "sports.prod.api", Principal.Type.SERVICE, true }, + { "+sports.prod", "sports.prod.west2.api", Principal.Type.SERVICE, true }, + { "+sports.prod", "weather.api", Principal.Type.SERVICE, false }, + { "user,+sports,-sports.prod", "user.joe", Principal.Type.USER, true }, + { "user,+sports,-sports.prod", "sports.api", Principal.Type.SERVICE, true }, + { "user,+sports,-sports.prod", "sports.dev.api", Principal.Type.SERVICE, true }, + { "user,+sports,-sports.prod", "sports.prod.api", Principal.Type.SERVICE, false }, + { "user,+sports,-sports.prod", "sports.prod.west2.api", Principal.Type.SERVICE, false }, + { "user,+sports,-sports.prod", "weather.api", Principal.Type.SERVICE, false }, + { "user,+sports,-sports.prod", "athenz:group.dev-team", Principal.Type.GROUP, false }, + { "+sports,-sports.prod", "user.joe", Principal.Type.USER, false }, + { "+sports,-sports.prod", "sports.api", Principal.Type.SERVICE, true }, + { "+sports,-sports.prod", "sports.dev.api", Principal.Type.SERVICE, true }, + { "+sports,-sports.prod", "sports.prod.api", Principal.Type.SERVICE, false }, + { "+sports,-sports.prod", "sports.prod.west2.api", Principal.Type.SERVICE, false }, + { "+sports,-sports.prod", "weather.api", Principal.Type.SERVICE, false }, + { "+sports,-sports.prod", "athenz:group.dev-team", Principal.Type.GROUP, false }, + }; + } + @Test(dataProvider = "DomainFilterData") + public void testDomainFilterValidation(String filter, String principalName, Principal.Type type, boolean expectedResult) { + PrincipalDomainFilter domainFilter = new PrincipalDomainFilter(filter); + assertEquals(domainFilter.validate(principalName, type), expectedResult); + } + + @Test + public void testPutRoleWithDomainFilter() { + + ZMSImpl zmsImpl = zmsTestInitializer.getZms(); + RsrcCtxWrapper ctx = zmsTestInitializer.getMockDomRsrcCtx(); + final String auditRef = zmsTestInitializer.getAuditRef(); + + final String domainName = "role-with-domain-filter"; + TopLevelDomain dom1 = zmsTestInitializer.createTopLevelDomainObject(domainName, + "Test Domain1", "testOrg", "user.user1"); + zmsImpl.postTopLevelDomain(ctx, auditRef, null, dom1); + + TopLevelDomain dom2 = zmsTestInitializer.createTopLevelDomainObject("sports", + "Test Domain1", "testOrg", "user.user1"); + zmsImpl.postTopLevelDomain(ctx, auditRef, null, dom2); + + ServiceIdentity service1 = zmsTestInitializer.createServiceObject("sports", "api", + "http://localhost:8080", null, null, null, null); + zmsImpl.putServiceIdentity(ctx, "sports", "api", auditRef, false, null, service1); + + Group group1 = zmsTestInitializer.createGroupObject("sports", "group1", null, null); + zmsImpl.putGroup(ctx, "sports", "group1", auditRef, false, null, group1); + + SubDomain subDom1 = zmsTestInitializer.createSubDomainObject("prod", "sports", "Test Domain1", + "testOrg", "user.user1"); + zmsImpl.postSubDomain(ctx, "sports", auditRef, null, subDom1); + + ServiceIdentity service2 = zmsTestInitializer.createServiceObject("sports.prod", "api", + "http://localhost:8080", null, null, null, null); + zmsImpl.putServiceIdentity(ctx, "sports.prod", "api", auditRef, false, null, service2); + + SubDomain subDom2 = zmsTestInitializer.createSubDomainObject("dev", "sports", "Test Domain1", + "testOrg", "user.user1"); + zmsImpl.postSubDomain(ctx, "sports", auditRef, null, subDom2); + + ServiceIdentity service3 = zmsTestInitializer.createServiceObject("sports.dev", "api", + "http://localhost:8080", null, null, null, null); + zmsImpl.putServiceIdentity(ctx, "sports.dev", "api", auditRef, false, null, service3); + + // add a role with the domain filter and allowed members + + List roleMembers = new ArrayList<>(); + roleMembers.add(new RoleMember().setMemberName("user.user1")); + roleMembers.add(new RoleMember().setMemberName("sports.api")); + roleMembers.add(new RoleMember().setMemberName("sports:group.group1")); + roleMembers.add(new RoleMember().setMemberName("sports.dev.api")); + + final String roleName1 = "filter-role1"; + Role role1 = zmsTestInitializer.createRoleObject(domainName, roleName1, null, roleMembers); + role1.setPrincipalDomainFilter("user,+sports,-sports.prod"); + zmsImpl.putRole(ctx, domainName, roleName1, auditRef, false, null, role1); + + // add a role with the domain filter and no allowed members + + roleMembers = new ArrayList<>(); + roleMembers.add(new RoleMember().setMemberName("user.user1")); + roleMembers.add(new RoleMember().setMemberName("sports.api")); + roleMembers.add(new RoleMember().setMemberName("sports:group.group1")); + roleMembers.add(new RoleMember().setMemberName("sports.dev.api")); + roleMembers.add(new RoleMember().setMemberName("sports.prod.api")); + role1.setRoleMembers(roleMembers); + + // sports.prod.api should be rejected + + try { + zmsImpl.putRole(ctx, domainName, roleName1, auditRef, false, null, role1); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), 400); + assertEquals(ex.getMessage().contains("Principal sports.prod.api is not allowed for the role"), true); + } + + zmsImpl.deleteSubDomain(ctx, "sports", "dev", auditRef, null); + zmsImpl.deleteSubDomain(ctx, "sports", "prod", auditRef, null); + zmsImpl.deleteMembership(ctx, domainName, roleName1, "sports:group.group1", auditRef, null); + zmsImpl.deleteTopLevelDomain(ctx, "sports", auditRef, null); + zmsImpl.deleteTopLevelDomain(ctx, domainName, auditRef, null); + } + + @Test + public void testPutRoleMembershipWithDomainFilter() { + + ZMSImpl zmsImpl = zmsTestInitializer.getZms(); + RsrcCtxWrapper ctx = zmsTestInitializer.getMockDomRsrcCtx(); + final String auditRef = zmsTestInitializer.getAuditRef(); + + final String domainName = "role-mbr-with-domain-filter"; + TopLevelDomain dom1 = zmsTestInitializer.createTopLevelDomainObject(domainName, + "Test Domain1", "testOrg", "user.user1"); + zmsImpl.postTopLevelDomain(ctx, auditRef, null, dom1); + + TopLevelDomain dom2 = zmsTestInitializer.createTopLevelDomainObject("sports", + "Test Domain1", "testOrg", "user.user1"); + zmsImpl.postTopLevelDomain(ctx, auditRef, null, dom2); + + ServiceIdentity service1 = zmsTestInitializer.createServiceObject("sports", "api", + "http://localhost:8080", null, null, null, null); + zmsImpl.putServiceIdentity(ctx, "sports", "api", auditRef, false, null, service1); + + Group group1 = zmsTestInitializer.createGroupObject("sports", "group1", null, null); + zmsImpl.putGroup(ctx, "sports", "group1", auditRef, false, null, group1); + + SubDomain subDom1 = zmsTestInitializer.createSubDomainObject("prod", "sports", "Test Domain1", + "testOrg", "user.user1"); + zmsImpl.postSubDomain(ctx, "sports", auditRef, null, subDom1); + + ServiceIdentity service2 = zmsTestInitializer.createServiceObject("sports.prod", "api", + "http://localhost:8080", null, null, null, null); + zmsImpl.putServiceIdentity(ctx, "sports.prod", "api", auditRef, false, null, service2); + + SubDomain subDom2 = zmsTestInitializer.createSubDomainObject("dev", "sports", "Test Domain1", + "testOrg", "user.user1"); + zmsImpl.postSubDomain(ctx, "sports", auditRef, null, subDom2); + + ServiceIdentity service3 = zmsTestInitializer.createServiceObject("sports.dev", "api", + "http://localhost:8080", null, null, null, null); + zmsImpl.putServiceIdentity(ctx, "sports.dev", "api", auditRef, false, null, service3); + + // add a role with the domain filter + + final String roleName1 = "filter-role1"; + Role role1 = zmsTestInitializer.createRoleObject(domainName, roleName1, null, null); + role1.setPrincipalDomainFilter("user,+sports,-sports.prod"); + zmsImpl.putRole(ctx, domainName, roleName1, auditRef, false, null, role1); + + // user.user1 should be able to be added to the role + + Membership membership = new Membership().setMemberName("user.user1"); + zmsImpl.putMembership(ctx, domainName, roleName1, "user.user1", auditRef, false, null, membership); + + // sports.api should be able to be added to the role + + membership = new Membership().setMemberName("sports.api"); + zmsImpl.putMembership(ctx, domainName, roleName1, "sports.api", auditRef, false, null, membership); + + // sports:group.group1 should be allowed to be added to the role + + membership = new Membership().setMemberName("sports:group.group1"); + zmsImpl.putMembership(ctx, domainName, roleName1, "sports:group.group1", auditRef, false, null, membership); + + // sports.dev.api should be able to be added to the role + + membership = new Membership().setMemberName("sports.dev.api"); + zmsImpl.putMembership(ctx, domainName, roleName1, "sports.dev.api", auditRef, false, null, membership); + + // sports.prod.api should be rejected + + membership = new Membership().setMemberName("sports.prod.api"); + try { + zmsImpl.putMembership(ctx, domainName, roleName1, "sports.prod.api", auditRef, false, null, membership); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), 400); + assertEquals(ex.getMessage().contains("Principal sports.prod.api is not allowed for the role"), true); + } + + zmsImpl.deleteSubDomain(ctx, "sports", "dev", auditRef, null); + zmsImpl.deleteSubDomain(ctx, "sports", "prod", auditRef, null); + zmsImpl.deleteMembership(ctx, domainName, roleName1, "sports:group.group1", auditRef, null); + zmsImpl.deleteTopLevelDomain(ctx, "sports", auditRef, null); + zmsImpl.deleteTopLevelDomain(ctx, domainName, auditRef, null); + } + + @Test + public void testPutGroupWithDomainFilter() { + + ZMSImpl zmsImpl = zmsTestInitializer.getZms(); + RsrcCtxWrapper ctx = zmsTestInitializer.getMockDomRsrcCtx(); + final String auditRef = zmsTestInitializer.getAuditRef(); + + final String domainName = "group-with-domain-filter"; + TopLevelDomain dom1 = zmsTestInitializer.createTopLevelDomainObject(domainName, + "Test Domain1", "testOrg", "user.user1"); + zmsImpl.postTopLevelDomain(ctx, auditRef, null, dom1); + + TopLevelDomain dom2 = zmsTestInitializer.createTopLevelDomainObject("sports", + "Test Domain1", "testOrg", "user.user1"); + zmsImpl.postTopLevelDomain(ctx, auditRef, null, dom2); + + ServiceIdentity service1 = zmsTestInitializer.createServiceObject("sports", "api", + "http://localhost:8080", null, null, null, null); + zmsImpl.putServiceIdentity(ctx, "sports", "api", auditRef, false, null, service1); + + SubDomain subDom1 = zmsTestInitializer.createSubDomainObject("prod", "sports", "Test Domain1", + "testOrg", "user.user1"); + zmsImpl.postSubDomain(ctx, "sports", auditRef, null, subDom1); + + ServiceIdentity service2 = zmsTestInitializer.createServiceObject("sports.prod", "api", + "http://localhost:8080", null, null, null, null); + zmsImpl.putServiceIdentity(ctx, "sports.prod", "api", auditRef, false, null, service2); + + SubDomain subDom2 = zmsTestInitializer.createSubDomainObject("dev", "sports", "Test Domain1", + "testOrg", "user.user1"); + zmsImpl.postSubDomain(ctx, "sports", auditRef, null, subDom2); + + ServiceIdentity service3 = zmsTestInitializer.createServiceObject("sports.dev", "api", + "http://localhost:8080", null, null, null, null); + zmsImpl.putServiceIdentity(ctx, "sports.dev", "api", auditRef, false, null, service3); + + // add a group with the domain filter and allowed members + + List groupMembers = new ArrayList<>(); + groupMembers.add(new GroupMember().setMemberName("user.user1")); + groupMembers.add(new GroupMember().setMemberName("sports.api")); + groupMembers.add(new GroupMember().setMemberName("sports.dev.api")); + + final String groupName1 = "filter-group1"; + Group group1 = zmsTestInitializer.createGroupObject(domainName, groupName1, groupMembers); + group1.setPrincipalDomainFilter("user,+sports,-sports.prod"); + zmsImpl.putGroup(ctx, domainName, groupName1, auditRef, false, null, group1); + + // add a group with the domain filter and no allowed members + + groupMembers = new ArrayList<>(); + groupMembers.add(new GroupMember().setMemberName("user.user1")); + groupMembers.add(new GroupMember().setMemberName("sports.api")); + groupMembers.add(new GroupMember().setMemberName("sports.dev.api")); + groupMembers.add(new GroupMember().setMemberName("sports.prod.api")); + group1.setGroupMembers(groupMembers); + + // sports.prod.api should be rejected + + try { + zmsImpl.putGroup(ctx, domainName, groupName1, auditRef, false, null, group1); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), 400); + assertEquals(ex.getMessage().contains("Principal sports.prod.api is not allowed for the group"), true); + } + + zmsImpl.deleteSubDomain(ctx, "sports", "dev", auditRef, null); + zmsImpl.deleteSubDomain(ctx, "sports", "prod", auditRef, null); + zmsImpl.deleteTopLevelDomain(ctx, "sports", auditRef, null); + zmsImpl.deleteTopLevelDomain(ctx, domainName, auditRef, null); + } + + @Test + public void testPutGroupMembershipWithDomainFilter() { + + ZMSImpl zmsImpl = zmsTestInitializer.getZms(); + RsrcCtxWrapper ctx = zmsTestInitializer.getMockDomRsrcCtx(); + final String auditRef = zmsTestInitializer.getAuditRef(); + + final String domainName = "group-mbr-with-domain-filter"; + TopLevelDomain dom1 = zmsTestInitializer.createTopLevelDomainObject(domainName, + "Test Domain1", "testOrg", "user.user1"); + zmsImpl.postTopLevelDomain(ctx, auditRef, null, dom1); + + TopLevelDomain dom2 = zmsTestInitializer.createTopLevelDomainObject("sports", + "Test Domain1", "testOrg", "user.user1"); + zmsImpl.postTopLevelDomain(ctx, auditRef, null, dom2); + + ServiceIdentity service1 = zmsTestInitializer.createServiceObject("sports", "api", + "http://localhost:8080", null, null, null, null); + zmsImpl.putServiceIdentity(ctx, "sports", "api", auditRef, false, null, service1); + + SubDomain subDom1 = zmsTestInitializer.createSubDomainObject("prod", "sports", "Test Domain1", + "testOrg", "user.user1"); + zmsImpl.postSubDomain(ctx, "sports", auditRef, null, subDom1); + + ServiceIdentity service2 = zmsTestInitializer.createServiceObject("sports.prod", "api", + "http://localhost:8080", null, null, null, null); + zmsImpl.putServiceIdentity(ctx, "sports.prod", "api", auditRef, false, null, service2); + + SubDomain subDom2 = zmsTestInitializer.createSubDomainObject("dev", "sports", "Test Domain1", + "testOrg", "user.user1"); + zmsImpl.postSubDomain(ctx, "sports", auditRef, null, subDom2); + + ServiceIdentity service3 = zmsTestInitializer.createServiceObject("sports.dev", "api", + "http://localhost:8080", null, null, null, null); + zmsImpl.putServiceIdentity(ctx, "sports.dev", "api", auditRef, false, null, service3); + + // add a group with the domain filter + + final String groupName1 = "filter-group1"; + Group group1 = zmsTestInitializer.createGroupObject(domainName, groupName1, null, null); + group1.setPrincipalDomainFilter("user,+sports,-sports.prod"); + zmsImpl.putGroup(ctx, domainName, groupName1, auditRef, false, null, group1); + + // user.user1 should be able to be added to the group + + GroupMembership membership = new GroupMembership().setMemberName("user.user1"); + zmsImpl.putGroupMembership(ctx, domainName, groupName1, "user.user1", auditRef, false, null, membership); + + // sports.api should be able to be added to the group + + membership = new GroupMembership().setMemberName("sports.api"); + zmsImpl.putGroupMembership(ctx, domainName, groupName1, "sports.api", auditRef, false, null, membership); + + // sports.dev.api should be able to be added to the group + + membership = new GroupMembership().setMemberName("sports.dev.api"); + zmsImpl.putGroupMembership(ctx, domainName, groupName1, "sports.dev.api", auditRef, false, null, membership); + + // sports.prod.api should be rejected + + membership = new GroupMembership().setMemberName("sports.prod.api"); + try { + zmsImpl.putGroupMembership(ctx, domainName, groupName1, "sports.prod.api", auditRef, false, null, membership); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), 400); + assertEquals(ex.getMessage().contains("Principal sports.prod.api is not allowed for the group"), true); + } + + zmsImpl.deleteSubDomain(ctx, "sports", "dev", auditRef, null); + zmsImpl.deleteSubDomain(ctx, "sports", "prod", auditRef, null); + zmsImpl.deleteTopLevelDomain(ctx, "sports", auditRef, null); + zmsImpl.deleteTopLevelDomain(ctx, domainName, auditRef, null); + } + + @Test + public void testUnknownDomainFilterName() { + + ZMSImpl zmsImpl = zmsTestInitializer.getZms(); + RsrcCtxWrapper ctx = zmsTestInitializer.getMockDomRsrcCtx(); + final String auditRef = zmsTestInitializer.getAuditRef(); + + final String domainName = "unknown-domain-filter-name"; + TopLevelDomain dom1 = zmsTestInitializer.createTopLevelDomainObject(domainName, + "Test Domain1", "testOrg", "user.user1"); + zmsImpl.postTopLevelDomain(ctx, auditRef, null, dom1); + + try { + Role role1 = zmsTestInitializer.createRoleObject(domainName, "role1", null, null); + role1.setPrincipalDomainFilter("user,unknown-domain-name"); + zmsImpl.putRole(ctx, domainName, "role1", auditRef, false, null, role1); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), 400); + assertTrue(ex.getMessage().contains("No such domain: unknown-domain-name")); + } + + try { + Group group1 = zmsTestInitializer.createGroupObject(domainName, "group1", null, null); + group1.setPrincipalDomainFilter("user,unknown-domain-name"); + zmsImpl.putGroup(ctx, domainName, "group1", auditRef, false, null, group1); + fail(); + } catch (ResourceException ex) { + assertEquals(ex.getCode(), 400); + assertTrue(ex.getMessage().contains("No such domain: unknown-domain-name")); + } + + zmsImpl.deleteTopLevelDomain(ctx, domainName, auditRef, null); + } +}