arvados-login-sync 2.6.3 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/bin/arvados-login-sync +122 -91
  3. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 051af26b13fc808c8fc6da81ec2d42948c38c660e072c04b4a4945c462bc7e1f
4
- data.tar.gz: 8169f9968940af3d08186738592b88ea19fb67ed3b630c127c6ea91798878e56
3
+ metadata.gz: c9b0950157ae5cff15171e2c96d7fd5bbdf4684a7fd2bac988a5f772abffb42e
4
+ data.tar.gz: 89dde3501d31d77bd5c5400abbc159b4f0d7bf0101239292b156cb0b9b7269d5
5
5
  SHA512:
6
- metadata.gz: 39951b7f294e5e4a926ac0286f23487804b47c8e1665eda866f0a04163d04e148eded1de600ad1c1af5528f221d1e7f601604fe2a173501b17ac2badf3919a74
7
- data.tar.gz: dc8fabfa2b5ba4134171eefc8c2c35cce94267911f2ce2015ded656eea9742a0a58fa3c735ae232834677f0c843559767dddb0a1d2d61e40852471edf5a94def
6
+ metadata.gz: 9e93ed77b495997e94bb662d8de19e19bd27867a882180598ae0d863cd0d2a13f1dbae8f9696f7a25a30db69509982948fc55ca21dac91ea006e1a8f36ebc78f
7
+ data.tar.gz: 193c3497f9cbab45f1170d3ea86ad832bacfb400fe965c375002d937972e66492eaaf896e76faeb6cee11068f9daf1e16300d93ce96af2ead21ceae6b020e9f8
@@ -12,6 +12,18 @@ require 'yaml'
12
12
  require 'optparse'
13
13
  require 'open3'
14
14
 
15
+ def ensure_dir(path, mode, owner, group)
16
+ begin
17
+ Dir.mkdir(path, mode)
18
+ rescue Errno::EEXIST
19
+ # No change needed
20
+ false
21
+ else
22
+ FileUtils.chown(owner, group, path)
23
+ true
24
+ end
25
+ end
26
+
15
27
  req_envs = %w(ARVADOS_API_HOST ARVADOS_API_TOKEN ARVADOS_VIRTUAL_MACHINE_UUID)
16
28
  req_envs.each do |k|
17
29
  unless ENV[k]
@@ -34,6 +46,15 @@ exclusive_banner = "############################################################
34
46
  start_banner = "### BEGIN Arvados-managed keys -- changes between markers will be overwritten\n"
35
47
  end_banner = "### END Arvados-managed keys -- changes between markers will be overwritten\n"
36
48
 
49
+ actions = {
50
+ # These names correspond to the names in the cluster Users configuration.
51
+ # Managing everything was the original behavior.
52
+ SyncUserAccounts: true,
53
+ SyncUserGroups: true,
54
+ SyncUserSSHKeys: true,
55
+ SyncUserAPITokens: true,
56
+ }
57
+
37
58
  keys = ''
38
59
 
39
60
  begin
@@ -45,6 +66,17 @@ begin
45
66
  logincluster_host = ENV['ARVADOS_API_HOST']
46
67
  logincluster_name = arv.cluster_config['Login']['LoginCluster'] or ''
47
68
 
69
+ # Requiring the fuse group was previous hardcoded behavior
70
+ minimum_groups = arv.cluster_config['Users']['SyncRequiredGroups'] || ['fuse']
71
+ ignored_groups = arv.cluster_config['Users']['SyncIgnoredGroups'] || []
72
+ (minimum_groups & ignored_groups).each do |group_name|
73
+ STDERR.puts "WARNING: #{group_name} is listed in both SyncRequiredGroups and SyncIgnoredGroups. It will be ignored."
74
+ end
75
+
76
+ actions.each_pair do |key, default|
77
+ actions[key] = arv.cluster_config['Users'].fetch(key.to_s, default)
78
+ end
79
+
48
80
  if logincluster_name != '' and logincluster_name != arv.cluster_config['ClusterID']
