Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Secure the subscription passwords #543

Merged
merged 12 commits into from
Apr 10, 2024
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.13</version>
<version>1.2.11</version>
</dependency>

</dependencies>
Expand Down
10 changes: 7 additions & 3 deletions src/main/java/com/ericsson/ei/services/SubscriptionService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -196,7 +200,7 @@ public List<Subscription> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
RajuBeemreddy1 marked this conversation as resolved.
Show resolved Hide resolved

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<JsonNode> requirementIterator = requirementNode.elements();
SubscriptionField subscriptionField = new SubscriptionField(subscriptionJson);
String subscriptionName = subscriptionField.get("subscriptionName");
Expand Down
40 changes: 40 additions & 0 deletions src/test/java/com/ericsson/ei/logFilter/LogFilter.java
Original file line number Diff line number Diff line change
@@ -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<ILoggingEvent> {

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;
}
}

115 changes: 91 additions & 24 deletions src/test/java/com/ericsson/ei/services/SubscriptionServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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",
Expand Down Expand Up @@ -88,6 +90,9 @@ public class SubscriptionServiceTest {
@Autowired
private ISubscriptionService subscriptionService;

@Autowired
private SubscriptionService subService;

@Autowired
private MongoDBHandler mongoDBHandler;

Expand Down Expand Up @@ -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]);
}
}
}
85 changes: 85 additions & 0 deletions src/test/resources/application.properties
Original file line number Diff line number Diff line change
@@ -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: [email protected]
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

16 changes: 13 additions & 3 deletions src/test/resources/logback.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,18 @@
<pattern>%d{dd-MM-yyyy HH:mm:ss.SSS} %magenta([%thread]) %highlight(%-5level) %logger{36}.%M - %msg%n</pattern>
</encoder>
</appender>
<root level="info">

<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>/var/tmp/eiffel-intelligence.log</file>
<encoder>
<pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
</encoder>
<filter class="com.ericsson.ei.logFilter.LogFilter" />
</appender>

<root level="DEBUG">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
<logger name="org.mockserver.mock" level="error" />
</configuration>
<logger name="org.mockserver.mock" level="ERROR" />
</configuration>
1 change: 1 addition & 0 deletions src/test/resources/subscription_CLME.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
],
"subscriptionName" : "Single_CLME_Subscription_Test",
"userName" : "ABC",
"password": "token",
"ldapUserName": ""
}
]