package ctrl import ( "crypto/rsa" "encoding/base64" "encoding/json" "fmt" "math/big" "regexp" "strings" "text/template" . "github.com/mickael-kerjean/filestash/server/common" "github.com/golang-jwt/jwt/v5" ) var tmplFuncs = template.FuncMap{ "split": func(s, sep string) []string { return strings.Split(sep, s) }, "get": func(i int, arr any) (string, error) { switch v := arr.(type) { case string: splits := strings.Split(v, ",") if i < len(splits) && i >= 0 { return strings.TrimSpace(splits[i]), nil } return "", nil case []string: if i < len(v) && i >= 0 { return v[i], nil } return "", nil default: return "", ErrNotImplemented } }, "contains": func(match string, opts ...any) (bool, error) { exact := true var input any if len(opts) == 0 { return false, ErrNotValid } else if len(opts) == 1 { input = opts[0] } else if len(opts) == 2 { exact = opts[0].(bool) input = opts[1] } switch in := input.(type) { case string: splits := strings.Split(in, ",") for _, split := range splits { split = strings.TrimSpace(split) if exact && split == match { return true, nil } else if !exact && strings.Contains(split, match) { return true, nil } } return false, nil case []string: for _, split := range in { split = strings.TrimSpace(split) if exact && split == match { return true, nil } else if !exact && strings.Contains(split, match) { return true, nil } } return false, nil default: return false, ErrNotImplemented } }, "filter": func(arg string, stdin string) (string, error) { out := []string{} r, regErr := regexp.Compile(arg) if regErr != nil { return "", regErr } for _, chunk := range strings.Split(stdin, ",") { c := strings.TrimSpace(chunk) if r.Match([]byte(c)) { out = append(out, c) } } return strings.Join(out, ", "), nil }, "replace": func(arguments ...string) (string, error) { var ( arg string stdin string replace string ) if len(arguments) == 2 { arg = arguments[0] stdin = arguments[1] } else if len(arguments) == 3 { arg = arguments[0] replace = arguments[1] stdin = arguments[2] } else { return "", ErrNotImplemented } chunks := strings.Split(stdin, ",") r, regErr := regexp.Compile(arg) if regErr != nil { return "", regErr } for i := range chunks { c := strings.TrimSpace(chunks[i]) chunks[i] = r.ReplaceAllString(c, replace) } return strings.Join(chunks, ", "), nil }, "encryptGCM": func(str string, key string) (string, error) { data, err := EncryptAESGCM([]byte(key), []byte(str)) return base64.StdEncoding.EncodeToString(data), err }, "jwt": func(args ...string) (string, error) { if len(args) == 0 { return "", ErrNotValid } var stdin = args[len(args)-1] var token *jwt.Token var err error claims := jwt.MapClaims{} if len(args) == 1 { token, _, err = jwt.NewParser(jwt.WithPaddingAllowed()).ParseUnverified(stdin, claims) token.Valid = true } else if len(args) == 2 { token, err = jwt.NewParser(jwt.WithPaddingAllowed()).ParseWithClaims(stdin, claims, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); ok { return []byte(args[0]), nil } else if _, ok := token.Method.(*jwt.SigningMethodRSA); ok { modBytes, err := base64.RawURLEncoding.DecodeString(args[0]) if err != nil { return "", err } return &rsa.PublicKey{ N: new(big.Int).SetBytes(modBytes), E: 65537, }, nil } Log.Debug("ctrl::tmpl invalid jwt signing method: %v", token.Header["alg"]) return nil, ErrNotImplemented }) } if err != nil { return "", err } else if !token.Valid { return "", ErrNotValid } b, err := json.Marshal(claims) return string(b), err }, "jq": func(args ...string) (out string, err error) { if len(args) == 0 || len(args) > 2 { return "", ErrNotValid } stdin := args[len(args)-1] data := map[string]any{} if err = json.Unmarshal([]byte(stdin), &data); err != nil { return "", err } if len(args) == 1 { return stdin, nil } filters := strings.Split(args[0], ".") for i := 0; i < len(filters)-1; i++ { if filters[i] == "" { continue } if obj, ok := data[filters[i]].(map[string]any); ok { data = obj } else { return "", nil } } return fmt.Sprintf("%v", data[filters[len(filters)-1]]), nil }, }