示例#1
0
class AccountsController(BaseController):
    "Accounts controller"

    def __before__(self):
        "set context"
        BaseController.__before__(self)
        if self.identity:
            c.user = self.identity['user']
        else:
            c.user = None
        c.selectedtab = 'accounts'

    def login(self):
        "login"
        if request.remote_addr in session:
            if session[request.remote_addr] > arrow.utcnow().datetime:
                msg = _('You have been banned after' ' several failed logins')
                log.info(msg)
                abort(409, msg)
            else:
                del session[request.remote_addr]
                session.save()

        identity = request.environ.get('repoze.who.identity')
        came_from = unquote(str(request.GET.get('came_from', '')))
        if not came_from or ' ' in came_from:
            came_from = url('home')
        if '://' in came_from:
            from_url = urlparse(came_from)
            came_from = from_url[2]

        if identity:
            redirect(url(came_from))
        else:
            c.came_from = came_from
            c.login_counter = request.environ['repoze.who.logins']
            if c.login_counter >= 3:
                ban_until = arrow.utcnow().datetime + timedelta(minutes=5)
                if request.remote_addr not in session:
                    session[request.remote_addr] = ban_until
                    session.save()
                else:
                    if arrow.utcnow().datetime > session[request.remote_addr]:
                        del session[request.remote_addr]
                        session.save()
            c.form = ResetPwForm(request.POST, csrf_context=session)
            return self.render('/accounts/login.html')

    def loggedin(self):
        "Landing page"
        came_from = (unquote(str(request.params.get('came_from', '')))
                     or url('/'))
        if not self.identity:
            if 'repoze.who.logins' in request.environ:
                login_counter = request.environ['repoze.who.logins'] + 1
            else:
                abort(409)
            redirect(
                url('/accounts/login',
                    came_from=came_from,
                    __logins=login_counter))
        userid = self.identity['repoze.who.userid']
        user = self.identity['user']
        if user is None:
            try:
                user, local_part, domain, domains = add_user(userid)
                msg = _('First time Login from external auth,'
                        ' your local account was created')
                user_address_update(user, local_part, domain, domains,
                                    self.identity)
            except IntegrityError:
                Session.rollback()
                redirect(url('/logout'))
            except ldap.LDAPError:
                pass
        else:
            if not user.active:
                redirect(url('/logout'))
            msg = _('Login successful, Welcome back %(username)s !' %
                    dict(username=userid))
        update_login(user)
        if user.is_peleb:
            for domain in user.domains:
                if check_language(domain.language):
                    session['lang'] = domain.language
                    session.save()
                    break
        session['taskids'] = []
        session.save()
        info = auditmsgs.ACCOUNTLOGIN_MSG % dict(u=user.username)
        audit_log(user.username, 6, unicode(info), request.host,
                  request.remote_addr,
                  arrow.utcnow().datetime)
        flash(msg)
        log.info(msg)
        redirect(url(came_from))

    # pylint: disable-msg=R0201
    def loggedout(self):
        "Logged out page"
        session.clear()
        if 'theme' in session:
            del session['theme']
        session.save()
        came_from = (unquote(str(request.params.get('came_from', '')))
                     or url('/accounts/login'))
        redirect(url(came_from))

    def passwdreset(self):
        """Render password reset page"""
        c.came_from = '/'
        c.login_counter = 0
        c.form = ResetPwForm(request.POST, csrf_context=session)
        if request.method == 'POST' and c.form.validate():
            key_seed = '%s%s' % (c.form.email.data, arrow.utcnow().ctime())
            token = hashlib.sha1(key_seed).hexdigest()
            user = Session.query(User)\
                            .filter(User.email == c.form.email.data)\
                            .one()
            if not user.local:
                flash(
                    _('The account %s is an external account, use your'
                      ' External systems to change the password. '
                      'Contact your system adminstrator if you do not '
                      'know which external systems you authenticate to') %
                    user.email)
                redirect(url('/accounts/login'))
            rtoken = Session\
                    .query(ResetToken.used)\
                    .filter(ResetToken.used == false())\
                    .filter(ResetToken.user_id == user.id)\
                    .all()
            if not rtoken:
                rtoken = ResetToken(token, user.id)
                Session.add(rtoken)
                Session.commit()
                host = URL_PREFIX_RE.sub('', request.host_url)
                c.username = user.username
                c.firstname = user.firstname or user.username
                c.reset_url = url('accounts-pw-token-reset',
                                  token=token,
                                  host=host)
                text = self.render('/email/pwreset.txt')
                mailer = Mailer(get_conf_options(config))
                mailer.start()
                sdrnme = config.get('baruwa.custom.name', 'Baruwa')
                email = Msg(author=[(sdrnme,
                                     config.get('baruwa.reports.sender'))],
                            to=[('', c.form.email.data)],
                            subject=_("[%s] Password reset request") % sdrnme)
                email.plain = text
                mailer.send(email)
                mailer.stop()
            flash(
                _('An email has been sent to the address provided, '
                  'please follow the instructions in that email to '
                  'reset your password.'))
            redirect(url('/accounts/login'))
        return self.render('/accounts/login.html')

    def pwtokenreset(self, token):
        """Reset password using token"""
        try:
            token = Session.query(ResetToken)\
                    .filter(ResetToken.token == token)\
                    .filter(ResetToken.used == false()).one()
            threshold = token.timestamp + timedelta(minutes=20)
            if arrow.utcnow().datetime > threshold:
                Session.delete(token)
                Session.commit()
                raise NoResultFound
            user = self._get_user(token.user_id)
            if not user or user.is_superadmin:
                raise NoResultFound
            passwd = mkpasswd()
            user.set_password(passwd)
            Session.add(user)
            Session.delete(token)
            Session.commit()
            c.passwd = passwd
            c.firstname = user.firstname or user.username
            text = self.render('/email/pwchanged.txt')
            mailer = Mailer(get_conf_options(config))
            mailer.start()
            sdrnme = config.get('baruwa.custom.name', 'Baruwa')
            email = Msg(author=[(sdrnme, config.get('baruwa.reports.sender'))],
                        to=[('', user.email)],
                        subject=_("[%s] Password reset") % sdrnme)
            email.plain = text
            mailer.send(email)
            mailer.stop()
            flash(
                _('The password has been reset, check your email for'
                  ' the temporary password you should use to login.'))
        except NoResultFound:
            msg = _('The token used is invalid or does not exist')
            flash_alert(msg)
            log.info(msg)
        redirect(url('/accounts/login'))

    @ActionProtector(All(not_anonymous(), OnlyAdminUsers(),
                         CanAccessAccount()))
    def pwchange(self, userid):
        """Reset a user password"""
        user = self._get_user(userid)
        if not user:
            abort(404)
        c.form = ChangePasswordForm(request.POST, csrf_context=session)
        if request.method == 'POST' and c.form.validate():
            if user.local and not user.is_superadmin:
                change_user_pw(user, c.user, c.form.password1.data,
                               request.host, request.remote_addr)
                msg = _('The account password for %(name)s has been reset') \
                        % dict(name=user.username)
                flash(msg)
            else:
                if user.is_superadmin:
                    flash(_('Admin accounts can not be modified via the web'))
                else:
                    flash(
                        _('This is an external account, use'
                          ' external system to reset the password'))
            redirect(url('account-detail', userid=user.id))
        c.id = userid
        c.username = user.username
        c.posturl = 'accounts-pw-change'
        return self.render('/accounts/pwchange.html')

    @ActionProtector(not_anonymous())
    def upwchange(self, userid):
        """User change own password"""
        user = self._get_user(userid)
        if not user:
            abort(404)
        if user.id != c.user.id or c.user.is_superadmin:
            abort(403)
        c.form = UserPasswordForm(request.POST, csrf_context=session)
        if (request.method == 'POST' and c.form.validate()
                and user.validate_password(c.form.password3.data)):
            if user.local:
                user.set_password(c.form.password1.data)
                Session.add(user)
                Session.commit()
                flash(
                    _('The account password for %(name)s has been reset') %
                    dict(name=user.username))
                info = auditmsgs.PASSWORDCHANGE_MSG % dict(u=user.username)
                audit_log(c.user.username, 2, unicode(info), request.host,
                          request.remote_addr,
                          arrow.utcnow().datetime)
            else:
                flash(
                    _('This is an external account, use'
                      ' external system to reset the password'))
            redirect(url('account-detail', userid=user.id))
        elif (request.method == 'POST'
              and not user.validate_password(c.form.password3.data)
              and not c.form.password3.errors):
            flash_alert(
                _('The old password supplied does'
                  ' not match our records'))
        c.id = userid
        c.username = user.username
        c.posturl = 'accounts-pw-uchange'
        return self.render('/accounts/pwchange.html')

    # pylint: disable-msg=W0622
    @ActionProtector(All(not_anonymous(), OnlyAdminUsers()))
    def index(self, page=1, orgid=None, domid=None, format=None):
        """GET /accounts/: Paginate items in the collection"""
        num_items = session.get('accounts_num_items', 10)
        c.form = BulkDelUsers(request.POST, csrf_context=session)
        if request.method == 'POST':
            if c.form.accountid.data and \
                str(c.user.id) in c.form.accountid.data:
                c.form.accountid.data.remove(str(c.user.id))
            if c.form.accountid.data and c.form.whatdo.data == 'disable':
                Session.query(User)\
                    .filter(User.id.in_(c.form.accountid.data))\
                    .update({'active': False}, synchronize_session='fetch')
                Session.commit()
            if c.form.accountid.data and c.form.whatdo.data == 'enable':
                Session.query(User)\
                        .filter(User.id.in_(c.form.accountid.data))\
                        .update({'active': True}, synchronize_session='fetch')
                Session.commit()
            if c.form.accountid.data and c.form.whatdo.data == 'delete':
                session['bulk_account_delete'] = c.form.accountid.data
                session.save()
                # redirect for confirmation
                redirect(url('accounts-confirm-delete'))
        users = Session.query(User.id, User.username, User.firstname,
                              User.lastname, User.email, User.active,
                              User.local, User.account_type).order_by(User.id)
        usrcount = Session.query(User.id)
        if c.user.is_domain_admin:
            users = users.join(domain_users, (dom_owns,
                                domain_users.c.domain_id ==
                                dom_owns.c.domain_id),
                                (oas,
                                dom_owns.c.organization_id ==
                                oas.c.organization_id))\
                                .filter(oas.c.user_id == c.user.id)
            usrcount = usrcount.join(domain_users, (dom_owns,
                                    domain_users.c.domain_id ==
                                    dom_owns.c.domain_id),
                                    (oas,
                                    dom_owns.c.organization_id ==
                                    oas.c.organization_id))\
                                    .filter(oas.c.user_id == c.user.id)
        if domid:
            users = users.filter(
                and_(domain_users.c.domain_id == domid,
                     domain_users.c.user_id == User.id))
            usrcount = usrcount.filter(
                and_(domain_users.c.domain_id == domid,
                     domain_users.c.user_id == User.id))
        if orgid:
            users = users.filter(
                and_(
                    domain_users.c.user_id == User.id,
                    domain_users.c.domain_id == dom_owns.c.domain_id,
                    dom_owns.c.organization_id == orgid,
                ))
            usrcount = usrcount.filter(
                and_(
                    domain_users.c.user_id == User.id,
                    domain_users.c.domain_id == dom_owns.c.domain_id,
                    dom_owns.c.organization_id == orgid,
                ))

        users = users.distinct(User.id)
        usrcount = usrcount.distinct(User.id)

        pages = paginate.Page(users,
                              page=int(page),
                              items_per_page=num_items,
                              item_count=usrcount.count())
        if format == 'json':
            response.headers['Content-Type'] = 'application/json'
            data = convert_acct_to_json(pages, orgid)
            return data

        c.page = pages
        c.domid = domid
        c.orgid = orgid
        return self.render('/accounts/index.html')

    # pylint: disable-msg=R0914,W0142,W0613
    @ActionProtector(All(not_anonymous(), OnlyAdminUsers()))
    def search(self, format=None):
        "Search for accounts"
        total_found = 0
        search_time = 0
        num_items = session.get('accounts_num_items', 10)
        qry = request.GET.get('q', '')
        doms = request.GET.get('d', None)
        kwds = {'presliced_list': True}
        page = int(request.GET.get('p', 1))
        conn = SphinxClient()
        sphinxopts = extract_sphinx_opts(config['sphinx.url'])
        conn.SetServer(sphinxopts.get('host', '127.0.0.1'))
        conn.SetMatchMode(SPH_MATCH_EXTENDED2)
        conn.SetFieldWeights(
            dict(username=50, email=30, firstname=10, lastname=10))
        if page == 1:
            conn.SetLimits(0, num_items, 500)
        else:
            page = int(page)
            offset = (page - 1) * num_items
            conn.SetLimits(offset, num_items, 500)
        if doms:
            conn.SetFilter('domains', [
                int(doms),
            ])
        if c.user.is_domain_admin:
            # crcs = get_dom_crcs(Session, c.user)
            domains = Session.query(Domain.id).join(dom_owns,
                        (oas, dom_owns.c.organization_id ==
                        oas.c.organization_id))\
                        .filter(oas.c.user_id == c.user.id)
            conn.SetFilter('domains', [domain[0] for domain in domains])
        qry = clean_sphinx_q(qry)
        try:
            results = conn.Query(qry, 'accounts, accounts_rt')
        except (socket.timeout, struct.error):
            redirect(request.path_qs)
        qry = restore_sphinx_q(qry)
        if results and results['matches']:
            ids = [hit['id'] for hit in results['matches']]
            total_found = results['total_found']
            search_time = results['time']
            users = Session.query(User.id,
                                    User.username,
                                    User.firstname,
                                    User.lastname,
                                    User.email,
                                    User.active,
                                    User.local,
                                    User.account_type)\
                                .filter(User.id.in_(ids))\
                                .order_by(User.id)\
                                .all()
            usercount = total_found
        else:
            users = []
            usercount = 0
        c.q = qry
        c.d = doms
        c.total_found = total_found
        c.search_time = search_time
        c.page = paginate.Page(users,
                               page=int(page),
                               items_per_page=num_items,
                               item_count=usercount,
                               **kwds)
        return self.render('/accounts/searchresults.html')

    @ActionProtector(All(not_anonymous(), CanAccessAccount()))
    def detail(self, userid):
        """GET /accounts/userid/ Show a specific item"""
        user = self._get_user(userid)
        if not user:
            abort(404)
        c.account = user
        return self.render('/accounts/account.html')

    @ActionProtector(All(not_anonymous(), OnlyAdminUsers()))
    def add(self):
        """/accounts/new"""
        c.form = user_add_form(c.user, request.POST, session)
        if request.method == 'POST' and c.form.validate():
            try:
                user = create_user(c.form, c.user.username, request.host,
                                   request.remote_addr)
                flash(
                    _('The account: %(user)s was created successfully') %
                    {'user': user.username})
                redirect(url('account-detail', userid=user.id))
            except IntegrityError:
                Session.rollback()
                flash_alert(
                    _('Either the username or email address already exist'))
        return self.render('/accounts/new.html')

    @ActionProtector(All(not_anonymous(), CanAccessAccount()))
    def edit(self, userid):
        """GET /accounts/edit/id: Form to edit an existing item"""
        user = self._get_user(userid)
        if not user:
            abort(404)

        c.form = user_update_form(user, c.user, request.POST, session)
        if request.method == 'POST' and c.form.validate():
            kwd = dict(userid=user.id)
            if update_changed(c.form, FORM_FIELDS, user):
                try:
                    update_user(user, c.user, request.host,
                                request.remote_addr)
                    flash(_('The account has been updated'))
                    kwd['uc'] = 1
                except IntegrityError:
                    Session.rollback()
                    flash_alert(
                        _('The account: %(acc)s could not be updated') %
                        dict(acc=user.username))
                if (user.id == c.user.id and c.form.active
                        and c.form.active.data is False):
                    redirect(url('/logout'))
            else:
                flash_info(_('No changes made to the account'))
            redirect(url(controller='accounts', action='detail', **kwd))
        c.fields = FORM_FIELDS
        c.id = userid
        return self.render('/accounts/edit.html')

    @ActionProtector(All(not_anonymous(), OnlyAdminUsers(),
                         CanAccessAccount()))
    def delete(self, userid):
        """/accounts/delete/id"""
        user = self._get_user(userid)
        if not user:
            abort(404)

        c.form = EditUserForm(request.POST, user, csrf_context=session)
        del c.form.domains
        if request.method == 'POST':
            if c.form.validate():
                delete_user(user, c.user, request.host, request.remote_addr)
                flash(_('The account has been deleted'))
                redirect(url(controller='accounts', action='index'))
        else:
            flash_info(
                _('The account: %(a)s and all associated data'
                  ' will be deleted, This action is not reversible.') %
                dict(a=user.username))
        c.fields = FORM_FIELDS
        c.id = userid
        return self.render('/accounts/delete.html')

    @ActionProtector(not_anonymous())
    def confirm_delete(self):
        "Confirm mass delete"
        accountids = session.get('bulk_account_delete', [])
        if not accountids:
            redirect(url(controller='accounts', action='index'))

        num_items = 10
        if len(accountids) > num_items and len(accountids) <= 20:
            num_items = 20
        if len(accountids) > num_items and len(accountids) <= 50:
            num_items = 50
        if len(accountids) > num_items and len(accountids) <= 100:
            num_items = 100

        users = Session.query(User).filter(User.id.in_(accountids))
        usrcount = Session.query(User.id)

        if c.user.is_domain_admin and usrcount:
            users = users.join(domain_users, (dom_owns,
                    domain_users.c.domain_id == dom_owns.c.domain_id),
                    (oas, dom_owns.c.organization_id == oas.c.organization_id))\
                    .filter(oas.c.user_id == c.user.id)
            usrcount = usrcount.join(domain_users, (dom_owns,
                        domain_users.c.domain_id == dom_owns.c.domain_id),
                        (oas, dom_owns.c.organization_id ==
                        oas.c.organization_id))\
                        .filter(oas.c.user_id == c.user.id)

        if request.method == 'POST':
            tasks = []
            # try:
            for account in users.all():
                info = auditmsgs.DELETEACCOUNT_MSG % dict(u=account.username)
                Session.delete(account)
                tasks.append([
                    c.user.username, 4,
                    unicode(info), request.host, request.remote_addr,
                    arrow.utcnow().datetime
                ])
            Session.commit()
            del session['bulk_account_delete']
            session.save()
            backend_user_update(None, True)
            for task in tasks:
                audit_log(*task)
            flash(_('The accounts have been deleted'))
            redirect(url(controller='accounts'))
        else:
            flash(
                _('The following accounts are about to be deleted,'
                  ' this action is not reversible, Do you wish to '
                  'continue ?'))

        try:
            c.page = paginate.Page(users,
                                   page=1,
                                   items_per_page=num_items,
                                   item_count=usrcount.count())
        except DataError:
            msg = _('An error occured try again')
            flash_alert(msg)
            log.info(msg)
            redirect(url(controller='accounts', action='index'))
        return self.render('/accounts/confirmbulkdel.html')

    @ActionProtector(All(not_anonymous(), OnlyAdminUsers(),
                         CanAccessAccount()))
    def addaddress(self, userid):
        "Add address"
        user = self._get_user(userid)
        if not user:
            abort(404)

        c.form = AddressForm(request.POST, csrf_context=session)
        if request.method == 'POST' and c.form.validate():
            try:
                addr = create_address(c.form, user, c.user, request.host,
                                      request.remote_addr)
                flash(
                    _('The alias address %(address)s was successfully created.'
                      % dict(address=addr.address)))
            except IntegrityError:
                Session.rollback()
                msg = _('The address %(addr)s already exists') % \
                        dict(addr=c.form.address.data)
                flash_alert(msg)
                log.info(msg)
            except NoResultFound:
                domain = c.form.address.data.split('@')[1]
                msg = _('Domain: %(d)s does not belong to you') % \
                        dict(d=domain)
                flash(msg)
                log.info(msg)
            redirect(url(controller='accounts', action='detail',
                         userid=userid))
        c.id = userid
        return self.render('/accounts/addaddress.html')

    @ActionProtector(All(not_anonymous(), OnlyAdminUsers(),
                         CanAccessAccount()))
    def editaddress(self, addressid):
        "Edit address"
        address = get_address(addressid)
        if not address:
            abort(404)

        c.form = AddressForm(request.POST, address, csrf_context=session)
        if request.method == 'POST' and c.form.validate():
            try:
                if (address.address != c.form.address.data
                        or address.enabled != c.form.enabled.data):
                    update_address(c.form, address, c.user, request.host,
                                   request.remote_addr)
                    flash(_('The alias address has been updated'))
                else:
                    flash_info(_('No changes were made to the address'))
            except IntegrityError:
                Session.rollback()
                msg = _('The address %(addr)s already exists') % \
                        dict(addr=c.form.address.data)
                flash_alert(msg)
                log.info(msg)
            except NoResultFound:
                domain = c.form.address.data.split('@')[1]
                msg = _('Domain: %(d)s does not belong to you') % \
                        dict(d=domain)
                flash(msg)
                log.info(msg)
            redirect(
                url(controller='accounts',
                    action='detail',
                    userid=address.user_id))
        c.id = addressid
        c.userid = address.user_id
        return self.render('/accounts/editaddress.html')

    @ActionProtector(All(not_anonymous(), OnlyAdminUsers(),
                         CanAccessAccount()))
    def deleteaddress(self, addressid):
        "Delete address"
        address = get_address(addressid)
        if not address:
            abort(404)

        c.form = AddressForm(request.POST, address, csrf_context=session)
        if request.method == 'POST' and c.form.validate():
            user_id = delete_address(address, c.user, request.host,
                                     request.remote_addr)
            flash(_('The address has been deleted'))
            redirect(
                url(controller='accounts', action='detail', userid=user_id))
        c.id = addressid
        c.userid = address.user_id
        return self.render('/accounts/deleteaddress.html')

    def set_language(self):
        "Set the language"
        nextpage = request.params.get('next', None)
        if not nextpage:
            nextpage = request.headers.get('Referer', None)
        if not nextpage:
            nextpage = '/'
        if '://' in nextpage:
            from_url = urlparse(nextpage)
            nextpage = from_url[2]
        lang_code = request.params.get('language', None)
        if lang_code and check_language(lang_code):
            session['lang'] = lang_code
            session.save()
        params = []
        for param in request.params:
            if param not in ['language', 'amp']:
                value = request.params[param]
                if value:
                    if (param == 'came_from'
                            and '://' in urllib2.unquote(value)):
                        urlparts = urlparse(urllib2.unquote(value))
                        value = urlparts[2] or '/'
                    params.append('%s=%s' %
                                  (urllib2.quote(param), urllib2.quote(value)))
        if 'lc=1' not in params:
            params.append('lc=1')
        if params:
            nextpage = "%s?%s" % (nextpage, '&amp;'.join(params))
        redirect(nextpage)

    @ActionProtector(All(not_anonymous(), OwnsDomain()))
    def import_accounts(self, domainid):
        "import accounts"
        try:
            cachekey = u'domain-%s' % domainid
            domain = Session.query(Domain.id, Domain.name)\
                    .filter(Domain.id == domainid)\
                    .options(FromCache('sql_cache_med', cachekey)).one()
        except NoResultFound:
            abort(404)

        c.form = ImportCSVForm(request.POST, csrf_context=session)
        if request.method == 'POST' and c.form.validate():
            basedir = config['pylons.cache_dir']
            csvdata = request.POST['csvfile']
            if hasattr(csvdata, 'filename'):
                dstfile = os.path.join(basedir, 'uploads',
                                       csvdata.filename.lstrip(os.sep))
                if not os.path.exists(dstfile) and iscsv(csvdata.file):
                    csvfile = open(dstfile, 'w')
                    shutil.copyfileobj(csvdata.file, csvfile)
                    csvdata.file.close()
                    csvfile.close()
                    task = importaccounts.apply_async(args=[
                        domainid, dstfile, c.form.skipfirst.data, c.user.id
                    ])
                    session['taskids'].append(task.task_id)
                    session['acimport-count'] = 1
                    session['acimport-file'] = dstfile
                    session.save()
                    msg = _('File uploaded, and is being processed, this page'
                            ' will automatically refresh to show the status')
                    flash(msg)
                    log.info(msg)
                    redirect(url('accounts-import-status',
                                 taskid=task.task_id))
                else:
                    filename = csvdata.filename.lstrip(os.sep)
                    if not iscsv(csvdata.file):
                        msg = _('The file: %s is not a CSV file') % filename
                        flash_alert(msg)
                        log.info(msg)
                    else:
                        msg = _('The file: %s already exists and'
                                ' is being processed.') % filename
                        flash_alert(msg)
                        log.info(msg)
                    csvdata.file.close()
            else:
                msg = _('No CSV was file uploaded, try again')
                flash_alert(msg)
                log.info(msg)

        c.domain = domain
        return self.render('/accounts/importaccounts.html')

    @ActionProtector(All(not_anonymous(), OnlyAdminUsers()))
    def import_status(self, taskid):
        "import status"
        result = AsyncResult(taskid)
        if result is None or taskid not in session['taskids']:
            msg = _('The task status requested has expired or does not exist')
            flash(msg)
            log.info(msg)
            redirect(url(controller='accounts', action='index'))

        if result.ready():
            finished = True
            flash.pop_messages()
            if isinstance(result.result, Exception):
                if c.user.is_superadmin:
                    msg = _('Error occured in processing %s') % result.result
                    flash_alert(msg)
                    log.info(msg)
                else:
                    flash_alert(_('Backend error occured during processing.'))
                redirect(url(controller='accounts'))
            backend_user_update(None, True)
            audit_log(c.user.username, 3, unicode(auditmsgs.ACCOUNTIMPORT_MSG),
                      request.host, request.remote_addr,
                      arrow.utcnow().datetime)
        else:
            session['acimport-count'] += 1
            if (session['acimport-count'] >= 10
                    and result.state in ['PENDING', 'RETRY', 'FAILURE']):
                result.revoke()
                try:
                    os.unlink(session['acimport-file'])
                except OSError:
                    pass
                del session['acimport-count']
                session.save()
                msg = _('The import could not be processed,'
                        ' try again later')
                flash_alert(msg)
                log.info(msg)
                redirect(url(controller='accounts'))
            finished = False

        c.finished = finished
        c.results = result.result
        c.success = result.successful()
        return self.render('/accounts/importstatus.html')

    @ActionProtector(All(not_anonymous(), OnlyAdminUsers()))
    def export_accounts(self, domainid=None, orgid=None):
        "export domains"
        task = exportaccounts.apply_async(args=[domainid, c.user.id, orgid])
        if 'taskids' not in session:
            session['taskids'] = []
        session['taskids'].append(task.task_id)
        session['acexport-count'] = 1
        session.save()
        msg = _('Accounts export is being processed')
        flash(msg)
        log.info(msg)
        redirect(url('accounts-export-status', taskid=task.task_id))

    @ActionProtector(All(not_anonymous(), OnlyAdminUsers()))
    def export_status(self, taskid):
        "export status"
        result = AsyncResult(taskid)
        if result is None or taskid not in session['taskids']:
            msg = _('The task status requested has expired or does not exist')
            flash(msg)
            log.info(msg)
            redirect(url(controller='accounts', action='index'))

        if result.ready():
            finished = True
            flash.pop_messages()
            if isinstance(result.result, Exception):
                msg = _('Error occured in processing %s') % result.result
                if c.user.is_superadmin:
                    flash_alert(msg)
                    log.info(msg)
                else:
                    flash_alert(_('Backend error occured during processing.'))
                    log.info(msg)
                redirect(url(controller='accounts', action='index'))
            results = dict(
                f=True if not result.result['global_error'] else False,
                id=taskid,
                global_error=result.result['global_error'])
            audit_log(c.user.username, 5, unicode(auditmsgs.ACCOUNTEXPORT_MSG),
                      request.host, request.remote_addr,
                      arrow.utcnow().datetime)
        else:
            try:
                session['acexport-count'] += 1
            except KeyError:
                session['acexport-count'] = 1
            session.save()
            if (session['acexport-count'] >= 10
                    and result.state in ['PENDING', 'RETRY', 'FAILURE']):
                result.revoke()
                del session['acexport-count']
                session.save()
                msg = _('The export could not be processed, try again later')
                flash_alert(msg)
                log.info(msg)
                redirect(url(controller='accounts', action='index'))
            finished = False
            results = dict(f=None, global_error=None)

        c.finished = finished
        c.results = results
        c.success = result.successful()
        dwn = request.GET.get('d', None)
        if finished and (dwn and dwn == 'y'):
            response.content_type = 'text/csv'
            response.headers['Cache-Control'] = 'max-age=0'
            csvdata = result.result['f']
            disposition = 'attachment; filename=accounts-export-%s.csv' % \
                            taskid
            response.headers['Content-Disposition'] = str(disposition)
            response.headers['Content-Length'] = len(csvdata)
            return csvdata
        return self.render('/accounts/exportstatus.html')

    # pylint: disable-msg=R0201,W0613
    @ActionProtector(All(not_anonymous(), OnlyAdminUsers()))
    def setnum(self, format=None):
        "Set number of account items returned"
        num = check_num_param(request)

        if num and num in [10, 20, 50, 100]:
            session['accounts_num_items'] = num
            session.save()
        nextpage = request.headers.get('Referer', '/')
        if '://' in nextpage:
            from_url = urlparse(nextpage)
            nextpage = from_url[2]
        redirect(nextpage)
