import async from "async"; // eslint-disable-next-line import moduleManager from "../../index"; const permissions = {}; permissions.dj = { "stations.autofill": true, "stations.blacklist": true, "stations.index": true, "stations.playback.toggle": true, "stations.queue.remove": true, "stations.queue.reposition": true, "stations.queue.reset": true, "stations.request": true, "stations.skip": true, "stations.view": true, "stations.view.manage": true }; permissions.owner = { ...permissions.dj, "stations.djs.add": true, "stations.djs.remove": true, "stations.remove": true, "stations.update": true }; permissions.moderator = { ...permissions.owner, "admin.view": true, "admin.view.import": true, "admin.view.news": true, "admin.view.playlists": true, "admin.view.punishments": true, "admin.view.reports": true, "admin.view.songs": true, "admin.view.stations": true, "admin.view.users": true, "admin.view.youtubeVideos": true, "admin.view.soundcloudTracks": true, "apis.searchDiscogs": true, "news.create": true, "news.update": true, "playlists.create.admin": true, "playlists.get": true, "playlists.update.displayName": true, "playlists.update.privacy": true, "playlists.songs.add": true, "playlists.songs.remove": true, "playlists.songs.reposition": true, "playlists.view.others": true, "punishments.banIP": true, "punishments.get": true, "reports.get": true, "reports.update": true, "songs.create": true, "songs.get": true, "songs.update": true, "songs.verify": true, "stations.create.official": true, "stations.index": false, "stations.index.other": true, "stations.remove": false, "users.get": true, "users.ban": true, "users.requestPasswordReset": true, "users.resendVerifyEmail": true, "users.update": true, "youtube.requestSetAdmin": true }; permissions.admin = { ...permissions.moderator, "admin.view.dataRequests": true, "admin.view.statistics": true, "admin.view.youtube": true, "admin.view.soundcloud": true, "admin.view.spotify": true, "dataRequests.resolve": true, "media.recalculateAllRatings": true, "media.removeImportJobs": true, "news.remove": true, "playlists.clearAndRefill": true, "playlists.clearAndRefillAll": true, "playlists.createMissing": true, "playlists.deleteOrphaned": true, "playlists.removeAdmin": true, "playlists.requestOrphanedPlaylistSongs": true, "punishments.deactivate": true, "reports.remove": true, "songs.remove": true, "songs.updateAll": true, "stations.clearEveryStationQueue": true, "stations.remove": true, "users.remove": true, "users.remove.sessions": true, "users.update.restricted": true, "utils.getModules": true, "youtube.getApiRequest": true, "youtube.resetStoredApiRequests": true, "youtube.removeStoredApiRequest": true, "youtube.removeVideos": true }; export const hasPermission = async (permission, session, stationId) => { const CacheModule = moduleManager.modules.cache; const DBModule = moduleManager.modules.db; const StationsModule = moduleManager.modules.stations; const UtilsModule = moduleManager.modules.utils; const userModel = await DBModule.runJob("GET_MODEL", { modelName: "user" }, this); return new Promise((resolve, reject) => { async.waterfall( [ next => { let userId; if (typeof session === "object") { if (session.userId) userId = session.userId; else CacheModule.runJob( "HGET", { table: "sessions", key: session.sessionId }, this ) .then(_session => { if (_session && _session.userId) userId = _session.userId; }) .catch(next); } else userId = session; if (!userId) return next("User ID required."); return userModel.findOne({ _id: userId }, next); }, (user, next) => { if (!user) return next("Login required."); if (!stationId) return next(null, [user.role]); return StationsModule.runJob("GET_STATION", { stationId }, this) .then(station => { if (!station) return next("Station not found."); if (station.type === "community" && station.owner === user._id.toString()) return next(null, [user.role, "owner"]); if (station.djs.find(dj => dj === user._id.toString())) return next(null, [user.role, "dj"]); if (user.role === "admin" || user.role === "moderator") return next(null, [user.role]); return next("Invalid permissions."); }) .catch(next); }, (roles, next) => { if (!roles) return next("Role required."); let permissionFound; roles.forEach(role => { if (permissions[role] && permissions[role][permission]) permissionFound = true; }); if (permissionFound) return next(); return next("Insufficient permissions."); } ], async err => { const userId = typeof session === "object" ? session.userId || session.sessionId : session; if (err) { err = await UtilsModule.runJob("GET_ERROR", { error: err }, this); UtilsModule.log( "INFO", "HAS_PERMISSION", `User "${userId}" does not have required permission "${permission}". "${err}"` ); return reject(err); } UtilsModule.log( "INFO", "HAS_PERMISSION", `User "${userId}" has required permission "${permission}".`, false ); return resolve(); } ); }); }; export const useHasPermission = (options, destination) => async function useHasPermission(session, ...args) { const UtilsModule = moduleManager.modules.utils; const permission = typeof options === "object" ? options.permission : options; const cb = args[args.length - 1]; async.waterfall( [ next => { if (!session || !session.sessionId) return next("Login required."); return hasPermission(permission, session) .then(() => next()) .catch(next); } ], async err => { if (err) { err = await UtilsModule.runJob("GET_ERROR", { error: err }, this); this.log( "INFO", "USE_HAS_PERMISSION", `User "${session.userId}" does not have required permission "${permission}". "${err}"` ); return cb({ status: "error", message: err }); } this.log( "INFO", "USE_HAS_PERMISSION", `User "${session.userId}" has required permission "${permission}".`, false ); return destination.apply(this, [session].concat(args)); } ); }; export const getUserPermissions = async (session, stationId) => { const CacheModule = moduleManager.modules.cache; const DBModule = moduleManager.modules.db; const StationsModule = moduleManager.modules.stations; const UtilsModule = moduleManager.modules.utils; const userModel = await DBModule.runJob("GET_MODEL", { modelName: "user" }, this); return new Promise((resolve, reject) => { async.waterfall( [ next => { let userId; if (typeof session === "object") { if (session.userId) userId = session.userId; else CacheModule.runJob( "HGET", { table: "sessions", key: session.sessionId }, this ) .then(_session => { if (_session && _session.userId) userId = _session.userId; }) .catch(next); } else userId = session; if (!userId) return next("User ID required."); return userModel.findOne({ _id: userId }, next); }, (user, next) => { if (!user) return next("Login required."); if (!stationId) return next(null, [user.role]); return StationsModule.runJob("GET_STATION", { stationId }, this) .then(station => { if (!station) return next("Station not found."); if (station.type === "community" && station.owner === user._id.toString()) return next(null, [user.role, "owner"]); if (station.djs.find(dj => dj === user._id.toString())) return next(null, [user.role, "dj"]); if (user.role === "admin" || user.role === "moderator") return next(null, [user.role]); return next("Invalid permissions."); }) .catch(next); }, (roles, next) => { if (!roles) return next("Role required."); let rolePermissions = {}; roles.forEach(role => { if (permissions[role]) rolePermissions = { ...rolePermissions, ...permissions[role] }; }); return next(null, rolePermissions); } ], async (err, rolePermissions) => { const userId = typeof session === "object" ? session.userId || session.sessionId : session; if (err) { err = await UtilsModule.runJob("GET_ERROR", { error: err }, this); UtilsModule.log( "INFO", "GET_USER_PERMISSIONS", `Failed to get permissions for user "${userId}". "${err}"` ); return reject(err); } UtilsModule.log("INFO", "GET_USER_PERMISSIONS", `Fetched permissions for user "${userId}".`, false); return resolve(rolePermissions); } ); }); };