Compare commits

..

5 Commits

Author SHA1 Message Date
c21df9d93f Credit François on bounty page
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-19 11:44:47 +01:00
f675047ce8 Refactor permissions checks to avoid questions/works leaks between promotions/groups/start-availability
Thanks-To François Dautrême <francois.dautreme@epita.fr>
2022-11-19 11:44:31 +01:00
bf5b0e88dd Fix correction template mess 2022-11-19 11:44:31 +01:00
d7f679ce84 Display survey mean to admin 2022-11-19 11:44:31 +01:00
d6f620bc0d ui: Use $lib in imports 2022-11-19 11:44:31 +01:00
58 changed files with 245 additions and 205 deletions

View File

@ -39,7 +39,7 @@ func declareAPIAuthQuestionsRoutes(router *gin.RouterGroup) {
c.JSON(http.StatusOK, questions) c.JSON(http.StatusOK, questions)
} }
} else { } else {
if (!s.Shown || s.Direct != nil) && !u.IsAdmin { if s.Direct != nil && !u.IsAdmin {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Not accessible"}) c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Not accessible"})
return return
} }
@ -62,24 +62,7 @@ func declareAPIAuthQuestionsRoutes(router *gin.RouterGroup) {
questionsRoutes.Use(questionHandler) questionsRoutes.Use(questionHandler)
questionsRoutes.GET("", func(c *gin.Context) { questionsRoutes.GET("", func(c *gin.Context) {
q := c.MustGet("question").(*Question) c.JSON(http.StatusOK, c.MustGet("question").(*Question))
u := c.MustGet("LoggedUser").(*User)
if !u.IsAdmin {
s, err := getSurvey(int(q.IdSurvey))
if err != nil {
log.Println("Unable to getSurvey:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during survey retrieval. Please try again later."})
return
}
if !s.Shown || (s.Direct != nil && *s.Direct != q.Id) {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Not authorized"})
return
}
}
c.JSON(http.StatusOK, q)
}) })
declareAPIAuthProposalsRoutes(questionsRoutes) declareAPIAuthProposalsRoutes(questionsRoutes)
@ -171,6 +154,8 @@ func declareAPIAdminUserQuestionsRoutes(router *gin.RouterGroup) {
} }
func questionHandler(c *gin.Context) { func questionHandler(c *gin.Context) {
u := c.MustGet("LoggedUser").(*User)
var survey *Survey var survey *Survey
if s, ok := c.Get("survey"); ok { if s, ok := c.Get("survey"); ok {
survey = s.(*Survey) survey = s.(*Survey)
@ -190,6 +175,15 @@ func questionHandler(c *gin.Context) {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Question not found"}) c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Question not found"})
return return
} }
s, err := getSurvey(int(question.IdSurvey))
if err != nil {
log.Println("Unable to getSurvey:", err)
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs during survey retrieval. Please try again later."})
return
}
survey = s
} else { } else {
question, err = survey.GetQuestion(qid) question, err = survey.GetQuestion(qid)
if err != nil { if err != nil {
@ -198,6 +192,15 @@ func questionHandler(c *gin.Context) {
} }
} }
if !u.IsAdmin && (!survey.checkUserAccessToSurvey(u) || (survey.Direct != nil && *survey.Direct != question.Id)) {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Not authorized"})
return
}
if !u.IsAdmin && survey.StartAvailability.After(time.Now()) {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Not accessible yet"})
return
}
c.Set("question", question) c.Set("question", question)
c.Next() c.Next()

View File

