CB-3834 secret controller redesign (#2343)

* CB-3834 Secret manager database schema

* CB-3834 add credentials storage flat to teams

* CB-3834 subject based secrets

---------

Co-authored-by: Serge Rider <serge@jkiss.org>
Co-authored-by: kseniaguzeeva <112612526+kseniaguzeeva@users.noreply.github.com>
This commit is contained in:
Alexander Skoblikov
2024-01-31 16:52:00 +03:00
committed by GitHub
co-authored by Serge Rider kseniaguzeeva
parent 71a61047e2
commit a2094a4d90
5 changed files with 122 additions and 29 deletions
@@ -37,12 +37,12 @@ public class VoidSecretController implements DBSSecretController, DBSSecretContr
@Nullable
@Override
public String getSecretValue(@NotNull String secretId) {
public String getPrivateSecretValue(@NotNull String secretId) {
return null;
}
@Override
public void setSecretValue(@NotNull String secretId, @Nullable String secretValue) throws DBException {
public void setPrivateSecretValue(@NotNull String secretId, @Nullable String secretValue) throws DBException {
throw new DBException("Secret controller is read-only");
}
@@ -60,8 +60,9 @@ CREATE TABLE {table_prefix}CB_WORKSPACE
CREATE TABLE {table_prefix}CB_AUTH_SUBJECT
(
SUBJECT_ID VARCHAR(128) NOT NULL,
SUBJECT_TYPE VARCHAR(8) NOT NULL,
SUBJECT_ID VARCHAR(128) NOT NULL,
SUBJECT_TYPE VARCHAR(8) NOT NULL,
IS_SECRET_STORAGE CHAR(1) DEFAULT 'Y' NOT NULL,
PRIMARY KEY (SUBJECT_ID)
);
@@ -105,16 +106,30 @@ CREATE TABLE {table_prefix}CB_OBJECT_PERMISSIONS
FOREIGN KEY (SUBJECT_ID) REFERENCES {table_prefix}CB_AUTH_SUBJECT (SUBJECT_ID) ON DELETE CASCADE
);
CREATE TABLE {table_prefix}CB_CREDENTIALS_PROFILE
(
PROFILE_ID VARCHAR(128) NOT NULL,
PROFILE_NAME VARCHAR(100) NOT NULL,
PROFILE_DESCRIPTION VARCHAR(255) NOT NULL,
PARENT_PROFILE_ID VARCHAR(128) NULL,
CREATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
PRIMARY KEY (PROFILE_ID),
FOREIGN KEY (PROFILE_ID) REFERENCES {table_prefix}CB_AUTH_SUBJECT (SUBJECT_ID) ON DELETE CASCADE,
FOREIGN KEY (PARENT_PROFILE_ID) REFERENCES {table_prefix}CB_CREDENTIALS_PROFILE(PROFILE_ID) ON DELETE NO ACTION
);
CREATE TABLE {table_prefix}CB_USER
(
USER_ID VARCHAR(128) NOT NULL,
IS_ACTIVE CHAR(1) NOT NULL,
CREATE_TIME TIMESTAMP NOT NULL,
DEFAULT_AUTH_ROLE VARCHAR(32) NULL,
CREDENTIALS_PROFILE_ID VARCHAR(128) NULL,
PRIMARY KEY (USER_ID),
FOREIGN KEY (USER_ID) REFERENCES {table_prefix}CB_AUTH_SUBJECT (SUBJECT_ID) ON DELETE CASCADE
FOREIGN KEY (USER_ID) REFERENCES {table_prefix}CB_AUTH_SUBJECT (SUBJECT_ID) ON DELETE CASCADE,
FOREIGN KEY (CREDENTIALS_PROFILE_ID) REFERENCES {table_prefix}CB_CREDENTIALS_PROFILE(PROFILE_ID) ON DELETE NO ACTION
);
-- Additional user properties (profile)
@@ -131,11 +146,10 @@ CREATE TABLE {table_prefix}CB_USER_PARAMETERS
CREATE TABLE {table_prefix}CB_TEAM
(
TEAM_ID VARCHAR(128) NOT NULL,
TEAM_NAME VARCHAR(100) NOT NULL,
TEAM_DESCRIPTION VARCHAR(255) NOT NULL,
CREATE_TIME TIMESTAMP NOT NULL,
TEAM_ID VARCHAR(128) NOT NULL,
TEAM_NAME VARCHAR(100) NOT NULL,
TEAM_DESCRIPTION VARCHAR(255) NOT NULL,
CREATE_TIME TIMESTAMP NOT NULL,
PRIMARY KEY (TEAM_ID),
FOREIGN KEY (TEAM_ID) REFERENCES {table_prefix}CB_AUTH_SUBJECT (SUBJECT_ID) ON DELETE CASCADE
@@ -315,3 +329,25 @@ CREATE TABLE {table_prefix}CB_USER_SECRETS
PRIMARY KEY (USER_ID, SECRET_ID),
FOREIGN KEY (USER_ID) REFERENCES {table_prefix}CB_USER (USER_ID) ON DELETE CASCADE
);
CREATE TABLE {table_prefix}CB_SUBJECT_SECRETS
(
SUBJECT_ID VARCHAR(128) NOT NULL,
SECRET_ID VARCHAR(255) NOT NULL,
PROJECT_ID VARCHAR(128),
OBJECT_TYPE VARCHAR(32),
OBJECT_ID VARCHAR(128),
SECRET_VALUE TEXT NOT NULL,
ENCODING_TYPE VARCHAR(32) DEFAULT 'PLAINTEXT' NOT NULL,
CREATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
UPDATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
PRIMARY KEY (SUBJECT_ID, SECRET_ID),
FOREIGN KEY (SUBJECT_ID) REFERENCES {table_prefix}CB_AUTH_SUBJECT (SUBJECT_ID) ON DELETE CASCADE
);
CREATE INDEX IDX_SUBJECT_SECRETS_PROJECT ON {table_prefix}CB_SUBJECT_SECRETS (PROJECT_ID,SUBJECT_ID);
CREATE INDEX IDX_SUBJECT_SECRETS_OBJECT ON {table_prefix}CB_SUBJECT_SECRETS (PROJECT_ID,OBJECT_TYPE,OBJECT_ID);
@@ -0,0 +1,29 @@
CREATE TABLE {table_prefix}CB_SUBJECT_SECRETS
(
SUBJECT_ID VARCHAR(128) NOT NULL,
SECRET_ID VARCHAR(255) NOT NULL,
PROJECT_ID VARCHAR(128),
OBJECT_TYPE VARCHAR(32),
OBJECT_ID VARCHAR(128),
SECRET_VALUE TEXT NOT NULL,
ENCODING_TYPE VARCHAR(32) DEFAULT 'PLAINTEXT' NOT NULL,
CREATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
UPDATE_TIME TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
PRIMARY KEY (SUBJECT_ID, SECRET_ID),
FOREIGN KEY (SUBJECT_ID) REFERENCES {table_prefix}CB_AUTH_SUBJECT (SUBJECT_ID) ON DELETE CASCADE
);
CREATE INDEX IDX_SUBJECT_SECRETS_PROJECT ON {table_prefix}CB_SUBJECT_SECRETS (PROJECT_ID,SUBJECT_ID);
CREATE INDEX IDX_SUBJECT_SECRETS_OBJECT ON {table_prefix}CB_SUBJECT_SECRETS (PROJECT_ID,OBJECT_TYPE,OBJECT_ID);
INSERT INTO {table_prefix}CB_SUBJECT_SECRETS (SUBJECT_ID, SECRET_ID, SECRET_VALUE, ENCODING_TYPE, CREATE_TIME, UPDATE_TIME)
SELECT USER_ID, SECRET_ID, SECRET_VALUE, ENCODING_TYPE, UPDATE_TIME, UPDATE_TIME FROM {table_prefix}CB_USER_SECRETS;
DROP TABLE {table_prefix}CB_USER_SECRETS;
ALTER TABLE {table_prefix}CB_AUTH_SUBJECT
ADD COLUMN IS_SECRET_STORAGE CHAR(1) DEFAULT 'Y' NOT NULL;
@@ -74,8 +74,6 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
protected static final String CHAR_BOOL_TRUE = "Y";
protected static final String CHAR_BOOL_FALSE = "N";
private static final String SUBJECT_USER = "U";
private static final String SUBJECT_TEAM = "R";
private static final Type MAP_STRING_OBJECT_TYPE = new TypeToken<Map<String, Object>>() {
}.getType();
private static final Gson gson = new GsonBuilder().create();
@@ -147,7 +145,7 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
boolean enabled,
@Nullable String defaultAuthRole
) throws DBException, SQLException {
createAuthSubject(dbCon, userId, SUBJECT_USER);
createAuthSubject(dbCon, userId, SMSubjectType.user, true);
try (PreparedStatement dbStat = dbCon.prepareStatement(
database.normalizeTableNames("INSERT INTO {table_prefix}CB_USER" +
"(USER_ID,IS_ACTIVE,CREATE_TIME,DEFAULT_AUTH_ROLE) VALUES(?,?,?,?)"))
@@ -254,9 +252,10 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
public SMTeam[] getUserTeams(String userId) throws DBException {
Map<String, SMTeam> teams = new LinkedHashMap<>();
try (Connection dbCon = database.openConnection()) {
try (PreparedStatement dbStat = dbCon.prepareStatement(
database.normalizeTableNames("SELECT R.* FROM {table_prefix}CB_USER_TEAM UR, {table_prefix}CB_TEAM R " +
"WHERE UR.USER_ID=? AND UR.TEAM_ID=R.TEAM_ID"))
try (PreparedStatement dbStat = dbCon.prepareStatement(database.normalizeTableNames(
"SELECT R.*,S.IS_SECRET_STORAGE FROM {table_prefix}CB_USER_TEAM UR, {table_prefix}CB_TEAM R, " +
"{table_prefix}CB_AUTH_SUBJECT S " +
"WHERE UR.USER_ID=? AND UR.TEAM_ID=R.TEAM_ID AND S.SUBJECT_ID=R.TEAM_ID"))
) {
dbStat.setString(1, userId);
try (ResultSet dbResult = dbStat.executeQuery()) {
@@ -300,15 +299,15 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
try (Connection dbCon = database.openConnection()) {
SMUser user;
try (PreparedStatement dbStat = dbCon.prepareStatement(
database.normalizeTableNames("SELECT USER_ID,IS_ACTIVE,DEFAULT_AUTH_ROLE FROM {table_prefix}CB_USER WHERE USER_ID=?")
database.normalizeTableNames(
"SELECT U.USER_ID,U.IS_ACTIVE,U.DEFAULT_AUTH_ROLE,S.IS_SECRET_STORAGE FROM " +
"{table_prefix}CB_USER U, {table_prefix}CB_AUTH_SUBJECT S " +
"WHERE U.USER_ID=? AND U.USER_ID=S.SUBJECT_ID")
)) {
dbStat.setString(1, userId);
try (ResultSet dbResult = dbStat.executeQuery()) {
if (dbResult.next()) {
String userName = dbResult.getString(1);
String active = dbResult.getString(2);
String authRole = dbResult.getString(3);
user = new SMUser(userName, CHAR_BOOL_TRUE.equals(active), authRole);
user = fetchUser(dbResult);
} else {
return null;
}
@@ -909,7 +908,9 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
Map<String, SMTeam> teams = new LinkedHashMap<>();
try (Statement dbStat = dbCon.createStatement()) {
try (ResultSet dbResult = dbStat.executeQuery(
database.normalizeTableNames("SELECT * FROM {table_prefix}CB_TEAM ORDER BY TEAM_ID"))) {
database.normalizeTableNames("SELECT T.*,S.IS_SECRET_STORAGE FROM {table_prefix}CB_TEAM T," +
"{table_prefix}CB_AUTH_SUBJECT S " +
"WHERE T.TEAM_ID=S.SUBJECT_ID ORDER BY TEAM_ID"))) {
while (dbResult.next()) {
SMTeam team = fetchTeam(dbResult);
teams.put(team.getTeamId(), team);
@@ -966,7 +967,18 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
return new SMTeam(
dbResult.getString("TEAM_ID"),
dbResult.getString("TEAM_NAME"),
dbResult.getString("TEAM_DESCRIPTION")
dbResult.getString("TEAM_DESCRIPTION"),
stringToBoolean(dbResult.getString("IS_SECRET_STORAGE"))
);
}
@NotNull
private SMUser fetchUser(ResultSet dbResult) throws SQLException {
return new SMUser(
dbResult.getString("USER_ID"),
stringToBoolean(dbResult.getString("IS_ACTIVE")),
dbResult.getString("DEFAULT_AUTH_ROLE"),
stringToBoolean(dbResult.getString("IS_SECRET_STORAGE"))
);
}
@@ -980,7 +992,7 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
}
try (Connection dbCon = database.openConnection()) {
try (JDBCTransaction txn = new JDBCTransaction(dbCon)) {
createAuthSubject(dbCon, teamId, SUBJECT_TEAM);
createAuthSubject(dbCon, teamId, SMSubjectType.team, true);
try (PreparedStatement dbStat = dbCon.prepareStatement(
database.normalizeTableNames("INSERT INTO {table_prefix}CB_TEAM" +
"(TEAM_ID,TEAM_NAME,TEAM_DESCRIPTION,CREATE_TIME) VALUES(?,?,?,?)"))) {
@@ -2794,15 +2806,31 @@ public class CBEmbeddedSecurityController<T extends WebAuthApplication>
}
}
private void createAuthSubject(Connection dbCon, String subjectId, String subjectType) throws SQLException {
private void createAuthSubject(
Connection dbCon,
String subjectId,
SMSubjectType subjectType,
boolean secretStorage
) throws SQLException {
try (PreparedStatement dbStat = dbCon.prepareStatement(
database.normalizeTableNames("INSERT INTO {table_prefix}CB_AUTH_SUBJECT(SUBJECT_ID,SUBJECT_TYPE) VALUES(?,?)"))) {
database.normalizeTableNames(
"INSERT INTO {table_prefix}CB_AUTH_SUBJECT(SUBJECT_ID,SUBJECT_TYPE,IS_SECRET_STORAGE) " +
"VALUES (?,?,?)"))) {
dbStat.setString(1, subjectId);
dbStat.setString(2, subjectType);
dbStat.setString(2, subjectType.getCode());
dbStat.setString(3, booleanToString(secretStorage));
dbStat.execute();
}
}
public static String booleanToString(boolean value) {
return value ? CHAR_BOOL_TRUE : CHAR_BOOL_FALSE;
}
public static boolean stringToBoolean(@Nullable String value) {
return CHAR_BOOL_TRUE.equals(value);
}
private void deleteAuthSubject(Connection dbCon, String subjectId) throws SQLException {
try (PreparedStatement dbStat = dbCon.prepareStatement(
database.normalizeTableNames("DELETE FROM {table_prefix}CB_AUTH_SUBJECT WHERE SUBJECT_ID=?"))) {
@@ -73,7 +73,7 @@ public class CBDatabase {
public static final String SCHEMA_UPDATE_SQL_PATH = "db/cb_schema_update_";
private static final int LEGACY_SCHEMA_VERSION = 1;
private static final int CURRENT_SCHEMA_VERSION = 15;
private static final int CURRENT_SCHEMA_VERSION = 17;
private static final String DEFAULT_DB_USER_NAME = "cb-data";
private static final String DEFAULT_DB_PWD_FILE = ".database-credentials.dat";