ParseACL.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. /**
  2. * Copyright (c) 2015-present, Parse, LLC.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. *
  9. * @flow
  10. */
  11. import ParseRole from './ParseRole';
  12. import ParseUser from './ParseUser';
  13. /*:: type PermissionsMap = { [permission: string]: boolean };*/
  14. /*:: type ByIdMap = { [userId: string]: PermissionsMap };*/
  15. const PUBLIC_KEY = '*';
  16. /**
  17. * Creates a new ACL.
  18. * If no argument is given, the ACL has no permissions for anyone.
  19. * If the argument is a Parse.User, the ACL will have read and write
  20. * permission for only that user.
  21. * If the argument is any other JSON object, that object will be interpretted
  22. * as a serialized ACL created with toJSON().
  23. *
  24. * <p>An ACL, or Access Control List can be added to any
  25. * <code>Parse.Object</code> to restrict access to only a subset of users
  26. * of your application.</p>
  27. * @alias Parse.ACL
  28. */
  29. class ParseACL {
  30. /*:: permissionsById: ByIdMap;*/
  31. /**
  32. * @param {(Parse.User|Object)} user The user to initialize the ACL for
  33. */
  34. constructor(arg1
  35. /*: ParseUser | ByIdMap*/
  36. ) {
  37. this.permissionsById = {};
  38. if (arg1 && typeof arg1 === 'object') {
  39. if (arg1 instanceof ParseUser) {
  40. this.setReadAccess(arg1, true);
  41. this.setWriteAccess(arg1, true);
  42. } else {
  43. for (const userId in arg1) {
  44. const accessList = arg1[userId];
  45. if (typeof userId !== 'string') {
  46. throw new TypeError('Tried to create an ACL with an invalid user id.');
  47. }
  48. this.permissionsById[userId] = {};
  49. for (const permission in accessList) {
  50. const allowed = accessList[permission];
  51. if (permission !== 'read' && permission !== 'write') {
  52. throw new TypeError('Tried to create an ACL with an invalid permission type.');
  53. }
  54. if (typeof allowed !== 'boolean') {
  55. throw new TypeError('Tried to create an ACL with an invalid permission value.');
  56. }
  57. this.permissionsById[userId][permission] = allowed;
  58. }
  59. }
  60. }
  61. } else if (typeof arg1 === 'function') {
  62. throw new TypeError('ParseACL constructed with a function. Did you forget ()?');
  63. }
  64. }
  65. /**
  66. * Returns a JSON-encoded version of the ACL.
  67. * @return {Object}
  68. */
  69. toJSON()
  70. /*: ByIdMap*/
  71. {
  72. const permissions = {};
  73. for (const p in this.permissionsById) {
  74. permissions[p] = this.permissionsById[p];
  75. }
  76. return permissions;
  77. }
  78. /**
  79. * Returns whether this ACL is equal to another object
  80. * @param other The other object to compare to
  81. * @return {Boolean}
  82. */
  83. equals(other
  84. /*: ParseACL*/
  85. )
  86. /*: boolean*/
  87. {
  88. if (!(other instanceof ParseACL)) {
  89. return false;
  90. }
  91. const users = Object.keys(this.permissionsById);
  92. const otherUsers = Object.keys(other.permissionsById);
  93. if (users.length !== otherUsers.length) {
  94. return false;
  95. }
  96. for (const u in this.permissionsById) {
  97. if (!other.permissionsById[u]) {
  98. return false;
  99. }
  100. if (this.permissionsById[u].read !== other.permissionsById[u].read) {
  101. return false;
  102. }
  103. if (this.permissionsById[u].write !== other.permissionsById[u].write) {
  104. return false;
  105. }
  106. }
  107. return true;
  108. }
  109. _setAccess(accessType
  110. /*: string*/
  111. , userId
  112. /*: ParseUser | ParseRole | string*/
  113. , allowed
  114. /*: boolean*/
  115. ) {
  116. if (userId instanceof ParseUser) {
  117. userId = userId.id;
  118. } else if (userId instanceof ParseRole) {
  119. const name = userId.getName();
  120. if (!name) {
  121. throw new TypeError('Role must have a name');
  122. }
  123. userId = 'role:' + name;
  124. }
  125. if (typeof userId !== 'string') {
  126. throw new TypeError('userId must be a string.');
  127. }
  128. if (typeof allowed !== 'boolean') {
  129. throw new TypeError('allowed must be either true or false.');
  130. }
  131. let permissions = this.permissionsById[userId];
  132. if (!permissions) {
  133. if (!allowed) {
  134. // The user already doesn't have this permission, so no action is needed
  135. return;
  136. } else {
  137. permissions = {};
  138. this.permissionsById[userId] = permissions;
  139. }
  140. }
  141. if (allowed) {
  142. this.permissionsById[userId][accessType] = true;
  143. } else {
  144. delete permissions[accessType];
  145. if (Object.keys(permissions).length === 0) {
  146. delete this.permissionsById[userId];
  147. }
  148. }
  149. }
  150. _getAccess(accessType
  151. /*: string*/
  152. , userId
  153. /*: ParseUser | ParseRole | string*/
  154. )
  155. /*: boolean*/
  156. {
  157. if (userId instanceof ParseUser) {
  158. userId = userId.id;
  159. if (!userId) {
  160. throw new Error('Cannot get access for a ParseUser without an ID');
  161. }
  162. } else if (userId instanceof ParseRole) {
  163. const name = userId.getName();
  164. if (!name) {
  165. throw new TypeError('Role must have a name');
  166. }
  167. userId = 'role:' + name;
  168. }
  169. const permissions = this.permissionsById[userId];
  170. if (!permissions) {
  171. return false;
  172. }
  173. return !!permissions[accessType];
  174. }
  175. /**
  176. * Sets whether the given user is allowed to read this object.
  177. * @param userId An instance of Parse.User or its objectId.
  178. * @param {Boolean} allowed Whether that user should have read access.
  179. */
  180. setReadAccess(userId
  181. /*: ParseUser | ParseRole | string*/
  182. , allowed
  183. /*: boolean*/
  184. ) {
  185. this._setAccess('read', userId, allowed);
  186. }
  187. /**
  188. * Get whether the given user id is *explicitly* allowed to read this object.
  189. * Even if this returns false, the user may still be able to access it if
  190. * getPublicReadAccess returns true or a role that the user belongs to has
  191. * write access.
  192. * @param userId An instance of Parse.User or its objectId, or a Parse.Role.
  193. * @return {Boolean}
  194. */
  195. getReadAccess(userId
  196. /*: ParseUser | ParseRole | string*/
  197. )
  198. /*: boolean*/
  199. {
  200. return this._getAccess('read', userId);
  201. }
  202. /**
  203. * Sets whether the given user id is allowed to write this object.
  204. * @param userId An instance of Parse.User or its objectId, or a Parse.Role..
  205. * @param {Boolean} allowed Whether that user should have write access.
  206. */
  207. setWriteAccess(userId
  208. /*: ParseUser | ParseRole | string*/
  209. , allowed
  210. /*: boolean*/
  211. ) {
  212. this._setAccess('write', userId, allowed);
  213. }
  214. /**
  215. * Gets whether the given user id is *explicitly* allowed to write this object.
  216. * Even if this returns false, the user may still be able to write it if
  217. * getPublicWriteAccess returns true or a role that the user belongs to has
  218. * write access.
  219. * @param userId An instance of Parse.User or its objectId, or a Parse.Role.
  220. * @return {Boolean}
  221. */
  222. getWriteAccess(userId
  223. /*: ParseUser | ParseRole | string*/
  224. )
  225. /*: boolean*/
  226. {
  227. return this._getAccess('write', userId);
  228. }
  229. /**
  230. * Sets whether the public is allowed to read this object.
  231. * @param {Boolean} allowed
  232. */
  233. setPublicReadAccess(allowed
  234. /*: boolean*/
  235. ) {
  236. this.setReadAccess(PUBLIC_KEY, allowed);
  237. }
  238. /**
  239. * Gets whether the public is allowed to read this object.
  240. * @return {Boolean}
  241. */
  242. getPublicReadAccess()
  243. /*: boolean*/
  244. {
  245. return this.getReadAccess(PUBLIC_KEY);
  246. }
  247. /**
  248. * Sets whether the public is allowed to write this object.
  249. * @param {Boolean} allowed
  250. */
  251. setPublicWriteAccess(allowed
  252. /*: boolean*/
  253. ) {
  254. this.setWriteAccess(PUBLIC_KEY, allowed);
  255. }
  256. /**
  257. * Gets whether the public is allowed to write this object.
  258. * @return {Boolean}
  259. */
  260. getPublicWriteAccess()
  261. /*: boolean*/
  262. {
  263. return this.getWriteAccess(PUBLIC_KEY);
  264. }
  265. /**
  266. * Gets whether users belonging to the given role are allowed
  267. * to read this object. Even if this returns false, the role may
  268. * still be able to write it if a parent role has read access.
  269. *
  270. * @param role The name of the role, or a Parse.Role object.
  271. * @return {Boolean} true if the role has read access. false otherwise.
  272. * @throws {TypeError} If role is neither a Parse.Role nor a String.
  273. */
  274. getRoleReadAccess(role
  275. /*: ParseRole | string*/
  276. )
  277. /*: boolean*/
  278. {
  279. if (role instanceof ParseRole) {
  280. // Normalize to the String name
  281. role = role.getName();
  282. }
  283. if (typeof role !== 'string') {
  284. throw new TypeError('role must be a ParseRole or a String');
  285. }
  286. return this.getReadAccess('role:' + role);
  287. }
  288. /**
  289. * Gets whether users belonging to the given role are allowed
  290. * to write this object. Even if this returns false, the role may
  291. * still be able to write it if a parent role has write access.
  292. *
  293. * @param role The name of the role, or a Parse.Role object.
  294. * @return {Boolean} true if the role has write access. false otherwise.
  295. * @throws {TypeError} If role is neither a Parse.Role nor a String.
  296. */
  297. getRoleWriteAccess(role
  298. /*: ParseRole | string*/
  299. )
  300. /*: boolean*/
  301. {
  302. if (role instanceof ParseRole) {
  303. // Normalize to the String name
  304. role = role.getName();
  305. }
  306. if (typeof role !== 'string') {
  307. throw new TypeError('role must be a ParseRole or a String');
  308. }
  309. return this.getWriteAccess('role:' + role);
  310. }
  311. /**
  312. * Sets whether users belonging to the given role are allowed
  313. * to read this object.
  314. *
  315. * @param role The name of the role, or a Parse.Role object.
  316. * @param {Boolean} allowed Whether the given role can read this object.
  317. * @throws {TypeError} If role is neither a Parse.Role nor a String.
  318. */
  319. setRoleReadAccess(role
  320. /*: ParseRole | string*/
  321. , allowed
  322. /*: boolean*/
  323. ) {
  324. if (role instanceof ParseRole) {
  325. // Normalize to the String name
  326. role = role.getName();
  327. }
  328. if (typeof role !== 'string') {
  329. throw new TypeError('role must be a ParseRole or a String');
  330. }
  331. this.setReadAccess('role:' + role, allowed);
  332. }
  333. /**
  334. * Sets whether users belonging to the given role are allowed
  335. * to write this object.
  336. *
  337. * @param role The name of the role, or a Parse.Role object.
  338. * @param {Boolean} allowed Whether the given role can write this object.
  339. * @throws {TypeError} If role is neither a Parse.Role nor a String.
  340. */
  341. setRoleWriteAccess(role
  342. /*: ParseRole | string*/
  343. , allowed
  344. /*: boolean*/
  345. ) {
  346. if (role instanceof ParseRole) {
  347. // Normalize to the String name
  348. role = role.getName();
  349. }
  350. if (typeof role !== 'string') {
  351. throw new TypeError('role must be a ParseRole or a String');
  352. }
  353. this.setWriteAccess('role:' + role, allowed);
  354. }
  355. }
  356. export default ParseACL;