Skip to content

Commit

Permalink
- The token which is generated for the third party app is now cached
Browse files Browse the repository at this point in the history
- within the cache the token itself, a counter and the name of the third party app is stored+
- the upload and download to/from the ELN by the third party app is limited by the counter
  • Loading branch information
Konrad1991 committed Jul 19, 2023
1 parent 39d2824 commit 236f34a
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 52 deletions.
54 changes: 34 additions & 20 deletions app/api/chemotion/third_party_app_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ def decode_token(token)
error!('401 Unauthorized', 401) if payload&.length&.zero?
att_id = payload[0]['attID']&.to_i
user_id = payload[0]['userID']&.to_i
[att_id, user_id]
name_third_party_app = payload[0]['nameThirdPartyApp']&.to_s
[att_id, user_id, name_third_party_app]
end

def verify_token(token)
Expand All @@ -29,42 +30,54 @@ def download_third_party_app(token)
@user = User.find_by(id: payload[1])
header['Content-Disposition'] = "attachment; filename=#{@attachment.filename}"
env['api.format'] = :binary
cache_key = "token/#{payload[0]}/#{payload[1]}"
cache_key = "token/#{payload[0]}/#{payload[1]}/#{payload[2]}"
token_cached = Rails.cache.read(cache_key)
if token_cached.nil?
error!('Invalid token', 403)
end
token_cached.counter = token_cached.counter + 1
Rails.cache.write(cache_key, token_cached)
@attachment.read_file if token_cached.counter <= 2
if token_cached.counter <= 3
@attachment.read_file
else
error!('To many requests with this token', 403)
end
end

def upload_third_party_app(token, file_name, file, file_type)
payload = decode_token(token)
cache_key = "token/#{payload[0]}/#{payload[1]}"
cache_key = "token/#{payload[0]}/#{payload[1]}/#{payload[2]}"
token_cached = Rails.cache.read(cache_key)
if token_cached.nil?
error!('Invalid token', 403)
end
token_cached.counter = token_cached.counter + 1
Rails.cache.write(cache_key, token_cached)
return unless token_cached.counter <= 30

attachment = Attachment.find_by(id: payload[0])
new_attachment = Attachment.new(attachable: attachment.attachable,
created_by: attachment.created_by,
created_for: attachment.created_for,
content_type: file_type)
File.open(file[:tempfile].path, 'rb') do |f|
new_attachment.update(file_path: f, filename: file_name)
if token_cached.counter > 30
error!('To many request with this token', 403)
else
attachment = Attachment.find_by(id: payload[0])
new_attachment = Attachment.new(attachable: attachment.attachable,
created_by: attachment.created_by,
created_for: attachment.created_for,
content_type: file_type)
File.open(file[:tempfile].path, 'rb') do |f|
new_attachment.update(file_path: f, filename: file_name)
end
{ message: 'File uploaded successfully' }
end
{ message: 'File uploaded successfully' }
end

def encode_token(payload)
def encode_token(payload, name_third_party_app)
cache_key = cache_key_for_encoded_token(payload)
Rails.cache.fetch(cache_key, expires_in: 48.hours) do
token = JsonWebToken.encode(payload, 48.hours.from_now)
CachedTokenThirdPartyApp.new(token, 0)
CachedTokenThirdPartyApp.new(token, 0, name_third_party_app)
end
end

def cache_key_for_encoded_token(payload)
"encoded_token/#{payload[:attID]}/#{payload[:userID]}"
"encoded_token/#{payload[:attID]}/#{payload[:userID]}/#{payload[:nameThirdPartyApp]}"
end
end

Expand Down Expand Up @@ -188,11 +201,12 @@ def cache_key_for_encoded_token(payload)
params do
requires :attID, type: String, desc: 'Attachment ID'
requires :userID, type: String, desc: 'User ID'
requires :nameThirdPartyApp, type: String, desc: 'name of the third party app'
end
get 'Token' do
cache_key = "token/#{params[:attID]}/#{params[:userID]}"
payload = { attID: params[:attID], userID: params[:userID] }
cached_token = encode_token(payload)
cache_key = "token/#{params[:attID]}/#{params[:userID]}/#{params[:nameThirdPartyApp]}"
payload = { attID: params[:attID], userID: params[:userID], nameThirdPartyApp: params[:nameThirdPartyApp] }
cached_token = encode_token(payload, params[:nameThirdPartyApp])
Rails.cache.write(cache_key, cached_token, expires_in: 48.hours)
cached_token.token
end
Expand Down
9 changes: 6 additions & 3 deletions app/models/cached_token_third_party_app.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# frozen_string_literal: true

class CachedTokenThirdPartyApp
attr_accessor :token, :counter
attr_accessor :token, :counter, :name_tpa

def initialize(token, counter)
def initialize(token, counter, name_tpa)
@token = token
@counter = counter
@name_tpa = name_tpa
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,10 @@ export default class ResearchPlanDetailsAttachments extends Component {
});
}

