diff --git a/common/djangoapps/third_party_auth/management/commands/generate_and_store_apple_transfer_ids.py b/common/djangoapps/third_party_auth/management/commands/generate_and_store_apple_transfer_ids.py index d367b30ae2..00177ec76c 100644 --- a/common/djangoapps/third_party_auth/management/commands/generate_and_store_apple_transfer_ids.py +++ b/common/djangoapps/third_party_auth/management/commands/generate_and_store_apple_transfer_ids.py @@ -21,6 +21,8 @@ from common.djangoapps.third_party_auth.appleid import AppleIdAuth log = logging.getLogger(__name__) +INVALID_GRANT_ERROR = "invalid_grant" + class AccessTokenExpiredException(Exception): """ @@ -28,6 +30,12 @@ class AccessTokenExpiredException(Exception): """ +class BadRequestException(Exception): + """ + Raised when access token has been expired. + """ + + class Command(BaseCommand): """ Management command to generate transfer identifiers for apple users using their apple_id @@ -115,7 +123,11 @@ class Command(BaseCommand): } response = requests.post(migration_url, data=payload, headers=headers) if response.status_code == 400: - raise AccessTokenExpiredException + error = response.json().get('error') + log.info("Error while fetching transfer_id for uid %s. Error: %s", apple_id, error) + if error == INVALID_GRANT_ERROR: + raise AccessTokenExpiredException + raise BadRequestException return response.json().get('transfer_sub') @@ -123,6 +135,9 @@ class Command(BaseCommand): """ Given an Apple ID from the old transferring team, create and return its respective transfer id. + Update access token if expired. + Skip the current apple_id in case of a malformed + request error and log info. """ try: transfer_id = self._fetch_transfer_id(apple_id, target_team_id) @@ -130,12 +145,15 @@ class Command(BaseCommand): log.info('Access token expired. Re-creating access token.') self._update_token_and_secret() transfer_id = self._fetch_transfer_id(apple_id, target_team_id) + except BadRequestException: + log.info('Bad request for uid %s.', apple_id) + transfer_id = '' + return transfer_id def add_arguments(self, parser): parser.add_argument('target_team_id', help='Team ID to which the app is to be migrated to.') - @transaction.atomic def handle(self, *args, **options): target_team_id = options['target_team_id'] @@ -148,11 +166,13 @@ class Command(BaseCommand): apple_ids = UserSocialAuth.objects.filter(provider=AppleIdAuth.name).exclude( uid__in=already_processed_apple_ids).values_list('uid', flat=True) for apple_id in apple_ids: + log.info("Begin processing uid %s", apple_id) transfer_id = self._get_transfer_id_for_apple_id(apple_id, target_team_id) if transfer_id: - apple_user_id_info, _ = AppleMigrationUserIdInfo.objects.get_or_create(old_apple_id=apple_id) - apple_user_id_info.transfer_id = transfer_id - apple_user_id_info.save() - log.info('Updated transfer_id for uid %s', apple_id) + with transaction.atomic(): + apple_user_id_info, _ = AppleMigrationUserIdInfo.objects.get_or_create(old_apple_id=apple_id) + apple_user_id_info.transfer_id = transfer_id + apple_user_id_info.save() + log.info('Updated transfer_id for uid %s', apple_id) else: - log.info('Unable to fetch transfer_id for uid %s', apple_id) + log.info('Unable to fetch transfer_id for uid %s. Skipping.', apple_id) diff --git a/common/djangoapps/third_party_auth/management/commands/generate_and_store_new_apple_ids.py b/common/djangoapps/third_party_auth/management/commands/generate_and_store_new_apple_ids.py index 1acb475d0e..94882d1803 100644 --- a/common/djangoapps/third_party_auth/management/commands/generate_and_store_new_apple_ids.py +++ b/common/djangoapps/third_party_auth/management/commands/generate_and_store_new_apple_ids.py @@ -19,6 +19,8 @@ from common.djangoapps.third_party_auth.appleid import AppleIdAuth log = logging.getLogger(__name__) +INVALID_GRANT_ERROR = "invalid_grant" + class AccessTokenExpiredException(Exception): """ @@ -26,13 +28,19 @@ class AccessTokenExpiredException(Exception): """ +class BadRequestException(Exception): + """ + Raised when access token has been expired. + """ + + class Command(BaseCommand): """ Management command to exchange transfer identifiers for new team-scoped identifier for the user in new migrated team. Usage: - manage.py generate_and_store_apple_transfer_ids + manage.py generate_and_store_new_apple_ids """ def _generate_client_secret(self): @@ -112,7 +120,11 @@ class Command(BaseCommand): } response = requests.post(migration_url, data=payload, headers=headers) if response.status_code == 400: - raise AccessTokenExpiredException + error = response.json().get('error') + log.info("Error while fetching apple_id for transfer_id %s. Error: %s", transfer_id, error) + if error == INVALID_GRANT_ERROR: + raise AccessTokenExpiredException + raise BadRequestException return response.json().get('sub') @@ -120,6 +132,9 @@ class Command(BaseCommand): """ For a Transfer ID obtained from the transferring team, return the correlating Apple ID belonging to the recipient team. + Update access token if expired. + Skip the current transfer_id in case of a malformed request + error and log info. """ try: new_apple_id = self._fetch_new_apple_id(transfer_id) @@ -127,10 +142,12 @@ class Command(BaseCommand): log.info('Access token expired. Re-creating access token.') self._update_token_and_secret() new_apple_id = self._fetch_new_apple_id(transfer_id) + except BadRequestException: + log.info('Bad request for transfer_id %s.', transfer_id) + new_apple_id = '' return new_apple_id - @transaction.atomic def handle(self, *args, **options): self._update_token_and_secret() if not self.access_token: @@ -139,12 +156,14 @@ class Command(BaseCommand): apple_user_ids_info = AppleMigrationUserIdInfo.objects.filter(Q(new_apple_id__isnull=True) | Q(new_apple_id=""), ~Q(transfer_id=""), transfer_id__isnull=False) for apple_user_id_info in apple_user_ids_info: - new_apple_id = self._exchange_transfer_id_for_new_apple_id(apple_user_id_info.transfer_id) + transfer_id = apple_user_id_info.transfer_id + old_apple_id = apple_user_id_info.old_apple_id + log.info("Begin processing old_apple_id %s, transfer_id %s", old_apple_id, transfer_id) + new_apple_id = self._exchange_transfer_id_for_new_apple_id(transfer_id) if new_apple_id: - apple_user_id_info.new_apple_id = new_apple_id - apple_user_id_info.save() - log.info('Updated new Apple ID for uid %s', - apple_user_id_info.old_apple_id) + with transaction.atomic(): + apple_user_id_info.new_apple_id = new_apple_id + apple_user_id_info.save() + log.info('Updated new Apple ID for uid %s', old_apple_id) else: - log.info('Unable to fetch new Apple ID for uid %s', - apple_user_id_info.old_apple_id) + log.info('Unable to fetch new Apple ID for uid %s', old_apple_id) diff --git a/common/djangoapps/third_party_auth/management/commands/update_new_apple_ids_in_social_auth.py b/common/djangoapps/third_party_auth/management/commands/update_new_apple_ids_in_social_auth.py index f3314efb1a..cd804e6e42 100644 --- a/common/djangoapps/third_party_auth/management/commands/update_new_apple_ids_in_social_auth.py +++ b/common/djangoapps/third_party_auth/management/commands/update_new_apple_ids_in_social_auth.py @@ -23,7 +23,6 @@ class Command(BaseCommand): manage.py update_new_apple_ids_in_social_auth """ - @transaction.atomic def handle(self, *args, **options): apple_user_ids_info = AppleMigrationUserIdInfo.objects.filter( ~Q(new_apple_id=''), new_apple_id__isnull=False @@ -35,10 +34,11 @@ class Command(BaseCommand): uid=apple_user_id_info.old_apple_id, provider=AppleIdAuth.name ).first() if user_social_auth: - user_social_auth.uid = apple_user_id_info.new_apple_id - user_social_auth.save() - log.info( - 'Replaced Apple ID %s with %s', - apple_user_id_info.old_apple_id, - apple_user_id_info.new_apple_id - ) + with transaction.atomic(): + user_social_auth.uid = apple_user_id_info.new_apple_id + user_social_auth.save() + log.info( + 'Replaced Apple ID %s with %s', + apple_user_id_info.old_apple_id, + apple_user_id_info.new_apple_id + )