split checker from token-validator
This commit is contained in:
parent
685dc0b0ea
commit
0c661f36f6
20 changed files with 634 additions and 748 deletions
152
libadlin/db.go
Normal file
152
libadlin/db.go
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
package adlin
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
var db *sql.DB
|
||||
|
||||
func DSNGenerator() string {
|
||||
db_user := "adlin"
|
||||
db_password := "adlin"
|
||||
db_host := ""
|
||||
db_db := "adlin"
|
||||
|
||||
if v, exists := os.LookupEnv("MYSQL_HOST"); exists {
|
||||
db_host = v
|
||||
}
|
||||
if v, exists := os.LookupEnv("MYSQL_PASSWORD"); exists {
|
||||
db_password = v
|
||||
} else if v, exists := os.LookupEnv("MYSQL_ROOT_PASSWORD"); exists {
|
||||
db_user = "root"
|
||||
db_password = v
|
||||
}
|
||||
if v, exists := os.LookupEnv("MYSQL_USER"); exists {
|
||||
db_user = v
|
||||
}
|
||||
if v, exists := os.LookupEnv("MYSQL_DATABASE"); exists {
|
||||
db_db = v
|
||||
}
|
||||
|
||||
return db_user + ":" + db_password + "@" + db_host + "/" + db_db
|
||||
}
|
||||
|
||||
func DBInit(dsn string) (err error) {
|
||||
if db, err = sql.Open("mysql", dsn+"?parseTime=true&foreign_key_checks=1"); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_, err = db.Exec(`SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';`)
|
||||
for i := 0; err != nil && i < 5; i += 1 {
|
||||
if _, err = db.Exec(`SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';`); err != nil && i <= 5 {
|
||||
log.Println("An error occurs when trying to connect to DB, will retry in 2 seconds: ", err)
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func DBCreate() (err error) {
|
||||
if _, err = db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS students(
|
||||
id_student INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
login VARCHAR(255) NOT NULL UNIQUE,
|
||||
time TIMESTAMP NOT NULL
|
||||
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
|
||||
`); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS student_keys(
|
||||
id_key INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
id_student INTEGER NOT NULL,
|
||||
sshkey TEXT,
|
||||
time TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY(id_student) REFERENCES students(id_student)
|
||||
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
|
||||
`); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS student_login(
|
||||
id_student INTEGER NOT NULL,
|
||||
ip VARCHAR(255) NOT NULL,
|
||||
mac VARCHAR(255) NOT NULL,
|
||||
time TIMESTAMP NOT NULL,
|
||||
FOREIGN KEY(id_student) REFERENCES students(id_student)
|
||||
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS student_challenges(
|
||||
id_st INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
id_student INTEGER NOT NULL,
|
||||
challenge INTEGER NOT NULL,
|
||||
time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
value VARCHAR(255) NOT NULL,
|
||||
CONSTRAINT token_found UNIQUE (id_student,challenge),
|
||||
FOREIGN KEY(id_student) REFERENCES students(id_student)
|
||||
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS student_pong(
|
||||
id_student INTEGER NOT NULL,
|
||||
time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
state BOOLEAN NOT NULL DEFAULT 0,
|
||||
FOREIGN KEY(id_student) REFERENCES students(id_student)
|
||||
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS student_sessions(
|
||||
id_session BLOB(255) NOT NULL,
|
||||
id_student INTEGER,
|
||||
time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY(id_student) REFERENCES students(id_student)
|
||||
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := db.Exec(`
|
||||
CREATE TABLE IF NOT EXISTS student_tunnel_tokens(
|
||||
token BLOB(255) NOT NULL,
|
||||
token_text CHAR(10) NOT NULL,
|
||||
id_student INTEGER NOT NULL,
|
||||
time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
pubkey BLOB(50) DEFAULT NULL,
|
||||
FOREIGN KEY(id_student) REFERENCES students(id_student)
|
||||
) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin;
|
||||
`); err != nil {
|
||||
return err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func DBClose() error {
|
||||
return db.Close()
|
||||
}
|
||||
|
||||
func DBPrepare(query string) (*sql.Stmt, error) {
|
||||
return db.Prepare(query)
|
||||
}
|
||||
|
||||
func DBQuery(query string, args ...interface{}) (*sql.Rows, error) {
|
||||
return db.Query(query, args...)
|
||||
}
|
||||
|
||||
func DBExec(query string, args ...interface{}) (sql.Result, error) {
|
||||
return db.Exec(query, args...)
|
||||
}
|
||||
|
||||
func DBQueryRow(query string, args ...interface{}) *sql.Row {
|
||||
return db.QueryRow(query, args...)
|
||||
}
|
||||
27
libadlin/domain.go
Normal file
27
libadlin/domain.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package adlin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
AssociatedDomainSuffix = "adlin2021.p0m.fr."
|
||||
DelegatedDomainSuffix = "srs.p0m.fr."
|
||||
)
|
||||
|
||||
func (student Student) MyDelegatedDomain() string {
|
||||
return fmt.Sprintf("%s.%s", strings.Trim(strings.Replace(student.Login, "_", "-", -1), "-_"), DelegatedDomainSuffix)
|
||||
}
|
||||
|
||||
func (student Student) MyAssociatedDomain() string {
|
||||
return fmt.Sprintf("%s.%s", strings.Trim(strings.Replace(student.Login, "_", "-", -1), "-_"), AssociatedDomainSuffix)
|
||||
}
|
||||
|
||||
func (student Student) GetAssociatedDomains() (ds []string) {
|
||||
studentDomain := student.MyAssociatedDomain()
|
||||
|
||||
ds = append(ds, studentDomain)
|
||||
|
||||
return
|
||||
}
|
||||
36
libadlin/ping.go
Normal file
36
libadlin/ping.go
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package adlin
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Pong struct {
|
||||
Date time.Time
|
||||
State bool
|
||||
}
|
||||
|
||||
func (s Student) LastPongs() (pongs []Pong, err error) {
|
||||
if rows, errr := DBQuery("SELECT time, state FROM student_pong WHERE id_student = ? ORDER BY time DESC", s.Id); errr != nil {
|
||||
return nil, errr
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var p Pong
|
||||
if err = rows.Scan(&p.Date, &p.State); err != nil {
|
||||
return
|
||||
}
|
||||
pongs = append(pongs, p)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Student) OnPong(state bool) (err error) {
|
||||
_, err = DBExec("INSERT INTO student_pong (id_student, time, state) VALUES (?, ?, ?)", s.Id, time.Now(), state)
|
||||
return
|
||||
}
|
||||
3
libadlin/secret.go
Normal file
3
libadlin/secret.go
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
package adlin
|
||||
|
||||
var SharedSecret string
|
||||
75
libadlin/session.go
Normal file
75
libadlin/session.go
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
package adlin
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
Id []byte `json:"id"`
|
||||
IdStudent *int64 `json:"login"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
func GetSession(id []byte) (s Session, err error) {
|
||||
err = DBQueryRow("SELECT id_session, id_student, time FROM student_sessions WHERE id_session=?", id).Scan(&s.Id, &s.IdStudent, &s.Time)
|
||||
return
|
||||
}
|
||||
|
||||
func NewSession() (Session, error) {
|
||||
session_id := make([]byte, 255)
|
||||
if _, err := rand.Read(session_id); err != nil {
|
||||
return Session{}, err
|
||||
} else if _, err := DBExec("INSERT INTO student_sessions (id_session, time) VALUES (?, ?)", session_id, time.Now()); err != nil {
|
||||
return Session{}, err
|
||||
} else {
|
||||
return Session{session_id, nil, time.Now()}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (student Student) NewSession() (Session, error) {
|
||||
session_id := make([]byte, 255)
|
||||
if _, err := rand.Read(session_id); err != nil {
|
||||
return Session{}, err
|
||||
} else if _, err := DBExec("INSERT INTO student_sessions (id_session, id_student, time) VALUES (?, ?, ?)", session_id, student.Id, time.Now()); err != nil {
|
||||
return Session{}, err
|
||||
} else {
|
||||
return Session{session_id, &student.Id, time.Now()}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s Session) SetStudent(student Student) (Session, error) {
|
||||
s.IdStudent = &student.Id
|
||||
_, err := s.Update()
|
||||
return s, err
|
||||
}
|
||||
|
||||
func (s Session) Update() (int64, error) {
|
||||
if res, err := DBExec("UPDATE student_sessions SET id_student = ?, time = ? WHERE id_session = ?", s.IdStudent, s.Time, s.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
||||
|
||||
func (s Session) Delete() (int64, error) {
|
||||
if res, err := DBExec("DELETE FROM student_sessions WHERE id_session = ?", s.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
||||
|
||||
func ClearSession() (int64, error) {
|
||||
if res, err := DBExec("DELETE FROM student_sessions"); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
||||
135
libadlin/ssh.go
Normal file
135
libadlin/ssh.go
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
package adlin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type StudentKey struct {
|
||||
Id int64 `json:"id"`
|
||||
IdStudent int64 `json:"id_student"`
|
||||
Key string `json:"key"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
func GetStudentKeys() (keys []StudentKey, err error) {
|
||||
if rows, errr := DBQuery("SELECT id_key, id_student, sshkey, time FROM student_keys"); errr != nil {
|
||||
return nil, errr
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var k StudentKey
|
||||
if err = rows.Scan(&k.Id, &k.IdStudent, &k.Key, &k.Time); err != nil {
|
||||
return
|
||||
}
|
||||
keys = append(keys, k)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s Student) GetKeys() (keys []StudentKey, err error) {
|
||||
if rows, errr := DBQuery("SELECT id_key, id_student, sshkey, time FROM student_keys WHERE id_student = ?", s.Id); errr != nil {
|
||||
return nil, errr
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var k StudentKey
|
||||
if err = rows.Scan(&k.Id, &k.IdStudent, &k.Key, &k.Time); err != nil {
|
||||
return
|
||||
}
|
||||
keys = append(keys, k)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func getStudentKey(id int) (k StudentKey, err error) {
|
||||
err = DBQueryRow("SELECT id_key, id_student, sshkey, time FROM student_keys WHERE id_key=?", id).Scan(&k.Id, &k.IdStudent, &k.Key, &k.Time)
|
||||
return
|
||||
}
|
||||
|
||||
func (s Student) NewKey(key string) (k StudentKey, err error) {
|
||||
// Check key before importing it
|
||||
cmd := exec.Command("ssh-keygen", "-l", "-f", "-")
|
||||
cmd.Stdin = strings.NewReader(key)
|
||||
var stdoutStderr []byte
|
||||
stdoutStderr, err = cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if _, ok := err.(*exec.ExitError); ok {
|
||||
err = errors.New(string(stdoutStderr))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
chunks := bytes.Fields(stdoutStderr)
|
||||
|
||||
keytype := string(chunks[len(chunks)-1])
|
||||
minkeysize := 2048
|
||||
if keytype == "(ED25519)" || keytype == "(ECDSA)" {
|
||||
minkeysize = 256
|
||||
}
|
||||
|
||||
var bits int
|
||||
if bits, err = strconv.Atoi(string(chunks[0])); err != nil {
|
||||
return
|
||||
} else if bits < minkeysize {
|
||||
err = errors.New("Keysize too small")
|
||||
return
|
||||
}
|
||||
|
||||
// Sanitize the given key
|
||||
keyf := strings.Fields(key)
|
||||
if len(keyf) < 2 {
|
||||
err = errors.New("Unexpected key file, this should never happen")
|
||||
return
|
||||
}
|
||||
key = keyf[0] + " " + keyf[1]
|
||||
|
||||
if res, err := DBExec("INSERT INTO student_keys (id_student, sshkey, time) VALUES (?, ?, ?)", s.Id, key, time.Now()); err != nil {
|
||||
return StudentKey{}, err
|
||||
} else if kid, err := res.LastInsertId(); err != nil {
|
||||
return StudentKey{}, err
|
||||
} else {
|
||||
s.UnlockNewChallenge(9, "")
|
||||
return StudentKey{kid, s.Id, key, time.Now()}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (k StudentKey) GetStudent() (Student, error) {
|
||||
return GetStudent(int(k.IdStudent))
|
||||
}
|
||||
|
||||
func (k StudentKey) Update() (int64, error) {
|
||||
if res, err := DBExec("UPDATE student_keys SET id_student = ?, sshkey = ?, time = ? WHERE id_key = ?", k.IdStudent, k.Key, k.Time, k.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
||||
|
||||
func (k StudentKey) Delete() (int64, error) {
|
||||
if res, err := DBExec("DELETE FROM student_keys WHERE id_key = ?", k.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
||||
177
libadlin/students.go
Normal file
177
libadlin/students.go
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
package adlin
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha512"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Student struct {
|
||||
Id int64 `json:"id"`
|
||||
Login string `json:"login"`
|
||||
Time *time.Time `json:"time"`
|
||||
IP *string `json:"ip"`
|
||||
MAC *string `json:"mac"`
|
||||
}
|
||||
|
||||
func GetStudents() (students []Student, err error) {
|
||||
if rows, errr := DBQuery("SELECT S.id_student, S.login, MAX(L.time), L.ip, L.mac FROM students S INNER JOIN (SELECT a.id_student, a.time, a.ip, a.mac FROM student_login a INNER JOIN (SELECT id_student, MAX(time) AS time FROM student_login GROUP BY id_student) b ON a.id_student = b.id_student AND a.time = b.time) L ON S.id_student = L.id_student GROUP BY id_student"); errr != nil {
|
||||
return nil, errr
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var s Student
|
||||
if err = rows.Scan(&s.Id, &s.Login, &s.Time, &s.IP, &s.MAC); err != nil {
|
||||
return
|
||||
}
|
||||
students = append(students, s)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func GetStudent(id int) (s Student, err error) {
|
||||
err = DBQueryRow("SELECT S.id_student, S.login, MAX(L.time), L.ip, L.mac FROM students S INNER JOIN (SELECT a.id_student, a.time, a.ip, a.mac FROM student_login a INNER JOIN (SELECT id_student, MAX(time) AS time FROM student_login GROUP BY id_student) b ON a.id_student = b.id_student AND a.time = b.time) L ON S.id_student = L.id_student WHERE S.id_student=?", id).Scan(&s.Id, &s.Login, &s.Time, &s.IP, &s.MAC)
|
||||
return
|
||||
}
|
||||
|
||||
func GetStudentByLogin(login string) (s Student, err error) {
|
||||
err = DBQueryRow("SELECT S.id_student, S.login, MAX(L.time), L.ip, L.mac FROM students S INNER JOIN (SELECT a.id_student, a.time, a.ip, a.mac FROM student_login a INNER JOIN (SELECT id_student, MAX(time) AS time FROM student_login GROUP BY id_student) b ON a.id_student = b.id_student AND a.time = b.time) L ON S.id_student = L.id_student WHERE login=?", login).Scan(&s.Id, &s.Login, &s.Time, &s.IP, &s.MAC)
|
||||
return
|
||||
}
|
||||
|
||||
func StudentExists(login string) bool {
|
||||
var z int
|
||||
err := DBQueryRow("SELECT 1 FROM students WHERE login=?", login).Scan(&z)
|
||||
return err == nil && z == 1
|
||||
}
|
||||
|
||||
func NewStudent(login string) (Student, error) {
|
||||
t := time.Now()
|
||||
if res, err := DBExec("INSERT INTO students (login, time) VALUES (?, ?)", login, t); err != nil {
|
||||
return Student{}, err
|
||||
} else if sid, err := res.LastInsertId(); err != nil {
|
||||
return Student{}, err
|
||||
} else {
|
||||
return Student{sid, login, &t, nil, nil}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s Student) GetPKey() []byte {
|
||||
return hmac.New(sha512.New512_224, []byte(SharedSecret)).Sum([]byte(s.Login))
|
||||
}
|
||||
|
||||
func (s Student) Update() (int64, error) {
|
||||
if res, err := DBExec("UPDATE students SET login = ?, time = ? WHERE id_student = ?", s.Login, s.Time, s.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
||||
|
||||
func (s Student) Delete() (int64, error) {
|
||||
if res, err := DBExec("DELETE FROM students WHERE id_student = ?", s.Id); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
||||
|
||||
func ClearStudents() (int64, error) {
|
||||
if res, err := DBExec("DELETE FROM students"); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
return nb, err
|
||||
}
|
||||
}
|
||||
|
||||
type UnlockedChallenge struct {
|
||||
Id int64 `json:"id,omitempty"`
|
||||
IdStudent int64 `json:"id_student"`
|
||||
Challenge int `json:"challenge,omitempty"`
|
||||
Time time.Time `json:"time"`
|
||||
Value interface{} `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
func (s Student) GetStates() (ucs []UnlockedChallenge, err error) {
|
||||
if rows, errr := DBQuery("SELECT id_st, challenge, time FROM student_challenges WHERE id_student = ?", s.Id); errr != nil {
|
||||
return nil, errr
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var u UnlockedChallenge
|
||||
u.IdStudent = s.Id
|
||||
if err = rows.Scan(&u.Id, &u.Challenge, &u.Time); err != nil {
|
||||
return
|
||||
}
|
||||
ucs = append(ucs, u)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s Student) GetStatesByChallenge() (ucs []UnlockedChallenge, err error) {
|
||||
if rows, errr := DBQuery("SELECT id_st, challenge, MIN(time), value FROM student_challenges WHERE id_student = ? GROUP BY challenge, id_student", s.Id); errr != nil {
|
||||
return nil, errr
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var u UnlockedChallenge
|
||||
u.IdStudent = s.Id
|
||||
if err = rows.Scan(&u.Id, &u.Challenge, &u.Time, &u.Value); err != nil {
|
||||
return
|
||||
}
|
||||
ucs = append(ucs, u)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (s Student) UnlockNewChallenge(challenge int, value string) (UnlockedChallenge, error) {
|
||||
if res, err := DBExec("INSERT INTO student_challenges (id_student, challenge, time, value) VALUES (?, ?, ?, ?)", s.Id, challenge, time.Now(), value); err != nil {
|
||||
return UnlockedChallenge{}, err
|
||||
} else if utid, err := res.LastInsertId(); err != nil {
|
||||
return UnlockedChallenge{}, err
|
||||
} else {
|
||||
return UnlockedChallenge{utid, s.Id, challenge, time.Now(), value}, err
|
||||
}
|
||||
}
|
||||
|
||||
func (s Student) UpdateUnlockedChallenge(challenge int, value string) (UnlockedChallenge, error) {
|
||||
if _, err := DBExec("UPDATE student_challenges SET time = ?, value = ? WHERE id_student = ? AND challenge = ?", time.Now(), value, s.Id, challenge); err != nil {
|
||||
return UnlockedChallenge{}, err
|
||||
} else {
|
||||
return UnlockedChallenge{0, s.Id, challenge, time.Now(), value}, err
|
||||
}
|
||||
}
|
||||
|
||||
func (s Student) RegisterAccess(ip, mac string) error {
|
||||
if res, err := DBExec("INSERT INTO student_login (id_student, ip, mac, time) VALUES (?, ?, ?, ?)", s.Id, ip, mac, time.Now()); err != nil {
|
||||
return err
|
||||
} else if _, err := res.LastInsertId(); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
Reference in a new issue