From e8ee482cf3be317dedad643a552b921b73d6c916 Mon Sep 17 00:00:00 2001 From: Martin Ashby Date: Tue, 2 Jul 2024 08:56:15 +0100 Subject: [PATCH] Add an additional authentication mechanism for proxy repositories Google auth will use Google Application Default Credentials present in the environment to add a Bearer token to HTTP requests. This allows nexus to be configured on supported environments for Workload Identity and avoids the need for service account keys. Fixes: https://github.com/sonatype/nexus-public/issues/420 --- buildsupport/other/pom.xml | 6 ++++ ...thenticationConfigurationDeserializer.java | 5 +++ ...AuthenticationConfigurationSerializer.java | 8 +++++ .../config/AuthenticationConfiguration.java | 6 ++-- .../config/ConfigurationCustomizer.java | 4 +++ .../GoogleAuthenticationConfiguration.java | 34 +++++++++++++++++++ components/nexus-repository-view/pom.xml | 4 +++ .../internal/HttpClientFacetImpl.java | 17 ++++++++-- .../facets/GenericHttpAuthConfiguration.jsx | 1 + .../admin/repository/RepositoriesStrings.jsx | 1 + .../pages/admin/system/HttpStrings.js | 1 + .../rapture/NX/coreui/app/PluginStrings.js | 1 + .../view/repository/facet/HttpClientFacet.js | 3 +- pom.xml | 1 - 14 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 components/nexus-httpclient/src/main/java/org/sonatype/nexus/httpclient/config/GoogleAuthenticationConfiguration.java diff --git a/buildsupport/other/pom.xml b/buildsupport/other/pom.xml index 4a62ee5410..899b8b7d5e 100644 --- a/buildsupport/other/pom.xml +++ b/buildsupport/other/pom.xml @@ -62,6 +62,12 @@ + + com.google.auth + google-auth-library-oauth2-http + 1.23.0 + + com.google.code.findbugs jsr305 diff --git a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/httpclient/AuthenticationConfigurationDeserializer.java b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/httpclient/AuthenticationConfigurationDeserializer.java index 44f427fde8..1251f9e51e 100644 --- a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/httpclient/AuthenticationConfigurationDeserializer.java +++ b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/httpclient/AuthenticationConfigurationDeserializer.java @@ -16,6 +16,7 @@ import org.sonatype.nexus.httpclient.config.AuthenticationConfiguration; import org.sonatype.nexus.httpclient.config.BearerTokenAuthenticationConfiguration; +import org.sonatype.nexus.httpclient.config.GoogleAuthenticationConfiguration; import org.sonatype.nexus.httpclient.config.NtlmAuthenticationConfiguration; import org.sonatype.nexus.httpclient.config.UsernameAuthenticationConfiguration; import org.sonatype.nexus.security.PasswordHelper; @@ -86,6 +87,10 @@ else if (BearerTokenAuthenticationConfiguration.class.equals(type)) { BearerTokenAuthenticationConfiguration btac = (BearerTokenAuthenticationConfiguration) configuration; btac.setBearerToken(passwordHelper.tryDecrypt(btac.getBearerToken())); } + else if (GoogleAuthenticationConfiguration.class.equals(type)) { + GoogleAuthenticationConfiguration gac = (GoogleAuthenticationConfiguration)configuration; + // nothing to set really. + } return configuration; } } diff --git a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/httpclient/AuthenticationConfigurationSerializer.java b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/httpclient/AuthenticationConfigurationSerializer.java index ce37bf92a3..d8faf0600b 100644 --- a/components/nexus-core/src/main/java/org/sonatype/nexus/internal/httpclient/AuthenticationConfigurationSerializer.java +++ b/components/nexus-core/src/main/java/org/sonatype/nexus/internal/httpclient/AuthenticationConfigurationSerializer.java @@ -16,6 +16,7 @@ import org.sonatype.nexus.httpclient.config.AuthenticationConfiguration; import org.sonatype.nexus.httpclient.config.BearerTokenAuthenticationConfiguration; +import org.sonatype.nexus.httpclient.config.GoogleAuthenticationConfiguration; import org.sonatype.nexus.httpclient.config.NtlmAuthenticationConfiguration; import org.sonatype.nexus.httpclient.config.UsernameAuthenticationConfiguration; import org.sonatype.nexus.security.PasswordHelper; @@ -72,6 +73,9 @@ else if (value instanceof NtlmAuthenticationConfiguration) { else if (value instanceof BearerTokenAuthenticationConfiguration) { jgen.writeStringField(typeSer.getPropertyName(), BearerTokenAuthenticationConfiguration.TYPE); } + else if (value instanceof GoogleAuthenticationConfiguration) { + jgen.writeStringField(typeSer.getPropertyName(), GoogleAuthenticationConfiguration.TYPE); + } else { // be foolproof, if new type added but this class is not updated throw new JsonGenerationException("Unsupported type:" + value.getClass().getName(), jgen); @@ -99,6 +103,10 @@ else if (value instanceof BearerTokenAuthenticationConfiguration) { BearerTokenAuthenticationConfiguration btac = (BearerTokenAuthenticationConfiguration) value; jgen.writeStringField(BearerTokenAuthenticationConfiguration.TYPE, passwordHelper.encrypt(btac.getBearerToken())); } + else if (value instanceof GoogleAuthenticationConfiguration) { + GoogleAuthenticationConfiguration gac = (GoogleAuthenticationConfiguration)value; + // nothing to write really. + } else { // be foolproof, if new type added but this class is not updated throw new JsonGenerationException("Unsupported type:" + value.getClass().getName()); diff --git a/components/nexus-httpclient/src/main/java/org/sonatype/nexus/httpclient/config/AuthenticationConfiguration.java b/components/nexus-httpclient/src/main/java/org/sonatype/nexus/httpclient/config/AuthenticationConfiguration.java index bf82f6fdca..dd3d6d84dc 100644 --- a/components/nexus-httpclient/src/main/java/org/sonatype/nexus/httpclient/config/AuthenticationConfiguration.java +++ b/components/nexus-httpclient/src/main/java/org/sonatype/nexus/httpclient/config/AuthenticationConfiguration.java @@ -32,7 +32,8 @@ @JsonSubTypes({ @Type(value = BearerTokenAuthenticationConfiguration.class, name = BearerTokenAuthenticationConfiguration.TYPE), @Type(value = NtlmAuthenticationConfiguration.class, name = NtlmAuthenticationConfiguration.TYPE), - @Type(value = UsernameAuthenticationConfiguration.class, name = UsernameAuthenticationConfiguration.TYPE) + @Type(value = UsernameAuthenticationConfiguration.class, name = UsernameAuthenticationConfiguration.TYPE), + @Type(value = GoogleAuthenticationConfiguration.class, name = GoogleAuthenticationConfiguration.TYPE), }) public abstract class AuthenticationConfiguration implements Cloneable @@ -44,7 +45,8 @@ public abstract class AuthenticationConfiguration public static final Map> TYPES = ImmutableMap.of( UsernameAuthenticationConfiguration.TYPE, UsernameAuthenticationConfiguration.class, NtlmAuthenticationConfiguration.TYPE, NtlmAuthenticationConfiguration.class, - BearerTokenAuthenticationConfiguration.TYPE, BearerTokenAuthenticationConfiguration.class + BearerTokenAuthenticationConfiguration.TYPE, BearerTokenAuthenticationConfiguration.class, + GoogleAuthenticationConfiguration.TYPE, GoogleAuthenticationConfiguration.class ); private final String type; diff --git a/components/nexus-httpclient/src/main/java/org/sonatype/nexus/httpclient/config/ConfigurationCustomizer.java b/components/nexus-httpclient/src/main/java/org/sonatype/nexus/httpclient/config/ConfigurationCustomizer.java index 7c2b9a41f3..36448d7944 100644 --- a/components/nexus-httpclient/src/main/java/org/sonatype/nexus/httpclient/config/ConfigurationCustomizer.java +++ b/components/nexus-httpclient/src/main/java/org/sonatype/nexus/httpclient/config/ConfigurationCustomizer.java @@ -283,6 +283,10 @@ else if (authentication instanceof BearerTokenAuthenticationConfiguration) { credentials = null; authSchemes = emptyList(); } + else if (authentication instanceof GoogleAuthenticationConfiguration) { + credentials = null; + authSchemes = emptyList(); + } else { throw new IllegalArgumentException("Unsupported authentication configuration: " + authentication); } diff --git a/components/nexus-httpclient/src/main/java/org/sonatype/nexus/httpclient/config/GoogleAuthenticationConfiguration.java b/components/nexus-httpclient/src/main/java/org/sonatype/nexus/httpclient/config/GoogleAuthenticationConfiguration.java new file mode 100644 index 0000000000..6d4dbd09a8 --- /dev/null +++ b/components/nexus-httpclient/src/main/java/org/sonatype/nexus/httpclient/config/GoogleAuthenticationConfiguration.java @@ -0,0 +1,34 @@ +/* + * Sonatype Nexus (TM) Open Source Version + * Copyright (c) 2008-present Sonatype, Inc. + * All rights reserved. Includes the third-party code listed at http://links.sonatype.com/products/nexus/oss/attributions. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License Version 1.0, + * which accompanies this distribution and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Sonatype Nexus (TM) Professional Version is available from Sonatype, Inc. "Sonatype" and "Sonatype Nexus" are trademarks + * of Sonatype, Inc. Apache Maven is a trademark of the Apache Software Foundation. M2eclipse is a trademark of the + * Eclipse Foundation. All other trademarks are the property of their respective owners. + */ +package org.sonatype.nexus.httpclient.config; + +/** + * Google authentication configuration. + * It's all automatic + * + * @since 3.0 + */ +public class GoogleAuthenticationConfiguration + extends AuthenticationConfiguration +{ + public static final String TYPE = "google"; + + public GoogleAuthenticationConfiguration() { + super(TYPE); + } + @Override + public String toString() { + return getClass().getSimpleName() + "{" + + '}'; + } +} diff --git a/components/nexus-repository-view/pom.xml b/components/nexus-repository-view/pom.xml index cb13be13e1..f031754426 100644 --- a/components/nexus-repository-view/pom.xml +++ b/components/nexus-repository-view/pom.xml @@ -78,6 +78,10 @@ objenesis test + + com.google.auth + google-auth-library-oauth2-http + diff --git a/components/nexus-repository-view/src/main/java/org/sonatype/nexus/repository/httpclient/internal/HttpClientFacetImpl.java b/components/nexus-repository-view/src/main/java/org/sonatype/nexus/repository/httpclient/internal/HttpClientFacetImpl.java index e2498bb897..f4936bb9df 100644 --- a/components/nexus-repository-view/src/main/java/org/sonatype/nexus/repository/httpclient/internal/HttpClientFacetImpl.java +++ b/components/nexus-repository-view/src/main/java/org/sonatype/nexus/repository/httpclient/internal/HttpClientFacetImpl.java @@ -21,6 +21,7 @@ import javax.inject.Named; import javax.validation.Valid; +import com.google.auth.oauth2.GoogleCredentials; import org.sonatype.nexus.common.event.EventHelper; import org.sonatype.nexus.common.stateguard.Guarded; import org.sonatype.nexus.distributed.event.service.api.common.RepositoryRemoteConnectionStatusEvent; @@ -29,6 +30,7 @@ import org.sonatype.nexus.httpclient.config.BearerTokenAuthenticationConfiguration; import org.sonatype.nexus.httpclient.config.ConfigurationCustomizer; import org.sonatype.nexus.httpclient.config.ConnectionConfiguration; +import org.sonatype.nexus.httpclient.config.GoogleAuthenticationConfiguration; import org.sonatype.nexus.httpclient.config.HttpClientConfiguration; import org.sonatype.nexus.httpclient.config.HttpClientConfigurationChangedEvent; import org.sonatype.nexus.httpclient.config.UsernameAuthenticationConfiguration; @@ -195,9 +197,18 @@ public Header createBasicAuthHeader() { @Override public String getBearerToken() { - if (config.authentication != null && - BearerTokenAuthenticationConfiguration.TYPE.equals(config.authentication.getType())) { - return ((BearerTokenAuthenticationConfiguration) config.authentication).getBearerToken(); + if (config.authentication != null) { + if (BearerTokenAuthenticationConfiguration.TYPE.equals(config.authentication.getType())) { + return ((BearerTokenAuthenticationConfiguration) config.authentication).getBearerToken(); + } else if (GoogleAuthenticationConfiguration.TYPE.equals(config.authentication.getType())) { + try { + GoogleCredentials creds = GoogleCredentials.getApplicationDefault(); + creds.refreshIfExpired(); + return creds.getAccessToken().getTokenValue(); + } catch (IOException ioe) { + throw new RuntimeException(ioe); + } + } } return null; } diff --git a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/Repositories/facets/GenericHttpAuthConfiguration.jsx b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/Repositories/facets/GenericHttpAuthConfiguration.jsx index d615a29051..9235a3d501 100644 --- a/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/Repositories/facets/GenericHttpAuthConfiguration.jsx +++ b/plugins/nexus-coreui-plugin/src/frontend/src/components/pages/admin/Repositories/facets/GenericHttpAuthConfiguration.jsx @@ -54,6 +54,7 @@ export default function GenericHttpAuthConfiguration({parentMachine}) { + diff --git a/plugins/nexus-coreui-plugin/src/frontend/src/constants/pages/admin/repository/RepositoriesStrings.jsx b/plugins/nexus-coreui-plugin/src/frontend/src/constants/pages/admin/repository/RepositoriesStrings.jsx index abf1e92d5a..dfc117df5b 100644 --- a/plugins/nexus-coreui-plugin/src/frontend/src/constants/pages/admin/repository/RepositoriesStrings.jsx +++ b/plugins/nexus-coreui-plugin/src/frontend/src/constants/pages/admin/repository/RepositoriesStrings.jsx @@ -150,6 +150,7 @@ export default { NTLM_OPTION: 'Windows NTLM', NTLM_HOST_LABEL: 'Windows NTLM hostname', NTLM_DOMAIN_LABEL: 'Windows NTLM domain', + GOOGLE_OPTION: 'Google', REQUEST_SETTINGS_CAPTION: 'HTTP Request Settings', USER_AGENT_LABEL: 'User-Agent Customization', USER_AGEN_SUBLABEL: 'Define a custom fragment to append to "User-Agent" header in HTTP requests', diff --git a/plugins/nexus-coreui-plugin/src/frontend/src/constants/pages/admin/system/HttpStrings.js b/plugins/nexus-coreui-plugin/src/frontend/src/constants/pages/admin/system/HttpStrings.js index d0a613d387..47a0f54c71 100644 --- a/plugins/nexus-coreui-plugin/src/frontend/src/constants/pages/admin/system/HttpStrings.js +++ b/plugins/nexus-coreui-plugin/src/frontend/src/constants/pages/admin/system/HttpStrings.js @@ -49,6 +49,7 @@ export default { USERNAME: 'Username', PASSWORD: 'Password', HOST_NAME: 'Windows NTLM Hostname', + DOMAIN: 'Windows NTLM Domain' }, EXCLUDE: { diff --git a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/app/PluginStrings.js b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/app/PluginStrings.js index b7bc523ace..2c01028725 100644 --- a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/app/PluginStrings.js +++ b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/app/PluginStrings.js @@ -540,6 +540,7 @@ Ext.define('NX.coreui.app.PluginStrings', { Repository_Facet_HttpClientFacet_AuthenticationType_FieldLabel: 'Authentication type', Repository_Facet_HttpClientFacet_AuthenticationType_Username: 'Username', Repository_Facet_HttpClientFacet_AuthenticationType_NTLM: 'Windows NTLM', + Repository_Facet_HttpClientFacet_AuthenticationType_Google: 'Google', Repository_Facet_HttpClientFacet_AuthenticationType_Bearer_Token: 'Preemptive Bearer Token', Repository_Facet_HttpClientFacet_Authentication_Title: 'Authentication', Repository_Facet_HttpClientFacet_HTTP_Title: 'HTTP request settings', diff --git a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/repository/facet/HttpClientFacet.js b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/repository/facet/HttpClientFacet.js index ebd7d3853b..add1c055dd 100644 --- a/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/repository/facet/HttpClientFacet.js +++ b/plugins/nexus-coreui-plugin/src/main/resources/static/rapture/NX/coreui/view/repository/facet/HttpClientFacet.js @@ -167,7 +167,8 @@ Ext.define('NX.coreui.view.repository.facet.HttpClientFacet', { getAuthTypeStore: function() { return [ ['username', NX.I18n.get('Repository_Facet_HttpClientFacet_AuthenticationType_Username')], - ['ntlm', NX.I18n.get('Repository_Facet_HttpClientFacet_AuthenticationType_NTLM')] + ['ntlm', NX.I18n.get('Repository_Facet_HttpClientFacet_AuthenticationType_NTLM')], + ['google', NX.I18n.get('Repository_Facet_HttpClientFacet_AuthenticationType_Google')] ]; } diff --git a/pom.xml b/pom.xml index c4526b2a84..5acb0d0eee 100644 --- a/pom.xml +++ b/pom.xml @@ -622,7 +622,6 @@ 3.71.0-SNAPSHOT zip -