diff --git a/lib/redmine_dmsf/webdav/dmsf_controller.rb b/lib/redmine_dmsf/webdav/dmsf_controller.rb index edac3bb1..ef567208 100644 --- a/lib/redmine_dmsf/webdav/dmsf_controller.rb +++ b/lib/redmine_dmsf/webdav/dmsf_controller.rb @@ -69,7 +69,7 @@ module RedmineDmsf nc = params['nc'] user = User.find_by(login: username) unless user - Rails.logger.error 'Digest authentication: provided user name has no match in the DB' + Rails.logger.error "Digest authentication: #{username} not found" raise Unauthorized end unless user.active? @@ -85,7 +85,7 @@ module RedmineDmsf ha1 = user.easy_digest_token else unless token - Rails.logger.error "Digest authentication: no digest found for #{user}" + Rails.logger.error "Digest authentication: no digest found for #{username}" raise Unauthorized end ha1 = token.value @@ -101,6 +101,8 @@ module RedmineDmsf else Rails.logger.error 'Digest authentication: digest response is incorrect' end + else + Rails.logger.error "Digest authentication method expected got #{scheme}" end raise Unauthorized if User.current.anonymous? diff --git a/test/integration/webdav/dmsf_webdav_get_test.rb b/test/integration/webdav/dmsf_webdav_get_test.rb index 110bba24..1d2f8902 100644 --- a/test/integration/webdav/dmsf_webdav_get_test.rb +++ b/test/integration/webdav/dmsf_webdav_get_test.rb @@ -34,6 +34,29 @@ class DmsfWebdavGetTest < RedmineDmsf::Test::IntegrationTest assert_response :unauthorized end + def test_digest_authentication + # Basic + with_settings plugin_redmine_dmsf: { 'dmsf_webdav_authentication' => 'Basic', 'dmsf_webdav' => '1' } do + get '/dmsf/webdav', params: nil, headers: credentials('jsmith', 'jsmith') + assert_response :success + end + # Wrong digest + with_settings plugin_redmine_dmsf: { 'dmsf_webdav_authentication' => 'Digest', 'dmsf_webdav' => '1' } do + get '/dmsf/webdav', params: nil, headers: credentials('jsmith', 'jsmith') + assert_response :unauthorized + end + # Right digest + digest = Digest::MD5.hexdigest("#{@jsmith_user.login}:#{RedmineDmsf::Webdav::AUTHENTICATION_REALM}:jsmith") + token ||= Token.create!(user_id: @jsmith_user.id, action: 'dmsf-webdav-digest') + token.value = digest + assert token.save + authorization = encode_credentials(username: 'jsmith', digest: digest, target: '/dmsf/webdav') + with_settings plugin_redmine_dmsf: { 'dmsf_webdav_authentication' => 'Digest', 'dmsf_webdav' => '1' } do + get '/dmsf/webdav', params: nil, headers: { HTTP_AUTHORIZATION: authorization } + assert_response :success + end + end + def test_should_permit_authenticated_user get '/dmsf/webdav', params: nil, headers: @admin assert_response :success diff --git a/test/integration_test.rb b/test/integration_test.rb index a7d539c3..149bd106 100644 --- a/test/integration_test.rb +++ b/test/integration_test.rb @@ -60,6 +60,7 @@ module RedmineDmsf Setting.plugin_redmine_dmsf['dmsf_webdav_use_project_names'] = nil Setting.plugin_redmine_dmsf['dmsf_projects_as_subfolders'] = nil Setting.plugin_redmine_dmsf['dmsf_storage_directory'] = File.join('files', ['dmsf']) + Setting.plugin_redmine_dmsf['dmsf_webdav_authentication'] = 'Basic' FileUtils.cp_r File.join(File.expand_path('../fixtures/files', __FILE__), '.'), DmsfFile.storage_path User.current = nil end @@ -115,6 +116,32 @@ module RedmineDmsf assert val.blank?, "Expected header #{key} should be empty." end end + + def encode_credentials(options) + options.reverse_merge!(nc: '00000001', cnonce: '0a4f113b', password_is_ha1: false) + # Perform unauthenticated request to retrieve digest parameters to use on subsequent request + target = options.delete(:target) || :index + get target + assert_response :unauthorized + # Credentials + credentials = { + uri: target, + realm: RedmineDmsf::Webdav::AUTHENTICATION_REALM, + username: options[:username], + nonce: ActionController::HttpAuthentication::Digest.nonce(Rails.configuration.secret_key_base), + opaque: ActionController::HttpAuthentication::Digest.opaque(Rails.configuration.secret_key_base) + } + credentials.merge!(options) + path_info = @request.env['PATH_INFO'].to_s + uri = options[:uri] || path_info + credentials[uri] = uri + @request.env['ORIGINAL_FULLPATH'] = path_info + ha2 = Digest::MD5.hexdigest("GET:#{target}") + nonce = ActionController::HttpAuthentication::Digest.nonce(Rails.configuration.secret_key_base) + ha1 = options.delete(:digest) + credentials[:response] = Digest::MD5.hexdigest("#{ha1}:#{nonce}:#{ha2}") + "Digest #{credentials.sort_by { |x| x[0].to_s }.map { |v| "#{v[0]}=#{v[1]}" }.join(',')}" + end end end end