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 c6230b2b12..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 @@ -53,144 +53,146 @@ 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); @@ -198,7 +200,7 @@ public class EncryptionHandler { } - boolean convertEncryption(Object source, Function transformer) { + synchronized boolean convertEncryption(Object source, Function transformer) { if (source == null) { return false; } @@ -294,12 +296,11 @@ public class EncryptionHandler { } } } - field.setAccessible(false); } + + 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 d0d335d3f0..1c002f2562 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; @@ -11,8 +10,6 @@ import lombok.Setter; import org.springframework.data.annotation.Transient; import reactor.core.publisher.Mono; -import java.util.Collections; -import java.util.Map; import java.util.Set; @Getter @@ -53,25 +50,6 @@ 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; - } - public Mono hasExpired() { return Mono.just(Boolean.FALSE); } 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 2f4c1416a5..8e305203f6 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; @@ -14,13 +15,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 e9f73e521a..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,10 +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.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError; -import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Getter; @@ -14,13 +12,8 @@ import lombok.ToString; import org.apache.logging.log4j.util.Strings; import org.springframework.data.annotation.Transient; import org.springframework.util.StringUtils; -import reactor.core.publisher.Mono; -import java.time.Instant; 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; @@ -46,6 +39,7 @@ public class OAuth2 extends AuthenticationDTO { String clientId; + @Encrypted @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) String clientSecret; @@ -79,73 +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; - } - - @Override - public Mono hasExpired() { - if (this.authenticationResponse == null) { - return Mono.error(new AppsmithPluginException( - AppsmithPluginError.PLUGIN_ERROR, - "Expected datasource to have valid authentication tokens at this point")); - } - - return Mono.just(authenticationResponse.expiresAt.isBefore(Instant.now().plusSeconds(60))); - } } diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/MongoConfig.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/MongoConfig.java index 16d9af3e2d..be6ff0b7a2 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/MongoConfig.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/MongoConfig.java @@ -79,9 +79,9 @@ public class MongoConfig { return converter; } -// @Bean -// public EncryptionMongoEventListener encryptionMongoEventListener(EncryptionService encryptionService) { -// return new EncryptionMongoEventListener(encryptionService); -// } + @Bean + public EncryptionMongoEventListener encryptionMongoEventListener(EncryptionService encryptionService) { + return new EncryptionMongoEventListener(encryptionService); + } } 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 2ad3e9949e..ad2724b376 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 @@ -18,7 +18,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 +87,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 +177,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 && !e.getValue().isBlank()) - .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 871e36404a..414285d9d7 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,7 +1,7 @@ package com.appsmith.server.services; +import com.appsmith.external.helpers.BeanCopyUtils; import com.appsmith.external.helpers.MustacheHelper; -import com.appsmith.external.models.AuthenticationDTO; import com.appsmith.external.models.DatasourceConfiguration; import com.appsmith.external.models.DatasourceTestResult; import com.appsmith.external.models.Endpoint; @@ -36,7 +36,6 @@ 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; @@ -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) { String orgId = datasource.getOrganizationId(); @@ -105,11 +93,6 @@ public class DatasourceServiceImpl extends BaseService datasourceMono = Mono.just(datasource); if (StringUtils.isEmpty(datasource.getName())) { datasourceMono = sequenceService @@ -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<>(); @@ -318,22 +279,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 @@ -426,19 +383,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 0a3f2b5812..4a77900821 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 @@ -8,7 +8,6 @@ import com.appsmith.external.helpers.MustacheHelper; import com.appsmith.external.models.ActionConfiguration; import com.appsmith.external.models.ActionExecutionRequest; import com.appsmith.external.models.ActionExecutionResult; -import com.appsmith.external.models.AuthenticationDTO; import com.appsmith.external.models.Param; import com.appsmith.external.models.Policy; import com.appsmith.external.models.Provider; @@ -243,17 +242,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 { @@ -540,13 +528,6 @@ public class NewActionServiceImpl extends BaseService validatedDatasourceMono.flatMap(datasource1 -> { - // Check encryption again - if (datasource1.getDatasourceConfiguration() != null && - datasource1.getDatasourceConfiguration().getAuthentication() != null) { - AuthenticationDTO authentication = datasource1.getDatasourceConfiguration().getAuthentication(); - datasource1.getDatasourceConfiguration().setAuthentication(datasourceContextService.decryptSensitiveFields(authentication)); - } return (Mono) pluginExecutor.executeParameterized( resourceContext.getConnection(), executeActionDTO, diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/AuthenticationService.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/AuthenticationService.java index a73d0dbbbf..d1fa19064a 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/AuthenticationService.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/AuthenticationService.java @@ -388,7 +388,6 @@ public class AuthenticationService { datasource.getDatasourceConfiguration() != null && datasource.getDatasourceConfiguration().getAuthentication() instanceof OAuth2); OAuth2 oAuth2 = (OAuth2) datasource.getDatasourceConfiguration().getAuthentication(); - assert (!oAuth2.isEncrypted()); return pluginService.findById(datasource.getPluginId()) .filter(plugin -> PluginType.SAAS.equals(plugin.getType())) .zipWith(configService.getInstanceId()) 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 d7c82d720a..3f9f3f7801 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 @@ -3,9 +3,9 @@ package com.appsmith.server.solutions; 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.AuthenticationDTO; 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 @@ -78,8 +75,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())) @@ -130,23 +125,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 abd99f4e91..74c861374e 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 @@ -404,9 +404,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..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 @@ -2,6 +2,7 @@ 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; @@ -10,6 +11,7 @@ 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; @@ -22,15 +24,13 @@ 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 @Slf4j public class DatasourceContextServiceTest { @Autowired - DatasourceContextService datasourceContextService; + EncryptionService encryptionService; @Autowired OrganizationRepository organizationRepository; @@ -44,7 +44,7 @@ public class DatasourceContextServiceTest { @MockBean PluginExecutorHelper pluginExecutorHelper; - String orgId = ""; + String orgId = ""; @Before @WithUserDetails(value = "api_user") @@ -53,7 +53,6 @@ public class DatasourceContextServiceTest { orgId = testOrg.getId(); } - @Test @WithUserDetails(value = "api_user") public void checkDecryptionOfAuthenticationDTOTest() { @@ -73,21 +72,29 @@ public class DatasourceContextServiceTest { datasource.setDatasourceConfiguration(datasourceConfiguration); datasource.setOrganizationId(orgId); - Mono datasourceMono = pluginMono.map(plugin -> { - datasource.setPluginId(plugin.getId()); - return datasource; - }).flatMap(datasourceService::create); + 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(); - DBAuth decryptedAuthentication = (DBAuth) datasourceContextService.decryptSensitiveFields(authentication); - assertThat(decryptedAuthentication.getPassword()).isEqualTo(password); + 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() { @@ -103,17 +110,24 @@ public class DatasourceContextServiceTest { datasource.setDatasourceConfiguration(datasourceConfiguration); datasource.setOrganizationId(orgId); - Mono datasourceMono = pluginMono.map(plugin -> { - datasource.setPluginId(plugin.getId()); - return datasource; - }).flatMap(datasourceService::create); + 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(); - DBAuth decryptedAuthentication = (DBAuth) datasourceContextService.decryptSensitiveFields(authentication); - assertThat(decryptedAuthentication.getPassword()).isNull(); + Assert.assertNull(authentication.getPassword()); + DBAuth encryptedAuthentication = (DBAuth) createdDatasource.getDatasourceConfiguration().getAuthentication(); + Assert.assertNull(encryptedAuthentication.getPassword()); }) .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 bcfda29a67..20a2f4c752 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 @@ -611,7 +611,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(); } @@ -645,7 +644,6 @@ public class DatasourceServiceTest { DBAuth authentication = (DBAuth) savedDatasource.getDatasourceConfiguration().getAuthentication(); assertThat(authentication.getUsername()).isNull(); assertThat(authentication.getPassword()).isNull(); - assertThat(authentication.isEncrypted()).isNull(); }) .verifyComplete(); } @@ -695,7 +693,6 @@ public class DatasourceServiceTest { assertThat(authentication.getUsername()).isEqualTo(username); assertThat(encryptionService.encryptString(password)).isEqualTo(authentication.getPassword()); - assertThat(authentication.isEncrypted()).isTrue(); }) .verifyComplete(); } 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 -> {