Assignment 4: tests, interface refactor, e2e suite

This commit is contained in:
Zagar Nashrullah
2026-10-03 19:35:30 +07:00
parent 6e7e35d9a5
commit 004bf4e292
25 changed files with 2644 additions and 151 deletions
+40 -30
View File
@@ -5,13 +5,14 @@ import (
"net/http"
"strings"
"5803025048/W04/internal/store"
"5803025048/W04/internal/models"
)
type contextKey string
const UserIDKey contextKey = "user_id"
// GetUserID mengambil user id yang disimpan RequireAuth di context request.
func GetUserID(ctx context.Context) string {
if val, ok := ctx.Value(UserIDKey).(string); ok {
return val
@@ -19,35 +20,54 @@ func GetUserID(ctx context.Context) string {
return ""
}
type AuthMiddleware struct {
sessionStore *store.SessionStore
// SessionLookup adalah interface kecil yang dibutuhkan middleware:
// mencari session (dan user id-nya) dari sebuah token.
// *store.SessionStore memenuhi interface ini tanpa perubahan.
type SessionLookup interface {
Get(ctx context.Context, token string) (*models.Session, error)
}
func NewAuthMiddleware(sessionStore *store.SessionStore) *AuthMiddleware {
return &AuthMiddleware{sessionStore: sessionStore}
type AuthMiddleware struct {
sessions SessionLookup
}
func NewAuthMiddleware(sessions SessionLookup) *AuthMiddleware {
return &AuthMiddleware{sessions: sessions}
}
const bearerPrefix = "Bearer "
// BearerToken mengambil token dari header "Authorization: Bearer <token>".
// Mengembalikan false jika header tidak ada, skema bukan Bearer, atau token kosong.
func BearerToken(r *http.Request) (string, bool) {
h := r.Header.Get("Authorization")
if len(h) <= len(bearerPrefix) || !strings.EqualFold(h[:len(bearerPrefix)], bearerPrefix) {
return "", false
}
token := strings.TrimSpace(h[len(bearerPrefix):])
if token == "" {
return "", false
}
return token, true
}
func unauthorized(w http.ResponseWriter) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusUnauthorized)
_, _ = w.Write([]byte(`{"error":"Unauthorized"}` + "\n"))
}
func (m *AuthMiddleware) RequireAuth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
token, ok := BearerToken(r)
if !ok {
unauthorized(w)
return
}
token := authHeader
if strings.HasPrefix(strings.ToLower(authHeader), "bearer ") {
token = authHeader[7:]
}
token = strings.TrimSpace(token)
if token == "" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
session, err := m.sessionStore.Get(r.Context(), token)
session, err := m.sessions.Get(r.Context(), token)
if err != nil || session == nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
unauthorized(w)
return
}
@@ -55,13 +75,3 @@ func (m *AuthMiddleware) RequireAuth(next http.Handler) http.Handler {
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func (m *AuthMiddleware) RequireAuthFunc(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
m.RequireAuth(next).ServeHTTP(w, r)
}
}
func RequireAuth(sessionStore *store.SessionStore) func(http.Handler) http.Handler {
return NewAuthMiddleware(sessionStore).RequireAuth
}