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