Skip to content

Commit d1fcff8

Browse files
authored
Add sessionToken support for S3InputSource (#18609)
Allows Druid to use an AWS_SESSION_TOKEN (and skip assuming a role to fetch a token) if provided in the spec. No explicit assumptions are made with the input (e.g. that adding sessionToken and assumeRoleAre are mutually exclusive). This support falls in line with how other engines use things like external catalogs, etc. to vend temporary credentials to access S3, while leaving things like the exact ARN opaque to the caller.
1 parent 78a7acf commit d1fcff8

File tree

10 files changed

+206
-23
lines changed

10 files changed

+206
-23
lines changed

docs/ingestion/input-sources.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,9 @@ Properties Object:
204204

205205
|Property|Description|Default|Required|
206206
|--------|-----------|-------|---------|
207-
|accessKeyId|The [Password Provider](../operations/password-provider.md) or plain text string of this S3 input source access key|None|yes if secretAccessKey is given|
208-
|secretAccessKey|The [Password Provider](../operations/password-provider.md) or plain text string of this S3 input source secret key|None|yes if accessKeyId is given|
207+
|accessKeyId|The [Password Provider](../operations/password-provider.md) or plain text string of this S3 input source access key|None|Yes, if `secretAccessKey` or `sessionToken` is given.|
208+
|secretAccessKey|The [Password Provider](../operations/password-provider.md) or plain text string of this S3 input source secret key|None|Yes, if `accessKeyId` or `sessionToken` is given.|
209+
|sessionToken|The [Password Provider](../operations/password-provider.md) or plain text string of this S3 input source session token|None|no|
209210
|assumeRoleArn|AWS ARN of the role to assume [see](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html). **assumeRoleArn** can be used either with the ingestion spec AWS credentials or with the default S3 credentials|None|no|
210211
|assumeRoleExternalId|A unique identifier that might be required when you assume a role in another account [see](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_request.html)|None|no|
211212

extensions-core/s3-extensions/src/main/java/org/apache/druid/catalog/model/table/S3InputSourceDefn.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ public class S3InputSourceDefn extends FormattedInputSourceDefn
7979
public static final String ACCESS_KEY_ID_PARAMETER = "accessKeyId";
8080
public static final String SECRET_ACCESS_KEY_PARAMETER = "secretAccessKey";
8181
public static final String ASSUME_ROLE_ARN_PARAMETER = "assumeRoleArn";
82+
public static final String SESSION_TOKEN_PARAMETER = "sessionToken";
8283

8384
/**
8485
* The {@code objectGlob} property exists in S3, but is not documented. The corresponding
@@ -102,7 +103,8 @@ public class S3InputSourceDefn extends FormattedInputSourceDefn
102103
private static final List<ParameterDefn> SECURITY_PARAMS = Arrays.asList(
103104
new Parameter(ACCESS_KEY_ID_PARAMETER, ParameterType.VARCHAR, true),
104105
new Parameter(SECRET_ACCESS_KEY_PARAMETER, ParameterType.VARCHAR, true),
105-
new Parameter(ASSUME_ROLE_ARN_PARAMETER, ParameterType.VARCHAR, true)
106+
new Parameter(ASSUME_ROLE_ARN_PARAMETER, ParameterType.VARCHAR, true),
107+
new Parameter(SESSION_TOKEN_PARAMETER, ParameterType.VARCHAR, true)
106108
);
107109

108110
// Field names in the S3InputSource
@@ -114,6 +116,7 @@ public class S3InputSourceDefn extends FormattedInputSourceDefn
114116
private static final String ACCESS_KEY_ID_FIELD = "accessKeyId";
115117
private static final String SECRET_ACCESS_KEY_FIELD = "secretAccessKey";
116118
private static final String ASSUME_ROLE_ARN_FIELD = "assumeRoleArn";
119+
private static final String SESSION_TOKEN_FIELD = "sessionToken";
117120

118121
@Override
119122
public String typeValue()
@@ -250,6 +253,7 @@ private void applySecurityParams(Map<String, Object> jsonMap, Map<String, Object
250253
final String accessKeyId = CatalogUtils.getNonBlankString(args, ACCESS_KEY_ID_PARAMETER);
251254
final String secretAccessKey = CatalogUtils.getNonBlankString(args, SECRET_ACCESS_KEY_PARAMETER);
252255
final String assumeRoleArn = CatalogUtils.getNonBlankString(args, ASSUME_ROLE_ARN_PARAMETER);
256+
final String sessionToken = CatalogUtils.getNonBlankString(args, SESSION_TOKEN_PARAMETER);
253257
if (accessKeyId != null || secretAccessKey != null || assumeRoleArn != null) {
254258
Map<String, Object> properties = new HashMap<>();
255259
if (accessKeyId != null) {
@@ -258,6 +262,9 @@ private void applySecurityParams(Map<String, Object> jsonMap, Map<String, Object
258262
if (secretAccessKey != null) {
259263
properties.put(SECRET_ACCESS_KEY_FIELD, secretAccessKey);
260264
}
265+
if (sessionToken != null) {
266+
properties.put(SESSION_TOKEN_FIELD, sessionToken);
267+
}
261268
if (assumeRoleArn != null) {
262269
properties.put(ASSUME_ROLE_ARN_FIELD, assumeRoleArn);
263270
}

extensions-core/s3-extensions/src/main/java/org/apache/druid/data/input/s3/S3InputSource.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121

2222
import com.amazonaws.Protocol;
2323
import com.amazonaws.auth.AWSCredentialsProvider;
24+
import com.amazonaws.auth.AWSSessionCredentials;
2425
import com.amazonaws.auth.AWSStaticCredentialsProvider;
2526
import com.amazonaws.auth.BasicAWSCredentials;
27+
import com.amazonaws.auth.BasicSessionCredentials;
2628
import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider;
2729
import com.amazonaws.client.builder.AwsClientBuilder;
2830
import com.amazonaws.services.s3.model.ObjectMetadata;
@@ -282,12 +284,21 @@ private void applyAssumeRole(
282284
@Nonnull
283285
private AWSStaticCredentialsProvider createStaticCredentialsProvider(S3InputSourceConfig s3InputSourceConfig)
284286
{
285-
return new AWSStaticCredentialsProvider(
286-
new BasicAWSCredentials(
287-
s3InputSourceConfig.getAccessKeyId().getPassword(),
288-
s3InputSourceConfig.getSecretAccessKey().getPassword()
289-
)
290-
);
287+
if (s3InputSourceConfig.getSessionToken() != null) {
288+
AWSSessionCredentials sessionCredentials = new BasicSessionCredentials(
289+
s3InputSourceConfig.getAccessKeyId().getPassword(),
290+
s3InputSourceConfig.getSecretAccessKey().getPassword(),
291+
s3InputSourceConfig.getSessionToken().getPassword()
292+
);
293+
return new AWSStaticCredentialsProvider(sessionCredentials);
294+
} else {
295+
return new AWSStaticCredentialsProvider(
296+
new BasicAWSCredentials(
297+
s3InputSourceConfig.getAccessKeyId().getPassword(),
298+
s3InputSourceConfig.getSecretAccessKey().getPassword()
299+
)
300+
);
301+
}
291302
}
292303

293304
@Nullable

extensions-core/s3-extensions/src/main/java/org/apache/druid/data/input/s3/S3InputSourceConfig.java

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,24 +39,27 @@ public class S3InputSourceConfig
3939
private final String assumeRoleExternalId;
4040
private final PasswordProvider accessKeyId;
4141
private final PasswordProvider secretAccessKey;
42+
private final PasswordProvider sessionToken;
4243

4344
@JsonCreator
4445
public S3InputSourceConfig(
4546
@JsonProperty("accessKeyId") @Nullable PasswordProvider accessKeyId,
4647
@JsonProperty("secretAccessKey") @Nullable PasswordProvider secretAccessKey,
4748
@JsonProperty("assumeRoleArn") @Nullable String assumeRoleArn,
48-
@JsonProperty("assumeRoleExternalId") @Nullable String assumeRoleExternalId
49+
@JsonProperty("assumeRoleExternalId") @Nullable String assumeRoleExternalId,
50+
@JsonProperty("sessionToken") @Nullable PasswordProvider sessionToken
4951
)
5052
{
5153
this.assumeRoleArn = assumeRoleArn;
5254
this.assumeRoleExternalId = assumeRoleExternalId;
53-
if (accessKeyId != null || secretAccessKey != null) {
54-
this.accessKeyId = Preconditions.checkNotNull(accessKeyId, "accessKeyId cannot be null if secretAccessKey is given");
55-
this.secretAccessKey = Preconditions.checkNotNull(secretAccessKey, "secretAccessKey cannot be null if accessKeyId is given");
55+
if (sessionToken != null || accessKeyId != null || secretAccessKey != null) {
56+
this.accessKeyId = Preconditions.checkNotNull(accessKeyId, "'accessKeyId' cannot be null if 'secretAccessKey' or 'sessionToken' is given");
57+
this.secretAccessKey = Preconditions.checkNotNull(secretAccessKey, "'secretAccessKey' cannot be null if 'accessKeyId' or 'sessionToken' is given");
5658
} else {
5759
this.accessKeyId = null;
5860
this.secretAccessKey = null;
5961
}
62+
this.sessionToken = sessionToken;
6063
}
6164

6265
@Nullable
@@ -91,6 +94,14 @@ public PasswordProvider getSecretAccessKey()
9194
return secretAccessKey;
9295
}
9396

97+
@Nullable
98+
@JsonProperty
99+
@JsonInclude(JsonInclude.Include.NON_NULL)
100+
public PasswordProvider getSessionToken()
101+
{
102+
return sessionToken;
103+
}
104+
94105
@JsonIgnore
95106
public boolean isCredentialsConfigured()
96107
{
@@ -106,6 +117,7 @@ public String toString()
106117
", secretAccessKey=" + secretAccessKey +
107118
", assumeRoleArn=" + assumeRoleArn +
108119
", assumeRoleExternalId=" + assumeRoleExternalId +
120+
", sessionToken=" + sessionToken +
109121
'}';
110122
}
111123

@@ -122,12 +134,13 @@ public boolean equals(Object o)
122134
return Objects.equals(accessKeyId, that.accessKeyId) &&
123135
Objects.equals(secretAccessKey, that.secretAccessKey) &&
124136
Objects.equals(assumeRoleArn, that.assumeRoleArn) &&
125-
Objects.equals(assumeRoleExternalId, that.assumeRoleExternalId);
137+
Objects.equals(assumeRoleExternalId, that.assumeRoleExternalId) &&
138+
Objects.equals(sessionToken, that.sessionToken);
126139
}
127140

128141
@Override
129142
public int hashCode()
130143
{
131-
return Objects.hash(accessKeyId, secretAccessKey, assumeRoleArn, assumeRoleExternalId);
144+
return Objects.hash(accessKeyId, secretAccessKey, assumeRoleArn, assumeRoleExternalId, sessionToken);
132145
}
133146
}

extensions-core/s3-extensions/src/test/java/org/apache/druid/catalog/model/table/S3InputSourceDefnTest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
import org.junit.Test;
4343

4444
import javax.annotation.Nullable;
45-
4645
import java.util.Arrays;
4746
import java.util.Collections;
4847
import java.util.HashMap;
@@ -558,7 +557,7 @@ public void testAdHocPrefixPathConflict()
558557
@Test
559558
public void testFullTableSpecHappyPath()
560559
{
561-
S3InputSourceConfig config = new S3InputSourceConfig(null, null, "foo", null);
560+
S3InputSourceConfig config = new S3InputSourceConfig(null, null, "foo", null, null);
562561
S3InputSource s3InputSource = s3InputSource(
563562
Arrays.asList("s3://foo/bar/", "s3://mumble/"), null, null, "*.csv", config);
564563
TableMetadata table = TableBuilder.external("foo")

extensions-core/s3-extensions/src/test/java/org/apache/druid/data/input/s3/S3InputSourceConfigTest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ public void testSerdeAccessSecretKey() throws Exception
3636
new DefaultPasswordProvider("the-access-key"),
3737
new DefaultPasswordProvider("the-secret-key"),
3838
null,
39-
null
39+
null,
40+
new DefaultPasswordProvider("the-secret-token")
4041
);
4142

4243
Assert.assertEquals(
@@ -53,7 +54,8 @@ public void testSerdeAssumeRole() throws Exception
5354
null,
5455
null,
5556
"the-role-arn",
56-
"the-role-external-id"
57+
"the-role-external-id",
58+
null
5759
);
5860

5961
Assert.assertEquals(

extensions-core/s3-extensions/src/test/java/org/apache/druid/data/input/s3/S3InputSourceTest.java

Lines changed: 150 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,10 @@ public class S3InputSourceTest extends InitializedNullHandlingTest
158158
);
159159

160160
private static final S3InputSourceConfig CLOUD_CONFIG_PROPERTIES = new S3InputSourceConfig(
161-
new DefaultPasswordProvider("myKey"), new DefaultPasswordProvider("mySecret"), null, null);
161+
new DefaultPasswordProvider("myKey"), new DefaultPasswordProvider("mySecret"), null, null, null);
162+
private static final S3InputSourceConfig CLOUD_CONFIG_PROPERTIES_WITH_SESSION_TOKEN = new S3InputSourceConfig(
163+
new DefaultPasswordProvider("myKey"), new DefaultPasswordProvider("mySecret"), null, null,
164+
new DefaultPasswordProvider("mySessionToken"));
162165
private static final AWSEndpointConfig ENDPOINT_CONFIG = new AWSEndpointConfig();
163166
private static final AWSProxyConfig PROXY_CONFIG = new AWSProxyConfig();
164167
private static final AWSClientConfig CLIENT_CONFIG = new AWSClientConfig();
@@ -370,6 +373,152 @@ public void testSerdeWithCloudConfigPropertiesWithKeyAndSecret() throws Exceptio
370373
EasyMock.verify(SERVER_SIDE_ENCRYPTING_AMAZON_S3_BUILDER);
371374
}
372375

376+
@Test
377+
public void testSerdeWithCloudConfigPropertiesWithSessionToken() throws Exception
378+
{
379+
EasyMock.reset(SERVER_SIDE_ENCRYPTING_AMAZON_S3_BUILDER);
380+
EasyMock.expect(SERVER_SIDE_ENCRYPTING_AMAZON_S3_BUILDER.getAmazonS3ClientBuilder())
381+
.andStubReturn(AMAZON_S3_CLIENT_BUILDER);
382+
AMAZON_S3_CLIENT_BUILDER.withClientConfiguration(CLIENT_CONFIGURATION);
383+
EasyMock.expect(SERVER_SIDE_ENCRYPTING_AMAZON_S3_BUILDER.build())
384+
.andReturn(SERVICE);
385+
EasyMock.replay(SERVER_SIDE_ENCRYPTING_AMAZON_S3_BUILDER);
386+
final S3InputSource withSessionToken = new S3InputSource(
387+
SERVICE,
388+
SERVER_SIDE_ENCRYPTING_AMAZON_S3_BUILDER,
389+
INPUT_DATA_CONFIG,
390+
null,
391+
null,
392+
EXPECTED_LOCATION,
393+
null,
394+
CLOUD_CONFIG_PROPERTIES_WITH_SESSION_TOKEN,
395+
null,
396+
null,
397+
null
398+
);
399+
final S3InputSource serdeWithSessionToken =
400+
MAPPER.readValue(MAPPER.writeValueAsString(withSessionToken), S3InputSource.class);
401+
// This is to force the s3ClientSupplier to initialize the ServerSideEncryptingAmazonS3
402+
serdeWithSessionToken.createEntity(new CloudObjectLocation("bucket", "path"));
403+
Assert.assertEquals(withSessionToken, serdeWithSessionToken);
404+
// Verify that the session token is properly set
405+
Assert.assertNotNull(serdeWithSessionToken.getS3InputSourceConfig());
406+
Assert.assertNotNull(serdeWithSessionToken.getS3InputSourceConfig().getSessionToken());
407+
Assert.assertEquals("mySessionToken", serdeWithSessionToken.getS3InputSourceConfig().getSessionToken().getPassword());
408+
EasyMock.verify(SERVER_SIDE_ENCRYPTING_AMAZON_S3_BUILDER);
409+
}
410+
411+
@Test
412+
public void testGetSetSessionToken()
413+
{
414+
// Test that session token getter/setter work correctly
415+
final S3InputSource inputSourceWithSessionToken = new S3InputSource(
416+
SERVICE,
417+
SERVER_SIDE_ENCRYPTING_AMAZON_S3_BUILDER,
418+
INPUT_DATA_CONFIG,
419+
EXPECTED_URIS,
420+
null,
421+
null,
422+
null,
423+
CLOUD_CONFIG_PROPERTIES_WITH_SESSION_TOKEN,
424+
null,
425+
null,
426+
null
427+
);
428+
429+
Assert.assertNotNull(inputSourceWithSessionToken.getS3InputSourceConfig());
430+
Assert.assertNotNull(inputSourceWithSessionToken.getS3InputSourceConfig().getSessionToken());
431+
Assert.assertEquals(
432+
"mySessionToken",
433+
inputSourceWithSessionToken.getS3InputSourceConfig().getSessionToken().getPassword()
434+
);
435+
436+
// Test without session token
437+
final S3InputSource inputSourceWithoutSessionToken = new S3InputSource(
438+
SERVICE,
439+
SERVER_SIDE_ENCRYPTING_AMAZON_S3_BUILDER,
440+
INPUT_DATA_CONFIG,
441+
EXPECTED_URIS,
442+
null,
443+
null,
444+
null,
445+
CLOUD_CONFIG_PROPERTIES,
446+
null,
447+
null,
448+
null
449+
);
450+
451+
Assert.assertNotNull(inputSourceWithoutSessionToken.getS3InputSourceConfig());
452+
Assert.assertNull(inputSourceWithoutSessionToken.getS3InputSourceConfig().getSessionToken());
453+
}
454+
455+
@Test
456+
public void testSessionCredentialsUsedWhenSessionTokenProvided() throws IOException
457+
{
458+
// This test verifies that when session token is provided, the S3InputSource
459+
// correctly uses BasicSessionCredentials instead of BasicAWSCredentials
460+
EasyMock.reset(S3_CLIENT);
461+
expectListObjects(PREFIXES.get(0), ImmutableList.of(EXPECTED_URIS.get(0)), CONTENT);
462+
expectGetObject(EXPECTED_URIS.get(0));
463+
EasyMock.replay(S3_CLIENT);
464+
465+
EasyMock.reset(SERVER_SIDE_ENCRYPTING_AMAZON_S3_BUILDER);
466+
EasyMock.expect(SERVER_SIDE_ENCRYPTING_AMAZON_S3_BUILDER.getAmazonS3ClientBuilder())
467+
.andStubReturn(AMAZON_S3_CLIENT_BUILDER);
468+
EasyMock.expect(SERVER_SIDE_ENCRYPTING_AMAZON_S3_BUILDER.build())
469+
.andReturn(SERVICE);
470+
EasyMock.replay(SERVER_SIDE_ENCRYPTING_AMAZON_S3_BUILDER);
471+
472+
// Create S3InputSource with session token
473+
S3InputSource inputSource = new S3InputSource(
474+
SERVICE,
475+
SERVER_SIDE_ENCRYPTING_AMAZON_S3_BUILDER,
476+
INPUT_DATA_CONFIG,
477+
null,
478+
ImmutableList.of(PREFIXES.get(0)),
479+
null,
480+
null,
481+
CLOUD_CONFIG_PROPERTIES_WITH_SESSION_TOKEN,
482+
null,
483+
null,
484+
null
485+
);
486+
487+
// Verify session token is set
488+
Assert.assertNotNull(inputSource.getS3InputSourceConfig());
489+
Assert.assertNotNull(inputSource.getS3InputSourceConfig().getSessionToken());
490+
Assert.assertEquals(
491+
"mySessionToken",
492+
inputSource.getS3InputSourceConfig().getSessionToken().getPassword()
493+
);
494+
495+
// Create a reader which will trigger the s3ClientSupplier and use the session credentials
496+
InputRowSchema someSchema = new InputRowSchema(
497+
new TimestampSpec("time", "auto", null),
498+
new DimensionsSpec(DimensionsSpec.getDefaultSchemas(ImmutableList.of("dim1", "dim2"))),
499+
ColumnsFilter.all()
500+
);
501+
502+
InputSourceReader reader = inputSource.reader(
503+
someSchema,
504+
new CsvInputFormat(ImmutableList.of("time", "dim1", "dim2"), "|", false, null, 0, null),
505+
temporaryFolder.newFolder()
506+
);
507+
508+
// Read data - this exercises the session credentials path
509+
CloseableIterator<InputRow> iterator = reader.read();
510+
511+
while (iterator.hasNext()) {
512+
InputRow nextRow = iterator.next();
513+
Assert.assertEquals(NOW, nextRow.getTimestamp());
514+
Assert.assertEquals("hello", nextRow.getDimension("dim1").get(0));
515+
Assert.assertEquals("world", nextRow.getDimension("dim2").get(0));
516+
}
517+
518+
EasyMock.verify(S3_CLIENT);
519+
EasyMock.verify(SERVER_SIDE_ENCRYPTING_AMAZON_S3_BUILDER);
520+
}
521+
373522
@Test
374523
public void testGetTypes()
375524
{

multi-stage-query/src/main/java/org/apache/druid/msq/util/MSQTaskQueryMakerUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
public class MSQTaskQueryMakerUtils
4141
{
4242

43-
public static final Set<String> SENSISTIVE_JSON_KEYS = ImmutableSet.of("accessKeyId", "secretAccessKey");
43+
public static final Set<String> SENSISTIVE_JSON_KEYS = ImmutableSet.of("accessKeyId", "secretAccessKey", "sessionToken");
4444
public static final Set<Pattern> SENSITIVE_KEYS_REGEX_PATTERNS = SENSISTIVE_JSON_KEYS.stream()
4545
.map(sensitiveKey ->
4646
Pattern.compile(

multi-stage-query/src/test/java/org/apache/druid/msq/util/MSQTaskQueryMakerUtilsTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public void maskSensitiveJsonKeys()
5757
+ "OVERWRITE ALL\\n"
5858
+ "WITH ext AS "
5959
+ "(SELECT *\\nFROM TABLE(\\n "
60-
+ "EXTERN(\\n '{\\\"type\\\":\\\"s3\\\",\\\"prefixes\\\":[\\\"s3://prefix\\\"],\\\"properties\\\":{\\\"accessKeyId\\\":{\\\"type\\\":\\\"default\\\",\\\"password\\\":\\\"secret_pass\\\"},\\\"secretAccessKey\\\":{\\\"type\\\":\\\"default\\\",\\\"password\\\":\\\"secret_pass\\\"}}}',\\n"
60+
+ "EXTERN(\\n '{\\\"type\\\":\\\"s3\\\",\\\"prefixes\\\":[\\\"s3://prefix\\\"],\\\"properties\\\":{\\\"accessKeyId\\\":{\\\"type\\\":\\\"default\\\",\\\"password\\\":\\\"secret_pass\\\"},\\\"secretAccessKey\\\":{\\\"type\\\":\\\"default\\\",\\\"password\\\":\\\"secret_pass\\\"},\\\"sessionToken\\\":{\\\"type\\\":\\\"default\\\",\\\"password\\\":\\\"secret_pass\\\"}}}',\\n"
6161
+ "'{\\\"type\\\":\\\"json\\\"}',\\n"
6262
+ "'[{\\\"name\\\":\\\"time\\\",\\\"type\\\":\\\"string\\\"},{\\\"name\\\":\\\"name\\\",\\\"type\\\":\\\"string\\\"}]'\\n )\\n))\\n"
6363
+ "SELECT\\n TIME_PARSE(\\\"time\\\") AS __time,\\n name,\\n country "
@@ -113,7 +113,7 @@ public void maskSensitiveJsonKeys()
113113
+ "OVERWRITE ALL\\n"
114114
+ "WITH ext AS "
115115
+ "(SELECT *\\nFROM TABLE(\\n "
116-
+ "EXTERN(\\n '{\\\"type\\\":\\\"s3\\\",\\\"prefixes\\\":[\\\"s3://prefix\\\"],\\\"properties\\\":{\\\"accessKeyId\\\":<masked>,\\\"secretAccessKey\\\":<masked>}}',\\n"
116+
+ "EXTERN(\\n '{\\\"type\\\":\\\"s3\\\",\\\"prefixes\\\":[\\\"s3://prefix\\\"],\\\"properties\\\":{\\\"accessKeyId\\\":<masked>,\\\"secretAccessKey\\\":<masked>,\\\"sessionToken\\\":<masked>}}',\\n"
117117
+ "'{\\\"type\\\":\\\"json\\\"}',\\n"
118118
+ "'[{\\\"name\\\":\\\"time\\\",\\\"type\\\":\\\"string\\\"},{\\\"name\\\":\\\"name\\\",\\\"type\\\":\\\"string\\\"}]'\\n )\\n))\\n"
119119
+ "SELECT\\n TIME_PARSE(\\\"time\\\") AS __time,\\n name,\\n country "

0 commit comments

Comments
 (0)