2018-02-20 12:49:03 +00:00
package main
import (
2018-02-20 17:20:07 +00:00
"crypto/hmac"
"crypto/sha512"
2018-02-20 12:49:03 +00:00
"encoding/json"
2018-02-22 05:48:48 +00:00
"fmt"
2018-02-20 12:49:03 +00:00
"strings"
"time"
"github.com/julienschmidt/httprouter"
)
func init ( ) {
2018-02-22 03:47:57 +00:00
router . GET ( "/api/progress/" , apiHandler (
func ( httprouter . Params , [ ] byte ) ( interface { } , error ) {
if stds , err := getStudents ( ) ; err != nil {
return nil , err
} else {
2018-02-22 05:48:48 +00:00
ret := map [ string ] map [ string ] UnlockedChallenge { }
2018-02-22 03:47:57 +00:00
for _ , std := range stds {
2018-02-22 05:48:48 +00:00
if sts , err := std . getStates ( ) ; err == nil {
ret [ std . Login ] = map [ string ] UnlockedChallenge { }
for _ , s := range sts {
ret [ std . Login ] [ fmt . Sprintf ( "%d" , s . Challenge ) ] = s
}
2018-02-22 03:47:57 +00:00
}
}
return ret , nil
}
} ) )
2018-02-20 12:49:03 +00:00
router . GET ( "/api/students/" , apiHandler (
2018-02-20 17:20:07 +00:00
func ( httprouter . Params , [ ] byte ) ( interface { } , error ) {
return getStudents ( )
} ) )
2018-02-20 13:55:32 +00:00
router . POST ( "/api/students/" , remoteValidatorHandler ( apiHandler ( createStudent ) ) )
router . GET ( "/api/students/:sid/" , apiHandler ( studentHandler (
func ( std Student , _ [ ] byte ) ( interface { } , error ) {
2018-02-20 17:20:07 +00:00
return std , nil
} ) ) )
2018-02-20 13:55:32 +00:00
router . PUT ( "/api/students/:sid/" , remoteValidatorHandler ( apiHandler ( studentHandler ( updateStudent ) ) ) )
router . DELETE ( "/api/students/:sid/" , remoteValidatorHandler ( apiHandler ( studentHandler (
func ( std Student , _ [ ] byte ) ( interface { } , error ) {
2018-02-20 17:20:07 +00:00
return std . Delete ( )
} ) ) ) )
2019-03-04 08:00:22 +00:00
router . GET ( "/api/students/:sid/progress" , apiHandler ( studentHandler (
func ( std Student , _ [ ] byte ) ( interface { } , error ) {
ret := map [ string ] UnlockedChallenge { }
if sts , err := std . getStates ( ) ; err == nil {
for _ , s := range sts {
ret [ fmt . Sprintf ( "%d" , s . Challenge ) ] = s
}
}
return ret , nil
} ) ) )
2018-02-20 12:49:03 +00:00
}
type Student struct {
2019-03-13 10:19:51 +00:00
Id int64 ` json:"id" `
Login string ` json:"login" `
Time * time . Time ` json:"time" `
IP * string ` json:"ip" `
MAC * string ` json:"mac" `
2018-02-20 12:49:03 +00:00
}
2018-02-22 04:36:46 +00:00
type uploadedStudent struct {
Login string ` json:"login" `
IP string ` json:"ip" `
MAC string ` json:"mac" `
}
2018-02-20 12:49:03 +00:00
func getStudents ( ) ( students [ ] Student , err error ) {
2019-03-04 08:00:22 +00:00
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 {
2018-02-20 12:49:03 +00:00
return nil , errr
} else {
defer rows . Close ( )
for rows . Next ( ) {
var s Student
2019-02-26 12:39:17 +00:00
if err = rows . Scan ( & s . Id , & s . Login , & s . Time , & s . IP , & s . MAC ) ; err != nil {
2018-02-20 12:49:03 +00:00
return
}
students = append ( students , s )
}
if err = rows . Err ( ) ; err != nil {
return
}
return
}
}
2018-02-20 13:55:32 +00:00
func getStudent ( id int ) ( s Student , err error ) {
2019-02-26 12:39:17 +00:00
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 )
2018-02-20 12:49:03 +00:00
return
}
2018-02-20 13:55:32 +00:00
func getStudentByLogin ( login string ) ( s Student , err error ) {
2019-02-27 04:49:08 +00:00
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 )
2018-02-20 13:55:32 +00:00
return
}
2018-02-21 23:45:51 +00:00
func studentExists ( login string ) bool {
var z int
err := DBQueryRow ( "SELECT 1 FROM students WHERE login=?" , login ) . Scan ( & z )
2019-03-13 10:19:51 +00:00
return err == nil && z == 1
2018-02-21 23:45:51 +00:00
}
2018-02-22 04:13:21 +00:00
func NewStudent ( login string ) ( Student , error ) {
2019-03-13 10:19:51 +00:00
t := time . Now ( )
if res , err := DBExec ( "INSERT INTO students (login, time) VALUES (?, ?)" , login , t ) ; err != nil {
2018-02-20 12:49:03 +00:00
return Student { } , err
} else if sid , err := res . LastInsertId ( ) ; err != nil {
return Student { } , err
} else {
2019-03-13 10:19:51 +00:00
return Student { sid , login , & t , nil , nil } , nil
2018-02-20 12:49:03 +00:00
}
}
2018-02-20 17:20:07 +00:00
func ( s Student ) GetPKey ( ) [ ] byte {
return hmac . New ( sha512 . New512_224 , [ ] byte ( sharedSecret ) ) . Sum ( [ ] byte ( s . Login ) )
}
2018-02-20 12:49:03 +00:00
func ( s Student ) Update ( ) ( int64 , error ) {
2018-02-22 04:13:21 +00:00
if res , err := DBExec ( "UPDATE students SET login = ?, time = ? WHERE id_student = ?" , s . Login , s . Time , s . Id ) ; err != nil {
2018-02-20 12:49:03 +00:00
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
}
}
func createStudent ( _ httprouter . Params , body [ ] byte ) ( interface { } , error ) {
2018-02-22 04:36:46 +00:00
var err error
var std uploadedStudent
if err = json . Unmarshal ( body , & std ) ; err != nil {
2018-02-20 12:49:03 +00:00
return nil , err
}
2018-02-22 04:36:46 +00:00
var exist Student
if exist , err = getStudentByLogin ( strings . TrimSpace ( std . Login ) ) ; err != nil {
if exist , err = NewStudent ( strings . TrimSpace ( std . Login ) ) ; err != nil {
return nil , err
}
2018-02-21 23:45:51 +00:00
}
2018-02-22 04:36:46 +00:00
exist . registerAccess ( std . IP , std . MAC )
2019-02-27 04:49:08 +00:00
2019-03-13 10:19:51 +00:00
ip := fmt . Sprintf ( "172.23.0.%d" , exist . IPSuffix ( ) )
exist . IP = & ip
2019-02-27 04:49:08 +00:00
2018-02-22 04:36:46 +00:00
return exist , nil
2018-02-20 12:49:03 +00:00
}
2018-02-20 13:55:32 +00:00
func updateStudent ( current Student , body [ ] byte ) ( interface { } , error ) {
var new Student
if err := json . Unmarshal ( body , & new ) ; err != nil {
return nil , err
}
current . Login = new . Login
current . Time = new . Time
return current . Update ( )
}
2018-02-20 17:20:07 +00:00
type UnlockedChallenge struct {
Id int64 ` json:"id" `
IdStudent int64 ` json:"id_student" `
Challenge int ` json:"challenge" `
Time time . Time ` json:"time" `
2019-09-16 13:36:19 +00:00
Value string ` json:"value,omitempty" `
2018-02-20 17:20:07 +00:00
}
2018-02-22 03:47:57 +00:00
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
}
}
2019-09-16 13:36:19 +00:00
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
}
}
2018-02-20 17:20:07 +00:00
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 {
2019-09-16 13:36:19 +00:00
return UnlockedChallenge { utid , s . Id , challenge , time . Now ( ) , value } , err
2018-02-20 17:20:07 +00:00
}
}
2018-02-22 04:36:46 +00:00
2019-03-25 21:46:35 +00:00
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 {
2019-09-16 13:36:19 +00:00
return UnlockedChallenge { 0 , s . Id , challenge , time . Now ( ) , value } , err
2019-03-25 21:46:35 +00:00
}
}
2018-02-22 04:36:46 +00:00
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
}
}