Repository: jclouds Updated Branches: refs/heads/2.1.x b144d9f47 -> 93a805ca5
JCLOUDS-1414: OpenStack Keystone V3 - different auth "domains" support Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/93a805ca Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/93a805ca Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/93a805ca Branch: refs/heads/2.1.x Commit: 93a805ca57cb29f4df534c9c1a073bad6125bafa Parents: b144d9f Author: Alix Lourme <[email protected]> Authored: Mon May 7 18:06:31 2018 +0200 Committer: Ignasi Barrera <[email protected]> Committed: Thu May 10 10:43:35 2018 +0200 ---------------------------------------------------------------------- .../domain/TenantOrDomainAndCredentials.java | 6 +- .../auth/functions/BaseAuthenticator.java | 36 +++-- .../keystone/config/KeystoneProperties.java | 32 ++++- .../v3/binders/BindAuthToJsonPayload.java | 29 +++- .../openstack/keystone/v3/domain/Auth.java | 73 +++++++--- .../v3/auth/V3AuthenticationApiMockTest.java | 144 +++++++++++++------ ...th-password-project-scoped-id-domain-id.json | 26 ++++ ...-password-project-scoped-id-domain-name.json | 26 ++++ ...ect-scoped-name-domain-backwards-compat.json | 26 ++++ ...-password-project-scoped-name-domain-id.json | 26 ++++ ...assword-project-scoped-name-domain-name.json | 26 ++++ 11 files changed, 368 insertions(+), 82 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jclouds/blob/93a805ca/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/auth/domain/TenantOrDomainAndCredentials.java ---------------------------------------------------------------------- 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 b30f3fc..f6ce1de 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<T> { @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<T> { public abstract Builder<T> tenantOrDomainId(String tenantId); public abstract Builder<T> tenantOrDomainName(String tenantName); public abstract Builder<T> scope(String scope); + public abstract Builder<T> projectDomainName(String projectDomainName); + public abstract Builder<T> projectDomainId(String projectDomainId); public abstract Builder<T> credentials(T credentials); public abstract TenantOrDomainAndCredentials<T> build(); http://git-wip-us.apache.org/repos/asf/jclouds/blob/93a805ca/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/auth/functions/BaseAuthenticator.java ---------------------------------------------------------------------- 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 d49204c..015f470 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<C> implements Function<Credentials, Auth @Inject(optional = true) @Named(REQUIRES_TENANT) protected boolean requiresTenant; - + @Inject(optional = true) @Named(SCOPE) protected String scope = Scope.UNSCOPED; + @Inject(optional = true) + @Named(PROJECT_DOMAIN_NAME) + protected String projectDomainName; + + @Inject(optional = true) + @Named(PROJECT_DOMAIN_ID) + protected String projectDomainId; + @PostConstruct public void checkPropertiesAreCompatible() { - checkState(defaultTenantName == null || defaultTenantId == null, "you cannot specify both %s and %s", - TENANT_NAME, TENANT_ID); + checkState(defaultTenantName == null || defaultTenantId == null, "you cannot specify both %s and %s", TENANT_NAME, + TENANT_ID); + checkState(projectDomainName == null || projectDomainId == null, "you cannot specify both %s and %s", + PROJECT_DOMAIN_NAME, PROJECT_DOMAIN_ID); } @Override @@ -74,21 +86,21 @@ public abstract class BaseAuthenticator<C> implements Function<Credentials, Auth } if (defaultTenantId == null && tenantName == null && requiresTenant) { - throw new IllegalArgumentException( - String.format( - "current configuration is set to [%s]. Unless you set [%s] or [%s], you must prefix your identity with 'tenantName:'", - REQUIRES_TENANT, TENANT_NAME, TENANT_ID)); + throw new IllegalArgumentException(String.format( + "current configuration is set to [%s]. Unless you set [%s] or [%s], you must prefix your identity with 'tenantName:'", + REQUIRES_TENANT, TENANT_NAME, TENANT_ID)); } - + C creds = createCredentials(usernameOrAccessKey, passwordOrSecretKeyOrToken); - TenantOrDomainAndCredentials<C> credsWithTenant = TenantOrDomainAndCredentials.<C> builder().tenantOrDomainId(defaultTenantId) - .tenantOrDomainName(tenantName).scope(scope).credentials(creds).build(); - + TenantOrDomainAndCredentials<C> credsWithTenant = TenantOrDomainAndCredentials.<C> 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<C> credentials); } http://git-wip-us.apache.org/repos/asf/jclouds/blob/93a805ca/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/config/KeystoneProperties.java ---------------------------------------------------------------------- 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 bab41a4..e97ff9c 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 <a href="http://wiki.openstack.org/CLIAuth">openstack docs</a> */ public static final String REQUIRES_TENANT = "jclouds.keystone.requires-tenant"; - + /** * set this property to specify for scoped authentication. * <p> @@ -81,12 +81,40 @@ public final class KeystoneProperties { public static final String SCOPE = "jclouds.keystone.scope"; /** + * Set this property to specify the domain name of project (tenant) + * scope.<br/> + * 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). <br/> + * Cannot be used simultaneously with {@link #PROJECT_DOMAIN_ID} + * + * @see <a href= + * "https://docs.openstack.org/keystone/latest/api_curl_examples.html#project-scoped">openstack + * docs : Identity service (Keystone)</a> + */ + public static final String PROJECT_DOMAIN_NAME = "jclouds.keystone.project-domain-name"; + + /** + * Set this property to specify the domain id of project (tenant) scope.<br/> + * 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). <br/> + * Cannot be used simultaneously with {@link #PROJECT_DOMAIN_NAME} + * + * + * @see <a href= + * "https://docs.openstack.org/keystone/latest/api_curl_examples.html#project-scoped">openstack + * docs : Identity service (Keystone)</a> + */ + 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. */ http://git-wip-us.apache.org/repos/asf/jclouds/blob/93a805ca/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/binders/BindAuthToJsonPayload.java ---------------------------------------------------------------------- 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 a27ae12..be0ba60 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<T> extends BindToJsonPayload implements MapBinder { - private static final Set<String> SCOPE_PREFIXES = ImmutableSet - .of(PROJECT, PROJECT_ID, DOMAIN, DOMAIN_ID); + private static final Set<String> SCOPE_PREFIXES = ImmutableSet.of(PROJECT, PROJECT_ID, DOMAIN, DOMAIN_ID); protected BindAuthToJsonPayload(Json jsonBinder) { super(jsonBinder); @@ -93,15 +93,32 @@ public abstract class BindAuthToJsonPayload<T> 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<T> 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; + } } http://git-wip-us.apache.org/repos/asf/jclouds/blob/93a805ca/apis/openstack-keystone/src/main/java/org/jclouds/openstack/keystone/v3/domain/Auth.java ---------------------------------------------------------------------- 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 58c9ee6..fcfe4fa 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<String> 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<String> 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(); http://git-wip-us.apache.org/repos/asf/jclouds/blob/93a805ca/apis/openstack-keystone/src/test/java/org/jclouds/openstack/keystone/v3/auth/V3AuthenticationApiMockTest.java ---------------------------------------------------------------------- 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 d601913..fac5d52 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<PasswordCredentials> credentials = TenantOrDomainAndCredentials.<PasswordCredentials> builder() - .tenantOrDomainName("domain") - .scope("unscoped") + TenantOrDomainAndCredentials<PasswordCredentials> credentials = TenantOrDomainAndCredentials + .<PasswordCredentials> 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<PasswordCredentials> credentials = TenantOrDomainAndCredentials.<PasswordCredentials> builder() - .tenantOrDomainName("domain") - .scope("projectId:1234567890") + TenantOrDomainAndCredentials<PasswordCredentials> credentials = TenantOrDomainAndCredentials + .<PasswordCredentials> 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<PasswordCredentials> credentials = TenantOrDomainAndCredentials + .<PasswordCredentials> 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<PasswordCredentials> credentials = TenantOrDomainAndCredentials + .<PasswordCredentials> 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<PasswordCredentials> credentials = TenantOrDomainAndCredentials + .<PasswordCredentials> 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<PasswordCredentials> credentials = TenantOrDomainAndCredentials + .<PasswordCredentials> 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<PasswordCredentials> credentials = TenantOrDomainAndCredentials + .<PasswordCredentials> 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<PasswordCredentials> credentials = TenantOrDomainAndCredentials + .<PasswordCredentials> 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<TokenCredentials> credentials = TenantOrDomainAndCredentials.<TokenCredentials> builder() - .tenantOrDomainName("domain") - .scope("unscoped") + TenantOrDomainAndCredentials<TokenCredentials> credentials = TenantOrDomainAndCredentials + .<TokenCredentials> 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 { - server.enqueue(jsonResponse("/v3/token.json")); - TenantOrDomainAndCredentials<TokenCredentials> credentials = TenantOrDomainAndCredentials.<TokenCredentials> builder() - .tenantOrDomainName("domain") - .scope("domain:mydomain") + TenantOrDomainAndCredentials<TokenCredentials> credentials = TenantOrDomainAndCredentials + .<TokenCredentials> builder().tenantOrDomainName("domain").scope("domain:mydomain") .credentials(TokenCredentials.builder().id("token").build()).build(); - - AuthInfo authInfo = authenticationApi.authenticateToken(credentials); + + checkTokenResult(credentials, "/v3/auth-token-scoped.json"); + } + + @SuppressWarnings("unchecked") + private void checkTokenResult(TenantOrDomainAndCredentials<?> credentials, String json) throws InterruptedException { + server.enqueue(jsonResponse("/v3/token.json")); + + AuthInfo authInfo = null; + + if (credentials.credentials() instanceof PasswordCredentials) { + authInfo = authenticationApi + .authenticatePassword((TenantOrDomainAndCredentials<PasswordCredentials>) credentials); + } else if (credentials.credentials() instanceof TokenCredentials) { + authInfo = authenticationApi.authenticateToken((TenantOrDomainAndCredentials<TokenCredentials>) credentials); + } else if (credentials.credentials() instanceof ApiAccessKeyCredentials) { + authInfo = authenticationApi + .authenticateAccessKey((TenantOrDomainAndCredentials<ApiAccessKeyCredentials>) 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)); } } http://git-wip-us.apache.org/repos/asf/jclouds/blob/93a805ca/apis/openstack-keystone/src/test/resources/v3/auth-password-project-scoped-id-domain-id.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 0000000..49b76e4 --- /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 http://git-wip-us.apache.org/repos/asf/jclouds/blob/93a805ca/apis/openstack-keystone/src/test/resources/v3/auth-password-project-scoped-id-domain-name.json ---------------------------------------------------------------------- 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 0000000..b910abe --- /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 http://git-wip-us.apache.org/repos/asf/jclouds/blob/93a805ca/apis/openstack-keystone/src/test/resources/v3/auth-password-project-scoped-name-domain-backwards-compat.json ---------------------------------------------------------------------- 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 0000000..69ea30e --- /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 http://git-wip-us.apache.org/repos/asf/jclouds/blob/93a805ca/apis/openstack-keystone/src/test/resources/v3/auth-password-project-scoped-name-domain-id.json ---------------------------------------------------------------------- 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 0000000..e99656f --- /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 http://git-wip-us.apache.org/repos/asf/jclouds/blob/93a805ca/apis/openstack-keystone/src/test/resources/v3/auth-password-project-scoped-name-domain-name.json ---------------------------------------------------------------------- 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 0000000..054393c --- /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
