1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
14 "git.arvados.org/arvados.git/sdk/go/arvados"
15 "git.arvados.org/arvados.git/sdk/go/arvadostest"
19 // Gocheck boilerplate
20 func Test(t *testing.T) {
24 type TestSuite struct {
26 users map[string]arvados.User
29 func (s *TestSuite) SetUpSuite(c *C) {
30 arvadostest.StartAPI()
33 func (s *TestSuite) TearDownSuite(c *C) {
37 func (s *TestSuite) SetUpTest(c *C) {
38 ac := arvados.NewClientFromEnv()
39 u, err := ac.CurrentUser()
41 // Check that the parent group doesn't exist
42 sysUserUUID := u.UUID[:12] + "000000000000000"
43 gl := arvados.GroupList{}
44 params := arvados.ResourceListParams{
45 Filters: []arvados.Filter{{
52 Operand: "Externally synchronized groups",
55 ac.RequestAndDecode(&gl, "GET", "/arvados/v1/groups", nil, params)
56 c.Assert(gl.ItemsAvailable, Equals, 0)
58 os.Args = []string{"cmd", "somefile.csv"}
59 config, err := GetConfig()
61 // Confirm that the parent group was created
62 gl = arvados.GroupList{}
63 ac.RequestAndDecode(&gl, "GET", "/arvados/v1/groups", nil, params)
64 c.Assert(gl.ItemsAvailable, Equals, 1)
65 // Config set up complete, save config for further testing
68 // Fetch current user list
69 ul := arvados.UserList{}
70 params = arvados.ResourceListParams{
71 Filters: []arvados.Filter{{
74 Operand: s.cfg.SysUserUUID,
77 ac.RequestAndDecode(&ul, "GET", "/arvados/v1/users", nil, params)
78 c.Assert(ul.ItemsAvailable, Not(Equals), 0)
79 s.users = make(map[string]arvados.User)
80 for _, u := range ul.Items {
83 c.Assert(len(s.users), Not(Equals), 0)
86 func (s *TestSuite) TearDownTest(c *C) {
88 // Reset database to fixture state after every test run.
89 err := s.cfg.Client.RequestAndDecode(&dst, "POST", "/database/reset", nil, nil)
93 var _ = Suite(&TestSuite{})
95 // MakeTempCSVFile creates a temp file with data as comma separated values
96 func MakeTempCSVFile(data [][]string) (f *os.File, err error) {
97 f, err = ioutil.TempFile("", "test_sync_remote_groups")
101 for _, line := range data {
102 fmt.Fprintf(f, "%s\n", strings.Join(line, ","))
108 // GroupMembershipExists checks that both needed links exist between user and group
109 func GroupMembershipExists(ac *arvados.Client, userUUID string, groupUUID string, perm string) bool {
111 // Check Group -> User can_read permission
112 params := arvados.ResourceListParams{
113 Filters: []arvados.Filter{{
116 Operand: "permission",
131 ac.RequestAndDecode(&ll, "GET", "/arvados/v1/links", nil, params)
135 // Check User -> Group can_write permission
136 params = arvados.ResourceListParams{
137 Filters: []arvados.Filter{{
140 Operand: "permission",
155 ac.RequestAndDecode(&ll, "GET", "/arvados/v1/links", nil, params)
162 // If named group exists, return its UUID
163 func RemoteGroupExists(cfg *ConfigParams, groupName string) (uuid string, err error) {
164 gl := arvados.GroupList{}
165 params := arvados.ResourceListParams{
166 Filters: []arvados.Filter{{
173 Operand: cfg.SysUserUUID,
180 err = cfg.Client.RequestAndDecode(&gl, "GET", "/arvados/v1/groups", nil, params)
184 if gl.ItemsAvailable == 0 {
185 // No group with this name
187 } else if gl.ItemsAvailable == 1 {
189 uuid = gl.Items[0].UUID
191 // This should never happen
193 err = fmt.Errorf("more than 1 group found with the same name and parent")
198 func (s *TestSuite) TestParseFlagsWithPositionalArgument(c *C) {
199 cfg := ConfigParams{}
200 os.Args = []string{"cmd", "-verbose", "/tmp/somefile.csv"}
201 err := ParseFlags(&cfg)
203 c.Check(cfg.Path, Equals, "/tmp/somefile.csv")
204 c.Check(cfg.Verbose, Equals, true)
207 func (s *TestSuite) TestParseFlagsWithoutPositionalArgument(c *C) {
208 os.Args = []string{"cmd", "-verbose"}
209 err := ParseFlags(&ConfigParams{})
210 c.Assert(err, NotNil)
213 func (s *TestSuite) TestGetUserID(c *C) {
215 Email: "testuser@example.com",
216 Username: "Testuser",
218 email, err := GetUserID(u, "email")
220 c.Check(email, Equals, "testuser@example.com")
221 _, err = GetUserID(u, "bogus")
222 c.Assert(err, NotNil)
225 func (s *TestSuite) TestGetConfig(c *C) {
226 os.Args = []string{"cmd", "/tmp/somefile.csv"}
227 cfg, err := GetConfig()
229 c.Check(cfg.SysUserUUID, NotNil)
230 c.Check(cfg.Client, NotNil)
231 c.Check(cfg.ParentGroupUUID, NotNil)
232 c.Check(cfg.ParentGroupName, Equals, "Externally synchronized groups")
235 // Ignore leading & trailing spaces on group & users names
236 func (s *TestSuite) TestIgnoreSpaces(c *C) {
237 activeUserEmail := s.users[arvadostest.ActiveUserUUID].Email
238 activeUserUUID := s.users[arvadostest.ActiveUserUUID].UUID
239 // Confirm that the groups don't exist
240 for _, groupName := range []string{"TestGroup1", "TestGroup2", "Test Group 3"} {
241 groupUUID, err := RemoteGroupExists(s.cfg, groupName)
243 c.Assert(groupUUID, Equals, "")
246 {" TestGroup1", activeUserEmail},
247 {"TestGroup2 ", " " + activeUserEmail},
248 {" Test Group 3 ", activeUserEmail + " "},
250 tmpfile, err := MakeTempCSVFile(data)
252 defer os.Remove(tmpfile.Name()) // clean up
253 s.cfg.Path = tmpfile.Name()
256 // Check that 3 groups were created correctly, and have the active user as
258 for _, groupName := range []string{"TestGroup1", "TestGroup2", "Test Group 3"} {
259 groupUUID, err := RemoteGroupExists(s.cfg, groupName)
261 c.Assert(groupUUID, Not(Equals), "")
262 c.Assert(GroupMembershipExists(s.cfg.Client, activeUserUUID, groupUUID, "can_write"), Equals, true)
266 // Error out when records have <2 or >3 records
267 func (s *TestSuite) TestWrongNumberOfFields(c *C) {
268 for _, testCase := range [][][]string{
270 {{"field1", "field2", "field3", "field4"}},
271 {{"field1", "field2", "field3", "field4", "field5"}},
273 tmpfile, err := MakeTempCSVFile(testCase)
275 defer os.Remove(tmpfile.Name())
276 s.cfg.Path = tmpfile.Name()
278 c.Assert(err, Not(IsNil))
282 // Check different membership permissions
283 func (s *TestSuite) TestMembershipLevels(c *C) {
284 userEmail := s.users[arvadostest.ActiveUserUUID].Email
285 userUUID := s.users[arvadostest.ActiveUserUUID].UUID
287 {"TestGroup1", userEmail, "can_read"},
288 {"TestGroup2", userEmail, "can_write"},
289 {"TestGroup3", userEmail, "can_manage"},
290 {"TestGroup4", userEmail, "invalid_permission"},
292 tmpfile, err := MakeTempCSVFile(data)
294 defer os.Remove(tmpfile.Name()) // clean up
295 s.cfg.Path = tmpfile.Name()
298 for _, record := range data {
299 groupName := record[0]
300 permLevel := record[2]
301 if permLevel != "invalid_permission" {
302 groupUUID, err := RemoteGroupExists(s.cfg, groupName)
304 c.Assert(groupUUID, Not(Equals), "")
305 c.Assert(GroupMembershipExists(s.cfg.Client, userUUID, groupUUID, permLevel), Equals, true)
307 groupUUID, err := RemoteGroupExists(s.cfg, groupName)
309 c.Assert(groupUUID, Equals, "")
314 // Check membership level change
315 func (s *TestSuite) TestMembershipLevelUpdate(c *C) {
316 userEmail := s.users[arvadostest.ActiveUserUUID].Email
317 userUUID := s.users[arvadostest.ActiveUserUUID].UUID
318 groupName := "TestGroup1"
319 // Give read permissions
320 tmpfile, err := MakeTempCSVFile([][]string{{groupName, userEmail, "can_read"}})
322 defer os.Remove(tmpfile.Name()) // clean up
323 s.cfg.Path = tmpfile.Name()
327 groupUUID, err := RemoteGroupExists(s.cfg, groupName)
329 c.Assert(groupUUID, Not(Equals), "")
330 c.Assert(GroupMembershipExists(s.cfg.Client, userUUID, groupUUID, "can_read"), Equals, true)
331 c.Assert(GroupMembershipExists(s.cfg.Client, userUUID, groupUUID, "can_write"), Equals, false)
332 c.Assert(GroupMembershipExists(s.cfg.Client, userUUID, groupUUID, "can_manage"), Equals, false)
334 // Give write permissions
335 tmpfile, err = MakeTempCSVFile([][]string{{groupName, userEmail, "can_write"}})
337 defer os.Remove(tmpfile.Name()) // clean up
338 s.cfg.Path = tmpfile.Name()
342 c.Assert(GroupMembershipExists(s.cfg.Client, userUUID, groupUUID, "can_read"), Equals, false)
343 c.Assert(GroupMembershipExists(s.cfg.Client, userUUID, groupUUID, "can_write"), Equals, true)
344 c.Assert(GroupMembershipExists(s.cfg.Client, userUUID, groupUUID, "can_manage"), Equals, false)
346 // Give manage permissions
347 tmpfile, err = MakeTempCSVFile([][]string{{groupName, userEmail, "can_manage"}})
349 defer os.Remove(tmpfile.Name()) // clean up
350 s.cfg.Path = tmpfile.Name()
354 c.Assert(GroupMembershipExists(s.cfg.Client, userUUID, groupUUID, "can_read"), Equals, false)
355 c.Assert(GroupMembershipExists(s.cfg.Client, userUUID, groupUUID, "can_write"), Equals, false)
356 c.Assert(GroupMembershipExists(s.cfg.Client, userUUID, groupUUID, "can_manage"), Equals, true)
359 // The absence of a user membership on the CSV file implies its removal
360 func (s *TestSuite) TestMembershipRemoval(c *C) {
361 localUserEmail := s.users[arvadostest.ActiveUserUUID].Email
362 localUserUUID := s.users[arvadostest.ActiveUserUUID].UUID
363 remoteUserEmail := s.users[arvadostest.FederatedActiveUserUUID].Email
364 remoteUserUUID := s.users[arvadostest.FederatedActiveUserUUID].UUID
366 {"TestGroup1", localUserEmail},
367 {"TestGroup1", remoteUserEmail},
368 {"TestGroup2", localUserEmail},
369 {"TestGroup2", remoteUserEmail},
371 tmpfile, err := MakeTempCSVFile(data)
373 defer os.Remove(tmpfile.Name()) // clean up
374 s.cfg.Path = tmpfile.Name()
377 // Confirm that memberships exist
378 for _, groupName := range []string{"TestGroup1", "TestGroup2"} {
379 groupUUID, err := RemoteGroupExists(s.cfg, groupName)
381 c.Assert(groupUUID, Not(Equals), "")
382 c.Assert(GroupMembershipExists(s.cfg.Client, localUserUUID, groupUUID, "can_write"), Equals, true)
383 c.Assert(GroupMembershipExists(s.cfg.Client, remoteUserUUID, groupUUID, "can_write"), Equals, true)
385 // New CSV with some previous membership missing
387 {"TestGroup1", localUserEmail},
388 {"TestGroup2", remoteUserEmail},
390 tmpfile2, err := MakeTempCSVFile(data)
392 defer os.Remove(tmpfile2.Name()) // clean up
393 s.cfg.Path = tmpfile2.Name()
396 // Confirm TestGroup1 memberships
397 groupUUID, err := RemoteGroupExists(s.cfg, "TestGroup1")
399 c.Assert(groupUUID, Not(Equals), "")
400 c.Assert(GroupMembershipExists(s.cfg.Client, localUserUUID, groupUUID, "can_write"), Equals, true)
401 c.Assert(GroupMembershipExists(s.cfg.Client, remoteUserUUID, groupUUID, "can_write"), Equals, false)
402 // Confirm TestGroup1 memberships
403 groupUUID, err = RemoteGroupExists(s.cfg, "TestGroup2")
405 c.Assert(groupUUID, Not(Equals), "")
406 c.Assert(GroupMembershipExists(s.cfg.Client, localUserUUID, groupUUID, "can_write"), Equals, false)
407 c.Assert(GroupMembershipExists(s.cfg.Client, remoteUserUUID, groupUUID, "can_write"), Equals, true)
410 // If a group doesn't exist on the system, create it before adding users
411 func (s *TestSuite) TestAutoCreateGroupWhenNotExisting(c *C) {
412 groupName := "Testers"
413 // Confirm that group doesn't exist
414 groupUUID, err := RemoteGroupExists(s.cfg, groupName)
416 c.Assert(groupUUID, Equals, "")
417 // Make a tmp CSV file
419 {groupName, s.users[arvadostest.ActiveUserUUID].Email},
421 tmpfile, err := MakeTempCSVFile(data)
423 defer os.Remove(tmpfile.Name()) // clean up
424 s.cfg.Path = tmpfile.Name()
427 // "Testers" group should now exist
428 groupUUID, err = RemoteGroupExists(s.cfg, groupName)
430 c.Assert(groupUUID, Not(Equals), "")
431 // active user should be a member
432 c.Assert(GroupMembershipExists(s.cfg.Client, arvadostest.ActiveUserUUID, groupUUID, "can_write"), Equals, true)
435 // Users listed on the file that don't exist on the system are ignored
436 func (s *TestSuite) TestIgnoreNonexistantUsers(c *C) {
437 activeUserEmail := s.users[arvadostest.ActiveUserUUID].Email
438 activeUserUUID := s.users[arvadostest.ActiveUserUUID].UUID
439 // Confirm that group doesn't exist
440 groupUUID, err := RemoteGroupExists(s.cfg, "TestGroup4")
442 c.Assert(groupUUID, Equals, "")
443 // Create file & run command
445 {"TestGroup4", "nonexistantuser@unknowndomain.com"}, // Processed first
446 {"TestGroup4", activeUserEmail},
448 tmpfile, err := MakeTempCSVFile(data)
450 defer os.Remove(tmpfile.Name()) // clean up
451 s.cfg.Path = tmpfile.Name()
454 // Confirm that memberships exist
455 groupUUID, err = RemoteGroupExists(s.cfg, "TestGroup4")
457 c.Assert(groupUUID, Not(Equals), "")
458 c.Assert(GroupMembershipExists(s.cfg.Client, activeUserUUID, groupUUID, "can_write"), Equals, true)
461 // Users listed on the file that don't exist on the system are ignored
462 func (s *TestSuite) TestIgnoreEmptyFields(c *C) {
463 activeUserEmail := s.users[arvadostest.ActiveUserUUID].Email
464 activeUserUUID := s.users[arvadostest.ActiveUserUUID].UUID
465 // Confirm that group doesn't exist
466 for _, groupName := range []string{"TestGroup4", "TestGroup5"} {
467 groupUUID, err := RemoteGroupExists(s.cfg, groupName)
469 c.Assert(groupUUID, Equals, "")
471 // Create file & run command
473 {"", activeUserEmail}, // Empty field
474 {"TestGroup5", ""}, // Empty field
475 {"TestGroup5", activeUserEmail, ""}, // Empty 3rd field: is optional but cannot be empty
476 {"TestGroup4", activeUserEmail},
478 tmpfile, err := MakeTempCSVFile(data)
480 defer os.Remove(tmpfile.Name()) // clean up
481 s.cfg.Path = tmpfile.Name()
484 // Confirm that records about TestGroup5 were skipped
485 groupUUID, err := RemoteGroupExists(s.cfg, "TestGroup5")
487 c.Assert(groupUUID, Equals, "")
488 // Confirm that membership exists
489 groupUUID, err = RemoteGroupExists(s.cfg, "TestGroup4")
491 c.Assert(groupUUID, Not(Equals), "")
492 c.Assert(GroupMembershipExists(s.cfg.Client, activeUserUUID, groupUUID, "can_write"), Equals, true)
495 // Instead of emails, use username as identifier
496 func (s *TestSuite) TestUseUsernames(c *C) {
497 activeUserName := s.users[arvadostest.ActiveUserUUID].Username
498 activeUserUUID := s.users[arvadostest.ActiveUserUUID].UUID
499 // Confirm that group doesn't exist
500 groupUUID, err := RemoteGroupExists(s.cfg, "TestGroup1")
502 c.Assert(groupUUID, Equals, "")
503 // Create file & run command
505 {"TestGroup1", activeUserName},
507 tmpfile, err := MakeTempCSVFile(data)
509 defer os.Remove(tmpfile.Name()) // clean up
510 s.cfg.Path = tmpfile.Name()
511 s.cfg.UserID = "username"
513 s.cfg.UserID = "email"
515 // Confirm that memberships exist
516 groupUUID, err = RemoteGroupExists(s.cfg, "TestGroup1")
518 c.Assert(groupUUID, Not(Equals), "")
519 c.Assert(GroupMembershipExists(s.cfg.Client, activeUserUUID, groupUUID, "can_write"), Equals, true)