mkdir -p "$WORKSPACE/services/api/tmp/pids"
+ cert="$WORKSPACE/services/api/tmp/self-signed"
+ if ! [[ -e "$cert.key" ]]; then
+ dir="$WORKSPACE/services/api/tmp"
+ openssl req -new -x509 -nodes -out "$cert.pem" -keyout "$cert.key" -days 3650 -subj /CN=0.0.0.0 -extfile <(printf 'subjectAltName=DNS:127.0.0.1,DNS:localhost,DNS:::1')
+ fi
+
cd "$WORKSPACE/services/api" \
&& RAILS_ENV=test bundle exec rake db:drop \
&& RAILS_ENV=test bundle exec rake db:setup \
gem 'arvados', '>= 0.1.20150615153458'
gem 'arvados-cli', '>= 0.1.20161017193526'
+gem 'httpclient'
gem 'puma', '~> 2.0'
gem 'sshkey'
hashie (3.5.5)
highline (1.7.8)
hike (1.2.3)
+ httpclient (2.8.3)
i18n (0.9.0)
concurrent-ruby (~> 1.0)
jquery-rails (4.2.2)
database_cleaner
factory_girl_rails
faye-websocket
+ httpclient
jquery-rails
lograge
logstash-event
.all
end
@read_auths.select! { |auth| auth.scopes_allow_request? request }
-
- # Use a salted token as a reader token for /groups/ and /users/current
- if params[:remote] && (
- request.path.start_with?('/arvados/v1/groups') ||
- request.path.start_with?('/arvados/v1/users/current'))
- auth = ApiClientAuthorization.
- validate(token: Thread.current[:supplied_token],
- remote: params[:remote])
- if auth && auth.user
- Thread.current[:user] = auth.user
- @read_auths << auth
- end
- end
-
@read_users = @read_auths.map(&:user).uniq
end
env["HTTP_AUTHORIZATION"].andand.
match(/(OAuth2|Bearer) ([-\/a-zA-Z0-9]+)/).andand[2]
+ if params[:remote] && (
+ request.path.start_with?('/arvados/v1/groups') ||
+ request.path.start_with?('/arvados/v1/users/current'))
+ # Request from a remote API server, asking to validate a salted
+ # token.
+ remote = params[:remote]
+ else
+ # Normal request.
+ remote = false
+ end
auth = ApiClientAuthorization.
- validate(token: Thread.current[:supplied_token], remote: false)
+ validate(token: Thread.current[:supplied_token],
+ remote: remote)
+
Thread.current[:api_client_ip_address] = remote_ip
Thread.current[:api_client_authorization] = auth
Thread.current[:api_client_uuid] = auth.andand.api_client.andand.uuid
["#{table_name}.id desc"]
end
- def self.remote_host(uuid:)
- Rails.configuration.remote_hosts[uuid[0..4]] ||
+ def self.remote_host(uuid_prefix:)
+ Rails.configuration.remote_hosts[uuid_prefix] ||
(Rails.configuration.remote_hosts_via_dns &&
- uuid[0..4]+".arvadosapi.com")
+ uuid_prefix+".arvadosapi.com")
end
def self.validate(token:, remote:)
(secret == auth.api_token ||
secret == OpenSSL::HMAC.hexdigest('sha1', auth.api_token, remote))
return auth
- elsif uuid[0..4] != Rails.configuration.uuid_prefix
- # Token was issued by a different cluster. If it's expired or
- # missing in our database, ask the originating cluster to
- # [re]validate it.
- arv = Arvados.new(api_host: remote_host(uuid: uuid),
- api_token: token)
- begin
- remote_user = arv.user.current(remote: Rails.configuration.uuid_prefix)
- rescue => e
- logger.warn "remote authentication with token #{token.inspect} failed: #{e}"
- return nil
- end
- if !remote_user.is_a?(Hash) || !remote_user[:uuid].is_a?(String) || remote_user[:uuid][0..4] != uuid[0..4]
- logger.warn "remote authentication rejected: remote_user=#{remote_user.inspect}"
- return nil
- end
- act_as_system_user do
- # Add/update user and token in our database so we can
- # validate subsequent requests faster.
- user = User.find_or_create_by(uuid: remote_user[:uuid])
- user.update_attributes!(remote_user.merge(is_admin: false))
- auth = ApiClientAuthorization.
- includes(:user).
- find_or_create_by(uuid: uuid,
- api_token: token,
- user: user,
- api_client_id: 0)
- # Accept this token (and don't reload the user record) for
- # 5 minutes. TODO: Request the actual api_client_auth
- # record from the remote server in case it wants the token
- # to expire sooner.
- auth.update_attributes!(expires_at: Time.now + 5.minutes)
- end
- return auth
end
+
+ uuid_prefix = uuid[0..4]
+ if uuid_prefix == Rails.configuration.uuid_prefix
+ # If the token were valid, we would have validated it above
+ return nil
+ elsif uuid_prefix.length != 5
+ # malformed
+ return nil
+ end
+
+ host = remote_host(uuid_prefix: uuid_prefix)
+ if !host
+ Rails.logger.warn "remote authentication rejected: no host for #{uuid_prefix.inspect}"
+ return nil
+ end
+
+ # Token was issued by a different cluster. If it's expired or
+ # missing in our database, ask the originating cluster to
+ # [re]validate it.
+ arv = Arvados.new(api_host: host,
+ api_token: token,
+ suppress_ssl_warnings: Rails.env == 'test')
+ begin
+ clnt = HTTPClient.new
+ remote_user = SafeJSON.load(
+ clnt.get_content('https://' + host + '/arvados/v1/users/current',
+ {'remote' => Rails.configuration.uuid_prefix},
+ {'Authorization' => 'Bearer ' + token}))
+ rescue => e
+ logger.warn "remote authentication with token #{token.inspect} failed: #{e}"
+ STDERR.puts e.backtrace
+ return nil
+ end
+ if !remote_user.is_a?(Hash) || !remote_user[:uuid].is_a?(String) || remote_user[:uuid][0..4] != uuid[0..4]
+ logger.warn "remote authentication rejected: remote_user=#{remote_user.inspect}"
+ return nil
+ end
+ act_as_system_user do
+ # Add/update user and token in our database so we can
+ # validate subsequent requests faster.
+ user = User.find_or_create_by(uuid: remote_user[:uuid])
+ user.update_attributes!(remote_user.merge(is_admin: false))
+ auth = ApiClientAuthorization.
+ includes(:user).
+ find_or_create_by(uuid: uuid,
+ api_token: token,
+ user: user,
+ api_client_id: 0)
+ # Accept this token (and don't reload the user record) for
+ # 5 minutes. TODO: Request the actual api_client_auth
+ # record from the remote server in case it wants the token
+ # to expire sooner.
+ auth.update_attributes!(expires_at: Time.now + 5.minutes)
+ end
+ return auth
else
auth = ApiClientAuthorization.
includes(:user, :api_client).
}
return return_obj
end
-
- ['zbbbb', 'z0000'].each do |token_valid_for|
- test "validate #{token_valid_for}-salted token for remote cluster zbbbb" do
- salted_token = salt_token(fixture: :active, remote: token_valid_for)
- ArvadosApiToken.new.call("rack.input" => "",
- "HTTP_AUTHORIZATION" => "Bearer #{salted_token}")
- get :current, {remote: 'zbbbb'}
- if token_valid_for == 'zbbbb'
- assert_equal(users(:active).uuid, json_response['uuid'])
- assert_response 200
- else
- assert_response 401
- end
- end
- end
end
require 'webrick'
require 'webrick/https'
require 'test_helper'
+require 'helpers/users_test_helper'
-class RemoteUserAccountTest < ActionController::TestCase
+class RemoteUsersTest < ActionDispatch::IntegrationTest
def auth(remote:)
token = salt_token(fixture: :active, remote: remote)
token.sub!('/zzzzz-', '/'+remote+'-')
- ArvadosApiToken.new.call("rack.input" => "",
- "HTTP_AUTHORIZATION" => "Bearer #{token}")
+ {"HTTP_AUTHORIZATION" => "Bearer #{token}"}
end
setup do
SSLEnable: true,
SSLVerifyClient: OpenSSL::SSL::VERIFY_NONE,
SSLPrivateKey: OpenSSL::PKey::RSA.new(
- File.open("self-signed.key").read),
+ File.open(Rails.root.join("tmp", "self-signed.key")).read),
SSLCertificate: OpenSSL::X509::Certificate.new(
- File.open("self-signed.pem").read),
+ File.open(Rails.root.join("tmp", "self-signed.pem")).read),
SSLCertName: [["CN", WEBrick::Utils::getservername]],
StartCallback: lambda { ready.push(true) })
srv.mount_proc '/discovery/v1/apis/arvados/v1/rest' do |req, res|
end
teardown do
- @remote_server.stop
+ @remote_server.andand.stop
end
test 'authenticate with remote token' do
- auth(remote: 'zbbbb')
- get :current
+ get '/arvados/v1/users/current', {}, auth(remote: 'zbbbb')
assert_response :success
assert_equal 'zbbbb-tpzed-000000000000000', json_response['uuid']
assert_equal false, json_response['is_admin']
test 'authenticate with remote token from wrong site' do
@stub_content[:uuid] = 'zcccc-tpzed-000000000000000'
- auth(remote: 'zbbbb')
- get :current
+ get '/arvados/v1/users/current', {}, auth(remote: 'zbbbb')
assert_response 401
end
@stub_content = {
error: 'not authorized',
}
- auth(remote: 'zbbbb')
- get :current
+ get '/arvados/v1/users/current', {}, auth(remote: 'zbbbb')
assert_response 401
end
test 'remote api server is not an api server' do
- @stub_status = 401
+ @stub_status = 200
@stub_content = '<html>bad</html>'
- auth(remote: 'zbbbb')
- get :current
+ get '/arvados/v1/users/current', {}, auth(remote: 'zbbbb')
assert_response 401
end
+
+ ['zbbbb', 'z0000'].each do |token_valid_for|
+ test "validate #{token_valid_for}-salted token for remote cluster zbbbb" do
+ salted_token = salt_token(fixture: :active, remote: token_valid_for)
+ get '/arvados/v1/users/current', {format: 'json', remote: 'zbbbb'}, {
+ "HTTP_AUTHORIZATION" => "Bearer #{salted_token}"
+ }
+ if token_valid_for == 'zbbbb'
+ assert_response 200
+ assert_equal(users(:active).uuid, json_response['uuid'])
+ else
+ assert_response 401
+ end
+ end
+ end
end