Source code for univention.management.console.modules.udm.tools

#!/usr/bin/python3
#
# Univention Management Console
#  module: manages UDM modules
#
# SPDX-FileCopyrightText: 2011-2025 Univention GmbH
# SPDX-License-Identifier: AGPL-3.0-only

import binascii
import urllib.request
from urllib.request import ProxyHandler, build_opener

import ldap
import ldap.modlist
import ldif

import univention.admin.modules
import univention.admin.uexceptions as udm_errors
import univention.admin.uldap
from univention.lib.i18n import Translation
from univention.management.console.log import MODULE


_ = Translation('univention-management-console-module-udm').translate


[docs] class LicenseError(Exception): pass
[docs] class LicenseImport(ldif.LDIFParser): """Import license from LDIF.""" dn = None mod_list = [] dncount = 0 base = None entry = None
[docs] def check(self, base): """Validate given license.""" # call parse from ldif.LDIFParser try: self.parse() except binascii.Error: raise LicenseError(_("No license has been found.")) # there should be exactly one object in the ldif file if self.dncount == 0: raise LicenseError(_("No license has been found.")) elif self.dncount > 1: raise LicenseError(_("More than one object has been found.")) # a free license does not have the ldap base in its dn if not self.dn.endswith(base) and self.base.lower() in ('free for personal use edition', 'ucs core edition'): self.dn = '%s,%s' % (self.dn, base) # check whether DN matches the LDAP base dnWithoutBase = self.dn[:-len(base)] if not self.dn.endswith(base) or not dnWithoutBase.endswith('cn=univention,'): raise LicenseError(_('The LDAP base of the license does not match the LDAP base of the UCS domain (%s).') % base) # check LDAP base if self.base.lower() not in [base.lower(), 'free for personal use edition', 'ucs core edition']: raise LicenseError(_("The license can not be applied. The LDAP base does not match (expected %(expected)s, found: %(found)s).") % {'expected': base, 'found': self.base})
[docs] def handle(self, dn, entry): """This method is invoked by LDIFParser.parse for each object in the LDIF file.""" if dn is None or dn == "": return self.dncount += 1 if 'univentionLicenseBaseDN' in entry: self.base = entry['univentionLicenseBaseDN'][0].decode('UTF-8') else: return self.dn = dn # create modification list self.entry = entry self.addlist = ldap.modlist.addModlist(entry)
# for atr in entry: # self.mod_list.insert(0, (ldap.MOD_REPLACE, atr, entry[atr]))
[docs] def write(self, ldap_connection): """Add the license object.""" if ldap_connection.authz.enabled: obj = univention.admin.modules.get('settings/license').object(None, ldap_connection, None, None, None, self.entry) obj.position.setDn(ldap_connection.parendDn(self.dn)) obj._exists = False ldap_connection.authz.is_create_allowed(obj) ldap_con = ldap_connection.authz_connection.lo.lo try: ldap_con.add_s(self.dn, self.addlist) except ldap.ALREADY_EXISTS: ldap_con.delete_s(self.dn) ldap_con.add_s(self.dn, self.addlist)
[docs] def check_license(ldap_connection, ignore_core_edition=False): try: try: _check_license(ldap_connection) except udm_errors.freeForPersonalUse: if ignore_core_edition: return except udm_errors.licenseNotFound: raise LicenseError(_('License not found. During this session add and modify are disabled.')) except udm_errors.licenseAccounts: # UCS license v1 raise LicenseError(_('You have too many user accounts for your license. Add and modify are disabled. Disable or delete <a href="javascript:void(0)" onclick="require(\'umc/app\').openModule(\'udm\', \'users/user\', {})"> user accounts</a> to re-enable editing.')) except udm_errors.licenseUsers: # UCS license v2 raise LicenseError(_('You have too many user accounts for your license. Add and modify are disabled. Disable or delete <a href="javascript:void(0)" onclick="require(\'umc/app\').openModule(\'udm\', \'users/user\', {})"> user accounts</a> to re-enable editing.')) except udm_errors.licenseClients: # UCS license v1 raise LicenseError(_('You have too many client accounts for your license. During this session add and modify are disabled.')) except udm_errors.licenseServers: # UCS license v2 raise LicenseError(_('You have too many server accounts for your license. During this session add and modify are disabled.')) except udm_errors.licenseManagedClients: # UCS license v2 raise LicenseError(_('You have too many managed client accounts for your license. During this session add and modify are disabled.')) except udm_errors.licenseCorporateClients: # UCS license v2 raise LicenseError(_('You have too many corporate client accounts for your license. During this session add and modify are disabled.')) except udm_errors.licenseDesktops: # UCS license v1 raise LicenseError(_('You have too many desktop accounts for your license. During this session add and modify are disabled.')) except udm_errors.licenseGroupware: # UCS license v1 raise LicenseError(_('You have too many groupware accounts for your license. During this session add and modify are disabled.')) except udm_errors.licenseDVSUsers: # UCS license v2 raise LicenseError(_('You have too many DVS user accounts for your license. During this session add and modify are disabled.')) except udm_errors.licenseDVSClients: # UCS license v2 raise LicenseError(_('You have too many DVS client accounts for your license. During this session add and modify are disabled.')) except udm_errors.licenseExpired: raise LicenseError(_('Your license is expired. During this session add and modify are disabled.')) except udm_errors.licenseWrongBaseDn: raise LicenseError(_('Your license is not valid for your LDAP-Base. During this session add and modify are disabled.')) except udm_errors.licenseInvalid: raise LicenseError(_('Your license is not valid. During this session add and modify are disabled.')) except udm_errors.licenseDisableModify: raise LicenseError(_('Your license does not allow modifications. During this session add and modify are disabled.')) except udm_errors.freeForPersonalUse: raise LicenseError(_('You are currently using the "Free for personal use" edition of Univention Corporate Server.'))
def _check_license(ldap_connection): mapping = { 1: udm_errors.licenseClients, 2: udm_errors.licenseAccounts, 3: udm_errors.licenseDesktops, 4: udm_errors.licenseGroupware, 5: udm_errors.freeForPersonalUse, 6: udm_errors.licenseUsers, 7: udm_errors.licenseServers, 8: udm_errors.licenseManagedClients, 9: udm_errors.licenseCorporateClients, 10: udm_errors.licenseDVSUsers, 11: udm_errors.licenseDVSClients, } code = univention.admin.license.init_select(ldap_connection, 'admin') ldap_connection.authz_connection._validateLicense() # throws more exceptions in case the license could not be found if code in mapping: raise mapping[code] # TODO: this should probably go into univention-lib # and hide urllib.request completely # i.e. it should be unnecessary to import them directly # in a module
[docs] def install_opener(ucr): proxy_http = ucr.get('proxy/http') if proxy_http: proxy = ProxyHandler({'http': proxy_http, 'https': proxy_http}) opener = build_opener(proxy) urllib.request.install_opener(opener)
[docs] def urlopen(request): # use this in __init__ # to have the proxy handler installed globally return urllib.request.urlopen(request) # noqa: S310
[docs] def dump_license(): try: _lo, _pos = univention.admin.uldap.getMachineConnection(ldap_master=False) data = _lo.search('objectClass=univentionLicense') del _lo del _pos # just one license (should be always the case) # return the dictionary without the dn data = ldif.CreateLDIF(data[0][0], data[0][1]) return data except Exception as e: # no udm, no ldap, malformed return value, whatever MODULE.error('getting License from LDAP failed: %s', e) return None