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

fix: Return placeholder instead of encrypted passwords in REST APIs #4893

Merged
merged 1 commit into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ Import-Package: javax.annotation.security;version="1.2.0",
org.osgi.service.component;version="1.3.0",
org.osgi.service.useradmin;version="1.1.0";resolution:=optional,
org.slf4j;version="1.7.25"
Export-Package: org.eclipse.kura.rest.configuration.api;version="1.0.0"
Export-Package: org.eclipse.kura.rest.configuration.api;version="1.1.0"
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2021, 2022 Eurotech and/or its affiliates and others
* Copyright (c) 2021, 2023 Eurotech and/or its affiliates and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -361,7 +361,7 @@ public ComponentConfigurationList listComponentConfigurations() {
throw DefaultExceptionHandler.toWebApplicationException(e);
}

return DTOUtil.toComponentConfigurationList(ccs, this.cryptoService, false);
return DTOUtil.toComponentConfigurationList(ccs, this.cryptoService, false).replacePasswordsWithPlaceholder();

}

Expand Down Expand Up @@ -398,7 +398,8 @@ public ComponentConfigurationList listComponentConfigurations(final PidSet pids)
}
});

return DTOUtil.toComponentConfigurationList(configs, this.cryptoService, false);
return DTOUtil.toComponentConfigurationList(configs, this.cryptoService, false)
.replacePasswordsWithPlaceholder();
}

/**
Expand Down Expand Up @@ -562,4 +563,5 @@ public Response rollbackSnapshot(final SnapshotId id) {

return Response.ok().build();
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2021, 2022 Eurotech and/or its affiliates and others
* Copyright (c) 2021, 2023 Eurotech and/or its affiliates and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
Expand All @@ -12,12 +12,15 @@
*******************************************************************************/
package org.eclipse.kura.rest.configuration.api;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;

import javax.ws.rs.core.Response.Status;

import org.eclipse.kura.configuration.metatype.OCD;
import org.eclipse.kura.configuration.metatype.Scalar;
import org.eclipse.kura.request.handler.jaxrs.DefaultExceptionHandler;

