zitadel/internal/query/userinfo.go
Tim Möhlmann 4cd52f33eb
chore(oidc): remove feature flag for introspection triggers (#10132)
# Which Problems Are Solved

Remove the feature flag that allowed triggers in introspection. This
option was a fallback in case introspection would not function properly
without triggers. The API documentation asked for anyone using this flag
to raise an issue. No such issue was received, hence we concluded it is
safe to remove it.

# How the Problems Are Solved

- Remove flags from the system and instance level feature APIs.
- Remove trigger functions that are no longer used
- Adjust tests that used the flag.

# Additional Changes

- none

# Additional Context

- Closes #10026 
- Flag was introduced in #7356

---------

Co-authored-by: Silvan <27845747+adlerhurst@users.noreply.github.com>
2025-06-30 05:48:04 +00:00

118 lines
3.7 KiB
Go

package query
import (
"context"
"database/sql"
_ "embed"
"errors"
"strings"
"sync"
"text/template"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/eventstore/handler/v2"
"github.com/zitadel/zitadel/internal/query/projection"
"github.com/zitadel/zitadel/internal/telemetry/tracing"
"github.com/zitadel/zitadel/internal/zerrors"
)
// oidcUserInfoTriggerHandlers slice can only be created after zitadel
// is fully initialized, otherwise the handlers are nil.
// OnceValue takes care of creating the slice on the first request
// and than will always return the same slice on subsequent requests.
var oidcUserInfoTriggerHandlers = sync.OnceValue(func() []*handler.Handler {
return []*handler.Handler{
projection.UserProjection,
projection.UserMetadataProjection,
projection.UserGrantProjection,
projection.OrgProjection,
projection.ProjectProjection,
}
})
var (
//go:embed userinfo_by_id.sql
oidcUserInfoQueryTmpl string
oidcUserInfoQuery string
oidcUserInfoWithRoleOrgIDsQuery string
)
// build the two variants of the userInfo query
func init() {
tmpl := template.Must(template.New("oidcUserInfoQuery").Parse(oidcUserInfoQueryTmpl))
var buf strings.Builder
if err := tmpl.Execute(&buf, false); err != nil {
panic(err)
}
oidcUserInfoQuery = buf.String()
buf.Reset()
if err := tmpl.Execute(&buf, true); err != nil {
panic(err)
}
oidcUserInfoWithRoleOrgIDsQuery = buf.String()
buf.Reset()
}
func (q *Queries) GetOIDCUserInfo(ctx context.Context, userID string, roleAudience []string, roleOrgIDs ...string) (userInfo *OIDCUserInfo, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
if len(roleOrgIDs) > 0 {
userInfo, err = database.QueryJSONObject[OIDCUserInfo](ctx, q.client, oidcUserInfoWithRoleOrgIDsQuery,
userID, authz.GetInstance(ctx).InstanceID(), database.TextArray[string](roleAudience), database.TextArray[string](roleOrgIDs),
)
} else {
userInfo, err = database.QueryJSONObject[OIDCUserInfo](ctx, q.client, oidcUserInfoQuery,
userID, authz.GetInstance(ctx).InstanceID(), database.TextArray[string](roleAudience),
)
}
if errors.Is(err, sql.ErrNoRows) {
return nil, zerrors.ThrowNotFound(err, "QUERY-Eey2a", "Errors.User.NotFound")
}
if err != nil {
return nil, zerrors.ThrowInternal(err, "QUERY-Oath6", "Errors.Internal")
}
if userInfo.User == nil {
return nil, zerrors.ThrowNotFound(nil, "QUERY-ahs4S", "Errors.User.NotFound")
}
return userInfo, nil
}
type OIDCUserInfo struct {
User *User `json:"user,omitempty"`
Metadata []UserMetadata `json:"metadata,omitempty"`
Org *UserInfoOrg `json:"org,omitempty"`
UserGrants []UserGrant `json:"user_grants,omitempty"`
}
type UserInfoOrg struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
PrimaryDomain string `json:"primary_domain,omitempty"`
}
//go:embed userinfo_client_by_id.sql
var oidcUserinfoClientQuery string
func (q *Queries) GetOIDCUserinfoClientByID(ctx context.Context, clientID string) (projectID string, projectRoleAssertion bool, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
scan := func(row *sql.Row) error {
err := row.Scan(&projectID, &projectRoleAssertion)
return err
}
err = q.client.QueryRowContext(ctx, scan, oidcUserinfoClientQuery, authz.GetInstance(ctx).InstanceID(), clientID)
if errors.Is(err, sql.ErrNoRows) {
return "", false, zerrors.ThrowNotFound(err, "QUERY-beeW8", "Errors.App.NotFound")
}
if err != nil {
return "", false, zerrors.ThrowInternal(err, "QUERY-Ais4r", "Errors.Internal")
}
return projectID, projectRoleAssertion, nil
}