split checker from token-validator
This commit is contained in:
parent
685dc0b0ea
commit
0c661f36f6
@ -9,6 +9,8 @@ import (
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/sparrc/go-ping"
|
||||
|
||||
"git.nemunai.re/lectures/adlin/libadlin"
|
||||
)
|
||||
|
||||
// ICMP
|
||||
@ -29,11 +31,6 @@ func check_ping(ip string, cb func(pkt *ping.Packet)) (err error) {
|
||||
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
|
||||
}
|
||||
|
||||
// PORT 53
|
||||
|
||||
func check_dns(domain, ip string) (err error) {
|
||||
@ -75,7 +72,7 @@ func check_https(domain, ip string) (err error) {
|
||||
// Main
|
||||
|
||||
func studentsChecker() {
|
||||
students, err := getStudents()
|
||||
students, err := adlin.GetStudents()
|
||||
if err != nil {
|
||||
log.Println("Unable to check students:", err)
|
||||
return
|
||||
@ -85,9 +82,9 @@ func studentsChecker() {
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
// Check ping
|
||||
std := s
|
||||
stdIP := studentIP(std.Id).String() + "1"
|
||||
stdIP := adlin.StudentIP(std.Id).String() + "1"
|
||||
go check_ping(stdIP, func(pkt *ping.Packet) {
|
||||
std.onPong(true)
|
||||
std.OnPong(true)
|
||||
|
||||
// Check HTTP
|
||||
if err := check_http(stdIP); err == nil {
|
||||
@ -99,7 +96,7 @@ func studentsChecker() {
|
||||
}
|
||||
|
||||
// Check HTTPs
|
||||
if err := check_https(std.myAssociatedDomain(), stdIP); err == nil {
|
||||
if err := check_https(std.MyAssociatedDomain(), stdIP); err == nil {
|
||||
if _, err := std.UnlockNewChallenge(101, ""); err != nil {
|
||||
if _, err := std.UpdateUnlockedChallenge(101, ""); err != nil {
|
||||
log.Printf("Unable to register challenge for %s: %s\n", std.Login, err.Error())
|
@ -1,4 +1,4 @@
|
||||
package main
|
||||
package adlin
|
||||
|
||||
import (
|
||||
"database/sql"
|
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
|
@ -1,4 +1,4 @@
|
||||
package main
|
||||
package adlin
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
@ -11,7 +11,7 @@ type Session struct {
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
func getSession(id []byte) (s Session, err error) {
|
||||
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
|
||||
}
|
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
|
||||
}
|
||||
}
|
@ -8,6 +8,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"git.nemunai.re/lectures/adlin/libadlin"
|
||||
)
|
||||
|
||||
var AuthFunc = checkAuth
|
||||
@ -20,17 +22,17 @@ func init() {
|
||||
router.POST("/api/auth/logout", apiRawHandler(logout))
|
||||
}
|
||||
|
||||
func validateAuthToken(s Student, _ httprouter.Params, _ []byte) (interface{}, error) {
|
||||
func validateAuthToken(s adlin.Student, _ httprouter.Params, _ []byte) (interface{}, error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func logout(w http.ResponseWriter, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "auth",
|
||||
Value: "",
|
||||
Path: baseURL,
|
||||
Expires: time.Unix(0,0),
|
||||
Secure: true,
|
||||
Name: "auth",
|
||||
Value: "",
|
||||
Path: baseURL + "/",
|
||||
Expires: time.Unix(0, 0),
|
||||
Secure: true,
|
||||
HttpOnly: true,
|
||||
})
|
||||
|
||||
@ -42,18 +44,18 @@ type loginForm struct {
|
||||
Password string
|
||||
}
|
||||
|
||||
func completeAuth(w http.ResponseWriter, username string, session *Session) (err error) {
|
||||
var std Student
|
||||
if !studentExists(username) {
|
||||
if std, err = NewStudent(username); err != nil {
|
||||
func completeAuth(w http.ResponseWriter, username string, session *adlin.Session) (err error) {
|
||||
var std adlin.Student
|
||||
if !adlin.StudentExists(username) {
|
||||
if std, err = adlin.NewStudent(username); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if std, err = getStudentByLogin(username); err != nil {
|
||||
} else if std, err = adlin.GetStudentByLogin(username); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if session == nil {
|
||||
var s Session
|
||||
var s adlin.Session
|
||||
s, err = std.NewSession()
|
||||
session = &s
|
||||
} else {
|
||||
@ -65,11 +67,11 @@ func completeAuth(w http.ResponseWriter, username string, session *Session) (err
|
||||
}
|
||||
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "auth",
|
||||
Value: base64.StdEncoding.EncodeToString(session.Id),
|
||||
Path: baseURL,
|
||||
Expires: time.Now().Add(30 * 24 * time.Hour),
|
||||
Secure: true,
|
||||
Name: "auth",
|
||||
Value: base64.StdEncoding.EncodeToString(session.Id),
|
||||
Path: baseURL + "/",
|
||||
Expires: time.Now().Add(30 * 24 * time.Hour),
|
||||
Secure: true,
|
||||
HttpOnly: true,
|
||||
})
|
||||
|
||||
@ -91,7 +93,7 @@ func checkAuth(w http.ResponseWriter, _ httprouter.Params, body []byte) (interfa
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if r, err := http.NewRequest("GET", "https://fic.srs.epita.fr/2021/", nil); err != nil {
|
||||
if r, err := http.NewRequest("GET", "https://owncloud.srs.epita.fr/remote.php/dav/", nil); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
r.SetBasicAuth(lf.Username, lf.Password)
|
||||
|
@ -12,11 +12,13 @@ import (
|
||||
|
||||
"github.com/coreos/go-oidc"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"git.nemunai.re/lectures/adlin/libadlin"
|
||||
)
|
||||
|
||||
var (
|
||||
oidcClientID = ""
|
||||
oidcSecret = ""
|
||||
oidcSecret = ""
|
||||
oauth2Config oauth2.Config
|
||||
oidcVerifier *oidc.IDTokenVerifier
|
||||
)
|
||||
@ -49,7 +51,7 @@ func initializeOIDC() {
|
||||
}
|
||||
|
||||
oidcConfig := oidc.Config{
|
||||
ClientID: oidcClientID,
|
||||
ClientID: oidcClientID,
|
||||
}
|
||||
oidcVerifier = provider.Verifier(&oidcConfig)
|
||||
}
|
||||
@ -57,7 +59,7 @@ func initializeOIDC() {
|
||||
}
|
||||
|
||||
func redirectOIDC_CRI(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
session, err := NewSession()
|
||||
session, err := adlin.NewSession()
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("{'errmsg':%q}", err.Error()), http.StatusInternalServerError)
|
||||
} else {
|
||||
@ -72,7 +74,7 @@ func OIDC_CRI_complete(w http.ResponseWriter, r *http.Request, ps httprouter.Par
|
||||
return
|
||||
}
|
||||
|
||||
session, err := getSession(idsession)
|
||||
session, err := adlin.GetSession(idsession)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("{'errmsg':%q}", err.Error()), http.StatusBadRequest)
|
||||
return
|
||||
|
@ -15,27 +15,29 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"git.nemunai.re/lectures/adlin/libadlin"
|
||||
)
|
||||
|
||||
const IPgwDMZ = "172.23.200.1"
|
||||
|
||||
type Challenge struct {
|
||||
Accessible []func(*Student, *http.Request) error
|
||||
Check func(*Student, *givenToken, int) error
|
||||
Accessible []func(*adlin.Student, *http.Request) error
|
||||
Check func(*adlin.Student, *givenToken, int) error
|
||||
}
|
||||
|
||||
/* Restrictions */
|
||||
|
||||
func noAccessRestriction(*Student, *http.Request) error {
|
||||
func noAccessRestriction(*adlin.Student, *http.Request) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func noAccess(*Student, *http.Request) error {
|
||||
func noAccess(*adlin.Student, *http.Request) error {
|
||||
return errors.New("This challenge cannot be accessed this way. ")
|
||||
}
|
||||
|
||||
func accessFrom(ip string) func(_ *Student, r *http.Request) error {
|
||||
return func(_ *Student, r *http.Request) error {
|
||||
func accessFrom(ip string) func(_ *adlin.Student, r *http.Request) error {
|
||||
return func(_ *adlin.Student, r *http.Request) error {
|
||||
if r.Header.Get("X-Forwarded-By") != ip {
|
||||
return errors.New("This challenge is not accessible this way.")
|
||||
}
|
||||
@ -43,8 +45,8 @@ func accessFrom(ip string) func(_ *Student, r *http.Request) error {
|
||||
}
|
||||
}
|
||||
|
||||
func notAccessFrom(ip string) func(_ *Student, r *http.Request) error {
|
||||
return func(_ *Student, r *http.Request) error {
|
||||
func notAccessFrom(ip string) func(_ *adlin.Student, r *http.Request) error {
|
||||
return func(_ *adlin.Student, r *http.Request) error {
|
||||
if r.Header.Get("X-Forwarded-By") == ip {
|
||||
return errors.New("This challenge is not accessible this way.")
|
||||
}
|
||||
@ -52,8 +54,8 @@ func notAccessFrom(ip string) func(_ *Student, r *http.Request) error {
|
||||
}
|
||||
}
|
||||
|
||||
func maxProxy(nb int) func(_ *Student, r *http.Request) error {
|
||||
return func(_ *Student, r *http.Request) error {
|
||||
func maxProxy(nb int) func(_ *adlin.Student, r *http.Request) error {
|
||||
return func(_ *adlin.Student, r *http.Request) error {
|
||||
if len(strings.Split(r.Header.Get("X-Forwarded-For"), ",")) > nb {
|
||||
return errors.New("This challenge is not accessible this way.")
|
||||
}
|
||||
@ -61,50 +63,49 @@ func maxProxy(nb int) func(_ *Student, r *http.Request) error {
|
||||
}
|
||||
}
|
||||
|
||||
func sslOnly(_ *Student, r *http.Request) error {
|
||||
func sslOnly(_ *adlin.Student, r *http.Request) error {
|
||||
if r.Header.Get("X-Forwarded-Proto") != "https" {
|
||||
return errors.New("This challenge should be performed over TLS.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
/* Challenges */
|
||||
|
||||
func challenge42(s *Student, t *givenToken, chid int) error {
|
||||
func challenge42(s *adlin.Student, t *givenToken, chid int) error {
|
||||
pkey := s.GetPKey()
|
||||
if expectedToken, err := GenerateToken(pkey, chid, []byte("42")); err != nil {
|
||||
return err
|
||||
} else if ! hmac.Equal(expectedToken, t.token) {
|
||||
} else if !hmac.Equal(expectedToken, t.token) {
|
||||
return errors.New("This is not the expected token.")
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func challengeDNS(s *Student, t *givenToken, chid int) error {
|
||||
func challengeDNS(s *adlin.Student, t *givenToken, chid int) error {
|
||||
pkey := s.GetPKey()
|
||||
if expectedToken, err := GenerateToken(pkey, chid, []byte("8dde678132d6c558fc6adaeb9f1d53bf6ec7b876308cf98c48604caa9138523c1ce58b672c87c7e7d9b7248b81804d3940dbf20bf263eeb683244f7c1143712d")); err != nil {
|
||||
return err
|
||||
} else if ! hmac.Equal(expectedToken, t.token) {
|
||||
} else if !hmac.Equal(expectedToken, t.token) {
|
||||
return errors.New("This is not the expected token.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func challengeTime(s *Student, t *givenToken, chid int) error {
|
||||
func challengeTime(s *adlin.Student, t *givenToken, chid int) error {
|
||||
pkey := s.GetPKey()
|
||||
if expectedToken, err := GenerateToken(pkey, chid, []byte(t.Data[0])); err != nil {
|
||||
return err
|
||||
} else if ! hmac.Equal(expectedToken, t.token) {
|
||||
} else if !hmac.Equal(expectedToken, t.token) {
|
||||
return errors.New("This is not the expected token.")
|
||||
} else if t, err := strconv.ParseInt(t.Data[0], 10, 64); err != nil {
|
||||
return err
|
||||
} else {
|
||||
var rt time.Time
|
||||
if t > 3000000000 {
|
||||
rt = time.Unix(t / 1000000000, t % 1000000000)
|
||||
rt = time.Unix(t/1000000000, t%1000000000)
|
||||
} else {
|
||||
rt = time.Unix(t, 0)
|
||||
}
|
||||
@ -117,7 +118,7 @@ func challengeTime(s *Student, t *givenToken, chid int) error {
|
||||
}
|
||||
}
|
||||
|
||||
func challengePing(s *Student, t *givenToken, chid int) error {
|
||||
func challengePing(s *adlin.Student, t *givenToken, chid int) error {
|
||||
var expected []byte
|
||||
switch s.Id % 5 {
|
||||
case 1:
|
||||
@ -164,7 +165,7 @@ func challengePing(s *Student, t *givenToken, chid int) error {
|
||||
}
|
||||
}
|
||||
|
||||
func challengeDisk(s *Student, t *givenToken, chid int) error {
|
||||
func challengeDisk(s *adlin.Student, t *givenToken, chid int) error {
|
||||
pkey := fmt.Sprintf("%x", s.GetPKey())
|
||||
|
||||
n1, err := strconv.Atoi(t.Data[0][0:2])
|
||||
@ -182,19 +183,19 @@ func challengeDisk(s *Student, t *givenToken, chid int) error {
|
||||
}
|
||||
|
||||
if n1+n2 > len(pkey) {
|
||||
n2 = len(pkey)-n1
|
||||
n2 = len(pkey) - n1
|
||||
}
|
||||
|
||||
expectedToken := sha512.Sum512([]byte(pkey[n1:n1+n2]))
|
||||
expectedToken := sha512.Sum512([]byte(pkey[n1 : n1+n2]))
|
||||
|
||||
if ! hmac.Equal(expectedToken[:], sum) {
|
||||
if !hmac.Equal(expectedToken[:], sum) {
|
||||
return errors.New("This is not the expected token.")
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func challengeEMail(s *Student, t *givenToken, chid int) error {
|
||||
func challengeEMail(s *adlin.Student, t *givenToken, chid int) error {
|
||||
return errors.New("This is not the expected token.")
|
||||
}
|
||||
|
||||
@ -204,61 +205,61 @@ func init() {
|
||||
challenges = []Challenge{
|
||||
/* Challenge 1 : 42 */
|
||||
Challenge{
|
||||
Accessible: []func(*Student, *http.Request) error{noAccessRestriction},
|
||||
Check: challenge42,
|
||||
Accessible: []func(*adlin.Student, *http.Request) error{noAccessRestriction},
|
||||
Check: challenge42,
|
||||
},
|
||||
|
||||
/* Challenge 2 : 42 from DMZ */
|
||||
Challenge{
|
||||
Accessible: []func(*Student, *http.Request) error{accessFrom(IPgwDMZ)},
|
||||
Check: challenge42,
|
||||
Accessible: []func(*adlin.Student, *http.Request) error{accessFrom(IPgwDMZ)},
|
||||
Check: challenge42,
|
||||
},
|
||||
|
||||
/* Challenge 3 : ssl (+ ntp) */
|
||||
Challenge{
|
||||
Accessible: []func(*Student, *http.Request) error{accessFrom(IPgwDMZ), sslOnly},
|
||||
Check: challengeTime,
|
||||
Accessible: []func(*adlin.Student, *http.Request) error{accessFrom(IPgwDMZ), sslOnly},
|
||||
Check: challengeTime,
|
||||
},
|
||||
|
||||
/* Challenge 4 : DNS TXT */
|
||||
Challenge{
|
||||
Accessible: []func(*Student, *http.Request) error{accessFrom(IPgwDMZ), sslOnly},
|
||||
Check: challengeDNS,
|
||||
Accessible: []func(*adlin.Student, *http.Request) error{accessFrom(IPgwDMZ), sslOnly},
|
||||
Check: challengeDNS,
|
||||
},
|
||||
|
||||
/* Challenge 5 : time net */
|
||||
Challenge{
|
||||
Accessible: []func(*Student, *http.Request) error{maxProxy(1)},
|
||||
Check: challengeTime,
|
||||
Accessible: []func(*adlin.Student, *http.Request) error{maxProxy(1)},
|
||||
Check: challengeTime,
|
||||
},
|
||||
|
||||
/* Bonus 0 : toctoc (read in source code) */
|
||||
Challenge{
|
||||
Accessible: []func(*Student, *http.Request) error{noAccessRestriction},
|
||||
Check: challenge42,
|
||||
Accessible: []func(*adlin.Student, *http.Request) error{noAccessRestriction},
|
||||
Check: challenge42,
|
||||
},
|
||||
|
||||
/* Bonus 1 : echo request */
|
||||
Challenge{
|
||||
Accessible: []func(*Student, *http.Request) error{noAccessRestriction},
|
||||
Check: challengePing,
|
||||
Accessible: []func(*adlin.Student, *http.Request) error{noAccessRestriction},
|
||||
Check: challengePing,
|
||||
},
|
||||
|
||||
/* Bonus 2 : disk */
|
||||
Challenge{
|
||||
Accessible: []func(*Student, *http.Request) error{noAccessRestriction},
|
||||
Check: challengeDisk,
|
||||
Accessible: []func(*adlin.Student, *http.Request) error{noAccessRestriction},
|
||||
Check: challengeDisk,
|
||||
},
|
||||
|
||||
/* Bonus 3 : mail */
|
||||
Challenge{
|
||||
Accessible: []func(*Student, *http.Request) error{noAccessRestriction},
|
||||
Check: challengeEMail,
|
||||
Accessible: []func(*adlin.Student, *http.Request) error{noAccessRestriction},
|
||||
Check: challengeEMail,
|
||||
},
|
||||
|
||||
/* Last : SSH key, see ssh.go:156 in NewKey function */
|
||||
Challenge{
|
||||
Accessible: []func(*Student, *http.Request) error{noAccess},
|
||||
Accessible: []func(*adlin.Student, *http.Request) error{noAccess},
|
||||
},
|
||||
}
|
||||
|
||||
@ -272,9 +273,9 @@ func init() {
|
||||
}
|
||||
|
||||
type givenToken struct {
|
||||
Login string `json:"login"`
|
||||
Challenge int `json:"challenge"`
|
||||
Token string `json:"token"`
|
||||
Login string `json:"login"`
|
||||
Challenge int `json:"challenge"`
|
||||
Token string `json:"token"`
|
||||
token []byte
|
||||
Data []string `json:"data"`
|
||||
}
|
||||
@ -293,7 +294,7 @@ func accessibleChallenge(r *http.Request, ps httprouter.Params, _ []byte) (inter
|
||||
} else if chid == 0 || chid >= len(challenges) {
|
||||
return nil, errors.New("This challenge doesn't exist")
|
||||
} else {
|
||||
for _, a := range challenges[chid - 1].Accessible {
|
||||
for _, a := range challenges[chid-1].Accessible {
|
||||
if err := a(nil, r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -319,13 +320,13 @@ func receiveChallenge(r *http.Request, ps httprouter.Params, body []byte) (inter
|
||||
return nil, errors.New("This is not the expected token.")
|
||||
}
|
||||
|
||||
var std Student
|
||||
var std adlin.Student
|
||||
|
||||
if stdid, err := strconv.Atoi(gt.Login); err == nil {
|
||||
if std, err = getStudent(stdid); err != nil {
|
||||
if std, err = adlin.GetStudent(stdid); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if std, err = getStudentByLogin(gt.Login); err != nil {
|
||||
} else if std, err = adlin.GetStudentByLogin(gt.Login); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -355,21 +356,21 @@ func receiveToken(r *http.Request, body []byte, chid int) (interface{}, error) {
|
||||
chid = gt.Challenge
|
||||
}
|
||||
|
||||
if chid <= 0 || chid - 1 > len(challenges) {
|
||||
if chid <= 0 || chid-1 > len(challenges) {
|
||||
return nil, errors.New("This challenge doesn't exist")
|
||||
}
|
||||
|
||||
// Is the challenge accessible?
|
||||
for _, a := range challenges[chid - 1].Accessible {
|
||||
for _, a := range challenges[chid-1].Accessible {
|
||||
if err := a(nil, r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if std, err := getStudentByLogin(gt.Login); err != nil {
|
||||
if std, err := adlin.GetStudentByLogin(gt.Login); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
if err := challenges[chid - 1].Check(&std, >, chid); err != nil {
|
||||
if err := challenges[chid-1].Check(&std, >, chid); err != nil {
|
||||
log.Printf("%s just try ch#%d: %s\n", std.Login, chid, err)
|
||||
return nil, err
|
||||
}
|
||||
|
@ -11,104 +11,104 @@ import (
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/miekg/dns"
|
||||
|
||||
"git.nemunai.re/lectures/adlin/libadlin"
|
||||
)
|
||||
|
||||
const (
|
||||
AssociatedDomainSuffix = "adlin2021.p0m.fr."
|
||||
DelegatedDomainSuffix = "srs.p0m.fr."
|
||||
ControlSocket = "[2a01:e0a:2b:2250::b]:53"
|
||||
ControlSocket = "[2a01:e0a:2b:2250::b]:53"
|
||||
)
|
||||
|
||||
var tsigSecret = map[string]string{"ddns.": "so6ZGir4GPAqINNh9U5c3A=="}
|
||||
|
||||
func init() {
|
||||
router.GET("/api/adomains/", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
router.GET("/api/adomains/", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
return student.GetAssociatedDomains(), nil
|
||||
}))
|
||||
router.POST("/api/adomains/", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
return true, student.AddAssociatedDomains()
|
||||
router.POST("/api/adomains/", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
return true, AddAssociatedDomains(student)
|
||||
}))
|
||||
router.GET("/api/adomains/:dn", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
return student.GetAssociatedDomain(ps.ByName("dn"))
|
||||
router.GET("/api/adomains/:dn", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
return GetAssociatedDomain(student, ps.ByName("dn"))
|
||||
}))
|
||||
|
||||
router.GET("/api/ddomains/", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
router.GET("/api/ddomains/", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
return []string{student.MyDelegatedDomain()}, nil
|
||||
}))
|
||||
router.GET("/api/ddomains/:dn/", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
return student.getRRDelegatedDomain(ps.ByName("dn"), "")
|
||||
router.GET("/api/ddomains/:dn/", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
return getRRDelegatedDomain(student, ps.ByName("dn"), "")
|
||||
}))
|
||||
router.GET("/api/ddomains/:dn/NS", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
return student.getRRDelegatedDomain(ps.ByName("dn"), "NS")
|
||||
router.GET("/api/ddomains/:dn/NS", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
return getRRDelegatedDomain(student, ps.ByName("dn"), "NS")
|
||||
}))
|
||||
router.POST("/api/ddomains/:dn/NS", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
router.POST("/api/ddomains/:dn/NS", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
var ue Entry
|
||||
if err := json.Unmarshal(body, &ue); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return true, student.AddNSDelegatedDomain(ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " "))
|
||||
return true, AddNSDelegatedDomain(student, ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " "))
|
||||
}))
|
||||
router.PATCH("/api/ddomains/:dn/NS", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
router.PATCH("/api/ddomains/:dn/NS", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
var ue Entry
|
||||
if err := json.Unmarshal(body, &ue); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return true, student.UpdateNSDelegatedDomain(ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, ""))
|
||||
return true, UpdateNSDelegatedDomain(student, ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, ""))
|
||||
}))
|
||||
router.DELETE("/api/ddomains/:dn/NS", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
router.DELETE("/api/ddomains/:dn/NS", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
var ue Entry
|
||||
if err := json.Unmarshal(body, &ue); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return true, student.DeleteRRDelegatedDomain(ps.ByName("dn"), "NS", strings.Join(ue.Values, " "))
|
||||
return true, DeleteRRDelegatedDomain(student, ps.ByName("dn"), "NS", strings.Join(ue.Values, " "))
|
||||
}))
|
||||
router.GET("/api/ddomains/:dn/GLUE", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
return student.getRRDelegatedDomain(ps.ByName("dn"), "AAAA")
|
||||
router.GET("/api/ddomains/:dn/GLUE", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
return getRRDelegatedDomain(student, ps.ByName("dn"), "AAAA")
|
||||
}))
|
||||
router.POST("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
router.POST("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
var ue Entry
|
||||
if err := json.Unmarshal(body, &ue); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return true, student.AddGLUEDelegatedDomain(ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " "))
|
||||
return true, AddGLUEDelegatedDomain(student, ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " "))
|
||||
}))
|
||||
router.PATCH("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
router.PATCH("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
var ue Entry
|
||||
if err := json.Unmarshal(body, &ue); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return true, student.UpdateGLUEDelegatedDomain(ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, " "))
|
||||
return true, UpdateGLUEDelegatedDomain(student, ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, " "))
|
||||
}))
|
||||
router.POST("/api/ddomains/:dn/GLUE", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
router.POST("/api/ddomains/:dn/GLUE", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
var ue Entry
|
||||
if err := json.Unmarshal(body, &ue); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return true, student.UpdateGLUEDelegatedDomain(ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, " "))
|
||||
return true, UpdateGLUEDelegatedDomain(student, ps.ByName("dn"), ue.TTL, ue.ValuesFrom, strings.Join(ue.Values, " "))
|
||||
}))
|
||||
router.DELETE("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
router.DELETE("/api/ddomains/:dn/AAAA", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
var ue Entry
|
||||
if err := json.Unmarshal(body, &ue); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return true, student.DeleteRRDelegatedDomain(ps.ByName("dn"), "AAAA", strings.Join(ue.Values, " "))
|
||||
return true, DeleteRRDelegatedDomain(student, ps.ByName("dn"), "AAAA", strings.Join(ue.Values, " "))
|
||||
}))
|
||||
router.GET("/api/ddomains/:dn/DS", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
return student.getRRDelegatedDomain(ps.ByName("dn"), "DS")
|
||||
router.GET("/api/ddomains/:dn/DS", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
return getRRDelegatedDomain(student, ps.ByName("dn"), "DS")
|
||||
}))
|
||||
router.POST("/api/ddomains/:dn/DS", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
router.POST("/api/ddomains/:dn/DS", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
var ue Entry
|
||||
if err := json.Unmarshal(body, &ue); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return true, student.AddDSDelegatedDomain(ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " "))
|
||||
return true, AddDSDelegatedDomain(student, ps.ByName("dn"), ue.TTL, strings.Join(ue.Values, " "))
|
||||
}))
|
||||
router.DELETE("/api/ddomains/:dn/DS", apiAuthHandler(func(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
router.DELETE("/api/ddomains/:dn/DS", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
var ue Entry
|
||||
if err := json.Unmarshal(body, &ue); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return true, student.DeleteRRDelegatedDomain(ps.ByName("dn"), "DS", strings.Join(ue.Values, " "))
|
||||
return true, DeleteRRDelegatedDomain(student, ps.ByName("dn"), "DS", strings.Join(ue.Values, " "))
|
||||
}))
|
||||
}
|
||||
|
||||
@ -160,19 +160,7 @@ func parseZoneRead(globalDomain string, domain string) (rr []Entry, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (student Student) GetAssociatedDomain(dn string) (rrs []Entry, err error) {
|
||||
func GetAssociatedDomain(student adlin.Student, dn string) (rrs []Entry, err error) {
|
||||
domains := student.GetAssociatedDomains()
|
||||
found := false
|
||||
for _, d := range domains {
|
||||
@ -185,7 +173,7 @@ func (student Student) GetAssociatedDomain(dn string) (rrs []Entry, err error) {
|
||||
err = errors.New(fmt.Sprintf("Unable to find domain %q.", dn))
|
||||
}
|
||||
|
||||
if entries, errr := parseZoneRead(AssociatedDomainSuffix, dn); err != nil {
|
||||
if entries, errr := parseZoneRead(adlin.AssociatedDomainSuffix, dn); err != nil {
|
||||
return nil, errr
|
||||
} else {
|
||||
for _, e := range entries {
|
||||
@ -198,19 +186,19 @@ func (student Student) GetAssociatedDomain(dn string) (rrs []Entry, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (student Student) AddAssociatedDomains() (err error) {
|
||||
func AddAssociatedDomains(student adlin.Student) (err error) {
|
||||
m1 := new(dns.Msg)
|
||||
m1.Id = dns.Id()
|
||||
m1.Opcode = dns.OpcodeUpdate
|
||||
m1.Question = make([]dns.Question, 1)
|
||||
m1.Question[0] = dns.Question{AssociatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||
m1.Question[0] = dns.Question{adlin.AssociatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||
|
||||
rrAd := new(dns.A)
|
||||
rrAd.Hdr = dns.RR_Header{Name: student.myAssociatedDomain(), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0}
|
||||
rrAd.Hdr = dns.RR_Header{Name: student.MyAssociatedDomain(), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 0}
|
||||
m1.Remove([]dns.RR{rrAd})
|
||||
|
||||
rrAAAAd := new(dns.AAAA)
|
||||
rrAAAAd.Hdr = dns.RR_Header{Name: student.myAssociatedDomain(), Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 0}
|
||||
rrAAAAd.Hdr = dns.RR_Header{Name: student.MyAssociatedDomain(), Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 0}
|
||||
m1.Remove([]dns.RR{rrAAAAd})
|
||||
|
||||
c := new(dns.Client)
|
||||
@ -226,16 +214,16 @@ func (student Student) AddAssociatedDomains() (err error) {
|
||||
m2.Id = dns.Id()
|
||||
m2.Opcode = dns.OpcodeUpdate
|
||||
m2.Question = make([]dns.Question, 1)
|
||||
m2.Question[0] = dns.Question{AssociatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||
m2.Question[0] = dns.Question{adlin.AssociatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||
|
||||
rrA := new(dns.A)
|
||||
rrA.Hdr = dns.RR_Header{Name: student.myAssociatedDomain(), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 3600}
|
||||
rrA.Hdr = dns.RR_Header{Name: student.MyAssociatedDomain(), Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 3600}
|
||||
rrA.A = net.IPv4(82, 64, 31, 248)
|
||||
m2.Insert([]dns.RR{rrA})
|
||||
|
||||
rrAAAA := new(dns.AAAA)
|
||||
rrAAAA.Hdr = dns.RR_Header{Name: student.myAssociatedDomain(), Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 3600}
|
||||
rrAAAA.AAAA = studentIP(student.Id)
|
||||
rrAAAA.Hdr = dns.RR_Header{Name: student.MyAssociatedDomain(), Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: 3600}
|
||||
rrAAAA.AAAA = adlin.StudentIP(student.Id)
|
||||
rrAAAA.AAAA[15] = 1
|
||||
m2.Insert([]dns.RR{rrAAAA})
|
||||
|
||||
@ -247,11 +235,7 @@ func (student Student) AddAssociatedDomains() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (student Student) MyDelegatedDomain() string {
|
||||
return fmt.Sprintf("%s.%s", strings.Trim(strings.Replace(student.Login, "_", "-", -1), "-_"), DelegatedDomainSuffix)
|
||||
}
|
||||
|
||||
func (student Student) getRRDelegatedDomain(dn string, rr string) (rrs []Entry, err error) {
|
||||
func getRRDelegatedDomain(student adlin.Student, dn string, rr string) (rrs []Entry, err error) {
|
||||
domains := []string{student.MyDelegatedDomain()}
|
||||
found := false
|
||||
for _, d := range domains {
|
||||
@ -264,7 +248,7 @@ func (student Student) getRRDelegatedDomain(dn string, rr string) (rrs []Entry,
|
||||
err = errors.New(fmt.Sprintf("Unable to find domain %q.", dn))
|
||||
}
|
||||
|
||||
if entries, errr := parseZoneRead(DelegatedDomainSuffix, dn); err != nil {
|
||||
if entries, errr := parseZoneRead(adlin.DelegatedDomainSuffix, dn); err != nil {
|
||||
return nil, errr
|
||||
} else {
|
||||
for _, e := range entries {
|
||||
@ -277,13 +261,13 @@ func (student Student) getRRDelegatedDomain(dn string, rr string) (rrs []Entry,
|
||||
return
|
||||
}
|
||||
|
||||
func (student Student) AddNSDelegatedDomain(dn string, ttl uint32, ns string) (err error) {
|
||||
func AddNSDelegatedDomain(student adlin.Student, dn string, ttl uint32, ns string) (err error) {
|
||||
for _, d := range []string{student.MyDelegatedDomain()} {
|
||||
m1 := new(dns.Msg)
|
||||
m1.Id = dns.Id()
|
||||
m1.Opcode = dns.OpcodeUpdate
|
||||
m1.Question = make([]dns.Question, 1)
|
||||
m1.Question[0] = dns.Question{DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||
m1.Question[0] = dns.Question{adlin.DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||
|
||||
rrNS := new(dns.NS)
|
||||
rrNS.Hdr = dns.RR_Header{Name: d, Rrtype: dns.TypeNS, Class: dns.ClassINET, Ttl: ttl}
|
||||
@ -300,13 +284,13 @@ func (student Student) AddNSDelegatedDomain(dn string, ttl uint32, ns string) (e
|
||||
return
|
||||
}
|
||||
|
||||
func (student Student) UpdateNSDelegatedDomain(dn string, ttl uint32, oldns string, ns string) (err error) {
|
||||
func UpdateNSDelegatedDomain(student adlin.Student, dn string, ttl uint32, oldns string, ns string) (err error) {
|
||||
for _, d := range []string{student.MyDelegatedDomain()} {
|
||||
m1 := new(dns.Msg)
|
||||
m1.Id = dns.Id()
|
||||
m1.Opcode = dns.OpcodeUpdate
|
||||
m1.Question = make([]dns.Question, 1)
|
||||
m1.Question[0] = dns.Question{DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||
m1.Question[0] = dns.Question{adlin.DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||
|
||||
rrOldNS := new(dns.NS)
|
||||
rrOldNS.Hdr = dns.RR_Header{Name: d, Rrtype: dns.TypeNS, Class: dns.ClassINET}
|
||||
@ -328,7 +312,7 @@ func (student Student) UpdateNSDelegatedDomain(dn string, ttl uint32, oldns stri
|
||||
return
|
||||
}
|
||||
|
||||
func (student Student) AddGLUEDelegatedDomain(dn string, ttl uint32, aaaa string) (err error) {
|
||||
func AddGLUEDelegatedDomain(student adlin.Student, dn string, ttl uint32, aaaa string) (err error) {
|
||||
domains := []string{student.MyDelegatedDomain()}
|
||||
found := false
|
||||
for _, d := range domains {
|
||||
@ -346,7 +330,7 @@ func (student Student) AddGLUEDelegatedDomain(dn string, ttl uint32, aaaa string
|
||||
m1.Id = dns.Id()
|
||||
m1.Opcode = dns.OpcodeUpdate
|
||||
m1.Question = make([]dns.Question, 1)
|
||||
m1.Question[0] = dns.Question{DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||
m1.Question[0] = dns.Question{adlin.DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||
|
||||
var rr dns.RR
|
||||
rr, err = dns.NewRR(fmt.Sprintf("%s %d IN AAAA %s", dn, ttl, aaaa))
|
||||
@ -364,7 +348,7 @@ func (student Student) AddGLUEDelegatedDomain(dn string, ttl uint32, aaaa string
|
||||
return
|
||||
}
|
||||
|
||||
func (student Student) UpdateGLUEDelegatedDomain(dn string, ttl uint32, oldaaaa string, aaaa string) (err error) {
|
||||
func UpdateGLUEDelegatedDomain(student adlin.Student, dn string, ttl uint32, oldaaaa string, aaaa string) (err error) {
|
||||
domains := []string{student.MyDelegatedDomain()}
|
||||
found := false
|
||||
for _, d := range domains {
|
||||
@ -382,7 +366,7 @@ func (student Student) UpdateGLUEDelegatedDomain(dn string, ttl uint32, oldaaaa
|
||||
m1.Id = dns.Id()
|
||||
m1.Opcode = dns.OpcodeUpdate
|
||||
m1.Question = make([]dns.Question, 1)
|
||||
m1.Question[0] = dns.Question{DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||
m1.Question[0] = dns.Question{adlin.DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||
|
||||
var rr dns.RR
|
||||
|
||||
@ -406,7 +390,7 @@ func (student Student) UpdateGLUEDelegatedDomain(dn string, ttl uint32, oldaaaa
|
||||
return
|
||||
}
|
||||
|
||||
func (student Student) AddDSDelegatedDomain(dn string, ttl uint32, rdata string) (err error) {
|
||||
func AddDSDelegatedDomain(student adlin.Student, dn string, ttl uint32, rdata string) (err error) {
|
||||
domains := []string{student.MyDelegatedDomain()}
|
||||
found := false
|
||||
for _, d := range domains {
|
||||
@ -438,7 +422,7 @@ func (student Student) AddDSDelegatedDomain(dn string, ttl uint32, rdata string)
|
||||
m1.Id = dns.Id()
|
||||
m1.Opcode = dns.OpcodeUpdate
|
||||
m1.Question = make([]dns.Question, 1)
|
||||
m1.Question[0] = dns.Question{DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||
m1.Question[0] = dns.Question{adlin.DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||
|
||||
var ds *dns.DS
|
||||
ds = dnskey.ToDS(dns.SHA256)
|
||||
@ -456,7 +440,7 @@ func (student Student) AddDSDelegatedDomain(dn string, ttl uint32, rdata string)
|
||||
return
|
||||
}
|
||||
|
||||
func (student Student) DeleteRRDelegatedDomain(dn string, rr string, values ...string) (err error) {
|
||||
func DeleteRRDelegatedDomain(student adlin.Student, dn string, rr string, values ...string) (err error) {
|
||||
domains := []string{student.MyDelegatedDomain()}
|
||||
found := false
|
||||
for _, d := range domains {
|
||||
@ -474,7 +458,7 @@ func (student Student) DeleteRRDelegatedDomain(dn string, rr string, values ...s
|
||||
m1.Id = dns.Id()
|
||||
m1.Opcode = dns.OpcodeUpdate
|
||||
m1.Question = make([]dns.Question, 1)
|
||||
m1.Question[0] = dns.Question{DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||
m1.Question[0] = dns.Question{adlin.DelegatedDomainSuffix, dns.TypeSOA, dns.ClassINET}
|
||||
|
||||
rrr, errr := dns.NewRR(fmt.Sprintf("%s %s %s", dn, rr, strings.Join(values, " ")))
|
||||
if errr != nil {
|
||||
|
@ -2,6 +2,8 @@ package main
|
||||
|
||||
import (
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"git.nemunai.re/lectures/adlin/libadlin"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -9,7 +11,7 @@ func init() {
|
||||
}
|
||||
|
||||
func computeGrades(_ httprouter.Params, _ []byte) (interface{}, error) {
|
||||
if stds, err := getStudents(); err != nil {
|
||||
if stds, err := adlin.GetStudents(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
res := map[string]map[string]float32{}
|
||||
@ -20,7 +22,7 @@ func computeGrades(_ httprouter.Params, _ []byte) (interface{}, error) {
|
||||
"TP2": 0,
|
||||
}
|
||||
|
||||
if states, err := std.getStatesByChallenge(); err != nil {
|
||||
if states, err := std.GetStatesByChallenge(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
for _, st := range states {
|
||||
|
@ -14,6 +14,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"git.nemunai.re/lectures/adlin/libadlin"
|
||||
)
|
||||
|
||||
var router = httprouter.New()
|
||||
@ -26,8 +28,8 @@ type DispatchFunction func(httprouter.Params, []byte) (interface{}, error)
|
||||
|
||||
func remoteValidatorHandler(f func(http.ResponseWriter, *http.Request, httprouter.Params)) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
expectedMAC := hmac.New(sha512.New, []byte(sharedSecret)).Sum([]byte(fmt.Sprintf("%d", time.Now().Unix()/10)))
|
||||
previousMAC := hmac.New(sha512.New, []byte(sharedSecret)).Sum([]byte(fmt.Sprintf("%d", time.Now().Unix()/10-1)))
|
||||
expectedMAC := hmac.New(sha512.New, []byte(adlin.SharedSecret)).Sum([]byte(fmt.Sprintf("%d", time.Now().Unix()/10)))
|
||||
previousMAC := hmac.New(sha512.New, []byte(adlin.SharedSecret)).Sum([]byte(fmt.Sprintf("%d", time.Now().Unix()/10-1)))
|
||||
|
||||
if aauth, err := base64.StdEncoding.DecodeString(r.Header.Get("X-ADLIN-Authentication")); err != nil {
|
||||
http.Error(w, fmt.Sprintf("{\"errmsg\":%q}\n", err), http.StatusUnauthorized)
|
||||
@ -39,7 +41,7 @@ func remoteValidatorHandler(f func(http.ResponseWriter, *http.Request, httproute
|
||||
}
|
||||
}
|
||||
|
||||
func rawHandler(f func(http.ResponseWriter, *http.Request, httprouter.Params, []byte), access ...func(*Student, *http.Request) error) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||
func rawHandler(f func(http.ResponseWriter, *http.Request, httprouter.Params, []byte), access ...func(*adlin.Student, *http.Request) error) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
if addr := r.Header.Get("X-Forwarded-For"); addr != "" {
|
||||
r.RemoteAddr = addr
|
||||
@ -49,17 +51,17 @@ func rawHandler(f func(http.ResponseWriter, *http.Request, httprouter.Params, []
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
// Read Authorization header
|
||||
var student *Student = nil
|
||||
var student *adlin.Student = nil
|
||||
if cookie, err := r.Cookie("auth"); err == nil {
|
||||
if sessionid, err := base64.StdEncoding.DecodeString(cookie.Value); err != nil {
|
||||
http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err), http.StatusNotAcceptable)
|
||||
return
|
||||
} else if session, err := getSession(sessionid); err != nil {
|
||||
} else if session, err := adlin.GetSession(sessionid); err != nil {
|
||||
http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err), http.StatusUnauthorized)
|
||||
return
|
||||
} else if session.IdStudent == nil {
|
||||
student = nil
|
||||
} else if std, err := getStudent(int(*session.IdStudent)); err != nil {
|
||||
} else if std, err := adlin.GetStudent(int(*session.IdStudent)); err != nil {
|
||||
http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err), http.StatusUnauthorized)
|
||||
return
|
||||
} else {
|
||||
@ -132,41 +134,41 @@ func responseHandler(f func(*http.Request, httprouter.Params, []byte) (interface
|
||||
}
|
||||
}
|
||||
|
||||
func challengeHandler(f func (*http.Request, []byte, int) (interface{}, error)) func(*http.Request, httprouter.Params, []byte) (interface{}, error) {
|
||||
func challengeHandler(f func(*http.Request, []byte, int) (interface{}, error)) func(*http.Request, httprouter.Params, []byte) (interface{}, error) {
|
||||
return func(r *http.Request, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
return f(r, body, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func definedChallengeHandler(f func (*http.Request, []byte, int) (interface{}, error), chid int) func(*http.Request, httprouter.Params, []byte) (interface{}, error) {
|
||||
func definedChallengeHandler(f func(*http.Request, []byte, int) (interface{}, error), chid int) func(*http.Request, httprouter.Params, []byte) (interface{}, error) {
|
||||
return func(r *http.Request, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
return f(r, body, chid)
|
||||
}
|
||||
}
|
||||
|
||||
func apiRawHandler(f func(http.ResponseWriter, httprouter.Params, []byte) (interface{}, error), access ...func(*Student, *http.Request) error) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||
return rawHandler(func (w http.ResponseWriter, r *http.Request, ps httprouter.Params, b []byte) {
|
||||
responseHandler(func (_ *http.Request, ps httprouter.Params, b []byte)(interface{}, error) {
|
||||
func apiRawHandler(f func(http.ResponseWriter, httprouter.Params, []byte) (interface{}, error), access ...func(*adlin.Student, *http.Request) error) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||
return rawHandler(func(w http.ResponseWriter, r *http.Request, ps httprouter.Params, b []byte) {
|
||||
responseHandler(func(_ *http.Request, ps httprouter.Params, b []byte) (interface{}, error) {
|
||||
return f(w, ps, b)
|
||||
})(w, r, ps, b)
|
||||
}, access...)
|
||||
}
|
||||
|
||||
func apiHandler(f DispatchFunction, access ...func(*Student, *http.Request) error) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||
return rawHandler(responseHandler(func (_ *http.Request, ps httprouter.Params, b []byte) (interface{}, error) { return f(ps, b) }), access...)
|
||||
func apiHandler(f DispatchFunction, access ...func(*adlin.Student, *http.Request) error) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||
return rawHandler(responseHandler(func(_ *http.Request, ps httprouter.Params, b []byte) (interface{}, error) { return f(ps, b) }), access...)
|
||||
}
|
||||
|
||||
func apiAuthHandler(f func(Student, httprouter.Params, []byte) (interface{}, error), access ...func(*Student, *http.Request) error) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||
return rawHandler(responseHandler(func (r *http.Request, ps httprouter.Params, b []byte) (interface{}, error) {
|
||||
func apiAuthHandler(f func(adlin.Student, httprouter.Params, []byte) (interface{}, error), access ...func(*adlin.Student, *http.Request) error) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||
return rawHandler(responseHandler(func(r *http.Request, ps httprouter.Params, b []byte) (interface{}, error) {
|
||||
if cookie, err := r.Cookie("auth"); err != nil {
|
||||
return nil, errors.New("Authorization required")
|
||||
} else if sessionid, err := base64.StdEncoding.DecodeString(cookie.Value); err != nil {
|
||||
return nil, err
|
||||
} else if session, err := getSession(sessionid); err != nil {
|
||||
} else if session, err := adlin.GetSession(sessionid); err != nil {
|
||||
return nil, err
|
||||
} else if session.IdStudent == nil {
|
||||
return nil, errors.New("Authorization required")
|
||||
} else if std, err := getStudent(int(*session.IdStudent)); err != nil {
|
||||
} else if std, err := adlin.GetStudent(int(*session.IdStudent)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return f(std, ps, b)
|
||||
@ -174,15 +176,15 @@ func apiAuthHandler(f func(Student, httprouter.Params, []byte) (interface{}, err
|
||||
}), access...)
|
||||
}
|
||||
|
||||
func studentHandler(f func(Student, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) {
|
||||
func studentHandler(f func(adlin.Student, []byte) (interface{}, error)) func(httprouter.Params, []byte) (interface{}, error) {
|
||||
return func(ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
if sid, err := strconv.Atoi(string(ps.ByName("sid"))); err != nil {
|
||||
if student, err := getStudentByLogin(ps.ByName("sid")); err != nil {
|
||||
if student, err := adlin.GetStudentByLogin(ps.ByName("sid")); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return f(student, body)
|
||||
}
|
||||
} else if student, err := getStudent(sid); err != nil {
|
||||
} else if student, err := adlin.GetStudent(sid); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return f(student, body)
|
||||
|
@ -4,23 +4,25 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"git.nemunai.re/lectures/adlin/libadlin"
|
||||
)
|
||||
|
||||
func init() {
|
||||
router.GET("/api/ips", apiHandler(showIPs))
|
||||
router.GET("/api/students/:sid/ips", apiHandler(studentHandler(func(student Student, body []byte) (interface{}, error) {
|
||||
router.GET("/api/students/:sid/ips", apiHandler(studentHandler(func(student adlin.Student, body []byte) (interface{}, error) {
|
||||
return getStudentIPs(student), nil
|
||||
})))
|
||||
}
|
||||
|
||||
func (s Student) IPSuffix() int64 {
|
||||
return s.Id * 5 + 10
|
||||
func IPSuffix(s adlin.Student) int64 {
|
||||
return s.Id*4 + 10
|
||||
}
|
||||
|
||||
func showIPs(_ httprouter.Params, body []byte) (interface{}, error) {
|
||||
r := make(map[string]map[string]string)
|
||||
|
||||
students, err := getStudents()
|
||||
students, err := adlin.GetStudents()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -40,13 +42,14 @@ func showIPs(_ httprouter.Params, body []byte) (interface{}, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func getStudentIPs(student Student) (r map[string]string) {
|
||||
func getStudentIPs(student adlin.Student) (r map[string]string) {
|
||||
r = make(map[string]string)
|
||||
|
||||
r["vlan0"] = fmt.Sprintf("172.23.0.%d", student.IPSuffix())
|
||||
r["vlan7"] = fmt.Sprintf("172.23.142.%d", student.IPSuffix())
|
||||
r["wg"] = studentIP(student.Id).String()
|
||||
r["adn"] = student.myAssociatedDomain()
|
||||
r["vlan0"] = fmt.Sprintf("172.23.0.%d", IPSuffix(student))
|
||||
r["wg0"] = fmt.Sprintf("172.17.0.%d", IPSuffix(student))
|
||||
r["vlan7"] = fmt.Sprintf("172.23.142.%d", IPSuffix(student))
|
||||
r["wg"] = adlin.StudentIP(student.Id).String()
|
||||
r["adn"] = student.MyAssociatedDomain()
|
||||
r["ddn"] = student.MyDelegatedDomain()
|
||||
|
||||
return
|
||||
|
@ -12,11 +12,11 @@ import (
|
||||
"path"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"git.nemunai.re/lectures/adlin/libadlin"
|
||||
)
|
||||
|
||||
var baseURL string = "/"
|
||||
var sharedSecret string
|
||||
|
||||
type ResponseWriterPrefix struct {
|
||||
real http.ResponseWriter
|
||||
@ -60,9 +60,9 @@ func StripPrefix(prefix string, h http.Handler) http.Handler {
|
||||
|
||||
func main() {
|
||||
var bind = flag.String("bind", ":8081", "Bind port/socket")
|
||||
var dsn = flag.String("dsn", DSNGenerator(), "DSN to connect to the MySQL server")
|
||||
var dsn = flag.String("dsn", adlin.DSNGenerator(), "DSN to connect to the MySQL server")
|
||||
flag.StringVar(&baseURL, "baseurl", baseURL, "URL prepended to each URL")
|
||||
flag.StringVar(&sharedSecret, "sharedsecret", "adelina", "secret used to communicate with remote validator")
|
||||
flag.StringVar(&adlin.SharedSecret, "sharedsecret", "adelina", "secret used to communicate with remote validator")
|
||||
flag.StringVar(&AuthorizedKeysLocation, "authorizedkeyslocation", AuthorizedKeysLocation, "File for allowing user to SSH to the machine")
|
||||
flag.StringVar(&SshPiperLocation, "sshPiperLocation", SshPiperLocation, "Directory containing directories for sshpiperd")
|
||||
var dummyauth = flag.Bool("dummyauth", false, "don't perform password check")
|
||||
@ -88,13 +88,13 @@ func main() {
|
||||
|
||||
// Initialize contents
|
||||
log.Println("Opening database...")
|
||||
if err := DBInit(*dsn); err != nil {
|
||||
if err := adlin.DBInit(*dsn); err != nil {
|
||||
log.Fatal("Cannot open the database: ", err)
|
||||
}
|
||||
defer DBClose()
|
||||
defer adlin.DBClose()
|
||||
|
||||
log.Println("Creating database...")
|
||||
if err := DBCreate(); err != nil {
|
||||
if err := adlin.DBCreate(); err != nil {
|
||||
log.Fatal("Cannot create database: ", err)
|
||||
}
|
||||
|
||||
@ -107,14 +107,6 @@ func main() {
|
||||
Handler: StripPrefix(baseURL, Router()),
|
||||
}
|
||||
|
||||
// Launch checker
|
||||
go func() {
|
||||
for {
|
||||
studentsChecker()
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
}()
|
||||
|
||||
// Serve content
|
||||
go func() {
|
||||
log.Fatal(srv.ListenAndServe())
|
||||
|
@ -3,21 +3,22 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"git.nemunai.re/lectures/adlin/libadlin"
|
||||
)
|
||||
|
||||
var PongSecret = "felixfixit"
|
||||
|
||||
func init() {
|
||||
router.GET("/api/students/:sid/ping", apiHandler(studentHandler(lastPing)))
|
||||
router.GET("/api/students/:sid/pong", apiHandler(studentHandler(func(student Student, body []byte) (interface{}, error) {
|
||||
return student.lastPongs()
|
||||
router.GET("/api/students/:sid/pong", apiHandler(studentHandler(func(student adlin.Student, body []byte) (interface{}, error) {
|
||||
return student.LastPongs()
|
||||
})))
|
||||
router.POST("/api/students/:sid/pong", apiHandler(studentHandler(stdPong), sslOnly))
|
||||
}
|
||||
|
||||
func lastPing(student Student, body []byte) (interface{}, error) {
|
||||
if pongs, err := student.lastPongs(); err != nil {
|
||||
func lastPing(student adlin.Student, body []byte) (interface{}, error) {
|
||||
if pongs, err := student.LastPongs(); err != nil {
|
||||
return nil, err
|
||||
} else if len(pongs) <= 0 {
|
||||
return false, nil
|
||||
@ -26,33 +27,7 @@ func lastPing(student Student, body []byte) (interface{}, error) {
|
||||
}
|
||||
}
|
||||
|
||||
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 stdPong(student Student, body []byte) (interface{}, error) {
|
||||
func stdPong(student adlin.Student, body []byte) (interface{}, error) {
|
||||
var gt givenToken
|
||||
if err := json.Unmarshal(body, >); err != nil {
|
||||
return nil, err
|
||||
@ -62,5 +37,5 @@ func stdPong(student Student, body []byte) (interface{}, error) {
|
||||
return nil, errors.New("This is not the expected token.")
|
||||
}
|
||||
|
||||
return true, student.onPong(gt.Challenge == 0)
|
||||
return true, student.OnPong(gt.Challenge == 0)
|
||||
}
|
||||
|
@ -1,23 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"encoding/json"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"git.nemunai.re/lectures/adlin/libadlin"
|
||||
)
|
||||
|
||||
var AuthorizedKeysLocation = "/root/.ssh/authorized_keys"
|
||||
@ -26,7 +24,7 @@ var SshPiperLocation = "/var/sshpiper/"
|
||||
func init() {
|
||||
router.GET("/sshkeys", apiHandler(
|
||||
func(httprouter.Params, []byte) (interface{}, error) {
|
||||
return getStudentKeys()
|
||||
return adlin.GetStudentKeys()
|
||||
}))
|
||||
router.POST("/sshkeys", rawHandler(responseHandler(receiveKey)))
|
||||
router.GET("/sshkeys/authorizedkeys", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
@ -35,153 +33,28 @@ func init() {
|
||||
router.GET("/api/students/:sid/hassshkeys", apiHandler(studentHandler(hasSSHKeys)))
|
||||
router.GET("/api/students/:sid/authorizedkeys", func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||
if sid, err := strconv.Atoi(string(ps.ByName("sid"))); err != nil {
|
||||
if student, err := getStudentByLogin(ps.ByName("sid")); err != nil {
|
||||
if student, err := adlin.GetStudentByLogin(ps.ByName("sid")); err != nil {
|
||||
http.Error(w, "Student doesn't exist.", http.StatusNotFound)
|
||||
} else {
|
||||
student.dumpAuthorizedKeysFile(w)
|
||||
dumpStdAuthorizedKeysFile(student, w)
|
||||
}
|
||||
} else if student, err := getStudent(sid); err != nil {
|
||||
} else if student, err := adlin.GetStudent(sid); err != nil {
|
||||
http.Error(w, "Student doesn't exist.", http.StatusNotFound)
|
||||
} else {
|
||||
student.dumpAuthorizedKeysFile(w)
|
||||
dumpStdAuthorizedKeysFile(student, w)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func hasSSHKeys(student Student, body []byte) (interface{}, error) {
|
||||
if keys, err := student.getKeys(); err != nil {
|
||||
func hasSSHKeys(student adlin.Student, body []byte) (interface{}, error) {
|
||||
if keys, err := student.GetKeys(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return len(keys) > 0, nil
|
||||
}
|
||||
}
|
||||
|
||||
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(len(challenges), "")
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
func receiveKey(r *http.Request, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
var gt givenToken
|
||||
if err := json.Unmarshal(body, >); err != nil {
|
||||
@ -193,7 +66,7 @@ func receiveKey(r *http.Request, ps httprouter.Params, body []byte) (interface{}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if std, err := getStudentByLogin(gt.Login); err != nil {
|
||||
if std, err := adlin.GetStudentByLogin(gt.Login); err != nil {
|
||||
return nil, err
|
||||
} else if len(gt.Data) < 2 {
|
||||
return nil, errors.New("No key found!")
|
||||
@ -207,7 +80,7 @@ func receiveKey(r *http.Request, ps httprouter.Params, body []byte) (interface{}
|
||||
|
||||
if expectedToken, err := GenerateToken(pkey, 0, data...); err != nil {
|
||||
return nil, err
|
||||
} else if ! hmac.Equal(expectedToken, gt.token) {
|
||||
} else if !hmac.Equal(expectedToken, gt.token) {
|
||||
return nil, errors.New("This is not the expected token.")
|
||||
}
|
||||
|
||||
@ -240,7 +113,7 @@ func receiveKey(r *http.Request, ps httprouter.Params, body []byte) (interface{}
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
std.dumpAuthorizedKeysFile(file)
|
||||
dumpStdAuthorizedKeysFile(std, file)
|
||||
os.Symlink(path.Join(SshPiperLocation, "sshpiper_upstream"), path.Join(SshPiperLocation, std.Login, "sshpiper_upstream"))
|
||||
os.Symlink(path.Join(SshPiperLocation, "id_rsa"), path.Join(SshPiperLocation, std.Login, "id_rsa"))
|
||||
}
|
||||
@ -254,7 +127,7 @@ func receiveKey(r *http.Request, ps httprouter.Params, body []byte) (interface{}
|
||||
func dumpAuthorizedKeysFile(w io.Writer) {
|
||||
seen := map[string]interface{}{}
|
||||
|
||||
if keys, _ := getStudentKeys(); keys != nil {
|
||||
if keys, _ := adlin.GetStudentKeys(); keys != nil {
|
||||
for _, k := range keys {
|
||||
if _, exists := seen[k.Key]; exists {
|
||||
continue
|
||||
@ -268,10 +141,10 @@ func dumpAuthorizedKeysFile(w io.Writer) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s Student) dumpAuthorizedKeysFile(w io.Writer) {
|
||||
func dumpStdAuthorizedKeysFile(s adlin.Student, w io.Writer) {
|
||||
seen := map[string]interface{}{}
|
||||
|
||||
if keys, _ := s.getKeys(); keys != nil {
|
||||
if keys, _ := s.GetKeys(); keys != nil {
|
||||
for _, k := range keys {
|
||||
if _, exists := seen[k.Key]; exists {
|
||||
continue
|
||||
|
@ -1,37 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha512"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"git.nemunai.re/lectures/adlin/libadlin"
|
||||
)
|
||||
|
||||
func init() {
|
||||
router.GET("/api/progress", apiHandler(
|
||||
func(httprouter.Params, []byte) (interface{}, error) {
|
||||
if stds, err := getStudents(); err != nil {
|
||||
if stds, err := adlin.GetStudents(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
ret := map[string]map[string]UnlockedChallenge{}
|
||||
ret := map[string]map[string]adlin.UnlockedChallenge{}
|
||||
for _, std := range stds {
|
||||
if sts, err := std.getStates(); err == nil {
|
||||
ret[std.Login] = map[string]UnlockedChallenge{}
|
||||
if sts, err := std.GetStates(); err == nil {
|
||||
ret[std.Login] = map[string]adlin.UnlockedChallenge{}
|
||||
|
||||
for _, s := range sts {
|
||||
ret[std.Login][fmt.Sprintf("%d", s.Challenge)] = s
|
||||
}
|
||||
|
||||
if pongs, err := std.lastPongs(); err == nil && len(pongs) > 0 {
|
||||
ret[std.Login]["ping"] = UnlockedChallenge{
|
||||
if pongs, err := std.LastPongs(); err == nil && len(pongs) > 0 {
|
||||
ret[std.Login]["ping"] = adlin.UnlockedChallenge{
|
||||
IdStudent: std.Id,
|
||||
Time: pongs[0].Date,
|
||||
Value: pongs[0].State,
|
||||
Time: pongs[0].Date,
|
||||
Value: pongs[0].State,
|
||||
}
|
||||
} else if err != nil {
|
||||
log.Println(err)
|
||||
@ -43,23 +42,23 @@ func init() {
|
||||
}))
|
||||
router.GET("/api/students/", apiHandler(
|
||||
func(httprouter.Params, []byte) (interface{}, error) {
|
||||
return getStudents()
|
||||
return adlin.GetStudents()
|
||||
}))
|
||||
router.POST("/api/students/", remoteValidatorHandler(apiHandler(createStudent)))
|
||||
router.GET("/api/students/:sid/", apiHandler(studentHandler(
|
||||
func(std Student, _ []byte) (interface{}, error) {
|
||||
func(std adlin.Student, _ []byte) (interface{}, error) {
|
||||
return std, nil
|
||||
})))
|
||||
router.PUT("/api/students/:sid/", remoteValidatorHandler(apiHandler(studentHandler(updateStudent))))
|
||||
router.DELETE("/api/students/:sid/", remoteValidatorHandler(apiHandler(studentHandler(
|
||||
func(std Student, _ []byte) (interface{}, error) {
|
||||
func(std adlin.Student, _ []byte) (interface{}, error) {
|
||||
return std.Delete()
|
||||
}))))
|
||||
router.GET("/api/students/:sid/progress", apiHandler(studentHandler(
|
||||
func(std Student, _ []byte) (interface{}, error) {
|
||||
ret := map[string]UnlockedChallenge{}
|
||||
func(std adlin.Student, _ []byte) (interface{}, error) {
|
||||
ret := map[string]adlin.UnlockedChallenge{}
|
||||
|
||||
if sts, err := std.getStates(); err == nil {
|
||||
if sts, err := std.GetStates(); err == nil {
|
||||
for _, s := range sts {
|
||||
ret[fmt.Sprintf("%d", s.Challenge)] = s
|
||||
}
|
||||
@ -69,102 +68,12 @@ func init() {
|
||||
})))
|
||||
}
|
||||
|
||||
type Student struct {
|
||||
Id int64 `json:"id"`
|
||||
Login string `json:"login"`
|
||||
Time *time.Time `json:"time"`
|
||||
IP *string `json:"ip"`
|
||||
MAC *string `json:"mac"`
|
||||
}
|
||||
|
||||
type uploadedStudent struct {
|
||||
Login string `json:"login"`
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
func createStudent(_ httprouter.Params, body []byte) (interface{}, error) {
|
||||
var err error
|
||||
var std uploadedStudent
|
||||
@ -172,22 +81,22 @@ func createStudent(_ httprouter.Params, body []byte) (interface{}, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var exist Student
|
||||
if exist, err = getStudentByLogin(strings.TrimSpace(std.Login)); err != nil {
|
||||
if exist, err = NewStudent(strings.TrimSpace(std.Login)); err != nil {
|
||||
var exist adlin.Student
|
||||
if exist, err = adlin.GetStudentByLogin(strings.TrimSpace(std.Login)); err != nil {
|
||||
if exist, err = adlin.NewStudent(strings.TrimSpace(std.Login)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
exist.registerAccess(std.IP, std.MAC)
|
||||
exist.RegisterAccess(std.IP, std.MAC)
|
||||
|
||||
ip := fmt.Sprintf("172.23.0.%d", exist.IPSuffix())
|
||||
ip := fmt.Sprintf("172.23.0.%d", IPSuffix(exist))
|
||||
exist.IP = &ip
|
||||
|
||||
return exist, nil
|
||||
}
|
||||
|
||||
func updateStudent(current Student, body []byte) (interface{}, error) {
|
||||
var new Student
|
||||
func updateStudent(current adlin.Student, body []byte) (interface{}, error) {
|
||||
var new adlin.Student
|
||||
if err := json.Unmarshal(body, &new); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -196,83 +105,3 @@ func updateStudent(current Student, body []byte) (interface{}, error) {
|
||||
current.Time = new.Time
|
||||
return current.Update()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha512"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
@ -12,10 +10,10 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"git.nemunai.re/lectures/adlin/libadlin"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -29,19 +27,19 @@ func init() {
|
||||
}
|
||||
})
|
||||
router.GET("/api/wg/", apiAuthHandler(showWgTunnel))
|
||||
router.GET("/api/wginfo", apiAuthHandler(func (student Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
router.GET("/api/wginfo", apiAuthHandler(func(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
return getTunnelInfo(student.Id), nil
|
||||
}))
|
||||
router.POST("/api/wg/", apiAuthHandler(genWgToken))
|
||||
router.POST("/api/wg/:token", getWgTunnelInfo)
|
||||
}
|
||||
|
||||
func showWgTunnel(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
func showWgTunnel(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
// Get tunnels assigned to the student
|
||||
return student.GetTunnelTokens()
|
||||
}
|
||||
|
||||
func genWgToken(student Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
func genWgToken(student adlin.Student, ps httprouter.Params, body []byte) (interface{}, error) {
|
||||
// Generate a token to access related wg info
|
||||
return student.NewTunnelToken()
|
||||
}
|
||||
@ -58,12 +56,12 @@ type TunnelInfo struct {
|
||||
func getTunnelInfo(student int64) TunnelInfo {
|
||||
srv_pubkey, _ := base64.StdEncoding.DecodeString("uSpqyYovvP4OG6wDxZ0Qkq45MfyK58PMUuPaLesY8FI=")
|
||||
return TunnelInfo{
|
||||
Status: "OK",
|
||||
Status: "OK",
|
||||
SrvPubKey: srv_pubkey,
|
||||
SrvPort: 42912,
|
||||
CltIPv6: studentIP(student),
|
||||
CltRange: 80,
|
||||
SrvGW6: "2a01:e0a:2b:2252::1",
|
||||
SrvPort: 42912,
|
||||
CltIPv6: adlin.StudentIP(student),
|
||||
CltRange: 80,
|
||||
SrvGW6: "2a01:e0a:2b:2252::1",
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,7 +90,7 @@ func getWgTunnelInfo(w http.ResponseWriter, r *http.Request, ps httprouter.Param
|
||||
return
|
||||
}
|
||||
|
||||
token, err := GetTunnelToken(tokendec[:n])
|
||||
token, err := adlin.GetTunnelToken(tokendec[:n])
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("{errmsg:%q}", err), http.StatusBadRequest)
|
||||
return
|
||||
@ -127,127 +125,8 @@ PersistentKeepalive = 5
|
||||
`, base64.StdEncoding.EncodeToString(tinfo.SrvPubKey), "82.64.31.248", tinfo.SrvPort, tinfo.CltIPv6, 64, tinfo.CltIPv6, tinfo.CltRange, tinfo.SrvGW6)))
|
||||
}
|
||||
|
||||
|
||||
type TunnelToken struct {
|
||||
token []byte
|
||||
TokenText string
|
||||
IdStudent int64
|
||||
PubKey []byte
|
||||
Time time.Time
|
||||
Dump *WGDump
|
||||
}
|
||||
|
||||
func GetTunnelToken(token []byte) (t TunnelToken, err error) {
|
||||
err = DBQueryRow("SELECT token, token_text, id_student, pubkey, time FROM student_tunnel_tokens WHERE token=? ORDER BY time DESC", token).Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time)
|
||||
if err == nil && t.PubKey != nil {
|
||||
if wgd, errr := readWgDump(); errr == nil {
|
||||
if v, ok := wgd[base64.StdEncoding.EncodeToString(t.PubKey)]; ok {
|
||||
t.Dump = &v
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func tokenFromText(token string) []byte {
|
||||
sha := sha512.Sum512([]byte(token))
|
||||
return sha[:]
|
||||
}
|
||||
|
||||
func (student Student) NewTunnelToken() (t TunnelToken, err error) {
|
||||
tok := make([]byte, 7)
|
||||
if _, err = rand.Read(tok); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
t.TokenText = strings.Replace(strings.Replace(strings.Replace(strings.Replace(strings.Replace(base64.RawStdEncoding.EncodeToString(tok), "/", ".", -1), "+", "_", -1), "O", "#", -1), "l", "$", -1), "I", ">", -1)
|
||||
t.token = tokenFromText(t.TokenText)
|
||||
t.IdStudent = student.Id
|
||||
|
||||
_, err = DBExec("INSERT INTO student_tunnel_tokens (token, token_text, id_student, time) VALUES (?, ?, ?, ?)", t.token, t.TokenText, student.Id, time.Now())
|
||||
return
|
||||
}
|
||||
|
||||
func (student Student) GetTunnelTokens() (ts []TunnelToken, err error) {
|
||||
if rows, errr := DBQuery("SELECT token, token_text, id_student, pubkey, time FROM student_tunnel_tokens WHERE id_student = ? ORDER BY time DESC", student.Id); errr != nil {
|
||||
return nil, errr
|
||||
} else if wgd, errr := readWgDump(); errr != nil {
|
||||
return nil, errr
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var t TunnelToken
|
||||
if err = rows.Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time); err != nil {
|
||||
return
|
||||
}
|
||||
if t.PubKey != nil {
|
||||
if v, ok := wgd[base64.StdEncoding.EncodeToString(t.PubKey)]; ok {
|
||||
t.Dump = &v
|
||||
}
|
||||
}
|
||||
ts = append(ts, t)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (student Student) GetTunnelToken(token []byte) (t TunnelToken, err error) {
|
||||
err = DBQueryRow("SELECT token, token_text, id_student, pubkey, time FROM student_tunnel_tokens WHERE token = ? AND id_student = ? ORDER BY time DESC", token, student.Id).Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time)
|
||||
if err == nil && t.PubKey != nil {
|
||||
if wgd, errr := readWgDump(); errr == nil {
|
||||
if v, ok := wgd[base64.StdEncoding.EncodeToString(t.PubKey)]; ok {
|
||||
t.Dump = &v
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *TunnelToken) Update() (int64, error) {
|
||||
newtoken := tokenFromText(t.TokenText)
|
||||
tm := time.Now()
|
||||
|
||||
if res, err := DBExec("UPDATE student_tunnel_tokens SET token = ?, token_text = ?, id_student = ?, pubkey = ?, time = ? WHERE token = ?", newtoken, t.TokenText, t.IdStudent, t.PubKey, tm, t.token); err != nil {
|
||||
return 0, err
|
||||
} else if nb, err := res.RowsAffected(); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
t.token = newtoken
|
||||
t.Time = tm
|
||||
return nb, err
|
||||
}
|
||||
}
|
||||
|
||||
func GetStudentsTunnels() (ts []TunnelToken, err error) {
|
||||
if rows, errr := DBQuery("SELECT T.token, T.token_text, T.id_student, T.pubkey, T.time FROM student_tunnel_tokens T INNER JOIN (SELECT B.id_student, MAX(B.time) AS time FROM student_tunnel_tokens B WHERE B.pubkey IS NOT NULL GROUP BY id_student) L ON T.id_student = L.id_student AND T.time = L.time"); errr != nil {
|
||||
return nil, errr
|
||||
} else {
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var t TunnelToken
|
||||
if err = rows.Scan(&t.token, &t.TokenText, &t.IdStudent, &t.PubKey, &t.Time); err != nil {
|
||||
return
|
||||
}
|
||||
ts = append(ts, t)
|
||||
}
|
||||
|
||||
err = rows.Err()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func studentIP(idstd int64) net.IP {
|
||||
return net.ParseIP(fmt.Sprintf("2a01:e0a:2b:2252:%x::", idstd))
|
||||
}
|
||||
|
||||
func GenWGConfig(w io.Writer) (error) {
|
||||
ts, err := GetStudentsTunnels()
|
||||
func GenWGConfig(w io.Writer) error {
|
||||
ts, err := adlin.GetStudentsTunnels()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -261,46 +140,13 @@ func GenWGConfig(w io.Writer) (error) {
|
||||
#IdStudent = %d
|
||||
PublicKey = %s
|
||||
AllowedIPs = %s/%d
|
||||
`, t.IdStudent, base64.StdEncoding.EncodeToString(t.PubKey), studentIP(t.IdStudent), 80)))
|
||||
`, t.IdStudent, base64.StdEncoding.EncodeToString(t.PubKey), adlin.StudentIP(t.IdStudent), 80)))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
func syncWgConf() (err error) {
|
||||
_, err = exec.Command("sh", "/root/wg-sync.sh").Output()
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
type WGDump struct {
|
||||
PubKey string
|
||||
PSK string
|
||||
Endpoint string
|
||||
AllowedIPs string
|
||||
LastHandS string
|
||||
RX string
|
||||
TX string
|
||||
KeepAlive string
|
||||
}
|
||||
|
||||
func readWgDump() (wgd map[string]WGDump, err error) {
|
||||
out, errr := exec.Command("wg", "show", "wg-adlin", "dump").Output()
|
||||
|
||||
if errr != nil {
|
||||
return nil, errr
|
||||
}
|
||||
|
||||
wgd = map[string]WGDump{}
|
||||
for _, line := range strings.Split(string(out), "\n") {
|
||||
cols := strings.Fields(line)
|
||||
if len(cols) != 8 {
|
||||
continue
|
||||
}
|
||||
|
||||
wgd[cols[0]] = WGDump{cols[0], cols[1], cols[2], cols[3], cols[4], cols[5], cols[6], cols[7]}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
Reference in New Issue
Block a user