package acl import ( "github.com/nspcc-dev/neofs-api-go/acl" "github.com/nspcc-dev/neofs-api-go/object" "github.com/nspcc-dev/neofs-node/internal" ) type ( // BasicChecker is an interface of the basic ACL control tool. BasicChecker interface { // Action returns true if request is allowed for this target. Action(uint32, object.RequestType, acl.Target) (bool, error) // Bearer returns true if bearer token is allowed for this request. Bearer(uint32, object.RequestType) (bool, error) // Extended returns true if extended ACL is allowed for this. Extended(uint32) bool // Sticky returns true if sticky bit is set. Sticky(uint32) bool } // BasicACLChecker performs basic ACL check. BasicACLChecker struct{} // MaskedBasicACLChecker performs all basic ACL checks, but applying // mask on ACL first. It is useful, when some bits must be always // set or unset. MaskedBasicACLChecker struct { BasicACLChecker andMask uint32 orMask uint32 } nibble struct { value uint32 } ) const ( errUnknownRequest = internal.Error("unknown request type") errUnknownTarget = internal.Error("unknown target type") ) const ( aclFinalBit = 0x10000000 // 29th bit aclStickyBit = 0x20000000 // 30th bit nibbleBBit = 0x1 nibbleOBit = 0x2 nibbleSBit = 0x4 nibbleUBit = 0x8 // DefaultAndFilter is a default AND mask of basic ACL value of container. DefaultAndFilter = 0xFFFFFFFF ) var ( nibbleOffset = map[object.RequestType]uint32{ object.RequestGet: 0, object.RequestHead: 1 * 4, object.RequestPut: 2 * 4, object.RequestDelete: 3 * 4, object.RequestSearch: 4 * 4, object.RequestRange: 5 * 4, object.RequestRangeHash: 6 * 4, } ) // Action returns true if request is allowed for target. func (c *BasicACLChecker) Action(rule uint32, req object.RequestType, t acl.Target) (bool, error) { n, err := fetchNibble(rule, req) if err != nil { return false, err } switch t { case acl.Target_User: return n.U(), nil case acl.Target_System: return n.S(), nil case acl.Target_Others: return n.O(), nil default: return false, errUnknownTarget } } // Bearer returns true if bearer token is allowed to use for this request // as source of extended ACL. func (c *BasicACLChecker) Bearer(rule uint32, req object.RequestType) (bool, error) { n, err := fetchNibble(rule, req) if err != nil { return false, err } return n.B(), nil } // Extended returns true if extended ACL stored in the container are allowed // to use. func (c *BasicACLChecker) Extended(rule uint32) bool { return rule&aclFinalBit != aclFinalBit } // Sticky returns true if container is not allowed to store objects with // owners different from request owner. func (c *BasicACLChecker) Sticky(rule uint32) bool { return rule&aclStickyBit == aclStickyBit } func fetchNibble(rule uint32, req object.RequestType) (*nibble, error) { offset, ok := nibbleOffset[req] if !ok { return nil, errUnknownRequest } return &nibble{value: (rule >> offset) & 0xf}, nil } // B returns true if `Bearer` bit set in the nibble. func (n *nibble) B() bool { return n.value&nibbleBBit == nibbleBBit } // O returns true if `Others` bit set in the nibble. func (n *nibble) O() bool { return n.value&nibbleOBit == nibbleOBit } // S returns true if `System` bit set in the nibble. func (n *nibble) S() bool { return n.value&nibbleSBit == nibbleSBit } // U returns true if `User` bit set in the nibble. func (n *nibble) U() bool { return n.value&nibbleUBit == nibbleUBit } // NewMaskedBasicACLChecker returns BasicChecker that applies predefined // bit mask on basic ACL value. func NewMaskedBasicACLChecker(or, and uint32) BasicChecker { return MaskedBasicACLChecker{ BasicACLChecker: BasicACLChecker{}, andMask: and, orMask: or, } } // Action returns true if request is allowed for target. func (c MaskedBasicACLChecker) Action(rule uint32, req object.RequestType, t acl.Target) (bool, error) { rule |= c.orMask rule &= c.andMask return c.BasicACLChecker.Action(rule, req, t) } // Bearer returns true if bearer token is allowed to use for this request // as source of extended ACL. func (c MaskedBasicACLChecker) Bearer(rule uint32, req object.RequestType) (bool, error) { rule |= c.orMask rule &= c.andMask return c.BasicACLChecker.Bearer(rule, req) } // Extended returns true if extended ACL stored in the container are allowed // to use. func (c MaskedBasicACLChecker) Extended(rule uint32) bool { rule |= c.orMask rule &= c.andMask return c.BasicACLChecker.Extended(rule) } // Sticky returns true if container is not allowed to store objects with // owners different from request owner. func (c MaskedBasicACLChecker) Sticky(rule uint32) bool { rule |= c.orMask rule &= c.andMask return c.BasicACLChecker.Sticky(rule) }