admin/api: use gorilla/mux instead of Go router

This commit is contained in:
nemunaire 2016-12-08 09:12:18 +01:00 committed by Pierre-Olivier Mercier
parent 0e30259b7e
commit 307c253d7a
18 changed files with 643 additions and 720 deletions

View file

@ -1,102 +0,0 @@
package main
import (
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
"strings"
)
type DispatchFunction func([]string, []byte) (interface{}, error)
var apiRoutes = map[string]*(map[string]DispatchFunction){
"version": &ApiVersionRouting,
"ca": &ApiCARouting,
"events": &ApiEventsRouting,
"exercices": &ApiExercicesRouting,
"themes": &ApiThemesRouting,
"teams": &ApiTeamsRouting,
}
type apiRouting struct{}
func ApiHandler() http.Handler {
return apiRouting{}
}
func (a apiRouting) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.Printf("Handling %s request from %s: %s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, r.UserAgent())
// Extract URL arguments
var sURL = strings.Split(r.URL.Path, "/")[1:]
if len(sURL) > 1 && sURL[len(sURL)-1] == "" {
// Remove trailing /
sURL = sURL[:len(sURL)-1]
}
w.Header().Set("Content-Type", "application/json")
var ret interface{}
var err error = nil
// Read the body
if r.ContentLength < 0 || r.ContentLength > 6553600 {
http.Error(w, fmt.Sprintf("{errmsg:\"Request too large or request size unknown\"}", err), http.StatusRequestEntityTooLarge)
return
}
var body []byte
if r.ContentLength > 0 {
tmp := make([]byte, 1024)
for {
n, err := r.Body.Read(tmp)
for j := 0; j < n; j++ {
body = append(body, tmp[j])
}
if err != nil || n <= 0 {
break
}
}
}
// Route request
if len(sURL) > 0 {
if h, ok := apiRoutes[sURL[0]]; ok {
if f, ok := (*h)[r.Method]; ok {
ret, err = f(sURL[1:], body)
} else {
err = errors.New(fmt.Sprintf("Invalid action (%s) provided for %s.", r.Method, sURL[0]))
}
}
} else {
err = errors.New("No action provided.")
}
// Format response
resStatus := http.StatusOK
if err != nil {
ret = map[string]string{"errmsg": err.Error()}
resStatus = http.StatusBadRequest
log.Println(r.RemoteAddr, resStatus, err.Error())
}
if ret == nil {
ret = map[string]string{"errmsg": "Page not found"}
resStatus = http.StatusNotFound
}
if str, found := ret.(string); found {
w.WriteHeader(resStatus)
io.WriteString(w, str)
} else if bts, found := ret.([]byte); found {
w.WriteHeader(resStatus)
w.Write(bts)
} else if j, err := json.Marshal(ret); err != nil {
http.Error(w, fmt.Sprintf("{\"errmsg\":\"%q\"}", err), http.StatusInternalServerError)
} else {
w.WriteHeader(resStatus)
w.Write(j)
}
}

31
admin/api/certificate.go Normal file
View file

@ -0,0 +1,31 @@
package api
import (
"io/ioutil"
"os"
"srs.epita.fr/fic-server/libfic"
)
func init() {
router.Path("/ca").Methods("GET").HandlerFunc(apiHandler(genCA))
rt := router.PathPrefix("/teams/{tid}/certificate").Subrouter()
rt.Path("/").Methods("GET").HandlerFunc(apiHandler(teamHandler(GetTeamCertificate)))
rt.Path("/generate").Methods("GET").HandlerFunc(apiHandler(teamHandler(
func(team fic.Team, args map[string]string, body []byte) (interface{}, error) { return team.GenerateCert(), nil })))
rt.Path("/revoke").Methods("GET").HandlerFunc(apiHandler(teamHandler(
func(team fic.Team, args map[string]string, body []byte) (interface{}, error) { return team.RevokeCert(), nil })))
}
func genCA(args map[string]string, body []byte) (interface{}, error) {
return fic.GenerateCA(), nil
}
func GetTeamCertificate(team fic.Team, args map[string]string, body []byte) (interface{}, error) {
if fd, err := os.Open("../PKI/pkcs/" + team.Name + ".p12"); err == nil {
return ioutil.ReadAll(fd)
} else {
return nil, err
}
}

17
admin/api/events.go Normal file
View file

@ -0,0 +1,17 @@
package api
import (
"srs.epita.fr/fic-server/libfic"
)
func init() {
router.Path("/events/").Methods("GET").HandlerFunc(apiHandler(getEvents))
}
func getEvents(args map[string]string, body []byte) (interface{}, error) {
if evts, err := fic.GetEvents(); err != nil {
return nil, err
} else {
return evts, nil
}
}

117
admin/api/exercice.go Normal file
View file

