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

DEVPROD-5753: Implement and apply requireHostAccess GQL directive #8337

Open
wants to merge 18 commits into
base: main
Choose a base branch
from

Conversation

SupaJoon
Copy link
Contributor

@SupaJoon SupaJoon commented Sep 23, 2024

DEVPROD-5753

Description

The actual added code to review is less than 540 lines.
These code changes implement and apply the @requireHostAccess directive to these mutations:

  • updateSpawnHostStatus
  • editSpawnHost
  • reprovisionToNew
  • restartJasper
  • updateHostStatus

and these queries:

  • host
  • hostEvents

I also applied the directive permission logic to the hosts query resolver instead of the schema since there is are no host IDs available before the host db query takes place.

These code changes combine the permission for editing spawn hosts and non-spawn hosts. Currently in Evergreen, the difference between the two implementations is that spawn hosts permission is granted if the host startedBy field is the same as the requesting user and the user has the host edit permission (code) whereas modifying a non-spawn host only checks the edit permission (code). I confirmed in staging that I am able to modify my spawn host with the current code changes and I could use edge case clarification from an App engineer.

e2e test is fixed in this UI PR: evergreen-ci/ui#410

@SupaJoon SupaJoon requested review from a team September 24, 2024 13:59
graphql/query_resolver.go Outdated Show resolved Hide resolved
Comment on lines 342 to 349
forbiddenHosts := []string{}
for _, h := range hosts {
if !userHasHostPermission(usr, h.Distro.Id, evergreen.HostsView.Value) {
forbiddenHosts = append(forbiddenHosts, h.Id)
}
}
if len(forbiddenHosts) > 0 {
return nil, Forbidden.Send(ctx, fmt.Sprintf("user '%s' does not have permission to access one or more hosts.", usr.Username()))
Copy link
Contributor

Choose a reason for hiding this comment

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

since we don't really do anything with the forbiddenHosts, I wonder if it makes sense to do this check in the same loop where we're building the APIHosts below (and just return immediately if you encounter no permissions)

Copy link
Contributor

Choose a reason for hiding this comment

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

You can either do that or include a formatted list of all the forbidden hosts in the error message here

Copy link
Contributor Author

@SupaJoon SupaJoon Sep 27, 2024

Choose a reason for hiding this comment

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

I originally considered returning a list of hosts that are inaccessible but I think that is insecure since the query parameters match the hostIds in the error message which makes the error message really useful to someone who doesn't have host permissions

graphql/resolver.go Outdated Show resolved Hide resolved
return nil, ResourceNotFound.Send(ctx, "host not specified")
}
hostId, hasHostId := args["hostId"].(string)
hostIdsInterface, hasHostIds := args["hostIds"].([]interface{})
Copy link
Contributor

Choose a reason for hiding this comment

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

does it make sense to do this so you can skip the extra loop for hostIds 👀

Suggested change
hostIdsInterface, hasHostIds := args["hostIds"].([]interface{})
hostIds, hasHostIds := args["hostIds"].([]string)

@@ -218,7 +234,87 @@ func setupPermissions(t *testing.T) {
}
require.NoError(t, roleManager.UpdateRole(logViewRole))
}

func TestRequireHostAccess(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The references to .Equal or .EqualError in this file should use assert rather than require for codebase style consistency.

Comment on lines 342 to 349
forbiddenHosts := []string{}
for _, h := range hosts {
if !userHasHostPermission(usr, h.Distro.Id, evergreen.HostsView.Value) {
forbiddenHosts = append(forbiddenHosts, h.Id)
}
}
if len(forbiddenHosts) > 0 {
return nil, Forbidden.Send(ctx, fmt.Sprintf("user '%s' does not have permission to access one or more hosts.", usr.Username()))
Copy link
Contributor

Choose a reason for hiding this comment

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

You can either do that or include a formatted list of all the forbidden hosts in the error message here

if !hasHostId && !hasHostIds {
return nil, ResourceNotFound.Send(ctx, "host not specified")
}
hostIdsToCheck := []string{}
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: initialize as []string{hostId} and remove L56

var requiredLevel int
if access == HostAccessLevelEdit {
requiredLevel = evergreen.HostsEdit.Value
} else if access == HostAccessLevelView {
Copy link
Contributor

Choose a reason for hiding this comment

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

is access not being specified a valid case? If not I would use else rather than else if

return nil, ResourceNotFound.Send(ctx, "No matching hosts found")
}
for _, h := range hostsToCheck {
if !userHasHostPermission(user, h.Distro.Id, requiredLevel) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be consistent with the logic here (as in whether we keep track of a list of forbidden hosts or not)

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.

3 participants