[ARVADOS] created: 2.1.0-305-gbb5d1bb61

Git user git at public.arvados.org
Thu Feb 4 17:47:24 UTC 2021


        at  bb5d1bb6158ebd6b9fc64ff465feba7c22eaa077 (commit)


commit bb5d1bb6158ebd6b9fc64ff465feba7c22eaa077
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date:   Thu Feb 4 14:46:30 2021 -0300

    16736: Updates arvados-login-sync to support expiring tokens.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas at di-pentima.com.ar>

diff --git a/services/login-sync/bin/arvados-login-sync b/services/login-sync/bin/arvados-login-sync
index a9bff0546..d8836f19b 100755
--- a/services/login-sync/bin/arvados-login-sync
+++ b/services/login-sync/bin/arvados-login-sync
@@ -9,6 +9,7 @@ require 'arvados'
 require 'etc'
 require 'fileutils'
 require 'yaml'
+require 'optparse'
 
 req_envs = %w(ARVADOS_API_HOST ARVADOS_API_TOKEN ARVADOS_VIRTUAL_MACHINE_UUID)
 req_envs.each do |k|
@@ -17,16 +18,20 @@ req_envs.each do |k|
   end
 end
 
-exclusive_mode = ARGV.index("--exclusive")
+options = {}
+OptionParser.new do |parser|
+  parser.on('--exclusive', 'Manage SSH keys file exclusively.')
+  parser.on('--rotate-tokens', 'Always create new user tokens. Usually needed with --token-lifetime.')
+  parser.on('--skip-missing-users', "Don't try to create any local accounts.")
+  parser.on('--token-lifetime SECONDS', 'Create user tokens that expire after SECONDS.', Integer)
+end.parse!(into: options)
+
 exclusive_banner = "#######################################################################################
 #  THIS FILE IS MANAGED BY #{$0} -- CHANGES WILL BE OVERWRITTEN  #
 #######################################################################################\n\n"
 start_banner = "### BEGIN Arvados-managed keys -- changes between markers will be overwritten\n"
 end_banner = "### END Arvados-managed keys -- changes between markers will be overwritten\n"
 
-# Don't try to create any local accounts
-skip_missing_users = ARGV.index("--skip-missing-users")
-
 keys = ''
 
 begin
@@ -64,7 +69,7 @@ begin
       begin
         pwnam[l[:username]] = Etc.getpwnam(l[:username])
       rescue
-        if skip_missing_users
+        if options[:"skip-missing-users"]
           STDERR.puts "Account #{l[:username]} not found. Skipping"
           true
         end
@@ -165,7 +170,7 @@ begin
       oldkeys = ""
     end
 
-    if exclusive_mode
+    if options[:exclusive]
       newkeys = exclusive_banner + newkeys
     elsif oldkeys.start_with?(exclusive_banner)
       newkeys = start_banner + newkeys + end_banner
@@ -192,8 +197,12 @@ begin
     tokenfile = File.join(configarvados, "settings.conf")
 
     begin
-      if !File.exist?(tokenfile)
-        user_token = logincluster_arv.api_client_authorization.create(api_client_authorization: {owner_uuid: l[:user_uuid], api_client_id: 0})
+      if !File.exist?(tokenfile) || options[:"rotate-tokens"]
+        aca_params = {owner_uuid: l[:user_uuid], api_client_id: 0}
+        if options[:"token-lifetime"] && options[:"token-lifetime"] > 0
+          aca_params.merge!(expires_at: (Time.now + options[:"token-lifetime"]))
+        end
+        user_token = logincluster_arv.api_client_authorization.create(api_client_authorization: aca_params)
         f = File.new(tokenfile, 'w')
         f.write("ARVADOS_API_HOST=#{ENV['ARVADOS_API_HOST']}\n")
         f.write("ARVADOS_API_TOKEN=v2/#{user_token[:uuid]}/#{user_token[:api_token]}\n")

commit d6424bfca29375a2a7ed4637f613141253c7ea3b
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date:   Thu Jan 28 18:30:30 2021 -0300

    16736: Fixes typo.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas at di-pentima.com.ar>

diff --git a/lib/config/config.default.yml b/lib/config/config.default.yml
index 8ced99b52..6b8de6061 100644
--- a/lib/config/config.default.yml
+++ b/lib/config/config.default.yml
@@ -161,7 +161,7 @@ Clusters:
       # Limits for how long a client token created by regular users can be valid,
       # and also is used as a default expiration policy when no expiration date is
       # specified.
-      # Default value zero menans token expirations don't get clamped and no
+      # Default value zero means token expirations don't get clamped and no
       # default expiration is set.
       MaxTokenLifetime: 0s
 
