fleet/server/utils.go
Victor Lyuboslavsky 92bc1c650e
Move PostJSONWithTimeout to platform/http package and activity cleanup (#40561)
<!-- Add the related story/sub-task/bug number, like Resolves #123, or
remove if NA -->
**Related issue:** Resolves #38536

- Moved PostJSONWithTimeout to platform/http
- Created platform/errors package with only types needed by ctxerr. This
way, ctxerr did not need to import fleethttp.
- Made activity bounded context use PostJSONWithTimeout directly
- Removed some activity types from legacy code that were no longer
needed

# Checklist for submitter

- [ ] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
- Changes file `38536-new-activity-bc` already present, and this is just
cleanup from that work.

## Testing

- [x] Added/updated automated tests
- [x] QA'd all new/changed functionality manually


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

## Summary by CodeRabbit

## Release Notes

* **Refactor**
* Reorganized error handling utilities for improved clarity and
decoupling.
* Consolidated HTTP utilities to centralize JSON posting functionality
with timeout support.
* Simplified activity service initialization by removing unused internal
parameters.
* Cleaned up test utilities and removed webhook-related test
scaffolding.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-02-26 17:39:10 -06:00

115 lines
3.8 KiB
Go

package server
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
"html/template"
"strings"
"github.com/fleetdm/fleet/v4/server/bindata"
platformhttp "github.com/fleetdm/fleet/v4/server/platform/http"
)
// GenerateRandomText return a string generated by filling in keySize bytes with
// random data and then base64 encoding those bytes
func GenerateRandomText(keySize int) (string, error) {
key := make([]byte, keySize)
_, err := rand.Read(key)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(key), nil
}
// GenerateRandomURLSafeText return a string generated by filling in keySize bytes with
// random data and then URL-safe base64 encoding those bytes
func GenerateRandomURLSafeText(keySize int) (string, error) {
key := make([]byte, keySize)
_, err := rand.Read(key)
if err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(key), nil
}
// PostJSONWithTimeout marshals v as JSON and POSTs it to the given URL with a 30-second timeout.
//
// Deprecated: Use github.com/fleetdm/fleet/v4/server/platform/http.PostJSONWithTimeout instead.
var PostJSONWithTimeout = platformhttp.PostJSONWithTimeout
// MaskSecretURLParams masks URL query values if the query param name includes "secret", "token",
// "key", "password". It accepts a raw string and returns a redacted string if the raw string is
// URL-parseable. If it is not URL-parseable, the raw string is returned unchanged.
//
// Deprecated: Use github.com/fleetdm/fleet/v4/server/platform/http.MaskSecretURLParams instead.
var MaskSecretURLParams = platformhttp.MaskSecretURLParams
// MaskURLError checks if the provided error is a *url.Error. If so, it applies MaskSecretURLParams
// to the URL value and returns the modified error. If not, the error is returned unchanged.
//
// Deprecated: Use github.com/fleetdm/fleet/v4/server/platform/http.MaskURLError instead.
var MaskURLError = platformhttp.MaskURLError
// TODO: Consider moving other crypto functions from server/mdm/apple/util to here
// DecodePrivateKeyPEM decodes PEM-encoded private key data.
func DecodePrivateKeyPEM(encoded []byte) (*rsa.PrivateKey, error) {
block, _ := pem.Decode(encoded)
if block == nil {
return nil, errors.New("no PEM-encoded data found")
}
if block.Type != "RSA PRIVATE KEY" {
return nil, fmt.Errorf("unexpected block type %s", block.Type)
}
return x509.ParsePKCS1PrivateKey(block.Bytes)
}
// GetTemplate takes a path to a template file and a template name and will
// include the template file in the build binary. It then returns a pointer to
// the template.
func GetTemplate(templatePath string, templateName string) (*template.Template, error) {
templateData, err := bindata.Asset(templatePath)
if err != nil {
return nil, err
}
t, err := template.New(templateName).Parse(string(templateData))
if err != nil {
return nil, err
}
return t, nil
}
// Base64DecodePaddingAgnostic decodes a base64 string that might be encoded
// using raw encoding or standard encoding (padded)
func Base64DecodePaddingAgnostic(s string) ([]byte, error) {
us := strings.TrimRight(s, string(base64.StdPadding))
return base64.RawStdEncoding.DecodeString(us)
}
// RemoveDuplicatesFromSlice returns a slice with all the duplicates removed from the input slice.
func RemoveDuplicatesFromSlice[T comparable](slice []T) []T {
// We are using the allKeys map as a set here
allKeys := make(map[T]struct{}, len(slice))
list := make([]T, 0, len(slice))
for _, i := range slice {
if _, exists := allKeys[i]; !exists {
allKeys[i] = struct{}{}
list = append(list, i)
}
}
return list
}
// NeverTimestamp is the timestamp used to indicate that an event has never
// occurred. For example, `hosts.detail_updated_at` is set to this value to indicate
// that Fleet has never updated the host vitals.
const NeverTimestamp = "2000-01-01 00:00:00"