Commit 6a68a039 authored by ORD's avatar ORD

Merge pull request #153 from FreeOpcUa/history

History fixes and event support WORK IN PROGRESS
parents eb491cfa cb660789
...@@ -74,11 +74,12 @@ Server: what works: ...@@ -74,11 +74,12 @@ Server: what works:
* encryption * encryption
* certificate handling * certificate handling
* removing nodes * removing nodes
* history support
Tested clients: freeopcua C++, freeopcua Python, uaexpert, prosys, quickopc Tested clients: freeopcua C++, freeopcua Python, uaexpert, prosys, quickopc
Server: what is not implemented Server: what is not implemented
* history support * history support for events
* views * views
* localized text feature * localized text feature
* better securty model with users and password * better securty model with users and password
......
...@@ -318,7 +318,11 @@ class Client(object): ...@@ -318,7 +318,11 @@ class Client(object):
params.RequestedSessionTimeout = 3600000 params.RequestedSessionTimeout = 3600000
params.MaxResponseMessageSize = 0 # means no max size params.MaxResponseMessageSize = 0 # means no max size
response = self.uaclient.create_session(params) response = self.uaclient.create_session(params)
self.security_policy.asymmetric_cryptography.verify(self.security_policy.client_certificate + nonce, response.ServerSignature.Signature) if self.security_policy.client_certificate is None:
data = nonce
else:
data = self.security_policy.client_certificate + nonce
self.security_policy.asymmetric_cryptography.verify(data, response.ServerSignature.Signature)
self._server_nonce = response.ServerNonce self._server_nonce = response.ServerNonce
if not self.security_policy.server_certificate: if not self.security_policy.server_certificate:
self.security_policy.server_certificate = response.ServerCertificate self.security_policy.server_certificate = response.ServerCertificate
...@@ -361,7 +365,8 @@ class Client(object): ...@@ -361,7 +365,8 @@ class Client(object):
Activate session using either username and password or private_key Activate session using either username and password or private_key
""" """
params = ua.ActivateSessionParameters() params = ua.ActivateSessionParameters()
challenge = self.security_policy.server_certificate + self._server_nonce cert = self.security_policy.server_certificate if self.security_policy.server_certificate is not None else b""
challenge = cert + self._server_nonce
params.ClientSignature.Algorithm = b"http://www.w3.org/2000/09/xmldsig#rsa-sha1" params.ClientSignature.Algorithm = b"http://www.w3.org/2000/09/xmldsig#rsa-sha1"
params.ClientSignature.Signature = self.security_policy.asymmetric_cryptography.signature(challenge) params.ClientSignature.Signature = self.security_policy.asymmetric_cryptography.signature(challenge)
params.LocaleIds.append("en") params.LocaleIds.append("en")
......
...@@ -3,6 +3,7 @@ from datetime import datetime ...@@ -3,6 +3,7 @@ from datetime import datetime
from opcua import Subscription from opcua import Subscription
from opcua import ua from opcua import ua
from opcua.common import utils
class HistoryStorageInterface(object): class HistoryStorageInterface(object):
...@@ -207,7 +208,7 @@ class HistoryManager(object): ...@@ -207,7 +208,7 @@ class HistoryManager(object):
# implementation. This is contradictory, so we assume details is # implementation. This is contradictory, so we assume details is
# send correctly with continuation point # send correctly with continuation point
#starttime = bytes_to_datetime(rv.ContinuationPoint) #starttime = bytes_to_datetime(rv.ContinuationPoint)
starttime = ua.unpack_datetime(rv.ContinuationPoint) starttime = ua.unpack_datetime(utils.Buffer(rv.ContinuationPoint))
dv, cont = self.storage.read_node_history(rv.NodeId, dv, cont = self.storage.read_node_history(rv.NodeId,
starttime, starttime,
......
...@@ -122,7 +122,11 @@ class UaProcessor(object): ...@@ -122,7 +122,11 @@ class UaProcessor(object):
response = ua.CreateSessionResponse() response = ua.CreateSessionResponse()
response.Parameters = sessiondata response.Parameters = sessiondata
response.Parameters.ServerCertificate = self._connection._security_policy.client_certificate response.Parameters.ServerCertificate = self._connection._security_policy.client_certificate
response.Parameters.ServerSignature.Signature = self._connection._security_policy.asymmetric_cryptography.signature(self._connection._security_policy.server_certificate + params.ClientNonce) if self._connection._security_policy.server_certificate is None:
data = params.ClientNonce
else:
data = self._connection._security_policy.server_certificate + params.ClientNonce
response.Parameters.ServerSignature.Signature = self._connection._security_policy.asymmetric_cryptography.signature(data)
response.Parameters.ServerSignature.Algorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1" response.Parameters.ServerSignature.Algorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"
self.logger.info("sending create sesssion response") self.logger.info("sending create sesssion response")
...@@ -146,7 +150,11 @@ class UaProcessor(object): ...@@ -146,7 +150,11 @@ class UaProcessor(object):
self.logger.info("request to activate non-existing session") self.logger.info("request to activate non-existing session")
raise utils.ServiceError(ua.StatusCodes.BadSessionIdInvalid) raise utils.ServiceError(ua.StatusCodes.BadSessionIdInvalid)
self._connection._security_policy.asymmetric_cryptography.verify(self._connection._security_policy.client_certificate + self.session.nonce, params.ClientSignature.Signature) if self._connection._security_policy.client_certificate is None:
data = self.session.nonce
else:
data = self._connection._security_policy.client_certificate + self.session.nonce
self._connection._security_policy.asymmetric_cryptography.verify(data, params.ClientSignature.Signature)
result = self.session.activate_session(params) result = self.session.activate_session(params)
......
...@@ -84,7 +84,11 @@ def parse_args(parser, requirenodeid=False): ...@@ -84,7 +84,11 @@ def parse_args(parser, requirenodeid=False):
def get_node(client, args): def get_node(client, args):
node = client.get_node(args.nodeid) node = client.get_node(args.nodeid)
if args.path: if args.path:
node = node.get_child(args.path.split(",")) path = args.path.split(",")
if node.nodeid == ua.NodeId(84, 0) and path[0] == "0:Root":
# let user specify root if not node given
path = path[1:]
node = node.get_child(path)
return node return node
......
...@@ -163,8 +163,8 @@ class AsymmetricAlgorithmHeader(uatypes.FrozenClass): ...@@ -163,8 +163,8 @@ class AsymmetricAlgorithmHeader(uatypes.FrozenClass):
def __init__(self): def __init__(self):
self.SecurityPolicyURI = "http://opcfoundation.org/UA/SecurityPolicy#None" self.SecurityPolicyURI = "http://opcfoundation.org/UA/SecurityPolicy#None"
self.SenderCertificate = b"" self.SenderCertificate = None
self.ReceiverCertificateThumbPrint = b"" self.ReceiverCertificateThumbPrint = None
self._freeze = True self._freeze = True
def to_binary(self): def to_binary(self):
...@@ -308,8 +308,8 @@ class SecurityPolicy(object): ...@@ -308,8 +308,8 @@ class SecurityPolicy(object):
self.asymmetric_cryptography = CryptographyNone() self.asymmetric_cryptography = CryptographyNone()
self.symmetric_cryptography = CryptographyNone() self.symmetric_cryptography = CryptographyNone()
self.Mode = auto.MessageSecurityMode.None_ self.Mode = auto.MessageSecurityMode.None_
self.server_certificate = b"" self.server_certificate = None
self.client_certificate = b"" self.client_certificate = None
def make_symmetric_key(self, a, b): def make_symmetric_key(self, a, b):
pass pass
......
...@@ -218,8 +218,6 @@ def pack_string(string): ...@@ -218,8 +218,6 @@ def pack_string(string):
if isinstance(string, unicode): if isinstance(string, unicode):
string = string.encode('utf-8') string = string.encode('utf-8')
length = len(string) length = len(string)
if length == 0:
return b'\xff\xff\xff\xff'
return uatype_Int32.pack(length) + string return uatype_Int32.pack(length) + string
pack_bytes = pack_string pack_bytes = pack_string
...@@ -228,13 +226,14 @@ pack_bytes = pack_string ...@@ -228,13 +226,14 @@ pack_bytes = pack_string
def unpack_bytes(data): def unpack_bytes(data):
length = uatype_Int32.unpack(data.read(4))[0] length = uatype_Int32.unpack(data.read(4))[0]
if length == -1: if length == -1:
# FIXME: return None, check it does not break things return None
return b''
return data.read(length) return data.read(length)
def py3_unpack_string(data): def py3_unpack_string(data):
b = unpack_bytes(data) b = unpack_bytes(data)
if b is None:
return b
return b.decode("utf-8") return b.decode("utf-8")
......
...@@ -16,7 +16,7 @@ except ImportError: ...@@ -16,7 +16,7 @@ except ImportError:
from tests_cmd_lines import TestCmdLines from tests_cmd_lines import TestCmdLines
from tests_server import TestServer from tests_server import TestServer
from tests_client import TestClient from tests_client import TestClient
from tests_unit import Unit from tests_unit import TestUnit
if CRYPTOGRAPHY_AVAILABLE: if CRYPTOGRAPHY_AVAILABLE:
from tests_crypto_connect import TestCryptoConnect from tests_crypto_connect import TestCryptoConnect
......
...@@ -12,7 +12,7 @@ from opcua.ua.uatypes import flatten, get_shape, reshape ...@@ -12,7 +12,7 @@ from opcua.ua.uatypes import flatten, get_shape, reshape
class Unit(unittest.TestCase): class TestUnit(unittest.TestCase):
''' '''
Simple unit test that do not need to setup a server or a client Simple unit test that do not need to setup a server or a client
...@@ -140,13 +140,22 @@ class Unit(unittest.TestCase): ...@@ -140,13 +140,22 @@ class Unit(unittest.TestCase):
with self.assertRaises(ua.UaError): with self.assertRaises(ua.UaError):
ua.QualifiedName.from_string("i:::yu") ua.QualifiedName.from_string("i:::yu")
def test_expandednodeid(self): def test_expandednodeid(self):
nid = ua.ExpandedNodeId() nid = ua.ExpandedNodeId()
self.assertEqual(nid.NodeIdType, ua.NodeIdType.TwoByte) self.assertEqual(nid.NodeIdType, ua.NodeIdType.TwoByte)
nid2 = ua.ExpandedNodeId.from_binary(ua.utils.Buffer(nid.to_binary())) nid2 = ua.ExpandedNodeId.from_binary(ua.utils.Buffer(nid.to_binary()))
self.assertEqual(nid, nid2) self.assertEqual(nid, nid2)
def test_null_string(self):
v = ua.Variant(None, ua.VariantType.String)
b = v.to_binary()
v2 = ua.Variant.from_binary(ua.utils.Buffer(b))
self.assertEqual(v.Value, v2.Value)
v = ua.Variant("", ua.VariantType.String)
b = v.to_binary()
v2 = ua.Variant.from_binary(ua.utils.Buffer(b))
self.assertEqual(v.Value, v2.Value)
def test_extension_object(self): def test_extension_object(self):
obj = ua.UserNameIdentityToken() obj = ua.UserNameIdentityToken()
obj.UserName = "admin" obj.UserName = "admin"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment