#Access Management

Complete examples for managing access control, monitoring permissions, and implementing security best practices.

#Access Dashboard

Build a comprehensive access management dashboard:

import { Walbucket } from '@walbucket/sdk';

const walbucket = new Walbucket({
  apiKey: process.env.WALBUCKET_API_KEY!,
  network: 'testnet',
  sponsorPrivateKey: process.env.SPONSOR_PRIVATE_KEY!,
});

async function getAccessOverview() {
  // Get all grants and links
  const [grants, links] = await Promise.all([
    walbucket.listAccessGrants(),
    walbucket.listShareableLinks(),
  ]);
  
  const stats = {
    // Access Grants
    totalGrants: grants.length,
    activeGrants: grants.filter(g => g.isActive).length,
    expiredGrants: grants.filter(g => 
      g.expiresAt && g.expiresAt < Date.now()
    ).length,
    grantsByPermission: {
      readOnly: grants.filter(g => g.canRead && !g.canWrite && !g.canAdmin).length,
      editor: grants.filter(g => g.canRead && g.canWrite && !g.canAdmin).length,
      admin: grants.filter(g => g.canAdmin).length,
    },
    
    // Shareable Links
    totalLinks: links.length,
    activeLinks: links.filter(l => l.isActive).length,
    expiredLinks: links.filter(l => 
      l.expiresAt && l.expiresAt < Date.now()
    ).length,
    totalLinkAccesses: links.reduce((sum, l) => sum + l.accessCount, 0),
  };
  
  console.log('=== Access Overview ===');
  console.log(`Access Grants: ${stats.totalGrants} (${stats.activeGrants} active)`);
  console.log(`  Read-only: ${stats.grantsByPermission.readOnly}`);
  console.log(`  Editor: ${stats.grantsByPermission.editor}`);
  console.log(`  Admin: ${stats.grantsByPermission.admin}`);
  console.log(`Shareable Links: ${stats.totalLinks} (${stats.activeLinks} active)`);
  console.log(`  Total Accesses: ${stats.totalLinkAccesses}`);
  
  return stats;
}
typescript

#Permission Audit

Audit permissions across all assets:

async function auditPermissions() {
  const grants = await walbucket.listAccessGrants();
  const assets = await walbucket.list();
  
  // Group grants by asset
  const grantsByAsset = grants.reduce((acc, grant) => {
    if (!acc[grant.assetId]) {
      acc[grant.assetId] = [];
    }
    acc[grant.assetId].push(grant);
    return acc;
  }, {} as Record<string, typeof grants>);
  
  // Audit findings
  const findings = {
    assetsWithAdminAccess: [],
    assetsWithManyGrants: [],
    expiredGrantsNotRevoked: [],
    publiclySharedAssets: [],
  };
  
  for (const asset of assets) {
    const assetGrants = grantsByAsset[asset.assetId] || [];
    
    // Check for admin access
    const adminGrants = assetGrants.filter(g => g.canAdmin && g.isActive);
    if (adminGrants.length > 0) {
      findings.assetsWithAdminAccess.push({
        assetId: asset.assetId,
        name: asset.name,
        adminCount: adminGrants.length,
      });
    }
    
    // Check for many grants
    if (assetGrants.length > 5) {
      findings.assetsWithManyGrants.push({
        assetId: asset.assetId,
        name: asset.name,
        grantCount: assetGrants.length,
      });
    }
    
    // Check for expired but not revoked
    const expiredActive = assetGrants.filter(g => 
      g.isActive && g.expiresAt && g.expiresAt < Date.now()
    );
    if (expiredActive.length > 0) {
      findings.expiredGrantsNotRevoked.push({
        assetId: asset.assetId,
        name: asset.name,
        expiredCount: expiredActive.length,
      });
    }
  }
  
  // Check for public links
  const links = await walbucket.listShareableLinks();
  const activeLinks = links.filter(l => l.isActive);
  findings.publiclySharedAssets = activeLinks.map(l => ({
    assetId: l.assetId,
    shareToken: l.shareToken,
    accessCount: l.accessCount,
  }));
  
  console.log('=== Security Audit ===');
  console.log(`Assets with admin access: ${findings.assetsWithAdminAccess.length}`);
  console.log(`Assets with many grants: ${findings.assetsWithManyGrants.length}`);
  console.log(`Expired grants not revoked: ${findings.expiredGrantsNotRevoked.length}`);
  console.log(`Publicly shared assets: ${findings.publiclySharedAssets.length}`);
  
  return findings;
}
typescript

