2024-01-15 16:01:41 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"flag"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"os/signal"
|
2024-02-29 15:45:53 +00:00
|
|
|
"strings"
|
2024-01-15 16:01:41 +00:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/golang-jwt/jwt/v4"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
|
|
|
|
"cdr.dev/slog"
|
|
|
|
"cdr.dev/slog/sloggers/sloghuman"
|
|
|
|
"github.com/coder/coder/v2/coderd/coderdtest/oidctest"
|
|
|
|
"github.com/coder/coder/v2/codersdk"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Flags
|
|
|
|
var (
|
|
|
|
expiry = flag.Duration("expiry", time.Minute*5, "Token expiry")
|
|
|
|
clientID = flag.String("client-id", "static-client-id", "Client ID, set empty to be random")
|
|
|
|
clientSecret = flag.String("client-sec", "static-client-secret", "Client Secret, set empty to be random")
|
2024-01-22 20:46:05 +00:00
|
|
|
deviceFlow = flag.Bool("device-flow", false, "Enable device flow")
|
2024-01-15 16:01:41 +00:00
|
|
|
// By default, no regex means it will never match anything. So at least default to matching something.
|
2024-02-29 15:45:53 +00:00
|
|
|
extRegex = flag.String("ext-regex", `^(https?://)?example\.com(/.*)?$`, "External auth regex")
|
|
|
|
tooManyRequests = flag.String("429", "", "Simulate too many requests for a given endpoint.")
|
2024-01-15 16:01:41 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
testing.Init()
|
|
|
|
_ = flag.Set("test.timeout", "0")
|
|
|
|
|
|
|
|
flag.Parse()
|
|
|
|
|
|
|
|
// This is just a way to run tests outside go test
|
|
|
|
testing.Main(func(pat, str string) (bool, error) {
|
|
|
|
return true, nil
|
|
|
|
}, []testing.InternalTest{
|
|
|
|
{
|
|
|
|
Name: "Run Fake IDP",
|
|
|
|
F: RunIDP(),
|
|
|
|
},
|
|
|
|
}, nil, nil)
|
|
|
|
}
|
|
|
|
|
|
|
|
type withClientSecret struct {
|
|
|
|
// We never unmarshal this in prod, but we need this field for testing.
|
|
|
|
ClientSecret string `json:"client_secret"`
|
|
|
|
codersdk.ExternalAuthConfig
|
|
|
|
}
|
|
|
|
|
|
|
|
// RunIDP needs the testing.T because our oidctest package requires the
|
|
|
|
// testing.T.
|
|
|
|
func RunIDP() func(t *testing.T) {
|
2024-02-29 15:45:53 +00:00
|
|
|
tooManyRequestParams := oidctest.With429Arguments{}
|
|
|
|
if *tooManyRequests != "" {
|
|
|
|
for _, v := range strings.Split(*tooManyRequests, ",") {
|
|
|
|
v = strings.ToLower(strings.TrimSpace(v))
|
|
|
|
switch v {
|
|
|
|
case "all":
|
|
|
|
tooManyRequestParams.AllPaths = true
|
|
|
|
case "auth":
|
|
|
|
tooManyRequestParams.AuthorizePath = true
|
|
|
|
case "token":
|
|
|
|
tooManyRequestParams.TokenPath = true
|
|
|
|
case "keys":
|
|
|
|
tooManyRequestParams.KeysPath = true
|
|
|
|
case "userinfo":
|
|
|
|
tooManyRequestParams.UserInfoPath = true
|
|
|
|
case "device":
|
|
|
|
tooManyRequestParams.DeviceAuth = true
|
|
|
|
case "device-verify":
|
|
|
|
tooManyRequestParams.DeviceVerify = true
|
|
|
|
default:
|
|
|
|
log.Printf("Unknown too-many-requests value: %s\nView the `testidp/main.go` for valid values.", v)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-15 16:01:41 +00:00
|
|
|
return func(t *testing.T) {
|
|
|
|
idp := oidctest.NewFakeIDP(t,
|
|
|
|
oidctest.WithServing(),
|
2024-05-02 16:19:19 +00:00
|
|
|
oidctest.WithStaticUserInfo(jwt.MapClaims{
|
|
|
|
// This is a static set of auth fields. Might be beneficial to make flags
|
|
|
|
// to allow different values here. This is only required for using the
|
|
|
|
// testIDP as primary auth. External auth does not ever fetch these fields.
|
|
|
|
"email": "oidc_member@coder.com",
|
|
|
|
"preferred_username": "oidc_member",
|
|
|
|
"email_verified": true,
|
|
|
|
}),
|
2024-01-15 16:01:41 +00:00
|
|
|
oidctest.WithDefaultIDClaims(jwt.MapClaims{}),
|
|
|
|
oidctest.WithDefaultExpire(*expiry),
|
|
|
|
oidctest.WithStaticCredentials(*clientID, *clientSecret),
|
|
|
|
oidctest.WithIssuer("http://localhost:4500"),
|
|
|
|
oidctest.WithLogger(slog.Make(sloghuman.Sink(os.Stderr))),
|
2024-02-29 15:45:53 +00:00
|
|
|
oidctest.With429(tooManyRequestParams),
|
2024-01-15 16:01:41 +00:00
|
|
|
)
|
|
|
|
id, sec := idp.AppCredentials()
|
|
|
|
prov := idp.WellknownConfig()
|
|
|
|
const appID = "fake"
|
2024-01-22 20:46:05 +00:00
|
|
|
coderCfg := idp.ExternalAuthConfig(t, appID, &oidctest.ExternalAuthConfigOptions{
|
|
|
|
UseDeviceAuth: *deviceFlow,
|
|
|
|
})
|
2024-01-15 16:01:41 +00:00
|
|
|
|
|
|
|
log.Println("IDP Issuer URL", idp.IssuerURL())
|
|
|
|
log.Println("Coderd Flags")
|
2024-01-22 20:46:05 +00:00
|
|
|
|
2024-01-15 16:01:41 +00:00
|
|
|
deviceCodeURL := ""
|
|
|
|
if coderCfg.DeviceAuth != nil {
|
|
|
|
deviceCodeURL = coderCfg.DeviceAuth.CodeURL
|
|
|
|
}
|
2024-01-22 20:46:05 +00:00
|
|
|
|
2024-01-15 16:01:41 +00:00
|
|
|
cfg := withClientSecret{
|
|
|
|
ClientSecret: sec,
|
|
|
|
ExternalAuthConfig: codersdk.ExternalAuthConfig{
|
|
|
|
Type: appID,
|
|
|
|
ClientID: id,
|
|
|
|
ClientSecret: sec,
|
|
|
|
ID: appID,
|
|
|
|
AuthURL: prov.AuthURL,
|
|
|
|
TokenURL: prov.TokenURL,
|
|
|
|
ValidateURL: prov.ExternalAuthURL,
|
|
|
|
AppInstallURL: coderCfg.AppInstallURL,
|
|
|
|
AppInstallationsURL: coderCfg.AppInstallationsURL,
|
|
|
|
NoRefresh: false,
|
|
|
|
Scopes: []string{"openid", "email", "profile"},
|
|
|
|
ExtraTokenKeys: coderCfg.ExtraTokenKeys,
|
2024-01-22 20:46:05 +00:00
|
|
|
DeviceFlow: *deviceFlow,
|
2024-01-15 16:01:41 +00:00
|
|
|
DeviceCodeURL: deviceCodeURL,
|
|
|
|
Regex: *extRegex,
|
|
|
|
DisplayName: coderCfg.DisplayName,
|
|
|
|
DisplayIcon: coderCfg.DisplayIcon,
|
|
|
|
},
|
|
|
|
}
|
2024-01-22 20:46:05 +00:00
|
|
|
|
2024-01-15 16:01:41 +00:00
|
|
|
data, err := json.Marshal([]withClientSecret{cfg})
|
|
|
|
require.NoError(t, err)
|
|
|
|
log.Printf(`--external-auth-providers='%s'`, string(data))
|
2024-05-02 16:19:19 +00:00
|
|
|
log.Println("As primary OIDC auth")
|
|
|
|
log.Printf(`--oidc-issuer-url=%s --oidc-client-id=%s --oidc-client-secret=%s`, idp.IssuerURL().String(), *clientID, *clientSecret)
|
2024-01-15 16:01:41 +00:00
|
|
|
|
|
|
|
log.Println("Press Ctrl+C to exit")
|
|
|
|
c := make(chan os.Signal, 1)
|
|
|
|
signal.Notify(c, os.Interrupt)
|
|
|
|
|
|
|
|
// Block until ctl+c
|
|
|
|
<-c
|
|
|
|
log.Println("Closing")
|
|
|
|
}
|
|
|
|
}
|