示例#2
0
from baruwa.model.meta import Session
from baruwa.model.auth import LDAPSettings, RadiusSettings
from baruwa.model.accounts import Group, domain_owners
from baruwa.model.accounts import organizations_admins as oa
from baruwa.model.domains import DomainAlias
from baruwa.model.domains import Domain, DeliveryServer, AuthServer
from baruwa.forms.domains import BulkDelDomains, AddLDAPSettingsForm
from baruwa.forms.domains import AddDomainForm, AddDeliveryServerForm
from baruwa.forms.domains import AddAuthForm, AUTH_PROTOCOLS, EditDomainAlias
from baruwa.forms.domains import AddDomainAlias, AddRadiusSettingsForm
from baruwa.lib.audit.msgs.domains import *

log = logging.getLogger(__name__)


@ControllerProtector(All(not_anonymous(), OnlyAdminUsers()))
class DomainsController(BaseController):
    def __before__(self):
        "set context"
        BaseController.__before__(self)
        if self.identity:
            c.user = self.identity['user']
        else:
            c.user = None
        c.selectedtab = 'domains'

    def _get_server(self, destinationid):
        "utility"
        try:
            cachekey = u'deliveryserver-%s' % destinationid
            q = Session.query(DeliveryServer)\
示例#3
0
class AccountsController(BaseController):
    "Accounts controller"

    def __before__(self):
        "set context"
        BaseController.__before__(self)
        if self.identity:
            c.user = self.identity['user']
        else:
            c.user = None
        c.selectedtab = 'accounts'

    def _get_address(self, addressid):
        "return address"
        try:
            address = Session.query(Address).get(addressid)
        except NoResultFound:
            address = None
        return address

    def login(self):
        "login"
        if request.remote_addr in session:
            if session[request.remote_addr] > now():
                abort(409,
                      _('You have been banned after'
                        ' several failed logins'))
            else:
                del session[request.remote_addr]
                session.save()

        identity = request.environ.get('repoze.who.identity')
        came_from = unquote(str(request.GET.get('came_from', '')))
        if not came_from or ' ' in came_from:
            came_from = url('home')
        if '://' in came_from:
            from_url = urlparse(came_from)
            came_from = from_url[2]

        if identity:
            redirect(url(came_from))
        else:
            c.came_from = came_from
            c.login_counter = request.environ['repoze.who.logins']
            if c.login_counter >= 3:
                ban_until = now() + timedelta(minutes=5)
                if request.remote_addr not in session:
                    session[request.remote_addr] = ban_until
                    session.save()
                else:
                    if now() > session[request.remote_addr]:
                        del session[request.remote_addr]
                        session.save()
            return render('/accounts/login.html')

    def loggedin(self):
        "Landing page"
        came_from = (unquote(str(request.params.get('came_from', '')))
                     or url('/'))
        if not self.identity:
            login_counter = request.environ['repoze.who.logins'] + 1
            redirect(
                url('/accounts/login',
                    came_from=came_from,
                    __logins=login_counter))
        userid = self.identity['repoze.who.userid']
        user = self.identity['user']
        if user is None:
            try:
                user = User(username=userid, email=userid)
                user.active = True
                local_part, domain = userid.split('@')
                domains = Session.query(Domain)\
                        .filter(Domain.name == domain)\
                        .all()
                user.domains = domains
                user.timezone = domains[0].timezone
                Session.add(user)
                Session.commit()
                msg = _('First time Login from external auth,'
                        ' your local account was created')
                addresses = []
                if ('tokens' in self.identity
                        and 'ldap' in self.identity['tokens']):
                    lsettings = Session.query(AuthServer.address,
                                    AuthServer.port, LDAPSettings.binddn,
                                    LDAPSettings.bindpw,
                                    LDAPSettings.usetls)\
                                    .join(Domain)\
                                    .filter(AuthServer.enabled == True)\
                                    .filter(Domain.name == domain)\
                                    .all()
                    lsettings = lsettings[0]
                    lurl = make_ldap_uri(lsettings.address, lsettings.port)
                    base_dn = get_user_dn(self.identity['tokens'][1])
                    attributes = [
                        'sn', 'givenName', 'proxyAddresses', 'mail', 'memberOf'
                    ]
                    ldapattributes = LDAPAttributes(lurl,
                                                    base_dn,
                                                    attributes=attributes,
                                                    bind_dn=lsettings.binddn,
                                                    bind_pass=lsettings.bindpw,
                                                    start_tls=lsettings.usetls)
                    ldapattributes()
                    attrmap = {
                        'sn': 'lastname',
                        'givenName': 'firstname',
                        'mail': 'email',
                    }

                    update_attrs = False

                    doms = [domains[0].name]
                    doms.extend([alias.name for alias in domains[0].aliases])

                    for attr in attrmap:
                        if (attr == 'mail' and attr in ldapattributes
                                and ldapattributes[attr][0] == user.email):
                            # Dont update if user.email = directory.email
                            continue
                        if (attr == 'mail' and attr in ldapattributes
                                and '@' in ldapattributes[attr][0]):
                            # Update if email is hosted by us
                            if ldapattributes[attr][0].split('@')[1] in doms:
                                setattr(user, attrmap[attr],
                                        ldapattributes[attr][0])
                                update_attrs = True
                            continue
                        if attr in ldapattributes:
                            setattr(user, attrmap[attr],
                                    ldapattributes[attr][0])
                            update_attrs = True

                    if update_attrs:
                        Session.add(user)
                        Session.commit()

                    # accounts aliases
                    if 'proxyAddresses' in ldapattributes:
                        for mailaddr in ldapattributes['proxyAddresses']:
                            try:
                                if mailaddr.startswith('SMTP:'):
                                    continue
                                if (mailaddr.startswith('smtp:') and
                                        mailaddr.strip('smtp:').lsplit('@')[1]
                                        in doms):
                                    # Only add domain if we host it
                                    address = Address(
                                        PROXY_ADDR_RE.sub('', mailaddr))
                                    address.user = user
                                    addresses.append(address)
                            except IndexError:
                                pass
                    # accounts groups
                    if 'memberOf' in ldapattributes:
                        for group_dn in ldapattributes['memberOf']:
                            groupattributes = LDAPAttributes(
                                lurl,
                                group_dn,
                                attributes=['proxyAddresses'],
                                bind_dn=lsettings.binddn,
                                bind_pass=lsettings.bindpw,
                                start_tls=lsettings.usetls)
                            groupattributes()
                            for mailaddr in groupattributes['proxyAddresses']:
                                try:
                                    mailaddr = mailaddr.lower()
                                    if (mailaddr.startswith('smtp:')
                                            and mailaddr.lstrip('smtp:').split(
                                                '@')[1] in doms):
                                        address = Address(
                                            PROXY_ADDR_RE.sub('', mailaddr))
                                        address.user = user
                                        addresses.append(address)
                                except IndexError:
                                    pass
                else:
                    for alias in domains[0].aliases:
                        address = Address('%s@%s' % (local_part, alias.name))
                        address.user = user
                        addresses.append(address)
                for unsaved in addresses:
                    try:
                        Session.add(unsaved)
                        Session.commit()
                    except IntegrityError:
                        Session.rollback()
            except IntegrityError:
                Session.rollback()
                redirect(url('/logout'))
        else:
            msg = _('Login successful, Welcome back %(username)s !' %
                    dict(username=userid))
        user.last_login = now()
        Session.add(user)
        Session.commit()
        if user.is_peleb:
            for domain in user.domains:
                if check_language(domain.language):
                    session['lang'] = domain.language
                    session.save()
                    break
        session['taskids'] = []
        session.save()
        info = ACCOUNTLOGIN_MSG % dict(u=user.username)
        audit_log(user.username, 6, info, request.host, request.remote_addr,
                  now())
        flash(msg)
        redirect(url(came_from))

    def loggedout(self):
        "Logged out page"
        # response.delete_cookie('baruwacsrf')
        if 'lang' in session:
            lang = session['lang']
        session.clear()
        if 'lang' in locals():
            session['lang'] = lang
        session.save()
        #flash(_('You have been logged out !'))
        came_from = (unquote(str(request.params.get('came_from', '')))
                     or url('/accounts/login/'))
        redirect(url(came_from))

    def passwdreset(self):
        """Render password reset page"""
        c.came_from = '/'
        c.login_counter = 0
        return render('/accounts/login.html')

    @ActionProtector(All(not_anonymous(), OnlyAdminUsers(),
                         CanAccessAccount()))
    def pwchange(self, userid):
        """Reset a user password"""
        user = self._get_user(userid)
        if not user:
            abort(404)
        c.form = ChangePasswordForm(request.POST, csrf_context=session)
        if request.POST and c.form.validate():
            if user.local:
                user.set_password(c.form.password1.data)
                Session.add(user)
                Session.commit()
                flash(
                    _('The account password for %(name)s has been reset') %
                    dict(name=user.username))
                info = PASSWORDCHANGE_MSG % dict(u=user.username)
                audit_log(c.user.username, 2, info, request.host,
                          request.remote_addr, now())
            else:
                flash(
                    _('This is an external account, use'
                      ' external system to reset the password'))
            redirect(url('account-detail', userid=user.id))
        c.id = userid
        c.username = user.username
        c.posturl = 'accounts-pw-change'
        return render('/accounts/pwchange.html')

    @ActionProtector(not_anonymous())
    def upwchange(self, userid):
        """User change own password"""
        user = self._get_user(userid)
        if not user:
            abort(404)
        if user.id != c.user.id or c.user.is_superadmin:
            abort(403)
        c.form = UserPasswordForm(request.POST, csrf_context=session)
        if (request.POST and c.form.validate()
                and user.validate_password(c.form.password3.data)):
            if user.local:
                user.set_password(c.form.password1.data)
                Session.add(user)
                Session.commit()
                flash(
                    _('The account password for %(name)s has been reset') %
                    dict(name=user.username))
                info = PASSWORDCHANGE_MSG % dict(u=user.username)
                audit_log(c.user.username, 2, info, request.host,
                          request.remote_addr, now())
            else:
                flash(
                    _('This is an external account, use'
                      ' external system to reset the password'))
            redirect(url('account-detail', userid=user.id))
        elif (request.POST
              and not user.validate_password(c.form.password3.data)
              and not c.form.password3.errors):
            flash_alert(
                _('The old password supplied does'
                  ' not match our records'))
        c.id = userid
        c.username = user.username
        c.posturl = 'accounts-pw-uchange'
        return render('/accounts/pwchange.html')

    @ActionProtector(All(not_anonymous(), OnlyAdminUsers()))
    def index(self, page=1, orgid=None, domid=None, format=None):
        """GET /accounts/: Paginate items in the collection"""
        num_items = session.get('accounts_num_items', 10)
        c.form = BulkDelUsers(request.POST, csrf_context=session)
        if request.POST:
            if str(c.user.id) in c.form.accountid.data:
                c.form.accountid.data.remove(str(c.user.id))
            if c.form.accountid.data and c.form.whatdo.data == 'disable':
                Session.query(User)\
                .filter(User.id.in_(c.form.accountid.data))\
                .update({'active':False}, synchronize_session='fetch')
                Session.commit()
            if c.form.accountid.data and c.form.whatdo.data == 'enable':
                Session.query(User)\
                .filter(User.id.in_(c.form.accountid.data))\
                .update({'active':True}, synchronize_session='fetch')
                Session.commit()
            if c.form.accountid.data and c.form.whatdo.data == 'delete':
                session['bulk_account_delete'] = c.form.accountid.data
                session.save()
                # redirect for confirmation
                redirect(url('accounts-confirm-delete'))
        users = Session.query(User.id, User.username, User.firstname,
                              User.lastname, User.email, User.active,
                              User.local, User.account_type).order_by(User.id)
        usrcount = Session.query(User.id)
        if c.user.is_domain_admin:
            users = users.join(domain_users, (dom_owns,
                                domain_users.c.domain_id ==
                                dom_owns.c.domain_id),
                                (oas,
                                dom_owns.c.organization_id ==
                                oas.c.organization_id))\
                                .filter(oas.c.user_id == c.user.id)
            usrcount = usrcount.join(domain_users, (dom_owns,
                                    domain_users.c.domain_id ==
                                    dom_owns.c.domain_id),
                                    (oas,
                                    dom_owns.c.organization_id ==
                                    oas.c.organization_id))\
                                    .filter(oas.c.user_id == c.user.id)
        if domid:
            users = users.filter(
                and_(domain_users.c.domain_id == domid,
                     domain_users.c.user_id == User.id))
            usrcount = usrcount.filter(
                and_(domain_users.c.domain_id == domid,
                     domain_users.c.user_id == User.id))
        if orgid:
            users = users.filter(
                and_(
                    domain_users.c.user_id == User.id,
                    domain_users.c.domain_id == dom_owns.c.domain_id,
                    dom_owns.c.organization_id == orgid,
                ))
            usrcount = usrcount.filter(
                and_(
                    domain_users.c.user_id == User.id,
                    domain_users.c.domain_id == dom_owns.c.domain_id,
                    dom_owns.c.organization_id == orgid,
                ))

        pages = paginate.Page(users,
                              page=int(page),
                              items_per_page=num_items,
                              item_count=usrcount.count())
        if format == 'json':
            response.headers['Content-Type'] = 'application/json'
            data = convert_acct_to_json(pages, orgid)
            return data

        c.page = pages
        c.domid = domid
        c.orgid = orgid
        return render('/accounts/index.html')

    @ActionProtector(All(not_anonymous(), OnlyAdminUsers()))
    def search(self, format=None):
        "Search for accounts"
        total_found = 0
        search_time = 0
        num_items = session.get('accounts_num_items', 10)
        q = request.GET.get('q', '')
        d = request.GET.get('d', None)
        kwds = {'presliced_list': True}
        page = int(request.GET.get('p', 1))
        conn = SphinxClient()
        conn.SetMatchMode(SPH_MATCH_EXTENDED2)
        conn.SetFieldWeights(
            dict(username=50, email=30, firstname=10, lastname=10))
        if page == 1:
            conn.SetLimits(0, num_items, 500)
        else:
            page = int(page)
            offset = (page - 1) * num_items
            conn.SetLimits(offset, num_items, 500)
        if d:
            conn.SetFilter('domains', [
                int(d),
            ])
        if c.user.is_domain_admin:
            #crcs = get_dom_crcs(Session, c.user)
            domains = Session.query(Domain.id).join(dom_owns,
                        (oas, dom_owns.c.organization_id ==
                        oas.c.organization_id))\
                        .filter(oas.c.user_id == c.user.id)
            conn.SetFilter('domains', [domain[0] for domain in domains])
        q = clean_sphinx_q(q)
        results = conn.Query(q, 'accounts, accounts_rt')
        q = restore_sphinx_q(q)
        if results and results['matches']:
            ids = [hit['id'] for hit in results['matches']]
            total_found = results['total_found']
            search_time = results['time']
            users = Session.query(User.id,
                                    User.username,
                                    User.firstname,
                                    User.lastname,
                                    User.email,
                                    User.active,
                                    User.local,
                                    User.account_type)\
                                .filter(User.id.in_(ids))\
                                .order_by(User.id)\
                                .all()
            usercount = total_found
        else:
            users = []
            usercount = 0
        c.q = q
        c.d = d
        c.total_found = total_found
        c.search_time = search_time
        c.page = paginate.Page(users,
                               page=int(page),
                               items_per_page=num_items,
                               item_count=usercount,
                               **kwds)
        return render('/accounts/searchresults.html')

    @ActionProtector(All(not_anonymous(), CanAccessAccount()))
    def detail(self, userid):
        """GET /accounts/userid/ Show a specific item"""
        user = self._get_user(userid)
        if not user:
            abort(404)
        c.account = user
        return render('/accounts/account.html')

    @ActionProtector(All(not_anonymous(), OnlyAdminUsers()))
    def add(self):
        """/accounts/new"""
        c.form = AddUserForm(request.POST, csrf_context=session)
        if c.user.is_domain_admin:
            account_types = (('3', 'User'), )
            c.form.account_type.choices = account_types
            c.form.domains.query = Session.query(Domain).join(dom_owns,
                                    (oas, dom_owns.c.organization_id ==
                                    oas.c.organization_id))\
                                    .filter(oas.c.user_id == c.user.id)
        else:
            c.form.domains.query = Session.query(Domain)
        if request.POST and c.form.validate():
            try:
                user = User(username=c.form.username.data,
                            email=c.form.email.data)
                for attr in [
                        'firstname', 'lastname', 'email', 'active',
                        'account_type', 'send_report', 'spam_checks',
                        'low_score', 'high_score', 'timezone'
                ]:
                    setattr(user, attr, getattr(c.form, attr).data)
                user.local = True
                user.set_password(c.form.password1.data)
                if int(user.account_type) == 3:
                    user.domains = c.form.domains.data
                Session.add(user)
                Session.commit()
                update_serial.delay()
                info = ADDACCOUNT_MSG % dict(u=user.username)
                audit_log(c.user.username, 3, info, request.host,
                          request.remote_addr, now())
                flash(
                    _('The account: %(user)s was created successfully') %
                    {'user': c.form.username.data})
                redirect(url('account-detail', userid=user.id))
            except IntegrityError:
                Session.rollback()
                flash_alert(
                    _('Either the username or email address already exist'))
        return render('/accounts/new.html')

    @ActionProtector(All(not_anonymous(), CanAccessAccount()))
    def edit(self, userid):
        """GET /accounts/edit/id: Form to edit an existing item"""
        user = self._get_user(userid)
        if not user:
            abort(404)

        c.form = EditUserForm(request.POST, user, csrf_context=session)
        c.form.domains.query = Session.query(Domain)
        if user.account_type != 3 or c.user.is_peleb:
            del c.form.domains
        if c.user.is_peleb:
            del c.form.username
            del c.form.email
            del c.form.active
        if request.POST and c.form.validate():
            update = False
            kwd = dict(userid=userid)
            for attr in FORM_FIELDS:
                field = getattr(c.form, attr)
                if field and field.data != getattr(user, attr):
                    setattr(user, attr, field.data)
                    update = True
            if update:
                try:
                    Session.add(user)
                    Session.commit()
                    update_serial.delay()
                    flash(_('The account has been updated'))
                    kwd['uc'] = 1
                    info = UPDATEACCOUNT_MSG % dict(u=user.username)
                    audit_log(c.user.username, 2, info, request.host,
                              request.remote_addr, now())
                except IntegrityError:
                    Session.rollback()
                    flash_alert(
                        _('The account: %(acc)s could not be updated') %
                        dict(acc=user.username))
                if (user.id == c.user.id and c.form.active
                        and c.form.active.data == False):
                    redirect(url('/logout'))
            else:
                flash_info(_('No changes made to the account'))
            redirect(url(controller='accounts', action='detail', **kwd))
        c.fields = FORM_FIELDS
        c.id = userid
        return render('/accounts/edit.html')

    @ActionProtector(All(not_anonymous(), OnlyAdminUsers(),
                         CanAccessAccount()))
    def delete(self, userid):
        """/accounts/delete/id"""
        user = self._get_user(userid)
        if not user:
            abort(404)

        c.form = EditUserForm(request.POST, user, csrf_context=session)
        c.form.domains.query = Session.query(Domain)
        if request.POST and c.form.validate():
            username = user.username
            Session.delete(user)
            Session.commit()
            update_serial.delay()
            flash(_('The account has been deleted'))
            info = DELETEACCOUNT_MSG % dict(u=username)
            audit_log(c.user.username, 4, info, request.host,
                      request.remote_addr, now())
            if userid == c.user.id:
                redirect(url('/logout'))
            redirect(url(controller='accounts', action='index'))
        else:
            flash_info(
                _('The account: %(a)s and all associated data'
                  ' will be deleted, This action is not reversible.') %
                dict(a=user.username))
        c.fields = FORM_FIELDS
        c.id = userid
        return render('/accounts/delete.html')

    @ActionProtector(not_anonymous())
    def confirm_delete(self):
        "Confirm mass delete"
        accountids = session.get('bulk_account_delete', [])
        if not accountids:
            redirect(url(controller='accounts', action='index'))

        num_items = 10
        if len(accountids) > num_items and len(accountids) <= 20:
            num_items = 20
        if len(accountids) > num_items and len(accountids) <= 50:
            num_items = 50
        if len(accountids) > num_items and len(accountids) <= 100:
            num_items = 100

        users = Session.query(User).filter(User.id.in_(accountids))
        usrcount = Session.query(User.id)

        if c.user.is_domain_admin and usrcount:
            users = users.join(domain_users, (dom_owns,
                    domain_users.c.domain_id == dom_owns.c.domain_id),
                    (oas, dom_owns.c.organization_id == oas.c.organization_id))\
                    .filter(oas.c.user_id == c.user.id)
            usrcount = usrcount.join(domain_users, (dom_owns,
                        domain_users.c.domain_id == dom_owns.c.domain_id),
                        (oas, dom_owns.c.organization_id ==
                        oas.c.organization_id))\
                        .filter(oas.c.user_id == c.user.id)

        if request.POST:
            tasks = []
            # try:
            for account in users.all():
                info = DELETEACCOUNT_MSG % dict(u=account.username)
                Session.delete(account)
                tasks.append([
                    c.user.username, 4, info, request.host,
                    request.remote_addr,
                    now()
                ])
            Session.commit()
            # except DataError:
            #     flash_alert(_('An error occured try again'))
            #     redirect(url(controller='accounts', action='index'))
            del session['bulk_account_delete']
            session.save()
            update_serial.delay()
            for task in tasks:
                audit_log(*task)
            flash(_('The accounts have been deleted'))
            redirect(url(controller='accounts'))
        else:
            flash(
                _('The following accounts are about to be deleted,'
                  ' this action is not reversible, Do you wish to '
                  'continue ?'))

        try:
            c.page = paginate.Page(users,
                                   page=1,
                                   items_per_page=num_items,
                                   item_count=usrcount.count())
        except DataError:
            flash_alert(_('An error occured try again'))
            redirect(url(controller='accounts', action='index'))
        return render('/accounts/confirmbulkdel.html')

    @ActionProtector(All(not_anonymous(), OnlyAdminUsers(),
                         CanAccessAccount()))
    def addaddress(self, userid):
        "Add address"
        user = self._get_user(userid)
        if not user:
            abort(404)

        c.form = AddressForm(request.POST, csrf_context=session)
        if request.POST and c.form.validate():
            try:
                if c.user.is_domain_admin:
                    # check if they own the domain
                    domain = c.form.address.data.split('@')[1]
                    domain = Session.query(Domain).options(
                                joinedload('organizations')).join(
                                dom_owns, (oas,
                                dom_owns.c.organization_id ==
                                oas.c.organization_id))\
                                .filter(oas.c.user_id == c.user.id)\
                                .filter(Domain.name == domain).one()
                addr = Address(address=c.form.address.data)
                addr.enabled = c.form.enabled.data
                addr.user = user
                Session.add(addr)
                Session.commit()
                update_serial.delay()
                info = ADDRADD_MSG % dict(a=addr.address, ac=user.username)
                audit_log(c.user.username, 3, info, request.host,
                          request.remote_addr, now())
                flash(
                    _('The alias address %(address)s was successfully created.'
                      % dict(address=addr.address)))
            except IntegrityError:
                flash_alert(
                    _('The address %(addr)s already exists') %
                    dict(addr=addr.address))
            except NoResultFound:
                flash(
                    _('Domain: %(d)s does not belong to you') % dict(d=domain))
            redirect(url(controller='accounts', action='detail',
                         userid=userid))
        c.id = userid
        return render('/accounts/addaddress.html')

    @ActionProtector(All(not_anonymous(), OnlyAdminUsers(),
                         CanAccessAccount()))
    def editaddress(self, addressid):
        "Edit address"
        address = self._get_address(addressid)
        if not address:
            abort(404)

        c.form = AddressForm(request.POST, address, csrf_context=session)
        if request.POST and c.form.validate():
            try:
                if (address.address != c.form.address.data
                        or address.enabled != c.form.enabled.data):
                    if c.user.is_domain_admin:
                        # check if they own the domain
                        domain = c.form.address.data.split('@')[1]
                        domain = Session.query(Domain).options(
                                    joinedload('organizations')).join(
                                    dom_owns, (oas,
                                    dom_owns.c.organization_id ==
                                    oas.c.organization_id))\
                                    .filter(oas.c.user_id == c.user.id)\
                                    .filter(Domain.name == domain).one()
                    address.address = c.form.address.data
                    address.enabled = c.form.enabled.data
                    Session.add(address)
                    Session.commit()
                    update_serial.delay()
                    info = ADDRUPDATE_MSG % dict(a=address.address,
                                                 ac=address.user.username)
                    audit_log(c.user.username, 2, info, request.host,
                              request.remote_addr, now())
                    flash(_('The alias address has been updated'))
                else:
                    flash_info(_('No changes were made to the address'))
            except IntegrityError:
                flash_alert(
                    _('The address %(addr)s already exists') %
                    dict(addr=c.form.address.data))
            except NoResultFound:
                flash(
                    _('Domain: %(d)s does not belong to you') % dict(d=domain))
            redirect(
                url(controller='accounts',
                    action='detail',
                    userid=address.user_id))
        c.id = addressid
        c.userid = address.user_id
        return render('/accounts/editaddress.html')

    @ActionProtector(All(not_anonymous(), OnlyAdminUsers(),
                         CanAccessAccount()))
    def deleteaddress(self, addressid):
        "Delete address"
        address = self._get_address(addressid)
        if not address:
            abort(404)

        c.form = AddressForm(request.POST, address, csrf_context=session)
        if request.POST and c.form.validate():
            user_id = address.user_id
            addr = address.address
            username = address.user.username
            Session.delete(address)
            Session.commit()
            update_serial.delay()
            info = ADDRDELETE_MSG % dict(a=addr, ac=username)
            audit_log(c.user.username, 4, info, request.host,
                      request.remote_addr, now())
            flash(_('The address has been deleted'))
            redirect(
                url(controller='accounts', action='detail', userid=user_id))
        c.id = addressid
        c.userid = address.user_id
        return render('/accounts/deleteaddress.html')

    def set_language(self):
        "Set the language"
        nextpage = request.params.get('next', None)
        if not nextpage:
            nextpage = request.headers.get('Referer', None)
        if not nextpage:
            nextpage = '/'
        if '://' in nextpage:
            from_url = urlparse(nextpage)
            nextpage = from_url[2]
        lang_code = request.params.get('language', None)
        if lang_code and check_language(lang_code):
            session['lang'] = lang_code
            session.save()
        params = []
        for param in request.params:
            if not param in ['language', 'amp']:
                value = request.params[param]
                if value:
                    if (param == 'came_from'
                            and '://' in urllib2.unquote(value)):
                        urlparts = urlparse(urllib2.unquote(value))
                        value = urlparts[2] or '/'
                    params.append('%s=%s' %
                                  (urllib2.quote(param), urllib2.quote(value)))
        if params:
            nextpage = "%s?%s" % (nextpage, '&amp;'.join(params))
        redirect(nextpage)

    @ActionProtector(All(not_anonymous(), OwnsDomain()))
    def import_accounts(self, domainid):
        "import accounts"
        try:
            cachekey = u'domain-%s' % domainid
            domain = Session.query(Domain.id, Domain.name)\
                    .filter(Domain.id==domainid)\
                    .options(FromCache('sql_cache_med', cachekey)).one()
        except NoResultFound:
            abort(404)

        c.form = ImportCSVForm(request.POST, csrf_context=session)
        if request.POST and c.form.validate():
            basedir = config['pylons.cache_dir']
            csvdata = request.POST['csvfile']
            if hasattr(csvdata, 'filename'):
                dstfile = os.path.join(basedir, 'uploads',
                                       csvdata.filename.lstrip(os.sep))
                if not os.path.exists(dstfile) and iscsv(csvdata.file):
                    csvfile = open(dstfile, 'w')
                    shutil.copyfileobj(csvdata.file, csvfile)
                    csvdata.file.close()
                    csvfile.close()
                    task = importaccounts.apply_async(args=[
                        domainid, dstfile, c.form.skipfirst.data, c.user.id
                    ])
                    session['taskids'].append(task.task_id)
                    session['acimport-count'] = 1
                    session['acimport-file'] = dstfile
                    session.save()
                    flash(
                        _('File uploaded, and is being processed, this page'
                          ' will automatically refresh to show the status'))
                    redirect(url('accounts-import-status',
                                 taskid=task.task_id))
                else:
                    filename = csvdata.filename.lstrip(os.sep)
                    if not iscsv(csvdata.file):
                        flash_alert(
                            _('The file: %s is not a CSV file') % filename)
                    else:
                        flash_alert(
                            _('The file: %s already exists and'
                              ' is being processed.') % filename)
                    csvdata.file.close()
            else:
                flash_alert(_('No CSV was file uploaded, try again'))

        c.domain = domain
        return render('/accounts/importaccounts.html')

    @ActionProtector(All(not_anonymous(), OnlyAdminUsers()))
    def import_status(self, taskid):
        "import status"
        result = AsyncResult(taskid)
        if result is None or taskid not in session['taskids']:
            flash(_('The task status requested has expired or does not exist'))
            redirect(url(controller='accounts', action='index'))

        if result.ready():
            finished = True
            flash.pop_messages()
            if isinstance(result.result, Exception):
                if c.user.is_superadmin:
                    flash_alert(
                        _('Error occured in processing %s') % result.result)
                else:
                    flash_alert(_('Backend error occured during processing.'))
                redirect(url(controller='accounts'))
            update_serial.delay()
            audit_log(c.user.username, 3, ACCOUNTIMPORT_MSG, request.host,
                      request.remote_addr, now())
        else:
            session['acimport-count'] += 1
            if (session['acimport-count'] >= 10
                    and result.state in ['PENDING', 'RETRY', 'FAILURE']):
                result.revoke()
                try:
                    os.unlink(session['acimport-file'])
                except OSError:
                    pass
                del session['acimport-count']
                session.save()
                flash_alert(
                    _('The import could not be processed,'
                      ' try again later'))
                redirect(url(controller='accounts'))
            finished = False

        c.finished = finished
        c.results = result.result
        c.success = result.successful()
        return render('/accounts/importstatus.html')

    @ActionProtector(All(not_anonymous(), OnlyAdminUsers()))
    def export_accounts(self, domainid=None, orgid=None):
        "export domains"
        task = exportaccounts.apply_async(args=[domainid, c.user.id, orgid])
        if not 'taskids' in session:
            session['taskids'] = []
        session['taskids'].append(task.task_id)
        session['acexport-count'] = 1
        session.save()
        flash(_('Accounts export is being processed'))
        redirect(url('accounts-export-status', taskid=task.task_id))

    @ActionProtector(All(not_anonymous(), OnlyAdminUsers()))
    def export_status(self, taskid):
        "export status"
        result = AsyncResult(taskid)
        if result is None or taskid not in session['taskids']:
            flash(_('The task status requested has expired or does not exist'))
            redirect(url(controller='accounts', action='index'))

        if result.ready():
            finished = True
            flash.pop_messages()
            if isinstance(result.result, Exception):
                if c.user.is_superadmin:
                    flash_alert(
                        _('Error occured in processing %s') % result.result)
                else:
                    flash_alert(_('Backend error occured during processing.'))
                redirect(url(controller='accounts', action='index'))
            results = dict(
                f=True if not result.result['global_error'] else False,
                id=taskid,
                global_error=result.result['global_error'])
            audit_log(c.user.username, 5, ACCOUNTEXPORT_MSG, request.host,
                      request.remote_addr, now())
        else:
            session['acexport-count'] += 1
            if (session['acexport-count'] >= 10
                    and result.state in ['PENDING', 'RETRY', 'FAILURE']):
                result.revoke()
                del session['acexport-count']
                session.save()
                flash_alert(
                    _('The export could not be processed, try again later'))
                redirect(url(controller='accounts', action='index'))
            finished = False
            results = dict(f=None, global_error=None)

        c.finished = finished
        c.results = results
        c.success = result.successful()
        d = request.GET.get('d', None)
        if finished and (d and d == 'y'):
            response.content_type = 'text/csv'
            response.headers['Cache-Control'] = 'max-age=0'
            csvdata = result.result['f']
            disposition = 'attachment; filename=accounts-export-%s.csv' % taskid
            response.headers['Content-Disposition'] = str(disposition)
            response.headers['Content-Length'] = len(csvdata)
            return csvdata
        return render('/accounts/exportstatus.html')

    @ActionProtector(All(not_anonymous(), OnlyAdminUsers()))
    def setnum(self, format=None):
        "Set number of account items returned"
        num = check_num_param(request)

        if num and num in [10, 20, 50, 100]:
            session['accounts_num_items'] = num
            session.save()
        nextpage = request.headers.get('Referer', '/')
        if '://' in nextpage:
            from_url = urlparse(nextpage)
            nextpage = from_url[2]
        redirect(nextpage)
