mirror of
https://github.com/openziti/ziti.git
synced 2026-10-07 21:31:16 +00:00
218 lines
6.2 KiB
Go
218 lines
6.2 KiB
Go
package tests
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"fmt"
|
|
"github.com/go-resty/resty/v2"
|
|
"github.com/google/uuid"
|
|
"github.com/openziti/ziti/controller/oidc_auth"
|
|
"github.com/zitadel/oidc/v2/pkg/client/rp"
|
|
httphelper "github.com/zitadel/oidc/v2/pkg/http"
|
|
"github.com/zitadel/oidc/v2/pkg/oidc"
|
|
"net"
|
|
"net/http"
|
|
"net/http/cookiejar"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
type testRpServer struct {
|
|
Server *http.Server
|
|
Port string
|
|
Listener net.Listener
|
|
TokenChan <-chan *oidc.Tokens[*oidc.IDTokenClaims]
|
|
CallbackPath string
|
|
CallbackUri string
|
|
LoginUri string
|
|
}
|
|
|
|
func (t *testRpServer) Stop() {
|
|
_ = t.Server.Shutdown(context.Background())
|
|
}
|
|
|
|
func (t *testRpServer) Start() {
|
|
go func() {
|
|
_ = t.Server.Serve(t.Listener)
|
|
}()
|
|
|
|
//allow the server to actually start so connections aren't simply closed by fast followup requests
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|
|
|
|
func newOidcTestRp(apiHost string) (*testRpServer, error) {
|
|
tokenOutChan := make(chan *oidc.Tokens[*oidc.IDTokenClaims], 1)
|
|
result := &testRpServer{
|
|
CallbackPath: "/auth/callback",
|
|
TokenChan: tokenOutChan,
|
|
}
|
|
var err error
|
|
|
|
// random port on localhost for our auth callback server, glob pattern mattching in controller must be on
|
|
result.Listener, err = net.Listen("tcp", ":0")
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not listen on a random port: %w", err)
|
|
}
|
|
|
|
_, result.Port, _ = net.SplitHostPort(result.Listener.Addr().String())
|
|
|
|
result.LoginUri = "http://127.0.0.1:" + result.Port + "/login"
|
|
|
|
key := []byte("test1234test1234")
|
|
urlBase := "https://" + apiHost
|
|
issuer := urlBase + "/oidc"
|
|
clientID := "native"
|
|
clientSecret := ""
|
|
scopes := []string{"openid", "offline_access"}
|
|
result.CallbackUri = "http://127.0.0.1:" + result.Port + result.CallbackPath
|
|
|
|
cookieHandler := httphelper.NewCookieHandler(key, key, httphelper.WithUnsecure())
|
|
jar, _ := cookiejar.New(&cookiejar.Options{})
|
|
httpClient := &http.Client{
|
|
|
|
Transport: &http.Transport{
|
|
TLSClientConfig: &tls.Config{
|
|
InsecureSkipVerify: true,
|
|
},
|
|
Proxy: http.ProxyFromEnvironment,
|
|
ForceAttemptHTTP2: true,
|
|
MaxIdleConns: 100,
|
|
IdleConnTimeout: 90 * time.Second,
|
|
TLSHandshakeTimeout: 10 * time.Second,
|
|
ExpectContinueTimeout: 1 * time.Second,
|
|
},
|
|
CheckRedirect: nil,
|
|
Jar: jar,
|
|
Timeout: 10 * time.Second,
|
|
}
|
|
|
|
options := []rp.Option{
|
|
rp.WithHTTPClient(httpClient),
|
|
rp.WithPKCE(cookieHandler),
|
|
}
|
|
|
|
provider, err := rp.NewRelyingPartyOIDC(issuer, clientID, clientSecret, result.CallbackUri, scopes, options...)
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("could not create rp OIDC: %w", err)
|
|
}
|
|
|
|
state := func() string {
|
|
return uuid.New().String()
|
|
}
|
|
serverMux := http.NewServeMux()
|
|
|
|
authhandler := rp.AuthURLHandler(state, provider, rp.WithPromptURLParam("Welcome back!"))
|
|
loginHandler := http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
|
|
authhandler.ServeHTTP(writer, request)
|
|
})
|
|
|
|
serverMux.Handle("/login", loginHandler)
|
|
|
|
marshalToken := func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens[*oidc.IDTokenClaims], state string, relyingParty rp.RelyingParty) {
|
|
tokenOutChan <- tokens
|
|
_, _ = w.Write([]byte("done!"))
|
|
}
|
|
|
|
serverMux.Handle(result.CallbackPath, rp.CodeExchangeHandler(marshalToken, provider))
|
|
|
|
result.Server = &http.Server{Handler: serverMux}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func Test_Authenticate_Distributed_Auth(t *testing.T) {
|
|
ctx := NewTestContext(t)
|
|
defer ctx.Teardown()
|
|
ctx.StartServer()
|
|
|
|
rpServer, err := newOidcTestRp(ctx.ApiHost)
|
|
ctx.Req.NoError(err)
|
|
|
|
rpServer.Start()
|
|
defer rpServer.Stop()
|
|
|
|
//clientApiUrl, err := url.Parse("https://" + ctx.ApiHost + EdgeClientApiPath)
|
|
//ctx.Req.NoError(err)
|
|
//
|
|
//managementApiUrl, err := url.Parse("https://" + ctx.ApiHost + EdgeManagementApiPath)
|
|
//ctx.Req.NoError(err)
|
|
|
|
t.Run("updb", func(t *testing.T) {
|
|
ctx.testContextChanged(t)
|
|
|
|
client := resty.NewWithClient(ctx.NewHttpClient(ctx.NewTransport()))
|
|
client.SetRedirectPolicy(resty.DomainCheckRedirectPolicy("127.0.0.1", "localhost"))
|
|
resp, err := client.R().Get(rpServer.LoginUri)
|
|
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.Equal(http.StatusOK, resp.StatusCode())
|
|
|
|
authRequestId := resp.Header().Get(oidc_auth.AuthRequestIdHeader)
|
|
ctx.Req.NotEmpty(authRequestId)
|
|
|
|
opLoginUri := "https://" + resp.RawResponse.Request.URL.Host + "/oidc/login/username"
|
|
|
|
resp, err = client.R().SetFormData(map[string]string{"id": authRequestId, "username": ctx.AdminAuthenticator.Username, "password": ctx.AdminAuthenticator.Password}).Post(opLoginUri)
|
|
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.Equal(http.StatusOK, resp.StatusCode())
|
|
|
|
var outTokens *oidc.Tokens[*oidc.IDTokenClaims]
|
|
|
|
select {
|
|
case tokens := <-rpServer.TokenChan:
|
|
outTokens = tokens
|
|
case <-time.After(5 * time.Second):
|
|
ctx.Fail("no tokens received, hit timeout")
|
|
}
|
|
|
|
ctx.Req.NotNil(outTokens)
|
|
ctx.Req.NotEmpty(outTokens.IDToken)
|
|
ctx.Req.NotEmpty(outTokens.IDTokenClaims)
|
|
ctx.Req.NotEmpty(outTokens.AccessToken)
|
|
ctx.Req.NotEmpty(outTokens.RefreshToken)
|
|
})
|
|
|
|
t.Run("cert", func(t *testing.T) {
|
|
ctx.testContextChanged(t)
|
|
|
|
ctx.RequireAdminManagementApiLogin()
|
|
|
|
_, certAuth := ctx.AdminManagementSession.requireCreateIdentityOttEnrollment("test", false)
|
|
|
|
client := resty.NewWithClient(ctx.NewHttpClient(ctx.NewTransportWithClientCert(certAuth.cert, certAuth.key)))
|
|
client.SetRedirectPolicy(resty.DomainCheckRedirectPolicy("127.0.0.1", "localhost"))
|
|
resp, err := client.R().Get(rpServer.LoginUri)
|
|
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.Equal(http.StatusOK, resp.StatusCode())
|
|
|
|
authRequestId := resp.Header().Get(oidc_auth.AuthRequestIdHeader)
|
|
ctx.Req.NotEmpty(authRequestId)
|
|
|
|
opLoginUri := "https://" + resp.RawResponse.Request.URL.Host + "/oidc/login/cert"
|
|
|
|
resp, err = client.R().SetFormData(map[string]string{"id": authRequestId}).Post(opLoginUri)
|
|
|
|
ctx.Req.NoError(err)
|
|
ctx.Req.Equal(http.StatusOK, resp.StatusCode())
|
|
|
|
var outTokens *oidc.Tokens[*oidc.IDTokenClaims]
|
|
|
|
select {
|
|
case tokens := <-rpServer.TokenChan:
|
|
outTokens = tokens
|
|
case <-time.After(5 * time.Second):
|
|
ctx.Fail("no tokens received, hit timeout")
|
|
}
|
|
|
|
ctx.Req.NotNil(outTokens)
|
|
ctx.Req.NotEmpty(outTokens.IDToken)
|
|
ctx.Req.NotEmpty(outTokens.IDTokenClaims)
|
|
ctx.Req.NotEmpty(outTokens.AccessToken)
|
|
ctx.Req.NotEmpty(outTokens.RefreshToken)
|
|
})
|
|
}
|