mirror of
https://github.com/fleetdm/fleet
synced 2026-04-21 13:37:30 +00:00
**Related issues:** - Resolves #42882 - Resolves #42880 - Resolves #42884 # Changes - Added POST /users/api_only endpoint for creating API-only users. - Added PATCH /users/api_only/{id} for updating existing API-only users. - Updated `fleetctl user create --api-only` removing email/password field requirements.
150 lines
4.8 KiB
Go
150 lines
4.8 KiB
Go
package server
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/x509"
|
|
"encoding/base64"
|
|
"encoding/pem"
|
|
"errors"
|
|
"fmt"
|
|
"html/template"
|
|
"math/big"
|
|
"strings"
|
|
|
|
"github.com/fleetdm/fleet/v4/server/bindata"
|
|
platformhttp "github.com/fleetdm/fleet/v4/server/platform/http"
|
|
)
|
|
|
|
// GenerateRandomEmail generates a random email using baseEmail as the base.
|
|
// For example: GenerateRandomEmail('email@example.com') -> 'email+somerandomtext@example.com'
|
|
func GenerateRandomEmail(baseEmail string) (string, error) {
|
|
emailSuffix, err := GenerateRandomURLSafeText(10)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
atIdx := strings.Index(baseEmail, "@")
|
|
var email string
|
|
if atIdx < 0 {
|
|
email = fmt.Sprintf("%s+%s", baseEmail, emailSuffix)
|
|
} else {
|
|
email = fmt.Sprintf("%s+%s%s", baseEmail[:atIdx], emailSuffix, baseEmail[atIdx:])
|
|
}
|
|
return email, nil
|
|
}
|
|
|
|
// GenerateRandomPwd generates a random text that
|
|
// complies with Fleet's password requirements.
|
|
func GenerateRandomPwd() (string, error) {
|
|
pwd, err := GenerateRandomText(14)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
n, err := rand.Int(rand.Reader, big.NewInt(int64(100)))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return fmt.Sprintf("%s%d!", pwd, n.Int64()), nil
|
|
}
|
|
|
|
// 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"
|