mirror of https://github.com/apache/druid.git
AWS RDS token based password provider (#9518)
* refresh db pwd * aws iam token password provider * fix analyze-dependencies build * fix doc build * add ut for BasicDataSourceExt * more doc updates * more doc update * moving aws token password provider to new extension * remove duplicate changes * make all config inline * extension docs * refresh db password in SQL Firehose code path as well * add ut * fix build * add new extension to distribution * rds lib is not provided * fix license build * add version to license * change parent version to 0.19.0-snapshot * address review comments * fix core/ code coverage * Update server/src/main/java/org/apache/druid/metadata/BasicDataSourceExt.java Co-authored-by: Clint Wylie <cjwylie@gmail.com> * address review comments * fix spellchecker * remove inadvertant website file change Co-authored-by: Clint Wylie <cjwylie@gmail.com>
This commit is contained in:
parent
48e576a307
commit
c7b1212a43
|
@ -230,6 +230,8 @@
|
||||||
<argument>-c</argument>
|
<argument>-c</argument>
|
||||||
<argument>org.apache.druid.extensions:druid-s3-extensions</argument>
|
<argument>org.apache.druid.extensions:druid-s3-extensions</argument>
|
||||||
<argument>-c</argument>
|
<argument>-c</argument>
|
||||||
|
<argument>org.apache.druid.extensions:druid-aws-rds-extensions</argument>
|
||||||
|
<argument>-c</argument>
|
||||||
<argument>org.apache.druid.extensions:druid-ec2-extensions</argument>
|
<argument>org.apache.druid.extensions:druid-ec2-extensions</argument>
|
||||||
<argument>-c</argument>
|
<argument>-c</argument>
|
||||||
<argument>org.apache.druid.extensions:druid-google-extensions</argument>
|
<argument>org.apache.druid.extensions:druid-google-extensions</argument>
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
---
|
||||||
|
id: druid-aws-rds
|
||||||
|
title: "Druid AWS RDS Module"
|
||||||
|
---
|
||||||
|
|
||||||
|
<!--
|
||||||
|
~ Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
~ or more contributor license agreements. See the NOTICE file
|
||||||
|
~ distributed with this work for additional information
|
||||||
|
~ regarding copyright ownership. The ASF licenses this file
|
||||||
|
~ to you under the Apache License, Version 2.0 (the
|
||||||
|
~ "License"); you may not use this file except in compliance
|
||||||
|
~ with the License. You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing,
|
||||||
|
~ software distributed under the License is distributed on an
|
||||||
|
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
~ KIND, either express or implied. See the License for the
|
||||||
|
~ specific language governing permissions and limitations
|
||||||
|
~ under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
[AWS RDS](https://aws.amazon.com/rds/) is a managed service to operate relation databases such as PostgreSQL, Mysql etc. These databases could be accessed using static db password mechanism or via [AWS IAM](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/UsingWithRDS.IAMDBAuth.html) temporary tokens. This module provides AWS RDS token [password provider](../../operations/password-provider.md) implementation to be used with [mysql-metadata-store](mysql.md) or [postgresql-metadata-store](postgresql.md) when mysql/postgresql is operated using AWS RDS.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "type": "aws-rds-token", "user": "USER", "host": "HOST", "port": PORT, "region": "AWS_REGION" }
|
||||||
|
```
|
||||||
|
|
||||||
|
Before using this password provider, please make sure that you have connected all dots for db user to connect using token.
|
||||||
|
See [AWS Guide](https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/UsingWithRDS.IAMDBAuth.html).
|
||||||
|
|
||||||
|
To use this extension, make sure you [include](../../development/extensions.md#loading-extensions) it in your config file along with other extensions e.g.
|
||||||
|
|
||||||
|
```
|
||||||
|
druid.extensions.loadList=["druid-aws-rds-extensions", "postgresql-metadata-storage", ...]
|
||||||
|
```
|
|
@ -56,6 +56,7 @@ Core extensions are maintained by Druid committers.
|
||||||
|druid-ranger-security|Support for access control through Apache Ranger.|[link](../development/extensions-core/druid-ranger-security.md)|
|
|druid-ranger-security|Support for access control through Apache Ranger.|[link](../development/extensions-core/druid-ranger-security.md)|
|
||||||
|druid-s3-extensions|Interfacing with data in AWS S3, and using S3 as deep storage.|[link](../development/extensions-core/s3.md)|
|
|druid-s3-extensions|Interfacing with data in AWS S3, and using S3 as deep storage.|[link](../development/extensions-core/s3.md)|
|
||||||
|druid-ec2-extensions|Interfacing with AWS EC2 for autoscaling middle managers|UNDOCUMENTED|
|
|druid-ec2-extensions|Interfacing with AWS EC2 for autoscaling middle managers|UNDOCUMENTED|
|
||||||
|
|druid-aws-rds-extensions|Support for AWS token based access to AWS RDS DB Cluster.|[link](../development/extensions-core/druid-aws-rds.md)|
|
||||||
|druid-stats|Statistics related module including variance and standard deviation.|[link](../development/extensions-core/stats.md)|
|
|druid-stats|Statistics related module including variance and standard deviation.|[link](../development/extensions-core/stats.md)|
|
||||||
|mysql-metadata-storage|MySQL metadata store.|[link](../development/extensions-core/mysql.md)|
|
|mysql-metadata-storage|MySQL metadata store.|[link](../development/extensions-core/mysql.md)|
|
||||||
|postgresql-metadata-storage|PostgreSQL metadata store.|[link](../development/extensions-core/postgresql.md)|
|
|postgresql-metadata-storage|PostgreSQL metadata store.|[link](../development/extensions-core/postgresql.md)|
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
~ Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
~ or more contributor license agreements. See the NOTICE file
|
||||||
|
~ distributed with this work for additional information
|
||||||
|
~ regarding copyright ownership. The ASF licenses this file
|
||||||
|
~ to you under the Apache License, Version 2.0 (the
|
||||||
|
~ "License"); you may not use this file except in compliance
|
||||||
|
~ with the License. You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing,
|
||||||
|
~ software distributed under the License is distributed on an
|
||||||
|
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
~ KIND, either express or implied. See the License for the
|
||||||
|
~ specific language governing permissions and limitations
|
||||||
|
~ under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<groupId>org.apache.druid.extensions</groupId>
|
||||||
|
<artifactId>druid-aws-rds-extensions</artifactId>
|
||||||
|
<name>druid-aws-rds-extensions</name>
|
||||||
|
<description>druid-aws-rds-extensions</description>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>org.apache.druid</groupId>
|
||||||
|
<artifactId>druid</artifactId>
|
||||||
|
<version>0.21.0-SNAPSHOT</version>
|
||||||
|
<relativePath>../../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.druid</groupId>
|
||||||
|
<artifactId>druid-core</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.amazonaws</groupId>
|
||||||
|
<artifactId>aws-java-sdk-rds</artifactId>
|
||||||
|
<version>${aws.sdk.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.inject</groupId>
|
||||||
|
<artifactId>guice</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.amazonaws</groupId>
|
||||||
|
<artifactId>aws-java-sdk-core</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-annotations</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.druid.aws.rds;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.Module;
|
||||||
|
import com.fasterxml.jackson.databind.jsontype.NamedType;
|
||||||
|
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.inject.Binder;
|
||||||
|
import org.apache.druid.initialization.DruidModule;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class AWSRDSModule implements DruidModule
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public List<? extends Module> getJacksonModules()
|
||||||
|
{
|
||||||
|
return ImmutableList.of(
|
||||||
|
new SimpleModule("DruidAwsRdsExtentionsModule").registerSubtypes(
|
||||||
|
new NamedType(AWSRDSTokenPasswordProvider.class, "aws-rds-token")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(Binder binder)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.druid.aws.rds;
|
||||||
|
|
||||||
|
import com.amazonaws.auth.AWSCredentialsProvider;
|
||||||
|
import com.amazonaws.services.rds.auth.GetIamAuthTokenRequest;
|
||||||
|
import com.amazonaws.services.rds.auth.RdsIamAuthTokenGenerator;
|
||||||
|
import com.fasterxml.jackson.annotation.JacksonInject;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import org.apache.druid.java.util.common.RE;
|
||||||
|
import org.apache.druid.java.util.common.logger.Logger;
|
||||||
|
import org.apache.druid.metadata.PasswordProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the AWS token same as aws cli
|
||||||
|
* aws rds generate-db-auth-token --hostname HOST --port PORT --region REGION --username USER
|
||||||
|
* and returns that as password.
|
||||||
|
*
|
||||||
|
* Before using this, please make sure that you have connected all dots for db user to connect using token.
|
||||||
|
* See https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/UsingWithRDS.IAMDBAuth.html
|
||||||
|
*/
|
||||||
|
public class AWSRDSTokenPasswordProvider implements PasswordProvider
|
||||||
|
{
|
||||||
|
private static final Logger LOGGER = new Logger(AWSRDSTokenPasswordProvider.class);
|
||||||
|
private final String user;
|
||||||
|
private final String host;
|
||||||
|
private final int port;
|
||||||
|
private final String region;
|
||||||
|
|
||||||
|
private final AWSCredentialsProvider awsCredentialsProvider;
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
public AWSRDSTokenPasswordProvider(
|
||||||
|
@JsonProperty("user") String user,
|
||||||
|
@JsonProperty("host") String host,
|
||||||
|
@JsonProperty("port") int port,
|
||||||
|
@JsonProperty("region") String region,
|
||||||
|
@JacksonInject AWSCredentialsProvider awsCredentialsProvider
|
||||||
|
)
|
||||||
|
{
|
||||||
|
this.user = Preconditions.checkNotNull(user, "null metadataStorage user");
|
||||||
|
this.host = Preconditions.checkNotNull(host, "null metadataStorage host");
|
||||||
|
Preconditions.checkArgument(port > 0, "must provide port");
|
||||||
|
this.port = port;
|
||||||
|
|
||||||
|
this.region = Preconditions.checkNotNull(region, "null region");
|
||||||
|
|
||||||
|
LOGGER.info("AWS RDS Config user[%s], host[%s], port[%d], region[%s]", this.user, this.host, port, this.region);
|
||||||
|
this.awsCredentialsProvider = Preconditions.checkNotNull(awsCredentialsProvider, "null AWSCredentialsProvider");
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
public String getUser()
|
||||||
|
{
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
public String getHost()
|
||||||
|
{
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
public int getPort()
|
||||||
|
{
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
public String getRegion()
|
||||||
|
{
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@Override
|
||||||
|
public String getPassword()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
RdsIamAuthTokenGenerator generator = RdsIamAuthTokenGenerator
|
||||||
|
.builder()
|
||||||
|
.credentials(awsCredentialsProvider)
|
||||||
|
.region(region)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
String authToken = generator.getAuthToken(
|
||||||
|
GetIamAuthTokenRequest
|
||||||
|
.builder()
|
||||||
|
.hostname(host)
|
||||||
|
.port(port)
|
||||||
|
.userName(user)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
return authToken;
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
LOGGER.error(ex, "Couldn't generate AWS token.");
|
||||||
|
throw new RE(ex, "Couldn't generate AWS token.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
# contributor license agreements. See the NOTICE file distributed with
|
||||||
|
# this work for additional information regarding copyright ownership.
|
||||||
|
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
# (the "License"); you may not use this file except in compliance with
|
||||||
|
# the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
org.apache.druid.aws.rds.AWSRDSModule
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.druid.aws.rds;
|
||||||
|
|
||||||
|
import com.amazonaws.auth.AWSCredentials;
|
||||||
|
import com.amazonaws.auth.AWSCredentialsProvider;
|
||||||
|
import com.fasterxml.jackson.databind.InjectableValues;
|
||||||
|
import com.fasterxml.jackson.databind.Module;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.apache.druid.metadata.PasswordProvider;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class AWSRDSTokenPasswordProviderTest
|
||||||
|
{
|
||||||
|
@Test
|
||||||
|
public void testSerde() throws IOException
|
||||||
|
{
|
||||||
|
ObjectMapper jsonMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
for (Module module : new AWSRDSModule().getJacksonModules()) {
|
||||||
|
jsonMapper.registerModule(module);
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonMapper.setInjectableValues(
|
||||||
|
new InjectableValues.Std().addValue(AWSCredentialsProvider.class, new AWSCredentialsProvider()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public AWSCredentials getCredentials()
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void refresh()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
String jsonStr = "{\n"
|
||||||
|
+ " \"type\": \"aws-rds-token\",\n"
|
||||||
|
+ " \"user\": \"testuser\",\n"
|
||||||
|
+ " \"host\": \"testhost\",\n"
|
||||||
|
+ " \"port\": 5273,\n"
|
||||||
|
+ " \"region\": \"testregion\"\n"
|
||||||
|
+ "}\n";
|
||||||
|
|
||||||
|
PasswordProvider pp = jsonMapper.readValue(
|
||||||
|
jsonMapper.writeValueAsString(
|
||||||
|
jsonMapper.readValue(jsonStr, PasswordProvider.class)
|
||||||
|
),
|
||||||
|
PasswordProvider.class
|
||||||
|
);
|
||||||
|
|
||||||
|
AWSRDSTokenPasswordProvider awsPwdProvider = (AWSRDSTokenPasswordProvider) pp;
|
||||||
|
Assert.assertEquals("testuser", awsPwdProvider.getUser());
|
||||||
|
Assert.assertEquals("testhost", awsPwdProvider.getHost());
|
||||||
|
Assert.assertEquals(5273, awsPwdProvider.getPort());
|
||||||
|
Assert.assertEquals("testregion", awsPwdProvider.getRegion());
|
||||||
|
}
|
||||||
|
}
|
|
@ -147,6 +147,16 @@ source_paths:
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
name: AWS RDS SDK for Java
|
||||||
|
license_category: source
|
||||||
|
module: extensions/druid-aws-rds-extensions
|
||||||
|
license_name: Apache License version 2.0
|
||||||
|
version: 1.11.199
|
||||||
|
libraries:
|
||||||
|
- com.amazonaws: aws-java-sdk-rds
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
name: LDAP string encoding function from OWASP ESAPI
|
name: LDAP string encoding function from OWASP ESAPI
|
||||||
license_category: source
|
license_category: source
|
||||||
module: extensions/druid-basic-security
|
module: extensions/druid-basic-security
|
||||||
|
|
4
pom.xml
4
pom.xml
|
@ -112,6 +112,7 @@
|
||||||
<powermock.version>2.0.2</powermock.version>
|
<powermock.version>2.0.2</powermock.version>
|
||||||
<aws.sdk.version>1.11.199</aws.sdk.version>
|
<aws.sdk.version>1.11.199</aws.sdk.version>
|
||||||
<caffeine.version>2.8.0</caffeine.version>
|
<caffeine.version>2.8.0</caffeine.version>
|
||||||
|
<jacoco.version>0.8.5</jacoco.version>
|
||||||
<!-- Curator requires 3.4.x ZooKeeper clients to maintain compatibility with 3.4.x ZooKeeper servers,
|
<!-- Curator requires 3.4.x ZooKeeper clients to maintain compatibility with 3.4.x ZooKeeper servers,
|
||||||
If we upgrade to 3.5.x clients, curator requires 3.5.x servers, which would break backwards compatibility
|
If we upgrade to 3.5.x clients, curator requires 3.5.x servers, which would break backwards compatibility
|
||||||
see http://curator.apache.org/zk-compatibility.html -->
|
see http://curator.apache.org/zk-compatibility.html -->
|
||||||
|
@ -168,6 +169,7 @@
|
||||||
<module>extensions-core/lookups-cached-single</module>
|
<module>extensions-core/lookups-cached-single</module>
|
||||||
<module>extensions-core/ec2-extensions</module>
|
<module>extensions-core/ec2-extensions</module>
|
||||||
<module>extensions-core/s3-extensions</module>
|
<module>extensions-core/s3-extensions</module>
|
||||||
|
<module>extensions-core/druid-aws-rds-extensions</module>
|
||||||
<module>extensions-core/simple-client-sslcontext</module>
|
<module>extensions-core/simple-client-sslcontext</module>
|
||||||
<module>extensions-core/druid-basic-security</module>
|
<module>extensions-core/druid-basic-security</module>
|
||||||
<module>extensions-core/google-extensions</module>
|
<module>extensions-core/google-extensions</module>
|
||||||
|
@ -1269,7 +1271,7 @@
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.jacoco</groupId>
|
<groupId>org.jacoco</groupId>
|
||||||
<artifactId>jacoco-maven-plugin</artifactId>
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
<version>0.8.5</version>
|
<version>${jacoco.version}</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<excludes>
|
<excludes>
|
||||||
<!-- Ignore initialization classes, these are tested by the integration tests. -->
|
<!-- Ignore initialization classes, these are tested by the integration tests. -->
|
||||||
|
|
|
@ -459,6 +459,17 @@
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jacoco</groupId>
|
||||||
|
<artifactId>jacoco-maven-plugin</artifactId>
|
||||||
|
<version>${jacoco.version}</version>
|
||||||
|
<configuration>
|
||||||
|
<excludes>
|
||||||
|
<!-- There are UTs for this class but it is very difficult to get required branch coverage -->
|
||||||
|
<exclude>org/apache/druid/metadata/BasicDataSourceExt.class</exclude>
|
||||||
|
</excludes>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
|
|
@ -0,0 +1,179 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.druid.metadata;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import org.apache.commons.dbcp2.BasicDataSource;
|
||||||
|
import org.apache.commons.dbcp2.ConnectionFactory;
|
||||||
|
import org.apache.druid.java.util.common.RE;
|
||||||
|
import org.apache.druid.java.util.common.logger.Logger;
|
||||||
|
|
||||||
|
import java.sql.Driver;
|
||||||
|
import java.sql.DriverManager;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class exists so that {@link MetadataStorageConnectorConfig} is asked for password every time a brand new
|
||||||
|
* connection is established with DB. {@link PasswordProvider} impls such as those based on AWS tokens refresh the
|
||||||
|
* underlying token periodically since each token is valid for a certain period of time only.
|
||||||
|
* So, This class overrides (and largely copies due to lack of extensibility), the methods from base class in order to keep
|
||||||
|
* track of connection properties and call {@link MetadataStorageConnectorConfig#getPassword()} everytime a new
|
||||||
|
* connection is setup.
|
||||||
|
*/
|
||||||
|
public class BasicDataSourceExt extends BasicDataSource
|
||||||
|
{
|
||||||
|
private static final Logger LOGGER = new Logger(BasicDataSourceExt.class);
|
||||||
|
|
||||||
|
private Properties connectionProperties;
|
||||||
|
private final MetadataStorageConnectorConfig connectorConfig;
|
||||||
|
|
||||||
|
public BasicDataSourceExt(MetadataStorageConnectorConfig connectorConfig)
|
||||||
|
{
|
||||||
|
this.connectorConfig = connectorConfig;
|
||||||
|
this.connectionProperties = new Properties();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addConnectionProperty(String name, String value)
|
||||||
|
{
|
||||||
|
connectionProperties.put(name, value);
|
||||||
|
super.addConnectionProperty(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeConnectionProperty(String name)
|
||||||
|
{
|
||||||
|
connectionProperties.remove(name);
|
||||||
|
super.removeConnectionProperty(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setConnectionProperties(String connectionProperties)
|
||||||
|
{
|
||||||
|
if (connectionProperties == null) {
|
||||||
|
throw new NullPointerException("connectionProperties is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] entries = connectionProperties.split(";");
|
||||||
|
Properties properties = new Properties();
|
||||||
|
for (String entry : entries) {
|
||||||
|
if (entry.length() > 0) {
|
||||||
|
int index = entry.indexOf('=');
|
||||||
|
if (index > 0) {
|
||||||
|
String name = entry.substring(0, index);
|
||||||
|
String value = entry.substring(index + 1);
|
||||||
|
properties.setProperty(name, value);
|
||||||
|
} else {
|
||||||
|
// no value is empty string which is how java.util.Properties works
|
||||||
|
properties.setProperty(entry, "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.connectionProperties = properties;
|
||||||
|
super.setConnectionProperties(connectionProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public Properties getConnectionProperties()
|
||||||
|
{
|
||||||
|
return connectionProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ConnectionFactory createConnectionFactory() throws SQLException
|
||||||
|
{
|
||||||
|
Driver driverToUse = getDriver();
|
||||||
|
|
||||||
|
if (driverToUse == null) {
|
||||||
|
Class<?> driverFromCCL = null;
|
||||||
|
if (getDriverClassName() != null) {
|
||||||
|
try {
|
||||||
|
try {
|
||||||
|
if (getDriverClassLoader() == null) {
|
||||||
|
driverFromCCL = Class.forName(getDriverClassName());
|
||||||
|
} else {
|
||||||
|
driverFromCCL = Class.forName(
|
||||||
|
getDriverClassName(), true, getDriverClassLoader());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException cnfe) {
|
||||||
|
driverFromCCL = Thread.currentThread(
|
||||||
|
).getContextClassLoader().loadClass(
|
||||||
|
getDriverClassName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception t) {
|
||||||
|
String message = "Cannot load JDBC driver class '" +
|
||||||
|
getDriverClassName() + "'";
|
||||||
|
LOGGER.error(t, message);
|
||||||
|
throw new SQLException(message, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (driverFromCCL == null) {
|
||||||
|
driverToUse = DriverManager.getDriver(getUrl());
|
||||||
|
} else {
|
||||||
|
// Usage of DriverManager is not possible, as it does not
|
||||||
|
// respect the ContextClassLoader
|
||||||
|
// N.B. This cast may cause ClassCastException which is handled below
|
||||||
|
driverToUse = (Driver) driverFromCCL.newInstance();
|
||||||
|
if (!driverToUse.acceptsURL(getUrl())) {
|
||||||
|
throw new SQLException("No suitable driver", "08001");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception t) {
|
||||||
|
String message = "Cannot create JDBC driver of class '" +
|
||||||
|
(getDriverClassName() != null ? getDriverClassName() : "") +
|
||||||
|
"' for connect URL '" + getUrl() + "'";
|
||||||
|
LOGGER.error(t, message);
|
||||||
|
throw new SQLException(message, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (driverToUse == null) {
|
||||||
|
throw new RE("Failed to find the DB Driver");
|
||||||
|
}
|
||||||
|
|
||||||
|
final Driver finalDriverToUse = driverToUse;
|
||||||
|
|
||||||
|
return () -> {
|
||||||
|
String user = connectorConfig.getUser();
|
||||||
|
if (user != null) {
|
||||||
|
connectionProperties.put("user", user);
|
||||||
|
} else {
|
||||||
|
log("DBCP DataSource configured without a 'username'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: This is the main point of this class where we are getting fresh password before setting up
|
||||||
|
// every new connection.
|
||||||
|
String password = connectorConfig.getPassword();
|
||||||
|
if (password != null) {
|
||||||
|
connectionProperties.put("password", password);
|
||||||
|
} else {
|
||||||
|
log("DBCP DataSource configured without a 'password'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalDriverToUse.connect(connectorConfig.getConnectURI(), connectionProperties);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -64,7 +64,7 @@ public abstract class SQLFirehoseDatabaseConnector
|
||||||
|
|
||||||
protected BasicDataSource getDatasource(MetadataStorageConnectorConfig connectorConfig)
|
protected BasicDataSource getDatasource(MetadataStorageConnectorConfig connectorConfig)
|
||||||
{
|
{
|
||||||
BasicDataSource dataSource = new BasicDataSource();
|
BasicDataSource dataSource = new BasicDataSourceExt(connectorConfig);
|
||||||
dataSource.setUsername(connectorConfig.getUser());
|
dataSource.setUsername(connectorConfig.getUser());
|
||||||
dataSource.setPassword(connectorConfig.getPassword());
|
dataSource.setPassword(connectorConfig.getPassword());
|
||||||
String uri = connectorConfig.getConnectURI();
|
String uri = connectorConfig.getConnectURI();
|
||||||
|
|
|
@ -654,7 +654,7 @@ public abstract class SQLMetadataConnector implements MetadataStorageConnector
|
||||||
if (dbcpProperties != null) {
|
if (dbcpProperties != null) {
|
||||||
dataSource = BasicDataSourceFactory.createDataSource(dbcpProperties);
|
dataSource = BasicDataSourceFactory.createDataSource(dbcpProperties);
|
||||||
} else {
|
} else {
|
||||||
dataSource = new BasicDataSource();
|
dataSource = new BasicDataSourceExt(connectorConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.apache.druid.metadata;
|
||||||
|
|
||||||
|
import org.apache.commons.dbcp2.ConnectionFactory;
|
||||||
|
import org.assertj.core.util.Lists;
|
||||||
|
import org.easymock.Capture;
|
||||||
|
import org.easymock.EasyMock;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.sql.Driver;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
public class BasicDataSourceExtTest
|
||||||
|
{
|
||||||
|
@Test
|
||||||
|
public void testCreateConnectionFactory() throws Exception
|
||||||
|
{
|
||||||
|
MetadataStorageConnectorConfig connectorConfig = new MetadataStorageConnectorConfig()
|
||||||
|
{
|
||||||
|
private final List<String> passwords = Lists.newArrayList("pwd1", "pwd2");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUser()
|
||||||
|
{
|
||||||
|
return "testuser";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPassword()
|
||||||
|
{
|
||||||
|
return passwords.remove(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
BasicDataSourceExt basicDataSourceExt = new BasicDataSourceExt(connectorConfig);
|
||||||
|
|
||||||
|
basicDataSourceExt.setConnectionProperties("p1=v1");
|
||||||
|
basicDataSourceExt.addConnectionProperty("p2", "v2");
|
||||||
|
|
||||||
|
Driver driver = EasyMock.mock(Driver.class);
|
||||||
|
Capture<String> uriArg = Capture.newInstance();
|
||||||
|
Capture<Properties> propsArg = Capture.newInstance();
|
||||||
|
EasyMock.expect(driver.connect(EasyMock.capture(uriArg), EasyMock.capture(propsArg))).andReturn(null).times(2);
|
||||||
|
EasyMock.replay(driver);
|
||||||
|
|
||||||
|
basicDataSourceExt.setDriver(driver);
|
||||||
|
|
||||||
|
ConnectionFactory connectionFactory = basicDataSourceExt.createConnectionFactory();
|
||||||
|
|
||||||
|
Properties expectedProps = new Properties();
|
||||||
|
expectedProps.put("p1", "v1");
|
||||||
|
expectedProps.put("p2", "v2");
|
||||||
|
expectedProps.put("user", connectorConfig.getUser());
|
||||||
|
|
||||||
|
|
||||||
|
Assert.assertNull(connectionFactory.createConnection());
|
||||||
|
Assert.assertEquals(connectorConfig.getConnectURI(), uriArg.getValue());
|
||||||
|
|
||||||
|
expectedProps.put("password", "pwd1");
|
||||||
|
Assert.assertEquals(expectedProps, propsArg.getValue());
|
||||||
|
|
||||||
|
Assert.assertNull(connectionFactory.createConnection());
|
||||||
|
Assert.assertEquals(connectorConfig.getConnectURI(), uriArg.getValue());
|
||||||
|
|
||||||
|
expectedProps.put("password", "pwd2");
|
||||||
|
Assert.assertEquals(expectedProps, propsArg.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConnectionPropertiesHanding()
|
||||||
|
{
|
||||||
|
BasicDataSourceExt basicDataSourceExt = new BasicDataSourceExt(EasyMock.mock(MetadataStorageConnectorConfig.class));
|
||||||
|
Properties expectedProps = new Properties();
|
||||||
|
|
||||||
|
basicDataSourceExt.setConnectionProperties("");
|
||||||
|
Assert.assertEquals(expectedProps, basicDataSourceExt.getConnectionProperties());
|
||||||
|
|
||||||
|
basicDataSourceExt.setConnectionProperties("p0;p1=v1;p2=v2;p3=v3");
|
||||||
|
basicDataSourceExt.addConnectionProperty("p4", "v4");
|
||||||
|
basicDataSourceExt.addConnectionProperty("p5", "v5");
|
||||||
|
basicDataSourceExt.removeConnectionProperty("p2");
|
||||||
|
basicDataSourceExt.removeConnectionProperty("p5");
|
||||||
|
|
||||||
|
expectedProps.put("p0", "");
|
||||||
|
expectedProps.put("p1", "v1");
|
||||||
|
expectedProps.put("p3", "v3");
|
||||||
|
expectedProps.put("p4", "v4");
|
||||||
|
|
||||||
|
Assert.assertEquals(expectedProps, basicDataSourceExt.getConnectionProperties());
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -88,6 +88,7 @@ HLL
|
||||||
HashSet
|
HashSet
|
||||||
Homebrew
|
Homebrew
|
||||||
HyperLogLog
|
HyperLogLog
|
||||||
|
IAM
|
||||||
IANA
|
IANA
|
||||||
IETF
|
IETF
|
||||||
IP
|
IP
|
||||||
|
@ -152,6 +153,7 @@ ParseSpecs
|
||||||
Protobuf
|
Protobuf
|
||||||
RDBMS
|
RDBMS
|
||||||
RDDs
|
RDDs
|
||||||
|
RDS
|
||||||
Rackspace
|
Rackspace
|
||||||
Redis
|
Redis
|
||||||
S3
|
S3
|
||||||
|
@ -872,6 +874,7 @@ DistinctCount
|
||||||
artifactId
|
artifactId
|
||||||
com.example
|
com.example
|
||||||
common.runtime.properties
|
common.runtime.properties
|
||||||
|
druid-aws-rds-extensions
|
||||||
druid-cassandra-storage
|
druid-cassandra-storage
|
||||||
druid-distinctcount
|
druid-distinctcount
|
||||||
druid-ec2-extensions
|
druid-ec2-extensions
|
||||||
|
|
Loading…
Reference in New Issue