add owner handle
Signed-off-by: GnomeZworc <nicolas.boufidjeline@g3e.fr>
This commit is contained in:
parent
0f2d7126eb
commit
f862abe5a4
12 changed files with 274 additions and 55 deletions
|
|
@ -35,7 +35,11 @@ func (h *AccountHandler) RegisterRoutes(mux *http.ServeMux) {
|
|||
// --- Comptes maîtres ---
|
||||
|
||||
func (h *AccountHandler) list(w http.ResponseWriter, r *http.Request) {
|
||||
accounts, err := h.store.ListAccounts(r.Context())
|
||||
ownerID, ok := requireOwner(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
accounts, err := h.store.ListAccounts(r.Context(), ownerID)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "failed to list accounts")
|
||||
return
|
||||
|
|
@ -44,11 +48,22 @@ func (h *AccountHandler) list(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (h *AccountHandler) get(w http.ResponseWriter, r *http.Request) {
|
||||
ownerID, ok := requireOwner(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
id, err := parseID(r)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid id")
|
||||
return
|
||||
}
|
||||
if err := h.store.VerifyAccountOwner(r.Context(), id, ownerID); errors.Is(err, pgx.ErrNoRows) {
|
||||
writeError(w, http.StatusNotFound, "account not found")
|
||||
return
|
||||
} else if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "failed to get account")
|
||||
return
|
||||
}
|
||||
account, err := h.store.GetAccountWithEnvelopes(r.Context(), id)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
writeError(w, http.StatusNotFound, "account not found")
|
||||
|
|
@ -62,6 +77,10 @@ func (h *AccountHandler) get(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (h *AccountHandler) create(w http.ResponseWriter, r *http.Request) {
|
||||
ownerID, ok := requireOwner(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var p store.CreateAccountParams
|
||||
if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid request body")
|
||||
|
|
@ -71,7 +90,7 @@ func (h *AccountHandler) create(w http.ResponseWriter, r *http.Request) {
|
|||
writeError(w, http.StatusBadRequest, "nom and type are required")
|
||||
return
|
||||
}
|
||||
account, err := h.store.CreateAccount(r.Context(), p)
|
||||
account, err := h.store.CreateAccount(r.Context(), p, ownerID)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "failed to create account")
|
||||
return
|
||||
|
|
@ -80,6 +99,10 @@ func (h *AccountHandler) create(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (h *AccountHandler) update(w http.ResponseWriter, r *http.Request) {
|
||||
ownerID, ok := requireOwner(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
id, err := parseID(r)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid id")
|
||||
|
|
@ -90,7 +113,7 @@ func (h *AccountHandler) update(w http.ResponseWriter, r *http.Request) {
|
|||
writeError(w, http.StatusBadRequest, "invalid request body")
|
||||
return
|
||||
}
|
||||
account, err := h.store.UpdateAccount(r.Context(), id, p)
|
||||
account, err := h.store.UpdateAccount(r.Context(), id, p, ownerID)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
writeError(w, http.StatusNotFound, "account not found")
|
||||
return
|
||||
|
|
@ -103,12 +126,16 @@ func (h *AccountHandler) update(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (h *AccountHandler) delete(w http.ResponseWriter, r *http.Request) {
|
||||
ownerID, ok := requireOwner(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
id, err := parseID(r)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid id")
|
||||
return
|
||||
}
|
||||
if err := h.store.DeleteAccount(r.Context(), id); err != nil {
|
||||
if err := h.store.DeleteAccount(r.Context(), id, ownerID); err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "failed to delete account")
|
||||
return
|
||||
}
|
||||
|
|
@ -118,11 +145,22 @@ func (h *AccountHandler) delete(w http.ResponseWriter, r *http.Request) {
|
|||
// --- Enveloppes ---
|
||||
|
||||
func (h *AccountHandler) listEnvelopes(w http.ResponseWriter, r *http.Request) {
|
||||
ownerID, ok := requireOwner(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
id, err := parseID(r)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid id")
|
||||
return
|
||||
}
|
||||
if err := h.store.VerifyAccountOwner(r.Context(), id, ownerID); errors.Is(err, pgx.ErrNoRows) {
|
||||
writeError(w, http.StatusNotFound, "account not found")
|
||||
return
|
||||
} else if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "failed to verify account")
|
||||
return
|
||||
}
|
||||
envelopes, err := h.store.ListEnvelopes(r.Context(), id)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "failed to list envelopes")
|
||||
|
|
@ -132,6 +170,10 @@ func (h *AccountHandler) listEnvelopes(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (h *AccountHandler) createEnvelope(w http.ResponseWriter, r *http.Request) {
|
||||
ownerID, ok := requireOwner(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
masterID, err := parseID(r)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid id")
|
||||
|
|
@ -146,7 +188,7 @@ func (h *AccountHandler) createEnvelope(w http.ResponseWriter, r *http.Request)
|
|||
writeError(w, http.StatusBadRequest, "nom is required")
|
||||
return
|
||||
}
|
||||
envelope, err := h.store.CreateEnvelope(r.Context(), masterID, p)
|
||||
envelope, err := h.store.CreateEnvelope(r.Context(), masterID, p, ownerID)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
writeError(w, http.StatusNotFound, "account not found")
|
||||
return
|
||||
|
|
@ -163,6 +205,10 @@ func (h *AccountHandler) createEnvelope(w http.ResponseWriter, r *http.Request)
|
|||
}
|
||||
|
||||
func (h *AccountHandler) updateEnvelope(w http.ResponseWriter, r *http.Request) {
|
||||
ownerID, ok := requireOwner(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
id, err := parseID(r)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid id")
|
||||
|
|
@ -173,7 +219,7 @@ func (h *AccountHandler) updateEnvelope(w http.ResponseWriter, r *http.Request)
|
|||
writeError(w, http.StatusBadRequest, "invalid request body")
|
||||
return
|
||||
}
|
||||
envelope, err := h.store.UpdateEnvelope(r.Context(), id, p)
|
||||
envelope, err := h.store.UpdateEnvelope(r.Context(), id, p, ownerID)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
writeError(w, http.StatusNotFound, "envelope not found")
|
||||
return
|
||||
|
|
@ -186,12 +232,16 @@ func (h *AccountHandler) updateEnvelope(w http.ResponseWriter, r *http.Request)
|
|||
}
|
||||
|
||||
func (h *AccountHandler) deleteEnvelope(w http.ResponseWriter, r *http.Request) {
|
||||
ownerID, ok := requireOwner(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
id, err := parseID(r)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid id")
|
||||
return
|
||||
}
|
||||
if err := h.store.DeleteAccount(r.Context(), id); err != nil {
|
||||
if err := h.store.DeleteAccount(r.Context(), id, ownerID); err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "failed to delete envelope")
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package handler
|
|||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"git.g3e.fr/H6N/account/internal/auth"
|
||||
)
|
||||
|
||||
func writeJSON(w http.ResponseWriter, status int, v any) {
|
||||
|
|
@ -14,3 +16,13 @@ func writeJSON(w http.ResponseWriter, status int, v any) {
|
|||
func writeError(w http.ResponseWriter, status int, msg string) {
|
||||
writeJSON(w, status, map[string]string{"error": msg})
|
||||
}
|
||||
|
||||
// requireOwner extrait l'owner du contexte ou écrit 401 et retourne false.
|
||||
func requireOwner(w http.ResponseWriter, r *http.Request) (int32, bool) {
|
||||
id, ok := auth.OwnerFromContext(r.Context())
|
||||
if !ok {
|
||||
writeError(w, http.StatusUnauthorized, "missing owner")
|
||||
return 0, false
|
||||
}
|
||||
return id, true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,26 @@ import (
|
|||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
|
||||
func (h *SnapshotHandler) verifyAccountOwner(w http.ResponseWriter, r *http.Request) (int32, int32, bool) {
|
||||
ownerID, ok := requireOwner(w, r)
|
||||
if !ok {
|
||||
return 0, 0, false
|
||||
}
|
||||
accountID, err := parseID(r)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid account id")
|
||||
return 0, 0, false
|
||||
}
|
||||
if err := h.engine.Store().VerifyAccountOwner(r.Context(), accountID, ownerID); errors.Is(err, pgx.ErrNoRows) {
|
||||
writeError(w, http.StatusNotFound, "account not found")
|
||||
return 0, 0, false
|
||||
} else if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "failed to verify account")
|
||||
return 0, 0, false
|
||||
}
|
||||
return ownerID, accountID, true
|
||||
}
|
||||
|
||||
type SnapshotHandler struct {
|
||||
engine *snapshot.Engine
|
||||
}
|
||||
|
|
@ -37,9 +57,8 @@ func parseDateParam(r *http.Request, key string) (time.Time, error) {
|
|||
// Liste les snapshots agrégés (valeur totale) d'un compte.
|
||||
// from/to optionnels : sans borne → pas de filtre de ce côté.
|
||||
func (h *SnapshotHandler) listAccountSnapshots(w http.ResponseWriter, r *http.Request) {
|
||||
accountID, err := parseID(r)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid account id")
|
||||
_, accountID, ok := h.verifyAccountOwner(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
from, err := parseDateParam(r, "from")
|
||||
|
|
@ -67,9 +86,8 @@ func (h *SnapshotHandler) listAccountSnapshots(w http.ResponseWriter, r *http.Re
|
|||
// GET /accounts/{id}/snapshots/positions?from=YYYY-MM-DD&to=YYYY-MM-DD
|
||||
// Liste les positions détaillées (par instrument) d'un compte.
|
||||
func (h *SnapshotHandler) listPositionSnapshots(w http.ResponseWriter, r *http.Request) {
|
||||
accountID, err := parseID(r)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid account id")
|
||||
_, accountID, ok := h.verifyAccountOwner(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
from, err := parseDateParam(r, "from")
|
||||
|
|
@ -98,9 +116,8 @@ func (h *SnapshotHandler) listPositionSnapshots(w http.ResponseWriter, r *http.R
|
|||
// Recalcule les snapshots du compte depuis sa date d'invalidation jusqu'à aujourd'hui.
|
||||
// Retourne 204 si aucune invalidation n'est en attente.
|
||||
func (h *SnapshotHandler) recomputeAccount(w http.ResponseWriter, r *http.Request) {
|
||||
accountID, err := parseID(r)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid account id")
|
||||
_, accountID, ok := h.verifyAccountOwner(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,11 +28,22 @@ func (h *TransactionHandler) RegisterRoutes(mux *http.ServeMux) {
|
|||
}
|
||||
|
||||
func (h *TransactionHandler) listForAccount(w http.ResponseWriter, r *http.Request) {
|
||||
ownerID, ok := requireOwner(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
accountID, err := parseID(r)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid account id")
|
||||
return
|
||||
}
|
||||
if err := h.store.VerifyAccountOwner(r.Context(), accountID, ownerID); errors.Is(err, pgx.ErrNoRows) {
|
||||
writeError(w, http.StatusNotFound, "account not found")
|
||||
return
|
||||
} else if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "failed to verify account")
|
||||
return
|
||||
}
|
||||
f := store.AccountTransactionFilters{
|
||||
Validated: parseBoolParam(r, "validated"),
|
||||
Pending: parseBoolParam(r, "pending"),
|
||||
|
|
@ -48,12 +59,16 @@ func (h *TransactionHandler) listForAccount(w http.ResponseWriter, r *http.Reque
|
|||
}
|
||||
|
||||
func (h *TransactionHandler) get(w http.ResponseWriter, r *http.Request) {
|
||||
ownerID, ok := requireOwner(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
id, err := parseTxID(r)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid id")
|
||||
return
|
||||
}
|
||||
tx, err := h.store.GetTransaction(r.Context(), id)
|
||||
tx, err := h.store.GetTransaction(r.Context(), id, ownerID)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
writeError(w, http.StatusNotFound, "transaction not found")
|
||||
return
|
||||
|
|
@ -66,6 +81,10 @@ func (h *TransactionHandler) get(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (h *TransactionHandler) create(w http.ResponseWriter, r *http.Request) {
|
||||
ownerID, ok := requireOwner(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
var p store.CreateTransactionParams
|
||||
if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid request body")
|
||||
|
|
@ -79,6 +98,25 @@ func (h *TransactionHandler) create(w http.ResponseWriter, r *http.Request) {
|
|||
writeError(w, http.StatusBadRequest, "date is required")
|
||||
return
|
||||
}
|
||||
// Vérifier que les comptes référencés appartiennent à l'owner.
|
||||
if p.AccountSourceID != nil {
|
||||
if err := h.store.VerifyAccountOwner(r.Context(), *p.AccountSourceID, ownerID); errors.Is(err, pgx.ErrNoRows) {
|
||||
writeError(w, http.StatusNotFound, "source account not found")
|
||||
return
|
||||
} else if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "failed to verify source account")
|
||||
return
|
||||
}
|
||||
}
|
||||
if p.AccountDestID != nil {
|
||||
if err := h.store.VerifyAccountOwner(r.Context(), *p.AccountDestID, ownerID); errors.Is(err, pgx.ErrNoRows) {
|
||||
writeError(w, http.StatusNotFound, "dest account not found")
|
||||
return
|
||||
} else if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "failed to verify dest account")
|
||||
return
|
||||
}
|
||||
}
|
||||
tx, err := h.store.CreateTransaction(r.Context(), p)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "failed to create transaction")
|
||||
|
|
@ -88,6 +126,10 @@ func (h *TransactionHandler) create(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (h *TransactionHandler) update(w http.ResponseWriter, r *http.Request) {
|
||||
ownerID, ok := requireOwner(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
id, err := parseTxID(r)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid id")
|
||||
|
|
@ -102,7 +144,7 @@ func (h *TransactionHandler) update(w http.ResponseWriter, r *http.Request) {
|
|||
writeError(w, http.StatusBadRequest, "label is required")
|
||||
return
|
||||
}
|
||||
tx, err := h.store.UpdateTransaction(r.Context(), id, p)
|
||||
tx, err := h.store.UpdateTransaction(r.Context(), id, p, ownerID)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
writeError(w, http.StatusNotFound, "transaction not found")
|
||||
return
|
||||
|
|
@ -115,12 +157,19 @@ func (h *TransactionHandler) update(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (h *TransactionHandler) delete(w http.ResponseWriter, r *http.Request) {
|
||||
ownerID, ok := requireOwner(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
id, err := parseTxID(r)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid id")
|
||||
return
|
||||
}
|
||||
if err := h.store.DeleteTransaction(r.Context(), id); err != nil {
|
||||
if err := h.store.DeleteTransaction(r.Context(), id, ownerID); errors.Is(err, pgx.ErrNoRows) {
|
||||
writeError(w, http.StatusNotFound, "transaction not found")
|
||||
return
|
||||
} else if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "failed to delete transaction")
|
||||
return
|
||||
}
|
||||
|
|
@ -128,6 +177,10 @@ func (h *TransactionHandler) delete(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (h *TransactionHandler) validate(w http.ResponseWriter, r *http.Request) {
|
||||
ownerID, ok := requireOwner(w, r)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
id, err := parseTxID(r)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusBadRequest, "invalid id")
|
||||
|
|
@ -140,7 +193,7 @@ func (h *TransactionHandler) validate(w http.ResponseWriter, r *http.Request) {
|
|||
writeError(w, http.StatusBadRequest, "invalid request body")
|
||||
return
|
||||
}
|
||||
tx, err := h.store.SetValidated(r.Context(), id, body.Validated)
|
||||
tx, err := h.store.SetValidated(r.Context(), id, body.Validated, ownerID)
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
writeError(w, http.StatusNotFound, "transaction not found")
|
||||
return
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue