Skip to content

feat(cli): add check, schema, data, and tenant management commands#2835

Open
HarshadaGawas05 wants to merge 2 commits intoPermify:masterfrom
HarshadaGawas05:feat/cli-management-commands
Open

feat(cli): add check, schema, data, and tenant management commands#2835
HarshadaGawas05 wants to merge 2 commits intoPermify:masterfrom
HarshadaGawas05:feat/cli-management-commands

Conversation

@HarshadaGawas05
Copy link

@HarshadaGawas05 HarshadaGawas05 commented Mar 21, 2026

Proposed Changes

Implements complete CLI management commands for interacting with a running Permify instance via gRPC, addressing #146.

Adds four command groups that were completely missing from the CLI:

  • permify check — evaluate permissions via Permission.Check RPC
  • permify schema write|read — manage authorization schemas
  • permify data write|read — write/read relationship tuples
  • permify tenant create|list|delete — manage tenants

Credentials are loaded from ~/.permify/credentials (YAML). When
api_token is set, TLS is used automatically (system trust store,
or custom CA via tls_ca_path). Without a token, insecure transport
is used for local development.

Improvements over existing PR #2799:

  • Fixes critical TLS/token conflict — API token now correctly uses
    TLS instead of insecure transport
  • All 4 management command groups implemented (not just configure)
  • Comprehensive test coverage with mocked gRPC clients
  • All comments in English
  • Context timeouts on all gRPC calls
  • gofumpt formatted, make vet clean

Proof

$ ./permify --help
Available Commands:
  check       Check whether a subject has a permission on a resource
  data        Write or read relationship data on a remote Permify server
  schema      Read or write authorization schema on a remote Permify server
  tenant      Create, list, or delete tenants on a remote Permify server

$ go test -race ./pkg/cmd/... 
ok  github.com/Permify/permify/pkg/cmd  2.010s

$ make vet && make build — all passed

New Files

File Role
pkg/cmd/credentials.go Credential loading + gRPC dial with TLS/token support
pkg/cmd/check.go permify check command
pkg/cmd/schema.go permify schema write|read commands
pkg/cmd/data.go permify data write|read commands
pkg/cmd/tenant.go permify tenant create|list|delete commands
pkg/cmd/parse_refs.go Parses type:id strings into protobuf types
pkg/cmd/grpc_output.go Human-readable output helpers
pkg/cmd/grpc_context.go Context timeout helpers for gRPC calls
pkg/cmd/*_test.go Full test coverage with mocked gRPC clients

Checklist

/claim #146

Summary by CodeRabbit

  • New Features
    • Added CLI commands for permissions, schemas, relationship data, and tenant management via gRPC
    • Added credentials management with TLS and API token support, plus parsing/formatting helpers and pagination/time formatting
  • Tests
    • Added comprehensive unit tests covering commands, credential handling, parsing, output formatting, and gRPC interactions
  • Chores
    • Added a Makefile vet target
    • Updated .gitignore to exclude local permify credentials/configuration paths

@coderabbitai
Copy link

coderabbitai bot commented Mar 21, 2026

📝 Walkthrough

Walkthrough

Adds four gRPC-based CLI commands (check, schema, data, tenant) plus supporting credential loading, gRPC dialing/TLS, reference parsing, output formatting, context timeout helpers, and test stubs; registers the new commands in the root CLI and updates .gitignore and Makefile.

Changes

Cohort / File(s) Summary
Build & Configuration
/.gitignore, /Makefile
Added /permify to .gitignore and a .PHONY vet target running go vet ./....
CLI Entry
cmd/permify/permify.go
Registered four new top-level subcommands: NewCheckCommand, NewSchemaCommand, NewDataCommand, and NewTenantCommand.
Check Command
pkg/cmd/check.go, pkg/cmd/check_test.go
New check Cobra command that validates flags, parses refs, dials gRPC, calls PermissionClient.Check, and formats allowed/denied output; includes unit tests.
Schema Command
pkg/cmd/schema.go, pkg/cmd/schema_test.go
New schema command with write and read subcommands invoking SchemaClient RPCs; includes tests and output formatting.
Data Command
pkg/cmd/data.go, pkg/cmd/data_test.go
New data command with write (YAML tuples) and read (relationship query with pagination) subcommands; includes validation and tests.
Tenant Command
pkg/cmd/tenant.go, pkg/cmd/tenant_test.go
New tenant command (create, list, delete) calling TenancyClient RPCs, formatting timestamps, and adding tests.
Credentials & Dialing
pkg/cmd/credentials.go, pkg/cmd/credentials_test.go
New YAML-backed CredentialsFile, resolution helpers, TLS/CAs and bearer-token per-RPC credentials, GRPCDialOptions and DialGRPC; tests for parsing and dial-option behaviors.
gRPC Context & Mocks
pkg/cmd/grpc_context.go, pkg/cmd/grpc_mocks_test.go
Added default gRPC timeout helper and stub client implementations for testing various gRPC client interfaces.
Output & Parsing Helpers
pkg/cmd/grpc_output.go, pkg/cmd/grpc_output_test.go, pkg/cmd/parse_refs.go, pkg/cmd/parse_refs_test.go
Added GRPCStatusError, formatting helpers (check result, schema summary, tuple lines), and ParseEntityRef/ParseSubjectRef with tests.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI as Check Command
    participant Creds as Credentials Module
    participant GRPC as gRPC Dialer
    participant Server as Permify Server

    User->>CLI: run `permify check --tenant-id ... --entity ... --resource ... --permission ...`
    CLI->>CLI: validate flags, parse refs
    CLI->>Creds: Resolve & load credentials
    Creds-->>CLI: CredentialsFile (endpoint, token, tls_ca)
    CLI->>GRPC: DialGRPC(endpoint, options)
    GRPC-->>CLI: grpc.ClientConn
    CLI->>Server: PermissionClient.Check(request)
    Server-->>CLI: PermissionCheckResponse (can: true/false, metadata)
    CLI->>CLI: formatCheckResult(writer, response)
    CLI->>User: print allowed/denied and metadata
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • tolgaOzen

Poem

🐇 A rabbit hops with keys in paw,

creds and TLS all set to thaw,
check, schema, data, tenant sing,
gRPC bells and tests take wing,
hop — the CLI's ready for the law.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.31% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely describes the main feature addition: new CLI commands for check, schema, data, and tenant management operations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can use OpenGrep to find security vulnerabilities and bugs across 17+ programming languages.

OpenGrep is compatible with Semgrep configurations. Add an opengrep.yml or semgrep.yml configuration file to your project to enable OpenGrep analysis.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (2)
pkg/cmd/parse_refs_test.go (1)

18-30: Consider table-driven invalid-case coverage for parser boundaries.

Current tests miss cases like empty input, :id, type:, and type:id:extra. Adding these would lock parser behavior and prevent regressions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/cmd/parse_refs_test.go` around lines 18 - 30, Add a table-driven test
covering parser boundary cases for ParseEntityRef (and similarly ParseSubjectRef
if applicable): replace or augment TestParseEntityRef_Invalid with a slice of
invalid inputs such as "", ":id", "type:", "type:id:extra", "nocolon" and
iterate asserting ParseEntityRef returns an error for each input; also add a
parallel table-driven test for ParseSubjectRef to assert errors on invalid
subject inputs (e.g., "", ":id", "type:", "type:id:extra") while keeping calls
to ParseSubjectRef/ParseEntityRef and using require.Error/require.NoError as
appropriate to lock down behavior.
pkg/cmd/tenant.go (1)

75-77: Redundant default assignment for pageSize.

The flag definition on line 95 already sets the default to 100, so the check on lines 75-77 is unnecessary—pageSize will never be 0 unless explicitly set by the user.

♻️ Suggested simplification
 	cmd := &cobra.Command{
 		Use:   "list",
 		Short: "List tenants (Tenancy.List)",
 		RunE: func(cmd *cobra.Command, _ []string) error {
-			if pageSize == 0 {
-				pageSize = 100
-			}
-
 			conn, err := DialGRPC(credentialsPath)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/cmd/tenant.go` around lines 75 - 77, The conditional block that assigns
pageSize = 100 when pageSize == 0 is redundant because the flag definition
already sets the default to 100; remove the if block that checks pageSize (the
snippet containing "if pageSize == 0 { pageSize = 100 }") and rely on the flag's
default; if you want to validate user input instead, replace the removal with an
explicit validation that returns an error when pageSize <= 0, referencing the
pageSize variable and the flag definition that sets its default.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.gitignore:
- Line 32: The .gitignore entry "permify" is unanchored and may match any path
segment named permify; update the rule in .gitignore to anchor the generated
root binary by replacing the unanchored token with an anchored entry (prefix
with a slash) so the ignore specifically targets ./permify.

In `@pkg/cmd/credentials.go`:
- Around line 61-63: Trimmed c.TLSCAPath is currently treated as relative to the
process CWD; change the loader so that after lines trimming c.Endpoint,
c.APIToken, c.TLSCAPath you resolve c.TLSCAPath against the directory containing
the credentials file: if c.TLSCAPath is non-empty and not an absolute path, join
it with filepath.Dir(<the credentials file path variable used to load the
credentials>) and assign the result back to c.TLSCAPath so subsequent CA loading
uses the correct absolute path.

In `@pkg/cmd/data.go`:
- Around line 166-168: The code currently only sets a default when pageSize == 0
but does not enforce the documented 1–100 range; update the validation for the
pageSize variable in pkg/cmd/data.go (the code that reads the --page-size flag)
to ensure it falls within 1..100 — e.g., if pageSize < 1 or pageSize > 100
return a user-facing error (or clamp to 100 if you prefer) so values above 100
are rejected/adjusted to match the documentation.
- Around line 118-127: Validation currently allows whitespace-only tuple fields
and reports zero-based tuple indices; update the loop that iterates doc.Tuples
to trim whitespace from row.Entity.Type, row.Entity.ID, row.Subject.Type,
row.Subject.ID and row.Relation (use strings.TrimSpace) before validating, and
change the error messages in those checks to report the tuple number as i+1
instead of i; ensure you reference the same identifiers (doc.Tuples,
row.Entity.Type, row.Entity.ID, row.Subject.Type, row.Subject.ID, row.Relation)
so callers see trimmed values and 1-based indices in errors.

In `@pkg/cmd/parse_refs.go`:
- Around line 30-38: splitTypeID currently allows multiple colons (e.g.
"user:alice:owner") by using Index only; change the validation in splitTypeID to
require exactly one colon (for example use strings.Count(s, ":") != 1 or compare
strings.Index and strings.LastIndex) and return the same "expected type:id (e.g.
user:1)" error when the count is not exactly one, otherwise split on that single
colon and return typ and id.

---

Nitpick comments:
In `@pkg/cmd/parse_refs_test.go`:
- Around line 18-30: Add a table-driven test covering parser boundary cases for
ParseEntityRef (and similarly ParseSubjectRef if applicable): replace or augment
TestParseEntityRef_Invalid with a slice of invalid inputs such as "", ":id",
"type:", "type:id:extra", "nocolon" and iterate asserting ParseEntityRef returns
an error for each input; also add a parallel table-driven test for
ParseSubjectRef to assert errors on invalid subject inputs (e.g., "", ":id",
"type:", "type:id:extra") while keeping calls to ParseSubjectRef/ParseEntityRef
and using require.Error/require.NoError as appropriate to lock down behavior.

In `@pkg/cmd/tenant.go`:
- Around line 75-77: The conditional block that assigns pageSize = 100 when
pageSize == 0 is redundant because the flag definition already sets the default
to 100; remove the if block that checks pageSize (the snippet containing "if
pageSize == 0 { pageSize = 100 }") and rely on the flag's default; if you want
to validate user input instead, replace the removal with an explicit validation
that returns an error when pageSize <= 0, referencing the pageSize variable and
the flag definition that sets its default.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1e72409b-f673-4b8a-9c4e-ec40785d1c1a

📥 Commits

Reviewing files that changed from the base of the PR and between 902d683 and d823fa0.

📒 Files selected for processing (19)
  • .gitignore
  • Makefile
  • cmd/permify/permify.go
  • pkg/cmd/check.go
  • pkg/cmd/check_test.go
  • pkg/cmd/credentials.go
  • pkg/cmd/credentials_test.go
  • pkg/cmd/data.go
  • pkg/cmd/data_test.go
  • pkg/cmd/grpc_context.go
  • pkg/cmd/grpc_mocks_test.go
  • pkg/cmd/grpc_output.go
  • pkg/cmd/grpc_output_test.go
  • pkg/cmd/parse_refs.go
  • pkg/cmd/parse_refs_test.go
  • pkg/cmd/schema.go
  • pkg/cmd/schema_test.go
  • pkg/cmd/tenant.go
  • pkg/cmd/tenant_test.go

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
pkg/cmd/credentials.go (1)

89-89: Minor: redundant TrimSpace call.

c.APIToken is already trimmed in LoadCredentials at line 62. This duplicate trim is harmless but unnecessary.

♻️ Suggested simplification
 func GRPCDialOptions(c *CredentialsFile) ([]grpc.DialOption, error) {
-	token := strings.TrimSpace(c.APIToken)
+	token := c.APIToken
 	if token == "" {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/cmd/credentials.go` at line 89, Remove the redundant trimming when
assigning token in credentials.go: since LoadCredentials already trims and sets
c.APIToken, replace or simplify the assignment token :=
strings.TrimSpace(c.APIToken) to just token := c.APIToken (or otherwise use
c.APIToken directly) in the function where token is created; keep references to
c.APIToken, token, and LoadCredentials to ensure callers rely on the
already-trimmed value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/cmd/credentials.go`:
- Line 89: Remove the redundant trimming when assigning token in credentials.go:
since LoadCredentials already trims and sets c.APIToken, replace or simplify the
assignment token := strings.TrimSpace(c.APIToken) to just token := c.APIToken
(or otherwise use c.APIToken directly) in the function where token is created;
keep references to c.APIToken, token, and LoadCredentials to ensure callers rely
on the already-trimmed value.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6d1be37c-a327-4ceb-85b0-b08f49fbfa51

📥 Commits

Reviewing files that changed from the base of the PR and between d823fa0 and ef2373c.

📒 Files selected for processing (6)
  • .gitignore
  • pkg/cmd/credentials.go
  • pkg/cmd/credentials_test.go
  • pkg/cmd/data.go
  • pkg/cmd/parse_refs.go
  • pkg/cmd/parse_refs_test.go
✅ Files skipped from review due to trivial changes (3)
  • .gitignore
  • pkg/cmd/credentials_test.go
  • pkg/cmd/data.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • pkg/cmd/parse_refs_test.go
  • pkg/cmd/parse_refs.go

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant