fix(app): move queries to query package (#2612)

* fix: move queries to query package

* fix(auth): switch project role requests to query pkg

* refactor: delete unused project role code

* remove repo

* implement sql queries

* fix(database): oidc config change type to int2

* fix(queries): implement app queries

* refactor: simplify code

* fix: correct app query

* Update app.go

* fix token check

* fix mock

* test: app prepares

* test: oidc compliance

* test: OIDCOriginAllowList

* fix: converter

* resolve unsupported oidc version

Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
Silvan 2021-11-26 07:57:05 +01:00 committed by GitHub
parent a9035def0f
commit 3473156c7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 3150 additions and 1066 deletions

View File

@ -47,7 +47,7 @@ type API struct {
type health interface {
Health(ctx context.Context) error
IamByID(ctx context.Context) (*iam_model.IAM, error)
VerifierClientID(ctx context.Context, appName string) (string, error)
VerifierClientID(ctx context.Context, appName string) (string, string, error)
}
type auth interface {
@ -159,7 +159,7 @@ func handleValidate(checks []ValidationFunction) func(w http.ResponseWriter, r *
}
func (a *API) handleClientID(w http.ResponseWriter, r *http.Request) {
id, err := a.health.VerifierClientID(r.Context(), "Zitadel Console")
id, _, err := a.health.VerifierClientID(r.Context(), "Zitadel Console")
if err != nil {
http_util.MarshalJSON(w, nil, err, http.StatusPreconditionFailed)
return

View File

@ -15,7 +15,7 @@ type testVerifier struct {
memberships []*Membership
}
func (v *testVerifier) VerifyAccessToken(ctx context.Context, token, clientID string) (string, string, string, string, string, error) {
func (v *testVerifier) VerifyAccessToken(ctx context.Context, token, clientID, projectID string) (string, string, string, string, string, error) {
return "userID", "agentID", "clientID", "de", "orgID", nil
}
func (v *testVerifier) SearchMyMemberships(ctx context.Context) ([]*Membership, error) {
@ -30,8 +30,8 @@ func (v *testVerifier) ExistsOrg(ctx context.Context, orgID string) error {
return nil
}
func (v *testVerifier) VerifierClientID(ctx context.Context, appName string) (string, error) {
return "clientID", nil
func (v *testVerifier) VerifierClientID(ctx context.Context, appName string) (string, string, error) {
return "clientID", "projectID", nil
}
func (v *testVerifier) CheckOrgFeatures(context.Context, string, ...string) error {

View File

@ -20,8 +20,8 @@ type TokenVerifier struct {
}
type authZRepo interface {
VerifyAccessToken(ctx context.Context, token, verifierClientID string) (userID, agentID, clientID, prefLang, resourceOwner string, err error)
VerifierClientID(ctx context.Context, name string) (clientID string, err error)
VerifyAccessToken(ctx context.Context, token, verifierClientID, projectID string) (userID, agentID, clientID, prefLang, resourceOwner string, err error)
VerifierClientID(ctx context.Context, name string) (clientID, projectID string, err error)
SearchMyMemberships(ctx context.Context) ([]*Membership, error)
ProjectIDAndOriginsByClientID(ctx context.Context, clientID string) (projectID string, origins []string, err error)
ExistsOrg(ctx context.Context, orgID string) error
@ -33,17 +33,18 @@ func Start(authZRepo authZRepo) (v *TokenVerifier) {
}
func (v *TokenVerifier) VerifyAccessToken(ctx context.Context, token string, method string) (userID, clientID, agentID, prefLang, resourceOwner string, err error) {
verifierClientID, err := v.clientIDFromMethod(ctx, method)
verifierClientID, projectID, err := v.clientIDAndProjectIDFromMethod(ctx, method)
if err != nil {
return "", "", "", "", "", err
}
userID, agentID, clientID, prefLang, resourceOwner, err = v.authZRepo.VerifyAccessToken(ctx, token, verifierClientID)
userID, agentID, clientID, prefLang, resourceOwner, err = v.authZRepo.VerifyAccessToken(ctx, token, verifierClientID, projectID)
return userID, clientID, agentID, prefLang, resourceOwner, err
}
type client struct {
id string
name string
id string
projectID string
name string
}
func (v *TokenVerifier) RegisterServer(appName, methodPrefix string, mappings MethodMapping) {
@ -64,28 +65,28 @@ func prefixFromMethod(method string) (string, bool) {
return parts[1], true
}
func (v *TokenVerifier) clientIDFromMethod(ctx context.Context, method string) (_ string, err error) {
func (v *TokenVerifier) clientIDAndProjectIDFromMethod(ctx context.Context, method string) (clientID, projectID string, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
prefix, ok := prefixFromMethod(method)
if !ok {
return "", caos_errs.ThrowPermissionDenied(nil, "AUTHZ-GRD2Q", "Errors.Internal")
return "", "", caos_errs.ThrowPermissionDenied(nil, "AUTHZ-GRD2Q", "Errors.Internal")
}
app, ok := v.clients.Load(prefix)
if !ok {
return "", caos_errs.ThrowPermissionDenied(nil, "AUTHZ-G2qrh", "Errors.Internal")
return "", "", caos_errs.ThrowPermissionDenied(nil, "AUTHZ-G2qrh", "Errors.Internal")
}
c := app.(*client)
if c.id != "" {
return c.id, nil
return c.id, c.projectID, nil
}
c.id, err = v.authZRepo.VerifierClientID(ctx, c.name)
c.id, c.projectID, err = v.authZRepo.VerifierClientID(ctx, c.name)
if err != nil {
return "", caos_errs.ThrowPermissionDenied(err, "AUTHZ-ptTIF2", "Errors.Internal")
return "", "", caos_errs.ThrowPermissionDenied(err, "AUTHZ-ptTIF2", "Errors.Internal")
}
v.clients.Store(prefix, c)
return c.id, nil
return c.id, c.projectID, nil
}
func (v *TokenVerifier) SearchMyMemberships(ctx context.Context) (_ []*Membership, err error) {
ctx, span := tracing.NewSpan(ctx)

View File

@ -12,7 +12,7 @@ import (
)
func (s *Server) GetAppByID(ctx context.Context, req *mgmt_pb.GetAppByIDRequest) (*mgmt_pb.GetAppByIDResponse, error) {
app, err := s.project.ApplicationByID(ctx, req.ProjectId, req.AppId)
app, err := s.query.AppByProjectAndAppID(ctx, req.ProjectId, req.AppId)
if err != nil {
return nil, err
}
@ -26,16 +26,16 @@ func (s *Server) ListApps(ctx context.Context, req *mgmt_pb.ListAppsRequest) (*m
if err != nil {
return nil, err
}
domains, err := s.project.SearchApplications(ctx, queries)
apps, err := s.query.SearchApps(ctx, queries)
if err != nil {
return nil, err
}
return &mgmt_pb.ListAppsResponse{
Result: project_grpc.AppsToPb(domains.Result),
Result: project_grpc.AppsToPb(apps.Apps),
Details: object_grpc.ToListDetails(
domains.TotalResult,
domains.Sequence,
domains.Timestamp,
apps.Count,
apps.Sequence,
apps.Timestamp,
),
}, nil
}
@ -196,16 +196,16 @@ func (s *Server) ListAppKeys(ctx context.Context, req *mgmt_pb.ListAppKeysReques
if err != nil {
return nil, err
}
domains, err := s.project.SearchClientKeys(ctx, queries)
keys, err := s.project.SearchClientKeys(ctx, queries)
if err != nil {
return nil, err
}
return &mgmt_pb.ListAppKeysResponse{
Result: authn_grpc.KeyViewsToPb(domains.Result),
Result: authn_grpc.KeyViewsToPb(keys.Result),
Details: object_grpc.ToListDetails(
domains.TotalResult,
domains.Sequence,
domains.Timestamp,
keys.TotalResult,
keys.Sequence,
keys.Timestamp,
),
}, nil
}

View File

@ -9,25 +9,27 @@ import (
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/eventstore/v1/models"
key_model "github.com/caos/zitadel/internal/key/model"
proj_model "github.com/caos/zitadel/internal/project/model"
"github.com/caos/zitadel/internal/query"
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management"
)
func ListAppsRequestToModel(req *mgmt_pb.ListAppsRequest) (*proj_model.ApplicationSearchRequest, error) {
func ListAppsRequestToModel(req *mgmt_pb.ListAppsRequest) (*query.AppSearchQueries, error) {
offset, limit, asc := object.ListQueryToModel(req.Query)
queries, err := app_grpc.AppQueriesToModel(req.Queries)
if err != nil {
return nil, err
}
queries = append(queries, &proj_model.ApplicationSearchQuery{
Key: proj_model.AppSearchKeyProjectID,
Method: domain.SearchMethodEquals,
Value: req.ProjectId,
})
return &proj_model.ApplicationSearchRequest{
Offset: offset,
Limit: limit,
Asc: asc,
projectQuery, err := query.NewAppProjectIDSearchQuery(req.ProjectId)
if err != nil {
return nil, err
}
queries = append(queries, projectQuery)
return &query.AppSearchQueries{
SearchRequest: query.SearchRequest{
Offset: offset,
Limit: limit,
Asc: asc,
},
//SortingColumn: //TODO: sorting
Queries: queries,
}, nil

View File

@ -7,11 +7,12 @@ import (
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
proj_model "github.com/caos/zitadel/internal/project/model"
"github.com/caos/zitadel/internal/query"
app_pb "github.com/caos/zitadel/pkg/grpc/app"
message_pb "github.com/caos/zitadel/pkg/grpc/message"
)
func AppsToPb(apps []*proj_model.ApplicationView) []*app_pb.App {
func AppsToPb(apps []*query.App) []*app_pb.App {
a := make([]*app_pb.App, len(apps))
for i, app := range apps {
a[i] = AppToPb(app)
@ -19,7 +20,7 @@ func AppsToPb(apps []*proj_model.ApplicationView) []*app_pb.App {
return a
}
func AppToPb(app *proj_model.ApplicationView) *app_pb.App {
func AppToPb(app *query.App) *app_pb.App {
return &app_pb.App{
Id: app.ID,
Details: object_grpc.ToViewDetailsPb(app.Sequence, app.CreationDate, app.ChangeDate, app.ResourceOwner),
@ -29,68 +30,68 @@ func AppToPb(app *proj_model.ApplicationView) *app_pb.App {
}
}
func AppConfigToPb(app *proj_model.ApplicationView) app_pb.AppConfig {
if app.IsOIDC {
return AppOIDCConfigToPb(app)
func AppConfigToPb(app *query.App) app_pb.AppConfig {
if app.OIDCConfig != nil {
return AppOIDCConfigToPb(app.OIDCConfig)
}
return AppAPIConfigToPb(app)
return AppAPIConfigToPb(app.APIConfig)
}
func AppOIDCConfigToPb(app *proj_model.ApplicationView) *app_pb.App_OidcConfig {
func AppOIDCConfigToPb(app *query.OIDCApp) *app_pb.App_OidcConfig {
return &app_pb.App_OidcConfig{
OidcConfig: &app_pb.OIDCConfig{
RedirectUris: app.OIDCRedirectUris,
ResponseTypes: OIDCResponseTypesFromModel(app.OIDCResponseTypes),
GrantTypes: OIDCGrantTypesFromModel(app.OIDCGrantTypes),
AppType: OIDCApplicationTypeToPb(domain.OIDCApplicationType(app.OIDCApplicationType)),
ClientId: app.OIDCClientID,
AuthMethodType: OIDCAuthMethodTypeToPb(domain.OIDCAuthMethodType(app.OIDCAuthMethodType)),
PostLogoutRedirectUris: app.OIDCPostLogoutRedirectUris,
Version: OIDCVersionToPb(domain.OIDCVersion(app.OIDCVersion)),
NoneCompliant: app.NoneCompliant,
RedirectUris: app.RedirectURIs,
ResponseTypes: OIDCResponseTypesFromModel(app.ResponseTypes),
GrantTypes: OIDCGrantTypesFromModel(app.GrantTypes),
AppType: OIDCApplicationTypeToPb(app.AppType),
ClientId: app.ClientID,
AuthMethodType: OIDCAuthMethodTypeToPb(app.AuthMethodType),
PostLogoutRedirectUris: app.PostLogoutRedirectURIs,
Version: OIDCVersionToPb(domain.OIDCVersion(app.Version)),
NoneCompliant: len(app.ComplianceProblems) != 0,
ComplianceProblems: ComplianceProblemsToLocalizedMessages(app.ComplianceProblems),
DevMode: app.DevMode,
AccessTokenType: oidcTokenTypeToPb(domain.OIDCTokenType(app.AccessTokenType)),
AccessTokenRoleAssertion: app.AccessTokenRoleAssertion,
IdTokenRoleAssertion: app.IDTokenRoleAssertion,
IdTokenUserinfoAssertion: app.IDTokenUserinfoAssertion,
DevMode: app.IsDevMode,
AccessTokenType: oidcTokenTypeToPb(app.AccessTokenType),
AccessTokenRoleAssertion: app.AssertAccessTokenRole,
IdTokenRoleAssertion: app.AssertIDTokenRole,
IdTokenUserinfoAssertion: app.AssertIDTokenUserinfo,
ClockSkew: durationpb.New(app.ClockSkew),
AdditionalOrigins: app.AdditionalOrigins,
AllowedOrigins: app.OriginAllowList,
AllowedOrigins: app.AllowedOrigins,
},
}
}
func AppAPIConfigToPb(app *proj_model.ApplicationView) app_pb.AppConfig {
func AppAPIConfigToPb(app *query.APIApp) app_pb.AppConfig {
return &app_pb.App_ApiConfig{
ApiConfig: &app_pb.APIConfig{
ClientId: app.OIDCClientID,
ClientId: app.ClientID,
ClientSecret: "", //TODO: remove from proto
AuthMethodType: APIAuthMethodeTypeToPb(domain.APIAuthMethodType(app.OIDCAuthMethodType)),
AuthMethodType: APIAuthMethodeTypeToPb(app.AuthMethodType),
},
}
}
func AppStateToPb(state proj_model.AppState) app_pb.AppState {
func AppStateToPb(state domain.AppState) app_pb.AppState {
switch state {
case proj_model.AppStateActive:
case domain.AppStateActive:
return app_pb.AppState_APP_STATE_ACTIVE
case proj_model.AppStateInactive:
case domain.AppStateInactive:
return app_pb.AppState_APP_STATE_INACTIVE
default:
return app_pb.AppState_APP_STATE_UNSPECIFIED
}
}
func OIDCResponseTypesFromModel(responseTypes []proj_model.OIDCResponseType) []app_pb.OIDCResponseType {
func OIDCResponseTypesFromModel(responseTypes []domain.OIDCResponseType) []app_pb.OIDCResponseType {
oidcResponseTypes := make([]app_pb.OIDCResponseType, len(responseTypes))
for i, responseType := range responseTypes {
switch responseType {
case proj_model.OIDCResponseTypeCode:
case domain.OIDCResponseTypeCode:
oidcResponseTypes[i] = app_pb.OIDCResponseType_OIDC_RESPONSE_TYPE_CODE
case proj_model.OIDCResponseTypeIDToken:
case domain.OIDCResponseTypeIDToken:
oidcResponseTypes[i] = app_pb.OIDCResponseType_OIDC_RESPONSE_TYPE_ID_TOKEN
case proj_model.OIDCResponseTypeIDTokenToken:
case domain.OIDCResponseTypeIDTokenToken:
oidcResponseTypes[i] = app_pb.OIDCResponseType_OIDC_RESPONSE_TYPE_ID_TOKEN_TOKEN
}
}
@ -98,7 +99,7 @@ func OIDCResponseTypesFromModel(responseTypes []proj_model.OIDCResponseType) []a
}
func OIDCResponseTypesToDomain(responseTypes []app_pb.OIDCResponseType) []domain.OIDCResponseType {
if responseTypes == nil || len(responseTypes) == 0 {
if len(responseTypes) == 0 {
return []domain.OIDCResponseType{domain.OIDCResponseTypeCode}
}
oidcResponseTypes := make([]domain.OIDCResponseType, len(responseTypes))
@ -115,15 +116,15 @@ func OIDCResponseTypesToDomain(responseTypes []app_pb.OIDCResponseType) []domain
return oidcResponseTypes
}
func OIDCGrantTypesFromModel(grantTypes []proj_model.OIDCGrantType) []app_pb.OIDCGrantType {
func OIDCGrantTypesFromModel(grantTypes []domain.OIDCGrantType) []app_pb.OIDCGrantType {
oidcGrantTypes := make([]app_pb.OIDCGrantType, len(grantTypes))
for i, grantType := range grantTypes {
switch grantType {
case proj_model.OIDCGrantTypeAuthorizationCode:
case domain.OIDCGrantTypeAuthorizationCode:
oidcGrantTypes[i] = app_pb.OIDCGrantType_OIDC_GRANT_TYPE_AUTHORIZATION_CODE
case proj_model.OIDCGrantTypeImplicit:
case domain.OIDCGrantTypeImplicit:
oidcGrantTypes[i] = app_pb.OIDCGrantType_OIDC_GRANT_TYPE_IMPLICIT
case proj_model.OIDCGrantTypeRefreshToken:
case domain.OIDCGrantTypeRefreshToken:
oidcGrantTypes[i] = app_pb.OIDCGrantType_OIDC_GRANT_TYPE_REFRESH_TOKEN
}
}
@ -272,8 +273,8 @@ func APIAuthMethodTypeToDomain(authType app_pb.APIAuthMethodType) domain.APIAuth
}
}
func AppQueriesToModel(queries []*app_pb.AppQuery) (_ []*proj_model.ApplicationSearchQuery, err error) {
q := make([]*proj_model.ApplicationSearchQuery, len(queries))
func AppQueriesToModel(queries []*app_pb.AppQuery) (q []query.SearchQuery, err error) {
q = make([]query.SearchQuery, len(queries))
for i, query := range queries {
q[i], err = AppQueryToModel(query)
if err != nil {
@ -283,10 +284,10 @@ func AppQueriesToModel(queries []*app_pb.AppQuery) (_ []*proj_model.ApplicationS
return q, nil
}
func AppQueryToModel(query *app_pb.AppQuery) (*proj_model.ApplicationSearchQuery, error) {
switch q := query.Query.(type) {
func AppQueryToModel(appQuery *app_pb.AppQuery) (query.SearchQuery, error) {
switch q := appQuery.Query.(type) {
case *app_pb.AppQuery_NameQuery:
return AppQueryNameToModel(q.NameQuery), nil
return query.NewAppNameSearchQuery(object_grpc.TextMethodToQuery(q.NameQuery.Method), q.NameQuery.Name)
default:
return nil, errors.ThrowInvalidArgument(nil, "APP-Add46", "List.Query.Invalid")
}

View File

@ -21,7 +21,7 @@ var (
type verifierMock struct{}
func (v *verifierMock) VerifyAccessToken(ctx context.Context, token, clientID string) (string, string, string, string, string, error) {
func (v *verifierMock) VerifyAccessToken(ctx context.Context, token, clientID, projectID string) (string, string, string, string, string, error) {
return "", "", "", "", "", nil
}
func (v *verifierMock) SearchMyMemberships(ctx context.Context) ([]*authz.Membership, error) {
@ -34,8 +34,8 @@ func (v *verifierMock) ProjectIDAndOriginsByClientID(ctx context.Context, client
func (v *verifierMock) ExistsOrg(ctx context.Context, orgID string) error {
return nil
}
func (v *verifierMock) VerifierClientID(ctx context.Context, appName string) (string, error) {
return "", nil
func (v *verifierMock) VerifierClientID(ctx context.Context, appName string) (string, string, error) {
return "", "", nil
}
func (v *verifierMock) CheckOrgFeatures(context.Context, string, ...string) error {
return nil

View File

@ -13,7 +13,6 @@ import (
"github.com/caos/zitadel/internal/api/http/middleware"
"github.com/caos/zitadel/internal/errors"
proj_model "github.com/caos/zitadel/internal/project/model"
"github.com/caos/zitadel/internal/query"
"github.com/caos/zitadel/internal/telemetry/tracing"
grant_model "github.com/caos/zitadel/internal/usergrant/model"
@ -26,15 +25,20 @@ func (o *OPStorage) CreateAuthRequest(ctx context.Context, req *oidc.AuthRequest
if !ok {
return nil, errors.ThrowPreconditionFailed(nil, "OIDC-sd436", "no user agent id")
}
app, err := o.repo.ApplicationByClientID(ctx, req.ClientID)
projectID, err := o.query.ProjectIDFromOIDCClientID(ctx, req.ClientID)
if err != nil {
return nil, errors.ThrowPreconditionFailed(nil, "OIDC-AEG4d", "Errors.Internal")
}
req.Scopes, err = o.assertProjectRoleScopes(app, req.Scopes)
project, err := o.query.ProjectByID(ctx, projectID)
if err != nil {
return nil, errors.ThrowPreconditionFailed(nil, "OIDC-w4wIn", "Errors.Internal")
}
req.Scopes, err = o.assertProjectRoleScopes(project, req.Scopes)
if err != nil {
return nil, errors.ThrowPreconditionFailed(nil, "OIDC-Gqrfg", "Errors.Internal")
}
authRequest := CreateAuthRequestToBusiness(ctx, req, userAgentID, userID)
//TODO: ensure splitting of command and query side durring auth request and login refactoring
resp, err := o.repo.CreateAuthRequest(ctx, authRequest)
if err != nil {
return nil, err
@ -205,8 +209,8 @@ func (o *OPStorage) GetKeySet(ctx context.Context) (_ *jose.JSONWebKeySet, err e
return o.repo.GetKeySet(ctx)
}
func (o *OPStorage) assertProjectRoleScopes(app *proj_model.ApplicationView, scopes []string) ([]string, error) {
if !app.ProjectRoleAssertion {
func (o *OPStorage) assertProjectRoleScopes(project *query.Project, scopes []string) ([]string, error) {
if !project.ProjectRoleAssertion {
return scopes, nil
}
for _, scope := range scopes {
@ -214,7 +218,7 @@ func (o *OPStorage) assertProjectRoleScopes(app *proj_model.ApplicationView, sco
return scopes, nil
}
}
projectIDQuery, err := query.NewProjectRoleProjectIDSearchQuery(app.ProjectID)
projectIDQuery, err := query.NewProjectRoleProjectIDSearchQuery(project.ID)
if err != nil {
return nil, errors.ThrowInternal(err, "OIDC-Cyc78", "Errors.Internal")
}

View File

@ -12,11 +12,10 @@ import (
"github.com/caos/zitadel/internal/api/authz"
"github.com/caos/zitadel/internal/api/http"
"github.com/caos/zitadel/internal/auth_request/model"
authreq_model "github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
proj_model "github.com/caos/zitadel/internal/project/model"
"github.com/caos/zitadel/internal/query"
"github.com/caos/zitadel/internal/telemetry/tracing"
user_model "github.com/caos/zitadel/internal/user/model"
@ -37,11 +36,11 @@ const (
func (o *OPStorage) GetClientByClientID(ctx context.Context, id string) (_ op.Client, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
client, err := o.repo.ApplicationByClientID(ctx, id)
client, err := o.query.AppByOIDCClientID(ctx, id)
if err != nil {
return nil, err
}
if client.State != proj_model.AppStateActive {
if client.State != domain.AppStateActive {
return nil, errors.ThrowPreconditionFailed(nil, "OIDC-sdaGg", "client is not active")
}
projectIDQuery, err := query.NewProjectRoleProjectIDSearchQuery(client.ProjectID)
@ -125,11 +124,11 @@ func (o *OPStorage) SetUserinfoFromToken(ctx context.Context, userInfo oidc.User
return errors.ThrowPermissionDenied(nil, "OIDC-Dsfb2", "token is not valid or has expired")
}
if token.ApplicationID != "" {
app, err := o.repo.ApplicationByClientID(ctx, token.ApplicationID)
app, err := o.query.AppByOIDCClientID(ctx, token.ApplicationID)
if err != nil {
return err
}
if origin != "" && !http.IsOriginAllowed(app.OriginAllowList, origin) {
if origin != "" && !http.IsOriginAllowed(app.OIDCConfig.AllowedOrigins, origin) {
return errors.ThrowPermissionDenied(nil, "OIDC-da1f3", "origin is not allowed")
}
}
@ -202,8 +201,8 @@ func (o *OPStorage) SetUserinfoFromScopes(ctx context.Context, userInfo oidc.Use
if strings.HasPrefix(scope, ScopeProjectRolePrefix) {
roles = append(roles, strings.TrimPrefix(scope, ScopeProjectRolePrefix))
}
if strings.HasPrefix(scope, model.OrgDomainPrimaryScope) {
userInfo.AppendClaims(model.OrgDomainPrimaryClaim, strings.TrimPrefix(scope, model.OrgDomainPrimaryScope))
if strings.HasPrefix(scope, authreq_model.OrgDomainPrimaryScope) {
userInfo.AppendClaims(authreq_model.OrgDomainPrimaryClaim, strings.TrimPrefix(scope, authreq_model.OrgDomainPrimaryScope))
}
}
}
@ -225,12 +224,12 @@ func (o *OPStorage) SetIntrospectionFromToken(ctx context.Context, introspection
if err != nil {
return errors.ThrowPermissionDenied(nil, "OIDC-Dsfb2", "token is not valid or has expired")
}
app, err := o.repo.ApplicationByClientID(ctx, clientID)
projectID, err := o.query.ProjectIDFromOIDCClientID(ctx, clientID)
if err != nil {
return errors.ThrowPermissionDenied(nil, "OIDC-Adfg5", "client not found")
}
for _, aud := range token.Audience {
if aud == clientID || aud == app.ProjectID {
if aud == clientID || aud == projectID {
err := o.SetUserinfoFromScopes(ctx, introspection, token.UserID, clientID, token.Scopes)
if err != nil {
return err
@ -266,8 +265,8 @@ func (o *OPStorage) GetPrivateClaimsFromScopes(ctx context.Context, userID, clie
}
if strings.HasPrefix(scope, ScopeProjectRolePrefix) {
roles = append(roles, strings.TrimPrefix(scope, ScopeProjectRolePrefix))
} else if strings.HasPrefix(scope, model.OrgDomainPrimaryScope) {
claims = appendClaim(claims, model.OrgDomainPrimaryClaim, strings.TrimPrefix(scope, model.OrgDomainPrimaryScope))
} else if strings.HasPrefix(scope, authreq_model.OrgDomainPrimaryScope) {
claims = appendClaim(claims, authreq_model.OrgDomainPrimaryClaim, strings.TrimPrefix(scope, authreq_model.OrgDomainPrimaryScope))
}
}
if len(roles) == 0 || clientID == "" {
@ -284,11 +283,11 @@ func (o *OPStorage) GetPrivateClaimsFromScopes(ctx context.Context, userID, clie
}
func (o *OPStorage) assertRoles(ctx context.Context, userID, applicationID string, requestedRoles []string) (map[string]map[string]string, error) {
app, err := o.repo.ApplicationByClientID(ctx, applicationID)
projectID, err := o.query.ProjectIDFromOIDCClientID(ctx, applicationID)
if err != nil {
return nil, err
}
grants, err := o.repo.UserGrantsByProjectAndUserID(app.ProjectID, userID)
grants, err := o.repo.UserGrantsByProjectAndUserID(projectID, userID)
if err != nil {
return nil, err
}

View File

@ -8,24 +8,25 @@ import (
"github.com/caos/oidc/pkg/op"
authreq_model "github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/project/model"
"github.com/caos/zitadel/internal/query"
)
type Client struct {
*model.ApplicationView
app *query.App
defaultLoginURL string
defaultAccessTokenLifetime time.Duration
defaultIdTokenLifetime time.Duration
allowedScopes []string
}
func ClientFromBusiness(app *model.ApplicationView, defaultLoginURL string, defaultAccessTokenLifetime, defaultIdTokenLifetime time.Duration, allowedScopes []string) (op.Client, error) {
if !app.IsOIDC {
func ClientFromBusiness(app *query.App, defaultLoginURL string, defaultAccessTokenLifetime, defaultIdTokenLifetime time.Duration, allowedScopes []string) (op.Client, error) {
if app.OIDCConfig == nil {
return nil, errors.ThrowInvalidArgument(nil, "OIDC-d5bhD", "client is not a proper oidc application")
}
return &Client{
ApplicationView: app,
app: app,
defaultLoginURL: defaultLoginURL,
defaultAccessTokenLifetime: defaultAccessTokenLifetime,
defaultIdTokenLifetime: defaultIdTokenLifetime,
@ -34,15 +35,15 @@ func ClientFromBusiness(app *model.ApplicationView, defaultLoginURL string, defa
}
func (c *Client) ApplicationType() op.ApplicationType {
return op.ApplicationType(c.OIDCApplicationType)
return op.ApplicationType(c.app.OIDCConfig.AppType)
}
func (c *Client) AuthMethod() oidc.AuthMethod {
return authMethodToOIDC(c.OIDCAuthMethodType)
return authMethodToOIDC(c.app.OIDCConfig.AuthMethodType)
}
func (c *Client) GetID() string {
return c.OIDCClientID
return c.app.OIDCConfig.ClientID
}
func (c *Client) LoginURL(id string) string {
@ -50,28 +51,28 @@ func (c *Client) LoginURL(id string) string {
}
func (c *Client) RedirectURIs() []string {
return c.OIDCRedirectUris
return c.app.OIDCConfig.RedirectURIs
}
func (c *Client) PostLogoutRedirectURIs() []string {
return c.OIDCPostLogoutRedirectUris
return c.app.OIDCConfig.PostLogoutRedirectURIs
}
func (c *Client) ResponseTypes() []oidc.ResponseType {
return responseTypesToOIDC(c.OIDCResponseTypes)
return responseTypesToOIDC(c.app.OIDCConfig.ResponseTypes)
}
func (c *Client) GrantTypes() []oidc.GrantType {
return grantTypesToOIDC(c.OIDCGrantTypes)
return grantTypesToOIDC(c.app.OIDCConfig.GrantTypes)
}
func (c *Client) DevMode() bool {
return c.ApplicationView.DevMode
return c.app.OIDCConfig.IsDevMode
}
func (c *Client) RestrictAdditionalIdTokenScopes() func(scopes []string) []string {
return func(scopes []string) []string {
if c.IDTokenRoleAssertion {
if c.app.OIDCConfig.AssertIDTokenRole {
return scopes
}
return removeScopeWithPrefix(scopes, ScopeProjectRolePrefix)
@ -80,7 +81,7 @@ func (c *Client) RestrictAdditionalIdTokenScopes() func(scopes []string) []strin
func (c *Client) RestrictAdditionalAccessTokenScopes() func(scopes []string) []string {
return func(scopes []string) []string {
if c.AccessTokenRoleAssertion {
if c.app.OIDCConfig.AssertAccessTokenRole {
return scopes
}
return removeScopeWithPrefix(scopes, ScopeProjectRolePrefix)
@ -96,7 +97,7 @@ func (c *Client) IDTokenLifetime() time.Duration {
}
func (c *Client) AccessTokenType() op.AccessTokenType {
return accessTokenTypeToOIDC(c.ApplicationView.AccessTokenType)
return accessTokenTypeToOIDC(c.app.OIDCConfig.AccessTokenType)
}
func (c *Client) IsScopeAllowed(scope string) bool {
@ -124,40 +125,40 @@ func (c *Client) IsScopeAllowed(scope string) bool {
}
func (c *Client) ClockSkew() time.Duration {
return c.ApplicationView.ClockSkew
return c.app.OIDCConfig.ClockSkew
}
func (c *Client) IDTokenUserinfoClaimsAssertion() bool {
return c.ApplicationView.IDTokenUserinfoAssertion
return c.app.OIDCConfig.AssertIDTokenUserinfo
}
func accessTokenTypeToOIDC(tokenType model.OIDCTokenType) op.AccessTokenType {
func accessTokenTypeToOIDC(tokenType domain.OIDCTokenType) op.AccessTokenType {
switch tokenType {
case model.OIDCTokenTypeBearer:
case domain.OIDCTokenTypeBearer:
return op.AccessTokenTypeBearer
case model.OIDCTokenTypeJWT:
case domain.OIDCTokenTypeJWT:
return op.AccessTokenTypeJWT
default:
return op.AccessTokenTypeBearer
}
}
func authMethodToOIDC(authType model.OIDCAuthMethodType) oidc.AuthMethod {
func authMethodToOIDC(authType domain.OIDCAuthMethodType) oidc.AuthMethod {
switch authType {
case model.OIDCAuthMethodTypeBasic:
case domain.OIDCAuthMethodTypeBasic:
return oidc.AuthMethodBasic
case model.OIDCAuthMethodTypePost:
case domain.OIDCAuthMethodTypePost:
return oidc.AuthMethodPost
case model.OIDCAuthMethodTypeNone:
case domain.OIDCAuthMethodTypeNone:
return oidc.AuthMethodNone
case model.OIDCAuthMethodTypePrivateKeyJWT:
case domain.OIDCAuthMethodTypePrivateKeyJWT:
return oidc.AuthMethodPrivateKeyJWT
default:
return oidc.AuthMethodBasic
}
}
func responseTypesToOIDC(responseTypes []model.OIDCResponseType) []oidc.ResponseType {
func responseTypesToOIDC(responseTypes []domain.OIDCResponseType) []oidc.ResponseType {
oidcTypes := make([]oidc.ResponseType, len(responseTypes))
for i, t := range responseTypes {
oidcTypes[i] = responseTypeToOIDC(t)
@ -165,20 +166,20 @@ func responseTypesToOIDC(responseTypes []model.OIDCResponseType) []oidc.Response
return oidcTypes
}
func responseTypeToOIDC(responseType model.OIDCResponseType) oidc.ResponseType {
func responseTypeToOIDC(responseType domain.OIDCResponseType) oidc.ResponseType {
switch responseType {
case model.OIDCResponseTypeCode:
case domain.OIDCResponseTypeCode:
return oidc.ResponseTypeCode
case model.OIDCResponseTypeIDTokenToken:
case domain.OIDCResponseTypeIDTokenToken:
return oidc.ResponseTypeIDToken
case model.OIDCResponseTypeIDToken:
case domain.OIDCResponseTypeIDToken:
return oidc.ResponseTypeIDTokenOnly
default:
return oidc.ResponseTypeCode
}
}
func grantTypesToOIDC(grantTypes []model.OIDCGrantType) []oidc.GrantType {
func grantTypesToOIDC(grantTypes []domain.OIDCGrantType) []oidc.GrantType {
oidcTypes := make([]oidc.GrantType, len(grantTypes))
for i, t := range grantTypes {
oidcTypes[i] = grantTypeToOIDC(t)
@ -186,13 +187,13 @@ func grantTypesToOIDC(grantTypes []model.OIDCGrantType) []oidc.GrantType {
return oidcTypes
}
func grantTypeToOIDC(grantType model.OIDCGrantType) oidc.GrantType {
func grantTypeToOIDC(grantType domain.OIDCGrantType) oidc.GrantType {
switch grantType {
case model.OIDCGrantTypeAuthorizationCode:
case domain.OIDCGrantTypeAuthorizationCode:
return oidc.GrantTypeCode
case model.OIDCGrantTypeImplicit:
case domain.OIDCGrantTypeImplicit:
return oidc.GrantTypeImplicit
case model.OIDCGrantTypeRefreshToken:
case domain.OIDCGrantTypeRefreshToken:
return oidc.GrantTypeRefreshToken
default:
return oidc.GrantTypeCode

View File

@ -2,11 +2,8 @@ package repository
import (
"context"
"github.com/caos/zitadel/internal/project/model"
)
type ApplicationRepository interface {
ApplicationByClientID(ctx context.Context, clientID string) (*model.ApplicationView, error)
AuthorizeClientIDSecret(ctx context.Context, clientID, secret string) error
}

View File

@ -3,35 +3,25 @@ package eventstore
import (
"context"
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
"github.com/caos/zitadel/internal/command"
"github.com/caos/zitadel/internal/project/model"
proj_view_model "github.com/caos/zitadel/internal/project/repository/view/model"
"github.com/caos/zitadel/internal/query"
"github.com/caos/zitadel/internal/telemetry/tracing"
)
type ApplicationRepo struct {
Commands *command.Commands
View *view.View
}
func (a *ApplicationRepo) ApplicationByClientID(ctx context.Context, clientID string) (*model.ApplicationView, error) {
app, err := a.View.ApplicationByClientID(ctx, clientID)
if err != nil {
return nil, err
}
return proj_view_model.ApplicationViewToModel(app), nil
Query *query.Queries
}
func (a *ApplicationRepo) AuthorizeClientIDSecret(ctx context.Context, clientID, secret string) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
app, err := a.View.ApplicationByClientID(ctx, clientID)
app, err := a.Query.AppByOIDCClientID(ctx, clientID)
if err != nil {
return err
}
if app.IsOIDC {
if app.OIDCConfig != nil {
return a.Commands.VerifyOIDCClientSecret(ctx, app.ProjectID, app.ID, secret)
}
return a.Commands.VerifyAPIClientSecret(ctx, app.ProjectID, app.ID, secret)

View File

@ -29,6 +29,7 @@ import (
type AuthRequestRepo struct {
Command *command.Commands
Query *query.Queries
AuthRequests cache.AuthRequestCache
View *view.View
Eventstore v1.Eventstore
@ -101,12 +102,12 @@ type orgViewProvider interface {
}
type userGrantProvider interface {
ApplicationByClientID(context.Context, string) (*project_view_model.ApplicationView, error)
ProjectByOIDCClientID(context.Context, string) (*query.Project, error)
UserGrantsByProjectAndUserID(string, string) ([]*grant_view_model.UserGrantView, error)
}
type projectProvider interface {
ApplicationByClientID(context.Context, string) (*project_view_model.ApplicationView, error)
ProjectByOIDCClientID(context.Context, string) (*query.Project, error)
OrgProjectMappingByIDs(orgID, projectID string) (*project_view_model.OrgProjectMapping, error)
}
@ -122,18 +123,22 @@ func (repo *AuthRequestRepo) CreateAuthRequest(ctx context.Context, request *dom
return nil, err
}
request.ID = reqID
app, err := repo.View.ApplicationByClientID(ctx, request.ApplicationID)
project, err := repo.ProjectProvider.ProjectByOIDCClientID(ctx, request.ApplicationID)
if err != nil {
return nil, err
}
appIDs, err := repo.View.AppIDsFromProjectID(ctx, app.ProjectID)
projectIDQuery, err := query.NewAppProjectIDSearchQuery(project.ID)
if err != nil {
return nil, err
}
appIDs, err := repo.Query.SearchAppIDs(ctx, &query.AppSearchQueries{Queries: []query.SearchQuery{projectIDQuery}})
if err != nil {
return nil, err
}
request.Audience = appIDs
request.AppendAudIfNotExisting(app.ProjectID)
request.ApplicationResourceOwner = app.ResourceOwner
request.PrivateLabelingSetting = app.PrivateLabelingSetting
request.AppendAudIfNotExisting(project.ID)
request.ApplicationResourceOwner = project.ResourceOwner
request.PrivateLabelingSetting = project.PrivateLabelingSetting
if err := setOrgID(repo.OrgViewProvider, request); err != nil {
return nil, err
}
@ -1175,20 +1180,20 @@ func linkingIDPConfigExistingInAllowedIDPs(linkingUsers []*domain.ExternalUser,
}
func userGrantRequired(ctx context.Context, request *domain.AuthRequest, user *user_model.UserView, userGrantProvider userGrantProvider) (_ bool, err error) {
var app *project_view_model.ApplicationView
var project *query.Project
switch request.Request.Type() {
case domain.AuthRequestTypeOIDC:
app, err = userGrantProvider.ApplicationByClientID(ctx, request.ApplicationID)
project, err = userGrantProvider.ProjectByOIDCClientID(ctx, request.ApplicationID)
if err != nil {
return false, err
}
default:
return false, errors.ThrowPreconditionFailed(nil, "EVENT-dfrw2", "Errors.AuthRequest.RequestTypeNotSupported")
}
if !app.ProjectRoleCheck {
if !project.ProjectRoleCheck {
return false, nil
}
grants, err := userGrantProvider.UserGrantsByProjectAndUserID(app.ProjectID, user.ID)
grants, err := userGrantProvider.UserGrantsByProjectAndUserID(project.ID, user.ID)
if err != nil {
return false, err
}
@ -1196,20 +1201,20 @@ func userGrantRequired(ctx context.Context, request *domain.AuthRequest, user *u
}
func projectRequired(ctx context.Context, request *domain.AuthRequest, projectProvider projectProvider) (_ bool, err error) {
var app *project_view_model.ApplicationView
var project *query.Project
switch request.Request.Type() {
case domain.AuthRequestTypeOIDC:
app, err = projectProvider.ApplicationByClientID(ctx, request.ApplicationID)
project, err = projectProvider.ProjectByOIDCClientID(ctx, request.ApplicationID)
if err != nil {
return false, err
}
default:
return false, errors.ThrowPreconditionFailed(nil, "EVENT-dfrw2", "Errors.AuthRequest.RequestTypeNotSupported")
}
if !app.HasProjectCheck {
if !project.HasProjectCheck {
return false, nil
}
_, err = projectProvider.OrgProjectMappingByIDs(request.UserOrgID, app.ProjectID)
_, err = projectProvider.OrgProjectMappingByIDs(request.UserOrgID, project.ID)
if errors.IsNotFound(err) {
return true, nil
}

View File

@ -212,8 +212,8 @@ type mockUserGrants struct {
userGrants int
}
func (m *mockUserGrants) ApplicationByClientID(ctx context.Context, s string) (*proj_view_model.ApplicationView, error) {
return &proj_view_model.ApplicationView{ProjectRoleCheck: m.roleCheck}, nil
func (m *mockUserGrants) ProjectByOIDCClientID(ctx context.Context, s string) (*query.Project, error) {
return &query.Project{ProjectRoleCheck: m.roleCheck}, nil
}
func (m *mockUserGrants) UserGrantsByProjectAndUserID(s string, s2 string) ([]*grant_view_model.UserGrantView, error) {
@ -229,8 +229,8 @@ type mockProject struct {
projectCheck bool
}
func (m *mockProject) ApplicationByClientID(ctx context.Context, s string) (*proj_view_model.ApplicationView, error) {
return &proj_view_model.ApplicationView{HasProjectCheck: m.projectCheck}, nil
func (m *mockProject) ProjectByOIDCClientID(ctx context.Context, s string) (*query.Project, error) {
return &query.Project{HasProjectCheck: m.projectCheck}, nil
}
func (m *mockProject) OrgProjectMappingByIDs(orgID, projectID string) (*proj_view_model.OrgProjectMapping, error) {

View File

@ -1,168 +0,0 @@
package handler
import (
"context"
"github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/eventstore/v1/query"
es_sdk "github.com/caos/zitadel/internal/eventstore/v1/sdk"
"github.com/caos/zitadel/internal/eventstore/v1/spooler"
proj_model "github.com/caos/zitadel/internal/project/model"
es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
proj_view "github.com/caos/zitadel/internal/project/repository/view"
view_model "github.com/caos/zitadel/internal/project/repository/view/model"
)
const (
applicationTable = "auth.applications"
)
type Application struct {
handler
subscription *v1.Subscription
}
func newApplication(handler handler) *Application {
h := &Application{
handler: handler,
}
h.subscribe()
return h
}
func (a *Application) subscribe() {
a.subscription = a.es.Subscribe(a.AggregateTypes()...)
go func() {
for event := range a.subscription.Events {
query.ReduceEvent(a, event)
}
}()
}
func (a *Application) ViewModel() string {
return applicationTable
}
func (a *Application) Subscription() *v1.Subscription {
return a.subscription
}
func (_ *Application) AggregateTypes() []models.AggregateType {
return []models.AggregateType{es_model.ProjectAggregate}
}
func (a *Application) CurrentSequence() (uint64, error) {
sequence, err := a.view.GetLatestApplicationSequence()
if err != nil {
return 0, err
}
return sequence.CurrentSequence, nil
}
func (a *Application) EventQuery() (*models.SearchQuery, error) {
sequence, err := a.view.GetLatestApplicationSequence()
if err != nil {
return nil, err
}
return proj_view.ProjectQuery(sequence.CurrentSequence), nil
}
func (a *Application) Reduce(event *models.Event) (err error) {
app := new(view_model.ApplicationView)
switch event.Type {
case es_model.ApplicationAdded:
project, err := a.getProjectByID(context.Background(), event.AggregateID)
if err != nil {
return err
}
app.ProjectRoleCheck = project.ProjectRoleCheck
app.HasProjectCheck = project.HasProjectCheck
app.ProjectRoleAssertion = project.ProjectRoleAssertion
app.PrivateLabelingSetting = project.PrivateLabelingSetting
err = app.AppendEvent(event)
case es_model.ApplicationChanged,
es_model.OIDCConfigAdded,
es_model.OIDCConfigChanged,
es_model.APIConfigAdded,
es_model.APIConfigChanged,
es_model.ApplicationDeactivated,
es_model.ApplicationReactivated:
err = app.SetData(event)
if err != nil {
return err
}
app, err = a.view.ApplicationByID(event.AggregateID, app.ID)
if err != nil {
return err
}
err = app.AppendEvent(event)
case es_model.ApplicationRemoved:
err = app.SetData(event)
if err != nil {
return err
}
return a.view.DeleteApplication(app.ID, event)
case es_model.ProjectChanged:
apps, err := a.view.ApplicationsByProjectID(event.AggregateID)
if err != nil {
return err
}
if len(apps) == 0 {
return a.view.ProcessedApplicationSequence(event)
}
for _, app := range apps {
if err := app.AppendEvent(event); err != nil {
return err
}
}
return a.view.PutApplications(apps, event)
case es_model.ProjectRemoved:
err = a.view.DeleteApplicationsByProjectID(event.AggregateID)
if err == nil {
return a.view.ProcessedApplicationSequence(event)
}
default:
return a.view.ProcessedApplicationSequence(event)
}
if err != nil {
return err
}
return a.view.PutApplication(app, event)
}
func (a *Application) OnError(event *models.Event, spoolerError error) error {
logging.LogWithFields("SPOOL-ls9ew", "id", event.AggregateID).WithError(spoolerError).Warn("something went wrong in project app handler")
return spooler.HandleError(event, spoolerError, a.view.GetLatestApplicationFailedEvent, a.view.ProcessedApplicationFailedEvent, a.view.ProcessedApplicationSequence, a.errorCountUntilSkip)
}
func (a *Application) OnSuccess() error {
return spooler.HandleSuccess(a.view.UpdateApplicationSpoolerRunTimestamp)
}
func (a *Application) getProjectByID(ctx context.Context, projID string) (*proj_model.Project, error) {
query, err := proj_view.ProjectByIDQuery(projID, 0)
if err != nil {
return nil, err
}
esProject := &es_model.Project{
ObjectRoot: models.ObjectRoot{
AggregateID: projID,
},
}
err = es_sdk.Filter(ctx, a.Eventstore().FilterEvents, esProject.AppendEvents, query)
if err != nil && !errors.IsNotFound(err) {
return nil, err
}
if esProject.Sequence == 0 {
return nil, errors.ThrowNotFound(nil, "EVENT-DBf32", "Errors.Project.NotFound")
}
return es_model.ProjectToModel(esProject), nil
}

View File

@ -44,7 +44,6 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
newKey(
handler{view, bulkLimit, configs.cycleDuration("Key"), errorCount, es},
keyChan),
newApplication(handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount, es}),
newUserGrant(
handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount, es},
systemDefaults.IamID),

View File

@ -68,7 +68,7 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, co
assetsAPI := conf.APIDomain + "/assets/v1/"
view, err := auth_view.StartView(sqlClient, keyAlgorithm, idGenerator, assetsAPI)
view, err := auth_view.StartView(sqlClient, keyAlgorithm, queries, idGenerator, assetsAPI)
if err != nil {
return nil, err
}
@ -92,6 +92,14 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, co
SystemDefaults: systemDefaults,
PrefixAvatarURL: assetsAPI,
}
//TODO: remove as soon as possible
queryView := struct {
*query.Queries
*auth_view.View
}{
queries,
view,
}
return &EsRepository{
spool,
es,
@ -111,8 +119,9 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, co
IDPProviderViewProvider: view,
LockoutPolicyViewProvider: queries,
LoginPolicyViewProvider: queries,
UserGrantProvider: view,
ProjectProvider: view,
Query: queries,
UserGrantProvider: queryView,
ProjectProvider: queryView,
IdGenerator: idGenerator,
PasswordCheckLifeTime: systemDefaults.VerificationLifetimes.PasswordCheck.Duration,
ExternalLoginCheckLifeTime: systemDefaults.VerificationLifetimes.PasswordCheck.Duration,
@ -143,7 +152,7 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, co
},
eventstore.ApplicationRepo{
Commands: command,
View: view,
Query: queries,
},
eventstore.UserSessionRepo{

View File

@ -1,133 +0,0 @@
package view
import (
"context"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models"
proj_model "github.com/caos/zitadel/internal/project/model"
"github.com/caos/zitadel/internal/project/repository/view"
"github.com/caos/zitadel/internal/project/repository/view/model"
"github.com/caos/zitadel/internal/view/repository"
)
const (
applicationTable = "auth.applications"
)
func (v *View) ApplicationByID(projectID, appID string) (*model.ApplicationView, error) {
return view.ApplicationByID(v.Db, applicationTable, projectID, appID)
}
func (v *View) ApplicationsByProjectID(projectID string) ([]*model.ApplicationView, error) {
return view.ApplicationsByProjectID(v.Db, applicationTable, projectID)
}
func (v *View) SearchApplications(request *proj_model.ApplicationSearchRequest) ([]*model.ApplicationView, uint64, error) {
return view.SearchApplications(v.Db, applicationTable, request)
}
func (v *View) PutApplication(app *model.ApplicationView, event *models.Event) error {
err := view.PutApplication(v.Db, applicationTable, app)
if err != nil {
return err
}
return v.ProcessedApplicationSequence(event)
}
func (v *View) PutApplications(apps []*model.ApplicationView, event *models.Event) error {
err := view.PutApplications(v.Db, applicationTable, apps...)
if err != nil {
return err
}
return v.ProcessedApplicationSequence(event)
}
func (v *View) DeleteApplication(appID string, event *models.Event) error {
err := view.DeleteApplication(v.Db, applicationTable, appID)
if err != nil && !errors.IsNotFound(err) {
return err
}
return v.ProcessedApplicationSequence(event)
}
func (v *View) DeleteApplicationsByProjectID(projectID string) error {
return view.DeleteApplicationsByProjectID(v.Db, applicationTable, projectID)
}
func (v *View) GetLatestApplicationSequence() (*repository.CurrentSequence, error) {
return v.latestSequence(applicationTable)
}
func (v *View) ProcessedApplicationSequence(event *models.Event) error {
return v.saveCurrentSequence(applicationTable, event)
}
func (v *View) UpdateApplicationSpoolerRunTimestamp() error {
return v.updateSpoolerRunSequence(applicationTable)
}
func (v *View) GetLatestApplicationFailedEvent(sequence uint64) (*repository.FailedEvent, error) {
return v.latestFailedEvent(applicationTable, sequence)
}
func (v *View) ProcessedApplicationFailedEvent(failedEvent *repository.FailedEvent) error {
return v.saveFailedEvent(failedEvent)
}
func (v *View) ApplicationByClientID(_ context.Context, clientID string) (*model.ApplicationView, error) {
return view.ApplicationByOIDCClientID(v.Db, applicationTable, clientID)
}
func (v *View) AppIDsFromProjectByClientID(ctx context.Context, clientID string) ([]string, error) {
app, err := v.ApplicationByClientID(ctx, clientID)
if err != nil {
return nil, err
}
req := &proj_model.ApplicationSearchRequest{
Queries: []*proj_model.ApplicationSearchQuery{
{
Key: proj_model.AppSearchKeyProjectID,
Method: domain.SearchMethodEquals,
Value: app.ProjectID,
},
},
}
apps, _, err := view.SearchApplications(v.Db, applicationTable, req)
if err != nil {
return nil, errors.ThrowPreconditionFailed(err, "VIEW-Gd24q", "cannot find applications")
}
ids := make([]string, 0, len(apps))
for _, app := range apps {
if !app.IsOIDC {
continue
}
ids = append(ids, app.OIDCClientID)
}
return ids, nil
}
func (v *View) AppIDsFromProjectID(ctx context.Context, projectID string) ([]string, error) {
req := &proj_model.ApplicationSearchRequest{
Queries: []*proj_model.ApplicationSearchQuery{
{
Key: proj_model.AppSearchKeyProjectID,
Method: domain.SearchMethodEquals,
Value: projectID,
},
},
}
apps, _, err := view.SearchApplications(v.Db, applicationTable, req)
if err != nil {
return nil, errors.ThrowPreconditionFailed(err, "VIEW-Gd24q", "cannot find applications")
}
ids := make([]string, 0, len(apps))
for _, app := range apps {
if !app.IsOIDC {
continue
}
ids = append(ids, app.OIDCClientID)
}
return ids, nil
}

View File

@ -7,6 +7,7 @@ import (
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/id"
"github.com/caos/zitadel/internal/query"
)
type View struct {
@ -14,9 +15,10 @@ type View struct {
keyAlgorithm crypto.EncryptionAlgorithm
idGenerator id.Generator
prefixAvatarURL string
query *query.Queries
}
func StartView(sqlClient *sql.DB, keyAlgorithm crypto.EncryptionAlgorithm, idGenerator id.Generator, prefixAvatarURL string) (*View, error) {
func StartView(sqlClient *sql.DB, keyAlgorithm crypto.EncryptionAlgorithm, queries *query.Queries, idGenerator id.Generator, prefixAvatarURL string) (*View, error) {
gorm, err := gorm.Open("postgres", sqlClient)
if err != nil {
return nil, err
@ -26,6 +28,7 @@ func StartView(sqlClient *sql.DB, keyAlgorithm crypto.EncryptionAlgorithm, idGen
keyAlgorithm: keyAlgorithm,
idGenerator: idGenerator,
prefixAvatarURL: prefixAvatarURL,
query: queries,
}, nil
}

View File

@ -66,7 +66,7 @@ func (repo *TokenVerifierRepo) TokenByID(ctx context.Context, tokenID, userID st
return model.TokenViewToModel(token), nil
}
func (repo *TokenVerifierRepo) VerifyAccessToken(ctx context.Context, tokenString, verifierClientID string) (userID string, agentID string, clientID, prefLang, resourceOwner string, err error) {
func (repo *TokenVerifierRepo) VerifyAccessToken(ctx context.Context, tokenString, verifierClientID, projectID string) (userID string, agentID string, clientID, prefLang, resourceOwner string, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
tokenData, err := base64.RawURLEncoding.DecodeString(tokenString)
@ -90,10 +90,6 @@ func (repo *TokenVerifierRepo) VerifyAccessToken(ctx context.Context, tokenStrin
return "", "", "", "", "", caos_errs.ThrowUnauthenticated(err, "APP-k9KS0", "invalid token")
}
projectID, _, err := repo.ProjectIDAndOriginsByClientID(ctx, verifierClientID)
if err != nil {
return "", "", "", "", "", caos_errs.ThrowUnauthenticated(err, "APP-5M9so", "invalid token")
}
for _, aud := range token.Audience {
if verifierClientID == aud || projectID == aud {
return token.UserID, token.UserAgentID, token.ApplicationID, token.PreferredLanguage, token.ResourceOwner, nil
@ -107,7 +103,7 @@ func (repo *TokenVerifierRepo) ProjectIDAndOriginsByClientID(ctx context.Context
if err != nil {
return "", nil, err
}
return app.ProjectID, app.OriginAllowList, nil
return app.ProjectID, app.OIDCConfig.AllowedOrigins, nil
}
func (repo *TokenVerifierRepo) CheckOrgFeatures(ctx context.Context, orgID string, requiredFeatures ...string) error {
@ -237,19 +233,24 @@ func MissingFeatureErr(feature string) error {
return caos_errs.ThrowPermissionDeniedf(nil, "AUTH-Dvgsf", "missing feature %v", feature)
}
func (repo *TokenVerifierRepo) VerifierClientID(ctx context.Context, appName string) (_ string, err error) {
func (repo *TokenVerifierRepo) VerifierClientID(ctx context.Context, appName string) (clientID, projectID string, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
iam, err := repo.getIAMByID(ctx)
if err != nil {
return "", err
return "", "", err
}
app, err := repo.View.ApplicationByProjecIDAndAppName(ctx, iam.IAMProjectID, appName)
if err != nil {
return "", err
return "", "", err
}
return app.OIDCClientID, nil
if app.OIDCConfig != nil {
clientID = app.OIDCConfig.ClientID
} else if app.APIConfig != nil {
clientID = app.APIConfig.ClientID
}
return clientID, app.ProjectID, nil
}
func (r *TokenVerifierRepo) getUserEvents(ctx context.Context, userID string, sequence uint64) ([]*models.Event, error) {

View File

@ -1,114 +0,0 @@
package handler
import (
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/eventstore/v1/query"
"github.com/caos/zitadel/internal/eventstore/v1/spooler"
es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
"github.com/caos/zitadel/internal/project/repository/view"
view_model "github.com/caos/zitadel/internal/project/repository/view/model"
)
const (
applicationTable = "authz.applications"
)
type Application struct {
handler
subscription *v1.Subscription
}
func newApplication(handler handler) *Application {
h := &Application{
handler: handler,
}
h.subscribe()
return h
}
func (k *Application) subscribe() {
k.subscription = k.es.Subscribe(k.AggregateTypes()...)
go func() {
for event := range k.subscription.Events {
query.ReduceEvent(k, event)
}
}()
}
func (a *Application) ViewModel() string {
return applicationTable
}
func (p *Application) Subscription() *v1.Subscription {
return p.subscription
}
func (a *Application) AggregateTypes() []models.AggregateType {
return []models.AggregateType{es_model.ProjectAggregate}
}
func (a *Application) CurrentSequence() (uint64, error) {
sequence, err := a.view.GetLatestApplicationSequence()
if err != nil {
return 0, err
}
return sequence.CurrentSequence, nil
}
func (a *Application) EventQuery() (*models.SearchQuery, error) {
sequence, err := a.view.GetLatestApplicationSequence()
if err != nil {
return nil, err
}
return view.ProjectQuery(sequence.CurrentSequence), nil
}
func (a *Application) Reduce(event *models.Event) (err error) {
app := new(view_model.ApplicationView)
switch event.Type {
case es_model.ApplicationAdded:
app.AppendEvent(event)
case es_model.ApplicationChanged,
es_model.OIDCConfigAdded,
es_model.OIDCConfigChanged,
es_model.APIConfigAdded,
es_model.APIConfigChanged,
es_model.ApplicationDeactivated,
es_model.ApplicationReactivated:
err := app.SetData(event)
if err != nil {
return err
}
app, err = a.view.ApplicationByID(event.AggregateID, app.ID)
if err != nil {
return err
}
app.AppendEvent(event)
case es_model.ApplicationRemoved:
err := app.SetData(event)
if err != nil {
return err
}
return a.view.DeleteApplication(app.ID, event)
default:
return a.view.ProcessedApplicationSequence(event)
}
if err != nil {
return err
}
return a.view.PutApplication(app, event)
}
func (a *Application) OnError(event *models.Event, spoolerError error) error {
logging.LogWithFields("SPOOL-sjZw", "id", event.AggregateID).WithError(spoolerError).Warn("something went wrong in project app handler")
return spooler.HandleError(event, spoolerError, a.view.GetLatestApplicationFailedEvent, a.view.ProcessedApplicationFailedEvent, a.view.ProcessedApplicationSequence, a.errorCountUntilSkip)
}
func (a *Application) OnSuccess() error {
return spooler.HandleSuccess(a.view.UpdateApplicationSpoolerRunTimestamp)
}

View File

@ -36,8 +36,6 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
systemDefaults.IamID),
newUserMembership(
handler{view, bulkLimit, configs.cycleDuration("UserMemberships"), errorCount, es}),
newApplication(
handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount, es}),
}
}

View File

@ -4,7 +4,7 @@ import (
"context"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/eventstore/v1"
v1 "github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/zitadel/internal/query"
@ -43,7 +43,7 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, qu
}
idGenerator := id.SonyFlakeGenerator
view, err := authz_view.StartView(sqlClient, idGenerator)
view, err := authz_view.StartView(sqlClient, idGenerator, queries)
if err != nil {
return nil, err
}

View File

@ -4,69 +4,45 @@ import (
"context"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models"
proj_model "github.com/caos/zitadel/internal/project/model"
"github.com/caos/zitadel/internal/project/repository/view"
"github.com/caos/zitadel/internal/project/repository/view/model"
"github.com/caos/zitadel/internal/query"
"github.com/caos/zitadel/internal/telemetry/tracing"
"github.com/caos/zitadel/internal/view/repository"
)
const (
applicationTable = "authz.applications"
)
func (v *View) ApplicationByID(projectID, appID string) (*model.ApplicationView, error) {
return view.ApplicationByID(v.Db, applicationTable, projectID, appID)
func (v *View) ApplicationByOIDCClientID(clientID string) (*query.App, error) {
return v.Query.AppByOIDCClientID(context.TODO(), clientID)
}
func (v *View) ApplicationByOIDCClientID(clientID string) (*model.ApplicationView, error) {
return view.ApplicationByOIDCClientID(v.Db, applicationTable, clientID)
}
func (v *View) ApplicationByProjecIDAndAppName(ctx context.Context, projectID, appName string) (_ *model.ApplicationView, err error) {
func (v *View) ApplicationByProjecIDAndAppName(ctx context.Context, projectID, appName string) (_ *query.App, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
return view.ApplicationByProjectIDAndAppName(v.Db, applicationTable, projectID, appName)
}
func (v *View) SearchApplications(request *proj_model.ApplicationSearchRequest) ([]*model.ApplicationView, uint64, error) {
return view.SearchApplications(v.Db, applicationTable, request)
}
func (v *View) PutApplication(project *model.ApplicationView, event *models.Event) error {
err := view.PutApplication(v.Db, applicationTable, project)
nameQuery, err := query.NewAppNameSearchQuery(query.TextEquals, appName)
if err != nil {
return err
return nil, err
}
return v.ProcessedApplicationSequence(event)
}
func (v *View) DeleteApplication(appID string, event *models.Event) error {
err := view.DeleteApplication(v.Db, applicationTable, appID)
if err != nil && !errors.IsNotFound(err) {
return err
projectQuery, err := query.NewAppProjectIDSearchQuery(projectID)
if err != nil {
return nil, err
}
return v.ProcessedApplicationSequence(event)
queries := &query.AppSearchQueries{
Queries: []query.SearchQuery{
nameQuery,
projectQuery,
},
}
apps, err := v.Query.SearchApps(ctx, queries)
if err != nil {
return nil, err
}
if len(apps.Apps) != 1 {
return nil, errors.ThrowNotFound(nil, "VIEW-svLQq", "app not found")
}
return apps.Apps[0], nil
}
func (v *View) GetLatestApplicationSequence() (*repository.CurrentSequence, error) {
return v.latestSequence(applicationTable)
}
func (v *View) ProcessedApplicationSequence(event *models.Event) error {
return v.saveCurrentSequence(applicationTable, event)
}
func (v *View) UpdateApplicationSpoolerRunTimestamp() error {
return v.updateSpoolerRunSequence(applicationTable)
}
func (v *View) GetLatestApplicationFailedEvent(sequence uint64) (*repository.FailedEvent, error) {
return v.latestFailedEvent(applicationTable, sequence)
}
func (v *View) ProcessedApplicationFailedEvent(failedEvent *repository.FailedEvent) error {
return v.saveFailedEvent(failedEvent)
func (v *View) SearchApplications(request *query.AppSearchQueries) (*query.Apps, error) {
return v.Query.SearchApps(context.TODO(), request)
}

View File

@ -2,17 +2,20 @@ package view
import (
"database/sql"
"github.com/caos/zitadel/internal/id"
"github.com/caos/zitadel/internal/query"
"github.com/jinzhu/gorm"
)
type View struct {
Db *gorm.DB
Query *query.Queries
idGenerator id.Generator
}
func StartView(sqlClient *sql.DB, idGenerator id.Generator) (*View, error) {
func StartView(sqlClient *sql.DB, idGenerator id.Generator, queries *query.Queries) (*View, error) {
gorm, err := gorm.Open("postgres", sqlClient)
if err != nil {
return nil, err
@ -20,6 +23,7 @@ func StartView(sqlClient *sql.DB, idGenerator id.Generator) (*View, error) {
return &View{
Db: gorm,
idGenerator: idGenerator,
Query: queries,
}, nil
}

View File

@ -181,16 +181,38 @@ func GetOIDCCompliance(version OIDCVersion, appType OIDCApplicationType, grantTy
case OIDCVersionV1:
return GetOIDCV1Compliance(appType, grantTypes, authMethod, redirectUris)
}
return nil
return &Compliance{
NoneCompliant: true,
Problems: []string{"Application.OIDC.UnsupportedVersion"},
}
}
func GetOIDCV1Compliance(appType OIDCApplicationType, grantTypes []OIDCGrantType, authMethod OIDCAuthMethodType, redirectUris []string) *Compliance {
compliance := &Compliance{NoneCompliant: false}
if redirectUris == nil || len(redirectUris) == 0 {
checkGrantTypesCombination(compliance, grantTypes)
checkRedirectURIs(compliance, grantTypes, appType, redirectUris)
checkApplicaitonType(compliance, appType, authMethod)
if compliance.NoneCompliant {
compliance.Problems = append([]string{"Application.OIDC.V1.NotCompliant"}, compliance.Problems...)
}
return compliance
}
func checkGrantTypesCombination(compliance *Compliance, grantTypes []OIDCGrantType) {
if containsOIDCGrantType(grantTypes, OIDCGrantTypeRefreshToken) && !containsOIDCGrantType(grantTypes, OIDCGrantTypeAuthorizationCode) {
compliance.NoneCompliant = true
compliance.Problems = append(compliance.Problems, "Application.OIDC.V1.GrantType.Refresh.NoAuthCode")
}
}
func checkRedirectURIs(compliance *Compliance, grantTypes []OIDCGrantType, appType OIDCApplicationType, redirectUris []string) {
if len(redirectUris) == 0 {
compliance.NoneCompliant = true
compliance.Problems = append([]string{"Application.OIDC.V1.NoRedirectUris"}, compliance.Problems...)
}
CheckGrantTypes(compliance, grantTypes)
if containsOIDCGrantType(grantTypes, OIDCGrantTypeImplicit) && containsOIDCGrantType(grantTypes, OIDCGrantTypeAuthorizationCode) {
CheckRedirectUrisImplicitAndCode(compliance, appType, redirectUris)
} else {
@ -201,7 +223,9 @@ func GetOIDCV1Compliance(appType OIDCApplicationType, grantTypes []OIDCGrantType
CheckRedirectUrisCode(compliance, appType, redirectUris)
}
}
}
func checkApplicaitonType(compliance *Compliance, appType OIDCApplicationType, authMethod OIDCAuthMethodType) {
switch appType {
case OIDCApplicationTypeNative:
GetOIDCV1NativeApplicationCompliance(compliance, authMethod)
@ -211,14 +235,6 @@ func GetOIDCV1Compliance(appType OIDCApplicationType, grantTypes []OIDCGrantType
if compliance.NoneCompliant {
compliance.Problems = append([]string{"Application.OIDC.V1.NotCompliant"}, compliance.Problems...)
}
return compliance
}
func CheckGrantTypes(compliance *Compliance, grantTypes []OIDCGrantType) {
if containsOIDCGrantType(grantTypes, OIDCGrantTypeRefreshToken) && !containsOIDCGrantType(grantTypes, OIDCGrantTypeAuthorizationCode) {
compliance.NoneCompliant = true
compliance.Problems = append(compliance.Problems, "Application.OIDC.V1.GrantType.Refresh.NoAuthCode")
}
}
func GetOIDCV1NativeApplicationCompliance(compliance *Compliance, authMethod OIDCAuthMethodType) {
@ -345,3 +361,22 @@ func isHTTPLoopbackLocalhost(uri string) bool {
strings.HasPrefix(uri, httpLoopbackV6LongWithoutPort) ||
strings.HasPrefix(uri, httpLoopbackV6LongWithPort)
}
func OIDCOriginAllowList(redirectURIs, additionalOrigins []string) ([]string, error) {
allowList := make([]string, 0)
for _, redirect := range redirectURIs {
origin, err := http_util.GetOriginFromURLString(redirect)
if err != nil {
return nil, err
}
if !http_util.IsOriginAllowed(allowList, origin) {
allowList = append(allowList, origin)
}
}
for _, origin := range additionalOrigins {
if !http_util.IsOriginAllowed(allowList, origin) {
allowList = append(allowList, origin)
}
}
return allowList, nil
}

View File

@ -1,6 +1,8 @@
package domain
import (
"reflect"
"strings"
"testing"
"time"
@ -185,3 +187,489 @@ func TestApplicationValid(t *testing.T) {
})
}
}
func TestGetOIDCV1Compliance(t *testing.T) {
type args struct {
appType OIDCApplicationType
grantTypes []OIDCGrantType
authMethod OIDCAuthMethodType
redirectUris []string
}
tests := []struct {
name string
args args
}{
{
name: "none compliant",
args: args{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := GetOIDCV1Compliance(tt.args.appType, tt.args.grantTypes, tt.args.authMethod, tt.args.redirectUris)
if !got.NoneCompliant {
t.Error("compliance should be none compliant")
}
if len(got.Problems) == 0 || got.Problems[0] != "Application.OIDC.V1.NotCompliant" {
t.Errorf("first entry of problems should be \"Application.OIDC.V1.NotCompliant\" but got %v", got.Problems)
}
})
}
}
func Test_checkGrantTypesCombination(t *testing.T) {
tests := []struct {
name string
want *Compliance
grantTypes []OIDCGrantType
}{
{
name: "implicit",
want: new(Compliance),
grantTypes: []OIDCGrantType{OIDCGrantTypeImplicit},
},
{
name: "refresh token and implicit",
want: &Compliance{
NoneCompliant: true,
Problems: []string{"Application.OIDC.V1.GrantType.Refresh.NoAuthCode"},
},
grantTypes: []OIDCGrantType{OIDCGrantTypeImplicit, OIDCGrantTypeRefreshToken},
},
{
name: "refresh token and authorization code",
want: &Compliance{},
grantTypes: []OIDCGrantType{OIDCGrantTypeAuthorizationCode, OIDCGrantTypeRefreshToken},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
compliance := new(Compliance)
checkGrantTypesCombination(compliance, tt.grantTypes)
if tt.want.NoneCompliant != compliance.NoneCompliant {
t.Errorf("NoneCompliant: expected: %v, got %v", tt.want.NoneCompliant, compliance.NoneCompliant)
}
if !reflect.DeepEqual(tt.want.Problems, compliance.Problems) {
t.Errorf("Problems: expected: %v, got %v", tt.want.Problems, compliance.Problems)
}
})
}
}
func Test_checkRedirectURIs(t *testing.T) {
type args struct {
grantTypes []OIDCGrantType
appType OIDCApplicationType
redirectUris []string
}
tests := []struct {
name string
want *Compliance
args args
}{
{
name: "no redirect uris",
want: &Compliance{
NoneCompliant: true,
Problems: []string{
"Application.OIDC.V1.NoRedirectUris",
},
},
args: args{},
},
{
name: "implicit and authorization code",
want: &Compliance{
NoneCompliant: false,
Problems: []string{"Application.OIDC.V1.NotAllCombinationsAreAllowed"},
},
args: args{
redirectUris: []string{"http://redirect.to/me"},
grantTypes: []OIDCGrantType{OIDCGrantTypeImplicit, OIDCGrantTypeAuthorizationCode},
},
},
{
name: "only implicit",
want: &Compliance{
NoneCompliant: true,
Problems: []string{"Application.OIDC.V1.Implicit.RedirectUris.HttpNotAllowed"},
},
args: args{
redirectUris: []string{"http://redirect.to/me"},
grantTypes: []OIDCGrantType{OIDCGrantTypeImplicit},
appType: OIDCApplicationTypeUserAgent,
},
},
{
name: "only authorization code",
want: &Compliance{
NoneCompliant: true,
Problems: []string{"Application.OIDC.V1.Code.RedirectUris.HttpOnlyForWeb"},
},
args: args{
redirectUris: []string{"http://redirect.to/me"},
grantTypes: []OIDCGrantType{OIDCGrantTypeAuthorizationCode},
appType: OIDCApplicationTypeUserAgent,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
compliance := new(Compliance)
checkRedirectURIs(compliance, tt.args.grantTypes, tt.args.appType, tt.args.redirectUris)
if tt.want.NoneCompliant != compliance.NoneCompliant {
t.Errorf("NoneCompliant: expected: %v, got %v", tt.want.NoneCompliant, compliance.NoneCompliant)
}
if !reflect.DeepEqual(tt.want.Problems, compliance.Problems) {
t.Errorf("Problems: expected: %v, got %v", tt.want.Problems, compliance.Problems)
}
})
}
}
func Test_CheckRedirectUrisImplicitAndCode(t *testing.T) {
type args struct {
appType OIDCApplicationType
redirectUris []string
}
tests := []struct {
name string
want *Compliance
args args
}{
{
name: "implicit and code https",
want: &Compliance{
NoneCompliant: false,
Problems: nil,
},
args: args{
redirectUris: []string{"https://redirect.to/me"},
},
},
// {
// name: "custom protocol, not native",
// want: &Compliance{
// NoneCompliant: true,
// Problems: []string{"Application.OIDC.V1.Implicit.RedirectUris.CustomNotAllowed"},
// },
// args: args{
// redirectUris: []string{"protocol://redirect.to/me"},
// appType: OIDCApplicationTypeWeb,
// },
// },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
compliance := new(Compliance)
CheckRedirectUrisImplicitAndCode(compliance, tt.args.appType, tt.args.redirectUris)
if tt.want.NoneCompliant != compliance.NoneCompliant {
t.Errorf("NoneCompliant: expected: %v, got %v", tt.want.NoneCompliant, compliance.NoneCompliant)
}
if !reflect.DeepEqual(tt.want.Problems, compliance.Problems) {
t.Errorf("Problems: expected: %v, got %v", tt.want.Problems, compliance.Problems)
}
})
}
}
func TestCheckRedirectUrisImplicitAndCode(t *testing.T) {
type args struct {
appType OIDCApplicationType
redirectUris []string
}
tests := []struct {
name string
args args
want *Compliance
}{
{
name: "only https",
args: args{},
want: &Compliance{},
},
{
name: "custom protocol not native app",
args: args{
appType: OIDCApplicationTypeWeb,
redirectUris: []string{"custom://nirvana.com"},
},
want: &Compliance{
NoneCompliant: true,
Problems: []string{"Application.OIDC.V1.Implicit.RedirectUris.CustomNotAllowed"},
},
},
{
name: "http localhost user agent app",
args: args{
appType: OIDCApplicationTypeUserAgent,
redirectUris: []string{"http://localhost:9009"},
},
want: &Compliance{
NoneCompliant: true,
Problems: []string{"Application.OIDC.V1.Code.RedirectUris.HttpOnlyForWeb"},
},
},
{
name: "http, not only localhost native app",
args: args{
appType: OIDCApplicationTypeNative,
redirectUris: []string{"http://nirvana.com", "http://localhost:9009"},
},
want: &Compliance{
NoneCompliant: true,
Problems: []string{"Application.OIDC.V1.Native.RedirectUris.MustBeHttpLocalhost"},
},
},
{
name: "not allowed combination",
args: args{
appType: OIDCApplicationTypeNative,
redirectUris: []string{"https://nirvana.com", "cutom://nirvana.com"},
},
want: &Compliance{
NoneCompliant: false,
Problems: []string{"Application.OIDC.V1.NotAllCombinationsAreAllowed"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := new(Compliance)
CheckRedirectUrisImplicitAndCode(got, tt.args.appType, tt.args.redirectUris)
if tt.want.NoneCompliant != got.NoneCompliant {
t.Errorf("NoneCompliant: expected: %v, got %v", tt.want.NoneCompliant, got.NoneCompliant)
}
if !reflect.DeepEqual(tt.want.Problems, got.Problems) {
t.Errorf("Problems: expected: %v, got %v", tt.want.Problems, got.Problems)
}
})
}
}
func TestCheckRedirectUrisImplicit(t *testing.T) {
type args struct {
appType OIDCApplicationType
redirectUris []string
}
tests := []struct {
name string
args args
want *Compliance
}{
{
name: "only https",
args: args{},
want: &Compliance{},
},
{
name: "custom protocol",
args: args{
redirectUris: []string{"custom://nirvana.com"},
},
want: &Compliance{
NoneCompliant: true,
Problems: []string{"Application.OIDC.V1.Implicit.RedirectUris.CustomNotAllowed"},
},
},
{
name: "only http protocol, app type native, not only localhost",
args: args{
redirectUris: []string{"http://nirvana.com"},
appType: OIDCApplicationTypeNative,
},
want: &Compliance{
NoneCompliant: true,
Problems: []string{"Application.OIDC.V1.Native.RedirectUris.MustBeHttpLocalhost"},
},
},
{
name: "only http protocol, app type native, only localhost",
args: args{
redirectUris: []string{"http://localhost:8080"},
appType: OIDCApplicationTypeNative,
},
want: &Compliance{
NoneCompliant: false,
Problems: nil,
},
},
{
name: "only http protocol, app type web",
args: args{
redirectUris: []string{"http://nirvana.com"},
appType: OIDCApplicationTypeWeb,
},
want: &Compliance{
NoneCompliant: true,
Problems: []string{"Application.OIDC.V1.Implicit.RedirectUris.HttpNotAllowed"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := new(Compliance)
CheckRedirectUrisImplicit(got, tt.args.appType, tt.args.redirectUris)
if tt.want.NoneCompliant != got.NoneCompliant {
t.Errorf("NoneCompliant: expected: %v, got %v", tt.want.NoneCompliant, got.NoneCompliant)
}
if !reflect.DeepEqual(tt.want.Problems, got.Problems) {
t.Errorf("Problems: expected: %v, got %v", tt.want.Problems, got.Problems)
}
})
}
}
func TestCheckRedirectUrisCode(t *testing.T) {
type args struct {
appType OIDCApplicationType
redirectUris []string
}
tests := []struct {
name string
args args
want *Compliance
}{
{
name: "only https",
args: args{},
want: &Compliance{},
},
{
name: "custom prefix, app type web",
args: args{
redirectUris: []string{"custom://nirvana.com"},
appType: OIDCApplicationTypeWeb,
},
want: &Compliance{
NoneCompliant: true,
Problems: []string{"Application.OIDC.V1.Code.RedirectUris.CustomOnlyForNative"},
},
},
{
name: "only http protocol, app type user agent",
args: args{
redirectUris: []string{"http://nirvana.com"},
appType: OIDCApplicationTypeUserAgent,
},
want: &Compliance{
NoneCompliant: true,
Problems: []string{"Application.OIDC.V1.Code.RedirectUris.HttpOnlyForWeb"},
},
},
{
name: "only http protocol, app type native, only localhost",
args: args{
redirectUris: []string{"http://localhost:8080", "http://nirvana.com:8080"},
appType: OIDCApplicationTypeNative,
},
want: &Compliance{
NoneCompliant: true,
Problems: []string{"Application.OIDC.V1.Native.RedirectUris.MustBeHttpLocalhost"},
},
},
{
name: "custom protocol, not native",
args: args{
redirectUris: []string{"custom://nirvana.com"},
appType: OIDCApplicationTypeWeb,
},
want: &Compliance{
NoneCompliant: true,
Problems: []string{"Application.OIDC.V1.Code.RedirectUris.CustomOnlyForNative"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := new(Compliance)
CheckRedirectUrisCode(got, tt.args.appType, tt.args.redirectUris)
if tt.want.NoneCompliant != got.NoneCompliant {
t.Errorf("NoneCompliant: expected: %v, got %v", tt.want.NoneCompliant, got.NoneCompliant)
}
if !reflect.DeepEqual(tt.want.Problems, got.Problems) {
t.Errorf("Problems: expected: %v, got %v", tt.want.Problems, got.Problems)
}
})
}
}
func TestOIDCOriginAllowList(t *testing.T) {
type args struct {
redirectUris []string
additionalOrigins []string
}
type want struct {
allowed []string
err func(error) bool
}
tests := []struct {
name string
args args
want want
}{
{
name: "no uris, no origins",
args: args{},
want: want{
allowed: []string{},
},
},
{
name: "redirects invalid schema",
args: args{
redirectUris: []string{"https:// localhost:8080"},
},
want: want{
allowed: nil,
err: func(e error) bool {
return strings.HasPrefix(e.Error(), "invalid chavalid character")
},
},
},
{
name: "redirects additional",
args: args{
redirectUris: []string{"https://localhost:8080"},
},
want: want{
allowed: []string{"https://localhost:8080"},
},
},
{
name: "additional origin",
args: args{
additionalOrigins: []string{"https://localhost:8080"},
},
want: want{
allowed: []string{"https://localhost:8080"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
allowed, err := OIDCOriginAllowList(tt.args.redirectUris, tt.args.additionalOrigins)
if tt.want.err == nil && err != nil {
t.Errorf("unexpected error: %v", err)
} else if tt.want.err == nil && err == nil {
//ok
} else if tt.want.err(err) {
t.Errorf("unexpected err got %v", err)
}
if !reflect.DeepEqual(allowed, tt.want.allowed) {
t.Errorf("expected list: %v, got: %v", tt.want.allowed, allowed)
}
})
}
}

View File

@ -93,63 +93,6 @@ func (repo *ProjectRepo) ProjectChanges(ctx context.Context, id string, lastSequ
return changes, nil
}
func (repo *ProjectRepo) ApplicationByID(ctx context.Context, projectID, appID string) (*proj_model.ApplicationView, error) {
app, viewErr := repo.View.ApplicationByID(projectID, appID)
if viewErr != nil && !caos_errs.IsNotFound(viewErr) {
return nil, viewErr
}
if caos_errs.IsNotFound(viewErr) {
app = new(model.ApplicationView)
app.ID = appID
}
events, esErr := repo.getProjectEvents(ctx, projectID, app.Sequence)
if caos_errs.IsNotFound(viewErr) && len(events) == 0 {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-Fshu8", "Errors.Application.NotFound")
}
if esErr != nil {
logging.Log("EVENT-SLCo9").WithError(viewErr).Debug("error retrieving new events")
return model.ApplicationViewToModel(app), nil
}
viewApp := *app
for _, event := range events {
err := app.AppendEventIfMyApp(event)
if err != nil {
return model.ApplicationViewToModel(&viewApp), nil
}
if app.State == int32(proj_model.AppStateRemoved) {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-Msl96", "Errors.Application.NotFound")
}
}
return model.ApplicationViewToModel(app), nil
}
func (repo *ProjectRepo) SearchApplications(ctx context.Context, request *proj_model.ApplicationSearchRequest) (*proj_model.ApplicationSearchResponse, error) {
err := request.EnsureLimit(repo.SearchLimit)
if err != nil {
return nil, err
}
sequence, sequenceErr := repo.View.GetLatestApplicationSequence()
logging.Log("EVENT-SKe8s").OnError(sequenceErr).Warn("could not read latest application sequence")
apps, count, err := repo.View.SearchApplications(request)
if err != nil {
return nil, err
}
result := &proj_model.ApplicationSearchResponse{
Offset: request.Offset,
Limit: request.Limit,
TotalResult: count,
Result: model.ApplicationViewsToModel(apps),
}
if sequenceErr == nil {
result.Sequence = sequence.CurrentSequence
result.Timestamp = sequence.LastSuccessfulSpoolerRun
}
return result, nil
}
func (repo *ProjectRepo) ApplicationChanges(ctx context.Context, projectID string, appID string, lastSequence uint64, limit uint64, sortAscending bool, retention time.Duration) (*proj_model.ApplicationChanges, error) {
changes, err := repo.getApplicationChanges(ctx, projectID, appID, lastSequence, limit, sortAscending, retention)
if err != nil {

View File

@ -1,167 +0,0 @@
package handler
import (
"context"
"github.com/caos/zitadel/internal/eventstore/v1"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models"
"github.com/caos/zitadel/internal/eventstore/v1/query"
es_sdk "github.com/caos/zitadel/internal/eventstore/v1/sdk"
"github.com/caos/zitadel/internal/eventstore/v1/spooler"
proj_model "github.com/caos/zitadel/internal/project/model"
es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
proj_view "github.com/caos/zitadel/internal/project/repository/view"
view_model "github.com/caos/zitadel/internal/project/repository/view/model"
)
const (
applicationTable = "management.applications"
)
type Application struct {
handler
subscription *v1.Subscription
}
func newApplication(
handler handler,
) *Application {
h := &Application{
handler: handler,
}
h.subscribe()
return h
}
func (a *Application) subscribe() {
a.subscription = a.es.Subscribe(a.AggregateTypes()...)
go func() {
for event := range a.subscription.Events {
query.ReduceEvent(a, event)
}
}()
}
func (a *Application) ViewModel() string {
return applicationTable
}
func (a *Application) Subscription() *v1.Subscription {
return a.subscription
}
func (_ *Application) AggregateTypes() []models.AggregateType {
return []models.AggregateType{es_model.ProjectAggregate}
}
func (a *Application) CurrentSequence() (uint64, error) {
sequence, err := a.view.GetLatestApplicationSequence()
if err != nil {
return 0, err
}
return sequence.CurrentSequence, nil
}
func (a *Application) EventQuery() (*models.SearchQuery, error) {
sequence, err := a.view.GetLatestApplicationSequence()
if err != nil {
return nil, err
}
return proj_view.ProjectQuery(sequence.CurrentSequence), nil
}
func (a *Application) Reduce(event *models.Event) (err error) {
app := new(view_model.ApplicationView)
switch event.Type {
case es_model.ApplicationAdded:
project, err := a.getProjectByID(context.Background(), event.AggregateID)
if err != nil {
return err
}
app.ProjectRoleCheck = project.ProjectRoleCheck
app.HasProjectCheck = project.HasProjectCheck
app.ProjectRoleAssertion = project.ProjectRoleAssertion
app.PrivateLabelingSetting = project.PrivateLabelingSetting
err = app.AppendEvent(event)
case es_model.ApplicationChanged,
es_model.OIDCConfigAdded,
es_model.OIDCConfigChanged,
es_model.APIConfigAdded,
es_model.APIConfigChanged,
es_model.ApplicationDeactivated,
es_model.ApplicationReactivated:
err = app.SetData(event)
if err != nil {
return err
}
app, err = a.view.ApplicationByID(event.AggregateID, app.ID)
if err != nil {
return err
}
err = app.AppendEvent(event)
case es_model.ApplicationRemoved:
err = app.SetData(event)
if err != nil {
return err
}
return a.view.DeleteApplication(app.ID, event)
case es_model.ProjectChanged:
apps, err := a.view.ApplicationsByProjectID(event.AggregateID)
if err != nil {
return err
}
if len(apps) == 0 {
return a.view.ProcessedApplicationSequence(event)
}
for _, app := range apps {
if err := app.AppendEvent(event); err != nil {
return err
}
}
return a.view.PutApplications(apps, event)
case es_model.ProjectRemoved:
return a.view.DeleteApplicationsByProjectID(event.AggregateID)
default:
return a.view.ProcessedApplicationSequence(event)
}
if err != nil {
return err
}
return a.view.PutApplication(app, event)
}
func (a *Application) OnError(event *models.Event, spoolerError error) error {
logging.LogWithFields("SPOOL-ls9ew", "id", event.AggregateID).WithError(spoolerError).Warn("something went wrong in project app handler")
return spooler.HandleError(event, spoolerError, a.view.GetLatestApplicationFailedEvent, a.view.ProcessedApplicationFailedEvent, a.view.ProcessedApplicationSequence, a.errorCountUntilSkip)
}
func (a *Application) OnSuccess() error {
return spooler.HandleSuccess(a.view.UpdateApplicationSpoolerRunTimestamp)
}
func (a *Application) getProjectByID(ctx context.Context, projID string) (*proj_model.Project, error) {
query, err := proj_view.ProjectByIDQuery(projID, 0)
if err != nil {
return nil, err
}
esProject := &es_model.Project{
ObjectRoot: models.ObjectRoot{
AggregateID: projID,
},
}
err = es_sdk.Filter(ctx, a.Eventstore().FilterEvents, esProject.AppendEvents, query)
if err != nil && !errors.IsNotFound(err) {
return nil, err
}
if esProject.Sequence == 0 {
return nil, errors.ThrowNotFound(nil, "EVENT-ADvfs", "Errors.Project.NotFound")
}
return es_model.ProjectToModel(esProject), nil
}

View File

@ -35,7 +35,6 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
return []query.Handler{
newProjectMember(handler{view, bulkLimit, configs.cycleDuration("ProjectMember"), errorCount, es}),
newProjectGrantMember(handler{view, bulkLimit, configs.cycleDuration("ProjectGrantMember"), errorCount, es}),
newApplication(handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount, es}),
newUser(handler{view, bulkLimit, configs.cycleDuration("User"), errorCount, es},
defaults.IamID),
newUserGrant(handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount, es}),

View File

@ -1,74 +0,0 @@
package view
import (
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/v1/models"
proj_model "github.com/caos/zitadel/internal/project/model"
"github.com/caos/zitadel/internal/project/repository/view"
"github.com/caos/zitadel/internal/project/repository/view/model"
"github.com/caos/zitadel/internal/view/repository"
)
const (
applicationTable = "management.applications"
)
func (v *View) ApplicationByID(projectID, appID string) (*model.ApplicationView, error) {
return view.ApplicationByID(v.Db, applicationTable, projectID, appID)
}
func (v *View) ApplicationsByProjectID(projectID string) ([]*model.ApplicationView, error) {
return view.ApplicationsByProjectID(v.Db, applicationTable, projectID)
}
func (v *View) SearchApplications(request *proj_model.ApplicationSearchRequest) ([]*model.ApplicationView, uint64, error) {
return view.SearchApplications(v.Db, applicationTable, request)
}
func (v *View) PutApplication(app *model.ApplicationView, event *models.Event) error {
err := view.PutApplication(v.Db, applicationTable, app)
if err != nil && !errors.IsNotFound(err) {
return err
}
return v.ProcessedApplicationSequence(event)
}
func (v *View) PutApplications(apps []*model.ApplicationView, event *models.Event) error {
err := view.PutApplications(v.Db, applicationTable, apps...)
if err != nil {
return err
}
return v.ProcessedApplicationSequence(event)
}
func (v *View) DeleteApplication(appID string, event *models.Event) error {
err := view.DeleteApplication(v.Db, applicationTable, appID)
if err != nil {
return err
}
return v.ProcessedApplicationSequence(event)
}
func (v *View) DeleteApplicationsByProjectID(projectID string) error {
return view.DeleteApplicationsByProjectID(v.Db, applicationTable, projectID)
}
func (v *View) GetLatestApplicationSequence() (*repository.CurrentSequence, error) {
return v.latestSequence(applicationTable)
}
func (v *View) ProcessedApplicationSequence(event *models.Event) error {
return v.saveCurrentSequence(applicationTable, event)
}
func (v *View) UpdateApplicationSpoolerRunTimestamp() error {
return v.updateSpoolerRunSequence(applicationTable)
}
func (v *View) GetLatestApplicationFailedEvent(sequence uint64) (*repository.FailedEvent, error) {
return v.latestFailedEvent(applicationTable, sequence)
}
func (v *View) ProcessedApplicationFailedEvent(failedEvent *repository.FailedEvent) error {
return v.saveFailedEvent(failedEvent)
}

View File

@ -17,8 +17,6 @@ type ProjectRepository interface {
ProjectChanges(ctx context.Context, id string, lastSequence uint64, limit uint64, sortAscending bool, retention time.Duration) (*model.ProjectChanges, error)
ApplicationByID(ctx context.Context, projectID, appID string) (*model.ApplicationView, error)
SearchApplications(ctx context.Context, request *model.ApplicationSearchRequest) (*model.ApplicationSearchResponse, error)
ApplicationChanges(ctx context.Context, projectID string, appID string, lastSequence uint64, limit uint64, sortAscending bool, retention time.Duration) (*model.ApplicationChanges, error)
SearchClientKeys(ctx context.Context, request *key_model.AuthNKeySearchRequest) (*key_model.AuthNKeySearchResponse, error)
GetClientKey(ctx context.Context, projectID, applicationID, keyID string) (*key_model.AuthNKeyView, error)

View File

@ -1,89 +0,0 @@
package view
import (
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors"
proj_model "github.com/caos/zitadel/internal/project/model"
"github.com/caos/zitadel/internal/project/repository/view/model"
"github.com/caos/zitadel/internal/view/repository"
"github.com/jinzhu/gorm"
)
func ApplicationByID(db *gorm.DB, table, projectID, appID string) (*model.ApplicationView, error) {
app := new(model.ApplicationView)
projectIDQuery := &model.ApplicationSearchQuery{Key: proj_model.AppSearchKeyProjectID, Value: projectID, Method: domain.SearchMethodEquals}
appIDQuery := &model.ApplicationSearchQuery{Key: proj_model.AppSearchKeyAppID, Value: appID, Method: domain.SearchMethodEquals}
query := repository.PrepareGetByQuery(table, projectIDQuery, appIDQuery)
err := query(db, app)
if caos_errs.IsNotFound(err) {
return nil, caos_errs.ThrowNotFound(nil, "VIEW-DGdfx", "Errors.Application.NotFound")
}
return app, err
}
func ApplicationsByProjectID(db *gorm.DB, table, projectID string) ([]*model.ApplicationView, error) {
applications := make([]*model.ApplicationView, 0)
queries := []*proj_model.ApplicationSearchQuery{
{Key: proj_model.AppSearchKeyProjectID, Value: projectID, Method: domain.SearchMethodEquals},
}
query := repository.PrepareSearchQuery(table, model.ApplicationSearchRequest{Queries: queries})
_, err := query(db, &applications)
if err != nil {
return nil, err
}
return applications, nil
}
func ApplicationByOIDCClientID(db *gorm.DB, table, clientID string) (*model.ApplicationView, error) {
app := new(model.ApplicationView)
clientIDQuery := model.ApplicationSearchQuery{Key: proj_model.AppSearchKeyOIDCClientID, Value: clientID, Method: domain.SearchMethodEquals}
query := repository.PrepareGetByQuery(table, clientIDQuery)
err := query(db, app)
if caos_errs.IsNotFound(err) {
return nil, caos_errs.ThrowNotFound(nil, "VIEW-DG1qh", "Errors.Project.App.NotFound")
}
return app, err
}
func ApplicationByProjectIDAndAppName(db *gorm.DB, table, projectID, appName string) (*model.ApplicationView, error) {
app := new(model.ApplicationView)
projectIDQuery := model.ApplicationSearchQuery{Key: proj_model.AppSearchKeyProjectID, Value: projectID, Method: domain.SearchMethodEquals}
appNameQuery := model.ApplicationSearchQuery{Key: proj_model.AppSearchKeyName, Value: appName, Method: domain.SearchMethodEquals}
query := repository.PrepareGetByQuery(table, projectIDQuery, appNameQuery)
err := query(db, app)
if caos_errs.IsNotFound(err) {
return nil, caos_errs.ThrowNotFound(nil, "VIEW-Jqw1z", "Errors.Project.App.NotFound")
}
return app, err
}
func SearchApplications(db *gorm.DB, table string, req *proj_model.ApplicationSearchRequest) ([]*model.ApplicationView, uint64, error) {
apps := make([]*model.ApplicationView, 0)
query := repository.PrepareSearchQuery(table, model.ApplicationSearchRequest{Limit: req.Limit, Offset: req.Offset, Queries: req.Queries})
count, err := query(db, &apps)
return apps, count, err
}
func PutApplication(db *gorm.DB, table string, app *model.ApplicationView) error {
save := repository.PrepareSave(table)
return save(db, app)
}
func PutApplications(db *gorm.DB, table string, apps ...*model.ApplicationView) error {
save := repository.PrepareBulkSave(table)
s := make([]interface{}, len(apps))
for i, app := range apps {
s[i] = app
}
return save(db, s...)
}
func DeleteApplication(db *gorm.DB, table, appID string) error {
delete := repository.PrepareDeleteByKey(table, model.ApplicationSearchKey(proj_model.AppSearchKeyAppID), appID)
return delete(db)
}
func DeleteApplicationsByProjectID(db *gorm.DB, table, projectID string) error {
delete := repository.PrepareDeleteByKey(table, model.ApplicationSearchKey(proj_model.AppSearchKeyProjectID), projectID)
return delete(db)
}

658
internal/query/app.go Normal file
View File

@ -0,0 +1,658 @@
package query
import (
"context"
"database/sql"
errs "errors"
"time"
sq "github.com/Masterminds/squirrel"
"github.com/lib/pq"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/query/projection"
)
type Apps struct {
SearchResponse
Apps []*App
}
type App struct {
ID string
CreationDate time.Time
ChangeDate time.Time
ResourceOwner string
State domain.AppState
Sequence uint64
ProjectID string
Name string
OIDCConfig *OIDCApp
APIConfig *APIApp
}
type OIDCApp struct {
RedirectURIs []string
ResponseTypes []domain.OIDCResponseType
GrantTypes []domain.OIDCGrantType
AppType domain.OIDCApplicationType
ClientID string
AuthMethodType domain.OIDCAuthMethodType
PostLogoutRedirectURIs []string
Version domain.OIDCVersion
ComplianceProblems []string
IsDevMode bool
AccessTokenType domain.OIDCTokenType
AssertAccessTokenRole bool
AssertIDTokenRole bool
AssertIDTokenUserinfo bool
ClockSkew time.Duration
AdditionalOrigins []string
AllowedOrigins []string
}
type APIApp struct {
ClientID string
AuthMethodType domain.APIAuthMethodType
}
type AppSearchQueries struct {
SearchRequest
Queries []SearchQuery
}
func (q *AppSearchQueries) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
query = q.SearchRequest.toQuery(query)
for _, q := range q.Queries {
query = q.toQuery(query)
}
return query
}
var (
appsTable = table{
name: projection.AppProjectionTable,
}
AppColumnID = Column{
name: projection.AppColumnID,
table: appsTable,
}
AppColumnName = Column{
name: projection.AppColumnName,
table: appsTable,
}
AppColumnProjectID = Column{
name: projection.AppColumnProjectID,
table: appsTable,
}
AppColumnCreationDate = Column{
name: projection.AppColumnCreationDate,
table: appsTable,
}
AppColumnChangeDate = Column{
name: projection.AppColumnChangeDate,
table: appsTable,
}
AppColumnResourceOwner = Column{
name: projection.AppColumnResourceOwner,
table: appsTable,
}
AppColumnState = Column{
name: projection.AppColumnState,
table: appsTable,
}
AppColumnSequence = Column{
name: projection.AppColumnSequence,
table: appsTable,
}
)
var (
appAPIConfigsTable = table{
name: projection.AppAPITable,
}
AppAPIConfigColumnAppID = Column{
name: projection.AppAPIConfigColumnAppID,
table: appAPIConfigsTable,
}
AppAPIConfigColumnClientID = Column{
name: projection.AppAPIConfigColumnClientID,
table: appAPIConfigsTable,
}
AppAPIConfigColumnAuthMethod = Column{
name: projection.AppAPIConfigColumnAuthMethod,
table: appAPIConfigsTable,
}
)
var (
appOIDCConfigsTable = table{
name: projection.AppOIDCTable,
}
AppOIDCConfigColumnAppID = Column{
name: projection.AppOIDCConfigColumnAppID,
table: appOIDCConfigsTable,
}
AppOIDCConfigColumnVersion = Column{
name: projection.AppOIDCConfigColumnVersion,
table: appOIDCConfigsTable,
}
AppOIDCConfigColumnClientID = Column{
name: projection.AppOIDCConfigColumnClientID,
table: appOIDCConfigsTable,
}
AppOIDCConfigColumnRedirectUris = Column{
name: projection.AppOIDCConfigColumnRedirectUris,
table: appOIDCConfigsTable,
}
AppOIDCConfigColumnResponseTypes = Column{
name: projection.AppOIDCConfigColumnResponseTypes,
table: appOIDCConfigsTable,
}
AppOIDCConfigColumnGrantTypes = Column{
name: projection.AppOIDCConfigColumnGrantTypes,
table: appOIDCConfigsTable,
}
AppOIDCConfigColumnApplicationType = Column{
name: projection.AppOIDCConfigColumnApplicationType,
table: appOIDCConfigsTable,
}
AppOIDCConfigColumnAuthMethodType = Column{
name: projection.AppOIDCConfigColumnAuthMethodType,
table: appOIDCConfigsTable,
}
AppOIDCConfigColumnPostLogoutRedirectUris = Column{
name: projection.AppOIDCConfigColumnPostLogoutRedirectUris,
table: appOIDCConfigsTable,
}
AppOIDCConfigColumnDevMode = Column{
name: projection.AppOIDCConfigColumnDevMode,
table: appOIDCConfigsTable,
}
AppOIDCConfigColumnAccessTokenType = Column{
name: projection.AppOIDCConfigColumnAccessTokenType,
table: appOIDCConfigsTable,
}
AppOIDCConfigColumnAccessTokenRoleAssertion = Column{
name: projection.AppOIDCConfigColumnAccessTokenRoleAssertion,
table: appOIDCConfigsTable,
}
AppOIDCConfigColumnIDTokenRoleAssertion = Column{
name: projection.AppOIDCConfigColumnIDTokenRoleAssertion,
table: appOIDCConfigsTable,
}
AppOIDCConfigColumnIDTokenUserinfoAssertion = Column{
name: projection.AppOIDCConfigColumnIDTokenUserinfoAssertion,
table: appOIDCConfigsTable,
}
AppOIDCConfigColumnClockSkew = Column{
name: projection.AppOIDCConfigColumnClockSkew,
table: appOIDCConfigsTable,
}
AppOIDCConfigColumnAdditionalOrigins = Column{
name: projection.AppOIDCConfigColumnAdditionalOrigins,
table: appOIDCConfigsTable,
}
)
func (q *Queries) AppByProjectAndAppID(ctx context.Context, projectID, appID string) (*App, error) {
stmt, scan := prepareAppQuery()
query, args, err := stmt.Where(
sq.Eq{
AppColumnID.identifier(): appID,
AppColumnProjectID.identifier(): projectID,
},
).ToSql()
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-AFDgg", "Errors.Query.SQLStatement")
}
row := q.client.QueryRowContext(ctx, query, args...)
return scan(row)
}
func (q *Queries) AppByID(ctx context.Context, appID string) (*App, error) {
stmt, scan := prepareAppQuery()
query, args, err := stmt.Where(
sq.Eq{AppColumnID.identifier(): appID},
).ToSql()
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-immt9", "Errors.Query.SQLStatement")
}
row := q.client.QueryRowContext(ctx, query, args...)
return scan(row)
}
func (q *Queries) ProjectIDFromOIDCClientID(ctx context.Context, appID string) (string, error) {
stmt, scan := prepareProjectIDByAppQuery()
query, args, err := stmt.Where(
sq.Eq{AppOIDCConfigColumnClientID.identifier(): appID},
).ToSql()
if err != nil {
return "", errors.ThrowInternal(err, "QUERY-7d92U", "Errors.Query.SQLStatement")
}
row := q.client.QueryRowContext(ctx, query, args...)
return scan(row)
}
func (q *Queries) ProjectByOIDCClientID(ctx context.Context, id string) (*Project, error) {
stmt, scan := prepareProjectByAppQuery()
query, args, err := stmt.Where(
sq.Eq{AppOIDCConfigColumnClientID.identifier(): id},
).ToSql()
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-XhJi4", "Errors.Query.SQLStatement")
}
row := q.client.QueryRowContext(ctx, query, args...)
return scan(row)
}
func (q *Queries) AppByOIDCClientID(ctx context.Context, clientID string) (*App, error) {
stmt, scan := prepareAppQuery()
query, args, err := stmt.Where(
sq.Eq{
AppOIDCConfigColumnClientID.identifier(): clientID,
},
).ToSql()
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-JgVop", "Errors.Query.SQLStatement")
}
row := q.client.QueryRowContext(ctx, query, args...)
return scan(row)
}
func (q *Queries) SearchApps(ctx context.Context, queries *AppSearchQueries) (*Apps, error) {
query, scan := prepareAppsQuery()
stmt, args, err := queries.toQuery(query).ToSql()
if err != nil {
return nil, errors.ThrowInvalidArgument(err, "QUERY-fajp8", "Errors.Query.InvalidRequest")
}
rows, err := q.client.QueryContext(ctx, stmt, args...)
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-aJnZL", "Errors.Internal")
}
apps, err := scan(rows)
if err != nil {
return nil, err
}
apps.LatestSequence, err = q.latestSequence(ctx, appsTable)
return apps, err
}
func (q *Queries) SearchAppIDs(ctx context.Context, queries *AppSearchQueries) ([]string, error) {
query, scan := prepareAppIDsQuery()
stmt, args, err := queries.toQuery(query).ToSql()
if err != nil {
return nil, errors.ThrowInvalidArgument(err, "QUERY-fajp8", "Errors.Query.InvalidRequest")
}
rows, err := q.client.QueryContext(ctx, stmt, args...)
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-aJnZL", "Errors.Internal")
}
return scan(rows)
}
func NewAppNameSearchQuery(method TextComparison, value string) (SearchQuery, error) {
return NewTextQuery(AppColumnName, value, method)
}
func NewAppProjectIDSearchQuery(id string) (SearchQuery, error) {
return NewTextQuery(AppColumnProjectID, id, TextEquals)
}
func prepareAppQuery() (sq.SelectBuilder, func(*sql.Row) (*App, error)) {
return sq.Select(
AppColumnID.identifier(),
AppColumnName.identifier(),
AppColumnProjectID.identifier(),
AppColumnCreationDate.identifier(),
AppColumnChangeDate.identifier(),
AppColumnResourceOwner.identifier(),
AppColumnState.identifier(),
AppColumnSequence.identifier(),
AppAPIConfigColumnAppID.identifier(),
AppAPIConfigColumnClientID.identifier(),
AppAPIConfigColumnAuthMethod.identifier(),
AppOIDCConfigColumnAppID.identifier(),
AppOIDCConfigColumnVersion.identifier(),
AppOIDCConfigColumnClientID.identifier(),
AppOIDCConfigColumnRedirectUris.identifier(),
AppOIDCConfigColumnResponseTypes.identifier(),
AppOIDCConfigColumnGrantTypes.identifier(),
AppOIDCConfigColumnApplicationType.identifier(),
AppOIDCConfigColumnAuthMethodType.identifier(),
AppOIDCConfigColumnPostLogoutRedirectUris.identifier(),
AppOIDCConfigColumnDevMode.identifier(),
AppOIDCConfigColumnAccessTokenType.identifier(),
AppOIDCConfigColumnAccessTokenRoleAssertion.identifier(),
AppOIDCConfigColumnIDTokenRoleAssertion.identifier(),
AppOIDCConfigColumnIDTokenUserinfoAssertion.identifier(),
AppOIDCConfigColumnClockSkew.identifier(),
AppOIDCConfigColumnAdditionalOrigins.identifier(),
).From(appsTable.identifier()).
LeftJoin(join(AppAPIConfigColumnAppID, AppColumnID)).
LeftJoin(join(AppOIDCConfigColumnAppID, AppColumnID)).
PlaceholderFormat(sq.Dollar), func(row *sql.Row) (*App, error) {
app := new(App)
var (
apiConfig = sqlAPIConfig{}
oidcConfig = sqlOIDCConfig{}
)
err := row.Scan(
&app.ID,
&app.Name,
&app.ProjectID,
&app.CreationDate,
&app.ChangeDate,
&app.ResourceOwner,
&app.State,
&app.Sequence,
&apiConfig.appID,
&apiConfig.clientID,
&apiConfig.authMethod,
&oidcConfig.appID,
&oidcConfig.version,
&oidcConfig.clientID,
&oidcConfig.redirectUris,
&oidcConfig.responseTypes,
&oidcConfig.grantTypes,
&oidcConfig.applicationType,
&oidcConfig.authMethodType,
&oidcConfig.postLogoutRedirectUris,
&oidcConfig.devMode,
&oidcConfig.accessTokenType,
&oidcConfig.accessTokenRoleAssertion,
&oidcConfig.iDTokenRoleAssertion,
&oidcConfig.iDTokenUserinfoAssertion,
&oidcConfig.clockSkew,
&oidcConfig.additionalOrigins,
)
if err != nil {
if errs.Is(err, sql.ErrNoRows) {
return nil, errors.ThrowNotFound(err, "QUERY-pCP8P", "Errors.App.NotExisting")
}
return nil, errors.ThrowInternal(err, "QUERY-0R2Nw", "Errors.Internal")
}
apiConfig.set(app)
oidcConfig.set(app)
return app, nil
}
}
func prepareProjectIDByAppQuery() (sq.SelectBuilder, func(*sql.Row) (projectID string, err error)) {
return sq.Select(
AppColumnProjectID.identifier(),
).From(appsTable.identifier()).
LeftJoin(join(AppAPIConfigColumnAppID, AppColumnID)).
LeftJoin(join(AppOIDCConfigColumnAppID, AppColumnID)).
PlaceholderFormat(sq.Dollar), func(row *sql.Row) (projectID string, err error) {
err = row.Scan(
&projectID,
)
if err != nil {
if errs.Is(err, sql.ErrNoRows) {
return "", errors.ThrowNotFound(err, "QUERY-aKcc2", "Errors.Project.NotExisting")
}
return "", errors.ThrowInternal(err, "QUERY-3A5TG", "Errors.Internal")
}
return projectID, nil
}
}
func prepareProjectByAppQuery() (sq.SelectBuilder, func(*sql.Row) (*Project, error)) {
return sq.Select(
ProjectColumnID.identifier(),
ProjectColumnCreationDate.identifier(),
ProjectColumnChangeDate.identifier(),
ProjectColumnResourceOwner.identifier(),
ProjectColumnState.identifier(),
ProjectColumnSequence.identifier(),
ProjectColumnName.identifier(),
ProjectColumnProjectRoleAssertion.identifier(),
ProjectColumnProjectRoleCheck.identifier(),
ProjectColumnHasProjectCheck.identifier(),
ProjectColumnPrivateLabelingSetting.identifier(),
).From(projectsTable.identifier()).
Join(join(AppColumnProjectID, ProjectColumnID)).
LeftJoin(join(AppAPIConfigColumnAppID, AppColumnID)).
LeftJoin(join(AppOIDCConfigColumnAppID, AppColumnID)).
PlaceholderFormat(sq.Dollar),
func(row *sql.Row) (*Project, error) {
p := new(Project)
err := row.Scan(
&p.ID,
&p.CreationDate,
&p.ChangeDate,
&p.ResourceOwner,
&p.State,
&p.Sequence,
&p.Name,
&p.ProjectRoleAssertion,
&p.ProjectRoleCheck,
&p.HasProjectCheck,
&p.PrivateLabelingSetting,
)
if err != nil {
if errs.Is(err, sql.ErrNoRows) {
return nil, errors.ThrowNotFound(err, "QUERY-fk2fs", "Errors.Project.NotFound")
}
return nil, errors.ThrowInternal(err, "QUERY-dj2FF", "Errors.Internal")
}
return p, nil
}
}
func prepareAppsQuery() (sq.SelectBuilder, func(*sql.Rows) (*Apps, error)) {
return sq.Select(
AppColumnID.identifier(),
AppColumnName.identifier(),
AppColumnProjectID.identifier(),
AppColumnCreationDate.identifier(),
AppColumnChangeDate.identifier(),
AppColumnResourceOwner.identifier(),
AppColumnState.identifier(),
AppColumnSequence.identifier(),
AppAPIConfigColumnAppID.identifier(),
AppAPIConfigColumnClientID.identifier(),
AppAPIConfigColumnAuthMethod.identifier(),
AppOIDCConfigColumnAppID.identifier(),
AppOIDCConfigColumnVersion.identifier(),
AppOIDCConfigColumnClientID.identifier(),
AppOIDCConfigColumnRedirectUris.identifier(),
AppOIDCConfigColumnResponseTypes.identifier(),
AppOIDCConfigColumnGrantTypes.identifier(),
AppOIDCConfigColumnApplicationType.identifier(),
AppOIDCConfigColumnAuthMethodType.identifier(),
AppOIDCConfigColumnPostLogoutRedirectUris.identifier(),
AppOIDCConfigColumnDevMode.identifier(),
AppOIDCConfigColumnAccessTokenType.identifier(),
AppOIDCConfigColumnAccessTokenRoleAssertion.identifier(),
AppOIDCConfigColumnIDTokenRoleAssertion.identifier(),
AppOIDCConfigColumnIDTokenUserinfoAssertion.identifier(),
AppOIDCConfigColumnClockSkew.identifier(),
AppOIDCConfigColumnAdditionalOrigins.identifier(),
countColumn.identifier(),
).From(appsTable.identifier()).
LeftJoin(join(AppAPIConfigColumnAppID, AppColumnID)).
LeftJoin(join(AppOIDCConfigColumnAppID, AppColumnID)).
PlaceholderFormat(sq.Dollar), func(row *sql.Rows) (*Apps, error) {
apps := &Apps{Apps: []*App{}}
for row.Next() {
app := new(App)
var (
apiConfig = sqlAPIConfig{}
oidcConfig = sqlOIDCConfig{}
)
err := row.Scan(
&app.ID,
&app.Name,
&app.ProjectID,
&app.CreationDate,
&app.ChangeDate,
&app.ResourceOwner,
&app.State,
&app.Sequence,
&apiConfig.appID,
&apiConfig.clientID,
&apiConfig.authMethod,
&oidcConfig.appID,
&oidcConfig.version,
&oidcConfig.clientID,
&oidcConfig.redirectUris,
&oidcConfig.responseTypes,
&oidcConfig.grantTypes,
&oidcConfig.applicationType,
&oidcConfig.authMethodType,
&oidcConfig.postLogoutRedirectUris,
&oidcConfig.devMode,
&oidcConfig.accessTokenType,
&oidcConfig.accessTokenRoleAssertion,
&oidcConfig.iDTokenRoleAssertion,
&oidcConfig.iDTokenUserinfoAssertion,
&oidcConfig.clockSkew,
&oidcConfig.additionalOrigins,
&apps.Count,
)
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-0R2Nw", "Errors.Internal")
}
apiConfig.set(app)
oidcConfig.set(app)
apps.Apps = append(apps.Apps, app)
}
return apps, nil
}
}
func prepareAppIDsQuery() (sq.SelectBuilder, func(*sql.Rows) ([]string, error)) {
return sq.Select(
AppColumnID.identifier(),
).From(appsTable.identifier()).
LeftJoin(join(AppAPIConfigColumnAppID, AppColumnID)).
LeftJoin(join(AppOIDCConfigColumnAppID, AppColumnID)).
PlaceholderFormat(sq.Dollar), func(row *sql.Rows) ([]string, error) {
ids := []string{}
for row.Next() {
var id string
if err := row.Scan(&id); err != nil {
return nil, errors.ThrowInternal(err, "QUERY-0R2Nw", "Errors.Internal")
}
ids = append(ids, id)
}
return ids, nil
}
}
type sqlOIDCConfig struct {
appID sql.NullString
version sql.NullInt32
clientID sql.NullString
redirectUris pq.StringArray
applicationType sql.NullInt16
authMethodType sql.NullInt16
postLogoutRedirectUris pq.StringArray
devMode sql.NullBool
accessTokenType sql.NullInt16
accessTokenRoleAssertion sql.NullBool
iDTokenRoleAssertion sql.NullBool
iDTokenUserinfoAssertion sql.NullBool
clockSkew sql.NullInt64
additionalOrigins pq.StringArray
responseTypes pq.Int32Array
grantTypes pq.Int32Array
}
func (c sqlOIDCConfig) set(app *App) {
if !c.appID.Valid {
return
}
app.OIDCConfig = &OIDCApp{
Version: domain.OIDCVersion(c.version.Int32),
ClientID: c.clientID.String,
RedirectURIs: c.redirectUris,
AppType: domain.OIDCApplicationType(c.applicationType.Int16),
AuthMethodType: domain.OIDCAuthMethodType(c.authMethodType.Int16),
PostLogoutRedirectURIs: c.postLogoutRedirectUris,
IsDevMode: c.devMode.Bool,
AccessTokenType: domain.OIDCTokenType(c.accessTokenType.Int16),
AssertAccessTokenRole: c.accessTokenRoleAssertion.Bool,
AssertIDTokenRole: c.iDTokenRoleAssertion.Bool,
AssertIDTokenUserinfo: c.iDTokenUserinfoAssertion.Bool,
ClockSkew: time.Duration(c.clockSkew.Int64),
AdditionalOrigins: c.additionalOrigins,
ResponseTypes: oidcResponseTypesToDomain(c.responseTypes),
GrantTypes: oidcGrantTypesToDomain(c.grantTypes),
}
compliance := domain.GetOIDCCompliance(app.OIDCConfig.Version, app.OIDCConfig.AppType, app.OIDCConfig.GrantTypes, app.OIDCConfig.ResponseTypes, app.OIDCConfig.AuthMethodType, app.OIDCConfig.RedirectURIs)
app.OIDCConfig.ComplianceProblems = compliance.Problems
var err error
app.OIDCConfig.AllowedOrigins, err = domain.OIDCOriginAllowList(app.OIDCConfig.RedirectURIs, app.OIDCConfig.AdditionalOrigins)
logging.LogWithFields("app", app.ID).OnError(err).Warn("unable to set allowed origins")
}
type sqlAPIConfig struct {
appID sql.NullString
clientID sql.NullString
authMethod sql.NullInt16
}
func (c sqlAPIConfig) set(app *App) {
if !c.appID.Valid {
return
}
app.APIConfig = &APIApp{
ClientID: c.clientID.String,
AuthMethodType: domain.APIAuthMethodType(c.authMethod.Int16),
}
}
func oidcResponseTypesToDomain(t pq.Int32Array) []domain.OIDCResponseType {
types := make([]domain.OIDCResponseType, len(t))
for i, typ := range t {
types[i] = domain.OIDCResponseType(typ)
}
return types
}
func oidcGrantTypesToDomain(t pq.Int32Array) []domain.OIDCGrantType {
types := make([]domain.OIDCGrantType, len(t))
for i, typ := range t {
types[i] = domain.OIDCGrantType(typ)
}
return types
}

1699
internal/query/app_test.go Normal file

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,22 @@ import (
errs "github.com/caos/zitadel/internal/errors"
)
var (
projectCols = []string{
"id",
"creation_date",
"change_date",
"resource_owner",
"state",
"sequence",
"name",
"project_role_assertion",
"project_role_check",
"has_project_check",
"private_labeling_setting",
}
)
func Test_ProjectPrepares(t *testing.T) {
type want struct {
sqlExpectations sqlExpectation

View File

@ -815,6 +815,7 @@ EventTypes:
removed: Aktion gelöscht
Application:
OIDC:
UnsupportedVersion: Deine OIDC Version wird nicht unterstützt
V1:
NotCompliant: Deine Konfiguration ist nicht konform und weicht vom OIDC 1.0 Standard ab.
NoRedirectUris: Es muss mindestens eine Redirect URI erfasst sein.

View File

@ -812,6 +812,7 @@ EventTypes:
removed: Action removed
Application:
OIDC:
UnsupportedVersion: Your OIDC version is not supported
V1:
NotCompliant: Your configuration is not compliant and differs from OIDC 1.0 standard.
NoRedirectUris: At least one redirect uri must be registered.

View File

@ -810,6 +810,7 @@ EventTypes:
removed: Azione rimossa
Application:
OIDC:
UnsupportedVersion: La tua versione di OIDC non è supportata
V1:
NotCompliant: La tua configurazione non è conforme e differisce dallo standard OIDC 1.0.
NoRedirectUris: Deve essere registrato almeno un URI di reindirizzamento.