diff --git a/lib/config/generated_config.go b/lib/config/generated_config.go
index d4be8e833..0a30398b4 100644
--- a/lib/config/generated_config.go
+++ b/lib/config/generated_config.go
@@ -167,7 +167,7 @@ Clusters:
       # Limits for how long a client token created by regular users can be valid,
       # and also is used as a default expiration policy when no expiration date is
       # specified.
-      # Default value zero menans token expirations don't get clamped and no
+      # Default value zero means token expirations don't get clamped and no
       # default expiration is set.
       MaxTokenLifetime: 0s
 

commit 9b59516d808b34b6176af1aea6fe78f279af067c
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date:   Thu Jan 28 17:44:59 2021 -0300

    16736: Limit token's expires_at depending on the cluster config and user type.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas at di-pentima.com.ar>

diff --git a/services/api/app/controllers/arvados/v1/api_client_authorizations_controller.rb b/services/api/app/controllers/arvados/v1/api_client_authorizations_controller.rb
index 59e359232..99446688d 100644
--- a/services/api/app/controllers/arvados/v1/api_client_authorizations_controller.rb
+++ b/services/api/app/controllers/arvados/v1/api_client_authorizations_controller.rb
@@ -17,6 +17,7 @@ class Arvados::V1::ApiClientAuthorizationsController < ApplicationController
       scopes: {type: 'array', required: false}
     }
   end
