mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-14 07:27:39 +00:00
chore: add acceptance tests with saml sp
This commit is contained in:
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -97,6 +97,10 @@ jobs:
|
||||
run: ZITADEL_DEV_UID=root pnpm run-sink
|
||||
if: ${{ matrix.command == 'test:acceptance' }}
|
||||
|
||||
- name: Run SAML SP
|
||||
run: ZITADEL_DEV_UID=root pnpm run-samlsp
|
||||
if: ${{ matrix.command == 'test:acceptance' }}
|
||||
|
||||
- name: Create Cloud Env File
|
||||
run: |
|
||||
if [ "${{ matrix.command }}" == "test:acceptance:prod" ]; then
|
||||
|
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.19-alpine
|
||||
FROM golang:1.24-alpine
|
||||
RUN apk add curl jq
|
||||
COPY setup.sh /setup.sh
|
||||
RUN chmod +x /setup.sh
|
||||
|
@@ -1,7 +1,7 @@
|
||||
services:
|
||||
zitadel:
|
||||
user: "${ZITADEL_DEV_UID}"
|
||||
image: "${ZITADEL_IMAGE:-ghcr.io/zitadel/zitadel:v2.67.2}"
|
||||
image: "${ZITADEL_IMAGE:-ghcr.io/zitadel/zitadel:dc64e35128108d70471c7a5b9ad1dfc2c7c4c654}"
|
||||
command: 'start-from-init --masterkey "MasterkeyNeedsToHave32Characters" --tlsMode disabled --config /zitadel.yaml --steps /zitadel.yaml'
|
||||
ports:
|
||||
- "8080:8080"
|
||||
@@ -11,6 +11,8 @@ services:
|
||||
depends_on:
|
||||
db:
|
||||
condition: "service_healthy"
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
|
||||
db:
|
||||
restart: "always"
|
||||
@@ -57,7 +59,7 @@ services:
|
||||
condition: "service_completed_successfully"
|
||||
|
||||
sink:
|
||||
image: golang:1.19-alpine
|
||||
image: golang:1.24-alpine
|
||||
container_name: sink
|
||||
command: go run /sink/main.go -port '3333' -email '/email' -sms '/sms' -notification '/notification'
|
||||
ports:
|
||||
|
5
acceptance/saml/Dockerfile
Normal file
5
acceptance/saml/Dockerfile
Normal file
@@ -0,0 +1,5 @@
|
||||
FROM golang:1.24-alpine
|
||||
RUN apk add curl jq
|
||||
COPY setup.sh /setup.sh
|
||||
RUN chmod +x /setup.sh
|
||||
ENTRYPOINT [ "/setup.sh" ]
|
35
acceptance/saml/docker-compose.yaml
Normal file
35
acceptance/saml/docker-compose.yaml
Normal file
@@ -0,0 +1,35 @@
|
||||
services:
|
||||
samlsp:
|
||||
image: golang:1.24-alpine
|
||||
container_name: samlsp
|
||||
command: go run main.go -host 'http://localhost' -port '8001' -idp 'http://host.docker.internal:3000/saml/v2/metadata'
|
||||
working_dir: /saml
|
||||
ports:
|
||||
- 8001:8001
|
||||
volumes:
|
||||
- "./:/saml"
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
|
||||
wait_for_samlsp:
|
||||
image: curlimages/curl:8.00.1
|
||||
command: /bin/sh -c "until curl -s -o /dev/null -i -f http://samlsp:8001/saml/metadata; do echo 'waiting' && sleep 1; done; echo 'ready' && sleep 5;" || false
|
||||
depends_on:
|
||||
- samlsp
|
||||
|
||||
setup-samlsp:
|
||||
user: "${ZITADEL_DEV_UID}"
|
||||
container_name: setup-samlsp
|
||||
build: .
|
||||
environment:
|
||||
PAT_FILE: /pat/zitadel-admin-sa.pat
|
||||
ZITADEL_API_URL: http://host.docker.internal:8080
|
||||
LOGIN_URI: http://localhost:3000
|
||||
SAML_SP_METADATA: http://host.docker.internal:8001/saml/metadata
|
||||
volumes:
|
||||
- "../pat:/pat"
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
depends_on:
|
||||
wait_for_samlsp:
|
||||
condition: "service_completed_successfully"
|
16
acceptance/saml/go.mod
Normal file
16
acceptance/saml/go.mod
Normal file
@@ -0,0 +1,16 @@
|
||||
module github.com/zitadel/typescript/acceptance/saml
|
||||
|
||||
go 1.24.0
|
||||
|
||||
require github.com/crewjam/saml v0.4.14
|
||||
|
||||
require (
|
||||
github.com/beevik/etree v1.5.0 // indirect
|
||||
github.com/crewjam/httperr v0.2.0 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
|
||||
github.com/jonboulle/clockwork v0.5.0 // indirect
|
||||
github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/russellhaering/goxmldsig v1.5.0 // indirect
|
||||
golang.org/x/crypto v0.36.0 // indirect
|
||||
)
|
63
acceptance/saml/go.sum
Normal file
63
acceptance/saml/go.sum
Normal file
@@ -0,0 +1,63 @@
|
||||
github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
|
||||
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
|
||||
github.com/beevik/etree v1.5.0 h1:iaQZFSDS+3kYZiGoc9uKeOkUY3nYMXOKLl6KIJxiJWs=
|
||||
github.com/beevik/etree v1.5.0/go.mod h1:gPNJNaBGVZ9AwsidazFZyygnd+0pAU38N4D+WemwKNs=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo=
|
||||
github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4=
|
||||
github.com/crewjam/saml v0.4.14 h1:g9FBNx62osKusnFzs3QTN5L9CVA/Egfgm+stJShzw/c=
|
||||
github.com/crewjam/saml v0.4.14/go.mod h1:UVSZCf18jJkk6GpWNVqcyQJMD5HsRugBPf4I1nl2mME=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
|
||||
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
||||
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
|
||||
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU=
|
||||
github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/russellhaering/goxmldsig v1.3.0 h1:DllIWUgMy0cRUMfGiASiYEa35nsieyD3cigIwLonTPM=
|
||||
github.com/russellhaering/goxmldsig v1.3.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw=
|
||||
github.com/russellhaering/goxmldsig v1.5.0 h1:AU2UkkYIUOTyZRbe08XMThaOCelArgvNfYapcmSjBNw=
|
||||
github.com/russellhaering/goxmldsig v1.5.0/go.mod h1:x98CjQNFJcWfMxeOrMnMKg70lvDP6tE0nTaeUnjXDmk=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/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=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
118
acceptance/saml/main.go
Normal file
118
acceptance/saml/main.go
Normal file
@@ -0,0 +1,118 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/crewjam/saml/samlsp"
|
||||
)
|
||||
|
||||
var keyPair = func() tls.Certificate {
|
||||
cert := []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIDITCCAgmgAwIBAgIUKjAUmxsHO44X+/TKBNciPgNl1GEwDQYJKoZIhvcNAQEL
|
||||
BQAwIDEeMBwGA1UEAwwVbXlzZXJ2aWNlLmV4YW1wbGUuY29tMB4XDTI0MTIxOTEz
|
||||
Mzc1MVoXDTI1MTIxOTEzMzc1MVowIDEeMBwGA1UEAwwVbXlzZXJ2aWNlLmV4YW1w
|
||||
bGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0QYuJsayILRI
|
||||
hVT7G1DlitVSXnt1iw3gEXJZfe81Egz06fUbvXF6Yo1LJmwYpqe/rm+hf4FNUb8e
|
||||
2O+LH2FieA9FkVe4P2gKOzw87A/KxvpV8stgNgl4LlqRCokbc1AzeE/NiLr5TcTD
|
||||
RXm3DUcYxXxinprtDu2jftFysaOZmNAukvE/iL6qS3X6ggVEDDM7tY9n5FV2eJ4E
|
||||
p0ImKfypi2aZYROxOK+v5x9ryFRMl4y07lMDvmtcV45uXYmfGNCgG9PNf91Kk/mh
|
||||
JxEQbxycJwFoSi9XWljR8ahPdO11LXG7Dsj/RVbY8k2LdKNstl6Ae3aCpbe9u2Pj
|
||||
vxYs1bVJuQIDAQABo1MwUTAdBgNVHQ4EFgQU+mRVN5HYJWgnpopReaLhf2cMcoYw
|
||||
HwYDVR0jBBgwFoAU+mRVN5HYJWgnpopReaLhf2cMcoYwDwYDVR0TAQH/BAUwAwEB
|
||||
/zANBgkqhkiG9w0BAQsFAAOCAQEABJpHVuc9tGhD04infRVlofvqXIUizTlOrjZX
|
||||
vozW9pIhSWEHX8o+sJP8AMZLnrsdq+bm0HE0HvgYrw7Lb8pd4FpR46TkFHjeukoj
|
||||
izqfgckjIBl2nwPGlynbKA0/U/rTCSxVt7XiAn+lgYUGIpOzNdk06/hRMitrMNB7
|
||||
t2C97NseVC4b1ZgyFrozsefCfUmD8IJF0+XJ4Wzmsh0jRrI8koCtVmPYnKn6vw1b
|
||||
cZprg/97CWHYrsavd406wOB60CMtYl83Q16ucOF1dretDFqJC5kY+aFLvuqfag2+
|
||||
kIaoPV1MnGsxveQyyHdOsEatS5XOv/1OWcmnvePDPxcvb9jCcw==
|
||||
-----END CERTIFICATE-----
|
||||
`)
|
||||
key := []byte(`-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRBi4mxrIgtEiF
|
||||
VPsbUOWK1VJee3WLDeARcll97zUSDPTp9Ru9cXpijUsmbBimp7+ub6F/gU1Rvx7Y
|
||||
74sfYWJ4D0WRV7g/aAo7PDzsD8rG+lXyy2A2CXguWpEKiRtzUDN4T82IuvlNxMNF
|
||||
ebcNRxjFfGKemu0O7aN+0XKxo5mY0C6S8T+IvqpLdfqCBUQMMzu1j2fkVXZ4ngSn
|
||||
QiYp/KmLZplhE7E4r6/nH2vIVEyXjLTuUwO+a1xXjm5diZ8Y0KAb081/3UqT+aEn
|
||||
ERBvHJwnAWhKL1daWNHxqE907XUtcbsOyP9FVtjyTYt0o2y2XoB7doKlt727Y+O/
|
||||
FizVtUm5AgMBAAECggEACak+l5f6Onj+u5vrjc4JyAaXW6ra6loSM9g8Uu3sHukW
|
||||
plwoA7Pzp0u20CAxrP1Gpqw984/hSCCcb0Q2ItWMWLaC/YZni5W2WFnOyo3pzlPa
|
||||
hmH4UNMT+ReCSfF/oW8w69QLcNEMjhfEu0i2iWBygIlA4SoRwC2Db6yEX7nLMwUB
|
||||
6AICid9hfeACNRz/nq5ytdcHdmcB7Ptgb9jLiXr6RZw26g5AsRPHU3LdcyZAOXjP
|
||||
aUHriHuHQFKAVkoEUxslvCB6ePCTCpB0bSAuzQbeGoY8fmvmNSCvJ1vrH5hiSUYp
|
||||
Axtl5iNgFl5o9obb0eBYlY9x3pMSz0twdbCwfR7HAQKBgQDtWhmFm0NaJALoY+tq
|
||||
lIIC0EOMSrcRIlgeXr6+g8womuDOMi5m/Nr5Mqt4mPOdP4HytrQb+a/ZmEm17KHh
|
||||
mQb1vwH8ffirCBHbPNC1vwSNoxDKv9E6OysWlKiOzxPFSVZr3dKl2EMX6qi17n0l
|
||||
LBrGXXaNPgYiHSmwBA5CZvvouQKBgQDhclGJfZfuoubQkUuz8yOA2uxalh/iUmQ/
|
||||
G8ac6/w7dmnL9pXehqCWh06SeC3ZvW7yrf7IIGx4sTJji2FzQ+8Ta6pPELMyBEXr
|
||||
1VirIFrlNVMlMQEbZcbzdzEhchM1RUpZJtl3b4amvH21UcRB69d9klcDRisKoFRm
|
||||
k0P9QLHpAQKBgQDh5J9nphZa4u0ViYtTW1XFIbs3+R/0IbCl7tww67TRbF3KQL4i
|
||||
7EHna88ALumkXf3qJvKRsXgoaqS0jSqgUAjst8ZHLQkOldaQxneIkezedDSWEisp
|
||||
9YgTrJYjnHefiyXB8VL63jE0wPOiewEF8Mzmv6sFz+L8cq7rQ2Di16qmmQKBgQDH
|
||||
bvCwVxkrMpJK2O2GH8U9fOzu6bUE6eviY/jb4mp8U7EdjGJhuuieoM2iBoxQ/SID
|
||||
rmYftYcfcWlo4+juJZ99p5W+YcCTs3IDQPUyVOnzr6uA0Avxp6RKxhsBQj+5tTUj
|
||||
Dpn77P3JzB7MYqvhwPcdD3LH46+5s8FWCFpx02RPAQKBgARbngtggfifatcsMC7n
|
||||
lSv/FVLH7LYQAHdoW/EH5Be7FeeP+eQvGXwh1dgl+u0VZO8FvI8RwFganpBRR2Nc
|
||||
ZSBRIb0fSUlTvIsckSWjpEvUJUomJXyi4PIZAfNvd9/u1uLInQiCDtObwb6hnLTU
|
||||
FHHEZ+dR4eMaJp6PhNm8hu2O
|
||||
-----END PRIVATE KEY-----
|
||||
`)
|
||||
|
||||
kp, err := tls.X509KeyPair(cert, key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
kp.Leaf, err = x509.ParseCertificate(kp.Certificate[0])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return kp
|
||||
}()
|
||||
|
||||
func hello(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "Hello, %s!", samlsp.AttributeFromContext(r.Context(), "displayName"))
|
||||
}
|
||||
|
||||
func main() {
|
||||
idpURL := flag.String("idp", "http://localhost:3000/saml/v2/metadata", "url to idp metadata, proxied through typescript")
|
||||
host := flag.String("host", "http://localhost", "url for sp")
|
||||
port := flag.String("port", "8001", "port for sp")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
idpMetadataURL, err := url.Parse(*idpURL)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
idpMetadata, err := samlsp.FetchMetadata(context.Background(), http.DefaultClient,
|
||||
*idpMetadataURL)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("idpMetadata: %+v\n", idpMetadata)
|
||||
rootURL, err := url.Parse(*host + ":" + *port)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
samlSP, err := samlsp.New(samlsp.Options{
|
||||
URL: *rootURL,
|
||||
Key: keyPair.PrivateKey.(*rsa.PrivateKey),
|
||||
Certificate: keyPair.Leaf,
|
||||
IDPMetadata: idpMetadata,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
app := http.HandlerFunc(hello)
|
||||
http.Handle("/hello", samlSP.RequireAccount(app))
|
||||
http.Handle("/saml/", samlSP)
|
||||
http.ListenAndServe(":"+*port, nil)
|
||||
}
|
36
acceptance/saml/setup.sh
Executable file
36
acceptance/saml/setup.sh
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -ex
|
||||
|
||||
PAT_FILE=${PAT_FILE:-../pat/zitadel-admin-sa.pat}
|
||||
ZITADEL_API_URL="${ZITADEL_API_URL:-"http://localhost:8080"}"
|
||||
LOGIN_URI="${LOGIN_URI:-"http://localhost:3000"}"
|
||||
SAML_SP_METADATA="${SAML_SP_METADATA:-"http://samlsp:8081/saml/metadata"}"
|
||||
|
||||
if [ -z "${PAT}" ]; then
|
||||
echo "Reading PAT from file ${PAT_FILE}"
|
||||
PAT=$(cat ${PAT_FILE})
|
||||
fi
|
||||
|
||||
#################################################################
|
||||
# SAML Application
|
||||
#################################################################
|
||||
|
||||
SAML_PROJECT_RESPONSE=$(curl -s --request POST \
|
||||
--url "${ZITADEL_API_URL}/management/v1/projects" \
|
||||
--header "Authorization: Bearer ${PAT}" \
|
||||
--header "Host: ${ZITADEL_API_DOMAIN}" \
|
||||
--header "Content-Type: application/json" \
|
||||
-d "{ \"name\": \"SAML\", \"projectRoleAssertion\": true, \"projectRoleCheck\": true, \"hasProjectCheck\": true, \"privateLabelingSetting\": \"PRIVATE_LABELING_SETTING_UNSPECIFIED\"}")
|
||||
echo "Received SAML Project response: ${SAML_PROJECT_RESPONSE}"
|
||||
|
||||
SAML_PROJECT_ID=$(echo ${SAML_PROJECT_RESPONSE} | jq -r '. | .id')
|
||||
echo "Received Project ID: ${SAML_PROJECT_ID}"
|
||||
|
||||
SAML_APP_RESPONSE=$(curl -s --request POST \
|
||||
--url "${ZITADEL_API_URL}/management/v1/projects/${SAML_PROJECT_ID}/apps/saml" \
|
||||
--header "Authorization: Bearer ${PAT}" \
|
||||
--header "Host: ${ZITADEL_API_DOMAIN}" \
|
||||
--header "Content-Type: application/json" \
|
||||
-d "{ \"name\": \"SAML\", \"metadataUrl\": \"${SAML_SP_METADATA}\", \"loginVersion\": { \"loginV2\": { \"baseUri\": \"${LOGIN_URI}\" }}}")
|
||||
echo "Received SAML App response: ${SAML_APP_RESPONSE}"
|
@@ -17,6 +17,40 @@ if [ -z "${PAT}" ]; then
|
||||
PAT=$(cat ${PAT_FILE})
|
||||
fi
|
||||
|
||||
#################################################################
|
||||
# ServiceAccount as Login Client
|
||||
#################################################################
|
||||
|
||||
SERVICEACCOUNT_RESPONSE=$(curl -s --request POST \
|
||||
--url "${ZITADEL_API_INTERNAL_URL}/management/v1/users/machine" \
|
||||
--header "Authorization: Bearer ${PAT}" \
|
||||
--header "Host: ${ZITADEL_API_DOMAIN}" \
|
||||
--header "Content-Type: application/json" \
|
||||
-d "{\"userName\": \"login\", \"name\": \"Login v2\", \"description\": \"Serviceaccount for Login v2\", \"accessTokenType\": \"ACCESS_TOKEN_TYPE_BEARER\"}")
|
||||
echo "Received ServiceAccount response: ${SERVICEACCOUNT_RESPONSE}"
|
||||
|
||||
SERVICEACCOUNT_ID=$(echo ${SERVICEACCOUNT_RESPONSE} | jq -r '. | .userId')
|
||||
echo "Received ServiceAccount ID: ${SERVICEACCOUNT_ID}"
|
||||
|
||||
MEMBER_RESPONSE=$(curl -s --request POST \
|
||||
--url "${ZITADEL_API_INTERNAL_URL}/admin/v1/members" \
|
||||
--header "Authorization: Bearer ${PAT}" \
|
||||
--header "Host: ${ZITADEL_API_DOMAIN}" \
|
||||
--header "Content-Type: application/json" \
|
||||
-d "{\"userId\": \"${SERVICEACCOUNT_ID}\", \"roles\": [\"IAM_LOGIN_CLIENT\"]}")
|
||||
echo "Received Member response: ${MEMBER_RESPONSE}"
|
||||
|
||||
SA_PAT_RESPONSE=$(curl -s --request POST \
|
||||
--url "${ZITADEL_API_INTERNAL_URL}/management/v1/users/${SERVICEACCOUNT_ID}/pats" \
|
||||
--header "Authorization: Bearer ${PAT}" \
|
||||
--header "Host: ${ZITADEL_API_DOMAIN}" \
|
||||
--header "Content-Type: application/json" \
|
||||
-d "{\"expirationDate\": \"2519-04-01T08:45:00.000000Z\"}")
|
||||
echo "Received Member response: ${MEMBER_RESPONSE}"
|
||||
|
||||
SA_PAT=$(echo ${SA_PAT_RESPONSE} | jq -r '. | .token')
|
||||
echo "Received ServiceAccount Token: ${SA_PAT}"
|
||||
|
||||
#################################################################
|
||||
# Environment files
|
||||
#################################################################
|
||||
@@ -27,7 +61,8 @@ WRITE_TEST_ENVIRONMENT_FILE=${WRITE_TEST_ENVIRONMENT_FILE:-$(dirname "$0")/../ac
|
||||
echo "Writing environment file to ${WRITE_TEST_ENVIRONMENT_FILE} when done."
|
||||
|
||||
echo "ZITADEL_API_URL=${ZITADEL_API_URL}
|
||||
ZITADEL_SERVICE_USER_TOKEN=${PAT}
|
||||
ZITADEL_SERVICE_USER_TOKEN=${SA_PAT}
|
||||
ZITADEL_ADMIN_TOKEN=${PAT}
|
||||
SINK_NOTIFICATION_URL=${SINK_NOTIFICATION_URL}
|
||||
EMAIL_VERIFICATION=true
|
||||
DEBUG=true"| tee "${WRITE_ENVIRONMENT_FILE}" "${WRITE_TEST_ENVIRONMENT_FILE}" > /dev/null
|
||||
|
@@ -1,3 +1,3 @@
|
||||
module github.com/zitadel/typescript/acceptance/sink
|
||||
|
||||
go 1.22.6
|
||||
go 1.24.0
|
||||
|
39
acceptance/tests/saml-username-password.spec.ts
Normal file
39
acceptance/tests/saml-username-password.spec.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { faker } from "@faker-js/faker";
|
||||
import { test as base } from "@playwright/test";
|
||||
import dotenv from "dotenv";
|
||||
import path from "path";
|
||||
import { loginname } from "./loginname";
|
||||
import { password } from "./password";
|
||||
import { PasswordUser } from "./user";
|
||||
import {startSAML} from "./saml";
|
||||
import {selectNewAccount} from "./select-account";
|
||||
|
||||
// Read from ".env" file.
|
||||
dotenv.config({ path: path.resolve(__dirname, ".env.local") });
|
||||
|
||||
const test = base.extend<{ user: PasswordUser }>({
|
||||
user: async ({ page }, use) => {
|
||||
const user = new PasswordUser({
|
||||
email: faker.internet.email(),
|
||||
isEmailVerified: true,
|
||||
firstName: faker.person.firstName(),
|
||||
lastName: faker.person.lastName(),
|
||||
organization: "",
|
||||
phone: faker.phone.number(),
|
||||
isPhoneVerified: false,
|
||||
password: "Password1!",
|
||||
passwordChangeRequired: false,
|
||||
});
|
||||
await user.ensure(page);
|
||||
await use(user);
|
||||
await user.cleanup();
|
||||
},
|
||||
});
|
||||
|
||||
test("saml username and password login", async ({ user, page }) => {
|
||||
await startSAML(page)
|
||||
await selectNewAccount(page)
|
||||
await loginname(page, user.getUsername());
|
||||
await password(page, user.getPassword());
|
||||
// currently fails because of issuer problems
|
||||
});
|
5
acceptance/tests/saml.ts
Normal file
5
acceptance/tests/saml.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { expect, Page } from "@playwright/test";
|
||||
|
||||
export async function startSAML(page: Page) {
|
||||
await page.goto("http://localhost:8001/hello");
|
||||
}
|
5
acceptance/tests/select-account.ts
Normal file
5
acceptance/tests/select-account.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import {Page} from "@playwright/test";
|
||||
|
||||
export async function selectNewAccount(page: Page) {
|
||||
await page.getByRole('link', {name: 'Add another account'}).click();
|
||||
}
|
@@ -53,7 +53,7 @@ async function deleteCall(url: string) {
|
||||
try {
|
||||
const response = await axios.delete(url, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${process.env.ZITADEL_SERVICE_USER_TOKEN}`,
|
||||
Authorization: `Bearer ${process.env.ZITADEL_ADMIN_TOKEN}`,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -87,7 +87,7 @@ async function listCall(url: string, data: any): Promise<any> {
|
||||
const response = await axios.post(url, data, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${process.env.ZITADEL_SERVICE_USER_TOKEN}`,
|
||||
Authorization: `Bearer ${process.env.ZITADEL_ADMIN_TOKEN}`,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -123,7 +123,7 @@ async function pushCall(url: string, data: any) {
|
||||
const response = await axios.post(url, data, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${process.env.ZITADEL_SERVICE_USER_TOKEN}`,
|
||||
Authorization: `Bearer ${process.env.ZITADEL_ADMIN_TOKEN}`,
|
||||
},
|
||||
});
|
||||
|
||||
|
@@ -473,16 +473,11 @@ export async function GET(request: NextRequest) {
|
||||
if (url && binding.case === "redirect") {
|
||||
return NextResponse.redirect(url);
|
||||
} else if (url && binding.case === "post") {
|
||||
const formData = {
|
||||
RelayState: binding.value.relayState,
|
||||
SAMLResponse: binding.value.samlResponse,
|
||||
};
|
||||
|
||||
const redirectUrl = constructUrl(request, "/saml-post");
|
||||
|
||||
redirectUrl.searchParams.set("url", url);
|
||||
redirectUrl.searchParams.set("RelayState", formData.RelayState);
|
||||
redirectUrl.searchParams.set("SAMLResponse", formData.SAMLResponse);
|
||||
redirectUrl.searchParams.set("RelayState", binding.value.relayState);
|
||||
redirectUrl.searchParams.set("SAMLResponse", binding.value.samlResponse);
|
||||
|
||||
return NextResponse.redirect(redirectUrl.toString());
|
||||
} else {
|
||||
|
@@ -22,7 +22,7 @@ export async function middleware(request: NextRequest) {
|
||||
|
||||
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
|
||||
|
||||
const instanceHost = `${serviceUrl}`.replace("https://", "");
|
||||
const instanceHost = `${serviceUrl}`.replace("https://", "").replace("http://", "");
|
||||
|
||||
const requestHeaders = new Headers(request.headers);
|
||||
|
||||
|
@@ -26,6 +26,7 @@
|
||||
"release": "turbo run build --filter=login^... && changeset publish",
|
||||
"run-zitadel": "docker compose -f ./acceptance/docker-compose.yaml run setup",
|
||||
"run-sink": "docker compose -f ./acceptance/docker-compose.yaml up -d sink",
|
||||
"run-samlsp": "docker compose -f ./acceptance/saml/docker-compose.yaml up -d",
|
||||
"stop": "docker compose -f ./acceptance/docker-compose.yaml stop"
|
||||
},
|
||||
"pnpm": {
|
||||
|
Reference in New Issue
Block a user