49
81
  logincluster_host = arv.cluster_config['RemoteClusters'][logincluster_name]['Host']
50
82
  end
@@ -112,11 +144,12 @@ begin
112
144
 
113
145
  seen = Hash.new()
114
146
 
115
- current_user_groups = Hash.new
147
+ all_groups = []
148
+ current_user_groups = Hash.new { |hash, key| hash[key] = [] }
116
149
  while (ent = Etc.getgrent()) do
150
+ all_groups << ent.name
117
151
  ent.mem.each do |member|
118
- current_user_groups[member] ||= Array.new
119
- current_user_groups[member].push ent.name
152
+ current_user_groups[member] << ent.name
120
153
  end
121
154
  end
122
155
  Etc.endgrent()
@@ -128,6 +161,10 @@ begin
128
161
  username = l[:username]
129
162
 
130
163
  unless pwnam[l[:username]]
164
+ unless actions[:SyncUserAccounts]
165
+ STDERR.puts "User #{username} does not exist and SyncUserAccounts=false. Skipping."
166
+ next
167
+ end
131
168
  STDERR.puts "Creating account #{l[:username]}"
132
169
  # Create new user
133
170
  out, st = Open3.capture2e("useradd", "-m",
@@ -146,15 +183,21 @@ begin
146
183
  end
147
184
  end
148
185
 
149
- existing_groups = current_user_groups[username] || []
150
- groups = l[:groups] || []
151
- # Adding users to the FUSE group has long been hardcoded behavior.
152
- groups << "fuse"
153
- groups << username
154
- groups.select! { |g| Etc.getgrnam(g) rescue false }
186
+ user_gid = pwnam[username].gid
187
+ homedir = pwnam[l[:username]].dir
188
+ if !File.exist?(homedir)
189
+ STDERR.puts "Cannot set up user #{username} because their home directory #{homedir} does not exist. Skipping."
190
+ next
191
+ end
192
+
193
+ if actions[:SyncUserGroups]
194
+ have_groups = current_user_groups[username] - ignored_groups
195
+ want_groups = l[:groups] || []
196
+ want_groups |= minimum_groups
197
+ want_groups -= ignored_groups
198
+ want_groups &= all_groups
155
199
 
156
- groups.each do |addgroup|
157
- if existing_groups.index(addgroup).nil?
200
+ (want_groups - have_groups).each do |addgroup|
158
201
  # User should be in group, but isn't, so add them.
159
202
  STDERR.puts "Add user #{username} to #{addgroup} group"
160
203
  out, st = Open3.capture2e("usermod", "-aG", addgroup, username)
@@ -162,10 +205,8 @@ begin
162
205
  STDERR.puts "Failed to add #{username} to #{addgroup} group:\n#{out}"
163
206
  end
164
207
  end
165
- end
166
208
 
167
- existing_groups.each do |removegroup|
168
- if groups.index(removegroup).nil?
209
+ (have_groups - want_groups).each do |removegroup|
169
210
  # User is in a group, but shouldn't be, so remove them.
170
211
  STDERR.puts "Remove user #{username} from #{removegroup} group"
171
212
  out, st = Open3.capture2e("gpasswd", "-d", username, removegroup)
@@ -175,96 +216,86 @@ begin
175
216
  end
176
217
  end
177
218
 
178
- homedir = pwnam[l[:username]].dir
179
- userdotssh = File.join(homedir, ".ssh")
180
- Dir.mkdir(userdotssh) if !File.exist?(userdotssh)
219
+ if actions[:SyncUserSSHKeys]
220
+ userdotssh = File.join(homedir, ".ssh")
221
+ ensure_dir(userdotssh, 0700, username, user_gid)
181
222
 
182
- newkeys = "###\n###\n" + keys[l[:username]].join("\n") + "\n###\n###\n"
223
+ newkeys = "###\n###\n" + keys[l[:username]].join("\n") + "\n###\n###\n"
183
224
 
184
- keysfile = File.join(userdotssh, "authorized_keys")
225
+ keysfile = File.join(userdotssh, "authorized_keys")
226
+ begin
227
+ oldkeys = File.read(keysfile)
228
+ rescue Errno::ENOENT
229
+ oldkeys = ""
230
+ end
185
231
 
186
- if File.exist?(keysfile)
187
- oldkeys = IO::read(keysfile)
188
- else
189
- oldkeys = ""
190
- end
232
+ if options[:exclusive]
233
+ newkeys = exclusive_banner + newkeys
234
+ elsif oldkeys.start_with?(exclusive_banner)
235
+ newkeys = start_banner + newkeys + end_banner
236
+ elsif (m = /^(.*?\n|)#{start_banner}(.*?\n|)#{end_banner}(.*)/m.match(oldkeys))
237
+ newkeys = m[1] + start_banner + newkeys + end_banner + m[3]
238
+ else
239
+ newkeys = start_banner + newkeys + end_banner + oldkeys
240
+ end
191
241
 
192
- if options[:exclusive]
193
- newkeys = exclusive_banner + newkeys
194
- elsif oldkeys.start_with?(exclusive_banner)
195
- newkeys = start_banner + newkeys + end_banner
196
- elsif (m = /^(.*?\n|)#{start_banner}(.*?\n|)#{end_banner}(.*)/m.match(oldkeys))
197
- newkeys = m[1] + start_banner + newkeys + end_banner + m[3]
198
- else
199
- newkeys = start_banner + newkeys + end_banner + oldkeys
242
+ if oldkeys != newkeys then
243
+ File.open(keysfile, 'w', 0600) do |f|
244
+ f.write(newkeys)
245
+ end
246
+ FileUtils.chown(username, user_gid, keysfile)
247
+ end
200
248
  end
201
249
 
202
- if oldkeys != newkeys then
203
- f = File.new(keysfile, 'w')
204
- f.write(newkeys)
205
- f.close()
206
- end
250
+ if actions[:SyncUserAPITokens]
251
+ userdotconfig = File.join(homedir, ".config")
252
+ ensure_dir(userdotconfig, 0755, username, user_gid)
253
+ configarvados = File.join(userdotconfig, "arvados")
254
+ ensure_dir(configarvados, 0700, username, user_gid)
207
255
 
208
- userdotconfig = File.join(homedir, ".config")
209
- if !File.exist?(userdotconfig)
210
- Dir.mkdir(userdotconfig)
211
- end
256
+ tokenfile = File.join(configarvados, "settings.conf")
212
257
 
213
- configarvados = File.join(userdotconfig, "arvados")
214
- Dir.mkdir(configarvados) if !File.exist?(configarvados)
215
-
216
- tokenfile = File.join(configarvados, "settings.conf")
217
-
218
- begin
219
- STDERR.puts "Processing #{tokenfile} ..." if debug
220
- newToken = false
221
- if File.exist?(tokenfile)
222
- # check if the token is still valid
223
- myToken = ENV["ARVADOS_API_TOKEN"]
224
- userEnv = IO::read(tokenfile)
225
- if (m = /^ARVADOS_API_TOKEN=(.*?\n)/m.match(userEnv))
226
- begin
227
- tmp_arv = Arvados.new({ :api_host => logincluster_host,
228
- :api_token => (m[1]),
229
- :suppress_ssl_warnings => false })
230
- tmp_arv.user.current
231
- rescue Arvados::TransactionFailedError => e
232
- if e.to_s =~ /401 Unauthorized/
233
- STDERR.puts "Account #{l[:username]} token not valid, creating new token."
234
- newToken = true
235
- else
236
- raise
258
+ begin
259
+ STDERR.puts "Processing #{tokenfile} ..." if debug
260
+ newToken = false
261
+ if File.exist?(tokenfile)
262
+ # check if the token is still valid
263
+ myToken = ENV["ARVADOS_API_TOKEN"]
264
+ userEnv = File.read(tokenfile)
265
+ if (m = /^ARVADOS_API_TOKEN=(.*?\n)/m.match(userEnv))
266
+ begin
267
+ tmp_arv = Arvados.new({ :api_host => logincluster_host,
268
+ :api_token => (m[1]),
269
+ :suppress_ssl_warnings => false })
270
+ tmp_arv.user.current
271
+ rescue Arvados::TransactionFailedError => e
272
+ if e.to_s =~ /401 Unauthorized/
273
+ STDERR.puts "Account #{l[:username]} token not valid, creating new token."
274
+ newToken = true
275
+ else
276
+ raise
277
+ end
237
278
  end
238
279
  end
280
+ elsif !File.exist?(tokenfile) || options[:"rotate-tokens"]
281
+ STDERR.puts "Account #{l[:username]} token file not found, creating new token."
282
+ newToken = true
239
283
  end
240
- elsif !File.exist?(tokenfile) || options[:"rotate-tokens"]
241
- STDERR.puts "Account #{l[:username]} token file not found, creating new token."
242
- newToken = true
243
- end
244
- if newToken
245
- aca_params = {owner_uuid: l[:user_uuid], api_client_id: 0}
246
- if options[:"token-lifetime"] && options[:"token-lifetime"] > 0
247
- aca_params.merge!(expires_at: (Time.now + options[:"token-lifetime"]))
284
+ if newToken
285
+ aca_params = {owner_uuid: l[:user_uuid], api_client_id: 0}
286
+ if options[:"token-lifetime"] && options[:"token-lifetime"] > 0
287
+ aca_params.merge!(expires_at: (Time.now + options[:"token-lifetime"]))
288
+ end
289
+ user_token = logincluster_arv.api_client_authorization.create(api_client_authorization: aca_params)
290
+ File.open(tokenfile, 'w', 0600) do |f|
291
+ f.write("ARVADOS_API_HOST=#{ENV['ARVADOS_API_HOST']}\n")
292
+ f.write("ARVADOS_API_TOKEN=v2/#{user_token[:uuid]}/#{user_token[:api_token]}\n")
293
+ end
294
+ FileUtils.chown(username, user_gid, tokenfile)
248
295
  end
249
- user_token = logincluster_arv.api_client_authorization.create(api_client_authorization: aca_params)
250
- f = File.new(tokenfile, 'w')
251
- f.write("ARVADOS_API_HOST=#{ENV['ARVADOS_API_HOST']}\n")
252
- f.write("ARVADOS_API_TOKEN=v2/#{user_token[:uuid]}/#{user_token[:api_token]}\n")
253
- f.close()
296
+ rescue => e
297
+ STDERR.puts "Error setting token for #{l[:username]}: #{e}"
254
298
  end
255
- rescue => e
256
- STDERR.puts "Error setting token for #{l[:username]}: #{e}"
257
- end
258
-
259
- FileUtils.chown_R(l[:username], nil, userdotssh)
260
- FileUtils.chown_R(l[:username], nil, userdotconfig)
261
- File.chmod(0700, userdotssh)
262
- File.chmod(0700, userdotconfig)
263
- File.chmod(0700, configarvados)
264
- File.chmod(0750, homedir)
265
- File.chmod(0600, keysfile)
266
- if File.exist?(tokenfile)
267
- File.chmod(0600, tokenfile)
268
299
  end
269
300
  end
270
301
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: arvados-login-sync
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.3
4
+ version: 2.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arvados Authors
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-10-13 00:00:00.000000000 Z
11
+ date: 2023-06-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: arvados
@@ -79,7 +79,7 @@ dependencies:
79
79
  - !ruby/object:Gem::Version
80
80
  version: '0.12'
81
81
  description: Creates and updates local login accounts for Arvados users. Built from
82
- git commit 83974ae9df4060f7aaa6bba61997404a2a7405b2
82
+ git commit b5dad64d1faa5063482db0d33d22805912abdda6
83
83
  email: packaging@arvados.org
84
84
  executables:
85
85
  - arvados-login-sync