#User Access Report

Generate a report of what a specific user can access:

async function getUserAccessReport(userAddress: string) {
  const grants = await walbucket.listAccessGrants();
  
  // Filter grants for this user
  const userGrants = grants.filter(g => 
    g.grantedTo === userAddress && g.isActive
  );
  
  // Group by permission level
  const byPermission = {
    readOnly: userGrants.filter(g => g.canRead && !g.canWrite && !g.canAdmin),
    editor: userGrants.filter(g => g.canRead && g.canWrite && !g.canAdmin),
    admin: userGrants.filter(g => g.canAdmin),
  };
  
  // Check for expiring soon
  const expiringSoon = userGrants.filter(g => {
    if (!g.expiresAt) return false;
    const hoursRemaining = (g.expiresAt - Date.now()) / (60 * 60 * 1000);
    return hoursRemaining > 0 && hoursRemaining < 24;
  });
  
  console.log(`=== Access Report for ${userAddress} ===`);
  console.log(`Total Access: ${userGrants.length} assets`);
  console.log(`  Read-only: ${byPermission.readOnly.length}`);
  console.log(`  Editor: ${byPermission.editor.length}`);
  console.log(`  Admin: ${byPermission.admin.length}`);
  console.log(`Expiring in 24h: ${expiringSoon.length}`);
  
  return {
    totalAccess: userGrants.length,
    byPermission,
    expiringSoon,
    grants: userGrants,
  };
}
typescript

#Cleanup Expired Access

Automatically clean up expired grants and links:

async function cleanupExpiredAccess() {
  console.log('Starting cleanup...');
  
  // Clean up expired grants
  const grants = await walbucket.listAccessGrants();
  const expiredGrants = grants.filter(g => 
    g.isActive && g.expiresAt && g.expiresAt < Date.now()
  );
  
  console.log(`Found ${expiredGrants.length} expired grants`);
  
  for (const grant of expiredGrants) {
    try {
      await walbucket.revokeShare(grant.id);
      console.log(`✓ Revoked expired grant: ${grant.id}`);
    } catch (error) {
      console.error(`✗ Failed to revoke ${grant.id}:`, error.message);
    }
  }
  
  // Clean up expired links
  const links = await walbucket.listShareableLinks();
  const expiredLinks = links.filter(l => 
    l.isActive && l.expiresAt && l.expiresAt < Date.now()
  );
  
  console.log(`Found ${expiredLinks.length} expired links`);
  
  for (const link of expiredLinks) {
    try {
      await walbucket.deactivateShareableLink(link.id);
      console.log(`✓ Deactivated expired link: ${link.shareToken}`);
    } catch (error) {
      console.error(`✗ Failed to deactivate ${link.id}:`, error.message);
    }
  }
  
  console.log('Cleanup complete');
}

// Run cleanup daily
setInterval(cleanupExpiredAccess, 24 * 60 * 60 * 1000);
typescript

#Access Analytics

Track and analyze access patterns:

interface AccessAnalytics {
  period: 'day' | 'week' | 'month';
  linkAccesses: number;
  mostAccessedLinks: Array<{ token: string; count: number }>;
  newGrants: number;
  revokedGrants: number;
  activeUsers: Set<string>;
}

