From 13a3e8f2405dfc5701f36afc150c1450520fc010 Mon Sep 17 00:00:00 2001 From: Nidhi Date: Thu, 8 Apr 2021 16:58:45 +0530 Subject: [PATCH 1/4] Applying AOP encryption --- .../encryption/EncryptionHandler.java | 258 +++++++++--------- .../external/models/AuthenticationDTO.java | 23 -- .../models/AuthenticationResponse.java | 5 + .../com/appsmith/external/models/DBAuth.java | 29 +- .../com/appsmith/external/models/OAuth2.java | 67 +---- .../services/DatasourceContextService.java | 3 - .../DatasourceContextServiceImpl.java | 23 -- .../server/services/DatasourceService.java | 5 +- .../services/DatasourceServiceImpl.java | 78 +----- .../server/services/NewActionServiceImpl.java | 11 - .../DatasourceStructureSolution.java | 28 +- .../solutions/ExamplesOrganizationCloner.java | 3 - .../DatasourceContextServiceTest.java | 95 ++----- .../services/DatasourceServiceTest.java | 3 - 14 files changed, 177 insertions(+), 454 deletions(-) diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/annotations/encryption/EncryptionHandler.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/annotations/encryption/EncryptionHandler.java index b2d9225618..4d5cac48bc 100644 --- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/annotations/encryption/EncryptionHandler.java +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/annotations/encryption/EncryptionHandler.java @@ -50,151 +50,153 @@ public class EncryptionHandler { // If it is not known, scan each field for annotation or Appsmith type List finalCandidateFields = new ArrayList<>(); - ReflectionUtils.doWithFields(sourceClass, field -> { - if (field.getAnnotation(Encrypted.class) != null) { - CandidateField candidateField = new CandidateField(field, CandidateField.Type.ANNOTATED_FIELD); - finalCandidateFields.add(candidateField); - } else if (AppsmithDomain.class.isAssignableFrom(field.getType())) { - CandidateField candidateField = null; - - field.setAccessible(true); - Object fieldValue = ReflectionUtils.getField(field, source); - if (fieldValue == null) { - if (this.encryptedFieldsMap.containsKey(field.getType())) { - // If this field is null, but the cache has a non-empty list of candidates already, - // then this is an appsmith field with known annotations - candidateField = new CandidateField(field, CandidateField.Type.APPSMITH_FIELD_KNOWN); - } else { - // If it is null and the cache is not aware of the field, this is still a prospect, - // but with an unknown type (could also be polymorphic) - candidateField = new CandidateField(field, CandidateField.Type.APPSMITH_FIELD_UNKNOWN); - } - } else { - // If an object exists, check if the object type is the same as the field type - CandidateField.Type appsmithFieldType; - if (field.getType().getCanonicalName().equals(fieldValue.getClass().getCanonicalName())) { - // If they match, then this is going to be an appsmith known field - appsmithFieldType = CandidateField.Type.APPSMITH_FIELD_KNOWN; - } else { - // If not, then this field is polymorphic, - // it will need to be checked for type every time - appsmithFieldType = CandidateField.Type.APPSMITH_FIELD_POLYMORPHIC; - } - // Now, go into field type and repeat - List candidateFieldsForType = findCandidateFieldsForType(fieldValue); - - if (appsmithFieldType.equals(CandidateField.Type.APPSMITH_FIELD_POLYMORPHIC) - || !candidateFieldsForType.isEmpty()) { - // This type only qualifies as a candidate if it is polymorphic, - // or has a list of candidates - candidateField = new CandidateField(field, appsmithFieldType); - } - } - - field.setAccessible(false); - if (candidateField != null) { - // This will only ever be null if the field value is populated, - // and is known to be a non-encryption related field + synchronized (sourceClass) { + ReflectionUtils.doWithFields(sourceClass, field -> { + if (field.getAnnotation(Encrypted.class) != null) { + CandidateField candidateField = new CandidateField(field, CandidateField.Type.ANNOTATED_FIELD); finalCandidateFields.add(candidateField); - } - } else if (Collection.class.isAssignableFrom(field.getType()) && - field.getGenericType() instanceof ParameterizedType) { - // If this is a collection, check if the Type parameter is an AppsmithDomain - Type[] typeArguments; - ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType(); - typeArguments = parameterizedType.getActualTypeArguments(); - Class subFieldType = (Class) typeArguments[0]; - - if (this.encryptedFieldsMap.containsKey(subFieldType)) { - // This is a known type, it should necessarily be of AppsmithDomain type - assert AppsmithDomain.class.isAssignableFrom(subFieldType); - final List existingSubTypeCandidates = this.encryptedFieldsMap.get(subFieldType); - if (!existingSubTypeCandidates.isEmpty()) { - finalCandidateFields.add(new CandidateField(field, CandidateField.Type.APPSMITH_LIST_KNOWN)); - } - } else if (AppsmithDomain.class.isAssignableFrom(subFieldType)) { - // If the type is not known, then this is either not parsed yet, or has polymorphic implementations + } else if (AppsmithDomain.class.isAssignableFrom(field.getType())) { + CandidateField candidateField = null; field.setAccessible(true); Object fieldValue = ReflectionUtils.getField(field, source); - List list = (List) fieldValue; - - if (list == null || list.isEmpty()) { - finalCandidateFields.add(new CandidateField(field, CandidateField.Type.APPSMITH_LIST_UNKNOWN)); + if (fieldValue == null) { + if (this.encryptedFieldsMap.containsKey(field.getType())) { + // If this field is null, but the cache has a non-empty list of candidates already, + // then this is an appsmith field with known annotations + candidateField = new CandidateField(field, CandidateField.Type.APPSMITH_FIELD_KNOWN); + } else { + // If it is null and the cache is not aware of the field, this is still a prospect, + // but with an unknown type (could also be polymorphic) + candidateField = new CandidateField(field, CandidateField.Type.APPSMITH_FIELD_UNKNOWN); + } } else { - for (final Object o : list) { - if (o == null) { - continue; - } - if (o.getClass().getCanonicalName().equals(subFieldType.getTypeName())) { - final List candidateFieldsForListMember = findCandidateFieldsForType(o); - if (candidateFieldsForListMember != null && !candidateFieldsForListMember.isEmpty()) { - finalCandidateFields.add(new CandidateField(field, CandidateField.Type.APPSMITH_LIST_KNOWN)); - } - } else { - finalCandidateFields.add(new CandidateField(field, CandidateField.Type.APPSMITH_LIST_POLYMORPHIC)); - } - break; + // If an object exists, check if the object type is the same as the field type + CandidateField.Type appsmithFieldType; + if (field.getType().getCanonicalName().equals(fieldValue.getClass().getCanonicalName())) { + // If they match, then this is going to be an appsmith known field + appsmithFieldType = CandidateField.Type.APPSMITH_FIELD_KNOWN; + } else { + // If not, then this field is polymorphic, + // it will need to be checked for type every time + appsmithFieldType = CandidateField.Type.APPSMITH_FIELD_POLYMORPHIC; + } + // Now, go into field type and repeat + List candidateFieldsForType = findCandidateFieldsForType(fieldValue); + + if (appsmithFieldType.equals(CandidateField.Type.APPSMITH_FIELD_POLYMORPHIC) + || !candidateFieldsForType.isEmpty()) { + // This type only qualifies as a candidate if it is polymorphic, + // or has a list of candidates + candidateField = new CandidateField(field, appsmithFieldType); } } + field.setAccessible(false); - - } - // TODO Add support for nested collections - } else if (Map.class.isAssignableFrom(field.getType()) && - field.getGenericType() instanceof ParameterizedType) { - Type[] typeArguments; - ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType(); - typeArguments = parameterizedType.getActualTypeArguments(); - Class subFieldType = (Class) typeArguments[1]; - - if (this.encryptedFieldsMap.containsKey(subFieldType)) { - // This is a known type, it should necessarily be of AppsmithDomain type - assert AppsmithDomain.class.isAssignableFrom(subFieldType); - final List existingSubTypeCandidates = this.encryptedFieldsMap.get(subFieldType); - if (!existingSubTypeCandidates.isEmpty()) { - finalCandidateFields.add(new CandidateField(field, CandidateField.Type.APPSMITH_MAP_KNOWN)); + if (candidateField != null) { + // This will only ever be null if the field value is populated, + // and is known to be a non-encryption related field + finalCandidateFields.add(candidateField); } - } else if (AppsmithDomain.class.isAssignableFrom(subFieldType)) { - // If the type is not known, then this is either not parsed yet, or has polymorphic implementations + } else if (Collection.class.isAssignableFrom(field.getType()) && + field.getGenericType() instanceof ParameterizedType) { + // If this is a collection, check if the Type parameter is an AppsmithDomain + Type[] typeArguments; + ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType(); + typeArguments = parameterizedType.getActualTypeArguments(); + Class subFieldType = (Class) typeArguments[0]; - field.setAccessible(true); - Object fieldValue = ReflectionUtils.getField(field, source); - Map map = (Map) fieldValue; - if (map == null || map.isEmpty()) { - finalCandidateFields.add(new CandidateField(field, CandidateField.Type.APPSMITH_MAP_UNKNOWN)); - } else { - for (Map.Entry entry : map.entrySet()) { - final Object value = entry.getValue(); - if (value == null) { - continue; - } - if (value.getClass().getCanonicalName().equals(subFieldType.getTypeName())) { - final List candidateFieldsForListMember = findCandidateFieldsForType(value); - if (candidateFieldsForListMember != null && !candidateFieldsForListMember.isEmpty()) { - finalCandidateFields.add(new CandidateField(field, CandidateField.Type.APPSMITH_MAP_KNOWN)); - } - } else { - finalCandidateFields.add(new CandidateField(field, CandidateField.Type.APPSMITH_MAP_POLYMORPHIC)); - } - break; + if (this.encryptedFieldsMap.containsKey(subFieldType)) { + // This is a known type, it should necessarily be of AppsmithDomain type + assert AppsmithDomain.class.isAssignableFrom(subFieldType); + final List existingSubTypeCandidates = this.encryptedFieldsMap.get(subFieldType); + if (!existingSubTypeCandidates.isEmpty()) { + finalCandidateFields.add(new CandidateField(field, CandidateField.Type.APPSMITH_LIST_KNOWN)); } - } - field.setAccessible(false); - } - } + } else if (AppsmithDomain.class.isAssignableFrom(subFieldType)) { + // If the type is not known, then this is either not parsed yet, or has polymorphic implementations - }, field -> field.getAnnotation(Encrypted.class) != null || - AppsmithDomain.class.isAssignableFrom(field.getType()) || - Collection.class.isAssignableFrom(field.getType()) || - Map.class.isAssignableFrom(field.getType())); + field.setAccessible(true); + Object fieldValue = ReflectionUtils.getField(field, source); + List list = (List) fieldValue; + + if (list == null || list.isEmpty()) { + finalCandidateFields.add(new CandidateField(field, CandidateField.Type.APPSMITH_LIST_UNKNOWN)); + } else { + for (final Object o : list) { + if (o == null) { + continue; + } + if (o.getClass().getCanonicalName().equals(subFieldType.getTypeName())) { + final List candidateFieldsForListMember = findCandidateFieldsForType(o); + if (candidateFieldsForListMember != null && !candidateFieldsForListMember.isEmpty()) { + finalCandidateFields.add(new CandidateField(field, CandidateField.Type.APPSMITH_LIST_KNOWN)); + } + } else { + finalCandidateFields.add(new CandidateField(field, CandidateField.Type.APPSMITH_LIST_POLYMORPHIC)); + } + break; + } + } + field.setAccessible(false); + + } + // TODO Add support for nested collections + } else if (Map.class.isAssignableFrom(field.getType()) && + field.getGenericType() instanceof ParameterizedType) { + Type[] typeArguments; + ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType(); + typeArguments = parameterizedType.getActualTypeArguments(); + Class subFieldType = (Class) typeArguments[1]; + + if (this.encryptedFieldsMap.containsKey(subFieldType)) { + // This is a known type, it should necessarily be of AppsmithDomain type + assert AppsmithDomain.class.isAssignableFrom(subFieldType); + final List existingSubTypeCandidates = this.encryptedFieldsMap.get(subFieldType); + if (!existingSubTypeCandidates.isEmpty()) { + finalCandidateFields.add(new CandidateField(field, CandidateField.Type.APPSMITH_MAP_KNOWN)); + } + } else if (AppsmithDomain.class.isAssignableFrom(subFieldType)) { + // If the type is not known, then this is either not parsed yet, or has polymorphic implementations + + field.setAccessible(true); + Object fieldValue = ReflectionUtils.getField(field, source); + Map map = (Map) fieldValue; + if (map == null || map.isEmpty()) { + finalCandidateFields.add(new CandidateField(field, CandidateField.Type.APPSMITH_MAP_UNKNOWN)); + } else { + for (Map.Entry entry : map.entrySet()) { + final Object value = entry.getValue(); + if (value == null) { + continue; + } + if (value.getClass().getCanonicalName().equals(subFieldType.getTypeName())) { + final List candidateFieldsForListMember = findCandidateFieldsForType(value); + if (candidateFieldsForListMember != null && !candidateFieldsForListMember.isEmpty()) { + finalCandidateFields.add(new CandidateField(field, CandidateField.Type.APPSMITH_MAP_KNOWN)); + } + } else { + finalCandidateFields.add(new CandidateField(field, CandidateField.Type.APPSMITH_MAP_POLYMORPHIC)); + } + break; + } + } + field.setAccessible(false); + } + } + + }, field -> field.getAnnotation(Encrypted.class) != null || + AppsmithDomain.class.isAssignableFrom(field.getType()) || + Collection.class.isAssignableFrom(field.getType()) || + Map.class.isAssignableFrom(field.getType())); + } // Update cache for next use encryptedFieldsMap.put(sourceClass, finalCandidateFields); return finalCandidateFields; } - boolean convertEncryption(Object source, Function transformer) { + synchronized boolean convertEncryption(Object source, Function transformer) { if (source == null) { return false; } @@ -291,8 +293,8 @@ public class EncryptionHandler { } } } - field.setAccessible(false); } + } return hasEncryptedFields; diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/AuthenticationDTO.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/AuthenticationDTO.java index df2b41c7c1..35611963fa 100644 --- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/AuthenticationDTO.java +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/AuthenticationDTO.java @@ -1,6 +1,5 @@ package com.appsmith.external.models; -import com.appsmith.external.annotations.encryption.Encrypted; import com.appsmith.external.constants.Authentication; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonSubTypes; @@ -10,8 +9,6 @@ import lombok.Getter; import lombok.Setter; import org.springframework.data.annotation.Transient; -import java.util.Collections; -import java.util.Map; import java.util.Set; @Getter @@ -43,24 +40,4 @@ public class AuthenticationDTO implements AppsmithDomain { @JsonIgnore AuthenticationResponse authenticationResponse; - - @JsonIgnore - public Map getEncryptionFields() { - return Collections.emptyMap(); - } - - public void setEncryptionFields(Map encryptedFields) { - // This is supposed to be overridden by implementations. - } - - @JsonIgnore - public Set getEmptyEncryptionFields() { - return Collections.emptySet(); - } - - @JsonIgnore - public Boolean isEncrypted() { - return this.isEncrypted; - } - } diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/AuthenticationResponse.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/AuthenticationResponse.java index 4136869d63..d1453b8aff 100644 --- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/AuthenticationResponse.java +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/AuthenticationResponse.java @@ -1,5 +1,6 @@ package com.appsmith.external.models; +import com.appsmith.external.annotations.encryption.Encrypted; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; @@ -12,13 +13,17 @@ import java.time.Instant; @NoArgsConstructor @AllArgsConstructor public class AuthenticationResponse implements AppsmithDomain { + + @Encrypted String token; + @Encrypted String refreshToken; Instant issuedAt; Instant expiresAt; + @Encrypted Object tokenResponse; } diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/DBAuth.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/DBAuth.java index bd8c2d1ddc..b0785b1765 100644 --- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/DBAuth.java +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/DBAuth.java @@ -1,8 +1,8 @@ package com.appsmith.external.models; import com.appsmith.external.annotations.documenttype.DocumentType; +import com.appsmith.external.annotations.encryption.Encrypted; import com.appsmith.external.constants.Authentication; -import com.appsmith.external.constants.FieldName; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Getter; @@ -10,9 +10,6 @@ import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; -import java.util.Map; -import java.util.Set; - @Getter @Setter @ToString @@ -29,31 +26,9 @@ public class DBAuth extends AuthenticationDTO { String username; + @Encrypted @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) String password; String databaseName; - - @Override - public Map getEncryptionFields() { - if (this.password != null && !this.password.isBlank()) { - return Map.of(FieldName.PASSWORD, this.password); - } - return Map.of(); - } - - @Override - public void setEncryptionFields(Map encryptedFields) { - if (encryptedFields != null && encryptedFields.containsKey(FieldName.PASSWORD)) { - this.password = encryptedFields.get(FieldName.PASSWORD); - } - } - - @Override - public Set getEmptyEncryptionFields() { - if (this.password == null || this.password.isBlank()) { - return Set.of(FieldName.PASSWORD); - } - return Set.of(); - } } diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/OAuth2.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/OAuth2.java index 8370d52322..732e0a24c6 100644 --- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/OAuth2.java +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/OAuth2.java @@ -1,8 +1,8 @@ package com.appsmith.external.models; import com.appsmith.external.annotations.documenttype.DocumentType; +import com.appsmith.external.annotations.encryption.Encrypted; import com.appsmith.external.constants.Authentication; -import com.appsmith.external.constants.FieldName; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Getter; @@ -14,9 +14,6 @@ import org.springframework.data.annotation.Transient; import org.springframework.util.StringUtils; import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -42,6 +39,7 @@ public class OAuth2 extends AuthenticationDTO { String clientId; + @Encrypted @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) String clientSecret; @@ -58,7 +56,6 @@ public class OAuth2 extends AuthenticationDTO { Set customTokenParameters; - public String getScopeString() { if (scopeString != null && !scopeString.isBlank()) { return scopeString; @@ -76,64 +73,4 @@ public class OAuth2 extends AuthenticationDTO { .collect(Collectors.toSet()); } } - - @Override - public Map getEncryptionFields() { - Map map = new HashMap<>(); - if (this.clientSecret != null) { - map.put(FieldName.CLIENT_SECRET, this.clientSecret); - } - if (this.getAuthenticationResponse() != null) { - if (this.authenticationResponse.getToken() != null) { - map.put(FieldName.TOKEN, this.authenticationResponse.getToken()); - } - if (this.authenticationResponse.getRefreshToken() != null) { - map.put(FieldName.REFRESH_TOKEN, this.authenticationResponse.getRefreshToken()); - } - if (this.authenticationResponse.getTokenResponse() != null) { - map.put(FieldName.TOKEN_RESPONSE, String.valueOf(this.authenticationResponse.getTokenResponse())); - } - } - return map; - } - - @Override - public void setEncryptionFields(Map encryptedFields) { - if (encryptedFields != null) { - if (encryptedFields.containsKey(FieldName.CLIENT_SECRET)) { - this.clientSecret = encryptedFields.get(FieldName.CLIENT_SECRET); - } - if (encryptedFields.containsKey(FieldName.TOKEN)) { - this.authenticationResponse.setToken(encryptedFields.get(FieldName.TOKEN)); - } - if (encryptedFields.containsKey(FieldName.REFRESH_TOKEN)) { - this.authenticationResponse.setRefreshToken(encryptedFields.get(FieldName.REFRESH_TOKEN)); - } - if (encryptedFields.containsKey(FieldName.TOKEN_RESPONSE)) { - this.authenticationResponse.setTokenResponse(encryptedFields.get(FieldName.TOKEN_RESPONSE)); - } - } - } - - @Override - public Set getEmptyEncryptionFields() { - Set set = new HashSet<>(); - if (this.clientSecret == null || this.clientSecret.isEmpty()) { - set.add(FieldName.CLIENT_SECRET); - } - if (this.getAuthenticationResponse() != null) { - if (this.authenticationResponse.getToken() == null || this.authenticationResponse.getToken().isEmpty()) { - set.add(FieldName.TOKEN); - } - if (this.authenticationResponse.getRefreshToken() == null || this.authenticationResponse.getRefreshToken().isEmpty()) { - set.add(FieldName.REFRESH_TOKEN); - } - if (this.authenticationResponse.getTokenResponse() == null || (String.valueOf(this.authenticationResponse.getTokenResponse())).isEmpty()) { - set.add(FieldName.TOKEN_RESPONSE); - } - } - return set; - } - - } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/DatasourceContextService.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/DatasourceContextService.java index 231d38168f..98ce6ef748 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/DatasourceContextService.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/DatasourceContextService.java @@ -1,6 +1,5 @@ package com.appsmith.server.services; -import com.appsmith.external.models.AuthenticationDTO; import com.appsmith.server.domains.Datasource; import com.appsmith.server.domains.DatasourceContext; import reactor.core.publisher.Mono; @@ -22,6 +21,4 @@ public interface DatasourceContextService { Mono retryOnce(Datasource datasource, Function> task); Mono deleteDatasourceContext(String datasourceId); - - AuthenticationDTO decryptSensitiveFields(AuthenticationDTO authenticationDTO); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/DatasourceContextServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/DatasourceContextServiceImpl.java index d4b27d3693..e48215f931 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/DatasourceContextServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/DatasourceContextServiceImpl.java @@ -1,6 +1,5 @@ package com.appsmith.server.services; -import com.appsmith.external.models.AuthenticationDTO; import com.appsmith.external.exceptions.pluginExceptions.StaleConnectionException; import com.appsmith.external.models.UpdatableConnection; import com.appsmith.external.plugins.PluginExecutor; @@ -18,7 +17,6 @@ import java.time.Instant; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; -import java.util.stream.Collectors; import static com.appsmith.server.acl.AclPermission.EXECUTE_DATASOURCES; @@ -88,13 +86,6 @@ public class DatasourceContextServiceImpl implements DatasourceContextService { .flatMap(objects -> { Datasource datasource1 = objects.getT1(); - // If authentication exists for the datasource, decrypt the fields - if (datasource1.getDatasourceConfiguration() != null && - datasource1.getDatasourceConfiguration().getAuthentication() != null) { - AuthenticationDTO authentication = datasource1.getDatasourceConfiguration().getAuthentication(); - datasource1.getDatasourceConfiguration().setAuthentication(decryptSensitiveFields(authentication)); - } - PluginExecutor pluginExecutor = objects.getT2(); if (isStale) { @@ -185,18 +176,4 @@ public class DatasourceContextServiceImpl implements DatasourceContextService { return datasourceContextMap.remove(datasourceId); }); } - - @Override - public AuthenticationDTO decryptSensitiveFields(AuthenticationDTO authentication) { - if (authentication != null && Boolean.TRUE.equals(authentication.isEncrypted())) { - Map decryptedFields = authentication.getEncryptionFields().entrySet().stream() - .filter(e -> e.getValue() != null) - .collect(Collectors.toMap( - Map.Entry::getKey, - e -> encryptionService.decryptString(e.getValue()))); - authentication.setEncryptionFields(decryptedFields); - authentication.setIsEncrypted(false); - } - return authentication; - } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/DatasourceService.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/DatasourceService.java index 3ac255de0b..a497b0b9ed 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/DatasourceService.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/DatasourceService.java @@ -1,6 +1,5 @@ package com.appsmith.server.services; -import com.appsmith.external.models.AuthenticationDTO; import com.appsmith.external.models.DatasourceTestResult; import com.appsmith.server.acl.AclPermission; import com.appsmith.server.domains.Datasource; @@ -30,7 +29,5 @@ public interface DatasourceService extends CrudService { Flux saveAll(List datasourceList); - AuthenticationDTO encryptAuthenticationFields(AuthenticationDTO authentication); - - public Mono populateHintMessages(Datasource datasource); + Mono populateHintMessages(Datasource datasource); } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/DatasourceServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/DatasourceServiceImpl.java index e79882e143..c3ba9627b7 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/DatasourceServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/DatasourceServiceImpl.java @@ -1,6 +1,7 @@ package com.appsmith.server.services; -import com.appsmith.external.models.AuthenticationDTO; +import com.appsmith.external.helpers.BeanCopyUtils; +import com.appsmith.external.helpers.MustacheHelper; import com.appsmith.external.models.DatasourceConfiguration; import com.appsmith.external.models.DatasourceTestResult; import com.appsmith.external.models.Endpoint; @@ -16,7 +17,6 @@ import com.appsmith.server.domains.Plugin; import com.appsmith.server.domains.User; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; -import com.appsmith.external.helpers.MustacheHelper; import com.appsmith.server.helpers.PluginExecutorHelper; import com.appsmith.server.repositories.DatasourceRepository; import com.appsmith.server.repositories.NewActionRepository; @@ -36,14 +36,13 @@ import javax.validation.Validator; import javax.validation.constraints.NotNull; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import static com.appsmith.external.helpers.BeanCopyUtils.copyNestedNonNullProperties; import static com.appsmith.server.acl.AclPermission.MANAGE_DATASOURCES; import static com.appsmith.server.acl.AclPermission.ORGANIZATION_MANAGE_APPLICATIONS; import static com.appsmith.server.acl.AclPermission.ORGANIZATION_READ_APPLICATIONS; -import static com.appsmith.external.helpers.BeanCopyUtils.copyNestedNonNullProperties; @Slf4j @Service @@ -84,17 +83,6 @@ public class DatasourceServiceImpl extends BaseService getById(String s) { - return super.getById(s).flatMap(datasource -> { - if (datasource.getDatasourceConfiguration().getAuthentication() != null && - Boolean.TRUE.equals(datasource.getDatasourceConfiguration().getAuthentication().isEncrypted())) { - datasource.getDatasourceConfiguration().setAuthentication(decryptSensitiveFields(datasource.getDatasourceConfiguration().getAuthentication())); - } - return Mono.just(datasource); - }); - } - @Override public Mono create(@NotNull Datasource datasource) { if (datasource.getId() != null) { @@ -104,11 +92,6 @@ public class DatasourceServiceImpl extends BaseService datasourceMono = Mono.just(datasource); if (StringUtils.isEmpty(datasource.getName())) { @@ -190,11 +173,6 @@ public class DatasourceServiceImpl extends BaseService encryptedFields = authentication.getEncryptionFields().entrySet().stream() - .filter(e -> e.getValue() != null) - .collect(Collectors.toMap( - Map.Entry::getKey, - e -> encryptionService.encryptString(e.getValue()))); - if (!encryptedFields.isEmpty()) { - authentication.setEncryptionFields(encryptedFields); - authentication.setIsEncrypted(true); - } - } - return authentication; - } - @Override public Mono validateDatasource(Datasource datasource) { Set invalids = new HashSet<>(); @@ -316,22 +277,18 @@ public class DatasourceServiceImpl extends BaseService testDatasource(Datasource datasource) { - Mono datasourceMono = null; + Mono datasourceMono = Mono.just(datasource); // Fetch any fields that maybe encrypted from the db if the datasource being tested does not have those fields set. // This scenario would happen whenever an existing datasource is being tested and no changes are present in the // encrypted field (because encrypted fields are not sent over the network after encryption back to the client if (datasource.getId() != null && datasource.getDatasourceConfiguration() != null && datasource.getDatasourceConfiguration().getAuthentication() != null) { - Set emptyFields = datasource.getDatasourceConfiguration().getAuthentication().getEmptyEncryptionFields(); - if (emptyFields != null && !emptyFields.isEmpty()) { - - datasourceMono = getById(datasource.getId()) - .switchIfEmpty(Mono.just(datasource)); - } - } - - if (datasourceMono == null) { - datasourceMono = Mono.just(datasource); + datasourceMono = getById(datasource.getId()) + .map(datasource1 -> { + BeanCopyUtils.copyNestedNonNullProperties(datasource, datasource1); + return datasource1; + }) + .switchIfEmpty(Mono.just(datasource)); } return datasourceMono @@ -424,19 +381,4 @@ public class DatasourceServiceImpl extends BaseService repository.archive(toDelete).thenReturn(toDelete)) .flatMap(analyticsService::sendDeleteEvent); } - - private AuthenticationDTO decryptSensitiveFields(AuthenticationDTO authentication) { - if (authentication != null && Boolean.TRUE.equals(authentication.isEncrypted())) { - Map decryptedFields = authentication.getEncryptionFields().entrySet().stream() - .filter(e -> e.getValue() != null) - .collect(Collectors.toMap( - Map.Entry::getKey, - e -> encryptionService.decryptString(e.getValue()))); - authentication.setEncryptionFields(decryptedFields); - authentication.setIsEncrypted(false); - } - return authentication; - } - - } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NewActionServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NewActionServiceImpl.java index 9a665ee69c..6cfe65758b 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NewActionServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NewActionServiceImpl.java @@ -294,17 +294,6 @@ public class NewActionServiceImpl extends BaseService datasourceMono; if (action.getDatasource().getId() == null) { - if (action.getDatasource().getDatasourceConfiguration() != null && - action.getDatasource().getDatasourceConfiguration().getAuthentication() != null) { - action.getDatasource() - .getDatasourceConfiguration() - .setAuthentication(datasourceService.encryptAuthenticationFields(action - .getDatasource() - .getDatasourceConfiguration() - .getAuthentication() - )); - } - datasourceMono = Mono.just(action.getDatasource()) .flatMap(datasourceService::validateDatasource); } else { diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/DatasourceStructureSolution.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/DatasourceStructureSolution.java index 82e14d9a63..81402e5621 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/DatasourceStructureSolution.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/DatasourceStructureSolution.java @@ -1,11 +1,11 @@ package com.appsmith.server.solutions; -import com.appsmith.external.models.AuthenticationDTO; -import com.appsmith.external.models.DatasourceStructure; import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError; import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException; import com.appsmith.external.exceptions.pluginExceptions.StaleConnectionException; +import com.appsmith.external.models.DatasourceStructure; import com.appsmith.external.plugins.PluginExecutor; +import com.appsmith.external.services.EncryptionService; import com.appsmith.server.constants.FieldName; import com.appsmith.server.domains.Datasource; import com.appsmith.server.exceptions.AppsmithError; @@ -14,7 +14,6 @@ import com.appsmith.server.helpers.PluginExecutorHelper; import com.appsmith.server.repositories.CustomDatasourceRepository; import com.appsmith.server.services.DatasourceContextService; import com.appsmith.server.services.DatasourceService; -import com.appsmith.external.services.EncryptionService; import com.appsmith.server.services.PluginService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -23,9 +22,7 @@ import org.springframework.util.CollectionUtils; import reactor.core.publisher.Mono; import java.time.Duration; -import java.util.Map; import java.util.concurrent.TimeoutException; -import java.util.stream.Collectors; @Component @RequiredArgsConstructor @@ -73,8 +70,6 @@ public class DatasourceStructureSolution { return Mono.just(datasource.getStructure()); } - decryptEncryptedFieldsInDatasource(datasource); - // This mono, when computed, will load the structure of the datasource by calling the plugin method. return pluginExecutorHelper .getPluginExecutor(pluginService.findById(datasource.getPluginId())) @@ -123,23 +118,4 @@ public class DatasourceStructureSolution { : datasourceRepository.saveStructure(datasource.getId(), structure).thenReturn(structure) ); } - - private Datasource decryptEncryptedFieldsInDatasource(Datasource datasource) { - // If datasource has encrypted fields, decrypt and set it in the datasource. - if (datasource.getDatasourceConfiguration() != null) { - AuthenticationDTO authentication = datasource.getDatasourceConfiguration().getAuthentication(); - if (authentication != null && authentication.isEncrypted()) { - Map decryptedFields = authentication.getEncryptionFields().entrySet().stream() - .filter(e -> e.getValue() != null) - .collect(Collectors.toMap( - Map.Entry::getKey, - e -> encryptionService.decryptString(e.getValue()))); - authentication.setEncryptionFields(decryptedFields); - authentication.setIsEncrypted(false); - } - } - - return datasource; - } - } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ExamplesOrganizationCloner.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ExamplesOrganizationCloner.java index f7ee8a397c..85cd983fa8 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ExamplesOrganizationCloner.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ExamplesOrganizationCloner.java @@ -402,9 +402,6 @@ public class ExamplesOrganizationCloner { makePristine(templateDatasource); templateDatasource.setOrganizationId(toOrganizationId); - if (authentication != null) { - datasourceContextService.decryptSensitiveFields(authentication); - } return createSuffixedDatasource(templateDatasource); })); diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceContextServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceContextServiceTest.java index 474af7bfa8..745f1eb6e9 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceContextServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceContextServiceTest.java @@ -1,28 +1,18 @@ package com.appsmith.server.services; -import com.appsmith.external.models.DBAuth; -import com.appsmith.external.models.DatasourceConfiguration; import com.appsmith.server.acl.AclPermission; -import com.appsmith.server.domains.Datasource; import com.appsmith.server.domains.Organization; -import com.appsmith.server.domains.Plugin; -import com.appsmith.server.helpers.MockPluginExecutor; import com.appsmith.server.helpers.PluginExecutorHelper; import com.appsmith.server.repositories.OrganizationRepository; import lombok.extern.slf4j.Slf4j; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.security.test.context.support.WithUserDetails; import org.springframework.test.context.junit4.SpringRunner; -import reactor.core.publisher.Mono; -import reactor.test.StepVerifier; - -import static org.assertj.core.api.Assertions.assertThat; @RunWith(SpringRunner.class) @SpringBootTest @@ -53,69 +43,34 @@ public class DatasourceContextServiceTest { orgId = testOrg.getId(); } - - @Test - @WithUserDetails(value = "api_user") - public void checkDecryptionOfAuthenticationDTOTest() { - Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(new MockPluginExecutor())); - - Mono pluginMono = pluginService.findByName("Installed Plugin Name"); - Datasource datasource = new Datasource(); - datasource.setName("test datasource name for authenticated fields decryption test"); - DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration(); - datasourceConfiguration.setUrl("http://test.com"); - DBAuth authenticationDTO = new DBAuth(); - String username = "username"; - String password = "password"; - authenticationDTO.setUsername(username); - authenticationDTO.setPassword(password); - datasourceConfiguration.setAuthentication(authenticationDTO); - datasource.setDatasourceConfiguration(datasourceConfiguration); - datasource.setOrganizationId(orgId); - - Mono datasourceMono = pluginMono.map(plugin -> { - datasource.setPluginId(plugin.getId()); - return datasource; - }).flatMap(datasourceService::create); - - StepVerifier - .create(datasourceMono) - .assertNext(savedDatasource -> { - DBAuth authentication = (DBAuth) savedDatasource.getDatasourceConfiguration().getAuthentication(); - DBAuth decryptedAuthentication = (DBAuth) datasourceContextService.decryptSensitiveFields(authentication); - assertThat(decryptedAuthentication.getPassword()).isEqualTo(password); - }) - .verifyComplete(); - } - @Test @WithUserDetails(value = "api_user") public void checkDecryptionOfAuthenticationDTONullPassword() { - Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(new MockPluginExecutor())); - - Mono pluginMono = pluginService.findByName("Installed Plugin Name"); - Datasource datasource = new Datasource(); - datasource.setName("test datasource name for authenticated fields decryption test null password"); - DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration(); - datasourceConfiguration.setUrl("http://test.com"); - DBAuth authenticationDTO = new DBAuth(); - datasourceConfiguration.setAuthentication(authenticationDTO); - datasource.setDatasourceConfiguration(datasourceConfiguration); - datasource.setOrganizationId(orgId); - - Mono datasourceMono = pluginMono.map(plugin -> { - datasource.setPluginId(plugin.getId()); - return datasource; - }).flatMap(datasourceService::create); - - StepVerifier - .create(datasourceMono) - .assertNext(savedDatasource -> { - DBAuth authentication = (DBAuth) savedDatasource.getDatasourceConfiguration().getAuthentication(); - DBAuth decryptedAuthentication = (DBAuth) datasourceContextService.decryptSensitiveFields(authentication); - assertThat(decryptedAuthentication.getPassword()).isNull(); - }) - .verifyComplete(); +// Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(new MockPluginExecutor())); +// +// Mono pluginMono = pluginService.findByName("Installed Plugin Name"); +// Datasource datasource = new Datasource(); +// datasource.setName("test datasource name for authenticated fields decryption test null password"); +// DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration(); +// datasourceConfiguration.setUrl("http://test.com"); +// DBAuth authenticationDTO = new DBAuth(); +// datasourceConfiguration.setAuthentication(authenticationDTO); +// datasource.setDatasourceConfiguration(datasourceConfiguration); +// datasource.setOrganizationId(orgId); +// +// Mono datasourceMono = pluginMono.map(plugin -> { +// datasource.setPluginId(plugin.getId()); +// return datasource; +// }).flatMap(datasourceService::create); +// +// StepVerifier +// .create(datasourceMono) +// .assertNext(savedDatasource -> { +// DBAuth authentication = (DBAuth) savedDatasource.getDatasourceConfiguration().getAuthentication(); +// DBAuth decryptedAuthentication = (DBAuth) datasourceContextService.decryptSensitiveFields(authentication); +// assertThat(decryptedAuthentication.getPassword()).isNull(); +// }) +// .verifyComplete(); } } diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceServiceTest.java index fc9c70ac3d..be37e45096 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceServiceTest.java @@ -570,7 +570,6 @@ public class DatasourceServiceTest { DBAuth authentication = (DBAuth) savedDatasource.getDatasourceConfiguration().getAuthentication(); assertThat(authentication.getUsername()).isEqualTo(username); assertThat(authentication.getPassword()).isEqualTo(encryptionService.encryptString(password)); - assertThat(authentication.isEncrypted()).isTrue(); }) .verifyComplete(); } @@ -604,7 +603,6 @@ public class DatasourceServiceTest { DBAuth authentication = (DBAuth) savedDatasource.getDatasourceConfiguration().getAuthentication(); assertThat(authentication.getUsername()).isNull(); assertThat(authentication.getPassword()).isNull(); - assertThat(authentication.isEncrypted()).isNull(); }) .verifyComplete(); } @@ -654,7 +652,6 @@ public class DatasourceServiceTest { assertThat(authentication.getUsername()).isEqualTo(username); assertThat(encryptionService.encryptString(password)).isEqualTo(authentication.getPassword()); - assertThat(authentication.isEncrypted()).isTrue(); }) .verifyComplete(); } From 4f68d5f47200127acb3564d9cfc3178c18477765 Mon Sep 17 00:00:00 2001 From: Nidhi Date: Thu, 8 Apr 2021 19:53:48 +0530 Subject: [PATCH 2/4] Logging test failure --- .../server/solutions/ExamplesOrganizationClonerTests.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ExamplesOrganizationClonerTests.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ExamplesOrganizationClonerTests.java index b0b16354fd..b2eabd06a6 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ExamplesOrganizationClonerTests.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/solutions/ExamplesOrganizationClonerTests.java @@ -929,7 +929,8 @@ public class ExamplesOrganizationClonerTests { .thenReturn(targetOrg1); }); }) - .flatMap(this::loadOrganizationData); + .flatMap(this::loadOrganizationData) + .doOnError(error -> log.error("Error in test", error)); StepVerifier.create(resultMono) .assertNext(data -> { From c8b2035eeb0e2779392a15eaaae358aedce363df Mon Sep 17 00:00:00 2001 From: Nidhi Date: Mon, 26 Apr 2021 18:13:55 +0530 Subject: [PATCH 3/4] Accessibility reset --- .../external/annotations/encryption/EncryptionHandler.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/annotations/encryption/EncryptionHandler.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/annotations/encryption/EncryptionHandler.java index a257bfac53..aac53e06a6 100644 --- a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/annotations/encryption/EncryptionHandler.java +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/annotations/encryption/EncryptionHandler.java @@ -298,10 +298,9 @@ public class EncryptionHandler { } } + field.setAccessible(false); } return hasEncryptedFields; } - - } From 6c0668e74dd800ee49a43a5694b663dfc877e4bb Mon Sep 17 00:00:00 2001 From: Nidhi Date: Thu, 6 May 2021 13:36:37 +0530 Subject: [PATCH 4/4] Tests for encryption --- .../DatasourceContextServiceTest.java | 113 +++++++++++++----- 1 file changed, 86 insertions(+), 27 deletions(-) diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceContextServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceContextServiceTest.java index 745f1eb6e9..6f7ca33aed 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceContextServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceContextServiceTest.java @@ -1,18 +1,28 @@ package com.appsmith.server.services; +import com.appsmith.external.models.DBAuth; +import com.appsmith.external.models.DatasourceConfiguration; +import com.appsmith.external.services.EncryptionService; import com.appsmith.server.acl.AclPermission; +import com.appsmith.server.domains.Datasource; import com.appsmith.server.domains.Organization; +import com.appsmith.server.domains.Plugin; +import com.appsmith.server.helpers.MockPluginExecutor; import com.appsmith.server.helpers.PluginExecutorHelper; import com.appsmith.server.repositories.OrganizationRepository; import lombok.extern.slf4j.Slf4j; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.security.test.context.support.WithUserDetails; import org.springframework.test.context.junit4.SpringRunner; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; @RunWith(SpringRunner.class) @SpringBootTest @@ -20,7 +30,7 @@ import org.springframework.test.context.junit4.SpringRunner; public class DatasourceContextServiceTest { @Autowired - DatasourceContextService datasourceContextService; + EncryptionService encryptionService; @Autowired OrganizationRepository organizationRepository; @@ -34,7 +44,7 @@ public class DatasourceContextServiceTest { @MockBean PluginExecutorHelper pluginExecutorHelper; - String orgId = ""; + String orgId = ""; @Before @WithUserDetails(value = "api_user") @@ -43,34 +53,83 @@ public class DatasourceContextServiceTest { orgId = testOrg.getId(); } + @Test + @WithUserDetails(value = "api_user") + public void checkDecryptionOfAuthenticationDTOTest() { + Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(new MockPluginExecutor())); + + Mono pluginMono = pluginService.findByName("Installed Plugin Name"); + Datasource datasource = new Datasource(); + datasource.setName("test datasource name for authenticated fields decryption test"); + DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration(); + datasourceConfiguration.setUrl("http://test.com"); + DBAuth authenticationDTO = new DBAuth(); + String username = "username"; + String password = "password"; + authenticationDTO.setUsername(username); + authenticationDTO.setPassword(password); + datasourceConfiguration.setAuthentication(authenticationDTO); + datasource.setDatasourceConfiguration(datasourceConfiguration); + datasource.setOrganizationId(orgId); + + final Datasource createdDatasource = pluginMono + .map(plugin -> { + datasource.setPluginId(plugin.getId()); + return datasource; + }) + .flatMap(datasourceService::create) + .block(); + + assert createdDatasource != null; + Mono datasourceMono = datasourceService.findById(createdDatasource.getId()); + + StepVerifier + .create(datasourceMono) + .assertNext(savedDatasource -> { + DBAuth authentication = (DBAuth) savedDatasource.getDatasourceConfiguration().getAuthentication(); + Assert.assertEquals(password, authentication.getPassword()); + DBAuth encryptedAuthentication = (DBAuth) createdDatasource.getDatasourceConfiguration().getAuthentication(); + Assert.assertEquals(encryptionService.encryptString(password), encryptedAuthentication.getPassword()); + }) + .verifyComplete(); + } + + @Test @WithUserDetails(value = "api_user") public void checkDecryptionOfAuthenticationDTONullPassword() { -// Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(new MockPluginExecutor())); -// -// Mono pluginMono = pluginService.findByName("Installed Plugin Name"); -// Datasource datasource = new Datasource(); -// datasource.setName("test datasource name for authenticated fields decryption test null password"); -// DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration(); -// datasourceConfiguration.setUrl("http://test.com"); -// DBAuth authenticationDTO = new DBAuth(); -// datasourceConfiguration.setAuthentication(authenticationDTO); -// datasource.setDatasourceConfiguration(datasourceConfiguration); -// datasource.setOrganizationId(orgId); -// -// Mono datasourceMono = pluginMono.map(plugin -> { -// datasource.setPluginId(plugin.getId()); -// return datasource; -// }).flatMap(datasourceService::create); -// -// StepVerifier -// .create(datasourceMono) -// .assertNext(savedDatasource -> { -// DBAuth authentication = (DBAuth) savedDatasource.getDatasourceConfiguration().getAuthentication(); -// DBAuth decryptedAuthentication = (DBAuth) datasourceContextService.decryptSensitiveFields(authentication); -// assertThat(decryptedAuthentication.getPassword()).isNull(); -// }) -// .verifyComplete(); + Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(new MockPluginExecutor())); + + Mono pluginMono = pluginService.findByName("Installed Plugin Name"); + Datasource datasource = new Datasource(); + datasource.setName("test datasource name for authenticated fields decryption test null password"); + DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration(); + datasourceConfiguration.setUrl("http://test.com"); + DBAuth authenticationDTO = new DBAuth(); + datasourceConfiguration.setAuthentication(authenticationDTO); + datasource.setDatasourceConfiguration(datasourceConfiguration); + datasource.setOrganizationId(orgId); + + final Datasource createdDatasource = pluginMono + .map(plugin -> { + datasource.setPluginId(plugin.getId()); + return datasource; + }) + .flatMap(datasourceService::create) + .block(); + + assert createdDatasource != null; + Mono datasourceMono = datasourceService.findById(createdDatasource.getId()); + + StepVerifier + .create(datasourceMono) + .assertNext(savedDatasource -> { + DBAuth authentication = (DBAuth) savedDatasource.getDatasourceConfiguration().getAuthentication(); + Assert.assertNull(authentication.getPassword()); + DBAuth encryptedAuthentication = (DBAuth) createdDatasource.getDatasourceConfiguration().getAuthentication(); + Assert.assertNull(encryptedAuthentication.getPassword()); + }) + .verifyComplete(); } }