Skip to content

Commit

Permalink
feat(example): implement standard example methods
Browse files Browse the repository at this point in the history
More tests to be added in a follow-up change.
  • Loading branch information
odsod committed May 3, 2021
1 parent d708a93 commit 9c63fe0
Show file tree
Hide file tree
Showing 47 changed files with 3,499 additions and 814 deletions.
149 changes: 124 additions & 25 deletions iamexample/authorization.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package iamexample
import (
"context"

"go.einride.tech/iam/iamspanner"
iamexamplev1 "go.einride.tech/iam/proto/gen/einride/iam/example/v1"
"google.golang.org/genproto/googleapis/iam/v1"
"google.golang.org/grpc/codes"
Expand All @@ -11,6 +12,7 @@ import (

type Authorization struct {
Next iamexamplev1.FreightServiceServer
IAM *iamspanner.Server
}

var _ iamexamplev1.FreightServiceServer = &Authorization{}
Expand All @@ -20,7 +22,7 @@ func (a *Authorization) GetShipper(
request *iamexamplev1.GetShipperRequest,
) (*iamexamplev1.Shipper, error) {
const permission = "freight.shippers.get"
if err := a.test(ctx, permission, request.GetName()); err != nil {
if err := a.require(ctx, permission, request.GetName()); err != nil {
return nil, err
}
return a.Next.GetShipper(ctx, request)
Expand All @@ -31,7 +33,7 @@ func (a *Authorization) ListShippers(
request *iamexamplev1.ListShippersRequest,
) (*iamexamplev1.ListShippersResponse, error) {
const permission = "freight.shippers.list"
if err := a.test(ctx, permission, "*"); err != nil {
if err := a.require(ctx, permission, "*"); err != nil {
return nil, err
}
return a.Next.ListShippers(ctx, request)
Expand All @@ -42,7 +44,7 @@ func (a *Authorization) CreateShipper(
request *iamexamplev1.CreateShipperRequest,
) (*iamexamplev1.Shipper, error) {
const permission = "freight.shippers.create"
if err := a.test(ctx, permission, "*"); err != nil {
if err := a.require(ctx, permission, "*"); err != nil {
return nil, err
}
return a.Next.CreateShipper(ctx, request)
Expand All @@ -53,7 +55,7 @@ func (a *Authorization) UpdateShipper(
request *iamexamplev1.UpdateShipperRequest,
) (*iamexamplev1.Shipper, error) {
const permission = "freight.shippers.update"
if err := a.test(ctx, permission, request.GetShipper().GetName()); err != nil {
if err := a.require(ctx, permission, request.GetShipper().GetName()); err != nil {
return nil, err
}
return a.Next.UpdateShipper(ctx, request)
Expand All @@ -64,7 +66,7 @@ func (a *Authorization) DeleteShipper(
request *iamexamplev1.DeleteShipperRequest,
) (*iamexamplev1.Shipper, error) {
const permission = "freight.shippers.delete"
if err := a.test(ctx, permission, request.GetName()); err != nil {
if err := a.require(ctx, permission, request.GetName()); err != nil {
return nil, err
}
return a.Next.DeleteShipper(ctx, request)
Expand All @@ -75,7 +77,7 @@ func (a *Authorization) GetSite(
request *iamexamplev1.GetSiteRequest,
) (*iamexamplev1.Site, error) {
const permission = "freight.sites.get"
if err := a.test(ctx, permission, request.GetName()); err != nil {
if err := a.require(ctx, permission, request.GetName()); err != nil {
return nil, err
}
return a.Next.GetSite(ctx, request)
Expand All @@ -86,7 +88,7 @@ func (a *Authorization) ListSites(
request *iamexamplev1.ListSitesRequest,
) (*iamexamplev1.ListSitesResponse, error) {
const permission = "freight.sites.list"
if err := a.test(ctx, permission, request.GetParent()); err != nil {
if err := a.require(ctx, permission, request.GetParent()); err != nil {
return nil, err
}
return a.Next.ListSites(ctx, request)
Expand All @@ -97,7 +99,7 @@ func (a *Authorization) CreateSite(
request *iamexamplev1.CreateSiteRequest,
) (*iamexamplev1.Site, error) {
const permission = "freight.sites.create"
if err := a.test(ctx, permission, request.GetParent()); err != nil {
if err := a.require(ctx, permission, request.GetParent()); err != nil {
return nil, err
}
return a.Next.CreateSite(ctx, request)
Expand All @@ -108,7 +110,7 @@ func (a *Authorization) UpdateSite(
request *iamexamplev1.UpdateSiteRequest,
) (*iamexamplev1.Site, error) {
const permission = "freight.sites.update"
if err := a.test(ctx, permission, request.GetSite().GetName()); err != nil {
if err := a.require(ctx, permission, request.GetSite().GetName()); err != nil {
return nil, err
}
return a.Next.UpdateSite(ctx, request)
Expand All @@ -119,7 +121,7 @@ func (a *Authorization) DeleteSite(
request *iamexamplev1.DeleteSiteRequest,
) (*iamexamplev1.Site, error) {
const permission = "freight.sites.delete"
if err := a.test(ctx, permission, request.GetName()); err != nil {
if err := a.require(ctx, permission, request.GetName()); err != nil {
return nil, err
}
return a.Next.DeleteSite(ctx, request)
Expand All @@ -129,22 +131,52 @@ func (a *Authorization) BatchGetSites(
ctx context.Context,
request *iamexamplev1.BatchGetSitesRequest,
) (*iamexamplev1.BatchGetSitesResponse, error) {
return nil, status.Error(codes.Unimplemented, "TODO: implement me")
const permission = "freight.sites.get"
if request.Parent != "" {
if ok, err := a.test(ctx, permission, request.Parent); err != nil {
return nil, err
} else if ok {
return a.Next.BatchGetSites(ctx, request)
}
}
if err := a.requireAll(ctx, permission, request.Names); err != nil {
return nil, err
}
return a.Next.BatchGetSites(ctx, request)
}

func (a *Authorization) SearchSites(
ctx context.Context,
request *iamexamplev1.SearchSitesRequest,
) (*iamexamplev1.SearchSitesResponse, error) {
return nil, status.Error(codes.Unimplemented, "TODO: implement me")
const permission = "freight.sites.get"
if request.Parent != "" {
if ok, err := a.test(ctx, permission, request.Parent); err != nil {
return nil, err
} else if ok {
return a.Next.SearchSites(ctx, request)
}
}
response, err := a.Next.SearchSites(ctx, request)
if err != nil {
return nil, err
}
names := make([]string, 0, len(response.Sites))
for _, site := range response.Sites {
names = append(names, site.Name)
}
if err := a.requireAll(ctx, permission, names); err != nil {
return nil, err
}
return response, nil
}

func (a *Authorization) GetShipment(
ctx context.Context,
request *iamexamplev1.GetShipmentRequest,
) (*iamexamplev1.Shipment, error) {
const permission = "freight.shipments.get"
if err := a.test(ctx, permission, request.GetName()); err != nil {
if err := a.require(ctx, permission, request.GetName()); err != nil {
return nil, err
}
return a.Next.GetShipment(ctx, request)
Expand All @@ -155,7 +187,7 @@ func (a *Authorization) ListShipments(
request *iamexamplev1.ListShipmentsRequest,
) (*iamexamplev1.ListShipmentsResponse, error) {
const permission = "freight.shipments.list"
if err := a.test(ctx, permission, request.GetParent()); err != nil {
if err := a.require(ctx, permission, request.GetParent()); err != nil {
return nil, err
}
return a.Next.ListShipments(ctx, request)
Expand All @@ -166,7 +198,7 @@ func (a *Authorization) CreateShipment(
request *iamexamplev1.CreateShipmentRequest,
) (*iamexamplev1.Shipment, error) {
const permission = "freight.shipments.create"
if err := a.test(ctx, permission, request.GetParent()); err != nil {
if err := a.require(ctx, permission, request.GetParent()); err != nil {
return nil, err
}
return a.Next.CreateShipment(ctx, request)
Expand All @@ -177,7 +209,20 @@ func (a *Authorization) UpdateShipment(
request *iamexamplev1.UpdateShipmentRequest,
) (*iamexamplev1.Shipment, error) {
const permission = "freight.shipments.update"
if err := a.test(ctx, permission, request.GetShipment().GetName()); err != nil {
ok, err := a.test(ctx, permission, request.GetShipment().GetName())
if err != nil {
return nil, err
}
if ok {
return a.Next.UpdateShipment(ctx, request)
}
shipment, err := a.GetShipment(ctx, &iamexamplev1.GetShipmentRequest{
Name: request.GetShipment().GetName(),
})
if err != nil {
return nil, err
}
if err := a.requireAny(ctx, permission, []string{shipment.OriginSite, shipment.DestinationSite}); err != nil {
return nil, err
}
return a.Next.UpdateShipment(ctx, request)
Expand All @@ -188,7 +233,7 @@ func (a *Authorization) DeleteShipment(
request *iamexamplev1.DeleteShipmentRequest,
) (*iamexamplev1.Shipment, error) {
const permission = "freight.shipments.delete"
if err := a.test(ctx, permission, request.GetName()); err != nil {
if err := a.require(ctx, permission, request.GetName()); err != nil {
return nil, err
}
return a.Next.DeleteShipment(ctx, request)
Expand All @@ -198,7 +243,32 @@ func (a *Authorization) BatchGetShipments(
ctx context.Context,
request *iamexamplev1.BatchGetShipmentsRequest,
) (*iamexamplev1.BatchGetShipmentsResponse, error) {
return nil, status.Error(codes.Unimplemented, "TODO: implement me")
const permission = "freight.shipments.get"
if request.Parent != "" {
if ok, err := a.test(ctx, permission, request.Parent); err != nil {
return nil, err
} else if ok {
return a.Next.BatchGetShipments(ctx, request)
}
}
response, err := a.Next.BatchGetShipments(ctx, request)
if err != nil {
return nil, err
}
resources := make([]string, 0, 3*len(response.Shipments))
for _, shipment := range response.Shipments {
resources = append(resources, shipment.Name, shipment.OriginSite, shipment.DestinationSite)
}
results, err := a.IAM.TestPermissionOnResources(ctx, permission, resources)
if err != nil {
return nil, err
}
for _, shipment := range response.Shipments {
if !(results[shipment.Name] || results[shipment.OriginSite] || results[shipment.DestinationSite]) {
return nil, status.Errorf(codes.PermissionDenied, "missing permission %s for %s", permission, shipment.Name)
}
}
return response, nil
}

func (a *Authorization) SetIamPolicy(
Expand All @@ -222,16 +292,45 @@ func (a *Authorization) TestIamPermissions(
return a.Next.TestIamPermissions(ctx, request)
}

func (a *Authorization) test(ctx context.Context, permission, resource string) error {
response, err := a.Next.TestIamPermissions(ctx, &iam.TestIamPermissionsRequest{
Resource: resource,
Permissions: []string{permission},
})
func (a *Authorization) require(ctx context.Context, permission, resource string) error {
if ok, err := a.test(ctx, permission, resource); err != nil {
return err
} else if !ok {
return status.Errorf(codes.PermissionDenied, "caller must have permission `%s`", permission)
}
return nil
}

func (a *Authorization) test(ctx context.Context, permission, resource string) (bool, error) {
return a.IAM.TestPermissionOnResource(ctx, permission, resource)
}

func (a *Authorization) testAll(ctx context.Context, permission string, resources []string) (bool, error) {
results, err := a.IAM.TestPermissionOnResources(ctx, permission, resources)
if err != nil {
return false, err
}
all := true
for _, resource := range resources {
all = all && results[resource]
}
return all, nil
}

func (a *Authorization) requireAll(ctx context.Context, permission string, resources []string) error {
if ok, err := a.testAll(ctx, permission, resources); err != nil {
return err
} else if !ok {
return status.Errorf(codes.PermissionDenied, "caller must have permission `%s` on all resources", permission)
}
if len(response.Permissions) != 1 || response.Permissions[0] != permission {
return status.Errorf(codes.PermissionDenied, "caller must have permission `%s`", permission)
return nil
}

func (a *Authorization) requireAny(ctx context.Context, permission string, resources []string) error {
if ok, err := a.testAll(ctx, permission, resources); err != nil {
return err
} else if !ok {
return status.Errorf(codes.PermissionDenied, "caller must have permission `%s` on any of the resources", permission)
}
return nil
}
Loading

0 comments on commit 9c63fe0

Please sign in to comment.