mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 21:17:32 +00:00
feat: idp v2 api GetIDPByID (#8425)
# Which Problems Are Solved GetIDPByID as endpoint in the API v2 so that it can be available for the new login. # How the Problems Are Solved Create GetIDPByID endpoint with IDP v2 API, throught the GetProviderByID implementation from admin and management API. # Additional Changes - Remove the OwnerType attribute from the response, as the information is available through the resourceOwner. - correct refs to messages in proto which are used for doc generation - renaming of elements for API v3 # Additional Context Closes #8337 --------- Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
@@ -157,7 +157,7 @@ func (s *Server) GetProviderByID(ctx context.Context, req *admin_pb.GetProviderB
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
idp, err := s.query.IDPTemplateByID(ctx, true, req.Id, false, instanceIDQuery)
|
||||
idp, err := s.query.IDPTemplateByID(ctx, true, req.Id, false, nil, instanceIDQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
369
internal/api/grpc/idp/v2/query.go
Normal file
369
internal/api/grpc/idp/v2/query.go
Normal file
@@ -0,0 +1,369 @@
|
||||
package idp
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/crewjam/saml"
|
||||
"github.com/muhlemmer/gu"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/object/v2"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/idp/providers/azuread"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
idp_rp "github.com/zitadel/zitadel/internal/repository/idp"
|
||||
idp_pb "github.com/zitadel/zitadel/pkg/grpc/idp/v2"
|
||||
)
|
||||
|
||||
func (s *Server) GetIDPByID(ctx context.Context, req *idp_pb.GetIDPByIDRequest) (*idp_pb.GetIDPByIDResponse, error) {
|
||||
idp, err := s.query.IDPTemplateByID(ctx, true, req.Id, false, s.checkPermission)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &idp_pb.GetIDPByIDResponse{Idp: idpToPb(idp)}, nil
|
||||
}
|
||||
|
||||
func idpToPb(idp *query.IDPTemplate) *idp_pb.IDP {
|
||||
return &idp_pb.IDP{
|
||||
Id: idp.ID,
|
||||
Details: object.DomainToDetailsPb(
|
||||
&domain.ObjectDetails{
|
||||
Sequence: idp.Sequence,
|
||||
EventDate: idp.ChangeDate,
|
||||
ResourceOwner: idp.ResourceOwner,
|
||||
}),
|
||||
State: idpStateToPb(idp.State),
|
||||
Name: idp.Name,
|
||||
Type: idpTypeToPb(idp.Type),
|
||||
Config: configToPb(idp),
|
||||
}
|
||||
}
|
||||
|
||||
func idpStateToPb(state domain.IDPState) idp_pb.IDPState {
|
||||
switch state {
|
||||
case domain.IDPStateActive:
|
||||
return idp_pb.IDPState_IDP_STATE_ACTIVE
|
||||
case domain.IDPStateInactive:
|
||||
return idp_pb.IDPState_IDP_STATE_INACTIVE
|
||||
case domain.IDPStateUnspecified:
|
||||
return idp_pb.IDPState_IDP_STATE_UNSPECIFIED
|
||||
case domain.IDPStateMigrated:
|
||||
return idp_pb.IDPState_IDP_STATE_MIGRATED
|
||||
case domain.IDPStateRemoved:
|
||||
return idp_pb.IDPState_IDP_STATE_REMOVED
|
||||
default:
|
||||
return idp_pb.IDPState_IDP_STATE_UNSPECIFIED
|
||||
}
|
||||
}
|
||||
|
||||
func idpTypeToPb(idpType domain.IDPType) idp_pb.IDPType {
|
||||
switch idpType {
|
||||
case domain.IDPTypeOIDC:
|
||||
return idp_pb.IDPType_IDP_TYPE_OIDC
|
||||
case domain.IDPTypeJWT:
|
||||
return idp_pb.IDPType_IDP_TYPE_JWT
|
||||
case domain.IDPTypeOAuth:
|
||||
return idp_pb.IDPType_IDP_TYPE_OAUTH
|
||||
case domain.IDPTypeLDAP:
|
||||
return idp_pb.IDPType_IDP_TYPE_LDAP
|
||||
case domain.IDPTypeAzureAD:
|
||||
return idp_pb.IDPType_IDP_TYPE_AZURE_AD
|
||||
case domain.IDPTypeGitHub:
|
||||
return idp_pb.IDPType_IDP_TYPE_GITHUB
|
||||
case domain.IDPTypeGitHubEnterprise:
|
||||
return idp_pb.IDPType_IDP_TYPE_GITHUB_ES
|
||||
case domain.IDPTypeGitLab:
|
||||
return idp_pb.IDPType_IDP_TYPE_GITLAB
|
||||
case domain.IDPTypeGitLabSelfHosted:
|
||||
return idp_pb.IDPType_IDP_TYPE_GITLAB_SELF_HOSTED
|
||||
case domain.IDPTypeGoogle:
|
||||
return idp_pb.IDPType_IDP_TYPE_GOOGLE
|
||||
case domain.IDPTypeApple:
|
||||
return idp_pb.IDPType_IDP_TYPE_APPLE
|
||||
case domain.IDPTypeSAML:
|
||||
return idp_pb.IDPType_IDP_TYPE_SAML
|
||||
case domain.IDPTypeUnspecified:
|
||||
return idp_pb.IDPType_IDP_TYPE_UNSPECIFIED
|
||||
default:
|
||||
return idp_pb.IDPType_IDP_TYPE_UNSPECIFIED
|
||||
}
|
||||
}
|
||||
|
||||
func configToPb(config *query.IDPTemplate) *idp_pb.IDPConfig {
|
||||
idpConfig := &idp_pb.IDPConfig{
|
||||
Options: &idp_pb.Options{
|
||||
IsLinkingAllowed: config.IsLinkingAllowed,
|
||||
IsCreationAllowed: config.IsCreationAllowed,
|
||||
IsAutoCreation: config.IsAutoCreation,
|
||||
IsAutoUpdate: config.IsAutoUpdate,
|
||||
AutoLinking: autoLinkingOptionToPb(config.AutoLinking),
|
||||
},
|
||||
}
|
||||
if config.OAuthIDPTemplate != nil {
|
||||
oauthConfigToPb(idpConfig, config.OAuthIDPTemplate)
|
||||
return idpConfig
|
||||
}
|
||||
if config.OIDCIDPTemplate != nil {
|
||||
oidcConfigToPb(idpConfig, config.OIDCIDPTemplate)
|
||||
return idpConfig
|
||||
}
|
||||
if config.JWTIDPTemplate != nil {
|
||||
jwtConfigToPb(idpConfig, config.JWTIDPTemplate)
|
||||
return idpConfig
|
||||
}
|
||||
if config.AzureADIDPTemplate != nil {
|
||||
azureConfigToPb(idpConfig, config.AzureADIDPTemplate)
|
||||
return idpConfig
|
||||
}
|
||||
if config.GitHubIDPTemplate != nil {
|
||||
githubConfigToPb(idpConfig, config.GitHubIDPTemplate)
|
||||
return idpConfig
|
||||
}
|
||||
if config.GitHubEnterpriseIDPTemplate != nil {
|
||||
githubEnterpriseConfigToPb(idpConfig, config.GitHubEnterpriseIDPTemplate)
|
||||
return idpConfig
|
||||
}
|
||||
if config.GitLabIDPTemplate != nil {
|
||||
gitlabConfigToPb(idpConfig, config.GitLabIDPTemplate)
|
||||
return idpConfig
|
||||
}
|
||||
if config.GitLabSelfHostedIDPTemplate != nil {
|
||||
gitlabSelfHostedConfigToPb(idpConfig, config.GitLabSelfHostedIDPTemplate)
|
||||
return idpConfig
|
||||
}
|
||||
if config.GoogleIDPTemplate != nil {
|
||||
googleConfigToPb(idpConfig, config.GoogleIDPTemplate)
|
||||
return idpConfig
|
||||
}
|
||||
if config.LDAPIDPTemplate != nil {
|
||||
ldapConfigToPb(idpConfig, config.LDAPIDPTemplate)
|
||||
return idpConfig
|
||||
}
|
||||
if config.AppleIDPTemplate != nil {
|
||||
appleConfigToPb(idpConfig, config.AppleIDPTemplate)
|
||||
return idpConfig
|
||||
}
|
||||
if config.SAMLIDPTemplate != nil {
|
||||
samlConfigToPb(idpConfig, config.SAMLIDPTemplate)
|
||||
return idpConfig
|
||||
}
|
||||
return idpConfig
|
||||
}
|
||||
|
||||
func autoLinkingOptionToPb(linking domain.AutoLinkingOption) idp_pb.AutoLinkingOption {
|
||||
switch linking {
|
||||
case domain.AutoLinkingOptionUnspecified:
|
||||
return idp_pb.AutoLinkingOption_AUTO_LINKING_OPTION_UNSPECIFIED
|
||||
case domain.AutoLinkingOptionUsername:
|
||||
return idp_pb.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME
|
||||
case domain.AutoLinkingOptionEmail:
|
||||
return idp_pb.AutoLinkingOption_AUTO_LINKING_OPTION_EMAIL
|
||||
default:
|
||||
return idp_pb.AutoLinkingOption_AUTO_LINKING_OPTION_UNSPECIFIED
|
||||
}
|
||||
}
|
||||
|
||||
func oauthConfigToPb(idpConfig *idp_pb.IDPConfig, template *query.OAuthIDPTemplate) {
|
||||
idpConfig.Config = &idp_pb.IDPConfig_Oauth{
|
||||
Oauth: &idp_pb.OAuthConfig{
|
||||
ClientId: template.ClientID,
|
||||
AuthorizationEndpoint: template.AuthorizationEndpoint,
|
||||
TokenEndpoint: template.TokenEndpoint,
|
||||
UserEndpoint: template.UserEndpoint,
|
||||
Scopes: template.Scopes,
|
||||
IdAttribute: template.IDAttribute,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func oidcConfigToPb(idpConfig *idp_pb.IDPConfig, template *query.OIDCIDPTemplate) {
|
||||
idpConfig.Config = &idp_pb.IDPConfig_Oidc{
|
||||
Oidc: &idp_pb.GenericOIDCConfig{
|
||||
ClientId: template.ClientID,
|
||||
Issuer: template.Issuer,
|
||||
Scopes: template.Scopes,
|
||||
IsIdTokenMapping: template.IsIDTokenMapping,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func jwtConfigToPb(idpConfig *idp_pb.IDPConfig, template *query.JWTIDPTemplate) {
|
||||
idpConfig.Config = &idp_pb.IDPConfig_Jwt{
|
||||
Jwt: &idp_pb.JWTConfig{
|
||||
JwtEndpoint: template.Endpoint,
|
||||
Issuer: template.Issuer,
|
||||
KeysEndpoint: template.KeysEndpoint,
|
||||
HeaderName: template.HeaderName,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func azureConfigToPb(idpConfig *idp_pb.IDPConfig, template *query.AzureADIDPTemplate) {
|
||||
idpConfig.Config = &idp_pb.IDPConfig_AzureAd{
|
||||
AzureAd: &idp_pb.AzureADConfig{
|
||||
ClientId: template.ClientID,
|
||||
Tenant: azureTenantToPb(template.Tenant),
|
||||
EmailVerified: template.IsEmailVerified,
|
||||
Scopes: template.Scopes,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func azureTenantToPb(tenant string) *idp_pb.AzureADTenant {
|
||||
var tenantType idp_pb.IsAzureADTenantType
|
||||
switch azuread.TenantType(tenant) {
|
||||
case azuread.CommonTenant:
|
||||
tenantType = &idp_pb.AzureADTenant_TenantType{TenantType: idp_pb.AzureADTenantType_AZURE_AD_TENANT_TYPE_COMMON}
|
||||
case azuread.OrganizationsTenant:
|
||||
tenantType = &idp_pb.AzureADTenant_TenantType{TenantType: idp_pb.AzureADTenantType_AZURE_AD_TENANT_TYPE_ORGANISATIONS}
|
||||
case azuread.ConsumersTenant:
|
||||
tenantType = &idp_pb.AzureADTenant_TenantType{TenantType: idp_pb.AzureADTenantType_AZURE_AD_TENANT_TYPE_CONSUMERS}
|
||||
default:
|
||||
tenantType = &idp_pb.AzureADTenant_TenantId{TenantId: tenant}
|
||||
}
|
||||
return &idp_pb.AzureADTenant{Type: tenantType}
|
||||
}
|
||||
|
||||
func githubConfigToPb(idpConfig *idp_pb.IDPConfig, template *query.GitHubIDPTemplate) {
|
||||
idpConfig.Config = &idp_pb.IDPConfig_Github{
|
||||
Github: &idp_pb.GitHubConfig{
|
||||
ClientId: template.ClientID,
|
||||
Scopes: template.Scopes,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func githubEnterpriseConfigToPb(idpConfig *idp_pb.IDPConfig, template *query.GitHubEnterpriseIDPTemplate) {
|
||||
idpConfig.Config = &idp_pb.IDPConfig_GithubEs{
|
||||
GithubEs: &idp_pb.GitHubEnterpriseServerConfig{
|
||||
ClientId: template.ClientID,
|
||||
AuthorizationEndpoint: template.AuthorizationEndpoint,
|
||||
TokenEndpoint: template.TokenEndpoint,
|
||||
UserEndpoint: template.UserEndpoint,
|
||||
Scopes: template.Scopes,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func gitlabConfigToPb(idpConfig *idp_pb.IDPConfig, template *query.GitLabIDPTemplate) {
|
||||
idpConfig.Config = &idp_pb.IDPConfig_Gitlab{
|
||||
Gitlab: &idp_pb.GitLabConfig{
|
||||
ClientId: template.ClientID,
|
||||
Scopes: template.Scopes,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func gitlabSelfHostedConfigToPb(idpConfig *idp_pb.IDPConfig, template *query.GitLabSelfHostedIDPTemplate) {
|
||||
idpConfig.Config = &idp_pb.IDPConfig_GitlabSelfHosted{
|
||||
GitlabSelfHosted: &idp_pb.GitLabSelfHostedConfig{
|
||||
ClientId: template.ClientID,
|
||||
Issuer: template.Issuer,
|
||||
Scopes: template.Scopes,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func googleConfigToPb(idpConfig *idp_pb.IDPConfig, template *query.GoogleIDPTemplate) {
|
||||
idpConfig.Config = &idp_pb.IDPConfig_Google{
|
||||
Google: &idp_pb.GoogleConfig{
|
||||
ClientId: template.ClientID,
|
||||
Scopes: template.Scopes,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func ldapConfigToPb(idpConfig *idp_pb.IDPConfig, template *query.LDAPIDPTemplate) {
|
||||
var timeout *durationpb.Duration
|
||||
if template.Timeout != 0 {
|
||||
timeout = durationpb.New(template.Timeout)
|
||||
}
|
||||
idpConfig.Config = &idp_pb.IDPConfig_Ldap{
|
||||
Ldap: &idp_pb.LDAPConfig{
|
||||
Servers: template.Servers,
|
||||
StartTls: template.StartTLS,
|
||||
BaseDn: template.BaseDN,
|
||||
BindDn: template.BindDN,
|
||||
UserBase: template.UserBase,
|
||||
UserObjectClasses: template.UserObjectClasses,
|
||||
UserFilters: template.UserFilters,
|
||||
Timeout: timeout,
|
||||
Attributes: ldapAttributesToPb(template.LDAPAttributes),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func ldapAttributesToPb(attributes idp_rp.LDAPAttributes) *idp_pb.LDAPAttributes {
|
||||
return &idp_pb.LDAPAttributes{
|
||||
IdAttribute: attributes.IDAttribute,
|
||||
FirstNameAttribute: attributes.FirstNameAttribute,
|
||||
LastNameAttribute: attributes.LastNameAttribute,
|
||||
DisplayNameAttribute: attributes.DisplayNameAttribute,
|
||||
NickNameAttribute: attributes.NickNameAttribute,
|
||||
PreferredUsernameAttribute: attributes.PreferredUsernameAttribute,
|
||||
EmailAttribute: attributes.EmailAttribute,
|
||||
EmailVerifiedAttribute: attributes.EmailVerifiedAttribute,
|
||||
PhoneAttribute: attributes.PhoneAttribute,
|
||||
PhoneVerifiedAttribute: attributes.PhoneVerifiedAttribute,
|
||||
PreferredLanguageAttribute: attributes.PreferredLanguageAttribute,
|
||||
AvatarUrlAttribute: attributes.AvatarURLAttribute,
|
||||
ProfileAttribute: attributes.ProfileAttribute,
|
||||
}
|
||||
}
|
||||
|
||||
func appleConfigToPb(idpConfig *idp_pb.IDPConfig, template *query.AppleIDPTemplate) {
|
||||
idpConfig.Config = &idp_pb.IDPConfig_Apple{
|
||||
Apple: &idp_pb.AppleConfig{
|
||||
ClientId: template.ClientID,
|
||||
TeamId: template.TeamID,
|
||||
KeyId: template.KeyID,
|
||||
Scopes: template.Scopes,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func samlConfigToPb(idpConfig *idp_pb.IDPConfig, template *query.SAMLIDPTemplate) {
|
||||
nameIDFormat := idp_pb.SAMLNameIDFormat_SAML_NAME_ID_FORMAT_PERSISTENT
|
||||
if template.NameIDFormat.Valid {
|
||||
nameIDFormat = nameIDToPb(template.NameIDFormat.V)
|
||||
}
|
||||
idpConfig.Config = &idp_pb.IDPConfig_Saml{
|
||||
Saml: &idp_pb.SAMLConfig{
|
||||
MetadataXml: template.Metadata,
|
||||
Binding: bindingToPb(template.Binding),
|
||||
WithSignedRequest: template.WithSignedRequest,
|
||||
NameIdFormat: nameIDFormat,
|
||||
TransientMappingAttributeName: gu.Ptr(template.TransientMappingAttributeName),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func bindingToPb(binding string) idp_pb.SAMLBinding {
|
||||
switch binding {
|
||||
case "":
|
||||
return idp_pb.SAMLBinding_SAML_BINDING_UNSPECIFIED
|
||||
case saml.HTTPPostBinding:
|
||||
return idp_pb.SAMLBinding_SAML_BINDING_POST
|
||||
case saml.HTTPRedirectBinding:
|
||||
return idp_pb.SAMLBinding_SAML_BINDING_REDIRECT
|
||||
case saml.HTTPArtifactBinding:
|
||||
return idp_pb.SAMLBinding_SAML_BINDING_ARTIFACT
|
||||
default:
|
||||
return idp_pb.SAMLBinding_SAML_BINDING_UNSPECIFIED
|
||||
}
|
||||
}
|
||||
|
||||
func nameIDToPb(format domain.SAMLNameIDFormat) idp_pb.SAMLNameIDFormat {
|
||||
switch format {
|
||||
case domain.SAMLNameIDFormatUnspecified:
|
||||
return idp_pb.SAMLNameIDFormat_SAML_NAME_ID_FORMAT_UNSPECIFIED
|
||||
case domain.SAMLNameIDFormatEmailAddress:
|
||||
return idp_pb.SAMLNameIDFormat_SAML_NAME_ID_FORMAT_EMAIL_ADDRESS
|
||||
case domain.SAMLNameIDFormatPersistent:
|
||||
return idp_pb.SAMLNameIDFormat_SAML_NAME_ID_FORMAT_PERSISTENT
|
||||
case domain.SAMLNameIDFormatTransient:
|
||||
return idp_pb.SAMLNameIDFormat_SAML_NAME_ID_FORMAT_TRANSIENT
|
||||
default:
|
||||
return idp_pb.SAMLNameIDFormat_SAML_NAME_ID_FORMAT_UNSPECIFIED
|
||||
}
|
||||
}
|
235
internal/api/grpc/idp/v2/query_integration_test.go
Normal file
235
internal/api/grpc/idp/v2/query_integration_test.go
Normal file
@@ -0,0 +1,235 @@
|
||||
//go:build integration
|
||||
|
||||
package idp_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/idp/v2"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/object/v2"
|
||||
)
|
||||
|
||||
type idpAttr struct {
|
||||
ID string
|
||||
Name string
|
||||
Details *object.Details
|
||||
}
|
||||
|
||||
func TestServer_GetIDPByID(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *idp.GetIDPByIDRequest
|
||||
dep func(ctx context.Context, request *idp.GetIDPByIDRequest) *idpAttr
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *idp.GetIDPByIDResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "idp by ID, no id provided",
|
||||
args: args{
|
||||
IamCTX,
|
||||
&idp.GetIDPByIDRequest{
|
||||
Id: "",
|
||||
},
|
||||
func(ctx context.Context, request *idp.GetIDPByIDRequest) *idpAttr {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "idp by ID, not found",
|
||||
args: args{
|
||||
IamCTX,
|
||||
&idp.GetIDPByIDRequest{
|
||||
Id: "unknown",
|
||||
},
|
||||
func(ctx context.Context, request *idp.GetIDPByIDRequest) *idpAttr {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "idp by ID, instance, ok",
|
||||
args: args{
|
||||
IamCTX,
|
||||
&idp.GetIDPByIDRequest{},
|
||||
func(ctx context.Context, request *idp.GetIDPByIDRequest) *idpAttr {
|
||||
name := fmt.Sprintf("GetIDPByID%d", time.Now().UnixNano())
|
||||
resp := Tester.AddGenericOAuthIDP(ctx, name)
|
||||
request.Id = resp.Id
|
||||
return &idpAttr{
|
||||
resp.GetId(),
|
||||
name,
|
||||
&object.Details{
|
||||
Sequence: resp.Details.Sequence,
|
||||
ChangeDate: resp.Details.ChangeDate,
|
||||
ResourceOwner: resp.Details.ResourceOwner,
|
||||
}}
|
||||
},
|
||||
},
|
||||
want: &idp.GetIDPByIDResponse{
|
||||
Idp: &idp.IDP{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
},
|
||||
State: idp.IDPState_IDP_STATE_ACTIVE,
|
||||
Type: idp.IDPType_IDP_TYPE_OAUTH,
|
||||
Config: &idp.IDPConfig{
|
||||
Config: &idp.IDPConfig_Oauth{
|
||||
Oauth: &idp.OAuthConfig{
|
||||
ClientId: "clientID",
|
||||
AuthorizationEndpoint: "https://example.com/oauth/v2/authorize",
|
||||
TokenEndpoint: "https://example.com/oauth/v2/token",
|
||||
UserEndpoint: "https://api.example.com/user",
|
||||
Scopes: []string{"openid", "profile", "email"},
|
||||
IdAttribute: "id",
|
||||
},
|
||||
},
|
||||
Options: &idp.Options{
|
||||
IsLinkingAllowed: true,
|
||||
IsCreationAllowed: true,
|
||||
IsAutoCreation: true,
|
||||
IsAutoUpdate: true,
|
||||
AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "idp by ID, instance, no permission",
|
||||
args: args{
|
||||
UserCTX,
|
||||
&idp.GetIDPByIDRequest{},
|
||||
func(ctx context.Context, request *idp.GetIDPByIDRequest) *idpAttr {
|
||||
name := fmt.Sprintf("GetIDPByID%d", time.Now().UnixNano())
|
||||
resp := Tester.AddGenericOAuthIDP(IamCTX, name)
|
||||
request.Id = resp.Id
|
||||
return &idpAttr{
|
||||
resp.GetId(),
|
||||
name,
|
||||
&object.Details{
|
||||
Sequence: resp.Details.Sequence,
|
||||
ChangeDate: resp.Details.ChangeDate,
|
||||
ResourceOwner: resp.Details.ResourceOwner,
|
||||
}}
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "idp by ID, org, ok",
|
||||
args: args{
|
||||
CTX,
|
||||
&idp.GetIDPByIDRequest{},
|
||||
func(ctx context.Context, request *idp.GetIDPByIDRequest) *idpAttr {
|
||||
name := fmt.Sprintf("GetIDPByID%d", time.Now().UnixNano())
|
||||
resp := Tester.AddOrgGenericOAuthIDP(ctx, name)
|
||||
request.Id = resp.Id
|
||||
return &idpAttr{
|
||||
resp.GetId(),
|
||||
name,
|
||||
&object.Details{
|
||||
Sequence: resp.Details.Sequence,
|
||||
ChangeDate: resp.Details.ChangeDate,
|
||||
ResourceOwner: resp.Details.ResourceOwner,
|
||||
}}
|
||||
},
|
||||
},
|
||||
want: &idp.GetIDPByIDResponse{
|
||||
Idp: &idp.IDP{
|
||||
Details: &object.Details{
|
||||
ChangeDate: timestamppb.Now(),
|
||||
},
|
||||
State: idp.IDPState_IDP_STATE_ACTIVE,
|
||||
Type: idp.IDPType_IDP_TYPE_OAUTH,
|
||||
Config: &idp.IDPConfig{
|
||||
Config: &idp.IDPConfig_Oauth{
|
||||
Oauth: &idp.OAuthConfig{
|
||||
ClientId: "clientID",
|
||||
AuthorizationEndpoint: "https://example.com/oauth/v2/authorize",
|
||||
TokenEndpoint: "https://example.com/oauth/v2/token",
|
||||
UserEndpoint: "https://api.example.com/user",
|
||||
Scopes: []string{"openid", "profile", "email"},
|
||||
IdAttribute: "id",
|
||||
},
|
||||
},
|
||||
Options: &idp.Options{
|
||||
IsLinkingAllowed: true,
|
||||
IsCreationAllowed: true,
|
||||
IsAutoCreation: true,
|
||||
IsAutoUpdate: true,
|
||||
AutoLinking: idp.AutoLinkingOption_AUTO_LINKING_OPTION_USERNAME,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "idp by ID, org, no permission",
|
||||
args: args{
|
||||
UserCTX,
|
||||
&idp.GetIDPByIDRequest{},
|
||||
func(ctx context.Context, request *idp.GetIDPByIDRequest) *idpAttr {
|
||||
name := fmt.Sprintf("GetIDPByID%d", time.Now().UnixNano())
|
||||
resp := Tester.AddOrgGenericOAuthIDP(CTX, name)
|
||||
request.Id = resp.Id
|
||||
return &idpAttr{
|
||||
resp.GetId(),
|
||||
name,
|
||||
&object.Details{
|
||||
Sequence: resp.Details.Sequence,
|
||||
ChangeDate: resp.Details.ChangeDate,
|
||||
ResourceOwner: resp.Details.ResourceOwner,
|
||||
}}
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
idpAttr := tt.args.dep(tt.args.ctx, tt.args.req)
|
||||
retryDuration := time.Minute
|
||||
if ctxDeadline, ok := CTX.Deadline(); ok {
|
||||
retryDuration = time.Until(ctxDeadline)
|
||||
}
|
||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||
got, getErr := Client.GetIDPByID(tt.args.ctx, tt.args.req)
|
||||
assertErr := assert.NoError
|
||||
if tt.wantErr {
|
||||
assertErr = assert.Error
|
||||
}
|
||||
assertErr(ttt, getErr)
|
||||
if getErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// set provided info from creation
|
||||
tt.want.Idp.Details = idpAttr.Details
|
||||
tt.want.Idp.Name = idpAttr.Name
|
||||
tt.want.Idp.Id = idpAttr.ID
|
||||
|
||||
// first check for details, mgmt and admin api don't fill the details correctly
|
||||
integration.AssertDetails(t, tt.want.Idp, got.Idp)
|
||||
// then set details
|
||||
tt.want.Idp.Details = got.Idp.Details
|
||||
// to check the rest of the content
|
||||
assert.Equal(ttt, tt.want.Idp, got.Idp)
|
||||
}, retryDuration, time.Second)
|
||||
})
|
||||
}
|
||||
}
|
56
internal/api/grpc/idp/v2/server.go
Normal file
56
internal/api/grpc/idp/v2/server.go
Normal file
@@ -0,0 +1,56 @@
|
||||
package idp
|
||||
|
||||
import (
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/server"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/idp/v2"
|
||||
)
|
||||
|
||||
var _ idp.IdentityProviderServiceServer = (*Server)(nil)
|
||||
|
||||
type Server struct {
|
||||
idp.UnimplementedIdentityProviderServiceServer
|
||||
command *command.Commands
|
||||
query *query.Queries
|
||||
|
||||
checkPermission domain.PermissionCheck
|
||||
}
|
||||
|
||||
type Config struct{}
|
||||
|
||||
func CreateServer(
|
||||
command *command.Commands,
|
||||
query *query.Queries,
|
||||
checkPermission domain.PermissionCheck,
|
||||
) *Server {
|
||||
return &Server{
|
||||
command: command,
|
||||
query: query,
|
||||
checkPermission: checkPermission,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) RegisterServer(grpcServer *grpc.Server) {
|
||||
idp.RegisterIdentityProviderServiceServer(grpcServer, s)
|
||||
}
|
||||
|
||||
func (s *Server) AppName() string {
|
||||
return idp.IdentityProviderService_ServiceDesc.ServiceName
|
||||
}
|
||||
|
||||
func (s *Server) MethodPrefix() string {
|
||||
return idp.IdentityProviderService_ServiceDesc.ServiceName
|
||||
}
|
||||
|
||||
func (s *Server) AuthMethods() authz.MethodMapping {
|
||||
return idp.IdentityProviderService_AuthMethods
|
||||
}
|
||||
|
||||
func (s *Server) RegisterGateway() server.RegisterGatewayFunc {
|
||||
return idp.RegisterIdentityProviderServiceHandler
|
||||
}
|
40
internal/api/grpc/idp/v2/server_integration_test.go
Normal file
40
internal/api/grpc/idp/v2/server_integration_test.go
Normal file
@@ -0,0 +1,40 @@
|
||||
//go:build integration
|
||||
|
||||
package idp_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
idp_pb "github.com/zitadel/zitadel/pkg/grpc/idp/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
CTX context.Context
|
||||
IamCTX context.Context
|
||||
UserCTX context.Context
|
||||
SystemCTX context.Context
|
||||
ErrCTX context.Context
|
||||
Tester *integration.Tester
|
||||
Client idp_pb.IdentityProviderServiceClient
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(func() int {
|
||||
ctx, errCtx, cancel := integration.Contexts(time.Hour)
|
||||
defer cancel()
|
||||
|
||||
Tester = integration.NewTester(ctx)
|
||||
defer Tester.Done()
|
||||
|
||||
UserCTX = Tester.WithAuthorization(ctx, integration.Login)
|
||||
IamCTX = Tester.WithAuthorization(ctx, integration.IAMOwner)
|
||||
SystemCTX = Tester.WithAuthorization(ctx, integration.SystemUser)
|
||||
CTX, ErrCTX = Tester.WithAuthorization(ctx, integration.OrgOwner), errCtx
|
||||
Client = Tester.Client.IDPv2
|
||||
return m.Run()
|
||||
}())
|
||||
}
|
@@ -149,7 +149,7 @@ func (s *Server) GetProviderByID(ctx context.Context, req *mgmt_pb.GetProviderBy
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
idp, err := s.query.IDPTemplateByID(ctx, true, req.Id, false, orgIDQuery)
|
||||
idp, err := s.query.IDPTemplateByID(ctx, true, req.Id, false, nil, orgIDQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@ func (l *Login) getOrgDomainPolicy(r *http.Request, orgID string) (*query.Domain
|
||||
}
|
||||
|
||||
func (l *Login) getIDPByID(r *http.Request, id string) (*query.IDPTemplate, error) {
|
||||
return l.query.IDPTemplateByID(r.Context(), false, id, false)
|
||||
return l.query.IDPTemplateByID(r.Context(), false, id, false, nil)
|
||||
}
|
||||
|
||||
func (l *Login) getLoginPolicy(r *http.Request, orgID string) (*query.LoginPolicy, error) {
|
||||
|
Reference in New Issue
Block a user