async function getAccessAnalytics(period: 'day' | 'week' | 'month'): Promise<AccessAnalytics> {
  const now = Date.now();
  const periods = {
    day: 24 * 60 * 60 * 1000,
    week: 7 * 24 * 60 * 60 * 1000,
    month: 30 * 24 * 60 * 60 * 1000,
  };
  const cutoff = now - periods[period];
  
  // Get data
  const [grants, links] = await Promise.all([
    walbucket.listAccessGrants(),
    walbucket.listShareableLinks(),
  ]);
  
  // Calculate analytics
  const recentGrants = grants.filter(g => g.createdAt > cutoff);
  const recentRevoked = grants.filter(g => 
    !g.isActive && g.createdAt > cutoff
  );
  
  const totalAccesses = links.reduce((sum, l) => sum + l.accessCount, 0);
  const mostAccessed = links
    .sort((a, b) => b.accessCount - a.accessCount)
    .slice(0, 10)
    .map(l => ({ token: l.shareToken, count: l.accessCount }));
  
  const activeUsers = new Set(
    grants
      .filter(g => g.isActive)
      .map(g => g.grantedTo)
  );
  
  return {
    period,
    linkAccesses: totalAccesses,
    mostAccessedLinks: mostAccessed,
    newGrants: recentGrants.length,
    revokedGrants: recentRevoked.length,
    activeUsers,
  };
}

// Usage
const analytics = await getAccessAnalytics('week');
console.log(`Analytics for past ${analytics.period}:`);
console.log(`  Link accesses: ${analytics.linkAccesses}`);
console.log(`  New grants: ${analytics.newGrants}`);
console.log(`  Revoked grants: ${analytics.revokedGrants}`);
console.log(`  Active users: ${analytics.activeUsers.size}`);
typescript

#Role-Based Access Control (RBAC)

Implement RBAC patterns:

type Role = 'viewer' | 'editor' | 'admin' | 'owner';

interface TeamMember {
  address: string;
  name: string;
  role: Role;
}

class AccessControlManager {
  private walbucket: Walbucket;
  
  constructor(walbucket: Walbucket) {
    this.walbucket = walbucket;
  }
  
  getPermissionsForRole(role: Role) {
    const permissions = {
      viewer: { canRead: true, canWrite: false, canAdmin: false },
      editor: { canRead: true, canWrite: true, canAdmin: false },
      admin: { canRead: true, canWrite: true, canAdmin: true },
      owner: { canRead: true, canWrite: true, canAdmin: true },
    };
    return permissions[role];
  }
  
  async shareWithTeam(assetId: string, team: TeamMember[]) {
    const results = [];
    
    for (const member of team) {
      if (member.role === 'owner') continue; // Skip owner
      
      const permissions = this.getPermissionsForRole(member.role);
      
      try {
        await this.walbucket.shareAsset(assetId, member.address, permissions);
        results.push({
          success: true,
          member: member.name,
          role: member.role,
        });
      } catch (error) {
        results.push({
          success: false,
          member: member.name,
          error: error.message,
        });
      }
    }
    
    return results;
  }
  
  async updateMemberRole(
    assetId: string,
    memberAddress: string,
    newRole: Role
  ) {
    // Get current grant
    const grants = await this.walbucket.listAccessGrants();
    const grant = grants.find(g => 
      g.assetId === assetId && g.grantedTo === memberAddress
    );
    
    if (!grant) {
      throw new Error('Grant not found');
    }
    
    // Revoke old grant
    await this.walbucket.revokeShare(grant.id);
    
    // Create new grant with updated permissions
    const permissions = this.getPermissionsForRole(newRole);
    await this.walbucket.shareAsset(assetId, memberAddress, permissions);
    
    console.log(`Updated role to: ${newRole}`);
  }
  
  async removeMember(assetId: string, memberAddress: string) {
    const grants = await this.walbucket.listAccessGrants();
    const grant = grants.find(g => 
      g.assetId === assetId && g.grantedTo === memberAddress
    );
    
    if (!grant) {
      throw new Error('Grant not found');
    }
    
    await this.walbucket.revokeShare(grant.id);
    console.log('Member removed');
  }
}

// Usage
const acm = new AccessControlManager(walbucket);

