Compare commits

...

10 Commits

Author SHA1 Message Date
Bel LaPointe
cd5b6a95df go mod 2023-07-06 08:40:41 -06:00
Bel LaPointe
9a0ef6da85 lower timeout 2022-09-29 14:59:52 -06:00
Bel LaPointe
db23e9152a add .i 2021-09-07 11:07:13 -06:00
Bel LaPointe
ca9c1b5f7c allow auth to have normal separater 2020-02-26 14:16:14 -07:00
Bel LaPointe
e05a287e4c Fix 2019-10-15 13:03:43 -06:00
Bel LaPointe
e2b17a261f show response time with status as stderr 2019-09-23 10:03:28 -06:00
Bel LaPointe
1546b88225 remove secret 2019-08-06 12:32:34 -06:00
bel
f89dd39356 verbosity 2019-06-28 20:09:52 -06:00
Bel LaPointe
850052c4a5 basic auth 2019-04-19 07:23:57 -06:00
Bel LaPointe
07502502ef pretty print by default 2019-04-09 08:26:28 -06:00
3 changed files with 165 additions and 20 deletions

12
go.mod Normal file
View File

@@ -0,0 +1,12 @@
module gogs.inhome.blapointe.com/jwtcurl
go 1.20
require gitlab-app.eng.qops.net/data-store/jwt v1.4.0
require (
github.com/alexcesaro/statsd v2.0.0+incompatible // indirect
github.com/form3tech-oss/jwt-go v3.2.2+incompatible // indirect
gitlab-app.eng.qops.net/golang/metrics/v2 v2.0.0 // indirect
gitlab-app.eng.qops.net/golang/qtoken v1.0.1 // indirect
)

12
go.sum Normal file
View File

@@ -0,0 +1,12 @@
github.com/alexcesaro/statsd v2.0.0+incompatible h1:HG17k1Qk8V1F4UOoq6tx+IUoAbOcI5PHzzEUGeDD72w=
github.com/alexcesaro/statsd v2.0.0+incompatible/go.mod h1:vNepIbQAiyLe1j480173M6NYYaAsGwEcvuDTU3OCUGY=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
gitlab-app.eng.qops.net/data-store/jwt v1.4.0 h1:32/UWp6dJGSXqNH3oOA4aw3Dt3nmc2eBRc6pCPG1JUU=
gitlab-app.eng.qops.net/data-store/jwt v1.4.0/go.mod h1:qwxQUeakZi9C8mSEHJPlvrXj4pEDwm2MnOz7bjqUNVg=
gitlab-app.eng.qops.net/golang/metrics/v2 v2.0.0 h1:bZ8sE1e/+Q6j/5hk8UvLRsg7RDo6GYH3s96dMXMbyCM=
gitlab-app.eng.qops.net/golang/metrics/v2 v2.0.0/go.mod h1:3t9G/KnpMPKci08VpOUzPj7oSk7V1iLCM6yV6/PQZss=
gitlab-app.eng.qops.net/golang/qtoken v1.0.1 h1:JP0TeZssYXZvPWrxahQVnklrmLcUXLasnWAXvlRjZvE=
gitlab-app.eng.qops.net/golang/qtoken v1.0.1/go.mod h1:7Hd83PrhqrStQf3RDcI8AYZo0iNaGOo11Faf/U45yBA=
gopkg.in/alexcesaro/statsd.v2 v2.0.0 h1:FXkZSCZIH17vLCO5sO2UucTHsH9pc+17F6pl3JVCwMc=
gopkg.in/alexcesaro/statsd.v2 v2.0.0/go.mod h1:i0ubccKGzBVNBpdGV5MocxyA/XlLUJzA7SLonnE4drU=

159
main.go Normal file → Executable file
View File