示例#4
0
class StatusController(BaseController):
    def __before__(self):
        "set context"
        BaseController.__before__(self)
        if self.identity:
            c.user = self.identity['user']
        else:
            c.user = None
        c.selectedtab = 'status'

    def _get_server(self, serverid):
        "utility"
        try:
            server = Session.query(Server).get(serverid)
        except NoResultFound:
            server = None
        return server

    @ActionProtector(OnlyAdminUsers())
    def index(self):
        "System status"
        c.servers = Session.query(Server).filter(
                    Server.hostname != 'default').all()
        labels = dict(clean=_('Clean'),
                    highspam=_('High scoring spam'),
                    lowspam=_('Low scoring spam'),
                    virii=_('Virus infected'),
                    infected=_('Policy blocked'))
        pie_colors = dict(clean='#006400',
                    highspam='#FF0000',
                    lowspam='#ffa07a',
                    virii='#000000',
                    infected='#d2691e')
        jsondata = [dict(tooltip=labels[attr],
                    y=getattr(c.baruwa_totals, attr),
                    stroke='black',
                    color=pie_colors[attr])
                    for attr in ['clean', 'highspam', 'lowspam', 'virii',
                    'infected'] if getattr(c.baruwa_totals, attr)]
        c.chart_data = json.dumps(jsondata)
        return render('/status/index.html')

    def graph(self, nodeid=None):
        "Generate graphical system status"
        totals = DailyTotals(Session, c.user)
        if nodeid:
            server = self._get_server(nodeid)
            if not server:
                abort(404)
            baruwa_totals = totals.get(hostname=server.hostname)
        else:
            baruwa_totals = totals.get()

        if not baruwa_totals.total:
            abort(404)

        piedata = []
        labels = []
        for attr in ['clean', 'highspam', 'lowspam', 'virii', 'infected']:
            value = getattr(baruwa_totals, attr) or 0
            piedata.append(value)
            if baruwa_totals.total > 0:
                labels.append(("%.1f%% %s" % ((1.0 * value /
                        baruwa_totals.total) * 100, attr)))
            else:
                labels.append('0%')
        pie = PieChart(350, 264)
        pie.chart.labels = labels
        pie.chart.data = piedata
        pie.chart.width = 180
        pie.chart.height = 180
        pie.chart.x = 90
        pie.chart.y = 30
        pie.chart.slices.strokeWidth = 1
        pie.chart.slices.strokeColor = colors.black
        pie.chart.slices[0].fillColor = PIE_CHART_COLORS[5]
        pie.chart.slices[1].fillColor = PIE_CHART_COLORS[0]
        pie.chart.slices[2].fillColor = PIE_CHART_COLORS[1]
        pie.chart.slices[3].fillColor = PIE_CHART_COLORS[9]
        pie.chart.slices[4].fillColor = PIE_CHART_COLORS[3]
        self.imgfile = StringIO()
        renderPM.drawToFile(pie,
                            self.imgfile, 'PNG',
                            bg=colors.HexColor('#ffffff'))
        response.content_type = 'image/png'
        response.headers['Cache-Control'] = 'max-age=0'
        return self.imgfile.getvalue()

    def mailq(self, serverid=None, queue='inbound', page=1, direction='dsc',
        order_by='timestamp'):
        "Display mailqueue"
        server = None
        if not serverid is None:
            server = self._get_server(serverid)
            if not server:
                abort(404)

        c.queue = queue
        c.server = server
        c.direction = direction
        c.order_by = desc(order_by) if direction == 'dsc' else order_by
        if queue == 'inbound':
            qdirection = 1
        else:
            qdirection = 2

        num_items = session.get('mailq_num_items', 10)

        query = Session.query(MailQueueItem).filter(
                MailQueueItem.direction == qdirection).order_by(order_by)

        if server:
            query = query.filter(MailQueueItem.hostname == server.hostname)

        uquery = UserFilter(Session, c.user, query, model=MailQueueItem)
        query = uquery.filter()

        c.form = MailQueueProcessForm(request.POST, csrf_context=session)
        pages = paginate.Page(query, page=int(page), items_per_page=num_items)
        choices = [(str(item.id), item.id) for item in pages.items]
        session['queue_choices'] = choices
        session.save()

        c.page = paginate.Page(query, page=int(page),
                                items_per_page=num_items)
        return render('/status/mailq.html')

    def process_mailq(self):
        "Process mailq"
        sendto = url('mailq-status')
        choices = session.get('queue_choices', [])
        form = MailQueueProcessForm(request.POST, csrf_context=session)
        form.id.choices = choices

        if request.POST and form.validate() and choices:
            queueids = form.id.data
            if form.queue_action.data != '0':
                hosts = {}
                direction = None
                queueitems = Session.query(MailQueueItem)\
                            .filter(MailQueueItem.id.in_(queueids))\
                            .all()
                for item in queueitems:
                    if not item.hostname in hosts:
                        hosts[item.hostname] = []
                    if not direction:
                        direction = item.direction
                    hosts[item.hostname].append(item.messageid)
                for hostname in hosts:
                    process_queued_msgs.apply_async(args=[hosts[hostname],
                                                    form.queue_action.data,
                                                    direction],
                                                    queue=hostname)
                flash(_('The request has been queued for processing'))
            session['queue_choices'] = []
            session.save()

        referer = request.headers.get('Referer', None)
        if referer and '/mailq' in referer:
            sendto = referer
        redirect(sendto)

    def mailq_detail(self, queueid):
        "View a queued message's details"
        query = Session.query(MailQueueItem)
        uquery = UserFilter(Session, c.user, query, model=MailQueueItem)
        query = uquery.filter()

        try:
            mailqitem = query.filter(MailQueueItem.id == queueid).one()
        except NoResultFound:
            #abort(404)
            flash_alert(_('The requested queued message was not found.'))
            redirect(url('mailq-status'))

        c.mailqitem = mailqitem
        c.form = MailQueueProcessForm(request.POST, csrf_context=session)
        session['queue_choices'] = [(queueid, queueid),]
        session.save()
        return render('/status/detail.html')

    def mailq_preview(self, queueid, attachid=None, imgid=None, allowimgs=None):
        "preview a queued message"
        query = Session.query(MailQueueItem)
        uquery = UserFilter(Session, c.user, query, model=MailQueueItem)
        query = uquery.filter()

        try:
            mailqitem = query.filter(MailQueueItem.id == queueid).one()
        except NoResultFound:
            flash_alert(_('The requested queued message was not found.'))
            redirect(url('mailq-status'))

        try:
            task = preview_queued_msg.apply_async(args=[mailqitem.messageid,
                    mailqitem.direction, attachid, imgid],
                    queue=mailqitem.hostname)
            task.wait(30)
            if task.result:
                if imgid:
                    response.content_type = task.result['content_type']
                    if task.result and 'img' in task.result:
                        info = QUEUEDOWNLOAD_MSG % dict(m=mailqitem.messageid,
                                                        a=task.result['name'])
                        audit_log(c.user.username,
                                1, info, request.host,
                                request.remote_addr, datetime.now())
                        return base64.decodestring(task.result['img'])
                    abort(404)
                if attachid:
                    info = QUEUEDOWNLOAD_MSG % dict(m=mailqitem.messageid,
                                                    a=task.result['name'])
                    audit_log(c.user.username,
                            1, info, request.host,
                            request.remote_addr, datetime.now())
                    response.content_type = task.result['mimetype']
                    dispos = 'attachment; filename="%s"' % task.result['name']
                    response.headers['Content-Disposition'] = dispos
                    content_len = len(task.result['attachment'])
                    response.headers['Content-Length'] = content_len
                    response.headers['Pragma'] = 'public'
                    response.headers['Cache-Control'] = 'max-age=0'
                    return base64.decodestring(task.result['attachment'])
                for part in task.result['parts']:
                    if part['type'] == 'html':
                        html = fromstring(part['content'])
                        for element, attribute, link, pos in iterlinks(html):
                            if not link.startswith('cid:'):
                                if not allowimgs and attribute == 'src':
                                    element.attrib['src'] = '%simgs/blocked.gif' % media_url()
                                    element.attrib['title'] = link
                                    flash(_('This message contains external images, which have been blocked. ') +
                                    literal(link_to(_('Display images'),
                                    url('queue-preview-with-imgs', queueid=queueid), class_='uline')))
                            else:
                                imgname = link.replace('cid:', '')
                                element.attrib['src'] = url('queue-preview-img',
                                                        imgid=imgname.replace('/', '__xoxo__'),
                                                        queueid=queueid)
                        part['content'] = tostring(html)
                c.message = task.result
                info = QUEUEPREVIEW_MSG % dict(m=mailqitem.messageid)
                audit_log(c.user.username,
                        1, info, request.host,
                        request.remote_addr, datetime.now())
            else:
                raise TimeoutError
        except (TimeoutError, QueueNotFound):
            flash_alert(_('The message could not be processed'))
            redirect(url('mailq-status'))
        c.queueid = queueid
        c.messageid = mailqitem.messageid
        return render('/status/preview.html')

    @ActionProtector(OnlySuperUsers())
    def server_status(self, serverid):
        "Display server status"
        server = self._get_server(serverid)
        if not server:
            abort(404)

        totals = DailyTotals(Session, c.user)
        mailq = MailQueue(Session, c.user)
        totals = totals.get(server.hostname)
        inbound = mailq.get(1, server.hostname)[0]
        outbound = mailq.get(2, server.hostname)[0]

        statusdict = dict(total=totals.total,
                        mta=0,
                        scanners=0,
                        av=0,
                        clean_mail=totals.clean,
                        high_spam=totals.highspam,
                        virii=totals.virii,
                        spam_mail=totals.lowspam,
                        inq=inbound,
                        outq=outbound,
                        otherinfected=totals.infected,
                        uptime='Unknown',
                        load=(0, 0, 0),
                        mem=dict(free=0, used=0, total=0,
                                percent=0),
                        partitions=[],
                        net={},
                        cpu=0)
        try:
            task = systemstatus.apply_async(queue=server.hostname)
            task.wait(30)
            hoststatus = task.result
            statusdict.update(hoststatus)
            info = HOSTSTATUS_MSG % dict(n=server.hostname)
            audit_log(c.user.username,
                    1, info, request.host,
                    request.remote_addr, datetime.now())
        except (TimeoutError, QueueNotFound):
            pass

        c.server = server
        c.status = statusdict
        return render('/status/serverstatus.html')

    @ActionProtector(OnlySuperUsers())
    def server_bayes_status(self, serverid):
        "Display bayes stats"
        server = self._get_server(serverid)
        if not server:
            abort(404)

        try:
            task = bayesinfo.apply_async(queue=server.hostname)
            task.wait(30)
            result = task.result
            info = HOSTBAYES_MSG % dict(n=server.hostname)
            audit_log(c.user.username,
                    1, info, request.host,
                    request.remote_addr, datetime.now())
        except (TimeoutError, QueueNotFound):
            result = {}
        c.server = server
        c.data = result
        return render('/status/bayes.html')

    @ActionProtector(OnlySuperUsers())
    def server_salint_stat(self, serverid):
        "Display server salint output"
        server = self._get_server(serverid)
        if not server:
            abort(404)

        try:
            task = salint.apply_async(queue=server.hostname)
            task.wait(30)
            result = task.result
            info = HOSTSALINT_MSG % dict(n=server.hostname)
            audit_log(c.user.username,
                    1, info, request.host,
                    request.remote_addr, datetime.now())
        except (TimeoutError, QueueNotFound):
            result = []
        c.server = server
        c.data = result
        return render('/status/salint.html')

    @ActionProtector(OnlySuperUsers())
    def audit(self, page=1, format=None):
        "Audit log"
        total_found = 0
        search_time = 0
        num_items = session.get('auditlog_num_items', 50)
        q = request.GET.get('q', None)
        kwds = {}
        if q:
            conn = SphinxClient()
            conn.SetMatchMode(SPH_MATCH_EXTENDED2)
            if page == 1:
                conn.SetLimits(0, num_items, 500)
            else:
                page = int(page)
                offset = (page - 1) * num_items
                conn.SetLimits(offset, num_items, 500)
            q = clean_sphinx_q(q)
            results = conn.Query(q, 'auditlog, auditlog_rt')
            q = restore_sphinx_q(q)
            if results and results['matches']:
                ids = [hit['id'] for hit in results['matches']]
                query = Session.query(AuditLog)\
                        .filter(AuditLog.id.in_(ids))\
                        .order_by(desc('timestamp'))\
                        .all()
                total_found = results['total_found']
                search_time = results['time']
                logcount = total_found
                kwds['presliced_list'] = True
            else:
                query = []
                lcount = 0
                logcount = 0
        else:
            query = Session.query(AuditLog)\
                    .order_by(desc('timestamp'))
            lcount = Session.query(AuditLog)\
                    .order_by(desc('timestamp'))
        if not 'logcount' in locals():
            logcount = lcount.count()
        items = paginate.Page(query, page=int(page),
                            items_per_page=num_items,
                            item_count=logcount, **kwds)
        if format == 'json':
            response.headers['Content-Type'] = 'application/json'
            jdict = convert_settings_to_json(items)
            if q:
                encoded = json.loads(jdict)
                encoded['q'] = q
                jdict = json.dumps(encoded)
            return jdict

        c.page = items
        c.q = q
        c.total_found = total_found
        c.search_time = search_time
        return render('/status/audit.html')

    @ActionProtector(OnlySuperUsers())
    def audit_export(self, isquery=None, format=None):
        "Export audit logs"
        query = request.GET.get('q', None)
        if isquery and query is None:
            flash_alert(_('No query specified for audit log export'))
            redirect(url('status-audit-logs'))

        task = export_auditlog.apply_async(args=[format, query])
        if not 'taskids' in session:
            session['taskids'] = []
        session['taskids'].append(task.task_id)
        session['exportauditlog-counter'] = 1
        session.save()
        redirect(url('status-auditlog-export-status', taskid=task.task_id))

    @ActionProtector(OnlySuperUsers())
    def audit_export_status(self, taskid):
        "Audit log export status"
        result = AsyncResult(taskid)
        if result is None or taskid not in session['taskids']:
            flash(_('The task status requested has expired or does not exist'))
            redirect(url('status-audit-logs'))

        if result.ready():
            finished = True
            flash.pop_messages()
            if isinstance(result.result, Exception):
                if c.user.is_superadmin:
                    flash_alert(_('Error occured in processing %s') %
                                result.result)
                else:
                    flash_alert(_('Backend error occured during processing.'))
                redirect(url('status-audit-logs'))
        else:
            session['exportauditlog-counter'] += 1
            session.save()
            if (session['exportauditlog-counter'] >= 20 and
                result.state in ['PENDING', 'RETRY', 'FAILURE']):
                result.revoke()
                del session['exportauditlog-counter']
                session.save()
                flash_alert(_('The audit log export failed, try again later'))
                redirect(url('status-audit-logs'))
            finished = False

        c.finished = finished
        c.results = result.result
        c.success = result.successful()
        d = request.GET.get('d', None)
        if finished and (d and d == 'y'):
            audit_log(c.user.username,
                    5, AUDITLOGEXPORT_MSG, request.host,
                    request.remote_addr, datetime.now())
            response.content_type = result.result['content_type']
            response.headers['Cache-Control'] = 'max-age=0'
            respdata = result.result['f']
            disposition = 'attachment; filename=%s' % result.result['filename']
            response.headers['Content-Disposition'] = disposition
            response.headers['Content-Length'] = len(respdata)
            return respdata
        return render('/status/auditexportstatus.html')

    def setnum(self, format=None):
        "Set number of items to return for auditlog/mailq"
        app = request.GET.get('ac', 'mailq')
        num = check_num_param(request)

        if num and num in [10, 20, 50, 100]:
            if app == 'auditlog':
                session['auditlog_num_items'] = num
            else:
                session['mailq_num_items'] = num
            session.save()
        nextpage = request.headers.get('Referer', '/')
        if '://' in nextpage:
            from_url = urlparse(nextpage)
            nextpage = from_url[2]
        redirect(nextpage)
