Skip to content

Commit 0f3e2c6

Browse files
committed
* db: support gcloud postgres IAM auth
Signed-off-by: neo <1100909+neowu@users.noreply.github.com>
1 parent 5a9050f commit 0f3e2c6

File tree

9 files changed

+38
-12
lines changed

9 files changed

+38
-12
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* db: better support PostgreSQL
1010
> support UUID type
1111
> support JSONB type, use "?::jsonb" instead of "?" for jsonb query
12+
> support gcloud IAM auth
1213
1314
### 9.2.1 (4/24/2025 - 5/19/2025) !!! only support java 24
1415

core-ng/src/main/java/core/framework/db/CloudAuthProvider.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package core.framework.db;
22

3+
import core.framework.internal.db.Dialect;
4+
35
/**
46
* @author neo
57
*/
68
public interface CloudAuthProvider {
7-
String user();
9+
String user(Dialect dialect);
810

911
String accessToken();
1012

core-ng/src/main/java/core/framework/internal/db/DatabaseImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ private Connection createConnection() {
9090
}
9191
if (authProvider != null) {
9292
// properties are thread safe, it's ok to set user/password with multiple threads
93-
driverProperties.setProperty("user", authProvider.user());
93+
driverProperties.setProperty("user", authProvider.user(operation.dialect));
9494
driverProperties.setProperty("password", authProvider.accessToken());
9595
}
9696
Connection connection = null;

core-ng/src/main/java/core/framework/internal/db/DatabaseOperation.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,11 @@ private <T> List<T> fetch(PreparedStatement statement, RowMapper<T> mapper) thro
179179
}
180180
}
181181

182-
// the LAST_INSERT_ID() function of mysql returns BIGINT, so here it uses Long
182+
// MySQL:
183+
// the LAST_INSERT_ID() function returns BIGINT, so here it uses Long
183184
// http://dev.mysql.com/doc/refman/5.7/en/information-functions.html
185+
// PostgreSQL:
186+
// SERIAL type maps to int type, BIGSERIAL maps to Long
184187
private OptionalLong fetchGeneratedKey(PreparedStatement statement) throws SQLException {
185188
try (ResultSet keys = statement.getGeneratedKeys()) {
186189
if (keys != null && keys.next()) {

core-ng/src/main/java/core/framework/internal/db/cloud/AzureAuthProvider.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import core.framework.http.HTTPMethod;
66
import core.framework.http.HTTPRequest;
77
import core.framework.http.HTTPResponse;
8+
import core.framework.internal.db.Dialect;
89
import core.framework.util.Files;
910
import core.framework.util.Strings;
1011

@@ -43,7 +44,7 @@ public AzureAuthProvider(String user) {
4344
}
4445

4546
@Override
46-
public String user() {
47+
public String user(Dialect dialect) {
4748
return user;
4849
}
4950

core-ng/src/main/java/core/framework/internal/db/cloud/GCloudAuthProvider.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import core.framework.http.HTTPMethod;
66
import core.framework.http.HTTPRequest;
77
import core.framework.http.HTTPResponse;
8+
import core.framework.internal.db.Dialect;
9+
import core.framework.util.Strings;
810

911
import java.time.Duration;
1012

@@ -25,10 +27,10 @@ public final class GCloudAuthProvider implements CloudAuthProvider {
2527
long expirationTime;
2628

2729
@Override
28-
public String user() {
30+
public String user(Dialect dialect) {
2931
// email won't change once pod created
3032
if (user == null) {
31-
user = parseUser(metadata("email"));
33+
user = parseUser(dialect, metadata("email"));
3234
}
3335
return user;
3436
}
@@ -48,9 +50,16 @@ public String accessToken() {
4850
return accessToken;
4951
}
5052

51-
private String parseUser(String email) {
52-
int index = email.indexOf('@');
53-
return email.substring(0, index);
53+
// MySQL: strip entire domain
54+
// PostgreSQL: strip ".gserviceaccount.com", refer to https://cloud.google.com/sql/docs/postgres/add-manage-iam-users
55+
private String parseUser(Dialect dialect, String email) {
56+
if (dialect == Dialect.MYSQL) {
57+
int index = email.indexOf('@');
58+
return email.substring(0, index);
59+
} else if (dialect == Dialect.POSTGRESQL && email.endsWith(".gserviceaccount.com")) {
60+
return email.substring(0, email.length() - 20); // remove ".gserviceaccount.com"
61+
}
62+
throw new Error(Strings.format("unsupported gcloud iam user, dialect={}, email={}", dialect, email));
5463
}
5564

5665
private String parseAccessToken(String tokenJSON) {

core-ng/src/test/java/core/framework/internal/db/cloud/AzureAuthProviderTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package core.framework.internal.db.cloud;
22

33
import core.framework.http.HTTPRequest;
4+
import core.framework.internal.db.Dialect;
45
import core.framework.util.Strings;
56
import org.junit.jupiter.api.BeforeEach;
67
import org.junit.jupiter.api.Test;
@@ -24,7 +25,7 @@ void createAzureAuthProvider() {
2425

2526
@Test
2627
void user() {
27-
assertThat(provider.user())
28+
assertThat(provider.user(Dialect.MYSQL))
2829
.isEqualTo("some-service");
2930
}
3031

core-ng/src/test/java/core/framework/internal/db/cloud/GCloudAuthProviderTest.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package core.framework.internal.db.cloud;
22

3+
import core.framework.internal.db.Dialect;
34
import org.junit.jupiter.api.BeforeEach;
45
import org.junit.jupiter.api.Test;
56

@@ -27,11 +28,18 @@ void createCloudAuthProvider() {
2728

2829
@Test
2930
void user() {
30-
assertThat(provider.user())
31+
assertThat(provider.user(Dialect.MYSQL))
3132
.isEqualTo("lab-customer-service")
3233
.isEqualTo(provider.user);
3334
}
3435

36+
@Test
37+
void userWithPosgreSQL() {
38+
assertThat(provider.user(Dialect.POSTGRESQL))
39+
.isEqualTo("lab-customer-service@lab.iam")
40+
.isEqualTo(provider.user);
41+
}
42+
3543
@Test
3644
void accessToken() {
3745
assertThat(provider.accessToken())

core-ng/src/test/java/core/framework/module/DBConfigTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package core.framework.module;
22

3+
import core.framework.internal.db.Dialect;
34
import core.framework.internal.db.cloud.AzureAuthProvider;
45
import core.framework.internal.db.cloud.GCloudAuthProvider;
56
import core.framework.internal.module.ModuleContext;
@@ -35,7 +36,7 @@ void validate() {
3536
void provider() {
3637
assertThat(config.provider("iam/azure/some-service"))
3738
.isInstanceOf(AzureAuthProvider.class)
38-
.satisfies(provider -> assertThat(provider.user()).isEqualTo("some-service"));
39+
.satisfies(provider -> assertThat(provider.user(Dialect.MYSQL)).isEqualTo("some-service"));
3940
assertThatThrownBy(() -> config.provider("iam/azure/")).hasMessageStartingWith("invalid azure iam user");
4041

4142
assertThat(config.provider("iam/gcloud")).isInstanceOf(GCloudAuthProvider.class);

0 commit comments

Comments
 (0)