def check_xpi_info(xpi_info, addon=None, xpi_file=None): from olympia.addons.models import Addon, DeniedGuid guid = xpi_info['guid'] is_webextension = xpi_info.get('is_webextension', False) # If we allow the guid to be omitted we assume that one was generated # or existed before and use that one. # An example are WebExtensions that don't require a guid but we generate # one once they're uploaded. Now, if you update that WebExtension we # just use the original guid. if addon and not guid and is_webextension: xpi_info['guid'] = guid = addon.guid if not guid and not is_webextension: raise forms.ValidationError(ugettext('Could not find an add-on ID.')) if guid: current_user = core.get_user() if current_user: deleted_guid_clashes = Addon.unfiltered.exclude( authors__id=current_user.id).filter(guid=guid) else: deleted_guid_clashes = Addon.unfiltered.filter(guid=guid) if addon and addon.guid != guid: msg = ugettext( 'The add-on ID in your manifest.json or install.rdf (%s) ' 'does not match the ID of your add-on on AMO (%s)') raise forms.ValidationError(msg % (guid, addon.guid)) if (not addon and # Non-deleted add-ons. ( Addon.objects.filter(guid=guid).exists() or # DeniedGuid objects for legacy deletions. DeniedGuid.objects.filter(guid=guid).exists() or # Deleted add-ons that don't belong to the uploader. deleted_guid_clashes.exists())): raise forms.ValidationError(ugettext('Duplicate add-on ID found.')) if len(xpi_info['version']) > 32: raise forms.ValidationError( ugettext('Version numbers should have fewer than 32 characters.')) if not VERSION_RE.match(xpi_info['version']): raise forms.ValidationError( ugettext('Version numbers should only contain letters, numbers, ' 'and these punctuation characters: +*.-_.')) if is_webextension and xpi_info.get('type') == amo.ADDON_STATICTHEME: if not waffle.switch_is_active('allow-static-theme-uploads'): raise forms.ValidationError( ugettext( 'WebExtension theme uploads are currently not supported.')) if xpi_file: # Make sure we pass in a copy of `xpi_info` since # `resolve_webext_translations` modifies data in-place translations = Addon.resolve_webext_translations( xpi_info.copy(), xpi_file) verify_mozilla_trademark(translations['name'], core.get_user()) return xpi_info
def test_set_task_user(self): @decorators.set_task_user def some_func(): return core.get_user() core.set_user(UserProfile.objects.get(username='******')) assert core.get_user().pk == 999 assert some_func().pk == int(settings.TASK_USER_ID) assert core.get_user().pk == 999
def test_set_get_user_anonymous(): core.set_user(AnonymousUser()) assert core.get_user() is None user = UserProfile() core.set_user(user) assert core.get_user() == user core.set_user(None) assert core.get_user() is None
def ban_and_disable_related_content_bulk(cls, users, move_files=False): """Admin method to ban users and disable the content they produced. Similar to deletion, except that the content produced by the user is forcibly disabled instead of being deleted where possible, and the user is not fully anonymized: we keep their fxa_id and email so that they are never able to log back in. """ from olympia.addons.models import Addon, AddonUser from olympia.addons.tasks import index_addons from olympia.bandwagon.models import Collection from olympia.files.models import File from olympia.ratings.models import Rating # collect affected addons addon_ids = set( Addon.unfiltered.exclude(status=amo.STATUS_DELETED).filter( addonuser__user__in=users).values_list('id', flat=True)) # First addons who have other authors we aren't banning addon_joint_ids = set( AddonUser.objects.filter(addon_id__in=addon_ids).exclude( user__in=users).values_list('addon_id', flat=True)) AddonUser.objects.filter(user__in=users, addon_id__in=addon_joint_ids).delete() # Then deal with users who are the sole author addons_sole = Addon.unfiltered.filter(id__in=addon_ids - addon_joint_ids) # set the status to disabled - using the manager update() method addons_sole.update(status=amo.STATUS_DISABLED) # collect Files that need to be disabled now the addons are disabled files_to_disable = File.objects.filter(version__addon__in=addons_sole) files_to_disable.update(status=amo.STATUS_DISABLED) if move_files: # if necessary move the files out of the CDN (expensive operation) for file_ in files_to_disable: file_.hide_disabled_file() # Finally run Addon.force_disable to add the logging; update versions # Status was already DISABLED so shouldn't fire watch_disabled again. for addon in addons_sole: addon.force_disable() # Don't pass a set to a .delay - sets can't be serialized as JSON index_addons.delay(list(addon_ids - addon_joint_ids)) # delete the other content associated with the user Collection.objects.filter(author__in=users).delete() Rating.objects.filter(user__in=users).delete( user_responsible=core.get_user()) # And then delete the users. for user in users: log.info(f'User ({user}: <{user.email}>) is being ' 'anonymized and banned.') user.banned = user.modified = datetime.now() user.deleted = True cls.anonymize_users(users) cls.objects.bulk_update(users, fields=('banned', 'deleted', 'modified') + cls.ANONYMIZED_FIELDS)
def new_theme_version_with_69_properties(old_version): timer = StopWatch( 'addons.tasks.repack_themes_for_69.new_theme_version.') timer.start() author = get_user() # Wrap zip in FileUpload for Version from_upload to consume. upload = FileUpload.objects.create(user=author, valid=True) filename = uuid.uuid4().hex + '.xpi' destination = os.path.join(user_media_path('addons'), 'temp', filename) old_xpi = get_filepath(old_version.all_files[0]) build_69_compatible_theme( old_xpi, destination, get_next_version_number(old_version.addon)) upload.update(path=destination, name=filename) timer.log_interval('1.build_xpi') # Create addon + version parsed_data = parse_addon(upload, addon=old_version.addon, user=author) timer.log_interval('2.parse_addon') version = Version.from_upload( upload, old_version.addon, selected_apps=[amo.FIREFOX.id], channel=amo.RELEASE_CHANNEL_LISTED, parsed_data=parsed_data) timer.log_interval('3.initialize_version') # And finally sign the files (actually just one) for file_ in version.all_files: sign_file(file_) file_.update( reviewed=datetime.now(), status=amo.STATUS_APPROVED) timer.log_interval('4.sign_files') return version
def extract_theme_properties(addon, channel): version = addon.find_latest_version(channel) if not version or not version.all_files: return {} try: parsed_data = parse_xpi( version.all_files[0].file_path, addon=addon, user=core.get_user()) except ValidationError: # If we can't parse the existing manifest safely return. return {} theme_props = parsed_data.get('theme', {}) # pre-process colors to convert chrome style colors and strip spaces theme_props['colors'] = dict( process_color_value(prop, color) for prop, color in theme_props.get('colors', {}).items()) # replace headerURL with path to existing background if 'images' in theme_props: if 'theme_frame' in theme_props['images']: header_url = theme_props['images'].pop('theme_frame') if 'headerURL' in theme_props['images']: header_url = theme_props['images'].pop('headerURL') if header_url: theme_props['images']['headerURL'] = '/'.join(( user_media_url('addons'), text_type(addon.id), text_type(version.id), header_url)) return theme_props
def create(cls, action, *args, **kw): """ e.g. ActivityLog.create(amo.LOG.CREATE_ADDON, addon), ActivityLog.create(amo.LOG.ADD_FILE_TO_VERSION, file, version) In case of circular import you can use `olympia.activity.log_create()` """ from olympia import core user = kw.get('user', core.get_user()) if not user: log.warning('Activity log called with no user: %s' % action.id) return # We make sure that we take the timestamp if provided, instead of # creating a new one, especially useful for log entries created # in a loop. al = ActivityLog( user=user, action=action.id, created=kw.get('created', timezone.now())) al.arguments = args if 'details' in kw: al.details = kw['details'] al.save() if 'details' in kw and 'comments' in al.details: CommentLog.objects.create( comments=al.details['comments'], activity_log=al, created=kw.get('created', timezone.now())) for arg in args: if isinstance(arg, tuple): class_ = arg[0] id_ = arg[1] else: class_ = arg.__class__ id_ = arg.id if isinstance(arg, ModelBase) else None if class_ == Addon: AddonLog.objects.create( addon_id=id_, activity_log=al, created=kw.get('created', timezone.now())) elif class_ == Version: VersionLog.objects.create( version_id=id_, activity_log=al, created=kw.get('created', timezone.now())) elif class_ == UserProfile: UserLog.objects.create( user_id=id_, activity_log=al, created=kw.get('created', timezone.now())) elif class_ == Group: GroupLog.objects.create( group_id=id_, activity_log=al, created=kw.get('created', timezone.now())) # Index by every user UserLog.objects.create( activity_log=al, user=user, created=kw.get('created', timezone.now())) return al
def wrapper(*args, **kw): old_user = core.get_user() core.set_user(get_task_user()) try: result = f(*args, **kw) finally: core.set_user(old_user) return result
def process(self, msg, kwargs): kwargs.setdefault('extra', {}).update({ 'REMOTE_ADDR': core.get_remote_addr() or '', 'USERNAME': getattr(core.get_user(), 'username', None) or '<anon>', }) return msg, kwargs
def check_xpi_info(xpi_info, addon=None): from olympia.addons.models import Addon, DeniedGuid guid = xpi_info['guid'] is_webextension = xpi_info.get('is_webextension', False) # If we allow the guid to be omitted we assume that one was generated # or existed before and use that one. # An example are WebExtensions that don't require a guid but we generate # one once they're uploaded. Now, if you update that WebExtension we # just use the original guid. if addon and not guid and is_webextension: xpi_info['guid'] = guid = addon.guid if not guid and not is_webextension: raise forms.ValidationError(ugettext('Could not find an add-on ID.')) if guid: current_user = core.get_user() if current_user: deleted_guid_clashes = Addon.unfiltered.exclude( authors__id=current_user.id).filter(guid=guid) else: deleted_guid_clashes = Addon.unfiltered.filter(guid=guid) guid_too_long = ( not waffle.switch_is_active('allow-long-addon-guid') and len(guid) > 64 ) if guid_too_long: raise forms.ValidationError( ugettext('Add-on ID must be 64 characters or less.')) if addon and addon.guid != guid: msg = ugettext( 'The add-on ID in your manifest.json or install.rdf (%s) ' 'does not match the ID of your add-on on AMO (%s)') raise forms.ValidationError(msg % (guid, addon.guid)) if (not addon and # Non-deleted add-ons. (Addon.objects.filter(guid=guid).exists() or # DeniedGuid objects for legacy deletions. DeniedGuid.objects.filter(guid=guid).exists() or # Deleted add-ons that don't belong to the uploader. deleted_guid_clashes.exists())): raise forms.ValidationError(ugettext('Duplicate add-on ID found.')) if len(xpi_info['version']) > 32: raise forms.ValidationError( ugettext('Version numbers should have fewer than 32 characters.')) if not VERSION_RE.match(xpi_info['version']): raise forms.ValidationError( ugettext('Version numbers should only contain letters, numbers, ' 'and these punctuation characters: +*.-_.')) if is_webextension and xpi_info.get('is_static_theme', False): if not waffle.switch_is_active('allow-static-theme-uploads'): raise forms.ValidationError(ugettext( 'WebExtension theme uploads are currently not supported.')) return xpi_info
def _delete_related_content(self, *, addon_msg=''): """Delete content produced by this user if they are the only author.""" self.collections.all().delete() for addon in self.addons.all().iterator(): if not addon.authors.exclude(pk=self.pk).exists(): addon.delete(msg=addon_msg) else: addon.addonuser_set.get(user=self).delete() user_responsible = core.get_user() self._ratings_all.all().delete(user_responsible=user_responsible)
def check_xpi_info(xpi_info, addon=None): from olympia.addons.models import Addon, DeniedGuid guid = xpi_info['guid'] is_webextension = xpi_info.get('is_webextension', False) # If we allow the guid to be omitted we assume that one was generated # or existed before and use that one. # An example are WebExtensions that don't require a guid but we generate # one once they're uploaded. Now, if you update that WebExtension we # just use the original guid. if addon and not guid and is_webextension: xpi_info['guid'] = guid = addon.guid if not guid and not is_webextension: raise forms.ValidationError(_("Could not find an add-on ID.")) if guid: current_user = core.get_user() if current_user: deleted_guid_clashes = Addon.unfiltered.exclude( authors__id=current_user.id).filter(guid=guid) else: deleted_guid_clashes = Addon.unfiltered.filter(guid=guid) guid_too_long = (not waffle.switch_is_active('allow-long-addon-guid') and len(guid) > 64) if guid_too_long: raise forms.ValidationError( _("Add-on ID must be 64 characters or less.")) if addon and addon.guid != guid: msg = _("The add-on ID in your manifest.json or install.rdf (%s) " "does not match the ID of your add-on on AMO (%s)") raise forms.ValidationError(msg % (guid, addon.guid)) if (not addon and # Non-deleted add-ons. ( Addon.objects.filter(guid=guid).exists() or # DeniedGuid objects for legacy deletions. DeniedGuid.objects.filter(guid=guid).exists() or # Deleted add-ons that don't belong to the uploader. deleted_guid_clashes.exists())): raise forms.ValidationError(_('Duplicate add-on ID found.')) if len(xpi_info['version']) > 32: raise forms.ValidationError( _('Version numbers should have fewer than 32 characters.')) if not VERSION_RE.match(xpi_info['version']): raise forms.ValidationError( _('Version numbers should only contain letters, numbers, ' 'and these punctuation characters: +*.-_.')) if is_webextension and xpi_info.get('is_static_theme', False): if not waffle.switch_is_active('allow-static-theme-uploads'): raise forms.ValidationError( _('WebExtension theme uploads are currently not supported.')) return xpi_info
def delete_or_disable_related_content(self, delete=False): """Delete or disable content produced by this user if they are the only author.""" self.collections.all().delete() for addon in self.addons.all().iterator(): if not addon.authors.exclude(pk=self.pk).exists(): if delete: addon.delete() else: addon.force_disable() user_responsible = core.get_user() self._ratings_all.all().delete(user_responsible=user_responsible) self.delete_picture()
def start_validation(request): # FIXME: `@transaction.non_atomic_requests` is a workaround for an issue # that might exist elsewhere too. The view is wrapped in a transaction # by default and because of that tasks being started in this view # won't see the `ValidationJob` object created. form = BulkValidationForm(request.POST) if form.is_valid(): job = form.save(commit=False) job.creator = core.get_user() job.save() find_files(job) return redirect(reverse('zadmin.validation')) else: return validation(request, form=form)
def delete_or_disable_related_content(self, delete=False): """Delete or disable content produced by this user if they are the only author.""" self.collections.all().delete() for addon in self.addons.all().iterator(): if not addon.authors.exclude(pk=self.pk).exists(): if delete: addon.delete() else: addon.force_disable() else: addon.addonuser_set.filter(user=self).delete() user_responsible = core.get_user() self._ratings_all.all().delete(user_responsible=user_responsible) self.delete_picture()
def extract_theme_properties(addon, channel): version = addon.find_latest_version(channel) if not version or not version.all_files: return {} try: parsed_data = parse_xpi( version.all_files[0].file_path, addon=addon, user=core.get_user()) except ValidationError: # If we can't parse the existing manifest safely return. return {} theme_props = parsed_data.get('theme', {}) # pre-process colors to convert chrome style colors and strip spaces theme_props['colors'] = dict( process_color_value(prop, color) for prop, color in theme_props.get('colors', {}).items()) return theme_props
def ban_and_disable_related_content_bulk(cls, users, move_files=False): """Like ban_and_disable_related_content, but in bulk. """ from olympia.addons.models import Addon, AddonUser from olympia.addons.tasks import index_addons from olympia.bandwagon.models import Collection from olympia.files.models import File from olympia.ratings.models import Rating # collect affected addons addon_ids = set( Addon.unfiltered.exclude(status=amo.STATUS_DELETED).filter( addonuser__user__in=users).values_list('id', flat=True)) # First addons who have other authors we aren't banning addon_joint_ids = set( AddonUser.objects.filter(addon_id__in=addon_ids).exclude( user__in=users).values_list('addon_id', flat=True)) AddonUser.objects.filter(user__in=users, addon_id__in=addon_joint_ids).delete() # Then deal with users who are the sole author addons_sole = Addon.unfiltered.filter(id__in=addon_ids - addon_joint_ids) # set the status to disabled - using the manager update() method addons_sole.update(status=amo.STATUS_DISABLED) # collect Files that need to be disabled now the addons are disabled files_to_disable = File.objects.filter(version__addon__in=addons_sole) files_to_disable.update(status=amo.STATUS_DISABLED) if move_files: # if necessary move the files out of the CDN (expensive operation) for file_ in files_to_disable: file_.hide_disabled_file() # Finally run Addon.force_disable to add the logging; update versions # Status was already DISABLED so shouldn't fire watch_disabled again. for addon in addons_sole: addon.force_disable() # Don't pass a set to a .delay - sets can't be serialized as JSON index_addons.delay(list(addon_ids - addon_joint_ids)) # delete the other content associated with the user Collection.objects.filter(author__in=users).delete() Rating.objects.filter(user__in=users).delete( user_responsible=core.get_user()) # And then delete the users. for user in users: user.delete(ban_user=True)
def ban_and_disable_related_content_bulk(cls, users, move_files=False): """Like ban_and_disable_related_content, but in bulk. """ from olympia.addons.models import Addon, AddonUser from olympia.addons.tasks import index_addons from olympia.bandwagon.models import Collection from olympia.files.models import File from olympia.ratings.models import Rating # collect affected addons addon_ids = set( Addon.unfiltered.exclude(status=amo.STATUS_DELETED) .filter(addonuser__user__in=users).values_list('id', flat=True)) # First addons who have other authors we aren't banning addon_joint_ids = set( AddonUser.objects.filter(addon_id__in=addon_ids) .exclude(user__in=users).values_list('addon_id', flat=True)) AddonUser.objects.filter( user__in=users, addon_id__in=addon_joint_ids).delete() # Then deal with users who are the sole author addons_sole = Addon.unfiltered.filter( id__in=addon_ids - addon_joint_ids) # set the status to disabled - using the manager update() method addons_sole.update(status=amo.STATUS_DISABLED) # collect Files that need to be disabled now the addons are disabled files_to_disable = File.objects.filter(version__addon__in=addons_sole) files_to_disable.update(status=amo.STATUS_DISABLED) if move_files: # if necessary move the files out of the CDN (expensive operation) for file_ in files_to_disable: file_.hide_disabled_file() # Finally run Addon.force_disable to add the logging; update versions # Status was already DISABLED so shouldn't fire watch_disabled again. for addon in addons_sole: addon.force_disable() index_addons.delay(addon_ids - addon_joint_ids) # delete the other content associated with the user Collection.objects.filter(author__in=users).delete() Rating.objects.filter(user__in=users).delete( user_responsible=core.get_user()) # And then delete the users. for user in users: user.delete(keep_fxa_id_and_email=True)
def create(cls, action, *args, **kw): """ e.g. ActivityLog.create(amo.LOG.CREATE_ADDON, addon), ActivityLog.create(amo.LOG.ADD_FILE_TO_VERSION, file, version) In case of circular import you can use `olympia.activity.log_create()` """ from olympia import core user = kw.get('user', core.get_user()) if not user: log.warning('Activity log called with no user: %s' % action.id) return al = ActivityLog(user=user, action=action.id) al.arguments = args if 'details' in kw: al.details = kw['details'] al.save() if 'details' in kw and 'comments' in al.details: CommentLog(comments=al.details['comments'], activity_log=al).save() for arg in args: if isinstance(arg, tuple): if arg[0] == Addon: AddonLog(addon_id=arg[1], activity_log=al).save() elif arg[0] == Version: VersionLog(version_id=arg[1], activity_log=al).save() elif arg[0] == UserProfile: UserLog(user_id=arg[1], activity_log=al).save() elif arg[0] == Group: GroupLog(group_id=arg[1], activity_log=al).save() elif isinstance(arg, Addon): AddonLog(addon=arg, activity_log=al).save() elif isinstance(arg, Version): VersionLog(version=arg, activity_log=al).save() elif isinstance(arg, UserProfile): # Index by any user who is mentioned as an argument. UserLog(activity_log=al, user=arg).save() elif isinstance(arg, Group): GroupLog(group=arg, activity_log=al).save() # Index by every user UserLog(activity_log=al, user=user).save() return al
def extract_theme_properties(addon, channel): version = addon.find_latest_version(channel) if not version or not version.all_files: return {} try: parsed_data = parse_xpi( version.all_files[0].file_path, addon=addon, user=core.get_user()) except ValidationError: # If we can't parse the existing manifest safely return. return {} theme_props = parsed_data.get('theme', {}) # pre-process colors to deprecated colors; strip spaces. theme_props['colors'] = dict( process_color_value(prop, color) for prop, color in theme_props.get('colors', {}).items()) # upgrade manifest from deprecated headerURL to theme_frame if 'headerURL' in theme_props.get('images', {}): url = theme_props['images'].pop('headerURL') theme_props['images']['theme_frame'] = url return theme_props
def check_xpi_info(xpi_info, addon=None, xpi_file=None, user=None): from olympia.addons.models import Addon, DeniedGuid guid = xpi_info['guid'] is_webextension = xpi_info.get('is_webextension', False) # If we allow the guid to be omitted we assume that one was generated # or existed before and use that one. # An example are WebExtensions that don't require a guid but we generate # one once they're uploaded. Now, if you update that WebExtension we # just use the original guid. if addon and not guid and is_webextension: xpi_info['guid'] = guid = addon.guid if not guid and not is_webextension: raise forms.ValidationError(ugettext('Could not find an add-on ID.')) if guid: current_user = core.get_user() if current_user: deleted_guid_clashes = Addon.unfiltered.exclude( authors__id=current_user.id).filter(guid=guid) else: deleted_guid_clashes = Addon.unfiltered.filter(guid=guid) if addon and addon.guid != guid: msg = ugettext( 'The add-on ID in your manifest.json or install.rdf (%s) ' 'does not match the ID of your add-on on AMO (%s)') raise forms.ValidationError(msg % (guid, addon.guid)) if (not addon and # Non-deleted add-ons. (Addon.objects.filter(guid=guid).exists() or # DeniedGuid objects for deletions for Mozilla disabled add-ons DeniedGuid.objects.filter(guid=guid).exists() or # Deleted add-ons that don't belong to the uploader. deleted_guid_clashes.exists())): raise forms.ValidationError(ugettext('Duplicate add-on ID found.')) if len(xpi_info['version']) > 32: raise forms.ValidationError( ugettext('Version numbers should have fewer than 32 characters.')) if not VERSION_RE.match(xpi_info['version']): raise forms.ValidationError( ugettext('Version numbers should only contain letters, numbers, ' 'and these punctuation characters: +*.-_.')) if is_webextension and xpi_info.get('type') == amo.ADDON_STATICTHEME: if not waffle.switch_is_active('allow-static-theme-uploads'): raise forms.ValidationError(ugettext( 'WebExtension theme uploads are currently not supported.')) if xpi_file: # Make sure we pass in a copy of `xpi_info` since # `resolve_webext_translations` modifies data in-place translations = Addon.resolve_webext_translations( xpi_info.copy(), xpi_file) verify_mozilla_trademark(translations['name'], core.get_user()) # Parse the file to get and validate package data with the addon. if not acl.submission_allowed(user, xpi_info): raise forms.ValidationError( ugettext(u'You cannot submit this type of add-on')) if not addon and not system_addon_submission_allowed( user, xpi_info): guids = ' or '.join( '"' + guid + '"' for guid in amo.SYSTEM_ADDON_GUIDS) raise forms.ValidationError( ugettext(u'You cannot submit an add-on with a guid ending ' u'%s' % guids)) if not mozilla_signed_extension_submission_allowed(user, xpi_info): raise forms.ValidationError( ugettext(u'You cannot submit a Mozilla Signed Extension')) return xpi_info
def create(cls, action, *args, **kw): """ e.g. ActivityLog.create(amo.LOG.CREATE_ADDON, addon), ActivityLog.create(amo.LOG.ADD_FILE_TO_VERSION, file, version) In case of circular import you can use `olympia.activity.log_create()` """ from olympia import core user = kw.get('user', core.get_user()) if not user: log.warning('Activity log called with no user: %s' % action.id) return al = ActivityLog(user=user, action=action.id) al.arguments = args if 'details' in kw: al.details = kw['details'] al.save() # We make sure that we take the timestamp if provided, instead of # creating a new one, especially useful for log entries created # in a loop. if 'created' in kw: al.update(created=kw.get('created')) if 'details' in kw and 'comments' in al.details: cl = CommentLog(comments=al.details['comments'], activity_log=al) cl.save() if 'created' in kw: cl.update(created=kw.get('created')) for arg in args: if isinstance(arg, tuple): if arg[0] == Addon: addon_l = AddonLog(addon_id=arg[1], activity_log=al) addon_l.save() if 'created' in kw: addon_l.update(created=kw.get('created')) elif arg[0] == Version: vl = VersionLog(version_id=arg[1], activity_log=al) vl.save() if 'created' in kw: vl.update(created=kw.get('created')) elif arg[0] == UserProfile: ul = UserLog(user_id=arg[1], activity_log=al) ul.save() if 'created' in kw: ul.update(created=kw.get('created')) elif arg[0] == Group: gl = GroupLog(group_id=arg[1], activity_log=al) gl.save() if 'created' in kw: gl.update(created=kw.get('created')) elif isinstance(arg, Addon): addon_l = AddonLog(addon=arg, activity_log=al) addon_l.save() if 'created' in kw: addon_l.update(created=kw.get('created')) elif isinstance(arg, Version): vl = VersionLog(version=arg, activity_log=al) vl.save() if 'created' in kw: vl.update(created=kw.get('created')) elif isinstance(arg, UserProfile): # Index by any user who is mentioned as an argument. ul = UserLog(activity_log=al, user=arg) ul.save() if 'created' in kw: ul.update(created=kw.get('created')) elif isinstance(arg, Group): gl = GroupLog(group=arg, activity_log=al) gl.save() if 'created' in kw: gl.update(created=kw.get('created')) # Index by every user ul = UserLog(activity_log=al, user=user) ul.save() if 'created' in kw: ul.update(created=kw.get('created')) return al
def some_func(): return core.get_user()
def create(cls, action, *args, **kw): """ e.g. ActivityLog.create(amo.LOG.CREATE_ADDON, addon), ActivityLog.create(amo.LOG.ADD_FILE_TO_VERSION, file, version) In case of circular import you can use `olympia.activity.log_create()` """ from olympia import core user = kw.get('user', core.get_user()) if not user: log.warning('Activity log called with no user: %s' % action.id) return # We make sure that we take the timestamp if provided, instead of # creating a new one, especially useful for log entries created # in a loop. al = ActivityLog(user=user, action=action.id, created=kw.get('created', timezone.now())) al.set_arguments(args) if 'details' in kw: al.details = kw['details'] al.save() if 'details' in kw and 'comments' in al.details: CommentLog.objects.create( comments=al.details['comments'], activity_log=al, created=kw.get('created', timezone.now()), ) for arg in args: create_kwargs = { 'activity_log': al, 'created': kw.get('created', timezone.now()), } if isinstance(arg, tuple): class_ = arg[0] id_ = arg[1] else: class_ = arg.__class__ id_ = arg.id if isinstance(arg, ModelBase) else None if class_ == Addon: AddonLog.objects.create(addon_id=id_, **create_kwargs) elif class_ == Version: VersionLog.objects.create(version_id=id_, **create_kwargs) elif class_ == UserProfile: UserLog.objects.create(user_id=id_, **create_kwargs) elif class_ == Group: GroupLog.objects.create(group_id=id_, **create_kwargs) elif class_ == Block: BlockLog.objects.create(block_id=id_, guid=arg.guid, **create_kwargs) elif class_ == ReviewActionReason: ReviewActionReasonLog.objects.create(reason_id=id_, **create_kwargs) if getattr(action, 'store_ip', False): # Index specific actions by their IP address. Note that the caller # must take care of overriding remote addr if the action is created # from a task. IPLog.objects.create( ip_address=core.get_remote_addr(), activity_log=al, created=kw.get('created', timezone.now()), ) # Index by every user UserLog.objects.create(activity_log=al, user=user, created=kw.get('created', timezone.now())) return al
def process(self, msg, kwargs): kwargs['extra'] = { 'REMOTE_ADDR': core.get_remote_addr() or '', 'USERNAME': getattr(core.get_user(), 'username', None) or '<anon>' } return msg, kwargs
def check_xpi_info(xpi_info, addon=None, xpi_file=None, user=None): from olympia.addons.models import Addon, DeniedGuid guid = xpi_info['guid'] is_webextension = xpi_info.get('is_webextension', False) # If we allow the guid to be omitted we assume that one was generated # or existed before and use that one. # An example are WebExtensions that don't require a guid but we generate # one once they're uploaded. Now, if you update that WebExtension we # just use the original guid. if addon and not guid and is_webextension: xpi_info['guid'] = guid = addon.guid if not guid and not is_webextension: raise forms.ValidationError(ugettext('Could not find an add-on ID.')) if guid: current_user = core.get_user() if current_user: deleted_guid_clashes = Addon.unfiltered.exclude( authors__id=current_user.id).filter(guid=guid) else: deleted_guid_clashes = Addon.unfiltered.filter(guid=guid) if addon and addon.guid != guid: msg = ugettext( 'The add-on ID in your manifest.json or install.rdf (%s) ' 'does not match the ID of your add-on on AMO (%s)') raise forms.ValidationError(msg % (guid, addon.guid)) if (not addon and # Non-deleted add-ons. ( Addon.objects.filter(guid=guid).exists() or # DeniedGuid objects for deletions for Mozilla disabled add-ons DeniedGuid.objects.filter(guid=guid).exists() or # Deleted add-ons that don't belong to the uploader. deleted_guid_clashes.exists())): raise forms.ValidationError(ugettext('Duplicate add-on ID found.')) if len(xpi_info['version']) > 32: raise forms.ValidationError( ugettext('Version numbers should have fewer than 32 characters.')) if not VERSION_RE.match(xpi_info['version']): raise forms.ValidationError( ugettext('Version numbers should only contain letters, numbers, ' 'and these punctuation characters: +*.-_.')) if is_webextension and xpi_info.get('type') == amo.ADDON_STATICTHEME: max_size = settings.MAX_STATICTHEME_SIZE if xpi_file and os.path.getsize(xpi_file.name) > max_size: raise forms.ValidationError( ugettext( u'Maximum size for WebExtension themes is {0}.').format( filesizeformat(max_size))) if xpi_file: # Make sure we pass in a copy of `xpi_info` since # `resolve_webext_translations` modifies data in-place translations = Addon.resolve_webext_translations( xpi_info.copy(), xpi_file) verify_mozilla_trademark(translations['name'], core.get_user()) # Parse the file to get and validate package data with the addon. if not acl.submission_allowed(user, xpi_info): raise forms.ValidationError( ugettext(u'You cannot submit this type of add-on')) if not addon and not system_addon_submission_allowed(user, xpi_info): guids = ' or '.join('"' + guid + '"' for guid in amo.SYSTEM_ADDON_GUIDS) raise forms.ValidationError( ugettext(u'You cannot submit an add-on with a guid ending ' u'%s' % guids)) if not mozilla_signed_extension_submission_allowed(user, xpi_info): raise forms.ValidationError( ugettext(u'You cannot submit a Mozilla Signed Extension')) return xpi_info