summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Marius Venstad <venstad@gmail.com>2019-04-04 16:54:27 +0200
committerJon Marius Venstad <venstad@gmail.com>2019-04-04 16:54:27 +0200
commit80c249f94ade782c3b1f2dd2781f089d1245f34a (patch)
treea46657d3740b8dc06c3af0a2ba15e17cf84a833c
parentf83e2033db821695a7984577a1ee18c617fc0b55 (diff)
Add ID classes and interface for user management
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/GroupId.java116
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserId.java40
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserManagement.java33
-rw-r--r--controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/user/GroupIdTest.java74
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));
+ }
+
+}