FreeOpcUa / opcua-asyncio

OPC UA library for python >= 3.7
GNU Lesser General Public License v3.0
1.12k stars 361 forks source link

Read registered nodes as Anonymous returns BadUserAccessDenied #547

Open OlivierVerhaegen opened 3 years ago

OlivierVerhaegen commented 3 years ago

This is a *BUG REPORT for issues in asyncua v0.9.14.

Describe the bug
When connected as anonymous using UA Expert and using an encrypted connection. A test in performance view for Read Registered returns BadUserAccessDenied. The normal Read mode works without any problems. Without an encrypted connection (None) everything works as expected.

Is there something I am missing? Please take a look at the screenshots and my code.

To Reproduce

  1. Open an encrypted connection to the OPC UA server using UA Expert as Anonymous
  2. Open performance view
  3. Drag in some nodes to read
  4. Only check "Read Registered"
  5. Choose an applicable node count (I used 100, 150)
  6. Start the test and watch the log

Expected behavior
Perform the registered read action without any error.

Code

#!/usr/bin/python3

import logging
import asyncio
import sys, getopt

from asyncua import ua, Server, Node
from random import randint
from os import _exit
sys.path.insert(0, "..")

IMPORT_XML = False
NUM_OF_VAR = 200

async def main(argv):
    _logger = logging.getLogger('asyncua')

    host = 'localhost'
    port = '4840'
    endpoint = 'server-1'
    name = 'Server #1'

    # Handle arguments https://www.tutorialspoint.com/python/python_command_line_arguments.htm
    try:
        opts, args = getopt.getopt(argv, 'h::p::e::n::', ['host=', 'port=', 'endpoint=', 'name=']) # : -> required, :: -> optional
    except getopt.GetoptError:
        _logger.error('Argument error: server.py -p <port> -e <endpoint> -n <name>')
        sys.exit(2)

    for opt, arg in opts:
        if opt in ('-h', '--host'):
            host = arg
        if opt in ('-p', '--port'):
            port = arg
        if opt in ('-e', '--endpoint'):
            endpoint = arg
        if opt in ('-n', '--name'):
            name = arg

    # Create server instance
    server = await init_server(host, port, endpoint, name)
    # Populate address space
    variables = await populate_address_space(server)

    _logger.info('Starting server!')
    async with server:
        while True:
            await asyncio.sleep(1)
            await simulate(variables)

async def init_server(host: str, port: str, endpoint: str, name: str) -> Server:
    """ Initializes an OPC UA server instance.

    Args:
        host (str): The hostname or IP-address.
        port (str): The port to use in the web socket.
        endpoint (str): Path to the server. eg. path/to/server (default: /)
        name (str): Display name of the server.

    Returns:
        Server: An OPC UA server instance setup using the given arguments.
    """
    # Initialize server instance
    server = Server()
    await server.init()
    server.set_endpoint(f'opc.tcp://{host}:{port}/{endpoint}/')
    server.set_server_name(f'{name}')
    server.set_security_policy([
        ua.SecurityPolicyType.NoSecurity,
        ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt
    ])

    await server.load_certificate('certs/own/server.der')
    await server.load_private_key('certs/own/pk.pem')

    return server

async def populate_address_space(server: Server) -> list[Node]:
    """ Used when there is no XML file imported. This function will populate the servers address space with simulation variables.

    Args:
        server (Server): The server instance to which the simulation variables will be added.

    Returns:
        list[Node]: A list of the created nodes.
    """
    # Set our own namespace (must according to OPC UA spec)
    uri = 'http://uasim.test.com'
    idx = await server.register_namespace(uri)

    # Add organization object.
    test_uasim_obj = await server.nodes.objects.add_object(idx, 'BASF-UASimulation')
    # Add variables under object.
    variables = []
    for i in range(NUM_OF_VAR):
        variables.append(await test_uasim_obj.add_variable(idx, f'Counter-{i}', randint(1, 100)))

    return variables

async def simulate(variables: list[Node]):
    """ Simulates the values of the given variables.

    Each variable is expected to be an integer, a random number between from 1 to 100 is added
    to the variables each time the simulation is run.

    Args:
        variables (list[Node]): The variables to be simulated.
    """
    for var in variables:
        new_value = await var.get_value() + randint(1, 100)
        await var.write_value(new_value)

if __name__ == '__main__':
    logging.basicConfig(level=logging.DEBUG)

    try:
        asyncio.run(main(sys.argv[1:]), debug=True)
    except OSError as e:
        print(str(e))
        input('Press any key to close.')
    except KeyboardInterrupt:
        print('Closing server...')
        try:
            sys.exit(0)
        except SystemExit:
            _exit(0)

Screenshots
image image image image

Version
Python-Version: 3.9.4
opcua-asyncio Version: 0.9.14

magnus-bakke commented 8 months ago

Also seeing this.

OlivierVerhaegen commented 2 months ago

Hi, any news?