Repository: hashicorp/nomad
File: command/agent/agent_endpoint.go (Go)

Code before:
	var args api.AgentSchedulerWorkerConfigRequest

	if err := decodeBody(req, &args); err != nil {
		return nil, CodedError(http.StatusBadRequest, fmt.Sprintf("Invalid request: %s", err.Error()))
	}
	// the server_id provided in the payload is ignored to allow the
	// response to be roundtripped right into a PUT.
	newArgs := nomad.SchedulerWorkerPoolArgs{
		NumSchedulers:     args.NumSchedulers,
		EnabledSchedulers: args.EnabledSchedulers,
	}
	if newArgs.IsInvalid() {
		return nil, CodedError(http.StatusBadRequest, "Invalid request")
	}
	reply := srv.SetSchedulerWorkerConfig(newArgs)

	response := &api.AgentSchedulerWorkerConfigResponse{
		ServerID:          srv.LocalMember().Name,
		NumSchedulers:     reply.NumSchedulers,
		EnabledSchedulers: reply.EnabledSchedulers,
	}

	return response, nil
}

func checkPermissions(srv *nomad.Server, secret string, perm func(acl *acl.ACL) bool) error {
	r := &structs.GenericRequest{}
	r.AuthToken = secret
	if authErr := srv.Authenticate(nil, r); authErr != nil {
		return CodedError(http.StatusInternalServerError, authErr.Error())
	}

	aclObj, err := srv.ResolveACL(r)
	if err != nil {
		return CodedError(http.StatusInternalServerError, err.Error())
	} else if !perm(aclObj) {
		return CodedError(http.StatusForbidden, structs.ErrPermissionDenied.Error())
	}

	return nil
}


Review comment (security):
I almost want this to be a method on `*Server` in that `nomad/acl.go` file, but it really doesn't feel like it fits there and could be misused. Should we at least name it and/or add a docstring to make its intent clear? Particularly because this is token-only authentication and can't be used with, say, only the mTLS cert the way a server-to-server RPC could be.

Code after:
	var args api.AgentSchedulerWorkerConfigRequest

	if err := decodeBody(req, &args); err != nil {
		return nil, CodedError(http.StatusBadRequest, fmt.Sprintf("Invalid request: %s", err.Error()))
	}
	// the server_id provided in the payload is ignored to allow the
	// response to be roundtripped right into a PUT.
	newArgs := nomad.SchedulerWorkerPoolArgs{
		NumSchedulers:     args.NumSchedulers,
		EnabledSchedulers: args.EnabledSchedulers,
	}
	if newArgs.IsInvalid() {
		return nil, CodedError(http.StatusBadRequest, "Invalid request")
	}
	reply := srv.SetSchedulerWorkerConfig(newArgs)

	response := &api.AgentSchedulerWorkerConfigResponse{
		ServerID:          srv.LocalMember().Name,
		NumSchedulers:     reply.NumSchedulers,
		EnabledSchedulers: reply.EnabledSchedulers,
	}

	return response, nil
}

// aclPermissionCheckHelper takes a token string and checks it with Authenticate
// and ResolveACL methods. If the token doesn't satisfy perm function, an error
// is returned.
func aclPermissionCheckHelper(srv *nomad.Server, secret string, perm func(acl *acl.ACL) bool) error {
	r := &structs.GenericRequest{}
	r.AuthToken = secret
	if authErr := srv.Authenticate(nil, r); authErr != nil {
		return CodedError(http.StatusForbidden, authErr.Error())
	}

	aclObj, err := srv.ResolveACL(r)
	if err != nil {
		return CodedError(http.StatusInternalServerError, err.Error())
	} else if !perm(aclObj) {
		return CodedError(http.StatusForbidden, structs.ErrPermissionDenied.Error())
	}

	return nil
}