Feat: authenticate direct upload (#4160)
This commit is contained in:
parent
796a7805db
commit
207a03155e
12 changed files with 174 additions and 57 deletions
|
@ -1,32 +1,6 @@
|
||||||
class Api::V1::Accounts::BaseController < Api::BaseController
|
class Api::V1::Accounts::BaseController < Api::BaseController
|
||||||
include SwitchLocale
|
include SwitchLocale
|
||||||
|
include EnsureCurrentAccountHelper
|
||||||
before_action :current_account
|
before_action :current_account
|
||||||
around_action :switch_locale_using_account_locale
|
around_action :switch_locale_using_account_locale
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def current_account
|
|
||||||
@current_account ||= ensure_current_account
|
|
||||||
Current.account = @current_account
|
|
||||||
end
|
|
||||||
|
|
||||||
def ensure_current_account
|
|
||||||
account = Account.find(params[:account_id])
|
|
||||||
if current_user
|
|
||||||
account_accessible_for_user?(account)
|
|
||||||
elsif @resource.is_a?(AgentBot)
|
|
||||||
account_accessible_for_bot?(account)
|
|
||||||
end
|
|
||||||
account
|
|
||||||
end
|
|
||||||
|
|
||||||
def account_accessible_for_user?(account)
|
|
||||||
@current_account_user = account.account_users.find_by(user_id: current_user.id)
|
|
||||||
Current.account_user = @current_account_user
|
|
||||||
render_unauthorized('You are not authorized to access this account') unless @current_account_user
|
|
||||||
end
|
|
||||||
|
|
||||||
def account_accessible_for_bot?(account)
|
|
||||||
render_unauthorized('You are not authorized to access this account') unless @resource.agent_bot_inboxes.find_by(account_id: account.id)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
class Api::V1::Accounts::Conversations::BaseController < Api::V1::Accounts::BaseController
|
class Api::V1::Accounts::Conversations::BaseController < Api::V1::Accounts::BaseController
|
||||||
|
include EnsureCurrentAccountHelper
|
||||||
before_action :conversation
|
before_action :conversation
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
class Api::V1::Accounts::Conversations::DirectUploadsController < ActiveStorage::DirectUploadsController
|
||||||
|
include EnsureCurrentAccountHelper
|
||||||
|
before_action :current_account
|
||||||
|
before_action :conversation
|
||||||
|
|
||||||
|
def create
|
||||||
|
return if @conversation.nil? || @current_account.nil?
|
||||||
|
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def conversation
|
||||||
|
@conversation ||= Current.account.conversations.find_by(display_id: params[:conversation_id])
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,5 +1,6 @@
|
||||||
class Api::V1::Widget::BaseController < ApplicationController
|
class Api::V1::Widget::BaseController < ApplicationController
|
||||||
include SwitchLocale
|
include SwitchLocale
|
||||||
|
include WebsiteTokenHelper
|
||||||
|
|
||||||
before_action :set_web_widget
|
before_action :set_web_widget
|
||||||
before_action :set_contact
|
before_action :set_contact
|
||||||
|
@ -19,25 +20,6 @@ class Api::V1::Widget::BaseController < ApplicationController
|
||||||
@conversation ||= conversations.last
|
@conversation ||= conversations.last
|
||||||
end
|
end
|
||||||
|
|
||||||
def auth_token_params
|
|
||||||
@auth_token_params ||= ::Widget::TokenService.new(token: request.headers['X-Auth-Token']).decode_token
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_web_widget
|
|
||||||
@web_widget = ::Channel::WebWidget.find_by!(website_token: permitted_params[:website_token])
|
|
||||||
@current_account = @web_widget.account
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_contact
|
|
||||||
@contact_inbox = @web_widget.inbox.contact_inboxes.find_by(
|
|
||||||
source_id: auth_token_params[:source_id]
|
|
||||||
)
|
|
||||||
@contact = @contact_inbox&.contact
|
|
||||||
raise ActiveRecord::RecordNotFound unless @contact
|
|
||||||
|
|
||||||
Current.contact = @contact
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_conversation
|
def create_conversation
|
||||||
::Conversation.create!(conversation_params)
|
::Conversation.create!(conversation_params)
|
||||||
end
|
end
|
||||||
|
@ -96,10 +78,6 @@ class Api::V1::Widget::BaseController < ApplicationController
|
||||||
{ timestamp: permitted_params[:message][:timestamp] }
|
{ timestamp: permitted_params[:message][:timestamp] }
|
||||||
end
|
end
|
||||||
|
|
||||||
def permitted_params
|
|
||||||
params.permit(:website_token)
|
|
||||||
end
|
|
||||||
|
|
||||||
def message_params
|
def message_params
|
||||||
{
|
{
|
||||||
account_id: conversation.account_id,
|
account_id: conversation.account_id,
|
||||||
|
|
11
app/controllers/api/v1/widget/direct_uploads_controller.rb
Normal file
11
app/controllers/api/v1/widget/direct_uploads_controller.rb
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
class Api::V1::Widget::DirectUploadsController < ActiveStorage::DirectUploadsController
|
||||||
|
include WebsiteTokenHelper
|
||||||
|
before_action :set_web_widget
|
||||||
|
before_action :set_contact
|
||||||
|
|
||||||
|
def create
|
||||||
|
return if @contact.nil? || @current_account.nil?
|
||||||
|
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
28
app/controllers/concerns/ensure_current_account_helper.rb
Normal file
28
app/controllers/concerns/ensure_current_account_helper.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
module EnsureCurrentAccountHelper
|
||||||
|
private
|
||||||
|
|
||||||
|
def current_account
|
||||||
|
@current_account ||= ensure_current_account
|
||||||
|
Current.account = @current_account
|
||||||
|
end
|
||||||
|
|
||||||
|
def ensure_current_account
|
||||||
|
account = Account.find(params[:account_id])
|
||||||
|
if current_user
|
||||||
|
account_accessible_for_user?(account)
|
||||||
|
elsif @resource.is_a?(AgentBot)
|
||||||
|
account_accessible_for_bot?(account)
|
||||||
|
end
|
||||||
|
account
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_accessible_for_user?(account)
|
||||||
|
@current_account_user = account.account_users.find_by(user_id: current_user.id)
|
||||||
|
Current.account_user = @current_account_user
|
||||||
|
render_unauthorized('You are not authorized to access this account') unless @current_account_user
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_accessible_for_bot?(account)
|
||||||
|
render_unauthorized('You are not authorized to access this account') unless @resource.agent_bot_inboxes.find_by(account_id: account.id)
|
||||||
|
end
|
||||||
|
end
|
22
app/controllers/concerns/website_token_helper.rb
Normal file
22
app/controllers/concerns/website_token_helper.rb
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
module WebsiteTokenHelper
|
||||||
|
def auth_token_params
|
||||||
|
@auth_token_params ||= ::Widget::TokenService.new(token: request.headers['X-Auth-Token']).decode_token
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_web_widget
|
||||||
|
@web_widget = ::Channel::WebWidget.find_by!(website_token: permitted_params[:website_token])
|
||||||
|
@current_account = @web_widget.account
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_contact
|
||||||
|
@contact_inbox = @web_widget.inbox.contact_inboxes.find_by(
|
||||||
|
source_id: auth_token_params[:source_id]
|
||||||
|
)
|
||||||
|
@contact = @contact_inbox&.contact
|
||||||
|
raise ActiveRecord::RecordNotFound unless @contact
|
||||||
|
end
|
||||||
|
|
||||||
|
def permitted_params
|
||||||
|
params.permit(:website_token)
|
||||||
|
end
|
||||||
|
end
|
|
@ -645,10 +645,17 @@ export default {
|
||||||
if (checkFileSizeLimit(file, MAXIMUM_FILE_UPLOAD_SIZE)) {
|
if (checkFileSizeLimit(file, MAXIMUM_FILE_UPLOAD_SIZE)) {
|
||||||
const upload = new DirectUpload(
|
const upload = new DirectUpload(
|
||||||
file.file,
|
file.file,
|
||||||
'/rails/active_storage/direct_uploads',
|
`/api/v1/accounts/${this.accountId}/conversations/${this.currentChat.id}/direct_uploads`,
|
||||||
null,
|
{
|
||||||
file.file.name
|
directUploadWillCreateBlobWithXHR: xhr => {
|
||||||
|
xhr.setRequestHeader(
|
||||||
|
'api_access_token',
|
||||||
|
this.currentUser.access_token
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
upload.create((error, blob) => {
|
upload.create((error, blob) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
this.showAlert(error);
|
this.showAlert(error);
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
:size="4096 * 2048"
|
:size="4096 * 2048"
|
||||||
:accept="allowedFileTypes"
|
:accept="allowedFileTypes"
|
||||||
:data="{
|
:data="{
|
||||||
direct_upload_url: '/rails/active_storage/direct_uploads',
|
direct_upload_url: '/api/v1/widget/direct_uploads',
|
||||||
direct_upload: true,
|
direct_upload: true,
|
||||||
}"
|
}"
|
||||||
@input-file="onFileUpload"
|
@input-file="onFileUpload"
|
||||||
|
@ -66,11 +66,15 @@ export default {
|
||||||
this.isUploading = true;
|
this.isUploading = true;
|
||||||
try {
|
try {
|
||||||
if (checkFileSizeLimit(file, MAXIMUM_FILE_UPLOAD_SIZE)) {
|
if (checkFileSizeLimit(file, MAXIMUM_FILE_UPLOAD_SIZE)) {
|
||||||
|
const { websiteToken } = window.chatwootWebChannel;
|
||||||
const upload = new DirectUpload(
|
const upload = new DirectUpload(
|
||||||
file.file,
|
file.file,
|
||||||
'/rails/active_storage/direct_uploads',
|
`/api/v1/widget/direct_uploads?website_token=${websiteToken}`,
|
||||||
null,
|
{
|
||||||
file.file.name
|
directUploadWillCreateBlobWithXHR: xhr => {
|
||||||
|
xhr.setRequestHeader('X-Auth-Token', window.authToken);
|
||||||
|
},
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
upload.create((error, blob) => {
|
upload.create((error, blob) => {
|
||||||
|
|
|
@ -71,6 +71,7 @@ Rails.application.routes.draw do
|
||||||
resources :messages, only: [:index, :create, :destroy]
|
resources :messages, only: [:index, :create, :destroy]
|
||||||
resources :assignments, only: [:create]
|
resources :assignments, only: [:create]
|
||||||
resources :labels, only: [:create, :index]
|
resources :labels, only: [:create, :index]
|
||||||
|
resource :direct_uploads, only: [:create]
|
||||||
end
|
end
|
||||||
member do
|
member do
|
||||||
post :mute
|
post :mute
|
||||||
|
@ -179,6 +180,7 @@ Rails.application.routes.draw do
|
||||||
resource :notification_subscriptions, only: [:create, :destroy]
|
resource :notification_subscriptions, only: [:create, :destroy]
|
||||||
|
|
||||||
namespace :widget do
|
namespace :widget do
|
||||||
|
resource :direct_uploads, only: [:create]
|
||||||
resource :config, only: [:create]
|
resource :config, only: [:create]
|
||||||
resources :campaigns, only: [:index]
|
resources :campaigns, only: [:index]
|
||||||
resources :events, only: [:create]
|
resources :events, only: [:create]
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe '/api/v1/accounts/:account_id/conversations/:conversation_id/direct_uploads', type: :request do
|
||||||
|
let(:account) { create(:account) }
|
||||||
|
let(:web_widget) { create(:channel_widget, account: account) }
|
||||||
|
let(:agent) { create(:user, account: account, role: :agent) }
|
||||||
|
let(:contact) { create(:contact, account: account, email: nil) }
|
||||||
|
let(:contact_inbox) { create(:contact_inbox, contact: contact, inbox: web_widget.inbox) }
|
||||||
|
let(:conversation) { create(:conversation, contact: contact, account: account, inbox: web_widget.inbox, contact_inbox: contact_inbox) }
|
||||||
|
|
||||||
|
describe 'POST /api/v1/accounts/:account_id/conversations/:conversation_id/direct_uploads' do
|
||||||
|
context 'when post request is made' do
|
||||||
|
it 'creates attachment message in conversation' do
|
||||||
|
contact
|
||||||
|
|
||||||
|
post api_v1_account_conversation_direct_uploads_path(account_id: account.id, conversation_id: conversation.display_id),
|
||||||
|
params: {
|
||||||
|
blob: {
|
||||||
|
filename: 'avatar.png',
|
||||||
|
byte_size: '1234',
|
||||||
|
checksum: 'dsjbsdhbfif3874823mnsdbf',
|
||||||
|
content_type: 'image/png'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
headers: { api_access_token: agent.access_token.token },
|
||||||
|
as: :json
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
json_response = JSON.parse(response.body)
|
||||||
|
expect(json_response['content_type']).to eq('image/png')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,39 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe '/api/v1/widget/direct_uploads', type: :request do
|
||||||
|
let(:account) { create(:account) }
|
||||||
|
let(:web_widget) { create(:channel_widget, account: account) }
|
||||||
|
let(:contact) { create(:contact, account: account, email: nil) }
|
||||||
|
let(:contact_inbox) { create(:contact_inbox, contact: contact, inbox: web_widget.inbox) }
|
||||||
|
let(:conversation) { create(:conversation, contact: contact, account: account, inbox: web_widget.inbox, contact_inbox: contact_inbox) }
|
||||||
|
let(:payload) { { source_id: contact_inbox.source_id, inbox_id: web_widget.inbox.id } }
|
||||||
|
let(:token) { ::Widget::TokenService.new(payload: payload).generate_token }
|
||||||
|
|
||||||
|
describe 'POST /api/v1/widget/direct_uploads' do
|
||||||
|
context 'when post request is made' do
|
||||||
|
before do
|
||||||
|
token
|
||||||
|
contact
|
||||||
|
payload
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates attachment message in conversation' do
|
||||||
|
post api_v1_widget_direct_uploads_url,
|
||||||
|
params: {
|
||||||
|
website_token: web_widget.website_token,
|
||||||
|
blob: {
|
||||||
|
filename: 'avatar.png',
|
||||||
|
byte_size: '1234',
|
||||||
|
checksum: 'dsjbsdhbfif3874823mnsdbf',
|
||||||
|
content_type: 'image/png'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
headers: { 'X-Auth-Token' => token }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
json_response = JSON.parse(response.body)
|
||||||
|
expect(json_response['content_type']).to eq('image/png')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue