Skip to content

Commit 17d4f6f

Browse files
committed
Added support for listing permissions. Added additional tests
Signed-off-by: Kevin Stanley <stanleyk@objectcomputing.com>
1 parent b8fad80 commit 17d4f6f

12 files changed

Lines changed: 1548 additions & 5 deletions

File tree

unityauth-cli/README.md

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ The UnityAuth CLI is a cross-platform command-line tool that enables system and
1111
| Feature | Status |
1212
|---------|--------|
1313
| Authentication (login/logout/token-info) | Implemented |
14-
| User Management (create/list/update roles) | Implemented |
14+
| User Management (create/list/update/update-profile) | Implemented |
1515
| Tenant Discovery (list/users) | Implemented |
1616
| Role Discovery (list) | Implemented |
17+
| Permissions Discovery (list) | Implemented |
1718
| Configuration Management | Implemented |
18-
| Permission Verification | Planned |
19+
| Service Discovery (list) | Not Available (see [Known Limitations](#known-limitations)) |
1920
| Batch Operations | Planned |
2021

2122
## Installation
@@ -125,12 +126,15 @@ unityauth
125126
├── user # User management
126127
│ ├── create # Create a new user
127128
│ ├── list # List users in a tenant
128-
│ └── update # Update user roles
129+
│ ├── update # Update user roles
130+
│ └── update-profile # Update your own profile
129131
├── tenant # Tenant discovery
130132
│ ├── list # List accessible tenants
131133
│ └── users # List users in a tenant
132-
└── role # Role discovery
133-
└── list # List available roles
134+
├── role # Role discovery
135+
│ └── list # List available roles
136+
└── permissions # Permission discovery
137+
└── list # List your permissions for a tenant/service
134138
```
135139

136140
## Global Options
@@ -203,6 +207,29 @@ unityauth config set timeout 60 # Set request timeout
203207
unityauth config edit # Open in editor
204208
```
205209

210+
## Known Limitations
211+
212+
### No Service Discovery API
213+
214+
The UnityAuth backend does not expose an API endpoint to list available services. This means:
215+
216+
- **No `service list` command**: The CLI cannot retrieve a list of services
217+
- **Service IDs must be known in advance**: Commands like `permissions list` require a `--service-id` that users must know beforehand
218+
219+
**Common Service IDs:**
220+
221+
| ID | Name | Description |
222+
|----|------|-------------|
223+
| 1 | Libre311 | Libre311 citizen request management |
224+
225+
**Workaround:** Contact your UnityAuth administrator for the correct service ID, or query the database directly if you have access:
226+
227+
```sql
228+
SELECT id, name, description FROM service WHERE status = 'ENABLED';
229+
```
230+
231+
**For Backend Developers:** To enable service discovery in the CLI, implement a `GET /api/services` endpoint in the UnityAuth backend.
232+
206233
## Documentation
207234

208235
- [User Guide](docs/user-guide.md) - Complete command reference

unityauth-cli/docs/user-guide.md

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ Complete command reference for the UnityAuth command-line interface.
2323
- [tenant users](#tenant-users)
2424
- [Role Commands](#role-commands)
2525
- [role list](#role-list)
26+
- [Permissions Commands](#permissions-commands)
27+
- [permissions list](#permissions-list)
28+
- [Known Limitations](#known-limitations)
2629
- [Output Formats](#output-formats)
2730
- [Exit Codes](#exit-codes)
2831
- [Environment Variables](#environment-variables)
@@ -534,6 +537,96 @@ unityauth role list --format json | jq '.[] | select(.name == "Tenant Administra
534537

535538
---
536539

540+
## Permissions Commands
541+
542+
### permissions list
543+
544+
List your permissions for a specific tenant and service.
545+
546+
```
547+
unityauth permissions list --tenant-id TENANT_ID --service-id SERVICE_ID
548+
```
549+
550+
**Required Options:**
551+
552+
| Option | Description |
553+
|--------|-------------|
554+
| `--tenant-id INTEGER` | Tenant ID to check permissions for |
555+
| `--service-id INTEGER` | Service ID to check permissions for |
556+
557+
**Output:**
558+
559+
Returns all permissions the authenticated user has for the specified tenant and service combination.
560+
561+
**Examples:**
562+
563+
```bash
564+
# List your permissions for tenant 1 and Libre311 service (ID: 1)
565+
unityauth permissions list --tenant-id 1 --service-id 1
566+
567+
# Get JSON output
568+
unityauth permissions list --tenant-id 1 --service-id 1 --format json
569+
570+
# Get CSV output
571+
unityauth permissions list --tenant-id 1 --service-id 1 --format csv
572+
```
573+
574+
**Sample Output:**
575+
576+
```
577+
┌───────────────────────────────┐
578+
│ Permission │
579+
├───────────────────────────────┤
580+
│ AUTH_SERVICE_VIEW-SYSTEM │
581+
│ AUTH_SERVICE_EDIT-SYSTEM │
582+
│ LIBRE311_ADMIN_VIEW-TENANT │
583+
│ LIBRE311_ADMIN_EDIT-TENANT │
584+
└───────────────────────────────┘
585+
```
586+
587+
**Common Errors:**
588+
589+
| Error | Cause | Solution |
590+
|-------|-------|----------|
591+
| "No tenant found" | Invalid tenant ID | Check tenant ID with `tenant list` |
592+
| "The service does not exist" | Invalid service ID | See [Known Limitations](#known-limitations) |
593+
| "Tenant/Service not available" | User not authorized for this combination | Contact your administrator |
594+
595+
---
596+
597+
## Known Limitations
598+
599+
### Service Discovery
600+
601+
**There is currently no API endpoint to list available services.** The UnityAuth backend does not expose a `/api/services` endpoint, so the CLI cannot provide a `service list` command.
602+
603+
**What this means:**
604+
605+
- Commands that require `--service-id` (like `permissions list`) need you to know the service ID in advance
606+
- Service IDs are configured by database administrators and are deployment-specific
607+
608+
**Current Services:**
609+
610+
In most UnityAuth deployments, the following service is available:
611+
612+
| ID | Name | Description |
613+
|----|------|-------------|
614+
| 1 | Libre311 | Libre311 citizen request management system |
615+
616+
**Workaround:**
617+
618+
If you don't know the service ID, contact your UnityAuth administrator or check the database directly:
619+
620+
```sql
621+
SELECT id, name, description, status FROM service;
622+
```
623+
624+
**For Developers:**
625+
626+
To add a `service list` command, a new API endpoint would need to be implemented in the UnityAuth backend (e.g., `GET /api/services`).
627+
628+
---
629+
537630
## Output Formats
538631

539632
### Table (Default)

unityauth-cli/src/unityauth_cli/cli.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ def register_commands() -> None:
238238
from unityauth_cli.commands.users import create, update, update_profile, list_users
239239
from unityauth_cli.commands.tenants import list_tenants, tenant_users
240240
from unityauth_cli.commands.roles import list_roles
241+
from unityauth_cli.commands.permissions import list_permissions
241242

242243
# Register authentication commands
243244
cli.add_command(login)
@@ -274,3 +275,11 @@ def role():
274275
pass
275276

276277
role.add_command(list_roles, name='list')
278+
279+
# Register permissions commands
280+
@cli.group()
281+
def permissions():
282+
"""Permission discovery and verification commands."""
283+
pass
284+
285+
permissions.add_command(list_permissions, name='list')
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
"""Permission commands: list, check.
2+
3+
Handles permission discovery and verification operations.
4+
"""
5+
6+
import sys
7+
8+
import click
9+
10+
from unityauth_cli.cli import (
11+
CLIContext,
12+
console,
13+
error,
14+
handle_error,
15+
info,
16+
pass_context,
17+
require_auth,
18+
success,
19+
warning,
20+
)
21+
from unityauth_cli.client import UnityAuthAPIClient
22+
from unityauth_cli.formatters.table import format_table
23+
from unityauth_cli.formatters.json_fmt import format_json
24+
from unityauth_cli.formatters.csv_fmt import format_csv
25+
from unityauth_cli.utils.errors import AuthorizationError, ValidationError
26+
27+
28+
@click.command('list')
29+
@click.option('--tenant-id', required=True, type=int, help='Tenant ID to check permissions for')
30+
@click.option('--service-id', required=True, type=int, help='Service ID to check permissions for')
31+
@pass_context
32+
@require_auth
33+
def list_permissions(
34+
ctx: CLIContext,
35+
tenant_id: int,
36+
service_id: int,
37+
client: UnityAuthAPIClient,
38+
) -> None:
39+
"""List your permissions for a tenant and service.
40+
41+
Returns all permissions the authenticated user has for the specified
42+
tenant and service combination.
43+
44+
\b
45+
Examples:
46+
unityauth permissions list --tenant-id 1 --service-id 1
47+
unityauth permissions list --tenant-id 1 --service-id 1 --format json
48+
"""
49+
try:
50+
# Validate IDs
51+
if tenant_id <= 0:
52+
raise ValidationError("Tenant ID must be a positive integer")
53+
if service_id <= 0:
54+
raise ValidationError("Service ID must be a positive integer")
55+
56+
if ctx.verbose:
57+
info(f"Fetching permissions for tenant {tenant_id}, service {service_id}...")
58+
59+
# Build request payload
60+
payload = {
61+
'tenantId': tenant_id,
62+
'serviceId': service_id,
63+
}
64+
65+
# Make POST request to get permissions
66+
response = client.post('/api/principal/permissions', data=payload)
67+
68+
# Handle error response (Failure case)
69+
if response and 'errorMessage' in response:
70+
error(f"Failed to get permissions: {response['errorMessage']}")
71+
sys.exit(1)
72+
73+
# Handle success response
74+
permissions = response.get('permissions', []) if response else []
75+
76+
if not permissions:
77+
warning("No permissions found for this tenant/service combination")
78+
return
79+
80+
# Format and display output based on format option
81+
if ctx.output_format == 'json':
82+
console.print(format_json({'permissions': permissions}))
83+
elif ctx.output_format == 'csv':
84+
# For CSV, create a simple list format
85+
headers = ['permission']
86+
rows = [{'permission': perm} for perm in permissions]
87+
console.print(format_csv(rows, headers))
88+
else:
89+
# Table format (default)
90+
headers = ['Permission']
91+
rows = [[perm] for perm in permissions]
92+
console.print(format_table(rows, headers))
93+
94+
if ctx.verbose:
95+
info(f"Total permissions: {len(permissions)}")
96+
97+
except AuthorizationError as e:
98+
error(
99+
str(e),
100+
"You may not have access to this tenant/service combination.\n"
101+
"Contact your administrator for access."
102+
)
103+
sys.exit(3)
104+
except Exception as e:
105+
handle_error(e)

unityauth-cli/tests/conftest.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
"""Shared pytest fixtures for UnityAuth CLI tests."""
22

33
import pytest
4+
from click.testing import CliRunner
45
from unittest.mock import MagicMock, patch
56

67
from unityauth_cli.cli import CLIContext
78
from unityauth_cli.config import Configuration
9+
from unityauth_cli.client import UnityAuthAPIClient
810

911

1012
@pytest.fixture
@@ -45,3 +47,20 @@ def mock_requests_session():
4547
session_instance = MagicMock()
4648
mock.return_value = session_instance
4749
yield session_instance
50+
51+
52+
@pytest.fixture
53+
def cli_runner():
54+
"""Create a Click CLI test runner."""
55+
return CliRunner()
56+
57+
58+
@pytest.fixture
59+
def mock_api_client():
60+
"""Create a mock API client."""
61+
client = MagicMock(spec=UnityAuthAPIClient)
62+
client.get.return_value = None
63+
client.post.return_value = None
64+
client.patch.return_value = None
65+
client.delete.return_value = None
66+
return client

unityauth-cli/tests/unit/commands/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)