Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API Refactor: Implement Entry.GetAuthorizedEntries RPC #1647

Merged

Conversation

amartinezfayo
Copy link
Member

Implementation of Entry.GetAuthorizedEntries RPC

Fixes: #1603

Signed-off-by: Agustín Martínez Fayó <amartinezfayo@gmail.com>
)

type entryFetcher struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you call it fakeEntry fetcher and move together with methods?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe put it at the end of file (after all tests

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll move this to a fakeentryfetcher package

ctx := context.Background()

entry1 := types.Entry{
Id: "entry-1",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe for this tests we can provide entries with less information, we are not doing any parsing that requires this level of information

)

type entryFetcher struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you move it to end of test file? and looks like it is the same we use on fetcher tests, may we expose it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll move this to a fakeentryfetcher package

expectEntries: []*types.Entry{&entry1, &entry2},
},
{
name: "success with output mask",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can add a test case with mask all false?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and another without results?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another test where caller id is not provided?

@@ -623,11 +623,14 @@ func TestServiceBatchNewX509SVID(t *testing.T) {
name: "no caller id",
reqs: []string{workloadEntry.Id},
code: codes.Internal,
err: "caller ID missing from request context",
err: "failed to fetch registration entries: no caller ID on context",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe: failed to fetch registration entries: missing caller ID?


// FetchAuthEntries fetches authorized entries using caller ID from context
func FetchAuthEntries(ctx context.Context, log logrus.FieldLogger, ef AuthorizedEntryFetcher) (map[string]*types.Entry, error) {
entries, err := ef.FetchAuthorizedEntries(ctx)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to put callerID validation in some place, is the idea to move it into ef.FetchAuthorizedEntries(ctx)? if that is the case can you add comments to that interface so, when we we implement it we didnt miss that validation?
annd I think we must validate how our code works when that validation fails.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current implementation gets the caller ID in the fetchEntries function and sends the caller ID as an argument to the FetchAuthorizedEntries function along with the context, which seems to be redundant, since the FetchAuthorizedEntries function can get the caller ID from the context and should return the authorized entries based on that caller ID (current code calls rpccontext.CallerID(ctx) twice).
I was thinking that it should be documented that FetchAuthorizedEntries should do the proper caller ID validation. I'll add comments to have this documented.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion, use of rpccontext should stay at the API layer. I'd rather the dependencies receive their arguments explicitly.

Signed-off-by: Agustín Martínez Fayó <amartinezfayo@gmail.com>
Signed-off-by: Agustín Martínez Fayó <amartinezfayo@gmail.com>
Signed-off-by: Agustín Martínez Fayó <amartinezfayo@gmail.com>

// FetchAuthEntries fetches authorized entries using caller ID from context
func FetchAuthEntries(ctx context.Context, log logrus.FieldLogger, ef AuthorizedEntryFetcher) (map[string]*types.Entry, error) {
entries, err := ef.FetchAuthorizedEntries(ctx)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion, use of rpccontext should stay at the API layer. I'd rather the dependencies receive their arguments explicitly.

@@ -51,6 +59,87 @@ func TestIDFromProto(t *testing.T) {
}
}

func TestFetchAuthEntries(t *testing.T) {
ef := &fakeentryfetcher.EntryFetcher{}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this can just be initialized down inside the loop? Then it's clear that we're starting from a clean slate instead of having to reset state every time (e.g. the err)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm removing the FetchAuthEntries public function from the api package.

// The provided implementation of that interface must ensure that
// the authorized entries are fetched from the caller that
// is obtained from the context.
func FetchAuthEntries(ctx context.Context, log logrus.FieldLogger, ef AuthorizedEntryFetcher) (map[string]*types.Entry, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand wanting to DRY this up, but I think we should wait. Both places that use this (so far) have different needs. One wants to build a map, the other doesn't. And outside of building the map, there isn't much to share here (since the AuthorizedEntryFetcher does all the work).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was debating myself whether this should be here or in the specific packages. It's clear for me now that two different implementations are needed.

@@ -26,6 +30,33 @@ func ProtoFromID(id spiffeid.ID) *types.SPIFFEID {
}
}

// AuthorizedEntryFetcher is the interface to fetch authorized entries
type AuthorizedEntryFetcher interface {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for moving this here :)

Signed-off-by: Agustín Martínez Fayó <amartinezfayo@gmail.com>
Copy link
Member

@azdagron azdagron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great :) I just have a couple small nits

Comment on lines 250 to 251
var entriesWithMask []*types.Entry
for _, entry := range entries {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we reuse the entries slice?

for i, entry := range entries {
    applyMask(entry, req.OutputMask)
    entries[i] = entry
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤦‍♂️ sure!

return nil, status.Error(codes.Internal, "failed to fetch registration entries")
}

entriesMap := make(map[string]*types.Entry)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super nit:Since the length is known, it may be worth pre-allocating map capacity to reduce allocation pressure? (the RPC that uses this is going to be 3rd on the most frequently list)

Suggested change
entriesMap := make(map[string]*types.Entry)
entriesMap := make(map[string]*types.Entry, len(entries))

Copy link
Member

@azdagron azdagron left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

\o/

@azdagron azdagron merged commit d0d080c into spiffe:master Jun 16, 2020
@amartinezfayo amartinezfayo deleted the api-refactor-entry-getauthorizedentries branch July 28, 2020 17:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

API Refactor: Implement Entry.GetAuthorizedEntries RPC
3 participants