From 31e324d0a279c779b12ddb260561270380e348b1 Mon Sep 17 00:00:00 2001
From: Andrew Williams
Date: Sun, 30 May 2010 22:16:00 +0100
Subject: [PATCH] Added API access tracking to EVE Proxy and presented via SSO.
---
eve_proxy/admin.py | 13 ++++++--
eve_proxy/models.py | 62 ++++++++++++++++++++++++++---------
eve_proxy/views.py | 22 +++++++++----
sso/urls.py | 1 +
sso/views.py | 17 ++++++++++
templates/sso/eveapi_log.html | 19 +++++++++++
templates/sso/profile.html | 1 +
7 files changed, 110 insertions(+), 25 deletions(-)
create mode 100644 templates/sso/eveapi_log.html
diff --git a/eve_proxy/admin.py b/eve_proxy/admin.py
index b4e029f..9ad1ee3 100755
--- a/eve_proxy/admin.py
+++ b/eve_proxy/admin.py
@@ -1,9 +1,16 @@
from django.contrib import admin
-from eve_proxy.models import CachedDocument
+from eve_proxy.models import CachedDocument, ApiAccessLog
class CachedDocumentAdmin(admin.ModelAdmin):
model = CachedDocument
list_display = ('url_path', 'time_retrieved', 'cached_until')
verbose_name = 'Cached Document'
- verbose_name_plural = 'Cached Documents'
-admin.site.register(CachedDocument, CachedDocumentAdmin)
+ verbose_name_plural = 'Cached Documents'
+admin.site.register(CachedDocument, CachedDocumentAdmin)
+
+class ApiAccessLogAdmin(admin.ModelAdmin):
+ model = ApiAccessLog
+ list_display = ('userid', 'service', 'document', 'time_access')
+ verbose_name = 'API Access Log'
+ verbose_name_plural = 'API Access Logs'
+admin.site.register(ApiAccessLog, ApiAccessLogAdmin)
diff --git a/eve_proxy/models.py b/eve_proxy/models.py
index 3d755f7..68ef7c5 100755
--- a/eve_proxy/models.py
+++ b/eve_proxy/models.py
@@ -60,17 +60,17 @@ class CachedDocumentManager(models.Manager):
May also be a string representation of
the query: userID=1&characterID=xxxxxxxx
"""
- if type({}) == type(params):
- # If 'params' is a dictionary, convert it to a URL string.
- params = urllib.urlencode(params)
- elif params == None or params.strip() == '':
+
+ paramstr = urllib.urlencode(params)
+
+ if params == None or paramstr.strip() == '':
# For whatever reason, EVE API freaks out if there are no parameters.
# Add a bogus parameter if none are specified. I'm sure there's a
# better fix for this.
- params = 'odd_parm=1'
+ paramstr = 'odd_parm=1'
# Combine the URL path and the parameters to create the full query.
- query_name = '%s?%s' % (url_path, params)
+ query_name = '%s?%s' % (url_path, paramstr)
if no_cache:
# If no_cache is enabled, don't even attempt a lookup.
@@ -84,14 +84,33 @@ class CachedDocumentManager(models.Manager):
# EVE uses UTC.
current_eve_time = datetime.utcnow()
+ # If we have a service ID, store and strip it from the query string.
+ if 'service' in params:
+ service = params['service']
+ del params['service']
+ else:
+ service = 'auth'
+
+ if 'userID' in params:
+ userid = params['userID']
+
# Figure out if we need hit EVE API and re-cache, or just pull from
# the local cache (based on cached_until).
if no_cache or created or \
cached_doc.cached_until == None or \
current_eve_time > cached_doc.cached_until:
# Cache from EVE API
- dom = self.cache_from_eve_api(cached_doc, url_path, params,
+ dom = self.cache_from_eve_api(cached_doc, url_path, paramstr,
no_cache=no_cache)
+
+ # If its a user related request, log the transaction.
+ if 'userID' in params:
+ log = ApiAccessLog()
+ log.userid = userid
+ log.service = service
+ log.time_access = datetime.utcnow()
+ log.document = url_path
+ log.save()
else:
# Parse the document here since it was retrieved from the
# database cache instead of queried for.
@@ -100,15 +119,17 @@ class CachedDocumentManager(models.Manager):
# Check for the presence errors. Only check the bare minimum,
# generic stuff that applies to most or all queries. User-level code
# should check for the more specific errors.
- error_node = dom.getElementsByTagName('error')
- if error_node:
- error_code = error_node[0].getAttribute('code')
- # User specified an invalid userid and/or auth key.
- if error_code == '203':
- raise APIAuthException()
- elif error_code == '106':
- raise APINoUserIDException()
-
+
+ if dom:
+ error_node = dom.getElementsByTagName('error')
+ if error_node:
+ error_code = error_node[0].getAttribute('code')
+ # User specified an invalid userid and/or auth key.
+ if error_code == '203':
+ raise APIAuthException()
+ elif error_code == '106':
+ raise APINoUserIDException()
+
return cached_doc
class CachedDocument(models.Model):
@@ -122,3 +143,12 @@ class CachedDocument(models.Model):
# The custom manager handles the querying.
objects = CachedDocumentManager()
+
+class ApiAccessLog(models.Model):
+ """
+ Provides a list of API accesses made by applications or Auth
+ """
+ userid = models.IntegerField()
+ service = models.CharField(max_length=255)
+ time_access = models.DateTimeField()
+ document = models.CharField(max_length=255)
diff --git a/eve_proxy/views.py b/eve_proxy/views.py
index 56c2711..a6019a4 100755
--- a/eve_proxy/views.py
+++ b/eve_proxy/views.py
@@ -1,5 +1,5 @@
from django.core.urlresolvers import reverse
-from django.http import HttpResponse
+from django.http import HttpResponse, HttpResponseNotFound
from eve_proxy.models import CachedDocument
def retrieve_xml(request):
@@ -12,18 +12,28 @@ def retrieve_xml(request):
# The parameters attached to the end of the URL path.
if request.method == 'POST':
- params = request.POST.urlencode()
- print params
+ p = request.POST
else:
- params = request.META['QUERY_STRING']
- print params
+ p = request.GET
+
+ # Convert the QuerySet object into a dict
+ params = {}
+ for key,value in p.items():
+ params[key] = value
if url_path == '/' or url_path == '':
# If they don't provide any kind of query, shoot a quick error message.
return HttpResponse('No API query specified.')
+ if not 'service' in params:
+ return HttpResponse('No Service ID provided.')
+
# The query system will retrieve a cached_doc that was either previously
# or newly cached depending on cache intervals.
cached_doc = CachedDocument.objects.api_query(url_path, params)
# Return the document's body as XML.
- return HttpResponse(cached_doc.body, mimetype='text/xml')
+
+ if cached_doc:
+ return HttpResponse(cached_doc.body, mimetype='text/xml')
+
+ return HttpResponseNotFound('Error retrieving the document')
diff --git a/sso/urls.py b/sso/urls.py
index 3805aea..b9f5a6e 100644
--- a/sso/urls.py
+++ b/sso/urls.py
@@ -17,6 +17,7 @@ urlpatterns = patterns('',
(r'^profile/del/reddit/$', views.reddit_del),
(r'^profile/del/reddit/(?P\d+)/$', views.reddit_del),
(r'^profile/refresh/eveapi/(?P\d+)/$', views.eveapi_refresh),
+ (r'^profile/log/eveapi/(?P\d+)/$', views.eveapi_log),
(r'^profile/characters$', views.characters),
(r'^profile/characters/(?P.*)/$', views.characters),
(r'^users/(?P.*)/$', views.user_view),
diff --git a/sso/views.py b/sso/views.py
index 406b94f..83dd8eb 100644
--- a/sso/views.py
+++ b/sso/views.py
@@ -12,6 +12,8 @@ from eve_api.api_exceptions import APIAuthException, APINoUserIDException
from eve_api.api_puller.accounts import import_eve_account
from eve_api.models.api_player import EVEAccount, EVEPlayerCharacter
+from eve_proxy.models import ApiAccessLog
+
from sso.models import ServiceAccount, Service, SSOUser, ExistingUser, ServiceError
from sso.forms import EveAPIForm, UserServiceAccountForm, ServiceAccountResetForm, RedditAccountForm, UserLookupForm
@@ -132,6 +134,21 @@ def eveapi_refresh(request, userid=0):
return HttpResponseRedirect(reverse('sso.views.profile'))
+@login_required
+def eveapi_log(request, userid=0):
+ if userid > 0 :
+
+ try:
+ acc = EVEAccount.objects.get(id=userid)
+ except:
+ pass
+
+ if acc and (acc.user == request.user or request.user.is_staff):
+ logs = ApiAccessLog.objects.filter(userid=userid)[:50]
+ return render_to_response('sso/eveapi_log.html', locals(), context_instance=RequestContext(request))
+
+ return HttpResponseRedirect(reverse('sso.views.profile'))
+
@login_required
def service_add(request):
clsform = UserServiceAccountForm(request.user)
diff --git a/templates/sso/eveapi_log.html b/templates/sso/eveapi_log.html
new file mode 100644
index 0000000..8099e25
--- /dev/null
+++ b/templates/sso/eveapi_log.html
@@ -0,0 +1,19 @@
+{% extends "base.html" %}
+
+{% block title %}EVE API Access Logs{% endblock %}
+
+{% block content %}
+
+Access Logs for API Key {{ userid }}
+
+
+| Service ID | Date / Time | API |
+{% for log in logs %}
+| {{ log.service }} |
+ {{ log.time_access }} |
+ {{ log.document }} |
+
+{% endfor %}
+
+
+{% endblock %}
diff --git a/templates/sso/profile.html b/templates/sso/profile.html
index 1a576a6..ef36c97 100644
--- a/templates/sso/profile.html
+++ b/templates/sso/profile.html
@@ -69,6 +69,7 @@ setup.
{{ acc.api_status_description }} |
{{ acc.api_last_updated|naturaltimediff }} |
Refresh,
+ Logs,
Delete |