diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/auth/domain/TenantOrDomainAndCredentials.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/auth/domain/TenantOrDomainAndCredentials.java index b30f3fc290..f6ce1de761 100644 --- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/auth/domain/TenantOrDomainAndCredentials.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/auth/domain/TenantOrDomainAndCredentials.java @@ -30,7 +30,9 @@ public abstract class TenantOrDomainAndCredentials { @Nullable public abstract String tenantOrDomainId(); @Nullable public abstract String tenantOrDomainName(); @Nullable public abstract String scope(); - public abstract T credentials(); + @Nullable public abstract String projectDomainName(); + @Nullable public abstract String projectDomainId(); + public abstract T credentials(); TenantOrDomainAndCredentials() { @@ -45,6 +47,8 @@ public abstract class TenantOrDomainAndCredentials { public abstract Builder tenantOrDomainId(String tenantId); public abstract Builder tenantOrDomainName(String tenantName); public abstract Builder scope(String scope); + public abstract Builder projectDomainName(String projectDomainName); + public abstract Builder projectDomainId(String projectDomainId); public abstract Builder credentials(T credentials); public abstract TenantOrDomainAndCredentials build(); diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/auth/functions/BaseAuthenticator.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/auth/functions/BaseAuthenticator.java index d49204c91a..015f470142 100644 --- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/auth/functions/BaseAuthenticator.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/auth/functions/BaseAuthenticator.java @@ -17,6 +17,8 @@ package org.jclouds.openstack.keystone.auth.functions; import static com.google.common.base.Preconditions.checkState; +import static org.jclouds.openstack.keystone.config.KeystoneProperties.PROJECT_DOMAIN_ID; +import static org.jclouds.openstack.keystone.config.KeystoneProperties.PROJECT_DOMAIN_NAME; import static org.jclouds.openstack.keystone.config.KeystoneProperties.REQUIRES_TENANT; import static org.jclouds.openstack.keystone.config.KeystoneProperties.SCOPE; import static org.jclouds.openstack.keystone.config.KeystoneProperties.TENANT_ID; @@ -51,15 +53,25 @@ public abstract class BaseAuthenticator implements Function implements Function credsWithTenant = TenantOrDomainAndCredentials. builder().tenantOrDomainId(defaultTenantId) - .tenantOrDomainName(tenantName).scope(scope).credentials(creds).build(); - + TenantOrDomainAndCredentials credsWithTenant = TenantOrDomainAndCredentials. builder() + .tenantOrDomainId(defaultTenantId).tenantOrDomainName(tenantName).scope(scope) + .projectDomainName(projectDomainName).projectDomainId(projectDomainId).credentials(creds).build(); + return authenticate(credsWithTenant); } public abstract C createCredentials(String identity, String credential); - + public abstract AuthInfo authenticate(TenantOrDomainAndCredentials credentials); } diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/config/KeystoneProperties.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/config/KeystoneProperties.java index bab41a47d4..e97ff9cda3 100644 --- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/config/KeystoneProperties.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/config/KeystoneProperties.java @@ -65,7 +65,7 @@ public final class KeystoneProperties { * @see openstack docs */ public static final String REQUIRES_TENANT = "jclouds.keystone.requires-tenant"; - + /** * set this property to specify for scoped authentication. *

@@ -80,13 +80,41 @@ public final class KeystoneProperties { @SinceApiVersion("3") public static final String SCOPE = "jclouds.keystone.scope"; + /** + * Set this property to specify the domain name of project (tenant) + * scope.
+ * Required property when authentication {@link #SCOPE} is 'project:' and + * project (tenant) domain is different than the user domain (Otherwise, the + * domain used is the same as the user).
+ * Cannot be used simultaneously with {@link #PROJECT_DOMAIN_ID} + * + * @see openstack + * docs : Identity service (Keystone) + */ + public static final String PROJECT_DOMAIN_NAME = "jclouds.keystone.project-domain-name"; + + /** + * Set this property to specify the domain id of project (tenant) scope.
+ * Required property when authentication {@link #SCOPE} is 'project:' and + * project (tenant) domain is different than the user domain (Otherwise, the + * domain used is the same as the user).
+ * Cannot be used simultaneously with {@link #PROJECT_DOMAIN_NAME} + * + * + * @see openstack + * docs : Identity service (Keystone) + */ + public static final String PROJECT_DOMAIN_ID = "jclouds.keystone.project-domain-id"; + /** * type of the keystone service. ex. {@code compute} * * @see ServiceType */ public static final String SERVICE_TYPE = "jclouds.keystone.service-type"; - + /** * Version of keystone to be used by services. Default: 3. */ diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/binders/BindAuthToJsonPayload.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/binders/BindAuthToJsonPayload.java index a27ae12d12..be0ba60456 100644 --- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/binders/BindAuthToJsonPayload.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/binders/BindAuthToJsonPayload.java @@ -38,6 +38,7 @@ import org.jclouds.openstack.keystone.v3.domain.Auth.DomainScope; import org.jclouds.openstack.keystone.v3.domain.Auth.Id; import org.jclouds.openstack.keystone.v3.domain.Auth.Name; import org.jclouds.openstack.keystone.v3.domain.Auth.ProjectIdScope; +import org.jclouds.openstack.keystone.v3.domain.Auth.ProjectIdScope.ProjectId; import org.jclouds.openstack.keystone.v3.domain.Auth.ProjectScope; import org.jclouds.openstack.keystone.v3.domain.Auth.ProjectScope.ProjectName; import org.jclouds.rest.MapBinder; @@ -50,8 +51,7 @@ import com.google.common.collect.ImmutableSet; public abstract class BindAuthToJsonPayload extends BindToJsonPayload implements MapBinder { - private static final Set SCOPE_PREFIXES = ImmutableSet - .of(PROJECT, PROJECT_ID, DOMAIN, DOMAIN_ID); + private static final Set SCOPE_PREFIXES = ImmutableSet.of(PROJECT, PROJECT_ID, DOMAIN, DOMAIN_ID); protected BindAuthToJsonPayload(Json jsonBinder) { super(jsonBinder); @@ -93,15 +93,32 @@ public abstract class BindAuthToJsonPayload extends BindToJsonPayload impleme checkArgument(SCOPE_PREFIXES.contains(parts[0]), "Scope prefix should be: %s", SCOPE_PREFIXES); if (PROJECT.equals(parts[0])) { - Object domainScope = credentials.tenantOrDomainId() != null ? Id.create(credentials.tenantOrDomainId()) : Name - .create(credentials.tenantOrDomainName()); - return ProjectScope.create(ProjectName.create(parts[1], domainScope)); + return ProjectScope.create(ProjectName.create(parts[1], parseProjectDomain(credentials, true))); } else if (PROJECT_ID.equals(parts[0])) { - return ProjectIdScope.create(Id.create(parts[1])); + // tenant (name/id) was never used as domain for project-id; so try to + // keep backward compatibility + return ProjectIdScope.create(ProjectId.create(parts[1], parseProjectDomain(credentials, false))); } else if (DOMAIN.equals(parts[0])) { return DomainScope.create(Name.create(parts[1])); } else { return DomainIdScope.create(Id.create(parts[1])); } } + + private Object parseProjectDomain(TenantOrDomainAndCredentials credentials, boolean useTenantAsDefaultDomain) { + // Before 'projectDomainName'/'projectDomainId' support, + // 'tenantOrDomainId' was used as domain (id) for project-scoped by name, + // but not by id, so 'useTenantAsDefaultDomain' flag allows to manage that + Object domainScope = null; + if (useTenantAsDefaultDomain && credentials.tenantOrDomainId() != null) { + domainScope = Id.create(credentials.tenantOrDomainId()); + } else if (credentials.projectDomainName() != null) { + domainScope = Name.create(credentials.projectDomainName()); + } else if (credentials.projectDomainId() != null) { + domainScope = Id.create(credentials.projectDomainId()); + } else if (useTenantAsDefaultDomain) { + domainScope = Name.create(credentials.tenantOrDomainName()); + } + return domainScope; + } } diff --git a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Auth.java b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Auth.java index 58c9ee62f1..fcfe4faaf9 100644 --- a/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Auth.java +++ b/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Auth.java @@ -26,18 +26,24 @@ import com.google.auto.value.AutoValue; @AutoValue public abstract class Auth { public abstract Identity identity(); - @Nullable public abstract Object scope(); + + @Nullable + public abstract Object scope(); @SerializedNames({ "identity", "scope" }) public static Auth create(Identity identity, Object scope) { return new AutoValue_Auth(identity, scope); } - + @AutoValue public abstract static class Identity { public abstract List methods(); - @Nullable public abstract Id token(); - @Nullable public abstract PasswordAuth password(); + + @Nullable + public abstract Id token(); + + @Nullable + public abstract PasswordAuth password(); @SerializedNames({ "methods", "token", "password" }) public static Identity create(List methods, Id token, PasswordAuth password) { @@ -56,7 +62,9 @@ public abstract class Auth { @AutoValue public abstract static class UserAuth { public abstract String name(); + public abstract DomainAuth domain(); + public abstract String password(); @SerializedNames({ "name", "domain", "password" }) @@ -66,7 +74,8 @@ public abstract class Auth { @AutoValue public abstract static class DomainAuth { - @Nullable public abstract String name(); + @Nullable + public abstract String name(); @SerializedNames({ "name" }) public static DomainAuth create(String name) { @@ -76,7 +85,7 @@ public abstract class Auth { } } } - + @AutoValue public abstract static class Id { public abstract String id(); @@ -86,17 +95,18 @@ public abstract class Auth { return new AutoValue_Auth_Id(id); } } - + @AutoValue public abstract static class Name { - @Nullable public abstract String name(); + @Nullable + public abstract String name(); @SerializedNames({ "name" }) public static Name create(String name) { return new AutoValue_Auth_Name(name); } } - + public static class Scope { public static final String PROJECT = "project"; public static final String PROJECT_ID = "projectId"; @@ -108,22 +118,24 @@ public abstract class Auth { @AutoValue public abstract static class ProjectScope { public abstract ProjectName project(); - + @SerializedNames({ Scope.PROJECT }) public static ProjectScope create(ProjectName project) { return new AutoValue_Auth_ProjectScope(project); } - + @AutoValue public abstract static class ProjectName { public abstract String name(); - @Nullable public abstract Object domain(); - + + @Nullable + public abstract Object domain(); + @SerializedNames({ "name", Scope.DOMAIN }) public static ProjectName create(String name, Object domain) { return new AutoValue_Auth_ProjectScope_ProjectName(name, domain); } - + public static ProjectName create(String name, Name domain) { return new AutoValue_Auth_ProjectScope_ProjectName(name, domain); } @@ -133,17 +145,38 @@ public abstract class Auth { } } } - + @AutoValue public abstract static class ProjectIdScope { - public abstract Id project(); + public abstract ProjectId project(); @SerializedNames({ Scope.PROJECT }) - public static ProjectIdScope create(Id id) { - return new AutoValue_Auth_ProjectIdScope(id); + public static ProjectIdScope create(ProjectId project) { + return new AutoValue_Auth_ProjectIdScope(project); + } + + @AutoValue + public abstract static class ProjectId { + public abstract String id(); + + @Nullable + public abstract Object domain(); + + @SerializedNames({ "id", Scope.DOMAIN }) + public static ProjectId create(String id, Object domain) { + return new AutoValue_Auth_ProjectIdScope_ProjectId(id, domain); + } + + public static ProjectId create(String id, Name domain) { + return new AutoValue_Auth_ProjectIdScope_ProjectId(id, domain); + } + + public static ProjectId create(String id, Id domain) { + return new AutoValue_Auth_ProjectIdScope_ProjectId(id, domain); + } } } - + @AutoValue public abstract static class DomainIdScope { public abstract Id domain(); @@ -153,7 +186,7 @@ public abstract class Auth { return new AutoValue_Auth_DomainIdScope(id); } } - + @AutoValue public abstract static class DomainScope { public abstract Name domain(); diff --git a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v3/auth/V3AuthenticationApiMockTest.java b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v3/auth/V3AuthenticationApiMockTest.java index d6019132ad..fac5d52bde 100644 --- a/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v3/auth/V3AuthenticationApiMockTest.java +++ b/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v3/auth/V3AuthenticationApiMockTest.java @@ -19,6 +19,7 @@ package org.jclouds.openstack.keystone.v3.auth; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; +import org.jclouds.openstack.keystone.auth.domain.ApiAccessKeyCredentials; import org.jclouds.openstack.keystone.auth.domain.AuthInfo; import org.jclouds.openstack.keystone.auth.domain.PasswordCredentials; import org.jclouds.openstack.keystone.auth.domain.TenantOrDomainAndCredentials; @@ -31,71 +32,132 @@ import org.testng.annotations.Test; public class V3AuthenticationApiMockTest extends BaseV3KeystoneApiMockTest { public void testAuthenticatePassword() throws InterruptedException { - server.enqueue(jsonResponse("/v3/token.json")); - TenantOrDomainAndCredentials credentials = TenantOrDomainAndCredentials. builder() - .tenantOrDomainName("domain") - .scope("unscoped") + TenantOrDomainAndCredentials credentials = TenantOrDomainAndCredentials + . builder().tenantOrDomainName("domain").scope("unscoped") .credentials(PasswordCredentials.builder().username("identity").password("credential").build()).build(); - - AuthInfo authInfo = authenticationApi.authenticatePassword(credentials); - assertTrue(authInfo instanceof Token); - assertEquals(authInfo, tokenFromResource("/v3/token.json")); - - assertEquals(server.getRequestCount(), 1); - assertSent(server, "POST", "/auth/tokens", stringFromResource("/v3/auth-password.json")); + checkTokenResult(credentials, "/v3/auth-password.json"); } - + public void testAuthenticatePasswordScoped() throws InterruptedException { - server.enqueue(jsonResponse("/v3/token.json")); - TenantOrDomainAndCredentials credentials = TenantOrDomainAndCredentials. builder() - .tenantOrDomainName("domain") - .scope("projectId:1234567890") + TenantOrDomainAndCredentials credentials = TenantOrDomainAndCredentials + . builder().tenantOrDomainName("domain").scope("projectId:1234567890") .credentials(PasswordCredentials.builder().username("identity").password("credential").build()).build(); - - AuthInfo authInfo = authenticationApi.authenticatePassword(credentials); - assertTrue(authInfo instanceof Token); - assertEquals(authInfo, tokenFromResource("/v3/token.json")); + checkTokenResult(credentials, "/v3/auth-password-scoped.json"); + } - assertEquals(server.getRequestCount(), 1); - assertSent(server, "POST", "/auth/tokens", stringFromResource("/v3/auth-password-scoped.json")); + public void testAuthenticatePasswordProjectScopedIdDomainBackwardsCompat() throws InterruptedException { + // See JCLOUDS-1414, before add of KeystoneProperties.PROJECT_DOMAIN, + // TENANT_ID was not used as domain for project-scoped with id + // => Unit test only for backward compatibility (is the same as + // 'testAuthenticatePasswordScoped' with TENANT-ID in addition) + + TenantOrDomainAndCredentials credentials = TenantOrDomainAndCredentials + . builder().tenantOrDomainName("domain").scope("projectId:1234567890") + .tenantOrDomainId("somethingShouldNotBeUsed") + .credentials(PasswordCredentials.builder().username("identity").password("credential").build()).build(); + + checkTokenResult(credentials, "/v3/auth-password-scoped.json"); + } + + public void testAuthenticatePasswordProjectScopedNameDomainBackwardsCompat() throws InterruptedException { + // See JCLOUDS-1414, before add of KeystoneProperties.PROJECT_DOMAIN, + // domain-id of project-scoped could be filled with TENANT_ID + // => Unit test only for backward compatibility + + TenantOrDomainAndCredentials credentials = TenantOrDomainAndCredentials + . builder().tenantOrDomainName("domain").scope("project:my-project") + .tenantOrDomainId("default") + .credentials(PasswordCredentials.builder().username("identity").password("credential").build()).build(); + + checkTokenResult(credentials, "/v3/auth-password-project-scoped-name-domain-backwards-compat.json"); + } + + public void testAuthenticatePasswordProjectScopedIdDomainId() throws InterruptedException { + + TenantOrDomainAndCredentials credentials = TenantOrDomainAndCredentials + . builder().tenantOrDomainName("domain").scope("projectId:42-project-42") + .projectDomainId("42-domain-42") + .credentials(PasswordCredentials.builder().username("identity").password("credential").build()).build(); + + checkTokenResult(credentials, "/v3/auth-password-project-scoped-id-domain-id.json"); + } + + public void testAuthenticatePasswordProjectScopedIdDomainName() throws InterruptedException { + + TenantOrDomainAndCredentials credentials = TenantOrDomainAndCredentials + . builder().tenantOrDomainName("domain").scope("projectId:42") + .projectDomainName("default") + .credentials(PasswordCredentials.builder().username("identity").password("credential").build()).build(); + + checkTokenResult(credentials, "/v3/auth-password-project-scoped-id-domain-name.json"); + } + + public void testAuthenticatePasswordProjectScopedNameDomainId() throws InterruptedException { + + TenantOrDomainAndCredentials credentials = TenantOrDomainAndCredentials + . builder().tenantOrDomainName("domain").scope("project:my-project") + .projectDomainId("42") + .credentials(PasswordCredentials.builder().username("identity").password("credential").build()).build(); + + checkTokenResult(credentials, "/v3/auth-password-project-scoped-name-domain-id.json"); + } + + public void testAuthenticatePasswordProjectScopedNameDomainName() throws InterruptedException { + + TenantOrDomainAndCredentials credentials = TenantOrDomainAndCredentials + . builder().tenantOrDomainName("domain").scope("project:my-project") + .projectDomainName("default") + .credentials(PasswordCredentials.builder().username("identity").password("credential").build()).build(); + + checkTokenResult(credentials, "/v3/auth-password-project-scoped-name-domain-name.json"); } public void testAuthenticateToken() throws InterruptedException { - server.enqueue(jsonResponse("/v3/token.json")); - TenantOrDomainAndCredentials credentials = TenantOrDomainAndCredentials. builder() - .tenantOrDomainName("domain") - .scope("unscoped") + TenantOrDomainAndCredentials credentials = TenantOrDomainAndCredentials + . builder().tenantOrDomainName("domain").scope("unscoped") .credentials(TokenCredentials.builder().id("token").build()).build(); - - AuthInfo authInfo = authenticationApi.authenticateToken(credentials); - assertTrue(authInfo instanceof Token); - assertEquals(authInfo, tokenFromResource("/v3/token.json")); - - assertEquals(server.getRequestCount(), 1); - assertSent(server, "POST", "/auth/tokens", stringFromResource("/v3/auth-token.json")); + checkTokenResult(credentials, "/v3/auth-token.json"); } - + public void testAuthenticateTokenScoped() throws InterruptedException { + + TenantOrDomainAndCredentials credentials = TenantOrDomainAndCredentials + . builder().tenantOrDomainName("domain").scope("domain:mydomain") + .credentials(TokenCredentials.builder().id("token").build()).build(); + + checkTokenResult(credentials, "/v3/auth-token-scoped.json"); + } + + @SuppressWarnings("unchecked") + private void checkTokenResult(TenantOrDomainAndCredentials credentials, String json) throws InterruptedException { server.enqueue(jsonResponse("/v3/token.json")); - TenantOrDomainAndCredentials credentials = TenantOrDomainAndCredentials. builder() - .tenantOrDomainName("domain") - .scope("domain:mydomain") - .credentials(TokenCredentials.builder().id("token").build()).build(); - - AuthInfo authInfo = authenticationApi.authenticateToken(credentials); + AuthInfo authInfo = null; + + if (credentials.credentials() instanceof PasswordCredentials) { + authInfo = authenticationApi + .authenticatePassword((TenantOrDomainAndCredentials) credentials); + } else if (credentials.credentials() instanceof TokenCredentials) { + authInfo = authenticationApi.authenticateToken((TenantOrDomainAndCredentials) credentials); + } else if (credentials.credentials() instanceof ApiAccessKeyCredentials) { + authInfo = authenticationApi + .authenticateAccessKey((TenantOrDomainAndCredentials) credentials); + } else { + throw new IllegalArgumentException(String.format("Unsupported authentication method with class: %s", + credentials.credentials().getClass().getName())); + } assertTrue(authInfo instanceof Token); assertEquals(authInfo, tokenFromResource("/v3/token.json")); assertEquals(server.getRequestCount(), 1); - assertSent(server, "POST", "/auth/tokens", stringFromResource("/v3/auth-token-scoped.json")); + assertSent(server, "POST", "/auth/tokens", stringFromResource(json)); } } diff --git a/apis/openstack-keystone/src/test/resources/v3/auth-password-project-scoped-id-domain-id.json b/apis/openstack-keystone/src/test/resources/v3/auth-password-project-scoped-id-domain-id.json new file mode 100644 index 0000000000..49b76e4342 --- /dev/null +++ b/apis/openstack-keystone/src/test/resources/v3/auth-password-project-scoped-id-domain-id.json @@ -0,0 +1,26 @@ +{ + "auth": { + "identity": { + "methods": [ + "password" + ], + "password": { + "user": { + "name": "identity", + "domain": { + "name": "domain" + }, + "password": "credential" + } + } + }, + "scope": { + "project": { + "id": "42-project-42", + "domain": { + "id": "42-domain-42" + } + } + } + } +} \ No newline at end of file diff --git a/apis/openstack-keystone/src/test/resources/v3/auth-password-project-scoped-id-domain-name.json b/apis/openstack-keystone/src/test/resources/v3/auth-password-project-scoped-id-domain-name.json new file mode 100644 index 0000000000..b910abeb3d --- /dev/null +++ b/apis/openstack-keystone/src/test/resources/v3/auth-password-project-scoped-id-domain-name.json @@ -0,0 +1,26 @@ +{ + "auth": { + "identity": { + "methods": [ + "password" + ], + "password": { + "user": { + "name": "identity", + "domain": { + "name": "domain" + }, + "password": "credential" + } + } + }, + "scope": { + "project": { + "id": "42", + "domain": { + "name": "default" + } + } + } + } +} \ No newline at end of file diff --git a/apis/openstack-keystone/src/test/resources/v3/auth-password-project-scoped-name-domain-backwards-compat.json b/apis/openstack-keystone/src/test/resources/v3/auth-password-project-scoped-name-domain-backwards-compat.json new file mode 100644 index 0000000000..69ea30ee4e --- /dev/null +++ b/apis/openstack-keystone/src/test/resources/v3/auth-password-project-scoped-name-domain-backwards-compat.json @@ -0,0 +1,26 @@ +{ + "auth": { + "identity": { + "methods": [ + "password" + ], + "password": { + "user": { + "name": "identity", + "domain": { + "name": "domain" + }, + "password": "credential" + } + } + }, + "scope": { + "project": { + "name": "my-project", + "domain": { + "id": "default" + } + } + } + } +} \ No newline at end of file diff --git a/apis/openstack-keystone/src/test/resources/v3/auth-password-project-scoped-name-domain-id.json b/apis/openstack-keystone/src/test/resources/v3/auth-password-project-scoped-name-domain-id.json new file mode 100644 index 0000000000..e99656f964 --- /dev/null +++ b/apis/openstack-keystone/src/test/resources/v3/auth-password-project-scoped-name-domain-id.json @@ -0,0 +1,26 @@ +{ + "auth": { + "identity": { + "methods": [ + "password" + ], + "password": { + "user": { + "name": "identity", + "domain": { + "name": "domain" + }, + "password": "credential" + } + } + }, + "scope": { + "project": { + "name": "my-project", + "domain": { + "id": "42" + } + } + } + } +} \ No newline at end of file diff --git a/apis/openstack-keystone/src/test/resources/v3/auth-password-project-scoped-name-domain-name.json b/apis/openstack-keystone/src/test/resources/v3/auth-password-project-scoped-name-domain-name.json new file mode 100644 index 0000000000..054393c7b6 --- /dev/null +++ b/apis/openstack-keystone/src/test/resources/v3/auth-password-project-scoped-name-domain-name.json @@ -0,0 +1,26 @@ +{ + "auth": { + "identity": { + "methods": [ + "password" + ], + "password": { + "user": { + "name": "identity", + "domain": { + "name": "domain" + }, + "password": "credential" + } + } + }, + "scope": { + "project": { + "name": "my-project", + "domain": { + "name": "default" + } + } + } + } +} \ No newline at end of file