fleet/server/service/certificate_templates.go
Juan Fernandez a098a6c9bc
Android certificate crud: validate variable replacement (#36648)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #36533 

If variables can't be interpolated return 400.

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [X] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.

## Testing

- [X] Added/updated automated tests

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Corrected HTTP status code returned when certificate template variable
interpolation fails
* Certificate delivery status now properly reflects failed
interpolation, improving visibility into deployment issues

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Victor Lyuboslavsky <2685025+getvictor@users.noreply.github.com>
2025-12-05 12:14:14 -06:00

70 lines
2.4 KiB
Go

package service
import (
"context"
"fmt"
"slices"
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/variables"
)
// Fleet variables supported in certificate template subject names.
var fleetVarsSupportedInCertificateTemplates = []fleet.FleetVarName{
fleet.FleetVarHostUUID,
fleet.FleetVarHostHardwareSerial,
fleet.FleetVarHostEndUserIDPUsername,
}
func validateCertificateTemplateFleetVariables(subjectName string) error {
fleetVars := variables.Find(subjectName)
if len(fleetVars) == 0 {
return nil
}
for _, fleetVar := range fleetVars {
if !slices.Contains(fleetVarsSupportedInCertificateTemplates, fleet.FleetVarName(fleetVar)) {
return fmt.Errorf("Fleet variable $FLEET_VAR_%s is not supported in certificate templates", fleetVar)
}
}
return nil
}
// replaceCertificateVariables replaces FLEET_VAR_* variables in the subject name with actual host values
func (svc *Service) replaceCertificateVariables(ctx context.Context, subjectName string, host *fleet.Host) (string, error) {
fleetVars := variables.Find(subjectName)
if len(fleetVars) == 0 {
return subjectName, nil
}
result := subjectName
for _, fleetVar := range fleetVars {
switch fleetVar {
case string(fleet.FleetVarHostUUID):
if host.UUID == "" {
return "", ctxerr.Errorf(ctx, "host does not have a UUID for variable %s", fleetVar)
}
result = fleet.FleetVarHostUUIDRegexp.ReplaceAllString(result, host.UUID)
case string(fleet.FleetVarHostHardwareSerial):
if host.HardwareSerial == "" {
return "", ctxerr.Errorf(ctx, "host %s does not have a hardware serial for variable %s", host.UUID, fleetVar)
}
result = fleet.FleetVarHostHardwareSerialRegexp.ReplaceAllString(result, host.HardwareSerial)
case string(fleet.FleetVarHostEndUserIDPUsername):
users, err := fleet.GetEndUsers(ctx, svc.ds, host.ID)
if err != nil {
return "", ctxerr.Wrapf(ctx, err, "getting host end users for variable %s", fleetVar)
}
if len(users) == 0 || users[0].IdpUserName == "" {
return "", ctxerr.Errorf(ctx, "host %s does not have an IDP username for variable %s", host.UUID, fleetVar)
}
result = fleet.FleetVarHostEndUserIDPUsernameRegexp.ReplaceAllString(result, users[0].IdpUserName)
default:
return "", ctxerr.Errorf(ctx, "unsupported Fleet variable %s in certificate template", fleetVar)
}
}
return result, nil
}