def test_coauthor_api_key_in_submission_is_found(self): coauthor = user_factory() AddonUser.objects.create(addon=self.addon, user_id=coauthor.id) upload = self.get_upload(abspath=self.file, with_validation=False, addon=self.addon, user=coauthor) tasks.validate(upload, listed=True) upload.refresh_from_db() assert upload.processed_validation['errors'] == 1 messages = upload.processed_validation['messages'] assert len(messages) == 1 assert messages[0]['id'] == [ 'validation', 'messages', 'api_key_detected' ] assert ('The developer API key of a coauthor was found in the ' 'submitted file.' in messages[0]['message']) assert not upload.valid # If the key has been revoked, there is no active key, # so `get_jwt_key` raises `DoesNotExist`. with pytest.raises(APIKey.DoesNotExist): APIKey.get_jwt_key(user_id=self.user.id) assert len(mail.outbox) == 1 assert 'Your AMO API credentials have been revoked' in mail.outbox[ 0].subject assert 'never share your credentials' in mail.outbox[0].body # We submit as the coauthor, the leaked key is the one from 'self.user' assert mail.outbox[0].to[0] == self.user.email
def test_api_key_in_submission_is_found(self): upload = FileUpload.objects.create(path=self.file, addon=self.addon, user=self.user) tasks.validate(upload, listed=True) upload.refresh_from_db() assert upload.processed_validation['errors'] == 1 messages = upload.processed_validation['messages'] assert len(messages) == 1 assert messages[0]['id'] == [ u'validation', u'messages', u'api_key_detected' ] assert ('Your developer API key was found in the submitted ' 'file.' in messages[0]['message']) assert not upload.valid # If the key has been revoked, there is no active key, # so `get_jwt_key` raises `DoesNotExist`. with pytest.raises(APIKey.DoesNotExist): APIKey.get_jwt_key(user_id=self.user.id) assert len(mail.outbox) == 1 assert ('Your AMO API credentials have been revoked' in mail.outbox[0].subject) assert ('never share your credentials' in mail.outbox[0].body) assert mail.outbox[0].to[0] == self.user.email
def test_api_key_already_revoked_by_developer(self): self.key.update(is_active=None) tasks.revoke_api_key(self.key.id) # If the key has already been revoked, there is no active key, # so `get_jwt_key` raises `DoesNotExist`. with pytest.raises(APIKey.DoesNotExist): APIKey.get_jwt_key(user_id=self.user.id)
def test_coauthor_api_key_in_submission_is_found(self): coauthor = user_factory() AddonUser.objects.create(addon=self.addon, user_id=coauthor.id) upload = FileUpload.objects.create(path=self.file, addon=self.addon, user=coauthor) tasks.validate(upload, listed=True) upload.refresh_from_db() assert upload.processed_validation['errors'] == 1 messages = upload.processed_validation['messages'] assert len(messages) == 1 assert messages[0]['id'] == [ u'validation', u'messages', u'api_key_detected'] assert ('The developer API key of a coauthor was found in the ' 'submitted file.' in messages[0]['message']) assert not upload.valid # If the key has been revoked, there is no active key, # so `get_jwt_key` raises `DoesNotExist`. with pytest.raises(APIKey.DoesNotExist): APIKey.get_jwt_key(user_id=self.user.id) assert len(mail.outbox) == 1 assert ('Your AMO API credentials have been revoked' in mail.outbox[0].subject) assert ('never share your credentials' in mail.outbox[0].body) # We submit as the coauthor, the leaked key is the one from 'self.user' assert mail.outbox[0].to[0] == self.user.email
def revoke_api_key(key_id): try: # Fetch the original key, do not use `get_jwt_key` # so we get access to a user object for logging later. original_key = APIKey.objects.get(type=SYMMETRIC_JWT_TYPE, id=key_id) # Fetch the current key to compare to the original, # throws if the key has been revoked, which also means # `original_key` is not active. current_key = APIKey.get_jwt_key(user_id=original_key.user.id) if current_key.key != original_key.key: log.info( 'User %s has already regenerated the key, nothing to be ' 'done.' % original_key.user ) else: with transaction.atomic(): log.info('Revoking key for user %s.' % current_key.user) current_key.update(is_active=None) send_api_key_revocation_email(emails=[current_key.user.email]) except APIKey.DoesNotExist: log.info( 'User %s has already revoked the key, nothing to be done.' % original_key.user ) pass
def test_api_key_already_regenerated_by_developer(self): self.key.update(is_active=None) current_key = APIKey.new_jwt_credentials(user=self.user) tasks.revoke_api_key(self.key.id) key_from_db = APIKey.get_jwt_key(user_id=self.user.id) assert current_key.key == key_from_db.key assert current_key.secret == key_from_db.secret
def check_for_api_keys_in_file(results, upload_pk): upload = FileUpload.objects.get(pk=upload_pk) if upload.addon: users = upload.addon.authors.all() else: users = [upload.user] if upload.user else [] keys = [] for user in users: try: key = APIKey.get_jwt_key(user_id=user.id) keys.append(key) except APIKey.DoesNotExist: pass try: if len(keys) > 0: zipfile = SafeZip(source=upload.path) for zipinfo in zipfile.info_list: if zipinfo.file_size >= 64: file_ = zipfile.read(zipinfo) for key in keys: if key.secret in file_.decode(errors='ignore'): log.info('Developer API key for user %s found in ' 'submission.' % key.user) if key.user == upload.user: msg = gettext('Your developer API key was ' 'found in the submitted file. ' 'To protect your account, the ' 'key will be revoked.') else: msg = gettext('The developer API key of a ' 'coauthor was found in the ' 'submitted file. To protect ' 'your add-on, the key will be ' 'revoked.') annotations.insert_validation_message( results, type_='error', message=msg, msg_id='api_key_detected', compatibility_type=None, ) # Revoke after 2 minutes to allow the developer to # fetch the validation results revoke_api_key.apply_async( kwargs={'key_id': key.id}, countdown=120) zipfile.close() except (ValidationError, BadZipFile, IOError): pass return results
def test_api_key_in_new_submission_is_found(self): upload = FileUpload.objects.create(path=self.file, user=self.user) tasks.validate(upload, listed=True) upload.refresh_from_db() assert upload.processed_validation['errors'] == 1 messages = upload.processed_validation['messages'] assert len(messages) == 1 assert messages[0]['id'] == [ u'validation', u'messages', u'api_key_detected'] assert ('Your developer API key was found in the submitted ' 'file.' in messages[0]['message']) assert not upload.valid # If the key has been revoked, there is no active key, # so `get_jwt_key` raises `DoesNotExist`. with pytest.raises(APIKey.DoesNotExist): APIKey.get_jwt_key(user_id=self.user.id) assert len(mail.outbox) == 1 assert ('Your AMO API credentials have been revoked' in mail.outbox[0].subject) assert mail.outbox[0].to[0] == self.user.email
def check_for_api_keys_in_file(results, upload): if upload.addon: users = upload.addon.authors.all() else: users = [upload.user] if upload.user else [] keys = [] for user in users: try: key = APIKey.get_jwt_key(user_id=user.id) keys.append(key) except APIKey.DoesNotExist: pass if len(keys) > 0: zipfile = SafeZip(source=upload.path) zipfile.is_valid() for zipinfo in zipfile.info_list: if zipinfo.file_size >= 64: file_ = zipfile.read(zipinfo) for key in keys: if key.secret in file_.decode(encoding='unicode-escape', errors="ignore"): log.info('Developer API key for user %s found in ' 'submission.' % key.user) if key.user == upload.user: msg = ugettext('Your developer API key was found ' 'in the submitted file. To protect ' 'your account, the key will be ' 'revoked.') else: msg = ugettext('The developer API key of a ' 'coauthor was found in the ' 'submitted file. To protect your ' 'add-on, the key will be revoked.') insert_validation_message( results, type_='error', message=msg, msg_id='api_key_detected', compatibility_type=None) # Revoke after 2 minutes to allow the developer to # fetch the validation results revoke_api_key.apply_async( kwargs={'key_id': key.id}, countdown=120) zipfile.close() return results
def authenticate_credentials(self, payload): """ Returns a verified AMO user who is active and allowed to make API requests. """ try: api_key = APIKey.get_jwt_key(key=payload['iss']) except APIKey.DoesNotExist: msg = 'Invalid API Key.' raise exceptions.AuthenticationFailed(msg) if api_key.user.deleted: msg = 'User account is disabled.' raise exceptions.AuthenticationFailed(msg) if not api_key.user.read_dev_agreement: msg = 'User has not read developer agreement.' raise exceptions.AuthenticationFailed(msg) return api_key.user
def revoke_api_key(key_id): try: # Fetch the original key, do not use `get_jwt_key` # so we get access to a user object for logging later. original_key = APIKey.objects.get( type=SYMMETRIC_JWT_TYPE, id=key_id) # Fetch the current key to compare to the original, # throws if the key has been revoked, which also means # `original_key` is not active. current_key = APIKey.get_jwt_key(user_id=original_key.user.id) if current_key.key != original_key.key: log.info('User %s has already regenerated the key, nothing to be ' 'done.' % original_key.user) else: with transaction.atomic(): log.info('Revoking key for user %s.' % current_key.user) current_key.update(is_active=None) send_api_key_revocation_email(emails=[current_key.user.email]) except APIKey.DoesNotExist: log.info('User %s has already revoked the key, nothing to be done.' % original_key.user) pass
def authenticate_credentials(self, payload): """ Returns a verified AMO user who is active and allowed to make API requests. """ if 'orig_iat' in payload: msg = ("API key based tokens are not refreshable, don't include " '`orig_iat` in their payload.') raise exceptions.AuthenticationFailed(msg) try: api_key = APIKey.get_jwt_key(key=payload['iss']) except APIKey.DoesNotExist: msg = 'Invalid API Key.' raise exceptions.AuthenticationFailed(msg) if api_key.user.deleted: msg = 'User account is disabled.' raise exceptions.AuthenticationFailed(msg) if not api_key.user.read_dev_agreement: msg = 'User has not read developer agreement.' raise exceptions.AuthenticationFailed(msg) core.set_user(api_key.user) return api_key.user
def authenticate_credentials(self, payload): """ Returns a verified AMO user who is active and allowed to make API requests. """ if 'orig_iat' in payload: msg = ("API key based tokens are not refreshable, don't include " "`orig_iat` in their payload.") raise exceptions.AuthenticationFailed(msg) try: api_key = APIKey.get_jwt_key(key=payload['iss']) except APIKey.DoesNotExist: msg = 'Invalid API Key.' raise exceptions.AuthenticationFailed(msg) if api_key.user.deleted: msg = 'User account is disabled.' raise exceptions.AuthenticationFailed(msg) if not api_key.user.read_dev_agreement: msg = 'User has not read developer agreement.' raise exceptions.AuthenticationFailed(msg) amo.set_user(api_key.user) return api_key.user