diff --git a/alliance_auth/settings.py.example b/alliance_auth/settings.py.example index c263dcbf..9249416c 100644 --- a/alliance_auth/settings.py.example +++ b/alliance_auth/settings.py.example @@ -87,6 +87,10 @@ CELERYBEAT_SCHEDULE = { 'task': 'eveonline.tasks.run_model_update', 'schedule': crontab(minute=0, hour="*/6"), }, + 'check_all_character_ownership': { + 'task': 'authentication.tasks.check_all_character_ownership', + 'schedule': crontab(hour='*/4'), + } } # Build paths inside the project like this: os.path.join(BASE_DIR, ...) diff --git a/authentication/tasks.py b/authentication/tasks.py new file mode 100644 index 00000000..dc4f07f4 --- /dev/null +++ b/authentication/tasks.py @@ -0,0 +1,36 @@ +from alliance_auth.celeryapp import app +from esi.models import Token +from esi.errors import TokenExpiredError, TokenInvalidError +from authentication.models import CharacterOwnership + +import logging + +logger = logging.getLogger(__name__) + + +@app.task +def check_character_ownership(owner_hash): + tokens = Token.objects.filter(character_owner_hash=owner_hash) + if tokens: + for t in tokens: + old_hash = t.character_owner_hash + try: + t.update_token_data(commit=False) + except (TokenExpiredError, TokenInvalidError): + t.delete() + continue + + if t.character_owner_hash == old_hash: + break + else: + logger.info('Character %s has changed ownership. Revoking %s tokens.' % (t.character_name, tokens.count())) + tokens.delete() + else: + logger.info('No tokens found with owner hash %s. Revoking ownership.' % owner_hash) + CharacterOwnership.objects.filter(owner_hash=owner_hash).delete() + + +@app.task +def check_all_character_ownership(): + for c in CharacterOwnership.objects.all().only('owner_hash'): + check_character_ownership.delay(c.owner_hash) diff --git a/authentication/tests.py b/authentication/tests.py index 28c0f4eb..5b0727d0 100644 --- a/authentication/tests.py +++ b/authentication/tests.py @@ -12,6 +12,7 @@ from django.contrib.auth.models import User from alliance_auth.tests.auth_utils import AuthUtils from authentication.models import CharacterOwnership, UserProfile, State, get_guest_state from authentication.backends import StateBackend +from authentication.tasks import check_character_ownership from eveonline.models import EveCharacter, EveCorporationInfo, EveAllianceInfo from esi.models import Token @@ -136,6 +137,28 @@ class CharacterOwnershipTestCase(TestCase): self.user = User.objects.get(pk=self.user.pk) self.assertIsNone(self.user.profile.main_character) + @mock.patch('esi.models.Token.update_token_data') + def test_character_ownership_check(self, update_token_data): + t = Token.objects.create( + user=self.user, + character_id=self.character.character_id, + character_name=self.character.character_name, + character_owner_hash='1', + ) + co = CharacterOwnership.objects.get(owner_hash='1') + check_character_ownership(co.owner_hash) + self.assertTrue(CharacterOwnership.objects.filter(owner_hash='1').exists()) + + t.character_owner_hash = '2' + t.save() + check_character_ownership(co.owner_hash) + self.assertFalse(CharacterOwnership.objects.filter(owner_hash='1').exists()) + + t.delete() + co = CharacterOwnership.objects.create(user=self.user, character=self.character, owner_hash='3') + check_character_ownership(co.owner_hash) + self.assertFalse(CharacterOwnership.objects.filter(owner_hash='3').exists()) + class StateTestCase(TestCase): @classmethod @@ -269,4 +292,4 @@ class StateTestCase(TestCase): self.user.is_active = True self.user.save() self._refresh_user() - self.assertEquals(self.user.profile.state, self.member_state) \ No newline at end of file + self.assertEquals(self.user.profile.state, self.member_state) diff --git a/hrapplications/migrations/0002_make_strings_more_stringy.py b/hrapplications/migrations/0003_make_strings_more_stringy.py similarity index 96% rename from hrapplications/migrations/0002_make_strings_more_stringy.py rename to hrapplications/migrations/0003_make_strings_more_stringy.py index db7d274d..f7070d34 100644 --- a/hrapplications/migrations/0002_make_strings_more_stringy.py +++ b/hrapplications/migrations/0003_make_strings_more_stringy.py @@ -8,7 +8,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('hrapplications', '0001_initial'), + ('hrapplications', '0002_choices_for_questions'), ] operations = [ diff --git a/hrapplications/migrations/0003_sorted_questions.py b/hrapplications/migrations/0004_sorted_questions.py similarity index 90% rename from hrapplications/migrations/0003_sorted_questions.py rename to hrapplications/migrations/0004_sorted_questions.py index a45a8634..90588c75 100644 --- a/hrapplications/migrations/0003_sorted_questions.py +++ b/hrapplications/migrations/0004_sorted_questions.py @@ -10,7 +10,7 @@ from sortedm2m.operations import AlterSortedManyToManyField class Migration(migrations.Migration): dependencies = [ - ('hrapplications', '0002_make_strings_more_stringy'), + ('hrapplications', '0003_make_strings_more_stringy'), ] operations = [ diff --git a/hrapplications/migrations/0004_remove_legacy_models.py b/hrapplications/migrations/0005_remove_legacy_models.py similarity index 97% rename from hrapplications/migrations/0004_remove_legacy_models.py rename to hrapplications/migrations/0005_remove_legacy_models.py index a78b3c90..fba0fb3d 100644 --- a/hrapplications/migrations/0004_remove_legacy_models.py +++ b/hrapplications/migrations/0005_remove_legacy_models.py @@ -18,7 +18,7 @@ def delete_permissions(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ - ('hrapplications', '0003_sorted_questions'), + ('hrapplications', '0004_sorted_questions'), ] operations = [ diff --git a/services/migrations/0003_make_strings_more_stringy.py b/services/migrations/0003_make_strings_more_stringy.py deleted file mode 100644 index ef58eaa8..00000000 --- a/services/migrations/0003_make_strings_more_stringy.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.10.5 on 2017-03-22 23:35 -from __future__ import unicode_literals - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('services', '0002_auto_20161016_0135'), - ] - - operations = [ - migrations.AlterField( - model_name='groupcache', - name='service', - field=models.CharField(choices=[('discourse', 'discourse'), ('discord', 'discord')], max_length=254, unique=True), - ), - ] diff --git a/services/modules/discord/tasks.py b/services/modules/discord/tasks.py index b57f8f0b..880e5301 100644 --- a/services/modules/discord/tasks.py +++ b/services/modules/discord/tasks.py @@ -6,7 +6,6 @@ from alliance_auth.celeryapp import app from django.conf import settings from django.contrib.auth.models import User from django.core.exceptions import ObjectDoesNotExist - from notifications import notify from services.modules.discord.manager import DiscordOAuthManager, DiscordApiBackoff from .models import DiscordUser @@ -59,7 +58,6 @@ class DiscordTasks: @staticmethod @app.task(bind=True, name='discord.update_groups') - @only_one def update_groups(task_self, pk): user = User.objects.get(pk=pk) logger.debug("Updating discord groups for user %s" % user)