X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/030a7ee62740e5f7def3a7333d1c8996b3111fef..HEAD:/services/api/app/models/api_client_authorization.rb diff --git a/services/api/app/models/api_client_authorization.rb b/services/api/app/models/api_client_authorization.rb index ef73d79c17..3600fb78ce 100644 --- a/services/api/app/models/api_client_authorization.rb +++ b/services/api/app/models/api_client_authorization.rb @@ -19,14 +19,12 @@ class ApiClientAuthorization < ArvadosModel api_accessible :user, extend: :common do |t| t.add :owner_uuid - t.add :user_id t.add :api_client_id # NB the "api_token" db column is a misnomer in that it's only the # "secret" part of a token: a v1 token is just the secret, but a # v2 token is "v2/uuid/secret". t.add :api_token t.add :created_by_ip_address - t.add :default_owner_uuid t.add :expires_at t.add :last_used_at t.add :last_used_by_ip_address @@ -294,6 +292,10 @@ class ApiClientAuthorization < ArvadosModel raise "remote cluster #{upstream_cluster_id} returned invalid token uuid #{token_uuid.inspect}" end rescue HTTPClient::BadResponseError => e + if e.res.status_code >= 400 && e.res.status_code < 500 + # Remote cluster does not accept this token. + return nil + end # CurrentApiToken#call and ApplicationController#render_error will # propagate the status code from the #http_status method, so define # that here. @@ -363,71 +365,17 @@ class ApiClientAuthorization < ArvadosModel if user.nil? and remote_user.nil? Rails.logger.warn "remote token #{token.inspect} rejected: cannot get owner #{remote_user_uuid} from database or remote cluster" return nil + end + # Invariant: remote_user_prefix == upstream_cluster_id # therefore: remote_user_prefix != Rails.configuration.ClusterID # Add or update user and token in local database so we can # validate subsequent requests faster. - elsif user.nil? - # Create a new record for this user. - user = User.new(uuid: remote_user['uuid'], - is_active: false, - is_admin: false, - email: remote_user['email'], - owner_uuid: system_user_uuid) - user.set_initial_username(requested: remote_user['username']) - end - # Sync user record if we loaded a remote user. act_as_system_user do - if remote_user - %w[first_name last_name email prefs].each do |attr| - user.send(attr+'=', remote_user[attr]) - end - - begin - user.save! - rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique - Rails.logger.debug("remote user #{remote_user['uuid']} already exists, retrying...") - # Some other request won the race: retry fetching the user record. - user = User.uncached do - User.find_by_uuid(remote_user['uuid']) - end - if !user - Rails.logger.warn("cannot find or create remote user #{remote_user['uuid']}") - return nil - end - end - - if user.is_invited && !remote_user['is_invited'] - # Remote user is not "invited" state, they should be unsetup, which - # also makes them inactive. - user.unsetup - else - if !user.is_invited && remote_user['is_invited'] and - (remote_user_prefix == Rails.configuration.Login.LoginCluster or - Rails.configuration.Users.AutoSetupNewUsers or - Rails.configuration.Users.NewUsersAreActive or - Rails.configuration.RemoteClusters[remote_user_prefix].andand["ActivateUsers"]) - user.setup - end - - if !user.is_active && remote_user['is_active'] && user.is_invited and - (remote_user_prefix == Rails.configuration.Login.LoginCluster or - Rails.configuration.Users.NewUsersAreActive or - Rails.configuration.RemoteClusters[remote_user_prefix].andand["ActivateUsers"]) - user.update!(is_active: true) - elsif user.is_active && !remote_user['is_active'] - user.update!(is_active: false) - end - - if remote_user_prefix == Rails.configuration.Login.LoginCluster and - user.is_active and - user.is_admin != remote_user['is_admin'] - # Remote cluster controls our user database, including the - # admin flag. - user.update!(is_admin: remote_user['is_admin']) - end - end + if remote_user && remote_user_uuid != anonymous_user_uuid + # Sync user record if we loaded a remote user. + user = User.update_remote_user remote_user end # If stored_secret is set, we save stored_secret in the database @@ -453,8 +401,17 @@ class ApiClientAuthorization < ArvadosModel end rescue ActiveRecord::RecordNotUnique Rails.logger.debug("cached remote token #{token_uuid} already exists, retrying...") - # Some other request won the race: retry just once before erroring out - if (retries += 1) <= 1 + # Another request won the race (trying to find_or_create the + # same token UUID) ...and/or... there is an expired entry with + # the same secret but a different UUID (e.g., the token is an + # OIDC access token and [a] our database has an expired cached + # row that was not used above, and [b] the remote cluster had + # deleted its expired cached row so it assigned a new UUID). + # + # Delete any conflicting row if any. Retry twice (in case we + # hit both of those situations at once), then give up. + if (retries += 1) <= 2 + ApiClientAuthorization.where('api_token=? and uuid<>?', stored_secret, token_uuid).delete_all retry else Rails.logger.warn("cannot find or create cached remote token #{token_uuid}") @@ -462,10 +419,10 @@ class ApiClientAuthorization < ArvadosModel end end auth.update!(user: user, - api_token: stored_secret, - api_client_id: 0, - scopes: scopes, - expires_at: exp) + api_token: stored_secret, + api_client_id: 0, + scopes: scopes, + expires_at: exp) Rails.logger.debug "cached remote token #{token_uuid} with secret #{stored_secret} and scopes #{scopes} in local db" auth.api_token = secret return auth