Completed the UserApi
Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/fa11e377 Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/fa11e377 Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/fa11e377 Branch: refs/heads/keystonev3 Commit: fa11e377869f471510dd22a5a916207d9f83dfdb Parents: 98a8584 Author: Ignasi Barrera <[email protected]> Authored: Thu Jan 11 15:57:07 2018 +0100 Committer: Ignasi Barrera <[email protected]> Committed: Thu Jan 11 16:21:31 2018 +0100 ---------------------------------------------------------------------- .../openstack/keystone/v3/KeystoneApi.java | 7 + .../openstack/keystone/v3/domain/Group.java | 56 +++++++ .../openstack/keystone/v3/features/UserApi.java | 73 +++++++- .../keystone/v3/features/RegionApiMockTest.java | 2 +- .../keystone/v3/features/UserApiLiveTest.java | 88 ++++++++++ .../keystone/v3/features/UserApiMockTest.java | 168 +++++++++++++++++++ .../src/test/resources/v3/groups.json | 27 +++ .../src/test/resources/v3/user.json | 13 ++ .../src/test/resources/v3/users.json | 132 +++++++++++++++ 9 files changed, 559 insertions(+), 7 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jclouds/blob/fa11e377/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/KeystoneApi.java ---------------------------------------------------------------------- diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/KeystoneApi.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/KeystoneApi.java index d4c2176..05cd91d 100644 --- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/KeystoneApi.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/KeystoneApi.java @@ -22,6 +22,7 @@ import org.jclouds.openstack.keystone.v3.features.AuthApi; import org.jclouds.openstack.keystone.v3.features.CatalogApi; import org.jclouds.openstack.keystone.v3.features.ProjectApi; import org.jclouds.openstack.keystone.v3.features.RegionApi; +import org.jclouds.openstack.keystone.v3.features.UserApi; import org.jclouds.rest.annotations.Delegate; /** @@ -52,4 +53,10 @@ public interface KeystoneApi extends Closeable { */ @Delegate ProjectApi getProjectApi(); + + /** + * Provides access to user features. + */ + @Delegate + UserApi getUserApi(); } http://git-wip-us.apache.org/repos/asf/jclouds/blob/fa11e377/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Group.java ---------------------------------------------------------------------- diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Group.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Group.java new file mode 100644 index 0000000..ee2738a --- /dev/null +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Group.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.jclouds.openstack.keystone.v3.domain; + +import org.jclouds.javax.annotation.Nullable; +import org.jclouds.json.SerializedNames; + +import com.google.auto.value.AutoValue; + +@AutoValue +public abstract class Group { + + public abstract String id(); + public abstract String name(); + @Nullable public abstract String description(); + @Nullable public abstract String domainId(); + @Nullable public abstract Link link(); + + @SerializedNames({ "id", "name", "description", "domain_id", "links" }) + public static Group create(String id, String name, String description, String domainId, Link link) { + return builder().id(id).name(name).description(description).domainId(domainId).link(link).build(); + } + + Group() { + } + + public abstract Builder toBuilder(); + + public static Builder builder() { + return new AutoValue_Group.Builder(); + } + + @AutoValue.Builder + public abstract static class Builder { + public abstract Builder id(String id); + public abstract Builder name(String String); + public abstract Builder description(String description); + public abstract Builder domainId(String domainId); + public abstract Builder link(Link link); + public abstract Group build(); + } +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/fa11e377/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/features/UserApi.java ---------------------------------------------------------------------- diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/features/UserApi.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/features/UserApi.java index 1bb884d..1617b51 100644 --- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/features/UserApi.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/features/UserApi.java @@ -20,31 +20,92 @@ import java.util.List; import javax.inject.Named; 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.PathParam; import javax.ws.rs.core.MediaType; import org.jclouds.Fallbacks.EmptyListOnNotFoundOr404; +import org.jclouds.Fallbacks.FalseOnNotFoundOr404; +import org.jclouds.Fallbacks.NullOnNotFoundOr404; +import org.jclouds.javax.annotation.Nullable; import org.jclouds.openstack.keystone.auth.filters.AuthenticateRequest; -import org.jclouds.openstack.keystone.v3.domain.Region; +import org.jclouds.openstack.keystone.v3.domain.Group; +import org.jclouds.openstack.keystone.v3.domain.Project; +import org.jclouds.openstack.keystone.v3.domain.User; import org.jclouds.openstack.v2_0.services.Identity; import org.jclouds.rest.annotations.Endpoint; import org.jclouds.rest.annotations.Fallback; +import org.jclouds.rest.annotations.PATCH; +import org.jclouds.rest.annotations.PayloadParam; import org.jclouds.rest.annotations.RequestFilters; import org.jclouds.rest.annotations.SelectJson; +import org.jclouds.rest.annotations.WrapWith; /** - * Provides access to the Keystone Region API. + * Provides access to the Keystone User API. */ @Consumes(MediaType.APPLICATION_JSON) @RequestFilters(AuthenticateRequest.class) @Endpoint(Identity.class) -@Path("/regions") +@Path("/users") public interface UserApi { - @Named("regions:list") + @Named("users:list") @GET - @SelectJson("regions") + @SelectJson("users") @Fallback(EmptyListOnNotFoundOr404.class) - List<Region> list(); + List<User> list(); + + @Named("users:get") + @GET + @Path("/{id}") + @SelectJson("user") + @Fallback(NullOnNotFoundOr404.class) + User get(@PathParam("id") String id); + + @Named("users:create") + @POST + @SelectJson("user") + @WrapWith("user") + User create(@PayloadParam("name") String name, @Nullable @PayloadParam("password") String password, + @Nullable @PayloadParam("enabled") Boolean enabled, @Nullable @PayloadParam("domain_id") String domainId, + @Nullable @PayloadParam("default_project_id") String defaultProjectId); + + @Named("users:update") + @PATCH + @Path("/{id}") + @SelectJson("user") + @WrapWith("user") + User update(@PathParam("id") String id, @PayloadParam("name") String name, + @Nullable @PayloadParam("password") String password, @Nullable @PayloadParam("enabled") Boolean enabled, + @Nullable @PayloadParam("domain_id") String domainId, + @Nullable @PayloadParam("default_project_id") String defaultProjectId); + + @Named("users:delete") + @DELETE + @Path("/{id}") + @Fallback(FalseOnNotFoundOr404.class) + boolean delete(@PathParam("id") String id); + + @Named("users:groups") + @GET + @Path("/{id}/groups") + @SelectJson("groups") + List<Group> listGroups(@PathParam("id") String id); + + @Named("users:projects") + @GET + @Path("/{id}/projects") + @SelectJson("projects") + List<Project> listProjects(@PathParam("id") String id); + + @Named("users:password") + @POST + @Path("/{id}/password") + @WrapWith("user") + void changePassword(@PathParam("id") String id, @PayloadParam("original_password") String originalPassword, + @PayloadParam("password") String newPassword); } http://git-wip-us.apache.org/repos/asf/jclouds/blob/fa11e377/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v3/features/RegionApiMockTest.java ---------------------------------------------------------------------- diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v3/features/RegionApiMockTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v3/features/RegionApiMockTest.java index 2e68246..bc7b1e0 100644 --- a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v3/features/RegionApiMockTest.java +++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v3/features/RegionApiMockTest.java @@ -43,7 +43,7 @@ public class RegionApiMockTest extends BaseV3KeystoneApiMockTest { assertSent(server, "GET", "/regions"); } - public void testListEndpointsReturns404() throws InterruptedException { + public void testListRegionsReturns404() throws InterruptedException { enqueueAuthentication(server); server.enqueue(response404()); http://git-wip-us.apache.org/repos/asf/jclouds/blob/fa11e377/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v3/features/UserApiLiveTest.java ---------------------------------------------------------------------- diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v3/features/UserApiLiveTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v3/features/UserApiLiveTest.java new file mode 100644 index 0000000..addcf39 --- /dev/null +++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v3/features/UserApiLiveTest.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.jclouds.openstack.keystone.v3.features; + +import static com.google.common.collect.Iterables.any; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; + +import org.jclouds.openstack.keystone.v3.domain.User; +import org.jclouds.openstack.keystone.v3.internal.BaseV3KeystoneApiLiveTest; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import com.google.common.base.Predicate; + +@Test(groups = "live", testName = "UserApiLiveTest", singleThreaded = true) +public class UserApiLiveTest extends BaseV3KeystoneApiLiveTest { + + private User user; + + @BeforeClass + public void createTestUser() { + user = api().create(getClass().getSimpleName(), "p4ssw0rd", true, null, null); + assertNotNull(user); + } + + @Test + public void testListUsers() { + assertTrue(any(api().list(), new Predicate<User>() { + @Override + public boolean apply(User input) { + return input.id().equals(user.id()); + } + })); + } + + @Test + public void testGetUser() { + assertNotNull(api().get(user.id())); + } + + @Test + public void testUpdateUser() { + api().update(user.id(), "Updated", null, null, null, null); + user = api().get(user.id()); + assertEquals(user.name(), "Updated"); + } + + @Test + public void testListGroups() { + assertNotNull(api().listGroups(user.id())); + } + + @Test + public void testListProjects() { + assertNotNull(api().listProjects(user.id())); + } + + @Test + public void testChangePassword() { + api().changePassword(user.id(), "p4ssw0rd", "Newp4ssw0rd"); + } + + @AfterClass(alwaysRun = true) + public void deleteUser() { + assertTrue(api().delete(user.id())); + } + + private UserApi api() { + return api.getUserApi(); + } +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/fa11e377/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v3/features/UserApiMockTest.java ---------------------------------------------------------------------- diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v3/features/UserApiMockTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v3/features/UserApiMockTest.java new file mode 100644 index 0000000..f31ae62 --- /dev/null +++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v3/features/UserApiMockTest.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.jclouds.openstack.keystone.v3.features; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; + +import java.util.List; + +import org.jclouds.openstack.keystone.v3.domain.Group; +import org.jclouds.openstack.keystone.v3.domain.Project; +import org.jclouds.openstack.keystone.v3.domain.User; +import org.jclouds.openstack.keystone.v3.internal.BaseV3KeystoneApiMockTest; +import org.testng.annotations.Test; + +@Test(groups = "unit", testName = "UserApiMockTest", singleThreaded = true) +public class UserApiMockTest extends BaseV3KeystoneApiMockTest { + + public void testListUsers() throws InterruptedException { + enqueueAuthentication(server); + server.enqueue(jsonResponse("/v3/users.json")); + + List<User> users = api.getUserApi().list(); + assertFalse(users.isEmpty()); + + assertEquals(server.getRequestCount(), 2); + assertAuthentication(server); + assertSent(server, "GET", "/users"); + } + + public void testListUsersReturns404() throws InterruptedException { + enqueueAuthentication(server); + server.enqueue(response404()); + + List<User> users = api.getUserApi().list(); + assertTrue(users.isEmpty()); + + assertEquals(server.getRequestCount(), 2); + assertAuthentication(server); + assertSent(server, "GET", "/users"); + } + + public void testGetUser() throws InterruptedException { + enqueueAuthentication(server); + server.enqueue(jsonResponse("/v3/user.json")); + + User user = api.getUserApi().get("0bedc61110fd4e94a251260a47f18f29"); + assertNotNull(user); + + assertEquals(server.getRequestCount(), 2); + assertAuthentication(server); + assertSent(server, "GET", "/users/0bedc61110fd4e94a251260a47f18f29"); + } + + public void testGetUserReturns404() throws InterruptedException { + enqueueAuthentication(server); + server.enqueue(response404()); + + User user = api.getUserApi().get("0bedc61110fd4e94a251260a47f18f29"); + assertNull(user); + + assertEquals(server.getRequestCount(), 2); + assertAuthentication(server); + assertSent(server, "GET", "/users/0bedc61110fd4e94a251260a47f18f29"); + } + + public void testCreateUser() throws InterruptedException { + enqueueAuthentication(server); + server.enqueue(jsonResponse("/v3/user.json")); + + User user = api.getUserApi().create("user", "p4ssw0rd", true, "123", "789"); + assertNotNull(user); + + assertEquals(server.getRequestCount(), 2); + assertAuthentication(server); + assertSent(server, "POST", "/users", "{\"user\":{\"name\":\"user\",\"password\":\"p4ssw0rd\",\"enabled\":true," + + "\"domain_id\":\"123\",\"default_project_id\":\"789\"}}"); + } + + public void testUpdateUser() throws InterruptedException { + enqueueAuthentication(server); + server.enqueue(jsonResponse("/v3/user.json")); + + User user = api.getUserApi().update("0bedc61110fd4e94a251260a47f18f29", "foo", null, null, null, null); + assertNotNull(user); + + assertEquals(server.getRequestCount(), 2); + assertAuthentication(server); + assertSent(server, "PATCH", "/users/0bedc61110fd4e94a251260a47f18f29", "{\"user\":{\"name\":\"foo\"}}"); + } + + public void testDeleteUser() throws InterruptedException { + enqueueAuthentication(server); + server.enqueue(response204()); + + boolean deleted = api.getUserApi().delete("0bedc61110fd4e94a251260a47f18f29"); + assertTrue(deleted); + + assertEquals(server.getRequestCount(), 2); + assertAuthentication(server); + assertSent(server, "DELETE", "/users/0bedc61110fd4e94a251260a47f18f29"); + } + + public void testDeleteUserReturns404() throws InterruptedException { + enqueueAuthentication(server); + server.enqueue(response404()); + + boolean deleted = api.getUserApi().delete("0bedc61110fd4e94a251260a47f18f29"); + assertFalse(deleted); + + assertEquals(server.getRequestCount(), 2); + assertAuthentication(server); + assertSent(server, "DELETE", "/users/0bedc61110fd4e94a251260a47f18f29"); + } + + public void testListGroups() throws InterruptedException { + enqueueAuthentication(server); + server.enqueue(jsonResponse("/v3/groups.json")); + + List<Group> groups = api.getUserApi().listGroups("0bedc61110fd4e94a251260a47f18f29"); + assertFalse(groups.isEmpty()); + + assertEquals(server.getRequestCount(), 2); + assertAuthentication(server); + assertSent(server, "GET", "/users/0bedc61110fd4e94a251260a47f18f29/groups"); + } + + public void testListProjects() throws InterruptedException { + enqueueAuthentication(server); + server.enqueue(jsonResponse("/v3/projects.json")); + + List<Project> projects = api.getUserApi().listProjects("0bedc61110fd4e94a251260a47f18f29"); + assertFalse(projects.isEmpty()); + + assertEquals(server.getRequestCount(), 2); + assertAuthentication(server); + assertSent(server, "GET", "/users/0bedc61110fd4e94a251260a47f18f29/projects"); + } + + public void testChangePassword() throws InterruptedException { + enqueueAuthentication(server); + server.enqueue(response204()); + + api.getUserApi().changePassword("0bedc61110fd4e94a251260a47f18f29", "foo", "bar"); + + assertEquals(server.getRequestCount(), 2); + assertAuthentication(server); + assertSent(server, "POST", "/users/0bedc61110fd4e94a251260a47f18f29/password", + "{\"user\":{\"original_password\":\"foo\",\"password\":\"bar\"}}"); + } +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/fa11e377/apis/openstack-keystone/src/test/resources/v3/groups.json ---------------------------------------------------------------------- diff --git a/apis/openstack-keystone/src/test/resources/v3/groups.json b/apis/openstack-keystone/src/test/resources/v3/groups.json new file mode 100644 index 0000000..17f061c --- /dev/null +++ b/apis/openstack-keystone/src/test/resources/v3/groups.json @@ -0,0 +1,27 @@ +{ + "groups": [ + { + "description": "Developers cleared for work on all general projects", + "domain_id": "1789d1", + "id": "ea167b", + "links": { + "self": "https://example.com/identity/v3/groups/ea167b" + }, + "name": "Developers" + }, + { + "description": "Developers cleared for work on secret projects", + "domain_id": "1789d1", + "id": "a62db1", + "links": { + "self": "https://example.com/identity/v3/groups/a62db1" + }, + "name": "Secure Developers" + } + ], + "links": { + "self": "http://example.com/identity/v3/users/9fe1d3/groups", + "previous": null, + "next": null + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/jclouds/blob/fa11e377/apis/openstack-keystone/src/test/resources/v3/user.json ---------------------------------------------------------------------- diff --git a/apis/openstack-keystone/src/test/resources/v3/user.json b/apis/openstack-keystone/src/test/resources/v3/user.json new file mode 100644 index 0000000..1c64db9 --- /dev/null +++ b/apis/openstack-keystone/src/test/resources/v3/user.json @@ -0,0 +1,13 @@ +{ + "user": { + "password_expires_at": null, + "links": { + "self": "http://localhost/identity/v3/users/0bedc61110fd4e94a251260a47f18f29" + }, + "enabled": true, + "id": "0bedc61110fd4e94a251260a47f18f29", + "options": {}, + "domain_id": "default", + "name": "User" + } +} http://git-wip-us.apache.org/repos/asf/jclouds/blob/fa11e377/apis/openstack-keystone/src/test/resources/v3/users.json ---------------------------------------------------------------------- diff --git a/apis/openstack-keystone/src/test/resources/v3/users.json b/apis/openstack-keystone/src/test/resources/v3/users.json new file mode 100644 index 0000000..4830220 --- /dev/null +++ b/apis/openstack-keystone/src/test/resources/v3/users.json @@ -0,0 +1,132 @@ +{ + "users": [ + { + "password_expires_at": null, + "name": "UserApiLiveTest", + "links": { + "self": "http://localhost/identity/v3/users/0bedc61110fd4e94a251260a47f18f29" + }, + "domain_id": "default", + "enabled": true, + "id": "0bedc61110fd4e94a251260a47f18f29", + "options": {} + }, + { + "password_expires_at": null, + "name": "glance-swift", + "links": { + "self": "http://localhost/identity/v3/users/1194933d3f1147a3b0824848bb7ec5e2" + }, + "domain_id": "default", + "enabled": true, + "id": "1194933d3f1147a3b0824848bb7ec5e2", + "options": {} + }, + { + "password_expires_at": null, + "name": "nova", + "links": { + "self": "http://localhost/identity/v3/users/290999fc41fb48e397feef465d014fb6" + }, + "domain_id": "default", + "enabled": true, + "id": "290999fc41fb48e397feef465d014fb6", + "options": {} + }, + { + "password_expires_at": null, + "name": "placement", + "links": { + "self": "http://localhost/identity/v3/users/34e6ac0dd9bb4601bac4971785abd7f5" + }, + "domain_id": "default", + "enabled": true, + "id": "34e6ac0dd9bb4601bac4971785abd7f5", + "options": {} + }, + { + "name": "jclouds", + "links": { + "self": "http://localhost/identity/v3/users/6c3b325a28264f00865b38442429fd77" + }, + "domain_id": "default", + "enabled": true, + "options": {}, + "default_project_id": "43de288ea0ce4d2b8b811055b10f156b", + "id": "6c3b325a28264f00865b38442429fd77", + "password_expires_at": null + }, + { + "password_expires_at": null, + "name": "cinder", + "links": { + "self": "http://localhost/identity/v3/users/6e705bff20794de5955acf0936f02b3f" + }, + "domain_id": "default", + "enabled": true, + "id": "6e705bff20794de5955acf0936f02b3f", + "options": {} + }, + { + "name": "demo", + "links": { + "self": "http://localhost/identity/v3/users/84910c7070144530a6b9627fe0e1743f" + }, + "domain_id": "default", + "enabled": true, + "options": {}, + "id": "84910c7070144530a6b9627fe0e1743f", + "email": "[email protected]", + "password_expires_at": null + }, + { + "password_expires_at": null, + "name": "swift", + "links": { + "self": "http://localhost/identity/v3/users/87306cb2f0954ca8919f0fbaf29a780f" + }, + "domain_id": "default", + "enabled": true, + "id": "87306cb2f0954ca8919f0fbaf29a780f", + "options": {} + }, + { + "password_expires_at": null, + "name": "glance", + "links": { + "self": "http://localhost/identity/v3/users/94c9f0f5e056489ebfef25870e8944fe" + }, + "domain_id": "default", + "enabled": true, + "id": "94c9f0f5e056489ebfef25870e8944fe", + "options": {} + }, + { + "password_expires_at": null, + "name": "admin", + "links": { + "self": "http://localhost/identity/v3/users/ab7bd6c2dd394fce8318e7562115d3f8" + }, + "domain_id": "default", + "enabled": true, + "id": "ab7bd6c2dd394fce8318e7562115d3f8", + "options": {} + }, + { + "password_expires_at": null, + "name": "neutron", + "links": { + "self": "http://localhost/identity/v3/users/d3607e141e334823978eec2e1ccca8de" + }, + "domain_id": "default", + "enabled": true, + "id": "d3607e141e334823978eec2e1ccca8de", + "options": {} + } + ], + "links": { + "self": "http://localhost/identity/v3/users", + "previous": null, + "next": null + } +}
