1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
16 "git.arvados.org/arvados.git/lib/controller/rpc"
17 "git.arvados.org/arvados.git/lib/ctrlctx"
18 "git.arvados.org/arvados.git/sdk/go/arvados"
19 "git.arvados.org/arvados.git/sdk/go/arvadostest"
20 "git.arvados.org/arvados.git/sdk/go/auth"
21 "git.arvados.org/arvados.git/sdk/go/ctxlog"
22 check "gopkg.in/check.v1"
25 var _ = check.Suite(&UserSuite{})
27 type UserSuite struct {
31 func (s *UserSuite) TestLoginClusterUserList(c *check.C) {
32 s.cluster.ClusterID = "local"
33 s.cluster.Login.LoginCluster = "zzzzz"
34 s.fed = New(s.ctx, s.cluster, nil, (&ctrlctx.DBConnector{PostgreSQL: s.cluster.PostgreSQL}).GetDB)
35 s.addDirectRemote(c, "zzzzz", rpc.NewConn("zzzzz", &url.URL{Scheme: "https", Host: os.Getenv("ARVADOS_API_HOST")}, true, rpc.PassthroughTokenProvider))
37 for _, updateFail := range []bool{false, true} {
38 for _, opts := range []arvados.ListOptions{
39 {Offset: 0, Limit: -1, Select: nil},
40 {Offset: 0, Limit: math.MaxInt64, Select: nil},
41 {Offset: 1, Limit: 1, Select: nil},
42 {Offset: 0, Limit: 2, Select: []string{"uuid"}},
43 {Offset: 0, Limit: 2, Select: []string{"uuid", "email"}},
45 c.Logf("updateFail %v, opts %#v", updateFail, opts)
46 spy := arvadostest.NewProxy(c, s.cluster.Services.RailsAPI)
47 stub := &arvadostest.APIStub{Error: errors.New("local cluster failure")}
51 s.fed.local = rpc.NewConn(s.cluster.ClusterID, spy.URL, true, rpc.PassthroughTokenProvider)
53 userlist, err := s.fed.UserList(s.ctx, opts)
55 c.Logf("... UserList failed %q", err)
57 if updateFail && err == nil {
58 // All local updates fail, so the only
59 // cases expected to succeed are the
60 // ones with 0 results.
61 c.Check(userlist.Items, check.HasLen, 0)
62 c.Check(stub.Calls(nil), check.HasLen, 0)
63 } else if updateFail {
64 c.Logf("... err %#v", err)
65 calls := stub.Calls(stub.UserBatchUpdate)
66 if c.Check(calls, check.HasLen, 1) {
67 c.Logf("... stub.UserUpdate called with options: %#v", calls[0].Options)
68 shouldUpdate := map[string]bool{
76 // can't safely update locally
78 "identity_url": false,
83 if opts.Select != nil {
85 // fields (minus uuid)
87 for k := range shouldUpdate {
88 shouldUpdate[k] = false
90 for _, k := range opts.Select {
92 shouldUpdate[k] = true
97 for uuid = range calls[0].Options.(arvados.UserBatchUpdateOptions).Updates {
99 for k, shouldFind := range shouldUpdate {
100 _, found := calls[0].Options.(arvados.UserBatchUpdateOptions).Updates[uuid][k]
101 c.Check(found, check.Equals, shouldFind, check.Commentf("offending attr: %s", k))
106 for _, d := range spy.RequestDumps {
108 if strings.Contains(d, "PATCH /arvados/v1/users/batch") {
109 c.Check(d, check.Matches, `(?ms).*Authorization: Bearer `+arvadostest.SystemRootToken+`.*`)
113 c.Check(err, check.IsNil)
114 c.Check(updates, check.Equals, 1)
115 c.Logf("... response items %#v", userlist.Items)
121 func (s *UserSuite) TestLoginClusterUserGet(c *check.C) {
122 s.cluster.ClusterID = "local"
123 s.cluster.Login.LoginCluster = "zzzzz"
124 s.fed = New(s.ctx, s.cluster, nil, (&ctrlctx.DBConnector{PostgreSQL: s.cluster.PostgreSQL}).GetDB)
125 s.addDirectRemote(c, "zzzzz", rpc.NewConn("zzzzz", &url.URL{Scheme: "https", Host: os.Getenv("ARVADOS_API_HOST")}, true, rpc.PassthroughTokenProvider))
127 opts := arvados.GetOptions{UUID: "zzzzz-tpzed-xurymjxw79nv3jz", Select: []string{"uuid", "email"}}
129 stub := &arvadostest.APIStub{Error: errors.New("local cluster failure")}
131 s.fed.UserGet(s.ctx, opts)
133 calls := stub.Calls(stub.UserBatchUpdate)
134 if c.Check(calls, check.HasLen, 1) {
135 c.Logf("... stub.UserUpdate called with options: %#v", calls[0].Options)
136 shouldUpdate := map[string]bool{
144 // can't safely update locally
146 "identity_url": false,
151 if opts.Select != nil {
153 // fields (minus uuid)
154 // should be updated.
155 for k := range shouldUpdate {
156 shouldUpdate[k] = false
158 for _, k := range opts.Select {
160 shouldUpdate[k] = true
165 for uuid = range calls[0].Options.(arvados.UserBatchUpdateOptions).Updates {
167 for k, shouldFind := range shouldUpdate {
168 _, found := calls[0].Options.(arvados.UserBatchUpdateOptions).Updates[uuid][k]
169 c.Check(found, check.Equals, shouldFind, check.Commentf("offending attr: %s", k))
175 func (s *UserSuite) TestLoginClusterUserListBypassFederation(c *check.C) {
176 s.cluster.ClusterID = "local"
177 s.cluster.Login.LoginCluster = "zzzzz"
178 s.fed = New(s.ctx, s.cluster, nil, (&ctrlctx.DBConnector{PostgreSQL: s.cluster.PostgreSQL}).GetDB)
179 s.addDirectRemote(c, "zzzzz", rpc.NewConn("zzzzz", &url.URL{Scheme: "https", Host: os.Getenv("ARVADOS_API_HOST")},
180 true, rpc.PassthroughTokenProvider))
182 spy := arvadostest.NewProxy(c, s.cluster.Services.RailsAPI)
183 s.fed.local = rpc.NewConn(s.cluster.ClusterID, spy.URL, true, rpc.PassthroughTokenProvider)
185 _, err := s.fed.UserList(s.ctx, arvados.ListOptions{Offset: 0, Limit: math.MaxInt64, Select: nil, BypassFederation: true})
186 // this will fail because it is not using a root token
187 c.Check(err.(*arvados.TransactionError).StatusCode, check.Equals, 403)
189 // Now use SystemRootToken
190 ctx := context.Background()
191 ctx = ctxlog.Context(ctx, ctxlog.TestLogger(c))
192 ctx = auth.NewContext(ctx, &auth.Credentials{Tokens: []string{arvadostest.SystemRootToken}})
194 // Assert that it did not try to batch update users.
195 _, err = s.fed.UserList(ctx, arvados.ListOptions{Offset: 0, Limit: math.MaxInt64, Select: nil, BypassFederation: true})
196 for _, d := range spy.RequestDumps {
198 if strings.Contains(d, "PATCH /arvados/v1/users/batch") {
202 c.Check(err, check.IsNil)
205 // userAttrsCachedFromLoginCluster must have an entry for every field
206 // in the User struct.
207 func (s *UserSuite) TestUserAttrsUpdateWhitelist(c *check.C) {
208 buf, err := json.Marshal(&arvados.User{})
209 c.Assert(err, check.IsNil)
210 var allFields map[string]interface{}
211 err = json.Unmarshal(buf, &allFields)
212 c.Assert(err, check.IsNil)
213 for k := range allFields {
214 _, ok := userAttrsCachedFromLoginCluster[k]
215 c.Check(ok, check.Equals, true, check.Commentf("field name %q missing from userAttrsCachedFromLoginCluster", k))