From 5de32fb197f6b98d0722c2aacc0bfb8f63a6516f Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Fri, 11 Aug 2023 16:07:10 -0400 Subject: [PATCH] 20750: Valid tokens can always GET the current token This is required for clusters in a federation to be able to validate tokens issued by other clusters. Rather than requiring all users to specify this manually (and fail cryptically when they don't), simply grant the permission to all tokens. Arvados-DCO-1.1-Signed-off-by: Brett Smith --- doc/admin/scoped-tokens.html.textile.liquid | 2 ++ doc/admin/upgrading.html.textile.liquid | 4 ++++ doc/api/tokens.html.textile.liquid | 4 +++- services/api/app/models/api_client_authorization.rb | 5 ++++- .../v1/api_client_authorizations_controller_test.rb | 13 +++++++++++++ 5 files changed, 26 insertions(+), 2 deletions(-) diff --git a/doc/admin/scoped-tokens.html.textile.liquid b/doc/admin/scoped-tokens.html.textile.liquid index 18578a78d6..de09b42615 100644 --- a/doc/admin/scoped-tokens.html.textile.liquid +++ b/doc/admin/scoped-tokens.html.textile.liquid @@ -36,6 +36,8 @@ Object update calls use the @PATCH@ method. A scope of @["PATCH", "/arvados/v1/ Similarly, you can use a scope of @["PATCH", "/arvados/v1/collections/zzzzz-4zz18-0123456789abcde"]@ to restrict updates to a single collection. +There is one special exception to the scope rules: a valid token is always allowed to issue a request to "@GET /arvados/v1/api_client_authorizations/current@":{{ site.baseurl }}/api/methods/api_client_authorizations.html#current regardless of its scopes. This allows clients to reliably determine whether a request failed because a token is invalid, or because the token is not permitted to perform a particular request. The API server itself needs to be able to do this to validate tokens issued by other clusters in a federation. + h2. Creating a scoped token A scoped token can be created at the command line: diff --git a/doc/admin/upgrading.html.textile.liquid b/doc/admin/upgrading.html.textile.liquid index 1534d1eb6e..7c521a24ba 100644 --- a/doc/admin/upgrading.html.textile.liquid +++ b/doc/admin/upgrading.html.textile.liquid @@ -48,6 +48,10 @@ In the @Users@ section of your cluster configuration, there are now several opti The defaults for all of these options match the previous behavior of @arvados-login-sync@ _except_ for @SyncIgnoredGroups@. This list names groups that @arvados-login-sync@ will never modify by adding or removing members. As a security precaution, the default list names security-sensitive system groups on Debian- and Red Hat-based distributions. If you are using Arvados to manage system group membership on shell nodes, especially @sudo@ or @wheel@, you may want to provide your own list. Set @SyncIgnoredGroups: []@ to restore the original behavior of ignoring no groups. +h3. API clients can always retrieve their current token, regardless of scopes + +We have introduced a small exception to the previous behavior of "Arvados API token scopes":{{ site.baseurl }}:/admin/scoped-tokens.html in this release. A valid token is now always allowed to issue a request to "@GET /arvados/v1/api_client_authorizations/current@":{{ site.baseurl }}/api/methods/api_client_authorizations.html#current regardless of its scopes. This allows clients to reliably determine whether a request failed because a token is invalid, or because the token is not permitted to perform a particular request. The API server itself needs to be able to do this to validate tokens issued by other clusters in a federation. + h3. UseAWSS3v2Driver option removed The old "v1" S3 driver for keepstore has been removed. The new "v2" implementation, which has been the default since Arvados 2.5.0, is always used. The @Volumes.*.DriverParameters.UseAWSS3v2Driver@ configuration key is no longer recognized. If your config file uses it, remove it to avoid warning messages at startup. diff --git a/doc/api/tokens.html.textile.liquid b/doc/api/tokens.html.textile.liquid index 0935f9ba1d..99c5f58a21 100644 --- a/doc/api/tokens.html.textile.liquid +++ b/doc/api/tokens.html.textile.liquid @@ -73,6 +73,8 @@ Each entry in scopes consists of a @request_method@ and @request_path@. The @re As a special case, a scope of @["all"]@ allows all resources. This is the default if no scope is given. +A valid token is always allowed to issue a request to "@GET /arvados/v1/api_client_authorizations/current@":{{ site.baseurl }}/api/methods/api_client_authorizations.html#current regardless of its scopes. + Using scopes is also described on the "Securing API access with scoped tokens":{{site.baseurl}}/admin/scoped-tokens.html page of the admin documentation. h3. Scope examples @@ -80,7 +82,7 @@ h3. Scope examples A scope of @GET /arvados/v1/collections@ permits listing collections. * Requests with different methods, such as creating a new collection using @POST /arvados/v1/collections@, will be rejected. -* Requests to access other resources, such as @GET /arvados/v1/groups@, will be rejected. +* Requests to access other resources, such as @GET /arvados/v1/groups@, will be rejected (except "@GET /arvados/v1/api_client_authorizations/current@":{{ site.baseurl }}/api/methods/api_client_authorizations.html#current, which is always allowed). * Be aware that requests for specific records, such as @GET /arvados/v1/collections/962eh-4zz18-xi32mpz2621o8km@ will also be rejected. This is because the scope @GET /arvados/v1/collections@ does not end in @/@ A scope of @GET /arvados/v1/collections/@ (with @/@ suffix) will permit access to individual collections. diff --git a/services/api/app/models/api_client_authorization.rb b/services/api/app/models/api_client_authorization.rb index 52922d32b1..5889b74a5a 100644 --- a/services/api/app/models/api_client_authorization.rb +++ b/services/api/app/models/api_client_authorization.rb @@ -6,6 +6,7 @@ class ApiClientAuthorization < ArvadosModel include HasUuid include KindAndEtag include CommonApiTemplate + include Rails.application.routes.url_helpers extend CurrentApiClient extend DbCurrentTime @@ -78,7 +79,9 @@ class ApiClientAuthorization < ArvadosModel def scopes_allow_request?(request) method = request.request_method - if method == 'HEAD' + if method == 'GET' and request.path == url_for(controller: 'arvados/v1/api_client_authorizations', action: 'current', only_path: true) + true + elsif method == 'HEAD' (scopes_allow?(['HEAD', request.path].join(' ')) || scopes_allow?(['GET', request.path].join(' '))) else diff --git a/services/api/test/functional/arvados/v1/api_client_authorizations_controller_test.rb b/services/api/test/functional/arvados/v1/api_client_authorizations_controller_test.rb index 9c70f6f417..60b4133f9a 100644 --- a/services/api/test/functional/arvados/v1/api_client_authorizations_controller_test.rb +++ b/services/api/test/functional/arvados/v1/api_client_authorizations_controller_test.rb @@ -199,6 +199,19 @@ class Arvados::V1::ApiClientAuthorizationsControllerTest < ActionController::Tes assert_not_empty(json_response['uuid']) end + [ + :active_noscope, + :active_all_collections, + :active_userlist, + :foo_collection_sharing_token, + ].each do |auth| + test "#{auth} can get current token without the appropriate scope" do + authorize_with auth + get :current + assert_response :success + end + end + test "get current token, no auth" do get :current assert_response 401 -- 2.30.2