package api import ( "bytes" "fmt" "io/ioutil" "log" "net/http" "os" "path" "text/template" "srs.epita.fr/fic-server/admin/pki" "srs.epita.fr/fic-server/libfic" "github.com/gin-gonic/gin" ) var OidcSecret = "" func declarePasswordRoutes(router *gin.RouterGroup) { router.POST("/password", func(c *gin.Context) { passwd, err := fic.GeneratePassword() if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"password": passwd}) }) router.GET("/dex.yaml", func(c *gin.Context) { cfg, err := genDexConfig() if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) return } c.String(http.StatusOK, string(cfg)) }) router.POST("/dex.yaml", func(c *gin.Context) { if dexcfg, err := genDexConfig(); err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) return } else if err := ioutil.WriteFile(path.Join(pki.PKIDir, "shared", "dex-config.yaml"), []byte(dexcfg), 0644); err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) return } c.JSON(http.StatusOK, true) }) router.GET("/dex-password.tpl", func(c *gin.Context) { passtpl, err := genDexPasswordTpl() if err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) return } c.String(http.StatusOK, string(passtpl)) }) router.POST("/dex-password.tpl", func(c *gin.Context) { if dexcfg, err := genDexPasswordTpl(); err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) return } else if err := ioutil.WriteFile(path.Join(pki.PKIDir, "shared", "dex-password.tpl"), []byte(dexcfg), 0644); err != nil { c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": err.Error()}) return } c.JSON(http.StatusOK, true) }) } func declareTeamsPasswordRoutes(router *gin.RouterGroup) { router.GET("/password", func(c *gin.Context) { team := c.MustGet("team").(*fic.Team) if team.Password != nil { c.String(http.StatusOK, *team.Password) } else { c.AbortWithStatusJSON(http.StatusNotFound, nil) } }) router.POST("/password", func(c *gin.Context) { team := c.MustGet("team").(*fic.Team) if passwd, err := fic.GeneratePassword(); err != nil { log.Println("Unable to GeneratePassword:", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Something went wrong when generating the new team password"}) return } else { team.Password = &passwd _, err := team.Update() if err != nil { log.Println("Unable to Update Team:", err.Error()) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "Something went wrong when updating the new team password"}) return } c.JSON(http.StatusOK, team) } }) } const dexcfgtpl = `issuer: https://fic.srs.epita.fr storage: type: sqlite3 config: file: /var/dex/dex.db web: http: 0.0.0.0:5556 frontend: issuer: Challenge forensic logoURL: img/fic.png dir: /srv/dex/web/ oauth2: skipApprovalScreen: true staticClients: {{ range $c := .Clients }} - id: {{ $c.Id }} name: {{ $c.Name }} redirectURIs: [{{ range $u := $c.RedirectURIs }}'{{ $u }}'{{ end }}] secret: {{ $c.Secret }} {{ end }} enablePasswordDB: true staticPasswords: {{ range $t := .Teams }} - email: "team{{ printf "%02d" $t.Id }}" hash: "{{with $t }}{{ .HashedPassword }}{{end}}" {{ end }} ` const dexpasswdtpl = `{{ "{{" }} template "header.html" . {{ "}}" }}

Bienvenue au challenge Forensic !

{{ "{{" }} if .Invalid {{ "}}" }}
Identifiants incorrects.
{{ "{{" }} end {{ "}}" }}
{{ "{{" }} if .BackLink {{ "}}" }} {{ "{{" }} end {{ "}}" }}
{{ "{{" }} template "footer.html" . {{ "}}" }} ` type dexConfigClient struct { Id string Name string RedirectURIs []string Secret string } type dexConfig struct { Clients []dexConfigClient Teams []*fic.Team } func genDexConfig() ([]byte, error) { if teams, err := fic.GetTeams(); err != nil { return nil, err } else if OidcSecret == "" { return nil, fmt.Errorf("Unable to generate dex configuration: OIDC Secret not defined. Please define FICOIDC_SECRET in your environment.") } else { b := bytes.NewBufferString("") if challengeInfo, err := GetChallengeInfo(); err != nil { return nil, fmt.Errorf("Cannot create template: %w", err) } else if dexTmpl, err := template.New("dexcfg").Parse(dexcfgtpl); err != nil { return nil, fmt.Errorf("Cannot create template: %w", err) } else if err = dexTmpl.Execute(b, dexConfig{ Clients: []dexConfigClient{ dexConfigClient{ Id: "epita-challenge", Name: challengeInfo.Title, RedirectURIs: []string{"https://fic.srs.epita.fr/challenge_access/auth"}, Secret: OidcSecret, }, }, Teams: teams, }); err != nil { return nil, fmt.Errorf("An error occurs during template execution: %w", err) } else { // Also generate team associations for _, team := range teams { if err := os.Symlink(fmt.Sprintf("%d", team.Id), path.Join(TeamsDir, fmt.Sprintf("team%02d", team.Id))); err != nil { log.Println("Unable to create association symlink:", err.Error()) return nil, fmt.Errorf("Unable to create association symlink: %s", err.Error()) } } return b.Bytes(), nil } } } func genDexPasswordTpl() ([]byte, error) { if teams, err := fic.GetTeams(); err != nil { return nil, err } else { b := bytes.NewBufferString("") if dexTmpl, err := template.New("dexpasswd").Parse(dexpasswdtpl); err != nil { return nil, fmt.Errorf("Cannot create template: %w", err) } else if err = dexTmpl.Execute(b, dexConfig{ Teams: teams, }); err != nil { return nil, fmt.Errorf("An error occurs during template execution: %w", err) } else { return b.Bytes(), nil } } }