2021-09-09 09:20:45 +00:00
package api
import (
"bytes"
"fmt"
"io/ioutil"
2022-05-16 09:38:46 +00:00
"log"
"net/http"
2022-06-04 16:10:09 +00:00
"os"
2021-09-09 09:20:45 +00:00
"path"
"text/template"
"srs.epita.fr/fic-server/admin/pki"
"srs.epita.fr/fic-server/libfic"
2022-05-16 09:38:46 +00:00
"github.com/gin-gonic/gin"
2021-09-09 09:20:45 +00:00
)
var OidcSecret = ""
2022-05-16 09:38:46 +00:00
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 } )
} )
2022-05-31 12:54:19 +00:00
router . GET ( "/dex.yaml" , func ( c * gin . Context ) {
2022-05-16 09:38:46 +00:00
cfg , err := genDexConfig ( )
if err != nil {
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : err . Error ( ) } )
return
}
c . String ( http . StatusOK , string ( cfg ) )
} )
2022-05-31 12:54:19 +00:00
router . POST ( "/dex.yaml" , func ( c * gin . Context ) {
2022-05-16 09:38:46 +00:00
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 )
} )
2022-05-31 12:54:19 +00:00
router . GET ( "/dex-password.tpl" , func ( c * gin . Context ) {
2022-05-16 09:38:46 +00:00
passtpl , err := genDexPasswordTpl ( )
if err != nil {
c . AbortWithStatusJSON ( http . StatusInternalServerError , gin . H { "errmsg" : err . Error ( ) } )
return
}
c . String ( http . StatusOK , string ( passtpl ) )
} )
2022-05-31 12:54:19 +00:00
router . POST ( "/dex-password.tpl" , func ( c * gin . Context ) {
2022-05-16 09:38:46 +00:00
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 )
2022-05-31 12:53:26 +00:00
if team . Password != nil {
c . String ( http . StatusOK , * team . Password )
} else {
c . AbortWithStatusJSON ( http . StatusNotFound , nil )
}
2022-05-16 09:38:46 +00:00
} )
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
2022-05-31 12:53:53 +00:00
_ , err := team . Update ( )
2022-05-16 09:38:46 +00:00
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
2021-09-09 09:20:45 +00:00
}
2022-05-16 09:38:46 +00:00
2022-05-31 12:53:53 +00:00
c . JSON ( http . StatusOK , team )
2022-05-16 09:38:46 +00:00
}
} )
2021-09-09 09:20:45 +00:00
}
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 " % 02 d " $t.Id }}"
hash : "{{with $t }}{{ .HashedPassword }}{{end}}"
{ { end } }
`
const dexpasswdtpl = ` { { "{{" } } template "header.html" . { { "}}" } }
< div class = "theme-panel" >
< h2 class = "theme-heading" >
Bienvenue au challenge Forensic & nbsp ; !
< / h2 >
< form method = "post" action = "{{ " { { " }} .PostURL {{ " } } " }}" >
< div class = "theme-form-row" >
< div class = "theme-form-label" >
< label for = "userid" > Votre équipe < / label >
< / div >
< select tabindex = "1" required id = "login" name = "login" class = "theme-form-input" autofocus >
{ { range $ t := . Teams } } < option value = "team{{ printf " % 02 d " $t.Id }}" > { { $ t . Name } } < / option >
{ { end } } < / select >
< / div >
< div class = "theme-form-row" >
< div class = "theme-form-label" >
< label for = "password" > Mot de passe < / label >
< / div >
< input tabindex = "2" required id = "password" name = "password" type = "password" class = "theme-form-input" placeholder = "mot de passe" { { "{{" } } if . Invalid { { "}}" } } autofocus { { "{{" } } end { { "}}" } } / >
< / div >
{ { "{{" } } if . Invalid { { "}}" } }
< div id = "login-error" class = "dex-error-box" >
Identifiants incorrects .
< / div >
{ { "{{" } } end { { "}}" } }
< button tabindex = "3" id = "submit-login" type = "submit" class = "dex-btn theme-btn--primary" > C ' est parti & nbsp ; ! < / button >
< / form >
{ { "{{" } } if . BackLink { { "}}" } }
< div class = "theme-link-back" >
< a class = "dex-subtle-text" href = "{{ " { { " }} .BackLink {{ " } } " }}" > Sélectionner une autre méthode d ' authentification . < / a >
< / div >
{ { "{{" } } end { { "}}" } }
< / div >
{ { "{{" } } template "footer.html" . { { "}}" } }
`
type dexConfigClient struct {
Id string
Name string
RedirectURIs [ ] string
Secret string
}
type dexConfig struct {
Clients [ ] dexConfigClient
2021-11-22 14:35:07 +00:00
Teams [ ] * fic . Team
2021-09-09 09:20:45 +00:00
}
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 ( "" )
2022-06-04 16:21:41 +00:00
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 {
2021-09-09 09:20:45 +00:00
return nil , fmt . Errorf ( "Cannot create template: %w" , err )
} else if err = dexTmpl . Execute ( b , dexConfig {
Clients : [ ] dexConfigClient {
dexConfigClient {
Id : "epita-challenge" ,
2022-06-04 16:21:41 +00:00
Name : challengeInfo . Title ,
2021-09-09 09:20:45 +00:00
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 {
2022-06-04 16:10:09 +00:00
// Also generate team associations
for _ , team := range teams {
2022-06-08 09:22:30 +00:00
if err := os . Symlink ( fmt . Sprintf ( "%d" , team . Id ) , path . Join ( TeamsDir , fmt . Sprintf ( "team%02d" , team . Id ) ) ) ; err != nil {
2022-06-04 16:10:09 +00:00
log . Println ( "Unable to create association symlink:" , err . Error ( ) )
return nil , fmt . Errorf ( "Unable to create association symlink: %s" , err . Error ( ) )
}
}
2021-09-09 09:20:45 +00:00
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
}
}
}