@controller = Arvados::V1::UsersController.new
ready = Thread::Queue.new
- srv = WEBrick::HTTPServer.new(
- Port: 0,
- Logger: WEBrick::Log.new(
- Rails.root.join("log", "webrick.log").to_s,
- WEBrick::Log::INFO),
- AccessLog: [[File.open(Rails.root.join(
- "log", "webrick_access.log").to_s, 'a+'),
- WEBrick::AccessLog::COMBINED_LOG_FORMAT]],
- SSLEnable: true,
- SSLVerifyClient: OpenSSL::SSL::VERIFY_NONE,
- SSLPrivateKey: OpenSSL::PKey::RSA.new(
- File.open(Rails.root.join("tmp", "self-signed.key")).read),
- SSLCertificate: OpenSSL::X509::Certificate.new(
- 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|
- Rails.cache.delete 'arvados_v1_rest_discovery'
- res.body = Arvados::V1::SchemaController.new.send(:discovery_doc).to_json
- end
- srv.mount_proc '/arvados/v1/users/current' do |req, res|
- res.status = @stub_status
- res.body = @stub_content.is_a?(String) ? @stub_content : @stub_content.to_json
- end
- Thread.new do
- srv.start
+
+ @remote_server = []
+ @remote_host = []
+
+ ['zbbbb', 'zbork'].each do |clusterid|
+ srv = WEBrick::HTTPServer.new(
+ Port: 0,
+ Logger: WEBrick::Log.new(
+ Rails.root.join("log", "webrick.log").to_s,
+ WEBrick::Log::INFO),
+ AccessLog: [[File.open(Rails.root.join(
+ "log", "webrick_access.log").to_s, 'a+'),
+ WEBrick::AccessLog::COMBINED_LOG_FORMAT]],
+ SSLEnable: true,
+ SSLVerifyClient: OpenSSL::SSL::VERIFY_NONE,
+ SSLPrivateKey: OpenSSL::PKey::RSA.new(
+ File.open(Rails.root.join("tmp", "self-signed.key")).read),
+ SSLCertificate: OpenSSL::X509::Certificate.new(
+ 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|
+ res.body = Arvados::V1::SchemaController.new.send(:discovery_doc).to_json
+ end
+ srv.mount_proc '/arvados/v1/users/current' do |req, res|
+ if clusterid == 'zbbbb' and req.header['authorization'][0][10..14] == 'zbork'
+ # asking zbbbb about zbork should yield an error, zbbbb doesn't trust zbork
+ res.status = 401
+ return
+ end
+ res.status = @stub_status
+ res.body = @stub_content.is_a?(String) ? @stub_content : @stub_content.to_json
+ end
+ srv.mount_proc '/arvados/v1/api_client_authorizations/current' do |req, res|
+ if clusterid == 'zbbbb' and req.header['authorization'][0][10..14] == 'zbork'
+ # asking zbbbb about zbork should yield an error, zbbbb doesn't trust zbork
+ res.status = 401
+ return
+ end
+ res.status = @stub_token_status
+ if res.status == 200
+ body = {
+ uuid: api_client_authorizations(:active).uuid.sub('zzzzz', clusterid),
+ owner_uuid: "#{clusterid}-tpzed-00000000000000z",
+ scopes: @stub_token_scopes,
+ }
+ if @stub_content.is_a?(Hash) and owner_uuid = @stub_content[:uuid]
+ body[:owner_uuid] = owner_uuid
+ end
+ res.body = body.to_json
+ end
+ end
+ Thread.new do
+ srv.start
+ end
+ ready.pop
+ @remote_server << srv
+ @remote_host << "127.0.0.1:#{srv.config[:Port]}"
end
- ready.pop
- @remote_server = srv
- @remote_host = "127.0.0.1:#{srv.config[:Port]}"
- Rails.configuration.remote_hosts = Rails.configuration.remote_hosts.merge({'zbbbb' => @remote_host,
- 'zbork' => @remote_host})
- Arvados::V1::SchemaController.any_instance.stubs(:root_url).returns "https://#{@remote_host}"
+ Rails.configuration.RemoteClusters = Rails.configuration.RemoteClusters.merge({zbbbb: ActiveSupport::InheritableOptions.new({Host: @remote_host[0]}),
+ zbork: ActiveSupport::InheritableOptions.new({Host: @remote_host[1]})})
+ Arvados::V1::SchemaController.any_instance.stubs(:root_url).returns "https://#{@remote_host[0]}"
@stub_status = 200
@stub_content = {
- uuid: 'zbbbb-tpzed-000000000000000',
+ uuid: 'zbbbb-tpzed-000000000000001',
email: 'foo@example.com',
username: 'barney',
+ first_name: "Barney",
+ last_name: "Foo",
is_admin: true,
is_active: true,
+ is_invited: true,
}
+ @stub_token_status = 200
+ @stub_token_scopes = ["all"]
+ ActionMailer::Base.deliveries = []
end
teardown do
- @remote_server.andand.stop
+ @remote_server.each do |srv|
+ srv.stop
+ end
+ end
+
+ def uncache_token(src)
+ if match = src.match(/\b(?:[a-z0-9]{5}-){2}[a-z0-9]{15}\b/)
+ tokens = ApiClientAuthorization.where(uuid: match[0])
+ else
+ tokens = ApiClientAuthorization.where("uuid like ?", "#{src}-%")
+ end
+ tokens.update_all(expires_at: "1995-05-15T01:02:03Z")
+ end
+
+ test 'authenticate with remote token that has limited scope' do
+ get '/arvados/v1/collections',
+ params: {format: 'json'},
+ headers: auth(remote: 'zbbbb')
+ assert_response :success
+
+ @stub_token_scopes = ["GET /arvados/v1/users/current"]
+
+ # re-authorize before cache expires
+ get '/arvados/v1/collections',
+ params: {format: 'json'},
+ headers: auth(remote: 'zbbbb')
+ assert_response :success
+
+ uncache_token('zbbbb')
+ # re-authorize after cache expires
+ get '/arvados/v1/collections',
+ params: {format: 'json'},
+ headers: auth(remote: 'zbbbb')
+ assert_response 403
+ end
+
+ test "authenticate with remote token with limited initial scope" do
+ @stub_token_scopes = ["GET /arvados/v1/users/"]
+ get "/arvados/v1/users/#{@stub_content[:uuid]}",
+ params: {format: "json"},
+ headers: auth(remote: "zbbbb")
+ assert_response :success
end
test 'authenticate with remote token' do
params: {format: 'json'},
headers: auth(remote: 'zbbbb')
assert_response :success
- assert_equal 'zbbbb-tpzed-000000000000000', json_response['uuid']
+ assert_equal 'zbbbb-tpzed-000000000000001', json_response['uuid']
assert_equal false, json_response['is_admin']
assert_equal false, json_response['is_active']
assert_equal 'foo@example.com', json_response['email']
assert_equal 'barney', json_response['username']
# revoke original token
- @stub_status = 401
+ @stub_token_status = 401
# re-authorize before cache expires
get '/arvados/v1/users/current',
headers: auth(remote: 'zbbbb')
assert_response :success
- # simulate cache expiry
- ApiClientAuthorization.where(
- uuid: salted_active_token(remote: 'zbbbb').split('/')[1]).
- update_all(expires_at: db_current_time - 1.minute)
-
+ uncache_token('zbbbb')
# re-authorize after cache expires
get '/arvados/v1/users/current',
params: {format: 'json'},
update_all(user_id: users(:active).id)
# revive original token and re-authorize
- @stub_status = 200
+ @stub_token_status = 200
@stub_content[:username] = 'blarney'
@stub_content[:email] = 'blarney@example.com'
get '/arvados/v1/users/current',
assert_equal 'blarney@example.com', json_response['email']
end
+ test 'remote user is deactivated' do
+ Rails.configuration.RemoteClusters['zbbbb'].ActivateUsers = true
+ get '/arvados/v1/users/current',
+ params: {format: 'json'},
+ headers: auth(remote: 'zbbbb')
+ assert_response :success
+ assert_equal true, json_response['is_active']
+
+ # revoke original token
+ @stub_content[:is_active] = false
+ @stub_content[:is_invited] = false
+
+ uncache_token('zbbbb')
+ # re-authorize after cache expires
+ get '/arvados/v1/users/current',
+ params: {format: 'json'},
+ headers: auth(remote: 'zbbbb')
+ assert_equal false, json_response['is_active']
+
+ end
+
test 'authenticate with remote token, remote username conflicts with local' do
@stub_content[:username] = 'active'
get '/arvados/v1/users/current',
assert_equal 'foo', json_response['username']
end
- test 'authenticate with remote token from misbhehaving remote cluster' do
+ test 'authenticate with remote token from misbehaving remote cluster' do
get '/arvados/v1/users/current',
params: {format: 'json'},
headers: auth(remote: 'zbork')
end
test "list readable groups with salted token" do
+ Rails.configuration.Users.RoleGroupsVisibleToAll = false
salted_token = salt_token(fixture: :active, remote: 'zbbbb')
get '/arvados/v1/groups',
params: {
refute_includes(group_uuids, groups(:testusergroup_admins).uuid)
end
+ test 'do not auto-activate user from untrusted cluster' do
+ Rails.configuration.RemoteClusters['zbbbb'].AutoSetupNewUsers = false
+ Rails.configuration.RemoteClusters['zbbbb'].ActivateUsers = false
+ get '/arvados/v1/users/current',
+ params: {format: 'json'},
+ headers: auth(remote: 'zbbbb')
+ assert_response :success
+ assert_equal 'zbbbb-tpzed-000000000000001', json_response['uuid']
+ assert_equal false, json_response['is_admin']
+ assert_equal false, json_response['is_active']
+ assert_equal 'foo@example.com', json_response['email']
+ assert_equal 'barney', json_response['username']
+ post '/arvados/v1/users/zbbbb-tpzed-000000000000001/activate',
+ params: {format: 'json'},
+ headers: auth(remote: 'zbbbb')
+ assert_response 422
+ end
+
test 'auto-activate user from trusted cluster' do
- Rails.configuration.auto_activate_users_from = ['zbbbb']
+ Rails.configuration.RemoteClusters['zbbbb'].ActivateUsers = true
+ get '/arvados/v1/users/current',
+ params: {format: 'json'},
+ headers: auth(remote: 'zbbbb')
+ assert_response :success
+ assert_equal 'zbbbb-tpzed-000000000000001', json_response['uuid']
+ assert_equal false, json_response['is_admin']
+ assert_equal true, json_response['is_active']
+ assert_equal 'foo@example.com', json_response['email']
+ assert_equal 'barney', json_response['username']
+ end
+
+ test 'get user from Login cluster' do
+ Rails.configuration.Login.LoginCluster = 'zbbbb'
+ email_dest = ActiveSupport::OrderedOptions.new
+ email_dest[:'arvados-admin@example.com'] = ActiveSupport::OrderedOptions.new
+ Rails.configuration.Users.UserNotifierEmailBcc = email_dest
+ Rails.configuration.Users.NewUserNotificationRecipients = email_dest
+ Rails.configuration.Users.NewInactiveUserNotificationRecipients = email_dest
+
+ get '/arvados/v1/users/current',
+ params: {format: 'json'},
+ headers: auth(remote: 'zbbbb')
+ assert_response :success
+ assert_equal 'zbbbb-tpzed-000000000000001', json_response['uuid']
+ assert_equal true, json_response['is_admin']
+ assert_equal true, json_response['is_active']
+ assert_equal 'foo@example.com', json_response['email']
+ assert_equal 'barney', json_response['username']
+
+ assert_equal 2, ActionMailer::Base.deliveries.length
+ assert_equal "Welcome to Arvados - account enabled", ActionMailer::Base.deliveries[0].subject
+ assert_equal "[ARVADOS] New user created notification", ActionMailer::Base.deliveries[1].subject
+ end
+
+ [true, false].each do |trusted|
+ [true, false].each do |logincluster|
+ [true, false, nil].each do |admin|
+ [true, false, nil].each do |active|
+ [true, false].each do |autosetup|
+ [true, false, nil].each do |invited|
+ test "get invited=#{invited}, active=#{active}, admin=#{admin} user from #{if logincluster then "Login" else "peer" end} cluster when AutoSetupNewUsers=#{autosetup} ActivateUsers=#{trusted}" do
+ Rails.configuration.Login.LoginCluster = 'zbbbb' if logincluster
+ Rails.configuration.RemoteClusters['zbbbb'].ActivateUsers = trusted
+ Rails.configuration.Users.AutoSetupNewUsers = autosetup
+ @stub_content = {
+ uuid: 'zbbbb-tpzed-000000000000001',
+ email: 'foo@example.com',
+ username: 'barney',
+ is_admin: admin,
+ is_active: active,
+ is_invited: invited,
+ }
+ get '/arvados/v1/users/current',
+ params: {format: 'json'},
+ headers: auth(remote: 'zbbbb')
+ assert_response :success
+ assert_equal 'zbbbb-tpzed-000000000000001', json_response['uuid']
+ assert_equal (logincluster && !!admin && (invited != false) && !!active), json_response['is_admin']
+ assert_equal ((invited == true || (invited == nil && !!active)) && (logincluster || trusted || autosetup)), json_response['is_invited']
+ assert_equal ((invited != false) && (logincluster || trusted) && !!active), json_response['is_active']
+ assert_equal 'foo@example.com', json_response['email']
+ assert_equal 'barney', json_response['username']
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ test 'get active user from Login cluster when AutoSetupNewUsers is set' do
+ Rails.configuration.Login.LoginCluster = 'zbbbb'
+ Rails.configuration.Users.AutoSetupNewUsers = true
+ @stub_content = {
+ uuid: 'zbbbb-tpzed-000000000000001',
+ email: 'foo@example.com',
+ username: 'barney',
+ is_admin: false,
+ is_active: true,
+ is_invited: true,
+ }
+ get '/arvados/v1/users/current',
+ params: {format: 'json'},
+ headers: auth(remote: 'zbbbb')
+ assert_response :success
+ assert_equal 'zbbbb-tpzed-000000000000001', json_response['uuid']
+ assert_equal false, json_response['is_admin']
+ assert_equal true, json_response['is_active']
+ assert_equal true, json_response['is_invited']
+ assert_equal 'foo@example.com', json_response['email']
+ assert_equal 'barney', json_response['username']
+
+ @stub_content = {
+ uuid: 'zbbbb-tpzed-000000000000001',
+ email: 'foo@example.com',
+ username: 'barney',
+ is_admin: false,
+ is_active: false,
+ is_invited: false,
+ }
+
+ # Use cached value. User will still be active because we haven't
+ # re-queried the upstream cluster.
get '/arvados/v1/users/current',
params: {format: 'json'},
headers: auth(remote: 'zbbbb')
assert_response :success
- assert_equal 'zbbbb-tpzed-000000000000000', json_response['uuid']
+ assert_equal 'zbbbb-tpzed-000000000000001', json_response['uuid']
assert_equal false, json_response['is_admin']
assert_equal true, json_response['is_active']
+ assert_equal true, json_response['is_invited']
+ assert_equal 'foo@example.com', json_response['email']
+ assert_equal 'barney', json_response['username']
+
+ uncache_token('zbbbb')
+ # User should be inactive now.
+ get '/arvados/v1/users/current',
+ params: {format: 'json'},
+ headers: auth(remote: 'zbbbb')
+ assert_response :success
+ assert_equal 'zbbbb-tpzed-000000000000001', json_response['uuid']
+ assert_equal false, json_response['is_admin']
+ assert_equal false, json_response['is_active']
+ assert_equal false, json_response['is_invited']
assert_equal 'foo@example.com', json_response['email']
assert_equal 'barney', json_response['username']
+
end
test 'pre-activate remote user' do
+ @stub_content = {
+ uuid: 'zbbbb-tpzed-000000000001234',
+ email: 'foo@example.com',
+ username: 'barney',
+ is_admin: true,
+ is_active: true,
+ is_invited: true,
+ }
+
post '/arvados/v1/users',
params: {
"user" => {
- "uuid" => "zbbbb-tpzed-000000000000000",
+ "uuid" => "zbbbb-tpzed-000000000001234",
"email" => 'foo@example.com',
"username" => 'barney',
- "is_active" => true
+ "is_active" => true,
+ "is_admin" => false
}
},
headers: {'HTTP_AUTHORIZATION' => "OAuth2 #{api_token(:admin)}"}
params: {format: 'json'},
headers: auth(remote: 'zbbbb')
assert_response :success
- assert_equal 'zbbbb-tpzed-000000000000000', json_response['uuid']
- assert_equal nil, json_response['is_admin']
+ assert_equal 'zbbbb-tpzed-000000000001234', json_response['uuid']
+ assert_equal false, json_response['is_admin']
assert_equal true, json_response['is_active']
assert_equal 'foo@example.com', json_response['email']
assert_equal 'barney', json_response['username']
end
+
+ test 'remote user inactive without pre-activation' do
+ @stub_content = {
+ uuid: 'zbbbb-tpzed-000000000001234',
+ email: 'foo@example.com',
+ username: 'barney',
+ is_admin: true,
+ is_active: true,
+ is_invited: true,
+ }
+
+ get '/arvados/v1/users/current',
+ params: {format: 'json'},
+ headers: auth(remote: 'zbbbb')
+ assert_response :success
+ assert_equal 'zbbbb-tpzed-000000000001234', json_response['uuid']
+ assert_equal false, json_response['is_admin']
+ assert_equal false, json_response['is_active']
+ assert_equal 'foo@example.com', json_response['email']
+ assert_equal 'barney', json_response['username']
+ end
+
test "validate unsalted v2 token for remote cluster zbbbb" do
auth = api_client_authorizations(:active)
token = "v2/#{auth.uuid}/#{auth.api_token}"
end
end
+ test 'authenticate with remote token, remote user is system user' do
+ @stub_content[:uuid] = 'zbbbb-tpzed-000000000000000'
+ get '/arvados/v1/users/current',
+ params: {format: 'json'},
+ headers: auth(remote: 'zbbbb')
+ assert_equal 'from cluster zbbbb', json_response['last_name']
+ end
+
+ test 'authenticate with remote token, remote user is anonymous user' do
+ @stub_content[:uuid] = 'zbbbb-tpzed-anonymouspublic'
+ get '/arvados/v1/users/current',
+ params: {format: 'json'},
+ headers: auth(remote: 'zbbbb')
+ assert_response :success
+ assert_equal 'zzzzz-tpzed-anonymouspublic', json_response['uuid']
+ end
+
+ [401, 403, 422, 500, 502, 503].each do |status|
+ test "propagate #{status} response from getting remote token" do
+ @stub_token_status = status
+ get "/arvados/v1/users/#{@stub_content[:uuid]}",
+ params: {format: "json"},
+ headers: auth(remote: "zbbbb")
+ assert_response status
+ end
+
+ test "propagate #{status} response from getting uncached user" do
+ @stub_status = status
+ get "/arvados/v1/users/#{@stub_content[:uuid]}",
+ params: {format: "json"},
+ headers: auth(remote: "zbbbb")
+ assert_response status
+ end
+
+ test "use cached user after getting #{status} response" do
+ url_path = "/arvados/v1/users/#{@stub_content[:uuid]}"
+ params = {format: "json"}
+ headers = auth(remote: "zbbbb")
+
+ get url_path, params: params, headers: headers
+ assert_response :success
+
+ uncache_token(headers["HTTP_AUTHORIZATION"])
+ expect_email = @stub_content[:email]
+ @stub_content[:email] = "new#{expect_email}"
+ @stub_status = status
+ get url_path, params: params, headers: headers
+ assert_response :success
+ user = User.find_by_uuid(@stub_content[:uuid])
+ assert_not_nil user
+ assert_equal expect_email, user.email
+ end
+ end
end