diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorMediaType.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorMediaType.java index be26e4b8c0..3b11b91782 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorMediaType.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/VCloudDirectorMediaType.java @@ -153,6 +153,10 @@ public class VCloudDirectorMediaType { public static final String ADMIN_ORG_NETWORK = "application/vnd.vmware.admin.orgNetwork+xml"; + public static final String USER = "application/vnd.vmware.admin.user+xml"; + + public static final String ROLE = "application/vnd.vmware.admin.role+xml"; + /** * All acceptable media types. * @@ -173,6 +177,6 @@ public class VCloudDirectorMediaType { PUBLISH_CATALOG_PARAMS, GROUP, ORG_VAPP_TEMPLATE_LEASE_SETTINGS, ORG_LEASE_SETTINGS, ORG_PASSWORD_POLICY_SETTINGS, ORG_LDAP_SETTINGS, ORG_GENERAL_SETTINGS, ORG_EMAIL_SETTINGS, ORG_SETTINGS, ADMIN_NETWORK, - ADMIN_ORG_NETWORK + ADMIN_ORG_NETWORK, USER, ROLE ); } diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/User.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/User.java index 2ff0303b55..d3c91e8a31 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/User.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/domain/User.java @@ -30,6 +30,8 @@ import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; +import org.testng.collections.Lists; + import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.Sets; @@ -125,7 +127,7 @@ public class User private Integer deployedVmQuota; private Reference role; private String password; - private List groups; + private List groups = Lists.newArrayList(); /** * @see User#getFullName() @@ -267,7 +269,7 @@ public class User * @see User#getGroups() */ public Builder groups(List groups) { - this.groups = ImmutableList.copyOf(groups); + this.groups = groups == null ? null : ImmutableList.copyOf(groups); return this; } @@ -287,6 +289,14 @@ public class User role, password, groups); } + /** + * @see EntityType#getName() + */ + @Override + public Builder name(String name) { + this.name = name; + return this; + } /** * @see EntityType#getId() @@ -616,7 +626,7 @@ public class User * {@link GroupsListType } */ public List getGroups() { - return groups; + return groups == null ? Lists.newArrayList() : groups; } @Override @@ -690,5 +700,4 @@ public class User .add("password", password) .add("groups", groups).toString(); } - } diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/UserAsyncClient.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/UserAsyncClient.java index 24c80c9c45..efec47c519 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/UserAsyncClient.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/UserAsyncClient.java @@ -18,8 +18,27 @@ */ package org.jclouds.vcloud.director.v1_5.features; +import java.net.URI; + +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; + +import org.jclouds.rest.annotations.BinderParam; +import org.jclouds.rest.annotations.EndpointParam; +import org.jclouds.rest.annotations.ExceptionParser; +import org.jclouds.rest.annotations.JAXBResponseParser; import org.jclouds.rest.annotations.RequestFilters; +import org.jclouds.rest.binders.BindToXMLPayload; +import org.jclouds.vcloud.director.v1_5.VCloudDirectorMediaType; +import org.jclouds.vcloud.director.v1_5.domain.User; import org.jclouds.vcloud.director.v1_5.filters.AddVCloudAuthorizationToRequest; +import org.jclouds.vcloud.director.v1_5.functions.ThrowVCloudErrorOn4xx; + +import com.google.common.util.concurrent.ListenableFuture; /** * @see GroupClient @@ -27,13 +46,37 @@ import org.jclouds.vcloud.director.v1_5.filters.AddVCloudAuthorizationToRequest; */ @RequestFilters(AddVCloudAuthorizationToRequest.class) public interface UserAsyncClient { -// POST /admin/org/{id}/users + /** + * @see UserClient#createUser(URI, User) + */ + @POST + @Path("/users") + @Consumes(VCloudDirectorMediaType.USER) + @Produces(VCloudDirectorMediaType.USER) + @JAXBResponseParser + @ExceptionParser(ThrowVCloudErrorOn4xx.class) + ListenableFuture createUser(@EndpointParam URI orgRef, + @BinderParam(BindToXMLPayload.class) User user); -// GET /admin/user/{id} + /** + * @see UserClient#getUser(URI) + */ + @GET + @Consumes + @JAXBResponseParser + @ExceptionParser(ThrowVCloudErrorOn4xx.class) + ListenableFuture getUser(@EndpointParam URI userRef); // PUT /admin/user/{id} -// DELETE /admin/user/{id} + /** + * @see UserClient#deleteUser(URI) + */ + @DELETE + @Consumes + @JAXBResponseParser + @ExceptionParser(ThrowVCloudErrorOn4xx.class) + ListenableFuture deleteUser(@EndpointParam URI userRef); // POST /admin/user/{id}/action/unlock } diff --git a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/UserClient.java b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/UserClient.java index 694efaf5a5..a69bbac130 100644 --- a/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/UserClient.java +++ b/labs/vcloud-director/src/main/java/org/jclouds/vcloud/director/v1_5/features/UserClient.java @@ -18,10 +18,12 @@ */ package org.jclouds.vcloud.director.v1_5.features; +import java.net.URI; import java.util.concurrent.TimeUnit; import org.jclouds.concurrent.Timeout; import org.jclouds.vcloud.director.v1_5.domain.Group; +import org.jclouds.vcloud.director.v1_5.domain.User; /** * Provides synchronous access to {@link Group} objects. @@ -31,13 +33,40 @@ import org.jclouds.vcloud.director.v1_5.domain.Group; */ @Timeout(duration = 180, timeUnit = TimeUnit.SECONDS) public interface UserClient { -// POST /admin/org/{id}/users + /** + * Creates or imports a user in an organization. The user could be enabled or disabled. + * + *
+    * POST /admin/org/{id}/users
+    * 
+ * + * @param orgRef the reference for the org + * @return the created user + */ + User createUser(URI orgRef, User user); -// GET /admin/user/{id} + /** + * Retrieves a user. This entity could be enabled or disabled. + * + *
+    * GET /admin/user/{id}
+    * 
+ * + * @param userRef the reference for the user + * @return a user + */ + User getUser(URI userRef); // PUT /admin/user/{id} -// DELETE /admin/user/{id} + /** + * Deletes a user. Enabled and disabled users could be deleted. + * + *
+    * DELETE /admin/catalog/{id}
+    * 
+ */ + void deleteUser(URI userRef); // POST /admin/user/{id}/action/unlock } diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/UserClientExpectTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/UserClientExpectTest.java index 0100e1aecd..f9a1ac83dd 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/UserClientExpectTest.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/UserClientExpectTest.java @@ -18,9 +18,16 @@ */ package org.jclouds.vcloud.director.v1_5.features; -import java.net.URI; +import static org.testng.Assert.assertEquals; +import java.net.URI; +import java.util.Collections; + +import org.jclouds.vcloud.director.v1_5.VCloudDirectorClient; +import org.jclouds.vcloud.director.v1_5.VCloudDirectorMediaType; +import org.jclouds.vcloud.director.v1_5.domain.Link; import org.jclouds.vcloud.director.v1_5.domain.Reference; +import org.jclouds.vcloud.director.v1_5.domain.User; import org.jclouds.vcloud.director.v1_5.internal.BaseVCloudDirectorRestClientExpectTest; import org.testng.annotations.Test; @@ -32,17 +39,113 @@ import org.testng.annotations.Test; @Test(groups = { "unit", "admin", "adminUser"}, singleThreaded = true, testName = "UserClientExpectTest") public class UserClientExpectTest extends BaseVCloudDirectorRestClientExpectTest { - private Reference userRef = Reference.builder() - .href(URI.create(endpoint + "/admin/user/???")) + private Reference orgRef = Reference.builder() + .href(URI.create(endpoint + "/admin/org/6f312e42-cd2b-488d-a2bb-97519cd57ed0")) .build(); -// POST /admin/org/{id}/users + private Reference userRef = Reference.builder() + .href(URI.create(endpoint + "/admin/user/b37223f3-8792-477a-820f-334998f61cd6")) + .build(); -// GET /admin/user/{id} + @Test + public void testCreateUser() { + VCloudDirectorClient client = requestsSendResponses(loginRequest, sessionResponse, + new VcloudHttpRequestPrimer() + .apiCommand("POST", "/admin/org/6f312e42-cd2b-488d-a2bb-97519cd57ed0/catalogs") + .xmlFilePayload("/user/createUserSource.xml", VCloudDirectorMediaType.USER) + .acceptMedia(VCloudDirectorMediaType.USER) + .httpRequestBuilder().build(), + new VcloudHttpResponsePrimer() + .xmlFilePayload("/user/createUser.xml", VCloudDirectorMediaType.USER) + .httpResponseBuilder().build()); + + User source = createUserSource(); + User expected = createUser(); + + assertEquals(client.getUserClient().createUser(orgRef.getHref(), source), expected); + } + + public static final User createUserSource() { + return User.builder() + .name("test") + .fullName("testFullName") + .emailAddress("test@test.com") + .telephone("555-1234") + .isEnabled(false) + .im("testIM") + .isAlertEnabled(false) + .alertEmailPrefix("testPrefix") + .alertEmail("testAlert@test.com") + .isExternal(false) + .isGroupRole(false) + .role(Reference.builder() + .type("application/vnd.vmware.admin.role+xml") + .name("vApp User") + .href(URI.create("https://vcloudbeta.bluelock.com/api/admin/role/ff1e0c91-1288-3664-82b7-a6fa303af4d1")) + .build()) + .password("password") + .groups(Collections.emptyList()) + .build(); + } + + public static final User createUser() { + return createUserSource().toBuilder() + .id("urn:vcloud:user:b37223f3-8792-477a-820f-334998f61cd6") + .type("application/vnd.vmware.admin.user+xml") + .href(URI.create("https://vcloudbeta.bluelock.com/api/admin/user/b37223f3-8792-477a-820f-334998f61cd6")) + .link(Link.builder() + .rel("edit") + .type("application/vnd.vmware.admin.user+xml") + .href(URI.create("https://vcloudbeta.bluelock.com/api/admin/user/b37223f3-8792-477a-820f-334998f61cd6")) + .build()) + .link(Link.builder() + .rel("remove") + .href(URI.create("https://vcloudbeta.bluelock.com/api/admin/user/b37223f3-8792-477a-820f-334998f61cd6")) + .build()) + .isLocked(false) + .isDefaultCached(false) + .storedVmQuota(0) + .deployedVmQuota(0) + .password(null) + .build(); + } + + @Test + public void testGetUser() { + VCloudDirectorClient client = requestsSendResponses(loginRequest, sessionResponse, + new VcloudHttpRequestPrimer() + .apiCommand("GET", "/admin/user/b37223f3-8792-477a-820f-334998f61cd6") + .acceptAnyMedia() + .httpRequestBuilder().build(), + new VcloudHttpResponsePrimer() + .xmlFilePayload("/user/user.xml", VCloudDirectorMediaType.USER) + .httpResponseBuilder().build()); + + User expected = user(); + + assertEquals(client.getUserClient().getUser(userRef.getHref()), expected); + } + + public static final User user() { + return createUser().toBuilder() + .nameInSource("test") + .build(); + } // PUT /admin/user/{id} -// DELETE /admin/user/{id} - // POST /admin/user/{id}/action/unlock + + @Test + public void testDeleteUser() { + VCloudDirectorClient client = requestsSendResponses(loginRequest, sessionResponse, + new VcloudHttpRequestPrimer() + .apiCommand("DELETE", "/admin/user/b37223f3-8792-477a-820f-334998f61cd6") + .acceptAnyMedia() + .httpRequestBuilder().build(), + new VcloudHttpResponsePrimer() + .httpResponseBuilder().statusCode(204).build()); + + client.getUserClient().deleteUser(userRef.getHref()); + } } diff --git a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/UserClientLiveTest.java b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/UserClientLiveTest.java index 5e715b70bc..0b317cfb87 100644 --- a/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/UserClientLiveTest.java +++ b/labs/vcloud-director/src/test/java/org/jclouds/vcloud/director/v1_5/features/UserClientLiveTest.java @@ -18,10 +18,25 @@ */ package org.jclouds.vcloud.director.v1_5.features; +import static org.jclouds.vcloud.director.v1_5.VCloudDirectorLiveTestConstants.OBJ_DEL; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.fail; + +import java.net.URI; + +import org.jclouds.vcloud.director.v1_5.VCloudDirectorException; +import org.jclouds.vcloud.director.v1_5.domain.Checks; +import org.jclouds.vcloud.director.v1_5.domain.Error; +import org.jclouds.vcloud.director.v1_5.domain.Reference; +import org.jclouds.vcloud.director.v1_5.domain.ReferenceType; +import org.jclouds.vcloud.director.v1_5.domain.User; import org.jclouds.vcloud.director.v1_5.internal.BaseVCloudDirectorClientLiveTest; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import com.google.common.collect.Iterables; + /** * Tests live behavior of {@link AdminGroupClient}. * @@ -30,28 +45,88 @@ import org.testng.annotations.Test; @Test(groups = { "live", "admin", "adminUser" }, singleThreaded = true, testName = "UserClientLiveTest") public class UserClientLiveTest extends BaseVCloudDirectorClientLiveTest { - public static final String GROUP = "admin group"; + public static final String USER = "admin user"; /* * Convenience references to API clients. */ + UserClient userClient; /* * Shared state between dependant tests. */ + private ReferenceType orgRef; + private User user; @Override @BeforeClass(inheritGroups = true) public void setupRequiredClients() { + userClient = context.getApi().getUserClient(); + orgRef = Iterables.getFirst(context.getApi().getOrgClient().getOrgList().getOrgs(), null).toAdminReference(endpoint); } -// POST /admin/org/{id}/users + @Test(testName = "POST /admin/org/{id}/users") + public void testCreateUser() { + User newUser = User.builder() + .name("test") + .fullName("testFullName") + .emailAddress("test@test.com") + .telephone("555-1234") + .isEnabled(false) + .im("testIM") + .isAlertEnabled(false) + .alertEmailPrefix("testPrefix") + .alertEmail("testAlert@test.com") + .isExternal(false) + .isGroupRole(false) + .role(Reference.builder() // FIXME: auto-fetch a role? or inject + .name("vApp User") + .href(URI.create("https://vcloudbeta.bluelock.com/api/admin/role/ff1e0c91-1288-3664-82b7-a6fa303af4d1")) + .build()) + .password("password") +// .group() + .build(); + user = userClient.createUser(orgRef.getHref(), newUser); + + Checks.checkUser(user); + } -// GET /admin/user/{id} + @Test(testName = "GET /admin/user/{id}", + dependsOnMethods = { "testCreateUser" }) + public void testGetUser() { + user = userClient.getUser(user.getHref()); + + Checks.checkUser(user); + } // PUT /admin/user/{id} -// DELETE /admin/user/{id} - // POST /admin/user/{id}/action/unlock + + @Test(testName = "DELETE /admin/user/{id}", + dependsOnMethods = { "testGetUser" } ) + public void testDeleteUser() { + userClient.deleteUser(user.getHref()); + + Error expected = Error.builder() + .message("No access to entity \"(com.vmware.vcloud.entity.user:"+ + user.getId().substring("urn:vcloud:user:".length())+")\".") + .majorErrorCode(403) + .minorErrorCode("ACCESS_TO_RESOURCE_IS_FORBIDDEN") + .build(); + + try { + user = userClient.getUser(user.getHref()); + fail("Should give HTTP 403 error"); + } catch (VCloudDirectorException vde) { + assertEquals(vde.getError(), expected); + user = null; + } catch (Exception e) { + fail("Should have thrown a VCloudDirectorException"); + } + + if (user != null) { // guard against NPE on the .toStrings + assertNull(user, String.format(OBJ_DEL, USER, user.toString())); + } + } } diff --git a/labs/vcloud-director/src/test/resources/user/createUser.xml b/labs/vcloud-director/src/test/resources/user/createUser.xml new file mode 100644 index 0000000000..7736d548bd --- /dev/null +++ b/labs/vcloud-director/src/test/resources/user/createUser.xml @@ -0,0 +1,21 @@ + + + + + testFullName + test@test.com + 555-1234 + false + false + testIM + false + testPrefix + testAlert@test.com + false + false + false + 0 + 0 + + + \ No newline at end of file diff --git a/labs/vcloud-director/src/test/resources/user/createUserSource.xml b/labs/vcloud-director/src/test/resources/user/createUserSource.xml new file mode 100644 index 0000000000..f3ff55dcc0 --- /dev/null +++ b/labs/vcloud-director/src/test/resources/user/createUserSource.xml @@ -0,0 +1,16 @@ + + + testFullName + test@test.com + 555-1234 + false + testIM + false + testPrefix + testAlert@test.com + false + false + + password + + \ No newline at end of file diff --git a/labs/vcloud-director/src/test/resources/user/user.xml b/labs/vcloud-director/src/test/resources/user/user.xml new file mode 100644 index 0000000000..4ee7b3d970 --- /dev/null +++ b/labs/vcloud-director/src/test/resources/user/user.xml @@ -0,0 +1,22 @@ + + + + + testFullName + test@test.com + 555-1234 + false + false + testIM + test + false + testPrefix + testAlert@test.com + false + false + false + 0 + 0 + + + \ No newline at end of file