fleet/server/utils.go
Gabriel Hernandez 6555d8def4
Feat UI windows automatic enrollment (#12988)
relates to #12606

Implementation of the Windows automatic enrollment Fleet UI pages. This
includes implementation of card for windows automatic enrollment, the
setup page for windows automatic enrollment, and terms and conditions
page for windows (This is currently still being worked on as our current
solution is not working).

**windows mdm auto enrollment card**


![image](https://github.com/fleetdm/fleet/assets/1153709/d4dc1813-dc28-4a63-bacd-cb7e43e18170)

**windows auto enrollment setup page**


![image](https://github.com/fleetdm/fleet/assets/1153709/92da4b05-0d5d-4404-867f-6d315957bdc3)

- [x] Changes file added for user-visible changes in `changes/` or
`orbit/changes/`.
See [Changes
files](https://fleetdm.com/docs/contributing/committing-changes#changes-files)
for more information.
- [x] Manual QA for all new/changed functionality

---------

Co-authored-by: Marcos Oviedo <marcos@fleetdm.com>
2023-08-08 15:57:55 +01:00

140 lines
3.5 KiB
Go

package server
import (
"bytes"
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"html/template"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"
"github.com/fleetdm/fleet/v4/pkg/fleethttp"
"github.com/fleetdm/fleet/v4/server/bindata"
)
// 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
}
func httpSuccessStatus(statusCode int) bool {
return statusCode >= 200 && statusCode <= 299
}
func PostJSONWithTimeout(ctx context.Context, url string, v interface{}) error {
jsonBytes, err := json.Marshal(v)
if err != nil {
return err
}
client := fleethttp.NewClient(fleethttp.WithTimeout(30 * time.Second))
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(jsonBytes))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("failed to POST to %s: %s, request-size=%d", MaskSecretURLParams(url), MaskURLError(err), len(jsonBytes))
}
defer resp.Body.Close()
if !httpSuccessStatus(resp.StatusCode) {
body, _ := ioutil.ReadAll(resp.Body)
return fmt.Errorf("error posting to %s: %d. %s", MaskSecretURLParams(url), resp.StatusCode, string(body))
}
return nil
}
// 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.
func MaskSecretURLParams(rawURL string) string {
u, err := url.Parse(rawURL)
if err != nil {
return rawURL
}
keywords := []string{"secret", "token", "key", "password"}
containsKeyword := func(s string) bool {
s = strings.ToLower(s)
for _, kw := range keywords {
if strings.Contains(s, kw) {
return true
}
}
return false
}
q := u.Query()
for k := range q {
if containsKeyword(k) {
q[k] = []string{"MASKED"}
}
}
u.RawQuery = q.Encode()
return u.Redacted()
}
// 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.
func MaskURLError(e error) error {
ue, ok := e.(*url.Error)
if !ok {
return e
}
ue.URL = MaskSecretURLParams(ue.URL)
return ue
}
// 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
}