From ab74e70ba50aa31d9a063f5bd7a9d2f6ed562fd2 Mon Sep 17 00:00:00 2001 From: abhishek Date: Tue, 1 Sep 2026 13:17:52 +0530 Subject: [PATCH] login bug --- services/newAccount.go | 46 ++++++++++++++++++++++++++ services/newAccount_test.go | 64 +++++++++++++++++++++++++++++++++++++ services/tenantService.go | 5 ++- services/userService.go | 5 --- 4 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 services/newAccount.go create mode 100644 services/newAccount_test.go diff --git a/services/newAccount.go b/services/newAccount.go new file mode 100644 index 0000000..1a453b2 --- /dev/null +++ b/services/newAccount.go @@ -0,0 +1,46 @@ +package services + +import ( + "strings" + + "nearle/models" +) + +// The console's default configuration id. +// +// `weblogin` filters on it — `WHERE authname = ? AND configid = ?` — so an +// account carrying 0 is invisible to the sign-in query however correct +// everything else about it is. +const ConsoleConfigID = 1 + +// PrepareNewAccount fills in the two fields without which an account cannot +// sign in. +// +// Both were left to whatever the caller sent, and the caller is a form that has +// no reason to know about either. The result was an account that is created +// successfully, appears in every list, has the right name, email, role and +// branch — and is refused at the login screen with "we do not recognise that +// email", because: +// +// - **authname** is what `GetUserLogin` matches on. An account with an email +// and no authname is unreachable: nothing on the sign-in path ever looks at +// the email column. +// - **configid** is ANDed into the same query. Zero matches no console +// account, and zero is what an omitted field arrives as. +// +// Observed 2026-09-01: a merchant added their own administrator through the +// console, got a green confirmation, and could not sign in as them. +// +// Neither value is ever overwritten. A caller that supplies its own authname — +// somebody whose sign-in name is not their email — keeps it. +func PrepareNewAccount(user models.User) models.User { + if strings.TrimSpace(user.Authname) == "" { + // The email IS the sign-in name everywhere in this console; the form + // even labels it "This is how they sign in". + user.Authname = strings.TrimSpace(user.Email) + } + if user.Configid == 0 { + user.Configid = ConsoleConfigID + } + return user +} diff --git a/services/newAccount_test.go b/services/newAccount_test.go new file mode 100644 index 0000000..731ac14 --- /dev/null +++ b/services/newAccount_test.go @@ -0,0 +1,64 @@ +package services + +import ( + "testing" + + "nearle/models" +) + +/* +An account that is created perfectly and cannot sign in. + +`weblogin` matches `WHERE authname = ? AND configid = ?`. A form that sends an +email and a name — which is every form in this console — produces a row with an +empty authname and a configid of 0, so the sign-in query matches nothing and +answers "we do not recognise that email". + +Observed on 1 Sep 2026: a merchant added their own administrator through the +console, saw it succeed, saw it listed, and was refused at the login screen. +*/ + +func TestANewAccountCanActuallySignIn(t *testing.T) { + ready := PrepareNewAccount(models.User{Email: "thiruomart@gmail.com"}) + + if ready.Authname != "thiruomart@gmail.com" { + t.Errorf("authname was not derived from the email: %q", ready.Authname) + } + if ready.Configid != ConsoleConfigID { + t.Errorf("configid = %d, want %d — 0 matches no console account", ready.Configid, ConsoleConfigID) + } +} + +// A caller with its own sign-in name keeps it. Deriving from the email is a +// fallback for forms that do not ask, not a rule that overwrites an answer. +func TestAnExplicitSignInNameIsKept(t *testing.T) { + ready := PrepareNewAccount(models.User{Authname: "suriya.nsn", Email: "suriya@example.com"}) + if ready.Authname != "suriya.nsn" { + t.Errorf("an explicit authname was overwritten with %q", ready.Authname) + } +} + +func TestAnExplicitConfigIsKept(t *testing.T) { + ready := PrepareNewAccount(models.User{Email: "a@b.com", Configid: 4}) + if ready.Configid != 4 { + t.Errorf("configid = %d, want the caller's 4", ready.Configid) + } +} + +// Whitespace around a pasted email would become an authname nothing can match — +// the same invisible failure, one space wide. +func TestASurroundingSpaceDoesNotBecomePartOfTheSignInName(t *testing.T) { + ready := PrepareNewAccount(models.User{Email: " thiru@omart.com "}) + if ready.Authname != "thiru@omart.com" { + t.Errorf("authname = %q, want it trimmed", ready.Authname) + } +} + +// An account with no email at all cannot be given a sign-in name, and must not +// be given a blank one that looks like it has been handled. +func TestNoEmailLeavesTheSignInNameEmpty(t *testing.T) { + ready := PrepareNewAccount(models.User{}) + if ready.Authname != "" { + t.Errorf("authname = %q, want empty", ready.Authname) + } +} diff --git a/services/tenantService.go b/services/tenantService.go index c51ad6e..0f2bc04 100644 --- a/services/tenantService.go +++ b/services/tenantService.go @@ -105,7 +105,10 @@ func (s *tenantService) GetStaffs(tid int) ([]models.StaffInfo, error) { } func (s *tenantService) CreateStaff(user models.User) error { - return s.repo.CreateStaff(user) + // Same two fields, same reason: without an authname and a console configid + // the account is created, listed, and refused at the login screen. This path + // and users/create both make back-office accounts, so both need it. + return s.repo.CreateStaff(PrepareNewAccount(user)) } func (s *tenantService) UpdateStaff(user models.User) error { diff --git a/services/userService.go b/services/userService.go index f458120..36125c2 100644 --- a/services/userService.go +++ b/services/userService.go @@ -139,8 +139,6 @@ func (s *userService) AppLogin(user models.User) (models.TenantUserInfo, fiber.M _ = s.repo.UpdateFCMToken(uid, user.Userfcmtoken) } - - // ✅ Fetch tenant user info info := s.repo.GetTenantUserById(uid) @@ -184,7 +182,6 @@ func (s *userService) CreateUser(user models.User) (models.UserInfo, error) { return info, nil } - func (s *userService) TenantWebLogin(user models.User) (models.TenantUserInfo, map[string]interface{}) { tenantFormExists := true @@ -291,5 +288,3 @@ func (s *userService) TenantWebLogin(user models.User) (models.TenantUserInfo, m func (s *userService) DeleteUser(userid int) error { return s.repo.DeleteUser(userid) } - -