Compare commits

..

3 Commits

Author SHA1 Message Date
87d60c5fd5 Try to clarify webhook usage
All checks were successful
continuous-integration/drone/push Build is passing
2022-09-07 21:41:20 +02:00
b119fe5da4 Disable button if no repo selected 2022-09-07 21:36:40 +02:00
d787d1c350 Harden works and surveys routes 2022-09-07 21:33:54 +02:00
4 changed files with 43 additions and 9 deletions

View File

@ -52,6 +52,7 @@ func declareAPISurveysRoutes(router *gin.RouterGroup) {
surveysRoutes := router.Group("/surveys/:sid") surveysRoutes := router.Group("/surveys/:sid")
surveysRoutes.Use(surveyHandler) surveysRoutes.Use(surveyHandler)
surveysRoutes.Use(surveyUserAccessHandler)
surveysRoutes.GET("", func(c *gin.Context) { surveysRoutes.GET("", func(c *gin.Context) {
u := c.MustGet("LoggedUser").(*User) u := c.MustGet("LoggedUser").(*User)
@ -198,6 +199,20 @@ func surveyHandler(c *gin.Context) {
} }
} }
func surveyUserAccessHandler(c *gin.Context) {
u := c.MustGet("LoggedUser").(*User)
w := c.MustGet("survey").(*Survey)
if u.IsAdmin {
c.Next()
} else if w.Shown && (w.Group == "" || strings.Contains(u.Groups, ","+w.Group+",")) {
c.Next()
} else {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Survey not found."})
return
}
}
type Survey struct { type Survey struct {
Id int64 `json:"id"` Id int64 `json:"id"`
Title string `json:"title"` Title string `json:"title"`

View File

@ -82,17 +82,20 @@
{#each repos as repo (repo.id)} {#each repos as repo (repo.id)}
<div class="{className} card"> <div class="{className} card">
<div class="card-body d-flex justify-content-between"> <div class="card-body d-flex justify-content-between">
<div class="d-flex flex-column justify-content-center"> <div class="d-flex flex-column justify-content-center pe-3">
<div> <div>
<div class="row"> <div class="row">
<label for={repo.id + "url"} class="col-sm-6 col-form-label">Dépôt lié&nbsp;:</label> <label for={repo.id + "url"} class="col-sm-4 col-form-label">Dépôt lié&nbsp;:</label>
<div class="col-sm-6"> <div class="col-sm-8">
<input type="text" class="form-control form-control-sm" style="font-family: monospace" disabled id={repo.id + "url"} value={repo.uri}> <input type="text" class="form-control form-control-sm" style="font-family: monospace" disabled id={repo.id + "url"} value={repo.uri}>
</div> </div>
</div> </div>
<p class="mt-2 mb-0 pe-1">
Vous pouvez ajouter un <span class="fst-italic">webhook</span> sur les <span class="fst-italic"><strong>Tag push events</strong></span> afin d'automatiser la récupération de votre travail. Dans les paramètres de votre dépôt sur GitLab, faite pointer un webhook sur <code>https://lessons.nemunai.re/api/callbacks/trigger.json</code> avec le secret ci-dessous.
</p>
<div class="row"> <div class="row">
<label for={repo.id + "secret"} class="col-sm-6 col-form-label">Webhook Secret token (à indiquer dans GitLab)&nbsp;:</label> <label for={repo.id + "secret"} class="col-sm-4 col-form-label">Webhook Secret token&nbsp;:</label>
<div class="col-sm-6"> <div class="col-sm-8">
<div class="input-group"> <div class="input-group">
<input type={repo.show_secret?"text":"password"} class="form-control form-control-sm" disabled id={repo.id + "secret"} value={repo.secret}> <input type={repo.show_secret?"text":"password"} class="form-control form-control-sm" disabled id={repo.id + "secret"} value={repo.secret}>
<button class="btn btn-sm btn-outline-info" on:click={() => { repo.show_secret = !repo.show_secret}}> <button class="btn btn-sm btn-outline-info" on:click={() => { repo.show_secret = !repo.show_secret}}>
@ -175,7 +178,7 @@
<button <button
type="submit" type="submit"
class="mt-2 btn btn-primary" class="mt-2 btn btn-primary"
disable={submitInProgress || readonly} disable={submitInProgress || readonly || !repo_used || !repo_used.uri}
> >
Utiliser ce dépôt Utiliser ce dépôt
</button> </button>

View File

@ -145,8 +145,7 @@
<ul> <ul>
<li>être dans l'espace de nom de votre utilisateur (à la fin de la liste des <span class="fst-italic">namespaces</span>),</li> <li>être dans l'espace de nom de votre utilisateur (à la fin de la liste des <span class="fst-italic">namespaces</span>),</li>
<li>avoir la visibilité «&nbsp;Privé&nbsp;»,</li> <li>avoir la visibilité «&nbsp;Privé&nbsp;»,</li>
<li>avoir invité <a href="https://gitlab.cri.epita.fr/nemunaire" target="_blank" style="font-family: monospace">nemunaire</a> avec le rôle <span class="fst-italic">Reporter</span> une fois le dépôt créé,</li> <li>avoir invité <a href="https://gitlab.cri.epita.fr/nemunaire" target="_blank" style="font-family: monospace">nemunaire</a> avec le rôle <span class="fst-italic">Reporter</span> une fois le dépôt créé.</li>
<li>avoir configuré un <span class="fst-italic">webhook <strong>Tag push events</strong></span> pointant sur <code>https://lessons.nemunai.re/api/callbacks/trigger.json</code> avec le secret donné.</li>
</ul> </ul>
{#if w.tag} {#if w.tag}

View File

@ -190,6 +190,7 @@ func declareAPIAdminWorksRoutes(router *gin.RouterGroup) {
func declareAPIAuthWorksRoutes(router *gin.RouterGroup) { func declareAPIAuthWorksRoutes(router *gin.RouterGroup) {
worksRoutes := router.Group("/works/:wid") worksRoutes := router.Group("/works/:wid")
worksRoutes.Use(workHandler) worksRoutes.Use(workHandler)
worksRoutes.Use(workUserAccessHandler)
worksRoutes.GET("", func(c *gin.Context) { worksRoutes.GET("", func(c *gin.Context) {
u := c.MustGet("LoggedUser").(*User) u := c.MustGet("LoggedUser").(*User)
@ -209,7 +210,9 @@ func declareAPIAuthWorksRoutes(router *gin.RouterGroup) {
u := c.MustGet("LoggedUser").(*User) u := c.MustGet("LoggedUser").(*User)
w := c.MustGet("work").(*Work) w := c.MustGet("work").(*Work)
if g, err := u.GetMyWorkGrade(w); err != nil && errors.Is(err, sql.ErrNoRows) { if !u.IsAdmin && !w.Corrected {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Permission denied"})
} else if g, err := u.GetMyWorkGrade(w); err != nil && errors.Is(err, sql.ErrNoRows) {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Aucune note n'a été attribuée pour ce travail. Avez-vous rendu ce travail ?"}) c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Aucune note n'a été attribuée pour ce travail. Avez-vous rendu ce travail ?"})
} else if err != nil { } else if err != nil {
log.Printf("Unable to GetMyWorkGrade(uid=%d;wid=%d): %s", u.Id, w.Id, err.Error()) log.Printf("Unable to GetMyWorkGrade(uid=%d;wid=%d): %s", u.Id, w.Id, err.Error())
@ -236,6 +239,20 @@ func workHandler(c *gin.Context) {
} }
} }
func workUserAccessHandler(c *gin.Context) {
u := c.MustGet("LoggedUser").(*User)
w := c.MustGet("work").(*Work)
if u.IsAdmin {
c.Next()
} else if w.Shown && (w.Group == "" || strings.Contains(u.Groups, ","+w.Group+",")) {
c.Next()
} else {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Work not found."})
return
}
}
type OneWork struct { type OneWork struct {
Kind string `json:"kind"` Kind string `json:"kind"`
Id int64 `json:"id"` Id int64 `json:"id"`