Compare commits
2 Commits
0b256863d6
...
7991c80176
| Author | SHA1 | Date | |
|---|---|---|---|
| 7991c80176 | |||
| bad44a6ddd |
@@ -5,6 +5,7 @@ import (
|
|||||||
|
|
||||||
"adam-french.co.uk/backend/models"
|
"adam-french.co.uk/backend/models"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/golang-jwt/jwt/v5"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,6 +27,28 @@ func (store *Store) AuthMiddlewear(ctx *gin.Context) {
|
|||||||
ctx.Next()
|
ctx.Next()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (store *Store) AdminMiddleware(ctx *gin.Context) {
|
||||||
|
claims, exists := ctx.Get("userClaims")
|
||||||
|
if !exists {
|
||||||
|
ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mapClaims, ok := claims.(*jwt.MapClaims)
|
||||||
|
if !ok {
|
||||||
|
ctx.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "invalid claims"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
admin, ok := (*mapClaims)["admin"].(bool)
|
||||||
|
if !ok || !admin {
|
||||||
|
ctx.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "admin access required"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Next()
|
||||||
|
}
|
||||||
|
|
||||||
func (store *Store) CheckToken(ctx *gin.Context) {
|
func (store *Store) CheckToken(ctx *gin.Context) {
|
||||||
access_token, err := ctx.Cookie("access_token")
|
access_token, err := ctx.Cookie("access_token")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -67,11 +67,6 @@ func (store *Store) CreatePost(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
userID := uint(userIDF)
|
userID := uint(userIDF)
|
||||||
|
|
||||||
if !(*claims)["admin"].(bool) {
|
|
||||||
ctx.JSON(http.StatusForbidden, gin.H{"error": "you are not admin :("})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create post
|
// Create post
|
||||||
post := models.Post{Title: input.Title, Content: input.Content, AuthorID: userID}
|
post := models.Post{Title: input.Title, Content: input.Content, AuthorID: userID}
|
||||||
tx := store.DB.Create(&post)
|
tx := store.DB.Create(&post)
|
||||||
|
|||||||
@@ -19,21 +19,6 @@ type SetAdminInput struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (store *Store) CreateUser(ctx *gin.Context) {
|
func (store *Store) CreateUser(ctx *gin.Context) {
|
||||||
claimsVal, ok := ctx.Get("userClaims")
|
|
||||||
if !ok {
|
|
||||||
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "user claims could not be found"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
claims, ok := claimsVal.(*jwt.MapClaims)
|
|
||||||
if !ok {
|
|
||||||
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "invalid claims"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !(*claims)["admin"].(bool) {
|
|
||||||
ctx.JSON(http.StatusForbidden, gin.H{"error": "admin access required"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var input UserCredentials
|
var input UserCredentials
|
||||||
if err := ctx.ShouldBindBodyWithJSON(&input); err != nil {
|
if err := ctx.ShouldBindBodyWithJSON(&input); err != nil {
|
||||||
ctx.JSON(http.StatusBadRequest, err.Error())
|
ctx.JSON(http.StatusBadRequest, err.Error())
|
||||||
@@ -116,11 +101,6 @@ func (store *Store) SetUserAdmin(ctx *gin.Context) {
|
|||||||
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "invalid claims"})
|
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "invalid claims"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !(*claims)["admin"].(bool) {
|
|
||||||
ctx.JSON(http.StatusForbidden, gin.H{"error": "admin access required"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
callerIDF, ok := (*claims)["id"].(float64)
|
callerIDF, ok := (*claims)["id"].(float64)
|
||||||
if !ok {
|
if !ok {
|
||||||
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user id in claims"})
|
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user id in claims"})
|
||||||
|
|||||||
@@ -71,33 +71,34 @@ func main() {
|
|||||||
store := handlers.Store{DB: db, SpotifyAuth: spotifyAuth, SpotifyClient: spotifyClient, ClaudeClient: claudeClient, Auth: auth, Notes: notes}
|
store := handlers.Store{DB: db, SpotifyAuth: spotifyAuth, SpotifyClient: spotifyClient, ClaudeClient: claudeClient, Auth: auth, Notes: notes}
|
||||||
|
|
||||||
protected := r.Group("/", store.AuthMiddlewear)
|
protected := r.Group("/", store.AuthMiddlewear)
|
||||||
|
admin := r.Group("/", store.AuthMiddlewear, store.AdminMiddleware)
|
||||||
|
|
||||||
// FAVORITES
|
// FAVORITES
|
||||||
r.GET("/favorites", store.GetFavorites)
|
r.GET("/favorites", store.GetFavorites)
|
||||||
protected.POST("/favorites", store.CreateFavorite)
|
admin.POST("/favorites", store.CreateFavorite)
|
||||||
|
|
||||||
// ROWING
|
// ROWING
|
||||||
r.GET("/rowing", store.GetRowing)
|
r.GET("/rowing", store.GetRowing)
|
||||||
protected.POST("/rowing", store.CreateRowing)
|
admin.POST("/rowing", store.CreateRowing)
|
||||||
|
|
||||||
// ACTIVITIES
|
// ACTIVITIES
|
||||||
r.GET("/activity", store.GetActivity)
|
r.GET("/activity", store.GetActivity)
|
||||||
protected.POST("/activity", store.CreateActivity)
|
admin.POST("/activity", store.CreateActivity)
|
||||||
|
|
||||||
// POSTS
|
// POSTS
|
||||||
r.GET("/posts", store.GetPosts)
|
r.GET("/posts", store.GetPosts)
|
||||||
protected.POST("/posts", store.CreatePost)
|
admin.POST("/posts", store.CreatePost)
|
||||||
r.GET("/posts/:id", store.GetPost)
|
r.GET("/posts/:id", store.GetPost)
|
||||||
protected.PUT("/posts/:id", store.UpdatePost)
|
admin.PUT("/posts/:id", store.UpdatePost)
|
||||||
protected.DELETE("/posts/:id", store.DeletePost)
|
admin.DELETE("/posts/:id", store.DeletePost)
|
||||||
|
|
||||||
// USERS
|
// USERS
|
||||||
r.GET("/user/:id", store.GetUser)
|
r.GET("/user/:id", store.GetUser)
|
||||||
protected.PUT("/user/:id", store.UpdateUser)
|
admin.PUT("/user/:id", store.UpdateUser)
|
||||||
protected.DELETE("/user/:id", store.DeleteUser)
|
admin.DELETE("/user/:id", store.DeleteUser)
|
||||||
r.GET("/user", store.GetUsers)
|
r.GET("/user", store.GetUsers)
|
||||||
protected.POST("/user", store.CreateUser)
|
admin.POST("/user", store.CreateUser)
|
||||||
protected.PATCH("/user/:id/admin", store.SetUserAdmin)
|
admin.PATCH("/user/:id/admin", store.SetUserAdmin)
|
||||||
|
|
||||||
// AUTH
|
// AUTH
|
||||||
r.POST("/auth/login", store.Login)
|
r.POST("/auth/login", store.Login)
|
||||||
|
|||||||
@@ -1,28 +1,45 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import Button from "@/components/input/Button.vue";
|
import Button from "@/components/input/Button.vue";
|
||||||
import { ref, onMounted, computed } from "vue";
|
import { ref } from "vue";
|
||||||
import { useAuthStore } from "@/stores/auth";
|
import { useAuthStore } from "@/stores/auth";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
const auth = useAuthStore();
|
const auth = useAuthStore();
|
||||||
const username = ref("");
|
const username = ref("");
|
||||||
const password = ref("");
|
const password = ref("");
|
||||||
|
const message = ref("");
|
||||||
|
const error = ref("");
|
||||||
|
|
||||||
function handleLogin() {
|
async function handleCreate() {
|
||||||
auth.createUser(username.value, password.value);
|
message.value = "";
|
||||||
|
error.value = "";
|
||||||
|
try {
|
||||||
|
const res = await axios.post("/api/user", {
|
||||||
|
username: username.value,
|
||||||
|
password: password.value,
|
||||||
|
});
|
||||||
|
message.value = `User "${res.data.username}" created successfully.`;
|
||||||
|
username.value = "";
|
||||||
|
password.value = "";
|
||||||
|
} catch (err) {
|
||||||
|
error.value = err.response?.data?.message || "Failed to create user.";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-if="auth.loggedIn" class="flex flex-col">
|
<div v-if="auth.loggedIn && auth.user.admin" class="flex flex-col">
|
||||||
<h1>Logged in</h1>
|
|
||||||
<p>{{ auth.user.id }}</p>
|
|
||||||
<p>{{ auth.user.username }}</p>
|
|
||||||
<p>{{ auth.user.admin }}</p>
|
|
||||||
</div>
|
|
||||||
<div v-else class="flex flex-col">
|
|
||||||
<h1>Create User</h1>
|
<h1>Create User</h1>
|
||||||
|
<p v-if="message" class="text-green-500">{{ message }}</p>
|
||||||
|
<p v-if="error" class="text-red-500">{{ error }}</p>
|
||||||
<input type="text" v-model="username" placeholder="Username" />
|
<input type="text" v-model="username" placeholder="Username" />
|
||||||
<input type="password" v-model="password" placeholder="Password" />
|
<input type="password" v-model="password" placeholder="Password" />
|
||||||
<Button @click="handleLogin">Create Account</Button>
|
<Button @click="handleCreate">Create Account</Button>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="auth.loggedIn" class="flex flex-col">
|
||||||
|
<p>You do not have permission to create users.</p>
|
||||||
|
</div>
|
||||||
|
<div v-else class="flex flex-col">
|
||||||
|
<p>You must be logged in as an admin to create users.</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user