From 0c1366f0a5ffa0d1e3e87cad46b6da9a2b2bcc74 Mon Sep 17 00:00:00 2001 From: RajuBeemreddy1 <77657521+RajuBeemreddy1@users.noreply.github.com> Date: Wed, 10 Apr 2024 21:33:48 +0530 Subject: [PATCH] Secure the subscription passwords (#543) * Missing the encryption of the passwords when updating the subscription. Fixed the issue by encrypting the passwords when updation of the subscription. Also updated the code not to print the passwords in debug logs. * Update main.yml updated the main.yml to trigger local changes * Revert the change to enable only for community branch * Revert work flow changes * Missing the encryption of the passwords when updating the subscription. Fixed the issue by encrypting the passwords when updation of the subscription. Also updated the code not to print the passwords in debug logs. * Update main.yml updated the main.yml to trigger local changes * Revert the change to enable only for community branch * Revert work flow changes * Rebase master * Added Nidhi changes for testcases --------- Co-authored-by: Raju Beemreddy Co-authored-by: Dhruvin Kalavadia (EXT) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jainad Chinta --- pom.xml | 2 +- .../ei/services/SubscriptionService.java | 10 +- .../ei/subscription/SubscriptionHandler.java | 12 +- .../com/ericsson/ei/logFilter/LogFilter.java | 40 ++++++ .../ei/services/SubscriptionServiceTest.java | 115 ++++++++++++++---- src/test/resources/application.properties | 85 +++++++++++++ src/test/resources/logback.xml | 16 ++- src/test/resources/subscription_CLME.json | 1 + 8 files changed, 247 insertions(+), 34 deletions(-) create mode 100644 src/test/java/com/ericsson/ei/logFilter/LogFilter.java create mode 100644 src/test/resources/application.properties diff --git a/pom.xml b/pom.xml index 355e5100e..d67468a66 100644 --- a/pom.xml +++ b/pom.xml @@ -441,7 +441,7 @@ ch.qos.logback logback-classic - 1.2.13 + 1.2.11 diff --git a/src/main/java/com/ericsson/ei/services/SubscriptionService.java b/src/main/java/com/ericsson/ei/services/SubscriptionService.java index 6af2eca42..e670ee540 100644 --- a/src/main/java/com/ericsson/ei/services/SubscriptionService.java +++ b/src/main/java/com/ericsson/ei/services/SubscriptionService.java @@ -50,7 +50,7 @@ public class SubscriptionService implements ISubscriptionService { private static final Logger LOGGER = LoggerFactory.getLogger(SubscriptionService.class); @Value("${spring.application.name}") - private String SpringApplicationName; + private String springApplicationName; @Value("${spring.data.mongodb.database}") private String dataBaseName; @@ -99,7 +99,7 @@ public Subscription getSubscription(String subscriptionName) try { subscription = mapper.readValue(input, Subscription.class); // Inject aggregationtype - subscription.setAggregationtype(SpringApplicationName); + subscription.setAggregationtype(springApplicationName); return subscription; } catch (IOException e) { LOGGER.error("Malformed JSON string", e); @@ -129,6 +129,10 @@ public boolean modifySubscription(Subscription subscription, String subscription ObjectMapper mapper = new ObjectMapper(); Document result = null; try { + String password = subscription.getPassword(); + if (isEncryptionReady(subscription.getAuthenticationType(), subscription.getUserName(), password)) { + subscription.setPassword(encryptPassword(password)); + } String stringSubscription = mapper.writeValueAsString(subscription); final MongoCondition subscriptionNameCondition = MongoCondition.subscriptionNameCondition( @@ -196,7 +200,7 @@ public List getSubscriptions() throws SubscriptionNotFoundExceptio try { subscription = mapper.readValue(input, Subscription.class); // Inject aggregationtype - subscription.setAggregationtype(SpringApplicationName); + subscription.setAggregationtype(springApplicationName); subscriptions.add(subscription); } catch (IOException e) { LOGGER.error("Failed to get subscription.", e); diff --git a/src/main/java/com/ericsson/ei/subscription/SubscriptionHandler.java b/src/main/java/com/ericsson/ei/subscription/SubscriptionHandler.java index c6b4f7864..49c72bbad 100644 --- a/src/main/java/com/ericsson/ei/subscription/SubscriptionHandler.java +++ b/src/main/java/com/ericsson/ei/subscription/SubscriptionHandler.java @@ -28,6 +28,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.Getter; import lombok.Setter; @@ -97,11 +98,16 @@ private void extractConditions(String aggregatedObject, try { JsonNode subscriptionJson = new ObjectMapper().readTree( subscriptionData); - LOGGER.debug("SubscriptionJson : " + subscriptionJson.toString()); - LOGGER.debug("Aggregated Object : " + aggregatedObject + " for the event id: " + id); + + // Remove password from subscription details and put empty value before logging. + JsonNode subscriptoinToDisplay = new ObjectMapper().readTree(subscriptionJson.toString()); + LOGGER.debug("SubscriptionJson : {}", + ((ObjectNode) subscriptoinToDisplay).put("password", "").toPrettyString()); + + LOGGER.debug("Aggregated Object : {} for event id: {}", aggregatedObject, id); ArrayNode requirementNode = (ArrayNode) subscriptionJson.get( "requirements"); - LOGGER.debug("Requirements : " + requirementNode.toString()); + LOGGER.debug("Requirements : {}", requirementNode); Iterator requirementIterator = requirementNode.elements(); SubscriptionField subscriptionField = new SubscriptionField(subscriptionJson); String subscriptionName = subscriptionField.get("subscriptionName"); diff --git a/src/test/java/com/ericsson/ei/logFilter/LogFilter.java b/src/test/java/com/ericsson/ei/logFilter/LogFilter.java new file mode 100644 index 000000000..7822b7765 --- /dev/null +++ b/src/test/java/com/ericsson/ei/logFilter/LogFilter.java @@ -0,0 +1,40 @@ +package com.ericsson.ei.logFilter; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.filter.Filter; +import ch.qos.logback.core.spi.FilterReply; +import org.springframework.context.annotation.PropertySource; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; + +@PropertySource("classpath:logback.xml") +public class LogFilter extends Filter { + +public boolean filter(LocalDateTime start, LocalDateTime end, String logLine) { + DateTimeFormatter logFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss,SSS"); + LocalDateTime current=start; + while(current.isBefore(end)) + { + String time=current.format(logFormatter); + if(logLine.contains(time)) + { + return (true); + } + else + { + current=current.plus(1, ChronoUnit.MILLIS); + } + + + } + return (false); + } + + @Override + public FilterReply decide(ILoggingEvent iLoggingEvent) { + return null; + } +} + diff --git a/src/test/java/com/ericsson/ei/services/SubscriptionServiceTest.java b/src/test/java/com/ericsson/ei/services/SubscriptionServiceTest.java index c6450ed69..81cb0b3f9 100644 --- a/src/test/java/com/ericsson/ei/services/SubscriptionServiceTest.java +++ b/src/test/java/com/ericsson/ei/services/SubscriptionServiceTest.java @@ -16,17 +16,19 @@ */ package com.ericsson.ei.services; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import javax.annotation.PostConstruct; - +import com.ericsson.ei.App; +import com.ericsson.ei.controller.model.Subscription; +import com.ericsson.ei.exception.SubscriptionNotFoundException; +import com.ericsson.ei.logFilter.LogFilter; +import com.ericsson.ei.mongo.MongoCondition; +import com.ericsson.ei.mongo.MongoDBHandler; +import com.ericsson.ei.test.utils.TestConfigs; +import com.ericsson.ei.utils.TestContextInitializer; +import com.ericsson.eiffelcommons.subscriptionobject.RestPostSubscriptionObject; +import com.ericsson.eiffelcommons.subscriptionobject.SubscriptionObject; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mongodb.BasicDBObject; +import com.mongodb.client.MongoClient; import org.apache.commons.io.FileUtils; import org.json.JSONArray; import org.json.JSONException; @@ -49,18 +51,18 @@ import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import com.ericsson.ei.App; -import com.ericsson.ei.controller.model.Subscription; -import com.ericsson.ei.exception.SubscriptionNotFoundException; -import com.ericsson.ei.mongo.MongoCondition; -import com.ericsson.ei.mongo.MongoDBHandler; -import com.ericsson.ei.test.utils.TestConfigs; -import com.ericsson.ei.utils.TestContextInitializer; -import com.ericsson.eiffelcommons.subscriptionobject.RestPostSubscriptionObject; -import com.ericsson.eiffelcommons.subscriptionobject.SubscriptionObject; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.mongodb.BasicDBObject; -import com.mongodb.client.MongoClient; +import javax.annotation.PostConstruct; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +import static org.junit.Assert.*; @TestPropertySource(properties = { "spring.data.mongodb.database: SubscriptionServiceTest", @@ -88,6 +90,9 @@ public class SubscriptionServiceTest { @Autowired private ISubscriptionService subscriptionService; + @Autowired + private SubscriptionService subService; + @Autowired private MongoDBHandler mongoDBHandler; @@ -360,4 +365,66 @@ private void deleteSubscriptionsByName(String subscriptionName) throws AccessExc Mockito.when(authentication.getName()).thenReturn("ABC"); subscriptionService.deleteSubscription(subscriptionName); } -} + + @Test + public void testLogForPasswordAdd() throws Exception { + Path logFilePath= Paths.get("/var/tmp/eiffel-intelligence.log"); + try { + + Scanner scanner = new Scanner(logFilePath); + LocalDateTime start = LocalDateTime.now(); + Subscription subscription2 = mapper.readValue(jsonArray.getJSONObject(0).toString(), Subscription.class); + String expectedSubscriptionName = subscription2.getSubscriptionName(); + subscription2.setAuthenticationType("BASIC_AUTH"); + String expectedSubscriptionPassword = subscription2.getPassword(); + subService.addSubscription(subscription2); + LocalDateTime end = LocalDateTime.now(); + LogFilter log = new LogFilter(); + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + if (log.filter(start, end, line)) { + if ((line.contains(expectedSubscriptionName) && (line.contains("password")))) { + assertFalse(line.contains(expectedSubscriptionPassword)); + } + } + } + scanner.close(); + } + catch(Exception e) { + LOGGER.error(e.getMessage(),e); + } finally { + Files.write(logFilePath, new byte[0]); + } + } + @Test + public void testLogForPasswordUpdate() throws Exception { + Path logFilePath=Paths.get("/var/tmp/eiffel-intelligence.log"); + try { + + Scanner scanner = new Scanner(logFilePath); + LocalDateTime start = LocalDateTime.now(); + Subscription subscription2 = mapper.readValue(jsonArray.getJSONObject(0).toString(), Subscription.class); + String expectedSubscriptionName = subscription2.getSubscriptionName(); + subscription2.setAuthenticationType("BASIC_AUTH"); + subscription2.setPassword("token123"); + String expectedSubscriptionPassword = subscription2.getPassword(); + subscriptionService.modifySubscription(subscription2,expectedSubscriptionName); + LocalDateTime end = LocalDateTime.now(); + LogFilter log = new LogFilter(); + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + if (log.filter(start, end, line)) { + if ((line.contains(expectedSubscriptionName) && (line.contains("password")))) { + assertFalse(line.contains(expectedSubscriptionPassword)); + } + } + } + scanner.close(); + } + catch(Exception e) { + LOGGER.error(e.getMessage(),e); + } finally { + Files.write(logFilePath, new byte[0]); + } + } +} \ No newline at end of file diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties new file mode 100644 index 000000000..108f68f32 --- /dev/null +++ b/src/test/resources/application.properties @@ -0,0 +1,85 @@ +# Configuration documentation can be found here: +# https://github.com/eiffel-community/eiffel-intelligence/blob/master/wiki/configuration.md +spring.application.name: eiffel-intelligence +server.port: 8090 +spring.mvc.pathmatch.matching-strategy: ANT_PATH_MATCHER +logging.level.root: INFO +logging.level.org.springframework.web: ERROR +logging.level.com.ericsson.ei: ERROR + +rules.path: /rules/ArtifactRules-Eiffel-Agen-Version.json +rules.replacement.marker: %IdentifyRulesEventId% + +# WARNING! Do not enable this in a production environment! +test.aggregation.enabled: false + +rabbitmq.host: localhost +rabbitmq.port: 5672 +rabbitmq.user: myuser +rabbitmq.password: myuser +rabbitmq.tls.version: +rabbitmq.exchange.name: ei-exchange +rabbitmq.domain.id: ei-domain +rabbitmq.component.name: eiffel-intelligence +rabbitmq.queue.suffix: messageQueue +rabbitmq.queue.durable: true +rabbitmq.binding.key: # +rabbitmq.waitlist.queue.suffix: waitList + +bindingkeys.collection.name: binding_keys + +spring.data.mongodb.uri: mongodb://localhost:27017 +spring.data.mongodb.database: eiffel_intelligence + +server.session.timeout: 1200 +sessions.collection.name: sessions + +aggregations.collection.name: aggregations +aggregations.collection.ttl: +event.object.map.collection.name: event_object_map +subscriptions.collection.name: subscriptions +subscriptions.repeat.handler.collection.name: subscriptions_repeat_handler +waitlist.collection.name: wait_list +waitlist.collection.ttl: 600 +waitlist.resend.initial.delay: 2000 +waitlist.resend.fixed.rate: 15000 +failed.notifications.collection.name: failed_notifications +failed.notifications.collection.ttl: 600 +notification.retry: 3 +notification.httpRequest.timeout: 5000 + +email.sender: noreply@domain.com +email.subject: Email Subscription Notification + +spring.mail.host: +spring.mail.port: +spring.mail.username: +spring.mail.password: +spring.mail.properties.mail.smtp.auth: false +spring.mail.properties.mail.smtp.starttls.enable: false + +event.repository.url: +event.repository.shallow: true + +ldap.enabled: false +ldap.server.list: [{\ + "url": "",\ + "base.dn": "",\ + "username": "",\ + "password": "",\ + "user.filter": ""\ + }] + +### DEVELOPER SETTINGS + +spring.mongodb.embedded.version: 3.4.1 +# We remove the embedded mongodb in tests since in most of them we set up our own before Spring +# starts and activate it manually in tests where we need the Spring's own embedded mongo DB +spring.autoconfigure.exclude: org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration + +threads.core.pool.size: 200 +threads.queue.capacity: 7000 +threads.max.pool.size: 250 +scheduled.threadpool.size: 200 +jasypt.encryptor.password=test + diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml index b95b27f52..1ddfe15bf 100644 --- a/src/test/resources/logback.xml +++ b/src/test/resources/logback.xml @@ -5,8 +5,18 @@ %d{dd-MM-yyyy HH:mm:ss.SSS} %magenta([%thread]) %highlight(%-5level) %logger{36}.%M - %msg%n - + + + /var/tmp/eiffel-intelligence.log + + %date %level [%thread] %logger{10} [%file:%line] %msg%n + + + + + + - - \ No newline at end of file + + diff --git a/src/test/resources/subscription_CLME.json b/src/test/resources/subscription_CLME.json index ba2680166..a37a0636b 100644 --- a/src/test/resources/subscription_CLME.json +++ b/src/test/resources/subscription_CLME.json @@ -26,6 +26,7 @@ ], "subscriptionName" : "Single_CLME_Subscription_Test", "userName" : "ABC", + "password": "token", "ldapUserName": "" } ] \ No newline at end of file