mirror of
https://github.com/dkmstr/openuds.git
synced 2025-01-08 21:18:00 +03:00
Merge pull request #81 from Future998/linux-client-DC
Adding new Linux OS Domain Managers
This commit is contained in:
commit
98f79f83f1
@ -3,6 +3,7 @@ Description=UDS Broker actor
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
KillMode=mixed
|
||||
Type=simple
|
||||
User=root
|
||||
Group=root
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2014-2023 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -27,6 +27,7 @@
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
@author: Alexander Burmatov, thatman at altlinux dot org
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import configparser
|
||||
@ -186,10 +187,67 @@ def renameComputer(newName: str) -> bool:
|
||||
rename(newName)
|
||||
return True # Always reboot right now. Not much slower but much more convenient
|
||||
|
||||
def joinDomain( # pylint: disable=unused-argument, too-many-arguments
|
||||
name: str,
|
||||
domain: str,
|
||||
ou: str,
|
||||
account: str,
|
||||
password: str,
|
||||
client_software: str,
|
||||
server_software: str,
|
||||
membership_software: str,
|
||||
ssl: bool,
|
||||
automatic_id_mapping: bool
|
||||
) -> None:
|
||||
if server_software == 'ipa':
|
||||
try:
|
||||
hostname = getComputerName() + domain[domain.index('.'):]
|
||||
command = f'hostnamectl set-hostname {hostname}'
|
||||
subprocess.run(command, shell=True)
|
||||
except Exception as e:
|
||||
logger.error(f'Error set hostname for freeeipa: {e}')
|
||||
try:
|
||||
command = f'realm join -U {account} '
|
||||
if client_software and client_software != 'automatically':
|
||||
command += f'--client-software={client_software} '
|
||||
if server_software:
|
||||
command += f'--server-software={server_software} '
|
||||
if membership_software and membership_software != 'automatically':
|
||||
command += f'--membership-software={membership_software} '
|
||||
if ou and server_software !='ipa':
|
||||
command += f'--computer-ou="{ou}" '
|
||||
if ssl == 'y':
|
||||
command += '--use-ldaps '
|
||||
if automatic_id_mapping == 'n':
|
||||
command += '--automatic-id-mapping=no '
|
||||
command += domain
|
||||
subprocess.run(command, input=password.encode(), shell=True)
|
||||
except Exception as e:
|
||||
logger.error(f'Error join machine to domain {name}: {e}')
|
||||
|
||||
def joinDomain(domain: str, ou: str, account: str, password: str, executeInOneStep: bool = False):
|
||||
pass
|
||||
|
||||
def leaveDomain(
|
||||
domain: str,
|
||||
account: str,
|
||||
password: str,
|
||||
client_software: str,
|
||||
server_software: str,
|
||||
) -> None:
|
||||
if server_software == 'ipa':
|
||||
try:
|
||||
command = f'hostnamectl set-hostname {getComputerName()}'
|
||||
subprocess.run(command, shell=True)
|
||||
except Exception as e:
|
||||
logger.error(f'Error set hostname for leave freeeipa domain: {e}')
|
||||
try:
|
||||
command = f'realm leave -U {account} '
|
||||
if client_software and client_software != 'automatically':
|
||||
command += f'--client-software={client_software} '
|
||||
if server_software:
|
||||
command += f'--server-software={server_software} '
|
||||
command += domain
|
||||
subprocess.run(command, input=password.encode(), shell=True)
|
||||
except Exception as e:
|
||||
logger.error(f'Error leave machine from domain {domain}: {e}')
|
||||
|
||||
def changeUserPassword(
|
||||
user: str, oldPassword: str, newPassword: str
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2014-2023 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -28,10 +28,12 @@
|
||||
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
@author: Alexander Burmatov, thatman at altlinux dot org
|
||||
'''
|
||||
import signal
|
||||
|
||||
from . import daemon
|
||||
from . import operations
|
||||
|
||||
from ..log import logger
|
||||
from ..service import CommonService
|
||||
@ -60,10 +62,42 @@ class UDSActorSvc(daemon.Daemon, CommonService):
|
||||
domain: str,
|
||||
ou: str,
|
||||
account: str,
|
||||
password: str
|
||||
password: str,
|
||||
client_software: str,
|
||||
server_software: str,
|
||||
membership_software: str,
|
||||
ssl: bool,
|
||||
automatic_id_mapping: bool
|
||||
) -> None:
|
||||
logger.info('Join domain is not supported on linux platforms right now. Just renaming.')
|
||||
self.rename(name)
|
||||
logger.debug(f'Starting joining domain {domain} with name {name}')
|
||||
operations.joinDomain(name, \
|
||||
domain, \
|
||||
ou, \
|
||||
account, \
|
||||
password, \
|
||||
client_software, \
|
||||
server_software, \
|
||||
membership_software, \
|
||||
ssl, \
|
||||
automatic_id_mapping
|
||||
)
|
||||
|
||||
def finish(self) -> None:
|
||||
try:
|
||||
if self._cfg.config and self._cfg.config.os:
|
||||
osData = self._cfg.config.os
|
||||
if osData.action == 'rename_ad' and osData.isPersistent == 'n' :
|
||||
operations.leaveDomain(
|
||||
osData.ad or '',
|
||||
osData.username or '',
|
||||
osData.password or '',
|
||||
osData.clientSoftware or '',
|
||||
osData.serverSoftware or '',
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f'Got exception operating machine: {e}')
|
||||
super().finish()
|
||||
|
||||
def run(self) -> None:
|
||||
logger.debug('Running Daemon: {}'.format(self._isAlive))
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2019-2021 Virtual Cable S.L.U.
|
||||
# Copyright (c) 2019-2023 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -27,6 +27,7 @@
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
@author: Alexander Burmatov, thatman at altlinux dot org
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
import warnings
|
||||
@ -293,6 +294,12 @@ class UDSServerApi(UDSApi):
|
||||
new_password=os.get('new_password'),
|
||||
ad=os.get('ad'),
|
||||
ou=os.get('ou'),
|
||||
clientSoftware = os.get('clientSoftware'),
|
||||
serverSoftware = os.get('serverSoftware'),
|
||||
membershipSoftware = os.get('membershipSoftware'),
|
||||
ssl = os.get('ssl'),
|
||||
automaticIdMapping = os.get('automaticIdMapping'),
|
||||
isPersistent = os.get('isPersistent'),
|
||||
)
|
||||
if r['os']
|
||||
else None,
|
||||
|
@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (c) 2014-2019 Virtual Cable S.L.
|
||||
# Copyright (c) 2014-2023 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -27,6 +27,7 @@
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
'''
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
@author: Alexander Burmatov, thatman at altlinux dot org
|
||||
'''
|
||||
# pylint: disable=invalid-name
|
||||
|
||||
@ -194,10 +195,15 @@ class CommonService: # pylint: disable=too-many-instance-attributes
|
||||
|
||||
# Cleans sensible data
|
||||
if self._cfg.config:
|
||||
self._cfg = self._cfg._replace(
|
||||
config=self._cfg.config._replace(os=None), data=None
|
||||
)
|
||||
platform.store.writeConfig(self._cfg)
|
||||
try:
|
||||
isPersistent = self._cfg.config.os.isPersistent == 'y'
|
||||
except:
|
||||
isPersistent = True
|
||||
if isPersistent:
|
||||
self._cfg = self._cfg._replace(
|
||||
config=self._cfg.config._replace(os=None), data=None
|
||||
)
|
||||
platform.store.writeConfig(self._cfg)
|
||||
|
||||
logger.info('Service ready')
|
||||
|
||||
@ -235,13 +241,27 @@ class CommonService: # pylint: disable=too-many-instance-attributes
|
||||
osData.new_password,
|
||||
)
|
||||
elif osData.action == 'rename_ad':
|
||||
self.joinDomain(
|
||||
osData.name,
|
||||
osData.ad or '',
|
||||
osData.ou or '',
|
||||
osData.username or '',
|
||||
osData.password or '',
|
||||
)
|
||||
if not osData.serverSoftware:
|
||||
self.joinDomain(
|
||||
osData.name,
|
||||
osData.ad or '',
|
||||
osData.ou or '',
|
||||
osData.username or '',
|
||||
osData.password or '',
|
||||
)
|
||||
else:
|
||||
self.joinDomain(
|
||||
osData.name,
|
||||
osData.ad or '',
|
||||
osData.ou or '',
|
||||
osData.username or '',
|
||||
osData.password or '',
|
||||
osData.clientSoftware or '',
|
||||
osData.serverSoftware or '',
|
||||
osData.membershipSoftware or '',
|
||||
osData.ssl or '',
|
||||
osData.automaticIdMapping or ''
|
||||
)
|
||||
|
||||
if self._rebootRequested:
|
||||
try:
|
||||
|
@ -24,6 +24,12 @@ class ActorOsConfigurationType(typing.NamedTuple):
|
||||
new_password: typing.Optional[str] = None
|
||||
ad: typing.Optional[str] = None
|
||||
ou: typing.Optional[str] = None
|
||||
clientSoftware: typing.Optional[str] = None
|
||||
serverSoftware: typing.Optional[str] = None
|
||||
membershipSoftware: typing.Optional[str] = None
|
||||
ssl: typing.Optional[str] = None
|
||||
automaticIdMapping: typing.Optional[str] = None
|
||||
isPersistent: typing.Optional[str] = None
|
||||
|
||||
class ActorDataConfigurationType(typing.NamedTuple):
|
||||
unique_id: typing.Optional[str] = None
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2022 Virtual Cable S.L.U.
|
||||
# Copyright (c) 2012-2023 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -29,6 +29,7 @@
|
||||
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
@author: Alexander Burmatov, thatman at altlinux dot org
|
||||
"""
|
||||
import typing
|
||||
|
||||
@ -117,6 +118,12 @@ class OSManager(Module):
|
||||
'ou': 'ou' # or '' if default ou
|
||||
'username': 'userwithaddmachineperms@domain.xxxx'
|
||||
'password': 'passwordForTheUserWithPerms',
|
||||
'clientSoftware': 'sssd' or 'winbind' or 'automatically' if linux os manager,
|
||||
'serverSoftware': 'active-directory' or 'ipa' if linux os manager,
|
||||
'membershipSoftware': 'samba' or 'adcli' or 'automatically' if linux os manager,
|
||||
'ssl': 'n' or 'y' if linux os manager,
|
||||
'automaticIdMapping': 'n' or 'y' if linux os manager,
|
||||
'isPersistent': 'n' or 'y' if linux os manager,
|
||||
}
|
||||
* rename vm, do NOT ADD to AD, and change password for an user
|
||||
{
|
||||
|
@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2022 Virtual Cable S.L.U.
|
||||
# Copyright (c) 2012-2023 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
@ -29,6 +29,7 @@
|
||||
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
@author: Alexander Burmatov, thatman at altlinux dot org
|
||||
"""
|
||||
import os.path
|
||||
import typing
|
||||
@ -41,6 +42,8 @@ from uds.core import VERSION
|
||||
|
||||
from .linux_osmanager import LinuxOsManager
|
||||
from .linux_randompass_osmanager import LinuxRandomPassManager
|
||||
from .linux_ad_osmanager import LinuxOsADManager
|
||||
from .linux_freeipa_osmanager import LinuxOsFreeIPAManager
|
||||
|
||||
downloadsManager().registerDownloadable(
|
||||
f'udsactor_{VERSION}_all.deb',
|
||||
|
255
server/src/uds/osmanagers/LinuxOsManager/linux_ad_osmanager.py
Normal file
255
server/src/uds/osmanagers/LinuxOsManager/linux_ad_osmanager.py
Normal file
@ -0,0 +1,255 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2023 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
@author: Alexander Burmatov, thatman at altlinux dot org
|
||||
"""
|
||||
import codecs
|
||||
import logging
|
||||
import typing
|
||||
|
||||
from django.utils.translation import gettext_noop as _, gettext_lazy
|
||||
|
||||
from uds.core.ui import gui
|
||||
from uds.core import exceptions
|
||||
from uds.core.managers import cryptoManager
|
||||
|
||||
from .linux_osmanager import LinuxOsManager
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from uds.models.user_service import UserService
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LinuxOsADManager(LinuxOsManager):
|
||||
typeName = _('Linux OS Active Directory Manager')
|
||||
typeType = 'LinuxADManager'
|
||||
typeDescription = _('Os Manager to control Linux virtual machines with active directory')
|
||||
iconFile = 'losmanager.png'
|
||||
|
||||
domain = gui.TextField(
|
||||
length=64,
|
||||
label=_('Domain'),
|
||||
order=1,
|
||||
tooltip=_(
|
||||
'Domain to join machines to (use FQDN form, Netbios name not supported for most operations)'
|
||||
),
|
||||
required=True,
|
||||
)
|
||||
account = gui.TextField(
|
||||
length=64,
|
||||
label=_('Account'),
|
||||
order=2,
|
||||
tooltip=_('Account with rights to add machines to domain'),
|
||||
required=True,
|
||||
)
|
||||
password = gui.PasswordField(
|
||||
length=64,
|
||||
label=_('Password'),
|
||||
order=3,
|
||||
tooltip=_('Password of the account'),
|
||||
required=True,
|
||||
)
|
||||
ou = gui.TextField(
|
||||
length=256,
|
||||
label=_('OU'),
|
||||
order=4,
|
||||
tooltip=_(
|
||||
'Organizational unit where to add machines in domain (check it before using it). i.e.: ou=My Machines,dc=mydomain,dc=local'
|
||||
),
|
||||
)
|
||||
clientSoftware = gui.ChoiceField(
|
||||
label=_('Client software'),
|
||||
order=5,
|
||||
tooltip=_('Use specific client software'),
|
||||
values=[
|
||||
{'id': 'automatically', 'text': gettext_lazy('Automatically')},
|
||||
{'id': 'sssd', 'text': gettext_lazy('SSSD')},
|
||||
{'id': 'winbind', 'text': gettext_lazy('Winbind')},
|
||||
],
|
||||
tab=_('Advanced'),
|
||||
defvalue='automatically',
|
||||
)
|
||||
membershipSoftware = gui.ChoiceField(
|
||||
label=_('Membership software'),
|
||||
order=6,
|
||||
tooltip=_('Use specific membership software'),
|
||||
values=[
|
||||
{'id': 'automatically', 'text': gettext_lazy('Automatically')},
|
||||
{'id': 'samba', 'text': gettext_lazy('Samba')},
|
||||
{'id': 'adcli', 'text': gettext_lazy('adcli')},
|
||||
],
|
||||
tab=_('Advanced'),
|
||||
defvalue='automatically',
|
||||
)
|
||||
removeOnExit = gui.CheckBoxField(
|
||||
label=_('Machine clean'),
|
||||
order=7,
|
||||
tooltip=_(
|
||||
'If checked, UDS will try to remove the machine from the domain USING the provided credentials'
|
||||
),
|
||||
tab=_('Advanced'),
|
||||
defvalue=gui.TRUE,
|
||||
)
|
||||
ssl = gui.CheckBoxField(
|
||||
label=_('Use SSL'),
|
||||
order=8,
|
||||
tooltip=_('If checked, a ssl connection to Active Directory will be used'),
|
||||
tab=_('Advanced'),
|
||||
defvalue=gui.TRUE,
|
||||
)
|
||||
automaticIdMapping = gui.CheckBoxField(
|
||||
label=_('Automatic ID mapping'),
|
||||
order=9,
|
||||
tooltip=_('If checked, automatic ID mapping'),
|
||||
tab=_('Advanced'),
|
||||
defvalue=gui.TRUE,
|
||||
)
|
||||
|
||||
# Inherits base "onLogout"
|
||||
onLogout = LinuxOsManager.onLogout
|
||||
idle = LinuxOsManager.idle
|
||||
deadLine = LinuxOsManager.deadLine
|
||||
|
||||
_domain: str
|
||||
_ou: str
|
||||
_account: str
|
||||
_password: str
|
||||
_removeOnExit: str
|
||||
_ssl: str
|
||||
_automaticIdMapping: str
|
||||
_clientSoftware: str
|
||||
_serverSoftware: str
|
||||
_membershipSoftware: str
|
||||
|
||||
def __init__(self, environment, values):
|
||||
super(LinuxOsADManager, self).__init__(environment, values)
|
||||
if values:
|
||||
if values['domain'] == '':
|
||||
raise exceptions.ValidationError(_('Must provide a domain!'))
|
||||
if values['account'] == '':
|
||||
raise exceptions.ValidationError(
|
||||
_('Must provide an account to add machines to domain!')
|
||||
)
|
||||
if values['password'] == '':
|
||||
raise exceptions.ValidationError(
|
||||
_('Must provide a password for the account!')
|
||||
)
|
||||
self._domain = values['domain']
|
||||
self._account = values['account']
|
||||
self._password = values['password']
|
||||
self._ou = values['ou'].strip()
|
||||
self._clientSoftware = values['clientSoftware']
|
||||
self._serverSoftware = 'active-directory'
|
||||
self._membershipSoftware = values['membershipSoftware']
|
||||
self._removeOnExit = 'y' if values['removeOnExit'] else 'n'
|
||||
self._ssl = 'y' if values['ssl'] else 'n'
|
||||
self._automaticIdMapping = 'y' if values['automaticIdMapping'] else 'n'
|
||||
else:
|
||||
self._domain = ''
|
||||
self._account = ''
|
||||
self._password = '' # nosec: no encoded password
|
||||
self._ou = ''
|
||||
self._clientSoftware = ''
|
||||
self._serverSoftware = 'active-directory'
|
||||
self._membershipSoftware = ''
|
||||
self._removeOnExit = 'n'
|
||||
self._ssl = 'n'
|
||||
self._automaticIdMapping = 'n'
|
||||
|
||||
def actorData(
|
||||
self, userService: 'UserService'
|
||||
) -> typing.MutableMapping[str, typing.Any]:
|
||||
return {
|
||||
'action': 'rename_ad',
|
||||
'name': userService.getName(),
|
||||
'ad': self._domain,
|
||||
'username': self._account,
|
||||
'password': self._password,
|
||||
'ou': self._ou,
|
||||
'clientSoftware': self._clientSoftware,
|
||||
'serverSoftware': self._serverSoftware,
|
||||
'membershipSoftware': self._membershipSoftware,
|
||||
'ssl': self._ssl,
|
||||
'automaticIdMapping': self._automaticIdMapping,
|
||||
'isPersistent': 'y' if self.isPersistent() else 'n',
|
||||
}
|
||||
|
||||
def marshal(self) -> bytes:
|
||||
"""
|
||||
Serializes the os manager data so we can store it in database
|
||||
"""
|
||||
base = super().marshal()
|
||||
return '\t'.join(
|
||||
[
|
||||
'v1',
|
||||
self._domain,
|
||||
self._account,
|
||||
cryptoManager().encrypt(self._password),
|
||||
self._ou,
|
||||
self._clientSoftware,
|
||||
self._serverSoftware,
|
||||
self._membershipSoftware,
|
||||
self._removeOnExit,
|
||||
self._ssl,
|
||||
self._automaticIdMapping,
|
||||
codecs.encode(base, 'hex').decode(),
|
||||
]
|
||||
).encode('utf8')
|
||||
|
||||
def unmarshal(self, data: bytes) -> None:
|
||||
values = data.decode('utf8').split('\t')
|
||||
if values[0] in ('v1'):
|
||||
self._domain = values[1]
|
||||
self._account = values[2]
|
||||
self._password = cryptoManager().decrypt(values[3])
|
||||
self._ou = values[4]
|
||||
self._clientSoftware = values[5]
|
||||
self._serverSoftware = values[6]
|
||||
self._membershipSoftware = values[7]
|
||||
self._removeOnExit = values[8]
|
||||
self._ssl = values[9]
|
||||
self._automaticIdMapping = values[10]
|
||||
super().unmarshal(codecs.decode(values[11].encode(), 'hex'))
|
||||
|
||||
def valuesDict(self) -> gui.ValuesDictType:
|
||||
dct = super().valuesDict()
|
||||
dct['domain'] = self._domain
|
||||
dct['account'] = self._account
|
||||
dct['password'] = self._password
|
||||
dct['ou'] = self._ou
|
||||
dct['clientSoftware'] = self._clientSoftware
|
||||
dct['serverSoftware'] = self._serverSoftware
|
||||
dct['membershipSoftware'] = self._membershipSoftware
|
||||
dct['removeOnExit'] = self._removeOnExit == 'y'
|
||||
dct['ssl'] = self._ssl == 'y'
|
||||
dct['automaticIdMapping'] = self._automaticIdMapping == 'y'
|
||||
return dct
|
@ -0,0 +1,240 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2023 Virtual Cable S.L.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
@author: Alexander Burmatov, thatman at altlinux dot org
|
||||
"""
|
||||
import codecs
|
||||
import logging
|
||||
import typing
|
||||
|
||||
from django.utils.translation import gettext_noop as _, gettext_lazy
|
||||
|
||||
from uds.core.ui import gui
|
||||
from uds.core import exceptions
|
||||
from uds.core.managers import cryptoManager
|
||||
|
||||
from .linux_osmanager import LinuxOsManager
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from uds.models.user_service import UserService
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LinuxOsFreeIPAManager(LinuxOsManager):
|
||||
typeName = _('Linux OS FreeIPA Manager')
|
||||
typeType = 'LinuxFreeIPAManager'
|
||||
typeDescription = _('Os Manager to control Linux virtual machines with FreeIPA')
|
||||
iconFile = 'losmanager.png'
|
||||
|
||||
domain = gui.TextField(
|
||||
length=64,
|
||||
label=_('Domain'),
|
||||
order=1,
|
||||
tooltip=_(
|
||||
'Domain to join machines to (use FQDN form, Netbios name not supported for most operations)'
|
||||
),
|
||||
required=True,
|
||||
)
|
||||
account = gui.TextField(
|
||||
length=64,
|
||||
label=_('Account'),
|
||||
order=2,
|
||||
tooltip=_('Account with rights to add machines to domain'),
|
||||
required=True,
|
||||
)
|
||||
password = gui.PasswordField(
|
||||
length=64,
|
||||
label=_('Password'),
|
||||
order=3,
|
||||
tooltip=_('Password of the account'),
|
||||
required=True,
|
||||
)
|
||||
clientSoftware = gui.ChoiceField(
|
||||
label=_('Client software'),
|
||||
order=4,
|
||||
tooltip=_('Use specific client software'),
|
||||
values=[
|
||||
{'id': 'automatically', 'text': gettext_lazy('Automatically')},
|
||||
{'id': 'sssd', 'text': gettext_lazy('SSSD')},
|
||||
{'id': 'winbind', 'text': gettext_lazy('Winbind')},
|
||||
],
|
||||
tab=_('Advanced'),
|
||||
defvalue='automatically',
|
||||
)
|
||||
membershipSoftware = gui.ChoiceField(
|
||||
label=_('Membership software'),
|
||||
order=5,
|
||||
tooltip=_('Use specific membership software'),
|
||||
values=[
|
||||
{'id': 'automatically', 'text': gettext_lazy('Automatically')},
|
||||
{'id': 'samba', 'text': gettext_lazy('Samba')},
|
||||
{'id': 'adcli', 'text': gettext_lazy('adcli')},
|
||||
],
|
||||
tab=_('Advanced'),
|
||||
defvalue='automatically',
|
||||
)
|
||||
removeOnExit = gui.CheckBoxField(
|
||||
label=_('Machine clean'),
|
||||
order=6,
|
||||
tooltip=_(
|
||||
'If checked, UDS will try to remove the machine from the domain USING the provided credentials'
|
||||
),
|
||||
tab=_('Advanced'),
|
||||
defvalue=gui.TRUE,
|
||||
)
|
||||
ssl = gui.CheckBoxField(
|
||||
label=_('Use SSL'),
|
||||
order=7,
|
||||
tooltip=_('If checked, a ssl connection to Active Directory will be used'),
|
||||
tab=_('Advanced'),
|
||||
defvalue=gui.TRUE,
|
||||
)
|
||||
automaticIdMapping = gui.CheckBoxField(
|
||||
label=_('Automatic ID mapping'),
|
||||
order=8,
|
||||
tooltip=_('If checked, automatic ID mapping'),
|
||||
tab=_('Advanced'),
|
||||
defvalue=gui.TRUE,
|
||||
)
|
||||
|
||||
# Inherits base "onLogout"
|
||||
onLogout = LinuxOsManager.onLogout
|
||||
idle = LinuxOsManager.idle
|
||||
deadLine = LinuxOsManager.deadLine
|
||||
|
||||
_domain: str
|
||||
_account: str
|
||||
_password: str
|
||||
_removeOnExit: str
|
||||
_ssl: str
|
||||
_automaticIdMapping: str
|
||||
_clientSoftware: str
|
||||
_serverSoftware: str
|
||||
_membershipSoftware: str
|
||||
|
||||
def __init__(self, environment, values):
|
||||
super(LinuxOsFreeIPAManager, self).__init__(environment, values)
|
||||
if values:
|
||||
if values['domain'] == '':
|
||||
raise exceptions.ValidationError(_('Must provide a domain!'))
|
||||
if values['account'] == '':
|
||||
raise exceptions.ValidationError(
|
||||
_('Must provide an account to add machines to domain!')
|
||||
)
|
||||
if values['password'] == '':
|
||||
raise exceptions.ValidationError(
|
||||
_('Must provide a password for the account!')
|
||||
)
|
||||
self._domain = values['domain']
|
||||
self._account = values['account']
|
||||
self._password = values['password']
|
||||
self._clientSoftware = values['clientSoftware']
|
||||
self._serverSoftware = 'ipa'
|
||||
self._membershipSoftware = values['membershipSoftware']
|
||||
self._removeOnExit = 'y' if values['removeOnExit'] else 'n'
|
||||
self._ssl = 'y' if values['ssl'] else 'n'
|
||||
self._automaticIdMapping = 'y' if values['automaticIdMapping'] else 'n'
|
||||
else:
|
||||
self._domain = ''
|
||||
self._account = ''
|
||||
self._password = '' # nosec: no encoded password
|
||||
self._clientSoftware = ''
|
||||
self._serverSoftware = 'ipa'
|
||||
self._membershipSoftware = ''
|
||||
self._removeOnExit = 'n'
|
||||
self._ssl = 'n'
|
||||
self._automaticIdMapping = 'n'
|
||||
|
||||
def actorData(
|
||||
self, userService: 'UserService'
|
||||
) -> typing.MutableMapping[str, typing.Any]:
|
||||
return {
|
||||
'action': 'rename_ad',
|
||||
'name': userService.getName(),
|
||||
'ad': self._domain,
|
||||
'username': self._account,
|
||||
'password': self._password,
|
||||
'clientSoftware': self._clientSoftware,
|
||||
'serverSoftware': self._serverSoftware,
|
||||
'membershipSoftware': self._membershipSoftware,
|
||||
'ssl': self._ssl,
|
||||
'automaticIdMapping': self._automaticIdMapping,
|
||||
'isPersistent': 'y' if self.isPersistent() else 'n',
|
||||
}
|
||||
|
||||
def marshal(self) -> bytes:
|
||||
"""
|
||||
Serializes the os manager data so we can store it in database
|
||||
"""
|
||||
base = super().marshal()
|
||||
return '\t'.join(
|
||||
[
|
||||
'v1',
|
||||
self._domain,
|
||||
self._account,
|
||||
cryptoManager().encrypt(self._password),
|
||||
self._clientSoftware,
|
||||
self._serverSoftware,
|
||||
self._membershipSoftware,
|
||||
self._removeOnExit,
|
||||
self._ssl,
|
||||
self._automaticIdMapping,
|
||||
codecs.encode(base, 'hex').decode(),
|
||||
]
|
||||
).encode('utf8')
|
||||
|
||||
def unmarshal(self, data: bytes) -> None:
|
||||
values = data.decode('utf8').split('\t')
|
||||
if values[0] in ('v1'):
|
||||
self._domain = values[1]
|
||||
self._account = values[2]
|
||||
self._password = cryptoManager().decrypt(values[3])
|
||||
self._clientSoftware = values[4]
|
||||
self._serverSoftware = values[5]
|
||||
self._membershipSoftware = values[6]
|
||||
self._removeOnExit = values[7]
|
||||
self._ssl = values[8]
|
||||
self._automaticIdMapping = values[9]
|
||||
super().unmarshal(codecs.decode(values[10].encode(), 'hex'))
|
||||
|
||||
def valuesDict(self) -> gui.ValuesDictType:
|
||||
dct = super().valuesDict()
|
||||
dct['domain'] = self._domain
|
||||
dct['account'] = self._account
|
||||
dct['password'] = self._password
|
||||
dct['clientSoftware'] = self._clientSoftware
|
||||
dct['serverSoftware'] = self._serverSoftware
|
||||
dct['membershipSoftware'] = self._membershipSoftware
|
||||
dct['removeOnExit'] = self._removeOnExit == 'y'
|
||||
dct['ssl'] = self._ssl == 'y'
|
||||
dct['automaticIdMapping'] = self._automaticIdMapping == 'y'
|
||||
return dct
|
Loading…
Reference in New Issue
Block a user