Commit 35288e53 authored by Olivier R-D's avatar Olivier R-D

add minimal user support, default address space to read-only

parent 549ec004
......@@ -48,10 +48,15 @@ if __name__ == "__main__":
# optional setup logging
logging.basicConfig(level=logging.WARN)
#logger = logging.getLogger("opcua.address_space")
#logger.setLevel(logging.DEBUG)
#logger = logging.getLogger("opcua.internal_server")
# logger.setLevel(logging.DEBUG)
#logger = logging.getLogger("opcua.subscription_server")
# logger.setLevel(logging.DEBUG)
#logger.setLevel(logging.DEBUG)
#logger = logging.getLogger("opcua.binary_server_asyncio")
#logger.setLevel(logging.DEBUG)
#logger = logging.getLogger("opcua.uaprocessor")
#logger.setLevel(logging.DEBUG)
#logger = logging.getLogger("opcua.subscription_service")
#logger.setLevel(logging.DEBUG)
# now setup our server
server = Server()
......
......@@ -4,6 +4,7 @@ import pickle
from datetime import datetime
from opcua import ua
from opcua.users import User
class AttributeValue(object):
......@@ -44,10 +45,18 @@ class AttributeService(object):
res.append(self._aspace.get_attribute_value(readvalue.NodeId, readvalue.AttributeId))
return res
def write(self, params):
self.logger.debug("write %s", params)
def write(self, params, user=User.Admin):
self.logger.debug("write %s as user %s", params, user)
res = []
for writevalue in params.NodesToWrite:
if user != User.Admin:
# FIXME: check WriteMask and UserWriteMask!!!
al = self._aspace.get_attribute_value(writevalue.NodeId, ua.AttributeIds.WriteMask)
ual = self._aspace.get_attribute_value(writevalue.NodeId, ua.AttributeIds.UserWriteMask)
# FIXME: very strange to use OpenFileMode for access rights?!?!?
if not al.Value.Value & ua.OpenFileMode.Write and not ual.Value.Value & ua.OpenFileMode.Write:
res.append(ua.StatusCode(ua.StatusCodes.BadUserAccessDenied))
continue
res.append(self._aspace.set_attribute_value(writevalue.NodeId, writevalue.AttributeId, writevalue.Value))
return res
......@@ -158,13 +167,13 @@ class NodeManagementService(object):
self.logger = logging.getLogger(__name__)
self._aspace = aspace
def add_nodes(self, addnodeitems):
def add_nodes(self, addnodeitems, user=User.Admin):
results = []
for item in addnodeitems:
results.append(self._add_node(item))
results.append(self._add_node(item, user))
return results
def _add_node(self, item):
def _add_node(self, item, user):
result = ua.AddNodesResult()
if item.RequestedNewNodeId in self._aspace:
......@@ -188,6 +197,15 @@ class NodeManagementService(object):
result.StatusCode = ua.StatusCode(ua.StatusCodes.BadParentNodeIdInvalid)
return result
else:
if user != User.Admin:
# FIXME: is checking against WriteMask correct??
al = self._aspace.get_attribute_value(item.ParentNodeId, ua.AttributeIds.WriteMask)
ual = self._aspace.get_attribute_value(item.ParentNodeId, ua.AttributeIds.UserWriteMask)
# FIXME: very strange to use OpenFileMode for access rights?!?!?
if not al.Value.Value & ua.OpenFileMode.Write and not ual.Value.Value & ua.OpenFileMode.Write:
result.StatusCode = ua.StatusCode(ua.StatusCodes.BadUserAccessDenied)
return result
desc = ua.ReferenceDescription()
desc.ReferenceTypeId = item.ReferenceTypeId
desc.NodeId = item.RequestedNewNodeId
......
......@@ -169,7 +169,7 @@ class Client(object):
params.EndpointUrl = self.server_url.geturl()
params.SessionName = self.description + " Session" + str(self._session_counter)
params.RequestedSessionTimeout = 3600000
params.MaxResponseMessageSize = 0 # means not max size
params.MaxResponseMessageSize = 0 # means no max size
response = self.bclient.create_session(params)
self.session_timeout = response.RevisedSessionTimeout
self.keepalive = KeepAlive(self, min(self.session_timeout, self.secure_channel_timeout) * 0.7) # 0.7 is from spec
......@@ -179,8 +179,16 @@ class Client(object):
def activate_session(self):
params = ua.ActivateSessionParameters()
params.LocaleIds.append("en")
params.UserIdentityToken = ua.AnonymousIdentityToken()
params.UserIdentityToken.PolicyId = b"anonymous"
if not self.server_url.username:
params.UserIdentityToken = ua.AnonymousIdentityToken()
params.UserIdentityToken.PolicyId = b"anonymous"
else:
params.UserIdentityToken = ua.UserNameIdentityToken()
params.UserIdentityToken.UserName = self.server_url.username
if self.server_url.password:
params.UserIdentityToken.Password = bytes(self.server_url.password)
params.UserIdentityToken.PolicyId = b"user_name"
#params.EncryptionAlgorithm = ''
return self.bclient.activate_session(params)
def close_session(self):
......
......@@ -23,6 +23,7 @@ from opcua.address_space import NodeManagementService
from opcua.address_space import MethodService
from opcua.subscription_service import SubscriptionService
from opcua import standard_address_space
from opcua.users import User
class SessionState(Enum):
Created = 0
......@@ -46,11 +47,10 @@ class InternalServer(object):
#standard_address_space.fill_address_space_from_disk(self.aspace)
self.loop = utils.ThreadLoop()
self.subcsription_service = SubscriptionService(self.loop, self.aspace)
self.subscription_service = SubscriptionService(self.loop, self.aspace)
# create a session to use on server side
self.isession = InternalSession(self, self.aspace, self.subcsription_service, "Internal")
self._timer = None
self.isession = InternalSession(self, self.aspace, self.subscription_service, "Internal", user=User.Admin)
self.current_time_node = Node(self.isession, ua.NodeId(ua.ObjectIds.Server_ServerStatus_CurrentTime))
uries = ["http://opcfoundation.org/UA/"]
ns_node = Node(self.isession, ua.NodeId(ua.ObjectIds.Server_NamespaceArray))
......@@ -106,20 +106,21 @@ class InternalServer(object):
return servers
def create_session(self, name):
return InternalSession(self, self.aspace, self.subcsription_service, name)
def create_session(self, name, user=User.Anonymous):
return InternalSession(self, self.aspace, self.subscription_service, name, user=user)
class InternalSession(object):
_counter = 10
_auth_counter = 1000
def __init__(self, internal_server, aspace, submgr, name):
def __init__(self, internal_server, aspace, submgr, name, user=User.Anonymous):
self.logger = logging.getLogger(__name__)
self.iserver = internal_server
self.aspace = aspace
self.subscription_service = submgr
self.name = name
self.user = user
self.state = SessionState.Created
self.session_id = ua.NodeId(self._counter)
InternalSession._counter += 1
......@@ -127,11 +128,12 @@ class InternalSession(object):
InternalSession._auth_counter += 1
self.nonce = utils.create_nonce()
self.subscriptions = []
self.logger.warning("Created internal session %s", self.name)
#self.logger.debug("Created internal session %s for user %s", self.name, self.user)
print("Created internal session {} for user {}".format(self.name, self.user))
self._lock = Lock()
def __str__(self):
return "InternalSession(name:{}, id:{}, auth_token:{})".format(self.name, self.session_id, self.authentication_token)
return "InternalSession(name:{}, user:{}, id:{}, auth_token:{})".format(self.name, self.user, self.session_id, self.authentication_token)
def get_endpoints(self, params=None, sockname=None):
return self.iserver.get_endpoints(params, sockname)
......@@ -164,13 +166,17 @@ class InternalSession(object):
for _ in params.ClientSoftwareCertificates:
result.Results.append(ua.StatusCode())
self.state = SessionState.Activated
id_token = ua.downcast_extobject(params.UserIdentityToken)
if id_token.TypeId == ua.FourByteNodeId(ua.ObjectIds.UserNameIdentityToken_Encoding_DefaultBinary):
if id_token.UserName in ("admin", "Admin"):
self.user = User.Admin
return result
def read(self, params):
return self.iserver.attribute_service.read(params)
def write(self, params):
return self.iserver.attribute_service.write(params)
return self.iserver.attribute_service.write(params, self.user)
def browse(self, params):
return self.iserver.view_service.browse(params)
......@@ -179,7 +185,7 @@ class InternalSession(object):
return self.iserver.view_service.translate_browsepaths_to_nodeids(params)
def add_nodes(self, params):
return self.iserver.node_mgt_service.add_nodes(params)
return self.iserver.node_mgt_service.add_nodes(params, self.user)
def add_method_callback(self, methodid, callback):
return self.aspace.add_method_callback(methodid, callback)
......
......@@ -195,6 +195,14 @@ class Node(object):
set_data_value = set_value
def set_writable(self, writable=True):
if writable:
self.set_attribute(ua.AttributeIds.WriteMask, ua.DataValue(ua.OpenFileMode.Write))
self.set_attribute(ua.AttributeIds.UserWriteMask, ua.DataValue(ua.OpenFileMode.Write))
else:
self.set_attribute(ua.AttributeIds.WriteMask, ua.DataValue(ua.OpenFileMode.Read))
self.set_attribute(ua.AttributeIds.UserWriteMask, ua.DataValue(ua.OpenFileMode.Read))
def set_attribute(self, attributeid, datavalue):
"""
Set an attribute of a node
......@@ -331,6 +339,8 @@ def _create_folder(server, parentnodeid, nodeid, qname):
attrs = ua.ObjectAttributes()
attrs.Description = ua.LocalizedText(qname.Name)
attrs.DisplayName = ua.LocalizedText(qname.Name)
attrs.WriteMask = ua.OpenFileMode.Read
attrs.UserWriteMask = ua.OpenFileMode.Read
attrs.EventNotifier = 0
node.NodeAttributes = attrs
results = server.add_nodes([node])
......@@ -350,6 +360,8 @@ def _create_object(server, parentnodeid, nodeid, qname):
attrs.Description = ua.LocalizedText(qname.Name)
attrs.DisplayName = ua.LocalizedText(qname.Name)
attrs.EventNotifier = 0
attrs.WriteMask = ua.OpenFileMode.Read
attrs.UserWriteMask = ua.OpenFileMode.Read
node.NodeAttributes = attrs
results = server.add_nodes([node])
results[0].StatusCode.check()
......@@ -381,8 +393,8 @@ def _create_variable(server, parentnodeid, nodeid, qname, val, isproperty=False)
attrs.DataType = _guess_uatype(val)
attrs.Value = val
attrs.ValueRank = 0
attrs.WriteMask = 0
attrs.UserWriteMask = 0
attrs.WriteMask = ua.OpenFileMode.Read
attrs.UserWriteMask = ua.OpenFileMode.Read
attrs.Historizing = 0
node.NodeAttributes = attrs
results = server.add_nodes([node])
......@@ -401,8 +413,8 @@ def _create_method(parent, nodeid, qname, callback, inputs, outputs):
attrs = ua.MethodAttributes()
attrs.Description = ua.LocalizedText(qname.Name)
attrs.DisplayName = ua.LocalizedText(qname.Name)
attrs.WriteMask = 0
attrs.UserWriteMask = 0
attrs.WriteMask = ua.OpenFileMode.Read
attrs.UserWriteMask = ua.OpenFileMode.Read
attrs.Executable = True
attrs.UserExecutable = True
node.NodeAttributes = attrs
......
......@@ -629,7 +629,7 @@ def add_server_methods(srv):
o = srv.get_objects_node()
v = o.add_method(ua.NodeId("ServerMethodArray2", 2), ua.QualifiedName('ServerMethodArray2', 2), func3, [ua.VariantType.Int64], [ua.VariantType.Int64])
class TestClient(unittest.TestCase, CommonTests):
class AdminTestClient(unittest.TestCase, CommonTests):
'''
Run common tests on client side
......@@ -645,15 +645,23 @@ class TestClient(unittest.TestCase, CommonTests):
add_server_methods(self.srv)
self.srv.start()
# start client
self.clt = Client('opc.tcp://localhost:%d' % port_num1)
# start admin client
self.clt = Client('opc.tcp://admin@localhost:%d' % port_num1)
self.clt.connect()
self.opc = self.clt
# start anonymous client
self.ro_clt = Client('opc.tcp://localhost:%d' % port_num1)
self.ro_clt.connect()
@classmethod
def tearDownClass(self):
#stop our clients
self.ro_clt.disconnect()
self.clt.disconnect()
# stop the server in its own process
# stop the server
self.srv.stop()
def test_service_fault(self):
......@@ -662,6 +670,30 @@ class TestClient(unittest.TestCase, CommonTests):
with self.assertRaises(Exception):
self.clt.bclient._send_request(request)
def test_ro_objects(self):
objects = self.ro_clt.get_objects_node()
with self.assertRaises(Exception):
objects.set_attribute(ua.AttributeIds.WriteMask, ua.DataValue(999))
with self.assertRaises(Exception):
f = objects.add_folder(3, 'MyFolder')
def test_ro_variable(self):
objects = self.clt.get_objects_node()
v = objects.add_variable(3, 'MyROVariable', 6)
v.set_value(4) #this should work
v_ro = self.ro_clt.get_node(v.nodeid)
with self.assertRaises(Exception):
v_ro.set_value(2)
self.assertEqual(v_ro.get_value(), 4)
v.set_writable(True)
v_ro.set_value(2) #now it should work
self.assertEqual(v_ro.get_value(), 2)
v.set_writable(False)
with self.assertRaises(Exception):
v_ro.set_value(9)
self.assertEqual(v_ro.get_value(), 2)
class TestServer(unittest.TestCase, CommonTests):
......
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