Coverage for sm/management/commands/cleanup_api_keys.py: 100%

30 statements  

« prev     ^ index     » next       coverage.py v7.17.0, created at 2026-10-24 19:06 +0000

1from datetime import timedelta 

2 

3from django.core.management.base import BaseCommand 

4from django.utils import timezone 

5 

6from sm.models import ApiKey 

7 

8 

9class Command(BaseCommand): 

10 help = ( 

11 "Revoke API keys that have expired or have been inactive for too long. " 

12 "Use --inactive-days to control the inactivity threshold (default 90)." 

13 ) 

14 

15 def add_arguments(self, parser): 

16 parser.add_argument( 

17 "--inactive-days", 

18 type=int, 

19 default=90, 

20 help="Revoke keys unused for this many days (default: 90).", 

21 ) 

22 parser.add_argument( 

23 "--dry-run", 

24 action="store_true", 

25 help="Report what would be revoked without changing anything.", 

26 ) 

27 

28 def handle(self, *args, **options): 

29 inactive_days = options["inactive_days"] 

30 dry_run = options["dry_run"] 

31 now = timezone.now() 

32 

33 expired = ApiKey.objects.filter( 

34 is_active=True, expires_at__isnull=False, expires_at__lte=now 

35 ) 

36 inactive = ApiKey.objects.filter( 

37 is_active=True, 

38 expires_at__isnull=True, 

39 last_used_at__isnull=False, 

40 last_used_at__lte=now - timedelta(days=inactive_days), 

41 ) 

42 

43 expired_count = expired.count() 

44 inactive_count = inactive.count() 

45 self.stdout.write( 

46 self.style.WARNING( 

47 "Found %d expired and %d inactive API key(s)." 

48 % (expired_count, inactive_count) 

49 ) 

50 ) 

51 

52 if dry_run: 

53 self.stdout.write("Dry run - nothing was revoked.") 

54 return 

55 

56 for key in expired: 

57 key.is_active = False 

58 key.revoked_at = now 

59 key.save(update_fields=["is_active", "revoked_at"]) 

60 for key in inactive: 

61 key.is_active = False 

62 key.revoked_at = now 

63 key.save(update_fields=["is_active", "revoked_at"]) 

64 

65 self.stdout.write( 

66 self.style.SUCCESS( 

67 "Revoked %d expired and %d inactive API key(s)." 

68 % (expired_count, inactive_count) 

69 ) 

70 )