def load( self, request ): """Retrieve current status of the UCS Active Directory Connector configuration and the service options: {} return: { <all AD connector UCR variables> } """ result = {} for option, var, default in Instance.OPTION_MAPPING: result[ option ] = ucr.get( var, default ) pwd_file = ucr.get( 'connector/ad/ldap/bindpw' ) result[ 'passwordExists' ] = bool( pwd_file and os.path.exists( pwd_file ) ) self.finished( request.id, result )
def write(self, user_dn, passwd): ldap_con = ldap.open("localhost", port=int(ucr.get("ldap/server/port", 7389))) ldap_con.simple_bind_s(user_dn, passwd) 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) ldap_con.unbind_s()
def types( self, request, ldap_connection = None, ldap_position = None ): """Returns the list of object types matching the given flavor or container. requests.options = {} 'superordinate' -- if available only types for the given superordinate are returned (not for the navigation) 'container' -- if available only types suitable for the given container are returned (only for the navigation) return: [ { 'id' : <LDAP DN of container or None>, 'label' : <name> }, ... ] """ if request.flavor != 'navigation': module = UDM_Module( request.flavor ) superordinate = request.options.get( 'superordinate' ) if superordinate: self.finished( request.id, module.types4superordinate( request.flavor, superordinate ) ) else: self.finished( request.id, module.child_modules ) else: container = request.options.get( 'container' ) if not container: # no container is specified, return all existing object types MODULE.info('no container specified, returning all object types') self.finished( request.id, map( lambda module: { 'id' : module[ 0 ], 'label' : getattr( module[ 1 ], 'short_description', module[ 0 ] ) }, udm_modules.modules.items() ) ) return if 'None' == container: # if 'None' is given, use the LDAP base container = ucr.get( 'ldap/base' ) MODULE.info('no container == \'None\', set LDAP base as container') # create a list of modules that can be created # ... all container types except container/dc allowed_modules = set([m for m in udm_modules.containers if udm_modules.name(m) != 'container/dc']) # the container may be a superordinate or have one as its parent # (or grandparent, ....) superordinate = udm_modules.find_superordinate(container, None, ldap_connection) if superordinate: # there is a superordinate... add its subtypes to the list of allowed modules MODULE.info('container has a superordinate: %s' % superordinate) allowed_modules.update(udm_modules.subordinates(superordinate)) else: # add all types that do not have a superordinate MODULE.info('container has no superordinate') allowed_modules.update(filter(lambda mod: not udm_modules.superordinate(mod), udm_modules.modules.values())) # make sure that the object type can be created allowed_modules = filter(lambda mod: udm_modules.supports(mod, 'add'), allowed_modules) MODULE.info('all modules that are allowed: %s' % [udm_modules.name(mod) for mod in allowed_modules]) # return the final list of object types self.finished( request.id, map( lambda module: { 'id' : udm_modules.name(module), 'label' : getattr( module, 'short_description', udm_modules.name(module) ) }, allowed_modules ) )
def connect(self, request): url = None (is_running, passwd_exists, ) = self._get_status() port = None host = None if is_running: args = self._get_cmdline() if '-rfbport' in args: port = args[args.index('-rfbport') + 1] fqdn = '%s.%s' % ( ucr.get( 'hostname' ), ucr.get( 'domainname' ) ) VNC_LINK_BY_NAME, VNC_LINK_BY_IPV4, VNC_LINK_BY_IPV6 = range(3) vnc_link_format = VNC_LINK_BY_IPV4 if vnc_link_format == VNC_LINK_BY_IPV4: addrs = socket.getaddrinfo( fqdn, port, socket.AF_INET ) (family, socktype, proto, canonname, sockaddr) = addrs[0] host = sockaddr[0] elif vnc_link_format == VNC_LINK_BY_IPV6: addrs = socket.getaddrinfo( fqdn, port, socket.AF_INET6 ) (family, socktype, proto, canonname, sockaddr) = addrs[0] host = '[%s]' % sockaddr[0] self.finished( request.id, { 'port': port, 'host' : host } )
def templates( self, request ): """Returns the list of template objects for the given object type. requests.options = {} 'objectType' -- The UDM module name return: [ { 'id' : <LDAP DN of container or None>, 'label' : <name> }, ... ] """ module = self._get_module_by_request( request ) result = [] if module.template: template = UDM_Module( module.template ) objects = template.search( ucr.get( 'ldap/base' ) ) for obj in objects: obj.open() result.append( { 'id' : obj.dn, 'label' : obj[ template.identifies ] } ) self.finished( request.id, result )
def nav_container_query( self, request ): """Returns a list of LDAP containers located under the given LDAP base (option 'container'). If no base container is specified the LDAP base object is returned.""" if not request.options.get( 'container' ): ldap_base = ucr.get( 'ldap/base' ) self.finished( request.id, [ { 'id' : ldap_base, 'label' : ldap_dn2path( ldap_base ), 'icon' : 'udm-container-dc', 'objectType' : 'container/dc' } ] ) return def _thread( container ): success = True message = None superordinate = None result = [] for base, typ in map( lambda x: x.split( '/' ), self.modules_with_childs ): module = UDM_Module( '%s/%s' % ( base, typ ) ) if module.superordinate: if superordinate is None: try: so_module = UDM_Module( module.superordinate ) so_obj = so_module.get( request.options.get( 'container' ) ) superordinate = so_obj except UDM_Error: # superordinate object could not be load -> ignore module continue else: so_obj = superordinate else: so_obj = None try: for item in module.search( container, scope = 'one', superordinate = so_obj ): result.append( { 'id' : item.dn, 'label' : item[ module.identifies ], 'icon' : 'udm-%s-%s' % ( base, typ ), 'path': ldap_dn2path( item.dn ), 'objectType': '%s/%s' % (base, typ) } ) except UDM_Error, e: success = False result = None message = str( e ) return result, message, success
def query(self, pattern): ucr.load() srvs = ServiceInfo() lang = _.im_self.locale.language if lang in (None, 'C'): lang = 'en' result = [] for name, srv in srvs.services.items(): key = srv.get('start_type', '%s/autostart' % (name,)) entry = { 'service': name, 'description': srv.get('description[%s]' % (lang,), srv.get('description')), 'autostart': ucr.get(key, 'yes'), 'isRunning': srv.running, } if entry['autostart'] not in ('yes', 'no', 'manually'): entry['autostart'] = 'yes' for value in entry.values(): if pattern.match(str(value)): result.append(entry) break return result
def test_identity_provider_certificate(): # download from all ip addresses of ucs-sso. the IDP certificate (/etc/simplesamlphp/*-idp-certificate.crt) # compare this with /usr/share/univention-management-console/saml/idp/*.xml # If it fails: univention-run-joinscripts --force --run-scripts 92univention-management-console-web-server sso_fqdn = ucr.get('ucs/server/sso/fqdn') MODULE.process( "Checks ucs-sso by comparing 'ucr get ucs/server/sso/fqdn' with the Location field in /usr/share/univention-management-console/saml/idp/*.xml" ) if not sso_fqdn: return for host in socket.gethostbyname_ex(sso_fqdn)[2]: try: with download_tempfile( 'https://%s/simplesamlphp/saml2/idp/certificate' % (host, ), {'host': sso_fqdn}) as certificate: certificate = certificate.read() for idp in glob.glob( '/usr/share/univention-management-console/saml/idp/*.xml' ): with open(idp) as fd: cert = find_node( fromstring(fd.read()), '{http://www.w3.org/2000/09/xmldsig#}X509Certificate' ) if cert.text.strip() not in certificate: MODULE.error( 'The certificate of the SAML identity provider does not match.' ) raise Critical( _('The certificate of the SAML identity provider does not match.' )) except requests.exceptions.ConnectionError: print 'error, connecting' pass
def run(_umc_instance): if ucr.get('server/role') != 'domaincontroller_master': return lo, pos = getAdminConnection() objects = udm_objects_without_type(lo) if len(objects): counted_objects = {} details = '\n\n' + _('These objects were found:') for dn, modules, object_classes in objects: for module in modules: counted_objects.setdefault(module.short_description, 0) counted_objects[module.short_description] += 1 for module_name in sorted(counted_objects.iterkeys()): num_objs = counted_objects[module_name] details += '\n· ' + _('%d objects should be "%s"') % (num_objs, module_name) raise Warning(description + details, buttons=[{ 'action': 'migrate_objects', 'label': _('Migrate %d LDAP objects') % len(objects), }])
def run(_umc_instance): if ucr.get('server/role') != 'domaincontroller_master': return lo, pos = getAdminConnection() objs = udm_objects_without_ServerRole(lo) details = '\n\n' + _('These objects were found:') total_objs = 0 fixable_objs = 0 for server_role in sorted(objs): num_objs = len(objs[server_role]) if num_objs: total_objs += num_objs if server_role: fixable_objs += num_objs details += '\n· ' + _( 'Number of objects that should be marked as "%s": %d') % ( server_role, num_objs, ) else: details += '\n· ' + _( 'Number of unspecific Windows computer objects with inconsistent univentionObjectType: %d (Can\'t fix this automatically)' ) % (num_objs, ) if total_objs: if fixable_objs: raise Warning(description + details, buttons=[{ 'action': 'migrate_objects', 'label': _('Migrate %d LDAP objects') % fixable_objs, }]) else: raise Warning(description + details, buttons=[])
def set_user_attributes(self, username, password, attributes): dn, username = self.auth(username, password) if self.is_blacklisted(username): raise ServiceForbidden() user_attributes = [ attr.strip() for attr in ucr.get('self-service/udm_attributes', '').split(',') ] lo, po = get_user_connection(binddn=dn, bindpw=password) user = self.usersmod.object(None, lo, po, dn) user.open() for propname, value in attributes.items(): if propname in user_attributes and user.has_property(propname): user[propname] = value try: user.modify() except univention.admin.uexceptions.base as exc: MODULE.error( 'set_user_attributes(): modifying the user failed: %s' % (traceback.format_exc(), )) raise UMC_Error( _('The attributes could not be saved: %s') % (UDM_Error(exc))) return _("Successfully changed your profile data.")
def ucr_try_int(variable, default): try: return int(ucr.get(variable, default)) except ValueError: MODULE.error('UCR variables %s is not a number, using default: %s' % (variable, default)) return default
def _update_required_attr_of_props_for_registration(self, properties): for k in properties.keys(): if isinstance(properties[k], dict): properties[k]['required'] = False else: properties[k].required = False required_ids = set(['PasswordRecoveryEmail', 'password'] + [attr.strip() for attr in ucr.get('umc/self-service/account-registration/udm_attributes/required', '').split(',') if attr.strip()]) for id_ in required_ids: if id_ in properties: if isinstance(properties[id_], dict): properties[id_]['required'] = True else: properties[id_].required = True
def check_dcmaster_srv_rec(self): result = bool(admember.get_domaincontroller_srv_record(ucr.get('domainname'))) return {'success': result}
def _finished( thread, result, request ): if self._check_thread_error( thread, result, request ): return success, data = result if not success: self.finished( request.id, None, message = str( data ), status = MODULE_ERR_COMMAND_FAILED ) return node_uri = urlparse.urlsplit( request.options[ 'domainURI' ] ) uri, uuid = urlparse.urldefrag( request.options[ 'domainURI' ] ) json = object2dict( data ) ## re-arrange a few attributes for the frontend # annotations for key in json[ 'annotations' ]: if key == 'uuid': continue json[ key ] = json[ 'annotations' ][ key ] # type json[ 'type' ] = '%(domain_type)s-%(os_type)s' % json # STOP here if domain is not available if not json[ 'available' ]: MODULE.info( 'Domain is not available: %s' % str( json ) ) self.finished( request.id, json ) return # RAM json[ 'maxMem' ] = MemorySize.num2str( json[ 'maxMem' ] ) # interfaces (fake the special type network:<source>) for iface in json[ 'interfaces' ]: if iface[ 'type' ] == Interface.TYPE_NETWORK: iface[ 'type' ] = 'network:' + iface[ 'source' ] # disks for disk in json[ 'disks' ]: if disk[ 'type' ] == Disk.TYPE_FILE: disk[ 'volumeFilename' ] = os.path.basename( disk[ 'source' ] ) disk[ 'pool' ] = self.get_pool_name( uri, os.path.dirname( disk[ 'source' ] ) ) else: disk[ 'volumeFilename' ] = disk[ 'source' ] disk[ 'pool' ] = None disk[ 'paravirtual' ] = disk[ 'target_bus' ] in ( 'virtio', 'xen' ) disk[ 'volumeType' ] = disk[ 'type' ] if isinstance( disk[ 'size' ], ( int, long ) ): disk[ 'size' ] = MemorySize.num2str( disk[ 'size' ] ) # graphics if json['graphics']: try: gfx = json[ 'graphics' ][ 0 ] json[ 'vnc' ] = True json[ 'vnc_host' ] = None json[ 'vnc_port' ] = None json[ 'kblayout' ] = gfx[ 'keymap' ] json[ 'vnc_remote' ] = gfx[ 'listen' ] == '0.0.0.0' json[ 'vnc_password' ] = gfx[ 'passwd' ] # vnc_password will not be send to frontend port = int( json[ 'graphics' ][ 0 ][ 'port' ] ) if port == -1: raise ValueError() host = node_uri.netloc vnc_link_format = ucr.get('uvmm/umc/vnc/host', 'IPv4') or '' match = Domains.RE_VNC.match(vnc_link_format) if match: family, pattern, substs = match.groups() if family: # IPvX family = Domains.SOCKET_FAMILIES[family] regex = re.compile(pattern or '.*') addrs = socket.getaddrinfo(host, port, family, socket.SOCK_STREAM, socket.SOL_TCP) for (family, _socktype, _proto, _canonname, sockaddr) in addrs: host, port = sockaddr[:2] if regex.search(host): break else: raise LookupError(pattern) host = Domains.SOCKET_FORMATS[family] % (host,) elif substs: # NAME for subst in substs.split(): old, new = subst.split('=', 1) host = host.replace(old, new) elif vnc_link_format: # overwrite all hosts with fixed host host = vnc_link_format json[ 'vnc_host' ] = host json[ 'vnc_port' ] = port except re.error, ex: # port is not valid MODULE.warn('Invalid VNC regex: %s' % (ex,)) except socket.gaierror, ex: MODULE.warn('Invalid VNC host: %s' % (ex,))
def __update_status( self ): ucr.load() self.status_configured = bool( ucr.get( 'connector/ad/ldap/host' ) and ucr.get( 'connector/ad/ldap/base' ) and ucr.get( 'connector/ad/ldap/binddn' ) and ucr.get( 'connector/ad/ldap/bindpw' ) ) fn = ucr.get( 'connector/ad/ldap/certificate' ) self.status_certificate = bool( fn and os.path.exists( fn ) ) self.status_running = self.__is_process_running( '*python*univention/connector/ad/main.py*' )
def run(_umc_instance, url='http://www.univention.de/', connecttimeout=30, timeout=30): ucr.load() proxy = ucr.get('proxy/http') if not proxy: return proxy = urlparse(proxy) MODULE.info('The proxy is configured, using host=%r, port=%r' % (proxy.hostname, proxy.port)) curl = pycurl.Curl() curl.setopt(pycurl.PROXYTYPE, pycurl.PROXYTYPE_HTTP) if proxy.hostname: curl.setopt(pycurl.PROXY, proxy.hostname) if proxy.port: curl.setopt(pycurl.PROXYPORT, proxy.port) curl.setopt(pycurl.FOLLOWLOCATION, True) curl.setopt(pycurl.MAXREDIRS, 5) curl.setopt(pycurl.CONNECTTIMEOUT, connecttimeout) curl.setopt(pycurl.TIMEOUT, 30) if proxy.username: curl.setopt(pycurl.PROXYAUTH, pycurl.HTTPAUTH_ANY) credentials = '%s' % (proxy.username, ) if proxy.password: credentials = '%s:%s' % (proxy.username, proxy.password) curl.setopt(pycurl.PROXYUSERPWD, credentials) curl.setopt(pycurl.URL, url) # curl.setopt(pycurl.VERBOSE, bVerbose) buf = StringIO.StringIO() curl.setopt(pycurl.WRITEFUNCTION, buf.write) try: curl.perform() except pycurl.error as exc: try: code, msg = exc.args msg = '%s (code=%s)' % (msg, code) MODULE.info(msg) except ValueError: MODULE.error(traceback.format_exc()) code = 0 msg = str(exc) if code == pycurl.E_COULDNT_CONNECT: msg = _( 'The proxy host could not be reached. Make sure that hostname (%(hostname)r) and port (%(port)r) are correctly set up.' ) % { 'hostname': proxy.hostname, 'port': proxy.port } elif code == pycurl.E_COULDNT_RESOLVE_PROXY: msg = _( 'The hostname of the proxy could not be resolved. May check your DNS configuration.' ) elif code == pycurl.E_OPERATION_TIMEOUTED: msg = _( 'The server did not respond within %d seconds. Please check your network configuration.' ) % (timeout, ) elif code == 0: MODULE.error(traceback.format_exc()) raise Critical('\n'.join([description, msg])) else: # page = buf.getvalue() # MODULE.info(page[:100]) buf.close() http_status = curl.getinfo(pycurl.HTTP_CODE) if http_status >= 400: raise Warning('\n'.join([ description, _('The proxy server is reachable but the HTTP response status code (%d) does not indicate success.' ) % (http_status, ), _('This warning might be harmless. Nevertheless make sure the authentication credentials (if any) are correct and the proxy server ACLs do not forbid requests to %s.' ) % (url, ) ])) finally: curl.close()
def _check_dcmaster_srv_rec(self): if admember.get_domaincontroller_srv_record(ucr.get('domainname')): return True else: return False
def init(self): self._hostname = ucr.get('hostname')
def get_metainfo(self): """Queries the specified DC Master for metainformation about the UCS@school environment""" master = ucr.get('ldap/master') or get_master_dns_lookup() if not master: return return self._umc_master(self.username, self.password, master, 'schoolinstaller/get/metainfo/master')
def system_join(username, password, info_handler, error_handler, step_handler): # make sure we got the correct server role server_role = ucr.get('server/role') assert server_role in ('domaincontroller_slave', 'domaincontroller_backup', 'domaincontroller_master') # get the number of join scripts n_joinscripts = len(glob.glob('/usr/lib/univention-install/*.inst')) steps_per_script = 100.0 / n_joinscripts # disable UMC/apache restart MODULE.info('disabling UMC and apache server restart') subprocess.call(CMD_DISABLE_EXEC) try: with tempfile.NamedTemporaryFile() as password_file: password_file.write('%s' % password) password_file.flush() # regular expressions for output parsing error_pattern = re.compile('^\* Message:\s*(?P<message>.*)\s*$') joinscript_pattern = re.compile('(Configure|Running)\s+(?P<script>.*)\.inst.*$') info_pattern = re.compile('^(?P<message>.*?)\s*:?\s*\x1b.*$') # call to univention-join process = None if server_role == 'domaincontroller_slave': # DC slave -> complete re-join MODULE.process('Performing system join...') process = subprocess.Popen(['/usr/sbin/univention-join', '-dcaccount', username, '-dcpwd', password_file.name], shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) else: # DC backup/master -> only run join scripts MODULE.process('Executing join scripts ...') process = subprocess.Popen(['/usr/sbin/univention-run-join-scripts', '-dcaccount', username, '-dcpwd', password_file.name], shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) failed_join_scripts = [] executed_join_scripts = set() def parse(line): if not line.strip(): return MODULE.process(repr(line.strip()).strip('"\'')) # parse output... first check for errors m = error_pattern.match(line) if m: error_handler(_('Software packages have been installed, however, the system join could not be completed: %s. More details can be found in the log file /var/log/univention/join.log. Please retry the join process via the UMC module "Domain join" after resolving any conflicting issues.') % m.groupdict().get('message')) return # check for currently called join script m = joinscript_pattern.match(line) if m: current_script = m.groupdict().get('script') info_handler(_('Executing join script %s') % (current_script,)) if current_script not in executed_join_scripts: executed_join_scripts.add(current_script) step_handler(steps_per_script) if 'failed' in line: failed_join_scripts.append(current_script) return # check for other information m = info_pattern.match(line) if m: info_handler(m.groupdict().get('message')) return # make stdout file descriptor of the process non-blocking fd = process.stdout.fileno() fl = fcntl.fcntl(fd, fcntl.F_GETFL) fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) unfinished_line = '' while True: try: fd = select.select([process.stdout], [], [])[0][0] except IndexError: continue # not ready / no further data except select.error as exc: if exc.args[0] == errno.EINTR: continue raise # get the next line line = fd.read() if not line: break # no more text from stdout unfinished_line = '' if line.endswith('\n') else '%s%s' % (unfinished_line, line.rsplit('\n', 1)[-1]) for line in line.splitlines(): parse(line) if unfinished_line: parse(unfinished_line) # get all remaining output stdout, stderr = process.communicate() if stderr: # write stderr into the log file MODULE.warn('stderr from univention-join: %s' % stderr) # check for errors if process.returncode != 0: # error case MODULE.warn('Could not perform system join: %s%s' % (stdout, stderr)) error_handler(_('Software packages have been installed successfully, however, the join process could not be executed. More details can be found in the log file /var/log/univention/join.log. Please retry to join the system via the UMC module "Domain join" after resolving any conflicting issues.')) elif failed_join_scripts: MODULE.warn('The following join scripts could not be executed: %s' % failed_join_scripts) error_handler(_('Software packages have been installed successfully, however, some join scripts could not be executed. More details can be found in the log file /var/log/univention/join.log. Please retry to execute the join scripts via the UMC module "Domain join" after resolving any conflicting issues.')) finally: # make sure that UMC servers and apache can be restarted again MODULE.info('enabling UMC and apache server restart') subprocess.call(CMD_ENABLE_EXEC)
def create_self_registered_account(self, attributes): MODULE.info('create_self_registered_account(): attributes: {}'.format(attributes)) ucr.load() if ucr.is_false('umc/self-service/account-registration/backend/enabled', True): msg = _('The account registration was disabled via the Univention Configuration Registry.') MODULE.error('create_self_registered_account(): {}'.format(msg)) raise UMC_Error(msg) # filter out attributes that are not valid to set allowed_to_set = set(['PasswordRecoveryEmail', 'password'] + [attr.strip() for attr in ucr.get('umc/self-service/account-registration/udm_attributes', '').split(',') if attr.strip()]) attributes = {k: v for (k, v) in attributes.items() if k in allowed_to_set} # validate attributes res = self._validate_user_attributes(attributes, self._update_required_attr_of_props_for_registration) # check username taken if 'username' in attributes: try: UDM.machine().version(2).get('users/user').get_by_id(attributes['username']) except NoObject: pass else: res['username'] = { 'isValid': False, 'message': _('The username is already taken'), } invalid = {k: v for (k, v) in res.items() if not (all(v['isValid']) if isinstance(v['isValid'], list) else v['isValid'])} if len(invalid): return { 'success': False, 'failType': 'INVALID_ATTRIBUTES', 'data': invalid, } # check for missing required attributes from umc/self-service/account-registration/udm_attributes/required required_attrs = [attr.strip() for attr in ucr.get('umc/self-service/account-registration/udm_attributes/required', '').split(',') if attr.strip()] not_found = [attr for attr in required_attrs if attr not in attributes] if not_found: msg = _('The account could not be created:\nInformation provided is not sufficient. The following properties are missing:\n%s') % ('\n'.join(not_found),) MODULE.error('create_self_registered_account(): {}'.format(msg)) raise UMC_Error(msg) univention.admin.modules.update() lo, po = get_admin_connection() # get usertemplate template_dn = ucr.get('umc/self-service/account-registration/usertemplate', '') usertemplate = None if template_dn: usertemplate_mod = univention.admin.modules.get('settings/usertemplate') univention.admin.modules.init(lo, po, usertemplate_mod, None, True) try: usertemplate = usertemplate_mod.object(None, lo, None, template_dn) except udm_errors.noObject: msg = _('The user template "{}" set by the "umc/self-service/account-registration/usertemplate" UCR variable does not exist. A user account can not be created. Please contact your system administrator.'.format(template_dn)) MODULE.error('create_self_registered_account(): {}'.format(msg)) raise UMC_Error(msg) # init user module with template usersmod = univention.admin.modules.get('users/user') univention.admin.modules.init(lo, po, usersmod, usertemplate, True) # get user container udm = UDM.machine().version(2) user_position = univention.admin.uldap.position(po.getBase()) container_dn = ucr.get('umc/self-service/account-registration/usercontainer', None) if container_dn: try: container = udm.obj_by_dn(container_dn) except NoObject: msg = _('The container "{}" set by the "umc/self-service/account-registration/usercontainer" UCR variable does not exist. A user account can not be created. Please contact your system administrator.'.format(container_dn)) MODULE.error('create_self_registered_account(): {}'.format(msg)) raise UMC_Error(msg) else: user_position.setDn(container.dn) else: for dn in usersmod.object.get_default_containers(lo): try: container = udm.obj_by_dn(dn) except NoObject: pass else: user_position.setDn(container.dn) break # create user attributes['PasswordRecoveryEmailVerified'] = 'FALSE' attributes['RegisteredThroughSelfService'] = 'TRUE' new_user = usersmod.object(None, lo, user_position) new_user.open() for key, value in attributes.items(): if key in new_user and value: new_user[key] = value try: new_user.create() except univention.admin.uexceptions.base as exc: MODULE.error('create_self_registered_account(): could not create user: %s' % (traceback.format_exc(),)) return { 'success': False, 'failType': 'CREATION_FAILED', 'data': _('The account could not be created:\n%s') % UDM_Error(exc), } finally: # TODO cleanup # reinit user module without template. # This has to be done since the modules are singletons? univention.admin.modules.update() self._usersmod = None # univention.admin.modules.init(lo, po, usersmod, None, True) try: self.send_message(new_user['username'], 'verify_email', new_user['PasswordRecoveryEmail'], raise_on_success=False) except Exception: verify_token_successfully_send = False else: verify_token_successfully_send = True return { 'success': True, 'verifyTokenSuccessfullySend': verify_token_successfully_send, 'data': { 'username': new_user['username'], 'email': new_user['PasswordRecoveryEmail'], } }
def status(self, request: Request) -> None: # TODO: remove unneeded things """One call for all single-value variables.""" result = {} # type: Dict[str, Any] ucr.load() try: result['erratalevel'] = int(ucr.get('version/erratalevel', 0)) except ValueError: result['erratalevel'] = 0 result['appliance_mode'] = ucr.is_true('server/appliance') result['timestamp'] = int(time()) result['reboot_required'] = ucr.is_true('update/reboot/required', False) try: # be as current as possible. what = 'reinitializing UniventionUpdater' self.uu.ucr_reinit() what = 'getting UCS version' result['ucs_version'] = str(self.uu.current_version) # if nothing is returned -> convert to empty string. what = 'querying available release updates' try: ver = self.uu.release_update_available(errorsto='exception') result[ 'release_update_available'] = '' if ver is None else str( ver) except RequiredComponentError as exc: result['release_update_available'] = exc.version what = 'querying update-blocking components' blocking_components = self.uu.get_all_available_release_updates( )[1] or set() # check apps if result['release_update_available']: try: from univention.appcenter.actions import get_action update_check = get_action('update-check') if update_check: blocking_apps = update_check.get_blocking_apps( ucs_version=result['release_update_available']) if blocking_apps: blocking_components.update(set(blocking_apps)) except (ImportError, ValueError): # the new univention.appcenter package is not installed. # Cannot be a dependency as the app center depends on updater... raise UMC_Error( _('Error checking if installed apps are available for next UCS version.' )) result['release_update_blocking_components'] = ' '.join( blocking_components) # Component counts are now part of the general 'status' data. what = "counting components" components = [ bool(comp) for comp in self.uu.get_components(all=True) ] result['components'] = len(components) result['enabled'] = sum(components) # HACK: the 'Updates' form polls on the serial file # to refresh itself. Including the serial value # into the form helps us to have a dependent field # that can trigger the refresh of the "Releases" # combobox and the 'package updates available' field. result['serial'] = self._serial_file.timestamp() except Exception as exc: # FIXME: don't catch everything raise UMC_Error("%s %s %s" % ( _('Error contacting the update server. Please check your proxy or firewall settings, if any. Or it may be a problem with your configured DNS server.' ), _('This is the error message:'), exc, ), traceback=format_exc()) self.finished(request.id, [result])
def send(self, request, ldap_user_read=None, ldap_position=None): ucr.load() if not ucr.get('ucsschool/helpdesk/recipient'): raise UMC_Error(_( 'The message could not be send to the helpdesk team: The email address for the helpdesk team is not configured. It must be configured by an administrator via the UCR variable "ucsschool/helpdesk/recipient".' ), status=500) def _send_thread(sender, recipients, subject, message): MODULE.info('sending mail: thread running') msg = u'From: %s\r\n' % (sanitize_header(sender), ) msg += u'To: %s\r\n' % (sanitize_header(', '.join(recipients)), ) msg += u'Subject: =?UTF-8?Q?%s?=\r\n' % ( sanitize_header(subject).encode('quopri'), ) msg += u'Content-Type: text/plain; charset="UTF-8"\r\n' msg += u'\r\n' msg += message msg += u'\r\n' msg = msg.encode('UTF-8') server = smtplib.SMTP('localhost') server.set_debuglevel(0) server.sendmail(sender, recipients, msg) server.quit() return True recipients = ucr['ucsschool/helpdesk/recipient'].split(' ') school = School.from_dn( School(name=request.options['school']).dn, None, ldap_user_read).display_name category = request.options['category'] message = request.options['message'] subject = u'%s (%s: %s)' % (category, _('School'), school) try: user = User(None, ldap_user_read, ldap_position, self.user_dn) user.open() except ldap.LDAPError: MODULE.error('Errror receiving user information: %s' % (traceback.format_exception(), )) user = { 'displayName': self.username, 'mailPrimaryAddress': '', 'mailAlternativeAddress': [], 'e-mail': [], 'phone': [] } mails = set([user['mailPrimaryAddress']]) | set( user['mailAlternativeAddress']) | set(user['e-mail']) sender = user['mailPrimaryAddress'] if not sender: if ucr.get('hostname') and ucr.get('domainname'): sender = 'ucsschool-helpdesk@%s.%s' % (ucr['hostname'], ucr['domainname']) else: sender = 'ucsschool-helpdesk@localhost' data = [ (_('Sender'), u'%s (%s)' % (user['displayName'], self.username)), (_('School'), school), (_('Mail address'), u', '.join(mails)), (_('Phone number'), u', '.join(user['phone'])), (_('Category'), category), (_('Message'), u'\r\n%s' % (message, )), ] msg = u'\r\n'.join(u'%s: %s' % (key, value) for key, value in data) MODULE.info( 'sending message: %s' % ('\n'.join(map(lambda x: repr(x.strip()), msg.splitlines()))), ) func = notifier.Callback(_send_thread, sender, recipients, subject, msg) MODULE.info('sending mail: starting thread') thread = notifier.threads.Simple( 'HelpdeskMessage', func, notifier.Callback(self.thread_finished_callback, request)) thread.run()
def get_school_version(self): return ucr.get('appcenter/apps/ucsschool/version')
def __init__(self): super(SearchLimitReached, self).__init__(_('The query you have entered yields too many matching entries. Please narrow down your search by specifying more query parameters. The current size limit of %s can be configured with the UCR variable directory/manager/web/sizelimit.') % ucr.get('directory/manager/web/sizelimit', '2000'))
def install(self, request): # get all arguments username = request.options.get('username') password = request.options.get('password') master = request.options.get('master') school_ou = request.options.get('schoolOU') educational_slave = request.options.get('nameEduServer') ou_display_name = request.options.get('OUdisplayname', school_ou) # use school OU name as fallback server_type = request.options.get('server_type') setup = request.options.get('setup') server_role = ucr.get('server/role') joined = os.path.exists('/var/univention-join/joined') if self._installation_started: raise ValueError('The installation was started twice. This should not have happened.') if server_role != 'domaincontroller_slave': # use the credentials of the currently authenticated user on a master/backup system self.require_password() username = self.username password = self.password master = '%s.%s' % (ucr.get('hostname'), ucr.get('domainname')) if server_role == 'domaincontroller_backup': master = ucr.get('ldap/master') self.original_certificate_file = None # check for valid school OU if ((setup == 'singlemaster' and server_role == 'domaincontroller_master') or server_role == 'domaincontroller_slave') and not RE_OU.match(school_ou): raise SchoolInstallerError(_('The specified school OU is not valid.')) # check for valid server role if server_role not in ('domaincontroller_master', 'domaincontroller_backup', 'domaincontroller_slave'): raise SchoolInstallerError(_('Invalid server role! UCS@school can only be installed on the system roles master domain controller, backup domain controller, or slave domain controller.')) if server_role == 'domaincontroller_slave' and not server_type: raise SchoolInstallerError(_('Server type has to be set for domain controller slave')) if server_role == 'domaincontroller_slave' and server_type == 'administrative' and not educational_slave: raise SchoolInstallerError(_('The name of an educational server has to be specified if the system shall be configured as administrative server.')) if server_role == 'domaincontroller_slave' and server_type == 'administrative' and educational_slave.lower() == ucr.get('hostname').lower(): raise SchoolInstallerError(_('The name of the educational server may not be equal to the name of the administrative slave.')) if server_role == 'domaincontroller_slave': # on slave systems, download the certificate from the master in order # to be able to build up secure connections self.original_certificate_file = self.retrieve_root_certificate(master) if server_role != 'domaincontroller_master': # check for a compatible environment on the DC master masterinfo = self._umc_master(username, password, master, 'schoolinstaller/get/metainfo') school_environment = masterinfo['school_environment'] master_samba_version = masterinfo['samba'] if not school_environment: raise SchoolInstallerError(_('Please install UCS@school on the master domain controller system. Cannot proceed installation on this system.')) if master_samba_version == 3: raise SchoolInstallerError(_('This UCS domain uses Samba 3 which is no longer supported by UCS@school. Please update all domain systems to samba 4 to be able to continue.')) if server_role == 'domaincontroller_slave' and school_environment != 'multiserver': raise SchoolInstallerError(_('The master domain controller is not configured for a UCS@school multi server environment. Cannot proceed installation on this system.')) if server_role == 'domaincontroller_backup' and school_environment != setup: raise SchoolInstallerError(_('The UCS@school master domain controller needs to be configured similarly to this backup system. Please choose the correct environment type for this system.')) if server_role == 'domaincontroller_backup' and not joined: raise SchoolInstallerError(_('In order to install UCS@school on a backup domain controller, the system needs to be joined first.')) # everything ok, try to acquire the lock for the package installation lock_aquired = self.package_manager.lock(raise_on_fail=False) if not lock_aquired: MODULE.warn('Could not aquire lock for package manager') raise SchoolInstallerError(_('Cannot get lock for installation process. Another package manager seems to block the operation.')) # see which packages we need to install MODULE.process('performing UCS@school installation') packages_to_install = [] installed_samba_version = self.get_samba_version() if installed_samba_version == 3: raise SchoolInstallerError(_('This UCS domain uses Samba 3 which is no longer supported by UCS@school. Please update all domain systems to samba 4 to be able to continue.')) if server_role == 'domaincontroller_slave': # slave packages_to_install.extend(['univention-samba4', 'univention-s4-connector']) if server_type == 'educational': packages_to_install.append('ucs-school-slave') else: packages_to_install.append('ucs-school-nonedu-slave') else: # master or backup if setup == 'singlemaster': if installed_samba_version: pass # do not install samba a second time else: # otherwise install samba4 packages_to_install.extend(['univention-samba4', 'univention-s4-connector']) packages_to_install.append('ucs-school-singlemaster') elif setup == 'multiserver': packages_to_install.append('ucs-school-master') else: raise SchoolInstallerError(_('Invalid UCS@school configuration.')) MODULE.info('Packages to be installed: %s' % ', '.join(packages_to_install)) # reset the current installation progress steps = 100 # installation -> 100 if server_role != 'domaincontroller_backup' and not (server_role == 'domaincontroller_master' and setup == 'multiserver'): steps += 10 # create_ou -> 10 if server_role == 'domaincontroller_slave': steps += 10 # move_slave_into_ou -> 10 steps += 100 # system_join -> 100 steps self._installation_started = True progress_state = self.progress_state progress_state.reset(steps) progress_state.component = _('Installation of UCS@school packages') self.package_manager.reset_status() def _thread(_self, packages): MODULE.process('Starting package installation') with _self.package_manager.locked(reset_status=True, set_finished=True): with _self.package_manager.no_umc_restart(exclude_apache=True): _self.package_manager.update() if not _self.package_manager.install(*packages): raise SchoolInstallerError(_('Failed to install packages.')) if server_role != 'domaincontroller_backup' and not (server_role == 'domaincontroller_master' and setup == 'multiserver'): # create the school OU (not on backup and not on master w/multi server environment) MODULE.info('Starting creation of LDAP school OU structure...') progress_state.component = _('Creation of LDAP school structure') progress_state.info = '' try: if server_role == 'domaincontroller_slave': _educational_slave = ucr.get('hostname') if server_type == 'educational' else educational_slave administrative_slave = None if server_type == 'educational' else ucr.get('hostname') create_ou_remote(master, username, password, school_ou, ou_display_name, _educational_slave, administrative_slave) elif server_role == 'domaincontroller_master': create_ou_local(school_ou, ou_display_name) except SchoolInstallerError as exc: MODULE.error(str(exc)) raise SchoolInstallerError(_( 'The UCS@school software packages have been installed, however, a school OU could not be created and consequently a re-join of the system has not been performed. ' 'Please create a new school OU structure using the UMC module "Add school" on the master and perform a domain join on this machine via the UMC module "Domain join".' )) progress_state.add_steps(10) if server_role == 'domaincontroller_slave': # make sure that the slave is correctly moved below its OU MODULE.info('Trying to move the slave entry in the right OU structure...') result = umc(username, password, master, 'schoolwizards/schools/move_dc', {'schooldc': ucr.get('hostname'), 'schoolou': school_ou}, 'schoolwizards/schools').result if not result.get('success'): MODULE.warn('Could not successfully move the slave DC into its correct OU structure:\n%s' % result.get('message')) raise SchoolInstallerError(_('Validating the LDAP school OU structure failed. It seems that the current slave system has already been assigned to a different school or that the specified school OU name is already in use.')) # system join on a slave system progress_state.component = _('Domain join') if server_role == 'domaincontroller_slave': progress_state.info = _('Preparing domain join...') MODULE.process('Starting system join...') else: # run join scripts on DC backup/master progress_state.info = _('Executing join scripts...') MODULE.process('Running join scripts...') system_join( username, password, info_handler=self.progress_state.info_handler, step_handler=self.progress_state.add_steps, error_handler=self.progress_state.error_handler, ) def _finished(thread, result): MODULE.info('Finished installation') progress_state.finish() progress_state.info = _('finished...') self._installation_started = None if isinstance(result, SchoolInstallerError): MODULE.warn('Error during installation: %s' % (result,)) self.restore_original_certificate() progress_state.error_handler(str(result)) elif isinstance(result, BaseException): self.restore_original_certificate() msg = ''.join(traceback.format_exception(*thread.exc_info)) MODULE.error('Exception during installation: %s' % msg) progress_state.error_handler(_('An unexpected error occurred during installation: %s') % result) thread = notifier.threads.Simple('ucsschool-install', notifier.Callback(_thread, self, packages_to_install), notifier.Callback(_finished)) thread.run() self.finished(request.id, None)
def containers(self): """List of LDAP DNs of default containers""" containers = getattr(self.module, 'default_containers', []) ldap_base = ucr.get('ldap/base') return map(lambda x: {'id': '%s,%s' % (x, ldap_base), 'label': ldap_dn2path('%s,%s' % (x, ldap_base))}, containers)
def status(self, request): # TODO: remove unneeded things """One call for all single-value variables.""" result = {} ucr.load() try: result['erratalevel'] = int(ucr.get('version/erratalevel', 0)) except ValueError: result['erratalevel'] = 0 result['appliance_mode'] = ucr.is_true('server/appliance') result['easy_mode'] = ucr.is_true('update/umc/updateprocess/easy', False) result['timestamp'] = int(time()) result['reboot_required'] = ucr.is_true('update/reboot/required', False) try: # be as current as possible. what = 'reinitializing UniventionUpdater' self.uu.ucr_reinit() what = 'getting UCS version' result['ucs_version'] = self.uu.get_ucs_version() # if nothing is returned -> convert to empty string. what = 'querying available release updates' try: result[ 'release_update_available'] = self.uu.release_update_available( errorsto='exception') except RequiredComponentError as exc: result['release_update_available'] = exc.version if result['release_update_available'] is None: result['release_update_available'] = '' what = 'querying update-blocking components' blocking_components = self.uu.get_all_available_release_updates( )[1] result['release_update_blocking_components'] = ' '.join( blocking_components or []) what = "querying availability for easy mode" if result['easy_mode']: # updates/available should reflect the need for an update easy_update_available = ucr.is_true('update/available', False) # but dont rely on ucr! update/available is set during univention-upgrade --check # but when was the last time this was run? # release update easy_update_available = easy_update_available or result[ 'release_update_available'] # if no update seems necessary perform a real (expensive) check nonetheless easy_update_available = easy_update_available or self.uu.component_update_available( ) result['easy_update_available'] = bool(easy_update_available) else: result['easy_update_available'] = False # Component counts are now part of the general 'status' data. what = "counting components" c_count = 0 e_count = 0 for comp in self.uu.get_all_components(): c_count = c_count + 1 if ucr.is_true('repository/online/component/%s' % (comp, ), False): e_count = e_count + 1 result['components'] = c_count result['enabled'] = e_count # HACK: the 'Updates' form polls on the serial file # to refresh itself. Including the serial value # into the form helps us to have a dependent field # that can trigger the refresh of the "Releases" # combobox and the 'package updates available' field. result['serial'] = self._serial_file.timestamp() except Exception as exc: # FIXME: don't catch everything typ = str(type(exc)).strip('<>') msg = '[while %s] [%s] %s' % (what, typ, exc) result['message'] = msg result['status'] = 1 MODULE.error(msg) self.finished(request.id, [result])
def __init__(self, **kwargs): ucr.load() self._is_master = ucr.get('server/role') == 'domaincontroller_master' self._updates_available = ucr.is_true('update/available') self._fqdn = '%s.%s' % (ucr.get('hostname'), ucr.get('domainname')) super(UMCError, self).__init__('\n'.join(self._error_msg()), **kwargs)
import notifier import notifier.signals import notifier.threads from PyQt4.QtCore import QObject, pyqtSlot import italc import ldap import sip from ldap.filter import filter_format from ldap.dn import explode_dn _ = Translation('ucs-school-umc-computerroom').translate ITALC_DEMO_PORT = int(ucr.get('ucsschool/umc/computerroom/demo/port', 11400)) ITALC_VNC_PORT = int(ucr.get('ucsschool/umc/computerroom/vnc/port', 11100)) ITALC_VNC_UPDATE = float(ucr.get('ucsschool/umc/computerroom/vnc/update', 1)) ITALC_CORE_UPDATE = max( 1, int(ucr.get('ucsschool/umc/computerroom/core/update', 1))) ITALC_CORE_TIMEOUT = max( 1, int(ucr.get('ucsschool/umc/computerroom/core/timeout', 10))) italc.ItalcCore.init() italc.ItalcCore.config.setLogLevel(italc.Logger.LogLevelDebug) italc.ItalcCore.config.setLogToStdErr(True) italc.ItalcCore.config.setLogFileDirectory('/var/log/univention/') italc.Logger('ucs-school-umc-computerroom') italc.ItalcCore.config.setLogonAuthenticationEnabled(False)
def _finished(data): """ Process asynchronous UVMM DOMAIN_INFO answer. Convert UVMM protocol to JSON. """ node_uri = urlsplit(request.options['domainURI']) uri, _uuid = urldefrag(request.options['domainURI']) json = object2dict(data) # re-arrange a few attributes for the frontend # annotations for key in json['annotations']: if key == 'uuid': continue json[key] = json['annotations'][key] # STOP here if domain is not available if not json['available']: MODULE.info('Domain is not available: %s' % (json, )) self.finished(request.id, json) return # interfaces (fake the special type network:<source>) for iface in json['interfaces']: if iface['type'] == Interface.TYPE_NETWORK: iface['type'] = 'network:' + iface['source'] # disks for disk in json['disks']: disk['volumeFilename'] = os.path.basename( disk['source']) if disk['pool'] else disk['source'] disk['paravirtual'] = disk['target_bus'] in ('virtio', ) disk['volumeType'] = disk['type'] # graphics if json['graphics']: try: gfx = json['graphics'][0] json['vnc'] = True json['vnc_host'] = None json['vnc_port'] = None json['kblayout'] = gfx['keymap'] json['vnc_remote'] = gfx['listen'] == '0.0.0.0' json['vnc_password'] = gfx['passwd'] # vnc_password will not be send to frontend port = int(json['graphics'][0]['port']) if port == -1: raise ValueError(json['graphics'][0]['port']) host = node_uri.netloc vnc_link_format = ucr.get('uvmm/umc/vnc/host', 'IPv4') or '' match = Domains.RE_VNC.match(vnc_link_format) if match: family, pattern, substs = match.groups() if family: # IPvX family = Domains.SOCKET_FAMILIES[family] regex = re.compile(pattern or '.*') addrs = socket.getaddrinfo(host, port, family, socket.SOCK_STREAM, socket.SOL_TCP) for (family, _socktype, _proto, _canonname, sockaddr) in addrs: host, port = sockaddr[:2] if regex.search(host): break else: raise LookupError(pattern) host = Domains.SOCKET_FORMATS[family] % (host, ) elif substs: # NAME for subst in substs.split(): old, new = subst.split('=', 1) host = host.replace(old, new) elif vnc_link_format: # overwrite all hosts with fixed host host = vnc_link_format json['vnc_host'] = host json['vnc_port'] = port except re.error as ex: # port is not valid MODULE.warn('Invalid VNC regex: %s' % (ex, )) except socket.gaierror as ex: MODULE.warn('Invalid VNC host: %s' % (ex, )) except (ValueError, LookupError) as ex: # port is not valid MODULE.warn('Failed VNC lookup: %s' % (ex, )) # profile (MUST be after mapping annotations) profile_dn = json.get('profile') profile = None if profile_dn: for dn, pro in self.profiles: if dn == profile_dn: profile = pro break if profile: json['profileData'] = object2dict(profile) MODULE.info('Got domain description: %s' % (json, )) return json
def _settings_set(self, printMode, internetRule, shareMode, period=None, customRule=None): """Defines settings for a room""" if not self._italc.school or not self._italc.room: raise UMC_Error('no room selected') # find AT jobs for the room and execute it to remove current settings jobs = atjobs.list(extended=True) for job in jobs: if job.comments.get(Instance.ATJOB_KEY, False) == self._italc.room: job.rm() subprocess.call(shlex.split(job.command)) roomInfo = _readRoomInfo(self._italc.roomDN) in_exam_mode = roomInfo.get('exam') # for the exam mode, remove current settings before setting new ones if in_exam_mode and roomInfo.get('cmd'): MODULE.info('unsetting room settings for exam (%s): %s' % (roomInfo['exam'], roomInfo['cmd'])) try: subprocess.call(shlex.split(roomInfo['cmd'])) except (OSError, IOError): MODULE.warn( 'Failed to reinitialize current room settings: %s' % roomInfo['cmd']) _updateRoomInfo(self._italc.roomDN, cmd=None) # reset to defaults. No atjob is necessary. if internetRule == 'none' and shareMode == 'all' and printMode == 'default': self._ruleEndAt = None self.reset_smb_connections() self.reload_cups() return # collect new settings vset = {} vappend = {} vunset = [] vunset_now = [] vextract = [] hosts = self._italc.ipAddresses(students_only=True) # print mode if printMode in ('none', 'all'): vextract.append('samba/printmode/hosts/%s' % printMode) vappend[vextract[-1]] = hosts vextract.append('cups/printmode/hosts/%s' % printMode) vappend[vextract[-1]] = hosts vunset.append('samba/printmode/room/%s' % self._italc.room) vset[vunset[-1]] = printMode else: vunset_now.append('samba/printmode/room/%s' % self._italc.room) # share mode if shareMode == 'home': vunset.append('samba/sharemode/room/%s' % self._italc.room) vset[vunset[-1]] = shareMode vextract.append('samba/othershares/hosts/deny') vappend[vextract[-1]] = hosts vextract.append('samba/share/Marktplatz/hosts/deny') vappend[vextract[-1]] = hosts else: vunset_now.append('samba/sharemode/room/%s' % self._italc.room) # internet rule if internetRule != 'none': vextract.append('proxy/filter/room/%s/ip' % self._italc.room) vappend[vextract[-1]] = hosts if internetRule == 'custom': # remove old rules i = 1 while True: var = 'proxy/filter/setting-user/%s/domain/whitelisted/%d' % ( self._username, i) if var in ucr: vunset_now.append(var) i += 1 else: break vunset.append('proxy/filter/room/%s/rule' % self._italc.room) vset[vunset[-1]] = self._username vset['proxy/filter/setting-user/%s/filtertype' % self._username] = 'whitelist-block' i = 1 for domain in (customRule or '').split('\n'): MODULE.info('Setting whitelist entry for domain %s' % domain) if not domain: continue parsed = urlparse.urlsplit(domain) MODULE.info('Setting whitelist entry for domain %s' % str(parsed)) if parsed.netloc: vset[ 'proxy/filter/setting-user/%s/domain/whitelisted/%d' % (self._username, i)] = parsed.netloc i += 1 elif parsed.path: vset[ 'proxy/filter/setting-user/%s/domain/whitelisted/%d' % (self._username, i)] = parsed.path i += 1 else: vunset.append('proxy/filter/room/%s/rule' % self._italc.room) vset[vunset[-1]] = internetRule else: vunset_now.append('proxy/filter/room/%s/ip' % self._italc.room) vunset_now.append('proxy/filter/room/%s/rule' % self._italc.room) # write configuration # remove old values handler_unset(vunset_now) # append values ucr.load() MODULE.info('Merging UCR variables') for key, value in vappend.items(): if ucr.get(key): old = set(ucr[key].split(' ')) MODULE.info('Old value: %s' % old) else: old = set() MODULE.info('Old value empty') new = set(value) MODULE.info('New value: %s' % new) new = old.union(new) MODULE.info('Merged value of %s: %s' % (key, new)) if not new: MODULE.info('Unset variable %s' % key) vunset.append(key) else: vset[key] = ' '.join(new) # Workaround for bug 30450: # if samba/printmode/hosts/none is not set but samba/printmode/hosts/all then all other hosts # are unable to print on samba shares. Solution: set empty value for .../none if no host is on deny list. varname = 'samba/printmode/hosts/none' if varname not in vset: ucr.load() if not ucr.get(varname): vset[varname] = '""' else: # remove empty items ('""') in list vset[varname] = ' '.join( [x for x in vset[varname].split(' ') if x != '""']) if varname in vunset: vunset.remove(varname) # set values ucr_vars = sorted('%s=%s' % x for x in vset.items()) MODULE.info('Writing room rules: %s' % '\n'.join(ucr_vars)) handler_set(ucr_vars) # create at job to remove settings unset_vars = ['-r %s' % quote(x) for x in vunset] MODULE.info('Will remove: %s' % ' '.join(unset_vars)) extract_vars = ['-e %s' % quote(x) for x in vextract] MODULE.info('Will extract: %s' % ' '.join(extract_vars)) cmd = '/usr/share/ucs-school-umc-computerroom/ucs-school-deactivate-rules %s %s %s' % ( ' '.join(unset_vars), ' '.join(extract_vars), ' '.join( quote(x) for x in hosts)) MODULE.info('command for reinitialization is: %s' % (cmd, )) if in_exam_mode: # Command for the exam mode to be executed manually when changing the settings again... _updateRoomInfo(self._italc.roomDN, cmd=cmd) else: starttime = datetime.datetime.now() MODULE.info('Now: %s' % starttime) MODULE.info('Endtime: %s' % period) starttime = starttime.replace(hour=period.hour, minute=period.minute, second=0, microsecond=0) while starttime < datetime.datetime.now( ): # prevent problems due to intra-day limit starttime += datetime.timedelta(days=1) # AT job for the normal case MODULE.info('Remove settings at %s' % (starttime, )) atjobs.add(cmd, starttime, {Instance.ATJOB_KEY: self._italc.room}) self._ruleEndAt = starttime self.reset_smb_connections() self.reload_cups()
from datetime import datetime from univention.lib import atjobs from univention.lib.i18n import Translation from univention.management.console.log import MODULE from univention.management.console.config import ucr # import univention.admin.modules as udm_modules import univention.admin.uexceptions as udm_exceptions import ucsschool.lib.models _ = Translation('ucs-school-umc-distribution').translate DISTRIBUTION_CMD = '/usr/lib/ucs-school-umc-distribution/umc-distribution' DISTRIBUTION_DATA_PATH = ucr.get('ucsschool/datadistribution/cache', '/var/lib/ucs-school-umc-distribution') POSTFIX_DATADIR_SENDER = ucr.get('ucsschool/datadistribution/datadir/sender', 'Unterrichtsmaterial') POSTFIX_DATADIR_SENDER_PROJECT_SUFFIX = ucr.get( 'ucsschool/datadistribution/datadir/sender/project/suffix', '-Ergebnisse') POSTFIX_DATADIR_RECIPIENT = ucr.get( 'ucsschool/datadistribution/datadir/recipient', 'Unterrichtsmaterial') TYPE_USER = '******' TYPE_GROUP = 'GROUP' TYPE_PROJECT = 'PROJECT' class DistributionException(Exception): pass
def adconnector_save(self, request): """Saves the Active Directory connection configuration options: Host_IP: IP address of the AD server LDAP_Host: hostname of the AD server LDAP_Base: LDAP base of the AD server LDAP_BindDN: LDAP DN to use for authentication KerberosDomain: kerberos domain PollSleep: time in seconds between polls RetryRejected: how many time to retry a synchronisation MappingSyncMode: synchronisation mode MappingGroupLanguage: language of the AD server return: { 'success' : (True|False), 'message' : <details> } """ for umckey, ucrkey, default in Instance.OPTION_MAPPING: val = request.options.get(umckey, default) if val: if isinstance(val, bool): val = 'yes' if val else 'no' MODULE.info('Setting %s=%s' % (ucrkey, val)) univention.config_registry.handler_set([u'%s=%s' % (ucrkey, val)]) ucr.load() if ucr.get('connector/ad/ldap/ldaps'): MODULE.info('Unsetting connector/ad/ldap/ldaps') univention.config_registry.handler_unset([u'connector/ad/ldap/ldaps']) if ucr.get('connector/ad/ldap/port') == '636': MODULE.info('Setting ldap port to 389') univention.config_registry.handler_set([u'connector/ad/ldap/port=389']) if not request.options.get('LDAP_Password') in (None, '', DO_NOT_CHANGE_PWD): fn = ucr.get('connector/ad/ldap/bindpw', FN_BINDPW) try: with open(fn, 'w') as fd: fd.write(request.options.get('LDAP_Password')) os.chmod(fn, 0o600) os.chown(fn, 0, 0) univention.config_registry.handler_set([u'connector/ad/ldap/bindpw=%s' % fn]) except Exception as e: MODULE.info('Saving bind password failed (filename=%(fn)s ; exception=%(exception)s)' % {'fn': fn, 'exception': str(e.__class__)}) self.finished(request.id, {'success': False, 'message': _('Saving bind password failed (filename=%(fn)s ; exception=%(exception)s)') % {'fn': fn, 'exception': str(e.__class__)}}) return ssldir = '/etc/univention/ssl/%s' % request.options.get('LDAP_Host') if not os.path.exists(ssldir): self._create_certificate(request) return # enter a static host entry such that the AD server's FQDN can be resolved univention.config_registry.handler_set([u'hosts/static/%(Host_IP)s=%(LDAP_Host)s' % request.options]) # check for SSL support on AD side if admember.server_supports_ssl(server=request.options.get('LDAP_Host')): MODULE.process('Enabling SSL...') admember.enable_ssl() else: MODULE.warn('SSL is not supported') admember.disable_ssl() # UCR variables are set, and now we can try to guess the language of # the AD domain ad_lang = guess_ad_domain_language() univention.config_registry.handler_set([u'connector/ad/mapping/group/language=%s' % ad_lang]) self.finished(request.id, {'success': True, 'message': _('Active Directory connection settings have been saved.')})
def _thread(): # make sure that a project with the same name does not exist directory = request.options['directory'] # get absolute path of project file and test for existance fn_test_project = util.distribution.Project.sanitize_project_filename( directory) if os.path.exists(fn_test_project): raise UMC_Error( _('An exam with the name "%s" already exists. Please choose a different name for the exam.' ) % (directory, )) # validate the project data and save project my.project = util.distribution.Project( dict( name=directory, description=request.options['name'], files=request.options.get('files'), sender=sender, )) my.project.validate() my.project.save() # copy files into project directory if self._tmpDir: for ifile in my.project.files: isrc = os.path.join(self._tmpDir, ifile) itarget = os.path.join(my.project.cachedir, ifile) if os.path.exists(isrc): # copy file to cachedir shutil.move(isrc, itarget) os.chown(itarget, 0, 0) # open a new connection to the master UMC try: master = ucr['ldap/master'] client = Client(master) client.authenticate_with_machine_account() except (ConnectionError, HTTPError) as exc: MODULE.error('Could not connect to UMC on %s: %s' % (master, exc)) raise UMC_Error( _('Could not connect to master server %s.') % ucr.get('ldap/master')) # mark the computer room for exam mode progress.component( _('Preparing the computer room for exam mode...')) client.umc_command( 'schoolexam-master/set-computerroom-exammode', dict( school=request.options['school'], roomdn=request.options['room'], )).result # FIXME: no error handling progress.add_steps(5) # read all recipients and fetch all user objects users = [] for idn in request.options['recipients']: ientry = util.distribution.openRecipients(idn, ldap_user_read) if not ientry: continue # recipients can in theory be users or groups members = [] if isinstance(ientry, util.distribution.User): members = [ientry] elif isinstance(ientry, util.distribution.Group): members = ientry.members for entry in members: # ignore exam users user = User.from_dn(entry.dn, None, ldap_user_read) if not user.is_exam_student(ldap_user_read): users.append(entry) # start to create exam user accounts progress.component(_('Preparing exam accounts')) percentPerUser = 25.0 / (1 + len(users)) examUsers = set() student_dns = set() usersReplicated = set() for iuser in users: progress.info( '%s, %s (%s)' % (iuser.lastname, iuser.firstname, iuser.username)) try: ires = client.umc_command( 'schoolexam-master/create-exam-user', dict( school=request.options['school'], userdn=iuser.dn, )).result examuser_dn = ires.get('examuserdn') examUsers.add(examuser_dn) student_dns.add(iuser.dn) MODULE.info('Exam user has been created: %r' % examuser_dn) except (ConnectionError, HTTPError) as exc: MODULE.warn( 'Could not create exam user account for %r: %s' % (iuser.dn, exc)) # indicate the the user has been processed progress.add_steps(percentPerUser) client.umc_command( 'schoolexam-master/add-exam-users-to-groups', dict( users=list(student_dns), school=request.options['school'], )) progress.add_steps(percentPerUser) # wait for the replication of all users to be finished progress.component(_('Preparing user home directories')) recipients = [] # list of User objects for all exam users openAttempts = 30 * 60 # wait max. 30 minutes for replication while (len(examUsers) > len(usersReplicated)) and (openAttempts > 0): openAttempts -= 1 MODULE.info( 'waiting for replication to be finished, %s user objects missing' % (len(examUsers) - len(usersReplicated))) for idn in examUsers - usersReplicated: try: ldap_user_read.get(idn, required=True) except ldap.NO_SUCH_OBJECT: continue # not replicated yet iuser = util.distribution.openRecipients( idn, ldap_user_read) if not iuser: continue # not a users/user object MODULE.info('user has been replicated: %s' % idn) # call hook scripts if 0 != subprocess.call([ '/bin/run-parts', CREATE_USER_POST_HOOK_DIR, '--arg', iuser.username, '--arg', iuser.dn, '--arg', iuser.homedir ]): raise ValueError( 'failed to run hook scripts for user %r' % (iuser.username)) # store User object in list of final recipients recipients.append(iuser) # mark the user as replicated usersReplicated.add(idn) progress.info( '%s, %s (%s)' % (iuser.lastname, iuser.firstname, iuser.username)) progress.add_steps(percentPerUser) # wait a second time.sleep(1) progress.add_steps(percentPerUser) if openAttempts <= 0: MODULE.error( 'replication timeout - %s user objects missing: %r ' % ((len(examUsers) - len(usersReplicated)), (examUsers - usersReplicated))) raise UMC_Error( _('Replication timeout: could not create all exam users')) # update the final list of recipients my.project.recipients = recipients my.project.save() # update local NSS group cache if ucr.is_true('nss/group/cachefile', True): cmd = ['/usr/lib/univention-pam/ldap-group-to-file.py'] if ucr.is_true('nss/group/cachefile/check_member', False): cmd.append('--check_member') MODULE.info('Updating local nss group cache...') if subprocess.call(cmd): MODULE.error('Updating local nss group cache failed: %s' % ' '.join(cmd)) else: MODULE.info( 'Update of local nss group cache finished successfully.' ) # distribute exam files progress.component(_('Distributing exam files')) progress.info('') my.project.distribute() progress.add_steps(20) # prepare room settings via UMCP... # first step: acquire room # second step: adjust room settings progress.component(_('Prepare room settings')) try: user_client = Client(None, self.username, self.password) except (ConnectionError, HTTPError) as exc: MODULE.warn('Authentication failed: %s' % (exc, )) raise UMC_Error(_('Could not connect to local UMC server.')) room = request.options['room'] MODULE.info('Acquire room: %s' % (room, )) user_client.umc_command('computerroom/room/acquire', dict( room=request.options['room'], )).result progress.add_steps(1) MODULE.info('Adjust room settings:\n%s' % '\n'.join( [' %s=%s' % (k, v) for k, v in request.options.iteritems()])) user_client.umc_command( 'computerroom/exam/start', dict( room=room, examDescription=request.options['name'], exam=directory, examEndTime=request.options.get('examEndTime'), )).result progress.add_steps(4) user_client.umc_command( 'computerroom/settings/set', dict( room=room, internetRule=request.options['internetRule'], customRule=request.options.get('customRule'), shareMode=request.options['shareMode'], printMode='default', )).result progress.add_steps(5)
def change(self, role, ip, netmask, oldip=None): '''Return a dict with all necessary values for ipchange read from the current status of the system.''' # ignore link local addresses (no DHCP address received) network = ipaddress.IPv4Network(u'%s/%s' % (ip, netmask), False) if network.is_link_local: MODULE.error('Ignore link local address change.') return lo, position = univention.admin.uldap.getAdminConnection() hmodule = univention.admin.modules.get('dns/host_record') cmodule = univention.admin.modules.get('computers/%s' % (role,)) # check if already used res = univention.admin.modules.lookup(hmodule, None, lo, scope='sub', filter=filter_format('aRecord=%s', (ip,))) if res: used_by = [] for i in res: if 'name' in i: used_by.append(i['name']) raise BadRequest('The IP address is already in used by host record(s) for: %s' % ', '.join(used_by)) # do we have a forward zone for this IP address? if oldip and oldip != ip: fmodule = univention.admin.modules.get('dns/forward_zone') for forwardobject in univention.admin.modules.lookup(fmodule, None, lo, scope='sub', superordinate=None, filter=filter_format('(aRecord=%s)', (oldip,))): forwardobject.open() forwardobject['a'].remove(oldip) forwardobject['a'].append(ip) forwardobject.modify() # remove old DNS reverse entries with old IP server = cmodule.object(None, lo, position, self.user_dn) server.open() current_ips = server['ip'] for e in server['dnsEntryZoneReverse']: if e[1] in current_ips: server['dnsEntryZoneReverse'].remove(e) # change IP server['ip'] = ip MODULE.info('Change IP to %s' % (ip,)) server.modify() # do we have a new reverse zone for this IP address? rmodule = univention.admin.modules.get('dns/reverse_zone') parts = network.network_address.exploded.split('.') while parts[-1] == '0': parts.pop() while parts: subnet = '.'.join(parts) parts.pop() filter = filter_format('(subnet=%s)', (subnet,)) reverseobject = univention.admin.modules.lookup(rmodule, None, lo, scope='sub', superordinate=None, filter=filter) if reverseobject: server = cmodule.object(None, lo, position, self.user_dn) server.open() server['dnsEntryZoneReverse'].append([reverseobject[0].dn, ip]) server.modify() break # Change ucs-sso entry # FIXME: this should be done for UCS-in-AD domains as well! ucr.load() sso_fqdn = ucr.get('ucs/server/sso/fqdn') if ucr.is_true('ucs/server/sso/autoregistraton', True): fmodule = univention.admin.modules.get('dns/forward_zone') forwardobjects = univention.admin.modules.lookup(fmodule, None, lo, scope='sub', superordinate=None, filter=None) for forwardobject in forwardobjects: zone = forwardobject.get('zone') if not sso_fqdn.endswith(zone): continue sso_name = sso_fqdn[:-(len(zone) + 1)] for current_ip in current_ips: records = univention.admin.modules.lookup(hmodule, None, lo, scope='sub', superordinate=forwardobject, filter=filter_format('(&(relativeDomainName=%s)(aRecord=%s))', (sso_name, current_ip))) for record in records: record.open() if oldip in record['a']: record['a'].remove(oldip) record['a'].append(ip) record.modify()
from univention.management.console.config import ucr from univention.management.console.modules.decorators import sanitize, simple_response from univention.management.console.modules.sanitizers import StringSanitizer from univention.management.console.modules import UMC_Error from univention.management.console.ldap import get_user_connection, get_machine_connection, get_admin_connection, machine_connection from .tokendb import TokenDB, MultipleTokensInDB from .sending import get_plugins as get_sending_plugins _ = Translation('univention-self-service-passwordreset-umc').translate TOKEN_VALIDITY_TIME = 3600 MEMCACHED_SOCKET = "/var/lib/univention-self-service-passwordreset-umc/memcached.socket" MEMCACHED_MAX_KEY = 250 SELFSERVICE_MASTER = ucr.get("self-service/backend-server", ucr.get("ldap/master")) IS_SELFSERVICE_MASTER = '%s.%s' % (ucr.get('hostname'), ucr.get('domainname')) == SELFSERVICE_MASTER DEREGISTRATION_TIMESTAMP_FORMATTING = '%Y%m%d%H%M%SZ' if IS_SELFSERVICE_MASTER: try: from univention.management.console.modules.udm.syntax import widget from univention.management.console.modules.udm.udm_ldap import UDM_Error, UDM_Module from univention.udm import UDM, NoObject except ImportError as exc: MODULE.error('Could not load udm module: %s' % (exc,)) widget = None def forward_to_master(func):