22Internal server implementing opcu-ua interface.
33Can be used on server side or to implement binary/https opc-ua servers
44"""
5-
65from datetime import datetime , timedelta
76from copy import copy
7+ from struct import unpack_from , unpack
88import os
99import logging
1010from threading import Lock
3131from 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
3542class 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
275321class 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
0 commit comments