Skip to content

Instantly share code, notes, and snippets.

@tsibley
Created August 29, 2022 18:47
Show Gist options
  • Select an option

  • Save tsibley/a42514eca2a44a73e5bf7eb33cfe3235 to your computer and use it in GitHub Desktop.

Select an option

Save tsibley/a42514eca2a44a73e5bf7eb33cfe3235 to your computer and use it in GitHub Desktop.
diff --git a/src/app.js b/src/app.js
index 75b14b7..4a03fb4 100644
--- a/src/app.js
+++ b/src/app.js
@@ -20,6 +20,7 @@ const PRODUCTION = process.env.NODE_ENV === "production";
const CANARY_ORIGIN = process.env.CANARY_ORIGIN;
const authn = require("./authn");
+const authz = require("./authz");
const endpoints = require("./endpoints");
const {AuthzDenied} = require("./exceptions");
const {replacer: jsonReplacer} = require("./json");
@@ -45,7 +46,6 @@ const {
CoreStagingSource,
CommunitySource,
UrlDefinedSource,
- GroupSource,
} = sources;
const esc = encodeURIComponent;
@@ -279,13 +279,13 @@ app.routeAsync("/fetch/:authority/*")
*/
app.use("/groups/:groupName",
- setSource(req => new GroupSource(req.params.groupName)),
+ endpoints.groups.setGroup(req => req.params.groupName),
// Canonicalize the Group name.
(req, res, next) => {
const restOfUrl = req.url !== "/" ? req.url : "";
- const canonicalName = req.context.source.group.name;
+ const canonicalName = req.context.group.name;
const canonicalUrl = `/groups/${esc(canonicalName)}${restOfUrl}`;
return req.params.groupName !== canonicalName
@@ -301,7 +301,7 @@ app.routeAsync("/groups/:groupName")
;
app.use("/groups/:groupName/settings",
- endpoints.groups.setGroup(req => req.params.groupName));
+ authz.assertAuthorizedReq(req => [req.user, authz.actions.Read, req.context.group]));
app.routeAsync("/groups/:groupName/settings/members")
.getAsync(endpoints.groups.listMembers);
@@ -326,14 +326,18 @@ app.routeAsync("/groups/:groupName/narratives")
.getAsync((req, res) => res.redirect(`/groups/${esc(req.params.groupName)}`));
app.routeAsync("/groups/:groupName/narratives/*")
- .all(setNarrative(req => req.params[0]))
+ .all(
+ setSource(req => req.context.group.source()),
+ setNarrative(req => req.params[0]))
.getAsync(getNarrative)
.putAsync(putNarrative)
.deleteAsync(deleteNarrative)
;
app.routeAsync("/groups/:groupName/*")
- .all(setDataset(req => req.params[0]))
+ .all(
+ setSource(req => req.context.group.source()),
+ setDataset(req => req.params[0]))
.getAsync(getDataset)
.putAsync(putDataset)
.deleteAsync(deleteDataset)
diff --git a/src/authz/index.js b/src/authz/index.js
index 8aeeab2..85dc8b9 100644
--- a/src/authz/index.js
+++ b/src/authz/index.js
@@ -122,9 +122,31 @@ const assertAuthorized = (user, action, object) => {
};
+/**
+ * Express-style middleware that calls {@link assertAuthorized} with the (user,
+ * action, object) produced from the request by the given extractor function.
+ *
+ * @param {argsExtractor} argsExtractor - Function to extract (user, action,
+ * object) array from the request
+ * @returns {expressMiddleware}
+ * @throws {AuthzDenied}
+ */
+const assertAuthorizedReq = (argsExtractor) => (req, res, next) => {
+ assertAuthorized(...argsExtractor(req));
+ return next();
+};
+
+/**
+ * @callback argsExtractor
+ * @param {express.request} req
+ * @returns {Array} args - three element array of (user, action, object)
+ */
+
+
module.exports = {
authorized,
assertAuthorized,
+ assertAuthorizedReq,
evaluatePolicy,
actions,
diff --git a/src/endpoints/groups.js b/src/endpoints/groups.js
index 7a38e66..ad72d48 100644
--- a/src/endpoints/groups.js
+++ b/src/endpoints/groups.js
@@ -8,8 +8,6 @@ const {slurp} = require("../utils/iterators");
const setGroup = (nameExtractor) => (req, res, next) => {
const group = new Group(nameExtractor(req));
- authz.assertAuthorized(req.user, authz.actions.Read, group);
-
req.context.group = group;
return next();
};
diff --git a/src/groups.js b/src/groups.js
index 552c588..1512133 100644
--- a/src/groups.js
+++ b/src/groups.js
@@ -88,6 +88,18 @@ class Group {
}
+ /**
+ * Source for this Group.
+ */
+ source() {
+ // XXX FIXME re-consider ordering that puts this require here or if we'd
+ // rather shift a require around elsewhere or otherwise change the module
+ // dep graph.
+ const {GroupSource} = require("./sources");
+ return new GroupSource(this);
+ }
+
+
/**
* Policy for this Group itself. Separate from the policy for the group's
* Source, which is the container for the group's datasets and narratives.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment