This commit is contained in:
Maxime Duchene-Savard 2025-04-14 09:48:25 -04:00
parent 5aa5520e4c
commit b57cb700ec
9 changed files with 84 additions and 23 deletions

View File

@ -8,7 +8,8 @@ import (
func InitAuthEndpoints(r *gin.RouterGroup) { func InitAuthEndpoints(r *gin.RouterGroup) {
group := r.Group("/auth") group := r.Group("/auth")
group.POST("/login", authLogin) group.POST("/signin", authSignin)
group.POST("/signout", authSignout)
group.GET("/me", getMe) group.GET("/me", getMe)
} }
@ -17,7 +18,7 @@ type loginRequest struct {
Password string `json:"password"` Password string `json:"password"`
} }
func authLogin(c *gin.Context) { func authSignin(c *gin.Context) {
var loginRequest loginRequest var loginRequest loginRequest
err := c.BindJSON(&loginRequest) err := c.BindJSON(&loginRequest)
if err != nil { if err != nil {
@ -48,6 +49,11 @@ func authLogin(c *gin.Context) {
c.JSON(200, gin.H{"valid": true}) c.JSON(200, gin.H{"valid": true})
} }
func authSignout(c *gin.Context) {
c.SetCookie("CLORTHO_AUTH", "", -1, "/", "", true, true)
c.JSON(200, gin.H{})
}
func getMe(c *gin.Context) { func getMe(c *gin.Context) {
session, hasSession := c.Get("session") session, hasSession := c.Get("session")
if !hasSession { if !hasSession {

View File

@ -31,7 +31,7 @@ func TestMain(m *testing.M) {
os.Exit(exitCode) os.Exit(exitCode)
} }
func TestInitAuthEndpoints_authLogin(t *testing.T) { func TestInitAuthEndpoints_authSignin(t *testing.T) {
adminPass, err := users.InitAdminUser() adminPass, err := users.InitAdminUser()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -43,11 +43,34 @@ func TestInitAuthEndpoints_authLogin(t *testing.T) {
reqBody := loginRequest{Username: "admin", Password: *adminPass} reqBody := loginRequest{Username: "admin", Password: *adminPass}
strReqBody, _ := json.Marshal(reqBody) strReqBody, _ := json.Marshal(reqBody)
w := httptest.NewRecorder() w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/gui/auth/login", strings.NewReader(string(strReqBody))) req, _ := http.NewRequest("POST", "/gui/auth/signin", strings.NewReader(string(strReqBody)))
r.ServeHTTP(w, req) r.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code) assert.Equal(t, 200, w.Code)
assert.JSONEq(t, `{"valid": true}`, w.Body.String()) assert.JSONEq(t, `{"valid": true}`, w.Body.String())
setCookie := w.Header().Get("Set-Cookie")
assert.True(t, strings.Contains(setCookie, "CLORTHO_AUTH="))
}
func TestInitAuthEndpoints_authSignout(t *testing.T) {
adminPass, err := users.InitAdminUser()
if err != nil {
t.Fatal(err)
}
r := gin.Default()
SetupRouter(r, nil)
reqBody := loginRequest{Username: "admin", Password: *adminPass}
strReqBody, _ := json.Marshal(reqBody)
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", "/gui/auth/signout", strings.NewReader(string(strReqBody)))
r.ServeHTTP(w, req)
assert.Equal(t, 200, w.Code)
assert.JSONEq(t, `{}`, w.Body.String())
setCookie := w.Header().Get("Set-Cookie")
assert.True(t, strings.Contains(setCookie, "CLORTHO_AUTH="))
} }
func TestInitAuthEndpoints_getMe(t *testing.T) { func TestInitAuthEndpoints_getMe(t *testing.T) {

View File

@ -12,12 +12,14 @@
<v-menu> <v-menu>
<template #activator="{ props }"> <template #activator="{ props }">
<v-avatar <v-avatar
class="hidden-sm-and-down mr-3" class="mr-3"
color="grey-darken-1" color="grey-darken-1"
size="32" size="32"
v-bind="props" v-bind="props"
/> />
</template> </template>
<div>
{{ appStore.user.displayName }}
<v-list> <v-list>
<v-list-item <v-list-item
v-for="(item, index) in items" v-for="(item, index) in items"
@ -28,6 +30,7 @@
<v-list-item-title>{{ item.title }}</v-list-item-title> <v-list-item-title>{{ item.title }}</v-list-item-title>
</v-list-item> </v-list-item>
</v-list> </v-list>
</div>
</v-menu> </v-menu>
</v-app-bar> </v-app-bar>
@ -51,9 +54,9 @@ watch(() => appStore.user, initMenu)
function initMenu() { function initMenu() {
if (appStore.user) { if (appStore.user) {
items.value = [{title: "Log out", link: "/logout"}] items.value = [{title: "Log out", link: "/auth/signout"}]
} else { } else {
items.value = [{title: "Sign In", link: "/signin"}] items.value = [{title: "Sign In", link: "/auth/signin"}]
} }
} }
</script> </script>

View File

@ -65,7 +65,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import {ref} from 'vue' import {ref} from 'vue'
import {useRouter} from 'vue-router'
const router = useRouter() const router = useRouter()
const valid = ref(false) const valid = ref(false)

View File

@ -0,0 +1,17 @@
<template>
</template>
<script setup lang="ts">
onMounted(async () => {
await fetch('/gui/auth/signout', {
method: 'POST',
})
const router = useRouter();
await router.replace("/")
})
</script>
<style scoped lang="sass">
</style>

View File

@ -1,7 +1,11 @@
<template> <template>
<div style="min-height: 3000px"></div> <div style="min-height: 3000px" />
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
// /**
* @route
* @meta public true
* @meta title "Home"
*/
</script> </script>

View File

@ -38,13 +38,15 @@ router.beforeEach(async (to, from, next) => {
const appStore = useAppStore(); const appStore = useAppStore();
await appStore.updateUser() await appStore.updateUser()
console.log("to", to)
if (to.meta.public) { if (to.meta.public) {
next() next()
return return
} }
if (!appStore.user) { if (!appStore.user) {
next('/signin') next('/auth/signin')
} else { } else {
next() next()
} }

View File

@ -9,7 +9,13 @@ export const useAppStore = defineStore('app', {
async updateUser() { async updateUser() {
const res = await fetch('/gui/auth/me') const res = await fetch('/gui/auth/me')
if (res.status === 200) { if (res.status === 200) {
this.user = await res.json() const data = await res.json()
if (data.loggedIn) {
this.user = data.user
} else {
this.user = null
}
} else if (res.status === 401) { } else if (res.status === 401) {
this.user = null this.user = null
} }

View File

@ -19,6 +19,7 @@ declare module 'vue-router/auto-routes' {
*/ */
export interface RouteNamedMap { export interface RouteNamedMap {
'/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>, '/': RouteRecordInfo<'/', '/', Record<never, never>, Record<never, never>>,
'/signin': RouteRecordInfo<'/signin', '/signin', Record<never, never>, Record<never, never>>, '/auth/signin': RouteRecordInfo<'/auth/signin', '/auth/signin', Record<never, never>, Record<never, never>>,
'/auth/signout': RouteRecordInfo<'/auth/signout', '/auth/signout', Record<never, never>, Record<never, never>>,
} }
} }