summaryrefslogtreecommitdiffstats
path: root/controller-api
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@oath.com>2017-10-16 12:13:04 +0200
committerBjørn Christian Seime <bjorncs@oath.com>2017-10-16 12:13:04 +0200
commit8e22dbbac60165e2c887e05d33961ed6fa4b6a0f (patch)
treee5579a0c2e1d85e796d4751a8f224f97b24ec36d /controller-api
parenta21f0df17e4ac59244a153ca4ba2ad0dc8ebee74 (diff)
Revert "Open-source Athenz integration for controller-server"
This reverts commit 2d12b78dc2dd3a755e9a9dd05fa7f98acb2f544c.
Diffstat (limited to 'controller-api')
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/identifiers/AthensDomain.java1
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/ApplicationAction.java17
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/Athens.java23
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/AthensPrincipal.java59
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/AthensPublicKey.java48
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/AthensService.java51
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/InvalidTokenException.java11
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/NToken.java21
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/NTokenValidator.java12
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/ZmsClient.java35
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/ZmsClientFactory.java11
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/ZmsException.java23
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/ZmsKeystore.java19
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/AthensDbMock.java73
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/AthensMock.java87
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/NTokenMock.java68
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/ZmsClientFactoryMock.java49
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/ZmsClientMock.java131
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/mock/package-info.java8
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athens/package-info.java5
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;