[ARVADOS] created: 1.3.0-1532-gfb7a09468
Git user
git at public.curoverse.com
Tue Aug 20 19:59:01 UTC 2019
at fb7a094681db65daa2f28b50ca734dc27e9d6db7 (commit)
commit fb7a094681db65daa2f28b50ca734dc27e9d6db7
Author: Peter Amstutz <pamstutz at veritasgenetics.com>
Date: Mon Aug 19 14:32:03 2019 -0400
15558: Support login to user account by email address
Can also login through aliases supplied as "alternate_emails".
Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <pamstutz at veritasgenetics.com>
diff --git a/services/api/app/controllers/user_sessions_controller.rb b/services/api/app/controllers/user_sessions_controller.rb
index ef0f88686..ca7ba4a40 100644
--- a/services/api/app/controllers/user_sessions_controller.rb
+++ b/services/api/app/controllers/user_sessions_controller.rb
@@ -15,68 +15,13 @@ class UserSessionsController < ApplicationController
def create
omniauth = request.env['omniauth.auth']
- identity_url_ok = (omniauth['info']['identity_url'].length > 0) rescue false
- unless identity_url_ok
- # Whoa. This should never happen.
- logger.error "UserSessionsController.create: omniauth object missing/invalid"
- logger.error "omniauth: "+omniauth.pretty_inspect
-
+ begin
+ user = User.register omniauth['info']
+ rescue => e
+ Rails.logger.warn e
return redirect_to login_failure_url
end
- # Only local users can create sessions, hence uuid_like_pattern
- # here.
- user = User.unscoped.where('identity_url = ? and uuid like ?',
- omniauth['info']['identity_url'],
- User.uuid_like_pattern).first
- if not user
- # Check for permission to log in to an existing User record with
- # a different identity_url
- Link.where("link_class = ? and name = ? and tail_uuid = ? and head_uuid like ?",
- 'permission',
- 'can_login',
- omniauth['info']['email'],
- User.uuid_like_pattern).each do |link|
- if prefix = link.properties['identity_url_prefix']
- if prefix == omniauth['info']['identity_url'][0..prefix.size-1]
- user = User.find_by_uuid(link.head_uuid)
- break if user
- end
- end
- end
- end
-
- if not user
- # New user registration
- user = User.new(:email => omniauth['info']['email'],
- :first_name => omniauth['info']['first_name'],
- :last_name => omniauth['info']['last_name'],
- :identity_url => omniauth['info']['identity_url'],
- :is_active => Rails.configuration.Users.NewUsersAreActive,
- :owner_uuid => system_user_uuid)
- if omniauth['info']['username']
- user.set_initial_username(requested: omniauth['info']['username'])
- end
- act_as_system_user do
- user.save or raise Exception.new(user.errors.messages)
- end
- else
- user.email = omniauth['info']['email']
- user.first_name = omniauth['info']['first_name']
- user.last_name = omniauth['info']['last_name']
- if user.identity_url.nil?
- # First login to a pre-activated account
- user.identity_url = omniauth['info']['identity_url']
- end
-
- while (uuid = user.redirect_to_user_uuid)
- user = User.unscoped.where(uuid: uuid).first
- if !user
- raise Exception.new("identity_url #{omniauth['info']['identity_url']} redirects to nonexistent uuid #{uuid}")
- end
- end
- end
-
# For the benefit of functional and integration tests:
@user = user
diff --git a/services/api/app/models/user.rb b/services/api/app/models/user.rb
index ee44812e0..6f2f92b93 100644
--- a/services/api/app/models/user.rb
+++ b/services/api/app/models/user.rb
@@ -327,6 +327,104 @@ class User < ArvadosModel
end
end
+ def self.register info
+ # login info should have fields:
+ # email
+ # first_name
+ # last_name
+ # username
+ # alternate_emails
+ # identity_url
+
+ primary_user = nil
+
+ # local database
+ identity_url = info['identity_url']
+
+ if identity_url && identity_url.length > 0
+ # Only local users can create sessions, hence uuid_like_pattern
+ # here.
+ user = User.unscoped.where('identity_url = ? and uuid like ?',
+ info['identity_url'],
+ User.uuid_like_pattern).first
+ if user
+ while (uuid = user.redirect_to_user_uuid)
+ user = User.unscoped.find_by_uuid(uuid)
+ if !user
+ raise Exception.new("user uuid #{user.uuid} redirects to nonexistent uuid #{uuid}")
+ end
+ end
+ primary_user = user
+ end
+
+ # Don't think this is necessary if admin can just create a user
+ # record with the desired email.
+ #
+ # if not user
+ # # Check for permission to log in to an existing User record with
+ # # a different identity_url
+ # Link.where("link_class = ? and name = ? and tail_uuid = ? and head_uuid like ?",
+ # 'permission',
+ # 'can_login',
+ # info['email'],
+ # User.uuid_like_pattern).each do |link|
+ # if prefix = link.properties['identity_url_prefix']
+ # if prefix == info['identity_url'][0..prefix.size-1]
+ # user = User.find_by_uuid(link.head_uuid)
+ # break if user
+ # end
+ # end
+ # end
+ # end
+ end
+
+ if !primary_user and info['email'] and !info['email'].empty?
+ # identity url is unset or didn't find anything.
+ emails = [info['email']] + (info['alternate_emails'] || [])
+ emails.select! {|em| !em.empty?}
+ emails.each do |em|
+ # Go through each email address, try to find a user record
+ # corresponding to one of the addresses supplied.
+
+ user = User.unscoped.where('email = ? and uuid like ?',
+ em,
+ User.uuid_like_pattern).first
+ next if !user
+
+ while (uuid = user.redirect_to_user_uuid)
+ user = User.unscoped.find_by_uuid(uuid)
+ if !user
+ raise Exception.new("user uuid #{user.uuid} redirects to nonexistent uuid #{uuid}")
+ end
+ end
+
+ primary_user = user
+ break
+ end
+ end
+
+ if !primary_user
+ # New user registration
+ primary_user = User.new(:owner_uuid => system_user_uuid,
+ :is_admin => false,
+ :is_active => Rails.configuration.Users.NewUsersAreActive)
+
+ primary_user.set_initial_username(requested: info['username']) if info['username']
+ end
+
+ primary_user.email = info['email']
+ primary_user.identity_url = info['identity_url']
+ primary_user.first_name = info['first_name']
+ primary_user.last_name = info['last_name']
+
+ act_as_system_user do
+ primary_user.save!
+ end
+
+ return primary_user
+
+ end
+
protected
def change_all_uuid_refs(old_uuid:, new_uuid:)
diff --git a/services/api/test/integration/remote_user_test.rb b/services/api/test/integration/remote_user_test.rb
index 90a558653..259d7f554 100644
--- a/services/api/test/integration/remote_user_test.rb
+++ b/services/api/test/integration/remote_user_test.rb
@@ -33,39 +33,54 @@ class RemoteUsersTest < ActionDispatch::IntegrationTest
@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|
+ 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|
+ 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/users/register' 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
+ 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.RemoteClusters = Rails.configuration.RemoteClusters.merge({zbbbb: ActiveSupport::InheritableOptions.new({Host: @remote_host}),
- zbork: ActiveSupport::InheritableOptions.new({Host: @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',
@@ -77,7 +92,9 @@ class RemoteUsersTest < ActionDispatch::IntegrationTest
end
teardown do
- @remote_server.andand.stop
+ @remote_server.each do |srv|
+ srv.stop
+ end
end
test 'authenticate with remote token' do
@@ -148,7 +165,7 @@ class RemoteUsersTest < ActionDispatch::IntegrationTest
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')
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list