From 61919013a389eb369e39e034baedebbf1859b62c Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Thu, 27 Jun 2024 19:15:49 +0530 Subject: [PATCH 01/21] refactor: isPlugin for API level --- server/api/api.go | 11 +- server/api/auth.go | 344 ---------------------------------------- server/api/context.go | 15 +- server/server/server.go | 3 +- 4 files changed, 12 insertions(+), 361 deletions(-) diff --git a/server/api/api.go b/server/api/api.go index 91bfb4133..cf3d2910e 100644 --- a/server/api/api.go +++ b/server/api/api.go @@ -41,7 +41,6 @@ type API struct { MattermostAuth bool logger mlog.LoggerIFace audit *audit.Audit - isPlugin bool } func NewAPI( @@ -51,7 +50,6 @@ func NewAPI( permissions permissions.PermissionsService, logger mlog.LoggerIFace, audit *audit.Audit, - isPlugin bool, ) *API { return &API{ app: app, @@ -60,7 +58,6 @@ func NewAPI( permissions: permissions, logger: logger, audit: audit, - isPlugin: isPlugin, } } @@ -77,7 +74,7 @@ func (a *API) RegisterRoutes(r *mux.Router) { // V2 routes (ToDo: migrate these to V3 when ready to ship V3) a.registerUsersRoutes(apiv2) - a.registerAuthRoutes(apiv2) + // a.registerAuthRoutes(apiv2) a.registerMembersRoutes(apiv2) a.registerCategoriesRoutes(apiv2) a.registerSharingRoutes(apiv2) @@ -104,9 +101,9 @@ func (a *API) RegisterRoutes(r *mux.Router) { a.registerSystemRoutes(r) } -func (a *API) RegisterAdminRoutes(r *mux.Router) { - r.HandleFunc("/api/v2/admin/users/{username}/password", a.adminRequired(a.handleAdminSetPassword)).Methods("POST") -} +// func (a *API) RegisterAdminRoutes(r *mux.Router) { +// r.HandleFunc("/api/v2/admin/users/{username}/password", a.adminRequired(a.handleAdminSetPassword)).Methods("POST") +// } func getUserID(r *http.Request) string { ctx := r.Context() diff --git a/server/api/auth.go b/server/api/auth.go index 203de88b8..5ad2f0660 100644 --- a/server/api/auth.go +++ b/server/api/auth.go @@ -2,326 +2,15 @@ package api import ( "context" - "encoding/json" - "io" - "net" "net/http" - "strings" - "github.com/gorilla/mux" "github.com/mattermost/mattermost-plugin-boards/server/model" - "github.com/mattermost/mattermost-plugin-boards/server/services/audit" "github.com/mattermost/mattermost-plugin-boards/server/services/auth" "github.com/mattermost/mattermost-plugin-boards/server/utils" "github.com/mattermost/mattermost/server/public/shared/mlog" ) -func (a *API) registerAuthRoutes(r *mux.Router) { - // personal-server specific routes. These are not needed in plugin mode. - if !a.isPlugin { - r.HandleFunc("/login", a.handleLogin).Methods("POST") - r.HandleFunc("/logout", a.sessionRequired(a.handleLogout)).Methods("POST") - r.HandleFunc("/register", a.handleRegister).Methods("POST") - r.HandleFunc("/teams/{teamID}/regenerate_signup_token", a.sessionRequired(a.handlePostTeamRegenerateSignupToken)).Methods("POST") - r.HandleFunc("/users/{userID}/changepassword", a.sessionRequired(a.handleChangePassword)).Methods("POST") - } -} - -func (a *API) handleLogin(w http.ResponseWriter, r *http.Request) { - // swagger:operation POST /login login - // - // Login user - // - // --- - // produces: - // - application/json - // parameters: - // - name: body - // in: body - // description: Login request - // required: true - // schema: - // "$ref": "#/definitions/LoginRequest" - // responses: - // '200': - // description: success - // schema: - // "$ref": "#/definitions/LoginResponse" - // '401': - // description: invalid login - // schema: - // "$ref": "#/definitions/ErrorResponse" - // '500': - // description: internal error - // schema: - // "$ref": "#/definitions/ErrorResponse" - if a.MattermostAuth { - a.errorResponse(w, r, model.NewErrNotImplemented("not permitted in plugin mode")) - return - } - - if len(a.singleUserToken) > 0 { - // Not permitted in single-user mode - a.errorResponse(w, r, model.NewErrUnauthorized("not permitted in single-user mode")) - return - } - - requestBody, err := io.ReadAll(r.Body) - if err != nil { - a.errorResponse(w, r, err) - return - } - - var loginData model.LoginRequest - err = json.Unmarshal(requestBody, &loginData) - if err != nil { - a.errorResponse(w, r, err) - return - } - - auditRec := a.makeAuditRecord(r, "login", audit.Fail) - defer a.audit.LogRecord(audit.LevelAuth, auditRec) - auditRec.AddMeta("username", loginData.Username) - auditRec.AddMeta("type", loginData.Type) - - if loginData.Type == "normal" { - token, err := a.app.Login(loginData.Username, loginData.Email, loginData.Password, loginData.MfaToken) - if err != nil { - a.errorResponse(w, r, model.NewErrUnauthorized("incorrect login")) - return - } - json, err := json.Marshal(model.LoginResponse{Token: token}) - if err != nil { - a.errorResponse(w, r, err) - return - } - - jsonBytesResponse(w, http.StatusOK, json) - auditRec.Success() - return - } - - a.errorResponse(w, r, model.NewErrBadRequest("invalid login type")) -} - -func (a *API) handleLogout(w http.ResponseWriter, r *http.Request) { - // swagger:operation POST /logout logout - // - // Logout user - // - // --- - // produces: - // - application/json - // security: - // - BearerAuth: [] - // responses: - // '200': - // description: success - // '500': - // description: internal error - // schema: - // "$ref": "#/definitions/ErrorResponse" - if a.MattermostAuth { - a.errorResponse(w, r, model.NewErrNotImplemented("not permitted in plugin mode")) - return - } - - if len(a.singleUserToken) > 0 { - // Not permitted in single-user mode - a.errorResponse(w, r, model.NewErrUnauthorized("not permitted in single-user mode")) - return - } - - ctx := r.Context() - - session := ctx.Value(sessionContextKey).(*model.Session) - - auditRec := a.makeAuditRecord(r, "logout", audit.Fail) - defer a.audit.LogRecord(audit.LevelAuth, auditRec) - auditRec.AddMeta("userID", session.UserID) - - if err := a.app.Logout(session.ID); err != nil { - a.errorResponse(w, r, model.NewErrUnauthorized("incorrect logout")) - return - } - - auditRec.AddMeta("sessionID", session.ID) - - jsonStringResponse(w, http.StatusOK, "{}") - auditRec.Success() -} - -func (a *API) handleRegister(w http.ResponseWriter, r *http.Request) { - // swagger:operation POST /register register - // - // Register new user - // - // --- - // produces: - // - application/json - // parameters: - // - name: body - // in: body - // description: Register request - // required: true - // schema: - // "$ref": "#/definitions/RegisterRequest" - // responses: - // '200': - // description: success - // '401': - // description: invalid registration token - // '500': - // description: internal error - // schema: - // "$ref": "#/definitions/ErrorResponse" - if a.MattermostAuth { - a.errorResponse(w, r, model.NewErrNotImplemented("not permitted in plugin mode")) - return - } - - if len(a.singleUserToken) > 0 { - // Not permitted in single-user mode - a.errorResponse(w, r, model.NewErrUnauthorized("not permitted in single-user mode")) - return - } - - requestBody, err := io.ReadAll(r.Body) - if err != nil { - a.errorResponse(w, r, err) - return - } - - var registerData model.RegisterRequest - err = json.Unmarshal(requestBody, ®isterData) - if err != nil { - a.errorResponse(w, r, err) - return - } - registerData.Email = strings.TrimSpace(registerData.Email) - registerData.Username = strings.TrimSpace(registerData.Username) - - // Validate token - if len(registerData.Token) > 0 { - team, err2 := a.app.GetRootTeam() - if err2 != nil { - a.errorResponse(w, r, err2) - return - } - - if registerData.Token != team.SignupToken { - a.errorResponse(w, r, model.NewErrUnauthorized("invalid token")) - return - } - } else { - // No signup token, check if no active users - userCount, err2 := a.app.GetRegisteredUserCount() - if err2 != nil { - a.errorResponse(w, r, err2) - return - } - if userCount > 0 { - a.errorResponse(w, r, model.NewErrUnauthorized("no sign-up token and user(s) already exist")) - return - } - } - - if err = registerData.IsValid(); err != nil { - a.errorResponse(w, r, err) - return - } - - auditRec := a.makeAuditRecord(r, "register", audit.Fail) - defer a.audit.LogRecord(audit.LevelAuth, auditRec) - auditRec.AddMeta("username", registerData.Username) - - err = a.app.RegisterUser(registerData.Username, registerData.Email, registerData.Password) - if err != nil { - a.errorResponse(w, r, model.NewErrBadRequest(err.Error())) - return - } - - jsonStringResponse(w, http.StatusOK, "{}") - auditRec.Success() -} - -func (a *API) handleChangePassword(w http.ResponseWriter, r *http.Request) { - // swagger:operation POST /users/{userID}/changepassword changePassword - // - // Change a user's password - // - // --- - // produces: - // - application/json - // parameters: - // - name: userID - // in: path - // description: User ID - // required: true - // type: string - // - name: body - // in: body - // description: Change password request - // required: true - // schema: - // "$ref": "#/definitions/ChangePasswordRequest" - // security: - // - BearerAuth: [] - // responses: - // '200': - // description: success - // '400': - // description: invalid request - // schema: - // "$ref": "#/definitions/ErrorResponse" - // '500': - // description: internal error - // schema: - // "$ref": "#/definitions/ErrorResponse" - if a.MattermostAuth { - a.errorResponse(w, r, model.NewErrNotImplemented("not permitted in plugin mode")) - return - } - - if len(a.singleUserToken) > 0 { - // Not permitted in single-user mode - a.errorResponse(w, r, model.NewErrUnauthorized("not permitted in single-user mode")) - return - } - - vars := mux.Vars(r) - userID := vars["userID"] - - requestBody, err := io.ReadAll(r.Body) - if err != nil { - a.errorResponse(w, r, err) - return - } - - var requestData model.ChangePasswordRequest - if err = json.Unmarshal(requestBody, &requestData); err != nil { - a.errorResponse(w, r, err) - return - } - - if err = requestData.IsValid(); err != nil { - a.errorResponse(w, r, err) - return - } - - auditRec := a.makeAuditRecord(r, "changePassword", audit.Fail) - defer a.audit.LogRecord(audit.LevelAuth, auditRec) - - if err = a.app.ChangePassword(userID, requestData.OldPassword, requestData.NewPassword); err != nil { - a.errorResponse(w, r, model.NewErrBadRequest(err.Error())) - return - } - - jsonStringResponse(w, http.StatusOK, "{}") - auditRec.Success() -} - func (a *API) sessionRequired(handler func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { return a.attachSession(handler, true) } @@ -331,26 +20,6 @@ func (a *API) attachSession(handler func(w http.ResponseWriter, r *http.Request) token, _ := auth.ParseAuthTokenFromRequest(r) a.logger.Debug(`attachSession`, mlog.Bool("single_user", len(a.singleUserToken) > 0)) - if len(a.singleUserToken) > 0 { - if required && (token != a.singleUserToken) { - a.errorResponse(w, r, model.NewErrUnauthorized("invalid single user token")) - return - } - - now := utils.GetMillis() - session := &model.Session{ - ID: model.SingleUser, - Token: token, - UserID: model.SingleUser, - AuthService: a.authService, - Props: map[string]interface{}{}, - CreateAt: now, - UpdateAt: now, - } - ctx := context.WithValue(r.Context(), sessionContextKey, session) - handler(w, r.WithContext(ctx)) - return - } if a.MattermostAuth && r.Header.Get("Mattermost-User-Id") != "" { userID := r.Header.Get("Mattermost-User-Id") @@ -397,16 +66,3 @@ func (a *API) attachSession(handler func(w http.ResponseWriter, r *http.Request) handler(w, r.WithContext(ctx)) } } - -func (a *API) adminRequired(handler func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { - return func(w http.ResponseWriter, r *http.Request) { - // Currently, admin APIs require local unix connections - conn := GetContextConn(r) - if _, isUnix := conn.(*net.UnixConn); !isUnix { - a.errorResponse(w, r, model.NewErrUnauthorized("not a local unix connection")) - return - } - - handler(w, r) - } -} diff --git a/server/api/context.go b/server/api/context.go index 5e4d51ef8..1bdae7c27 100644 --- a/server/api/context.go +++ b/server/api/context.go @@ -3,7 +3,6 @@ package api import ( "context" "net" - "net/http" ) type contextKey int @@ -19,11 +18,11 @@ func SetContextConn(ctx context.Context, c net.Conn) context.Context { } // GetContextConn gets the stored connection from the request context. -func GetContextConn(r *http.Request) net.Conn { - value := r.Context().Value(httpConnContextKey) - if value == nil { - return nil - } +// func GetContextConn(r *http.Request) net.Conn { +// value := r.Context().Value(httpConnContextKey) +// if value == nil { +// return nil +// } - return value.(net.Conn) -} +// return value.(net.Conn) +// } diff --git a/server/server/server.go b/server/server/server.go index fd2c5c1f5..b5838bf2b 100644 --- a/server/server/server.go +++ b/server/server/server.go @@ -143,11 +143,10 @@ func New(params Params) (*Server, error) { } app := app.New(params.Cfg, wsAdapter, appServices) - focalboardAPI := api.NewAPI(app, params.SingleUserToken, params.Cfg.AuthMode, params.PermissionsService, params.Logger, auditService, params.IsPlugin) + focalboardAPI := api.NewAPI(app, params.SingleUserToken, params.Cfg.AuthMode, params.PermissionsService, params.Logger, auditService) // Local router for admin APIs localRouter := mux.NewRouter() - focalboardAPI.RegisterAdminRoutes(localRouter) // Init team if _, err := app.GetRootTeam(); err != nil { From fae82fe65edf968d30e0eded01bcdfde8308711c Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Thu, 27 Jun 2024 19:20:36 +0530 Subject: [PATCH 02/21] chore: removed GetContextConn as it is not required --- server/api/context.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/server/api/context.go b/server/api/context.go index 1bdae7c27..ff200fe95 100644 --- a/server/api/context.go +++ b/server/api/context.go @@ -16,13 +16,3 @@ const ( func SetContextConn(ctx context.Context, c net.Conn) context.Context { return context.WithValue(ctx, httpConnContextKey, c) } - -// GetContextConn gets the stored connection from the request context. -// func GetContextConn(r *http.Request) net.Conn { -// value := r.Context().Value(httpConnContextKey) -// if value == nil { -// return nil -// } - -// return value.(net.Conn) -// } From a20079b6a2c57c01d06b21f9cf22bd186980295d Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Thu, 27 Jun 2024 19:21:41 +0530 Subject: [PATCH 03/21] chore: clean up api.go removed none required function --- server/api/api.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/server/api/api.go b/server/api/api.go index cf3d2910e..ab73c128d 100644 --- a/server/api/api.go +++ b/server/api/api.go @@ -101,10 +101,6 @@ func (a *API) RegisterRoutes(r *mux.Router) { a.registerSystemRoutes(r) } -// func (a *API) RegisterAdminRoutes(r *mux.Router) { -// r.HandleFunc("/api/v2/admin/users/{username}/password", a.adminRequired(a.handleAdminSetPassword)).Methods("POST") -// } - func getUserID(r *http.Request) string { ctx := r.Context() session, ok := ctx.Value(sessionContextKey).(*model.Session) From 60c76e9c3a4a48f38ac5d3204d89020fbbe08d5b Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Thu, 27 Jun 2024 19:22:18 +0530 Subject: [PATCH 04/21] chore: removed commented code --- server/api/api.go | 1 - 1 file changed, 1 deletion(-) diff --git a/server/api/api.go b/server/api/api.go index ab73c128d..b54b656ed 100644 --- a/server/api/api.go +++ b/server/api/api.go @@ -74,7 +74,6 @@ func (a *API) RegisterRoutes(r *mux.Router) { // V2 routes (ToDo: migrate these to V3 when ready to ship V3) a.registerUsersRoutes(apiv2) - // a.registerAuthRoutes(apiv2) a.registerMembersRoutes(apiv2) a.registerCategoriesRoutes(apiv2) a.registerSharingRoutes(apiv2) From 4c9a8a9371849eb000de7ebdb77a38ae52ff2740 Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Thu, 27 Jun 2024 19:53:27 +0530 Subject: [PATCH 05/21] refactor: removed the admin as its no log required --- server/api/admin.go | 56 --------------------------------------------- 1 file changed, 56 deletions(-) delete mode 100644 server/api/admin.go diff --git a/server/api/admin.go b/server/api/admin.go deleted file mode 100644 index 12832460b..000000000 --- a/server/api/admin.go +++ /dev/null @@ -1,56 +0,0 @@ -package api - -import ( - "encoding/json" - "io" - "net/http" - "strings" - - "github.com/gorilla/mux" - "github.com/mattermost/mattermost-plugin-boards/server/model" - "github.com/mattermost/mattermost-plugin-boards/server/services/audit" - - "github.com/mattermost/mattermost/server/public/shared/mlog" -) - -type AdminSetPasswordData struct { - Password string `json:"password"` -} - -func (a *API) handleAdminSetPassword(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - username := vars["username"] - - requestBody, err := io.ReadAll(r.Body) - if err != nil { - a.errorResponse(w, r, err) - return - } - - var requestData AdminSetPasswordData - err = json.Unmarshal(requestBody, &requestData) - if err != nil { - a.errorResponse(w, r, err) - return - } - - auditRec := a.makeAuditRecord(r, "adminSetPassword", audit.Fail) - defer a.audit.LogRecord(audit.LevelAuth, auditRec) - auditRec.AddMeta("username", username) - - if !strings.Contains(requestData.Password, "") { - a.errorResponse(w, r, model.NewErrBadRequest("password is required")) - return - } - - err = a.app.UpdateUserPassword(username, requestData.Password) - if err != nil { - a.errorResponse(w, r, err) - return - } - - a.logger.Debug("AdminSetPassword, username: %s", mlog.String("username", username)) - - jsonStringResponse(w, http.StatusOK, "{}") - auditRec.Success() -} From f942a47e9cec5172828059ea38b187951d3618af Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Thu, 27 Jun 2024 19:54:33 +0530 Subject: [PATCH 06/21] refactor: removed handlePostTeamRegenerateSignupToken as not used anymore --- server/api/teams.go | 48 --------------------------------------------- 1 file changed, 48 deletions(-) diff --git a/server/api/teams.go b/server/api/teams.go index 9723a9005..eb150706e 100644 --- a/server/api/teams.go +++ b/server/api/teams.go @@ -131,54 +131,6 @@ func (a *API) handleGetTeam(w http.ResponseWriter, r *http.Request) { auditRec.Success() } -func (a *API) handlePostTeamRegenerateSignupToken(w http.ResponseWriter, r *http.Request) { - // swagger:operation POST /teams/{teamID}/regenerate_signup_token regenerateSignupToken - // - // Regenerates the signup token for the root team - // - // --- - // produces: - // - application/json - // parameters: - // - name: teamID - // in: path - // description: Team ID - // required: true - // type: string - // security: - // - BearerAuth: [] - // responses: - // '200': - // description: success - // default: - // description: internal error - // schema: - // "$ref": "#/definitions/ErrorResponse" - if a.MattermostAuth { - a.errorResponse(w, r, model.NewErrNotImplemented("not permitted in plugin mode")) - return - } - - team, err := a.app.GetRootTeam() - if err != nil { - a.errorResponse(w, r, err) - return - } - - auditRec := a.makeAuditRecord(r, "regenerateSignupToken", audit.Fail) - defer a.audit.LogRecord(audit.LevelModify, auditRec) - - team.SignupToken = utils.NewID(utils.IDTypeToken) - - if err = a.app.UpsertTeamSignupToken(*team); err != nil { - a.errorResponse(w, r, err) - return - } - - jsonStringResponse(w, http.StatusOK, "{}") - auditRec.Success() -} - func (a *API) handleGetTeamUsers(w http.ResponseWriter, r *http.Request) { // swagger:operation GET /teams/{teamID}/users getTeamUsers // From 688f53a101703176278ab546816ced5b62af2620 Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Fri, 28 Jun 2024 15:48:01 +0530 Subject: [PATCH 07/21] refactor: updated make file rules --- Makefile | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a585bb4cf..0d0b096f3 100644 --- a/Makefile +++ b/Makefile @@ -154,6 +154,14 @@ endif @echo plugin built at: dist/$(BUNDLE_NAME) +info: ## Display build information + @echo "Build Number: $(BUILD_NUMBER)" + @echo "Build Date: $(BUILD_DATE)" + @echo "Build Hash: $(BUILD_HASH)" + @echo "Plugin ID: $(PLUGIN_ID)" + @echo "Plugin Version: $(PLUGIN_VERSION)" + @echo "Bundle Name: $(BUNDLE_NAME)" + ## Builds and bundles the plugin. .PHONY: dist dist: apply server webapp bundle @@ -382,7 +390,7 @@ watch-plugin: modd-precheck ## Run and upload the plugin to a development server env FOCALBOARD_BUILD_TAGS='$(BUILD_TAGS)' modd -f modd-watchplugin.conf live-watch-plugin: modd-precheck ## Run and update locally the plugin in the development server - cd mattermost-plugin; make live-watch + make live-watch swagger: ## Generate swagger API spec and clients based on it. mkdir -p server/swagger/docs From 80d402ec3dfe67366826b6e32d004dd9d948dfff Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Fri, 28 Jun 2024 16:19:37 +0530 Subject: [PATCH 08/21] refactor: removed isGuest code --- server/admin-scripts/reset-password.sh | 8 - server/api/api.go | 31 +-- server/api/archive.go | 18 +- server/api/auth.go | 2 - server/api/blocks.go | 13 -- server/api/boards.go | 41 +--- server/api/boards_and_blocks.go | 10 - server/api/members.go | 20 -- server/api/onboarding.go | 10 - server/api/search.go | 16 +- server/api/system.go | 4 - server/api/system_test.go | 35 --- server/api/teams.go | 9 - server/api/templates.go | 10 - server/main/doc.go | 26 --- server/main/main.go | 297 ------------------------- 16 files changed, 15 insertions(+), 535 deletions(-) delete mode 100755 server/admin-scripts/reset-password.sh delete mode 100644 server/main/doc.go delete mode 100644 server/main/main.go diff --git a/server/admin-scripts/reset-password.sh b/server/admin-scripts/reset-password.sh deleted file mode 100755 index 6c614ed1b..000000000 --- a/server/admin-scripts/reset-password.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -if [[ $# < 2 ]] ; then - echo 'reset-password.sh ' - exit 1 -fi - -curl --unix-socket /var/tmp/focalboard_local.socket http://localhost/api/v2/admin/users/$1/password -X POST -H 'Content-Type: application/json' -d '{ "password": "'$2'" }' diff --git a/server/api/api.go b/server/api/api.go index b54b656ed..830f57ff3 100644 --- a/server/api/api.go +++ b/server/api/api.go @@ -34,13 +34,12 @@ var ( // REST APIs type API struct { - app *app.App - authService string - permissions permissions.PermissionsService - singleUserToken string - MattermostAuth bool - logger mlog.LoggerIFace - audit *audit.Audit + app *app.App + authService string + permissions permissions.PermissionsService + MattermostAuth bool + logger mlog.LoggerIFace + audit *audit.Audit } func NewAPI( @@ -52,12 +51,11 @@ func NewAPI( audit *audit.Audit, ) *API { return &API{ - app: app, - singleUserToken: singleUserToken, - authService: authService, - permissions: permissions, - logger: logger, - audit: audit, + app: app, + authService: authService, + permissions: permissions, + logger: logger, + audit: audit, } } @@ -160,13 +158,6 @@ func (a *API) hasValidReadTokenForBoard(r *http.Request, boardID string) bool { return isValid } -func (a *API) userIsGuest(userID string) (bool, error) { - if a.singleUserToken != "" { - return false, nil - } - return a.app.UserIsGuest(userID) -} - // Response helpers func (a *API) errorResponse(w http.ResponseWriter, r *http.Request, err error) { diff --git a/server/api/archive.go b/server/api/archive.go index a8bb55435..11590f423 100644 --- a/server/api/archive.go +++ b/server/api/archive.go @@ -137,16 +137,6 @@ func (a *API) handleArchiveImport(w http.ResponseWriter, r *http.Request) { return } - isGuest, err := a.userIsGuest(userID) - if err != nil { - a.errorResponse(w, r, err) - return - } - if isGuest { - a.errorResponse(w, r, model.NewErrPermission("access denied to create board")) - return - } - file, handle, err := r.FormFile(UploadFormFileKey) if err != nil { fmt.Fprintf(w, "%v", err) @@ -220,13 +210,7 @@ func (a *API) handleArchiveExportTeam(w http.ResponseWriter, r *http.Request) { defer a.audit.LogRecord(audit.LevelRead, auditRec) auditRec.AddMeta("TeamID", teamID) - isGuest, err := a.userIsGuest(userID) - if err != nil { - a.errorResponse(w, r, err) - return - } - - boards, err := a.app.GetBoardsForUserAndTeam(userID, teamID, !isGuest) + boards, err := a.app.GetBoardsForUserAndTeam(userID, teamID, false) if err != nil { a.errorResponse(w, r, err) return diff --git a/server/api/auth.go b/server/api/auth.go index 5ad2f0660..837ce3b3d 100644 --- a/server/api/auth.go +++ b/server/api/auth.go @@ -19,8 +19,6 @@ func (a *API) attachSession(handler func(w http.ResponseWriter, r *http.Request) return func(w http.ResponseWriter, r *http.Request) { token, _ := auth.ParseAuthTokenFromRequest(r) - a.logger.Debug(`attachSession`, mlog.Bool("single_user", len(a.singleUserToken) > 0)) - if a.MattermostAuth && r.Header.Get("Mattermost-User-Id") != "" { userID := r.Header.Get("Mattermost-User-Id") now := utils.GetMillis() diff --git a/server/api/blocks.go b/server/api/blocks.go index 36732244f..93db90ec5 100644 --- a/server/api/blocks.go +++ b/server/api/blocks.go @@ -98,19 +98,6 @@ func (a *API) handleGetBlocks(w http.ResponseWriter, r *http.Request) { return } } - if board.IsTemplate { - var isGuest bool - isGuest, err = a.userIsGuest(userID) - if err != nil { - a.errorResponse(w, r, err) - return - } - - if isGuest { - a.errorResponse(w, r, model.NewErrPermission("guest are not allowed to get board templates")) - return - } - } } auditRec := a.makeAuditRecord(r, "getBlocks", audit.Fail) diff --git a/server/api/boards.go b/server/api/boards.go index 1314c289a..8548f1238 100644 --- a/server/api/boards.go +++ b/server/api/boards.go @@ -63,14 +63,8 @@ func (a *API) handleGetBoards(w http.ResponseWriter, r *http.Request) { defer a.audit.LogRecord(audit.LevelRead, auditRec) auditRec.AddMeta("teamID", teamID) - isGuest, err := a.userIsGuest(userID) - if err != nil { - a.errorResponse(w, r, err) - return - } - // retrieve boards list - boards, err := a.app.GetBoardsForUserAndTeam(userID, teamID, !isGuest) + boards, err := a.app.GetBoardsForUserAndTeam(userID, teamID, false) if err != nil { a.errorResponse(w, r, err) return @@ -147,16 +141,6 @@ func (a *API) handleCreateBoard(w http.ResponseWriter, r *http.Request) { } } - isGuest, err := a.userIsGuest(userID) - if err != nil { - a.errorResponse(w, r, err) - return - } - if isGuest { - a.errorResponse(w, r, model.NewErrPermission("access denied to create board")) - return - } - if err = newBoard.IsValid(); err != nil { a.errorResponse(w, r, model.NewErrBadRequest(err.Error())) return @@ -243,19 +227,6 @@ func (a *API) handleGetBoard(w http.ResponseWriter, r *http.Request) { return } } else { - var isGuest bool - isGuest, err = a.userIsGuest(userID) - if err != nil { - a.errorResponse(w, r, err) - return - } - if isGuest { - if !a.permissions.HasPermissionToBoard(userID, boardID, model.PermissionViewBoard) { - a.errorResponse(w, r, model.NewErrPermission("access denied to board")) - return - } - } - if !a.permissions.HasPermissionToTeam(userID, board.TeamID, model.PermissionViewTeam) { a.errorResponse(w, r, model.NewErrPermission("access denied to board")) return @@ -515,16 +486,6 @@ func (a *API) handleDuplicateBoard(w http.ResponseWriter, r *http.Request) { } } - isGuest, err := a.userIsGuest(userID) - if err != nil { - a.errorResponse(w, r, err) - return - } - if isGuest { - a.errorResponse(w, r, model.NewErrPermission("access denied to create board")) - return - } - auditRec := a.makeAuditRecord(r, "duplicateBoard", audit.Fail) defer a.audit.LogRecord(audit.LevelRead, auditRec) auditRec.AddMeta("boardID", boardID) diff --git a/server/api/boards_and_blocks.go b/server/api/boards_and_blocks.go index 8bb57d99e..19cdd7a3e 100644 --- a/server/api/boards_and_blocks.go +++ b/server/api/boards_and_blocks.go @@ -92,16 +92,6 @@ func (a *API) handleCreateBoardsAndBlocks(w http.ResponseWriter, r *http.Request return } - isGuest, err := a.userIsGuest(userID) - if err != nil { - a.errorResponse(w, r, err) - return - } - if isGuest { - a.errorResponse(w, r, model.NewErrPermission("access denied to create board")) - return - } - for _, block := range newBab.Blocks { // Error checking if len(block.Type) < 1 { diff --git a/server/api/members.go b/server/api/members.go index 1a5d644d6..ddde1f7ed 100644 --- a/server/api/members.go +++ b/server/api/members.go @@ -257,16 +257,6 @@ func (a *API) handleJoinBoard(w http.ResponseWriter, r *http.Request) { return } - isGuest, err := a.userIsGuest(userID) - if err != nil { - a.errorResponse(w, r, err) - return - } - if isGuest { - a.errorResponse(w, r, model.NewErrPermission("guests not allowed to join boards")) - return - } - newBoardMember := &model.BoardMember{ UserID: userID, BoardID: boardID, @@ -434,16 +424,6 @@ func (a *API) handleUpdateMember(w http.ResponseWriter, r *http.Request) { SchemeViewer: reqBoardMember.SchemeViewer, } - isGuest, err := a.userIsGuest(paramsUserID) - if err != nil { - a.errorResponse(w, r, err) - return - } - - if isGuest { - newBoardMember.SchemeAdmin = false - } - if !a.permissions.HasPermissionToBoard(userID, boardID, model.PermissionManageBoardRoles) { a.errorResponse(w, r, model.NewErrPermission("access denied to modify board members")) return diff --git a/server/api/onboarding.go b/server/api/onboarding.go index bca6e201d..7a9922a3d 100644 --- a/server/api/onboarding.go +++ b/server/api/onboarding.go @@ -53,16 +53,6 @@ func (a *API) handleOnboard(w http.ResponseWriter, r *http.Request) { return } - isGuest, err := a.userIsGuest(userID) - if err != nil { - a.errorResponse(w, r, err) - return - } - if isGuest { - a.errorResponse(w, r, model.NewErrPermission("access denied to create board")) - return - } - teamID, boardID, err := a.app.PrepareOnboardingTour(userID, teamID) if err != nil { a.errorResponse(w, r, err) diff --git a/server/api/search.go b/server/api/search.go index d6ba4560a..86dd16e15 100644 --- a/server/api/search.go +++ b/server/api/search.go @@ -161,14 +161,8 @@ func (a *API) handleSearchBoards(w http.ResponseWriter, r *http.Request) { defer a.audit.LogRecord(audit.LevelRead, auditRec) auditRec.AddMeta("teamID", teamID) - isGuest, err := a.userIsGuest(userID) - if err != nil { - a.errorResponse(w, r, err) - return - } - // retrieve boards list - boards, err := a.app.SearchBoardsForUser(term, searchField, userID, !isGuest) + boards, err := a.app.SearchBoardsForUser(term, searchField, userID, false) if err != nil { a.errorResponse(w, r, err) return @@ -320,14 +314,8 @@ func (a *API) handleSearchAllBoards(w http.ResponseWriter, r *http.Request) { auditRec := a.makeAuditRecord(r, "searchAllBoards", audit.Fail) defer a.audit.LogRecord(audit.LevelRead, auditRec) - isGuest, err := a.userIsGuest(userID) - if err != nil { - a.errorResponse(w, r, err) - return - } - // retrieve boards list - boards, err := a.app.SearchBoardsForUser(term, model.BoardSearchFieldTitle, userID, !isGuest) + boards, err := a.app.SearchBoardsForUser(term, model.BoardSearchFieldTitle, userID, false) if err != nil { a.errorResponse(w, r, err) return diff --git a/server/api/system.go b/server/api/system.go index 1e579b452..4cefb3ff6 100644 --- a/server/api/system.go +++ b/server/api/system.go @@ -40,10 +40,6 @@ func (a *API) handlePing(w http.ResponseWriter, r *http.Request) { // description: success serverMetadata := a.app.GetServerMetadata() - if a.singleUserToken != "" { - serverMetadata.SKU = "personal_desktop" - } - if serverMetadata.Edition == "plugin" { serverMetadata.SKU = "suite" } diff --git a/server/api/system_test.go b/server/api/system_test.go index dd750539b..70fd41baf 100644 --- a/server/api/system_test.go +++ b/server/api/system_test.go @@ -71,41 +71,6 @@ func TestPing(t *testing.T) { } }) - t.Run("Sets SKU to 'personal_desktop' when in single-user mode", func(t *testing.T) { - testAPI.singleUserToken = "abc-123-xyz-456" - request, _ := http.NewRequest(http.MethodGet, "/ping", nil) - response := httptest.NewRecorder() - - testAPI.handlePing(response, request) - - var got app.ServerMetadata - err := json.NewDecoder(response.Body).Decode(&got) - if err != nil { - t.Fatalf("Unable to JSON decode response body %q", response.Body) - } - - want := app.ServerMetadata{ - Version: model.CurrentVersion, - BuildNumber: model.BuildNumber, - BuildDate: model.BuildDate, - Commit: model.BuildHash, - Edition: model.Edition, - DBType: "", - DBVersion: "", - OSType: runtime.GOOS, - OSArch: runtime.GOARCH, - SKU: "personal_desktop", - } - - if got != want { - t.Errorf("got %q want %q", got, want) - } - - if response.Code != http.StatusOK { - t.Errorf("got HTTP %d want %d", response.Code, http.StatusOK) - } - }) - t.Run("Sets SKU to 'suite' when in plugin mode", func(t *testing.T) { model.Edition = "plugin" request, _ := http.NewRequest(http.MethodGet, "/ping", nil) diff --git a/server/api/teams.go b/server/api/teams.go index eb150706e..ccf87ec90 100644 --- a/server/api/teams.go +++ b/server/api/teams.go @@ -184,16 +184,7 @@ func (a *API) handleGetTeamUsers(w http.ResponseWriter, r *http.Request) { auditRec := a.makeAuditRecord(r, "getUsers", audit.Fail) defer a.audit.LogRecord(audit.LevelRead, auditRec) - isGuest, err := a.userIsGuest(userID) - if err != nil { - a.errorResponse(w, r, err) - return - } asGuestUser := "" - if isGuest { - asGuestUser = userID - } - users, err := a.app.SearchTeamUsers(teamID, searchQuery, asGuestUser, excludeBots) if err != nil { a.errorResponse(w, r, err) diff --git a/server/api/templates.go b/server/api/templates.go index 7a1b9d6eb..2ae1734f6 100644 --- a/server/api/templates.go +++ b/server/api/templates.go @@ -51,16 +51,6 @@ func (a *API) handleGetTemplates(w http.ResponseWriter, r *http.Request) { return } - isGuest, err := a.userIsGuest(userID) - if err != nil { - a.errorResponse(w, r, err) - return - } - if isGuest { - a.errorResponse(w, r, model.NewErrPermission("access denied to templates")) - return - } - auditRec := a.makeAuditRecord(r, "getTemplates", audit.Fail) defer a.audit.LogRecord(audit.LevelRead, auditRec) auditRec.AddMeta("teamID", teamID) diff --git a/server/main/doc.go b/server/main/doc.go deleted file mode 100644 index 7d7427eba..000000000 --- a/server/main/doc.go +++ /dev/null @@ -1,26 +0,0 @@ -// Package classification Focalboard Server -// -// Focalboard Server -// -// Schemes: http, https -// Host: localhost -// BasePath: /api/v2 -// Version: 2.0.0 -// License: Custom https://github.com/mattermost/focalboard/blob/main/LICENSE.txt -// Contact: Focalboard https://www.focalboard.com -// -// Consumes: -// - application/json -// -// Produces: -// - application/json -// -// securityDefinitions: -// BearerAuth: -// type: apiKey -// name: Authorization -// in: header -// description: 'Pass session token using Bearer authentication, e.g. set header "Authorization: Bearer "' -// -// swagger:meta -package main diff --git a/server/main/main.go b/server/main/main.go deleted file mode 100644 index 0327c78bd..000000000 --- a/server/main/main.go +++ /dev/null @@ -1,297 +0,0 @@ -// Server for Focalboard -package main - -import ( - "C" - "flag" - "log" - "os" - "os/signal" - "syscall" - "time" - - "github.com/mattermost/mattermost-plugin-boards/server/model" - "github.com/mattermost/mattermost-plugin-boards/server/server" - "github.com/mattermost/mattermost-plugin-boards/server/services/config" - "github.com/mattermost/mattermost-plugin-boards/server/services/permissions/localpermissions" -) -import ( - "github.com/mattermost/mattermost/server/public/shared/mlog" -) - -// Active server used with shared code (dll) -var pServer *server.Server - -const ( - timeBetweenPidMonitoringChecks = 2 * time.Second -) - -func isProcessRunning(pid int) bool { - process, err := os.FindProcess(pid) - if err != nil { - return false - } - - err = process.Signal(syscall.Signal(0)) - - return err == nil -} - -// monitorPid is used to keep the server lifetime in sync with another (client app) process -func monitorPid(pid int, logger *mlog.Logger) { - logger.Info("Monitoring PID", mlog.Int("pid", pid)) - - go func() { - for { - if !isProcessRunning(pid) { - logger.Info("Monitored process not found, exiting.") - os.Exit(1) - } - - time.Sleep(timeBetweenPidMonitoringChecks) - } - }() -} - -func main() { - // Command line args - pMonitorPid := flag.Int("monitorpid", -1, "a process ID") - pPort := flag.Int("port", 0, "the port number") - pSingleUser := flag.Bool("single-user", false, "single user mode") - pDBType := flag.String("dbtype", "", "Database type") - pDBConfig := flag.String("dbconfig", "", "Database config") - pConfigFilePath := flag.String( - "config", - "", - "Location of the JSON config file", - ) - flag.Parse() - - config, err := config.ReadConfigFile(*pConfigFilePath) - if err != nil { - log.Fatal("Unable to read the config file: ", err) - return - } - - logger, _ := mlog.NewLogger() - cfgJSON := config.LoggingCfgJSON - if config.LoggingCfgFile == "" && cfgJSON == "" { - // if no logging defined, use default config (console output) - cfgJSON = defaultLoggingConfig() - } - err = logger.Configure(config.LoggingCfgFile, cfgJSON, nil) - if err != nil { - log.Fatal("Error in config file for logger: ", err) - return - } - defer func() { _ = logger.Shutdown() }() - - if logger.HasTargets() { - restore := logger.RedirectStdLog(mlog.LvlInfo, mlog.String("src", "stdlog")) - defer restore() - } - - model.LogServerInfo(logger) - - singleUser := false - if pSingleUser != nil { - singleUser = *pSingleUser - } - - singleUserToken := "" - if singleUser { - singleUserToken = os.Getenv("FOCALBOARD_SINGLE_USER_TOKEN") - if len(singleUserToken) < 1 { - logger.Fatal("The FOCALBOARD_SINGLE_USER_TOKEN environment variable must be set for single user mode ") - return - } - logger.Info("Single user mode") - } - - if pMonitorPid != nil && *pMonitorPid > 0 { - monitorPid(*pMonitorPid, logger) - } - - // Override config from commandline - - if pDBType != nil && len(*pDBType) > 0 { - config.DBType = *pDBType - logger.Info("DBType from commandline", mlog.String("DBType", *pDBType)) - } - - if pDBConfig != nil && len(*pDBConfig) > 0 { - config.DBConfigString = *pDBConfig - // Don't echo, as the confix string may contain passwords - logger.Info("DBConfigString overridden from commandline") - } - - if pPort != nil && *pPort > 0 && *pPort != config.Port { - // Override port - logger.Info("Port from commandline", mlog.Int("port", *pPort)) - config.Port = *pPort - } - - db, err := server.NewStore(config, singleUser, logger) - if err != nil { - logger.Fatal("server.NewStore ERROR", mlog.Err(err)) - } - - permissionsService := localpermissions.New(db, logger) - - params := server.Params{ - Cfg: config, - SingleUserToken: singleUserToken, - DBStore: db, - Logger: logger, - PermissionsService: permissionsService, - } - - server, err := server.New(params) - if err != nil { - logger.Fatal("server.New ERROR", mlog.Err(err)) - } - - if err := server.Start(); err != nil { - logger.Fatal("server.Start ERROR", mlog.Err(err)) - } - - // Setting up signal capturing - stop := make(chan os.Signal, 1) - signal.Notify(stop, os.Interrupt) - - // Waiting for SIGINT (pkill -2) - <-stop - - _ = server.Shutdown() -} - -// StartServer starts the server -// -//export StartServer -func StartServer(webPath *C.char, filesPath *C.char, port int, singleUserToken, dbConfigString, configFilePath *C.char) { - startServer( - C.GoString(webPath), - C.GoString(filesPath), - port, - C.GoString(singleUserToken), - C.GoString(dbConfigString), - C.GoString(configFilePath), - ) -} - -// StopServer stops the server -// -//export StopServer -func StopServer() { - stopServer() -} - -func startServer(webPath string, filesPath string, port int, singleUserToken, dbConfigString, configFilePath string) { - if pServer != nil { - stopServer() - pServer = nil - } - - // config.json file - config, err := config.ReadConfigFile(configFilePath) - if err != nil { - log.Fatal("Unable to read the config file: ", err) - return - } - - logger, _ := mlog.NewLogger() - err = logger.Configure(config.LoggingCfgFile, config.LoggingCfgJSON, nil) - if err != nil { - log.Fatal("Error in config file for logger: ", err) - return - } - - model.LogServerInfo(logger) - - if len(filesPath) > 0 { - config.FilesPath = filesPath - } - - if len(webPath) > 0 { - config.WebPath = webPath - } - - if port > 0 { - config.Port = port - } - - if len(dbConfigString) > 0 { - config.DBConfigString = dbConfigString - } - - singleUser := len(singleUserToken) > 0 - db, err := server.NewStore(config, singleUser, logger) - if err != nil { - logger.Fatal("server.NewStore ERROR", mlog.Err(err)) - } - - permissionsService := localpermissions.New(db, logger) - - params := server.Params{ - Cfg: config, - SingleUserToken: singleUserToken, - DBStore: db, - Logger: logger, - PermissionsService: permissionsService, - } - - pServer, err = server.New(params) - if err != nil { - logger.Fatal("server.New ERROR", mlog.Err(err)) - } - - if err := pServer.Start(); err != nil { - logger.Fatal("server.Start ERROR", mlog.Err(err)) - } -} - -func stopServer() { - if pServer == nil { - return - } - - logger := pServer.Logger() - - err := pServer.Shutdown() - if err != nil { - logger.Error("server.Shutdown ERROR", mlog.Err(err)) - } - - if l, ok := logger.(*mlog.Logger); ok { - _ = l.Shutdown() - } - pServer = nil -} - -func defaultLoggingConfig() string { - return ` - { - "def": { - "type": "console", - "options": { - "out": "stdout" - }, - "format": "plain", - "format_options": { - "delim": " ", - "min_level_len": 5, - "min_msg_len": 40, - "enable_color": true, - "enable_caller": true - }, - "levels": [ - {"id": 5, "name": "debug"}, - {"id": 4, "name": "info", "color": 36}, - {"id": 3, "name": "warn"}, - {"id": 2, "name": "error", "color": 31}, - {"id": 1, "name": "fatal", "stacktrace": true}, - {"id": 0, "name": "panic", "stacktrace": true} - ] - } - }` -} From 403ba9cf08f4a6ac1155e8cc18e7daa790012165 Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Fri, 28 Jun 2024 17:59:45 +0530 Subject: [PATCH 09/21] refactor: removed auth login password changepassword code --- server/app/auth.go | 158 ----------------------- server/app/auth_test.go | 143 +------------------- server/integrationtests/clienttestlib.go | 6 - server/model/auth.go | 5 - server/services/auth/email.go | 13 -- server/services/auth/password.go | 106 --------------- server/services/auth/password_test.go | 145 --------------------- 7 files changed, 1 insertion(+), 575 deletions(-) delete mode 100644 server/services/auth/email.go delete mode 100644 server/services/auth/password.go delete mode 100644 server/services/auth/password_test.go diff --git a/server/app/auth.go b/server/app/auth.go index 42f77a2b3..dcd481073 100644 --- a/server/app/auth.go +++ b/server/app/auth.go @@ -2,10 +2,6 @@ package app import ( "github.com/mattermost/mattermost-plugin-boards/server/model" - "github.com/mattermost/mattermost-plugin-boards/server/services/auth" - "github.com/mattermost/mattermost-plugin-boards/server/utils" - - "github.com/mattermost/mattermost/server/public/shared/mlog" "github.com/pkg/errors" ) @@ -75,157 +71,3 @@ func (a *App) GetUsersList(userIDs []string) ([]*model.User, error) { } return users, nil } - -// Login create a new user session if the authentication data is valid. -func (a *App) Login(username, email, password, mfaToken string) (string, error) { - var user *model.User - if username != "" { - var err error - user, err = a.store.GetUserByUsername(username) - if err != nil && !model.IsErrNotFound(err) { - a.metrics.IncrementLoginFailCount(1) - return "", errors.Wrap(err, "invalid username or password") - } - } - - if user == nil && email != "" { - var err error - user, err = a.store.GetUserByEmail(email) - if err != nil && model.IsErrNotFound(err) { - a.metrics.IncrementLoginFailCount(1) - return "", errors.Wrap(err, "invalid username or password") - } - } - - if user == nil { - a.metrics.IncrementLoginFailCount(1) - return "", errors.New("invalid username or password") - } - - if !auth.ComparePassword(user.Password, password) { - a.metrics.IncrementLoginFailCount(1) - a.logger.Debug("Invalid password for user", mlog.String("userID", user.ID)) - return "", errors.New("invalid username or password") - } - - authService := user.AuthService - if authService == "" { - authService = "native" - } - - session := model.Session{ - ID: utils.NewID(utils.IDTypeSession), - Token: utils.NewID(utils.IDTypeToken), - UserID: user.ID, - AuthService: authService, - Props: map[string]interface{}{}, - } - err := a.store.CreateSession(&session) - if err != nil { - return "", errors.Wrap(err, "unable to create session") - } - - a.metrics.IncrementLoginCount(1) - - // TODO: MFA verification - return session.Token, nil -} - -// Logout invalidates the user session. -func (a *App) Logout(sessionID string) error { - err := a.store.DeleteSession(sessionID) - if err != nil { - return errors.Wrap(err, "unable to delete the session") - } - - a.metrics.IncrementLogoutCount(1) - - return nil -} - -// RegisterUser creates a new user if the provided data is valid. -func (a *App) RegisterUser(username, email, password string) error { - var user *model.User - if username != "" { - var err error - user, err = a.store.GetUserByUsername(username) - if err != nil && !model.IsErrNotFound(err) { - return err - } - if user != nil { - return errors.New("The username already exists") - } - } - - if user == nil && email != "" { - var err error - user, err = a.store.GetUserByEmail(email) - if err != nil && !model.IsErrNotFound(err) { - return err - } - if user != nil { - return errors.New("The email already exists") - } - } - - // TODO: Move this into the config - passwordSettings := auth.PasswordSettings{ - MinimumLength: 6, - } - - err := auth.IsPasswordValid(password, passwordSettings) - if err != nil { - return errors.Wrap(err, "Invalid password") - } - - _, err = a.store.CreateUser(&model.User{ - ID: utils.NewID(utils.IDTypeUser), - Username: username, - Email: email, - Password: auth.HashPassword(password), - MfaSecret: "", - AuthService: a.config.AuthMode, - AuthData: "", - }) - if err != nil { - return errors.Wrap(err, "Unable to create the new user") - } - - return nil -} - -func (a *App) UpdateUserPassword(username, password string) error { - err := a.store.UpdateUserPassword(username, auth.HashPassword(password)) - if err != nil { - return err - } - - return nil -} - -func (a *App) ChangePassword(userID, oldPassword, newPassword string) error { - var user *model.User - if userID != "" { - var err error - user, err = a.store.GetUserByID(userID) - if err != nil { - return errors.Wrap(err, "invalid username or password") - } - } - - if user == nil { - return errors.New("invalid username or password") - } - - if !auth.ComparePassword(user.Password, oldPassword) { - a.logger.Debug("Invalid password for user", mlog.String("userID", user.ID)) - return errors.New("invalid username or password") - } - - err := a.store.UpdateUserPasswordByID(userID, auth.HashPassword(newPassword)) - if err != nil { - return errors.Wrap(err, "unable to update password") - } - - return nil -} diff --git a/server/app/auth_test.go b/server/app/auth_test.go index f63b08749..9819f2d39 100644 --- a/server/app/auth_test.go +++ b/server/app/auth_test.go @@ -3,9 +3,7 @@ package app import ( "testing" - "github.com/golang/mock/gomock" "github.com/mattermost/mattermost-plugin-boards/server/model" - "github.com/mattermost/mattermost-plugin-boards/server/services/auth" "github.com/mattermost/mattermost-plugin-boards/server/utils" "github.com/pkg/errors" "github.com/stretchr/testify/require" @@ -15,46 +13,7 @@ var mockUser = &model.User{ ID: utils.NewID(utils.IDTypeUser), Username: "testUsername", Email: "testEmail", - Password: auth.HashPassword("testPassword"), -} - -func TestLogin(t *testing.T) { - th, tearDown := SetupTestHelper(t) - defer tearDown() - - testcases := []struct { - title string - userName string - email string - password string - mfa string - isError bool - }{ - {"fail, missing login information", "", "", "", "", true}, - {"fail, invalid username", "badUsername", "", "", "", true}, - {"fail, invalid email", "", "badEmail", "", "", true}, - {"fail, invalid password", "testUsername", "", "badPassword", "", true}, - {"success, using username", "testUsername", "", "testPassword", "", false}, - {"success, using email", "", "testEmail", "testPassword", "", false}, - } - - th.Store.EXPECT().GetUserByUsername("badUsername").Return(nil, errors.New("Bad Username")) - th.Store.EXPECT().GetUserByEmail("badEmail").Return(nil, errors.New("Bad Email")) - th.Store.EXPECT().GetUserByUsername("testUsername").Return(mockUser, nil).Times(2) - th.Store.EXPECT().GetUserByEmail("testEmail").Return(mockUser, nil) - th.Store.EXPECT().CreateSession(gomock.Any()).Return(nil).Times(2) - - for _, test := range testcases { - t.Run(test.title, func(t *testing.T) { - token, err := th.App.Login(test.userName, test.email, test.password, test.mfa) - if test.isError { - require.Error(t, err) - } else { - require.NoError(t, err) - require.NotNil(t, token) - } - }) - } + Password: "testPassword", } func TestGetUser(t *testing.T) { @@ -86,103 +45,3 @@ func TestGetUser(t *testing.T) { }) } } - -func TestRegisterUser(t *testing.T) { - th, tearDown := SetupTestHelper(t) - defer tearDown() - - testcases := []struct { - title string - userName string - email string - password string - isError bool - }{ - {"fail, missing login information", "", "", "", true}, - {"fail, username exists", "existingUsername", "", "", true}, - {"fail, email exists", "", "existingEmail", "", true}, - {"fail, invalid password", "newUsername", "", "test", true}, - {"success, using email", "", "newEmail", "testPassword", false}, - } - - th.Store.EXPECT().GetUserByUsername("existingUsername").Return(mockUser, nil) - th.Store.EXPECT().GetUserByUsername("newUsername").Return(mockUser, errors.New("user not found")) - th.Store.EXPECT().GetUserByEmail("existingEmail").Return(mockUser, nil) - th.Store.EXPECT().GetUserByEmail("newEmail").Return(nil, model.NewErrNotFound("user")) - th.Store.EXPECT().CreateUser(gomock.Any()).Return(nil, nil) - - for _, test := range testcases { - t.Run(test.title, func(t *testing.T) { - err := th.App.RegisterUser(test.userName, test.email, test.password) - if test.isError { - require.Error(t, err) - } else { - require.NoError(t, err) - } - }) - } -} - -func TestUpdateUserPassword(t *testing.T) { - th, tearDown := SetupTestHelper(t) - defer tearDown() - - testcases := []struct { - title string - userName string - password string - isError bool - }{ - {"fail, missing login information", "", "", true}, - {"fail, invalid username", "badUsername", "", true}, - {"success, username", "testUsername", "testPassword", false}, - } - - th.Store.EXPECT().UpdateUserPassword("", gomock.Any()).Return(errors.New("user not found")) - th.Store.EXPECT().UpdateUserPassword("badUsername", gomock.Any()).Return(errors.New("user not found")) - th.Store.EXPECT().UpdateUserPassword("testUsername", gomock.Any()).Return(nil) - - for _, test := range testcases { - t.Run(test.title, func(t *testing.T) { - err := th.App.UpdateUserPassword(test.userName, test.password) - if test.isError { - require.Error(t, err) - } else { - require.NoError(t, err) - } - }) - } -} - -func TestChangePassword(t *testing.T) { - th, tearDown := SetupTestHelper(t) - defer tearDown() - - testcases := []struct { - title string - userName string - oldPassword string - password string - isError bool - }{ - {"fail, missing login information", "", "", "", true}, - {"fail, invalid userId", "badID", "", "", true}, - {"fail, invalid password", mockUser.ID, "wrongPassword", "newPassword", true}, - {"success, using username", mockUser.ID, "testPassword", "newPassword", false}, - } - - th.Store.EXPECT().GetUserByID("badID").Return(nil, errors.New("userID not found")) - th.Store.EXPECT().GetUserByID(mockUser.ID).Return(mockUser, nil).Times(2) - th.Store.EXPECT().UpdateUserPasswordByID(mockUser.ID, gomock.Any()).Return(nil) - - for _, test := range testcases { - t.Run(test.title, func(t *testing.T) { - err := th.App.ChangePassword(test.userName, test.oldPassword, test.password) - if test.isError { - require.Error(t, err) - } else { - require.NoError(t, err) - } - }) - } -} diff --git a/server/integrationtests/clienttestlib.go b/server/integrationtests/clienttestlib.go index cc60195bc..debcb6fe5 100644 --- a/server/integrationtests/clienttestlib.go +++ b/server/integrationtests/clienttestlib.go @@ -11,7 +11,6 @@ import ( "github.com/mattermost/mattermost-plugin-boards/server/client" "github.com/mattermost/mattermost-plugin-boards/server/model" "github.com/mattermost/mattermost-plugin-boards/server/server" - "github.com/mattermost/mattermost-plugin-boards/server/services/auth" "github.com/mattermost/mattermost-plugin-boards/server/services/config" "github.com/mattermost/mattermost-plugin-boards/server/services/permissions/localpermissions" "github.com/mattermost/mattermost-plugin-boards/server/services/permissions/mmpermissions" @@ -255,9 +254,6 @@ func newTestServerLocalMode() *server.Server { panic(err) } - // Reduce password has strength for unit tests to dramatically speed up account creation and login - auth.PasswordHashStrength = 4 - return srv } @@ -363,8 +359,6 @@ func (th *TestHelper) Start() *TestHelper { // InitBasic starts the test server and initializes the clients of the // helper, registering them and logging them into the system. func (th *TestHelper) InitBasic() *TestHelper { - // Reduce password has strength for unit tests to dramatically speed up account creation and login - auth.PasswordHashStrength = 4 th.Start() diff --git a/server/model/auth.go b/server/model/auth.go index 629701c7d..99ca61f7f 100644 --- a/server/model/auth.go +++ b/server/model/auth.go @@ -5,8 +5,6 @@ import ( "fmt" "io" "strings" - - "github.com/mattermost/mattermost-plugin-boards/server/services/auth" ) const ( @@ -95,9 +93,6 @@ func (rd *RegisterRequest) IsValid() error { if strings.TrimSpace(rd.Email) == "" { return NewErrAuthParam("email is required") } - if !auth.IsEmailValid(rd.Email) { - return NewErrAuthParam("invalid email format") - } if rd.Password == "" { return NewErrAuthParam("password is required") } diff --git a/server/services/auth/email.go b/server/services/auth/email.go deleted file mode 100644 index f8f1d930f..000000000 --- a/server/services/auth/email.go +++ /dev/null @@ -1,13 +0,0 @@ -package auth - -import "regexp" - -var emailRegex = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$") - -// IsEmailValid checks if the email provided passes the required structure and length. -func IsEmailValid(e string) bool { - if len(e) < 3 || len(e) > 254 { - return false - } - return emailRegex.MatchString(e) -} diff --git a/server/services/auth/password.go b/server/services/auth/password.go deleted file mode 100644 index 6f371eda0..000000000 --- a/server/services/auth/password.go +++ /dev/null @@ -1,106 +0,0 @@ -package auth - -import ( - "fmt" - "strings" - - "golang.org/x/crypto/bcrypt" -) - -const ( - PasswordMaximumLength = 64 - PasswordSpecialChars = "!\"\\#$%&'()*+,-./:;<=>?@[]^_`|~" //nolint:gosec - PasswordNumbers = "0123456789" - PasswordUpperCaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - PasswordLowerCaseLetters = "abcdefghijklmnopqrstuvwxyz" - PasswordAllChars = PasswordSpecialChars + PasswordNumbers + PasswordUpperCaseLetters + PasswordLowerCaseLetters - - InvalidLowercasePassword = "lowercase" - InvalidMinLengthPassword = "min-length" - InvalidMaxLengthPassword = "max-length" - InvalidNumberPassword = "number" - InvalidUppercasePassword = "uppercase" - InvalidSymbolPassword = "symbol" -) - -var PasswordHashStrength = 10 - -// HashPassword generates a hash using the bcrypt.GenerateFromPassword. -func HashPassword(password string) string { - hash, err := bcrypt.GenerateFromPassword([]byte(password), PasswordHashStrength) - if err != nil { - panic(err) - } - - return string(hash) -} - -// ComparePassword compares the hash. -func ComparePassword(hash, password string) bool { - if len(password) == 0 || len(hash) == 0 { - return false - } - - err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) - return err == nil -} - -type InvalidPasswordError struct { - FailingCriterias []string -} - -func (ipe *InvalidPasswordError) Error() string { - return fmt.Sprintf("invalid password, failing criteria: %s", strings.Join(ipe.FailingCriterias, ", ")) -} - -type PasswordSettings struct { - MinimumLength int - Lowercase bool - Number bool - Uppercase bool - Symbol bool -} - -func IsPasswordValid(password string, settings PasswordSettings) error { - err := &InvalidPasswordError{ - FailingCriterias: []string{}, - } - - if len(password) < settings.MinimumLength { - err.FailingCriterias = append(err.FailingCriterias, InvalidMinLengthPassword) - } - - if len(password) > PasswordMaximumLength { - err.FailingCriterias = append(err.FailingCriterias, InvalidMaxLengthPassword) - } - - if settings.Lowercase { - if !strings.ContainsAny(password, PasswordLowerCaseLetters) { - err.FailingCriterias = append(err.FailingCriterias, InvalidLowercasePassword) - } - } - - if settings.Uppercase { - if !strings.ContainsAny(password, PasswordUpperCaseLetters) { - err.FailingCriterias = append(err.FailingCriterias, InvalidUppercasePassword) - } - } - - if settings.Number { - if !strings.ContainsAny(password, PasswordNumbers) { - err.FailingCriterias = append(err.FailingCriterias, InvalidNumberPassword) - } - } - - if settings.Symbol { - if !strings.ContainsAny(password, PasswordSpecialChars) { - err.FailingCriterias = append(err.FailingCriterias, InvalidSymbolPassword) - } - } - - if len(err.FailingCriterias) > 0 { - return err - } - - return nil -} diff --git a/server/services/auth/password_test.go b/server/services/auth/password_test.go deleted file mode 100644 index 52697ab26..000000000 --- a/server/services/auth/password_test.go +++ /dev/null @@ -1,145 +0,0 @@ -package auth - -import ( - "strings" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestPasswordHash(t *testing.T) { - hash := HashPassword("Test") - - assert.True(t, ComparePassword(hash, "Test"), "Passwords don't match") - assert.False(t, ComparePassword(hash, "Test2"), "Passwords should not have matched") -} - -func TestIsPasswordValidWithSettings(t *testing.T) { - for name, tc := range map[string]struct { - Password string - Settings PasswordSettings - ExpectedFailingCriterias []string - }{ - "Short": { - Password: strings.Repeat("x", 3), - Settings: PasswordSettings{ - MinimumLength: 3, - Lowercase: false, - Uppercase: false, - Number: false, - Symbol: false, - }, - }, - "Long": { - Password: strings.Repeat("x", PasswordMaximumLength), - Settings: PasswordSettings{ - MinimumLength: 3, - Lowercase: false, - Uppercase: false, - Number: false, - Symbol: false, - }, - }, - "TooShort": { - Password: strings.Repeat("x", 2), - Settings: PasswordSettings{ - MinimumLength: 3, - Lowercase: false, - Uppercase: false, - Number: false, - Symbol: false, - }, - ExpectedFailingCriterias: []string{"min-length"}, - }, - "TooLong": { - Password: strings.Repeat("x", PasswordMaximumLength+1), - Settings: PasswordSettings{ - MinimumLength: 3, - Lowercase: false, - Uppercase: false, - Number: false, - Symbol: false, - }, - ExpectedFailingCriterias: []string{"max-length"}, - }, - "MissingLower": { - Password: "AAAAAAAAAAASD123!@#", - Settings: PasswordSettings{ - MinimumLength: 3, - Lowercase: true, - Uppercase: false, - Number: false, - Symbol: false, - }, - ExpectedFailingCriterias: []string{"lowercase"}, - }, - "MissingUpper": { - Password: "aaaaaaaaaaaaasd123!@#", - Settings: PasswordSettings{ - MinimumLength: 3, - Uppercase: true, - Lowercase: false, - Number: false, - Symbol: false, - }, - ExpectedFailingCriterias: []string{"uppercase"}, - }, - "MissingNumber": { - Password: "asasdasdsadASD!@#", - Settings: PasswordSettings{ - MinimumLength: 3, - Number: true, - Lowercase: false, - Uppercase: false, - Symbol: false, - }, - ExpectedFailingCriterias: []string{"number"}, - }, - "MissingSymbol": { - Password: "asdasdasdasdasdASD123", - Settings: PasswordSettings{ - MinimumLength: 3, - Symbol: true, - Lowercase: false, - Uppercase: false, - Number: false, - }, - ExpectedFailingCriterias: []string{"symbol"}, - }, - "MissingMultiple": { - Password: "asdasdasdasdasdasd", - Settings: PasswordSettings{ - MinimumLength: 3, - Lowercase: true, - Uppercase: true, - Number: true, - Symbol: true, - }, - ExpectedFailingCriterias: []string{"uppercase", "number", "symbol"}, - }, - "Everything": { - Password: "asdASD!@#123", - Settings: PasswordSettings{ - MinimumLength: 3, - Lowercase: true, - Uppercase: true, - Number: true, - Symbol: true, - }, - }, - } { - t.Run(name, func(t *testing.T) { - err := IsPasswordValid(tc.Password, tc.Settings) - if len(tc.ExpectedFailingCriterias) == 0 { - assert.NoError(t, err) - } else { - require.Error(t, err) - var errFC *InvalidPasswordError - if assert.ErrorAs(t, err, &errFC) { - assert.Equal(t, tc.ExpectedFailingCriterias, errFC.FailingCriterias) - } - } - }) - } -} From f2b54b82e8662c9b4f367c76643c11eebf831d48 Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Fri, 28 Jun 2024 18:07:45 +0530 Subject: [PATCH 10/21] chore: linter fixes --- server/integrationtests/clienttestlib.go | 1 - 1 file changed, 1 deletion(-) diff --git a/server/integrationtests/clienttestlib.go b/server/integrationtests/clienttestlib.go index debcb6fe5..4d24f2bb3 100644 --- a/server/integrationtests/clienttestlib.go +++ b/server/integrationtests/clienttestlib.go @@ -359,7 +359,6 @@ func (th *TestHelper) Start() *TestHelper { // InitBasic starts the test server and initializes the clients of the // helper, registering them and logging them into the system. func (th *TestHelper) InitBasic() *TestHelper { - th.Start() // user1 From 70467de3f355c29901d63ebd64b251239af7dda4 Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Sun, 30 Jun 2024 14:59:23 +0530 Subject: [PATCH 11/21] refactor: removed user login register code --- server/client/client.go | 51 ------- server/integrationtests/board_test.go | 18 --- server/integrationtests/cards_test.go | 7 - server/integrationtests/clienttestlib.go | 44 ------ server/integrationtests/compliance_test.go | 2 - server/integrationtests/file_test.go | 1 - server/integrationtests/permissions_test.go | 13 -- server/integrationtests/sharing_test.go | 3 - server/integrationtests/user_test.go | 144 -------------------- 9 files changed, 283 deletions(-) diff --git a/server/client/client.go b/server/client/client.go index 83cbe59e3..0ac0b44ef 100644 --- a/server/client/client.go +++ b/server/client/client.go @@ -523,43 +523,6 @@ func (c *Client) PostSharing(sharing *model.Sharing) (bool, *Response) { return true, BuildResponse(r) } -func (c *Client) GetRegisterRoute() string { - return "/register" -} - -func (c *Client) Register(request *model.RegisterRequest) (bool, *Response) { - r, err := c.DoAPIPost(c.GetRegisterRoute(), toJSON(&request)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - - return true, BuildResponse(r) -} - -func (c *Client) GetLoginRoute() string { - return "/login" -} - -func (c *Client) Login(request *model.LoginRequest) (*model.LoginResponse, *Response) { - r, err := c.DoAPIPost(c.GetLoginRoute(), toJSON(&request)) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - defer closeBody(r) - - data, err := model.LoginResponseFromJSON(r.Body) - if err != nil { - return nil, BuildErrorResponse(r, err) - } - - if data.Token != "" { - c.Token = data.Token - } - - return data, BuildResponse(r) -} - func (c *Client) GetMeRoute() string { return "/users/me" } @@ -624,20 +587,6 @@ func (c *Client) GetUserList(ids []string) ([]model.User, *Response) { return users, BuildResponse(r) } -func (c *Client) GetUserChangePasswordRoute(id string) string { - return fmt.Sprintf("/users/%s/changepassword", id) -} - -func (c *Client) UserChangePassword(id string, data *model.ChangePasswordRequest) (bool, *Response) { - r, err := c.DoAPIPost(c.GetUserChangePasswordRoute(id), toJSON(&data)) - if err != nil { - return false, BuildErrorResponse(r, err) - } - defer closeBody(r) - - return true, BuildResponse(r) -} - func (c *Client) CreateBoard(board *model.Board) (*model.Board, *Response) { r, err := c.DoAPIPost(c.GetBoardsRoute(), toJSON(board)) if err != nil { diff --git a/server/integrationtests/board_test.go b/server/integrationtests/board_test.go index 3bf9fc9fb..9d01a1a83 100644 --- a/server/integrationtests/board_test.go +++ b/server/integrationtests/board_test.go @@ -17,7 +17,6 @@ func TestGetBoards(t *testing.T) { t.Run("a non authenticated client should be rejected", func(t *testing.T) { th := SetupTestHelper(t).InitBasic() defer th.TearDown() - th.Logout(th.Client) teamID := "0" newBoard := &model.Board{ @@ -117,7 +116,6 @@ func TestCreateBoard(t *testing.T) { t.Run("a non authenticated user should be rejected", func(t *testing.T) { th := SetupTestHelper(t).InitBasic() defer th.TearDown() - th.Logout(th.Client) newBoard := &model.Board{ Title: "board title", @@ -475,7 +473,6 @@ func TestSearchBoards(t *testing.T) { t.Run("a non authenticated user should be rejected", func(t *testing.T) { th := SetupTestHelper(t).InitBasic() defer th.TearDown() - th.Logout(th.Client) boards, resp := th.Client.SearchBoardsForTeam(testTeamID, "term") th.CheckUnauthorized(resp) @@ -587,7 +584,6 @@ func TestGetBoard(t *testing.T) { t.Run("a non authenticated user should be rejected", func(t *testing.T) { th := SetupTestHelper(t).InitBasic() defer th.TearDown() - th.Logout(th.Client) board, resp := th.Client.GetBoard("boar-id", "") th.CheckUnauthorized(resp) @@ -621,9 +617,6 @@ func TestGetBoard(t *testing.T) { th.CheckOK(resp) require.True(t, success) - // the client logs out - th.Logout(th.Client) - // we make sure that the client cannot currently retrieve the // board with no session board, resp = th.Client.GetBoard(rBoard.ID, "") @@ -702,7 +695,6 @@ func TestGetBoardMetadata(t *testing.T) { t.Run("a non authenticated user should be rejected", func(t *testing.T) { th := SetupTestHelperWithLicense(t, LicenseEnterprise).InitBasic() defer th.TearDown() - th.Logout(th.Client) boardMetadata, resp := th.Client.GetBoardMetadata("boar-id", "") th.CheckUnauthorized(resp) @@ -861,9 +853,6 @@ func TestGetBoardMetadata(t *testing.T) { th.CheckOK(resp) require.True(t, success) - // the client logs out - th.Logout(th.Client) - // we make sure that the client cannot currently retrieve the // board with no session boardMetadata, resp := th.Client.GetBoardMetadata(rBoard.ID, "") @@ -883,7 +872,6 @@ func TestPatchBoard(t *testing.T) { t.Run("a non authenticated user should be rejected", func(t *testing.T) { th := SetupTestHelper(t).InitBasic() defer th.TearDown() - th.Logout(th.Client) initialTitle := "title 1" newBoard := &model.Board{ @@ -998,7 +986,6 @@ func TestDeleteBoard(t *testing.T) { t.Run("a non authenticated user should be rejected", func(t *testing.T) { th := SetupTestHelper(t).InitBasic() defer th.TearDown() - th.Logout(th.Client) newBoard := &model.Board{ Title: "title", @@ -1076,7 +1063,6 @@ func TestUndeleteBoard(t *testing.T) { t.Run("a non authenticated user should be rejected", func(t *testing.T) { th := SetupTestHelper(t).InitBasic() defer th.TearDown() - th.Logout(th.Client) newBoard := &model.Board{ Title: "title", @@ -1225,7 +1211,6 @@ func TestGetMembersForBoard(t *testing.T) { th := SetupTestHelper(t).InitBasic() defer th.TearDown() board := createBoardWithUsers(th) - th.Logout(th.Client) members, resp := th.Client.GetMembersForBoard(board.ID) th.CheckUnauthorized(resp) @@ -1270,7 +1255,6 @@ func TestAddMember(t *testing.T) { t.Run("a non authenticated user should be rejected", func(t *testing.T) { th := SetupTestHelper(t).InitBasic() defer th.TearDown() - th.Logout(th.Client) newBoard := &model.Board{ Title: "title", @@ -1490,7 +1474,6 @@ func TestUpdateMember(t *testing.T) { SchemeEditor: true, } - th.Logout(th.Client) member, resp := th.Client.UpdateBoardMember(updatedMember) th.CheckUnauthorized(resp) require.Nil(t, member) @@ -1665,7 +1648,6 @@ func TestDeleteMember(t *testing.T) { BoardID: board.ID, } - th.Logout(th.Client) success, resp := th.Client.DeleteBoardMember(member) th.CheckUnauthorized(resp) require.False(t, success) diff --git a/server/integrationtests/cards_test.go b/server/integrationtests/cards_test.go index ac3fa5186..7a212a518 100644 --- a/server/integrationtests/cards_test.go +++ b/server/integrationtests/cards_test.go @@ -17,7 +17,6 @@ func TestCreateCard(t *testing.T) { defer th.TearDown() board := th.CreateBoard(testTeamID, model.BoardTypeOpen) - th.Logout(th.Client) card := &model.Card{ Title: "basic card", @@ -157,8 +156,6 @@ func TestGetCards(t *testing.T) { }) t.Run("a non authenticated user should be rejected", func(t *testing.T) { - th.Logout(th.Client) - cards, resp := th.Client.GetCards(board.ID, 0, 10) th.CheckUnauthorized(resp) require.Nil(t, cards) @@ -173,8 +170,6 @@ func TestPatchCard(t *testing.T) { _, cards := th.CreateBoardAndCards(testTeamID, model.BoardTypeOpen, 1) card := cards[0] - th.Logout(th.Client) - newTitle := "another title" patch := &model.CardPatch{ Title: &newTitle, @@ -243,8 +238,6 @@ func TestGetCard(t *testing.T) { _, cards := th.CreateBoardAndCards(testTeamID, model.BoardTypeOpen, 1) card := cards[0] - th.Logout(th.Client) - cardFetched, resp := th.Client.GetCard(card.ID) th.CheckUnauthorized(resp) require.Nil(t, cardFetched) diff --git a/server/integrationtests/clienttestlib.go b/server/integrationtests/clienttestlib.go index 4d24f2bb3..380485763 100644 --- a/server/integrationtests/clienttestlib.go +++ b/server/integrationtests/clienttestlib.go @@ -361,18 +361,12 @@ func (th *TestHelper) Start() *TestHelper { func (th *TestHelper) InitBasic() *TestHelper { th.Start() - // user1 - th.RegisterAndLogin(th.Client, user1Username, "user1@sample.com", password, "") - // get token team, resp := th.Client.GetTeam(model.GlobalTeamID) th.CheckOK(resp) require.NotNil(th.T, team) require.NotNil(th.T, team.SignupToken) - // user2 - th.RegisterAndLogin(th.Client2, user2Username, "user2@sample.com", password, team.SignupToken) - return th } @@ -399,44 +393,6 @@ func (th *TestHelper) TearDown() { } } -func (th *TestHelper) RegisterAndLogin(client *client.Client, username, email, password, token string) { - req := &model.RegisterRequest{ - Username: username, - Email: email, - Password: password, - Token: token, - } - - success, resp := th.Client.Register(req) - th.CheckOK(resp) - require.True(th.T, success) - - th.Login(client, username, password) -} - -func (th *TestHelper) Login(client *client.Client, username, password string) { - req := &model.LoginRequest{ - Type: "normal", - Username: username, - Password: password, - } - data, resp := client.Login(req) - th.CheckOK(resp) - require.NotNil(th.T, data) -} - -func (th *TestHelper) Login1() { - th.Login(th.Client, user1Username, password) -} - -func (th *TestHelper) Login2() { - th.Login(th.Client2, user2Username, password) -} - -func (th *TestHelper) Logout(client *client.Client) { - client.Token = "" -} - func (th *TestHelper) Me(client *client.Client) *model.User { user, resp := client.GetMe() th.CheckOK(resp) diff --git a/server/integrationtests/compliance_test.go b/server/integrationtests/compliance_test.go index 32425e450..b4d99f054 100644 --- a/server/integrationtests/compliance_test.go +++ b/server/integrationtests/compliance_test.go @@ -47,7 +47,6 @@ func TestGetBoardsForCompliance(t *testing.T) { defer th.TearDown() _ = th.CreateBoards(testTeamID, model.BoardTypeOpen, 2) - th.Logout(th.Client) bcr, resp := clients.Anon.GetBoardsForCompliance(testTeamID, 0, 0) @@ -134,7 +133,6 @@ func TestGetBoardsComplianceHistory(t *testing.T) { defer th.TearDown() _ = th.CreateBoards(testTeamID, model.BoardTypeOpen, 2) - th.Logout(th.Client) bchr, resp := clients.Anon.GetBoardsComplianceHistory(utils.GetMillis()-OneDay, true, testTeamID, 0, 0) diff --git a/server/integrationtests/file_test.go b/server/integrationtests/file_test.go index 2a431d820..fa03e987b 100644 --- a/server/integrationtests/file_test.go +++ b/server/integrationtests/file_test.go @@ -17,7 +17,6 @@ func TestUploadFile(t *testing.T) { t.Run("a non authenticated user should be rejected", func(t *testing.T) { th := SetupTestHelper(t).InitBasic() defer th.TearDown() - th.Logout(th.Client) file, resp := th.Client.TeamUploadFile(testTeamID, "test-board-id", bytes.NewBuffer([]byte("test"))) th.CheckUnauthorized(resp) diff --git a/server/integrationtests/permissions_test.go b/server/integrationtests/permissions_test.go index aca374a4f..a983bb25e 100644 --- a/server/integrationtests/permissions_test.go +++ b/server/integrationtests/permissions_test.go @@ -91,7 +91,6 @@ func setupClients(th *TestHelper) Clients { func setupLocalClients(th *TestHelper) Clients { th.Client = client.NewClient(th.Server.Config().ServerRoot, "") - th.RegisterAndLogin(th.Client, "sysadmin", "sysadmin@sample.com", password, "") clients := Clients{ Anon: client.NewClient(th.Server.Config().ServerRoot, ""), @@ -109,23 +108,11 @@ func setupLocalClients(th *TestHelper) Clients { th.CheckOK(resp) require.NotNil(th.T, team) require.NotNil(th.T, team.SignupToken) - - th.RegisterAndLogin(clients.NoTeamMember, userNoTeamMember, userNoTeamMember+"@sample.com", password, team.SignupToken) userNoTeamMemberID = clients.NoTeamMember.GetUserID() - - th.RegisterAndLogin(clients.TeamMember, userTeamMember, userTeamMember+"@sample.com", password, team.SignupToken) userTeamMemberID = clients.TeamMember.GetUserID() - - th.RegisterAndLogin(clients.Viewer, userViewer, userViewer+"@sample.com", password, team.SignupToken) userViewerID = clients.Viewer.GetUserID() - - th.RegisterAndLogin(clients.Commenter, userCommenter, userCommenter+"@sample.com", password, team.SignupToken) userCommenterID = clients.Commenter.GetUserID() - - th.RegisterAndLogin(clients.Editor, userEditor, userEditor+"@sample.com", password, team.SignupToken) userEditorID = clients.Editor.GetUserID() - - th.RegisterAndLogin(clients.Admin, userAdmin, userAdmin+"@sample.com", password, team.SignupToken) userAdminID = clients.Admin.GetUserID() return clients diff --git a/server/integrationtests/sharing_test.go b/server/integrationtests/sharing_test.go index 2288dd3be..81e527efc 100644 --- a/server/integrationtests/sharing_test.go +++ b/server/integrationtests/sharing_test.go @@ -16,15 +16,12 @@ func TestSharing(t *testing.T) { token := utils.NewID(utils.IDTypeToken) t.Run("an unauthenticated client should not be able to get a sharing", func(t *testing.T) { - th.Logout(th.Client) - sharing, resp := th.Client.GetSharing("board-id") th.CheckUnauthorized(resp) require.Nil(t, sharing) }) t.Run("Check no initial sharing", func(t *testing.T) { - th.Login1() teamID := "0" newBoard := &model.Board{ diff --git a/server/integrationtests/user_test.go b/server/integrationtests/user_test.go index e2246ec2a..d3fd69d49 100644 --- a/server/integrationtests/user_test.go +++ b/server/integrationtests/user_test.go @@ -15,68 +15,6 @@ const ( fakeEmail = "mock@test.com" ) -func TestUserRegister(t *testing.T) { - th := SetupTestHelper(t).Start() - defer th.TearDown() - - // register - registerRequest := &model.RegisterRequest{ - Username: fakeUsername, - Email: fakeEmail, - Password: utils.NewID(utils.IDTypeNone), - } - success, resp := th.Client.Register(registerRequest) - require.NoError(t, resp.Error) - require.True(t, success) - - // register again will fail - success, resp = th.Client.Register(registerRequest) - require.Error(t, resp.Error) - require.False(t, success) -} - -func TestUserLogin(t *testing.T) { - th := SetupTestHelper(t).Start() - defer th.TearDown() - - t.Run("with nonexist user", func(t *testing.T) { - loginRequest := &model.LoginRequest{ - Type: "normal", - Username: "nonexistuser", - Email: "", - Password: utils.NewID(utils.IDTypeNone), - } - data, resp := th.Client.Login(loginRequest) - require.Error(t, resp.Error) - require.Nil(t, data) - }) - - t.Run("with registered user", func(t *testing.T) { - password := utils.NewID(utils.IDTypeNone) - // register - registerRequest := &model.RegisterRequest{ - Username: fakeUsername, - Email: fakeEmail, - Password: password, - } - success, resp := th.Client.Register(registerRequest) - require.NoError(t, resp.Error) - require.True(t, success) - - // login - loginRequest := &model.LoginRequest{ - Type: "normal", - Username: fakeUsername, - Email: fakeEmail, - Password: password, - } - data, resp := th.Client.Login(loginRequest) - require.NoError(t, resp.Error) - require.NotNil(t, data) - require.NotNil(t, data.Token) - }) -} - func TestGetMe(t *testing.T) { th := SetupTestHelper(t).Start() defer th.TearDown() @@ -86,65 +24,12 @@ func TestGetMe(t *testing.T) { require.Error(t, resp.Error) require.Nil(t, me) }) - - t.Run("logged in", func(t *testing.T) { - // register - password := utils.NewID(utils.IDTypeNone) - registerRequest := &model.RegisterRequest{ - Username: fakeUsername, - Email: fakeEmail, - Password: password, - } - success, resp := th.Client.Register(registerRequest) - require.NoError(t, resp.Error) - require.True(t, success) - // login - loginRequest := &model.LoginRequest{ - Type: "normal", - Username: fakeUsername, - Email: fakeEmail, - Password: password, - } - data, resp := th.Client.Login(loginRequest) - require.NoError(t, resp.Error) - require.NotNil(t, data) - require.NotNil(t, data.Token) - - // get user me - me, resp := th.Client.GetMe() - require.NoError(t, resp.Error) - require.NotNil(t, me) - require.Equal(t, "", me.Email) - require.Equal(t, registerRequest.Username, me.Username) - }) } func TestGetUser(t *testing.T) { th := SetupTestHelper(t).Start() defer th.TearDown() - // register - password := utils.NewID(utils.IDTypeNone) - registerRequest := &model.RegisterRequest{ - Username: fakeUsername, - Email: fakeEmail, - Password: password, - } - success, resp := th.Client.Register(registerRequest) - require.NoError(t, resp.Error) - require.True(t, success) - // login - loginRequest := &model.LoginRequest{ - Type: "normal", - Username: fakeUsername, - Email: fakeEmail, - Password: password, - } - data, resp := th.Client.Login(loginRequest) - require.NoError(t, resp.Error) - require.NotNil(t, data) - require.NotNil(t, data.Token) - me, resp := th.Client.GetMe() require.NoError(t, resp.Error) require.NotNil(t, me) @@ -241,39 +126,10 @@ func TestUserChangePassword(t *testing.T) { th := SetupTestHelper(t).Start() defer th.TearDown() - // register - password := utils.NewID(utils.IDTypeNone) - registerRequest := &model.RegisterRequest{ - Username: fakeUsername, - Email: fakeEmail, - Password: password, - } - success, resp := th.Client.Register(registerRequest) - require.NoError(t, resp.Error) - require.True(t, success) - // login - loginRequest := &model.LoginRequest{ - Type: "normal", - Username: fakeUsername, - Email: fakeEmail, - Password: password, - } - data, resp := th.Client.Login(loginRequest) - require.NoError(t, resp.Error) - require.NotNil(t, data) - require.NotNil(t, data.Token) - originalMe, resp := th.Client.GetMe() require.NoError(t, resp.Error) require.NotNil(t, originalMe) - // change password - success, resp = th.Client.UserChangePassword(originalMe.ID, &model.ChangePasswordRequest{ - OldPassword: password, - NewPassword: utils.NewID(utils.IDTypeNone), - }) - require.NoError(t, resp.Error) - require.True(t, success) } func randomBytes(t *testing.T, n int) []byte { From ef9438aec69d39aa8b83b52b4ec54afba1f0666e Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Mon, 1 Jul 2024 11:50:32 +0530 Subject: [PATCH 12/21] chore: linter fixes --- server/integrationtests/sharing_test.go | 1 - server/integrationtests/user_test.go | 6 ------ 2 files changed, 7 deletions(-) diff --git a/server/integrationtests/sharing_test.go b/server/integrationtests/sharing_test.go index 81e527efc..687343fff 100644 --- a/server/integrationtests/sharing_test.go +++ b/server/integrationtests/sharing_test.go @@ -22,7 +22,6 @@ func TestSharing(t *testing.T) { }) t.Run("Check no initial sharing", func(t *testing.T) { - teamID := "0" newBoard := &model.Board{ TeamID: teamID, diff --git a/server/integrationtests/user_test.go b/server/integrationtests/user_test.go index d3fd69d49..28b3c0344 100644 --- a/server/integrationtests/user_test.go +++ b/server/integrationtests/user_test.go @@ -10,11 +10,6 @@ import ( "github.com/stretchr/testify/require" ) -const ( - fakeUsername = "fakeUsername" - fakeEmail = "mock@test.com" -) - func TestGetMe(t *testing.T) { th := SetupTestHelper(t).Start() defer th.TearDown() @@ -129,7 +124,6 @@ func TestUserChangePassword(t *testing.T) { originalMe, resp := th.Client.GetMe() require.NoError(t, resp.Error) require.NotNil(t, originalMe) - } func randomBytes(t *testing.T, n int) []byte { From 2a1a362dd1d3be71b675e1b8a6787af5d277adb4 Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Mon, 1 Jul 2024 12:31:19 +0530 Subject: [PATCH 13/21] refactor: removed auth layer --- server/api/api_test.go | 1 - server/integrationtests/permissions_test.go | 100 --------------- server/model/auth.go | 130 -------------------- server/model/error.go | 6 - 4 files changed, 237 deletions(-) delete mode 100644 server/model/auth.go diff --git a/server/api/api_test.go b/server/api/api_test.go index 6426ebc23..7652dfbef 100644 --- a/server/api/api_test.go +++ b/server/api/api_test.go @@ -27,7 +27,6 @@ func TestErrorResponse(t *testing.T) { // bad request {"ErrBadRequest", model.NewErrBadRequest("bad field"), http.StatusBadRequest, "bad field"}, {"ErrViewsLimitReached", model.ErrViewsLimitReached, http.StatusBadRequest, "limit reached"}, - {"ErrAuthParam", model.NewErrAuthParam("password is required"), http.StatusBadRequest, "password is required"}, {"ErrInvalidCategory", model.NewErrInvalidCategory("open"), http.StatusBadRequest, "open"}, {"ErrBoardMemberIsLastAdmin", model.ErrBoardMemberIsLastAdmin, http.StatusBadRequest, "no admins"}, {"ErrBoardIDMismatch", model.ErrBoardIDMismatch, http.StatusBadRequest, "Board IDs do not match"}, diff --git a/server/integrationtests/permissions_test.go b/server/integrationtests/permissions_test.go index a983bb25e..be6874de9 100644 --- a/server/integrationtests/permissions_test.go +++ b/server/integrationtests/permissions_test.go @@ -2644,36 +2644,6 @@ func TestPermissionsGetUser(t *testing.T) { }) } -func TestPermissionsUserChangePassword(t *testing.T) { - postBody := toJSON(t, model.ChangePasswordRequest{ - OldPassword: password, - NewPassword: "newpa$$word123", - }) - - t.Run("plugin", func(t *testing.T) { - th := SetupTestHelperPluginMode(t) - defer th.TearDown() - clients := setupClients(th) - testData := setupData(t, th) - ttCases := []TestCase{ - {"/users/{USER_ADMIN_ID}/changepassword", methodPost, postBody, userAnon, http.StatusUnauthorized, 0}, - {"/users/{USER_ADMIN_ID}/changepassword", methodPost, postBody, userAdmin, http.StatusNotImplemented, 0}, - } - runTestCases(t, ttCases, testData, clients) - }) - t.Run("local", func(t *testing.T) { - th := SetupTestHelperLocalMode(t) - defer th.TearDown() - clients := setupLocalClients(th) - testData := setupData(t, th) - ttCases := []TestCase{ - {"/users/{USER_ADMIN_ID}/changepassword", methodPost, postBody, userAnon, http.StatusUnauthorized, 0}, - {"/users/{USER_ADMIN_ID}/changepassword", methodPost, postBody, userAdmin, http.StatusOK, 0}, - } - runTestCases(t, ttCases, testData, clients) - }) -} - func TestPermissionsUpdateUserConfig(t *testing.T) { patch := toJSON(t, model.UserPreferencesPatch{UpdatedFields: map[string]string{"test": "test"}}) @@ -2860,39 +2830,6 @@ func TestPermissionsDeleteBoardsAndBlocks(t *testing.T) { }) } -func TestPermissionsLogin(t *testing.T) { - loginReq := func(username, password string) string { - return toJSON(t, model.LoginRequest{ - Type: "normal", - Username: username, - Password: password, - }) - } - - t.Run("plugin", func(t *testing.T) { - th := SetupTestHelperPluginMode(t) - defer th.TearDown() - clients := setupClients(th) - testData := setupData(t, th) - ttCases := []TestCase{ - {"/login", methodPost, loginReq(userAnon, password), userAnon, http.StatusNotImplemented, 0}, - {"/login", methodPost, loginReq(userAdmin, password), userAdmin, http.StatusNotImplemented, 0}, - } - runTestCases(t, ttCases, testData, clients) - }) - t.Run("local", func(t *testing.T) { - th := SetupTestHelperLocalMode(t) - defer th.TearDown() - clients := setupLocalClients(th) - testData := setupData(t, th) - ttCases := []TestCase{ - {"/login", methodPost, loginReq(userAnon, password), userAnon, http.StatusUnauthorized, 0}, - {"/login", methodPost, loginReq(userAdmin, password), userAdmin, http.StatusOK, 1}, - } - runTestCases(t, ttCases, testData, clients) - }) -} - func TestPermissionsLogout(t *testing.T) { t.Run("plugin", func(t *testing.T) { th := SetupTestHelperPluginMode(t) @@ -2918,43 +2855,6 @@ func TestPermissionsLogout(t *testing.T) { }) } -func TestPermissionsRegister(t *testing.T) { - t.Run("plugin", func(t *testing.T) { - th := SetupTestHelperPluginMode(t) - defer th.TearDown() - clients := setupClients(th) - testData := setupData(t, th) - ttCases := []TestCase{ - {"/register", methodPost, "", userAnon, http.StatusNotImplemented, 0}, - {"/register", methodPost, "", userAdmin, http.StatusNotImplemented, 0}, - } - runTestCases(t, ttCases, testData, clients) - }) - t.Run("local", func(t *testing.T) { - th := SetupTestHelperLocalMode(t) - defer th.TearDown() - clients := setupLocalClients(th) - testData := setupData(t, th) - - team, resp := th.Client.GetTeam(model.GlobalTeamID) - th.CheckOK(resp) - require.NotNil(th.T, team) - require.NotNil(th.T, team.SignupToken) - - postData := toJSON(t, model.RegisterRequest{ - Username: "newuser", - Email: "newuser@test.com", - Password: password, - Token: team.SignupToken, - }) - - ttCases := []TestCase{ - {"/register", methodPost, postData, userAnon, http.StatusOK, 0}, - } - runTestCases(t, ttCases, testData, clients) - }) -} - func TestPermissionsClientConfig(t *testing.T) { ttCases := []TestCase{ {"/clientConfig", methodGet, "", userAnon, http.StatusOK, 1}, diff --git a/server/model/auth.go b/server/model/auth.go deleted file mode 100644 index 99ca61f7f..000000000 --- a/server/model/auth.go +++ /dev/null @@ -1,130 +0,0 @@ -package model - -import ( - "encoding/json" - "fmt" - "io" - "strings" -) - -const ( - MinimumPasswordLength = 8 -) - -func NewErrAuthParam(msg string) *ErrAuthParam { - return &ErrAuthParam{ - msg: msg, - } -} - -type ErrAuthParam struct { - msg string -} - -func (pe *ErrAuthParam) Error() string { - return pe.msg -} - -// LoginRequest is a login request -// swagger:model -type LoginRequest struct { - // Type of login, currently must be set to "normal" - // required: true - Type string `json:"type"` - - // If specified, login using username - // required: false - Username string `json:"username"` - - // If specified, login using email - // required: false - Email string `json:"email"` - - // Password - // required: true - Password string `json:"password"` - - // MFA token - // required: false - // swagger:ignore - MfaToken string `json:"mfa_token"` -} - -// LoginResponse is a login response -// swagger:model -type LoginResponse struct { - // Session token - // required: true - Token string `json:"token"` -} - -func LoginResponseFromJSON(data io.Reader) (*LoginResponse, error) { - var resp LoginResponse - if err := json.NewDecoder(data).Decode(&resp); err != nil { - return nil, err - } - return &resp, nil -} - -// RegisterRequest is a user registration request -// swagger:model -type RegisterRequest struct { - // User name - // required: true - Username string `json:"username"` - - // User's email - // required: true - Email string `json:"email"` - - // Password - // required: true - Password string `json:"password"` - - // Registration authorization token - // required: true - Token string `json:"token"` -} - -func (rd *RegisterRequest) IsValid() error { - if strings.TrimSpace(rd.Username) == "" { - return NewErrAuthParam("username is required") - } - if strings.TrimSpace(rd.Email) == "" { - return NewErrAuthParam("email is required") - } - if rd.Password == "" { - return NewErrAuthParam("password is required") - } - return isValidPassword(rd.Password) -} - -// ChangePasswordRequest is a user password change request -// swagger:model -type ChangePasswordRequest struct { - // Old password - // required: true - OldPassword string `json:"oldPassword"` - - // New password - // required: true - NewPassword string `json:"newPassword"` -} - -// IsValid validates a password change request. -func (rd *ChangePasswordRequest) IsValid() error { - if rd.OldPassword == "" { - return NewErrAuthParam("old password is required") - } - if rd.NewPassword == "" { - return NewErrAuthParam("new password is required") - } - return isValidPassword(rd.NewPassword) -} - -func isValidPassword(password string) error { - if len(password) < MinimumPasswordLength { - return NewErrAuthParam(fmt.Sprintf("password must be at least %d characters", MinimumPasswordLength)) - } - return nil -} diff --git a/server/model/error.go b/server/model/error.go index 772c90450..aad437c94 100644 --- a/server/model/error.go +++ b/server/model/error.go @@ -185,12 +185,6 @@ func IsErrBadRequest(err error) bool { return true } - // check if this is a model.ErrAuthParam - var ap *ErrAuthParam - if errors.As(err, &ap) { - return true - } - // check if this is a model.ErrInvalidCategory var ic *ErrInvalidCategory if errors.As(err, &ic) { From e718d7e959cbb670f38cba411b0577012e6ff8e0 Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Mon, 1 Jul 2024 14:07:43 +0530 Subject: [PATCH 14/21] refactor: removed isSingle user checks --- server/api/sharing.go | 8 ---- server/api/teams.go | 36 +++++---------- server/api/users.go | 44 ++++--------------- server/app/import.go | 3 -- server/model/block.go | 4 -- server/model/user.go | 1 - .../mattermostauthlayer.go | 1 + .../services/store/sqlstore/public_methods.go | 2 + server/ws/server.go | 30 ++----------- server/ws/server_test.go | 7 --- 10 files changed, 27 insertions(+), 109 deletions(-) diff --git a/server/api/sharing.go b/server/api/sharing.go index 8d79633e5..8b83cc9b3 100644 --- a/server/api/sharing.go +++ b/server/api/sharing.go @@ -147,15 +147,7 @@ func (a *API) handlePostSharing(w http.ResponseWriter, r *http.Request) { // Stamp ModifiedBy modifiedBy := userID - if userID == model.SingleUser { - modifiedBy = "" - } sharing.ModifiedBy = modifiedBy - - if userID == model.SingleUser { - userID = "" - } - if !a.app.GetClientConfig().EnablePublicSharedBoards { a.logger.Warn( "Attempt to turn on sharing for board via API failed, sharing off in configuration.", diff --git a/server/api/teams.go b/server/api/teams.go index ccf87ec90..81d1179bc 100644 --- a/server/api/teams.go +++ b/server/api/teams.go @@ -8,7 +8,6 @@ import ( "github.com/gorilla/mux" "github.com/mattermost/mattermost-plugin-boards/server/model" "github.com/mattermost/mattermost-plugin-boards/server/services/audit" - "github.com/mattermost/mattermost-plugin-boards/server/utils" ) func (a *API) registerTeamsRoutes(r *mux.Router) { @@ -268,31 +267,18 @@ func (a *API) handleGetTeamUsersByID(w http.ResponseWriter, r *http.Request) { return } - if userIDs[0] == model.SingleUser { - ws, _ := a.app.GetRootTeam() - now := utils.GetMillis() - user := &model.User{ - ID: model.SingleUser, - Username: model.SingleUser, - Email: model.SingleUser, - CreateAt: ws.UpdateAt, - UpdateAt: now, - } - users = append(users, user) - } else { - users, error = a.app.GetUsersList(userIDs) - if error != nil { - a.errorResponse(w, r, error) - return - } + users, error = a.app.GetUsersList(userIDs) + if error != nil { + a.errorResponse(w, r, error) + return + } - for i, u := range users { - if a.permissions.HasPermissionToTeam(u.ID, teamID, model.PermissionManageTeam) { - users[i].Permissions = append(users[i].Permissions, model.PermissionManageTeam.Id) - } - if a.permissions.HasPermissionTo(u.ID, model.PermissionManageSystem) { - users[i].Permissions = append(users[i].Permissions, model.PermissionManageSystem.Id) - } + for i, u := range users { + if a.permissions.HasPermissionToTeam(u.ID, teamID, model.PermissionManageTeam) { + users[i].Permissions = append(users[i].Permissions, model.PermissionManageTeam.Id) + } + if a.permissions.HasPermissionTo(u.ID, model.PermissionManageSystem) { + users[i].Permissions = append(users[i].Permissions, model.PermissionManageSystem.Id) } } diff --git a/server/api/users.go b/server/api/users.go index 52780c6e2..8610dc59c 100644 --- a/server/api/users.go +++ b/server/api/users.go @@ -8,7 +8,6 @@ import ( "github.com/gorilla/mux" "github.com/mattermost/mattermost-plugin-boards/server/model" "github.com/mattermost/mattermost-plugin-boards/server/services/audit" - "github.com/mattermost/mattermost-plugin-boards/server/utils" ) func (a *API) registerUsersRoutes(r *mux.Router) { @@ -70,23 +69,10 @@ func (a *API) handleGetUsersList(w http.ResponseWriter, r *http.Request) { return } - if userIDs[0] == model.SingleUser { - ws, _ := a.app.GetRootTeam() - now := utils.GetMillis() - user := &model.User{ - ID: model.SingleUser, - Username: model.SingleUser, - Email: model.SingleUser, - CreateAt: ws.UpdateAt, - UpdateAt: now, - } - users = append(users, user) - } else { - users, error = a.app.GetUsersList(userIDs) - if error != nil { - a.errorResponse(w, r, error) - return - } + users, error = a.app.GetUsersList(userIDs) + if error != nil { + a.errorResponse(w, r, error) + return } ctx := r.Context() @@ -163,23 +149,11 @@ func (a *API) handleGetMe(w http.ResponseWriter, r *http.Request) { auditRec := a.makeAuditRecord(r, "getMe", audit.Fail) defer a.audit.LogRecord(audit.LevelRead, auditRec) - if userID == model.SingleUser { - ws, _ := a.app.GetRootTeam() - now := utils.GetMillis() - user = &model.User{ - ID: model.SingleUser, - Username: model.SingleUser, - Email: model.SingleUser, - CreateAt: ws.UpdateAt, - UpdateAt: now, - } - } else { - user, err = a.app.GetUser(userID) - if err != nil { - // ToDo: wrap with an invalid token error - a.errorResponse(w, r, err) - return - } + user, err = a.app.GetUser(userID) + if err != nil { + // ToDo: wrap with an invalid token error + a.errorResponse(w, r, err) + return } if teamID != "" && a.permissions.HasPermissionToTeam(userID, teamID, model.PermissionManageTeam) { diff --git a/server/app/import.go b/server/app/import.go index 325cca68d..47b300af3 100644 --- a/server/app/import.go +++ b/server/app/import.go @@ -159,9 +159,6 @@ func (a *App) ImportBoardJSONL(r io.Reader, opt model.ImportArchiveOptions) (*mo scanner := bufio.NewScanner(lineReader) userID := opt.ModifiedBy - if userID == model.SingleUser { - userID = "" - } now := utils.GetMillis() var boardID string var boardMembers []*model.BoardMember diff --git a/server/model/block.go b/server/model/block.go index 1a1e4bb50..f03845520 100644 --- a/server/model/block.go +++ b/server/model/block.go @@ -245,10 +245,6 @@ type QueryBlockHistoryChildOptions struct { } func StampModificationMetadata(userID string, blocks []*Block, auditRec *audit.Record) { - if userID == SingleUser { - userID = "" - } - now := GetMillis() for i := range blocks { blocks[i].ModifiedBy = userID diff --git a/server/model/user.go b/server/model/user.go index 1840e082f..d73e57455 100644 --- a/server/model/user.go +++ b/server/model/user.go @@ -6,7 +6,6 @@ import ( ) const ( - SingleUser = "single-user" GlobalTeamID = "0" SystemUserID = "system" PreferencesCategoryFocalboard = "focalboard" diff --git a/server/services/store/mattermostauthlayer/mattermostauthlayer.go b/server/services/store/mattermostauthlayer/mattermostauthlayer.go index bc3248743..24c2785e2 100644 --- a/server/services/store/mattermostauthlayer/mattermostauthlayer.go +++ b/server/services/store/mattermostauthlayer/mattermostauthlayer.go @@ -369,6 +369,7 @@ func (s *MattermostAuthLayer) GetUsersByTeam(teamID string, asGuestID string, sh } func (s *MattermostAuthLayer) GetUsersList(userIDs []string, showEmail, showName bool) ([]*model.User, error) { + fmt.Println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") query := s.baseUserQuery(showEmail, showName). Where(sq.Eq{"u.id": userIDs}) diff --git a/server/services/store/sqlstore/public_methods.go b/server/services/store/sqlstore/public_methods.go index 45e1b53ca..92340e8b6 100644 --- a/server/services/store/sqlstore/public_methods.go +++ b/server/services/store/sqlstore/public_methods.go @@ -14,6 +14,7 @@ package sqlstore import ( "context" + "fmt" "time" "github.com/mattermost/mattermost-plugin-boards/server/model" @@ -584,6 +585,7 @@ func (s *SQLStore) GetUsersByTeam(teamID string, asGuestID string, showEmail boo } func (s *SQLStore) GetUsersList(userIDs []string, showEmail bool, showName bool) ([]*model.User, error) { + fmt.Println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$") return s.getUsersList(s.db, userIDs, showEmail, showName) } diff --git a/server/ws/server.go b/server/ws/server.go index 39ec010e6..b39f15854 100644 --- a/server/ws/server.go +++ b/server/ws/server.go @@ -49,7 +49,6 @@ type Server struct { listenersByBlock map[string][]*websocketSession mu sync.RWMutex auth *auth.Auth - singleUserToken string isMattermostAuth bool logger mlog.LoggerIFace store Store @@ -79,7 +78,6 @@ func NewServer(auth *auth.Auth, singleUserToken string, isMattermostAuth bool, l }, }, auth: auth, - singleUserToken: singleUserToken, isMattermostAuth: isMattermostAuth, logger: logger, store: store, @@ -212,22 +210,10 @@ func (ws *Server) handleWebSocket(w http.ResponseWriter, r *http.Request) { mlog.String("teamID", command.TeamID), mlog.Stringer("client", wsSession.conn.RemoteAddr()), ) - - // if single user mode, check that the userID is valid and - // assume that the user has permission if so - if len(ws.singleUserToken) != 0 { - if wsSession.userID != model.SingleUser { - continue - } - - // if not in single user mode validate that the session - // has permissions to the team - } else { - ws.logger.Debug("Not single user mode") - if !ws.auth.DoesUserHaveTeamAccess(wsSession.userID, command.TeamID) { - ws.logger.Error("WS user doesn't have team access", mlog.String("teamID", command.TeamID), mlog.String("userID", wsSession.userID)) - continue - } + ws.logger.Debug("Not single user mode") + if !ws.auth.DoesUserHaveTeamAccess(wsSession.userID, command.TeamID) { + ws.logger.Error("WS user doesn't have team access", mlog.String("teamID", command.TeamID), mlog.String("userID", wsSession.userID)) + continue } ws.subscribeListenerToTeam(wsSession, command.TeamID) @@ -416,14 +402,6 @@ func (ws *Server) removeListenerFromBlock(listener *websocketSession, blockID st } func (ws *Server) getUserIDForToken(token string) string { - if len(ws.singleUserToken) > 0 { - if token == ws.singleUserToken { - return model.SingleUser - } else { - return "" - } - } - session, err := ws.auth.GetSession(token) if session == nil || err != nil { return "" diff --git a/server/ws/server_test.go b/server/ws/server_test.go index d8a1d89a6..b345d0796 100644 --- a/server/ws/server_test.go +++ b/server/ws/server_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/mattermost/mattermost-plugin-boards/server/auth" - "github.com/mattermost/mattermost-plugin-boards/server/model" "github.com/mattermost/mattermost/server/public/shared/mlog" @@ -265,9 +264,7 @@ func TestBlocksSubscription(t *testing.T) { } func TestGetUserIDForTokenInSingleUserMode(t *testing.T) { - singleUserToken := "single-user-token" server := NewServer(&auth.Auth{}, "token", false, &mlog.Logger{}, nil) - server.singleUserToken = singleUserToken t.Run("Should return nothing if the token is empty", func(t *testing.T) { require.Empty(t, server.getUserIDForToken("")) @@ -276,8 +273,4 @@ func TestGetUserIDForTokenInSingleUserMode(t *testing.T) { t.Run("Should return nothing if the token is invalid", func(t *testing.T) { require.Empty(t, server.getUserIDForToken("invalid-token")) }) - - t.Run("Should return the single user ID if the token is correct", func(t *testing.T) { - require.Equal(t, model.SingleUser, server.getUserIDForToken(singleUserToken)) - }) } From 084839a28b787263ba7883879838eb66805d414f Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Mon, 1 Jul 2024 14:35:30 +0530 Subject: [PATCH 15/21] chore: removed debugging logs --- server/services/store/sqlstore/public_methods.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/services/store/sqlstore/public_methods.go b/server/services/store/sqlstore/public_methods.go index 92340e8b6..45e1b53ca 100644 --- a/server/services/store/sqlstore/public_methods.go +++ b/server/services/store/sqlstore/public_methods.go @@ -14,7 +14,6 @@ package sqlstore import ( "context" - "fmt" "time" "github.com/mattermost/mattermost-plugin-boards/server/model" @@ -585,7 +584,6 @@ func (s *SQLStore) GetUsersByTeam(teamID string, asGuestID string, showEmail boo } func (s *SQLStore) GetUsersList(userIDs []string, showEmail bool, showName bool) ([]*model.User, error) { - fmt.Println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$") return s.getUsersList(s.db, userIDs, showEmail, showName) } From 3b46c4b2024df733d4376d0a688efcd3b77fc269 Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Mon, 1 Jul 2024 14:35:50 +0530 Subject: [PATCH 16/21] chore: removed debugging logs --- server/services/store/mattermostauthlayer/mattermostauthlayer.go | 1 - 1 file changed, 1 deletion(-) diff --git a/server/services/store/mattermostauthlayer/mattermostauthlayer.go b/server/services/store/mattermostauthlayer/mattermostauthlayer.go index 24c2785e2..bc3248743 100644 --- a/server/services/store/mattermostauthlayer/mattermostauthlayer.go +++ b/server/services/store/mattermostauthlayer/mattermostauthlayer.go @@ -369,7 +369,6 @@ func (s *MattermostAuthLayer) GetUsersByTeam(teamID string, asGuestID string, sh } func (s *MattermostAuthLayer) GetUsersList(userIDs []string, showEmail, showName bool) ([]*model.User, error) { - fmt.Println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") query := s.baseUserQuery(showEmail, showName). Where(sq.Eq{"u.id": userIDs}) From 64af4d51625fd2fc257ad57f0ab2f5e4eaad6357 Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Fri, 5 Jul 2024 10:16:07 +0530 Subject: [PATCH 17/21] revert: isGuestUser check --- server/api/api.go | 4 ++++ server/api/archive.go | 18 ++++++++++++++- server/api/blocks.go | 13 +++++++++++ server/api/boards.go | 40 ++++++++++++++++++++++++++++++++- server/api/boards_and_blocks.go | 10 +++++++++ server/api/members.go | 20 +++++++++++++++++ server/api/onboarding.go | 10 +++++++++ server/api/search.go | 16 +++++++++++-- server/api/teams.go | 9 ++++++++ server/api/templates.go | 10 +++++++++ 10 files changed, 146 insertions(+), 4 deletions(-) diff --git a/server/api/api.go b/server/api/api.go index 830f57ff3..4cb0290df 100644 --- a/server/api/api.go +++ b/server/api/api.go @@ -158,6 +158,10 @@ func (a *API) hasValidReadTokenForBoard(r *http.Request, boardID string) bool { return isValid } +func (a *API) userIsGuest(userID string) (bool, error) { + return a.app.UserIsGuest(userID) +} + // Response helpers func (a *API) errorResponse(w http.ResponseWriter, r *http.Request, err error) { diff --git a/server/api/archive.go b/server/api/archive.go index 11590f423..a8bb55435 100644 --- a/server/api/archive.go +++ b/server/api/archive.go @@ -137,6 +137,16 @@ func (a *API) handleArchiveImport(w http.ResponseWriter, r *http.Request) { return } + isGuest, err := a.userIsGuest(userID) + if err != nil { + a.errorResponse(w, r, err) + return + } + if isGuest { + a.errorResponse(w, r, model.NewErrPermission("access denied to create board")) + return + } + file, handle, err := r.FormFile(UploadFormFileKey) if err != nil { fmt.Fprintf(w, "%v", err) @@ -210,7 +220,13 @@ func (a *API) handleArchiveExportTeam(w http.ResponseWriter, r *http.Request) { defer a.audit.LogRecord(audit.LevelRead, auditRec) auditRec.AddMeta("TeamID", teamID) - boards, err := a.app.GetBoardsForUserAndTeam(userID, teamID, false) + isGuest, err := a.userIsGuest(userID) + if err != nil { + a.errorResponse(w, r, err) + return + } + + boards, err := a.app.GetBoardsForUserAndTeam(userID, teamID, !isGuest) if err != nil { a.errorResponse(w, r, err) return diff --git a/server/api/blocks.go b/server/api/blocks.go index 93db90ec5..36732244f 100644 --- a/server/api/blocks.go +++ b/server/api/blocks.go @@ -98,6 +98,19 @@ func (a *API) handleGetBlocks(w http.ResponseWriter, r *http.Request) { return } } + if board.IsTemplate { + var isGuest bool + isGuest, err = a.userIsGuest(userID) + if err != nil { + a.errorResponse(w, r, err) + return + } + + if isGuest { + a.errorResponse(w, r, model.NewErrPermission("guest are not allowed to get board templates")) + return + } + } } auditRec := a.makeAuditRecord(r, "getBlocks", audit.Fail) diff --git a/server/api/boards.go b/server/api/boards.go index 8548f1238..d89cff621 100644 --- a/server/api/boards.go +++ b/server/api/boards.go @@ -63,8 +63,14 @@ func (a *API) handleGetBoards(w http.ResponseWriter, r *http.Request) { defer a.audit.LogRecord(audit.LevelRead, auditRec) auditRec.AddMeta("teamID", teamID) + isGuest, err := a.userIsGuest(userID) + if err != nil { + a.errorResponse(w, r, err) + return + } + // retrieve boards list - boards, err := a.app.GetBoardsForUserAndTeam(userID, teamID, false) + boards, err := a.app.GetBoardsForUserAndTeam(userID, teamID, !isGuest) if err != nil { a.errorResponse(w, r, err) return @@ -141,6 +147,16 @@ func (a *API) handleCreateBoard(w http.ResponseWriter, r *http.Request) { } } + isGuest, err := a.userIsGuest(userID) + if err != nil { + a.errorResponse(w, r, err) + return + } + if isGuest { + a.errorResponse(w, r, model.NewErrPermission("access denied to create board")) + return + } + if err = newBoard.IsValid(); err != nil { a.errorResponse(w, r, model.NewErrBadRequest(err.Error())) return @@ -227,6 +243,18 @@ func (a *API) handleGetBoard(w http.ResponseWriter, r *http.Request) { return } } else { + var isGuest bool + isGuest, err = a.userIsGuest(userID) + if err != nil { + a.errorResponse(w, r, err) + return + } + if isGuest { + if !a.permissions.HasPermissionToBoard(userID, boardID, model.PermissionViewBoard) { + a.errorResponse(w, r, model.NewErrPermission("access denied to board")) + return + } + } if !a.permissions.HasPermissionToTeam(userID, board.TeamID, model.PermissionViewTeam) { a.errorResponse(w, r, model.NewErrPermission("access denied to board")) return @@ -486,6 +514,16 @@ func (a *API) handleDuplicateBoard(w http.ResponseWriter, r *http.Request) { } } + isGuest, err := a.userIsGuest(userID) + if err != nil { + a.errorResponse(w, r, err) + return + } + if isGuest { + a.errorResponse(w, r, model.NewErrPermission("access denied to create board")) + return + } + auditRec := a.makeAuditRecord(r, "duplicateBoard", audit.Fail) defer a.audit.LogRecord(audit.LevelRead, auditRec) auditRec.AddMeta("boardID", boardID) diff --git a/server/api/boards_and_blocks.go b/server/api/boards_and_blocks.go index 19cdd7a3e..8bb57d99e 100644 --- a/server/api/boards_and_blocks.go +++ b/server/api/boards_and_blocks.go @@ -92,6 +92,16 @@ func (a *API) handleCreateBoardsAndBlocks(w http.ResponseWriter, r *http.Request return } + isGuest, err := a.userIsGuest(userID) + if err != nil { + a.errorResponse(w, r, err) + return + } + if isGuest { + a.errorResponse(w, r, model.NewErrPermission("access denied to create board")) + return + } + for _, block := range newBab.Blocks { // Error checking if len(block.Type) < 1 { diff --git a/server/api/members.go b/server/api/members.go index ddde1f7ed..1a5d644d6 100644 --- a/server/api/members.go +++ b/server/api/members.go @@ -257,6 +257,16 @@ func (a *API) handleJoinBoard(w http.ResponseWriter, r *http.Request) { return } + isGuest, err := a.userIsGuest(userID) + if err != nil { + a.errorResponse(w, r, err) + return + } + if isGuest { + a.errorResponse(w, r, model.NewErrPermission("guests not allowed to join boards")) + return + } + newBoardMember := &model.BoardMember{ UserID: userID, BoardID: boardID, @@ -424,6 +434,16 @@ func (a *API) handleUpdateMember(w http.ResponseWriter, r *http.Request) { SchemeViewer: reqBoardMember.SchemeViewer, } + isGuest, err := a.userIsGuest(paramsUserID) + if err != nil { + a.errorResponse(w, r, err) + return + } + + if isGuest { + newBoardMember.SchemeAdmin = false + } + if !a.permissions.HasPermissionToBoard(userID, boardID, model.PermissionManageBoardRoles) { a.errorResponse(w, r, model.NewErrPermission("access denied to modify board members")) return diff --git a/server/api/onboarding.go b/server/api/onboarding.go index 7a9922a3d..bca6e201d 100644 --- a/server/api/onboarding.go +++ b/server/api/onboarding.go @@ -53,6 +53,16 @@ func (a *API) handleOnboard(w http.ResponseWriter, r *http.Request) { return } + isGuest, err := a.userIsGuest(userID) + if err != nil { + a.errorResponse(w, r, err) + return + } + if isGuest { + a.errorResponse(w, r, model.NewErrPermission("access denied to create board")) + return + } + teamID, boardID, err := a.app.PrepareOnboardingTour(userID, teamID) if err != nil { a.errorResponse(w, r, err) diff --git a/server/api/search.go b/server/api/search.go index 86dd16e15..d6ba4560a 100644 --- a/server/api/search.go +++ b/server/api/search.go @@ -161,8 +161,14 @@ func (a *API) handleSearchBoards(w http.ResponseWriter, r *http.Request) { defer a.audit.LogRecord(audit.LevelRead, auditRec) auditRec.AddMeta("teamID", teamID) + isGuest, err := a.userIsGuest(userID) + if err != nil { + a.errorResponse(w, r, err) + return + } + // retrieve boards list - boards, err := a.app.SearchBoardsForUser(term, searchField, userID, false) + boards, err := a.app.SearchBoardsForUser(term, searchField, userID, !isGuest) if err != nil { a.errorResponse(w, r, err) return @@ -314,8 +320,14 @@ func (a *API) handleSearchAllBoards(w http.ResponseWriter, r *http.Request) { auditRec := a.makeAuditRecord(r, "searchAllBoards", audit.Fail) defer a.audit.LogRecord(audit.LevelRead, auditRec) + isGuest, err := a.userIsGuest(userID) + if err != nil { + a.errorResponse(w, r, err) + return + } + // retrieve boards list - boards, err := a.app.SearchBoardsForUser(term, model.BoardSearchFieldTitle, userID, false) + boards, err := a.app.SearchBoardsForUser(term, model.BoardSearchFieldTitle, userID, !isGuest) if err != nil { a.errorResponse(w, r, err) return diff --git a/server/api/teams.go b/server/api/teams.go index 81d1179bc..f722221e1 100644 --- a/server/api/teams.go +++ b/server/api/teams.go @@ -183,7 +183,16 @@ func (a *API) handleGetTeamUsers(w http.ResponseWriter, r *http.Request) { auditRec := a.makeAuditRecord(r, "getUsers", audit.Fail) defer a.audit.LogRecord(audit.LevelRead, auditRec) + isGuest, err := a.userIsGuest(userID) + if err != nil { + a.errorResponse(w, r, err) + return + } asGuestUser := "" + if isGuest { + asGuestUser = userID + } + users, err := a.app.SearchTeamUsers(teamID, searchQuery, asGuestUser, excludeBots) if err != nil { a.errorResponse(w, r, err) diff --git a/server/api/templates.go b/server/api/templates.go index 2ae1734f6..7a1b9d6eb 100644 --- a/server/api/templates.go +++ b/server/api/templates.go @@ -51,6 +51,16 @@ func (a *API) handleGetTemplates(w http.ResponseWriter, r *http.Request) { return } + isGuest, err := a.userIsGuest(userID) + if err != nil { + a.errorResponse(w, r, err) + return + } + if isGuest { + a.errorResponse(w, r, model.NewErrPermission("access denied to templates")) + return + } + auditRec := a.makeAuditRecord(r, "getTemplates", audit.Fail) defer a.audit.LogRecord(audit.LevelRead, auditRec) auditRec.AddMeta("teamID", teamID) From 4735490705809c7d633b5f1e7d8f27048b8f1c5e Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Sat, 13 Jul 2024 11:43:09 +0530 Subject: [PATCH 18/21] Deprecate standalone SQL store level (#4) * refactor: removed the mattermost auth check * refactor: removed standalone sql part of code * refactor: removed not required function from store level * refactor: removed session check for plugin * refactor: removed the unnecessary test * refactor: removed MattermostAuth * Deperate isPlugin and isSingleUser part - 2 (#5) * removed remaining singleUser and isPlugin code * chore: linter fixes --- server/api/api.go | 11 +- server/api/archive.go | 48 +-- server/api/auth.go | 37 +- server/api/blocks.go | 2 +- server/api/boards.go | 2 +- server/api/channels.go | 5 - server/api/files.go | 4 +- server/api/search.go | 10 - server/api/statistics.go | 4 - server/api/teams.go | 20 +- server/app/auth.go | 5 - server/app/helper_test.go | 3 +- server/auth/auth.go | 18 - server/auth/auth_test.go | 74 ---- server/boards/boardsapp.go | 11 +- server/integrationtests/clienttestlib.go | 7 +- server/server/initHandlers.go | 6 - server/server/server.go | 25 +- .../mattermostauthlayer.go | 40 -- server/services/store/mockstore/mockstore.go | 26 -- server/services/store/sqlstore/board.go | 136 ------ .../store/sqlstore/data_migrations.go | 30 +- server/services/store/sqlstore/file.go | 92 ---- .../services/store/sqlstore/helpers_test.go | 1 - server/services/store/sqlstore/migrate.go | 32 +- .../migrationstests/boards_migrator_test.go | 1 - .../sqlstore/migrationstests/helpers_test.go | 18 +- server/services/store/sqlstore/params.go | 4 +- .../services/store/sqlstore/public_methods.go | 99 ++--- server/services/store/sqlstore/session.go | 111 ----- server/services/store/sqlstore/sqlstore.go | 17 - .../services/store/sqlstore/sqlstore_test.go | 1 - server/services/store/sqlstore/team.go | 40 -- server/services/store/sqlstore/user.go | 406 ------------------ server/services/store/store.go | 10 - server/services/store/storetests/session.go | 73 ---- .../store/storetests/subscriptions.go | 325 -------------- server/services/store/storetests/users.go | 268 ------------ server/services/store/storetests/util.go | 17 - server/ws/server.go | 11 +- server/ws/server_test.go | 6 +- webapp/src/pages/changePasswordPage.scss | 73 ---- webapp/src/pages/changePasswordPage.tsx | 99 ----- webapp/src/pages/loginPage.scss | 68 --- webapp/src/pages/loginPage.tsx | 103 ----- webapp/src/pages/registerPage.scss | 69 --- webapp/src/pages/registerPage.tsx | 106 ----- webapp/src/router.tsx | 69 +-- 48 files changed, 110 insertions(+), 2533 deletions(-) delete mode 100644 server/server/initHandlers.go delete mode 100644 server/services/store/sqlstore/file.go delete mode 100644 server/services/store/sqlstore/session.go delete mode 100644 server/services/store/storetests/subscriptions.go delete mode 100644 webapp/src/pages/changePasswordPage.scss delete mode 100644 webapp/src/pages/changePasswordPage.tsx delete mode 100644 webapp/src/pages/loginPage.scss delete mode 100644 webapp/src/pages/loginPage.tsx delete mode 100644 webapp/src/pages/registerPage.scss delete mode 100644 webapp/src/pages/registerPage.tsx diff --git a/server/api/api.go b/server/api/api.go index 4cb0290df..1ef3d4d81 100644 --- a/server/api/api.go +++ b/server/api/api.go @@ -34,12 +34,11 @@ var ( // REST APIs type API struct { - app *app.App - authService string - permissions permissions.PermissionsService - MattermostAuth bool - logger mlog.LoggerIFace - audit *audit.Audit + app *app.App + authService string + permissions permissions.PermissionsService + logger mlog.LoggerIFace + audit *audit.Audit } func NewAPI( diff --git a/server/api/archive.go b/server/api/archive.go index a8bb55435..18be5b400 100644 --- a/server/api/archive.go +++ b/server/api/archive.go @@ -204,51 +204,7 @@ func (a *API) handleArchiveExportTeam(w http.ResponseWriter, r *http.Request) { // description: internal error // schema: // "$ref": "#/definitions/ErrorResponse" - if a.MattermostAuth { - a.errorResponse(w, r, model.NewErrNotImplemented("not permitted in plugin mode")) - return - } - - vars := mux.Vars(r) - teamID := vars["teamID"] - - ctx := r.Context() - session, _ := ctx.Value(sessionContextKey).(*model.Session) - userID := session.UserID - - auditRec := a.makeAuditRecord(r, "archiveExportTeam", audit.Fail) - defer a.audit.LogRecord(audit.LevelRead, auditRec) - auditRec.AddMeta("TeamID", teamID) - - isGuest, err := a.userIsGuest(userID) - if err != nil { - a.errorResponse(w, r, err) - return - } - - boards, err := a.app.GetBoardsForUserAndTeam(userID, teamID, !isGuest) - if err != nil { - a.errorResponse(w, r, err) - return - } - ids := []string{} - for _, board := range boards { - ids = append(ids, board.ID) - } - - opts := model.ExportArchiveOptions{ - TeamID: teamID, - BoardIDs: ids, - } - - filename := fmt.Sprintf("archive-%s%s", time.Now().Format("2006-01-02"), archiveExtension) - w.Header().Set("Content-Type", "application/octet-stream") - w.Header().Set("Content-Disposition", "attachment; filename="+filename) - w.Header().Set("Content-Transfer-Encoding", "binary") + a.errorResponse(w, r, model.NewErrNotImplemented("not permitted in plugin mode")) + return - if err := a.app.ExportArchive(w, opts); err != nil { - a.errorResponse(w, r, err) - } - - auditRec.Success() } diff --git a/server/api/auth.go b/server/api/auth.go index 837ce3b3d..44f4a2dd9 100644 --- a/server/api/auth.go +++ b/server/api/auth.go @@ -5,21 +5,16 @@ import ( "net/http" "github.com/mattermost/mattermost-plugin-boards/server/model" - "github.com/mattermost/mattermost-plugin-boards/server/services/auth" "github.com/mattermost/mattermost-plugin-boards/server/utils" - - "github.com/mattermost/mattermost/server/public/shared/mlog" ) func (a *API) sessionRequired(handler func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { - return a.attachSession(handler, true) + return a.attachSession(handler) } -func (a *API) attachSession(handler func(w http.ResponseWriter, r *http.Request), required bool) func(w http.ResponseWriter, r *http.Request) { +func (a *API) attachSession(handler func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) { - token, _ := auth.ParseAuthTokenFromRequest(r) - - if a.MattermostAuth && r.Header.Get("Mattermost-User-Id") != "" { + if r.Header.Get("Mattermost-User-Id") != "" { userID := r.Header.Get("Mattermost-User-Id") now := utils.GetMillis() session := &model.Session{ @@ -37,30 +32,6 @@ func (a *API) attachSession(handler func(w http.ResponseWriter, r *http.Request) return } - session, err := a.app.GetSession(token) - if err != nil { - if required { - a.errorResponse(w, r, model.NewErrUnauthorized(err.Error())) - return - } - - handler(w, r) - return - } - - authService := session.AuthService - if authService != a.authService { - msg := `Session authService mismatch` - a.logger.Error(msg, - mlog.String("sessionID", session.ID), - mlog.String("want", a.authService), - mlog.String("got", authService), - ) - a.errorResponse(w, r, model.NewErrUnauthorized(msg)) - return - } - - ctx := context.WithValue(r.Context(), sessionContextKey, session) - handler(w, r.WithContext(ctx)) + a.errorResponse(w, r, model.NewErrUnauthorized("Unauthorized")) } } diff --git a/server/api/blocks.go b/server/api/blocks.go index 36732244f..772382d59 100644 --- a/server/api/blocks.go +++ b/server/api/blocks.go @@ -16,7 +16,7 @@ import ( func (a *API) registerBlocksRoutes(r *mux.Router) { // Blocks APIs - r.HandleFunc("/boards/{boardID}/blocks", a.attachSession(a.handleGetBlocks, false)).Methods("GET") + r.HandleFunc("/boards/{boardID}/blocks", a.attachSession(a.handleGetBlocks)).Methods("GET") r.HandleFunc("/boards/{boardID}/blocks", a.sessionRequired(a.handlePostBlocks)).Methods("POST") r.HandleFunc("/boards/{boardID}/blocks", a.sessionRequired(a.handlePatchBlocks)).Methods("PATCH") r.HandleFunc("/boards/{boardID}/blocks/{blockID}", a.sessionRequired(a.handleDeleteBlock)).Methods("DELETE") diff --git a/server/api/boards.go b/server/api/boards.go index d89cff621..a4912dbef 100644 --- a/server/api/boards.go +++ b/server/api/boards.go @@ -15,7 +15,7 @@ import ( func (a *API) registerBoardsRoutes(r *mux.Router) { r.HandleFunc("/teams/{teamID}/boards", a.sessionRequired(a.handleGetBoards)).Methods("GET") r.HandleFunc("/boards", a.sessionRequired(a.handleCreateBoard)).Methods("POST") - r.HandleFunc("/boards/{boardID}", a.attachSession(a.handleGetBoard, false)).Methods("GET") + r.HandleFunc("/boards/{boardID}", a.attachSession(a.handleGetBoard)).Methods("GET") r.HandleFunc("/boards/{boardID}", a.sessionRequired(a.handlePatchBoard)).Methods("PATCH") r.HandleFunc("/boards/{boardID}", a.sessionRequired(a.handleDeleteBoard)).Methods("DELETE") r.HandleFunc("/boards/{boardID}/duplicate", a.sessionRequired(a.handleDuplicateBoard)).Methods("POST") diff --git a/server/api/channels.go b/server/api/channels.go index 234e5b769..d9acc3c77 100644 --- a/server/api/channels.go +++ b/server/api/channels.go @@ -50,11 +50,6 @@ func (a *API) handleGetChannel(w http.ResponseWriter, r *http.Request) { // schema: // "$ref": "#/definitions/ErrorResponse" - if !a.MattermostAuth { - a.errorResponse(w, r, model.NewErrNotImplemented("not permitted in standalone mode")) - return - } - teamID := mux.Vars(r)["teamID"] channelID := mux.Vars(r)["channelID"] userID := getUserID(r) diff --git a/server/api/files.go b/server/api/files.go index f270916d2..58c0161dd 100644 --- a/server/api/files.go +++ b/server/api/files.go @@ -75,8 +75,8 @@ func FileInfoResponseFromJSON(data io.Reader) (*mmModel.FileInfo, error) { func (a *API) registerFilesRoutes(r *mux.Router) { // Files API - r.HandleFunc("/files/teams/{teamID}/{boardID}/{filename}", a.attachSession(a.handleServeFile, false)).Methods("GET") - r.HandleFunc("/files/teams/{teamID}/{boardID}/{filename}/info", a.attachSession(a.getFileInfo, false)).Methods("GET") + r.HandleFunc("/files/teams/{teamID}/{boardID}/{filename}", a.attachSession(a.handleServeFile)).Methods("GET") + r.HandleFunc("/files/teams/{teamID}/{boardID}/{filename}/info", a.attachSession(a.getFileInfo)).Methods("GET") r.HandleFunc("/teams/{teamID}/{boardID}/files", a.sessionRequired(a.handleUploadFile)).Methods("POST") } diff --git a/server/api/search.go b/server/api/search.go index d6ba4560a..5a964fa1b 100644 --- a/server/api/search.go +++ b/server/api/search.go @@ -51,11 +51,6 @@ func (a *API) handleSearchMyChannels(w http.ResponseWriter, r *http.Request) { // schema: // "$ref": "#/definitions/ErrorResponse" - if !a.MattermostAuth { - a.errorResponse(w, r, model.NewErrNotImplemented("not permitted in standalone mode")) - return - } - query := r.URL.Query() searchQuery := query.Get("search") @@ -226,11 +221,6 @@ func (a *API) handleSearchLinkableBoards(w http.ResponseWriter, r *http.Request) // schema: // "$ref": "#/definitions/ErrorResponse" - if !a.MattermostAuth { - a.errorResponse(w, r, model.NewErrNotImplemented("not permitted in standalone mode")) - return - } - teamID := mux.Vars(r)["teamID"] term := r.URL.Query().Get("q") userID := getUserID(r) diff --git a/server/api/statistics.go b/server/api/statistics.go index bf773eaa9..9f4a10b8f 100644 --- a/server/api/statistics.go +++ b/server/api/statistics.go @@ -33,10 +33,6 @@ func (a *API) handleStatistics(w http.ResponseWriter, r *http.Request) { // description: internal error // schema: // "$ref": "#/definitions/ErrorResponse" - if !a.MattermostAuth { - a.errorResponse(w, r, model.NewErrNotImplemented("not permitted in standalone mode")) - return - } // user must have right to access analytics userID := getUserID(r) diff --git a/server/api/teams.go b/server/api/teams.go index f722221e1..f41936ae4 100644 --- a/server/api/teams.go +++ b/server/api/teams.go @@ -100,20 +100,12 @@ func (a *API) handleGetTeam(w http.ResponseWriter, r *http.Request) { var team *model.Team var err error - if a.MattermostAuth { - team, err = a.app.GetTeam(teamID) - if model.IsErrNotFound(err) { - a.errorResponse(w, r, model.NewErrUnauthorized("invalid team")) - } - if err != nil { - a.errorResponse(w, r, err) - } - } else { - team, err = a.app.GetRootTeam() - if err != nil { - a.errorResponse(w, r, err) - return - } + team, err = a.app.GetTeam(teamID) + if model.IsErrNotFound(err) { + a.errorResponse(w, r, model.NewErrUnauthorized("invalid team")) + } + if err != nil { + a.errorResponse(w, r, err) } auditRec := a.makeAuditRecord(r, "getTeam", audit.Fail) diff --git a/server/app/auth.go b/server/app/auth.go index dcd481073..d484a9871 100644 --- a/server/app/auth.go +++ b/server/app/auth.go @@ -14,11 +14,6 @@ const ( SecondsPerMinute = 60 ) -// GetSession Get a user active session and refresh the session if is needed. -func (a *App) GetSession(token string) (*model.Session, error) { - return a.auth.GetSession(token) -} - // IsValidReadToken validates the read token for a block. func (a *App) IsValidReadToken(boardID string, readToken string) (bool, error) { return a.auth.IsValidReadToken(boardID, readToken) diff --git a/server/app/helper_test.go b/server/app/helper_test.go index 43b46e9e8..828e948a9 100644 --- a/server/app/helper_test.go +++ b/server/app/helper_test.go @@ -36,8 +36,7 @@ func SetupTestHelper(t *testing.T) (*TestHelper, func()) { filesBackend := &mocks.FileBackend{} auth := auth.New(&cfg, store, nil) logger, _ := mlog.NewLogger() - sessionToken := "TESTTOKEN" - wsserver := ws.NewServer(auth, sessionToken, false, logger, store) + wsserver := ws.NewServer(auth, logger, store) webhook := webhook.NewClient(&cfg, logger) metricsService := metrics.NewMetrics(metrics.InstanceInfo{}) diff --git a/server/auth/auth.go b/server/auth/auth.go index 5474b9eff..267d060ea 100644 --- a/server/auth/auth.go +++ b/server/auth/auth.go @@ -6,12 +6,10 @@ import ( "github.com/mattermost/mattermost-plugin-boards/server/services/config" "github.com/mattermost/mattermost-plugin-boards/server/services/permissions" "github.com/mattermost/mattermost-plugin-boards/server/services/store" - "github.com/mattermost/mattermost-plugin-boards/server/utils" "github.com/pkg/errors" ) type AuthInterface interface { - GetSession(token string) (*model.Session, error) IsValidReadToken(boardID string, readToken string) (bool, error) DoesUserHaveTeamAccess(userID string, teamID string) bool } @@ -28,22 +26,6 @@ func New(config *config.Configuration, store store.Store, permissions permission return &Auth{config: config, store: store, permissions: permissions} } -// GetSession Get a user active session and refresh the session if needed. -func (a *Auth) GetSession(token string) (*model.Session, error) { - if len(token) < 1 { - return nil, errors.New("no session token") - } - - session, err := a.store.GetSession(token, a.config.SessionExpireTime) - if err != nil { - return nil, errors.Wrap(err, "unable to get the session for the token") - } - if session.UpdateAt < (utils.GetMillis() - utils.SecondsToMillis(a.config.SessionRefreshTime)) { - _ = a.store.RefreshSession(session) - } - return session, nil -} - // IsValidReadToken validates the read token for a board. func (a *Auth) IsValidReadToken(boardID string, readToken string) (bool, error) { sharing, err := a.store.GetSharing(boardID) diff --git a/server/auth/auth_test.go b/server/auth/auth_test.go index eabded6b3..076eefd12 100644 --- a/server/auth/auth_test.go +++ b/server/auth/auth_test.go @@ -3,17 +3,8 @@ package auth import ( "testing" - "github.com/golang/mock/gomock" "github.com/mattermost/mattermost-plugin-boards/server/model" - "github.com/mattermost/mattermost-plugin-boards/server/services/config" - "github.com/mattermost/mattermost-plugin-boards/server/services/permissions/localpermissions" - mockpermissions "github.com/mattermost/mattermost-plugin-boards/server/services/permissions/mocks" "github.com/mattermost/mattermost-plugin-boards/server/services/store/mockstore" - "github.com/mattermost/mattermost-plugin-boards/server/utils" - "github.com/pkg/errors" - "github.com/stretchr/testify/require" - - "github.com/mattermost/mattermost/server/public/shared/mlog" ) type TestHelper struct { @@ -22,71 +13,6 @@ type TestHelper struct { Store *mockstore.MockStore } -var mockSession = &model.Session{ - ID: utils.NewID(utils.IDTypeSession), - Token: "goodToken", - UserID: "12345", - CreateAt: utils.GetMillis() - utils.SecondsToMillis(2000), - UpdateAt: utils.GetMillis() - utils.SecondsToMillis(2000), -} - -func setupTestHelper(t *testing.T) *TestHelper { - ctrl := gomock.NewController(t) - ctrlPermissions := gomock.NewController(t) - cfg := config.Configuration{} - mockStore := mockstore.NewMockStore(ctrl) - mockPermissions := mockpermissions.NewMockStore(ctrlPermissions) - logger, err := mlog.NewLogger() - require.NoError(t, err) - newAuth := New(&cfg, mockStore, localpermissions.New(mockPermissions, logger)) - - // called during default template setup for every test - mockStore.EXPECT().GetTemplateBoards("0", "").AnyTimes() - mockStore.EXPECT().RemoveDefaultTemplates(gomock.Any()).AnyTimes() - mockStore.EXPECT().InsertBlock(gomock.Any(), gomock.Any()).AnyTimes() - - return &TestHelper{ - Auth: newAuth, - Session: *mockSession, - Store: mockStore, - } -} - -func TestGetSession(t *testing.T) { - th := setupTestHelper(t) - - testcases := []struct { - title string - token string - refreshTime int64 - isError bool - }{ - {"fail, no token", "", 0, true}, - {"fail, invalid username", "badToken", 0, true}, - {"success, good token", "goodToken", 1000, false}, - } - - th.Store.EXPECT().GetSession("badToken", gomock.Any()).Return(nil, errors.New("Invalid Token")) - th.Store.EXPECT().GetSession("goodToken", gomock.Any()).Return(mockSession, nil) - th.Store.EXPECT().RefreshSession(gomock.Any()).Return(nil) - - for _, test := range testcases { - t.Run(test.title, func(t *testing.T) { - if test.refreshTime > 0 { - th.Auth.config.SessionRefreshTime = test.refreshTime - } - - session, err := th.Auth.GetSession(test.token) - if test.isError { - require.Error(t, err) - } else { - require.NoError(t, err) - require.NotNil(t, session) - } - }) - } -} - func TestIsValidReadToken(t *testing.T) { // ToDo: reimplement diff --git a/server/boards/boardsapp.go b/server/boards/boardsapp.go index c1a733e49..7fcc2a9fa 100644 --- a/server/boards/boardsapp.go +++ b/server/boards/boardsapp.go @@ -78,7 +78,6 @@ func NewBoardsApp(api model.ServicesAPI) (*BoardsApp, error) { TablePrefix: cfg.DBTablePrefix, Logger: logger, DB: sqlDB, - IsPlugin: true, NewMutexFn: func(name string) (*cluster.Mutex, error) { return cluster.NewMutex(&mutexAPIAdapter{api: api}, name) }, @@ -91,13 +90,11 @@ func NewBoardsApp(api model.ServicesAPI) (*BoardsApp, error) { if err != nil { return nil, fmt.Errorf("error initializing the DB: %w", err) } - if cfg.AuthMode == server.MattermostAuthMod { - layeredStore, err2 := mattermostauthlayer.New(cfg.DBType, sqlDB, db, logger, api, storeParams.TablePrefix) - if err2 != nil { - return nil, fmt.Errorf("error initializing the DB: %w", err2) - } - db = layeredStore + layeredStore, err2 := mattermostauthlayer.New(cfg.DBType, sqlDB, db, logger, api, storeParams.TablePrefix) + if err2 != nil { + return nil, fmt.Errorf("error initializing the DB: %w", err2) } + db = layeredStore permissionsService := mmpermissions.New(db, api, logger) diff --git a/server/integrationtests/clienttestlib.go b/server/integrationtests/clienttestlib.go index 380485763..27cb15bba 100644 --- a/server/integrationtests/clienttestlib.go +++ b/server/integrationtests/clienttestlib.go @@ -150,8 +150,7 @@ func newTestServerWithLicense(singleUserToken string, licenseType LicenseType) * if err = logger.Configure("", cfg.LoggingCfgJSON, nil); err != nil { panic(err) } - singleUser := len(singleUserToken) > 0 - innerStore, err := server.NewStore(cfg, singleUser, logger) + innerStore, err := server.NewStore(cfg, logger) if err != nil { panic(err) } @@ -199,7 +198,7 @@ func NewTestServerPluginMode() *server.Server { if err = logger.Configure("", cfg.LoggingCfgJSON, nil); err != nil { panic(err) } - innerStore, err := server.NewStore(cfg, false, logger) + innerStore, err := server.NewStore(cfg, logger) if err != nil { panic(err) } @@ -235,7 +234,7 @@ func newTestServerLocalMode() *server.Server { panic(err) } - db, err := server.NewStore(cfg, false, logger) + db, err := server.NewStore(cfg, logger) if err != nil { panic(err) } diff --git a/server/server/initHandlers.go b/server/server/initHandlers.go deleted file mode 100644 index e891032aa..000000000 --- a/server/server/initHandlers.go +++ /dev/null @@ -1,6 +0,0 @@ -package server - -func (s *Server) initHandlers() { - cfg := s.config - s.api.MattermostAuth = cfg.AuthMode == MattermostAuthMod -} diff --git a/server/server/server.go b/server/server/server.go index b5838bf2b..e5f1c578e 100644 --- a/server/server/server.go +++ b/server/server/server.go @@ -40,10 +40,6 @@ import ( const ( cleanupSessionTaskFrequency = 10 * time.Minute updateMetricsTaskFrequency = 15 * time.Minute - - minSessionExpiryTime = int64(60 * 60 * 24 * 31) // 31 days - - MattermostAuthMod = "mattermost" ) type Server struct { @@ -78,7 +74,7 @@ func New(params Params) (*Server, error) { // if no ws adapter is provided, we spin up a websocket server wsAdapter := params.WSAdapter if wsAdapter == nil { - wsAdapter = ws.NewServer(authenticator, params.SingleUserToken, params.Cfg.AuthMode == MattermostAuthMod, params.Logger, params.DBStore) + wsAdapter = ws.NewServer(authenticator, params.Logger, params.DBStore) } filesBackendSettings := filestore.FileBackendSettings{} @@ -202,12 +198,10 @@ func New(params Params) (*Server, error) { app: app, } - server.initHandlers() - return &server, nil } -func NewStore(config *config.Configuration, isSingleUser bool, logger mlog.LoggerIFace) (store.Store, error) { +func NewStore(config *config.Configuration, logger mlog.LoggerIFace) (store.Store, error) { sqlDB, err := sql.Open(config.DBType, config.DBConfigString) if err != nil { logger.Error("connectDatabase failed", mlog.Err(err)) @@ -227,8 +221,6 @@ func NewStore(config *config.Configuration, isSingleUser bool, logger mlog.Logge TablePrefix: config.DBTablePrefix, Logger: logger, DB: sqlDB, - IsPlugin: false, - IsSingleUser: isSingleUser, } var db store.Store @@ -253,19 +245,6 @@ func (s *Server) Start() error { } } - if s.config.AuthMode != MattermostAuthMod { - s.cleanUpSessionsTask = scheduler.CreateRecurringTask("cleanUpSessions", func() { - secondsAgo := minSessionExpiryTime - if secondsAgo < s.config.SessionExpireTime { - secondsAgo = s.config.SessionExpireTime - } - - if err := s.store.CleanUpSessions(secondsAgo); err != nil { - s.logger.Error("Unable to clean up the sessions", mlog.Err(err)) - } - }, cleanupSessionTaskFrequency) - } - metricsUpdater := func() { blockCounts, err := s.store.GetBlockCountsByType() if err != nil { diff --git a/server/services/store/mattermostauthlayer/mattermostauthlayer.go b/server/services/store/mattermostauthlayer/mattermostauthlayer.go index bc3248743..2b2c48a80 100644 --- a/server/services/store/mattermostauthlayer/mattermostauthlayer.go +++ b/server/services/store/mattermostauthlayer/mattermostauthlayer.go @@ -115,22 +115,6 @@ func (s *MattermostAuthLayer) GetUserByUsername(username string) (*model.User, e return &user, nil } -func (s *MattermostAuthLayer) CreateUser(user *model.User) (*model.User, error) { - return nil, store.NewNotSupportedError("no user creation allowed from focalboard, create it using mattermost") -} - -func (s *MattermostAuthLayer) UpdateUser(user *model.User) (*model.User, error) { - return nil, store.NewNotSupportedError("no update allowed from focalboard, update it using mattermost") -} - -func (s *MattermostAuthLayer) UpdateUserPassword(username, password string) error { - return store.NewNotSupportedError("no update allowed from focalboard, update it using mattermost") -} - -func (s *MattermostAuthLayer) UpdateUserPasswordByID(userID, password string) error { - return store.NewNotSupportedError("no update allowed from focalboard, update it using mattermost") -} - func (s *MattermostAuthLayer) PatchUserPreferences(userID string, patch model.UserPreferencesPatch) (mmModel.Preferences, error) { preferences, err := s.GetUserPreferences(userID) if err != nil { @@ -236,30 +220,6 @@ func (s *MattermostAuthLayer) GetActiveUserCount(updatedSecondsAgo int64) (int, return count, nil } -func (s *MattermostAuthLayer) GetSession(token string, expireTime int64) (*model.Session, error) { - return nil, store.NewNotSupportedError("sessions not used when using mattermost") -} - -func (s *MattermostAuthLayer) CreateSession(session *model.Session) error { - return store.NewNotSupportedError("no update allowed from focalboard, update it using mattermost") -} - -func (s *MattermostAuthLayer) RefreshSession(session *model.Session) error { - return store.NewNotSupportedError("no update allowed from focalboard, update it using mattermost") -} - -func (s *MattermostAuthLayer) UpdateSession(session *model.Session) error { - return store.NewNotSupportedError("no update allowed from focalboard, update it using mattermost") -} - -func (s *MattermostAuthLayer) DeleteSession(sessionID string) error { - return store.NewNotSupportedError("no update allowed from focalboard, update it using mattermost") -} - -func (s *MattermostAuthLayer) CleanUpSessions(expireTime int64) error { - return store.NewNotSupportedError("no update allowed from focalboard, update it using mattermost") -} - func (s *MattermostAuthLayer) GetTeam(id string) (*model.Team, error) { if id == "0" { team := model.Team{ diff --git a/server/services/store/mockstore/mockstore.go b/server/services/store/mockstore/mockstore.go index 973dae3f2..77c8a555f 100644 --- a/server/services/store/mockstore/mockstore.go +++ b/server/services/store/mockstore/mockstore.go @@ -65,20 +65,6 @@ func (mr *MockStoreMockRecorder) CanSeeUser(arg0, arg1 interface{}) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CanSeeUser", reflect.TypeOf((*MockStore)(nil).CanSeeUser), arg0, arg1) } -// CleanUpSessions mocks base method. -func (m *MockStore) CleanUpSessions(arg0 int64) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CleanUpSessions", arg0) - ret0, _ := ret[0].(error) - return ret0 -} - -// CleanUpSessions indicates an expected call of CleanUpSessions. -func (mr *MockStoreMockRecorder) CleanUpSessions(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CleanUpSessions", reflect.TypeOf((*MockStore)(nil).CleanUpSessions), arg0) -} - // CreateBoardsAndBlocks mocks base method. func (m *MockStore) CreateBoardsAndBlocks(arg0 *model.BoardsAndBlocks, arg1 string) (*model.BoardsAndBlocks, error) { m.ctrl.T.Helper() @@ -906,12 +892,6 @@ func (m *MockStore) GetSession(arg0 string, arg1 int64) (*model.Session, error) return ret0, ret1 } -// GetSession indicates an expected call of GetSession. -func (mr *MockStoreMockRecorder) GetSession(arg0, arg1 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSession", reflect.TypeOf((*MockStore)(nil).GetSession), arg0, arg1) -} - // GetSharing mocks base method. func (m *MockStore) GetSharing(arg0 string) (*model.Sharing, error) { m.ctrl.T.Helper() @@ -1396,12 +1376,6 @@ func (m *MockStore) RefreshSession(arg0 *model.Session) error { return ret0 } -// RefreshSession indicates an expected call of RefreshSession. -func (mr *MockStoreMockRecorder) RefreshSession(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RefreshSession", reflect.TypeOf((*MockStore)(nil).RefreshSession), arg0) -} - // RemoveDefaultTemplates mocks base method. func (m *MockStore) RemoveDefaultTemplates(arg0 []*model.Board) error { m.ctrl.T.Helper() diff --git a/server/services/store/sqlstore/board.go b/server/services/store/sqlstore/board.go index 48998c687..42896baa5 100644 --- a/server/services/store/sqlstore/board.go +++ b/server/services/store/sqlstore/board.go @@ -234,36 +234,6 @@ func (s *SQLStore) getBoard(db sq.BaseRunner, boardID string) (*model.Board, err return s.getBoardByCondition(db, sq.Eq{"id": boardID}) } -func (s *SQLStore) getBoardsForUserAndTeam(db sq.BaseRunner, userID, teamID string, includePublicBoards bool) ([]*model.Board, error) { - query := s.getQueryBuilder(db). - Select(boardFields("b.")...). - Distinct(). - From(s.tablePrefix + "boards as b"). - LeftJoin(s.tablePrefix + "board_members as bm on b.id=bm.board_id"). - Where(sq.Eq{"b.team_id": teamID}). - Where(sq.Eq{"b.is_template": false}) - - if includePublicBoards { - query = query.Where(sq.Or{ - sq.Eq{"b.type": model.BoardTypeOpen}, - sq.Eq{"bm.user_id": userID}, - }) - } else { - query = query.Where(sq.Or{ - sq.Eq{"bm.user_id": userID}, - }) - } - - rows, err := query.Query() - if err != nil { - s.logger.Error(`getBoardsForUserAndTeam ERROR`, mlog.Err(err)) - return nil, err - } - defer s.CloseRows(rows) - - return s.boardsFromRows(rows) -} - func (s *SQLStore) getBoardsInTeamByIds(db sq.BaseRunner, boardIDs []string, teamID string) ([]*model.Board, error) { query := s.getQueryBuilder(db). Select(boardFields("b.")...). @@ -646,112 +616,6 @@ func (s *SQLStore) getMembersForBoard(db sq.BaseRunner, boardID string) ([]*mode return s.boardMembersFromRows(rows) } -// searchBoardsForUser returns all boards that match with the -// term that are either private and which the user is a member of, or -// they're open, regardless of the user membership. -// Search is case-insensitive. -func (s *SQLStore) searchBoardsForUser(db sq.BaseRunner, term string, searchField model.BoardSearchField, userID string, includePublicBoards bool) ([]*model.Board, error) { - query := s.getQueryBuilder(db). - Select(boardFields("b.")...). - Distinct(). - From(s.tablePrefix + "boards as b"). - LeftJoin(s.tablePrefix + "board_members as bm on b.id=bm.board_id"). - Where(sq.Eq{"b.is_template": false}) - - if includePublicBoards { - query = query.Where(sq.Or{ - sq.Eq{"b.type": model.BoardTypeOpen}, - sq.Eq{"bm.user_id": userID}, - }) - } else { - query = query.Where(sq.Or{ - sq.Eq{"bm.user_id": userID}, - }) - } - - if term != "" { - if searchField == model.BoardSearchFieldPropertyName { - switch s.dbType { - case model.PostgresDBType: - where := "b.properties->? is not null" - query = query.Where(where, term) - case model.MysqlDBType, model.SqliteDBType: - where := "JSON_EXTRACT(b.properties, ?) IS NOT NULL" - query = query.Where(where, "$."+term) - default: - where := "b.properties LIKE ?" - query = query.Where(where, "%\""+term+"\"%") - } - } else { // model.BoardSearchFieldTitle - // break search query into space separated words - // and search for all words. - // This should later be upgraded to industrial-strength - // word tokenizer, that uses much more than space - // to break words. - conditions := sq.And{} - for _, word := range strings.Split(strings.TrimSpace(term), " ") { - conditions = append(conditions, sq.Like{"lower(b.title)": "%" + strings.ToLower(word) + "%"}) - } - query = query.Where(conditions) - } - } - - rows, err := query.Query() - if err != nil { - s.logger.Error(`searchBoardsForUser ERROR`, mlog.Err(err)) - return nil, err - } - defer s.CloseRows(rows) - - return s.boardsFromRows(rows) -} - -// searchBoardsForUserInTeam returns all boards that match with the -// term that are either private and which the user is a member of, or -// they're open, regardless of the user membership. -// Search is case-insensitive. -func (s *SQLStore) searchBoardsForUserInTeam(db sq.BaseRunner, teamID, term, userID string) ([]*model.Board, error) { - query := s.getQueryBuilder(db). - Select(boardFields("b.")...). - Distinct(). - From(s.tablePrefix + "boards as b"). - LeftJoin(s.tablePrefix + "board_members as bm on b.id=bm.board_id"). - Where(sq.Eq{"b.is_template": false}). - Where(sq.Eq{"b.team_id": teamID}). - Where(sq.Or{ - sq.Eq{"b.type": model.BoardTypeOpen}, - sq.And{ - sq.Eq{"b.type": model.BoardTypePrivate}, - sq.Eq{"bm.user_id": userID}, - }, - }) - - if term != "" { - // break search query into space separated words - // and search for all words. - // This should later be upgraded to industrial-strength - // word tokenizer, that uses much more than space - // to break words. - - conditions := sq.And{} - - for _, word := range strings.Split(strings.TrimSpace(term), " ") { - conditions = append(conditions, sq.Like{"lower(b.title)": "%" + strings.ToLower(word) + "%"}) - } - - query = query.Where(conditions) - } - - rows, err := query.Query() - if err != nil { - s.logger.Error(`searchBoardsForUser ERROR`, mlog.Err(err)) - return nil, err - } - defer s.CloseRows(rows) - - return s.boardsFromRows(rows) -} - func (s *SQLStore) getBoardHistory(db sq.BaseRunner, boardID string, opts model.QueryBoardHistoryOptions) ([]*model.Board, error) { var order string if opts.Descending { diff --git a/server/services/store/sqlstore/data_migrations.go b/server/services/store/sqlstore/data_migrations.go index 07169ae5d..264a83d00 100644 --- a/server/services/store/sqlstore/data_migrations.go +++ b/server/services/store/sqlstore/data_migrations.go @@ -154,20 +154,18 @@ func (s *SQLStore) RunCategoryUUIDIDMigration() error { return txErr } - if s.isPlugin { - if err := s.createCategories(tx); err != nil { - if rollbackErr := tx.Rollback(); rollbackErr != nil { - s.logger.Error("category UUIDs insert categories transaction rollback error", mlog.Err(rollbackErr), mlog.String("methodName", "setSystemSetting")) - } - return err + if err := s.createCategories(tx); err != nil { + if rollbackErr := tx.Rollback(); rollbackErr != nil { + s.logger.Error("category UUIDs insert categories transaction rollback error", mlog.Err(rollbackErr), mlog.String("methodName", "setSystemSetting")) } + return err + } - if err := s.createCategoryBoards(tx); err != nil { - if rollbackErr := tx.Rollback(); rollbackErr != nil { - s.logger.Error("category UUIDs insert category boards transaction rollback error", mlog.Err(rollbackErr), mlog.String("methodName", "setSystemSetting")) - } - return err + if err := s.createCategoryBoards(tx); err != nil { + if rollbackErr := tx.Rollback(); rollbackErr != nil { + s.logger.Error("category UUIDs insert category boards transaction rollback error", mlog.Err(rollbackErr), mlog.String("methodName", "setSystemSetting")) } + return err } if err := s.setSystemSetting(tx, CategoryUUIDIDMigrationKey, strconv.FormatBool(true)); err != nil { @@ -353,10 +351,6 @@ func (s *SQLStore) createCategoryBoards(db sq.BaseRunner) error { // group messages. This function migrates all boards // belonging to a DM to the best possible team. func (s *SQLStore) RunTeamLessBoardsMigration() error { - if !s.isPlugin { - return nil - } - setting, err := s.GetSystemSetting(TeamLessBoardsMigrationKey) if err != nil { return fmt.Errorf("cannot get teamless boards migration state: %w", err) @@ -555,10 +549,6 @@ func (s *SQLStore) getBoardUserTeams(tx sq.BaseRunner, board *model.Board) (map[ } func (s *SQLStore) RunDeletedMembershipBoardsMigration() error { - if !s.isPlugin { - return nil - } - setting, err := s.GetSystemSetting(DeletedMembershipBoardsMigrationKey) if err != nil { return fmt.Errorf("cannot get deleted membership boards migration state: %w", err) @@ -661,7 +651,7 @@ func (s *SQLStore) RunFixCollationsAndCharsetsMigration() error { var collation string var charSet string var err error - if !s.isPlugin || os.Getenv("FOCALBOARD_UNIT_TESTING") == "1" { + if os.Getenv("FOCALBOARD_UNIT_TESTING") == "1" { collation = "utf8mb4_general_ci" charSet = "utf8mb4" } else { diff --git a/server/services/store/sqlstore/file.go b/server/services/store/sqlstore/file.go deleted file mode 100644 index 72645690f..000000000 --- a/server/services/store/sqlstore/file.go +++ /dev/null @@ -1,92 +0,0 @@ -package sqlstore - -import ( - "database/sql" - "errors" - - sq "github.com/Masterminds/squirrel" - - "github.com/mattermost/mattermost-plugin-boards/server/model" - - mmModel "github.com/mattermost/mattermost/server/public/model" - "github.com/mattermost/mattermost/server/public/shared/mlog" -) - -func (s *SQLStore) saveFileInfo(db sq.BaseRunner, fileInfo *mmModel.FileInfo) error { - query := s.getQueryBuilder(db). - Insert(s.tablePrefix+"file_info"). - Columns( - "id", - "create_at", - "name", - "extension", - "size", - "delete_at", - "path", - "archived", - ). - Values( - fileInfo.Id, - fileInfo.CreateAt, - fileInfo.Name, - fileInfo.Extension, - fileInfo.Size, - fileInfo.DeleteAt, - fileInfo.Path, - false, - ) - - if _, err := query.Exec(); err != nil { - s.logger.Error( - "failed to save fileinfo", - mlog.String("file_name", fileInfo.Name), - mlog.Int("size", fileInfo.Size), - mlog.Err(err), - ) - return err - } - - return nil -} - -func (s *SQLStore) getFileInfo(db sq.BaseRunner, id string) (*mmModel.FileInfo, error) { - query := s.getQueryBuilder(db). - Select( - "id", - "create_at", - "delete_at", - "name", - "extension", - "size", - "archived", - "path", - ). - From(s.tablePrefix + "file_info"). - Where(sq.Eq{"Id": id}) - - row := query.QueryRow() - - fileInfo := mmModel.FileInfo{} - - err := row.Scan( - &fileInfo.Id, - &fileInfo.CreateAt, - &fileInfo.DeleteAt, - &fileInfo.Name, - &fileInfo.Extension, - &fileInfo.Size, - &fileInfo.Archived, - &fileInfo.Path, - ) - - if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil, model.NewErrNotFound("file info ID=" + id) - } - - s.logger.Error("error scanning fileinfo row", mlog.String("id", id), mlog.Err(err)) - return nil, err - } - - return &fileInfo, nil -} diff --git a/server/services/store/sqlstore/helpers_test.go b/server/services/store/sqlstore/helpers_test.go index 95ad63977..952e41797 100644 --- a/server/services/store/sqlstore/helpers_test.go +++ b/server/services/store/sqlstore/helpers_test.go @@ -32,7 +32,6 @@ func SetupTests(t *testing.T) (store.Store, func()) { TablePrefix: "test_", Logger: logger, DB: sqlDB, - IsPlugin: false, } store, err := New(storeParams) require.NoError(t, err) diff --git a/server/services/store/sqlstore/migrate.go b/server/services/store/sqlstore/migrate.go index 294e1c00e..419865684 100644 --- a/server/services/store/sqlstore/migrate.go +++ b/server/services/store/sqlstore/migrate.go @@ -74,20 +74,18 @@ func (s *SQLStore) getMigrationConnection() (*sql.DB, error) { } func (s *SQLStore) Migrate() error { - if s.isPlugin { - mutex, mutexErr := s.NewMutexFn("Boards_dbMutex") - if mutexErr != nil { - return fmt.Errorf("error creating database mutex: %w", mutexErr) - } - - s.logger.Debug("Acquiring cluster lock for Focalboard migrations") - mutex.Lock() - defer func() { - s.logger.Debug("Releasing cluster lock for Focalboard migrations") - mutex.Unlock() - }() + mutex, mutexErr := s.NewMutexFn("Boards_dbMutex") + if mutexErr != nil { + return fmt.Errorf("error creating database mutex: %w", mutexErr) } + s.logger.Debug("Acquiring cluster lock for Focalboard migrations") + mutex.Lock() + defer func() { + s.logger.Debug("Releasing cluster lock for Focalboard migrations") + mutex.Unlock() + }() + if err := s.EnsureSchemaMigrationFormat(); err != nil { return err } @@ -148,12 +146,10 @@ func (s *SQLStore) Migrate() error { } params := map[string]interface{}{ - "prefix": s.tablePrefix, - "postgres": s.dbType == model.PostgresDBType, - "sqlite": s.dbType == model.SqliteDBType, - "mysql": s.dbType == model.MysqlDBType, - "plugin": s.isPlugin, - "singleUser": s.isSingleUser, + "prefix": s.tablePrefix, + "postgres": s.dbType == model.PostgresDBType, + "sqlite": s.dbType == model.SqliteDBType, + "mysql": s.dbType == model.MysqlDBType, } migrationAssets := &embedded.AssetSource{ diff --git a/server/services/store/sqlstore/migrationstests/boards_migrator_test.go b/server/services/store/sqlstore/migrationstests/boards_migrator_test.go index d82c6c0ad..11f601679 100644 --- a/server/services/store/sqlstore/migrationstests/boards_migrator_test.go +++ b/server/services/store/sqlstore/migrationstests/boards_migrator_test.go @@ -210,7 +210,6 @@ func (bm *BoardsMigrator) Setup() error { TablePrefix: tablePrefix, Logger: logger, DB: bm.db, - IsPlugin: bm.withMattermostMigrations, NewMutexFn: func(name string) (*cluster.Mutex, error) { return nil, fmt.Errorf("not implemented") }, diff --git a/server/services/store/sqlstore/migrationstests/helpers_test.go b/server/services/store/sqlstore/migrationstests/helpers_test.go index c4532e45c..4508828ac 100644 --- a/server/services/store/sqlstore/migrationstests/helpers_test.go +++ b/server/services/store/sqlstore/migrationstests/helpers_test.go @@ -10,9 +10,8 @@ import ( ) type TestHelper struct { - t *testing.T - f *foundation.Foundation - isPlugin bool + t *testing.T + f *foundation.Foundation } func (th *TestHelper) IsPostgres() bool { @@ -33,20 +32,19 @@ func SetupPluginTestHelper(t *testing.T) (*TestHelper, func()) { t.Skip("Skipping plugin mode test for SQLite") } - return setupTestHelper(t, true) + return setupTestHelper(t) } func SetupTestHelper(t *testing.T) (*TestHelper, func()) { - return setupTestHelper(t, false) + return setupTestHelper(t) } -func setupTestHelper(t *testing.T, isPlugin bool) (*TestHelper, func()) { - f := foundation.New(t, NewBoardsMigrator(isPlugin)) +func setupTestHelper(t *testing.T) (*TestHelper, func()) { + f := foundation.New(t, NewBoardsMigrator(true)) th := &TestHelper{ - t: t, - f: f, - isPlugin: isPlugin, + t: t, + f: f, } tearDown := func() { diff --git a/server/services/store/sqlstore/params.go b/server/services/store/sqlstore/params.go index eb2e38493..352ae3b38 100644 --- a/server/services/store/sqlstore/params.go +++ b/server/services/store/sqlstore/params.go @@ -22,8 +22,6 @@ type Params struct { TablePrefix string Logger mlog.LoggerIFace DB *sql.DB - IsPlugin bool - IsSingleUser bool NewMutexFn MutexFactory ServicesAPI servicesAPI SkipMigrations bool @@ -31,7 +29,7 @@ type Params struct { } func (p Params) CheckValid() error { - if p.IsPlugin && p.NewMutexFn == nil { + if p.NewMutexFn == nil { return ErrStoreParam{name: "NewMutexFn", issue: "cannot be nil in plugin mode"} } return nil diff --git a/server/services/store/sqlstore/public_methods.go b/server/services/store/sqlstore/public_methods.go index 45e1b53ca..1f4d52146 100644 --- a/server/services/store/sqlstore/public_methods.go +++ b/server/services/store/sqlstore/public_methods.go @@ -14,6 +14,7 @@ package sqlstore import ( "context" + "errors" "time" "github.com/mattermost/mattermost-plugin-boards/server/model" @@ -47,12 +48,7 @@ func (s *SQLStore) AddUpdateCategoryBoard(userID string, categoryID string, boar } func (s *SQLStore) CanSeeUser(seerID string, seenID string) (bool, error) { - return s.canSeeUser(s.db, seerID, seenID) - -} - -func (s *SQLStore) CleanUpSessions(expireTime int64) error { - return s.cleanUpSessions(s.db, expireTime) + return false, errors.New("can see user not supported in focalboard, will fetch from mattermost") } @@ -128,21 +124,11 @@ func (s *SQLStore) CreateCategory(category model.Category) error { } -func (s *SQLStore) CreateSession(session *model.Session) error { - return s.createSession(s.db, session) - -} - func (s *SQLStore) CreateSubscription(sub *model.Subscription) (*model.Subscription, error) { return s.createSubscription(s.db, sub) } -func (s *SQLStore) CreateUser(user *model.User) (*model.User, error) { - return s.createUser(s.db, user) - -} - func (s *SQLStore) DeleteBlock(blockID string, modifiedBy string) error { if s.dbType == model.SqliteDBType { return s.deleteBlock(s.db, blockID, modifiedBy) @@ -240,11 +226,6 @@ func (s *SQLStore) DeleteNotificationHint(blockID string) error { } -func (s *SQLStore) DeleteSession(sessionID string) error { - return s.deleteSession(s.db, sessionID) - -} - func (s *SQLStore) DeleteSubscription(blockID string, subscriberID string) error { return s.deleteSubscription(s.db, blockID, subscriberID) @@ -299,7 +280,7 @@ func (s *SQLStore) DuplicateBoard(boardID string, userID string, toTeam string, } func (s *SQLStore) GetActiveUserCount(updatedSecondsAgo int64) (int, error) { - return s.getActiveUserCount(s.db, updatedSecondsAgo) + return 0, errors.New("active user count not supported in focalboard when using plugin mode, will fetch from mattermost") } @@ -409,7 +390,7 @@ func (s *SQLStore) GetBoardsForCompliance(opts model.QueryBoardsForComplianceOpt } func (s *SQLStore) GetBoardsForUserAndTeam(userID string, teamID string, includePublicBoards bool) ([]*model.Board, error) { - return s.getBoardsForUserAndTeam(s.db, userID, teamID, includePublicBoards) + return nil, errors.New("boards for user and team will be fetch from mattermost not from focalboard while using in plugin mode") } @@ -429,17 +410,17 @@ func (s *SQLStore) GetCategory(id string) (*model.Category, error) { } func (s *SQLStore) GetChannel(teamID string, channelID string) (*mmModel.Channel, error) { - return s.getChannel(s.db, teamID, channelID) + return nil, errors.New("channel will be fetch from mattermost not from focalboard while using in plugin mode") } func (s *SQLStore) GetFileInfo(id string) (*mmModel.FileInfo, error) { - return s.getFileInfo(s.db, id) + return nil, errors.New("file info will be fetch from mattermost not from focalboard while using in plugin mode") } func (s *SQLStore) GetLicense() *mmModel.License { - return s.getLicense(s.db) + return nil } @@ -469,12 +450,7 @@ func (s *SQLStore) GetNotificationHint(blockID string) (*model.NotificationHint, } func (s *SQLStore) GetRegisteredUserCount() (int, error) { - return s.getRegisteredUserCount(s.db) - -} - -func (s *SQLStore) GetSession(token string, expireTime int64) (*model.Session, error) { - return s.getSession(s.db, token, expireTime) + return 0, errors.New("registered user count not supported in focalboard, will fetch from mattermost") } @@ -519,7 +495,7 @@ func (s *SQLStore) GetSystemSettings() (map[string]string, error) { } func (s *SQLStore) GetTeam(ID string) (*model.Team, error) { - return s.getTeam(s.db, ID) + return nil, errors.New("team will be fetch from mattermost not from focalboard while using in plugin mode") } @@ -529,7 +505,7 @@ func (s *SQLStore) GetTeamCount() (int64, error) { } func (s *SQLStore) GetTeamsForUser(userID string) ([]*model.Team, error) { - return s.getTeamsForUser(s.db, userID) + return nil, errors.New("teams for user will be fetch from mattermost not from focalboard while using in plugin mode") } @@ -544,17 +520,17 @@ func (s *SQLStore) GetUsedCardsCount() (int, error) { } func (s *SQLStore) GetUserByEmail(email string) (*model.User, error) { - return s.getUserByEmail(s.db, email) + return nil, errors.New("user email will be fetch from mattermost not from focalboard while using in plugin mode") } func (s *SQLStore) GetUserByID(userID string) (*model.User, error) { - return s.getUserByID(s.db, userID) + return nil, errors.New("user id will be fetch from mattermost not from focalboard while using in plugin mode") } func (s *SQLStore) GetUserByUsername(username string) (*model.User, error) { - return s.getUserByUsername(s.db, username) + return nil, errors.New("user username will be fetch from mattermost not from focalboard while using in plugin mode") } @@ -569,22 +545,22 @@ func (s *SQLStore) GetUserCategoryBoards(userID string, teamID string) ([]model. } func (s *SQLStore) GetUserPreferences(userID string) (mmModel.Preferences, error) { - return s.getUserPreferences(s.db, userID) + return nil, errors.New("user preferences will be fetch from mattermost not from focalboard while using in plugin mode") } func (s *SQLStore) GetUserTimezone(userID string) (string, error) { - return s.getUserTimezone(s.db, userID) + return "", errors.New("user timezone will be fetch from mattermost not from focalboard while using in plugin mode") } func (s *SQLStore) GetUsersByTeam(teamID string, asGuestID string, showEmail bool, showName bool) ([]*model.User, error) { - return s.getUsersByTeam(s.db, teamID, asGuestID, showEmail, showName) + return nil, errors.New("users by team will be fetch from mattermost not from focalboard while using in plugin mode") } func (s *SQLStore) GetUsersList(userIDs []string, showEmail bool, showName bool) ([]*model.User, error) { - return s.getUsersList(s.db, userIDs, showEmail, showName) + return nil, errors.New("users list will be fetch from mattermost not from focalboard while using in plugin mode") } @@ -762,17 +738,12 @@ func (s *SQLStore) PatchBoardsAndBlocks(pbab *model.PatchBoardsAndBlocks, userID } func (s *SQLStore) PatchUserPreferences(userID string, patch model.UserPreferencesPatch) (mmModel.Preferences, error) { - return s.patchUserPreferences(s.db, userID, patch) + return nil, errors.New("no patch allowed from focalboard, patch it using mattermost") } func (s *SQLStore) PostMessage(message string, postType string, channelID string) error { - return s.postMessage(s.db, message, postType, channelID) - -} - -func (s *SQLStore) RefreshSession(session *model.Session) error { - return s.refreshSession(s.db, session) + return errors.New("no post message allowed from focalboard, post it using mattermost") } @@ -816,7 +787,7 @@ func (s *SQLStore) RunDataRetention(globalRetentionDate int64, batchSize int64) } func (s *SQLStore) SaveFileInfo(fileInfo *mmModel.FileInfo) error { - return s.saveFileInfo(s.db, fileInfo) + return errors.New("no save file info allowed from focalboard, save it using mattermost") } @@ -826,27 +797,27 @@ func (s *SQLStore) SaveMember(bm *model.BoardMember) (*model.BoardMember, error) } func (s *SQLStore) SearchBoardsForUser(term string, searchField model.BoardSearchField, userID string, includePublicBoards bool) ([]*model.Board, error) { - return s.searchBoardsForUser(s.db, term, searchField, userID, includePublicBoards) + return nil, errors.New("searching boards for user will be using from mattermost not from focalboard in plugin mode") } func (s *SQLStore) SearchBoardsForUserInTeam(teamID string, term string, userID string) ([]*model.Board, error) { - return s.searchBoardsForUserInTeam(s.db, teamID, term, userID) + return nil, errors.New("searching boards for user in team will be using from mattermost not from focalboard in plugin mode") } func (s *SQLStore) SearchUserChannels(teamID string, userID string, query string) ([]*mmModel.Channel, error) { - return s.searchUserChannels(s.db, teamID, userID, query) + return nil, errors.New("searching user channels will be fetch from mattermost not from focalboard while using in plugin mode") } func (s *SQLStore) SearchUsersByTeam(teamID string, searchQuery string, asGuestID string, excludeBots bool, showEmail bool, showName bool) ([]*model.User, error) { - return s.searchUsersByTeam(s.db, teamID, searchQuery, asGuestID, excludeBots, showEmail, showName) + return nil, errors.New("searching users by team will be fetch from mattermost not from focalboard while using in plugin mode") } func (s *SQLStore) SendMessage(message string, postType string, receipts []string) error { - return s.sendMessage(s.db, message, postType, receipts) + return errors.New("no send message allowed from focalboard, send it using mattermost") } @@ -918,31 +889,11 @@ func (s *SQLStore) UpdateCategory(category model.Category) error { } -func (s *SQLStore) UpdateSession(session *model.Session) error { - return s.updateSession(s.db, session) - -} - func (s *SQLStore) UpdateSubscribersNotifiedAt(blockID string, notifiedAt int64) error { return s.updateSubscribersNotifiedAt(s.db, blockID, notifiedAt) } -func (s *SQLStore) UpdateUser(user *model.User) (*model.User, error) { - return s.updateUser(s.db, user) - -} - -func (s *SQLStore) UpdateUserPassword(username string, password string) error { - return s.updateUserPassword(s.db, username, password) - -} - -func (s *SQLStore) UpdateUserPasswordByID(userID string, password string) error { - return s.updateUserPasswordByID(s.db, userID, password) - -} - func (s *SQLStore) UpsertNotificationHint(hint *model.NotificationHint, notificationFreq time.Duration) (*model.NotificationHint, error) { return s.upsertNotificationHint(s.db, hint, notificationFreq) diff --git a/server/services/store/sqlstore/session.go b/server/services/store/sqlstore/session.go deleted file mode 100644 index f87b68ebf..000000000 --- a/server/services/store/sqlstore/session.go +++ /dev/null @@ -1,111 +0,0 @@ -package sqlstore - -import ( - "encoding/json" - - sq "github.com/Masterminds/squirrel" - "github.com/mattermost/mattermost-plugin-boards/server/model" - "github.com/mattermost/mattermost-plugin-boards/server/utils" -) - -// GetActiveUserCount returns the number of users with active sessions within N seconds ago. -func (s *SQLStore) getActiveUserCount(db sq.BaseRunner, updatedSecondsAgo int64) (int, error) { - query := s.getQueryBuilder(db). - Select("count(distinct user_id)"). - From(s.tablePrefix + "sessions"). - Where(sq.Gt{"update_at": utils.GetMillis() - utils.SecondsToMillis(updatedSecondsAgo)}) - - row := query.QueryRow() - - var count int - err := row.Scan(&count) - if err != nil { - return 0, err - } - - return count, nil -} - -func (s *SQLStore) getSession(db sq.BaseRunner, token string, expireTimeSeconds int64) (*model.Session, error) { - query := s.getQueryBuilder(db). - Select("id", "token", "user_id", "auth_service", "props"). - From(s.tablePrefix + "sessions"). - Where(sq.Eq{"token": token}). - Where(sq.Gt{"update_at": utils.GetMillis() - utils.SecondsToMillis(expireTimeSeconds)}) - - row := query.QueryRow() - session := model.Session{} - - var propsBytes []byte - err := row.Scan(&session.ID, &session.Token, &session.UserID, &session.AuthService, &propsBytes) - if err != nil { - return nil, err - } - - err = json.Unmarshal(propsBytes, &session.Props) - if err != nil { - return nil, err - } - - return &session, nil -} - -func (s *SQLStore) createSession(db sq.BaseRunner, session *model.Session) error { - now := utils.GetMillis() - - propsBytes, err := json.Marshal(session.Props) - if err != nil { - return err - } - - query := s.getQueryBuilder(db).Insert(s.tablePrefix+"sessions"). - Columns("id", "token", "user_id", "auth_service", "props", "create_at", "update_at"). - Values(session.ID, session.Token, session.UserID, session.AuthService, propsBytes, now, now) - - _, err = query.Exec() - return err -} - -func (s *SQLStore) refreshSession(db sq.BaseRunner, session *model.Session) error { - now := utils.GetMillis() - - query := s.getQueryBuilder(db).Update(s.tablePrefix+"sessions"). - Where(sq.Eq{"token": session.Token}). - Set("update_at", now) - - _, err := query.Exec() - return err -} - -func (s *SQLStore) updateSession(db sq.BaseRunner, session *model.Session) error { - now := utils.GetMillis() - - propsBytes, err := json.Marshal(session.Props) - if err != nil { - return err - } - - query := s.getQueryBuilder(db).Update(s.tablePrefix+"sessions"). - Where(sq.Eq{"token": session.Token}). - Set("update_at", now). - Set("props", propsBytes) - - _, err = query.Exec() - return err -} - -func (s *SQLStore) deleteSession(db sq.BaseRunner, sessionID string) error { - query := s.getQueryBuilder(db).Delete(s.tablePrefix + "sessions"). - Where(sq.Eq{"id": sessionID}) - - _, err := query.Exec() - return err -} - -func (s *SQLStore) cleanUpSessions(db sq.BaseRunner, expireTimeSeconds int64) error { - query := s.getQueryBuilder(db).Delete(s.tablePrefix + "sessions"). - Where(sq.Lt{"update_at": utils.GetMillis() - utils.SecondsToMillis(expireTimeSeconds)}) - - _, err := query.Exec() - return err -} diff --git a/server/services/store/sqlstore/sqlstore.go b/server/services/store/sqlstore/sqlstore.go index d9b43a35b..2165be4f3 100644 --- a/server/services/store/sqlstore/sqlstore.go +++ b/server/services/store/sqlstore/sqlstore.go @@ -9,7 +9,6 @@ import ( sq "github.com/Masterminds/squirrel" "github.com/mattermost/mattermost-plugin-boards/server/model" - "github.com/mattermost/mattermost-plugin-boards/server/services/store" "github.com/mattermost/mattermost/server/public/pluginapi/cluster" mmModel "github.com/mattermost/mattermost/server/public/model" @@ -23,8 +22,6 @@ type SQLStore struct { tablePrefix string connectionString string dbPingAttempts int - isPlugin bool - isSingleUser bool logger mlog.LoggerIFace NewMutexFn MutexFactory servicesAPI servicesAPI @@ -52,8 +49,6 @@ func New(params Params) (*SQLStore, error) { tablePrefix: params.TablePrefix, connectionString: params.ConnectionString, logger: params.Logger, - isPlugin: params.IsPlugin, - isSingleUser: params.IsSingleUser, NewMutexFn: params.NewMutexFn, servicesAPI: params.ServicesAPI, configFn: params.ConfigFn, @@ -171,18 +166,6 @@ func (s *SQLStore) elementInColumn(column string) string { return "" } -func (s *SQLStore) getLicense(db sq.BaseRunner) *mmModel.License { - return nil -} - -func (s *SQLStore) searchUserChannels(db sq.BaseRunner, teamID, userID, query string) ([]*mmModel.Channel, error) { - return nil, store.NewNotSupportedError("search user channels not supported on standalone mode") -} - -func (s *SQLStore) getChannel(db sq.BaseRunner, teamID, channel string) (*mmModel.Channel, error) { - return nil, store.NewNotSupportedError("get channel not supported on standalone mode") -} - func (s *SQLStore) DBVersion() string { var version string var row *sql.Row diff --git a/server/services/store/sqlstore/sqlstore_test.go b/server/services/store/sqlstore/sqlstore_test.go index cdc3f495c..06a81d4f4 100644 --- a/server/services/store/sqlstore/sqlstore_test.go +++ b/server/services/store/sqlstore/sqlstore_test.go @@ -21,7 +21,6 @@ func TestSQLStore(t *testing.T) { t.Run("TeamStore", func(t *testing.T) { storetests.StoreTestTeamStore(t, SetupTests) }) t.Run("BoardStore", func(t *testing.T) { storetests.StoreTestBoardStore(t, SetupTests) }) t.Run("BoardsAndBlocksStore", func(t *testing.T) { storetests.StoreTestBoardsAndBlocksStore(t, SetupTests) }) - t.Run("SubscriptionStore", func(t *testing.T) { storetests.StoreTestSubscriptionsStore(t, SetupTests) }) t.Run("NotificationHintStore", func(t *testing.T) { storetests.StoreTestNotificationHintsStore(t, SetupTests) }) t.Run("DataRetention", func(t *testing.T) { storetests.StoreTestDataRetention(t, SetupTests) }) t.Run("CloudStore", func(t *testing.T) { storetests.StoreTestCloudStore(t, SetupTests) }) diff --git a/server/services/store/sqlstore/team.go b/server/services/store/sqlstore/team.go index 3702d04cb..7a6c8d597 100644 --- a/server/services/store/sqlstore/team.go +++ b/server/services/store/sqlstore/team.go @@ -91,46 +91,6 @@ func (s *SQLStore) upsertTeamSettings(db sq.BaseRunner, team model.Team) error { return err } -func (s *SQLStore) getTeam(db sq.BaseRunner, id string) (*model.Team, error) { - var settingsJSON string - - query := s.getQueryBuilder(db). - Select( - "id", - "signup_token", - "COALESCE(settings, '{}')", - "modified_by", - "update_at", - ). - From(s.tablePrefix + "teams"). - Where(sq.Eq{"id": id}) - row := query.QueryRow() - team := model.Team{} - - err := row.Scan( - &team.ID, - &team.SignupToken, - &settingsJSON, - &team.ModifiedBy, - &team.UpdateAt, - ) - if err != nil { - return nil, err - } - - err = json.Unmarshal([]byte(settingsJSON), &team.Settings) - if err != nil { - s.logger.Error(`ERROR GetTeam settings json.Unmarshal`, mlog.Err(err)) - return nil, err - } - - return &team, nil -} - -func (s *SQLStore) getTeamsForUser(db sq.BaseRunner, _ string) ([]*model.Team, error) { - return s.getAllTeams(db) -} - func (s *SQLStore) getTeamCount(db sq.BaseRunner) (int64, error) { query := s.getQueryBuilder(db). Select( diff --git a/server/services/store/sqlstore/user.go b/server/services/store/sqlstore/user.go index 849d81917..ef5e747fd 100644 --- a/server/services/store/sqlstore/user.go +++ b/server/services/store/sqlstore/user.go @@ -1,23 +1,7 @@ package sqlstore import ( - "database/sql" - "errors" "fmt" - - mmModel "github.com/mattermost/mattermost/server/public/model" - "github.com/mattermost/mattermost/server/v8/channels/store" - - sq "github.com/Masterminds/squirrel" - - "github.com/mattermost/mattermost-plugin-boards/server/model" - "github.com/mattermost/mattermost-plugin-boards/server/utils" - - "github.com/mattermost/mattermost/server/public/shared/mlog" -) - -var ( - errUnsupportedOperation = errors.New("unsupported operation") ) type UserNotFoundError struct { @@ -27,393 +11,3 @@ type UserNotFoundError struct { func (unf UserNotFoundError) Error() string { return fmt.Sprintf("user not found (%s)", unf.id) } - -func (s *SQLStore) getRegisteredUserCount(db sq.BaseRunner) (int, error) { - query := s.getQueryBuilder(db). - Select("count(*)"). - From(s.tablePrefix + "users"). - Where(sq.Eq{"delete_at": 0}) - row := query.QueryRow() - - var count int - err := row.Scan(&count) - if err != nil { - return 0, err - } - - return count, nil -} - -func (s *SQLStore) getUserByCondition(db sq.BaseRunner, condition sq.Eq) (*model.User, error) { - users, err := s.getUsersByCondition(db, condition, 0) - if err != nil { - return nil, err - } - - if len(users) == 0 { - return nil, model.NewErrNotFound("user") - } - - return users[0], nil -} - -func (s *SQLStore) getUsersByCondition(db sq.BaseRunner, condition interface{}, limit uint64) ([]*model.User, error) { - query := s.getQueryBuilder(db). - Select( - "id", - "username", - "email", - "password", - "mfa_secret", - "auth_service", - "auth_data", - "create_at", - "update_at", - "delete_at", - ). - From(s.tablePrefix + "users"). - Where(sq.Eq{"delete_at": 0}). - Where(condition) - - if limit != 0 { - query = query.Limit(limit) - } - - rows, err := query.Query() - if err != nil { - s.logger.Error(`getUsersByCondition ERROR`, mlog.Err(err)) - return nil, err - } - defer s.CloseRows(rows) - - users, err := s.usersFromRows(rows) - if err != nil { - return nil, err - } - - if len(users) == 0 { - return nil, model.NewErrNotFound("user") - } - - return users, nil -} - -func (s *SQLStore) getUserByID(db sq.BaseRunner, userID string) (*model.User, error) { - return s.getUserByCondition(db, sq.Eq{"id": userID}) -} - -func (s *SQLStore) getUsersList(db sq.BaseRunner, userIDs []string, _, _ bool) ([]*model.User, error) { - users, err := s.getUsersByCondition(db, sq.Eq{"id": userIDs}, 0) - if err != nil { - return nil, err - } - - if len(users) != len(userIDs) { - return users, model.NewErrNotAllFound("user", userIDs) - } - - return users, nil -} - -func (s *SQLStore) getUserByEmail(db sq.BaseRunner, email string) (*model.User, error) { - return s.getUserByCondition(db, sq.Eq{"email": email}) -} - -func (s *SQLStore) getUserByUsername(db sq.BaseRunner, username string) (*model.User, error) { - return s.getUserByCondition(db, sq.Eq{"username": username}) -} - -func (s *SQLStore) createUser(db sq.BaseRunner, user *model.User) (*model.User, error) { - now := utils.GetMillis() - user.CreateAt = now - user.UpdateAt = now - user.DeleteAt = 0 - - query := s.getQueryBuilder(db).Insert(s.tablePrefix+"users"). - Columns("id", "username", "email", "password", "mfa_secret", "auth_service", "auth_data", "create_at", "update_at", "delete_at"). - Values(user.ID, user.Username, user.Email, user.Password, user.MfaSecret, user.AuthService, user.AuthData, user.CreateAt, user.UpdateAt, user.DeleteAt) - - _, err := query.Exec() - return user, err -} - -func (s *SQLStore) updateUser(db sq.BaseRunner, user *model.User) (*model.User, error) { - now := utils.GetMillis() - user.UpdateAt = now - - query := s.getQueryBuilder(db).Update(s.tablePrefix+"users"). - Set("username", user.Username). - Set("email", user.Email). - Set("update_at", user.UpdateAt). - Where(sq.Eq{"id": user.ID}) - - result, err := query.Exec() - if err != nil { - return nil, err - } - - rowCount, err := result.RowsAffected() - if err != nil { - return nil, err - } - - if rowCount < 1 { - return nil, UserNotFoundError{user.ID} - } - - return user, nil -} - -func (s *SQLStore) updateUserPassword(db sq.BaseRunner, username, password string) error { - now := utils.GetMillis() - - query := s.getQueryBuilder(db).Update(s.tablePrefix+"users"). - Set("password", password). - Set("update_at", now). - Where(sq.Eq{"username": username}) - - result, err := query.Exec() - if err != nil { - return err - } - - rowCount, err := result.RowsAffected() - if err != nil { - return err - } - - if rowCount < 1 { - return UserNotFoundError{username} - } - - return nil -} - -func (s *SQLStore) updateUserPasswordByID(db sq.BaseRunner, userID, password string) error { - now := utils.GetMillis() - - query := s.getQueryBuilder(db).Update(s.tablePrefix+"users"). - Set("password", password). - Set("update_at", now). - Where(sq.Eq{"id": userID}) - - result, err := query.Exec() - if err != nil { - return err - } - - rowCount, err := result.RowsAffected() - if err != nil { - return err - } - - if rowCount < 1 { - return UserNotFoundError{userID} - } - - return nil -} - -func (s *SQLStore) getUsersByTeam(db sq.BaseRunner, _ string, _ string, _, _ bool) ([]*model.User, error) { - users, err := s.getUsersByCondition(db, nil, 0) - if model.IsErrNotFound(err) { - return []*model.User{}, nil - } - - return users, err -} - -func (s *SQLStore) searchUsersByTeam(db sq.BaseRunner, _ string, searchQuery string, _ string, _, _, _ bool) ([]*model.User, error) { - users, err := s.getUsersByCondition(db, &sq.Like{"username": "%" + searchQuery + "%"}, 10) - if model.IsErrNotFound(err) { - return []*model.User{}, nil - } - - return users, err -} - -func (s *SQLStore) usersFromRows(rows *sql.Rows) ([]*model.User, error) { - users := []*model.User{} - - for rows.Next() { - var user model.User - - err := rows.Scan( - &user.ID, - &user.Username, - &user.Email, - &user.Password, - &user.MfaSecret, - &user.AuthService, - &user.AuthData, - &user.CreateAt, - &user.UpdateAt, - &user.DeleteAt, - ) - if err != nil { - return nil, err - } - - users = append(users, &user) - } - - return users, nil -} - -func (s *SQLStore) patchUserPreferences(db sq.BaseRunner, userID string, patch model.UserPreferencesPatch) (mmModel.Preferences, error) { - preferences, err := s.getUserPreferences(db, userID) - if err != nil { - return nil, err - } - - if len(patch.UpdatedFields) > 0 { - for key, value := range patch.UpdatedFields { - preference := mmModel.Preference{ - UserId: userID, - Category: model.PreferencesCategoryFocalboard, - Name: key, - Value: value, - } - - if err := s.updateUserPreference(db, preference); err != nil { - return nil, err - } - - newPreferences := mmModel.Preferences{} - for _, existingPreference := range preferences { - if preference.Name != existingPreference.Name { - newPreferences = append(newPreferences, existingPreference) - } - } - newPreferences = append(newPreferences, preference) - preferences = newPreferences - } - } - - if len(patch.DeletedFields) > 0 { - for _, key := range patch.DeletedFields { - preference := mmModel.Preference{ - UserId: userID, - Category: model.PreferencesCategoryFocalboard, - Name: key, - } - - if err := s.deleteUserPreference(db, preference); err != nil { - return nil, err - } - - newPreferences := mmModel.Preferences{} - for _, existingPreference := range preferences { - if preference.Name != existingPreference.Name { - newPreferences = append(newPreferences, existingPreference) - } - } - preferences = newPreferences - } - } - - return preferences, nil -} - -func (s *SQLStore) updateUserPreference(db sq.BaseRunner, preference mmModel.Preference) error { - query := s.getQueryBuilder(db). - Insert(s.tablePrefix+"preferences"). - Columns("UserId", "Category", "Name", "Value"). - Values(preference.UserId, preference.Category, preference.Name, preference.Value) - - switch s.dbType { - case model.MysqlDBType: - query = query.SuffixExpr(sq.Expr("ON DUPLICATE KEY UPDATE Value = ?", preference.Value)) - case model.PostgresDBType: - query = query.SuffixExpr(sq.Expr("ON CONFLICT (userid, category, name) DO UPDATE SET Value = ?", preference.Value)) - case model.SqliteDBType: - query = query.SuffixExpr(sq.Expr(" on conflict(userid, category, name) do update set value = excluded.value")) - default: - return store.NewErrNotImplemented("failed to update preference because of missing driver") - } - - if _, err := query.Exec(); err != nil { - return fmt.Errorf("failed to upsert user preference in database: userID: %s name: %s value: %s error: %w", preference.UserId, preference.Name, preference.Value, err) - } - - return nil -} - -func (s *SQLStore) deleteUserPreference(db sq.BaseRunner, preference mmModel.Preference) error { - query := s.getQueryBuilder(db). - Delete(s.tablePrefix + "preferences"). - Where(sq.Eq{"UserId": preference.UserId}). - Where(sq.Eq{"Category": preference.Category}). - Where(sq.Eq{"Name": preference.Name}) - - if _, err := query.Exec(); err != nil { - return fmt.Errorf("failed to delete user preference from database: %w", err) - } - - return nil -} - -func (s *SQLStore) canSeeUser(db sq.BaseRunner, seerID string, seenID string) (bool, error) { - return true, nil -} - -func (s *SQLStore) sendMessage(db sq.BaseRunner, message, postType string, receipts []string) error { - return errUnsupportedOperation -} - -func (s *SQLStore) postMessage(db sq.BaseRunner, message, postType string, channel string) error { - return errUnsupportedOperation -} - -func (s *SQLStore) getUserTimezone(_ sq.BaseRunner, _ string) (string, error) { - return "", errUnsupportedOperation -} - -func (s *SQLStore) getUserPreferences(db sq.BaseRunner, userID string) (mmModel.Preferences, error) { - query := s.getQueryBuilder(db). - Select("userid", "category", "name", "value"). - From(s.tablePrefix + "preferences"). - Where(sq.Eq{ - "userid": userID, - "category": model.PreferencesCategoryFocalboard, - }) - - rows, err := query.Query() - if err != nil { - s.logger.Error("failed to fetch user preferences", mlog.String("user_id", userID), mlog.Err(err)) - return nil, err - } - - defer rows.Close() - - preferences, err := s.preferencesFromRows(rows) - if err != nil { - return nil, err - } - - return preferences, nil -} - -func (s *SQLStore) preferencesFromRows(rows *sql.Rows) ([]mmModel.Preference, error) { - preferences := []mmModel.Preference{} - - for rows.Next() { - var preference mmModel.Preference - - err := rows.Scan( - &preference.UserId, - &preference.Category, - &preference.Name, - &preference.Value, - ) - - if err != nil { - s.logger.Error("failed to scan row for user preference", mlog.Err(err)) - return nil, err - } - - preferences = append(preferences, preference) - } - - return preferences, nil -} diff --git a/server/services/store/store.go b/server/services/store/store.go index 5109ea834..a51741835 100644 --- a/server/services/store/store.go +++ b/server/services/store/store.go @@ -60,22 +60,12 @@ type Store interface { GetUsersList(userIDs []string, showEmail, showName bool) ([]*model.User, error) GetUserByEmail(email string) (*model.User, error) GetUserByUsername(username string) (*model.User, error) - CreateUser(user *model.User) (*model.User, error) - UpdateUser(user *model.User) (*model.User, error) - UpdateUserPassword(username, password string) error - UpdateUserPasswordByID(userID, password string) error GetUsersByTeam(teamID string, asGuestID string, showEmail, showName bool) ([]*model.User, error) SearchUsersByTeam(teamID string, searchQuery string, asGuestID string, excludeBots bool, showEmail, showName bool) ([]*model.User, error) PatchUserPreferences(userID string, patch model.UserPreferencesPatch) (mmModel.Preferences, error) GetUserPreferences(userID string) (mmModel.Preferences, error) GetActiveUserCount(updatedSecondsAgo int64) (int, error) - GetSession(token string, expireTime int64) (*model.Session, error) - CreateSession(session *model.Session) error - RefreshSession(session *model.Session) error - UpdateSession(session *model.Session) error - DeleteSession(sessionID string) error - CleanUpSessions(expireTime int64) error UpsertSharing(sharing model.Sharing) error GetSharing(rootID string) (*model.Sharing, error) diff --git a/server/services/store/storetests/session.go b/server/services/store/storetests/session.go index 54dc5987a..c69a1b7d2 100644 --- a/server/services/store/storetests/session.go +++ b/server/services/store/storetests/session.go @@ -1,63 +1,19 @@ package storetests import ( - "fmt" "testing" "time" - "github.com/mattermost/mattermost-plugin-boards/server/model" "github.com/mattermost/mattermost-plugin-boards/server/services/store" "github.com/stretchr/testify/require" ) func StoreTestSessionStore(t *testing.T, setup func(t *testing.T) (store.Store, func())) { - t.Run("CreateAndGetAndDeleteSession", func(t *testing.T) { - store, tearDown := setup(t) - defer tearDown() - testCreateAndGetAndDeleteSession(t, store) - }) - t.Run("GetActiveUserCount", func(t *testing.T) { store, tearDown := setup(t) defer tearDown() testGetActiveUserCount(t, store) }) - - t.Run("UpdateSession", func(t *testing.T) { - store, tearDown := setup(t) - defer tearDown() - testUpdateSession(t, store) - }) -} - -func testCreateAndGetAndDeleteSession(t *testing.T, store store.Store) { - session := &model.Session{ - ID: "session-id", - Token: "token", - } - - t.Run("CreateAndGetSession", func(t *testing.T) { - err := store.CreateSession(session) - require.NoError(t, err) - - got, err := store.GetSession(session.Token, 60*60) - require.NoError(t, err) - require.Equal(t, session, got) - }) - - t.Run("Get nonexistent session", func(t *testing.T) { - got, err := store.GetSession("nonexistent-token", 60*60) - require.True(t, model.IsErrNotFound(err)) - require.Nil(t, got) - }) - - t.Run("DeleteAndGetSession", func(t *testing.T) { - err := store.DeleteSession(session.ID) - require.NoError(t, err) - - _, err = store.GetSession(session.Token, 60*60) - require.Error(t, err) - }) } func testGetActiveUserCount(t *testing.T, store store.Store) { @@ -70,38 +26,9 @@ func testGetActiveUserCount(t *testing.T, store store.Store) { t.Run("active user", func(t *testing.T) { // gen random count active user session count := int(time.Now().Unix() % 10) - for i := 0; i < count; i++ { - session := &model.Session{ - ID: fmt.Sprintf("id-%d", i), - UserID: fmt.Sprintf("user-id-%d", i), - Token: fmt.Sprintf("token-%d", i), - } - err := store.CreateSession(session) - require.NoError(t, err) - } got, err := store.GetActiveUserCount(60) require.NoError(t, err) require.Equal(t, count, got) }) } - -func testUpdateSession(t *testing.T, store store.Store) { - session := &model.Session{ - ID: "session-id", - Token: "token", - Props: map[string]interface{}{"field1": "A"}, - } - - err := store.CreateSession(session) - require.NoError(t, err) - - // update session - session.Props["field1"] = "B" - err = store.UpdateSession(session) - require.NoError(t, err) - - got, err := store.GetSession(session.Token, 60) - require.NoError(t, err) - require.Equal(t, session, got) -} diff --git a/server/services/store/storetests/subscriptions.go b/server/services/store/storetests/subscriptions.go deleted file mode 100644 index e4a213242..000000000 --- a/server/services/store/storetests/subscriptions.go +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -package storetests - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/mattermost/mattermost-plugin-boards/server/model" - "github.com/mattermost/mattermost-plugin-boards/server/services/store" -) - -//nolint:dupl -func StoreTestSubscriptionsStore(t *testing.T, setup func(t *testing.T) (store.Store, func())) { - t.Run("CreateSubscription", func(t *testing.T) { - store, tearDown := setup(t) - defer tearDown() - testCreateSubscription(t, store) - }) - - t.Run("DeleteSubscription", func(t *testing.T) { - store, tearDown := setup(t) - defer tearDown() - testDeleteSubscription(t, store) - }) - - t.Run("UndeleteSubscription", func(t *testing.T) { - store, tearDown := setup(t) - defer tearDown() - testUndeleteSubscription(t, store) - }) - - t.Run("GetSubscription", func(t *testing.T) { - store, tearDown := setup(t) - defer tearDown() - testGetSubscription(t, store) - }) - - t.Run("GetSubscriptions", func(t *testing.T) { - store, tearDown := setup(t) - defer tearDown() - testGetSubscriptions(t, store) - }) - - t.Run("GetSubscribersForBlock", func(t *testing.T) { - store, tearDown := setup(t) - defer tearDown() - testGetSubscribersForBlock(t, store) - }) -} - -func testCreateSubscription(t *testing.T, store store.Store) { - t.Run("create subscriptions", func(t *testing.T) { - users := createTestUsers(t, store, 10) - blocks := createTestBlocks(t, store, users[0].ID, 50) - - for i, user := range users { - for j := 0; j < i; j++ { - sub := &model.Subscription{ - BlockType: blocks[j].Type, - BlockID: blocks[j].ID, - SubscriberType: "user", - SubscriberID: user.ID, - } - subNew, err := store.CreateSubscription(sub) - require.NoError(t, err, "create subscription should not error") - - assert.NotZero(t, subNew.NotifiedAt) - assert.NotZero(t, subNew.CreateAt) - assert.Zero(t, subNew.DeleteAt) - } - } - - // ensure each user has the right number of subscriptions - for i, user := range users { - subs, err := store.GetSubscriptions(user.ID) - require.NoError(t, err, "get subscriptions should not error") - assert.Len(t, subs, i) - } - }) - - t.Run("duplicate subscription", func(t *testing.T) { - admin := createTestUsers(t, store, 1)[0] - user := createTestUsers(t, store, 1)[0] - block := createTestBlocks(t, store, admin.ID, 1)[0] - - sub := &model.Subscription{ - BlockType: block.Type, - BlockID: block.ID, - SubscriberType: "user", - SubscriberID: user.ID, - } - subNew, err := store.CreateSubscription(sub) - require.NoError(t, err, "create subscription should not error") - - sub = &model.Subscription{ - BlockType: block.Type, - BlockID: block.ID, - SubscriberType: "user", - SubscriberID: user.ID, - } - - subDup, err := store.CreateSubscription(sub) - require.NoError(t, err, "create duplicate subscription should not error") - - assert.Equal(t, subNew.BlockID, subDup.BlockID) - assert.Equal(t, subNew.SubscriberID, subDup.SubscriberID) - }) - - t.Run("invalid subscription", func(t *testing.T) { - admin := createTestUsers(t, store, 1)[0] - user := createTestUsers(t, store, 1)[0] - block := createTestBlocks(t, store, admin.ID, 1)[0] - - sub := &model.Subscription{} - - _, err := store.CreateSubscription(sub) - assert.ErrorAs(t, err, &model.ErrInvalidSubscription{}, "invalid subscription should error") - - sub.BlockType = block.Type - _, err = store.CreateSubscription(sub) - assert.ErrorAs(t, err, &model.ErrInvalidSubscription{}, "invalid subscription should error") - - sub.BlockID = block.ID - _, err = store.CreateSubscription(sub) - assert.ErrorAs(t, err, &model.ErrInvalidSubscription{}, "invalid subscription should error") - - sub.SubscriberType = "user" - _, err = store.CreateSubscription(sub) - assert.ErrorAs(t, err, &model.ErrInvalidSubscription{}, "invalid subscription should error") - - sub.SubscriberID = user.ID - subNew, err := store.CreateSubscription(sub) - assert.NoError(t, err, "valid subscription should not error") - - assert.NoError(t, subNew.IsValid(), "created subscription should be valid") - }) -} - -func testDeleteSubscription(t *testing.T, s store.Store) { - t.Run("delete subscription", func(t *testing.T) { - user := createTestUsers(t, s, 1)[0] - block := createTestBlocks(t, s, user.ID, 1)[0] - - sub := &model.Subscription{ - BlockType: block.Type, - BlockID: block.ID, - SubscriberType: "user", - SubscriberID: user.ID, - } - subNew, err := s.CreateSubscription(sub) - require.NoError(t, err, "create subscription should not error") - - // check the subscription exists - subs, err := s.GetSubscriptions(user.ID) - require.NoError(t, err, "get subscriptions should not error") - assert.Len(t, subs, 1) - assert.Equal(t, subNew.BlockID, subs[0].BlockID) - assert.Equal(t, subNew.SubscriberID, subs[0].SubscriberID) - - err = s.DeleteSubscription(block.ID, user.ID) - require.NoError(t, err, "delete subscription should not error") - - // check the subscription was deleted - subs, err = s.GetSubscriptions(user.ID) - require.NoError(t, err, "get subscriptions should not error") - assert.Empty(t, subs) - }) - - t.Run("delete non-existent subscription", func(t *testing.T) { - err := s.DeleteSubscription("bogus", "bogus") - require.Error(t, err, "delete non-existent subscription should error") - require.True(t, model.IsErrNotFound(err), "Should be ErrNotFound compatible error") - }) -} - -func testUndeleteSubscription(t *testing.T, s store.Store) { - t.Run("undelete subscription", func(t *testing.T) { - user := createTestUsers(t, s, 1)[0] - block := createTestBlocks(t, s, user.ID, 1)[0] - - sub := &model.Subscription{ - BlockType: block.Type, - BlockID: block.ID, - SubscriberType: "user", - SubscriberID: user.ID, - } - subNew, err := s.CreateSubscription(sub) - require.NoError(t, err, "create subscription should not error") - - // check the subscription exists - subs, err := s.GetSubscriptions(user.ID) - require.NoError(t, err, "get subscriptions should not error") - assert.Len(t, subs, 1) - assert.Equal(t, subNew.BlockID, subs[0].BlockID) - assert.Equal(t, subNew.SubscriberID, subs[0].SubscriberID) - - err = s.DeleteSubscription(block.ID, user.ID) - require.NoError(t, err, "delete subscription should not error") - - // check the subscription was deleted - subs, err = s.GetSubscriptions(user.ID) - require.NoError(t, err, "get subscriptions should not error") - assert.Empty(t, subs) - - // re-create the subscription - subUndeleted, err := s.CreateSubscription(sub) - require.NoError(t, err, "create subscription should not error") - - // check the undeleted subscription exists - subs, err = s.GetSubscriptions(user.ID) - require.NoError(t, err, "get subscriptions should not error") - assert.Len(t, subs, 1) - assert.Equal(t, subUndeleted.BlockID, subs[0].BlockID) - assert.Equal(t, subUndeleted.SubscriberID, subs[0].SubscriberID) - }) -} - -func testGetSubscription(t *testing.T, s store.Store) { - t.Run("get subscription", func(t *testing.T) { - user := createTestUsers(t, s, 1)[0] - block := createTestBlocks(t, s, user.ID, 1)[0] - - sub := &model.Subscription{ - BlockType: block.Type, - BlockID: block.ID, - SubscriberType: "user", - SubscriberID: user.ID, - } - subNew, err := s.CreateSubscription(sub) - require.NoError(t, err, "create subscription should not error") - - // make sure subscription can be fetched - sub, err = s.GetSubscription(block.ID, user.ID) - require.NoError(t, err, "get subscription should not error") - assert.Equal(t, subNew, sub) - }) - - t.Run("get non-existent subscription", func(t *testing.T) { - sub, err := s.GetSubscription("bogus", "bogus") - require.Error(t, err, "get non-existent subscription should error") - require.True(t, model.IsErrNotFound(err), "Should be ErrNotFound compatible error") - require.Nil(t, sub, "get subscription should return nil") - }) -} - -func testGetSubscriptions(t *testing.T, store store.Store) { - t.Run("get subscriptions", func(t *testing.T) { - author := createTestUsers(t, store, 1)[0] - user := createTestUsers(t, store, 1)[0] - blocks := createTestBlocks(t, store, author.ID, 50) - - for _, block := range blocks { - sub := &model.Subscription{ - BlockType: block.Type, - BlockID: block.ID, - SubscriberType: "user", - SubscriberID: user.ID, - } - _, err := store.CreateSubscription(sub) - require.NoError(t, err, "create subscription should not error") - } - - // ensure user has the right number of subscriptions - subs, err := store.GetSubscriptions(user.ID) - require.NoError(t, err, "get subscriptions should not error") - assert.Len(t, subs, len(blocks)) - - // ensure author has no subscriptions - subs, err = store.GetSubscriptions(author.ID) - require.NoError(t, err, "get subscriptions should not error") - assert.Empty(t, subs) - }) - - t.Run("get subscriptions for invalid user", func(t *testing.T) { - subs, err := store.GetSubscriptions("bogus") - require.NoError(t, err, "get subscriptions should not error") - assert.Empty(t, subs) - }) -} - -func testGetSubscribersForBlock(t *testing.T, store store.Store) { - t.Run("get subscribers for block", func(t *testing.T) { - users := createTestUsers(t, store, 50) - blocks := createTestBlocks(t, store, users[0].ID, 2) - - for _, user := range users { - sub := &model.Subscription{ - BlockType: blocks[1].Type, - BlockID: blocks[1].ID, - SubscriberType: "user", - SubscriberID: user.ID, - } - _, err := store.CreateSubscription(sub) - require.NoError(t, err, "create subscription should not error") - } - - // make sure block[1] has the right number of users subscribed - subs, err := store.GetSubscribersForBlock(blocks[1].ID) - require.NoError(t, err, "get subscribers for block should not error") - assert.Len(t, subs, 50) - - count, err := store.GetSubscribersCountForBlock(blocks[1].ID) - require.NoError(t, err, "get subscribers for block should not error") - assert.Equal(t, 50, count) - - // make sure block[0] has zero users subscribed - subs, err = store.GetSubscribersForBlock(blocks[0].ID) - require.NoError(t, err, "get subscribers for block should not error") - assert.Empty(t, subs) - - count, err = store.GetSubscribersCountForBlock(blocks[0].ID) - require.NoError(t, err, "get subscribers for block should not error") - assert.Zero(t, count) - }) - - t.Run("get subscribers for invalid block", func(t *testing.T) { - subs, err := store.GetSubscribersForBlock("bogus") - require.NoError(t, err, "get subscribers for block should not error") - assert.Empty(t, subs) - }) -} diff --git a/server/services/store/storetests/users.go b/server/services/store/storetests/users.go index 177517fda..7c327628e 100644 --- a/server/services/store/storetests/users.go +++ b/server/services/store/storetests/users.go @@ -4,9 +4,7 @@ package storetests import ( - "fmt" "testing" - "time" "github.com/stretchr/testify/require" @@ -17,72 +15,12 @@ import ( //nolint:dupl func StoreTestUserStore(t *testing.T, setup func(t *testing.T) (store.Store, func())) { - t.Run("GetUsersByTeam", func(t *testing.T) { - store, tearDown := setup(t) - defer tearDown() - testGetUsersByTeam(t, store) - }) t.Run("CreateAndGetUser", func(t *testing.T) { store, tearDown := setup(t) defer tearDown() testCreateAndGetUser(t, store) }) - - t.Run("GetUsersList", func(t *testing.T) { - store, tearDown := setup(t) - defer tearDown() - testGetUsersList(t, store) - }) - - t.Run("CreateAndUpdateUser", func(t *testing.T) { - store, tearDown := setup(t) - defer tearDown() - testCreateAndUpdateUser(t, store) - }) - - t.Run("CreateAndGetRegisteredUserCount", func(t *testing.T) { - store, tearDown := setup(t) - defer tearDown() - testCreateAndGetRegisteredUserCount(t, store) - }) - - t.Run("TestPatchUserProps", func(t *testing.T) { - store, tearDown := setup(t) - defer tearDown() - testPatchUserProps(t, store) - }) -} - -func testGetUsersByTeam(t *testing.T, store store.Store) { - t.Run("GetTeamUsers", func(t *testing.T) { - users, err := store.GetUsersByTeam("team_1", "", false, false) - require.Equal(t, 0, len(users)) - require.NoError(t, err) - - userID := utils.NewID(utils.IDTypeUser) - - user, err := store.CreateUser(&model.User{ - ID: userID, - Username: "darth.vader", - }) - require.NoError(t, err) - require.NotNil(t, user) - require.Equal(t, userID, user.ID) - require.Equal(t, "darth.vader", user.Username) - - defer func() { - _, _ = store.UpdateUser(&model.User{ - ID: userID, - DeleteAt: utils.GetMillis(), - }) - }() - - users, err = store.GetUsersByTeam("team_1", "", false, false) - require.Equal(t, 1, len(users)) - require.Equal(t, "darth.vader", users[0].Username) - require.NoError(t, err) - }) } func testCreateAndGetUser(t *testing.T, store store.Store) { @@ -92,12 +30,6 @@ func testCreateAndGetUser(t *testing.T, store store.Store) { Email: "mock@email.com", } - t.Run("CreateUser", func(t *testing.T) { - newUser, err := store.CreateUser(user) - require.NoError(t, err) - require.NotNil(t, newUser) - }) - t.Run("GetUserByID", func(t *testing.T) { got, err := store.GetUserByID(user.ID) require.NoError(t, err) @@ -143,203 +75,3 @@ func testCreateAndGetUser(t *testing.T, store store.Store) { require.Nil(t, got) }) } - -func testGetUsersList(t *testing.T, store store.Store) { - for _, id := range []string{"user1", "user2"} { - user := &model.User{ - ID: id, - Username: fmt.Sprintf("%s-username", id), - Email: fmt.Sprintf("%s@sample.com", id), - } - newUser, err := store.CreateUser(user) - require.NoError(t, err) - require.NotNil(t, newUser) - } - - testCases := []struct { - Name string - UserIDs []string - ExpectedError bool - ExpectedIDs []string - }{ - { - Name: "all of the IDs are found", - UserIDs: []string{"user1", "user2"}, - ExpectedError: false, - ExpectedIDs: []string{"user1", "user2"}, - }, - { - Name: "some of the IDs are found", - UserIDs: []string{"user2", "non-existent"}, - ExpectedError: true, - ExpectedIDs: []string{"user2"}, - }, - { - Name: "none of the IDs are found", - UserIDs: []string{"non-existent-1", "non-existent-2"}, - ExpectedError: true, - ExpectedIDs: []string{}, - }, - } - - for _, tc := range testCases { - t.Run(tc.Name, func(t *testing.T) { - users, err := store.GetUsersList(tc.UserIDs, false, false) - if tc.ExpectedError { - require.Error(t, err) - require.True(t, model.IsErrNotFound(err)) - } else { - require.NoError(t, err) - } - - userIDs := []string{} - for _, user := range users { - userIDs = append(userIDs, user.ID) - } - require.ElementsMatch(t, tc.ExpectedIDs, userIDs) - }) - } -} - -func testCreateAndUpdateUser(t *testing.T, store store.Store) { - user := &model.User{ - ID: utils.NewID(utils.IDTypeUser), - } - newUser, err := store.CreateUser(user) - require.NoError(t, err) - require.NotNil(t, newUser) - - t.Run("UpdateUser", func(t *testing.T) { - user.Username = "damao" - user.Email = "mock@email.com" - uUser, err := store.UpdateUser(user) - require.NoError(t, err) - require.NotNil(t, uUser) - require.Equal(t, user.Username, uUser.Username) - require.Equal(t, user.Email, uUser.Email) - - got, err := store.GetUserByID(user.ID) - require.NoError(t, err) - require.Equal(t, user.ID, got.ID) - require.Equal(t, user.Username, got.Username) - require.Equal(t, user.Email, got.Email) - }) - - t.Run("UpdateUserPassword", func(t *testing.T) { - newPassword := utils.NewID(utils.IDTypeNone) - err := store.UpdateUserPassword(user.Username, newPassword) - require.NoError(t, err) - - got, err := store.GetUserByUsername(user.Username) - require.NoError(t, err) - require.Equal(t, user.Username, got.Username) - require.Equal(t, newPassword, got.Password) - }) - - t.Run("UpdateUserPasswordByID", func(t *testing.T) { - newPassword := utils.NewID(utils.IDTypeNone) - err := store.UpdateUserPasswordByID(user.ID, newPassword) - require.NoError(t, err) - - got, err := store.GetUserByID(user.ID) - require.NoError(t, err) - require.Equal(t, user.ID, got.ID) - require.Equal(t, newPassword, got.Password) - }) -} - -func testCreateAndGetRegisteredUserCount(t *testing.T, store store.Store) { - randomN := int(time.Now().Unix() % 10) - for i := 0; i < randomN; i++ { - user, err := store.CreateUser(&model.User{ - ID: utils.NewID(utils.IDTypeUser), - }) - require.NoError(t, err) - require.NotNil(t, user) - } - - got, err := store.GetRegisteredUserCount() - require.NoError(t, err) - require.Equal(t, randomN, got) -} - -func testPatchUserProps(t *testing.T, store store.Store) { - user := &model.User{ - ID: utils.NewID(utils.IDTypeUser), - } - newUser, err := store.CreateUser(user) - require.NoError(t, err) - require.NotNil(t, newUser) - - key1 := "new_key_1" - key2 := "new_key_2" - key3 := "new_key_3" - - // Only update props - patch := model.UserPreferencesPatch{ - UpdatedFields: map[string]string{ - key1: "new_value_1", - key2: "new_value_2", - key3: "new_value_3", - }, - } - - userPreferences, err := store.PatchUserPreferences(user.ID, patch) - require.NoError(t, err) - require.Equal(t, 3, len(userPreferences)) - - for _, preference := range userPreferences { - switch preference.Name { - case key1: - require.Equal(t, "new_value_1", preference.Value) - case key2: - require.Equal(t, "new_value_2", preference.Value) - case key3: - require.Equal(t, "new_value_3", preference.Value) - } - } - - // Delete a prop - patch = model.UserPreferencesPatch{ - DeletedFields: []string{ - key1, - }, - } - - userPreferences, err = store.PatchUserPreferences(user.ID, patch) - require.NoError(t, err) - - for _, preference := range userPreferences { - switch preference.Name { - case key1: - t.Errorf("new_key_1 shouldn't exist in user preference as we just deleted it") - case key2: - require.Equal(t, "new_value_2", preference.Value) - case key3: - require.Equal(t, "new_value_3", preference.Value) - } - } - - // update and delete together - patch = model.UserPreferencesPatch{ - UpdatedFields: map[string]string{ - key3: "new_value_3_new_again", - }, - DeletedFields: []string{ - key2, - }, - } - userPreferences, err = store.PatchUserPreferences(user.ID, patch) - require.NoError(t, err) - - for _, preference := range userPreferences { - switch preference.Name { - case key1: - t.Errorf("new_key_1 shouldn't exist in user preference as we just deleted it") - case key2: - t.Errorf("new_key_2 shouldn't exist in user preference as we just deleted it") - case key3: - require.Equal(t, "new_value_3_new_again", preference.Value) - } - } -} diff --git a/server/services/store/storetests/util.go b/server/services/store/storetests/util.go index 7959485d0..9c169888c 100644 --- a/server/services/store/storetests/util.go +++ b/server/services/store/storetests/util.go @@ -15,23 +15,6 @@ import ( "github.com/stretchr/testify/require" ) -func createTestUsers(t *testing.T, store store.Store, num int) []*model.User { - var users []*model.User - for i := 0; i < num; i++ { - user := &model.User{ - ID: utils.NewID(utils.IDTypeUser), - Username: fmt.Sprintf("mooncake.%d", i), - Email: fmt.Sprintf("mooncake.%d@example.com", i), - } - newUser, err := store.CreateUser(user) - require.NoError(t, err) - require.NotNil(t, newUser) - - users = append(users, user) - } - return users -} - func createTestBlocks(t *testing.T, store store.Store, userID string, num int) []*model.Block { var blocks []*model.Block for i := 0; i < num; i++ { diff --git a/server/ws/server.go b/server/ws/server.go index b39f15854..16d2b432f 100644 --- a/server/ws/server.go +++ b/server/ws/server.go @@ -67,7 +67,7 @@ func (wss *websocketSession) isAuthenticated() bool { } // NewServer creates a new Server. -func NewServer(auth *auth.Auth, singleUserToken string, isMattermostAuth bool, logger mlog.LoggerIFace, store Store) *Server { +func NewServer(auth *auth.Auth, logger mlog.LoggerIFace, store Store) *Server { return &Server{ listeners: make(map[*websocketSession]bool), listenersByTeam: make(map[string][]*websocketSession), @@ -78,7 +78,7 @@ func NewServer(auth *auth.Auth, singleUserToken string, isMattermostAuth bool, l }, }, auth: auth, - isMattermostAuth: isMattermostAuth, + isMattermostAuth: true, logger: logger, store: store, } @@ -402,12 +402,7 @@ func (ws *Server) removeListenerFromBlock(listener *websocketSession, blockID st } func (ws *Server) getUserIDForToken(token string) string { - session, err := ws.auth.GetSession(token) - if session == nil || err != nil { - return "" - } - - return session.UserID + return "" } func (ws *Server) authenticateListener(wsSession *websocketSession, token string) { diff --git a/server/ws/server_test.go b/server/ws/server_test.go index b345d0796..585045b94 100644 --- a/server/ws/server_test.go +++ b/server/ws/server_test.go @@ -13,7 +13,7 @@ import ( ) func TestTeamSubscription(t *testing.T) { - server := NewServer(&auth.Auth{}, "token", false, &mlog.Logger{}, nil) + server := NewServer(&auth.Auth{}, &mlog.Logger{}, nil) session := &websocketSession{ conn: &websocket.Conn{}, mu: sync.Mutex{}, @@ -145,7 +145,7 @@ func TestTeamSubscription(t *testing.T) { } func TestBlocksSubscription(t *testing.T) { - server := NewServer(&auth.Auth{}, "token", false, &mlog.Logger{}, nil) + server := NewServer(&auth.Auth{}, &mlog.Logger{}, nil) session := &websocketSession{ conn: &websocket.Conn{}, mu: sync.Mutex{}, @@ -264,7 +264,7 @@ func TestBlocksSubscription(t *testing.T) { } func TestGetUserIDForTokenInSingleUserMode(t *testing.T) { - server := NewServer(&auth.Auth{}, "token", false, &mlog.Logger{}, nil) + server := NewServer(&auth.Auth{}, &mlog.Logger{}, nil) t.Run("Should return nothing if the token is empty", func(t *testing.T) { require.Empty(t, server.getUserIDForToken("")) diff --git a/webapp/src/pages/changePasswordPage.scss b/webapp/src/pages/changePasswordPage.scss deleted file mode 100644 index 15b37ec23..000000000 --- a/webapp/src/pages/changePasswordPage.scss +++ /dev/null @@ -1,73 +0,0 @@ -.ChangePasswordPage { - border: 1px solid #ccc; - border-radius: 15px; - width: 450px; - height: 400px; - margin: 150px auto; - padding: 40px; - display: flex; - align-items: center; - justify-content: flex-start; - flex-direction: column; - box-shadow: rgba(var(--center-channel-color-rgb), 0.1) 0 0 0 1px, - rgba(var(--center-channel-color-rgb), 0.3) 0 4px 8px; - - form { - display: flex; - flex-direction: column; - align-items: flex-start; - justify-content: center; - } - - @media screen and (max-width: 430px) { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - width: 100%; - height: 100%; - margin: auto; - padding-top: 10px; - } - - .title { - font-size: 16px; - font-weight: 500; - } - - .oldPassword, - .newPassword { - margin-bottom: 10px; - - label { - display: inline-block; - width: 140px; - } - - input { - display: inline-block; - width: 250px; - border: 1px solid #ccc; - border-radius: 4px; - padding: 7px; - min-height: 44px; - } - } - - form > .Button { - margin-top: 10px; - margin-bottom: 20px; - min-height: 38px; - min-width: 250px; - } - - .error { - color: #900000; - } - - .succeeded { - background-color: #cfc; - padding: 5px; - } -} diff --git a/webapp/src/pages/changePasswordPage.tsx b/webapp/src/pages/changePasswordPage.tsx deleted file mode 100644 index dc969729b..000000000 --- a/webapp/src/pages/changePasswordPage.tsx +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. -import React, {useState} from 'react' -import {Link} from 'react-router-dom' - -import Button from '../widgets/buttons/button' -import client from '../octoClient' -import './changePasswordPage.scss' -import {IUser} from '../user' -import {useAppSelector} from '../store/hooks' -import {getMe} from '../store/users' - -const ChangePasswordPage = () => { - const [oldPassword, setOldPassword] = useState('') - const [newPassword, setNewPassword] = useState('') - const [errorMessage, setErrorMessage] = useState('') - const [succeeded, setSucceeded] = useState(false) - const user = useAppSelector(getMe) - - if (!user) { - return ( -
-
{'Change Password'}
- {'Log in first'} -
- ) - } - - const handleSubmit = async (userId: string): Promise => { - const response = await client.changePassword(userId, oldPassword, newPassword) - if (response.code === 200) { - setOldPassword('') - setNewPassword('') - setErrorMessage('') - setSucceeded(true) - } else { - setErrorMessage(`Change password failed: ${response.json?.error}`) - } - } - - return ( -
-
{'Change Password'}
-
{ - e.preventDefault() - handleSubmit(user.id) - }} - > -
- { - setOldPassword(e.target.value) - setErrorMessage('') - }} - /> -
-
- { - setNewPassword(e.target.value) - setErrorMessage('') - }} - /> -
- -
- {errorMessage && -
- {errorMessage} -
- } - {succeeded && - {'Password changed, click to continue.'} - } - {!succeeded && - {'Cancel'} - } -
- ) -} - -export default React.memo(ChangePasswordPage) diff --git a/webapp/src/pages/loginPage.scss b/webapp/src/pages/loginPage.scss deleted file mode 100644 index 78f440d6b..000000000 --- a/webapp/src/pages/loginPage.scss +++ /dev/null @@ -1,68 +0,0 @@ -.LoginPage { - border: 1px solid #ccc; - border-radius: 15px; - width: 450px; - height: 400px; - margin: 150px auto; - display: flex; - align-items: center; - justify-content: flex-start; - flex-direction: column; - box-shadow: rgba(var(--center-channel-color-rgb), 0.1) 0 0 0 1px, - rgba(var(--center-channel-color-rgb), 0.3) 0 4px 8px; - - form { - display: flex; - flex-direction: column; - align-items: flex-start; - justify-content: center; - margin: 50px auto; - } - - @media screen and (max-width: 430px) { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - width: 100%; - height: 100%; - margin: auto; - padding-top: 10px; - } - - .title { - font-size: 16px; - font-weight: 500; - } - - .username, - .password { - margin-bottom: 10px; - - label { - display: inline-block; - width: 140px; - } - - input { - display: inline-block; - width: 250px; - border: 1px solid #ccc; - border-radius: 4px; - padding: 7px; - min-height: 44px; - } - } - - form > .Button { - margin-top: 10px; - margin-bottom: 20px; - min-height: 38px; - min-width: 250px; - } - - .error { - color: #900000; - } -} diff --git a/webapp/src/pages/loginPage.tsx b/webapp/src/pages/loginPage.tsx deleted file mode 100644 index 702b8e404..000000000 --- a/webapp/src/pages/loginPage.tsx +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. -import React, {useState} from 'react' -import {Link, Redirect, useLocation, useHistory} from 'react-router-dom' -import {FormattedMessage} from 'react-intl' - -import {useAppDispatch, useAppSelector} from '../store/hooks' -import {fetchMe, getLoggedIn} from '../store/users' - -import Button from '../widgets/buttons/button' -import client from '../octoClient' -import './loginPage.scss' - -const LoginPage = () => { - const [username, setUsername] = useState('') - const [password, setPassword] = useState('') - const [errorMessage, setErrorMessage] = useState('') - const dispatch = useAppDispatch() - const loggedIn = useAppSelector(getLoggedIn) - const queryParams = new URLSearchParams(useLocation().search) - const history = useHistory() - - const handleLogin = async (): Promise => { - const logged = await client.login(username, password) - if (logged) { - await dispatch(fetchMe()) - if (queryParams) { - history.push(queryParams.get('r') || '/') - } else { - history.push('/') - } - } else { - setErrorMessage('Login failed') - } - } - - if (loggedIn) { - return - } - - return ( -
-
{ - e.preventDefault() - handleLogin() - }} - > -
- -
-
- { - setUsername(e.target.value) - setErrorMessage('') - }} - /> -
-
- { - setPassword(e.target.value) - setErrorMessage('') - }} - /> -
- -
- - - - {errorMessage && -
- {errorMessage} -
- } -
- ) -} - -export default React.memo(LoginPage) diff --git a/webapp/src/pages/registerPage.scss b/webapp/src/pages/registerPage.scss deleted file mode 100644 index 20a47fe7a..000000000 --- a/webapp/src/pages/registerPage.scss +++ /dev/null @@ -1,69 +0,0 @@ -.RegisterPage { - border: 1px solid #ccc; - border-radius: 15px; - width: 450px; - height: 600px; - margin: 150px auto; - padding: 40px; - display: flex; - align-items: center; - justify-content: flex-start; - flex-direction: column; - box-shadow: rgba(var(--center-channel-color-rgb), 0.1) 0 0 0 1px, - rgba(var(--center-channel-color-rgb), 0.3) 0 4px 8px; - - form { - display: flex; - flex-direction: column; - align-items: flex-start; - justify-content: center; - } - - @media screen and (max-width: 430px) { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - width: 100%; - height: 100%; - margin: auto; - padding-top: 10px; - } - - .title { - font-size: 16px; - font-weight: 500; - } - - .email, - .username, - .password { - margin-bottom: 10px; - - label { - display: inline-block; - width: 140px; - } - - input { - display: inline-block; - width: 250px; - border: 1px solid #ccc; - border-radius: 4px; - min-height: 44px; - padding: 7px; - } - } - - form > .Button { - margin-top: 10px; - margin-bottom: 20px; - min-height: 38px; - min-width: 250px; - } - - .error { - color: #900000; - } -} diff --git a/webapp/src/pages/registerPage.tsx b/webapp/src/pages/registerPage.tsx deleted file mode 100644 index bb8c56095..000000000 --- a/webapp/src/pages/registerPage.tsx +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. -import React, {useState} from 'react' -import {useHistory, Link, Redirect} from 'react-router-dom' -import {FormattedMessage} from 'react-intl' - -import {useAppDispatch, useAppSelector} from '../store/hooks' -import {fetchMe, getLoggedIn} from '../store/users' - -import Button from '../widgets/buttons/button' -import client from '../octoClient' -import './registerPage.scss' - -const RegisterPage = () => { - const [username, setUsername] = useState('') - const [password, setPassword] = useState('') - const [email, setEmail] = useState('') - const [errorMessage, setErrorMessage] = useState('') - const history = useHistory() - const dispatch = useAppDispatch() - const loggedIn = useAppSelector(getLoggedIn) - - const handleRegister = async (): Promise => { - const queryString = new URLSearchParams(window.location.search) - const signupToken = queryString.get('t') || '' - - const response = await client.register(email, username, password, signupToken) - if (response.code === 200) { - const logged = await client.login(username, password) - if (logged) { - await dispatch(fetchMe()) - history.push('/') - } - } else if (response.code === 401) { - setErrorMessage('Invalid registration link, please contact your administrator') - } else { - setErrorMessage(`${response.json?.error}`) - } - } - - if (loggedIn) { - return - } - - return ( -
-
{ - e.preventDefault() - handleRegister() - }} - > -
- -
-
- setEmail(e.target.value.trim())} - /> -
-
- setUsername(e.target.value.trim())} - /> -
-
- setPassword(e.target.value)} - /> -
- -
- - - - {errorMessage && -
- {errorMessage} -
- } -
- ) -} - -export default React.memo(RegisterPage) diff --git a/webapp/src/router.tsx b/webapp/src/router.tsx index 01b67f1ee..02de04451 100644 --- a/webapp/src/router.tsx +++ b/webapp/src/router.tsx @@ -14,11 +14,8 @@ import {createBrowserHistory, History} from 'history' import {IAppWindow} from './types' import BoardPage from './pages/boardPage/boardPage' -import ChangePasswordPage from './pages/changePasswordPage' import WelcomePage from './pages/welcome/welcomePage' import ErrorPage from './pages/errorPage' -import LoginPage from './pages/loginPage' -import RegisterPage from './pages/registerPage' import {Utils} from './utils' import octoClient from './octoClient' import {setGlobalError, getGlobalError} from './store/globalError' @@ -29,8 +26,6 @@ import FBRoute from './route' declare let window: IAppWindow -const UUID_REGEX = new RegExp(/^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/) - function HomeToCurrentTeam(props: {path: string, exact: boolean}) { return ( { - const isPlugin = Utils.isFocalboardPlugin() let browserHistory: History if (props.history) { @@ -123,48 +117,30 @@ const FocalboardRouter = (props: Props): JSX.Element => { }, []) } - if (isPlugin) { - useEffect(() => { - if (window.frontendBaseURL) { - browserHistory.replace(window.location.pathname.replace(window.frontendBaseURL, '')) - } - }, []) - } + useEffect(() => { + if (window.frontendBaseURL) { + browserHistory.replace(window.location.pathname.replace(window.frontendBaseURL, '')) + } + }, []) return ( - {isPlugin && - } - {isPlugin && - - - } + + + + - - {!isPlugin && - - - } - {!isPlugin && - - - } - {!isPlugin && - - - } - @@ -194,21 +170,6 @@ const FocalboardRouter = (props: Props): JSX.Element => { > - - {!isPlugin && - { - const boardIdIsValidUUIDV4 = UUID_REGEX.test(boardId || '') - if (boardIdIsValidUUIDV4) { - return `/${Utils.buildOriginalPath('', boardId, viewId, cardId)}` - } - return '' - }} - > - - } ) From fdeee3717b0d00f2e4bd467613ee97f17a0f8763 Mon Sep 17 00:00:00 2001 From: Rajat Dabade Date: Tue, 16 Jul 2024 14:53:37 +0530 Subject: [PATCH 19/21] MM-59356: Fixing testcase for webapp and server (#6) * fix: webapp test cases * refactor: added rules to makefile and added github workflow for testing * chore: added ci github workflow * chore: updated github flow for ci and typos * chore: typefix * refactor: added rule to install golangci lint * chore: to trigger action * fix: error in ci.yml * refactor: updated rule * refactor: fix linter issue --- .github/workflows/ci.yml | 44 + Makefile | 8 +- server/api/archive.go | 2 - server/services/store/storetests/users.go | 1 - server/services/store/storetests/util.go | 17 - server/ws/server_test.go | 12 - webapp/__mocks__/fileMock.js | 3 + webapp/__mocks__/styleMock.js | 3 + .../addContentMenuItem.test.tsx.snap | 163 + .../blockIconSelector.test.tsx.snap | 199 + .../__snapshots__/cardBadges.test.tsx.snap | 47 + .../__snapshots__/cardDialog.test.tsx.snap | 1497 +++ .../__snapshots__/centerPanel.test.tsx.snap | 10139 ++++++++++++++++ ...nfirmAddUserForNotifications.test.tsx.snap | 170 + .../confirmationDialogBox.test.tsx.snap | 163 + .../__snapshots__/contentBlock.test.tsx.snap | 492 + .../__snapshots__/dialog.test.tsx.snap | 109 + .../__snapshots__/flashMessages.test.tsx.snap | 55 + .../markdownEditor.test.tsx.snap | 91 + .../__snapshots__/modal.test.tsx.snap | 105 + .../personSelector.test.tsx.snap | 1204 ++ .../propertyValueElement.test.tsx.snap | 179 + .../__snapshots__/rootPortal.test.tsx.snap | 13 + .../__snapshots__/topBar.test.tsx.snap | 54 + .../__snapshots__/viewMenu.test.tsx.snap | 897 ++ .../__snapshots__/viewTitle.test.tsx.snap | 269 + .../__snapshots__/workspace.test.tsx.snap | 2492 ++++ .../__snapshots__/blocksEditor.test.tsx.snap | 4 - .../__snapshots__/editor.test.tsx.snap | 2 - .../__snapshots__/rootInput.test.tsx.snap | 33 +- webapp/src/components/boardSelector.test.tsx | 2 +- .../__snapshots__/boardsUnfurl.test.tsx.snap | 4 +- .../boardsUnfurl/boardsUnfurl.test.tsx | 10 +- .../__snapshots__/calculation.test.tsx.snap | 2 - .../__snapshots__/options.test.tsx.snap | 13 +- .../createBoardFromTemplate.test.tsx | 2 +- .../kanban/__snapshots__/kanban.test.tsx.snap | 7 +- .../__snapshots__/calculation.test.tsx.snap | 7 +- .../calculationOptions.test.tsx.snap | 16 +- .../__snapshots__/shareBoard.test.tsx.snap | 125 +- .../__snapshots__/multiperson.test.tsx.snap | 25 +- .../__snapshots__/confirmPerson.test.tsx.snap | 50 +- .../person/__snapshots__/person.test.tsx.snap | 27 +- webapp/src/types/mattermost-webapp/index.d.ts | 4 +- webapp/src/utils.test.ts | 2 +- webapp/tsconfig.json | 6 +- 46 files changed, 18451 insertions(+), 318 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 webapp/__mocks__/fileMock.js create mode 100644 webapp/__mocks__/styleMock.js create mode 100644 webapp/src/components/__snapshots__/addContentMenuItem.test.tsx.snap create mode 100644 webapp/src/components/__snapshots__/blockIconSelector.test.tsx.snap create mode 100644 webapp/src/components/__snapshots__/cardBadges.test.tsx.snap create mode 100644 webapp/src/components/__snapshots__/cardDialog.test.tsx.snap create mode 100644 webapp/src/components/__snapshots__/centerPanel.test.tsx.snap create mode 100644 webapp/src/components/__snapshots__/confirmAddUserForNotifications.test.tsx.snap create mode 100644 webapp/src/components/__snapshots__/confirmationDialogBox.test.tsx.snap create mode 100644 webapp/src/components/__snapshots__/contentBlock.test.tsx.snap create mode 100644 webapp/src/components/__snapshots__/dialog.test.tsx.snap create mode 100644 webapp/src/components/__snapshots__/flashMessages.test.tsx.snap create mode 100644 webapp/src/components/__snapshots__/markdownEditor.test.tsx.snap create mode 100644 webapp/src/components/__snapshots__/modal.test.tsx.snap create mode 100644 webapp/src/components/__snapshots__/personSelector.test.tsx.snap create mode 100644 webapp/src/components/__snapshots__/propertyValueElement.test.tsx.snap create mode 100644 webapp/src/components/__snapshots__/rootPortal.test.tsx.snap create mode 100644 webapp/src/components/__snapshots__/topBar.test.tsx.snap create mode 100644 webapp/src/components/__snapshots__/viewMenu.test.tsx.snap create mode 100644 webapp/src/components/__snapshots__/viewTitle.test.tsx.snap create mode 100644 webapp/src/components/__snapshots__/workspace.test.tsx.snap diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..8b277888c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,44 @@ +name: Check-in tests + +on: + push: + branches: + - 'main' + - 'releases-**' + pull_request: + workflow_dispatch: + +env: + BRANCH_NAME: ${{ github.head_ref || github.ref_name }} + EXCLUDE_ENTERPRISE: true + +jobs: + webapp-test: + runs-on: ubuntu-20.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + path: "focalboard" + - name: npm ci + run: | + cd focalboard/webapp && npm ci + + - name: Set up Go + uses: actions/setup-go@v3 + with: + go-version: 1.21 + + - name: Setup Node + uses: actions/setup-node@v3 + with: + node-version: 20.11.0 + + - name: Lint & test webapp + run: cd focalboard; make webapp-ci + + - name: set up golangci-lint + run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.59.0 + + - name: Lint & test server + run: cd focalboard; make server-ci \ No newline at end of file diff --git a/Makefile b/Makefile index 0d0b096f3..79ba5ef6a 100644 --- a/Makefile +++ b/Makefile @@ -365,6 +365,8 @@ generate: ## Install and run code generators. cd server; go install github.com/golang/mock/mockgen@v1.6.0 cd server; go generate ./... +server-ci: server-lint + server-lint: ## Run linters on server code. @if ! [ -x "$$(command -v golangci-lint)" ]; then \ echo "golangci-lint is not installed. Please see https://github.com/golangci/golangci-lint#install-golangci-lint for installation instructions."; \ @@ -381,7 +383,7 @@ modd-precheck: webapp-ci: ## Webapp CI: linting & testing. cd webapp; npm run check cd webapp; npm run test - cd webapp: npm run check-types + cd webapp; npm run check-types webapp-test: ## jest tests for webapp cd webapp; npm run test @@ -392,6 +394,10 @@ watch-plugin: modd-precheck ## Run and upload the plugin to a development server live-watch-plugin: modd-precheck ## Run and update locally the plugin in the development server make live-watch +server-test: ## Run server tests + @echo Starting tests for server + cd server; go test -tags '$(BUILD_TAGS)' -race -v -coverpkg=./... -coverprofile=plugin-profile.coverage -count=1 -timeout=30m ./... + swagger: ## Generate swagger API spec and clients based on it. mkdir -p server/swagger/docs mkdir -p server/swagger/clients diff --git a/server/api/archive.go b/server/api/archive.go index 18be5b400..acbd6e750 100644 --- a/server/api/archive.go +++ b/server/api/archive.go @@ -205,6 +205,4 @@ func (a *API) handleArchiveExportTeam(w http.ResponseWriter, r *http.Request) { // schema: // "$ref": "#/definitions/ErrorResponse" a.errorResponse(w, r, model.NewErrNotImplemented("not permitted in plugin mode")) - return - } diff --git a/server/services/store/storetests/users.go b/server/services/store/storetests/users.go index 7c327628e..4dde0b075 100644 --- a/server/services/store/storetests/users.go +++ b/server/services/store/storetests/users.go @@ -15,7 +15,6 @@ import ( //nolint:dupl func StoreTestUserStore(t *testing.T, setup func(t *testing.T) (store.Store, func())) { - t.Run("CreateAndGetUser", func(t *testing.T) { store, tearDown := setup(t) defer tearDown() diff --git a/server/services/store/storetests/util.go b/server/services/store/storetests/util.go index 9c169888c..317d0235c 100644 --- a/server/services/store/storetests/util.go +++ b/server/services/store/storetests/util.go @@ -15,23 +15,6 @@ import ( "github.com/stretchr/testify/require" ) -func createTestBlocks(t *testing.T, store store.Store, userID string, num int) []*model.Block { - var blocks []*model.Block - for i := 0; i < num; i++ { - block := &model.Block{ - ID: utils.NewID(utils.IDTypeBlock), - BoardID: utils.NewID(utils.IDTypeBoard), - Type: model.TypeCard, - CreatedBy: userID, - } - err := store.InsertBlock(block, userID) - require.NoError(t, err) - - blocks = append(blocks, block) - } - return blocks -} - func createTestBlocksForCard(t *testing.T, store store.Store, cardID string, num int) []*model.Block { card, err := store.GetBlock(cardID) require.NoError(t, err) diff --git a/server/ws/server_test.go b/server/ws/server_test.go index 585045b94..07bbb27d0 100644 --- a/server/ws/server_test.go +++ b/server/ws/server_test.go @@ -262,15 +262,3 @@ func TestBlocksSubscription(t *testing.T) { require.Empty(t, server.listenersByBlock[blockID3]) }) } - -func TestGetUserIDForTokenInSingleUserMode(t *testing.T) { - server := NewServer(&auth.Auth{}, &mlog.Logger{}, nil) - - t.Run("Should return nothing if the token is empty", func(t *testing.T) { - require.Empty(t, server.getUserIDForToken("")) - }) - - t.Run("Should return nothing if the token is invalid", func(t *testing.T) { - require.Empty(t, server.getUserIDForToken("invalid-token")) - }) -} diff --git a/webapp/__mocks__/fileMock.js b/webapp/__mocks__/fileMock.js new file mode 100644 index 000000000..fe29b3a27 --- /dev/null +++ b/webapp/__mocks__/fileMock.js @@ -0,0 +1,3 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. +module.exports = 'test-file-stub'; diff --git a/webapp/__mocks__/styleMock.js b/webapp/__mocks__/styleMock.js new file mode 100644 index 000000000..a8ab4d252 --- /dev/null +++ b/webapp/__mocks__/styleMock.js @@ -0,0 +1,3 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. +module.exports = {}; diff --git a/webapp/src/components/__snapshots__/addContentMenuItem.test.tsx.snap b/webapp/src/components/__snapshots__/addContentMenuItem.test.tsx.snap new file mode 100644 index 000000000..60bac1973 --- /dev/null +++ b/webapp/src/components/__snapshots__/addContentMenuItem.test.tsx.snap @@ -0,0 +1,163 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`components/addContentMenuItem return a checkbox menu item 1`] = ` +
+ +`; + +exports[`components/addContentMenuItem return a divider menu item 1`] = ` +
+ +`; + +exports[`components/addContentMenuItem return a text menu item 1`] = ` +
+ +`; + +exports[`components/addContentMenuItem return an error and empty element from unknown type 1`] = `
`; + +exports[`components/addContentMenuItem return an image menu item 1`] = ` +
+ +`; diff --git a/webapp/src/components/__snapshots__/blockIconSelector.test.tsx.snap b/webapp/src/components/__snapshots__/blockIconSelector.test.tsx.snap new file mode 100644 index 000000000..eab155068 --- /dev/null +++ b/webapp/src/components/__snapshots__/blockIconSelector.test.tsx.snap @@ -0,0 +1,199 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`components/blockIconSelector return an icon correctly 1`] = ` +
+
+ +
+
+`; + +exports[`components/blockIconSelector return menu on click 1`] = ` +
+
+