+
   def create_system_auth
     @object = ApiClientAuthorization.
       new(user_id: system_user.id,
diff --git a/services/api/app/models/api_client_authorization.rb b/services/api/app/models/api_client_authorization.rb
index 9290e01a1..4218645d5 100644
--- a/services/api/app/models/api_client_authorization.rb
+++ b/services/api/app/models/api_client_authorization.rb
@@ -13,6 +13,8 @@ class ApiClientAuthorization < ArvadosModel
   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
@@ -384,6 +386,17 @@ class ApiClientAuthorization < ArvadosModel
 
   protected
 
+  def clamp_token_expiration
+    if !current_user.andand.is_admin && Rails.configuration.API.MaxTokenLifetime > 0
+      max_token_expiration = Time.now + Rails.configuration.API.MaxTokenLifetime
+      if self.new_record? && (self.expires_at.nil? || self.expires_at > max_token_expiration)
+        self.expires_at = max_token_expiration
+      elsif !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
diff --git a/services/api/config/arvados_config.rb b/services/api/config/arvados_config.rb
index 5327713f6..8f4395dad 100644
--- a/services/api/config/arvados_config.rb
+++ b/services/api/config/arvados_config.rb
@@ -92,6 +92,7 @@ arvcfg.declare_config "API.DisabledAPIs", Hash, :disable_api_methods, ->(cfg, k,
 arvcfg.declare_config "API.MaxRequestSize", Integer, :max_request_size
 arvcfg.declare_config "API.MaxIndexDatabaseRead", Integer, :max_index_database_read
 arvcfg.declare_config "API.MaxItemsPerResponse", Integer, :max_items_per_response
+arvcfg.declare_config "API.MaxTokenLifetime", ActiveSupport::Duration
 arvcfg.declare_config "API.AsyncPermissionsUpdateInterval", ActiveSupport::Duration, :async_permissions_update_interval
 arvcfg.declare_config "Users.AutoSetupNewUsers", Boolean, :auto_setup_new_users
 arvcfg.declare_config "Users.AutoSetupNewUsersWithVmUUID", String, :auto_setup_new_users_with_vm_uuid
diff --git a/services/api/test/integration/api_client_authorizations_api_test.rb b/services/api/test/integration/api_client_authorizations_api_test.rb
index 296ab8a2f..3a7b20131 100644
--- a/services/api/test/integration/api_client_authorizations_api_test.rb
+++ b/services/api/test/integration/api_client_authorizations_api_test.rb
@@ -74,4 +74,95 @@ class ApiClientAuthorizationsApiTest < ActionDispatch::IntegrationTest
     assert_response 403
   end
 
+  [nil, Time.now + 2.hours].each do |desired_expiration|
+    test "expires_at gets clamped on non-admins when API.MaxTokenLifetime is set and desired expires_at #{desired_expiration.nil? ? 'is not set' : 'exceeds the limit'}" do
+      Rails.configuration.API.MaxTokenLifetime = 1.hour
+
+      # Test token creation
+      start_t = Time.now
+      post "/arvados/v1/api_client_authorizations",
+        params: {
+          :format => :json,
+          :api_client_authorization => {
+            :owner_uuid => users(:active).uuid,
+            :expires_at => desired_expiration,
+          }
+        },
+        headers: {'HTTP_AUTHORIZATION' => "OAuth2 #{api_client_authorizations(:active_trustedclient).api_token}"}
+      end_t = Time.now
+      assert_response 200
+      expiration_t = json_response['expires_at'].to_time
+      assert_operator expiration_t.to_f, :>, (start_t + Rails.configuration.API.MaxTokenLifetime).to_f
+      if !desired_expiration.nil?
+        assert_operator expiration_t.to_f, :<, desired_expiration.to_f
+      else
+        assert_operator expiration_t.to_f, :<, (end_t + Rails.configuration.API.MaxTokenLifetime).to_f
+      end
+
+      # Test token update
+      previous_expiration = expiration_t
+      token_uuid = json_response["uuid"]
+      start_t = Time.now
+      put "/arvados/v1/api_client_authorizations/#{token_uuid}",
+        params: {
+          :api_client_authorization => {
+            :expires_at => desired_expiration
+          }
+        },
+        headers: {'HTTP_AUTHORIZATION' => "OAuth2 #{api_client_authorizations(:active_trustedclient).api_token}"}
+      end_t = Time.now
+      assert_response 200
+      expiration_t = json_response['expires_at'].to_time
+      assert_operator previous_expiration.to_f, :<, expiration_t.to_f
+      assert_operator expiration_t.to_f, :>, (start_t + Rails.configuration.API.MaxTokenLifetime).to_f
+      if !desired_expiration.nil?
+        assert_operator expiration_t.to_f, :<, desired_expiration.to_f
+      else
+        assert_operator expiration_t.to_f, :<, (end_t + Rails.configuration.API.MaxTokenLifetime).to_f
+      end
+    end
+
+    test "expires_at can be set to #{desired_expiration.nil? ? 'nil' : 'exceed the limit'} by admins when API.MaxTokenLifetime is set" do
+      Rails.configuration.API.MaxTokenLifetime = 1.hour
+
+      # Test token creation
+      post "/arvados/v1/api_client_authorizations",
+        params: {
+          :format => :json,
+          :api_client_authorization => {
+            :owner_uuid => users(:admin).uuid,
+            :expires_at => desired_expiration,
+          }
+        },
+        headers: {'HTTP_AUTHORIZATION' => "OAuth2 #{api_client_authorizations(:admin_trustedclient).api_token}"}
+      assert_response 200
+      if desired_expiration.nil?
+        assert json_response['expires_at'].nil?
+      else
+        assert_equal json_response['expires_at'].to_time.to_i, desired_expiration.to_i
+      end
+
+      # Test token update (reverse the above behavior)
+      previous_expiration = json_response['expires_at']
+      token_uuid = json_response['uuid']
+      if previous_expiration.nil?
+        desired_updated_expiration = Time.now + Rails.configuration.API.MaxTokenLifetime + 1.hour
+      else
+        desired_updated_expiration = nil
+      end
+      put "/arvados/v1/api_client_authorizations/#{token_uuid}",
+        params: {
+          :api_client_authorization => {
+            :expires_at => desired_updated_expiration,
+          }
+        },
+        headers: {'HTTP_AUTHORIZATION' => "OAuth2 #{api_client_authorizations(:admin_trustedclient).api_token}"}
+      assert_response 200
+      if desired_updated_expiration.nil?
+        assert json_response['expires_at'].nil?
+      else
+        assert_equal json_response['expires_at'].to_time.to_i, desired_updated_expiration.to_i
+      end
+    end
+  end
 end
diff --git a/services/api/test/integration/user_sessions_test.rb b/services/api/test/integration/user_sessions_test.rb
index fcc0ce4e5..6e951499a 100644
--- a/services/api/test/integration/user_sessions_test.rb
+++ b/services/api/test/integration/user_sessions_test.rb
@@ -53,19 +53,19 @@ class UserSessionsApiTest < ActionDispatch::IntegrationTest
   test 'existing user login' do
     mock_auth_with(identity_url: "https://active-user.openid.local")
     u = assigns(:user)
-    assert_equal 'zzzzz-tpzed-xurymjxw79nv3jz', u.uuid
+    assert_equal users(:active).uuid, u.uuid
   end
 
   test 'user redirect_to_user_uuid' do
     mock_auth_with(identity_url: "https://redirects-to-active-user.openid.local")
     u = assigns(:user)
-    assert_equal 'zzzzz-tpzed-xurymjxw79nv3jz', u.uuid
+    assert_equal users(:active).uuid, u.uuid
   end
 
   test 'user double redirect_to_user_uuid' do
     mock_auth_with(identity_url: "https://double-redirects-to-active-user.openid.local")
     u = assigns(:user)
-    assert_equal 'zzzzz-tpzed-xurymjxw79nv3jz', u.uuid
+    assert_equal users(:active).uuid, u.uuid
   end
 
   test 'create new user during omniauth callback' do

commit ca3ae59a7f68bd5ed558aed5332fd30c6882dfd6
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date:   Fri Jan 15 12:34:57 2021 -0300

    16736: Adds API.MaxTokenLifetime config knob.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas at di-pentima.com.ar>

diff --git a/lib/config/config.default.yml b/lib/config/config.default.yml
index 771dc2ee7..8ced99b52 100644
--- a/lib/config/config.default.yml
+++ b/lib/config/config.default.yml
@@ -158,6 +158,13 @@ Clusters:
         dbname: ""
         SAMPLE: ""
     API:
+      # Limits for how long a client token created by regular users can be valid,
+      # and also is used as a default expiration policy when no expiration date is
+      # specified.
+      # Default value zero menans token expirations don't get clamped and no
+      # default expiration is set.
+      MaxTokenLifetime: 0s
+
       # Maximum size (in bytes) allowed for a single API request.  This
       # limit is published in the discovery document for use by clients.
       # Note: You must separately configure the upstream web server or
diff --git a/lib/config/export.go b/lib/config/export.go
index e4917032f..74c08703b 100644
--- a/lib/config/export.go
+++ b/lib/config/export.go
@@ -69,6 +69,7 @@ var whitelist = map[string]bool{
 	"API.MaxKeepBlobBuffers":                       false,
 	"API.MaxRequestAmplification":                  false,
 	"API.MaxRequestSize":                           true,
+	"API.MaxTokenLifetime":                         false,
 	"API.RequestTimeout":                           true,
 	"API.SendTimeout":                              true,
 	"API.WebsocketClientEventQueue":                false,
diff --git a/lib/config/generated_config.go b/lib/config/generated_config.go
index a202a5404..d4be8e833 100644
--- a/lib/config/generated_config.go
+++ b/lib/config/generated_config.go
@@ -164,6 +164,13 @@ Clusters:
         dbname: ""
         SAMPLE: ""
     API:
+      # Limits for how long a client token created by regular users can be valid,
+      # and also is used as a default expiration policy when no expiration date is
+      # specified.
+      # Default value zero menans token expirations don't get clamped and no
+      # default expiration is set.
+      MaxTokenLifetime: 0s
+
       # Maximum size (in bytes) allowed for a single API request.  This
       # limit is published in the discovery document for use by clients.
       # Note: You must separately configure the upstream web server or
diff --git a/sdk/go/arvados/config.go b/sdk/go/arvados/config.go
index 9dc9e17dd..05ac36cb4 100644
--- a/sdk/go/arvados/config.go
+++ b/sdk/go/arvados/config.go
@@ -86,6 +86,7 @@ type Cluster struct {
 		MaxKeepBlobBuffers             int
 		MaxRequestAmplification        int
 		MaxRequestSize                 int
+		MaxTokenLifetime               Duration
 		RequestTimeout                 Duration
 		SendTimeout                    Duration
 		WebsocketClientEventQueue      int

commit 88927599a6a5cec8b1914908ade787899fc3e418
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date:   Thu Sep 10 11:11:33 2020 -0300

    16736: Enhances tests about login issued tokens.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas at di-pentima.com.ar>

diff --git a/services/api/test/functional/user_sessions_controller_test.rb b/services/api/test/functional/user_sessions_controller_test.rb
index 129464cf1..1f9196893 100644
--- a/services/api/test/functional/user_sessions_controller_test.rb
+++ b/services/api/test/functional/user_sessions_controller_test.rb
@@ -30,6 +30,7 @@ class UserSessionsControllerTest < ActionController::TestCase
     authorize_with :inactive
     api_client_page = 'http://client.example.com/home'
     get :login, params: {return_to: api_client_page}
+    assert_response :redirect
     assert_not_nil assigns(:api_client)
     assert_nil assigns(:api_client_auth).expires_at
   end
@@ -40,6 +41,7 @@ class UserSessionsControllerTest < ActionController::TestCase
     authorize_with :inactive
     api_client_page = 'http://client.example.com/home'
     get :login, params: {return_to: api_client_page}
+    assert_response :redirect
     assert_not_nil assigns(:api_client)
     api_client_auth = assigns(:api_client_auth)
     assert_in_delta(api_client_auth.expires_at,

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list