mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 21:47:32 +00:00
feat: add SAML as identity provider (#6454)
* feat: first implementation for saml sp * fix: add command side instance and org for saml provider * fix: add query side instance and org for saml provider * fix: request handling in event and retrieval of finished intent * fix: add review changes and integration tests * fix: add integration tests for saml idp * fix: correct unit tests with review changes * fix: add saml session unit test * fix: add saml session unit test * fix: add saml session unit test * fix: changes from review * fix: changes from review * fix: proto build error * fix: proto build error * fix: proto build error * fix: proto require metadata oneof * fix: login with saml provider * fix: integration test for saml assertion * lint client.go * fix json tag * fix: linting * fix import * fix: linting * fix saml idp query * fix: linting * lint: try all issues * revert linting config * fix: add regenerate endpoints * fix: translations * fix mk.yaml * ignore acs path for user agent cookie * fix: add AuthFromProvider test for saml * fix: integration test for saml retrieve information --------- Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
@@ -426,6 +426,37 @@ func (s *Server) UpdateAppleProvider(ctx context.Context, req *admin_pb.UpdateAp
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) AddSAMLProvider(ctx context.Context, req *admin_pb.AddSAMLProviderRequest) (*admin_pb.AddSAMLProviderResponse, error) {
|
||||
id, details, err := s.command.AddInstanceSAMLProvider(ctx, addSAMLProviderToCommand(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.AddSAMLProviderResponse{
|
||||
Id: id,
|
||||
Details: object_pb.DomainToAddDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateSAMLProvider(ctx context.Context, req *admin_pb.UpdateSAMLProviderRequest) (*admin_pb.UpdateSAMLProviderResponse, error) {
|
||||
details, err := s.command.UpdateInstanceSAMLProvider(ctx, req.Id, updateSAMLProviderToCommand(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.UpdateSAMLProviderResponse{
|
||||
Details: object_pb.DomainToChangeDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) RegenerateSAMLProviderCertificate(ctx context.Context, req *admin_pb.RegenerateSAMLProviderCertificateRequest) (*admin_pb.RegenerateSAMLProviderCertificateResponse, error) {
|
||||
details, err := s.command.RegenerateInstanceSAMLProviderCertificate(ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.RegenerateSAMLProviderCertificateResponse{
|
||||
Details: object_pb.DomainToChangeDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) DeleteProvider(ctx context.Context, req *admin_pb.DeleteProviderRequest) (*admin_pb.DeleteProviderResponse, error) {
|
||||
details, err := s.command.DeleteInstanceProvider(ctx, req.Id)
|
||||
if err != nil {
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"github.com/crewjam/saml"
|
||||
|
||||
idp_grpc "github.com/zitadel/zitadel/internal/api/grpc/idp"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/object"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
@@ -9,6 +11,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin"
|
||||
idp_pb "github.com/zitadel/zitadel/pkg/grpc/idp"
|
||||
)
|
||||
|
||||
func addOIDCIDPRequestToDomain(req *admin_pb.AddOIDCIDPRequest) *domain.IDPConfig {
|
||||
@@ -464,3 +467,40 @@ func updateAppleProviderToCommand(req *admin_pb.UpdateAppleProviderRequest) comm
|
||||
IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions),
|
||||
}
|
||||
}
|
||||
|
||||
func addSAMLProviderToCommand(req *admin_pb.AddSAMLProviderRequest) command.SAMLProvider {
|
||||
return command.SAMLProvider{
|
||||
Name: req.Name,
|
||||
Metadata: req.GetMetadataXml(),
|
||||
MetadataURL: req.GetMetadataUrl(),
|
||||
Binding: bindingToCommand(req.Binding),
|
||||
WithSignedRequest: req.WithSignedRequest,
|
||||
IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions),
|
||||
}
|
||||
}
|
||||
|
||||
func updateSAMLProviderToCommand(req *admin_pb.UpdateSAMLProviderRequest) command.SAMLProvider {
|
||||
return command.SAMLProvider{
|
||||
Name: req.Name,
|
||||
Metadata: req.GetMetadataXml(),
|
||||
MetadataURL: req.GetMetadataUrl(),
|
||||
Binding: bindingToCommand(req.Binding),
|
||||
WithSignedRequest: req.WithSignedRequest,
|
||||
IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions),
|
||||
}
|
||||
}
|
||||
|
||||
func bindingToCommand(binding idp_pb.SAMLBinding) string {
|
||||
switch binding {
|
||||
case idp_pb.SAMLBinding_SAML_BINDING_UNSPECIFIED:
|
||||
return ""
|
||||
case idp_pb.SAMLBinding_SAML_BINDING_POST:
|
||||
return saml.HTTPPostBinding
|
||||
case idp_pb.SAMLBinding_SAML_BINDING_REDIRECT:
|
||||
return saml.HTTPRedirectBinding
|
||||
case idp_pb.SAMLBinding_SAML_BINDING_ARTIFACT:
|
||||
return saml.HTTPArtifactBinding
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
@@ -418,6 +418,37 @@ func (s *Server) UpdateAppleProvider(ctx context.Context, req *mgmt_pb.UpdateApp
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) AddSAMLProvider(ctx context.Context, req *mgmt_pb.AddSAMLProviderRequest) (*mgmt_pb.AddSAMLProviderResponse, error) {
|
||||
id, details, err := s.command.AddOrgSAMLProvider(ctx, authz.GetCtxData(ctx).OrgID, addSAMLProviderToCommand(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.AddSAMLProviderResponse{
|
||||
Id: id,
|
||||
Details: object_pb.DomainToAddDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateSAMLProvider(ctx context.Context, req *mgmt_pb.UpdateSAMLProviderRequest) (*mgmt_pb.UpdateSAMLProviderResponse, error) {
|
||||
details, err := s.command.UpdateOrgSAMLProvider(ctx, authz.GetCtxData(ctx).OrgID, req.Id, updateSAMLProviderToCommand(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.UpdateSAMLProviderResponse{
|
||||
Details: object_pb.DomainToChangeDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) RegenerateSAMLProviderCertificate(ctx context.Context, req *mgmt_pb.RegenerateSAMLProviderCertificateRequest) (*mgmt_pb.RegenerateSAMLProviderCertificateResponse, error) {
|
||||
details, err := s.command.RegenerateOrgSAMLProviderCertificate(ctx, authz.GetCtxData(ctx).OrgID, req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.RegenerateSAMLProviderCertificateResponse{
|
||||
Details: object_pb.DomainToChangeDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) DeleteProvider(ctx context.Context, req *mgmt_pb.DeleteProviderRequest) (*mgmt_pb.DeleteProviderResponse, error) {
|
||||
details, err := s.command.DeleteOrgProvider(ctx, authz.GetCtxData(ctx).OrgID, req.Id)
|
||||
if err != nil {
|
||||
|
@@ -3,6 +3,8 @@ package management
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/crewjam/saml"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
idp_grpc "github.com/zitadel/zitadel/internal/api/grpc/idp"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/object"
|
||||
@@ -12,6 +14,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
iam_model "github.com/zitadel/zitadel/internal/iam/model"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
idp_pb "github.com/zitadel/zitadel/pkg/grpc/idp"
|
||||
mgmt_pb "github.com/zitadel/zitadel/pkg/grpc/management"
|
||||
)
|
||||
|
||||
@@ -481,3 +484,40 @@ func updateAppleProviderToCommand(req *mgmt_pb.UpdateAppleProviderRequest) comma
|
||||
IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions),
|
||||
}
|
||||
}
|
||||
|
||||
func addSAMLProviderToCommand(req *mgmt_pb.AddSAMLProviderRequest) command.SAMLProvider {
|
||||
return command.SAMLProvider{
|
||||
Name: req.Name,
|
||||
Metadata: req.GetMetadataXml(),
|
||||
MetadataURL: req.GetMetadataUrl(),
|
||||
Binding: bindingToCommand(req.Binding),
|
||||
WithSignedRequest: req.WithSignedRequest,
|
||||
IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions),
|
||||
}
|
||||
}
|
||||
|
||||
func updateSAMLProviderToCommand(req *mgmt_pb.UpdateSAMLProviderRequest) command.SAMLProvider {
|
||||
return command.SAMLProvider{
|
||||
Name: req.Name,
|
||||
Metadata: req.GetMetadataXml(),
|
||||
MetadataURL: req.GetMetadataUrl(),
|
||||
Binding: bindingToCommand(req.Binding),
|
||||
WithSignedRequest: req.WithSignedRequest,
|
||||
IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions),
|
||||
}
|
||||
}
|
||||
|
||||
func bindingToCommand(binding idp_pb.SAMLBinding) string {
|
||||
switch binding {
|
||||
case idp_pb.SAMLBinding_SAML_BINDING_UNSPECIFIED:
|
||||
return ""
|
||||
case idp_pb.SAMLBinding_SAML_BINDING_POST:
|
||||
return saml.HTTPPostBinding
|
||||
case idp_pb.SAMLBinding_SAML_BINDING_REDIRECT:
|
||||
return saml.HTTPRedirectBinding
|
||||
case idp_pb.SAMLBinding_SAML_BINDING_ARTIFACT:
|
||||
return saml.HTTPArtifactBinding
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@ type Server struct {
|
||||
userCodeAlg crypto.EncryptionAlgorithm
|
||||
idpAlg crypto.EncryptionAlgorithm
|
||||
idpCallback func(ctx context.Context) string
|
||||
samlRootURL func(ctx context.Context, idpID string) string
|
||||
}
|
||||
|
||||
type Config struct{}
|
||||
@@ -32,6 +33,7 @@ func CreateServer(
|
||||
userCodeAlg crypto.EncryptionAlgorithm,
|
||||
idpAlg crypto.EncryptionAlgorithm,
|
||||
idpCallback func(ctx context.Context) string,
|
||||
samlRootURL func(ctx context.Context, idpID string) string,
|
||||
) *Server {
|
||||
return &Server{
|
||||
command: command,
|
||||
@@ -39,6 +41,7 @@ func CreateServer(
|
||||
userCodeAlg: userCodeAlg,
|
||||
idpAlg: idpAlg,
|
||||
idpCallback: idpCallback,
|
||||
samlRootURL: samlRootURL,
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -145,14 +145,23 @@ func (s *Server) startIDPIntent(ctx context.Context, idpID string, urls *user.Re
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authURL, err := s.command.AuthURLFromProvider(ctx, idpID, intentWriteModel.AggregateID, s.idpCallback(ctx))
|
||||
content, redirect, err := s.command.AuthFromProvider(ctx, idpID, intentWriteModel.AggregateID, s.idpCallback(ctx), s.samlRootURL(ctx, idpID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user.StartIdentityProviderIntentResponse{
|
||||
Details: object.DomainToDetailsPb(details),
|
||||
NextStep: &user.StartIdentityProviderIntentResponse_AuthUrl{AuthUrl: authURL},
|
||||
}, nil
|
||||
if redirect {
|
||||
return &user.StartIdentityProviderIntentResponse{
|
||||
Details: object.DomainToDetailsPb(details),
|
||||
NextStep: &user.StartIdentityProviderIntentResponse_AuthUrl{AuthUrl: content},
|
||||
}, nil
|
||||
} else {
|
||||
return &user.StartIdentityProviderIntentResponse{
|
||||
Details: object.DomainToDetailsPb(details),
|
||||
NextStep: &user.StartIdentityProviderIntentResponse_PostForm{
|
||||
PostForm: []byte(content),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) startLDAPIntent(ctx context.Context, idpID string, ldapCredentials *user.LDAPCredentials) (*user.StartIdentityProviderIntentResponse, error) {
|
||||
@@ -206,7 +215,7 @@ func (s *Server) checkLinkedExternalUser(ctx context.Context, idpID, externalUse
|
||||
}
|
||||
|
||||
func (s *Server) ldapLogin(ctx context.Context, idpID, username, password string) (idp.User, string, map[string][]string, error) {
|
||||
provider, err := s.command.GetProvider(ctx, idpID, "")
|
||||
provider, err := s.command.GetProvider(ctx, idpID, "", "")
|
||||
if err != nil {
|
||||
return nil, "", nil, err
|
||||
}
|
||||
@@ -279,6 +288,14 @@ func idpIntentToIDPIntentPb(intent *command.IDPIntentWriteModel, alg crypto.Encr
|
||||
information.IdpInformation.Access = access
|
||||
}
|
||||
|
||||
if intent.Assertion != nil {
|
||||
assertion, err := crypto.Decrypt(intent.Assertion, alg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
information.IdpInformation.Access = IDPSAMLResponseToPb(assertion)
|
||||
}
|
||||
|
||||
return information, nil
|
||||
}
|
||||
|
||||
@@ -330,6 +347,14 @@ func IDPEntryAttributesToPb(entryAttributes map[string][]string) (*user.IDPInfor
|
||||
}, nil
|
||||
}
|
||||
|
||||
func IDPSAMLResponseToPb(assertion []byte) *user.IDPInformation_Saml {
|
||||
return &user.IDPInformation_Saml{
|
||||
Saml: &user.IDPSAMLAccessInformation{
|
||||
Assertion: assertion,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) checkIntentToken(token string, intentID string) error {
|
||||
return crypto.CheckToken(s.idpAlg, token, intentID)
|
||||
}
|
||||
|
@@ -5,8 +5,8 @@ package user_test
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -624,14 +624,24 @@ func TestServer_AddIDPLink(t *testing.T) {
|
||||
|
||||
func TestServer_StartIdentityProviderIntent(t *testing.T) {
|
||||
idpID := Tester.AddGenericOAuthProvider(t)
|
||||
samlIdpID := Tester.AddSAMLProvider(t)
|
||||
samlRedirectIdpID := Tester.AddSAMLRedirectProvider(t)
|
||||
samlPostIdpID := Tester.AddSAMLPostProvider(t)
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *user.StartIdentityProviderIntentRequest
|
||||
}
|
||||
type want struct {
|
||||
details *object.Details
|
||||
url string
|
||||
parametersExisting []string
|
||||
parametersEqual map[string]string
|
||||
postForm bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *user.StartIdentityProviderIntentResponse
|
||||
want want
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
@@ -642,11 +652,10 @@ func TestServer_StartIdentityProviderIntent(t *testing.T) {
|
||||
IdpId: idpID,
|
||||
},
|
||||
},
|
||||
want: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "next step auth url",
|
||||
name: "next step oauth auth url",
|
||||
args: args{
|
||||
CTX,
|
||||
&user.StartIdentityProviderIntentRequest{
|
||||
@@ -659,14 +668,91 @@ func TestServer_StartIdentityProviderIntent(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &user.StartIdentityProviderIntentResponse{
|
||||
Details: &object.Details{
|
||||
want: want{
|
||||
details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Tester.Organisation.ID,
|
||||
},
|
||||
NextStep: &user.StartIdentityProviderIntentResponse_AuthUrl{
|
||||
AuthUrl: "https://example.com/oauth/v2/authorize?client_id=clientID&prompt=select_account&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fidps%2Fcallback&response_type=code&scope=openid+profile+email&state=",
|
||||
url: "https://example.com/oauth/v2/authorize",
|
||||
parametersEqual: map[string]string{
|
||||
"client_id": "clientID",
|
||||
"prompt": "select_account",
|
||||
"redirect_uri": "http://localhost:8080/idps/callback",
|
||||
"response_type": "code",
|
||||
"scope": "openid profile email",
|
||||
},
|
||||
parametersExisting: []string{"state"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "next step saml default",
|
||||
args: args{
|
||||
CTX,
|
||||
&user.StartIdentityProviderIntentRequest{
|
||||
IdpId: samlIdpID,
|
||||
Content: &user.StartIdentityProviderIntentRequest_Urls{
|
||||
Urls: &user.RedirectURLs{
|
||||
SuccessUrl: "https://example.com/success",
|
||||
FailureUrl: "https://example.com/failure",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: want{
|
||||
details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Tester.Organisation.ID,
|
||||
},
|
||||
url: "http://localhost:8000/sso",
|
||||
parametersExisting: []string{"RelayState", "SAMLRequest"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "next step saml auth url",
|
||||
args: args{
|
||||
CTX,
|
||||
&user.StartIdentityProviderIntentRequest{
|
||||
IdpId: samlRedirectIdpID,
|
||||
Content: &user.StartIdentityProviderIntentRequest_Urls{
|
||||
Urls: &user.RedirectURLs{
|
||||
SuccessUrl: "https://example.com/success",
|
||||
FailureUrl: "https://example.com/failure",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: want{
|
||||
details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Tester.Organisation.ID,
|
||||
},
|
||||
url: "http://localhost:8000/sso",
|
||||
parametersExisting: []string{"RelayState", "SAMLRequest"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "next step saml form",
|
||||
args: args{
|
||||
CTX,
|
||||
&user.StartIdentityProviderIntentRequest{
|
||||
IdpId: samlPostIdpID,
|
||||
Content: &user.StartIdentityProviderIntentRequest_Urls{
|
||||
Urls: &user.RedirectURLs{
|
||||
SuccessUrl: "https://example.com/success",
|
||||
FailureUrl: "https://example.com/failure",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: want{
|
||||
details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
ResourceOwner: Tester.Organisation.ID,
|
||||
},
|
||||
postForm: true,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
@@ -680,12 +766,25 @@ func TestServer_StartIdentityProviderIntent(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
if nextStep := tt.want.GetNextStep(); nextStep != nil {
|
||||
if !strings.HasPrefix(got.GetAuthUrl(), tt.want.GetAuthUrl()) {
|
||||
assert.Failf(t, "auth url does not match", "expected: %s, but got: %s", tt.want.GetAuthUrl(), got.GetAuthUrl())
|
||||
if tt.want.url != "" {
|
||||
authUrl, err := url.Parse(got.GetAuthUrl())
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Len(t, authUrl.Query(), len(tt.want.parametersEqual)+len(tt.want.parametersExisting))
|
||||
|
||||
for _, existing := range tt.want.parametersExisting {
|
||||
assert.True(t, authUrl.Query().Has(existing))
|
||||
}
|
||||
for key, equal := range tt.want.parametersEqual {
|
||||
assert.Equal(t, equal, authUrl.Query().Get(key))
|
||||
}
|
||||
}
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
if tt.want.postForm {
|
||||
assert.NotEmpty(t, got.GetPostForm())
|
||||
}
|
||||
integration.AssertDetails(t, &user.StartIdentityProviderIntentResponse{
|
||||
Details: tt.want.details,
|
||||
}, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -697,6 +796,7 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) {
|
||||
successfulWithUserID, WithUsertoken, WithUserchangeDate, WithUsersequence := Tester.CreateSuccessfulOAuthIntent(t, idpID, "user", "id")
|
||||
ldapSuccessfulID, ldapToken, ldapChangeDate, ldapSequence := Tester.CreateSuccessfulLDAPIntent(t, idpID, "", "id")
|
||||
ldapSuccessfulWithUserID, ldapWithUserToken, ldapWithUserChangeDate, ldapWithUserSequence := Tester.CreateSuccessfulLDAPIntent(t, idpID, "user", "id")
|
||||
samlSuccessfulID, samlToken, samlChangeDate, samlSequence := Tester.CreateSuccessfulSAMLIntent(t, idpID, "", "id")
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *user.RetrieveIdentityProviderIntentRequest
|
||||
@@ -895,6 +995,44 @@ func TestServer_RetrieveIdentityProviderIntent(t *testing.T) {
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "retrieve successful saml intent",
|
||||
args: args{
|
||||
CTX,
|
||||
&user.RetrieveIdentityProviderIntentRequest{
|
||||
IdpIntentId: samlSuccessfulID,
|
||||
IdpIntentToken: samlToken,
|
||||
},
|
||||
},
|
||||
want: &user.RetrieveIdentityProviderIntentResponse{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.New(samlChangeDate),
|
||||
ResourceOwner: Tester.Organisation.ID,
|
||||
Sequence: samlSequence,
|
||||
},
|
||||
IdpInformation: &user.IDPInformation{
|
||||
Access: &user.IDPInformation_Saml{
|
||||
Saml: &user.IDPSAMLAccessInformation{
|
||||
Assertion: []byte("<Assertion xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"id\" IssueInstant=\"0001-01-01T00:00:00Z\" Version=\"\"><Issuer xmlns=\"urn:oasis:names:tc:SAML:2.0:assertion\" NameQualifier=\"\" SPNameQualifier=\"\" Format=\"\" SPProvidedID=\"\"></Issuer></Assertion>"),
|
||||
},
|
||||
},
|
||||
IdpId: idpID,
|
||||
UserId: "id",
|
||||
UserName: "",
|
||||
RawInformation: func() *structpb.Struct {
|
||||
s, err := structpb.NewStruct(map[string]interface{}{
|
||||
"id": "id",
|
||||
"attributes": map[string]interface{}{
|
||||
"attribute1": []interface{}{"value1"},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return s
|
||||
}(),
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
Reference in New Issue
Block a user