diff options
author | Bjørn Christian Seime <bjorncs@oath.com> | 2017-10-16 12:13:04 +0200 |
---|---|---|
committer | Bjørn Christian Seime <bjorncs@oath.com> | 2017-10-16 12:13:04 +0200 |
commit | 8e22dbbac60165e2c887e05d33961ed6fa4b6a0f (patch) | |
tree | e5579a0c2e1d85e796d4751a8f224f97b24ec36d /controller-api | |
parent | a21f0df17e4ac59244a153ca4ba2ad0dc8ebee74 (diff) |
Revert "Open-source Athenz integration for controller-server"
This reverts commit 2d12b78dc2dd3a755e9a9dd05fa7f98acb2f544c.
Diffstat (limited to 'controller-api')
20 files changed, 751 insertions, 1 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/identifiers/AthensDomain.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/identifiers/AthensDomain.java index 194088df7dc..eb8b5c5256b 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/identifiers/AthensDomain.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/identifiers/AthensDomain.java @@ -4,7 +4,6 @@ package com.yahoo.vespa.hosted.controller.api.identifiers; /** * @author smorgrav */ -// TODO Rename to AthenzDomain public class AthensDomain extends Identifier { public AthensDomain(String id) { diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/ApplicationAction.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/ApplicationAction.java new file mode 100644 index 00000000000..cb5731164c8 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/ApplicationAction.java @@ -0,0 +1,17 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.athens; + +/** + * @author mpolden + */ +public enum ApplicationAction { + deploy("deployer"), + read("reader"), + write("writer"); + + public final String roleName; + + ApplicationAction(String roleName) { + this.roleName = roleName; + } +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/Athens.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/Athens.java new file mode 100644 index 00000000000..0635fb850b7 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/Athens.java @@ -0,0 +1,23 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.athens; + +import com.yahoo.vespa.hosted.controller.api.identifiers.AthensDomain; +import com.yahoo.vespa.hosted.controller.api.identifiers.ScrewdriverId; +import com.yahoo.vespa.hosted.controller.api.identifiers.UserId; + +/** + * Interface for integrating controller with Athens. + * + * @author mpolden + */ +public interface Athens { + + String principalTokenHeader(); + AthensPrincipal principalFrom(ScrewdriverId screwdriverId); + AthensPrincipal principalFrom(UserId userId); + NTokenValidator validator(); + NToken nTokenFrom(String rawToken); + ZmsClientFactory zmsClientFactory(); + AthensDomain screwdriverDomain(); + +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/AthensPrincipal.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/AthensPrincipal.java new file mode 100644 index 00000000000..58b878870b9 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/AthensPrincipal.java @@ -0,0 +1,59 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.athens; + +import com.yahoo.vespa.hosted.controller.api.identifiers.AthensDomain; +import com.yahoo.vespa.hosted.controller.api.identifiers.UserId; + +import java.security.Principal; +import java.util.Objects; + +/** + * @author bjorncs + */ +public class AthensPrincipal implements Principal { + + private final AthensDomain domain; + private final UserId userId; + + public AthensPrincipal(AthensDomain domain, UserId userId) { + this.domain = domain; + this.userId = userId; + } + + public UserId getUserId() { + return userId; + } + + public AthensDomain getDomain() { + return domain; + } + + public String toYRN() { + return domain.id() + "." + userId.id(); + } + + @Override + public String toString() { + return toYRN(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AthensPrincipal that = (AthensPrincipal) o; + return Objects.equals(domain, that.domain) && + Objects.equals(userId, that.userId); + } + + @Override + public int hashCode() { + return Objects.hash(domain, userId); + } + + @Override + public String getName() { + return userId.id(); + } + +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/AthensPublicKey.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/AthensPublicKey.java new file mode 100644 index 00000000000..9bbb5f28d8f --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/AthensPublicKey.java @@ -0,0 +1,48 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.athens; + +import java.security.PublicKey; +import java.util.Objects; + +/** + * @author bjorncs + */ +public class AthensPublicKey { + private final PublicKey publicKey; + private final String keyId; + + public AthensPublicKey(PublicKey publicKey, String keyId) { + this.publicKey = publicKey; + this.keyId = keyId; + } + + public PublicKey getPublicKey() { + return publicKey; + } + + public String getKeyId() { + return keyId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AthensPublicKey that = (AthensPublicKey) o; + return Objects.equals(publicKey, that.publicKey) && + Objects.equals(keyId, that.keyId); + } + + @Override + public int hashCode() { + return Objects.hash(publicKey, keyId); + } + + @Override + public String toString() { + return "AthensPublicKey{" + + "publicKey=" + publicKey + + ", keyId='" + keyId + '\'' + + '}'; + } +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/AthensService.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/AthensService.java new file mode 100644 index 00000000000..42af966be3d --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/AthensService.java @@ -0,0 +1,51 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.athens; + +import com.yahoo.vespa.hosted.controller.api.identifiers.AthensDomain; + +import java.util.Objects; + +/** + * @author bjorncs + */ +public class AthensService { + + private final AthensDomain domain; + private final String serviceName; + + public AthensService(AthensDomain domain, String serviceName) { + this.domain = domain; + this.serviceName = serviceName; + } + + public String toFullServiceName() { + return domain.id() + "." + serviceName; + } + + public AthensDomain getDomain() { + return domain; + } + + public String getServiceName() { + return serviceName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AthensService that = (AthensService) o; + return Objects.equals(domain, that.domain) && + Objects.equals(serviceName, that.serviceName); + } + + @Override + public int hashCode() { + return Objects.hash(domain, serviceName); + } + + @Override + public String toString() { + return String.format("AthensService(%s)", toFullServiceName()); + } +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/InvalidTokenException.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/InvalidTokenException.java new file mode 100644 index 00000000000..9c21d5814cb --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/InvalidTokenException.java @@ -0,0 +1,11 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.athens; + +/** + * @author bjorncs + */ +public class InvalidTokenException extends Exception { + public InvalidTokenException(String message) { + super(message); + } +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/NToken.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/NToken.java new file mode 100644 index 00000000000..b74872b4c6a --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/NToken.java @@ -0,0 +1,21 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.athens; + +import com.yahoo.vespa.hosted.controller.api.identifiers.AthensDomain; +import com.yahoo.vespa.hosted.controller.api.identifiers.UserId; + +import java.security.PublicKey; + +/** + * @author mpolden + */ +public interface NToken { + + AthensPrincipal getPrincipal(); + UserId getUser(); + AthensDomain getDomain(); + String getToken(); + String getKeyId(); + void validateSignatureAndExpiration(PublicKey publicKey) throws InvalidTokenException; + +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/NTokenValidator.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/NTokenValidator.java new file mode 100644 index 00000000000..905d7d864a3 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/NTokenValidator.java @@ -0,0 +1,12 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.athens; + +/** + * @author mpolden + */ +public interface NTokenValidator { + + void preloadPublicKeys(); + AthensPrincipal validate(NToken nToken) throws InvalidTokenException; + +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/ZmsClient.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/ZmsClient.java new file mode 100644 index 00000000000..7ff54957e16 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/ZmsClient.java @@ -0,0 +1,35 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.athens; + +import com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId; +import com.yahoo.vespa.hosted.controller.api.identifiers.AthensDomain; + +import java.util.List; + +/** + * @author bjorncs + */ +public interface ZmsClient { + void createTenant(AthensDomain tenantDomain); + + void deleteTenant(AthensDomain tenantDomain); + + void addApplication(AthensDomain tenantDomain, ApplicationId applicationName); + + void deleteApplication(AthensDomain tenantDomain, ApplicationId applicationName); + + boolean hasApplicationAccess(AthensPrincipal principal, ApplicationAction action, AthensDomain tenantDomain, ApplicationId applicationName); + + boolean hasTenantAdminAccess(AthensPrincipal principal, AthensDomain tenantDomain); + + // Used before vespa tenancy is established for the domain. + boolean isDomainAdmin(AthensPrincipal principal, AthensDomain domain); + + List<AthensDomain> getDomainList(String prefix); + + List<AthensDomain> getTenantDomainsForUser(AthensPrincipal principal); + + AthensPublicKey getPublicKey(AthensService service, String keyId); + + List<AthensPublicKey> getPublicKeys(AthensService service); +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/ZmsClientFactory.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/ZmsClientFactory.java new file mode 100644 index 00000000000..e00f3ce2f64 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/ZmsClientFactory.java @@ -0,0 +1,11 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.athens; + +/** + * @author bjorncs + */ +public interface ZmsClientFactory { + ZmsClient createClientWithServicePrincipal(); + + ZmsClient createClientWithAuthorizedServiceToken(NToken authorizedServiceToken); +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/ZmsException.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/ZmsException.java new file mode 100644 index 00000000000..ed5b2daca86 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/ZmsException.java @@ -0,0 +1,23 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.athens; + +/** + * @author bjorncs + */ +public class ZmsException extends RuntimeException { + + private final int code; + + public ZmsException(Throwable t, int code) { + super(t.getMessage(), t); + this.code = code; + } + + public ZmsException(int code) { + this.code = code; + } + + public int getCode() { + return code; + } +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/ZmsKeystore.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/ZmsKeystore.java new file mode 100644 index 00000000000..4f8e5f5ff05 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/ZmsKeystore.java @@ -0,0 +1,19 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.athens; + +import java.security.PublicKey; +import java.util.Optional; + +/** + * Interface for a keystore containing public keys for Athens services + * + * @author bjorncs + */ +@FunctionalInterface +public interface ZmsKeystore { + Optional<PublicKey> getPublicKey(AthensService service, String keyId); + + default void preloadKeys(AthensService service) { + // Default implementation is noop + } +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/AthensDbMock.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/AthensDbMock.java new file mode 100644 index 00000000000..8a02d0dcff5 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/AthensDbMock.java @@ -0,0 +1,73 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.athens.mock; + +import com.yahoo.vespa.hosted.controller.api.integration.athens.ApplicationAction; +import com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId; +import com.yahoo.vespa.hosted.controller.api.identifiers.AthensDomain; +import com.yahoo.vespa.hosted.controller.api.integration.athens.AthensPrincipal; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * @author bjorncs + */ +public class AthensDbMock { + + public final Map<AthensDomain, Domain> domains = new HashMap<>(); + + public AthensDbMock addDomain(Domain domain) { + domains.put(domain.name, domain); + return this; + } + + public static class Domain { + + public final AthensDomain name; + public final Set<AthensPrincipal> admins = new HashSet<>(); + public final Set<AthensPrincipal> tenantAdmins = new HashSet<>(); + public final Map<ApplicationId, Application> applications = new HashMap<>(); + public boolean isVespaTenant = false; + + public Domain(AthensDomain name) { + this.name = name; + } + + public Domain admin(AthensPrincipal user) { + admins.add(user); + return this; + } + + public Domain tenantAdmin(AthensPrincipal user) { + tenantAdmins.add(user); + return this; + } + + /** + * Simulates establishing Vespa tenancy in Athens. + */ + public void markAsVespaTenant() { + isVespaTenant = true; + } + + } + + public static class Application { + + public final Map<ApplicationAction, Set<AthensPrincipal>> acl = new HashMap<>(); + + public Application() { + acl.put(ApplicationAction.deploy, new HashSet<>()); + acl.put(ApplicationAction.read, new HashSet<>()); + acl.put(ApplicationAction.write, new HashSet<>()); + } + + public Application addRoleMember(ApplicationAction action, AthensPrincipal user) { + acl.get(action).add(user); + return this; + } + } + +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/AthensMock.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/AthensMock.java new file mode 100644 index 00000000000..8bed95bfcd4 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/AthensMock.java @@ -0,0 +1,87 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.athens.mock; + +import com.google.inject.Inject; +import com.yahoo.component.AbstractComponent; +import com.yahoo.vespa.hosted.controller.api.identifiers.AthensDomain; +import com.yahoo.vespa.hosted.controller.api.identifiers.ScrewdriverId; +import com.yahoo.vespa.hosted.controller.api.identifiers.UserId; +import com.yahoo.vespa.hosted.controller.api.integration.athens.Athens; +import com.yahoo.vespa.hosted.controller.api.integration.athens.AthensPrincipal; +import com.yahoo.vespa.hosted.controller.api.integration.athens.InvalidTokenException; +import com.yahoo.vespa.hosted.controller.api.integration.athens.NToken; +import com.yahoo.vespa.hosted.controller.api.integration.athens.NTokenValidator; +import com.yahoo.vespa.hosted.controller.api.integration.athens.ZmsClientFactory; + +/** + * @author mpolden + */ +public class AthensMock extends AbstractComponent implements Athens { + + private static final AthensDomain userDomain = new AthensDomain("domain1"); + private static final AthensDomain screwdriverDomain = new AthensDomain("screwdriver-domain"); + + private final ZmsClientFactory zmsClientFactory; + private final NTokenValidator nTokenValidator; + + public AthensMock(AthensDbMock athensDb, NTokenValidator nTokenValidator) { + this.zmsClientFactory = new ZmsClientFactoryMock(athensDb); + this.nTokenValidator = nTokenValidator; + } + + public AthensMock(AthensDbMock athensDbMock) { + this(athensDbMock, mockValidator); + } + + @Inject + public AthensMock() { + this(new AthensDbMock(), mockValidator); + } + + @Override + public String principalTokenHeader() { + return "X-Athens-Token"; + } + + @Override + public AthensPrincipal principalFrom(ScrewdriverId screwdriverId) { + return new AthensPrincipal(screwdriverDomain, new UserId("screwdriver-" + screwdriverId.id())); + } + + @Override + public AthensPrincipal principalFrom(UserId userId) { + return new AthensPrincipal(userDomain, userId); + } + + @Override + public NTokenValidator validator() { + return nTokenValidator; + } + + @Override + public NToken nTokenFrom(String rawToken) { + return new NTokenMock(rawToken); + } + + @Override + public ZmsClientFactory zmsClientFactory() { + return zmsClientFactory; + } + + @Override + public AthensDomain screwdriverDomain() { + return screwdriverDomain; + } + + private static final NTokenValidator mockValidator = new NTokenValidator() { + @Override + public void preloadPublicKeys() { + } + + @Override + public AthensPrincipal validate(NToken nToken) throws InvalidTokenException { + return nToken.getPrincipal(); + } + }; + +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/NTokenMock.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/NTokenMock.java new file mode 100644 index 00000000000..ae23a69e409 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/NTokenMock.java @@ -0,0 +1,68 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.athens.mock; + +import com.yahoo.vespa.hosted.controller.api.identifiers.AthensDomain; +import com.yahoo.vespa.hosted.controller.api.identifiers.UserId; +import com.yahoo.vespa.hosted.controller.api.integration.athens.AthensPrincipal; +import com.yahoo.vespa.hosted.controller.api.integration.athens.InvalidTokenException; +import com.yahoo.vespa.hosted.controller.api.integration.athens.NToken; + +import java.security.PublicKey; +import java.util.Objects; + +/** + * @author mpolden + */ +public class NTokenMock implements NToken { + + private static final AthensDomain domain = new AthensDomain("test"); + private static final UserId userId = new UserId("user"); + + private final String rawToken; + + public NTokenMock(String rawToken) { + this.rawToken = rawToken; + } + + @Override + public AthensPrincipal getPrincipal() { + return new AthensPrincipal(domain, userId); + } + + @Override + public UserId getUser() { + return userId; + } + + @Override + public AthensDomain getDomain() { + return domain; + } + + @Override + public String getToken() { + return "test-token"; + } + + @Override + public String getKeyId() { + return "test-key"; + } + + @Override + public void validateSignatureAndExpiration(PublicKey publicKey) throws InvalidTokenException { + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof NTokenMock)) return false; + NTokenMock that = (NTokenMock) o; + return Objects.equals(rawToken, that.rawToken); + } + + @Override + public int hashCode() { + return Objects.hash(rawToken); + } +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/ZmsClientFactoryMock.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/ZmsClientFactoryMock.java new file mode 100644 index 00000000000..8bc8b29fb4c --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/ZmsClientFactoryMock.java @@ -0,0 +1,49 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.athens.mock; + +import com.yahoo.component.AbstractComponent; +import com.yahoo.vespa.hosted.controller.api.integration.athens.NToken; +import com.yahoo.vespa.hosted.controller.api.integration.athens.ZmsClient; +import com.yahoo.vespa.hosted.controller.api.integration.athens.ZmsClientFactory; + +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * @author bjorncs + */ +public class ZmsClientFactoryMock extends AbstractComponent implements ZmsClientFactory { + + private static final Logger log = Logger.getLogger(ZmsClientFactoryMock.class.getName()); + + private final AthensDbMock athens; + + public ZmsClientFactoryMock() { + this(new AthensDbMock()); + } + + ZmsClientFactoryMock(AthensDbMock athens) { + this.athens = athens; + } + + public AthensDbMock getSetup() { + return athens; + } + + @Override + public ZmsClient createClientWithServicePrincipal() { + log("createClientWithServicePrincipal()"); + return new ZmsClientMock(athens); + } + + @Override + public ZmsClient createClientWithAuthorizedServiceToken(NToken authorizedServiceToken) { + log("createClientWithAuthorizedServiceToken(authorizedServiceToken='%s')", authorizedServiceToken); + return new ZmsClientMock(athens); + } + + private static void log(String format, Object... args) { + log.log(Level.INFO, String.format(format, args)); + } + +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/ZmsClientMock.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/ZmsClientMock.java new file mode 100644 index 00000000000..97f391f792d --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/ZmsClientMock.java @@ -0,0 +1,131 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.api.integration.athens.mock; + +import com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId; +import com.yahoo.vespa.hosted.controller.api.identifiers.AthensDomain; +import com.yahoo.vespa.hosted.controller.api.integration.athens.ApplicationAction; +import com.yahoo.vespa.hosted.controller.api.integration.athens.AthensPrincipal; +import com.yahoo.vespa.hosted.controller.api.integration.athens.AthensPublicKey; +import com.yahoo.vespa.hosted.controller.api.integration.athens.AthensService; +import com.yahoo.vespa.hosted.controller.api.integration.athens.ZmsClient; +import com.yahoo.vespa.hosted.controller.api.integration.athens.ZmsException; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.util.stream.Collectors.toList; + +/** + * @author bjorncs + */ +public class ZmsClientMock implements ZmsClient { + + private static final Logger log = Logger.getLogger(ZmsClientMock.class.getName()); + + private final AthensDbMock athens; + + public ZmsClientMock(AthensDbMock athens) { + this.athens = athens; + } + + @Override + public void createTenant(AthensDomain tenantDomain) { + log("createTenant(tenantDomain='%s')", tenantDomain); + getDomainOrThrow(tenantDomain, false).isVespaTenant = true; + } + + @Override + public void deleteTenant(AthensDomain tenantDomain) { + log("deleteTenant(tenantDomain='%s')", tenantDomain); + AthensDbMock.Domain domain = getDomainOrThrow(tenantDomain, false); + domain.isVespaTenant = false; + domain.applications.clear(); + domain.tenantAdmins.clear(); + } + + @Override + public void addApplication(AthensDomain tenantDomain, ApplicationId applicationName) { + log("addApplication(tenantDomain='%s', applicationName='%s')", tenantDomain, applicationName); + AthensDbMock.Domain domain = getDomainOrThrow(tenantDomain, true); + if (!domain.applications.containsKey(applicationName)) { + domain.applications.put(applicationName, new AthensDbMock.Application()); + } + } + + @Override + public void deleteApplication(AthensDomain tenantDomain, ApplicationId applicationName) { + log("addApplication(tenantDomain='%s', applicationName='%s')", tenantDomain, applicationName); + getDomainOrThrow(tenantDomain, true).applications.remove(applicationName); + } + + @Override + public boolean hasApplicationAccess(AthensPrincipal principal, ApplicationAction action, AthensDomain tenantDomain, ApplicationId applicationName) { + log("hasApplicationAccess(principal='%s', action='%s', tenantDomain='%s', applicationName='%s')", + principal, action, tenantDomain, applicationName); + AthensDbMock.Domain domain = getDomainOrThrow(tenantDomain, true); + AthensDbMock.Application application = domain.applications.get(applicationName); + if (application == null) { + throw zmsException(400, "Application '%s' not found", applicationName); + } + return domain.admins.contains(principal) || application.acl.get(action).contains(principal); + } + + @Override + public boolean hasTenantAdminAccess(AthensPrincipal principal, AthensDomain tenantDomain) { + log("hasTenantAdminAccess(principal='%s', tenantDomain='%s')", principal, tenantDomain); + return isDomainAdmin(principal, tenantDomain) || + getDomainOrThrow(tenantDomain, true).tenantAdmins.contains(principal); + } + + @Override + public boolean isDomainAdmin(AthensPrincipal principal, AthensDomain domain) { + log("isDomainAdmin(principal='%s', domain='%s')", principal, domain); + return getDomainOrThrow(domain, false).admins.contains(principal); + } + + @Override + public List<AthensDomain> getDomainList(String prefix) { + log("getDomainList()"); + return new ArrayList<>(athens.domains.keySet()); + } + + @Override + public List<AthensDomain> getTenantDomainsForUser(AthensPrincipal principal) { + log("getTenantDomainsForUser(principal='%s')", principal); + return athens.domains.values().stream() + .filter(domain -> domain.tenantAdmins.contains(principal) || domain.admins.contains(principal)) + .map(domain -> domain.name) + .collect(toList()); + } + + @Override + public AthensPublicKey getPublicKey(AthensService service, String keyId) { + throw new UnsupportedOperationException(); + } + + @Override + public List<AthensPublicKey> getPublicKeys(AthensService service) { + throw new UnsupportedOperationException(); + } + + private AthensDbMock.Domain getDomainOrThrow(AthensDomain domainName, boolean verifyVespaTenant) { + AthensDbMock.Domain domain = Optional.ofNullable(athens.domains.get(domainName)) + .orElseThrow(() -> zmsException(400, "Domain '%s' not found", domainName)); + if (verifyVespaTenant && !domain.isVespaTenant) { + throw zmsException(400, "Domain not a Vespa tenant: '%s'", domainName); + } + return domain; + } + + private static ZmsException zmsException(int code, String message, Object... args) { + return new ZmsException(new RuntimeException(String.format(message, args)), code); + } + + private static void log(String format, Object... args) { + log.log(Level.INFO, String.format(format, args)); + } + +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/package-info.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/package-info.java new file mode 100644 index 00000000000..d4454503786 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/package-info.java @@ -0,0 +1,8 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +/** + * @author bjorncs + */ +@ExportPackage +package com.yahoo.vespa.hosted.controller.api.integration.athens.mock; + +import com.yahoo.osgi.annotation.ExportPackage; diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/package-info.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/package-info.java new file mode 100644 index 00000000000..eabe214abf2 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/package-info.java @@ -0,0 +1,5 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +@ExportPackage +package com.yahoo.vespa.hosted.controller.api.integration.athens; + +import com.yahoo.osgi.annotation.ExportPackage; |