FreeOpcUa / python-opcua

LGPL Pure Python OPC-UA Client and Server
http://freeopcua.github.io/
GNU Lesser General Public License v3.0
1.36k stars 658 forks source link

Client connect using username and password throws AttributeError #641

Open ns247 opened 6 years ago

ns247 commented 6 years ago

When trying to connect to an OPC server with a username and password using the following code an AttributeError is thrown:

client = Client("opc.tcp://127.0.0.1:53530/OPCUA/SimulationServer")
client.set_user("opctest")
client.set_password("test")
client.connect()

Traceback:

Error
Traceback (most recent call last):
  File "\lib\unittest\case.py", line 58, in testPartExecutor
    yield
  File "\lib\unittest\case.py", line 580, in run
    testMethod()
  File "\code\opc\test\test_OPCModule.py", line 26, in testPlaintext
    self.client.connect()
  File "\lib\site-packages\opcua\client\client.py", line 259, in connect
    self.activate_session(username=self._username, password=self._password, certificate=self.user_certificate)
  File "\lib\site-packages\opcua\client\client.py", line 436, in activate_session
    return self.uaclient.activate_session(params)
  File "\lib\site-packages\opcua\client\ua_client.py", line 259, in activate_session
    data = self._uasocket.send_request(request)
  File "\lib\site-packages\opcua\client\ua_client.py", line 75, in send_request
    future = self._send_request(request, callback, timeout, message_type)
  File "\lib\site-packages\opcua\client\ua_client.py", line 54, in _send_request
    binreq = struct_to_binary(request)
  File "\lib\site-packages\opcua\ua\ua_binary.py", line 258, in struct_to_binary
    packet.append(to_binary(uatype, val))
  File "\lib\site-packages\opcua\ua\ua_binary.py", line 281, in to_binary
    return struct_to_binary(val)
  File "\lib\site-packages\opcua\ua\ua_binary.py", line 258, in struct_to_binary
    packet.append(to_binary(uatype, val))
  File "\lib\site-packages\opcua\ua\ua_binary.py", line 271, in to_binary
    return pack_uatype(vtype, val)
  File "\lib\site-packages\opcua\ua\ua_binary.py", line 185, in pack_uatype
    return extensionobject_to_binary(value)
  File "\lib\site-packages\opcua\ua\ua_binary.py", line 457, in extensionobject_to_binary
    Body = struct_to_binary(obj)
  File "\lib\site-packages\opcua\ua\ua_binary.py", line 258, in struct_to_binary
    packet.append(to_binary(uatype, val))
  File "\lib\site-packages\opcua\ua\ua_binary.py", line 271, in to_binary
    return pack_uatype(vtype, val)
  File "\lib\site-packages\opcua\ua\ua_binary.py", line 181, in pack_uatype
    return getattr(Primitives, vtype.name).pack(value)
  File "\lib\site-packages\opcua\ua\ua_binary.py", line 69, in pack
    string = string.encode('utf-8')
AttributeError: 'bytes' object has no attribute 'encode'

Anonymous authentication works fine.

zerox1212 commented 6 years ago

Are you unit testing this on purpose?

ns247 commented 6 years ago

Yes, it was on purpose.

The posted code still fails with the same error even when it isn't wrapped as part of a unit test.

zerox1212 commented 6 years ago

Ok, we get a lot of people new to python so I had to ask. User management stuff is not really implemented. pull requests welcome.

ns247 commented 6 years ago

No problem!

To be clear I'm trying to use the python FreeOpcUa client to connect to a third party server, not set up a FreeOpcUa server.

It looks like this should already be covered by the tests already:

https://github.com/FreeOpcUa/python-opcua/blob/a8553adb23a040ea64e171f6ae4c2d4d22356b98/tests/tests_client.py#L34-L37

Not sure why it's failing for me.

zerox1212 commented 6 years ago

Are you using Python 2 or 3?

I would step through the code and see where your string is turning into bytes, because https://github.com/FreeOpcUa/python-opcua/blob/master/opcua/ua/ua_binary.py#L69 needs a string.

oroulet commented 6 years ago

I remember this was added by a PR and might be missing a test... Looks like somewhere bytes are written when code expect a string.. What version of Python?

zerox1212 commented 6 years ago

I'm also curious if using the admin@127.0.0.1 style is handled differently than set_user.

ns247 commented 6 years ago

I'm using Python 3.4 on Windows, I get the same error if I use this style instead:

client = Client("opc.tcp://opctest:test@127.0.0.1:53530/OPCUA/SimulationServer")

I'll step through my code and try to find out where it's becoming bytes.

ns247 commented 6 years ago

Some progress in the following section of code:

https://github.com/FreeOpcUa/python-opcua/blob/ddfff2c1cee9189b33dc8edd76741f3681ee756f/opcua/ua/ua_binary.py#L269-L271

Inspecting I find this:

uatype = {str} 'String'
val = {bytes} b'username_basic256'
vtype = {VariantType} VariantType.String

So the value is bytes, but it thinks the type is String.

Earlier in the trace I can see:

Password = {bytes} b'test'
PolicyId = {bytes} b'username_basic256'
UserName = {str} 'opctest'

ua_types = {list} <class 'list'>: [('PolicyId', 'String'), ('UserName', 'String'), ('Password', 'ByteString'), ('EncryptionAlgorithm', 'String')]

So looks like the problem is only with PolicyId, not the UserName or Password.

ns247 commented 6 years ago

So the schema xml files define PolicyId to be a string:

https://github.com/FreeOpcUa/python-opcua/blob/a8553adb23a040ea64e171f6ae4c2d4d22356b98/schemas/Opc.Ua.Types.xsd#L2663-L2677

But the code defines it as bytes:

https://github.com/FreeOpcUa/python-opcua/blob/0c76785ddab168edba9815b212151fda3aa1d0ef/opcua/client/client.py#L469

I'm not familiar enough with this project to know which way round this bug is.

I guess the next question is why is the built in test I linked before passing.

ns247 commented 6 years ago

Okay, so it appears our OPC UA Server wasn't returning appropriate policy id's, so it was falling through to the default of b"username_basic256".

When tested against a server that correctly returns the right username policy everything works as expected.

I've created a pull request: #643