Mime_type for encrypted files saved as 'octet-stream' but i want 'image/png'?

I keep running into problems with making an encryption gem named Lockbox work Shrine.

What’s confusing me is that their suggestions for encrypting files interfere with Shrine’s suggested implementation for uploading. Simply put, Shrine mime type validations don’t work with encrypted files. Though this can be handled in the controller action with Shrine.mime_type(io) prior to creating the record, it feels weird. Doing this means I have to remove those validations from the uploader. Now the file validates and uploads.

However! Shrine is still working with an encrypted file, so the mime_type field in image_data is saved as application/octet-stream. I want to stream the file to the browser with send_data, but there’s no way to tell the browser how to interpret the binary data if that’s what the mime type is being saved as.

So … is there a way for Shrine to validate encrypted files? If not, how would you set the mime type of the image before the record is persisted? I could presumably parse image_data for a certain number of backward slashes, colons, and curly brackets, and then change the value of mime_type, but that would feel super hacky.

def create
 image = params.require(:id_doc).fetch(:image)
 if Shrine.mime_type(image) == 'image/jpeg' || Shrine.mime_type(image) == 'image/png' && image.size <= 3*1024*1024
  lockbox = Lockbox.new(key: Lockbox.attribute_key(table: "id_docs", attribute: "image"))
  encrypted_image = lockbox.encrypt_io(image)
  @id_doc = IdDoc.create(user_id: current_user.id, image: encrypted_image)
  @id_doc.save
  redirect_to root_path
 else
  redirect_to upload_path
 end
end

I recommend extracting metadata before uploading the file using Shrine#extract_metadata, and passing this metadata to Attacher#assign (which is what the attachment setter on the model calls internally). This should make attacher validations work.

image = params.require(:id_doc).fetch(:image)
meadata = IdDoc.image_attacher.cache.extract_metadata(image)

lockbox = Lockbox.new(key: Lockbox.attribute_key(table: "id_docs", attribute: "image"))
encrypted_image = lockbox.encrypt_io(image)

@id_doc = IdDoc.create(user_id: current_user.id)
@id_doc.image_attacher.assign(encrypted_image, metadata: metadata)

# ...
1 Like