Skip to content

Commit 64fc22a

Browse files
igfarmoroulet
authored andcommitted
Add support for server username/password auth
1 parent 4a9c569 commit 64fc22a

2 files changed

Lines changed: 67 additions & 14 deletions

File tree

opcua/server/internal_server.py

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
Internal server implementing opcu-ua interface.
33
Can be used on server side or to implement binary/https opc-ua servers
44
"""
5-
65
from datetime import datetime, timedelta
76
from copy import copy
7+
from struct import unpack_from, unpack
88
import os
99
import logging
1010
from threading import Lock
@@ -31,6 +31,13 @@
3131
from opcua.server.users import User
3232
#from opcua.common import xmlimporter
3333

34+
use_crypto = True
35+
try:
36+
from opcua.crypto import uacrypto
37+
except ImportError:
38+
logging.getLogger(__name__).warning("cryptography is not installed, use of crypto disabled")
39+
use_crypto = False
40+
3441

3542
class SessionState(Enum):
3643
Created = 0
@@ -57,6 +64,9 @@ def __init__(self, shelffile=None):
5764
self.disabled_clock = False # for debugging we may want to disable clock that writes too much in log
5865
self._known_servers = {} # used if we are a discovery server
5966

67+
self.certificate = None
68+
self.private_key = None
69+
6070
self.aspace = AddressSpace()
6171
self.attribute_service = AttributeService(self.aspace)
6272
self.view_service = ViewService(self.aspace)
@@ -71,6 +81,8 @@ def __init__(self, shelffile=None):
7181

7282
self.history_manager = HistoryManager(self)
7383

84+
self.user_manager = default_user_manager # defined at the end of this file
85+
7486
# create a session to use on server side
7587
self.isession = InternalSession(self, self.aspace, self.subscription_service, "Internal", user=User.Admin)
7688

@@ -269,7 +281,41 @@ def set_attribute_value(self, nodeid, datavalue, attr=ua.AttributeIds.Value):
269281
"""
270282
self.aspace.set_attribute_value(nodeid, ua.AttributeIds.Value, datavalue)
271283

284+
def set_user_manager(self, user_manager):
285+
"""
286+
set up a function which that will check for authorize users. Input function takes username
287+
and password as paramters and returns True of user is allowed access, False otherwise.
288+
"""
289+
self.user_manager = user_manager
272290

291+
def check_user_token(self, isession, token):
292+
"""
293+
unpack the username and password for the benefit of the user defined user manager
294+
"""
295+
userName = token.UserName
296+
passwd = token.Password
297+
298+
# decrypt password is we can
299+
if str(token.EncryptionAlgorithm) != "None":
300+
if use_crypto == False:
301+
return False;
302+
try:
303+
if token.EncryptionAlgorithm == "http://www.w3.org/2001/04/xmlenc#rsa-1_5":
304+
raw_pw = uacrypto.decrypt_rsa15(self.private_key, passwd)
305+
elif token.EncryptionAlgorithm == "http://www.w3.org/2001/04/xmlenc#rsa-oaep":
306+
raw_pw = uacrypto.decrypt_rsa_oaep(self.private_key, passwd)
307+
else:
308+
self.logger.warning("Unknown password encoding '{0}'".format(token.EncryptionAlgorithm))
309+
return False
310+
length = unpack_from('<I', raw_pw)[0] - len(isession.nonce)
311+
passwd = raw_pw[4:4 + length]
312+
passwd = passwd.decode('utf-8')
313+
except Exception as exp:
314+
self.logger.warning("Unable to decrypt password")
315+
return False
316+
317+
# call user_manager
318+
return self.user_manager(self, isession, userName, passwd)
273319

274320

