diff --git a/actor/src/udsactor/linux/operations.py b/actor/src/udsactor/linux/operations.py index bc9e80279..a93361431 100644 --- a/actor/src/udsactor/linux/operations.py +++ b/actor/src/udsactor/linux/operations.py @@ -187,13 +187,16 @@ def renameComputer(newName: str) -> bool: rename(newName) return True # Always reboot right now. Not much slower but much more convenient -def joinDomain(domain: str, ou: str, account: str, password: str, executeInOneStep: bool = False, custom: typing.Optional[typing.Mapping[str, typing.Any]] = None): +def joinDomain(name: str, custom: typing.Optional[typing.Mapping[str, typing.Any]] = None): if not custom: logger.error('Error joining domain: no custom data provided') return # Read parameters from custom data - name: str = custom.get('name', '') + domain: str = custom.get('domain', '') + ou: str = custom.get('ou', '') + account: str = custom.get('account', '') + password: str = custom.get('password', '') client_software: str = custom.get('client_software', '') server_software: str = custom.get('server_software', '') membership_software: str = custom.get('membership_software', '') diff --git a/actor/src/udsactor/linux/service.py b/actor/src/udsactor/linux/service.py index 34e2feb84..3a8698260 100644 --- a/actor/src/udsactor/linux/service.py +++ b/actor/src/udsactor/linux/service.py @@ -64,20 +64,15 @@ class UDSActorSvc(daemon.Daemon, CommonService): return self._sensibleDataCleanable def joinDomain( # pylint: disable=unused-argument, too-many-arguments - self, name: str, domain: str, ou: str, account: str, password: str, custom: typing.Optional[typing.Mapping[str, typing.Any]] = None + self, name: str, custom: typing.Mapping[str, typing.Any] ) -> None: - # Add name to custom data, needed by joinDomain on Linux - # First, Copy mapping to mutable dict so we can add name to it - localCustom = {k: v for k, v in (custom.items() if custom is not None else [])} - localCustom['name'] = name - - # If isPersistent is False, we need to keep sensible data, so it's not cleaned - self._sensibleDataCleanable = localCustom.get('isPersistent', False) + + self._sensibleDataCleanable = custom.get('isPersistent', False) self.rename(name) - logger.debug(f'Starting joining domain {domain} with name {name}') - operations.joinDomain(domain, ou, account, password, custom=localCustom) + logger.debug('Starting joining domain %s with name %s', custom.get('domain', ''), name) + operations.joinDomain(name, custom) def finish(self) -> None: try: @@ -86,11 +81,11 @@ class UDSActorSvc(daemon.Daemon, CommonService): custom = self._cfg.config.os.custom if osData.action == 'rename_ad' and custom.get('isPersistent', False): operations.leaveDomain( - osData.ad or '', - osData.username or '', - osData.password or '', - custom['clientSoftware'] or '', - custom['serverSoftware'] or '', + custom.get('ad', ''), + custom.get('username', ''), + custom.get('password', ''), + custom.get('clientSoftware', ''), + custom.get('serverSoftware', ''), ) except Exception as e: logger.error(f'Got exception operating machine: {e}') diff --git a/actor/src/udsactor/rest.py b/actor/src/udsactor/rest.py index 8881512e2..b7fe21ec6 100644 --- a/actor/src/udsactor/rest.py +++ b/actor/src/udsactor/rest.py @@ -289,11 +289,6 @@ class UDSServerApi(UDSApi): os=types.ActorOsConfigurationType( action=os['action'], name=os['name'], - username=os.get('username'), - password=os.get('password'), - new_password=os.get('new_password'), - ad=os.get('ad'), - ou=os.get('ou'), custom=os.get('custom'), ) if r['os'] diff --git a/actor/src/udsactor/service.py b/actor/src/udsactor/service.py index fb84fcbe4..15fe82e6c 100644 --- a/actor/src/udsactor/service.py +++ b/actor/src/udsactor/service.py @@ -75,9 +75,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes logger.debug('Executing command on {}: {}'.format(section, cmdLine)) res = subprocess.check_call(cmdLine, shell=True) except Exception as e: - logger.error( - 'Got exception executing: {} - {} - {}'.format(section, cmdLine, e) - ) + logger.error('Got exception executing: {} - {} - {}'.format(section, cmdLine, e)) return False logger.debug('Result of executing cmd for {} was {}'.format(section, res)) return True @@ -131,9 +129,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes ) # Emty interfaces is like "no ip change" because cannot be notified if self._cfg.config and interfaces: try: - return next( - x for x in interfaces if x.mac.lower() == self._cfg.config.unique_id - ) + return next(x for x in interfaces if x.mac.lower() == self._cfg.config.unique_id) except StopIteration: return interfaces[0] @@ -188,9 +184,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes # Success or any error that is not recoverable (retunerd by UDS). if Error, service will be cleaned in a while. break else: - logger.error( - 'Could not locate IP address!!!. (Not registered with UDS)' - ) + logger.error('Could not locate IP address!!!. (Not registered with UDS)') # Do not continue if not alive... if not self._isAlive: @@ -199,9 +193,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes # Cleans sensible data if self._cfg.config: if self.canCleanSensibleData(): - self._cfg = self._cfg._replace( - config=self._cfg.config._replace(os=None), data=None - ) + self._cfg = self._cfg._replace(config=self._cfg.config._replace(os=None), data=None) platform.store.writeConfig(self._cfg) logger.info('Service ready') @@ -232,22 +224,17 @@ class CommonService: # pylint: disable=too-many-instance-attributes try: if self._cfg.config and self._cfg.config.os: osData = self._cfg.config.os + custom: typing.Mapping[str, typing.Any] = osData.custom or {} + # Needs UDS Server >= 4.0 to work if osData.action == 'rename': self.rename( osData.name, - osData.username, - osData.password, - osData.new_password, + custom.get('username'), + custom.get('password'), + custom.get('new_password'), ) elif osData.action == 'rename_ad': - self.joinDomain( - osData.name, - osData.ad or '', - osData.ou or '', - osData.username or '', - osData.password or '', - osData.custom - ) + self.joinDomain(osData.name, custom) if self._rebootRequested: try: @@ -295,9 +282,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes self.doWait(5000) def initialize(self) -> bool: - if ( - self._initialized or not self._cfg.host or not self._isAlive - ): # Not configured or not running + if self._initialized or not self._cfg.host or not self._isAlive: # Not configured or not running return False self._initialized = True @@ -319,18 +304,14 @@ class CommonService: # pylint: disable=too-many-instance-attributes ) if not initResult.own_token: # Not managed logger.debug( - 'This host is not managed by UDS Broker (ids: {})'.format( - self._interfaces - ) + 'This host is not managed by UDS Broker (ids: {})'.format(self._interfaces) ) return False # Only removes master token for managed machines (will need it on next client execution) # For unmanaged, if alias is present, replace master token with it master_token = ( - None - if self.isManaged() - else (initResult.alias_token or self._cfg.master_token) + None if self.isManaged() else (initResult.alias_token or self._cfg.master_token) ) # Replace master token with alias token if present self._cfg = self._cfg._replace( @@ -352,16 +333,10 @@ class CommonService: # pylint: disable=too-many-instance-attributes break # Initial configuration done.. except rest.RESTConnectionError as e: - logger.info( - 'Trying to inititialize connection with broker (last error: {})'.format( - e - ) - ) + logger.info('Trying to inititialize connection with broker (last error: {})'.format(e)) self.doWait(5000) # Wait a bit and retry except rest.RESTError as e: # Invalid key? - logger.error( - 'Error validating with broker. (Invalid token?): {}'.format(e) - ) + logger.error('Error validating with broker. (Invalid token?): {}'.format(e)) return False except Exception: logger.exception() @@ -371,9 +346,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes def uninitialize(self): self._initialized = False - self._cfg = self._cfg._replace( - own_token=None - ) # Ensures assigned token is cleared + self._cfg = self._cfg._replace(own_token=None) # Ensures assigned token is cleared def finish(self) -> None: if self._http: @@ -389,8 +362,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes self._cfg.actorType, self._cfg.own_token, '', - client.session_id - or 'stop', # If no session id, pass "stop" + client.session_id or 'stop', # If no session id, pass "stop" '', self._interfaces, self._secret, @@ -405,11 +377,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes return # Unamanaged hosts does not changes ips. (The full initialize-login-logout process is done in a row, so at login the IP is correct) try: - if ( - not self._cfg.own_token - or not self._cfg.config - or not self._cfg.config.unique_id - ): + if not self._cfg.own_token or not self._cfg.config or not self._cfg.config.unique_id: # Not enouth data do check return currentInterfaces = tools.validNetworkCards( @@ -418,59 +386,20 @@ class CommonService: # pylint: disable=too-many-instance-attributes old = self.serviceInterfaceInfo() new = self.serviceInterfaceInfo(currentInterfaces) if not new or not old: - raise Exception( - 'No ip currently available for {}'.format( - self._cfg.config.unique_id - ) - ) + raise Exception('No ip currently available for {}'.format(self._cfg.config.unique_id)) if old.ip != new.ip: self._certificate = self._api.notifyIpChange( self._cfg.own_token, self._secret, new.ip, rest.LISTEN_PORT ) # Now store new addresses & interfaces... self._interfaces = currentInterfaces - logger.info( - 'Ip changed from {} to {}. Notified to UDS'.format(old.ip, new.ip) - ) + logger.info('Ip changed from {} to {}. Notified to UDS'.format(old.ip, new.ip)) # Stop the running HTTP Thread and start a new one, with new generated cert self.startHttpServer() except Exception as e: # No ip changed, log exception for info logger.warn('Checking ips failed: {}'.format(e)) - def rename( - self, - name: str, - userName: typing.Optional[str] = None, - oldPassword: typing.Optional[str] = None, - newPassword: typing.Optional[str] = None, - ) -> None: - ''' - Invoked when broker requests a rename action - default does nothing - ''' - hostName = platform.operations.getComputerName() - - # Check for password change request for an user - if userName and newPassword: - logger.info('Setting password for configured user') - try: - platform.operations.changeUserPassword( - userName, oldPassword or '', newPassword - ) - except Exception as e: - # Logs error, but continue renaming computer - logger.error( - 'Could not change password for user {}: {}'.format(userName, e) - ) - - if hostName.lower() == name.lower(): - logger.info('Computer name is already {}'.format(hostName)) - return - - if platform.operations.renameComputer(name): - self.reboot() - def loop(self): # Main common loop try: @@ -487,22 +416,44 @@ class CommonService: # pylint: disable=too-many-instance-attributes # ****************************************************** # Methods that can be overriden by linux & windows Actor # ****************************************************** - def joinDomain( # pylint: disable=unused-argument, too-many-arguments - self, name: str, domain: str, ou: str, account: str, password: str, custom: typing.Optional[typing.Mapping[str, typing.Any]] = None + def rename( + self, + name: str, + userName: typing.Optional[str] = None, + oldPassword: typing.Optional[str] = None, + newPassword: typing.Optional[str] = None, ) -> None: + ''' + Invoked when broker requests a rename action + ''' + hostName = platform.operations.getComputerName() + + # Check for password change request for an user + if userName and newPassword: + logger.info('Setting password for configured user') + try: + platform.operations.changeUserPassword(userName, oldPassword or '', newPassword) + except Exception as e: + # Logs error, but continue renaming computer + logger.error('Could not change password for user {}: {}'.format(userName, e)) + + if hostName.lower() == name.lower(): + logger.info('Computer name is already {}'.format(hostName)) + return + + if platform.operations.renameComputer(name): + self.reboot() + + def joinDomain(self, name: str, custom: typing.Mapping[str, typing.Any]) -> None: ''' Invoked when broker requests a "domain" action default does nothing ''' - logger.debug('Base join invoked: {} on {}, {}'.format(name, domain, ou)) + logger.debug('Base join invoked: %s on %s, %s', name, custom) # Client notifications - def login( - self, username: str, sessionType: typing.Optional[str] = None - ) -> types.LoginResultInfoType: - result = types.LoginResultInfoType( - ip='', hostname='', dead_line=None, max_idle=None, session_id=None - ) + def login(self, username: str, sessionType: typing.Optional[str] = None) -> types.LoginResultInfoType: + result = types.LoginResultInfoType(ip='', hostname='', dead_line=None, max_idle=None, session_id=None) master_token = None secret = None # If unmanaged, do initialization now, because we don't know before this @@ -564,9 +515,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes ) != 'ok' # Can return also "notified", that means the logout has not been processed by UDS ): - logger.info( - 'Logout from %s ignored as required by uds broker', username - ) + logger.info('Logout from %s ignored as required by uds broker', username) return self.onLogout(username, session_id or '') @@ -595,9 +544,7 @@ class CommonService: # pylint: disable=too-many-instance-attributes ''' logger.info('Service stopped') - def preConnect( - self, userName: str, protocol: str, ip: str, hostname: str, udsUserName: str - ) -> str: + def preConnect(self, userName: str, protocol: str, ip: str, hostname: str, udsUserName: str) -> str: ''' Invoked when received a PRE Connection request via REST Base preconnect executes the preconnect command diff --git a/actor/src/udsactor/types.py b/actor/src/udsactor/types.py index ca97b6e9d..beec0a8e6 100644 --- a/actor/src/udsactor/types.py +++ b/actor/src/udsactor/types.py @@ -19,11 +19,6 @@ class AuthenticatorType(typing.NamedTuple): class ActorOsConfigurationType(typing.NamedTuple): action: str name: str - username: typing.Optional[str] = None - password: typing.Optional[str] = None - new_password: typing.Optional[str] = None - ad: typing.Optional[str] = None - ou: typing.Optional[str] = None custom: typing.Optional[typing.Mapping[str, typing.Any]] = None class ActorDataConfigurationType(typing.NamedTuple): diff --git a/actor/src/udsactor/windows/operations.py b/actor/src/udsactor/windows/operations.py index 4a9cdce65..677df5355 100644 --- a/actor/src/udsactor/windows/operations.py +++ b/actor/src/udsactor/windows/operations.py @@ -63,24 +63,17 @@ def getComputerName() -> str: def getNetworkInfo() -> typing.Iterator[types.InterfaceInfoType]: obj = win32com.client.Dispatch("WbemScripting.SWbemLocator") wmobj = obj.ConnectServer("localhost", "root\\cimv2") - adapters = wmobj.ExecQuery( - "Select * from Win32_NetworkAdapterConfiguration where IpEnabled=True" - ) + adapters = wmobj.ExecQuery("Select * from Win32_NetworkAdapterConfiguration where IpEnabled=True") try: for obj in adapters: for ip in obj.IPAddress: if ':' in ip: # Is IPV6, skip this continue if ( - ip is None - or ip == '' - or ip.startswith('169.254') - or ip.startswith('0.') + ip is None or ip == '' or ip.startswith('169.254') or ip.startswith('0.') ): # If single link ip, or no ip continue - yield types.InterfaceInfoType( - name=obj.Caption, mac=obj.MACAddress, ip=ip - ) + yield types.InterfaceInfoType(name=obj.Caption, mac=obj.MACAddress, ip=ip) except Exception: return @@ -97,7 +90,7 @@ def getDomainName() -> str: # 3 = Domain domain, status = win32net.NetGetJoinInformation() if status != 3: - domain = None + domain = '' return domain @@ -109,9 +102,7 @@ def getWindowsVersion() -> typing.Tuple[int, int, int, int, str]: def getVersion() -> str: verinfo = getWindowsVersion() # Remove platform id i - return 'Windows-{}.{} Build {} ({})'.format( - verinfo[0], verinfo[1], verinfo[2], verinfo[4] - ) + return 'Windows-{}.{} Build {} ({})'.format(verinfo[0], verinfo[1], verinfo[2], verinfo[4]) EWX_LOGOFF = 0x00000000 @@ -129,11 +120,11 @@ def reboot(flags: int = EWX_FORCEIFHUNG | EWX_REBOOT) -> None: ) privs = ( ( - win32security.LookupPrivilegeValue(None, win32security.SE_SHUTDOWN_NAME), + win32security.LookupPrivilegeValue(None, win32security.SE_SHUTDOWN_NAME), # type: ignore win32security.SE_PRIVILEGE_ENABLED, ), ) - win32security.AdjustTokenPrivileges(htok, 0, privs) + win32security.AdjustTokenPrivileges(htok, 0, privs) # type: ignore win32api.ExitWindowsEx(flags, 0) @@ -148,7 +139,7 @@ def renameComputer(newName: str) -> bool: ''' # Needs admin privileges to work if ( - ctypes.windll.kernel32.SetComputerNameExW( + ctypes.windll.kernel32.SetComputerNameExW( # type: ignore DWORD(win32con.ComputerNamePhysicalDnsHostname), LPCWSTR(newName) ) == 0 @@ -157,14 +148,8 @@ def renameComputer(newName: str) -> bool: # win32api.GetLastError -> returns error code # (just put this comment here to remember to log this when logger is available) error = getErrorMessage() - computerName = win32api.GetComputerNameEx( - win32con.ComputerNamePhysicalDnsHostname - ) - raise Exception( - 'Error renaming computer from {} to {}: {}'.format( - computerName, newName, error - ) - ) + computerName = win32api.GetComputerNameEx(win32con.ComputerNamePhysicalDnsHostname) + raise Exception('Error renaming computer from {} to {}: {}'.format(computerName, newName, error)) return True @@ -179,9 +164,7 @@ NETSETUP_JOIN_WITH_NEW_NAME = 0x00000400 NETSETUP_DEFER_SPN_SET = 0x1000000 -def joinDomain( - domain: str, ou: str, account: str, password: str, executeInOneStep: bool = False -) -> None: +def joinDomain(domain: str, ou: str, account: str, password: str, executeInOneStep: bool = False) -> None: ''' Joins machine to a windows domain :param domain: Domain to join to @@ -198,9 +181,7 @@ def joinDomain( account = domain + '\\' + account # Do log - flags: typing.Any = ( - NETSETUP_ACCT_CREATE | NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_DOMAIN - ) + flags: typing.Any = NETSETUP_ACCT_CREATE | NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_DOMAIN if executeInOneStep: flags |= NETSETUP_JOIN_WITH_NEW_NAME @@ -214,13 +195,13 @@ def joinDomain( lpAccount = LPCWSTR(account) lpPassword = LPCWSTR(password) - res = ctypes.windll.netapi32.NetJoinDomain( + res = ctypes.windll.netapi32.NetJoinDomain( # type: ignore None, lpDomain, lpOu, lpAccount, lpPassword, flags ) # Machine found in another ou, use it and warn this on log if res == 2224: flags = DWORD(NETSETUP_DOMAIN_JOIN_IF_JOINED | NETSETUP_JOIN_DOMAIN) - res = ctypes.windll.netapi32.NetJoinDomain( + res = ctypes.windll.netapi32.NetJoinDomain( # type: ignore None, lpDomain, None, lpAccount, lpPassword, flags ) if res: @@ -247,14 +228,12 @@ def changeUserPassword(user: str, oldPassword: str, newPassword: str) -> None: # res = ctypes.windll.netapi32.NetUserChangePassword(None, lpUser, lpOldPassword, lpNewPassword) # Try to set new password "a las bravas", ignoring old one. This will not work with domain users - res = win32net.NetUserSetInfo(None, user, 1003, {'password': newPassword}) + res = win32net.NetUserSetInfo(None, user, 1003, {'password': newPassword}) # type: ignore if res: # Log the error, and raise exception to parent error = getErrorMessage(res) - raise Exception( - 'Error changing password for user {}: {} {}'.format(user, res, error) - ) + raise Exception('Error changing password for user {}: {} {}'.format(user, res, error)) class LASTINPUTINFO(ctypes.Structure): # pylint: disable=too-few-public-methods @@ -274,14 +253,14 @@ def initIdleDuration(atLeastSeconds: int): # pylint: disable=unused-argument def getIdleDuration() -> float: try: lastInputInfo = LASTINPUTINFO() - lastInputInfo.cbSize = ctypes.sizeof( - lastInputInfo - ) # pylint: disable=attribute-defined-outside-init - if ctypes.windll.user32.GetLastInputInfo(ctypes.byref(lastInputInfo)) == 0: + lastInputInfo.cbSize = ctypes.sizeof(lastInputInfo) # pylint: disable=attribute-defined-outside-init + if ctypes.windll.user32.GetLastInputInfo(ctypes.byref(lastInputInfo)) == 0: # type: ignore return 0 - current = ctypes.c_uint(ctypes.windll.kernel32.GetTickCount()).value + current = ctypes.c_uint(ctypes.windll.kernel32.GetTickCount()).value # type: ignore if current < lastInputInfo.dwTime: - current += 4294967296 # If current has "rolled" to zero, adjust it so it is greater than lastInputInfo + current += ( + 4294967296 # If current has "rolled" to zero, adjust it so it is greater than lastInputInfo + ) millis = current - lastInputInfo.dwTime # @UndefinedVariable return millis / 1000.0 except Exception as e: @@ -306,9 +285,7 @@ def getSessionType() -> str: return os.environ.get('SESSIONNAME', 'unknown') -def writeToPipe( - pipeName: str, bytesPayload: bytes, waitForResponse: bool -) -> typing.Optional[bytes]: +def writeToPipe(pipeName: str, bytesPayload: bytes, waitForResponse: bool) -> typing.Optional[bytes]: # (str, bytes, bool) -> Optional[bytes] try: with open(pipeName, 'r+b', 0) as f: @@ -323,8 +300,6 @@ def writeToPipe( def forceTimeSync() -> None: try: - subprocess.call( - [r'c:\WINDOWS\System32\w32tm.exe', ' /resync'] - ) # , '/rediscover']) + subprocess.call([r'c:\WINDOWS\System32\w32tm.exe', ' /resync']) # , '/rediscover']) except Exception as e: logger.error('Error invoking time sync command: %s', e) diff --git a/actor/src/udsactor/windows/service.py b/actor/src/udsactor/windows/service.py index 0568a8ada..ed08ca16f 100644 --- a/actor/src/udsactor/windows/service.py +++ b/actor/src/udsactor/windows/service.py @@ -49,11 +49,13 @@ from ..log import logger REMOTE_USERS_SID = 'S-1-5-32-555' # Well nown sid for remote desktop users + class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService): ''' This class represents a Windows Service for managing actor interactions with UDS Broker and Machine ''' + # ServiceeFramework related _svc_name_ = "UDSActorNG" _svc_display_name_ = "UDS Actor Service" @@ -78,7 +80,9 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService): SvcShutdown = SvcStop def notifyStop(self) -> None: - servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STOPPED, (self._svc_name_, '')) + servicemanager.LogMsg( + servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STOPPED, (self._svc_name_, '') + ) super().notifyStop() def doWait(self, miliseconds: int) -> None: @@ -86,7 +90,9 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService): # On windows, and while on tasks, ensure that our app processes waiting messages on "wait times" pythoncom.PumpWaitingMessages() # pylint: disable=no-member - def oneStepJoin(self, name: str, domain: str, ou: str, account: str, password: str) -> None: # pylint: disable=too-many-arguments + def oneStepJoin( + self, name: str, domain: str, ou: str, account: str, password: str + ) -> None: # pylint: disable=too-many-arguments ''' Ejecutes the join domain in exactly one step ''' @@ -103,7 +109,9 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService): logger.debug('Requested join domain {} without errors'.format(domain)) self.reboot() - def multiStepJoin(self, name: str, domain: str, ou: str, account: str, password: str) -> None: # pylint: disable=too-many-arguments + def multiStepJoin( + self, name: str, domain: str, ou: str, account: str, password: str + ) -> None: # pylint: disable=too-many-arguments currName = operations.getComputerName() if currName.lower() == name.lower(): currDomain = operations.getDomainName() @@ -119,17 +127,21 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService): logger.info('Rebooting computer for activating new name {}'.format(name)) self.reboot() - def joinDomain( # pylint: disable=unused-argument, too-many-arguments - self, - name: str, - domain: str, - ou: str, - account: str, - password: str - ) -> None: + def joinDomain(self, name: str, custom: typing.Mapping[str, typing.Any]) -> None: versionData = operations.getWindowsVersion() versionInt = versionData[0] * 10 + versionData[1] - logger.debug('Starting joining domain {} with name {} (detected operating version: {})'.format(domain, name, versionData)) + + # Extract custom data + domain = custom.get('domain', '') + ou = custom.get('ou', '') + account = custom.get('account', '') + password = custom.get('password', '') + + logger.debug( + 'Starting joining domain {} with name {} (detected operating version: {})'.format( + domain, name, versionData + ) + ) # Accepts one step joinDomain, also remember XP is no more supported by # microsoft, but this also must works with it because will do a "multi # step" join @@ -139,17 +151,19 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService): logger.info('Using multiple step join because configuration requests to do so') self.multiStepJoin(name, domain, ou, account, password) - def preConnect(self, userName: str, protocol: str, ip: str, hostname: str, udsUserName: str) -> str: + def preConnect(self, userName: str, protocol: str, ip: str, hostname: str, udsUserName: str) -> str: logger.debug('Pre connect invoked') if protocol == 'rdp': # If connection is not using rdp, skip adding user # Well known SSID for Remote Desktop Users - groupName = win32security.LookupAccountSid(None, win32security.GetBinarySid(REMOTE_USERS_SID))[0] + groupName = win32security.LookupAccountSid(None, win32security.GetBinarySid(REMOTE_USERS_SID))[0] # type: ignore useraAlreadyInGroup = False resumeHandle = 0 while True: - users, _, resumeHandle = win32net.NetLocalGroupGetMembers(None, groupName, 1, resumeHandle, 32768) + users, _, resumeHandle = win32net.NetLocalGroupGetMembers( + None, groupName, 1, resumeHandle, 32768 # type: ignore + )[:3] if userName.lower() in [u['name'].lower() for u in users]: useraAlreadyInGroup = True break @@ -161,7 +175,7 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService): self._user = userName try: userSSID = win32security.LookupAccountName(None, userName)[0] - win32net.NetLocalGroupAddMembers(None, groupName, 0, [{'sid': userSSID}]) + win32net.NetLocalGroupAddMembers(None, groupName, 0, [{'sid': userSSID}]) # type: ignore except Exception as e: logger.error('Exception adding user to Remote Desktop Users: {}'.format(e)) else: @@ -178,7 +192,12 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService): # Compose packet for ov usernameBytes = username.encode() passwordBytes = password.encode() - packet = struct.pack('!I', len(usernameBytes)) + usernameBytes + struct.pack('!I', len(passwordBytes)) + passwordBytes + packet = ( + struct.pack('!I', len(usernameBytes)) + + usernameBytes + + struct.pack('!I', len(passwordBytes)) + + passwordBytes + ) # Send packet with username/password to ov pipe operations.writeToPipe("\\\\.\\pipe\\VDSMDPipe", packet, True) return 'done' @@ -187,14 +206,14 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService): logger.debug('Windows onLogout invoked: {}, {}'.format(userName, self._user)) try: p = win32security.GetBinarySid(REMOTE_USERS_SID) - groupName = win32security.LookupAccountSid(None, p)[0] + groupName = win32security.LookupAccountSid(None, p)[0] # type: ignore except Exception: logger.error('Exception getting Windows Group') return if self._user: try: - win32net.NetLocalGroupDelMembers(None, groupName, [self._user]) + win32net.NetLocalGroupDelMembers(None, groupName, [self._user]) # type: ignore except Exception as e: logger.error('Exception removing user from Remote Desktop Users: {}'.format(e)) @@ -203,18 +222,22 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService): Detect if windows is installing anything, so we can delay the execution of Service ''' try: - key = wreg.OpenKey(wreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\State') - data, _ = wreg.QueryValueEx(key, 'ImageState') + key = wreg.OpenKey(wreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\State') # type: ignore + data, _ = wreg.QueryValueEx(key, 'ImageState') # type: ignore logger.debug('State: %s', data) - return data != 'IMAGE_STATE_COMPLETE' # If ImageState is different of ImageStateComplete, there is something running on installation - except Exception: # If not found, means that no installation is running + return ( + data != 'IMAGE_STATE_COMPLETE' + ) # If ImageState is different of ImageStateComplete, there is something running on installation + except Exception: # If not found, means that no installation is running return False def SvcDoRun(self) -> None: # pylint: disable=too-many-statements, too-many-branches ''' Main service loop ''' - servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED, (self._svc_name_, '')) + servicemanager.LogMsg( + servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED, (self._svc_name_, '') + ) # call the CoInitialize to allow the registration to run in an other # thread @@ -239,7 +262,7 @@ class UDSActorSvc(win32serviceutil.ServiceFramework, CommonService): logger.info('Service stopped due to init') self.finish() win32event.WaitForSingleObject(self._hWaitStop, 5000) - return # Stop daemon if initializes told to do so + return # Stop daemon if initializes told to do so # Initialization is done, set machine to ready for UDS, communicate urls, etc... self.setReady() diff --git a/server/src/uds/osmanagers/LinuxOsManager/linux_ad_osmanager.py b/server/src/uds/osmanagers/LinuxOsManager/linux_ad_osmanager.py index 54a59d2aa..d6c067b06 100644 --- a/server/src/uds/osmanagers/LinuxOsManager/linux_ad_osmanager.py +++ b/server/src/uds/osmanagers/LinuxOsManager/linux_ad_osmanager.py @@ -185,12 +185,12 @@ class LinuxOsADManager(LinuxOsManager): return { 'action': 'rename_ad', 'name': userService.getName(), - 'ad': self._domain, - 'username': self._account, - 'password': self._password, - 'ou': self._ou, - 'isPersistent': self.isPersistent(), 'custom': { + 'domain': self._domain, + 'username': self._account, + 'password': self._password, + 'ou': self._ou, + 'isPersistent': self.isPersistent(), 'clientSoftware': self._clientSoftware, 'serverSoftware': self._serverSoftware, 'membershipSoftware': self._membershipSoftware, diff --git a/server/src/uds/osmanagers/LinuxOsManager/linux_freeipa_osmanager.py b/server/src/uds/osmanagers/LinuxOsManager/linux_freeipa_osmanager.py index df8a50e80..c0034d018 100644 --- a/server/src/uds/osmanagers/LinuxOsManager/linux_freeipa_osmanager.py +++ b/server/src/uds/osmanagers/LinuxOsManager/linux_freeipa_osmanager.py @@ -174,11 +174,11 @@ class LinuxOsFreeIPAManager(LinuxOsManager): return { 'action': 'rename_ad', 'name': userService.getName(), - 'ad': self._domain, - 'username': self._account, - 'password': self._password, - 'isPersistent': self.isPersistent(), 'custom': { + 'domain': self._domain, + 'username': self._account, + 'password': self._password, + 'isPersistent': self.isPersistent(), 'clientSoftware': self._clientSoftware, 'serverSoftware': self._serverSoftware, 'membershipSoftware': self._membershipSoftware, diff --git a/server/src/uds/osmanagers/LinuxOsManager/linux_osmanager.py b/server/src/uds/osmanagers/LinuxOsManager/linux_osmanager.py index 971a2f20a..7bc82248f 100644 --- a/server/src/uds/osmanagers/LinuxOsManager/linux_osmanager.py +++ b/server/src/uds/osmanagers/LinuxOsManager/linux_osmanager.py @@ -151,7 +151,7 @@ class LinuxOsManager(osmanagers.OSManager): def actorData( self, userService: 'UserService' ) -> typing.MutableMapping[str, typing.Any]: - return {'action': 'rename', 'name': userService.getName()} + return {'action': 'rename', 'name': userService.getName()} # No custom data def processUnused(self, userService: 'UserService') -> None: """ diff --git a/server/src/uds/osmanagers/LinuxOsManager/linux_randompass_osmanager.py b/server/src/uds/osmanagers/LinuxOsManager/linux_randompass_osmanager.py index e4e64c9ec..443aa4400 100644 --- a/server/src/uds/osmanagers/LinuxOsManager/linux_randompass_osmanager.py +++ b/server/src/uds/osmanagers/LinuxOsManager/linux_randompass_osmanager.py @@ -114,9 +114,18 @@ class LinuxRandomPassManager(LinuxOsManager): return { 'action': 'rename', 'name': userService.getName(), + + # Repeat data, to keep compat with old versions of Actor + # Will be removed in a couple of versions 'username': self._userAccount, 'password': '', # On linux, user password is not needed so we provide an empty one 'new_password': self.genPassword(userService), + + 'custom': { + 'username': self._userAccount, + 'password': '', # On linux, user password is not needed so we provide an empty one + 'new_password': self.genPassword(userService), + }, } def marshal(self) -> bytes: diff --git a/server/src/uds/osmanagers/WindowsOsManager/windows.py b/server/src/uds/osmanagers/WindowsOsManager/windows.py index a77ac074a..e8d277355 100644 --- a/server/src/uds/osmanagers/WindowsOsManager/windows.py +++ b/server/src/uds/osmanagers/WindowsOsManager/windows.py @@ -160,7 +160,7 @@ class WindowsOsManager(osmanagers.OSManager): def actorData( self, userService: 'UserService' ) -> typing.MutableMapping[str, typing.Any]: - return {'action': 'rename', 'name': userService.getName()} + return {'action': 'rename', 'name': userService.getName()} # No custom data def processUserPassword( self, userService: 'UserService', username: str, password: str diff --git a/server/src/uds/osmanagers/WindowsOsManager/windows_domain.py b/server/src/uds/osmanagers/WindowsOsManager/windows_domain.py index d5c228726..b3c4443c6 100644 --- a/server/src/uds/osmanagers/WindowsOsManager/windows_domain.py +++ b/server/src/uds/osmanagers/WindowsOsManager/windows_domain.py @@ -440,10 +440,20 @@ class WinDomainOsManager(WindowsOsManager): return { 'action': 'rename_ad', 'name': userService.getName(), + + # Repeat data, to keep compat with old versions of Actor + # Will be removed in a couple of versions 'ad': self._domain, 'ou': self._ou, 'username': self._account, 'password': self._password, + + 'custom': { + 'domain': self._domain, + 'ou': self._ou, + 'username': self._account, + 'password': self._password, + }, } def marshal(self) -> bytes: diff --git a/server/src/uds/osmanagers/WindowsOsManager/windows_random.py b/server/src/uds/osmanagers/WindowsOsManager/windows_random.py index b77a7a62e..fce60c83c 100644 --- a/server/src/uds/osmanagers/WindowsOsManager/windows_random.py +++ b/server/src/uds/osmanagers/WindowsOsManager/windows_random.py @@ -127,9 +127,18 @@ class WinRandomPassManager(WindowsOsManager): return { 'action': 'rename', 'name': userService.getName(), + + # Repeat data, to keep compat with old versions of Actor + # Will be removed in a couple of versions 'username': self._userAccount, 'password': self._password, 'new_password': self.genPassword(userService), + + 'custom': { + 'username': self._userAccount, + 'password': self._password, + 'new_password': self.genPassword(userService), + }, } def marshal(self) -> bytes: