18
18
19
19
import static com .couchbase .client .core .util .CbCollections .mapOf ;
20
20
import static com .couchbase .client .java .query .QueryScanConsistency .REQUEST_PLUS ;
21
+ import static org .junit .Assert .assertNull ;
21
22
import static org .junit .jupiter .api .Assertions .assertEquals ;
22
23
import static org .junit .jupiter .api .Assertions .assertFalse ;
23
24
import static org .junit .jupiter .api .Assertions .assertTrue ;
27
28
import java .security .NoSuchAlgorithmException ;
28
29
import java .util .Base64 ;
29
30
import java .util .HashMap ;
31
+ import java .util .List ;
30
32
import java .util .Map ;
31
33
import java .util .Optional ;
32
34
import java .util .UUID ;
33
35
34
36
import javax .crypto .Mac ;
35
37
import javax .crypto .spec .SecretKeySpec ;
36
38
37
- import com .fasterxml .jackson .databind .ObjectMapper ;
38
- import com .fasterxml .jackson .datatype .joda .JodaModule ;
39
- import org .joda .time .DateTime ;
40
39
import org .junit .jupiter .api .BeforeEach ;
41
40
import org .junit .jupiter .api .Test ;
42
41
import org .springframework .beans .factory .annotation .Autowired ;
43
42
import org .springframework .context .annotation .Configuration ;
43
+ import org .springframework .dao .DataRetrievalFailureException ;
44
44
import org .springframework .data .couchbase .CouchbaseClientFactory ;
45
45
import org .springframework .data .couchbase .config .AbstractCouchbaseConfiguration ;
46
46
import org .springframework .data .couchbase .core .CouchbaseTemplate ;
47
47
import org .springframework .data .couchbase .domain .Address ;
48
- import org .springframework .data .couchbase .domain .PersonValueRepository ;
49
48
import org .springframework .data .couchbase .domain .UserEncrypted ;
50
49
import org .springframework .data .couchbase .domain .UserEncryptedRepository ;
51
50
import org .springframework .data .couchbase .repository .config .EnableCouchbaseRepositories ;
64
63
import com .couchbase .client .encryption .EncryptionResult ;
65
64
import com .couchbase .client .encryption .Keyring ;
66
65
import com .couchbase .client .java .env .ClusterEnvironment ;
66
+ import com .couchbase .client .java .json .JsonObject ;
67
+ import com .fasterxml .jackson .databind .ObjectMapper ;
68
+ import com .fasterxml .jackson .datatype .joda .JodaModule ;
67
69
68
70
/**
69
71
* Repository KV tests
@@ -78,13 +80,20 @@ public class CouchbaseRepositoryFieldLevelEncryptionIntegrationTests extends Clu
78
80
@ Autowired UserEncryptedRepository userEncryptedRepository ;
79
81
@ Autowired CouchbaseClientFactory clientFactory ;
80
82
81
- @ Autowired PersonValueRepository personValueRepository ;
82
83
@ Autowired CouchbaseTemplate couchbaseTemplate ;
83
84
84
85
@ BeforeEach
85
86
public void beforeEach () {
86
87
super .beforeEach ();
87
- couchbaseTemplate .removeByQuery (UserEncrypted .class ).withConsistency (REQUEST_PLUS ).all ();
88
+ List <UserEncrypted > users = couchbaseTemplate .findByQuery (UserEncrypted .class ).withConsistency (REQUEST_PLUS ).all ();
89
+ for (UserEncrypted user : users ) {
90
+ couchbaseTemplate .removeById (UserEncrypted .class ).one (user .getId ());
91
+ try { // may have also used upperCased-id
92
+ couchbaseTemplate .removeById (UserEncrypted .class ).one (user .getId ().toUpperCase ());
93
+ } catch (DataRetrievalFailureException iae ) {
94
+ // ignore
95
+ }
96
+ }
88
97
couchbaseTemplate .findByQuery (UserEncrypted .class ).withConsistency (REQUEST_PLUS ).all ();
89
98
}
90
99
@@ -96,46 +105,92 @@ void javaSDKEncryption() {
96
105
@ Test
97
106
@ IgnoreWhen (clusterTypes = ClusterType .MOCKED )
98
107
void saveAndFindById () {
108
+ boolean cleanAfter = true ;
99
109
UserEncrypted user = new UserEncrypted (UUID .randomUUID ().toString (), "saveAndFindById" , "l" , "hello" );
100
110
Address address = new Address (); // plaintext address with encrypted street
101
- // address.setEncStreet("Olcott Street");
102
- address .setStreet ("Castro Street" );
111
+ address .setEncStreet ("Castro Street" );
103
112
address .setCity ("Santa Clara" );
104
113
user .addAddress (address );
105
114
user .setHomeAddress (null );
106
- // cannot set encrypted fields within encrypted objects (i.e. setEncAddress())
107
115
Address encAddress = new Address (); // encrypted address with plaintext street.
108
116
encAddress .setStreet ("Castro St" );
109
117
encAddress .setCity ("Mountain View" );
110
118
user .setEncAddress (encAddress );
111
119
assertFalse (userEncryptedRepository .existsById (user .getId ()));
112
- DateTime beforeDateTime = user .plainDateTime .plus (1 ).minus (1 );
113
- assertEquals (user .plainDateTime , beforeDateTime );
114
- System .err .println ("before: " +beforeDateTime );
115
120
userEncryptedRepository .save (user );
116
- DateTime afterDateTime = user .plainDateTime .plus (1 ).minus (1 );
117
- assertEquals (beforeDateTime , afterDateTime );
118
- System .err .println ("afterDateTime: " +afterDateTime );
119
121
Optional <UserEncrypted > found = userEncryptedRepository .findById (user .getId ());
120
122
assertTrue (found .isPresent ());
121
- System .err .println ("Found: " +found .get ());
122
123
found .ifPresent (u -> assertEquals (user , u ));
123
124
assertTrue (userEncryptedRepository .existsById (user .getId ()));
124
-
125
- clientFactory . getCluster (). bucket ( config (). bucketname ()). defaultCollection (). insert ( user . getId (). toUpperCase (), user );
125
+ clientFactory . getCluster (). bucket ( config (). bucketname ()). defaultCollection (). insert ( user . getId (). toUpperCase (),
126
+ user );
126
127
UserEncrypted sdkUser = clientFactory .getCluster ().bucket (config ().bucketname ()).defaultCollection ()
127
128
.get (user .getId ()).contentAs (UserEncrypted .class );
128
- System .err .println ("user: : " + user );
129
129
sdkUser .setId (user .getId ());
130
130
sdkUser .setVersion (user .getVersion ());
131
- //assertTrue(user.encDateTime.equals( sdkUser.encDateTime));
132
- System .err .println ("sdkUser : " + sdkUser );
133
131
assertEquals (user .plainDateTime , found .get ().plainDateTime );
134
132
assertEquals (user .plainDateTime , sdkUser .plainDateTime );
135
133
assertEquals (user .encDateTime , found .get ().encDateTime );
136
134
assertEquals (user .encDateTime , sdkUser .encDateTime );
135
+ assertEquals (user , found .get ());
137
136
assertEquals (user , sdkUser );
138
- // userEncryptedRepository.delete(user);
137
+ if (cleanAfter ) {
138
+ couchbaseTemplate .removeById (UserEncrypted .class ).one (user .getId ());
139
+ try { // may have also used upperCased-id
140
+ couchbaseTemplate .removeById (UserEncrypted .class ).one (user .getId ().toUpperCase ());
141
+ } catch (DataRetrievalFailureException iae ) {
142
+ // ignore
143
+ }
144
+ }
145
+ }
146
+
147
+ @ Test
148
+ @ IgnoreWhen (clusterTypes = ClusterType .MOCKED )
149
+ void testFromMigration () {
150
+ boolean cleanAfter = true ;
151
+ UserEncrypted user = new UserEncrypted (UUID .randomUUID ().toString (), "testFromMigration" , "l" ,
152
+ "migrating from unencrypted" );
153
+ JsonObject jo = JsonObject .jo ();
154
+ jo .put ("firstname" , user .getFirstname ());
155
+ jo .put ("lastname" , user .getLastname ());
156
+ jo .put ("encryptedField" , user .encryptedField );
157
+ jo .put ("_class" , UserEncrypted .class .getName ());
158
+
159
+ // save it unencrypted
160
+ clientFactory .getCluster ().bucket (config ().bucketname ()).defaultCollection ().insert (user .getId (), jo );
161
+ JsonObject migration = clientFactory .getCluster ().bucket (config ().bucketname ()).defaultCollection ()
162
+ .get (user .getId ()).contentAsObject ();
163
+ assertEquals ("migrating from unencrypted" , migration .get ("encryptedField" ));
164
+ assertNull ( migration .get (CryptoManager .DEFAULT_ENCRYPTER_ALIAS +"encryptedField" ));
165
+
166
+
167
+ // it will be retrieved successfully
168
+ Optional <UserEncrypted > found = userEncryptedRepository .findById (user .getId ());
169
+ assertTrue (found .isPresent ());
170
+ user .setVersion (found .get ().getVersion ());
171
+ found .ifPresent (u -> assertEquals (user , u ));
172
+ // save it encrypted
173
+ UserEncrypted saved = userEncryptedRepository .save (user );
174
+ // it will be retrieved successfully
175
+ Optional <UserEncrypted > foundEnc = userEncryptedRepository .findById (user .getId ());
176
+ assertTrue (foundEnc .isPresent ());
177
+ user .setVersion (foundEnc .get ().getVersion ());
178
+ foundEnc .ifPresent (u -> assertEquals (user , u ));
179
+
180
+ // retrieve it without decrypting
181
+ JsonObject encrypted = clientFactory .getCluster ().bucket (config ().bucketname ()).defaultCollection ()
182
+ .get (user .getId ()).contentAsObject ();
183
+ assertEquals ("myKey" , ((JsonObject )encrypted .get (CryptoManager .DEFAULT_ENCRYPTED_FIELD_NAME_PREFIX +"encryptedField" )).get ("kid" ));
184
+ assertNull ( encrypted .get ("encryptedField" ));
185
+
186
+ if (cleanAfter ) {
187
+ couchbaseTemplate .removeById (UserEncrypted .class ).one (user .getId ());
188
+ try { // may have also used upperCased-id
189
+ couchbaseTemplate .removeById (UserEncrypted .class ).one (user .getId ().toUpperCase ());
190
+ } catch (DataRetrievalFailureException iae ) {
191
+ // ignore
192
+ }
193
+ }
139
194
}
140
195
141
196
@ Configuration
@@ -163,7 +218,7 @@ public String getBucketName() {
163
218
}
164
219
165
220
@ Override
166
- public ObjectMapper couchbaseObjectMapper (CryptoManager cryptoManager ){
221
+ public ObjectMapper couchbaseObjectMapper (CryptoManager cryptoManager ) {
167
222
ObjectMapper om = super .couchbaseObjectMapper (cryptoManager );
168
223
om .registerModule (new JodaModule ());
169
224
return om ;
@@ -182,29 +237,8 @@ protected void configureEnvironment(ClusterEnvironment.Builder builder) {
182
237
@ Override
183
238
protected CryptoManager cryptoManager () {
184
239
185
- Decrypter decrypter = new Decrypter () {
186
- @ Override
187
- public String algorithm () {
188
- return "myAlg" ;
189
- }
190
-
191
- @ Override
192
- public byte [] decrypt (EncryptionResult encrypted ) {
193
- return Base64 .getDecoder ().decode (encrypted .getString ("ciphertext" ));
194
- }
195
- };
196
-
197
- Encrypter encrypter = new Encrypter () {
198
- @ Override
199
- public EncryptionResult encrypt (byte [] plaintext ) {
200
- return EncryptionResult
201
- .fromMap (mapOf ("alg" , "myAlg" , "ciphertext" , Base64 .getEncoder ().encodeToString (plaintext )));
202
- }
203
- };
204
240
Map <String , byte []> keyMap = new HashMap ();
205
- keyMap .put ("myKey" ,
206
- new byte [] { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
207
- 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , });
241
+ keyMap .put ("myKey" , new byte [64 ] /* all zeroes */ );
208
242
Keyring keyring = Keyring .fromMap (keyMap );
209
243
// Provider secProvider;
210
244
AeadAes256CbcHmacSha512Provider provider = AeadAes256CbcHmacSha512Provider .builder ().keyring (keyring )
@@ -213,6 +247,7 @@ public EncryptionResult encrypt(byte[] plaintext) {
213
247
.defaultEncrypter (provider .encrypterForKey ("myKey" )).build ();
214
248
}
215
249
250
+ // not used
216
251
byte [] hmacMe (String cbc_secret_key , String cbc_api_message ) {
217
252
try {
218
253
return hmac ("hmacSHA256" , cbc_secret_key .getBytes ("utf-8" ), cbc_api_message .getBytes ("utf-8" ));
@@ -221,6 +256,7 @@ byte[] hmacMe(String cbc_secret_key, String cbc_api_message) {
221
256
}
222
257
}
223
258
259
+ // not used
224
260
static byte [] hmac (String algorithm , byte [] key , byte [] message )
225
261
throws NoSuchAlgorithmException , InvalidKeyException {
226
262
Mac mac = Mac .getInstance (algorithm );
@@ -229,5 +265,4 @@ static byte[] hmac(String algorithm, byte[] key, byte[] message)
229
265
}
230
266
231
267
}
232
-
233
268
}
0 commit comments