hasPermission.js 10 KB

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