getAttachmentToken(attachment) {
getAttachmentToken(attachment, nameThirdPartyApp) {
const userID = this.state.currentUser.user.id;
const attID = attachment.id;
return ThirdPartyAppFetcher.fetchAttachmentToken(attID, userID)
return ThirdPartyAppFetcher.fetchAttachmentToken(attID, userID, nameThirdPartyApp)
.then((result) => {
this.setState({
attachmentToken: result
Expand All @@ -282,8 +282,8 @@ export default class ResearchPlanDetailsAttachments extends Component {

const currentAttachment = arrIdx.find(entry => entry.id === attachment.id);
const index = currentAttachment.i;

this.getAttachmentToken(attachment)
const nameThirdPartyApp = this.state.thirdPartyAppNames[index]
this.getAttachmentToken(attachment, nameThirdPartyApp)
.then(() => {
const ip = this.state.thirdPartyAppIPList[attachment.id];

Expand Down
4 changes: 2 additions & 2 deletions app/packs/src/fetchers/ThirdPartyAppFetcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ export default class ThirdPartyAppFetcher {
.catch((errorMessage) => { console.log(errorMessage); });
}

static fetchAttachmentToken(attID, userID) {
const obj = { attID, userID };
static fetchAttachmentToken(attID, userID, nameThirdPartyApp) {
const obj = { attID, userID, nameThirdPartyApp };
const queryParams = new URLSearchParams(obj).toString();
const url = `/api/v1/third_party_apps/Token.json?${queryParams}`;
return fetch(url, {
Expand Down
50 changes: 27 additions & 23 deletions spec/api/chemotion/third_party_app_api_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@
{
attID: 1,
userID: user_id,
nameThirdPartyApp: 'fakeName'
}
end

Expand Down Expand Up @@ -252,22 +253,18 @@
{
attID: attachment.id,
userID: user.id,
nameThirdPartyApp: 'fakeDownload',
}
end

it 'download a file' do
payload = { attID: params_token[:attID], userID: params_token[:userID]}
cache_key = "token/#{params_token[:attID]}/#{params_token[:userID]}"
cached_token = Rails.cache.read(cache_key)
if cached_token.nil?
payload = { attID: params_token[:attID], userID: params_token[:userID] }
secret = Rails.application.secrets.secret_key_base
token = JWT.encode(payload, secret, 'HS256')
token_class = CachedTokenThirdPartyApp.new(token, 0)
Rails.cache.write(cache_key, token_class, expires_in: 48.hours)
end
payload = { attID: params_token[:attID], userID: params_token[:userID],
nameThirdPartyApp: params_token[:nameThirdPartyApp] }
cache_key = "token/#{params_token[:attID]}/#{params_token[:userID]}/#{params_token[:nameThirdPartyApp]}"
secret = Rails.application.secrets.secret_key_base
token = JWT.encode payload, secret, 'HS256'
token = JWT.encode(payload, secret, 'HS256')
token_class = CachedTokenThirdPartyApp.new(token, 0, 'fakeDownload')
Rails.cache.write(cache_key, token_class, expires_in: 48.hours)
params = {token: token}
file = File.open('spec/fixtures/upload.csv')
file_content = file.read
Expand All @@ -276,9 +273,20 @@
res = response.body
expect(res).to eq(file_content)
end

it 'download a file with an invalid token (not in cache)' do
payload_invalid = { attID: params_token[:attID], userID: params_token[:userID],
nameThirdPartyApp: "Invalid" }
secret_invalid = Rails.application.secrets.secret_key_base
token_invalid = JWT.encode(payload_invalid, secret_invalid, 'HS256')
params_invalid = {token: token_invalid}
get '/api/v1/public_third_party_app/download', params: params_invalid
res_invalid = response.body
expect(res_invalid).to eq("{\"error\":\"Invalid token\"}")
end
end

describe 'upload a file to the ELN' , type: :request do
describe 'upload a file to the ELN', type: :request do
let(:user) { create(:person) }
let!(:attachment) do
create(
Expand All @@ -293,22 +301,18 @@
{
attID: attachment.id,
userID: user.id,
nameThirdPartyApp: 'fakeUpload',
}
end

it 'upload a file' do
payload = { attID: params_token[:attID], userID: params_token[:userID]}
cache_key = "token/#{params_token[:attID]}/#{params_token[:userID]}"
cached_token = Rails.cache.read(cache_key)
if cached_token.nil?
payload = { attID: params_token[:attID], userID: params_token[:userID] }
secret = Rails.application.secrets.secret_key_base
token = JWT.encode(payload, secret, 'HS256')
token_class = CachedTokenThirdPartyApp.new(token, 0)
Rails.cache.write(cache_key, token_class, expires_in: 48.hours)
end
payload = { attID: params_token[:attID], userID: params_token[:userID],
nameThirdPartyApp: params_token[:nameThirdPartyApp] }
cache_key = "token/#{params_token[:attID]}/#{params_token[:userID]}/#{params_token[:nameThirdPartyApp]}"
secret = Rails.application.secrets.secret_key_base
token = JWT.encode payload, secret, 'HS256'
token = JWT.encode(payload, secret, 'HS256')
token_class = CachedTokenThirdPartyApp.new(token, 0, 'fakeUpload')
Rails.cache.write(cache_key, token_class, expires_in: 48.hours)
file_path = 'spec/fixtures/upload.csv'
file = Rack::Test::UploadedFile.new(file_path, 'spec/fixtures/upload2.csv')
params = {token: token, attachmentName: 'NewName', file: file, fileType: '.csv'}
Expand Down

0 comments on commit 236f34a

Please sign in to comment.