This is an automated email from the ASF dual-hosted git repository.
riemer pushed a commit to branch 3822-opc-ua-x509-user-authentication
in repository https://gitbox.apache.org/repos/asf/streampipes.git
The following commit(s) were added to
refs/heads/3822-opc-ua-x509-user-authentication by this push:
new fd91938bea feat(#3822): Support OPC-UA certificate authentication
fd91938bea is described below
commit fd91938beae0334fdd52fcb4732a434c48722076
Author: Dominik Riemer <[email protected]>
AuthorDate: Tue Oct 7 15:13:33 2025 +0200
feat(#3822): Support OPC-UA certificate authentication
---
.../opcua/OpcUaConnectorsModuleExport.java | 6 +-
.../connectors/opcua/adapter/OpcUaAdapter.java | 2 +-
.../opcua/config/SharedUserConfiguration.java | 13 ++-
.../opcua/config/SpOpcUaConfigExtractor.java | 13 ++-
.../opcua/config/identity/X509IdentityConfig.java | 114 +++++++++++++++++++++
.../opcua/migration/OpcUaAdapterMigrationV6.java | 76 ++++++++++++++
.../opcua/migration/OpcUaSinkMigrationV2.java | 48 +++++++++
.../connectors/opcua/sink/OpcUaSink.java | 2 +-
.../strings.en | 9 ++
.../strings.en | 9 ++
ui/src/app/core-ui/core-ui.module.ts | 3 +-
.../static-free-input.component.html | 43 +++++---
.../static-free-input.component.ts | 4 +
13 files changed, 316 insertions(+), 26 deletions(-)
diff --git
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/OpcUaConnectorsModuleExport.java
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/OpcUaConnectorsModuleExport.java
index e70c1dceea..4eb8168567 100644
---
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/OpcUaConnectorsModuleExport.java
+++
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/OpcUaConnectorsModuleExport.java
@@ -29,7 +29,9 @@ import
org.apache.streampipes.extensions.connectors.opcua.migration.OpcUaAdapter
import
org.apache.streampipes.extensions.connectors.opcua.migration.OpcUaAdapterMigrationV3;
import
org.apache.streampipes.extensions.connectors.opcua.migration.OpcUaAdapterMigrationV4;
import
org.apache.streampipes.extensions.connectors.opcua.migration.OpcUaAdapterMigrationV5;
+import
org.apache.streampipes.extensions.connectors.opcua.migration.OpcUaAdapterMigrationV6;
import
org.apache.streampipes.extensions.connectors.opcua.migration.OpcUaSinkMigrationV1;
+import
org.apache.streampipes.extensions.connectors.opcua.migration.OpcUaSinkMigrationV2;
import org.apache.streampipes.extensions.connectors.opcua.sink.OpcUaSink;
import java.util.List;
@@ -64,7 +66,9 @@ public class OpcUaConnectorsModuleExport implements
IExtensionModuleExport {
new OpcUaAdapterMigrationV3(),
new OpcUaAdapterMigrationV4(),
new OpcUaAdapterMigrationV5(),
- new OpcUaSinkMigrationV1()
+ new OpcUaAdapterMigrationV6(),
+ new OpcUaSinkMigrationV1(),
+ new OpcUaSinkMigrationV2()
);
}
}
diff --git
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/adapter/OpcUaAdapter.java
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/adapter/OpcUaAdapter.java
index 75d0b2ec44..d9c6f4d7b0 100644
---
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/adapter/OpcUaAdapter.java
+++
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/adapter/OpcUaAdapter.java
@@ -237,7 +237,7 @@ public class OpcUaAdapter implements StreamPipesAdapter,
IPullAdapter, SupportsR
@Override
public IAdapterConfiguration declareConfig() {
- var builder = AdapterConfigurationBuilder.create(ID, 5, () -> new
OpcUaAdapter(clientProvider))
+ var builder = AdapterConfigurationBuilder.create(ID, 6, () -> new
OpcUaAdapter(clientProvider))
.withAssets(ExtensionAssetType.DOCUMENTATION, ExtensionAssetType.ICON)
.withLocales(Locales.EN)
.withCategory(AdapterType.Generic, AdapterType.Manufacturing)
diff --git
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/SharedUserConfiguration.java
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/SharedUserConfiguration.java
index 2668b90347..2e3960baf4 100644
---
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/SharedUserConfiguration.java
+++
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/SharedUserConfiguration.java
@@ -56,6 +56,9 @@ public class SharedUserConfiguration {
public static final String SECURITY_POLICY = "securityPolicy";
public static final String USER_AUTHENTICATION = "userAuthentication";
public static final String USER_AUTHENTICATION_ANONYMOUS = "anonymous";
+ public static final String X509_GROUP = "x509Group";
+ public static final String X509_PRIVATE_KEY_PEM = "x509PrivateKeyPem";
+ public static final String X509_PUBLIC_KEY_PEM = "x509PublicKeyPem";
public static OneOfStaticProperty makeNamingStrategyOption() {
return StaticProperties.singleValueSelection(
@@ -72,6 +75,13 @@ public class SharedUserConfiguration {
var dependsOn = getDependsOn(adapterConfig);
+ var x509Group = StaticProperties.group(
+ Labels.withId(X509_GROUP),
+ StaticProperties.secretValue(Labels.withId(X509_PRIVATE_KEY_PEM)),
+
StaticProperties.stringFreeTextProperty(Labels.withId(X509_PUBLIC_KEY_PEM),
true, false));
+
+ x509Group.setHorizontalRendering(false);
+
builder
.requiredSingleValueSelection(
Labels.withId(SECURITY_MODE),
@@ -89,7 +99,8 @@ public class SharedUserConfiguration {
StaticProperties.stringFreeTextProperty(
Labels.withId(USERNAME)),
StaticProperties.secretValue(Labels.withId(PASSWORD))
- ))
+ )),
+ Alternatives.from(Labels.withId(X509_GROUP), x509Group)
)
.requiredAlternatives(Labels.withId(OPC_HOST_OR_URL),
Alternatives.from(
diff --git
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/SpOpcUaConfigExtractor.java
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/SpOpcUaConfigExtractor.java
index 36f49e4754..e6fea5c2d6 100644
---
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/SpOpcUaConfigExtractor.java
+++
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/SpOpcUaConfigExtractor.java
@@ -23,6 +23,7 @@ import
org.apache.streampipes.extensions.api.extractor.IParameterExtractor;
import
org.apache.streampipes.extensions.api.extractor.IStaticPropertyExtractor;
import
org.apache.streampipes.extensions.connectors.opcua.config.identity.AnonymousIdentityConfig;
import
org.apache.streampipes.extensions.connectors.opcua.config.identity.UsernamePasswordIdentityConfig;
+import
org.apache.streampipes.extensions.connectors.opcua.config.identity.X509IdentityConfig;
import
org.apache.streampipes.extensions.connectors.opcua.config.security.SecurityConfig;
import org.apache.streampipes.extensions.connectors.opcua.utils.OpcUaLabels;
import
org.apache.streampipes.extensions.connectors.opcua.utils.OpcUaNamingStrategy;
@@ -44,6 +45,7 @@ import static
org.apache.streampipes.extensions.connectors.opcua.utils.OpcUaLabe
import static
org.apache.streampipes.extensions.connectors.opcua.utils.OpcUaLabels.PULLING_INTERVAL;
import static
org.apache.streampipes.extensions.connectors.opcua.utils.OpcUaLabels.PULL_MODE;
import static
org.apache.streampipes.extensions.connectors.opcua.utils.OpcUaLabels.USERNAME;
+import static
org.apache.streampipes.extensions.connectors.opcua.utils.OpcUaLabels.USERNAME_GROUP;
public class SpOpcUaConfigExtractor {
@@ -128,15 +130,16 @@ public class SpOpcUaConfigExtractor {
config.setOpcServerURL(serverAddress + ":" + port);
}
- boolean unauthenticated = selectedAlternativeAuthentication.equals(
- SharedUserConfiguration.USER_AUTHENTICATION_ANONYMOUS
- );
- if (unauthenticated) {
+ if
(selectedAlternativeAuthentication.equals(SharedUserConfiguration.USER_AUTHENTICATION_ANONYMOUS))
{
config.setIdentityConfig(new AnonymousIdentityConfig());
- } else {
+ } else if
(selectedAlternativeAuthentication.equals(USERNAME_GROUP.name())) {
String username = extractor.singleValueParameter(USERNAME.name(),
String.class);
String password = extractor.secretValue(PASSWORD.name());
config.setIdentityConfig(new UsernamePasswordIdentityConfig(username,
password));
+ } else {
+ String privateKeyPem =
extractor.secretValue(SharedUserConfiguration.X509_PRIVATE_KEY_PEM);
+ String publicKeyPem =
extractor.textParameter(SharedUserConfiguration.X509_PUBLIC_KEY_PEM);
+ config.setIdentityConfig(new X509IdentityConfig(publicKeyPem,
privateKeyPem));
}
return config;
diff --git
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/identity/X509IdentityConfig.java
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/identity/X509IdentityConfig.java
new file mode 100644
index 0000000000..1b79459586
--- /dev/null
+++
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/config/identity/X509IdentityConfig.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.streampipes.extensions.connectors.opcua.config.identity;
+
+import org.apache.commons.codec.digest.DigestUtils;
+import org.eclipse.milo.opcua.sdk.client.api.config.OpcUaClientConfigBuilder;
+import org.eclipse.milo.opcua.sdk.client.api.identity.X509IdentityProvider;
+
+import java.io.ByteArrayInputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.Base64;
+import java.util.Objects;
+
+public class X509IdentityConfig implements IdentityConfig {
+
+ private final X509Certificate certificate;
+ private final PrivateKey privateKey;
+
+ /**
+ * @param certificatePem String containing one X.509 certificate in PEM
+ * (-----BEGIN CERTIFICATE----- ... -----END
CERTIFICATE-----)
+ * @param privateKeyPem String containing a PKCS#8 private key in PEM
+ * (-----BEGIN PRIVATE KEY----- ... -----END PRIVATE
KEY-----)
+ */
+ public X509IdentityConfig(String certificatePem, String privateKeyPem) {
+ this.certificate = parseCertificatePem(certificatePem);
+ this.privateKey = parsePrivateKeyPem(privateKeyPem);
+ }
+
+ @Override
+ public void configureIdentity(OpcUaClientConfigBuilder builder) {
+ builder.setIdentityProvider(new X509IdentityProvider(certificate,
privateKey));
+ }
+
+ @Override
+ public String toString() {
+ try {
+ String subject = certificate.getSubjectX500Principal().getName();
+ String thumb = DigestUtils.sha256Hex(certificate.getEncoded());
+ return String.format("%s [%s]", subject, thumb);
+ } catch (Exception e) {
+ return "X509PemIdentity";
+ }
+ }
+
+ private X509Certificate parseCertificatePem(String pem) {
+ Objects.requireNonNull(pem, "certificatePem");
+ byte[] der = extractPemBlock(pem, "CERTIFICATE");
+ try {
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ return (X509Certificate) cf.generateCertificate(new
ByteArrayInputStream(der));
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Failed to parse X.509 certificate
PEM", e);
+ }
+ }
+
+ private PrivateKey parsePrivateKeyPem(String pem) {
+ Objects.requireNonNull(pem, "privateKeyPem");
+ byte[] der = extractPemBlock(pem, "PRIVATE KEY"); // PKCS#8
+ PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(der);
+
+ // Try common algorithms in order.
+ String[] algos = new String[] { "RSA", "EC", "Ed25519", "Ed448" };
+ for (String algo : algos) {
+ try {
+ KeyFactory kf = KeyFactory.getInstance(algo);
+ return kf.generatePrivate(spec);
+ } catch (Exception ignore) {
+ // try next
+ }
+ }
+ throw new IllegalArgumentException(
+ "Unsupported or invalid PKCS#8 private key. " +
+ "Make sure it is an unencrypted PKCS#8 key (BEGIN PRIVATE KEY).");
+ }
+
+ private static byte[] extractPemBlock(String pem, String type) {
+ String begin = "-----BEGIN " + type + "-----";
+ String end = "-----END " + type + "-----";
+
+ String normalized = pem.replace("\r", "");
+ int start = normalized.indexOf(begin);
+ int stop = normalized.indexOf(end);
+ if (start < 0 || stop < 0) {
+ throw new IllegalArgumentException("Missing PEM markers for " + type);
+ }
+ String base64 = normalized.substring(start + begin.length(), stop)
+ .replace("\n", "")
+ .replace("\t", "")
+ .replace(" ", "");
+ return
Base64.getMimeDecoder().decode(base64.getBytes(StandardCharsets.US_ASCII));
+ }
+}
diff --git
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/migration/OpcUaAdapterMigrationV6.java
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/migration/OpcUaAdapterMigrationV6.java
new file mode 100644
index 0000000000..619f565f71
--- /dev/null
+++
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/migration/OpcUaAdapterMigrationV6.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.streampipes.extensions.connectors.opcua.migration;
+
+import org.apache.streampipes.extensions.api.extractor.IParameterExtractor;
+import
org.apache.streampipes.extensions.api.extractor.IStaticPropertyExtractor;
+import org.apache.streampipes.extensions.api.migration.IAdapterMigrator;
+import org.apache.streampipes.extensions.connectors.opcua.adapter.OpcUaAdapter;
+import org.apache.streampipes.model.connect.adapter.AdapterDescription;
+import org.apache.streampipes.model.extensions.svcdiscovery.SpServiceTagPrefix;
+import org.apache.streampipes.model.migration.MigrationResult;
+import org.apache.streampipes.model.migration.ModelMigratorConfig;
+import org.apache.streampipes.model.staticproperty.StaticProperty;
+import org.apache.streampipes.model.staticproperty.StaticPropertyAlternatives;
+import org.apache.streampipes.sdk.StaticProperties;
+import org.apache.streampipes.sdk.helpers.Alternatives;
+import org.apache.streampipes.sdk.helpers.Labels;
+
+import java.util.List;
+
+import static
org.apache.streampipes.extensions.connectors.opcua.config.SharedUserConfiguration.X509_GROUP;
+import static
org.apache.streampipes.extensions.connectors.opcua.config.SharedUserConfiguration.X509_PRIVATE_KEY_PEM;
+import static
org.apache.streampipes.extensions.connectors.opcua.config.SharedUserConfiguration.X509_PUBLIC_KEY_PEM;
+
+public class OpcUaAdapterMigrationV6 implements IAdapterMigrator {
+ @Override
+ public ModelMigratorConfig config() {
+ return new ModelMigratorConfig(
+ OpcUaAdapter.ID,
+ SpServiceTagPrefix.ADAPTER,
+ 5,
+ 6
+ );
+ }
+
+ @Override
+ public MigrationResult<AdapterDescription> migrate(AdapterDescription
element,
+ IStaticPropertyExtractor
extractor) throws RuntimeException {
+ var config = element.getConfig();
+ element.setConfig(migrate(config, 4));
+
+ return MigrationResult.success(element);
+ }
+
+ public List<StaticProperty> migrate(List<StaticProperty> staticProperties,
+ int authenticationConfigIndex) {
+ var authentication = staticProperties.get(authenticationConfigIndex);
+
+ if (authentication instanceof StaticPropertyAlternatives) {
+ var group = StaticProperties.group(
+ Labels.withId(X509_GROUP),
+ StaticProperties.secretValue(Labels.withId(X509_PRIVATE_KEY_PEM)),
+
StaticProperties.stringFreeTextProperty(Labels.withId(X509_PUBLIC_KEY_PEM),
true, false));
+ group.setHorizontalRendering(false);
+ var x509Alternative = Alternatives.from(Labels.withId(X509_GROUP),
group);
+ ((StaticPropertyAlternatives)
authentication).getAlternatives().add(x509Alternative);
+ }
+ return staticProperties;
+ }
+}
diff --git
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/migration/OpcUaSinkMigrationV2.java
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/migration/OpcUaSinkMigrationV2.java
new file mode 100644
index 0000000000..29656a9c75
--- /dev/null
+++
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/migration/OpcUaSinkMigrationV2.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.streampipes.extensions.connectors.opcua.migration;
+
+import
org.apache.streampipes.extensions.api.extractor.IDataSinkParameterExtractor;
+import org.apache.streampipes.extensions.api.migration.IDataSinkMigrator;
+import org.apache.streampipes.extensions.connectors.opcua.sink.OpcUaSink;
+import org.apache.streampipes.model.extensions.svcdiscovery.SpServiceTagPrefix;
+import org.apache.streampipes.model.graph.DataSinkInvocation;
+import org.apache.streampipes.model.migration.MigrationResult;
+import org.apache.streampipes.model.migration.ModelMigratorConfig;
+
+public class OpcUaSinkMigrationV2 implements IDataSinkMigrator {
+ @Override
+ public ModelMigratorConfig config() {
+ return new ModelMigratorConfig(
+ OpcUaSink.ID,
+ SpServiceTagPrefix.DATA_SINK,
+ 1,
+ 2
+ );
+ }
+
+ @Override
+ public MigrationResult<DataSinkInvocation> migrate(DataSinkInvocation
element,
+
IDataSinkParameterExtractor extractor) throws RuntimeException {
+ var config = element.getStaticProperties();
+ var migratedConfigs = new OpcUaAdapterMigrationV6().migrate(config, 3);
+ element.setStaticProperties(migratedConfigs);
+ return MigrationResult.success(element);
+ }
+}
diff --git
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/sink/OpcUaSink.java
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/sink/OpcUaSink.java
index 6e908f1102..9270f18b8c 100644
---
a/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/sink/OpcUaSink.java
+++
b/streampipes-extensions/streampipes-connectors-opcua/src/main/java/org/apache/streampipes/extensions/connectors/opcua/sink/OpcUaSink.java
@@ -57,7 +57,7 @@ public class OpcUaSink implements IStreamPipesDataSink,
SupportsRuntimeConfig {
@Override
public IDataSinkConfiguration declareConfig() {
- var builder = DataSinkBuilder.create(ID, 0)
+ var builder = DataSinkBuilder.create(ID, 2)
.withLocales(Locales.EN)
.withAssets(ExtensionAssetType.DOCUMENTATION, ExtensionAssetType.ICON)
.category(DataSinkType.FORWARD)
diff --git
a/streampipes-extensions/streampipes-connectors-opcua/src/main/resources/org.apache.streampipes.connect.iiot.adapters.opcua/strings.en
b/streampipes-extensions/streampipes-connectors-opcua/src/main/resources/org.apache.streampipes.connect.iiot.adapters.opcua/strings.en
index a7a8008e52..853b9f11ea 100644
---
a/streampipes-extensions/streampipes-connectors-opcua/src/main/resources/org.apache.streampipes.connect.iiot.adapters.opcua/strings.en
+++
b/streampipes-extensions/streampipes-connectors-opcua/src/main/resources/org.apache.streampipes.connect.iiot.adapters.opcua/strings.en
@@ -85,3 +85,12 @@ incomplete-event-handling.description=Select how events with
missing values (e.g
NAMING_STRATEGY.title=Naming Strategy
NAMING_STRATEGY.description=Select how the runtime name of the OPC-UA nodes
are generated.
+
+x509Group.title=X.509 Certificate
+x509Group.description=
+
+x509PrivateKeyPem.title=Private Key PEM
+x509PrivateKeyPem.description=Private key in PEM format
+
+x509PublicKeyPem.title=Certificate PEM
+x509PublicKeyPem.description=Public key in PEM format
diff --git
a/streampipes-extensions/streampipes-connectors-opcua/src/main/resources/org.apache.streampipes.sinks.databases.jvm.opcua/strings.en
b/streampipes-extensions/streampipes-connectors-opcua/src/main/resources/org.apache.streampipes.sinks.databases.jvm.opcua/strings.en
index b26c09c598..a08aef30b6 100644
---
a/streampipes-extensions/streampipes-connectors-opcua/src/main/resources/org.apache.streampipes.sinks.databases.jvm.opcua/strings.en
+++
b/streampipes-extensions/streampipes-connectors-opcua/src/main/resources/org.apache.streampipes.sinks.databases.jvm.opcua/strings.en
@@ -68,3 +68,12 @@ userAuthentication.description=Choose an authentication
method for the user
anonymous.title=Anonymous
anonymous.description=
+
+x509Group.title=X.509 Certificate
+x509Group.description=
+
+x509PrivateKeyPem.title=Private Key PEM
+x509PrivateKeyPem.description=Private key in PEM format
+
+x509PublicKeyPem.title=Certificate PEM
+x509PublicKeyPem.description=Public key in PEM format
diff --git a/ui/src/app/core-ui/core-ui.module.ts
b/ui/src/app/core-ui/core-ui.module.ts
index cbc12c8ca2..9d2886d92d 100644
--- a/ui/src/app/core-ui/core-ui.module.ts
+++ b/ui/src/app/core-ui/core-ui.module.ts
@@ -67,7 +67,6 @@ import { PipelineElementTemplatePipe } from
'./pipeline-element-template-config/
import { StatusWidgetComponent } from './status/status-widget.component';
import { SpSimpleMetricsComponent } from
'./monitoring/simple-metrics/simple-metrics.component';
import { SpSimpleLogsComponent } from
'./monitoring/simple-logs/simple-logs.component';
-//import { HelpComponent } from
'../../../projects/streampipes/shared-ui/src/lib/dialog/pipeline-element-help/help.component';
import { MatButtonModule } from '@angular/material/button';
import { MatCardModule } from '@angular/material/card';
import { MatCheckboxModule } from '@angular/material/checkbox';
@@ -108,6 +107,7 @@ import { JsonPrettyPrintPipe } from
'./pipes/json-pretty-print.pipe';
import { YamlPrettyPrintPipe } from './pipes/yaml-pretty-print.pipe';
import { TopicsComponent } from './topics/topics.component';
import { TranslateModule } from '@ngx-translate/core';
+import { TextFieldModule } from '@angular/cdk/text-field';
@NgModule({
imports: [
@@ -159,6 +159,7 @@ import { TranslateModule } from '@ngx-translate/core';
MatTreeModule,
TranslateModule.forChild(),
LeafletModule,
+ TextFieldModule,
],
declarations: [
ConfigurationCodePanelComponent,
diff --git
a/ui/src/app/core-ui/static-properties/static-free-input/static-free-input.component.html
b/ui/src/app/core-ui/static-properties/static-free-input/static-free-input.component.html
index 16ef21767d..73ee8ba815 100644
---
a/ui/src/app/core-ui/static-properties/static-free-input/static-free-input.component.html
+++
b/ui/src/app/core-ui/static-properties/static-free-input/static-free-input.component.html
@@ -22,7 +22,7 @@
fxLayout="row"
*ngIf="!staticProperty.valueSpecification && !staticProperty.multiLine"
>
- <mat-form-field fxFlex color="accent">
+ <mat-form-field fxFlex>
<input
formControlName="{{ fieldName }}"
fxFlex
@@ -46,7 +46,6 @@
[attr.data-cy]="fieldName"
[discrete]="true"
[displayWith]="formatLabel"
- color="accent"
>
<input
matSliderThumb
@@ -80,20 +79,32 @@
</div>
</div>
<div fxFlex="100" *ngIf="staticProperty.multiLine">
- <quill-editor
- fxFlex="100"
- *ngIf="staticProperty.htmlFontFormat"
- #textEditor
- formControlName="{{ fieldName }}"
- [modules]="quillModulesFontFormat"
- ></quill-editor>
- <quill-editor
- fxFlex="100"
- *ngIf="!staticProperty.htmlFontFormat"
- #textEditor
- formControlName="{{ fieldName }}"
- [modules]="quillModules"
- ></quill-editor>
+ @if (staticProperty.htmlAllowed) {
+ <quill-editor
+ fxFlex="100"
+ *ngIf="staticProperty.htmlFontFormat"
+ #textEditor
+ formControlName="{{ fieldName }}"
+ [modules]="quillModulesFontFormat"
+ ></quill-editor>
+ <quill-editor
+ fxFlex="100"
+ *ngIf="!staticProperty.htmlFontFormat"
+ #textEditor
+ formControlName="{{ fieldName }}"
+ [modules]="quillModules"
+ ></quill-editor>
+ } @else {
+ <mat-form-field fxFlex>
+ <textarea
+ formControlName="{{ fieldName }}"
+ matInput
+ [placeholder]="staticProperty.label"
+ (blur)="emitUpdate()"
+ [attr.data-cy]="fieldName"
+ ></textarea>
+ </mat-form-field>
+ }
</div>
</div>
</div>
diff --git
a/ui/src/app/core-ui/static-properties/static-free-input/static-free-input.component.ts
b/ui/src/app/core-ui/static-properties/static-free-input/static-free-input.component.ts
index 664471eb25..f5a963b626 100644
---
a/ui/src/app/core-ui/static-properties/static-free-input/static-free-input.component.ts
+++
b/ui/src/app/core-ui/static-properties/static-free-input/static-free-input.component.ts
@@ -32,6 +32,7 @@ import {
import { AbstractValidatedStaticPropertyRenderer } from
'../base/abstract-validated-static-property';
import { QuillEditorComponent } from 'ngx-quill';
import { TranslateService } from '@ngx-translate/core';
+import { CdkTextareaAutosize } from '@angular/cdk/text-field';
@Component({
selector: 'sp-app-static-free-input',
@@ -61,6 +62,8 @@ export class StaticFreeInputComponent
@ViewChild('textEditor', { static: false })
quillEditorComponent: QuillEditorComponent;
+ @ViewChild('autosize') autosize: CdkTextareaAutosize;
+
constructor() {
super();
}
@@ -69,6 +72,7 @@ export class StaticFreeInputComponent
this.addValidator(this.staticProperty.value, this.collectValidators());
this.enableValidators();
this.emitUpdate();
+ console.log(this.staticProperty);
}
collectValidators() {