tTransfer ranking request as database logic - scoreboard - Interactive scoreboard for CTF-like games
(HTM) git clone git://git.z3bra.org/scoreboard.git
(DIR) Log
(DIR) Files
(DIR) Refs
---
(DIR) commit e64e15888e10934f5d3ee1c75facb402562a0549
(DIR) parent 61269ee4372b67a9d212fc9227c2c46acbb58f39
(HTM) Author: Willy Goiffon <contact@z3bra.org>
Date: Tue, 1 Oct 2024 00:40:02 +0200
Transfer ranking request as database logic
Diffstat:
M db.go | 56 ++++++++++++++++++++++---------
M main.go | 15 ++++++++-------
M player.go | 71 +++++++++++++++++--------------
M playerbox.go | 2 +-
M ui.go | 2 +-
5 files changed, 88 insertions(+), 58 deletions(-)
---
(DIR) diff --git a/db.go b/db.go
t@@ -42,7 +42,6 @@ const (
`
)
-
func db_init(file string) (*sql.DB, error) {
var err error
var db *sql.DB
t@@ -72,21 +71,6 @@ func db_count_players(db *sql.DB) (int, error) {
return count, err
}
-func db_get_user_score(db *sql.DB, name string) (int, error) {
- var count int
-
- query := `SELECT
- IFNULL(SUM(flag.score),0)
- FROM flag
- INNER JOIN score ON score.flag = flag.value
- WHERE score.name = ?;`
-
- row := db.QueryRow(query, name)
- err := row.Scan(&count)
-
- return count, err
-}
-
func db_get_user_flags(db *sql.DB, name string) ([]Flag, error) {
var flags []Flag
t@@ -113,6 +97,46 @@ func db_get_user_flags(db *sql.DB, name string) ([]Flag, error) {
return flags, err
}
+func db_get_user_score(db *sql.DB, name string) (int, error) {
+ var count int
+
+ query := `SELECT
+ IFNULL(SUM(flag.score),0)
+ FROM flag
+ INNER JOIN score ON score.flag = flag.value
+ WHERE score.name = ?;`
+
+ row := db.QueryRow(query, name)
+ err := row.Scan(&count)
+
+ return count, err
+}
+
+func db_get_user_rank(db *sql.DB, name string) (int, error) {
+ var rank int
+
+ score, err := db_get_user_score(db, name)
+ if err != nil {
+ return -1, err
+ }
+
+ query := `SELECT COUNT(*) FROM (
+ SELECT
+ user.name, user.ts as ts, SUM(flag.score) as score
+ FROM score
+ INNER JOIN user ON user.name = score.name
+ INNER JOIN flag ON flag.value = score.flag
+ WHERE user.name != ?
+ GROUP BY user.name
+ ) WHERE score > ? OR (score == ? AND ts < (SELECT ts FROM user WHERE name = ?))
+ ;`
+
+ row := db.QueryRow(query, name, score, score)
+ row.Scan(&rank)
+
+ return rank, err
+}
+
func db_get_flag(db *sql.DB, flag string) (Flag, error) {
var res Flag
(DIR) diff --git a/main.go b/main.go
t@@ -15,6 +15,7 @@
package main
import (
+ "errors"
"flag"
"fmt"
"os"
t@@ -68,13 +69,13 @@ func usage() {
}
-func flagid(hash string) int {
- for i := 0; i<len(scoreboard.flag_ref); i++ {
- if strings.ToUpper(hash) == scoreboard.flag_ref[i].value {
- return scoreboard.flag_ref[i].id
+func checkflag(ref []Flag, flag string) (Flag, error) {
+ for _, f := range ref {
+ if strings.ToUpper(flag) == f.value {
+ return f, nil
}
}
- return -1
+ return Flag{}, errors.New("Unknown flag")
}
func pageBoard() tview.Primitive {
t@@ -162,7 +163,7 @@ func main() {
/* anything not a command is treated as a flag */
default:
- scoreboard.flag, err = db_get_flag(scoreboard.db, args[0])
+ scoreboard.flag, err = checkflag(scoreboard.flag_ref, args[0])
if err != nil {
fmt.Println("Incorrect flag")
return
t@@ -174,7 +175,7 @@ func main() {
scoreboard.Fatal(err)
return
}
- scoreboard.HighlightBoard(scoreboard.player.ScoreRank() + 1)
+ scoreboard.HighlightBoard(scoreboard.player.Rank() + 1)
scoreboard.pages.RemovePage("token")
scoreboard.GenerateHTML()
})
(DIR) diff --git a/player.go b/player.go
t@@ -49,14 +49,13 @@ func randbuf(length int64) []byte {
}
/* Generate a random base32 token using the provided input as a salt */
-func mktoken(input string) (string, string, error) {
- key := randbuf(12)
- salt := base32.StdEncoding.EncodeToString([]byte(input))
+func mktoken(key []byte, salt string) (string, string, error) {
+ salt32 := base32.StdEncoding.EncodeToString([]byte(salt))
token := key
- token = append(token, []byte(input)...)
+ token = append(token, []byte(salt)...)
- dk, err := scrypt.Key(key, []byte(salt), 1<<15, 8, 1, 32)
+ dk, err := scrypt.Key(key, []byte(salt32), 1<<15, 8, 1, 32)
if err != nil {
return "", "", err
}
t@@ -66,18 +65,40 @@ func mktoken(input string) (string, string, error) {
return token32, hash32, nil
}
+func hashtoken(token string) (string, error) {
+ var err error
+
+ blob, err := base32.StdEncoding.DecodeString(token)
+ if err != nil {
+ return "", err
+ }
+
+ key := blob[:12]
+ name := string(blob[12:])
+
+ salt := base32.StdEncoding.EncodeToString([]byte(name))
+
+ dk, err := scrypt.Key(key, []byte(salt), 1<<15, 8, 1, 32)
+ if err != nil {
+ return "", err
+ }
+ hash := base32.StdEncoding.EncodeToString(dk)
+
+ return hash, nil
+}
+
/* Register a user in the database */
func (p *Player) Register() error {
var hash string
var err error
p.ts = time.Now().Unix()
- p.token, hash, err = mktoken(p.name)
+ p.token, hash, err = mktoken(randbuf(12), p.name)
if err != nil {
return err
}
- query := `INSERT INTO user(name,hash,score,flag,ts) VALUES(?,?,0,0,?);`
+ query := `INSERT INTO user(name,hash,ts) VALUES(?,?,?);`
_, err = p.db.Exec(query, p.name, hash, p.ts)
if err != nil {
return err
t@@ -121,7 +142,7 @@ func (p *Player) Refresh(ts int64) error {
return err
}
- query := `UPDATE user ts = ? WHERE name = ?;`
+ query := `UPDATE user SET ts = ? WHERE name = ?;`
_, err = p.db.Exec(query, ts, p.name)
if err != nil {
return err
t@@ -132,18 +153,13 @@ func (p *Player) Refresh(ts int64) error {
return nil
}
-func (p *Player) ScoreRank() int {
- var count int
- query := `SELECT
- count(*)
- FROM user
- WHERE
- name != ? AND (score > ? OR (score == ? AND ts <= ?))
- ;`
+func (p *Player) Rank() int {
+ rank, err := db_get_user_rank(p.db, p.name)
+ if err != nil {
+ return -1
+ }
- row := p.db.QueryRow(query, p.name, p.score, p.score, p.ts)
- row.Scan(&count)
- return count
+ return rank
}
func (p *Player) FlagStr() string {
t@@ -221,7 +237,7 @@ func (p *Player) BadgeStr() string {
}
func (p *Player) RankStr() string {
- return humanize.Ordinal(p.ScoreRank() + 1)
+ return humanize.Ordinal(p.Rank() + 1)
}
func (p *Player) Exists() bool {
t@@ -279,23 +295,12 @@ func (p *Player) Submit(flag Flag) error {
/* Retrieve username from given token */
func (p *Player) FromToken(token string) error {
var err error
- blob, err := base32.StdEncoding.DecodeString(token)
+ hash, err := hashtoken(token)
if err != nil {
return err
}
- key := blob[:12]
- p.name = string(blob[12:])
-
- salt := base32.StdEncoding.EncodeToString([]byte(p.name))
-
- // use player id as salt
- dk, err := scrypt.Key(key, []byte(salt), 1<<15, 8, 1, 32)
- if err != nil {
- return err
- }
- hash := base32.StdEncoding.EncodeToString(dk)
- query := `SELECT name,flag,score,ts FROM user WHERE hash = ?`
+ query := `SELECT name,ts FROM user WHERE hash = ?`
row := p.db.QueryRow(query, hash)
err = row.Scan(&p.name, &p.ts)
(DIR) diff --git a/playerbox.go b/playerbox.go
t@@ -90,7 +90,7 @@ func PlayerBoxName(p *Player) *tview.TextView {
if err != nil {
scoreboard.Fatal(err)
}
- scoreboard.HighlightBoard(p.ScoreRank())
+ scoreboard.HighlightBoard(p.Rank())
scoreboard.GenerateHTML()
scoreboard.Popup("CONGRATULATIONS", fmt.Sprintf(TOKEN_WELCOME, p.name, p.token));
} else {
(DIR) diff --git a/ui.go b/ui.go
t@@ -135,7 +135,7 @@ func (a *Application) DrawBoard() {
if event.Rune() == 'l' && a.player.token == "" {
page := a.Token(func () {
a.pages.RemovePage("token")
- a.HighlightBoard(a.player.ScoreRank() + 1)
+ a.HighlightBoard(a.player.Rank() + 1)
})
a.pages.AddAndSwitchToPage("token", page, true)
}