git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/container/acl/acl_basic.go (about)

     1  package acl
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"strings"
     7  )
     8  
     9  // Basic represents basic part of the FrostFS container's ACL. It includes
    10  // common (pretty simple) access rules for operations inside the container.
    11  // See FrostFS Specification for details.
    12  //
    13  // One can find some similarities with the traditional Unix permission, such as
    14  //
    15  //	division into scopes: user, group, others
    16  //	op-permissions: read, write, etc.
    17  //	sticky bit
    18  //
    19  // However, these similarities should only be used for better understanding,
    20  // in general these mechanisms are different.
    21  //
    22  // Instances can be created using built-in var declaration, but look carefully
    23  // at the default values, and how individual permissions are regulated.
    24  // Some frequently used values are presented in exported variables.
    25  //
    26  // Basic instances are comparable: values can be compared directly using
    27  // == operator.
    28  //
    29  // Note that type conversion from- and to numerical types is not recommended,
    30  // use corresponding constants and/or methods instead.
    31  type Basic uint32
    32  
    33  // FromBits decodes Basic from the numerical representation.
    34  //
    35  // See also Bits.
    36  func (x *Basic) FromBits(bits uint32) {
    37  	*x = Basic(bits)
    38  }
    39  
    40  // Bits returns numerical encoding of Basic.
    41  //
    42  // See also FromBits.
    43  func (x Basic) Bits() uint32 {
    44  	return uint32(x)
    45  }
    46  
    47  // common bit sections.
    48  const (
    49  	opAmount  = 7
    50  	bitsPerOp = 4
    51  
    52  	bitPosFinal  = opAmount * bitsPerOp
    53  	bitPosSticky = bitPosFinal + 1
    54  )
    55  
    56  // per-op bit order.
    57  const (
    58  	opBitPosBearer uint8 = iota
    59  	opBitPosOthers
    60  	opBitPosContainer
    61  	opBitPosOwner
    62  )
    63  
    64  // DisableExtension makes Basic FINAL. FINAL indicates the ACL non-extendability
    65  // in the related container.
    66  //
    67  // See also Extendable.
    68  func (x *Basic) DisableExtension() {
    69  	setBit((*uint32)(x), bitPosFinal)
    70  }
    71  
    72  // Extendable checks if Basic is NOT made FINAL using DisableExtension.
    73  //
    74  // Zero Basic is extendable.
    75  func (x Basic) Extendable() bool {
    76  	return !isBitSet(uint32(x), bitPosFinal)
    77  }
    78  
    79  // MakeSticky makes Basic STICKY. STICKY indicates that only the owner of any
    80  // particular object is allowed to operate on it.
    81  //
    82  // See also Sticky.
    83  func (x *Basic) MakeSticky() {
    84  	setBit((*uint32)(x), bitPosSticky)
    85  }
    86  
    87  // Sticky checks if Basic is made STICKY using MakeSticky.
    88  //
    89  // Zero Basic is NOT STICKY.
    90  func (x Basic) Sticky() bool {
    91  	return isBitSet(uint32(x), bitPosSticky)
    92  }
    93  
    94  // checks if op is used by the storage nodes within replication mechanism.
    95  func isReplicationOp(op Op) bool {
    96  	switch op {
    97  	default:
    98  		return false
    99  	case
   100  		OpObjectGet,
   101  		OpObjectHead,
   102  		OpObjectPut,
   103  		OpObjectSearch,
   104  		OpObjectHash:
   105  		return true
   106  	}
   107  }
   108  
   109  // AllowOp allows the parties with the given role to the given operation.
   110  // Op MUST be one of the Op enumeration. Role MUST be one of:
   111  //
   112  //	RoleOwner
   113  //	RoleContainer
   114  //	RoleOthers
   115  //
   116  // and if role is RoleContainer, op MUST NOT be:
   117  //
   118  //	OpObjectGet
   119  //	OpObjectHead
   120  //	OpObjectPut
   121  //	OpObjectSearch
   122  //	OpObjectHash
   123  //
   124  // See also IsOpAllowed.
   125  func (x *Basic) AllowOp(op Op, role Role) {
   126  	var bitPos uint8
   127  
   128  	switch role {
   129  	default:
   130  		panic(fmt.Sprintf("unable to set rules for unsupported role %v", role))
   131  	case RoleInnerRing:
   132  		panic("basic ACL MUST NOT be modified for Inner Ring")
   133  	case RoleOwner:
   134  		bitPos = opBitPosOwner
   135  	case RoleContainer:
   136  		if isReplicationOp(op) {
   137  			panic("basic ACL for container replication ops MUST NOT be modified")
   138  		}
   139  
   140  		bitPos = opBitPosContainer
   141  	case RoleOthers:
   142  		bitPos = opBitPosOthers
   143  	}
   144  
   145  	setOpBit((*uint32)(x), op, bitPos)
   146  }
   147  
   148  // IsOpAllowed checks if parties with the given role are allowed to the given op
   149  // according to the Basic rules. Op MUST be one of the Op enumeration.
   150  // Role MUST be one of the Role enumeration.
   151  //
   152  // Members with RoleContainer role have exclusive default access to the
   153  // operations of the data replication mechanism:
   154  //
   155  //	OpObjectGet
   156  //	OpObjectHead
   157  //	OpObjectPut
   158  //	OpObjectSearch
   159  //	OpObjectHash
   160  //
   161  // RoleInnerRing members are allowed to data audit ops only:
   162  //
   163  //	OpObjectGet
   164  //	OpObjectHead
   165  //	OpObjectHash
   166  //	OpObjectSearch
   167  //
   168  // Zero Basic prevents any role from accessing any operation in the absence
   169  // of default rights.
   170  //
   171  // See also AllowOp.
   172  func (x Basic) IsOpAllowed(op Op, role Role) bool {
   173  	var bitPos uint8
   174  
   175  	switch role {
   176  	default:
   177  		panic(fmt.Sprintf("role is unsupported %v", role))
   178  	case RoleInnerRing:
   179  		switch op {
   180  		case
   181  			OpObjectGet,
   182  			OpObjectHead,
   183  			OpObjectHash,
   184  			OpObjectSearch:
   185  			return true
   186  		default:
   187  			return false
   188  		}
   189  	case RoleOwner:
   190  		bitPos = opBitPosOwner
   191  	case RoleContainer:
   192  		if isReplicationOp(op) {
   193  			return true
   194  		}
   195  
   196  		bitPos = opBitPosContainer
   197  	case RoleOthers:
   198  		bitPos = opBitPosOthers
   199  	}
   200  
   201  	return isOpBitSet(uint32(x), op, bitPos)
   202  }
   203  
   204  // AllowBearerRules allows bearer to provide extended ACL rules for the given
   205  // operation. Bearer rules doesn't depend on container ACL extensibility.
   206  //
   207  // See also AllowedBearerRules.
   208  func (x *Basic) AllowBearerRules(op Op) {
   209  	setOpBit((*uint32)(x), op, opBitPosBearer)
   210  }
   211  
   212  // AllowedBearerRules checks if bearer rules are allowed using AllowBearerRules.
   213  // Op MUST be one of the Op enumeration.
   214  //
   215  // Zero Basic disallows bearer rules for any op.
   216  func (x Basic) AllowedBearerRules(op Op) bool {
   217  	return isOpBitSet(uint32(x), op, opBitPosBearer)
   218  }
   219  
   220  // EncodeToString encodes Basic into hexadecimal string.
   221  //
   222  // See also DecodeString.
   223  func (x Basic) EncodeToString() string {
   224  	return strconv.FormatUint(uint64(x), 16)
   225  }
   226  
   227  // Names of the frequently used Basic values.
   228  const (
   229  	NamePrivate              = "private"
   230  	NamePrivateExtended      = "eacl-private"
   231  	NamePublicRO             = "public-read"
   232  	NamePublicROExtended     = "eacl-public-read"
   233  	NamePublicRW             = "public-read-write"
   234  	NamePublicRWExtended     = "eacl-public-read-write"
   235  	NamePublicAppend         = "public-append"
   236  	NamePublicAppendExtended = "eacl-public-append"
   237  )
   238  
   239  // Frequently used Basic values. Bitmasks are taken from the FrostFS Specification.
   240  const (
   241  	Private              = Basic(0x1C8C8CCC) // private
   242  	PrivateExtended      = Basic(0x0C8C8CCC) // eacl-private
   243  	PublicRO             = Basic(0x1FBF8CFF) // public-read
   244  	PublicROExtended     = Basic(0x0FBF8CFF) // eacl-public-read
   245  	PublicRW             = Basic(0x1FBFBFFF) // public-read-write
   246  	PublicRWExtended     = Basic(0x0FBFBFFF) // eacl-public-read-write
   247  	PublicAppend         = Basic(0x1FBF9FFF) // public-append
   248  	PublicAppendExtended = Basic(0x0FBF9FFF) // eacl-public-append
   249  )
   250  
   251  // DecodeString decodes string calculated using EncodeToString. Also supports
   252  // human-readable names (Name* constants).
   253  func (x *Basic) DecodeString(s string) (e error) {
   254  	switch s {
   255  	case NamePrivate:
   256  		*x = Private
   257  	case NamePrivateExtended:
   258  		*x = PrivateExtended
   259  	case NamePublicRO:
   260  		*x = PublicRO
   261  	case NamePublicROExtended:
   262  		*x = PublicROExtended
   263  	case NamePublicRW:
   264  		*x = PublicRW
   265  	case NamePublicRWExtended:
   266  		*x = PublicRWExtended
   267  	case NamePublicAppend:
   268  		*x = PublicAppend
   269  	case NamePublicAppendExtended:
   270  		*x = PublicAppendExtended
   271  	default:
   272  		s = strings.TrimPrefix(strings.ToLower(s), "0x")
   273  
   274  		v, err := strconv.ParseUint(s, 16, 32)
   275  		if err != nil {
   276  			return fmt.Errorf("parse hex: %w", err)
   277  		}
   278  
   279  		*x = Basic(v)
   280  	}
   281  
   282  	return nil
   283  }