@ -62,26 +62,22 @@ func declareAPISurveysRoutes(router *gin.RouterGroup) {
return return
} }
s := c.MustGet("survey").(*Survey) c.JSON(http.StatusOK, c.MustGet("survey").(*Survey))
if (s.Promo == u.Promo && (s.Group == "" || strings.Contains(u.Groups, ","+s.Group+",") && s.Shown)) || u.IsAdmin {
c.JSON(http.StatusOK, s)
} else {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Not accessible"})
}
}) })
} }
func declareAPIAuthSurveysRoutes(router *gin.RouterGroup) { func declareAPIAuthSurveysRoutes(router *gin.RouterGroup) {
surveysRoutes := router.Group("/surveys/:sid") surveysRoutes := router.Group("/surveys/:sid")
surveysRoutes.Use(surveyHandler) surveysRoutes.Use(surveyHandler)
surveysRoutes.Use(surveyUserAccessHandler)
surveysRoutes.GET("/score", func(c *gin.Context) { surveysRoutes.GET("/score", func(c *gin.Context) {
loggedUser := c.MustGet("LoggedUser").(*User)
var u *User var u *User
if user, ok := c.Get("user"); ok { if user, ok := c.Get("user"); ok {
u = user.(*User) u = user.(*User)
} else { } else {
u = c.MustGet("LoggedUser").(*User) u = loggedUser
} }
s := c.MustGet("survey").(*Survey) s := c.MustGet("survey").(*Survey)
@ -93,10 +89,29 @@ func declareAPIAuthSurveysRoutes(router *gin.RouterGroup) {
return return
} }
if score == nil { if score != nil {
c.JSON(http.StatusOK, map[string]string{"score": "N/A"})
} else {
c.JSON(http.StatusOK, map[string]float64{"score": *score}) c.JSON(http.StatusOK, map[string]float64{"score": *score})
} else if _, ok := c.Get("user"); !ok && loggedUser.IsAdmin {
// Admin retrieve mean score
scores, err := s.GetGrades()
if err != nil {
log.Printf("Unable to GetGrades(sid=%d): %s", s.Id, err.Error())
c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"errmsg": "An error occurs when trying to retrieve grades."})
return
}
*score = 0
nbGrades := 0
for _, s := range scores {
*score += *s
nbGrades += 1
}
*score /= float64(nbGrades)
c.JSON(http.StatusOK, map[string]float64{"score": *score})
} else {
c.JSON(http.StatusOK, map[string]string{"score": "N/A"})
} }
} else { } else {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Not accessible"}) c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Not accessible"})
@ -199,18 +214,20 @@ func surveyHandler(c *gin.Context) {
} }
} }
func (s *Survey) checkUserAccessToSurvey(u *User) bool {
return u.IsAdmin || (u.Promo == s.Promo && s.Shown && (s.Group == "" || strings.Contains(u.Groups, ","+s.Group+",")))
}
func surveyUserAccessHandler(c *gin.Context) { func surveyUserAccessHandler(c *gin.Context) {
u := c.MustGet("LoggedUser").(*User) u := c.MustGet("LoggedUser").(*User)
w := c.MustGet("survey").(*Survey) s := c.MustGet("survey").(*Survey)
if u.IsAdmin { if !s.checkUserAccessToSurvey(u) {
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."}) c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Survey not found."})
return return
} }
c.Next()
} }
type Survey struct { type Survey struct {

View File

@ -1,5 +1,5 @@
<script> <script>
import { CorrectionTemplate } from '../lib/correctionTemplates'; import { CorrectionTemplate } from '$lib/correctionTemplates';
let className = ''; let className = '';
export { className as class }; export { className as class };

View File

@ -1,6 +1,6 @@
<script> <script>
import { user } from '../stores/user'; import { user } from '$lib/stores/user';
import { autoCorrection } from '../lib/correctionTemplates'; import { autoCorrection } from '$lib/correctionTemplates';
export let cts = null; export let cts = null;
export let rid = 0; export let rid = 0;

View File

@ -4,8 +4,8 @@
import QuestionProposals from './QuestionProposals.svelte'; import QuestionProposals from './QuestionProposals.svelte';
import ResponseCorrected from './ResponseCorrected.svelte'; import ResponseCorrected from './ResponseCorrected.svelte';
import CorrectionResponseFooter from './CorrectionResponseFooter.svelte'; import CorrectionResponseFooter from './CorrectionResponseFooter.svelte';
import { autoCorrection } from '../lib/correctionTemplates'; import { autoCorrection } from '$lib/correctionTemplates';
import { getUser } from '../lib/users'; import { getUser } from '$lib/users';
export let cts = null; export let cts = null;
export let filter = ""; export let filter = "";

View File

@ -5,7 +5,7 @@
import QuestionHeader from './QuestionHeader.svelte'; import QuestionHeader from './QuestionHeader.svelte';
import QuestionProposals from './QuestionProposals.svelte'; import QuestionProposals from './QuestionProposals.svelte';
import ResponseCorrected from './ResponseCorrected.svelte'; import ResponseCorrected from './ResponseCorrected.svelte';
import { user } from '../stores/user'; import { user } from '$lib/stores/user';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();

View File

@ -1,7 +1,7 @@
<script> <script>
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
import { user } from '../stores/user'; import { user } from '$lib/stores/user';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();

View File

@ -1,7 +1,7 @@
<script> <script>
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
import { QuestionProposal } from '../lib/questions'; import { QuestionProposal } from '$lib/questions';
export let edit = false; export let edit = false;
export let proposals = []; export let proposals = [];

View File

@ -1,6 +1,6 @@
<script> <script>
import { user } from '../stores/user'; import { user } from '$lib/stores/user';
import { ToastsStore } from '../stores/toasts'; import { ToastsStore } from '$lib/stores/toasts';
export let response = null; export let response = null;
export let survey = null; export let survey = null;

View File

@ -1,6 +1,6 @@
<script> <script>
import { getSurveys } from '../lib/surveys'; import { getSurveys } from '$lib/surveys';
import { getUsers, getGrades, getPromos } from '../lib/users'; import { getUsers, getGrades, getPromos } from '$lib/users';
export let promo = null; export let promo = null;
</script> </script>

View File

@ -1,8 +1,8 @@
<script> <script>
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
import DateFormat from '../components/DateFormat.svelte'; import DateFormat from '$lib/components/DateFormat.svelte';
import { getUserRendu } from '../lib/works'; import { getUserRendu } from '$lib/works';
let className = ''; let className = '';
export { className as class }; export { className as class };

View File

@ -2,9 +2,9 @@
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { getQuestions } from '../lib/questions'; import { getQuestions } from '$lib/questions';
import DateTimeInput from './DateTimeInput.svelte'; import DateTimeInput from './DateTimeInput.svelte';
import { ToastsStore } from '../stores/toasts'; import { ToastsStore } from '$lib/stores/toasts';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
export let survey = null; export let survey = null;

View File

@ -1,12 +1,12 @@
<script lang="ts"> <script lang="ts">
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { user } from '../stores/user'; import { user } from '$lib/stores/user';
import DateFormat from '../components/DateFormat.svelte'; import DateFormat from '$lib/components/DateFormat.svelte';
import SurveyBadge from '../components/SurveyBadge.svelte'; import SurveyBadge from '$lib/components/SurveyBadge.svelte';
import SubmissionStatus from '../components/SubmissionStatus.svelte'; import SubmissionStatus from '$lib/components/SubmissionStatus.svelte';
import { getSurveys } from '../lib/surveys'; import { getSurveys } from '$lib/surveys';
import { getScore } from '../lib/users'; import { getScore } from '$lib/users';
export let allworks = false; export let allworks = false;

View File

@ -1,8 +1,8 @@
<script> <script>
import { user } from '../stores/user'; import { user } from '$lib/stores/user';
import { ToastsStore } from '../stores/toasts'; import { ToastsStore } from '$lib/stores/toasts';
import QuestionForm from '../components/QuestionForm.svelte'; import QuestionForm from '$lib/components/QuestionForm.svelte';
import { Question } from '../lib/questions'; import { Question } from '$lib/questions';
export let survey = null; export let survey = null;
export let id_user = null; export let id_user = null;

View File

@ -1,5 +1,5 @@
<script> <script>
import { ToastsStore } from '../stores/toasts'; import { ToastsStore } from '$lib/stores/toasts';
</script> </script>
<div class="toast-container position-fixed top-0 end-0 p-3"> <div class="toast-container position-fixed top-0 end-0 p-3">

View File

@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { getKeys, getKey, Key } from '../lib/key'; import { getKeys, getKey, Key } from '$lib/key';
export let student = null; export let student = null;
</script> </script>

View File

@ -1,8 +1,8 @@
<script lang="ts"> <script lang="ts">
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { getSurveys } from '../lib/surveys'; import { getSurveys } from '$lib/surveys';
import { getUser, getUserGrade, getUserScore } from '../lib/users'; import { getUser, getUserGrade, getUserScore } from '$lib/users';
export let student = null; export let student = null;
export let allPromos = false; export let allPromos = false;

View File

@ -1,6 +1,6 @@
<script> <script>
import { user } from '../stores/user'; import { user } from '$lib/stores/user';
import DateFormat from '../components/DateFormat.svelte'; import DateFormat from '$lib/components/DateFormat.svelte';
let className = ''; let className = '';
export { className as class }; export { className as class };

View File

@ -3,7 +3,7 @@
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import DateTimeInput from './DateTimeInput.svelte'; import DateTimeInput from './DateTimeInput.svelte';
import { ToastsStore } from '../stores/toasts'; import { ToastsStore } from '$lib/stores/toasts';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
export let work = null; export let work = null;

View File

@ -1,10 +1,10 @@
<script> <script>
import { createEventDispatcher } from 'svelte'; import { createEventDispatcher } from 'svelte';
import BuildState from '../components/BuildState.svelte'; import BuildState from '$lib/components/BuildState.svelte';
import DateFormat from '../components/DateFormat.svelte'; import DateFormat from '$lib/components/DateFormat.svelte';
import { WorkRepository, getRemoteRepositories, getRepositories } from '../lib/repositories'; import { WorkRepository, getRemoteRepositories, getRepositories } from '$lib/repositories';
import { ToastsStore } from '../stores/toasts'; import { ToastsStore } from '$lib/stores/toasts';
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();

View File

@ -1,5 +1,5 @@
<script context="module"> <script context="module">
import { user } from '../stores/user'; import { user } from '$lib/stores/user';
let stop_refresh = false; let stop_refresh = false;
let refresh_interval_auth = null; let refresh_interval_auth = null;
@ -42,8 +42,8 @@
</script> </script>
<script> <script>
import AuthButton from '../components/AuthButton.svelte'; import AuthButton from '$lib/components/AuthButton.svelte';
import Toaster from '../components/Toaster.svelte'; import Toaster from '$lib/components/Toaster.svelte';
export let rroute = ''; export let rroute = '';

View File

@ -2,9 +2,9 @@
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { page } from '$app/stores' import { page } from '$app/stores'
import AuthButton from '../components/AuthButton.svelte'; import AuthButton from '$lib/components/AuthButton.svelte';
import { ToastsStore } from '../stores/toasts'; import { ToastsStore } from '$lib/stores/toasts';
import { user } from '../stores/user'; import { user } from '$lib/stores/user';
let auth = { username: "", password: "" }; let auth = { username: "", password: "" };
let pleaseWait = false; let pleaseWait = false;

View File

@ -65,6 +65,22 @@
<h3 class="mt-5 mb-3">Hall of Fame</h3> <h3 class="mt-5 mb-3">Hall of Fame</h3>
<div class="card mb-3">
<div class="card-header">
L'accès aux questionnaires n'était pas filtré selon les groupes ou les promos.
<span class="badge bg-success shadow-lg">+2&nbsp;pts</span>
</div>
<div class="card-body">
<div class="row row-cols-6">
<img class="img-thumbnail" src="//photos.cri.epita.fr/francois.dautreme" alt="francois.dautreme">
</div>
<p class="card-text mt-3">
Divulguée et corrigée le 19 novembre 2022.
<a href="https://git.nemunai.re/srs/atsebay.t/commit/f675047ce8f6636aa45336b56c069172330b050f" target="_blank">Commit</a>
</p>
</div>
</div>
<div class="card mb-3"> <div class="card mb-3">
<div class="card-header"> <div class="card-header">
Il était toujours possible de répondre aux questionnaires après l'heure de clôture. Il était toujours possible de répondre aux questionnaires après l'heure de clôture.

View File

@ -9,7 +9,7 @@
</script> </script>
<script> <script>
import StudentGrades from '../../components/StudentGrades.svelte'; import StudentGrades from '$lib/components/StudentGrades.svelte';
export let promo; export let promo;
</script> </script>

View File

@ -1,5 +1,5 @@
<script> <script>
import StudentGrades from '../../components/StudentGrades.svelte'; import StudentGrades from '$lib/components/StudentGrades.svelte';
</script> </script>
<StudentGrades /> <StudentGrades />

View File

@ -1,6 +1,6 @@
<script> <script>
import { user } from '../stores/user'; import { user } from '$lib/stores/user';
import { ToastsStore } from '../stores/toasts'; import { ToastsStore } from '$lib/stores/toasts';
function needhelp() { function needhelp() {
fetch('api/help', { fetch('api/help', {

View File

@ -1,9 +1,9 @@
<script lang="ts"> <script lang="ts">
import { user } from '../stores/user'; import { user } from '$lib/stores/user';
import { getUser, getUserNeedingHelp } from '../lib/users'; import { getUser, getUserNeedingHelp } from '$lib/users';
import DateFormat from '../components/DateFormat.svelte'; import DateFormat from '$lib/components/DateFormat.svelte';
import SurveyList from '../components/SurveyList.svelte'; import SurveyList from '$lib/components/SurveyList.svelte';
import ValidateSubmissions from '../components/ValidateSubmissions.svelte'; import ValidateSubmissions from '$lib/components/ValidateSubmissions.svelte';
let direct = null; let direct = null;

View File

@ -1,7 +1,7 @@
<script> <script>
import { deleteKey, getKeys, getKey, Key } from '../lib/key'; import { deleteKey, getKeys, getKey, Key } from '$lib/key';
import { user } from '../stores/user'; import { user } from '$lib/stores/user';
import { ToastsStore } from '../stores/toasts'; import { ToastsStore } from '$lib/stores/toasts';
let keysP = getKeys(); let keysP = getKeys();

View File

@ -1,5 +1,5 @@
<script context="module"> <script context="module">
import { getSurvey } from '../../../lib/surveys'; import { getSurvey } from '$lib/surveys';
export async function load({ params, stuff }) { export async function load({ params, stuff }) {
const survey = getSurvey(params.sid); const survey = getSurvey(params.sid);

View File

@ -10,16 +10,16 @@
</script> </script>
<script> <script>
import { user } from '../../../stores/user'; import { user } from '$lib/stores/user';
import CorrectionPieChart from '../../../components/CorrectionPieChart.svelte'; import CorrectionPieChart from '$lib/components/CorrectionPieChart.svelte';
import ListInputResponses from '../../../components/ListInputResponses.svelte'; import ListInputResponses from '$lib/components/ListInputResponses.svelte';
import QuestionForm from '../../../components/QuestionForm.svelte'; import QuestionForm from '$lib/components/QuestionForm.svelte';
import StartStopLiveSurvey from '../../../components/StartStopLiveSurvey.svelte'; import StartStopLiveSurvey from '$lib/components/StartStopLiveSurvey.svelte';
import SurveyAdmin from '../../../components/SurveyAdmin.svelte'; import SurveyAdmin from '$lib/components/SurveyAdmin.svelte';
import SurveyBadge from '../../../components/SurveyBadge.svelte'; import SurveyBadge from '$lib/components/SurveyBadge.svelte';
import { getSurvey } from '../../../lib/surveys'; import { getSurvey } from '$lib/surveys';
import { getQuestion, getQuestions, Question } from '../../../lib/questions'; import { getQuestion, getQuestions, Question } from '$lib/questions';
import { getUsers } from '../../../lib/users'; import { getUsers } from '$lib/users';
export let surveyP; export let surveyP;
export let sid; export let sid;

View File

@ -1,5 +1,5 @@
<script context="module"> <script context="module">
import { getSurvey } from '../../../lib/surveys'; import { getSurvey } from '$lib/surveys';
export async function load({ params, stuff }) { export async function load({ params, stuff }) {
return { return {
@ -13,11 +13,11 @@
<script lang="ts"> <script lang="ts">
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { user } from '../../../stores/user'; import { user } from '$lib/stores/user';
import SurveyAdmin from '../../../components/SurveyAdmin.svelte'; import SurveyAdmin from '$lib/components/SurveyAdmin.svelte';
import SurveyBadge from '../../../components/SurveyBadge.svelte'; import SurveyBadge from '$lib/components/SurveyBadge.svelte';
import SurveyQuestions from '../../../components/SurveyQuestions.svelte'; import SurveyQuestions from '$lib/components/SurveyQuestions.svelte';
import { getQuestions } from '../../../lib/questions'; import { getQuestions } from '$lib/questions';
export let surveyP; export let surveyP;

View File

@ -12,11 +12,11 @@
<script> <script>
import { onDestroy } from 'svelte'; import { onDestroy } from 'svelte';
import { user } from '../../../stores/user'; import { user } from '$lib/stores/user';
import { ToastsStore } from '../../../stores/toasts'; import { ToastsStore } from '$lib/stores/toasts';
import SurveyBadge from '../../../components/SurveyBadge.svelte'; import SurveyBadge from '$lib/components/SurveyBadge.svelte';
import QuestionForm from '../../../components/QuestionForm.svelte'; import QuestionForm from '$lib/components/QuestionForm.svelte';
import { getQuestion } from '../../../lib/questions'; import { getQuestion } from '$lib/questions';
export let surveyP; export let surveyP;
export let sid; export let sid;

View File

@ -1,5 +1,5 @@
<script context="module"> <script context="module">
import { getSurvey } from '../../../../lib/surveys'; import { getSurvey } from '$lib/surveys';
export async function load({ params, stuff }) { export async function load({ params, stuff }) {
return { return {
@ -12,13 +12,13 @@
</script> </script>
<script lang="ts"> <script lang="ts">
import Correction from '../../../../components/Correction.svelte'; import Correction from '$lib/components/Correction.svelte';
import CorrectionPieChart from '../../../../components/CorrectionPieChart.svelte'; import CorrectionPieChart from '$lib/components/CorrectionPieChart.svelte';
import CorrectionReference from '../../../../components/CorrectionReference.svelte'; import CorrectionReference from '$lib/components/CorrectionReference.svelte';
import SurveyBadge from '../../../../components/SurveyBadge.svelte'; import SurveyBadge from '$lib/components/SurveyBadge.svelte';
import QuestionHeader from '../../../../components/QuestionHeader.svelte'; import QuestionHeader from '$lib/components/QuestionHeader.svelte';
import { getCorrectionTemplates } from '../../../../lib/correctionTemplates'; import { getCorrectionTemplates } from '$lib/correctionTemplates';
import { getQuestion } from '../../../../lib/questions'; import { getQuestion } from '$lib/questions';
export let surveyP; export let surveyP;
export let rid; export let rid;
@ -85,7 +85,7 @@
<SurveyBadge class="ms-2" {survey} /> <SurveyBadge class="ms-2" {survey} />
</div> </div>
<div class="card sticky-top"> <div class="card sticky-top" style="overflow-y: auto; max-height: 70vh">
<QuestionHeader <QuestionHeader
{question} {question}
nodescription={nodescription} nodescription={nodescription}
@ -180,4 +180,4 @@
{/await} {/await}
{/await} {/await}
<div class="mb-5"></div> <div class="mb-5" style="min-height: 60vh"></div>

View File

@ -1,6 +1,6 @@
<script context="module"> <script context="module">
import { getSurvey } from '../../../../lib/surveys'; import { getSurvey } from '$lib/surveys';
import { getUsers } from '../../../../lib/users'; import { getUsers } from '$lib/users';
export async function load({ params, stuff }) { export async function load({ params, stuff }) {
return { return {
@ -14,12 +14,12 @@
<script lang="ts"> <script lang="ts">
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { user } from '../../../../stores/user'; import { user } from '$lib/stores/user';
import StartStopLiveSurvey from '../../../../components/StartStopLiveSurvey.svelte'; import StartStopLiveSurvey from '$lib/components/StartStopLiveSurvey.svelte';
import SurveyAdmin from '../../../../components/SurveyAdmin.svelte'; import SurveyAdmin from '$lib/components/SurveyAdmin.svelte';
import SurveyBadge from '../../../../components/SurveyBadge.svelte'; import SurveyBadge from '$lib/components/SurveyBadge.svelte';
import SurveyQuestions from '../../../../components/SurveyQuestions.svelte'; import SurveyQuestions from '$lib/components/SurveyQuestions.svelte';
import { getQuestions } from '../../../../lib/questions'; import { getQuestions } from '$lib/questions';
export let surveyP; export let surveyP;
let usersP = null; let usersP = null;

View File

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import { user } from '../../stores/user'; import { user } from '$lib/stores/user';
import SurveyList from '../../components/SurveyList.svelte'; import SurveyList from '$lib/components/SurveyList.svelte';
</script> </script>
<div class="card bg-light"> <div class="card bg-light">

View File

@ -1,10 +1,10 @@
<script> <script>
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { user } from '../../stores/user'; import { user } from '$lib/stores/user';
import SurveyAdmin from '../../components/SurveyAdmin.svelte'; import SurveyAdmin from '$lib/components/SurveyAdmin.svelte';
import SurveyBadge from '../../components/SurveyBadge.svelte'; import SurveyBadge from '$lib/components/SurveyBadge.svelte';
import { Survey } from '../../lib/surveys'; import { Survey } from '$lib/surveys';
let survey = new Survey(); let survey = new Survey();
</script> </script>

View File

@ -9,11 +9,11 @@
</script> </script>
<script lang="ts"> <script lang="ts">
import UserKeys from '../../../components/UserKeys.svelte'; import UserKeys from '$lib/components/UserKeys.svelte';
import UserSurveys from '../../../components/UserSurveys.svelte'; import UserSurveys from '$lib/components/UserSurveys.svelte';
import { user } from '../../../stores/user'; import { user } from '$lib/stores/user';
import { getSurveys } from '../../../lib/surveys'; import { getSurveys } from '$lib/surveys';
import { getUser, getUserGrade, getUserScore } from '../../../lib/users'; import { getUser, getUserGrade, getUserScore } from '$lib/users';
export let uid; export let uid;

View File

@ -10,11 +10,11 @@
</script> </script>
<script lang="ts"> <script lang="ts">
import SurveyBadge from '../../../../components/SurveyBadge.svelte'; import SurveyBadge from '$lib/components/SurveyBadge.svelte';
import SurveyQuestions from '../../../../components/SurveyQuestions.svelte'; import SurveyQuestions from '$lib/components/SurveyQuestions.svelte';
import { getSurvey } from '../../../../lib/surveys'; import { getSurvey } from '$lib/surveys';
import { getQuestions } from '../../../../lib/questions'; import { getQuestions } from '$lib/questions';
import { getUser } from '../../../../lib/users'; import { getUser } from '$lib/users';
export let sid; export let sid;
export let uid; export let uid;

View File

@ -9,10 +9,10 @@
</script> </script>
<script lang="ts"> <script lang="ts">
import UserSurveys from '../../../../components/UserSurveys.svelte'; import UserSurveys from '$lib/components/UserSurveys.svelte';
import { user } from '../../../../stores/user'; import { user } from '$lib/stores/user';
import { getSurveys } from '../../../../lib/surveys'; import { getSurveys } from '$lib/surveys';
import { getUser, getUserGrade, getUserScore } from '../../../../lib/users'; import { getUser, getUserGrade, getUserScore } from '$lib/users';
export let uid; export let uid;

View File

@ -1,9 +1,9 @@
<script> <script>
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { user } from '../../stores/user'; import { user } from '$lib/stores/user';
import DateFormat from '../../components/DateFormat.svelte'; import DateFormat from '$lib/components/DateFormat.svelte';
import { getUsers, getPromos } from '../../lib/users'; import { getUsers, getPromos } from '$lib/users';
function showUser(user) { function showUser(user) {
goto(`users/${user.id}`) goto(`users/${user.id}`)

View File

@ -1,5 +1,5 @@
<script context="module"> <script context="module">
import { getWork } from '../../../lib/works'; import { getWork } from '$lib/works';
export async function load({ params, stuff }) { export async function load({ params, stuff }) {
const work = getWork(params.wid); const work = getWork(params.wid);

View File

@ -1,5 +1,5 @@
<script context="module"> <script context="module">
import { getWork } from '../../../lib/works'; import { getWork } from '$lib/works';
export async function load({ params, stuff }) { export async function load({ params, stuff }) {
return { return {
@ -13,13 +13,13 @@
<script lang="ts"> <script lang="ts">
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { user } from '../../../stores/user'; import { user } from '$lib/stores/user';
import DateFormat from '../../../components/DateFormat.svelte'; import DateFormat from '$lib/components/DateFormat.svelte';
import SubmissionStatus from '../../../components/SubmissionStatus.svelte'; import SubmissionStatus from '$lib/components/SubmissionStatus.svelte';
import SurveyBadge from '../../../components/SurveyBadge.svelte'; import SurveyBadge from '$lib/components/SurveyBadge.svelte';
import WorkAdmin from '../../../components/WorkAdmin.svelte'; import WorkAdmin from '$lib/components/WorkAdmin.svelte';
import WorkRepository from '../../../components/WorkRepository.svelte'; import WorkRepository from '$lib/components/WorkRepository.svelte';
import { getScore } from '../../../lib/users'; import { getScore } from '$lib/users';
export let work = null; export let work = null;
let edit = false; let edit = false;

View File

@ -1,5 +1,5 @@
<script context="module"> <script context="module">
import { getWork } from '../../../lib/works'; import { getWork } from '$lib/works';
export async function load({ params, stuff }) { export async function load({ params, stuff }) {
return { return {
@ -13,15 +13,15 @@
<script lang="ts"> <script lang="ts">
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { user } from '../../../stores/user'; import { user } from '$lib/stores/user';
import BuildState from '../../../components/BuildState.svelte'; import BuildState from '$lib/components/BuildState.svelte';
import DateFormat from '../../../components/DateFormat.svelte'; import DateFormat from '$lib/components/DateFormat.svelte';
import SubmissionStatus from '../../../components/SubmissionStatus.svelte'; import SubmissionStatus from '$lib/components/SubmissionStatus.svelte';
import SurveyBadge from '../../../components/SurveyBadge.svelte'; import SurveyBadge from '$lib/components/SurveyBadge.svelte';
import WorkRepository from '../../../components/WorkRepository.svelte'; import WorkRepository from '$lib/components/WorkRepository.svelte';
import { getRepositories } from '../../../lib/repositories'; import { getRepositories } from '$lib/repositories';
import { ToastsStore } from '../../../stores/toasts'; import { ToastsStore } from '$lib/stores/toasts';
import { getUsers } from '../../../lib/users'; import { getUsers } from '$lib/users';
export let work = null; export let work = null;
let usersP = null; let usersP = null;

View File

@ -1,13 +1,13 @@
<script> <script>
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { user } from '../../stores/user'; import { user } from '$lib/stores/user';
import DateFormat from '../../components/DateFormat.svelte'; import DateFormat from '$lib/components/DateFormat.svelte';
import SurveyBadge from '../../components/SurveyBadge.svelte'; import SurveyBadge from '$lib/components/SurveyBadge.svelte';
import SubmissionStatus from '../../components/SubmissionStatus.svelte'; import SubmissionStatus from '$lib/components/SubmissionStatus.svelte';
import { getWorks } from '../../lib/works'; import { getWorks } from '$lib/works';
import { getPromos } from '../../lib/users'; import { getPromos } from '$lib/users';
import { getScore } from '../../lib/users'; import { getScore } from '$lib/users';
let filterPromo = ""; let filterPromo = "";
</script> </script>

View File

@ -1,10 +1,10 @@
<script> <script>
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { user } from '../../stores/user'; import { user } from '$lib/stores/user';
import WorkAdmin from '../../components/WorkAdmin.svelte'; import WorkAdmin from '$lib/components/WorkAdmin.svelte';
import SurveyBadge from '../../components/SurveyBadge.svelte'; import SurveyBadge from '$lib/components/SurveyBadge.svelte';
import { Work } from '../../lib/works'; import { Work } from '$lib/works';
let work = new Work(); let work = new Work();
</script> </script>

View File

@ -43,7 +43,11 @@ func declareAPIWorksRoutes(router *gin.RouterGroup) {
} else { } else {
for _, w := range works { for _, w := range works {
if w.Group == "" || strings.Contains(u.Groups, ","+w.Group+",") { if w.Group == "" || strings.Contains(u.Groups, ","+w.Group+",") {
// Remove informations not needed on front page for students
w.Promo = 0
w.Group = "" w.Group = ""
w.DescriptionRaw = ""
response = append(response, w) response = append(response, w)
} }
} }
@ -79,7 +83,10 @@ func declareAPIWorksRoutes(router *gin.RouterGroup) {
} else { } else {
for _, w := range works { for _, w := range works {
if w.Group == "" || strings.Contains(u.Groups, ","+w.Group+",") { if w.Group == "" || strings.Contains(u.Groups, ","+w.Group+",") {
// Remove informations not needed on front page for students
w.Promo = 0
w.Group = "" w.Group = ""
response = append(response, w) response = append(response, w)
} }
} }
@ -193,16 +200,7 @@ func declareAPIAuthWorksRoutes(router *gin.RouterGroup) {
worksRoutes.Use(workUserAccessHandler) worksRoutes.Use(workUserAccessHandler)
worksRoutes.GET("", func(c *gin.Context) { worksRoutes.GET("", func(c *gin.Context) {
u := c.MustGet("LoggedUser").(*User) c.JSON(http.StatusOK, c.MustGet("work").(*Work))
w := c.MustGet("work").(*Work)
if u.IsAdmin {
c.JSON(http.StatusOK, w)
} else if w.Shown && w.StartAvailability.Before(time.Now()) && (w.Group == "" || strings.Contains(u.Groups, ","+w.Group+",")) {
c.JSON(http.StatusOK, w)
} else {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Permission denied"})
}
}) })
// Grades related to works // Grades related to works
@ -239,18 +237,24 @@ func workHandler(c *gin.Context) {
} }
} }
func (w *Work) checkUserAccessToWork(u *User) bool {
return u.IsAdmin || (u.Promo == w.Promo && w.Shown && (w.Group == "" || strings.Contains(u.Groups, ","+w.Group+",")))
}
func workUserAccessHandler(c *gin.Context) { func workUserAccessHandler(c *gin.Context) {
u := c.MustGet("LoggedUser").(*User) u := c.MustGet("LoggedUser").(*User)
w := c.MustGet("work").(*Work) w := c.MustGet("work").(*Work)
if u.IsAdmin { if !w.checkUserAccessToWork(u) {
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."}) c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"errmsg": "Work not found."})
return return
} }
if !u.IsAdmin && w.StartAvailability.After(time.Now()) {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"errmsg": "Not accessible yet"})
return
}
c.Next()
} }
type OneWork struct { type OneWork struct {
@ -291,14 +295,14 @@ func allWorks(cnd string, param ...interface{}) (items []*OneWork, err error) {
type Work struct { type Work struct {
Id int64 `json:"id"` Id int64 `json:"id"`
Title string `json:"title"` Title string `json:"title"`
Promo uint `json:"promo"` Promo uint `json:"promo,omitempty"`
Group string `json:"group"` Group string `json:"group,omitempty"`
Shown bool `json:"shown"` Shown bool `json:"shown"`
Description string `json:"description"` Description string `json:"description,omitempty"`
DescriptionRaw string `json:"descr_raw,omitempty"` DescriptionRaw string `json:"descr_raw,omitempty"`
Tag string `json:"tag"` Tag string `json:"tag,omitempty"`
SubmissionURL *string `json:"submission_url"` SubmissionURL *string `json:"submission_url,omitempty"`
Corrected bool `json:"corrected"` Corrected bool `json:"corrected,omitempty"`
StartAvailability time.Time `json:"start_availability"` StartAvailability time.Time `json:"start_availability"`
EndAvailability time.Time `json:"end_availability"` EndAvailability time.Time `json:"end_availability"`
} }