github.com/go-kivik/kivik/v4@v4.3.2/x/fsdb/cdb/security.go (about) 1 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 2 // use this file except in compliance with the License. You may obtain a copy of 3 // the License at 4 // 5 // http://www.apache.org/licenses/LICENSE-2.0 6 // 7 // Unless required by applicable law or agreed to in writing, software 8 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 // License for the specific language governing permissions and limitations under 11 // the License. 12 13 package cdb 14 15 import ( 16 "context" 17 "encoding/json" 18 "os" 19 "path/filepath" 20 21 "gopkg.in/yaml.v3" 22 23 "github.com/go-kivik/kivik/v4/driver" 24 "github.com/go-kivik/kivik/v4/x/fsdb/cdb/decode" 25 ) 26 27 // ReadSecurity reads the _security.{ext} document from path. If not found, 28 // an empty security document is returned. 29 func (fs *FS) ReadSecurity(ctx context.Context, path string) (*driver.Security, error) { 30 sec := new(driver.Security) 31 ext, err := fs.findSecurityExt(ctx, path) 32 if err != nil { 33 return nil, err 34 } 35 if ext == "" { 36 return sec, nil 37 } 38 f, err := fs.fs.Open(filepath.Join(path, "_security."+ext)) 39 if err == nil { 40 defer f.Close() 41 err := decode.Decode(f, ext, sec) 42 return sec, err 43 } 44 if !os.IsNotExist(err) { 45 return nil, err 46 } 47 48 return sec, nil 49 } 50 51 // findSecurityExt looks in path for the `_security` document by extension, and 52 // returns the first matching extension it finds, or an empty string if the 53 // security document is not found. 54 func (fs *FS) findSecurityExt(ctx context.Context, path string) (string, error) { 55 for _, ext := range decode.Extensions() { 56 if err := ctx.Err(); err != nil { 57 return "", err 58 } 59 stat, err := fs.fs.Stat(filepath.Join(path, "_security."+ext)) 60 if os.IsNotExist(err) { 61 continue 62 } 63 if err != nil { 64 return "", err 65 } 66 if stat.Mode().IsRegular() { 67 return ext, nil 68 } 69 } 70 return "", nil 71 } 72 73 // WriteSecurity overwrites the existing _security document, if it exists. If it 74 // does not exist, it is created with the .json extension. 75 func (fs *FS) WriteSecurity(ctx context.Context, path string, sec *driver.Security) error { 76 ext, err := fs.findSecurityExt(ctx, path) 77 if err != nil { 78 return err 79 } 80 if ext == "" { 81 ext = "json" 82 } 83 filename := filepath.Join(path, "_security."+ext) 84 tmp, err := fs.fs.TempFile(path, "_security."+ext) 85 if err != nil { 86 return err 87 } 88 var enc interface { 89 Encode(interface{}) error 90 } 91 switch ext { 92 case "json": 93 enc = json.NewEncoder(tmp) 94 case "yaml", "yml": 95 enc = yaml.NewEncoder(tmp) 96 } 97 if err := enc.Encode(sec); err != nil { 98 return err 99 } 100 if err := tmp.Close(); err != nil { 101 return err 102 } 103 return fs.fs.Rename(tmp.Name(), filename) 104 }