UNPKG

26.4 kBJavaScriptView Raw
1"use strict";
2// Copyright 2019 Google LLC
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15Object.defineProperty(exports, "__esModule", { value: true });
16exports.AclRoleAccessorMethods = exports.Acl = void 0;
17const promisify_1 = require("@google-cloud/promisify");
18const arrify = require("arrify");
19/**
20 * Attach functionality to a {@link Storage.acl} instance. This will add an
21 * object for each role group (owners, readers, and writers), with each object
22 * containing methods to add or delete a type of entity.
23 *
24 * As an example, here are a few methods that are created.
25 *
26 * myBucket.acl.readers.deleteGroup('groupId', function(err) {});
27 *
28 * myBucket.acl.owners.addUser('email@example.com', function(err, acl) {});
29 *
30 * myBucket.acl.writers.addDomain('example.com', function(err, acl) {});
31 *
32 * @private
33 */
34class AclRoleAccessorMethods {
35 constructor() {
36 this.owners = {};
37 this.readers = {};
38 this.writers = {};
39 /**
40 * An object of convenience methods to add or delete owner ACL permissions
41 * for a given entity.
42 *
43 * The supported methods include:
44 *
45 * - `myFile.acl.owners.addAllAuthenticatedUsers`
46 * - `myFile.acl.owners.deleteAllAuthenticatedUsers`
47 * - `myFile.acl.owners.addAllUsers`
48 * - `myFile.acl.owners.deleteAllUsers`
49 * - `myFile.acl.owners.addDomain`
50 * - `myFile.acl.owners.deleteDomain`
51 * - `myFile.acl.owners.addGroup`
52 * - `myFile.acl.owners.deleteGroup`
53 * - `myFile.acl.owners.addProject`
54 * - `myFile.acl.owners.deleteProject`
55 * - `myFile.acl.owners.addUser`
56 * - `myFile.acl.owners.deleteUser`
57 *
58 * @name Acl#owners
59 *
60 * @example
61 * const storage = require('@google-cloud/storage')();
62 * const myBucket = storage.bucket('my-bucket');
63 * const myFile = myBucket.file('my-file');
64 *
65 * //-
66 * // Add a user as an owner of a file.
67 * //-
68 * const myBucket = gcs.bucket('my-bucket');
69 * const myFile = myBucket.file('my-file');
70 * myFile.acl.owners.addUser('email@example.com', function(err, aclObject)
71 * {});
72 *
73 * //-
74 * // For reference, the above command is the same as running the following.
75 * //-
76 * myFile.acl.add({
77 * entity: 'user-email@example.com',
78 * role: gcs.acl.OWNER_ROLE
79 * }, function(err, aclObject) {});
80 *
81 * //-
82 * // If the callback is omitted, we'll return a Promise.
83 * //-
84 * myFile.acl.owners.addUser('email@example.com').then(function(data) {
85 * const aclObject = data[0];
86 * const apiResponse = data[1];
87 * });
88 */
89 this.owners = {};
90 /**
91 * An object of convenience methods to add or delete reader ACL permissions
92 * for a given entity.
93 *
94 * The supported methods include:
95 *
96 * - `myFile.acl.readers.addAllAuthenticatedUsers`
97 * - `myFile.acl.readers.deleteAllAuthenticatedUsers`
98 * - `myFile.acl.readers.addAllUsers`
99 * - `myFile.acl.readers.deleteAllUsers`
100 * - `myFile.acl.readers.addDomain`
101 * - `myFile.acl.readers.deleteDomain`
102 * - `myFile.acl.readers.addGroup`
103 * - `myFile.acl.readers.deleteGroup`
104 * - `myFile.acl.readers.addProject`
105 * - `myFile.acl.readers.deleteProject`
106 * - `myFile.acl.readers.addUser`
107 * - `myFile.acl.readers.deleteUser`
108 *
109 * @name Acl#readers
110 *
111 * @example
112 * const storage = require('@google-cloud/storage')();
113 * const myBucket = storage.bucket('my-bucket');
114 * const myFile = myBucket.file('my-file');
115 *
116 * //-
117 * // Add a user as a reader of a file.
118 * //-
119 * myFile.acl.readers.addUser('email@example.com', function(err, aclObject)
120 * {});
121 *
122 * //-
123 * // For reference, the above command is the same as running the following.
124 * //-
125 * myFile.acl.add({
126 * entity: 'user-email@example.com',
127 * role: gcs.acl.READER_ROLE
128 * }, function(err, aclObject) {});
129 *
130 * //-
131 * // If the callback is omitted, we'll return a Promise.
132 * //-
133 * myFile.acl.readers.addUser('email@example.com').then(function(data) {
134 * const aclObject = data[0];
135 * const apiResponse = data[1];
136 * });
137 */
138 this.readers = {};
139 /**
140 * An object of convenience methods to add or delete writer ACL permissions
141 * for a given entity.
142 *
143 * The supported methods include:
144 *
145 * - `myFile.acl.writers.addAllAuthenticatedUsers`
146 * - `myFile.acl.writers.deleteAllAuthenticatedUsers`
147 * - `myFile.acl.writers.addAllUsers`
148 * - `myFile.acl.writers.deleteAllUsers`
149 * - `myFile.acl.writers.addDomain`
150 * - `myFile.acl.writers.deleteDomain`
151 * - `myFile.acl.writers.addGroup`
152 * - `myFile.acl.writers.deleteGroup`
153 * - `myFile.acl.writers.addProject`
154 * - `myFile.acl.writers.deleteProject`
155 * - `myFile.acl.writers.addUser`
156 * - `myFile.acl.writers.deleteUser`
157 *
158 * @name Acl#writers
159 *
160 * @example
161 * const storage = require('@google-cloud/storage')();
162 * const myBucket = storage.bucket('my-bucket');
163 * const myFile = myBucket.file('my-file');
164 *
165 * //-
166 * // Add a user as a writer of a file.
167 * //-
168 * myFile.acl.writers.addUser('email@example.com', function(err, aclObject)
169 * {});
170 *
171 * //-
172 * // For reference, the above command is the same as running the following.
173 * //-
174 * myFile.acl.add({
175 * entity: 'user-email@example.com',
176 * role: gcs.acl.WRITER_ROLE
177 * }, function(err, aclObject) {});
178 *
179 * //-
180 * // If the callback is omitted, we'll return a Promise.
181 * //-
182 * myFile.acl.writers.addUser('email@example.com').then(function(data) {
183 * const aclObject = data[0];
184 * const apiResponse = data[1];
185 * });
186 */
187 this.writers = {};
188 AclRoleAccessorMethods.roles.forEach(this._assignAccessMethods.bind(this));
189 }
190 _assignAccessMethods(role) {
191 const accessMethods = AclRoleAccessorMethods.accessMethods;
192 const entities = AclRoleAccessorMethods.entities;
193 const roleGroup = role.toLowerCase() + 's';
194 // eslint-disable-next-line @typescript-eslint/no-explicit-any
195 this[roleGroup] = entities.reduce((acc, entity) => {
196 const isPrefix = entity.charAt(entity.length - 1) === '-';
197 accessMethods.forEach(accessMethod => {
198 let method = accessMethod + entity[0].toUpperCase() + entity.substr(1);
199 if (isPrefix) {
200 method = method.replace('-', '');
201 }
202 // Wrap the parent accessor method (e.g. `add` or `delete`) to avoid the
203 // more complex API of specifying an `entity` and `role`.
204 // eslint-disable-next-line @typescript-eslint/no-explicit-any
205 acc[method] = (entityId, options, callback) => {
206 let apiEntity;
207 if (typeof options === 'function') {
208 callback = options;
209 options = {};
210 }
211 if (isPrefix) {
212 apiEntity = entity + entityId;
213 }
214 else {
215 // If the entity is not a prefix, it is a special entity group
216 // that does not require further details. The accessor methods
217 // only accept a callback.
218 apiEntity = entity;
219 callback = entityId;
220 }
221 options = Object.assign({
222 entity: apiEntity,
223 role,
224 }, options);
225 const args = [options];
226 if (typeof callback === 'function') {
227 args.push(callback);
228 }
229 // eslint-disable-next-line @typescript-eslint/no-explicit-any
230 return this[accessMethod].apply(this, args);
231 };
232 });
233 return acc;
234 }, {});
235 }
236}
237exports.AclRoleAccessorMethods = AclRoleAccessorMethods;
238AclRoleAccessorMethods.accessMethods = ['add', 'delete'];
239AclRoleAccessorMethods.entities = [
240 // Special entity groups that do not require further specification.
241 'allAuthenticatedUsers',
242 'allUsers',
243 // Entity groups that require specification, e.g. `user-email@example.com`.
244 'domain-',
245 'group-',
246 'project-',
247 'user-',
248];
249AclRoleAccessorMethods.roles = ['OWNER', 'READER', 'WRITER'];
250/**
251 * Cloud Storage uses access control lists (ACLs) to manage object and
252 * bucket access. ACLs are the mechanism you use to share objects with other
253 * users and allow other users to access your buckets and objects.
254 *
255 * An ACL consists of one or more entries, where each entry grants permissions
256 * to an entity. Permissions define the actions that can be performed against an
257 * object or bucket (for example, `READ` or `WRITE`); the entity defines who the
258 * permission applies to (for example, a specific user or group of users).
259 *
260 * Where an `entity` value is accepted, we follow the format the Cloud Storage
261 * API expects.
262 *
263 * Refer to
264 * https://cloud.google.com/storage/docs/json_api/v1/defaultObjectAccessControls
265 * for the most up-to-date values.
266 *
267 * - `user-userId`
268 * - `user-email`
269 * - `group-groupId`
270 * - `group-email`
271 * - `domain-domain`
272 * - `project-team-projectId`
273 * - `allUsers`
274 * - `allAuthenticatedUsers`
275 *
276 * Examples:
277 *
278 * - The user "liz@example.com" would be `user-liz@example.com`.
279 * - The group "example@googlegroups.com" would be
280 * `group-example@googlegroups.com`.
281 * - To refer to all members of the Google Apps for Business domain
282 * "example.com", the entity would be `domain-example.com`.
283 *
284 * For more detailed information, see
285 * [About Access Control Lists](http://goo.gl/6qBBPO).
286 *
287 * @constructor Acl
288 * @mixin
289 * @param {object} options Configuration options.
290 */
291class Acl extends AclRoleAccessorMethods {
292 constructor(options) {
293 super();
294 this.pathPrefix = options.pathPrefix;
295 this.request_ = options.request;
296 }
297 /**
298 * @typedef {array} AddAclResponse
299 * @property {object} 0 The Acl Objects.
300 * @property {object} 1 The full API response.
301 */
302 /**
303 * @callback AddAclCallback
304 * @param {?Error} err Request error, if any.
305 * @param {object} acl The Acl Objects.
306 * @param {object} apiResponse The full API response.
307 */
308 /**
309 * Add access controls on a {@link Bucket} or {@link File}.
310 *
311 * @see [BucketAccessControls: insert API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/bucketAccessControls/insert}
312 * @see [ObjectAccessControls: insert API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/objectAccessControls/insert}
313 *
314 * @param {object} options Configuration options.
315 * @param {string} options.entity Whose permissions will be added.
316 * @param {string} options.role Permissions allowed for the defined entity.
317 * See {@link https://cloud.google.com/storage/docs/access-control Access
318 * Control}.
319 * @param {number} [options.generation] **File Objects Only** Select a specific
320 * revision of this file (as opposed to the latest version, the default).
321 * @param {string} [options.userProject] The ID of the project which will be
322 * billed for the request.
323 * @param {AddAclCallback} [callback] Callback function.
324 * @returns {Promise<AddAclResponse>}
325 *
326 * @example
327 * const storage = require('@google-cloud/storage')();
328 * const myBucket = storage.bucket('my-bucket');
329 * const myFile = myBucket.file('my-file');
330 *
331 * const options = {
332 * entity: 'user-useremail@example.com',
333 * role: gcs.acl.OWNER_ROLE
334 * };
335 *
336 * myBucket.acl.add(options, function(err, aclObject, apiResponse) {});
337 *
338 * //-
339 * // For file ACL operations, you can also specify a `generation` property.
340 * // Here is how you would grant ownership permissions to a user on a
341 * specific
342 * // revision of a file.
343 * //-
344 * myFile.acl.add({
345 * entity: 'user-useremail@example.com',
346 * role: gcs.acl.OWNER_ROLE,
347 * generation: 1
348 * }, function(err, aclObject, apiResponse) {});
349 *
350 * //-
351 * // If the callback is omitted, we'll return a Promise.
352 * //-
353 * myBucket.acl.add(options).then(function(data) {
354 * const aclObject = data[0];
355 * const apiResponse = data[1];
356 * });
357 *
358 * @example <caption>include:samples/acl.js</caption>
359 * region_tag:storage_add_file_owner
360 * Example of adding an owner to a file:
361 *
362 * @example <caption>include:samples/acl.js</caption>
363 * region_tag:storage_add_bucket_owner
364 * Example of adding an owner to a bucket:
365 *
366 * @example <caption>include:samples/acl.js</caption>
367 * region_tag:storage_add_bucket_default_owner
368 * Example of adding a default owner to a bucket:
369 */
370 add(options, callback) {
371 const query = {};
372 if (options.generation) {
373 query.generation = options.generation;
374 }
375 if (options.userProject) {
376 query.userProject = options.userProject;
377 }
378 this.request({
379 method: 'POST',
380 uri: '',
381 qs: query,
382 json: {
383 entity: options.entity,
384 role: options.role.toUpperCase(),
385 },
386 }, (err, resp) => {
387 if (err) {
388 callback(err, null, resp);
389 return;
390 }
391 callback(null, this.makeAclObject_(resp), resp);
392 });
393 }
394 /**
395 * @typedef {array} RemoveAclResponse
396 * @property {object} 0 The full API response.
397 */
398 /**
399 * @callback RemoveAclCallback
400 * @param {?Error} err Request error, if any.
401 * @param {object} apiResponse The full API response.
402 */
403 /**
404 * Delete access controls on a {@link Bucket} or {@link File}.
405 *
406 * @see [BucketAccessControls: delete API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/bucketAccessControls/delete}
407 * @see [ObjectAccessControls: delete API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/objectAccessControls/delete}
408 *
409 * @param {object} options Configuration object.
410 * @param {string} options.entity Whose permissions will be revoked.
411 * @param {int} [options.generation] **File Objects Only** Select a specific
412 * revision of this file (as opposed to the latest version, the default).
413 * @param {string} [options.userProject] The ID of the project which will be
414 * billed for the request.
415 * @param {RemoveAclCallback} callback The callback function.
416 * @returns {Promise<RemoveAclResponse>}
417 *
418 * @example
419 * const storage = require('@google-cloud/storage')();
420 * const myBucket = storage.bucket('my-bucket');
421 * const myFile = myBucket.file('my-file');
422 *
423 * myBucket.acl.delete({
424 * entity: 'user-useremail@example.com'
425 * }, function(err, apiResponse) {});
426 *
427 * //-
428 * // For file ACL operations, you can also specify a `generation` property.
429 * //-
430 * myFile.acl.delete({
431 * entity: 'user-useremail@example.com',
432 * generation: 1
433 * }, function(err, apiResponse) {});
434 *
435 * //-
436 * // If the callback is omitted, we'll return a Promise.
437 * //-
438 * myFile.acl.delete().then(function(data) {
439 * const apiResponse = data[0];
440 * });
441 *
442 * @example <caption>include:samples/acl.js</caption>
443 * region_tag:storage_remove_bucket_owner
444 * Example of removing an owner from a bucket:
445 *
446 * @example <caption>include:samples/acl.js</caption>
447 * region_tag:storage_remove_bucket_default_owner
448 * Example of removing a default owner from a bucket:
449 *
450 * @example <caption>include:samples/acl.js</caption>
451 * region_tag:storage_remove_file_owner
452 * Example of removing an owner from a bucket:
453 */
454 delete(options, callback) {
455 const query = {};
456 if (options.generation) {
457 query.generation = options.generation;
458 }
459 if (options.userProject) {
460 query.userProject = options.userProject;
461 }
462 this.request({
463 method: 'DELETE',
464 uri: '/' + encodeURIComponent(options.entity),
465 qs: query,
466 }, (err, resp) => {
467 callback(err, resp);
468 });
469 }
470 /**
471 * @typedef {array} GetAclResponse
472 * @property {object|object[]} 0 Single or array of Acl Objects.
473 * @property {object} 1 The full API response.
474 */
475 /**
476 * @callback GetAclCallback
477 * @param {?Error} err Request error, if any.
478 * @param {object|object[]} acl Single or array of Acl Objects.
479 * @param {object} apiResponse The full API response.
480 */
481 /**
482 * Get access controls on a {@link Bucket} or {@link File}. If
483 * an entity is omitted, you will receive an array of all applicable access
484 * controls.
485 *
486 * @see [BucketAccessControls: get API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/bucketAccessControls/get}
487 * @see [ObjectAccessControls: get API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/objectAccessControls/get}
488 *
489 * @param {object|function} [options] Configuration options. If you want to
490 * receive a list of all access controls, pass the callback function as
491 * the only argument.
492 * @param {string} [options.entity] Whose permissions will be fetched.
493 * @param {number} [options.generation] **File Objects Only** Select a specific
494 * revision of this file (as opposed to the latest version, the default).
495 * @param {string} [options.userProject] The ID of the project which will be
496 * billed for the request.
497 * @param {GetAclCallback} [callback] Callback function.
498 * @returns {Promise<GetAclResponse>}
499 *
500 * @example
501 * const storage = require('@google-cloud/storage')();
502 * const myBucket = storage.bucket('my-bucket');
503 * const myFile = myBucket.file('my-file');
504 *
505 * myBucket.acl.get({
506 * entity: 'user-useremail@example.com'
507 * }, function(err, aclObject, apiResponse) {});
508 *
509 * //-
510 * // Get all access controls.
511 * //-
512 * myBucket.acl.get(function(err, aclObjects, apiResponse) {
513 * // aclObjects = [
514 * // {
515 * // entity: 'user-useremail@example.com',
516 * // role: 'owner'
517 * // }
518 * // ]
519 * });
520 *
521 * //-
522 * // For file ACL operations, you can also specify a `generation` property.
523 * //-
524 * myFile.acl.get({
525 * entity: 'user-useremail@example.com',
526 * generation: 1
527 * }, function(err, aclObject, apiResponse) {});
528 *
529 * //-
530 * // If the callback is omitted, we'll return a Promise.
531 * //-
532 * myBucket.acl.get().then(function(data) {
533 * const aclObject = data[0];
534 * const apiResponse = data[1];
535 * });
536 *
537 * @example <caption>include:samples/acl.js</caption>
538 * region_tag:storage_print_file_acl
539 * Example of printing a file's ACL:
540 *
541 * @example <caption>include:samples/acl.js</caption>
542 * region_tag:storage_print_file_acl_for_user
543 * Example of printing a file's ACL for a specific user:
544 *
545 * @example <caption>include:samples/acl.js</caption>
546 * region_tag:storage_print_bucket_acl
547 * Example of printing a bucket's ACL:
548 *
549 * @example <caption>include:samples/acl.js</caption>
550 * region_tag:storage_print_bucket_acl_for_user
551 * Example of printing a bucket's ACL for a specific user:
552 */
553 get(optionsOrCallback, cb) {
554 const options = typeof optionsOrCallback === 'object' ? optionsOrCallback : null;
555 const callback = typeof optionsOrCallback === 'function' ? optionsOrCallback : cb;
556 let path = '';
557 const query = {};
558 if (options) {
559 path = '/' + encodeURIComponent(options.entity);
560 if (options.generation) {
561 query.generation = options.generation;
562 }
563 if (options.userProject) {
564 query.userProject = options.userProject;
565 }
566 }
567 this.request({
568 uri: path,
569 qs: query,
570 }, (err, resp) => {
571 if (err) {
572 callback(err, null, resp);
573 return;
574 }
575 let results;
576 if (resp.items) {
577 results = arrify(resp.items).map(this.makeAclObject_);
578 }
579 else {
580 results = this.makeAclObject_(resp);
581 }
582 callback(null, results, resp);
583 });
584 }
585 /**
586 * @typedef {array} UpdateAclResponse
587 * @property {object} 0 The updated Acl Objects.
588 * @property {object} 1 The full API response.
589 */
590 /**
591 * @callback UpdateAclCallback
592 * @param {?Error} err Request error, if any.
593 * @param {object} acl The updated Acl Objects.
594 * @param {object} apiResponse The full API response.
595 */
596 /**
597 * Update access controls on a {@link Bucket} or {@link File}.
598 *
599 * @see [BucketAccessControls: update API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/bucketAccessControls/update}
600 * @see [ObjectAccessControls: update API Documentation]{@link https://cloud.google.com/storage/docs/json_api/v1/objectAccessControls/update}
601 *
602 * @param {object} options Configuration options.
603 * @param {string} options.entity Whose permissions will be updated.
604 * @param {string} options.role Permissions allowed for the defined entity.
605 * See {@link Storage.acl}.
606 * @param {number} [options.generation] **File Objects Only** Select a specific
607 * revision of this file (as opposed to the latest version, the default).
608 * @param {string} [options.userProject] The ID of the project which will be
609 * billed for the request.
610 * @param {UpdateAclCallback} [callback] Callback function.
611 * @returns {Promise<UpdateAclResponse>}
612 *
613 * @example
614 * const storage = require('@google-cloud/storage')();
615 * const myBucket = storage.bucket('my-bucket');
616 * const myFile = myBucket.file('my-file');
617 *
618 * const options = {
619 * entity: 'user-useremail@example.com',
620 * role: gcs.acl.WRITER_ROLE
621 * };
622 *
623 * myBucket.acl.update(options, function(err, aclObject, apiResponse) {});
624 *
625 * //-
626 * // For file ACL operations, you can also specify a `generation` property.
627 * //-
628 * myFile.acl.update({
629 * entity: 'user-useremail@example.com',
630 * role: gcs.acl.WRITER_ROLE,
631 * generation: 1
632 * }, function(err, aclObject, apiResponse) {});
633 *
634 * //-
635 * // If the callback is omitted, we'll return a Promise.
636 * //-
637 * myFile.acl.update(options).then(function(data) {
638 * const aclObject = data[0];
639 * const apiResponse = data[1];
640 * });
641 */
642 update(options, callback) {
643 const query = {};
644 if (options.generation) {
645 query.generation = options.generation;
646 }
647 if (options.userProject) {
648 query.userProject = options.userProject;
649 }
650 this.request({
651 method: 'PUT',
652 uri: '/' + encodeURIComponent(options.entity),
653 qs: query,
654 json: {
655 role: options.role.toUpperCase(),
656 },
657 }, (err, resp) => {
658 if (err) {
659 callback(err, null, resp);
660 return;
661 }
662 callback(null, this.makeAclObject_(resp), resp);
663 });
664 }
665 /**
666 * Transform API responses to a consistent object format.
667 *
668 * @private
669 */
670 makeAclObject_(accessControlObject) {
671 const obj = {
672 entity: accessControlObject.entity,
673 role: accessControlObject.role,
674 };
675 if (accessControlObject.projectTeam) {
676 obj.projectTeam = accessControlObject.projectTeam;
677 }
678 return obj;
679 }
680 /**
681 * Patch requests up to the bucket's request object.
682 *
683 * @private
684 *
685 * @param {string} method Action.
686 * @param {string} path Request path.
687 * @param {*} query Request query object.
688 * @param {*} body Request body contents.
689 * @param {function} callback Callback function.
690 */
691 request(reqOpts, callback) {
692 reqOpts.uri = this.pathPrefix + reqOpts.uri;
693 this.request_(reqOpts, callback);
694 }
695}
696exports.Acl = Acl;
697/*! Developer Documentation
698 *
699 * All async methods (except for streams) will return a Promise in the event
700 * that a callback is omitted.
701 */
702promisify_1.promisifyAll(Acl, {
703 exclude: ['request'],
704});
705//# sourceMappingURL=acl.js.map
\No newline at end of file