示例#5
0
class StatusController(BaseController):
    "status controller"

    def __before__(self):
        "set context"
        BaseController.__before__(self)
        if self.identity:
            c.user = self.identity['user']
        else:
            c.user = None
        c.selectedtab = 'status'

    def _get_server(self, serverid):
        "utility"
        try:
            server = Session.query(Server).get(serverid)
        except NoResultFound:
            server = None
        return server

    @ActionProtector(OnlyAdminUsers())
    def index(self):
        "System status"
        c.servers = Session.query(Server)\
                    .filter(Server.hostname != 'default')\
                    .filter(Server.enabled == true()).all()
        jsondata = [
            dict(tooltip=LABELS[attr],
                 y=getattr(c.baruwa_totals, attr),
                 stroke='black',
                 color=PIE_COLORS[attr])
            for attr in ['clean', 'highspam', 'lowspam', 'virii', 'infected']
            if getattr(c.baruwa_totals, attr)
        ]
        c.chart_data = json.dumps(jsondata)
        return self.render('/status/index.html')

    def graph(self, nodeid=None):
        "Generate graphical system status"
        totals = DailyTotals(Session, c.user)
        if nodeid:
            server = self._get_server(nodeid)
            if not server:
                abort(404)
            baruwa_totals = totals.get(hostname=server.hostname)
        else:
            baruwa_totals = totals.get()

        if not baruwa_totals.total:
            abort(404)

        piedata = []
        labels = []
        for attr in ['clean', 'highspam', 'lowspam', 'virii', 'infected']:
            value = getattr(baruwa_totals, attr) or 0
            piedata.append(value)
            if baruwa_totals.total > 0:
                labels.append(
                    ("%.1f%% %s" %
                     ((1.0 * value / baruwa_totals.total) * 100, attr)))
            else:
                labels.append('0%')
        pie = PieChart(350, 264)
        pie.chart.labels = labels
        pie.chart.data = piedata
        pie.chart.width = 180
        pie.chart.height = 180
        pie.chart.x = 90
        pie.chart.y = 30
        pie.chart.slices.strokeWidth = 1
        pie.chart.slices.strokeColor = colors.black
        pie.chart.slices[0].fillColor = PIE_CHART_COLORS[5]
        pie.chart.slices[1].fillColor = PIE_CHART_COLORS[0]
        pie.chart.slices[2].fillColor = PIE_CHART_COLORS[1]
        pie.chart.slices[3].fillColor = PIE_CHART_COLORS[9]
        pie.chart.slices[4].fillColor = PIE_CHART_COLORS[3]
        self.imgfile = StringIO()
        renderPM.drawToFile(pie,
                            self.imgfile,
                            'PNG',
                            bg=colors.HexColor('#ffffff'))
        response.content_type = 'image/png'
        response.headers['Cache-Control'] = 'max-age=0'
        return self.imgfile.getvalue()

    def mailq(self,
              serverid=None,
              queue='inbound',
              page=1,
              direction='dsc',
              order_by='timestamp'):
        "Display mailqueue"
        server = None
        if serverid is not None:
            server = self._get_server(serverid)
            if not server:
                abort(404)

        c.queue = queue
        c.server = server
        c.direction = direction
        c.order_by = desc(order_by) if direction == 'dsc' else order_by
        if queue == 'inbound':
            qdirection = 1
        else:
            qdirection = 2

        num_items = session.get('mailq_num_items', 10)

        query = Session.query(MailQueueItem).filter(
            MailQueueItem.direction == qdirection).order_by(order_by)

        if server:
            query = query.filter(MailQueueItem.hostname == server.hostname)

        uquery = UserFilter(Session, c.user, query, model=MailQueueItem)
        query = uquery.filter()

        c.form = MailQueueProcessForm(request.POST, csrf_context=session)
        pages = paginate.Page(query, page=int(page), items_per_page=num_items)
        choices = [(str(item.id), item.id) for item in pages.items]
        session['queue_choices'] = choices
        session.save()

        c.page = paginate.Page(query, page=int(page), items_per_page=num_items)
        return self.render('/status/mailq.html')

    def process_mailq(self):
        "Process mailq"
        sendto = url('mailq-status')
        choices = session.get('queue_choices', [])
        form = MailQueueProcessForm(request.POST, csrf_context=session)
        form.id.choices = choices

        if request.method == 'POST' and form.validate() and choices:
            queueids = form.id.data
            if form.queue_action.data != '0':
                hosts = {}
                direction = None
                queueitems = Session.query(MailQueueItem)\
                            .filter(MailQueueItem.id.in_(queueids))\
                            .all()
                for item in queueitems:
                    if item.hostname not in hosts:
                        hosts[item.hostname] = []
                    if not direction:
                        direction = item.direction
                    hosts[item.hostname].append(item.messageid)
                for hostname in hosts:
                    process_queued_msgs.apply_async(args=[
                        hosts[hostname], form.queue_action.data, direction
                    ],
                                                    routing_key=hostname)
                flash(_('The request has been queued for processing'))
            session['queue_choices'] = []
            session.save()

        referer = request.headers.get('Referer', None)
        if referer and '/mailq' in referer:
            sendto = referer
        redirect(sendto)

    def mailq_detail(self, queueid):
        "View a queued message's details"
        query = Session.query(MailQueueItem)
        uquery = UserFilter(Session, c.user, query, model=MailQueueItem)
        query = uquery.filter()

        try:
            mailqitem = query.filter(MailQueueItem.id == queueid).one()
        except NoResultFound:
            # abort(404)
            flash_alert(_('The requested queued message was not found.'))
            redirect(url('mailq-status'))

        c.mailqitem = mailqitem
        c.form = MailQueueProcessForm(request.POST, csrf_context=session)
        session['queue_choices'] = [
            (queueid, queueid),
        ]
        session.save()
        return self.render('/status/detail.html')

    def mailq_preview(self,
                      queueid,
                      attachid=None,
                      imgid=None,
                      allowimgs=None,
                      richformat=None):
        "preview a queued message"
        query = Session.query(MailQueueItem)
        uquery = UserFilter(Session, c.user, query, model=MailQueueItem)
        query = uquery.filter()

        try:
            mailqitem = query.filter(MailQueueItem.id == queueid).one()
        except NoResultFound:
            flash_alert(_('The requested queued message was not found.'))
            redirect(url('mailq-status'))

        try:
            task = preview_queued_msg.apply_async(
                args=[
                    mailqitem.messageid, mailqitem.direction, attachid, imgid
                ],
                routing_key=mailqitem.hostname)
            task.wait(30)
            if task.result:
                if imgid:
                    response.content_type = task.result['content_type']
                    if task.result and 'img' in task.result:
                        info = QUEUEDOWNLOAD_MSG % dict(m=mailqitem.messageid,
                                                        a=task.result['name'])
                        audit_log(c.user.username, 1, unicode(info),
                                  request.host, request.remote_addr,
                                  arrow.utcnow().datetime)
                        return base64.decodestring(task.result['img'])
                    abort(404)
                if attachid:
                    info = QUEUEDOWNLOAD_MSG % dict(m=mailqitem.messageid,
                                                    a=task.result['name'])
                    audit_log(c.user.username, 1, unicode(info), request.host,
                              request.remote_addr,
                              arrow.utcnow().datetime)
                    response.content_type = task.result['mimetype']
                    dispos = 'attachment; filename="%s"' % task.result['name']
                    response.headers['Content-Disposition'] = str(dispos)
                    content_len = len(task.result['attachment'])
                    response.headers['Content-Length'] = content_len
                    response.headers['Pragma'] = 'public'
                    response.headers['Cache-Control'] = 'max-age=0'
                    return base64.decodestring(task.result['attachment'])
                for part in task.result['parts']:
                    if part['type'] == 'text/html':
                        local_rf = (not task.result['is_multipart']
                                    or richformat)
                        part['content'] = img_fixups(part['content'], queueid,
                                                     allowimgs, local_rf)
                c.message = task.result
                info = QUEUEPREVIEW_MSG % dict(m=mailqitem.messageid)
                audit_log(c.user.username, 1, unicode(info), request.host,
                          request.remote_addr,
                          arrow.utcnow().datetime)
            else:
                raise TimeoutError
        except (TimeoutError, QueueNotFound), error:
            msg = _('The message could not be processed')
            flash_alert(msg)
            msg = _('The message could not be processed: %s') % error
            log.info(msg)
            redirect(url('mailq-status'))
        c.queueid = queueid
        c.messageid = mailqitem.messageid
        c.richformat = richformat
        return self.render('/status/preview.html')