|
@@ -9,6 +9,7 @@ import { UserRole } from "../schemas/user";
|
|
|
import { StationType } from "../schemas/station";
|
|
|
import permissions from "../permissions";
|
|
|
import Job from "../Job";
|
|
|
+import { Models } from "../types/Models";
|
|
|
|
|
|
export default class APIModule extends BaseModule {
|
|
|
private _subscriptions: Record<string, Set<string>>;
|
|
@@ -187,28 +188,12 @@ export default class APIModule extends BaseModule {
|
|
|
};
|
|
|
}
|
|
|
|
|
|
- public async getUserPermissions(
|
|
|
- context: JobContext,
|
|
|
- { stationId }: { stationId?: Types.ObjectId }
|
|
|
- ) {
|
|
|
- const user = await context.getUser();
|
|
|
-
|
|
|
- const roles: (UserRole | "owner" | "dj")[] = [user.role];
|
|
|
+ public async getUserPermissions(context: JobContext) {
|
|
|
+ const user = await context.getUser().catch(() => null);
|
|
|
|
|
|
- if (stationId) {
|
|
|
- const Station = await context.getModel("station");
|
|
|
+ if (!user) return {};
|
|
|
|
|
|
- const station = await Station.findById(stationId);
|
|
|
-
|
|
|
- if (!station) throw new Error("Station not found");
|
|
|
-
|
|
|
- if (
|
|
|
- station.type === StationType.COMMUNITY &&
|
|
|
- station.owner === user._id
|
|
|
- )
|
|
|
- roles.push("owner");
|
|
|
- else if (station.djs.find(dj => dj === user._id)) roles.push("dj");
|
|
|
- }
|
|
|
+ const roles: UserRole[] = [user.role];
|
|
|
|
|
|
let rolePermissions: Record<string, boolean> = {};
|
|
|
roles.forEach(role => {
|
|
@@ -219,6 +204,93 @@ export default class APIModule extends BaseModule {
|
|
|
return rolePermissions;
|
|
|
}
|
|
|
|
|
|
+ public async getUserModelPermissions(
|
|
|
+ context: JobContext,
|
|
|
+ {
|
|
|
+ modelName,
|
|
|
+ modelId
|
|
|
+ }: { modelName: keyof Models; modelId?: Types.ObjectId }
|
|
|
+ ) {
|
|
|
+ const user = await context.getUser().catch(() => null);
|
|
|
+ const permissions = await context.getUserPermissions();
|
|
|
+
|
|
|
+ const Model = await context.getModel(modelName);
|
|
|
+
|
|
|
+ if (!Model) throw new Error("Model not found");
|
|
|
+
|
|
|
+ const model = modelId ? await Model.findById(modelId) : null;
|
|
|
+
|
|
|
+ if (modelId && !model) throw new Error("Model not found");
|
|
|
+
|
|
|
+ const jobs = await Promise.all(
|
|
|
+ Object.keys(this._moduleManager.getModule("data")?.getJobs() ?? {})
|
|
|
+ .filter(
|
|
|
+ jobName =>
|
|
|
+ jobName.startsWith(modelName.toString()) &&
|
|
|
+ (modelId ? true : !jobName.endsWith("ById"))
|
|
|
+ )
|
|
|
+ .map(async jobName => {
|
|
|
+ jobName = `data.${jobName}`;
|
|
|
+
|
|
|
+ let hasPermission = permissions[jobName];
|
|
|
+
|
|
|
+ if (!hasPermission && modelId)
|
|
|
+ hasPermission =
|
|
|
+ permissions[`${jobName}.*`] ||
|
|
|
+ permissions[`${jobName}.${modelId}`];
|
|
|
+
|
|
|
+ if (hasPermission) return [jobName, true];
|
|
|
+
|
|
|
+ const [, shortJobName] =
|
|
|
+ new RegExp(`^data.${modelName}.([A-z]+)`).exec(
|
|
|
+ jobName
|
|
|
+ ) ?? [];
|
|
|
+
|
|
|
+ const schemaOptions = (Model.schema.get("jobConfig") ?? {})[
|
|
|
+ shortJobName
|
|
|
+ ];
|
|
|
+ let options = schemaOptions?.hasPermission ?? [];
|
|
|
+
|
|
|
+ if (!Array.isArray(options)) options = [options];
|
|
|
+
|
|
|
+ hasPermission = await options.reduce(
|
|
|
+ async (previous, option) => {
|
|
|
+ if (await previous) return true;
|
|
|
+
|
|
|
+ if (option === "loggedIn" && user) return true;
|
|
|
+
|
|
|
+ if (option === "owner" && user && model) {
|
|
|
+ let ownerAttribute;
|
|
|
+
|
|
|
+ if (model.schema.path("createdBy"))
|
|
|
+ ownerAttribute = "createdBy";
|
|
|
+ else if (model.schema.path("owner"))
|
|
|
+ ownerAttribute = "owner";
|
|
|
+
|
|
|
+ if (ownerAttribute)
|
|
|
+ return (
|
|
|
+ model[ownerAttribute].toString() ===
|
|
|
+ user._id.toString()
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ if (typeof option === "boolean") return option;
|
|
|
+
|
|
|
+ if (typeof option === "function")
|
|
|
+ return option(model, user);
|
|
|
+
|
|
|
+ return false;
|
|
|
+ },
|
|
|
+ Promise.resolve(false)
|
|
|
+ );
|
|
|
+
|
|
|
+ return [jobName, !!hasPermission];
|
|
|
+ })
|
|
|
+ );
|
|
|
+
|
|
|
+ return Object.fromEntries(jobs);
|
|
|
+ }
|
|
|
+
|
|
|
private async _subscriptionCallback(channel: string, value?: any) {
|
|
|
const promises = [];
|
|
|
for await (const socketId of this._subscriptions[channel].values()) {
|