login fix

This commit is contained in:
2026-09-25 10:36:38 +05:30
parent db84a9a752
commit 276e12beb9
7 changed files with 366 additions and 0 deletions

View File

@@ -14,6 +14,7 @@ import (
type UserRepository interface {
GetAllUsers(roleID, tenantID, pageno, pagesize int, keyword string) ([]models.UserInfo, error)
GetUserByID(uid int) (models.UserInfo, error)
SetInitialPassword(userid int, password string) error
Login(user models.User) (models.UserInfo, error)
FindUserID(authname, contactno string, configid int) (int, error)
UpdateStaff(user models.User) error
@@ -391,3 +392,46 @@ func (r *userRepository) GetLocationStatus(locationid int) string {
func (r *userRepository) DeleteUser(userid int) error {
return r.db.Table("app_users").Where("userid = ?", userid).Delete(&models.User{}).Error
}
// SetInitialPassword writes the first password on an account that has none.
//
// ── Why this is a separate call and not `UpdateStaff` ───────────────────────
//
// It is the one write that MUST work without a session, and that is the whole
// difficulty. A brand-new account — `createtenantlocation` spawns branch logins
// with an empty password — signs in, is told to set one, and at that moment has
// no token and no way to get one. The console was doing this through
// `PUT /users/update`, which sits behind the session guard, so the call came
// back "a session token is required; sign in again" and the account could never
// be used. Sign-in needs a password; setting the password needed a sign-in.
//
// `/users/update` could not simply be opened up: it writes whatever struct it
// is handed, so an unauthenticated caller could edit any field of any user.
// This can do exactly one thing, to exactly one kind of account.
//
// ── What makes it safe to expose ────────────────────────────────────────────
//
// The empty-password check IS the authorisation. An account with a password set
// is refused, so this can never overwrite a credential — it is a setup call,
// never a reset. There is no "forgot password" flow on this backend and this
// must not become one by accident: a reset needs proof of identity, and nothing
// here has any.
//
// The check and the write are one statement, so two callers racing cannot both
// see an empty password and both set one. Postgres decides, not this process.
func (r *userRepository) SetInitialPassword(userid int, password string) error {
result := r.db.Table("app_users").
Where("userid = ? AND (password IS NULL OR TRIM(password) = '')", userid).
Update("password", password)
if result.Error != nil {
return result.Error
}
if result.RowsAffected == 0 {
// One message for "no such user" and "already has a password". They
// must not be distinguishable, or this becomes a way to ask whether a
// given userid exists and whether it has been set up.
return errors.New("that account cannot have its password set here — it may already have one")
}
return nil
}