Added API access tracking to EVE Proxy and presented via SSO.

This commit is contained in:
2010-05-30 22:16:00 +01:00
parent 981a1843a6
commit 31e324d0a2
7 changed files with 110 additions and 25 deletions

View File

@@ -1,9 +1,16 @@
from django.contrib import admin from django.contrib import admin
from eve_proxy.models import CachedDocument from eve_proxy.models import CachedDocument, ApiAccessLog
class CachedDocumentAdmin(admin.ModelAdmin): class CachedDocumentAdmin(admin.ModelAdmin):
model = CachedDocument model = CachedDocument
list_display = ('url_path', 'time_retrieved', 'cached_until') list_display = ('url_path', 'time_retrieved', 'cached_until')
verbose_name = 'Cached Document' verbose_name = 'Cached Document'
verbose_name_plural = 'Cached Documents' verbose_name_plural = 'Cached Documents'
admin.site.register(CachedDocument, CachedDocumentAdmin) 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)

View File

@@ -60,17 +60,17 @@ class CachedDocumentManager(models.Manager):
May also be a string representation of May also be a string representation of
the query: userID=1&characterID=xxxxxxxx the query: userID=1&characterID=xxxxxxxx
""" """
if type({}) == type(params):
# If 'params' is a dictionary, convert it to a URL string. paramstr = urllib.urlencode(params)
params = urllib.urlencode(params)
elif params == None or params.strip() == '': if params == None or paramstr.strip() == '':
# For whatever reason, EVE API freaks out if there are no parameters. # 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 # Add a bogus parameter if none are specified. I'm sure there's a
# better fix for this. # better fix for this.
params = 'odd_parm=1' paramstr = 'odd_parm=1'
# Combine the URL path and the parameters to create the full query. # 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:
# If no_cache is enabled, don't even attempt a lookup. # If no_cache is enabled, don't even attempt a lookup.
@@ -84,14 +84,33 @@ class CachedDocumentManager(models.Manager):
# EVE uses UTC. # EVE uses UTC.
current_eve_time = datetime.utcnow() 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 # Figure out if we need hit EVE API and re-cache, or just pull from
# the local cache (based on cached_until). # the local cache (based on cached_until).
if no_cache or created or \ if no_cache or created or \
cached_doc.cached_until == None or \ cached_doc.cached_until == None or \
current_eve_time > cached_doc.cached_until: current_eve_time > cached_doc.cached_until:
# Cache from EVE API # 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) 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: else:
# Parse the document here since it was retrieved from the # Parse the document here since it was retrieved from the
# database cache instead of queried for. # database cache instead of queried for.
@@ -100,15 +119,17 @@ class CachedDocumentManager(models.Manager):
# Check for the presence errors. Only check the bare minimum, # Check for the presence errors. Only check the bare minimum,
# generic stuff that applies to most or all queries. User-level code # generic stuff that applies to most or all queries. User-level code
# should check for the more specific errors. # should check for the more specific errors.
error_node = dom.getElementsByTagName('error')
if error_node: if dom:
error_code = error_node[0].getAttribute('code') error_node = dom.getElementsByTagName('error')
# User specified an invalid userid and/or auth key. if error_node:
if error_code == '203': error_code = error_node[0].getAttribute('code')
raise APIAuthException() # User specified an invalid userid and/or auth key.
elif error_code == '106': if error_code == '203':
raise APINoUserIDException() raise APIAuthException()
elif error_code == '106':
raise APINoUserIDException()
return cached_doc return cached_doc
class CachedDocument(models.Model): class CachedDocument(models.Model):
@@ -122,3 +143,12 @@ class CachedDocument(models.Model):
# The custom manager handles the querying. # The custom manager handles the querying.
objects = CachedDocumentManager() 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)

View File

