Skip to content

Commit

Permalink
feat: add basic-auth credential tool
Browse files Browse the repository at this point in the history
Add the `Basic Auth` tool to support collecting a username and
password from a user. This is useful for interacting with platforms
with no or limited OAuth support.

Signed-off-by: Nick Hale <[email protected]>
  • Loading branch information
njhale committed Nov 25, 2024
1 parent bd71a48 commit 33e3b13
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 0 deletions.
22 changes: 22 additions & 0 deletions basic-auth/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module basic-auth

go 1.23.3

require (
github.com/gptscript-ai/go-gptscript v0.9.6-0.20241106212914-ba040ce8f47b
github.com/tidwall/gjson v1.18.0
)

require (
github.com/getkin/kin-openapi v0.124.0 // indirect
github.com/go-openapi/jsonpointer v0.20.2 // indirect
github.com/go-openapi/swag v0.22.8 // indirect
github.com/invopop/yaml v0.2.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
46 changes: 46 additions & 0 deletions basic-auth/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/getkin/kin-openapi v0.124.0 h1:VSFNMB9C9rTKBnQ/fpyDU8ytMTr4dWI9QovSKj9kz/M=
github.com/getkin/kin-openapi v0.124.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM=
github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw=
github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/gptscript-ai/go-gptscript v0.9.6-0.20241106212914-ba040ce8f47b h1:adIh3EnMTlC19t2k1IJoOtF6me/hPxks2GxSSgB7oEw=
github.com/gptscript-ai/go-gptscript v0.9.6-0.20241106212914-ba040ce8f47b/go.mod h1:/FVuLwhz+sIfsWUgUHWKi32qT0i6+IXlUlzs70KKt/Q=
github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY=
github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
148 changes: 148 additions & 0 deletions basic-auth/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package main

import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
"os/signal"
"regexp"
"strconv"
"strings"

"github.com/gptscript-ai/go-gptscript"
"github.com/tidwall/gjson"
)

type input struct {
ToolDisplayName string `json:"tool_display_name,omitempty"`
Message string `json:"message"`
UsernameField string `json:"username_field,omitempty"`
UsernameEnv string `json:"username_env,omitempty"`
PasswordField string `json:"password_field,omitempty"`
PasswordEnv string `json:"password_env,omitempty"`
Metadata map[string]string
}

type sysPromptInput struct {
Message string `json:"message,omitempty"`
Fields string `json:"fields,omitempty"`
Sensitive string `json:"sensitive,omitempty"`
Metadata map[string]string `json:"metadata,omitempty"`
}

func main() {
// Set up signal handler
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
defer cancel()

in, err := getInput()
if err != nil {
fmt.Println("Input error: ", err)
os.Exit(1)
}

username, password, err := getCredentials(ctx, in)
if err != nil {
fmt.Println("Error getting credentials:", err)
os.Exit(1)
}
fmt.Printf(`{"env": {"%s": "%s", "%s": "%s"}}`, in.UsernameEnv, username, in.PasswordEnv, password)
}

func getCredentials(ctx context.Context, in input) (string, string, error) {
client, err := gptscript.NewGPTScript()
if err != nil {
fmt.Println("Error creating GPTScript client:", err)
return "", "", fmt.Errorf("Error creating GPTScript client: %w", err)
}
defer client.Close()

sysPromptIn, err := json.Marshal(sysPromptInput{
Message: in.Message,
Fields: strings.Join([]string{in.UsernameField, in.PasswordField}, ","),
Sensitive: strconv.FormatBool(true),
Metadata: in.Metadata,
})
if err != nil {
return "", "", fmt.Errorf("Error marshalling sys prompt input: %w", err)
}

run, err := client.Run(ctx, "sys.prompt", gptscript.Options{
Input: string(sysPromptIn),
})
if err != nil {
return "", "", fmt.Errorf("Error running GPTScript prompt: %w", err)
}

res, err := run.Text()
if err != nil {
return "", "", fmt.Errorf("Error getting GPTScript response: %w", err)
}

username := gjson.Get(res, in.UsernameField).String()
password := gjson.Get(res, in.PasswordField).String()

return username, password, nil

}

func getInput() (input, error) {
if len(os.Args) != 2 {
return input{}, errors.New("Missing input string")
}

inputStr := os.Args[1]

var in input
if err := json.Unmarshal([]byte(inputStr), &in); err != nil {
return input{}, fmt.Errorf("Error parsing input JSON: %w", err)
}

// Helper function to trim and set default values
cleanField := func(field, defaultValue string) string {
field = strings.TrimSpace(field)
if field == "" {
return defaultValue
}
return field
}

in.ToolDisplayName = cleanField(in.ToolDisplayName, "Basic Auth Credential")
in.UsernameField = cleanField(in.UsernameField, "username")
in.PasswordField = cleanField(in.PasswordField, "password")

// Set environment variables and validate
var validEnvPattern = regexp.MustCompile(`^[A-Za-z_][A-Za-z0-9_]*$`)
cleanEnv := func(env, field string) (string, error) {
env = strings.TrimSpace(env)
if env == "" {
return "", fmt.Errorf("Environment variable name for field %s must be set", field)
}
if !validEnvPattern.MatchString(env) {
return "", fmt.Errorf("Invalid environment variable name: %s", env)
}
return env, nil
}

var err error
in.UsernameEnv, err = cleanEnv(in.UsernameEnv, in.UsernameField)
if err != nil {
return input{}, err
}
in.PasswordEnv, err = cleanEnv(in.PasswordEnv, in.PasswordField)
if err != nil {
return input{}, err
}

in.Message = fmt.Sprintf("Enter your %s and %s", in.UsernameField, in.PasswordField)

in.Metadata = map[string]string{
"authType": "basic",
"toolContext": "credential",
"toolDisplayName": in.ToolDisplayName,
}

return in, nil
}
9 changes: 9 additions & 0 deletions basic-auth/tool.gpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Name: basic-auth
Param: tool_display_name: The tool name to display to the user when prompting for credentials
Param: message: The message to display the when prompting for this credential
Param: username_field: The name of the username field
Param: username_env: The name of the environment variable to set for the captured username
Param: password_field: The name of the password field
Param: password_env: The name of the environment variable to set for the captured password

#!${GPTSCRIPT_TOOL_DIR}/bin/gptscript-go-tool "${GPTSCRIPT_INPUT}"

0 comments on commit 33e3b13

Please sign in to comment.