From 38902bee8d2d866c813d017e34fb1b6294e5d51e Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Wed, 13 Mar 2019 11:19:51 +0100 Subject: [PATCH] token-handler: handle authorization through Epita CRI LDAP --- token-validator/auth.go | 77 +++++++++++++++++++++++++++++++++++++ token-validator/db.go | 10 +++++ token-validator/handler.go | 35 ++++++++++++++++- token-validator/ip.go | 6 ++- token-validator/session.go | 58 ++++++++++++++++++++++++++++ token-validator/students.go | 20 +++++----- 6 files changed, 194 insertions(+), 12 deletions(-) create mode 100644 token-validator/auth.go create mode 100644 token-validator/session.go diff --git a/token-validator/auth.go b/token-validator/auth.go new file mode 100644 index 0000000..9b10e99 --- /dev/null +++ b/token-validator/auth.go @@ -0,0 +1,77 @@ +package main + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + + "github.com/julienschmidt/httprouter" +) + +func init() { + router.GET("/auth", authHandler(apiHandler(validateAuthToken, printStudent))) + router.POST("/auth", apiHandler(checkAuth)) +} + +func printStudent(std *Student, r *http.Request) error { + if std != nil { + return errors.New(fmt.Sprintf("%s", *std)) + } else { + return nil + } +} + +func validateAuthToken(_ httprouter.Params, _ []byte) (interface{}, error) { + return false, nil +} + +type loginForm struct { + Username string + Password string +} + +func checkAuth(_ httprouter.Params, body []byte) (interface{}, error) { + var lf loginForm + if err := json.Unmarshal(body, &lf); err != nil { + return nil, err + } + + if r, err := http.NewRequest("GET", "https://owncloud.srs.epita.fr/remote.php/webdav/", nil); err != nil { + return nil, err + } else { + r.SetBasicAuth(lf.Username, lf.Password) + + if resp, err := http.DefaultClient.Do(r); err != nil { + return nil, err + } else { + defer resp.Body.Close() + + if resp.StatusCode == http.StatusOK { + var std Student + if !studentExists(lf.Username) { + if std, err = NewStudent(lf.Username); err != nil { + return nil, err + } + } else if std, err = getStudentByLogin(lf.Username); err != nil { + return nil, err + } + + session, err := std.NewSession() + if err != nil { + return nil, err + } + + res := map[string]interface{}{} + res["status"] = "OK" + res["id_session"] = session.Id + + return res, nil + } else { + return nil, errors.New(`{"status": "Invalid username or password"}`) + } + } + + return nil, nil + } +} diff --git a/token-validator/db.go b/token-validator/db.go index b3aa178..58376a8 100644 --- a/token-validator/db.go +++ b/token-validator/db.go @@ -102,6 +102,16 @@ CREATE TABLE IF NOT EXISTS student_pong( time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(id_student) REFERENCES students(id_student) ) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin; +`); err != nil { + return err + } + if _, err := db.Exec(` +CREATE TABLE IF NOT EXISTS student_sessions( + id_session BLOB(255) NOT NULL, + id_student INTEGER NOT NULL, + time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY(id_student) REFERENCES students(id_student) +) DEFAULT CHARACTER SET = utf8 COLLATE = utf8_bin; `); err != nil { return err } diff --git a/token-validator/handler.go b/token-validator/handler.go index bcd84b1..b85285f 100644 --- a/token-validator/handler.go +++ b/token-validator/handler.go @@ -10,6 +10,7 @@ import ( "log" "net/http" "strconv" + "strings" "time" "github.com/julienschmidt/httprouter" @@ -38,6 +39,20 @@ func remoteValidatorHandler(f func(http.ResponseWriter, *http.Request, httproute } } +func authHandler(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) { + if flds := strings.Fields(r.Header.Get("Authorization")); len(flds) != 2 || flds[0] != "Bearer" { + http.Error(w, `{"errmsg": "Authorization required"}`, http.StatusUnauthorized) + } else if sessionid, err := base64.StdEncoding.DecodeString(flds[1]); err != nil { + http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err), http.StatusNotAcceptable) + } else if _, err := getSession(sessionid); err != nil { + http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err), http.StatusUnauthorized) + } else { + f(w, r, ps) + } + } +} + func rawHandler(f func(*http.Request, httprouter.Params, []byte) (interface{}, error), access ...func(*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 != "" { @@ -50,10 +65,26 @@ func rawHandler(f func(*http.Request, httprouter.Params, []byte) (interface{}, e var ret interface{} var err error = nil + // Read Authorization header + var student *Student = nil + if flds := strings.Fields(r.Header.Get("Authorization")); len(flds) == 2 && flds[0] == "Bearer" { + if sessionid, err := base64.StdEncoding.DecodeString(flds[1]); err != nil { + http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err), http.StatusNotAcceptable) + } else if session, err := getSession(sessionid); err != nil { + http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err), http.StatusUnauthorized) + return + } else if std, err := getStudent(int(session.IdStudent)); err != nil { + http.Error(w, fmt.Sprintf(`{"errmsg": %q}`, err), http.StatusUnauthorized) + return + } else { + student = &std + } + } + // Check access limitation for _, a := range access { - if err := a(nil, r); err != nil { - http.Error(w, fmt.Sprintf("{errmsg:\"You're not allowed to access this page this way!\"}", err), http.StatusForbidden) + if err := a(student, r); err != nil { + http.Error(w, fmt.Sprintf(`{"errmsg":%q}`, err), http.StatusForbidden) return } } diff --git a/token-validator/ip.go b/token-validator/ip.go index 598e376..e4f7426 100644 --- a/token-validator/ip.go +++ b/token-validator/ip.go @@ -28,7 +28,11 @@ func showIPs(_ httprouter.Params, body []byte) (interface{}, error) { r[sid] = make(map[string]string) - r[sid]["mac"] = std.MAC + if std.MAC == nil { + continue + } + + r[sid]["mac"] = *std.MAC r[sid]["vlan0"] = fmt.Sprintf("172.23.0.%d", std.IPSuffix()) r[sid]["vlan7"] = fmt.Sprintf("172.23.142.%d", std.IPSuffix()) } diff --git a/token-validator/session.go b/token-validator/session.go new file mode 100644 index 0000000..382b4b6 --- /dev/null +++ b/token-validator/session.go @@ -0,0 +1,58 @@ +package main + +import ( + "crypto/rand" + "time" +) + +type Session struct { + Id []byte `json:"id"` + IdStudent int64 `json:"login"` + Time time.Time `json:"time"` +} + +func getSession(id []byte) (s Session, err error) { + err = DBQueryRow("SELECT id_session, id_student, time FROM student_sessions WHERE id_session=?", id).Scan(&s.Id, &s.IdStudent, &s.Time) + return +} + +func (student Student) NewSession() (Session, error) { + session_id := make([]byte, 255) + if _, err := rand.Read(session_id); err != nil { + return Session{}, err + } else if _, err := DBExec("INSERT INTO student_sessions (id_session, id_student, time) VALUES (?, ?, ?)", session_id, student.Id, time.Now()); err != nil { + return Session{}, err + } else { + return Session{session_id, student.Id, time.Now()}, nil + } +} + +func (s Session) Update() (int64, error) { + if res, err := DBExec("UPDATE student_sessions SET id_student = ?, time = ? WHERE id_session = ?", s.IdStudent, s.Time, s.Id); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } +} + +func (s Session) Delete() (int64, error) { + if res, err := DBExec("DELETE FROM student_sessions WHERE id_session = ?", s.Id); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } +} + +func ClearSession() (int64, error) { + if res, err := DBExec("DELETE FROM student_sessions"); err != nil { + return 0, err + } else if nb, err := res.RowsAffected(); err != nil { + return 0, err + } else { + return nb, err + } +} diff --git a/token-validator/students.go b/token-validator/students.go index d31c13a..8ebd97c 100644 --- a/token-validator/students.go +++ b/token-validator/students.go @@ -59,11 +59,11 @@ 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"` + Id int64 `json:"id"` + Login string `json:"login"` + Time *time.Time `json:"time"` + IP *string `json:"ip"` + MAC *string `json:"mac"` } type uploadedStudent struct { @@ -106,16 +106,17 @@ func getStudentByLogin(login string) (s Student, err error) { func studentExists(login string) bool { var z int err := DBQueryRow("SELECT 1 FROM students WHERE login=?", login).Scan(&z) - return err != nil && z == 1 + return err == nil && z == 1 } func NewStudent(login string) (Student, error) { - if res, err := DBExec("INSERT INTO students (login, time) VALUES (?, ?)", login, time.Now()); err != nil { + 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, time.Now(), "", ""}, nil + return Student{sid, login, &t, nil, nil}, nil } } @@ -168,7 +169,8 @@ func createStudent(_ httprouter.Params, body []byte) (interface{}, error) { } exist.registerAccess(std.IP, std.MAC) - exist.IP = fmt.Sprintf("172.23.0.%d", exist.IPSuffix()) + ip := fmt.Sprintf("172.23.0.%d", exist.IPSuffix()) + exist.IP = &ip return exist, nil }