[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