github.com/cs3org/reva/v2@v2.27.7/pkg/storage/utils/acl/acl.go (about) 1 // Copyright 2018-2021 CERN 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package acl 20 21 import ( 22 "errors" 23 "fmt" 24 "strings" 25 ) 26 27 // The ACLs represent a delimiter separated list of ACL entries. 28 type ACLs struct { 29 Entries []*Entry 30 delimiter string 31 } 32 33 var ( 34 errInvalidACL = errors.New("invalid acl") 35 ) 36 37 const ( 38 // LongTextForm contains one ACL entry per line. 39 LongTextForm = "\n" 40 // ShortTextForm is a sequence of ACL entries separated by commas, and is used for input. 41 ShortTextForm = "," 42 43 // TypeUser indicates the qualifier identifies a user 44 TypeUser = "u" 45 // TypeLightweight indicates the qualifier identifies a lightweight user 46 TypeLightweight = "lw" 47 // TypeGroup indicates the qualifier identifies a group 48 TypeGroup = "egroup" 49 ) 50 51 // Parse parses an acl string with the given delimiter (LongTextForm or ShortTextForm) 52 func Parse(acls string, delimiter string) (*ACLs, error) { 53 tokens := strings.Split(acls, delimiter) 54 entries := []*Entry{} 55 for _, t := range tokens { 56 // ignore empty lines and comments 57 if t == "" || isComment(t) { 58 continue 59 } 60 var err error 61 var entry *Entry 62 if strings.HasPrefix(t, TypeLightweight) { 63 entry, err = ParseLWEntry(t) 64 } else { 65 entry, err = ParseEntry(t) 66 } 67 if err != nil { 68 return nil, err 69 } 70 entries = append(entries, entry) 71 } 72 73 return &ACLs{Entries: entries, delimiter: delimiter}, nil 74 } 75 76 func isComment(line string) bool { 77 return strings.HasPrefix(line, "#") 78 } 79 80 // Serialize always serializes to short text form 81 func (m *ACLs) Serialize() string { 82 sysACL := []string{} 83 for _, e := range m.Entries { 84 sysACL = append(sysACL, e.CitrineSerialize()) 85 } 86 return strings.Join(sysACL, ShortTextForm) 87 } 88 89 // DeleteEntry removes an entry uniquely identified by acl type and qualifier 90 func (m *ACLs) DeleteEntry(aclType string, qualifier string) { 91 for i, e := range m.Entries { 92 if e.Qualifier == qualifier && e.Type == aclType { 93 m.Entries = append(m.Entries[:i], m.Entries[i+1:]...) 94 return 95 } 96 } 97 } 98 99 // SetEntry replaces the permissions of an entry with the given set 100 func (m *ACLs) SetEntry(aclType string, qualifier string, permissions string) error { 101 if aclType == "" || permissions == "" { 102 return errInvalidACL 103 } 104 m.DeleteEntry(aclType, qualifier) 105 entry := &Entry{ 106 Type: aclType, 107 Qualifier: qualifier, 108 Permissions: permissions, 109 } 110 m.Entries = append(m.Entries, entry) 111 return nil 112 } 113 114 // The Entry of an ACL is represented as three colon separated fields: 115 type Entry struct { 116 // an ACL entry tag type: user, group, mask or other. comments start with # 117 Type string 118 // an ACL entry qualifier 119 Qualifier string 120 // and the discretionary access permissions 121 Permissions string 122 } 123 124 // ParseEntry parses a single ACL 125 func ParseEntry(singleSysACL string) (*Entry, error) { 126 tokens := strings.Split(singleSysACL, ":") 127 switch len(tokens) { 128 case 2: 129 // The ACL entries might be stored as type:qualifier=permissions 130 // Handle that case separately 131 parts := strings.SplitN(tokens[1], "=", 2) 132 if len(parts) == 2 { 133 return &Entry{ 134 Type: tokens[0], 135 Qualifier: parts[0], 136 Permissions: parts[1], 137 }, nil 138 } 139 case 3: 140 return &Entry{ 141 Type: tokens[0], 142 Qualifier: tokens[1], 143 Permissions: tokens[2], 144 }, nil 145 } 146 return nil, errInvalidACL 147 } 148 149 // ParseLWEntry parses a single lightweight ACL 150 func ParseLWEntry(singleSysACL string) (*Entry, error) { 151 if !strings.HasPrefix(singleSysACL, TypeLightweight+":") { 152 return nil, errInvalidACL 153 } 154 singleSysACL = strings.TrimPrefix(singleSysACL, TypeLightweight+":") 155 156 tokens := strings.Split(singleSysACL, "=") 157 if len(tokens) != 2 { 158 return nil, errInvalidACL 159 } 160 return &Entry{ 161 Type: TypeLightweight, 162 Qualifier: tokens[0], 163 Permissions: tokens[1], 164 }, nil 165 } 166 167 // CitrineSerialize serializes an ACL entry for citrine EOS ACLs 168 func (a *Entry) CitrineSerialize() string { 169 return fmt.Sprintf("%s:%s=%s", a.Type, a.Qualifier, a.Permissions) 170 }