forked from TrueCloudLab/frostfs-s3-gw
Merge pull request #170 from KirillovDenis/feature/89-placement_policy
[#89] Add placement policy
This commit is contained in:
commit
f4cd1e4c38
12 changed files with 394 additions and 124 deletions
13
README.md
13
README.md
|
@ -280,6 +280,18 @@ Rules for session token can be set via param `session-rules` (json-string and fi
|
||||||
If `session-rules` is set, but `create-session-token` is not, the session
|
If `session-rules` is set, but `create-session-token` is not, the session
|
||||||
token will not be created.
|
token will not be created.
|
||||||
|
|
||||||
|
Rules for mapping of `LocationConstraint` ([aws spec](https://docs.aws.amazon.com/AmazonS3/latest/API/API_CreateBucket.html#API_CreateBucket_RequestBody))
|
||||||
|
to `PlacementPolicy` ([neofs spec](https://github.com/nspcc-dev/neofs-spec/blob/master/01-arch/02-policy.md))
|
||||||
|
can be set via param `container-policy` (json-string and file path allowed):
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"rep-3": "REP 3",
|
||||||
|
"complex": "REP 1 IN X CBF 1 SELECT 1 FROM * AS X",
|
||||||
|
"example-json-policy": "{\"replicas\":[{\"count\":3,\"selector\":\"SelASD0\"}],\"container_backup_factor\":3,\"selectors\":[{\"name\":\"SelASD0\",\"count\":3,\"filter\":\"*\"}],\"filters\":[]}"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
Example of a command to issue a secret with custom rules for multiple gates:
|
Example of a command to issue a secret with custom rules for multiple gates:
|
||||||
```
|
```
|
||||||
$ ./neofs-authmate issue-secret --wallet wallet.json \
|
$ ./neofs-authmate issue-secret --wallet wallet.json \
|
||||||
|
@ -289,6 +301,7 @@ $ ./neofs-authmate issue-secret --wallet wallet.json \
|
||||||
--gate-public-key 0317585fa8274f7afdf1fc5f2a2e7bece549d5175c4e5182e37924f30229aef967 \
|
--gate-public-key 0317585fa8274f7afdf1fc5f2a2e7bece549d5175c4e5182e37924f30229aef967 \
|
||||||
--create-session-token \
|
--create-session-token \
|
||||||
--session-rules '{"verb":"DELETE","wildcard":false,"containerID":{"value":"%CID"}}'
|
--session-rules '{"verb":"DELETE","wildcard":false,"containerID":{"value":"%CID"}}'
|
||||||
|
--container-policy '{"rep-3": "REP 3"}'
|
||||||
|
|
||||||
Enter password for wallet.json >
|
Enter password for wallet.json >
|
||||||
{
|
{
|
||||||
|
|
|
@ -26,7 +26,7 @@ var authorizationFieldRegexp = regexp.MustCompile(`AWS4-HMAC-SHA256 Credential=(
|
||||||
type (
|
type (
|
||||||
// Center is a user authentication interface.
|
// Center is a user authentication interface.
|
||||||
Center interface {
|
Center interface {
|
||||||
Authenticate(request *http.Request) (*accessbox.GateData, error)
|
Authenticate(request *http.Request) (*accessbox.Box, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
center struct {
|
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()
|
queryValues := r.URL.Query()
|
||||||
if queryValues.Get("X-Amz-Algorithm") == "AWS4-HMAC-SHA256" {
|
if queryValues.Get("X-Amz-Algorithm") == "AWS4-HMAC-SHA256" {
|
||||||
return nil, errors.New("pre-signed form of request is not supported")
|
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)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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 := v4.NewSigner(awsCreds)
|
||||||
signer.DisableURIPathEscaping = true
|
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 nil, errors.New("failed to pass authentication procedure")
|
||||||
}
|
}
|
||||||
|
|
||||||
return tkns, nil
|
return box, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/xml"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -11,6 +13,7 @@ import (
|
||||||
"github.com/nspcc-dev/neofs-node/pkg/policy"
|
"github.com/nspcc-dev/neofs-node/pkg/policy"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api"
|
"github.com/nspcc-dev/neofs-s3-gw/api"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
|
"github.com/nspcc-dev/neofs-s3-gw/api/layer"
|
||||||
|
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -24,6 +27,11 @@ const (
|
||||||
publicBasicRule = 0x0FFFFFFF
|
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) {
|
func (h *handler) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
|
@ -79,7 +87,6 @@ func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
p = layer.CreateBucketParams{}
|
p = layer.CreateBucketParams{}
|
||||||
rid = api.GetRequestID(r.Context())
|
|
||||||
req = mux.Vars(r)
|
req = mux.Vars(r)
|
||||||
)
|
)
|
||||||
p.Name = req["bucket"]
|
p.Name = req["bucket"]
|
||||||
|
@ -90,43 +97,41 @@ func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.log.Error("could not parse basic ACL",
|
h.registerAndSendError(w, r, err, "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)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createParams, err := parseLocationConstraint(r)
|
||||||
|
if err != nil {
|
||||||
|
h.registerAndSendError(w, r, err, "could not parse body")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
p.BoxData, err = getBoxData(r.Context())
|
||||||
|
if err != nil {
|
||||||
|
h.registerAndSendError(w, r, err, "could not get boxData")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if createParams.LocationConstraint != "" {
|
||||||
|
for _, placementPolicy := range p.BoxData.Policies {
|
||||||
|
if placementPolicy.LocationConstraint == createParams.LocationConstraint {
|
||||||
|
p.Policy = placementPolicy.Policy
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.Policy == nil {
|
||||||
p.Policy, err = policy.Parse(defaultPolicy)
|
p.Policy, err = policy.Parse(defaultPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.log.Error("could not parse policy",
|
h.registerAndSendError(w, r, err, "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)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cid, err := h.obj.CreateBucket(r.Context(), &p)
|
cid, err := h.obj.CreateBucket(r.Context(), &p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.log.Error("could not create bucket",
|
h.registerAndSendError(w, r, err, "could not create bucket")
|
||||||
zap.String("request_id", rid),
|
|
||||||
zap.Error(err))
|
|
||||||
|
|
||||||
api.WriteErrorResponse(r.Context(), w, api.Error{
|
|
||||||
Code: api.GetAPIError(api.ErrInternalError).Code,
|
|
||||||
Description: err.Error(),
|
|
||||||
HTTPStatusCode: http.StatusInternalServerError,
|
|
||||||
}, r.URL)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,6 +141,18 @@ func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
api.WriteSuccessResponseHeadersOnly(w)
|
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) {
|
func parseBasicACL(basicACL string) (uint32, error) {
|
||||||
switch basicACL {
|
switch basicACL {
|
||||||
case basicACLPublic:
|
case basicACLPublic:
|
||||||
|
@ -155,3 +172,17 @@ func parseBasicACL(basicACL string) (uint32, error) {
|
||||||
return uint32(value), nil
|
return uint32(value), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ import (
|
||||||
cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id"
|
cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id"
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/owner"
|
"github.com/nspcc-dev/neofs-api-go/pkg/owner"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/api"
|
"github.com/nspcc-dev/neofs-s3-gw/api"
|
||||||
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
|
|
||||||
"github.com/nspcc-dev/neofs-sdk-go/pkg/pool"
|
"github.com/nspcc-dev/neofs-sdk-go/pkg/pool"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
@ -119,14 +118,7 @@ func (n *layer) createContainer(ctx context.Context, p *CreateBucketParams) (*ci
|
||||||
container.WithAttribute(container.AttributeName, p.Name),
|
container.WithAttribute(container.AttributeName, p.Name),
|
||||||
container.WithAttribute(container.AttributeTimestamp, strconv.FormatInt(time.Now().Unix(), 10)))
|
container.WithAttribute(container.AttributeTimestamp, strconv.FormatInt(time.Now().Unix(), 10)))
|
||||||
|
|
||||||
var gateData *accessbox.GateData
|
cnr.SetSessionToken(p.BoxData.Gate.SessionToken)
|
||||||
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.SetOwnerID(n.Owner(ctx))
|
cnr.SetOwnerID(n.Owner(ctx))
|
||||||
|
|
||||||
cid, err := n.pool.PutContainer(ctx, cnr)
|
cid, err := n.pool.PutContainer(ctx, cnr)
|
||||||
|
@ -138,7 +130,7 @@ func (n *layer) createContainer(ctx context.Context, p *CreateBucketParams) (*ci
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := n.setContainerEACL(ctx, cid, gateData.GateKey); err != nil {
|
if err := n.setContainerEACL(ctx, cid, p.BoxData.Gate.GateKey); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,7 @@ type (
|
||||||
Name string
|
Name string
|
||||||
ACL uint32
|
ACL uint32
|
||||||
Policy *netmap.PlacementPolicy
|
Policy *netmap.PlacementPolicy
|
||||||
|
BoxData *accessbox.Box
|
||||||
}
|
}
|
||||||
// DeleteBucketParams stores delete bucket request parameters.
|
// DeleteBucketParams stores delete bucket request parameters.
|
||||||
DeleteBucketParams struct {
|
DeleteBucketParams struct {
|
||||||
|
@ -134,8 +135,8 @@ func NewLayer(log *zap.Logger, conns pool.Pool) Client {
|
||||||
|
|
||||||
// Owner returns owner id from BearerToken (context) or from client owner.
|
// Owner returns owner id from BearerToken (context) or from client owner.
|
||||||
func (n *layer) Owner(ctx context.Context) *owner.ID {
|
func (n *layer) Owner(ctx context.Context) *owner.ID {
|
||||||
if data, ok := ctx.Value(api.GateData).(*accessbox.GateData); ok && data != nil {
|
if data, ok := ctx.Value(api.BoxData).(*accessbox.Box); ok && data != nil && data.Gate != nil {
|
||||||
return data.BearerToken.Issuer()
|
return data.Gate.BearerToken.Issuer()
|
||||||
}
|
}
|
||||||
|
|
||||||
return n.pool.OwnerID()
|
return n.pool.OwnerID()
|
||||||
|
@ -143,8 +144,8 @@ func (n *layer) Owner(ctx context.Context) *owner.ID {
|
||||||
|
|
||||||
// BearerOpt returns client.WithBearer call option with token from context or with nil token.
|
// BearerOpt returns client.WithBearer call option with token from context or with nil token.
|
||||||
func (n *layer) BearerOpt(ctx context.Context) client.CallOption {
|
func (n *layer) BearerOpt(ctx context.Context) client.CallOption {
|
||||||
if data, ok := ctx.Value(api.GateData).(*accessbox.GateData); ok && data != nil {
|
if data, ok := ctx.Value(api.BoxData).(*accessbox.Box); ok && data != nil && data.Gate != nil {
|
||||||
return client.WithBearer(data.BearerToken)
|
return client.WithBearer(data.Gate.BearerToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
return client.WithBearer(nil)
|
return client.WithBearer(nil)
|
||||||
|
@ -152,8 +153,8 @@ func (n *layer) BearerOpt(ctx context.Context) client.CallOption {
|
||||||
|
|
||||||
// SessionOpt returns client.WithSession call option with token from context or with nil token.
|
// SessionOpt returns client.WithSession call option with token from context or with nil token.
|
||||||
func (n *layer) SessionOpt(ctx context.Context) client.CallOption {
|
func (n *layer) SessionOpt(ctx context.Context) client.CallOption {
|
||||||
if data, ok := ctx.Value(api.GateData).(*accessbox.GateData); ok && data != nil {
|
if data, ok := ctx.Value(api.BoxData).(*accessbox.Box); ok && data != nil && data.Gate != nil {
|
||||||
return client.WithSession(data.SessionToken)
|
return client.WithSession(data.Gate.SessionToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
return client.WithSession(nil)
|
return client.WithSession(nil)
|
||||||
|
|
|
@ -12,15 +12,15 @@ import (
|
||||||
// KeyWrapper is wrapper for context keys.
|
// KeyWrapper is wrapper for context keys.
|
||||||
type KeyWrapper string
|
type KeyWrapper string
|
||||||
|
|
||||||
// GateData is an ID used to store GateData in a context.
|
// BoxData is an ID used to store accessbox.Box in a context.
|
||||||
var GateData = KeyWrapper("__context_gate_data_key")
|
var BoxData = KeyWrapper("__context_box_key")
|
||||||
|
|
||||||
// AttachUserAuth adds user authentication via center to router using log for logging.
|
// AttachUserAuth adds user authentication via center to router using log for logging.
|
||||||
func AttachUserAuth(router *mux.Router, center auth.Center, log *zap.Logger) {
|
func AttachUserAuth(router *mux.Router, center auth.Center, log *zap.Logger) {
|
||||||
router.Use(func(h http.Handler) http.Handler {
|
router.Use(func(h http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
var ctx context.Context
|
var ctx context.Context
|
||||||
tokens, err := center.Authenticate(r)
|
box, err := center.Authenticate(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == auth.ErrNoAuthorizationHeader {
|
if err == auth.ErrNoAuthorizationHeader {
|
||||||
log.Debug("couldn't receive bearer token, using neofs-key")
|
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
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ctx = context.WithValue(r.Context(), GateData, tokens)
|
ctx = context.WithValue(r.Context(), BoxData, box)
|
||||||
}
|
}
|
||||||
|
|
||||||
h.ServeHTTP(w, r.WithContext(ctx))
|
h.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
|
|
@ -44,6 +44,9 @@ func New(log *zap.Logger, conns pool.Pool) *Agent {
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
// ContainerPolicies contains mapping of aws LocationConstraint to neofs PlacementPolicy.
|
||||||
|
ContainerPolicies map[string]string
|
||||||
|
|
||||||
// IssueSecretOptions contains options for passing to Agent.IssueSecret method.
|
// IssueSecretOptions contains options for passing to Agent.IssueSecret method.
|
||||||
IssueSecretOptions struct {
|
IssueSecretOptions struct {
|
||||||
ContainerID *cid.ID
|
ContainerID *cid.ID
|
||||||
|
@ -54,6 +57,7 @@ type (
|
||||||
ContextRules []byte
|
ContextRules []byte
|
||||||
SessionTkn bool
|
SessionTkn bool
|
||||||
Lifetime uint64
|
Lifetime uint64
|
||||||
|
ContainerPolicies ContainerPolicies
|
||||||
}
|
}
|
||||||
|
|
||||||
// ObtainSecretOptions contains options for passing to Agent.ObtainSecret method.
|
// ObtainSecretOptions contains options for passing to Agent.ObtainSecret method.
|
||||||
|
@ -122,6 +126,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.
|
// 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 {
|
func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecretOptions) error {
|
||||||
var (
|
var (
|
||||||
|
@ -131,6 +174,11 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
|
||||||
lifetime lifetimeOptions
|
lifetime lifetimeOptions
|
||||||
)
|
)
|
||||||
|
|
||||||
|
policies, err := preparePolicy(options.ContainerPolicies)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
lifetime.Iat, err = a.getCurrentEpoch(ctx)
|
lifetime.Iat, err = a.getCurrentEpoch(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -157,6 +205,8 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
box.ContainerPolicy = policies
|
||||||
|
|
||||||
oid, err := ownerIDFromNeoFSKey(options.NeoFSKey.PublicKey())
|
oid, err := ownerIDFromNeoFSKey(options.NeoFSKey.PublicKey())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -203,14 +253,14 @@ func (a *Agent) ObtainSecret(ctx context.Context, w io.Writer, options *ObtainSe
|
||||||
return fmt.Errorf("failed to parse secret address: %w", err)
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get tokens: %w", err)
|
return fmt.Errorf("failed to get tokens: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
or := &obtainingResult{
|
or := &obtainingResult{
|
||||||
BearerToken: tkns.BearerToken,
|
BearerToken: box.Gate.BearerToken,
|
||||||
SecretAccessKey: tkns.AccessKey,
|
SecretAccessKey: box.Gate.AccessKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
enc := json.NewEncoder(w)
|
enc := json.NewEncoder(w)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
@ -45,6 +46,7 @@ var (
|
||||||
logDebugEnabledFlag bool
|
logDebugEnabledFlag bool
|
||||||
sessionTokenFlag bool
|
sessionTokenFlag bool
|
||||||
lifetimeFlag uint64
|
lifetimeFlag uint64
|
||||||
|
containerPolicies string
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -201,6 +203,12 @@ func issueSecret() *cli.Command {
|
||||||
Destination: &lifetimeFlag,
|
Destination: &lifetimeFlag,
|
||||||
Value: defaultLifetime,
|
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 {
|
Action: func(c *cli.Context) error {
|
||||||
ctx, log := prepare()
|
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)
|
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{
|
issueSecretOptions := &authmate.IssueSecretOptions{
|
||||||
ContainerID: containerID,
|
ContainerID: containerID,
|
||||||
ContainerFriendlyName: containerFriendlyName,
|
ContainerFriendlyName: containerFriendlyName,
|
||||||
|
@ -248,6 +261,7 @@ func issueSecret() *cli.Command {
|
||||||
GatesPublicKeys: gatesPublicKeys,
|
GatesPublicKeys: gatesPublicKeys,
|
||||||
EACLRules: getJSONRules(eaclRulesFlag),
|
EACLRules: getJSONRules(eaclRulesFlag),
|
||||||
ContextRules: getJSONRules(contextRulesFlag),
|
ContextRules: getJSONRules(contextRulesFlag),
|
||||||
|
ContainerPolicies: policies,
|
||||||
SessionTkn: sessionTokenFlag,
|
SessionTkn: sessionTokenFlag,
|
||||||
Lifetime: lifetimeFlag,
|
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 {
|
func getJSONRules(val string) []byte {
|
||||||
if data, err := os.ReadFile(val); err == nil {
|
if data, err := os.ReadFile(val); err == nil {
|
||||||
return data
|
return data
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"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/session"
|
||||||
"github.com/nspcc-dev/neofs-api-go/pkg/token"
|
"github.com/nspcc-dev/neofs-api-go/pkg/token"
|
||||||
"golang.org/x/crypto/chacha20poly1305"
|
"golang.org/x/crypto/chacha20poly1305"
|
||||||
|
@ -18,6 +19,18 @@ import (
|
||||||
"google.golang.org/protobuf/proto"
|
"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.
|
// GateData represents gate tokens in AccessBox.
|
||||||
type GateData struct {
|
type GateData struct {
|
||||||
AccessKey string
|
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)
|
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 {
|
func (x *AccessBox) addTokens(gatesData []*GateData, ephemeralKey *keys.PrivateKey, secret []byte) error {
|
||||||
for i, gate := range gatesData {
|
for i, gate := range gatesData {
|
||||||
encBearer, err := gate.BearerToken.Marshal()
|
encBearer, err := gate.BearerToken.Marshal()
|
||||||
|
|
|
@ -7,10 +7,11 @@
|
||||||
package accessbox
|
package accessbox
|
||||||
|
|
||||||
import (
|
import (
|
||||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
|
||||||
reflect "reflect"
|
reflect "reflect"
|
||||||
sync "sync"
|
sync "sync"
|
||||||
|
|
||||||
|
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -27,12 +28,13 @@ type AccessBox struct {
|
||||||
|
|
||||||
OwnerPublicKey []byte `protobuf:"bytes,1,opt,name=ownerPublicKey,proto3" json:"ownerPublicKey,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"`
|
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() {
|
func (x *AccessBox) Reset() {
|
||||||
*x = AccessBox{}
|
*x = AccessBox{}
|
||||||
if protoimpl.UnsafeEnabled {
|
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 := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
@ -45,7 +47,7 @@ func (x *AccessBox) String() string {
|
||||||
func (*AccessBox) ProtoMessage() {}
|
func (*AccessBox) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *AccessBox) ProtoReflect() protoreflect.Message {
|
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 {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
@ -58,7 +60,7 @@ func (x *AccessBox) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use AccessBox.ProtoReflect.Descriptor instead.
|
// Deprecated: Use AccessBox.ProtoReflect.Descriptor instead.
|
||||||
func (*AccessBox) Descriptor() ([]byte, []int) {
|
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 {
|
func (x *AccessBox) GetOwnerPublicKey() []byte {
|
||||||
|
@ -75,6 +77,13 @@ func (x *AccessBox) GetGates() []*AccessBox_Gate {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *AccessBox) GetContainerPolicy() []*AccessBox_ContainerPolicy {
|
||||||
|
if x != nil {
|
||||||
|
return x.ContainerPolicy
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type Tokens struct {
|
type Tokens struct {
|
||||||
state protoimpl.MessageState
|
state protoimpl.MessageState
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
|
@ -88,7 +97,7 @@ type Tokens struct {
|
||||||
func (x *Tokens) Reset() {
|
func (x *Tokens) Reset() {
|
||||||
*x = Tokens{}
|
*x = Tokens{}
|
||||||
if protoimpl.UnsafeEnabled {
|
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 := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
@ -101,7 +110,7 @@ func (x *Tokens) String() string {
|
||||||
func (*Tokens) ProtoMessage() {}
|
func (*Tokens) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *Tokens) ProtoReflect() protoreflect.Message {
|
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 {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
@ -114,7 +123,7 @@ func (x *Tokens) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use Tokens.ProtoReflect.Descriptor instead.
|
// Deprecated: Use Tokens.ProtoReflect.Descriptor instead.
|
||||||
func (*Tokens) Descriptor() ([]byte, []int) {
|
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 {
|
func (x *Tokens) GetAccessKey() []byte {
|
||||||
|
@ -150,7 +159,7 @@ type AccessBox_Gate struct {
|
||||||
func (x *AccessBox_Gate) Reset() {
|
func (x *AccessBox_Gate) Reset() {
|
||||||
*x = AccessBox_Gate{}
|
*x = AccessBox_Gate{}
|
||||||
if protoimpl.UnsafeEnabled {
|
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 := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
ms.StoreMessageInfo(mi)
|
ms.StoreMessageInfo(mi)
|
||||||
}
|
}
|
||||||
|
@ -163,7 +172,7 @@ func (x *AccessBox_Gate) String() string {
|
||||||
func (*AccessBox_Gate) ProtoMessage() {}
|
func (*AccessBox_Gate) ProtoMessage() {}
|
||||||
|
|
||||||
func (x *AccessBox_Gate) ProtoReflect() protoreflect.Message {
|
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 {
|
if protoimpl.UnsafeEnabled && x != nil {
|
||||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||||
if ms.LoadMessageInfo() == nil {
|
if ms.LoadMessageInfo() == nil {
|
||||||
|
@ -176,7 +185,7 @@ func (x *AccessBox_Gate) ProtoReflect() protoreflect.Message {
|
||||||
|
|
||||||
// Deprecated: Use AccessBox_Gate.ProtoReflect.Descriptor instead.
|
// Deprecated: Use AccessBox_Gate.ProtoReflect.Descriptor instead.
|
||||||
func (*AccessBox_Gate) Descriptor() ([]byte, []int) {
|
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 {
|
func (x *AccessBox_Gate) GetTokens() []byte {
|
||||||
|
@ -193,70 +202,137 @@ func (x *AccessBox_Gate) GetGatePublicKey() []byte {
|
||||||
return nil
|
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{
|
LocationConstraint string `protobuf:"bytes,1,opt,name=locationConstraint,proto3" json:"locationConstraint,omitempty"`
|
||||||
0x0a, 0x1f, 0x63, 0x72, 0x65, 0x64, 0x73, 0x2f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x62, 0x6f,
|
Policy []byte `protobuf:"bytes,2,opt,name=policy,proto3" json:"policy,omitempty"`
|
||||||
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,
|
|
||||||
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x1a, 0x59, 0x0a, 0x0f, 0x43, 0x6f, 0x6e,
|
||||||
0x65, 0x6e, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79,
|
0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x2e, 0x0a, 0x12,
|
||||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65,
|
0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69,
|
||||||
0x79, 0x12, 0x20, 0x0a, 0x0b, 0x62, 0x65, 0x61, 0x72, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
|
0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x69,
|
||||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x62, 0x65, 0x61, 0x72, 0x65, 0x72, 0x54, 0x6f,
|
0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x74, 0x72, 0x61, 0x69, 0x6e, 0x74, 0x12, 0x16, 0x0a, 0x06,
|
||||||
0x6b, 0x65, 0x6e, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x6f,
|
0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x70, 0x6f,
|
||||||
0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x73, 0x65, 0x73, 0x73, 0x69,
|
0x6c, 0x69, 0x63, 0x79, 0x22, 0x6c, 0x0a, 0x06, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x12, 0x1c,
|
||||||
0x6f, 0x6e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75,
|
0x0a, 0x09, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6e, 0x73, 0x70, 0x63, 0x63, 0x2d, 0x64, 0x65, 0x76, 0x2f,
|
0x0c, 0x52, 0x09, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b,
|
||||||
0x6e, 0x65, 0x6f, 0x66, 0x73, 0x2d, 0x73, 0x33, 0x2d, 0x67, 0x77, 0x2f, 0x63, 0x72, 0x65, 0x64,
|
0x62, 0x65, 0x61, 0x72, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28,
|
||||||
0x73, 0x2f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x62, 0x6f, 0x78, 0x3b, 0x61, 0x63, 0x63, 0x65, 0x73,
|
0x0c, 0x52, 0x0b, 0x62, 0x65, 0x61, 0x72, 0x65, 0x72, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x22,
|
||||||
0x73, 0x62, 0x6f, 0x78, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
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 (
|
var (
|
||||||
file_creds_accessbox_accessbox_proto_rawDescOnce sync.Once
|
file_accessbox_proto_rawDescOnce sync.Once
|
||||||
file_creds_accessbox_accessbox_proto_rawDescData = file_creds_accessbox_accessbox_proto_rawDesc
|
file_accessbox_proto_rawDescData = file_accessbox_proto_rawDesc
|
||||||
)
|
)
|
||||||
|
|
||||||
func file_creds_accessbox_accessbox_proto_rawDescGZIP() []byte {
|
func file_accessbox_proto_rawDescGZIP() []byte {
|
||||||
file_creds_accessbox_accessbox_proto_rawDescOnce.Do(func() {
|
file_accessbox_proto_rawDescOnce.Do(func() {
|
||||||
file_creds_accessbox_accessbox_proto_rawDescData = protoimpl.X.CompressGZIP(file_creds_accessbox_accessbox_proto_rawDescData)
|
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_accessbox_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
|
||||||
var file_creds_accessbox_accessbox_proto_goTypes = []interface{}{
|
var file_accessbox_proto_goTypes = []interface{}{
|
||||||
(*AccessBox)(nil), // 0: accessbox.AccessBox
|
(*AccessBox)(nil), // 0: accessbox.AccessBox
|
||||||
(*Tokens)(nil), // 1: accessbox.Tokens
|
(*Tokens)(nil), // 1: accessbox.Tokens
|
||||||
(*AccessBox_Gate)(nil), // 2: accessbox.AccessBox.Gate
|
(*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
|
2, // 0: accessbox.AccessBox.gates:type_name -> accessbox.AccessBox.Gate
|
||||||
1, // [1:1] is the sub-list for method output_type
|
3, // 1: accessbox.AccessBox.containerPolicy:type_name -> accessbox.AccessBox.ContainerPolicy
|
||||||
1, // [1:1] is the sub-list for method input_type
|
2, // [2:2] is the sub-list for method output_type
|
||||||
1, // [1:1] is the sub-list for extension type_name
|
2, // [2:2] is the sub-list for method input_type
|
||||||
1, // [1:1] is the sub-list for extension extendee
|
2, // [2:2] is the sub-list for extension type_name
|
||||||
0, // [0:1] is the sub-list for field 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 init() { file_accessbox_proto_init() }
|
||||||
func file_creds_accessbox_accessbox_proto_init() {
|
func file_accessbox_proto_init() {
|
||||||
if File_creds_accessbox_accessbox_proto != nil {
|
if File_accessbox_proto != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !protoimpl.UnsafeEnabled {
|
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 {
|
switch v := v.(*AccessBox); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -268,7 +344,7 @@ func file_creds_accessbox_accessbox_proto_init() {
|
||||||
return nil
|
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 {
|
switch v := v.(*Tokens); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -280,7 +356,7 @@ func file_creds_accessbox_accessbox_proto_init() {
|
||||||
return nil
|
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 {
|
switch v := v.(*AccessBox_Gate); i {
|
||||||
case 0:
|
case 0:
|
||||||
return &v.state
|
return &v.state
|
||||||
|
@ -292,23 +368,35 @@ func file_creds_accessbox_accessbox_proto_init() {
|
||||||
return nil
|
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{}
|
type x struct{}
|
||||||
out := protoimpl.TypeBuilder{
|
out := protoimpl.TypeBuilder{
|
||||||
File: protoimpl.DescBuilder{
|
File: protoimpl.DescBuilder{
|
||||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||||
RawDescriptor: file_creds_accessbox_accessbox_proto_rawDesc,
|
RawDescriptor: file_accessbox_proto_rawDesc,
|
||||||
NumEnums: 0,
|
NumEnums: 0,
|
||||||
NumMessages: 3,
|
NumMessages: 4,
|
||||||
NumExtensions: 0,
|
NumExtensions: 0,
|
||||||
NumServices: 0,
|
NumServices: 0,
|
||||||
},
|
},
|
||||||
GoTypes: file_creds_accessbox_accessbox_proto_goTypes,
|
GoTypes: file_accessbox_proto_goTypes,
|
||||||
DependencyIndexes: file_creds_accessbox_accessbox_proto_depIdxs,
|
DependencyIndexes: file_accessbox_proto_depIdxs,
|
||||||
MessageInfos: file_creds_accessbox_accessbox_proto_msgTypes,
|
MessageInfos: file_accessbox_proto_msgTypes,
|
||||||
}.Build()
|
}.Build()
|
||||||
File_creds_accessbox_accessbox_proto = out.File
|
File_accessbox_proto = out.File
|
||||||
file_creds_accessbox_accessbox_proto_rawDesc = nil
|
file_accessbox_proto_rawDesc = nil
|
||||||
file_creds_accessbox_accessbox_proto_goTypes = nil
|
file_accessbox_proto_goTypes = nil
|
||||||
file_creds_accessbox_accessbox_proto_depIdxs = nil
|
file_accessbox_proto_depIdxs = nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,14 @@ message AccessBox {
|
||||||
bytes gatePublicKey = 2 [json_name = "gatePublicKey"];
|
bytes gatePublicKey = 2 [json_name = "gatePublicKey"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message ContainerPolicy {
|
||||||
|
string locationConstraint = 1;
|
||||||
|
bytes policy = 2;
|
||||||
|
}
|
||||||
|
|
||||||
bytes ownerPublicKey = 1 [json_name = "ownerPublicKey"];
|
bytes ownerPublicKey = 1 [json_name = "ownerPublicKey"];
|
||||||
repeated Gate gates = 2 [json_name = "gates"];
|
repeated Gate gates = 2 [json_name = "gates"];
|
||||||
|
repeated ContainerPolicy containerPolicy = 3 [json_name = "containerPolicy"];
|
||||||
}
|
}
|
||||||
|
|
||||||
message Tokens {
|
message Tokens {
|
||||||
|
|
|
@ -20,7 +20,7 @@ import (
|
||||||
type (
|
type (
|
||||||
// Credentials is a bearer token get/put interface.
|
// Credentials is a bearer token get/put interface.
|
||||||
Credentials 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)
|
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)
|
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) {
|
func (c *cred) getAccessBox(ctx context.Context, address *object.Address) (*accessbox.AccessBox, error) {
|
||||||
var (
|
var (
|
||||||
box accessbox.AccessBox
|
box accessbox.AccessBox
|
||||||
|
|
Loading…
Reference in a new issue