Skip to content

Commit f9bbed5

Browse files
ZqyDaoDaoMatheMatrix
authored andcommitted
<fix>[encrypt]: recover iam2 attarbute data
remove conver need to recover data Resolves: ZSTAC-50892 Change-Id: I63646164716f736d64677969636a6d726d767764
1 parent 139097d commit f9bbed5

File tree

3 files changed

+278
-3
lines changed

3 files changed

+278
-3
lines changed

conf/db/upgrade/V4.5.3__schema.sql

+70
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,73 @@
11
alter table LicenseHistoryVO modify COLUMN `userName` varchar(64) NOT NULL;
22

33
ALTER TABLE LicenseHistoryVO ADD COLUMN capacity int(10) NOT NULL;
4+
5+
CREATE TABLE IF NOT EXISTS `zstack`.`IAM2VirtualIDInformationVO` (
6+
`uuid` varchar(32) NOT NULL UNIQUE,
7+
`phone` varchar(255),
8+
`mail` varchar(255),
9+
PRIMARY KEY (`uuid`),
10+
CONSTRAINT `fkIAM2VirtualIDInformationVOIAM2VirtualIDVO` FOREIGN KEY (`uuid`) REFERENCES `IAM2VirtualIDVO` (`uuid`) ON DELETE CASCADE
11+
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
12+
13+
DELIMITER $$
14+
CREATE PROCEDURE attributePhoneToInformation()
15+
BEGIN
16+
DECLARE vitualIdPhone VARCHAR(32);
17+
DECLARE vitualIdUuid VARCHAR(32);
18+
DECLARE done INT DEFAULT FALSE;
19+
DECLARE phoneCursor CURSOR FOR SELECT virtualIDUuid, value from `zstack`.`IAM2VirtualIDAttributeVO` WHERE name = "phone";
20+
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
21+
22+
open phoneCursor;
23+
read_loop: LOOP
24+
FETCH phoneCursor INTO vitualIdUuid, vitualIdPhone;
25+
IF done THEN
26+
LEAVE read_loop;
27+
END IF;
28+
29+
IF (select count(*) from IAM2VirtualIDInformationVO where uuid = vitualIdUuid) = 0 THEN
30+
INSERT `zstack`.`IAM2VirtualIDInformationVO`(uuid, phone) values (vitualIdUuid, vitualIdPhone);
31+
else
32+
update `zstack`.`IAM2VirtualIDInformationVO` set phone = vitualIdPhone where uuid = vitualIdUuid;
33+
END IF;
34+
35+
END LOOP;
36+
close phoneCursor;
37+
SELECT CURTIME();
38+
END $$
39+
DELIMITER ;
40+
41+
call attributePhoneToInformation();
42+
DROP PROCEDURE IF EXISTS attributePhoneToInformation;
43+
44+
DELIMITER $$
45+
CREATE PROCEDURE attributeMailToInformation()
46+
BEGIN
47+
DECLARE vitualIdMail VARCHAR(32);
48+
DECLARE vitualIdUuid VARCHAR(32);
49+
DECLARE done INT DEFAULT FALSE;
50+
DECLARE mailCursor CURSOR FOR SELECT virtualIDUuid, value from `zstack`.`IAM2VirtualIDAttributeVO` WHERE name = "mail";
51+
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
52+
53+
open mailCursor;
54+
read_loop: LOOP
55+
FETCH mailCursor INTO vitualIdUuid, vitualIdMail;
56+
IF done THEN
57+
LEAVE read_loop;
58+
END IF;
59+
60+
IF (select count(*) from IAM2VirtualIDInformationVO where uuid = vitualIdUuid) = 0 THEN
61+
INSERT `zstack`.`IAM2VirtualIDInformationVO`(uuid, mail) values (vitualIdUuid, vitualIdMail);
62+
ELSE
63+
update `zstack`.`IAM2VirtualIDInformationVO` set mail = vitualIdMail where uuid = vitualIdUuid;
64+
END IF;
65+
66+
END LOOP;
67+
close mailCursor;
68+
SELECT CURTIME();
69+
END $$
70+
DELIMITER ;
71+
72+
call attributeMailToInformation();
73+
DROP PROCEDURE IF EXISTS attributeMailToInformation;

core/src/main/java/org/zstack/core/encrypt/EncryptFacadeImpl.java

+119-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
package org.zstack.core.encrypt;
22

33
import org.springframework.beans.factory.annotation.Autowired;
4+
import org.springframework.transaction.annotation.Transactional;
45
import org.zstack.core.Platform;
56
import org.zstack.core.cloudbus.CloudBus;
67
import org.zstack.core.componentloader.PluginRegistry;
7-
import org.zstack.core.config.GlobalConfig;
8-
import org.zstack.core.config.GlobalConfigBeforeUpdateExtensionPoint;
9-
import org.zstack.core.config.GlobalConfigUpdateExtensionPoint;
8+
import org.zstack.core.config.*;
109
import org.zstack.core.convert.PasswordConverter;
1110
import org.zstack.core.db.DatabaseFacade;
1211
import org.zstack.core.db.SQLBatch;
@@ -273,6 +272,123 @@ public void updateGlobalConfig(GlobalConfig oldConfig, GlobalConfig newConfig) {
273272
}
274273
}
275274
});
275+
}
276+
277+
protected void handleNewAddedEncryptEntity() {
278+
if (PasswordEncryptType.None.toString().equals(EncryptGlobalConfig.ENABLE_PASSWORD_ENCRYPT.value())) {
279+
return;
280+
}
281+
282+
List<EncryptEntityMetadataVO> metadataVOList = Q.New(EncryptEntityMetadataVO.class)
283+
.eq(EncryptEntityMetadataVO_.state, EncryptEntityState.NewAdded)
284+
.list();
285+
286+
new SQLBatch() {
287+
@Override
288+
protected void scripts() {
289+
for (EncryptEntityMetadataVO metadata : metadataVOList) {
290+
// do encrypt
291+
long count = SQL.New(String.format("select count(1) from %s", metadata.getEntityName()), Long.class).find();
292+
metadata.setState(EncryptEntityState.Encrypting);
293+
metadata = dbf.updateAndRefresh(metadata);
294+
String className = metadata.getEntityName();
295+
String fieldName = metadata.getColumnName();
296+
sql(String.format("select uuid from %s", metadata.getEntityName()), String.class)
297+
.limit(1000)
298+
.paginate(count, (List<String> uuids) -> {
299+
for (String uuid : uuids) {
300+
String value = sql(String.format("select %s from %s where uuid = '%s'", fieldName, className, uuid)).find();
301+
302+
try {
303+
// If part of the data has been encrypted, first decrypt all the data before encrypting
304+
String decryptedString = decrypt(value);
305+
String encryptedString = encrypt(decryptedString);
306+
307+
String sql = String.format("update %s set %s = :encrypted where uuid = :uuid", className, fieldName);
308+
309+
Query query = dbf.getEntityManager().createQuery(sql);
310+
query.setParameter("encrypted", encryptedString);
311+
query.setParameter("uuid", uuid);
312+
query.executeUpdate();
313+
} catch (Exception e) {
314+
logger.debug(String.format("encrypt error because : %s", e.getMessage()));
315+
}
316+
}
317+
318+
});
319+
metadata.setState(EncryptEntityState.Encrypted);
320+
dbf.updateAndRefresh(metadata);
321+
}
322+
}
323+
}.execute();
324+
}
325+
326+
private void collectEncryptEntityMetadata() {
327+
for (Field field : encryptedFields) {
328+
List<String> classNames = new ArrayList<>();
329+
330+
if (field.getDeclaringClass().getAnnotation(Entity.class) != null && field.getDeclaringClass().getAnnotation(Table.class) != null) {
331+
classNames.add(field.getDeclaringClass().getSimpleName());
332+
} else {
333+
classNames.addAll(BeanUtils.reflections.getSubTypesOf(field.getDeclaringClass()).stream()
334+
.filter(aClass -> aClass.getAnnotation(Entity.class) != null && aClass.getAnnotation(Table.class) != null)
335+
.map(Class::getSimpleName)
336+
.collect(Collectors.toList()));
337+
}
338+
339+
for (String className : classNames) {
340+
createIfNotExists(className, field.getName());
341+
}
342+
}
343+
}
344+
345+
private void createIfNotExists(String entity, String column) {
346+
if (Q.New(EncryptEntityMetadataVO.class)
347+
.eq(EncryptEntityMetadataVO_.entityName, entity)
348+
.eq(EncryptEntityMetadataVO_.columnName, column)
349+
.isExists()) {
350+
return;
351+
}
352+
353+
EncryptEntityMetadataVO metadataVO = new EncryptEntityMetadataVO();
354+
metadataVO.setColumnName(column);
355+
metadataVO.setEntityName(entity);
356+
metadataVO.setState(EncryptEntityState.NewAdded);
357+
dbf.persist(metadataVO);
358+
}
359+
360+
public void updateEncryptDataStateIfExists(String entity, String column, EncryptEntityState state) {
361+
String sql = String.format("update EncryptEntityMetadataVO set state = :state where columnName = :columnName and entityName = :entityName");
362+
Query query = dbf.getEntityManager().createQuery(sql);
363+
query.setParameter("state", state);
364+
query.setParameter("entityName", entity);
365+
query.setParameter("columnName", column);
366+
query.executeUpdate();
367+
}
368+
369+
@Transactional
370+
public void removeConvertRecoverData() {
371+
if (Q.New(EncryptEntityMetadataVO.class)
372+
.isExists()) {
373+
return;
374+
}
375+
376+
if (PasswordEncryptType.None.toString().equals(EncryptGlobalConfig.ENABLE_PASSWORD_ENCRYPT.value())) {
377+
return;
378+
}
379+
380+
decryptAllPassword();
381+
encryptAllPassword();
382+
}
383+
384+
@Override
385+
public boolean start() {
386+
initEncryptDriver();
387+
collectAllEncryptPassword();
388+
installGlobalConfigUpdateHooks();
389+
removeConvertRecoverData();
390+
collectEncryptEntityMetadata();
391+
handleNewAddedEncryptEntity();
276392

277393
return true;
278394
}

