-
Notifications
You must be signed in to change notification settings - Fork 113
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
[A2-835] make custom endpoints projects aware for iam v2.1 #380
[A2-835] make custom endpoints projects aware for iam v2.1 #380
Conversation
2d08d13
to
a2a442f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking like Handle
and IsAuthorized
are getting pretty redundant and I'm wondering if we still need both? I'd love a few function comments for both of those two explaining when they are used and how they are different 😅
Subjects: subjects, | ||
Resource: resource, | ||
Action: action, | ||
ProjectsFilter: projectsToFilter, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking similar to the Handle
function. When do we hit Handle
vs IsAuthorized
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it? Handle
is to be used in a GRPC interceptor, and depends on
a) the grpc method invoked, and
b) the policy info being mapped in our proto files
i.e. this half of the method, neither of which exist for custom handlers.
I'll outline how the authorizer is supposed to work in another comment, give me a minute 😉
ℹ️ That's the authorizer machinery in a nutshell:
|
Everything would be nice and tidy if there was only GRPC (and if we rearranged how introspection works, maybe?). 😉 Also: please note that I'm not particularly attached to how this currently works (I entirely blame my lack of imagination and not my tools) -- if anyone comes up with some better means to reach the same end, I'm all ears. |
@@ -42,7 +41,7 @@ type SwitchingAuthorizationHandler interface { | |||
GRPCAuthorizationHandler | |||
SwitchingFilterHandler | |||
IsAuthorized(ctx context.Context, subjects []string, | |||
resourceV1, actionV1, resourceV2, actionV2 string) (AnnotatedAuthorizationResponse, error) | |||
resourceV1, actionV1, resourceV2, actionV2 string, projects []string) (AnnotatedAuthorizationResponse, error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suppose this is the "last param" but oof we're passing around a lot of args at this point!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah -- the root cause is that this is used for authorizing requests in the custom endpoints' helper function -- and there's no way they can retrieve the v1 or v2 mappings (no grpc/proto files involved). So, it'll have to be presented up front.
@@ -182,7 +182,7 @@ func getProjectsFromMetadata(projectHeaderEntries []string) []string { | |||
for _, entry := range projectHeaderEntries { | |||
for _, project := range strings.Split(entry, ",") { | |||
newProject := strings.TrimSpace(project) | |||
if _, value := keys[newProject]; !value { | |||
if !keys[newProject] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sort of surprised this worked-- I would expect that if the value of keys[newproject]
was nil, this would fail or that we'd get "invalid operation".
Don't we need to both check the value and the success of extracting it (if val, ok := keys[newProject]; ok {
)?
Maybe I'm missing something 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
keys[newProject]
, if not present, will return the zero-value for the type, in this case false
. So this statement is still asserting "if there is no newProject entry in keys then..."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@msorens I know that it reads that way, but I was under the impression that golang didn't necessarily behave how we'd expect here. I've tried this in a playground and I keep getting the error I'd expect (invalid operation)-- would you mind linking me to an example playground?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://play.golang.org/p/y1GXa4D4vdv does that do the trick? Go really isn't dynamic at all, but non-existing values in maps will return their zero value, as @msorens suggested. Here's the spec section:
For a of map type M:
- x's type must be assignable to the key type of M
- if the map contains an entry with key x, a[x] is the map element with key x and the type of a[x] is the element type of M
- if the map is nil or does not contain such an entry, a[x] is the zero value for the element type of M
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ugh thank you for the example-- I'd read the docs but was having trouble manually testing . . . I had my map constructed incorrectly (the value as a string, not a bool) 🙄
return nil, err | ||
} | ||
log.WithError(err).Error("error authorizing request") | ||
return nil, status.Errorf(codes.PermissionDenied, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this message/error also include projects?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As this is the gatekeeper for both v2 and v2.1, and it does not know which for any given call, we cannot appropriately include projects.
if status.Convert(err).Code() == codes.FailedPrecondition { | ||
return nil, err | ||
} | ||
log.WithError(err).Error("error authorizing request") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious about the choice to log here-- can we not guarantee that the error itself will surface in the logs?
If that's the case, I imagine it would be helpful to have the subj/resource/etc in the log message as well as the error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😄 a) I don't think I've spent much time deliberating the choice of log here.
b) ANY GRPC response that has a status !=OK
will be logged by the gateway. So, we should end up having most of the stuff in the logs because of the status message below.
Good enough? 😉
@@ -662,10 +665,21 @@ func (s *Server) authRequest(r *http.Request, | |||
return authzResp.Err() | |||
} | |||
|
|||
func logError(err error, resource, action string) { | |||
log.WithFields(log.Fields{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did we move away from WithFields
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, just realised that the function hadn't been used 🤷♂
}).Error("error authorizing request") | ||
// XXX needs headers, doesn't it? | ||
func getProjectsFromMetadata(projectHeaderEntries []string) []string { | ||
if projectHeaderEntries == nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 but also 😢
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry that one should have been deleted -- I've done the refactor here, after all. 😄
Signed-off-by: Stephan Renatus <[email protected]>
Signed-off-by: Stephan Renatus <[email protected]>
…text method Signed-off-by: Stephan Renatus <[email protected]>
There's no need to translate incoming MD to outgoing MD. Outgoing metadata is only required if another grpc CLIENT call uses that context, and the key-val pairs are supposed to be sent along. Also fixes the comment. Signed-off-by: Stephan Renatus <[email protected]>
a2a442f
to
44e870e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tested with audit cookbook and inspec -> automate direct. all working as expected! 😃
🔩 Description
When we've introduced projects getting passed along from authz to the domain services, we've done that for the general case (read: GRPC). This PR extends that treatment to the special case, our hand-written HTTP endpoints defined in
automate-gateway
.Caveats
✔️
There's some TODO in the code, and a small refactoring I'd like to wrap into this. I'll tackle that next week.👍 Definition of Done
🚀 The custom endpoints are projects-aware (if the domain-services (downstream) care about the projects).
👟 Demo Script / Repro Steps
rebuild components/automate-gateway
chef-automate iam upgrade-to-v2 --beta2.1
For testing,
rebuilt components/compliance-service
, and$TOK
is an admin token)Note:
FailedPrecondition
denotes the moment where the "authorizer" package in automate-gateway realises that sending a v1 authz query is wrong (authz-service will return FailedPreconditon), and switches itself to using v2.⛓️ Related Resources
✅ Checklist