From: Michael Tremer Date: Fri, 23 Nov 2018 23:11:24 +0000 (+0000) Subject: wiki: Add ACLs X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=11afe905c5fab195f67191f960d14aef4cf8d55a;p=ipfire.org.git wiki: Add ACLs Signed-off-by: Michael Tremer --- diff --git a/src/backend/wiki.py b/src/backend/wiki.py index 33ed4040..7962ea0b 100644 --- a/src/backend/wiki.py +++ b/src/backend/wiki.py @@ -42,10 +42,20 @@ class Wiki(misc.Object): return self._get_page("SELECT * FROM wiki WHERE page = %s \ ORDER BY timestamp DESC LIMIT 1", page) - def get_recent_changes(self, limit=None): - return self._get_pages("SELECT * FROM wiki \ + def get_recent_changes(self, account, limit=None): + pages = self._get_pages("SELECT * FROM wiki \ WHERE timestamp >= NOW() - INTERVAL '4 weeks' \ - ORDER BY timestamp DESC LIMIT %s", limit) + ORDER BY timestamp DESC") + + for page in pages: + if not page.check_acl(account): + continue + + yield page + + limit -= 1 + if not limit: + break def create_page(self, page, author, content, changes=None, address=None): page = Page.sanitise_page_name(page) @@ -71,16 +81,26 @@ class Wiki(misc.Object): return ret - def search(self, query, limit=None): + def search(self, query, account=None, limit=None): query = util.parse_search_query(query) res = self._get_pages("SELECT wiki.* FROM wiki_search_index search_index \ LEFT JOIN wiki ON search_index.wiki_id = wiki.id \ WHERE search_index.document @@ to_tsquery('english', %s) \ - ORDER BY ts_rank(search_index.document, to_tsquery('english', %s)) DESC \ - LIMIT %s", query, query, limit) + ORDER BY ts_rank(search_index.document, to_tsquery('english', %s)) DESC", + query, query) - return list(res) + for page in res: + # Skip any pages the user doesn't have permission for + if not page.check_acl(account): + continue + + # Return any other pages + yield page + + limit -= 1 + if not limit: + break def refresh(self): """ @@ -88,6 +108,28 @@ class Wiki(misc.Object): """ self.db.execute("REFRESH MATERIALIZED VIEW wiki_search_index") + # ACL + + def check_acl(self, page, account): + res = self.db.query("SELECT * FROM wiki_acls \ + WHERE %s ILIKE (path || '%%') ORDER BY LENGTH(path) DESC LIMIT 1", page) + + for row in res: + # Access not permitted when user is not logged in + if not account: + return False + + # If user is in a matching group, we grant permission + for group in row.groups: + if group in account.groups: + return True + + # Otherwise access is not permitted + return False + + # If no ACLs are found, we permit access + return True + # Files def _get_files(self, query, *args): @@ -285,6 +327,11 @@ class Page(misc.Object): def changes(self): return self.data.changes + # ACL + + def check_acl(self, account): + return self.backend.wiki.check_acl(self.page, account) + # Sidebar @lazy_property diff --git a/src/web/wiki.py b/src/web/wiki.py index bc59b25a..4f92be24 100644 --- a/src/web/wiki.py +++ b/src/web/wiki.py @@ -11,6 +11,10 @@ class ActionUploadHandler(auth.CacheMixin, base.BaseHandler): def post(self): path = self.get_argument("path") + # Check permissions + if not self.backend.wiki.check_acl(path, self.current_user): + raise tornado.web.HTTPError(403, "Access to %s not allowed for %s" % (path, self.current_user)) + try: filename, data, mimetype = self.get_file("file") @@ -30,6 +34,10 @@ class ActionUploadHandler(auth.CacheMixin, base.BaseHandler): class FilesHandler(auth.CacheMixin, base.BaseHandler): @tornado.web.authenticated def get(self, path): + # Check permissions + if not self.backend.wiki.check_acl(path, self.current_user): + raise tornado.web.HTTPError(403, "Access to %s not allowed for %s" % (path, self.current_user)) + files = self.backend.wiki.get_files(path) self.render("wiki/files/index.html", path=path, files=files) @@ -41,6 +49,11 @@ class FileHandler(base.BaseHandler): return self.get_argument("action", None) def get(self, path): + # Check permissions + if not self.backend.wiki.check_acl(path, self.current_user): + raise tornado.web.HTTPError(403, "Access to %s not allowed for %s" % (path, self.current_user)) + + # Fetch the file file = self.backend.wiki.get_file_by_path(path) if not file: raise tornado.web.HTTPError(404, "Could not find %s" % path) @@ -85,6 +98,10 @@ class PageHandler(auth.CacheMixin, base.BaseHandler): @tornado.web.removeslash def get(self, page): + # Check permissions + if not self.backend.wiki.check_acl(page, self.current_user): + raise tornado.web.HTTPError(403, "Access to %s not allowed for %s" % (page, self.current_user)) + # Check if we are asked to render a certain revision revision = self.get_argument("revision", None) @@ -121,6 +138,10 @@ class PageHandler(auth.CacheMixin, base.BaseHandler): @tornado.web.authenticated def post(self, page): + # Check permissions + if not self.backend.wiki.check_acl(page, self.current_user): + raise tornado.web.HTTPError(403, "Access to %s not allowed for %s" % (page, self.current_user)) + content = self.get_argument("content", None) changes = self.get_argument("changes") @@ -145,14 +166,14 @@ class SearchHandler(auth.CacheMixin, base.BaseHandler): def get(self): q = self.get_argument("q") - pages = self.backend.wiki.search(q, limit=50) + pages = self.backend.wiki.search(q, account=self.current_user, limit=50) - self.render("wiki/search-results.html", q=q, pages=pages) + self.render("wiki/search-results.html", q=q, pages=list(pages)) class RecentChangesHandler(auth.CacheMixin, base.BaseHandler): def get(self): - recent_changes = self.backend.wiki.get_recent_changes(limit=50) + recent_changes = self.backend.wiki.get_recent_changes(self.current_user, limit=50) self.render("wiki/recent-changes.html", recent_changes=recent_changes)