Skip to content

Commit

Permalink
feat: add authorization endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
arxeiss committed Jun 17, 2022
1 parent 14e770a commit dba80c7
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 1 deletion.
11 changes: 10 additions & 1 deletion errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ func FromError(err error) *StatusError {
return s
}
if s, ok := err.(*ClientError); ok {
return &StatusError{grpcStatus: status.New(s.code, s.msg)}
return &StatusError{grpcStatus: status.New(s.code, s.msg), origin: s.cause}
}
if se, ok := err.(interface {
GRPCStatus() *status.Status
Expand Down Expand Up @@ -172,6 +172,10 @@ func NewGRPCError(entryErr interface{}) *StatusError {
}

func (err *StatusError) Error() string {
if err.origin != nil {
return fmt.Sprintf(
"client error: code = %s desc = %s | caused by %s", err.Code(), err.Message(), err.origin.Error())
}
return fmt.Sprintf("client error: code = %s desc = %s", err.Code(), err.Message())
}

Expand Down Expand Up @@ -199,3 +203,8 @@ func (err *StatusError) Status() *status.Status {
func (err *StatusError) WithPrefix(prefix string) *StatusError {
return err
}

// Origin returns underlying error, if any
func (err *StatusError) Origin() error {
return err.origin
}
131 changes: 131 additions & 0 deletions identity/authorization.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright (c) 2022 IndyKite
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package identity

import (
"context"

"google.golang.org/grpc"

"github.com/indykite/jarvis-sdk-go/errors"
identitypb "github.com/indykite/jarvis-sdk-go/gen/indykite/identity/v1beta1"
objects "github.com/indykite/jarvis-sdk-go/gen/indykite/objects/v1beta1"
)

const (
externalIDProperty = "extid"
)

// IsAuthorized checks if DigitalTwin can perform actions on resources.
func (c *Client) IsAuthorized(
ctx context.Context,
digitalTwin *identitypb.DigitalTwin,
actions, resourceReferences []string,
opts ...grpc.CallOption,
) (*identitypb.IsAuthorizedResponse, error) {
return c.IsAuthorizedWithRawRequest(ctx, &identitypb.IsAuthorizedRequest{
Subject: &identitypb.DigitalTwinIdentifier{Filter: &identitypb.DigitalTwinIdentifier_DigitalTwin{
DigitalTwin: digitalTwin,
}},
Actions: actions,
ResourceReferences: resourceReferences,
}, opts...)
}

// IsAuthorizedByToken checks if DigitalTwin, identified by access token,
// can perform actions on resources.
func (c *Client) IsAuthorizedByToken(
ctx context.Context,
token string,
actions, resourceReferences []string,
opts ...grpc.CallOption,
) (*identitypb.IsAuthorizedResponse, error) {
return c.IsAuthorizedWithRawRequest(ctx, &identitypb.IsAuthorizedRequest{
Subject: &identitypb.DigitalTwinIdentifier{Filter: &identitypb.DigitalTwinIdentifier_AccessToken{
AccessToken: token,
}},
Actions: actions,
ResourceReferences: resourceReferences,
}, opts...)
}

// IsAuthorizedByStringExternalID checks if DigitalTwin, identified by textual ExternalID,
// can perform actions on resources.
func (c *Client) IsAuthorizedByStringExternalID(
ctx context.Context,
externalID string,
actions, resourceReferences []string,
opts ...grpc.CallOption,
) (*identitypb.IsAuthorizedResponse, error) {
return c.IsAuthorizedWithRawRequest(ctx, &identitypb.IsAuthorizedRequest{
Subject: &identitypb.DigitalTwinIdentifier{Filter: &identitypb.DigitalTwinIdentifier_Property{
Property: &identitypb.Property{
Definition: &identitypb.PropertyDefinition{Property: externalIDProperty},
Value: &identitypb.Property_ObjectValue{ObjectValue: objects.String(externalID)},
},
}},
Actions: actions,
ResourceReferences: resourceReferences,
}, opts...)
}

// IsAuthorizedByNumericExternalID checks if DigitalTwin, identified by numerical ExternalID,
// can perform actions on resources.
func (c *Client) IsAuthorizedByNumericExternalID(
ctx context.Context,
externalID int64,
actions, resourceReferences []string,
opts ...grpc.CallOption,
) (*identitypb.IsAuthorizedResponse, error) {
return c.IsAuthorizedWithRawRequest(ctx, &identitypb.IsAuthorizedRequest{
Subject: &identitypb.DigitalTwinIdentifier{Filter: &identitypb.DigitalTwinIdentifier_Property{
Property: &identitypb.Property{
Definition: &identitypb.PropertyDefinition{Property: externalIDProperty},
Value: &identitypb.Property_ObjectValue{ObjectValue: objects.Int64(externalID)},
},
}},
Actions: actions,
ResourceReferences: resourceReferences,
}, opts...)
}

func (c *Client) IsAuthorizedWithRawRequest(
ctx context.Context,
req *identitypb.IsAuthorizedRequest,
opts ...grpc.CallOption,
) (*identitypb.IsAuthorizedResponse, error) {
if err := req.Validate(); err != nil {
return nil, errors.NewInvalidArgumentErrorWithCause(err, "unable to call IsAuthorized client endpoint")
}

switch sub := req.Subject.Filter.(type) {
case *identitypb.DigitalTwinIdentifier_DigitalTwin:
if _, _, err := sub.DigitalTwin.Verify(); err != nil {
return nil, errors.NewInvalidArgumentError(err.Error())
}
case *identitypb.DigitalTwinIdentifier_AccessToken:
if err := verifyTokenFormat(sub.AccessToken); err != nil {
return nil, err
}
}

ctx = insertMetadata(ctx, c.xMetadata)
resp, err := c.client.IsAuthorized(ctx, req, opts...)

if s := errors.FromError(err); s != nil {
return nil, s
}
return resp, nil
}

0 comments on commit dba80c7

Please sign in to comment.