2016-09-26 18:48:55 +00:00
|
|
|
package service
|
2016-09-01 04:51:38 +00:00
|
|
|
|
|
|
|
|
import (
|
2017-03-15 15:55:30 +00:00
|
|
|
"context"
|
2017-01-15 23:23:09 +00:00
|
|
|
"errors"
|
|
|
|
|
"unicode"
|
2016-09-15 19:27:55 +00:00
|
|
|
|
2021-11-22 14:13:26 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
|
2021-06-26 04:46:51 +00:00
|
|
|
"github.com/fleetdm/fleet/v4/server/contexts/viewer"
|
|
|
|
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
2016-09-01 04:51:38 +00:00
|
|
|
)
|
|
|
|
|
|
2021-06-06 22:07:29 +00:00
|
|
|
func (mw validationMiddleware) CreateUserFromInvite(ctx context.Context, p fleet.UserPayload) (*fleet.User, error) {
|
|
|
|
|
invalid := &fleet.InvalidArgumentError{}
|
2021-06-24 20:42:29 +00:00
|
|
|
if p.Name == nil {
|
|
|
|
|
invalid.Append("name", "Full name missing required argument")
|
2016-12-15 17:28:53 +00:00
|
|
|
} else {
|
2021-06-24 20:42:29 +00:00
|
|
|
if *p.Name == "" {
|
|
|
|
|
invalid.Append("name", "Full name cannot be empty")
|
2016-12-15 17:28:53 +00:00
|
|
|
}
|
2016-09-15 19:27:55 +00:00
|
|
|
}
|
2016-12-15 17:28:53 +00:00
|
|
|
|
2017-05-10 16:26:05 +00:00
|
|
|
// we don't need a password for single sign on
|
2017-12-04 14:43:43 +00:00
|
|
|
if p.SSOInvite == nil || !*p.SSOInvite {
|
2017-05-10 16:26:05 +00:00
|
|
|
if p.Password == nil {
|
2021-06-24 20:42:29 +00:00
|
|
|
invalid.Append("password", "Password missing required argument")
|
2017-05-10 16:26:05 +00:00
|
|
|
} else {
|
|
|
|
|
if *p.Password == "" {
|
2021-06-24 20:42:29 +00:00
|
|
|
invalid.Append("password", "Password cannot be empty")
|
2017-05-10 16:26:05 +00:00
|
|
|
}
|
|
|
|
|
if err := validatePasswordRequirements(*p.Password); err != nil {
|
|
|
|
|
invalid.Append("password", err.Error())
|
|
|
|
|
}
|
2017-01-15 23:23:09 +00:00
|
|
|
}
|
2016-09-01 04:51:38 +00:00
|
|
|
}
|
2016-12-15 17:28:53 +00:00
|
|
|
|
2016-09-01 04:51:38 +00:00
|
|
|
if p.Email == nil {
|
2021-06-24 20:42:29 +00:00
|
|
|
invalid.Append("email", "Email missing required argument")
|
2016-12-15 17:28:53 +00:00
|
|
|
} else {
|
|
|
|
|
if *p.Email == "" {
|
2021-06-24 20:42:29 +00:00
|
|
|
invalid.Append("email", "Email cannot be empty")
|
2016-12-15 17:28:53 +00:00
|
|
|
}
|
2016-09-16 15:23:48 +00:00
|
|
|
}
|
2016-12-15 17:28:53 +00:00
|
|
|
|
2016-09-29 02:44:05 +00:00
|
|
|
if p.InviteToken == nil {
|
2021-06-24 20:42:29 +00:00
|
|
|
invalid.Append("invite_token", "Invite token missing required argument")
|
2016-12-15 17:28:53 +00:00
|
|
|
} else {
|
|
|
|
|
if *p.InviteToken == "" {
|
2021-06-24 20:42:29 +00:00
|
|
|
invalid.Append("invite_token", "Invite token cannot be empty")
|
2016-12-15 17:28:53 +00:00
|
|
|
}
|
2016-09-29 02:44:05 +00:00
|
|
|
}
|
2016-12-15 17:28:53 +00:00
|
|
|
|
2016-09-29 02:44:05 +00:00
|
|
|
if invalid.HasErrors() {
|
2021-11-22 14:13:26 +00:00
|
|
|
return nil, ctxerr.Wrap(ctx, invalid)
|
2016-09-01 04:51:38 +00:00
|
|
|
}
|
2021-06-03 23:24:15 +00:00
|
|
|
return mw.Service.CreateUserFromInvite(ctx, p)
|
2020-11-05 01:06:55 +00:00
|
|
|
}
|
|
|
|
|
|
2021-06-06 22:07:29 +00:00
|
|
|
func (mw validationMiddleware) CreateUser(ctx context.Context, p fleet.UserPayload) (*fleet.User, error) {
|
|
|
|
|
invalid := &fleet.InvalidArgumentError{}
|
2021-06-24 20:42:29 +00:00
|
|
|
if p.Name == nil {
|
|
|
|
|
invalid.Append("name", "Full name missing required argument")
|
2020-11-05 01:06:55 +00:00
|
|
|
} else {
|
2021-06-24 20:42:29 +00:00
|
|
|
if *p.Name == "" {
|
|
|
|
|
invalid.Append("name", "Full name cannot be empty")
|
2020-11-05 01:06:55 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// we don't need a password for single sign on
|
|
|
|
|
if (p.SSOInvite == nil || !*p.SSOInvite) && (p.SSOEnabled == nil || !*p.SSOEnabled) {
|
|
|
|
|
if p.Password == nil {
|
2021-06-24 20:42:29 +00:00
|
|
|
invalid.Append("password", "Password missing required argument")
|
2020-11-05 01:06:55 +00:00
|
|
|
} else {
|
|
|
|
|
if *p.Password == "" {
|
2021-06-24 20:42:29 +00:00
|
|
|
invalid.Append("password", "Password cannot be empty")
|
2020-11-05 01:06:55 +00:00
|
|
|
}
|
|
|
|
|
// Skip password validation in the case of admin created users
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-02 15:35:26 +00:00
|
|
|
if p.SSOEnabled != nil && *p.SSOEnabled && p.Password != nil && len(*p.Password) > 0 {
|
|
|
|
|
invalid.Append("password", "not allowed for SSO users")
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-05 01:06:55 +00:00
|
|
|
if p.Email == nil {
|
2021-06-24 20:42:29 +00:00
|
|
|
invalid.Append("email", "Email missing required argument")
|
2020-11-05 01:06:55 +00:00
|
|
|
} else {
|
|
|
|
|
if *p.Email == "" {
|
2021-06-24 20:42:29 +00:00
|
|
|
invalid.Append("email", "Email cannot be empty")
|
2020-11-05 01:06:55 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if p.InviteToken != nil {
|
2021-06-24 20:42:29 +00:00
|
|
|
invalid.Append("invite_token", "Invite token should not be specified with admin user creation")
|
2020-11-05 01:06:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if invalid.HasErrors() {
|
2021-11-22 14:13:26 +00:00
|
|
|
return nil, ctxerr.Wrap(ctx, invalid)
|
2020-11-05 01:06:55 +00:00
|
|
|
}
|
|
|
|
|
return mw.Service.CreateUser(ctx, p)
|
2016-09-01 04:51:38 +00:00
|
|
|
}
|
|
|
|
|
|
2021-06-06 22:07:29 +00:00
|
|
|
func (mw validationMiddleware) ModifyUser(ctx context.Context, userID uint, p fleet.UserPayload) (*fleet.User, error) {
|
|
|
|
|
invalid := &fleet.InvalidArgumentError{}
|
2016-12-15 17:28:53 +00:00
|
|
|
if p.Name != nil {
|
|
|
|
|
if *p.Name == "" {
|
2021-06-24 20:42:29 +00:00
|
|
|
invalid.Append("name", "Full name cannot be empty")
|
2016-12-15 17:28:53 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if p.Email != nil {
|
|
|
|
|
if *p.Email == "" {
|
2021-06-24 20:42:29 +00:00
|
|
|
invalid.Append("email", "Email cannot be empty")
|
2016-12-15 17:28:53 +00:00
|
|
|
}
|
2017-01-27 13:35:58 +00:00
|
|
|
// if the user is not an admin, or if an admin is changing their own email
|
|
|
|
|
// address a password is required,
|
|
|
|
|
if passwordRequiredForEmailChange(ctx, userID, invalid) {
|
|
|
|
|
if p.Password == nil {
|
2021-06-24 20:42:29 +00:00
|
|
|
invalid.Append("password", "Password cannot be empty if email is changed")
|
2017-01-27 13:35:58 +00:00
|
|
|
}
|
|
|
|
|
}
|
2016-12-15 17:28:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if invalid.HasErrors() {
|
2021-11-22 14:13:26 +00:00
|
|
|
return nil, ctxerr.Wrap(ctx, invalid)
|
2016-12-15 17:28:53 +00:00
|
|
|
}
|
|
|
|
|
return mw.Service.ModifyUser(ctx, userID, p)
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-06 22:07:29 +00:00
|
|
|
func passwordRequiredForEmailChange(ctx context.Context, uid uint, invalid *fleet.InvalidArgumentError) bool {
|
2017-01-27 13:35:58 +00:00
|
|
|
vc, ok := viewer.FromContext(ctx)
|
|
|
|
|
if !ok {
|
2021-06-24 20:42:29 +00:00
|
|
|
invalid.Append("viewer", "Viewer not present")
|
2017-01-27 13:35:58 +00:00
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
// if a user is changing own email need a password no matter what
|
2021-06-16 17:55:41 +00:00
|
|
|
return vc.UserID() == uid
|
2017-01-27 13:35:58 +00:00
|
|
|
}
|
|
|
|
|
|
2016-12-14 18:11:43 +00:00
|
|
|
func (mw validationMiddleware) ChangePassword(ctx context.Context, oldPass, newPass string) error {
|
2021-06-06 22:07:29 +00:00
|
|
|
invalid := &fleet.InvalidArgumentError{}
|
2016-12-14 18:11:43 +00:00
|
|
|
if oldPass == "" {
|
2021-06-24 20:42:29 +00:00
|
|
|
invalid.Append("old_password", "Old password cannot be empty")
|
2016-12-14 18:11:43 +00:00
|
|
|
}
|
|
|
|
|
if newPass == "" {
|
2021-06-24 20:42:29 +00:00
|
|
|
invalid.Append("new_password", "New password cannot be empty")
|
2016-12-14 18:11:43 +00:00
|
|
|
}
|
2017-01-15 23:23:09 +00:00
|
|
|
|
|
|
|
|
if err := validatePasswordRequirements(newPass); err != nil {
|
|
|
|
|
invalid.Append("new_password", err.Error())
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-14 18:11:43 +00:00
|
|
|
if invalid.HasErrors() {
|
2021-11-22 14:13:26 +00:00
|
|
|
return ctxerr.Wrap(ctx, invalid)
|
2016-12-14 18:11:43 +00:00
|
|
|
}
|
|
|
|
|
return mw.Service.ChangePassword(ctx, oldPass, newPass)
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-15 14:52:17 +00:00
|
|
|
func (mw validationMiddleware) ResetPassword(ctx context.Context, token, password string) error {
|
2021-06-06 22:07:29 +00:00
|
|
|
invalid := &fleet.InvalidArgumentError{}
|
2016-09-15 14:52:17 +00:00
|
|
|
if token == "" {
|
2021-06-24 20:42:29 +00:00
|
|
|
invalid.Append("token", "Token cannot be empty field")
|
2016-09-01 04:51:38 +00:00
|
|
|
}
|
2016-09-15 14:52:17 +00:00
|
|
|
if password == "" {
|
2021-06-24 20:42:29 +00:00
|
|
|
invalid.Append("new_password", "New password cannot be empty field")
|
2016-09-16 15:23:48 +00:00
|
|
|
}
|
2017-01-15 23:23:09 +00:00
|
|
|
if err := validatePasswordRequirements(password); err != nil {
|
|
|
|
|
invalid.Append("new_password", err.Error())
|
|
|
|
|
}
|
2016-09-29 02:44:05 +00:00
|
|
|
if invalid.HasErrors() {
|
2021-11-22 14:13:26 +00:00
|
|
|
return ctxerr.Wrap(ctx, invalid)
|
2016-09-01 04:51:38 +00:00
|
|
|
}
|
2016-09-15 14:52:17 +00:00
|
|
|
return mw.Service.ResetPassword(ctx, token, password)
|
2016-09-01 04:51:38 +00:00
|
|
|
}
|
2016-09-16 15:23:48 +00:00
|
|
|
|
2017-01-15 23:23:09 +00:00
|
|
|
// Requirements for user password:
|
|
|
|
|
// at least 7 character length
|
|
|
|
|
// at least 1 symbol
|
|
|
|
|
// at least 1 number
|
|
|
|
|
func validatePasswordRequirements(password string) error {
|
|
|
|
|
var (
|
|
|
|
|
number bool
|
|
|
|
|
symbol bool
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
for _, s := range password {
|
|
|
|
|
switch {
|
|
|
|
|
case unicode.IsNumber(s):
|
|
|
|
|
number = true
|
|
|
|
|
case unicode.IsPunct(s) || unicode.IsSymbol(s):
|
|
|
|
|
symbol = true
|
|
|
|
|
}
|
2016-09-23 02:41:58 +00:00
|
|
|
}
|
2016-09-16 15:23:48 +00:00
|
|
|
|
2017-01-15 23:23:09 +00:00
|
|
|
if len(password) >= 7 &&
|
|
|
|
|
number &&
|
|
|
|
|
symbol {
|
|
|
|
|
return nil
|
2016-09-16 15:23:48 +00:00
|
|
|
}
|
2017-01-15 23:23:09 +00:00
|
|
|
|
2021-06-24 20:42:29 +00:00
|
|
|
return errors.New("Password does not meet validation requirements")
|
2016-09-16 15:23:48 +00:00
|
|
|
}
|