Merge pull request #50 from caffeinehit/pr/48

Implement OAUTH_DELETE_EXPIRED setting
This commit is contained in:
Evan Culver 2013-11-06 10:45:56 -08:00
commit 2c345f2f5b
6 changed files with 102 additions and 11 deletions

View File

@ -28,7 +28,7 @@
:settings: `OAUTH_EXPIRE_DELTA`
:default: `datetime.timedelta(days=365)`
The time to expiry for access tokens as outlined in :rfc:`4.2.2` and
:rfc:`5.1`.
@ -36,9 +36,17 @@
:settings: `OAUTH_EXPIRE_CODE_DELTA`
:default: `datetime.timedelta(seconds=10*60)`
The time to expiry for an authorization code grant as outlined in :rfc:`4.1.2`.
.. attribute:: DELETE_EXPIRED
:settings: `OAUTH_DELETE_EXPIRED`
:default: `False`
To remove expired tokens immediately instead of letting them persist, set
to `True`.
.. attribute:: ENFORCE_SECURE
:settings: `OAUTH_ENFORCE_SECURE`

View File

@ -26,11 +26,15 @@ DEFAULT_SCOPES = (
SCOPES = getattr(settings, 'OAUTH_SCOPES', DEFAULT_SCOPES)
EXPIRE_DELTA = getattr(settings, 'OAUTH_EXPIRE_DELTA', timedelta(days=365))
# Expiry delta for public clients (which typically have shorter lived tokens)
EXPIRE_DELTA_PUBLIC = getattr(settings, 'OAUTH_EXPIRE_DELTA_PUBLIC', timedelta(days=30))
EXPIRE_CODE_DELTA = getattr(settings, 'OAUTH_EXPIRE_CODE_DELTA', timedelta(seconds=10 * 60))
# Remove expired tokens immediately instead of letting them persist.
DELETE_EXPIRED = getattr(settings, 'OAUTH_DELETE_EXPIRED', False)
ENFORCE_SECURE = getattr(settings, 'OAUTH_ENFORCE_SECURE', False)
ENFORCE_CLIENT_SECURE = getattr(settings, 'OAUTH_ENFORCE_CLIENT_SECURE', True)

View File

@ -7,7 +7,7 @@ views in :attr:`provider.views`.
from django.db import models
from django.conf import settings
from .. import constants
from ..constants import CLIENT_TYPES
from ..constants import CLIENT_TYPES, DELETE_EXPIRED
from ..utils import short_token, long_token, get_token_expiry
from ..utils import get_code_expiry
from ..utils import now

View File

@ -12,7 +12,7 @@ from ..compat import skipIfCustomUser
from ..templatetags.scope import scopes
from ..utils import now as date_now
from .forms import ClientForm
from .models import Client, Grant, AccessToken
from .models import Client, Grant, AccessToken, RefreshToken
from .backends import BasicClientBackend, RequestParamsClientBackend
from .backends import AccessTokenBackend
@ -531,3 +531,71 @@ class ScopeTest(TestCase):
names.sort()
self.assertEqual('read read+write write', ' '.join(names))
class DeleteExpiredTest(BaseOAuth2TestCase):
fixtures = ['test_oauth2']
def setUp(self):
self._delete_expired = constants.DELETE_EXPIRED
constants.DELETE_EXPIRED = True
def tearDown(self):
constants.DELETE_EXPIRED = self._delete_expired
def test_clear_expired(self):
self.login()
self._login_and_authorize()
response = self.client.get(self.redirect_url())
self.assertEqual(302, response.status_code)
location = response['Location']
self.assertFalse('error' in location)
self.assertTrue('code' in location)
# verify that Grant with code exists
code = urlparse.parse_qs(location)['code'][0]
self.assertTrue(Grant.objects.filter(code=code).exists())
# use the code/grant
response = self.client.post(self.access_token_url(), {
'grant_type': 'authorization_code',
'client_id': self.get_client().client_id,
'client_secret': self.get_client().client_secret,
'code': code})
self.assertEquals(200, response.status_code)
token = json.loads(response.content)
self.assertTrue('access_token' in token)
access_token = token['access_token']
self.assertTrue('refresh_token' in token)
refresh_token = token['refresh_token']
# make sure the grant is gone
self.assertFalse(Grant.objects.filter(code=code).exists())
# and verify that the AccessToken and RefreshToken exist
self.assertTrue(AccessToken.objects.filter(token=access_token)
.exists())
self.assertTrue(RefreshToken.objects.filter(token=refresh_token)
.exists())
# refresh the token
response = self.client.post(self.access_token_url(), {
'grant_type': 'refresh_token',
'refresh_token': token['refresh_token'],
'client_id': self.get_client().client_id,
'client_secret': self.get_client().client_secret,
})
self.assertEqual(200, response.status_code)
token = json.loads(response.content)
self.assertTrue('access_token' in token)
self.assertNotEquals(access_token, token['access_token'])
self.assertTrue('refresh_token' in token)
self.assertNotEquals(refresh_token, token['refresh_token'])
# make sure the orig AccessToken and RefreshToken are gone
self.assertFalse(AccessToken.objects.filter(token=access_token)
.exists())
self.assertFalse(RefreshToken.objects.filter(token=refresh_token)
.exists())

View File

@ -1,5 +1,6 @@
from datetime import timedelta
from django.core.urlresolvers import reverse
from .. import constants
from ..views import Capture, Authorize, Redirect
from ..views import AccessToken as AccessTokenView, OAuthError
from ..utils import now
@ -116,13 +117,22 @@ class AccessTokenView(AccessTokenView):
)
def invalidate_grant(self, grant):
grant.expires = now() - timedelta(days=1)
grant.save()
if constants.DELETE_EXPIRED:
grant.delete()
else:
grant.expires = now() - timedelta(days=1)
grant.save()
def invalidate_refresh_token(self, rt):
rt.expired = True
rt.save()
if constants.DELETE_EXPIRED:
rt.delete()
else:
rt.expired = True
rt.save()
def invalidate_access_token(self, at):
at.expires = now() - timedelta(days=1)
at.save()
if constants.DELETE_EXPIRED:
at.delete()
else:
at.expires = now() - timedelta(days=1)
at.save()

View File

@ -492,6 +492,7 @@ class AccessToken(OAuthView, Mixin):
"""
rt = self.get_refresh_token_grant(request, data, client)
# this must be called first in case we need to purge expired tokens
self.invalidate_refresh_token(rt)
self.invalidate_access_token(rt.access_token)