From bb9f3efdb1b03bc019bb5b8df0e1475dd506f1f5 Mon Sep 17 00:00:00 2001 From: O F Date: Thu, 16 May 2024 12:38:18 -0700 Subject: [PATCH] chore: remove v0-version of appspacerouter and related types - rename domain.V0AppRoute and others - merge AppspaceRouter and V0AppspaceRouter - rename appspacerouter.V0AppRoutes to AppRoutes - tweak ds-dev frontend with changed route hit event field names --- cmd/ds-dev/ds-dev.go | 52 +++--- cmd/ds-dev/routehitservice.go | 24 +-- cmd/ds-dev/userservice.go | 18 +- cmd/ds-host/appops/appgetter.go | 10 +- cmd/ds-host/appspacerouter/appspacerouter.go | 41 +++-- cmd/ds-host/appspacerouter/v0approutes.go | 34 ++-- .../appspacerouter/v0appspacerouter.go | 159 +++++------------ .../appspacerouter/v0appspacerouter_test.go | 106 ++++++------ cmd/ds-host/domain/contextvalues.go | 10 +- cmd/ds-host/domain/domain.go | 74 ++++---- cmd/ds-host/domain/mocks.go | 162 +----------------- cmd/ds-host/ds-host.go | 38 ++-- cmd/ds-host/sandboxproxy/sandboxproxy.go | 2 +- cmd/ds-host/sandboxproxy/sandboxproxy_test.go | 6 +- cmd/ds-host/testmocks/auth.go | 1 + cmd/ds-host/testmocks/auth_mocks.go | 14 ++ frontend-ds-dev/src/components/RouteHits.vue | 22 +-- frontend-ds-dev/src/models/route-hits.ts | 2 +- 18 files changed, 277 insertions(+), 498 deletions(-) diff --git a/cmd/ds-dev/ds-dev.go b/cmd/ds-dev/ds-dev.go index 41c22c84..e57b9fd1 100644 --- a/cmd/ds-dev/ds-dev.go +++ b/cmd/ds-dev/ds-dev.go @@ -163,7 +163,7 @@ func main() { devSandboxRunsModel := &DevSandboxRunsModel{} - v0AppRoutes := &appspacerouter.V0AppRoutes{ + AppRoutes := &appspacerouter.AppRoutes{ AppModel: devAppModel, AppFilesModel: devAppFilesModel, Config: runtimeConfig, @@ -178,7 +178,7 @@ func main() { AppFilesModel: devAppFilesModel, AppLocation2Path: appLocation2Path, AppModel: devAppModel, - V0AppRoutes: v0AppRoutes, + AppRoutes: AppRoutes, AppLogger: appLogger, } appGetter.Init() @@ -319,32 +319,26 @@ func main() { sandboxProxy := &sandboxproxy.SandboxProxy{ SandboxManager: devSandboxManager} - appspaceRouterV0 := &appspacerouter.V0{ + dropserverRoutes := &appspacerouter.DropserverRoutes{ + V0DropServerRoutes: &appspacerouter.V0DropserverRoutes{ + AppspaceModel: devAppspaceModel, + Authenticator: devAuth, + }, + } + + appspaceRouter := &appspacerouter.AppspaceRouter{ + Authenticator: devAuth, + AppModel: devAppModel, + AppspaceModel: devAppspaceModel, + AppspaceStatus: appspaceStatus, + DropserverRoutes: dropserverRoutes, AppspaceUserModel: appspaceUserModel, - V0AppRoutes: v0AppRoutes, + AppRoutes: AppRoutes, SandboxProxy: sandboxProxy, - Authenticator: devAuth, RouteHitEvents: routeHitEvents, Config: runtimeConfig, AppLocation2Path: appLocation2Path, - AppspaceLocation2Path: appspaceLocation2Path} - appspaceRouterV0.Init() - - v0dropserverRoutes := &appspacerouter.V0DropserverRoutes{ - AppspaceModel: devAppspaceModel, - Authenticator: devAuth, - } - dropserverRoutes := &appspacerouter.DropserverRoutes{ - V0DropServerRoutes: v0dropserverRoutes, - } - - appspaceRouter := &appspacerouter.AppspaceRouter{ - Authenticator: devAuth, - AppModel: devAppModel, - AppspaceModel: devAppspaceModel, - AppspaceStatus: appspaceStatus, - V0AppspaceRouter: appspaceRouterV0, - DropserverRoutes: dropserverRoutes, + AppspaceLocation2Path: appspaceLocation2Path, } appspaceRouter.Init() appspaceStatus.AppspaceRouter = appspaceRouter @@ -372,14 +366,14 @@ func main() { AppVersionEvents: appVersionEvents, } userService := &UserService{ - DevAuthenticator: devAuth, - AppspaceUsersModelV0: appspaceUserModel, - Avatars: avatars, - AppspaceFilesEvents: appspaceFilesEvents} + DevAuthenticator: devAuth, + AppspaceUsersModel: appspaceUserModel, + Avatars: avatars, + AppspaceFilesEvents: appspaceFilesEvents} routeHitService := &RouteHitService{ - RouteHitEvents: routeHitEvents, - AppspaceUsersModelV0: appspaceUserModel} + RouteHitEvents: routeHitEvents, + AppspaceUsersModel: appspaceUserModel} migrationJobTwine := &twineservices.MigrationJobService{ AppspaceModel: devAppspaceModel, diff --git a/cmd/ds-dev/routehitservice.go b/cmd/ds-dev/routehitservice.go index efa008f6..5e8ed429 100644 --- a/cmd/ds-dev/routehitservice.go +++ b/cmd/ds-dev/routehitservice.go @@ -16,12 +16,12 @@ type RequestJSON struct { Method string `json:"method"` } type RouteHitEventJSON struct { - Timestamp time.Time `json:"timestamp"` - Request RequestJSON `json:"request"` - V0RouteConfig *domain.V0AppRoute `json:"v0_route_config"` // this might be nil.OK? - User *domain.AppspaceUser `json:"user"` //make nil OK - Authorized bool `json:"authorized"` - Status int `json:"status"` + Timestamp time.Time `json:"timestamp"` + Request RequestJSON `json:"request"` + RouteConfig *domain.AppRoute `json:"route_config"` // this might be nil.OK? + User *domain.AppspaceUser `json:"user"` //make nil OK + Authorized bool `json:"authorized"` + Status int `json:"status"` } // RouteHitService forwards route hit events to provided twine instance @@ -30,7 +30,7 @@ type RouteHitService struct { Subscribe(ch chan<- *domain.AppspaceRouteHitEvent) Unsubscribe(ch chan<- *domain.AppspaceRouteHitEvent) } `checkinject:"required"` - AppspaceUsersModelV0 interface { + AppspaceUsersModel interface { Get(appspaceID domain.AppspaceID, proxyID domain.ProxyID) (domain.AppspaceUser, error) } `checkinject:"required"` } @@ -63,15 +63,15 @@ func (s *RouteHitService) sendRouteEvent(twine *twine.Twine, routeEvent *domain. Request: RequestJSON{ URL: routeEvent.Request.URL.String(), Method: routeEvent.Request.Method}, - V0RouteConfig: routeEvent.V0RouteConfig, - Authorized: routeEvent.Authorized, - Status: routeEvent.Status} + RouteConfig: routeEvent.RouteConfig, + Authorized: routeEvent.Authorized, + Status: routeEvent.Status} if routeEvent.Credentials.ProxyID != "" { - user, err := s.AppspaceUsersModelV0.Get(appspaceID, routeEvent.Credentials.ProxyID) + user, err := s.AppspaceUsersModel.Get(appspaceID, routeEvent.Credentials.ProxyID) if err != nil { // very possible the user is no loger in DB if appspace data was changed externally (re-imported for ex) - fmt.Println("sendRouteEvent s.AppspaceUsersModelV0.Get() Error: " + err.Error()) + fmt.Println("sendRouteEvent s.AppspaceUsersModel.Get() Error: " + err.Error()) } else { send.User = &user } diff --git a/cmd/ds-dev/userservice.go b/cmd/ds-dev/userservice.go index 1feb9f18..6ae36daf 100644 --- a/cmd/ds-dev/userservice.go +++ b/cmd/ds-dev/userservice.go @@ -15,8 +15,8 @@ import ( // UserService is a twine service that sets the desired user params // and keeps the frontend up to date with app's declared permissions type UserService struct { - DevAuthenticator *DevAuthenticator `checkinject:"required"` - AppspaceUsersModelV0 interface { + DevAuthenticator *DevAuthenticator `checkinject:"required"` + AppspaceUsersModel interface { Get(appspaceID domain.AppspaceID, proxyID domain.ProxyID) (domain.AppspaceUser, error) GetAll(appspaceID domain.AppspaceID) ([]domain.AppspaceUser, error) Create(appspaceID domain.AppspaceID, authType string, authID string) (domain.ProxyID, error) @@ -85,7 +85,7 @@ const ( ) func (u *UserService) sendUsers(twine *twine.Twine) { - users, err := u.AppspaceUsersModelV0.GetAll(appspaceID) + users, err := u.AppspaceUsersModel.GetAll(appspaceID) if err != nil { fmt.Println("sendUsers error getting users: " + err.Error()) } @@ -150,7 +150,7 @@ func (u *UserService) handleUserCreateMessage(m twine.ReceivedMessageI) { } u.dummyDropidNum++ - proxyID, err := u.AppspaceUsersModelV0.Create(appspaceID, "dropid", fmt.Sprintf("dropid.dummy.develop/%v", u.dummyDropidNum)) + proxyID, err := u.AppspaceUsersModel.Create(appspaceID, "dropid", fmt.Sprintf("dropid.dummy.develop/%v", u.dummyDropidNum)) if err != nil { m.SendError(err.Error()) panic(err) @@ -169,7 +169,7 @@ func (u *UserService) handleUserCreateMessage(m twine.ReceivedMessageI) { } } - err = u.AppspaceUsersModelV0.UpdateMeta(appspaceID, proxyID, incomingUser.DisplayName, avatar, incomingUser.Permissions) + err = u.AppspaceUsersModel.UpdateMeta(appspaceID, proxyID, incomingUser.DisplayName, avatar, incomingUser.Permissions) if err != nil { m.SendError(err.Error()) panic(err) @@ -192,7 +192,7 @@ func (u *UserService) handleUserUpdateMessage(m twine.ReceivedMessageI) { } avatar := "" - user, err := u.AppspaceUsersModelV0.Get(appspaceID, incomingUser.ProxyID) + user, err := u.AppspaceUsersModel.Get(appspaceID, incomingUser.ProxyID) if err != nil { m.SendError(err.Error()) panic(err) @@ -220,7 +220,7 @@ func (u *UserService) handleUserUpdateMessage(m twine.ReceivedMessageI) { } } - err = u.AppspaceUsersModelV0.UpdateMeta(appspaceID, incomingUser.ProxyID, incomingUser.DisplayName, avatar, incomingUser.Permissions) + err = u.AppspaceUsersModel.UpdateMeta(appspaceID, incomingUser.ProxyID, incomingUser.DisplayName, avatar, incomingUser.Permissions) if err != nil { m.SendError(err.Error()) panic(err) @@ -237,7 +237,7 @@ func (u *UserService) handleUserUpdateMessage(m twine.ReceivedMessageI) { func (u *UserService) handleUserDeleteMessage(m twine.ReceivedMessageI) { proxyID := domain.ProxyID(string(m.Payload())) - user, err := u.AppspaceUsersModelV0.Get(appspaceID, proxyID) + user, err := u.AppspaceUsersModel.Get(appspaceID, proxyID) if err != nil { m.SendError(err.Error()) panic(err) @@ -251,7 +251,7 @@ func (u *UserService) handleUserDeleteMessage(m twine.ReceivedMessageI) { } } - err = u.AppspaceUsersModelV0.Delete(appspaceID, proxyID) + err = u.AppspaceUsersModel.Delete(appspaceID, proxyID) if err != nil { m.SendError(err.Error()) panic(err) diff --git a/cmd/ds-host/appops/appgetter.go b/cmd/ds-host/appops/appgetter.go index ac51a2fd..981d7c86 100644 --- a/cmd/ds-host/appops/appgetter.go +++ b/cmd/ds-host/appops/appgetter.go @@ -73,8 +73,8 @@ type AppGetter struct { SandboxManager interface { ForApp(appVersion *domain.AppVersion) (domain.SandboxI, error) } `checkinject:"required"` - V0AppRoutes interface { - ValidateRoutes(routes []domain.V0AppRoute) error + AppRoutes interface { + ValidateRoutes(routes []domain.AppRoute) error } `checkinject:"required"` keysMux sync.Mutex @@ -531,7 +531,7 @@ func (g *AppGetter) getDataFromSandbox(keyData appGetData) error { return err } - err = g.V0AppRoutes.ValidateRoutes(routesData) // pass meta, assume err is internal / fatal. + err = g.AppRoutes.ValidateRoutes(routesData) // pass meta, assume err is internal / fatal. if err != nil { return err } @@ -594,7 +594,7 @@ func (g *AppGetter) getMigrations(keyData appGetData, s domain.SandboxI) error { } // Note this is a versioned API -func (g *AppGetter) getRoutes(s domain.SandboxI) ([]domain.V0AppRoute, error) { +func (g *AppGetter) getRoutes(s domain.SandboxI) ([]domain.AppRoute, error) { sent, err := s.SendMessage(domain.SandboxAppService, 11, nil) if err != nil { g.getLogger("getRoutes, s.SendMessage").Error(err) @@ -610,7 +610,7 @@ func (g *AppGetter) getRoutes(s domain.SandboxI) ([]domain.V0AppRoute, error) { // Should also verify that the response is command 11? - var routes []domain.V0AppRoute + var routes []domain.AppRoute err = json.Unmarshal(reply.Payload(), &routes) if err != nil { diff --git a/cmd/ds-host/appspacerouter/appspacerouter.go b/cmd/ds-host/appspacerouter/appspacerouter.go index 8ff3a613..2ee70e42 100644 --- a/cmd/ds-host/appspacerouter/appspacerouter.go +++ b/cmd/ds-host/appspacerouter/appspacerouter.go @@ -18,8 +18,10 @@ import ( // AppspaceRouter handles routes for appspaces. type AppspaceRouter struct { + Config *domain.RuntimeConfig `checkinject:"required"` Authenticator interface { AppspaceUserProxyID(http.Handler) http.Handler + SetForAppspace(http.ResponseWriter, domain.ProxyID, domain.AppspaceID, string) (string, error) } `checkinject:"required"` AppModel interface { GetFromID(domain.AppID) (domain.App, error) @@ -28,13 +30,32 @@ type AppspaceRouter struct { AppspaceModel interface { GetFromDomain(string) (*domain.Appspace, error) } `checkinject:"required"` + AppspaceUserModel interface { + Get(appspaceID domain.AppspaceID, proxyID domain.ProxyID) (domain.AppspaceUser, error) + } `checkinject:"required"` AppspaceStatus interface { Ready(domain.AppspaceID) bool } `checkinject:"required"` + V0TokenManager interface { + CheckToken(appspaceID domain.AppspaceID, token string) (domain.V0AppspaceLoginToken, bool) + } `checkinject:"required"` DropserverRoutes interface { Router() http.Handler } `checkinject:"required"` - V0AppspaceRouter http.Handler `checkinject:"required"` + AppRoutes interface { + Match(appID domain.AppID, version domain.Version, method string, reqPath string) (domain.AppRoute, error) + } `checkinject:"required"` + SandboxProxy http.Handler `checkinject:"required"` // versioned? + RouteHitEvents interface { + Send(*domain.AppspaceRouteHitEvent) + } `checkinject:"optional"` + AppLocation2Path interface { + Files(string) string + } `checkinject:"required"` + AppspaceLocation2Path interface { + Files(string) string + Avatars(string) string + } `checkinject:"required"` liveCounterMux sync.Mutex liveCounter map[domain.AppspaceID]int @@ -45,7 +66,7 @@ type AppspaceRouter struct { mux *chi.Mux } -// Init initializes data structures +// Init initializes the router func (a *AppspaceRouter) Init() { a.liveCounter = make(map[domain.AppspaceID]int) a.subscribers = make(map[domain.AppspaceID][]chan<- int) @@ -56,14 +77,13 @@ func (a *AppspaceRouter) Init() { mux.Use(a.errorPage) mux.Use(a.appspaceAvailable, a.countRequest) mux.Use(a.loadApp) - // Not sure we need all these middlewares for all routes. - // - dropserver routes like get login token do not need appspace user, and may not care if available or to count request? - // -> actually may need available + count because there may be side-effects to appspace, like recording last used or whatever - // - also does not need app and ap version - // first match dropserver routes mux.Mount("/.dropserver", a.DropserverRoutes.Router()) - mux.Handle("/*", http.HandlerFunc(a.branchToVersionedRouters)) + mux.Route("/", func(r chi.Router) { + r.Use(a.securityHeaders) + r.Use(a.loadRouteConfig, a.routeHit, a.processLoginToken, a.loadAppspaceUser, a.authorizeRoute) + r.Handle("/*", http.HandlerFunc(a.handleRoute)) + }) a.mux = mux } @@ -151,11 +171,6 @@ func (a *AppspaceRouter) loadApp(next http.Handler) http.Handler { }) } -func (a *AppspaceRouter) branchToVersionedRouters(w http.ResponseWriter, r *http.Request) { - // Here eventually we will branch off to different versions of appspace routers. - a.V0AppspaceRouter.ServeHTTP(w, r) -} - // errorPage shows an HTML error page for certian statuses func (a *AppspaceRouter) errorPage(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/cmd/ds-host/appspacerouter/v0approutes.go b/cmd/ds-host/appspacerouter/v0approutes.go index 5d9931e3..b064aa38 100644 --- a/cmd/ds-host/appspacerouter/v0approutes.go +++ b/cmd/ds-host/appspacerouter/v0approutes.go @@ -15,7 +15,7 @@ import ( // type compiledRoute struct { - domain.V0AppRoute + domain.AppRoute match func(string) (*pathToRegexp.MatchResult, error) } @@ -24,7 +24,7 @@ type appVersionKey struct { appVersion domain.Version } -type V0AppRoutes struct { +type AppRoutes struct { AppModel interface { GetVersion(appID domain.AppID, version domain.Version) (domain.AppVersion, error) } `checkinject:"required"` @@ -38,15 +38,15 @@ type V0AppRoutes struct { // also track last usage timestamp so flush unused } -func (r *V0AppRoutes) Init() { +func (r *AppRoutes) Init() { r.appRoutes = make(map[appVersionKey][]compiledRoute) } -func (r *V0AppRoutes) Match(appID domain.AppID, version domain.Version, method string, reqPath string) (domain.V0AppRoute, error) { +func (r *AppRoutes) Match(appID domain.AppID, version domain.Version, method string, reqPath string) (domain.AppRoute, error) { k := appVersionKey{appID, version} routes, err := r.load(k) if err != nil { - return domain.V0AppRoute{}, err + return domain.AppRoute{}, err } method = strings.ToLower(method) @@ -58,16 +58,16 @@ func (r *V0AppRoutes) Match(appID domain.AppID, version domain.Version, method s m, err := route.match(reqPath) if err != nil { r.getLogger("Match").Error(err) - return domain.V0AppRoute{}, err + return domain.AppRoute{}, err } if m != nil { - return route.V0AppRoute, nil + return route.AppRoute, nil } } - return domain.V0AppRoute{}, nil + return domain.AppRoute{}, nil } -func (r *V0AppRoutes) load(k appVersionKey) ([]compiledRoute, error) { +func (r *AppRoutes) load(k appVersionKey) ([]compiledRoute, error) { routes, ok := r.appRoutes[k] if ok { return routes, nil @@ -83,7 +83,7 @@ func (r *V0AppRoutes) load(k appVersionKey) ([]compiledRoute, error) { return []compiledRoute{}, err } - var stored []domain.V0AppRoute + var stored []domain.AppRoute err = json.Unmarshal(routesData, &stored) if err != nil { r.getLogger("load, json.Unmarshal").Error(err) @@ -92,7 +92,7 @@ func (r *V0AppRoutes) load(k appVersionKey) ([]compiledRoute, error) { return r.compile(stored) } -func (r *V0AppRoutes) compile(storedRoutes []domain.V0AppRoute) ([]compiledRoute, error) { +func (r *AppRoutes) compile(storedRoutes []domain.AppRoute) ([]compiledRoute, error) { compiled := make([]compiledRoute, len(storedRoutes)) @@ -109,7 +109,7 @@ func (r *V0AppRoutes) compile(storedRoutes []domain.V0AppRoute) ([]compiledRoute } // ValidateRoutes validates routes passed to it -func (r *V0AppRoutes) ValidateRoutes(routes []domain.V0AppRoute) error { +func (r *AppRoutes) ValidateRoutes(routes []domain.AppRoute) error { if len(routes) == 0 { return errors.New("there should be at least one route") } @@ -123,7 +123,7 @@ func (r *V0AppRoutes) ValidateRoutes(routes []domain.V0AppRoute) error { return nil } -func (r *V0AppRoutes) validateStoredRoute(route domain.V0AppRoute) error { +func (r *AppRoutes) validateStoredRoute(route domain.AppRoute) error { // TODO validate mehod, route type, and Auth! if route.Path.Path == "" { @@ -144,21 +144,21 @@ func (r *V0AppRoutes) validateStoredRoute(route domain.V0AppRoute) error { return nil } -func (r *V0AppRoutes) getMatchFn(route domain.V0AppRoute) (func(string) (*pathToRegexp.MatchResult, error), error) { +func (r *AppRoutes) getMatchFn(route domain.AppRoute) (func(string) (*pathToRegexp.MatchResult, error), error) { options := getOptions(route) matchFn, err := pathToRegexp.Match(route.Path.Path, &options) return matchFn, err } -func (r *V0AppRoutes) getLogger(note string) *record.DsLogger { - l := record.NewDsLogger().AddNote("V0AppRoutes") +func (r *AppRoutes) getLogger(note string) *record.DsLogger { + l := record.NewDsLogger().AddNote("AppRoutes") if note != "" { l.AddNote(note) } return l } -func getOptions(route domain.V0AppRoute) pathToRegexp.Options { +func getOptions(route domain.AppRoute) pathToRegexp.Options { return pathToRegexp.Options{ End: &route.Path.End, } diff --git a/cmd/ds-host/appspacerouter/v0appspacerouter.go b/cmd/ds-host/appspacerouter/v0appspacerouter.go index 28e1051e..38c53da4 100644 --- a/cmd/ds-host/appspacerouter/v0appspacerouter.go +++ b/cmd/ds-host/appspacerouter/v0appspacerouter.go @@ -8,77 +8,13 @@ import ( "path/filepath" "strings" - "github.com/go-chi/chi/v5" "github.com/teleclimber/DropServer/cmd/ds-host/domain" - "github.com/teleclimber/DropServer/cmd/ds-host/record" ) -// route handler for when we know the route is for an app-space. -// Could be proxied to sandbox, or static file, or crud or whatever - -// V0 handles routes for appspaces. -type V0 struct { - AppspaceUserModel interface { - Get(appspaceID domain.AppspaceID, proxyID domain.ProxyID) (domain.AppspaceUser, error) - } `checkinject:"required"` - SandboxProxy http.Handler `checkinject:"required"` // versioned? - V0TokenManager interface { - CheckToken(appspaceID domain.AppspaceID, token string) (domain.V0AppspaceLoginToken, bool) - } `checkinject:"required"` - V0AppRoutes interface { - Match(appID domain.AppID, version domain.Version, method string, reqPath string) (domain.V0AppRoute, error) - } `checkinject:"required"` - Authenticator interface { - // should have ProcessLoginToken instead. Also removes dep on Token Manager - // Exepct all this stuff is versioned. - SetForAppspace(http.ResponseWriter, domain.ProxyID, domain.AppspaceID, string) (string, error) - } `checkinject:"required"` - RouteHitEvents interface { - Send(*domain.AppspaceRouteHitEvent) - } `checkinject:"optional"` - Config *domain.RuntimeConfig `checkinject:"required"` - AppLocation2Path interface { - Files(string) string - } `checkinject:"required"` - AppspaceLocation2Path interface { - Files(string) string - Avatars(string) string - } `checkinject:"required"` - - mux *chi.Mux -} - -func (arV0 *V0) Init() { - // basically it's all middleware - // then a branch on handling via static file serve or sandbox - // - route hit event - // - get route config - // - process login token - // - load user - // - authorize - // - next handler splits on function versus static - - mux := chi.NewRouter() - mux.Use(arV0.securityHeaders) - mux.Use(arV0.loadRouteConfig, arV0.routeHit, arV0.processLoginToken, arV0.loadAppspaceUser, arV0.authorizeRoute) - // Note change route hit such that it's driven independently and references a request id. - // ..each middelware that has new info on request should push that to some route hit data aggregator. - - mux.Handle("/*", http.HandlerFunc(arV0.handleRoute)) - - arV0.mux = mux -} - -func (arV0 *V0) ServeHTTP(w http.ResponseWriter, r *http.Request) { - arV0.mux.ServeHTTP(w, r) -} - // securityHeaders is where we would loosen CORS CSP and other headers for an appspace // if that appspace is set to allow some cross-origin requests -func (arV0 *V0) securityHeaders(next http.Handler) http.Handler { +func (a *AppspaceRouter) securityHeaders(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - //appspace, _ := domain.CtxAppspaceData(r.Context()) - // deny deny deny by default w.Header().Set("Content-Security-Policy", "default-src 'self'") @@ -89,7 +25,7 @@ func (arV0 *V0) securityHeaders(next http.Handler) http.Handler { }) } -func (arV0 *V0) routeHit(next http.Handler) http.Handler { +func (a *AppspaceRouter) routeHit(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { statusW := statusRecorder{w, 0} next.ServeHTTP(&statusW, r) @@ -97,47 +33,47 @@ func (arV0 *V0) routeHit(next http.Handler) http.Handler { ctx := r.Context() appspace, _ := domain.CtxAppspaceData(ctx) proxyID, _ := domain.CtxAppspaceUserProxyID(ctx) - routeConfig, _ := domain.CtxV0RouteConfig(ctx) + routeConfig, _ := domain.CtxRouteConfig(ctx) cred := struct { ProxyID domain.ProxyID }{proxyID} defer func(e domain.AppspaceRouteHitEvent) { - if arV0.RouteHitEvents != nil { - arV0.RouteHitEvents.Send(&e) + if a.RouteHitEvents != nil { + a.RouteHitEvents.Send(&e) } }(domain.AppspaceRouteHitEvent{ - AppspaceID: appspace.AppspaceID, - Request: r, - V0RouteConfig: &routeConfig, - Credentials: cred, - Authorized: statusW.status != http.StatusForbidden, //redundant with status? - Status: statusW.status}) + AppspaceID: appspace.AppspaceID, + Request: r, + RouteConfig: &routeConfig, + Credentials: cred, + Authorized: statusW.status != http.StatusForbidden, //redundant with status? + Status: statusW.status}) }) } -func (arV0 *V0) loadRouteConfig(next http.Handler) http.Handler { +func (a *AppspaceRouter) loadRouteConfig(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() appspace, _ := domain.CtxAppspaceData(ctx) - route, err := arV0.V0AppRoutes.Match(appspace.AppID, appspace.AppVersion, r.Method, r.URL.Path) + route, err := a.AppRoutes.Match(appspace.AppID, appspace.AppVersion, r.Method, r.URL.Path) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } - if route == (domain.V0AppRoute{}) { + if route == (domain.AppRoute{}) { w.WriteHeader(http.StatusNotFound) return } - ctx = domain.CtxWithV0RouteConfig(ctx, route) + ctx = domain.CtxWithRouteConfig(ctx, route) next.ServeHTTP(w, r.WithContext(ctx)) }) } -func (arV0 *V0) processLoginToken(next http.Handler) http.Handler { +func (a *AppspaceRouter) processLoginToken(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { loginTokenValues := r.URL.Query()["dropserver-login-token"] if len(loginTokenValues) == 0 { @@ -152,14 +88,14 @@ func (arV0 *V0) processLoginToken(next http.Handler) http.Handler { ctx := r.Context() appspace, _ := domain.CtxAppspaceData(ctx) - token, ok := arV0.V0TokenManager.CheckToken(appspace.AppspaceID, loginTokenValues[0]) + token, ok := a.V0TokenManager.CheckToken(appspace.AppspaceID, loginTokenValues[0]) if !ok { // no matching token is not an error. It can happen if user reloads the page for ex. next.ServeHTTP(w, r) return } - cookieID, err := arV0.Authenticator.SetForAppspace(w, token.ProxyID, token.AppspaceID, appspace.DomainName) + cookieID, err := a.Authenticator.SetForAppspace(w, token.ProxyID, token.AppspaceID, appspace.DomainName) if err != nil { http.Error(w, "internal server error", http.StatusInternalServerError) return @@ -172,7 +108,7 @@ func (arV0 *V0) processLoginToken(next http.Handler) http.Handler { }) } -func (arV0 *V0) loadAppspaceUser(next http.Handler) http.Handler { +func (a *AppspaceRouter) loadAppspaceUser(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := r.Context() appspace, _ := domain.CtxAppspaceData(ctx) @@ -182,7 +118,7 @@ func (arV0 *V0) loadAppspaceUser(next http.Handler) http.Handler { return } - appspaceUser, err := arV0.AppspaceUserModel.Get(appspace.AppspaceID, proxyID) + appspaceUser, err := a.AppspaceUserModel.Get(appspace.AppspaceID, proxyID) if err != nil { if err == sql.ErrNoRows { next.ServeHTTP(w, r) @@ -197,7 +133,7 @@ func (arV0 *V0) loadAppspaceUser(next http.Handler) http.Handler { }) } -func (arV0 *V0) authorizeRoute(next http.Handler) http.Handler { +func (a *AppspaceRouter) authorizeRoute(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // And we'll have a bunch of attached creds for request: // - userID / contact ID @@ -207,7 +143,7 @@ func (arV0 *V0) authorizeRoute(next http.Handler) http.Handler { // [if no user but api key, repeat for api key (look it up, get permissions, find match)] ctx := r.Context() - routeConfig, _ := domain.CtxV0RouteConfig(ctx) + routeConfig, _ := domain.CtxRouteConfig(ctx) // if it's public route, then always authorized if routeConfig.Auth.Allow == "public" { @@ -239,15 +175,16 @@ func (arV0 *V0) authorizeRoute(next http.Handler) http.Handler { } // ServeHTTP handles http traffic to the appspace -func (arV0 *V0) handleRoute(w http.ResponseWriter, r *http.Request) { - routeConfig, _ := domain.CtxV0RouteConfig(r.Context()) +func (a *AppspaceRouter) handleRoute(w http.ResponseWriter, r *http.Request) { + routeConfig, _ := domain.CtxRouteConfig(r.Context()) switch routeConfig.Type { case "function": - arV0.SandboxProxy.ServeHTTP(w, r) + a.SandboxProxy.ServeHTTP(w, r) case "static": - arV0.serveFile(w, r) + a.serveFile(w, r) default: - arV0.getLogger("ServeHTTP").Log("route type not implemented: " + routeConfig.Type) + appspace, _ := domain.CtxAppspaceData(r.Context()) + a.getLogger(appspace.AppspaceID).Log("route type not implemented: " + routeConfig.Type) http.Error(w, "route type not implemented", http.StatusInternalServerError) } } @@ -259,8 +196,8 @@ func (arV0 *V0) handleRoute(w http.ResponseWriter, r *http.Request) { // - join request wildcard path (if config request is wildcard) // - if this points to file, serve file // - if dir, look for index.html? or ...? -func (arV0 *V0) serveFile(w http.ResponseWriter, r *http.Request) { - p, err := arV0.getConfigPath(r) +func (a *AppspaceRouter) serveFile(w http.ResponseWriter, r *http.Request) { + p, err := a.getConfigPath(r) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return @@ -278,7 +215,7 @@ func (arV0 *V0) serveFile(w http.ResponseWriter, r *http.Request) { } // Otherwise append request wildcard path and stat again - p, err = arV0.joinBaseToRequest(p, r) + p, err = a.joinBaseToRequest(p, r) if err != nil { w.WriteHeader(http.StatusNotFound) return @@ -301,50 +238,52 @@ func (arV0 *V0) serveFile(w http.ResponseWriter, r *http.Request) { // getConfigPath returns the actual path that the route config options intends to serve // From route config + appspace/app locations, determine the path of the desired file on local system -func (arV0 *V0) getConfigPath(r *http.Request) (string, error) { +func (a *AppspaceRouter) getConfigPath(r *http.Request) (string, error) { ctx := r.Context() - routeConfig, _ := domain.CtxV0RouteConfig(ctx) + routeConfig, _ := domain.CtxRouteConfig(ctx) root := "" p := routeConfig.Options.Path if strings.HasPrefix(p, "@appspace/") { appspace, ok := domain.CtxAppspaceData(ctx) if !ok { - panic("v0appspaceRouter getFilePath: expected an appspace") + panic("appspaceRouter getFilePath: expected an appspace") } p = strings.TrimPrefix(p, "@appspace/") - root = arV0.AppspaceLocation2Path.Files(appspace.LocationKey) + root = a.AppspaceLocation2Path.Files(appspace.LocationKey) } else if strings.HasPrefix(p, "@avatars/") { appspace, ok := domain.CtxAppspaceData(ctx) if !ok { - panic("v0appspaceRouter getFilePath: expected an appspace") + panic("appspaceRouter getFilePath: expected an appspace") } p = strings.TrimPrefix(p, "@avatars/") - root = arV0.AppspaceLocation2Path.Avatars(appspace.LocationKey) + root = a.AppspaceLocation2Path.Avatars(appspace.LocationKey) } else if strings.HasPrefix(p, "@app/") { appVersion, ok := domain.CtxAppVersionData(ctx) if !ok { - panic("v0appspaceRouter getFilePath: expected an app version") + panic("appspaceRouter getFilePath: expected an app version") } p = strings.TrimPrefix(p, "@app/") - root = arV0.AppLocation2Path.Files(appVersion.LocationKey) + root = a.AppLocation2Path.Files(appVersion.LocationKey) } else { - arV0.getLogger("getFilePath").Log("Path prefix not recognized: " + p) // This should be logged to appspace log, not general log + appspace, _ := domain.CtxAppspaceData(r.Context()) + a.getLogger(appspace.AppspaceID).Log("getFilePath() Path prefix not recognized: " + p) // This should be logged to appspace log, not general log return "", errors.New("path prefix not recognized") } // p is from app so untrusted. Check it doesn't breach the appspace or app root: configPath := filepath.Join(root, filepath.FromSlash(p)) if !strings.HasPrefix(configPath, root) { - arV0.getLogger("getFilePath").Log("route config path out of bounds: " + root) + appspace, _ := domain.CtxAppspaceData(r.Context()) + a.getLogger(appspace.AppspaceID).Log("getFilePath() route config path out of bounds: " + root) return "", errors.New("route config path out of bounds") } return configPath, nil } -func (arV0 *V0) joinBaseToRequest(basePath string, r *http.Request) (string, error) { +func (a *AppspaceRouter) joinBaseToRequest(basePath string, r *http.Request) (string, error) { ctx := r.Context() - routeConfig, _ := domain.CtxV0RouteConfig(ctx) + routeConfig, _ := domain.CtxRouteConfig(ctx) // Now determine the path of the file requested from the url urlTail := filepath.FromSlash(r.URL.Path) // have to remove the prefix from urltail. @@ -360,14 +299,6 @@ func (arV0 *V0) joinBaseToRequest(basePath string, r *http.Request) (string, err return joinedPath, nil } -func (arV0 *V0) getLogger(note string) *record.DsLogger { - l := record.NewDsLogger().AddNote("V0AppspaceRoutes") - if note != "" { - l.AddNote(note) - } - return l -} - func serveFile(w http.ResponseWriter, r *http.Request, p string) { f, err := os.Open(p) if err != nil { diff --git a/cmd/ds-host/appspacerouter/v0appspacerouter_test.go b/cmd/ds-host/appspacerouter/v0appspacerouter_test.go index 465281fb..0dcdfe85 100644 --- a/cmd/ds-host/appspacerouter/v0appspacerouter_test.go +++ b/cmd/ds-host/appspacerouter/v0appspacerouter_test.go @@ -14,21 +14,21 @@ import ( ) func TestAuthorizePublic(t *testing.T) { - routeConfig := domain.V0AppRoute{ + routeConfig := domain.AppRoute{ Auth: domain.AppspaceRouteAuth{ Allow: "public", }, } - v0 := &V0{} + ar := &AppspaceRouter{} nextCalled := false - handler := v0.authorizeRoute(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handler := ar.authorizeRoute(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { nextCalled = true })) req, _ := http.NewRequest(http.MethodGet, "/", nil) - req = req.WithContext(domain.CtxWithV0RouteConfig(req.Context(), routeConfig)) + req = req.WithContext(domain.CtxWithRouteConfig(req.Context(), routeConfig)) rr := httptest.NewRecorder() @@ -43,21 +43,21 @@ func TestAuthorizePublic(t *testing.T) { } func TestAuthorizeForbidden(t *testing.T) { - routeConfig := domain.V0AppRoute{ + routeConfig := domain.AppRoute{ Auth: domain.AppspaceRouteAuth{ Allow: "authorized", }, } - v0 := &V0{} + ar := &AppspaceRouter{} nextCalled := false - handler := v0.authorizeRoute(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handler := ar.authorizeRoute(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { nextCalled = true })) req, _ := http.NewRequest(http.MethodGet, "/", nil) - req = req.WithContext(domain.CtxWithV0RouteConfig(req.Context(), routeConfig)) + req = req.WithContext(domain.CtxWithRouteConfig(req.Context(), routeConfig)) rr := httptest.NewRecorder() @@ -72,7 +72,7 @@ func TestAuthorizeForbidden(t *testing.T) { } func TestAuthorizedUser(t *testing.T) { - routeConfig := domain.V0AppRoute{ + routeConfig := domain.AppRoute{ Auth: domain.AppspaceRouteAuth{ Allow: "authorized", }, @@ -80,15 +80,15 @@ func TestAuthorizedUser(t *testing.T) { user := domain.AppspaceUser{} - v0 := &V0{} + ar := &AppspaceRouter{} nextCalled := false - handler := v0.authorizeRoute(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handler := ar.authorizeRoute(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { nextCalled = true })) req, _ := http.NewRequest(http.MethodGet, "/", nil) - ctx := domain.CtxWithV0RouteConfig(req.Context(), routeConfig) + ctx := domain.CtxWithRouteConfig(req.Context(), routeConfig) ctx = domain.CtxWithAppspaceUserData(ctx, user) rr := httptest.NewRecorder() @@ -104,7 +104,7 @@ func TestAuthorizedUser(t *testing.T) { } func TestAuthorizePermissionDenied(t *testing.T) { - routeConfig := domain.V0AppRoute{ + routeConfig := domain.AppRoute{ Auth: domain.AppspaceRouteAuth{ Allow: "authorized", Permission: "delete", @@ -113,15 +113,15 @@ func TestAuthorizePermissionDenied(t *testing.T) { user := domain.AppspaceUser{Permissions: []string{"create", "update"}} - v0 := &V0{} + ar := &AppspaceRouter{} nextCalled := false - handler := v0.authorizeRoute(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handler := ar.authorizeRoute(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { nextCalled = true })) req, _ := http.NewRequest(http.MethodGet, "/", nil) - ctx := domain.CtxWithV0RouteConfig(req.Context(), routeConfig) + ctx := domain.CtxWithRouteConfig(req.Context(), routeConfig) ctx = domain.CtxWithAppspaceUserData(ctx, user) rr := httptest.NewRecorder() @@ -137,7 +137,7 @@ func TestAuthorizePermissionDenied(t *testing.T) { } func TestAuthorizePermissionAllowed(t *testing.T) { - routeConfig := domain.V0AppRoute{ + routeConfig := domain.AppRoute{ Auth: domain.AppspaceRouteAuth{ Allow: "authorized", Permission: "delete", @@ -146,15 +146,15 @@ func TestAuthorizePermissionAllowed(t *testing.T) { user := domain.AppspaceUser{Permissions: []string{"create", "update", "delete"}} - v0 := &V0{} + ar := &AppspaceRouter{} nextCalled := false - handler := v0.authorizeRoute(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handler := ar.authorizeRoute(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { nextCalled = true })) req, _ := http.NewRequest(http.MethodGet, "/", nil) - ctx := domain.CtxWithV0RouteConfig(req.Context(), routeConfig) + ctx := domain.CtxWithRouteConfig(req.Context(), routeConfig) ctx = domain.CtxWithAppspaceUserData(ctx, user) rr := httptest.NewRecorder() @@ -170,10 +170,10 @@ func TestAuthorizePermissionAllowed(t *testing.T) { } func TestLoginTokenNoToken(t *testing.T) { - v0 := &V0{} + ar := &AppspaceRouter{} nextCalled := false - handler := v0.processLoginToken(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handler := ar.processLoginToken(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { nextCalled = true })) @@ -190,10 +190,10 @@ func TestLoginTokenNoToken(t *testing.T) { } func TestLoginTokenTwoTokens(t *testing.T) { - v0 := &V0{} + ar := &AppspaceRouter{} nextCalled := false - handler := v0.processLoginToken(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handler := ar.processLoginToken(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { nextCalled = true })) @@ -218,12 +218,12 @@ func TestLoginTokenNotfound(t *testing.T) { v0TokenManager := testmocks.NewMockV0TokenManager(mockCtrl) v0TokenManager.EXPECT().CheckToken(appspaceID, "abcd").Return(domain.V0AppspaceLoginToken{}, false) - v0 := &V0{ + ar := &AppspaceRouter{ V0TokenManager: v0TokenManager, } nextCalled := false - handler := v0.processLoginToken(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handler := ar.processLoginToken(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { nextCalled = true })) @@ -254,13 +254,13 @@ func TestLoginToken(t *testing.T) { authenticator := testmocks.NewMockAuthenticator(mockCtrl) authenticator.EXPECT().SetForAppspace(gomock.Any(), proxyID, appspaceID, domainName).Return("cid", nil) - v0 := &V0{ + ar := &AppspaceRouter{ V0TokenManager: v0TokenManager, Authenticator: authenticator, } nextCalled := false - handler := v0.processLoginToken(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + handler := ar.processLoginToken(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { reqProxyID, ok := domain.CtxAppspaceUserProxyID(r.Context()) if !ok { t.Error("no proxy id set") @@ -297,7 +297,7 @@ func TestGetConfigPath(t *testing.T) { appVersion := domain.AppVersion{ LocationKey: "app-version-123", } - v0 := &V0{ + ar := &AppspaceRouter{ AppLocation2Path: &l2p{appFiles: "/data-dir/apps-path"}, } @@ -323,16 +323,16 @@ func TestGetConfigPath(t *testing.T) { for _, c := range cases { t.Run(c.configP+" -> "+c.expP, func(t *testing.T) { - routeConfig := domain.V0AppRoute{ - Options: domain.V0AppRouteOptions{ + routeConfig := domain.AppRoute{ + Options: domain.AppRouteOptions{ Path: c.configP, }} req, _ := http.NewRequest(http.MethodGet, "/", nil) ctx := domain.CtxWithAppVersionData(req.Context(), appVersion) - ctx = domain.CtxWithV0RouteConfig(ctx, routeConfig) + ctx = domain.CtxWithRouteConfig(ctx, routeConfig) - p, err := v0.getConfigPath(req.WithContext(ctx)) + p, err := ar.getConfigPath(req.WithContext(ctx)) if err == nil && c.err { t.Error("Expected error, got nil") } @@ -347,27 +347,27 @@ func TestGetConfigPath(t *testing.T) { } func TestJoinBaseToRequest(t *testing.T) { - v0 := &V0{} + ar := &AppspaceRouter{} basePath := "/base/path/" cases := []struct { - routeP domain.V0AppRoutePath + routeP domain.AppRoutePath reqP string expP string err bool }{ { - domain.V0AppRoutePath{Path: "/static/", End: false}, + domain.AppRoutePath{Path: "/static/", End: false}, "/static/style/app.css", "/base/path/style/app.css", false}, { - domain.V0AppRoutePath{Path: "/../../", End: false}, // An attempt to use config to break out of /base/path/ + domain.AppRoutePath{Path: "/../../", End: false}, // An attempt to use config to break out of /base/path/ "/not-secrets.txt", "/base/path/not-secrets.txt", false}, { - domain.V0AppRoutePath{Path: "/static/", End: false}, // using request path to break out of /base/path + domain.AppRoutePath{Path: "/static/", End: false}, // using request path to break out of /base/path "/static/../../secrets.txt", "", true}, @@ -375,13 +375,13 @@ func TestJoinBaseToRequest(t *testing.T) { for _, c := range cases { t.Run(c.reqP, func(t *testing.T) { - routeConfig := domain.V0AppRoute{ + routeConfig := domain.AppRoute{ Path: c.routeP} req, _ := http.NewRequest(http.MethodGet, c.reqP, nil) - ctx := domain.CtxWithV0RouteConfig(req.Context(), routeConfig) + ctx := domain.CtxWithRouteConfig(req.Context(), routeConfig) - p, err := v0.joinBaseToRequest(basePath, req.WithContext(ctx)) + p, err := ar.joinBaseToRequest(basePath, req.WithContext(ctx)) if err == nil && c.err { t.Error("Expected error, got nil") } @@ -406,14 +406,14 @@ func TestServeFile(t *testing.T) { appVersion := domain.AppVersion{ LocationKey: "app-version-123", } - routeConfig := domain.V0AppRoute{ - Path: domain.V0AppRoutePath{Path: "/some-files", End: true}, + routeConfig := domain.AppRoute{ + Path: domain.AppRoutePath{Path: "/some-files", End: true}, Type: "static", - Options: domain.V0AppRouteOptions{ + Options: domain.AppRouteOptions{ Path: "@app/static-files/", }} - v0 := &V0{ + ar := &AppspaceRouter{ AppLocation2Path: &l2p{appFiles: dir}, } @@ -427,10 +427,10 @@ func TestServeFile(t *testing.T) { req, _ := http.NewRequest("GET", "/some-files/css/style.css", nil) ctx := domain.CtxWithAppVersionData(req.Context(), appVersion) - ctx = domain.CtxWithV0RouteConfig(ctx, routeConfig) + ctx = domain.CtxWithRouteConfig(ctx, routeConfig) rr := httptest.NewRecorder() - v0.serveFile(rr, req.WithContext(ctx)) + ar.serveFile(rr, req.WithContext(ctx)) respString := rr.Body.String() if respString != string(fileData) { @@ -451,15 +451,15 @@ func TestServeFileOverlapPath(t *testing.T) { appVersion := domain.AppVersion{ LocationKey: "app-version-123", } - routeConfig := domain.V0AppRoute{ - Path: domain.V0AppRoutePath{Path: "/", End: true}, + routeConfig := domain.AppRoute{ + Path: domain.AppRoutePath{Path: "/", End: true}, Type: "static", - Options: domain.V0AppRouteOptions{ + Options: domain.AppRouteOptions{ Path: "@app/static-files/index.html", }, } - v0 := &V0{ + ar := &AppspaceRouter{ AppLocation2Path: &l2p{appFiles: dir}, //Config: config, } @@ -474,10 +474,10 @@ func TestServeFileOverlapPath(t *testing.T) { req, _ := http.NewRequest("GET", "/favicon.ico", nil) ctx := domain.CtxWithAppVersionData(req.Context(), appVersion) - ctx = domain.CtxWithV0RouteConfig(ctx, routeConfig) + ctx = domain.CtxWithRouteConfig(ctx, routeConfig) rr := httptest.NewRecorder() - v0.serveFile(rr, req.WithContext(ctx)) + ar.serveFile(rr, req.WithContext(ctx)) if rr.Result().StatusCode != http.StatusNotFound { t.Error("expected 404") diff --git a/cmd/ds-host/domain/contextvalues.go b/cmd/ds-host/domain/contextvalues.go index 7c2781ab..3e7ca650 100644 --- a/cmd/ds-host/domain/contextvalues.go +++ b/cmd/ds-host/domain/contextvalues.go @@ -145,15 +145,15 @@ func CtxAppspaceUserData(ctx context.Context) (AppspaceUser, bool) { } // App Route Config Data -const v0routeConfigDataCtxKey = ctxKey("V0 appspace route config user data") +const routeConfigDataCtxKey = ctxKey("appspace route config user data") // CtxWithRouteConfig sets the appspace route data for the request -func CtxWithV0RouteConfig(ctx context.Context, routeConfig V0AppRoute) context.Context { - return context.WithValue(ctx, v0routeConfigDataCtxKey, routeConfig) +func CtxWithRouteConfig(ctx context.Context, routeConfig AppRoute) context.Context { + return context.WithValue(ctx, routeConfigDataCtxKey, routeConfig) } // CtxRouteConfig gets the appspace route config data for the request -func CtxV0RouteConfig(ctx context.Context) (V0AppRoute, bool) { - t, ok := ctx.Value(v0routeConfigDataCtxKey).(V0AppRoute) +func CtxRouteConfig(ctx context.Context) (AppRoute, bool) { + t, ok := ctx.Value(routeConfigDataCtxKey).(AppRoute) return t, ok } diff --git a/cmd/ds-host/domain/domain.go b/cmd/ds-host/domain/domain.go index aca84248..21479b4e 100644 --- a/cmd/ds-host/domain/domain.go +++ b/cmd/ds-host/domain/domain.go @@ -1,6 +1,6 @@ package domain -//go:generate mockgen -destination=mocks.go -package=domain -self_package=github.com/teleclimber/DropServer/cmd/ds-host/domain github.com/teleclimber/DropServer/cmd/ds-host/domain MetricsI,SandboxI,V0RouteModel,AppspaceRouteModels,StdInput +//go:generate mockgen -destination=mocks.go -package=domain -self_package=github.com/teleclimber/DropServer/cmd/ds-host/domain github.com/teleclimber/DropServer/cmd/ds-host/domain MetricsI,SandboxI,StdInput // ^^ remember to add new interfaces to list of interfaces to mock ^^ import ( @@ -545,15 +545,6 @@ type AppspaceUserPermission struct { Description string `json:"description"` } -// V0AppspaceDBQuery is the structure expected when Posting a DB request -type V0AppspaceDBQuery struct { - DBName string `json:"db_name"` - Type string `json:"type"` // "query" or "exec" - SQL string `json:"sql"` - Params []interface{} `json:"params"` - NamedParams map[string]interface{} `json:"named_params"` -} - // MigrationJobStatus represents the Status of an appspace's migration to a different version // including possibly a different schema type MigrationJobStatus int @@ -622,25 +613,25 @@ type MigrationStep struct { // New appspace route stuff: -// V0AppRoute is route config for appspace as stored with app -type V0AppRoute struct { +// AppRoute is route config for appspace as stored with app +type AppRoute struct { ID string `json:"id"` Method string `json:"method"` - Path V0AppRoutePath `json:"path"` // Path is the request path to match + Path AppRoutePath `json:"path"` // Path is the request path to match Auth AppspaceRouteAuth `json:"auth"` Type string `json:"type"` // Type of handler: "function" or "static" for now - Options V0AppRouteOptions `json:"options"` // Options for the route handler + Options AppRouteOptions `json:"options"` // Options for the route handler } -// V0AppRoutePath is the request path twe are seeking to match -type V0AppRoutePath struct { +// AppRoutePath is the request path twe are seeking to match +type AppRoutePath struct { Path string `json:"path"` End bool `json:"end"` // End of false makes the path a wildcard /path/** } -// V0AppRouteOptions is a JSON friendly struct +// AppRouteOptions is a JSON friendly struct // that describes the desired handling for the route -type V0AppRouteOptions struct { +type AppRouteOptions struct { Name string `json:"name,omitempty"` // this is called "location" downstream. (but why?) Path string `json:"path,omitempty"` } @@ -667,7 +658,6 @@ type Contact struct { // AppspaceUser identifies a user of an appspace // Not sure we want this to have this form? Auth should be its own struct? -// TODO this is AppspaceUserV0 type AppspaceUser struct { AppspaceID AppspaceID `json:"appspace_id"` ProxyID ProxyID `json:"proxy_id"` @@ -681,30 +671,30 @@ type AppspaceUser struct { } // V0RouteModel serves route data queries at version 0 -type V0RouteModel interface { - ReverseServiceI +// type V0RouteModel interface { +// ReverseServiceI - Create(methods []string, url string, auth AppspaceRouteAuth, handler AppspaceRouteHandler) error +// Create(methods []string, url string, auth AppspaceRouteAuth, handler AppspaceRouteHandler) error - // Get returns all routes that - // - match one of the methods passed, and - // - matches the routePath exactly (no interpolation is done to match sub-paths) - Get(methods []string, routePath string) (*[]AppspaceRouteConfig, error) - GetAll() (*[]AppspaceRouteConfig, error) - GetPath(string) (*[]AppspaceRouteConfig, error) +// // Get returns all routes that +// // - match one of the methods passed, and +// // - matches the routePath exactly (no interpolation is done to match sub-paths) +// Get(methods []string, routePath string) (*[]AppspaceRouteConfig, error) +// GetAll() (*[]AppspaceRouteConfig, error) +// GetPath(string) (*[]AppspaceRouteConfig, error) - Delete(methods []string, url string) error +// Delete(methods []string, url string) error - // Match finds the route that should handle the request - // The path will be broken into parts to find the subset path that matches. - // It returns (nil, nil) if no matches found - Match(method string, url string) (*AppspaceRouteConfig, error) -} +// // Match finds the route that should handle the request +// // The path will be broken into parts to find the subset path that matches. +// // It returns (nil, nil) if no matches found +// Match(method string, url string) (*AppspaceRouteConfig, error) +// } -// AppspaceRouteModels returns models of the desired version -type AppspaceRouteModels interface { - GetV0(AppspaceID) V0RouteModel -} +// // AppspaceRouteModels returns models of the desired version +// type AppspaceRouteModels interface { +// GetV0(AppspaceID) V0RouteModel +// } // ReverseServiceI is a common interface for sandbox services type ReverseServiceI interface { @@ -782,10 +772,10 @@ type LogChunk struct { // Is this versioned or not? It would be easier if not. // Or at least have basic data unversioned, and more details versioned? type AppspaceRouteHitEvent struct { - Timestamp time.Time - AppspaceID AppspaceID - Request *http.Request - V0RouteConfig *V0AppRoute // use generic app route, not versioned + Timestamp time.Time + AppspaceID AppspaceID + Request *http.Request + RouteConfig *AppRoute // this needs to be normalized. use generic app route, not versioned // Credentials presented by the requester // zero-values indicate credential not presented Credentials struct { diff --git a/cmd/ds-host/domain/mocks.go b/cmd/ds-host/domain/mocks.go index 38401fb5..5fc31d70 100644 --- a/cmd/ds-host/domain/mocks.go +++ b/cmd/ds-host/domain/mocks.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: github.com/teleclimber/DropServer/cmd/ds-host/domain (interfaces: MetricsI,SandboxI,V0RouteModel,AppspaceRouteModels,StdInput) +// Source: github.com/teleclimber/DropServer/cmd/ds-host/domain (interfaces: MetricsI,SandboxI,StdInput) // Package domain is a generated GoMock package. package domain @@ -273,166 +273,6 @@ func (mr *MockSandboxIMockRecorder) WaitFor(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WaitFor", reflect.TypeOf((*MockSandboxI)(nil).WaitFor), arg0) } -// MockV0RouteModel is a mock of V0RouteModel interface -type MockV0RouteModel struct { - ctrl *gomock.Controller - recorder *MockV0RouteModelMockRecorder -} - -// MockV0RouteModelMockRecorder is the mock recorder for MockV0RouteModel -type MockV0RouteModelMockRecorder struct { - mock *MockV0RouteModel -} - -// NewMockV0RouteModel creates a new mock instance -func NewMockV0RouteModel(ctrl *gomock.Controller) *MockV0RouteModel { - mock := &MockV0RouteModel{ctrl: ctrl} - mock.recorder = &MockV0RouteModelMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockV0RouteModel) EXPECT() *MockV0RouteModelMockRecorder { - return m.recorder -} - -// Create mocks base method -func (m *MockV0RouteModel) Create(arg0 []string, arg1 string, arg2 AppspaceRouteAuth, arg3 AppspaceRouteHandler) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Create", arg0, arg1, arg2, arg3) - ret0, _ := ret[0].(error) - return ret0 -} - -// Create indicates an expected call of Create -func (mr *MockV0RouteModelMockRecorder) Create(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockV0RouteModel)(nil).Create), arg0, arg1, arg2, arg3) -} - -// Delete mocks base method -func (m *MockV0RouteModel) Delete(arg0 []string, arg1 string) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Delete", arg0, arg1) - ret0, _ := ret[0].(error) - return ret0 -} - -// Delete indicates an expected call of Delete -func (mr *MockV0RouteModelMockRecorder) Delete(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockV0RouteModel)(nil).Delete), arg0, arg1) -} - -// Get mocks base method -func (m *MockV0RouteModel) Get(arg0 []string, arg1 string) (*[]AppspaceRouteConfig, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Get", arg0, arg1) - ret0, _ := ret[0].(*[]AppspaceRouteConfig) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Get indicates an expected call of Get -func (mr *MockV0RouteModelMockRecorder) Get(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockV0RouteModel)(nil).Get), arg0, arg1) -} - -// GetAll mocks base method -func (m *MockV0RouteModel) GetAll() (*[]AppspaceRouteConfig, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAll") - ret0, _ := ret[0].(*[]AppspaceRouteConfig) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetAll indicates an expected call of GetAll -func (mr *MockV0RouteModelMockRecorder) GetAll() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAll", reflect.TypeOf((*MockV0RouteModel)(nil).GetAll)) -} - -// GetPath mocks base method -func (m *MockV0RouteModel) GetPath(arg0 string) (*[]AppspaceRouteConfig, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPath", arg0) - ret0, _ := ret[0].(*[]AppspaceRouteConfig) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetPath indicates an expected call of GetPath -func (mr *MockV0RouteModelMockRecorder) GetPath(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPath", reflect.TypeOf((*MockV0RouteModel)(nil).GetPath), arg0) -} - -// HandleMessage mocks base method -func (m *MockV0RouteModel) HandleMessage(arg0 twine.ReceivedMessageI) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "HandleMessage", arg0) -} - -// HandleMessage indicates an expected call of HandleMessage -func (mr *MockV0RouteModelMockRecorder) HandleMessage(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HandleMessage", reflect.TypeOf((*MockV0RouteModel)(nil).HandleMessage), arg0) -} - -// Match mocks base method -func (m *MockV0RouteModel) Match(arg0, arg1 string) (*AppspaceRouteConfig, error) { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Match", arg0, arg1) - ret0, _ := ret[0].(*AppspaceRouteConfig) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Match indicates an expected call of Match -func (mr *MockV0RouteModelMockRecorder) Match(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Match", reflect.TypeOf((*MockV0RouteModel)(nil).Match), arg0, arg1) -} - -// MockAppspaceRouteModels is a mock of AppspaceRouteModels interface -type MockAppspaceRouteModels struct { - ctrl *gomock.Controller - recorder *MockAppspaceRouteModelsMockRecorder -} - -// MockAppspaceRouteModelsMockRecorder is the mock recorder for MockAppspaceRouteModels -type MockAppspaceRouteModelsMockRecorder struct { - mock *MockAppspaceRouteModels -} - -// NewMockAppspaceRouteModels creates a new mock instance -func NewMockAppspaceRouteModels(ctrl *gomock.Controller) *MockAppspaceRouteModels { - mock := &MockAppspaceRouteModels{ctrl: ctrl} - mock.recorder = &MockAppspaceRouteModelsMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockAppspaceRouteModels) EXPECT() *MockAppspaceRouteModelsMockRecorder { - return m.recorder -} - -// GetV0 mocks base method -func (m *MockAppspaceRouteModels) GetV0(arg0 AppspaceID) V0RouteModel { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetV0", arg0) - ret0, _ := ret[0].(V0RouteModel) - return ret0 -} - -// GetV0 indicates an expected call of GetV0 -func (mr *MockAppspaceRouteModelsMockRecorder) GetV0(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetV0", reflect.TypeOf((*MockAppspaceRouteModels)(nil).GetV0), arg0) -} - // MockStdInput is a mock of StdInput interface type MockStdInput struct { ctrl *gomock.Controller diff --git a/cmd/ds-host/ds-host.go b/cmd/ds-host/ds-host.go index bda9b457..d5d4bc49 100644 --- a/cmd/ds-host/ds-host.go +++ b/cmd/ds-host/ds-host.go @@ -215,12 +215,12 @@ func main() { AppspaceMetaDB: appspaceMetaDb, } - v0AppRoutes := &appspacerouter.V0AppRoutes{ + AppRoutes := &appspacerouter.AppRoutes{ AppModel: appModel, AppFilesModel: appFilesModel, Config: runtimeConfig, } - v0AppRoutes.Init() + AppRoutes.Init() migrationJobModel := &migrationjobmodel.MigrationJobModel{ MigrationJobEvents: migrationJobEvents, @@ -323,7 +323,7 @@ func main() { AppLogger: appLogger, RemoteAppGetter: remoteAppGetter, SandboxManager: sandboxManager, - V0AppRoutes: v0AppRoutes, + AppRoutes: AppRoutes, } appGetter.Init() @@ -533,34 +533,28 @@ func main() { userRoutes.Init() userRoutes.DumpRoutes(*dumpRoutesFlag) - v0dropserverRoutes := &appspacerouter.V0DropserverRoutes{ - AppspaceModel: appspaceModel, - Authenticator: authenticator, - V0RequestToken: v0requestToken, - V0TokenManager: v0tokenManager, - } dropserverRoutes := &appspacerouter.DropserverRoutes{ - V0DropServerRoutes: v0dropserverRoutes, + V0DropServerRoutes: &appspacerouter.V0DropserverRoutes{ + AppspaceModel: appspaceModel, + Authenticator: authenticator, + V0RequestToken: v0requestToken, + V0TokenManager: v0tokenManager, + }, } - v0appspaceRouter := &appspacerouter.V0{ - V0AppRoutes: v0AppRoutes, + appspaceRouter := &appspacerouter.AppspaceRouter{ + Authenticator: authenticator, + AppModel: appModel, + AppspaceModel: appspaceModel, + AppspaceStatus: appspaceStatus, + DropserverRoutes: dropserverRoutes, + AppRoutes: AppRoutes, AppspaceUserModel: appspaceUserModel, SandboxProxy: sandboxProxy, - Authenticator: authenticator, V0TokenManager: v0tokenManager, Config: runtimeConfig, AppLocation2Path: appLocation2Path, AppspaceLocation2Path: appspaceLocation2Path} - v0appspaceRouter.Init() - - appspaceRouter := &appspacerouter.AppspaceRouter{ - Authenticator: authenticator, - AppModel: appModel, - AppspaceModel: appspaceModel, - AppspaceStatus: appspaceStatus, - DropserverRoutes: dropserverRoutes, - V0AppspaceRouter: v0appspaceRouter} appspaceRouter.Init() appspaceStatus.AppspaceRouter = appspaceRouter diff --git a/cmd/ds-host/sandboxproxy/sandboxproxy.go b/cmd/ds-host/sandboxproxy/sandboxproxy.go index e56c159c..a5013c3c 100644 --- a/cmd/ds-host/sandboxproxy/sandboxproxy.go +++ b/cmd/ds-host/sandboxproxy/sandboxproxy.go @@ -37,7 +37,7 @@ func (s *SandboxProxy) ServeHTTP(oRes http.ResponseWriter, oReq *http.Request) { sbTransport := sb.GetTransport() - routeConfig, _ := domain.CtxV0RouteConfig(ctx) + routeConfig, _ := domain.CtxRouteConfig(ctx) header := oReq.Header.Clone() header.Set("X-Dropserver-Request-URL", getURLString(*oReq.URL)) diff --git a/cmd/ds-host/sandboxproxy/sandboxproxy_test.go b/cmd/ds-host/sandboxproxy/sandboxproxy_test.go index 7d6c026d..73d372ac 100644 --- a/cmd/ds-host/sandboxproxy/sandboxproxy_test.go +++ b/cmd/ds-host/sandboxproxy/sandboxproxy_test.go @@ -69,7 +69,7 @@ type testMocks struct { sandboxServer *http.Server appVersion domain.AppVersion appspace domain.Appspace - routeConfig domain.V0AppRoute + routeConfig domain.AppRoute } func TestSandboxBadStart(t *testing.T) { @@ -127,7 +127,7 @@ func TestServeHTTP200(t *testing.T) { if err != nil { t.Fatal(err) } - req = req.WithContext(domain.CtxWithV0RouteConfig(req.Context(), tm.routeConfig)) + req = req.WithContext(domain.CtxWithRouteConfig(req.Context(), tm.routeConfig)) // We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response. rr := httptest.NewRecorder() @@ -268,7 +268,7 @@ func createMocks(mockCtrl *gomock.Controller, sbHandler func(http.ResponseWriter sandboxServer: &server, appVersion: domain.AppVersion{}, appspace: domain.Appspace{DomainName: "as1.ds.dev"}, - routeConfig: domain.V0AppRoute{ + routeConfig: domain.AppRoute{ ID: "test-route-id", Type: "function", }} diff --git a/cmd/ds-host/testmocks/auth.go b/cmd/ds-host/testmocks/auth.go index f23e3d16..793ba62a 100644 --- a/cmd/ds-host/testmocks/auth.go +++ b/cmd/ds-host/testmocks/auth.go @@ -15,6 +15,7 @@ type Authenticator interface { SetForAccount(http.ResponseWriter, domain.UserID) error SetForAppspace(http.ResponseWriter, domain.ProxyID, domain.AppspaceID, string) (string, error) Unset(http.ResponseWriter, *http.Request) + AppspaceUserProxyID(http.Handler) http.Handler } // V0TokenManager tracks and returns appspace login tokens diff --git a/cmd/ds-host/testmocks/auth_mocks.go b/cmd/ds-host/testmocks/auth_mocks.go index b2f9de9a..5d91cca6 100644 --- a/cmd/ds-host/testmocks/auth_mocks.go +++ b/cmd/ds-host/testmocks/auth_mocks.go @@ -35,6 +35,20 @@ func (m *MockAuthenticator) EXPECT() *MockAuthenticatorMockRecorder { return m.recorder } +// AppspaceUserProxyID mocks base method +func (m *MockAuthenticator) AppspaceUserProxyID(arg0 http.Handler) http.Handler { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AppspaceUserProxyID", arg0) + ret0, _ := ret[0].(http.Handler) + return ret0 +} + +// AppspaceUserProxyID indicates an expected call of AppspaceUserProxyID +func (mr *MockAuthenticatorMockRecorder) AppspaceUserProxyID(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AppspaceUserProxyID", reflect.TypeOf((*MockAuthenticator)(nil).AppspaceUserProxyID), arg0) +} + // SetForAccount mocks base method func (m *MockAuthenticator) SetForAccount(arg0 http.ResponseWriter, arg1 domain.UserID) error { m.ctrl.T.Helper() diff --git a/frontend-ds-dev/src/components/RouteHits.vue b/frontend-ds-dev/src/components/RouteHits.vue index 180dd45b..9748784f 100644 --- a/frontend-ds-dev/src/components/RouteHits.vue +++ b/frontend-ds-dev/src/components/RouteHits.vue @@ -30,12 +30,12 @@ onUpdated( () => { -