hasPermission.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. import async from "async";
  2. // eslint-disable-next-line
  3. import moduleManager from "../../index";
  4. const permissions = {};
  5. permissions.dj = {
  6. "stations.view": true,
  7. "stations.view.manage": true,
  8. "stations.skip": true,
  9. "stations.pause": true,
  10. "stations.resume": true,
  11. "stations.addToQueue": true,
  12. "stations.removeFromQueue": true,
  13. "stations.repositionSongInQueue": true,
  14. "stations.autofillPlaylist": true,
  15. "stations.removeAutofillPlaylist": true,
  16. "stations.blacklistPlaylist": true,
  17. "stations.removeBlacklistedPlaylist": true,
  18. "stations.index": true,
  19. "stations.getPlaylist": true
  20. };
  21. permissions.owner = {
  22. ...permissions.dj,
  23. "stations.update": true,
  24. "stations.remove": true
  25. };
  26. permissions.moderator = {
  27. ...permissions.owner,
  28. "songs.length": true,
  29. "songs.getData": true,
  30. "songs.getSongFromId": true,
  31. "songs.getSongsFromYoutubeIds": true,
  32. "songs.create": true,
  33. "songs.update": true,
  34. "songs.verify": true,
  35. "songs.verifyMany": true,
  36. "songs.unverify": true,
  37. "songs.unverifyMany": true,
  38. "songs.getGenres": true,
  39. "songs.editGenres": true,
  40. "songs.getArtists": true,
  41. "songs.editArtists": true,
  42. "songs.getTags": true,
  43. "songs.editTags": true,
  44. "apis.searchDiscogs": true,
  45. "apis.joinAdminRoom.songs": true,
  46. "apis.joinAdminRoom.stations": true,
  47. "apis.joinAdminRoom.reports": true,
  48. "apis.joinAdminRoom.news": true,
  49. "apis.joinAdminRoom.playlists": true,
  50. "apis.joinAdminRoom.punishments": true,
  51. "apis.joinAdminRoom.youtubeVideos": true,
  52. "apis.joinAdminRoom.import": true,
  53. "media.getImportJobs": true,
  54. "news.getData": true,
  55. "news.create": true,
  56. "news.update": true,
  57. "playlists.getData": true,
  58. "playlists.searchOfficial": true,
  59. "playlists.updatePrivacy": true,
  60. "playlists.updateDisplayName": false,
  61. "playlists.getPlaylist": true,
  62. "playlists.repositionSong": true,
  63. "playlists.addSongToPlaylist": true,
  64. "playlists.addSetToPlaylist": true,
  65. "playlists.removeSongFromPlaylist": true,
  66. "playlists.view.others": true,
  67. "punishments.getData": true,
  68. "punishments.getPunishmentsForUser": true,
  69. "punishments.findOne": true,
  70. "punishments.banIP": true,
  71. "reports.getData": true,
  72. "reports.findOne": true,
  73. "reports.getReportsForSong": true,
  74. "reports.resolve": true,
  75. "reports.toggleIssue": true,
  76. "stations.getData": true,
  77. "stations.resetQueue": true,
  78. "stations.remove": false,
  79. "stations.index": false,
  80. "stations.index.other": true,
  81. "stations.create.official": true,
  82. "youtube.getVideos": true,
  83. "youtube.requestSetAdmin": true,
  84. "admin.view": true
  85. };
  86. permissions.admin = {
  87. ...permissions.moderator,
  88. "songs.updateAll": true,
  89. "songs.remove": true,
  90. "songs.removeMany": true,
  91. "apis.joinAdminRoom.users": true,
  92. "apis.joinAdminRoom.statistics": true,
  93. "apis.joinAdminRoom.youtube": true,
  94. "dataRequests.getData": true,
  95. "dataRequests.resolve": true,
  96. "media.recalculateAllRatings": true,
  97. "media.removeImportJobs": true,
  98. "news.remove": true,
  99. "playlists.removeAdmin": true,
  100. "playlists.deleteOrphanedStationPlaylists": true,
  101. "playlists.deleteOrphanedGenrePlaylists": true,
  102. "playlists.requestOrphanedPlaylistSongs": true,
  103. "playlists.clearAndRefillStationPlaylist": true,
  104. "playlists.clearAndRefillGenrePlaylist": true,
  105. "playlists.clearAndRefillAllStationPlaylists": true,
  106. "playlists.clearAndRefillAllGenrePlaylists": true,
  107. "playlists.createMissingGenrePlaylists": true,
  108. "reports.remove": true,
  109. "stations.clearEveryStationQueue": true,
  110. "stations.remove": true,
  111. "users.getData": true,
  112. "users.adminRemove": true,
  113. "users.getUserFromId": true,
  114. "users.updateRole": true,
  115. "users.adminRequestPasswordReset": true,
  116. "users.resendVerifyEmail": true,
  117. "users.banUserById": true,
  118. "users.removeSessions": true,
  119. "users.updateUsername": true,
  120. "users.updateEmail": true,
  121. "users.updateName": true,
  122. "users.updateLocation": true,
  123. "users.updateBio": true,
  124. "users.updateAvatar": true,
  125. "utils.getModules": true,
  126. "utils.getModule": true,
  127. "youtube.getQuotaStatus": true,
  128. "youtube.getQuotaChartData": true,
  129. "youtube.getApiRequests": true,
  130. "youtube.getApiRequest": true,
  131. "youtube.resetStoredApiRequests": true,
  132. "youtube.removeStoredApiRequest": true,
  133. "youtube.removeVideos": true
  134. };
  135. export const hasPermission = async (permission, session, stationId) => {
  136. const CacheModule = moduleManager.modules.cache;
  137. const DBModule = moduleManager.modules.db;
  138. const StationsModule = moduleManager.modules.stations;
  139. const UtilsModule = moduleManager.modules.utils;
  140. const userModel = await DBModule.runJob("GET_MODEL", { modelName: "user" }, this);
  141. return new Promise((resolve, reject) => {
  142. async.waterfall(
  143. [
  144. next => {
  145. let userId;
  146. if (typeof session === "object") {
  147. if (session.userId) userId = session.userId;
  148. else
  149. CacheModule.runJob(
  150. "HGET",
  151. {
  152. table: "sessions",
  153. key: session.sessionId
  154. },
  155. this
  156. )
  157. .then(_session => {
  158. if (_session && _session.userId) userId = _session.userId;
  159. })
  160. .catch(next);
  161. } else userId = session;
  162. if (!userId) return next("User ID required.");
  163. return userModel.findOne({ _id: userId }, next);
  164. },
  165. (user, next) => {
  166. if (!user) return next("Login required.");
  167. if (!stationId) return next(null, [user.role]);
  168. return StationsModule.runJob("GET_STATION", { stationId }, this)
  169. .then(station => {
  170. if (!station) return next("Station not found.");
  171. if (station.type === "community" && station.owner === user._id.toString())
  172. return next(null, [user.role, "owner"]);
  173. // if (station.type === "community" && station.djs.find(userId))
  174. // return next(null, [user.role, "dj"]);
  175. if (user.role === "admin" || user.role === "moderator") return next(null, [user.role]);
  176. return next("Invalid permissions.");
  177. })
  178. .catch(next);
  179. },
  180. (roles, next) => {
  181. if (!roles) return next("Role required.");
  182. let permissionFound;
  183. roles.forEach(role => {
  184. if (permissions[role] && permissions[role][permission]) permissionFound = true;
  185. });
  186. if (permissionFound) return next();
  187. return next("Insufficient permissions.");
  188. }
  189. ],
  190. async err => {
  191. const userId = typeof session === "object" ? session.userId || session.sessionId : session;
  192. if (err) {
  193. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  194. UtilsModule.log(
  195. "INFO",
  196. "HAS_PERMISSION",
  197. `User "${userId}" does not have required permission "${permission}". "${err}"`
  198. );
  199. return reject(err);
  200. }
  201. UtilsModule.log(
  202. "INFO",
  203. "HAS_PERMISSION",
  204. `User "${userId}" has required permission "${permission}".`,
  205. false
  206. );
  207. return resolve();
  208. }
  209. );
  210. });
  211. };
  212. export const useHasPermission = (options, destination) =>
  213. async function useHasPermission(session, ...args) {
  214. const UtilsModule = moduleManager.modules.utils;
  215. const permission = typeof options === "object" ? options.permission : options;
  216. const stationId = typeof options === "object" ? options.stationId : null;
  217. const cb = args[args.length - 1];
  218. async.waterfall(
  219. [
  220. next => {
  221. if (!session || !session.sessionId) return next("Login required.");
  222. return hasPermission(permission, session, stationId)
  223. .then(() => next())
  224. .catch(next);
  225. }
  226. ],
  227. async err => {
  228. if (err) {
  229. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  230. this.log(
  231. "INFO",
  232. "USE_HAS_PERMISSION",
  233. `User "${session.userId}" does not have required permission "${permission}". "${err}"`
  234. );
  235. return cb({ status: "error", message: err });
  236. }
  237. this.log(
  238. "INFO",
  239. "USE_HAS_PERMISSION",
  240. `User "${session.userId}" has required permission "${permission}".`,
  241. false
  242. );
  243. return destination.apply(this, [session].concat(args));
  244. }
  245. );
  246. };
  247. export const getUserPermissions = async (session, stationId) => {
  248. const CacheModule = moduleManager.modules.cache;
  249. const DBModule = moduleManager.modules.db;
  250. const StationsModule = moduleManager.modules.stations;
  251. const UtilsModule = moduleManager.modules.utils;
  252. const userModel = await DBModule.runJob("GET_MODEL", { modelName: "user" }, this);
  253. return new Promise((resolve, reject) => {
  254. async.waterfall(
  255. [
  256. next => {
  257. let userId;
  258. if (typeof session === "object") {
  259. if (session.userId) userId = session.userId;
  260. else
  261. CacheModule.runJob(
  262. "HGET",
  263. {
  264. table: "sessions",
  265. key: session.sessionId
  266. },
  267. this
  268. )
  269. .then(_session => {
  270. if (_session && _session.userId) userId = _session.userId;
  271. })
  272. .catch(next);
  273. } else userId = session;
  274. if (!userId) return next("User ID required.");
  275. return userModel.findOne({ _id: userId }, next);
  276. },
  277. (user, next) => {
  278. if (!user) return next("Login required.");
  279. if (!stationId) return next(null, [user.role]);
  280. return StationsModule.runJob("GET_STATION", { stationId }, this)
  281. .then(station => {
  282. if (!station) return next("Station not found.");
  283. if (station.type === "community" && station.owner === user._id.toString())
  284. return next(null, [user.role, "owner"]);
  285. // if (station.type === "community" && station.djs.find(userId))
  286. // return next(null, [user.role, "dj"]);
  287. if (user.role === "admin" || user.role === "moderator") return next(null, [user.role]);
  288. return next("Invalid permissions.");
  289. })
  290. .catch(next);
  291. },
  292. (roles, next) => {
  293. if (!roles) return next("Role required.");
  294. let rolePermissions = {};
  295. roles.forEach(role => {
  296. if (permissions[role]) rolePermissions = { ...rolePermissions, ...permissions[role] };
  297. });
  298. return next(null, rolePermissions);
  299. }
  300. ],
  301. async (err, rolePermissions) => {
  302. const userId = typeof session === "object" ? session.userId || session.sessionId : session;
  303. if (err) {
  304. err = await UtilsModule.runJob("GET_ERROR", { error: err }, this);
  305. UtilsModule.log(
  306. "INFO",
  307. "GET_USER_PERMISSIONS",
  308. `Failed to get permissions for user "${userId}". "${err}"`
  309. );
  310. return reject(err);
  311. }
  312. UtilsModule.log("INFO", "GET_USER_PERMISSIONS", `Fetched permissions for user "${userId}".`, false);
  313. return resolve(rolePermissions);
  314. }
  315. );
  316. });
  317. };