diff options
author | Morten Tokle <morten.tokle@gmail.com> | 2018-10-25 08:59:00 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-10-25 08:59:00 +0200 |
commit | d39518cec5b3b0202efbcc8a9a22527136f83a3c (patch) | |
tree | 9f5442d90fa63fbd0a4249f11b4f0086a21a48b3 /vespa-athenz | |
parent | 9a91a16f12a00eae938b349b44fce589451ed3b2 (diff) | |
parent | d3d43177c8bc9a5a3a376f5eaf2384edcfc7e285 (diff) |
Merge pull request #7436 from vespa-engine/bjorncs/athenz-okta-tenancy
Bjorncs/athenz okta tenancy
Diffstat (limited to 'vespa-athenz')
24 files changed, 608 insertions, 84 deletions
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzResourceGroup.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzResourceGroup.java new file mode 100644 index 00000000000..2825cf57c7b --- /dev/null +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/AthenzResourceGroup.java @@ -0,0 +1,40 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.athenz.api; + +import java.util.Objects; + +/** + * @author bjorncs + */ +public class AthenzResourceGroup { + private final String name; + + public AthenzResourceGroup(String name) { + this.name = name; + } + + public String name() { + return name; + } + + @Override + public String toString() { + return "AthenzResourceGroup{" + + "name='" + name + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AthenzResourceGroup that = (AthenzResourceGroup) o; + return Objects.equals(name, that.name); + } + + @Override + public int hashCode() { + return Objects.hash(name); + } + +} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/OktaAccessToken.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/OktaAccessToken.java new file mode 100644 index 00000000000..8b19f7abdd5 --- /dev/null +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/OktaAccessToken.java @@ -0,0 +1,42 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.athenz.api; + +import java.util.Objects; + +/** + * @author bjorncs + */ +public class OktaAccessToken { + + public static final String HTTP_HEADER_NAME = "Okta-Access-Token"; + + private final String token; + + public OktaAccessToken(String token) { + this.token = token; + } + + public String token() { + return token; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + OktaAccessToken that = (OktaAccessToken) o; + return Objects.equals(token, that.token); + } + + @Override + public int hashCode() { + return Objects.hash(token); + } + + @Override + public String toString() { + return "OktaAccessToken{" + + "token='" + token + '\'' + + '}'; + } +} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/common/ClientBase.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/common/ClientBase.java new file mode 100644 index 00000000000..02952d7c26b --- /dev/null +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/common/ClientBase.java @@ -0,0 +1,95 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.athenz.client.common; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.yahoo.vespa.athenz.client.common.bindings.ErrorResponseEntity; +import com.yahoo.vespa.athenz.identity.ServiceIdentitySslSocketFactory; +import org.apache.http.HttpResponse; +import org.apache.http.client.ResponseHandler; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; +import org.apache.http.impl.client.HttpClientBuilder; +import org.eclipse.jetty.http.HttpStatus; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.time.Duration; +import java.util.function.Supplier; + +/** + * @author bjorncs + */ +public abstract class ClientBase implements AutoCloseable { + + private static final ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule()); + + private final CloseableHttpClient client; + private final ClientExceptionFactory exceptionFactory; + + protected ClientBase(String userAgent, + Supplier<SSLContext> sslContextSupplier, + ClientExceptionFactory exceptionFactory) { + this.exceptionFactory = exceptionFactory; + this.client = createHttpClient(userAgent, sslContextSupplier); + } + + protected <T> T execute(HttpUriRequest request, ResponseHandler<T> responseHandler) { + try { + return client.execute(request, responseHandler); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + protected StringEntity toJsonStringEntity(Object entity) { + try { + return new StringEntity(objectMapper.writeValueAsString(entity), ContentType.APPLICATION_JSON); + } catch (JsonProcessingException e) { + throw new UncheckedIOException(e); + } + } + + protected <T> T readEntity(HttpResponse response, Class<T> entityType) throws IOException { + if (HttpStatus.isSuccess(response.getStatusLine().getStatusCode())) { + return objectMapper.readValue(response.getEntity().getContent(), entityType); + } else { + ErrorResponseEntity errorEntity = objectMapper.readValue(response.getEntity().getContent(), ErrorResponseEntity.class); + throw exceptionFactory.createException(errorEntity.code, errorEntity.description); + } + } + + private static CloseableHttpClient createHttpClient(String userAgent, Supplier<SSLContext> sslContextSupplier) { + return HttpClientBuilder.create() + .setRetryHandler(new DefaultHttpRequestRetryHandler(3, /*requestSentRetryEnabled*/true)) + .setUserAgent(userAgent) + .setSSLSocketFactory(new SSLConnectionSocketFactory(new ServiceIdentitySslSocketFactory(sslContextSupplier), (HostnameVerifier)null)) + .setDefaultRequestConfig(RequestConfig.custom() + .setConnectTimeout((int) Duration.ofSeconds(10).toMillis()) + .setConnectionRequestTimeout((int)Duration.ofSeconds(10).toMillis()) + .setSocketTimeout((int)Duration.ofSeconds(20).toMillis()) + .build()) + .build(); + } + + @Override + public void close() { + try { + this.client.close(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + protected interface ClientExceptionFactory { + RuntimeException createException(int errorCode, String description); + } +} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/ErrorResponseEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/common/bindings/ErrorResponseEntity.java index 431af084f9f..acbb831194e 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/ErrorResponseEntity.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/common/bindings/ErrorResponseEntity.java @@ -1,5 +1,5 @@ // Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.athenz.client.zts.bindings; +package com.yahoo.vespa.athenz.client.common.bindings; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/Pkcs10CsrSerializer.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/common/serializers/Pkcs10CsrSerializer.java index ca33962c7c8..9b26f5f2517 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/Pkcs10CsrSerializer.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/common/serializers/Pkcs10CsrSerializer.java @@ -1,5 +1,5 @@ // Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.athenz.client.zts.bindings.serializers; +package com.yahoo.vespa.athenz.client.common.serializers; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/X509CertificateDeserializer.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/common/serializers/X509CertificateDeserializer.java index 59f10a78a58..f948115d4f2 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/X509CertificateDeserializer.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/common/serializers/X509CertificateDeserializer.java @@ -1,5 +1,5 @@ // Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.athenz.client.zts.bindings.serializers; +package com.yahoo.vespa.athenz.client.common.serializers; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/X509CertificateListDeserializer.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/common/serializers/X509CertificateListDeserializer.java index 64b23af9295..baa56512a62 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/X509CertificateListDeserializer.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/common/serializers/X509CertificateListDeserializer.java @@ -1,5 +1,5 @@ // Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.athenz.client.zts.bindings.serializers; +package com.yahoo.vespa.athenz.client.common.serializers; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/package-info.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/common/serializers/package-info.java index 4c442617494..f1fe2a75b1b 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/serializers/package-info.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/common/serializers/package-info.java @@ -3,6 +3,6 @@ * @author bjorncs */ @ExportPackage -package com.yahoo.vespa.athenz.client.zts.bindings.serializers; +package com.yahoo.vespa.athenz.client.common.serializers; import com.yahoo.osgi.annotation.ExportPackage;
\ No newline at end of file diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/DefaultZmsClient.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/DefaultZmsClient.java new file mode 100644 index 00000000000..ed9c1da56c2 --- /dev/null +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/DefaultZmsClient.java @@ -0,0 +1,141 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.athenz.client.zms; + +import com.yahoo.vespa.athenz.api.AthenzDomain; +import com.yahoo.vespa.athenz.api.AthenzIdentity; +import com.yahoo.vespa.athenz.api.AthenzResourceName; +import com.yahoo.vespa.athenz.api.AthenzRole; +import com.yahoo.vespa.athenz.api.AthenzService; +import com.yahoo.vespa.athenz.api.OktaAccessToken; +import com.yahoo.vespa.athenz.client.common.ClientBase; +import com.yahoo.vespa.athenz.client.zms.bindings.AccessResponseEntity; +import com.yahoo.vespa.athenz.client.zms.bindings.DomainListResponseEntity; +import com.yahoo.vespa.athenz.client.zms.bindings.MembershipResponseEntity; +import com.yahoo.vespa.athenz.client.zms.bindings.ProviderResourceGroupRolesRequestEntity; +import com.yahoo.vespa.athenz.client.zms.bindings.TenancyRequestEntity; +import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider; +import org.apache.http.Header; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.methods.RequestBuilder; +import org.apache.http.message.BasicHeader; + +import javax.net.ssl.SSLContext; +import java.net.URI; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; + +import static java.util.stream.Collectors.toList; + +/** + * @author bjorncs + */ +public class DefaultZmsClient extends ClientBase implements ZmsClient { + + private final URI zmsUrl; + private final AthenzIdentity identity; + + public DefaultZmsClient(URI zmsUrl, AthenzIdentity identity, SSLContext sslContext) { + this(zmsUrl, identity, () -> sslContext); + } + + public DefaultZmsClient(URI zmsUrl, ServiceIdentityProvider identityProvider) { + this(zmsUrl, identityProvider.identity(), identityProvider::getIdentitySslContext); + } + + private DefaultZmsClient(URI zmsUrl, AthenzIdentity identity, Supplier<SSLContext> sslContextSupplier) { + super("vespa-zms-client", sslContextSupplier, ZmsClientException::new); + this.zmsUrl = addTrailingSlash(zmsUrl); + this.identity = identity; + } + + private static URI addTrailingSlash(URI zmsUrl) { + return zmsUrl.getPath().endsWith("/") ? zmsUrl : URI.create(zmsUrl.toString() + '/'); + } + + @Override + public void createTenancy(AthenzDomain tenantDomain, AthenzService providerService, OktaAccessToken token) { + URI uri = zmsUrl.resolve(String.format("domain/%s/tenancy/%s", tenantDomain.getName(), providerService.getFullName())); + HttpUriRequest request = RequestBuilder.put() + .setUri(uri) + .addHeader(creatOktaAccessTokenHeader(token)) + .setEntity(toJsonStringEntity(new TenancyRequestEntity(tenantDomain, providerService, Collections.emptyList()))) + .build(); + execute(request, response -> { + readEntity(response, String.class); + return response.getStatusLine().getStatusCode(); + }); + } + + @Override + public void deleteTenancy(AthenzDomain tenantDomain, AthenzService providerService, OktaAccessToken token) { + URI uri = zmsUrl.resolve(String.format("domain/%s/tenancy/%s", tenantDomain.getName(), providerService.getFullName())); + HttpUriRequest request = RequestBuilder.delete() + .setUri(uri) + .addHeader(creatOktaAccessTokenHeader(token)) + .build(); + execute(request, response -> readEntity(response, String.class)); + } + + @Override + public void createProviderResourceGroup(AthenzDomain tenantDomain, AthenzService providerService, String resourceGroup, Set<RoleAction> roleActions, OktaAccessToken token) { + URI uri = zmsUrl.resolve(String.format("domain/%s/provDomain/%s/provService/%s/resourceGroup/%s", tenantDomain.getName(), providerService.getDomainName(), providerService.getName(), resourceGroup)); + HttpUriRequest request = RequestBuilder.put() + .setUri(uri) + .addHeader(creatOktaAccessTokenHeader(token)) + .setEntity(toJsonStringEntity(new ProviderResourceGroupRolesRequestEntity(providerService, tenantDomain, roleActions, resourceGroup))) + .build(); + execute(request, response -> readEntity(response, String.class)); // The ZMS API will return a json object that is similar to ProviderResourceGroupRolesRequestEntity + } + + @Override + public void deleteProviderResourceGroup(AthenzDomain tenantDomain, AthenzService providerService, String resourceGroup, OktaAccessToken token) { + URI uri = zmsUrl.resolve(String.format("domain/%s/provDomain/%s/provService/%s/resourceGroup/%s", tenantDomain.getName(), providerService.getDomainName(), providerService.getName(), resourceGroup)); + HttpUriRequest request = RequestBuilder.delete() + .setUri(uri) + .addHeader(creatOktaAccessTokenHeader(token)) + .build(); + execute(request, response -> readEntity(response, String.class)); + } + + @Override + public boolean getMembership(AthenzRole role, AthenzIdentity identity) { + URI uri = zmsUrl.resolve(String.format("domain/%s/role/%s/member/%s", role.domain().getName(), role.roleName(), identity.getFullName())); + HttpUriRequest request = RequestBuilder.get() + .setUri(uri) + .build(); + return execute(request, response -> { + MembershipResponseEntity membership = readEntity(response, MembershipResponseEntity.class); + return membership.isMember; + }); + } + + @Override + public List<AthenzDomain> getDomainList(String prefix) { + HttpUriRequest request = RequestBuilder.get() + .setUri(zmsUrl.resolve("domain")) + .addParameter("prefix", prefix) + .build(); + return execute(request, response -> { + DomainListResponseEntity result = readEntity(response, DomainListResponseEntity.class); + return result.domains.stream().map(AthenzDomain::new).collect(toList()); + }); + } + + @Override + public boolean hasAccess(AthenzResourceName resource, String action, AthenzIdentity identity) { + URI uri = zmsUrl.resolve(String.format("access/%s/%s", action, resource.toResourceNameString())); + HttpUriRequest request = RequestBuilder.get() + .setUri(uri) + .build(); + return execute(request, response -> { + AccessResponseEntity result = readEntity(response, AccessResponseEntity.class); + return result.granted; + }); + } + + private static Header creatOktaAccessTokenHeader(OktaAccessToken token) { + return new BasicHeader("Cookie", String.format("okta_at=%s", token.token())); + } +} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/RoleAction.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/RoleAction.java new file mode 100644 index 00000000000..405dd1aa56a --- /dev/null +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/RoleAction.java @@ -0,0 +1,49 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.athenz.client.zms; + +import com.yahoo.vespa.athenz.api.AthenzRole; + +import java.util.Objects; + +/** + * @author bjorncs + */ +public class RoleAction { + private final String roleName; + private final String action; + + public RoleAction(String roleName, String action) { + this.roleName = roleName; + this.action = action; + } + + public String getRoleName() { + return roleName; + } + + public String getAction() { + return action; + } + + @Override + public String toString() { + return "RoleAction{" + + "roleName=" + roleName + + ", action='" + action + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RoleAction that = (RoleAction) o; + return Objects.equals(roleName, that.roleName) && + Objects.equals(action, that.action); + } + + @Override + public int hashCode() { + return Objects.hash(roleName, action); + } +} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/ZmsClient.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/ZmsClient.java new file mode 100644 index 00000000000..cf044edeac0 --- /dev/null +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/ZmsClient.java @@ -0,0 +1,35 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.athenz.client.zms; + +import com.yahoo.vespa.athenz.api.AthenzDomain; +import com.yahoo.vespa.athenz.api.AthenzIdentity; +import com.yahoo.vespa.athenz.api.AthenzResourceName; +import com.yahoo.vespa.athenz.api.AthenzRole; +import com.yahoo.vespa.athenz.api.AthenzService; +import com.yahoo.vespa.athenz.api.OktaAccessToken; + +import java.time.Instant; +import java.util.List; +import java.util.Set; + +/** + * @author bjorncs + */ +public interface ZmsClient extends AutoCloseable { + + void createTenancy(AthenzDomain tenantDomain, AthenzService providerService, OktaAccessToken token); + + void deleteTenancy(AthenzDomain tenantDomain, AthenzService providerService, OktaAccessToken token); + + void createProviderResourceGroup(AthenzDomain tenantDomain, AthenzService providerService, String resourceGroup, Set<RoleAction> roleActions, OktaAccessToken token); + + void deleteProviderResourceGroup(AthenzDomain tenantDomain, AthenzService providerService, String resourceGroup, OktaAccessToken token); + + boolean getMembership(AthenzRole role, AthenzIdentity identity); + + List<AthenzDomain> getDomainList(String prefix); + + boolean hasAccess(AthenzResourceName resource, String action, AthenzIdentity identity); + + void close(); +} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/ZmsClientException.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/ZmsClientException.java new file mode 100644 index 00000000000..f1b3ab8e7da --- /dev/null +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/ZmsClientException.java @@ -0,0 +1,32 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.athenz.client.zms; + +/** + * An exception that can be thrown by {@link ZmsClient} implementations. + * + * @author bjorncs + */ +public class ZmsClientException extends RuntimeException { + + private final int errorCode; + private final String description; + + public ZmsClientException(int errorCode, String description) { + super(createMessage(errorCode, description)); + this.errorCode = errorCode; + this.description = description; + } + + public int getErrorCode() { + return errorCode; + } + + public String getDescription() { + return description; + } + + private static String createMessage(int code, String description) { + return String.format("Received error from ZMS: code=%d, message=\"%s\"", code, description); + } + +} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/bindings/AccessResponseEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/bindings/AccessResponseEntity.java new file mode 100644 index 00000000000..dcc17bc807a --- /dev/null +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/bindings/AccessResponseEntity.java @@ -0,0 +1,19 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.athenz.client.zms.bindings; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * @author bjorncs + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class AccessResponseEntity { + public final boolean granted; + + @JsonCreator + public AccessResponseEntity(@JsonProperty("granted") boolean granted) { + this.granted = granted; + } +} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/bindings/DomainListResponseEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/bindings/DomainListResponseEntity.java new file mode 100644 index 00000000000..938d85dd74f --- /dev/null +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/bindings/DomainListResponseEntity.java @@ -0,0 +1,21 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.athenz.client.zms.bindings; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +/** + * @author bjorncs + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class DomainListResponseEntity { + public final List<String> domains; + + @JsonCreator + public DomainListResponseEntity(@JsonProperty("names") List<String> domains) { + this.domains = domains; + } +} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/bindings/MembershipResponseEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/bindings/MembershipResponseEntity.java new file mode 100644 index 00000000000..499afb48f25 --- /dev/null +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/bindings/MembershipResponseEntity.java @@ -0,0 +1,28 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.athenz.client.zms.bindings; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * @author bjorncs + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class MembershipResponseEntity { + public final String memberName; + public final boolean isMember; + public final String roleName; + public final String expiration; + + @JsonCreator + public MembershipResponseEntity(@JsonProperty("memberName") String memberName, + @JsonProperty("isMember") boolean isMember, + @JsonProperty("roleName") String roleName, + @JsonProperty("expiration") String expiration) { + this.memberName = memberName; + this.isMember = isMember; + this.roleName = roleName; + this.expiration = expiration; + } +} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/bindings/ProviderResourceGroupRolesRequestEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/bindings/ProviderResourceGroupRolesRequestEntity.java new file mode 100644 index 00000000000..dccd18fed61 --- /dev/null +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/bindings/ProviderResourceGroupRolesRequestEntity.java @@ -0,0 +1,56 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.athenz.client.zms.bindings; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.yahoo.vespa.athenz.api.AthenzDomain; +import com.yahoo.vespa.athenz.api.AthenzIdentity; +import com.yahoo.vespa.athenz.api.AthenzService; +import com.yahoo.vespa.athenz.client.zms.RoleAction; + +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import static java.util.stream.Collectors.toList; + +/** + * @author bjorncs + */ +public class ProviderResourceGroupRolesRequestEntity { + + @JsonProperty("domain") + private final String domain; + + @JsonProperty("service") + private final String service; + + @JsonProperty("tenant") + private final String tenant; + + @JsonProperty("roles") + private final List<TenantRoleAction> roles; + + @JsonProperty("resourceGroup") + private final String resourceGroup; + + public ProviderResourceGroupRolesRequestEntity(AthenzService providerService, AthenzDomain tenantDomain, Set<RoleAction> rolesActions, String resourceGroup) { + this.domain = providerService.getDomainName(); + this.service = providerService.getName(); + this.tenant = tenantDomain.getName(); + this.roles = rolesActions.stream().map(roleAction -> new TenantRoleAction(roleAction.getRoleName(), roleAction.getAction())).collect(toList()); + this.resourceGroup = resourceGroup; + } + + public static class TenantRoleAction { + @JsonProperty("role") + private final String role; + + @JsonProperty("action") + private final String action; + + public TenantRoleAction(String role, String action) { + this.role = role; + this.action = action; + } + } +} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/bindings/TenancyRequestEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/bindings/TenancyRequestEntity.java new file mode 100644 index 00000000000..7883a505c71 --- /dev/null +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/bindings/TenancyRequestEntity.java @@ -0,0 +1,31 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.athenz.client.zms.bindings; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.yahoo.vespa.athenz.api.AthenzDomain; +import com.yahoo.vespa.athenz.api.AthenzService; + +import java.util.List; + +/** + * @author bjorncs + */ +public class TenancyRequestEntity { + + @JsonProperty("domain") + private final String tenantDomain; + + @JsonProperty("service") + private final String providerService; + + @JsonProperty("resourceGroups") + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private final List<String> resourceGroups; + + public TenancyRequestEntity(AthenzDomain tenantDomain, AthenzService providerService, List<String> resourceGroups) { + this.tenantDomain = tenantDomain.getName(); + this.providerService = providerService.getFullName(); + this.resourceGroups = resourceGroups; + } +} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/DefaultZtsClient.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/DefaultZtsClient.java index e9aba31cf56..9eef2ff9903 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/DefaultZtsClient.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/DefaultZtsClient.java @@ -1,16 +1,13 @@ // Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.athenz.client.zts; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.yahoo.vespa.athenz.api.AthenzDomain; import com.yahoo.vespa.athenz.api.AthenzIdentity; import com.yahoo.vespa.athenz.api.AthenzRole; import com.yahoo.vespa.athenz.api.AthenzService; import com.yahoo.vespa.athenz.api.NToken; import com.yahoo.vespa.athenz.api.ZToken; -import com.yahoo.vespa.athenz.client.zts.bindings.ErrorResponseEntity; +import com.yahoo.vespa.athenz.client.common.ClientBase; import com.yahoo.vespa.athenz.client.zts.bindings.IdentityRefreshRequestEntity; import com.yahoo.vespa.athenz.client.zts.bindings.IdentityResponseEntity; import com.yahoo.vespa.athenz.client.zts.bindings.InstanceIdentityCredentials; @@ -22,25 +19,13 @@ import com.yahoo.vespa.athenz.client.zts.bindings.RoleTokenResponseEntity; import com.yahoo.vespa.athenz.client.zts.bindings.TenantDomainsResponseEntity; import com.yahoo.vespa.athenz.client.zts.utils.IdentityCsrGenerator; import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider; -import com.yahoo.vespa.athenz.identity.ServiceIdentitySslSocketFactory; import com.yahoo.security.Pkcs10Csr; import org.apache.http.HttpResponse; -import org.apache.http.client.ResponseHandler; -import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.methods.RequestBuilder; -import org.apache.http.conn.ssl.SSLConnectionSocketFactory; -import org.apache.http.entity.ContentType; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; -import org.apache.http.impl.client.HttpClientBuilder; -import org.eclipse.jetty.http.HttpStatus; - -import javax.net.ssl.HostnameVerifier; + import javax.net.ssl.SSLContext; import java.io.IOException; -import java.io.UncheckedIOException; import java.net.URI; import java.security.KeyPair; import java.security.cert.X509Certificate; @@ -56,12 +41,9 @@ import static java.util.stream.Collectors.toList; * @author bjorncs * @author mortent */ -public class DefaultZtsClient implements ZtsClient { - - private static final ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule()); +public class DefaultZtsClient extends ClientBase implements ZtsClient { private final URI ztsUrl; - private final CloseableHttpClient client; private final AthenzIdentity identity; public DefaultZtsClient(URI ztsUrl, AthenzIdentity identity, SSLContext sslContext) { @@ -73,9 +55,9 @@ public class DefaultZtsClient implements ZtsClient { } private DefaultZtsClient(URI ztsUrl, AthenzIdentity identity, Supplier<SSLContext> sslContextSupplier) { + super("vespa-zts-client", sslContextSupplier, ZtsClientException::new); this.ztsUrl = addTrailingSlash(ztsUrl); this.identity = identity; - this.client = createHttpClient(sslContextSupplier); } @Override @@ -91,7 +73,7 @@ public class DefaultZtsClient implements ZtsClient { .setUri(ztsUrl.resolve("instance/")) .setEntity(toJsonStringEntity(payload)) .build(); - return execute(request, DefaultZtsClient::getInstanceIdentity); + return execute(request, this::getInstanceIdentity); } @Override @@ -111,7 +93,7 @@ public class DefaultZtsClient implements ZtsClient { .setUri(uri) .setEntity(toJsonStringEntity(payload)) .build(); - return execute(request, DefaultZtsClient::getInstanceIdentity); + return execute(request, this::getInstanceIdentity); } @Override @@ -189,30 +171,13 @@ public class DefaultZtsClient implements ZtsClient { }); } - private <T> T execute(HttpUriRequest request, ResponseHandler<T> responseHandler) { - try { - return client.execute(request, responseHandler); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - private static InstanceIdentity getInstanceIdentity(HttpResponse response) throws IOException { + private InstanceIdentity getInstanceIdentity(HttpResponse response) throws IOException { InstanceIdentityCredentials entity = readEntity(response, InstanceIdentityCredentials.class); return entity.getServiceToken() != null ? new InstanceIdentity(entity.getX509Certificate(), new NToken(entity.getServiceToken())) : new InstanceIdentity(entity.getX509Certificate()); } - private static <T> T readEntity(HttpResponse response, Class<T> entityType) throws IOException { - if (HttpStatus.isSuccess(response.getStatusLine().getStatusCode())) { - return objectMapper.readValue(response.getEntity().getContent(), entityType); - } else { - ErrorResponseEntity errorEntity = objectMapper.readValue(response.getEntity().getContent(), ErrorResponseEntity.class); - throw new ZtsClientException(errorEntity.code, errorEntity.description); - } - } - private static URI addTrailingSlash(URI ztsUrl) { if (ztsUrl.getPath().endsWith("/")) return ztsUrl; @@ -220,34 +185,4 @@ public class DefaultZtsClient implements ZtsClient { return URI.create(ztsUrl.toString() + '/'); } - private static StringEntity toJsonStringEntity(Object entity) { - try { - return new StringEntity(objectMapper.writeValueAsString(entity), ContentType.APPLICATION_JSON); - } catch (JsonProcessingException e) { - throw new UncheckedIOException(e); - } - } - - private static CloseableHttpClient createHttpClient(Supplier<SSLContext> sslContextSupplier) { - return HttpClientBuilder.create() - .setRetryHandler(new DefaultHttpRequestRetryHandler(3, /*requestSentRetryEnabled*/true)) - .setUserAgent("vespa-zts-client") - .setSSLSocketFactory(new SSLConnectionSocketFactory(new ServiceIdentitySslSocketFactory(sslContextSupplier), (HostnameVerifier)null)) - .setDefaultRequestConfig(RequestConfig.custom() - .setConnectTimeout((int)Duration.ofSeconds(10).toMillis()) - .setConnectionRequestTimeout((int)Duration.ofSeconds(10).toMillis()) - .setSocketTimeout((int)Duration.ofSeconds(20).toMillis()) - .build()) - .build(); - } - - @Override - public void close() { - try { - this.client.close(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - } diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/IdentityRefreshRequestEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/IdentityRefreshRequestEntity.java index 808c1162ef1..0704fef2ae3 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/IdentityRefreshRequestEntity.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/IdentityRefreshRequestEntity.java @@ -3,7 +3,7 @@ package com.yahoo.vespa.athenz.client.zts.bindings; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.yahoo.vespa.athenz.client.zts.bindings.serializers.Pkcs10CsrSerializer; +import com.yahoo.vespa.athenz.client.common.serializers.Pkcs10CsrSerializer; import com.yahoo.security.Pkcs10Csr; /** diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/IdentityResponseEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/IdentityResponseEntity.java index 7bd04362599..f36858ef7b8 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/IdentityResponseEntity.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/IdentityResponseEntity.java @@ -5,8 +5,8 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.yahoo.vespa.athenz.client.zts.bindings.serializers.X509CertificateDeserializer; -import com.yahoo.vespa.athenz.client.zts.bindings.serializers.X509CertificateListDeserializer; +import com.yahoo.vespa.athenz.client.common.serializers.X509CertificateDeserializer; +import com.yahoo.vespa.athenz.client.common.serializers.X509CertificateListDeserializer; import java.security.cert.X509Certificate; import java.util.List; diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceIdentityCredentials.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceIdentityCredentials.java index 0ab697a1c4c..b9baba85ea1 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceIdentityCredentials.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceIdentityCredentials.java @@ -5,7 +5,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.yahoo.vespa.athenz.client.zts.bindings.serializers.X509CertificateDeserializer; +import com.yahoo.vespa.athenz.client.common.serializers.X509CertificateDeserializer; import java.security.cert.X509Certificate; diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceRefreshInformation.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceRefreshInformation.java index 0e7e94e96ac..fee91dbc15b 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceRefreshInformation.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/InstanceRefreshInformation.java @@ -5,7 +5,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.yahoo.vespa.athenz.client.zts.bindings.serializers.Pkcs10CsrSerializer; +import com.yahoo.vespa.athenz.client.common.serializers.Pkcs10CsrSerializer; import com.yahoo.security.Pkcs10Csr; /** diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/RoleCertificateRequestEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/RoleCertificateRequestEntity.java index 1b974bcc6fc..89bfce91154 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/RoleCertificateRequestEntity.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/RoleCertificateRequestEntity.java @@ -8,7 +8,7 @@ import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.yahoo.vespa.athenz.client.zts.bindings.serializers.Pkcs10CsrSerializer; +import com.yahoo.vespa.athenz.client.common.serializers.Pkcs10CsrSerializer; import com.yahoo.security.Pkcs10Csr; import java.io.IOException; diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/RoleCertificateResponseEntity.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/RoleCertificateResponseEntity.java index e80f5626843..857bfad9143 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/RoleCertificateResponseEntity.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/bindings/RoleCertificateResponseEntity.java @@ -5,7 +5,7 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.yahoo.vespa.athenz.client.zts.bindings.serializers.X509CertificateDeserializer; +import com.yahoo.vespa.athenz.client.common.serializers.X509CertificateDeserializer; import java.security.cert.X509Certificate; import java.time.Instant; |