You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by ne...@apache.org on 2018/10/17 19:44:50 UTC
[trafficcontrol] 04/08: TO Go move getting user to func,
for plugin use
This is an automated email from the ASF dual-hosted git repository.
neuman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
commit 9d3361316d5298cdebaabf7902b6ea8b3b3a1121
Author: Robert Butts <ro...@apache.org>
AuthorDate: Tue Sep 25 16:05:45 2018 -0600
TO Go move getting user to func, for plugin use
---
traffic_ops/traffic_ops_golang/api/api.go | 69 ++++++++++++++++--
traffic_ops/traffic_ops_golang/plugin/plugin.go | 7 +-
traffic_ops/traffic_ops_golang/routing.go | 19 +++--
.../traffic_ops_golang/traffic_ops_golang.go | 2 +-
traffic_ops/traffic_ops_golang/wrappers.go | 81 ++--------------------
5 files changed, 87 insertions(+), 91 deletions(-)
diff --git a/traffic_ops/traffic_ops_golang/api/api.go b/traffic_ops/traffic_ops_golang/api/api.go
index 6d3d5d0..d46df5f 100644
--- a/traffic_ops/traffic_ops_golang/api/api.go
+++ b/traffic_ops/traffic_ops_golang/api/api.go
@@ -37,6 +37,7 @@ import (
"github.com/apache/trafficcontrol/lib/go-util"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/auth"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/config"
+ "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tocookie"
"github.com/jmoiron/sqlx"
"github.com/lib/pq"
@@ -264,7 +265,7 @@ type APIInfo struct {
//
// It is encouraged to call APIInfo.Tx.Tx.Commit() manually when all queries are finished, to release database resources early, and also to return an error to the user if the commit failed.
//
-// NewInfo guarantees the returned APIInfo.Tx is nil or valid, even if a returned error is not nil. Hence, it is safe to pass the Tx to HandleErr when this returns errors.
+// NewInfo guarantees the returned APIInfo.Tx is non-nil and APIInfo.Tx.Tx is nil or valid, even if a returned error is not nil. Hence, it is safe to pass the Tx.Tx to HandleErr when this returns errors.
//
// Close() must be called to free resources, and should be called in a defer immediately after NewInfo(), to finish the transaction.
//
@@ -292,29 +293,29 @@ type APIInfo struct {
func NewInfo(r *http.Request, requiredParams []string, intParamNames []string) (*APIInfo, error, error, int) {
db, err := getDB(r.Context())
if err != nil {
- return &APIInfo{}, errors.New("getting db: " + err.Error()), nil, http.StatusInternalServerError
+ return &APIInfo{Tx: &sqlx.Tx{}}, errors.New("getting db: " + err.Error()), nil, http.StatusInternalServerError
}
cfg, err := getConfig(r.Context())
if err != nil {
- return &APIInfo{}, errors.New("getting config: " + err.Error()), nil, http.StatusInternalServerError
+ return &APIInfo{Tx: &sqlx.Tx{}}, errors.New("getting config: " + err.Error()), nil, http.StatusInternalServerError
}
reqID, err := getReqID(r.Context())
if err != nil {
- return &APIInfo{}, errors.New("getting reqID: " + err.Error()), nil, http.StatusInternalServerError
+ return &APIInfo{Tx: &sqlx.Tx{}}, errors.New("getting reqID: " + err.Error()), nil, http.StatusInternalServerError
}
user, err := auth.GetCurrentUser(r.Context())
if err != nil {
- return &APIInfo{}, errors.New("getting user: " + err.Error()), nil, http.StatusInternalServerError
+ return &APIInfo{Tx: &sqlx.Tx{}}, errors.New("getting user: " + err.Error()), nil, http.StatusInternalServerError
}
params, intParams, userErr, sysErr, errCode := AllParams(r, requiredParams, intParamNames)
if userErr != nil || sysErr != nil {
- return &APIInfo{}, userErr, sysErr, errCode
+ return &APIInfo{Tx: &sqlx.Tx{}}, userErr, sysErr, errCode
}
dbCtx, _ := context.WithTimeout(r.Context(), time.Duration(cfg.DBQueryTimeoutSeconds)*time.Second) //only place we could call cancel here is in APIInfo.Close(), which already will rollback the transaction (which is all cancel will do.)
tx, err := db.BeginTxx(dbCtx, nil) // must be last, MUST not return an error if this succeeds, without closing the tx
if err != nil {
- return &APIInfo{}, userErr, errors.New("could not begin transaction: " + err.Error()), http.StatusInternalServerError
+ return &APIInfo{Tx: &sqlx.Tx{}}, userErr, errors.New("could not begin transaction: " + err.Error()), http.StatusInternalServerError
}
return &APIInfo{
Config: cfg,
@@ -479,3 +480,57 @@ func ParseDBError(ierr error) (error, error, int) {
return nil, err, http.StatusInternalServerError
}
+
+// GetUserFromReq returns the current user, any user error, any system error, and an error code to be returned if either error was not nil.
+// This also uses the given ResponseWriter to refresh the cookie, if it was valid.
+func GetUserFromReq(w http.ResponseWriter, r *http.Request, secret string) (auth.CurrentUser, error, error, int) {
+ cookie, err := r.Cookie(tocookie.Name)
+ if err != nil {
+ return auth.CurrentUser{}, errors.New("Unauthorized, please log in."), errors.New("error getting cookie: " + err.Error()), http.StatusUnauthorized
+ }
+
+ if cookie == nil {
+ return auth.CurrentUser{}, errors.New("Unauthorized, please log in."), nil, http.StatusUnauthorized
+ }
+
+ oldCookie, err := tocookie.Parse(secret, cookie.Value)
+ if err != nil {
+ return auth.CurrentUser{}, errors.New("Unauthorized, please log in."), errors.New("error parsing cookie: " + err.Error()), http.StatusUnauthorized
+ }
+
+ username := oldCookie.AuthData
+ if username == "" {
+ return auth.CurrentUser{}, errors.New("Unauthorized, please log in."), nil, http.StatusUnauthorized
+ }
+ db := (*sqlx.DB)(nil)
+ val := r.Context().Value(DBContextKey)
+ if val == nil {
+ return auth.CurrentUser{}, nil, errors.New("request context db missing"), http.StatusInternalServerError
+ }
+ switch v := val.(type) {
+ case *sqlx.DB:
+ db = v
+ default:
+ return auth.CurrentUser{}, nil, fmt.Errorf("request context db unknown type %T", val), http.StatusInternalServerError
+ }
+
+ cfg, err := GetConfig(r.Context())
+ if err != nil {
+ return auth.CurrentUser{}, nil, errors.New("request context config missing"), http.StatusInternalServerError
+ }
+
+ user, userErr, sysErr, code := auth.GetCurrentUserFromDB(db, username, time.Duration(cfg.DBQueryTimeoutSeconds)*time.Second)
+ if userErr != nil || sysErr != nil {
+ return auth.CurrentUser{}, userErr, sysErr, code
+ }
+
+ newCookieVal := tocookie.Refresh(oldCookie, secret)
+ http.SetCookie(w, &http.Cookie{Name: tocookie.Name, Value: newCookieVal, Path: "/", HttpOnly: true})
+ return user, nil, nil, http.StatusOK
+}
+
+func AddUserToReq(r *http.Request, u auth.CurrentUser) *http.Request {
+ ctx := r.Context()
+ ctx = context.WithValue(ctx, auth.CurrentUserKey, u)
+ return r.WithContext(ctx)
+}
diff --git a/traffic_ops/traffic_ops_golang/plugin/plugin.go b/traffic_ops/traffic_ops_golang/plugin/plugin.go
index 2c2ab79..998303f 100644
--- a/traffic_ops/traffic_ops_golang/plugin/plugin.go
+++ b/traffic_ops/traffic_ops_golang/plugin/plugin.go
@@ -97,7 +97,9 @@ func AddPlugin(priority uint64, funcs Funcs) {
fmt.Println(time.Now().Format(time.RFC3339Nano) + " Error plugin.AddPlugin: runtime.Caller failed, can't get plugin names") // print, because this is called in init, loggers don't exist yet
os.Exit(1)
}
+
pluginName := strings.TrimSuffix(path.Base(filename), ".go")
+ log.Debugln("AddPlugin adding " + pluginName)
initPlugins = append(initPlugins, pluginObj{funcs: funcs, priority: priority, name: pluginName})
}
@@ -113,11 +115,11 @@ type Data struct {
Ctx *interface{}
SharedCfg map[string]interface{}
RequestID uint64
+ AppCfg config.Config
}
type StartupData struct {
Data
- AppCfg config.Config
}
type OnRequestData struct {
@@ -173,12 +175,15 @@ func (ps plugins) OnStartup(d StartupData) {
// OnRequest returns a boolean whether to immediately stop processing the request. If a plugin returns true, this is immediately returned with no further plugins processed.
func (ps plugins) OnRequest(d OnRequestData) bool {
+ log.Debugln("DEBUG plugins.OnRequest calling %+v\n", len(ps.slice))
for _, p := range ps.slice {
if p.funcs.onRequest == nil {
+ log.Debugln("plugins.OnRequest plugging " + p.name + " - no onRequest func")
continue
}
d.Ctx = ps.ctx[p.name]
d.Cfg = ps.cfg[p.name]
+ log.Debugln("plugins.OnRequest plugging " + p.name)
if stop := p.funcs.onRequest(d); stop {
return true
}
diff --git a/traffic_ops/traffic_ops_golang/routing.go b/traffic_ops/traffic_ops_golang/routing.go
index 8a19861..a6a75ce 100644
--- a/traffic_ops/traffic_ops_golang/routing.go
+++ b/traffic_ops/traffic_ops_golang/routing.go
@@ -191,7 +191,16 @@ func Handler(
log.Infoln(r.Method + " " + r.URL.Path + " handled (reqid " + reqIDStr + ") in " + time.Since(start).String())
}()
- onReqData := plugin.OnRequestData{Data: plugin.Data{RequestID: reqID}, W: w, R: r}
+ ctx := r.Context()
+ ctx = context.WithValue(ctx, api.DBContextKey, db)
+ ctx = context.WithValue(ctx, api.ConfigContextKey, cfg)
+ ctx = context.WithValue(ctx, api.ReqIDContextKey, reqID)
+
+ // plugins have no pre-parsed path params, but add an empty map so they can use the api helper funcs that require it.
+ pluginCtx := context.WithValue(ctx, api.PathParamsKey, map[string]string{})
+ pluginReq := r.WithContext(pluginCtx)
+
+ onReqData := plugin.OnRequestData{Data: plugin.Data{RequestID: reqID, AppCfg: *cfg}, W: w, R: pluginReq}
if handled := plugins.OnRequest(onReqData); handled {
return
}
@@ -213,12 +222,8 @@ func Handler(
params[v] = match[i+1]
}
- ctx := r.Context()
- ctx = context.WithValue(ctx, api.PathParamsKey, params)
- ctx = context.WithValue(ctx, api.DBContextKey, db)
- ctx = context.WithValue(ctx, api.ConfigContextKey, cfg)
- ctx = context.WithValue(ctx, api.ReqIDContextKey, reqID)
- r = r.WithContext(ctx)
+ routeCtx := context.WithValue(ctx, api.PathParamsKey, params)
+ r = r.WithContext(routeCtx)
compiledRoute.Handler(w, r)
return
}
diff --git a/traffic_ops/traffic_ops_golang/traffic_ops_golang.go b/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
index 1941293..3fad34d 100644
--- a/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
+++ b/traffic_ops/traffic_ops_golang/traffic_ops_golang.go
@@ -160,7 +160,7 @@ func main() {
return
}
- plugins.OnStartup(plugin.StartupData{Data: plugin.Data{SharedCfg: cfg.PluginSharedConfig}, AppCfg: cfg})
+ plugins.OnStartup(plugin.StartupData{Data: plugin.Data{SharedCfg: cfg.PluginSharedConfig, AppCfg: cfg}})
log.Infof("Listening on " + cfg.Port)
diff --git a/traffic_ops/traffic_ops_golang/wrappers.go b/traffic_ops/traffic_ops_golang/wrappers.go
index 914f706..6d3b0d9 100644
--- a/traffic_ops/traffic_ops_golang/wrappers.go
+++ b/traffic_ops/traffic_ops_golang/wrappers.go
@@ -22,10 +22,8 @@ package main
import (
"bytes"
"compress/gzip"
- "context"
"crypto/sha512"
"encoding/base64"
- "encoding/json"
"errors"
"fmt"
"net/http"
@@ -38,9 +36,7 @@ import (
tc "github.com/apache/trafficcontrol/lib/go-tc"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/about"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/api"
- "github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/auth"
"github.com/apache/trafficcontrol/traffic_ops/traffic_ops_golang/tocookie"
- "github.com/jmoiron/sqlx"
)
// ServerName - the server identifier
@@ -59,81 +55,16 @@ func (a AuthBase) GetWrapper(privLevelRequired int) Middleware {
}
return func(handlerFunc http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
- // TODO remove, and make username available to wrapLogTime
- iw := &Interceptor{w: w}
- w = iw
- handleErr := func(status int, err error) {
- errBytes, jsonErr := json.Marshal(tc.CreateErrorAlerts(err))
- if jsonErr != nil {
- log.Errorf("failed to marshal error: %s\n", jsonErr)
- w.WriteHeader(http.StatusInternalServerError)
- fmt.Fprintf(w, http.StatusText(http.StatusInternalServerError))
- return
- }
- w.Header().Set(tc.ContentType, tc.ApplicationJson)
- w.WriteHeader(status)
- fmt.Fprintf(w, "%s", errBytes)
- }
-
- cookie, err := r.Cookie(tocookie.Name)
- if err != nil {
- log.Errorf("error getting cookie: %s", err)
- handleErr(http.StatusUnauthorized, errors.New("Unauthorized, please log in."))
- return
- }
-
- if cookie == nil {
- handleErr(http.StatusUnauthorized, errors.New("Unauthorized, please log in."))
- return
- }
-
- oldCookie, err := tocookie.Parse(a.secret, cookie.Value)
- if err != nil {
- log.Errorf("error parsing cookie: %s", err)
- handleErr(http.StatusUnauthorized, errors.New("Unauthorized, please log in."))
- return
- }
-
- username := oldCookie.AuthData
- if username == "" {
- handleErr(http.StatusUnauthorized, errors.New("Unauthorized, please log in."))
- return
- }
- var DB *sqlx.DB
- val := r.Context().Value(api.DBContextKey)
- if val != nil {
- switch v := val.(type) {
- case *sqlx.DB:
- DB = v
- default:
- handleErr(http.StatusInternalServerError, errors.New("No DB found"))
- }
- } else {
- handleErr(http.StatusInternalServerError, errors.New("No DB found"))
- }
-
- cfg, err := api.GetConfig(r.Context())
- if err != nil {
- handleErr(http.StatusInternalServerError, errors.New("No config found"))
- }
-
- currentUserInfo, userErr, sysErr, code := auth.GetCurrentUserFromDB(DB, username, time.Duration(cfg.DBQueryTimeoutSeconds)*time.Second)
+ user, userErr, sysErr, errCode := api.GetUserFromReq(w, r, a.secret)
if userErr != nil || sysErr != nil {
- api.HandleErr(w, r, nil, code, userErr, sysErr)
+ api.HandleErr(w, r, nil, errCode, userErr, sysErr)
return
}
- if currentUserInfo.PrivLevel < privLevelRequired {
- handleErr(http.StatusForbidden, errors.New("Forbidden."))
- return
+ if user.PrivLevel < privLevelRequired {
+ api.HandleErr(w, r, nil, http.StatusForbidden, errors.New("Forbidden."), nil)
}
-
- newCookieVal := tocookie.Refresh(oldCookie, a.secret)
- http.SetCookie(w, &http.Cookie{Name: tocookie.Name, Value: newCookieVal, Path: "/", HttpOnly: true})
-
- ctx := r.Context()
- ctx = context.WithValue(ctx, auth.CurrentUserKey, currentUserInfo)
-
- handlerFunc(w, r.WithContext(ctx))
+ r = api.AddUserToReq(r, user)
+ handlerFunc(w, r)
}
}
}