From 964ce721494b8cf97541741c4bea67261e25f5cb Mon Sep 17 00:00:00 2001 From: Anthony Truskinger Date: Thu, 7 Apr 2022 04:42:35 +0000 Subject: [PATCH] Fixes username and password auth for downloader script Fixes #570 --- app/modules/include_controller.rb | 8 ++-- .../downloader/download_audio_files_ps1.erb | 29 ++++++++++--- spec/requests/application_spec.rb | 19 +++++++++ .../audio_recordings/downloader_spec.rb | 41 +++++++++++++++++++ 4 files changed, 89 insertions(+), 8 deletions(-) diff --git a/app/modules/include_controller.rb b/app/modules/include_controller.rb index 1b8469d1..097c939d 100644 --- a/app/modules/include_controller.rb +++ b/app/modules/include_controller.rb @@ -256,8 +256,8 @@ def render_error(status_symbol, detail_message, error, method_name, options = {} error, env: request.env, data: { - method_name: method_name, - json_response: json_response + method_name:, + json_response: } ) end @@ -346,7 +346,9 @@ def validate_contains_post_params # json or form encoded with text/plain content type will have the form {} return unless request.POST.values.all?(&:blank?) - if request.body.string.blank? + # sometimes in prod body can be a PhusionPassenger::Utils::TeeInput which does not have a `string` method + body = request.body + if body.respond_to?(:string) && body.string.blank? message = 'Request body was empty' status = :bad_request # include link to 'new' endpoint if body was empty diff --git a/app/views/audio_recordings/downloader/download_audio_files_ps1.erb b/app/views/audio_recordings/downloader/download_audio_files_ps1.erb index 1d67fc6f..ec88b97d 100644 --- a/app/views/audio_recordings/downloader/download_audio_files_ps1.erb +++ b/app/views/audio_recordings/downloader/download_audio_files_ps1.erb @@ -30,10 +30,13 @@ $ ./download_audio_files.ps1 param( # Where to download the recordings. Defaults to the present working directory if not specified. $target = $null, - # The username to use to login to the workbench. This value's default value is templated when the script is generated. - $user_name = $null, - # The auth token to use to login to the workbench. You only need to supply an auth token if you want to log in without a password. + # The auth token to use to login to the workbench. You only need to supply an auth token if you want to log in without a password. $auth_token = $null, + # The username to use to login to the workbench. This value's default value is templated when the script is generated. This is not needed if you use an auth_token. + $user_name = $null, + # The poassword to use to login to the workbench. This is not needed if you use an auth_token. + $password = $null, + # The filter to use to select audio recordings to download. This value's default value is templated when the script is generated. # See https://github.com/QutEcoacoustics/baw-server/wiki/API:-Filtering for details on valid filters. This argument # should be a valid JSON encoded string. @@ -87,8 +90,24 @@ Write-Information "Workbench URL: $workbench_url" $headers = @{} if ($null -eq $auth_token) { - $credentials = Get-Credential -Message "Provide credentials for logging into $workbench_url" -UserName $user_name - $json_credentials = $credentials.GetNetworkCredential() | ForEach-Object{ @{"email"=$_.UserName;"password"=$_.Password}} | ConvertTo-Json + if ($user_name -and $password) { + $User = $user_name + $PWord = ConvertTo-SecureString -String $password -AsPlainText -Force + $credentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $PWord + } + else { + $credentials = Get-Credential -Message "Provide credentials for logging into $workbench_url" -UserName $user_name + } + + + $json_credentials = $credentials.GetNetworkCredential() | ForEach-Object{ + if ($_.UserName -like '*@*') { + @{"email"=$_.UserName;"password"=$_.Password} + } + else { + @{"login"=$_.UserName;"password"=$_.Password} + } + } Write-Information "Logging in to workbench $workbench" diff --git a/spec/requests/application_spec.rb b/spec/requests/application_spec.rb index 3ffc78eb..670e6f7c 100644 --- a/spec/requests/application_spec.rb +++ b/spec/requests/application_spec.rb @@ -27,6 +27,25 @@ expect(response.headers).not_to have_key('X-Error-Message') end + it 'can handle malformed post requests' do + # a json value encoded in a string (malformed) + body = "{\r\n \"password\": \"password\",\r\n \"email\": \"SladeAA\"\r\n}" + post '/security', params: body, + headers: { + 'CONTENT_TYPE' => 'application/x-www-form-urlencoded', + 'Accept' => 'application/json', + 'CONTENT_LENGTH' => 56 + } + + expect(response).to have_http_status(:unsupported_media_type) + expect(response.headers).not_to have_key('X-Error-Message') + expect(response.content_type).to include('application/json') + parsed_response = JSON.parse(response.body) + expect(parsed_response['meta']['error']['details']).to eq( + 'Failed to parse the request body. Ensure that it is formed correctly and matches the content-type (application/x-www-form-urlencoded)' + ) + end + describe 'filtering tests' do create_entire_hierarchy diff --git a/spec/requests/audio_recordings/downloader_spec.rb b/spec/requests/audio_recordings/downloader_spec.rb index af86fb12..2aac8134 100644 --- a/spec/requests/audio_recordings/downloader_spec.rb +++ b/spec/requests/audio_recordings/downloader_spec.rb @@ -193,6 +193,47 @@ def prepare_audio_file(audio_recording) expect(files.size).to eq(11) expect(files.map(&:to_s)).to all(end_with('.mp3')) end + + it 'downloads the files (with username and password)', web_server_timeout: 30 do + logger.measure_info('downloading script') do + out_and_err, status = Open3.capture2e( + 'curl -JO localhost:3000/audio_recordings/downloader?items=2', + chdir: BawApp.tmp_dir + ) + logger.info(out_and_err, status:) + end + + script.chmod(0o764) + + script_output = '' + + logger.measure_info('running download script') do + logger.tagged('download script output') do + # we need a promise here to force the process wait onto another thread + # without it, our async web server (from expose_app_as_web_server above) crashes with + # Errno::EBADF: + # Bad file descriptor - epoll_ctl(process_wait) + Concurrent::Promises.future { + script_output, status = Open3.capture2e( + 'pwsh download_audio_files.ps1 -target downloader_test -user_name admin -password password', + chdir: BawApp.tmp_dir, + stdin_data: "password\n" + ) + logger.info(script_output, status:) + }.run.wait! + end + end + + expect(script_output).to match( + "Downloading recordings\nGetting page 1\nGot page 1 of 6, 2 recordings in this page.\nDownloading recording" + ) + expect(script_output).to match('Got page 6 of 6, 1 recordings in this page.') + expect(script_output).to match(%r{Downloaded recording \d+ to downloader_test/\d+_sitename\d+/.*.mp3}) + + files = (BawApp.tmp_dir / 'downloader_test').glob('**/*.mp3') + expect(files.size).to eq(11) + expect(files.map(&:to_s)).to all(end_with('.mp3')) + end end end end