15720: Defer user listing to login cluster, cache results locally.
[arvados.git] / lib / controller / federation / user_test.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package federation
6
7 import (
8         "encoding/json"
9         "errors"
10         "net/url"
11         "os"
12         "strings"
13
14         "git.curoverse.com/arvados.git/lib/controller/rpc"
15         "git.curoverse.com/arvados.git/sdk/go/arvados"
16         "git.curoverse.com/arvados.git/sdk/go/arvadostest"
17         check "gopkg.in/check.v1"
18 )
19
20 var _ = check.Suite(&UserSuite{})
21
22 type UserSuite struct {
23         FederationSuite
24 }
25
26 func (s *UserSuite) TestLoginClusterUserList(c *check.C) {
27         s.cluster.ClusterID = "local"
28         s.cluster.Login.LoginCluster = "zzzzz"
29         s.fed = New(s.cluster)
30         s.addDirectRemote(c, "zzzzz", rpc.NewConn("zzzzz", &url.URL{Scheme: "https", Host: os.Getenv("ARVADOS_API_HOST")}, true, rpc.PassthroughTokenProvider))
31
32         for _, updateFail := range []bool{false, true} {
33                 for _, opts := range []arvados.ListOptions{
34                         {Offset: 0, Limit: -1, Select: nil},
35                         {Offset: 1, Limit: 1, Select: nil},
36                         {Offset: 0, Limit: 2, Select: []string{"uuid"}},
37                         {Offset: 0, Limit: 2, Select: []string{"uuid", "email"}},
38                 } {
39                         c.Logf("updateFail %v, opts %#v", updateFail, opts)
40                         spy := arvadostest.NewProxy(c, s.cluster.Services.RailsAPI)
41                         stub := &arvadostest.APIStub{Error: errors.New("local cluster failure")}
42                         if updateFail {
43                                 s.fed.local = stub
44                         } else {
45                                 s.fed.local = rpc.NewConn(s.cluster.ClusterID, spy.URL, true, rpc.PassthroughTokenProvider)
46                         }
47                         userlist, err := s.fed.UserList(s.ctx, opts)
48                         if updateFail && err == nil {
49                                 // All local updates fail, so the only
50                                 // cases expected to succeed are the
51                                 // ones with 0 results.
52                                 c.Check(userlist.Items, check.HasLen, 0)
53                                 c.Check(stub.Calls(nil), check.HasLen, 0)
54                         } else if updateFail {
55                                 c.Logf("... err %#v", err)
56                                 calls := stub.Calls(stub.UserUpdate)
57                                 if c.Check(calls, check.HasLen, 1) {
58                                         c.Logf("... stub.UserUpdate called with options: %#v", calls[0].Options)
59                                         shouldUpdate := map[string]bool{
60                                                 "uuid":       false,
61                                                 "email":      true,
62                                                 "first_name": true,
63                                                 "last_name":  true,
64                                                 "is_admin":   true,
65                                                 "is_active":  true,
66                                                 "prefs":      true,
67                                                 // can't safely update locally
68                                                 "owner_uuid":   false,
69                                                 "identity_url": false,
70                                                 // virtual attrs
71                                                 "full_name":  false,
72                                                 "is_invited": false,
73                                         }
74                                         if opts.Select != nil {
75                                                 // Only the selected
76                                                 // fields (minus uuid)
77                                                 // should be updated.
78                                                 for k := range shouldUpdate {
79                                                         shouldUpdate[k] = false
80                                                 }
81                                                 for _, k := range opts.Select {
82                                                         if k != "uuid" {
83                                                                 shouldUpdate[k] = true
84                                                         }
85                                                 }
86                                         }
87                                         for k, shouldFind := range shouldUpdate {
88                                                 _, found := calls[0].Options.(arvados.UpdateOptions).Attrs[k]
89                                                 c.Check(found, check.Equals, shouldFind, check.Commentf("offending attr: %s", k))
90                                         }
91                                 }
92                         } else {
93                                 updates := 0
94                                 for _, d := range spy.RequestDumps {
95                                         d := string(d)
96                                         if strings.Contains(d, "PATCH /arvados/v1/users/zzzzz-tpzed-") {
97                                                 c.Check(d, check.Matches, `(?ms).*Authorization: Bearer `+arvadostest.SystemRootToken+`.*`)
98                                                 updates++
99                                         }
100                                 }
101                                 c.Check(err, check.IsNil)
102                                 c.Check(updates, check.Equals, len(userlist.Items))
103                                 c.Logf("... response items %#v", userlist.Items)
104                         }
105                 }
106         }
107 }
108
109 // userAttrsCachedFromLoginCluster must have an entry for every field
110 // in the User struct.
111 func (s *UserSuite) TestUserAttrsUpdateWhitelist(c *check.C) {
112         buf, err := json.Marshal(&arvados.User{})
113         c.Assert(err, check.IsNil)
114         var allFields map[string]interface{}
115         err = json.Unmarshal(buf, &allFields)
116         c.Assert(err, check.IsNil)
117         for k := range allFields {
118                 _, ok := userAttrsCachedFromLoginCluster[k]
119                 c.Check(ok, check.Equals, true, check.Commentf("field name %q missing from userAttrsCachedFromLoginCluster", k))
120         }
121 }