ab3bf6312c
* Persistent user config (#2133) * Added user config API * Add unit tests * lint fix * Fixed webapp tests * Fixed webapp tests * Updated props in store after updating * Minor fixes * Removed redundent data from audit logs * Onboarding Tour (#2287) * Created private board * Roughly displayed tour * Synced with Dhama's changes * WIP * Trying to add GIF * Added 3 tour steps * WIP * WIP * WIP * checked in missed file * Synced with feature branch * WIp * Adde skip tour option * Fixed image loading for on-prem * Made tour work on presonal server: * Adde missed file * Adding telemetry * Adding telemetry * Added tour tip telemetry * Fixed pulsating dot styling for personal server * reverted personal config * Added reset tour button * Displayed share tour tip of feature is enabled * Lint fixes * Fixed webapp tests * Fixed webapp tests * Completed webapp tests * Completed webapp tests * Webapp lint fixes * Added server tests * Testing cypress skip tour fix * Fixed Cypress tests * Added share board tour step * Added share board tour step * webapp lint fixes * Updated logic to pick welcome board * Updated tests: * lint fixes * Updating UI changes * Fixed a bug causing card tour to re-appear * FIxed minor issue * FIxed bug where card tour didn't start in clickingh on card * Fixed tests * Make update user props use string instead of interface * Fixed a value type * Updating gif size * Updating resolution breakpoint * Updating tutorial tip * Updating view selector * Refactored tour components * Misc fixes * minor refactoring * GH-2258: allow date range to overflow (#2268) * allow date range to overflow * Fixed issue with date overflowing into neighbouring column Co-authored-by: Harshil Sharma <harshilsharma63@gmail.com> Co-authored-by: Mattermod <mattermod@users.noreply.github.com> * Update readme with accurate Linux standalone app build instructions (#2351) * Bump follow-redirects from 1.14.7 to 1.14.8 in /experiments/webext (#2339) Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.7 to 1.14.8. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.7...v1.14.8) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Switch component style fixed: selector specificity increased by adding additional class. (#2179) * Adding sever side undelete endpoint (#2222) * Adding sever side undelete endpoint * Removing long lines golangci-lint errors * Fixing linter errors * Fixing a test problem * Fixing tests Co-authored-by: Mattermod <mattermod@users.noreply.github.com> * Removing transactions from sqlite backend (#2361) * Removing transactions from sqlite backend * Skipping tests in sqlite because the lack of transactions * Generating the mocks * Fixing golangci-lint * Fixing problem opening the tour tooltip on card open * Fixing texts missmatch * Adding the Product Tour entry in the user settings menu * Fixing some tests * Fixing tests Co-authored-by: Asaad Mahmood <asaadmahmood@users.noreply.github.com> Co-authored-by: Scott Bishel <scott.bishel@mattermost.com> Co-authored-by: Mattermod <mattermod@users.noreply.github.com> Co-authored-by: Doug Lauder <wiggin77@warpmail.net> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: kamre <eremchenko@gmail.com> Co-authored-by: Jesús Espino <jespinog@gmail.com> * Restored package json * Restored package json Co-authored-by: Asaad Mahmood <asaadmahmood@users.noreply.github.com> Co-authored-by: Scott Bishel <scott.bishel@mattermost.com> Co-authored-by: Mattermod <mattermod@users.noreply.github.com> Co-authored-by: Doug Lauder <wiggin77@warpmail.net> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: kamre <eremchenko@gmail.com> Co-authored-by: Jesús Espino <jespinog@gmail.com>
257 lines
5.2 KiB
Go
257 lines
5.2 KiB
Go
package sqlstore
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
|
|
"github.com/mattermost/focalboard/server/model"
|
|
"github.com/mattermost/focalboard/server/utils"
|
|
|
|
sq "github.com/Masterminds/squirrel"
|
|
)
|
|
|
|
type UserNotFoundError struct {
|
|
id string
|
|
}
|
|
|
|
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)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(users) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
return users[0], nil
|
|
}
|
|
|
|
func (s *SQLStore) getUsersByCondition(db sq.BaseRunner, condition sq.Eq) ([]*model.User, error) {
|
|
query := s.getQueryBuilder(db).
|
|
Select(
|
|
"id",
|
|
"username",
|
|
"email",
|
|
"password",
|
|
"mfa_secret",
|
|
"auth_service",
|
|
"auth_data",
|
|
"props",
|
|
"create_at",
|
|
"update_at",
|
|
"delete_at",
|
|
).
|
|
From(s.tablePrefix + "users").
|
|
Where(sq.Eq{"delete_at": 0}).
|
|
Where(condition)
|
|
rows, err := query.Query()
|
|
if err != nil {
|
|
log.Printf("getUsersByCondition ERROR: %v", 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, sql.ErrNoRows
|
|
}
|
|
|
|
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) 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) error {
|
|
now := utils.GetMillis()
|
|
|
|
propsBytes, err := json.Marshal(user.Props)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
query := s.getQueryBuilder(db).Insert(s.tablePrefix+"users").
|
|
Columns("id", "username", "email", "password", "mfa_secret", "auth_service", "auth_data", "props", "create_at", "update_at", "delete_at").
|
|
Values(user.ID, user.Username, user.Email, user.Password, user.MfaSecret, user.AuthService, user.AuthData, propsBytes, now, now, 0)
|
|
|
|
_, err = query.Exec()
|
|
return err
|
|
}
|
|
|
|
func (s *SQLStore) updateUser(db sq.BaseRunner, user *model.User) error {
|
|
now := utils.GetMillis()
|
|
|
|
propsBytes, err := json.Marshal(user.Props)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
query := s.getQueryBuilder(db).Update(s.tablePrefix+"users").
|
|
Set("username", user.Username).
|
|
Set("email", user.Email).
|
|
Set("props", propsBytes).
|
|
Set("update_at", now).
|
|
Where(sq.Eq{"id": user.ID})
|
|
|
|
result, err := query.Exec()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
rowCount, err := result.RowsAffected()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if rowCount < 1 {
|
|
return UserNotFoundError{user.ID}
|
|
}
|
|
|
|
return 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) getUsersByWorkspace(db sq.BaseRunner, _ string) ([]*model.User, error) {
|
|
return s.getUsersByCondition(db, nil)
|
|
}
|
|
|
|
func (s *SQLStore) usersFromRows(rows *sql.Rows) ([]*model.User, error) {
|
|
users := []*model.User{}
|
|
|
|
for rows.Next() {
|
|
var user model.User
|
|
var propsBytes []byte
|
|
|
|
err := rows.Scan(
|
|
&user.ID,
|
|
&user.Username,
|
|
&user.Email,
|
|
&user.Password,
|
|
&user.MfaSecret,
|
|
&user.AuthService,
|
|
&user.AuthData,
|
|
&propsBytes,
|
|
&user.CreateAt,
|
|
&user.UpdateAt,
|
|
&user.DeleteAt,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = json.Unmarshal(propsBytes, &user.Props)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
users = append(users, &user)
|
|
}
|
|
|
|
return users, nil
|
|
}
|
|
|
|
func (s *SQLStore) patchUserProps(db sq.BaseRunner, userID string, patch model.UserPropPatch) error {
|
|
user, err := s.getUserByID(db, userID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if user.Props == nil {
|
|
user.Props = map[string]interface{}{}
|
|
}
|
|
|
|
for _, key := range patch.DeletedFields {
|
|
delete(user.Props, key)
|
|
}
|
|
|
|
for key, value := range patch.UpdatedFields {
|
|
user.Props[key] = value
|
|
}
|
|
|
|
return s.updateUser(db, user)
|
|
}
|