1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package cz.zcu.mre.anonmed.anonymizer;
23
24 import cz.zcu.mre.anonmed.AnonMedConfiguration;
25 import cz.zcu.mre.anonmed.AnonMedException;
26 import cz.zcu.mre.anonmed.FilenameGenerator;
27 import cz.zcu.mre.anonmed.report.AnonMedReport;
28 import cz.zcu.mre.anonmed.rule.Operation;
29 import cz.zcu.mre.anonmed.rule.Rule;
30 import java.io.File;
31 import java.io.IOException;
32 import java.util.List;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36
37
38
39
40
41
42 public abstract class AbstractAnonymizer implements Anonymizer {
43
44
45
46
47 private static final Logger LOG = LoggerFactory.getLogger(AbstractAnonymizer.class);
48
49
50
51
52 private AnonMedConfiguration config = null;
53
54
55
56
57 private String fileType = null;
58
59
60
61
62 private String fileExtension = null;
63
64
65
66
67 private File activeFile = null;
68
69
70
71
72 private Boolean uncertain = false;
73
74
75
76
77 protected long originalLastModified = 0L;
78
79
80
81
82 private String anonymousIdentification;
83
84
85
86
87
88
89 public AbstractAnonymizer(final AnonMedConfiguration conf) {
90 setConfig(conf);
91 }
92
93
94
95
96
97
98 protected final void setConfig(
99 final AnonMedConfiguration conf) {
100
101 if (conf == null) {
102 throw new NullPointerException("Application config is null.");
103 }
104
105 this.config = conf;
106 }
107
108
109
110
111
112
113 @Override
114 public final AnonMedConfiguration getConfig() {
115 return config;
116 }
117
118
119
120
121
122
123 protected final void setFileExtension(
124 final String extension) {
125
126 this.fileExtension = extension;
127 }
128
129
130
131
132
133
134 public final File getActiveFile() {
135
136 return activeFile;
137 }
138
139
140
141
142
143
144 protected final void setActiveFile(
145 final File value) {
146
147 this.activeFile = value;
148
149
150 if (value != null) {
151 originalLastModified = value.lastModified();
152 }
153 }
154
155
156
157
158
159
160 @Override
161 public final String getFileType() {
162
163 return this.fileType;
164 }
165
166
167
168
169
170
171 protected final void setFileType(
172 final String type) {
173
174 this.fileType = type;
175 }
176
177
178
179
180
181
182 public final Boolean isUncertain() {
183
184 return uncertain;
185 }
186
187
188
189
190
191
192 protected final void setUncertain(
193 final Boolean value) {
194
195 this.uncertain = value;
196 }
197
198
199
200
201
202
203
204 protected final String filenameGenerator() {
205
206 String filename;
207 String output = config.getOutputDirectory().getAbsolutePath();
208
209
210 if (isUncertain()) {
211 output = config.getUncertainDirectory().getAbsolutePath();
212 }
213
214
215 if (config.isChangeFilename()) {
216 filename = FilenameGenerator.generateFileName()
217 .concat(".").concat(fileExtension);
218 } else {
219 filename = File.separator + activeFile.getName();
220 }
221
222 LOG.debug("OUTPUT in file {}", output.concat(filename));
223
224 return output.concat(filename);
225 }
226
227
228
229
230
231
232
233
234 @Override
235 public final File anonymize(
236 final File file)
237 throws AnonMedException {
238
239 fileOpen(file);
240
241 if (getActiveFile() != null) {
242
243 processRules();
244
245 if (getConfig().isStrictMode()) {
246 strictMode();
247 }
248
249 String outputFileName = filenameGenerator();
250 fileWrite(outputFileName);
251
252
253 File anonymousFile = new File(outputFileName);
254
255 if (!anonymousFile.setLastModified(originalLastModified)) {
256 LOG.error("ERROR Fail to change last modified time stamp {}", file.getAbsolutePath());
257 return null;
258 }
259
260 return anonymousFile;
261 } else {
262 LOG.error("ERROR Fail to anonymise file {}", file.getAbsolutePath());
263 }
264
265 return null;
266 }
267
268
269
270
271 @Override
272 public final void processRules() {
273
274 List<Rule> profile = config.getProfileBuilder()
275 .getProfile(getFileType());
276 if (profile == null) {
277 LOG.error("No profile for ''{}'' file type.",
278 getFileType());
279 return;
280 }
281
282 for (Rule r : profile) {
283
284 switch (r.getOperation()) {
285 case APPEND_AFTER ->
286 ruleAppendAfter(r);
287
288 case APPEND_BEFORE ->
289 ruleAppendBefore(r);
290
291 case CHANGE ->
292 ruleChange(r);
293
294 case DECRYPT ->
295 ruleDecrypt(r);
296
297 case EMPTY ->
298 ruleEmpty(r);
299
300 case ENCRYPT ->
301 ruleEncrypt(r);
302
303 case EXTERNAL -> {
304 try {
305 ruleExternal(r);
306 } catch (AnonMedException e) {
307 LOG.error("Error during external application call: ", e);
308 }
309 }
310
311 case IDENTIFICATION ->
312 ruleIdentification(r);
313
314 case KEEP ->
315 ruleKeep(r);
316
317 case LOWERCASE ->
318 ruleLowercase(r);
319
320 case NONE ->
321 ruleNone(r);
322
323 case REMOVE ->
324 ruleRemove(r);
325
326 case SPECIFIC ->
327 ruleSpecific(r);
328
329 case SUBSTRING ->
330 ruleSubstring(r);
331
332 case SUBSTITUTE ->
333 ruleSubstitute(r);
334
335 case UPPERCASE ->
336 ruleUppercase(r);
337
338 default ->
339 LOG.error("Not supported requested operation {}", r.getOperation());
340
341 }
342 }
343 }
344
345
346
347
348
349
350
351 @Override
352 public abstract void fileOpen(final File file) throws AnonMedException;
353
354
355
356
357
358
359
360
361 @Override
362 public abstract void fileWrite(final String filename) throws AnonMedException;
363
364
365
366
367
368
369
370 @Override
371 public final void ruleExternal(final Rule rule) throws AnonMedException {
372
373 try {
374 Process p = Runtime.getRuntime().exec(
375 rule.getRule() + " " + getActiveFile()
376 + " " + rule.getNewValue());
377
378 if (p.exitValue() != 0) {
379 throw new AnonMedException(p.exitValue());
380 }
381
382 } catch (IOException e) {
383 LOG.error(null, e);
384 }
385 }
386
387
388
389
390
391
392
393 @Override
394 public void ruleKeep(final Rule rule) {
395
396
397 }
398
399
400
401
402
403
404 @Override
405 public void ruleNone(final Rule rule) {
406
407
408 }
409
410
411
412
413
414
415
416
417 @Override
418 public final Boolean isStrictModeRemove(final String rule) {
419
420 List<Rule> profile
421 = config.getProfileBuilder().getProfile(getFileType());
422
423 for (Rule r : profile) {
424
425 if (testForSameRule(r.getRule(), rule)
426 && (r.getOperation() == Operation.CHANGE
427 || r.getOperation() == Operation.EMPTY
428 || r.getOperation() == Operation.IDENTIFICATION
429 || r.getOperation() == Operation.KEEP
430 || r.getOperation() == Operation.ENCRYPT)) {
431 return Boolean.FALSE;
432 }
433 }
434
435 return Boolean.TRUE;
436 }
437
438
439
440
441
442
443
444
445 private Boolean testForSameRule(final String rule1, final String rule2) {
446
447 if (rule1.equals(rule2)) {
448 return Boolean.TRUE;
449 }
450
451 return Boolean.FALSE;
452 }
453
454
455
456
457
458
459
460
461 protected String makeEncrypt(final Rule rule, final String oldValue) {
462
463 if (rule == null) {
464 throw new IllegalArgumentException("The rule cannot be null");
465 }
466
467 String value = rule.getNewValue();
468 if (value == null || value.isEmpty()) {
469 throw new IllegalArgumentException("Missing or wrong argument(s) for ENCRYPT operation");
470 }
471
472 String[] settings = value.split(",");
473 switch (settings.length) {
474 case 0 ->
475 throw new IllegalArgumentException("Missing argument for ENCRYPT operation");
476 case 1 -> {
477
478 String alg = settings[0].trim().toLowerCase();
479
480 String alias = getAnonymousIdentification();
481
482 LOG.debug("Use encrypt algorithm {} and key with alias {}", alg, alias);
483 return config.getCipherService().encrypt(alg, oldValue, alias);
484 }
485 case 2 -> {
486
487 String alg = settings[0].trim().toLowerCase();
488
489 String alias = settings[1].trim().toLowerCase();
490
491 LOG.debug("Use encrypt algorithm {} and key with alias {}", alg, alias);
492 return config.getCipherService().encrypt(alg, oldValue, alias);
493 }
494 default ->
495 throw new IllegalArgumentException("Only two arguments are allowed per ENCRYPT rule");
496 }
497 }
498
499
500
501
502
503
504
505
506 protected String makeDecrypt(final Rule rule, String encrypted) {
507
508 if (rule == null) {
509 throw new IllegalArgumentException("The rule cannot be null");
510 }
511
512 String value = rule.getNewValue();
513 if (value == null || value.isEmpty()) {
514 throw new IllegalArgumentException("Missing or wrong argument(s) for ENCRYPT operation");
515 }
516
517 String[] settings = value.split(",");
518
519 switch (settings.length) {
520 case 0 ->
521 throw new IllegalArgumentException("Missing argument for ENCRYPT operation");
522 case 1 -> {
523
524 String alg = settings[0].trim().toLowerCase();
525
526 String alias = getAnonymousIdentification();
527
528 LOG.debug("Use decrypt algorithm {} and key with alias {}", alg, alias);
529 return config.getCipherService().decrypt(alg, encrypted, alias);
530
531 }
532 case 2 -> {
533
534 String alg = settings[0].trim().toLowerCase();
535
536 String alias = settings[1].trim().toLowerCase();
537
538 LOG.debug("Use decrypt algorithm {} and key with alias {}", alg, alias);
539 return config.getCipherService().decrypt(alg, encrypted, alias);
540 }
541 default ->
542 throw new IllegalArgumentException("Only two arguments are allowed per ENCRYPT rule");
543 }
544 }
545
546
547
548
549
550
551
552
553 protected void report(Rule rule, String oldValue, String newValue) {
554 AnonMedReport.add(activeFile, rule, oldValue, newValue);
555 }
556
557
558
559
560
561
562
563
564
565 protected String makeSubstitute(Rule rule, String oldValue) {
566
567 if (rule == null || oldValue == null || oldValue.isEmpty()) {
568 LOG.debug("Empty or null value for SUBSTITUTE {}", rule);
569 return "";
570 }
571
572
573 String ruleValue = rule.getNewValue();
574 if (ruleValue != null && !ruleValue.isEmpty()) {
575 String[] conf = ruleValue.split("#");
576
577 switch (conf.length) {
578 case 2 -> {
579 return oldValue.replaceFirst(conf[0], conf[1]);
580 }
581
582 case 3 -> {
583 if (conf[2].equals("g")) {
584
585 return oldValue.replaceAll(conf[0], conf[1]);
586 } else {
587 return oldValue.replaceFirst(conf[0], conf[1]);
588 }
589 }
590
591 default ->
592 LOG.debug("Wrong parameters (e.g. missing '#' separator) for SUBSTITUTE {}", rule);
593 }
594 }
595
596 return oldValue;
597 }
598
599
600
601
602
603
604
605
606 protected String makeSubstring(Rule rule, String oldValue) {
607
608 if (rule == null || oldValue == null || oldValue.isEmpty()) {
609 return "";
610 }
611
612
613 int beginIndex = 0;
614 int endIndex = oldValue.length();
615
616
617 String ruleValue = rule.getNewValue();
618 if (ruleValue != null) {
619 String[] conf = ruleValue.split(",");
620
621 switch (conf.length) {
622 case 0 -> {
623 LOG.error("Missing configuration for the SUBSTRING {}", rule);
624 return oldValue;
625 }
626 case 1 -> {
627 try {
628 beginIndex = Integer.parseInt(conf[0]);
629 return oldValue.substring(beginIndex);
630 } catch (NumberFormatException ex) {
631 LOG.error("Wrong substring number in the rule {}", rule);
632 }
633 }
634 case 2 -> {
635
636 if (conf[0].equals("MIN")) {
637 beginIndex = 0;
638 } else {
639 try {
640 beginIndex = Integer.parseInt(conf[0]);
641 } catch (NumberFormatException ex) {
642 LOG.error("Wrong substring number in the rule {}", rule);
643 }
644 }
645
646
647 if (conf[1].equals("MAX")) {
648 endIndex = oldValue.length();
649 } else {
650 try {
651 endIndex = Integer.parseInt(conf[1]);
652 } catch (NumberFormatException ex) {
653 LOG.error("Wrong substring number in the rule {}", rule);
654 }
655 }
656 }
657 default -> {
658 LOG.error("The rule configuration should have two arguments - numbers, MIN or MAX string instead. Wrong parameters for substring: {}", rule);
659 }
660 }
661 }
662
663
664 if (beginIndex < 0) {
665 beginIndex = 0;
666 }
667 if (endIndex > oldValue.length()) {
668 endIndex = oldValue.length();
669 }
670 return oldValue.substring(beginIndex, endIndex);
671 }
672
673
674
675
676
677
678
679
680 protected String makeLowercase(Rule rule, String oldValue) {
681
682 return oldValue.toLowerCase();
683 }
684
685
686
687
688
689
690
691
692 protected String makeUppercase(Rule rule, String oldValue) {
693
694 return oldValue.toUpperCase();
695 }
696
697 @Override
698 public String getAnonymousIdentification() {
699 return anonymousIdentification;
700 }
701
702 protected void setAnonymousIdentification(String anonymousIdentification) {
703 this.anonymousIdentification = anonymousIdentification;
704 }
705
706 }