gamma-enterprise-rbac

为企业级 Gamma 应用提供完整的多租户角色权限管理体系,支持基于组织、工作区、团队的层级化角色定义与继承,实现细粒度资源访问控制、操作授权验证、租户数据隔离及权限审计追踪。

快捷安装

在终端运行此命令,即可一键安装该 Skill 到您的 Claude 中

npx skills add jeremylongshore/claude-code-plugins-plus-skills --skill "gamma-enterprise-rbac"

Gamma Enterprise RBAC

Overview

Implement role-based access control for Gamma API integrations. Gamma’s API uses a single API key per workspace — granular permissions must be implemented in your application layer. The Teams and Business plans support workspace-level collaboration with shared themes and folders.

Prerequisites

  • Gamma Teams or Business subscription
  • Application database for user/role storage
  • Completed gamma-install-auth setup

Gamma Access Model

Gamma Workspace (1 API key)
├── Themes (shared across workspace)
├── Folders (shared across workspace)
└── Generations (tied to API key, not individual users)

Your Application Layer (you implement this):
├── Organization
│   ├── Admin (manage API key, configure themes)
│   ├── Editor (generate presentations, use templates)
│   ├── Viewer (view generated presentations, download exports)
│   └── Guest (no generation access)

Key point: Gamma’s API does not have per-user authentication. All API calls use the workspace API key. You must enforce per-user permissions in your application.

Instructions

Step 1: Define Role Hierarchy

// src/auth/gamma-roles.ts
type GammaRole = "guest" | "viewer" | "editor" | "admin";

const PERMISSIONS: Record<GammaRole, string[]> = {
  guest: [],
  viewer: ["generation:view", "export:download"],
  editor: ["generation:view", "generation:create", "export:download", "template:use"],
  admin: [
    "generation:view", "generation:create", "export:download",
    "template:use", "template:manage", "theme:manage",
    "settings:manage", "member:manage",
  ],
};

function hasPermission(role: GammaRole, permission: string): boolean {
  return PERMISSIONS[role]?.includes(permission) ?? false;
}

Step 2: Authorization Middleware

// src/middleware/gamma-auth.ts
import { Request, Response, NextFunction } from "express";

function requireGammaPermission(permission: string) {
  return (req: Request, res: Response, next: NextFunction) => {
    const user = req.user; // Set by your auth middleware
    if (!user) return res.status(401).json({ error: "Unauthorized" });

    if (!hasPermission(user.gammaRole, permission)) {
      return res.status(403).json({
        error: "Forbidden",
        required: permission,
        userRole: user.gammaRole,
      });
    }
    next();
  };
}

// Usage
app.post("/api/presentations",
  requireGammaPermission("generation:create"),
  async (req, res) => {
    const gamma = createGammaClient({ apiKey: process.env.GAMMA_API_KEY! });
    const { generationId } = await gamma.generate(req.body);
    // Track ownership in your database
    await db.generations.create({
      data: { generationId, userId: req.user.id, teamId: req.user.teamId },
    });
    res.json({ generationId });
  }
);

app.get("/api/presentations/:id",
  requireGammaPermission("generation:view"),
  async (req, res) => {
    // Only return if user owns it or is in the same team
    const gen = await db.generations.findFirst({
      where: { generationId: req.params.id, teamId: req.user.teamId },
    });
    if (!gen) return res.status(404).json({ error: "Not found" });
    res.json(gen);
  }
);

Step 3: Multi-Tenant Workspace Isolation

// src/tenant/gamma-tenant.ts
// Each tenant can have their own Gamma workspace (API key)
// or share a workspace with resource-level isolation

interface Tenant {
  id: string;
  name: string;
  gammaApiKey: string; // Encrypted in database
}

class TenantGammaService {
  private clients = new Map<string, ReturnType<typeof createGammaClient>>();

  getClient(tenant: Tenant) {
    if (!this.clients.has(tenant.id)) {
      this.clients.set(
        tenant.id,
        createGammaClient({ apiKey: tenant.gammaApiKey })
      );
    }
    return this.clients.get(tenant.id)!;
  }

  async generate(tenant: Tenant, userId: string, content: string, options: any = {}) {
    const gamma = this.getClient(tenant);
    const { generationId } = await gamma.generate({
      content,
      ...options,
    });

    // Track with tenant isolation
    await db.generations.create({
      data: { generationId, tenantId: tenant.id, userId },
    });

    return { generationId };
  }
}

Step 4: Credit Quota Per User/Team

// src/quota/gamma-quotas.ts
interface Quota {
  maxGenerationsPerDay: number;
  maxCreditsPerMonth: number;
}

const ROLE_QUOTAS: Record<GammaRole, Quota> = {
  guest: { maxGenerationsPerDay: 0, maxCreditsPerMonth: 0 },
  viewer: { maxGenerationsPerDay: 0, maxCreditsPerMonth: 0 },
  editor: { maxGenerationsPerDay: 10, maxCreditsPerMonth: 500 },
  admin: { maxGenerationsPerDay: 50, maxCreditsPerMonth: 5000 },
};

async function checkQuota(userId: string, role: GammaRole): Promise<boolean> {
  const quota = ROLE_QUOTAS[role];
  if (quota.maxGenerationsPerDay === 0) return false;

  const todayCount = await db.generations.count({
    where: {
      userId,
      createdAt: { gte: new Date(new Date().toDateString()) },
    },
  });

  return todayCount < quota.maxGenerationsPerDay;
}

Step 5: Audit Logging

// src/audit/gamma-audit.ts
async function auditGammaAction(entry: {
  userId: string;
  teamId: string;
  action: string;
  resourceId?: string;
  metadata?: Record<string, any>;
}) {
  await db.auditLog.create({
    data: {
      ...entry,
      timestamp: new Date(),
      service: "gamma",
    },
  });
}

// Usage
await auditGammaAction({
  userId: req.user.id,
  teamId: req.user.teamId,
  action: "generation.create",
  resourceId: generationId,
  metadata: { outputFormat: "presentation", credits: result.creditsUsed },
});

Permission Matrix

PermissionGuestViewerEditorAdmin
View presentationsNoYesYesYes
Download exportsNoYesYesYes
Create generationsNoNoYesYes
Use templatesNoNoYesYes
Manage themes/foldersNoNoNoYes
Manage team membersNoNoNoYes
Configure API keyNoNoNoYes

Error Handling

IssueCauseSolution
403 ForbiddenInsufficient roleCheck user’s gammaRole assignment
Cross-tenant accessWrong API keyVerify tenant isolation in getClient()
Quota exceededToo many generationsShow remaining quota, wait for reset
Privilege escalationMissing role checkVerify middleware on all routes

Resources

Next Steps

Proceed to gamma-migration-deep-dive for platform migration.