Skip to main content

Permissions in Practice

Two users (alice, project lead; bob, read-only analyst), an Analytics project, a full-access and a read-only policy, project-scoped API keys for each, and a check of what each key can do.

Prerequisites​

Export the server URL:

export SOAT_BASE_URL=http://localhost:5047

CLI path flags are kebab-cased per resource: --user-id, --project-id.


Step 1 — Log in as admin​

Admin is the built-in superuser role and bypasses policy evaluation (IAM — Authentication).

soat login-user --username admin --password Admin1234!

Save the token and update the profile:

soat configure
# Token: <paste token here>

Step 2 — Create regular users​

Only admins can create users.

ALICE_ID=$(soat create-user --username alice --password Alice1234! | jq -r '.id')
BOB_ID=$(soat create-user --username bob --password Bob1234! | jq -r '.id')
echo "alice: $ALICE_ID"
echo "bob : $BOB_ID"

Keep each id (user_…) for attaching policies.


Step 3 — Create the Analytics project​

See Projects.

PROJECT_ID=$(soat create-project --name "Analytics" | jq -r '.id')
echo "project: $PROJECT_ID"

Keep the returned id (proj_…).


Step 4 — Create policies​

Policies are global; the resource field uses SRNs to restrict which projects a policy covers. Format and evaluation rules: IAM — Policy Documents.

4a — Full-access policy (for Alice)​

All actions on every resource inside the Analytics project.

FULL_POLICY_ID=$(soat create-policy \
--name "analytics-full-access" \
--description "Full access to the Analytics project" \
--document '{
"statement": [
{
"effect": "Allow",
"action": ["*"],
"resource": ["srn:'"$PROJECT_ID"':*:*"]
}
]
}' | jq -r '.id')
echo "full-access policy: $FULL_POLICY_ID"

4b — Read-only policy (for Bob)​

Read actions on files inside the project only. files:* and documents:* actions: Permissions Reference.

READ_POLICY_ID=$(soat create-policy \
--name "analytics-read-only" \
--description "Read-only access to files and documents in Analytics" \
--document '{
"statement": [
{
"effect": "Allow",
"action": [
"files:GetFile",
"documents:ListDocuments",
"documents:GetDocument"
],
"resource": ["srn:'"$PROJECT_ID"':*:*"]
}
]
}' | jq -r '.id')
echo "read-only policy: $READ_POLICY_ID"

Step 5 — Attach policies to users​

Full-access to Alice, read-only to Bob (Policies — Attaching Policies to Users).

note

PUT /users/:user_id/policies replaces the user's entire policy list with the provided array.

# Give Alice full access
soat attach-user-policies \
--user-id "$ALICE_ID" \
--policy-ids '["'"$FULL_POLICY_ID"'"]'

# Give Bob read-only access
soat attach-user-policies \
--user-id "$BOB_ID" \
--policy-ids '["'"$READ_POLICY_ID"'"]'

Step 6 — Create API keys​

API keys give programmatic access without a password. Both are scoped to the Analytics project: Alice's inherits her full-access policy; Bob's is further restricted to the read-only policy. Log in as each user first to obtain their JWT tokens.

# Log in as Alice and save her token to a named profile
soat login-user --username alice --password Alice1234!
soat configure --profile alice

# Log in as Bob and save his token
soat login-user --username bob --password Bob1234!
soat configure --profile bob

# Create Alice's API key (using her profile)
ALICE_API_KEY=$(soat --profile alice create-api-key \
--name "alice-analytics-key" \
--project-id "$PROJECT_ID" | jq -r '.key')

# Create Bob's API key, explicitly restricting it to the read-only policy
BOB_API_KEY=$(soat --profile bob create-api-key \
--name "bob-analytics-key" \
--project-id "$PROJECT_ID" \
--policy-ids '["'"$READ_POLICY_ID"'"]' | jq -r '.key')

echo "Alice key: $ALICE_API_KEY"
echo "Bob key : $BOB_API_KEY"
warning

The raw sk_… key is returned only once; store it in a secret manager. If lost, delete the key and create a new one.


Step 7 — Verify permissions​

Upload and list operations are from the Files module.

Alice can upload a file​

echo "hello world" > sample.txt

SOAT_TOKEN="$ALICE_API_KEY" soat upload-file-base64 \
--project-id "$PROJECT_ID" \
--content "$(base64 -w 0 sample.txt)" \
--filename "sample.txt"

# Switch to Bob's profile to test his permissions
# → expect-fail
SOAT_TOKEN="$BOB_API_KEY" soat upload-file-base64 \
--project-id "$PROJECT_ID" \
--content "$(base64 -w 0 sample.txt)" \
--filename "sample.txt"

Bob can read files​

# Bob can list files — read is allowed
SOAT_TOKEN="$BOB_API_KEY" soat list-files --project-id "$PROJECT_ID"

Bob's key cannot exceed Bob's own permissions​

Assigning FULL_POLICY_ID to Bob's API key grants nothing beyond Bob's user policies: effective permissions are the intersection (IAM — Authorization Model).

# Attempt to create a key for Bob with the full-access policy
# Key is created, but when used it is still limited to Bob's read-only permissions
# because Bob's user policies are the ceiling.
ESCALATED_KEY=$(soat --profile bob create-api-key \
--name "bob-escalation-attempt" \
--project-id "$PROJECT_ID" \
--policy-ids '["'"$FULL_POLICY_ID"'"]' | jq -r '.key')

# → expect-fail
SOAT_TOKEN="$ESCALATED_KEY" soat upload-file-base64 \
--project-id "$PROJECT_ID" \
--content "$(base64 -w 0 sample.txt)" \
--filename "sample.txt"

Next steps​

  • IAM — policy document format, wildcard actions, SRN patterns.
  • Policies — policy CRUD API.
  • API Keys — rotation and revocation.