feedback needed: modules which match logs but don't remove the entries from the parsed list
by Seth Vidal
A log module I've been noodling with would need to match every line of
every log. Take the line, extract the hostname and collect a list of
all the hosts that have logged something. The goal is to compare that to
a known hosts list and report any host which has reported no logs in the
last time segment.
the problem, of course, is if you match a line it gets removed and can't
be used by other modules or unparsed lines. So that's obviously not
gonna work.
Looking at the code it seems like modules should be able to hand back
None as a result which supposedly is to say "this looked like a match
but it wasn't, we don't need this, give it to unparsed".
However, testing that code seems to bear out that it, in fact, doesn't
get handed over to unparsed.
So my options are:
1. fix that so None == no match and hand them back
2. make the 'report a list of hosts which logged nothing in the last
time segment' a core feature that isn't in a module at all.
Not sure how I feel about 2 b/c it feels like something you could safely
do in a module.
So - thoughts?
-sv
12 years, 8 months
[epylog] remove debug output left in by mistake
by Seth Vidal
commit 7c3257ddd634cc72fb0f45218f9cac191be169d9
Author: Seth Vidal <skvidal(a)fedoraproject.org>
Date: Wed Sep 7 11:57:49 2011 -0400
remove debug output left in by mistake
modules/logins_mod.py | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
---
diff --git a/modules/logins_mod.py b/modules/logins_mod.py
index 7e0b504..f95db3e 100644
--- a/modules/logins_mod.py
+++ b/modules/logins_mod.py
@@ -836,7 +836,6 @@ class logins_mod(InternalModule):
report = self.report_wrap % report
if self.oldest_to_keep:
- print self.oldest_to_keep
q = """delete from logins where stamp < ?"""
p = (self.oldest_to_keep,)
executeSQL(self.db_cx.cursor(), q, p)
12 years, 8 months
Ubuntu 10.04 LTS - weed_local.cf, sudo parsing, deprecation question
by Jon Peck
Hi,
I've become quite enamored with epylog as a replacement for logwatch; it's
been meeting my needs nicely.
I've been working on tweaking my epylog 1.0.3 configuration for a LAMP
server, and wanted to share some of my configurations.
===
/etc/epylog/weed_local.cf
## Ubuntu 10.04 LTS
# CRON invocation
CRON\[\d+\]: \(root\) CMD \( cd / && run-parts --report
/etc/cron.(daily|hourly|weekly|monthly)\)
# CRON session open / close for root when invoked by uid=0
CRON\[\d+\]: pam_unix\(cron:session\): session (open|clos)ed for user root(
by \(uid=0\))?
# Automatic PHP Session garbage collection
CRON\[\d+\]: \(root\) CMD \( \[ -x /usr/lib/php5/maxlifetime \] && \[ -d
/var/lib/php5 \] && find /var/lib/php5/ -type f -cmin
+$\(/usr/lib/php5/maxlifetime\) -print0 | xargs -n 200 -r -0 rm\)
# cracklib
cracklib: no dictionary update necessary\.
# ntdp peer validation
ntpd\[\d+\]: peer \d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} now (valid|invalid)
===
Back in 2007, Jeremy Kindy at WFU posted a few modules, two of which work
with Ubuntu; sudo usage logs and user/group reporting (imperfect, I may
update it). Note: you'll get some noise about their self-signed
certificate.
https://lists.dulug.duke.edu/pipermail/epylog/2007-August/000274.html
sudo mkdir /usr/local/src/epylog_kindyjd_modules
cd /usr/local/src/epylog_kindyjd_modules
sudo wget --no-check-certificate
http://lists.dulug.duke.edu/pipermail/epylog/attachments/20070821/004982a...
sudo tar zxvf epylog_modules.tar.gz
# sudo
sudo cp sudo_mod.py /usr/share/epylog/modules
sudo cp sudo.conf /etc/epylog/modules.d
# users
sudo cp users_mod.py /usr/share/epylog/modules
sudo cp users.conf /etc/epylog/modules.d
Edit /etc/epylog/modules.d/sudo.conf and change files to:
files = /var/log/auth.log[.#]
Edit /etc/epylog/modules.d/users.conf and change files to:
files = /var/log/auth.log[.#]
===
I've been getting a deprecation warning:
/usr/lib/pymodules/python2.6/epylog/publishers.py:268: DeprecationWarning:
the MimeWriter module is deprecated; use the email package instead
import StringIO, MimeWriter
http://osdir.com/ml/debian-bugs-dist/2010-11/msg02837.html referred to a
patch available at
https://fedorahosted.org/epylog/changeset/394/epylog#file8 , but that link
is now invalid. To the best of my knowledge, it's a known issue and has
been fixed in dev, but not in any releases. Is there any way I can get that
patch?
===
Thanks for all your hard work on this, and to Seth Vidal for picking up
development again!
Best regards,
Jon Peck
Owner, FluxSauce.com
12 years, 8 months
[epylog] not quoted, dumbass
by Seth Vidal
commit ccafdd8ecc3af94abdbaa6ec3017f997923bac2d
Author: Seth Vidal <skvidal(a)fedoraproject.org>
Date: Tue Sep 6 16:20:53 2011 -0400
not quoted, dumbass
etc/modules.d/logins.conf.in | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
---
diff --git a/etc/modules.d/logins.conf.in b/etc/modules.d/logins.conf.in
index aab22ed..d1a2f85 100644
--- a/etc/modules.d/logins.conf.in
+++ b/etc/modules.d/logins.conf.in
@@ -42,7 +42,7 @@ systems_collapse = 10
# comma/space separated list of users to ignore - unknown is the internal "no user given"
ignore_users = unknown
# path to where we keep the logins db
-loginsdb_path = '/var/lib/epylog/logins_db.sqlite'
+loginsdb_path = /var/lib/epylog/logins_db.sqlite
# clean up entries in the db which are more than this many days old
remove_older_than = 14
# time fuzz - default time (in minutes) which is valid fuzzy match for a login to not be listed
12 years, 8 months
logins with sqlite dbs and new mechanism for reporting
by Seth Vidal
Hi,
The logins module was not terribly useful for me b/c I just ended up
seeing the same set of logins over and over and over again b/c my users
are those who do the same thing, commonly. It ended up just being noise
in the log report and I couldn't sensibly parse it.
With that in mind I modified it to keep a sqlite db of all system
logins. It uses this db to determine what is common login.
so if user skvidal logs in once at 2pm using ssh(pk) on the host
login.mydomain.org. I should get a notice about it.
However, the next time they do the same thing, to the same place, at the
same time of day(plus or minus a time_fuzz) amount using the same
service don't add it to the report. Just skip it.
But if they login at 4pm, let me know that they logged in.
This helps me by just reporting the outliers the oddball logins. So if a
user has never logged into host X before, I'll see that in the log
report.
I checked it into git upstream:
http://git.fedorahosted.org/git/?p=epylog.git;a=commitdiff;h=84101b41b0eb...
let me know if it does or does not work for you.
the config file for the module is commented pretty well.
suggestions welcome.
-sv
12 years, 8 months
[epylog] make the logins module use a sqlite db and some fuzzy matching to determine if the user login needs
by Seth Vidal
commit 84101b41b0eb76939ae00fd9b18e110604a051e7
Author: Seth Vidal <skvidal(a)fedoraproject.org>
Date: Tue Sep 6 16:08:20 2011 -0400
make the logins module use a sqlite db and some fuzzy matching to determine if the user
login needs to be reported. This will end up showing only new or 'odd' logins by your users.
etc/modules.d/logins.conf.in | 11 ++
modules/logins_mod.py | 235 ++++++++++++++++++++++++++++++++++--------
2 files changed, 201 insertions(+), 45 deletions(-)
---
diff --git a/etc/modules.d/logins.conf.in b/etc/modules.d/logins.conf.in
index 17708d6..aab22ed 100644
--- a/etc/modules.d/logins.conf.in
+++ b/etc/modules.d/logins.conf.in
@@ -20,6 +20,7 @@ enable_dovecot = 0
enable_courier = 0
enable_imp = 0
enable_proftpd = 0
+
##
# This is a fun setting. You can list domains that are "safe" here.
# E.g. if your org's domain is example.com and you generally don't
@@ -37,3 +38,13 @@ safe_domains = .*
# If you have too many systems, wide-scale probing may turn ugly. This
# will collapse the reports.
systems_collapse = 10
+
+# comma/space separated list of users to ignore - unknown is the internal "no user given"
+ignore_users = unknown
+# path to where we keep the logins db
+loginsdb_path = '/var/lib/epylog/logins_db.sqlite'
+# clean up entries in the db which are more than this many days old
+remove_older_than = 14
+# time fuzz - default time (in minutes) which is valid fuzzy match for a login to not be listed
+time_fuzz = 60
+
diff --git a/modules/logins_mod.py b/modules/logins_mod.py
index 983e203..7e0b504 100644
--- a/modules/logins_mod.py
+++ b/modules/logins_mod.py
@@ -1,3 +1,5 @@
+
+
#!/usr/bin/python -tt
"""
Description will eventually go here.
@@ -28,10 +30,26 @@ Description will eventually go here.
import sys
import re
-
+import time
+import os
+import sqlite3 as sqlite
sys.path.insert(0, '../py/')
from epylog import Result, InternalModule
+
+def executeSQL(cursor, query, params=None):
+ """
+ Execute a python 2.5 (sqlite3) style query.
+
+ @param cursor: A sqlite cursor
+ @param query: The query to execute
+ @param params: An optional list of parameters to the query
+ """
+ if params is None:
+ return cursor.execute(query)
+
+ return cursor.execute(query, params)
+
class logins_mod(InternalModule):
def __init__(self, opts, logger):
InternalModule.__init__(self)
@@ -46,7 +64,19 @@ class logins_mod(InternalModule):
self.root_failure = 12
self.pam_ignore = []
self.xinetd_ignore = []
-
+ self.logins_db = opts.get('loginsdb_path', '/var/lib/epylog/logins_db.sqlite') # where to keep the loginsdb
+ self.time_fuzz = int(opts.get('time_fuzz', 60)) # how much to fuzz the time in minutes (default 60m)
+ remove_older_than = int(opts.get('remove_older_than', 14)) # time in days to start remove from the db
+ self.oldest_to_keep = time.time() - (remove_older_than*86400)
+ if remove_older_than == '0': # if it is zero then don't delete any, ever - hah your funeral
+ self.oldest_to_keep = None
+
+ ig_users = opts.get('ignore_users', '')
+ ig_users.replace(',',' ')
+ self.ignore_users = ig_users.split(' ')
+
+ self.db_cx = None
+
##
# PAM reports
#
@@ -250,7 +280,7 @@ class logins_mod(InternalModule):
mo = self.pam_failure_more_re.search(message)
if mo: mult += int(mo.group(1))
restuple = self._mk_restuple(action, system, service, user,
- byuser, rhost)
+ byuser, rhost, linemap['stamp'])
return {restuple: mult}
def pam_open(self, linemap):
@@ -269,7 +299,7 @@ class logins_mod(InternalModule):
return None
user, byuser, byuid = mo.groups()
if byuser == '': byuser = self.getuname(int(byuid))
- restuple = self._mk_restuple(action, system, service, user, byuser, '')
+ restuple = self._mk_restuple(action, system, service, user, byuser, '', linemap['stamp'])
return {restuple: mult}
def pam_baduser(self, linemap):
@@ -281,7 +311,7 @@ class logins_mod(InternalModule):
return None
user = mo.group(1)
service = self._get_pam_service(message)
- restuple = self._mk_restuple(action, system, service, user, '', '')
+ restuple = self._mk_restuple(action, system, service, user, '', '', linemap['stamp'])
return {restuple: mult}
def pam_chelper_failure(self, linemap):
@@ -293,7 +323,7 @@ class logins_mod(InternalModule):
return None
user = mo.group(1)
service = self._get_pam_service(message)
- restuple = self._mk_restuple(action, system, service, user, '', '')
+ restuple = self._mk_restuple(action, system, service, user, '', '', linemap['stamp'])
return {restuple: mult}
def pam_krb5_open(self, linemap):
@@ -311,7 +341,7 @@ class logins_mod(InternalModule):
#
result = self.general_ignore(linemap)
return result
- restuple = self._mk_restuple(action, system, service, user, '', '')
+ restuple = self._mk_restuple(action, system, service, user, '', '', linemap['stamp'])
return {restuple: mult}
def pam_krb5_failure(self, linemap):
@@ -333,7 +363,7 @@ class logins_mod(InternalModule):
#
result = self.general_ignore(linemap)
return result
- restuple = self._mk_restuple(action, system, service, user, '', '')
+ restuple = self._mk_restuple(action, system, service, user, '', '', linemap['stamp'])
return {restuple: mult}
def xinetd_start(self, linemap):
@@ -350,7 +380,7 @@ class logins_mod(InternalModule):
#
result = self.general_ignore(linemap)
return result
- restuple = self._mk_restuple(action, system, service, '', '', '')
+ restuple = self._mk_restuple(action, system, service, '', '', '', linemap['stamp'])
return {restuple: mult}
def sshd_open(self, linemap):
@@ -369,7 +399,7 @@ class logins_mod(InternalModule):
if not service: service = 'ssh1'
service = '%s(%s)' % (service, method)
restuple = self._mk_restuple(action, system, service, user,
- ruser, rhost)
+ ruser, rhost, linemap['stamp'])
return {restuple: mult}
def sshd_failure(self, linemap):
@@ -384,7 +414,7 @@ class logins_mod(InternalModule):
rhost = self.gethost(rhost)
if not service: service = 'ssh1'
service = '%s(%s)' % (service, method)
- restuple = self._mk_restuple(action, system, service, user, '', rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
return {restuple: mult}
def uw_imap_failure(self, linemap):
@@ -398,7 +428,7 @@ class logins_mod(InternalModule):
return None
user, rhost = mo.groups()
rhost = self.gethost(rhost)
- restuple = self._mk_restuple(action, system, service, user, '', rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
return {restuple: mult}
def uw_imap_open(self, linemap):
@@ -412,7 +442,7 @@ class logins_mod(InternalModule):
return None
user, rhost = mo.groups()
rhost = self.gethost(rhost)
- restuple = self._mk_restuple(action, system, service, user, '', rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
return {restuple: mult}
def dovecot_open(self, linemap):
@@ -425,7 +455,7 @@ class logins_mod(InternalModule):
return None
user, rhost = mo.groups()
rhost = self.gethost(rhost)
- restuple = self._mk_restuple(action, system, service, user, '', rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
return {restuple: mult}
def dovecot_failure(self, linemap):
@@ -439,7 +469,7 @@ class logins_mod(InternalModule):
rhost = mo.group(1)
rhost = self.gethost(rhost)
user = 'unknown'
- restuple = self._mk_restuple(action, system, service, user, '', rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
return {restuple: mult}
def courier_open(self, linemap):
@@ -452,7 +482,7 @@ class logins_mod(InternalModule):
service, user, rhost = mo.groups()
service = '%s(cr)' % service
rhost = self.gethost(rhost)
- restuple = self._mk_restuple(action, system, service, user, '', rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
return {restuple: mult}
def courier_failure(self, linemap):
@@ -466,7 +496,7 @@ class logins_mod(InternalModule):
service = '%s(cr)' % service
rhost = self.gethost(rhost)
user = 'unknown'
- restuple = self._mk_restuple(action, system, service, user, '', rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
return {restuple: mult}
def proftpd_open(self, linemap):
@@ -479,7 +509,7 @@ class logins_mod(InternalModule):
service = 'ftp(pro)'
rhost, user = mo.groups()
rhost = self.gethost(rhost)
- restuple = self._mk_restuple(action, system, service, user, '', rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
return {restuple: mult}
def proftpd_failure(self, linemap):
@@ -492,7 +522,7 @@ class logins_mod(InternalModule):
service = 'ftp(pro)'
rhost, user = mo.groups()
rhost = self.gethost(rhost)
- restuple = self._mk_restuple(action, system, service, user, '', rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
return {restuple: mult}
def imp2_failure(self, linemap):
@@ -505,7 +535,7 @@ class logins_mod(InternalModule):
rhost, system, user = mo.groups()
rhost = self.gethost(rhost)
service = 'IMP2'
- restuple = self._mk_restuple(action, system, service, user, '', rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
return {restuple: mult}
def imp2_open(self, linemap):
@@ -518,7 +548,7 @@ class logins_mod(InternalModule):
rhost, system, user = mo.groups()
rhost = self.gethost(rhost)
service = 'IMP2'
- restuple = self._mk_restuple(action, system, service, user, '', rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
return {restuple: mult}
def imp3_failure(self, linemap):
@@ -531,7 +561,7 @@ class logins_mod(InternalModule):
rhost, system, user = mo.groups()
rhost = self.gethost(rhost)
service = 'IMP3'
- restuple = self._mk_restuple(action, system, service, user, '', rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
return {restuple: mult}
def imp3_open(self, linemap):
@@ -544,7 +574,7 @@ class logins_mod(InternalModule):
user, rhost, system = mo.groups()
rhost = self.gethost(rhost)
service = 'IMP3'
- restuple = self._mk_restuple(action, system, service, user, '', rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
return {restuple: mult}
def cyrus_failure(self,linemap):
@@ -557,7 +587,7 @@ class logins_mod(InternalModule):
return None
rhost, user = mo.groups()
rhost = self.gethost(rhost)
- restuple = self._mk_restuple(action, system, service, user, '', rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
return {restuple: mult}
def cyrus_open(self,linemap):
@@ -570,7 +600,7 @@ class logins_mod(InternalModule):
return None
rhost, user = mo.groups()
rhost = self.gethost(rhost)
- restuple = self._mk_restuple(action, system, service, user, '', rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
return {restuple: mult}
def qpopper_failure(self, linemap):
@@ -583,9 +613,9 @@ class logins_mod(InternalModule):
user, rhost = mo.groups()
rhost = self.gethost(rhost)
service = 'qpopper'
- restuple = self._mk_restuple(action, system, service, user, '', rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
return {restuple: mult}
-
+
def qpopper_open(self, linemap):
action = self.open
system, message, mult = self.get_smm(linemap)
@@ -596,17 +626,17 @@ class logins_mod(InternalModule):
user, rhost = mo.groups()
rhost = self.gethost(rhost)
service = 'qpopper'
- restuple = self._mk_restuple(action, system, service, user, '', rhost)
+ restuple = self._mk_restuple(action, system, service, user, '', rhost, linemap['stamp'])
return {restuple: mult}
##
# HELPER METHODS
#
- def _mk_restuple(self, action, system, service, user, byuser, rhost):
+ def _mk_restuple(self, action, system, service, user, byuser, rhost, stamp):
if user == '': user = 'unknown'
if user == 'root' or user == 'ROOT':
action += 10
remote = self._mk_userat(byuser, rhost)
- restuple = (action, system, service, remote)
+ restuple = (action, system, service, remote, stamp)
else:
if rhost:
match = 0
@@ -617,7 +647,7 @@ class logins_mod(InternalModule):
if not match:
tmp = {'system': system, 'rhost': rhost}
system = self.untrusted_host % tmp
- restuple = (action, user, service, system)
+ restuple = (action, user, service, system, stamp)
return restuple
def _mk_dots(self, str, lim):
@@ -651,6 +681,69 @@ class logins_mod(InternalModule):
if mo: service = mo.group(1)
return service
+
+
+ def _check_for_login(self, username, service, hostname, tid):
+ # check if we have a login which matches in the db,
+
+ if not os.path.exists(self.logins_db):
+ return False
+
+ p = (username, service, hostname, self.last_entry)
+ q = "select * from logins where username=? and service=? and host=? and pkey <= ?"
+ if not self.db_cx:
+ self._db_cx()
+
+ cur = self.db_cx.cursor()
+ ob = executeSQL(cur, q, p)
+ for i in ob:
+ # if we get any matches if they match within the fuzzed time then
+ # don't show it
+ if abs(tid - i[7]) <= self.time_fuzz:
+ return True
+ return False
+
+ def _add_login(self, username, action, hostname, timestamp, service, u_from=None):
+ if not self.db_cx:
+ self._db_cx()
+
+ cur = self.db_cx.cursor()
+ t_st = time.localtime(int(timestamp))
+ time_in_day = int(t_st[3]*60) + int(t_st[4])
+ q = "insert into logins values (NULL, ?, ?, ?, ?, ?, ?, ?)"
+ p = (action, username, hostname, u_from, service, timestamp, time_in_day)
+ cur.execute(q, p)
+
+
+ def _db_cx(self):
+ if not os.path.exists(self.logins_db):
+ self.db_cx = self._setup_logins_db()
+ else:
+ self.db_cx = sqlite.Connection(self.logins_db)
+
+ q = "select max(pkey) from logins"
+ last_e = executeSQL(self.db_cx.cursor(), q)
+ val = last_e.fetchone()[0]
+ if not val:
+ self.last_entry = 0
+ else:
+ self.last_entry = val
+
+ def _setup_logins_db(self):
+ schema = [
+ """PRAGMA synchronous="OFF";""",
+ """CREATE TABLE logins ( pkey INTEGER PRIMARY KEY, action INTEGER,
+ username TEXT, host TEXT, u_from TEXT, service TEXT,
+ stamp INTEGER, time_in_day INTEGER);""",
+ ]
+
+ cx = sqlite.Connection(self.logins_db)
+ cursor = cx.cursor()
+ for cmd in schema:
+ executeSQL(cursor, cmd)
+
+ return cx
+
##
# FINALIZE!!
#
@@ -661,27 +754,71 @@ class logins_mod(InternalModule):
#
report = ''
rep = {}
+
+ # FIXME
+ # go through each item in the rs
+ # feed them into the db
+ # pull back from the db all the info you need for the report
+ # simplifies a lot of this code
+
+ # chuck it all into the db
+ for (rt,count) in rs.items():
+ if rt[0] in (self.root_failure, self.root_open):
+ (action, host, service, remote, stamp) = rt
+ user = 'root'
+ elif rt[0] in (self.open, self.failure):
+ (action, user, service, host, stamp) = rt
+ remote = 'NULL'
+ else:
+ continue
+ if user in self.ignore_users:
+ continue
+ for num in range(0, count):
+ self._add_login(user, action, host, stamp, service, remote)
+ self.db_cx.commit()
+
+ #return "lalallala"
+
for action in [self.root_failure, self.root_open,
- self.failure, self.open]:
+ self.failure, self.open]:
rep[action] = ''
+ per_user = {}
flipper = ''
- for key in rs.get_distinct((action,)):
+ q = """select distinct username, service, host from logins where action = ? and pkey > ?"""
+ p = (action, self.last_entry)
+ act_tuple = [(i[0],i[1], i[2]) for i in executeSQL(self.db_cx.cursor(), q, p)]
+
+ for entry in act_tuple:
+ username = entry[0]
+ if username not in per_user:
+ per_user[username] = {}
+ service = entry[1]
+ if service not in per_user[username]:
+ per_user[username][service] = []
+ hn = entry[2]
+ q = """select time_in_day from logins where username = ? and host = ? and service = ? and action = ? and pkey > ?"""
+ p = (username, hn, service, action, self.last_entry)
+
+ this_logins_times = [row[0] for row in executeSQL(self.db_cx.cursor(), q, p)]
+ count = 0
+ for t in this_logins_times:
+ if not self._check_for_login(username, service, hn, t):
+ # DEBUG print 'new login %s %s %s %s' % (username, service, hn, t)
+ count += 1
+
+ if count:
+ per_user[username][service].append('%s(%d)' % (hn, count))
+
+ blank = 0
+ for username in sorted(per_user):
if flipper: flipper = ''
else: flipper = self.flip
- service_rep = []
- for service in rs.get_distinct((action, key)):
- mymap = rs.get_submap((action, key, service))
- key2s = []
- for key2 in mymap.keys():
- field = key2[0]
- key2s.append('%s(%d)' % (field, mymap[key2]))
- service_rep.append([service, ', '.join(key2s)])
- blank = 0
- for svcrep in service_rep:
+ for (svc,reps) in per_user[username].items():
if blank: key = ' '
else: blank = 1
- rep[action] += self.line_rep % (flipper, key,
- svcrep[0], svcrep[1])
+ if reps:
+ rep[action] += self.line_rep % (flipper, username,
+ svc, ', '.join(reps))
if rep[self.root_failure]:
report += self.subreport_wrap % (self.root_failures_title,
@@ -697,6 +834,14 @@ class logins_mod(InternalModule):
rep[self.open])
report = self.report_wrap % report
+
+ if self.oldest_to_keep:
+ print self.oldest_to_keep
+ q = """delete from logins where stamp < ?"""
+ p = (self.oldest_to_keep,)
+ executeSQL(self.db_cx.cursor(), q, p)
+ self.db_cx.commit()
+
return report
if __name__ == '__main__':
12 years, 8 months