Skip to content

Commit

Permalink
Merge 8.0 (#103)
Browse files Browse the repository at this point in the history
* fix: remove db password from logs (#89)

* fix: remove db password from logs

* fix: mask db password

* fix: Add tests

* fix: Add more tests

* fix: PR changes

* fix: tests for connection pool (#90)

* adding dev-v5.0.6 tag to this commit to ensure building

* fix: cicd tests (#92)

* fix: logging test (#93)

* adding dev-v5.0.6 tag to this commit to ensure building

* fix: flaky test (#94)

* fix: make connection pool test less flaky

* fix: test

* fix: typo

* adding dev-v5.0.6 tag to this commit to ensure building

* fix: adds idle timeout and minimum idle configs (#95)

* fix: idle connection configs

* test: protected configs

* adding dev-v5.0.6 tag to this commit to ensure building

* fix: cicd (#96)

* fix: cicd

* fix: test

* adding dev-v5.0.6 tag to this commit to ensure building

* fix: typo (#97)

* adding dev-v5.0.6 tag to this commit to ensure building

* fixes tests

* adding dev-v5.0.6 tag to this commit to ensure building

* fix: Vulnerability fix (#98)

* fix: updated dependencies

* chore: version and changelog

* fix: versions

* adding dev-v5.0.7 tag to this commit to ensure building

* fix: dependencies (#101)

* fix: dependency fix

* fix: dep fix

* adding dev-v5.0.7 tag to this commit to ensure building

* fix: fixes storage handling for non-auth recipes (#102)

* fix: non-auth fix

* fix: migration script and test fixes

* fix: plugin interface version

* fix: remove unnecessary func

* adding dev-v6.0.0 tag to this commit to ensure building

---------

Co-authored-by: Ankit Tiwari <[email protected]>
Co-authored-by: rishabhpoddar <[email protected]>
  • Loading branch information
3 people authored Mar 11, 2024
1 parent d20f267 commit aacd53d
Show file tree
Hide file tree
Showing 19 changed files with 1,098 additions and 48 deletions.
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,31 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- This enables smooth switching between `useDynamicAccessTokenSigningKey` settings by allowing refresh calls to
change the signing key type of a session

## [6.0.0] - 2024-03-05

- Implements `deleteAllUserRoleAssociationsForRole`
- Drops `(app_id, role)` foreign key constraint on `user_roles` table

### Migration

```sql
ALTER TABLE user_roles DROP FOREIGN KEY user_roles_ibfk_1;
ALTER TABLE user_roles DROP FOREIGN KEY user_roles_ibfk_2;
ALTER TABLE user_roles
ADD FOREIGN KEY (app_id, tenant_id)
REFERENCES tenants (app_id, tenant_id) ON DELETE CASCADE;
```

## [5.0.7] - 2024-02-19

- Fixes vulnerabilities in dependencies

## [5.0.6] - 2024-01-25

- Fixes the issue where passwords were inadvertently logged in the logs.
- Adds tests to check connection pool behaviour.
- Adds `mysql_idle_connection_timeout` and `mysql_minimum_idle_connections` configs to control active connections to the database.

## [5.0.5] - 2023-12-06

- Validates db config types in `canBeUsed` function
Expand Down
16 changes: 8 additions & 8 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ plugins {
id 'java-library'
}

version = "5.0.5"
version = "6.0.0"

repositories {
mavenCentral()
Expand All @@ -20,7 +20,7 @@ dependencies {
implementation group: 'org.mariadb.jdbc', name: 'mariadb-java-client', version: '2.6.0'

// https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
compileOnly group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
compileOnly group: 'ch.qos.logback', name: 'logback-classic', version: '1.4.14'

// https://mvnrepository.com/artifact/com.google.code.gson/gson
compileOnly group: 'com.google.code.gson', name: 'gson', version: '2.3.1'
Expand All @@ -32,32 +32,32 @@ dependencies {
compileOnly group: 'org.jetbrains', name: 'annotations', version: '13.0'

// https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml
compileOnly group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.14.0'
compileOnly group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.16.1'

// https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core
compileOnly group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.14.0'
compileOnly group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.16.1'

testImplementation 'junit:junit:4.12'

// https://mvnrepository.com/artifact/org.mockito/mockito-core
testImplementation group: 'org.mockito', name: 'mockito-core', version: '3.1.0'

// https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core
testImplementation group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '10.1.1'
testImplementation group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '10.1.18'

// https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
testImplementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
testImplementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.4.14'

// https://mvnrepository.com/artifact/com.google.code.gson/gson
testImplementation group: 'com.google.code.gson', name: 'gson', version: '2.3.1'

testImplementation 'com.tngtech.archunit:archunit-junit4:0.22.0'

// https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformat-yaml
testImplementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.14.0'
testImplementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.16.1'

// https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core
testImplementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.14.0'
testImplementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.16.1'
}

jar {
Expand Down
11 changes: 10 additions & 1 deletion config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,13 @@ mysql_config_version: 0

# (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: "thirdparty_users") string value. Specify the name of the table that will
# store the thirdparty recipe users.
# mysql_thirdparty_users_table_name
# mysql_thirdparty_users_table_name


# (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: 60000) long value. Timeout in milliseconds for the idle connections
# to be closed.
# mysql_idle_connection_timeout:

# (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: null) integer value. Minimum number of idle connections to be kept
# active. If not set, minimum idle connections will be same as the connection pool size.
# mysql_minimum_idle_connections:
11 changes: 10 additions & 1 deletion devConfig.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,13 @@ mysql_password: "root"

# (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: "thirdparty_users") string value. Specify the name of the table that will
# store the thirdparty recipe users.
# mysql_thirdparty_users_table_name
# mysql_thirdparty_users_table_name


# (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: 60000) long value. Timeout in milliseconds for the idle connections
# to be closed.
# mysql_idle_connection_timeout:

# (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: 1) integer value. Minimum number of idle connections to be kept
# active. If not set, minimum idle connections will be same as the connection pool size.
# mysql_minimum_idle_connections:
6 changes: 3 additions & 3 deletions implementationDependencies.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
"src": "https://repo1.maven.org/maven2/com/zaxxer/HikariCP/3.4.1/HikariCP-3.4.1-sources.jar"
},
{
"jar": "https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar",
"name": "SLF4j API 1.7.25",
"src": "https://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25-sources.jar"
"jar": "https://repo1.maven.org/maven2/org/slf4j/slf4j-api/2.0.7/slf4j-api-2.0.7.jar",
"name": "SLF4j API 2.0.7",
"src": "https://repo1.maven.org/maven2/org/slf4j/slf4j-api/2.0.7/slf4j-api-2.0.7-sources.jar"
}
]
}
Binary file not shown.
2 changes: 1 addition & 1 deletion pluginInterfaceSupported.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"_comment": "contains a list of plugin interfaces branch names that this core supports",
"versions": [
"4.0"
"5.0"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ private synchronized void initialiseHikariDataSource() throws SQLException {
}
config.setMaximumPoolSize(userConfig.getConnectionPoolSize());
config.setConnectionTimeout(5000);
if (userConfig.getMinimumIdleConnections() != null) {
config.setMinimumIdle(userConfig.getMinimumIdleConnections());
config.setIdleTimeout(userConfig.getIdleConnectionTimeout());
}
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("prepStmtCacheSize", "250");
config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
Expand Down Expand Up @@ -129,7 +133,7 @@ static boolean isAlreadyInitialised(Start start) {
}

static void initPool(Start start, boolean shouldWait) throws DbInitException, SQLException {
if (isAlreadyInitialised(start)) {
if (isAlreadyInitialised(start)) {
return;
}
Logging.info(start, "Setting up MySQL connection pool.", true);
Expand Down
34 changes: 29 additions & 5 deletions src/main/java/io/supertokens/storage/mysql/Start.java
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@
import java.util.List;
import java.util.Set;

import static io.supertokens.storage.mysql.QueryExecutorTemplate.execute;
import static io.supertokens.storage.mysql.QueryExecutorTemplate.update;


public class Start
implements SessionSQLStorage, EmailPasswordSQLStorage, EmailVerificationSQLStorage, ThirdPartySQLStorage,
JWTRecipeSQLStorage, PasswordlessSQLStorage, UserMetadataSQLStorage, UserRolesSQLStorage, UserIdMappingStorage,
Expand All @@ -112,7 +116,7 @@ public class Start
// SaaS. If the core is not running in SuperTokens SaaS, this array has no effect.
private static final String[] PROTECTED_DB_CONFIG = new String[]{"mysql_connection_pool_size",
"mysql_connection_uri", "mysql_host", "mysql_port", "mysql_user", "mysql_password",
"mysql_database_name"};
"mysql_database_name", "mysql_idle_connection_timeout", "mysql_minimum_idle_connections"};

private static final Object appenderLock = new Object();
public static boolean silent = false;
Expand Down Expand Up @@ -1851,7 +1855,7 @@ public int deleteUserMetadata(AppIdentifier appIdentifier, String userId) throws

@Override
public void addRoleToUser(TenantIdentifier tenantIdentifier, String userId, String role)
throws StorageQueryException, UnknownRoleException, DuplicateUserRoleMappingException,
throws StorageQueryException, DuplicateUserRoleMappingException,
TenantOrAppNotFoundException {
try {
UserRolesQueries.addRoleToUser(this, tenantIdentifier, userId, role);
Expand All @@ -1860,9 +1864,6 @@ public void addRoleToUser(TenantIdentifier tenantIdentifier, String userId, Stri
MySQLConfig config = Config.getConfig(this);
String serverErrorMessage = e.getMessage();

if (isForeignKeyConstraintError(serverErrorMessage, config.getUserRolesTable(), "role")) {
throw new UnknownRoleException();
}
if (isPrimaryKeyError(serverErrorMessage, config.getUserRolesTable())) {
throw new DuplicateUserRoleMappingException();
}
Expand Down Expand Up @@ -1933,6 +1934,16 @@ public boolean deleteRole(AppIdentifier appIdentifier, String role) throws Stora
}
}

@Override
public boolean deleteAllUserRoleAssociationsForRole(AppIdentifier appIdentifier, String role)
throws StorageQueryException {
try {
return UserRolesQueries.deleteAllUserRoleAssociationsForRole(this, appIdentifier, role);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
}

@Override
public String[] getRoles(AppIdentifier appIdentifier) throws StorageQueryException {
try {
Expand Down Expand Up @@ -2966,4 +2977,17 @@ public static void setEnableForDeadlockTesting(boolean value) {
assert(isTesting);
enableForDeadlockTesting = value;
}

@TestOnly
public int getDbActivityCount(String dbname) throws SQLException, StorageQueryException {
String QUERY = "SELECT COUNT(*) as c FROM information_schema.processlist WHERE DB = ?;";
return execute(this, QUERY, pst -> {
pst.setString(1, dbname);
}, result -> {
if (result.next()) {
return result.getInt("c");
}
return -1;
});
}
}
43 changes: 40 additions & 3 deletions src/main/java/io/supertokens/storage/mysql/config/MySQLConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,14 @@ public class MySQLConfig {
@ConnectionPoolProperty
private String mysql_connection_scheme = "mysql";

@JsonProperty
@ConnectionPoolProperty
private long mysql_idle_connection_timeout = 60000;

@JsonProperty
@ConnectionPoolProperty
private Integer mysql_minimum_idle_connections = null;

@IgnoreForAnnotationCheck
boolean isValidAndNormalised = false;

Expand Down Expand Up @@ -236,6 +244,14 @@ public String getThirdPartyUsersTable() {
return mysql_thirdparty_users_table_name;
}

public long getIdleConnectionTimeout() {
return mysql_idle_connection_timeout;
}

public Integer getMinimumIdleConnections() {
return mysql_minimum_idle_connections;
}

public String getThirdPartyUserToTenantTable() {
return addPrefixToTableName("thirdparty_user_to_tenant");
}
Expand Down Expand Up @@ -331,6 +347,19 @@ void validateAndNormalise() throws InvalidConfigException {
"'mysql_connection_pool_size' in the config.yaml file must be > 0");
}

if (mysql_minimum_idle_connections != null) {
if (mysql_minimum_idle_connections < 0) {
throw new InvalidConfigException(
"'mysql_minimum_idle_connections' must be a >= 0");
}

if (mysql_minimum_idle_connections > mysql_connection_pool_size) {
throw new InvalidConfigException(
"'mysql_minimum_idle_connections' must be less than or equal to "
+ "'mysql_connection_pool_size'");
}
}

// Normalisation
if (mysql_connection_uri != null) {
{ // mysql_connection_attributes
Expand Down Expand Up @@ -517,10 +546,18 @@ public String getConnectionPoolId() {
StringBuilder connectionPoolId = new StringBuilder();
for (Field field : MySQLConfig.class.getDeclaredFields()) {
if (field.isAnnotationPresent(ConnectionPoolProperty.class)) {
connectionPoolId.append("|");
try {
if (field.get(this) != null) {
connectionPoolId.append(field.get(this).toString());
String fieldName = field.getName();
String fieldValue = field.get(this) != null ? field.get(this).toString() : null;
if(fieldValue == null) {
continue;
}
// To ensure a unique connectionPoolId we include the database password and use the "|db_pass|" identifier.
// This facilitates easy removal of the password from logs when necessary.
if (fieldName.equals("mysql_password")) {
connectionPoolId.append("|db_pass|" + fieldValue + "|db_pass");
} else {
connectionPoolId.append("|" + fieldValue);
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.LayoutBase;
import io.supertokens.storage.mysql.Start;
import io.supertokens.storage.mysql.utils.Utils;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
Expand Down Expand Up @@ -58,7 +58,7 @@ public String doLayout(ILoggingEvent event) {
sbuf.append(event.getCallerData()[1]);
sbuf.append(" | ");

sbuf.append(event.getFormattedMessage());
sbuf.append(Utils.maskDBPassword(event.getFormattedMessage()));
sbuf.append(CoreConstants.LINE_SEPARATOR);
sbuf.append(CoreConstants.LINE_SEPARATOR);

Expand Down
11 changes: 6 additions & 5 deletions src/main/java/io/supertokens/storage/mysql/output/Logging.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ public class Logging extends ResourceDistributor.SingletonResource {

private Logging(Start start, String infoLogPath, String errorLogPath) {
this.infoLogger = infoLogPath.equals("null")
? createLoggerForConsole(start, "io.supertokens.storage.mysql.Info")
? createLoggerForConsole(start, "io.supertokens.storage.mysql.Info", LOG_LEVEL.INFO)
: createLoggerForFile(start, infoLogPath, "io.supertokens.storage.mysql.Info");
this.errorLogger = errorLogPath.equals("null")
? createLoggerForConsole(start, "io.supertokens.storage.mysql.Error")
? createLoggerForConsole(start, "io.supertokens.storage.mysql.Error", LOG_LEVEL.ERROR)
: createLoggerForFile(start, errorLogPath, "io.supertokens.storage.mysql.Error");
}

Expand Down Expand Up @@ -154,12 +154,12 @@ public static void error(Start start, String message, boolean toConsoleAsWell, E

private static void systemOut(String msg) {
if (!Start.silent) {
System.out.println(msg);
System.out.println(Utils.maskDBPassword(msg));
}
}

private static void systemErr(String err) {
System.err.println(err);
System.err.println(Utils.maskDBPassword(err));
}

public static void stopLogging(Start start) {
Expand Down Expand Up @@ -198,7 +198,7 @@ private Logger createLoggerForFile(Start start, String file, String name) {
return logger;
}

private Logger createLoggerForConsole(Start start, String name) {
private Logger createLoggerForConsole(Start start, String name, LOG_LEVEL logLevel) {
Logger logger = (Logger) LoggerFactory.getLogger(name);

// We don't need to add appender if it is already added
Expand All @@ -211,6 +211,7 @@ private Logger createLoggerForConsole(Start start, String name) {
ple.setContext(lc);
ple.start();
ConsoleAppender<ILoggingEvent> logConsoleAppender = new ConsoleAppender<>();
logConsoleAppender.setTarget(logLevel == LOG_LEVEL.ERROR ? "System.err" : "System.out");
logConsoleAppender.setEncoder(ple);
logConsoleAppender.setContext(lc);
logConsoleAppender.start();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ public static String getQueryToCreateUserRolesTable(Start start) {
+ "user_id VARCHAR(128) NOT NULL, "
+ "role VARCHAR(255) NOT NULL,"
+ "PRIMARY KEY (app_id, tenant_id, user_id, role),"
+ "FOREIGN KEY (app_id, role)"
+ " REFERENCES " + Config.getConfig(start).getRolesTable() + "(app_id, role) ON DELETE CASCADE,"
+ "FOREIGN KEY (app_id, tenant_id)"
+ " REFERENCES " + Config.getConfig(start).getTenantsTable() + "(app_id, tenant_id) ON DELETE CASCADE"
+ ")";
Expand Down Expand Up @@ -331,4 +329,14 @@ public static int deleteAllRolesForUser_Transaction(Connection con, Start start,
pst.setString(2, userId);
});
}

public static boolean deleteAllUserRoleAssociationsForRole(Start start, AppIdentifier appIdentifier, String role)
throws SQLException, StorageQueryException {
String QUERY = "DELETE FROM " + Config.getConfig(start).getUserRolesTable()
+ " WHERE app_id = ? AND role = ? ;";
return update(start, QUERY, pst -> {
pst.setString(1, appIdentifier.getAppId());
pst.setString(2, role);
}) >= 1;
}
}
Loading

0 comments on commit aacd53d

Please sign in to comment.