mirror of
https://github.com/zitadel/zitadel.git
synced 2025-01-09 19:43:40 +00:00
Merge branch 'main' into next-rc
This commit is contained in:
commit
977ee004e3
10
.github/workflows/build.yml
vendored
10
.github/workflows/build.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
||||
with:
|
||||
node_version: "20"
|
||||
buf_version: "latest"
|
||||
go_version: "1.22"
|
||||
go_version: "1.23"
|
||||
|
||||
console:
|
||||
uses: ./.github/workflows/console.yml
|
||||
@ -43,7 +43,7 @@ jobs:
|
||||
needs: [core, console, version]
|
||||
uses: ./.github/workflows/compile.yml
|
||||
with:
|
||||
go_version: "1.22"
|
||||
go_version: "1.23"
|
||||
core_cache_key: ${{ needs.core.outputs.cache_key }}
|
||||
console_cache_key: ${{ needs.console.outputs.cache_key }}
|
||||
core_cache_path: ${{ needs.core.outputs.cache_path }}
|
||||
@ -54,7 +54,7 @@ jobs:
|
||||
needs: core
|
||||
uses: ./.github/workflows/core-unit-test.yml
|
||||
with:
|
||||
go_version: "1.22"
|
||||
go_version: "1.23"
|
||||
core_cache_key: ${{ needs.core.outputs.cache_key }}
|
||||
core_cache_path: ${{ needs.core.outputs.cache_path }}
|
||||
secrets:
|
||||
@ -64,7 +64,7 @@ jobs:
|
||||
needs: core
|
||||
uses: ./.github/workflows/core-integration-test.yml
|
||||
with:
|
||||
go_version: "1.22"
|
||||
go_version: "1.23"
|
||||
core_cache_key: ${{ needs.core.outputs.cache_key }}
|
||||
core_cache_path: ${{ needs.core.outputs.cache_path }}
|
||||
secrets:
|
||||
@ -74,7 +74,7 @@ jobs:
|
||||
needs: [core, console]
|
||||
uses: ./.github/workflows/lint.yml
|
||||
with:
|
||||
go_version: "1.22"
|
||||
go_version: "1.23"
|
||||
node_version: "18"
|
||||
buf_version: "latest"
|
||||
go_lint_version: "v1.62.2"
|
||||
|
@ -12,6 +12,7 @@ If you are using Zitadel, please consider adding yourself as a user with a quick
|
||||
| ----------------------- | -------------------------------------------------------------------- | ----------------------------------------------- |
|
||||
| Zitadel | [@fforootd](https://github.com/fforootd) (and many more) | Zitadel Cloud makes heavy use of of Zitadel ;-) |
|
||||
| Rawkode Academy | [@RawkodeAcademy](https://github.com/RawkodeAcademy) | Rawkode Academy Platform & Zulip use Zitadel for all user and M2M authentication |
|
||||
| XPeditionist | [@XPeditionistTravel](https://github.com/XPeditionistTravel) | An innovative all-in-one travel solution use Zitadel as complete auth solution. |
|
||||
| devOS: Sanity Edition | [@devOS-Sanity-Edition](https://github.com/devOS-Sanity-Edition) | Uses SSO Auth for every piece of our internal and external infrastructure |
|
||||
| CNAP.tech | [@cnap-tech](https://github.com/cnap-tech) | Using Zitadel for authentication and authorization in cloud-native applications |
|
||||
| Minekube | [@minekube](https://github.com/minekube) | Leveraging Zitadel for secure user authentication in gaming infrastructure |
|
||||
|
14
go.mod
14
go.mod
@ -1,6 +1,6 @@
|
||||
module github.com/zitadel/zitadel
|
||||
|
||||
go 1.22.2
|
||||
go 1.23.4
|
||||
|
||||
require (
|
||||
cloud.google.com/go/profiler v0.4.1
|
||||
@ -80,12 +80,12 @@ require (
|
||||
go.opentelemetry.io/otel/sdk/metric v1.29.0
|
||||
go.opentelemetry.io/otel/trace v1.29.0
|
||||
go.uber.org/mock v0.5.0
|
||||
golang.org/x/crypto v0.29.0
|
||||
golang.org/x/crypto v0.31.0
|
||||
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8
|
||||
golang.org/x/net v0.31.0
|
||||
golang.org/x/net v0.33.0
|
||||
golang.org/x/oauth2 v0.23.0
|
||||
golang.org/x/sync v0.9.0
|
||||
golang.org/x/text v0.20.0
|
||||
golang.org/x/sync v0.10.0
|
||||
golang.org/x/text v0.21.0
|
||||
google.golang.org/api v0.187.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240822170219-fc7c04adadcd
|
||||
google.golang.org/grpc v1.65.0
|
||||
@ -107,7 +107,7 @@ require (
|
||||
github.com/go-sql-driver/mysql v1.7.1 // indirect
|
||||
github.com/go-webauthn/x v0.1.9 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
@ -205,7 +205,7 @@ require (
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.29.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
golang.org/x/sys v0.27.0
|
||||
golang.org/x/sys v0.28.0
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
|
24
go.sum
24
go.sum
@ -269,8 +269,8 @@ github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/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/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
@ -809,8 +809,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
||||
@ -873,8 +873,8 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -890,8 +890,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -934,8 +934,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@ -950,8 +950,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
|
@ -35,7 +35,7 @@ func TestServer_ListIAMMemberRoles(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestServer_ListIAMMembers(t *testing.T) {
|
||||
user := Instance.CreateHumanUserVerified(AdminCTX, Instance.DefaultOrg.Id, gofakeit.Email())
|
||||
user := Instance.CreateHumanUserVerified(AdminCTX, Instance.DefaultOrg.Id, gofakeit.Email(), gofakeit.Phone())
|
||||
_, err := Client.AddIAMMember(AdminCTX, &admin_pb.AddIAMMemberRequest{
|
||||
UserId: user.GetUserId(),
|
||||
Roles: iamRoles,
|
||||
@ -116,7 +116,7 @@ func TestServer_ListIAMMembers(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestServer_AddIAMMember(t *testing.T) {
|
||||
user := Instance.CreateHumanUserVerified(AdminCTX, Instance.DefaultOrg.Id, gofakeit.Email())
|
||||
user := Instance.CreateHumanUserVerified(AdminCTX, Instance.DefaultOrg.Id, gofakeit.Email(), gofakeit.Phone())
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *admin_pb.AddIAMMemberRequest
|
||||
@ -190,7 +190,7 @@ func TestServer_AddIAMMember(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestServer_UpdateIAMMember(t *testing.T) {
|
||||
user := Instance.CreateHumanUserVerified(AdminCTX, Instance.DefaultOrg.Id, gofakeit.Email())
|
||||
user := Instance.CreateHumanUserVerified(AdminCTX, Instance.DefaultOrg.Id, gofakeit.Email(), gofakeit.Phone())
|
||||
_, err := Client.AddIAMMember(AdminCTX, &admin_pb.AddIAMMemberRequest{
|
||||
UserId: user.GetUserId(),
|
||||
Roles: []string{"IAM_OWNER"},
|
||||
@ -271,7 +271,7 @@ func TestServer_UpdateIAMMember(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestServer_RemoveIAMMember(t *testing.T) {
|
||||
user := Instance.CreateHumanUserVerified(AdminCTX, Instance.DefaultOrg.Id, gofakeit.Email())
|
||||
user := Instance.CreateHumanUserVerified(AdminCTX, Instance.DefaultOrg.Id, gofakeit.Email(), gofakeit.Phone())
|
||||
_, err := Client.AddIAMMember(AdminCTX, &admin_pb.AddIAMMemberRequest{
|
||||
UserId: user.GetUserId(),
|
||||
Roles: []string{"IAM_OWNER"},
|
||||
|
@ -39,7 +39,7 @@ func TestServer_ListOrgMemberRoles(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestServer_ListOrgMembers(t *testing.T) {
|
||||
user := Instance.CreateHumanUserVerified(OrgCTX, Instance.DefaultOrg.Id, gofakeit.Email())
|
||||
user := Instance.CreateHumanUserVerified(OrgCTX, Instance.DefaultOrg.Id, gofakeit.Email(), gofakeit.Phone())
|
||||
_, err := Client.AddOrgMember(OrgCTX, &mgmt_pb.AddOrgMemberRequest{
|
||||
UserId: user.GetUserId(),
|
||||
Roles: iamRoles[1:],
|
||||
@ -120,7 +120,7 @@ func TestServer_ListOrgMembers(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestServer_AddOrgMember(t *testing.T) {
|
||||
user := Instance.CreateHumanUserVerified(OrgCTX, Instance.DefaultOrg.Id, gofakeit.Email())
|
||||
user := Instance.CreateHumanUserVerified(OrgCTX, Instance.DefaultOrg.Id, gofakeit.Email(), gofakeit.Phone())
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *mgmt_pb.AddOrgMemberRequest
|
||||
@ -194,7 +194,7 @@ func TestServer_AddOrgMember(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestServer_UpdateOrgMember(t *testing.T) {
|
||||
user := Instance.CreateHumanUserVerified(OrgCTX, Instance.DefaultOrg.Id, gofakeit.Email())
|
||||
user := Instance.CreateHumanUserVerified(OrgCTX, Instance.DefaultOrg.Id, gofakeit.Email(), gofakeit.Phone())
|
||||
_, err := Client.AddOrgMember(OrgCTX, &mgmt_pb.AddOrgMemberRequest{
|
||||
UserId: user.GetUserId(),
|
||||
Roles: []string{"ORG_OWNER"},
|
||||
@ -275,7 +275,7 @@ func TestServer_UpdateOrgMember(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestServer_RemoveIAMMember(t *testing.T) {
|
||||
user := Instance.CreateHumanUserVerified(OrgCTX, Instance.DefaultOrg.Id, gofakeit.Email())
|
||||
user := Instance.CreateHumanUserVerified(OrgCTX, Instance.DefaultOrg.Id, gofakeit.Email(), gofakeit.Phone())
|
||||
_, err := Client.AddOrgMember(OrgCTX, &mgmt_pb.AddOrgMemberRequest{
|
||||
UserId: user.GetUserId(),
|
||||
Roles: []string{"ORG_OWNER"},
|
||||
|
@ -67,6 +67,33 @@ func (s *Server) ResendEmailCode(ctx context.Context, req *user.ResendEmailCodeR
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) SendEmailCode(ctx context.Context, req *user.SendEmailCodeRequest) (resp *user.SendEmailCodeResponse, err error) {
|
||||
var email *domain.Email
|
||||
|
||||
switch v := req.GetVerification().(type) {
|
||||
case *user.SendEmailCodeRequest_SendCode:
|
||||
email, err = s.command.SendUserEmailCodeURLTemplate(ctx, req.GetUserId(), s.userCodeAlg, v.SendCode.GetUrlTemplate())
|
||||
case *user.SendEmailCodeRequest_ReturnCode:
|
||||
email, err = s.command.SendUserEmailReturnCode(ctx, req.GetUserId(), s.userCodeAlg)
|
||||
case nil:
|
||||
email, err = s.command.SendUserEmailCode(ctx, req.GetUserId(), s.userCodeAlg)
|
||||
default:
|
||||
err = zerrors.ThrowUnimplementedf(nil, "USERv2-faj0l0nj5x", "verification oneOf %T in method SendEmailCode not implemented", v)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &user.SendEmailCodeResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: email.Sequence,
|
||||
ChangeDate: timestamppb.New(email.ChangeDate),
|
||||
ResourceOwner: email.ResourceOwner,
|
||||
},
|
||||
VerificationCode: email.PlainCode,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) VerifyEmail(ctx context.Context, req *user.VerifyEmailRequest) (*user.VerifyEmailResponse, error) {
|
||||
details, err := s.command.VerifyUserEmail(ctx,
|
||||
req.GetUserId(),
|
||||
|
@ -147,7 +147,7 @@ func TestServer_SetEmail(t *testing.T) {
|
||||
|
||||
func TestServer_ResendEmailCode(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
verifiedUserID := Instance.CreateHumanUserVerified(CTX, Instance.DefaultOrg.Id, gofakeit.Email()).GetUserId()
|
||||
verifiedUserID := Instance.CreateHumanUserVerified(CTX, Instance.DefaultOrg.Id, gofakeit.Email(), gofakeit.Phone()).GetUserId()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -249,6 +249,116 @@ func TestServer_ResendEmailCode(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_SendEmailCode(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
verifiedUserID := Instance.CreateHumanUserVerified(CTX, Instance.DefaultOrg.Id, gofakeit.Email(), gofakeit.Phone()).GetUserId()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
req *user.SendEmailCodeRequest
|
||||
want *user.SendEmailCodeResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "user not existing",
|
||||
req: &user.SendEmailCodeRequest{
|
||||
UserId: "xxx",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "user no code",
|
||||
req: &user.SendEmailCodeRequest{
|
||||
UserId: verifiedUserID,
|
||||
},
|
||||
want: &user.SendEmailCodeResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "resend",
|
||||
req: &user.SendEmailCodeRequest{
|
||||
UserId: userID,
|
||||
},
|
||||
want: &user.SendEmailCodeResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom url template",
|
||||
req: &user.SendEmailCodeRequest{
|
||||
UserId: userID,
|
||||
Verification: &user.SendEmailCodeRequest_SendCode{
|
||||
SendCode: &user.SendEmailVerificationCode{
|
||||
UrlTemplate: gu.Ptr("https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}"),
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &user.SendEmailCodeResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "template error",
|
||||
req: &user.SendEmailCodeRequest{
|
||||
UserId: userID,
|
||||
Verification: &user.SendEmailCodeRequest_SendCode{
|
||||
SendCode: &user.SendEmailVerificationCode{
|
||||
UrlTemplate: gu.Ptr("{{"),
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "return code",
|
||||
req: &user.SendEmailCodeRequest{
|
||||
UserId: userID,
|
||||
Verification: &user.SendEmailCodeRequest_ReturnCode{
|
||||
ReturnCode: &user.ReturnEmailVerificationCode{},
|
||||
},
|
||||
},
|
||||
want: &user.SendEmailCodeResponse{
|
||||
Details: &object.Details{
|
||||
Sequence: 1,
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Instance.DefaultOrg.Id,
|
||||
},
|
||||
VerificationCode: gu.Ptr("xxx"),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := Client.SendEmailCode(CTX, tt.req)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
if tt.want.GetVerificationCode() != "" {
|
||||
assert.NotEmpty(t, got.GetVerificationCode())
|
||||
} else {
|
||||
assert.Empty(t, got.GetVerificationCode())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_VerifyEmail(t *testing.T) {
|
||||
userResp := Instance.CreateHumanUser(CTX)
|
||||
tests := []struct {
|
||||
|
@ -102,17 +102,17 @@ func TestServer_ListIDPLinks(t *testing.T) {
|
||||
orgResp := Instance.CreateOrganization(IamCTX, fmt.Sprintf("ListIDPLinks-%s", gofakeit.AppName()), gofakeit.Email())
|
||||
|
||||
instanceIdpResp := Instance.AddGenericOAuthProvider(IamCTX, Instance.DefaultOrg.Id)
|
||||
userInstanceResp := Instance.CreateHumanUserVerified(IamCTX, orgResp.OrganizationId, gofakeit.Email())
|
||||
userInstanceResp := Instance.CreateHumanUserVerified(IamCTX, orgResp.OrganizationId, gofakeit.Email(), gofakeit.Phone())
|
||||
_, err := Instance.CreateUserIDPlink(IamCTX, userInstanceResp.GetUserId(), "external_instance", instanceIdpResp.Id, "externalUsername_instance")
|
||||
require.NoError(t, err)
|
||||
|
||||
ctxOrg := metadata.AppendToOutgoingContext(IamCTX, "x-zitadel-orgid", orgResp.GetOrganizationId())
|
||||
orgIdpResp := Instance.AddOrgGenericOAuthProvider(ctxOrg, orgResp.OrganizationId)
|
||||
userOrgResp := Instance.CreateHumanUserVerified(ctxOrg, orgResp.OrganizationId, gofakeit.Email())
|
||||
userOrgResp := Instance.CreateHumanUserVerified(ctxOrg, orgResp.OrganizationId, gofakeit.Email(), gofakeit.Phone())
|
||||
_, err = Instance.CreateUserIDPlink(ctxOrg, userOrgResp.GetUserId(), "external_org", orgIdpResp.Id, "externalUsername_org")
|
||||
require.NoError(t, err)
|
||||
|
||||
userMultipleResp := Instance.CreateHumanUserVerified(IamCTX, orgResp.OrganizationId, gofakeit.Email())
|
||||
userMultipleResp := Instance.CreateHumanUserVerified(IamCTX, orgResp.OrganizationId, gofakeit.Email(), gofakeit.Phone())
|
||||
_, err = Instance.CreateUserIDPlink(IamCTX, userMultipleResp.GetUserId(), "external_multi", instanceIdpResp.Id, "externalUsername_multi")
|
||||
require.NoError(t, err)
|
||||
_, err = Instance.CreateUserIDPlink(ctxOrg, userMultipleResp.GetUserId(), "external_multi", orgIdpResp.Id, "externalUsername_multi")
|
||||
@ -256,17 +256,17 @@ func TestServer_RemoveIDPLink(t *testing.T) {
|
||||
orgResp := Instance.CreateOrganization(IamCTX, fmt.Sprintf("ListIDPLinks-%s", gofakeit.AppName()), gofakeit.Email())
|
||||
|
||||
instanceIdpResp := Instance.AddGenericOAuthProvider(IamCTX, Instance.DefaultOrg.Id)
|
||||
userInstanceResp := Instance.CreateHumanUserVerified(IamCTX, orgResp.OrganizationId, gofakeit.Email())
|
||||
userInstanceResp := Instance.CreateHumanUserVerified(IamCTX, orgResp.OrganizationId, gofakeit.Email(), gofakeit.Phone())
|
||||
_, err := Instance.CreateUserIDPlink(IamCTX, userInstanceResp.GetUserId(), "external_instance", instanceIdpResp.Id, "externalUsername_instance")
|
||||
require.NoError(t, err)
|
||||
|
||||
ctxOrg := metadata.AppendToOutgoingContext(IamCTX, "x-zitadel-orgid", orgResp.GetOrganizationId())
|
||||
orgIdpResp := Instance.AddOrgGenericOAuthProvider(ctxOrg, orgResp.OrganizationId)
|
||||
userOrgResp := Instance.CreateHumanUserVerified(ctxOrg, orgResp.OrganizationId, gofakeit.Email())
|
||||
userOrgResp := Instance.CreateHumanUserVerified(ctxOrg, orgResp.OrganizationId, gofakeit.Email(), gofakeit.Phone())
|
||||
_, err = Instance.CreateUserIDPlink(ctxOrg, userOrgResp.GetUserId(), "external_org", orgIdpResp.Id, "externalUsername_org")
|
||||
require.NoError(t, err)
|
||||
|
||||
userNoLinkResp := Instance.CreateHumanUserVerified(IamCTX, orgResp.OrganizationId, gofakeit.Email())
|
||||
userNoLinkResp := Instance.CreateHumanUserVerified(IamCTX, orgResp.OrganizationId, gofakeit.Email(), gofakeit.Phone())
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
|
@ -123,7 +123,7 @@ func TestServer_SetPhone(t *testing.T) {
|
||||
|
||||
func TestServer_ResendPhoneCode(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
verifiedUserID := Instance.CreateHumanUserVerified(CTX, Instance.DefaultOrg.Id, gofakeit.Email()).GetUserId()
|
||||
verifiedUserID := Instance.CreateHumanUserVerified(CTX, Instance.DefaultOrg.Id, gofakeit.Email(), gofakeit.Phone()).GetUserId()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
|
@ -5,6 +5,7 @@ package user_test
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -24,7 +25,7 @@ func TestServer_GetUserByID(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *user.GetUserByIDRequest
|
||||
dep func(ctx context.Context, username string, request *user.GetUserByIDRequest) (*userAttr, error)
|
||||
dep func(ctx context.Context, request *user.GetUserByIDRequest) *userAttr
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -39,8 +40,8 @@ func TestServer_GetUserByID(t *testing.T) {
|
||||
&user.GetUserByIDRequest{
|
||||
UserId: "",
|
||||
},
|
||||
func(ctx context.Context, username string, request *user.GetUserByIDRequest) (*userAttr, error) {
|
||||
return nil, nil
|
||||
func(ctx context.Context, request *user.GetUserByIDRequest) *userAttr {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
@ -52,8 +53,8 @@ func TestServer_GetUserByID(t *testing.T) {
|
||||
&user.GetUserByIDRequest{
|
||||
UserId: "unknown",
|
||||
},
|
||||
func(ctx context.Context, username string, request *user.GetUserByIDRequest) (*userAttr, error) {
|
||||
return nil, nil
|
||||
func(ctx context.Context, request *user.GetUserByIDRequest) *userAttr {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
@ -63,10 +64,10 @@ func TestServer_GetUserByID(t *testing.T) {
|
||||
args: args{
|
||||
IamCTX,
|
||||
&user.GetUserByIDRequest{},
|
||||
func(ctx context.Context, username string, request *user.GetUserByIDRequest) (*userAttr, error) {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
request.UserId = resp.GetUserId()
|
||||
return &userAttr{resp.GetUserId(), username, nil, resp.GetDetails()}, nil
|
||||
func(ctx context.Context, request *user.GetUserByIDRequest) *userAttr {
|
||||
info := createUser(ctx, orgResp.OrganizationId, false)
|
||||
request.UserId = info.UserID
|
||||
return &info
|
||||
},
|
||||
},
|
||||
want: &user.GetUserByIDResponse{
|
||||
@ -90,7 +91,6 @@ func TestServer_GetUserByID(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -107,11 +107,10 @@ func TestServer_GetUserByID(t *testing.T) {
|
||||
args: args{
|
||||
IamCTX,
|
||||
&user.GetUserByIDRequest{},
|
||||
func(ctx context.Context, username string, request *user.GetUserByIDRequest) (*userAttr, error) {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
request.UserId = resp.GetUserId()
|
||||
details := Instance.SetUserPassword(ctx, resp.GetUserId(), integration.UserPassword, true)
|
||||
return &userAttr{resp.GetUserId(), username, details.GetChangeDate(), resp.GetDetails()}, nil
|
||||
func(ctx context.Context, request *user.GetUserByIDRequest) *userAttr {
|
||||
info := createUser(ctx, orgResp.OrganizationId, true)
|
||||
request.UserId = info.UserID
|
||||
return &info
|
||||
},
|
||||
},
|
||||
want: &user.GetUserByIDResponse{
|
||||
@ -135,7 +134,6 @@ func TestServer_GetUserByID(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
PasswordChangeRequired: true,
|
||||
@ -152,9 +150,7 @@ func TestServer_GetUserByID(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
username := gofakeit.Email()
|
||||
userAttr, err := tt.args.dep(tt.args.ctx, username, tt.args.req)
|
||||
require.NoError(t, err)
|
||||
userAttr := tt.args.dep(IamCTX, tt.args.req)
|
||||
|
||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tt.args.ctx, time.Minute)
|
||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||
@ -174,11 +170,12 @@ func TestServer_GetUserByID(t *testing.T) {
|
||||
tt.want.User.LoginNames = []string{userAttr.Username}
|
||||
if human := tt.want.User.GetHuman(); human != nil {
|
||||
human.Email.Email = userAttr.Username
|
||||
human.Phone.Phone = userAttr.Phone
|
||||
if tt.want.User.GetHuman().GetPasswordChanged() != nil {
|
||||
human.PasswordChanged = userAttr.Changed
|
||||
}
|
||||
}
|
||||
assert.Equal(ttt, tt.want.User, got.User)
|
||||
assert.EqualExportedValues(ttt, tt.want.User, got.User)
|
||||
integration.AssertDetails(ttt, tt.want, got)
|
||||
}, retryDuration, tick)
|
||||
})
|
||||
@ -325,21 +322,60 @@ func TestServer_GetUserByID_Permission(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type userAttrs []userAttr
|
||||
|
||||
func (u userAttrs) userIDs() []string {
|
||||
ids := make([]string, len(u))
|
||||
for i := range u {
|
||||
ids[i] = u[i].UserID
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func (u userAttrs) emails() []string {
|
||||
emails := make([]string, len(u))
|
||||
for i := range u {
|
||||
emails[i] = u[i].Username
|
||||
}
|
||||
return emails
|
||||
}
|
||||
|
||||
type userAttr struct {
|
||||
UserID string
|
||||
Username string
|
||||
Phone string
|
||||
Changed *timestamppb.Timestamp
|
||||
Details *object.Details
|
||||
}
|
||||
|
||||
func createUsers(ctx context.Context, orgID string, count int, passwordChangeRequired bool) userAttrs {
|
||||
infos := make([]userAttr, count)
|
||||
for i := 0; i < count; i++ {
|
||||
infos[i] = createUser(ctx, orgID, passwordChangeRequired)
|
||||
}
|
||||
slices.Reverse(infos)
|
||||
return infos
|
||||
}
|
||||
|
||||
func createUser(ctx context.Context, orgID string, passwordChangeRequired bool) userAttr {
|
||||
username := gofakeit.Email()
|
||||
// used as default country prefix
|
||||
phone := "+41" + gofakeit.Phone()
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgID, username, phone)
|
||||
info := userAttr{resp.GetUserId(), username, phone, nil, resp.GetDetails()}
|
||||
if passwordChangeRequired {
|
||||
details := Instance.SetUserPassword(ctx, resp.GetUserId(), integration.UserPassword, true)
|
||||
info.Changed = details.GetChangeDate()
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
func TestServer_ListUsers(t *testing.T) {
|
||||
orgResp := Instance.CreateOrganization(IamCTX, fmt.Sprintf("ListUsersOrg-%s", gofakeit.AppName()), gofakeit.Email())
|
||||
userResp := Instance.CreateHumanUserVerified(IamCTX, orgResp.OrganizationId, gofakeit.Email())
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
count int
|
||||
req *user.ListUsersRequest
|
||||
dep func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error)
|
||||
ctx context.Context
|
||||
req *user.ListUsersRequest
|
||||
dep func(ctx context.Context, request *user.ListUsersRequest) userAttrs
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -351,11 +387,11 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
name: "list user by id, no permission",
|
||||
args: args{
|
||||
UserCTX,
|
||||
0,
|
||||
&user.ListUsersRequest{},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
request.Queries = append(request.Queries, InUserIDsQuery([]string{userResp.UserId}))
|
||||
return []userAttr{}, nil
|
||||
func(ctx context.Context, request *user.ListUsersRequest) userAttrs {
|
||||
info := createUser(ctx, orgResp.OrganizationId, false)
|
||||
request.Queries = append(request.Queries, InUserIDsQuery([]string{info.UserID}))
|
||||
return []userAttr{}
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
@ -371,22 +407,15 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
name: "list user by id, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
1,
|
||||
&user.ListUsersRequest{
|
||||
Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
infos := make([]userAttr, len(usernames))
|
||||
userIDs := make([]string, len(usernames))
|
||||
for i, username := range usernames {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
userIDs[i] = resp.GetUserId()
|
||||
infos[i] = userAttr{resp.GetUserId(), username, nil, resp.GetDetails()}
|
||||
}
|
||||
request.Queries = append(request.Queries, InUserIDsQuery(userIDs))
|
||||
return infos, nil
|
||||
func(ctx context.Context, request *user.ListUsersRequest) userAttrs {
|
||||
info := createUser(ctx, orgResp.OrganizationId, false)
|
||||
request.Queries = append(request.Queries, InUserIDsQuery([]string{info.UserID}))
|
||||
return []userAttr{info}
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
@ -412,7 +441,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -425,23 +453,15 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
name: "list user by id, passwordChangeRequired, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
1,
|
||||
&user.ListUsersRequest{
|
||||
Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
infos := make([]userAttr, len(usernames))
|
||||
userIDs := make([]string, len(usernames))
|
||||
for i, username := range usernames {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
userIDs[i] = resp.GetUserId()
|
||||
details := Instance.SetUserPassword(ctx, resp.GetUserId(), integration.UserPassword, true)
|
||||
infos[i] = userAttr{resp.GetUserId(), username, details.GetChangeDate(), resp.GetDetails()}
|
||||
}
|
||||
request.Queries = append(request.Queries, InUserIDsQuery(userIDs))
|
||||
return infos, nil
|
||||
func(ctx context.Context, request *user.ListUsersRequest) userAttrs {
|
||||
info := createUser(ctx, orgResp.OrganizationId, true)
|
||||
request.Queries = append(request.Queries, InUserIDsQuery([]string{info.UserID}))
|
||||
return []userAttr{info}
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
@ -467,7 +487,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
PasswordChangeRequired: true,
|
||||
@ -482,22 +501,15 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
name: "list user by id multiple, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
3,
|
||||
&user.ListUsersRequest{
|
||||
Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
infos := make([]userAttr, len(usernames))
|
||||
userIDs := make([]string, len(usernames))
|
||||
for i, username := range usernames {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
userIDs[i] = resp.GetUserId()
|
||||
infos[i] = userAttr{resp.GetUserId(), username, nil, resp.GetDetails()}
|
||||
}
|
||||
request.Queries = append(request.Queries, InUserIDsQuery(userIDs))
|
||||
return infos, nil
|
||||
func(ctx context.Context, request *user.ListUsersRequest) userAttrs {
|
||||
infos := createUsers(ctx, orgResp.OrganizationId, 3, false)
|
||||
request.Queries = append(request.Queries, InUserIDsQuery(infos.userIDs()))
|
||||
return infos
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
@ -523,7 +535,27 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
State: user.UserState_USER_STATE_ACTIVE,
|
||||
Type: &user.User_Human{
|
||||
Human: &user.HumanUser{
|
||||
Profile: &user.HumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
NickName: gu.Ptr("Mickey"),
|
||||
DisplayName: gu.Ptr("Mickey Mouse"),
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: user.Gender_GENDER_MALE.Enum(),
|
||||
},
|
||||
Email: &user.HumanEmail{
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -544,28 +576,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
State: user.UserState_USER_STATE_ACTIVE,
|
||||
Type: &user.User_Human{
|
||||
Human: &user.HumanUser{
|
||||
Profile: &user.HumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
NickName: gu.Ptr("Mickey"),
|
||||
DisplayName: gu.Ptr("Mickey Mouse"),
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: user.Gender_GENDER_MALE.Enum(),
|
||||
},
|
||||
Email: &user.HumanEmail{
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -578,22 +588,15 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
name: "list user by username, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
1,
|
||||
&user.ListUsersRequest{
|
||||
Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
infos := make([]userAttr, len(usernames))
|
||||
userIDs := make([]string, len(usernames))
|
||||
for i, username := range usernames {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
userIDs[i] = resp.GetUserId()
|
||||
infos[i] = userAttr{resp.GetUserId(), username, nil, resp.GetDetails()}
|
||||
request.Queries = append(request.Queries, UsernameQuery(username))
|
||||
}
|
||||
return infos, nil
|
||||
func(ctx context.Context, request *user.ListUsersRequest) userAttrs {
|
||||
info := createUser(ctx, orgResp.OrganizationId, false)
|
||||
request.Queries = append(request.Queries, UsernameQuery(info.Username))
|
||||
return []userAttr{info}
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
@ -619,7 +622,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -632,20 +634,15 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
name: "list user in emails, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
1,
|
||||
&user.ListUsersRequest{
|
||||
Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
infos := make([]userAttr, len(usernames))
|
||||
for i, username := range usernames {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
infos[i] = userAttr{resp.GetUserId(), username, nil, resp.GetDetails()}
|
||||
}
|
||||
request.Queries = append(request.Queries, InUserEmailsQuery(usernames))
|
||||
return infos, nil
|
||||
func(ctx context.Context, request *user.ListUsersRequest) userAttrs {
|
||||
info := createUser(ctx, orgResp.OrganizationId, false)
|
||||
request.Queries = append(request.Queries, InUserEmailsQuery([]string{info.Username}))
|
||||
return []userAttr{info}
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
@ -671,7 +668,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -684,20 +680,15 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
name: "list user in emails multiple, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
3,
|
||||
&user.ListUsersRequest{
|
||||
Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
infos := make([]userAttr, len(usernames))
|
||||
for i, username := range usernames {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
infos[i] = userAttr{resp.GetUserId(), username, nil, resp.GetDetails()}
|
||||
}
|
||||
request.Queries = append(request.Queries, InUserEmailsQuery(usernames))
|
||||
return infos, nil
|
||||
func(ctx context.Context, request *user.ListUsersRequest) userAttrs {
|
||||
infos := createUsers(ctx, orgResp.OrganizationId, 3, false)
|
||||
request.Queries = append(request.Queries, InUserEmailsQuery(infos.emails()))
|
||||
return infos
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
@ -723,7 +714,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -744,7 +734,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -765,7 +754,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -778,14 +766,81 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
name: "list user in emails no found, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
3,
|
||||
&user.ListUsersRequest{Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
InUserEmailsQuery([]string{"notfound"}),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
return []userAttr{}, nil
|
||||
func(ctx context.Context, request *user.ListUsersRequest) userAttrs {
|
||||
return []userAttr{}
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
Details: &object.ListDetails{
|
||||
TotalResult: 0,
|
||||
Timestamp: timestamppb.Now(),
|
||||
},
|
||||
SortingColumn: 0,
|
||||
Result: []*user.User{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list user phone, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
&user.ListUsersRequest{
|
||||
Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, request *user.ListUsersRequest) userAttrs {
|
||||
info := createUser(ctx, orgResp.OrganizationId, false)
|
||||
request.Queries = append(request.Queries, PhoneQuery(info.Phone))
|
||||
return []userAttr{info}
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
Details: &object.ListDetails{
|
||||
TotalResult: 1,
|
||||
Timestamp: timestamppb.Now(),
|
||||
},
|
||||
SortingColumn: 0,
|
||||
Result: []*user.User{
|
||||
{
|
||||
State: user.UserState_USER_STATE_ACTIVE,
|
||||
Type: &user.User_Human{
|
||||
Human: &user.HumanUser{
|
||||
Profile: &user.HumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
NickName: gu.Ptr("Mickey"),
|
||||
DisplayName: gu.Ptr("Mickey Mouse"),
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: user.Gender_GENDER_MALE.Enum(),
|
||||
},
|
||||
Email: &user.HumanEmail{
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list user in emails no found, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
&user.ListUsersRequest{Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
InUserEmailsQuery([]string{"notfound"}),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, request *user.ListUsersRequest) userAttrs {
|
||||
return []userAttr{}
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
@ -801,19 +856,14 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
name: "list user resourceowner multiple, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
3,
|
||||
&user.ListUsersRequest{},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
func(ctx context.Context, request *user.ListUsersRequest) userAttrs {
|
||||
orgResp := Instance.CreateOrganization(ctx, fmt.Sprintf("ListUsersResourceowner-%s", gofakeit.AppName()), gofakeit.Email())
|
||||
|
||||
infos := make([]userAttr, len(usernames))
|
||||
for i, username := range usernames {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
infos[i] = userAttr{resp.GetUserId(), username, nil, resp.GetDetails()}
|
||||
}
|
||||
infos := createUsers(ctx, orgResp.OrganizationId, 3, false)
|
||||
request.Queries = append(request.Queries, OrganizationIdQuery(orgResp.OrganizationId))
|
||||
request.Queries = append(request.Queries, InUserEmailsQuery(usernames))
|
||||
return infos, nil
|
||||
request.Queries = append(request.Queries, InUserEmailsQuery(infos.emails()))
|
||||
return infos
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
@ -839,7 +889,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -860,7 +909,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -881,7 +929,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -893,12 +940,7 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
usernames := make([]string, tt.args.count)
|
||||
for i := 0; i < tt.args.count; i++ {
|
||||
usernames[i] = gofakeit.Email()
|
||||
}
|
||||
infos, err := tt.args.dep(tt.args.ctx, usernames, tt.args.req)
|
||||
require.NoError(t, err)
|
||||
infos := tt.args.dep(IamCTX, tt.args.req)
|
||||
|
||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tt.args.ctx, time.Minute)
|
||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||
@ -924,6 +966,7 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
tt.want.Result[i].LoginNames = []string{infos[i].Username}
|
||||
if human := tt.want.Result[i].GetHuman(); human != nil {
|
||||
human.Email.Email = infos[i].Username
|
||||
human.Phone.Phone = infos[i].Phone
|
||||
if tt.want.Result[i].GetHuman().GetPasswordChanged() != nil {
|
||||
human.PasswordChanged = infos[i].Changed
|
||||
}
|
||||
@ -931,7 +974,7 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
tt.want.Result[i].Details = infos[i].Details
|
||||
}
|
||||
for i := range tt.want.Result {
|
||||
assert.Contains(ttt, got.Result, tt.want.Result[i])
|
||||
assert.EqualExportedValues(ttt, got.Result[i], tt.want.Result[i])
|
||||
}
|
||||
}
|
||||
integration.AssertListDetails(ttt, tt.want, got)
|
||||
@ -958,6 +1001,15 @@ func InUserEmailsQuery(emails []string) *user.SearchQuery {
|
||||
}
|
||||
}
|
||||
|
||||
func PhoneQuery(number string) *user.SearchQuery {
|
||||
return &user.SearchQuery{Query: &user.SearchQuery_PhoneQuery{
|
||||
PhoneQuery: &user.PhoneQuery{
|
||||
Number: number,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func UsernameQuery(username string) *user.SearchQuery {
|
||||
return &user.SearchQuery{Query: &user.SearchQuery_UserNameQuery{
|
||||
UserNameQuery: &user.UserNameQuery{
|
||||
|
@ -238,6 +238,8 @@ func userQueryToQuery(query *user.SearchQuery, level uint8) (query.SearchQuery,
|
||||
return displayNameQueryToQuery(q.DisplayNameQuery)
|
||||
case *user.SearchQuery_EmailQuery:
|
||||
return emailQueryToQuery(q.EmailQuery)
|
||||
case *user.SearchQuery_PhoneQuery:
|
||||
return phoneQueryToQuery(q.PhoneQuery)
|
||||
case *user.SearchQuery_StateQuery:
|
||||
return stateQueryToQuery(q.StateQuery)
|
||||
case *user.SearchQuery_TypeQuery:
|
||||
@ -285,6 +287,10 @@ func emailQueryToQuery(q *user.EmailQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserEmailSearchQuery(q.EmailAddress, object.TextMethodToQuery(q.Method))
|
||||
}
|
||||
|
||||
func phoneQueryToQuery(q *user.PhoneQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserPhoneSearchQuery(q.Number, object.TextMethodToQuery(q.Method))
|
||||
}
|
||||
|
||||
func stateQueryToQuery(q *user.StateQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserStateSearchQuery(int32(q.State))
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ func TestServer_SetEmail(t *testing.T) {
|
||||
|
||||
func TestServer_ResendEmailCode(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
verifiedUserID := Instance.CreateHumanUserVerified(CTX, Instance.DefaultOrg.Id, gofakeit.Email()).GetUserId()
|
||||
verifiedUserID := Instance.CreateHumanUserVerified(CTX, Instance.DefaultOrg.Id, gofakeit.Email(), gofakeit.Phone()).GetUserId()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
|
@ -125,7 +125,7 @@ func TestServer_SetPhone(t *testing.T) {
|
||||
|
||||
func TestServer_ResendPhoneCode(t *testing.T) {
|
||||
userID := Instance.CreateHumanUser(CTX).GetUserId()
|
||||
verifiedUserID := Instance.CreateHumanUserVerified(CTX, Instance.DefaultOrg.Id, gofakeit.Email()).GetUserId()
|
||||
verifiedUserID := Instance.CreateHumanUserVerified(CTX, Instance.DefaultOrg.Id, gofakeit.Email(), gofakeit.Phone()).GetUserId()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
|
@ -5,6 +5,7 @@ package user_test
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -33,7 +34,7 @@ func TestServer_GetUserByID(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *user.GetUserByIDRequest
|
||||
dep func(ctx context.Context, username string, request *user.GetUserByIDRequest) (*userAttr, error)
|
||||
dep func(ctx context.Context, request *user.GetUserByIDRequest) *userAttr
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -48,8 +49,8 @@ func TestServer_GetUserByID(t *testing.T) {
|
||||
&user.GetUserByIDRequest{
|
||||
UserId: "",
|
||||
},
|
||||
func(ctx context.Context, username string, request *user.GetUserByIDRequest) (*userAttr, error) {
|
||||
return nil, nil
|
||||
func(ctx context.Context, request *user.GetUserByIDRequest) *userAttr {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
@ -61,8 +62,8 @@ func TestServer_GetUserByID(t *testing.T) {
|
||||
&user.GetUserByIDRequest{
|
||||
UserId: "unknown",
|
||||
},
|
||||
func(ctx context.Context, username string, request *user.GetUserByIDRequest) (*userAttr, error) {
|
||||
return nil, nil
|
||||
func(ctx context.Context, request *user.GetUserByIDRequest) *userAttr {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
@ -72,10 +73,10 @@ func TestServer_GetUserByID(t *testing.T) {
|
||||
args: args{
|
||||
IamCTX,
|
||||
&user.GetUserByIDRequest{},
|
||||
func(ctx context.Context, username string, request *user.GetUserByIDRequest) (*userAttr, error) {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
request.UserId = resp.GetUserId()
|
||||
return &userAttr{resp.GetUserId(), username, nil, resp.GetDetails()}, nil
|
||||
func(ctx context.Context, request *user.GetUserByIDRequest) *userAttr {
|
||||
info := createUser(ctx, orgResp.OrganizationId, false)
|
||||
request.UserId = info.UserID
|
||||
return &info
|
||||
},
|
||||
},
|
||||
want: &user.GetUserByIDResponse{
|
||||
@ -99,7 +100,6 @@ func TestServer_GetUserByID(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -116,11 +116,10 @@ func TestServer_GetUserByID(t *testing.T) {
|
||||
args: args{
|
||||
IamCTX,
|
||||
&user.GetUserByIDRequest{},
|
||||
func(ctx context.Context, username string, request *user.GetUserByIDRequest) (*userAttr, error) {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
request.UserId = resp.GetUserId()
|
||||
details := Instance.SetUserPassword(ctx, resp.GetUserId(), integration.UserPassword, true)
|
||||
return &userAttr{resp.GetUserId(), username, details.GetChangeDate(), resp.GetDetails()}, nil
|
||||
func(ctx context.Context, request *user.GetUserByIDRequest) *userAttr {
|
||||
info := createUser(ctx, orgResp.OrganizationId, true)
|
||||
request.UserId = info.UserID
|
||||
return &info
|
||||
},
|
||||
},
|
||||
want: &user.GetUserByIDResponse{
|
||||
@ -144,7 +143,6 @@ func TestServer_GetUserByID(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
PasswordChangeRequired: true,
|
||||
@ -161,9 +159,7 @@ func TestServer_GetUserByID(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
username := gofakeit.Email()
|
||||
userAttr, err := tt.args.dep(tt.args.ctx, username, tt.args.req)
|
||||
require.NoError(t, err)
|
||||
userAttr := tt.args.dep(IamCTX, tt.args.req)
|
||||
|
||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tt.args.ctx, time.Minute)
|
||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||
@ -183,6 +179,7 @@ func TestServer_GetUserByID(t *testing.T) {
|
||||
tt.want.User.LoginNames = []string{userAttr.Username}
|
||||
if human := tt.want.User.GetHuman(); human != nil {
|
||||
human.Email.Email = userAttr.Username
|
||||
human.Phone.Phone = userAttr.Phone
|
||||
if tt.want.User.GetHuman().GetPasswordChanged() != nil {
|
||||
human.PasswordChanged = userAttr.Changed
|
||||
}
|
||||
@ -335,21 +332,60 @@ func TestServer_GetUserByID_Permission(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type userAttrs []userAttr
|
||||
|
||||
func (u userAttrs) userIDs() []string {
|
||||
ids := make([]string, len(u))
|
||||
for i := range u {
|
||||
ids[i] = u[i].UserID
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func (u userAttrs) emails() []string {
|
||||
emails := make([]string, len(u))
|
||||
for i := range u {
|
||||
emails[i] = u[i].Username
|
||||
}
|
||||
return emails
|
||||
}
|
||||
|
||||
type userAttr struct {
|
||||
UserID string
|
||||
Username string
|
||||
Phone string
|
||||
Changed *timestamppb.Timestamp
|
||||
Details *object.Details
|
||||
}
|
||||
|
||||
func createUsers(ctx context.Context, orgID string, count int, passwordChangeRequired bool) userAttrs {
|
||||
infos := make([]userAttr, count)
|
||||
for i := 0; i < count; i++ {
|
||||
infos[i] = createUser(ctx, orgID, passwordChangeRequired)
|
||||
}
|
||||
slices.Reverse(infos)
|
||||
return infos
|
||||
}
|
||||
|
||||
func createUser(ctx context.Context, orgID string, passwordChangeRequired bool) userAttr {
|
||||
username := gofakeit.Email()
|
||||
// used as default country prefix
|
||||
phone := "+41" + gofakeit.Phone()
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgID, username, phone)
|
||||
info := userAttr{resp.GetUserId(), username, phone, nil, resp.GetDetails()}
|
||||
if passwordChangeRequired {
|
||||
details := Instance.SetUserPassword(ctx, resp.GetUserId(), integration.UserPassword, true)
|
||||
info.Changed = details.GetChangeDate()
|
||||
}
|
||||
return info
|
||||
}
|
||||
|
||||
func TestServer_ListUsers(t *testing.T) {
|
||||
orgResp := Instance.CreateOrganization(IamCTX, fmt.Sprintf("ListUsersOrg-%s", gofakeit.AppName()), gofakeit.Email())
|
||||
userResp := Instance.CreateHumanUserVerified(IamCTX, orgResp.OrganizationId, gofakeit.Email())
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
count int
|
||||
req *user.ListUsersRequest
|
||||
dep func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error)
|
||||
ctx context.Context
|
||||
req *user.ListUsersRequest
|
||||
dep func(ctx context.Context, request *user.ListUsersRequest) userAttrs
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -361,11 +397,11 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
name: "list user by id, no permission",
|
||||
args: args{
|
||||
UserCTX,
|
||||
0,
|
||||
&user.ListUsersRequest{},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
request.Queries = append(request.Queries, InUserIDsQuery([]string{userResp.UserId}))
|
||||
return []userAttr{}, nil
|
||||
func(ctx context.Context, request *user.ListUsersRequest) userAttrs {
|
||||
info := createUser(ctx, orgResp.OrganizationId, false)
|
||||
request.Queries = append(request.Queries, InUserIDsQuery([]string{info.UserID}))
|
||||
return []userAttr{}
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
@ -381,22 +417,15 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
name: "list user by id, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
1,
|
||||
&user.ListUsersRequest{
|
||||
Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
infos := make([]userAttr, len(usernames))
|
||||
userIDs := make([]string, len(usernames))
|
||||
for i, username := range usernames {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
userIDs[i] = resp.GetUserId()
|
||||
infos[i] = userAttr{resp.GetUserId(), username, nil, resp.GetDetails()}
|
||||
}
|
||||
request.Queries = append(request.Queries, InUserIDsQuery(userIDs))
|
||||
return infos, nil
|
||||
func(ctx context.Context, request *user.ListUsersRequest) userAttrs {
|
||||
info := createUser(ctx, orgResp.OrganizationId, false)
|
||||
request.Queries = append(request.Queries, InUserIDsQuery([]string{info.UserID}))
|
||||
return []userAttr{info}
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
@ -422,7 +451,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -435,23 +463,15 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
name: "list user by id, passwordChangeRequired, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
1,
|
||||
&user.ListUsersRequest{
|
||||
Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
infos := make([]userAttr, len(usernames))
|
||||
userIDs := make([]string, len(usernames))
|
||||
for i, username := range usernames {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
userIDs[i] = resp.GetUserId()
|
||||
details := Instance.SetUserPassword(ctx, resp.GetUserId(), integration.UserPassword, true)
|
||||
infos[i] = userAttr{resp.GetUserId(), username, details.GetChangeDate(), resp.GetDetails()}
|
||||
}
|
||||
request.Queries = append(request.Queries, InUserIDsQuery(userIDs))
|
||||
return infos, nil
|
||||
func(ctx context.Context, request *user.ListUsersRequest) userAttrs {
|
||||
info := createUser(ctx, orgResp.OrganizationId, true)
|
||||
request.Queries = append(request.Queries, InUserIDsQuery([]string{info.UserID}))
|
||||
return []userAttr{info}
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
@ -477,7 +497,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
PasswordChangeRequired: true,
|
||||
@ -492,22 +511,15 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
name: "list user by id multiple, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
3,
|
||||
&user.ListUsersRequest{
|
||||
Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
infos := make([]userAttr, len(usernames))
|
||||
userIDs := make([]string, len(usernames))
|
||||
for i, username := range usernames {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
userIDs[i] = resp.GetUserId()
|
||||
infos[i] = userAttr{resp.GetUserId(), username, nil, resp.GetDetails()}
|
||||
}
|
||||
request.Queries = append(request.Queries, InUserIDsQuery(userIDs))
|
||||
return infos, nil
|
||||
func(ctx context.Context, request *user.ListUsersRequest) userAttrs {
|
||||
infos := createUsers(ctx, orgResp.OrganizationId, 3, false)
|
||||
request.Queries = append(request.Queries, InUserIDsQuery(infos.userIDs()))
|
||||
return infos
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
@ -533,7 +545,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -554,7 +565,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -575,7 +585,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -588,22 +597,15 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
name: "list user by username, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
1,
|
||||
&user.ListUsersRequest{
|
||||
Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
infos := make([]userAttr, len(usernames))
|
||||
userIDs := make([]string, len(usernames))
|
||||
for i, username := range usernames {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
userIDs[i] = resp.GetUserId()
|
||||
infos[i] = userAttr{resp.GetUserId(), username, nil, resp.GetDetails()}
|
||||
request.Queries = append(request.Queries, UsernameQuery(username))
|
||||
}
|
||||
return infos, nil
|
||||
func(ctx context.Context, request *user.ListUsersRequest) userAttrs {
|
||||
info := createUser(ctx, orgResp.OrganizationId, false)
|
||||
request.Queries = append(request.Queries, UsernameQuery(info.Username))
|
||||
return []userAttr{info}
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
@ -629,7 +631,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -642,20 +643,15 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
name: "list user in emails, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
1,
|
||||
&user.ListUsersRequest{
|
||||
Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
infos := make([]userAttr, len(usernames))
|
||||
for i, username := range usernames {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
infos[i] = userAttr{resp.GetUserId(), username, nil, resp.GetDetails()}
|
||||
}
|
||||
request.Queries = append(request.Queries, InUserEmailsQuery(usernames))
|
||||
return infos, nil
|
||||
func(ctx context.Context, request *user.ListUsersRequest) userAttrs {
|
||||
info := createUser(ctx, orgResp.OrganizationId, false)
|
||||
request.Queries = append(request.Queries, InUserEmailsQuery([]string{info.Username}))
|
||||
return []userAttr{info}
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
@ -681,7 +677,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -694,20 +689,15 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
name: "list user in emails multiple, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
3,
|
||||
&user.ListUsersRequest{
|
||||
Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
infos := make([]userAttr, len(usernames))
|
||||
for i, username := range usernames {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
infos[i] = userAttr{resp.GetUserId(), username, nil, resp.GetDetails()}
|
||||
}
|
||||
request.Queries = append(request.Queries, InUserEmailsQuery(usernames))
|
||||
return infos, nil
|
||||
func(ctx context.Context, request *user.ListUsersRequest) userAttrs {
|
||||
infos := createUsers(ctx, orgResp.OrganizationId, 3, false)
|
||||
request.Queries = append(request.Queries, InUserEmailsQuery(infos.emails()))
|
||||
return infos
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
@ -733,7 +723,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -754,7 +743,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -775,7 +763,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -788,14 +775,13 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
name: "list user in emails no found, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
3,
|
||||
&user.ListUsersRequest{Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
InUserEmailsQuery([]string{"notfound"}),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
return []userAttr{}, nil
|
||||
func(ctx context.Context, request *user.ListUsersRequest) userAttrs {
|
||||
return []userAttr{}
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
@ -807,23 +793,64 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
Result: []*user.User{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list user phone, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
&user.ListUsersRequest{
|
||||
Queries: []*user.SearchQuery{
|
||||
OrganizationIdQuery(orgResp.OrganizationId),
|
||||
},
|
||||
},
|
||||
func(ctx context.Context, request *user.ListUsersRequest) userAttrs {
|
||||
info := createUser(ctx, orgResp.OrganizationId, false)
|
||||
request.Queries = append(request.Queries, PhoneQuery(info.Phone))
|
||||
return []userAttr{info}
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
Details: &object_v2beta.ListDetails{
|
||||
TotalResult: 1,
|
||||
Timestamp: timestamppb.Now(),
|
||||
},
|
||||
SortingColumn: 0,
|
||||
Result: []*user.User{
|
||||
{
|
||||
State: user.UserState_USER_STATE_ACTIVE,
|
||||
Type: &user.User_Human{
|
||||
Human: &user.HumanUser{
|
||||
Profile: &user.HumanProfile{
|
||||
GivenName: "Mickey",
|
||||
FamilyName: "Mouse",
|
||||
NickName: gu.Ptr("Mickey"),
|
||||
DisplayName: gu.Ptr("Mickey Mouse"),
|
||||
PreferredLanguage: gu.Ptr("nl"),
|
||||
Gender: user.Gender_GENDER_MALE.Enum(),
|
||||
},
|
||||
Email: &user.HumanEmail{
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list user resourceowner multiple, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
3,
|
||||
&user.ListUsersRequest{},
|
||||
func(ctx context.Context, usernames []string, request *user.ListUsersRequest) ([]userAttr, error) {
|
||||
func(ctx context.Context, request *user.ListUsersRequest) userAttrs {
|
||||
orgResp := Instance.CreateOrganization(ctx, fmt.Sprintf("ListUsersResourceowner-%s", gofakeit.AppName()), gofakeit.Email())
|
||||
|
||||
infos := make([]userAttr, len(usernames))
|
||||
for i, username := range usernames {
|
||||
resp := Instance.CreateHumanUserVerified(ctx, orgResp.OrganizationId, username)
|
||||
infos[i] = userAttr{resp.GetUserId(), username, nil, resp.GetDetails()}
|
||||
}
|
||||
infos := createUsers(ctx, orgResp.OrganizationId, 3, false)
|
||||
request.Queries = append(request.Queries, OrganizationIdQuery(orgResp.OrganizationId))
|
||||
request.Queries = append(request.Queries, InUserEmailsQuery(usernames))
|
||||
return infos, nil
|
||||
request.Queries = append(request.Queries, InUserEmailsQuery(infos.emails()))
|
||||
return infos
|
||||
},
|
||||
},
|
||||
want: &user.ListUsersResponse{
|
||||
@ -849,7 +876,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -870,7 +896,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -891,7 +916,6 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
IsVerified: true,
|
||||
},
|
||||
Phone: &user.HumanPhone{
|
||||
Phone: "+41791234567",
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
@ -903,12 +927,7 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
usernames := make([]string, tt.args.count)
|
||||
for i := 0; i < tt.args.count; i++ {
|
||||
usernames[i] = gofakeit.Email()
|
||||
}
|
||||
infos, err := tt.args.dep(tt.args.ctx, usernames, tt.args.req)
|
||||
require.NoError(t, err)
|
||||
infos := tt.args.dep(IamCTX, tt.args.req)
|
||||
|
||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tt.args.ctx, time.Minute)
|
||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||
@ -934,6 +953,7 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
tt.want.Result[i].LoginNames = []string{infos[i].Username}
|
||||
if human := tt.want.Result[i].GetHuman(); human != nil {
|
||||
human.Email.Email = infos[i].Username
|
||||
human.Phone.Phone = infos[i].Phone
|
||||
if tt.want.Result[i].GetHuman().GetPasswordChanged() != nil {
|
||||
human.PasswordChanged = infos[i].Changed
|
||||
}
|
||||
@ -941,7 +961,7 @@ func TestServer_ListUsers(t *testing.T) {
|
||||
tt.want.Result[i].Details = detailsV2ToV2beta(infos[i].Details)
|
||||
}
|
||||
for i := range tt.want.Result {
|
||||
assert.Contains(ttt, got.Result, tt.want.Result[i])
|
||||
assert.EqualExportedValues(ttt, got.Result[i], tt.want.Result[i])
|
||||
}
|
||||
}
|
||||
integration.AssertListDetails(ttt, tt.want, got)
|
||||
@ -968,6 +988,15 @@ func InUserEmailsQuery(emails []string) *user.SearchQuery {
|
||||
}
|
||||
}
|
||||
|
||||
func PhoneQuery(number string) *user.SearchQuery {
|
||||
return &user.SearchQuery{Query: &user.SearchQuery_PhoneQuery{
|
||||
PhoneQuery: &user.PhoneQuery{
|
||||
Number: number,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func UsernameQuery(username string) *user.SearchQuery {
|
||||
return &user.SearchQuery{Query: &user.SearchQuery_UserNameQuery{
|
||||
UserNameQuery: &user.UserNameQuery{
|
||||
|
@ -238,6 +238,8 @@ func userQueryToQuery(query *user.SearchQuery, level uint8) (query.SearchQuery,
|
||||
return displayNameQueryToQuery(q.DisplayNameQuery)
|
||||
case *user.SearchQuery_EmailQuery:
|
||||
return emailQueryToQuery(q.EmailQuery)
|
||||
case *user.SearchQuery_PhoneQuery:
|
||||
return phoneQueryToQuery(q.PhoneQuery)
|
||||
case *user.SearchQuery_StateQuery:
|
||||
return stateQueryToQuery(q.StateQuery)
|
||||
case *user.SearchQuery_TypeQuery:
|
||||
@ -285,6 +287,10 @@ func emailQueryToQuery(q *user.EmailQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserEmailSearchQuery(q.EmailAddress, object.TextMethodToQuery(q.Method))
|
||||
}
|
||||
|
||||
func phoneQueryToQuery(q *user.PhoneQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserPhoneSearchQuery(q.Number, object.TextMethodToQuery(q.Method))
|
||||
}
|
||||
|
||||
func stateQueryToQuery(q *user.StateQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserStateSearchQuery(int32(q.State))
|
||||
}
|
||||
|
@ -57,6 +57,28 @@ func (c *Commands) ResendUserEmailReturnCode(ctx context.Context, userID string,
|
||||
return c.resendUserEmailCode(ctx, userID, alg, true, "")
|
||||
}
|
||||
|
||||
// SendUserEmailCode generates a new code
|
||||
// and triggers a notification e-mail with the default confirmation URL format.
|
||||
func (c *Commands) SendUserEmailCode(ctx context.Context, userID string, alg crypto.EncryptionAlgorithm) (*domain.Email, error) {
|
||||
return c.sendUserEmailCode(ctx, userID, alg, false, "")
|
||||
}
|
||||
|
||||
// SendUserEmailCodeURLTemplate generates a new code
|
||||
// and triggers a notification e-mail with the confirmation URL rendered from the passed urlTmpl.
|
||||
// urlTmpl must be a valid [tmpl.Template].
|
||||
func (c *Commands) SendUserEmailCodeURLTemplate(ctx context.Context, userID string, alg crypto.EncryptionAlgorithm, urlTmpl string) (*domain.Email, error) {
|
||||
if err := domain.RenderConfirmURLTemplate(io.Discard, urlTmpl, userID, "code", "orgID"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.sendUserEmailCode(ctx, userID, alg, false, urlTmpl)
|
||||
}
|
||||
|
||||
// SendUserEmailReturnCode generates a new code and does not send a notification email.
|
||||
// The generated plain text code will be set in the returned Email object.
|
||||
func (c *Commands) SendUserEmailReturnCode(ctx context.Context, userID string, alg crypto.EncryptionAlgorithm) (*domain.Email, error) {
|
||||
return c.sendUserEmailCode(ctx, userID, alg, true, "")
|
||||
}
|
||||
|
||||
// ChangeUserEmailVerified sets a user's email address and marks it is verified.
|
||||
// No code is generated and no confirmation e-mail is send.
|
||||
func (c *Commands) ChangeUserEmailVerified(ctx context.Context, userID, email string) (*domain.Email, error) {
|
||||
@ -89,7 +111,16 @@ func (c *Commands) resendUserEmailCode(ctx context.Context, userID string, alg c
|
||||
return nil, err
|
||||
}
|
||||
gen := crypto.NewEncryptionGenerator(*config, alg)
|
||||
return c.resendUserEmailCodeWithGenerator(ctx, userID, gen, returnCode, urlTmpl)
|
||||
return c.sendUserEmailCodeWithGenerator(ctx, userID, gen, returnCode, urlTmpl, true)
|
||||
}
|
||||
|
||||
func (c *Commands) sendUserEmailCode(ctx context.Context, userID string, alg crypto.EncryptionAlgorithm, returnCode bool, urlTmpl string) (*domain.Email, error) {
|
||||
config, err := cryptoGeneratorConfig(ctx, c.eventstore.Filter, domain.SecretGeneratorTypeVerifyEmailCode) //nolint:staticcheck
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gen := crypto.NewEncryptionGenerator(*config, alg)
|
||||
return c.sendUserEmailCodeWithGenerator(ctx, userID, gen, returnCode, urlTmpl, false)
|
||||
}
|
||||
|
||||
// changeUserEmailWithGenerator set a user's email address.
|
||||
@ -104,8 +135,8 @@ func (c *Commands) changeUserEmailWithGenerator(ctx context.Context, userID, ema
|
||||
return cmd.Push(ctx)
|
||||
}
|
||||
|
||||
func (c *Commands) resendUserEmailCodeWithGenerator(ctx context.Context, userID string, gen crypto.Generator, returnCode bool, urlTmpl string) (*domain.Email, error) {
|
||||
cmd, err := c.resendUserEmailCodeWithGeneratorEvents(ctx, userID, gen, returnCode, urlTmpl)
|
||||
func (c *Commands) sendUserEmailCodeWithGenerator(ctx context.Context, userID string, gen crypto.Generator, returnCode bool, urlTmpl string, existingCheck bool) (*domain.Email, error) {
|
||||
cmd, err := c.sendUserEmailCodeWithGeneratorEvents(ctx, userID, gen, returnCode, urlTmpl, existingCheck)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -129,7 +160,7 @@ func (c *Commands) changeUserEmailWithGeneratorEvents(ctx context.Context, userI
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func (c *Commands) resendUserEmailCodeWithGeneratorEvents(ctx context.Context, userID string, gen crypto.Generator, returnCode bool, urlTmpl string) (*UserEmailEvents, error) {
|
||||
func (c *Commands) sendUserEmailCodeWithGeneratorEvents(ctx context.Context, userID string, gen crypto.Generator, returnCode bool, urlTmpl string, existingCheck bool) (*UserEmailEvents, error) {
|
||||
cmd, err := c.NewUserEmailEvents(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -137,7 +168,7 @@ func (c *Commands) resendUserEmailCodeWithGeneratorEvents(ctx context.Context, u
|
||||
if err = c.checkPermissionUpdateUser(ctx, cmd.aggregate.ResourceOwner, userID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if cmd.model.Code == nil {
|
||||
if existingCheck && cmd.model.Code == nil {
|
||||
return nil, zerrors.ThrowPreconditionFailed(err, "EMAIL-5w5ilin4yt", "Errors.User.Code.Empty")
|
||||
}
|
||||
if err = cmd.AddGeneratedCode(ctx, gen, urlTmpl, returnCode); err != nil {
|
||||
|
@ -512,6 +512,85 @@ func TestCommands_ResendUserEmailCode(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommands_SendUserEmailCode(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
checkPermission domain.PermissionCheck
|
||||
}
|
||||
type args struct {
|
||||
userID string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "missing permission",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewSecretGeneratorAddedEvent(context.Background(),
|
||||
&instance.NewAggregate("inst1").Aggregate,
|
||||
domain.SecretGeneratorTypeVerifyEmailCode,
|
||||
12, time.Minute, true, true, true, true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username",
|
||||
"firstname",
|
||||
"lastname",
|
||||
"nickname",
|
||||
"displayname",
|
||||
language.German,
|
||||
domain.GenderUnspecified,
|
||||
"email@test.ch",
|
||||
true,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
time.Hour*1,
|
||||
"", false, "",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckNotAllowed(),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
},
|
||||
wantErr: zerrors.ThrowPermissionDenied(nil, "AUTHZ-HKJD33", "Errors.PermissionDenied"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
checkPermission: tt.fields.checkPermission,
|
||||
}
|
||||
_, err := c.SendUserEmailCode(context.Background(), tt.args.userID, crypto.CreateMockEncryptionAlg(gomock.NewController(t)))
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
// successful cases are tested in TestCommands_sendUserEmailCodeWithGeneratorEvents
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommands_ResendUserEmailCodeURLTemplate(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
@ -638,7 +717,99 @@ func TestCommands_ResendUserEmailCodeURLTemplate(t *testing.T) {
|
||||
}
|
||||
_, err := c.ResendUserEmailCodeURLTemplate(context.Background(), tt.args.userID, crypto.CreateMockEncryptionAlg(gomock.NewController(t)), tt.args.urlTmpl)
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
// successful cases are tested in TestCommands_resendUserEmailCodeWithGenerator
|
||||
// successful cases are tested in TestCommands_sendUserEmailCodeWithGeneratorEvents
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommands_SendUserEmailCodeURLTemplate(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
checkPermission domain.PermissionCheck
|
||||
}
|
||||
type args struct {
|
||||
userID string
|
||||
urlTmpl string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "invalid template",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(t),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
urlTmpl: "{{",
|
||||
},
|
||||
wantErr: zerrors.ThrowInvalidArgument(nil, "DOMAIN-oGh5e", "Errors.User.InvalidURLTemplate"),
|
||||
},
|
||||
{
|
||||
name: "permission missing",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewSecretGeneratorAddedEvent(context.Background(),
|
||||
&instance.NewAggregate("inst1").Aggregate,
|
||||
domain.SecretGeneratorTypeVerifyEmailCode,
|
||||
12, time.Minute, true, true, true, true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username",
|
||||
"firstname",
|
||||
"lastname",
|
||||
"nickname",
|
||||
"displayname",
|
||||
language.German,
|
||||
domain.GenderUnspecified,
|
||||
"email@test.ch",
|
||||
true,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
time.Hour*1,
|
||||
"", false, "",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckNotAllowed(),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
urlTmpl: "https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}",
|
||||
},
|
||||
wantErr: zerrors.ThrowPermissionDenied(nil, "AUTHZ-HKJD33", "Errors.PermissionDenied"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
checkPermission: tt.fields.checkPermission,
|
||||
}
|
||||
_, err := c.SendUserEmailCodeURLTemplate(context.Background(), tt.args.userID, crypto.CreateMockEncryptionAlg(gomock.NewController(t)), tt.args.urlTmpl)
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
// successful cases are tested in TestCommands_sendUserEmailCodeWithGeneratorEvents
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -760,6 +931,85 @@ func TestCommands_ResendUserEmailReturnCode(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommands_SendUserEmailReturnCode(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
checkPermission domain.PermissionCheck
|
||||
}
|
||||
type args struct {
|
||||
userID string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "missing permission",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewSecretGeneratorAddedEvent(context.Background(),
|
||||
&instance.NewAggregate("inst1").Aggregate,
|
||||
domain.SecretGeneratorTypeVerifyEmailCode,
|
||||
12, time.Minute, true, true, true, true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username",
|
||||
"firstname",
|
||||
"lastname",
|
||||
"nickname",
|
||||
"displayname",
|
||||
language.German,
|
||||
domain.GenderUnspecified,
|
||||
"email@test.ch",
|
||||
true,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
time.Hour*1,
|
||||
"", false, "",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckNotAllowed(),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
},
|
||||
wantErr: zerrors.ThrowPermissionDenied(nil, "AUTHZ-HKJD33", "Errors.PermissionDenied"),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
checkPermission: tt.fields.checkPermission,
|
||||
}
|
||||
_, err := c.SendUserEmailReturnCode(context.Background(), tt.args.userID, crypto.CreateMockEncryptionAlg(gomock.NewController(t)))
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
// successful cases are tested in TestCommands_sendUserEmailCodeWithGeneratorEvents
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommands_ChangeUserEmailVerified(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
@ -1218,15 +1468,16 @@ func TestCommands_changeUserEmailWithGenerator(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommands_resendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
func TestCommands_sendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
checkPermission domain.PermissionCheck
|
||||
}
|
||||
type args struct {
|
||||
userID string
|
||||
returnCode bool
|
||||
urlTmpl string
|
||||
userID string
|
||||
returnCode bool
|
||||
urlTmpl string
|
||||
checkExisting bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -1247,37 +1498,6 @@ func TestCommands_resendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
},
|
||||
wantErr: zerrors.ThrowInvalidArgument(nil, "COMMAND-0Gzs3", "Errors.User.Email.IDMissing"),
|
||||
},
|
||||
{
|
||||
name: "resend code, missing code",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username",
|
||||
"firstname",
|
||||
"lastname",
|
||||
"nickname",
|
||||
"displayname",
|
||||
language.German,
|
||||
domain.GenderUnspecified,
|
||||
"email@test.ch",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
returnCode: false,
|
||||
urlTmpl: "",
|
||||
},
|
||||
wantErr: zerrors.ThrowPreconditionFailed(nil, "EMAIL-5w5ilin4yt", "Errors.User.Code.Empty"),
|
||||
},
|
||||
{
|
||||
name: "missing permission",
|
||||
fields: fields{
|
||||
@ -1322,6 +1542,58 @@ func TestCommands_resendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
},
|
||||
wantErr: zerrors.ThrowPermissionDenied(nil, "AUTHZ-HKJD33", "Errors.PermissionDenied"),
|
||||
},
|
||||
{
|
||||
name: "send code",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username",
|
||||
"firstname",
|
||||
"lastname",
|
||||
"nickname",
|
||||
"displayname",
|
||||
language.German,
|
||||
domain.GenderUnspecified,
|
||||
"email@test.ch",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
time.Hour*1,
|
||||
"", false, "",
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
returnCode: false,
|
||||
urlTmpl: "",
|
||||
checkExisting: false,
|
||||
},
|
||||
want: &domain.Email{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "user1",
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
EmailAddress: "email@test.ch",
|
||||
IsEmailVerified: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "resend code",
|
||||
fields: fields{
|
||||
@ -1373,9 +1645,10 @@ func TestCommands_resendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
returnCode: false,
|
||||
urlTmpl: "",
|
||||
userID: "user1",
|
||||
returnCode: false,
|
||||
urlTmpl: "",
|
||||
checkExisting: true,
|
||||
},
|
||||
want: &domain.Email{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
@ -1387,7 +1660,7 @@ func TestCommands_resendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "resend code, return code",
|
||||
name: "resend code, missing code",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
@ -1406,17 +1679,36 @@ func TestCommands_resendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
returnCode: false,
|
||||
urlTmpl: "",
|
||||
checkExisting: true,
|
||||
},
|
||||
wantErr: zerrors.ThrowPreconditionFailed(nil, "EMAIL-5w5ilin4yt", "Errors.User.Code.Empty"),
|
||||
},
|
||||
{
|
||||
name: "send code, return code",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
time.Hour*1,
|
||||
"", false, "",
|
||||
"username",
|
||||
"firstname",
|
||||
"lastname",
|
||||
"nickname",
|
||||
"displayname",
|
||||
language.German,
|
||||
domain.GenderUnspecified,
|
||||
"email@test.ch",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -1437,9 +1729,10 @@ func TestCommands_resendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
returnCode: true,
|
||||
urlTmpl: "",
|
||||
userID: "user1",
|
||||
returnCode: true,
|
||||
urlTmpl: "",
|
||||
checkExisting: false,
|
||||
},
|
||||
want: &domain.Email{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
@ -1452,7 +1745,7 @@ func TestCommands_resendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "resend code, URL template",
|
||||
name: "send code, URL template",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
@ -1471,19 +1764,6 @@ func TestCommands_resendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
true,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("a"),
|
||||
},
|
||||
time.Hour*1,
|
||||
"", false, "",
|
||||
),
|
||||
),
|
||||
),
|
||||
expectPush(
|
||||
user.NewHumanEmailCodeAddedEventV2(context.Background(),
|
||||
@ -1502,9 +1782,10 @@ func TestCommands_resendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
checkPermission: newMockPermissionCheckAllowed(),
|
||||
},
|
||||
args: args{
|
||||
userID: "user1",
|
||||
returnCode: false,
|
||||
urlTmpl: "https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}",
|
||||
userID: "user1",
|
||||
returnCode: false,
|
||||
urlTmpl: "https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}",
|
||||
checkExisting: false,
|
||||
},
|
||||
want: &domain.Email{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
@ -1522,7 +1803,7 @@ func TestCommands_resendUserEmailCodeWithGeneratorEvents(t *testing.T) {
|
||||
eventstore: tt.fields.eventstore,
|
||||
checkPermission: tt.fields.checkPermission,
|
||||
}
|
||||
got, err := c.resendUserEmailCodeWithGenerator(context.Background(), tt.args.userID, GetMockSecretGenerator(t), tt.args.returnCode, tt.args.urlTmpl)
|
||||
got, err := c.sendUserEmailCodeWithGenerator(context.Background(), tt.args.userID, GetMockSecretGenerator(t), tt.args.returnCode, tt.args.urlTmpl, tt.args.checkExisting)
|
||||
require.ErrorIs(t, err, tt.wantErr)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
|
@ -64,10 +64,22 @@ func (t *Translator) SupportedLanguages() []language.Tag {
|
||||
return t.allowedLanguages
|
||||
}
|
||||
|
||||
// AddMessages adds messages to the translator for the given language tag.
|
||||
// If the tag is not in the allowed languages, the messages are not added.
|
||||
func (t *Translator) AddMessages(tag language.Tag, messages ...Message) error {
|
||||
if len(messages) == 0 {
|
||||
return nil
|
||||
}
|
||||
var isAllowed bool
|
||||
for _, allowed := range t.allowedLanguages {
|
||||
if allowed == tag {
|
||||
isAllowed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isAllowed {
|
||||
return nil
|
||||
}
|
||||
i18nMessages := make([]*i18n.Message, len(messages))
|
||||
for i, message := range messages {
|
||||
i18nMessages[i] = &i18n.Message{
|
||||
|
@ -126,7 +126,7 @@ func ParseMetadata(metadata []byte) (*saml.EntityDescriptor, error) {
|
||||
if _, err := reader.Seek(0, io.SeekStart); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entities := &saml.EntitiesDescriptor{}
|
||||
entities := &EntitiesDescriptor{}
|
||||
if err := decoder.Decode(entities); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -253,3 +253,26 @@ func nameIDFormatFromDomain(format domain.SAMLNameIDFormat) saml.NameIDFormat {
|
||||
return saml.UnspecifiedNameIDFormat
|
||||
}
|
||||
}
|
||||
|
||||
// EntitiesDescriptor is a workaround until we eventually fork the crewjam/saml library, since maintenance on that repo seems to have stopped.
|
||||
// This is to be able to handle xsd:duration format using the UnmarshalXML method.
|
||||
// crewjam/saml only implements the xsd:dateTime format for EntityDescriptor, but not EntitiesDescriptor.
|
||||
type EntitiesDescriptor saml.EntitiesDescriptor
|
||||
|
||||
// UnmarshalXML implements xml.Unmarshaler
|
||||
func (m *EntitiesDescriptor) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
type Alias EntitiesDescriptor
|
||||
aux := &struct {
|
||||
ValidUntil *saml.RelaxedTime `xml:"validUntil,attr,omitempty"`
|
||||
CacheDuration *saml.Duration `xml:"cacheDuration,attr,omitempty"`
|
||||
*Alias
|
||||
}{
|
||||
Alias: (*Alias)(m),
|
||||
}
|
||||
if err := d.DecodeElement(aux, &start); err != nil {
|
||||
return err
|
||||
}
|
||||
m.ValidUntil = (*time.Time)(aux.ValidUntil)
|
||||
m.CacheDuration = (*time.Duration)(aux.CacheDuration)
|
||||
return nil
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package saml
|
||||
import (
|
||||
"encoding/xml"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/crewjam/saml"
|
||||
"github.com/crewjam/saml/samlsp"
|
||||
@ -271,6 +272,31 @@ func TestParseMetadata(t *testing.T) {
|
||||
},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"valid entities using xsd duration descriptor",
|
||||
args{
|
||||
metadata: []byte(`<?xml version="1.0" encoding="UTF-8"?><EntitiesDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" cacheDuration="PT5H"><EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" entityID="http://localhost:8000/metadata" cacheDuration="PT5H"><IDPSSODescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"><SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8000/sso"></SingleSignOnService></IDPSSODescriptor></EntityDescriptor></EntitiesDescriptor>`),
|
||||
},
|
||||
&saml.EntityDescriptor{
|
||||
EntityID: "http://localhost:8000/metadata",
|
||||
CacheDuration: 5 * time.Hour,
|
||||
IDPSSODescriptors: []saml.IDPSSODescriptor{
|
||||
{
|
||||
XMLName: xml.Name{
|
||||
Space: "urn:oasis:names:tc:SAML:2.0:metadata",
|
||||
Local: "IDPSSODescriptor",
|
||||
},
|
||||
SingleSignOnServices: []saml.Endpoint{
|
||||
{
|
||||
Binding: saml.HTTPRedirectBinding,
|
||||
Location: "http://localhost:8000/sso",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
@ -271,7 +271,7 @@ func (i *Instance) CreateOrganizationWithUserID(ctx context.Context, name, userI
|
||||
return resp
|
||||
}
|
||||
|
||||
func (i *Instance) CreateHumanUserVerified(ctx context.Context, org, email string) *user_v2.AddHumanUserResponse {
|
||||
func (i *Instance) CreateHumanUserVerified(ctx context.Context, org, email, phone string) *user_v2.AddHumanUserResponse {
|
||||
resp, err := i.Client.UserV2.AddHumanUser(ctx, &user_v2.AddHumanUserRequest{
|
||||
Organization: &object.Organization{
|
||||
Org: &object.Organization_OrgId{
|
||||
@ -292,7 +292,7 @@ func (i *Instance) CreateHumanUserVerified(ctx context.Context, org, email strin
|
||||
},
|
||||
},
|
||||
Phone: &user_v2.SetHumanPhone{
|
||||
Phone: "+41791234567",
|
||||
Phone: phone,
|
||||
Verification: &user_v2.SetHumanPhone_IsVerified{
|
||||
IsVerified: true,
|
||||
},
|
||||
|
@ -30,6 +30,7 @@ message SearchQuery {
|
||||
NotQuery not_query = 13;
|
||||
InUserEmailsQuery in_user_emails_query = 14;
|
||||
OrganizationIdQuery organization_id_query = 15;
|
||||
PhoneQuery phone_query = 16;
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,6 +185,26 @@ message EmailQuery {
|
||||
];
|
||||
}
|
||||
|
||||
// Query for users with a specific phone.
|
||||
message PhoneQuery {
|
||||
string number = 1 [
|
||||
(validate.rules).string = {max_len: 20},
|
||||
(google.api.field_behavior) = REQUIRED,
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
description: "Phone number of the user"
|
||||
min_length: 1;
|
||||
max_length: 20;
|
||||
example: "\"+41791234567\"";
|
||||
}
|
||||
];
|
||||
zitadel.object.v2.TextQueryMethod method = 2 [
|
||||
(validate.rules).enum.defined_only = true,
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
description: "defines which text equality method is used";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
// Query for users with a specific state.
|
||||
message LoginNameQuery {
|
||||
string login_name = 1 [
|
||||
|
@ -252,9 +252,34 @@ service UserService {
|
||||
};
|
||||
}
|
||||
|
||||
// Send code to verify user email
|
||||
//
|
||||
// Send code to verify user email.
|
||||
rpc SendEmailCode (SendEmailCodeRequest) returns (SendEmailCodeResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v2/users/{user_id}/email/send"
|
||||
body: "*"
|
||||
};
|
||||
|
||||
option (zitadel.protoc_gen_zitadel.v2.options) = {
|
||||
auth_option: {
|
||||
permission: "authenticated"
|
||||
}
|
||||
};
|
||||
|
||||
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||
responses: {
|
||||
key: "200"
|
||||
value: {
|
||||
description: "OK";
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// Verify the email
|
||||
//
|
||||
// Verify the email with the generated code..
|
||||
// Verify the email with the generated code.
|
||||
rpc VerifyEmail (VerifyEmailRequest) returns (VerifyEmailResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/v2/users/{user_id}/email/verify"
|
||||
@ -1310,6 +1335,29 @@ message ResendEmailCodeResponse{
|
||||
optional string verification_code = 2;
|
||||
}
|
||||
|
||||
message SendEmailCodeRequest{
|
||||
string user_id = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
(google.api.field_behavior) = REQUIRED,
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
min_length: 1;
|
||||
max_length: 200;
|
||||
example: "\"69629026806489455\"";
|
||||
}
|
||||
];
|
||||
// if no verification is specified, an email is sent with the default url
|
||||
oneof verification {
|
||||
SendEmailVerificationCode send_code = 2;
|
||||
ReturnEmailVerificationCode return_code = 3;
|
||||
}
|
||||
}
|
||||
|
||||
message SendEmailCodeResponse{
|
||||
zitadel.object.v2.Details details = 1;
|
||||
// in case the verification was set to return_code, the code will be returned
|
||||
optional string verification_code = 2;
|
||||
}
|
||||
|
||||
message VerifyEmailRequest{
|
||||
string user_id = 1 [
|
||||
(validate.rules).string = {min_len: 1, max_len: 200},
|
||||
|
@ -30,6 +30,7 @@ message SearchQuery {
|
||||
NotQuery not_query = 13;
|
||||
InUserEmailsQuery in_user_emails_query = 14;
|
||||
OrganizationIdQuery organization_id_query = 15;
|
||||
PhoneQuery phone_query = 16;
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,6 +185,26 @@ message EmailQuery {
|
||||
];
|
||||
}
|
||||
|
||||
// Query for users with a specific phone.
|
||||
message PhoneQuery {
|
||||
string number = 1 [
|
||||
(validate.rules).string = {max_len: 20},
|
||||
(google.api.field_behavior) = REQUIRED,
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
description: "Phone number of the user"
|
||||
min_length: 1;
|
||||
max_length: 20;
|
||||
example: "\"+41791234567\"";
|
||||
}
|
||||
];
|
||||
zitadel.object.v2beta.TextQueryMethod method = 2 [
|
||||
(validate.rules).enum.defined_only = true,
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
description: "defines which text equality method is used";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
// Query for users with a specific state.
|
||||
message LoginNameQuery {
|
||||
string login_name = 1 [
|
||||
|
Loading…
x
Reference in New Issue
Block a user