admin: Handle team password

This commit is contained in:
nemunaire 2021-09-09 11:20:45 +02:00
commit 5eeb1a6297
11 changed files with 299 additions and 40 deletions

View file

@ -99,7 +99,8 @@ CREATE TABLE IF NOT EXISTS teams(
name VARCHAR(255) NOT NULL,
color INTEGER NOT NULL,
active BOOLEAN NOT NULL DEFAULT 1,
external_id VARCHAR(255) NOT NULL
external_id VARCHAR(255) NOT NULL,
password VARCHAR(255) NULL
) DEFAULT CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
`); err != nil {
return err

31
libfic/password.go Normal file
View file

@ -0,0 +1,31 @@
package fic
import (
"crypto/rand"
"encoding/base64"
"strings"
)
func GeneratePassword() (password string, err error) {
// This will make a 12 chars long password
b := make([]byte, 9)
if _, err = rand.Read(b); err != nil {
return
}
password = base64.StdEncoding.EncodeToString(b)
// Avoid hard to read characters
for _, i := range [][2]string{
{"v", "*"}, {"u", "("},
{"l", "%"}, {"1", "?"},
{"o", "@"}, {"O", "!"}, {"0", ">"},
// This one is to avoid problem with openssl
{"/", "^"},
} {
password = strings.Replace(password, i[0], i[1], -1)
}
return
}

View file

@ -4,6 +4,8 @@ import (
"log"
"math"
"time"
"golang.org/x/crypto/bcrypt"
)
// UnlockedChallengeDepth is the number of challenges to unlock ahead (0: only the next one, -1: all)
@ -14,15 +16,16 @@ var WChoiceCoefficient = 1.0
// Team represents a group of players, come to solve our challenges.
type Team struct {
Id int64 `json:"id"`
Name string `json:"name"`
Color uint32 `json:"color"`
Active bool `json:"active"`
ExternalId string `json:"external_id"`
Id int64 `json:"id"`
Name string `json:"name"`
Color uint32 `json:"color"`
Active bool `json:"active"`
ExternalId string `json:"external_id"`
Password *string `json:"password"`
}
func getTeams(filter string) ([]Team, error) {
if rows, err := DBQuery("SELECT id_team, name, color, active, external_id FROM teams " + filter); err != nil {
if rows, err := DBQuery("SELECT id_team, name, color, active, external_id, password FROM teams " + filter); err != nil {
return nil, err
} else {
defer rows.Close()
@ -30,7 +33,7 @@ func getTeams(filter string) ([]Team, error) {
var teams = make([]Team, 0)
for rows.Next() {
var t Team
if err := rows.Scan(&t.Id, &t.Name, &t.Color, &t.Active, &t.ExternalId); err != nil {
if err := rows.Scan(&t.Id, &t.Name, &t.Color, &t.Active, &t.ExternalId, &t.Password); err != nil {
return nil, err
}
teams = append(teams, t)
@ -56,7 +59,7 @@ func GetActiveTeams() ([]Team, error) {
// GetTeam retrieves a Team from its identifier.
func GetTeam(id int64) (Team, error) {
var t Team
if err := DBQueryRow("SELECT id_team, name, color, active, external_id FROM teams WHERE id_team = ?", id).Scan(&t.Id, &t.Name, &t.Color, &t.Active, &t.ExternalId); err != nil {
if err := DBQueryRow("SELECT id_team, name, color, active, external_id, password FROM teams WHERE id_team = ?", id).Scan(&t.Id, &t.Name, &t.Color, &t.Active, &t.ExternalId, &t.Password); err != nil {
return t, err
}
@ -66,7 +69,7 @@ func GetTeam(id int64) (Team, error) {
// GetTeamBySerial retrieves a Team from one of its associated certificates.
func GetTeamBySerial(serial int64) (Team, error) {
var t Team
if err := DBQueryRow("SELECT T.id_team, T.name, T.color, T.active, T.external_id FROM certificates C INNER JOIN teams T ON T.id_team = C.id_team WHERE id_cert = ?", serial).Scan(&t.Id, &t.Name, &t.Color, &t.Active, &t.ExternalId); err != nil {
if err := DBQueryRow("SELECT T.id_team, T.name, T.color, T.active, T.external_id, T.password FROM certificates C INNER JOIN teams T ON T.id_team = C.id_team WHERE id_cert = ?", serial).Scan(&t.Id, &t.Name, &t.Color, &t.Active, &t.ExternalId, &t.Password); err != nil {
return t, err
}
@ -80,13 +83,13 @@ func CreateTeam(name string, color uint32, externalId string) (Team, error) {
} else if tid, err := res.LastInsertId(); err != nil {
return Team{}, err
} else {
return Team{tid, name, color, true, ""}, nil
return Team{tid, name, color, true, "", nil}, nil
}
}
// Update applies modifications back to the database.
func (t Team) Update() (int64, error) {
if res, err := DBExec("UPDATE teams SET name = ?, color = ?, active = ?, external_id = ? WHERE id_team = ?", t.Name, t.Color, t.Active, t.ExternalId, t.Id); err != nil {
if res, err := DBExec("UPDATE teams SET name = ?, color = ?, active = ?, external_id = ?, password = ? WHERE id_team = ?", t.Name, t.Color, t.Active, t.ExternalId, t.Password, t.Id); err != nil {
return 0, err
} else if nb, err := res.RowsAffected(); err != nil {
return 0, err
@ -310,3 +313,18 @@ func (t Team) HasPartiallySolved(f Flag) (tm *time.Time) {
}
return
}
// HashedPassword compute a bcrypt version of the team's password.
func (t Team) HashedPassword() (string, error) {
if t.Password == nil {
if passwd, err := GeneratePassword(); err != nil {
return "", err
} else {
h, err := bcrypt.GenerateFromPassword([]byte(passwd), bcrypt.DefaultCost)
return string(h), err
}
}
h, err := bcrypt.GenerateFromPassword([]byte(*t.Password), bcrypt.DefaultCost)
return string(h), err
}