feat: add tracing interceptors to login and oidc (#764)

* add tracing interceptors to login and oidc

* add some tracing spans

* trace login calls

* add some spans

* add some spans (change password)

* add some more tracing in oauth/oidc

* revert org exists

* Merge branch 'master' into http-tracing

# Conflicts:
#	internal/api/oidc/auth_request.go
#	internal/api/oidc/client.go
#	internal/auth/repository/eventsourcing/eventstore/auth_request.go
#	internal/auth/repository/eventsourcing/eventstore/user.go
#	internal/authz/repository/eventsourcing/eventstore/token_verifier.go
#	internal/authz/repository/eventsourcing/view/token.go
#	internal/user/repository/eventsourcing/eventstore.go
This commit is contained in:
Livio Amstutz
2020-10-21 10:18:34 +02:00
committed by GitHub
parent 6e602e6b8d
commit b3f68c8f48
25 changed files with 228 additions and 75 deletions

View File

@@ -7,14 +7,18 @@ import (
"strings"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/tracing"
)
const (
authenticated = "authenticated"
)
func CheckUserAuthorization(ctx context.Context, req interface{}, token, orgID string, verifier *TokenVerifier, authConfig Config, requiredAuthOption Option, method string) (context.Context, error) {
ctx, err := VerifyTokenAndWriteCtxData(ctx, token, orgID, verifier, method)
func CheckUserAuthorization(ctx context.Context, req interface{}, token, orgID string, verifier *TokenVerifier, authConfig Config, requiredAuthOption Option, method string) (_ context.Context, err error) {
ctx, span := tracing.NewServerInterceptorSpan(ctx)
defer func() { span.EndWithError(err) }()
ctx, err = VerifyTokenAndWriteCtxData(ctx, token, orgID, verifier, method)
if err != nil {
return nil, err
}
@@ -29,7 +33,9 @@ func CheckUserAuthorization(ctx context.Context, req interface{}, token, orgID s
return nil, err
}
ctx, userPermissionSpan := tracing.NewNamedSpan(ctx, "checkUserPermissions")
err = checkUserPermissions(req, perms, requiredAuthOption)
userPermissionSpan.EndWithError(err)
if err != nil {
return nil, err
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/caos/zitadel/internal/api/grpc"
http_util "github.com/caos/zitadel/internal/api/http"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/tracing"
)
type key int
@@ -36,6 +37,9 @@ type Grant struct {
}
func VerifyTokenAndWriteCtxData(ctx context.Context, token, orgID string, t *TokenVerifier, method string) (_ context.Context, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
if orgID != "" {
err = t.ExistsOrg(ctx, orgID)
if err != nil {

View File

@@ -4,9 +4,13 @@ import (
"context"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/tracing"
)
func getUserMethodPermissions(ctx context.Context, t *TokenVerifier, requiredPerm string, authConfig Config) (context.Context, []string, error) {
func getUserMethodPermissions(ctx context.Context, t *TokenVerifier, requiredPerm string, authConfig Config) (_ context.Context, _ []string, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
ctxData := GetCtxData(ctx)
if ctxData.IsZero() {
return nil, nil, errors.ThrowUnauthenticated(nil, "AUTH-rKLWEH", "context missing")

View File

@@ -6,6 +6,7 @@ import (
"sync"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/tracing"
)
const (
@@ -62,7 +63,10 @@ func prefixFromMethod(method string) (string, bool) {
return parts[1], true
}
func (v *TokenVerifier) clientIDFromMethod(ctx context.Context, method string) (string, error) {
func (v *TokenVerifier) clientIDFromMethod(ctx context.Context, method string) (_ 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")
@@ -71,7 +75,6 @@ func (v *TokenVerifier) clientIDFromMethod(ctx context.Context, method string) (
if !ok {
return "", caos_errs.ThrowPermissionDenied(nil, "AUTHZ-G2qrh", "Errors.Internal")
}
var err error
c := app.(*client)
if c.id != "" {
return c.id, nil
@@ -84,15 +87,22 @@ func (v *TokenVerifier) clientIDFromMethod(ctx context.Context, method string) (
return c.id, nil
}
func (v *TokenVerifier) ResolveGrant(ctx context.Context) (*Grant, error) {
func (v *TokenVerifier) ResolveGrant(ctx context.Context) (_ *Grant, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
return v.authZRepo.ResolveGrants(ctx)
}
func (v *TokenVerifier) ProjectIDAndOriginsByClientID(ctx context.Context, clientID string) (string, []string, error) {
func (v *TokenVerifier) ProjectIDAndOriginsByClientID(ctx context.Context, clientID string) (_ string, _ []string, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
return v.authZRepo.ProjectIDAndOriginsByClientID(ctx, clientID)
}
func (v *TokenVerifier) ExistsOrg(ctx context.Context, orgID string) error {
func (v *TokenVerifier) ExistsOrg(ctx context.Context, orgID string) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
return v.authZRepo.ExistsOrg(ctx, orgID)
}
@@ -102,6 +112,9 @@ func (v *TokenVerifier) CheckAuthMethod(method string) (Option, bool) {
}
func verifyAccessToken(ctx context.Context, token string, t *TokenVerifier, method string) (userID, clientID, agentID, prefLang string, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
parts := strings.Split(token, BearerPrefix)
if len(parts) != 2 {
return "", "", "", "", caos_errs.ThrowUnauthenticated(nil, "AUTH-7fs1e", "invalid auth header")

View File

@@ -9,7 +9,7 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/stats"
"github.com/caos/zitadel/internal/api/http"
grpc_utils "github.com/caos/zitadel/internal/api/grpc"
"github.com/caos/zitadel/internal/tracing"
)
@@ -29,7 +29,7 @@ func TracingStatsClient(ignoredMethods ...GRPCMethod) grpc.DialOption {
}
func DefaultTracingStatsClient() grpc.DialOption {
return TracingStatsClient(http.Healthz, http.Readiness, http.Validation)
return TracingStatsClient(grpc_utils.Healthz, grpc_utils.Readiness, grpc_utils.Validation)
}
type tracingClientHandler struct {

View File

@@ -30,6 +30,9 @@ func CaosToGRPCError(ctx context.Context, err error) error {
}
func ExtractCaosError(err error) (c codes.Code, msg, id string, ok bool) {
if err == nil {
return codes.OK, "", "", false
}
switch caosErr := err.(type) {
case *caos_errs.AlreadyExistsError:
return codes.AlreadyExists, caosErr.GetMessage(), caosErr.GetID(), true

View File

@@ -0,0 +1,11 @@
package grpc
const (
Healthz = "/Healthz"
Readiness = "/Ready"
Validation = "/Validate"
)
var (
Probes = []string{Healthz, Readiness, Validation}
)

View File

@@ -10,6 +10,7 @@ import (
"github.com/caos/zitadel/internal/api/authz"
grpc_util "github.com/caos/zitadel/internal/api/grpc"
"github.com/caos/zitadel/internal/api/http"
"github.com/caos/zitadel/internal/tracing"
)
func AuthorizationInterceptor(verifier *authz.TokenVerifier, authConfig authz.Config) grpc.UnaryServerInterceptor {
@@ -18,9 +19,13 @@ func AuthorizationInterceptor(verifier *authz.TokenVerifier, authConfig authz.Co
}
}
func authorize(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, verifier *authz.TokenVerifier, authConfig authz.Config) (interface{}, error) {
func authorize(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, verifier *authz.TokenVerifier, authConfig authz.Config) (_ interface{}, err error) {
ctx, span := tracing.NewServerInterceptorSpan(ctx)
defer func() { span.EndWithError(err) }()
authOpt, needsToken := verifier.CheckAuthMethod(info.FullMethod)
if !needsToken {
span.End()
return handler(ctx, req)
}
@@ -31,10 +36,10 @@ func authorize(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo,
orgID := grpc_util.GetHeader(ctx, http.ZitadelOrgID)
ctx, err := authz.CheckUserAuthorization(ctx, req, authToken, orgID, verifier, authConfig, authOpt, info.FullMethod)
ctx, err = authz.CheckUserAuthorization(ctx, req, authToken, orgID, verifier, authConfig, authOpt, info.FullMethod)
if err != nil {
return nil, err
}
span.End()
return handler(ctx, req)
}

View File

@@ -9,7 +9,7 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/stats"
"github.com/caos/zitadel/internal/api/http"
grpc_utils "github.com/caos/zitadel/internal/api/grpc"
"github.com/caos/zitadel/internal/tracing"
)
@@ -30,7 +30,7 @@ func TracingStatsServer(ignoredMethods ...GRPCMethod) grpc.ServerOption {
}
func DefaultTracingStatsServer() grpc.ServerOption {
return TracingStatsServer(http.Healthz, http.Readiness, http.Validation)
return TracingStatsServer(grpc_utils.Healthz, grpc_utils.Readiness, grpc_utils.Validation)
}
type tracingServerHandler struct {

View File

@@ -27,7 +27,7 @@ type Server interface {
func CreateServer(verifier *authz.TokenVerifier, authConfig authz.Config, lang language.Tag) *grpc.Server {
return grpc.NewServer(
middleware.TracingStatsServer(http.Healthz, http.Readiness, http.Validation),
middleware.DefaultTracingStatsServer(),
grpc.UnaryInterceptor(
grpc_middleware.ChainUnaryServer(
middleware.ErrorHandler(),

View File

@@ -10,3 +10,9 @@ import (
func DefaultTraceHandler(handler http.Handler) http.Handler {
return tracing.TraceHandler(handler, http_utils.Probes...)
}
func TraceHandler(ignoredMethods ...string) func(http.Handler) http.Handler {
return func(handler http.Handler) http.Handler {
return tracing.TraceHandler(handler, ignoredMethods...)
}
}

View File

@@ -1,9 +1,9 @@
package http
const (
Healthz = "/Healthz"
Readiness = "/Ready"
Validation = "/Validate"
Healthz = "/healthz"
Readiness = "/ready"
Validation = "/validate"
)
var (

View File

@@ -13,10 +13,13 @@ 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/tracing"
grant_model "github.com/caos/zitadel/internal/usergrant/model"
)
func (o *OPStorage) CreateAuthRequest(ctx context.Context, req *oidc.AuthRequest, userID string) (op.AuthRequest, error) {
func (o *OPStorage) CreateAuthRequest(ctx context.Context, req *oidc.AuthRequest, userID string) (_ op.AuthRequest, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
userAgentID, ok := middleware.UserAgentIDFromCtx(ctx)
if !ok {
return nil, errors.ThrowPreconditionFailed(nil, "OIDC-sd436", "no user agent id")
@@ -37,7 +40,9 @@ func (o *OPStorage) CreateAuthRequest(ctx context.Context, req *oidc.AuthRequest
return AuthRequestFromBusiness(resp)
}
func (o *OPStorage) AuthRequestByID(ctx context.Context, id string) (op.AuthRequest, error) {
func (o *OPStorage) AuthRequestByID(ctx context.Context, id string) (_ op.AuthRequest, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
userAgentID, ok := middleware.UserAgentIDFromCtx(ctx)
if !ok {
return nil, errors.ThrowPreconditionFailed(nil, "OIDC-D3g21", "no user agent id")
@@ -49,7 +54,9 @@ func (o *OPStorage) AuthRequestByID(ctx context.Context, id string) (op.AuthRequ
return AuthRequestFromBusiness(resp)
}
func (o *OPStorage) AuthRequestByCode(ctx context.Context, code string) (op.AuthRequest, error) {
func (o *OPStorage) AuthRequestByCode(ctx context.Context, code string) (_ op.AuthRequest, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
resp, err := o.repo.AuthRequestByCode(ctx, code)
if err != nil {
return nil, err
@@ -57,7 +64,9 @@ func (o *OPStorage) AuthRequestByCode(ctx context.Context, code string) (op.Auth
return AuthRequestFromBusiness(resp)
}
func (o *OPStorage) SaveAuthCode(ctx context.Context, id, code string) error {
func (o *OPStorage) SaveAuthCode(ctx context.Context, id, code string) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
userAgentID, ok := middleware.UserAgentIDFromCtx(ctx)
if !ok {
return errors.ThrowPreconditionFailed(nil, "OIDC-Dgus2", "no user agent id")
@@ -65,11 +74,15 @@ func (o *OPStorage) SaveAuthCode(ctx context.Context, id, code string) error {
return o.repo.SaveAuthCode(ctx, id, code, userAgentID)
}
func (o *OPStorage) DeleteAuthRequest(ctx context.Context, id string) error {
func (o *OPStorage) DeleteAuthRequest(ctx context.Context, id string) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
return o.repo.DeleteAuthRequest(ctx, id)
}
func (o *OPStorage) CreateToken(ctx context.Context, req op.TokenRequest) (string, time.Time, error) {
func (o *OPStorage) CreateToken(ctx context.Context, req op.TokenRequest) (_ string, _ time.Time, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
var userAgentID, applicationID string
authReq, ok := req.(*AuthRequest)
if ok {
@@ -93,7 +106,9 @@ func grantsToScopes(grants []*grant_model.UserGrantView) []string {
return scopes
}
func (o *OPStorage) TerminateSession(ctx context.Context, userID, clientID string) error {
func (o *OPStorage) TerminateSession(ctx context.Context, userID, clientID string) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
userAgentID, ok := middleware.UserAgentIDFromCtx(ctx)
if !ok {
return errors.ThrowPreconditionFailed(nil, "OIDC-fso7F", "no user agent id")
@@ -105,7 +120,9 @@ func (o *OPStorage) GetSigningKey(ctx context.Context, keyCh chan<- jose.Signing
o.repo.GetSigningKey(ctx, keyCh, errCh, timer)
}
func (o *OPStorage) GetKeySet(ctx context.Context) (*jose.JSONWebKeySet, error) {
func (o *OPStorage) GetKeySet(ctx context.Context) (_ *jose.JSONWebKeySet, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
return o.repo.GetKeySet(ctx)
}

View File

@@ -15,6 +15,7 @@ import (
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/errors"
proj_model "github.com/caos/zitadel/internal/project/model"
"github.com/caos/zitadel/internal/tracing"
user_model "github.com/caos/zitadel/internal/user/model"
grant_model "github.com/caos/zitadel/internal/usergrant/model"
)
@@ -32,7 +33,9 @@ const (
oidcCtx = "oidc"
)
func (o *OPStorage) GetClientByClientID(ctx context.Context, id string) (op.Client, error) {
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)
if err != nil {
return nil, err
@@ -51,7 +54,9 @@ func (o *OPStorage) GetClientByClientID(ctx context.Context, id string) (op.Clie
return ClientFromBusiness(client, o.defaultLoginURL, o.defaultAccessTokenLifetime, o.defaultIdTokenLifetime, allowedScopes)
}
func (o *OPStorage) GetKeyByIDAndUserID(ctx context.Context, keyID, userID string) (*jose.JSONWebKey, error) {
func (o *OPStorage) GetKeyByIDAndUserID(ctx context.Context, keyID, userID string) (_ *jose.JSONWebKey, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
key, err := o.repo.MachineKeyByID(ctx, keyID)
if err != nil {
return nil, err
@@ -70,7 +75,9 @@ func (o *OPStorage) GetKeyByIDAndUserID(ctx context.Context, keyID, userID strin
}, nil
}
func (o *OPStorage) AuthorizeClientIDSecret(ctx context.Context, id string, secret string) error {
func (o *OPStorage) AuthorizeClientIDSecret(ctx context.Context, id string, secret string) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
ctx = authz.SetCtxData(ctx, authz.CtxData{
UserID: oidcCtx,
OrgID: oidcCtx,
@@ -78,7 +85,9 @@ func (o *OPStorage) AuthorizeClientIDSecret(ctx context.Context, id string, secr
return o.repo.AuthorizeOIDCApplication(ctx, id, secret)
}
func (o *OPStorage) GetUserinfoFromToken(ctx context.Context, tokenID, subject, origin string) (oidc.UserInfo, error) {
func (o *OPStorage) GetUserinfoFromToken(ctx context.Context, tokenID, subject, origin string) (_ oidc.UserInfo, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
token, err := o.repo.TokenByID(ctx, subject, tokenID)
if err != nil {
return nil, errors.ThrowPermissionDenied(nil, "OIDC-Dsfb2", "token is not valid or has expired")
@@ -95,7 +104,9 @@ func (o *OPStorage) GetUserinfoFromToken(ctx context.Context, tokenID, subject,
return o.GetUserinfoFromScopes(ctx, token.UserID, token.ApplicationID, token.Scopes)
}
func (o *OPStorage) GetUserinfoFromScopes(ctx context.Context, userID, applicationID string, scopes []string) (oidc.UserInfo, error) {
func (o *OPStorage) GetUserinfoFromScopes(ctx context.Context, userID, applicationID string, scopes []string) (_ oidc.UserInfo, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
user, err := o.repo.UserByID(ctx, userID)
if err != nil {
return nil, err

View File

@@ -59,6 +59,7 @@ func NewProvider(ctx context.Context, config OPHandlerConfig, repo repository.Re
config.OPConfig,
newStorage(config.StorageConfig, repo),
op.WithHttpInterceptors(
middleware.TraceHandler(),
middleware.NoCacheInterceptor,
cookieHandler,
http_utils.CopyHeadersToContext,