public class ComponentConfigurationDTO implements Validable {
Expand Down Expand Up @@ -45,6 +48,43 @@ public Map<String, PropertyDTO> getProperties() {
return this.properties;
}

public ComponentConfigurationDTO replacePasswordsWithPlaceholder() {

if (properties == null) {
return this;
}

final Map<String, PropertyDTO> result = new HashMap<>(this.properties);

for (final Entry<String, PropertyDTO> e : result.entrySet()) {
e.setValue(replacePasswordsWithPlaceholder(e.getValue()));
}

return new ComponentConfigurationDTO(pid, definition, result);
}

public PropertyDTO replacePasswordsWithPlaceholder(final PropertyDTO property) {

if (property == null || property.getType() != Scalar.PASSWORD) {
return property;
}

if (property.getValue() instanceof String[]) {
final String[] asStringArray = (String[]) property.getValue();
final String[] result = new String[asStringArray.length];

for (int i = 0; i < asStringArray.length; i++) {
if (asStringArray[i] != null) {
result[i] = "placeholder";
}
}

return new PropertyDTO(result, Scalar.PASSWORD);
} else {
return new PropertyDTO("placeholder", Scalar.PASSWORD);
}
}

@Override
public void validate() {
FailureHandler.requireParameter(this.pid, "pid");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2021, 2022 Eurotech and/or its affiliates and others
* Copyright (c) 2021, 2023 Eurotech and/or its affiliates and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
Expand All @@ -13,6 +13,7 @@
package org.eclipse.kura.rest.configuration.api;

import java.util.List;
import java.util.stream.Collectors;

public class ComponentConfigurationList implements Validable {

Expand All @@ -30,4 +31,14 @@ public List<ComponentConfigurationDTO> getConfigs() {
public void validate() {
FailureHandler.requireParameter(this.configs, "configs");
}

public ComponentConfigurationList replacePasswordsWithPlaceholder() {
if (configs == null) {
return this;
}

return new ComponentConfigurationList(
configs.stream().map(ComponentConfigurationDTO::replacePasswordsWithPlaceholder)
.collect(Collectors.toList()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Import-Package: com.google.gson;version="2.7.0",
org.eclipse.kura.internal.wire.asset;version="[1.0,2.0)",
org.eclipse.kura.marshalling;version="[1.0,2.0)",
org.eclipse.kura.request.handler.jaxrs;version="[1.0,2.0)",
org.eclipse.kura.rest.configuration.api;version="[1.0,2.0)",
org.eclipse.kura.rest.configuration.api;version="[1.1,2.0)",
org.eclipse.kura.util.service;version="[1.0,2.0)",
org.eclipse.kura.wire;version="[2.0,3.0)",
org.eclipse.kura.wire.graph;version="[1.0,2.0)",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2022 Eurotech and/or its affiliates and others
* Copyright (c) 2023 Eurotech and/or its affiliates and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -362,7 +362,7 @@ public ComponentConfigurationList getWireConfigsByPid(final PidSet pidSet) {
}
}

return DTOUtil.toComponentConfigurationList(result, cryptoService, true);
return DTOUtil.toComponentConfigurationList(result, cryptoService, false).replacePasswordsWithPlaceholder();

} catch (final Exception e) {
throw DefaultExceptionHandler.toWebApplicationException(e);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2021, 2022 Eurotech and/or its affiliates and others
* Copyright (c) 2021, 2023 Eurotech and/or its affiliates and others
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -80,7 +80,7 @@
@RunWith(Parameterized.class)
public class ConfigurationRestServiceTest extends AbstractRequestHandlerTest {

private char[] EncryptedPassword;
private char[] encryptedPassword;

@Test
public void shouldSupportGetSnapshots() throws KuraException {
Expand Down Expand Up @@ -270,16 +270,45 @@ public void testGetLongProperty() throws KuraException {
}

@Test
public void testGetPasswordProperty() throws KuraException {
this.EncryptedPassword = this.cryptoService.encryptAes("foobar".toCharArray());
public void testReturnPlaceholderInsteadOfEncryptedPassword() throws KuraException {
givenEncryptedPassword("foobar");
givenATestConfigurationPropertyWithAdTypeAndValue(Scalar.PASSWORD,
new Password(this.EncryptedPassword));
new Password(this.encryptedPassword));

whenRequestIsPerformed(new MethodSpec("GET"), "/configurableComponents/configurations");

thenRequestSucceeds();
thenTestPropertyTypeIs(Json.value("PASSWORD"));
thenTestPropertyValueIs(Json.value(new String(this.EncryptedPassword)));
thenTestPropertyValueIs(Json.value("placeholder"));
}

@Test
public void testReturnNoValueForMissingPasswordProperty() throws KuraException {
givenEncryptedPassword("foobar");
givenATestConfigurationPropertyWithAdTypeAndValue(Scalar.PASSWORD,
null);

whenRequestIsPerformed(new MethodSpec("GET"), "/configurableComponents/configurations");

thenRequestSucceeds();
thenTestPropertyIsMissing();
}

@Test
public void testGetSnapshotReturnsUnencryptedPassword() throws KuraException {
givenATestConfigurationPropertyWithAdTypeAndValue(Scalar.PASSWORD,
new Password("foobar".toCharArray()));

whenRequestIsPerformed(new MethodSpec("POST"), "/snapshots/byId",
"{\"id\":1}");

thenRequestSucceeds();
thenTestPropertyTypeIs(Json.value("PASSWORD"));
thenTestPropertyValueIs(Json.value("foobar"));
}

private void givenEncryptedPassword(final String password) throws KuraException {
this.encryptedPassword = this.cryptoService.encryptAes(password.toCharArray());
}

@Test
Expand Down Expand Up @@ -394,16 +423,16 @@ public void testGetLongArrayProperty() throws KuraException {
@Test
public void testGetPasswordArrayProperty() throws KuraException {

this.EncryptedPassword = this.cryptoService.encryptAes("foobar".toCharArray());
givenEncryptedPassword("foobar");

givenATestConfigurationPropertyWithAdTypeAndValue(Scalar.PASSWORD,
new Password[] { new Password(this.EncryptedPassword) });
new Password[] { new Password(this.encryptedPassword) });

whenRequestIsPerformed(new MethodSpec("GET"), "/configurableComponents/configurations");

thenRequestSucceeds();
thenTestPropertyTypeIs(Json.value("PASSWORD"));
thenTestPropertyValueIs(Json.array(new String(this.EncryptedPassword)));
thenTestPropertyValueIs(Json.array("placeholder"));
}

@Test
Expand Down Expand Up @@ -1036,7 +1065,7 @@ public void testADOption() throws KuraException {

@Test
public void testGetConfigurationByPid() throws KuraException {
this.EncryptedPassword = this.cryptoService.encryptAes("foobar".toCharArray());
givenEncryptedPassword("foobar");
givenConfigurations(configurationBuilder("foo") //
.withDefinition( //
ocdBuilder("foo") //
Expand All @@ -1046,7 +1075,7 @@ public void testGetConfigurationByPid() throws KuraException {
.build()) //
.build()) //
.withConfigurationProperties(
singletonMap("testProp", new Password[] { new Password(this.EncryptedPassword) }))
singletonMap("testProp", new Password[] { new Password(this.encryptedPassword) }))
.build());

whenRequestIsPerformed(new MethodSpec("POST"), "/configurableComponents/configurations/byPid",
Expand All @@ -1058,14 +1087,14 @@ public void testGetConfigurationByPid() throws KuraException {
+ "{\"label\":\"pass\",\"value\":\"baz\"}],\"id\":\"fooAdName\",\"type\":\"PASSWORD\","
+ "\"cardinality\":0,\"isRequired\":false}],\"id\":\"foo\"},"
+ "\"properties\":{\"testProp\":{\"value\":[\""
+ new String(this.EncryptedPassword)
+ "placeholder"
+ "\"],\"type\":\"PASSWORD\"}}}"),
self().field("configs").arrayItem(0));
}

@Test
public void testGetConfigurationByPidDefault() throws KuraException {
this.EncryptedPassword = this.cryptoService.encryptAes("foobar".toCharArray());
givenEncryptedPassword("foobar");
givenConfigurations(configurationBuilder("foo") //
.withDefinition( //
ocdBuilder("foo") //
Expand All @@ -1076,7 +1105,7 @@ public void testGetConfigurationByPidDefault() throws KuraException {
.build()) //
.build()) //
.withConfigurationProperties(
singletonMap("testProp", new Password[] { new Password(this.EncryptedPassword) }))
singletonMap("testProp", new Password[] { new Password(this.encryptedPassword) }))
.build());

whenRequestIsPerformed(new MethodSpec("POST"), "/configurableComponents/configurations/byPid/_default",
Expand All @@ -1088,7 +1117,7 @@ public void testGetConfigurationByPidDefault() throws KuraException {
+ "{\"label\":\"pass\",\"value\":\"baz\"}],\"id\":\"fooAdName\",\"type\":\"STRING\","
+ "\"cardinality\":0,\"defaultValue\":\"default\",\"isRequired\":false}]"
+ ",\"id\":\"foo\"},\"properties\":{\"testProp\":{\"value\":[\""
+ new String(this.EncryptedPassword)
+ new String(this.encryptedPassword)
+ "\"],\"type\":\"PASSWORD\"}}}"),
self().field("configs").arrayItem(0));
}
Expand Down Expand Up @@ -1405,6 +1434,9 @@ private void givenConfigurations(final ComponentConfiguration... configurations)
final String pid = i.getArgument(0, String.class);
return byPid.get(pid);
});

Mockito.when(configurationService.getSnapshot(Mockito.anyLong()))
.thenReturn(byPid.values().stream().collect(Collectors.toList()));
}

private void givenATestConfigurationPropertyWithAdTypeAndValue(final Scalar type, final Object value)
Expand Down