Skip to content

Commit

Permalink
add support for adding, removing and listing policies.
Browse files Browse the repository at this point in the history
This commit adds support for creating, describing,
deleting and removing policies.

Signed-off-by: Andreas Auernhammer <[email protected]>
  • Loading branch information
aead committed Dec 11, 2023
1 parent 056d16b commit a21f338
Show file tree
Hide file tree
Showing 11 changed files with 610 additions and 38 deletions.
179 changes: 177 additions & 2 deletions kms/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -362,8 +362,7 @@ func (c *Client) CreateEnclave(ctx context.Context, req *CreateEnclaveRequest) e
// DescribeEnclave returns metadata about the enclave with the
// the name req.Name.
//
// It returns ErrEnclaveNotFound if no such enclave exists and
// ErrKeyNotFound if no such key exists.
// It returns ErrEnclaveNotFound if no such enclave exists.
func (c *Client) DescribeEnclave(ctx context.Context, req *DescribeEnclaveRequest) (*DescribeEnclaveResponse, error) {
const (
Method = http.MethodGet
Expand Down Expand Up @@ -730,6 +729,7 @@ func (c *Client) ListKeyNames(ctx context.Context, req *ListRequest) (*ListRespo
return nil, err
}
r.Header.Set(headers.Accept, ContentType)
r.Header.Set(headers.Enclave, req.Enclave)

resp, err := c.client.Do(r)
if err != nil {
Expand Down Expand Up @@ -903,6 +903,181 @@ func (c *Client) GenerateKey(ctx context.Context, req *GenerateKeyRequest) (*Gen
return &data, nil
}

// CreatePolicy creates a new or overwrites an exisiting policy with the
// name req.Name within req.Enclave.
//
// It returns ErrEnclaveNotFound if no such enclave exists.
func (c *Client) CreatePolicy(ctx context.Context, req *CreatePolicyRequest) error {
const (
Method = http.MethodPut
Path = api.PathPolicyCreate
StatusOK = http.StatusOK
ContentType = headers.ContentTypeAppAny // accept JSON or protobuf
)

body, err := pb.Marshal(req)
if err != nil {
return err
}

url, err := c.lb.URL(Path, req.Name)
if err != nil {
return err
}
r, err := http.NewRequestWithContext(ctx, Method, url, bytes.NewReader(body))
if err != nil {
return err
}
r.Header.Set(headers.Accept, ContentType)
r.Header.Set(headers.Enclave, req.Enclave)

resp, err := c.client.Do(r)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != StatusOK {
return readError(resp)
}
return nil
}

// DescribePolicy returns metadata about the policy req.Name within
// the req.Enclave.
//
// It returns ErrEnclaveNotFound if no such enclave exists and
// ErrPolicyNotFound if no such policy exists.
func (c *Client) DescribePolicy(ctx context.Context, req *DescribePolicyRequest) (*DescribePolicyResponse, error) {
const (
Method = http.MethodGet
Path = api.PathPolicyDescribe
StatusOK = http.StatusOK
ContentType = headers.ContentTypeAppAny // accept JSON or protobuf
)

url, err := c.lb.URL(Path, req.Name)
if err != nil {
return nil, err
}
r, err := http.NewRequestWithContext(ctx, Method, url, nil)
if err != nil {
return nil, err
}
r.Header.Set(headers.Accept, ContentType)
r.Header.Set(headers.Enclave, req.Enclave)

resp, err := c.client.Do(r)
if err != nil {
return nil, err
}
defer resp.Body.Close()

if resp.StatusCode != StatusOK {
return nil, readError(resp)
}

var data DescribePolicyResponse
if err := readResponse(resp, &data); err != nil {
return nil, err
}
return &data, nil
}

// DeletePolicy deletes the policy with the name req.Name within req.Enclave.
//
// It returns ErrEnclaveNotFound if no such enclave exists and ErrPolicyNotFound
// if such policy exists.
func (c *Client) DeletePolicy(ctx context.Context, req *DeletePolicyRequest) error {
const (
Method = http.MethodDelete
Path = api.PathPolicyDelete
StatusOK = http.StatusOK
ContentType = headers.ContentTypeAppAny // accept JSON or protobuf
)

url, err := c.lb.URL(Path, req.Name)
if err != nil {
return err
}
r, err := http.NewRequestWithContext(ctx, Method, url, nil)
if err != nil {
return err
}
r.Header.Set(headers.Accept, ContentType)
r.Header.Set(headers.Enclave, req.Enclave)

resp, err := c.client.Do(r)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != StatusOK {
return readError(resp)
}
return nil
}

// ListPolicyNames returns a list of policy names within the req.Enclave.
// The list starts at the given req.Prefix and req.ContinueAt and contains
// at most req.Limit names.
//
// ListEnclaveNames implements paginated listing. For iterating over a stream
// of policy names combine it with an Iter.
func (c *Client) ListPolicyNames(ctx context.Context, req *ListRequest) (*ListResponse[string], error) {
const (
Method = http.MethodGet
Path = api.PathPolicyList
StatusOK = http.StatusOK
ContentType = headers.ContentTypeAppAny // accept JSON or protobuf

QueryContinue = api.QueryListContinue
QueryLimit = api.QueryListLimit
)

query := url.Values{}
if req.ContinueAt != "" {
query[QueryContinue] = []string{req.ContinueAt}
}
if req.Limit > 0 {
query[QueryLimit] = []string{strconv.Itoa(req.Limit)}
}

url, err := c.lb.URL(Path, req.Prefix)
if err != nil {
return nil, err
}
if len(query) > 0 {
url += "?" + query.Encode()
}
r, err := http.NewRequestWithContext(ctx, Method, url, nil)
if err != nil {
return nil, err
}
r.Header.Set(headers.Accept, ContentType)
r.Header.Set(headers.Enclave, req.Enclave)

resp, err := c.client.Do(r)
if err != nil {
return nil, err
}
defer resp.Body.Close()

if resp.StatusCode != StatusOK {
return nil, readError(resp)
}

var data pb.ListPolicyNamesResponse
if err = readProtoResponse(resp, &data); err != nil {
return nil, err
}
return &ListResponse[string]{
Items: data.Names,
ContinueAt: data.ContinueAt,
}, nil
}

// httpsURL turns the endpoint into an HTTPS endpoint.
func httpsURL(endpoint string) string {
endpoint = strings.TrimPrefix(endpoint, "http://")
Expand Down
10 changes: 10 additions & 0 deletions kms/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ import (

// MinIO KMS API errors.
var (
// ErrPermission is returned when the client has insufficient permissions
// for performing the tried operation. For example, policy associated to the
// client's identity may not allow or even deny the request or the client
// may try to perform an admin operation without admin permissions.
ErrPermission = Error{http.StatusForbidden, "access denied: insufficient permissions"}

// ErrEnclaveExists is returned when trying to create an enclave
// that already exists.
ErrEnclaveExists = Error{http.StatusConflict, "enclave already exists"}
Expand All @@ -35,6 +41,10 @@ var (
// ErrKeyNotFound is returned when trying to use a key that
// that does not exist.
ErrKeyNotFound = Error{http.StatusNotFound, "key does not exist"}

// ErrPolicyNotFound is returned when trying to fetch or delete a policy
// that does not exist.
ErrPolicyNotFound = Error{http.StatusNotFound, "policy does not exist"}
)

// Error is a KMS API error.
Expand Down
5 changes: 5 additions & 0 deletions kms/internal/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ const (
PathSecretKeyEncrypt = "/v1/key/encrypt/"
PathSecretKeyDecrypt = "/v1/key/decrypt/"

PathPolicyCreate = "/v1/policy/create/"
PathPolicyDescribe = "/v1/policy/describe/"
PathPolicyDelete = "/v1/policy/delete/"
PathPolicyList = "/v1/policy/list/"

PathClusterStatus = "/v1/cluster/status"
PathClusterAdd = "/v1/cluster/add/"
PathClusterRemove = "/v1/cluster/remove/"
Expand Down
3 changes: 3 additions & 0 deletions kms/iter.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type Iter[T any] struct {
NextFn func(context.Context, *ListRequest) (*ListResponse[T], error)

items []T
enclave string
prefix string
continueAt string
limit int
Expand All @@ -31,6 +32,7 @@ func (i *Iter[T]) SeekTo(ctx context.Context, req *ListRequest) (item T, err err
return item, i.err
}

i.enclave = req.Enclave
i.prefix = req.Prefix
i.continueAt = req.ContinueAt
i.limit = req.Limit
Expand All @@ -49,6 +51,7 @@ func (i *Iter[T]) Next(ctx context.Context) (item T, err error) {
}

resp, err := i.NextFn(ctx, &ListRequest{
Enclave: i.enclave,
Prefix: i.prefix,
ContinueAt: i.continueAt,
Limit: i.limit,
Expand Down
Loading

0 comments on commit a21f338

Please sign in to comment.