2020-11-06 16:46:35 +01:00
|
|
|
package app
|
|
|
|
|
|
|
|
import (
|
2021-01-26 23:13:46 +01:00
|
|
|
"github.com/mattermost/focalboard/server/model"
|
|
|
|
"github.com/mattermost/focalboard/server/services/auth"
|
2021-10-05 15:52:59 +02:00
|
|
|
"github.com/mattermost/focalboard/server/utils"
|
2020-11-06 16:46:35 +01:00
|
|
|
|
2021-08-25 22:08:01 +02:00
|
|
|
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
|
|
|
|
2020-11-06 16:46:35 +01:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
2021-06-21 11:21:42 +02:00
|
|
|
const (
|
|
|
|
DaysPerMonth = 30
|
|
|
|
DaysPerWeek = 7
|
|
|
|
HoursPerDay = 24
|
|
|
|
MinutesPerHour = 60
|
|
|
|
SecondsPerMinute = 60
|
|
|
|
)
|
|
|
|
|
|
|
|
// GetSession Get a user active session and refresh the session if is needed.
|
2020-12-02 21:12:14 +01:00
|
|
|
func (a *App) GetSession(token string) (*model.Session, error) {
|
2021-02-02 21:11:21 +01:00
|
|
|
return a.auth.GetSession(token)
|
2020-12-02 21:12:14 +01:00
|
|
|
}
|
|
|
|
|
2021-06-21 11:21:42 +02:00
|
|
|
// IsValidReadToken validates the read token for a block.
|
2022-03-22 15:24:34 +01:00
|
|
|
func (a *App) IsValidReadToken(boardID string, readToken string) (bool, error) {
|
|
|
|
return a.auth.IsValidReadToken(boardID, readToken)
|
2021-02-03 03:15:03 +01:00
|
|
|
}
|
|
|
|
|
2021-06-21 11:21:42 +02:00
|
|
|
// GetRegisteredUserCount returns the number of registered users.
|
2021-01-27 18:22:33 +01:00
|
|
|
func (a *App) GetRegisteredUserCount() (int, error) {
|
|
|
|
return a.store.GetRegisteredUserCount()
|
2021-01-14 01:56:01 +01:00
|
|
|
}
|
|
|
|
|
2021-06-21 11:21:42 +02:00
|
|
|
// GetDailyActiveUsers returns the number of daily active users.
|
2021-01-27 19:01:24 +01:00
|
|
|
func (a *App) GetDailyActiveUsers() (int, error) {
|
2021-06-21 11:21:42 +02:00
|
|
|
secondsAgo := int64(SecondsPerMinute * MinutesPerHour * HoursPerDay)
|
2021-01-27 19:01:24 +01:00
|
|
|
return a.store.GetActiveUserCount(secondsAgo)
|
|
|
|
}
|
|
|
|
|
2021-06-21 11:21:42 +02:00
|
|
|
// GetWeeklyActiveUsers returns the number of weekly active users.
|
2021-01-27 19:01:24 +01:00
|
|
|
func (a *App) GetWeeklyActiveUsers() (int, error) {
|
2021-06-21 11:21:42 +02:00
|
|
|
secondsAgo := int64(SecondsPerMinute * MinutesPerHour * HoursPerDay * DaysPerWeek)
|
2021-01-27 19:01:24 +01:00
|
|
|
return a.store.GetActiveUserCount(secondsAgo)
|
|
|
|
}
|
|
|
|
|
2021-06-21 11:21:42 +02:00
|
|
|
// GetMonthlyActiveUsers returns the number of monthly active users.
|
2021-01-27 19:01:24 +01:00
|
|
|
func (a *App) GetMonthlyActiveUsers() (int, error) {
|
2021-06-21 11:21:42 +02:00
|
|
|
secondsAgo := int64(SecondsPerMinute * MinutesPerHour * HoursPerDay * DaysPerMonth)
|
2021-01-27 19:01:24 +01:00
|
|
|
return a.store.GetActiveUserCount(secondsAgo)
|
|
|
|
}
|
|
|
|
|
2021-06-21 11:21:42 +02:00
|
|
|
// GetUser gets an existing active user by id.
|
|
|
|
func (a *App) GetUser(id string) (*model.User, error) {
|
|
|
|
if len(id) < 1 {
|
2021-01-12 19:53:48 +01:00
|
|
|
return nil, errors.New("no user ID")
|
|
|
|
}
|
|
|
|
|
2021-06-21 11:21:42 +02:00
|
|
|
user, err := a.store.GetUserByID(id)
|
2020-12-07 20:40:16 +01:00
|
|
|
if err != nil {
|
2021-01-20 18:47:08 +01:00
|
|
|
return nil, errors.Wrap(err, "unable to find user")
|
2020-12-07 20:40:16 +01:00
|
|
|
}
|
|
|
|
return user, nil
|
|
|
|
}
|
|
|
|
|
2021-06-21 11:21:42 +02:00
|
|
|
// Login create a new user session if the authentication data is valid.
|
2021-03-21 09:28:26 +01:00
|
|
|
func (a *App) Login(username, email, password, mfaToken string) (string, error) {
|
2020-11-06 16:46:35 +01:00
|
|
|
var user *model.User
|
|
|
|
if username != "" {
|
|
|
|
var err error
|
|
|
|
user, err = a.store.GetUserByUsername(username)
|
|
|
|
if err != nil {
|
2021-06-04 16:38:49 +02:00
|
|
|
a.metrics.IncrementLoginFailCount(1)
|
2020-11-06 16:46:35 +01:00
|
|
|
return "", errors.Wrap(err, "invalid username or password")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if user == nil && email != "" {
|
|
|
|
var err error
|
|
|
|
user, err = a.store.GetUserByEmail(email)
|
|
|
|
if err != nil {
|
2021-06-04 16:38:49 +02:00
|
|
|
a.metrics.IncrementLoginFailCount(1)
|
2020-11-06 16:46:35 +01:00
|
|
|
return "", errors.Wrap(err, "invalid username or password")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if user == nil {
|
2021-06-04 16:38:49 +02:00
|
|
|
a.metrics.IncrementLoginFailCount(1)
|
2020-11-06 16:46:35 +01:00
|
|
|
return "", errors.New("invalid username or password")
|
|
|
|
}
|
|
|
|
|
|
|
|
if !auth.ComparePassword(user.Password, password) {
|
2021-06-04 16:38:49 +02:00
|
|
|
a.metrics.IncrementLoginFailCount(1)
|
2021-05-29 08:23:10 +02:00
|
|
|
a.logger.Debug("Invalid password for user", mlog.String("userID", user.ID))
|
2020-11-06 16:46:35 +01:00
|
|
|
return "", errors.New("invalid username or password")
|
|
|
|
}
|
|
|
|
|
2021-03-26 19:01:54 +01:00
|
|
|
authService := user.AuthService
|
|
|
|
if authService == "" {
|
|
|
|
authService = "native"
|
|
|
|
}
|
|
|
|
|
2020-12-02 21:12:14 +01:00
|
|
|
session := model.Session{
|
2021-10-05 15:52:59 +02:00
|
|
|
ID: utils.NewID(utils.IDTypeSession),
|
|
|
|
Token: utils.NewID(utils.IDTypeToken),
|
2021-03-26 19:01:54 +01:00
|
|
|
UserID: user.ID,
|
|
|
|
AuthService: authService,
|
|
|
|
Props: map[string]interface{}{},
|
2020-12-02 21:12:14 +01:00
|
|
|
}
|
|
|
|
err := a.store.CreateSession(&session)
|
|
|
|
if err != nil {
|
|
|
|
return "", errors.Wrap(err, "unable to create session")
|
|
|
|
}
|
|
|
|
|
2021-06-04 16:38:49 +02:00
|
|
|
a.metrics.IncrementLoginCount(1)
|
|
|
|
|
2020-11-06 16:46:35 +01:00
|
|
|
// TODO: MFA verification
|
2020-12-02 21:12:14 +01:00
|
|
|
return session.Token, nil
|
2020-11-06 16:46:35 +01:00
|
|
|
}
|
|
|
|
|
2021-12-01 10:21:31 +01:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2021-06-21 11:21:42 +02:00
|
|
|
// RegisterUser creates a new user if the provided data is valid.
|
2021-03-21 09:28:26 +01:00
|
|
|
func (a *App) RegisterUser(username, email, password string) error {
|
2020-11-06 16:46:35 +01:00
|
|
|
var user *model.User
|
|
|
|
if username != "" {
|
|
|
|
var err error
|
|
|
|
user, err = a.store.GetUserByUsername(username)
|
|
|
|
if err == nil && user != nil {
|
2021-01-14 19:58:16 +01:00
|
|
|
return errors.New("The username already exists")
|
2020-11-06 16:46:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if user == nil && email != "" {
|
|
|
|
var err error
|
|
|
|
user, err = a.store.GetUserByEmail(email)
|
|
|
|
if err == nil && user != nil {
|
2021-01-14 19:58:16 +01:00
|
|
|
return errors.New("The email already exists")
|
2020-11-06 16:46:35 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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{
|
2021-10-05 15:52:59 +02:00
|
|
|
ID: utils.NewID(utils.IDTypeUser),
|
2020-11-06 16:46:35 +01:00
|
|
|
Username: username,
|
|
|
|
Email: email,
|
|
|
|
Password: auth.HashPassword(password),
|
|
|
|
MfaSecret: "",
|
2021-03-26 19:01:54 +01:00
|
|
|
AuthService: a.config.AuthMode,
|
2020-11-06 16:46:35 +01:00
|
|
|
AuthData: "",
|
|
|
|
Props: map[string]interface{}{},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "Unable to create the new user")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2021-01-20 22:52:25 +01:00
|
|
|
|
2021-03-21 09:28:26 +01:00
|
|
|
func (a *App) UpdateUserPassword(username, password string) error {
|
2021-01-20 22:52:25 +01:00
|
|
|
err := a.store.UpdateUserPassword(username, auth.HashPassword(password))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2021-01-22 20:28:45 +01:00
|
|
|
|
2021-03-21 09:28:26 +01:00
|
|
|
func (a *App) ChangePassword(userID, oldPassword, newPassword string) error {
|
2021-01-21 19:16:40 +01:00
|
|
|
var user *model.User
|
|
|
|
if userID != "" {
|
|
|
|
var err error
|
2021-06-21 11:21:42 +02:00
|
|
|
user, err = a.store.GetUserByID(userID)
|
2021-01-21 19:16:40 +01:00
|
|
|
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) {
|
2021-05-29 08:23:10 +02:00
|
|
|
a.logger.Debug("Invalid password for user", mlog.String("userID", user.ID))
|
2021-01-21 19:16:40 +01:00
|
|
|
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
|
|
|
|
}
|