diff options
15 files changed, 190 insertions, 45 deletions
diff --git a/client/go/internal/vespa/target_cloud.go b/client/go/internal/vespa/target_cloud.go index 31108dae51f..708810061d2 100644 --- a/client/go/internal/vespa/target_cloud.go +++ b/client/go/internal/vespa/target_cloud.go @@ -42,9 +42,10 @@ type cloudTarget struct { } type deploymentEndpoint struct { - Cluster string `json:"cluster"` - URL string `json:"url"` - Scope string `json:"scope"` + Cluster string `json:"cluster"` + URL string `json:"url"` + Scope string `json:"scope"` + AuthMethod string `json:"authMethod"` } type deploymentResponse struct { @@ -370,6 +371,9 @@ func (t *cloudTarget) discoverEndpoints(timeout time.Duration) (map[string]strin if endpoint.Scope != "zone" { continue } + if endpoint.AuthMethod == "token" { + continue + } urlsByCluster[endpoint.Cluster] = endpoint.URL } return true, nil diff --git a/container-disc/abi-spec.json b/container-disc/abi-spec.json index 92f21af0cde..49d740d369d 100644 --- a/container-disc/abi-spec.json +++ b/container-disc/abi-spec.json @@ -16,6 +16,7 @@ "public abstract java.lang.String getRoleToken(java.lang.String, java.lang.String)", "public abstract java.lang.String getAccessToken(java.lang.String)", "public abstract java.lang.String getAccessToken(java.lang.String, java.util.List)", + "public abstract java.lang.String getAccessToken(java.lang.String, java.util.List, java.util.List)", "public abstract java.util.List getIdentityCertificate()", "public abstract java.security.cert.X509Certificate getRoleCertificate(java.lang.String, java.lang.String)", "public abstract java.security.PrivateKey getPrivateKey()", diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/AthenzIdentityProviderProvider.java b/container-disc/src/main/java/com/yahoo/container/jdisc/AthenzIdentityProviderProvider.java index e644ab1664e..f91d25534cf 100644 --- a/container-disc/src/main/java/com/yahoo/container/jdisc/AthenzIdentityProviderProvider.java +++ b/container-disc/src/main/java/com/yahoo/container/jdisc/AthenzIdentityProviderProvider.java @@ -71,6 +71,11 @@ public class AthenzIdentityProviderProvider implements Provider<AthenzIdentityPr } @Override + public String getAccessToken(String domain, List<String> roles, List<String> proxyPrincipal) { + throw new UnsupportedOperationException(message); + } + + @Override public List<X509Certificate> getIdentityCertificate() { throw new UnsupportedOperationException(message); } diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProvider.java b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProvider.java index ef328fca760..fc55512f7f7 100644 --- a/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProvider.java +++ b/container-disc/src/main/java/com/yahoo/container/jdisc/athenz/AthenzIdentityProvider.java @@ -8,21 +8,117 @@ import java.security.cert.X509Certificate; import java.util.List; /** + * Provides convenience methods to interact with Athenz authenticated services + * * @author mortent + * @author bjorncs */ public interface AthenzIdentityProvider { + /** + * Get the Athenz domain associated with this identity provider. + * + * @return The Athenz domain. + */ String domain(); + + /** + * Get the Athenz service name associated with this identity provider. + * + * @return The Athenz service name. + */ String service(); + + /** + * Get the SSLContext used for authenticating with the configured Athenz service + * + * @return An SSLContext for identity authentication. + */ SSLContext getIdentitySslContext(); + + /** + * Get the SSLContext for authenticating with an Athenz role + * + * @param domain Athenz domain name for the role + * @param role Athenz role name + * @return A SSLContext for role authentication within the specified domain and role. + */ SSLContext getRoleSslContext(String domain, String role); + + /** + * Get a role token for the specified Athenz domain. + * + * @param domain The Athenz domain for the role token + * @return A role token for the specified domain. + */ String getRoleToken(String domain); + + /** + * Get a role token for a specific Athenz role. + * + * @param domain The Athenz domain name for the role + * @param role The Athenz role name + * @return A role token for the specified domain and role. + */ String getRoleToken(String domain, String role); + + /** + * Get an access token for the specified Athenz domain. + * + * @param domain Athenz domain name for the token + * @return An access token for the specified domain. + */ String getAccessToken(String domain); + + /** + * Get an access token for a list of roles in an Athenz domain. + * + * @param domain Athenz domain name for the roles + * @param roles The list of Athenz roles names + * @return An access token for the specified roles. + */ String getAccessToken(String domain, List<String> roles); + + /** + * Get an access token for the specified Athenz domain. + * + * @param domain Athenz domain name + * @param roles List of Athenz role names. Empty list or null will fetch a token for all roles in the domain. + * @param proxyPrincipal List of principals to allow proxying the token. Each principal must be provided as: <em><domain>:service.<service></em> + * Empty list or <em>null</em> will return a token without proxy principals. + * @return An access token for the specified domain. + */ + String getAccessToken(String domain, List<String> roles, List<String> proxyPrincipal); + + /** + * Get the X.509 identity certificate associated with this identity provider. + * + * @return The X.509 identity certificate. + */ List<X509Certificate> getIdentityCertificate(); + + /** + * Get the X.509 role certificate for a specific Athenz role. + * + * @param domain Athenz domain name for the role + * @param role Athenz role name + * @return An X.509 role certificate for the specified domain and role. + */ X509Certificate getRoleCertificate(String domain, String role); + + /** + * Get the private key associated with this identity provider. + * + * @return The private key used for authentication. + */ PrivateKey getPrivateKey(); + + /** + * Get the path to the trust store used for SSL verification. + * + * @return The path to the trust store. + */ Path trustStorePath(); + void deconstruct(); } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Nodes.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Nodes.java index 9a4e47ed154..192d90e1da1 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Nodes.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/node/Nodes.java @@ -783,9 +783,9 @@ public class Nodes { if (host.allocation().map(alloc -> alloc.membership().retired()).orElse(false)) return false; if (dynamicProvisioning) - return EnumSet.of(Node.State.active, Node.State.ready, Node.State.provisioned).contains(host.state()); + return EnumSet.of(State.provisioned, State.ready, State.reserved, State.active).contains(host.state()); else - return host.state() == Node.State.active; + return host.state() == State.active; } public boolean suspended(Node node) { diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizer.java index b92d6fb6d18..7e7c78f82f4 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/provisioning/NodePrioritizer.java @@ -211,7 +211,7 @@ public class NodePrioritizer { /** * We may regret that a non-active node is allocated to a host and not offer it to the application - * now, e.g if we want to retire the host. + * now, e.g., if we want to retire the host. * * @return true if we still want to allocate the given node to its parent */ diff --git a/searchcore/src/tests/proton/matching/match_phase_limiter/match_phase_limiter_test.cpp b/searchcore/src/tests/proton/matching/match_phase_limiter/match_phase_limiter_test.cpp index 9f4edac9ba4..21c572995d3 100644 --- a/searchcore/src/tests/proton/matching/match_phase_limiter/match_phase_limiter_test.cpp +++ b/searchcore/src/tests/proton/matching/match_phase_limiter/match_phase_limiter_test.cpp @@ -181,6 +181,7 @@ TEST("require that max group size is calculated correctly") { TEST("require that the attribute limiter works correctly") { FakeRequestContext requestContext; MockRangeLocator rangeLocator; + constexpr double HIT_RATE = 0.1; for (int i = 0; i <= 7; ++i) { bool descending = (i & 1) != 0; bool strict = (i & 2) != 0; @@ -190,10 +191,10 @@ TEST("require that the attribute limiter works correctly") { "category", 10.0, AttributeLimiter::LOOSE); EXPECT_EQUAL(0u, searchable.create_cnt); EXPECT_FALSE(limiter.was_used()); - SearchIterator::UP s1 = limiter.create_search(42, diverse ? 3 : 42, strict); + SearchIterator::UP s1 = limiter.create_search(42, diverse ? 3 : 42, HIT_RATE, strict); EXPECT_TRUE(limiter.was_used()); EXPECT_EQUAL(1u, searchable.create_cnt); - SearchIterator::UP s2 = limiter.create_search(42, diverse ? 3 : 42, strict); + SearchIterator::UP s2 = limiter.create_search(42, diverse ? 3 : 42, HIT_RATE, strict); EXPECT_EQUAL(1u, searchable.create_cnt); auto *ms = dynamic_cast<MockSearch*>(s1.get()); ASSERT_TRUE(ms != nullptr); diff --git a/searchcore/src/vespa/searchcore/proton/matching/attribute_limiter.cpp b/searchcore/src/vespa/searchcore/proton/matching/attribute_limiter.cpp index 9911b04e087..4dec253946f 100644 --- a/searchcore/src/vespa/searchcore/proton/matching/attribute_limiter.cpp +++ b/searchcore/src/vespa/searchcore/proton/matching/attribute_limiter.cpp @@ -76,8 +76,8 @@ toString(AttributeLimiter::DiversityCutoffStrategy strategy) return (strategy == AttributeLimiter::DiversityCutoffStrategy::STRICT) ? STRICT_STR : LOOSE_STR; } -SearchIterator::UP -AttributeLimiter::create_search(size_t want_hits, size_t max_group_size, bool strictSearch) +std::unique_ptr<SearchIterator> +AttributeLimiter::create_search(size_t want_hits, size_t max_group_size, double hit_rate, bool strictSearch) { std::lock_guard<std::mutex> guard(_lock); const uint32_t my_field_id = 0; @@ -99,7 +99,7 @@ AttributeLimiter::create_search(size_t want_hits, size_t max_group_size, bool st FieldSpecList field; // single field API is protected field.add(FieldSpec(_attribute_name, my_field_id, my_handle)); _blueprint = _searchable_attributes.createBlueprint(_requestContext, field, node); - _blueprint->fetchPostings(ExecuteInfo::create(strictSearch, &_requestContext.getDoom())); + _blueprint->fetchPostings(ExecuteInfo::create(strictSearch, strictSearch ? 1.0F : hit_rate, &_requestContext.getDoom())); _estimatedHits.store(_blueprint->getState().estimate().estHits, std::memory_order_relaxed); _blueprint->freeze(); } diff --git a/searchcore/src/vespa/searchcore/proton/matching/attribute_limiter.h b/searchcore/src/vespa/searchcore/proton/matching/attribute_limiter.h index 676dbf26108..df0acccbd7a 100644 --- a/searchcore/src/vespa/searchcore/proton/matching/attribute_limiter.h +++ b/searchcore/src/vespa/searchcore/proton/matching/attribute_limiter.h @@ -31,6 +31,7 @@ class RangeQueryLocator; class AttributeLimiter { public: + using SearchIterator = search::queryeval::SearchIterator; enum DiversityCutoffStrategy { LOOSE, STRICT}; AttributeLimiter(const RangeQueryLocator & _rangeQueryLocator, search::queryeval::Searchable &searchable_attributes, @@ -40,7 +41,7 @@ public: double diversityCutoffFactor, DiversityCutoffStrategy diversityCutoffStrategy); ~AttributeLimiter(); - std::unique_ptr<search::queryeval::SearchIterator> create_search(size_t want_hits, size_t max_group_size, bool strictSearch); + std::unique_ptr<SearchIterator> create_search(size_t want_hits, size_t max_group_size, double hit_rate, bool strictSearch); bool was_used() const; ssize_t getEstimatedHits() const; static DiversityCutoffStrategy toDiversityCutoffStrategy(vespalib::stringref strategy); diff --git a/searchcore/src/vespa/searchcore/proton/matching/match_phase_limit_calculator.h b/searchcore/src/vespa/searchcore/proton/matching/match_phase_limit_calculator.h index 6cbeeebbfa0..c422529ea30 100644 --- a/searchcore/src/vespa/searchcore/proton/matching/match_phase_limit_calculator.h +++ b/searchcore/src/vespa/searchcore/proton/matching/match_phase_limit_calculator.h @@ -25,21 +25,21 @@ public: * @param min_groups the minimum number of diversity groups you want * @param sample fraction of max_hits to be used as sample size before performing match phase limiting */ - MatchPhaseLimitCalculator(size_t max_hits, size_t min_groups, double sample) : - _max_hits(max_hits), - _min_groups(std::max(size_t(1), min_groups)), - _sample_hits(max_hits * sample) + MatchPhaseLimitCalculator(size_t max_hits, size_t min_groups, double sample) noexcept + : _max_hits(max_hits), + _min_groups(std::max(size_t(1), min_groups)), + _sample_hits(max_hits * sample) {} - size_t sample_hits_per_thread(size_t num_threads) const { + size_t sample_hits_per_thread(size_t num_threads) const noexcept { return std::max(size_t(1), std::max(128 / num_threads, _sample_hits / num_threads)); } - size_t wanted_num_docs(double hit_rate) const { + size_t wanted_num_docs(double hit_rate) const noexcept { return std::min((double)0x7fffFFFF, std::max(128.0, _max_hits / hit_rate)); } - size_t estimated_hits(double hit_rate, size_t num_docs) const { + size_t estimated_hits(double hit_rate, size_t num_docs) const noexcept { return (size_t) (hit_rate * num_docs); } - size_t max_group_size(size_t wanted_num_docs_in) const { + size_t max_group_size(size_t wanted_num_docs_in) const noexcept { return (wanted_num_docs_in / _min_groups); } }; diff --git a/searchcore/src/vespa/searchcore/proton/matching/match_phase_limiter.cpp b/searchcore/src/vespa/searchcore/proton/matching/match_phase_limiter.cpp index 3d319b84828..908843ca3ca 100644 --- a/searchcore/src/vespa/searchcore/proton/matching/match_phase_limiter.cpp +++ b/searchcore/src/vespa/searchcore/proton/matching/match_phase_limiter.cpp @@ -87,11 +87,11 @@ namespace { template <bool PRE_FILTER> SearchIterator::UP -do_limit(AttributeLimiter &limiter_factory, SearchIterator::UP search, +do_limit(AttributeLimiter &limiter_factory, SearchIterator::UP search, double match_freq, size_t wanted_num_docs, size_t max_group_size, uint32_t current_id, uint32_t end_id) { - SearchIterator::UP limiter = limiter_factory.create_search(wanted_num_docs, max_group_size, PRE_FILTER); + SearchIterator::UP limiter = limiter_factory.create_search(wanted_num_docs, max_group_size, match_freq, PRE_FILTER); limiter = search->andWith(std::move(limiter), wanted_num_docs); if (limiter) { search = std::make_unique<LimitedSearchT<PRE_FILTER>>(std::move(limiter), std::move(search)); @@ -139,8 +139,8 @@ MatchPhaseLimiter::maybe_limit(SearchIterator::UP search, double match_freq, siz use_pre_filter ? "pre" : "post", match_freq, num_docs, max_filter_docs, wanted_num_docs, max_group_size, current_id, end_id, total_query_hits); return (use_pre_filter) - ? do_limit<true>(_limiter_factory, std::move(search), wanted_num_docs, max_group_size, current_id, end_id) - : do_limit<false>(_limiter_factory, std::move(search), wanted_num_docs, max_group_size, current_id, end_id); + ? do_limit<true>(_limiter_factory, std::move(search), match_freq, wanted_num_docs, max_group_size, current_id, end_id) + : do_limit<false>(_limiter_factory, std::move(search), match_freq, wanted_num_docs, max_group_size, current_id, end_id); } void 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 1a921c3b3ad..101d40be491 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 @@ -141,16 +141,21 @@ public class DefaultZtsClient extends ClientBase implements ZtsClient { } @Override - public AthenzAccessToken getAccessToken(AthenzDomain domain, List<AthenzIdentity> proxyPrincipals) { + public AthenzAccessToken getAccessToken(AthenzDomain domain, List<AthenzIdentity> proxyPrincipals) { return this.getAccessTokenImpl(List.of(new AthenzResourceName(domain, "domain")), proxyPrincipals); } @Override public AthenzAccessToken getAccessToken(List<AthenzRole> athenzRole) { + return getAccessToken(athenzRole, List.of()); + } + + @Override + public AthenzAccessToken getAccessToken(List<AthenzRole> athenzRole, List<AthenzIdentity> proxyPrincipals) { List<AthenzResourceName> athenzResourceNames = athenzRole.stream() .map(AthenzRole::toResourceName) .toList(); - return this.getAccessTokenImpl(athenzResourceNames, List.of()); + return this.getAccessTokenImpl(athenzResourceNames, proxyPrincipals); } private AthenzAccessToken getAccessTokenImpl(List<AthenzResourceName> resources, List<AthenzIdentity> proxyPrincipals) { diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClient.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClient.java index 67a53715cec..0528042da16 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClient.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zts/ZtsClient.java @@ -129,6 +129,15 @@ public interface ZtsClient extends AutoCloseable { AthenzAccessToken getAccessToken(List<AthenzRole> athenzRole); /** + * Fetch an access token for the target roles + * + * @param athenzRole List of athenz roles to get access token for + * @param proxyPrincipals List of principals to allow proxying token + * @return An Athenz access token + */ + AthenzAccessToken getAccessToken(List<AthenzRole> athenzRole, List<AthenzIdentity> proxyPrincipals); + + /** * Fetch role certificate for the target domain and role * * @param role Target role diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java index 623a8c856bc..a3916cbe080 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java @@ -20,6 +20,7 @@ import com.yahoo.security.SslContextBuilder; import com.yahoo.security.X509CertificateWithKey; import com.yahoo.vespa.athenz.api.AthenzAccessToken; 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.ZToken; @@ -28,6 +29,7 @@ import com.yahoo.vespa.athenz.client.zts.ZtsClient; import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider; import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId; import com.yahoo.vespa.athenz.tls.AthenzX509CertificateUtils; +import com.yahoo.vespa.athenz.utils.AthenzIdentities; import com.yahoo.vespa.athenz.utils.SiaUtils; import javax.net.ssl.SSLContext; @@ -43,6 +45,7 @@ import java.time.Instant; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -89,8 +92,7 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen private final Map<AthenzRole, MutableX509KeyManager> roleKeyManagerCache; private final LoadingCache<AthenzRole, ZToken> roleSpecificRoleTokenCache; private final LoadingCache<AthenzDomain, ZToken> domainSpecificRoleTokenCache; - private final LoadingCache<AthenzDomain, AthenzAccessToken> domainSpecificAccessTokenCache; - private final LoadingCache<List<AthenzRole>, AthenzAccessToken> roleSpecificAccessTokenCache; + private final LoadingCache<AccessTokenCacheKey, AthenzAccessToken> accessTokenCache; private final CsrGenerator csrGenerator; @Inject @@ -115,8 +117,7 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen this.roleKeyManagerCache = new HashMap<>(); this.roleSpecificRoleTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createRoleToken); this.domainSpecificRoleTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createRoleToken); - this.domainSpecificAccessTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createAccessToken); - this.roleSpecificAccessTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createAccessToken); + this.accessTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createAccessToken); this.csrGenerator = new CsrGenerator(config.athenzDnsSuffix(), config.configserverIdentityName()); this.autoReloadingX509KeyManager = autoReloadingX509KeyManager; this.identitySslContext = createIdentitySslContext(autoReloadingX509KeyManager, trustStore); @@ -221,7 +222,7 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen @Override public String getAccessToken(String domain) { try { - return domainSpecificAccessTokenCache.get(new AthenzDomain(domain)).value(); + return accessTokenCache.get(AccessTokenCacheKey.from(domain, List.of(), List.of())).value(); } catch (Exception e) { throw new AthenzIdentityProviderException("Could not retrieve access token: " + e.getMessage(), e); } @@ -230,10 +231,16 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen @Override public String getAccessToken(String domain, List<String> roles) { try { - List<AthenzRole> roleList = roles.stream() - .map(roleName -> new AthenzRole(domain, roleName)) - .toList(); - return roleSpecificAccessTokenCache.get(roleList).value(); + return accessTokenCache.get(AccessTokenCacheKey.from(domain, roles, List.of())).value(); + } catch (Exception e) { + throw new AthenzIdentityProviderException("Could not retrieve access token: " + e.getMessage(), e); + } + } + + @Override + public String getAccessToken(String domain, List<String> roles, List<String> proxyPrincipals) { + try { + return accessTokenCache.get(AccessTokenCacheKey.from(domain, roles, proxyPrincipals)).value(); } catch (Exception e) { throw new AthenzIdentityProviderException("Could not retrieve access token: " + e.getMessage(), e); } @@ -305,15 +312,15 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen } } - private AthenzAccessToken createAccessToken(AthenzDomain domain) { + private AthenzAccessToken createAccessToken(AccessTokenCacheKey cacheKey) { + List<AthenzRole> roles = Optional.ofNullable(cacheKey.roles()).orElse(List.of()); + List<AthenzIdentity> proxyPrincipals = Optional.ofNullable(cacheKey.proxyPrincipals()).orElse(List.of()); try (ZtsClient client = createZtsClient()) { - return client.getAccessToken(domain); - } - } - - private AthenzAccessToken createAccessToken(List<AthenzRole> roles) { - try (ZtsClient client = createZtsClient()) { - return client.getAccessToken(roles); + if (roles.isEmpty()) { + return client.getAccessToken(cacheKey.domain(), proxyPrincipals); + } else { + return client.getAccessToken(roles, proxyPrincipals); + } } } @@ -349,5 +356,15 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen log.log(Level.WARNING, "Failed to update metrics: " + t.getMessage(), t); } } + private record AccessTokenCacheKey(AthenzDomain domain, List<AthenzRole> roles, List<AthenzIdentity> proxyPrincipals) { + static AccessTokenCacheKey from(String domain, List<String> roles, List<String> proxyPrincipals) { + List<AthenzRole> roleList = Optional.ofNullable(roles).orElse(List.of()).stream() + .map(roleName -> new AthenzRole(domain, roleName)) + .toList(); + List<AthenzIdentity> proxyPrincipalList = Optional.ofNullable(proxyPrincipals).orElse(List.of()).stream() + .map(AthenzIdentities::from) + .toList(); + return new AccessTokenCacheKey(new AthenzDomain(domain), roleList, proxyPrincipalList); + } + } } - diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/LegacyAthenzIdentityProviderImpl.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/LegacyAthenzIdentityProviderImpl.java index 4058af31dde..a881dc2b100 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/LegacyAthenzIdentityProviderImpl.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/LegacyAthenzIdentityProviderImpl.java @@ -261,6 +261,11 @@ public final class LegacyAthenzIdentityProviderImpl extends AbstractComponent im } @Override + public String getAccessToken(String domain, List<String> roles, List<String> proxyPrincipal) { + throw new UnsupportedOperationException("Not implemented in legacy client"); + } + + @Override public PrivateKey getPrivateKey() { return credentials.getKeyPair().getPrivate(); } @@ -383,7 +388,8 @@ public final class LegacyAthenzIdentityProviderImpl extends AbstractComponent im try { Instant expirationTime = getExpirationTime(credentials); Duration remainingLifetime = Duration.between(clock.instant(), expirationTime); - metric.set(CERTIFICATE_EXPIRY_METRIC_NAME, remainingLifetime.getSeconds(), null); + Metric.Context dimensions = metric.createContext(Map.of("implementation", this.getClassName())); + metric.set(CERTIFICATE_EXPIRY_METRIC_NAME, remainingLifetime.getSeconds(), dimensions); } catch (Throwable t) { log.log(Level.WARNING, "Failed to update metrics: " + t.getMessage(), t); } |