package main import ( "context" "errors" "net/http" "time" "git.xeserv.us/xena/route/internal/database" "git.xeserv.us/xena/route/internal/middleware" "github.com/Xe/ln" "github.com/twitchtv/twirp" "golang.org/x/net/trace" ) // errors var ( ErrNotAuthorized = errors.New("server: not authorized") ) func (s *Server) makeTwirpHooks() *twirp.ServerHooks { hooks := &twirp.ServerHooks{} hooks.RequestRouted = func(ctx context.Context) (context.Context, error) { ctx = withStartTime(ctx) method, ok := twirp.MethodName(ctx) if !ok { return ctx, nil } pkg, ok := twirp.PackageName(ctx) if !ok { return ctx, nil } svc, ok := twirp.ServiceName(ctx) if !ok { return ctx, nil } ctx = ln.WithF(ctx, ln.F{ "twirp_method": method, "twirp_package": pkg, "twirp_service": svc, }) hdr, ok := middleware.GetHeaders(ctx) if !ok { return ctx, errors.New("can't get request headers") } req, _ := http.NewRequest("GET", "/", nil) req.Header = hdr ck, err := req.Cookie("routed") if err != nil { return ctx, err } tok := ck.Value t, err := s.db.Tokens().Get(ctx, tok) if err != nil { return ctx, err } ctx = withAuthToken(ctx, t) ctx = ln.WithF(ctx, t.F()) return ctx, nil } hooks.ResponseSent = func(ctx context.Context) { f := ln.F{} now := time.Now() t, ok := getStartTime(ctx) if ok { f["response_time"] = now.Sub(t) } ln.Log(ctx, f, ln.Action("response sent")) } hooks.Error = func(ctx context.Context, e twirp.Error) context.Context { f := ln.F{} for k, v := range e.MetaMap() { f["twirp_meta_"+k] = v } ln.Error(ctx, e, f, ln.Action("twirp error"), ln.F{ "twirp_error_code": e.Code(), "twirp_error_msg": e.Msg(), }) tr, ok := trace.FromContext(ctx) if !ok { return ctx } tr.SetError() return ctx } return hooks } func (s *Server) getAuth(ctx context.Context, operation, scope string) (database.Token, error) { t, ok := getAuthToken(ctx) if !ok { return database.Token{}, errors.New("no auth token in context") } ok = false for _, sc := range t.Scopes { // the "admin" scope is implicitly allowed for everything. if sc == "admin" { ok = true break } if sc == scope { ok = true } } if !ok { return database.Token{}, ErrNotAuthorized } ln.WithF(ctx, ln.F{"operation": operation}) return t, nil } func handleError(ctx context.Context, clitok database.Token, err error, f ln.F) error { tr, ok := trace.FromContext(ctx) if !ok { goto skip } tr.SetError() skip: ln.Error(ctx, err, f, clitok) return err }