Skip to content

Commit

Permalink
feat: Set default web ports for console and rest (#5624)
Browse files Browse the repository at this point in the history
* feat: Set default web ports for console and rest

Signed-off-by: MMaiero <[email protected]>

* Additional changes

Signed-off-by: MMaiero <[email protected]>

* Added defaults

Signed-off-by: MMaiero <[email protected]>

* More changes

Signed-off-by: MMaiero <[email protected]>

* fix: tentative test update

Signed-off-by: MMaiero <[email protected]>

* WIP: refactor configuration service for the self configuring part

Signed-off-by: MMaiero <[email protected]>

* Further cleaning

Signed-off-by: MMaiero <[email protected]>

* Improvement on corner cases

Signed-off-by: MMaiero <[email protected]>

* Added more coverage

Signed-off-by: MMaiero <[email protected]>

* Test updated following comment

Signed-off-by: MMaiero <[email protected]>

---------

Signed-off-by: MMaiero <[email protected]>
  • Loading branch information
MMaiero authored Jan 11, 2025
1 parent 2cb6b9a commit db8462c
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 138 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2011, 2022 Eurotech and/or its affiliates and others
* Copyright (c) 2011, 2024 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.core.configuration;

import static java.util.Objects.isNull;
import static java.util.Objects.requireNonNull;

import java.io.File;
Expand Down Expand Up @@ -64,6 +65,7 @@
import org.eclipse.kura.marshalling.Marshaller;
import org.eclipse.kura.marshalling.Unmarshaller;
import org.eclipse.kura.system.SystemService;
import org.eclipse.kura.util.service.ServiceUtil;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
Expand Down Expand Up @@ -1232,103 +1234,99 @@ private ComponentConfiguration getConfigurableComponentConfiguration(String pid)

private ComponentConfiguration getSelfConfiguringComponentConfiguration(String pid) {
ComponentConfiguration cc = null;
final ServiceReference<?>[] refs = ServiceUtil.getServiceReferences(this.bundleContext,
SelfConfiguringComponent.class, null);

try {
ServiceReference<?>[] refs = this.ctx.getBundleContext().getServiceReferences((String) null, null);
if (refs != null) {
for (ServiceReference<?> ref : refs) {
String ppid = (String) ref.getProperty(KURA_SERVICE_PID);
if (pid.equals(ppid)) {
Object obj = this.ctx.getBundleContext().getService(ref);
try {
if (obj instanceof SelfConfiguringComponent) {
SelfConfiguringComponent selfConfigComp = null;
selfConfigComp = (SelfConfiguringComponent) obj;
try {
cc = selfConfigComp.getConfiguration();
if (cc.getPid() == null || !cc.getPid().equals(pid)) {
logger.error(
"Invalid pid for returned Configuration of SelfConfiguringComponent with pid: "
+ pid + ". Ignoring it.");
return null;
}

OCD ocd = cc.getDefinition();
if (ocd != null) {
List<AD> ads = ocd.getAD();

if (ads != null) {
for (AD ad : ads) {
String adId = ad.getId();
String adType = ad.getType().value();

if (adId == null) {
logger.error(
"null required id for AD for returned Configuration of SelfConfiguringComponent with pid: {}",
pid);
return null;
}
if (adType == null) {
logger.error(
"null required type for AD id: {} for returned Configuration of SelfConfiguringComponent with pid: {}",
adId, pid);
return null;
}

Map<String, Object> props = cc.getConfigurationProperties();
if (props != null) {
Object value = props.get(adId);
if (value != null) {
String propType;
if (!value.getClass().isArray()) {
propType = value.getClass().getSimpleName();
} else {
propType = value.getClass().getComponentType()
.getSimpleName();
}

try {
logger.debug(
"pid: {}, property name: {}, type: {}, value: {}",
pid, adId, propType, value);
Scalar propertyScalar = Scalar.fromValue(propType);
Scalar adScalar = Scalar.fromValue(adType);
if (propertyScalar != adScalar) {
logger.error(
"Type: {} for property named: {} does not match the AD type: {} for returned Configuration of SelfConfiguringComponent with pid: {}",
new Object[] { propType, adId, adType, pid });
return null;
}
} catch (IllegalArgumentException e) {
logger.error(
"Invalid class: {} for property named: {} for returned Configuration of SelfConfiguringComponent with pid: "
+ pid,
propType, adId);
return null;
}
}
}
}
}
}
} catch (KuraException e) {
logger.error(GETTING_CONFIGURATION_ERROR, pid, e);
}
} else {
logger.error("Component {} is not a SelfConfiguringComponent. Ignoring it.", obj);
}
} finally {
this.ctx.getBundleContext().ungetService(ref);
}
for (ServiceReference<?> ref : refs) {
String ppid = (String) ref.getProperty(KURA_SERVICE_PID);
final SelfConfiguringComponent selfConfigComp = (SelfConfiguringComponent) this.bundleContext
.getService(ref);
if (pid.equals(ppid)) {

cc = selfConfigComp.getConfiguration();
if (!isValidSelfConfiguringComponent(pid, cc)) {
return null;
}
}
}
} catch (InvalidSyntaxException e) {
} catch (KuraException e) {
logger.error(GETTING_CONFIGURATION_ERROR, pid, e);
} finally {
ServiceUtil.ungetServiceReferences(this.bundleContext, refs);
}

return cc;
}

private boolean isValidSelfConfiguringComponent(String pid, ComponentConfiguration cc) {

if (isNull(cc) || cc.getPid() == null || !cc.getPid().equals(pid)) {
logger.error(
"Invalid pid for returned Configuration of SelfConfiguringComponent with pid: {}. Ignoring it.",
pid);
return false;
}

OCD ocd = cc.getDefinition();
if (isNull(ocd) || isNull(ocd.getAD())) {
return false;
}

List<AD> ads = ocd.getAD();

for (AD ad : ads) {
String adId = ad.getId();
String adType = ad.getType().value();

if (isNull(adId) || isNull(adType)) {
logger.error(
"null required type for AD id: {} for returned Configuration of SelfConfiguringComponent with pid: {}",
adId, pid);
return false;
}

Map<String, Object> props = cc.getConfigurationProperties();
if (!isNull(props) && !isNull(props.get(adId)) && !isMatchingADType(pid, adId, adType, props.get(adId))) {
return false;
}
}
return true;
}

private boolean isMatchingADType(String pid, String adId, String adType, Object value) {
boolean result = false;
try {
logger.debug("pid: {}, property name: {}, value: {}", pid, adId, value);
Scalar propertyScalar = getScalarFromObject(value);
Scalar adScalar = Scalar.fromValue(adType);
if (propertyScalar != adScalar) {
logger.error(
"Type: {} for property named: {} does not match the AD type: {} for returned Configuration of SelfConfiguringComponent with pid: {}",
propertyScalar.name(), adId, adType, pid);
}
result = true;
} catch (IllegalArgumentException e) {
logger.error(
"Invalid class for property named: {} for returned Configuration of SelfConfiguringComponent with pid: {}",
adId, pid);
}
return result;
}

private Scalar getScalarFromObject(Object p) {
Class<?> clazz = p.getClass();
if (clazz.isArray()) {
Object[] tempArray = (Object[]) p;
if (tempArray.length > 0 && tempArray[0] != null) {
clazz = tempArray[0].getClass();
} else {
clazz = clazz.getComponentType();
}
}
return Scalar.fromValue(clazz.getSimpleName());
}

private TreeSet<Long> getSnapshotsInternal() {
// keeps the list of snapshots ordered
TreeSet<Long> ids = new TreeSet<>();
Expand Down Expand Up @@ -1901,10 +1899,7 @@ public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
TrackedComponentFactory other = (TrackedComponentFactory) obj;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
/*******************************************************************************
* Copyright (c) 2021, 2022 Eurotech and/or its affiliates and others
*
* Copyright (c) 2021, 2024 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
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
*
* SPDX-License-Identifier: EPL-2.0
*
*
* Contributors:
* Eurotech
*******************************************************************************/
Expand Down Expand Up @@ -45,10 +45,10 @@ public Object getValue() {
public void validate() {
FailureHandler.requireParameter(this.type, "type");

if (value instanceof List<?>) {
validateArrayProperty(type, (List<?>) value);
if (this.value instanceof List<?>) {
validateArrayProperty(this.type, (List<?>) this.value);
} else {
validateSingletonProperty(type, value);
validateSingletonProperty(this.type, this.value);
}
}

Expand Down Expand Up @@ -96,22 +96,35 @@ private static void validateSingletonProperty(final Scalar type, final Object va

public static Optional<PropertyDTO> fromConfigurationProperty(final Object property) {

return Optional.ofNullable(property).flatMap(p -> scalarFromClass(p.getClass()))
return Optional.ofNullable(property).flatMap(PropertyDTO::getScalarFromObject)
.map(type -> new PropertyDTO(configurationPropertyToDTOProperty(property), type));
}

private static Optional<Scalar> getScalarFromObject(Object p) {
Class<?> clazz = p.getClass();
if (clazz.isArray()) {
Object[] tempArray = (Object[]) p;
if (tempArray.length > 0 && tempArray[0] != null) {
clazz = tempArray[0].getClass();
} else {
clazz = clazz.getComponentType();
}
}
return scalarFromSingletonClass(clazz);
}

public Optional<Object> toConfigurationProperty() {
if (value == null) {
if (this.value == null) {
return Optional.empty();
}

final Optional<Object> asSingleton = singletonToProperty(value, type);
final Optional<Object> asSingleton = singletonToProperty(this.value, this.type);

if (asSingleton.isPresent()) {
return asSingleton;
}

return arrayToProperty(value, type);
return arrayToProperty(this.value, this.type);
}

@SuppressWarnings("unchecked")
Expand Down Expand Up @@ -233,7 +246,7 @@ private static Optional<Object> arrayToProperty(final Object propertyValue, fina
}
}

public static Optional<Scalar> scalarFormSingletonClass(final Class<?> clazz) {
public static Optional<Scalar> scalarFromSingletonClass(final Class<?> clazz) {
final Scalar result;

if (clazz == Boolean.class) {
Expand Down Expand Up @@ -263,14 +276,6 @@ public static Optional<Scalar> scalarFormSingletonClass(final Class<?> clazz) {
return Optional.of(result);
}

public static Optional<Scalar> scalarFromClass(final Class<?> clazz) {
if (clazz.isArray()) {
return scalarFormSingletonClass(clazz.getComponentType());
} else {
return scalarFormSingletonClass(clazz);
}
}

private static Object configurationPropertyToDTOProperty(final Object property) {
if (property instanceof Password) {
return new String(((Password) property).getPassword());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
type="Integer"
cardinality="3"
required="false"
default="443,4443"
min="1"
max="65535"
description="If set to a non empty list, REST API access will be allowed only on the specified ports. If set to an empty list, access will be allowed on all ports. Please make sure that the allowed ports are open in HttpService and Firewall configuration.">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ public class ConsoleOptions {
new AdBuilder("allowed.ports", "Allowed ports", Tscalar.INTEGER) //
.setRequired(false) //
.setCardinality(3) //
.setDefault("443,4443") //
.setDescription(
"If set to a non empty list, Web Console access will be allowed only on the specified ports. If set to an empty list, access will be allowed on all ports. Please make sure that the allowed ports are open in HttpService and Firewall configuration.") //
.build(), //
Expand Down
Loading

0 comments on commit db8462c

Please sign in to comment.