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/sdk/go/arvados"
18 "git.arvados.org/arvados.git/sdk/go/arvadostest"
19 "git.arvados.org/arvados.git/sdk/go/auth"
20 "git.arvados.org/arvados.git/sdk/go/ctxlog"
21 check "gopkg.in/check.v1"
24 var _ = check.Suite(&UserSuite{})
26 type UserSuite struct {
30 func (s *UserSuite) TestLoginClusterUserList(c *check.C) {
31 s.cluster.ClusterID = "local"
32 s.cluster.Login.LoginCluster = "zzzzz"
33 s.fed = New(s.cluster, nil)
34 s.addDirectRemote(c, "zzzzz", rpc.NewConn("zzzzz", &url.URL{Scheme: "https", Host: os.Getenv("ARVADOS_API_HOST")}, true, rpc.PassthroughTokenProvider))
36 for _, updateFail := range []bool{false, true} {
37 for _, opts := range []arvados.ListOptions{
38 {Offset: 0, Limit: -1, Select: nil},
39 {Offset: 0, Limit: math.MaxInt64, Select: nil},
40 {Offset: 1, Limit: 1, Select: nil},
41 {Offset: 0, Limit: 2, Select: []string{"uuid"}},
42 {Offset: 0, Limit: 2, Select: []string{"uuid", "email"}},
44 c.Logf("updateFail %v, opts %#v", updateFail, opts)
45 spy := arvadostest.NewProxy(c, s.cluster.Services.RailsAPI)
46 stub := &arvadostest.APIStub{Error: errors.New("local cluster failure")}
50 s.fed.local = rpc.NewConn(s.cluster.ClusterID, spy.URL, true, rpc.PassthroughTokenProvider)
52 userlist, err := s.fed.UserList(s.ctx, opts)
54 c.Logf("... UserList failed %q", err)
56 if updateFail && err == nil {
57 // All local updates fail, so the only
58 // cases expected to succeed are the
59 // ones with 0 results.
60 c.Check(userlist.Items, check.HasLen, 0)
61 c.Check(stub.Calls(nil), check.HasLen, 0)
62 } else if updateFail {
63 c.Logf("... err %#v", err)
64 calls := stub.Calls(stub.UserBatchUpdate)
65 if c.Check(calls, check.HasLen, 1) {
66 c.Logf("... stub.UserUpdate called with options: %#v", calls[0].Options)
67 shouldUpdate := map[string]bool{
75 // can't safely update locally
77 "identity_url": false,
82 if opts.Select != nil {
84 // fields (minus uuid)
86 for k := range shouldUpdate {
87 shouldUpdate[k] = false
89 for _, k := range opts.Select {
91 shouldUpdate[k] = true
96 for uuid = range calls[0].Options.(arvados.UserBatchUpdateOptions).Updates {
98 for k, shouldFind := range shouldUpdate {
99 _, found := calls[0].Options.(arvados.UserBatchUpdateOptions).Updates[uuid][k]
100 c.Check(found, check.Equals, shouldFind, check.Commentf("offending attr: %s", k))
105 for _, d := range spy.RequestDumps {
107 if strings.Contains(d, "PATCH /arvados/v1/users/batch") {
108 c.Check(d, check.Matches, `(?ms).*Authorization: Bearer `+arvadostest.SystemRootToken+`.*`)
112 c.Check(err, check.IsNil)
113 c.Check(updates, check.Equals, 1)
114 c.Logf("... response items %#v", userlist.Items)
120 func (s *UserSuite) TestLoginClusterUserGet(c *check.C) {
121 s.cluster.ClusterID = "local"
122 s.cluster.Login.LoginCluster = "zzzzz"
123 s.fed = New(s.cluster, nil)
124 s.addDirectRemote(c, "zzzzz", rpc.NewConn("zzzzz", &url.URL{Scheme: "https", Host: os.Getenv("ARVADOS_API_HOST")}, true, rpc.PassthroughTokenProvider))
126 opts := arvados.GetOptions{UUID: "zzzzz-tpzed-xurymjxw79nv3jz", Select: []string{"uuid", "email"}}
128 stub := &arvadostest.APIStub{Error: errors.New("local cluster failure")}
130 s.fed.UserGet(s.ctx, opts)
132 calls := stub.Calls(stub.UserBatchUpdate)
133 if c.Check(calls, check.HasLen, 1) {
134 c.Logf("... stub.UserUpdate called with options: %#v", calls[0].Options)
135 shouldUpdate := map[string]bool{
143 // can't safely update locally
145 "identity_url": false,
150 if opts.Select != nil {
152 // fields (minus uuid)
153 // should be updated.
154 for k := range shouldUpdate {
155 shouldUpdate[k] = false
157 for _, k := range opts.Select {
159 shouldUpdate[k] = true
164 for uuid = range calls[0].Options.(arvados.UserBatchUpdateOptions).Updates {
166 for k, shouldFind := range shouldUpdate {
167 _, found := calls[0].Options.(arvados.UserBatchUpdateOptions).Updates[uuid][k]
168 c.Check(found, check.Equals, shouldFind, check.Commentf("offending attr: %s", k))
174 func (s *UserSuite) TestLoginClusterUserListBypassFederation(c *check.C) {
175 s.cluster.ClusterID = "local"
176 s.cluster.Login.LoginCluster = "zzzzz"
177 s.fed = New(s.cluster, nil)
178 s.addDirectRemote(c, "zzzzz", rpc.NewConn("zzzzz", &url.URL{Scheme: "https", Host: os.Getenv("ARVADOS_API_HOST")},
179 true, rpc.PassthroughTokenProvider))
181 spy := arvadostest.NewProxy(c, s.cluster.Services.RailsAPI)
182 s.fed.local = rpc.NewConn(s.cluster.ClusterID, spy.URL, true, rpc.PassthroughTokenProvider)
184 _, err := s.fed.UserList(s.ctx, arvados.ListOptions{Offset: 0, Limit: math.MaxInt64, Select: nil, BypassFederation: true})
185 // this will fail because it is not using a root token
186 c.Check(err.(*arvados.TransactionError).StatusCode, check.Equals, 403)
188 // Now use SystemRootToken
189 ctx := context.Background()
190 ctx = ctxlog.Context(ctx, ctxlog.TestLogger(c))
191 ctx = auth.NewContext(ctx, &auth.Credentials{Tokens: []string{arvadostest.SystemRootToken}})
193 // Assert that it did not try to batch update users.
194 _, err = s.fed.UserList(ctx, arvados.ListOptions{Offset: 0, Limit: math.MaxInt64, Select: nil, BypassFederation: true})
195 for _, d := range spy.RequestDumps {
197 if strings.Contains(d, "PATCH /arvados/v1/users/batch") {
201 c.Check(err, check.IsNil)
204 // userAttrsCachedFromLoginCluster must have an entry for every field
205 // in the User struct.
206 func (s *UserSuite) TestUserAttrsUpdateWhitelist(c *check.C) {
207 buf, err := json.Marshal(&arvados.User{})
208 c.Assert(err, check.IsNil)
209 var allFields map[string]interface{}
210 err = json.Unmarshal(buf, &allFields)
211 c.Assert(err, check.IsNil)
212 for k := range allFields {
213 _, ok := userAttrsCachedFromLoginCluster[k]
214 c.Check(ok, check.Equals, true, check.Commentf("field name %q missing from userAttrsCachedFromLoginCluster", k))