test/src/test/groovy/org/zstack/test/integration/kvm/host/HostPasswordEncryptCase.groovy

+89
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,69 @@ class HostPasswordEncryptCase extends SubCase {
7474
assert password == "password"
7575
}
7676

77+
BeanUtils.reflections.getFieldsAnnotatedWith(Convert.class).forEach({ ec ->
78+
if (ec.getDeclaringClass().getAnnotation(Entity.class) == null || ec.getDeclaringClass().getAnnotation(Table.class) == null) {
79+
return;
80+
}
81+
if (!ec.getAnnotation(Convert.class).converter().equals(PasswordConverter.class)) {
82+
return;
83+
}
84+
85+
String entityName = ec.getDeclaringClass().getSimpleName();
86+
String columnName = ec.getName();
87+
retryInSecs {
88+
assert Q.New(EncryptEntityMetadataVO.class).select(EncryptEntityMetadataVO_.state).eq(EncryptEntityMetadataVO_.entityName, entityName).eq(EncryptEntityMetadataVO_.columnName, columnName).findValue() == EncryptEntityState.Encrypted
89+
}
90+
})
91+
92+
assert Q.New(EncryptEntityMetadataVO.class).select(EncryptEntityMetadataVO_.state).eq(EncryptEntityMetadataVO_.entityName, KVMHostVO.class.getSimpleName()).findValue() == EncryptEntityState.Encrypted
93+
94+
// test for unencrypted strings Decryption failures
95+
SQL.New(EncryptEntityMetadataVO.class)
96+
.eq(EncryptEntityMetadataVO_.entityName, KVMHostVO.class.getSimpleName())
97+
.set(EncryptEntityMetadataVO_.state, EncryptEntityState.NewAdded)
98+
.update()
99+
100+
((EncryptFacadeImpl) encryptFacade).handleNewAddedEncryptEntity()
101+
102+
assert Q.New(EncryptEntityMetadataVO.class).select(EncryptEntityMetadataVO_.state).eq(EncryptEntityMetadataVO_.entityName, KVMHostVO.class.getSimpleName()).findValue() == EncryptEntityState.Encrypted
103+
104+
retryInSecs {
105+
password = Q.New(KVMHostVO.class).select(KVMHostVO_.password).eq(KVMHostVO_.uuid, host.uuid).findValue()
106+
107+
assert password == encryptFacade.encrypt("password")
108+
}
109+
110+
// test handleNewAddedEncryptEntity again, result unchanged
111+
SQL.New(EncryptEntityMetadataVO.class)
112+
.eq(EncryptEntityMetadataVO_.entityName, KVMHostVO.class.getSimpleName())
113+
.set(EncryptEntityMetadataVO_.state, EncryptEntityState.NewAdded)
114+
.update()
115+
116+
((EncryptFacadeImpl) encryptFacade).handleNewAddedEncryptEntity()
117+
118+
assert Q.New(EncryptEntityMetadataVO.class).select(EncryptEntityMetadataVO_.state).eq(EncryptEntityMetadataVO_.entityName, KVMHostVO.class.getSimpleName()).findValue() == EncryptEntityState.Encrypted
119+
120+
retryInSecs {
121+
password = Q.New(KVMHostVO.class).select(KVMHostVO_.password).eq(KVMHostVO_.uuid, host.uuid).findValue()
122+
123+
assert password == encryptFacade.encrypt("password")
124+
}
125+
126+
List<Tuple> needToChangeHosts = Q.New(KVMHostVO.class).select(KVMHostVO_.password, KVMHostVO_.uuid).listTuple()
127+
needToChangeHosts.forEach({ changeHost ->
128+
SQL.New(KVMHostVO.class)
129+
.eq(KVMHostVO_.uuid, changeHost.get(1, String.class))
130+
.set(KVMHostVO_.password, encryptFacade.decrypt(changeHost.get(0, String.class)))
131+
.update()
132+
})
133+
134+
retryInSecs {
135+
password = Q.New(KVMHostVO.class).select(KVMHostVO_.password).eq(KVMHostVO_.uuid, host.uuid).findValue()
136+
137+
assert password == "password"
138+
}
139+
77140
updateGlobalConfig {
78141
category = EncryptGlobalConfig.CATEGORY
79142
name = EncryptGlobalConfig.ENABLE_PASSWORD_ENCRYPT.name
@@ -85,6 +148,32 @@ class HostPasswordEncryptCase extends SubCase {
85148

86149
assert password == "password"
87150
}
151+
152+
BeanUtils.reflections.getFieldsAnnotatedWith(Convert.class).forEach({ ec ->
153+
if (ec.getDeclaringClass().getAnnotation(Entity.class) == null || ec.getDeclaringClass().getAnnotation(Table.class) == null) {
154+
return;
155+
}
156+
if (!ec.getAnnotation(Convert.class).converter().equals(PasswordConverter.class)) {
157+
return;
158+
}
159+
160+
String entityName = ec.getDeclaringClass().getSimpleName();
161+
String columnName = ec.getName();
162+
assert Q.New(EncryptEntityMetadataVO.class).select(EncryptEntityMetadataVO_.state).eq(EncryptEntityMetadataVO_.entityName, entityName).eq(EncryptEntityMetadataVO_.columnName, columnName).findValue() == EncryptEntityState.NewAdded
163+
164+
})
165+
166+
assert Q.New(EncryptEntityMetadataVO.class).select(EncryptEntityMetadataVO_.state).eq(EncryptEntityMetadataVO_.entityName, KVMHostVO.class.getSimpleName()).findValue() == EncryptEntityState.NewAdded
167+
168+
((EncryptFacadeImpl) encryptFacade).handleNewAddedEncryptEntity()
169+
170+
assert Q.New(EncryptEntityMetadataVO.class).select(EncryptEntityMetadataVO_.state).eq(EncryptEntityMetadataVO_.entityName, KVMHostVO.class.getSimpleName()).findValue() == EncryptEntityState.NewAdded
171+
172+
retryInSecs {
173+
password = Q.New(KVMHostVO.class).select(KVMHostVO_.password).eq(KVMHostVO_.uuid, host.uuid).findValue()
174+
175+
assert password == "password"
176+
}
88177
}
89178

90179

0 commit comments

Comments
 (0)