[java-idp-log-analysis] 02/06: Initial comit of IdP audit log analysis tool - SC-30

Ian Young ian at iay.org.uk
Mon Jan 23 11:33:02 EST 2017


This is an automated email from the git hooks/post-receive script.

iay pushed a commit to branch master
in repository java-idp-log-analysis.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-log-analysis.git;a=commit;h=12fba31a341629f8ed981f657fb554a2e6d74097

commit 12fba31a341629f8ed981f657fb554a2e6d74097
Author: Chad La Joie <clajoie at gmail.com>
AuthorDate: Tue Mar 16 06:29:06 2010 +0000

    Initial comit of IdP audit log analysis tool - SC-30
---
 .project       |  11 ++++
 README.txt     |  29 +++++++++
 loganalyais.py | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 225 insertions(+)

diff --git a/.project b/.project
new file mode 100644
index 0000000..c386349
--- /dev/null
+++ b/.project
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>java-idp-log-analysis</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+	</buildSpec>
+	<natures>
+	</natures>
+</projectDescription>
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..a4f55b9
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,29 @@
+Shibboleth IdP 2 Audit Log Analysis Tool
+
+This analysis tool operates over one, or more, IdP audit log files and 
+provides the follow statistics:
+ * Entity IDs of unique relying parties
+ * Number of unique relying parties
+ * Number of authentication events
+ * Number of unique authenticated principals
+ * Number of authentication events per relying party
+ * SAML profiles used per relying party
+ 
+ Requirements:
+ * Python
+ * Shibboleth 2 IdP Audit Log Files
+ 
+ Usage:
+   python loganalysis.py [options] <log_files>
+   
+   Options:
+     -r, --relyingparties - list of unique relying parties, sorted by name
+     -c, --rpcount - number of unique relying parties
+     -u, --users - number of unique principals
+     -l, --logins - number of logins
+     -p, --rplogins - number of events per relying party, by name
+     -n, --rploginssort - number of events per relying party, sorted numerically
+     -m, --msgprofiles - usage of SAML message profiles per relying party 
+     -q, --quiet - suppress all descriptive or decorative output
+     
+   More than one option may be used at a time.
\ No newline at end of file
diff --git a/loganalyais.py b/loganalyais.py
new file mode 100644
index 0000000..d53e41f
--- /dev/null
+++ b/loganalyais.py
@@ -0,0 +1,185 @@
+#!/usr/bin/python
+"""Parse Shibboleth 2.1 Identity Provider audit logfile and generate simple stats.
+   Audit log file format: https://spaces.internet2.edu/display/SHIB2/IdPLogging"""
+   
+import sys
+from optparse import OptionParser
+from operator import itemgetter
+
+def getLines(files):
+    """Return lines from file(s) or STDIN."""
+    lines = []
+    for file in files:
+        if file != "-":
+            try:
+                for line in open(file, "r"):
+                    lines.append(line)
+            except IOError:
+                print "File '" + file + "' not found."
+                sys.exit(-1)
+        else:
+            for line in sys.stdin:
+                lines.append(line)
+    return lines
+
+def linesFromFiles(files):
+    """Return lists (events) of lists (data fields)."""
+    lines = getLines(files)
+    table = []
+    for line in lines:
+        table.append(line.rstrip().split("|"))
+    return table
+
+def parseFiles(files,options):
+    """Build datastructures from lines."""
+    lines = linesFromFiles(files)
+    db = {}
+    db['rp'], db['users'], db['msgprof'], db['logins'] = {},{},{},0
+
+    for event in lines:
+        datetime,reqBind,reqId,rp,msgProfile,idp,respBind,respId,user,authnMeth,relAttribs,nameId,assertIds,EOL = event
+        if msgProfile.lower().endswith(":sso"):
+            db['logins'] += 1
+
+        # we almost always need to cound rps:
+        if db['rp'].has_key(rp):
+            db['rp'][rp] += 1 
+        else:
+            db['rp'][rp] = 1 
+
+        # only count users if asked to
+        if options.uniqusers:
+            if db['users'].has_key(user):
+               db['users'][user] += 1
+            else:
+               db['users'][user] = 1
+
+        # only count message profiles and rps if asked to
+        if options.msgprofiles:
+            if db['msgprof'].has_key(msgProfile):
+                if db['msgprof'][msgProfile].has_key(rp):
+                   db['msgprof'][msgProfile][rp] += 1
+                else:
+                   db['msgprof'][msgProfile][rp] = 1
+            else:
+                db['msgprof'][msgProfile] = {}
+                db['msgprof'][msgProfile][rp] = 1
+    return db
+
+def uniqueRps(db):
+    """Output unique relying parties."""
+    for rp in sorted(db['rp'].keys()):
+        print rp
+    
+def uniqueRpCount(db,options):
+    """Output number of unique relying parties."""
+    rps = len(db['rp'].keys())
+    if options.quiet:
+        print rps
+    else:
+        print "%d unique relying part%s" % (rps, ('y', 'ies')[rps!=1])
+    
+def loginCount(db,options):
+    """Output total number of logins."""
+    logins = db['logins']
+    if not options.quiet:
+        print "%d login%s" % (logins, ('', 's')[logins!=1])
+    else:
+        print logins
+
+def uniqueUsers(db,options):
+    """Output number of unique userids."""
+    users = len(db['users'].keys())
+    if not options.quiet:
+        print "%d unique userid%s" % (users, ('', 's')[users!=1])
+    else:
+        print users
+
+def loginsPerRp(db,options):
+    """Output list of logins per relying party."""
+    for rp,i in db['rp'].items():
+        if options.quiet:
+            print "%d %s" % (i, rp)
+        else:
+            print "%d\t | %s" % (i, rp)
+
+def loginsPerRpSorted(db,options):
+    """Output sorted list of logins per relying party."""
+    for rp,i in sorted(db['rp'].iteritems(), key=itemgetter(1), reverse=True):
+        if options.quiet:
+            print "%d %s" % (i, rp)
+        else:
+            print "%d\t | %s" % (i, rp)
+
+def rpPerMessageProfile(db,options):
+    """Output usage of SAML message profiles per relying party."""
+    for mp,rps in db['msgprof'].items():
+        print mp
+        for rp,i in sorted(rps.iteritems(), key=itemgetter(1), reverse=True):
+            if options.quiet:
+                print "%d %s" % (i, rp)
+            else:
+                print "%d\t | %s" % (i, rp)
+        if not options.quiet:
+            print
+
+def main():
+    """Parse command line options and aguments and their contents."""
+    parser = OptionParser()
+    usage = "usage: %prog [options] [files ...]"
+    parser = OptionParser(usage)
+    parser.add_option("-r", "--relyingparties", help="list of unique relying parties, sorted by name",
+                      action="store_true", dest="uniqrp")
+    parser.add_option("-c", "--rpcount", help="number of unique relying parties",
+                      action="store_true")
+    parser.add_option("-u", "--users", help="number of unique userids",
+                      action="store_true", dest="uniqusers")
+    parser.add_option("-l", "--logins", help="number of logins",
+                      action="store_true")
+    parser.add_option("-p", "--rplogins", help="number of events per relying party, by name",
+                      action="store_true")
+    parser.add_option("-n", "--rploginssort", help="number of events per relying party, sorted numerically",
+                      action="store_true")
+    parser.add_option("-m", "--msgprofiles", help="usage of SAML message profiles per relying party ",
+                      action="store_true")
+    parser.add_option("-q", "--quiet", help="suppress all descriptive or decorative output",
+                      action="store_true" )
+
+    # Parse options and do basic sanity checking
+    (options, args) = parser.parse_args()
+    if len(args) == 0:
+        print "Missing filename(s). Specify '-' as filename to read from STDIN.\n"
+        parser.print_help()
+        sys.exit(-1)
+    if options.rplogins and options.rploginssort:
+        parser.error("Options -p and -n are mutually exclusive (just use one or the other).")
+
+    # Make sure that at least one option is set, otherwise don't bother parsing any logfiles
+    hasOpt = False
+    for value in options.__dict__.values():
+        if value:
+            hasOpt = True
+            break
+    if hasOpt:
+        db = parseFiles(args,options)
+    else:
+        print "Missing option: At least one option needs to be supplied.\n"
+        parser.print_help()
+        sys.exit(-1)
+
+    # map comman line options to procedures
+    if options.uniqrp: uniqueRps(db)
+    if options.rpcount: uniqueRpCount(db,options)
+    if options.uniqusers: uniqueUsers(db,options)
+    if options.logins: loginCount(db,options)
+    if options.rplogins or options.rploginssort:
+        if not options.quiet:
+            header = "logins\t | relyingPartyId"
+            print "\n" + header + "\n" + '-'*(len(header)+1)
+    if options.rplogins: loginsPerRp(db,options)
+    if options.rploginssort: loginsPerRpSorted(db,options)
+    if options.msgprofiles: rpPerMessageProfile(db,options)
+
+if __name__ == "__main__":
+    main()
+

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list