open62541 / open62541-nodeset-loader

Library for loading opc ua nodesets from xml and sorting the nodes
Mozilla Public License 2.0
27 stars 23 forks source link

Changing UserAccessLevel didn't work. #237

Closed xydan83 closed 1 year ago

xydan83 commented 1 year ago

Hi. The UA CPP Server has SecurityAccess nodes in ComplianceTest. image

And I generate some export from this code:

<?xml version="1.0" encoding="UTF-8"?>
<UANodeSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:uax="http://opcfoundation.org/UA/2008/02/Types.xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd">
    <NamespaceUris>
        <Uri>urn:rozhkov-nb:UnifiedAutomation:UaServerCpp</Uri>
        <Uri>http://www.unifiedautomation.com/DemoServer/</Uri>
        <Uri>urn:UnifiedAutomation:CppDemoServer:BuildingAutomation</Uri>
        <Uri>http://www.unifiedautomation.com/DemoServer/AccessPermission</Uri>
        <Uri>http://www.unifiedautomation.com/DemoServer/StateMachines</Uri>
        <Uri>urn:UnifiedAutomation:CppDemoServer:UANodeSetXmlImport</Uri>
        <Uri>urn:UnifiedAutomation:PubSubConfiguration</Uri>
    </NamespaceUris>
    <Aliases>
        <Alias Alias="HasComponent">i=47</Alias>
        <Alias Alias="HasTypeDefinition">i=40</Alias>
        <Alias Alias="Int16">i=4</Alias>
        <Alias Alias="Organizes">i=35</Alias>
    </Aliases>
    <UAObject NodeId="ns=2;s=Demo.CTT.SecurityAccess" BrowseName="2:SecurityAccess" ParentNodeId="i=85">
        <DisplayName>SecurityAccess</DisplayName>
        <References>
            <Reference ReferenceType="HasTypeDefinition">i=61</Reference>
            <Reference ReferenceType="HasComponent">ns=2;s=Demo.CTT.SecurityAccess.AccessLevel_CurrentRead</Reference>
            <Reference ReferenceType="HasComponent">ns=2;s=Demo.CTT.SecurityAccess.AccessLevel_CurrentRead_NotCurrentWrite</Reference>
            <Reference ReferenceType="HasComponent">ns=2;s=Demo.CTT.SecurityAccess.AccessLevel_CurrentRead_NotUser</Reference>
            <Reference ReferenceType="HasComponent">ns=2;s=Demo.CTT.SecurityAccess.AccessLevel_CurrentWrite</Reference>
            <Reference ReferenceType="HasComponent">ns=2;s=Demo.CTT.SecurityAccess.AccessLevel_CurrentWrite_NotCurrentRead</Reference>
            <Reference ReferenceType="HasComponent">ns=2;s=Demo.CTT.SecurityAccess.AccessLevel_CurrentWrite_NotUser</Reference>
            <Reference ReferenceType="Organizes" IsForward="false">i=85</Reference>
        </References>
    </UAObject>
    <UAVariable NodeId="ns=2;s=Demo.CTT.SecurityAccess.AccessLevel_CurrentRead" BrowseName="2:AccessLevel_CurrentRead" ParentNodeId="ns=2;s=Demo.CTT.SecurityAccess" DataType="Int16" AccessLevel="3" UserAccessLevel="3">
        <DisplayName>AccessLevel_CurrentRead</DisplayName>
        <Description>A Node whose AccessLevel attribute contains 'CurrentRead'. The UserAccessLevel should match the AccessLevel so that it is not more restrictive.</Description>
        <References>
            <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
            <Reference ReferenceType="HasComponent" IsForward="false">ns=2;s=Demo.CTT.SecurityAccess</Reference>
        </References>
    </UAVariable>
    <UAVariable NodeId="ns=2;s=Demo.CTT.SecurityAccess.AccessLevel_CurrentRead_NotCurrentWrite" BrowseName="2:AccessLevel_CurrentRead_NotCurrentWrite" ParentNodeId="ns=2;s=Demo.CTT.SecurityAccess" DataType="Int16">
        <DisplayName>AccessLevel_CurrentRead_NotCurrentWrite</DisplayName>
        <Description>A Node whose UserAccessLevel contains 'CurrentRead', but explicitly does not include 'CurrentWrite'.</Description>
        <References>
            <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
            <Reference ReferenceType="HasComponent" IsForward="false">ns=2;s=Demo.CTT.SecurityAccess</Reference>
        </References>
    </UAVariable>
    <UAVariable NodeId="ns=2;s=Demo.CTT.SecurityAccess.AccessLevel_CurrentRead_NotUser" BrowseName="2:AccessLevel_CurrentRead_NotUser" ParentNodeId="ns=2;s=Demo.CTT.SecurityAccess" DataType="Int16" AccessLevel="3" UserAccessLevel="2">
        <DisplayName>AccessLevel_CurrentRead_NotUser</DisplayName>
        <Description>A Node whose AccessLevel attribute contains 'CurrentRead' but the UserAccessLevel does not contain 'CurrentRead'.</Description>
        <References>
            <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
            <Reference ReferenceType="HasComponent" IsForward="false">ns=2;s=Demo.CTT.SecurityAccess</Reference>
        </References>
    </UAVariable>
    <UAVariable NodeId="ns=2;s=Demo.CTT.SecurityAccess.AccessLevel_CurrentWrite" BrowseName="2:AccessLevel_CurrentWrite" ParentNodeId="ns=2;s=Demo.CTT.SecurityAccess" DataType="Int16" AccessLevel="3" UserAccessLevel="3">
        <DisplayName>AccessLevel_CurrentWrite</DisplayName>
        <Description>A Node whose AccessLevel attribute contains 'CurrentWrite'. The UserAccessLevel should match the AccessLevel so that it is not more restrictive.</Description>
        <References>
            <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
            <Reference ReferenceType="HasComponent" IsForward="false">ns=2;s=Demo.CTT.SecurityAccess</Reference>
        </References>
    </UAVariable>
    <UAVariable NodeId="ns=2;s=Demo.CTT.SecurityAccess.AccessLevel_CurrentWrite_NotCurrentRead" BrowseName="2:AccessLevel_CurrentWrite_NotCurrentRead" ParentNodeId="ns=2;s=Demo.CTT.SecurityAccess" DataType="Int16" AccessLevel="2" UserAccessLevel="2">
        <DisplayName>AccessLevel_CurrentWrite_NotCurrentRead</DisplayName>
        <Description>A Node whose UserAccessLevel contains 'CurrentWrite', but explicitly does not include 'CurrentRead'.</Description>
        <References>
            <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
            <Reference ReferenceType="HasComponent" IsForward="false">ns=2;s=Demo.CTT.SecurityAccess</Reference>
        </References>
    </UAVariable>
    <UAVariable NodeId="ns=2;s=Demo.CTT.SecurityAccess.AccessLevel_CurrentWrite_NotUser" BrowseName="2:AccessLevel_CurrentWrite_NotUser" ParentNodeId="ns=2;s=Demo.CTT.SecurityAccess" DataType="Int16" AccessLevel="3">
        <DisplayName>AccessLevel_CurrentWrite_NotUser</DisplayName>
        <Description>A Node whose AccessLevel attribute contains 'CurrentWrite' but the UserAccessLevel does not contain 'CurrentWrite'</Description>
        <References>
            <Reference ReferenceType="HasTypeDefinition">i=63</Reference>
            <Reference ReferenceType="HasComponent" IsForward="false">ns=2;s=Demo.CTT.SecurityAccess</Reference>
        </References>
    </UAVariable>