275321
class InternalSession(object):
@@ -332,8 +378,8 @@ def activate_session(self, params):
332378
self.state = SessionState.Activated
333379
id_token = params.UserIdentityToken
334380
if isinstance(id_token, ua.UserNameIdentityToken):
335-
if self.iserver.allow_remote_admin and id_token.UserName in ("admin", "Admin"):
336-
self.user = User.Admin
381+
if self.iserver.check_user_token(self, id_token) == False:
382+
raise utils.ServiceError(ua.StatusCodes.BadUserAccessDenied)
337383
self.logger.info("Activated internal session %s for user %s", self.name, self.user)
338384
return result
339385

@@ -412,3 +458,12 @@ def publish(self, acks=None):
412458
if acks is None:
413459
acks = []
414460
return self.subscription_service.publish(acks)
461+
462+
463+
def default_user_manager(iserver, isession, userName, password):
464+
"""
465+
Default user_manager, does nothing much but check for admin
466+
"""
467+
if iserver.allow_remote_admin and userName in ("admin", "Admin"):
468+
isession.user = User.Admin
469+
return True

opcua/server/server.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,6 @@ def __init__(self, shelffile=None, iserver=None):
9090
self.bserver = None
9191
self._discovery_clients = {}
9292
self._discovery_period = 60
93-
self.certificate = None
94-
self.private_key = None
9593
self._policies = []
9694
self.nodes = Shortcuts(self.iserver.isession)
9795

@@ -132,10 +130,10 @@ def load_certificate(self, path):
132130
"""
133131
load server certificate from file, either pem or der
134132
"""
135-
self.certificate = uacrypto.load_certificate(path)
133+
self.iserver.certificate = uacrypto.load_certificate(path)
136134

137135
def load_private_key(self, path):
138-
self.private_key = uacrypto.load_private_key(path)
136+
self.iserver.private_key = uacrypto.load_private_key(path)
139137

140138
def disable_clock(self, val=True):
141139
"""
@@ -265,7 +263,7 @@ def _setup_server_nodes(self):
265263
self._policies = [ua.SecurityPolicyFactory()]
266264

267265
if self._security_policy != [ua.SecurityPolicyType.NoSecurity]:
268-
if not (self.certificate and self.private_key):
266+
if not (self.iserver.certificate and self.iserver.private_key):
269267
self.logger.warning("Endpoints other than open requested but private key and certificate are not set.")
270268
return
271269

@@ -277,16 +275,16 @@ def _setup_server_nodes(self):
277275
ua.MessageSecurityMode.SignAndEncrypt)
278276
self._policies.append(ua.SecurityPolicyFactory(security_policies.SecurityPolicyBasic256Sha256,
279277
ua.MessageSecurityMode.SignAndEncrypt,
280-
self.certificate,
281-
self.private_key)
278+
self.iserver.certificate,
279+
self.iserver.private_key)
282280
)
283281
if ua.SecurityPolicyType.Basic256Sha256_Sign in self._security_policy:
284282
self._set_endpoints(security_policies.SecurityPolicyBasic256Sha256,
285283
ua.MessageSecurityMode.Sign)
286284
self._policies.append(ua.SecurityPolicyFactory(security_policies.SecurityPolicyBasic256Sha256,
287285
ua.MessageSecurityMode.Sign,
288-
self.certificate,
289-
self.private_key)
286+
self.iserver.certificate,
287+
self.iserver.private_key)
290288
)
291289

292290
def _set_endpoints(self, policy=ua.SecurityPolicy, mode=ua.MessageSecurityMode.None_):
@@ -319,8 +317,8 @@ def _set_endpoints(self, policy=ua.SecurityPolicy, mode=ua.MessageSecurityMode.N
319317
edp = ua.EndpointDescription()
320318
edp.EndpointUrl = self.endpoint.geturl()
321319
edp.Server = appdesc
322-
if self.certificate:
323-
edp.ServerCertificate = uacrypto.der_from_x509(self.certificate)
320+
if self.iserver.certificate:
321+
edp.ServerCertificate = uacrypto.der_from_x509(self.iserver.certificate)
324322
edp.SecurityMode = mode
325323
edp.SecurityPolicyUri = policy.URI
326324
edp.UserIdentityTokens = idtokens

0 commit comments

Comments
 (0)