forked from shaba/openuds
improving actor to better support unmanaged hosts
This commit is contained in:
parent
7a9dbc3edd
commit
7ec2197634
@ -246,6 +246,22 @@ class UDSServerApi(UDSApi):
|
||||
password=result['password']
|
||||
)
|
||||
|
||||
def notifyUnmanagedCallback(self, master_token: str, secret: str, interfaces: typing.Iterable[types.InterfaceInfoType], port: int) -> types.CertificateInfoType:
|
||||
payload = {
|
||||
'id': [{'mac': i.mac, 'ip': i.ip} for i in interfaces],
|
||||
'token': master_token,
|
||||
'secret': secret,
|
||||
'port': port
|
||||
}
|
||||
result = self._doPost('unmanaged', payload)
|
||||
|
||||
return types.CertificateInfoType(
|
||||
private_key=result['private_key'],
|
||||
server_certificate=result['server_certificate'],
|
||||
password=result['password']
|
||||
)
|
||||
|
||||
|
||||
def login(self, own_token: str, username: str) -> types.LoginResultInfoType:
|
||||
if not own_token:
|
||||
return types.LoginResultInfoType(
|
||||
|
@ -58,7 +58,8 @@ from .http import clients_pool, server, cert
|
||||
class CommonService: # pylint: disable=too-many-instance-attributes
|
||||
_isAlive: bool = True
|
||||
_rebootRequested: bool = False
|
||||
_loggedIn = False
|
||||
_loggedIn: bool = False
|
||||
_initialized: bool = False
|
||||
|
||||
_cfg: types.ActorConfigurationType
|
||||
_api: rest.UDSServerApi
|
||||
@ -229,24 +230,41 @@ class CommonService: # pylint: disable=too-many-instance-attributes
|
||||
return True
|
||||
|
||||
def initializeUnmanaged(self) -> bool:
|
||||
# Notify UDS about my callback
|
||||
self.getInterfaces() # Ensure we have interfaces
|
||||
if self._cfg.master_token:
|
||||
try:
|
||||
self._certificate = self._api.notifyUnmanagedCallback(self._cfg.master_token, self._secret, self._interfaces, rest.LISTEN_PORT)
|
||||
except Exception as e:
|
||||
logger.error('Couuld not notify unmanaged callback: %s', e)
|
||||
|
||||
return True
|
||||
|
||||
def getInterfaces(self) -> None:
|
||||
if self._interfaces:
|
||||
return
|
||||
|
||||
while self._isAlive:
|
||||
self._interfaces = list(platform.operations.getNetworkInfo())
|
||||
if self._interfaces:
|
||||
break
|
||||
self.doWait(5000)
|
||||
|
||||
def initialize(self) -> bool:
|
||||
if 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
|
||||
|
||||
# Force time sync, just in case...
|
||||
if self.isManaged():
|
||||
platform.operations.forceTimeSync()
|
||||
|
||||
# Wait for Broker to be ready
|
||||
while self._isAlive:
|
||||
if not self._interfaces:
|
||||
self._interfaces = list(platform.operations.getNetworkInfo())
|
||||
if not self._interfaces: # Wait a bit for interfaces to get initialized... (has valid IPs)
|
||||
self.doWait(5000)
|
||||
continue
|
||||
# Ensure we have intefaces...
|
||||
self.getInterfaces()
|
||||
|
||||
while self._isAlive:
|
||||
try:
|
||||
# If master token is present, initialize and get configuration data
|
||||
if self._cfg.master_token:
|
||||
@ -284,6 +302,9 @@ class CommonService: # pylint: disable=too-many-instance-attributes
|
||||
|
||||
return self.configureMachine()
|
||||
|
||||
def uninitialize(self):
|
||||
self._initialized = False
|
||||
|
||||
def finish(self) -> None:
|
||||
if self._http:
|
||||
self._http.stop()
|
||||
@ -398,6 +419,9 @@ class CommonService: # pylint: disable=too-many-instance-attributes
|
||||
|
||||
self.onLogout(username)
|
||||
|
||||
if not self.isManaged():
|
||||
self.uninitialize()
|
||||
|
||||
self._cfg = self._cfg._replace(own_token=None) # Ensures assigned token is cleared
|
||||
|
||||
# ****************************************
|
||||
|
@ -53,7 +53,8 @@ from uds.core.util.config import GlobalConfig
|
||||
from ..handlers import Handler, AccessDenied, RequestError
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
# if typing.TYPE_CHECKING:
|
||||
if typing.TYPE_CHECKING:
|
||||
from uds.core import services
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -308,7 +309,7 @@ class BaseReadyChange(ActorV3Action):
|
||||
osManager.toReady(userService)
|
||||
userServiceManager().notifyReadyFromOsManager(userService, '')
|
||||
|
||||
# Generates a certificate and send it to client. Currently, we do not store it locally
|
||||
# Generates a certificate and send it to client.
|
||||
privateKey, cert, password = certs.selfSignedCert(self._params['ip'])
|
||||
# Store certificate with userService
|
||||
userService.setProperty('cert', cert)
|
||||
@ -321,7 +322,6 @@ class ChangeIp(BaseReadyChange):
|
||||
"""
|
||||
Processses IP Change. Needs to be "last" on a lead to be auto added to list of available methods
|
||||
"""
|
||||
|
||||
name = 'changeip'
|
||||
|
||||
class Ready(BaseReadyChange):
|
||||
@ -449,6 +449,55 @@ class Ticket(ActorV3Action):
|
||||
except TicketStore.DoesNotExists:
|
||||
return ActorV3Action.actorResult(error='Invalid ticket')
|
||||
|
||||
class Unmanaged(ActorV3Action):
|
||||
name = 'unmanaged'
|
||||
|
||||
def action(self) -> typing.MutableMapping[str, typing.Any]:
|
||||
"""
|
||||
Changeip method expect a json POST with this fields:
|
||||
* id: List[dict] -> List of dictionary containing ip and mac:
|
||||
* token: str -> Valid Actor "master_token" (if invalid, will return an error).
|
||||
* secret: Secret for commsUrl for actor
|
||||
* port: port of the listener (normally 43910)
|
||||
|
||||
This method will also regenerater the public-private key pair for client, that will be needed for the new ip
|
||||
|
||||
Returns: {
|
||||
private_key: str -> Generated private key, PEM
|
||||
server_certificate: str -> Generated public key, PEM
|
||||
}
|
||||
"""
|
||||
logger.debug('Args: %s, Params: %s', self._args, self._params)
|
||||
|
||||
try:
|
||||
dbService: Service = Service.objects.get(token=self._params['token'])
|
||||
service: 'services.Service' = dbService.getInstance()
|
||||
except Exception:
|
||||
return ActorV3Action.actorResult(error='Invalid token')
|
||||
|
||||
# Build the possible ids and ask service if it recognizes any of it
|
||||
# If not recognized, will generate anyway the certificate, but will not be saved
|
||||
idsList = [x['ip'] for x in self._params['id']] + [x['mac'] for x in self._params['id']][:10]
|
||||
validId: typing.Optional[str] = service.getValidId(idsList)
|
||||
ip: str
|
||||
try:
|
||||
ip = next(x['ip'] for x in self._params['id'] if x['ip'] == validId or x['mac'] == validId)
|
||||
except StopIteration:
|
||||
ip = self._params['id'][0]['ip'] # Get first IP if no valid ip found
|
||||
|
||||
# Generates a certificate and send it to client.
|
||||
privateKey, cert, password = certs.selfSignedCert(ip)
|
||||
cert = {'private_key': privateKey, 'server_certificate': cert, 'password': password}
|
||||
# Store certificate, secret & port with service if validId
|
||||
if validId:
|
||||
service.storeIdInfo(validId, {
|
||||
'cert': cert,
|
||||
'secret': self._params['secret'],
|
||||
'port': int(self._params['port'])
|
||||
})
|
||||
|
||||
return ActorV3Action.actorResult(cert)
|
||||
|
||||
class Notify(ActorV3Action):
|
||||
name = 'notify'
|
||||
|
||||
@ -476,3 +525,4 @@ class Notify(ActorV3Action):
|
||||
incFailedIp(self._request.ip) # pylint: disable=protected-access
|
||||
|
||||
raise AccessDenied('Access denied')
|
||||
|
||||
|
@ -289,7 +289,21 @@ class Service(Module):
|
||||
By default, services does not have a token
|
||||
"""
|
||||
return None
|
||||
|
||||
|
||||
def getValidId(self, idsList: typing.Iterable[str]) -> typing.Optional[str]:
|
||||
return None
|
||||
|
||||
def storeIdInfo(self, id: str, data: typing.Any) -> None:
|
||||
self.storage.putPickle('__nfo_' + id, data)
|
||||
|
||||
def recoverIdInfo(self, id: str, delete: bool = False) -> typing.Any:
|
||||
# recovers the information
|
||||
value = self.storage.getPickle('__nfo_' + id)
|
||||
if value and delete:
|
||||
self.storage.delete('__nfo_' + id)
|
||||
return value
|
||||
|
||||
|
||||
def doLog(self, level: int, message: str) -> None:
|
||||
"""
|
||||
Logs a message with requested level associated with this service
|
||||
|
Loading…
x
Reference in New Issue
Block a user