8/6 livestream: AI-Powered Figma to Storybook in Minutes

Announcing Visual Copilot - Figma to production in half the time

Builder logo
builder.io
Contact sales

8/6 livestream: AI-Powered Figma to Storybook in Minutes

Announcing Visual Copilot - Figma to production in half the time

Builder logo
builder.io

Blog

Home

Resources

Blog

Forum

Github

Login

Signup

×

Visual CMS

Drag-and-drop visual editor and headless CMS for any tech stack

Theme Studio for Shopify

Build and optimize your Shopify-hosted storefront, no coding required

Resources

Blog

Get StartedLogin

Principal-based access control lets you create different permission levels for different team members. Instead of giving everyone the same access, you can specify that only developers can modify source code, only designers can access design files, or only admins can change production configurations.

This document shows how to use the principal property in your ACL rules to implement role-based permissions and evaluate those permissions programmatically in your code.

To get the most out of this document, make sure you're familiar with Access Control List (ACL) basics.

The principal property lets you restrict rules to specific teams or roles. This enables fine-grained access control based on user membership in various groups.

  • Teams and roles: each user can belong to multiple teams, such as ["developer", "admin"].
  • Rule matching: rules only apply if the user has at least one matching principal.
  • No principal: rules without principals apply to all users.
  • Default behavior: if a user has no matching principals for any rule, access is denied.

Example principals:

  • ["admin"]: only applies to admin users
  • ["developer", "designer"]: applies to developers or designers
  • [] or undefined: applies to all users (default behavior)

As an example, note the different access levels for different team members in the json below:

{
  "accessControl": {
    "entries": [
      {
        "action": "allow",
        "resource": "/docs/**",
        "permissions": ["read"],
        "comment": "Everyone can read documentation"
      },
      {
        "action": "allow",
        "resource": "/docs/**",
        "permissions": ["write"],
        "principal": ["editor", "admin"],
        "comment": "Only editors and admins can write docs"
      },
      {
        "action": "allow",
        "resource": "/src/**",
        "permissions": ["read", "write"],
        "principal": ["developer", "admin"],
        "comment": "Developers and admins can access source code"
      },
      {
        "action": "allow",
        "resource": "/design/**",
        "permissions": ["read", "write"],
        "principal": ["designer", "admin"],
        "comment": "Designers and admins can access design files"
      },
      {
        "action": "deny",
        "resource": "/src/production/**",
        "permissions": ["write"],
        "principal": ["developer"],
        "failMessage": "Developers cannot modify production code - admin approval required"
      },
      {
        "action": "deny",
        "resource": "/admin/**",
        "permissions": ["read", "write", "list"],
        "principal": ["developer", "designer"],
        "failMessage": "Admin area is restricted to administrators only"
      }
    ]
  }
}

In this setup:

  • Everyone can read documentation.
  • Editors and admins can modify documentation.
  • Developers and admins can work with source code.
  • Designers and admins can access design files.
  • Developers cannot modify production code (even if they're also admins).

Handle users with multiple roles, for example, a developer who is also an admin:

{
  "accessControl": {
    "entries": [
      {
        "action": "allow",
        "resource": "/dev/**",
        "permissions": ["read", "write"],
        "principal": ["developer"]
      },
      {
        "action": "allow",
        "resource": "/admin/**",
        "permissions": ["read", "write"],
        "principal": ["admin"]
      },
      {
        "action": "deny",
        "resource": "/admin/critical/**",
        "permissions": ["write"],
        "principal": ["developer"],
        "failMessage": "Developer role cannot modify critical admin files, even with admin privileges"
      }
    ]
  }
}

In this example, a user with both ["developer", "admin"] roles would:

  • Have access to /dev/** files through the developer role
  • Have access to /admin/** files through the admin role
  • Be denied write access to /admin/critical/** (deny rule for developer takes precedence)

When working with team-based ACLs, you often need to evaluate permissions dynamically in your code. This is essential for:

  • Showing or hiding UI elements based on user roles
  • Authorizing API calls before making requests
  • Implementing custom business logic that depends on user permissions
  • Debugging access issues by checking what rules apply to specific users

You can also evaluate ACL rules programmatically:

import { evaluateAccess } from "$/vcp-common/acl";
import type { AclPolicy } from "@builder.io/ai-utils";

const policy: AclPolicy = {
  entries: [
    {
      action: "allow",
      resource: "/docs/**", 
      permissions: ["read", "write"]
    },
    {
      action: "deny",
      resource: "/docs/secret.md",
      permissions: ["read"],
      failMessage: "Secret documentation is restricted"
    }
  ]
};

// Check if a file can be read (without user principals)
const result = evaluateAccess("/docs/readme.md", "read", policy);

if (result.allowed) {
  console.log("Access granted:", result.message);
} else {
  console.log("Access denied:", result.message);
}

// Check access with user principals/teams
const userPrincipals = ["developer", "admin"];
const resultWithPrincipals = evaluateAccess(
  "/admin/config.json", 
  "write", 
  policy, 
  userPrincipals
);
const teamBasedPolicy: AclPolicy = {
  entries: [
    {
      action: "allow",
      resource: "/src/**",
      permissions: ["read", "write"],
      principals: ["developer", "admin"]
    },
    {
      action: "deny",
      resource: "/src/secrets/**",
      permissions: ["read"],
      principals: ["developer"],
      failMessage: "Developers cannot access secret files"
    }
  ]
};

// Developer trying to read source code
const devResult = evaluateAccess(
  "/src/app.js", 
  "read", 
  teamBasedPolicy, 
  ["developer"]
);
console.log(devResult.allowed); // true

// Developer trying to read secrets
const secretResult = evaluateAccess(
  "/src/secrets/api-key.txt", 
  "read", 
  teamBasedPolicy, 
  ["developer"]
);
console.log(secretResult.allowed); // false
console.log(secretResult.message); // "Developers cannot access secret files"

// Admin can access everything
const adminResult = evaluateAccess(
  "/src/secrets/api-key.txt", 
  "read", 
  teamBasedPolicy, 
  ["admin"]
);
console.log(adminResult.allowed); // true

// User with no matching principals
const viewerResult = evaluateAccess(
  "/src/app.js", 
  "read", 
  teamBasedPolicy, 
  ["viewer"]
);
console.log(viewerResult.allowed); // false

After setting up your ACL, make sure you test it:

Was this article helpful?

Product

Visual CMS

Theme Studio for Shopify

Sign up

Login

Featured Integrations

React

Angular

Next.js

Gatsby

Get In Touch

Chat With Us

Twitter

Linkedin

Careers

© 2020 Builder.io, Inc.

Security

Privacy Policy

Terms of Service

Get the latest from Builder.io

By submitting, you agree to our Privacy Policy

  • Fusion

  • Publish

  • Product Updates

  • Figma to Code Guide

  • Headless CMS Guide

  • Headless Commerce Guide

  • Composable DXP Guide

Security

Privacy Policy

SaaS Terms

Compliance

Cookie Preferences

Gartner Cool Vendor 2024