const team: TeamMember[] = [
  { address: '0x111...', name: 'Alice', role: 'admin' },
  { address: '0x222...', name: 'Bob', role: 'editor' },
  { address: '0x333...', name: 'Charlie', role: 'viewer' },
];

// Share with team
await acm.shareWithTeam(assetId, team);

// Update role
await acm.updateMemberRole(assetId, '0x222...', 'admin');

// Remove member
await acm.removeMember(assetId, '0x333...');
typescript

#React Access Management Component

import { useState, useEffect } from 'react';
import { Walbucket } from '@walbucket/sdk';

function AccessManagement({ assetId, walbucket }) {
  const [grants, setGrants] = useState([]);
  const [links, setLinks] = useState([]);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    loadAccess();
  }, [assetId]);
  
  async function loadAccess() {
    setLoading(true);
    try {
      const [allGrants, allLinks] = await Promise.all([
        walbucket.listAccessGrants(),
        walbucket.listShareableLinks(),
      ]);
      
      setGrants(allGrants.filter(g => g.assetId === assetId));
      setLinks(allLinks.filter(l => l.assetId === assetId));
    } finally {
      setLoading(false);
    }
  }
  
  async function handleRevoke(grantId: string) {
    if (confirm('Revoke this access?')) {
      await walbucket.revokeShare(grantId);
      await loadAccess();
    }
  }
  
  async function handleDeactivateLink(linkId: string) {
    if (confirm('Deactivate this link?')) {
      await walbucket.deactivateShareableLink(linkId);
      await loadAccess();
    }
  }
  
  if (loading) return <div>Loading...</div>;
  
  return (
    <div className="access-management">
      <h3>Access Management</h3>
      
      {/* Access Grants */}
      <div className="grants-section">
        <h4>Shared With ({grants.length})</h4>
        {grants.map(grant => (
          <div key={grant.id} className="grant-item">
            <div>
              <strong>{grant.grantedTo.slice(0, 10)}...</strong>
              <div className="permissions">
                {grant.canRead && <span>Read</span>}
                {grant.canWrite && <span>Write</span>}
                {grant.canAdmin && <span>Admin</span>}
              </div>
              {grant.expiresAt && (
                <div className="expiry">
                  Expires: {new Date(grant.expiresAt).toLocaleDateString()}
                </div>
              )}
            </div>
            {grant.isActive && (
              <button onClick={() => handleRevoke(grant.id)}>
                Revoke
              </button>
            )}
          </div>
        ))}
      </div>
      
      {/* Shareable Links */}
      <div className="links-section">
        <h4>Public Links ({links.length})</h4>
        {links.map(link => (
          <div key={link.id} className="link-item">
            <div>
              <code>{link.shareToken}</code>
              <div>Accessed: {link.accessCount} times</div>
              {link.expiresAt && (
                <div className="expiry">
                  Expires: {new Date(link.expiresAt).toLocaleDateString()}
                </div>
              )}
            </div>
            {link.isActive && (
              <button onClick={() => handleDeactivateLink(link.id)}>
                Deactivate
              </button>
            )}
          </div>
        ))}
      </div>
    </div>
  );
}
typescript

#Best Practices

#Regular Audits

// Schedule regular permission audits
setInterval(async () => {
  const findings = await auditPermissions();
  if (findings.expiredGrantsNotRevoked.length > 0) {
    await cleanupExpiredAccess();
  }
}, 24 * 60 * 60 * 1000); // Daily
typescript

#Principle of Least Privilege

// Always grant minimum necessary permissions
await walbucket.shareAsset(assetId, userAddress, {
  canRead: true,
  canWrite: false, // Only if truly needed
  canAdmin: false, // Rarely needed
});
typescript

#Time-Limited Access

// Use expiration for temporary access
const oneWeek = Date.now() + (7 * 24 * 60 * 60 * 1000);
await walbucket.shareAsset(assetId, contractorAddress, {
  canRead: true,
  expiresAt: oneWeek,
});
typescript