mirror of
https://github.com/nikdoof/test-auth.git
synced 2025-12-14 06:42:16 +00:00
OAuth Support
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
from django.contrib import admin
|
||||
from api.models import AuthAPIKey, AuthAPILog
|
||||
|
||||
from piston.models import Consumer, Token
|
||||
|
||||
class AuthAPIKeyAdmin(admin.ModelAdmin):
|
||||
list_display = ('key', 'name', 'url', 'active')
|
||||
@@ -18,3 +18,5 @@ class AuthAPILogAdmin(admin.ModelAdmin):
|
||||
|
||||
admin.site.register(AuthAPIKey, AuthAPIKeyAdmin)
|
||||
admin.site.register(AuthAPILog, AuthAPILogAdmin)
|
||||
admin.site.register(Consumer, admin.ModelAdmin)
|
||||
admin.site.register(Token, admin.ModelAdmin)
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
from v1 import *
|
||||
from v2 import *
|
||||
from oauth import *
|
||||
|
||||
120
app/api/handlers/oauth.py
Normal file
120
app/api/handlers/oauth.py
Normal file
@@ -0,0 +1,120 @@
|
||||
import re
|
||||
from datetime import datetime
|
||||
from xml.dom import minidom
|
||||
from operator import itemgetter
|
||||
|
||||
from django.contrib.auth import login, logout, authenticate
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from django.http import HttpResponse
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.conf import settings
|
||||
|
||||
from piston.handler import BaseHandler
|
||||
from piston.utils import rc, throttle
|
||||
|
||||
from api.models import AuthAPIKey, AuthAPILog
|
||||
|
||||
from eve_proxy.models import CachedDocument
|
||||
from eve_proxy.exceptions import *
|
||||
from eve_api.app_defines import *
|
||||
from eve_api.models import EVEAccount, EVEPlayerCharacter
|
||||
|
||||
|
||||
class OAuthEveAPIHandler(BaseHandler):
|
||||
allowed_methods = ('GET')
|
||||
exclude = ('api_key')
|
||||
|
||||
def read(self, request):
|
||||
if request.user:
|
||||
s = EVEAccount.objects.filter(user=request.user)
|
||||
return {'keys': s.values('api_user_id', 'user_id', 'api_status', 'api_last_updated')}
|
||||
|
||||
|
||||
class OAuthOpTimerHandler(BaseHandler):
|
||||
allowed_methods = ('GET')
|
||||
|
||||
def read(self, request, id=None):
|
||||
|
||||
objs = EVEAccount.objects.filter(user=request.user, api_keytype=API_KEYTYPE_FULL)
|
||||
|
||||
if not objs.count():
|
||||
objs = [get_object_or_404(EVEAccount, pk=settings.FULL_API_USER_ID)]
|
||||
|
||||
events = []
|
||||
for obj in objs:
|
||||
params = {'userID': obj.pk, 'apiKey': obj.api_key, 'characterID': settings.FULL_API_CHARACTER_ID}
|
||||
error_doc = {'ops': [{'startsIn': -1, 'eventID': 0, 'ownerName': '', 'eventDate': '', 'eventTitle': '<div style="text-align:center">The EVE API calendar is unavailable</div>', 'duration': 0, 'isImportant': 0, 'eventText': 'Fuck CCP tbqh imho srsly', 'endsIn':-1, 'forumLink': ''}]}
|
||||
try:
|
||||
cached_doc = CachedDocument.objects.api_query('/char/UpcomingCalendarEvents.xml.aspx', params, timeout=10, service="Optimer")
|
||||
except DocumentRetrievalError:
|
||||
return error_doc
|
||||
dom = minidom.parseString(cached_doc.body.encode('utf-8'))
|
||||
if dom.getElementsByTagName('error'):
|
||||
error_doc['raw_xml'] = cached_doc.body
|
||||
return error_doc
|
||||
|
||||
for node in dom.getElementsByTagName('rowset')[0].childNodes:
|
||||
if node.nodeType == 1:
|
||||
ownerID = node.getAttribute('ownerID')
|
||||
if ownerID != '1':
|
||||
date = node.getAttribute('eventDate')
|
||||
dt = datetime.strptime(date, '%Y-%m-%d %H:%M:%S')
|
||||
now = datetime.utcnow()
|
||||
startsIn = int(dt.strftime('%s')) - int(now.strftime('%s'))
|
||||
duration = int(node.getAttribute('duration'))
|
||||
|
||||
fid = re.search('topic=[\d]+', node.getAttribute('eventText'))
|
||||
if fid:
|
||||
forumlink = 'http://forum.pleaseignore.com/index.php?%s' % fid.group(0)
|
||||
else:
|
||||
forumlink = ''
|
||||
#In case people forget to set a duration, we'll give a default of 1 hour
|
||||
if duration == 0:
|
||||
duration = 60
|
||||
endsIn = startsIn + (duration * 60)
|
||||
if startsIn < 0:
|
||||
startsIn = 0
|
||||
if endsIn > 0:
|
||||
event = {
|
||||
'startsIn': startsIn,
|
||||
'eventID': node.getAttribute('eventID'),
|
||||
'ownerName': node.getAttribute('ownerName'),
|
||||
'eventDate': date,
|
||||
'eventTitle': node.getAttribute('eventTitle'),
|
||||
'duration': duration,
|
||||
'isImportant': node.getAttribute('importance'),
|
||||
'eventText': node.getAttribute('eventText'),
|
||||
'endsIn':endsIn,
|
||||
'forumLink': forumlink}
|
||||
events.append(event)
|
||||
|
||||
if len(events) == 0:
|
||||
return {'ops':[{
|
||||
'startsIn': -1,
|
||||
'eventID': 0,
|
||||
'ownerName': '',
|
||||
'eventDate': '',
|
||||
'eventTitle': '<div style="text-align:center">No ops are currently scheduled</div>',
|
||||
'duration': 0,
|
||||
'isImportant': 0,
|
||||
'eventText': 'Add ops using EVE-Gate or the in-game calendar',
|
||||
'endsIn':-1,
|
||||
'forumLink': ''}]}
|
||||
else:
|
||||
events.sort(key=itemgetter('startsIn'))
|
||||
return {'ops': events, 'doc_time': cached_doc.time_retrieved, 'cache_until': cached_doc.cached_until, 'current_time': datetime.utcnow() }
|
||||
|
||||
|
||||
class CharacterHandler(BaseHandler):
|
||||
allowed_methods = ('GET')
|
||||
|
||||
fields = ('id', 'name', ('corporation', ('id', 'name', ('alliance', ('id', 'name')))), 'corporation_date', 'balance', 'total_sp', 'security_status', 'director', 'skillset')
|
||||
|
||||
@classmethod
|
||||
def skillset(cls, instance):
|
||||
return instance.eveplayercharacterskill_set.all().values('skill__id', 'skill__name', 'level', 'skillpoints')
|
||||
|
||||
def read(self, request):
|
||||
return EVEPlayerCharacter.objects.filter(eveaccount__user=request.user)
|
||||
23
app/api/templates/api/view_tokens.html
Normal file
23
app/api/templates/api/view_tokens.html
Normal file
@@ -0,0 +1,23 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Application Access{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>Application Access</h1>
|
||||
|
||||
<p>This is the list of external applications that can access your data stored on Auth, this includes any EVE API data and also any linked information (Reddit accounts, service accounts).</p>
|
||||
|
||||
{% if tokens %}
|
||||
<table>
|
||||
<tr><th>Application</th><th>Granted Date/Time</th><th>Action</th></tr>
|
||||
{% for token in tokens %}
|
||||
<tr>
|
||||
<tr><td>{{ token.consumer.name }}</td><td></td><td><a href="{% url oauth-revoke-token token.key %}">Revoke</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p><b>You have not granted any applications access to your data</b></p>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
@@ -1,5 +1,5 @@
|
||||
from django.conf.urls.defaults import *
|
||||
from piston.authentication import NoAuthentication
|
||||
from piston.authentication import NoAuthentication, OAuthAuthentication
|
||||
|
||||
from api.resource import SentryResource as Resource
|
||||
from api.auth import APIKeyAuthentication
|
||||
@@ -7,6 +7,7 @@ from api.handlers import *
|
||||
|
||||
noauth = {'authentication': NoAuthentication() }
|
||||
apikeyauth = {'authentication': APIKeyAuthentication() }
|
||||
oauth = {'authentication': OAuthAuthentication() }
|
||||
|
||||
# v1 APIs
|
||||
user_resource = Resource(handler=UserHandler, **apikeyauth)
|
||||
@@ -19,17 +20,6 @@ characters_resource = Resource(handler=CharacterHandler, **apikeyauth)
|
||||
announce_resource = Resource(handler=AnnounceHandler, **apikeyauth)
|
||||
|
||||
urlpatterns = patterns('',
|
||||
url(r'^user/$', user_resource),
|
||||
url(r'^login/$', login_resource),
|
||||
url(r'^eveapi/$', eveapi_resource),
|
||||
url(r'^eveapi/', eveapiproxy_resource, name='api-eveapiproxy'),
|
||||
url(r'^character/$', characters_resource),
|
||||
url(r'^optimer/$', optimer_resource),
|
||||
url(r'^blacklist/$', blacklist_resource),
|
||||
url(r'^announce/$', announce_resource),
|
||||
)
|
||||
|
||||
urlpatterns += patterns('',
|
||||
url(r'^1.0/user/$', user_resource),
|
||||
url(r'^1.0/login/$', login_resource),
|
||||
url(r'^1.0/eveapi/$', eveapi_resource),
|
||||
@@ -50,3 +40,23 @@ urlpatterns += patterns('',
|
||||
url(r'^2.0/proxy/', v2_eveapiproxy_resource, name='v2-api-eveapiproxy'),
|
||||
url(r'^2.0/user/(?P<userid>\d+)/$', v2_user_resource, name='v2-api-user'),
|
||||
)
|
||||
|
||||
# OAuth
|
||||
urlpatterns += patterns('piston.authentication',
|
||||
url(r'^oauth/request_token/$','oauth_request_token'),
|
||||
url(r'^oauth/authorize/$','oauth_user_auth'),
|
||||
url(r'^oauth/access_token/$','oauth_access_token'),
|
||||
)
|
||||
|
||||
urlpatterns += patterns('api.views',
|
||||
url(r'^oauth/tokens/$', 'oauth_list_tokens', name='oauth-list-tokens'),
|
||||
url(r'^oauth/tokens/(?P<key>.*)$', 'oauth_revoke_token', name='oauth-revoke-token'),
|
||||
)
|
||||
|
||||
oauth_optimer_resource = Resource(handler=OAuthOpTimerHandler, **oauth)
|
||||
|
||||
# API
|
||||
urlpatterns += patterns('',
|
||||
url(r'^oauth/optimer/$', oauth_optimer_resource),
|
||||
)
|
||||
|
||||
|
||||
@@ -1,5 +1,39 @@
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import render_to_response, get_object_or_404
|
||||
from django.template import RequestContext
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib import messages
|
||||
|
||||
from piston import forms
|
||||
from piston.models import Token
|
||||
|
||||
def oauth_callback_view(request, other):
|
||||
return HttpResponse("The application you allowed access to didn't request a callback, thats odd and should be fixed")
|
||||
|
||||
|
||||
def oauth_callback(request, other):
|
||||
return HttpResponse('Fake callback view.')
|
||||
@login_required
|
||||
def oauth_auth_view(request, token, callback, params):
|
||||
form = forms.OAuthAuthenticationForm(initial={
|
||||
'oauth_token': token.key,
|
||||
'oauth_callback': token.get_callback_url() or callback,
|
||||
})
|
||||
|
||||
return render_to_response('piston/authorize_token.html',
|
||||
{ 'form': form, 'consumer': token.consumer, 'user': token.user, 'request': request }, context_instance=RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
def oauth_list_tokens(request):
|
||||
# List all Access tokens
|
||||
tokens = Token.objects.filter(token_type=Token.ACCESS)
|
||||
return render_to_response('api/view_tokens.html', { 'tokens': tokens, 'request': request }, context_instance=RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
def oauth_revoke_token(request, key):
|
||||
token = get_object_or_404(Token, key=key)
|
||||
if token.user == request.user:
|
||||
token.delete()
|
||||
messages.success(request, "Access for %s has been revoked" % token.consumer.name, fail_silently=True)
|
||||
|
||||
return redirect(oauth_list_tokens)
|
||||
|
||||
@@ -87,6 +87,11 @@ AUTH_PROFILE_MODULE = 'sso.SSOUser'
|
||||
LOGIN_REDIRECT_URL = "/profile"
|
||||
LOGIN_URL = "/login"
|
||||
|
||||
### OAuth
|
||||
|
||||
OAUTH_AUTH_VIEW = 'api.views.oauth_auth_view'
|
||||
OAUTH_CALLBACK_VIEW = 'api.views.oauth_callback_view'
|
||||
|
||||
### Celery Schedule
|
||||
|
||||
from celeryschedule import *
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
{% if "groups"|installed %}
|
||||
<li><a href="{% url groups.views.group_list %}">Groups</a></li>
|
||||
{% endif %}
|
||||
{% if "api"|installed %}
|
||||
<li><a href="{% url oauth-list-tokens %}">Application Access</a></li>
|
||||
{% endif %}
|
||||
{% if "hr"|installed %}
|
||||
<li><a href="{% url hr.views.index %}">HR</a></li>
|
||||
{% endif %}
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
|
||||
<p>You have come here because you are in the process of allowing a external application to access your private Auth data. If you are not, then please close this window. Otherwise, please confirm below if you wish to give access to your private Auth data. This can be revoked at any time from the main Auth panel.</p>
|
||||
|
||||
<p><b>{{ consumer.name }}</b></p>
|
||||
<p>{{ consumer.description }}</p>
|
||||
|
||||
<form action="{% url piston.authentication.oauth_user_auth %}" method="POST">
|
||||
<table>
|
||||
{{ form.as_table }}
|
||||
|
||||
Reference in New Issue
Block a user