route/cmd/routed/common.go

150 lines
2.5 KiB
Go

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
}