X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/0dddb614432b0b7474b44b4a6f1b1ea7cd9c4e13..864c3b0afd16c77e046f0072d8517d34c5a44792:/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 6a34ed9552..ee63c4d934 100644 --- a/services/api/app/models/api_client_authorization.rb +++ b/services/api/app/models/api_client_authorization.rb @@ -7,12 +7,15 @@ class ApiClientAuthorization < ArvadosModel include KindAndEtag include CommonApiTemplate extend CurrentApiClient + extend DbCurrentTime belongs_to :api_client belongs_to :user after_initialize :assign_random_api_token serialize :scopes, Array + before_validation :clamp_token_expiration + api_accessible :user, extend: :common do |t| t.add :owner_uuid t.add :user_id @@ -130,6 +133,7 @@ class ApiClientAuthorization < ArvadosModel token_uuid = '' secret = token + stored_secret = nil # ...if different from secret optional = nil case token[0..2] @@ -206,8 +210,7 @@ class ApiClientAuthorization < ArvadosModel # below. If so, we'll stuff the database with hmac instead of # the real OIDC token. upstream_cluster_id = Rails.configuration.Login.LoginCluster - token_uuid = generate_uuid - secret = hmac + stored_secret = hmac else return nil end @@ -246,6 +249,23 @@ class ApiClientAuthorization < ArvadosModel remote_user_prefix = remote_user['uuid'][0..4] + if token_uuid == '' + # Use the same UUID as the remote when caching the token. + begin + remote_token = SafeJSON.load( + clnt.get_content('https://' + host + '/arvados/v1/api_client_authorizations/current', + {'remote' => Rails.configuration.ClusterID}, + {'Authorization' => 'Bearer ' + token})) + token_uuid = remote_token['uuid'] + if !token_uuid.match(HasUuid::UUID_REGEX) || token_uuid[0..4] != upstream_cluster_id + raise "remote cluster #{upstream_cluster_id} returned invalid token uuid #{token_uuid.inspect}" + end + rescue => e + Rails.logger.warn "error getting remote token details for #{token.inspect}: #{e}" + return nil + end + end + # Clusters can only authenticate for their own users. if remote_user_prefix != upstream_cluster_id Rails.logger.warn "remote authentication rejected: claimed remote user #{remote_user_prefix} but token was issued by #{upstream_cluster_id}" @@ -328,11 +348,18 @@ class ApiClientAuthorization < ArvadosModel auth.user = user auth.api_client_id = 0 end + # If stored_secret is set, we save stored_secret in the database + # but return the real secret to the caller. This way, if we end + # up returning the auth record to the client, they see the same + # secret they supplied, instead of the HMAC we saved in the + # database. + stored_secret = stored_secret || secret auth.update_attributes!(user: user, - api_token: secret, + api_token: stored_secret, api_client_id: 0, - expires_at: Time.now + Rails.configuration.Login.RemoteTokenRefresh) - Rails.logger.debug "cached remote token #{token_uuid} with secret #{secret} in local db" + expires_at: db_current_time + Rails.configuration.Login.RemoteTokenRefresh) + Rails.logger.debug "cached remote token #{token_uuid} with secret #{stored_secret} in local db" + auth.api_token = secret return auth end @@ -360,6 +387,15 @@ class ApiClientAuthorization < ArvadosModel protected + def clamp_token_expiration + if !current_user.andand.is_admin && Rails.configuration.API.MaxTokenLifetime > 0 + max_token_expiration = db_current_time + Rails.configuration.API.MaxTokenLifetime + if (self.new_record? || self.expires_at_changed?) && (self.expires_at.nil? || self.expires_at > max_token_expiration) + self.expires_at = max_token_expiration + end + end + end + def permission_to_create current_user.andand.is_admin or (current_user.andand.id == self.user_id) end @@ -370,7 +406,6 @@ class ApiClientAuthorization < ArvadosModel end def log_update - super unless (saved_changes.keys - UNLOGGED_CHANGES).empty? end end