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  }