Skip to content

Commit fe171e1

Browse files
committed
Ownership fixes in cell related endpoints
1 parent 31b6481 commit fe171e1

4 files changed

Lines changed: 311 additions & 76 deletions

File tree

controllers/cell_controller.go

Lines changed: 156 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net/http"
88
"strings"
99

10+
"github.com/Thanus-Kumaar/controller_microservice_v2/middleware"
1011
"github.com/Thanus-Kumaar/controller_microservice_v2/modules"
1112
"github.com/Thanus-Kumaar/controller_microservice_v2/pkg"
1213
"github.com/Thanus-Kumaar/controller_microservice_v2/pkg/models"
@@ -42,17 +43,17 @@ var validOutputTypes = map[string]struct{}{
4243

4344
// CellController holds the dependencies for the cell handlers.
4445
type CellController struct {
45-
Module *modules.CellModule
46-
Logger zerolog.Logger
46+
Module *modules.CellModule
47+
Logger zerolog.Logger
48+
NotebookModule *modules.NotebookModule // Added NotebookModule
4749
}
4850

49-
50-
5151
// NewCellController creates and returns a new CellController.
52-
func NewCellController(module *modules.CellModule, logger zerolog.Logger) *CellController {
52+
func NewCellController(module *modules.CellModule, logger zerolog.Logger, notebookModule *modules.NotebookModule) *CellController {
5353
return &CellController{
54-
Module: module,
55-
Logger: logger,
54+
Module: module,
55+
Logger: logger,
56+
NotebookModule: notebookModule,
5657
}
5758
}
5859

@@ -61,14 +62,29 @@ func (c *CellController) UpdateCellsHandler(w http.ResponseWriter, r *http.Reque
6162
notebookID, err := uuid.Parse(notebookIDStr)
6263
if err != nil {
6364
pkg.WriteJSONResponseWithLogger(
64-
w,
65-
http.StatusBadRequest,
66-
map[string]string{"error": "Invalid notebook ID"},
65+
w,
66+
http.StatusBadRequest,
67+
map[string]string{"error": "Invalid notebook ID"},
6768
&c.Logger,
6869
)
6970
return
7071
}
7172

73+
user, ok := r.Context().Value(middleware.UserContextKey).(*middleware.User)
74+
if !ok || user.ID == "" {
75+
c.Logger.Error().Msg("user not found in context for cell update")
76+
http.Error(w, "User not found in context", http.StatusUnauthorized)
77+
return
78+
}
79+
80+
// Verify ownership of the notebook
81+
_, err = c.NotebookModule.GetNotebookByID(r.Context(), notebookIDStr, user.ID)
82+
if err != nil {
83+
c.Logger.Error().Err(err).Str("notebook_id", notebookIDStr).Msg("Notebook not found or not owned by user for cell update")
84+
http.Error(w, "Notebook not found or not owned by user", http.StatusNotFound)
85+
return
86+
}
87+
7288
body, err := io.ReadAll(r.Body)
7389
if err != nil {
7490
c.Logger.Error().Err(err).Msg("Failed to read request body")
@@ -86,37 +102,52 @@ func (c *CellController) UpdateCellsHandler(w http.ResponseWriter, r *http.Reque
86102
if err := json.Unmarshal(body, &req); err != nil {
87103
c.Logger.Error().Err(err).Msg("Failed to decode request body")
88104
pkg.WriteJSONResponseWithLogger(
89-
w,
90-
http.StatusBadRequest,
91-
map[string]string{"error": "Invalid request body"},
105+
w,
106+
http.StatusBadRequest,
107+
map[string]string{"error": "Invalid request body"},
92108
&c.Logger,
93109
)
94110
return
95111
}
96112

97113
c.Logger.Info().Interface("decoded_request", req).Msg("Decoded request body")
98114

99-
if err := c.Module.UpdateCells(r.Context(), notebookID, &req); err != nil {
115+
if err := c.Module.UpdateCells(r.Context(), notebookID, &req, user.ID); err != nil { // Pass userID to module
100116
c.Logger.Error().Err(err).Msg("Failed to update cells")
101117
pkg.WriteJSONResponseWithLogger(
102-
w,
103-
http.StatusInternalServerError,
104-
map[string]string{"error": "Failed to update cells"},
118+
w,
119+
http.StatusInternalServerError,
120+
map[string]string{"error": "Failed to update cells"},
105121
&c.Logger,
106122
)
107123
return
108124
}
109125

110126
pkg.WriteJSONResponseWithLogger(w, http.StatusOK, map[string]string{"status": "success"}, &c.Logger)
111127
}
112-
113128
func (c *CellController) CreateCellHandler(w http.ResponseWriter, r *http.Request) {
129+
user, ok := r.Context().Value(middleware.UserContextKey).(*middleware.User)
130+
if !ok || user.ID == "" {
131+
c.Logger.Error().Msg("user not found in context for cell creation")
132+
http.Error(w, "User not found in context", http.StatusUnauthorized)
133+
return
134+
}
135+
114136
var req models.CreateCellRequest
115137
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
116138
pkg.WriteJSONResponseWithLogger(w, http.StatusBadRequest, map[string]string{"error": "Invalid request body"}, &c.Logger)
117139
return
118140
}
119141

142+
// Verify ownership of the parent notebook
143+
notebookIDStr := req.NotebookID.String()
144+
_, err := c.NotebookModule.GetNotebookByID(r.Context(), notebookIDStr, user.ID)
145+
if err != nil {
146+
c.Logger.Error().Err(err).Str("notebook_id", notebookIDStr).Msg("Notebook not found or not owned by user for cell creation")
147+
http.Error(w, "Notebook not found or not owned by user", http.StatusNotFound)
148+
return
149+
}
150+
120151
if _, ok := validCellTypes[req.CellType]; !ok {
121152
allowedTypes := make([]string, 0, len(validCellTypes))
122153
for k := range validCellTypes {
@@ -127,7 +158,7 @@ func (c *CellController) CreateCellHandler(w http.ResponseWriter, r *http.Reques
127158
return
128159
}
129160

130-
cell, err := c.Module.CreateCell(r.Context(), &req)
161+
cell, err := c.Module.CreateCell(r.Context(), &req, user.ID) // Pass userID to module
131162
if err != nil {
132163
c.Logger.Error().Err(err).Msg("Failed to create cell")
133164
pkg.WriteJSONResponseWithLogger(w, http.StatusInternalServerError, map[string]string{"error": "Failed to create cell"}, &c.Logger)
@@ -145,7 +176,22 @@ func (c *CellController) GetCellsByNotebookIDHandler(w http.ResponseWriter, r *h
145176
return
146177
}
147178

148-
cells, err := c.Module.GetCellsByNotebookID(r.Context(), notebookID)
179+
user, ok := r.Context().Value(middleware.UserContextKey).(*middleware.User)
180+
if !ok || user.ID == "" {
181+
c.Logger.Error().Msg("user not found in context for getting cells by notebook ID")
182+
http.Error(w, "User not found in context", http.StatusUnauthorized)
183+
return
184+
}
185+
186+
// Verify ownership of the parent notebook
187+
_, err = c.NotebookModule.GetNotebookByID(r.Context(), notebookIDStr, user.ID)
188+
if err != nil {
189+
c.Logger.Error().Err(err).Str("notebook_id", notebookIDStr).Msg("Notebook not found or not owned by user for getting cells")
190+
http.Error(w, "Notebook not found or not owned by user", http.StatusNotFound)
191+
return
192+
}
193+
194+
cells, err := c.Module.GetCellsByNotebookID(r.Context(), notebookID, user.ID)
149195
if err != nil {
150196
c.Logger.Error().Err(err).Msg("Failed to get cells")
151197
pkg.WriteJSONResponseWithLogger(w, http.StatusInternalServerError, map[string]string{"error": "Failed to get cells"}, &c.Logger)
@@ -163,10 +209,17 @@ func (c *CellController) GetCellByIDHandler(w http.ResponseWriter, r *http.Reque
163209
return
164210
}
165211

166-
cell, err := c.Module.GetCellByID(r.Context(), cellID)
212+
user, ok := r.Context().Value(middleware.UserContextKey).(*middleware.User)
213+
if !ok || user.ID == "" {
214+
c.Logger.Error().Msg("user not found in context for getting cell by ID")
215+
http.Error(w, "User not found in context", http.StatusUnauthorized)
216+
return
217+
}
218+
219+
cell, err := c.Module.GetCellByID(r.Context(), cellID, user.ID) // Pass userID to module
167220
if err != nil {
168221
c.Logger.Error().Err(err).Msg("Failed to get cell")
169-
pkg.WriteJSONResponseWithLogger(w, http.StatusNotFound, map[string]string{"error": "Cell not found"}, &c.Logger)
222+
pkg.WriteJSONResponseWithLogger(w, http.StatusNotFound, map[string]string{"error": "Cell not found or not owned by user"}, &c.Logger)
170223
return
171224
}
172225

@@ -181,6 +234,21 @@ func (c *CellController) UpdateCellHandler(w http.ResponseWriter, r *http.Reques
181234
return
182235
}
183236

237+
user, ok := r.Context().Value(middleware.UserContextKey).(*middleware.User)
238+
if !ok || user.ID == "" {
239+
c.Logger.Error().Msg("user not found in context for updating cell")
240+
http.Error(w, "User not found in context", http.StatusUnauthorized)
241+
return
242+
}
243+
244+
// Retrieve the cell to get its notebook ID and implicitly check ownership via module call
245+
_, err = c.Module.GetCellByID(r.Context(), cellID, user.ID)
246+
if err != nil {
247+
c.Logger.Error().Err(err).Str("cell_id", cellIDStr).Msg("Failed to retrieve existing cell for update or not owned by user")
248+
pkg.WriteJSONResponseWithLogger(w, http.StatusNotFound, map[string]string{"error": "Cell not found or not owned by user"}, &c.Logger)
249+
return
250+
}
251+
184252
var req models.UpdateCellRequest
185253
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
186254
pkg.WriteJSONResponseWithLogger(w, http.StatusBadRequest, map[string]string{"error": "Invalid request body"}, &c.Logger)
@@ -199,7 +267,7 @@ func (c *CellController) UpdateCellHandler(w http.ResponseWriter, r *http.Reques
199267
}
200268
}
201269

202-
cell, err := c.Module.UpdateCell(r.Context(), cellID, &req)
270+
cell, err := c.Module.UpdateCell(r.Context(), cellID, &req, user.ID) // Pass userID to module
203271
if err != nil {
204272
c.Logger.Error().Err(err).Msg("Failed to update cell")
205273
pkg.WriteJSONResponseWithLogger(w, http.StatusInternalServerError, map[string]string{"error": "Failed to update cell"}, &c.Logger)
@@ -217,7 +285,22 @@ func (c *CellController) DeleteCellHandler(w http.ResponseWriter, r *http.Reques
217285
return
218286
}
219287

220-
if err := c.Module.DeleteCell(r.Context(), cellID); err != nil {
288+
user, ok := r.Context().Value(middleware.UserContextKey).(*middleware.User)
289+
if !ok || user.ID == "" {
290+
c.Logger.Error().Msg("user not found in context for deleting cell")
291+
http.Error(w, "User not found in context", http.StatusUnauthorized)
292+
return
293+
}
294+
295+
// Verify ownership before deleting
296+
_, err = c.Module.GetCellByID(r.Context(), cellID, user.ID)
297+
if err != nil {
298+
c.Logger.Error().Err(err).Str("cell_id", cellIDStr).Msg("Failed to retrieve existing cell for delete or not owned by user")
299+
pkg.WriteJSONResponseWithLogger(w, http.StatusNotFound, map[string]string{"error": "Cell not found or not owned by user"}, &c.Logger)
300+
return
301+
}
302+
303+
if err := c.Module.DeleteCell(r.Context(), cellID, user.ID); err != nil {
221304
c.Logger.Error().Err(err).Msg("Failed to delete cell")
222305
pkg.WriteJSONResponseWithLogger(w, http.StatusInternalServerError, map[string]string{"error": "Failed to delete cell"}, &c.Logger)
223306
return
@@ -227,6 +310,13 @@ func (c *CellController) DeleteCellHandler(w http.ResponseWriter, r *http.Reques
227310
}
228311

229312
func (c *CellController) CreateCellOutputHandler(w http.ResponseWriter, r *http.Request) {
313+
user, ok := r.Context().Value(middleware.UserContextKey).(*middleware.User)
314+
if !ok || user.ID == "" {
315+
c.Logger.Error().Msg("user not found in context for creating cell output")
316+
http.Error(w, "User not found in context", http.StatusUnauthorized)
317+
return
318+
}
319+
230320
var req models.CreateCellOutputRequest
231321
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
232322
c.Logger.Error().Err(err).Msg("CreateCellOutputHandler: Invalid request body")
@@ -235,6 +325,14 @@ func (c *CellController) CreateCellOutputHandler(w http.ResponseWriter, r *http.
235325
}
236326
c.Logger.Debug().Interface("request_body", req).Msg("CreateCellOutputHandler: Decoded request body")
237327

328+
// Verify ownership of the cell before creating an output for it
329+
_, err := c.Module.GetCellByID(r.Context(), req.CellID.ToUUID(), user.ID)
330+
if err != nil {
331+
c.Logger.Error().Err(err).Str("cell_id", req.CellID.ToUUID().String()).Msg("Cell not found or not owned by user for creating output")
332+
http.Error(w, "Cell not found or not owned by user", http.StatusNotFound)
333+
return
334+
}
335+
238336
if _, ok := validOutputTypes[req.Type]; !ok {
239337
allowedTypes := make([]string, 0, len(validOutputTypes))
240338
for k := range validOutputTypes {
@@ -246,7 +344,7 @@ func (c *CellController) CreateCellOutputHandler(w http.ResponseWriter, r *http.
246344
return
247345
}
248346
c.Logger.Debug().Str("cell_id", req.CellID.ToUUID().String()).Msg("CreateCellOutputHandler: Calling module to create cell output")
249-
output, err := c.Module.CreateCellOutput(r.Context(), &req)
347+
output, err := c.Module.CreateCellOutput(r.Context(), &req) // Ownership already checked
250348
if err != nil {
251349
c.Logger.Error().Err(err).Msg("CreateCellOutputHandler: Failed to create cell output")
252350
pkg.WriteJSONResponseWithLogger(w, http.StatusInternalServerError, map[string]string{"error": "Failed to create cell output"}, &c.Logger)
@@ -264,7 +362,22 @@ func (c *CellController) GetCellOutputsByCellIDHandler(w http.ResponseWriter, r
264362
return
265363
}
266364

267-
outputs, err := c.Module.GetCellOutputsByCellID(r.Context(), cellID)
365+
user, ok := r.Context().Value(middleware.UserContextKey).(*middleware.User)
366+
if !ok || user.ID == "" {
367+
c.Logger.Error().Msg("user not found in context for getting cell outputs")
368+
http.Error(w, "User not found in context", http.StatusUnauthorized)
369+
return
370+
}
371+
372+
// Verify ownership of the cell before getting outputs
373+
_, err = c.Module.GetCellByID(r.Context(), cellID, user.ID)
374+
if err != nil {
375+
c.Logger.Error().Err(err).Str("cell_id", cellID.String()).Msg("Cell not found or not owned by user for getting outputs")
376+
http.Error(w, "Cell not found or not owned by user", http.StatusNotFound)
377+
return
378+
}
379+
380+
outputs, err := c.Module.GetCellOutputsByCellID(r.Context(), cellID, user.ID)
268381
if err != nil {
269382
c.Logger.Error().Err(err).Msg("Failed to get cell outputs")
270383
pkg.WriteJSONResponseWithLogger(w, http.StatusInternalServerError, map[string]string{"error": "Failed to get cell outputs"}, &c.Logger)
@@ -282,7 +395,23 @@ func (c *CellController) DeleteCellOutputHandler(w http.ResponseWriter, r *http.
282395
return
283396
}
284397

285-
if err := c.Module.DeleteCellOutput(r.Context(), outputID); err != nil {
398+
user, ok := r.Context().Value(middleware.UserContextKey).(*middleware.User)
399+
if !ok || user.ID == "" {
400+
c.Logger.Error().Msg("user not found in context for deleting cell output")
401+
http.Error(w, "User not found in context", http.StatusUnauthorized)
402+
return
403+
}
404+
405+
// To verify ownership, we must check if the user owns the notebook associated with this output.
406+
// This requires getting the output, then its cell, then its notebook.
407+
_, err = c.Module.GetCellOutputByID(r.Context(), outputID, user.ID)
408+
if err != nil {
409+
c.Logger.Error().Err(err).Str("output_id", outputIDStr).Msg("Output not found or not owned by user")
410+
pkg.WriteJSONResponseWithLogger(w, http.StatusNotFound, map[string]string{"error": "Output not found or not owned by user"}, &c.Logger)
411+
return
412+
}
413+
414+
if err := c.Module.DeleteCellOutput(r.Context(), outputID, user.ID); err != nil {
286415
c.Logger.Error().Err(err).Msg("Failed to delete cell output")
287416
pkg.WriteJSONResponseWithLogger(w, http.StatusInternalServerError, map[string]string{"error": "Failed to delete cell output"}, &c.Logger)
288417
return

0 commit comments

Comments
 (0)