Correct unique constraints for UserConsent entity
Closes #13045 Signed-off-by: cgeorgilakis-grnet <cgeorgilakis@admin.grnet.gr>
This commit is contained in:
parent
05056330dc
commit
4bca804d5a
4 changed files with 170 additions and 7 deletions
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed 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.keycloak.connections.jpa.updater.liquibase.custom;
|
||||||
|
|
||||||
|
import liquibase.exception.CustomChangeException;
|
||||||
|
import liquibase.statement.core.RawSqlStatement;
|
||||||
|
|
||||||
|
public class JpaUpdate25_0_0_ConsentConstraints extends CustomKeycloakTask {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void generateStatementsImpl() throws CustomChangeException {
|
||||||
|
final String userConsentClientScopeTable = getTableName("USER_CONSENT_CLIENT_SCOPE");
|
||||||
|
final String userConsentTable = getTableName("USER_CONSENT");
|
||||||
|
statements.add(new RawSqlStatement(
|
||||||
|
"DELETE FROM "+ userConsentClientScopeTable + " WHERE USER_CONSENT_ID IN (" +
|
||||||
|
" SELECT uc.ID FROM "+userConsentTable+" uc INNER JOIN (" +
|
||||||
|
" SELECT CLIENT_ID, USER_ID, MAX(LAST_UPDATED_DATE) AS MAX_UPDATED_DATE FROM " + userConsentTable +
|
||||||
|
" GROUP BY CLIENT_ID, USER_ID HAVING COUNT(*) > 1 ) max_dates ON uc.CLIENT_ID = max_dates.CLIENT_ID" +
|
||||||
|
" AND uc.USER_ID = max_dates.USER_ID AND uc.LAST_UPDATED_DATE = max_dates.MAX_UPDATED_DATE)"
|
||||||
|
));
|
||||||
|
statements.add(new RawSqlStatement(
|
||||||
|
" DELETE FROM "+userConsentTable+" WHERE ID IN (" +
|
||||||
|
" SELECT uc.ID FROM "+userConsentTable+" uc INNER JOIN (" +
|
||||||
|
" SELECT CLIENT_ID, USER_ID, MAX(LAST_UPDATED_DATE) AS MAX_UPDATED_DATE" +
|
||||||
|
" FROM "+userConsentTable+" GROUP BY CLIENT_ID, USER_ID HAVING COUNT(*) > 1 )" +
|
||||||
|
" max_dates ON uc.CLIENT_ID = max_dates.CLIENT_ID" +
|
||||||
|
" AND uc.USER_ID = max_dates.USER_ID AND uc.LAST_UPDATED_DATE = max_dates.MAX_UPDATED_DATE )"
|
||||||
|
));
|
||||||
|
statements.add(new RawSqlStatement(
|
||||||
|
" DELETE FROM "+ userConsentClientScopeTable + " WHERE USER_CONSENT_ID IN (" +
|
||||||
|
" SELECT uc.ID FROM "+userConsentTable+" uc INNER JOIN (" +
|
||||||
|
" SELECT CLIENT_STORAGE_PROVIDER, EXTERNAL_CLIENT_ID, USER_ID, MAX(LAST_UPDATED_DATE) AS MAX_UPDATED_DATE" +
|
||||||
|
" FROM "+userConsentTable+" GROUP BY CLIENT_STORAGE_PROVIDER, EXTERNAL_CLIENT_ID, USER_ID HAVING COUNT(*) > 1 )" +
|
||||||
|
" max_dates ON uc.CLIENT_STORAGE_PROVIDER = max_dates.CLIENT_STORAGE_PROVIDER" +
|
||||||
|
" AND uc.EXTERNAL_CLIENT_ID = max_dates.EXTERNAL_CLIENT_ID AND uc.USER_ID = max_dates.USER_ID AND uc.LAST_UPDATED_DATE = max_dates.MAX_UPDATED_DATE )"
|
||||||
|
));
|
||||||
|
statements.add(new RawSqlStatement(
|
||||||
|
" DELETE FROM "+userConsentTable+" WHERE ID IN (" +
|
||||||
|
" SELECT uc.ID FROM "+userConsentTable+" uc INNER JOIN (" +
|
||||||
|
" SELECT CLIENT_STORAGE_PROVIDER, EXTERNAL_CLIENT_ID, USER_ID, MAX(LAST_UPDATED_DATE) AS MAX_UPDATED_DATE" +
|
||||||
|
" FROM "+userConsentTable+" GROUP BY CLIENT_STORAGE_PROVIDER, EXTERNAL_CLIENT_ID, USER_ID HAVING COUNT(*) > 1 )" +
|
||||||
|
" max_dates ON uc.CLIENT_STORAGE_PROVIDER = max_dates.CLIENT_STORAGE_PROVIDER" +
|
||||||
|
" AND uc.EXTERNAL_CLIENT_ID = max_dates.EXTERNAL_CLIENT_ID AND uc.USER_ID = max_dates.USER_ID AND uc.LAST_UPDATED_DATE = max_dates.MAX_UPDATED_DATE )"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getTaskId() {
|
||||||
|
return "Correct User Consent Unique Constraints for PostgreSQL and MariaDB";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Red Hat, Inc. and/or its affiliates
|
||||||
|
* and other contributors as indicated by the @author tags.
|
||||||
|
*
|
||||||
|
* Licensed 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.keycloak.connections.jpa.updater.liquibase.custom;
|
||||||
|
|
||||||
|
import liquibase.exception.CustomChangeException;
|
||||||
|
import liquibase.statement.core.DeleteStatement;
|
||||||
|
import liquibase.statement.core.RawSqlStatement;
|
||||||
|
import liquibase.structure.core.Table;
|
||||||
|
|
||||||
|
public class JpaUpdate25_0_0_MySQL_ConsentConstraints extends CustomKeycloakTask {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void generateStatementsImpl() throws CustomChangeException {
|
||||||
|
final String userConsentClientScopeTable = getTableName("USER_CONSENT_CLIENT_SCOPE");
|
||||||
|
final String userConsentTable = getTableName("USER_CONSENT");
|
||||||
|
statements.add(new RawSqlStatement(
|
||||||
|
"DELETE FROM "+ userConsentClientScopeTable + " WHERE USER_CONSENT_ID IN (" +
|
||||||
|
" SELECT uc.ID FROM "+userConsentTable+" uc INNER JOIN (" +
|
||||||
|
" SELECT CLIENT_ID, USER_ID, MAX(LAST_UPDATED_DATE) AS MAX_UPDATED_DATE FROM " + userConsentTable +
|
||||||
|
" GROUP BY CLIENT_ID, USER_ID HAVING COUNT(*) > 1 ) max_dates ON uc.CLIENT_ID = max_dates.CLIENT_ID" +
|
||||||
|
" AND uc.USER_ID = max_dates.USER_ID AND uc.LAST_UPDATED_DATE = max_dates.MAX_UPDATED_DATE)"
|
||||||
|
));
|
||||||
|
statements.add(new RawSqlStatement(
|
||||||
|
" CREATE TEMPORARY TABLE TEMP_USER_CONSENT_IDS" +
|
||||||
|
" AS SELECT uc.ID FROM "+userConsentTable+" uc INNER JOIN (" +
|
||||||
|
" SELECT CLIENT_ID, USER_ID, MAX(LAST_UPDATED_DATE) AS MAX_UPDATED_DATE" +
|
||||||
|
" FROM "+userConsentTable+" GROUP BY CLIENT_ID, USER_ID HAVING COUNT(*) > 1 )" +
|
||||||
|
" max_dates ON uc.CLIENT_ID = max_dates.CLIENT_ID" +
|
||||||
|
" AND uc.USER_ID = max_dates.USER_ID AND uc.LAST_UPDATED_DATE = max_dates.MAX_UPDATED_DATE"
|
||||||
|
));
|
||||||
|
statements.add(new DeleteStatement(null, null, database.correctObjectName("USER_CONSENT", Table.class))
|
||||||
|
.setWhere("ID IN (SELECT ID FROM TEMP_USER_CONSENT_IDS)"));
|
||||||
|
statements.add(new RawSqlStatement("DROP TEMPORARY TABLE IF EXISTS TEMP_USER_CONSENT_IDS"));
|
||||||
|
|
||||||
|
statements.add(new RawSqlStatement(
|
||||||
|
" DELETE FROM "+ userConsentClientScopeTable + " WHERE USER_CONSENT_ID IN (" +
|
||||||
|
" SELECT uc.ID FROM "+userConsentTable+" uc INNER JOIN (" +
|
||||||
|
" SELECT CLIENT_STORAGE_PROVIDER, EXTERNAL_CLIENT_ID, USER_ID, MAX(LAST_UPDATED_DATE) AS MAX_UPDATED_DATE" +
|
||||||
|
" FROM "+userConsentTable+" GROUP BY CLIENT_STORAGE_PROVIDER, EXTERNAL_CLIENT_ID, USER_ID HAVING COUNT(*) > 1 )" +
|
||||||
|
" max_dates ON uc.CLIENT_STORAGE_PROVIDER = max_dates.CLIENT_STORAGE_PROVIDER" +
|
||||||
|
" AND uc.EXTERNAL_CLIENT_ID = max_dates.EXTERNAL_CLIENT_ID AND uc.USER_ID = max_dates.USER_ID AND uc.LAST_UPDATED_DATE = max_dates.MAX_UPDATED_DATE )"
|
||||||
|
));
|
||||||
|
statements.add(new RawSqlStatement(
|
||||||
|
"CREATE TEMPORARY TABLE TEMP_USER_CONSENT_IDS2" +
|
||||||
|
" AS SELECT uc.ID FROM "+userConsentTable+" uc INNER JOIN (" +
|
||||||
|
" SELECT CLIENT_STORAGE_PROVIDER, EXTERNAL_CLIENT_ID, USER_ID, MAX(LAST_UPDATED_DATE) AS MAX_UPDATED_DATE" +
|
||||||
|
" FROM "+userConsentTable+" GROUP BY CLIENT_STORAGE_PROVIDER, EXTERNAL_CLIENT_ID, USER_ID HAVING COUNT(*) > 1 )" +
|
||||||
|
" max_dates ON uc.CLIENT_STORAGE_PROVIDER = max_dates.CLIENT_STORAGE_PROVIDER" +
|
||||||
|
" AND uc.EXTERNAL_CLIENT_ID = max_dates.EXTERNAL_CLIENT_ID AND uc.USER_ID = max_dates.USER_ID AND uc.LAST_UPDATED_DATE = max_dates.MAX_UPDATED_DATE;"
|
||||||
|
));
|
||||||
|
statements.add(new DeleteStatement(null, null, database.correctObjectName("USER_CONSENT", Table.class))
|
||||||
|
.setWhere("ID IN (SELECT ID FROM TEMP_USER_CONSENT_IDS2)"));
|
||||||
|
statements.add(new RawSqlStatement("DROP TEMPORARY TABLE IF EXISTS TEMP_USER_CONSENT_IDS2"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getTaskId() {
|
||||||
|
return "Correct User Consent Unique Constraints for MySQL";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -208,15 +208,10 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
|
||||||
@Override
|
@Override
|
||||||
public void addConsent(RealmModel realm, String userId, UserConsentModel consent) {
|
public void addConsent(RealmModel realm, String userId, UserConsentModel consent) {
|
||||||
String clientId = consent.getClient().getId();
|
String clientId = consent.getClient().getId();
|
||||||
|
|
||||||
UserConsentEntity consentEntity = getGrantedConsentEntity(userId, clientId, LockModeType.NONE);
|
|
||||||
if (consentEntity != null) {
|
|
||||||
throw new ModelDuplicateException("Consent already exists for client [" + clientId + "] and user [" + userId + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
long currentTime = Time.currentTimeMillis();
|
long currentTime = Time.currentTimeMillis();
|
||||||
|
|
||||||
consentEntity = new UserConsentEntity();
|
UserConsentEntity consentEntity = new UserConsentEntity();
|
||||||
consentEntity.setId(KeycloakModelUtils.generateId());
|
consentEntity.setId(KeycloakModelUtils.generateId());
|
||||||
consentEntity.setUser(em.getReference(UserEntity.class, userId));
|
consentEntity.setUser(em.getReference(UserEntity.class, userId));
|
||||||
StorageId clientStorageId = new StorageId(clientId);
|
StorageId clientStorageId = new StorageId(clientId);
|
||||||
|
|
|
@ -34,4 +34,28 @@
|
||||||
<addUniqueConstraint tableName="ORGANIZATION" columnNames="REALM_ID, NAME" constraintName="UK_ORG_NAME"/>
|
<addUniqueConstraint tableName="ORGANIZATION" columnNames="REALM_ID, NAME" constraintName="UK_ORG_NAME"/>
|
||||||
</changeSet>
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="keycloak" id="unique-consentuser">
|
||||||
|
<preConditions onSqlOutput="TEST" onFail="MARK_RAN">
|
||||||
|
<or>
|
||||||
|
<dbms type="mariadb"/>
|
||||||
|
<dbms type="postgresql"/>
|
||||||
|
</or>
|
||||||
|
</preConditions>
|
||||||
|
<customChange class="org.keycloak.connections.jpa.updater.liquibase.custom.JpaUpdate25_0_0_ConsentConstraints"/>
|
||||||
|
<dropUniqueConstraint tableName="USER_CONSENT" constraintName="UK_JKUWUVD56ONTGSUHOGM8UEWRT"/>
|
||||||
|
<addUniqueConstraint columnNames="CLIENT_ID, USER_ID" constraintName="UK_LOCAL_CONSENT" tableName="USER_CONSENT"/>
|
||||||
|
<addUniqueConstraint columnNames="CLIENT_STORAGE_PROVIDER, EXTERNAL_CLIENT_ID, USER_ID" constraintName="UK_EXTERNAL_CONSENT" tableName="USER_CONSENT"/>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet author="keycloak" id="unique-consentuser-mysql">
|
||||||
|
<preConditions onSqlOutput="TEST" onFail="MARK_RAN">
|
||||||
|
<dbms type="mysql"/>
|
||||||
|
</preConditions>
|
||||||
|
<customChange class="org.keycloak.connections.jpa.updater.liquibase.custom.JpaUpdate25_0_0_MySQL_ConsentConstraints"/>
|
||||||
|
<dropUniqueConstraint tableName="USER_CONSENT" constraintName="UK_JKUWUVD56ONTGSUHOGM8UEWRT"/>
|
||||||
|
<addUniqueConstraint columnNames="CLIENT_ID, USER_ID" constraintName="UK_LOCAL_CONSENT" tableName="USER_CONSENT"/>
|
||||||
|
<addUniqueConstraint columnNames="CLIENT_STORAGE_PROVIDER, EXTERNAL_CLIENT_ID, USER_ID" constraintName="UK_EXTERNAL_CONSENT" tableName="USER_CONSENT"/>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|
Loading…
Reference in a new issue