Google Cloud Storage provides two access control systems: Access Control Lists (ACLs) for fine-grained permissions and Identity and Access Management (IAM) for resource-level permissions.
class Acl {
constructor(options: AclOptions);
// Properties
pathPrefix: string;
// Dynamic role accessors
owners: AclRoleAccessors;
readers: AclRoleAccessors;
writers: AclRoleAccessors;
// Core methods
add(options: AddAclOptions): Promise<AddAclResponse>;
get(options: GetAclOptions): Promise<GetAclResponse>;
update(options: UpdateAclOptions): Promise<UpdateAclResponse>;
remove(options: RemoveAclOptions): Promise<RemoveAclResponse>;
}
interface AclOptions {
pathPrefix: string;
request: (reqOpts: object, callback: Function) => void;
}interface AclMetadata {
entity?: string;
role?: 'OWNER' | 'READER' | 'WRITER' | 'FULL_CONTROL';
projectTeam?: {
projectNumber?: string;
team?: 'editors' | 'owners' | 'viewers';
};
}
interface AccessControlObject {
entity: string;
role: string;
projectTeam?: string;
}
// Entity types
type AclEntity =
| `user-${string}` // user-email@example.com
| `group-${string}` // group-groupname@domain.com
| `domain-${string}` // domain-example.com
| `project-editors-${string}` // project-editors-123456789
| `project-owners-${string}` // project-owners-123456789
| `project-viewers-${string}` // project-viewers-123456789
| 'allUsers' // All users (public)
| 'allAuthenticatedUsers'; // All authenticated users
// Predefined ACL values
type PredefinedAcl =
| 'authenticatedRead'
| 'bucketOwnerFullControl'
| 'bucketOwnerRead'
| 'private'
| 'projectPrivate'
| 'publicRead'
| 'publicReadWrite';add(options: AddAclOptions): Promise<AddAclResponse>
get(options?: GetAclOptions): Promise<GetAclResponse>
update(options: UpdateAclOptions): Promise<UpdateAclResponse>
remove(options: RemoveAclOptions): Promise<RemoveAclResponse>
interface AddAclOptions {
entity: string;
role: string;
generation?: number;
userProject?: string;
}
interface GetAclOptions {
entity?: string;
generation?: number;
userProject?: string;
}
interface UpdateAclOptions {
entity: string;
role: string;
generation?: number;
userProject?: string;
}
interface RemoveAclOptions {
entity: string;
generation?: number;
userProject?: string;
}
type AddAclResponse = [AccessControlObject, unknown]; // [acl, apiResponse]
type GetAclResponse = [AccessControlObject[], unknown]; // [acls, apiResponse]
type UpdateAclResponse = [AccessControlObject, unknown]; // [acl, apiResponse]
type RemoveAclResponse = [unknown]; // [apiResponse]
// Add ACL entry
await bucket.acl.add({
entity: 'user-john@example.com',
role: 'READER'
});
// Add group access
await file.acl.add({
entity: 'group-developers@company.com',
role: 'WRITER'
});
// Add domain access
await bucket.acl.add({
entity: 'domain-example.com',
role: 'READER'
});
// Add project team access
await bucket.acl.add({
entity: 'project-editors-123456789',
role: 'WRITER'
});
// Make public
await file.acl.add({
entity: 'allUsers',
role: 'READER'
});
// Get all ACL entries
const [acls] = await bucket.acl.get();
acls.forEach(acl => {
console.log(`Entity: ${acl.entity}, Role: ${acl.role}`);
});
// Get specific entity ACL
const [acls] = await file.acl.get({
entity: 'user-john@example.com'
});
// Update ACL entry
await bucket.acl.update({
entity: 'user-john@example.com',
role: 'OWNER'
});
// Remove ACL entry
await file.acl.remove({
entity: 'user-john@example.com'
});Each role (owners, readers, writers) provides convenient methods:
interface AclRoleAccessors {
addUser(entity: string): Promise<AddAclResponse>;
deleteUser(entity: string): Promise<RemoveAclResponse>;
addGroup(entity: string): Promise<AddAclResponse>;
deleteGroup(entity: string): Promise<RemoveAclResponse>;
addDomain(entity: string): Promise<AddAclResponse>;
deleteDomain(entity: string): Promise<RemoveAclResponse>;
addProject(entity: string): Promise<AddAclResponse>;
deleteProject(entity: string): Promise<RemoveAclResponse>;
addAllUsers(): Promise<AddAclResponse>;
deleteAllUsers(): Promise<RemoveAclResponse>;
addAllAuthenticatedUsers(): Promise<AddAclResponse>;
deleteAllAuthenticatedUsers(): Promise<RemoveAclResponse>;
}
// Owners (OWNER role)
await bucket.acl.owners.addUser('owner@example.com');
await bucket.acl.owners.addGroup('owners@company.com');
await bucket.acl.owners.deleteUser('former-owner@example.com');
// Readers (READER role)
await file.acl.readers.addUser('reader@example.com');
await file.acl.readers.addDomain('partner.com');
await file.acl.readers.addAllUsers(); // Make public read
await file.acl.readers.deleteAllUsers(); // Remove public access
// Writers (WRITER role)
await bucket.acl.writers.addUser('editor@example.com');
await bucket.acl.writers.addProject('project-editors-123456789');
await bucket.acl.writers.deleteGroup('old-editors@company.com');
// Authenticated users access
await file.acl.readers.addAllAuthenticatedUsers();
await file.acl.readers.deleteAllAuthenticatedUsers();// Bucket ACLs control bucket-level permissions
const bucket = storage.bucket('my-bucket');
// Who can list files in the bucket
await bucket.acl.readers.addUser('lister@example.com');
// Who can add/delete files in the bucket
await bucket.acl.writers.addGroup('editors@company.com');
// Who has full control over the bucket
await bucket.acl.owners.addUser('admin@example.com');
// File ACLs control individual file permissions
const file = bucket.file('document.pdf');
// Who can read this specific file
await file.acl.readers.addUser('viewer@example.com');
// Who can overwrite this specific file
await file.acl.writers.addUser('editor@example.com');class Iam {
constructor(bucket: Bucket);
// Methods
getPolicy(options?: GetPolicyOptions): Promise<GetPolicyResponse>;
setPolicy(policy: Policy, options?: SetPolicyOptions): Promise<SetPolicyResponse>;
testIamPermissions(permissions: string | string[], options?: TestIamPermissionsOptions): Promise<TestIamPermissionsResponse>;
}interface Policy {
bindings?: PolicyBinding[];
etag?: string;
version?: number;
}
interface PolicyBinding {
role?: string;
members?: string[];
condition?: Expr;
}
interface Expr {
expression?: string;
title?: string;
description?: string;
location?: string;
}
// Common IAM roles for Cloud Storage
type StorageRole =
| 'roles/storage.admin'
| 'roles/storage.objectAdmin'
| 'roles/storage.objectCreator'
| 'roles/storage.objectViewer'
| 'roles/storage.legacyBucketOwner'
| 'roles/storage.legacyBucketReader'
| 'roles/storage.legacyBucketWriter'
| 'roles/storage.legacyObjectOwner'
| 'roles/storage.legacyObjectReader';
// Member types
type IamMember =
| `user:${string}` // user:john@example.com
| `group:${string}` // group:team@company.com
| `serviceAccount:${string}` // serviceAccount:service@project.iam.gserviceaccount.com
| `domain:${string}` // domain:example.com
| `projectEditor:${string}` // projectEditor:project-id
| `projectOwner:${string}` // projectOwner:project-id
| `projectViewer:${string}` // projectViewer:project-id
| 'allUsers' // All users
| 'allAuthenticatedUsers'; // All authenticated usersgetPolicy(options?: GetPolicyOptions): Promise<GetPolicyResponse>
setPolicy(policy: Policy, options?: SetPolicyOptions): Promise<SetPolicyResponse>
testIamPermissions(permissions: string | string[], options?: TestIamPermissionsOptions): Promise<TestIamPermissionsResponse>
interface GetPolicyOptions {
userProject?: string;
requestedPolicyVersion?: number;
}
interface SetPolicyOptions {
userProject?: string;
}
interface TestIamPermissionsOptions {
userProject?: string;
}
type GetPolicyResponse = [Policy, unknown]; // [policy, apiResponse]
type SetPolicyResponse = [Policy, unknown]; // [policy, apiResponse]
type TestIamPermissionsResponse = [string[], unknown]; // [permissions, apiResponse]
// Get current IAM policy
const [policy] = await bucket.iam.getPolicy();
console.log('Current policy:', policy);
// Create a new policy
const newPolicy: Policy = {
bindings: [
{
role: 'roles/storage.objectViewer',
members: [
'user:viewer@example.com',
'group:readers@company.com'
]
},
{
role: 'roles/storage.objectAdmin',
members: [
'user:admin@example.com',
'serviceAccount:app@project.iam.gserviceaccount.com'
]
}
]
};
// Set the policy
await bucket.iam.setPolicy(newPolicy);
// Test permissions
const [permissions] = await bucket.iam.testIamPermissions([
'storage.objects.get',
'storage.objects.create',
'storage.objects.delete'
]);
console.log('Allowed permissions:', permissions);// Add a binding to existing policy
const [currentPolicy] = await bucket.iam.getPolicy();
// Find existing binding or create new one
let binding = currentPolicy.bindings?.find(b => b.role === 'roles/storage.objectViewer');
if (!binding) {
binding = {
role: 'roles/storage.objectViewer',
members: []
};
currentPolicy.bindings = currentPolicy.bindings || [];
currentPolicy.bindings.push(binding);
}
// Add member to binding
if (!binding.members?.includes('user:newuser@example.com')) {
binding.members = binding.members || [];
binding.members.push('user:newuser@example.com');
}
// Update policy
await bucket.iam.setPolicy(currentPolicy);
// Remove a member
const updatedBindings = currentPolicy.bindings?.map(binding => {
if (binding.role === 'roles/storage.objectViewer') {
binding.members = binding.members?.filter(member =>
member !== 'user:olduser@example.com'
);
}
return binding;
}).filter(binding => binding.members && binding.members.length > 0);
await bucket.iam.setPolicy({
...currentPolicy,
bindings: updatedBindings
});// Policy with conditions
const conditionalPolicy: Policy = {
bindings: [
{
role: 'roles/storage.objectViewer',
members: ['user:contractor@example.com'],
condition: {
title: 'Temporary Access',
description: 'Access expires at end of project',
expression: 'request.time < timestamp("2024-12-31T23:59:59Z")'
}
},
{
role: 'roles/storage.objectCreator',
members: ['user:uploader@example.com'],
condition: {
title: 'Upload to specific prefix',
description: 'Can only upload to uploads/ prefix',
expression: 'resource.name.startsWith("projects/_/buckets/my-bucket/objects/uploads/")'
}
}
]
};
await bucket.iam.setPolicy(conditionalPolicy);
// Time-based access
const timeBasedPolicy: Policy = {
bindings: [
{
role: 'roles/storage.objectViewer',
members: ['user:timeuser@example.com'],
condition: {
expression: `
request.time.getHours() >= 9 &&
request.time.getHours() <= 17 &&
request.time.getDayOfWeek() >= 2 &&
request.time.getDayOfWeek() <= 6
`.replace(/\s+/g, ' ').trim()
}
}
]
};// Make bucket public for reading
await bucket.acl.readers.addAllUsers();
// Make specific file public
await file.acl.readers.addAllUsers();
// Public read using IAM
await bucket.iam.setPolicy({
bindings: [
{
role: 'roles/storage.objectViewer',
members: ['allUsers']
}
]
});
// Using predefined ACLs
await bucket.upload('/local/file.txt', {
predefinedAcl: 'publicRead'
});// Authenticated users can read via ACL
await bucket.acl.readers.addAllAuthenticatedUsers();
// Authenticated users via IAM
await bucket.iam.setPolicy({
bindings: [
{
role: 'roles/storage.objectViewer',
members: ['allAuthenticatedUsers']
}
]
});// Team access via groups
await bucket.acl.readers.addGroup('team-viewers@company.com');
await bucket.acl.writers.addGroup('team-editors@company.com');
await bucket.acl.owners.addGroup('team-admins@company.com');
// Team access via IAM
await bucket.iam.setPolicy({
bindings: [
{
role: 'roles/storage.objectViewer',
members: [
'group:team-viewers@company.com',
'group:all-employees@company.com'
]
},
{
role: 'roles/storage.objectCreator',
members: [
'group:team-editors@company.com'
]
},
{
role: 'roles/storage.admin',
members: [
'group:team-admins@company.com'
]
}
]
});// Service account access via ACL
await bucket.acl.writers.addUser('service@project.iam.gserviceaccount.com');
// Service account access via IAM
await bucket.iam.setPolicy({
bindings: [
{
role: 'roles/storage.objectAdmin',
members: [
'serviceAccount:app-backend@project.iam.gserviceaccount.com',
'serviceAccount:data-processor@project.iam.gserviceaccount.com'
]
}
]
});// Project team access via ACL
await bucket.acl.readers.addProject('project-viewers-123456789');
await bucket.acl.writers.addProject('project-editors-123456789');
// Project team access via IAM
await bucket.iam.setPolicy({
bindings: [
{
role: 'roles/storage.objectViewer',
members: ['projectViewer:my-project-id']
},
{
role: 'roles/storage.objectCreator',
members: ['projectEditor:my-project-id']
},
{
role: 'roles/storage.admin',
members: ['projectOwner:my-project-id']
}
]
});// Use ACLs for:
// - Fine-grained, object-level permissions
// - Simple read/write/owner permissions
// - Legacy applications expecting ACL semantics
// - When you need different permissions per file
// Example: Different access per file
await publicFile.acl.readers.addAllUsers();
await privateFile.acl.readers.addUser('specific-user@example.com');
await internalFile.acl.readers.addGroup('internal@company.com');// Use IAM for:
// - Resource-level permissions
// - Complex conditions and constraints
// - Integration with Google Cloud IAM
// - Centralized access management
// Example: Bucket-wide policies
await bucket.iam.setPolicy({
bindings: [
{
role: 'roles/storage.objectViewer',
members: ['group:all-employees@company.com']
},
{
role: 'roles/storage.objectCreator',
members: ['group:content-creators@company.com'],
condition: {
expression: 'request.time.getHours() >= 9 && request.time.getHours() <= 17'
}
}
]
});import { ApiError } from '@google-cloud/storage';
try {
await bucket.acl.add({
entity: 'user-invalid@example.com',
role: 'READER'
});
} catch (error) {
if (error instanceof ApiError) {
if (error.code === 400) {
console.log('Invalid entity or role');
} else if (error.code === 403) {
console.log('Permission denied');
} else {
console.error(`ACL Error ${error.code}: ${error.message}`);
}
}
}
// Test permissions before operations
const [permissions] = await bucket.iam.testIamPermissions([
'storage.objects.create'
]);
if (permissions.includes('storage.objects.create')) {
// User can create objects
await bucket.upload('/local/file.txt');
} else {
console.log('User cannot create objects in this bucket');
}// Promise pattern (recommended)
const [acls] = await bucket.acl.get();
// Callback pattern
bucket.acl.get((err, acls, apiResponse) => {
if (err) {
console.error('Error:', err);
return;
}
console.log('ACL entries:', acls);
});
// Callback types
interface GetAclCallback {
(err: Error | null, acls?: AccessControlObject[], apiResponse?: unknown): void;
}
interface AddAclCallback {
(err: Error | null, acl?: AccessControlObject, apiResponse?: unknown): void;
}
interface GetPolicyCallback {
(err: Error | null, policy?: Policy, apiResponse?: unknown): void;
}
interface SetPolicyCallback {
(err: Error | null, policy?: Policy, apiResponse?: unknown): void;
}