github.com/mutagen-io/mutagen@v0.18.0-rc1/pkg/filesystem/permissions.go (about)

     1  package filesystem
     2  
     3  import (
     4  	"strings"
     5  )
     6  
     7  // OwnershipIdentifierKind specifies the type of an identifier provided for
     8  // ownership specification.
     9  type OwnershipIdentifierKind uint8
    10  
    11  const (
    12  	// OwnershipIdentifierKindInvalid specifies an invalid identifier kind.
    13  	OwnershipIdentifierKindInvalid OwnershipIdentifierKind = iota
    14  	// OwnershipIdentifierKindPOSIXID specifies a POSIX user or group ID.
    15  	OwnershipIdentifierKindPOSIXID
    16  	// OwnershipIdentifierKindWindowsSID specifies a Windows SID.
    17  	OwnershipIdentifierKindWindowsSID
    18  	// OwnershipIdentifierKindName specifies a name-based identifier.
    19  	OwnershipIdentifierKindName
    20  )
    21  
    22  // isValidPOSIXID determines whether or not a string represents a valid POSIX
    23  // user or group ID.
    24  func isValidPOSIXID(value string) bool {
    25  	// Ensure that the value is non-empty.
    26  	if len(value) == 0 {
    27  		return false
    28  	}
    29  
    30  	// As a special case, allow 0 for root specification. We disallow numeric
    31  	// values that start with a 0 below, so we have to allow this specification
    32  	// explicitly.
    33  	if value == "0" {
    34  		return true
    35  	}
    36  
    37  	// Ensure that the string starts with a non-0 digit (just so we know that
    38  	// we're not going to parse in octal mode) and that all digits are between
    39  	// 0 and 9.
    40  	first := true
    41  	for _, r := range value {
    42  		if first {
    43  			if !('1' <= r && r <= '9') {
    44  				return false
    45  			}
    46  			first = false
    47  		} else {
    48  			if !('0' <= r && r <= '9') {
    49  				return false
    50  			}
    51  		}
    52  	}
    53  
    54  	// Success.
    55  	return true
    56  }
    57  
    58  // isValidWindowsSID determines whether or not a string represents a valid
    59  // Windows SID. It does not validate that the string resolves to a valid SID,
    60  // merely that the formatting is plausible.
    61  func isValidWindowsSID(value string) bool {
    62  	// Ensure that the value is non-empty.
    63  	if len(value) == 0 {
    64  		return false
    65  	}
    66  
    67  	// Unfortunately there's not much validation we can do beyond this because
    68  	// Windows supports string constants (e.g. "BA" resolves to built-in
    69  	// administrators) that we have no way of validating (and they seem to
    70  	// change by syste language). So we just assume that the identifier is valid
    71  	// for now. It will fail later on resolution if it's invalid.
    72  	return true
    73  }
    74  
    75  // ParseOwnershipIdentifier parses an identifier provided for ownership
    76  // specification.
    77  func ParseOwnershipIdentifier(specification string) (OwnershipIdentifierKind, string) {
    78  	// Ensure that the specification is non-empty.
    79  	if len(specification) == 0 {
    80  		return OwnershipIdentifierKindInvalid, ""
    81  	}
    82  
    83  	// Check if this is a POSIX ID.
    84  	if strings.HasPrefix(specification, "id:") {
    85  		if value := specification[3:]; !isValidPOSIXID(value) {
    86  			return OwnershipIdentifierKindInvalid, ""
    87  		} else {
    88  			return OwnershipIdentifierKindPOSIXID, value
    89  		}
    90  	}
    91  
    92  	// Check if this is a Windows SID.
    93  	if strings.HasPrefix(specification, "sid:") {
    94  		if value := specification[4:]; !isValidWindowsSID(value) {
    95  			return OwnershipIdentifierKindInvalid, ""
    96  		} else {
    97  			return OwnershipIdentifierKindWindowsSID, value
    98  		}
    99  	}
   100  
   101  	// Otherwise assume this is a name-based specification. If it's not valid,
   102  	// it will fail during lookup. Unfortunately there isn't a good set of
   103  	// cross-platform validations that we can perform to ensure that the name is
   104  	// valid. On POSIX it's governed by NAME_REGEX, which is system-dependent
   105  	// and not accessible except via cgo. On Windows, I think it's more of a
   106  	// trial-and-error check.
   107  	return OwnershipIdentifierKindName, specification
   108  }