From 2fb0d145a2908e85cf7ac90e201ac8eba7e45457 Mon Sep 17 00:00:00 2001 From: Steven Date: Tue, 27 Jun 2023 22:15:26 +0800 Subject: [PATCH] feat: add secret session workspace setting --- api/v1/auth.go | 2 +- api/v1/v1.go | 4 ++-- api/v1/workspace.go | 33 +++++++------------------- cmd/shortify/main.go | 2 +- go.mod | 4 +--- go.sum | 13 +++++------ server/server.go | 35 ++++++++++++++++++++++------ store/workspace_setting.go | 24 +++++++++++++++++-- test/store/workspace_setting_test.go | 30 ++++++++++++++++++++++++ 9 files changed, 100 insertions(+), 47 deletions(-) create mode 100644 test/store/workspace_setting_test.go diff --git a/api/v1/auth.go b/api/v1/auth.go index f913aa6..c155db6 100644 --- a/api/v1/auth.go +++ b/api/v1/auth.go @@ -57,7 +57,7 @@ func (s *APIV1Service) registerAuthRoutes(g *echo.Group, secret string) { g.POST("/auth/signup", func(c echo.Context) error { ctx := c.Request().Context() disallowSignUpSetting, err := s.Store.GetWorkspaceSetting(ctx, &store.FindWorkspaceSetting{ - Key: WorkspaceDisallowSignUp.String(), + Key: store.WorkspaceDisallowSignUp, }) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to get workspace setting").SetInternal(err) diff --git a/api/v1/v1.go b/api/v1/v1.go index ec9fcb5..1a36157 100644 --- a/api/v1/v1.go +++ b/api/v1/v1.go @@ -22,7 +22,7 @@ func NewAPIV1Service(profile *profile.Profile, store *store.Store) *APIV1Service func (s *APIV1Service) Start(apiGroup *echo.Group, secret string) { apiV1Group := apiGroup.Group("/api/v1") apiV1Group.Use(func(next echo.HandlerFunc) echo.HandlerFunc { - return JWTMiddleware(s, next, string(secret)) + return JWTMiddleware(s, next, secret) }) s.registerWorkspaceRoutes(apiV1Group) s.registerAuthRoutes(apiV1Group, secret) @@ -31,7 +31,7 @@ func (s *APIV1Service) Start(apiGroup *echo.Group, secret string) { redirectorGroup := apiGroup.Group("/s") redirectorGroup.Use(func(next echo.HandlerFunc) echo.HandlerFunc { - return JWTMiddleware(s, next, string(secret)) + return JWTMiddleware(s, next, secret) }) s.registerRedirectorRoutes(redirectorGroup) } diff --git a/api/v1/workspace.go b/api/v1/workspace.go index 991c36f..563ae70 100644 --- a/api/v1/workspace.go +++ b/api/v1/workspace.go @@ -10,33 +10,18 @@ import ( "github.com/labstack/echo/v4" ) -type WorkspaceSettingKey string - -const ( - // WorkspaceDisallowSignUp is the key type for disallow sign up in workspace level. - WorkspaceDisallowSignUp WorkspaceSettingKey = "disallow-signup" -) - -// String returns the string format of WorkspaceSettingKey type. -func (key WorkspaceSettingKey) String() string { - if key == WorkspaceDisallowSignUp { - return "disallow-signup" - } - return "" -} - type WorkspaceSetting struct { - Key WorkspaceSettingKey `json:"key"` - Value string `json:"value"` + Key string `json:"key"` + Value string `json:"value"` } type WorkspaceSettingUpsert struct { - Key WorkspaceSettingKey `json:"key"` - Value string `json:"value"` + Key string `json:"key"` + Value string `json:"value"` } func (upsert WorkspaceSettingUpsert) Validate() error { - if upsert.Key == WorkspaceDisallowSignUp { + if upsert.Key == store.WorkspaceDisallowSignUp.String() { value := false err := json.Unmarshal([]byte(upsert.Value), &value) if err != nil { @@ -63,7 +48,7 @@ func (s *APIV1Service) registerWorkspaceRoutes(g *echo.Group) { } disallowSignUpSetting, err := s.Store.GetWorkspaceSetting(ctx, &store.FindWorkspaceSetting{ - Key: WorkspaceDisallowSignUp.String(), + Key: store.WorkspaceDisallowSignUp, }) if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, "Failed to get workspace setting") @@ -97,11 +82,11 @@ func (s *APIV1Service) registerWorkspaceRoutes(g *echo.Group) { return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post workspace setting request").SetInternal(err) } if err := upsert.Validate(); err != nil { - return echo.NewHTTPError(http.StatusBadRequest, "system setting invalidate").SetInternal(err) + return echo.NewHTTPError(http.StatusBadRequest, "Invalid system setting key or value").SetInternal(err) } workspaceSetting, err := s.Store.UpsertWorkspaceSetting(ctx, &store.WorkspaceSetting{ - Key: upsert.Key.String(), + Key: store.WorkspaceSettingKey(upsert.Key), Value: upsert.Value, }) if err != nil { @@ -142,7 +127,7 @@ func (s *APIV1Service) registerWorkspaceRoutes(g *echo.Group) { func convertWorkspaceSettingFromStore(workspaceSetting *store.WorkspaceSetting) *WorkspaceSetting { return &WorkspaceSetting{ - Key: WorkspaceSettingKey(workspaceSetting.Key), + Key: workspaceSetting.Key.String(), Value: workspaceSetting.Value, } } diff --git a/cmd/shortify/main.go b/cmd/shortify/main.go index 4946f66..affbb34 100644 --- a/cmd/shortify/main.go +++ b/cmd/shortify/main.go @@ -48,7 +48,7 @@ var ( } storeInstance := store.New(db.DBInstance, profile) - s, err := server.NewServer(profile, storeInstance) + s, err := server.NewServer(ctx, profile, storeInstance) if err != nil { cancel() fmt.Printf("failed to create server, error: %+v\n", err) diff --git a/go.mod b/go.mod index 00de087..1ac853a 100644 --- a/go.mod +++ b/go.mod @@ -18,15 +18,12 @@ require ( ) require ( - github.com/gorilla/context v1.1.1 // indirect github.com/labstack/echo/v4 v4.10.1 github.com/labstack/gommon v0.4.0 // indirect ) require ( github.com/gorilla/securecookie v1.1.1 - github.com/gorilla/sessions v1.2.1 - github.com/labstack/echo-contrib v0.14.0 github.com/spf13/cobra v1.6.1 github.com/spf13/viper v1.15.0 github.com/stretchr/testify v1.8.4 @@ -39,6 +36,7 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect diff --git a/go.sum b/go.sum index 6bd5dbd..7ce4ab9 100644 --- a/go.sum +++ b/go.sum @@ -47,6 +47,7 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -126,12 +127,8 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= -github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -148,11 +145,11 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/labstack/echo-contrib v0.14.0 h1:tVHJjhqOcB183bzAeNDVwKgf1GWRAM6k9PvIVKEjQ/A= -github.com/labstack/echo-contrib v0.14.0/go.mod h1:0tmJZUHWLU7zGvMoxZwotRxHgUqBfW37T6bHg17SgAw= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/labstack/echo/v4 v4.10.1 h1:rB+D8In9PWjsp1OpHaqK+t04nQv/SBD1IoIcXCg0lpY= github.com/labstack/echo/v4 v4.10.1/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k= github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8= @@ -171,6 +168,7 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= @@ -181,7 +179,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qq github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= diff --git a/server/server.go b/server/server.go index d6a99ce..408165f 100644 --- a/server/server.go +++ b/server/server.go @@ -10,8 +10,6 @@ import ( "github.com/boojack/shortify/store" "github.com/gorilla/securecookie" - "github.com/gorilla/sessions" - "github.com/labstack/echo-contrib/session" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" ) @@ -23,7 +21,7 @@ type Server struct { Store *store.Store } -func NewServer(profile *profile.Profile, store *store.Store) (*Server, error) { +func NewServer(ctx context.Context, profile *profile.Profile, store *store.Store) (*Server, error) { e := echo.New() e.Debug = true e.HideBanner = true @@ -53,12 +51,15 @@ func NewServer(profile *profile.Profile, store *store.Store) (*Server, error) { embedFrontend(e) - // In dev mode, set the const secret key to make signin session persistence. - secret := "iamshortify" + // In dev mode, we'd like to set the const secret key to make signin session persistence. + secret := "shortify" if profile.Mode == "prod" { - secret = string(securecookie.GenerateRandomKey(16)) + var err error + secret, err = s.getSystemSecretSessionName(ctx) + if err != nil { + return nil, err + } } - e.Use(session.Middleware(sessions.NewCookieStore([]byte(secret)))) apiGroup := e.Group("") // Register API v1 routes. @@ -88,3 +89,23 @@ func (s *Server) Shutdown(ctx context.Context) { fmt.Printf("server stopped properly\n") } + +func (s *Server) getSystemSecretSessionName(ctx context.Context) (string, error) { + secretSessionNameValue, err := s.Store.GetWorkspaceSetting(ctx, &store.FindWorkspaceSetting{ + Key: store.WorkspaceDisallowSignUp, + }) + if err != nil { + return "", err + } + if secretSessionNameValue == nil || secretSessionNameValue.Value == "" { + tempSecret := securecookie.GenerateRandomKey(16) + secretSessionNameValue, err = s.Store.UpsertWorkspaceSetting(ctx, &store.WorkspaceSetting{ + Key: store.WorkspaceSecretSessionName, + Value: string(tempSecret), + }) + if err != nil { + return "", err + } + } + return secretSessionNameValue.Value, nil +} diff --git a/store/workspace_setting.go b/store/workspace_setting.go index ec425cd..949f62c 100644 --- a/store/workspace_setting.go +++ b/store/workspace_setting.go @@ -6,13 +6,33 @@ import ( "strings" ) +type WorkspaceSettingKey string + +const ( + // WorkspaceDisallowSignUp is the key type for disallow sign up in workspace level. + WorkspaceDisallowSignUp WorkspaceSettingKey = "disallow-signup" + // WorkspaceSecretSessionName is the key type for secret session name. + WorkspaceSecretSessionName WorkspaceSettingKey = "secret-session-name" +) + +// String returns the string format of WorkspaceSettingKey type. +func (key WorkspaceSettingKey) String() string { + switch key { + case WorkspaceDisallowSignUp: + return "disallow-signup" + case WorkspaceSecretSessionName: + return "secret-session-name" + } + return "" +} + type WorkspaceSetting struct { - Key string + Key WorkspaceSettingKey Value string } type FindWorkspaceSetting struct { - Key string + Key WorkspaceSettingKey } func (s *Store) UpsertWorkspaceSetting(ctx context.Context, upsert *WorkspaceSetting) (*WorkspaceSetting, error) { diff --git a/test/store/workspace_setting_test.go b/test/store/workspace_setting_test.go new file mode 100644 index 0000000..37458ac --- /dev/null +++ b/test/store/workspace_setting_test.go @@ -0,0 +1,30 @@ +package teststore + +import ( + "context" + "testing" + + "github.com/boojack/shortify/store" + "github.com/gorilla/securecookie" + "github.com/stretchr/testify/require" +) + +func TestWorkspaceSettingStore(t *testing.T) { + ctx := context.Background() + ts := NewTestingStore(ctx, t) + tempSecret := securecookie.GenerateRandomKey(16) + workspaceSetting, err := ts.UpsertWorkspaceSetting(ctx, &store.WorkspaceSetting{ + Key: store.WorkspaceSecretSessionName, + Value: string(tempSecret), + }) + require.NoError(t, err) + foundWorkspaceSetting, err := ts.GetWorkspaceSetting(ctx, &store.FindWorkspaceSetting{ + Key: store.WorkspaceSecretSessionName, + }) + require.NoError(t, err) + require.Equal(t, workspaceSetting, foundWorkspaceSetting) + workspaceSettings, err := ts.ListWorkspaceSettings(ctx, &store.FindWorkspaceSetting{}) + require.NoError(t, err) + require.Equal(t, 1, len(workspaceSettings)) + require.Equal(t, foundWorkspaceSetting, workspaceSettings[0]) +}