use of org.eclipse.milo.opcua.sdk.client.api.identity.IdentityProvider in project vantiq-extension-sources by Vantiq.
the class OpcUaESClient method createClient.
@SuppressWarnings({ "PMD.CognitiveComplexity", "PMD.MethodLengthCheck" })
private OpcUaClient createClient(Map<String, Object> config) throws Exception {
if (storageDirectory == null) {
throw new OpcExtConfigException(ERROR_PREFIX + ".missingStorageDirectory: No storage directory specified.");
}
SecurityPolicy securityPolicy = determineSecurityPolicy(config);
MessageSecurityMode msgSecMode = determineMessageSecurityMode(config);
File securityDir = new File(storageDirectory, SECURITY_DIRECTORY);
if (!securityDir.exists() && !securityDir.mkdirs()) {
throw new OpcExtConfigException(ERROR_PREFIX + ".invalidStorageDirectory: unable to create security dir: " + securityDir);
}
log.info("security temp dir: {}", securityDir.getAbsolutePath());
keyStoreManager = new KeyStoreManager().load(securityDir);
IdentityProvider idProvider = constructIdentityProvider(config);
List<EndpointDescription> endpoints;
discoveryEndpoint = (String) config.get(OpcConstants.CONFIG_DISCOVERY_ENDPOINT);
serverEndpoint = (String) config.get(OpcConstants.CONFIG_SERVER_ENDPOINT);
if (discoveryEndpoint == null && serverEndpoint == null) {
String errorMsg = ERROR_PREFIX + ".noDiscoveryEndpoint: No discovery or server endpoint was provided in the configuration.";
log.error(errorMsg);
throw new OpcExtConfigException(errorMsg);
}
OpcUaClientConfig opcConfig;
try {
endpoints = DiscoveryClient.getEndpoints(discoveryEndpoint).get();
} catch (Throwable ex) {
try {
// try the explicit discovery endpoint as well
String discoveryUrl = discoveryEndpoint + "/discovery";
log.info("Trying explicit discovery URL: {}", discoveryUrl);
endpoints = DiscoveryClient.getEndpoints(discoveryUrl).get();
} catch (ExecutionException e) {
String errMsg = ERROR_PREFIX + ".discoveryError: Could not discover OPC Endpoints:" + e.getClass().getName() + "::" + e.getMessage();
log.error(ERROR_PREFIX + ".discoveryError: Could not discover OPC Endpoints: {} :: {}", e.getClass().getName(), e.getMessage());
throw new OpcExtConfigException(errMsg, e);
}
}
if (log.isDebugEnabled()) {
logDiscoveredEndpoints(endpoints);
}
List<EndpointDescription> validEndpoints = endpoints.stream().filter(e -> (e.getSecurityPolicyUri().equals(securityPolicy.getUri()) && e.getSecurityMode().equals(msgSecMode))).collect(Collectors.toList());
if (log.isDebugEnabled()) {
logAcceptableEndpoints(validEndpoints, securityPolicy, msgSecMode);
// The following code is here for testing only. It allows us to fake a poorly configured
// server that reports invalid or unreachable endpoints as part of discovery. This is, for
// reasons I'm sure i don't agree with, part of the protocol, so we must tolerate it. The
// purportedly proper response is to substitute the address used for discovery for any
// unreachable addresses. This, of course, makes little sense since the whole point of discovery
// is to allow these to be spread across different nodes. But I didn't write the spec.
Boolean fakeBadAddress = (Boolean) config.get(OpcConstants.CONFIG_TEST_DISCOVERY_UNREACHABLE);
if (fakeBadAddress != null && fakeBadAddress) {
List<EndpointDescription> newValidEndpoints = new ArrayList<>();
for (EndpointDescription e : validEndpoints) {
URI url = new URI(e.getEndpointUrl());
URI borkedUri = new URI(url.getScheme(), null, "utterlyWorthlessHostThatShouldNeverResolve", url.getPort(), url.getPath(), null, null);
EndpointDescription borkedEd = new EndpointDescription(borkedUri.toString(), e.getServer(), e.getServerCertificate(), e.getSecurityMode(), e.getSecurityPolicyUri(), e.getUserIdentityTokens(), e.getTransportProfileUri(), e.getSecurityLevel());
newValidEndpoints.add(borkedEd);
}
validEndpoints = newValidEndpoints;
}
}
// First, we'll look for an endpoint that doesn't contain localhost. This is, generally,
// a not too useful configuration since localhost is always a relative address.
EndpointDescription endpoint = validEndpoints.stream().filter(e -> {
try {
// Note: Must use URI here. If you use URL, it will fail with
// a MailformedURLException because the generic system doesn't
// understand opc.tcp: as a scheme/protocol.
URI url = new URI(e.getEndpointUrl());
InetAddress ina = InetAddress.getByName(url.getHost());
if (!ina.isLoopbackAddress() || ina.isReachable(3000)) {
return true;
}
} catch (UnknownHostException | URISyntaxException ex) {
log.warn("Recoverable error during discovered server URL validation:" + ex.getClass().getName() + "::" + ex.getMessage() + "-->" + e.getEndpointUrl());
} catch (Exception ex) {
// This means that we have some non-optimal addresses returned by discovery.
// In these cases, we'll leave it up to the SDK & network stack to figure out how to get there.
log.debug("Recoverable error during discovered server URL validation. Left to network stack to resolve:" + ex.getClass().getName() + "::" + ex.getMessage() + "-->" + e.getEndpointUrl());
}
return false;
}).findFirst().orElse(null);
if (endpoint == null) {
// Discovery server returned either no reasonable endpoints or none that weren't a loopback.
log.warn("No servers at reachable, non-loopback addresses found via discovery. " + "Fixing up addresses to match discovery server.");
endpoint = validEndpoints.stream().findFirst().orElse(null);
if (endpoint != null) {
endpoint = fixLookbackAddress(endpoint);
}
}
if (endpoint == null) {
throw new Exception("No acceptable endpoints returned for security policy: " + securityPolicy.getUri() + " and security mode " + msgSecMode);
}
if (serverEndpoint != null) {
// Then we'll override the endpoint provided but otherwise use the endpoint descriptor returned.
// The SDK seems to have an issue when no EndpointDescriptor is provided.
EndpointDescription newEndpoint = new EndpointDescription(serverEndpoint, endpoint.getServer(), endpoint.getServerCertificate(), endpoint.getSecurityMode(), endpoint.getSecurityPolicyUri(), endpoint.getUserIdentityTokens(), endpoint.getTransportProfileUri(), endpoint.getSecurityLevel());
log.debug("Replacing endpoint address with provided serverEndpoint: {} --> {}", endpoint.getEndpointUrl(), newEndpoint.getEndpointUrl());
endpoint = newEndpoint;
}
log.info("Using discovered endpoint: {} [{}, {}]", endpoint.getEndpointUrl(), securityPolicy, msgSecMode.toString());
opcConfig = OpcUaClientConfig.builder().setApplicationName(LocalizedText.english("VANTIQ OPC-UA Source")).setApplicationUri("urn:io:vantiq:extsrc:opcua:client").setCertificate(keyStoreManager.getClientCertificate()).setKeyPair(keyStoreManager.getClientKeyPair()).setEndpoint(endpoint).setIdentityProvider(idProvider).setRequestTimeout(uint(5000)).build();
return OpcUaClient.create(opcConfig);
}
use of org.eclipse.milo.opcua.sdk.client.api.identity.IdentityProvider in project vantiq-extension-sources by Vantiq.
the class OpcUaESClient method constructIdentityProvider.
private IdentityProvider constructIdentityProvider(Map<String, Object> config) throws OpcExtConfigException, OpcExtKeyStoreException {
IdentityProvider retVal = null;
String anonymous = (String) config.get(OpcConstants.CONFIG_IDENTITY_ANONYMOUS);
// This can be empty -- presence is sufficient
boolean anonIsPresent = anonymous != null;
String certAlias = (String) config.get(OpcConstants.CONFIG_IDENTITY_CERTIFICATE);
boolean certIsPresent = foundValue(certAlias);
String userPass = (String) config.get(OpcConstants.CONFIG_IDENTITY_USERNAME_PASSWORD);
boolean upwIsPresent = foundValue(userPass);
boolean exactlyOnePresent = (anonIsPresent ^ certIsPresent ^ upwIsPresent) ^ (anonIsPresent && certIsPresent && upwIsPresent);
if (!anonIsPresent && !certIsPresent && !upwIsPresent) {
log.warn(ERROR_PREFIX + ".noIdentitySpecification: No identity specification was provided. Using Anonymous as default.");
retVal = new AnonymousProvider();
} else if (exactlyOnePresent) {
// Now we know there is exactly one of them set.
if (anonIsPresent) {
retVal = new AnonymousProvider();
} else if (certIsPresent) {
X509Certificate namedCert = keyStoreManager.fetchCertByAlias(certAlias);
PrivateKey pKey = keyStoreManager.fetchPrivateKeyByAlias(certAlias);
retVal = new X509IdentityProvider(namedCert, pKey);
} else if (upwIsPresent) {
String[] upw = userPass.split(",[ ]*");
if (upw.length != 2) {
String errMsg = MessageFormatter.arrayFormat(ERROR_PREFIX + ".invalidUserPasswordSpecification: the {} ({}) must contain only a username AND password separated by a comma.", new Object[] { OpcConstants.CONFIG_IDENTITY_USERNAME_PASSWORD, userPass }).getMessage();
log.error(errMsg);
throw new OpcExtConfigException(errMsg);
} else {
retVal = new UsernameProvider(upw[0], upw[1]);
}
}
} else {
String errMsg = MessageFormatter.arrayFormat(ERROR_PREFIX + ".invalidIdentitySpecification: exactly one identity specification ({}, {}, {}) is required.", new Object[] { OpcConstants.CONFIG_IDENTITY_ANONYMOUS, OpcConstants.CONFIG_IDENTITY_CERTIFICATE, OpcConstants.CONFIG_IDENTITY_USERNAME_PASSWORD }).getMessage();
log.error(errMsg);
throw new OpcExtConfigException(errMsg);
}
return retVal;
}
Aggregations