Exemplo n.º 1
0
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)
Exemplo n.º 2
0
    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
Exemplo n.º 3
0
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
Exemplo n.º 4
0
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()
Exemplo n.º 5
0
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))
Exemplo n.º 6
0
 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'])
Exemplo n.º 7
0
 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)
Exemplo n.º 8
0
    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'
Exemplo n.º 9
0
 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)
Exemplo n.º 10
0
 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
Exemplo n.º 11
0
 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
Exemplo n.º 12
0
 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
Exemplo n.º 13
0
    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)
Exemplo n.º 14
0
    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
Exemplo n.º 15
0
 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,
     }
Exemplo n.º 16
0
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
Exemplo n.º 17
0
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
Exemplo n.º 18
0
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()
Exemplo n.º 19
0
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
Exemplo n.º 20
0
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)
Exemplo n.º 21
0
 def get_server_name(server_id):
     if not server_id:
         return None
     es = Email.EmailServer(db)
     es.find(server_id)
     return u(es.name)
Exemplo n.º 22
0
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
Exemplo n.º 23
0
    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
Exemplo n.º 24
0
    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
Exemplo n.º 25
0
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
Exemplo n.º 26
0
    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
Exemplo n.º 27
0
    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