package handlers import ( "errors" "strings" "time" "git.coopgo.io/coopgo-platform/mobility-accounts/storage" "github.com/google/uuid" "github.com/rs/zerolog/log" "github.com/santhosh-tekuri/jsonschema/v5" "golang.org/x/crypto/bcrypt" ) func (h MobilityAccountsHandler) Login(username string, password string, namespace string) (*storage.Account, error) { if password == "" { return nil, errors.New("empty password not allowed") } u := strings.ToLower(username) account, err := h.storage.DB.LocalAuthentication(namespace, &u, nil, nil) if err != nil { log.Error().Err(err).Msg("") return nil, err } if err = bcrypt.CompareHashAndPassword([]byte(account.Authentication.Local.Password), []byte(password)); err != nil { log.Error().Err(err).Msg("") return nil, err } return account, nil } func (h MobilityAccountsHandler) Register(account storage.Account) (*storage.Account, error) { if account.ID != "" { return nil, errors.New("id should be empty") } // Generate new UUID account.ID = uuid.NewString() if account.Authentication.Local != nil && account.Authentication.Local.Username != nil { username := strings.ToLower(*account.Authentication.Local.Username) account.Authentication.Local.Username = &username } if account.Authentication.Local != nil && account.Authentication.Local.Email != nil { email := strings.ToLower(*account.Authentication.Local.Email) account.Authentication.Local.Username = &email } //TODO remove this as we want to handle unicity in storage if account.Authentication.Local != nil { // If a password was sent, hash the password if account.Authentication.Local.Password != "" { hashedPassword, err := bcrypt.GenerateFromPassword([]byte(account.Authentication.Local.Password), bcrypt.DefaultCost) if err != nil { return nil, err } account.Authentication.Local.Password = string(hashedPassword) } _, _ = h.storage.DB.LocalAuthentication(account.Namespace, account.Authentication.Local.Username, account.Authentication.Local.Email, account.Authentication.Local.PhoneNumber) } // Validate data schemas dataschemas := h.config.GetStringMap("data_schemas") for k, v := range account.Data { if !h.config.GetBool("allow_any_data") && dataschemas[k] == nil { return nil, errors.New("data scope not allowed") } if dataschemas[k] != nil { s := dataschemas[k].(map[string]string) sch, err := jsonschema.Compile(s["schema"]) if err != nil { return nil, err } if err = sch.Validate(v); err != nil { return nil, err } } } // Set the "created" metadata if account.Metadata == nil { account.Metadata = map[string]any{} } account.Metadata["created"] = time.Now() // Store the account if err := h.storage.DB.CreateAccount(account); err != nil { return nil, err } return &account, nil } func (h MobilityAccountsHandler) UpdateData(accountid string, datas map[string]any) (*storage.Account, error) { account, err := h.storage.DB.GetAccount(accountid) if err != nil { return nil, err } // Validate data schemas and update account dataschemas := h.config.GetStringMap("data_schemas") for k, v := range datas { if !h.config.GetBool("allow_any_data") && dataschemas[k] == nil { log.Error().Msg("data scope not allowed") return nil, errors.New("data scope not allowed") } if dataschemas[k] != nil { s := dataschemas[k].(map[string]string) sch, err := jsonschema.Compile(s["schema"]) if err != nil { log.Error().Err(err).Msg("") return nil, err } if err = sch.Validate(v); err != nil { log.Error().Err(err).Msg("") return nil, err } } account.Data[k] = v } if err = h.storage.DB.UpdateAccount(*account); err != nil { log.Error().Err(err).Msg("") return nil, err } return account, nil } func (h MobilityAccountsHandler) UpdatePhoneNumber(accountid, phone_number string, verified bool, verification_code string) error { account, err := h.storage.DB.GetAccount(accountid) if err != nil { return err } account2, err := h.storage.DB.LocalAuthentication(account.Namespace, nil, nil, &phone_number) if err == nil && account.ID != account2.ID { return errors.New("user with this phone number already exists") } account.Authentication.Local.PhoneNumber = &phone_number account.Authentication.Local.PhoneNumberValidation.Validated = verified account.Authentication.Local.PhoneNumberValidation.ValidationCode = verification_code if err = h.storage.DB.UpdateAccount(*account); err != nil { log.Error().Err(err).Msg("") return err } return nil } func (h MobilityAccountsHandler) GetAccount(id string) (account *storage.Account, err error) { account, err = h.storage.DB.GetAccount(id) return } func (h MobilityAccountsHandler) GetAccountUsername(username string, namespace string) (account *storage.Account, err error) { account, err = h.storage.DB.LocalAuthentication(namespace, &username, nil, nil) return } func (h MobilityAccountsHandler) GetAccounts(namespaces []string) (accounts []storage.Account, err error) { accounts, err = h.storage.DB.GetAccounts(namespaces) return } func (h MobilityAccountsHandler) GetAccountsBatch(accountIds []string) (accounts []storage.Account, err error) { if len(accountIds) == 0 { return accounts, nil } accounts, err = h.storage.DB.GetAccountsByIds(accountIds) return } func (h MobilityAccountsHandler) ChangePassword(accountid string, newpassword string) error { account, err := h.storage.DB.GetAccount(accountid) if err != nil { return err } hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newpassword), bcrypt.DefaultCost) if err != nil { return err } account.Authentication.Local.Password = string(hashedPassword) if err = h.storage.DB.UpdateAccount(*account); err != nil { return err } return nil }