@@ -1,5 +1,5 @@
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.http import HttpResponse from django.http import HttpResponse, HttpResponseNotFound
from eve_proxy.models import CachedDocument from eve_proxy.models import CachedDocument
def retrieve_xml(request): def retrieve_xml(request):
@@ -12,18 +12,28 @@ def retrieve_xml(request):
# The parameters attached to the end of the URL path. # The parameters attached to the end of the URL path.
if request.method == 'POST': if request.method == 'POST':
params = request.POST.urlencode() p = request.POST
print params
else: else:
params = request.META['QUERY_STRING'] p = request.GET
print params
# Convert the QuerySet object into a dict
params = {}
for key,value in p.items():
params[key] = value
if url_path == '/' or url_path == '': if url_path == '/' or url_path == '':
# If they don't provide any kind of query, shoot a quick error message. # If they don't provide any kind of query, shoot a quick error message.
return HttpResponse('No API query specified.') 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 # The query system will retrieve a cached_doc that was either previously
# or newly cached depending on cache intervals. # or newly cached depending on cache intervals.
cached_doc = CachedDocument.objects.api_query(url_path, params) cached_doc = CachedDocument.objects.api_query(url_path, params)
# Return the document's body as XML. # 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')

View File

@@ -17,6 +17,7 @@ urlpatterns = patterns('',
(r'^profile/del/reddit/$', views.reddit_del), (r'^profile/del/reddit/$', views.reddit_del),
(r'^profile/del/reddit/(?P<redditid>\d+)/$', views.reddit_del), (r'^profile/del/reddit/(?P<redditid>\d+)/$', views.reddit_del),
(r'^profile/refresh/eveapi/(?P<userid>\d+)/$', views.eveapi_refresh), (r'^profile/refresh/eveapi/(?P<userid>\d+)/$', views.eveapi_refresh),
(r'^profile/log/eveapi/(?P<userid>\d+)/$', views.eveapi_log),
(r'^profile/characters$', views.characters), (r'^profile/characters$', views.characters),
(r'^profile/characters/(?P<charid>.*)/$', views.characters), (r'^profile/characters/(?P<charid>.*)/$', views.characters),
(r'^users/(?P<username>.*)/$', views.user_view), (r'^users/(?P<username>.*)/$', views.user_view),

View File

@@ -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.api_puller.accounts import import_eve_account
from eve_api.models.api_player import EVEAccount, EVEPlayerCharacter 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.models import ServiceAccount, Service, SSOUser, ExistingUser, ServiceError
from sso.forms import EveAPIForm, UserServiceAccountForm, ServiceAccountResetForm, RedditAccountForm, UserLookupForm 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')) 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 @login_required
def service_add(request): def service_add(request):
clsform = UserServiceAccountForm(request.user) clsform = UserServiceAccountForm(request.user)

View File

@@ -0,0 +1,19 @@
{% extends "base.html" %}
{% block title %}EVE API Access Logs{% endblock %}
{% block content %}
<h1>Access Logs for API Key {{ userid }}</h1>
<table>
<tr><th>Service ID</th><th>Date / Time</th><th>API</th></tr>
{% for log in logs %}
<tr><td>{{ log.service }}</td>
<td>{{ log.time_access }}</td>
<td>{{ log.document }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}

View File

@@ -69,6 +69,7 @@ setup.</p>
<td>{{ acc.api_status_description }}</td> <td>{{ acc.api_status_description }}</td>
<td>{{ acc.api_last_updated|naturaltimediff }}</td> <td>{{ acc.api_last_updated|naturaltimediff }}</td>
<td><a href="{% url sso.views.eveapi_refresh acc.api_user_id %}">Refresh</a>,&nbsp; <td><a href="{% url sso.views.eveapi_refresh acc.api_user_id %}">Refresh</a>,&nbsp;
<a href="{% url sso.views.eveapi_log acc.api_user_id %}">Logs</a>,&nbsp;
<a href="{% url sso.views.eveapi_del acc.api_user_id %}">Delete</a></td> <a href="{% url sso.views.eveapi_del acc.api_user_id %}">Delete</a></td>
</tr> </tr>
</tbody> </tbody>