From c1ae4360a33fbaeb7bef8bc31bc7ccca4beb7dd8 Mon Sep 17 00:00:00 2001 From: Jure Grabnar Date: Fri, 23 Sep 2016 10:27:30 +0200 Subject: [PATCH] Refactor WopiController --- app/controllers/wopi_controller.rb | 368 ++++++++++++++--------------- config/routes.rb | 9 +- 2 files changed, 178 insertions(+), 199 deletions(-) diff --git a/app/controllers/wopi_controller.rb b/app/controllers/wopi_controller.rb index e63ff2472..b18e234ea 100644 --- a/app/controllers/wopi_controller.rb +++ b/app/controllers/wopi_controller.rb @@ -1,251 +1,232 @@ class WopiController < ActionController::Base include WopiUtil - before_action :load_vars,:authenticate_user_from_token! + before_action :load_vars, :authenticate_user_from_token! before_action :verify_proof! - def get_file_endpoint - logger.warn "get_file called" - #Only used for checkfileinfo + # Only used for checkfileinfo + def file_get_endpoint check_file_info end - def get_file_contents_endpoint - logger.warn "get_file_contents called" - #Only used for getfile - get_file - + def file_contents_get_endpoint + # get_file + response.headers['X-WOPI-ItemVersion'] = @asset.version + response.body = Paperclip.io_adapters.for(@asset.file).read + send_data response.body, disposition: 'inline', content_type: 'text/plain' end def post_file_endpoint - logger.warn "post_file called" - override = request.headers["X-WOPI-Override"] + override = request.headers['X-WOPI-Override'] case override - when "GET_LOCK" - get_lock - when "PUT_RELATIVE" - put_relative - when "LOCK" - old_lock = request.headers["X-WOPI-OldLock"] - if old_lock.nil? - lock - else - unlock_and_relock - end - when "UNLOCK" - unlock - when "REFRESH_LOCK" - refresh_lock - when "GET_SHARE_URL" - render :nothing => true, :status => 501 and return + when 'GET_LOCK' + get_lock + when 'PUT_RELATIVE' + put_relative + when 'LOCK' + old_lock = request.headers['X-WOPI-OldLock'] + if old_lock.nil? + lock else - render :nothing => true, :status => 404 and return + unlock_and_relock end + when 'UNLOCK' + unlock + when 'REFRESH_LOCK' + refresh_lock + when 'GET_SHARE_URL' + render nothing: :true, status: 501 and return + else + render nothing: :true, status: 404 and return + end end - def post_file_contents_endpoint - logger.warn "post_file_contents called" - #Only used for putfile + # Only used for putfile + def file_contents_post_endpoint + logger.warn 'WOPI: post_file_contents called' put_file end def check_file_info - logger.warn "Check file info started" - msg = { :BaseFileName => @asset.file_file_name, - :OwnerId => @asset.created_by_id.to_s, - :Size => @asset.file_file_size, - :UserId => @user.id, - :Version => @asset.version, - :SupportsExtendedLockLength => true, - :SupportsGetLock => true, - :SupportsLocks => true, - :SupportsUpdate => true, - #Setting all users to business until we figure out which should NOT be business - :LicenseCheckForEditIsEnabled => true, - :UserFriendlyName => @user.name, - #TODO Check user permisisons - :ReadOnly => false, - :UserCanNotWriteRelative => true, - :UserCanWrite => true, - #TODO decide what to put here - :CloseUrl => "https://scinote-preview.herokuapp.com", - :DownloadUrl => url_for(controller: 'assets',action: 'download',id: @asset.id), - :HostEditUrl => url_for(controller: 'assets',action: 'edit',id: @asset.id), - :HostViewUrl => url_for(controller: 'assets',action: 'view',id: @asset.id) - #TODO breadcrumbs? - #:FileExtension - } - response.headers['X-WOPI-HostEndpoint'] = ENV["WOPI_ENDPOINT_URL"] - response.headers['X-WOPI-MachineName'] = ENV["WOPI_ENDPOINT_URL"] - response.headers['X-WOPI-ServerVersion'] = APP_VERSION - render json:msg and return - - end - - def get_file - logger.warn "getting file" - response.headers["X-WOPI-ItemVersion"] = @asset.version - response.body = Paperclip.io_adapters.for(@asset.file).read - send_data response.body, disposition: "inline", :content_type => 'text/plain' + msg = { + BaseFileName: @asset.file_file_name, + OwnerId: @asset.created_by_id.to_s, + Size: @asset.file_file_size, + UserId: @user.id, + Version: @asset.version, + SupportsExtendedLockLength: true, + SupportsGetLock: true, + SupportsLocks: true, + SupportsUpdate: true, + # Setting all users to business until we figure out + # which should NOT be business + LicenseCheckForEditIsEnabled: true, + UserFriendlyName: @user.name, + # TODO: Check user permisisons + ReadOnly: false, + UserCanNotWriteRelative: true, + UserCanWrite: true, + # TODO: decide what to put here + CloseUrl: 'https://scinote-preview.herokuapp.com', + DownloadUrl: url_for(controller: 'assets', action: 'download', + id: @asset.id), + HostEditUrl: url_for(controller: 'assets', action: 'edit', + id: @asset.id), + HostViewUrl: url_for(controller: 'assets', action: 'view', + id: @asset.id) + # TODO: breadcrumbs? + #:FileExtension + } + response.headers['X-WOPI-HostEndpoint'] = ENV['WOPI_ENDPOINT_URL'] + response.headers['X-WOPI-MachineName'] = ENV['WOPI_ENDPOINT_URL'] + response.headers['X-WOPI-ServerVersion'] = APP_VERSION + render json: msg and return end def put_relative - logger.warn "put relative" - render :nothing => true, :status => 501 and return + render nothing: :true, status: 501 and return end def lock - logger.warn "lock" - lock = request.headers["X-WOPI-Lock"] - if lock.nil? || lock.blank? - render :nothing => true, :status => 400 and return - end + lock = request.headers['X-WOPI-Lock'] + logger.warn 'WOPI: lock; ' + lock.to_s + render nothing: :true, status: 404 and return if lock.nil? || lock.blank? @asset.with_lock do if @asset.is_locked if @asset.lock == lock @asset.refresh_lock - response.headers["X-WOPI-ItemVersion"] = @asset.version - render :nothing => true, :status => 200 and return + response.headers['X-WOPI-ItemVersion'] = @asset.version + render nothing: :true, status: 200 and return else - response.headers["X-WOPI-Lock"] = @asset.lock - render :nothing => true, :status => 409 and return + response.headers['X-WOPI-Lock'] = @asset.lock + render nothing: :true, status: 409 and return end else @asset.lock_asset(lock) - response.headers["X-WOPI-ItemVersion"] = @asset.version - render :nothing => true, :status => 200 and return + response.headers['X-WOPI-ItemVersion'] = @asset.version + render nothing: :true, status: 200 and return end end end def unlock_and_relock - logger.warn "lock and relock" - lock = request.headers["X-WOPI-Lock"] - old_lock = request.headers["X-WOPI-OldLock"] + logger.warn 'lock and relock' + lock = request.headers['X-WOPI-Lock'] + old_lock = request.headers['X-WOPI-OldLock'] if lock.nil? || lock.blank? || old_lock.blank? - render :nothing => true, :status => 400 and return + render nothing: :true, status: 400 and return end @asset.with_lock do if @asset.is_locked if @asset.lock == old_lock @asset.unlock @asset.lock_asset(lock) - response.headers["X-WOPI-ItemVersion"] = @asset.version - render :nothing => true, :status => 200 and return + response.headers['X-WOPI-ItemVersion'] = @asset.version + render nothing: :true, status: 200 and return else - response.headers["X-WOPI-Lock"] = @asset.lock - render :nothing => true, :status => 409 and return + response.headers['X-WOPI-Lock'] = @asset.lock + render nothing: :true, status: 409 and return end else - response.headers["X-WOPI-Lock"] = "" - render :nothing => true, :status => 409 and return + response.headers['X-WOPI-Lock'] = '' + render nothing: :true, status: 409 and return end end end def unlock - logger.warn "unlock" - lock = request.headers["X-WOPI-Lock"] - if lock.nil? || lock.blank? - render :nothing => true, :status => 400 and return - end + lock = request.headers['X-WOPI-Lock'] + render nothing: :true, status: 400 and return if lock.nil? || lock.blank? @asset.with_lock do if @asset.is_locked - logger.warn "Current asset lock: #{@asset.lock}, unlocking lock #{lock}" + logger.warn 'WOPI: current asset lock: #{@asset.lock}, + unlocking lock #{lock}' if @asset.lock == lock @asset.unlock - response.headers["X-WOPI-ItemVersion"] = @asset.version - render :nothing => true, :status => 200 and return + response.headers['X-WOPI-ItemVersion'] = @asset.version + render nothing: :true, status: 200 and return else - response.headers["X-WOPI-Lock"] = @asset.lock - render :nothing => true, :status => 409 and return + response.headers['X-WOPI-Lock'] = @asset.lock + render nothing: :true, status: 409 and return end else - logger.warn "Tried to unlock non-locked file" - response.headers["X-WOPI-Lock"] = " " - render :nothing => true, :status => 409 and return + logger.warn 'WOPI: tried to unlock non-locked file' + response.headers['X-WOPI-Lock'] = ' ' + render nothing: :true, status: 409 and return end end end def refresh_lock - logger.warn "refresh lock" - lock = request.headers["X-WOPI-Lock"] - if lock.nil? || lock.blank? - render :nothing => true, :status => 400 and return - end + lock = request.headers['X-WOPI-Lock'] + render nothing: :true, status: 400 and return if lock.nil? || lock.blank? @asset.with_lock do if @asset.is_locked if @asset.lock == lock @asset.refresh_lock - response.headers["X-WOPI-ItemVersion"] = @asset.version - response.headers["X-WOPI-ItemVersion"] = @asset.version - render :nothing => true, :status => 200 and return + response.headers['X-WOPI-ItemVersion'] = @asset.version + response.headers['X-WOPI-ItemVersion'] = @asset.version + render nothing: :true, status: 200 and return else - response.headers["X-WOPI-Lock"] = @asset.lock - render :nothing => true, :status => 409 and return + response.headers['X-WOPI-Lock'] = @asset.lock + render nothing: :true, status: 409 and return end else - response.headers["X-WOPI-Lock"] = "" - render :nothing => true, :status => 409 and return + response.headers['X-WOPI-Lock'] = '' + render nothing: :true, status: 409 and return end end end def get_lock - logger.warn "get lock" @asset.with_lock do if @asset.is_locked - response.headers["X-WOPI-Lock"] = @asset.lock - render :nothing => true, :status => 200 and return + response.headers['X-WOPI-Lock'] = @asset.lock else - response.headers["X-WOPI-Lock"] = "" - render :nothing => true, :status => 200 and return - end - end - end - # TODO When should we extract file text? - def put_file - logger.warn "put file" - @asset.with_lock do - lock = request.headers["X-WOPI-Lock"] - if @asset.is_locked - if @asset.lock == lock - logger.warn "replacing file" - @asset.update_contents(request.body) - response.headers["X-WOPI-ItemVersion"] = @asset.version - render :nothing => true, :status => 200 and return - else - logger.warn "wrong lock used to try and modify file" - response.headers["X-WOPI-Lock"] = @asset.lock - render :nothing => true, :status => 409 and return - end - else - if !@asset.file_file_size.nil? and @asset.file_file_size==0 - logger.warn "initializing empty file" - @asset.update_contents(request.body) - response.headers["X-WOPI-ItemVersion"] = @asset.version - render :nothing => true, :status => 200 and return - else - logger.warn "trying to modify unlocked file" - response.headers["X-WOPI-Lock"] = "" - render :nothing => true, :status => 409 and return - end + response.headers['X-WOPI-Lock'] = '' end + render nothing: :true, status: 200 and return end end + # TODO: When should we extract file text? + def put_file + @asset.with_lock do + lock = request.headers['X-WOPI-Lock'] + if @asset.is_locked + if @asset.lock == lock + logger.warn 'WOPI: replacing file' + @asset.update_contents(request.body) + response.headers['X-WOPI-ItemVersion'] = @asset.version + render nothing: :true, status: 200 and return + else + logger.warn 'WOPI: wrong lock used to try and modify file' + response.headers['X-WOPI-Lock'] = @asset.lock + render nothing: :true, status: 409 and return + end + elsif !@asset.file_file_size.nil? && @asset.file_file_size.zero? + logger.warn 'WOPI: initializing empty file' + @asset.update_contents(request.body) + response.headers['X-WOPI-ItemVersion'] = @asset.version + render nothing: :true, status: 200 and return + else + logger.warn 'WOPI: trying to modify unlocked file' + response.headers['X-WOPI-Lock'] = '' + render nothing: :true, status: 409 and return + end + end + end def load_vars @asset = Asset.find_by_id(params[:id]) if @asset.nil? - render :nothing => true, :status => 404 and return + render nothing: :true, status: 404 and return else - logger.warn "Found asset" + logger.warn 'Found asset' step_assoc = @asset.step result_assoc = @asset.result - @assoc = step_assoc if not step_assoc.nil? - @assoc = result_assoc if not result_assoc.nil? + @assoc = step_assoc unless step_assoc.nil? + @assoc = result_assoc unless result_assoc.nil? if @assoc.class == Step @protocol = @asset.step.protocol @@ -256,47 +237,46 @@ class WopiController < ActionController::Base end private - def authenticate_user_from_token! - wopi_token = params[:access_token] - if wopi_token.nil? - logger.warn "nil wopi token" - render :nothing => true, :status => 401 and return - end - @user = User.find_by_valid_wopi_token(wopi_token) - if @user.nil? - logger.warn "no user with this token found" - render :nothing => true, :status => 401 and return - end - logger.warn "user found by token" - - #TODO check if the user can do anything with the file + def authenticate_user_from_token! + wopi_token = params[:access_token] + if wopi_token.nil? + logger.warn 'WOPI: nil wopi token' + render nothing: :true, status: 401 and return end - def verify_proof! - begin - token = params[:access_token].encode('utf-8') - timestamp = request.headers['X-WOPI-TimeStamp'].to_i - signed_proof = request.headers['X-WOPI-Proof'] - signed_proof_old = request.headers['X-WOPI-ProofOld'] - url = request.original_url.upcase.encode('utf-8') - - if convert_to_unix_timestamp(timestamp) + 20.minutes >= Time.now - if get_discovery.verify_proof(token, timestamp, signed_proof, - signed_proof_old, url) - logger.warn 'Proof verification: successful' - else - logger.warn 'Proof verification: not verified' - render :nothing => true, :status => 500 and return - end - else - logger.warn 'Proof verification: timestamp too old; ' + timestamp.to_s - render :nothing => true, :status => 500 and return - end - rescue => e - logger.warn 'Proof verification: failed; ' + e.message - render :nothing => true, :status => 500 and return - end + @user = User.find_by_valid_wopi_token(wopi_token) + if @user.nil? + logger.warn 'WOPI: no user with this token found' + render nothing: :true, status: 401 and return end + logger.warn 'WOPI: user found by token' + # TODO: check if the user can do anything with the file + end + + def verify_proof! + token = params[:access_token].encode('utf-8') + timestamp = request.headers['X-WOPI-TimeStamp'].to_i + signed_proof = request.headers['X-WOPI-Proof'] + signed_proof_old = request.headers['X-WOPI-ProofOld'] + url = request.original_url.upcase.encode('utf-8') + + if convert_to_unix_timestamp(timestamp) + 20.minutes >= Time.now + if get_discovery.verify_proof(token, timestamp, signed_proof, + signed_proof_old, url) + logger.warn 'WOPI: proof verification: successful' + else + logger.warn 'WOPI: proof verification: not verified' + render nothing: :true, status: 500 and return + end + else + logger.warn 'WOPI: proof verification: timestamp too old; ' + + timestamp.to_s + render nothing: :true, status: 500 and return + end + rescue => e + logger.warn 'WOPI: proof verification: failed; ' + e.message + render nothing: :true, status: 500 and return + end end diff --git a/config/routes.rb b/config/routes.rb index 47c65e092..acf7f6c54 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -267,10 +267,9 @@ Rails.application.routes.draw do end # Office integration - get "wopi/files/:id/contents", to: "wopi#get_file_contents_endpoint" - post "wopi/files/:id/contents", to: "wopi#post_file_contents_endpoint" - - get "wopi/files/:id", to: "wopi#get_file_endpoint", as: 'wopi_rest_endpoint' - post "wopi/files/:id", to: "wopi#post_file_endpoint" + get 'wopi/files/:id/contents', to: 'wopi#file_contents_get_endpoint' + post 'wopi/files/:id/contents', to: 'wopi#file_contents_post_endpoint' + get 'wopi/files/:id', to: 'wopi#file_get_endpoint', as: 'wopi_rest_endpoint' + post 'wopi/files/:id', to: 'wopi#post_file_endpoint' end