aboutsummaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
authorJon Marius Venstad <jvenstad@yahoo-inc.com>2019-04-09 16:32:09 +0200
committerJon Marius Venstad <jvenstad@yahoo-inc.com>2019-04-09 16:32:09 +0200
commit5dd6cc0f37e17c15082e5b80566977b95898df59 (patch)
tree555698e80fcecfc68f7276226f3b61cceafb349f /controller-server
parent8c0dd10b628295b7add03a01cd4efc8d6620dcb6 (diff)
Unit test user management and application APIs with alternative components
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java146
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/application-created.json6
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/application-roles.json54
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-roles.json44
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-without-applications.json5
5 files changed, 246 insertions, 9 deletions
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java
index a98b2b60bf8..caf0ec82aae 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java
@@ -1,24 +1,152 @@
package com.yahoo.vespa.hosted.controller.restapi.user;
-import com.yahoo.vespa.hosted.controller.restapi.ContainerControllerTester;
-import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest;
-import org.junit.Ignore;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.config.provision.SystemName;
+import com.yahoo.config.provision.TenantName;
+import com.yahoo.vespa.hosted.controller.api.role.Role;
+import com.yahoo.vespa.hosted.controller.api.role.Roles;
+import com.yahoo.vespa.hosted.controller.restapi.ContainerTester;
+import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerCloudTest;
import org.junit.Test;
+import java.io.File;
+import java.util.Set;
+
+import static com.yahoo.application.container.handler.Request.Method.DELETE;
+import static com.yahoo.application.container.handler.Request.Method.POST;
+import static com.yahoo.application.container.handler.Request.Method.PUT;
+import static org.junit.Assert.assertEquals;
+
/**
* @author jonmv
*/
-public class UserApiTest extends ControllerContainerTest {
+public class UserApiTest extends ControllerContainerCloudTest {
private static final String responseFiles = "src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/";
@Test
- @Ignore // TODO set up separate services.xlm for this kind of controller and unit test there.
- public void testUserApi() {
- ContainerControllerTester tester = new ContainerControllerTester(container, responseFiles);
+ public void testUserManagement() {
+ ContainerTester tester = new ContainerTester(container, responseFiles);
+ assertEquals(SystemName.Public, tester.controller().system());
+ Roles roles = new Roles(tester.controller().system());
+ Set<Role> operator = Set.of(roles.hostedOperator());
+ ApplicationId id = ApplicationId.from("my-tenant", "my-app", "default");
+
+
+ // GET at application/v4 root fails as it's not public read.
+ tester.assertResponse(request("/application/v4/"),
+ accessDenied, 403);
+
+ // GET at application/v4/tenant succeeds for operators.
+ tester.assertResponse(request("/application/v4/tenant")
+ .roles(operator),
+ "[]");
+
+ // POST a tenant is not available to everyone.
+ tester.assertResponse(request("/application/v4/tenant/my-tenant", POST)
+ .data("{\"token\":\"hello\"}"),
+ accessDenied, 403);
+
+ // POST a tenant is available to operators.
+ tester.assertResponse(request("/application/v4/tenant/my-tenant", POST)
+ .roles(operator)
+ .user("owner@tenant")
+ .data("{\"token\":\"hello\"}"),
+ new File("tenant-without-applications.json"));
+
+ // PUT a tenant is not available to anyone.
+ tester.assertResponse(request("/application/v4/user/", PUT)
+ .roles(operator),
+ "{\"error-code\":\"FORBIDDEN\",\"message\":\"Not authenticated or not a user.\"}", 403);
+
+ // GET at user/v1 root fails as no access control is defined there.
+ tester.assertResponse(request("/user/v1/"),
+ accessDenied, 403);
+
+ // POST a hosted operator role is not allowed.
+ tester.assertResponse(request("/user/v1/tenant/my-tenant", POST)
+ .roles(Set.of(roles.tenantOwner(id.tenant())))
+ .data("{\"user\":\"evil@evil\",\"roleName\":\"hostedOperator\"}"),
+ "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Malformed or illegal role name 'hostedOperator'.\"}", 400);
+
+ // POST a tenant operator is available to the tenant owner.
+ tester.assertResponse(request("/user/v1/tenant/my-tenant", POST)
+ .roles(Set.of(roles.tenantOwner(id.tenant())))
+ .data("{\"user\":\"operator@tenant\",\"roleName\":\"tenantOperator\"}"),
+ "{\"message\":\"user 'operator@tenant' is now a member of role 'tenantOperator' of 'my-tenant'\"}");
+
+ // POST a tenant admin is not available to a tenant operator.
+ tester.assertResponse(request("/user/v1/tenant/my-tenant", POST)
+ .roles(Set.of(roles.tenantOperator(id.tenant())))
+ .data("{\"user\":\"admin@tenant\",\"roleName\":\"tenantAdmin\"}"),
+ accessDenied, 403);
+
+ // POST an application admin for a non-existent application fails.
+ tester.assertResponse(request("/user/v1/tenant/my-tenant/application/my-app", POST)
+ .roles(Set.of(roles.tenantOwner(TenantName.from("my-tenant"))))
+ .data("{\"user\":\"admin@app\",\"roleName\":\"applicationAdmin\"}"),
+ "{\"error-code\":\"INTERNAL_SERVER_ERROR\",\"message\":\"NullPointerException\"}", 500);
+
+ // POST an application is allowed for a tenant operator.
+ tester.assertResponse(request("/application/v4/tenant/my-tenant/application/my-app", POST)
+ .user("operator@tenant")
+ .roles(Set.of(roles.tenantOperator(id.tenant()))),
+ new File("application-created.json"));
+
+ // POST an application is not allowed under a different tenant.
+ tester.assertResponse(request("/application/v4/tenant/other-tenant/application/my-app", POST)
+ .roles(Set.of(roles.tenantOperator(id.tenant()))),
+ accessDenied, 403);
+
+ // POST an application role is allowed for a tenant admin.
+ tester.assertResponse(request("/user/v1/tenant/my-tenant/application/my-app", POST)
+ .roles(Set.of(roles.tenantAdmin(id.tenant())))
+ .data("{\"user\":\"reader@app\",\"roleName\":\"applicationReader\"}"),
+ "{\"message\":\"user 'reader@app' is now a member of role 'applicationReader' of 'my-app' owned by 'my-tenant'\"}");
+
+ // POST a tenant role is not allowed to an application.
+ tester.assertResponse(request("/user/v1/tenant/my-tenant/application/my-app", POST)
+ .roles(Set.of(roles.hostedOperator()))
+ .data("{\"user\":\"reader@app\",\"roleName\":\"tenantOperator\"}"),
+ "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Malformed or illegal role name 'tenantOperator'.\"}", 400);
+
+ // GET tenant role information is available to application readers.
+ tester.assertResponse(request("/user/v1/tenant/my-tenant")
+ .roles(Set.of(roles.applicationReader(id.tenant(), id.application()))),
+ new File("tenant-roles.json"));
+
+ // GET application role information is available to tenant operators.
+ tester.assertResponse(request("/user/v1/tenant/my-tenant/application/my-app")
+ .roles(Set.of(roles.tenantOperator(id.tenant()))),
+ new File("application-roles.json"));
+
+ // DELETE an application role is allowed for an application admin.
+ tester.assertResponse(request("/user/v1/tenant/my-tenant/application/my-app", DELETE)
+ .roles(Set.of(roles.applicationAdmin(id.tenant(), id.application())))
+ .data("{\"user\":\"operator@tenant\",\"roleName\":\"applicationAdmin\"}"),
+ "{\"message\":\"user 'operator@tenant' is no longer a member of role 'applicationAdmin' of 'my-app' owned by 'my-tenant'\"}");
+
+ // DELETE an application is available to application admins.
+ tester.assertResponse(request("/application/v4/tenant/my-tenant/application/my-app", DELETE)
+ .roles(Set.of(roles.applicationAdmin(id.tenant(), id.application()))),
+ "");
+
+ // DELETE a tenant role is available to tenant admins.
+ tester.assertResponse(request("/user/v1/tenant/my-tenant", DELETE)
+ .roles(Set.of(roles.tenantAdmin(id.tenant())))
+ .data("{\"user\":\"operator@tenant\",\"roleName\":\"tenantOperator\"}"),
+ "{\"message\":\"user 'operator@tenant' is no longer a member of role 'tenantOperator' of 'my-tenant'\"}");
+
+ // DELETE the last tenant owner is not allowed.
+ tester.assertResponse(request("/user/v1/tenant/my-tenant", DELETE)
+ .roles(operator)
+ .data("{\"user\":\"owner@tenant\",\"roleName\":\"tenantOwner\"}"),
+ "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Can't remove the last owner of a tenant.\"}", 400);
- tester.assertResponse(authenticatedRequest("http://localhost:8080/user/v1/"),
- "{\"error-code\":\"NOT_FOUND\",\"message\":\"No 'GET' handler at '/user/v1/'\"}", 404);
+ // DELETE the tenant is available to the tenant owner.
+ tester.assertResponse(request("/application/v4/tenant/my-tenant", DELETE)
+ .roles(Set.of(roles.tenantOwner(id.tenant()))),
+ new File("tenant-without-applications.json"));
}
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/application-created.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/application-created.json
new file mode 100644
index 00000000000..2a779f0ee55
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/application-created.json
@@ -0,0 +1,6 @@
+{
+ "tenant": "my-tenant",
+ "application": "my-app",
+ "instance": "default",
+ "url": "http://localhost:8080/application/v4/tenant/my-tenant/application/my-app"
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/application-roles.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/application-roles.json
new file mode 100644
index 00000000000..e6ecf86c4ec
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/application-roles.json
@@ -0,0 +1,54 @@
+{
+ "tenant": "my-tenant",
+ "application": "my-app",
+ "roleNames": [
+ "applicationAdmin",
+ "applicationOperator",
+ "applicationDeveloper",
+ "applicationReader"
+ ],
+ "users": [
+ {
+ "name": "reader@app",
+ "roles": {
+ "applicationAdmin": {
+ "explicit": false,
+ "implied": false
+ },
+ "applicationOperator": {
+ "explicit": false,
+ "implied": false
+ },
+ "applicationDeveloper": {
+ "explicit": false,
+ "implied": false
+ },
+ "applicationReader": {
+ "explicit": true,
+ "implied": false
+ }
+ }
+ },
+ {
+ "name": "operator@tenant",
+ "roles": {
+ "applicationAdmin": {
+ "explicit": true,
+ "implied": false
+ },
+ "applicationOperator": {
+ "explicit": false,
+ "implied": true
+ },
+ "applicationDeveloper": {
+ "explicit": false,
+ "implied": true
+ },
+ "applicationReader": {
+ "explicit": false,
+ "implied": true
+ }
+ }
+ }
+ ]
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-roles.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-roles.json
new file mode 100644
index 00000000000..46f715b481b
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-roles.json
@@ -0,0 +1,44 @@
+{
+ "tenant": "my-tenant",
+ "roleNames": [
+ "tenantOwner",
+ "tenantAdmin",
+ "tenantOperator"
+ ],
+ "users": [
+ {
+ "name": "operator@tenant",
+ "roles": {
+ "tenantOwner": {
+ "explicit": false,
+ "implied": false
+ },
+ "tenantAdmin": {
+ "explicit": false,
+ "implied": false
+ },
+ "tenantOperator": {
+ "explicit": true,
+ "implied": false
+ }
+ }
+ },
+ {
+ "name": "owner@tenant",
+ "roles": {
+ "tenantOwner": {
+ "explicit": true,
+ "implied": false
+ },
+ "tenantAdmin": {
+ "explicit": false,
+ "implied": true
+ },
+ "tenantOperator": {
+ "explicit": false,
+ "implied": true
+ }
+ }
+ }
+ ]
+}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-without-applications.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-without-applications.json
new file mode 100644
index 00000000000..e4ca5a02446
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/responses/tenant-without-applications.json
@@ -0,0 +1,5 @@
+{
+ "tenant": "my-tenant",
+ "type": "CLOUD",
+ "applications": []
+}