feat [both]: authentication

This commit is contained in:
Alice 2022-12-25 23:52:43 +01:00
parent 42fd29ce95
commit 04fedf7985
10 changed files with 178 additions and 4 deletions

View file

@ -33,12 +33,31 @@
{{ menuItem.label }}
</nuxt-link>
</div>
<div class="navbar-end">
<a class="navbar-item" role="button" @click="onLogoutClick">
Se déconnecter
</a>
</div>
</div>
</nav>
<div v-if="errorMessage" class="modal is-active">
<div class="modal-background" @click="errorMessage = ''" />
<div class="modal-card">
<section class="modal-card-body">
La déconnexion a échoué avec le message d'erreur suivant : "{{
errorMessage
}}".
</section>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { useAuthStore } from "~/stores/authStore"
const authStore = useAuthStore()
const menuItems = [
{
label: "Liste des films",
@ -51,6 +70,13 @@ const menuItems = [
]
const isBurgerOpen = ref(false)
const errorMessage = ref("")
async function onLogoutClick() {
const { error } = await authStore.logout()
errorMessage.value = error.value?.message || ""
if (!error.value) await navigateTo("/", { replace: true })
}
</script>
<style scoped lang="sass"></style>

View file

@ -1,8 +1,12 @@
<template>
<AdminHeader />
<AdminHeader v-if="authStore.isLogged" />
<div class="container"><slot /></div>
</template>
<script setup lang="ts"></script>
<script setup lang="ts">
import { useAuthStore } from "~/stores/authStore"
const authStore = useAuthStore()
</script>
<style scoped lang="sass"></style>

View file

@ -0,0 +1,12 @@
import { useAuthStore } from "~/stores/authStore"
export default defineNuxtRouteMiddleware(async (to) => {
if (
!process.server &&
to.meta.layout === "admin" &&
to.name !== "admin-login" &&
!(await useAuthStore().isLogged)
) {
return navigateTo("/admin/login")
}
})

View file

@ -0,0 +1,84 @@
<template>
<div class="mt-5 hero is-fullheight">
<div class="hero-body is-justify-content-center is-align-items-center">
<div class="columns is-flex is-flex-direction-column box">
<div class="column is-half-desktop is-offset-one-quarter-desktop">
<div v-if="errorMessage" class="notification is-danger">
La connexion a échoué pour le motif suivant : "{{ errorMessage }}".
</div>
<form action="#" @submit.prevent="onSubmit">
<fieldset>
<label for="username">Identifiant</label>
<input
id="username"
v-model="login.username"
class="input is-primary"
type="text"
placeholder="Identifiant"
/>
</fieldset>
<fieldset>
<label for="password">Mot de passe</label>
<input
id="password"
v-model="login.password"
class="input is-primary"
type="password"
placeholder="Mot de passe"
/>
</fieldset>
<fieldset>
<label class="button is-primary is-fullwidth" for="submit">
Connexion
</label>
<input id="submit" type="submit" />
</fieldset>
<div class="has-text-centered">
<p class="is-size-7">
Pas de compte ? Oublié votre mot de passe ?
<a href="mailto:cineclub-contact@ens.psl.eu">
Contactez le ciné-club.
</a>
</p>
</div>
</form>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { useAuthStore } from "~/stores/authStore"
definePageMeta({
layout: "admin",
})
const authStore = useAuthStore()
const login = ref({ username: "", password: "" })
const errorMessage = ref("")
async function onSubmit() {
const { error } = await authStore.login(
login.value.username,
login.value.password
)
errorMessage.value = error.value?.message || ""
if (!error.value) await navigateTo("/admin/", { replace: true })
}
</script>
<style scoped lang="sass">
fieldset
margin-bottom: 1.5rem
input
margin-top: 0.5rem
input[type="submit"]
display: none
</style>

View file

@ -0,0 +1,12 @@
import { useAuthStore } from "~/stores/authStore";
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook("app:mounted", async () => {
// the data should already be fetched from SSR
// but if it's missing, we try again from the client
const authStore = useAuthStore()
if (authStore.logStatus === undefined) {
await authStore.updateLogStatus()
}
})
})

View file

@ -0,0 +1,5 @@
import { useAuthStore } from "~/stores/authStore";
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook("vue:setup", () => useAuthStore().updateLogStatus())
})

28
front/stores/authStore.ts Normal file
View file

@ -0,0 +1,28 @@
import { defineStore } from "pinia"
export const useAuthStore = defineStore("auth", {
state: () =>
({
logStatus: undefined,
} as { logStatus: boolean | undefined }),
actions: {
async login(username: string, password: string) {
const res = await apiPost("auth/login/", { username, password })
if (!res.error.value) this.logStatus = true
return res
},
async logout() {
const res = await apiPost("auth/logout/")
if (!res.error.value) this.logStatus = false
return res
},
async updateLogStatus() {
const res = await apiGet("auth/user/")
this.logStatus = !res.error.value
return res
},
},
getters: {
isLogged: (state) => state.logStatus,
},
})

View file

@ -12,5 +12,5 @@ router.register(r"tmdb", TmdbViewSet, "tmdb")
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
path("", include(router.urls)),
path("api-auth/", include("rest_framework.urls", namespace="rest_framework")),
path('auth/', include('dj_rest_auth.urls')),
]

View file

@ -5,3 +5,4 @@ djangorestframework-camel-case==1.3.0
getconf~=1.11.1
tmdbv3api~=1.7.6
factory-boy==3.2.1
dj-rest-auth==2.2.5

View file

@ -39,13 +39,14 @@ INSTALLED_APPS = [
"myapi.apps.MyapiConfig",
"rest_framework",
"corsheaders",
"dj_rest_auth",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"corsheaders.middleware.CorsMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
@ -128,3 +129,4 @@ REST_FRAMEWORK = {
}
TMDB_API_KEY = config.getstr("tmdb.api_key")
REST_AUTH_TOKEN_MODEL = None