Skip to content

Instantly share code, notes, and snippets.

@johnmeehan
Last active October 6, 2017 10:31
Show Gist options
  • Save johnmeehan/b587510798286486d612b655e8a168b4 to your computer and use it in GitHub Desktop.
Save johnmeehan/b587510798286486d612b655e8a168b4 to your computer and use it in GitHub Desktop.
Obfuscated Download Links - How to add an obfuscated download link to a downloadable attachment.
# Rails side:
# 1. Attachment.rb model needs a token method that encrypts the model id.
class Attachment < ActiveRecord::Base
mount_uploader :file, AttachmentUploader
# Add this
def self.find_by_token(token)
begin
find(UrlParamEncryptor.decrypt(token))
rescue UrlParamEncryptor::DecryptionError
raise ActiveRecord::RecordNotFound
end
end
def file_name
File.basename(file.path)
end
def file_path
file.path
end
def file_url
file.url
end
# Add this
def token
UrlParamEncryptor.encrypt(id)
end
end
// 4 . Attachment.ts model - add `downloadUrl: string` and `token: string`
export class Attachment {
id: number;
fileName: string;
originalFileName: string;
filePath: string;
fileUrl: string;
fileItem: Object;
contentType: string;
downloadUrl: string;
token: string;
@DateAttr updatedAt: moment.Moment;
}
# lib/warden/attachment_download_strategy.rb
# Authenticates the download token
# success: returns the attachment object requested.
module Warden
module Strategies
class AttachmentDownloadStrategy < ::Warden::Strategies::Base
def valid?
attachment_token
end
def authenticate!
attachment = Attachment.find_by_token(attachment_token)
attachment.nil? ? fail!('strategies.attachment_download_strtegy.failed') : success!(attachment)
end
private
def attachment_token
params['attachment_token']
end
end
end
end
Warden::Strategies.add(:attachment_download_strategy, Warden::Strategies::AttachmentDownloadStrategy)
<!-- Displaying the links -->
<li *ngFor="let attachment of $attachments | async"
class="attachments__item">
<a
class="attachments__item-link"
download-file
target="_blank"
[href]="downloadUrl(attachment)">
{{attachment.originalFileName}}
</a>
<a
title="Remove"
confirm="Are you sure you want to remove this document?"
class="attachments__remove-item btn-icon--times btn-icon--secondary"
(confirmed)="removeAction(attachment)">
</a>
</li>
// 5
//Angular side:
// Attachmet_list.ts ( the component that iterates over the attachments ) add `downloadUrl()`
// Use this url in the view for the user to click on.
export class AttachmentsComponent {
$attachments = this._service.$attachments;
downloadUrl(attachment) {
return `/attachments/${attachment.token}`;
}
}
# 2. Attachment Serializer needs to now pass this `:token`
# attachment_serializer.rb
class AttachmentSerializer < ActiveModel::Serializer
attributes :id, :file_name, :file_path, :file_url, :original_file_name,
:content_type, :updated_at, :token
end
# 3. Attachment Controller
# We need to find the attachment by the token
# We can use a Warden Strategy to validated the link and set the id without having to go through the entire rails app.
#attachments_controller.rb
before_action :set_attachment, only: [:show, :destroy]
private
def set_attachment
@attachment = warden.authenticate!(scope: :attachment)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment