jwtcurl/main.go

207 lines
5.3 KiB
Go

package main
import (
"bytes"
"crypto/tls"
"crypto/x509"
"encoding/json"
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
"time"
"gitlab-app.eng.qops.net/golang/jwt"
)
type LagReader struct {
lag time.Duration
src io.Reader
}
func main() {
err := Main()
if err != nil {
log.Println(err)
os.Exit(1)
}
}
func Main() error {
var bodyRepeat int
var path, host, method, body, headers, brandID, userID, issuer, basicAuth, claims string
var ca, cert, key, secret string
var needJWT, verbose, jsonPP bool
var timeout, lag time.Duration
flag.StringVar(&method, "method", "get", "method for request")
flag.StringVar(&path, "path", "fieldsetdefinitions/v1/index/surveys/SV_031sm3MMOPSa8Tz/fieldsets?assumeHasPermission=true", "path for request")
flag.StringVar(&host, "host", "data-platform.service.b1-prv.consul:8080", "host and port for request")
flag.StringVar(&body, "body", "", "body for request")
flag.IntVar(&bodyRepeat, "bodyrepeat", 1, "repeat body for request")
flag.StringVar(&brandID, "brand", "testencresponse", "brandID for request JWT")
flag.StringVar(&userID, "user", "breel", "userid for request JWT")
flag.StringVar(&basicAuth, "auth", "", "comma separated user,password for basic auth")
flag.StringVar(&headers, "headers", "", "headers as k=v,k=v for request")
flag.StringVar(&issuer, "issuer", "dataprocessing,responseengine,fieldset-definitions,qualtrics,objectstore,svs,monolith,ex,blixt,null,responseengine", "issuer for jwt")
flag.BoolVar(&needJWT, "jwt", true, "need jwt boolean")
flag.BoolVar(&jsonPP, "jpp", true, "try json pretty print")
flag.BoolVar(&verbose, "v", false, "is verbose")
flag.DurationVar(&timeout, "t", time.Second*10, "request timeout")
flag.DurationVar(&lag, "lag", time.Second*0, "writing request lag after connecting")
flag.StringVar(&ca, "ca", "", "ca for server")
flag.StringVar(&cert, "cert", "", "cert for client")
flag.StringVar(&key, "key", "", "key for client")
flag.StringVar(&secret, "secret", "", "secret for jwt")
flag.StringVar(&claims, "claims", "", "extra claims as k=v,k=v")
flag.Parse()
if !strings.HasPrefix(host, "http") {
host = "http://" + host
}
c := makeClient(timeout, ca, cert, key)
var reqBody io.Reader
if bodyRepeat >= 1 {
reqBody = strings.NewReader(strings.Repeat(body, bodyRepeat))
} else {
reqBody = os.Stdin
}
reqBody = NewLagReader(lag, reqBody)
req, err := http.NewRequest(
strings.ToUpper(method),
host+"/"+strings.Trim(path, "/"),
reqBody,
)
if err != nil {
return err
}
req.Header.Add("brandId", brandID)
req.Header.Add("Content-Type", "application/json")
if len(headers) > 0 {
for _, pair := range strings.Split(headers, ",") {
kv := strings.Split(pair, "=")
req.Header.Add(kv[0], kv[1])
}
}
if needJWT {
setJWT(verbose, req, brandID, userID, issuer, secret, claims)
}
if basicAuth != "" {
splits := strings.Split(basicAuth, ",")
req.SetBasicAuth(splits[0], splits[1])
}
if verbose {
fmt.Fprintf(os.Stderr, "%v\n", req)
}
start := time.Now()
resp, err := c.Do(req)
elapsed := time.Since(start)
if err != nil {
return fmt.Errorf("DO failed: %v", err)
}
if verbose {
fmt.Fprintf(os.Stderr, "%v\n", resp.Header)
}
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("READ BODY failed: %v", err)
}
defer resp.Body.Close()
fmt.Fprintf(os.Stderr, "(%d / %v) ", resp.StatusCode, elapsed)
if jsonPP {
var v interface{}
if err := json.Unmarshal(b, &v); err == nil {
if c, err := json.MarshalIndent(v, "", " "); err == nil {
b = c
}
}
}
fmt.Printf("%s\n", bytes.TrimSpace(b))
if resp.StatusCode >= http.StatusBadRequest && resp.StatusCode != http.StatusNotFound {
return fmt.Errorf("Status %v", resp.StatusCode)
}
return nil
}
func makeClient(timeout time.Duration, ca, cert, key string) *http.Client {
transport := &http.Transport{
TLSClientConfig: &tls.Config{},
}
if ca == "" {
transport.TLSClientConfig.InsecureSkipVerify = true
} else {
caBytes, err := ioutil.ReadFile(ca)
if err != nil {
panic(err)
}
rootCAs := x509.NewCertPool()
rootCAs.AppendCertsFromPEM(caBytes)
transport.TLSClientConfig.RootCAs = rootCAs
}
if cert != "" && key != "" {
clientCert, err := tls.LoadX509KeyPair(cert, key)
if err != nil {
panic(err)
}
transport.TLSClientConfig.Certificates = []tls.Certificate{clientCert}
transport.TLSClientConfig.BuildNameToCertificate()
}
return &http.Client{
Timeout: timeout,
Transport: transport,
}
}
func setJWT(verbose bool, r *http.Request, brandID, userID string, issuer, secret, claims string) {
signer := &jwt.Signer{
Key: []byte(secret),
DefaultClaims: jwt.Claims{
Audience: "qualtrics",
Issuer: issuer,
UserID: userID,
BrandID: brandID,
Custom: map[string]interface{}{
"IsolationPartitionID": brandID,
"userType": "UT_SERVERADMIN",
},
},
IncludeBodyHash: true,
}
for _, claim := range strings.Split(claims, ",") {
c := strings.Split(claim, "=")
if len(c) < 2 {
continue
}
signer.DefaultClaims.Custom[c[0]] = c[1]
}
if err := signer.Sign(r, jwt.Claims{}); err != nil {
panic(err)
}
if verbose {
log.Println(*signer)
}
}
func NewLagReader(lag time.Duration, src io.Reader) *LagReader {
return &LagReader{
lag: lag,
src: src,
}
}
func (lr *LagReader) Read(p []byte) (n int, err error) {
if lr.lag > 0 {
<-time.After(lr.lag)
lr.lag = 0
}
return lr.src.Read(p)
}