@ -0,0 +1,117 @@
package api
import (
"encoding/json"
"errors"
"strconv"
"srs.epita.fr/fic-server/libfic"
)
func init() {
router.Path("/exercices/").Methods("GET").HandlerFunc(apiHandler(listExercices))
re := router.Path("/exercices/{eid:[0-9]+}").Subrouter()
re.Methods("GET").HandlerFunc(apiHandler(exerciceHandler(showExercice)))
re.Methods("PUT").HandlerFunc(apiHandler(updateExercice))
re.Methods("DELETE").HandlerFunc(apiHandler(deleteExercice))
}
func listExercices(args map[string]string, body []byte) (interface{}, error) {
// List all exercices
return fic.GetExercices()
}
func showExercice(exercice fic.Exercice, args map[string]string, body []byte) (interface{}, error) {
return exercice, nil
}
func deleteExercice(args map[string]string, body []byte) (interface{}, error) {
if eid, err := strconv.Atoi(args["eid"]); err != nil {
return nil, err
} else if exercice, err := fic.GetExercice(int64(eid)); err != nil {
return nil, err
} else {
return exercice.Delete()
}
}
type uploadedExercice struct {
Title string
Statement string
Depend *int64
Gain int
VideoURI string
}
func updateExercice(args map[string]string, body []byte) (interface{}, error) {
if eid, err := strconv.Atoi(args["eid"]); err != nil {
return nil, err
} else if exercice, err := fic.GetExercice(int64(eid)); err != nil {
return nil, err
} else {
// Update an exercice
var ue uploadedExercice
if err := json.Unmarshal(body, &ue); err != nil {
return nil, err
}
if len(ue.Title) == 0 {
return nil, errors.New("Exercice's title not filled")
}
if ue.Depend != nil {
if _, err := fic.GetExercice(*ue.Depend); err != nil {
return nil, err
}
}
exercice.Title = ue.Title
exercice.Statement = ue.Statement
exercice.Depend = ue.Depend
exercice.Gain = int64(ue.Gain)
exercice.VideoURI = ue.VideoURI
return exercice.Update()
}
}
func createExercice(theme fic.Theme, args map[string]string, body []byte) (interface{}, error) {
// Create a new exercice
var ue uploadedExercice
if err := json.Unmarshal(body, &ue); err != nil {
return nil, err
}
if len(ue.Title) == 0 {
return nil, errors.New("Title not filled")
}
var depend *fic.Exercice = nil
if ue.Depend != nil {
if d, err := fic.GetExercice(*ue.Depend); err != nil {
return nil, err
} else {
depend = &d
}
}
return theme.AddExercice(ue.Title, ue.Statement, depend, ue.Gain, ue.VideoURI)
}
type uploadedKey struct {
Name string
Key string
}
func createExerciceKey(theme fic.Theme, exercice fic.Exercice, args map[string]string, body []byte) (interface{}, error) {
var uk uploadedKey
if err := json.Unmarshal(body, &uk); err != nil {
return nil, err
}
if len(uk.Key) == 0 {
return nil, errors.New("Key not filled")
}
return exercice.AddRawKey(uk.Name, uk.Key)
}

View file

