diff --git a/README.md b/README.md index f89d0c17..2b28fc80 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ This is the CoreConfig that takserver war will look for when running from the ta See appendix B in src/docs/TAK_Server_Configuration_Guide.pdf for cert generation instructions. -### Build and run TAK server locally for development +### Build TAK server to run locally for development Note that due to Java 17, there are a lot of '--add-opens' arguments in the JDK_JAVA_OPTIONS ``` @@ -67,20 +67,30 @@ export IGNITE_HOME="$PWD/ignite" export JDK_JAVA_OPTIONS="-Dloader.path=WEB-INF/lib-provided,WEB-INF/lib,WEB-INF/classes,file:lib/ -Djava.net.preferIPv4Stack=true -Djava.security.egd=file:/dev/./urandom -DIGNITE_UPDATE_NOTIFIER=false -DIGNITE_QUIET=true -Dio.netty.tmpdir=$PWD -Djava.io.tmpdir=$PWD -Dio.netty.native.workdir=$PWD -Djdk.tls.client.protocols=TLSv1.2 --add-opens=java.base/sun.security.pkcs=ALL-UNNAMED --add-opens=java.base/sun.security.pkcs10=ALL-UNNAMED --add-opens=java.base/sun.security.util=ALL-UNNAMED --add-opens=java.base/sun.security.x509=ALL-UNNAMED --add-opens=java.base/sun.security.tools.keytool=ALL-UNNAMED --add-opens=java.base/jdk.internal.misc=ALL-UNNAMED --add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.management/com.sun.jmx.mbeanserver=ALL-UNNAMED --add-opens=jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED --add-opens=java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED --add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.locks=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.math=ALL-UNNAMED --add-opens=java.sql/java.sql=ALL-UNNAMED --add-opens=java.base/javax.net.ssl=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=jdk.unsupported/sun.misc=ALL-UNNAMED --add-opens=java.base/java.lang.ref=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.security.ssl=ALL-UNNAMED --add-opens=java.base/java.security.cert=ALL-UNNAMED --add-opens=java.base/sun.security.rsa=ALL-UNNAMED --add-opens=java.base/sun.security.ssl=ALL-UNNAMED --add-opens=java.base/sun.security.x500=ALL-UNNAMED --add-opens=java.base/sun.security.pkcs12=ALL-UNNAMED --add-opens=java.base/sun.security.provider=ALL-UNNAMED --add-opens=java.base/javax.security.auth.x500=ALL-UNNAMED" ``` +### Running TAK server locally for development -TAK server consists of two processes: Messaging and API. The messaging process can run independently, but the API process needs to connect to the ignite server that runs as a part of the messaging process. For both processes, -Xmx should always be specified. +TAK server consists of three processes: Configuration, Messaging and API. -Run Messaging (note - this command and the following one to run api include the **duplicatelogs** profile. This turns off the filter that blocks duplicated log messages that cause log spam in operational deployments of TAK Server. +The configuration process needs to be running first in order for the Messaging, API or any other services to retrieve the centralized configuration. This is separate from the TAKIgniteConfiguration that is loaded **per service** using defaults or the overridden values in TAKIgniteConfig.xml. + +The messaging process can run independently, but the API process may need to connect to the ignite server that runs as a part of the messaging process if it is not configured to run its own Ignite server. For both processes, -Xmx should always be specified. + +Note - These commands include the **duplicatelogs** profile. This turns off the filter that blocks duplicated log messages that cause log spam in operational deployments of TAK Server. + +#### Run Configuration Microservice +``` +java -server -XX:+AlwaysPreTouch -XX:+UseG1GC -XX:+ScavengeBeforeFullGC -XX:+DisableExplicitGC -Xmx -Dspring.profiles.active=config,duplicatelogs -jar ../build/libs/takserver-core-xyz.war +``` +#### Run Messaging Microservice ``` java -server -XX:+AlwaysPreTouch -XX:+UseG1GC -XX:+ScavengeBeforeFullGC -XX:+DisableExplicitGC -Xmx -Dspring.profiles.active=messaging,duplicatelogs -jar ../build/libs/takserver-core-xyz.war ``` - -Run API +#### Run API Microservice ``` java -server -XX:+AlwaysPreTouch -XX:+UseG1GC -XX:+ScavengeBeforeFullGC -XX:+DisableExplicitGC -Xmx -Dspring.profiles.active=api,duplicatelogs -Dkeystore.pkcs12.legacy -jar ../build/libs/takserver-core-xyz.war ``` -Run Plugin Manager (useful when working on plugin capability) +#### Run Plugin Manager Microservice (optional - useful when working on plugin capability) ``` java -server -XX:+AlwaysPreTouch -XX:+UseG1GC -XX:+ScavengeBeforeFullGC -XX:+DisableExplicitGC -Xmx -jar ../../takserver-plugin-manager/build/libs/takserver-plugin-manager-xyz.jar ``` @@ -150,10 +160,12 @@ i.e. The TAK Server log files can be found in the _logs_ subdirectory: -1. _takserver-messaging.log_ - Execution-level information about the messaging process, including client connection events, error messages and warnings. -2. _takserver-api.log_ - Execution-level information about the API process, including error messages and warnings. -3. _takserver-messaging-console.log_ - Java Virtual Machine (JVM) informational messages and errors, for the messaging process. -4. _takserver-api-console.log_ - Java Virtual Machine (JVM) informational messages and errors, for the API process. +1. _takserver-config.log_ - Execution-level information about the configuration process including setup, error messages and warnings. +2. _takserver-messaging.log_ - Execution-level information about the messaging process, including client connection events, error messages and warnings. +3. _takserver-api.log_ - Execution-level information about the API process, including error messages and warnings. +4. _takserver-config-console.log_ - Java Virtual Machine (JVM) informational messages and errors, for the config process. +5. _takserver-messaging-console.log_ - Java Virtual Machine (JVM) informational messages and errors, for the messaging process. +6. _takserver-api-console.log_ - Java Virtual Machine (JVM) informational messages and errors, for the API process. ## Swagger https://localhost:8443/swagger-ui.html diff --git a/src/docs/README_fedhub.md b/src/docs/README_fedhub.md index c831496a..3002eaa7 100644 --- a/src/docs/README_fedhub.md +++ b/src/docs/README_fedhub.md @@ -1,6 +1,7 @@ + # TAK Server Federation Hub -*Requires Java 11.* +*Requires Java 17.* ## Description @@ -37,18 +38,97 @@ To build the .rpm for the Federation Hub, run: 2. broker 3. UI (optional) -## Install and Run +## Install and Run RHEL7 +Update yum + +``` +sudo yum update -y +``` + +Install Java 17 +``` +sudo yum install wget -y +sudo wget https://download.oracle.com/java/17/latest/jdk-17_linux-x64_bin.rpm +sudo yum install -y ./jdk-17_linux-x64_bin.rpm +``` + +To install from the .rpm, run: + +``` +sudo rpm -ivh takserver-fed-hub-*.noarch.rpm --nodeps +``` + +## Install and Run RHEL8 +Update yum + +``` +sudo dnf update -y +``` + +Install Java 17 +``` +sudo dnf install java-17-openjdk-devel -y +``` To install from the .rpm, run: ``` -sudo yum install federation-hub-*.noarch.rpm +sudo yum install takserver-fed-hub-*.noarch.rpm -y +``` + +Add and Apply SELinux +``` +sudo dnf install checkpolicy +cd /opt/tak/federation-hub && sudo ./apply-selinux.sh && sudo semodule -l | grep takserver ``` +## Install Mongo +Make sure /opt/tak/federation-hub/configs/federation-hub-broker.yml has your database credentials defined. Defaults will be generated otherwise +``` +dbUsername: martiuser +dbPassword: pass4marti +``` + +Mongo Setup +``` +sudo yum install -y mongodb-org +sudo systemctl daemon-reload +sudo systemctl enable mongod +sudo systemctl restart mongod +sudo /opt/tak/federation-hub/scripts/db/configure.sh +``` + +## Update from RPM +Before updating the Federation Hub, you should back up the policy file and list of authorized users: + +``` +mv /opt/tak/federation-hub/ui_generated_policy.json /tmp +mv /opt/tak/federation-hub/authorized_users.yml /tmp +``` + +RHEL7 +``` +sudo rpm -Uvh takserver-fed-hub-*.noarch.rpm --nodeps +``` + +RHEL8 +``` +sudo yum upgrade takserver-fed-hub-*.noarch.rpm +``` + +The policy and authorized can then be replaced: +``` +mv /tmp/ui_generated_policy.json /opt/tak/federation-hub/ +mv /tmp/authorized_users.yml /opt/tak/federation-hub/ +``` + +## Configuration +**The Federation Hub authenticates clients using TLS with X.509 client certificates. Scripts for generating a private security enclave, including a Certificate Authority (CA), and certs for use by the Federation Hub are in the TAK server documentation. See the TAK server configuration guide (docs/TAK_Server_Configuration_Guide.pdf) for additional information.** + The Federation Hub can then be started as a system service (and enabled to run on boot): ``` -sudo systemctl start federation-hub +sudo systemctl restart federation-hub sudo systemctl enable federation-hub ``` @@ -64,8 +144,6 @@ The Federation Hub consists of three processes: a policy manager, an administrat ## Client Authentication and Authorization -The Federation Hub authenticates clients using TLS with X.509 client certificates. Scripts for generating a private security enclave, including a Certificate Authority (CA), and certs for use by the Federation Hub are in the TAK server documentation. See the TAK server configuration guide (docs/TAK_Server_Configuration_Guide.pdf) for additional information. - To authorize clients to act as administrators and enable access to the admin UI, use `federation-hub-manager.jar`: ``` diff --git a/src/docs/TAK_Server_Configuration_Guide.odt b/src/docs/TAK_Server_Configuration_Guide.odt index 920e43e4..39e1c078 100644 Binary files a/src/docs/TAK_Server_Configuration_Guide.odt and b/src/docs/TAK_Server_Configuration_Guide.odt differ diff --git a/src/docs/TAK_Server_Configuration_Guide.pdf b/src/docs/TAK_Server_Configuration_Guide.pdf index e0ab34ec..e2282995 100644 Binary files a/src/docs/TAK_Server_Configuration_Guide.pdf and b/src/docs/TAK_Server_Configuration_Guide.pdf differ diff --git a/src/federation-common/build.gradle b/src/federation-common/build.gradle index 5dc0bea6..87e29398 100644 --- a/src/federation-common/build.gradle +++ b/src/federation-common/build.gradle @@ -8,6 +8,7 @@ dependencies { implementation group: 'org.slf4j', name: 'slf4j-api', version: slf4j_version implementation group: 'org.slf4j', name: 'log4j-over-slf4j', version: slf4j_version + implementation(project(':takserver-common')) api project(':takserver-fig-core') // Apache Ignite (cache and distributed service grid). @@ -15,7 +16,7 @@ dependencies { // implementation group: 'org.apache.ignite', name: 'ignite-spring-cache-ext', version: ignite_spring_cache_version implementation group: 'org.springframework', name: 'spring-beans', version: spring_version implementation group: 'org.springframework', name: 'spring-context', version: spring_version - + implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-mongodb', version: spring_boot_version implementation group: 'org.apache.ignite', name: 'ignite-kubernetes', version: ignite_version implementation group: 'org.apache.ignite', name: 'ignite-slf4j', version: ignite_version diff --git a/src/federation-common/docker/Dockerfile.fedhub-db b/src/federation-common/docker/Dockerfile.fedhub-db new file mode 100644 index 00000000..15d3d4b3 --- /dev/null +++ b/src/federation-common/docker/Dockerfile.fedhub-db @@ -0,0 +1,7 @@ +FROM mongo:6.0 + +COPY tak/federation-hub/scripts/db /opt/tak/federation-hub/scripts/db + +RUN mkdir -p /var/lib/mongodb + +ENTRYPOINT ["/bin/sh","-c","/opt/tak/federation-hub/scripts/db/configureInDocker.sh && tail -f /dev/null"] diff --git a/src/federation-common/src/main/java/tak/server/federation/FederateGroup.java b/src/federation-common/src/main/java/tak/server/federation/FederateGroup.java index 8968c78b..d7a8d051 100644 --- a/src/federation-common/src/main/java/tak/server/federation/FederateGroup.java +++ b/src/federation-common/src/main/java/tak/server/federation/FederateGroup.java @@ -23,42 +23,6 @@ public FederateGroup(FederateIdentity federateIdentity) { this.filterExpression = ""; } - public FederateGroup(FederateIdentity federateIdentity, boolean interconnected) { - super(federateIdentity); - this.interconnected = interconnected; - this.federatesInGroup = new HashSet<>(); - this.filterExpression = ""; - } - - /* If there is a group filter expression, the group is interconnected. */ - public FederateGroup(FederateIdentity federateIdentity, String filterExpression) { - super(federateIdentity); - this.interconnected = true; - this.filterExpression = filterExpression; - this.federatesInGroup = new HashSet<>(); - } - - public FederateGroup(String name, FederateIdentity federateIdentity) { - super(name, federateIdentity); - this.interconnected = true; - this.federatesInGroup = new HashSet<>(); - this.filterExpression = ""; - } - - public FederateGroup(String name, FederateIdentity federateIdentity, boolean interconnected) { - super(name, federateIdentity); - this.interconnected = interconnected; - this.federatesInGroup = new HashSet<>(); - this.filterExpression = ""; - } - - /* If there is a group filter expression, the group is interconnected. */ - public FederateGroup(String name, FederateIdentity federateIdentity, String filterExpression) { - super(name, federateIdentity); - this.interconnected = true; - this.filterExpression = filterExpression; - this.federatesInGroup = new HashSet<>(); - } public boolean isInterconnected() { return interconnected; diff --git a/src/federation-common/src/main/java/tak/server/federation/FederationException.java b/src/federation-common/src/main/java/tak/server/federation/FederationException.java index 7637ba89..67f8c9bd 100644 --- a/src/federation-common/src/main/java/tak/server/federation/FederationException.java +++ b/src/federation-common/src/main/java/tak/server/federation/FederationException.java @@ -24,7 +24,7 @@ public FederationException(String additionalInformation, Throwable failureCausin @Override public String toString() { Throwable cause = this.getCause(); - if(cause == null) { + if (cause == null) { return this.getMessage(); } else if (this.additionalInformation == null) { return cause.toString(); diff --git a/src/federation-common/src/main/java/tak/server/federation/FederationFilter.java b/src/federation-common/src/main/java/tak/server/federation/FederationFilter.java index 5a4f6845..05ee05ea 100644 --- a/src/federation-common/src/main/java/tak/server/federation/FederationFilter.java +++ b/src/federation-common/src/main/java/tak/server/federation/FederationFilter.java @@ -31,7 +31,7 @@ public String getMethodName() { } public void addMessageAttribute(String key, Object value) { - if(isValueValidType(value)) { + if (isValueValidType(value)) { messageAttributes.put(key, value); } } @@ -41,7 +41,7 @@ public Map getMessageAttributes() { } public void addSourceAttribute(String key, Object value) { - if(isValueValidType(value)) { + if (isValueValidType(value)) { sourceAttributes.put(key, value); } } @@ -51,7 +51,7 @@ public Map getSourceAttributes() { } public void addDestinationAttribute(String key, Object value) { - if(isValueValidType(value)) { + if (isValueValidType(value)) { destinationAttributes.put(key, value); } diff --git a/src/federation-common/src/main/java/tak/server/federation/GuardedStreamHolder.java b/src/federation-common/src/main/java/tak/server/federation/GuardedStreamHolder.java index 33382fcf..5d4dc39c 100644 --- a/src/federation-common/src/main/java/tak/server/federation/GuardedStreamHolder.java +++ b/src/federation-common/src/main/java/tak/server/federation/GuardedStreamHolder.java @@ -21,6 +21,7 @@ import com.atakmap.Tak.FederatedEvent; import com.atakmap.Tak.ROL; import com.atakmap.Tak.Subscription; +import com.atakmap.Tak.Identity.ConnectionType; import com.google.common.base.Strings; import io.grpc.ClientCall; @@ -46,7 +47,7 @@ public class GuardedStreamHolder { private Subscription subscription; private int maxFederateHops = -1; - private boolean isHub = false; + private boolean isRunningInHub = false; private final Set cache; @@ -55,13 +56,13 @@ public Set getCache() { } // for outgoing connections - public GuardedStreamHolder(ClientCall clientCall, String fedId, Comparator comp, boolean isHub) { + public GuardedStreamHolder(ClientCall clientCall, String fedId, Comparator comp, boolean isRunningInHub) { requireNonNull(clientCall, "FederatedEvent groupCall"); requireNonNull(comp, "comparator"); - this.isHub = isHub; + this.isRunningInHub = isRunningInHub; this.cache = new ConcurrentSkipListSet(comp); @@ -74,14 +75,14 @@ public GuardedStreamHolder(ClientCall clientCall, String fedId, } // for incoming connections - public GuardedStreamHolder(StreamObserver clientStream, String clientName, String certHash, SSLSession session, Subscription subscription, Comparator comp, boolean isHub) { + public GuardedStreamHolder(StreamObserver clientStream, String clientName, String certHash, SSLSession session, Subscription subscription, Comparator comp, boolean isRunningInHub) { requireNonNull(clientStream, "FederatedEvent client stream"); requireNonNull(subscription, "client subscription"); requireNonNull(comp, "comparator"); - this.isHub = isHub; + this.isRunningInHub = isRunningInHub; this.cache = new ConcurrentSkipListSet(comp); @@ -93,8 +94,12 @@ public GuardedStreamHolder(StreamObserver clientStream, String clientName, St throw new IllegalArgumentException("empty cert hash - invalid stream"); } - // append a random id to the end, to prevent collisions. this is done for outgoing connections as well in the javascript code - String fedId = clientName + "-" + certHash + "-" + new BigInteger(session.getId()); + // new takservers will send their CoreConfig serverId. if present, use it, otherwise generate a random unique identifier + String serverId = subscription.getIdentity().getServerId(); + if (Strings.isNullOrEmpty(serverId)) { + serverId = new BigInteger(session.getId()).toString(); + } + String fedId = clientName + "-" + certHash + "-" + serverId; this.subscription = subscription; @@ -135,7 +140,7 @@ public synchronized void send(T event) { } T modifiedEvent = null; - if (isHub) { + if (isRunningInHub) { // since hub outgoing connections can forward traffic to other hubs, we need to keep a list of visited nodes // so that we can stop cycles FederateProvenance prov = FederateProvenance.newBuilder() @@ -155,7 +160,7 @@ public synchronized void send(T event) { if (modifiedEvent == null) { return; } - + // clientStream = stream of messages going from server to a connected outgoing client if (clientStream != null) clientStream.onNext(modifiedEvent); @@ -173,7 +178,7 @@ public T addPropertiesToEvent(T event, Set federateProvenanc // if hops exist, check/increment them, if no hops exist, we need to add them FederateHops federateHops = fedEvent.hasFederateHops() ? checkHops(event, fedEvent.getFederateHops()) : - FederateHops.newBuilder().setCurrentHops(0).setMaxHops(maxFederateHops).build(); + FederateHops.newBuilder().setCurrentHops(1).setMaxHops(maxFederateHops).build(); if (federateHops == null) { return null; @@ -196,7 +201,7 @@ public T addPropertiesToEvent(T event, Set federateProvenanc // if hops exist, check/increment them, if no hops exist, we need to add them FederateHops federateHops = fedGroup.hasFederateHops() ? checkHops(event, fedGroup.getFederateHops()) : - FederateHops.newBuilder().setCurrentHops(0).setMaxHops(maxFederateHops).build(); + FederateHops.newBuilder().setCurrentHops(1).setMaxHops(maxFederateHops).build(); if (federateHops == null) { return null; @@ -219,7 +224,7 @@ public T addPropertiesToEvent(T event, Set federateProvenanc // if hops exist, check/increment them, if no hops exist, we need to add them FederateHops federateHops = rol.hasFederateHops() ? checkHops(event, rol.getFederateHops()) : - FederateHops.newBuilder().setCurrentHops(0).setMaxHops(maxFederateHops).build(); + FederateHops.newBuilder().setCurrentHops(1).setMaxHops(maxFederateHops).build(); if (federateHops == null) { return null; @@ -242,7 +247,7 @@ public T addPropertiesToEvent(T event, Set federateProvenanc // if hops exist, check/increment them, if no hops exist, we need to add them FederateHops federateHops = blob.hasFederateHops() ? checkHops(event, blob.getFederateHops()) : - FederateHops.newBuilder().setCurrentHops(0).setMaxHops(maxFederateHops).build(); + FederateHops.newBuilder().setCurrentHops(1).setMaxHops(maxFederateHops).build(); if (federateHops == null) { return null; @@ -265,18 +270,33 @@ public T addPropertiesToEvent(T event, Set federateProvenanc private FederateHops checkHops(T event, FederateHops federateHops) { long maxHops = federateHops.getMaxHops(); long currentHops = federateHops.getCurrentHops() + 1; - - if (currentHops >= maxHops && maxHops != -1) { + + if (currentHops > maxHops && maxHops != -1) { if (logger.isDebugEnabled()) { logger.debug("dropping message because of hop limit " + event); } return null; - } - else { + } + // if there is only 1 hop left and the next hop is to a federation hub, + // just drop the message now because the next hub will not be allowed to send it + // further anyways + else if (isHubSubscription() && currentHops == maxHops) { + if (logger.isDebugEnabled()) { + logger.debug("dropping message because of hop limit (1 hop left but next destination is a hub) " + event); + } + return null; + } else { return FederateHops.newBuilder().setCurrentHops(currentHops).setMaxHops(maxHops).build(); } } + // if the subscription associated with this connection is a federation hub + private boolean isHubSubscription() { + return subscription != null && + (subscription.getIdentity().getType() == ConnectionType.FEDERATION_HUB_CLIENT + || subscription.getIdentity().getType() == ConnectionType.FEDERATION_HUB_SERVER); + } + public void throwDeadlineExceptionToClient() { try { if (clientStream != null) diff --git a/src/federation-common/src/main/java/tak/server/federation/hub/FederationHubDependencyInjectionProxy.java b/src/federation-common/src/main/java/tak/server/federation/hub/FederationHubDependencyInjectionProxy.java index 6fd4c715..8b3f3f5f 100644 --- a/src/federation-common/src/main/java/tak/server/federation/hub/FederationHubDependencyInjectionProxy.java +++ b/src/federation-common/src/main/java/tak/server/federation/hub/FederationHubDependencyInjectionProxy.java @@ -1,11 +1,8 @@ package tak.server.federation.hub; +import tak.server.federation.hub.broker.*; import tak.server.federation.hub.policy.FederationHubPolicyManager; -import tak.server.federation.hub.broker.SSLConfig; import tak.server.federation.hub.broker.events.RestartServerEvent; -import tak.server.federation.hub.broker.FederationHubServerConfig; -import tak.server.federation.hub.broker.HubConnectionStore; -import tak.server.federation.hub.broker.FederationHubBroker; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; @@ -79,7 +76,7 @@ public FederationHubServerConfig fedHubServerConfig() { return fedHubServerConfig; } - + private FederationHubBroker federationHubBroker = null; public FederationHubBroker federationHubBroker() { @@ -93,7 +90,20 @@ public FederationHubBroker federationHubBroker() { return federationHubBroker; } - + + private FederationHubBrokerMetrics federationHubBrokerMetrics = null; + + public FederationHubBrokerMetrics federationHubBrokerMetrics() { + if (federationHubBrokerMetrics == null) { + synchronized (this) { + if (federationHubBrokerMetrics == null) { + federationHubBrokerMetrics = springContext.getBean(FederationHubBrokerMetrics.class); + } + } + } + return federationHubBrokerMetrics; + } + private HubConnectionStore hubConnectionStore = null; public HubConnectionStore hubConnectionStore() { @@ -107,8 +117,8 @@ public HubConnectionStore hubConnectionStore() { return hubConnectionStore; } - + public void restartV2Server() { springContext.publishEvent(new RestartServerEvent(this)); } -} +} \ No newline at end of file diff --git a/src/federation-common/src/main/java/tak/server/federation/hub/broker/FederationHubBroker.java b/src/federation-common/src/main/java/tak/server/federation/hub/broker/FederationHubBroker.java index 6ff141a6..37deb03f 100644 --- a/src/federation-common/src/main/java/tak/server/federation/hub/broker/FederationHubBroker.java +++ b/src/federation-common/src/main/java/tak/server/federation/hub/broker/FederationHubBroker.java @@ -2,6 +2,7 @@ import java.security.cert.X509Certificate; import java.util.List; +import java.util.Map; import tak.server.federation.hub.ui.graph.FederationPolicyModel; @@ -9,6 +10,8 @@ public interface FederationHubBroker { void addGroupCa(X509Certificate ca); void updatePolicy(FederationPolicyModel federationPolicyModel); List getActiveConnections(); + FederationHubBrokerMetrics getFederationHubBrokerMetrics(); List getGroupsForNode(String federateId); void deleteGroupCa(String groupId); -} + Map getCAsFromFile(); +} \ No newline at end of file diff --git a/src/federation-common/src/main/java/tak/server/federation/hub/broker/FederationHubBrokerImpl.java b/src/federation-common/src/main/java/tak/server/federation/hub/broker/FederationHubBrokerImpl.java index 0140d6e4..34916869 100644 --- a/src/federation-common/src/main/java/tak/server/federation/hub/broker/FederationHubBrokerImpl.java +++ b/src/federation-common/src/main/java/tak/server/federation/hub/broker/FederationHubBrokerImpl.java @@ -11,7 +11,9 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -97,7 +99,7 @@ public void addGroupCa(X509Certificate ca) { try { String dn = ca.getSubjectX500Principal().getName(); String alias = FederationUtils.getBytesSHA256(ca.getEncoded()); - + sslConfig.getTrust().setEntry(alias, new KeyStore.TrustedCertificateEntry(ca), null); saveTruststoreFile(sslConfig, fedHubConfig); sslConfig.refresh(); @@ -108,7 +110,30 @@ public void addGroupCa(X509Certificate ca) { throw new RuntimeException(e); } } - + + @Override + public Map getCAsFromFile() { + Map cas = new HashMap<>(); + + FederationHubDependencyInjectionProxy depProxy = FederationHubDependencyInjectionProxy.getInstance(); + SSLConfig sslConfig = depProxy.sslConfig(); + + try { + for (Enumeration e = sslConfig.getTrust().aliases(); e.hasMoreElements();) { + String alias = e.nextElement(); + X509Certificate cert = (X509Certificate) sslConfig.getTrust().getCertificate(alias); + String issuerName = cert.getIssuerX500Principal().getName(); + String groupName = issuerName + "-" + FederationUtils.getBytesSHA256(cert.getEncoded()); + + cas.put(groupName, cert); + } + } catch (Exception e) { + logger.error("Exception deleteing CA", e); + } + + return cas; + } + @Override public void deleteGroupCa(String groupId) { FederationHubDependencyInjectionProxy depProxy = FederationHubDependencyInjectionProxy.getInstance(); @@ -126,11 +151,11 @@ public void deleteGroupCa(String groupId) { if (groupName.equals(groupId)) { FederateGroup group = new FederateGroup(new FederateIdentity(groupId)); fedHubPolicyManager.removeCaGroup(group); - + sslConfig.getTrust().deleteEntry(alias); saveTruststoreFile(sslConfig, fedHubConfig); sslConfig.refresh(); - + depProxy.restartV2Server(); } } @@ -163,13 +188,13 @@ public void execute() throws Exception { @Override public void updatePolicy(FederationPolicyModel federationPolicyModel) { Collection cells = federationPolicyModel.getCells(); - + if (cells != null) { List outgoings = new ArrayList<>(); cells.stream().filter(cell -> cell instanceof FederationOutgoingCell).forEach( cell -> { outgoings.add((FederationOutgoingCell) cell); }); - + FederationHubDependencyInjectionProxy.getSpringContext().publishEvent(new UpdatePolicy(this, outgoings)); } } @@ -184,12 +209,19 @@ public List getActiveConnections() { .collect(Collectors.toList()); } + @Override + public FederationHubBrokerMetrics getFederationHubBrokerMetrics() { + + return FederationHubDependencyInjectionProxy.getInstance() + .federationHubBrokerMetrics(); + } + @Override public List getGroupsForNode(String federateId) { FederationPolicyGraph policyGraph = FederationHubDependencyInjectionProxy.getInstance() .fedHubPolicyManager() .getPolicyGraph(); - + FederationNode sourceNode = policyGraph.getNode(federateId); if (sourceNode != null) { @@ -206,18 +238,18 @@ public List getGroupsForNode(String federateId) { .flatMap(g -> g.getFederateGroupsList().stream()) .distinct() .collect(Collectors.toList()); - + return groups; } - + if (sourceNode instanceof FederateGroup) { FederateGroup sourceFederateGroup = (FederateGroup) policyGraph.getNode(federateId); - + Set federateIdentitiesInGroup = sourceFederateGroup.getFederatesInGroup() .stream() .map(f -> f.getFederateIdentity().getFedId()) .collect(Collectors.toSet()); - + List groups = FederationHubDependencyInjectionProxy.getInstance() .hubConnectionStore() .getClientGroupStreamMap() @@ -230,11 +262,11 @@ public List getGroupsForNode(String federateId) { .flatMap(g -> g.getFederateGroupsList().stream()) .distinct() .collect(Collectors.toList()); - + return groups; } } - + return new ArrayList(); } -} +} \ No newline at end of file diff --git a/src/federation-common/src/main/java/tak/server/federation/hub/broker/FederationHubBrokerMetrics.java b/src/federation-common/src/main/java/tak/server/federation/hub/broker/FederationHubBrokerMetrics.java new file mode 100644 index 00000000..219909d5 --- /dev/null +++ b/src/federation-common/src/main/java/tak/server/federation/hub/broker/FederationHubBrokerMetrics.java @@ -0,0 +1,182 @@ +package tak.server.federation.hub.broker; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +public class FederationHubBrokerMetrics { + + @JsonIgnore + private final ConcurrentHashMap> channelInfosInternal = new ConcurrentHashMap<>(); + + + private final List channelInfos = new ArrayList<>(); + + public ConcurrentHashMap> getChannelInfosInternal() { + return new ConcurrentHashMap<>(channelInfosInternal); + } + + public List getChannelInfos() { + return new ArrayList(channelInfos); + } + + public void incrementChannelWrite(String sourceId, + String sourceCert, + String targetId, + String targetCert, + long messageLength) { + if (!channelInfosInternal.containsKey(sourceCert)) { + channelInfosInternal.put(sourceCert, new ConcurrentHashMap<>()); + } + ConcurrentHashMap channelInfo = channelInfosInternal.get(sourceCert); + if (!channelInfo.containsKey(targetCert)) { + ChannelInfo newChannelInfo = new ChannelInfo(sourceId, sourceCert, targetId, targetCert); + channelInfo.put(targetCert, newChannelInfo); + channelInfos.add(newChannelInfo); + } + + // update the id's to the latest + channelInfo.get(targetCert).setTargetId(targetId); + channelInfo.get(targetCert).setSourceId(sourceId); + + channelInfo.get(targetCert).messagesWritten += 1; + channelInfo.get(targetCert).bytesWritten += messageLength; + channelInfo.get(targetCert).totalMessages += 1; + } + + public void incrementChannelRead(String targetId, + String targetCert, + String sourceId, + String sourceCert, + long messageLength) { + if (!channelInfosInternal.containsKey(sourceCert)) { + channelInfosInternal.put(sourceCert, new ConcurrentHashMap<>()); + } + ConcurrentHashMap channelInfo = channelInfosInternal.get(sourceCert); + if (!channelInfo.containsKey(targetCert)) { + ChannelInfo newChannelInfo = new ChannelInfo(sourceId, sourceCert, targetId, targetCert); + channelInfo.put(targetCert, newChannelInfo); + channelInfos.add(newChannelInfo); + } + + // update the id's to the latest + channelInfo.get(targetCert).setTargetId(targetId); + channelInfo.get(targetCert).setSourceId(sourceId); + + channelInfo.get(targetCert).messagesRead += 1; + channelInfo.get(targetCert).bytesRead += messageLength; + channelInfo.get(targetCert).totalMessages += 1; + } + + public static class ChannelInfo { + + public ChannelInfo(String sourceId, + String sourceCert, + String targetId, + String targetCert) { + this.sourceId = sourceId; + this.targetId = targetId; + this.sourceCert = sourceCert; + this.targetCert = targetCert; + } + + public long getMessagesRead() { + return messagesRead; + } + + public void setMessagesRead(long messagesRead) { + this.messagesRead = messagesRead; + } + + public long getTotalMessages() { + return totalMessages; + } + + public void setTotalMessages(long totalMessages) { + this.totalMessages = totalMessages; + } + + public long getBytesRead() { + return bytesRead; + } + + public void setBytesRead(long bytesRead) { + this.bytesRead = bytesRead; + } + + public String getSourceId() { + return sourceId; + } + + public void setSourceId(String sourceId) { + this.sourceId = sourceId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getSourceCert() { + return sourceCert; + } + + public void setSourceCert(String sourceCert) { + this.sourceCert = sourceCert; + } + + public String getTarget() { + return targetCert; + } + + public void setTarget(String target) { + this.targetCert = target; + } + + public long getMessagesWritten() { + return messagesWritten; + } + + public void setMessagesWritten(long messagesWritten) { + this.messagesWritten = messagesWritten; + } + + public String sourceId; + public String targetId; + public String sourceCert; + public String targetCert; + public long messagesWritten; + public long messagesRead; + public long totalMessages; + public long bytesWritten; + public long bytesRead; + + @Override + public String toString() { + return "ChannelInfo{" + + "sourceId='" + sourceId + '\'' + + ", targetId='" + targetId + '\'' + + ", sourceCert='" + sourceCert + '\'' + + ", targetCert='" + targetCert + '\'' + + ", messagesWritten=" + messagesWritten + + ", messagesRead=" + messagesRead + + ", totalMessages=" + totalMessages + + ", bytesWritten=" + bytesWritten + + ", bytesRead=" + bytesRead + + '}'; + } + } + + @Override + public String toString() { + return "FederationHubBrokerMetrics{" + + "channelWriteInfosInternal=" + channelInfosInternal + + ", channelInfos=" + channelInfos + + '}'; + } +} \ No newline at end of file diff --git a/src/federation-common/src/main/java/tak/server/federation/hub/broker/FederationHubServerConfig.java b/src/federation-common/src/main/java/tak/server/federation/hub/broker/FederationHubServerConfig.java index 38c650b2..9f30fcf8 100644 --- a/src/federation-common/src/main/java/tak/server/federation/hub/broker/FederationHubServerConfig.java +++ b/src/federation-common/src/main/java/tak/server/federation/hub/broker/FederationHubServerConfig.java @@ -43,7 +43,7 @@ public FederationHubServerConfig() { /* For v2 federation only. */ private boolean v2Enabled; private Integer v2Port; - private int maxMessageSizeBytes = 67108864; + private int maxMessageSizeBytes = 268435456; private int metricsLogIntervalSeconds = 5; private int clientTimeoutTime = 15; private int clientRefreshTime = 5; @@ -60,6 +60,18 @@ public FederationHubServerConfig() { @JsonIgnore private String fullId; + private String dbUsername = "martiuser"; + private String dbPassword = ""; + private int dbPort = 27017; + private String dbHost = "localhost"; + private long dbConnectionTimeoutMS = 5000; + + private long missionFederationDBRetentionDays = 7; + + private long missionFederationRecencySeconds = 43200; + private long missionFederationDisruptionMaxFileSizeBytes = 268435456; + private boolean missionFederationDisruptionEnabled = false; + public int getOutgoingReconnectSeconds() { return outgoingReconnectSeconds; } @@ -267,19 +279,95 @@ public void setUseCaGroups(boolean useCaGroups) { this.useCaGroups = useCaGroups; } + public String getDbUsername() { + return dbUsername; + } + + public void setDbUsername(String dbUsername) { + this.dbUsername = dbUsername; + } + + public String getDbPassword() { + return dbPassword; + } + + public void setDbPassword(String dbPassword) { + this.dbPassword = dbPassword; + } + + public int getDbPort() { + return dbPort; + } + + public void setDbPort(int dbPort) { + this.dbPort = dbPort; + } + + public String getDbHost() { + return dbHost; + } + + public void setDbHost(String dbHost) { + this.dbHost = dbHost; + } + + public long getDbConnectionTimeoutMS() { + return dbConnectionTimeoutMS; + } + + public void setDbConnectionTimeoutMS(long dbConnectionTimeoutMS) { + this.dbConnectionTimeoutMS = dbConnectionTimeoutMS; + } + + public long getMissionFederationRecencySeconds() { + return missionFederationRecencySeconds; + } + + public void setMissionFederationRecencySeconds(long missionFederationRecencySeconds) { + this.missionFederationRecencySeconds = missionFederationRecencySeconds; + } + + public boolean isMissionFederationDisruptionEnabled() { + return missionFederationDisruptionEnabled; + } + + public void setMissionFederationDisruptionEnabled(boolean missionFederationDisruptionEnabled) { + this.missionFederationDisruptionEnabled = missionFederationDisruptionEnabled; + } + + public long getMissionFederationDBRetentionDays() { + return missionFederationDBRetentionDays; + } + + public void setMissionFederationDBRetentionDays(long missionFederationDBRetentionDays) { + this.missionFederationDBRetentionDays = missionFederationDBRetentionDays; + } + + public long getMissionFederationDisruptionMaxFileSizeBytes() { + return missionFederationDisruptionMaxFileSizeBytes; + } + + public void setMissionFederationDisruptionMaxFileSizeBytes(long missionFederationDisruptionMaxFileSizeBytes) { + this.missionFederationDisruptionMaxFileSizeBytes = missionFederationDisruptionMaxFileSizeBytes; + } + @Override public String toString() { return "FederationHubServerConfig [keystoreType=" + keystoreType + ", keystoreFile=" + keystoreFile - + ", keystorePassword=" + keystorePassword + ", truststoreType=" + truststoreType + ", truststoreFile=" - + truststoreFile + ", truststorePassword=" + truststorePassword + ", keyManagerType=" + keyManagerType - + ", v1Enabled=" + v1Enabled + ", v1Port=" + v1Port + ", context=" + context + ", useEpoll=" + useEpoll - + ", allow128cipher=" + allow128cipher + ", allowNonSuiteB=" + allowNonSuiteB + ", enableOCSP=" - + enableOCSP + ", tlsVersions=" + tlsVersions + ", v2Enabled=" + v2Enabled + ", v2Port=" + v2Port - + ", maxMessageSizeBytes=" + maxMessageSizeBytes + ", metricsLogIntervalSeconds=" + + ", truststoreType=" + truststoreType + ", truststoreFile=" + truststoreFile + ", keyManagerType=" + + keyManagerType + ", v1Enabled=" + v1Enabled + ", v1Port=" + v1Port + ", context=" + context + + ", useEpoll=" + useEpoll + ", allow128cipher=" + allow128cipher + ", allowNonSuiteB=" + allowNonSuiteB + + ", enableOCSP=" + enableOCSP + ", tlsVersions=" + tlsVersions + ", v2Enabled=" + v2Enabled + + ", v2Port=" + v2Port + ", maxMessageSizeBytes=" + maxMessageSizeBytes + ", metricsLogIntervalSeconds=" + metricsLogIntervalSeconds + ", clientTimeoutTime=" + clientTimeoutTime + ", clientRefreshTime=" + clientRefreshTime + ", maxConcurrentCallsPerConnection=" + maxConcurrentCallsPerConnection + ", enableHealthCheck=" + enableHealthCheck + ", useCaGroups=" + useCaGroups + ", serverName=" + serverName + ", outgoingReconnectSeconds=" + outgoingReconnectSeconds + ", id=" + id + ", nonce=" - + nonce + ", fullId=" + fullId + "]"; + + nonce + ", fullId=" + fullId + ", dbUsername=" + dbUsername + ", dbPort=" + dbPort + ", dbHost=" + + dbHost + ", dbConnectionTimeoutMS=" + dbConnectionTimeoutMS + ", missionFederationDBRetentionDays=" + + missionFederationDBRetentionDays + ", missionFederationRecencySeconds=" + + missionFederationRecencySeconds + ", missionFederationDisruptionMaxFileSizeBytes=" + + missionFederationDisruptionMaxFileSizeBytes + ", missionFederationDisruptionEnabled=" + + missionFederationDisruptionEnabled + "]"; } } diff --git a/src/federation-common/src/main/java/tak/server/federation/hub/broker/HubConnectionStore.java b/src/federation-common/src/main/java/tak/server/federation/hub/broker/HubConnectionStore.java index b873c1b1..e3811713 100644 --- a/src/federation-common/src/main/java/tak/server/federation/hub/broker/HubConnectionStore.java +++ b/src/federation-common/src/main/java/tak/server/federation/hub/broker/HubConnectionStore.java @@ -1,7 +1,9 @@ package tak.server.federation.hub.broker; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -15,6 +17,9 @@ import com.atakmap.Tak.ROL; import tak.server.federation.GuardedStreamHolder; +import tak.server.federation.hub.FederationHubDependencyInjectionProxy; +import tak.server.federation.hub.broker.events.StreamReadyEvent; +import tak.server.federation.hub.broker.events.UpdatePolicy; public class HubConnectionStore { private static final Logger logger = LoggerFactory.getLogger(HubConnectionStore.class); @@ -26,10 +31,18 @@ public class HubConnectionStore { private final Map sessionMap = new ConcurrentHashMap<>(); private final Map connectionInfos = new ConcurrentHashMap<>(); + private final Map> tempRolCache = new ConcurrentHashMap<>(); + + public void cacheRol( ROL rol, String id) { + if (!tempRolCache.containsKey(id)) + tempRolCache.put(id, new ArrayList<>()); + + tempRolCache.get(id).add(rol); + } + public Collection getConnectionInfos() { return connectionInfos.values(); } - public void addConnectionInfo(String id, HubConnectionInfo info) { connectionInfos.put(id, info); @@ -42,6 +55,7 @@ public void clearIdFromAllStores(String id) { this.clientToGroups.remove(id); this.sessionMap.remove(id); this.connectionInfos.remove(id); + this.tempRolCache.remove(id); } public void clearStreamMaps() { @@ -51,6 +65,7 @@ public void clearStreamMaps() { this.clientToGroups.clear(); this.sessionMap.clear(); this.connectionInfos.clear(); + this.tempRolCache.clear(); } public Map> getClientStreamMap() { @@ -67,6 +82,12 @@ public Map> getClientROLStreamMap() { public void addRolStream(String id, GuardedStreamHolder holder) { this.clientROLStreamMap.put(id, holder); + List cachedRol = tempRolCache.get(id); + if (cachedRol != null) { + FederationHubDependencyInjectionProxy.getSpringContext() + .publishEvent(new StreamReadyEvent(this, StreamReadyEvent.StreamReadyType.ROL, id, new ArrayList(cachedRol))); + } + tempRolCache.remove(id); } public void removeRolStream(String id) { diff --git a/src/federation-common/src/main/java/tak/server/federation/hub/broker/events/StreamReadyEvent.java b/src/federation-common/src/main/java/tak/server/federation/hub/broker/events/StreamReadyEvent.java new file mode 100644 index 00000000..6477f778 --- /dev/null +++ b/src/federation-common/src/main/java/tak/server/federation/hub/broker/events/StreamReadyEvent.java @@ -0,0 +1,34 @@ +package tak.server.federation.hub.broker.events; + +import java.util.List; + +public class StreamReadyEvent extends BrokerServerEvent { + private static final long serialVersionUID = 5023824657075431825L; + + public enum StreamReadyType { + EVENT, GROUPS, ROL + } + + private final StreamReadyType type; + private final String streamKey; + private final List events; + + public StreamReadyEvent(Object source, StreamReadyType type, String streamKey, List events) { + super(source); + this.type = type; + this.streamKey = streamKey; + this.events = events; + } + + public StreamReadyType getType() { + return type; + } + + public String getStreamKey() { + return streamKey; + } + + public List getEvents() { + return events; + } +} diff --git a/src/federation-common/src/main/java/tak/server/federation/hub/db/FederationHubDatabase.java b/src/federation-common/src/main/java/tak/server/federation/hub/db/FederationHubDatabase.java new file mode 100644 index 00000000..f61af742 --- /dev/null +++ b/src/federation-common/src/main/java/tak/server/federation/hub/db/FederationHubDatabase.java @@ -0,0 +1,62 @@ +package tak.server.federation.hub.db; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; + +import com.mongodb.BasicDBObject; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; +import com.mongodb.client.MongoDatabase; + +public class FederationHubDatabase { + private static final Logger logger = LoggerFactory.getLogger(FederationHubDatabase.class); + + private AbstractMongoClientConfiguration mongo; + private MongoClient mongoClient; + private MongoDatabase cotDatabase; + private boolean dbIsConnected = false; + + public FederationHubDatabase(String username, String password, String host, int port) { + try { + mongo = new AbstractMongoClientConfiguration() { + + @Override + protected String getDatabaseName() { + return "cot"; + } + + @Override + public MongoClient mongoClient() { + MongoClient client = MongoClients.create("mongodb://" + username + ":" + password + "@" + host + ":" + port + "/?serverSelectionTimeoutMS=5000&connectTimeoutMS=5000"); + + return client; + } + + }; + + mongoClient = mongo.mongoClient(); + mongoClient.getDatabase("cot").runCommand(new BasicDBObject("ping", "1")); + dbIsConnected = true; + } catch (Exception e) { + dbIsConnected = false; + logger.error("Could not initialize mongo connection", e); + } + } + + public boolean isDBConnected() { + return dbIsConnected; + } + + public MongoDatabase getDB() { + if (cotDatabase == null) { + cotDatabase = mongoClient.getDatabase("cot"); + } + + return cotDatabase; + } + + public MongoClient getClient() { + return mongoClient; + } +} diff --git a/src/federation-common/src/main/java/tak/server/federation/hub/policy/FederationPolicy.java b/src/federation-common/src/main/java/tak/server/federation/hub/policy/FederationPolicy.java index 5248dfbd..809253ec 100644 --- a/src/federation-common/src/main/java/tak/server/federation/hub/policy/FederationPolicy.java +++ b/src/federation-common/src/main/java/tak/server/federation/hub/policy/FederationPolicy.java @@ -5,7 +5,10 @@ import tak.server.federation.hub.ui.StringEdge; import tak.server.federation.hub.ui.UidHolder; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; @JsonInclude(JsonInclude.Include.NON_EMPTY) public class FederationPolicy { diff --git a/src/federation-common/src/main/java/tak/server/federation/hub/policy/FederationPolicyGraphImpl.java b/src/federation-common/src/main/java/tak/server/federation/hub/policy/FederationPolicyGraphImpl.java index 0637ba78..04ffb65f 100644 --- a/src/federation-common/src/main/java/tak/server/federation/hub/policy/FederationPolicyGraphImpl.java +++ b/src/federation-common/src/main/java/tak/server/federation/hub/policy/FederationPolicyGraphImpl.java @@ -1,7 +1,14 @@ package tak.server.federation.hub.policy; import java.io.Serializable; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.slf4j.Logger; diff --git a/src/federation-common/src/main/java/tak/server/federation/hub/ui/graph/FilterUtils.java b/src/federation-common/src/main/java/tak/server/federation/hub/ui/graph/FilterUtils.java index 4a5bc80b..cfc05bd3 100644 --- a/src/federation-common/src/main/java/tak/server/federation/hub/ui/graph/FilterUtils.java +++ b/src/federation-common/src/main/java/tak/server/federation/hub/ui/graph/FilterUtils.java @@ -1,6 +1,10 @@ package tak.server.federation.hub.ui.graph; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -18,9 +22,9 @@ public class FilterUtils { public static String filterNodeToString(FilterNode filterNode) { if (filterNode.getType().equals(FILTER_TYPE)) { return filterToString(filterNode.getFilter()); - } else if(filterNode.getType().equals(AND_TYPE)) { + } else if (filterNode.getType().equals(AND_TYPE)) { return andedFiltersToString(filterNode.getNodes()); - } else if(filterNode.getType().equals(OR_TYPE)) { + } else if (filterNode.getType().equals(OR_TYPE)) { return oredFiltersToString(filterNode.getNodes()); } return ""; @@ -31,7 +35,7 @@ public static String filterToString(EdgeFilter edgeFilter) { filterStringBuilder.append(edgeFilter.name).append("("); for(int i = 0; i < edgeFilter.getArgs().size(); i++) { filterStringBuilder.append(valueToString(edgeFilter.getArgs().get(i))); - if(isNotLastIndex(edgeFilter.getArgs(), i)) { + if (isNotLastIndex(edgeFilter.getArgs(), i)) { filterStringBuilder.append(", "); } } @@ -100,15 +104,15 @@ private static List getValidNodeList(List filterNodes) { filterNodes.stream().filter(node -> node.getType().equals(AND_TYPE)).forEach(andList::add); filterNodes.stream().filter(node -> node.getType().equals(OR_TYPE)).forEach(orList::add); - if(!nodeList.isEmpty()) { + if (!nodeList.isEmpty()) { nodeList.addAll(andList); nodeList.addAll(orList); return nodeList; - } else if(!andList.isEmpty()) { + } else if (!andList.isEmpty()) { andList.stream().forEach(node -> nodeList.addAll(node.getNodes())); nodeList.addAll(orList); return getValidNodeList(nodeList); - } else if(!orList.isEmpty()) { + } else if (!orList.isEmpty()) { return orList; } @@ -116,7 +120,7 @@ private static List getValidNodeList(List filterNodes) { } private static String valueToString(FilterArgument argument) { - if(argument.getType().equals("string")) { + if (argument.getType().equals("string")) { return "\"" + argument.getValue() + "\""; } return argument.getValue(); diff --git a/src/federation-common/src/main/java/tak/server/federation/hub/ui/graph/NodeProperties.java b/src/federation-common/src/main/java/tak/server/federation/hub/ui/graph/NodeProperties.java index f5ba0747..f608f07f 100644 --- a/src/federation-common/src/main/java/tak/server/federation/hub/ui/graph/NodeProperties.java +++ b/src/federation-common/src/main/java/tak/server/federation/hub/ui/graph/NodeProperties.java @@ -58,11 +58,11 @@ public Map attributesToMap() { @SuppressWarnings("unchecked") private Object nodeToAttributeValue(Map node) { - if((node.get("type")).equals("attribute")) { + if ((node.get("type")).equals("attribute")) { return node.get("value"); - } else if((node.get("type")).equals("attributes")) { + } else if ((node.get("type")).equals("attributes")) { return node.get("values"); - } else if((node.get("type")).equals("nodes")) { + } else if ((node.get("type")).equals("nodes")) { return attributesToMap((List) node.get("nodes")); } return null; diff --git a/src/takserver-core/takserver-war/src/main/java/com/bbn/marti/sync/federation/MissionEnterpriseSyncRolVisitor.java b/src/federation-common/src/main/java/tak/server/federation/rol/MissionEnterpriseSyncRolVisitor.java similarity index 99% rename from src/takserver-core/takserver-war/src/main/java/com/bbn/marti/sync/federation/MissionEnterpriseSyncRolVisitor.java rename to src/federation-common/src/main/java/tak/server/federation/rol/MissionEnterpriseSyncRolVisitor.java index 36d52331..200f7042 100644 --- a/src/takserver-core/takserver-war/src/main/java/com/bbn/marti/sync/federation/MissionEnterpriseSyncRolVisitor.java +++ b/src/federation-common/src/main/java/tak/server/federation/rol/MissionEnterpriseSyncRolVisitor.java @@ -1,4 +1,4 @@ -package com.bbn.marti.sync.federation; +package tak.server.federation.rol; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/takserver-fig-core/rol/src/main/java/mil/af/rl/rol/MissionRolVisitor.java b/src/federation-common/src/main/java/tak/server/federation/rol/MissionRolVisitor.java similarity index 95% rename from src/takserver-fig-core/rol/src/main/java/mil/af/rl/rol/MissionRolVisitor.java rename to src/federation-common/src/main/java/tak/server/federation/rol/MissionRolVisitor.java index 8fd77546..39811696 100644 --- a/src/takserver-fig-core/rol/src/main/java/mil/af/rl/rol/MissionRolVisitor.java +++ b/src/federation-common/src/main/java/tak/server/federation/rol/MissionRolVisitor.java @@ -1,7 +1,10 @@ -package mil.af.rl.rol; +package tak.server.federation.rol; import java.io.IOException; +import mil.af.rl.rol.ResourceOperationParameterEvaluator; +import mil.af.rl.rol.RolBaseVisitor; +import mil.af.rl.rol.RolParser; import mil.af.rl.rol.value.Parameters; import org.slf4j.Logger; diff --git a/src/federation-hub-broker/build.gradle b/src/federation-hub-broker/build.gradle index 77772682..411140da 100644 --- a/src/federation-hub-broker/build.gradle +++ b/src/federation-hub-broker/build.gradle @@ -11,7 +11,6 @@ jar { apply plugin: 'eclipse' apply plugin: 'idea' apply plugin: 'org.springframework.boot' -apply plugin: 'io.spring.dependency-management' test { testLogging.showStandardStreams = true @@ -56,19 +55,20 @@ dependencies { implementation "io.netty:netty-tcnative-boringssl-static:$netty_tcnative_version:osx-aarch_64" implementation "io.netty:netty-tcnative-boringssl-static:$netty_tcnative_version:windows-x86_64" - // exclude from actuator implementation ("org.springframework.boot:spring-boot-starter-actuator:$spring_boot_version") { exclude group: 'org.apache.logging.log4j' exclude group: 'org.slf4j', module: 'log4j-over-slf4j' } implementation group: 'org.springframework.boot', name: 'spring-boot-loader', version: spring_boot_version - implementation group: 'org.springframework', name: 'spring-context' - + implementation group: 'org.springframework', name: 'spring-context', version: spring_version + implementation group: 'org.springframework.boot', name: 'spring-boot-starter-cache', version: spring_boot_version + implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-mongodb', version: spring_boot_version + api group: 'com.github.ben-manes.caffeine', name: 'caffeine', version: caffeine_version testImplementation group: 'junit', name: 'junit', version: junit_version testImplementation group: 'org.mockito', name: 'mockito-core', version: mockito_version - testImplementation("org.springframework.boot:spring-boot-starter-test") { + testImplementation("org.springframework.boot:spring-boot-starter-test:$spring_boot_version") { exclude group: "com.vaadin.external.google", module:"android-json" } diff --git a/src/federation-hub-broker/scripts/db/configure.sh b/src/federation-hub-broker/scripts/db/configure.sh new file mode 100755 index 00000000..2b8ead32 --- /dev/null +++ b/src/federation-hub-broker/scripts/db/configure.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +echo "Configuring DB" + +cd /opt/tak/federation-hub + +# stop mongo +echo "Trying to setup Mongo." +systemctl stop mongod +sleep 1 + +# create data directory, set mongod permissions, remove lock file +echo "Creating Mongo data directory at /var/lib/mongodb" +mkdir -p /var/lib/mongodb +chown -R mongod:mongod /var/lib/mongodb +chcon -Rv --type=mongod_var_lib_t /var/lib/mongodb +rm -f /tmp/mongodb-27017.sock +sleep 1 +cp /opt/tak/federation-hub/scripts/db/mongod-noauth.conf /etc/mongod.conf + +echo "Restarting Mongo..." +systemctl start mongod +sleep 10 + +# generate username + password and create user with mongosh +/opt/tak/federation-hub/scripts/db/setup-db.sh + +sleep 1 +rm -f /tmp/mongodb-27017.sock +sleep 1 + +cp /opt/tak/federation-hub/scripts/db/mongod-auth.conf /etc/mongod.conf +sleep 1 +systemctl restart mongod + +echo "Mongo Setup!" \ No newline at end of file diff --git a/src/federation-hub-broker/scripts/db/configureInDocker.sh b/src/federation-hub-broker/scripts/db/configureInDocker.sh new file mode 100755 index 00000000..9e1d7eab --- /dev/null +++ b/src/federation-hub-broker/scripts/db/configureInDocker.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +echo "Running configureInDocker.sh" + +cd /opt/tak/federation-hub + +if [ ! -f "/etc/mongod.conf" ]; then + echo "Mongo has not been setup set. Trying now." + + /usr/bin/mongod --bind_ip_all --config /opt/tak/federation-hub/scripts/db/mongod-noauth.conf & + mongoPID=$! + sleep 5 + /opt/tak/federation-hub/scripts/db/setup-db.sh + + sleep 1 + rm -f /tmp/mongodb-27017.sock + sleep 1 + + kill -9 $mongoPID + sleep 5 + cp /opt/tak/federation-hub/scripts/db/mongod-auth.conf /etc/mongod.conf +fi + +/usr/bin/mongod --bind_ip_all --config /etc/mongod.conf & diff --git a/src/federation-hub-broker/scripts/db/mongod-auth.conf b/src/federation-hub-broker/scripts/db/mongod-auth.conf new file mode 100755 index 00000000..0a557e16 --- /dev/null +++ b/src/federation-hub-broker/scripts/db/mongod-auth.conf @@ -0,0 +1,29 @@ +# mongod.conf + +# for documentation of all options, see: +# http://docs.mongodb.org/manual/reference/configuration-options/ + +security: + authorization: "enabled" + +# Where and how to store data. +storage: + dbPath: /var/lib/mongodb +# engine: +# wiredTiger: + +# where to write logging data. +systemLog: + destination: file + logAppend: true + path: /var/log/mongodb/mongod.log + +# network interfaces +net: + port: 27017 + bindIp: 127.0.0.1 + + +# how the process runs +processManagement: + timeZoneInfo: /usr/share/zoneinfo diff --git a/src/federation-hub-broker/scripts/db/mongod-noauth.conf b/src/federation-hub-broker/scripts/db/mongod-noauth.conf new file mode 100755 index 00000000..9f202b4d --- /dev/null +++ b/src/federation-hub-broker/scripts/db/mongod-noauth.conf @@ -0,0 +1,26 @@ +# mongod.conf + +# for documentation of all options, see: +# http://docs.mongodb.org/manual/reference/configuration-options/ + +# Where and how to store data. +storage: + dbPath: /var/lib/mongodb +# engine: +# wiredTiger: + +# where to write logging data. +systemLog: + destination: file + logAppend: true + path: /var/log/mongodb/mongod.log + +# network interfaces +net: + port: 27017 + bindIp: 127.0.0.1 + + +# how the process runs +processManagement: + timeZoneInfo: /usr/share/zoneinfo diff --git a/src/federation-hub-broker/scripts/db/mongodb-org.repo b/src/federation-hub-broker/scripts/db/mongodb-org.repo new file mode 100755 index 00000000..e0c89fc8 --- /dev/null +++ b/src/federation-hub-broker/scripts/db/mongodb-org.repo @@ -0,0 +1,6 @@ +[mongodb-org-6.0] +name=MongoDB Repository +baseurl=https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/6.0/x86_64/ +gpgcheck=1 +enabled=1 +gpgkey=https://www.mongodb.org/static/pgp/server-6.0.asc \ No newline at end of file diff --git a/src/federation-hub-broker/scripts/db/setup-db.sh b/src/federation-hub-broker/scripts/db/setup-db.sh new file mode 100755 index 00000000..b81e2eef --- /dev/null +++ b/src/federation-hub-broker/scripts/db/setup-db.sh @@ -0,0 +1,40 @@ +setup_mongo () { + username='' + password='' + + # try to get username from /opt/tak/CoreConfig.xml + if [ -f "/opt/tak/federation-hub/configs/federation-hub-broker.yml" ]; then + username=$(echo $(grep -m 1 "dbUsername:" /opt/tak/federation-hub/configs/federation-hub-broker.yml) | sed 's/.*dbUsername: *//') + fi + + # cant find username - use default + if [ -z "$username" ]; then + username='martiuser' + sed -i "/dbUsername:/d" /opt/tak/federation-hub/configs/federation-hub-broker.yml + echo "" >> /opt/tak/federation-hub/configs/federation-hub-broker.yml + echo "dbUsername: $username" >> /opt/tak/federation-hub/configs/federation-hub-broker.yml + fi + + # try to get password from /opt/tak/CoreConfig.xml + if [ -f "/opt/tak/federation-hub/configs/federation-hub-broker.yml" ]; then + password=$(echo $(grep -m 1 "dbPassword:" /opt/tak/federation-hub/configs/federation-hub-broker.yml) | sed 's/.*dbPassword: *//') + fi + + # cant find password - generate one + if [ -z "$password" ]; then + password=$(openssl rand -base64 12 | tr -dc 'a-zA-Z0-9') + sed -i "/dbPassword:/d" /opt/tak/federation-hub/configs/federation-hub-broker.yml + echo "" >> /opt/tak/federation-hub/configs/federation-hub-broker.yml + echo "dbPassword: $password" >> /opt/tak/federation-hub/configs/federation-hub-broker.yml + fi + + echo "db.createUser({user: '$username', pwd: '$password', roles: [ { role: 'root', db: 'admin' } ]})" > /opt/tak/federation-hub/scripts/db/create_user.js + echo "Creating the following admin user within mongo:" + cat "/opt/tak/federation-hub/scripts/db/create_user.js" + sleep 1 + $(echo "mongosh admin --file /opt/tak/federation-hub/scripts/db/create_user.js") + sleep 1 + rm "/opt/tak/federation-hub/scripts/db/create_user.js" +} + +setup_mongo diff --git a/src/federation-hub-broker/scripts/federation-hub-broker.sh b/src/federation-hub-broker/scripts/federation-hub-broker.sh index 295c6d53..66823690 100755 --- a/src/federation-hub-broker/scripts/federation-hub-broker.sh +++ b/src/federation-hub-broker/scripts/federation-hub-broker.sh @@ -3,4 +3,12 @@ export FEDERATION_HUB=/opt/tak/federation-hub export JDK_JAVA_OPTIONS="-Dio.netty.tmpdir=/opt/tak -Djava.io.tmpdir=/opt/tak -Dio.netty.native.workdir=/opt/tak -DIGNITE_UPDATE_NOTIFIER=false" -java -Dlogging.config=${FEDERATION_HUB}/configs/logback-broker.xml --add-opens java.base/jdk.internal.misc=ALL-UNNAMED -Dio.netty.tryReflectionSetAccessible=true -jar ${FEDERATION_HUB}/jars/federation-hub-broker.jar "$@" +# get total RAM +TOTALRAMBYTES=`awk '/MemTotal/ {print $2}' /proc/meminfo` + +# set BROKER max if not set already +if [ -z "$BROKER_MAX_HEAP" ]; then + export BROKER_MAX_HEAP=$(($TOTALRAMBYTES / 1000 / 100 * 50)) +fi + +java -Xmx${BROKER_MAX_HEAP}m -Dlogging.config=${FEDERATION_HUB}/configs/logback-broker.xml --add-opens java.base/jdk.internal.misc=ALL-UNNAMED -Dio.netty.tryReflectionSetAccessible=true -jar ${FEDERATION_HUB}/jars/federation-hub-broker.jar "$@" diff --git a/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/FederationHubBrokerService.java b/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/FederationHubBrokerService.java index 5dfe2e93..e5ae8c20 100644 --- a/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/FederationHubBrokerService.java +++ b/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/FederationHubBrokerService.java @@ -5,6 +5,7 @@ import java.io.FileInputStream; import java.math.BigInteger; import java.net.SocketAddress; +import java.rmi.RemoteException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.cert.Certificate; @@ -47,8 +48,8 @@ import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.tree.ParseTree; import org.apache.ignite.Ignite; -import org.apache.ignite.binary.BinaryObjectException; import org.apache.ignite.cache.query.ContinuousQuery; +import org.bson.types.ObjectId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationListener; @@ -105,7 +106,6 @@ import io.netty.handler.ssl.SslHandler; import io.netty.handler.ssl.SslProvider; import mil.af.rl.rol.FederationProcessor; -import mil.af.rl.rol.MissionRolVisitor; import mil.af.rl.rol.ResourceOperationParameterEvaluator; import mil.af.rl.rol.RolLexer; import mil.af.rl.rol.RolParser; @@ -118,16 +118,19 @@ import tak.server.federation.FederationPolicyGraph; import tak.server.federation.GuardedStreamHolder; import tak.server.federation.hub.FederationHubCache; +import tak.server.federation.hub.broker.db.FederationHubMissionDisruptionManager; import tak.server.federation.hub.broker.events.BrokerServerEvent; import tak.server.federation.hub.broker.events.HubClientDisconnectEvent; import tak.server.federation.hub.broker.events.RestartServerEvent; +import tak.server.federation.hub.broker.events.StreamReadyEvent; import tak.server.federation.hub.broker.events.UpdatePolicy; import tak.server.federation.hub.policy.FederationHubPolicyManager; import tak.server.federation.hub.ui.graph.FederationOutgoingCell; import tak.server.federation.hub.ui.graph.PolicyObjectCell; +import tak.server.federation.rol.MissionRolVisitor; public class FederationHubBrokerService implements ApplicationListener { - + private final Ignite ignite; private static final String SSL_SESSION_ID = "sslSessionId"; @@ -135,11 +138,16 @@ public class FederationHubBrokerService implements ApplicationListener v1ClientStreamMap = new ConcurrentHashMap<>(); private EventLoopGroup workerGroup; @@ -151,6 +159,7 @@ public class FederationHubBrokerService implements ApplicationListener continuousConfigurationQuery = new ContinuousQuery<>(); + private ContinuousQuery continuousConfigurationQuery = new ContinuousQuery<>(); - public FederationHubBrokerService(Ignite ignite, SSLConfig sslConfig, FederationHubServerConfig fedHubConfig, FederationHubPolicyManager fedHubPolicyManager, HubConnectionStore hubConnectionStore) { + public FederationHubBrokerService(Ignite ignite, SSLConfig sslConfig, FederationHubServerConfig fedHubConfig, FederationHubPolicyManager fedHubPolicyManager, HubConnectionStore hubConnectionStore, FederationHubMissionDisruptionManager federationHubMissionDisruptionManager, + FederationHubBrokerMetrics fedHubBrokerMetrics) { instance = this; this.ignite = ignite; this.sslConfig = sslConfig; this.fedHubConfig = fedHubConfig; this.fedHubPolicyManager = fedHubPolicyManager; this.hubConnectionStore = hubConnectionStore; + this.fedHubBrokerMetrics = fedHubBrokerMetrics; + this.federationHubMissionDisruptionManager = federationHubMissionDisruptionManager; + + federationHubROLHandler = new FederationHubROLHandler(federationHubMissionDisruptionManager); + setupFederationServers(); - + // rather than hitting ignite every time we need the policy graph, // use event driven approach to always have the updated graph // available here for instant access @@ -193,16 +212,16 @@ public FederationHubBrokerService(Ignite ignite, SSLConfig sslConfig, Federation federationPolicyGraph = e.getValue(); } }); - + FederationHubCache.getFederationHubPolicyStoreCache(ignite).query(continuousConfigurationQuery); } - + private FederationPolicyGraph federationPolicyGraph; - + public FederationPolicyGraph getFederationPolicyGraph() { if (federationPolicyGraph == null) federationPolicyGraph = fedHubPolicyManager.getPolicyGraph(); - + return federationPolicyGraph; } @@ -217,7 +236,7 @@ private void removeInactiveClientStreams() { logger.debug("Detected FederatedEvent client stream {} inactivity", clientStreamEntry.getValue().getFederateIdentity()); } - + clientStreamEntry.getValue().throwDeadlineExceptionToClient(); hubConnectionStore.clearIdFromAllStores(clientStreamEntry.getKey()); } @@ -239,20 +258,20 @@ public void stopV1Server() { channelFuture = null; } } - + public boolean addV1ConnectionInfo(String sessionId, HubConnectionInfo info) { Federate clientNode = getFederationPolicyGraph().getFederate(info.getConnectionId()); if (clientNode == null) { logger.info("Permission Denied. Federate/CA Group not found in the policy graph: " + info.getConnectionId()); return false; } - + info.setGroupIdentities(getFederationPolicyGraph().getFederate(info.getConnectionId()).getGroupIdentities()); hubConnectionStore.addConnectionInfo(sessionId, info); - + return true; } - + public void removeV1Connection(String sessionId) { v1ClientStreamMap.remove(sessionId); hubConnectionStore.clearIdFromAllStores(sessionId); @@ -394,7 +413,7 @@ public int compare(FederatedEvent a, FederatedEvent b) { ); v1ClientStreamMap.put(sessionId, handler); - + channel.pipeline() .addLast("ssl", sslHandler) .addLast(new ByteArrayDecoder()) @@ -499,7 +518,7 @@ public void onApplicationEvent(BrokerServerEvent event) { if (event instanceof HubClientDisconnectEvent) { outgoingClientMap.remove(((HubClientDisconnectEvent) event).getHubId()); } - + if (event instanceof RestartServerEvent) { if (fedHubConfig.isV2Enabled()) { logger.info("Restarting V2 federation server after truststore update"); @@ -516,49 +535,49 @@ public void onApplicationEvent(BrokerServerEvent event) { sslConfig.initSslContext(fedHubConfig); setupFederationV2Server(); } - + Collection cells = fedHubPolicyManager.getPolicyCells(); - + List outgoings = new ArrayList<>(); if (cells != null) { cells.stream().filter(cell -> cell instanceof FederationOutgoingCell).forEach( cell -> { outgoings.add((FederationOutgoingCell) cell); }); } - + updateOutgoingConnections(outgoings); } - + if (event instanceof UpdatePolicy) { // when the policy gets updated, the federate nodes from currently connected spokes will be cleared from the graph // they will not get re-added to the graph until a messages comes over the federate. to get around this, // we can re-add them based on their active sessions - + hubConnectionStore.getClientStreamMap().entrySet().forEach(entry -> { SSLSession session = hubConnectionStore.getSessionMap().get(entry.getKey()); if (session != null && session.isValid()) { addFederateToGroupPolicyIfMissingV2(hubConnectionStore.getSessionMap().get(entry.getKey()) ,entry.getValue()); - + // check if the currently connected spoke is still allowed to be connected after the policy change FederationPolicyGraph fpg = getFederationPolicyGraph(); Federate clientNode = checkFederateExistsInPolicy(entry.getValue(), session, fpg); if (clientNode == null) { logger.info("Permission Denied. Federate/CA Group not found in the policy graph: " + entry.getValue().getFederateIdentity()); entry.getValue().throwPermissionDeniedToClient(); - + HubFigClient client = outgoingClientMap.get(entry.getKey()); if (client != null) client.processDisconnect(); - + return; } - + } else { hubConnectionStore.removeSession(entry.getKey()); } }); - - v1ClientStreamMap.entrySet().forEach(entry -> { + + v1ClientStreamMap.entrySet().forEach(entry -> { // check if the currently connected spoke is still allowed to be connected after the policy change FederationPolicyGraph fpg = getFederationPolicyGraph(); Federate clientNode = getFederationPolicyGraph().getFederate(entry.getValue().getFederateIdentity()); @@ -568,10 +587,35 @@ public void onApplicationEvent(BrokerServerEvent event) { } }); - updateOutgoingConnections(((UpdatePolicy) event).getOutgoings()); + updateOutgoingConnections(((UpdatePolicy) event).getOutgoings()); + } + + if (event instanceof StreamReadyEvent) { + @SuppressWarnings("rawtypes") + StreamReadyEvent streamReadyEvent = (StreamReadyEvent) event; + + switch (streamReadyEvent.getType()) { + case EVENT: + { + break; + } + case GROUPS: + { + break; + } + case ROL: + { + @SuppressWarnings("unchecked") + List rolEvents = streamReadyEvent.getEvents(); + rolEvents.forEach(r -> this.parseRol(r, streamReadyEvent.getStreamKey())); + break; + } + default: + break; + } } } - + Map outgoingConfigMap = new HashMap<>(); Map outgoingClientMap = new HashMap<>(); Map> outgoingClientRetryMap = new HashMap<>(); @@ -584,49 +628,49 @@ private synchronized void updateOutgoingConnections(List .stream() .filter(future -> future != null) .forEach(future -> future.cancel(true)); - + outgoingClientRetryMap.clear(); - + // create a temp map for the updated outgoings Map updatedOutgoing = new HashMap<>(); outgoings.forEach(outgoing -> { updatedOutgoing.put(outgoing.getProperties().getOutgoingName(), outgoing); }); - - + + // check existing outgoings against updated outgoings for ones that got deleted Iterator> itr = outgoingConfigMap.entrySet().iterator(); while(itr.hasNext()) { Entry outgoing = itr.next(); - + if (updatedOutgoing.get(outgoing.getKey()) == null) { HubFigClient client = outgoingClientMap.get(outgoing.getKey()); outgoingClientMap.remove(outgoing.getKey()); - + if (client != null) { client.processDisconnect(); } } } - + // replace the old outgoings configs with the new ones outgoingConfigMap.clear(); outgoingConfigMap.putAll(updatedOutgoing); - + // if a new outgoing already has a client, lets stop it so it can be restarted with the updated config for (String key: outgoingConfigMap.keySet()) { if (outgoingClientMap.containsKey(key)) { outgoingClientMap.get(key).processDisconnectWithoutRetry(); } } - + // start all the outgoings defined in the policy outgoingConfigMap.entrySet().forEach(e -> { try { if (e.getValue().getProperties().isOutgoingEnabled()) { - HubFigClient client = new HubFigClient(fedHubConfig, e.getValue()); + HubFigClient client = new HubFigClient(fedHubConfig, federationHubMissionDisruptionManager, e.getValue()); client.start(); - outgoingClientMap.put(e.getKey(), client); + outgoingClientMap.put(e.getKey(), client); } } catch (Exception e1) { logger.error("Error creating hub client", e); @@ -637,7 +681,7 @@ private synchronized void updateOutgoingConnections(List logger.error("Error trying to update outgoing connections", e); } } - + public void scheduleRetry(String name) { fedHubPolicyManager.getPolicyCells().forEach(cell -> { if (cell instanceof FederationOutgoingCell) { @@ -648,28 +692,28 @@ public void scheduleRetry(String name) { } }); } - + public void scheduleRetry(String name, FederationOutgoingCell outgoing) { if (outgoingClientRetryMap.get(name) != null) outgoingClientRetryMap.get(name).cancel(true); - + if (fedHubConfig.getOutgoingReconnectSeconds() > 0 && outgoing.getProperties().isOutgoingEnabled()) { logger.info("Connection for {} failed. Trying again in {} seconds.", name, fedHubConfig.getOutgoingReconnectSeconds()); ScheduledFuture future = retryScheduler.scheduleAtFixedRate(() -> { attemptRetry(name, outgoing); }, fedHubConfig.getOutgoingReconnectSeconds(), fedHubConfig.getOutgoingReconnectSeconds(), TimeUnit.SECONDS); - + outgoingClientRetryMap.put(name, future); } else { logger.info("Not attempting a retry for {} because the Outgoing Reconnect is not > 0", name); } } - + private void attemptRetry(String name, FederationOutgoingCell outgoing) { try { - HubFigClient client = new HubFigClient(fedHubConfig, outgoing); + HubFigClient client = new HubFigClient(fedHubConfig, federationHubMissionDisruptionManager, outgoing); client.start(); - outgoingClientMap.put(name, client); + outgoingClientMap.put(name, client); outgoingClientRetryMap.get(name).cancel(true); outgoingClientRetryMap.remove(name); } catch (Exception e) { @@ -677,7 +721,7 @@ private void attemptRetry(String name, FederationOutgoingCell outgoing) { attemptRetry(name, outgoing); } } - + public void setupFederationServers() { sslConfig.initSslContext(fedHubConfig); @@ -688,7 +732,7 @@ public void setupFederationServers() { if (fedHubConfig.isV1Enabled()) { setupFederationV1Server(); } - + retryScheduler.schedule(() -> { // try to initialize outgoing connections from the saved policy List outgoings = fedHubPolicyManager.getPolicyCells() @@ -696,8 +740,8 @@ public void setupFederationServers() { .filter(c -> c instanceof FederationOutgoingCell) .map(c -> (FederationOutgoingCell) c) .collect(Collectors.toList()); - - this.updateOutgoingConnections(outgoings); + + this.updateOutgoingConnections(outgoings); }, 5, TimeUnit.SECONDS); } @@ -715,7 +759,7 @@ private void sendCaGroupsToFedManager(KeyStore keyStore) throws KeyStoreExceptio } } - public void addCaFederateToPolicyGraph(FederateIdentity federateIdentity, Certificate[] caCertArray) { + public void addCaFederateToPolicyGraph(FederateIdentity federateIdentity, Certificate[] caCertArray) { List caCertNames = new LinkedList<>(); /* @@ -736,9 +780,8 @@ public void addCaFederateToPolicyGraph(FederateIdentity federateIdentity, Certif if (logger.isDebugEnabled()) { logger.debug("addCaFederateToPolicyGraph issuerName: " + issuerName); } - - String groupName = issuerName + "-" + - FederationUtils.getBytesSHA256(caCertArray[i].getEncoded()); + + String groupName = issuerName + "-" + FederationUtils.getBytesSHA256(caCertArray[i].getEncoded()); caCertNames.add(groupName); } catch (CertificateEncodingException e) { logger.error("Could not encode certificate", e); @@ -758,13 +801,13 @@ public void addFederateToGroupPolicyIfMissingV1(Certificate[] certArray, addCaFederateToPolicyGraph(federateIdentity, certArray); } } - + public void addFederateToGroupPolicyIfMissingV2(SSLSession session, GuardedStreamHolder holder) { hubConnectionStore.addSession(new BigInteger(session.getId()).toString(), session); String fedId = holder.getFederateIdentity().getFedId(); - + FederationPolicyGraph fpg = getFederationPolicyGraph(); - + if (fpg.getNode(fedId) == null) { try { Certificate[] certArray = session.getPeerCertificates(); @@ -774,18 +817,18 @@ public void addFederateToGroupPolicyIfMissingV2(SSLSession session, GuardedStrea } } } - + // collect all groups for federates connected to the hub that can reach the given federate public FederateGroups getFederationHubGroups(String selfId) { try { // find receivable federates for the given federate - Collection receivableFederates = + Collection receivableFederates = getFederationPolicyGraph() .allReceivableFederates(selfId) .stream() .map(f -> f.getFederateIdentity().getFedId()) .collect(Collectors.toCollection(HashSet::new)); - + List federatedGroups = hubConnectionStore.getClientGroupStreamMap().entrySet() .stream() // ignore self groups @@ -812,7 +855,7 @@ public FederateGroups getFederationHubGroups(String selfId) { if (federateHops != null) { long maxHops = federateHops.getMaxHops(); long currentHops = federateHops.getCurrentHops() + 1; - + if (currentHops >= maxHops && maxHops != -1) { return false; } @@ -826,29 +869,29 @@ public FederateGroups getFederationHubGroups(String selfId) { .toBuilder() .setCurrentHops(g.getFederateHops().getCurrentHops() + 1) .build(); - + builder.setFederateHops(hops); return builder.build(); }) .collect(Collectors.toList()); - + // the reason we must use nested groups here is to maintain the hop limit of each individual FederateGroups object. // all of the group name strings will still be added to getFederateGroupsList() as usual. TAK Servers will still only look at - // the getFederateGroupsList(). but federation hubs will look at getNestedGroupsList() before sending to ensure the + // the getFederateGroupsList(). but federation hubs will look at getNestedGroupsList() before sending to ensure the // hop limit has not been reached. (TAK Servers are end of line, so they don't need to check) FederateGroups.Builder builder = FederateGroups.newBuilder(); - + federatedGroups.forEach(g -> builder.addNestedGroups(g)); - - Set federatedGroupsNameSet = + + Set federatedGroupsNameSet = federatedGroups .stream() .map(g->g.getFederateGroupsList()) .flatMap(list -> list.stream()) .collect(Collectors.toCollection(HashSet::new)); - + builder.addAllFederateGroups(federatedGroupsNameSet); - + return builder.build(); } catch (FederationException e) { logger.error("Could not get Federation Hub Groups", e); @@ -860,8 +903,8 @@ private class FederatedChannelService extends FederatedChannelGrpc.FederatedChan private final FederationHubBrokerService broker = FederationHubBrokerService.this; AtomicReference start = new AtomicReference<>(); - - + + @Override public void serverFederateGroupsStream(Subscription request, StreamObserver responseObserver) { @@ -874,7 +917,7 @@ public void serverFederateGroupsStream(Subscription request, StreamObserver streamHolder = new GuardedStreamHolder( responseObserver, request.getIdentity().getName(), FederationUtils.getBytesSHA256(clientCertArray[0].getEncoded()), session, request, @@ -884,27 +927,27 @@ public int compare(FederateGroups a, FederateGroups b) { return ComparisonChain.start().compare(a.hashCode(), b.hashCode()).result(); } }, true); - + addFederateToGroupPolicyIfMissingV2(session, streamHolder); - + FederationPolicyGraph fpg = getFederationPolicyGraph(); - requireNonNull(fpg, "federation policy graph object"); - + requireNonNull(fpg, "federation policy graph object"); + Federate clientNode = checkFederateExistsInPolicy(streamHolder, session, fpg); if (clientNode == null) { responseObserver.onError(new StatusRuntimeException(Status.PERMISSION_DENIED)); return; } - + hubConnectionStore.addGroupStream(id, streamHolder); // when a client connects to the hub, indicate a serving status and send it the // current list of groups - FederateGroups federateGroups = + FederateGroups federateGroups = getFederationHubGroups(streamHolder.getFederateIdentity().getFedId()).toBuilder() .setStreamUpdate(ServerHealth.newBuilder().setStatus(ServerHealth.ServingStatus.SERVING).build()) .build(); - + // do not use the stream holder to send the initial groups. // since this is a handshake, we are expecting groups back, // and the stream holder will attach the provenance, causing @@ -922,17 +965,17 @@ public StreamObserver clientFederateGroupsStream(StreamObserver< public void onNext(FederateGroups fedGroups) { SSLSession session = (SSLSession)sslSessionKey.get(Context.current()); String id = new BigInteger(session.getId()).toString(); - + GuardedStreamHolder holder = hubConnectionStore.getClientStreamMap().get(id); if (holder != null) { addFederateToGroupPolicyIfMissingV2(session, hubConnectionStore.getClientStreamMap().get(id)); } - + GuardedStreamHolder groupHolder = hubConnectionStore.getClientGroupStreamMap().get(id); if (groupHolder != null) { addFederateToGroupPolicyIfMissingV2(session, hubConnectionStore.getClientGroupStreamMap().get(id)); } - + addFederateGroups(id, fedGroups); } @@ -947,10 +990,10 @@ public void onError(Throwable t) { @Override public void onCompleted() { } - + }; } - + @Override public void sendOneEvent(FederatedEvent clientEvent, io.grpc.stub.StreamObserver emptyReseponse) { if (logger.isDebugEnabled()) { @@ -1041,7 +1084,7 @@ public void clientEventStream(Subscription subscription, StreamObserver streamHolder = null; if (!Strings.isNullOrEmpty(subscription.getFilter())) { @@ -1062,7 +1105,7 @@ public void clientEventStream(Subscription subscription, StreamObserver(clientStream, clientName, FederationUtils.getBytesSHA256(clientCertArray[0].getEncoded()), session, subscription, new Comparator() { @@ -1072,19 +1115,19 @@ public int compare(FederatedEvent a, FederatedEvent b) { } }, true ); - + addFederateToGroupPolicyIfMissingV2(session, streamHolder); - + FederationPolicyGraph fpg = getFederationPolicyGraph(); - requireNonNull(fpg, "federation policy graph object"); - + requireNonNull(fpg, "federation policy graph object"); + Federate clientNode = checkFederateExistsInPolicy(streamHolder, session, fpg); if (clientNode == null) { logger.info("Permission Denied. Federate/CA Group not found in the policy graph: " + streamHolder.getFederateIdentity()); clientStream.onError(new StatusRuntimeException(Status.PERMISSION_DENIED)); return; } - + // Send contact messages from other clients back to this new client. for (GuardedStreamHolder otherClient : hubConnectionStore.getClientStreamMap().values()) { @@ -1100,7 +1143,7 @@ public int compare(FederatedEvent a, FederatedEvent b) { FederateEdge edge = fpg.getEdge(otherClientNode, clientNode); if (otherClientNode != null && edge != null) { for (FederatedEvent event : otherClient.getCache()) { - if (isDestinationReachableByGroupFilter(edge, event.getFederateGroupsList())) { + if (isDestinationEdgeReachableByGroupFilter(edge, event.getFederateGroupsList())) { streamHolder.send(event); if (logger.isDebugEnabled()) { logger.debug("Sending v2 cached " + event + @@ -1125,7 +1168,7 @@ public int compare(FederatedEvent a, FederatedEvent b) { * a new session id. */ String sessionId = new BigInteger(session.getId()).toString(); - + HubConnectionInfo info = new HubConnectionInfo(); info.setConnectionId(streamHolder.getFederateIdentity().getFedId()); info.setRemoteServerId(subscription.getIdentity().getServerId()); @@ -1133,21 +1176,21 @@ public int compare(FederatedEvent a, FederatedEvent b) { info.setLocalConnectionType(Identity.ConnectionType.FEDERATION_HUB_SERVER.toString()); info.setFederationProtocolVersion(2); info.setGroupIdentities(getFederationPolicyGraph().getFederate(streamHolder.getFederateIdentity().getFedId()).getGroupIdentities()); - + SocketAddress socketAddress = getCurrentSocketAddress(); - + if (socketAddress != null) { info.setRemoteAddress(socketAddress.toString().replace("/", "")); } else { info.setRemoteAddress(""); } - + hubConnectionStore.addConnectionInfo(sessionId, info); hubConnectionStore.addClientStreamHolder(sessionId, streamHolder); - + // send a dummy message to make sure things are initialized streamHolder.send(FederatedEvent.newBuilder().build()); - + // if groups for this connection exist, send them here as well incase it failed if (hubConnectionStore.getClientToGroupsMap().get(sessionId) != null) { addFederateGroups(sessionId, hubConnectionStore.getClientToGroupsMap().get(sessionId)); @@ -1202,22 +1245,47 @@ public int compare(ROL a, ROL b) { } }, true ); - + addFederateToGroupPolicyIfMissingV2(session, rolStreamHolder); - + FederationPolicyGraph fpg = getFederationPolicyGraph(); - requireNonNull(fpg, "federation policy graph object"); - + requireNonNull(fpg, "federation policy graph object"); + Federate clientNode = checkFederateExistsInPolicy(rolStreamHolder, session, fpg); if (clientNode == null) { clientStream.onError(new StatusRuntimeException(Status.PERMISSION_DENIED)); return; } - + + // get the changes, but don't send till we add the rolStream because the stream will get used + // down the line for getting the session id + FederationHubMissionDisruptionManager.OfflineMissionChanges changes = null; + + if (fedHubConfig.isMissionFederationDisruptionEnabled()) { + changes = federationHubMissionDisruptionManager.getMissionChangesAndTrackConnectEvent( + rolStreamHolder.getFederateIdentity().getFedId(), session.getPeerCertificates()); + } + /* Keep track of client stream and its associated federate identity. */ String id = new BigInteger(session.getId()).toString(); hubConnectionStore.addRolStream(id, rolStreamHolder); + AtomicLong delayMs = new AtomicLong(0L); + if (changes != null) { + + for(final Entry entry: changes.getResourceRols().entrySet()) { + mfdtScheduler.schedule(() -> { + ROL rol = federationHubMissionDisruptionManager.hydrateResourceROL(entry.getKey(), entry.getValue()); + rolStreamHolder.send(rol); + }, delayMs.getAndAdd(500), TimeUnit.MILLISECONDS); + } + for(final ROL rol: changes.getRols()) { + mfdtScheduler.schedule(() -> { + rolStreamHolder.send(rol); + }, delayMs.getAndAdd(100), TimeUnit.MILLISECONDS); + } + } + logger.info("Client ROL stream added. Count: " + broker.hubConnectionStore.getClientROLStreamMap().size()); } catch (SSLPeerUnverifiedException | CertificateEncodingException e) { @@ -1228,23 +1296,29 @@ public int compare(ROL a, ROL b) { @Override public StreamObserver serverROLStream(StreamObserver responseObserver) { return new StreamObserver() { - @Override public void onNext(ROL clientROL) { try { if (logger.isDebugEnabled()) { logger.debug("ROL from client: " + clientROL.getProgram()); } - + requireNonNull(clientROL, "ROL message from client"); requireNonNull(clientROL.getProgram(), "ROL program from client"); - + SSLSession session = (SSLSession) sslSessionKey.get(Context.current()); String sessionId = new BigInteger(session.getId()).toString(); addFederateToGroupPolicyIfMissingV2(session, hubConnectionStore.getClientStreamMap().get(sessionId)); - - parseRol(clientROL, sessionId); + + GuardedStreamHolder streamHolder = hubConnectionStore.getClientROLStreamMap().get(sessionId); + // sometimes rol comes in before we're completely ready. + // temporarily cache rol until the stream holder gets added + if (streamHolder == null) { + hubConnectionStore.cacheRol(clientROL, sessionId); + } else { + parseRol(clientROL, sessionId); + } } catch (Exception e) { logger.error("Error with Rol read",e); } @@ -1272,12 +1346,12 @@ public void onCompleted() { public StreamObserver serverEventStream(StreamObserver responseObserver) { SSLSession session = (SSLSession)sslSessionKey.get(Context.current()); String id = new BigInteger(session.getId()).toString(); - + Subscription subscription = Subscription.newBuilder() .setFilter("") .setIdentity(Identity.newBuilder().setServerId(fedHubConfig.getFullId()).setType(Identity.ConnectionType.FEDERATION_HUB_SERVER).setName(id).setUid(id).build()) .build(); - + responseObserver.onNext(subscription); return new StreamObserver() { @@ -1286,7 +1360,7 @@ public StreamObserver serverEventStream(StreamObserver holder = hubConnectionStore.getClientStreamMap().get(id); if (holder != null) { @@ -1354,14 +1428,14 @@ public void healthCheck(ClientHealth request, StreamObserver respo } } } - + public Federate checkFederateExistsInPolicy(GuardedStreamHolder streamHolder, SSLSession session, FederationPolicyGraph fpg) { Federate clientNode = null; try { String fedId = streamHolder.getFederateIdentity().getFedId(); - + clientNode = fpg.getFederate(fedId); - + if (logger.isDebugEnabled()) { logger.debug("New client federated event stream client node id: " + streamHolder.getFederateIdentity().getFedId() + " " + @@ -1387,16 +1461,16 @@ public ServerCall.Listener interceptCall( SSLSession sslSession = call.getAttributes().get(Grpc.TRANSPORT_ATTR_SSL_SESSION); SocketAddress socketAddress = call.getAttributes().get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR); - + Context context = Context.current() .withValue(sslSessionKey, sslSession) .withValue(remoteAddressKey, socketAddress); - + return Contexts.interceptCall(context, call, requestHeaders, next); } }; } - + private SocketAddress getCurrentSocketAddress() { return remoteAddressKey.get(Context.current()); } @@ -1411,31 +1485,43 @@ public void assignMessageSourceAndDestinationsFromPolicy(Message message, message.getDestinations().add(new AddressableEntity<>(node.getFederateIdentity())) ); } - + public void assignGroupFilteredMessageSourceAndDestinationsFromPolicy(Message message, List groups, FederateIdentity federateIdentity) throws FederationException{ assignGroupFilteredMessageSourceAndDestinationsFromPolicy(message, groups, federateIdentity, getFederationPolicyGraph()); } - + private void assignGroupFilteredMessageSourceAndDestinationsFromPolicy(Message message, List groups, FederateIdentity federateIdentity, FederationPolicyGraph policyGraph) - throws FederationException { - + throws FederationException { + message.setSource(new AddressableEntity(federateIdentity)); - + + getGroupFilteredDestinations(groups, federateIdentity, policyGraph).forEach(node -> { + message.getDestinations().add(new AddressableEntity<>(node.getFederateIdentity())); + }); + + } + + public static Set getGroupFilteredDestinations(List groups, + FederateIdentity federateIdentity, FederationPolicyGraph policyGraph) + throws FederationException { + + Set reachableGroupFilteredFederates = new HashSet<>(); + Set destinationNodes = policyGraph.allReachableFederates(federateIdentity.getFedId()).federates; Map destinationToFederateEdges = policyGraph.allReachableFederates(federateIdentity.getFedId()).getDestinationToFederateEdgeMappings(); Map> destinationToFederateGroupEdges = policyGraph.allReachableFederates(federateIdentity.getFedId()).getDestinationToFederateGroupEdgeMappings(); - + destinationNodes.stream().forEach(node -> { - // direct edge between two nodes takes top priority for group filtereing + // direct edge between two nodes takes top priority for group filtering if (destinationToFederateEdges.containsKey(node)) { FederateEdge edge = destinationToFederateEdges.get(node); - if (!isDestinationReachableByGroupFilter(edge, groups)) + if (!isDestinationEdgeReachableByGroupFilter(edge, groups)) return; } - + // edge between nodes where the source is a CA group takes second priority // if there are multiple CA groups for some reason (intermediate certs?) // make sure all the edges pass @@ -1443,20 +1529,21 @@ else if (destinationToFederateGroupEdges.containsKey(node)) { // if any edges are false, fail the check boolean isNotReachableByGroupsForAllEdges = destinationToFederateGroupEdges.get(node) .stream() - .map(edge -> isDestinationReachableByGroupFilter(edge, groups)) + .map(edge -> isDestinationEdgeReachableByGroupFilter(edge, groups)) .anyMatch(isDestinationReachableByGroupFilter -> !isDestinationReachableByGroupFilter); if (isNotReachableByGroupsForAllEdges) return; } - + // if no edges, they must be interconnected, so skip group filtering - message.getDestinations().add(new AddressableEntity<>(node.getFederateIdentity())); - + reachableGroupFilteredFederates.add(node); }); + + return reachableGroupFilteredFederates; } - - public static boolean isDestinationReachableByGroupFilter(FederateEdge edge, List groups) { + + public static boolean isDestinationEdgeReachableByGroupFilter(FederateEdge edge, List groups) { // if no groups are attached, and the group filtering isn't set to // allow all messages through, drop the message if (groups == null || groups.isEmpty()) { @@ -1466,9 +1553,9 @@ public static boolean isDestinationReachableByGroupFilter(FederateEdge edge, Lis return false; } } - + Set groupSet = new HashSet<>(groups); - + switch (edge.getFilterType()) { case ALL: { return true; @@ -1480,7 +1567,7 @@ public static boolean isDestinationReachableByGroupFilter(FederateEdge edge, Lis return Sets.intersection(groupSet, edge.getDisallowedGroups()).isEmpty(); } case ALLOWED_AND_DISALLOWED: { - return !Sets.intersection(groupSet, edge.getAllowedGroups()).isEmpty() && + return !Sets.intersection(groupSet, edge.getAllowedGroups()).isEmpty() && Sets.intersection(groupSet, edge.getDisallowedGroups()).isEmpty(); } default: @@ -1519,6 +1606,8 @@ private void sendRolMessage(Message message) { FederateIdentity src = (FederateIdentity)message.getSource().getEntity(); FederateIdentity dest = (FederateIdentity)entity.getEntity(); + if (src == dest) continue; + FederationPolicyGraph policyGraph = getFederationPolicyGraph(); Federate srcNode = policyGraph.getFederate(src.getFedId()); @@ -1608,6 +1697,8 @@ private void deliver(Message message, FederateIdentity src, FederateIdentity des /* Track message sends for metrics. */ clientMessageCounter.incrementAndGet(); + recordBrokerMetrics(src, dest, message); + clientByteAccumulator.addAndGet(event.getSerializedSize()); } catch (Exception ex) { logger.error("Exception sending message to client stream", ex); @@ -1666,12 +1757,12 @@ private void sendFederatedEvent(Message message) { /* Validate src/dest and nodes. */ FederateEdge edge = policyGraph.getEdge(srcNode, destNode); - + deliver(message, src, dest); } } } - + private void deliverGroup(Message message, FederateIdentity src, FederateIdentity dest) { for (Entry> entry : hubConnectionStore.getClientGroupStreamMap().entrySet()) { if (entry.getValue().getFederateIdentity().equals(dest)) { @@ -1691,7 +1782,7 @@ private void deliverGroup(Message message, FederateIdentity src, FederateIdentit } } } - + private void sendFederatedGroup(Message message) { for (AddressableEntity entity : message.getDestinations()) { if (entity.getEntity() instanceof FederateIdentity) { @@ -1709,8 +1800,8 @@ private void sendFederatedGroup(Message message) { FederateGroups updatedGroups = getFederationHubGroups(dest.getFedId()).toBuilder() .addAllFederateProvenance(groups.getFederateProvenanceList()) - .build(); - + .build(); + deliverGroup(new Message(new HashMap<>(), new FederatedGroupPayload(updatedGroups)), src, dest); } } @@ -1731,7 +1822,7 @@ private void sendMessage(Message message) { sendRolMessage(message); } else if (message.getPayload().getContent() instanceof FederateGroups) { sendFederatedGroup(message); - } + } else { logger.info("Not handling send to client of " + message.getPayload().getContent().getClass().getSimpleName() + @@ -1746,7 +1837,7 @@ public void handleRead(BinaryBlob event, String streamKey) { } return; } - + Message federatedMessage = new Message(new HashMap<>(), new BinaryBlobPayload(event)); federatedMessage.setMetadataValue(SSL_SESSION_ID, streamKey); @@ -1764,6 +1855,7 @@ public void handleRead(BinaryBlob event, String streamKey) { getFederationPolicyGraph()); sendMessage(federatedMessage); + } catch (FederationException e) { logger.error("Could not get destinations from policy graph", e); } @@ -1771,21 +1863,12 @@ public void handleRead(BinaryBlob event, String streamKey) { } public void handleRead(ROL event, String streamKey) { - if (hasAlreadySeenMessage(event)) { - if (logger.isDebugEnabled()) { - logger.debug("Stopping circular event " + event); - } - - return; - } - Message federatedMessage = new Message(new HashMap<>(), new ROLPayload(event)); federatedMessage.setMetadataValue(SSL_SESSION_ID, streamKey); GuardedStreamHolder streamHolder = hubConnectionStore.getClientROLStreamMap().get(streamKey); if (streamHolder != null) { - federatedMessage.setMetadataValue(FEDERATED_ID_KEY, - streamHolder.getFederateIdentity()); + federatedMessage.setMetadataValue(FEDERATED_ID_KEY, streamHolder.getFederateIdentity()); try { assignGroupFilteredMessageSourceAndDestinationsFromPolicy(federatedMessage, event.getFederateGroupsList(), streamHolder.getFederateIdentity(), @@ -1795,10 +1878,10 @@ public void handleRead(ROL event, String streamKey) { logger.error("Could not get destinations from policy graph", e); } } else { - logger.error("Could not find stream holder for streamkey: {}", streamKey); + logger.error("Could not find stream holder for streamkey: {}", streamKey); } } - + public void handleRead(FederatedEvent event, String streamKey) { if (hasAlreadySeenMessage(event)) { if (logger.isDebugEnabled()) { @@ -1806,10 +1889,10 @@ public void handleRead(FederatedEvent event, String streamKey) { } return; } - + Message federatedMessage = new Message(new HashMap<>(), new FederatedEventPayload(event)); - + federatedMessage.setMetadataValue(SSL_SESSION_ID, streamKey); GuardedStreamHolder streamHolder = hubConnectionStore.getClientStreamMap().get(streamKey); @@ -1824,7 +1907,7 @@ public void handleRead(FederatedEvent event, String streamKey) { getFederationPolicyGraph()); sendMessage(federatedMessage); - + streamHolder.getCache().add(event); if (logger.isDebugEnabled()) { logger.debug("caching " + event + @@ -1843,45 +1926,45 @@ public void handleRead(FederatedEvent event, String streamKey) { logger.error("Exception sending message", e); } } else { - if (logger.isDebugEnabled()) + if (logger.isDebugEnabled()) logger.debug("StreamHolder is null"); } } - + private boolean hasAlreadySeenMessage(Object event) { try { List federateProvenances = null; - - + + if (event instanceof FederatedEvent) { federateProvenances = ((FederatedEvent) event).getFederateProvenanceList(); } - + if (event instanceof FederateGroups) { federateProvenances = ((FederateGroups) event).getFederateProvenanceList(); } - + if (event instanceof BinaryBlob) { federateProvenances = ((BinaryBlob) event).getFederateProvenanceList(); } - + if (event instanceof ROL) { federateProvenances = ((ROL) event).getFederateProvenanceList(); } - + if (federateProvenances == null) return false; - + Set visitedHubs = federateProvenances .stream() .map(prov -> prov.getFederationServerId()) .collect(Collectors.toSet()); - + return visitedHubs.contains(fedHubConfig.getFullId()); } catch (Exception e) { return false; } } - + public void addFederateGroups(String sourceId, FederateGroups groups) { if (hasAlreadySeenMessage(groups)) { if (logger.isDebugEnabled()) { @@ -1890,12 +1973,12 @@ public void addFederateGroups(String sourceId, FederateGroups groups) { return; } - + hubConnectionStore.putFederateGroups(sourceId, groups); - + Message federatedMessage = new Message(new HashMap<>(),new FederatedGroupPayload(groups)); federatedMessage.setMetadataValue(SSL_SESSION_ID, sourceId); - + GuardedStreamHolder holder = hubConnectionStore.getClientGroupStreamMap().containsKey(sourceId) ? hubConnectionStore.getClientGroupStreamMap().get(sourceId) : hubConnectionStore.getClientStreamMap().get(sourceId); FederateIdentity ident = holder.getFederateIdentity(); @@ -1909,8 +1992,22 @@ public void addFederateGroups(String sourceId, FederateGroups groups) { logger.error("Could not get destinations from policy graph", e); } } - - public void parseRol(ROL clientROL, String id) { + + public void parseRol(ROL clientROL, String streamKey) { + if (hasAlreadySeenMessage(clientROL)) { + if (logger.isDebugEnabled()) { + logger.debug("Stopping circular event " + clientROL); + } + + return; + } + + handleRead(clientROL, streamKey); + + // no need to process rol any further if mfd is disabled + if (!fedHubConfig.isMissionFederationDisruptionEnabled()) + return; + try { /* Interpret and execute the ROL program. */ RolLexer lexer = new RolLexer(new ANTLRInputStream(clientROL.getProgram())); @@ -1935,8 +2032,8 @@ public String evaluate(String res, String op, Parameters params) { resource.set(res); operation.set(op); parameters.set(params); - - logger.info("Evaluating " + op + " on " + resource + " given " + params); + + logger.info("Evaluating " + op + " on " + resource); return res; } @@ -1945,9 +2042,11 @@ public String evaluate(String res, String op, Parameters params) { requireNonNull(resource.get(), "resource"); requireNonNull(operation.get(), "operation"); + GuardedStreamHolder streamHolder = hubConnectionStore.getClientROLStreamMap().get(streamKey); + /* Create a federation processor for this ROL type, and process the ROL program. */ federationProcessorFactory.newProcessor(resource.get(), operation.get(), - parameters.get(), id).process(clientROL); + parameters.get(), streamKey, streamHolder.getFederateIdentity().getFedId()).process(clientROL); } catch (Exception e) { logger.error("Exception in ROL parsing " + e.getClass().getName(), e); @@ -1955,26 +2054,67 @@ public String evaluate(String res, String op, Parameters params) { } private class FederationProcessorFactory { - FederationProcessor newProcessor(String resource, String operation, Parameters parameters, String sessionId) { - return new FederationDefaultProcessor(resource, operation, sessionId); - } - - - private class FederationDefaultProcessor implements FederationProcessor { - private final String res; - private final String op; - private final String sessionId; - - FederationDefaultProcessor(String res, String op, String sessionId) { - this.res = res; - this.op = op; - this.sessionId = sessionId; - } + FederationProcessor newProcessor(String resource, String operation, Parameters parameters, String streamKey, String federateServerId) { + return new FederationMissionProcessor(streamKey, federateServerId); + } + } - @Override - public void process(ROL rol) { - handleRead(rol, sessionId); + private class FederationMissionProcessor implements FederationProcessor { + private final String streamKey; + private final String federateServerId; + + FederationMissionProcessor(String streamKey, String federateServerId) { + this.streamKey = streamKey; + this.federateServerId = federateServerId; + } + + @Override + public void process(ROL rol) { + try { + + if (federationHubROLHandler != null) { + federationHubROLHandler.onNewEvent(rol, streamKey, federateServerId); + } + + } catch (RemoteException e) { + if (logger.isDebugEnabled()) { + logger.debug("exception submitting ROL", e); + } + } + } + } + + private void recordBrokerMetrics(FederateIdentity src, FederateIdentity dest, Message message) { + FederationPolicyGraph fpg = getFederationPolicyGraph(); + Federate srcFederate = fpg.getFederate(src.getFedId()); + Federate destFederate = fpg.getFederate(dest.getFedId()); + Set srcFederateIdentities = srcFederate.getGroupIdentities(); + Set destFederateIdentities = destFederate.getGroupIdentities(); + String srcCert = null; + if (!srcFederateIdentities.isEmpty()) { + Iterator it = srcFederateIdentities.iterator(); + srcCert = it.next().getFedId(); + } else { + logger.warn("Failed to get cert for src."); + } + String destCert = null; + if (!destFederateIdentities.isEmpty()) { + Iterator it = destFederateIdentities.iterator(); + destCert = it.next().getFedId(); + } else { + logger.warn("Failed to get cert for dest."); + } + Payload messagePayload = message.getPayload(); + long messageLength = -1; + if (messagePayload != null) { + byte[] messageBytes = messagePayload.getBytes(); + if (messageBytes != null) { + messageLength = messageBytes.length; } } + if (srcCert != null && destCert != null && messageLength != -1) { + fedHubBrokerMetrics.incrementChannelWrite(src.getFedId(), srcCert, dest.getFedId(), destCert, messageLength); + fedHubBrokerMetrics.incrementChannelRead(src.getFedId(), srcCert, dest.getFedId(), destCert, messageLength); + } } -} +} \ No newline at end of file diff --git a/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/FederationHubMissionDisruptionManager.java b/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/FederationHubMissionDisruptionManager.java new file mode 100644 index 00000000..bdb7c9c4 --- /dev/null +++ b/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/FederationHubMissionDisruptionManager.java @@ -0,0 +1,272 @@ +package tak.server.federation.hub.broker.db; + +import java.io.ByteArrayInputStream; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +import org.bson.Document; +import org.bson.types.Binary; +import org.bson.types.ObjectId; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.atakmap.Tak.BinaryBlob; +import com.atakmap.Tak.FederateHops; +import com.atakmap.Tak.FederateProvenance; +import com.atakmap.Tak.ROL; +import com.bbn.roger.fig.FederationUtils; +import com.google.protobuf.ByteString; +import com.mongodb.BasicDBObject; + +import tak.server.federation.Federate; +import tak.server.federation.FederateIdentity; +import tak.server.federation.FederationPolicyGraph; +import tak.server.federation.hub.FederationHubDependencyInjectionProxy; +import tak.server.federation.hub.broker.FederationHubBrokerService; + +public class FederationHubMissionDisruptionManager { + private static final Logger logger = LoggerFactory.getLogger(FederationHubMissionDisruptionManager.class); + + private FederationHubDatabaseService federationHubDatabaseService; + + public FederationHubMissionDisruptionManager (FederationHubDatabaseService federationHubDatabaseService) { + this.federationHubDatabaseService = federationHubDatabaseService; + } + + @SuppressWarnings("unchecked") + // get all the federates that this new connection is allowed to receive from + private Collection getReceivableFederates(String sourceServerId) throws Exception { + // make sure all of the persisted federates are in the policy graph + for(Document document : federationHubDatabaseService.getFederateMetadatas()) { + String federateId = document.getString("federate_id"); + + List certs = (List) document.get("cert_array"); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + for (Binary cert : certs) { + ByteArrayInputStream bais = new ByteArrayInputStream(cert.getData()); + X509Certificate x509Cert = (X509Certificate) cf.generateCertificate(bais); + + String issuerDN = x509Cert.getIssuerX500Principal().getName(); + String group = issuerDN + "-" + FederationUtils.getBytesSHA256(x509Cert.getEncoded()); + Federate federate = new Federate(new FederateIdentity(federateId)); + federate.addGroupIdentity(new FederateIdentity(group)); + List federateGroups = new ArrayList<>(); + federateGroups.add(group); + + FederationHubDependencyInjectionProxy.getInstance().fedHubPolicyManager().addCaFederate(federate, federateGroups); + } + } + + Collection receivableFederates = + FederationHubDependencyInjectionProxy.getInstance().fedHubPolicyManager().getPolicyGraph() + .allReceivableFederates(sourceServerId) + .stream() + .map(f -> f.getFederateIdentity().getFedId()) + .collect(Collectors.toCollection(HashSet::new)); + + return receivableFederates; + } + + @SuppressWarnings("unchecked") + public OfflineMissionChanges getMissionChangesAndTrackConnectEvent(String federateServerId, Certificate[] certificates) { + OfflineMissionChanges changes = new OfflineMissionChanges(); + + try { + Document metadata = federationHubDatabaseService.getFederateMetadata(federateServerId); + + // if this federate previously connected, only pull changes from last update time + Date lastUpdate = new Date(0L); + if (metadata != null) { + lastUpdate = (Date) metadata.get("last_update"); + } + + Date now = new Date(); + + long recencySecs = FederationHubDependencyInjectionProxy.getInstance().fedHubServerConfig().getMissionFederationRecencySeconds(); + long maxRecencyMillis; + if (recencySecs == -1) { + maxRecencyMillis = lastUpdate.getTime(); + } else { + maxRecencyMillis = now.getTime() - (recencySecs * 1000); + } + + long bestRecencyMillis = lastUpdate.getTime() > maxRecencyMillis ? lastUpdate.getTime() : maxRecencyMillis; + + Collection receivableFederates = getReceivableFederates(federateServerId); + // must get updated policy after we check for receivable federates + // because getReceivableFederates(...) will modify the graph + FederationPolicyGraph policyGraph = FederationHubDependencyInjectionProxy.getInstance().fedHubPolicyManager().getPolicyGraph(); + + for (String receivableFederate: receivableFederates) { + // for every federate this newly connected federate can receive from, + // pull the offline changes + List updates = federationHubDatabaseService.getOfflineUpdates(receivableFederate, new Date(bestRecencyMillis)); + for (Document update: updates) { + ROL.Builder rolBuilder = ROL.newBuilder(); + Document eventRol = update.get("event_rol", Document.class); + rolBuilder.setProgram(eventRol.getString("rol_program")); + + if (eventRol.containsKey("federate_groups")) { + List federateGroups = eventRol.getList("federate_groups", String.class); + rolBuilder.addAllFederateGroups(federateGroups); + } + + if (eventRol.containsKey("federate_provenance")) { + List federateProvenances = eventRol.getList("federate_provenance", Document.class); + for (Document federateProvenance: federateProvenances) { + rolBuilder.addFederateProvenance(FederateProvenance.newBuilder() + .setFederationServerName(federateProvenance.getString("federation_server_name")) + .setFederationServerId(federateProvenance.getString("federation_server_id"))); + } + } + + if (eventRol.containsKey("federate_hops")) { + Document federateHops = eventRol.get("federate_hops", Document.class); + rolBuilder.setFederateHops(FederateHops.newBuilder() + .setMaxHops(federateHops.getLong("max_hops")) + .setCurrentHops(federateHops.getLong("current_hops"))); + } + + ROL rol = rolBuilder.build(); + + // make sure we check reachability if group filtering is active + Set reachableFederates = FederationHubBrokerService.getGroupFilteredDestinations(rol.getFederateGroupsList(), new FederateIdentity(receivableFederate), policyGraph); + boolean isReachable = reachableFederates + .stream() + .map(f -> f.getFederateIdentity().getFedId()) + .anyMatch(id -> id.equals(federateServerId)); + + if (isReachable) { + if (eventRol.containsKey("resource_object_id")) + changes.addResourceROL(eventRol.getObjectId("resource_object_id"), rolBuilder); + else + changes.addROL(rol); + } + } + } + + federationHubDatabaseService.addFederateMetadata(federateServerId, certificates); + } catch (Exception e) { + logger.error("getMissionChangesAndTrackConnectEvent error", e); + } + + logger.info("Found " + (changes.getRols().size() + changes.getResourceRols().size()) + " mission changes for " + federateServerId); + + return changes; + } + + public ROL hydrateResourceROL(ObjectId resourceObjectId, ROL.Builder rol) { + try { + byte[] resource = federationHubDatabaseService.getResource(resourceObjectId); + + if (resource == null) return rol.build(); + + BinaryBlob blob = BinaryBlob.newBuilder().setData(ByteString.readFrom(new ByteArrayInputStream(resource))).build(); + rol.addPayload(blob); + } catch (Exception e) { + logger.error("hydrateResourceROL error", e); + } + + return rol.build(); + } + + public ObjectId addResource(byte[] data, Map parameters) { + try { + return federationHubDatabaseService.addResource(data, parameters); + } catch (Exception e) { + logger.error("Error adding resource to DB", e); + return null; + } + } + + public void storeRol(ROL event, String res, String op, Map parameters, String federateServerId) { + this.storeRol(event, res, op, parameters, federateServerId, null); + } + + public void storeRol(ROL event, String res, String op, Map parameters, String federateServerId, ObjectId resourceObjectId) { + try { + BasicDBObject rolParameters = new BasicDBObject(); + rolParameters.putAll(parameters); + + List federateProvenances = null; + if (event.getFederateProvenanceList() != null) { + federateProvenances = event.getFederateProvenanceList() + .stream() + .map(p -> { + BasicDBObject prov = new BasicDBObject(); + prov.append("federation_server_id", p.getFederationServerId()); + prov.append("federation_server_name", p.getFederationServerName()); + return prov; + }).collect(Collectors.toList()); + } + + BasicDBObject federateHops = null; + if (event.getFederateHops() != null) { + federateHops = new BasicDBObject(); + federateHops.append("max_hops", event.getFederateHops().getMaxHops()); + federateHops.append("current_hops", event.getFederateHops().getCurrentHops()); + } + + BasicDBObject eventRol = new BasicDBObject(); + eventRol.put("rol_program", event.getProgram()); + eventRol.put("rol_resource", res); + eventRol.put("rol_operation", op); + eventRol.put("rol_parameters", rolParameters); + eventRol.put("federate_groups", event.getFederateGroupsList()); + eventRol.put("federate_provenance", federateProvenances); + eventRol.put("federate_hops", federateHops); + + if (resourceObjectId != null) { + eventRol.put("resource_object_id", resourceObjectId); + } + + Document doc = new Document(); + doc.append("federate_id", federateServerId); + doc.append("federate_metadata_id", federationHubDatabaseService.getFederateMetadata(federateServerId).get("_id")); + doc.append("event_kind", "send-changes"); + doc.append("received_time", Instant.now()); + doc.append("event_rol", eventRol); + + federationHubDatabaseService.storeRol(doc); + } catch (Exception e) { + logger.error("Error storing ROL in DB", e); + } + } + + private void isDBAvailable() { + + } + + public static final class OfflineMissionChanges { + Map resourceRols = new HashMap<>(); + List rols = new ArrayList<>(); + + public Map getResourceRols() { + return resourceRols; + } + + public List getRols() { + return rols; + } + + public void addResourceROL(ObjectId key, ROL.Builder value) { + resourceRols.put(key, value); + } + + public void addROL(ROL e) { + rols.add(e); + } + } +} diff --git a/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/FederationHubROLHandler.java b/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/FederationHubROLHandler.java new file mode 100644 index 00000000..44b140d2 --- /dev/null +++ b/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/FederationHubROLHandler.java @@ -0,0 +1,209 @@ +package tak.server.federation.hub.broker; + +import static java.util.Objects.requireNonNull; + +import java.rmi.RemoteException; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicReference; + +import org.antlr.v4.runtime.ANTLRInputStream; +import org.antlr.v4.runtime.BailErrorStrategy; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.tree.ParseTree; +import org.bson.types.ObjectId; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.atakmap.Tak.ROL; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; + +import mil.af.rl.rol.FederationProcessor; +import mil.af.rl.rol.Resource; +import mil.af.rl.rol.ResourceOperationParameterEvaluator; +import mil.af.rl.rol.RolLexer; +import mil.af.rl.rol.RolParser; +import tak.server.federation.hub.broker.db.FederationHubMissionDisruptionManager; +import tak.server.federation.rol.MissionEnterpriseSyncRolVisitor; + +public class FederationHubROLHandler { + + private static final Logger logger = LoggerFactory.getLogger(FederationHubROLHandler.class); + private int numThreads = Runtime.getRuntime().availableProcessors() + 1; + private ExecutorService executor = Executors.newFixedThreadPool(numThreads); + + private FederationHubMissionDisruptionManager federationHubMissionDisruptionManager; + + public FederationHubROLHandler(FederationHubMissionDisruptionManager federationHubMissionDisruptionManager) { + this.federationHubMissionDisruptionManager = federationHubMissionDisruptionManager; + } + + public void onNewEvent(ROL rol, String streamKey, String federateServerId) throws RemoteException { + executor.execute(() -> { + if (rol == null) { + if (logger.isDebugEnabled()) { + logger.debug("skipping null ROL message"); + } + return; + } + + if (logger.isDebugEnabled()) { + logger.debug("Got ROL message: " + rol.getProgram() + " for federateServerId " + federateServerId); + } + + // interpret and execute the ROL program + RolLexer lexer = new RolLexer(new ANTLRInputStream(rol.getProgram())); + + CommonTokenStream tokens = new CommonTokenStream(lexer); + + RolParser parser = new RolParser(tokens); + parser.setErrorHandler(new BailErrorStrategy()); + + // parse the ROL program + ParseTree rolParseTree = parser.program(); + + requireNonNull(rolParseTree, "parsed ROL program"); + + final AtomicReference res = new AtomicReference<>(); + final AtomicReference op = new AtomicReference<>(); + final AtomicReference parameters = new AtomicReference<>(); + + new MissionEnterpriseSyncRolVisitor(new ResourceOperationParameterEvaluator() { + @Override + public String evaluate(String resource, String operation, Object params) { + if (logger.isDebugEnabled()) { + logger.debug(" evaluating " + operation + " on " + resource + " given " + params); + } + + res.set(resource); + op.set(operation); + parameters.set(params); + + return resource; + } + }).visit(rolParseTree); + + try { + new FederationProcessorFactory().newProcessor(res.get(), op.get(), parameters.get(), streamKey, federateServerId).process(rol); + } catch (Exception e) { + logger.warn("exception processing incoming ROL", e); + } + }); + } + + class FederationProcessorFactory { + FederationProcessor newProcessor(String resource, String operation, Object parameters, String streamKey, String federateServerId) { + switch (Resource.valueOf(resource.toUpperCase())) { + case PACKAGE: + throw new UnsupportedOperationException( + "federated mission package processing occurs in core - this ROL should not have been sent"); + case MISSION: + return new FederationMissionProcessor(resource, operation, parameters, streamKey, federateServerId); + case DATA_FEED: + return new FederationDataFeedProcessor(resource, operation, parameters, streamKey, federateServerId); + case RESOURCE: + return new FederationSyncResourceProcessor(resource, operation, parameters, streamKey, + federateServerId); + default: + throw new IllegalArgumentException("invalid federation processor kind " + resource); + } + } + } + + private class FederationMissionProcessor implements FederationProcessor { + + private final String res; + private final String op; + private Map parameters; + private final String streamKey; + private final String federateServerId; + + FederationMissionProcessor(String res, String op, Object parameters, String streamKey, String federateServerId) { + this.res = res; + this.op = op; + this.parameters = objectMapper().convertValue(parameters, Map.class); + Iterables.removeIf(this.parameters.values(), Predicates.isNull()); + this.streamKey = streamKey; + this.federateServerId = federateServerId; + } + + @Override + public void process(ROL source) { + federationHubMissionDisruptionManager.storeRol(source, res, op, parameters, federateServerId); + } + } + + private class FederationDataFeedProcessor implements FederationProcessor { + + private final String res; + private final String op; + private Map parameters; + private final String streamKey; + private final String federateServerId; + + FederationDataFeedProcessor(String res, String op, Object parameters, String streamKey, String federateServerId) { + this.res = res; + this.op = op; + this.parameters = objectMapper().convertValue(parameters, Map.class); + Iterables.removeIf(this.parameters.values(), Predicates.isNull()); + this.streamKey = streamKey; + this.federateServerId = federateServerId; + } + + @Override + public void process(ROL source) { + federationHubMissionDisruptionManager.storeRol(source, res, op, parameters, federateServerId); + } + } + + private class FederationSyncResourceProcessor implements FederationProcessor { + + private final String res; + private final String op; + private Map parameters; + private final String streamKey; + private final String federateServerId; + + FederationSyncResourceProcessor(String res, String op, Object parameters, String streamKey, String federateServerId) { + this.res = res; + this.op = op; + this.parameters = objectMapper().convertValue(parameters, Map.class); + Iterables.removeIf(this.parameters.values(), Predicates.isNull()); + this.streamKey = streamKey; + this.federateServerId = federateServerId; + } + + @Override + public void process(ROL source) { + switch (op.toLowerCase(Locale.ENGLISH)) { + case "create": { + if (source.getPayloadList().isEmpty()) { + logger.info("empty resource payload"); + return; + } + + byte[] content = source.getPayload(0).getData().toByteArray(); + ObjectId resId = federationHubMissionDisruptionManager.addResource(content, parameters); + federationHubMissionDisruptionManager.storeRol(source, res, op, parameters, federateServerId, resId); + } + break; + default: + federationHubMissionDisruptionManager.storeRol(source, res, op, parameters, federateServerId); + } + } + } + + private ThreadLocal objectMapper = new ThreadLocal<>(); + + private ObjectMapper objectMapper() { + if (objectMapper.get() == null) { + objectMapper.set(new ObjectMapper()); + } + + return objectMapper.get(); + } +} diff --git a/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/FederationHubServer.java b/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/FederationHubServer.java index ed57f880..66d3159e 100644 --- a/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/FederationHubServer.java +++ b/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/FederationHubServer.java @@ -5,6 +5,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.UUID; +import java.util.concurrent.TimeUnit; import org.apache.ignite.Ignite; import org.apache.ignite.Ignition; @@ -14,134 +15,187 @@ import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.caffeine.CaffeineCacheManager; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; +import org.springframework.data.mongodb.core.MongoTemplate; import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.github.benmanes.caffeine.cache.Caffeine; import com.google.common.base.Strings; import tak.server.federation.hub.FederationHubConstants; import tak.server.federation.hub.FederationHubDependencyInjectionProxy; import tak.server.federation.hub.FederationHubUtils; +import tak.server.federation.hub.broker.db.FederationHubDatabaseService; +import tak.server.federation.hub.broker.db.FederationHubDatabaseServiceImpl; +import tak.server.federation.hub.broker.db.FederationHubMissionDisruptionManager; +import tak.server.federation.hub.db.FederationHubDatabase; import tak.server.federation.hub.policy.FederationHubPolicyManager; import tak.server.federation.hub.policy.FederationHubPolicyManagerProxyFactory; -@SpringBootApplication +@SpringBootApplication(exclude = {MongoAutoConfiguration.class}) +@EnableCaching public class FederationHubServer implements CommandLineRunner { - private static final String DEFAULT_CONFIG_FILE = "/opt/tak/federation-hub/configs/federation-hub-broker.yml"; - - private static final Logger logger = LoggerFactory.getLogger(FederationHubServer.class); - - private static Ignite ignite = null; - - private static String configFile; - - public static void main(String[] args) { - - if (args.length > 1) { - System.err.println("Usage: java -jar federation-hub-broker.jar [CONFIG_FILE_PATH]"); - return; - } else if (args.length == 1) { - configFile = args[0]; - } else if (!Strings.isNullOrEmpty(System.getProperty("FEDERATION_HUB_BROKER_CONFIG"))) { - configFile = System.getProperties().getProperty("FEDERATION_HUB_BROKER_CONFIG"); - } else { - configFile = DEFAULT_CONFIG_FILE; - } - - SpringApplication application = new SpringApplication(FederationHubServer.class); - - ignite = Ignition.getOrStart(FederationHubUtils.getIgniteConfiguration( - FederationHubConstants.FEDERATION_HUB_BROKER_IGNITE_PROFILE, - true)); - if (ignite == null) { - System.exit(1); - } - - ApplicationContext context = application.run(args); - } - - @Override - public void run(String... args) throws Exception {} - - @Bean - public FederationHubDependencyInjectionProxy dependencyProxy() { - return new FederationHubDependencyInjectionProxy(); - } - - @Bean - public FederationHubBroker federationHubBroker(Ignite ignite) { - FederationHubBrokerImpl hb = new FederationHubBrokerImpl(); - ClusterGroup cg = ignite.cluster().forAttribute( - FederationHubConstants.FEDERATION_HUB_IGNITE_PROFILE_KEY, - FederationHubConstants.FEDERATION_HUB_BROKER_IGNITE_PROFILE); - ignite.services(cg).deployClusterSingleton( - FederationHubConstants.FED_HUB_BROKER_SERVICE, hb); - - return hb; - } - - @Bean - public Ignite getIgnite() { - return ignite; - } - - @Bean - public FederationHubPolicyManagerProxyFactory fedHubPolicyManagerProxyFactory() { - return new FederationHubPolicyManagerProxyFactory(); - } - - @Bean - public SSLConfig getSslConfig() { - return new SSLConfig(); - } - - @Bean - public HubConnectionStore hubConnectionStore() { - return new HubConnectionStore(); - } - - @Bean - public FederationHubServerConfig getFedHubConfig() - throws JsonParseException, JsonMappingException, IOException { - FederationHubServerConfig config = loadConfig(configFile); - if (Strings.isNullOrEmpty(config.getId())) { - config.setId(UUID.randomUUID().toString().replace("-", "")); - saveConfig(configFile, config); - } - - return loadConfig(configFile); - } - - @Bean - @Order(Ordered.LOWEST_PRECEDENCE) - public FederationHubBrokerService FederationHubBrokerService(Ignite ignite, SSLConfig getSslConfig, FederationHubServerConfig fedHubConfig, FederationHubPolicyManager fedHubPolicyManager, HubConnectionStore hubConnectionStore) { - return new FederationHubBrokerService(ignite, getSslConfig, fedHubConfig, fedHubPolicyManager, hubConnectionStore); - } - - private FederationHubServerConfig loadConfig(String configFile) - throws JsonParseException, JsonMappingException, FileNotFoundException, IOException { - if (getClass().getResource(configFile) != null) { - // It's a resource. - return new ObjectMapper(new YAMLFactory()).readValue(getClass().getResourceAsStream(configFile), - FederationHubServerConfig.class); - } - - // It's a file. - return new ObjectMapper(new YAMLFactory()).readValue(new FileInputStream(configFile), - FederationHubServerConfig.class); - } - - private void saveConfig(String configFile, FederationHubServerConfig config) - throws JsonParseException, JsonMappingException, FileNotFoundException, IOException { - - ObjectMapper om = new ObjectMapper(new YAMLFactory()); - om.writeValue(new File(configFile), config); - } -} + private static final String DEFAULT_CONFIG_FILE = "/opt/tak/federation-hub/configs/federation-hub-broker.yml"; + + private static final Logger logger = LoggerFactory.getLogger(FederationHubServer.class); + + private static Ignite ignite = null; + + private static String configFile; + + public static void main(String[] args) throws FileNotFoundException, IOException { + if (args.length > 1) { + System.err.println("Usage: java -jar federation-hub-broker.jar [CONFIG_FILE_PATH]"); + return; + } else if (args.length == 1) { + configFile = args[0]; + } else if (!Strings.isNullOrEmpty(System.getProperty("FEDERATION_HUB_BROKER_CONFIG"))) { + configFile = System.getProperties().getProperty("FEDERATION_HUB_BROKER_CONFIG"); + } else { + configFile = DEFAULT_CONFIG_FILE; + } + + SpringApplication application = new SpringApplication(FederationHubServer.class); + + ignite = Ignition.getOrStart(FederationHubUtils + .getIgniteConfiguration(FederationHubConstants.FEDERATION_HUB_BROKER_IGNITE_PROFILE, true)); + if (ignite == null) { + System.exit(1); + } + + ApplicationContext context = application.run(args); + } + + @Override + public void run(String... args) throws Exception { + } + + @Bean + public FederationHubDependencyInjectionProxy dependencyProxy() { + return new FederationHubDependencyInjectionProxy(); + } + + @Bean + public FederationHubBroker federationHubBroker(Ignite ignite) { + FederationHubBrokerImpl hb = new FederationHubBrokerImpl(); + ClusterGroup cg = ignite.cluster().forAttribute(FederationHubConstants.FEDERATION_HUB_IGNITE_PROFILE_KEY, + FederationHubConstants.FEDERATION_HUB_BROKER_IGNITE_PROFILE); + ignite.services(cg).deployClusterSingleton(FederationHubConstants.FED_HUB_BROKER_SERVICE, hb); + + return hb; + } + + @Bean + public Ignite getIgnite() { + return ignite; + } + + @Bean + public FederationHubPolicyManagerProxyFactory fedHubPolicyManagerProxyFactory() { + return new FederationHubPolicyManagerProxyFactory(); + } + + @Bean + public SSLConfig getSslConfig() { + return new SSLConfig(); + } + + @Bean + public HubConnectionStore hubConnectionStore() { + return new HubConnectionStore(); + } + + @Bean + public FederationHubBrokerMetrics federationHubBrokerMetrics() { + return new FederationHubBrokerMetrics(); + } + + @Bean + public FederationHubServerConfig getFedHubConfig() throws JsonParseException, JsonMappingException, IOException { + FederationHubServerConfig config = loadConfig(configFile); + if (Strings.isNullOrEmpty(config.getId())) { + config.setId(UUID.randomUUID().toString().replace("-", "")); + saveConfig(configFile, config); + } + + return loadConfig(configFile); + } + + @Bean + @Order(Ordered.LOWEST_PRECEDENCE) + public FederationHubBrokerService FederationHubBrokerService(Ignite ignite, SSLConfig getSslConfig, + FederationHubServerConfig fedHubConfig, FederationHubPolicyManager fedHubPolicyManager, + HubConnectionStore hubConnectionStore, FederationHubMissionDisruptionManager federationHubMissionDisruptionManager, + FederationHubBrokerMetrics fedHubBrokerMetrics) { + + return new FederationHubBrokerService(ignite, getSslConfig, fedHubConfig, + fedHubPolicyManager, hubConnectionStore, federationHubMissionDisruptionManager, + fedHubBrokerMetrics); + } + + @Bean + public CacheManager cacheManager() { + Caffeine caffeine = Caffeine.newBuilder() + .initialCapacity(100) + .maximumSize(150) + .expireAfterAccess(5, TimeUnit.MINUTES) + .recordStats(); + + CaffeineCacheManager cacheManager = new CaffeineCacheManager("federate-connect-disconnect", "federate_metadata"); + cacheManager.setAllowNullValues(true); // can happen if you get a value from a @Cachable that returns null + cacheManager.setCaffeine(caffeine); + return cacheManager; + } + + @Bean + public FederationHubMissionDisruptionManager federationHubMissionDisruptionManager(FederationHubDatabaseService federationHubDatabaseService) { + return new FederationHubMissionDisruptionManager(federationHubDatabaseService); + } + + @Bean + public FederationHubDatabaseService HubDataBaseService(FederationHubDatabase federationHubDatabase, CacheManager cacheManager) { + return new FederationHubDatabaseServiceImpl(federationHubDatabase, cacheManager); + } + + @Bean + public FederationHubDatabase federationHubDatabase(FederationHubServerConfig fedHubConfig) { + return new FederationHubDatabase(fedHubConfig.getDbUsername(), fedHubConfig.getDbPassword(), + fedHubConfig.getDbHost(), fedHubConfig.getDbPort()); + } + + @Bean + public MongoTemplate mongoTemplate(FederationHubDatabase hubDatabase) throws Exception { + return new MongoTemplate(hubDatabase.getClient(), "cot"); + } + + private FederationHubServerConfig loadConfig(String configFile) + throws JsonParseException, JsonMappingException, FileNotFoundException, IOException { + if (getClass().getResource(configFile) != null) { + // It's a resource. + return new ObjectMapper(new YAMLFactory()).readValue(getClass().getResourceAsStream(configFile), + FederationHubServerConfig.class); + } + + // It's a file. + return new ObjectMapper(new YAMLFactory()).readValue(new FileInputStream(configFile), + FederationHubServerConfig.class); + } + + private void saveConfig(String configFile, FederationHubServerConfig config) + throws JsonParseException, JsonMappingException, FileNotFoundException, IOException { + + ObjectMapper om = new ObjectMapper(new YAMLFactory()); + om.writeValue(new File(configFile), config); + } +} \ No newline at end of file diff --git a/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/HubFigClient.java b/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/HubFigClient.java index 934abd37..77684093 100644 --- a/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/HubFigClient.java +++ b/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/HubFigClient.java @@ -3,12 +3,15 @@ import static io.grpc.MethodDescriptor.generateFullMethodName; import static java.util.Objects.requireNonNull; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.Serializable; +import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import java.util.Map.Entry; import java.util.Optional; import java.util.UUID; import java.util.concurrent.BlockingQueue; @@ -21,11 +24,15 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; +import org.bson.types.ObjectId; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import com.atakmap.Tak.BinaryBlob; import com.atakmap.Tak.ClientHealth; import com.atakmap.Tak.FederateGroups; import com.atakmap.Tak.FederatedChannelGrpc; @@ -34,6 +41,7 @@ import com.atakmap.Tak.FederatedEvent; import com.atakmap.Tak.Identity; import com.atakmap.Tak.ROL; +import com.atakmap.Tak.ROL.Builder; import com.atakmap.Tak.ServerHealth; import com.atakmap.Tak.ServerHealth.ServingStatus; import com.atakmap.Tak.Subscription; @@ -42,6 +50,7 @@ import com.bbn.roger.fig.Propagator; import com.google.common.collect.ComparisonChain; import com.google.common.util.concurrent.ThreadFactoryBuilder; +import com.google.protobuf.ByteString; import io.grpc.ClientCall; import io.grpc.ManagedChannel; @@ -65,6 +74,7 @@ import tak.server.federation.FederationPolicyGraph; import tak.server.federation.GuardedStreamHolder; import tak.server.federation.hub.FederationHubDependencyInjectionProxy; +import tak.server.federation.hub.broker.db.FederationHubMissionDisruptionManager; import tak.server.federation.hub.broker.events.HubClientDisconnectEvent; import tak.server.federation.hub.ui.graph.FederationOutgoingCell; @@ -75,11 +85,13 @@ */ public class HubFigClient implements Serializable { + private static final long serialVersionUID = 1L; + private static final Logger logger = LoggerFactory.getLogger(HubFigClient.class); private static final int NUM_AVAIL_CORES = Runtime.getRuntime().availableProcessors(); - private static ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); + private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); // shared executor public static final ExecutorService federationGrpcExecutor = newGrpcThreadPoolExecutor("grpc-federation-executor",1, NUM_AVAIL_CORES); @@ -106,6 +118,7 @@ private static EventLoopGroup newGrpcEventLoopGroup(String name, int maxPoolSize private FederationHubServerConfig fedHubConfig; private String host; private int port; + private X509Certificate[] sessionCerts; private String fedName; private String clientUid = UUID.randomUUID().toString().replace("-", ""); @@ -132,8 +145,11 @@ private static EventLoopGroup newGrpcEventLoopGroup(String name, int maxPoolSize private Subscription serverSubscription; private HubConnectionInfo info; - public HubFigClient(FederationHubServerConfig fedHubConfig, FederationOutgoingCell federationOutgoingCell) { + private FederationHubMissionDisruptionManager federationHubMissionDisruptionManager; + + public HubFigClient(FederationHubServerConfig fedHubConfig, FederationHubMissionDisruptionManager federationHubMissionDisruptionManager, FederationOutgoingCell federationOutgoingCell) { this.fedHubConfig = fedHubConfig; + this.federationHubMissionDisruptionManager = federationHubMissionDisruptionManager; this.host = federationOutgoingCell.getProperties().getHost(); this.port = federationOutgoingCell.getProperties().getPort(); this.fedName = federationOutgoingCell.getProperties().getOutgoingName(); @@ -192,7 +208,7 @@ public void onCompleted() {} new StreamObserver() { @Override - public void onNext(FederateGroups value) { + public void onNext(FederateGroups value) { // once the group stream is established, we are ready to setup event streaming if (value.getStreamUpdate() != null && value.getStreamUpdate().getStatus() == ServingStatus.SERVING) { setupEventStreamSender(); @@ -295,8 +311,10 @@ private ManagedChannel openFigConnection(final String host, final int port, SslC @Override public X509Certificate[] propogate(X509Certificate[] certs) { try { + sessionCerts = certs; X509Certificate clientCert = certs[0]; X509Certificate caCert = certs[1]; + String fingerprint = FederationUtils.getBytesSHA256(clientCert.getEncoded()); String issuerDN = clientCert.getIssuerX500Principal().getName(); String issuerCN = Optional.ofNullable(FederationHubBrokerImpl.getCN(issuerDN)).map(cn -> cn.toLowerCase()).orElse(""); @@ -472,7 +490,7 @@ public int compare(FederatedEvent a, FederatedEvent b) { FederateEdge edge = fpg.getEdge(otherClientNode, clientNode); if (otherClientNode != null && edge != null) { for (FederatedEvent event : otherClient.getCache()) { - if(FederationHubBrokerService.isDestinationReachableByGroupFilter(edge, event.getFederateGroupsList())) { + if (FederationHubBrokerService.isDestinationEdgeReachableByGroupFilter(edge, event.getFederateGroupsList())) { eventStreamHolder.send(event); if (logger.isDebugEnabled()) { logger.debug("Sending v2 cached " + event + @@ -524,7 +542,31 @@ public int compare(ROL a, ROL b) { }, true ); + // get the changes, but don't send till we add the rolStream because the stream will get used + // down the line for getting the session id + FederationHubMissionDisruptionManager.OfflineMissionChanges changes = null; + if (fedHubConfig.isMissionFederationDisruptionEnabled()) { + changes = federationHubMissionDisruptionManager.getMissionChangesAndTrackConnectEvent( + rolStreamHolder.getFederateIdentity().getFedId(), sessionCerts); + } + FederationHubDependencyInjectionProxy.getInstance().hubConnectionStore().addRolStream(fedName, rolStreamHolder); + + AtomicLong delayMs = new AtomicLong(5000l); + + if (changes != null) { + for(final Entry entry: changes.getResourceRols().entrySet()) { + FederationHubBrokerService.getInstance().getMfdtScheduler().schedule(() -> { + ROL rol = federationHubMissionDisruptionManager.hydrateResourceROL(entry.getKey(), entry.getValue()); + rolStreamHolder.send(rol); + }, delayMs.getAndAdd(500), TimeUnit.MILLISECONDS); + } + for(final ROL rol: changes.getRols()) { + FederationHubBrokerService.getInstance().getMfdtScheduler().schedule(() -> { + rolStreamHolder.send(rol); + }, delayMs.getAndAdd(100), TimeUnit.MILLISECONDS); + } + } } private final AtomicBoolean hasDisconnected = new AtomicBoolean(false); diff --git a/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/db/FederationHubDatabaseService.java b/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/db/FederationHubDatabaseService.java new file mode 100644 index 00000000..dce96abb --- /dev/null +++ b/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/db/FederationHubDatabaseService.java @@ -0,0 +1,25 @@ +package tak.server.federation.hub.broker.db; + +import java.security.cert.Certificate; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import org.bson.Document; +import org.bson.types.ObjectId; + +public interface FederationHubDatabaseService { + + void storeRol(Document rol); + + Document addFederateMetadata(String id, Certificate[] certificates); + Document getFederateMetadata(String id); + List getFederateMetadatas(); + + void trackFederateConnect(String id); + + ObjectId addResource(byte[] data, Map parameters); + byte[] getResource(ObjectId resourceObjectId); + + List getOfflineUpdates(String id, Date lastUpdate); +} diff --git a/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/db/FederationHubDatabaseServiceImpl.java b/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/db/FederationHubDatabaseServiceImpl.java new file mode 100644 index 00000000..7af5b37e --- /dev/null +++ b/src/federation-hub-broker/src/main/java/tak/server/federation/hub/broker/db/FederationHubDatabaseServiceImpl.java @@ -0,0 +1,289 @@ +package tak.server.federation.hub.broker.db; + +import java.io.IOException; +import java.io.InputStream; +import java.security.cert.Certificate; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import org.bson.Document; +import org.bson.conversions.Bson; +import org.bson.types.ObjectId; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.stats.CacheStats; +import com.google.common.io.ByteSource; +import com.mongodb.MongoException; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.gridfs.GridFSBucket; +import com.mongodb.client.gridfs.GridFSBuckets; +import com.mongodb.client.gridfs.GridFSDownloadStream; +import com.mongodb.client.gridfs.GridFSFindIterable; +import com.mongodb.client.gridfs.model.GridFSUploadOptions; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Sorts; +import com.mongodb.client.model.UpdateOptions; +import com.mongodb.client.model.Updates; +import com.mongodb.client.result.DeleteResult; +import com.mongodb.client.result.InsertOneResult; +import com.mongodb.client.result.UpdateResult; + +import tak.server.federation.hub.FederationHubDependencyInjectionProxy; +import tak.server.federation.hub.db.FederationHubDatabase; + +public class FederationHubDatabaseServiceImpl implements FederationHubDatabaseService { + private static final Logger logger = LoggerFactory.getLogger(FederationHubDatabaseService.class); + + private static final String FEDERATE_EVENT_COLLECTION_NAME = "federate_event"; + private static final String FEDERATE_METADATA_COLLECTION_NAME = "federate_metadata"; + private static final String FEDERATE_RESOURCES_COLLECTION_NAME = "mission_resources"; + + private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); + + private FederationHubDatabase federationHubDatabase; + private CacheManager cacheManager; + + public FederationHubDatabaseServiceImpl(FederationHubDatabase federationHubDatabase, CacheManager cacheManager) { + this.federationHubDatabase = federationHubDatabase; + this.cacheManager = cacheManager; + + scheduler.scheduleWithFixedDelay(() -> { + try { + if (!isDBConnected()) + return; + + long retentionDays = FederationHubDependencyInjectionProxy.getInstance().fedHubServerConfig().getMissionFederationDBRetentionDays(); + + // delete expired events based on received_time + Bson receivedTimeFilter = Filters.lt("received_time", new Date(Instant.now().toEpochMilli() - retentionDays*24*60*60 * 1000)); + DeleteResult eventDeleteResult = federateEventCollection().deleteMany(receivedTimeFilter); + + if (logger.isDebugEnabled()) { + logger.debug("Deleted " + eventDeleteResult.getDeletedCount() + " events from the database"); + } + + // delete expired resources based on uploadDate + Bson uploadDateFilter = Filters.lt("uploadDate", new Date(Instant.now().toEpochMilli() - retentionDays*24*60*60 * 1000)); + GridFSFindIterable filesToDelete = resourceCollection().find(uploadDateFilter); + + filesToDelete.forEach(file -> { + resourceCollection().delete(file.getObjectId()); + }); + } catch (MongoException me) { + logger.error("retention scheduler error: ", me); + } + + }, 10, 60, TimeUnit.SECONDS); + } + + @Override + public List getOfflineUpdates(String id, Date lastUpdate) { + List updates = new ArrayList<>(); + + if (!isDBConnected()) + return updates; + + try { + Bson sort = Sorts.ascending("received_time"); + Bson dateFilter = Filters.gte("received_time", lastUpdate); + Bson findFilter = Filters.eq("federate_id", id); + + updates.addAll(federateEventCollection() + .find(findFilter) + .filter(dateFilter) + .sort(sort) + .into(new ArrayList())); + } catch (MongoException me) { + logger.error("getOfflineUpdates error: ", me); + } + + return updates; + } + + @Override + public void trackFederateConnect(String id) { + if (!isDBConnected()) + return; + + try { + InsertOneResult result = federateEventCollection().insertOne(new Document() + .append("_id", new ObjectId()) + .append("federate_id", id) + .append("event_kind", "connect") + .append("event_time", Instant.now()) + .append("remote", true)); + } catch (MongoException me) { + logger.error("trackFederateConnect error: ", me); + } + } + + @Override + public void storeRol(Document rol) { + if (!isDBConnected()) + return; + + try { + InsertOneResult result = federateEventCollection().insertOne(rol); + } catch (MongoException me) { + logger.error("storeRol error: ", me); + } + } + + @Override + public ObjectId addResource(byte[] data, Map parameters) { + if (!isDBConnected()) + return null; + + try { + Document metadata = new Document(); + metadata.putAll(parameters); + InputStream streamToUploadFrom = ByteSource.wrap(data).openStream(); + GridFSUploadOptions options = new GridFSUploadOptions() + .chunkSizeBytes(1048576) + .metadata(metadata); + ObjectId fileId = resourceCollection().uploadFromStream((String) parameters.get("filename"), streamToUploadFrom, options); + return fileId; + } catch (IOException e) { + logger.error("Error writing resource", e); + } + return null; + } + + @Override + public byte[] getResource(ObjectId resourceObjectId) { + if (!isDBConnected()) + return null; + + try { + try (GridFSDownloadStream downloadStream = resourceCollection().openDownloadStream(resourceObjectId)) { + int fileLength = (int) downloadStream.getGridFSFile().getLength(); + + long maxSizeBytes = FederationHubDependencyInjectionProxy.getInstance().fedHubServerConfig().getMissionFederationDisruptionMaxFileSizeBytes(); + if (fileLength > maxSizeBytes) { + logger.info("File size of " + fileLength + " exceeds config limit of " + maxSizeBytes + "MB. Skipping " + downloadStream.getGridFSFile().getMetadata()); + return null; + } + + byte[] bytesToWriteTo = new byte[fileLength]; + downloadStream.read(bytesToWriteTo); + return bytesToWriteTo; + } + } catch (Exception e) { + logger.error("Error reading resource", e); + } + return null; + } + + @Override + @Cacheable(value = "federate_metadata", key = "{#root.args[0]}", sync = true) + public Document getFederateMetadata(String federateServerId) { + if (!isDBConnected()) + return null; + + try { + Bson filter = Filters.eq("federate_id", federateServerId); + Document result = federateMetadataCollection().find(filter).first(); + return result; + } catch (MongoException me) { + logger.error("getFederateMetadata error", me); + return null; + } + } + + @Override + public List getFederateMetadatas() { + List results = new ArrayList<>(); + + if (!isDBConnected()) + return results; + + try { + results.addAll(federateMetadataCollection().find().into(new ArrayList())); + } catch (MongoException me) { + logger.error("getFederateMetadatas error", me); + } + return results; + } + + @Override + @CachePut(value = "federate_metadata", key = "{#root.args[0]}") + public Document addFederateMetadata(String id, Certificate[] certificates) { + if (!isDBConnected()) + return null; + + try { + List binaryCerts = new ArrayList<>(); + for (int i = 1; i < certificates.length; i++) { + if (certificates[i] == null) { + break; + } + binaryCerts.add(certificates[i].getEncoded()); + } + + Bson filter = Filters.eq("federate_id", id); + Bson update = Updates.combine( + Updates.set("federate_id", id), + Updates.set("cert_array", binaryCerts), + Updates.set("last_update", Instant.now())); + + UpdateOptions options = new UpdateOptions().upsert(true); + + UpdateResult result = federateMetadataCollection().updateOne(filter, update, options); + + return federateMetadataCollection().find(filter).first(); + } catch (Exception e) { + logger.error("addFederateMetadata error", e); + return null; + } + } + + private MongoCollection federateEventCollection; + private MongoCollection federateEventCollection() { + if (federateEventCollection == null) + federateEventCollection = federationHubDatabase.getDB().getCollection(FEDERATE_EVENT_COLLECTION_NAME); + + return federateEventCollection; + } + + private MongoCollection federateMetadataCollection; + private MongoCollection federateMetadataCollection() { + if (federateMetadataCollection == null) + federateMetadataCollection = federationHubDatabase.getDB().getCollection(FEDERATE_METADATA_COLLECTION_NAME); + + return federateMetadataCollection; + } + + private GridFSBucket gridFSBucket; + private GridFSBucket resourceCollection() { + if (gridFSBucket == null) + gridFSBucket = GridFSBuckets.create(federationHubDatabase.getDB(), FEDERATE_RESOURCES_COLLECTION_NAME); + + return gridFSBucket; + } + + private CacheStats getCoffeeCacheStats(String cacheName) { + try { + org.springframework.cache.Cache cache = cacheManager.getCache(cacheName); + Cache nativeCoffeeCache = (Cache) cache.getNativeCache(); + return nativeCoffeeCache.stats(); + } catch(Exception e) { + logger.error("getCoffeeCacheStats: " + cacheName, e); + } + return null; + } + + private boolean isDBConnected() { + return federationHubDatabase.isDBConnected(); + } +} diff --git a/src/federation-hub-broker/src/main/resources/federation-hub-broker-docker.yml b/src/federation-hub-broker/src/main/resources/federation-hub-broker-docker.yml new file mode 100644 index 00000000..9d5e36e9 --- /dev/null +++ b/src/federation-hub-broker/src/main/resources/federation-hub-broker-docker.yml @@ -0,0 +1,46 @@ +keystoreType: JKS +keystoreFile: /opt/tak/federation-hub/certs/files/takserver.jks +keystorePassword: atakatak + +truststoreType: JKS +truststoreFile: /opt/tak/federation-hub/certs/files/fed-truststore.jks +truststorePassword: atakatak + +keyManagerType: SunX509 + +# v1 federation only. +v1Enabled: true +v1Port: 9101 +useEpoll: true +context: TLSv1.2 +allow128cipher: true +allowNonSuiteB: true +enableOCSP: false +tlsVersions: + - TLSv1.2 + - TLSv1.3 + +# v2 federation only. +v2Enabled: true +v2Port: 9102 +maxMessageSizeBytes: 268435456 +metricsLogIntervalSeconds: 5 +clientTimeoutTime: 15 +clientRefreshTime: 5 +enableHealthCheck: true +useCaGroups: true + +dbUsername: martiuser +dbPassword: + +dbPort: 27017 +dbHost: hub-db +dbConnectionTimeoutMS: 5000 + +# mission federation DB retention days - how long to keep mission events in the database before permanently deleting them +missionFederationDBRetentionDays: 7 +# mission federation recency seconds - how far back to look for offline changes (default 12 hours) +missionFederationRecencySeconds: 43200 +missionFederationDisruptionEnabled: false +missionFederationDisruptionMaxFileSizeBytes: 200 + diff --git a/src/federation-hub-broker/src/main/resources/federation-hub-broker.yml b/src/federation-hub-broker/src/main/resources/federation-hub-broker.yml index 107e2d69..832bffe1 100644 --- a/src/federation-hub-broker/src/main/resources/federation-hub-broker.yml +++ b/src/federation-hub-broker/src/main/resources/federation-hub-broker.yml @@ -1,9 +1,9 @@ keystoreType: JKS -keystoreFile: /opt/tak/certs/files/takserver.jks +keystoreFile: /opt/tak/federation-hub/certs/files/takserver.jks keystorePassword: atakatak truststoreType: JKS -truststoreFile: /opt/tak/certs/files/fed-truststore.jks +truststoreFile: /opt/tak/federation-hub/certs/files/fed-truststore.jks truststorePassword: atakatak keyManagerType: SunX509 @@ -23,9 +23,24 @@ tlsVersions: # v2 federation only. v2Enabled: true v2Port: 9102 -maxMessageSizeBytes: 67108864 +maxMessageSizeBytes: 268435456 metricsLogIntervalSeconds: 5 clientTimeoutTime: 15 clientRefreshTime: 5 enableHealthCheck: true useCaGroups: true + +dbUsername: martiuser +dbPassword: + +dbPort: 27017 +dbHost: localhost +dbConnectionTimeoutMS: 5000 + +# mission federation DB retention days - how long to keep mission events in the database before permanently deleting them +missionFederationDBRetentionDays: 7 +# mission federation recency seconds - how far back to look for offline changes (default 12 hours) +missionFederationRecencySeconds: 43200 +missionFederationDisruptionEnabled: false +missionFederationDisruptionMaxFileSizeBytes: 200 + diff --git a/src/federation-hub-policy/build.gradle b/src/federation-hub-policy/build.gradle index a2cca44f..42c79ffa 100644 --- a/src/federation-hub-policy/build.gradle +++ b/src/federation-hub-policy/build.gradle @@ -11,7 +11,6 @@ jar { apply plugin: 'eclipse' apply plugin: 'idea' apply plugin: 'org.springframework.boot' -apply plugin: 'io.spring.dependency-management' jar { enabled = false @@ -36,7 +35,6 @@ dependencies { implementation group: 'xerces', name: 'xercesImpl', version: xerces_version implementation group: 'org.slf4j', name: 'slf4j-api', version: slf4j_version - // exclude log4j from fed common implementation(project(':federation-common')) { exclude group: 'log4j', module: 'log4j' exclude group: 'org.apache.logging.log4j' @@ -50,13 +48,13 @@ dependencies { implementation group: 'org.apache.ignite', name: 'ignite-kubernetes', version: ignite_version implementation group: 'org.apache.ignite', name: 'ignite-slf4j', version: ignite_version - implementation group: 'org.springframework.boot', name: 'spring-boot-starter-actuator' + implementation group: 'org.springframework.boot', name: 'spring-boot-starter-actuator', version: spring_boot_version implementation group: 'org.springframework.boot', name: 'spring-boot-loader', version: spring_boot_version - implementation group: 'org.springframework', name: 'spring-context' + implementation group: 'org.springframework', name: 'spring-context', version: spring_version testImplementation group: 'junit', name: 'junit', version: junit_version testImplementation group: 'org.mockito', name: 'mockito-core', version: mockito_version - testImplementation("org.springframework.boot:spring-boot-starter-test") { + testImplementation("org.springframework.boot:spring-boot-starter-test:$spring_boot_version") { exclude group: "com.vaadin.external.google", module:"android-json" } diff --git a/src/federation-hub-policy/scripts/federation-hub-policy.sh b/src/federation-hub-policy/scripts/federation-hub-policy.sh index 361ce6fa..b4dcfdec 100755 --- a/src/federation-hub-policy/scripts/federation-hub-policy.sh +++ b/src/federation-hub-policy/scripts/federation-hub-policy.sh @@ -3,4 +3,12 @@ export FEDERATION_HUB=/opt/tak/federation-hub export JDK_JAVA_OPTIONS="-Dio.netty.tmpdir=/opt/tak -Djava.io.tmpdir=/opt/tak -Dio.netty.native.workdir=/opt/tak -DIGNITE_UPDATE_NOTIFIER=false" -java -Dlogging.config=${FEDERATION_HUB}/configs/logback-policy.xml -jar ${FEDERATION_HUB}/jars/federation-hub-policy.jar +# get total RAM +TOTALRAMBYTES=`awk '/MemTotal/ {print $2}' /proc/meminfo` + +# set POLICY max if not set already +if [ -z "$POLICY_MAX_HEAP" ]; then + export POLICY_MAX_HEAP=$(($TOTALRAMBYTES / 1000 / 100 * 15)) +fi + +java -Xmx${POLICY_MAX_HEAP}m -Dlogging.config=${FEDERATION_HUB}/configs/logback-policy.xml -jar ${FEDERATION_HUB}/jars/federation-hub-policy.jar diff --git a/src/federation-hub-policy/src/main/java/tak/server/federation/hub/policy/FederationHubPolicyManagerService.java b/src/federation-hub-policy/src/main/java/tak/server/federation/hub/policy/FederationHubPolicyManagerService.java index d7fed286..d3aded3e 100644 --- a/src/federation-hub-policy/src/main/java/tak/server/federation/hub/policy/FederationHubPolicyManagerService.java +++ b/src/federation-hub-policy/src/main/java/tak/server/federation/hub/policy/FederationHubPolicyManagerService.java @@ -8,6 +8,8 @@ import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration; +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; @@ -18,7 +20,7 @@ import tak.server.federation.hub.broker.FederationHubBroker; import tak.server.federation.hub.broker.FederationHubBrokerProxyFactory; -@SpringBootApplication +@SpringBootApplication(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class}) public class FederationHubPolicyManagerService implements CommandLineRunner { private static final Logger logger = LoggerFactory.getLogger(FederationHubPolicyManagerService.class); diff --git a/src/federation-hub-ui/build.gradle b/src/federation-hub-ui/build.gradle index 437018b3..610f7d2f 100644 --- a/src/federation-hub-ui/build.gradle +++ b/src/federation-hub-ui/build.gradle @@ -11,7 +11,6 @@ jar { apply plugin: 'eclipse' apply plugin: 'idea' apply plugin: 'org.springframework.boot' -apply plugin: 'io.spring.dependency-management' apply plugin: 'war' @@ -75,14 +74,8 @@ dependencies { implementation group: 'org.springframework.security', name: 'spring-security-config', version: spring_security_version implementation group: 'org.springframework.security', name: 'spring-security-messaging', version: spring_security_version implementation group: 'org.springframework.security', name: 'spring-security-web', version: spring_security_version - - api group: 'org.springframework.security.oauth', name: 'spring-security-oauth2', version: spring_security_oauth_version - - implementation ("org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:$spring_security_oauth2_autoconfigure_version") { - exclude group: 'log4j', module: 'log4j' - exclude group: 'org.apache.logging.log4j' - exclude group: 'org.slf4j', module: 'log4j-over-slf4j' - } + implementation group: 'org.springframework.security', name: 'spring-security-oauth2-authorization-server', version: spring_security_oauth2_authorization_server_version + implementation group: 'org.springframework.security', name: 'spring-security-oauth2-core', version: spring_security_version implementation(project(':federation-common')) { exclude group: 'log4j', module: 'log4j' @@ -101,28 +94,35 @@ dependencies { implementation group: 'org.apache.ignite', name: 'ignite-kubernetes', version: ignite_version implementation group: 'org.apache.ignite', name: 'ignite-slf4j', version: ignite_version - implementation group: 'org.springframework.boot', name: 'spring-boot-starter-actuator' + implementation group: 'org.springframework.boot', name: 'spring-boot-starter-actuator', version: spring_boot_version + implementation group: 'io.micrometer', name: 'micrometer-core', version: micrometer_version implementation ("org.springframework.boot:spring-boot-loader:$spring_boot_version") { exclude group: 'log4j', module: 'log4j' exclude group: 'org.apache.logging.log4j', module: 'log4j-to-slf4j' } - implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: spring_boot_version implementation group: 'org.springframework.boot', name: 'spring-boot-starter-jetty', version: spring_boot_version implementation group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: spring_boot_version implementation group: 'org.springframework', name: 'spring-web', version: spring_version + + implementation group: 'org.eclipse.jetty', name: 'jetty-server', version: jetty_server_version + implementation group: 'jakarta.servlet', name: 'jakarta.servlet-api', version: jakarta_servlet_api_version testImplementation group: 'junit', name: 'junit', version: junit_version testImplementation group: 'org.mockito', name: 'mockito-core', version: mockito_version - testImplementation("org.springframework.boot:spring-boot-starter-test") { + testImplementation("org.springframework.boot:spring-boot-starter-test:$spring_boot_version") { exclude group: "com.vaadin.external.google", module:"android-json" } - implementation group: 'commons-fileupload', name: 'commons-fileupload', version: commons_fileupload_version + //implementation group: 'commons-fileupload', name: 'commons-fileupload', version: commons_fileupload_version implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: okhttp3_version testImplementation group: 'ch.qos.logback', name: 'logback-classic', version: logback_version + + // This one is used in MartiValidator. We should find a way to remove this and use jakarta.servlet instead + compileOnly group: 'javax.servlet', name: 'servlet-api', version: javax_servlet_version + } compileJava { diff --git a/src/federation-hub-ui/scripts/federation-hub-ui.sh b/src/federation-hub-ui/scripts/federation-hub-ui.sh index 7dd35c6a..fcb31480 100755 --- a/src/federation-hub-ui/scripts/federation-hub-ui.sh +++ b/src/federation-hub-ui/scripts/federation-hub-ui.sh @@ -3,4 +3,12 @@ export FEDERATION_HUB=/opt/tak/federation-hub export JDK_JAVA_OPTIONS="-Dio.netty.tmpdir=/opt/tak -Djava.io.tmpdir=/opt/tak -Dio.netty.native.workdir=/opt/tak -DIGNITE_UPDATE_NOTIFIER=false" -java -Dlogging.config=${FEDERATION_HUB}/configs/logback-ui.xml -jar ${FEDERATION_HUB}/jars/federation-hub-ui.war "$@" +# get total RAM +TOTALRAMBYTES=`awk '/MemTotal/ {print $2}' /proc/meminfo` + +# set UI max if not set already +if [ -z "$UI_MAX_HEAP" ]; then + export UI_MAX_HEAP=$(($TOTALRAMBYTES / 1000 / 100 * 25)) +fi + +java -Xmx${UI_MAX_HEAP}m -Dlogging.config=${FEDERATION_HUB}/configs/logback-ui.xml -jar ${FEDERATION_HUB}/jars/federation-hub-ui.war "$@" diff --git a/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/FederationHubBrokerMetricsPoller.java b/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/FederationHubBrokerMetricsPoller.java new file mode 100644 index 00000000..ffa37019 --- /dev/null +++ b/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/FederationHubBrokerMetricsPoller.java @@ -0,0 +1,62 @@ +package tak.server.federation.hub.ui; + +import java.util.ArrayList; +import java.util.Map; +import java.util.concurrent.*; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.Tag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.micrometer.core.instrument.Metrics; +import org.springframework.beans.factory.DisposableBean; +import tak.server.federation.hub.broker.FederationHubBroker; +import tak.server.federation.hub.broker.FederationHubBrokerMetrics; +import tak.server.federation.hub.broker.FederationHubBrokerMetrics.ChannelInfo; + +public class FederationHubBrokerMetricsPoller implements DisposableBean { + + private static final Logger logger = LoggerFactory.getLogger(FederationHubBrokerMetricsPoller.class); + + private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); + + private FederationHubBroker fedHubBroker; + + private final ScheduledFuture scheduleFuture; + + public FederationHubBrokerMetricsPoller(FederationHubBroker fedHubBroker) { + this.fedHubBroker = fedHubBroker; + logger.info("Initializing federation hub broker metrics poller."); + + // scheduleAtFixedRate will run every x seconds no matter what + // scheduleWithFixedDelay will run every x seconds starting AFTER the logic has completed + scheduleFuture = scheduler.scheduleWithFixedDelay(() -> { + + FederationHubBrokerMetrics latestBrokerMetrics = fedHubBroker.getFederationHubBrokerMetrics(); + + ConcurrentHashMap> channelWriteCounters = latestBrokerMetrics.getChannelInfosInternal(); + for (Map.Entry> senderEntries : channelWriteCounters.entrySet()) { + String source = senderEntries.getKey(); + ConcurrentHashMap receiverCounters = senderEntries.getValue(); + for (String target : receiverCounters.keySet()) { + long writtenCount = receiverCounters.get(target).messagesWritten; + ArrayList tags = new ArrayList<>(); + tags.add(Tag.of("source", source)); + tags.add(Tag.of("target", target)); + Counter counter = Metrics.counter("messages.written", tags); + counter.increment(writtenCount - counter.count()); + } + } + + + }, 1, 1, TimeUnit.SECONDS); + + } + + @Override + public void destroy() throws Exception { + // to stop + scheduleFuture.cancel(true); + } +} \ No newline at end of file diff --git a/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/FederationHubUIServer.java b/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/FederationHubUIServer.java index 97e84d75..41264fa8 100644 --- a/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/FederationHubUIServer.java +++ b/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/FederationHubUIServer.java @@ -23,14 +23,14 @@ import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration; +import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration; import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer; import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; import org.springframework.boot.web.server.ErrorPage; import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; -import org.springframework.core.Ordered; -import org.springframework.core.annotation.Order; import org.springframework.http.HttpStatus; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @@ -42,12 +42,13 @@ import tak.server.federation.hub.FederationHubConstants; import tak.server.federation.hub.FederationHubUtils; +import tak.server.federation.hub.broker.FederationHubBroker; import tak.server.federation.hub.broker.FederationHubBrokerProxyFactory; import tak.server.federation.hub.policy.FederationHubPolicyManagerProxyFactory; import tak.server.federation.hub.ui.keycloak.KeycloakTokenParser; import tak.server.federation.hub.ui.manage.AuthorizationFileWatcher; -@SpringBootApplication +@SpringBootApplication(exclude = {MongoAutoConfiguration.class, MongoDataAutoConfiguration.class}) public class FederationHubUIServer { private static final String DEFAULT_CONFIG_FILE = "/opt/tak/federation-hub/configs/federation-hub-ui.yml"; @@ -58,7 +59,6 @@ public class FederationHubUIServer { private static String configFile; - public static void main(String[] args) { if (args.length > 1) { System.err.println("Usage: java -jar federation-hub-ui.jar [CONFIG_FILE_PATH]"); @@ -107,7 +107,7 @@ public FederationHubBrokerProxyFactory fedHubBrokerProxyFactory() { public FederationHubPolicyManagerProxyFactory fedHubPolicyManagerProxyFactory() { return new FederationHubPolicyManagerProxyFactory(); } - + private void makeConnector(FederationHubUIConfig fedHubConfig, Server server, int port, boolean clientAuth) { HttpConfiguration httpConfig = new HttpConfiguration(); httpConfig.setSecureScheme("https"); @@ -118,7 +118,7 @@ private void makeConnector(FederationHubUIConfig fedHubConfig, Server server, in httpConfig.setSendServerVersion(true); httpConfig.setSendDateHeader(false); - SslContextFactory sslContextFactory = new SslContextFactory.Server(); + SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); sslContextFactory.setKeyStorePath(fedHubConfig.getKeystoreFile()); sslContextFactory.setKeyStorePassword(fedHubConfig.getKeystorePassword()); sslContextFactory.setKeyStoreType(fedHubConfig.getKeystoreType()); @@ -131,7 +131,9 @@ private void makeConnector(FederationHubUIConfig fedHubConfig, Server server, in // SSL HTTP Configuration HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig); - httpsConfig.addCustomizer(new SecureRequestCustomizer()); + SecureRequestCustomizer src = new SecureRequestCustomizer(); + src.setSniHostCheck(false); + httpsConfig.addCustomizer(src); // SSL Connector ServerConnector sslConnector = new ServerConnector(server, @@ -144,9 +146,11 @@ private void makeConnector(FederationHubUIConfig fedHubConfig, Server server, in @Bean public ConfigurableServletWebServerFactory jettyServletFactory(FederationHubUIConfig fedHubConfig) { JettyServletWebServerFactory factory = new JettyServletWebServerFactory(); - + factory.addServerCustomizers(new JettyServerCustomizer() { + + @Override public void customize(Server server) { try { @@ -154,9 +158,9 @@ public void customize(Server server) { logger.info("Stopping default Jetty Connector: " + defaultConnector); server.getConnectors()[0].stop(); } catch (Exception e) {} - + makeConnector(fedHubConfig, server, fedHubConfig.getPort(), true); - + if (fedHubConfig.isAllowOauth() && Strings.isNotEmpty(fedHubConfig.getKeycloakAccessTokenName()) && Strings.isNotEmpty(fedHubConfig.getKeycloakAdminClaimValue()) && Strings.isNotEmpty(fedHubConfig.getKeycloakAuthEndpoint()) && @@ -170,11 +174,11 @@ public void customize(Server server) { Strings.isNotEmpty(fedHubConfig.getKeycloakTokenEndpoint())) makeConnector(fedHubConfig, server, fedHubConfig.getOauthPort(), false); } - + }); - + factory.addErrorPages(new ErrorPage(HttpStatus.UNAUTHORIZED, "/login"), new ErrorPage(HttpStatus.FORBIDDEN, "/login")); - + return factory; } @@ -196,11 +200,11 @@ public FederationHubUIConfig getFedHubConfig() throws JsonParseException, JsonMappingException, IOException { return loadConfig(configFile); } - + @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); - } + } @Bean public AuthorizationFileWatcher authFileWatcher(FederationHubUIConfig fedHubConfig) { @@ -214,15 +218,14 @@ public AuthorizationFileWatcher authFileWatcher(FederationHubUIConfig fedHubConf Runtime.getRuntime().addShutdownHook(new Thread(authFileWatcher::stop)); return authFileWatcher; } - + @Bean public KeycloakTokenParser keycloakTokenParser(FederationHubUIConfig getFedHubConfig) { return new KeycloakTokenParser(getFedHubConfig); } @Bean - @Order(Ordered.LOWEST_PRECEDENCE) - public FederationHubUIService federationHubUIService() { - return new FederationHubUIService(); + public FederationHubBrokerMetricsPoller federationHubBrokerMetricsPoller(FederationHubBroker fedHubBroker) { + return new FederationHubBrokerMetricsPoller(fedHubBroker); } -} +} \ No newline at end of file diff --git a/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/FederationHubUIService.java b/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/FederationHubUIService.java index 7dcac895..003bcfe7 100644 --- a/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/FederationHubUIService.java +++ b/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/FederationHubUIService.java @@ -16,25 +16,23 @@ import java.util.List; import java.util.Map; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - import org.owasp.esapi.Validator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseCookie; import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.User; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -47,6 +45,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.view.InternalResourceView; @@ -57,10 +56,13 @@ import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import com.google.common.base.Strings; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import tak.server.federation.FederateGroup; import tak.server.federation.FederationException; import tak.server.federation.FederationPolicyGraph; import tak.server.federation.hub.broker.FederationHubBroker; +import tak.server.federation.hub.broker.FederationHubBrokerMetrics; import tak.server.federation.hub.broker.HubConnectionInfo; import tak.server.federation.hub.policy.FederationHubPolicyManager; import tak.server.federation.hub.policy.FederationPolicy; @@ -75,30 +77,36 @@ import tak.server.federation.hub.ui.keycloak.AuthCookieUtils; @RequestMapping("/") -public class FederationHubUIService { +@RestController +@Order(Ordered.LOWEST_PRECEDENCE) +public class FederationHubUIService implements ApplicationListener { private static final Logger logger = LoggerFactory.getLogger(FederationHubUIService.class); - + private static final int CERT_FILE_UPLOAD_SIZE = 1048576; private String activePolicyName = null; private Map cachedPolicies = new HashMap<>(); private Validator validator = new MartiValidator(); - @Autowired private FederationHubPolicyManager fedHubPolicyManager; - @Autowired private FederationHubBroker fedHubBroker; - + @Autowired AuthenticationManager authManager; - + @Autowired JwtTokenUtil jwtUtil; - + @Autowired FederationHubUIConfig fedHubConfig; + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + // hydrate cache + getActivePolicy(); + } + @RequestMapping(value = "/login", method = RequestMethod.GET) public ModelAndView getLoginPage() { return new ModelAndView(new InternalResourceView("/login/index.html")); @@ -106,21 +114,21 @@ public ModelAndView getLoginPage() { @RequestMapping(value = "/login/authserver", method = RequestMethod.GET) public ResponseEntity getAuthServerName() { - + if (fedHubConfig.isAllowOauth()) { return new ResponseEntity<>("{\"data\":\"" + fedHubConfig.getKeycloakServerName() + "\"}", new HttpHeaders(), HttpStatus.OK); } else { return new ResponseEntity<>(null, new HttpHeaders(), HttpStatus.NOT_FOUND); } } - + @RequestMapping(value = "/login/auth", method = RequestMethod.GET) public void handleAuthRequest(HttpServletResponse response) { try { // get the auth server config - if (!fedHubConfig.isAllowOauth() || - Strings.isNullOrEmpty(fedHubConfig.getKeycloakAuthEndpoint()) || - Strings.isNullOrEmpty(fedHubConfig.getKeycloakTokenEndpoint()) || + if (!fedHubConfig.isAllowOauth() || + Strings.isNullOrEmpty(fedHubConfig.getKeycloakAuthEndpoint()) || + Strings.isNullOrEmpty(fedHubConfig.getKeycloakTokenEndpoint()) || Strings.isNullOrEmpty(fedHubConfig.getKeycloakrRedirectUri())) { throw new IllegalStateException("missing auth server config"); } @@ -157,7 +165,7 @@ public void handleAuthRequest(HttpServletResponse response) { response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } } - + @RequestMapping(value = "/login/redirect", method = RequestMethod.GET) public ModelAndView handleRedirect( @RequestParam(value = "code", required = true) String code, @@ -206,7 +214,7 @@ public ModelAndView handleRedirect( return null; } } - + // TODO use this if we ever support plain username + password auth from a db or file @PostMapping("/oauth/token") public ResponseEntity login(@RequestBody AuthRequest request) { @@ -214,23 +222,23 @@ public ResponseEntity login(@RequestBody AuthRequest request) { Authentication authentication = authManager.authenticate( new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()) ); - + User user = (User) authentication.getPrincipal(); String accessToken = jwtUtil.generateAccessToken(request.getUsername()); AuthResponse authResponse = new AuthResponse(user.getUsername(), accessToken); - + ResponseCookie.ResponseCookieBuilder responseCookieBuilder = ResponseCookie .from("hubState", authResponse.getAccessToken()) .secure(true) .httpOnly(true) .path("/") .maxAge(86400); - + HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.set(HttpHeaders.SET_COOKIE, responseCookieBuilder.build().toString()); - + return ResponseEntity.ok().headers(responseHeaders).body(authResponse); - + } catch (BadCredentialsException ex) { logger.error("Bad credentials", ex); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); @@ -241,12 +249,6 @@ public ResponseEntity login(@RequestBody AuthRequest request) { } } - - @RequestMapping(value = "/home", method = RequestMethod.GET) - public String getHome() { - return "home"; - } - @RequestMapping(value = "/fig/saveFederation", method = RequestMethod.POST) @ResponseBody public FederationPolicyModel saveFederation(RequestEntity requestEntity) { @@ -254,7 +256,7 @@ public FederationPolicyModel saveFederation(RequestEntity if (policy != null) { this.cachedPolicies.put(policy.getName(), policy); } - + return policy; } @@ -327,7 +329,7 @@ public ResponseEntity updateFederationManager(@PathVariable String federat fedHubPolicyManager.setPolicyGraph(policyModel, null); activePolicyName = policyModel.getName(); } - + fedHubBroker.updatePolicy(policyModel); return new ResponseEntity<>(new HttpHeaders(), HttpStatus.OK); @@ -356,9 +358,9 @@ public ResponseEntity updateFederationManagerAndFile(@PathVariable String policyModel.getFederationPolicyObjectFromModel()); activePolicyName = policyModel.getName(); } - + fedHubBroker.updatePolicy(policyModel); - + return new ResponseEntity<>(new HttpHeaders(), HttpStatus.OK); } catch (FederationException e) { logger.error("Could not save the policy graph to the federation manager", e); @@ -371,7 +373,7 @@ public ResponseEntity updateFederationManagerAndFile(@PathVariable String } @RequestMapping(value = "/fig/getKnownCaGroups", method = RequestMethod.GET) - public ResponseEntity> getKnownCaGroups(HttpServletRequest request) { + public ResponseEntity> getKnownCaGroups(HttpServletRequest request) { if (isUpdateActiveFederation()) { return new ResponseEntity<>( federateGroupsToGroupHolders(fedHubPolicyManager.getCaGroups()), @@ -380,12 +382,12 @@ public ResponseEntity> getKnownCaGroups(HttpServletRequest req } return new ResponseEntity<>(new HttpHeaders(), HttpStatus.FORBIDDEN); } - + @RequestMapping(value = "/fig/getKnownGroupsForGraphNode/{graphNodeId}", method = RequestMethod.GET) @ResponseBody public ResponseEntity> getKnownGroupsForGraphNode(@PathVariable String graphNodeId) { List groupsForNode = new ArrayList<>(); - + FederationPolicyModel activePolicy = getActivePolicyAsPolicyModel(); activePolicy.getCells().forEach(cell -> { if (cell.getId().equals(graphNodeId)) { @@ -403,15 +405,20 @@ public ResponseEntity> getKnownGroupsForGraphNode(@PathVariable Str } } }); - + return new ResponseEntity>(groupsForNode, new HttpHeaders(), HttpStatus.OK); } - + @RequestMapping(value = "/fig/getActiveConnections", method = RequestMethod.GET) public ResponseEntity> getActiveConnections() { return new ResponseEntity>(fedHubBroker.getActiveConnections(), new HttpHeaders(), HttpStatus.OK); } + @RequestMapping(value = "/fig/getBrokerMetrics", method = RequestMethod.GET) + public ResponseEntity getFederationHubBrokerMetrics() { + return new ResponseEntity(fedHubBroker.getFederationHubBrokerMetrics(), new HttpHeaders(), HttpStatus.OK); + } + @RequestMapping(value = "/fig/addNewGroupCa", method = RequestMethod.POST) public ResponseEntity addNewGroupCa(@RequestPart("file") MultipartFile certificateFile) { if (isUpdateActiveFederation()) { @@ -436,7 +443,7 @@ public ResponseEntity addNewGroupCa(@RequestPart("file") MultipartFile cer } return new ResponseEntity<>(new HttpHeaders(), HttpStatus.FORBIDDEN); } - + @RequestMapping(value = "/fig/deleteGroupCa/{uid}", method = RequestMethod.DELETE) public ResponseEntity deleteGroupCa(@PathVariable("uid") String uid) { try { @@ -454,13 +461,13 @@ public ResponseEntity deleteGroupCa(@PathVariable("uid") String uid) { return new ResponseEntity<>(new HttpHeaders(), HttpStatus.BAD_REQUEST); } } - + private String sha256(String input) throws UnsupportedEncodingException, NoSuchAlgorithmException { byte[] bytes = input.getBytes("US-ASCII"); MessageDigest md = MessageDigest.getInstance("SHA-256"); return Base64.getUrlEncoder().withoutPadding().encodeToString(md.digest(bytes)); } - + private boolean isUpdateActiveFederation() { /* TODO do we really need to configure this? */ return true; @@ -474,8 +481,8 @@ private FederationPolicyModel getActivePolicyAsPolicyModel() { // Production code. FederationPolicyGraph activePolicy = fedHubPolicyManager.getPolicyGraph(); Map additionalData = activePolicy.getAdditionalData(); - - + + // UI Test code. //FederationPolicyGraph testActualPolicy = cachedPolicys.values().iterator().next().getPolicyGraphFromModel(); //Map additionalData = testActualPolicy.getAdditionalData(); @@ -484,12 +491,49 @@ private FederationPolicyModel getActivePolicyAsPolicyModel() { return mapper.convertValue(additionalData.get("uiData"), FederationPolicyModel.class); } - private List federateGroupsToGroupHolders(Collection groups) { - List groupList = new LinkedList<>(); + private List federateGroupsToGroupHolders(Collection groups) { + Map cas = fedHubBroker.getCAsFromFile(); + List groupList = new LinkedList<>(); for (FederateGroup group : groups) { - GroupHolder groupHolder = new GroupHolder(group.getFederateIdentity().getFedId()); - groupList.add(groupHolder); + String fedId = group.getFederateIdentity().getFedId(); + + RemoteGroup remoteGroup = new RemoteGroup(); + remoteGroup.setUid(fedId); + if (cas.containsKey(fedId)) { + X509Certificate ca = cas.get(fedId); + remoteGroup.setIssuer(ca.getIssuerX500Principal().getName()); + remoteGroup.setSubject(ca.getSubjectX500Principal().getName()); + } + + groupList.add(remoteGroup); } return groupList; } -} + + private class RemoteGroup { + private String uid; + private String issuer; + private String subject; + + public String getUid() { + return uid; + } + public void setUid(String uid) { + this.uid = uid; + } + public String getIssuer() { + return issuer; + } + public void setIssuer(String issuer) { + this.issuer = issuer; + } + public String getSubject() { + return subject; + } + public void setSubject(String subject) { + this.subject = subject; + } + + + } +} \ No newline at end of file diff --git a/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/JwtTokenFilter.java b/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/JwtTokenFilter.java index fd1778cd..c072e769 100644 --- a/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/JwtTokenFilter.java +++ b/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/JwtTokenFilter.java @@ -5,11 +5,11 @@ import java.util.Collection; import java.util.List; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.json.simple.parser.ParseException; import org.slf4j.Logger; @@ -22,7 +22,7 @@ import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; +import org.springframework.security.oauth2.server.resource.InvalidBearerTokenException; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; import org.springframework.util.Assert; @@ -85,7 +85,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse String refreshToken = (String) request.getSession().getAttribute(fedHubConfig.getKeycloakRefreshTokenName()); if (Strings.isNullOrEmpty(refreshToken)) { SecurityContextHolder.clearContext(); - AuthCookieUtils.logout(request, response, null); + AuthCookieUtils.logout(request, response); return; } @@ -106,7 +106,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse } if (claims == null) { - throw new InvalidTokenException("Unable to parse claims from token : " + cookieToken); + throw new InvalidBearerTokenException("Unable to parse claims from token : " + cookieToken); } // make sure the keycloak token has an email and contains the admin claim diff --git a/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/JwtTokenUtil.java b/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/JwtTokenUtil.java index 560e53f1..a744e5ab 100644 --- a/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/JwtTokenUtil.java +++ b/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/JwtTokenUtil.java @@ -19,8 +19,8 @@ import java.util.Enumeration; import javax.crypto.spec.SecretKeySpec; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; @@ -35,7 +35,7 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.client.OkHttp3ClientHttpRequestFactory; -import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.server.authorization.OAuth2TokenType; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; @@ -148,7 +148,7 @@ public String processAuthServerRequest( // store the access token in a cookie String access_token = (String)tokenJson.get(fedHubConfig.getKeycloakAccessTokenName()); response.addHeader(HttpHeaders.SET_COOKIE, AuthCookieUtils.createCookie( - OAuth2AccessToken.ACCESS_TOKEN, access_token, -1, true).toString()); + OAuth2TokenType.ACCESS_TOKEN.getValue(), access_token, -1, true).toString()); // store the refresh token in the session, if we have one if (tokenJson.containsKey(fedHubConfig.getKeycloakRefreshTokenName())) { diff --git a/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/SecurityConfig.java b/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/SecurityConfig.java index 094e9ffe..431b4b75 100644 --- a/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/SecurityConfig.java +++ b/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/SecurityConfig.java @@ -5,36 +5,39 @@ import java.security.cert.X509Certificate; import javax.naming.InvalidNameException; -import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.userdetails.AuthenticationUserDetailsService; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; +import jakarta.servlet.http.HttpServletResponse; import tak.server.federation.hub.ui.jwt.UserService; import tak.server.federation.hub.ui.manage.AuthManager; import tak.server.federation.hub.ui.manage.AuthorizationFileWatcher; +@Configuration @EnableWebSecurity -public class SecurityConfig extends WebSecurityConfigurerAdapter { +public class SecurityConfig { private static final Logger logger = LoggerFactory.getLogger(SecurityConfig.class); - @Autowired - private UserService userService; +// @Autowired +// private UserService userService; @Autowired private JwtTokenFilter jwtTokenFilter; @@ -42,22 +45,33 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private AuthorizationFileWatcher authFileWatcher; - @Override - protected void configure(AuthenticationManagerBuilder auth) throws Exception { - auth.userDetailsService(username -> { - return userService.loadUserByUsername(username); - }); +// @Override +// protected void configure(AuthenticationManagerBuilder auth) throws Exception { +// auth.userDetailsService(username -> { +// return userService.loadUserByUsername(username); +// }); +// } + + @Bean + public AuthenticationManager authenticationManager(UserService userService) { + + DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); + authenticationProvider.setUserDetailsService(userService); + return new ProviderManager(authenticationProvider); } - @Override - protected void configure(HttpSecurity http) throws Exception { + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + http.csrf().disable(); http.x509().authenticationUserDetailsService(new X509AuthenticatedUserDetailsService()); http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS); - http.authorizeRequests().antMatchers("/error","/oauth/**", "/login**", "/login/**", "/bowerDependencies/**", "/favicon.ico").permitAll() +// http.authorizeRequests().antMatchers("/error","/oauth/**", "/login**", "/login/**", "/bowerDependencies/**", "/favicon.ico").permitAll() +// .anyRequest().authenticated(); + http.authorizeHttpRequests().requestMatchers("/error","/oauth/**", "/login**", "/login/**", "/bowerDependencies/**", "/favicon.ico").permitAll() .anyRequest().authenticated(); http.exceptionHandling().authenticationEntryPoint((request, response, ex) -> { @@ -65,14 +79,10 @@ protected void configure(HttpSecurity http) throws Exception { }); http.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class); + + return http.build(); } - - @Override - @Bean - public AuthenticationManager authenticationManagerBean() throws Exception { - return super.authenticationManagerBean(); - } - + private class X509AuthenticatedUserDetailsService implements AuthenticationUserDetailsService { @Override diff --git a/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/WebMvcConfig.java b/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/WebMvcConfig.java index ee086ee8..12ef4a6c 100644 --- a/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/WebMvcConfig.java +++ b/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/WebMvcConfig.java @@ -3,8 +3,11 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; -import org.springframework.web.multipart.commons.CommonsMultipartResolver; +import org.springframework.web.multipart.MultipartResolver; +//import org.springframework.web.multipart.commons.CommonsMultipartResolver; +import org.springframework.web.multipart.support.StandardServletMultipartResolver; import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.ViewResolverRegistry; @@ -16,6 +19,11 @@ public class WebMvcConfig implements WebMvcConfigurer { private int maxUploadSize = 5 * 1024 * 1024; // 5 MB. + @Override + public void configurePathMatch(PathMatchConfigurer configurer) { + configurer.setUseTrailingSlashMatch(true); + } + @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("forward:index.html"); @@ -32,9 +40,9 @@ public void addResourceHandlers(ResourceHandlerRegistry registry) { } @Bean(name="multipartResolver") - public CommonsMultipartResolver multipartResolver() { - CommonsMultipartResolver resolver = new CommonsMultipartResolver(); - resolver.setMaxUploadSize(maxUploadSize); + public MultipartResolver multipartResolver() { + StandardServletMultipartResolver resolver = new StandardServletMultipartResolver(); + // resolver.setMaxUploadSize(maxUploadSize); // FIXME: return resolver; } } diff --git a/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/keycloak/AuthCookieUtils.java b/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/keycloak/AuthCookieUtils.java index 85fbc000..8e634455 100644 --- a/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/keycloak/AuthCookieUtils.java +++ b/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/keycloak/AuthCookieUtils.java @@ -1,9 +1,9 @@ package tak.server.federation.hub.ui.keycloak; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; import org.owasp.esapi.Validator; import org.owasp.esapi.errors.ValidationException; @@ -11,8 +11,7 @@ import org.slf4j.LoggerFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseCookie; -import org.springframework.security.oauth2.common.OAuth2AccessToken; -import org.springframework.security.oauth2.provider.token.DefaultTokenServices; +import org.springframework.security.oauth2.server.authorization.OAuth2TokenType; import tak.server.federation.hub.ui.MartiValidator; import tak.server.federation.hub.ui.MartiValidator.Regex; @@ -56,7 +55,7 @@ public static ResponseCookie createCookie(final String name, final String value, return createCookie(name, value, maxAge, sameSiteStrict, "/"); } - public static void logout(HttpServletRequest request, HttpServletResponse response, DefaultTokenServices defaultTokenServices) { + public static void logout(HttpServletRequest request, HttpServletResponse response) { Cookie[] cookies = request.getCookies(); if (cookies == null) { return; @@ -68,19 +67,15 @@ public static void logout(HttpServletRequest request, HttpServletResponse respon } for (Cookie cookie : cookies) { - if (cookie.getName().compareToIgnoreCase(OAuth2AccessToken.ACCESS_TOKEN) == 0) { + if (cookie.getName().compareToIgnoreCase(OAuth2TokenType.ACCESS_TOKEN.getValue()) == 0) { String token = cookie.getValue(); - if (defaultTokenServices != null) { - defaultTokenServices.revokeToken(token); - } - response.setHeader("Location", "/"); response.setHeader("Cache-Control", "no-store"); response.setHeader("Pragma", "no-cache"); response.setHeader(HttpHeaders.SET_COOKIE, createCookie( - OAuth2AccessToken.ACCESS_TOKEN, token, 0, true).toString()); + OAuth2TokenType.ACCESS_TOKEN.getValue(), token, 0, true).toString()); response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY); return; diff --git a/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/manage/AuthManager.java b/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/manage/AuthManager.java index 1d2c8ae7..fa71ed03 100644 --- a/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/manage/AuthManager.java +++ b/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/manage/AuthManager.java @@ -8,7 +8,11 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; -import java.nio.file.*; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.security.NoSuchAlgorithmException; import java.security.MessageDigest; import java.security.cert.CertificateException; @@ -20,7 +24,7 @@ import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; -import javax.xml.bind.DatatypeConverter; +import jakarta.xml.bind.DatatypeConverter; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; diff --git a/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/manage/AuthorizationFileWatcher.java b/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/manage/AuthorizationFileWatcher.java index cac490ff..700fcf90 100644 --- a/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/manage/AuthorizationFileWatcher.java +++ b/src/federation-hub-ui/src/main/java/tak/server/federation/hub/ui/manage/AuthorizationFileWatcher.java @@ -3,7 +3,13 @@ import java.io.File; import java.io.IOException; import java.net.URL; -import java.nio.file.*; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardWatchEventKinds; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; import java.util.List; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/src/federation-hub-ui/src/main/resources/application.properties b/src/federation-hub-ui/src/main/resources/application.properties new file mode 100644 index 00000000..705d36ce --- /dev/null +++ b/src/federation-hub-ui/src/main/resources/application.properties @@ -0,0 +1 @@ +management.endpoints.web.exposure.include=* \ No newline at end of file diff --git a/src/federation-hub-ui/src/main/resources/federation-hub-ui.yml b/src/federation-hub-ui/src/main/resources/federation-hub-ui.yml index f1fccba5..c1afc908 100644 --- a/src/federation-hub-ui/src/main/resources/federation-hub-ui.yml +++ b/src/federation-hub-ui/src/main/resources/federation-hub-ui.yml @@ -1,9 +1,9 @@ keystoreType: JKS -keystoreFile: /opt/tak/certs/files/takserver.jks +keystoreFile: /opt/tak/federation-hub/certs/files/takserver.jks keystorePassword: atakatak truststoreType: JKS -truststoreFile: /opt/tak/certs/files/fed-truststore.jks +truststoreFile: /opt/tak/federation-hub/certs/files/fed-truststore.jks truststorePassword: atakatak keyAlias: takserver diff --git a/src/federation-hub-ui/src/main/webapp/home.html b/src/federation-hub-ui/src/main/webapp/home.html index 2889ad15..69f18f42 100644 --- a/src/federation-hub-ui/src/main/webapp/home.html +++ b/src/federation-hub-ui/src/main/webapp/home.html @@ -42,7 +42,7 @@ - + @@ -219,29 +125,17 @@ - - - - - - - - - - - - @@ -251,16 +145,9 @@ + - - - - - - - - diff --git a/src/federation-hub-ui/src/main/webapp/index.html b/src/federation-hub-ui/src/main/webapp/index.html index 5772601e..723cb8af 100644 --- a/src/federation-hub-ui/src/main/webapp/index.html +++ b/src/federation-hub-ui/src/main/webapp/index.html @@ -6,7 +6,7 @@ window.onload = function() { if (confirm("Distribution Statement A: Approved for public release; distribution is unlimited.")) { - window.location.replace("/home"); + window.location.replace("home.html"); } else { // we did not accept the consent banner, so we do nothing! diff --git a/src/federation-hub-ui/src/main/webapp/login/js/controllers.js b/src/federation-hub-ui/src/main/webapp/login/js/controllers.js index 1befba39..feaf7f24 100644 --- a/src/federation-hub-ui/src/main/webapp/login/js/controllers.js +++ b/src/federation-hub-ui/src/main/webapp/login/js/controllers.js @@ -31,7 +31,7 @@ loginControllers.controller('loginController', ['$scope', '$location', '$window' $scope.onSubmit = async function () { console.log(username.value, password.value) - $http.post('/oauth/token', { + $http.post('/oauth2/token', { username: username.value, password: password.value }) diff --git a/src/federation-hub-ui/src/main/webapp/modules/authentication/auth_controller.js b/src/federation-hub-ui/src/main/webapp/modules/authentication/auth_controller.js deleted file mode 100644 index a902be6f..00000000 --- a/src/federation-hub-ui/src/main/webapp/modules/authentication/auth_controller.js +++ /dev/null @@ -1,75 +0,0 @@ - -'use strict'; - -angular.module('roger_federation.Authentication') - -.controller('AuthenticationController', ["$scope", "$rootScope", "$state", '$modalInstance', "$cookieStore", "AuthenticationService", "AUTH_EVENTS", 'ConfigService', - function($scope, $rootScope, $state, $modalInstance, $cookieStore, AuthenticationService, AUTH_EVENTS, ConfigService) { - - $scope.credentials = {}; - - AuthenticationService.logout(); - - var isServerInfoSet = false; - var heartBeatTimer; - - $scope.initialize = function() { - $scope.serverInfo = ConfigService.getroger_federationServerInfo(); - $scope.credentials = { - username: 'roger_federation', - password: 'roger_federation' - }; - - var heartBeat = function() { - ConfigService.getServerStatus().then( - function(res) { - heartBeatTimer = setTimeout(heartBeat, 2000); - $scope.serverStatus = res; - if ($scope.serverStatus.roger_federationOK) { - if (isServerInfoSet === false) { - ConfigService.setServerInfoFromHAL(); - isServerInfoSet = true; - } - } else { - isServerInfoSet = false; - } - }, - function(reason) { - throw reason; - }); - }; - heartBeat(); - }; - - $scope.$on('$destroy', function() { - clearTimeout(heartBeatTimer); //destroy heartBeat timer - }); - - $scope.$on(AUTH_EVENTS.loginFailed, function(event, data) { - $scope.error = data; - }); - - // $scope.$on(AUTH_EVENTS.loginSuccess, function() { - // $state.go('home'); - // }); - - $scope.submit = function(credentials) { - if (AuthenticationService.login(credentials)) { - $rootScope.$broadcast(AUTH_EVENTS.loginSuccess); - $modalInstance.close('ok'); - $scope.$close(true); - } else { - $rootScope.$broadcast(AUTH_EVENTS.loginFailed, response); - }; - /* - AuthenticationService.login(credentials).then(function() { - $rootScope.$broadcast(AUTH_EVENTS.loginSuccess); - }, function(response) { - $rootScope.$broadcast(AUTH_EVENTS.loginFailed, response); - }); - */ - - - }; - } -]); diff --git a/src/federation-hub-ui/src/main/webapp/modules/authentication/auth_module.js b/src/federation-hub-ui/src/main/webapp/modules/authentication/auth_module.js deleted file mode 100644 index 637e587e..00000000 --- a/src/federation-hub-ui/src/main/webapp/modules/authentication/auth_module.js +++ /dev/null @@ -1,15 +0,0 @@ - -angular.module('roger_federation.Authentication', []) - -.constant('AUTH_EVENTS', { - loginSuccess: 'auth-login-success', - loginFailed: 'auth-login-failed', - logoutSuccess: 'auth-logout-success', - sessionTimeout: 'auth-session-timeout', - notAuthenticated: 'auth-not-authenticated', - notAuthorized: 'auth-not-authorized' -}) - -.constant('ACCESS_EVENTS', { - accessFailed: 'server-access-failed' -}); diff --git a/src/federation-hub-ui/src/main/webapp/modules/authentication/auth_services.js b/src/federation-hub-ui/src/main/webapp/modules/authentication/auth_services.js deleted file mode 100644 index 042a4c8e..00000000 --- a/src/federation-hub-ui/src/main/webapp/modules/authentication/auth_services.js +++ /dev/null @@ -1,63 +0,0 @@ - -'use strict'; - -angular.module('roger_federation.Authentication') - -.factory('AuthenticationService', ["$http", "$cookieStore", "Session", - function ($http, $cookieStore, Session) { - var authService = {}; - - var userId = "unknown"; - - authService.login = function(credentials) { - /* - return $http - .put(ConfigService.getServerBaseUrlStr()+'access_control/login', { - "username" : credentials.username, - "password" : credentials.password - }) - .then(function (response) { - */ - userId = credentials.username; - Session.create(credentials.username, credentials.username, []); - return { - username: credentials.username, - clientId: credentials.username - }; - /* - }, function (reason) { - var message = ""; - if(reason.status === 0) { - message = "Cannot contact server"; - } else if(reason.status === 400 || reason.status === 401) { - message = "Invalid username or password."; - } else { - message = reason.status + ": " + reason.statusText; - }; - throw message; - }); - */ - }; - - - authService.getUserId = function() { - return userId; - }; - - authService.isAuthenticated = function() { - if(!Session.clientId && $cookieStore.get("session")) { - var session = $cookieStore.get("session"); - userId = session.username; - Session.create(session.username, session.clientId, session.activities); - } - - return !!Session.clientId; - }; - - authService.logout = function() { - Session.destroy(); - return true; - }; - - return authService; -}]); diff --git a/src/federation-hub-ui/src/main/webapp/modules/authentication/session_services.js b/src/federation-hub-ui/src/main/webapp/modules/authentication/session_services.js deleted file mode 100644 index 9c3b58da..00000000 --- a/src/federation-hub-ui/src/main/webapp/modules/authentication/session_services.js +++ /dev/null @@ -1,21 +0,0 @@ - -'use strict'; - -angular.module('roger_federation.Authentication') -.service('Session', function($cookieStore) { - this.create = function(username, clientId, activities) { - this.username = username; - this.clientId = clientId; - this.activities = activities; - $cookieStore.put("session", this); - }; - - this.destroy = function() { - this.clientId = null; - this.activities = null; - this.username = null; - $cookieStore.remove("session"); - }; - - return this; -}); diff --git a/src/federation-hub-ui/src/main/webapp/modules/commander-dashboard/commander_dashboard_controller.js b/src/federation-hub-ui/src/main/webapp/modules/commander-dashboard/commander_dashboard_controller.js deleted file mode 100644 index 2246791c..00000000 --- a/src/federation-hub-ui/src/main/webapp/modules/commander-dashboard/commander_dashboard_controller.js +++ /dev/null @@ -1,674 +0,0 @@ - -'use strict'; - -angular.module('roger_federation.CommanderDashboard') - .controller('CommanderDashboardController', ['$scope', '$rootScope', '$state', '$stateParams', '$interval', '$log', '$http', 'uuid4', 'growl', - 'WorkflowService', 'WorkflowTemplate', 'InstanceService', 'RoleProductSetService', 'AssetMatchingService', commanderDashboardController - ]); - -function commanderDashboardController($scope, $rootScope, $state, $stateParams, $interval, $log, $http, uuid4, growl, WorkflowService, - WorkflowTemplate, InstanceService, RoleProductSetService, AssetMatchingService) { - $rootScope.$state = $state; - $rootScope.$stateParams = $stateParams; - - $scope.workflowList = {}; - $scope.instanceList = {}; - $scope.displayedInstanceCollection = []; - - $scope.activeInstance = undefined; - $scope.selectedInstanceTag = undefined; - - $scope.graphLayout = { - zoomlevel: 1.0 - }; - - $scope.canAutoWidth = function(scale) { - if (scale.match(/.*?hour.*?/) || scale.match(/.*?minute.*?/)) { - return false; - } - return true; - }; - $scope.getColumnWidth = function(widthEnabled, scale, zoom) { - // if (!widthEnabled && $scope.canAutoWidth(scale)) { - // return undefined; - // } - // - // if (scale.match(/.*?week.*?/)) { - // return 150 * zoom; - // } - // - // if (scale.match(/.*?month.*?/)) { - // return 300 * zoom; - // } - // - // if (scale.match(/.*?quarter.*?/)) { - // return 500 * zoom; - // } - // - // if (scale.match(/.*?year.*?/)) { - // return 800 * zoom; - // } - - return 40 * zoom; - }; - - $scope.gantt = { - api: {}, - options: { - mode: 'custom', - scale: 'day', - sortMode: undefined, - sideMode: 'TreeTable', - daily: false, - maxHeight: false, - width: false, - zoom: 1, - columns: ['model.name', 'from', 'to'], - treeTableColumns: ['from', 'to'], - columnsHeaders: { - 'model.name': 'Name', - 'from': 'From', - 'to': 'To' - }, - columnsClasses: { - 'model.name': 'gantt-column-name', - 'from': 'gantt-column-from', - 'to': 'gantt-column-to' - }, - columnsFormatters: { - 'from': function(from) { - return from !== undefined ? from.format('lll') : undefined; - }, - 'to': function(to) { - return to !== undefined ? to.format('lll') : undefined; - } - }, - treeHeaderContent: ' {{getHeader()}}', - columnsHeaderContents: { - 'model.name': ' {{getHeader()}}', - 'from': ' {{getHeader()}}', - 'to': ' {{getHeader()}}' - }, - autoExpand: 'none', - taskOutOfRange: 'truncate', - fromDate: moment(null), - toDate: undefined, - // rowContent: ' {{row.model.name}}', - taskContent: ' {{task.model.name}}', - allowSideResizing: true, - labelsEnabled: true, - currentDate: 'line', - // currentDateValue: new Date(2013, 09, 26, 14, 24, 10), - draw: false, - readOnly: false, - groupDisplayMode: 'group', - filterTask: '', - filterRow: '', - timeFrames: { - // 'day': { - // start: moment('8:00', 'HH:mm'), - // end: moment('20:00', 'HH:mm'), - // color: '#ACFFA3', - // working: true, - // default: true - // }, - // 'noon': { - // start: moment('12:00', 'HH:mm'), - // end: moment('13:30', 'HH:mm'), - // working: false, - // default: true - // }, - // 'closed': { - // working: false, - // default: true - // }, - // 'weekend': { - // working: false - // }, - 'holiday': { - working: false, - color: 'red', - classes: ['gantt-timeframe-holiday'] - } - }, - dateFrames: { - 'weekend': { - evaluator: function(date) { - return date.isoWeekday() === 6 || date.isoWeekday() === 7; - }, - targets: ['weekend'] - }, - '11-november': { - evaluator: function(date) { - return date.month() === 9 && date.date() === 30; - }, - targets: ['holiday'] - } - }, - timeFramesWorkingMode: 'hidden', - timeFramesNonWorkingMode: 'visible', - columnMagnet: '15 minutes', - timeFramesMagnet: true, - dependencies: true, - canDraw: function(event) { - var isLeftMouseButton = event.button === 0 || event.button === 1; - return $scope.options.draw && !$scope.options.readOnly && isLeftMouseButton; - }, - drawTaskFactory: function() { - return { - id: utils.randomUuid(), // Unique id of the task. - name: 'Drawn task', // Name shown on top of each task. - color: '#AA8833' // Color of the task in HEX format (Optional). - }; - } - }, - zoomIn: function() { - this.options.zoom = this.options.zoom + 1; - }, - zoomOut: function() { - if (this.options.zoom > 1 ) { - this.options.zoom = this.options.zoom - 1; - } - }, - data: [], - mockupdata: [{ - name: 'Roles', - children: ['TCJ3-FC - SpecialAssignmentAirliftMissionPlanning', 'TCJ3-FC - SpecialAssignmentAirliftMissionMonitoring', 'SpecialAssignmentAirliftMissionExecution'], - content: ' {{row.model.name}}' - }, { - name: 'TCJ3-FC - SpecialAssignmentAirliftMissionPlanning', - tooltips: true, - tasks: [{ - id: '3333', - name: 'TCJ3-FC', - color: 'salmon', - from: new Date(2013, 9, 28, 8, 0, 0), - to: new Date(2013, 10, 1, 15, 0, 0) - } - // {id: '1111', name: 'TCJ3-FC', color: '#F1C232', from: new Date(2013, 9, 21, 8, 0, 0), to: new Date(2013, 9, 25, 15, 0, 0), progress: 25 } - ] - }, { - name: 'TCJ3-FC - SpecialAssignmentAirliftMissionMonitoring', - tasks: [{ - id: '2222', - name: 'TCJ3-FC', - color: 'salmon', - from: new Date(2013, 9, 28, 8, 0, 0), - to: new Date(2013, 10, 1, 15, 0, 0) - }] - }, { - name: 'SpecialAssignmentAirliftMissionExecution', - tasks: [ - // {id: '3333', name: 'TCJ3-FC', color: '#F1C232', from: new Date(2013, 9, 28, 8, 0, 0), to: new Date(2013, 10, 1, 15, 0, 0)} - ] - }, - - { - name: 'Products', - children: ['RadiologicalIncidentReport', 'SAAMRequest'], - content: ' {{row.model.name}}' - }, { - name: 'RadiologicalIncidentReport', - tooltips: true, - tasks: [{ - id: '4444', - name: 'RadiologicalIncidentReport', - color: 'blue', - from: new Date(2013, 9, 21, 8, 0, 0), - to: new Date(2013, 9, 25, 15, 0, 0), - progress: 25 - }] - }, { - name: 'SAAMRequest', - tasks: [ - // {id: 'Order basket', name: 'Order basket', color: '#F1C232', from: new Date(2013, 9, 28, 8, 0, 0), to: new Date(2013, 10, 1, 15, 0, 0)} - ] - } - ], - mockupdata3: [ -{name: 'Scenario', children: ['ISR Mission', 'Worflow 2', 'Worflow 3', 'Worflow 4', 'Worflow 5'], content: ' {{row.model.name}}'}, -// {name: 'ISR Mission', children: ['Mission Tasking', 'Mission Execution'], content: ' {{row.model.name}}'}, -{name: 'ISR Mission', children: ['TF 3-10', 'ISARC', 'DY 14', 'Airspace Control', 'TF South', 'DCGS GA', 'SW 16'], content: ' {{row.model.name}}'}, -{name: 'TF 3-10', tooltips: true, tasks: [ - {id: 'Role Z1', name: 'Submit ISR request', color: 'blue', from: new Date(2013, 9, 21, 8, 0, 0), to: new Date(2013, 9, 21, 15, 0, 0), - progress: 25, dependencies: [{to: 'Process Request'}]} - -]}, -{name: 'ISARC', tasks: [ - // {id: '2133211', name: '', color: 'rgba(0, 0, 0, 0.35)', from: new Date(2013, 9, 20, 15, 0, 0), to: new Date(2013, 10, 5, 15, 0, 0) }, - {id: 'Process Request', name: 'Process Request', color: 'blue', from: new Date(2013, 9, 21, 15, 0, 0), to: new Date(2013, 9, 21, 16, 0, 0)} -]}, -{name: 'DY 14', tasks: [ - {id: 'Role X1', name: 'Role X1', color: 'blue', from: new Date(2013, 9, 26, 15, 0, 0), to: new Date(2013, 9, 27, 15, 0, 0), - dependencies: {to: 'Product B1'}} -]}, -{name: 'Airspace Control', tasks: [ - {id: 'Role W1', name: 'Role W1', color: 'blue', from: new Date(2013, 9, 30, 15, 0, 0), to: new Date(2013, 9, 30, 15, 0, 0)} -]}, -{name: 'TF South', tasks: [ - {id: 'Role W13', name: 'Role W12', color: 'blue', from: new Date(2013, 9, 30, 15, 0, 0), to: new Date(2013, 9, 30, 15, 0, 0)} -]}, -{name: 'DCGS GA', tasks: [ - {id: 'Role W133', name: 'Role W122', color: 'blue', from: new Date(2013, 9, 30, 15, 0, 0), to: new Date(2013, 9, 30, 15, 0, 0)} -]}, -{name: 'SW 16', tasks: [ - {id: 'Role W134', name: 'Role W134', color: 'blue', from: new Date(2013, 9, 30, 15, 0, 0), to: new Date(2013, 9, 30, 15, 0, 0)} -]}, - - -], - mockupdata2: [ -{name: 'Worflow Set A', children: ['Worflow 1', 'Worflow 2', 'Worflow 3', 'Worflow 4', 'Worflow 5'], content: ' {{row.model.name}}'}, -{name: 'Worflow 1', children: ['Roles1', 'Products1'], content: ' {{row.model.name}}'}, -{name: 'Roles1', children: ['Role Z1', 'Role Y1', 'Role X1', 'Role W1'], content: ' {{row.model.name}}'}, -{id: 'Role Z1', name: 'Role Z1', tooltips: false, tasks: [ - {id: 'Role Z1', name: 'Role Z1', color: 'blue', from: new Date(2013, 9, 21, 8, 0, 0), to: new Date(2013, 9, 25, 15, 0, 0), - progress: 25, dependencies: [{to: 'Product A1'}]} - -]}, -{name: 'Role Y1', tasks: [ - {id: '2133211', name: '', color: 'rgba(0, 0, 0, 0.35)', from: new Date(2013, 9, 20, 15, 0, 0), to: new Date(2013, 10, 5, 15, 0, 0) }, - {id: 'Role Y1', name: 'Role Y1', color: 'blue', from: new Date(2013, 9, 26, 15, 0, 0), to: new Date(2013, 9, 28, 15, 0, 0), - dependencies: [{to: 'Product C1'}, {to: 'Role W1'}]} -]}, -{name: 'Role X1', tasks: [ - {id: 'Role X1', name: 'Role X1', color: 'blue', from: new Date(2013, 9, 26, 15, 0, 0), to: new Date(2013, 9, 27, 15, 0, 0), - dependencies: {to: 'Product B1'}} -]}, -{name: 'Role W1', tasks: [ - {id: 'Role W1', name: 'Role W1', color: 'blue', from: new Date(2013, 9, 30, 15, 0, 0), to: new Date(2013, 9, 30, 15, 0, 0), - dependencies: [{to: 'Product D1'}]} -]}, -{name: 'Products1', children: ['Product A1', 'Product B1', 'Product C1', 'Product D1'], content: ' {{row.model.name}}'}, -{name: 'Product A1', tooltips: false, tasks: [ - {id: 'Product A1', name: 'Product A1', color: '#F1C232', from: new Date(2013, 9, 26, 15, 0, 0), to: new Date(2013, 10, 25, 15, 0, 0), - progress: 25} -]}, -{name: 'Product B1', tasks: [ - {id: 'Product B1', name: 'Product B1', color: '#F1C232', from: new Date(2013, 9, 28, 15, 0, 0), to: new Date(2013, 10, 15, 15, 0, 0)} -]}, -{name: 'Product C1', tasks: [ - {id: 'Product C1', name: 'Product C1', color: '#F1C232', from: new Date(2013, 9, 29, 15, 0, 0), to: new Date(2013, 9, 29, 15, 0, 0)} -]}, -{name: 'Product D1', tasks: [ - {id: 'Product D1', name: 'Product D1', color: '#F1C232', from: new Date(2013, 10, 1, 15, 0, 0), to: new Date(2013, 10, 1, 15, 0, 0)} -]}, - - -{name: 'Worflow 2', children: ['Roles2', 'Products2'], content: ' {{row.model.name}}'}, -{name: 'Roles2', children: ['Role Z2', 'Role Y2', 'Role X2', 'Role W2'], content: ' {{row.model.name}}'}, -{id: 'Role Z2', name: 'Role Z2', tooltips: false, tasks: [ - {id: 'Role Z2', name: 'Role Z2', color: 'blue', from: new Date(2013, 9, 21, 8, 0, 0), to: new Date(2013, 9, 25, 15, 0, 0), - progress: 25, dependencies: [{to: 'Product A2'}]} -]}, -{name: 'Role Y2', tasks: [ - {id: 'Role Y2', name: 'Role Y2', color: 'blue', from: new Date(2013, 9, 26, 15, 0, 0), to: new Date(2013, 9, 28, 15, 0, 0), - dependencies: [{to: 'Product C2'}, {to: 'Role W2'}]} -]}, -{name: 'Role X2', tasks: [ - {id: 'Role X2', name: 'Role X2', color: 'blue', from: new Date(2013, 9, 26, 15, 0, 0), to: new Date(2013, 9, 27, 15, 0, 0), - dependencies: {to: 'Product B2'}} -]}, -{name: 'Role W2', tasks: [ - {id: 'Role W2', name: 'Role W2', color: 'blue', from: new Date(2013, 9, 30, 15, 0, 0), to: new Date(2013, 9, 30, 15, 0, 0), - dependencies: [{to: 'Product D2'}]} -]}, -{name: 'Products2', children: ['Product A2', 'Product B2', 'Product C2', 'Product D2'], content: ' {{row.model.name}}'}, -{name: 'Product A2', tooltips: false, tasks: [ - {id: 'Product A2', name: 'Product A2', color: '#F1C232', from: new Date(2013, 9, 26, 15, 0, 0), to: new Date(2013, 10, 25, 15, 0, 0), - progress: 25} -]}, -{name: 'Product B2', tasks: [ - {id: 'Product B2', name: 'Product B2', color: '#F1C232', from: new Date(2013, 9, 28, 15, 0, 0), to: new Date(2013, 10, 15, 15, 0, 0)} -]}, -{name: 'Product C2', tasks: [ - {id: 'Product C2', name: 'Product C2', color: '#F1C232', from: new Date(2013, 9, 29, 15, 0, 0), to: new Date(2013, 9, 29, 15, 0, 0)} -]}, -{name: 'Product D2', tasks: [ - {id: 'Product D2', name: 'Product D2', color: '#F1C232', from: new Date(2013, 10, 1, 15, 0, 0), to: new Date(2013, 10, 1, 15, 0, 0)} -]}, -{name: 'Worflow 3', children: ['Roles3', 'Products3'], content: ' {{row.model.name}}'}, -{name: 'Roles3', children: ['Role Z3', 'Role Y3', 'Role X3', 'Role W3'], content: ' {{row.model.name}}'}, -{id: 'Role Z3', name: 'Role Z3', tooltips: false, tasks: [ - {id: 'Role Z3', name: 'Role Z3', color: 'blue', from: new Date(2013, 9, 21, 8, 0, 0), to: new Date(2013, 9, 25, 15, 0, 0), - progress: 25, dependencies: [{to: 'Product A3'}]} -]}, -{name: 'Role Y3', tasks: [ - {id: 'Role Y3', name: 'Role Y3', color: 'blue', from: new Date(2013, 9, 26, 15, 0, 0), to: new Date(2013, 9, 28, 15, 0, 0), - dependencies: [{to: 'Product C3'}, {to: 'Role W3'}]} -]}, -{name: 'Role X3', tasks: [ - {id: 'Role X3', name: 'Role X3', color: 'blue', from: new Date(2013, 9, 26, 15, 0, 0), to: new Date(2013, 9, 27, 15, 0, 0), - dependencies: {to: 'Product B3'}} -]}, -{name: 'Role W3', tasks: [ - {id: 'Role W3', name: 'Role W3', color: 'blue', from: new Date(2013, 9, 30, 15, 0, 0), to: new Date(2013, 9, 30, 15, 0, 0), - dependencies: [{to: 'Product D3'}]} -]}, -{name: 'Products3', children: ['Product A3', 'Product B3', 'Product C3', 'Product D3'], content: ' {{row.model.name}}'}, -{name: 'Product A3', tooltips: false, tasks: [ - {id: 'Product A3', name: 'Product A3', color: '#F1C233', from: new Date(2013, 9, 26, 15, 0, 0), to: new Date(2013, 10, 25, 15, 0, 0), - progress: 25} -]}, -{name: 'Product B3', tasks: [ - {id: 'Product B3', name: 'Product B3', color: '#F1C233', from: new Date(2013, 9, 28, 15, 0, 0), to: new Date(2013, 10, 15, 15, 0, 0)} -]}, -{name: 'Product C3', tasks: [ - {id: 'Product C3', name: 'Product C3', color: '#F1C233', from: new Date(2013, 9, 29, 15, 0, 0), to: new Date(2013, 9, 29, 15, 0, 0)} -]}, -{name: 'Product D3', tasks: [ - {id: 'Product D3', name: 'Product D3', color: '#F1C233', from: new Date(2013, 10, 1, 15, 0, 0), to: new Date(2013, 10, 1, 15, 0, 0)} -]}, -{name: 'Worflow 4', children: ['Roles4', 'Products4'], content: ' {{row.model.name}}'}, -{name: 'Roles4', children: ['Role Z4', 'Role Y4', 'Role X4', 'Role W4'], content: ' {{row.model.name}}'}, -{id: 'Role Z4', name: 'Role Z4', tooltips: false, tasks: [ - {id: 'Role Z4', name: 'Role Z4', color: 'blue', from: new Date(2013, 9, 21, 8, 0, 0), to: new Date(2013, 9, 25, 15, 0, 0), - progress: 25, dependencies: [{to: 'Product A4'}]} -]}, -{name: 'Role Y4', tasks: [ - {id: 'Role Y4', name: 'Role Y4', color: 'blue', from: new Date(2013, 9, 26, 15, 0, 0), to: new Date(2013, 9, 28, 15, 0, 0), - dependencies: [{to: 'Product C4'}, {to: 'Role W4'}]} -]}, -{name: 'Role X4', tasks: [ - {id: 'Role X4', name: 'Role X4', color: 'blue', from: new Date(2013, 9, 26, 15, 0, 0), to: new Date(2013, 9, 27, 15, 0, 0), - dependencies: {to: 'Product B4'}} -]}, -{name: 'Role W4', tasks: [ - {id: 'Role W4', name: 'Role W4', color: 'blue', from: new Date(2013, 9, 30, 15, 0, 0), to: new Date(2013, 9, 30, 15, 0, 0), - dependencies: [{to: 'Product D4'}]} -]}, -{name: 'Products4', children: ['Product A4', 'Product B4', 'Product C4', 'Product D4'], content: ' {{row.model.name}}'}, -{name: 'Product A4', tooltips: false, tasks: [ - {id: 'Product A4', name: 'Product A4', color: '#F1C234', from: new Date(2013, 9, 26, 15, 0, 0), to: new Date(2013, 10, 25, 15, 0, 0), - progress: 25} -]}, -{name: 'Product B4', tasks: [ - {id: 'Product B4', name: 'Product B4', color: '#F1C234', from: new Date(2013, 9, 28, 15, 0, 0), to: new Date(2013, 10, 15, 15, 0, 0)} -]}, -{name: 'Product C4', tasks: [ - {id: 'Product C4', name: 'Product C4', color: '#F1C234', from: new Date(2013, 9, 29, 15, 0, 0), to: new Date(2013, 9, 29, 15, 0, 0)} -]}, -{name: 'Product D4', tasks: [ - {id: 'Product D4', name: 'Product D4', color: '#F1C234', from: new Date(2013, 10, 1, 15, 0, 0), to: new Date(2013, 10, 1, 15, 0, 0)} -]}, -{name: 'Worflow 5', children: ['Roles5', 'Products5'], content: ' {{row.model.name}}'}, -{name: 'Roles5', children: ['Role Z5', 'Role Y5', 'Role X5', 'Role W5'], content: ' {{row.model.name}}'}, -{id: 'Role Z5', name: 'Role Z5', tooltips: false, tasks: [ - {id: 'Role Z5', name: 'Role Z5', color: 'blue', from: new Date(2013, 9, 21, 8, 0, 0), to: new Date(2013, 9, 25, 15, 0, 0), - progress: 25, dependencies: [{to: 'Product A5'}]} -]}, -{name: 'Role Y5', tasks: [ - {id: 'Role Y5', name: 'Role Y5', color: 'blue', from: new Date(2013, 9, 26, 15, 0, 0), to: new Date(2013, 9, 28, 15, 0, 0), - dependencies: [{to: 'Product C5'}, {to: 'Role W5'}]} -]}, -{name: 'Role X5', tasks: [ - {id: 'Role X5', name: 'Role X5', color: 'blue', from: new Date(2013, 9, 26, 15, 0, 0), to: new Date(2013, 9, 27, 15, 0, 0), - dependencies: {to: 'Product B5'}} -]}, -{name: 'Role W5', tasks: [ - {id: 'Role W5', name: 'Role W5', color: 'blue', from: new Date(2013, 9, 30, 15, 0, 0), to: new Date(2013, 9, 30, 15, 0, 0), - dependencies: [{to: 'Product D5'}]} -]}, -{name: 'Products5', children: ['Product A5', 'Product B5', 'Product C5', 'Product D5'], content: ' {{row.model.name}}'}, -{name: 'Product A5', tooltips: false, tasks: [ - {id: 'Product A5', name: 'Product A5', color: '#F1C235', from: new Date(2013, 9, 26, 15, 0, 0), to: new Date(2013, 10, 25, 15, 0, 0), - progress: 25} -]}, -{name: 'Product B5', tasks: [ - {id: 'Product B5', name: 'Product B5', color: '#F1C235', from: new Date(2013, 9, 28, 15, 0, 0), to: new Date(2013, 10, 15, 15, 0, 0)} -]}, -{name: 'Product C5', tasks: [ - {id: 'Product C5', name: 'Product C5', color: '#F1C235', from: new Date(2013, 9, 29, 15, 0, 0), to: new Date(2013, 9, 29, 15, 0, 0)} -]}, -{name: 'Product D5', tasks: [ - {id: 'Product D5', name: 'Product D5', color: '#F1C235', from: new Date(2013, 10, 1, 15, 0, 0), to: new Date(2013, 10, 1, 15, 0, 0)} -]}, - - - ], - }; - - - $scope.initialize = function() { - return; //JEM FixMe - WorkflowService.getWorkflows().then(function(workflowList) { - $scope.workflowList = workflowList; - setContainerSize(); - - InstanceService.getInstances().then(function(instanceList) { - $scope.instanceList = instanceList; - $scope.instanceList.forEach(function(instance) { - instance.workflow = $scope.getWorkFlow(instance.workflowId); - }); - $scope.displayedInstanceCollection = [].concat($scope.instanceList); - - //Select first row in table - if ($scope.displayedInstanceCollection.length > 0) { - $scope.selectRow($scope.displayedInstanceCollection[$scope.displayedInstanceCollection.length - 1]); - } - - }, function(result) { - growl.error("Failed getting instances. Error: " + result.data.error); - $scope.instanceList.length = 0; - }); - - }, function(result) { - growl.error("Failed getting workflows. Error: " + result.data.error); - $scope.workflowList.length = 0; - }); - - }; - - $scope.getWorkFlow = function(workflowId) { - return $scope.workflowList.filter(function(item) { - return item.id === workflowId; - })[0]; - }; - - $scope.getTagList = function(uri) { - var ret = ""; - var maxTags = 10; - $scope.activeInstance.instanceTags.forEach(function(instanceTag) { - var numOfTags = Math.min(instanceTag.tags.length, maxTags); - if (instanceTag.product === uri) { - for (var i = 0; i < numOfTags; i++) { - ret += $scope.getUriLabel(instanceTag.tags[i]) + ", "; - } - } - ret = ret.slice(0, -2); //Remove last comma - if (ret !== "" && instanceTag.tags.length > maxTags) { - ret += "... (click product to show all tags)"; - } - }); - return ret; - }; - - $scope.getUriLabel = function(uri) { - if (uri.lastIndexOf("#") === -1) { - return uri.substring(uri.lastIndexOf("/") + 1); - } else { - return uri.substring(uri.lastIndexOf("#") + 1); - } - }; - $scope.initializeWorkflowDiagram = function(instance) { - $scope.activeInstance = instance; - $scope.selectedInstanceTag = undefined; - $scope.graphLayout.zoomLevel = 1; - $scope.initGanttData(); - setTimeout(function() { - - //Restore joint diagram div to orignal state - document.getElementById('pnlDiagram').innerHTML = ''; - - WorkflowGraphFactory.initCustomSize('#joint-diagram', $('#pnlDiagram').width(), 500); - - WorkflowService.getWorkflow(instance.workflow.id).then(function(workflow) { - WorkflowGraphFactory.loadData(workflow.graphItems, workflow.graphLinks); - - WorkflowGraphFactory.getPaper().on('cell:pointerup', function(cellView, evt, x, y) { - $log.debug('cell view ' + cellView.model.id + ' was double clicked'); - $scope.selectedInstanceTag = undefined; - $scope.activeInstance.instanceTags.forEach(function(instanceTag) { - if (instanceTag.product === cellView.model.attributes.uri) { - $scope.selectedInstanceTag = instanceTag; - $('#container').animate({ - scrollTop: $("#tagRegion").offset().top - }, 500); - } - }); - $scope.$apply(); - }); - - WorkflowGraphFactory.getPaper().on('cell:mouseover', function(cellView, evt) { - var cellType = cellView.model.attributes.type; - if (cellType === "bbn.Role" || cellType === "bbn.Product") { - if (!cellView.$el.data('bs.popover')) { - var ontology = cellView.model.attributes.ontology; - var classURI = cellView.model.attributes.uri; - var tagList = $scope.getTagList(cellView.model.attributes.uri); - if (tagList !== "") { - tagList = "
Tags: " + tagList; - } - RoleProductSetService.getClassDetails(ontology.dataset, classURI).then(function(result) { - $('.popover').popover('hide'); //Hide any lingering popovers - cellView.$el.popover({ - placement: 'auto', - trigger: 'hover', - html: true, - delay: { - "show": 250, - "hide": 0 - }, - container: 'body', - content: 'Ontology: (' + ontology.name + ')
Comment: ' + (result.description === "" ? "none" : result.description) + tagList - }).popover('show'); - }, function() {}); - $('.popover').popover('hide'); - } - } - }); - - var graphItems = WorkflowGraphFactory.getGraph().getCells(); - - instance.roleAssets.forEach(function(roleAsset) { - var assetLabel = ""; - - roleAsset.assets.forEach(function(asset) { - assetLabel += $scope.getUriLabel(asset) + ", "; - }); - assetLabel = assetLabel.slice(0, -2); - - if (assetLabel !== "") { //Replace original labels with assets - - var roleAssetGraphItems = graphItems.filter(function(item) { - return item.attributes.graphItemId === roleAsset.graphItem; - }); - - roleAssetGraphItems.forEach(function(roleAssetGraphItem) { - roleAssetGraphItem.attr('text/text', joint.util.breakText(assetLabel + "\n" + roleAssetGraphItem.attributes.label, { - width: 100 - })); - }); - } - - - $scope.createGanttRole($scope.getUriLabel(roleAsset.role), assetLabel); - - }); - - instance.instanceTags.forEach(function(instanceTag) { - graphItems.forEach(function(graphItem) { - if (instanceTag.product === graphItem.attributes.uri) { - graphItem.attr('text/text', graphItem.attr('text/text') + "\n[Tags]"); - } - }); - - $scope.createGanttProduct($scope.getUriLabel(instanceTag.product)); - }); - - - //Hide delete icon on shapes and links - $(".element-tool-remove").hide(); - $(".tool-remove").hide(); - WorkflowGraphFactory.getPaper().scaleContentToFit(); - // $scope.gantt.api.side.setWidth($scope.gantt.api.columns.getColumnsWidthToFit()); - $scope.gantt.api.side.setWidth(450); - }, function(result) { - growl.error("Failed to acquire role-product set. Error: " + result.data.error); - }); - }, 100); - }; - - - $scope.navigateToWorkflow = function(workflow) { - WorkflowTemplate.setId(workflow.id); - $state.go('workflows.editor', { - workflowId: workflow.id - }); - }; - - $scope.selectRow = function(row) { - for (var i = 0, len = $scope.displayedInstanceCollection.length; i < len; i += 1) { - $scope.displayedInstanceCollection[i].isSelected = false; - } - row.isSelected = true; - $scope.initializeWorkflowDiagram(row); - }; - - $scope.changeGraphZoom = function() { - WorkflowGraphFactory.getPaper().scale($scope.graphLayout.zoomLevel, $scope.graphLayout.zoomLevel); - }; - - $scope.initGanttData = function() { - $scope.gantt.data = [{ - name: 'Products', - children: [], - content: ' {{row.model.name}}' - }, { - name: 'Roles', - children: [], - content: ' {{row.model.name}}' - }]; - }; - - - $scope.createGanttRole = function(roleName, assestName) { - var ganttRole = { - name: roleName, - tooltips: true, - tasks: [] - }; - if (assestName !== "") { - var task = { - name: assestName, - color: 'salmon', - from: moment($scope.activeInstance.creationDate).add(1, 'days'), - to: moment($scope.activeInstance.creationDate).add(3, 'days') - }; - ganttRole.tasks.push(task); - } - $scope.gantt.data.push(ganttRole); - $scope.gantt.data[1].children.push(roleName); - }; - $scope.createGanttProduct = function(productName) { - var ganttProduct = { - name: productName, - tooltips: true, - tasks: [] - }; - - var task = { - name: productName, - color: 'blue', - from: moment($scope.activeInstance.creationDate), - to: moment($scope.activeInstance.creationDate).add(1, 'days') - }; - ganttProduct.tasks.push(task); - $scope.gantt.data.push(ganttProduct); - $scope.gantt.data[0].children.push(productName); - }; - $scope.registerApi = function(api) { - $scope.gantt.api = api; - }; - -} diff --git a/src/federation-hub-ui/src/main/webapp/modules/commander-dashboard/commander_dashboard_module.js b/src/federation-hub-ui/src/main/webapp/modules/commander-dashboard/commander_dashboard_module.js deleted file mode 100644 index decf9ac7..00000000 --- a/src/federation-hub-ui/src/main/webapp/modules/commander-dashboard/commander_dashboard_module.js +++ /dev/null @@ -1,2 +0,0 @@ - -angular.module('roger_federation.CommanderDashboard', []); diff --git a/src/federation-hub-ui/src/main/webapp/modules/config/config_service.js b/src/federation-hub-ui/src/main/webapp/modules/config/config_service.js index 9ffc63c0..1be03195 100644 --- a/src/federation-hub-ui/src/main/webapp/modules/config/config_service.js +++ b/src/federation-hub-ui/src/main/webapp/modules/config/config_service.js @@ -23,7 +23,13 @@ angular.module('roger_federation.Config') hostname = location.hostname; } - var port = Number(location.port) + var api_port = location.port; + var local_data = localStorage.getItem("api_port"); + + if(local_data !== null){ + api_port = local_data; + } + var port = Number(api_port) if (typeof $localStorage.configuration === "undefined") { $localStorage.configuration = {}; @@ -59,7 +65,6 @@ angular.module('roger_federation.Config') }; initialized = true; - service.setServerInfoFromHAL(); } @@ -249,32 +254,6 @@ angular.module('roger_federation.Config') // }); }; - service.getHALInfo = function() { - return $http.get( - service.getServerRootUrlStr() + '/info').then( - function(res) { - return res; - }, - function(reason) { - throw reason; - }); - }; - - service.setServerInfoFromHAL = function() { - service.getHALInfo().then( - function(res) { - serverInfo.fuseki.uri = res.data.fuseki.host + ":" + res.data.fuseki.port; - serverInfo.roger_federation.version = res.data.build.version; - return res; - }, - function(reason) { - serverInfo.fuseki.uri = "localhost:3030"; - serverInfo.roger_federation.version = "?"; - throw reason; - }); - }; - - return service; } diff --git a/src/federation-hub-ui/src/main/webapp/modules/core/application_controller.js b/src/federation-hub-ui/src/main/webapp/modules/core/application_controller.js deleted file mode 100644 index acb22867..00000000 --- a/src/federation-hub-ui/src/main/webapp/modules/core/application_controller.js +++ /dev/null @@ -1,33 +0,0 @@ - -"use strict"; - -angular.module('roger_federation.Core') - .controller('ApplicationController', ['$http', '$scope', '$rootScope', '$state', '$cookieStore', 'AuthenticationService', 'Session', 'ConfigService', applicationController]); - -function applicationController($http, $scope, $rootScope, $state, $cookieStore, AuthenticationService, Session, ConfigService) { - - // Setup some common display values. - var session = $cookieStore.get("session"); - if (session) { - $scope.currentUser = session.username; - } - - $scope.$watch(function() { - return AuthenticationService.getUserId(); - }, - function(newValue) { - $scope.currentUser = newValue; - }); - - var heartBeat = function() { -// ConfigService.getServerStatus().then( -// function(res) { -// setTimeout(heartBeat, 3000); -// $scope.serverStatus = res; -// }, -// function(reason) { -// throw reason; -// }); - }; - heartBeat(); -} diff --git a/src/federation-hub-ui/src/main/webapp/modules/core/core_module.js b/src/federation-hub-ui/src/main/webapp/modules/core/core_module.js deleted file mode 100644 index e27828a1..00000000 --- a/src/federation-hub-ui/src/main/webapp/modules/core/core_module.js +++ /dev/null @@ -1,2 +0,0 @@ - -angular.module('roger_federation.Core', []); \ No newline at end of file diff --git a/src/federation-hub-ui/src/main/webapp/modules/logout/logout_controller.js b/src/federation-hub-ui/src/main/webapp/modules/logout/logout_controller.js deleted file mode 100644 index 6e0717b5..00000000 --- a/src/federation-hub-ui/src/main/webapp/modules/logout/logout_controller.js +++ /dev/null @@ -1,22 +0,0 @@ - -'use strict'; - -angular.module('roger_federation.Logout') - -.controller('LogoutController', - ['$scope', '$rootScope', '$state', '$modalInstance', 'AuthenticationService', function ($scope, $rootScope, $state, $modalInstance, AuthenticationService) { - $rootScope.currentPage = 'Logout'; - - $scope.logout = function () { - AuthenticationService.logout(); - $modalInstance.close('ok'); - $scope.$close(true); - }; - - - $scope.cancel = function () { - $scope.$dismiss(); - $modalInstance.dismiss('cancel'); - }; - - }]); \ No newline at end of file diff --git a/src/federation-hub-ui/src/main/webapp/modules/logout/logout_module.js b/src/federation-hub-ui/src/main/webapp/modules/logout/logout_module.js deleted file mode 100644 index 2fbd20c4..00000000 --- a/src/federation-hub-ui/src/main/webapp/modules/logout/logout_module.js +++ /dev/null @@ -1,2 +0,0 @@ - -angular.module('roger_federation.Logout', []); \ No newline at end of file diff --git a/src/federation-hub-ui/src/main/webapp/modules/manage/manage_controller.js b/src/federation-hub-ui/src/main/webapp/modules/manage/manage_controller.js deleted file mode 100644 index 13873288..00000000 --- a/src/federation-hub-ui/src/main/webapp/modules/manage/manage_controller.js +++ /dev/null @@ -1,670 +0,0 @@ - -'use strict'; - -angular.module('roger_federation.Manage') - -.controller('ManageController', ['$scope', '$rootScope', '$state', '$stateParams', '$log', 'growl', 'ACCESS_EVENTS', - 'RoleProductSetService', 'WorkflowService', 'AssetService', - 'DefaultsService', 'AuthenticationService', manageController -]); - - -function manageController($scope, $rootScope, $state, $stateParams, $log, growl, ACCESS_EVENTS, RoleProductSetService, - WorkflowService, AssetService, DefaultsService, AuthenticationService) { - $rootScope.$state = $state; - $rootScope.$stateParams = $stateParams; - - $rootScope.$on('$stateChangeStart', handleStateChangeStart); - - $scope.rowCollection = []; - $scope.displayedCollection = []; - $scope.itemsByPage = 15; - $scope.defaultsData = { - datasets: [], - currentDataset: undefined, - currentDefaults: [], - orderAsc: false, - orderByField: 'ownerItem.name' - }; - - $scope.assetUploadData = {}; - - - $scope.initialize = function() { - if ($state.current.name === 'manage.role-sets') { - $scope.initializeRoleProductSetView(); - } else if ($state.current.name === 'manage.diagrams' || $state.current.name === 'manage.federations') { - $scope.initializeWorkflowView(); - } else if ($state.current.name === 'manage.workflow-sets') { - $scope.initializeWorkflowSetsView(); - } else if ($state.current.name === 'manage.instances') { - $scope.initializeInstanceView(); - } else if ($state.current.name === 'manage.assets') { - $scope.initializeAssetView(); - } else if ($state.current.name === 'manage.defaults') { - $scope.initializeDefaultsView(); - } - }; - - - function handleStateChangeStart(event, toState, toParams, fromState, fromParams) { - if (toState.name === 'manage.role-sets') { - $scope.initializeRoleProductSetView(); - } else if (toState.name === 'manage.diagrams' || $state.current.name === 'manage.federations') { - $scope.initializeWorkflowView(); - } else if (toState.name === 'manage.workflow-sets') { - $scope.initializeWorkflowSetsView(); - } else if (toState.name === 'manage.instances') { - $scope.initializeInstanceView(); - } else if (toState.name === 'manage.assets') { - $scope.initializeAssetView(); - } else if (toState.name === 'manage.defaults') { - $scope.initializeDefaultsView(); - } - } - - - // INITIALIZE VIEWS - - $scope.initializeRoleProductSetView = function() { - RoleProductSetService.getRoleProductSetDescriptors().then(function(roleProductSetList) { - $scope.rowCollection = roleProductSetList; - $scope.displayedCollection = [].concat($scope.rowCollection); - }, function(result) { - $scope.rowCollection.length = 0; - $scope.displayedCollection = [].concat($scope.rowCollection); - if (result.status === -1) { - $rootScope.$broadcast(ACCESS_EVENTS.accessFailed, result); - } else { - var err = "."; - if (((typeof result.data) !== "undefined") && result.data !== null && ((typeof result.data.error) !== "undefined") && result.data.error !== null) { - err = result.data.error + "."; - } - growl.error("Failed getting role-product sets. Error: " + err); - } - }); - }; - - - $scope.initializeWorkflowView = function() { - WorkflowService.getWorkflowDescriptors().then(function(workflowList) { - $scope.rowCollection = workflowList; - $scope.displayedCollection = [].concat($scope.rowCollection); - }, function(result) { - $scope.rowCollection.length = 0; - $scope.displayedCollection = [].concat($scope.rowCollection); - if (result.status === -1) { - $rootScope.$broadcast(ACCESS_EVENTS.accessFailed, result); - } else { - var err = "."; - if (((typeof result.data) !== "undefined") && result.data !== null && ((typeof result.data.error) !== "undefined") && result.data.error !== null) { - err = result.data.error + "."; - } - growl.error("Failed getting workflows. Error: " + err); - } - }); - }; - - $scope.initializeWorkflowSetsView = function() { - WorkflowSetsService.getWorkflowSetsDescriptors().then(function(workflowSetsList) { - $scope.rowCollection = workflowSetsList; - $scope.displayedCollection = [].concat($scope.rowCollection); - }, function(result) { - $scope.rowCollection.length = 0; - $scope.displayedCollection = [].concat($scope.rowCollection); - if (result.status === -1) { - $rootScope.$broadcast(ACCESS_EVENTS.accessFailed, result); - } else { - var err = "."; - if (((typeof result.data) !== "undefined") && result.data !== null && ((typeof result.data.error) !== "undefined") && result.data.error !== null) { - err = result.data.error + "."; - } - growl.error("Failed getting workflow sets files. Error: " + err); - } - }); - }; - - $scope.initializeInstanceView = function() { - InstanceService.getInstanceDescriptors().then(function(instanceList) { - $scope.rowCollection = instanceList; - $scope.displayedCollection = [].concat($scope.rowCollection); - }, function(result) { - $scope.rowCollection.length = 0; - $scope.displayedCollection = [].concat($scope.rowCollection); - if (result.status === -1) { - $rootScope.$broadcast(ACCESS_EVENTS.accessFailed, result); - } else { - var err = "."; - if (((typeof result.data) !== "undefined") && result.data !== null && ((typeof result.data.error) !== "undefined") && result.data.error !== null) { - err = result.data.error + "."; - } - growl.error("Failed getting instances. Error: " + err); - } - }); - }; - - $scope.initializeAssetView = function() { - AssetService.getAssetFileDescriptors().then(function(assetList) { - $scope.rowCollection = assetList; - $scope.displayedCollection = [].concat($scope.rowCollection); - }, function(result) { - $scope.rowCollection.length = 0; - $scope.displayedCollection = [].concat($scope.rowCollection); - if (result.status === -1) { - $rootScope.$broadcast(ACCESS_EVENTS.accessFailed, result); - } else { - var err = "."; - if (((typeof result.data) !== "undefined") && result.data !== null && ((typeof result.data.error) !== "undefined") && result.data.error !== null) { - err = result.data.error + "."; - } - growl.error("Failed getting asset files. Error: " + err); - } - }); - }; - - $scope.initializeDefaultsView = function() { - DefaultsService.initiateOntologiesRetrieval().then( - function(res) { - $scope.defaultsData.datasets = res; - }, - function(res) { - growl.error("Error retrieving dataset list: " + res); - } - ); - }; - - $scope.changeDefaultsOrderBy = function(fieldName) { - if ($scope.defaultsData.orderByField == fieldName) { - $scope.defaultsData.orderAsc = !$scope.defaultsData.orderAsc; - } else { - $scope.defaultsData.orderAsc = false; - } - $scope.defaultsData.orderByField = fieldName; - } - - $scope.datasetChanged = function() { - DefaultsService.initiateDefaultsRetrieval($scope.defaultsData.currentDataset).then( - function(res) { - $scope.defaultsData.currentDefaults = res; - }, - function(res) { - growl.error("Error retrieving current defaults list: " + res); - } - ); - }; - - $scope.importDefaults = function() { - DefaultsService.initiateDefaultsImport($scope.defaultsData.currentDataset).then( - function(res) { - growl.success("Defaults imported"); - $scope.datasetChanged(); - }, - function(res) { - growl.error("Error importing defaults: " + res); - } - ); - }; - - $scope.removeDefault = function(defaultID) { - DefaultsService.initiateDefaultRemoval(defaultID).then( - function(res) { - growl.success("Default removed"); - $scope.datasetChanged(); - }, - function(res) { - growl.error("Error removing default: " + res); - } - ); - }; - - // EXPORT ROWS - - - - - $scope.exportRoleProductSet = function(row) { - RoleProductSetService.getRoleProductSet(row.id).then(function(res) { - - function convertToDefinitionFile(data) { - var result = { - name: data.name, - description: data.description, - roles: [], - products: [] - }; - - for (var i = 0, len = data.roles.length; i < len; i++) { - result.roles.push(data.roles[i].item.uri); - } - for (var i = 0, len = data.products.length; i < len; i++) { - result.products.push(data.products[i].item.uri); - } - return result; - } - - var data = convertToDefinitionFile(res); - - var filename = 'roger_federation-role-product-set-' + res.name + '.json'; - data = new Blob([JSON.stringify(data)], { - type: "text/plain;charset=utf-8" - }); - saveAs(data, filename); - }, function(res) { - var msg = "Failed to export role-product set."; - growl.error(msg); - }); - }; - - $scope.exportWorkflow = function(row) { - WorkflowService.exportWorkflow(row.id).then(function(res) { - var filename = 'roger_federation-workflow-' + res.name + '.json'; - var data = new Blob([JSON.stringify(res)], { - type: "text/plain;charset=utf-8" - }); - saveAs(data, filename); - }, function(res) { - var msg = "Failed to export workflow."; - growl.error(msg); - }); - }; - - $scope.exportWorkflowSet = function(row) { - WorkflowSetsService.getWorkflowSet(row.id).then(function(res) { - var filename = 'roger_federation-workflow-set-' + res.name + '.json'; - var data = new Blob([JSON.stringify(res)], { - type: "text/plain;charset=utf-8" - }); - saveAs(data, filename); - }, function(res) { - var msg = "Failed to export workflow set."; - growl.error(msg); - }); - } - - $scope.exportInstance = function(row) { - InstanceService.getInstance(row.id).then(function(res) { - var filename = 'roger_federation-instance-' + res.name + '.json'; - var data = new Blob([JSON.stringify(res)], { - type: "text/plain;charset=utf-8" - }); - saveAs(data, filename); - }, function(res) { - var msg = "Failed to export instance."; - growl.error(msg); - }); - } - - $scope.exportAsset = function(row) { - AssetService.getAssetFile(row.id).then(function(res) { - var filename = 'roger_federation-asset-' + res.name + '.json'; - var data = new Blob([JSON.stringify(res)], { - type: "text/plain;charset=utf-8" - }); - saveAs(data, filename); - }, function(res) { - var msg = "Failed to export asset file."; - growl.error(msg); - }); - } - - - // REMOVE ROWS - - function removeRowFromCollection(row) { - var index = $scope.rowCollection.indexOf(row); - if (index !== -1) { - $scope.rowCollection.splice(index, 1); - // do I need to do this??? - $scope.displayedCollection = [].concat($scope.rowCollection); - } - } - - - - // IMPORT DATASET - - $scope.handleFileReadComplete = function(event) { - var content = event.target.result, - error = event.target.error; - - if (error != null) { - switch (error.code) { - case error.ENCODING_ERR: - growl.error("File read encoding error."); - break; - - case error.NOT_FOUND_ERR: - growl.error("The file could not be found at the time the read was processed."); - break; - - case error.NOT_READABLE_ERR: - growl.error("The file could not be read."); - break; - - case error.SECURITY_ERR: - growl.error("Security error with file."); - break; - - default: - growl.error("Uncategorized file read error: " + event.target.error.name + "."); - } - } else { - return content; - } - return null; - }; - - $scope.handleFileReadAbort = function() { - growl.error("File read aborted."); - }; - - $scope.handleFileReadError = function(evt) { - switch (evt.target.error.name) { - case "NotFoundError": - growl.error("The file could not be found at the time the read was processed."); - break; - - case "SecurityError": - growl.error("Security error with file."); - break; - - case "NotReadableError": - growl.error("The file could not be read."); - break; - - case "EncodingError": - growl.error("File read encoding error."); - break; - - default: - growl.error("Uncategorized file read error: " + event.target.error.name + "."); - - } // switch - }; // handleFileReadError - - - // Import Role Product Set - $scope.importRoleProductSet = function(file) { - if (file === null) { - return; - } - var reader = new FileReader(); - - reader.onloadend = (function() { - return function(event) { - var result = $scope.handleFileReadComplete(event); - if (result != null) { - result = JSON.parse(result); - delete result.id; - for (var i = 0, len = result.roles.length; i < len; i++) { - delete result.roles[i].id; - /* for (varresult.roles[i].children.length) */ - } - for (var i = 0, len = result.products.length; i < len; i++) { - delete result.products[i].id; - } - result = JSON.stringify(result); - RoleProductSetService.importRoleProductSet(result).then(function() { - growl.success("Role-Product file uploaded."); - $scope.initializeRoleProductSetView(); - }, function(result) { - growl.error("Failed to upload role-product set. Error: " + result.data.error); - }); - }; - }; - })(); - - reader.abort = (function() { - return function() { - $scope.handleFileReadAbort(); - }; - })(); - - reader.onerror = (function() { - return function(event) { - $scope.handleFileReadError(event); - }; - })(); - - reader.readAsText(file); - }; - - // Import Role Product Set Definition - $scope.importRoleProductSetDefinition = function(files) { - if (files === null) { - return; - } - files.forEach(function(file) { - if (file === null) { - return; - } - var reader = new FileReader(); - - reader.onloadend = (function() { - return function(event) { - var result = $scope.handleFileReadComplete(event); - if (result != null) { - RoleProductSetService.uploadRoleProductSetDefinition(result).then(function() { - growl.success("File: (" + file.name + ")
" + "Role-Product set definition file uploaded."); - $scope.initializeRoleProductSetView(); - }, function(result) { - growl.error("File: (" + file.name + ")
" + "Failed to upload role-product set definition file. Error: " + result.data.error); - }); - }; - }; - })(); - - reader.abort = (function() { - return function() { - $scope.handleFileReadAbort(); - }; - })(); - - reader.onerror = (function() { - return function(event) { - $scope.handleFileReadError(event); - }; - })(); - - reader.readAsText(file); - }); - }; - - // Import Workflow - $scope.importWorkflow = function(files) { - if (files === null) { - return; - } - files.forEach(function(file) { - if (file === null) { - return; - } - var reader = new FileReader(); - reader.onloadend = (function() { - return function(event) { - var result = $scope.handleFileReadComplete(event); - if (result != null) { - result = JSON.parse(result); - delete result.id; - result = JSON.stringify(result); - WorkflowService.importWorkflow(result).then(function() { - $scope.initializeWorkflowView(); - }, function(result) { - if (result.data.message !== undefined) { - var errMsg = result.data.message; - growl.error("File: (" + file.name + ")
" + errMsg.substring(errMsg.lastIndexOf(":") + 1)); - } else { - growl.error("File: (" + file.name + ")
" + "Failed to upload workflow file. Error: " + result.data.error); - } - }); - }; - }; - })(); - - reader.abort = (function() { - return function() { - $scope.handleFileReadAbort(); - }; - })(); - - reader.onerror = (function() { - return function(event) { - $scope.handleFileReadError(event); - }; - })(); - - reader.readAsText(file); - }); - }; - - // Import Asset - $scope.importAsset = function(file) { - if (file === null) { - return; - } - var reader = new FileReader(); - - reader.onloadend = (function() { - return function(event) { - var result = $scope.handleFileReadComplete(event); - if (result != null) { - var data = { - contents: result, - name: $scope.assetUploadData.name, - description: $scope.assetUploadData.description, - fileName: file.name, - creatorName: AuthenticationService.getUserId() - }; - AssetService.uploadAssetFile(data).then(function() { - $scope.$$prevSibling.initializeAssetView(); - }, function(result) { - growl.error("Failed to upload Asset file. Error: " + result.data.error + ". " + result.data.message); - }); - }; - }; - })(); - - reader.abort = (function() { - return function() { - $scope.handleFileReadAbort(); - }; - })(); - - reader.onerror = (function() { - return function(event) { - $scope.handleFileReadError(event); - }; - })(); - - reader.readAsText(file); - }; - - - // Import Instance - $scope.importInstance = function(file) { - if (file === null) { - return; - } - var reader = new FileReader(); - - reader.onloadend = (function() { - return function(event) { - var result = $scope.handleFileReadComplete(event); - if (result != null) { - result = JSON.parse(result); - delete result.id; - result = JSON.stringify(result); - InstanceService.uploadInstance(result).then(function() { - growl.success("Instance file uploaded."); - $scope.initializeInstanceView(); - }, function(result) { - growl.error("Failed to upload instance file. Error: " + result.data.error); - }); - }; - }; - })(); - - reader.abort = (function() { - return function() { - $scope.handleFileReadAbort(); - }; - })(); - - reader.onerror = (function() { - return function(event) { - $scope.handleFileReadError(event); - }; - })(); - - reader.readAsText(file); - }; - - - // Import Workflow Set - $scope.importWorkflowSet = function(files) { - if (files === null) { - return; - } - files.forEach(function(file) { - if (file === null) { - return; - } - var reader = new FileReader(); - - reader.onloadend = (function() { - return function(event) { - var result = $scope.handleFileReadComplete(event); - if (result != null) { - result = JSON.parse(result); - delete result.id; - result = JSON.stringify(result); - InstanceService.uploadWorkflowSet(result).then(function() { - growl.success("File: (" + file.name + ")
" + "Workflow set file uploaded."); - $scope.initializeInstanceView(); - }, function(result) { - growl.error("File: (" + file.name + ")
" + "Failed to upload workflow set file. Error: " + result.data.error); - }); - }; - }; - })(); - - reader.abort = (function() { - return function() { - $scope.handleFileReadAbort(); - }; - })(); - - reader.onerror = (function() { - return function(event) { - $scope.handleFileReadError(event); - }; - })(); - - reader.readAsText(file); - }); - }; - - - /** Upload Asset File Functions **/ - - $scope.initializeUploadAssetFile = function() { - $scope.assetUploadData = { - name: '', - assetFile: null, - description: '', - creatorName: AuthenticationService.getUserId() - }; - }; - - - $scope.uploadAssetFile = function() { - $scope.importAsset($scope.assetUploadData.assetFile); - $scope.$close(true); - }; - - - $scope.cancelUploadAssetModal = function() { - $scope.$dismiss('cancel'); - }; - -} diff --git a/src/federation-hub-ui/src/main/webapp/modules/manage/manage_module.js b/src/federation-hub-ui/src/main/webapp/modules/manage/manage_module.js deleted file mode 100644 index b2276add..00000000 --- a/src/federation-hub-ui/src/main/webapp/modules/manage/manage_module.js +++ /dev/null @@ -1,2 +0,0 @@ - -angular.module('roger_federation.Manage', ['angular-growl']); \ No newline at end of file diff --git a/src/federation-hub-ui/src/main/webapp/modules/workflows/add_federate_group_controller.js b/src/federation-hub-ui/src/main/webapp/modules/workflows/add_federate_group_controller.js index cd28400d..73786a29 100644 --- a/src/federation-hub-ui/src/main/webapp/modules/workflows/add_federate_group_controller.js +++ b/src/federation-hub-ui/src/main/webapp/modules/workflows/add_federate_group_controller.js @@ -2,9 +2,9 @@ "use strict"; angular.module('roger_federation.Workflows') - .controller('AddFederateGroupController', ['$rootScope', '$scope', '$state', '$http', '$stateParams', '$modalInstance', '$timeout', '$log', '$cookieStore', 'growl', 'WorkflowTemplate', 'WorkflowService', 'OntologyService', 'JointPaper', addFederateGroupController]); + .controller('AddFederateGroupController', ['$rootScope', '$scope', '$state', '$http', '$stateParams', '$modalInstance', '$timeout', '$log', '$cookieStore', 'growl', 'WorkflowTemplate', 'WorkflowService', 'OntologyService', 'JointPaper', 'ConfigService', addFederateGroupController]); -function addFederateGroupController($rootScope, $scope, $state, $http, $stateParams, $modalInstance, $timeout, $log, $cookieStore, growl, WorkflowTemplate, WorkflowService, OntologyService, JointPaper) { +function addFederateGroupController($rootScope, $scope, $state, $http, $stateParams, $modalInstance, $timeout, $log, $cookieStore, growl, WorkflowTemplate, WorkflowService, OntologyService, JointPaper, ConfigService) { $scope.editorTitle = "Add"; $scope.editExisting = false; @@ -89,7 +89,7 @@ function addFederateGroupController($rootScope, $scope, $state, $http, $statePar if (file == null) { alert("Please provide a valid certificate file before submitting."); } else { - var uploadUrl = "/fig/addNewGroupCa"; + var uploadUrl = ConfigService.getServerBaseUrlStrV2() + "addNewGroupCa"; $scope.submitInProgress = true; diff --git a/src/federation-hub-ui/src/main/webapp/modules/workflows/add_policy_filter_controller.js b/src/federation-hub-ui/src/main/webapp/modules/workflows/add_policy_filter_controller.js deleted file mode 100644 index 88ae28d3..00000000 --- a/src/federation-hub-ui/src/main/webapp/modules/workflows/add_policy_filter_controller.js +++ /dev/null @@ -1,23 +0,0 @@ - -"use strict"; - -angular.module('roger_federation.Workflows') - .controller('AddPolicyFilterController', ['$rootScope', '$scope', '$stateParams', '$modalInstance', '$log', '$cookieStore', 'growl', 'WorkflowTemplate', 'WorkflowService', 'OntologyService', 'JointPaper', addPolicyFilterController]); - -function addPolicyFilterController($rootScope, $scope, $stateParams, $modalInstance, $log, $cookieStore, growl, WorkflowTemplate, WorkflowService, OntologyService, JointPaper) { - - $scope.initialize = function() { - }; - - $scope.submit = function() { - - }; - - $scope.cancel = function() { - $modalInstance.dismiss('cancel'); - }; - - - - -}; \ No newline at end of file diff --git a/src/federation-hub-ui/src/main/webapp/modules/workflows/asset_services.js b/src/federation-hub-ui/src/main/webapp/modules/workflows/asset_services.js deleted file mode 100644 index 77fced2b..00000000 --- a/src/federation-hub-ui/src/main/webapp/modules/workflows/asset_services.js +++ /dev/null @@ -1,130 +0,0 @@ - -'use strict'; - -angular.module('roger_federation.Workflows') - -.factory('AssetService', function($http, ConfigService) { - var service = {}; - - - /** - * Get Asset Filenames - * - * @return {Array} - */ - service.getAssetFilenames = function() { - return $http.get( - ConfigService.getServerBaseUrlStr() + 'asset/filenames').then( - function(res) { - return res.data; - }, function(reason) { - throw reason; - }); - }; - - /** - * Get Workflow Descriptors - * - * @return {Array} - */ - service.getAssetFileDescriptors = function() { - return $http.get( - ConfigService.getServerBaseUrlStr() + 'asset/descriptors').then( - function(res) { - return res.data; - }, function(reason) { - throw reason; - }); - }; - - /** - * Upload Asset - * - * @param {Object} - * content - * @return {Boolean} true - */ - service.uploadAssetFile = function(data) { - var fd = new FormData(); - fd.append('name', data.name); - fd.append('description', data.description); - fd.append('creatorName', data.creatorName); - fd.append('fileName', data.fileName); - fd.append('contents', data.contents); - - return $http({ - method: 'POST', - url: ConfigService.getServerBaseUrlStr() + 'asset', - data: fd, - transformRequest: angular.identity, - headers: { - 'Content-Type': undefined - }}).then(function(res) { - return res.headers('Location'); - }, function(reason) { - throw reason; - }); - - }; - - - /** - * Delete Asset File - * - * @param {Object} - * filename - * @return {Array} - */ - service.deleteAssetFile = function(filename) { - return $http.delete( - ConfigService.getServerBaseUrlStr() + 'asset/' + filename).then( - function(res) { - return res; - }, function(reason) { - throw reason; - }); - }; - - - /** - * Update Asset File - * - * @param {Object} - * content - * @return {Array} - */ - service.updateAssetFile = function(content) { - return $http({ - method: 'PUT', - url: ConfigService.getServerBaseUrlStr() + 'asset' , - data: JSON.stringify(content), - transformRequest: [], - headers: { - 'Content-Type': 'application/json' - }}).then(function(res) { - return res.data; - }, function(reason) { - throw reason; - }); - }; - - /** - * Get Asset File - * - * @param {Object} - * assetFileId - * @return {Array} - */ - service.getAssetFile = function(assetFileId) { - return $http.get( - ConfigService.getServerBaseUrlStr() + 'asset/' + assetFileId).then( - function(res) { - return res.data; - }, function(reason) { - throw reason; - }); - }; - - - return service; -}) diff --git a/src/federation-hub-ui/src/main/webapp/modules/workflows/attribute_expression_editor_controller.js b/src/federation-hub-ui/src/main/webapp/modules/workflows/attribute_expression_editor_controller.js deleted file mode 100644 index 5b85560d..00000000 --- a/src/federation-hub-ui/src/main/webapp/modules/workflows/attribute_expression_editor_controller.js +++ /dev/null @@ -1,481 +0,0 @@ - -"use strict"; - -angular.module('roger_federation.Workflows') -.controller('AttributeExpressionEditorController', - [ '$scope', '$rootScope', '$state', '$stateParams', '$http', '$uibModalInstance', '$uibModal', '$log', '$timeout', 'growl', 'ConfigService', 'WorkflowTemplate', 'WorkflowGraphFactory', 'RoleProductSetService', 'RolService', 'WorkflowService', attributeExpressionEditorController]); - -function attributeExpressionEditorController($scope, $rootScope, $state, $stateParams, $http, $uibModalInstance, $uibModal, $log, $timeout, growl, ConfigService, WorkflowTemplate, WorkflowGraphFactory, RoleProductSetService, RolService, WorkflowService) { - $rootScope.$state = $state; - $rootScope.$stateParams = $stateParams; - - $scope.mode = $stateParams.mode; - $scope.workflowId = $stateParams.workflowId; - - $scope.productData = []; - $scope.roleData = []; - - - var initOntologyNames = function() { - return $http.get(ConfigService.getServerBaseUrlStr() + 'ontology').then( - function(res) { - $scope.ontologies = res.data; - }, - function(reason) { - throw reason; - } - ); - }; - - // initialize role and ROL state - var initRolAndRole = function () { - var roleSetData = WorkflowTemplate.getRoleSetData(); - if (roleSetData === undefined) { - var rpSetId = WorkflowTemplate.getRoleProductSet(); - if (rpSetId !== undefined) { - RoleProductSetService.getRoleProductSet(rpSetId).then(function(result) { - WorkflowTemplate.setRoleSetData(result.roles); - $scope.roleData = flattenRoleProductData(result.roles); - }, function() { - growl.error("Failed to acquire RoleProduct Set Data."); - }); - - } else { - growl.error("Role/Product Set is Unknown!"); - } - } else { - $scope.roleData = flattenRoleProductData(roleSetData); - - $scope.productData = WorkflowTemplate.getProductSetData(); - - $scope.graphItemId = $stateParams.graphItemId; - - $log.debug('graphItemId: ' + $scope.graphItemId); - - WorkflowService.getWorkflowGraphItem($scope.graphItemId).then(function(graphItem) { - $log.debug('graph item'); - $log.debug(JSON.stringify(graphItem)); - - $scope.graphItem = graphItem; - - $scope.itemId = graphItem.item.id; - - $scope.roleName = graphItem.item.name; - - $scope.roleUri = graphItem.item.uri; - - $scope.ontologyName = graphItem.item.ontology.name; - - if (graphItem.rolExpressions.length > 0) { - $scope.rolStatement = graphItem.rolExpressions[0].statements; - } else { - $scope.rolStatement = ''; - } - - $log.debug('loaded rol statement: ' + $scope.rolStatement); - - $log.debug('about to parse rol statement: ' + $scope.rolStatement); - - // get this from the role, later - var program = RolService.parse($scope.rolStatement); - - $log.debug('rol parse complete. program:'); - $log.debug(program); - - var treeData = rolTreeToObject(program); - - $scope.data = treeData; - - $log.debug('treeData'); - $log.debug(treeData); - - }); - } - }; - - var flattenRoleProductData = function(rpData) { - var flatDataArray = []; - for(var i = 0; i < rpData.length; i++) { - flatDataArray.push({item: rpData[i].item}); - flatDataArray = flatDataArray.concat(flattenRoleProductData(rpData[i].children)); - } - - return flatDataArray; - } - - var initProductData = function () { - var productSetData = WorkflowTemplate.getProductSetData(); - if (productSetData === undefined) { - var apSetId = WorkflowTemplate.getRoleProductSet(); - if (apSetId !== undefined) { - RoleProductSetService.getRoleProductSet(apSetId).then(function(result) { - WorkflowTemplate.setProductSetData(result.products); - $scope.productData = flattenRoleProductData(result.products); - }, function(result) { - growl.error("Failed to acquire role-product set. Error: " + result.data.error); - }); - - } else { - growl.error("Role/Product Set is Unknown!"); - } - } else { - $scope.productData = flattenRoleProductData(productSetData); - } - }; - - $scope.initialize = function () { - - $log.debug('init attribute_expression_editor_controller'); - - $log.debug('scope'); - $log.debug($scope); - - $log.debug('state'); - $log.debug($scope.$state); - $log.debug('rootScope'); - $log.debug($rootScope); - - $log.debug('stateParams'); - $log.debug($stateParams); - - initOntologyNames(); - initRolAndRole(); - initProductData(); - }; - - $scope.submit = function() { - $uibModalInstance.close('ok'); - }; - - $scope.cancel = function() { - $uibModalInstance.dismiss('cancel'); - }; - - // angular-ui-tree related - - // remove node - $scope.remove = function (scope) { - scope.remove(); - updateRol(); - }; - - $scope.toggle = function (scope) { - scope.toggle(); - }; - - $scope.moveLastToTheBeginning = function () { - var a = $scope.data.pop(); - $scope.data.splice(0, 0, a); - }; - - // add node - $scope.newSubItem = function (scope) { - var nodeData = scope.$modelValue; - nodeData.nodes.push({ - type: 'attribute', - key: null, - value: null, - nodes: [] - }); - - updateRol(); - }; - - $scope.collapseAll = function () { - $scope.$broadcast('angular-ui-tree:collapse-all'); - }; - - $scope.expandAll = function () { - $scope.$broadcast('angular-ui-tree:expand-all'); - }; - - // using a ROL parse tree visitor implementation, convert a parse tree to a JavaScript object tree for the UI - function rolTreeToObject(program) { - - var result = [{ - type : 'attribute', - key : null, - value : null, - nodes: [] - }]; - - var rol = RolService.getRol(); - - // invariant: currentNode always points to the node that will be either the logical op node, or the attribute node - var currentNode = result[0]; - - // expression visitor implementation - function ExpressionVisitor() { - rol.RolVisitor.call(this); - } - - ExpressionVisitor.prototype = Object.create(rol.RolVisitor.prototype); - - ExpressionVisitor.prototype.visitProgram = function(ctx) { - console.log('visiting program'); - console.log(ctx); - - // visit each statement in the program - ctx.children.forEach(function(child) { - // call parent object vist, to generic visit children - ExpressionVisitor.prototype.visit(child); - }); - }; - - ExpressionVisitor.prototype.visitAssertion = function(ctx) { - console.log('visiting assertion'); - console.log(ctx); - - // recursive case (parenthetical expression) - - // 'match attribute' assertion case (leaf) - if (ctx.children !== null && ctx.children.length === 2) { - var assertionType = ctx.children[0].symbol.text; - $log.debug('assertionType: ' + assertionType); - - // only process "match attribute" assertions without siblings (no logops, just one attribute assertion) - if (assertionType === 'match attribute') { - // visit the parameter - ExpressionVisitor.prototype.visit(ctx.children[1]); - } - - // TODO: parentheses logic can go here -// else if (assertionType === '(') { -// currentNode.type = 'parentheses'; -// $log.debug('parentheses case'); -// // save parent ref -// var logopNode = currentNode; -// // create a node for each node in the parenthetical expression -// // create left child -// currentNode = makeAttributeNode(); -// logopNode.nodes.push(currentNode); -// // left sibling of logop node -// ExpressionVisitor.prototype.visit(ctx.parentCtx.children[0]); -// // create right child -// currentNode = makeAttributeNode(); -// logopNode.nodes.push(currentNode); -// // right sibling of logop node -// ExpressionVisitor.prototype.visit(ctx.parentCtx.children[2]); -// $log.debug('processing binary op' + op); -// break; -// } - } - - // do something with the assertion so that it can be rendered - }; - - ExpressionVisitor.prototype.visitLogop = function(ctx) { - console.log('visiting logop'); - console.log(ctx); - - console.log('tree:'); - $log.debug(JSON.stringify(result)); - - var op = ctx.children[0].symbol.text; - $log.debug('op: ' + op); - - switch(op) { - - // binary ops - case 'or': - case 'and': - - var left = shallowCopyNode(currentNode); - - // save parent ref - var logopNode = currentNode; - - logopNode.type = op; - logopNode.key = null; - logopNode.value = null; - logopNode.nodes = []; - - logopNode.nodes.push(left); - - // create right child - var right = makeAttributeNode(); - logopNode.nodes.push(right); - - // TODO: check this - should the current be the sibling of the logop node now instead? - currentNode = right; - - // right sibling of logop node - ExpressionVisitor.prototype.visit(ctx.parentCtx.children[2]); - - $log.debug('processing binary op' + op); - break; - - // unary op - case 'not': - - // TODO: revisit this logic - // create child - currentNode = makeAttributeNode(); - logopNode.nodes.push(currentNode); - // right sibling of logop node - ExpressionVisitor.prototype.visit(ctx.parentCtx.children[1]); - - $log.debug('processing unary op not'); - break; - default: - // unsupported op type - $log.debug('operator ' + op + ' not supported in ROL visitor'); - } - }; - - ExpressionVisitor.prototype.visitAssertions = function(ctx) { - console.log('visiting assertions'); - console.log(ctx); - - // visit all children - ctx.children.forEach(function(child) { - ExpressionVisitor.prototype.visit(child); - }); - }; - - ExpressionVisitor.prototype.visitStatement = function(ctx) { - console.log('visiting statement'); - - // visit all children - ctx.children.forEach(function(child) { - ExpressionVisitor.prototype.visit(child); - }); - }; - - ExpressionVisitor.prototype.visitParameter = function(ctx) { - $log.debug("visit parameter") - $log.debug(ctx); - - if (currentNode.type === 'attribute' && ctx.children.length === 3) { - $log.debug('param type'); - - var key = ctx.children[0].symbol.text; - - // try to parse as JSON, to unescape and remove quotes. This works because the IDENT and STRING productions in ROL are compatible with JSON. - try { - key = JSON.parse(key); - } catch (e) { } - - $log.debug('key: ' + key); - - var value = ctx.children[2].children[0].symbol.text; - - try { - value = JSON.parse(value); - } catch (e) { } - - $log.debug('value: ' + value); - - currentNode.key = key; - currentNode.value = value; - } - }; - - var ev = new ExpressionVisitor(); - - // execute the visitor, updating the UI tree progressively - ev.visitProgram(program); - - console.log('ev in controller'); - console.log(ev); - - return result; - }; - - function makeAttributeNode(key, value) { - return { - type: 'attribute', - key: key, - value: value, - nodes: [] - } - }; - - function shallowCopyNode(node) { - return { - type: node.type, - key: node.key, - value: node.value, - nodes: [] - } - } - - function treeToRol(uiTree) { - var rol = 'assign role ' + JSON.stringify($scope.roleUri) + ' '; - - // iterate and recurse through top-level array - uiTree.forEach(function(node) { - rol += treeNodeToRol(node, ''); - rol += ' '; - }); - - return rol + ' {};'; - } - - function treeNodeToRol(node, rol) { - switch(node.type) { - // base case - case 'attribute': - rol += 'match attribute ' + node.key + ' : ' + JSON.stringify(node.value); - break; - // recursive cases - case 'and': - case 'or': - // must have minimum operand count. If invalid, stop. - if (node.nodes.length > 1) { - - // recurse left - rol += treeNodeToRol(node.nodes[0], '') + ' '; - - // operation - rol += node.type + ' '; - - // recurse right - rol += treeNodeToRol(node.nodes[1], '') + ' '; - } - break; - case 'not': - if (node.nodes.length > 0) { - // operation - rol += ' node.type '; - - // recurse right - rol += treeNodeToRol(node.nodes[0], '') + ' '; - // recurse right - } - break; - default: - // unsupported operation - $log.debug('unsupported node type ' + node.type); - } - - return rol; - } - - // regenerate ROL from tree and save to server - // this could be optimized by only regenerating changed subtrees - var updateRol = function() { - var rol = treeToRol($scope.data) - $log.debug('rol after tree change'); - $log.debug(rol); - - // Set new ROL on graph item - $scope.graphItem.rolExpressions[0] = rol; - - var gi = $scope.graphItem; - - // replace item with its id, backend expects this - gi.item = $scope.itemId; - var giJson = JSON.stringify(gi); - $log.debug("graph item json to save: " + giJson); - // save to server - WorkflowService.updateWorkflowGraphItem($scope.graphItemId, giJson); - - $log.debug('graph item saved'); - } - - $scope.updateRol = updateRol; -}; - - - diff --git a/src/federation-hub-ui/src/main/webapp/modules/workflows/connections_controller.js b/src/federation-hub-ui/src/main/webapp/modules/workflows/connections_controller.js index d29981a6..e201aac4 100644 --- a/src/federation-hub-ui/src/main/webapp/modules/workflows/connections_controller.js +++ b/src/federation-hub-ui/src/main/webapp/modules/workflows/connections_controller.js @@ -13,15 +13,35 @@ function connectionsController($rootScope, $scope, $http, $stateParams, $modalIn $scope.filteredActiveConnections = []; $scope.selectedCa = $rootScope.selectedCa ? $rootScope.selectedCa : 'All'; $scope.knownCas = []; + $scope.flows = []; var cellView; pollActiveConnections() getCaGroups(); + pollDataFlows(); function filterActiveConnections() { let connections = [] $scope.activeConnections.forEach(activeConnection => { activeConnection.groupIdentitiesSet = new Set(activeConnection.groupIdentities) + activeConnection["bRead"] = 0 + activeConnection["reads"] = 0 + activeConnection["bWritten"] = 0 + activeConnection["writes"] = 0 + if($scope.flows != []){ + var _flows = $scope.flows; + for(var flow of _flows){ + console.log(activeConnection.connectionId); + console.log(flow["targetId"]); + if(flow["sourceId"] == activeConnection.connectionId){ + activeConnection["bWritten"] = activeConnection["bWritten"] + flow["bytesWritten"] + activeConnection["writes"] = activeConnection["writes"] + flow["messagesWritten"] + activeConnection["bRead"] = activeConnection["bRead"] + flow["bytesRead"] + activeConnection["reads"] = activeConnection["reads"] + flow["messagesRead"] + } + } + } + if ($scope.selectedCa === 'All') { connections.push(activeConnection) } else { @@ -32,6 +52,13 @@ function connectionsController($rootScope, $scope, $http, $stateParams, $modalIn $scope.filteredActiveConnections = connections } + function pollDataFlows() { + WorkflowService.getDataFlowStats().then(function(dataFlows) { + $scope.flows = dataFlows.channelInfos; + }); + $timeout(pollDataFlows, 2000); + } + function pollActiveConnections() { WorkflowService.getActiveConnections().then(function(activeConnections) { $scope.activeConnections = activeConnections diff --git a/src/federation-hub-ui/src/main/webapp/modules/workflows/display_tc_controller.js b/src/federation-hub-ui/src/main/webapp/modules/workflows/display_tc_controller.js deleted file mode 100644 index 07c73dfb..00000000 --- a/src/federation-hub-ui/src/main/webapp/modules/workflows/display_tc_controller.js +++ /dev/null @@ -1,25 +0,0 @@ - -"use strict"; - -angular.module('roger_federation.Workflows') - .controller('TemplateContainerController', ['$scope', '$state', '$stateParams', 'FileSaver', 'Blob', '$log', '$uibModalInstance', 'data', loadTemplateController]); - -function loadTemplateController($scope, $state, $stateParams, FileSaver, Blob, $log, $uibModalInstance, data) { - - $scope.jsonData = {}; - $scope.filename = 'template_container.json'; - - $scope.initialize = function() { - if (data !== undefined) { - $scope.jsonData = data; - } else { - $scope.jsonData = "{error: 'undefined'}"; - } - }; - - $scope.download = function() { - var data = new Blob([JSON.stringify($scope.jsonData)], {type: 'text/json:charset=utf-8'}); - FileSaver.saveAs(data, 'template_container.json'); - $uibModalInstance.close('ok'); - }; -} diff --git a/src/federation-hub-ui/src/main/webapp/modules/workflows/edit_attributes_controller.js b/src/federation-hub-ui/src/main/webapp/modules/workflows/edit_attributes_controller.js deleted file mode 100644 index af101526..00000000 --- a/src/federation-hub-ui/src/main/webapp/modules/workflows/edit_attributes_controller.js +++ /dev/null @@ -1,18 +0,0 @@ - -"use strict"; - -angular.module('fed') - .controller('EditAttributesController', function($scope, $modalInstance, $log, growl, attributes) { - - $scope.initialize = function() { - $scope.attributes = attributes; - }; - - $scope.submit = function() { - $modalInstance.close('ok'); - }; - - $scope.cancel = function() { - $modalInstance.dismiss('cancel'); - }; - }); diff --git a/src/federation-hub-ui/src/main/webapp/modules/workflows/home_controller.js b/src/federation-hub-ui/src/main/webapp/modules/workflows/home_controller.js new file mode 100644 index 00000000..68d20454 --- /dev/null +++ b/src/federation-hub-ui/src/main/webapp/modules/workflows/home_controller.js @@ -0,0 +1,42 @@ + +"use strict"; + +angular.module('roger_federation.Workflows') + .controller('HomeController', ['$scope', '$state', '$stateParams', 'growl', 'WorkflowService', homeController]); + +function homeController($scope, $state, $stateParams, growl, WorkflowService) { + $scope.rowCollection = []; + $scope.displayedCollection = []; + $scope.itemsByPage = 15; + $scope.selectedRowId = -1; + $scope.mode = $stateParams.mode; + $scope.diagramType = $stateParams.diagramType; + + var successFunc = function (workflowList) { + $scope.rowCollection = workflowList; + $scope.displayedCollection = [].concat($scope.rowCollection); + $scope.firstWF = workflowList[0] + var forwarded_already = localStorage.getItem("forwarded_dash"); + + if (forwarded_already != "true") { + if ($scope.firstWF == undefined) { + localStorage.setItem("forwarded_dash", true); + $state.go('federations-new'); + } + else if ($scope.firstWF != undefined && $scope.firstWF.name != undefined) { + localStorage.setItem("forwarded_dash", true); + $state.go('workflows.editor', { + workflowId: $scope.firstWF.name + }); + } + } + }; + + var failureFunc = function (result) { + growl.error("Failed getting workflow names. Error: " + result.data.error); + $scope.rowCollection.length = 0; + $scope.displayedCollection = [].concat($scope.rowCollection); + }; + + WorkflowService.getWorkflowDescriptors().then(successFunc, failureFunc); +} \ No newline at end of file diff --git a/src/federation-hub-ui/src/main/webapp/modules/workflows/new_workflow_controller.js b/src/federation-hub-ui/src/main/webapp/modules/workflows/new_workflow_controller.js index 8ed34c39..e56462a6 100644 --- a/src/federation-hub-ui/src/main/webapp/modules/workflows/new_workflow_controller.js +++ b/src/federation-hub-ui/src/main/webapp/modules/workflows/new_workflow_controller.js @@ -2,9 +2,9 @@ "use strict"; angular.module('roger_federation.Workflows') - .controller('NewWorkflowController', ['$scope', '$state', '$stateParams', '$modalInstance', '$log', 'growl', 'WorkflowService', 'AuthenticationService', newWorkflowController]); + .controller('NewWorkflowController', ['$scope', '$state', '$stateParams', '$modalInstance', '$log', 'growl', 'WorkflowService', newWorkflowController]); -function newWorkflowController($scope, $state, $stateParams, $modalInstance, $log, growl, WorkflowService, AuthenticationService) { +function newWorkflowController($scope, $state, $stateParams, $modalInstance, $log, growl, WorkflowService) { $scope.workflow = { name: '', version: '', @@ -24,7 +24,7 @@ function newWorkflowController($scope, $state, $stateParams, $modalInstance, $lo version: $scope.workflow.version, type: $scope.workflow.type, description: $scope.workflow.description, - creatorName: AuthenticationService.getUserId(), + creatorName: '', diagramType: $stateParams.diagramType, federationId: '', federationName: '' diff --git a/src/federation-hub-ui/src/main/webapp/modules/workflows/select_asset_controller.js b/src/federation-hub-ui/src/main/webapp/modules/workflows/select_asset_controller.js deleted file mode 100644 index ea37b192..00000000 --- a/src/federation-hub-ui/src/main/webapp/modules/workflows/select_asset_controller.js +++ /dev/null @@ -1,60 +0,0 @@ - -"use strict"; - -angular.module('roger_federation.Workflows') -.controller('SelectAssetController', - [ '$scope', '$state', '$modalInstance', '$log', 'growl', 'AssetService', selectAssetController ]); - -function selectAssetController($scope, $state, $modalInstance, $log, growl, AssetService) { - - $scope.rowCollection = []; - $scope.displayedCollection = []; - $scope.itemsByPage=15; - $scope.selectedRowId = -1; - - - $scope.initialize = function () { - AssetService.getAssetFilenames().then(function (AssetList) { - $scope.rowCollection = AssetList; - $scope.displayedCollection = [].concat($scope.rowCollection); - }, function(result) { - growl.error("Failed getting asset filenames. Error: " + result.data.error); - $scope.rowCollection.length = 0; - $scope.displayedCollection = [].concat($scope.rowCollection); - }); - }; - - - //fires when table rows are selected - $scope.$watch('displayedCollection', function (row) { - $scope.selectedRowId = -1; - //get selected row - row.filter(function (r) { - if (r.isSelected) { - $scope.selectedRowId = r.id; - } - }); - }, true); - - - $scope.submit = function () { - - var selectedAssetArray = $scope.displayedCollection.filter(function( row ) { - return row.id === $scope.selectedRowId; - }); - - var AssetFilename = selectedAssetArray[0]; - - $state.go('workflows.editor.instantiation', {workflowId : $state.params.workflowId }); - $modalInstance.close(AssetFilename); - $scope.$close(true); - }; - - $scope.cancel = function () { - $state.go('workflows.editor', {workflowId : $state.params.workflowId }); - $modalInstance.dismiss('cancel'); - }; -} - - - diff --git a/src/federation-hub-ui/src/main/webapp/modules/workflows/select_product_controller.js b/src/federation-hub-ui/src/main/webapp/modules/workflows/select_product_controller.js deleted file mode 100644 index c709b558..00000000 --- a/src/federation-hub-ui/src/main/webapp/modules/workflows/select_product_controller.js +++ /dev/null @@ -1,52 +0,0 @@ - -"use strict"; - -angular.module('roger_federation.Workflows') -.controller('SelectProductController', - [ '$scope', '$modalInstance', '$log', 'growl', 'WorkflowTemplate', 'WorkflowGraphFactory', 'RoleProductSetService', selectProductController ]); - -function selectProductController($scope, $modalInstance, $log, growl, WorkflowTemplate, WorkflowGraphFactory, RoleProductSetService) { - - $scope.treeOptions = { - nodeChildren: "children", - dirSelectable: true, - multiSelection : false - }; - - $scope.selectedNode = undefined; - $scope.productData = []; - - $scope.initialize = function () { - var productSetData = WorkflowTemplate.getProductSetData(); - if (productSetData === undefined) { - var apSetId = WorkflowTemplate.getRoleProductSet(); - if (apSetId !== undefined) { - RoleProductSetService.getRoleProductSet(apSetId).then(function (result) { - WorkflowTemplate.setProductSetData(result.products); - $scope.productData = result.products; - }, function(result) { - growl.error("Failed to acquire role-product set. Error: " + result.data.error); - }); - - } else { - growl.error("Role/Product Set is Unknown!"); - } - } else { - $scope.productData = productSetData; - } - }; - - - $scope.submit = function() { - RoleProductSetService.setLatestSelectedProduct($scope.selectedNode); - - $modalInstance.dismiss('ok'); - }; - - - $scope.cancel = function() { - $modalInstance.dismiss('cancel'); - }; - -}; - diff --git a/src/federation-hub-ui/src/main/webapp/modules/workflows/select_role_controller.js b/src/federation-hub-ui/src/main/webapp/modules/workflows/select_role_controller.js deleted file mode 100644 index 3832a333..00000000 --- a/src/federation-hub-ui/src/main/webapp/modules/workflows/select_role_controller.js +++ /dev/null @@ -1,54 +0,0 @@ - -"use strict"; - -angular.module('roger_federation.Workflows') -.controller('SelectRoleController', - [ '$scope', '$stateParams', '$modalInstance', '$log', 'growl', 'WorkflowTemplate', 'WorkflowGraphFactory', 'RoleProductSetService', selectRoleController ]); - -function selectRoleController($scope, $stateParams, $modalInstance, $log, growl, WorkflowTemplate, WorkflowGraphFactory, RoleProductSetService) { - - - $scope.treeOptions = { - nodeChildren: "children", - dirSelectable: true, - multiSelection : false - }; - - $scope.selectedNode = undefined; - $scope.roleData = []; - - - $scope.initialize = function () { - var roleSetData = WorkflowTemplate.getRoleSetData(); - if (roleSetData === undefined) { - var apSetId = WorkflowTemplate.getRoleProductSet(); - if (apSetId !== undefined) { - RoleProductSetService.getRoleProductSet(apSetId).then(function (result) { - WorkflowTemplate.setRoleSetData(result.roles); - $scope.roleData = result.roles; - }, function() { - growl.error("Failed to acquire RoleProduct Set Data."); - }); - - } else { - growl.error("Role/Product Set is Unknown!"); - } - } else { - $scope.roleData = roleSetData; - } - }; - - $scope.submit = function() { - RoleProductSetService.setLatestSelectedRole($scope.selectedNode); - - $modalInstance.close('ok'); - }; - - - $scope.cancel = function() { - $modalInstance.dismiss('cancel'); - }; - -}; - - diff --git a/src/federation-hub-ui/src/main/webapp/modules/workflows/workflow_services.js b/src/federation-hub-ui/src/main/webapp/modules/workflows/workflow_services.js index dd95aed2..52d871de 100644 --- a/src/federation-hub-ui/src/main/webapp/modules/workflows/workflow_services.js +++ b/src/federation-hub-ui/src/main/webapp/modules/workflows/workflow_services.js @@ -633,6 +633,17 @@ angular.module('roger_federation.Workflows') }); }; + workflowService.getDataFlowStats = function() { + return $http.get( + ConfigService.getServerBaseUrlStrV2() + 'getBrokerMetrics/').then( + function(res) { + return res.data; + }, + function(reason) { + throw reason; + }); + }; + workflowService.deleteGroupCa = function(ca) { return $http.delete( ConfigService.getServerBaseUrlStrV2() + 'deleteGroupCa/' + ca).then( diff --git a/src/federation-hub-ui/src/main/webapp/modules/workflows/workflows_controller.js b/src/federation-hub-ui/src/main/webapp/modules/workflows/workflows_controller.js index afad52f6..36776b69 100644 --- a/src/federation-hub-ui/src/main/webapp/modules/workflows/workflows_controller.js +++ b/src/federation-hub-ui/src/main/webapp/modules/workflows/workflows_controller.js @@ -3,17 +3,123 @@ 'use strict'; angular.module('roger_federation.Workflows') -.controller('WorkflowsController', ['$scope', '$rootScope', '$window', '$state', '$stateParams', '$timeout', '$uibModal', - '$log', '$http', 'uuid4', 'growl', 'WorkflowService', 'OntologyService', 'JointPaper', 'SemanticsService', workflowsController +.controller('WorkflowsController', ['$scope', '$rootScope', '$window', '$state', '$stateParams', '$interval', '$timeout', '$uibModal', + '$log', '$http', 'uuid4', 'growl', 'WorkflowService', 'OntologyService', 'JointPaper', workflowsController ]); -function workflowsController($scope, $rootScope, $window, $state, $stateParams, $timeout, $uibModal, $log, $http, uuid4, growl, WorkflowService, OntologyService, JointPaper, SemanticsService) { +function workflowsController($scope, $rootScope, $window, $state, $stateParams, $interval, $timeout, $uibModal, $log, $http, uuid4, growl, WorkflowService, OntologyService, JointPaper) { $scope.JointPaper = JointPaper; $scope.criticResults = []; $scope.data = "{users: 'Joe'}"; + $state.fkdi = "" + $state.tids = {} + $state.cache = {} + + function translateIds(){ + if (JointPaper.paper && JointPaper.paper._views) { + var nodeKeys = Object.keys(JointPaper.paper._views); + for (var i = 0; i < nodeKeys.length; i++) { + let cellView = JointPaper.paper._views[nodeKeys[i]] + if (cellView.model.attributes.graphType === "GroupCell") { + if (cellView.model.attributes.roger_federation.name in $state.tids) { + continue; + } + else { + $state.tids[cellView.model.attributes.id] = cellView.model.attributes.roger_federation.name; + } + } + } + } + } + + function getRandomInt(max) { + return Math.floor(Math.random() * max); + } + pollActiveConnections() + $timeout(AnimateDataFlow, getRandomInt(5000)) + + function pollDataFlowStats(){ + WorkflowService.getDataFlowStats().then(function(dataFlows) { + var timestamp = new Date().getTime(); + translateIds(); + animateDataFlows(dataFlows.channelInfos, timestamp) + }).catch(e => animateDataFlows([])) + } + + function findDataFlow(dataFlows, source, target){ + var decodedSource = $state.tids[source]; + var decodedTarget = $state.tids[target]; + for(var flow of dataFlows){ + if(flow["sourceCert"] == decodedSource && flow["targetCert"] == decodedTarget){ + return flow; + } + } + + return undefined; + } + + function animateDataFlows(dataFlows, timestamp){ + if(dataFlows == []) + return + + if (JointPaper.paper && JointPaper.paper._views) { + var nodeKeys = Object.keys(JointPaper.paper._views); + for (var i = 0; i < nodeKeys.length; i++) { + let cellView = JointPaper.paper._views[nodeKeys[i]] + + if (cellView.model.attributes.graphType === "EdgeCell") { + var thisFlow = findDataFlow(dataFlows, cellView.model.attributes.source.id, cellView.model.attributes.target.id); + var key = cellView.model.attributes.source.id + cellView.model.attributes.target.id; + var flow = 0; + + if(key in $state.cache){ + flow = thisFlow["messagesWritten"] - $state.cache[key]; + $state.cache[key] = thisFlow["messagesWritten"]; + } + else { + $state.cache[key] = thisFlow["messagesWritten"]; + } + if(thisFlow != undefined){ + drawFlow(flow, cellView) + cellView.update() + } + } + } + } + } + + function drawFlow(flow, cellView){ + if(flow > 0){ + cellView.model.attributes.attrs['.connection']['stroke'] = 'green' + cellView.model.attributes.attrs['.connection']["stroke-dasharray"] = "4,4" + cellView.model.attributes.attrs['.marker-target']['fill'] = 'green' + + if(flow < 3){ + cellView.model.attributes.attrs['.connection']['stroke-width'] = 3 + } + else if (flow > 6){ + cellView.model.attributes.attrs['.connection']['stroke-width'] = 6 + } + else { + cellView.model.attributes.attrs['.connection']['stroke-width'] = flow + } + } + else { + cellView.model.attributes.attrs['.connection']['stroke'] = 'DarkGray' + cellView.model.attributes.attrs['.connection']['stroke-width'] = 2 + cellView.model.attributes.attrs['.marker-target']['fill'] = 'DarkGray' + cellView.model.attributes.attrs['.connection']["stroke-dasharray"] = "" + } + } + + function AnimateDataFlow(){ + if($state.fkdi == ""){ + $state.fkdi = $interval(pollDataFlowStats, 5000); + } + } function pollActiveConnections() { WorkflowService.getActiveConnections().then(function(activeConnections) { @@ -27,7 +133,6 @@ function workflowsController($scope, $rootScope, $window, $state, $stateParams, } function setNodeStatus(activeConnections) { - // console.log(JointPaper.paper) if (JointPaper.paper && JointPaper.paper._views) { var nodeKeys = Object.keys(JointPaper.paper._views); for (var i = 0; i < nodeKeys.length; i++) { @@ -38,7 +143,6 @@ function workflowsController($scope, $rootScope, $window, $state, $stateParams, + cellView.model.attributes.roger_federation.host + ':' + cellView.model.attributes.roger_federation.port let foundConnection = undefined - activeConnections.forEach(activeConnection => { if (activeConnection.connectionId === cellView.model.attributes.roger_federation.name) foundConnection = activeConnection diff --git a/src/federation-hub-ui/src/main/webapp/package.json b/src/federation-hub-ui/src/main/webapp/package.json new file mode 100644 index 00000000..feb42762 --- /dev/null +++ b/src/federation-hub-ui/src/main/webapp/package.json @@ -0,0 +1,18 @@ +{ + "name": "webapp", + "version": "1.0.0", + "description": "TakServer Fed Hub UI", + "main": "index.html", + "directories": { + "lib": "lib" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "bbn", + "license": "ISC", + "dependencies": { + "express": "^4.18.2", + "http-proxy-middleware": "^2.0.6" + } +} diff --git a/src/federation-hub-ui/src/main/webapp/scripts/app.js b/src/federation-hub-ui/src/main/webapp/scripts/app.js index 1ba5794e..3147de50 100644 --- a/src/federation-hub-ui/src/main/webapp/scripts/app.js +++ b/src/federation-hub-ui/src/main/webapp/scripts/app.js @@ -30,14 +30,9 @@ angular 'smart-table', 'treeControl', 'ncy-angular-breadcrumb', - 'roger_federation.Core', 'roger_federation.Config', - 'roger_federation.Authentication', - 'roger_federation.Manage', 'roger_federation.Workflows', 'roger_federation.OntologyService', - 'roger_federation.CommanderDashboard', - 'roger_federation.Logout', 'angular-growl', 'uuid4', 'ui.bootstrap.datetimepicker', @@ -158,11 +153,10 @@ angular .config(['$stateProvider', 'modalStateProvider', '$urlRouterProvider', '$breadcrumbProvider', function($stateProvider, modalStateProvider, $urlRouterProvider, $breadcrumbProvider) { - $breadcrumbProvider.setOptions({ prefixStateName: 'home' }); - + $stateProvider // Application States .state('home', { @@ -170,10 +164,8 @@ angular ncyBreadcrumb: { label: 'TAK Server Federation Hub' }, - templateUrl: "views/dashboard/dashboard.html" -// data: { -//// requireLogin: true -// } + templateUrl: "views/dashboard/dashboard.html", + controller: "HomeController" }) .state('about', { url: "/about", @@ -181,30 +173,7 @@ angular label: 'About' }, templateUrl: "views/about.html" -// data: { -// requireLogin: true -// } - }) - .state('help', { - url: "/help", - ncyBreadcrumb: { - label: 'Help' - }, - templateUrl: "views/help.html" -// data: { -//// requireLogin: true -// } - }) - .state('config', { - url: "/config", - ncyBreadcrumb: { - label: 'Config' - }, - templateUrl: "views/config/config.html", - controller: 'ConfigController' -// data: { -//// requireLogin: true -// } + }) // Template States .state('workflows', { @@ -212,9 +181,7 @@ angular url: "/workflows", templateUrl: "views/workflows/workflows.html", controller: 'WorkflowsController' -// data: { -// requireLogin: true -// } + }) .state('workflows.editor', { url: "/editor/{workflowId}", @@ -224,125 +191,7 @@ angular templateUrl: "views/workflows/workflow_editor.html", controller: "WorkflowsController" }) - .state('workflows.roles', { - url: "/roles", - ncyBreadcrumb: { - label: 'Workflow Roles' - }, - templateUrl: "views/workflows/workflow_roles.html" - }) - .state('workflows.subscriptions', { - url: "/subscriptions", - ncyBreadcrumb: { - label: 'Workflow Subscriptions' - }, - templateUrl: "views/workflows/workflow_subscriptions.html" - }) - - // // Instances States - // .state('instances', { - // abstract: true, - // url: "/instances", - // templateUrl: "views/instance/instances.html", - // controller: 'InstancesController', - // data: { - // requireLogin: true - // } - // }) - // .state('instances.editor', { - // url: "/editor/{instanceId}", - // ncyBreadcrumb: { - // label: 'Instance Editor' - // }, - // templateUrl: "views/instance/instance_editor.html", - // controller: 'InstancesController' - // }) - // - // // Workflow Sets States - // .state('workflow-sets', { - // abstract: true, - // url: "/workflow-sets", - // templateUrl: "views/workflow-sets/workflow_sets.html", - // controller: 'WorkflowSetsController', - // data: { - // requireLogin: true - // } - // }) - // - // .state('workflow-sets.editor', { - // url: "/editor/{workflowSetId}", - // ncyBreadcrumb: { - // label: 'Workflow Sets Editor' - // }, - // templateUrl: "views/workflow-sets/workflow_sets_editor.html", - // controller: 'WorkflowSetsController' - // }) - // - // // Role Product Set States - // .state('role-product-set', { - // abstract: true, - // url: "/role-product-set", - // templateUrl: "views/role-product-sets/role_product_set.html", - // controller: 'RoleProductSetController', - // data: { - // requireLogin: true - // } - // }) - // - // .state('role-product-set.editor', { - // url: "/editor/{roleProductSetId}", - // ncyBreadcrumb: { - // label: 'Role Product Set Editor' - // }, - // templateUrl: "views/role-product-sets/role_product_set_editor.html", - // controller: 'RoleProductSetController' - // }) - - //Commander Dashboard - .state('commander-dashboard', { - abstract: true, - url: "/commander-dashboard", - templateUrl: "views/commander-dashboard/commander_dashboard.html", - controller: 'CommanderDashboardController' -// data: { -// requireLogin: true -// } - }) - - .state('commander-dashboard.editor', { - url: "/editor/{CommanderDashboardId}", - ncyBreadcrumb: { - label: 'Commander Dashboard' - }, - templateUrl: "views/commander-dashboard/commander_dashboard.html", - controller: 'CommanderDashboardController' - }) - - // Manage States - .state('manage', { - abstract: true, - url: "/manage", - templateUrl: "views/manage/manage.html", - controller: 'ManageController' -// data: { -// requireLogin: true -// } - }) - // .state('manage.role-sets', { - // url: "/role-sets", - // ncyBreadcrumb: { - // label: 'Manage Role/Product Sets' - // }, - // templateUrl: "views/manage/manage_role_sets.html" - // }) - // .state('manage.diagrams', { - // url: "/workflows", - // ncyBreadcrumb: { - // label: 'Manage Diagrams' - // }, - // templateUrl: "views/manage/manage_workflows.html" - // }) .state('manage.federations', { url: "/federations", ncyBreadcrumb: { @@ -350,62 +199,6 @@ angular }, templateUrl: "views/manage/manage_federations.html" }) - // .state('manage.workflow-sets', { - // url: "/workflow-sets", - // ncyBreadcrumb: { - // label: 'Manage Workflow Linkage Files' - // }, - // templateUrl: "views/manage/manage_workflow_sets.html" - // }) - // .state('manage.instances', { - // url: "/instances", - // ncyBreadcrumb: { - // label: 'Manage Instances' - // }, - // templateUrl: "views/manage/manage_instances.html" - // }) - // .state('manage.assets', { - // url: "/assets", - // ncyBreadcrumb: { - // label: 'Manage Assets' - // }, - // templateUrl: "views/manage/manage_assets.html" - // }) - // .state('manage.assets.upload', { - // url: "/assets/upload", - // data: { - // requireLogin: true - // }, - // onEnter: ['$stateParams', '$state', '$log', '$uibModal', function($stateParams, $state, $log, $uibModal) { - // var modalInstance = $uibModal.open({ - // templateUrl: "views/assets/upload_asset_file.html", - // controller: 'ManageController' - // }); - // - // modalInstance.result.then(function() { - // $state.go('manage.assets'); - // }, function() { - // $state.go('manage.assets'); - // }); - // }] - // - // }) - // .state('manage.defaults', { - // url: "/defaults", - // ncyBreadcrumb: { - // label: 'Manage Defaults' - // }, - // templateUrl: "views/manage/manage_defaults.html" - // }) - // Monitor States - .state('monitor', { - abstract: true, - url: "/monitor", - template: '' -// data: { -// requireLogin: true -// } - }) // New Workflow .state('diagrams-new', { @@ -415,9 +208,7 @@ angular value: "Workflow" } }, -// data: { -// requireLogin: true -// }, + onEnter: ['$stateParams', '$state', '$log', '$uibModal', function($stateParams, $state, $log, $uibModal) { var modalInstance = $uibModal.open({ backdrop: 'static', @@ -437,9 +228,7 @@ angular // Load Workflow .state('diagrams-load', { url: '/load_workflow', -// data: { -// requireLogin: true -// }, + onEnter: ['$stateParams', '$state', '$log', '$uibModal', function($stateParams, $state, $log, $uibModal) { var modalInstance = $uibModal.open({ backdrop: 'static', @@ -472,9 +261,7 @@ angular value: "Federation" } }, -// data: { -// requireLogin: true -// }, + onEnter: ['$stateParams', '$state', '$log', '$uibModal', function($stateParams, $state, $log, $uibModal) { var modalInstance = $uibModal.open({ backdrop: 'static', @@ -494,9 +281,7 @@ angular // Load Federation .state('federations-load', { url: '/load_federation', -// data: { -// requireLogin: true -// }, + onEnter: ['$stateParams', '$state', '$log', '$uibModal', function($stateParams, $state, $log, $uibModal) { var modalInstance = $uibModal.open({ backdrop: 'static', @@ -529,9 +314,7 @@ angular value: false } }, -// data: { -// requireLogin: true -// }, + onEnter: ['$stateParams', '$state', '$log', '$uibModal', function($stateParams, $state, $log, $uibModal) { var modalInstance = $uibModal.open({ backdrop: 'static', @@ -548,365 +331,11 @@ angular }] }) - // New Instance - .state('instances-new', { - url: '/new_instance', -// data: { -// requireLogin: true -// }, - onEnter: ['$stateParams', '$state', '$log', '$uibModal', function($stateParams, $state, $log, $uibModal) { - var modalInstance = $uibModal.open({ - backdrop: 'static', - keyboard: false, - templateUrl: "views/instance/new_instance.html", - controller: "NewInstanceController" - }); - - modalInstance.result.then(function() { - - }, function() { - $state.go('home'); - }); - }] - }) - - // Load Instance - .state('instances-load', { - url: '/load_instance', -// data: { -// requireLogin: true -// }, - onEnter: ['$stateParams', '$state', '$log', '$uibModal', function($stateParams, $state, $log, $uibModal) { - var modalInstance = $uibModal.open({ - backdrop: 'static', - keyboard: false, - templateUrl: "views/instance/load_instance.html", - controller: 'LoadInstanceController', - size: 'lg' - }); - modalInstance.result.then(function() { - - }, function() { - $state.go('home'); - }); - }] - }) -// -// // New Instance From Inside Editor -// .state('instances.editor.instances-new', { -// url: '/new_instance', -// data: { -// requireLogin: true -// }, -// onEnter: ['$stateParams', '$state', '$log', '$uibModal', function($stateParams, $state, $log, $uibModal) { -// var modalInstance = $uibModal.open({ -// backdrop: 'static', -// keyboard: false, -// templateUrl: "views/instance/new_instance.html", -// controller: "NewInstanceController" -// }); -// -// modalInstance.result.then(function() { -// -// }, function() { -// $state.go('^'); -// }); -// }] -// }) -// -// // Load Instance From Inside Editor -// .state('instaces.editor.instances-load', { -// url: '/load_instance', -// data: { -// requireLogin: true -// }, -// onEnter: ['$stateParams', '$state', '$log', '$uibModal', function($stateParams, $state, $log, $uibModal) { -// var modalInstance = $uibModal.open({ -// backdrop: 'static', -// keyboard: false, -// templateUrl: "views/instance/load_instance.html", -// controller: 'LoadInstanceController', -// size: 'lg' -// }); -// modalInstance.result.then(function() { -// -// }, function() { -// $state.go('^'); -// }); -// }] -// }) -// -// -// // New Workflow Sets -// .state('workflow-sets-new', { -// url: '/new_workflow_Linkage', -// data: { -// requireLogin: true -// }, -// onEnter: ['$stateParams', '$state', '$log', '$uibModal', function($stateParams, $state, $log, $uibModal) { -// var modalInstance = $uibModal.open({ -// backdrop: 'static', -// keyboard: false, -// templateUrl: "views/workflow-sets/new_workflow_sets.html", -// controller: "NewWorkflowSetsController" -// }); -// -// modalInstance.result.then(function() { -// -// }, function() { -// $state.go('home'); -// }); -// }] -// }) -// -// // Load Workflow Sets -// .state('workflow-sets-load', { -// url: '/load_workflow_sets', -// data: { -// requireLogin: true -// }, -// onEnter: ['$stateParams', '$state', '$log', '$uibModal', function($stateParams, $state, $log, $uibModal) { -// var modalInstance = $uibModal.open({ -// templateUrl: "views/workflow-sets/load_workflow_sets.html", -// controller: 'LoadWorkflowSetsController', -// backdrop: 'static', -// keyboard: false, -// size: 'lg' -// }); -// modalInstance.result.then(function() { -// -// }, function() { -// $state.go('home'); -// }); -// }] -// }) -// -// // New Workflow Sets From Inside Editor -// .state('workflow-sets.editor.workflow-sets-new', { -// url: '/new_workflow_Linkage', -// data: { -// requireLogin: true -// }, -// onEnter: ['$stateParams', '$state', '$log', '$uibModal', function($stateParams, $state, $log, $uibModal) { -// var modalInstance = $uibModal.open({ -// backdrop: 'static', -// keyboard: false, -// templateUrl: "views/workflow-sets/new_workflow_sets.html", -// controller: "NewWorkflowSetsController" -// }); -// -// modalInstance.result.then(function() { -// -// }, function() { -// $state.go('^'); -// }); -// }] -// }) -// -// // Load Workflow Sets From Inside Editor -// .state('workflow-sets.editor.workflow-sets-load', { -// url: '/load_workflow_sets', -// data: { -// requireLogin: true -// }, -// onEnter: ['$stateParams', '$state', '$log', '$uibModal', function($stateParams, $state, $log, $uibModal) { -// var modalInstance = $uibModal.open({ -// templateUrl: "views/workflow-sets/load_workflow_sets.html", -// controller: 'LoadWorkflowSetsController', -// backdrop: 'static', -// keyboard: false, -// size: 'lg' -// }); -// modalInstance.result.then(function() { -// -// }, function() { -// $state.go('^'); -// }); -// }] -// }) -// -// -// // New Role-Product Sets -// .state('role-product-set-new', { -// url: '/new_role_product_set', -// data: { -// requireLogin: true -// }, -// onEnter: ['$stateParams', '$state', '$log', '$uibModal', function($stateParams, $state, $log, $uibModal) { -// var modalInstance = $uibModal.open({ -// backdrop: 'static', -// keyboard: false, -// templateUrl: "views/role-product-sets/new_role_product_set.html", -// controller: "NewRoleProductSetController" -// }); -// -// modalInstance.result.then(function() { -// -// }, function() { -// $state.go('home'); -// }); -// }] -// }) -// -// // Load Role-Product Sets -// .state('role-product-set-load', { -// url: '/load_role_product_set', -// data: { -// requireLogin: true -// }, -// onEnter: ['$stateParams', '$state', '$log', '$uibModal', function($stateParams, $state, $log, $uibModal) { -// var modalInstance = $uibModal.open({ -// templateUrl: "views/role-product-sets/load_role_product_set.html", -// controller: 'LoadRoleProductSetsController', -// backdrop: 'static', -// keyboard: false, -// size: 'lg' -// }); -// modalInstance.result.then(function() { -// -// }, function() { -// $state.go('home'); -// }); -// }] -// }) -// -// -// // New Role-Product Sets From Inside Editor -// .state('role-product-set.editor.role-product-set-new', { -// url: '/new_role_product_set', -// data: { -// requireLogin: true -// }, -// onEnter: ['$stateParams', '$state', '$log', '$uibModal', function($stateParams, $state, $log, $uibModal) { -// var modalInstance = $uibModal.open({ -// backdrop: 'static', -// keyboard: false, -// templateUrl: "views/role-product-sets/new_role_product_set.html", -// controller: "NewRoleProductSetController" -// }); -// -// modalInstance.result.then(function() { -// -// }, function() { -// $state.go('^'); -// }); -// }] -// }) -// -// // Load Role-Product Sets From Inside Editor -// .state('role-product-set.editor.role-product-set-load', { -// url: '/load_role_product_set', -// data: { -// requireLogin: true -// }, -// onEnter: ['$stateParams', '$state', '$log', '$uibModal', function($stateParams, $state, $log, $uibModal) { -// var modalInstance = $uibModal.open({ -// templateUrl: "views/role-product-sets/load_role_product_set.html", -// controller: 'LoadRoleProductSetsController', -// backdrop: 'static', -// keyboard: false, -// size: 'lg' -// }); -// modalInstance.result.then(function() { -// -// }, function() { -// $state.go('^'); -// }); -// }] -// }) - - - // Select Asset File - /* - .state('workflows.editor.instantiate', { - url: '/instantiate', - - onEnter: ['$stateParams', '$state', '$log', '$uibModal', function($stateParams, $state, $log, $uibModal) { - var modalInstance = $uibModal.open({ - templateUrl: "views/select_asset_file.html", - controller: 'SelectAssetController', - size : 'lg'}); - modalInstance.result.then(function() { - - }, function () { - $state.go('workflows.editor', { workflowId: $stateParams.workflowId } ); - }); - }] - }) - */ -// .state('login', { -// url: '/login', -//// data: { -//// requireLogin: false -//// }, -// onEnter: ['$stateParams', '$state', '$log', '$uibModal', function($stateParams, $state, $log, $uibModal) { -// var modalInstance = $uibModal.open({ -// backdrop: 'static', -// keyboard: false, -// templateUrl: 'views/authentication/login.html', -// controller: 'AuthenticationController', -// size: 'sm' -// }); -// modalInstance.result.then(function() { -// $state.go('home'); -// }, function() { -// $state.go('login'); -// }); -// }] -// }) -// -// .state('logout', { -// url: '/logout', -//// data: { -//// requireLogin: false -//// }, -// onEnter: ['$stateParams', '$state', '$log', '$uibModal', function($stateParams, $state, $log, $uibModal) { -// var modalInstance = $uibModal.open({ -// backdrop: 'static', -// keyboard: false, -// templateUrl: 'views/authentication/logout.html', -// controller: 'LogoutController', -// size: 'sm' -// }); -// modalInstance.result.then(function() { -// $state.go('login'); -// }, function() { -// $state.go('home'); -// }); -// }] -// }); - - // Send to login if the URL was not found //$urlRouterProvider.otherwise("/login"); $urlRouterProvider.otherwise("home"); // Workflow Modal Choosers - modalStateProvider.state('workflows.editor.selectRole', { - backdrop: 'static', - keyboard: false, - url: '/selectRole', - templateUrl: "views/workflows/select_role.html", - controller: 'SelectRoleController' - }); - - modalStateProvider.state('workflows.editor.selectProduct', { - backdrop: 'static', - keyboard: false, - url: '/selectProduct', - templateUrl: "views/workflows/select_product.html", - controller: 'SelectProductController' - }); - - modalStateProvider.state('workflows.editor.addRoleProduct', { - backdrop: 'static', - keyboard: false, - size: 'xxl', - url: '/addRoleProduct/{roleProductSetId}', - templateUrl: "views/workflows/add_role_product.html", - controller: 'AddRoleProductController' - }); - modalStateProvider.state('workflows.editor.addBPMNFederate', { backdrop: 'static', keyboard: false, @@ -960,132 +389,6 @@ angular templateUrl: "views/workflows/add_bpmn_federate_policy.html", controller: 'AddBpmnFederatePolicyController' }); - - modalStateProvider.state('workflows.editor.addBPMNFederatePolicy.addPolicyFilter', { - backdrop: 'static', - keyboard: false, - size: 'md', - url: '/addPolicyFilter', - templateUrl: "views/workflows/add_policy_filter.html", - controller: 'AddPolicyFilterController' - }); - - modalStateProvider.state('workflows.editor.addOntologyElement', { - backdrop: 'static', - keyboard: false, - size: 'xl', - url: '/addOntologyElement', - templateUrl: "views/workflows/add_ontology_element.html", - controller: 'AddOntologyElementController' - }); - - modalStateProvider.state('workflows.editor.rpset', { - // backdrop : 'static', - keyboard: false, - size: 'xxl', - // url: "/rpset/{roleProductSetId}/dataset/{initialDataSet}", - url: "/rpset/{roleProductSetId}/dataset/{initialDataSet}/class/{initialClass}", - //ncyBreadcrumb: {label: 'Role Product Set Editor'}, - templateUrl: "views/role-product-sets/role_product_set_editor.html", - controller: 'RoleProductSetController' - }); - - modalStateProvider.state('workflows.editor.addBPMNSubProcess', { - backdrop: 'static', - keyboard: false, - url: '/addBPMNSubProcess', - size: 'lg', - templateUrl: "views/workflows/add_bpmn_subprocess.html", - controller: 'AddBPMNSubProcessController' - }); - - modalStateProvider.state('workflows.editor.addBPMNTransition', { - backdrop: 'static', - keyboard: false, - url: '/addTransition', - templateUrl: "views/workflows/add_bpmn_transition.html", - controller: 'AddBPMNTransitionController' - }); - - modalStateProvider.state('workflows.editor.addSparqlQuery', { - backdrop: 'static', - params: { - mode: { - value: "new_request", - squash: false - }, - roleProductSetId: { - value: "" - } - }, - keyboard: false, - url: '/addSparqlQuery?mode&roleProductSetId', - templateUrl: "views/workflows/add_sparql_query.html", - controller: 'AddSparqlQueryController' - }); - - modalStateProvider.state('workflows.editor.lifecycleCommand', { - backdrop: 'static', - keyboard: false, - url: '/lifecycleCommand/{lifecycleEventId:int}', - templateUrl: "views/workflows/lifecycle_command.html", - controller: 'LifecycleCommandController' - }); - -// // Workflow Sets Modal Choosers -// modalStateProvider.state('workflow-sets.editor.addWorkflow', { -// backdrop: 'static', -// keyboard: false, -// url: '/addWorkflow', -// templateUrl: "views/workflow-sets/add_workflow.html", -// controller: 'AddWorkflowController' -// }); -// -// modalStateProvider.state('workflow-sets.editor.addWorkflowSet', { -// backdrop: 'static', -// keyboard: false, -// url: '/addWorkflowSet', -// templateUrl: "views/workflow-sets/add_workflow_set.html", -// controller: 'AddWorkflowSetController' -// }); -// -// modalStateProvider.state('workflow-sets.editor.addTransition', { -// backdrop: 'static', -// keyboard: false, -// url: '/addTransition/{sourceId:int}/{targetId:int}', -// templateUrl: "views/workflow-sets/add_workflow_transition.html", -// controller: 'AddWorkflowTransitionController' -// }); -// -// modalStateProvider.state('workflow-sets.editor.modifyTransition', { -// backdrop: 'static', -// keyboard: false, -// url: '/modifyTransition/{transitionId:int}', -// templateUrl: "views/workflow-sets/add_workflow_transition.html", -// controller: 'AddWorkflowTransitionController' -// }); -// -// modalStateProvider.state('workflow-sets.editor.lifecycleEvents', { -// backdrop: 'static', -// keyboard: false, -// url: '/lifecycleEvents/{workflowSetGraphItemId:int}/{entityType}', -// templateUrl: "views/workflow-sets/lifecycle_events.html", -// controller: 'LifecycleEventsController' -// }); -// -// modalStateProvider.state('workflows.editor.editRoleAttributeExpressions', { -// backdrop: 'static', -// params: { -// mode: { -// value: "new_request", -// squash: false -// } -// }, -// keyboard: false, -// url: '/attributeExpressions/:graphItemId', -// templateUrl: "views/workflows/attribute_expression_editor.html", -// controller: 'AttributeExpressionEditorController' -// }); } ]) @@ -1093,35 +396,6 @@ angular JSONFormatterConfigProvider.hoverPreviewEnabled = true; }) -// -//.run(function($rootScope, $state, AUTH_EVENTS, ACCESS_EVENTS, growl, AuthenticationService) { -// -// $rootScope.$on(ACCESS_EVENTS.accessFailed, function(event, result) { -// var url = ""; -// if (typeof result.config.url !== "undefined") { -// url = "
Url: " + result.config.url; -// } -// growl.error("Unable to connect to server." + url); -// event.preventDefault(); -// $state.go('config'); -// }); -// -// -// $rootScope.$on('$stateChangeStart', function(event, next) { -// if (next.name === "home") { //Hide scroll bars on all views except the Home page. -// document.body.style.overflow = "auto"; -// } else { -// document.body.style.overflow = "hidden"; -// } -// var requireLogin = next.data.requireLogin; -// if (requireLogin && !AuthenticationService.isAuthenticated()) { -// event.preventDefault(); -// // user is not logged in -// $state.go("login"); -// } -// }); -// -//}); function setContainerSize() { try { diff --git a/src/federation-hub-ui/src/main/webapp/server.js b/src/federation-hub-ui/src/main/webapp/server.js new file mode 100644 index 00000000..60b61862 --- /dev/null +++ b/src/federation-hub-ui/src/main/webapp/server.js @@ -0,0 +1,21 @@ +const express = require('express') +const { createProxyMiddleware } = require('http-proxy-middleware'); +const app = express(); +const port = 3000; +var path = require("path"); + +// these need to go first: +app.use("/images", express.static(__dirname + "/images")); +app.use("/scripts", express.static(__dirname + "/scripts")); +app.use("/modules", express.static(__dirname + "/modules")); +app.use("/styles", express.static(__dirname + "/styles")); +app.use("/lib", express.static(__dirname + "/lib")); +app.use("/bowerDependencies", express.static(__dirname + "/bowerDependencies")); +app.use("/views", express.static(__dirname + "/views")); +//app.use("/fig/**", createProxyMiddleware({ target: 'https://[::1]:9100', changeOrigin: true, secure: false })); + +app.get('/',function(req,res){ + res.sendFile(path.join(__dirname+'/home.html')); + }); + +app.listen(port, () => console.log(`Example app listening on port ${port}!`)) \ No newline at end of file diff --git a/src/federation-hub-ui/src/main/webapp/styles/main.css b/src/federation-hub-ui/src/main/webapp/styles/main.css index 86827a71..497b5a4d 100644 --- a/src/federation-hub-ui/src/main/webapp/styles/main.css +++ b/src/federation-hub-ui/src/main/webapp/styles/main.css @@ -279,3 +279,44 @@ tr.angular-ui-tree-empty { background-color: white; color: white; } + +.legend { + font-size: 18px; + position: absolute; + bottom: 0px; + width: 221px; + background-color: white; + padding: 10px; + border-radius: 5px; + background-color: #337ab7; + color: white; +} + +.legendItem { + display: flex; + display: -ms-flexbox; + background-color: white; + color: black; + align-items: center; + padding: 10px; + font-size: 13px; +} + +.legendLine { + border-bottom: 5px dashed green; + width: 40%; + height: 25%; + margin-left: 5px; +} + +.legenedConnected { + border-bottom: 5px solid rgb(0, 128, 0, .3) +} + +.legendActive { + border-bottom: 5px dashed green; +} + +.legendNotActive { + border-bottom: 5px solid DarkGray; +} diff --git a/src/federation-hub-ui/src/main/webapp/views/authentication/login.html b/src/federation-hub-ui/src/main/webapp/views/authentication/login.html deleted file mode 100644 index 804bbe8c..00000000 --- a/src/federation-hub-ui/src/main/webapp/views/authentication/login.html +++ /dev/null @@ -1,46 +0,0 @@ - - diff --git a/src/federation-hub-ui/src/main/webapp/views/authentication/logout.html b/src/federation-hub-ui/src/main/webapp/views/authentication/logout.html deleted file mode 100644 index b99ad2d9..00000000 --- a/src/federation-hub-ui/src/main/webapp/views/authentication/logout.html +++ /dev/null @@ -1,11 +0,0 @@ - - - - diff --git a/src/federation-hub-ui/src/main/webapp/views/commander-dashboard/commander_dashboard.html b/src/federation-hub-ui/src/main/webapp/views/commander-dashboard/commander_dashboard.html deleted file mode 100644 index 08a93ac5..00000000 --- a/src/federation-hub-ui/src/main/webapp/views/commander-dashboard/commander_dashboard.html +++ /dev/null @@ -1,221 +0,0 @@ -
-
- - - - -
- -
-

Instantiated Workflows

- - - - - - - - - - - - - - - - - - - - - - - - - -
Creation DateInstance NameInstance DescriptionWorkflow NameWorkflow Description
{{instanceRow.creationDate | date:'MM/dd/yyyy HH:mm:ss'}}{{instanceRow.name}}{{instanceRow.description}}{{instanceRow.workflow.name}}{{instanceRow.workflow.description}}
-
-
-
-
- - - {{gantt.options.zoom}}x - - -
-
- - - - - - - - - - -
-
-
-
-
-
-
-
- -
{{activeInstance.workflow.name}} - {{activeInstance.creationDate| date:'MM/dd/yyyy HH:mm:ss'}}
-
- -
-
- -
-
- -
- - - - - - - - -
- -
-
-
-
- - - - -
- -
-

Tags for {{getUriLabel(selectedInstanceTag.product)}}

-
- - - - - - - - - - - - - -
TagURI
{{getUriLabel(tag)}}{{tag}}
-
-
-
- - -
- -
diff --git a/src/federation-hub-ui/src/main/webapp/views/config/config.html b/src/federation-hub-ui/src/main/webapp/views/config/config.html deleted file mode 100644 index 2972472a..00000000 --- a/src/federation-hub-ui/src/main/webapp/views/config/config.html +++ /dev/null @@ -1,39 +0,0 @@ -
-
-
-

Configuration Management

-
- -
- -
-
-
AMT Server Connection
-
-
- - -

An AMT server name or ip address is required.

-
-
- - -

An AMT server port is required.

-
-
- {{serverInfo.fuseki.uri}} -
-
- {{serverInfo.amt.version}} -
-
- - -
-
- - - -
diff --git a/src/federation-hub-ui/src/main/webapp/views/connections/connections.html b/src/federation-hub-ui/src/main/webapp/views/connections/connections.html index f92c4dde..1e3f3394 100644 --- a/src/federation-hub-ui/src/main/webapp/views/connections/connections.html +++ b/src/federation-hub-ui/src/main/webapp/views/connections/connections.html @@ -38,6 +38,10 @@

{{roger_federation.name}}

Connection Id + Reads + BytesRead + Writes + BytesWritten Local Connection Type Remote Connection Type Remote Address @@ -49,6 +53,10 @@

{{roger_federation.name}}

+ + + + diff --git a/src/federation-hub-ui/src/main/webapp/views/manage/manage.html b/src/federation-hub-ui/src/main/webapp/views/manage/manage.html deleted file mode 100644 index 40f75224..00000000 --- a/src/federation-hub-ui/src/main/webapp/views/manage/manage.html +++ /dev/null @@ -1,19 +0,0 @@ -
- - - - -
- -
diff --git a/src/federation-hub-ui/src/main/webapp/views/workflows/add_bpmn_transition.html b/src/federation-hub-ui/src/main/webapp/views/workflows/add_bpmn_transition.html deleted file mode 100644 index 517aa629..00000000 --- a/src/federation-hub-ui/src/main/webapp/views/workflows/add_bpmn_transition.html +++ /dev/null @@ -1,76 +0,0 @@ - - diff --git a/src/federation-hub-ui/src/main/webapp/views/workflows/add_federate_group.html b/src/federation-hub-ui/src/main/webapp/views/workflows/add_federate_group.html index c8a1b00a..3122a39b 100644 --- a/src/federation-hub-ui/src/main/webapp/views/workflows/add_federate_group.html +++ b/src/federation-hub-ui/src/main/webapp/views/workflows/add_federate_group.html @@ -164,14 +164,22 @@

{{roger_federation.name}}

- +
+
+ +
+ +
+
diff --git a/src/federation-hub-ui/src/main/webapp/views/workflows/add_ontology_element.html b/src/federation-hub-ui/src/main/webapp/views/workflows/add_ontology_element.html deleted file mode 100644 index 353f6c5c..00000000 --- a/src/federation-hub-ui/src/main/webapp/views/workflows/add_ontology_element.html +++ /dev/null @@ -1,165 +0,0 @@ - - - diff --git a/src/federation-hub-ui/src/main/webapp/views/workflows/add_policy_filter.html b/src/federation-hub-ui/src/main/webapp/views/workflows/add_policy_filter.html deleted file mode 100644 index 1d554e37..00000000 --- a/src/federation-hub-ui/src/main/webapp/views/workflows/add_policy_filter.html +++ /dev/null @@ -1,15 +0,0 @@ - - - diff --git a/src/federation-hub-ui/src/main/webapp/views/workflows/attribute_expression_editor.html b/src/federation-hub-ui/src/main/webapp/views/workflows/attribute_expression_editor.html deleted file mode 100644 index 400e3aa1..00000000 --- a/src/federation-hub-ui/src/main/webapp/views/workflows/attribute_expression_editor.html +++ /dev/null @@ -1,94 +0,0 @@ - - - -
-
- - -
-
- - -
diff --git a/src/federation-hub-ui/src/main/webapp/views/workflows/workflow_editor.html b/src/federation-hub-ui/src/main/webapp/views/workflows/workflow_editor.html index 7409ba21..6fbe6150 100644 --- a/src/federation-hub-ui/src/main/webapp/views/workflows/workflow_editor.html +++ b/src/federation-hub-ui/src/main/webapp/views/workflows/workflow_editor.html @@ -5,6 +5,21 @@
-->
+
+ LEGEND + +
+ Data Flowing: +
+
+
+ No Data Flowing: +
+
+
-
diff --git a/src/takserver-core/src/main/webapp/Marti/oauth/partials/viewToken.html b/src/takserver-core/src/main/webapp/Marti/oauth/partials/viewToken.html deleted file mode 100644 index d445d42a..00000000 --- a/src/takserver-core/src/main/webapp/Marti/oauth/partials/viewToken.html +++ /dev/null @@ -1,24 +0,0 @@ - - - -
-
- -
-
{{ parsedToken }}
-
- -
-
{{ token }}
-
- -   -
-
diff --git a/src/takserver-core/src/main/webapp/Marti/sendMissionPackage.jsp b/src/takserver-core/src/main/webapp/Marti/sendMissionPackage.jsp index 47a6414a..c3c4e58f 100644 --- a/src/takserver-core/src/main/webapp/Marti/sendMissionPackage.jsp +++ b/src/takserver-core/src/main/webapp/Marti/sendMissionPackage.jsp @@ -15,7 +15,7 @@ <%@ page import="java.util.Collections" %> <%@ page import="com.bbn.marti.remote.SubscriptionManagerLite" %> <%@ page import="com.bbn.marti.remote.RemoteSubscription" %> - <%@ page import="com.bbn.marti.util.spring.SpringContextBeanForApi" %> + <%@ page import="com.bbn.marti.remote.util.SpringContextBeanForApi" %> diff --git a/src/takserver-core/src/main/webapp/Marti/trackExport.jsp b/src/takserver-core/src/main/webapp/Marti/trackExport.jsp index a40948bb..40a04378 100644 --- a/src/takserver-core/src/main/webapp/Marti/trackExport.jsp +++ b/src/takserver-core/src/main/webapp/Marti/trackExport.jsp @@ -18,10 +18,11 @@ <%@ page import="org.owasp.esapi.ESAPI" %> <%@ page import="org.owasp.esapi.errors.IntrusionException" %> <%@ page import="org.owasp.esapi.errors.ValidationException" %> +<%@ page import="com.bbn.marti.remote.util.SpringContextBeanForApi" %> <% final String context = "trackExport.jsp"; Logger log = Logger.getLogger(context); - Validator validator = com.bbn.marti.util.spring.SpringContextBeanForApi.getSpringContext().getBean(Validator.class); + Validator validator = SpringContextBeanForApi.getSpringContext().getBean(Validator.class); SimpleDateFormat sqlDateFormat = new SimpleDateFormat(Constants.SQL_DATE_FORMAT); SimpleDateFormat cotDateFormat = new SimpleDateFormat(Constants.COT_DATE_FORMAT); @@ -76,10 +77,10 @@ } sqlString += "uid=? ORDER BY servertime ASC;"; - try (java.sql.Connection connection = com.bbn.marti.util.spring.SpringContextBeanForApi.getSpringContext().getBean(javax.sql.DataSource.class).getConnection(); java.sql.PreparedStatement sqlQuery = com.bbn.marti.util.spring.SpringContextBeanForApi.getSpringContext().getBean(com.bbn.marti.JDBCQueryAuditLogHelper.class).prepareStatement(sqlString, connection)) { + try (java.sql.Connection connection = SpringContextBeanForApi.getSpringContext().getBean(javax.sql.DataSource.class).getConnection(); java.sql.PreparedStatement sqlQuery = SpringContextBeanForApi.getSpringContext().getBean(com.bbn.marti.JDBCQueryAuditLogHelper.class).prepareStatement(sqlString, connection)) { sqlQuery.setString(1, uid); - try (ResultSet results = com.bbn.marti.util.spring.SpringContextBeanForApi.getSpringContext().getBean(com.bbn.marti.JDBCQueryAuditLogHelper.class).doQuery(sqlQuery)) { + try (ResultSet results = SpringContextBeanForApi.getSpringContext().getBean(com.bbn.marti.JDBCQueryAuditLogHelper.class).doQuery(sqlQuery)) { if(results == null || results.isClosed()) { // do proper error handling... later.. } diff --git a/src/takserver-core/src/main/webapp/Marti/vbm/index.html b/src/takserver-core/src/main/webapp/Marti/vbm/index.html index 09573c38..9c47643b 100644 --- a/src/takserver-core/src/main/webapp/Marti/vbm/index.html +++ b/src/takserver-core/src/main/webapp/Marti/vbm/index.html @@ -38,6 +38,22 @@

VBM Settings

+ +
+ + +
+ +
+ + +
+ +
+ + +
+
diff --git a/src/takserver-core/src/main/webapp/Marti/vbm/js/controllers.js b/src/takserver-core/src/main/webapp/Marti/vbm/js/controllers.js index 0ed9fb04..d7903f63 100644 --- a/src/takserver-core/src/main/webapp/Marti/vbm/js/controllers.js +++ b/src/takserver-core/src/main/webapp/Marti/vbm/js/controllers.js @@ -15,6 +15,9 @@ app.controller('vbmController', $scope.vbm_enabled = current_config.vbmEnabled; $scope.sa_disabled = current_config.sadisabled; $scope.chat_disabled = current_config.chatDisabled; + $scope.ism_strict_enforcing = current_config.ismStrictEnforcing; + $scope.ism_url = current_config.ismUrl; + $scope.network_classification = current_config.networkClassification; }, function myError(response) { alert("Error fetching data from server"); @@ -31,6 +34,9 @@ app.controller('vbmController', vbmEnabled: $scope.vbm_enabled, sadisabled: $scope.sa_disabled, chatDisabled: $scope.chat_disabled, + ismStrictEnforcing: $scope.ism_strict_enforcing, + ismUrl: $scope.ism_url, + networkClassification: $scope.network_classification }; $http({ diff --git a/src/takserver-core/src/main/webapp/classification/css/main.css b/src/takserver-core/src/main/webapp/classification/css/main.css deleted file mode 100644 index 9642af4a..00000000 --- a/src/takserver-core/src/main/webapp/classification/css/main.css +++ /dev/null @@ -1,123 +0,0 @@ -* { - box-sizing: border-box; - } - - body { - margin: 0px; - } - - /* Style the header */ - .header { - background-color: #f1f1f1; - padding: 20px; - text-align: center; - } - - /* Style the top navigation bar */ - .topnav { - overflow: hidden; - background-color: #333; - } - - /* Style the topnav links */ - .topnav a { - float: left; - display: block; - color: #f2f2f2; - text-align: center; - padding: 14px 16px; - text-decoration: none; - } - - /* Change color on hover */ - .topnav a:hover { - background-color: #ddd; - color: black; - } - - .three_columns_parent { - display:flex; /* so that the height of this element depends on the height of its child elements */ - } - - /* Create three unequal columns that floats next to each other */ - .column { - float: left; - padding: 10px; - /* height: 700px; */ - } - - .left, .right { - width: 20%; - } - - .middle { - width: 60%; - } - - /* Clear floats after the columns */ - /* .row:after { - content: ""; - display: table; - clear: both; - } - */ - - /* Responsive layout - makes the three columns stack on top of each other instead of next to each other on smaller screens (600px wide or less) */ - @media screen and (max-width: 600px) { - .column { - width: 100%; - } - } - - .dropbox_selected_caveats { - float: center; - width: 100%; - height: 400px; - padding: 10px; - border: 3px solid #aaaaaa; - overflow: auto; - margin-bottom: 10px; - margin-top: 10px; - display: block; - } - - .list_container { - overflow: auto; - height: 500px; - } - - .columnFF { - float: left; - width: 50%; - } - - /* Clear floats after the columns */ - .rowFF:after { - content: ""; - display: table; - clear: both; - } - - .classification_object { - margin-bottom: 2px; - } - - .caveat_object { - margin-bottom: 2px; - } - - .caveat_button { - margin-bottom: 2px; - } - - .help-block { - color: red; - } - -[ng-drag].dragging { - opacity: 0; -} - -[ng-drop].drag-enter { - border: solid 5px red; -} diff --git a/src/takserver-core/src/main/webapp/classification/default.view.html b/src/takserver-core/src/main/webapp/classification/default.view.html deleted file mode 100644 index 4ea3711f..00000000 --- a/src/takserver-core/src/main/webapp/classification/default.view.html +++ /dev/null @@ -1,3 +0,0 @@ -

Classification Tool

- -

This tool is used to manage classifications for TAK Server

diff --git a/src/takserver-core/src/main/webapp/classification/index.html b/src/takserver-core/src/main/webapp/classification/index.html deleted file mode 100644 index 79ec91f7..00000000 --- a/src/takserver-core/src/main/webapp/classification/index.html +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - - Manage Classification - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - - -
-
-

Classifications

- -   - - -

Search

- -
-
- -
- - -
- -
-
- - - -
-
-
- -
-
-

Caveats

- -   - - -

Search

- -
-
- -
- - -
- -
-
-
- -
- -
-
- -
- -
- - - - - - - - - diff --git a/src/takserver-core/src/main/webapp/classification/js/app.js b/src/takserver-core/src/main/webapp/classification/js/app.js deleted file mode 100644 index c48874c2..00000000 --- a/src/takserver-core/src/main/webapp/classification/js/app.js +++ /dev/null @@ -1,13 +0,0 @@ -var app = angular.module('takClassificationApp', ["ngRoute", "ngDraggable", "ngDialog"]); - -app.config(function($routeProvider) { - $routeProvider - .when("/", { - templateUrl : "default.view.html" - }) - .when("/set_caveats_for_classification", { - templateUrl : "set_caveats_for_classification.view.html", - controller: "setCaveatsController" - }) - .otherwise({ redirectTo: '/' }); - }); diff --git a/src/takserver-core/src/main/webapp/classification/js/controllers/newCaveatController.js b/src/takserver-core/src/main/webapp/classification/js/controllers/newCaveatController.js deleted file mode 100644 index 9bd8b7a3..00000000 --- a/src/takserver-core/src/main/webapp/classification/js/controllers/newCaveatController.js +++ /dev/null @@ -1,51 +0,0 @@ -app.controller('newCaveatController', function($scope, $http) { - - reset_form = function(){ - // Reset the form model. - $scope.new_caveat_name = ""; - // Set back to pristine. - //$scope.form.$setPristine(); - // Since Angular 1.3, set back to untouched state. - //$scope.form.$setUntouched(); - } - - $scope.new_caveat = function(){ - - if ($scope.new_caveat_name.length < 3 || $scope.new_caveat_name.match("^[a-zA-Z0-9_\.\\-]+$") == null){ - - alert("Caveat name must be at least 3 characters and must only contain letters, number, hyphen, underscore and dot"); - return; - } - - - for (const element of $scope.caveats) { - if (element.name == $scope.new_caveat_name){ - alert("Caveat " + $scope.new_caveat_name + " already exists!"); - return; - } - } - - $http({ - method : "POST", - url : "/Marti/api/caveat/" + encodeURI($scope.new_caveat_name) - }).then(function mySuccess(response) { - - alert("Successfully created new caveat "+ $scope.new_caveat_name); - reset_form(); - $scope.list_caveats(); - - }, function myError(response) { - if (response.data != null){ - alert("Error creating caveat. " + response.data.message); - } else { - alert("Error creating caveat."); - } - console.error("response status: "+response.status); - console.error("response text: "+response.statusText); - }); - - $scope.closeThisDialog(1); - - } - -}); \ No newline at end of file diff --git a/src/takserver-core/src/main/webapp/classification/js/controllers/newClassificationController.js b/src/takserver-core/src/main/webapp/classification/js/controllers/newClassificationController.js deleted file mode 100644 index bf166a48..00000000 --- a/src/takserver-core/src/main/webapp/classification/js/controllers/newClassificationController.js +++ /dev/null @@ -1,51 +0,0 @@ -app.controller('newClassificationController', function($scope, $http) { - - reset_form = function(){ - // Reset the form model. - $scope.new_classification_level = ""; - // Set back to pristine. - //$scope.form.$setPristine(); - // Since Angular 1.3, set back to untouched state. - //$scope.form.$setUntouched(); - } - - $scope.new_classification = function(){ - - if ($scope.new_classification_level.length < 3 || $scope.new_classification_level.match("^[a-zA-Z0-9_\.\\-]+$") == null){ - - alert("Classification level must be at least 3 characters and must only contain letters, number, hyphen, underscore and dot"); - return; - } - - - for (const element of $scope.classifications) { - if (element.name == $scope.new_classification_level){ - alert("Classification level " + $scope.new_classification_level + " already exists!"); - return; - } - } - - $http({ - method : "POST", - url : "/Marti/api/classification/" + encodeURI($scope.new_classification_level) - }).then(function mySuccess(response) { - - alert("Successfully created new classification level: "+ $scope.new_classification_level); - reset_form(); - $scope.list_classifications(); - - }, function myError(response) { - if (response.data != null){ - alert("Error creating new classification. " + response.data.message); - } else { - alert("Error creating new classification."); - } - console.error("response status: "+response.status); - console.error("response text: "+response.statusText); - }); - - $scope.closeThisDialog(1); - - } - -}); \ No newline at end of file diff --git a/src/takserver-core/src/main/webapp/classification/js/controllers/setCaveatsController.js b/src/takserver-core/src/main/webapp/classification/js/controllers/setCaveatsController.js deleted file mode 100644 index cf01f4cc..00000000 --- a/src/takserver-core/src/main/webapp/classification/js/controllers/setCaveatsController.js +++ /dev/null @@ -1,35 +0,0 @@ -app.controller('setCaveatsController', function($scope, $http) { - - $scope.setCaveatsForClassification = function() { - - if ($scope.current_classification_level_to_set_caveats == undefined){ - alert("Please select Classification to edit from the left panel"); - return; - } - - data = { - level: $scope.current_classification_level_to_set_caveats, - caveats: $scope.selected_caveats_for_current_classification - } - - $http({ - method : "PUT", - url : "/Marti/api/classification", - data: JSON.stringify(data) - }).then(function mySuccess(response) { - if (response.status == 200){ - alert("Successfully set caveats for classification level " + data.level); - $scope.reset_middle_panel(); - }else{ - alert("response.status: "+ response.status); - } - - }, function myError(response) { - alert("Error setting caveats for classification level " + data.level); - console.error("response status: "+response.status); - console.error("response text: "+response.statusText); - }); - - } - -}); \ No newline at end of file diff --git a/src/takserver-core/src/main/webapp/classification/js/controllers/takClassificationController.js b/src/takserver-core/src/main/webapp/classification/js/controllers/takClassificationController.js deleted file mode 100644 index e222ad91..00000000 --- a/src/takserver-core/src/main/webapp/classification/js/controllers/takClassificationController.js +++ /dev/null @@ -1,246 +0,0 @@ -app.controller('takClassificationController', function($scope, $http, ngDialog) { - - $scope.list_classifications = function(){ - $http({ - method : "GET", - url : "/Marti/api/classification" - }).then(function mySuccess(response) { - response_data = angular.fromJson(response.data); - // response_data = { - // "data": [{ - // "level": "CLASSIFIED", - // "caveats":[{"name":"CUI"}, {"name":"ABC"}] - // }, - // { - // "level": "UNCLASSIFIED", - // "caveats":[] - // }] - // }; - $scope.classifications = response_data.data; - console.log("$scope.classifications: "+ JSON.stringify($scope.classifications)); - }, function myError(response) { - alert("Error fetching classification data from server"); - console.error("response status: "+response.status); - console.error("response text: "+response.statusText); - }); - } - - $scope.list_caveats = function(){ - $http({ - method : "GET", - url : "/Marti/api/caveat" - }).then(function mySuccess(response) { - response_data = angular.fromJson(response.data); - // response_data = { - // "data": [{ - // "name": "CUI" - // }, - // { - // "name": "ABC" - // }] - // }; - $scope.caveats = response_data.data; - console.log("$scope.caveats: "+JSON.stringify($scope.caveats)); - }, function myError(response) { - alert("Error fetching list of caveats from server"); - console.error("response status: "+response.status); - console.error("response text: "+response.statusText); - }); - } - - $scope.list_classifications(); - $scope.list_caveats(); - - $scope.refresh_classifications = function(){ - $scope.list_classifications(); - } - - $scope.refresh_caveats = function(){ - $scope.list_caveats(); - } - - $scope.reset_middle_panel = function(){ - $scope.current_classification_level_to_set_caveats = undefined; - $scope.selected_caveats_for_current_classification = []; - } - - $scope.refresh_middle_panel = function(){ - - if ($scope.current_classification_level_to_set_caveats == undefined || $scope.current_classification_level_to_set_caveats == ""){ - return; - } else{ - $scope.find_caveats_for_classification($scope.current_classification_level_to_set_caveats); - } - } - - - $scope.find_caveats_for_classification = function(level){ - - $scope.current_classification_level_to_set_caveats = level; - - // var found = false; - // for (let item in $scope.classifications){ - // if ($scope.classifications[item].level == level){ - // caveat_list = $scope.classifications[item].caveats; - // found = true; - // break; - // } - // } - // if (found) { - // $scope.selected_caveats_for_current_classification = caveat_list; //[{"name":"CUI"}, {"name":"ABC"}] - // } else{ - // $scope.selected_caveats_for_current_classification = []; - // alert("Could not find classification level " + level); - // } - - $http({ - method : "GET", - url : "/Marti/api/classification/" + encodeURI(level) - }).then(function mySuccess(response) { - response_data = angular.fromJson(response.data); - - if (response_data.data == undefined){ // No such classification exists - $scope.reset_middle_panel(); - }else{ - $scope.selected_caveats_for_current_classification = response_data.data.caveats; //[{"name":"CUI"}, {"name":"ABC"}] - } - - }, function myError(response) { - $scope.reset_middle_panel(); - alert("Error fetching caveats from server"); - }); - - } - - $scope.delete_classification = function(level) { - - var confirmDialog = ngDialog.openConfirm({ - template:'\ -

Are you sure you want to delete classification '+level+' ?

\ -
\ - \ - \ -
', - plain: true - }).then(function (confirm) { - $http({ - method : "DELETE", - url : "/Marti/api/classification/" + encodeURI(level) - }).then(function mySuccess(response) { - if (response.status == 200){ - alert("Successfully deleted classification " + level); - }else{ - alert("response.status: "+ response.status); - } - console.log("response.data: "+ response.data); - $scope.list_classifications(); - $scope.refresh_middle_panel(); - - }, function myError(response) { - alert("Error deleting classification"); - console.error("response status: "+response.status); - console.error("response text: "+response.statusText); - }); - }, function(reject) { - - }); - - }; - - $scope.delete_caveat = function(caveat_name){ - - var confirmDialog = ngDialog.openConfirm({ - template:'\ -

Are you sure you want to delete caveat '+caveat_name+' ?

\ -
\ - \ - \ -
', - plain: true - }).then(function (confirm) { - - $http({ - method : "DELETE", - url : "/Marti/api/caveat/" + encodeURI(caveat_name) - }).then(function mySuccess(response) { - if (response.status == 200){ - alert("Successfully deleted caveat " + caveat_name); - }else{ - alert("response.status: "+ response.status); - } - console.log("response.data: "+ response.data); - $scope.list_caveats(); - $scope.refresh_middle_panel(); - - }, function myError(response) { - alert("Error deleting caveat"); - console.error("response status: "+response.status); - console.error("response text: "+response.statusText); - }); - }, function(reject) { - - }); - - } - - $scope.onDropCompleteToSelectedCaveats = function(data, evt) { - console.log("onDropCompleteToSelectedCaveats , data:", data); - - if ($scope.selected_caveats_for_current_classification == null){ - $scope.selected_caveats_for_current_classification = []; - } - - // add to this drop box - if (!$scope.selected_caveats_for_current_classification.includes(data.caveat)){ - $scope.selected_caveats_for_current_classification.push(data.caveat); - } - - } - - $scope.onDropCompleteToCaveatRepo = function(data, evt) { - console.log("onDropCompleteToCaveatRepo , data:", data); - - if (data == null){ - return; - } - // remove from previous drop box if necessary - if (data.from == 'selectedCaveats'){ - index = $scope.selected_caveats_for_current_classification.indexOf(data.caveat); - if (index != -1){ - $scope.selected_caveats_for_current_classification.splice(index, 1); - } - } - } - - $scope.onDragComplete = function(data, evt) { - //console.log("onDropComplete, data:", data); - } - - $scope.open_new_caveat_dialog = function () { - ngDialog.open({ - template: 'new_caveat.dialog.html', - className: 'ngdialog-theme-default', - controller: 'newCaveatController', - scope: $scope - }); - - }; - - $scope.open_new_classification_dialog = function () { - ngDialog.open({ - template: 'new_classification.dialog.html', - className: 'ngdialog-theme-default', - controller: 'newClassificationController', - scope: $scope - }); - - }; - - $scope.showSimpleMessage = function(message){ - ngDialog.open({ - template: '

'+message+'

', - plain: true - }); - } - -}); \ No newline at end of file diff --git a/src/takserver-core/src/main/webapp/classification/new_caveat.dialog.html b/src/takserver-core/src/main/webapp/classification/new_caveat.dialog.html deleted file mode 100644 index 934c40db..00000000 --- a/src/takserver-core/src/main/webapp/classification/new_caveat.dialog.html +++ /dev/null @@ -1,15 +0,0 @@ - -
-

New Caveat

-
-
- - - Caveat name is required -
-
- - Cancel -
-
-
diff --git a/src/takserver-core/src/main/webapp/classification/new_classification.dialog.html b/src/takserver-core/src/main/webapp/classification/new_classification.dialog.html deleted file mode 100644 index 30a34c61..00000000 --- a/src/takserver-core/src/main/webapp/classification/new_classification.dialog.html +++ /dev/null @@ -1,15 +0,0 @@ - -
-

New Classification

-
-
- - - Classification level is required -
-
- - Cancel -
-
-
diff --git a/src/takserver-core/src/main/webapp/classification/set_caveats_for_classification.view.html b/src/takserver-core/src/main/webapp/classification/set_caveats_for_classification.view.html deleted file mode 100644 index 3212a859..00000000 --- a/src/takserver-core/src/main/webapp/classification/set_caveats_for_classification.view.html +++ /dev/null @@ -1,27 +0,0 @@ -
-

Set caveats for classification

- -
-
Classification level: {{ current_classification_level_to_set_caveats }}
-
-
- -
-
-
- -
Drag the caveats on the right panel and drop to the box below.
To remove a caveat from the box, drag it back to the right panel
- -
-
Selected Caveats
- -
- - - -
-
- -
\ No newline at end of file diff --git a/src/takserver-core/src/main/webapp/user-management/menubar_modified.html b/src/takserver-core/src/main/webapp/user-management/menubar_modified.html index f9ecdb99..f90b2c5a 100644 --- a/src/takserver-core/src/main/webapp/user-management/menubar_modified.html +++ b/src/takserver-core/src/main/webapp/user-management/menubar_modified.html @@ -10,83 +10,83 @@ @@ -95,32 +95,32 @@