1fde64d119a15892f560062895157238aa3a62e3
[arvados.git] / lib / controller / localdb / group_test.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package localdb
6
7 import (
8         "context"
9
10         "git.arvados.org/arvados.git/lib/config"
11         "git.arvados.org/arvados.git/lib/controller/rpc"
12         "git.arvados.org/arvados.git/sdk/go/arvados"
13         "git.arvados.org/arvados.git/sdk/go/arvadostest"
14         "git.arvados.org/arvados.git/sdk/go/auth"
15         "git.arvados.org/arvados.git/sdk/go/ctxlog"
16         check "gopkg.in/check.v1"
17 )
18
19 var _ = check.Suite(&GroupSuite{})
20
21 type GroupSuite struct {
22         cluster  *arvados.Cluster
23         localdb  *Conn
24         railsSpy *arvadostest.Proxy
25 }
26
27 func (s *GroupSuite) SetUpSuite(c *check.C) {
28         cfg, err := config.NewLoader(nil, ctxlog.TestLogger(c)).Load()
29         c.Assert(err, check.IsNil)
30         s.cluster, err = cfg.GetCluster("")
31         c.Assert(err, check.IsNil)
32         s.localdb = NewConn(s.cluster)
33         s.railsSpy = arvadostest.NewProxy(c, s.cluster.Services.RailsAPI)
34         *s.localdb.railsProxy = *rpc.NewConn(s.cluster.ClusterID, s.railsSpy.URL, true, rpc.PassthroughTokenProvider)
35 }
36
37 func (s *GroupSuite) TearDownSuite(c *check.C) {
38         s.railsSpy.Close()
39         // Undo any changes/additions to the user database so they
40         // don't affect subsequent tests.
41         arvadostest.ResetEnv()
42         c.Check(arvados.NewClientFromEnv().RequestAndDecode(nil, "POST", "database/reset", nil, nil), check.IsNil)
43 }
44
45 func (s *GroupSuite) setUpVocabulary(c *check.C, testVocabulary string) {
46         if testVocabulary == "" {
47                 testVocabulary = `{
48                         "strict_tags": false,
49                         "tags": {
50                                 "IDTAGIMPORTANCES": {
51                                         "strict": true,
52                                         "labels": [{"label": "Importance"}, {"label": "Priority"}],
53                                         "values": {
54                                                 "IDVALIMPORTANCES1": { "labels": [{"label": "Critical"}, {"label": "Urgent"}, {"label": "High"}] },
55                                                 "IDVALIMPORTANCES2": { "labels": [{"label": "Normal"}, {"label": "Moderate"}] },
56                                                 "IDVALIMPORTANCES3": { "labels": [{"label": "Low"}] }
57                                         }
58                                 }
59                         }
60                 }`
61         }
62         voc, err := arvados.NewVocabulary([]byte(testVocabulary), []string{})
63         c.Assert(err, check.IsNil)
64         s.localdb.vocabularyCache = voc
65         s.cluster.API.VocabularyPath = "foo"
66 }
67
68 func (s *GroupSuite) TestGroupCreateWithProperties(c *check.C) {
69         s.setUpVocabulary(c, "")
70         ctx := auth.NewContext(context.Background(), &auth.Credentials{Tokens: []string{arvadostest.ActiveTokenV2}})
71
72         tests := []struct {
73                 name    string
74                 props   map[string]interface{}
75                 success bool
76         }{
77                 {"Invalid prop key", map[string]interface{}{"Priority": "IDVALIMPORTANCES1"}, false},
78                 {"Invalid prop value", map[string]interface{}{"IDTAGIMPORTANCES": "high"}, false},
79                 {"Valid prop key & value", map[string]interface{}{"IDTAGIMPORTANCES": "IDVALIMPORTANCES1"}, true},
80                 {"Empty properties", map[string]interface{}{}, true},
81         }
82         for _, tt := range tests {
83                 c.Log(c.TestName()+" ", tt.name)
84
85                 grp, err := s.localdb.GroupCreate(ctx, arvados.CreateOptions{
86                         Select: []string{"uuid", "properties"},
87                         Attrs: map[string]interface{}{
88                                 "group_class": "project",
89                                 "properties":  tt.props,
90                         }})
91                 if tt.success {
92                         c.Assert(err, check.IsNil)
93                         c.Assert(grp.Properties, check.DeepEquals, tt.props)
94                 } else {
95                         c.Assert(err, check.NotNil)
96                 }
97         }
98 }
99
100 func (s *GroupSuite) TestGroupUpdateWithProperties(c *check.C) {
101         s.setUpVocabulary(c, "")
102         ctx := auth.NewContext(context.Background(), &auth.Credentials{Tokens: []string{arvadostest.ActiveTokenV2}})
103
104         tests := []struct {
105                 name    string
106                 props   map[string]interface{}
107                 success bool
108         }{
109                 {"Invalid prop key", map[string]interface{}{"Priority": "IDVALIMPORTANCES1"}, false},
110                 {"Invalid prop value", map[string]interface{}{"IDTAGIMPORTANCES": "high"}, false},
111                 {"Valid prop key & value", map[string]interface{}{"IDTAGIMPORTANCES": "IDVALIMPORTANCES1"}, true},
112                 {"Empty properties", map[string]interface{}{}, true},
113         }
114         for _, tt := range tests {
115                 c.Log(c.TestName()+" ", tt.name)
116                 grp, err := s.localdb.GroupCreate(ctx, arvados.CreateOptions{
117                         Attrs: map[string]interface{}{
118                                 "group_class": "project",
119                         },
120                 })
121                 c.Assert(err, check.IsNil)
122                 grp, err = s.localdb.GroupUpdate(ctx, arvados.UpdateOptions{
123                         UUID:   grp.UUID,
124                         Select: []string{"uuid", "properties"},
125                         Attrs: map[string]interface{}{
126                                 "properties": tt.props,
127                         }})
128                 if tt.success {
129                         c.Assert(err, check.IsNil)
130                         c.Assert(grp.Properties, check.DeepEquals, tt.props)
131                 } else {
132                         c.Assert(err, check.NotNil)
133                 }
134         }
135 }
136
137 func (s *GroupSuite) TestCanWriteCanManageResponses(c *check.C) {
138         ctxUser1 := auth.NewContext(context.Background(), &auth.Credentials{Tokens: []string{arvadostest.ActiveTokenV2}})
139         ctxUser2 := auth.NewContext(context.Background(), &auth.Credentials{Tokens: []string{arvadostest.SpectatorToken}})
140         ctxAdmin := auth.NewContext(context.Background(), &auth.Credentials{Tokens: []string{arvadostest.AdminToken}})
141         project, err := s.localdb.GroupCreate(ctxUser1, arvados.CreateOptions{
142                 Attrs: map[string]interface{}{
143                         "group_class": "project",
144                 },
145         })
146         c.Assert(err, check.IsNil)
147         c.Check(project.CanWrite, check.Equals, true)
148         c.Check(project.CanManage, check.Equals, true)
149
150         subproject, err := s.localdb.GroupCreate(ctxUser1, arvados.CreateOptions{
151                 Attrs: map[string]interface{}{
152                         "owner_uuid":  project.UUID,
153                         "group_class": "project",
154                 },
155         })
156         c.Assert(err, check.IsNil)
157         c.Check(subproject.CanWrite, check.Equals, true)
158         c.Check(subproject.CanManage, check.Equals, true)
159
160         // Give 2nd user permission to read
161         permlink, err := s.localdb.LinkCreate(ctxAdmin, arvados.CreateOptions{
162                 Attrs: map[string]interface{}{
163                         "link_class": "permission",
164                         "name":       "can_read",
165                         "tail_uuid":  arvadostest.SpectatorUserUUID,
166                         "head_uuid":  project.UUID,
167                 },
168         })
169         c.Assert(err, check.IsNil)
170
171         // As 2nd user: can read, cannot manage, cannot write
172         project2, err := s.localdb.GroupGet(ctxUser2, arvados.GetOptions{UUID: project.UUID})
173         c.Assert(err, check.IsNil)
174         c.Check(project2.CanWrite, check.Equals, false)
175         c.Check(project2.CanManage, check.Equals, false)
176
177         _, err = s.localdb.LinkUpdate(ctxAdmin, arvados.UpdateOptions{
178                 UUID: permlink.UUID,
179                 Attrs: map[string]interface{}{
180                         "name": "can_write",
181                 },
182         })
183         c.Assert(err, check.IsNil)
184
185         // As 2nd user: cannot manage, can write
186         project2, err = s.localdb.GroupGet(ctxUser2, arvados.GetOptions{UUID: project.UUID})
187         c.Assert(err, check.IsNil)
188         c.Check(project2.CanWrite, check.Equals, true)
189         c.Check(project2.CanManage, check.Equals, false)
190
191         // As owner: after freezing, can manage (owner), cannot write (frozen)
192         project, err = s.localdb.GroupUpdate(ctxUser1, arvados.UpdateOptions{
193                 UUID: project.UUID,
194                 Attrs: map[string]interface{}{
195                         "frozen_by_uuid": arvadostest.ActiveUserUUID,
196                 }})
197         c.Assert(err, check.IsNil)
198         c.Check(project.CanWrite, check.Equals, false)
199         c.Check(project.CanManage, check.Equals, true)
200
201         // As admin: can manage (admin), cannot write (frozen)
202         project, err = s.localdb.GroupGet(ctxAdmin, arvados.GetOptions{UUID: project.UUID})
203         c.Assert(err, check.IsNil)
204         c.Check(project.CanWrite, check.Equals, false)
205         c.Check(project.CanManage, check.Equals, true)
206
207         // As 2nd user: cannot manage (perm), cannot write (frozen)
208         project2, err = s.localdb.GroupGet(ctxUser2, arvados.GetOptions{UUID: project.UUID})
209         c.Assert(err, check.IsNil)
210         c.Check(project2.CanWrite, check.Equals, false)
211         c.Check(project2.CanManage, check.Equals, false)
212
213         // After upgrading perm to "manage", as 2nd user: can manage (perm), cannot write (frozen)
214         _, err = s.localdb.LinkUpdate(ctxAdmin, arvados.UpdateOptions{
215                 UUID: permlink.UUID,
216                 Attrs: map[string]interface{}{
217                         "name": "can_manage",
218                 },
219         })
220         c.Assert(err, check.IsNil)
221         project2, err = s.localdb.GroupGet(ctxUser2, arvados.GetOptions{UUID: project.UUID})
222         c.Assert(err, check.IsNil)
223         c.Check(project2.CanWrite, check.Equals, false)
224         c.Check(project2.CanManage, check.Equals, true)
225
226         // 2nd user can also manage (but not write) the subject inside the frozen project
227         subproject2, err := s.localdb.GroupGet(ctxUser2, arvados.GetOptions{UUID: subproject.UUID})
228         c.Assert(err, check.IsNil)
229         c.Check(subproject2.CanWrite, check.Equals, false)
230         c.Check(subproject2.CanManage, check.Equals, true)
231 }