Ciscopx Grid
Ciscopx Grid
Ciscopx Grid
Table of Contents
About this Document ............................................................................................................................................................. 4
Technical Overview ................................................................................................................................................................ 5
Illustrating Cisco pxGrid Client Flow using Chrome Postman........................................................................................ 7
Enabling pxGrid ................................................................................................................................................ 7
Exporting the ISE Identity Certificate into your Browser ................................................................................. 8
Creating the pxGrid client Account .................................................................................................................. 9
Activating the Account .................................................................................................................................... 10
Performing Services Lookup .......................................................................................................................... 12
Obtaining the Access Secret.......................................................................................................................... 13
Run GetSessionByIPAddress WebSockets REST API Script...................................................................... 14
Java Code Examples............................................................................................................................................................ 16
Generating pxGrid client Certificates from ISE Internal CA.......................................................................... 16
Converting Certificates to JKS format ........................................................................................................... 17
Downloading Java Examples ......................................................................................................................... 19
SessionSubscribe Coding Example............................................................................................................... 21
Rest of Java Code Examples .............................................................................................................................................. 25
Sample Configuration ..................................................................................................................................... 25
pxGrid Control................................................................................................................................................. 29
SampleHelper ................................................................................................................................................. 31
StompFrame ................................................................................................................................................... 33
StompSubscription ......................................................................................................................................... 36
StompPubSubClientEndpoint ........................................................................................................................ 37
SessionQueryAll ............................................................................................................................................. 40
SessionQueryByIP ......................................................................................................................................... 41
CustomServiceProvider.................................................................................................................................. 42
CustomServiceConsumer .............................................................................................................................. 44
Adaptive Network Control (ANC) Examples ..................................................................................................................... 46
ANCSubscribe ................................................................................................................................................ 46
ANCGetPolicies .............................................................................................................................................. 47
ANCGetPoliciesByName................................................................................................................................ 48
ANCGetEndpoints .......................................................................................................................................... 49
ANCGetEndpointsByMAC.............................................................................................................................. 50
ANCApplyByIP................................................................................................................................................ 51
ANCClearByIP ................................................................................................................................................ 52
ANCCreatePolicy ............................................................................................................................................ 53
ANCDeletePolicy ............................................................................................................................................ 54
ANCGetByOperationID .................................................................................................................................. 55
Cisco pxGrid Context-In ...................................................................................................................................................... 56
Enabling pxGrid as Subscriber for Profiling................................................................................................... 57
Running API_Simulator .................................................................................................................................. 58
Viewing Asset Device in Context Visibility Screen........................................................................................ 61
Creating Profiling Policy Based on Asset Attributes ..................................................................................... 63
Creating Authorization Policy Based on Asset’s Logical Profile................................................................... 67
Verifying Asset as Defined by the Logical Profile ......................................................................................... 71
Creating Profiling Policy based on Asset Custom Attributes ........................................................................ 73
Enabling Custom Attribute Value ............................................................................................................ 74
Creating Authorization Policy Based on Asset’s Logical Profile for Custom Attributes............................... 82
Verifying Asset as Defined by the Logical Profile for Custom Attributes ..................................................... 84
Editing Script Values ...................................................................................................................................... 87
Editing/Adding Customer Values ................................................................................................................... 94
API_Simulator Context-In Code................................................................................................................... 102
SampleConfiguration ............................................................................................................................. 102
PxgridControl ......................................................................................................................................... 106
PublisherController ................................................................................................................................ 108
Custom Publisher................................................................................................................................... 109
CustomSubscriber ................................................................................................................................. 113
Device List .............................................................................................................................................. 115
Devices ................................................................................................................................................... 115
Console.java .......................................................................................................................................... 117
Sample Helper. Java ............................................................................................................................. 117
Stompframe.java .................................................................................................................................... 119
StompSubscription................................................................................................................................. 122
StompPubSubClientEndpoint ................................................................................................................ 122
Endpoint Asset Configuration .......................................................................................................................................... 126
Cisco pxGrid 2.0 does not rely on “java” and “c” SDK’s for development as was the case with Cisco pxGrid 1.0.
Instead Cisco pxGrid 2.0 relies on WebSockets & REST API over the STOMP messaging protocol, which alleviates
the dependency on SDK’s for development. The developer may now use python for pxGrid application development.
This document explains how WebSockets & REST APIs are used in Cisco pxGrid 2.0, and STOMP is the underlying
messaging protocol. Code examples are provided to help in code development.
Cisco pxGrid Context-In is a new feature in ISE 2.4, where Internet of Technology (IOT) solutions can provide their
asset data for Cisco Identity Services Engine (ISE) to consume and incorporate with the ISE Profiling Policies. An
IOT organization’ security policy can then be written by assigning these ISE Profiling Policies to ISE Authorization
Profiles.
An API_Simulator.JAR file is included to learn the details of Cisco pxGrid Context-In. This simulates a pxGrid client
publishing asset information and ISE taking this context in via pxGrid. Coding examples for this JAR file are also
provided.
Chrome POSTMAN is a web-based REST Client that allows you to enter and monitor HTTP requests and responses.
This will be used to explain the concepts of WebSockets & REST APIs.
Technical Overview
Cisco Platform Exchange Grid (pxGrid) 2.0 is based on a REpresentational State Transfer (REST) and WebSockets
Model, over the Simple Text Oriented Protocol (STOMP) messaging protocol. WebSockets provide quick and
scalable bi-directional data transfer, while REST provides quick extensible querying mechanisms – all over the same
interface. pxGrid utilizes WebSockets for pubsub components while all one-shot queries (for both control and service
data) is done via REST. All message bodies are formatted in JSON.
Cisco pxGrid will use port 8910 on ISE for pxGrid-related REST and WebSocket communications. Messages over
Websockets will be sent and received in binary format, these messages should conform to the STOMP messaging
protocol. For more information on supported commands on pxGrid please see: https://github.com/cisco-
pxgrid/pxgrid-rest-ws/wiki
The following represents the typical pxGrid 2.0 client flow:
All clients must authenticate to the ISE pxGrid controller either via certificate-based SSL authentication or username-
password authentication.
Account Activate
All clients request to activate their accounts on the pxGrid server which is handled by the REST API.
Register/Unregister Service
Service providers will use these APIs to provide and update the necessary information (i.e. resource URLs) from
which their services are accessible for other pxGrid clients.
Service Lookup
All clients can use this API to dynamically discover all available provider services and their locations.
Access Secret
For every service returned that interests a particular client, that client must also query the pxGrid controller for an
access secret in order to obtain the information provided by the service.
Service Query / Subscribe
With the access secret and service location information in hand, client can then perform REST-based queries or build
WebSocket connections to receive information.
We will go through an example of authenticating and registering a pxGrid client using Chrome POSTMAN as the
REST client. An account will be created and activated. The pxGrid client will request the session topic or service
from the ISE pxGrid node via a service lookup. The service lookup will return the WebSockets URL (WsURL)
resource and the ISE node that publishes this session topic. The pxGrid client will then obtain an access secret and
retrieve the session information based on the WsURL resource.
The following is a visual representation with the REST and WebSocket architecture:
This representation shows the pxGrid client provider or publisher publishing notifications or topics to the pxGrid
controller via WebSockets and another pxGrid client consumer subscribing to these notification or topics.
Cisco pxGrid Context-In follows the same pxGrid client flow and will be discussed in detail in the Cisco pxGrid
Context-in Section.
Enabling pxGrid
Step 1 Enable pxGrid
Select Administration->System Deployment->Edit Node->Enable pxGrid
Step 1 Import ISE identity certificate into Chrome browser and set to “always trust”
Step 2 Ensure that you have “Allowed password based account creation” enabled under Administration->pxGrid
Services->Settings->pxGrid Settings
Step 1 Create an account and obtain the username and password that will be used for basic authentication for the
other WebSocket REST API calls. In this exercise we will be using password-based authentication.
Step 4 You will receive the username and password that will be used for other WebSockets REST calls. The node
name represents the pxGrid client node.
Step 3 Add the following headers, the authorization header will appear after the username and password have been
configured.
Step 4 You will be in the pending state until the ISE admin approves the account
Step 5 In ISE, you will see pxGrid client name (nodename) in the pending state
Note: Regardless of enabled password-based authentication for client settings, the admin will still have to approve the pending client request.
Step 10 If you re-run REST client request again, the account will be enabled
Step 1 Run Service Lookup, to see what services are available on all the ISE nodes
Step 4 Note the nodeName or the ISE node that publishes the session information and the properties JSON
example:
Step 3 Specify the ISE node that will publish the session information as provided by the returned service lookup
results.
Step 4 Below are the returned session attributes for the endpoint IP address of 192.168.1.15
For this example, we will run the SessionSubscribe.java code, to see the available contextual information from an
authenticated user session.
You can also develop code to subscribe and obtain contextual information from other topics: Radius Failure, these
topics are Session Directory, RADIUS failures, MDM, Profiler Configuration, System Health, TrustSec, TrustSec
Configuration and TrustSec SXP. For more information, please see: https://github.com/cisco-pxgrid/pxgrid-rest-
ws/wiki/pxGrid-Consumer
First, we will need to create certificates your system, which will be the pxGrid client. The pxGrid client will
authenticate to the ISE pxGrid node using certificates or pre-shared keys, in this example we will use certificates
generated from the ISE internal Certificate Authority (CA).
Step 4 Note, if using PEM format, when you unzip the file you will see the following:
Note: Please refer to https://communities.cisco.com/docs/DOC-71928 , using iSE 2.2 Internal CA to deploy to pxGrid clients (java keystores), for
Productional ISE deployments, please refer to: https://communities.cisco.com/docs/DOC-68284
Extensions:
Extensions:
usage: SessionSubscribe
-a,--hostname <arg> Host name (multiple accepted)
-d,--description <arg> Description (optional)
-k,--keystorefilename <arg> Keystore .jks filename (not required if
password is specified)
-p,--keystorepassword <arg> Keystore password (not required if
password is specified)
-q,--truststorepassword <arg> Truststore password
-t,--truststorefilename <arg> Truststore .jks filename
-u,--nodename <arg> Node name
-w,--password <arg> Password (not required if keystore is
specified)
hostname = ise24fc3.lab10.com
nodename = mac03
password = (not specified)
description = (not specified)
keystorefilename = /Applications/master_rest_samples/session1.jks
keystorepassword = Cisco123
truststorefilename = /Applications/master_rest_samples/rootsession.jks
truststorepassword = Cisco123
--------------------
22:29:27.354 [main] INFO com.cisco.pxgrid.samples.ise.PxgridControl - AccountActivate request={}
22:29:32.701 [main] INFO com.cisco.pxgrid.samples.ise.PxgridControl - AccountActivate
response={"accountState":"ENABLED","version":"2.0.0.13"}
22:29:32.701 [main] INFO com.cisco.pxgrid.samples.ise.SessionSubscribe - pxGrid controller version=2.0.0.13
22:29:32.703 [main] INFO com.cisco.pxgrid.samples.ise.PxgridControl - ServiceLookup
request={"name":"com.cisco.ise.session"}
22:29:32.722 [main] INFO com.cisco.pxgrid.samples.ise.PxgridControl - ServiceLookup
response={"services":[{"name":"com.cisco.ise.session","nodeName":"ise-mnt-
ise24fc3","properties":{"sessionTopic":"/topic/com.cisco.ise.session","groupTopic":"/topic/com.cisco.ise.sess
ion.group","wsPubsubService":"com.cisco.ise.pubsub","restBaseURL":"https://ise24fc3.lab10.com:8910/pxgrid/mnt
/sd","restBaseUrl":"https://ise24fc3.lab10.com:8910/pxgrid/mnt/sd"}}]}
22:29:32.722 [main] INFO com.cisco.pxgrid.samples.ise.SessionSubscribe -
wsPubsubServiceName=com.cisco.ise.pubsub sessionTopic=/topic/com.cisco.ise.session
22:29:32.722 [main] INFO com.cisco.pxgrid.samples.ise.PxgridControl - ServiceLookup
request={"name":"com.cisco.ise.pubsub"}
22:29:32.733 [main] INFO com.cisco.pxgrid.samples.ise.PxgridControl - ServiceLookup
response={"services":[{"name":"com.cisco.ise.pubsub","nodeName":"ise-pubsub-
ise24fc3","properties":{"wsUrl":"wss://ise24fc3.lab10.com:8910/pxgrid/ise/pubsub"}}]}
22:29:32.733 [main] INFO com.cisco.pxgrid.samples.ise.SessionSubscribe -
wsUrl=wss://ise24fc3.lab10.com:8910/pxgrid/ise/pubsub
22:29:32.735 [main] INFO com.cisco.pxgrid.samples.ise.PxgridControl - AccessSecret
request={"peerNodeName":"ise-pubsub-ise24fc3"}
22:29:32.895 [main] INFO com.cisco.pxgrid.samples.ise.PxgridControl - AccessSecret
response={"secret":"36QiBjNQdWnR6lPf"}
22:29:34.805 [Grizzly(1)] INFO com.cisco.pxgrid.samples.ise.StompPubsubClientEndpoint - WS onOpen
22:29:34.833 [main] INFO com.cisco.pxgrid.samples.ise.StompPubsubClientEndpoint - STOMP CONNECT
host=ise24fc3.lab10.com
22:29:34.840 [main] INFO com.cisco.pxgrid.samples.ise.StompPubsubClientEndpoint - STOMP SUBSCRIBE
topic=/topic/com.cisco.ise.session
22:29:34.842 [Grizzly(2)] INFO com.cisco.pxgrid.samples.ise.StompPubsubClientEndpoint - STOMP CONNECTED
version=1.2
press <enter> to disconnect...
22:31:57.678 [Grizzly(1)] INFO com.cisco.pxgrid.samples.ise.SessionSubscribe -
Content={"sessions":[{"timestamp":"2018-04-
16T02:31:53.599Z","state":"STARTED","userName":"jeppich","callingStationId":"10:DD:B1:C9:3C:39","calledStatio
nId":"50:3D:E5:C4:05:8C","auditSessionId":"0A0000010000002D02D9EFBF","ipAddresses":["192.168.1.136"],"macAddr
ess":"10:DD:B1:C9:3C:39","nasIpAddress":"192.168.1.3","nasPortId":"GigabitEthernet1/0/12","nasPortType":"Ethe
rnet","endpointProfile":"Apple-Device","endpointOperatingSystem":"Apple Mac OS X 10.7.0 (Lion) - 10.10
(Yosemite) or iOS 4.1 - 8.3 (Darwin 10.0.0 -
14.5.0)","adNormalizedUser":"jeppich","adUserDomainName":"lab10.com","adUserNetBiosName":"LAB10","adUserResol
vedIdentities":"[email protected]","adUserResolvedDns":"CN\u003dJohn
Eppich,CN\u003dUsers,DC\u003dlab10,DC\u003dcom","providers":["None"],"endpointCheckResult":"none","identitySo
urcePortStart":0,"identitySourcePortEnd":0,"identitySourcePortFirst":0,"isMachineAuthentication":"false","ser
viceType":"Framed","networkDeviceProfileName":"Cisco","radiusFlowType":"Wired802_1x","ssid":"50-3D-E5-C4-05-
8C","mdmRegistered":false,"mdmCompliant":false,"mdmDiskEncrypted":false,"mdmJailBroken":false,"mdmPinLocked":
false}]}
22:32:21.684 [Grizzly(1)] INFO com.cisco.pxgrid.samples.ise.SessionSubscribe -
Content={"sessions":[{"timestamp":"2018-04-
16T02:32:17.261Z","state":"STARTED","userName":"LAB10\\pxgrid5","callingStationId":"00:0C:29:01:5D:E8","calle
dStationId":"50:3D:E5:C4:05:96","auditSessionId":"0A000001000000190003320D","ipAddresses":["192.168.1.9"],"ma
cAddress":"00:0C:29:01:5D:E8","nasIpAddress":"192.168.1.3","nasPortId":"GigabitEthernet1/0/22","nasPortType":
"Ethernet","endpointProfile":"Windows7-Workstation","endpointOperatingSystem":"Windows 7
Professional","adNormalizedUser":"pxgrid5","adUserDomainName":"lab10.com","adUserNetBiosName":"LAB10","adUser
ResolvedIdentities":"[email protected]","adUserResolvedDns":"CN\u003dpxgrid5,CN\u003dUsers,DC\u003dlab10,DC\u
003dcom","providers":["None"],"endpointCheckResult":"none","identitySourcePortStart":0,"identitySourcePortEnd
":0,"identitySourcePortFirst":0,"isMachineAuthentication":"false","serviceType":"Framed","networkDeviceProfil
eName":"Cisco","radiusFlowType":"Wired802_1x","ssid":"50-3D-E5-C4-05-
96","mdmRegistered":false,"mdmCompliant":false,"mdmDiskEncrypted":false,"mdmJailBroken":false,"mdmPinLocked":
false}]}
22:36:17.720 [Grizzly(2)] INFO com.cisco.pxgrid.samples.ise.SessionSubscribe -
Content={"sessions":[{"timestamp":"2018-04-
16T02:36:12.99Z","state":"STARTED","userName":"LAB10\\pxgrid5","callingStationId":"00:0C:29:01:5D:E8","called
StationId":"50:3D:E5:C4:05:96","auditSessionId":"0A000001000000190003320D","ipAddresses":["192.168.1.9"],"mac
Address":"00:0C:29:01:5D:E8","nasIpAddress":"192.168.1.3","nasPortId":"GigabitEthernet1/0/22","nasPortType":"
Ethernet","endpointProfile":"Windows7-Workstation","endpointOperatingSystem":"Windows 7
Professional","adNormalizedUser":"pxgrid5","adUserDomainName":"lab10.com","adUserNetBiosName":"LAB10","adUser
ResolvedIdentities":"[email protected]","adUserResolvedDns":"CN\u003dpxgrid5,CN\u003dUsers,DC\u003dlab10,DC\u
003dcom","providers":["None"],"endpointCheckResult":"none","identitySourcePortStart":0,"identitySourcePortEnd
":0,"identitySourcePortFirst":0,"isMachineAuthentication":"false","serviceType":"Framed","networkDeviceProfil
eName":"Cisco","radiusFlowType":"Wired802_1x","ssid":"50-3D-E5-C4-05-
96","mdmRegistered":false,"mdmCompliant":false,"mdmDiskEncrypted":false,"mdmJailBroken":false,"mdmPinLocked":
false}]}
22:49:17.586 [Grizzly(2)] INFO com.cisco.pxgrid.samples.ise.SessionSubscribe -
Content={"sessions":[{"timestamp":"2018-04-
16T02:47:32.481Z","state":"DISCONNECTED","userName":"jeppich","callingStationId":"10:DD:B1:C9:3C:39","calledS
tationId":"50:3D:E5:C4:05:8C","auditSessionId":"0A0000010000002D02D9EFBF","ipAddresses":["192.168.1.136"],"ma
cAddress":"10:DD:B1:C9:3C:39","nasIpAddress":"192.168.1.3","nasPortId":"GigabitEthernet1/0/12","nasPortType":
"Ethernet","endpointProfile":"Apple-Device","endpointOperatingSystem":"Apple Mac OS X 10.7.0 (Lion) - 10.10
(Yosemite) or iOS 4.1 - 8.3 (Darwin 10.0.0 -
14.5.0)","adNormalizedUser":"jeppich","adUserDomainName":"lab10.com","adUserNetBiosName":"LAB10","adUserResol
vedIdentities":"[email protected]","adUserResolvedDns":"CN\u003dJohn
Eppich,CN\u003dUsers,DC\u003dlab10,DC\u003dcom","providers":["None"],"endpointCheckResult":"none","identitySo
urcePortStart":0,"identitySourcePortEnd":0,"identitySourcePortFirst":0,"isMachineAuthentication":"false","ser
viceType":"Framed","networkDeviceProfileName":"Cisco","radiusFlowType":"Wired802_1x","ssid":"50-3D-E5-C4-05-
8C","mdmRegistered":false,"mdmCompliant":false,"mdmDiskEncrypted":false,"mdmJailBroken":false,"mdmPinLocked":
false}]}
22:49:17.588 [Grizzly(2)] INFO com.cisco.pxgrid.samples.ise.SessionSubscribe -
Content={"sessions":[{"timestamp":"2018-04-
16T02:48:13.485Z","state":"STARTED","userName":"jeppich","callingStationId":"10:DD:B1:C9:3C:39","calledStatio
nId":"50:3D:E5:C4:05:8C","auditSessionId":"0A0000010000002F02F84944","ipAddresses":["192.168.1.136"],"macAddr
ess":"10:DD:B1:C9:3C:39","nasIpAddress":"192.168.1.3","nasPortId":"GigabitEthernet1/0/12","nasPortType":"Ethe
rnet","endpointProfile":"Apple-Device","endpointOperatingSystem":"Apple Mac OS X 10.7.0 (Lion) - 10.10
(Yosemite) or iOS 4.1 - 8.3 (Darwin 10.0.0 -
14.5.0)","adNormalizedUser":"jeppich","adUserDomainName":"lab10.com","adUserNetBiosName":"LAB10","adUserResol
vedIdentities":"[email protected]","adUserResolvedDns":"CN\u003dJohn
Eppich,CN\u003dUsers,DC\u003dlab10,DC\u003dcom","providers":["None"],"endpointCheckResult":"none","identitySo
urcePortStart":0,"identitySourcePortEnd":0,"identitySourcePortFirst":0,"isMachineAuthentication":"false","ser
viceType":"Framed","networkDeviceProfileName":"Cisco","radiusFlowType":"Wired802_1x","ssid":"50-3D-E5-C4-05-
8C","mdmRegistered":false,"mdmCompliant":false,"mdmDiskEncrypted":false,"mdmJailBroken":false,"mdmPinLocked":
false}]}
22:49:17.618 [Grizzly(2)] INFO com.cisco.pxgrid.samples.ise.StompPubsubClientEndpoint - WS onClose
closeReason code=VIOLATED_POLICY phrase=Did not receive a pong: too slow ...
Sample Configuration
This code configures the –D PXGRID Hostname, -D PXGRID Username, -D PXGRID PASSWORD, -D PXGRID
GROUP, -D PXGRID Description, -D PXGRID Keystore_Filename, -D PXGRID Keystore_Psssword, -D PXGRID
Truststore Filename, and –D Truststore Password values. This also sets up the keystores.
package com.cisco.pxgrid.samples.ise.http;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.Socket;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Enumeration;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
keystoreFilename = System.getProperty(PROP_KEYSTORE_FILENAME);
keystorePassword = System.getProperty(PROP_KEYSTORE_PASSWORD);
truststoreFilename = System.getProperty(PROP_TRUSTSTORE_FILENAME);
truststorePassword = System.getProperty(PROP_TRUSTSTORE_PASSWORD);
hostnames = hostnameProperty.split(",");
if (description != null) {
if (description.isEmpty()) description = null;
else description = description.trim();
}
sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(getKeyManagers(), getTrustManagers(), null);
}
KeyStore ks = null;
if(truststoreFilename.endsWith(".pem")) {
ks = KeyStore.getInstance("JKS");
ks.load(null, null);
CertificateFactory certFac = CertificateFactory.getInstance("X.509");
Collection<? extends Certificate> certs = certFac.generateCertificates(in);
int i = 0;
for(Certificate c : certs) {
ks.setCertificateEntry("trust-" + i, c);
}
} else if(truststoreFilename.endsWith(".p12")) {
ks = KeyStore.getInstance("pkcs12");
ks.load(in, truststorePassword.toCharArray());
} else {
ks = KeyStore.getInstance("JKS");
ks.load(in, truststorePassword.toCharArray());
}
in.close();
Enumeration<String> e = ks.aliases();
boolean hasCertEntries = false;
while (e.hasMoreElements()) {
String alias = e.nextElement();
if (ks.isCertificateEntry(alias)) {
hasCertEntries = true;
}
}
if (hasCertEntries == false) {
e = ks.aliases();
while (e.hasMoreElements()) {
String alias = e.nextElement();
if (ks.isKeyEntry(alias)) {
Certificate[] chain = ks.getCertificateChain(alias);
}
}
}
TrustManagerFactory tmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
@Override
public String chooseClientAlias(String[] arg0, Principal[] arg1, Socket arg2) {
String alias = mngr.chooseClientAlias(arg0, arg1, arg2);
if (alias == null) {
alias = mngr.chooseClientAlias(arg0, null, arg2);
if (alias == null) {
throw new RuntimeException("no client certificate found ...");
}
}
return alias;
}
@Override
public String chooseServerAlias(String arg0, Principal[] arg1, Socket arg2) {
throw new RuntimeException("Not implemented");
}
@Override
public X509Certificate[] getCertificateChain(String arg0) {
return mngr.getCertificateChain(arg0);
}
@Override
public String[] getClientAliases(String arg0, Principal[] arg1) {
return mngr.getClientAliases(arg0, null);
}
@Override
public PrivateKey getPrivateKey(String arg0) {
return mngr.getPrivateKey(arg0);
}
@Override
public String[] getServerAliases(String arg0, Principal[] arg1) {
throw new RuntimeException("Not implemented");
}
}
@Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws
CertificateException {
throw new RuntimeException("not implemented");
}
@Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws
CertificateException {
try {
mngr.checkServerTrusted(arg0, arg1);
} catch (CertificateException e) {
throw new CertificateException("Server certificate is not trusted:" +
arg0[0].getSubjectX500Principal(),
e);
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return mngr.getAcceptedIssuers();
}
}
pxGrid Control
This code provides the pxGrid client with account creation on the ISE pxGrid node and service lookup request and
access secret to the peer node, publishing the topic information. In the example, using API_Simulator, the peer node
would reflect the pxGrid client.
package com.cisco.pxgrid.samples.ise.http;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.util.Base64;
import java.util.Map;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.cisco.pxgrid.model.AccessSecretRequest;
import com.cisco.pxgrid.model.AccessSecretResponse;
import com.cisco.pxgrid.model.AccountActivateRequest;
import com.cisco.pxgrid.model.AccountActivateResponse;
import com.cisco.pxgrid.model.AccountCreateRequest;
import com.cisco.pxgrid.model.AccountCreateResponse;
import com.cisco.pxgrid.model.AccountState;
import com.cisco.pxgrid.model.Authorization;
import com.cisco.pxgrid.model.AuthorizationRequest;
import com.cisco.pxgrid.model.AuthorizationResponse;
import com.cisco.pxgrid.model.Service;
import com.cisco.pxgrid.model.ServiceLookupRequest;
import com.cisco.pxgrid.model.ServiceLookupResponse;
import com.cisco.pxgrid.model.ServiceRegisterRequest;
import com.cisco.pxgrid.model.ServiceRegisterResponse;
import com.google.gson.Gson;
/**
* Using HTTPS for pxGrid control
*/
public class PxgridControl {
private static Logger logger = LoggerFactory.getLogger(PxgridControl.class);
private SampleConfiguration config;
private String controllerVersion;
return response;
}
https.setRequestMethod("POST");
return https;
}
/**
* Create new account
*
* @return password
*/
public String accountCreate() throws IOException {
HttpsURLConnection https = getHttpsURLConnection("AccountCreate");
AccountCreateRequest request = new AccountCreateRequest();
request.setNodeName(config.getUserName());
AccountCreateResponse response = sendRequest(https, request, AccountCreateResponse.class);
return response.getPassword();
}
request.setServiceOperation(operation);
SampleHelper
This code provides the initial WebSockets connection.
package com.cisco.pxgrid.samples.ise;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.Scanner;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocketFactory;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
public static void postObjectAndPrint(String url, String user, String password, SSLSocketFactory ssl,
Object postObject) throws IOException {
Gson gson = new GsonBuilder().registerTypeAdapter(OffsetDateTime.class, new
OffsetDateTimeAdapter()).create();
postStringAndPrint(url, user, password, ssl, gson.toJson(postObject));
}
public static void postStringAndPrint(String url, String user, String password, SSLSocketFactory ssl,
String postData) throws IOException {
logger.info("postData={}", postData);
HttpsURLConnection httpsConn = SampleHelper.createHttpsURLConnection(url, user, password,
ssl);
httpsConn.setRequestMethod("POST");
httpsConn.setRequestProperty("Content-Type", "application/json");
httpsConn.setRequestProperty("Accept", "application/json");
httpsConn.setDoInput(true);
httpsConn.setDoOutput(true);
@Override
public void write(JsonWriter out, OffsetDateTime value) throws IOException {
if (value == null) {
out.nullValue();
return;
}
out.value(formatter.format(value));
}
@Override
public OffsetDateTime read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
return formatter.parse(in.nextString(), OffsetDateTime::from);
}
}
}
StompFrame
This code represents the parsing of STOMP frames.
package com.cisco.pxgrid.samples.ise;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
/**
* This follows STOMP 1.2 specification to parse and generate STOMP frames:
* https://stomp.github.io/stomp-specification-1.2.html
*
* This single class is self-sufficient handle all STOMP frames.
*
* Note for WebSocket:
* If input comes as WebSocket text type, (WS RFC says Text is UTF-8)
* server side handling code like Spring TextMessage may convert the bytes to String as UTF-8
* which maybe the wrong encoding as STOMP frame itself can use other encoding.
* e.g. A particular encoding may have bytes: FF FF FF FF FF FF FF FF FF FF... 10, that is completely out
of range for Unicode.
* Unless STOMP body is also UTF-8, STOMP frame must be sent as binary
*
* @author Alan Lei
*/
public class StompFrame {
public enum Command {
CONNECT, STOMP, CONNECTED, SEND, SUBSCRIBE, UNSUBSCRIBE, ACK, NACK,
BEGIN, COMMIT, ABORT, DISCONNECT, MESSAGE, RECEIPT, ERROR;
/*
* Using InputStream instead of Reader because
* content-length is octet count instead of character count
*/
public static StompFrame parse(InputStream reader) throws IOException, ParseException {
StompFrame stomp = new StompFrame();
// Read Command
String line = readLine(reader);
Command command = Command.get(line);
if (command == null) throw new ParseException("Unknown command: " + line, 0);
stomp.setCommand(command);
// Read Headers
int contentLength = -1;
while ((line = readLine(reader)) != null) {
if (line.equals("")) break;
int colon = line.indexOf(':');
String name = line.substring(0, colon);
String value = line.substring(colon + 1);
stomp.setHeader(name, value);
if (name.equals("content-length")) {
contentLength = Integer.parseInt(value);
}
}
// Read Content
if (contentLength != -1) {
// content-length is in octets
byte[] content = new byte[contentLength];
reader.read(content);
stomp.setContent(content);
if (reader.read() != 0) {
throw new ParseException("Byte after STOMP Body not NULL", -1);
}
}
else {
// No content-length. Look for ending NULL byte.
byte[] buffer = new byte[MAX_BUFFER_SIZE];
int length = 0;
while (length < MAX_BUFFER_SIZE) {
int b = reader.read();
if (b == -1) {
throw new ParseException("Premature end of stream", -1);
}
if (b == 0) {
if (length > 0) {
byte[] content = new byte[length];
System.arraycopy(buffer, 0, content, 0, length);
stomp.setContent(content);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("command=" + command);
sb.append(", headers={");
for (String name : headers.keySet()) {
sb.append("'" + name + "':");
sb.append("'" + headers.get(name) + "',");
}
sb.append("}");
sb.append(", content.length=" + content.length);
return sb.toString();
}
}
StompSubscription
This code provides service or topic subscription using the STOMP messaging protocol
package com.cisco.pxgrid.samples.ise;
import java.util.concurrent.atomic.AtomicInteger;
StompPubSubClientEndpoint
This code provides service or topic subscription using the STOMP messaging protocol for the client endpoint or asset
device.
package com.cisco.pxgrid.samples.ise;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.text.ParseException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.ClientEndpoint;
import javax.websocket.CloseReason;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.cisco.pxgrid.samples.ise.StompSubscription.Handler;
@ClientEndpoint
public class StompPubsubClientEndpoint extends Endpoint {
private static Logger logger = LoggerFactory.getLogger(StompPubsubClientEndpoint.class);
message.setCommand(StompFrame.Command.SEND);
message.setHeader("destination", topic);
message.setHeader("content-length", Integer.toString(content.length));
message.setContent(content);
send(message);
}
}
}
}
@Override
public void onOpen(Session session, EndpointConfig cfg) {
logger.info("WS onOpen");
this.session = session;
try {
session.addMessageHandler(new TextHandler());
session.addMessageHandler(new BinaryHandler());
@Override
public void onClose(Session session, CloseReason closeReason) {
logger.info("WS onClose closeReason code={} phrase={}", closeReason.getCloseCode(),
closeReason.getReasonPhrase());
this.session = null;
}
@Override
public void onError(Session session, Throwable thr) {
logger.info("WS onError thr={}", thr.getMessage());
this.session = null;
}
}
SessionQueryAll
This code returns all available sessions
package com.cisco.pxgrid.samples.ise;
import java.time.OffsetDateTime;
import org.apache.commons.cli.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.cisco.pxgrid.samples.ise.model.AccountState;
import com.cisco.pxgrid.samples.ise.model.Service;
/**
* Demonstrates how to query all sessions from ISE Session Directory service
*/
public class SessionQueryAll {
private static Logger logger = LoggerFactory.getLogger(SessionQueryAll.class);
// AccountActivate
PxgridControl control = new PxgridControl(config);
while (control.accountActivate() != AccountState.ENABLED)
Thread.sleep(60000);
logger.info("pxGrid controller version={}", control.getControllerVersion());
downloadUsingAccessSecret(config);
}
}
SessionQueryByIP
This code returns available session attributes for a queried IP Address
package com.cisco.pxgrid.samples.ise;
import java.io.IOException;
import org.apache.commons.cli.ParseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.cisco.pxgrid.samples.ise.model.AccountState;
import com.cisco.pxgrid.samples.ise.model.Service;
/**
* Demonstrates how to query session by IP from ISE Session Directory service
*/
public class SessionQueryByIP {
private static Logger logger = LoggerFactory.getLogger(SessionQueryByIP.class);
// AccountActivate
PxgridControl pxgrid = new PxgridControl(config);
while (pxgrid.accountActivate() != AccountState.ENABLED)
Thread.sleep(60000);
logger.info("pxGrid controller version={}", pxgrid.getControllerVersion());
while (true) {
String ip = SampleHelper.prompt("IP address (or <enter> to disconnect): ");
if (ip == null) break;
query(config, ip);
}
}
}
CustomServiceProvider
This code provides the pxGrid client to a publish a topic and the CustomServiceConsumer code will subscribe to this
topic or service.
package com.cisco.pxgrid.samples.ise;
import java.io.IOException;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javax.websocket.Session;
import org.apache.commons.cli.ParseException;
import org.glassfish.tyrus.client.ClientManager;
import org.glassfish.tyrus.client.ClientProperties;
import org.glassfish.tyrus.client.SslEngineConfigurator;
import org.glassfish.tyrus.client.auth.Credentials;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.cisco.pxgrid.samples.ise.model.AccountState;
import com.cisco.pxgrid.samples.ise.model.Service;
import com.cisco.pxgrid.samples.ise.model.ServiceRegisterResponse;
/**
* Demonstrate how to create a custom service that publishes data
*
* The flow of the application is as follows:
* 1. Parse arguments for configurations
* 2. Activate Account. This will then require ISE Admin to approve this new node.
* 3. pxGrid ServiceRegister to register the new custom service
* 4. Schedule periodic pxGrid ServiceReregister to signify the service is still alive
* 5. pxGrid ServiceLookup for ISE pubsub service
* 6. pxGrid get AccessSecret for the ISE pubsub node
* 7. Establish WebSocket connection with the ISE pubsub node
* 8. Establish STOMP connection for pubsub messaging
* 9. Schedule periodic publish of data
* 10. Wait for keyboard input for stopping the application
*/
public class CustomServiceProvider {
private static Logger logger = LoggerFactory.getLogger(CustomServiceProvider.class);
// Parse arguments
SampleConfiguration config = new SampleConfiguration();
try {
config.parse(args);
} catch (ParseException e) {
config.printHelp("CustomServiceProvider");
System.exit(1);
}
// AccountActivate
PxgridControl control = new PxgridControl(config);
while (control.accountActivate() != AccountState.ENABLED) {
Thread.sleep(60000);
}
// pxGrid ServiceRegister
Map<String, String> sessionProperties = new HashMap<>();
sessionProperties.put("wsPubsubService", "com.cisco.ise.pubsub");
sessionProperties.put("customTopic", "/topic/com.example.custom");
ServiceRegisterResponse response = control.serviceRegister("com.example.custom",
sessionProperties);
String registrationId = response.getId();
long reregisterTimeMillis = response.getReregisterTimeMillis();
// pxGrid AccessSecret
String secret = control.getAccessSecret(wsPubsubService.getNodeName());
// WebSocket connect
StompPubsubClientEndpoint endpoint = new StompPubsubClientEndpoint();
URI uri = new URI(wsURL);
Session session = client.connectToServer(endpoint, uri);
// STOMP connect
endpoint.connect(uri.getHost());
// pxGrid ServerUnregister
control.unregisterService(registrationId);
// Stop executor
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
// STOMP disconnect
endpoint.disconnect("ID-123");
// Wait for disconnect receipt
Thread.sleep(3000);
// Websocket close
session.close();
}
}
CustomServiceConsumer
This sample code provides consumer or pxGrid client subscription to a customer service or published topic
package com.cisco.pxgrid.samples.ise;
import java.net.URI;
import javax.net.ssl.SSLSession;
import javax.websocket.Session;
import org.apache.commons.cli.ParseException;
import org.glassfish.tyrus.client.ClientManager;
import org.glassfish.tyrus.client.ClientProperties;
import org.glassfish.tyrus.client.SslEngineConfigurator;
import org.glassfish.tyrus.client.auth.Credentials;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.cisco.pxgrid.samples.ise.model.AccountState;
import com.cisco.pxgrid.samples.ise.model.Service;
/**
* Demonstrates how to subscribe a topic from a custom service
*
* The flow of the application is as follows:
* 1. Parse arguments for configurations
* 2. Activate Account. This will then require ISE Admin to approve this new node.
* 3. pxGrid ServiceLookup for the custom service
* 4. pxGrid ServiceLookup for ISE pubsub service
* 5. pxGrid get AccessSecret for the ISE pubsub node
* 6. Establish WebSocket connection with the ISE pubsub node
* 7. Establish STOMP connection for pubsub messaging
* 8. Subscribe to the topic in the custom service
* 9. Wait for keyboard input for stopping the application
*/
public class CustomServiceConsumer {
private static Logger logger = LoggerFactory.getLogger(CustomServiceProvider.class);
// AccountActivate
PxgridControl control = new PxgridControl(config);
while (control.accountActivate() != AccountState.ENABLED) {
Thread.sleep(60000);
}
logger.info("pxGrid controller version={}", control.getControllerVersion());
// WebSocket config
ClientManager client = ClientManager.createClient();
SslEngineConfigurator sslEngineConfigurator = new
SslEngineConfigurator(config.getSSLContext());
client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator);
client.getProperties().put(ClientProperties.CREDENTIALS,
new Credentials(config.getNodeName(), secret.getBytes()));
// WebSocket connect
StompPubsubClientEndpoint endpoint = new StompPubsubClientEndpoint();
URI uri = new URI(wsURL);
Session session = client.connectToServer(endpoint, uri);
// STOMP connect
endpoint.connect(uri.getHost());
// Subscribe
StompSubscription subscription = new StompSubscription(customTopic, new MessageHandler());
endpoint.subscribe(subscription);
// STOMP disconnect
endpoint.disconnect("ID-123");
// Wait for disconnect receipt
Thread.sleep(3000);
session.close();
}
}
ANCSubscribe
The consumer or pxGrid client subscribes to the /topic/com.cisco.ise.config.anc.status topic or service
package com.cisco.pxgrid.samples.ise.anc;
import java.net.URI;
import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
import org.glassfish.tyrus.client.ClientManager;
import org.glassfish.tyrus.client.ClientProperties;
import org.glassfish.tyrus.client.auth.Credentials;
import com.cisco.pxgrid.model.AccountState;
import com.cisco.pxgrid.model.Service;
import com.cisco.pxgrid.samples.ise.http.Console;
import com.cisco.pxgrid.samples.ise.http.PxgridControl;
import com.cisco.pxgrid.samples.ise.http.SampleConfiguration;
import com.cisco.pxgrid.samples.ise.http.StompFrame;
import com.cisco.pxgrid.samples.ise.http.StompPubsubClientEndpoint;
import com.cisco.pxgrid.samples.ise.http.StompSubscription;
/**
* Demonstrates how to subscribe using REST/WS
*/
public class AncSubscribe {
// Subscribe handler class
private static class SubscriptionHandler implements StompSubscription.Handler {
@Override
public void handle(StompFrame message) {
System.out.println(new String(message.getContent()));
}
}
// AccountActivate
while (control.accountActivate() != AccountState.ENABLED) {
Thread.sleep(60000);
}
Console.log("pxGrid controller version=" + control.getControllerVersion());
// Session ServiceLookup
Console.log("Looking up service com.cisco.ise.config.anc");
Service[] services = control.lookupService("com.cisco.ise.config.anc");
if (services.length == 0) {
Console.log("Session service unavailabe");
return;
}
// Select first one for sample purpose. Should cycle through until connects.
Service wsPubsubService = services[0];
String wsURL = wsPubsubService.getProperties().get("wsUrl");
Console.log("url=" + wsURL);
// pxGrid AccessSecret
String secret = control.getAccessSecret(wsPubsubService.getNodeName());
// WebSocket config
ClientManager client = ClientManager.createClient();
SSLEngineConfigurator sslEngineConfigurator = new
SSLEngineConfigurator(config.getSSLContext());
client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator);
client.getProperties().put(ClientProperties.CREDENTIALS,
new Credentials(config.getUserName(), secret.getBytes()));
// WebSocket connect
StompPubsubClientEndpoint endpoint = new StompPubsubClientEndpoint();
URI uri = new URI(wsURL);
javax.websocket.Session session = client.connectToServer(endpoint, uri);
// STOMP connect
endpoint.connect(uri.getHost());
// Subscribe
StompSubscription subscription = new StompSubscription(statusTopic, new
SubscriptionHandler());
endpoint.subscribe(subscription);
// STOMP disconnect
endpoint.disconnect("ID-123");
// Wait for disconnect receipt
Thread.sleep(3000);
session.close();
}
}
ANCGetPolicies
This code retrieves all ISE ANC Policies
package com.cisco.pxgrid.samples.ise.anc;
import java.io.IOException;
import com.cisco.pxgrid.model.AccountState;
import com.cisco.pxgrid.model.Service;
import com.cisco.pxgrid.samples.ise.http.Console;
import com.cisco.pxgrid.samples.ise.http.PxgridControl;
import com.cisco.pxgrid.samples.ise.http.SampleConfiguration;
import com.cisco.pxgrid.samples.ise.http.SampleHelper;
/**
* Demonstrates how to query a session using IP address
*/
public class AncGetPolicies {
get(config);
}
}
ANCGetPoliciesByName
This code retrieves the ISE ANC policies by policy name
package com.cisco.pxgrid.samples.ise.anc;
import java.io.IOException;
import com.cisco.pxgrid.model.AccountState;
import com.cisco.pxgrid.model.Service;
import com.cisco.pxgrid.samples.ise.http.Console;
import com.cisco.pxgrid.samples.ise.http.PxgridControl;
import com.cisco.pxgrid.samples.ise.http.SampleConfiguration;
import com.cisco.pxgrid.samples.ise.http.SampleHelper;
/**
* Demonstrates how to query a session using IP address
*/
public class AncGetPolicyByName {
private static class ByNameRequest {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
private static void get(SampleConfiguration config, String name) throws IOException {
PxgridControl pxgrid = new PxgridControl(config);
Service[] services = pxgrid.lookupService("com.cisco.ise.config.anc");
if (services == null || services.length == 0) {
System.out.println("Service unavailabe");
return;
}
Service service = services[0];
String url = service.getProperties().get("restBaseUrl") + "/getPolicyByName";
String secret = pxgrid.getAccessSecret(service.getNodeName());
ByNameRequest request = new ByNameRequest();
request.setName(name);
SampleHelper.postObjectAndPrint(url, config.getUserName(), secret,
config.getSSLContext().getSocketFactory(), request);
}
ANCGetEndpoints
This code retrieves endpoints with the ANC policy applied
package com.cisco.pxgrid.samples.ise.anc;
import java.io.IOException;
import com.cisco.pxgrid.model.AccountState;
import com.cisco.pxgrid.model.Service;
import com.cisco.pxgrid.samples.ise.http.Console;
import com.cisco.pxgrid.samples.ise.http.PxgridControl;
import com.cisco.pxgrid.samples.ise.http.SampleConfiguration;
import com.cisco.pxgrid.samples.ise.http.SampleHelper;
/**
* Demonstrates how to query a session using IP address
*/
public class AncGetEndpoints {
getEndpoints(config);
}
}
ANCGetEndpointsByMAC
This code retrieves endpoints with an ISE ANC policy by MAC address
package com.cisco.pxgrid.samples.ise.anc;
import java.io.IOException;
import com.cisco.pxgrid.model.AccountState;
import com.cisco.pxgrid.model.Service;
import com.cisco.pxgrid.samples.ise.http.Console;
import com.cisco.pxgrid.samples.ise.http.PxgridControl;
import com.cisco.pxgrid.samples.ise.http.SampleConfiguration;
import com.cisco.pxgrid.samples.ise.http.SampleHelper;
/**
* Demonstrates how to query a session using IP address
*/
public class AncGetEndpointByMac {
private static class ByMacRequest {
private String mac;
public String getMac() {
return mac;
}
public void setMac(String mac) {
this.mac = mac;
}
}
private static void get(SampleConfiguration config, String mac) throws IOException {
PxgridControl pxgrid = new PxgridControl(config);
Service[] services = pxgrid.lookupService("com.cisco.ise.config.anc");
if (services == null || services.length == 0) {
System.out.println("Service unavailabe");
return;
}
Service service = services[0];
String url = service.getProperties().get("restBaseUrl") + "/getEndpointByMacAddress";
String secret = pxgrid.getAccessSecret(service.getNodeName());
ByMacRequest request = new ByMacRequest();
request.setMac(mac);
SampleHelper.postObjectAndPrint(url, config.getUserName(), secret,
config.getSSLContext().getSocketFactory(), request);
}
ANCApplyByIP
This code retrieves endpoints with an ANCY policy by IP Address
package com.cisco.pxgrid.samples.ise.anc;
import java.io.IOException;
import com.cisco.pxgrid.model.AccountState;
import com.cisco.pxgrid.model.Service;
import com.cisco.pxgrid.samples.ise.http.Console;
import com.cisco.pxgrid.samples.ise.http.PxgridControl;
import com.cisco.pxgrid.samples.ise.http.SampleConfiguration;
import com.cisco.pxgrid.samples.ise.http.SampleHelper;
/**
* Demonstrates how to query a session using IP address
*/
public class AncApplyByIp {
private static class ApplyEndpointPolicyByIpRequest {
private String policyName;
private String ipAddress;
public void setPolicyName(String policyName) {
this.policyName = policyName;
}
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
private static void apply(SampleConfiguration config, String policyName, String ip) throws
IOException {
PxgridControl pxgrid = new PxgridControl(config);
Service[] services = pxgrid.lookupService("com.cisco.ise.config.anc");
if (services == null || services.length == 0) {
System.out.println("Service unavailabe");
return;
}
Service service = services[0];
String url = service.getProperties().get("restBaseUrl") + "/applyEndpointByIpAddress";
String secret = pxgrid.getAccessSecret(service.getNodeName());
ApplyEndpointPolicyByIpRequest request = new ApplyEndpointPolicyByIpRequest();
request.setPolicyName(policyName);
request.setIpAddress(ip);
SampleHelper.postObjectAndPrint(url, config.getUserName(), secret,
config.getSSLContext().getSocketFactory(), request);
}
ANCClearByIP
This code clears or unquarantines endpoints with an ISE ANC policy by IP Address
package com.cisco.pxgrid.samples.ise.anc;
import java.io.IOException;
import com.cisco.pxgrid.model.AccountState;
import com.cisco.pxgrid.model.Service;
import com.cisco.pxgrid.samples.ise.http.Console;
import com.cisco.pxgrid.samples.ise.http.PxgridControl;
import com.cisco.pxgrid.samples.ise.http.SampleConfiguration;
import com.cisco.pxgrid.samples.ise.http.SampleHelper;
/**
* Demonstrates how to query a session using IP address
*/
public class AncClearByIp {
private static class EndpointPolicyByIpRequest {
private String ipAddress;
public void setIpAddress(String ipAddress) {
this.ipAddress = ipAddress;
}
ANCCreatePolicy
This code creates an ISE ANC policy based on Quarantine, Shut_down, and Port_Bounce Actions
package com.cisco.pxgrid.samples.ise.anc;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import com.cisco.pxgrid.model.AccountState;
import com.cisco.pxgrid.model.Service;
import com.cisco.pxgrid.samples.ise.http.Console;
import com.cisco.pxgrid.samples.ise.http.PxgridControl;
import com.cisco.pxgrid.samples.ise.http.SampleConfiguration;
import com.cisco.pxgrid.samples.ise.http.SampleHelper;
/**
* Demonstrates how to query a session using IP address
*/
public class AncCreatePolicy {
private static class Policy {
public enum Action {
QUARANTINE, SHUT_DOWN, PORT_BOUNCE
}
private String name;
private List<Action> actions;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Action> getActions() {
return actions;
}
public void setActions(List<Action> actions) {
this.actions = actions;
}
}
ANCDeletePolicy
This code deletes an ISE ANC policy
package com.cisco.pxgrid.samples.ise.anc;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import com.cisco.pxgrid.model.AccountState;
import com.cisco.pxgrid.model.Service;
import com.cisco.pxgrid.samples.ise.http.Console;
import com.cisco.pxgrid.samples.ise.http.PxgridControl;
import com.cisco.pxgrid.samples.ise.http.SampleConfiguration;
import com.cisco.pxgrid.samples.ise.http.SampleHelper;
/**
* Demonstrates how to query a session using IP address
*/
public class AncDeletePolicy {
private static class Policy {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
ANCGetByOperationID
This code obtains the OperationID for retrieving the ANC policy.
package com.cisco.pxgrid.samples.ise.anc;
import java.io.IOException;
import com.cisco.pxgrid.model.AccountState;
import com.cisco.pxgrid.model.Service;
import com.cisco.pxgrid.samples.ise.http.Console;
import com.cisco.pxgrid.samples.ise.http.PxgridControl;
import com.cisco.pxgrid.samples.ise.http.SampleConfiguration;
import com.cisco.pxgrid.samples.ise.http.SampleHelper;
/**
* Demonstrates how to query a session using IP address
*/
public class AncGetByOperationId {
private static class GetOperationStatusRequest {
private String operationId;
public void setOperationId(String operationId) {
this.operationId = operationId;
}
}
If the ecosystem partner has custom attributes that are not defined in the IOT Assets profiling dictionary, these can also
be published. These custom attributes are defined in JSON array in the custompublisher.java code. These are also
defined in the Custom Attributes profiling policies and defined in the ISE Identity Custom endpoint settings.
The Cisco ISE pxGrid node controller becomes the subscriber to the endpoint Asset topic and consumes this
information.
The pxGrid client publishes to the Asset topic, /topic/com.cisco.endpoint.asset. IO1 is the registered pxGrid client
publishing to this asset topic.
Running API_Simulator
The API Simulator is a tool that is used to provide pxGrid context-in simulating a pxGrid client asset device-
publishing asset attributes to the ISE pxgrid node.
The ./run_publisher38 script contains the command-line arguments to run the simulator, this also provides the access
secret between the publisher and ISE pxGrid node to begin publishing content from the asset device or client endpoint
in the script.
If you desire to modify the existing attributes in the script you must modify the custompublisher.java code. An
example is provided using Maven.
A profiling policy will be created based on the asset attribute values from the client device. The profiling policy will
then be assigned a Logical Profile that can be assigned in an ISE authorization condition rule that can be used in an
ISE authorization policy to determine network access for the asset device. We will also illustrate the same example
using custom attributes.
where:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.6.RELEASE)
username=IOT1
password=null
groups=Session
description=null
keystoreFilename=Johns-Macbook-Pro.lab10.com_Johns-Macbook-Pro.lab10.com.p12
keystorePassword=Cisco123
truststoreFilename=Johns-Macbook-Pro.lab10.com_Johns-Macbook-Pro.lab10.com.p12
truststorePassword=Cisco123
--------------------------
2018-04-14 13:18:19.919 INFO 5546 --- [ main] c.c.p.samples.ise.http.PxgridControl :
Request={}
2018-04-14 13:18:20.590 INFO 5546 --- [ main] c.c.p.samples.ise.http.PxgridControl :
Response={"accountState":"ENABLED","version":"2.0.0.13"}
14-Apr-18 13:18:20.591 [main-1]: pxGrid controller version=2.0.0.13
2018-04-14 13:18:20.601 INFO 5546 --- [ main] c.c.p.samples.ise.http.PxgridControl :
Request={"name":"com.cisco.endpoint.asset","properties":{"wsPubsubService":"com.cisco.ise.pubsub","restBaseUR
L":"http://raghdasa-lnv1:8080","assetTopic":"/topic/com.cisco.endpoint.asset"}}
2018-04-14 13:18:20.735 INFO 5546 --- [ main] c.c.p.samples.ise.http.PxgridControl :
Response={}
2018-04-14 13:18:20.742 INFO 5546 --- [ main] c.c.p.samples.ise.http.PxgridControl :
Request={"name":"com.cisco.ise.pubsub"}
2018-04-14 13:18:20.751 INFO 5546 --- [ main] c.c.p.samples.ise.http.PxgridControl :
Response={"services":[{"name":"com.cisco.ise.pubsub","nodeName":"ise-pubsub-
ise24fc3","properties":{"wsUrl":"wss://ise24fc3.lab10.com:8910/pxgrid/ise/pubsub"}}]}
14-Apr-18 13:18:20.751 [main-1]: wsUrl=wss://ise24fc3.lab10.com:8910/pxgrid/ise/pubsub
2018-04-14 13:18:20.757 INFO 5546 --- [ main] c.c.p.samples.ise.http.PxgridControl :
Request={"peerNodeName":"ise-pubsub-ise24fc3"}
2018-04-14 13:18:20.909 INFO 5546 --- [ main] c.c.p.samples.ise.http.PxgridControl :
Response={"secret":"tASXtHAbKDKbAODh"}
2018-04-14 13:18:22.072 INFO 5546 --- [ Grizzly(1)] c.c.p.s.i.h.StompPubsubClientEndpoint : WS onOpen
2018-04-14 13:18:22.075 INFO 5546 --- [ main] c.c.p.s.i.h.StompPubsubClientEndpoint : STOMP
CONNECT host=ise24fc3.lab10.com
press <enter> to start the publishing...2018-04-14 13:18:22.084 INFO 5546 --- [ Grizzly(2)]
c.c.p.s.i.h.StompPubsubClientEndpoint : STOMP CONNECTED version=1.2
Step 2 Click on the MAC address, and select Attributes, you will see the attributes:
Step 1 Create IOT device SGT which will be used in the Authorization Policy
Select WorkCenters->Trustsec->Components->Add->IOT Devices
Step 6 Under Actions, Click on “gear” and “Insert new rule above
Step 7 You should see the following:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.6.RELEASE)
Step 3 If you click on either of the MAC addresses, you will see the logical profile
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.6.RELEASE)
java.lang.Object>>
org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2018-04-15 00:16:27.039 INFO 7211 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped
"{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView
org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletReque
st,javax.servlet.http.HttpServletResponse)
2018-04-15 00:16:27.080 INFO 7211 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped
URL path [/webjars/**] onto handler of type [class
org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-04-15 00:16:27.080 INFO 7211 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped
URL path [/**] onto handler of type [class
org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-04-15 00:16:27.153 INFO 7211 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped
URL path [/**/favicon.ico] onto handler of type [class
org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2018-04-15 00:16:27.508 INFO 7211 --- [ main] o.s.j.e.a.AnnotationMBeanExporter :
Registering beans for JMX exposure on startup
2018-04-15 00:16:27.847 INFO 7211 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat
started on port(s): 8080 (http)
2018-04-15 00:16:27.858 INFO 7211 --- [ main] c.c.p.samples.ise.http.CustomPublisher : Started
CustomPublisher in 13.385 seconds (JVM running for 21.849)
------- properties -------
hostnames=ise24fc3.lab10.com
username=IOT1
password=null
groups=Session
description=null
keystoreFilename=Johns-Macbook-Pro.lab10.com_Johns-Macbook-Pro.lab10.com.p12
keystorePassword=Cisco123
truststoreFilename=Johns-Macbook-Pro.lab10.com_Johns-Macbook-Pro.lab10.com.p12
truststorePassword=Cisco123
--------------------------
2018-04-15 00:16:30.642 INFO 7211 --- [ main] c.c.p.samples.ise.http.PxgridControl :
Request={}
2018-04-15 00:16:30.740 INFO 7211 --- [ main] c.c.p.samples.ise.http.PxgridControl :
Response={"accountState":"ENABLED","version":"2.0.0.13"}
15-Apr-18 00:16:30.741 [main-1]: pxGrid controller version=2.0.0.13
2018-04-15 00:16:30.750 INFO 7211 --- [ main] c.c.p.samples.ise.http.PxgridControl :
Request={"name":"com.cisco.endpoint.asset","properties":{"wsPubsubService":"com.cisco.ise.pubsub","restBaseUR
L":"http://raghdasa-lnv1:8080","assetTopic":"/topic/com.cisco.endpoint.asset"}}
2018-04-15 00:16:30.906 INFO 7211 --- [ main] c.c.p.samples.ise.http.PxgridControl :
Response={}
2018-04-15 00:16:30.913 INFO 7211 --- [ main] c.c.p.samples.ise.http.PxgridControl :
Request={"name":"com.cisco.ise.pubsub"}
2018-04-15 00:16:30.924 INFO 7211 --- [ main] c.c.p.samples.ise.http.PxgridControl :
Response={"services":[{"name":"com.cisco.ise.pubsub","nodeName":"ise-pubsub-
ise24fc3","properties":{"wsUrl":"wss://ise24fc3.lab10.com:8910/pxgrid/ise/pubsub"}}]}
15-Apr-18 00:16:30.924 [main-1]: wsUrl=wss://ise24fc3.lab10.com:8910/pxgrid/ise/pubsub
2018-04-15 00:16:30.929 INFO 7211 --- [ main] c.c.p.samples.ise.http.PxgridControl :
Request={"peerNodeName":"ise-pubsub-ise24fc3"}
2018-04-15 00:16:30.955 INFO 7211 --- [ main] c.c.p.samples.ise.http.PxgridControl :
Response={"secret":"tASXtHAbKDKbAODh"}
2018-04-15 00:16:33.757 INFO 7211 --- [ Grizzly(1)] c.c.p.s.i.h.StompPubsubClientEndpoint : WS onOpen
2018-04-15 00:16:33.760 INFO 7211 --- [ main] c.c.p.s.i.h.StompPubsubClientEndpoint : STOMP
CONNECT host=ise24fc3.lab10.com
press <enter> to start the publishing...2018-04-15 00:16:33.768 INFO 7211 --- [ Grizzly(2)]
c.c.p.s.i.h.StompPubsubClientEndpoint : STOMP CONNECTED version=1.2
command=SEND, headers={'content-length':'591','destination':'/topic/com.cisco.endpoint.asset',},
content.length=591
15-Apr-18 00:19:00.175 [main-1]:
{"assetHwRevision":"5.6","assetProtocol":"CIP","assetConnectedLinks":[{"value":"3","key":"indattr2"},{"value"
:"Root","key":"assetGroup"},{"value":"1","key":"indattr3"}],"assetVendor":"Cisco
Systems","assetSwRevision":"4.6","assetCustomAttributes":[{"value":"3","key":"indattr2"},{"value":"Root","key
":"assetGroup"},{"value":"1","key":"indattr3"}],"assetProductId":"IE2000","assetSerialNumber":"1212121213243"
,"assetMacAddress":"48:b2:d0:63:d1:32","assetId":"260","assetIpAddress":"56.56.217.16","assetName":"Abjergary
n - 47","assetDeviceType":"EtherNet\/IP Node"}
Step 1 Create IOT device SGT which will be used in the Authorization Policy
Select WorkCenters->Trustsec->Components->Add->CustomIOTDevices
Step 6 Under Actions, Click on “gear” and “Insert new rule above
Step 7 You should see the following:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.6.RELEASE)
command=SEND, headers={'content-length':'593','destination':'/topic/com.cisco.endpoint.asset',},
content.length=593
14-Apr-18 15:42:29.802 [main-1]:
{"assetHwRevision":"5.6","assetProtocol":"CIP","assetConnectedLinks":[{"value":"3","key":"indattr2"},{"value"
:"Root","key":"assetGroup"},{"value":"1","key":"indattr3"}],"assetVendor":"Cisco
Systems","assetSwRevision":"4.6","assetCustomAttributes":[{"value":"3","key":"indattr2"},{"value":"Root","key
":"assetGroup"},{"value":"1","key":"indattr3"}],"assetProductId":"IE2000","assetSerialNumber":"1212121213243"
,"assetMacAddress":"b0:2c:27:93:fe:94","assetId":"215","assetIpAddress":"125.84.172.120","assetName":"Abjerga
ryn - 49","assetDeviceType":"EtherNet\/IP Node"}
Step 3 If you click on the MAC address, you will see the logical profile of CustomIOT devices. You may also see
IOTDevices if you did not remove the CutomIOT device policy from IOT devices logical profile.
/Users/jeppich/maven/repo/pxgrid-rest-ws
Step 2 Go to the java folder, and see the pom.xml file and the src folder
/Users/jeppich/maven/repo/pxgrid-rest-ws/java/src/main/java/com/cisco/pxgrid/samples/ise/http
Console.java SampleHelper.java
CustomPublisher.java SessionQueryAll.java
CustomSubscriber.java SessionQueryByIP.java
DeviceList.java SessionSubscribe.java
Devices.java StompFrame.java
PublisherController.java StompPubsubClientEndpoint.java
PxgridControl.java StompSubscription.java
SampleConfiguration.java
Step 5 Edit the CustomPublisher.java file, to change any of the values, keep the prefix name (i.e.)
object.put(prefix+"Vendor", and change the value name (i.e. "Cisco Systems"); in the section of the
code below. The (prefix + “.”) values cannot be changed as they represent the values if the IOTASSETS
dictionary.
package com.cisco.pxgrid.samples.ise.http;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import java.util.Random;
import org.glassfish.tyrus.client.ClientManager;
import org.glassfish.tyrus.client.ClientProperties;
import org.glassfish.tyrus.client.SslEngineConfigurator;
import org.glassfish.tyrus.client.auth.Credentials;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import java.util.concurrent.ThreadLocalRandom;
import com.cisco.pxgrid.model.AccountState;
import com.cisco.pxgrid.model.Service;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Sample creation of a Dynamic Service by a client, also publishes. Another Client may subscribe to this
service and receive
* notifications when this service is published to.
*
* USE CASE for Dynamic Services: My company that requires all devices that connect to our network to be
specially authenticated so they need
* more information. Using pxGrid dynamic services, I can set up a service that broadcasts the requirements
to all devices that connect and tell
* them to send this information over to allow for authentication.
*
* @author anirvenk
*/
@SpringBootApplication
public class CustomPublisher {
private static final String SERVICE_NAME = "com.cisco.endpoint.asset";
private static final String TOPIC_PATH = "/topic/com.cisco.endpoint.asset";
private static final String PUBSUBSERVICE = "com.cisco.ise.pubsub";
public static int counter = 0;
private static String prefix = "asset";
private static int counterIncrement = 1;
/**
* Static class that is used to create a sample Service object.
*/
public static Service createService() {
Service service = new Service();
service.setName(SERVICE_NAME);
service.setNodeName("dynamic capability");
Map<String, String> properties = new HashMap<String, String>();
properties.put("wsPubsubService", PUBSUBSERVICE);
properties.put("assetTopic", TOPIC_PATH);
properties.put("restBaseURL", "http://raghdasa-lnv1:8080");
service.setProperties(properties);
return service;
}
public static String getRandomIpAddress(){
Random r = new Random();
return r.nextInt(256) + "." + r.nextInt(256) + "." + r.nextInt(256) + "." + r.nextInt(256);
}
public static String getRandomMacAddress(){
Random rand = new Random();
byte[] macAddr = new byte[6];
rand.nextBytes(macAddr);
macAddr[0] = (byte)(macAddr[0] & (byte)254); //zeroing last 2 bytes to make it unicast and locally
adminstrated
if(sb.length() > 0)
sb.append(":");
sb.append(String.format("%02x", b));
}
return sb.toString();
}
public static JSONObject createJsonObject() {
JSONObject object = new JSONObject();
//change prefix to asset later
object.put(prefix+"Id",
Integer.toString(1+counterIncrement)+Integer.toString(15*ThreadLocalRandom.current().nextInt(1, 6 + 1)));
object.put(prefix+"Name", "Abjergaryn -
4"+Integer.toString(ThreadLocalRandom.current().nextInt(1, 9 + 1)));
object.put(prefix+"IpAddress", getRandomIpAddress());
//+Integer.toString(ThreadLocalRandom.current().nextInt(45, 100 + 1)));
//object.put(prefix+"MacAddress", "28:63:36:a2:94:"+Integer.toString(30+counterIncrement));
object.put(prefix+"MacAddress", getRandomMacAddress());
object.put(prefix+"Vendor", "Cisco Systems");
object.put(prefix+"ProductId", "IE2000");
object.put(prefix+"SerialNumber", "1212121213243");
object.put(prefix+"DeviceType", "EtherNet/IP Node");
object.put(prefix+"SwRevision", "4.6");
object.put(prefix+"HwRevision", "5.6");
object.put(prefix+"Protocol", "CIP");
JSONArray customAttr = new JSONArray();
JSONArray connectedLinks = new JSONArray();
// AccountActivate
while (control.accountActivate() != AccountState.ENABLED) {
Thread.sleep(45000);
}
Console.log("pxGrid controller version=" + control.getControllerVersion());
Service[] list_of_services;
//get wsURL so we can get the URI later from it via REST query.
String wsURL = wsPubsubService.getProperties().get("wsUrl");
Console.log("wsUrl=" + wsURL);
// pxGrid AccessSecret
String secret = control.getAccessSecret(wsPubsubService.getNodeName());
//setting up client manager which will use ssl connection for authentication.
ClientManager client = ClientManager.createClient();
SslEngineConfigurator sslEngineConfigurator = new
SslEngineConfigurator(config.getSSLContext());
sslEngineConfigurator.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator);
client.getProperties().put(ClientProperties.CREDENTIALS,
new Credentials(config.getUserName(), secret.getBytes()));
// WebSocket connect
StompPubsubClientEndpoint endpoint = new StompPubsubClientEndpoint();
//get URI, connect pxGrid client to the pxGrid server so that we can publish to dynamic
service
URI uri = new URI(wsURL);
// STOMP connect
endpoint.connect(uri.getHost());
/*
* publishing to the dynamic service. This message "dynamic topic publish" will be received by
all subscribers to the service.
* only triggers when key is pressed. This is to make it so we can subscribe another client to
the service by running
* DynamicServiceSubscribe.java to see if it receives the published info.
*/
SampleHelper.prompt("press <enter> to start the publishing...");
device2.put(prefix+"SwRevision", "7.8");
device3.put(prefix+"SwRevision", "3.3");
device3.put(prefix+"HwRevision", "2.5");
device4.put(prefix+"SwRevision", "3.5");
deviceArr.add(device1);
deviceArr.add(device2);
deviceArr.add(device3);
deviceArr.add(device4);
device_object.put("asset", device1);
device_object.put("opType","UPDATE");
// STOMP disconnect
//endpoint.disconnect("ID-123");
// Wait for disconnect receipt
//Thread.sleep(3000);
//session.close();
}
}
Step 6 Change the Vendor Name to “ACME” and Save the file
Step 7 Go to folder
johns-macbook-pro:java jeppich$ ls
pom.xml src
run_publisher.sh target
johns-macbook-pro:java jeppich$
Step 9 Replace the /Applications/api_partner_fc3/api_simulator/snapshot.jar file with the one that was just
created. (i.e. /Users/jeppich/.m2/repository/com/cisco/pxgrid/pxgrid-rest-ws-samples/2.0.0-
SNAPSHOT)
cp pxgrid-rest-ws-samples-2.0.0-SNAPSHOT.jar /Applications/api_partner_fc3/api_simulator
Step 10 Run the script, you should see “ACME” in the Asset Vendor
Step 11 You can now create a Profiling Policy based on IOTASSET assetvendor attribute using
Step 13 Select Context Visibility->Endpoints->Authentications, you should see “VendorACME” for the
EndpointProfile
Note: If you see, “CustomIOTDevices”, disable “CustomIOTDevices” profiling policy for this exercise.
/Users/jeppich/maven/repo/pxgrid-rest-ws
Step 2 Go to the java folder, and see the pom.xml file and the src folder
/Users/jeppich/maven/repo/pxgrid-rest-ws/java/src/main/java/com/cisco/pxgrid/samples/ise/http
Console.java SampleHelper.java
CustomPublisher.java SessionQueryAll.java
CustomSubscriber.java SessionQueryByIP.java
DeviceList.java SessionSubscribe.java
Devices.java StompFrame.java
PublisherController.java StompPubsubClientEndpoint.java
PxgridControl.java StompSubscription.java
SampleConfiguration.java
Step 5 Edit the CustomPublisher.java file, replace object1.put (“key”, “inadttr2”) with object1.put (“key”,
“CVSS”)”
Step 6 Also replace object1.put(“value”,”3”) with object1.put (“value”,”7”) in the highlighted fields below.
These fields will represent the custom attribute values of “key” and “value” that will be added to the ISE
identity custom attribute screen and also when creating the CUSTOMATTRIBUTE profiling policy.
package com.cisco.pxgrid.samples.ise.http;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import java.util.Random;
import org.glassfish.tyrus.client.ClientManager;
import org.glassfish.tyrus.client.ClientProperties;
import org.glassfish.tyrus.client.SslEngineConfigurator;
import org.glassfish.tyrus.client.auth.Credentials;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import java.util.concurrent.ThreadLocalRandom;
import com.cisco.pxgrid.model.AccountState;
import com.cisco.pxgrid.model.Service;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Sample creation of a Dynamic Service by a client, also publishes. Another Client may subscribe to this
service and receive
* notifications when this service is published to.
*
* USE CASE for Dynamic Services: My company that requires all devices that connect to our network to be
specially authenticated so they need
* more information. Using pxGrid dynamic services, I can set up a service that broadcasts the requirements
to all devices that connect and tell
* them to send this information over to allow for authentication.
*
* @author anirvenk
*/
@SpringBootApplication
public class CustomPublisher {
private static final String SERVICE_NAME = "com.cisco.endpoint.asset";
private static final String TOPIC_PATH = "/topic/com.cisco.endpoint.asset";
private static final String PUBSUBSERVICE = "com.cisco.ise.pubsub";
public static int counter = 0;
private static String prefix = "asset";
private static int counterIncrement = 1;
/**
* Static class that is used to create a sample Service object.
*/
public static Service createService() {
Service service = new Service();
service.setName(SERVICE_NAME);
service.setNodeName("dynamic capability");
Map<String, String> properties = new HashMap<String, String>();
properties.put("wsPubsubService", PUBSUBSERVICE);
properties.put("assetTopic", TOPIC_PATH);
properties.put("restBaseURL", "http://raghdasa-lnv1:8080");
service.setProperties(properties);
return service;
}
public static String getRandomIpAddress(){
Random r = new Random();
return r.nextInt(256) + "." + r.nextInt(256) + "." + r.nextInt(256) + "." + r.nextInt(256);
}
public static String getRandomMacAddress(){
Random rand = new Random();
byte[] macAddr = new byte[6];
rand.nextBytes(macAddr);
macAddr[0] = (byte)(macAddr[0] & (byte)254); //zeroing last 2 bytes to make it unicast and locally
adminstrated
if(sb.length() > 0)
sb.append(":");
sb.append(String.format("%02x", b));
}
return sb.toString();
}
public static JSONObject createJsonObject() {
JSONObject object = new JSONObject();
//change prefix to asset later
object.put(prefix+"Id",
Integer.toString(1+counterIncrement)+Integer.toString(15*ThreadLocalRandom.current().nextInt(1, 6 + 1)));
object.put(prefix+"Name", "Abjergaryn -
4"+Integer.toString(ThreadLocalRandom.current().nextInt(1, 9 + 1)));
object.put(prefix+"IpAddress", getRandomIpAddress());
//+Integer.toString(ThreadLocalRandom.current().nextInt(45, 100 + 1)));
//object.put(prefix+"MacAddress", "28:63:36:a2:94:"+Integer.toString(30+counterIncrement));
object.put(prefix+"MacAddress", getRandomMacAddress());
object.put(prefix+"Vendor", "Cisco Systems");
object.put(prefix+"ProductId", "IE2000");
object.put(prefix+"SerialNumber", "1212121213243");
object.put(prefix+"DeviceType", "EtherNet/IP Node");
object.put(prefix+"SwRevision", "4.6");
object.put(prefix+"HwRevision", "5.6");
object.put(prefix+"Protocol", "CIP");
JSONArray customAttr = new JSONArray();
JSONArray connectedLinks = new JSONArray();
// AccountActivate
while (control.accountActivate() != AccountState.ENABLED) {
Thread.sleep(45000);
}
Console.log("pxGrid controller version=" + control.getControllerVersion());
Service[] list_of_services;
//get wsURL so we can get the URI later from it via REST query.
String wsURL = wsPubsubService.getProperties().get("wsUrl");
Console.log("wsUrl=" + wsURL);
// pxGrid AccessSecret
String secret = control.getAccessSecret(wsPubsubService.getNodeName());
//setting up client manager which will use ssl connection for authentication.
ClientManager client = ClientManager.createClient();
SslEngineConfigurator sslEngineConfigurator = new
SslEngineConfigurator(config.getSSLContext());
sslEngineConfigurator.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator);
client.getProperties().put(ClientProperties.CREDENTIALS,
new Credentials(config.getUserName(), secret.getBytes()));
// WebSocket connect
StompPubsubClientEndpoint endpoint = new StompPubsubClientEndpoint();
//get URI, connect pxGrid client to the pxGrid server so that we can publish to dynamic
service
URI uri = new URI(wsURL);
// STOMP connect
endpoint.connect(uri.getHost());
/*
* publishing to the dynamic service. This message "dynamic topic publish" will be received by
all subscribers to the service.
* only triggers when key is pressed. This is to make it so we can subscribe another client to
the service by running
* DynamicServiceSubscribe.java to see if it receives the published info.
*/
SampleHelper.prompt("press <enter> to start the publishing...");
device2.put(prefix+"SwRevision", "7.8");
device3.put(prefix+"SwRevision", "3.3");
device3.put(prefix+"HwRevision", "2.5");
device4.put(prefix+"SwRevision", "3.5");
deviceArr.add(device1);
deviceArr.add(device2);
deviceArr.add(device3);
deviceArr.add(device4);
device_object.put("asset", device1);
device_object.put("opType","UPDATE");
byte[] array = device_object.toJSONString().getBytes(StandardCharsets.UTF_8);
endpoint.publish(TOPIC_PATH, array);
*/
// STOMP disconnect
//endpoint.disconnect("ID-123");
// Wait for disconnect receipt
//Thread.sleep(3000);
//session.close();
}
}
Step 7 Go to folder
johns-macbook-pro:java jeppich$ ls
pom.xml src
run_publisher.sh target
johns-macbook-pro:java jeppich$
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ pxgrid-rest-ws-samples ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 40 source files to /Users/jeppich/maven/repo/pxgrid-rest-ws/java/target/classes
[WARNING] /Users/jeppich/maven/repo/pxgrid-rest-
ws/java/src/main/java/com/cisco/pxgrid/samples/ise/http/CustomPublisher.java:
/Users/jeppich/maven/repo/pxgrid-rest-
ws/java/src/main/java/com/cisco/pxgrid/samples/ise/http/CustomPublisher.java uses unchecked or unsafe
operations.
[WARNING] /Users/jeppich/maven/repo/pxgrid-rest-
ws/java/src/main/java/com/cisco/pxgrid/samples/ise/http/CustomPublisher.java: Recompile with -Xlint:unchecked
for details.
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ pxgrid-rest-ws-samples ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/jeppich/maven/repo/pxgrid-rest-ws/java/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ pxgrid-rest-ws-samples ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-surefire-plugin:2.18.1:test (default-test) @ pxgrid-rest-ws-samples ---
[INFO] No tests to run.
[INFO]
[INFO] --- maven-jar-plugin:2.6:jar (default-jar) @ pxgrid-rest-ws-samples ---
[INFO] Building jar: /Users/jeppich/maven/repo/pxgrid-rest-ws/java/target/pxgrid-rest-ws-samples-2.0.0-
SNAPSHOT.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:1.5.6.RELEASE:repackage (default) @ pxgrid-rest-ws-samples ---
[INFO]
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ pxgrid-rest-ws-samples ---
[INFO] Installing /Users/jeppich/maven/repo/pxgrid-rest-ws/java/target/pxgrid-rest-ws-samples-2.0.0-
SNAPSHOT.jar to /Users/jeppich/.m2/repository/com/cisco/pxgrid/pxgrid-rest-ws-samples/2.0.0-SNAPSHOT/pxgrid-
rest-ws-samples-2.0.0-SNAPSHOT.jar
[INFO] Installing /Users/jeppich/maven/repo/pxgrid-rest-ws/java/pom.xml to
/Users/jeppich/.m2/repository/com/cisco/pxgrid/pxgrid-rest-ws-samples/2.0.0-SNAPSHOT/pxgrid-rest-ws-samples-
2.0.0-SNAPSHOT.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 19.717 s
[INFO] Finished at: 2018-04-21T21:47:59-04:00
[INFO] ------------------------------------------------------------------------
johns-macbook-pro:java jeppich$
Step 9 Replace the /Applications/api_partner_fc3/api_simulator/snapshot.jar file with the one that was just
created. (i.e /Users/jeppich/.m2/repository/com/cisco/pxgrid/pxgrid-rest-ws-samples/2.0.0-
SNAPSHOT)
cp pxgrid-rest-ws-samples-2.0.0-SNAPSHOT.jar /Applications/api_partner_fc3/api_simulator
Step 10 Run the script, you should see “value”:”7”, “key”:”CVSS” in the output
Id":"275","assetIpAddress":"117.65.215.140","assetName":"Abjergaryn - 43","assetDeviceType":"EtherNet\/IP
Node"}
Step 15 Run the script, you should see “value”:”7”, “key”:”CVSS” in the output
Step 16 Select Context Visibility->Endpoints->Authentication, you will see CVSSAttribute Endpoint Profile
package com.cisco.pxgrid.samples.ise.http;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.Socket;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Enumeration;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509KeyManager;
import javax.net.ssl.X509TrustManager;
keystoreFilename = System.getProperty(PROP_KEYSTORE_FILENAME);
keystorePassword = System.getProperty(PROP_KEYSTORE_PASSWORD);
truststoreFilename = System.getProperty(PROP_TRUSTSTORE_FILENAME);
truststorePassword = System.getProperty(PROP_TRUSTSTORE_PASSWORD);
hostnames = hostnameProperty.split(",");
if (description != null) {
if (description.isEmpty()) description = null;
else description = description.trim();
}
sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(getKeyManagers(), getTrustManagers(), null);
}
ks.load(in, keystorePassword.toCharArray());
in.close();
KeyManagerFactory kmf =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, keystorePassword.toCharArray());
KeyManager[] mngrs = kmf.getKeyManagers();
KeyStore ks = null;
if(truststoreFilename.endsWith(".pem")) {
ks = KeyStore.getInstance("JKS");
ks.load(null, null);
CertificateFactory certFac = CertificateFactory.getInstance("X.509");
Collection<? extends Certificate> certs = certFac.generateCertificates(in);
int i = 0;
for(Certificate c : certs) {
ks.setCertificateEntry("trust-" + i, c);
}
} else if(truststoreFilename.endsWith(".p12")) {
ks = KeyStore.getInstance("pkcs12");
ks.load(in, truststorePassword.toCharArray());
} else {
ks = KeyStore.getInstance("JKS");
ks.load(in, truststorePassword.toCharArray());
}
in.close();
Enumeration<String> e = ks.aliases();
boolean hasCertEntries = false;
while (e.hasMoreElements()) {
String alias = e.nextElement();
if (ks.isCertificateEntry(alias)) {
hasCertEntries = true;
}
}
if (hasCertEntries == false) {
e = ks.aliases();
while (e.hasMoreElements()) {
String alias = e.nextElement();
if (ks.isKeyEntry(alias)) {
Certificate[] chain = ks.getCertificateChain(alias);
TrustManagerFactory tmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
@Override
public String chooseClientAlias(String[] arg0, Principal[] arg1, Socket arg2) {
String alias = mngr.chooseClientAlias(arg0, arg1, arg2);
if (alias == null) {
alias = mngr.chooseClientAlias(arg0, null, arg2);
if (alias == null) {
throw new RuntimeException("no client certificate found ...");
}
}
return alias;
}
@Override
public String chooseServerAlias(String arg0, Principal[] arg1, Socket arg2) {
throw new RuntimeException("Not implemented");
}
@Override
public X509Certificate[] getCertificateChain(String arg0) {
return mngr.getCertificateChain(arg0);
}
@Override
public String[] getClientAliases(String arg0, Principal[] arg1) {
return mngr.getClientAliases(arg0, null);
}
@Override
public PrivateKey getPrivateKey(String arg0) {
return mngr.getPrivateKey(arg0);
}
@Override
public String[] getServerAliases(String arg0, Principal[] arg1) {
throw new RuntimeException("Not implemented");
}
}
@Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws
CertificateException {
throw new RuntimeException("not implemented");
}
@Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws
CertificateException {
try {
mngr.checkServerTrusted(arg0, arg1);
} catch (CertificateException e) {
throw new CertificateException("Server certificate is not trusted:" +
arg0[0].getSubjectX500Principal(),
e);
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return mngr.getAcceptedIssuers();
}
}
PxgridControl
This code provides the pxGrid client with account creation on the ISE pxGrid node and service lookup request and
access secret to the peer node.
package com.cisco.pxgrid.samples.ise.http;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.util.Base64;
import java.util.Map;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.cisco.pxgrid.model.AccessSecretRequest;
import com.cisco.pxgrid.model.AccessSecretResponse;
import com.cisco.pxgrid.model.AccountActivateRequest;
import com.cisco.pxgrid.model.AccountActivateResponse;
import com.cisco.pxgrid.model.AccountCreateRequest;
import com.cisco.pxgrid.model.AccountCreateResponse;
import com.cisco.pxgrid.model.AccountState;
import com.cisco.pxgrid.model.Authorization;
import com.cisco.pxgrid.model.AuthorizationRequest;
import com.cisco.pxgrid.model.AuthorizationResponse;
import com.cisco.pxgrid.model.Service;
import com.cisco.pxgrid.model.ServiceLookupRequest;
import com.cisco.pxgrid.model.ServiceLookupResponse;
import com.cisco.pxgrid.model.ServiceRegisterRequest;
import com.cisco.pxgrid.model.ServiceRegisterResponse;
import com.google.gson.Gson;
/**
* Using HTTPS for pxGrid control
*/
public class PxgridControl {
private static Logger logger = LoggerFactory.getLogger(PxgridControl.class);
private SampleConfiguration config;
private String controllerVersion;
return response;
}
https.setRequestMethod("POST");
return https;
}
/**
* Create new account
*
* @return password
*/
public String accountCreate() throws IOException {
HttpsURLConnection https = getHttpsURLConnection("AccountCreate");
AccountCreateRequest request = new AccountCreateRequest();
request.setNodeName(config.getUserName());
request.setServiceOperation(operation);
PublisherController
This code publishes the “assets” topic and looks like the following:
package com.cisco.pxgrid.samples.ise.http;
import java.util.concurrent.ThreadLocalRandom;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class PublisherController {
@RequestMapping("/getAssets")
public DeviceList device() {
Devices[] device_list = new Devices[5];
for(int i = 0; i < 5; i++) {
device_list[i] = DeviceGenerator(i);
}
return new DeviceList(device_list);
}
}
Custom Publisher
The CustomPublisher Java code publishes the asset device attributes by calling the service name
“com.cisco.endpoint.asset”, topic path “/topic/com.cisco.endpoint.asset”, PUBSERVCE “com.cisco.ise.pubsub.
This will return the wsPubsubServce: “com.cisco.ise.pubsub”, restbaseURL http://raghdasa-lnv1:8080, and assetTopic,
“/topic/com.cisco.endpoint.asset”.
package com.cisco.pxgrid.samples.ise.http;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import java.util.Random;
import org.glassfish.tyrus.client.ClientManager;
import org.glassfish.tyrus.client.ClientProperties;
import org.glassfish.tyrus.client.SslEngineConfigurator;
import org.glassfish.tyrus.client.auth.Credentials;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import java.util.concurrent.ThreadLocalRandom;
import com.cisco.pxgrid.model.AccountState;
import com.cisco.pxgrid.model.Service;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Sample creation of a Dynamic Service by a client, also publishes. Another Client may subscribe to this
service and receive
* notifications when this service is published to.
*
* USE CASE for Dynamic Services: My company that requires all devices that connect to our network to be
specially authenticated so they need
* more information. Using pxGrid dynamic services, I can set up a service that broadcasts the requirements
to all devices that connect and tell
* them to send this information over to allow for authentication.
*
* @author anirvenk
*/
@SpringBootApplication
/**
* Static class that is used to create a sample Service object.
*/
public static Service createService() {
Service service = new Service();
service.setName(SERVICE_NAME);
service.setNodeName("dynamic capability");
Map<String, String> properties = new HashMap<String, String>();
properties.put("wsPubsubService", PUBSUBSERVICE);
properties.put("assetTopic", TOPIC_PATH);
properties.put("restBaseURL", "http://raghdasa-lnv1:8080");
service.setProperties(properties);
return service;
}
public static String getRandomIpAddress(){
Random r = new Random();
return r.nextInt(256) + "." + r.nextInt(256) + "." + r.nextInt(256) + "." + r.nextInt(256);
}
public static String getRandomMacAddress(){
Random rand = new Random();
byte[] macAddr = new byte[6];
rand.nextBytes(macAddr);
macAddr[0] = (byte)(macAddr[0] & (byte)254); //zeroing last 2 bytes to make it unicast and locally
adminstrated
if(sb.length() > 0)
sb.append(":");
sb.append(String.format("%02x", b));
}
return sb.toString();
}
public static JSONObject createJsonObject() {
JSONObject object = new JSONObject();
//change prefix to asset later
object.put(prefix+"Id",
Integer.toString(1+counterIncrement)+Integer.toString(15*ThreadLocalRandom.current().nextInt(1, 6 + 1)));
object.put(prefix+"Name", "Abjergaryn -
4"+Integer.toString(ThreadLocalRandom.current().nextInt(1, 9 + 1)));
object.put(prefix+"IpAddress", getRandomIpAddress());
//+Integer.toString(ThreadLocalRandom.current().nextInt(45, 100 + 1)));
//object.put(prefix+"MacAddress", "28:63:36:a2:94:"+Integer.toString(30+counterIncrement));
object.put(prefix+"MacAddress", getRandomMacAddress());
object.put(prefix+"Vendor", "ACME");
object.put(prefix+"ProductId", "IE2000");
object.put(prefix+"SerialNumber", "1212121213243");
object.put(prefix+"DeviceType", "EtherNet/IP Node");
object.put(prefix+"SwRevision", "4.6");
object.put(prefix+"HwRevision", "5.6");
object.put(prefix+"Protocol", "CIP");
JSONArray customAttr = new JSONArray();
JSONArray connectedLinks = new JSONArray();
// AccountActivate
while (control.accountActivate() != AccountState.ENABLED) {
Thread.sleep(45000);
}
Console.log("pxGrid controller version=" + control.getControllerVersion());
Service[] list_of_services;
//get wsURL so we can get the URI later from it via REST query.
String wsURL = wsPubsubService.getProperties().get("wsUrl");
Console.log("wsUrl=" + wsURL);
// pxGrid AccessSecret
String secret = control.getAccessSecret(wsPubsubService.getNodeName());
//setting up client manager which will use ssl connection for authentication.
ClientManager client = ClientManager.createClient();
SslEngineConfigurator sslEngineConfigurator = new
SslEngineConfigurator(config.getSSLContext());
sslEngineConfigurator.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator);
client.getProperties().put(ClientProperties.CREDENTIALS,
new Credentials(config.getUserName(), secret.getBytes()));
// WebSocket connect
StompPubsubClientEndpoint endpoint = new StompPubsubClientEndpoint();
//get URI, connect pxGrid client to the pxGrid server so that we can publish to dynamic
service
URI uri = new URI(wsURL);
// STOMP connect
endpoint.connect(uri.getHost());
/*
* publishing to the dynamic service. This message "dynamic topic publish" will be received by
all subscribers to the service.
* only triggers when key is pressed. This is to make it so we can subscribe another client to
the service by running
* DynamicServiceSubscribe.java to see if it receives the published info.
*/
SampleHelper.prompt("press <enter> to start the publishing...");
device2.put(prefix+"SwRevision", "7.8");
device3.put(prefix+"SwRevision", "3.3");
device3.put(prefix+"HwRevision", "2.5");
device4.put(prefix+"SwRevision", "3.5");
deviceArr.add(device1);
deviceArr.add(device2);
deviceArr.add(device3);
deviceArr.add(device4);
device_object.put("asset", device1);
device_object.put("opType","UPDATE");
byte[] array = device_object.toJSONString().getBytes(StandardCharsets.UTF_8);
endpoint.publish(TOPIC_PATH, array);
*/
// STOMP disconnect
//endpoint.disconnect("ID-123");
// Wait for disconnect receipt
//Thread.sleep(3000);
//session.close();
}
}
CustomSubscriber
This code sets up the subscriber to the published topic, in the case, the ISE pxGrid node.
package com.cisco.pxgrid.samples.ise.http;
import java.net.URI;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
import org.glassfish.tyrus.client.ClientManager;
import org.glassfish.tyrus.client.ClientProperties;
import org.glassfish.tyrus.client.SslEngineConfigurator;
import org.glassfish.tyrus.client.auth.Credentials;
import com.cisco.pxgrid.model.AccountState;
import com.cisco.pxgrid.model.Service;
/**
* Demonstrates how to subscribe to a Dynamic Service using REST/WS.
* Works hand in hand with Dynamic Service.java class.
*/
public class CustomSubscriber {
// Subscribe handler class
private static class SessionHandler implements StompSubscription.Handler {
@Override
public void handle(StompFrame message) {
System.out.println(new String(message.getContent()));
}
}
// AccountActivate
while (control.accountActivate() != AccountState.ENABLED) {
Thread.sleep(60000);
}
Console.log("pxGrid controller version=" + control.getControllerVersion());
// Pubsub ServiceLookup
services = control.lookupService(wsPubsubServiceName);
if (services.length == 0) {
Console.log("Pubsub service unavailabe");
return;
}
// Select first one which ends up being the pxGrid server node. Should cycle through until
connects.
Service wsPubsubService = services[0];
String wsURL = wsPubsubService.getProperties().get("wsUrl");
Console.log("wsUrl=" + wsURL);
// pxGrid AccessSecret
String secret = control.getAccessSecret(wsPubsubService.getNodeName());
//setting up client manager which will use ssl connection for authentication.
ClientManager client = ClientManager.createClient();
SslEngineConfigurator sslEngineConfigurator = new
SslEngineConfigurator(config.getSSLContext());
sslEngineConfigurator.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
client.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngineConfigurator);
client.getProperties().put(ClientProperties.CREDENTIALS,
new Credentials(config.getUserName(), secret.getBytes()));
// WebSocket connect
StompPubsubClientEndpoint endpoint = new StompPubsubClientEndpoint();
URI uri = new URI(wsURL);
// STOMP connect
endpoint.connect(uri.getHost());
// STOMP disconnect
endpoint.disconnect("ID-123");
// Wait for disconnect receipt
Thread.sleep(3000);
session.close();
}
}
Device List
This code returns the list of devices as called in the PublishController Code
package com.cisco.pxgrid.samples.ise.http;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonProperty("assets")
public Devices[] getDeviceList() {
return list_of_devices;
}
}
Devices
This code defines the asset and custom attributes
package com.cisco.pxgrid.samples.ise.http;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.json.simple.JSONObject;
public class Devices {
this.assetIpAddress = assetIpAddress;
this.assetMacAddress = assetMacAddress;
this.assetVendor = assetVendor;
this.assetProductId = assetProductId;
this.assetSerialNumber = assetSerialNumber;
this.assetDeviceType = assetDeviceType;
this.assetSwRevision = assetSwRevision;
this.assetHwRevision = assetHwRevision;
this.assetProtocol = assetProtocol;
/* this.CustomAttributes= new JSONObject();
this.CustomAttributes.put("test1","value1");
this.CustomAttributes.put("test2","value2");*/
}
/*@JsonProperty("CustomAttributes")
public JSONObject getCustomAttributes() {
return CustomAttributes;
}*/
@JsonProperty("assetId")
public String getId() {
return assetId;
}
@JsonProperty("assetIpAddress")
public String getIpAddress() {
return assetIpAddress;
}
@JsonProperty("assetMacAddress")
public String getMacAddress() {
return assetMacAddress;
}
@JsonProperty("assetName")
public String getName() {
return assetName;
}
@JsonProperty("assetVendor")
public String getVendor() {
return assetVendor;
}
@JsonProperty("assetSerialNumber")
public String getSerialNumber() {
return assetSerialNumber;
}
@JsonProperty("assetProductId")
public String getProductId() {
return assetProductId;
}
@JsonProperty("assetDeviceType")
public String getDeviceType() {
return assetDeviceType;
}
@JsonProperty("assetSwRevision")
public String getSwRevision() {
return assetSwRevision;
}
@JsonProperty("assetHwRevision")
public String getHwRevision() {
return assetHwRevision;
}
@JsonProperty("assetProtocol")
public String getProtocol() {
return assetProtocol;
}
}
Console.java
This code prints out the time zone
package com.cisco.pxgrid.samples.ise.http;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
package com.cisco.pxgrid.samples.ise.http;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.Scanner;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLSocketFactory;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
public static void postObjectAndPrint(String url, String user, String password, SSLSocketFactory ssl,
Object postObject) throws IOException {
Gson gson = new GsonBuilder()
.registerTypeAdapter(OffsetDateTime.class, new OffsetDateTimeAdapter())
.create();
postStringAndPrint(url, user, password, ssl, gson.toJson(postObject));
}
public static void postStringAndPrint(String url, String user, String password, SSLSocketFactory ssl,
String postData) throws IOException {
logger.info("postData={}", postData);
HttpsURLConnection httpsConn = SampleHelper.createHttpsURLConnection(url, user, password,
ssl);
httpsConn.setRequestMethod("POST");
httpsConn.setRequestProperty("Content-Type", "application/json");
httpsConn.setRequestProperty("Accept", "application/json");
httpsConn.setDoInput(true);
httpsConn.setDoOutput(true);
@Override
public OffsetDateTime read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
return formatter.parse(in.nextString(), OffsetDateTime::from);
}
}
}
Stompframe.java
This code handles the STOMP message frame
package com.cisco.pxgrid.samples.ise.http;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
/**
* This follows STOMP 1.2 specification to parse and generate STOMP frames:
* https://stomp.github.io/stomp-specification-1.2.html
*
* This single class is self-sufficient handle all STOMP frames.
*
* Note for WebSocket:
* If input comes as WebSocket text type, (WS RFC says Text is UTF-8)
* server side handling code like Spring TextMessage may convert the bytes to String as UTF-8
* which maybe the wrong encoding as STOMP frame itself can use other encoding.
* e.g. A particular encoding may have bytes: FF FF FF FF FF FF FF FF FF FF... 10, that is completely out
of range for Unicode.
* Unless STOMP body is also UTF-8, STOMP frame must be sent as binary
*
* @author Alan Lei
*/
public class StompFrame {
public enum Command {
CONNECT, STOMP, CONNECTED, SEND, SUBSCRIBE, UNSUBSCRIBE, ACK, NACK,
BEGIN, COMMIT, ABORT, DISCONNECT, MESSAGE, RECEIPT, ERROR;
/*
* Using InputStream instead of Reader because
* content-length is octet count instead of character count
*/
public static StompFrame parse(InputStream reader) throws IOException, ParseException {
StompFrame stomp = new StompFrame();
// Read Command
String line = readLine(reader);
Command command = Command.get(line);
if (command == null) throw new ParseException("Unknown command: " + line, 0);
stomp.setCommand(command);
// Read Headers
int contentLength = -1;
while ((line = readLine(reader)) != null) {
if (line.equals("")) break;
int colon = line.indexOf(':');
String name = line.substring(0, colon);
String value = line.substring(colon + 1);
stomp.setHeader(name, value);
if (name.equals("content-length")) {
contentLength = Integer.parseInt(value);
}
}
// Read Content
if (contentLength != -1) {
// content-length is in octets
byte[] content = new byte[contentLength];
reader.read(content);
stomp.setContent(content);
if (reader.read() != 0) {
throw new ParseException("Byte after STOMP Body not NULL", -1);
}
}
else {
// No content-length. Look for ending NULL byte.
byte[] buffer = new byte[MAX_BUFFER_SIZE];
int length = 0;
while (length < MAX_BUFFER_SIZE) {
int b = reader.read();
if (b == -1) {
throw new ParseException("Premature end of stream", -1);
}
if (b == 0) {
if (length > 0) {
byte[] content = new byte[length];
System.arraycopy(buffer, 0, content, 0, length);
stomp.setContent(content);
}
// More EOLs may follow, but ignored.
return stomp;
}
buffer[length] = (byte)b;
length++;
}
throw new ParseException("Frame too long", -1);
}
return stomp;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("command=" + command);
sb.append(", headers={");
for (String name : headers.keySet()) {
sb.append("'" + name + "':");
sb.append("'" + headers.get(name) + "',");
}
sb.append("}");
sb.append(", content.length=" + content.length);
return sb.toString();
}
}
StompSubscription
This code handles subscription to the STOMP Messaging protocol.
package com.cisco.pxgrid.samples.ise;
import java.util.concurrent.atomic.AtomicInteger;
StompPubSubClientEndpoint
This code handles endpoint client subscription to the STOMP messaging protocol.
package com.cisco.pxgrid.samples.ise.http;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.text.ParseException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.ClientEndpoint;
import javax.websocket.CloseReason;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.cisco.pxgrid.samples.ise.http.StompSubscription.Handler;
@ClientEndpoint
public class StompPubsubClientEndpoint extends Endpoint {
private static Logger logger = LoggerFactory.getLogger(StompPubsubClientEndpoint.class);
@Override
public void onOpen(Session session, EndpointConfig cfg) {
logger.info("WS onOpen");
this.session = session;
try {
session.addMessageHandler(new TextHandler());
session.addMessageHandler(new BinaryHandler());
@Override
public void onClose(Session session, CloseReason closeReason) {
logger.info("WS onClose closeReason code={} phrase={}", closeReason.getCloseCode(),
closeReason.getReasonPhrase());
this.session = null;
}
@Override
public void onError(Session session, Throwable thr) {
logger.info("WS onError thr={}", thr.getMessage());
this.session = null;
}
}
Sample Response:
POST [restBaseUrl]/getAssets
This is used to get all Endpoint devices.
Content-Type: application/json
Accept: application/json
Label Description
Request:
Response:
"assets": [
"Asset" Object
assetProtocol String
assetGroup String Fully qualified group name assigned to the Asset 1.3, 1.4
"Link" Object
"Pair" Object
Sample Response:
POST [restBaseUrl]/getSyncStatus
This is used to get the sync status of the IND Pxgrid Service.
Content-Type: application/json
Accept: application/json
Label Description
Request:
Response:
IN_SYNC