</UANodeSet>

By default UserAccessLevel = 1 and we don't need to write it in xml.

And after doing the import, I noticed that ns=2;s=Demo.CTT.SecurityAccess.AccessLevel_CurrentRead_NotUser and ns=2;s=Demo.CTT.SecurityAccess.AccessLevel_CurrentWrite_NotUser can`t change UserAccessLevel.

I have some XML attributes:

  1. ns=2;s=Demo.CTT.SecurityAccess.AccessLevel_CurrentRead_NotUser: AccessLevel="3" UserAccessLevel="2" A Node whose AccessLevel attribute contains 'CurrentRead' but the UserAccessLevel does not contain 'CurrentRead'.

After doing the import I have: image

But I must have: image

  1. ns=2;s=Demo.CTT.SecurityAccess.AccessLevel_CurrentWrite_NotUser: AccessLevel="3". A Node whose AccessLevel attribute contains 'CurrentWrite' but the UserAccessLevel does not contain 'CurrentWrite'

After doing the import I have: image

But I must have: image

When I change the AccessLevel attribute in xml - it works, the access value changes. When I change UserAccessLevel it doesn't work.

Maybe some bug in nodeset_loader?

upd: I noticed that when I change the AccessLevel, the UserAccessLevel also changes.:

AccessLevel="0" UserAccessLevel="0" image

AccessLevel="1" UserAccessLevel="0" image

AccessLevel="2" UserAccessLevel="0" image

AccessLevel="3" UserAccessLevel="0" image

matkonnerth commented 1 year ago

userAccessLevel is parsed here: https://github.com/open62541/open62541-nodeset-loader/blob/dac3acec9ae674163cb149f1990265c1c3ece878/src/Nodeset.c#L350

and the value for the new node is set here: https://github.com/open62541/open62541-nodeset-loader/blob/dac3acec9ae674163cb149f1990265c1c3ece878/backends/open62541/src/import.c#L218

xydan83 commented 1 year ago

I tried using debag and didn't find any problems. Maybe we need some permission data to add these attributes?

matkonnerth commented 1 year ago

thanks for trying it out. I think the open62541 needs some logic to "calculate" the UserAcessLevel, the stack has to consider the accessLevel and the user identified with the session.

What happens if you add a node without the nodesetLoader and set AccessLevel to CurrentRead | CurrrentWrite and userAccessLevel to CurrentRead?

xydan83 commented 1 year ago

thanks for trying it out. I think the open62541 needs some logic to "calculate" the UserAcessLevel, the stack has to consider the accessLevel and the user identified with the session.

What happens if you add a node without the nodesetLoader and set AccessLevel to CurrentRead | CurrrentWrite and userAccessLevel to CurrentRead?

I found this:

The following attributes cannot be written from the server, as they are specific to the different users and set by the access control callback:

UserWriteMask
UserAccessLevel
UserExecutable

https://www.open62541.org/doc/1.3/server.html#reading-and-writing-node-attributes

I think we can only change it from a client with some user access. From what I've read above, this is what I understand...

What happens if you add a node without the nodesetLoader and set AccessLevel to CurrentRead | CurrrentWrite and userAccessLevel to CurrentRead? - Good idea, I will try this.

matkonnerth commented 1 year ago

Is this issue ready for closing or should we investigate something further?

xydan83 commented 1 year ago

What happens if you add a node without the nodesetLoader and set AccessLevel to CurrentRead | CurrrentWrite and userAccessLevel to CurrentRead?

I think we can close this issue.

What happens if you add a node without the nodesetLoader and set AccessLevel to CurrentRead | CurrrentWrite and userAccessLevel to CurrentRead?

I will try this when I have more time for it.

Thank you! :)