chore: allow running fake idp with coderd dev (#11555)

* chore: allow running fake idp with coderd dev
This commit is contained in:
Steven Masley 2024-01-11 12:10:57 -06:00 committed by GitHub
parent c91b885a4a
commit e3ad9580e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 108 additions and 10 deletions

17
cmd/testidp/README.md Normal file
View File

@ -0,0 +1,17 @@
# How to use
Start the idp service:
```bash
$ go run main.go
2024-01-10 16:48:01.415 [info] stdlib: 2024/01/10 10:48:01 IDP Issuer URL http://127.0.0.1:44517
2024-01-10 16:48:01.415 [info] stdlib: 2024/01/10 10:48:01 Oauth Flags
2024-01-10 16:48:01.415 [info] stdlib: 2024/01/10 10:48:01 --external-auth-providers='[{"type":"fake","client_id":"f2df566b-a1c9-407a-8b75-480db45c6476","client_secret":"55aca4e3-7b94-44b6-9f45-ecb5e81c560d","auth_url":"http://127.0.0.1:44517/oauth2/authorize","token_url":"http://127.0.0.1:44517/oauth2/token","validate_url":"http://127.0.0.1:44517/oauth2/userinfo","scopes":["openid","email","profile"]}]'
2024-01-10 16:48:01.415 [info] stdlib: 2024/01/10 10:48:01 Press Ctrl+C to exit
```
Then use the flag into your coderd instance:
```bash
develop.sh -- --external-auth-providers='[{"type":"fake","client_id":"f2df566b-a1c9-407a-8b75-480db45c6476","client_secret":"55aca4e3-7b94-44b6-9f45-ecb5e81c560d","auth_url":"http://127.0.0.1:44517/oauth2/authorize","token_url":"http://127.0.0.1:44517/oauth2/token","validate_url":"http://127.0.0.1:44517/oauth2/userinfo","scopes":["openid","email","profile"]}]'
```

58
cmd/testidp/main.go Normal file
View File

@ -0,0 +1,58 @@
package main
import (
"flag"
"log"
"os"
"os/signal"
"testing"
"github.com/golang-jwt/jwt/v4"
"github.com/coder/coder/v2/coderd/coderdtest/oidctest"
)
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)
}
// RunIDP needs the testing.T because our oidctest package requires the
// testing.T.
func RunIDP() func(t *testing.T) {
return func(t *testing.T) {
idp := oidctest.NewFakeIDP(t,
oidctest.WithServing(),
oidctest.WithStaticUserInfo(jwt.MapClaims{}),
oidctest.WithDefaultIDClaims(jwt.MapClaims{}),
)
id, sec := idp.AppCredentials()
prov := idp.WellknownConfig()
log.Println("IDP Issuer URL", idp.IssuerURL())
log.Println("Coderd Flags")
log.Printf(`--external-auth-providers='[{"type":"fake","client_id":"%s","client_secret":"%s","auth_url":"%s","token_url":"%s","validate_url":"%s","scopes":["openid","email","profile"]}]'`,
id, sec, prov.AuthURL, prov.TokenURL, prov.UserInfoURL,
)
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")
}
}

View File

@ -78,9 +78,12 @@ type FakeIDP struct {
// "Authorized Redirect URLs". This can be used to emulate that.
hookValidRedirectURL func(redirectURL string) error
hookUserInfo func(email string) (jwt.MapClaims, error)
hookMutateToken func(token map[string]interface{})
fakeCoderd func(req *http.Request) (*http.Response, error)
hookOnRefresh func(email string) error
// defaultIDClaims is if a new client connects and we didn't preset
// some claims.
defaultIDClaims jwt.MapClaims
hookMutateToken func(token map[string]interface{})
fakeCoderd func(req *http.Request) (*http.Response, error)
hookOnRefresh func(email string) error
// Custom authentication for the client. This is useful if you want
// to test something like PKI auth vs a client_secret.
hookAuthenticateClient func(t testing.TB, req *http.Request) (url.Values, error)
@ -162,6 +165,12 @@ func WithStaticUserInfo(info jwt.MapClaims) func(*FakeIDP) {
}
}
func WithDefaultIDClaims(claims jwt.MapClaims) func(*FakeIDP) {
return func(f *FakeIDP) {
f.defaultIDClaims = claims
}
}
func WithDynamicUserInfo(userInfoFunc func(email string) (jwt.MapClaims, error)) func(*FakeIDP) {
return func(f *FakeIDP) {
f.hookUserInfo = userInfoFunc
@ -679,7 +688,7 @@ func (f *FakeIDP) httpHandler(t testing.TB) http.Handler {
// Always invalidate the code after it is used.
f.codeToStateMap.Delete(code)
idTokenClaims, ok := f.stateToIDTokenClaims.Load(stateStr)
idTokenClaims, ok := f.getClaims(f.stateToIDTokenClaims, stateStr)
if !ok {
t.Errorf("missing id token claims")
http.Error(rw, "missing id token claims", http.StatusBadRequest)
@ -699,7 +708,7 @@ func (f *FakeIDP) httpHandler(t testing.TB) http.Handler {
return
}
idTokenClaims, ok := f.refreshIDTokenClaims.Load(refreshToken)
idTokenClaims, ok := f.getClaims(f.refreshIDTokenClaims, refreshToken)
if !ok {
t.Errorf("missing id token claims in refresh")
http.Error(rw, "missing id token claims in refresh", http.StatusBadRequest)
@ -971,6 +980,10 @@ func (f *FakeIDP) ExternalAuthConfig(t testing.TB, id string, custom *ExternalAu
return cfg
}
func (f *FakeIDP) AppCredentials() (clientID string, clientSecret string) {
return f.clientID, f.clientSecret
}
// OIDCConfig returns the OIDC config to use for Coderd.
func (f *FakeIDP) OIDCConfig(t testing.TB, scopes []string, opts ...func(cfg *coderd.OIDCConfig)) *coderd.OIDCConfig {
t.Helper()
@ -1023,6 +1036,17 @@ func (f *FakeIDP) OIDCConfig(t testing.TB, scopes []string, opts ...func(cfg *co
return cfg
}
func (f *FakeIDP) getClaims(m *syncmap.Map[string, jwt.MapClaims], key string) (jwt.MapClaims, bool) {
v, ok := m.Load(key)
if !ok {
if f.defaultIDClaims != nil {
return f.defaultIDClaims, true
}
return nil, false
}
return v, true
}
func httpErrorCode(defaultCode int, err error) int {
var stautsErr statusHookError
status := defaultCode

View File

@ -1790,11 +1790,10 @@ Write out the current server config as YAML to stdout.`,
// Env handling is done in cli.ReadGitAuthFromEnvironment
Name: "External Auth Providers",
Description: "External Authentication providers.",
// We need extra scrutiny to ensure this works, is documented, and
// tested before enabling.
YAML: "externalAuthProviders",
Value: &c.ExternalAuthConfigs,
Hidden: true,
YAML: "externalAuthProviders",
Flag: "external-auth-providers",
Value: &c.ExternalAuthConfigs,
Hidden: true,
},
{
Name: "Custom wgtunnel Host",