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 }}

+ + + +{% for log in logs %} + + + + +{% endfor %} +
Service IDDate / TimeAPI
{{ log.service }}{{ log.time_access }}{{ log.document }}
+ +{% 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,  + LogsDelete