From 6d4b287e88bcb1947ac01a8e49ba657de0e5ffe2 Mon Sep 17 00:00:00 2001 From: mStar aka a person <12024604-mstarongitlab@users.noreply.gitlab.com> Date: Fri, 26 Apr 2024 16:47:13 +0200 Subject: [PATCH] Uhhhh - Added build arg to Dockerfile for logging level - Github action now builds with warning logging level - fixed bugs with version finding - fixed current version being duplicated in plugin overview - some preperations for auth layer - decided to stop with authboss, making my own auth framework now --- .github/workflows/docker-image.yml | 2 + Dockerfile | 2 + go.mod | 1 + go.sum | 2 + main.go | 49 ++++++++++++++----- server/middlewares.go | 62 ++++++++++++++++++------ server/server.go | 47 +++++++++++------- server/util.go | 2 +- storage/plugin.go | 10 +++- storage/pluginVersions.go | 29 ++++++++--- storage/storage.go | 77 +++++++++++++++++++++++++++++- 11 files changed, 228 insertions(+), 55 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 8830f2a..abefd96 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -7,6 +7,7 @@ on: env: REGISTRY: ghcr.io IMAGE_NAME: mstarongithub/mk-plugin-repo + REPO_LOG_LEVEL: warn jobs: build: @@ -52,3 +53,4 @@ jobs: file: Dockerfile tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.output.labels }} + build-args: log_level=${{ env.REPO_LOG_LEVEL }} diff --git a/Dockerfile b/Dockerfile index 92b0977..cef6541 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,6 +29,8 @@ RUN --mount=type=cache,target=/go-cache GOCACHE=/go-cache CGO_ENABLED=1 GOOS=lin # ---- Final slim container FROM gcr.io/distroless/base-debian12 AS release-stage WORKDIR / +ARG log_level +ENV env_log_level $log_level COPY --from=buildstage-go /server /server EXPOSE 8080 CMD [ "/server" ] diff --git a/go.mod b/go.mod index f52ffe0..3d9d272 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( require ( cloud.google.com/go/compute v1.20.1 // indirect github.com/alexedwards/argon2id v1.0.0 // indirect + github.com/justinas/nosurf v1.1.1 // indirect github.com/rs/cors v1.11.0 // indirect ) diff --git a/go.sum b/go.sum index dbab16c..8eb31df 100644 --- a/go.sum +++ b/go.sum @@ -24,6 +24,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/justinas/nosurf v1.1.1 h1:92Aw44hjSK4MxJeMSyDa7jwuI9GR2J/JCQiaKvXXSlk= +github.com/justinas/nosurf v1.1.1/go.mod h1:ALpWdSbuNGy2lZWtyXdjkYv4edL23oSEgfBT1gPJ5BQ= github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/main.go b/main.go index decb651..52d5d6c 100644 --- a/main.go +++ b/main.go @@ -2,11 +2,14 @@ package main import ( "embed" + "flag" "github.com/sirupsen/logrus" _ "github.com/volatiletech/authboss-renderer" + abrenderer "github.com/volatiletech/authboss-renderer" _ "github.com/mstarongithub/mk-plugin-repo/auth-old" + authold "github.com/mstarongithub/mk-plugin-repo/auth-old" "github.com/mstarongithub/mk-plugin-repo/config" "github.com/mstarongithub/mk-plugin-repo/fswrapper" "github.com/mstarongithub/mk-plugin-repo/server" @@ -19,8 +22,14 @@ const DB_DEFAULT_FILE = "db.sqlite" //go:embed frontend/build frontend/build/_app/* var frontendFS embed.FS +var level = flag.String( + "loglevel", + "info", + "Set the log level of the app to one of \"debug\", \"info\", \"warn\", \"error\"", +) + func main() { - logrus.SetLevel(logrus.DebugLevel) + setLogLevelFromArgs() _, err := config.ReadConfig(nil) if err != nil { logrus.WithError(err).Warnln("Err reading config") @@ -35,18 +44,18 @@ func main() { if err != nil { panic(err) } - // ab, err := authold.SetupAuthboss( - // &store, - // []byte("placeholder string"), - // []byte("placeholder string"), - // abrenderer.NewEmail("/auth", "ab_views"), - // ) - // if err != nil { - // panic(err) - // } + ab, err := authold.SetupAuthboss( + &store, + []byte("placeholder string"), + []byte("placeholder string"), + abrenderer.NewEmail("/auth", "ab_views"), + ) + if err != nil { + panic(err) + } httpServer, err := server.NewServer( fswrapper.NewFSWrapper(frontendFS, "frontend/build/", false), - nil, + ab, &store, ) if err != nil { @@ -54,3 +63,21 @@ func main() { } panic(httpServer.Run(":8080")) } + +func setLogLevelFromArgs() { + flag.Parse() + switch *level { + case "debug": + logrus.SetLevel(logrus.DebugLevel) + case "info": + logrus.SetLevel(logrus.InfoLevel) + case "warn": + logrus.SetLevel(logrus.WarnLevel) + case "error": + logrus.SetLevel(logrus.ErrorLevel) + case "fatal": + logrus.SetLevel(logrus.FatalLevel) + default: + logrus.SetLevel(logrus.WarnLevel) + } +} diff --git a/server/middlewares.go b/server/middlewares.go index da983ce..166afb4 100644 --- a/server/middlewares.go +++ b/server/middlewares.go @@ -3,27 +3,59 @@ package server import ( "context" "net/http" -) -type withContextValsMiddleware struct { - pairs map[any]any - nextz http.Handler -} + "github.com/justinas/nosurf" + "github.com/sirupsen/logrus" + "gitlab.com/mstarongitlab/weblogger" +) type HandlerBuilder func(http.Handler) http.Handler -func addContextValsMiddleware(next http.Handler, pairs map[any]any) *withContextValsMiddleware { - return &withContextValsMiddleware{ - pairs: pairs, - nextz: next, +func ChainMiddlewares(base http.Handler, links ...HandlerBuilder) http.Handler { + for _, f := range links { + base = f(base) } + return base } -func (c *withContextValsMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - for key, val := range c.pairs { - ctx = context.WithValue(ctx, key, val) +func ContextValsMiddleware(pairs map[any]any) HandlerBuilder { + return func(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + for key, val := range pairs { + ctx = context.WithValue(ctx, key, val) + } + newRequest := r.WithContext(ctx) + h.ServeHTTP(w, newRequest) + }) } - newRequest := r.WithContext(ctx) - c.nextz.ServeHTTP(w, newRequest) +} + +func WebLoggerWrapper(h http.Handler) http.Handler { + return weblogger.LoggingMiddleware( + h, + &weblogger.Config{ + DefaultLogLevel: weblogger.LOG_LEVEL_DEBUG, + FailedRequestLevel: weblogger.LOG_LEVEL_WARN, + }, + ) +} + +func NosurfCheckWrapper(h http.Handler) http.Handler { + n := nosurf.New(h) + n.SetFailureHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + logrus.WithField("reason", nosurf.Reason(r)).Warnln("Failed to validate CSRF token") + w.WriteHeader(http.StatusBadRequest) + })) + return n +} + +func NosurfTokenInsertMiddleware(h http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + newReq := r.WithContext( + context.WithValue(r.Context(), CONTEXT_KEY_CSRF_TOKEN, nosurf.Token(r)), + ) + w.Header().Set("CSRF-Token", nosurf.Token(r)) + h.ServeHTTP(w, newReq) + }) } diff --git a/server/server.go b/server/server.go index 64007f3..1e69130 100644 --- a/server/server.go +++ b/server/server.go @@ -8,7 +8,9 @@ import ( "github.com/rs/cors" "github.com/sirupsen/logrus" "github.com/volatiletech/authboss/v3" - "gitlab.com/mstarongitlab/weblogger" + _ "github.com/volatiletech/authboss/v3/confirm" + _ "github.com/volatiletech/authboss/v3/lock" + "github.com/volatiletech/authboss/v3/remember" "github.com/mstarongithub/mk-plugin-repo/storage" ) @@ -23,9 +25,10 @@ type Server struct { type ServerContextKey string const ( - CONTEXT_KEY_SERVER = ServerContextKey("server") - CONTEXT_KEY_STORAGE = ServerContextKey("storage") - CONTEXT_KEY_AUTHBOSS = ServerContextKey("authboss") + CONTEXT_KEY_SERVER = ServerContextKey("server") + CONTEXT_KEY_STORAGE = ServerContextKey("storage") + CONTEXT_KEY_AUTHBOSS = ServerContextKey("authboss") + CONTEXT_KEY_CSRF_TOKEN = ServerContextKey("csrf_token") ) func NewServer( @@ -48,22 +51,23 @@ func NewServer( authboss: ab, } - server.handler = cors.AllowAll().Handler( - addContextValsMiddleware( - weblogger.LoggingMiddleware( - mainRouter, - &weblogger.Config{ - DefaultLogLevel: weblogger.LOG_LEVEL_DEBUG, - FailedRequestLevel: weblogger.LOG_LEVEL_WARN, - }, - ), + server.handler = ChainMiddlewares( + mainRouter, + ContextValsMiddleware( map[any]any{ CONTEXT_KEY_SERVER: &server, CONTEXT_KEY_STORAGE: store, CONTEXT_KEY_AUTHBOSS: ab, }, ), + cors.AllowAll().Handler, + ab.LoadClientStateMiddleware, + remember.Middleware(ab), + // NosurfTokenInsertMiddleware, + // NosurfCheckWrapper, + WebLoggerWrapper, ) + return &server, nil } @@ -95,26 +99,26 @@ func (s *Server) Run(addr string) error { } // NOTE: Error return value unused currently and can safely be ignored -func buildApiRouter(_ *authboss.Authboss) (http.Handler, error) { +func buildApiRouter(ab *authboss.Authboss) (http.Handler, error) { router := http.NewServeMux() // router.Handle("/auth", ab.Core.Router) - router.Handle("/v1/", http.StripPrefix("/v1", buildV1Router())) + router.Handle("/v1/", http.StripPrefix("/v1", buildV1Router(ab))) return router, nil } -func buildV1Router() http.Handler { +func buildV1Router(ab *authboss.Authboss) http.Handler { router := http.NewServeMux() router.HandleFunc("GET /plugins", getPluginList) router.HandleFunc("GET /plugins/{pluginId}", getSpecificPlugin) router.HandleFunc("GET /plugins/{pluginId}/{versionName}", getVersion) - router.Handle("/", buildV1RestrictedRouter()) + router.Handle("/", buildV1RestrictedRouter(ab)) return router } -func buildV1RestrictedRouter() http.Handler { +func buildV1RestrictedRouter(ab *authboss.Authboss) http.Handler { router := http.NewServeMux() router.HandleFunc("POST /plugins", addNewPlugin) @@ -123,5 +127,12 @@ func buildV1RestrictedRouter() http.Handler { router.HandleFunc("DELETE /plugins/{pluginId}", deleteSpecificPlugin) router.HandleFunc("DELETE /plugins/[pluginId]/{versionName}", hideVersion) + // handler := ChainMiddlewares( + // router, + // authboss.Middleware2(ab, authboss.RequireNone, authboss.RespondUnauthorized), + // lock.Middleware(ab), + // confirm.Middleware(ab), + // ) + return router } diff --git a/server/util.go b/server/util.go index f56502c..cdce364 100644 --- a/server/util.go +++ b/server/util.go @@ -46,7 +46,7 @@ func dbPluginToApiPlugin(plugin *storage.Plugin) Plugin { SummaryShort: plugin.SummaryShort, SummaryLong: plugin.SummaryLong, CurrentVersion: plugin.CurrentVersion, - AllVersions: append(plugin.PreviousVersions, plugin.CurrentVersion), + AllVersions: plugin.PreviousVersions, Tags: plugin.Tags, AuthorID: plugin.AuthorID, } diff --git a/storage/plugin.go b/storage/plugin.go index e4059fd..4c4ebf4 100644 --- a/storage/plugin.go +++ b/storage/plugin.go @@ -105,8 +105,16 @@ func (storage *Storage) NewPlugin( return nil, ErrAccountNotApproved } + // Check if version already exists + placeholder := Plugin{} + storage.db.First(&placeholder, "name = ?", name) + if placeholder.ID != 0 { + logrus.WithField("plugin", plugin).Debugln("new plugin already exists") + return nil, ErrAlreadyExists + } + logrus.WithField("plugin", plugin).Debugln("Inserting new plugin") - res := storage.db.Debug().Create(&plugin) + res := storage.db.Create(&plugin) if res.RowsAffected == 0 { return nil, fmt.Errorf("rows affected during plugin creation were 0: %w", err) } else if res.Error != nil { diff --git a/storage/pluginVersions.go b/storage/pluginVersions.go index 4e1117d..a87a775 100644 --- a/storage/pluginVersions.go +++ b/storage/pluginVersions.go @@ -56,9 +56,14 @@ func (storage *Storage) TryFindVersion(pluginID uint, versionName string) (*Plug PluginID: pluginID, } // TODO: Add logging - result := storage.db.First(&version) + result := storage.db.Where("version = ?", versionName). + Where("plugin_id = ?", pluginID). + First(&version) if result.RowsAffected < 1 { - // TODO: Add logging + logrus.WithFields(logrus.Fields{ + "pluginID": pluginID, + "versionName": versionName, + }).Debugln("Couldn't find plugin version") return nil, ErrVersionNotFound } if result.Error != nil { @@ -97,9 +102,18 @@ func (storage *Storage) NewVersion( ) error { // First check if a version already exists _, err := storage.TryFindVersion(forPluginID, versionName) - if !errors.Is(err, ErrVersionNotFound) { - // TODO: Add logging + if err == nil { + logrus.WithFields(logrus.Fields{ + "pluginID": forPluginID, + "versionName": versionName, + }).Debugln("Got no error while looking if a new version already exists. Assuming it exists already, aborting") return ErrAlreadyExists + } else if !errors.Is(err, ErrVersionNotFound) { + logrus.WithError(err).WithFields(logrus.Fields{ + "pluginID": forPluginID, + "versionName": versionName, + }).Debugln("Got err that is not ErrVersionNotFound from check if plugin version exists, aborting") + return err } // Then check if there actually is a plugin with the given ID @@ -111,9 +125,10 @@ func (storage *Storage) NewVersion( // Now make the new version, push it to the db newVersion := PluginVersion{ - PluginID: forPluginID, - Version: versionName, - Code: code, + PluginID: forPluginID, + Version: versionName, + Code: code, + AiScriptVersion: aiscript_version, } // TODO: Add logging result := storage.db.Create(&newVersion) diff --git a/storage/storage.go b/storage/storage.go index 887fcb5..996ff35 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "strconv" "time" "github.com/sirupsen/logrus" @@ -14,7 +15,8 @@ import ( ) type Storage struct { - db *gorm.DB + db *gorm.DB + tokens map[uint][]string } var ErrVersionNotFound = errors.New("version not found") @@ -59,15 +61,86 @@ func NewStorage(sqliteFile string, customConfig *gorm.Config) (storage Storage, Confirmed: true, }) storage.db = db + storage.tokens = map[uint][]string{} return storage, nil } +// ----- AUTHBOSS interface stuff + // Authboss ServerStorer interface implementation func (storage *Storage) Load(_ context.Context, key string) (authboss.User, error) { - return nil, fmt.Errorf("unimplemented") // FIX: Implement me! + uid, err := strconv.ParseUint(key, 10, 0) + if err != nil { + return nil, fmt.Errorf("key not a uint: %w", err) + } + user, err := storage.FindAccountByID(uint(uid)) + if err != nil { + return nil, fmt.Errorf("failed to get user: %w", err) + } + return user, nil } // Authboss ServerStorer interface implementation func (storage *Storage) Save(ctx context.Context, user authboss.User) error { return fmt.Errorf("unimplemented") // FIX: Implement me! } + +func (storage *Storage) New(_ context.Context) authboss.User { + return &Account{} +} + +func (storage *Storage) Create(_ context.Context, abUser authboss.User) error { + user, ok := abUser.(*Account) + if !ok { + return errors.New("failed to cast ab user to account") + } + if _, err := storage.FindAccountByID(user.Model.ID); err == nil || + !errors.Is(err, ErrAccountNotFound) { + return authboss.ErrUserFound + } + logrus.WithField("user", user).Infoln("Saving new user") + res := storage.db.Create(user) + if res.Error != nil { + return fmt.Errorf("failed to insert new user: %w", res.Error) + } + + return nil +} + +func (storage *Storage) LoadByConfirmSelector( + _ context.Context, + selector string, +) (user authboss.ConfirmableUser, err error) { + return nil, fmt.Errorf("unimplemented") // FIX: Implement me! +} + +func (storage *Storage) LoadByRecoverSelector( + _ context.Context, + selector string, +) (user authboss.RecoverableUser, err error) { + return nil, fmt.Errorf("unimplemented") // FIX: Implement me! +} + +func (storage *Storage) AddRememberToken(_ context.Context, pid, token string) error { + return fmt.Errorf("unimplemented") // FIX: Implement me! +} + +func (storage *Storage) DelRememberTokens(_ context.Context, pid string) error { + return fmt.Errorf("unimplemented") // FIX: Implement me! +} + +func (storage *Storage) UseRememberToken(_ context.Context, pid, token string) error { + return fmt.Errorf("unimplemented") // FIX: Implement me! +} + +func (storage *Storage) NewFromOAuth2( + _ context.Context, + provider string, + details map[string]string, +) (authboss.OAuth2User, error) { + return nil, fmt.Errorf("unimplemented") // FIX: Implement me! +} + +func (storage *Storage) SaveOAuth2(_ context.Context, user authboss.OAuth2User) error { + return fmt.Errorf("unimplemented") // FIX: Implement me! +}