def is_listing_owner_or_admin(username, instance): profile = generic_model_access.get_profile(username) if profile.highest_role() not in ['APPS_MALL_STEWARD', 'ORG_STEWARD']: if profile not in instance.owners.all(): raise errors.PermissionDenied( 'User ({0!s}) is not an owner of this listing'.format(username)) if instance.is_deleted: raise errors.PermissionDenied('Cannot update a previously deleted listing') return True
def update_bookmark_entry_for_profile(request_profile, bookmark_entry_instance, bookmark_parent_object, title): """ Update Bookmark Entries Method Responsibilities: * Rename folder bookmark titles * Moving folder/listing bookmarks under different folders Args: request_profile bookmark_entry_instance bookmark_parent_object: `BookmarkEntry` bookmark instance where request_profile want to put bookmark_entry_instance in that folder title """ if bookmark_parent_object is None and title is None: raise errors.PermissionDenied('Need at least the bookmark_parent or title field') # Check to see if request profile has access to bookmark_entry_instance # (bookmark_entry_instance can be folder or listing bookmark) check_permission_for_bookmark_entry(request_profile, bookmark_entry_instance) folder_title_changed = False bookmark_entry_moved = False if bookmark_parent_object: if bookmark_parent_object.type != FOLDER_TYPE: raise errors.PermissionDenied('bookmark_parent_object needs to be a folder type') # make sure user has owner access on bookmark_parent_object check_permission_for_bookmark_entry(request_profile, bookmark_parent_object) # get bookmark entries to folder relationships for request_profile bookmark_entry_folder_relationships = bookmark_entry_instance.bookmark_parent.filter( bookmark_permission__profile=request_profile) bookmark_entry_instance.bookmark_parent.remove(*bookmark_entry_folder_relationships) bookmark_entry_instance.bookmark_parent.add(bookmark_parent_object) bookmark_entry_moved = True if bookmark_entry_instance.type == FOLDER_TYPE: if title: bookmark_entry_instance.title = title folder_title_changed = True bookmark_entry_instance.save() if bookmark_entry_moved or folder_title_changed: dispatcher.publish('update_bookmark_entry', bookmark_entry_instance=bookmark_entry_instance, bookmark_parent_object=bookmark_parent_object, folder_title_changed=folder_title_changed, bookmark_entry_moved=bookmark_entry_moved) return bookmark_entry_instance
def share_bookmark_entry(request_profile, bookmark_entry_folder_to_share, target_profile, target_profile_bookmark_entry=None, target_user_type=None): """ Add Profile Permission to Bookmark Entry Args: request_profile: Profile performing the action (view, add, edit, delete) bookmark_entry_folder_to_share: The folder `request_profile` is trying to share/add permission target_profile: The profile the `bookmark_entry_folder_to_share` should go to target_bookmark: target_user_type: Steps: check to see if `request_profile` has owner permission on the folder `bookmark_entry_folder_to_share` trying to be shared Defaults the target_user_type to BookmarkPermission.VIEWER """ check_permission_for_bookmark_entry(request_profile, bookmark_entry_folder_to_share) target_user_type = target_user_type if target_user_type else models.BookmarkPermission.VIEWER if bookmark_entry_folder_to_share.type != FOLDER_TYPE: raise errors.PermissionDenied('bookmark_entry needs to be a folder type') # Check if target profile already has permissions for BookmarkEntry existing_target_profile_permission = models.BookmarkPermission.objects.filter( bookmark=bookmark_entry_folder_to_share, profile=target_profile ).first() if existing_target_profile_permission: raise errors.PermissionDenied('target user already has access to folder') # Add Bookmark entry to bookmark_entry_folder_to_share folder, add behaves like a set # target_profile_bookmark_entry.bookmark_parent.add(bookmark_entry_folder_to_share) if not target_profile_bookmark_entry: target_profile_bookmark_entry = create_get_user_root_bookmark_folder(target_profile) bookmark_entry_folder_to_share.bookmark_parent.add(target_profile_bookmark_entry) if bookmark_entry_folder_to_share.type == FOLDER_TYPE: bookmark_permission_folder = models.BookmarkPermission() bookmark_permission_folder.bookmark = bookmark_entry_folder_to_share bookmark_permission_folder.profile = target_profile bookmark_permission_folder.user_type = target_user_type bookmark_permission_folder.save() return bookmark_permission_folder elif target_bookmark.type == LISTING_TYPE: # LISTINGs inherit folder permissions pass
def delete_listing(username, listing, delete_description): """ TODO: need a way to keep track of this listing as being deleted. for now just remove """ profile = generic_model_access.get_profile(username) # app_owners = [i.user.username for i in listing.owners.all()] # ensure user is the author of this review, or that user is an org # steward or apps mall steward # Don't allow 2nd-party user to be an delete a listing if system_anonymize_identifiable_data(profile.user.username): raise errors.PermissionDenied( 'Current profile does not have delete permissions') priv_roles = ['APPS_MALL_STEWARD', 'ORG_STEWARD'] if profile.highest_role( ) in priv_roles or listing.approval_status == 'IN_PROGRESS': pass else: raise errors.PermissionDenied( 'Only Org Stewards and admins can delete listings') if listing.is_deleted: raise errors.PermissionDenied('The listing has already been deleted') old_approval_status = listing.approval_status listing = _add_listing_activity(profile, listing, models.ListingActivity.DELETED, description=delete_description) listing.is_deleted = True listing.is_enabled = False listing.is_featured = False listing.approval_status = models.Listing.DELETED # TODO Delete the values of other field # Keep lisiting as shell listing for history listing.save() # listing.delete() dispatcher.publish('listing_approval_status_changed', listing=listing, profile=profile, old_approval_status=old_approval_status, new_approval_status=listing.approval_status)
def check_local_permission(self, entity): if self.sender_profile.highest_role() in ['APPS_MALL_STEWARD', 'ORG_STEWARD']: return True if self.sender_profile not in self.entity_dict['listing'].owners.all(): raise errors.PermissionDenied('Cannot create a notification for a listing you do not own') else: return True return False
def destroy(self, request, pk=None): current_request_profile = model_access.get_self(request.user.username) if not current_request_profile.is_steward(): raise errors.PermissionDenied('Only Stewards can delete notifications') queryset = self.get_queryset() notification_instance = get_object_or_404(queryset, pk=pk) notification_instance.delete() return Response(status=status.HTTP_204_NO_CONTENT)
def update_profile_permission_for_bookmark_entry(request_profile, bookmark_permission_entry, user_type): """ Update Profile Permission for Bookmark Entry Assumes all the permission checks happen before this is called for bookmark_permission_entry """ bookmark_permission_entry_profile = bookmark_permission_entry.profile if request_profile == bookmark_permission_entry_profile: raise errors.PermissionDenied('can only update permissions for other users') bookmark_permission_entry.user_type = user_type bookmark_permission_entry.save() return bookmark_permission_entry
def user_create_listing_condition(profile_obj, listing): """ Listing create condition for user Args: profile_obj (models.Profile): Profile listing (models.Listing): Listing Return: bool: if user can create listings """ if profile_obj not in listing.owners.all(): raise errors.PermissionDenied( 'Cannot create a notification for a listing you do not own') return True
def get_bookmark_entry_by_id(request_profile, id): """ Get bookmark entry by id and filter based on permissions Only owner or viewer of folder should be able to see it """ # Validate to make sure user can see folder - bookmark_permission__profile query = models.BookmarkEntry.objects.filter( id=id, bookmark_parent__bookmark_permission__profile=request_profile ).first() if query is None: raise errors.PermissionDenied('Can not view bookmarks') return query
def check_permission_for_bookmark_entry(request_profile, bookmark_entry, allow_owner=True, allow_viewer=False): """ Check Permission for bookmark entry OWNER can view/add/edit/delete BookmarkEntry and BookmarkPermission models VIEWERs have no permissions """ bookmark_entry_type = bookmark_entry.type profile_bookmark_permission = None if bookmark_entry_type == FOLDER_TYPE: profile_bookmark_permission = models.BookmarkPermission.objects.filter( profile=request_profile, bookmark=bookmark_entry) elif bookmark_entry_type == LISTING_TYPE: profile_bookmark_permission = models.BookmarkPermission.objects.filter( profile=request_profile, bookmark__bookmarkentry=bookmark_entry) # Convert query to BookmarkPermission object if profile_bookmark_permission: profile_bookmark_permission = profile_bookmark_permission.first() if not profile_bookmark_permission: raise errors.PermissionDenied('request profile does not have permission to view permissions') elif profile_bookmark_permission.user_type == models.BookmarkPermission.VIEWER: if allow_viewer is False: raise errors.PermissionDenied('request profile does not have permission to view permissions because of user_type') elif profile_bookmark_permission.user_type == models.BookmarkPermission.OWNER: if allow_owner is False: raise errors.PermissionDenied('request profile does not have permission to view permissions because of user_type') else: raise errors.PermissionDenied('request profile does not have permission') return profile_bookmark_permission
def destroy(self, request, pk=None): queryset = self.get_queryset() image = get_object_or_404(queryset, pk=pk) # TODO: Verify that only stewards can delete images and upload user # enforce access control if not system_has_access_control(self.request.user.username, image.security_marking): raise errors.PermissionDenied( 'Security marking too high for current user') image.delete() # TODO: remove image from storage return Response(status=status.HTTP_204_NO_CONTENT)
def edit_listing_review(username, review, rate, text=None): """ Edit an existing review Args: username: user making this request review (models.Review): review to modify rate (int): rating (1-5) text (Optional(str)): review text Returns: The modified review """ # only the author of a review can edit it user = generic_model_access.get_profile(username) if review.author.user.username != username: raise errors.PermissionDenied() change_details = [{ 'field_name': 'rate', 'old_value': review.rate, 'new_value': rate }, { 'field_name': 'text', 'old_value': review.text, 'new_value': text }] listing = review.listing listing = _add_listing_activity(user, listing, models.ListingActivity.REVIEW_EDITED, change_details=change_details) review.rate = rate review.text = text review.edited_date = utils.get_now_utc() review.save() _update_rating(username, listing) dispatcher.publish('listing_review_changed', listing=listing, profile=user, rating=rate, text=text) return review
def get_user_permissions_for_bookmark_entry(request_profile, bookmark_entry): """ Get permissions for bookmark_entry Access Control Only Owners should be able to view all the permissions of a bookmark_entry Only Owners should be able to edit permissions of a bookmark_entry Access Control handle by get_bookmark_entry_by_id """ check_permission_for_bookmark_entry(request_profile, bookmark_entry) if bookmark_entry.type != FOLDER_TYPE: raise errors.PermissionDenied('Can only check permissions for folder bookmarks') query = models.BookmarkPermission.objects.filter(bookmark=bookmark_entry) return query
def _validate_create_bookmark_entry(request_profile=None, entry_type=None, folder_title=None, listing=None, is_root=None): """ Validate values for creating bookmark entries """ is_folder_type = (entry_type == FOLDER_TYPE) is_listing_type = (entry_type == LISTING_TYPE) is_root = is_root if is_root is True else False try: assert request_profile is not None, 'To create bookmark entry, profile is required' assert entry_type in BOOKMARK_TYPE_CHOICES, 'Entry Type needs to be one of the following: {}'.format(BOOKMARK_TYPE_CHOICES) if is_folder_type: if is_root is False and folder_title is None: raise AssertionError('Bookmark {} Entry require folder_title and is_root kwargs'.format(FOLDER_TYPE)) elif is_listing_type and listing is None: raise AssertionError('Bookmark {} Entry require listing object'.format(LISTING_TYPE)) except AssertionError as err: raise errors.PermissionDenied(err)
def delete_listing_review(username, review): """ Delete an existing review Args: username: user making this request review (models.Review): review to delete Returns: Listing associated with this review """ profile = generic_model_access.get_profile(username) # ensure user is the author of this review, or that user is an org # steward or apps mall steward priv_roles = ['APPS_MALL_STEWARD', 'ORG_STEWARD'] if profile.highest_role() in priv_roles: pass elif review.author.user.username != username: raise errors.PermissionDenied('Cannot update another user\'s review') # make a note of the change change_details = [ { 'field_name': 'rate', 'old_value': review.rate, 'new_value': None }, { 'field_name': 'text', 'old_value': review.text, 'new_value': None } ] # add this action to the log listing = review.listing listing = _add_listing_activity(profile, listing, models.ListingActivity.REVIEW_DELETED, change_details=change_details) # delete the review review.delete() # update this listing's rating _update_rating(username, listing) return listing
def create_folder_bookmark_for_profile(request_profile, folder_name, bookmark_entry_folder=None, bookmark_children=None, listing_object=None): """ Create Folder Bookmark for profile Args: profile (models.Profile): Profile folder_name (String): Folder name bookmark_entry_folder: (models.BookmarkEntry): Entry folder bookmark_children: (List of Integers) """ bookmark_entry_folder = bookmark_entry_folder if bookmark_entry_folder else create_get_user_root_bookmark_folder(request_profile) bookmark_children = bookmark_children if bookmark_children else [] if bookmark_entry_folder.type != FOLDER_TYPE: raise errors.PermissionDenied('bookmark_entry needs to be a folder type') # Only owners of bookmark_entry_folder should be able to add to folder check_permission_for_bookmark_entry(request_profile, bookmark_entry_folder) bookmark_folder_entry = create_bookmark_entry(request_profile, FOLDER_TYPE, folder_title=folder_name, is_root=False) # Add Permission so that user can see folder and make them owner add_profile_permission_for_bookmark_entry( request_profile, bookmark_entry_folder, request_profile, bookmark_folder_entry, target_user_type=models.BookmarkPermission.OWNER ) # TODO: What is this doing? for bookmark_child in bookmark_children: update_bookmark_entry_for_profile( request_profile, get_bookmark_entry_by_id(request_profile, bookmark_child), bookmark_folder_entry, None) if listing_object: create_listing_bookmark_for_profile(request_profile, listing_object, bookmark_folder_entry) return bookmark_folder_entry
def retrieve(self, request, pk=None): """ Return an image, enforcing access control """ queryset = self.get_queryset() image = get_object_or_404(queryset, pk=pk) image_path = str(image.id) + '_' + image.image_type.name + '.' + image.file_extension # enforce access control profile = generic_model_access.get_profile(self.request.user.username) if not system_has_access_control(profile.user.username, image.security_marking): raise errors.PermissionDenied() content_type = 'image/' + image.file_extension try: with media_storage.open(image_path) as f: return HttpResponse(f.read(), content_type=content_type) except IOError: logger.error('No image found for pk {}'.format(pk)) return Response(status=status.HTTP_404_NOT_FOUND)
def check_notification_permission(profile_instance, action, notification_type): """ Check to see if user has permission Args: profile_instance(Profile): Profile Instance action(string): add/change/delete notification_type(string): notification type Return: True or PermissionDenied Exception """ profile_role = profile_instance.highest_role() assert (profile_role in permission_dict), 'Profile group {} not found in permissions'.format(profile_role) user_action = '{}_{}_notification'.format(action, notification_type) profile_permission_list = permission_dict[profile_role] if user_action not in profile_permission_list: raise errors.PermissionDenied('Profile does not have [{}] permissions'.format(user_action)) return True
def destroy(self, request, bookmark_pk=None, pk=None): """ Validate make sure user has access if shared folder: if request_profile is viewer: raise PermissionDenied elif request_profile is owner and there is only one owner: Give owner "Are you sure you want to remove folder, it will affect all users" elif request_profile is owner and there is more than 1 owner: Remove Bookmark from user's bookmark list. else: Remove Bookmark from user's bookmark list. """ request_profile = request.user.profile bookmark_entry = model_access.get_bookmark_entry_by_id( request_profile, bookmark_pk) bookmark_permission_entry = model_access.get_bookmark_permission_by_id( request_profile, bookmark_entry, pk) # TODO: refactor to remove_profile_permission_for_bookmark_entry bookmark_permission_entry_profile = bookmark_permission_entry.profile if request_profile == bookmark_permission_entry_profile: raise errors.PermissionDenied( 'can only delete permissions for other users') # get bookmark entries to folder relationships for request_profile bookmark_entry_folder_relationships = bookmark_entry.bookmark_parent.filter( bookmark_permission__profile=bookmark_permission_entry_profile) bookmark_entry.bookmark_parent.remove( *bookmark_entry_folder_relationships) bookmark_permission_entry.delete() return Response(status=status.HTTP_204_NO_CONTENT)
def create_listing_bookmark_for_profile(request_profile, listing, bookmark_entry_folder=None): """ Create Listing Bookmark for profile """ bookmark_entry_folder = bookmark_entry_folder if bookmark_entry_folder else create_get_user_root_bookmark_folder(request_profile) if bookmark_entry_folder.type != FOLDER_TYPE: raise errors.PermissionDenied('bookmark_entry needs to be a folder type') # Only owners of bookmark_entry_folder should be able to add to folder check_permission_for_bookmark_entry(request_profile, bookmark_entry_folder) bookmark_entry = create_bookmark_entry(request_profile, LISTING_TYPE, listing=listing) # Add Permission so that user can see folder and make them owner add_profile_permission_for_bookmark_entry( request_profile, bookmark_entry_folder, request_profile, bookmark_entry, target_user_type=models.BookmarkPermission.OWNER ) return bookmark_entry
def org_create_listing_condition(profile_obj, listing): if profile_obj not in listing.owners.all(): raise errors.PermissionDenied( 'Cannot create a notification for a listing you do not own') return True
def _check_profile_permission(user_role_type, notification_action, notification_type, **kwargs): """ Check Permission Permissions: APP_MALL_STEWARD can [CREATE, UPDATE, DISMISS, DELETE] notification type [SYSTEM, AGENCY, LISTING] ORG_STEWARD can [CREATE, UPDATE, DISMISS] notification type [AGENCY(org_steward_agency_condition), LISTING(c2)] USER can [CREATE, DISMISS] notification type [LISTING(c3)] Return: lambda function """ profile_obj = kwargs.get('profile_obj') listing = kwargs.get('listing') permissions = { UserRoleType.APPS_MALL_STEWARD: { NotificationActionEnum.CREATE: { NotificationTypeEnum.SYSTEM: lambda: True, NotificationTypeEnum.AGENCY: lambda: True, NotificationTypeEnum.LISTING: lambda: True, NotificationTypeEnum.PEER: lambda: True }, NotificationActionEnum.UPDATE: { NotificationTypeEnum.SYSTEM: lambda: True, NotificationTypeEnum.AGENCY: lambda: True, NotificationTypeEnum.LISTING: lambda: True, NotificationTypeEnum.PEER: lambda: True }, NotificationActionEnum.DELETE: { NotificationTypeEnum.SYSTEM: lambda: True, NotificationTypeEnum.AGENCY: lambda: True, NotificationTypeEnum.LISTING: lambda: True, NotificationTypeEnum.PEER: lambda: True } }, UserRoleType.ORG_STEWARD: { NotificationActionEnum.CREATE: { NotificationTypeEnum.SYSTEM: lambda: raise_(errors.PermissionDenied('Only app mall stewards can create system notifications')), NotificationTypeEnum.AGENCY: lambda: True, NotificationTypeEnum.LISTING: lambda: True, # TODO: org_create_listing_condition(profile_obj, listing) NotificationTypeEnum.PEER: lambda: True }, NotificationActionEnum.UPDATE: { # lambda: raise_(errors.PermissionDenied('Only app mall # stewards can update system notifications')), NotificationTypeEnum.SYSTEM: lambda: True, NotificationTypeEnum.AGENCY: lambda: True, NotificationTypeEnum.LISTING: lambda: True, NotificationTypeEnum.PEER: lambda: True }, NotificationActionEnum.DELETE: { # lambda: raise_(errors.PermissionDenied('Only app mall # stewards can delete system notifications')), NotificationTypeEnum.SYSTEM: lambda: True, NotificationTypeEnum.AGENCY: lambda: True, NotificationTypeEnum.LISTING: lambda: True, NotificationTypeEnum.PEER: lambda: True } }, UserRoleType.USER: { NotificationActionEnum.CREATE: { NotificationTypeEnum.SYSTEM: lambda: raise_(errors.PermissionDenied('Only app mall stewards can create system notifications')), NotificationTypeEnum.AGENCY: lambda: raise_(errors.PermissionDenied('Only org stewards can create agency notifications')), NotificationTypeEnum.LISTING: lambda: user_create_listing_condition(profile_obj, listing), NotificationTypeEnum.PEER: lambda: True }, NotificationActionEnum.UPDATE: { NotificationTypeEnum.SYSTEM: lambda: raise_(errors.PermissionDenied('Only app mall stewards can update system notifications')), NotificationTypeEnum.AGENCY: lambda: raise_(errors.PermissionDenied('Only org stewards can create agency notifications')), NotificationTypeEnum.LISTING: None, NotificationTypeEnum.PEER: lambda: True }, NotificationActionEnum.DELETE: { NotificationTypeEnum.SYSTEM: lambda: raise_(errors.PermissionDenied('Only app mall stewards can delete system notifications')), NotificationTypeEnum.AGENCY: lambda: raise_(errors.PermissionDenied('Only org stewards can create agency notifications')), NotificationTypeEnum.LISTING: None, NotificationTypeEnum.PEER: lambda: True } } } return permissions.get(user_role_type, {}).get(notification_action, {}).get(notification_type, lambda: raise_(errors.PermissionDenied('Unknown Permissions')))
def update(self, instance, validated_data): # logger.debug('inside ListingSerializer.update', extra={'request':self.context.get('request')}) user = generic_model_access.get_profile( self.context['request'].user.username) if user.highest_role() not in ['APPS_MALL_STEWARD', 'ORG_STEWARD']: if user not in instance.owners.all(): raise errors.PermissionDenied( 'User ({0!s}) is not an owner of this listing'.format( user.username)) if instance.is_deleted: raise errors.PermissionDenied( 'Cannot update a previously deleted listing') change_details = [] simple_fields = [ 'title', 'description', 'description_short', 'launch_url', 'version_name', 'requirements', 'unique_name', 'what_is_new', 'security_marking' ] for i in simple_fields: if getattr(instance, i) != validated_data[i]: change_details.append({ 'old_value': getattr(instance, i), 'new_value': validated_data[i], 'field_name': i }) setattr(instance, i, validated_data[i]) if validated_data['is_enabled'] != instance.is_enabled: if validated_data['is_enabled']: model_access.enable_listing(user, instance) else: model_access.disable_listing(user, instance) instance.is_enabled = validated_data['is_enabled'] if validated_data['is_private'] != instance.is_private: change_details.append({ 'old_value': model_access.bool_to_string(instance.is_private), 'new_value': model_access.bool_to_string(validated_data['is_private']), 'field_name': 'is_private' }) instance.is_private = validated_data['is_private'] if validated_data['is_featured'] != instance.is_featured: if user.highest_role() not in ['APPS_MALL_STEWARD', 'ORG_STEWARD']: raise errors.PermissionDenied( 'Only stewards can change is_featured setting of a listing' ) change_details.append({ 'old_value': model_access.bool_to_string(instance.is_featured), 'new_value': model_access.bool_to_string(validated_data['is_featured']), 'field_name': 'is_featured' }) instance.is_featured = validated_data['is_featured'] s = validated_data['approval_status'] if s and s != instance.approval_status: if s == models.Listing.APPROVED and user.highest_role( ) != 'APPS_MALL_STEWARD': raise errors.PermissionDenied( 'Only an APPS_MALL_STEWARD can mark a listing as APPROVED') if s == models.Listing.APPROVED_ORG and user.highest_role( ) not in ['APPS_MALL_STEWARD', 'ORG_STEWARD']: raise errors.PermissionDenied( 'Only stewards can mark a listing as APPROVED_ORG') if s == models.Listing.PENDING: model_access.submit_listing(user, instance) if s == models.Listing.APPROVED_ORG: model_access.approve_listing_by_org_steward(user, instance) if s == models.Listing.APPROVED: model_access.approve_listing(user, instance) if s == models.Listing.REJECTED: # TODO: need to get the rejection text from somewhere model_access.reject_listing(user, instance, 'TODO: rejection reason') if instance.listing_type != validated_data['listing_type']: if instance.listing_type: old_value = instance.listing_type.title else: old_value = None if validated_data['listing_type']: new_value = validated_data['listing_type'].title else: new_value = None change_details.append({ 'old_value': old_value, 'new_value': new_value, 'field_name': 'listing_type' }) instance.listing_type = validated_data['listing_type'] image_keys = [ 'small_icon', 'large_icon', 'banner_icon', 'large_banner_icon' ] for image_key in image_keys: if validated_data[image_key]: old_value = model_access.image_to_string( getattr(instance, image_key), True, 'old_value({0!s})'.format(image_key)) new_value = model_access.image_to_string( validated_data[image_key], False, 'new_value({0!s})'.format(image_key)) if old_value != new_value: new_value_image = None old_image_id = None if old_value is not None: old_image_id = getattr(instance, image_key).id if validated_data[image_key].get('id') == old_image_id: new_value_image = getattr(instance, image_key) new_value_image.security_marking = validated_data[ image_key].get('security_marking') new_value_image.save() else: new_value_image = image_model_access.get_image_by_id( validated_data[image_key].get('id')) if new_value_image is None: raise errors.InvalidInput( 'Error while saving, can not find image by id') change_details.append({ 'old_value': old_value, 'new_value': new_value, 'field_name': image_key }) if image_key == 'small_icon': instance.small_icon = new_value_image elif image_key == 'large_icon': instance.large_icon = new_value_image elif image_key == 'banner_icon': instance.banner_icon = new_value_image elif image_key == 'large_banner_icon': instance.large_banner_icon = new_value_image if 'contacts' in validated_data: old_contact_instances = instance.contacts.all() old_contacts = model_access.contacts_to_string( old_contact_instances, True) new_contacts = model_access.contacts_to_string( validated_data['contacts']) if old_contacts != new_contacts: change_details.append({ 'old_value': old_contacts, 'new_value': new_contacts, 'field_name': 'contacts' }) instance.contacts.clear() for contact in validated_data['contacts']: # TODO: Smarter Handling of Duplicates Contact Records # A contact with the same name and email should be the same contact # in the backend. # Person1(name='N1',email='*****@*****.**') and # Person1' (name='N1',email='*****@*****.**',secure_phone = '414-444-444') # The two people above should be one contact # if approval_status: "IN_PROGRESS" then it should be using # contact model ids' since it is temporary contacts obj, created = models.Contact.objects.get_or_create( name=contact['name'], email=contact['email'], secure_phone=contact['secure_phone'], unsecure_phone=contact['unsecure_phone'], organization=contact.get('organization', None), contact_type=contact_type_model_access. get_contact_type_by_name( contact['contact_type']['name'])) instance.contacts.add(obj) if 'categories' in validated_data: old_category_instances = instance.categories.all() old_categories = model_access.categories_to_string( old_category_instances, True) new_categories = model_access.categories_to_string( validated_data['categories'], True) if old_categories != new_categories: change_details.append({ 'old_value': old_categories, 'new_value': new_categories, 'field_name': 'categories' }) instance.categories.clear() for category in validated_data['categories']: instance.categories.add(category) if 'owners' in validated_data: old_owner_instances = instance.owners.all() old_owners = model_access.owners_to_string(old_owner_instances, True) new_owners = model_access.owners_to_string( validated_data['owners'], True) if old_owners != new_owners: change_details.append({ 'old_value': old_owners, 'new_value': new_owners, 'field_name': 'owners' }) instance.owners.clear() for owner in validated_data['owners']: instance.owners.add(owner) # tags will be automatically created if necessary if 'tags' in validated_data: old_tag_instances = instance.tags.all() old_tags = model_access.tags_to_string(old_tag_instances, True) new_tags = model_access.tags_to_string(validated_data['tags']) if old_tags != new_tags: change_details.append({ 'old_value': old_tags, 'new_value': new_tags, 'field_name': 'tags' }) instance.tags.clear() for tag in validated_data['tags']: obj, created = models.Tag.objects.get_or_create( name=tag['name']) instance.tags.add(obj) if 'intents' in validated_data: old_intent_instances = instance.intents.all() old_intents = model_access.intents_to_string( old_intent_instances, True) new_intents = model_access.intents_to_string( validated_data['intents'], True) if old_intents != new_intents: change_details.append({ 'old_value': old_intents, 'new_value': new_intents, 'field_name': 'intents' }) instance.intents.clear() for intent in validated_data['intents']: instance.intents.add(intent) # doc_urls will be automatically created if 'doc_urls' in validated_data: old_doc_url_instances = model_access.get_doc_urls_for_listing( instance) old_doc_urls = model_access.doc_urls_to_string( old_doc_url_instances, True) new_doc_urls = model_access.doc_urls_to_string( validated_data['doc_urls']) if old_doc_urls != new_doc_urls: change_details.append({ 'old_value': old_doc_urls, 'new_value': new_doc_urls, 'field_name': 'doc_urls' }) new_doc_url_instances = [] for d in validated_data['doc_urls']: obj, created = models.DocUrl.objects.get_or_create( name=d['name'], url=d['url'], listing=instance) new_doc_url_instances.append(obj) for i in old_doc_url_instances: if i not in new_doc_url_instances: logger.info( 'Deleting doc_url: {0!s}'.format(i.id), extra={'request': self.context.get('request')}) i.delete() # screenshots will be automatically created if 'screenshots' in validated_data: old_screenshot_instances = model_access.get_screenshots_for_listing( instance) old_screenshots = model_access.screenshots_to_string( old_screenshot_instances, True) new_screenshots = model_access.screenshots_to_string( validated_data['screenshots']) if old_screenshots != new_screenshots: change_details.append({ 'old_value': old_screenshots, 'new_value': new_screenshots, 'field_name': 'screenshots' }) new_screenshot_instances = [] for s in validated_data['screenshots']: new_small_image = image_model_access.get_image_by_id( s['small_image']['id']) new_small_image.security_marking = s['small_image'][ 'security_marking'] new_small_image.save() new_large_image = image_model_access.get_image_by_id( s['large_image']['id']) new_large_image.security_marking = s['large_image'][ 'security_marking'] new_large_image.save() obj, created = models.Screenshot.objects.get_or_create( small_image=new_small_image, large_image=new_large_image, listing=instance) new_screenshot_instances.append(obj) for i in old_screenshot_instances: if i not in new_screenshot_instances: logger.info('Deleting screenshot: {0!s}'.format(i.id), extra={'request': self.context.get('request')}) i.delete() if 'agency' in validated_data: if instance.agency != validated_data['agency']: change_details.append({ 'old_value': instance.agency.title, 'new_value': validated_data['agency'].title, 'field_name': 'agency' }) instance.agency = validated_data['agency'] instance.save() # If the listing was modified add an entry showing changes if change_details: model_access.log_listing_modification(user, instance, change_details) instance.edited_date = datetime.datetime.now(pytz.utc) return instance