Skip to content

Commit 7b6cd92

Browse files
authored
Forbid the deletion of stack indexes/views (#2966)
The stack needs a few CouchDB design docs for its own indexes and views. The front applications will no longer be able to delete them with the DELETE /data/:doctype/_design/:ddoc route.
1 parent 30a47ff commit 7b6cd92

File tree

3 files changed

+60
-0
lines changed

3 files changed

+60
-0
lines changed

pkg/couchdb/index.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,3 +334,19 @@ func InitGlobalDB() error {
334334
DefineViews(g, GlobalDB, globalViews)
335335
return g.Wait()
336336
}
337+
338+
// CheckDesignDocCanBeDeleted will return false for an index or view used by
339+
// the stack.
340+
func CheckDesignDocCanBeDeleted(doctype, name string) bool {
341+
for _, index := range Indexes {
342+
if doctype == index.Doctype && name == index.Request.DDoc {
343+
return false
344+
}
345+
}
346+
for _, view := range Views {
347+
if doctype == view.Doctype && name == view.Name {
348+
return false
349+
}
350+
}
351+
return true
352+
}

web/data/data.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,11 @@ func deleteDesignDoc(c echo.Context) error {
456456
"error": "You must pass a rev param",
457457
})
458458
}
459+
if !couchdb.CheckDesignDocCanBeDeleted(doctype, ddoc) {
460+
return c.JSON(http.StatusForbidden, echo.Map{
461+
"error": "This design doc cannot be deleted",
462+
})
463+
}
459464
return proxy(c, "_design/"+ddoc)
460465
}
461466

web/data/data_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,6 +1068,45 @@ func TestDeleteDesignDoc(t *testing.T) {
10681068
assert.Less(t, len(rows), nDesignDocs)
10691069
}
10701070

1071+
func TestCannotDeleteStackDesignDoc(t *testing.T) {
1072+
url := ts.URL + "/data/io.cozy.files/_design_docs"
1073+
req, _ := http.NewRequest("GET", url, nil)
1074+
req.Header.Add("Authorization", "Bearer "+token)
1075+
req.Header.Set("Content-Type", "application/json")
1076+
out, res, err := doRequest(req, nil)
1077+
assert.NoError(t, err)
1078+
assert.Equal(t, 200, res.StatusCode)
1079+
rows := out["rows"].([]interface{})
1080+
var indexRev, viewRev string
1081+
for _, row := range rows {
1082+
info := row.(map[string]interface{})
1083+
if info["id"] == "_design/dir-by-path" {
1084+
value := info["value"].(map[string]interface{})
1085+
indexRev = value["rev"].(string)
1086+
}
1087+
if info["id"] == "_design/by-parent-type-name" {
1088+
value := info["value"].(map[string]interface{})
1089+
viewRev = value["rev"].(string)
1090+
}
1091+
}
1092+
1093+
url = ts.URL + "/data/io.cozy.files/_design/dir-by-path?rev=" + indexRev
1094+
req, _ = http.NewRequest("DELETE", url, nil)
1095+
req.Header.Add("Authorization", "Bearer "+token)
1096+
req.Header.Set("Content-Type", "application/json")
1097+
_, res, err = doRequest(req, nil)
1098+
assert.NoError(t, err)
1099+
assert.Equal(t, 403, res.StatusCode)
1100+
1101+
url = ts.URL + "/data/io.cozy.files/_design/by-parent-type-name?rev=" + viewRev
1102+
req, _ = http.NewRequest("DELETE", url, nil)
1103+
req.Header.Add("Authorization", "Bearer "+token)
1104+
req.Header.Set("Content-Type", "application/json")
1105+
_, res, err = doRequest(req, nil)
1106+
assert.NoError(t, err)
1107+
assert.Equal(t, 403, res.StatusCode)
1108+
}
1109+
10711110
func TestCopyDesignDoc(t *testing.T) {
10721111
srcDdoc := "indextocopy"
10731112
targetID := "_design/indexcopied"

0 commit comments

Comments
 (0)