// Proves the till and the Nearle Daily application no longer share accounts. // // Read-only. Four claims, each checked against live rows rather than asserted: // // 1. a provisioned supervisor can open a closed terminal; // // 2. a back-office account cannot, however senior it is; // // 3. a till account cannot reach the Nearle Daily application; and // // 4. a till account is not listed as though it were an app user. // // go run ./scratch/posseparation package main import ( "fmt" "os" "strings" "nearle/models" "nearle/repositories" "github.com/joho/godotenv" "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/logger" ) var failures int func check(claim string, ok bool, detail string) { mark := "PASS" if !ok { mark = "FAIL" failures++ } fmt.Printf(" [%s] %s\n %s\n", mark, claim, detail) } func main() { _ = godotenv.Load() dsn := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable", os.Getenv("DB_HOST"), os.Getenv("DB_PORT"), os.Getenv("DB_USER"), os.Getenv("DB_PASSWORD"), os.Getenv("DB_NAME")) db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) if err != nil { fmt.Println(err) os.Exit(1) } pos := repositories.NewPosRepository(db) users := repositories.NewUserRepository(db) // A real provisioned supervisor, and its password, read back out. var sup struct { Userid int Authname, Password string Configid, Tenantid, Location int } db.Raw(`SELECT userid, COALESCE(authname,'') authname, COALESCE(password,'') password, COALESCE(configid,0) configid, COALESCE(tenantid,0) tenantid, COALESCE(locationid,0) location FROM app_users WHERE COALESCE(roleid,0) = ? AND COALESCE(authname,'') <> '' ORDER BY userid LIMIT 1`, models.PosRoleSupervisor).Scan(&sup) if sup.Userid == 0 { fmt.Println("no provisioned supervisor to test with") os.Exit(1) } fmt.Printf("supervisor under test: %d %s (tenant %d, outlet %d)\n\n", sup.Userid, sup.Authname, sup.Tenantid, sup.Location) fmt.Println("1. a provisioned supervisor opens a closed terminal") session, err := pos.PosLogin(models.PosLoginRequest{ Authname: sup.Authname, Password: sup.Password, }) check("supervisor signs in at the till", err == nil && session != nil, fmt.Sprintf("err=%v", err)) if session != nil { check("and gets the supervisor shell", session.Canmanagestaff && session.Roleid == models.PosRoleSupervisor, fmt.Sprintf("role=%s can_manage_staff=%v", session.Role, session.Canmanagestaff)) } fmt.Println("\n2. back-office accounts cannot open a terminal at all") var backOffice []struct { Userid int Authname, Password string Roleid int } db.Raw(`SELECT userid, COALESCE(authname,'') authname, COALESCE(password,'') password, COALESCE(roleid,0) roleid FROM app_users WHERE COALESCE(roleid,0) IN (1,2,3,4,5,6) AND COALESCE(authname,'') <> '' AND COALESCE(password,'') <> '' AND LOWER(COALESCE(status,'active')) <> 'inactive' ORDER BY userid LIMIT 5`).Scan(&backOffice) for _, b := range backOffice { _, err := pos.PosLogin(models.PosLoginRequest{ Authname: b.Authname, Password: b.Password, }) check(fmt.Sprintf("roleid %d (%s) refused at the till", b.Roleid, b.Authname), err != nil && strings.Contains(err.Error(), "not set up for the till"), fmt.Sprintf("err=%v", err)) } fmt.Println("\n3. a till account cannot reach the Nearle Daily application") uid, _, _ := users.GetUserByAuthname(sup.Authname, sup.Configid) check("applogin lookup does not find the supervisor", uid == 0, fmt.Sprintf("GetUserByAuthname(%s) -> userid %d", sup.Authname, uid)) uid2, _, _, _ := users.GetUserLogin("authname", sup.Authname, sup.Configid) check("tenant web login does not find the supervisor", uid2 == 0, fmt.Sprintf("GetUserLogin(%s) -> userid %d", sup.Authname, uid2)) uid3, _ := users.FindUserID(sup.Authname, "", sup.Configid) check("password-setup lookup does not find the supervisor", uid3 == 0, fmt.Sprintf("FindUserID(%s) -> userid %d", sup.Authname, uid3)) fmt.Println("\n4. till accounts are not listed as app users") list, err := users.GetAllUsers(0, sup.Tenantid, 1, 500, "") leaked := 0 for _, u := range list { if u.Roleid == models.PosRoleSupervisor || u.Roleid == models.PosRoleCashier { leaked++ } } check("getallusers hides till accounts", err == nil && leaked == 0, fmt.Sprintf("%d of %d rows were till accounts", leaked, len(list))) // ...but the POS console can still read its own people by asking for them. sups, err := users.GetAllUsers(models.PosRoleSupervisor, sup.Tenantid, 1, 500, "") check("asking for role 7 explicitly still works", err == nil && len(sups) > 0, fmt.Sprintf("%d supervisor(s) returned", len(sups))) fmt.Println() if failures > 0 { fmt.Printf("%d CHECK(S) FAILED\n", failures) os.Exit(1) } fmt.Println("all checks passed") }