diff options
author | Jon Marius Venstad <venstad@gmail.com> | 2019-04-04 16:54:27 +0200 |
---|---|---|
committer | Jon Marius Venstad <venstad@gmail.com> | 2019-04-04 16:54:27 +0200 |
commit | 80c249f94ade782c3b1f2dd2781f089d1245f34a (patch) | |
tree | a46657d3740b8dc06c3af0a2ba15e17cf84a833c | |
parent | f83e2033db821695a7984577a1ee18c617fc0b55 (diff) |
Add ID classes and interface for user management
4 files changed, 263 insertions, 0 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/GroupId.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/GroupId.java new file mode 100644 index 00000000000..e0a363f4cb4 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/GroupId.java @@ -0,0 +1,116 @@ +package com.yahoo.vespa.hosted.controller.api.integration.user; + +import com.yahoo.config.provision.ApplicationName; +import com.yahoo.config.provision.TenantName; +import com.yahoo.vespa.hosted.controller.api.role.ApplicationRole; +import com.yahoo.vespa.hosted.controller.api.role.ProtoRole; +import com.yahoo.vespa.hosted.controller.api.role.Role; +import com.yahoo.vespa.hosted.controller.api.role.Roles; +import com.yahoo.vespa.hosted.controller.api.role.TenantRole; + +import java.util.Objects; + +/** + * An identifier for a group of {@link UserId}s, corresponding to a bound {@link Role}. + * + * @author jonmv + */ +public class GroupId { + + private final String value; + + private GroupId(String value) { + if (value.isBlank()) + throw new IllegalArgumentException("Id value must be non-blank."); + this.value = value; + } + + public static GroupId fromRole(TenantRole role) { + return new GroupId(valueOf(role)); + } + + public static GroupId fromRole(ApplicationRole role) { + return new GroupId(valueOf(role)); + } + + public static GroupId fromValue(String value) { + return new GroupId(value); + } + + public String value() { + return value; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + GroupId id = (GroupId) o; + return Objects.equals(value, id.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + + @Override + public String toString() { + return "group '" + value + "'"; + } + + /** Returns the {@link Role} this represent. */ + public Role toRole(Roles roles) { + String[] parts = value.split("\\."); + if (parts.length == 2) switch (parts[1]) { + case "tenantOwner": return roles.tenantOwner(TenantName.from(parts[0])); + case "tenantAdmin": return roles.tenantAdmin(TenantName.from(parts[0])); + case "tenantOperator": return roles.tenantOperator(TenantName.from(parts[0])); + } + if (parts.length == 3) switch (parts[2]) { + case "applicationOwner": return roles.applicationOwner(TenantName.from(parts[0]), ApplicationName.from(parts[1])); + case "applicationAdmin": return roles.applicationAdmin(TenantName.from(parts[0]), ApplicationName.from(parts[1])); + case "applicationOperator": return roles.applicationOperator(TenantName.from(parts[0]), ApplicationName.from(parts[1])); + case "applicationDeveloper": return roles.applicationDeveloper(TenantName.from(parts[0]), ApplicationName.from(parts[1])); + case "applicationReader": return roles.applicationReader(TenantName.from(parts[0]), ApplicationName.from(parts[1])); + } + throw new IllegalArgumentException("Malformed or illegal role value '" + value + "'."); + } + + private static String valueOf(TenantRole role) { + return valueOf(role.tenant()) + "." + valueOf(role.proto()); + } + + private static String valueOf(ApplicationRole role) { + return valueOf(role.tenant()) + "." + valueOf(role.application()) + "." + valueOf(role.proto()); + } + + private static String valueOf(TenantName tenant) { + if (tenant.value().contains(".")) + throw new IllegalArgumentException("Tenant names may not contain '.'."); + + return tenant.value(); + } + + private static String valueOf(ApplicationName application) { + if (application.value().contains(".")) + throw new IllegalArgumentException("Application names may not contain '.'."); + + return application.value(); + } + + private static String valueOf(ProtoRole role) { + switch (role) { + case tenantOwner: return "tenantOwner"; + case tenantAdmin: return "tenantAdmin"; + case tenantOperator: return "tenantOperator"; + case applicationOwner: return "applicationOwner"; + case applicationAdmin: return "applicationAdmin"; + case applicationOperator: return "applicationOperator"; + case applicationDeveloper: return "applicationDeveloper"; + case applicationReader: return "applicationReader"; + default: throw new IllegalArgumentException("No value defined for role '" + role + "'."); + } + } + +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserId.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserId.java new file mode 100644 index 00000000000..3b138d0ce18 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserId.java @@ -0,0 +1,40 @@ +package com.yahoo.vespa.hosted.controller.api.integration.user; + +import java.util.Objects; + +/** + * An identifier for a user. + * + * @author jonmv + */ +public class UserId { + + private final String value; + + public UserId(String value) { + if (value.isBlank()) + throw new IllegalArgumentException("Id must be non-blank."); + this.value = value; + } + + public String value() { return value; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + UserId id = (UserId) o; + return Objects.equals(value, id.value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + + @Override + public String toString() { + return "user '" + value + "'"; + } + +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserManagement.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserManagement.java new file mode 100644 index 00000000000..9e02e70a168 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserManagement.java @@ -0,0 +1,33 @@ +package com.yahoo.vespa.hosted.controller.api.integration.user; + +import com.yahoo.vespa.hosted.controller.api.role.Role; + +import java.util.Collection; +import java.util.List; + +/** + * Management of {@link UserId}s and {@link GroupId}s, used for access control with {@link Role}s. + * + * @author jonmv + */ +public interface UserManagement { + + /** Creates the given group, or throws if the group already exists. */ + void createGroup(GroupId group); + + /** Deletes the given group, or throws if it doesn't already exist.. */ + void deleteGroup(GroupId group); + + /** Ensures the given users exist, and are part of the given group, or throws if the group does not exist. */ + void addUsers(GroupId group, Collection<UserId> users); + + /** Ensures none of the given users are part of the given group, or throws if the group does not exist. */ + void removeUsers(GroupId group, Collection<UserId> users); + + /** Returns all known groups. */ + List<GroupId> listGroups(); + + /** Returns all users in the given group, or throws if the group does not exist. */ + List<UserId> listUsers(GroupId gruop); + +} diff --git a/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/user/GroupIdTest.java b/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/user/GroupIdTest.java new file mode 100644 index 00000000000..acb9b3b245b --- /dev/null +++ b/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/user/GroupIdTest.java @@ -0,0 +1,74 @@ +package com.yahoo.vespa.hosted.controller.api.integration.user; + +import com.yahoo.config.provision.ApplicationName; +import com.yahoo.config.provision.SystemName; +import com.yahoo.config.provision.TenantName; +import com.yahoo.vespa.hosted.controller.api.role.ApplicationRole; +import com.yahoo.vespa.hosted.controller.api.role.Roles; +import com.yahoo.vespa.hosted.controller.api.role.TenantRole; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + * @author jonmv + */ +public class GroupIdTest { + + @Test + public void testSerialization() { + Roles roles = new Roles(SystemName.main); + + TenantName tenant = TenantName.from("my-tenant"); + for (TenantRole role : List.of(roles.tenantOwner(tenant), + roles.tenantAdmin(tenant), + roles.tenantOperator(tenant))) + assertEquals(role, GroupId.fromRole(role).toRole(roles)); + + ApplicationName application = ApplicationName.from("my-application"); + for (ApplicationRole role : List.of(roles.applicationOwner(tenant, application), + roles.applicationAdmin(tenant, application), + roles.applicationOperator(tenant, application), + roles.applicationDeveloper(tenant, application), + roles.applicationReader(tenant, application))) + assertEquals(role, GroupId.fromRole(role).toRole(roles)); + + assertEquals(roles.tenantOperator(tenant), + GroupId.fromValue("my-tenant.tenantOperator").toRole(roles)); + assertEquals(roles.applicationReader(tenant, application), + GroupId.fromValue("my-tenant.my-application.applicationReader").toRole(roles)); + } + + @Test(expected = IllegalArgumentException.class) + public void illegalTenantName() { + GroupId.fromRole(new Roles(SystemName.main).tenantAdmin(TenantName.from("my.tenant"))); + } + + @Test(expected = IllegalArgumentException.class) + public void illegalApplicationName() { + GroupId.fromRole(new Roles(SystemName.main).applicationOperator(TenantName.from("my-tenant"), ApplicationName.from("my.app"))); + } + + @Test(expected = IllegalArgumentException.class) + public void illegalRole() { + GroupId.fromRole(new Roles(SystemName.main).tenantPipeline(TenantName.from("my-tenant"), ApplicationName.from("my-app"))); + } + + @Test(expected = IllegalArgumentException.class) + public void illegalRoleValue() { + GroupId.fromValue("my-tenant.awesomePerson").toRole(new Roles(SystemName.cd)); + } + + @Test(expected = IllegalArgumentException.class) + public void illegalCombination() { + GroupId.fromValue("my-tenant.my-application.tenantOwner").toRole(new Roles(SystemName.cd)); + } + + @Test(expected = IllegalArgumentException.class) + public void illegalValue() { + GroupId.fromValue("hostedOperator").toRole(new Roles(SystemName.Public)); + } + +} |