@ -1,4 +1,4 @@
package main package api
import ( import (
"bufio" "bufio"
@ -15,7 +15,11 @@ import (
"srs.epita.fr/fic-server/libfic" "srs.epita.fr/fic-server/libfic"
) )
func createExerciceFile(theme fic.Theme, exercice fic.Exercice, args []string, body []byte) (interface{}, error) { var CloudDAVBase string
var CloudUsername string
var CloudPassword string
func createExerciceFile(theme fic.Theme, exercice fic.Exercice, args map[string]string, body []byte) (interface{}, error) {
var uf map[string]string var uf map[string]string
if err := json.Unmarshal(body, &uf); err != nil { if err := json.Unmarshal(body, &uf); err != nil {
return nil, err return nil, err

131
admin/api/handlers.go Normal file
View file

@ -0,0 +1,131 @@
package api
import (
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"strconv"
"srs.epita.fr/fic-server/libfic"
"github.com/gorilla/mux"
)
type DispatchFunction func([]string, []byte) (interface{}, error)
func apiHandler(f func (map[string]string,[]byte) (interface{}, error)) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
log.Printf("Handling %s request from %s: %s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, r.UserAgent())
w.Header().Set("Content-Type", "application/json")
var ret interface{}
var err error = nil
// Read the body
if r.ContentLength < 0 || r.ContentLength > 6553600 {
http.Error(w, fmt.Sprintf("{errmsg:\"Request too large or request size unknown\"}", err), http.StatusRequestEntityTooLarge)
return
}
var body []byte
if r.ContentLength > 0 {
tmp := make([]byte, 1024)
for {
n, err := r.Body.Read(tmp)
for j := 0; j < n; j++ {
body = append(body, tmp[j])
}
if err != nil || n <= 0 {
break
}
}
}
ret, err = f(mux.Vars(r), body)
// Format response
resStatus := http.StatusOK
if err != nil {
ret = map[string]string{"errmsg": err.Error()}
resStatus = http.StatusBadRequest
log.Println(r.RemoteAddr, resStatus, err.Error())
}
if ret == nil {
ret = map[string]string{"errmsg": "Page not found"}
resStatus = http.StatusNotFound
}
if str, found := ret.(string); found {
w.WriteHeader(resStatus)
io.WriteString(w, str)
} else if bts, found := ret.([]byte); found {
w.WriteHeader(resStatus)
w.Write(bts)
} else if j, err := json.Marshal(ret); err != nil {
http.Error(w, fmt.Sprintf("{\"errmsg\":\"%q\"}", err), http.StatusInternalServerError)
} else {
w.WriteHeader(resStatus)
w.Write(j)
}
}
}
func teamHandler(f func(fic.Team,map[string]string,[]byte) (interface{}, error)) func (map[string]string,[]byte) (interface{}, error) {
return func (args map[string]string, body []byte) (interface{}, error) {
if tid, err := strconv.Atoi(string(args["tid"])); err != nil {
if team, err := fic.GetTeamByInitialName(args["tid"]); err != nil {
return nil, err
} else {
return f(team, args, body)
}
} else if team, err := fic.GetTeam(tid); err != nil {
return nil, err
} else {
return f(team, args, body)
}
}
}
func themeHandler(f func(fic.Theme,map[string]string,[]byte) (interface{}, error)) func (map[string]string,[]byte) (interface{}, error) {
return func (args map[string]string, body []byte) (interface{}, error) {
if tid, err := strconv.Atoi(string(args["tid"])); err != nil {
return nil, err
} else if theme, err := fic.GetTheme(tid); err != nil {
return nil, err
} else {
return f(theme, args, body)
}
}
}
func exerciceHandler(f func(fic.Exercice,map[string]string,[]byte) (interface{}, error)) func (map[string]string,[]byte) (interface{}, error) {
return func (args map[string]string, body []byte) (interface{}, error) {
if eid, err := strconv.Atoi(string(args["eid"])); err != nil {
return nil, err
} else if exercice, err := fic.GetExercice(int64(eid)); err != nil {
return nil, err
} else {
return f(exercice, args, body)
}
}
}
func themedExerciceHandler(f func(fic.Theme,fic.Exercice,map[string]string,[]byte) (interface{}, error)) func (fic.Theme,map[string]string,[]byte) (interface{}, error) {
return func (theme fic.Theme, args map[string]string, body []byte) (interface{}, error) {
if eid, err := strconv.Atoi(string(args["eid"])); err != nil {
return nil, err
} else if exercice, err := fic.GetExercice(int64(eid)); err != nil {
return nil, err
} else {
return f(theme, exercice, args, body)
}
}
}
func notFound(args map[string]string, body []byte) (interface{}, error) {
return nil, nil
}

14
admin/api/router.go Normal file
View file

@ -0,0 +1,14 @@
package api
import (
"github.com/gorilla/mux"
)
var api_router = mux.NewRouter().StrictSlash(true)
var router = api_router.PathPrefix("/api/").Subrouter()
func Router() *mux.Router {
return api_router
}

View file

@ -1,4 +1,4 @@
package main package api
import ( import (
"fmt" "fmt"

156
admin/api/team.go Normal file
View file

@ -0,0 +1,156 @@
package api
import (
"encoding/json"
"fmt"
"strings"
"srs.epita.fr/fic-server/libfic"
)
func init() {
rts := router.PathPrefix("/teams").Subrouter()
router.Path("/teams.json").Methods("GET").HandlerFunc(apiHandler(
func(map[string]string,[]byte) (interface{}, error) {
return fic.ExportTeams() }))
rts.Path("/").Methods("GET").HandlerFunc(apiHandler(
func(map[string]string,[]byte) (interface{}, error) {
return fic.GetTeams() }))
rts.Path("/binding").Methods("GET").HandlerFunc(apiHandler(
func(map[string]string,[]byte) (interface{}, error) {
return bindingTeams() }))
rts.Path("/nginx").Methods("GET").HandlerFunc(apiHandler(
func(map[string]string,[]byte) (interface{}, error) {
return nginxGenTeam() }))
rts.Path("/nginx-members").Methods("GET").HandlerFunc(apiHandler(
func(map[string]string,[]byte) (interface{}, error) {
return nginxGenMember() }))
rts.Path("/binding").Methods("GET").HandlerFunc(apiHandler(
func(map[string]string,[]byte) (interface{}, error) {
return fic.GetTries(nil, nil) }))
rts.Path("/0/my.json").Methods("GET").HandlerFunc(apiHandler(
func(map[string]string,[]byte) (interface{}, error) {
return fic.MyJSONTeam(nil, true) }))
rts.Path("/0/wait.json").Methods("GET").HandlerFunc(apiHandler(
func(map[string]string,[]byte) (interface{}, error) {
return fic.MyJSONTeam(nil, false) }))
rts.Path("/0/stats.json").Methods("GET").HandlerFunc(apiHandler(
func(map[string]string,[]byte) (interface{}, error) {
return fic.GetTeamsStats(nil) }))
rts.Path("/0/tries").Methods("GET").HandlerFunc(apiHandler(
func(map[string]string,[]byte) (interface{}, error) {
return fic.GetTries(nil, nil) }))
rt := rts.PathPrefix("/{tid}").Subrouter()
rt.Path("/").Methods("GET").HandlerFunc(apiHandler(teamHandler(
func(team fic.Team, args map[string]string, body []byte) (interface{}, error) {
return team, nil })))
rt.Path("/").Methods("DELETE").HandlerFunc(apiHandler(teamHandler(
func(team fic.Team, args map[string]string, body []byte) (interface{}, error) {
return team.Delete() })))
rt.Path("/my.json").Methods("GET").HandlerFunc(apiHandler(teamHandler(
func(team fic.Team, args map[string]string, body []byte) (interface{}, error) {
return fic.MyJSONTeam(&team, true) })))
rt.Path("/wait.json").Methods("GET").HandlerFunc(apiHandler(teamHandler(
func(team fic.Team, args map[string]string, body []byte) (interface{}, error) {
return fic.MyJSONTeam(&team, false) })))
rt.Path("/stats.json").Methods("GET").HandlerFunc(apiHandler(teamHandler(
func(team fic.Team, args map[string]string, body []byte) (interface{}, error) {
return team.GetStats() })))
rt.Path("/tries").Methods("GET").HandlerFunc(apiHandler(teamHandler(
func(team fic.Team, args map[string]string, body []byte) (interface{}, error) {
return fic.GetTries(&team, nil) })))
rt.Path("/members").Methods("GET").HandlerFunc(apiHandler(teamHandler(
func(team fic.Team, args map[string]string, body []byte) (interface{}, error) {
return team.GetMembers() })))
rt.Path("/name").Methods("GET").HandlerFunc(apiHandler(teamHandler(
func(team fic.Team, args map[string]string, body []byte) (interface{}, error) {
return team.Name, nil })))
}
func nginxGenMember() (string, error) {
if teams, err := fic.GetTeams(); err != nil {
return "", err
} else {
ret := ""
for _, team := range teams {
if members, err := team.GetMembers(); err == nil {
for _, member := range members {
ret += fmt.Sprintf(" if ($remote_user = \"%s\") { set $team \"%s\"; }\n", member.Nickname, team.InitialName)
}
} else {
return "", err
}
}
return ret, nil
}
}
func nginxGenTeam() (string, error) {
if teams, err := fic.GetTeams(); err != nil {
return "", err
} else {
ret := ""
for _, team := range teams {
ret += fmt.Sprintf(" if ($ssl_client_s_dn ~ \"/C=FR/ST=France/O=Epita/OU=SRS/CN=%s\") { set $team \"%s\"; }\n", team.InitialName, team.InitialName)
}
return ret, nil
}
}
func bindingTeams() (string, error) {
if teams, err := fic.GetTeams(); err != nil {
return "", err
} else {
ret := ""
for _, team := range teams {
if members, err := team.GetMembers(); err != nil {
return "", err
} else {
var mbs []string
for _, member := range members {
mbs = append(mbs, fmt.Sprintf("%s %s", member.Firstname, member.Lastname))
}
ret += fmt.Sprintf("%d;%s;%s\n", team.Id, team.Name, strings.Join(mbs, ";"))
}
}
return ret, nil
}
}
type uploadedTeam struct {
Name string
Color uint32
}
type uploadedMember struct {
Firstname string
Lastname string
Nickname string
Company string
}
func createTeam(args map[string]string, body []byte) (interface{}, error) {
var ut uploadedTeam
if err := json.Unmarshal(body, &ut); err != nil {
return nil, err
}
return fic.CreateTeam(ut.Name, ut.Color)
}
func addTeamMember(team fic.Team, args map[string]string, body []byte) (interface{}, error) {
var members []uploadedMember
if err := json.Unmarshal(body, &members); err != nil {
return nil, err
}
for _, member := range members {
team.AddMember(member.Firstname, member.Lastname, member.Nickname, member.Company)
}
return team.GetMembers()
}

153
admin/api/theme.go Normal file
View file

@ -0,0 +1,153 @@
package api
import (
"encoding/json"
"errors"
"fmt"
"strconv"
"srs.epita.fr/fic-server/libfic"
)
func init() {
router.Path("/themes/").Methods("GET").HandlerFunc(apiHandler(listThemes))
router.Path("/themes/").Methods("POST").HandlerFunc(apiHandler(createTheme))
router.Path("/themes.json").Methods("GET").HandlerFunc(apiHandler(exportThemes))
router.Path("/themes/files-bindings").Methods("GET").HandlerFunc(apiHandler(bindingFiles))
rt := router.PathPrefix("/themes/{tid:[0-9]+}").Subrouter()
rt.Path("/").Methods("GET").HandlerFunc(apiHandler(themeHandler(showTheme)))
rt.Path("/").Methods("PUT").HandlerFunc(apiHandler(themeHandler(updateTheme)))
rt.Path("/").Methods("DELETE").HandlerFunc(apiHandler(themeHandler(deleteTheme)))
rtes := rt.PathPrefix("/exercices").Subrouter()
rtes.Path("/").Methods("GET").HandlerFunc(apiHandler(themeHandler(listThemedExercices)))
rtes.Path("/").Methods("POST").HandlerFunc(apiHandler(themeHandler(createExercice)))
rte := rtes.PathPrefix("/{eid:[0-9]+}").Subrouter()
rte.Path("/").Methods("GET").HandlerFunc(apiHandler(themeHandler(themedExerciceHandler(showThemedExercice))))
rte.Path("/").Methods("PUT").HandlerFunc(apiHandler(themeHandler(themedExerciceHandler(updateThemedExercice))))
rte.Path("/").Methods("DELETE").HandlerFunc(apiHandler(themeHandler(themedExerciceHandler(deleteThemedExercice))))
rtef := rte.Path("/files").Subrouter()
rtef.Methods("GET").HandlerFunc(apiHandler(themeHandler(themedExerciceHandler(listThemedExerciceFiles))))
rtef.Methods("POST").HandlerFunc(apiHandler(themeHandler(themedExerciceHandler(createExerciceFile))))
rtek := rte.Path("/keys").Subrouter()
rtek.Methods("GET").HandlerFunc(apiHandler(themeHandler(themedExerciceHandler(listThemedExerciceKeys))))
rtek.Methods("POST").HandlerFunc(apiHandler(themeHandler(themedExerciceHandler(createExerciceKey))))
}
func bindingFiles(args map[string]string, body []byte) (interface{}, error) {
if files, err := fic.GetFiles(); err != nil {
return "", err
} else {
ret := ""
for _, file := range files {
ret += fmt.Sprintf("%s;%s\n", file.GetOrigin(), file.Path)
}
return ret, nil
}
}
func getExercice(args []string) (fic.Exercice, error) {
if tid, err := strconv.Atoi(string(args[0])); err != nil {
return fic.Exercice{}, err
} else if theme, err := fic.GetTheme(tid); err != nil {
return fic.Exercice{}, err
} else if eid, err := strconv.Atoi(string(args[1])); err != nil {
return fic.Exercice{}, err
} else {
return theme.GetExercice(eid)
}
}
func listThemes(args map[string]string, body []byte) (interface{}, error) {
return fic.GetThemes()
}
func exportThemes(args map[string]string, body []byte) (interface{}, error) {
return fic.ExportThemes()
}
func showTheme(theme fic.Theme, args map[string]string, body []byte) (interface{}, error) {
return theme, nil
}
func listThemedExercices(theme fic.Theme, args map[string]string, body []byte) (interface{}, error) {
return theme.GetExercices()
}
func showThemedExercice(theme fic.Theme, exercice fic.Exercice, args map[string]string, body []byte) (interface{}, error) {
return exercice, nil
}
func listThemedExerciceFiles(theme fic.Theme, exercice fic.Exercice, args map[string]string, body []byte) (interface{}, error) {
return exercice.GetFiles()
}
func listThemedExerciceKeys(theme fic.Theme, exercice fic.Exercice, args map[string]string, body []byte) (interface{}, error) {
return exercice.GetKeys()
}
type uploadedTheme struct {
Name string
Authors string
}
func createTheme(args map[string]string, body []byte) (interface{}, error) {
var ut uploadedTheme
if err := json.Unmarshal(body, &ut); err != nil {
return nil, err
}
if len(ut.Name) == 0 {
return nil, errors.New("Theme's name not filled")
}
return fic.CreateTheme(ut.Name, ut.Authors)
}
func updateTheme(theme fic.Theme, args map[string]string, body []byte) (interface{}, error) {
var ut fic.Theme
if err := json.Unmarshal(body, &ut); err != nil {
return nil, err
}
ut.Id = theme.Id
if len(ut.Name) == 0 {
return nil, errors.New("Theme's name not filled")
}
return ut.Update()
}
func updateThemedExercice(theme fic.Theme, exercice fic.Exercice, args map[string]string, body []byte) (interface{}, error) {
// Update an exercice
var ue fic.Exercice
if err := json.Unmarshal(body, &ue); err != nil {
return nil, err
}
ue.Id = exercice.Id
if len(ue.Title) == 0 {
return nil, errors.New("Exercice's title not filled")
}
if _, err := ue.Update(); err != nil {
return nil, err
}
return ue, nil
}
func deleteTheme(theme fic.Theme, args map[string]string, body []byte) (interface{}, error) {
return theme.Delete()
}
func deleteThemedExercice(theme fic.Theme, exercice fic.Exercice, args map[string]string, body []byte) (interface{}, error) {
return exercice.Delete()
}

11
admin/api/version.go Normal file
View file

@ -0,0 +1,11 @@
package api
import ()
func init() {
router.Path("/version").Methods("GET").HandlerFunc(apiHandler(showVersion))
}
func showVersion(args map[string]string, body []byte) (interface{}, error) {
return map[string]interface{}{"version": 0.1}, nil
}

View file

@ -1,32 +0,0 @@
package main
import (
"io/ioutil"
"os"
"srs.epita.fr/fic-server/libfic"
)
func CertificateAPI(team fic.Team, args []string) (interface{}, error) {
if len(args) == 1 {
if args[0] == "generate" {
return team.GenerateCert(), nil
} else if args[0] == "revoke" {
return team.RevokeCert(), nil
} else {
return nil, nil
}
} else if fd, err := os.Open("../PKI/pkcs/" + team.Name + ".p12"); err == nil {
return ioutil.ReadAll(fd)
} else {
return nil, err
}
}
var ApiCARouting = map[string]DispatchFunction{
"GET": genCA,
}
func genCA(args []string, body []byte) (interface{}, error) {
return fic.GenerateCA(), nil
}

View file

@ -1,17 +0,0 @@
package main
import (
"srs.epita.fr/fic-server/libfic"
)
var ApiEventsRouting = map[string]DispatchFunction{
"GET": getEvents,
}
func getEvents(args []string, body []byte) (interface{}, error) {
if evts, err := fic.GetEvents(); err != nil {
return nil, err
} else {
return evts, nil
}
}

View file

@ -1,144 +0,0 @@
package main
import (
"encoding/json"
"errors"
"strconv"
"srs.epita.fr/fic-server/libfic"
)
var ApiExercicesRouting = map[string]DispatchFunction{
"GET": listExercice,
"PATCH": updateExercice,
"DELETE": deletionExercice,
}
func listExercice(args []string, body []byte) (interface{}, error) {
if len(args) == 1 {
if eid, err := strconv.Atoi(string(args[0])); err != nil {
return nil, err
} else {
return fic.GetExercice(int64(eid))
}
} else {
// List all exercices
return fic.GetExercices()
}
}
func deletionExercice(args []string, body []byte) (interface{}, error) {
if len(args) == 1 {
if eid, err := strconv.Atoi(string(args[0])); err != nil {
return nil, err
} else if exercice, err := fic.GetExercice(int64(eid)); err != nil {
return nil, err
} else {
return exercice.Delete()
}
} else {
return nil, nil
}
}
type uploadedExercice struct {
Title string
Statement string
Hint string
Depend *int64
Gain int
VideoURI string
}
func updateExercice(args []string, body []byte) (interface{}, error) {
if len(args) == 1 {
if eid, err := strconv.Atoi(string(args[0])); err != nil {
return nil, err
} else if exercice, err := fic.GetExercice(int64(eid)); err != nil {
return nil, err
} else {
// Update an exercice
var ue uploadedExercice
if err := json.Unmarshal(body, &ue); err != nil {
return nil, err
}
if len(ue.Title) == 0 {
return nil, errors.New("Exercice's title not filled")
}
if ue.Depend != nil {
if _, err := fic.GetExercice(*ue.Depend); err != nil {
return nil, err
}
}
exercice.Title = ue.Title
exercice.Statement = ue.Statement
exercice.Hint = ue.Hint
exercice.Depend = ue.Depend
exercice.Gain = int64(ue.Gain)
exercice.VideoURI = ue.VideoURI
return exercice.Update()
}
} else {
return nil, nil
}
}
func createExercice(theme fic.Theme, args []string, body []byte) (interface{}, error) {
if len(args) >= 1 {
if eid, err := strconv.Atoi(args[0]); err != nil {
return nil, err
} else if exercice, err := theme.GetExercice(eid); err != nil {
return nil, err
} else {
if args[1] == "files" {
return createExerciceFile(theme, exercice, args[2:], body)
} else if args[1] == "keys" {
return createExerciceKey(theme, exercice, args[2:], body)
}
}
return nil, nil
} else {
// Create a new exercice
var ue uploadedExercice
if err := json.Unmarshal(body, &ue); err != nil {
return nil, err
}
if len(ue.Title) == 0 {
return nil, errors.New("Title not filled")
}
var depend *fic.Exercice = nil
if ue.Depend != nil {
if d, err := fic.GetExercice(*ue.Depend); err != nil {
return nil, err
} else {
depend = &d
}
}
return theme.AddExercice(ue.Title, ue.Statement, ue.Hint, depend, ue.Gain, ue.VideoURI)
}
}
type uploadedKey struct {
Name string
Key string
}
func createExerciceKey(theme fic.Theme, exercice fic.Exercice, args []string, body []byte) (interface{}, error) {
var uk uploadedKey
if err := json.Unmarshal(body, &uk); err != nil {
return nil, err
}
if len(uk.Key) == 0 {
return nil, errors.New("Key not filled")
}
return exercice.AddRawKey(uk.Name, uk.Key)
}

View file

@ -1,231 +0,0 @@
package main
import (
"encoding/json"
"fmt"
"strconv"
"strings"
"srs.epita.fr/fic-server/libfic"
)
var ApiTeamsRouting = map[string]DispatchFunction{
"GET": listTeam,
"PUT": creationTeamMembers,
"POST": creationTeam,
"DELETE": deletionTeam,
}
func nginxGenMember() (string, error) {
if teams, err := fic.GetTeams(); err != nil {
return "", err
} else {
ret := ""
for _, team := range teams {
if members, err := team.GetMembers(); err == nil {
for _, member := range members {
ret += fmt.Sprintf(" if ($remote_user = \"%s\") { set $team \"%s\"; }\n", member.Nickname, team.InitialName)
}
} else {
return "", err
}
}
return ret, nil
}
}
func nginxGenTeam() (string, error) {
if teams, err := fic.GetTeams(); err != nil {
return "", err
} else {
ret := ""
for _, team := range teams {
ret += fmt.Sprintf(" if ($ssl_client_s_dn ~ \"/C=FR/ST=France/O=Epita/OU=SRS/CN=%s\") { set $team \"%s\"; }\n", team.InitialName, team.InitialName)
}
return ret, nil
}
}
func bindingTeams() (string, error) {
if teams, err := fic.GetTeams(); err != nil {
return "", err
} else {
ret := ""
for _, team := range teams {
if members, err := team.GetMembers(); err != nil {
return "", err
} else {
var mbs []string
for _, member := range members {
mbs = append(mbs, fmt.Sprintf("%s %s", member.Firstname, member.Lastname))
}
ret += fmt.Sprintf("%d;%s;%s\n", team.Id, team.Name, strings.Join(mbs, ";"))
}
}
return ret, nil
}
}
type uploadedTeam struct {
Name string
Color uint32
}
type uploadedMember struct {
Firstname string
Lastname string
Nickname string
Company string
}
func listTeam(args []string, body []byte) (interface{}, error) {
if len(args) >= 2 {
var team *fic.Team
if tid, err := strconv.Atoi(args[0]); err != nil {
if t, err := fic.GetTeamByInitialName(args[0]); err != nil {
return nil, err
} else {
team = &t
}
} else {
if tid == 0 {
team = nil
} else if t, err := fic.GetTeam(tid); err != nil {
return nil, err
} else {
team = &t
}
}
if args[1] == "my.json" {
return fic.MyJSONTeam(team, true)
} else if args[1] == "wait.json" {
return fic.MyJSONTeam(team, false)
} else if args[1] == "stats.json" {
if team != nil {
return team.GetStats()
} else {
return fic.GetTeamsStats(nil)
}
} else if args[1] == "tries" {
return fic.GetTries(team, nil)
} else if team != nil && args[1] == "members" {
return team.GetMembers()
} else if args[1] == "certificate" && team != nil {
return CertificateAPI(*team, args[2:])
} else if team != nil && args[1] == "name" {
return team.Name, nil
}
} else if len(args) == 1 {
if args[0] == "teams.json" {
return fic.ExportTeams()
} else if args[0] == "tries" {
return fic.GetTries(nil, nil)
} else if args[0] == "nginx" {
return nginxGenTeam()
} else if args[0] == "nginx-members" {
return nginxGenMember()
} else if args[0] == "binding" {
return bindingTeams()
} else if tid, err := strconv.Atoi(string(args[0])); err != nil {
return fic.GetTeamByInitialName(args[0])
} else if team, err := fic.GetTeam(tid); err != nil {
return nil, err
} else {
return team, nil
}
} else if len(args) == 0 {
// List all teams
return fic.GetTeams()
}
return nil, nil
}
func creationTeam(args []string, body []byte) (interface{}, error) {
if len(args) == 1 {
// List given team
if tid, err := strconv.Atoi(string(args[0])); err != nil {
return nil, err
} else if team, err := fic.GetTeam(tid); err != nil {
return nil, err
} else {
var members []uploadedMember
if err := json.Unmarshal(body, &members); err != nil {
return nil, err
}
for _, member := range members {
team.AddMember(member.Firstname, member.Lastname, member.Nickname, member.Company)
}
return team.GetMembers()
}
} else if len(args) == 0 {
// Create a new team
var ut uploadedTeam
if err := json.Unmarshal(body, &ut); err != nil {
return nil, err
}
return fic.CreateTeam(ut.Name, ut.Color)
} else {
return nil, nil
}
}
func creationTeamMembers(args []string, body []byte) (interface{}, error) {
if len(args) == 1 {
// List given team
if tid, err := strconv.Atoi(string(args[0])); err != nil {
return nil, err
} else if team, err := fic.GetTeam(tid); err != nil {
return nil, err
} else {
var member uploadedMember
if err := json.Unmarshal(body, &member); err != nil {
return nil, err
}
team.AddMember(member.Firstname, member.Lastname, member.Nickname, member.Company)
return team.GetMembers()
}
} else if len(args) == 0 {
// Create a new team
var members []uploadedMember
if err := json.Unmarshal(body, &members); err != nil {
return nil, err
}
if team, err := fic.CreateTeam("", 0); err != nil {
return nil, err
} else {
for _, member := range members {
if _, err := team.AddMember(member.Firstname, member.Lastname, member.Nickname, member.Company); err != nil {
return nil, err
}
}
return team, nil
}
} else {
return nil, nil
}
}
func deletionTeam(args []string, body []byte) (interface{}, error) {
if len(args) == 1 {
if tid, err := strconv.Atoi(string(args[0])); err != nil {
return nil, err
} else if team, err := fic.GetTeam(tid); err != nil {
return nil, err
} else {
return team.Delete()
}
} else {
return nil, nil
}
}

View file

@ -1,164 +0,0 @@
package main
import (
"encoding/json"
"errors"
"fmt"
"strconv"
"srs.epita.fr/fic-server/libfic"
)
var ApiThemesRouting = map[string]DispatchFunction{
"GET": listTheme,
"PATCH": updateTheme,
"POST": creationTheme,
"DELETE": deletionTheme,
}
func bindingFiles() (string, error) {
if files, err := fic.GetFiles(); err != nil {
return "", err
} else {
ret := ""
for _, file := range files {
ret += fmt.Sprintf("%s;%s\n", file.GetOrigin(), file.Path)
}
return ret, nil
}
}
func getTheme(args []string) (fic.Theme, error) {
if tid, err := strconv.Atoi(string(args[0])); err != nil {
return fic.Theme{}, err
} else {
return fic.GetTheme(tid)
}
}
func getExercice(args []string) (fic.Exercice, error) {
if theme, err := getTheme(args); err != nil {
return fic.Exercice{}, err
} else if eid, err := strconv.Atoi(string(args[1])); err != nil {
return fic.Exercice{}, err
} else {
return theme.GetExercice(eid)
}
}
func listTheme(args []string, body []byte) (interface{}, error) {
if len(args) == 3 {
if e, err := getExercice(args); err != nil {
return nil, err
} else {
if args[2] == "files" {
return e.GetFiles()
} else if args[2] == "keys" {
return e.GetKeys()
}
}
} else if len(args) == 2 {
if args[1] == "exercices" {
if theme, err := getTheme(args); err != nil {
return nil, err
} else {
return theme.GetExercices()
}
} else {
return getExercice(args)
}
} else if len(args) == 1 {
if args[0] == "files-bindings" {
return bindingFiles()
} else if args[0] == "themes.json" {
return fic.ExportThemes()
} else {
return getTheme(args)
}
} else if len(args) == 0 {
// List all themes
return fic.GetThemes()
}
return nil, nil
}
type uploadedTheme struct {
Name string
Authors string
}
func creationTheme(args []string, body []byte) (interface{}, error) {
if len(args) >= 1 {
if theme, err := getTheme(args); err != nil {
return nil, err
} else {
return createExercice(theme, args[1:], body)
}
} else if len(args) == 0 {
// Create a new theme
var ut uploadedTheme
if err := json.Unmarshal(body, &ut); err != nil {
return nil, err
}
if len(ut.Name) == 0 {
return nil, errors.New("Theme's name not filled")
}
return fic.CreateTheme(ut.Name, ut.Authors)
} else {
return nil, nil
}
}
func updateTheme(args []string, body []byte) (interface{}, error) {
if len(args) == 2 {
// Update an exercice
var ue fic.Exercice
if err := json.Unmarshal(body, &ue); err != nil {
return nil, err
}
if len(ue.Title) == 0 {
return nil, errors.New("Exercice's title not filled")
}
if _, err := ue.Update(); err != nil {
return nil, err
}
return ue, nil
} else if len(args) == 1 {
// Update a theme
var ut fic.Theme
if err := json.Unmarshal(body, &ut); err != nil {
return nil, err
}
if len(ut.Name) == 0 {
return nil, errors.New("Theme's name not filled")
}
return ut.Update()
} else {
return nil, nil
}
}
func deletionTheme(args []string, body []byte) (interface{}, error) {
if len(args) == 2 {
if exercice, err := getExercice(args); err != nil {
return nil, err
} else {
return exercice.Delete()
}
} else if len(args) == 1 {
if theme, err := getTheme(args); err != nil {
return nil, err
} else {
return theme.Delete()
}
} else {
return nil, nil
}
}

View file

@ -1,11 +0,0 @@
package main
import ()
var ApiVersionRouting = map[string]DispatchFunction{
"GET": showVersion,
}
func showVersion(args []string, body []byte) (interface{}, error) {
return map[string]interface{}{"version": 0.1}, nil
}

View file

@ -10,29 +10,26 @@ import (
"path/filepath" "path/filepath"
"text/template" "text/template"
"srs.epita.fr/fic-server/admin/api"
"srs.epita.fr/fic-server/libfic" "srs.epita.fr/fic-server/libfic"
) )
var PKIDir string var PKIDir string
var SubmissionDir string var SubmissionDir string
var BaseURL string
var CloudDAVBase string
var CloudUsername string
var CloudPassword string
var StaticDir string var StaticDir string
func main() { func main() {
var bind = flag.String("bind", "127.0.0.1:8081", "Bind port/socket") var bind = flag.String("bind", "127.0.0.1:8081", "Bind port/socket")
var dsn = flag.String("dsn", "fic:fic@/fic", "DSN to connect to the MySQL server") var dsn = flag.String("dsn", "fic:fic@/fic", "DSN to connect to the MySQL server")
flag.StringVar(&BaseURL, "baseurl", "/", "URL prepended to each URL") var baseURL = flag.String("baseurl", "/", "URL prepended to each URL")
flag.StringVar(&SubmissionDir, "submission", "./submissions/", "Base directory where save submissions") flag.StringVar(&SubmissionDir, "submission", "./submissions/", "Base directory where save submissions")
flag.StringVar(&PKIDir, "pki", "./pki/", "Base directory where found PKI scripts") flag.StringVar(&PKIDir, "pki", "./pki/", "Base directory where found PKI scripts")
flag.StringVar(&StaticDir, "static", "./htdocs-admin/", "Directory containing static files") flag.StringVar(&StaticDir, "static", "./htdocs-admin/", "Directory containing static files")
flag.StringVar(&fic.FilesDir, "files", "./FILES/", "Base directory where found challenges files, local part") flag.StringVar(&fic.FilesDir, "files", "./FILES/", "Base directory where found challenges files, local part")
flag.StringVar(&CloudDAVBase, "clouddav", "https://srs.epita.fr/owncloud/remote.php/webdav/FIC 2016", flag.StringVar(&api.CloudDAVBase, "clouddav", "https://srs.epita.fr/owncloud/remote.php/webdav/FIC 2016",
"Base directory where found challenges files, cloud part") "Base directory where found challenges files, cloud part")
flag.StringVar(&CloudUsername, "clouduser", "fic", "Username used to sync") flag.StringVar(&api.CloudUsername, "clouduser", "fic", "Username used to sync")
flag.StringVar(&CloudPassword, "cloudpass", "", "Password used to sync") flag.StringVar(&api.CloudPassword, "cloudpass", "", "Password used to sync")
flag.Parse() flag.Parse()
log.SetPrefix("[admin] ") log.SetPrefix("[admin] ")
@ -78,15 +75,8 @@ func main() {
os.Chdir(PKIDir) os.Chdir(PKIDir)
log.Println("Registering handlers...")
mux := http.NewServeMux()
mux.Handle(path.Join(BaseURL, "api") + "/", http.StripPrefix(path.Join(BaseURL, "api"), ApiHandler()))
mux.Handle(path.Join(BaseURL, "teams") + "/", http.StripPrefix(BaseURL, StaticHandler(staticDir)))
mux.Handle(path.Join(BaseURL, "themes") + "/", http.StripPrefix(BaseURL, StaticHandler(staticDir)))
mux.Handle(BaseURL, http.StripPrefix(BaseURL, http.FileServer(http.Dir(staticDir))))
log.Println(fmt.Sprintf("Ready, listening on %s", *bind)) log.Println(fmt.Sprintf("Ready, listening on %s", *bind))
if err := http.ListenAndServe(*bind, mux); err != nil { if err := http.ListenAndServe(*bind, http.StripPrefix(*baseURL, api.Router())); err != nil {
log.Fatal("Unable to listen and serve: ", err) log.Fatal("Unable to listen and serve: ", err)
} }
} }