zitadel/internal/api/grpc/idp/v2/query.go
Stefan Benz 3e3d46ac0d
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>
2024-08-14 18:18:29 +00:00

370 lines
12 KiB
Go

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
}
}