mirror of
https://github.com/gabehf/Koito.git
synced 2026-03-07 13:38:15 -08:00
Merge pull request #18 from Ian-J-S/p1/ian-admin_access
Admin-only routes and authorization middleware tests
This commit is contained in:
commit
62bcbc6082
3 changed files with 106 additions and 7 deletions
65
engine/admin_test.go
Normal file
65
engine/admin_test.go
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
package engine_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gabehf/koito/internal/db"
|
||||
"github.com/gabehf/koito/internal/models"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Minimal test: non-admin gets 403 on delete artist; admin succeeds.
|
||||
func TestAdminProtectedDeleteArtist(t *testing.T) {
|
||||
// create non-admin user
|
||||
_, err := store.SaveUser(context.Background(), db.SaveUserOpts{
|
||||
Username: "regular_user",
|
||||
Password: "password123",
|
||||
Role: models.UserRoleUser,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// login as non-admin
|
||||
form := url.Values{}
|
||||
form.Set("username", "regular_user")
|
||||
form.Set("password", "password123")
|
||||
resp, err := http.DefaultClient.Post(host()+"/apis/web/v1/login", "application/x-www-form-urlencoded", strings.NewReader(form.Encode()))
|
||||
require.NoError(t, err)
|
||||
require.Len(t, resp.Cookies(), 1)
|
||||
session := resp.Cookies()[0].Value
|
||||
|
||||
// create an artist to delete
|
||||
artist, err := store.SaveArtist(context.Background(), db.SaveArtistOpts{Name: "ToBeDeleted"})
|
||||
require.NoError(t, err)
|
||||
|
||||
// attempt delete as non-admin, expect Forbidden
|
||||
req, err := http.NewRequest("DELETE", host()+fmt.Sprintf("/apis/web/v1/artist?id=%d", artist.ID), nil)
|
||||
require.NoError(t, err)
|
||||
req.AddCookie(&http.Cookie{Name: "koito_session", Value: session})
|
||||
resp2, err := http.DefaultClient.Do(req)
|
||||
require.NoError(t, err)
|
||||
defer resp2.Body.Close()
|
||||
require.Equal(t, http.StatusForbidden, resp2.StatusCode)
|
||||
|
||||
// login as admin (default user 'test')
|
||||
form2 := url.Values{}
|
||||
form2.Set("username", "test")
|
||||
form2.Set("password", "testuser123")
|
||||
resp3, err := http.DefaultClient.Post(host()+"/apis/web/v1/login", "application/x-www-form-urlencoded", strings.NewReader(form2.Encode()))
|
||||
require.NoError(t, err)
|
||||
require.Len(t, resp3.Cookies(), 1)
|
||||
adminSession := resp3.Cookies()[0].Value
|
||||
|
||||
// attempt delete as admin - expect NoContent
|
||||
req2, err := http.NewRequest("DELETE", host()+fmt.Sprintf("/apis/web/v1/artist?id=%d", artist.ID), nil)
|
||||
require.NoError(t, err)
|
||||
req2.AddCookie(&http.Cookie{Name: "koito_session", Value: adminSession})
|
||||
resp4, err := http.DefaultClient.Do(req2)
|
||||
require.NoError(t, err)
|
||||
defer resp4.Body.Close()
|
||||
require.Equal(t, http.StatusNoContent, resp4.StatusCode)
|
||||
}
|
||||
28
engine/middleware/authorize.go
Normal file
28
engine/middleware/authorize.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gabehf/koito/internal/logger"
|
||||
"github.com/gabehf/koito/internal/models"
|
||||
"github.com/gabehf/koito/internal/utils"
|
||||
)
|
||||
|
||||
// ensures the authenticated user has admin role
|
||||
func RequireAdmin(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
l := logger.FromContext(r.Context())
|
||||
user := GetUserFromContext(r.Context())
|
||||
if user == nil {
|
||||
l.Debug().Msg("RequireAdmin: Unauthorized access (no user)")
|
||||
utils.WriteError(w, "unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
if user.Role != models.UserRoleAdmin {
|
||||
l.Debug().Msgf("RequireAdmin: Forbidden - user %d is not admin", user.ID)
|
||||
utils.WriteError(w, "forbidden", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
|
@ -81,13 +81,19 @@ func bindRoutes(
|
|||
r.Get("/export", handlers.ExportHandler(db))
|
||||
r.Post("/replace-image", handlers.ReplaceImageHandler(db))
|
||||
r.Patch("/album", handlers.UpdateAlbumHandler(db))
|
||||
r.Post("/merge/tracks", handlers.MergeTracksHandler(db))
|
||||
r.Post("/merge/albums", handlers.MergeReleaseGroupsHandler(db))
|
||||
r.Post("/merge/artists", handlers.MergeArtistsHandler(db))
|
||||
r.Delete("/artist", handlers.DeleteArtistHandler(db))
|
||||
r.Post("/artists/primary", handlers.SetPrimaryArtistHandler(db))
|
||||
r.Delete("/album", handlers.DeleteAlbumHandler(db))
|
||||
r.Delete("/track", handlers.DeleteTrackHandler(db))
|
||||
|
||||
// Admin-only routes
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Use(middleware.RequireAdmin)
|
||||
r.Post("/merge/tracks", handlers.MergeTracksHandler(db))
|
||||
r.Post("/merge/albums", handlers.MergeReleaseGroupsHandler(db))
|
||||
r.Post("/merge/artists", handlers.MergeArtistsHandler(db))
|
||||
r.Delete("/artist", handlers.DeleteArtistHandler(db))
|
||||
r.Post("/artists/primary", handlers.SetPrimaryArtistHandler(db))
|
||||
r.Delete("/album", handlers.DeleteAlbumHandler(db))
|
||||
r.Delete("/track", handlers.DeleteTrackHandler(db))
|
||||
})
|
||||
|
||||
r.Post("/listen", handlers.SubmitListenWithIDHandler(db))
|
||||
r.Delete("/listen", handlers.DeleteListenHandler(db))
|
||||
r.Post("/aliases", handlers.CreateAliasHandler(db))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue