mirror of https://github.com/coder/coder.git
chore: allow running fake idp with coderd dev (#11555)
* chore: allow running fake idp with coderd dev
This commit is contained in:
parent
c91b885a4a
commit
e3ad9580e9
|
@ -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"]}]'
|
||||
```
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue