permission/Permissions.js

  1. var Collection = require("./../Collection").Collection;
  2. var Permission = require("./Permission").Permission;
  3. var Users = require("./../user/Users").Users;
  4. var CORS = require("./../CORS").CORS;
  5. var Q = require("q");
  6. /**
  7. * @class Permissions
  8. * @name Permissions
  9. *
  10. * @description A collection of open ended permissions which can be applied to any object
  11. * (usually a corpus, but could be a datalist, or datum, or datumField). A permission
  12. * can be thought of roughly as a phrase:
  13. * <pre>
  14. *
  15. * User [{username: "lingllama", gravatar: "123"}]
  16. * has permission ["admin","write","read","comment","export","import",etc]
  17. * to "corpus"/"datalist"/"datum"/"datumField"
  18. *
  19. * <pre>
  20. *
  21. * @property {Permission} admins Users who can perform admin operations on the corpus/datalist/datum/datumField.
  22. * @property {Permission} exporters Users who can export the items in a corpus/datalist/datum/datumField.
  23. * @property {Permission} writers Users who can modify the items in a corpus/datalist/datum/datumField.
  24. * @property {Permission} commenters Users who can comment on the items in a corpus/datalist/datum/datumField.
  25. * @property {Permission} readers Users who can read the items in a corpus/datalist/datum/datumField.
  26. *
  27. * @property {Object} viewAsBasePermissionSystem This is just syntactic sugar which points to the actual
  28. * permission system. The actual permission system should be used by apps who want provide a power user or
  29. * fine grained and open ended control over the permissions, or whose users want to understand
  30. * the fine graned control at a lower level.
  31. * - Kind of like Unix permissions.
  32. * @property {Object} viewAsDataBasePermissionSystem This is syntactic sugar which makes
  33. * the permission system look like there is an implicative relationship betwween roles.
  34. * - Kind of like PhP/MySQL systems.
  35. * @property {Object} viewAsGroupedPermissionSystem This is syntactic sugar which shows users in
  36. * only one category (readOnly, writeOnly, read/write, admins) it also makes
  37. * the permission system look like there is an implicative relationship betwween roles, but with some hint that
  38. * there are some rare roles (such as write only) for crowdsourcing/non-standard data entry contexts.
  39. * - Kind of like PhP/MySQL systems.
  40. * @property {Object} viewAsEmailingTeamPermissionSystem This might be the way that
  41. * some computational linguistics teams will understand the permission system best,
  42. * but the words `collaborator` and `contributor` are so similar that thus far no one
  43. * has used these terms (that we know of).
  44. * - Kind of like GitHub.
  45. *
  46. * @extends Collection
  47. * @tutorial tests/permission/PermissionTest.js
  48. */
  49. var Permissions = function Permissions(options) {
  50. if (!this._fieldDBtype) {
  51. this._fieldDBtype = "Permissions";
  52. }
  53. this.debug("Constructing Permissions ", options);
  54. this.cacheTimeLength = 1000;
  55. Collection.apply(this, arguments);
  56. };
  57. Permissions.prototype = Object.create(Collection.prototype, /** @lends Permissions.prototype */ {
  58. constructor: {
  59. value: Permissions
  60. },
  61. INTERNAL_MODELS: {
  62. value: {
  63. item: Permission,
  64. usersNotOnTeam: Users,
  65. allUsers: Users
  66. }
  67. },
  68. /*** Syntactic sugar for research teams how have been collaborating via email
  69. sharing (To, BCC, CC) and/or Dropbox and/or github ***/
  70. /**
  71. * Syntactic sugar for users who have reader+commenter+writer permissions.
  72. *
  73. * This is a common permission in version control systems
  74. * where the user can do everything, including categorize data, assign data cleaning
  75. * to team members, edit information about the database but cannot perform
  76. * except admin functions such as adding other users to the database.
  77. *
  78. * This should be the default permission for most users on the corpus, as the corpus
  79. * is version controlled any action can be undone if users use their power too much,
  80. * they can be downgraded to a collaborator or read only role.
  81. *
  82. * Email collaboration analog: This is for users who would be in a
  83. * To or CC category for emails,
  84. *
  85. * Dropbox analog: This is for users who would be invited to join a shared
  86. * folder on Dropbox.
  87. *
  88. * @type {Permission}
  89. */
  90. contributors: {
  91. get: function() {
  92. return this.readerCommenterWriters;
  93. },
  94. set: function() {
  95. this.warn("contributors cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
  96. }
  97. },
  98. /**
  99. * Syntactic sugar for users who have reader+commenter permissions.
  100. *
  101. * This is a common permission used by research teams where an external reviewer,
  102. * or a second language consultant can review and coment on the data,
  103. * but cannot modify the data itself.
  104. *
  105. * Email collaboration analog: This is for users who would be in a BCC category
  106. * for emails (they can read the paper, but they arent supposed to send back a
  107. * version which they modified for the team to accept),
  108. *
  109. * Dropbox analog: This is for uswers who would be given a URL to a folder in Dropbox
  110. * (but cannot download the files and modify them with the others)
  111. *
  112. * @type {Permission}
  113. */
  114. collaborators: {
  115. get: function() {
  116. return this.readerCommenterOnlys;
  117. },
  118. set: function() {
  119. this.warn("collaborators cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
  120. }
  121. },
  122. /**
  123. * Syntactic sugar for users who have reader+writer+admin permissions.
  124. *
  125. * This is a common permission used by most data base management systems meaning the user can do anything.
  126. *
  127. * Email collaboration analog: The person who initiates the email thread
  128. *
  129. * Dropbox analog: The person who creates the folder in dropbox and adds others to it.
  130. *
  131. * @type {Permission}
  132. */
  133. owners: {
  134. get: function() {
  135. return this.admins;
  136. },
  137. set: function(value) {
  138. this.admins = value;
  139. }
  140. },
  141. /*** Gropued permissions (useful to show permissions in an app) ***/
  142. giveMeUsersWithTheseRolesAndNotTheseRoles: {
  143. value: function(wantTheseRoles, dontWantTheseRoles) {
  144. if (!wantTheseRoles || !dontWantTheseRoles) {
  145. this.bug("Invalid request for users, you must supply the roles you want, and the roles you dont want.");
  146. return new Users();
  147. }
  148. if (Object.prototype.toString.call(wantTheseRoles) !== "[object Array]" || Object.prototype.toString.call(dontWantTheseRoles) !== "[object Array]") {
  149. this.bug("Invalid request for users, you must supply an array of the roles you want, and the roles you dont want.");
  150. return new Users();
  151. }
  152. // this.debugMode = true;
  153. var empty = new Users(),
  154. permissionType,
  155. permissionTypeIndex,
  156. self = this,
  157. groupLabel = wantTheseRoles.join(" ") + " onlys",
  158. usersWhoAreOnlyInThisPermissionType = [];
  159. /* accept requests for the plural or singular of the permission type */
  160. wantTheseRoles = wantTheseRoles.map(function(role) {
  161. return (role + "s").replace(/ss$/, "s");
  162. });
  163. dontWantTheseRoles = dontWantTheseRoles.map(function(role) {
  164. return (role + "s").replace(/ss+$/, "s");
  165. });
  166. // Add users who have the wanted roles
  167. permissionType = wantTheseRoles.pop();
  168. if (this[permissionType]) {
  169. usersWhoAreOnlyInThisPermissionType = new Users(this[permissionType].users.toJSON());
  170. } else {
  171. usersWhoAreOnlyInThisPermissionType = new Users();
  172. }
  173. var howToRemoveSomeoneWhoIsntInAllRoles = function(user) {
  174. self.debug(" Checking on " + user.username);
  175. if (self[permissionType].users.indexOf(user.username) === -1) {
  176. self.debug(" - removing " + user.username + " from " + groupLabel + " because they are not in " + permissionType);
  177. usersWhoAreOnlyInThisPermissionType.remove(user.username);
  178. }
  179. };
  180. var showUsernames = function(user) {
  181. return user.username;
  182. };
  183. for (permissionTypeIndex = wantTheseRoles.length - 1; permissionTypeIndex >= 0; permissionTypeIndex--) {
  184. permissionType = wantTheseRoles[permissionTypeIndex];
  185. self.debug("Making sure " + usersWhoAreOnlyInThisPermissionType.map(showUsernames) + " are " + permissionType);
  186. if (!self[permissionType]) {
  187. continue;
  188. }
  189. if (!self[permissionType]) {
  190. this.warn(" This team doesn't have a " + permissionType + " so this means the " + groupLabel + " is empty.");
  191. return empty;
  192. } else {
  193. // self.debug(" who are ", self[permissionType].users.map(function(user) {
  194. // return user.username;
  195. // }));
  196. }
  197. // If the user isn't in this permisisonType also, remove it
  198. for (var userIndex = usersWhoAreOnlyInThisPermissionType._collection.length - 1; userIndex >= 0; userIndex--) {
  199. howToRemoveSomeoneWhoIsntInAllRoles(usersWhoAreOnlyInThisPermissionType._collection[userIndex]);
  200. }
  201. }
  202. this.debug("These users " + usersWhoAreOnlyInThisPermissionType.map(function(user) {
  203. return user.username;
  204. }).join(" ") + " have all the roles requested: " + groupLabel.replace("onlys", ""));
  205. if (!usersWhoAreOnlyInThisPermissionType || usersWhoAreOnlyInThisPermissionType.length === 0) {
  206. return empty;
  207. }
  208. var howToRemoveSomeoneWhoIsInTheUnWantedPermissions = function(user) {
  209. if (usersWhoAreOnlyInThisPermissionType[user.username]) {
  210. self.debug(" - " + user.username + " is also in " + permissionType + " so removing them from the " + groupLabel + ", there are " + usersWhoAreOnlyInThisPermissionType.length + " left");
  211. usersWhoAreOnlyInThisPermissionType.remove(user.username);
  212. }
  213. };
  214. for (permissionTypeIndex = dontWantTheseRoles.length - 1; permissionTypeIndex >= 0; permissionTypeIndex--) {
  215. permissionType = dontWantTheseRoles[permissionTypeIndex];
  216. if (!self[permissionType]) {
  217. continue;
  218. }
  219. // remove users who are in the permission type we dont want
  220. self[permissionType].users.map(howToRemoveSomeoneWhoIsInTheUnWantedPermissions);
  221. if (usersWhoAreOnlyInThisPermissionType.length === 0) {
  222. return empty;
  223. }
  224. }
  225. return usersWhoAreOnlyInThisPermissionType;
  226. }
  227. },
  228. /**
  229. * Syntactic sugar for apps who want to show users in one category, not in each low level permissions.
  230. *
  231. * This permission is side effect of when a user has only admin permission.
  232. * In this case the user can only access to only admin functionalities such
  233. * as adding new users to the database. This role is used by project managers or IT staff
  234. * who dont know anything about the data itself, and are only setting up the corpus for
  235. * the team to use if the PI or corpus owner doesnt know how to do the permissions.
  236. * set-up themselves.
  237. *
  238. * @type {Permission}
  239. */
  240. adminOnlys: {
  241. get: function() {
  242. if (this._adminOnlys && this._adminOnlys.timestamp && (Date.now() - this._adminOnlys.timestamp < this.cacheTimeLength)) {
  243. this.debug("Not regenerating the list of adminOnlys, its fresh enough. " + new Date(this._adminOnlys.timestamp));
  244. } else {
  245. this._adminOnlys = this.giveMeUsersWithTheseRolesAndNotTheseRoles(["admin"], ["writer", "reader"]);
  246. this._adminOnlys.timestamp = Date.now();
  247. }
  248. return this._adminOnlys;
  249. },
  250. set: function() {
  251. this.warn("adminOnlys cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
  252. }
  253. },
  254. /**
  255. * Syntactic sugar for apps who want to show users in one category, not grouped by low level permissions.
  256. *
  257. * This permission is side effect of when a user has only write permission.
  258. * In this case the user can add new data to the database, but cannot
  259. * review or read or see existing data. They can edit data if it is specifically
  260. * presented to them, they cannot query the database. As all data is version
  261. * controlled edits can be undone so this user is not able to destroy a database that they cant read.
  262. *
  263. * This is not a common permission to use in the system.
  264. *
  265. * This permission can be used for psycholinguistics or crowdsourcing experiemnts
  266. * where users are anonymous (identified anoymously by session or by device)
  267. * and visit a given website or Android app and can respond to stimuli and their responses
  268. * become new data points in the system. These users cannot access fieldlinguistics apps
  269. * which permit browsing of the data, but is a rare permission used by web widgets or other smaller apps which write to a corpus.
  270. *
  271. * @type {Permission}
  272. */
  273. writeOnlys: {
  274. get: function() {
  275. if (this._writeOnlys && this._writeOnlys.timestamp && (Date.now() - this._writeOnlys.timestamp < this.cacheTimeLength)) {
  276. this.debug("Not regenerating the list of writeOnlys, its fresh enough. " + new Date(this._writeOnlys.timestamp));
  277. } else {
  278. this._writeOnlys = this.giveMeUsersWithTheseRolesAndNotTheseRoles(["writer"], ["admin", "reader"]);
  279. this._writeOnlys.timestamp = Date.now();
  280. }
  281. return this._writeOnlys;
  282. },
  283. set: function() {
  284. this.warn("writeOnlys cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
  285. }
  286. },
  287. /**
  288. * Syntactic sugar for apps who want to show users in one category, not grouped by low level permissions.
  289. *
  290. * This permission is side effect of when a user has only read permission.
  291. * In this case the user might be part of a grant commitees or the general public (for
  292. * the aspects of the corpus which have been made @publicic),
  293. * language consultants who might leave mean comments on other consultant's data,
  294. * or other sorts of external viewers of the data who the team doesnt want to leave
  295. * comments.
  296. *
  297. * @type {Permission}
  298. */
  299. readOnlys: {
  300. get: function() {
  301. if (this._readOnlys && this._readOnlys.timestamp && (Date.now() - this._readOnlys.timestamp < this.cacheTimeLength)) {
  302. this.debug("Not regenerating the list of readOnlys, its fresh enough. " + new Date(this._readOnlys.timestamp));
  303. } else {
  304. this._readOnlys = this.giveMeUsersWithTheseRolesAndNotTheseRoles(["reader"], ["admin", "writer"]);
  305. this._readOnlys.timestamp = Date.now();
  306. }
  307. return this._readOnlys;
  308. },
  309. set: function() {
  310. this.warn("readOnlys cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
  311. }
  312. },
  313. /**
  314. * Syntactic sugar for apps who want to show users in one category, not grouped by low level permissions.
  315. *
  316. * This permission is side effect of when a user has only comment permission.
  317. * This is a rare case which might be used in a web widget external to the data entry apps
  318. * where users or anonymous visitors can leave a comment on data if it is presented to them,
  319. * but cannot browse the database.
  320. *
  321. * @type {Permission}
  322. */
  323. commentOnlys: {
  324. get: function() {
  325. if (this._commentOnlys && this._commentOnlys.timestamp && (Date.now() - this._commentOnlys.timestamp < this.cacheTimeLength)) {
  326. this.debug("Not regenerating the list of commentOnlys, its fresh enough. " + new Date(this._commentOnlys.timestamp));
  327. } else {
  328. this._commentOnlys = this.giveMeUsersWithTheseRolesAndNotTheseRoles(["commenter"], ["admin", "writer", "reader"]);
  329. this._commentOnlys.timestamp = Date.now();
  330. }
  331. return this._commentOnlys;
  332. },
  333. set: function() {
  334. this.warn("commentOnlys cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
  335. }
  336. },
  337. /**
  338. * Syntactic sugar for apps who want to show users in one category, not grouped by low level permissions.
  339. *
  340. * This permission is side effect of when a user has only read and comment permission.
  341. * This is a common permission used by research teams where an external reviewer,
  342. * or a second language consultant can review and coment on the data,
  343. * but cannot modify the data itself.
  344. *
  345. * @type {Permission}
  346. */
  347. readerCommenterOnlys: {
  348. get: function() {
  349. if (this._readerCommenterOnlys && this._readerCommenterOnlys.timestamp && (Date.now() - this._readerCommenterOnlys.timestamp < this.cacheTimeLength)) {
  350. this.debug("Not regenerating the list of readerCommenterOnlys, its fresh enough. " + new Date(this._readerCommenterOnlys.timestamp));
  351. } else {
  352. this._readerCommenterOnlys = this.giveMeUsersWithTheseRolesAndNotTheseRoles(["reader", "commenter"], ["admin", "writer"]);
  353. this._readerCommenterOnlys.timestamp = Date.now();
  354. }
  355. return this._readerCommenterOnlys;
  356. },
  357. set: function() {
  358. this.warn("readerCommenterOnlys cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
  359. }
  360. },
  361. /**
  362. * Syntactic sugar for apps who want to show users in one category, not grouped by low level permissions.
  363. *
  364. * This permission is side effect of when a user has only read and write permission.
  365. * This is a rare permission where the user has been leaving abusive comments,
  366. * and are thus only permitted to browse and edit the data itself, not add or edit comments.
  367. *
  368. * If you want to give someone access as a reader+writer, use readerWritersComenters.
  369. *
  370. * @type {Permission}
  371. */
  372. readerWriters: {
  373. get: function() {
  374. return this.readerCommenterWriters;
  375. },
  376. set: function() {
  377. this.warn("readerWriters cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
  378. }
  379. },
  380. /**
  381. * Syntactic sugar for apps who want to show users in one category, not grouped by low level permissions.
  382. *
  383. * This permission is side effect of when a user has only read comment and write permission.
  384. * This is syntactic sugar which is often refered to as "writers" in traditional databases permissions.
  385. * It means the user can write data, read data and comment on data
  386. *
  387. * @return {Permission} A permissions group with an array of Users who fall into this category
  388. */
  389. readerCommenterWriters: {
  390. get: function() {
  391. if (this._readerCommenterWriterOnlys && this._readerCommenterWriterOnlys.timestamp && (Date.now() - this._readerCommenterWriterOnlys.timestamp < this.cacheTimeLength)) {
  392. this.debug("Not regenerating the list of readerCommenterWriterOnlys, its fresh enough. " + new Date(this._readerCommenterWriterOnlys.timestamp));
  393. } else {
  394. // this.debugMode = true;
  395. this._readerCommenterWriterOnlys = this.giveMeUsersWithTheseRolesAndNotTheseRoles(["reader", "writer", "commenter"], ["admin"]);
  396. this._readerCommenterWriterOnlys.timestamp = Date.now();
  397. // this.debugMode = false;
  398. }
  399. return this._readerCommenterWriterOnlys;
  400. },
  401. set: function() {
  402. this.warn("readerCommenterWriters cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
  403. }
  404. },
  405. /**
  406. * Syntactic sugar for apps who want to show users in one category, not grouped by low level permissions.
  407. *
  408. * Often refered to as "admins" in traditional databases permissio ns
  409. * It means the user can write data, read data and comment on data, and perform any operation on the data.
  410. *
  411. * @return {Permission} A permission group with an array of Users who fall into this category
  412. */
  413. readerCommenterWriterAdmins: {
  414. get: function() {
  415. if (this._readerCommenterWriterAdminAlso && this._readerCommenterWriterAdminAlso.timestamp && (Date.now() - this._readerCommenterWriterAdminAlso.timestamp < this.cacheTimeLength)) {
  416. this.debug("Not regenerating the list of readerCommenterWriterAdminAlso, its fresh enough. " + new Date(this._readerCommenterWriterAdminAlso.timestamp));
  417. } else {
  418. this._readerCommenterWriterAdminAlso = this.giveMeUsersWithTheseRolesAndNotTheseRoles(["reader", "writer", "admin"], []);
  419. this._readerCommenterWriterAdminAlso.timestamp = Date.now();
  420. }
  421. return this._readerCommenterWriterAdminAlso;
  422. },
  423. set: function() {
  424. this.warn("readerCommenterWriterAdmins cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
  425. }
  426. },
  427. /*** Permissions to control unruly users who the team is not able to convince to work as a team ***/
  428. /**
  429. * Syntactic sugar for apps who want to show users in one category, not grouped by low level permissions.
  430. *
  431. * This is a permission provided for teams who want many language consultants to contribute to one
  432. * database without seeing the data other consultants have added. We expect this may be a common
  433. * permission especially for teams where there is no `standard` dialect and speakers of other dialects
  434. * are very prescriptive and think others are making `errors` which need to be corrected, this permission prevents them from `correcting` others data.
  435. *
  436. * @return {Permission} A permission group with an array of Users who fall into this category
  437. */
  438. readOwnDataWriteOwnDataOnlys: {
  439. get: function() {
  440. return this.readOwnDataWriteOwnDataOnly || [];
  441. },
  442. set: function() {
  443. this.warn("readOwnDataWriteOwnDataOnlys cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
  444. }
  445. },
  446. /**
  447. * Syntactic sugar for apps who want to show users in one category, not grouped by low level permissions.
  448. *
  449. * This is a rare permission provided for team members who have been editing other people's
  450. * data either to update it to their own dialect (when it was supposed to be the dialect
  451. * of the original source) or other sorts of 'destructive' edits. A user with this permission
  452. * will know that they have been limited to commenting on others' data, and editing their own
  453. * data only (similar to Facebook).
  454. *
  455. * This permission should not be a default permission among research teams, instead the readerCommenterWriter
  456. * permission should be used to reduce territoriality in the data and makes it harder for the database to be kept clean
  457. * because users cannot update/comment on data when they notice a problem. All data is versioned so any
  458. * mistakes can be undone.
  459. *
  460. * @return {Permission} A permission group with an array of Users who fall into this category
  461. */
  462. readCommentAllWriteOwnDataOnlys: {
  463. get: function() {
  464. return this.readCommentAllWriteOwnDataOnly || [];
  465. },
  466. set: function() {
  467. this.warn("readCommentAllWriteOwnDataOnlys cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
  468. }
  469. },
  470. /**
  471. * Syntactic sugar for apps who want to show users in one category, not grouped by low level permissions.
  472. *
  473. * This is a rare permission provided for team members who have been editing other people's
  474. * data either to update it to their own dialect (when it was supposed to be the dialect
  475. * of the original source) or other sorts of 'destructive' edits which the team wants to
  476. * prevent the user from doing this without making the user uncomfortable. A user with this permission
  477. * will know that they have been limited to editing their own data only (similar to Facebook) and cannot
  478. * edit other people's data.
  479. *
  480. * This permission should not be a default permission among research teams, instead the readerCommenterWriter
  481. * permission should be used to reduce territoriality in the data and makes it harder for the database to be kept clean
  482. * because users cannot update/comment on data when they notice a problem. All data is versioned so any
  483. * mistakes can be undone.
  484. *
  485. * @return {Permission} A permission group with an array of Users who fall into this category
  486. */
  487. readAllWriteOwnDataOnlys: {
  488. get: function() {
  489. return this.readAllWriteOwnDataOnly || [];
  490. },
  491. set: function() {
  492. this.warn("readAllWriteOwnDataOnlys cannot be set. it is only syntactic sugar. If you want to modify roles, see the addUser and removeUser functions");
  493. }
  494. },
  495. /**
  496. * This might be used by teams who want to have fine grained control over the
  497. * permissions, or who want to understand the fine grained control at a lower level.
  498. *
  499. * @return {Object} an object with the 3 categories of permission like you
  500. * would have in a PHPmyAdmin console.
  501. */
  502. viewAsBasePermissionSystem: {
  503. get: function() {
  504. return this;
  505. }
  506. },
  507. /**
  508. * This view will be missing some members of the corpora since not all combinations of
  509. * permission have syntactic sugar methods in this library. This shoudlnt be used
  510. * unless the app dev is sure that the users wont be using corpora for psycholinguistics or
  511. * crowdsourcing or computational linguistics.
  512. *
  513. * @return {Object} an object with the 3 categories of permission like you
  514. * would have in a PHPmyAdmin console.
  515. */
  516. viewAsGroupedPermissionSystem: {
  517. get: function() {
  518. return {
  519. adminOnlys: this.adminOnlys,
  520. writeOnlys: this.writeOnlys,
  521. readOnlys: this.readOnlys,
  522. readerCommenterOnlys: this.readerCommenterOnlys,
  523. readerWriters: this.readerCommenterWriters,
  524. admins: this.readerCommenterWriterAdmins,
  525. commentOnlys: this.commentOnlys,
  526. // readerCommenterWriters: readerCommenterWriters,
  527. //writerCommenterOnlys
  528. //writerAdminOnlys
  529. //writerCommenterAdminOnlys
  530. };
  531. }
  532. },
  533. /**
  534. * This is the way most field linguistics apps present the permissions system.
  535. *
  536. * @return {Object} an object with the 3 categories of permission like you
  537. * would have in a PHPmyAdmin console.
  538. */
  539. viewAsDataBasePermissionSystem: {
  540. get: function() {
  541. return {
  542. admins: this.readerCommenterWriterAdmins,
  543. writers: this.readerCommenterWriters,
  544. readers: this.readerCommenterOnlys,
  545. commenters: this.readerCommenterOnlys
  546. };
  547. }
  548. },
  549. /**
  550. * This might be the way that some computational linguistics teams will
  551. * understand the permission system best, but the words
  552. * `collaborator` and `contributor` are so similar that thus far no one
  553. * has used these terms.
  554. *
  555. * @return {Object} an object with the 2 categories of permission like you
  556. * would have on GitHub
  557. */
  558. viewAsEmailingTeamPermissionSystem: {
  559. get: function() {
  560. return {
  561. contributors: this.contributors,
  562. collaborators: this.collaborators,
  563. owners: this.readerCommenterWriterAdmins
  564. };
  565. }
  566. },
  567. addUser: {
  568. value: function(user) {
  569. if (!user || !user.roles) {
  570. this.warn("You should provide roles you want to add... doing nothing.");
  571. return;
  572. }
  573. this.debug(user.roles);
  574. var roles = user.roles;
  575. delete user.roles;
  576. for (var roleIndex = roles.length - 1; roleIndex >= 0; roleIndex--) {
  577. var permissionType = roles[roleIndex];
  578. if (!this[permissionType]) {
  579. permissionType = permissionType.toLowerCase() + "s";
  580. if (!this[permissionType]) {
  581. var newPermission = this.buildPermissionFromAPermissionType(permissionType);
  582. this.debug(" Creating a permssion group for " + permissionType, newPermission);
  583. this.add(newPermission);
  584. }
  585. }
  586. this[permissionType].users.add(user);
  587. this.debug("the user was added to the permission group " + permissionType + " there are a total of " + this[permissionType].length + " users with this sort of access ", this[permissionType].users);
  588. }
  589. }
  590. },
  591. /**
  592. * Removes user from roles on this permissions team, or removes user entirely if only a username is supplied.
  593. *
  594. * @param {Object} user A user with minimally a username and roles which should be removed, or alternatively can be just a username if you want to remove the user entirely from the team.
  595. */
  596. removeUser: {
  597. value: function(user) {
  598. if (typeof user === "string") {
  599. user = {
  600. username: user,
  601. roles: this.currentPermissions
  602. };
  603. this.warn("Remove was requested of a username, removing the username entirely from the permission team.");
  604. }
  605. if (!user || !user.roles) {
  606. this.warn("You should provide roles you want to remove... doing nothing.");
  607. return;
  608. }
  609. // this.debugMode = true;
  610. this.debug(user.roles);
  611. var roles = user.roles;
  612. delete user.roles;
  613. for (var roleIndex = roles.length - 1; roleIndex >= 0; roleIndex--) {
  614. var permissionType = roles[roleIndex];
  615. if (!this[permissionType]) {
  616. permissionType = permissionType.toLowerCase() + "s";
  617. }
  618. if (this[permissionType]) {
  619. // this[permissionType].users.debugMode = true;
  620. if (this[permissionType].users[user.username]) {
  621. var userWasInThisPermission = this[permissionType].users.remove(user);
  622. this.debug("removed ", userWasInThisPermission);
  623. } else {
  624. this.warn("The user " + user.username + " was not in the " + permissionType + " anyway.");
  625. }
  626. this.debug("the user " + user.username + " is not in " + permissionType + " there are a total of " + this[permissionType].length + " users with this sort of access ", this[permissionType].users);
  627. }
  628. }
  629. }
  630. },
  631. /**
  632. * A list of "hats" which team members can wear in this team.
  633. */
  634. currentPermissions: {
  635. get: function() {
  636. if (!this._collection || this._collection === 0) {
  637. return [];
  638. }
  639. var self = this;
  640. return this._collection.map(function(permission) {
  641. return permission[self.primaryKey];
  642. });
  643. }
  644. },
  645. buildPermissionFromAPermissionType: {
  646. value: function(permissionType) {
  647. var verb = permissionType.replace(/s/, "").replace(/writer/, "write").replace(/er/, "");
  648. var label = permissionType;
  649. if (label && label.length > 2) {
  650. label = label[0].toUpperCase() + label.substring(1, label.length);
  651. }
  652. var helpLinguists = "These users can perform " + verb + " operations on the corpus";
  653. return {
  654. users: [],
  655. verb: verb,
  656. id: permissionType,
  657. labelFieldLinguists: label,
  658. helpLinguists: helpLinguists,
  659. directObject: "corpus",
  660. // debugMode: true
  661. };
  662. }
  663. },
  664. fetch: {
  665. value: function() {
  666. var deferred = Q.defer(),
  667. self = this;
  668. if (!this.parent) {
  669. Q.nextTick(function() {
  670. self.fetching = self.loading = false;
  671. deferred.reject({
  672. error: "Cannot fetch if the permissions are not attached to a corpus"
  673. });
  674. });
  675. return deferred.promise;
  676. }
  677. if (!this.application || !this.application.authentication || !this.application.authentication.user || !this.application.authentication.user.username) {
  678. Q.nextTick(function() {
  679. self.fetching = self.loading = false;
  680. deferred.reject({
  681. error: "Cannot fetch if the permissions are not in an application."
  682. });
  683. });
  684. return deferred.promise;
  685. }
  686. if (this.loaded) {
  687. this.warn("not fetching permissions, they are fresh.");
  688. Q.nextTick(function() {
  689. self.fetching = self.loading = false;
  690. deferred.resolve(self);
  691. });
  692. return deferred.promise;
  693. }
  694. var dataToPost = {
  695. connection: this.parent.connection.toJSON(),
  696. username: this.application.authentication.user.username,
  697. };
  698. dataToPost.connection = dataToPost.couchConnection = dataToPost.connection;
  699. this.fetching = this.loading = true;
  700. CORS.makeCORSRequest({
  701. type: "POST",
  702. data: dataToPost,
  703. dataType: "json",
  704. url: this.parent.connection.authUrl + "/corpusteam"
  705. }).then(function(result) {
  706. self.fetching = self.loading = false;
  707. self.loaded = true;
  708. self.populate(result.users);
  709. deferred.resolve(self);
  710. },
  711. function(reason) {
  712. self.fetching = self.loading = false;
  713. self.debug(reason);
  714. deferred.reject(reason);
  715. }).fail(
  716. function(error) {
  717. console.error(error.stack, self);
  718. deferred.reject(error);
  719. });
  720. return deferred.promise;
  721. }
  722. },
  723. /**
  724. * Accepts an object containing permission groups which are an array of
  725. * users masks which have this permission. This simple object is used to populate the permissions model.
  726. *
  727. * @param {Object} users an object containing keys which are verbs (permission types) and the value is an array of users who are in this category
  728. */
  729. populate: {
  730. value: function(users) {
  731. if (!users || users === "defaults") {
  732. users = {};
  733. this.debug("Using default permission types");
  734. }
  735. users.admins = users.admins || [];
  736. users.writers = users.writers || [];
  737. users.readers = users.readers || [];
  738. users.commenters = users.commenters || [];
  739. users.exporters = users.exporters || [];
  740. users.notonteam = users.notonteam || [];
  741. users.allusers = users.allusers || [];
  742. this.usersNotOnTeam = new Users(users.notonteam);
  743. this.allUsers = new Users(users.allusers);
  744. delete users.allusers;
  745. delete users.notonteam;
  746. var permissionType;
  747. for (permissionType in users) {
  748. if (users.hasOwnProperty(permissionType) && permissionType) {
  749. /* if the permission is just an array of users, construct basic permission meta data around it */
  750. if (Object.prototype.toString.call(users[permissionType]) === "[object Array]") {
  751. var usersArray = users[permissionType];
  752. users[permissionType] = this.buildPermissionFromAPermissionType(permissionType);
  753. users[permissionType].users = usersArray;
  754. }
  755. this.debug("adding " + permissionType, users[permissionType]);
  756. this.add(new Permission(users[permissionType]));
  757. }
  758. }
  759. }
  760. },
  761. sanitizeStringForPrimaryKey: {
  762. value: function(value) {
  763. return value;
  764. }
  765. }
  766. });
  767. exports.Permissions = Permissions;