From 7361b3258be0b5d7789769f3ed9d3a7dde0b062a Mon Sep 17 00:00:00 2001 From: Isan Rivkin Date: Thu, 11 Apr 2024 11:15:35 +0300 Subject: [PATCH 1/8] WIP docs --- .../reference/security/external-principals.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 docs/reference/security/external-principals.md diff --git a/docs/reference/security/external-principals.md b/docs/reference/security/external-principals.md new file mode 100644 index 00000000000..a6855a89348 --- /dev/null +++ b/docs/reference/security/external-principals.md @@ -0,0 +1,26 @@ +--- +title: Authentication with AWS IAM Roles +description: This section covers how to authenticate to lakeFS using AWS IAM. +grand_parent: Reference +parent: Security +redirect_from: + - /reference/external-principals-aws.html +--- + +# Authenticate to lakeFS with External Principals API + +{: .d-inline-block } +lakeFS Enterprise +{: .label .label-purple } + +{: .note} +> External principals API is available for lakeFS Enterprise. If you're using the open-source version you can check the [pluggable APIs](https://docs.lakefs.io/reference/security/rbac.html#pluggable-authentication-and-authorization). + +## Overview + +## Authenticate to lakeFS with AWS IAM Roles + +## Using with Spark + + + From afc65d5107e1a5945a740079f576d3c5b823bab6 Mon Sep 17 00:00:00 2001 From: Isan Rivkin Date: Thu, 11 Apr 2024 11:38:02 +0300 Subject: [PATCH 2/8] initial --- clients/hadoopfs/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clients/hadoopfs/pom.xml b/clients/hadoopfs/pom.xml index 23a9a6551b2..ff31651e65a 100644 --- a/clients/hadoopfs/pom.xml +++ b/clients/hadoopfs/pom.xml @@ -287,7 +287,7 @@ To export to S3: io.lakefs sdk - 1.17.0 + 1.18.0 org.apache.commons From 30e6b630e6f9fa2d16af927db49206c371ec14f0 Mon Sep 17 00:00:00 2001 From: Isan Rivkin Date: Thu, 11 Apr 2024 18:55:59 +0300 Subject: [PATCH 3/8] works --- .../src/main/java/io/lakefs/Constants.java | 2 + .../src/main/java/io/lakefs/LakeFSClient.java | 22 +++--- .../lakefs/auth/AWSLakeFSTokenProvider.java | 75 ++++++++++--------- .../auth/IdentityRequestRequestWrapper.java | 11 +++ .../auth/AWSLakeFSTokenProviderTest.java | 52 +++++++++++++ 5 files changed, 116 insertions(+), 46 deletions(-) create mode 100644 clients/hadoopfs/src/main/java/io/lakefs/auth/IdentityRequestRequestWrapper.java diff --git a/clients/hadoopfs/src/main/java/io/lakefs/Constants.java b/clients/hadoopfs/src/main/java/io/lakefs/Constants.java index 8d2fb35121b..750d0c22144 100644 --- a/clients/hadoopfs/src/main/java/io/lakefs/Constants.java +++ b/clients/hadoopfs/src/main/java/io/lakefs/Constants.java @@ -13,7 +13,9 @@ public class Constants { public static final String ACCESS_MODE_KEY_SUFFIX = "access.mode"; // io.lakefs.auth.TemporaryAWSCredentialsLakeFSTokenProvider, io.lakefs.auth.InstanceProfileAWSCredentialsLakeFSTokenProvider public static final String LAKEFS_AUTH_PROVIDER_KEY_SUFFIX = "auth.provider"; + // TODO(isan) document all configuration fields before merge. + public static final String LAKEFS_AUTH_TOKEN_TTL_KEY_SUFFIX = "token.duration_seconds"; public static final String TOKEN_AWS_CREDENTIALS_PROVIDER_ACCESS_KEY_SUFFIX = "token.aws.access.key"; public static final String TOKEN_AWS_CREDENTIALS_PROVIDER_SECRET_KEY_SUFFIX = "token.aws.secret.key"; public static final String TOKEN_AWS_CREDENTIALS_PROVIDER_SESSION_TOKEN_KEY_SUFFIX = "token.aws.session.token"; diff --git a/clients/hadoopfs/src/main/java/io/lakefs/LakeFSClient.java b/clients/hadoopfs/src/main/java/io/lakefs/LakeFSClient.java index 591f43eafa7..4925bbac051 100644 --- a/clients/hadoopfs/src/main/java/io/lakefs/LakeFSClient.java +++ b/clients/hadoopfs/src/main/java/io/lakefs/LakeFSClient.java @@ -6,19 +6,20 @@ import io.lakefs.clients.sdk.auth.HttpBasicAuth; import io.lakefs.clients.sdk.auth.HttpBearerAuth; import org.apache.hadoop.conf.Configuration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; -import static io.lakefs.auth.LakeFSTokenProviderFactory.newLakeFSTokenProvider; - /** * Provides access to lakeFS API using client library. * This class uses the configuration to initialize API client and instance per API interface we expose. */ public class LakeFSClient { + private static final Logger LOG = LoggerFactory.getLogger(LakeFSClient.class); private static final String BASIC_AUTH = "basic_auth"; private static final String JWT_TOKEN_AUTH = "jwt_token"; - + LakeFSTokenProvider provider; private final ObjectsApi objectsApi; private final StagingApi stagingApi; private final RepositoriesApi repositoriesApi; @@ -29,7 +30,7 @@ public class LakeFSClient { public LakeFSClient(String scheme, Configuration conf) throws IOException { String authProvider = FSConfiguration.get(conf, scheme, Constants.LAKEFS_AUTH_PROVIDER_KEY_SUFFIX, LakeFSClient.BASIC_AUTH); ApiClient apiClient; - + LOG.info("Initiating lakeFS auth provider: {}", authProvider); if (authProvider == BASIC_AUTH) { String accessKey = FSConfiguration.get(conf, scheme, Constants.ACCESS_KEY_KEY_SUFFIX); if (accessKey == null) { @@ -45,14 +46,11 @@ public LakeFSClient(String scheme, Configuration conf) throws IOException { basicAuth.setUsername(accessKey); basicAuth.setPassword(secretKey); } else { - // TODO(isan) depends on missing functionality PR https://github.com/treeverse/lakeFS/pull/7578 being merged. - // once merged, we can use the following code to get the token - throw new IOException("Unsupported auth provider: " + authProvider + ". Only basic_auth is supported at the moment."); -// LakeFSTokenProvider tokenProvider = newLakeFSTokenProvider(scheme, conf); -// String jwt = tokenProvider.getToken(); -// apiClient = newApiClientNoAuth(scheme, conf); -// HttpBearerAuth tokenAuth = (HttpBearerAuth)apiClient.getAuthentication(JWT_TOKEN_AUTH); -// tokenAuth.setBearerToken(jwt); + this.provider = LakeFSTokenProviderFactory.newLakeFSTokenProvider(Constants.DEFAULT_SCHEME, conf); + String lakeFSToken = provider.getToken(); + apiClient = newApiClientNoAuth(scheme, conf); + HttpBearerAuth tokenAuth = (HttpBearerAuth) apiClient.getAuthentication(JWT_TOKEN_AUTH); + tokenAuth.setBearerToken(lakeFSToken); } this.objectsApi = new ObjectsApi(apiClient); diff --git a/clients/hadoopfs/src/main/java/io/lakefs/auth/AWSLakeFSTokenProvider.java b/clients/hadoopfs/src/main/java/io/lakefs/auth/AWSLakeFSTokenProvider.java index 03e449a0e13..0be67221eed 100644 --- a/clients/hadoopfs/src/main/java/io/lakefs/auth/AWSLakeFSTokenProvider.java +++ b/clients/hadoopfs/src/main/java/io/lakefs/auth/AWSLakeFSTokenProvider.java @@ -1,17 +1,22 @@ package io.lakefs.auth; + import com.amazonaws.auth.AWSCredentialsProvider; import io.lakefs.Constants; import io.lakefs.FSConfiguration; import io.lakefs.clients.sdk.ApiClient; +import io.lakefs.clients.sdk.AuthApi; +import io.lakefs.clients.sdk.model.ExternalLoginInformation; import io.lakefs.clients.sdk.model.AuthenticationToken; import org.apache.commons.codec.binary.Base64; import java.io.IOException; + import java.net.URI; import java.net.URL; import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import org.apache.hadoop.conf.Configuration; @@ -23,6 +28,7 @@ public class AWSLakeFSTokenProvider implements LakeFSTokenProvider { String stsEndpoint; Map stsAdditionalHeaders; int stsExpirationInSeconds; + Optional lakeFSTokenTTLSeconds = Optional.empty(); ApiClient lakeFSApi; AWSLakeFSTokenProvider() { @@ -68,6 +74,12 @@ protected void initialize(AWSCredentialsProvider awsProvider, String scheme, Con } this.lakeFSApi.setBasePath(endpoint); + // optional timeout for lakeFS token + int tokenTTL = FSConfiguration.getInt(conf, scheme, Constants.LAKEFS_AUTH_TOKEN_TTL_KEY_SUFFIX, -1); + if (tokenTTL != -1) { + this.lakeFSTokenTTLSeconds = Optional.of(tokenTTL); + } + // set additional headers (non-canonical) to sign with each request to STS // non-canonical headers are signed by the presigner and sent to STS for verification in the requests by lakeFS to exchange the token Map additionalHeaders = FSConfiguration.getMap(conf, scheme, Constants.TOKEN_AWS_CREDENTIALS_PROVIDER_ADDITIONAL_HEADERS); @@ -94,12 +106,7 @@ private boolean needsNewToken() { } public GeneratePresignGetCallerIdentityResponse newPresignedRequest() throws Exception { - GeneratePresignGetCallerIdentityRequest stsReq = new GeneratePresignGetCallerIdentityRequest( - new URI(this.stsEndpoint), - this.awsProvider.getCredentials(), - this.stsAdditionalHeaders, - this.stsExpirationInSeconds - ); + GeneratePresignGetCallerIdentityRequest stsReq = new GeneratePresignGetCallerIdentityRequest(new URI(this.stsEndpoint), this.awsProvider.getCredentials(), this.stsAdditionalHeaders, this.stsExpirationInSeconds); return this.stsPresigner.presignRequest(stsReq); } @@ -107,40 +114,40 @@ public String newPresignedGetCallerIdentityToken() throws Exception { GeneratePresignGetCallerIdentityResponse signedRequest = this.newPresignedRequest(); // generate token parameters object - LakeFSExternalPrincipalIdentityRequest identityTokenParams = new LakeFSExternalPrincipalIdentityRequest( - signedRequest.getHTTPMethod(), - signedRequest.getHost(), - signedRequest.getRegion(), - signedRequest.getAction(), - signedRequest.getDate(), - signedRequest.getExpires(), - signedRequest.getAccessKeyId(), - signedRequest.getSignature(), - Arrays.asList(signedRequest.getSignedHeadersParam().split(";")), - signedRequest.getVersion(), - signedRequest.getAlgorithm(), - signedRequest.getSecurityToken() - ); + LakeFSExternalPrincipalIdentityRequest identityTokenParams = new LakeFSExternalPrincipalIdentityRequest(signedRequest.getHTTPMethod(), signedRequest.getHost(), signedRequest.getRegion(), signedRequest.getAction(), signedRequest.getDate(), signedRequest.getExpires(), signedRequest.getAccessKeyId(), signedRequest.getSignature(), Arrays.asList(signedRequest.getSignedHeadersParam().split(";")), signedRequest.getVersion(), signedRequest.getAlgorithm(), signedRequest.getSecurityToken()); + + // base64 encode + return Base64.encodeBase64URLSafeString(identityTokenParams.toJSON().getBytes()); + } + + public String newPresignedGetCallerIdentityTokenRequest() throws Exception { + GeneratePresignGetCallerIdentityResponse signedRequest = this.newPresignedRequest(); + + // generate token parameters object + LakeFSExternalPrincipalIdentityRequest identityTokenParams = new LakeFSExternalPrincipalIdentityRequest(signedRequest.getHTTPMethod(), signedRequest.getHost(), signedRequest.getRegion(), signedRequest.getAction(), signedRequest.getDate(), signedRequest.getExpires(), signedRequest.getAccessKeyId(), signedRequest.getSignature(), Arrays.asList(signedRequest.getSignedHeadersParam().split(";")), signedRequest.getVersion(), signedRequest.getAlgorithm(), signedRequest.getSecurityToken()); // base64 encode - return Base64.encodeBase64String(identityTokenParams.toJSON().getBytes()); + return Base64.encodeBase64URLSafeString(identityTokenParams.toJSON().getBytes()); } private void newToken() throws Exception { + // created identity token to exchange for lakeFS token String identityToken = this.newPresignedGetCallerIdentityToken(); - /* - TODO(isan) - depends on missing functionality PR https://github.com/treeverse/lakeFS/pull/7578 being merged. - before merging this code - implement the call to lakeFS. - it will introduce the functionality in the generated client of actually doing the login. - call lakeFS to exchange the token for a lakeFS token - The flow will be: - 1. use this.lakeFSApi Client with ExternalPrincipal API class (no auth required) - 2. this.lakeFSAuthToken = call api.ExternalPrincipalLogin(identityToken, ) - */ - // dummy initiation - this.lakeFSAuthToken = new AuthenticationToken(); - this.lakeFSAuthToken.setTokenExpiration(System.currentTimeMillis() + 60); + + // build lakeFS login request + ExternalLoginInformation req = new ExternalLoginInformation(); + + if (this.lakeFSTokenTTLSeconds.isPresent()) { + req.setTokenExpirationDuration(this.lakeFSTokenTTLSeconds.get()); + } + + // set identity request + IdentityRequestRequestWrapper t = new IdentityRequestRequestWrapper(identityToken); + req.setIdentityRequest(t); + + // call lakeFS to exchange the identity token for a lakeFS token + AuthApi auth = new AuthApi(this.lakeFSApi); + this.lakeFSAuthToken = auth.externalPrincipalLogin().externalLoginInformation(req).execute(); } // refresh can be called to create a new token regardless if the current token is expired or not or does not exist. diff --git a/clients/hadoopfs/src/main/java/io/lakefs/auth/IdentityRequestRequestWrapper.java b/clients/hadoopfs/src/main/java/io/lakefs/auth/IdentityRequestRequestWrapper.java new file mode 100644 index 00000000000..4ac5b70a56d --- /dev/null +++ b/clients/hadoopfs/src/main/java/io/lakefs/auth/IdentityRequestRequestWrapper.java @@ -0,0 +1,11 @@ +package io.lakefs.auth; + +import com.google.gson.annotations.SerializedName; + +public class IdentityRequestRequestWrapper { + @SerializedName("identity_token") + private String identityToken; + public IdentityRequestRequestWrapper(String identityToken) { + this.identityToken = identityToken; + } +} diff --git a/clients/hadoopfs/src/test/java/io/lakefs/auth/AWSLakeFSTokenProviderTest.java b/clients/hadoopfs/src/test/java/io/lakefs/auth/AWSLakeFSTokenProviderTest.java index 9106c979c73..304cbca6430 100644 --- a/clients/hadoopfs/src/test/java/io/lakefs/auth/AWSLakeFSTokenProviderTest.java +++ b/clients/hadoopfs/src/test/java/io/lakefs/auth/AWSLakeFSTokenProviderTest.java @@ -1,13 +1,31 @@ package io.lakefs.auth; +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import io.lakefs.Constants; import io.lakefs.FSConfiguration; +import io.lakefs.clients.sdk.model.AuthenticationToken; import org.apache.commons.codec.binary.Base64; import org.apache.hadoop.conf.Configuration; import org.junit.Assert; + +import org.junit.Rule; import org.junit.Test; +import org.mockserver.client.MockServerClient; +import org.mockserver.junit.MockServerRule; +import org.mockserver.matchers.Times; +import org.mockserver.model.Cookie; +import org.mockserver.model.HttpRequest; + +import static org.mockserver.model.HttpResponse.response; + public class AWSLakeFSTokenProviderTest { + @Rule + public MockServerRule mockServerRule = new MockServerRule(this); + protected MockServerClient mockServerClient; + protected final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create(); @Test public void testProviderIdentityTokenSerde() throws Exception { @@ -36,4 +54,38 @@ public void testProviderIdentityTokenSerde() throws Exception { Assert.assertEquals("AWS4-HMAC-SHA256", request.getAlgorithm()); Assert.assertEquals(FSConfiguration.get(conf, "lakefs", Constants.TOKEN_AWS_CREDENTIALS_PROVIDER_SESSION_TOKEN_KEY_SUFFIX), request.getSecurityToken()); } + + protected void mockExternalPrincipalLogin(Long tokenExpiration, String token, String sessionID) { + // lakeFSFS initialization requires a blockstore. + HttpRequest request = HttpRequest.request().withCookie(new Cookie("sessionId", sessionID)); + + mockServerClient + .when( + request.withMethod("POST").withPath("/auth/external/principal/login"), + Times.once()) + .respond( + response().withStatusCode(200).withBody(new AuthenticationToken().token(token).tokenExpiration(tokenExpiration).toJson()) + ); + } + + @Test + public void testProviderToken() throws Exception { + String sessionID = "testProviderToken"; + String expectedToken = "lakefs-jwt-token"; + Configuration conf = new Configuration(false); + conf.set("fs.lakefs." + Constants.LAKEFS_AUTH_PROVIDER_KEY_SUFFIX, TemporaryAWSCredentialsLakeFSTokenProvider.NAME); + conf.set("fs.lakefs." + Constants.TOKEN_AWS_CREDENTIALS_PROVIDER_ACCESS_KEY_SUFFIX, "accessKeyId"); + conf.set("fs.lakefs." + Constants.TOKEN_AWS_CREDENTIALS_PROVIDER_SECRET_KEY_SUFFIX, "secretAccessKey"); + conf.set("fs.lakefs." + Constants.TOKEN_AWS_CREDENTIALS_PROVIDER_SESSION_TOKEN_KEY_SUFFIX, "sessionToken"); + conf.setInt("fs.lakefs." + Constants.LAKEFS_AUTH_TOKEN_TTL_KEY_SUFFIX, 120); + conf.set("fs.lakefs." + Constants.TOKEN_AWS_STS_ENDPOINT, "https://sts.amazonaws.com"); + conf.set("fs.lakefs.endpoint", String.format("http://localhost:%d/", mockServerClient.getPort())); + conf.set("fs.lakefs.session_id", sessionID); + + LakeFSTokenProvider provider = LakeFSTokenProviderFactory.newLakeFSTokenProvider(Constants.DEFAULT_SCHEME, conf); + mockExternalPrincipalLogin(1000L, expectedToken, sessionID); + + String lakeFSJWT = provider.getToken(); + Assert.assertEquals(expectedToken, lakeFSJWT); + } } From 9ebd706fbc1f69f9fbb66d090325c1ffbb514da8 Mon Sep 17 00:00:00 2001 From: Isan Rivkin Date: Thu, 11 Apr 2024 19:03:15 +0300 Subject: [PATCH 4/8] revert deleted --- .../reference/security/external-principals.md | 26 ------------------- 1 file changed, 26 deletions(-) delete mode 100644 docs/reference/security/external-principals.md diff --git a/docs/reference/security/external-principals.md b/docs/reference/security/external-principals.md deleted file mode 100644 index a6855a89348..00000000000 --- a/docs/reference/security/external-principals.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -title: Authentication with AWS IAM Roles -description: This section covers how to authenticate to lakeFS using AWS IAM. -grand_parent: Reference -parent: Security -redirect_from: - - /reference/external-principals-aws.html ---- - -# Authenticate to lakeFS with External Principals API - -{: .d-inline-block } -lakeFS Enterprise -{: .label .label-purple } - -{: .note} -> External principals API is available for lakeFS Enterprise. If you're using the open-source version you can check the [pluggable APIs](https://docs.lakefs.io/reference/security/rbac.html#pluggable-authentication-and-authorization). - -## Overview - -## Authenticate to lakeFS with AWS IAM Roles - -## Using with Spark - - - From 475b4261068ef514e83c457200e8d14772c3b1ab Mon Sep 17 00:00:00 2001 From: Isan Rivkin Date: Fri, 12 Apr 2024 11:05:01 +0300 Subject: [PATCH 5/8] fix encodig --- .../java/io/lakefs/auth/AWSLakeFSTokenProvider.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/clients/hadoopfs/src/main/java/io/lakefs/auth/AWSLakeFSTokenProvider.java b/clients/hadoopfs/src/main/java/io/lakefs/auth/AWSLakeFSTokenProvider.java index 0be67221eed..5f9fdfb0d28 100644 --- a/clients/hadoopfs/src/main/java/io/lakefs/auth/AWSLakeFSTokenProvider.java +++ b/clients/hadoopfs/src/main/java/io/lakefs/auth/AWSLakeFSTokenProvider.java @@ -117,17 +117,7 @@ public String newPresignedGetCallerIdentityToken() throws Exception { LakeFSExternalPrincipalIdentityRequest identityTokenParams = new LakeFSExternalPrincipalIdentityRequest(signedRequest.getHTTPMethod(), signedRequest.getHost(), signedRequest.getRegion(), signedRequest.getAction(), signedRequest.getDate(), signedRequest.getExpires(), signedRequest.getAccessKeyId(), signedRequest.getSignature(), Arrays.asList(signedRequest.getSignedHeadersParam().split(";")), signedRequest.getVersion(), signedRequest.getAlgorithm(), signedRequest.getSecurityToken()); // base64 encode - return Base64.encodeBase64URLSafeString(identityTokenParams.toJSON().getBytes()); - } - - public String newPresignedGetCallerIdentityTokenRequest() throws Exception { - GeneratePresignGetCallerIdentityResponse signedRequest = this.newPresignedRequest(); - - // generate token parameters object - LakeFSExternalPrincipalIdentityRequest identityTokenParams = new LakeFSExternalPrincipalIdentityRequest(signedRequest.getHTTPMethod(), signedRequest.getHost(), signedRequest.getRegion(), signedRequest.getAction(), signedRequest.getDate(), signedRequest.getExpires(), signedRequest.getAccessKeyId(), signedRequest.getSignature(), Arrays.asList(signedRequest.getSignedHeadersParam().split(";")), signedRequest.getVersion(), signedRequest.getAlgorithm(), signedRequest.getSecurityToken()); - - // base64 encode - return Base64.encodeBase64URLSafeString(identityTokenParams.toJSON().getBytes()); + return Base64.encodeBase64String(identityTokenParams.toJSON().getBytes()); } private void newToken() throws Exception { From 120156b020310dc8903f9e0ed696f48f35ec094f Mon Sep 17 00:00:00 2001 From: Isan Rivkin Date: Fri, 12 Apr 2024 11:11:56 +0300 Subject: [PATCH 6/8] update review comment --- .../src/main/java/io/lakefs/auth/AWSLakeFSTokenProvider.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clients/hadoopfs/src/main/java/io/lakefs/auth/AWSLakeFSTokenProvider.java b/clients/hadoopfs/src/main/java/io/lakefs/auth/AWSLakeFSTokenProvider.java index 5f9fdfb0d28..3fb09ee09e1 100644 --- a/clients/hadoopfs/src/main/java/io/lakefs/auth/AWSLakeFSTokenProvider.java +++ b/clients/hadoopfs/src/main/java/io/lakefs/auth/AWSLakeFSTokenProvider.java @@ -127,9 +127,8 @@ private void newToken() throws Exception { // build lakeFS login request ExternalLoginInformation req = new ExternalLoginInformation(); - if (this.lakeFSTokenTTLSeconds.isPresent()) { - req.setTokenExpirationDuration(this.lakeFSTokenTTLSeconds.get()); - } + // set lakeFS token expiration if provided by the configuration + this.lakeFSTokenTTLSeconds.ifPresent(req::setTokenExpirationDuration); // set identity request IdentityRequestRequestWrapper t = new IdentityRequestRequestWrapper(identityToken); From aa589e451a6542683de83e1679ca6565f38ee5bc Mon Sep 17 00:00:00 2001 From: Isan Rivkin Date: Fri, 12 Apr 2024 11:15:59 +0300 Subject: [PATCH 7/8] update changelog --- clients/hadoopfs/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/clients/hadoopfs/CHANGELOG.md b/clients/hadoopfs/CHANGELOG.md index f518d6f37e1..bd63cf81b71 100644 --- a/clients/hadoopfs/CHANGELOG.md +++ b/clients/hadoopfs/CHANGELOG.md @@ -2,6 +2,10 @@ ## _Upcoming_ +## 0.2.4 + +lakeFSFS: new Token Provider feature with IAM Role Support for lakeFS authentication (#7659 + #7604) + ## 0.2.3 * Fix createDirectoryMarkerIfNotExists (#7510) From c507bbfc4c5aa440b4fbe8dca427f09347a9b285 Mon Sep 17 00:00:00 2001 From: Isan Rivkin Date: Fri, 12 Apr 2024 11:16:32 +0300 Subject: [PATCH 8/8] update changelog --- clients/hadoopfs/CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/clients/hadoopfs/CHANGELOG.md b/clients/hadoopfs/CHANGELOG.md index bd63cf81b71..6685bdbeccb 100644 --- a/clients/hadoopfs/CHANGELOG.md +++ b/clients/hadoopfs/CHANGELOG.md @@ -2,8 +2,6 @@ ## _Upcoming_ -## 0.2.4 - lakeFSFS: new Token Provider feature with IAM Role Support for lakeFS authentication (#7659 + #7604) ## 0.2.3