def register_email_server(email_srv_type, email_srv_name, description): email_server = Email.EmailServer(db) try: email_srv_type = const.EmailServerType(email_srv_type) int(email_srv_type) except Errors.NotFoundError: logger.error("Unknown email server type: %s. Entry skipped", email_srv_type) return try: email_server.find_by_name(email_srv_name) email_server.email_server_type=email_srv_type email_server.description=description except Errors.NotFoundError: try: host = Factory.get('Host')(db) host.find_by_name(email_srv_name) email_server.populate(email_srv_type, parent=host.entity_id) except Errors.NotFoundError: email_server.populate(email_srv_type, name=email_srv_name, description=description) try: email_server.write_db() logger.debug("Registered email server %s", email_srv_name) except Errors.DatabaseException: logger.error("Could not write to the Cerebrum-database") sys.exit(3)
def _pick_email_server(self): # We try to spread the usage across servers, but want a # random component to the choice of server. The choice is # weighted, although the determination of weights happens # externally to Cerebrum since it is a relatively # expensive operation (can take several seconds). # Typically the weight will vary with the amount of users # already assigned, the disk space available or similar # criteria. # # Servers MUST have a weight trait to be considered for # allocation. es = Email.EmailServer(self._db) user_weight = {} total_weight = 0 for row in es.list_traits(self.const.trait_email_server_weight): total_weight += row['numval'] user_weight[row['entity_id']] = row['numval'] pick = random.randint(0, total_weight - 1) for svr_id in user_weight: pick -= user_weight[svr_id] if pick <= 0: break return svr_id
def get_email_server(account_id, local_db=db): """Return Host object for account's mail server.""" et = Email.EmailTarget(local_db) et.find_by_target_entity(account_id) server = Email.EmailServer(local_db) server.find(et.email_server_id) return server
def process_email_srv_data(uname, account_id, email_srv): if email_srv in ['NOMAIL', 'NOLOCAL']: logger.error("Bad email server %s for %s", email_srv, uname) return None email_server = Email.EmailServer(db) email_server_target = Email.EmailServerTarget(db) email_target = Email.EmailTarget(db) email_server.find_by_name(email_srv) email_server_id = email_server.entity_id try: email_target.find_by_email_target_attrs( target_entity_id=int(account_id)) except Errors.NotFoundError: logger.error("No email target for %s", uname) return None try: email_server_target.find(email_target.entity_id) logger.debug("Email-server is registered %s for %s", email_srv, uname) except Errors.NotFoundError: email_server_target.clear() email_server_target.populate(email_server_id, parent=email_target) email_server_target.write_db() logger.debug("Populated email-server %s for %s", email_srv, uname) email_server.clear() email_server_target.clear() email_target.clear()
def proc_email_create(r): hq = get_email_hardquota(r['entity_id']) es = None if r['destination_id']: es = Email.EmailServer(db) es.find(r['destination_id']) return (cyrus_create(r['entity_id'], host=es) and cyrus_set_quota(r['entity_id'], hq, host=es))
def read_server(self, spread): mail_serv = Email.EmailServer(self._db) for row in mail_serv.list_email_server_ext(): self.serv_id2server[int( row['server_id'])] = [row['server_type'], row['name']] mail_targ = Email.EmailTarget(self._db) for row in mail_targ.list_email_server_targets(): self.targ2server_id[int(row['target_id'])] = int(row['server_id'])
def _get_email_server(self, name): """ Get EmailServer object by name. """ es = Email.EmailServer(self.db) try: if isinstance(name, (int, long)): es.find(name) else: es.find_by_name(name) return es except Errors.NotFoundError: raise CerebrumError("Unknown mail server: %s" % name)
def _email_info_detail(self, acc): """ Get quotas from Cerebrum, and usage from Cyrus. """ # NOTE: Very similar to hiof and uio info = [] eq = Email.EmailQuota(self.db) # Get quota and usage try: eq.find_by_target_entity(acc.entity_id) et = Email.EmailTarget(self.db) et.find_by_target_entity(acc.entity_id) es = Email.EmailServer(self.db) es.find(et.email_server_id) if es.email_server_type == self.const.email_server_type_cyrus: used = 'N/A' limit = None pw = self.db._read_password(cereconf.CYRUS_HOST, cereconf.CYRUS_ADMIN) try: cyrus = imaplib.IMAP4(es.name) # IVR 2007-08-29 If the server is too busy, we do not want # to lock the entire bofhd. # 5 seconds should be enough cyrus.socket().settimeout(5) cyrus.login(cereconf.CYRUS_ADMIN, pw) res, quotas = cyrus.getquota("user." + acc.account_name) cyrus.socket().settimeout(None) if res == "OK": for line in quotas: try: folder, qtype, qused, qlimit = line.split() if qtype == "(STORAGE": used = str(int(qused)/1024) limit = int(qlimit.rstrip(")"))/1024 except ValueError: # line.split fails e.g. because quota isn't set # on server folder, junk = line.split() self.logger.warning( "No IMAP quota set for '%s'" % acc.account_name) used = "N/A" limit = None except (TimeoutException, socket.error): used = 'DOWN' except ConnectException, e: used = str(e) except imaplib.IMAP4.error, e: used = 'DOWN'
def email_replace_server(self, operator, user, server_name): """ Replace the server for an email target. """ if not self.ba.is_postmaster(operator.get_entity_id()): raise PermissionDenied("Currently limited to superusers") et = self._get_email_target_for_account(user) es = Email.EmailServer(self.db) es.clear() try: es.find_by_name(server_name) except Errors.NotFoundError: raise CerebrumError("No such server: '%s'" % server_name) if et.email_server_id != es.entity_id: et.email_server_id = es.entity_id try: et.write_db() except self.db.DatabaseError, m: raise CerebrumError("Database error: %s" % m)
def _update_email_server(self, server_name): es = Email.EmailServer(self._db) et = Email.EmailTarget(self._db) es.find_by_name(server_name) try: et.find_by_email_target_attrs(target_entity_id = self.entity_id) except Errors.NotFoundError: # Not really sure about this. it is done at UiO, but maybe it is not # right to make en email_target if one is not found?? et.clear() et.populate(self.const.email_target_account, self.entity_id, self.const.entity_account) et.write_db() et.email_server_id = es.entity_id et.write_db() return et
def _update_email_server(self): es = Email.EmailServer(self._db) et = Email.EmailTarget(self._db) if self.is_employee(): server_name = 'postboks' else: server_name = 'student_placeholder' es.find_by_name(server_name) try: et.find_by_email_target_attrs(target_entity_id=self.entity_id) except Errors.NotFoundError: et.populate(self.const.email_target_account, self.entity_id, self.const.entity_account) et.write_db() if not et.email_server_id: et.email_server_id = es.entity_id et.write_db() return et
def _email_info_basic(self, acc, et): """ Basic email info. """ info = {} data = [ info, ] if et.email_target_alias is not None: info['alias_value'] = et.email_target_alias info["account"] = acc.account_name if et.email_server_id: es = Email.EmailServer(self.db) es.find(et.email_server_id) info["server"] = es.name info["server_type"] = "N/A" else: info["server"] = "<none>" info["server_type"] = "N/A" return data
def email_move(self, operator, uname, server): acc = self._get_account(uname) self.ba.can_email_move(operator.get_entity_id(), acc) et = Email.EmailTarget(self.db) et.find_by_target_entity(acc.entity_id) old_server = et.email_server_id es = Email.EmailServer(self.db) try: es.find_by_name(server) except Errors.NotFoundError: raise CerebrumError("%r is not registered as an e-mail server" % server) if old_server == es.entity_id: raise CerebrumError("User is already at %s" % server) et.email_server_id = es.entity_id et.write_db() return "OK, updated e-mail server for %s (to %s)" % (uname, server)
def _update_email_server(self, spread, force=False): """ Update `email_server` for the Account to a value based on spread. The `email_server` for the account's `EmailTarget` is set to an appropriate server, depending on the given spread. IMAP users will for instance be given an IMAP server, while Exchange 2013 users gets an Exchange 2013 server. Note: If the account doesn't have an `EmailTarget`, a new one will be created, which normally affects the mail systems. Use with care. :param _SpreadCode spread: The given spread, which could affect what server that gets chosen. :param bool force: If False, the server is not updated if it is already set. :rtype: `Email.EmailTarget` :return: The affected `EmailTarget`. """ es = Email.EmailServer(self._db) et = Email.EmailTarget(self._db) server_name = 'mail-imap2' if spread in (int(self.const.spread_exchange_account), int(self.const.spread_uia_office_365)): server_name = 'outlook.uia.no' if spread == int(self.const.spread_exchange_acc_old): server_name = 'exchkrs01.uia.no' es.find_by_name(server_name) try: et.find_by_email_target_attrs(target_entity_id=self.entity_id) except Errors.NotFoundError: # Not really sure about this. # It is done at UiO, but maybe it is not # right to make en email_target if one is not found?? et.clear() et.populate(self.const.email_target_account, self.entity_id, self.const.entity_account) et.write_db() if force or not et.email_server_id: et.email_server_id = es.entity_id et.write_db() return et
def email_replace_server(self, operator, user, server_name): """ Replace the server for an email target. """ self.ba.can_email_replace_server(operator.get_entity_id()) et = self._get_email_target_for_account(user) es = Email.EmailServer(self.db) es.clear() try: es.find_by_name(server_name) except Errors.NotFoundError: raise CerebrumError("No such server: %r" % server_name) if et.email_server_id != es.entity_id: et.email_server_id = es.entity_id try: et.write_db() except self.db.DatabaseError as m: raise CerebrumError("Database error: %s" % m) else: raise CerebrumError( "No change, from-server equeals to-server: %r" % server_name) return { 'new_server': server_name, }
def proc_email_delete(r): try: uname = get_account(r['entity_id']).account_name except Errors.NotFoundError: logger.error("bofh_email_delete: %d: user not found", r['entity_id']) return False # The database contains the new host, so the id of the server # to remove from is passed in destination_id. server = Email.EmailServer(db) try: server.find(r['destination_id']) except Errors.NotFoundError: logger.error("bofh_email_delete: %d: target server not found", r['destination_id']) return False account = get_account(r['entity_id']) # If the account is deleted, we assume that delete_user has # already bumped the generation. Otherwise, we bump the # generation ourselves generation = account.get_trait(const.trait_account_generation) update_gen = False if generation: generation = generation['numval'] else: generation = 0 if not account.is_deleted(): generation += 1 update_gen = True if cyrus_delete(server, uname, generation): if update_gen: account.populate_trait(const.trait_account_generation, numval=generation) account.write_db() return True else: return False
def generate_email_data(): all_accounts = account.search(spread=constants.spread_email_account) all_email_data = {} es = Email.EmailServer(db) for k in all_accounts: account.clear() account.find(k['account_id']) email_server = None # fetch primary address try: primary = account.get_primary_mailaddress() except Errors.NotFoundError: logger.warn("No primary address for %s", account.account_name) continue # find email target for this account et = Email.EmailTarget(db) et.clear() try: et.find_by_target_entity(account.entity_id) except Errors.NotFoundError: logger.warn("No e-mail target for %s", account.account_name) continue # fetch all valid adresses for account valid_addrs = get_valid_email_addrs(et) if not et.email_server_id: logger.warn("No server registered for %s", account.account_name) email_server = "N/A" else: email_server = et.email_server_id if email_server != "N/A": es.clear() es.find(et.email_server_id) email_server = es.name valid = "valid:" for a in valid_addrs: valid = valid + a + ':' all_email_data[account.account_name] = 'default:' + primary + ':' + valid + email_server return all_email_data
def email_move_child(host, r): local_db = Utils.Factory.get('Database')() local_co = Utils.Factory.get('Constants')(local_db) r_id = r['request_id'] if not is_valid_request(r_id, local_db=local_db, local_co=local_co): return if dependency_pending(r['state_data'], local_db=local_db, local_co=local_co): logger.debug("Request '%d' still has deps: '%s'.", r_id, r['state_data']) return try: acc = get_account(r['entity_id'], local_db=local_db) except Errors.NotFoundError: logger.error("email_move: user %d not found", r['entity_id']) return old_server = get_email_server(r['entity_id'], local_db=local_db) new_server = Email.EmailServer(local_db) new_server.find(r['destination_id']) if old_server.entity_id == new_server.entity_id: logger.error("Trying to move %s from " % acc.account_name + "and to the same server! Deleting request") br = BofhdRequests(local_db, local_co) br.delete_request(request_id=r_id) local_db.commit() return if not email_delivery_stopped(acc.account_name): logger.debug("E-mail delivery not stopped for %s", acc.account_name) return logger.debug("User being moved: '%s'.", acc.account_name) reqlock = RequestLockHandler() if not reqlock.grab(r_id): return # Disable quota while copying so the move doesn't fail cyrus_set_quota(acc.entity_id, 0, host=new_server, local_db=local_db) # Call the script cmd = [SSH_CMD, "cerebrum@%s" % host, cereconf.IMAPSYNC_SCRIPT, '--user1', acc.account_name, '--host1', old_server.name, '--user2', acc.account_name, '--host2', new_server.name, '--authusing', cereconf.CYRUS_ADMIN, '--passfile1', '/etc/cyrus.pw', '--useheader', 'Message-ID', '--regexmess', 's/\\0/ /g', '--ssl', '--subscribe', '--nofoldersizes'] proc = subprocess.Popen(cmd, capturestderr=True, bufsize=10240, close_fds=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) pid = proc.pid logger.debug("Called cmd(%d): '%s'", pid, cmd) proc.stdin.close() # Stolen from Utils.py:spawn_and_log_output() descriptor = {proc.stdout: logger.debug, proc.stderr: logger.info} while descriptor: # select() is called for _every_ line, since we can't inspect # the buffering in Python's file object. This works OK since # select() will return "readable" for an unread EOF, and # Python won't read the EOF until the buffers are exhausted. ready, x, x = select(descriptor.keys(), [], []) for fd in ready: line = fd.readline() if line == '': fd.close() del descriptor[fd] else: descriptor[fd]("[%d] %s" % (pid, line.rstrip())) status = proc.wait() if status == EXIT_SUCCESS: logger.debug("[%d] Completed successfully", pid) elif os.WIFSIGNALED(status): # The process was killed by a signal. sig = os.WTERMSIG(status) logger.warning('[%d] Command "%r" was killed by signal %d', pid, cmd, sig) return else: # The process exited with an exit status sig = os.WSTOPSIG(status) logger.warning("[%d] Return value was %d from command %r", pid, sig, cmd) return # Need move SIEVE filters as well cmd = [cereconf.MANAGESIEVE_SCRIPT, '-v', '-a', cereconf.CYRUS_ADMIN, '-p', pwfile, acc.account_name, old_server.name, new_server.name] if Utils.spawn_and_log_output( cmd, connect_to=[old_server.name, new_server.name]) != 0: logger.warning('%s: managesieve_sync failed!', acc.account_name) return logger.info('%s: managesieve_sync completed successfully', acc.account_name) # The move was successful, update the user's server # Now set the correct quota. hq = get_email_hardquota(acc.entity_id, local_db=local_db) cyrus_set_quota(acc.entity_id, hq, host=new_server, local_db=local_db) et = Email.EmailTarget(local_db) et.find_by_target_entity(acc.entity_id) et.email_server_id = new_server.entity_id et.write_db() # We need to delete this request before adding the # delete to avoid triggering the conflicting request # test. br = BofhdRequests(local_db, local_co) br.delete_request(request_id=r_id) local_db.commit() br.add_request(r['requestee_id'], r['run_at'], local_co.bofh_email_delete, r['entity_id'], old_server.entity_id) local_db.commit() logger.info("%s: move_email success.", acc.account_name) reqlock.release()
def process_delete_requests(): br = BofhdRequests(db, const) now = mx.DateTime.now() del_file = [] group = Factory.get('Group')(db) account = Factory.get('Account')(db) spreads = [] posix_home = '' pwd = '*' uid = '' gid = '' gecos = 'gecos' shell = '' line = '' for r in br.get_requests(operation=const.bofh_delete_user): if not is_valid_request(r['request_id']): continue if not keep_running(): break if r['run_at'] > now: continue try: account.clear() account.find(r['entity_id']) except Errors.NotFoundError: logger.error('Could not find account %s' % r['entity_id']) continue if account.is_deleted(): logger.warn("%s is already deleted" % account.account_name) br.delete_request(request_id=r['request_id']) db.commit() continue logger.info("Trying to delete account %s", account.account_name) blockers = account.get_delete_blockers(ignore_group_memberships=True) if blockers: logger.error( 'Manual cleaning required: ' 'Deleting account %s is blocked by: %s', account.account_name, ', '.join(blockers)) continue set_operator(r['requestee_id']) # Set expire_date (do not export data about this account) account.expire_date = br.now logger.debug("expire_date for %s registered as %s", account.account_name, br.now) account.write_db() # check for posix attrs posix_user = Factory.get('PosixUser')(db) posix_user.clear() try: posix_user.find(r['entity_id']) except Errors.NotFoundError: posix_user = None # Deal with all the systems that account data is exported to spreads = account.get_spread() logger.debug("Fetched all spreads for %s, %s", account.account_name, spreads) for row in spreads: # Account is valid in AD, remove account@ad spread if row['spread'] == const.spread_hia_ad_account: try: home = account.get_home(row['spread']) account.set_homedir(current_id=home['homedir_id'], status=const.home_status_archived) account.clear_home(row['spread']) except Errors.NotFoundError: logger.debug( "No home in AD for %s, will remove spread to " "AD only.", account.account_name) account.delete_spread(row['spread']) logger.debug("Deleted account@ad spread for %s", account.account_name) # student-accounts usually have account@ldap, remove this elif row['spread'] == const.spread_ldap_account: account.delete_spread(row['spread']) logger.debug("Deleted account@ldap spread for %s", account.account_name) # An email account exists, remove account@imap spread, # register email account delete elif row['spread'] == const.spread_hia_email: et = Email.EmailTarget(db) try: et.find_by_target_entity(account.entity_id) except Errors.NotFoundError: logger.warn( 'No email target for %s, removing imap spread ' 'only.', account.account_name) logger.debug("Found e-mail target for %s", account.account_name) if et: es = Email.EmailServer(db) es.find(et.email_server_id) del_file.append('EMAIL:' + account.account_name + ':' + es.name) et.email_target_type = const.email_target_deleted et.write_db() account.delete_spread(row['spread']) elif row['spread'] in (const.spread_exchange_account, const.spread_exchange_acc_old): et = Email.EmailTarget(db) try: et.find_by_target_entity(account.entity_id) except Errors.NotFoundError: logger.warn( 'No email target for %s, will remove exchange ' 'spread.', account.account_name) if et: et.email_target_type = const.email_target_deleted et.write_db() logger.info("Deleted e-mail target for %s", account.account_name) account.delete_spread(row['spread']) # Account is valid in nis@hia, remove account@nis spread, # register nis-home delete elif row['spread'] == const.spread_nis_user: if not isinstance(posix_user, Factory.get('PosixUser')): logger.error( "Manual intervention required, no posix " "account is found for account %s", account.account_name) continue posix_home = posix_user.get_posix_home(row['spread']) uid = posix_user.posix_uid gid = posix_user.gid_id shell = posix_user.shell line = string.join([ account.account_name, pwd, str(uid), str(gid), gecos, posix_home, str(shell) ], ':') line = 'NIS:' + line del_file.append(line) try: home = account.get_home(row['spread']) except Errors.NotFoundError: continue account.set_homedir(current_id=home['homedir_id'], status=const.home_status_archived) logger.debug("Set home to archived %s (%s)", home['homedir_id'], row['spread']) account.clear_home(row['spread']) logger.debug("clear_home in %s", row['spread']) account.delete_spread(row['spread']) # Account is valid in nisans@hia, remove account@nisans spread, # register nisans-home delete elif row['spread'] == const.spread_ans_nis_user: posix_home = posix_user.get_posix_home(row['spread']) uid = posix_user.posix_uid gid = posix_user.gid_id shell = posix_user.shell line = string.join([ account.account_name, pwd, str(uid), str(gid), gecos, posix_home, str(shell) ], ':') line = 'NISANS:' + line del_file.append(line) try: home = account.get_home(row['spread']) except Errors.NotFoundError: continue account.set_homedir(current_id=home['homedir_id'], status=const.home_status_archived) logger.debug("Set home to archived %s (%s)", home['homedir_id'], row['spread']) account.clear_home(row['spread']) account.delete_spread(row['spread']) else: account.delete_spread(row['spread']) if posix_user: posix_user.delete_posixuser() # Remove account from all groups for g in group.search(member_id=account.entity_id, indirect_members=False): group.clear() group.find(g['group_id']) group.remove_member(account.entity_id) # All done, remove request, commit results account.write_db() if posix_user: posix_user.write_db() br.delete_request(request_id=r['request_id']) db.commit() return del_file
def proc_email_create(r): es = None if r['destination_id']: es = Email.EmailServer(db) es.find(r['destination_id']) return cyrus_create(r['entity_id'], host=es)
def get_server_name(server_id): if not server_id: return None es = Email.EmailServer(db) es.find(server_id) return u(es.name)
def proc_delete_user(r): spread = default_spread # TODO: check spread in r['state_data'] # # IVR 2007-01-25 We do not care if it's a posix user or not. # TVL 2013-12-09 Except that if it's a POSIX user, it should retain a # personal file group. account, uname, old_host, old_disk = get_account_and_home(r['entity_id'], spread=spread) if account.is_deleted(): logger.warn("%s is already deleted" % uname) return True process_requests.set_operator(db, r['requestee_id']) operator = get_account(r['requestee_id']).account_name et = Email.EmailTarget(db) try: et.find_by_target_entity(account.entity_id) es = Email.EmailServer(db) es.find(et.email_server_id) mail_server = es.name except Errors.NotFoundError: mail_server = '' if not delete_user(uname, old_host, '%s/%s' % (old_disk, uname), operator, mail_server): return False account.expire_date = mx.DateTime.now() account.write_db() try: home = account.get_home(spread) except Errors.NotFoundError: pass else: account.set_homedir(current_id=home['homedir_id'], status=const.home_status_archived) # Remove references in other tables # Note that we preserve the quarantines for deleted users # TBD: Should we have an API function for this? for s in account.get_spread(): account.delete_spread(s['spread']) # Make sure the user's default file group is a personal group group = Utils.Factory.get('Group')(db) default_group = _get_default_group(account.entity_id) pu = Utils.Factory.get('PosixUser')(db) try: pu.find(account.entity_id) except Errors.NotFoundError: pass else: # The account is a PosixUser, special care needs to be taken with # regards to its default file group (dfg) personal_fg = pu.find_personal_group() if personal_fg is None: # Create a new personal file group op = Utils.Factory.get('Account')(db) op.find_by_name(cereconf.INITIAL_ACCOUNTNAME) with pu._new_personal_group(op.entity_id) as new_group: personal_fg = new_group roles = GroupRoles(db) roles.add_admin_to_group(pu.entity_id, personal_fg.entity_id) pu.map_user_spreads_to_pg() logger.debug("Created group: '%s'. Group ID = %d", personal_fg.group_name, personal_fg.entity_id) pg = Utils.Factory.get('PosixUser')(db) # If user has a personal group, but it currently isn't a posix group. # This scenario should never really occur, but hey, this is Cerebrum. if not personal_fg.has_extension('PosixGroup'): pg.populate(parent=personal_fg) pg.write_db() personal_fg = pg # Set group to be dfg for the user. if personal_fg.entity_id != default_group: pu.gid_id = personal_fg.entity_id pu.write_db() default_group = _get_default_group(account.entity_id) # Remove the user from groups group.clear() for g in group.search(member_id=account.entity_id, indirect_members=False): group.clear() group.find(g['group_id']) # # all posixuser-objects have to keep their membership # in their default group due to a FK-constraint from # posix_user to group_member if g['group_id'] == default_group: logger.debug("Skipping default group %s for user %s", group.group_name, account.account_name) continue group.remove_member(account.entity_id) # Remove password from user account.delete_password() return True
def _email_info_detail(self, acc): info = [] eq = Email.EmailQuota(self.db) try: eq.find_by_target_entity(acc.entity_id) et = Email.EmailTarget(self.db) et.find_by_target_entity(acc.entity_id) es = Email.EmailServer(self.db) es.find(et.email_server_id) # exchange-relatert-jazz # since Exchange-users will have a different kind of # server this code will not be affected at Exchange # roll-out It may, however, be removed as soon as # migration is completed (up to and including # "dis_quota_soft': eq.email_quota_soft})") if es.email_server_type == self.const.email_server_type_cyrus: pw = self.db._read_password(cereconf.CYRUS_HOST, cereconf.CYRUS_ADMIN) used = 'N/A' limit = None try: cyrus = Utils.CerebrumIMAP4_SSL( es.name, ssl_version=ssl.PROTOCOL_TLSv1) # IVR 2007-08-29 If the server is too busy, we do not want # to lock the entire bofhd. # 5 seconds should be enough cyrus.socket().settimeout(5) cyrus.login(cereconf.CYRUS_ADMIN, pw) res, quotas = cyrus.getquota("user." + acc.account_name) cyrus.socket().settimeout(None) if res == "OK": for line in quotas: try: folder, qtype, qused, qlimit = line.split() if qtype == "(STORAGE": used = str(int(qused)/1024) limit = int(qlimit.rstrip(")"))/1024 except ValueError: # line.split fails e.g. because quota isn't set # on server folder, junk = line.split() self.logger.warning("No IMAP quota set for %r", acc.account_name) used = "N/A" limit = None except (bofhd_uio_cmds.TimeoutException, socket.error): used = 'DOWN' except bofhd_uio_cmds.ConnectException as e: used = exc_to_text(e) except imaplib.IMAP4.error: used = 'DOWN' info.append({'quota_hard': eq.email_quota_hard, 'quota_soft': eq.email_quota_soft, 'quota_used': used}) if limit is not None and limit != eq.email_quota_hard: info.append({'quota_server': limit}) else: info.append({'dis_quota_hard': eq.email_quota_hard, 'dis_quota_soft': eq.email_quota_soft}) except Errors.NotFoundError: pass # exchange-relatert-jazz # delivery for exchange-mailboxes is not regulated through # LDAP, and LDAP should not be checked there my be some need # to implement support for checking if delivery is paused in # Exchange, but at this point only very vague explanation has # been given and priority is therefore low if acc.has_spread(self.const.spread_uit_exchange): return info # Check if the ldapservers have set mailPaused if self._is_email_delivery_stopped(acc.account_name): info.append({'status': 'Paused (migrating to new server)'}) return info
def add_spread(self, spread): # guest accounts: if (hasattr(self.const, 'trait_guest_owner') and self.get_trait(self.const.trait_guest_owner)): if spread not in (self.const.spread_ad_guest, self.const.spread_radius_guest): raise self._db.IntegrityError( "Guest accounts are not allowed other than guest spreads.") if spread == self.const.spread_uia_forward: email_spreads = (self.const.spread_exchange_account, self.const.spread_exchange_acc_old, self.const.spread_hia_email, self.const.spread_uia_office_365) if any([self.has_spread(s) for s in email_spreads]): raise self._db.IntegrityError( "Can't add spread {spread} to an account with the " "following spreads: {illegal_spreads}".format( spread=self.const.spread_uia_forward.str, illegal_spreads=map(lambda x: x.str, email_spreads))) if spread == self.const.spread_nis_user: if self.illegal_name(self.account_name): raise self._db.IntegrityError( "Can't add NIS spread to an account with illegal name.") if spread in (self.const.spread_exchange_account, self.const.spread_exchange_acc_old): if self.has_spread(self.const.spread_hia_email): raise self._db.IntegrityError( 'Cannot add Exchange-spread to an IMAP-account, ' 'use email exchange_migrate') # To be available in Exchange, you need to be in AD if not self.has_spread(self.const.spread_hia_ad_account): self.add_spread(self.const.spread_hia_ad_account) if spread == self.const.spread_exchange_acc_old: if self.has_spread(self.const.spread_exchange_account): raise self._db.IntegrityError("User already has new " "exchange spread") mdb = self._autopick_homeMDB() self.populate_trait(self.const.trait_exchange_mdb, strval=mdb) elif spread == self.const.spread_exchange_account: if self.has_spread(self.const.spread_exchange_acc_old): raise self._db.IntegrityError( "User has old exchange " "spread, cannot add new spread") self._update_email_server(spread, force=True) self.write_db() if spread == self.const.spread_hia_email: if (self.has_spread(self.const.spread_exchange_account) or self.has_spread(self.const.spread_exchange_acc_old)): # Accounts with Exchange can't have IMAP too. Should raise an # exception, but process_students tries to add IMAP spreads, # which would then fail, so it just returns instead. return et = Email.EmailTarget(self._db) try: et.find_by_email_target_attrs(target_entity_id=self.entity_id) except Errors.NotFoundError: # the user has no previosly assigned e-mail target to # fix, disregard the process pass else: # a target was found. make sure that the assigned server # is 'mail-imap2' es = Email.EmailServer(self._db) server_name = 'mail-imap2' es.find_by_name(server_name) et.email_server_id = es.entity_id et.write_db() if spread == self.const.spread_uia_office_365: # We except that the user already has an AD spread before setting # the spread for Office 365. This is handled by UiA. # Update the users email-server (and create EmailTarget, if it is # non-existent). self._update_email_server(spread, force=True) self.write_db() # Look up the domains that the user must have an email-address in, # for the cloud stuff to work. ed = Email.EmailDomain(self._db) ea = Email.EmailAddress(self._db) for domain in cereconf.EMAIL_OFFICE_365_DOMAINS: ed.clear() ed.find_by_domain(domain) # Ensure that the <uname>@thedomainfoundabove.uia.no address # exists. try: ea.clear() ea.find_by_local_part_and_domain(self.account_name, ed.entity_id) except Errors.NotFoundError: et = Email.EmailTarget(self._db) et.find_by_target_entity(self.entity_id) ea.populate(self.account_name, ed.entity_id, et.entity_id) ea.write_db() # (Try to) perform the actual spread addition. ret = self.__super.add_spread(spread) return ret
def proc_delete_user(r): spread = default_spread # TODO: check spread in r['state_data'] # # IVR 2007-01-25 We do not care if it's a posix user or not. # TVL 2013-12-09 Except that if it's a POSIX user, it should retain a # personal file group. account, uname, old_host, old_disk = get_account_and_home(r['entity_id'], spread=spread) if account.is_deleted(): logger.warn("%s is already deleted" % uname) return True set_operator(r['requestee_id']) operator = get_account(r['requestee_id']).account_name et = Email.EmailTarget(db) try: et.find_by_target_entity(account.entity_id) es = Email.EmailServer(db) es.find(et.email_server_id) mail_server = es.name except Errors.NotFoundError: mail_server = '' if not delete_user(uname, old_host, '%s/%s' % (old_disk, uname), operator, mail_server): return False account.expire_date = mx.DateTime.now() account.write_db() try: home = account.get_home(spread) except Errors.NotFoundError: pass else: account.set_homedir(current_id=home['homedir_id'], status=const.home_status_archived) # Remove references in other tables # Note that we preserve the quarantines for deleted users # TBD: Should we have an API function for this? for s in account.get_spread(): account.delete_spread(s['spread']) # Make sure the user's default file group is a personal group group = Utils.Factory.get('Group')(db) default_group = _get_default_group(account.entity_id) pu = Utils.Factory.get('PosixUser')(db) try: pu.find(account.entity_id) except Errors.NotFoundError: pass else: try: group.find_by_name(account.account_name) except Errors.NotFoundError: ac = Utils.Factory.get('Account')(db) ac.find_by_name(cereconf.INITIAL_ACCOUNTNAME) group.clear() group.new( ac.entity_id, const.group_visibility_all, account.account_name, description=( 'Personal file group for {}'.format(account.account_name))) group.add_member(account.entity_id) group.write_db() logger.debug("Created group: '%s'. Group ID = %d", group.group_name, group.entity_id) pg = Utils.Factory.get('PosixGroup')(db) try: pg.find(group.entity_id) except: pg.clear() pg.populate(parent=group) pg.write_db() logger.debug("Group upgraded to posixgroup") if group.entity_id != default_group: pu.gid_id = group.entity_id pu.write_db() default_group = _get_default_group(account.entity_id) # Remove the user from groups group.clear() for g in group.search(member_id=account.entity_id, indirect_members=False): group.clear() group.find(g['group_id']) # # all posixuser-objects have to keep their membership # in their default group due to a FK-constraint from # posix_user to group_member if g['group_id'] == default_group: logger.debug("Skipping default group %s for user %s", group.group_name, account.account_name) continue group.remove_member(account.entity_id) return True
def add_spread(self, spread): # # Pre-add checks # spreads = [int(r['spread']) for r in self.get_spread()] # All users in the 'ifi' NIS domain must also exist in the # 'uio' NIS domain. if (spread == self.const.spread_ifi_nis_user and int(self.const.spread_uio_nis_user) not in spreads): raise self._db.IntegrityError( "Can't add ifi spread to an account without uio spread.") # Gather information on present state, to be used later. Note # that this information gathering must be done before we pass # control to our superclass(es), as the superclass methods # might change the state. # # exchange-relatert-jazz # this code (up to and including 'pass') may be removed # after Exchange roll-out, as it has been decided that all # new mail-boxes will be created in Exchange and any old # mailboxes restored to Exchange # Jazz (2013-11) state = {} if spread == self.const.spread_uio_imap: # exchange-relatert-jazz # no account should have both IMAP and Exchange spread at the # same time, as this will create a double mailbox if self.has_spread(self.const.spread_exchange_account): raise self._db.IntegrityError("Can't add IMAP-spread to an " "account with Exchange-spread.") # Is this account already associated with an Cyrus # EmailTarget? et = Email.EmailTarget(self._db) try: et.find_by_target_entity(self.entity_id) if et.email_server_id: state['email_server_id'] = et.email_server_id except Errors.NotFoundError: pass if spread == self.const.spread_exchange_account: # no account should have both IMAP and Exchange spread at the # same time, as this will create a double mailbox if self.has_spread(self.const.spread_uio_imap): raise self._db.IntegrityError("Can't add Exchange-spread to " "an account with IMAP-spread.") # Check if there is an existing email-target for this account # (entity) before we actually add the spread. is_new_target = not self._has_email_target() # (Try to) perform the actual spread addition. # An exception will be thrown if the same type of spread exists for # this account (entity-id) ret = self.__super.add_spread(spread) # # Additional post-add magic # # exchange-relatert-jazz if spread == self.const.spread_exchange_account: # exchange-relatert-jazz es = Email.EmailServer(self._db) es.clear() # we could probably define a cereconf var to hold this # default (dummy) server value, but I hope that, since it # is not actually used in Exchange, the whole # server-reference may de removed from EmailTarget after # migration to Exchange is completed (it should be done, # no matter if Baardj says differently :-)). Jazz (2013-11) es.find_by_name('mail') # Check if the account already is associated with an # EmailTarget. If so, we are most likely looking at an # account restore (or else an anomaly in the cerebrum-db) # We should keep the EmailTarget, but make sure that # target_type is refreshed to "account" and target_server # is refreshed to dummy-exchange server. We are, at this # point, not interessted in any target-data. et = Email.EmailTarget(self._db) try: et.find_by_target_entity(self.entity_id) et.email_server_id = es.entity_id et.email_target_type = self.const.email_target_account except Errors.NotFoundError: # No EmailTarget found for account, creating one # after the migration to Exchange is completed this # part may be done by update_email_addresses, # but since we need to support both exchange and # imap for a while, it's easiest to create the # target manually et.populate(self.const.email_target_account, self.entity_id, self.const.entity_account, server_id=es.entity_id) et.write_db() self.update_email_quota() # register default spam and filter settings self._UiO_default_spam_settings(et) if is_new_target: self._UiO_default_filter_settings(et) # The user's email target is now associated with an email # server, try generating email addresses connected to the # target. self.update_email_addresses() # exchange-relatert-jazz # this code (up to and including 'update_email_addresse') # may be removed after Exchange roll-out, as it has been # decided that all new mail-boxes will be created in Exchange # and any old mailboxes restored to Exchange # Jazz (2013-11) if spread == self.const.spread_uio_imap: # Unless this account already has been associated with an # Cyrus EmailTarget, we need to do so. et = self._UiO_update_email_server( self.const.email_server_type_cyrus) self.update_email_quota() # register default spam and filter settings self._UiO_default_spam_settings(et) self._UiO_default_filter_settings(et) # The user's email target is now associated with an email # server; try generating email addresses connected to the # target. self.update_email_addresses() elif spread == self.const.spread_ifi_nis_user: # Add an account_home entry pointing to the same disk as # the uio spread try: tmp = self.get_home(self.const.spread_uio_nis_user) self.set_home(spread, tmp['homedir_id']) except Errors.NotFoundError: pass # User has no homedir for this spread yet return ret
def _UiO_update_email_server(self, server_type): """Due to diverse legacy stuff and changes in server types as well as requirements for assigning e-mail accounts this process is getting rather complicated. The email servers are now assigned as follows: - create on new server, update target_type = CNS - create on old server, update target_type = COS - move to new server = MNS t_type/srv_type| none| cyrus, active| cyrus,non-active| non-cyrus ------------------------------------------------------------------ target_deleted | CNS | COS | CNS | CNS ------------------------------------------------------------------ target_account | MNS | PASS | MNS | MNS ------------------------------------------------------------------ """ et = Email.EmailTarget(self._db) es = Email.EmailServer(self._db) new_server = None old_server = None srv_is_cyrus = False email_server_in_use = False target_type = self.const.email_target_account # Find the account's EmailTarget try: et.find_by_target_entity(self.entity_id) if et.email_target_type == self.const.email_target_deleted: target_type = self.const.email_target_deleted except Errors.NotFoundError: # No EmailTarget found for account, creating one et.populate(self.const.email_target_account, self.entity_id, self.const.entity_account) et.write_db() # Find old server id and type try: old_server = et.email_server_id es.find(old_server) if es.email_server_type == self.const.email_server_type_cyrus: srv_is_cyrus = True email_server_in_use = es.get_trait( self.const.trait_email_server_weight) except Errors.NotFoundError: pass # Different actions for target_type deleted and account if target_type == self.const.email_target_account: if srv_is_cyrus and email_server_in_use: # both target and server type er ok, do nothing pass elif old_server is None: # EmailTarget with no registered server new_server = self._pick_email_server() et.email_server_id = new_server et.write_db() self._UiO_order_cyrus_action(self.const.bofh_email_create, new_server) else: # cyrus_nonactive or non_cyrus new_server = self._pick_email_server() et.email_server_id = new_server et.write_db() elif target_type == self.const.email_target_deleted: if srv_is_cyrus and email_server_in_use: # Create cyrus account on active server self._UiO_order_cyrus_action(self.const.bofh_email_create, old_server) else: # Pick new server and create cyrus account new_server = self._pick_email_server() et.email_server_id = new_server et.write_db() self._UiO_order_cyrus_action(self.const.bofh_email_create, new_server) return et