github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/model/vfs/permissions_test.go (about) 1 package vfs_test 2 3 import ( 4 "testing" 5 6 "github.com/cozy/cozy-stack/model/permission" 7 "github.com/cozy/cozy-stack/model/vfs" 8 "github.com/cozy/cozy-stack/pkg/config/config" 9 "github.com/cozy/cozy-stack/pkg/consts" 10 "github.com/cozy/cozy-stack/pkg/couchdb" 11 "github.com/cozy/cozy-stack/tests/testutils" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 ) 15 16 func TestPermissions(t *testing.T) { 17 if testing.Short() { 18 t.Skip("an instance is required for this test: test skipped due to the use of --short flag") 19 } 20 21 config.UseTestFile(t) 22 testutils.NeedCouchdb(t) 23 24 aferoFS := makeAferoFS(t) 25 swiftFS := makeSwiftFS(t) 26 27 var tests = []struct { 28 name string 29 fs vfs.VFS 30 }{ 31 {"afero", aferoFS}, 32 {"swift", swiftFS}, 33 } 34 35 for _, tt := range tests { 36 fs := tt.fs 37 t.Run("Permissions", func(t *testing.T) { 38 origtree := H{ 39 "O/": H{ 40 "A/": H{ 41 "a1/": H{}, 42 "a2/": H{}, 43 }, 44 "B/": H{ 45 "b1.txt": nil, 46 "c1.txt": nil, 47 }, 48 "B2/": H{ 49 "b1.txt": nil, 50 "c1.txt": nil, 51 }, 52 "C/": H{}, 53 "d.txt": nil, 54 }, 55 } 56 O := createTree(t, fs, origtree, consts.RootDirID) 57 58 A, err := fs.DirByPath("/O/A") 59 require.NoError(t, err) 60 61 B, err := fs.DirByPath("/O/B") 62 require.NoError(t, err) 63 64 _, err = vfs.ModifyDirMetadata(fs, B, &vfs.DocPatch{ 65 Tags: &[]string{"testtagparent"}, 66 }) 67 assert.NoError(t, err) 68 69 B2, err := fs.DirByPath("/O/B2") 70 require.NoError(t, err) 71 72 f, err := fs.FileByPath("/O/B/b1.txt") 73 require.NoError(t, err) 74 75 _, err = vfs.ModifyFileMetadata(fs, f, &vfs.DocPatch{ 76 Tags: &[]string{"testtag"}, 77 }) 78 assert.NoError(t, err) 79 // reload 80 f, err = fs.FileByPath("/O/B/b1.txt") 81 require.NoError(t, err) 82 83 // hack to have a Class attribute 84 f.Class = "superfile" 85 86 psetWholeType := permission.Set{ 87 permission.Rule{ 88 Type: consts.Files, 89 Verbs: permission.ALL, 90 }, 91 } 92 assert.NoError(t, vfs.Allows(fs, psetWholeType, permission.GET, f)) 93 94 psetSelfID := permission.Set{ 95 permission.Rule{ 96 Type: consts.Files, 97 Verbs: permission.ALL, 98 Values: []string{f.ID()}, 99 }, 100 } 101 assert.NoError(t, vfs.Allows(fs, psetSelfID, permission.GET, f)) 102 103 psetSelfAttributes := permission.Set{ 104 permission.Rule{ 105 Type: consts.Files, 106 Verbs: permission.ALL, 107 Selector: "class", 108 Values: []string{"superfile"}, 109 }, 110 } 111 assert.NoError(t, vfs.Allows(fs, psetSelfAttributes, permission.GET, f)) 112 113 psetOnlyFiles := permission.Set{ 114 permission.Rule{ 115 Type: consts.Files, 116 Verbs: permission.ALL, 117 Selector: "type", 118 Values: []string{"file"}, 119 }, 120 } 121 assert.NoError(t, vfs.Allows(fs, psetOnlyFiles, permission.GET, f)) 122 123 psetOnlyDirs := permission.Set{ 124 permission.Rule{ 125 Type: consts.Files, 126 Verbs: permission.ALL, 127 Selector: "type", 128 Values: []string{"directory"}, 129 }, 130 } 131 assert.NoError(t, vfs.Allows(fs, psetOnlyDirs, permission.GET, B)) 132 133 psetMime := permission.Set{ 134 permission.Rule{ 135 Type: consts.Files, 136 Verbs: permission.ALL, 137 Selector: "mime", 138 Values: []string{"text/plain"}, 139 }, 140 } 141 f.Mime = "text/plain" 142 assert.NoError(t, vfs.Allows(fs, psetMime, permission.GET, f)) 143 144 psetReferences := permission.Set{ 145 permission.Rule{ 146 Type: consts.Files, 147 Verbs: permission.ALL, 148 Selector: "referenced_by", 149 Values: []string{"somealbumid"}, 150 }, 151 } 152 f.ReferencedBy = []couchdb.DocReference{{Type: "io.cozy.albums", ID: "somealbumid"}} 153 assert.NoError(t, vfs.Allows(fs, psetReferences, permission.GET, f)) 154 155 psetBadReferences := permission.Set{ 156 permission.Rule{ 157 Type: consts.Files, 158 Verbs: permission.ALL, 159 Selector: "referenced_by", 160 Values: []string{"anotheralbumid"}, 161 }, 162 } 163 assert.Error(t, vfs.Allows(fs, psetBadReferences, permission.GET, f)) 164 165 psetName := permission.Set{ 166 permission.Rule{ 167 Type: consts.Files, 168 Verbs: permission.ALL, 169 Selector: "name", 170 Values: []string{"b1.txt"}, 171 }, 172 } 173 assert.NoError(t, vfs.Allows(fs, psetName, permission.GET, f)) 174 175 psetSelfTag := permission.Set{ 176 permission.Rule{ 177 Type: consts.Files, 178 Verbs: permission.ALL, 179 Selector: "tags", 180 Values: []string{"testtag"}, 181 }, 182 } 183 assert.NoError(t, vfs.Allows(fs, psetSelfTag, permission.GET, f)) 184 185 psetParentID := permission.Set{ 186 permission.Rule{ 187 Type: consts.Files, 188 Verbs: permission.ALL, 189 Values: []string{O.ID()}, 190 }, 191 } 192 assert.NoError(t, vfs.Allows(fs, psetParentID, permission.GET, f)) 193 194 psetSelfParentTag := permission.Set{ 195 permission.Rule{ 196 Type: consts.Files, 197 Verbs: permission.ALL, 198 Selector: "tags", 199 Values: []string{"testtagparent"}, 200 }, 201 } 202 assert.NoError(t, vfs.Allows(fs, psetSelfParentTag, permission.GET, f)) 203 204 psetWrongType := permission.Set{ 205 permission.Rule{ 206 Type: "io.cozy.not.files", 207 Verbs: permission.ALL, 208 Values: []string{A.ID()}, 209 }, 210 } 211 assert.Error(t, vfs.Allows(fs, psetWrongType, permission.GET, f)) 212 213 psetWrongVerb := permission.Set{ 214 permission.Rule{ 215 Type: consts.Files, 216 Verbs: permission.Verbs(permission.POST), 217 Values: []string{A.ID()}, 218 }, 219 } 220 assert.Error(t, vfs.Allows(fs, psetWrongVerb, permission.GET, f)) 221 222 psetUncleID := permission.Set{ 223 permission.Rule{ 224 Type: consts.Files, 225 Verbs: permission.ALL, 226 Values: []string{B.ID()}, 227 }, 228 } 229 assert.Error(t, vfs.Allows(fs, psetUncleID, permission.GET, B2)) 230 231 psetUnclePrefixID := permission.Set{ 232 permission.Rule{ 233 Type: consts.Files, 234 Verbs: permission.ALL, 235 Values: []string{A.ID()}, 236 }, 237 } 238 assert.Error(t, vfs.Allows(fs, psetUnclePrefixID, permission.GET, f)) 239 }) 240 } 241 }