You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@streampipes.apache.org by ri...@apache.org on 2021/05/23 22:00:42 UTC
[incubator-streampipes] 01/02: [STREAMPIPES-374] Improve management
of SecretStaticProperty values
This is an automated email from the ASF dual-hosted git repository.
riemer pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/incubator-streampipes.git
commit 184bc66ee3c0209ec7d50ba624047a9ac6029e20
Author: Dominik Riemer <ri...@fzi.de>
AuthorDate: Sun May 23 16:45:08 2021 +0200
[STREAMPIPES-374] Improve management of SecretStaticProperty values
---
.../master/util/AdapterEncryptionService.java | 33 +-----
.../manager/execution/http/PipelineExecutor.java | 43 +++-----
.../execution/http/PipelineStorageService.java | 34 ++----
.../streampipes/manager/secret/ISecretHandler.java | 26 +++++
.../manager/secret/SecretDecrypter.java | 45 ++++++++
.../manager/secret/SecretEncrypter.java | 43 ++++++++
.../streampipes/manager/secret/SecretProvider.java | 30 ++++++
.../streampipes/manager/secret/SecretService.java | 52 +++++++++
.../streampipes/manager/secret/SecretVisitor.java | 116 +++++++++++++++++++++
9 files changed, 338 insertions(+), 84 deletions(-)
diff --git a/streampipes-connect-container-master/src/main/java/org/apache/streampipes/connect/container/master/util/AdapterEncryptionService.java b/streampipes-connect-container-master/src/main/java/org/apache/streampipes/connect/container/master/util/AdapterEncryptionService.java
index b795bee..984cf58 100644
--- a/streampipes-connect-container-master/src/main/java/org/apache/streampipes/connect/container/master/util/AdapterEncryptionService.java
+++ b/streampipes-connect-container-master/src/main/java/org/apache/streampipes/connect/container/master/util/AdapterEncryptionService.java
@@ -17,13 +17,11 @@
*/
package org.apache.streampipes.connect.container.master.util;
+import org.apache.streampipes.manager.secret.SecretProvider;
import org.apache.streampipes.model.connect.adapter.AdapterDescription;
import org.apache.streampipes.model.connect.adapter.GenericAdapterDescription;
-import org.apache.streampipes.model.staticproperty.SecretStaticProperty;
import org.apache.streampipes.model.staticproperty.StaticProperty;
-import org.apache.streampipes.user.management.encryption.CredentialsManager;
-import java.security.GeneralSecurityException;
import java.util.List;
public class AdapterEncryptionService {
@@ -58,35 +56,10 @@ public class AdapterEncryptionService {
}
private void encrypt(List<StaticProperty> staticProperties) {
- staticProperties
- .stream()
- .filter(SecretStaticProperty.class::isInstance)
- .forEach(secret -> {
- if (!((SecretStaticProperty) secret).getEncrypted()) {
- try {
- String encrypted = CredentialsManager.encrypt(ad.getUserName(),
- ((SecretStaticProperty) secret).getValue());
- ((SecretStaticProperty) secret).setValue(encrypted);
- ((SecretStaticProperty) secret).setEncrypted(true);
- } catch (GeneralSecurityException e) {
- e.printStackTrace();
- }
- }
- });
+ SecretProvider.getEncryptionService(ad.getUserName()).applyConfig(staticProperties);
}
private void decrypt(List<StaticProperty> staticProperties) {
- staticProperties.stream()
- .filter(SecretStaticProperty.class::isInstance)
- .forEach(sp -> {
- try {
- String decrypted = CredentialsManager.decrypt(ad.getUserName(),
- ((SecretStaticProperty) sp).getValue());
- ((SecretStaticProperty) sp).setValue(decrypted);
- ((SecretStaticProperty) sp).setEncrypted(false);
- } catch (GeneralSecurityException e) {
- e.printStackTrace();
- }
- });
+ SecretProvider.getDecryptionService(ad.getUserName()).applyConfig(staticProperties);
}
}
diff --git a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/execution/http/PipelineExecutor.java b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/execution/http/PipelineExecutor.java
index c8d3e68..5775753 100644
--- a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/execution/http/PipelineExecutor.java
+++ b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/execution/http/PipelineExecutor.java
@@ -19,6 +19,7 @@
package org.apache.streampipes.manager.execution.http;
import org.apache.streampipes.manager.execution.status.PipelineStatusManager;
+import org.apache.streampipes.manager.secret.SecretProvider;
import org.apache.streampipes.manager.util.TemporaryGraphStorage;
import org.apache.streampipes.model.SpDataSet;
import org.apache.streampipes.model.base.InvocableStreamPipesEntity;
@@ -30,13 +31,10 @@ import org.apache.streampipes.model.message.PipelineStatusMessageType;
import org.apache.streampipes.model.pipeline.Pipeline;
import org.apache.streampipes.model.pipeline.PipelineHealthStatus;
import org.apache.streampipes.model.pipeline.PipelineOperationStatus;
-import org.apache.streampipes.model.staticproperty.SecretStaticProperty;
import org.apache.streampipes.storage.api.IPipelineStorage;
import org.apache.streampipes.storage.management.StorageDispatcher;
-import org.apache.streampipes.user.management.encryption.CredentialsManager;
import org.lightcouch.DocumentConflictException;
-import java.security.GeneralSecurityException;
import java.util.*;
import java.util.stream.Collectors;
@@ -79,14 +77,16 @@ public class PipelineExecutor {
graphs.addAll(sepas);
graphs.addAll(secs);
- List<InvocableStreamPipesEntity> decryptedGraphs = decryptSecrets(graphs);
+ decryptSecrets(graphs);
- graphs.forEach(g -> g.setStreamRequirements(Collections.emptyList()));
+ //graphs.forEach(g -> g.setStreamRequirements(Collections.emptyList()));
PipelineOperationStatus status = new GraphSubmitter(pipeline.getPipelineId(),
- pipeline.getName(), decryptedGraphs, dataSets)
+ pipeline.getName(), graphs, dataSets)
.invokeGraphs();
+ encryptSecrets(graphs);
+
if (status.isSuccess()) {
storeInvocationGraphs(pipeline.getPipelineId(), graphs, dataSets);
@@ -110,31 +110,12 @@ public class PipelineExecutor {
.forEach(tp -> tp.setGroupId(UUID.randomUUID().toString()));
}
- private List<InvocableStreamPipesEntity> decryptSecrets(List<InvocableStreamPipesEntity> graphs) {
- List<InvocableStreamPipesEntity> decryptedGraphs = new ArrayList<>();
- graphs.stream().map(g -> {
- if (g instanceof DataProcessorInvocation) {
- return new DataProcessorInvocation((DataProcessorInvocation) g);
- } else {
- return new DataSinkInvocation((DataSinkInvocation) g);
- }
- }).forEach(g -> {
- g.getStaticProperties()
- .stream()
- .filter(SecretStaticProperty.class::isInstance)
- .forEach(sp -> {
- try {
- String decrypted = CredentialsManager.decrypt(pipeline.getCreatedByUser(),
- ((SecretStaticProperty) sp).getValue());
- ((SecretStaticProperty) sp).setValue(decrypted);
- ((SecretStaticProperty) sp).setEncrypted(false);
- } catch (GeneralSecurityException e) {
- e.printStackTrace();
- }
- });
- decryptedGraphs.add(g);
- });
- return decryptedGraphs;
+ private void decryptSecrets(List<InvocableStreamPipesEntity> graphs) {
+ SecretProvider.getDecryptionService(pipeline.getCreatedByUser()).apply(graphs);
+ }
+
+ private void encryptSecrets(List<InvocableStreamPipesEntity> graphs) {
+ SecretProvider.getEncryptionService(pipeline.getCreatedByUser()).apply(graphs);
}
public PipelineOperationStatus stopPipeline() {
diff --git a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/execution/http/PipelineStorageService.java b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/execution/http/PipelineStorageService.java
index e0fc07e..d59a4b3 100644
--- a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/execution/http/PipelineStorageService.java
+++ b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/execution/http/PipelineStorageService.java
@@ -21,15 +21,13 @@ package org.apache.streampipes.manager.execution.http;
import org.apache.streampipes.manager.data.PipelineGraph;
import org.apache.streampipes.manager.data.PipelineGraphBuilder;
import org.apache.streampipes.manager.matching.InvocationGraphBuilder;
+import org.apache.streampipes.manager.secret.SecretProvider;
import org.apache.streampipes.model.base.InvocableStreamPipesEntity;
-import org.apache.streampipes.model.pipeline.Pipeline;
import org.apache.streampipes.model.graph.DataProcessorInvocation;
import org.apache.streampipes.model.graph.DataSinkInvocation;
-import org.apache.streampipes.model.staticproperty.SecretStaticProperty;
+import org.apache.streampipes.model.pipeline.Pipeline;
import org.apache.streampipes.storage.management.StorageDispatcher;
-import org.apache.streampipes.user.management.encryption.CredentialsManager;
-import java.security.GeneralSecurityException;
import java.util.List;
import java.util.stream.Collectors;
@@ -42,7 +40,7 @@ public class PipelineStorageService {
}
public void updatePipeline() {
- preparePipeline();
+ encryptSecrets(pipeline);
StorageDispatcher.INSTANCE.getNoSqlStore().getPipelineStorageAPI().updatePipeline(pipeline);
}
@@ -54,7 +52,8 @@ public class PipelineStorageService {
private void preparePipeline() {
PipelineGraph pipelineGraph = new PipelineGraphBuilder(pipeline).buildGraph();
InvocationGraphBuilder builder = new InvocationGraphBuilder(pipelineGraph, pipeline.getPipelineId());
- List<InvocableStreamPipesEntity> graphs = encryptSecrets(builder.buildGraphs());
+ List<InvocableStreamPipesEntity> graphs = builder.buildGraphs();
+ encryptSecrets(graphs);
List<DataSinkInvocation> secs = filter(graphs, DataSinkInvocation.class);
List<DataProcessorInvocation> sepas = filter(graphs, DataProcessorInvocation.class);
@@ -63,23 +62,12 @@ public class PipelineStorageService {
pipeline.setActions(secs);
}
- private List<InvocableStreamPipesEntity> encryptSecrets(List<InvocableStreamPipesEntity> graphs) {
- graphs.forEach(g -> g.getStaticProperties()
- .stream()
- .filter(SecretStaticProperty.class::isInstance)
- .forEach(secret -> {
- if (!((SecretStaticProperty) secret).getEncrypted()) {
- try {
- String encrypted = CredentialsManager.encrypt(pipeline.getCreatedByUser(),
- ((SecretStaticProperty) secret).getValue());
- ((SecretStaticProperty) secret).setValue(encrypted);
- ((SecretStaticProperty) secret).setEncrypted(true);
- } catch (GeneralSecurityException e) {
- e.printStackTrace();
- }
- }
- }));
- return graphs;
+ private void encryptSecrets(List<InvocableStreamPipesEntity> graphs) {
+ SecretProvider.getEncryptionService(pipeline.getCreatedByUser()).apply(graphs);
+ }
+
+ private void encryptSecrets(Pipeline pipeline) {
+ SecretProvider.getEncryptionService(pipeline.getCreatedByUser()).apply(pipeline);
}
private <T> List<T> filter(List<InvocableStreamPipesEntity> graphs, Class<T> clazz) {
diff --git a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/secret/ISecretHandler.java b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/secret/ISecretHandler.java
new file mode 100644
index 0000000..2537ce3
--- /dev/null
+++ b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/secret/ISecretHandler.java
@@ -0,0 +1,26 @@
+/*
+ * 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.manager.secret;
+
+public interface ISecretHandler {
+
+ String apply(String username, String extractedValue);
+
+ boolean shouldApply(boolean encrypted);
+
+}
diff --git a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/secret/SecretDecrypter.java b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/secret/SecretDecrypter.java
new file mode 100644
index 0000000..9685632
--- /dev/null
+++ b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/secret/SecretDecrypter.java
@@ -0,0 +1,45 @@
+/*
+ * 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.manager.secret;
+
+
+import org.apache.streampipes.user.management.encryption.CredentialsManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.security.GeneralSecurityException;
+
+public class SecretDecrypter implements ISecretHandler {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SecretDecrypter.class);
+
+ @Override
+ public String apply(String username, String extractedValue) {
+ try {
+ return CredentialsManager.decrypt(username, extractedValue);
+ } catch (GeneralSecurityException e) {
+ LOG.error("Could not decrypt value, returning original value");
+ return extractedValue;
+ }
+ }
+
+ @Override
+ public boolean shouldApply(boolean encrypted) {
+ return encrypted;
+ }
+}
diff --git a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/secret/SecretEncrypter.java b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/secret/SecretEncrypter.java
new file mode 100644
index 0000000..877858d
--- /dev/null
+++ b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/secret/SecretEncrypter.java
@@ -0,0 +1,43 @@
+/*
+ * 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.manager.secret;
+
+import org.apache.streampipes.user.management.encryption.CredentialsManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.security.GeneralSecurityException;
+
+public class SecretEncrypter implements ISecretHandler {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SecretEncrypter.class);
+ @Override
+ public String apply(String username, String extractedValue) {
+ try {
+ return CredentialsManager.encrypt(username, extractedValue);
+ } catch (GeneralSecurityException e) {
+ LOG.error("Could not encrypt value, returning original value");
+ return extractedValue;
+ }
+ }
+
+ @Override
+ public boolean shouldApply(boolean encrypted) {
+ return !encrypted;
+ }
+}
diff --git a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/secret/SecretProvider.java b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/secret/SecretProvider.java
new file mode 100644
index 0000000..9676677
--- /dev/null
+++ b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/secret/SecretProvider.java
@@ -0,0 +1,30 @@
+/*
+ * 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.manager.secret;
+
+public class SecretProvider {
+
+ public static SecretService getEncryptionService(String username) {
+ return new SecretService(username, new SecretEncrypter()) {
+ };
+ }
+
+ public static SecretService getDecryptionService(String username) {
+ return new SecretService(username, new SecretDecrypter());
+ }
+}
diff --git a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/secret/SecretService.java b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/secret/SecretService.java
new file mode 100644
index 0000000..4f4223b
--- /dev/null
+++ b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/secret/SecretService.java
@@ -0,0 +1,52 @@
+/*
+ * 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.manager.secret;
+
+import org.apache.streampipes.model.base.InvocableStreamPipesEntity;
+import org.apache.streampipes.model.pipeline.Pipeline;
+import org.apache.streampipes.model.staticproperty.StaticProperty;
+
+import java.util.List;
+
+public class SecretService {
+
+ private SecretVisitor visitor;
+
+ public SecretService(String username,
+ ISecretHandler secretHandler) {
+ this.visitor = new SecretVisitor(username, secretHandler);
+ }
+
+ public void apply(Pipeline pipeline) {
+ pipeline.getSepas().forEach(this::apply);
+ pipeline.getActions().forEach(this::apply);
+ }
+
+ public void apply(List<InvocableStreamPipesEntity> graphs) {
+ graphs.forEach(this::apply);
+ }
+
+ public void apply(InvocableStreamPipesEntity graph) {
+ applyConfig(graph.getStaticProperties());
+ }
+
+ public void applyConfig(List<StaticProperty> staticProperties) {
+ staticProperties.forEach(sp -> sp.accept(visitor));
+ }
+
+}
diff --git a/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/secret/SecretVisitor.java b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/secret/SecretVisitor.java
new file mode 100644
index 0000000..de51d4e
--- /dev/null
+++ b/streampipes-pipeline-management/src/main/java/org/apache/streampipes/manager/secret/SecretVisitor.java
@@ -0,0 +1,116 @@
+/*
+ * 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.manager.secret;
+
+import org.apache.streampipes.model.staticproperty.*;
+
+public class SecretVisitor implements StaticPropertyVisitor {
+
+ private String username;
+ private ISecretHandler secretHandler;
+
+ public SecretVisitor(String username,
+ ISecretHandler secretHandler) {
+ this.username = username;
+ this.secretHandler = secretHandler;
+ }
+
+ @Override
+ public void visit(AnyStaticProperty property) {
+
+ }
+
+ @Override
+ public void visit(CodeInputStaticProperty codeInputStaticProperty) {
+
+ }
+
+ @Override
+ public void visit(CollectionStaticProperty collectionStaticProperty) {
+ collectionStaticProperty.getMembers().forEach(sp -> sp.accept(this));
+ }
+
+ @Override
+ public void visit(ColorPickerStaticProperty colorPickerStaticProperty) {
+
+ }
+
+ @Override
+ public void visit(DomainStaticProperty domainStaticProperty) {
+
+ }
+
+ @Override
+ public void visit(FileStaticProperty fileStaticProperty) {
+
+ }
+
+ @Override
+ public void visit(FreeTextStaticProperty freeTextStaticProperty) {
+
+ }
+
+ @Override
+ public void visit(MappingPropertyNary mappingPropertyNary) {
+
+ }
+
+ @Override
+ public void visit(MappingPropertyUnary mappingPropertyUnary) {
+
+ }
+
+ @Override
+ public void visit(MatchingStaticProperty matchingStaticProperty) {
+
+ }
+
+ @Override
+ public void visit(OneOfStaticProperty oneOfStaticProperty) {
+
+ }
+
+ @Override
+ public void visit(SecretStaticProperty secretStaticProperty) {
+ if (secretHandler.shouldApply(secretStaticProperty.getEncrypted())) {
+ String newValue = secretHandler.apply(username, secretStaticProperty.getValue());
+ secretStaticProperty.setValue(newValue);
+ secretStaticProperty.setEncrypted(!secretStaticProperty.getEncrypted());
+ }
+ }
+
+ @Override
+ public void visit(StaticPropertyAlternative staticPropertyAlternative) {
+ staticPropertyAlternative.getStaticProperty().accept(this);
+ }
+
+ @Override
+ public void visit(StaticPropertyAlternatives staticPropertyAlternatives) {
+ staticPropertyAlternatives.getAlternatives().forEach(sp -> sp.accept(this));
+ }
+
+ @Override
+ public void visit(StaticPropertyGroup staticPropertyGroup) {
+ staticPropertyGroup.getStaticProperties().forEach(sp -> sp.accept(this));
+ }
+
+ @Override
+ public void visit(RemoteOneOfStaticProperty remoteOneOfStaticProperty) {
+
+ }
+}