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)\ .filter(DeliveryServer.id==destinationid)\ .options(FromCache('sql_cache_med', cachekey)) if self.invalidate: q.invalidate() server = q.one() except NoResultFound: server = None return server def _get_organizations(self, orgid=None): "Get organizations" query = Session.query(Group) if orgid: query = query.filter(Group.id == orgid) return query def _get_authserver(self, authid): "Get an auth server" try: cachekey = u'authserver-%s' % authid q = Session.query(AuthServer).filter(AuthServer.id==authid)\ .options(FromCache('sql_cache_med', cachekey)) if self.invalidate: q.invalidate() server = q.one() except NoResultFound: server = None return server def _get_alias(self, aliasid): "Get a domain alias" try: cachekey = u'domainalias-%s' % aliasid q = Session.query(DomainAlias).filter(DomainAlias.id==aliasid)\ .options(FromCache('sql_cache_med', cachekey)) if self.invalidate: q.invalidate() alias = q.one() except NoResultFound: alias = None return alias def index(self, page=1, orgid=None, format=None): "Browse domains" num_items = session.get('domains_num_items', 10) c.form = BulkDelDomains(request.POST, csrf_context=session) if request.POST: if c.form.domainid.data and c.form.whatdo.data == 'disable': Session.query(Domain).filter( Domain.id.in_(c.form.domainid.data)).update( {'status': False}, synchronize_session='fetch') Session.commit() if c.form.domainid.data and c.form.whatdo.data == 'enable': Session.query(Domain).filter( Domain.id.in_(c.form.domainid.data)).update( {'status': True}, synchronize_session='fetch') Session.commit() if c.form.domainid.data and c.form.whatdo.data == 'delete': session['bulk_domain_delete'] = c.form.domainid.data session.save() # redirect for confirmation redirect(url('domains-confirm-delete')) domains = Session.query(Domain).options( joinedload(Domain.organizations)) domcount = Session.query(Domain.id) if orgid and c.user.is_superadmin: domains = domains.join(domain_owners).filter( domain_owners.c.organization_id == orgid) domcount = domcount.join(domain_owners).filter( domain_owners.c.organization_id == orgid) if c.user.is_domain_admin: domains = domains.join(domain_owners, (oa, domain_owners.c.organization_id == oa.c.organization_id))\ .filter(oa.c.user_id == c.user.id) domcount = domcount.join(domain_owners, (oa, domain_owners.c.organization_id == oa.c.organization_id))\ .filter(oa.c.user_id == c.user.id) pages = paginate.Page(domains, page=int(page), items_per_page=num_items, item_count=domcount.count()) if format == 'json': response.headers['Content-Type'] = 'application/json' data = convert_dom_to_json(pages, orgid) return data c.orgid = orgid c.page = pages return render('/domains/index.html') def search(self, format=None): "Search for domains" total_found = 0 search_time = 0 num_items = session.get('domains_num_items', 10) q = request.GET.get('q', '') org = request.GET.get('o', None) page = int(request.GET.get('p', 1)) # if q: kwds = {'presliced_list': True} conn = SphinxClient() conn.SetMatchMode(SPH_MATCH_EXTENDED2) if page == 1: conn.SetLimits(0, num_items, 500) else: offset = (page - 1) * num_items conn.SetLimits(offset, num_items, 500) if org: conn.SetFilter('orgs', [int(org)]) if c.user.is_domain_admin: crcs = get_dom_crcs(Session, c.user) conn.SetFilter('domain_name', crcs) q = clean_sphinx_q(q) results = conn.Query(q, 'domains, domains_rt') q = restore_sphinx_q(q) if results and results['matches']: ids = [hit['id'] for hit in results['matches']] domains = Session.query(Domain)\ .options(joinedload('organizations'))\ .filter(Domain.id.in_(ids))\ .all() total_found = results['total_found'] search_time = results['time'] domaincount = total_found else: domains = [] domaincount = 0 c.page = paginate.Page(domains, page=page, items_per_page=num_items, item_count=domaincount, **kwds) c.q = q c.org = org c.total_found = total_found c.search_time = search_time return render('/domains/searchresults.html') @ActionProtector(OnlySuperUsers()) def add(self, orgid=None): "Add a domain" c.form = AddDomainForm(request.POST, csrf_context=session) c.form.organizations.query = self._get_organizations(orgid) if request.POST and c.form.validate(): try: domain = Domain() for field in c.form: if field.name != 'csrf_token': setattr(domain, field.name, field.data) Session.add(domain) Session.commit() update_serial.delay() info = ADDDOMAIN_MSG % dict(d=domain.name) audit_log(c.user.username, 3, info, request.host, request.remote_addr, now()) flash( _('The domain: %(dom)s has been created') % dict(dom=domain.name)) redirect(url(controller='domains')) except IntegrityError: Session.rollback() flash_alert( _('The domain name %(dom)s already exists') % dict(dom=domain.name)) return render('/domains/new.html') @ActionProtector(OwnsDomain()) def detail(self, domainid): "Domain details" domain = self._get_domain(domainid) if not domain: abort(404) c.domain = domain return render('/domains/detail.html') @ActionProtector(OwnsDomain()) def edit(self, domainid): "Edit a domain" domain = self._get_domain(domainid) if not domain: abort(404) c.form = AddDomainForm(request.POST, domain, csrf_context=session) if c.user.is_superadmin: c.form.organizations.query_factory = self._get_organizations else: del c.form.organizations c.id = domainid if request.POST and c.form.validate(): updated = False kw = {'domainid': domain.id} for field in c.form: intfields = [ 'spam_actions', 'highspam_actions', 'delivery_mode', 'report_every' ] if (field.name in intfields and int(field.data) == getattr(domain, field.name)): continue if (field.name != 'csrf_token' and field.data != getattr(domain, field.name)): setattr(domain, field.name, field.data) updated = True if updated: try: Session.add(domain) Session.commit() update_serial.delay() info = UPDATEDOMAIN_MSG % dict(d=domain.name) audit_log(c.user.username, 2, info, request.host, request.remote_addr, now()) flash( _('The domain: %(dom)s has been updated') % dict(dom=domain.name)) kw['uc'] = 1 except IntegrityError: Session.rollback() flash( _('The domain %(dom)s could not be updated') % dict(dom=domain.name)) else: flash_info(_('No changes were made to the domain')) redirect(url('domain-detail', **kw)) return render('/domains/edit.html') @ActionProtector(OwnsDomain()) def delete(self, domainid): "Delete a domain" domain = self._get_domain(domainid) if not domain: abort(404) c.form = AddDomainForm(request.POST, domain, csrf_context=session) del c.form.organizations c.id = domainid if request.POST and c.form.validate(): name = domain.name Session.delete(domain) Session.commit() update_serial.delay() info = DELETEDOMAIN_MSG % dict(d=name) audit_log(c.user.username, 4, info, request.host, request.remote_addr, now()) flash(_('The domain has been deleted')) redirect(url(controller='domains')) else: flash( _('The domain: %(name)s and all associated data will' ' be deleted, This action cannot be reversed.') % dict(name=domain.name)) return render('/domains/delete.html') def confirm_delete(self): "Confirm bulk delete of domains" domainids = session.get('bulk_domain_delete', []) if not domainids: redirect(url(controller='domains', action='index')) num_items = 10 if len(domainids) > num_items and len(domainids) <= 20: num_items = 20 if len(domainids) > num_items and len(domainids) <= 50: num_items = 50 if len(domainids) > num_items and len(domainids) <= 100: num_items = 100 domains = Session.query(Domain).filter(Domain.id.in_(domainids))\ .options(joinedload('organizations')) domcount = Session.query(Domain.id).filter(Domain.id.in_(domainids)) if c.user.is_domain_admin: domains = domains.join(domain_owners, (oa, domain_owners.c.organization_id == oa.c.organization_id))\ .filter(oa.c.user_id == c.user.id) domcount = domcount.join(domain_owners, (oa, domain_owners.c.organization_id == oa.c.organization_id))\ .filter(oa.c.user_id == c.user.id) if request.POST: tasks = [] for domain in domains.all(): info = DELETEDOMAIN_MSG % dict(d=domain.name) tasks.append((c.user.username, 4, info, request.host, request.remote_addr, now())) Session.delete(domain) Session.commit() del session['bulk_domain_delete'] session.save() for task in tasks: audit_log(*task) flash(_('The domains have been deleted')) redirect(url(controller='domains')) else: flash( _('The following domains are about to be deleted,' ' this action is not reversible, Do you wish to' ' continue ?')) try: c.page = paginate.Page(domains, page=1, items_per_page=num_items, item_count=domcount.count()) except DataError: flash_alert(_('An error occured try again')) redirect(url(controller='domains', action='index')) return render('/domains/confirmbulkdel.html') @ActionProtector(OwnsDomain()) def adddestination(self, domainid): "Add a destination server" domain = self._get_domain(domainid) if not domain: abort(404) c.form = AddDeliveryServerForm(request.POST, csrf_context=session) c.id = domainid if request.POST and c.form.validate(): server = DeliveryServer() for field in c.form: if field.name != 'csrf_token': setattr(server, field.name, field.data) try: domain.servers.append(server) Session.add(server) Session.add(domain) Session.commit() info = ADDDELSVR_MSG % dict(d=domain.name, ds=server.address) audit_log(c.user.username, 3, info, request.host, request.remote_addr, now()) flash(_('The destination server has been created')) redirect( url(controller='domains', action='detail', domainid=domain.id)) except IntegrityError: Session.rollback() flash_alert( _('The destination server %(dest)s already exists ') % dict(dest=server.address)) return render('/domains/adddestination.html') @ActionProtector(OwnsDomain()) def editdestination(self, destinationid): "Edit destination server" server = self._get_server(destinationid) if not server: abort(404) c.form = AddDeliveryServerForm(request.POST, server, csrf_context=session) if request.POST and c.form.validate(): updated = False kw = dict(domainid=server.domain_id) for field in c.form: if (field.name != 'csrf_token' and field.data != getattr(server, field.name)): setattr(server, field.name, field.data) updated = True if updated: try: Session.add(server) Session.commit() flash(_('The destination server has been updated')) info = UPDATEDELSVR_MSG % dict(d=server.domains.name, ds=server.address) audit_log(c.user.username, 2, info, request.host, request.remote_addr, now()) self.invalidate = 1 self._get_server(destinationid) redirect(url('domain-detail', **kw)) except IntegrityError: Session.rollback() flash_alert(_('The update failed')) else: flash_info(_('No changes were made to the destination server')) redirect(url('domain-detail', **kw)) c.id = destinationid c.domainid = server.domain_id return render('/domains/editdestination.html') @ActionProtector(OwnsDomain()) def testdestination(self, destinationid): "Test mail destination server" server = self._get_server(destinationid) if not server: abort(404) taskid = request.GET.get('taskid', None) if not taskid: to_addr = 'postmaster@%s' % server.domains.name task = test_smtp_server.apply_async(args=[ server.address, server.port, '<>', to_addr, server.id, 3 ]) taskid = task.task_id session['taskids'].append(taskid) session['testdest-count'] = 1 session.save() redirect(url.current(taskid=taskid)) else: result = AsyncResult(taskid) if result is None or taskid not in session['taskids']: flash(_('The connection test failed try again later')) redirect(url('domain-detail', domainid=server.domain_id)) if result.ready(): if ('smtp' in result.result and 'ping' in result.result and result.result['smtp'] and result.result['ping']): flash( _('The server: %s is up and accepting mail from us' % server.address)) else: if 'ping' in result.result['errors']: errors = result.result['errors']['ping'] else: errors = result.result['errors']['smtp'] flash( _('The server: %s is not accepting mail from us: %s') % (server.address, errors)) redirect(url('domain-detail', domainid=server.domain_id)) else: session['testdest-count'] += 1 session.save() if (session['testdest-count'] >= 10 and result.state in ['PENDING', 'RETRY', 'FAILURE']): result.revoke() del session['testdest-count'] session.save() flash_alert('Failed to initialize backend,' ' try again later') redirect(url('domain-detail', domainid=server.domain_id)) c.server = server c.domainid = server.domain_id c.taskid = taskid c.finished = False return render('/domains/testdestination.html') @ActionProtector(OwnsDomain()) def deletedestination(self, destinationid): "Delete destination server" server = self._get_server(destinationid) if not server: abort(404) c.form = AddDeliveryServerForm(request.POST, server, csrf_context=session) if request.POST and c.form.validate(): name = server.domains.name server_addr = server.address domainid = server.domain_id Session.delete(server) Session.commit() flash(_('The destination server has been deleted')) info = DELETEDELSVR_MSG % dict(d=name, ds=server_addr) audit_log(c.user.username, 4, info, request.host, request.remote_addr, now()) redirect(url('domain-detail', domainid=domainid)) else: flash( _('The destination server: %(s)s will be deleted,' ' This action is not reversible') % dict(s=server.address)) c.id = destinationid c.domainid = server.domain_id return render('/domains/deletedestination.html') @ActionProtector(OwnsDomain()) def add_auth(self, domainid): "Add auth server" domain = self._get_domain(domainid) if not domain: abort(404) c.form = AddAuthForm(request.POST, csrf_context=session) if request.POST and c.form.validate(): server = AuthServer() for field in c.form: if field.data and field.name != 'csrf_token': setattr(server, field.name, field.data) try: domain.authservers.append(server) Session.add(server) Session.add(domain) Session.commit() info = ADDAUTHSVR_MSG % dict(d=domain.name, ds=server.address) audit_log(c.user.username, 3, info, request.host, request.remote_addr, now()) flash(_('The authentication settings have been created')) redirect( url(controller='domains', action='detail', domainid=domain.id)) except IntegrityError: Session.rollback() auth = dict(AUTH_PROTOCOLS)[str(server.protocol)] flash_alert( _('The host %(dest)s already configured for %(auth)s ' 'authentication for this domain') % dict(dest=server.address, auth=auth)) c.domainid = domainid c.domainname = domain.name return render('/domains/addauth.html') @ActionProtector(OwnsDomain()) def edit_auth(self, authid): "Edit auth server" server = self._get_authserver(authid) if not server: abort(404) c.form = AddAuthForm(request.POST, server, csrf_context=session) #del c.form.protocol if request.POST and c.form.validate(): updated = False kw = dict(domainid=server.domain_id) for field in c.form: if field.name == 'protocol' or field.name == 'csrf_token': continue if (field.data != getattr(server, field.name) and field.data != ''): setattr(server, field.name, field.data) updated = True if (field.name == 'user_map_template' and field.data != getattr(server, field.name)): setattr(server, field.name, field.data) updated = True if updated: try: Session.add(server) Session.commit() flash(_('The authentication settings have been updated')) self.invalidate = 1 self._get_authserver(authid) info = UPDATEAUTHSVR_MSG % dict(d=server.domains.name, ds=server.address) audit_log(c.user.username, 2, info, request.host, request.remote_addr, now()) redirect(url('domain-detail', **kw)) except IntegrityError: Session.rollback() flash_alert(_('The authentication settings update failed')) else: flash_info( _('No changes were made to the ' 'authentication settings')) redirect(url('domain-detail', **kw)) c.domainid = server.domains.id c.domainname = server.domains.name c.authid = authid return render('/domains/editauth.html') @ActionProtector(OwnsDomain()) def delete_auth(self, authid): "Delete auth server" server = self._get_authserver(authid) if not server: abort(404) c.form = AddAuthForm(request.POST, server, csrf_context=session) if request.POST and c.form.validate(): name = server.domains.name server_addr = server.address domainid = server.domains.id Session.delete(server) Session.commit() flash(_('The authentication settings have been deleted')) info = DELETEAUTHSVR_MSG % dict(d=name, ds=server_addr) audit_log(c.user.username, 4, info, request.host, request.remote_addr, now()) redirect(url('domain-detail', domainid=domainid)) else: flash( _('The authentication server: %(s)s will be deleted,' ' This action is not reversible') % dict(s=server.address)) c.domainid = server.domains.id c.domainname = server.domains.name c.authid = authid return render('/domains/deleteauth.html') @ActionProtector(OwnsDomain()) def auth_settings(self, domainid, proto=5): "Authentication settings" domain = self._get_domain(domainid) if not domain: abort(404) try: protocols = { '4': 'radius', '5': 'ldap', '6': 'yubikey', '7': 'oauth' } protocol = protocols[proto] server = Session.query(AuthServer)\ .filter(AuthServer.domain_id == domainid)\ .filter(AuthServer.protocol == proto).one() except KeyError: flash_alert(_('The protocol supplied does not use extra settings')) redirect( url(controller='domains', action='detail', domainid=domain.id)) except NoResultFound: flash_alert( _('Please add an authentication server for the ' '%(proto)s protocol Before attempting to configure ' 'the %(proto)s settings') % dict(proto=proto)) redirect( url(controller='domains', action='detail', domainid=domain.id)) forms = {'4': AddRadiusSettingsForm, '5': AddLDAPSettingsForm} form = forms[proto] if (hasattr(server, protocol + 'settings') and getattr(server, protocol + 'settings')): authobj = getattr(server, protocol + 'settings')[0] c.form = form(request.POST, authobj, csrf_context=session) else: authobj = None c.form = form(request.POST, csrf_context=session) if request.POST and c.form.validate(): updated = False if authobj: settings = getattr(server, protocol + 'settings')[0] else: settingsdict = {'4': RadiusSettings, '5': LDAPSettings} settings = settingsdict[proto]() for field in c.form: if field.name == 'csrf_token': continue if authobj: if getattr(settings, field.name) != field.data: setattr(settings, field.name, field.data) updated = True else: setattr(settings, field.name, field.data) try: if authobj is None: settings.auth_id = server.id if updated or authobj is None: Session.add(settings) Session.commit() if authobj: flash( _('The %(proto)s settings have been updated') % dict(proto=protocol)) else: flash( _('The %(proto)s settings have been created') % dict(proto=protocol)) info = AUTHSETTINGS_MSG % dict(d=domain.name, a=proto) audit_log(c.user.username, 2, info, request.host, request.remote_addr, now()) redirect( url(controller='domains', action='detail', domainid=domain.id)) except IntegrityError: Session.rollback() flash_alert( _('The auth settings already exist, ' 'use update to modify them')) else: if proto == '4' and 'authobj' in locals(): flash( _('The Radius secret is not be displayed in' ' the form, To update type the new secret in ' '"Radius secret" below.')) c.domain = domain c.proto = proto return render('/domains/authsettings.html') @ActionProtector(OwnsDomain()) def rulesets(self, domainid): "Scanner rulesets" domain = self._get_domain(domainid) if not domain: abort(404) c.domainid = domain.id c.domainname = domain.name return render('/domains/rulesets.html') @ActionProtector(OwnsDomain()) def addalias(self, domainid): "Add alias domain" domain = self._get_domain(domainid) if not domain: abort(404) c.form = AddDomainAlias(request.POST, csrf_context=session) c.form.domain.query = Session.query(Domain).filter( Domain.id == domainid) if request.POST and c.form.validate(): alias = DomainAlias() for field in c.form: if field.data and field.name != 'csrf_token': setattr(alias, field.name, field.data) try: domain.aliases.append(alias) Session.add(alias) Session.add(domain) Session.commit() update_serial.delay() info = ADDDOMALIAS_MSG % dict(d=alias.name) audit_log(c.user.username, 3, info, request.host, request.remote_addr, now()) flash(_('The domain alias: %s has been created') % alias.name) redirect( url(controller='domains', action='detail', domainid=domain.id)) except IntegrityError: Session.rollback() flash_alert( _('The domain alias: %s already exists') % alias.name) c.domainid = domain.id c.domainname = domain.name return render('/domains/addalias.html') def editalias(self, aliasid): "Edit alias domain" alias = self._get_alias(aliasid) if not alias: abort(404) c.form = EditDomainAlias(request.POST, alias, csrf_context=session) c.form.domain.query = Session.query(Domain)\ .filter(Domain.id==alias.domain_id) if request.POST and c.form.validate(): updated = False for field in c.form: if (field.name != 'csrf_token' and field.data != getattr(alias, field.name)): setattr(alias, field.name, field.data) updated = True if updated: try: Session.add(alias) Session.commit() update_serial.delay() info = UPDATEDOMALIAS_MSG % dict(d=alias.name) audit_log(c.user.username, 2, info, request.host, request.remote_addr, now()) flash( _('The domain alias: %s has been updated') % alias.name) redirect(url('domain-detail', domainid=alias.domain_id)) except IntegrityError: Session.rollback() flash_alert(_('The update failed')) else: flash_info(_('No changes were made to the domain alias')) redirect(url('domain-detail', domainid=alias.domain_id)) c.aliasid = aliasid c.domainid = alias.domain_id c.domainname = alias.domain.name return render('/domains/editalias.html') def deletealias(self, aliasid): "Delete alias domain" alias = self._get_alias(aliasid) if not alias: abort(404) c.form = AddDomainAlias(request.POST, alias, csrf_context=session) c.form.domain.query = Session.query(Domain)\ .filter(Domain.id==alias.domain_id) if request.POST and c.form.validate(): domainid = alias.domain_id aliasname = alias.name Session.delete(alias) Session.commit() update_serial.delay() info = DELETEDOMALIAS_MSG % dict(d=aliasname) audit_log(c.user.username, 4, info, request.host, request.remote_addr, now()) flash(_('The domain alias: %s has been deleted') % aliasname) redirect(url('domain-detail', domainid=domainid)) c.aliasid = aliasid c.domainid = alias.domain_id c.domainname = alias.domain.name return render('/domains/deletealias.html') def export_domains(self, orgid=None): "export domains" task = exportdomains.apply_async(args=[c.user.id, orgid]) session['taskids'].append(task.task_id) session['dexport-count'] = 1 session.save() flash(_('Domains export is being processed')) redirect(url('domains-export-status', taskid=task.task_id)) 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='domains', 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='domains')) results = dict( f=True if not result.result['global_error'] else False, id=taskid, global_error=result.result['global_error']) else: session['dexport-count'] += 1 if (session['dexport-count'] >= 10 and result.state in ['PENDING', 'RETRY', 'FAILURE']): result.revoke() flash_alert( _('The export could not be processed,' ' try again later')) del session['dexport-count'] session.save() redirect(url(controller='domains')) 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'): info = EXPORTDOM_MSG % dict(d='all') audit_log(c.user.username, 5, info, request.host, request.remote_addr, now()) response.content_type = 'text/csv' response.headers['Cache-Control'] = 'max-age=0' csvdata = result.result['f'] disposition = 'attachment; filename=domains-export-%s.csv' % taskid response.headers['Content-Disposition'] = str(disposition) response.headers['Content-Length'] = len(csvdata) return csvdata return render('/domains/exportstatus.html') def setnum(self, format=None): "Set number of items returned" num = check_num_param(request) if num and num in [10, 20, 50, 100]: session['domains_num_items'] = num session.save() nextpage = request.headers.get('Referer', '/') if '://' in nextpage: from_url = urlparse(nextpage) nextpage = from_url[2] redirect(nextpage)
class SettingsController(BaseController): #@ActionProtector(not_anonymous()) def __before__(self): "set context" BaseController.__before__(self) if self.identity: c.user = self.identity['user'] else: c.user = None c.selectedtab = 'settings' def _get_server(self, serverid): "returns server object" try: server = Session.query(Server).get(serverid) except NoResultFound: server = None return server def _get_setting(self, server, name): "return a configsettings object" try: conf_setting = Session.query(ConfigSettings)\ .filter_by( server_id=server, internal=name ).one() except NoResultFound: conf_setting = None return conf_setting def _get_domsign(self, sigid): "domain signature" try: sign = Session.query(DomSignature).get(sigid) except NoResultFound: sign = None return sign def _get_usrsign(self, sigid): "user signature" try: sign = Session.query(UserSignature).get(sigid) except NoResultFound: sign = None return sign @ActionProtector(OnlySuperUsers()) def index(self, page=1, format=None): "Index" num_items = session.get('settings_num_items', 10) servers = Session.query(Server).filter( Server.hostname != 'default').order_by(desc('id')).all() items = paginate.Page(servers, page=int(page), items_per_page=num_items) if format == 'json': response.headers['Content-Type'] = 'application/json' data = convert_settings_to_json(items) return data c.page = items return render('/settings/index.html') @ActionProtector(OnlySuperUsers()) def new_server(self): "Add scan server" c.form = ServerForm(request.POST, csrf_context=session) if request.POST and c.form.validate(): try: server = Server(hostname=c.form.hostname.data, enabled=c.form.enabled.data) Session.add(server) Session.commit() info = HOSTADD_MSG % dict(n=server.hostname) audit_log(c.user.username, 3, info, request.host, request.remote_addr, now()) flash(_('The scanning server has been added')) redirect(url(controller='settings')) except IntegrityError: Session.rollback() flash_info(_('The server already exists')) return render('/settings/addserver.html') @ActionProtector(OnlySuperUsers()) def edit_server(self, serverid): "Edit scan server" server = self._get_server(serverid) if not server: abort(404) c.form = ServerForm(request.POST, server, csrf_context=session) c.id = server.id if request.POST and c.form.validate(): if (server.hostname != c.form.hostname.data or server.enabled != c.form.enabled.data): try: server.hostname = c.form.hostname.data server.enabled = c.form.enabled.data Session.add(server) Session.commit() update_serial.delay() info = HOSTUPDATE_MSG % dict(n=server.hostname) audit_log(c.user.username, 2, info, request.host, request.remote_addr, now()) flash(_('The scanning server has been updated')) except IntegrityError: Session.rollback() flash(_('Update of server failed')) else: flash_info(_('No changes were made to the server')) redirect(url(controller='settings')) return render('/settings/editserver.html') @ActionProtector(OnlySuperUsers()) def delete_server(self, serverid): "Delete a scan server" server = self._get_server(serverid) if not server: abort(404) c.form = ServerForm(request.POST, server, csrf_context=session) c.id = server.id if request.POST and c.form.validate(): hostname = server.hostname Session.delete(server) Session.commit() update_serial.delay() info = HOSTDELETE_MSG % dict(n=hostname) audit_log(c.user.username, 4, info, request.host, request.remote_addr, now()) flash(_('The scanning server has been deleted')) redirect(url(controller='settings')) return render('/settings/deleteserver.html') @ActionProtector(OnlySuperUsers()) def section(self, serverid=1, sectionid='1'): "Settings section" server = self._get_server(serverid) if not server: abort(404) if not int(sectionid) in CONFIG_SECTIONS: abort(404) c.serverid = serverid c.sectionid = sectionid c.scanner = server c.sections = CONFIG_SECTIONS c.form = settings_forms[sectionid](request.POST, csrf_context=session) if not request.POST: for field in c.form: if (sectionid == '1' and '_' in field.name and not field.name == 'csrf_token'): internal = global_settings_dict[field.name] else: internal = field.name conf = self._get_setting(serverid, internal) if conf: attr = getattr(c.form, field.name) attr.data = conf.value updated = None if request.POST and c.form.validate(): for field in c.form: if field.data and not field.name == 'csrf_token': if sectionid == '1': if '_' in field.name: external = global_settings_dict[field.name] internal = external else: external = CONFIG_RE.sub(u'', unicode(field.label.text)) internal = field.name else: external = CONFIG_RE.sub(u'', unicode(field.label.text)) internal = field.name conf = self._get_setting(serverid, internal) if conf is None: if field.data != field.default: conf = ConfigSettings(internal=internal, external=external, section=sectionid) conf.value = field.data conf.server = server updated = True Session.add(conf) Session.commit() subs = dict(svr=server.hostname, s=external, a=conf.value) info = HOSTSETTING_MSG % subs audit_log(c.user.username, 3, info, request.host, request.remote_addr, now()) else: if conf.value != field.data: conf.value = field.data updated = True Session.add(conf) Session.commit() subs = dict(svr=conf.server.hostname, s=external, a=conf.value) info = HOSTSETTING_MSG % subs audit_log(c.user.username, 2, info, request.host, request.remote_addr, now()) if updated: flash( _('%(settings)s updated') % dict(settings=CONFIG_SECTIONS[int(sectionid)])) update_serial.delay() else: flash_info(_('No configuration changes made')) #redirect(url('settings-scanner', serverid=serverid)) return render('/settings/section.html') @ActionProtector(OwnsDomain()) def domain_settings(self, domainid): "Domain settings landing" domain = self._get_domain(domainid) if not domain: abort(404) c.domain = domain return render('/settings/domain_settings.html') @ActionProtector(OwnsDomain()) def domain_dkim(self, domainid): "Domain DKIM settings" domain = self._get_domain(domainid) if not domain: abort(404) c.domain = domain return render('/settings/domain_dkim.html') @ActionProtector(OwnsDomain()) def domain_dkim_generate(self, domainid): "Domain DKIM generate keys" domain = self._get_domain(domainid) if not domain: abort(404) pub_key, pri_key = make_key_pair() if domain.dkimkeys: dkimkeys = domain.dkimkeys[0] else: dkimkeys = DKIMKeys() dkimkeys.pub_key = pub_key dkimkeys.pri_key = pri_key try: if domain.dkimkeys: domain.dkimkeys[0] = dkimkeys else: domain.dkimkeys.append(dkimkeys) Session.add(dkimkeys) Session.add(domain) Session.commit() info = DKIMGEN_MSG % dict(d=domain.name) audit_log(c.user.username, 3, info, request.host, request.remote_addr, now()) flash(_('DKIM keys successfully generated')) except DatabaseError: flash_alert(_('Generation of DKIM keys failed')) redirect(url('domain-dkim', domainid=domain.id)) @ActionProtector(OwnsDomain()) def domain_dkim_enable(self, domainid): "Enable or disable DKIM signing" domain = self._get_domain(domainid) if not domain or not domain.dkimkeys: abort(404) c.form = DKIMForm(request.POST, domain.dkimkeys[0], csrf_context=session) if request.POST and c.form.validate(): dkimkeys = domain.dkimkeys[0] if dkimkeys.enabled != c.form.enabled.data: dkimkeys.enabled = c.form.enabled.data Session.add(dkimkeys) Session.commit() if c.form.enabled.data: state = _('enabled') save_dkim_key.apply_async( args=[domain.name, dkimkeys.pri_key], queue='msbackend') info = DKIMENABLED_MSG % dict(d=domain.name) else: info = DKIMDISABLED_MSG % dict(d=domain.name) delete_dkim_key.apply_async(args=[domain.name], queue='msbackend') state = _('disabled') audit_log(c.user.username, 2, info, request.host, request.remote_addr, now()) reload_exim.delay() flash( _('DKIM signing for: %s has been %s') % (domain.name, state)) else: flash(_('DKIM signing status: No changes made')) redirect(url('domain-dkim', domainid=domain.id)) c.domain = domain return render('/settings/domain_dkim_enable.html') @ActionProtector(OwnsDomain()) def domain_sigs(self, domainid): "Domain signatures landing" domain = self._get_domain(domainid) if not domain: abort(404) c.domain = domain return render('/settings/domain_sigs.html') @ActionProtector(OwnsDomain()) def domain_rules(self, domainid): "Domain rulesets" domain = self._get_domain(domainid) if not domain: abort(404) c.domain = domain return render('/settings/domain_rules.html') @ActionProtector(OwnsDomain()) def add_domain_sigs(self, domainid): "Add domain signature" domain = self._get_domain(domainid) if not domain: abort(404) c.form = SigForm(request.POST, csrf_context=session) if request.POST and c.form.validate(): try: sig = DomSignature() for field in c.form: if field.name != 'csrf_token': setattr(sig, field.name, field.data) domain.signatures.append(sig) Session.add(sig) Session.add(domain) Session.commit() save_dom_sig.apply_async(args=[sig.id], queue='msbackend') info = ADDDOMSIG_MSG % dict(d=domain.name) audit_log(c.user.username, 3, info, request.host, request.remote_addr, now()) flash(_('The signature has been created')) redirect(url('domain-settings-sigs', domainid=domainid)) except IntegrityError: Session.rollback() flash(_('This signature type already exists')) c.domain = domain return render('/settings/domain_addsig.html') @ActionProtector(OwnsDomain()) def edit_domain_sigs(self, sigid): "Edit domain signatures" sign = self._get_domsign(sigid) if not sign: abort(404) c.form = SigForm(request.POST, sign, csrf_context=session) del c.form['signature_type'] if request.POST and c.form.validate(): try: updated = False for field in c.form: if (field.name != 'csrf_token' and field.data != getattr(sign, field.name)): updated = True setattr(sign, field.name, field.data) if updated: Session.add(sign) Session.commit() save_dom_sig.apply_async(args=[sigid], queue='msbackend') info = UPDATEDOMSIG_MSG % dict(d=sign.domain.name) audit_log(c.user.username, 2, info, request.host, request.remote_addr, now()) flash(_('The signature has been updated')) else: flash(_('No changes made, signature not updated')) redirect(url('domain-settings-sigs', domainid=sign.domain_id)) except IntegrityError: Session.rollback() flash(_('Error occured updating the signature')) c.sign = sign return render('/settings/domain_editsig.html') @ActionProtector(OwnsDomain()) def delete_domain_sigs(self, sigid): "Delete domain signature" sign = self._get_domsign(sigid) if not sign: abort(404) c.form = SigForm(request.POST, sign, csrf_context=session) del c.form['signature_type'] if request.POST and c.form.validate(): domain_id = sign.domain_id domain_name = sign.domain.name files = [] basedir = config.get('ms.signatures.base', '/etc/MailScanner/signatures') if sign.signature_type == 1: domain = self._get_domain(domain_id) if domain: sigfile = os.path.join(basedir, 'domains', domain.name, 'sig.txt') files.append(sigfile) else: if sign.image: imgfile = os.path.join(basedir, 'domains', domain.name, sign.image.name) files.append(imgfile) sigfile = os.path.join(basedir, 'domains', domain.name, 'sig.html') files.append(sigfile) Session.delete(sign) Session.commit() delete_sig.apply_async(args=[files], queue='msbackend') info = DELETEDOMSIG_MSG % dict(d=domain_name) audit_log(c.user.username, 4, info, request.host, request.remote_addr, now()) flash(_('The signature has been deleted')) redirect(url('domain-settings-sigs', domainid=domain_id)) c.sign = sign return render('/settings/domain_deletesig.html') @ActionProtector(CanAccessAccount()) def add_account_sigs(self, userid): "Add account signature" account = self._get_user(userid) if not account: abort(404) c.form = SigForm(request.POST, csrf_context=session) if request.POST and c.form.validate(): try: sig = UserSignature() for field in c.form: if field.name != 'csrf_token': setattr(sig, field.name, field.data) account.signatures.append(sig) Session.add(sig) Session.add(account) Session.commit() save_user_sig.apply_async(args=[sig.id], queue='msbackend') info = ADDACCSIG_MSG % dict(u=account.username) audit_log(c.user.username, 3, info, request.host, request.remote_addr, now()) flash(_('The signature has been created')) redirect(url('account-detail', userid=userid)) except IntegrityError: Session.rollback() flash(_('This signature type already exists')) c.account = account return render('/settings/account_addsig.html') @ActionProtector(CanAccessAccount()) def edit_account_sigs(self, sigid): "Edit account signatures" sign = self._get_usrsign(sigid) if not sign: abort(404) c.form = SigForm(request.POST, sign, csrf_context=session) del c.form['signature_type'] if request.POST and c.form.validate(): try: updated = False for field in c.form: if (field.name != 'csrf_token' and field.data != getattr(sign, field.name)): updated = True setattr(sign, field.name, field.data) if updated: Session.add(sign) Session.commit() save_user_sig.apply_async(args=[sigid], queue='msbackend') info = UPDATEACCSIG_MSG % dict(u=sign.user.username) audit_log(c.user.username, 2, info, request.host, request.remote_addr, now()) flash(_('The signature has been updated')) else: flash(_('No changes made, signature not updated')) redirect(url('account-detail', userid=sign.user_id)) except IntegrityError: Session.rollback() flash(_('Error occured updating the signature')) c.sign = sign return render('/settings/account_editsig.html') @ActionProtector(CanAccessAccount()) def delete_account_sigs(self, sigid): "Delete account signatures" sign = self._get_usrsign(sigid) if not sign: abort(404) c.form = SigForm(request.POST, sign, csrf_context=session) del c.form['signature_type'] if request.POST and c.form.validate(): user_id = sign.user_id user_name = sign.user.username files = [] basedir = config.get('ms.signatures.base', '/etc/MailScanner/signatures') if sign.signature_type == 1: user = self._get_user(user_id) if user: sigfile = os.path.join(basedir, 'users', user.username, 'sig.txt') files.append(sigfile) else: if sign.image: imgfile = os.path.join(basedir, 'users', user.username, sign.image.name) files.append(imgfile) sigfile = os.path.join(basedir, 'users', user.username, 'sig.html') files.append(sigfile) Session.delete(sign) Session.commit() delete_sig.apply_async(args=[files], queue='msbackend') info = DELETEACCSIG_MSG % dict(u=user_name) audit_log(c.user.username, 4, info, request.host, request.remote_addr, now()) flash(_('The signature has been deleted')) redirect(url('account-detail', userid=user_id)) c.sign = sign return render('/settings/account_deletesig.html') @ActionProtector(OnlySuperUsers()) def setnum(self, format=None): "Set number of items returned" num = check_num_param(request) if num and num in [10, 20, 50, 100]: session['settings_num_items'] = num session.save() nextpage = request.headers.get('Referer', '/') if '://' in nextpage: from_url = urlparse(nextpage) nextpage = from_url[2] redirect(nextpage)
from baruwa.model.meta import Session from baruwa.lib.audit import audit_log from baruwa.lib.auth.predicates import OnlySuperUsers from baruwa.tasks.settings import update_serial from baruwa.model.accounts import Group, User, Relay from baruwa.model.domains import Domain from baruwa.forms.organizations import OrgForm, RelayForm, RelayEditForm from baruwa.forms.organizations import DelOrgForm from baruwa.forms.organizations import ImportCSVForm from baruwa.tasks import importdomains from baruwa.lib.audit.msgs.organizations import * log = logging.getLogger(__name__) @ControllerProtector(All(not_anonymous(), OnlySuperUsers())) class OrganizationsController(BaseController): def __before__(self): "set context" BaseController.__before__(self) if self.identity: c.user = self.identity['user'] else: c.user = None c.selectedtab = 'organizations' def _get_org(self, orgid): "Get organization" try: org = Session.query(Group).options( joinedload('domains')).get(orgid)
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)
class DomainsController(BaseController): "Domains controller" 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 qry = Session.query(DeliveryServer)\ .filter(DeliveryServer.id == destinationid)\ .options(FromCache('sql_cache_med', cachekey)) if self.invalidate: qry.invalidate() server = qry.one() except NoResultFound: server = None return server def _get_authserver(self, authid): "Get an auth server" try: cachekey = u'authserver-%s' % authid qry = Session.query(AuthServer).filter(AuthServer.id == authid)\ .options(FromCache('sql_cache_med', cachekey)) if self.invalidate: qry.invalidate() server = qry.one() except NoResultFound: server = None return server def _get_alias(self, aliasid): "Get a domain alias" try: cachekey = u'domainalias-%s' % aliasid qry = Session.query(DomainAlias).filter(DomainAlias.id == aliasid)\ .options(FromCache('sql_cache_med', cachekey)) if self.invalidate: qry.invalidate() alias = qry.one() except NoResultFound: alias = None return alias def index(self, page=1, orgid=None, format=None): "Browse domains" num_items = session.get('domains_num_items', 10) c.form = BulkDelDomains(request.POST, csrf_context=session) if request.method == 'POST': if c.form.domainid.data and c.form.whatdo.data == 'disable': Session.query(Domain).filter( Domain.id.in_(c.form.domainid.data)).update( {'status': False}, synchronize_session='fetch') Session.commit() if c.form.domainid.data and c.form.whatdo.data == 'enable': Session.query(Domain).filter( Domain.id.in_(c.form.domainid.data)).update( {'status': True}, synchronize_session='fetch') Session.commit() if c.form.domainid.data and c.form.whatdo.data == 'delete': session['bulk_domain_delete'] = c.form.domainid.data session.save() # redirect for confirmation redirect(url('domains-confirm-delete')) domains = Session.query(Domain).options( joinedload(Domain.organizations)) domcount = Session.query(Domain.id) if orgid and c.user.is_superadmin: domains = domains.join(domain_owners).filter( domain_owners.c.organization_id == orgid) domcount = domcount.join(domain_owners).filter( domain_owners.c.organization_id == orgid) if c.user.is_domain_admin: domains = domains.join(domain_owners, (oa, domain_owners.c.organization_id == oa.c.organization_id))\ .filter(oa.c.user_id == c.user.id) domcount = domcount.join(domain_owners, (oa, domain_owners.c.organization_id == oa.c.organization_id))\ .filter(oa.c.user_id == c.user.id) pages = paginate.Page(domains, page=int(page), items_per_page=num_items, item_count=domcount.count()) if format == 'json': response.headers['Content-Type'] = 'application/json' data = convert_dom_to_json(pages, orgid) return data c.orgid = orgid c.page = pages return self.render('/domains/index.html') def search(self, format=None): "Search for domains" total_found = 0 search_time = 0 num_items = session.get('domains_num_items', 10) qry = request.GET.get('q', '') org = request.GET.get('o', None) page = int(request.GET.get('p', 1)) # if q: kwds = {'presliced_list': True} conn = SphinxClient() sphinxopts = extract_sphinx_opts(config['sphinx.url']) conn.SetServer(sphinxopts.get('host', '127.0.0.1')) conn.SetMatchMode(SPH_MATCH_EXTENDED2) if page == 1: conn.SetLimits(0, num_items, 500) else: offset = (page - 1) * num_items conn.SetLimits(offset, num_items, 500) if org: conn.SetFilter('orgs', [int(org)]) if c.user.is_domain_admin: crcs = get_dom_crcs(Session, c.user) conn.SetFilter('domain_name', crcs) qry = clean_sphinx_q(qry) try: results = conn.Query(qry, 'domains, domains_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']] domains = Session.query(Domain)\ .options(joinedload('organizations'))\ .filter(Domain.id.in_(ids))\ .all() total_found = results['total_found'] search_time = results['time'] domaincount = total_found else: domains = [] domaincount = 0 c.page = paginate.Page(domains, page=page, items_per_page=num_items, item_count=domaincount, **kwds) c.q = qry c.org = org c.total_found = total_found c.search_time = search_time return self.render('/domains/searchresults.html') @ActionProtector(OwnsDomain()) def detail(self, domainid): "Domain details" domain = self._get_domain(domainid) if not domain: abort(404) c.domain = domain return self.render('/domains/detail.html') @ActionProtector(OnlySuperUsers()) def add(self, orgid=None): "Add a domain" c.form = AddDomainForm(request.POST, csrf_context=session) c.form.organizations.query = get_organizations(orgid) if request.method == 'POST' and c.form.validate(): try: domain = create_domain(c.form, c.user, request.host, request.remote_addr) try: from baruwa.tasks.invite import create_mx_records create_mx_records.apply_async(args=[domain.name]) except ImportError: pass flash( _('The domain: %(dom)s has been created') % dict(dom=domain.name)) redirect(url(controller='domains')) except IntegrityError: Session.rollback() msg = _('The domain name %(dom)s already exists') % \ dict(dom=domain.name) flash_alert(msg) log.info(msg) return self.render('/domains/new.html') @ActionProtector(OwnsDomain()) def edit(self, domainid): "Edit a domain" domain = self._get_domain(domainid) if not domain: abort(404) c.id = domainid c.form = domain_update_form(c.user, request.POST, domain, get_organizations, session) if request.method == 'POST' and c.form.validate(): kwd = dict(domainid=domain.id) if domain_update_if_changed(c.form, domain): try: update_domain(domain, c.user, request.host, request.remote_addr) flash( _('The domain: %(dom)s has been updated') % dict(dom=domain.name)) kwd['uc'] = 1 except IntegrityError: Session.rollback() msg = _('The domain %(dom)s could not be updated') % \ dict(dom=domain.name) flash(msg) log.info(msg) else: msg = _('No changes were made to the domain') flash_info(msg) log.info(msg) redirect(url('domain-detail', **kwd)) return self.render('/domains/edit.html') @ActionProtector(OwnsDomain()) def delete(self, domainid): "Delete a domain" domain = self._get_domain(domainid) if not domain: abort(404) c.id = domainid c.form = EditDomainForm(request.POST, domain, csrf_context=session) del c.form.organizations if request.method == 'POST': if c.form.validate(): delete_domain(domain, c.user, request.host, request.remote_addr) flash(_('The domain has been deleted')) redirect(url(controller='domains')) else: flash( _('The domain: %(name)s and all associated data will' ' be deleted, This action cannot be reversed.') % dict(name=domain.name)) return self.render('/domains/delete.html') def confirm_delete(self): "Confirm bulk delete of domains" domainids = session.get('bulk_domain_delete', []) if not domainids: redirect(url(controller='domains', action='index')) num_items = 10 if len(domainids) > num_items and len(domainids) <= 20: num_items = 20 if len(domainids) > num_items and len(domainids) <= 50: num_items = 50 if len(domainids) > num_items and len(domainids) <= 100: num_items = 100 domains = Session.query(Domain).filter(Domain.id.in_(domainids))\ .options(joinedload('organizations')) domcount = Session.query(Domain.id).filter(Domain.id.in_(domainids)) if c.user.is_domain_admin: domains = domains.join(domain_owners, (oa, domain_owners.c.organization_id == oa.c.organization_id))\ .filter(oa.c.user_id == c.user.id) domcount = domcount.join(domain_owners, (oa, domain_owners.c.organization_id == oa.c.organization_id))\ .filter(oa.c.user_id == c.user.id) if request.method == 'POST': tasks = [] for domain in domains.all(): info = auditmsgs.DELETEDOMAIN_MSG % dict(d=domain.name) tasks.append((c.user.username, 4, unicode(info), request.host, request.remote_addr, arrow.utcnow().datetime)) Session.delete(domain) Session.commit() del session['bulk_domain_delete'] update_domain_backend(None, True) session.save() for task in tasks: audit_log(*task) flash(_('The domains have been deleted')) redirect(url(controller='domains')) else: flash( _('The following domains are about to be deleted,' ' this action is not reversible, Do you wish to' ' continue ?')) try: c.page = paginate.Page(domains, page=1, items_per_page=num_items, item_count=domcount.count()) except DataError, error: msg = _('An error occured try again') flash_alert(msg) msg = _('An error occured try again: %s' % error) log.info(msg) redirect(url(controller='domains', action='index')) return self.render('/domains/confirmbulkdel.html')
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') @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,