@@ -64,6 +64,116 @@ public void setup() {
64
64
Mockito .when (fileSystem .getEmptyPath ()).thenReturn (empty );
65
65
}
66
66
67
+ @ Test
68
+ @ DisplayName ("Removing a cached cleartext path also removes all cached child paths" )
69
+ public void testInvalidatingCleartextPathCleansCacheFromChildPaths () throws IOException {
70
+ //prepare root
71
+ Path d00 = Mockito .mock (Path .class );
72
+ Mockito .when (dataRoot .resolve ("00" )).thenReturn (d00 );
73
+ Mockito .when (fileNameCryptor .hashDirectoryId ("" )).thenReturn ("0000" );
74
+
75
+ //prepare cleartextDir "/foo"
76
+ Path d0000 = Mockito .mock (Path .class , "d/00/00" );
77
+ Path d0000oof = Mockito .mock (Path .class , "d/00/00/oof.c9r" );
78
+ Path d0000oofdir = Mockito .mock (Path .class , "d/00/00/oof.c9r/dir.c9r" );
79
+ Mockito .when (d00 .resolve ("00" )).thenReturn (d0000 );
80
+ Mockito .when (d0000 .resolve ("oof.c9r" )).thenReturn (d0000oof );
81
+ Mockito .when (d0000oof .resolve ("dir.c9r" )).thenReturn (d0000oofdir );
82
+ Mockito .when (fileNameCryptor .encryptFilename (Mockito .any (), Mockito .eq ("foo" ), Mockito .any ())).thenReturn ("oof" );
83
+ Mockito .when (dirIdProvider .load (d0000oofdir )).thenReturn ("1" );
84
+ Mockito .when (fileNameCryptor .hashDirectoryId ("1" )).thenReturn ("0001" );
85
+
86
+ //prepare cleartextDir "/foo/bar"
87
+ Path d0001 = Mockito .mock (Path .class , "d/00/01" );
88
+ Path d0001rab = Mockito .mock (Path .class , "d/00/01/rab.c9r" );
89
+ Path d0000rabdir = Mockito .mock (Path .class , "d/00/00/rab.c9r/dir.c9r" );
90
+ Mockito .when (d00 .resolve ("01" )).thenReturn (d0001 );
91
+ Mockito .when (d0001 .resolve ("rab.c9r" )).thenReturn (d0001rab );
92
+ Mockito .when (d0001rab .resolve ("dir.c9r" )).thenReturn (d0000rabdir );
93
+ Mockito .when (fileNameCryptor .encryptFilename (Mockito .any (), Mockito .eq ("bar" ), Mockito .any ())).thenReturn ("rab" );
94
+ Mockito .when (dirIdProvider .load (d0000rabdir )).thenReturn ("2" );
95
+ Mockito .when (fileNameCryptor .hashDirectoryId ("2" )).thenReturn ("0002" );
96
+
97
+ Path d0002 = Mockito .mock (Path .class );
98
+ Mockito .when (d00 .resolve ("02" )).thenReturn (d0002 );
99
+
100
+ CryptoPathMapper mapper = new CryptoPathMapper (pathToVault , cryptor , dirIdProvider , longFileNameProvider , vaultConfig );
101
+ //put cleartextpath /foo
102
+ Path cipherFooPath = mapper .getCiphertextDir (fileSystem .getPath ("/foo" )).path ;
103
+ //put cleartextpath /foo/bar
104
+ Path cipherFooBarPath = mapper .getCiphertextDir (fileSystem .getPath ("/foo/bar" )).path ;
105
+ //invalidate /foo
106
+ mapper .invalidatePathMapping (fileSystem .getPath ("/foo" ));
107
+ //cache should miss
108
+ var mapperSpy = Mockito .spy (mapper );
109
+ mapperSpy .getCiphertextDir (fileSystem .getPath ("/foo/bar" ));
110
+ Mockito .verify (mapperSpy , Mockito .atLeast (1 )).getCiphertextFilePath (Mockito .any ());
111
+ }
112
+
113
+ @ Test
114
+ @ DisplayName ("Moving a cached cleartext path also remaps all cached child paths" )
115
+ public void testMovingCleartextPathRemapsCachedChildPaths () throws IOException {
116
+ CryptoPath fooPath = fileSystem .getPath ("/foo" );
117
+ CryptoPath fooBarPath = fileSystem .getPath ("/foo/bar" );
118
+ CryptoPath unkelFooPath = fileSystem .getPath ("/unkel/foo" );
119
+ CryptoPath unkelFooBarPath = fileSystem .getPath ("/unkel/foo/bar" );
120
+ //prepare root
121
+ Path d00 = Mockito .mock (Path .class );
122
+ Mockito .when (dataRoot .resolve ("00" )).thenReturn (d00 );
123
+ Mockito .when (fileNameCryptor .hashDirectoryId ("" )).thenReturn ("0000" );
124
+
125
+ //prepare cleartextDir "/foo"
126
+ Path d0000 = Mockito .mock (Path .class , "d/00/00" );
127
+ Path d0000oof = Mockito .mock (Path .class , "d/00/00/oof.c9r" );
128
+ Path d0000oofdir = Mockito .mock (Path .class , "d/00/00/oof.c9r/dir.c9r" );
129
+ Mockito .when (d00 .resolve ("00" )).thenReturn (d0000 );
130
+ Mockito .when (d0000 .resolve ("oof.c9r" )).thenReturn (d0000oof );
131
+ Mockito .when (d0000oof .resolve ("dir.c9r" )).thenReturn (d0000oofdir );
132
+ Mockito .when (fileNameCryptor .encryptFilename (Mockito .any (), Mockito .eq ("foo" ), Mockito .any ())).thenReturn ("oof" );
133
+ Mockito .when (dirIdProvider .load (d0000oofdir )).thenReturn ("1" );
134
+ Mockito .when (fileNameCryptor .hashDirectoryId ("1" )).thenReturn ("0001" );
135
+
136
+ //prepare cleartextDir "/foo/bar"
137
+ Path d0001 = Mockito .mock (Path .class , "d/00/01" );
138
+ Path d0001rab = Mockito .mock (Path .class , "d/00/01/rab.c9r" );
139
+ Path d0000rabdir = Mockito .mock (Path .class , "d/00/00/rab.c9r/dir.c9r" );
140
+ Mockito .when (d00 .resolve ("01" )).thenReturn (d0001 );
141
+ Mockito .when (d0001 .resolve ("rab.c9r" )).thenReturn (d0001rab );
142
+ Mockito .when (d0001rab .resolve ("dir.c9r" )).thenReturn (d0000rabdir );
143
+ Mockito .when (fileNameCryptor .encryptFilename (Mockito .any (), Mockito .eq ("bar" ), Mockito .any ())).thenReturn ("rab" );
144
+ Mockito .when (dirIdProvider .load (d0000rabdir )).thenReturn ("2" );
145
+ Mockito .when (fileNameCryptor .hashDirectoryId ("2" )).thenReturn ("0002" );
146
+
147
+ Path d0002 = Mockito .mock (Path .class );
148
+ Mockito .when (d00 .resolve ("02" )).thenReturn (d0002 );
149
+
150
+ CryptoPathMapper mapper = new CryptoPathMapper (pathToVault , cryptor , dirIdProvider , longFileNameProvider , vaultConfig );
151
+ //put cleartextpath /foo in cache
152
+ Path cipherFooPath = mapper .getCiphertextDir (fooPath ).path ;
153
+ //put cleartextpath /foo/bar in cache
154
+ Path cipherBarPath = mapper .getCiphertextDir (fooBarPath ).path ;
155
+ //move /foo to /unkel/dinkel/foo/, effectively moving also moving /foo/bar
156
+ mapper .movePathMapping (fooPath , unkelFooPath );
157
+
158
+ //cache should ...
159
+ var mapperSpy = Mockito .spy (mapper );
160
+ var someCiphertextFilePath = Mockito .mock (CiphertextFilePath .class );
161
+ var someCiphertextDirFilePath = Mockito .mock (Path .class );
162
+ var someCipherDirObj = Mockito .mock (CryptoPathMapper .CiphertextDirectory .class );
163
+ Mockito .doReturn (someCiphertextFilePath ).when (mapperSpy ).getCiphertextFilePath (fooBarPath );
164
+ Mockito .doReturn (someCiphertextDirFilePath ).when (someCiphertextFilePath ).getDirFilePath ();
165
+ Mockito .doReturn (someCipherDirObj ).when (mapperSpy ).resolveDirectory (someCiphertextDirFilePath );
166
+
167
+ //... succeed for /unkel/foo/ and /unkel/foo/bar
168
+ mapperSpy .getCiphertextDir (unkelFooPath );
169
+ mapperSpy .getCiphertextDir (unkelFooBarPath );
170
+ Mockito .verify (mapperSpy , Mockito .never ()).getCiphertextFilePath (Mockito .any ());
171
+
172
+ //...miss and return our mocked cipherDirObj
173
+ var actualCipherDirObj = mapperSpy .getCiphertextDir (fooBarPath );
174
+ Assertions .assertEquals (someCipherDirObj , actualCipherDirObj );
175
+ }
176
+
67
177
@ Test
68
178
public void testPathEncryptionForRoot () throws IOException {
69
179
Path d00 = Mockito .mock (Path .class );
0 commit comments