@@ -4,45 +4,86 @@ import (
"bytes" "bytes"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/json"
"flag" "flag"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"log"
"net/http" "net/http"
"net/url"
"os" "os"
"strings" "strings"
"time" "time"
"gitlab-app.eng.qops.net/golang/jwt" "gitlab-app.eng.qops.net/data-store/jwt"
) )
type LagReader struct {
lag time.Duration
src io.Reader
}
func main() { func main() {
var bodyRepeat int err := Main()
var path, host, method, body, headers, brandID, issuer string if err != nil {
var ca, cert, key, secret string log.Println(err)
var needJWT, verbose bool os.Exit(1)
var timeout time.Duration }
}
var bodyRepeat, n int
var path, host, method, body, headers, brandID, userID, issuer, audience, basicAuth, claims, kid string
var ca, cert, key, secret string
var needJWT, verbose, jsonPP, quiet, responseHeaders bool
var timeout, lag time.Duration
func Main() error {
flag.StringVar(&method, "method", "get", "method for request") flag.StringVar(&method, "method", "get", "method for request")
flag.StringVar(&kid, "kid", "prod/rems-api", "kid for request")
flag.StringVar(&path, "path", "fieldsetdefinitions/v1/index/surveys/SV_031sm3MMOPSa8Tz/fieldsets?assumeHasPermission=true", "path 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(&host, "host", "data-platform.service.b1-prv.consul:8080", "host and port for request")
flag.StringVar(&body, "body", "", "body for request") flag.StringVar(&body, "body", "", "body for request")
flag.IntVar(&bodyRepeat, "bodyrepeat", 1, "repeat body for request") flag.IntVar(&bodyRepeat, "bodyrepeat", 1, "repeat body for request")
flag.IntVar(&n, "n", 1, "how many times to execute")
flag.StringVar(&brandID, "brand", "testencresponse", "brandID for request JWT") 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(&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.StringVar(&issuer, "issuer", "dataprocessing,responseengine,fieldset-definitions,qualtrics,objectstore,svs,monolith,ex,blixt,null,responseengine", "issuer for jwt")
flag.StringVar(&audience, "audience", "qualtrics", "aud for jwt")
flag.BoolVar(&needJWT, "jwt", true, "need jwt boolean") 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.BoolVar(&verbose, "v", false, "is verbose")
flag.BoolVar(&responseHeaders, "i", false, "print response headers")
flag.BoolVar(&quiet, "q", false, "is quiet")
flag.DurationVar(&timeout, "t", time.Second*10, "request timeout") 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(&ca, "ca", "", "ca for server")
flag.StringVar(&cert, "cert", "", "cert for client") flag.StringVar(&cert, "cert", "", "cert for client")
flag.StringVar(&key, "key", "", "key for client") flag.StringVar(&key, "key", "", "key for client")
flag.StringVar(&secret, "secret", "dnKgzTPNZyEd2Kfop", "secret for jwt") flag.StringVar(&secret, "secret", "", "secret for jwt")
flag.StringVar(&claims, "claims", "", "extra claims as k=v,k=v")
flag.Parse() flag.Parse()
if quiet {
f, _ := ioutil.TempFile(os.TempDir(), "*")
os.Stderr = f
}
if !strings.HasPrefix(host, "http") { if !strings.HasPrefix(host, "http") {
host = "http://" + host host = "http://" + host
} }
for i := 0; i < n; i++ {
if err := do(); err != nil {
return err
}
}
return nil
}
func do() error {
c := makeClient(timeout, ca, cert, key) c := makeClient(timeout, ca, cert, key)
var reqBody io.Reader var reqBody io.Reader
if bodyRepeat >= 1 { if bodyRepeat >= 1 {
@@ -56,35 +97,84 @@ func main() {
reqBody, reqBody,
) )
if err != nil { if err != nil {
panic(err) return err
} }
if lag != 0 {
req.Body = io.NopCloser(NewLagReader(lag, req.Body))
} else {
b, _ := ioutil.ReadAll(req.Body)
req.Body = io.NopCloser(bytes.NewReader(b))
req.ContentLength = int64(len(b))
}
req.Header.Set("Content-Type", "application/json")
if len(headers) > 0 { if len(headers) > 0 {
for _, pair := range strings.Split(headers, ",") { for _, pair := range strings.Split(headers, ",") {
kv := strings.Split(pair, "=") kv := strings.Split(pair, "=")
req.Header.Add(kv[0], kv[1]) k := kv[0]
v := strings.Join(kv[1:], "=")
vd, err := url.QueryUnescape(v)
if err != nil {
panic(err)
} }
req.Header.Add(k, vd)
}
}
if req.Header.Get("brandId") == "" {
req.Header.Set("brandId", brandID)
} }
if needJWT { if needJWT {
setJWT(req, brandID, issuer, secret) setJWT(verbose, req, brandID, userID, issuer, secret, claims, kid, audience)
}
if basicAuth != "" {
splits := strings.Split(basicAuth, ",")
if len(splits) == 1 {
splits = strings.Split(basicAuth, ":")
}
req.SetBasicAuth(splits[0], splits[1])
} }
if verbose { if verbose {
fmt.Fprintf(os.Stderr, "%v\n", req) fmt.Fprintf(os.Stderr, "%+v\n", req)
for k, v := range req.Header {
fmt.Fprintf(os.Stderr, "\t[%s] = (%d) %+v\n", k, len(v), v)
} }
}
start := time.Now()
resp, err := c.Do(req) resp, err := c.Do(req)
elapsed := time.Since(start)
if err != nil { if err != nil {
fmt.Println("DO FAILED:", err) return fmt.Errorf("DO failed: %v", err)
return
} }
b, err := ioutil.ReadAll(resp.Body) b, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
fmt.Println("READ BODY FAILED:", err) return fmt.Errorf("READ BODY failed: %v", err)
return
} }
defer resp.Body.Close() defer resp.Body.Close()
fmt.Fprintf(os.Stderr, "(%d) ", resp.StatusCode) fmt.Fprintf(os.Stderr, "(%d / %v) ", resp.StatusCode, elapsed)
if responseHeaders || verbose {
f := os.Stdout
if verbose {
f = os.Stderr
}
fmt.Fprintf(f, "\n")
for k := range resp.Header {
fmt.Fprintf(f, "%s: %s\n", k, resp.Header.Get(k))
}
}
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)) 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 { func makeClient(timeout time.Duration, ca, cert, key string) *http.Client {
@@ -116,22 +206,53 @@ func makeClient(timeout time.Duration, ca, cert, key string) *http.Client {
} }
} }
func setJWT(r *http.Request, brandID string, issuer string, secret string) { func setJWT(verbose bool, r *http.Request, brandID, userID string, issuer, secret, claims, kid, audience string) {
signer := &jwt.Signer{ signer := &jwt.Signer{
Timeout: time.Minute * 5,
Key: []byte(secret), Key: []byte(secret),
DefaultHeaders: jwt.Headers{
KeyID: kid,
},
DefaultClaims: jwt.Claims{ DefaultClaims: jwt.Claims{
Audience: "qualtrics", Audience: audience,
Issuer: issuer, Issuer: issuer,
UserID: "breel", UserID: userID,
BrandID: brandID, BrandID: brandID,
Custom: map[string]interface{}{ Custom: map[string]interface{}{
"IsolationPartitionID": brandID, "IsolationPartitionID": brandID,
"userType": "UT_SERVERADMIN",
}, },
}, },
IncludeBodyHash: true, 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 { if err := signer.Sign(r, jwt.Claims{}); err != nil {
panic(err) panic(err)
} }
if verbose {
log.Printf("%+v", *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)
} }