From 1fbd192bd74370cd3b9904407ea27d93b571b91e Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Fri, 16 Jul 2021 15:35:07 +0300 Subject: [PATCH] [#89] Add placement policy Signed-off-by: Denis Kirillov --- api/auth/center.go | 10 +- api/handler/put.go | 93 +++++++++----- api/layer/container.go | 13 +- api/layer/layer.go | 18 +-- api/user-auth.go | 8 +- authmate/authmate.go | 56 ++++++++- cmd/authmate/main.go | 31 +++++ creds/accessbox/accessbox.go | 49 ++++++++ creds/accessbox/accessbox.pb.go | 214 ++++++++++++++++++++++---------- creds/accessbox/accessbox.proto | 6 + creds/tokens/credentials.go | 11 +- 11 files changed, 386 insertions(+), 123 deletions(-) diff --git a/api/auth/center.go b/api/auth/center.go index 3dccb2959..6ff416c85 100644 --- a/api/auth/center.go +++ b/api/auth/center.go @@ -26,7 +26,7 @@ var authorizationFieldRegexp = regexp.MustCompile(`AWS4-HMAC-SHA256 Credential=( type ( // Center is a user authentication interface. Center interface { - Authenticate(request *http.Request) (*accessbox.GateData, error) + Authenticate(request *http.Request) (*accessbox.Box, error) } center struct { @@ -64,7 +64,7 @@ func New(conns pool.Pool, key *keys.PrivateKey) Center { } } -func (c *center) Authenticate(r *http.Request) (*accessbox.GateData, error) { +func (c *center) Authenticate(r *http.Request) (*accessbox.Box, error) { queryValues := r.URL.Query() if queryValues.Get("X-Amz-Algorithm") == "AWS4-HMAC-SHA256" { return nil, errors.New("pre-signed form of request is not supported") @@ -98,7 +98,7 @@ func (c *center) Authenticate(r *http.Request) (*accessbox.GateData, error) { return nil, fmt.Errorf("could not parse AccessBox address: %s : %w", accessKeyID, err) } - tkns, err := c.cli.GetTokens(r.Context(), address) + box, err := c.cli.GetBox(r.Context(), address) if err != nil { return nil, err } @@ -114,7 +114,7 @@ func (c *center) Authenticate(r *http.Request) (*accessbox.GateData, error) { } } - awsCreds := credentials.NewStaticCredentials(accessKeyID, tkns.AccessKey, "") + awsCreds := credentials.NewStaticCredentials(accessKeyID, box.Gate.AccessKey, "") signer := v4.NewSigner(awsCreds) signer.DisableURIPathEscaping = true @@ -128,5 +128,5 @@ func (c *center) Authenticate(r *http.Request) (*accessbox.GateData, error) { return nil, errors.New("failed to pass authentication procedure") } - return tkns, nil + return box, nil } diff --git a/api/handler/put.go b/api/handler/put.go index ea9f8462b..834fb3a0d 100644 --- a/api/handler/put.go +++ b/api/handler/put.go @@ -1,6 +1,8 @@ package handler import ( + "context" + "encoding/xml" "fmt" "net/http" "strconv" @@ -11,6 +13,7 @@ import ( "github.com/nspcc-dev/neofs-node/pkg/policy" "github.com/nspcc-dev/neofs-s3-gw/api" "github.com/nspcc-dev/neofs-s3-gw/api/layer" + "github.com/nspcc-dev/neofs-s3-gw/creds/accessbox" "go.uber.org/zap" ) @@ -24,6 +27,11 @@ const ( publicBasicRule = 0x0FFFFFFF ) +type createBucketParams struct { + XMLName xml.Name `xml:"http://s3.amazonaws.com/doc/2006-03-01/ CreateBucketConfiguration" json:"-"` + LocationConstraint string +} + func (h *handler) PutObjectHandler(w http.ResponseWriter, r *http.Request) { var ( err error @@ -90,43 +98,41 @@ func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) { } if err != nil { - h.log.Error("could not parse basic ACL", - zap.String("request_id", rid), - zap.Error(err)) - - api.WriteErrorResponse(r.Context(), w, api.Error{ - Code: api.GetAPIError(api.ErrBadRequest).Code, - Description: err.Error(), - HTTPStatusCode: http.StatusBadRequest, - }, r.URL) + h.writeError(w, r, "could not parse basic ACL", rid, err) return } - p.Policy, err = policy.Parse(defaultPolicy) + createParams, err := parseLocationConstraint(r) if err != nil { - h.log.Error("could not parse policy", - zap.String("request_id", rid), - zap.Error(err)) - - api.WriteErrorResponse(r.Context(), w, api.Error{ - Code: api.GetAPIError(api.ErrBadRequest).Code, - Description: err.Error(), - HTTPStatusCode: http.StatusBadRequest, - }, r.URL) + h.writeError(w, r, "could not parse body", rid, err) return } - cid, err := h.obj.CreateBucket(r.Context(), &p) + boxData, err := getBoxData(r.Context()) if err != nil { - h.log.Error("could not create bucket", - zap.String("request_id", rid), - zap.Error(err)) + h.writeError(w, r, "could get boxData", rid, err) + return + } - api.WriteErrorResponse(r.Context(), w, api.Error{ - Code: api.GetAPIError(api.ErrInternalError).Code, - Description: err.Error(), - HTTPStatusCode: http.StatusInternalServerError, - }, r.URL) + if createParams.LocationConstraint != "" { + for _, placementPolicy := range boxData.Policies { + if placementPolicy.LocationConstraint == createParams.LocationConstraint { + p.Policy = placementPolicy.Policy + break + } + } + } + if p.Policy == nil { + p.Policy, err = policy.Parse(defaultPolicy) + if err != nil { + h.writeError(w, r, "could not parse policy", rid, err) + return + } + } + + cid, err := h.obj.CreateBucket(r.Context(), &p, boxData) + if err != nil { + h.writeError(w, r, "could not create bucket", rid, err) return } @@ -136,6 +142,18 @@ func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) { api.WriteSuccessResponseHeadersOnly(w) } +func parseLocationConstraint(r *http.Request) (*createBucketParams, error) { + if r.ContentLength == 0 { + return new(createBucketParams), nil + } + + params := new(createBucketParams) + if err := xml.NewDecoder(r.Body).Decode(params); err != nil { + return nil, err + } + return params, nil +} + func parseBasicACL(basicACL string) (uint32, error) { switch basicACL { case basicACLPublic: @@ -155,3 +173,22 @@ func parseBasicACL(basicACL string) (uint32, error) { return uint32(value), nil } } + +func (h *handler) writeError(w http.ResponseWriter, r *http.Request, msg, rid string, err error) { + h.log.Error(msg, zap.String("request_id", rid), zap.Error(err)) + api.WriteErrorResponse(r.Context(), w, err, r.URL) +} + +func getBoxData(ctx context.Context) (*accessbox.Box, error) { + var boxData *accessbox.Box + data, ok := ctx.Value(api.BoxData).(*accessbox.Box) + if !ok || data == nil { + return nil, fmt.Errorf("couldn't get box data from context") + } + + boxData = data + if boxData.Gate == nil { + boxData.Gate = &accessbox.GateData{} + } + return boxData, nil +} diff --git a/api/layer/container.go b/api/layer/container.go index ce3023271..4a25c1e9d 100644 --- a/api/layer/container.go +++ b/api/layer/container.go @@ -123,21 +123,14 @@ func (n *layer) containerList(ctx context.Context) ([]*BucketInfo, error) { return list, nil } -func (n *layer) createContainer(ctx context.Context, p *CreateBucketParams) (*cid.ID, error) { +func (n *layer) createContainer(ctx context.Context, p *CreateBucketParams, boxData *accessbox.Box) (*cid.ID, error) { cnr := container.New( container.WithPolicy(p.Policy), container.WithCustomBasicACL(p.ACL), container.WithAttribute(container.AttributeName, p.Name), container.WithAttribute(container.AttributeTimestamp, strconv.FormatInt(time.Now().Unix(), 10))) - var gateData *accessbox.GateData - if data, ok := ctx.Value(api.GateData).(*accessbox.GateData); ok && data != nil { - gateData = data - } else { - return nil, fmt.Errorf("couldn't get gate data from context") - } - - cnr.SetSessionToken(gateData.SessionToken) + cnr.SetSessionToken(boxData.Gate.SessionToken) cnr.SetOwnerID(n.Owner(ctx)) cid, err := n.pool.PutContainer(ctx, cnr) @@ -149,7 +142,7 @@ func (n *layer) createContainer(ctx context.Context, p *CreateBucketParams) (*ci return nil, err } - if err := n.setContainerEACL(ctx, cid, gateData.GateKey); err != nil { + if err := n.setContainerEACL(ctx, cid, boxData.Gate.GateKey); err != nil { return nil, err } diff --git a/api/layer/layer.go b/api/layer/layer.go index 45f047e97..0be06785c 100644 --- a/api/layer/layer.go +++ b/api/layer/layer.go @@ -104,7 +104,7 @@ type ( ListBuckets(ctx context.Context) ([]*BucketInfo, error) GetBucketInfo(ctx context.Context, name string) (*BucketInfo, error) - CreateBucket(ctx context.Context, p *CreateBucketParams) (*cid.ID, error) + CreateBucket(ctx context.Context, p *CreateBucketParams, boxData *accessbox.Box) (*cid.ID, error) DeleteBucket(ctx context.Context, p *DeleteBucketParams) error GetObject(ctx context.Context, p *GetObjectParams) error @@ -150,8 +150,8 @@ func NewLayer(log *zap.Logger, conns pool.Pool) Client { // Owner returns owner id from BearerToken (context) or from client owner. func (n *layer) Owner(ctx context.Context) *owner.ID { - if data, ok := ctx.Value(api.GateData).(*accessbox.GateData); ok && data != nil { - return data.BearerToken.Issuer() + if data, ok := ctx.Value(api.BoxData).(*accessbox.Box); ok && data != nil && data.Gate != nil { + return data.Gate.BearerToken.Issuer() } return n.pool.OwnerID() @@ -159,8 +159,8 @@ func (n *layer) Owner(ctx context.Context) *owner.ID { // BearerOpt returns client.WithBearer call option with token from context or with nil token. func (n *layer) BearerOpt(ctx context.Context) client.CallOption { - if data, ok := ctx.Value(api.GateData).(*accessbox.GateData); ok && data != nil { - return client.WithBearer(data.BearerToken) + if data, ok := ctx.Value(api.BoxData).(*accessbox.Box); ok && data != nil && data.Gate != nil { + return client.WithBearer(data.Gate.BearerToken) } return client.WithBearer(nil) @@ -168,8 +168,8 @@ func (n *layer) BearerOpt(ctx context.Context) client.CallOption { // SessionOpt returns client.WithSession call option with token from context or with nil token. func (n *layer) SessionOpt(ctx context.Context) client.CallOption { - if data, ok := ctx.Value(api.GateData).(*accessbox.GateData); ok && data != nil { - return client.WithSession(data.SessionToken) + if data, ok := ctx.Value(api.BoxData).(*accessbox.Box); ok && data != nil && data.Gate != nil { + return client.WithSession(data.Gate.SessionToken) } return client.WithSession(nil) @@ -498,11 +498,11 @@ func (n *layer) DeleteObjects(ctx context.Context, bucket string, objects []stri return errs } -func (n *layer) CreateBucket(ctx context.Context, p *CreateBucketParams) (*cid.ID, error) { +func (n *layer) CreateBucket(ctx context.Context, p *CreateBucketParams, boxData *accessbox.Box) (*cid.ID, error) { _, err := n.GetBucketInfo(ctx, p.Name) if err != nil { if errors.Is(err, ErrBucketNotFound) { - return n.createContainer(ctx, p) + return n.createContainer(ctx, p, boxData) } return nil, err } diff --git a/api/user-auth.go b/api/user-auth.go index 6b4d5437e..d41d272f8 100644 --- a/api/user-auth.go +++ b/api/user-auth.go @@ -12,15 +12,15 @@ import ( // KeyWrapper is wrapper for context keys. type KeyWrapper string -// GateData is an ID used to store GateData in a context. -var GateData = KeyWrapper("__context_gate_data_key") +// BoxData is an ID used to store accessbox.Box in a context. +var BoxData = KeyWrapper("__context_box_key") // AttachUserAuth adds user authentication via center to router using log for logging. func AttachUserAuth(router *mux.Router, center auth.Center, log *zap.Logger) { router.Use(func(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var ctx context.Context - tokens, err := center.Authenticate(r) + box, err := center.Authenticate(r) if err != nil { if err == auth.ErrNoAuthorizationHeader { log.Debug("couldn't receive bearer token, using neofs-key") @@ -31,7 +31,7 @@ func AttachUserAuth(router *mux.Router, center auth.Center, log *zap.Logger) { return } } else { - ctx = context.WithValue(r.Context(), GateData, tokens) + ctx = context.WithValue(r.Context(), BoxData, box) } h.ServeHTTP(w, r.WithContext(ctx)) diff --git a/authmate/authmate.go b/authmate/authmate.go index 510caacdb..f6cdc65e5 100644 --- a/authmate/authmate.go +++ b/authmate/authmate.go @@ -44,6 +44,9 @@ func New(log *zap.Logger, conns pool.Pool) *Agent { } type ( + // ContainerPolicies contains mapping of aws LocationConstraint to neofs PlacementPolicy. + ContainerPolicies map[string]string + // IssueSecretOptions contains options for passing to Agent.IssueSecret method. IssueSecretOptions struct { ContainerID *cid.ID @@ -54,6 +57,7 @@ type ( ContextRules []byte SessionTkn bool Lifetime uint64 + ContainerPolicies ContainerPolicies } // ObtainSecretOptions contains options for passing to Agent.ObtainSecret method. @@ -121,6 +125,45 @@ func (a *Agent) getCurrentEpoch(ctx context.Context) (uint64, error) { } } +func checkPolicy(policyString string) (*netmap.PlacementPolicy, error) { + result, err := policy.Parse(policyString) + if err == nil { + return result, nil + } + + result = netmap.NewPlacementPolicy() + if err = result.UnmarshalJSON([]byte(policyString)); err == nil { + return result, nil + } + + return nil, fmt.Errorf("can't parse placement policy") +} + +func preparePolicy(policy ContainerPolicies) ([]*accessbox.AccessBox_ContainerPolicy, error) { + if policy == nil { + return nil, nil + } + + var result []*accessbox.AccessBox_ContainerPolicy + for locationConstraint, placementPolicy := range policy { + parsedPolicy, err := checkPolicy(placementPolicy) + if err != nil { + return nil, err + } + marshaled, err := parsedPolicy.Marshal() + if err != nil { + return nil, fmt.Errorf("can't marshal placement policy: %w", err) + } + + result = append(result, &accessbox.AccessBox_ContainerPolicy{ + LocationConstraint: locationConstraint, + Policy: marshaled, + }) + } + + return result, nil +} + // IssueSecret creates an auth token, puts it in the NeoFS network and writes to io.Writer a new secret access key. func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecretOptions) error { var ( @@ -130,6 +173,11 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr lifetime lifetimeOptions ) + policies, err := preparePolicy(options.ContainerPolicies) + if err != nil { + return err + } + lifetime.Iat, err = a.getCurrentEpoch(ctx) if err != nil { return err @@ -156,6 +204,8 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr return err } + box.ContainerPolicy = policies + oid, err := ownerIDFromNeoFSKey(options.NeoFSKey.PublicKey()) if err != nil { return err @@ -201,14 +251,14 @@ func (a *Agent) ObtainSecret(ctx context.Context, w io.Writer, options *ObtainSe return fmt.Errorf("failed to parse secret address: %w", err) } - tkns, err := bearerCreds.GetTokens(ctx, address) + box, err := bearerCreds.GetBox(ctx, address) if err != nil { return fmt.Errorf("failed to get tokens: %w", err) } or := &obtainingResult{ - BearerToken: tkns.BearerToken, - SecretAccessKey: tkns.AccessKey, + BearerToken: box.Gate.BearerToken, + SecretAccessKey: box.Gate.AccessKey, } enc := json.NewEncoder(w) diff --git a/cmd/authmate/main.go b/cmd/authmate/main.go index 7e46c4c44..1f732a6c3 100644 --- a/cmd/authmate/main.go +++ b/cmd/authmate/main.go @@ -3,6 +3,7 @@ package main import ( "context" "crypto/ecdsa" + "encoding/json" "fmt" "os" "os/signal" @@ -45,6 +46,7 @@ var ( logDebugEnabledFlag bool sessionTokenFlag bool lifetimeFlag uint64 + containerPolicies string ) const ( @@ -201,6 +203,12 @@ func issueSecret() *cli.Command { Destination: &lifetimeFlag, Value: defaultLifetime, }, + &cli.StringFlag{ + Name: "container-policy", + Usage: "mapping AWS storage class to NeoFS storage policy as plain json string or path to json file", + Required: false, + Destination: &containerPolicies, + }, }, Action: func(c *cli.Context) error { ctx, log := prepare() @@ -241,6 +249,11 @@ func issueSecret() *cli.Command { return cli.Exit(fmt.Sprintf("lifetime must be at least 1, current value: %d", lifetimeFlag), 5) } + policies, err := parsePolicies(containerPolicies) + if err != nil { + return cli.Exit(fmt.Sprintf("couldn't parse container policy: %s", err.Error()), 6) + } + issueSecretOptions := &authmate.IssueSecretOptions{ ContainerID: containerID, ContainerFriendlyName: containerFriendlyName, @@ -248,6 +261,7 @@ func issueSecret() *cli.Command { GatesPublicKeys: gatesPublicKeys, EACLRules: getJSONRules(eaclRulesFlag), ContextRules: getJSONRules(contextRulesFlag), + ContainerPolicies: policies, SessionTkn: sessionTokenFlag, Lifetime: lifetimeFlag, } @@ -261,6 +275,23 @@ func issueSecret() *cli.Command { } } +func parsePolicies(val string) (authmate.ContainerPolicies, error) { + if val == "" { + return nil, nil + } + data, err := os.ReadFile(val) + if err != nil { + data = []byte(val) + } + + var policies authmate.ContainerPolicies + if err = json.Unmarshal(data, &policies); err != nil { + return nil, err + } + + return policies, nil +} + func getJSONRules(val string) []byte { if data, err := os.ReadFile(val); err == nil { return data diff --git a/creds/accessbox/accessbox.go b/creds/accessbox/accessbox.go index 231eb54fe..65790611c 100644 --- a/creds/accessbox/accessbox.go +++ b/creds/accessbox/accessbox.go @@ -11,6 +11,7 @@ import ( "io" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" + "github.com/nspcc-dev/neofs-api-go/pkg/netmap" "github.com/nspcc-dev/neofs-api-go/pkg/session" "github.com/nspcc-dev/neofs-api-go/pkg/token" "golang.org/x/crypto/chacha20poly1305" @@ -18,6 +19,18 @@ import ( "google.golang.org/protobuf/proto" ) +// Box represents friendly AccessBox. +type Box struct { + Gate *GateData + Policies []*ContainerPolicy +} + +// ContainerPolicy represents friendly AccessBox_ContainerPolicy. +type ContainerPolicy struct { + LocationConstraint string + Policy *netmap.PlacementPolicy +} + // GateData represents gate tokens in AccessBox. type GateData struct { AccessKey string @@ -91,6 +104,42 @@ func (x *AccessBox) GetTokens(owner *keys.PrivateKey) (*GateData, error) { return nil, fmt.Errorf("no gate data for key %x was found", ownerKey) } +// GetPlacementPolicy returns ContainerPolicy from AccessBox. +func (x *AccessBox) GetPlacementPolicy() ([]*ContainerPolicy, error) { + var result []*ContainerPolicy + for _, policy := range x.ContainerPolicy { + placementPolicy := netmap.NewPlacementPolicy() + if err := placementPolicy.Unmarshal(policy.Policy); err != nil { + return nil, err + } + + result = append(result, &ContainerPolicy{ + LocationConstraint: policy.LocationConstraint, + Policy: placementPolicy, + }) + } + + return result, nil +} + +// GetBox parse AccessBox to Box. +func (x *AccessBox) GetBox(owner *keys.PrivateKey) (*Box, error) { + tokens, err := x.GetTokens(owner) + if err != nil { + return nil, err + } + + policy, err := x.GetPlacementPolicy() + if err != nil { + return nil, err + } + + return &Box{ + Gate: tokens, + Policies: policy, + }, nil +} + func (x *AccessBox) addTokens(gatesData []*GateData, ephemeralKey *keys.PrivateKey, secret []byte) error { for i, gate := range gatesData { encBearer, err := gate.BearerToken.Marshal() diff --git a/creds/accessbox/accessbox.pb.go b/creds/accessbox/accessbox.pb.go index a0a309120..736adacaa 100644 --- a/creds/accessbox/accessbox.pb.go +++ b/creds/accessbox/accessbox.pb.go @@ -7,10 +7,11 @@ package accessbox import ( - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( @@ -25,14 +26,15 @@ type AccessBox struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - OwnerPublicKey []byte `protobuf:"bytes,1,opt,name=ownerPublicKey,proto3" json:"ownerPublicKey,omitempty"` - Gates []*AccessBox_Gate `protobuf:"bytes,2,rep,name=gates,proto3" json:"gates,omitempty"` + OwnerPublicKey []byte `protobuf:"bytes,1,opt,name=ownerPublicKey,proto3" json:"ownerPublicKey,omitempty"` + Gates []*AccessBox_Gate `protobuf:"bytes,2,rep,name=gates,proto3" json:"gates,omitempty"` + ContainerPolicy []*AccessBox_ContainerPolicy `protobuf:"bytes,3,rep,name=containerPolicy,proto3" json:"containerPolicy,omitempty"` } func (x *AccessBox) Reset() { *x = AccessBox{} if protoimpl.UnsafeEnabled { - mi := &file_creds_accessbox_accessbox_proto_msgTypes[0] + mi := &file_accessbox_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -45,7 +47,7 @@ func (x *AccessBox) String() string { func (*AccessBox) ProtoMessage() {} func (x *AccessBox) ProtoReflect() protoreflect.Message { - mi := &file_creds_accessbox_accessbox_proto_msgTypes[0] + mi := &file_accessbox_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -58,7 +60,7 @@ func (x *AccessBox) ProtoReflect() protoreflect.Message { // Deprecated: Use AccessBox.ProtoReflect.Descriptor instead. func (*AccessBox) Descriptor() ([]byte, []int) { - return file_creds_accessbox_accessbox_proto_rawDescGZIP(), []int{0} + return file_accessbox_proto_rawDescGZIP(), []int{0} } func (x *AccessBox) GetOwnerPublicKey() []byte { @@ -75,6 +77,13 @@ func (x *AccessBox) GetGates() []*AccessBox_Gate { return nil } +func (x *AccessBox) GetContainerPolicy() []*AccessBox_ContainerPolicy { + if x != nil { + return x.ContainerPolicy + } + return nil +} + type Tokens struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -88,7 +97,7 @@ type Tokens struct { func (x *Tokens) Reset() { *x = Tokens{} if protoimpl.UnsafeEnabled { - mi := &file_creds_accessbox_accessbox_proto_msgTypes[1] + mi := &file_accessbox_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -101,7 +110,7 @@ func (x *Tokens) String() string { func (*Tokens) ProtoMessage() {} func (x *Tokens) ProtoReflect() protoreflect.Message { - mi := &file_creds_accessbox_accessbox_proto_msgTypes[1] + mi := &file_accessbox_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -114,7 +123,7 @@ func (x *Tokens) ProtoReflect() protoreflect.Message { // Deprecated: Use Tokens.ProtoReflect.Descriptor instead. func (*Tokens) Descriptor() ([]byte, []int) { - return file_creds_accessbox_accessbox_proto_rawDescGZIP(), []int{1} + return file_accessbox_proto_rawDescGZIP(), []int{1} } func (x *Tokens) GetAccessKey() []byte { @@ -150,7 +159,7 @@ type AccessBox_Gate struct { func (x *AccessBox_Gate) Reset() { *x = AccessBox_Gate{} if protoimpl.UnsafeEnabled { - mi := &file_creds_accessbox_accessbox_proto_msgTypes[2] + mi := &file_accessbox_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -163,7 +172,7 @@ func (x *AccessBox_Gate) String() string { func (*AccessBox_Gate) ProtoMessage() {} func (x *AccessBox_Gate) ProtoReflect() protoreflect.Message { - mi := &file_creds_accessbox_accessbox_proto_msgTypes[2] + mi := &file_accessbox_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -176,7 +185,7 @@ func (x *AccessBox_Gate) ProtoReflect() protoreflect.Message { // Deprecated: Use AccessBox_Gate.ProtoReflect.Descriptor instead. func (*AccessBox_Gate) Descriptor() ([]byte, []int) { - return file_creds_accessbox_accessbox_proto_rawDescGZIP(), []int{0, 0} + return file_accessbox_proto_rawDescGZIP(), []int{0, 0} } func (x *AccessBox_Gate) GetTokens() []byte { @@ -193,70 +202,137 @@ func (x *AccessBox_Gate) GetGatePublicKey() []byte { return nil } -var File_creds_accessbox_accessbox_proto protoreflect.FileDescriptor +type AccessBox_ContainerPolicy struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields -var file_creds_accessbox_accessbox_proto_rawDesc = []byte{ - 0x0a, 0x1f, 0x63, 0x72, 0x65, 0x64, 0x73, 0x2f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x62, 0x6f, - 0x78, 0x2f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x62, 0x6f, 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x12, 0x09, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x62, 0x6f, 0x78, 0x22, 0xaa, 0x01, 0x0a, + LocationConstraint string `protobuf:"bytes,1,opt,name=locationConstraint,proto3" json:"locationConstraint,omitempty"` + Policy []byte `protobuf:"bytes,2,opt,name=policy,proto3" json:"policy,omitempty"` +} + +func (x *AccessBox_ContainerPolicy) Reset() { + *x = AccessBox_ContainerPolicy{} + if protoimpl.UnsafeEnabled { + mi := &file_accessbox_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AccessBox_ContainerPolicy) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AccessBox_ContainerPolicy) ProtoMessage() {} + +func (x *AccessBox_ContainerPolicy) ProtoReflect() protoreflect.Message { + mi := &file_accessbox_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use AccessBox_ContainerPolicy.ProtoReflect.Descriptor instead. +func (*AccessBox_ContainerPolicy) Descriptor() ([]byte, []int) { + return file_accessbox_proto_rawDescGZIP(), []int{0, 1} +} + +func (x *AccessBox_ContainerPolicy) GetLocationConstraint() string { + if x != nil { + return x.LocationConstraint + } + return "" +} + +func (x *AccessBox_ContainerPolicy) GetPolicy() []byte { + if x != nil { + return x.Policy + } + return nil +} + +var File_accessbox_proto protoreflect.FileDescriptor + +var file_accessbox_proto_rawDesc = []byte{ + 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x62, 0x6f, 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x09, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x62, 0x6f, 0x78, 0x22, 0xd5, 0x02, 0x0a, 0x09, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x42, 0x6f, 0x78, 0x12, 0x26, 0x0a, 0x0e, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x05, 0x67, 0x61, 0x74, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x62, 0x6f, 0x78, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x42, 0x6f, 0x78, 0x2e, 0x47, 0x61, 0x74, 0x65, 0x52, 0x05, 0x67, 0x61, - 0x74, 0x65, 0x73, 0x1a, 0x44, 0x0a, 0x04, 0x47, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, + 0x74, 0x65, 0x73, 0x12, 0x4e, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x62, 0x6f, 0x78, 0x2e, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x42, + 0x6f, 0x78, 0x2e, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x6f, 0x6c, + 0x69, 0x63, 0x79, 0x1a, 0x44, 0x0a, 0x04, 0x47, 0x61, 0x74, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x67, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x67, 0x61, 0x74, 0x65, - 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x22, 0x6c, 0x0a, 0x06, 0x54, 0x6f, 0x6b, - 0x65, 0x6e, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, - 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x65, 0x61, 0x72, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x62, 0x65, 0x61, 0x72, 0x65, 0x72, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, - 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, - 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x73, 0x70, 0x63, 0x63, 0x2d, 0x64, 0x65, 0x76, 0x2f, - 0x6e, 0x65, 0x6f, 0x66, 0x73, 0x2d, 0x73, 0x33, 0x2d, 0x67, 0x77, 0x2f, 0x63, 0x72, 0x65, 0x64, - 0x73, 0x2f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x62, 0x6f, 0x78, 0x3b, 0x61, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x62, 0x6f, 0x78, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x1a, 0x59, 0x0a, 0x0f, 0x43, 0x6f, 0x6e, + 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x2e, 0x0a, 0x12, + 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, + 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x6f, + 0x6c, 0x69, 0x63, 0x79, 0x22, 0x6c, 0x0a, 0x06, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x1c, + 0x0a, 0x09, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x09, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, + 0x62, 0x65, 0x61, 0x72, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x0b, 0x62, 0x65, 0x61, 0x72, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x22, + 0x0a, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f, 0x6b, + 0x65, 0x6e, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x6e, 0x73, 0x70, 0x63, 0x63, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x6e, 0x65, 0x6f, 0x66, 0x73, + 0x2d, 0x73, 0x33, 0x2d, 0x67, 0x77, 0x2f, 0x63, 0x72, 0x65, 0x64, 0x73, 0x2f, 0x74, 0x6f, 0x6b, + 0x65, 0x6e, 0x62, 0x6f, 0x78, 0x3b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x62, 0x6f, 0x78, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( - file_creds_accessbox_accessbox_proto_rawDescOnce sync.Once - file_creds_accessbox_accessbox_proto_rawDescData = file_creds_accessbox_accessbox_proto_rawDesc + file_accessbox_proto_rawDescOnce sync.Once + file_accessbox_proto_rawDescData = file_accessbox_proto_rawDesc ) -func file_creds_accessbox_accessbox_proto_rawDescGZIP() []byte { - file_creds_accessbox_accessbox_proto_rawDescOnce.Do(func() { - file_creds_accessbox_accessbox_proto_rawDescData = protoimpl.X.CompressGZIP(file_creds_accessbox_accessbox_proto_rawDescData) +func file_accessbox_proto_rawDescGZIP() []byte { + file_accessbox_proto_rawDescOnce.Do(func() { + file_accessbox_proto_rawDescData = protoimpl.X.CompressGZIP(file_accessbox_proto_rawDescData) }) - return file_creds_accessbox_accessbox_proto_rawDescData + return file_accessbox_proto_rawDescData } -var file_creds_accessbox_accessbox_proto_msgTypes = make([]protoimpl.MessageInfo, 3) -var file_creds_accessbox_accessbox_proto_goTypes = []interface{}{ - (*AccessBox)(nil), // 0: accessbox.AccessBox - (*Tokens)(nil), // 1: accessbox.Tokens - (*AccessBox_Gate)(nil), // 2: accessbox.AccessBox.Gate +var file_accessbox_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_accessbox_proto_goTypes = []interface{}{ + (*AccessBox)(nil), // 0: accessbox.AccessBox + (*Tokens)(nil), // 1: accessbox.Tokens + (*AccessBox_Gate)(nil), // 2: accessbox.AccessBox.Gate + (*AccessBox_ContainerPolicy)(nil), // 3: accessbox.AccessBox.ContainerPolicy } -var file_creds_accessbox_accessbox_proto_depIdxs = []int32{ +var file_accessbox_proto_depIdxs = []int32{ 2, // 0: accessbox.AccessBox.gates:type_name -> accessbox.AccessBox.Gate - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 3, // 1: accessbox.AccessBox.containerPolicy:type_name -> accessbox.AccessBox.ContainerPolicy + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } -func init() { file_creds_accessbox_accessbox_proto_init() } -func file_creds_accessbox_accessbox_proto_init() { - if File_creds_accessbox_accessbox_proto != nil { +func init() { file_accessbox_proto_init() } +func file_accessbox_proto_init() { + if File_accessbox_proto != nil { return } if !protoimpl.UnsafeEnabled { - file_creds_accessbox_accessbox_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_accessbox_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AccessBox); i { case 0: return &v.state @@ -268,7 +344,7 @@ func file_creds_accessbox_accessbox_proto_init() { return nil } } - file_creds_accessbox_accessbox_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_accessbox_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Tokens); i { case 0: return &v.state @@ -280,7 +356,7 @@ func file_creds_accessbox_accessbox_proto_init() { return nil } } - file_creds_accessbox_accessbox_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_accessbox_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*AccessBox_Gate); i { case 0: return &v.state @@ -292,23 +368,35 @@ func file_creds_accessbox_accessbox_proto_init() { return nil } } + file_accessbox_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AccessBox_ContainerPolicy); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_creds_accessbox_accessbox_proto_rawDesc, + RawDescriptor: file_accessbox_proto_rawDesc, NumEnums: 0, - NumMessages: 3, + NumMessages: 4, NumExtensions: 0, NumServices: 0, }, - GoTypes: file_creds_accessbox_accessbox_proto_goTypes, - DependencyIndexes: file_creds_accessbox_accessbox_proto_depIdxs, - MessageInfos: file_creds_accessbox_accessbox_proto_msgTypes, + GoTypes: file_accessbox_proto_goTypes, + DependencyIndexes: file_accessbox_proto_depIdxs, + MessageInfos: file_accessbox_proto_msgTypes, }.Build() - File_creds_accessbox_accessbox_proto = out.File - file_creds_accessbox_accessbox_proto_rawDesc = nil - file_creds_accessbox_accessbox_proto_goTypes = nil - file_creds_accessbox_accessbox_proto_depIdxs = nil + File_accessbox_proto = out.File + file_accessbox_proto_rawDesc = nil + file_accessbox_proto_goTypes = nil + file_accessbox_proto_depIdxs = nil } diff --git a/creds/accessbox/accessbox.proto b/creds/accessbox/accessbox.proto index 5750b8080..d85aa5cc0 100644 --- a/creds/accessbox/accessbox.proto +++ b/creds/accessbox/accessbox.proto @@ -12,8 +12,14 @@ message AccessBox { bytes gatePublicKey = 2 [json_name = "gatePublicKey"]; } + message ContainerPolicy { + string locationConstraint = 1; + bytes policy = 2; + } + bytes ownerPublicKey = 1 [json_name = "ownerPublicKey"]; repeated Gate gates = 2 [json_name = "gates"]; + repeated ContainerPolicy containerPolicy = 3 [json_name = "containerPolicy"]; } message Tokens { diff --git a/creds/tokens/credentials.go b/creds/tokens/credentials.go index 841ad8d78..4f0ad2aa4 100644 --- a/creds/tokens/credentials.go +++ b/creds/tokens/credentials.go @@ -20,7 +20,7 @@ import ( type ( // Credentials is a bearer token get/put interface. Credentials interface { - GetTokens(context.Context, *object.Address) (*accessbox.GateData, error) + GetBox(context.Context, *object.Address) (*accessbox.Box, error) Put(context.Context, *cid.ID, *owner.ID, *accessbox.AccessBox, ...*keys.PublicKey) (*object.Address, error) } @@ -68,6 +68,15 @@ func (c *cred) GetTokens(ctx context.Context, address *object.Address) (*accessb return box.GetTokens(c.key) } +func (c *cred) GetBox(ctx context.Context, address *object.Address) (*accessbox.Box, error) { + box, err := c.getAccessBox(ctx, address) + if err != nil { + return nil, err + } + + return box.GetBox(c.key) +} + func (c *cred) getAccessBox(ctx context.Context, address *object.Address) (*accessbox.AccessBox, error) { var ( box accessbox.AccessBox