272 lines
7.1 KiB
Markdown
272 lines
7.1 KiB
Markdown
# Sensitive Variables - Usage Guide
|
|
|
|
This document explains how to use the GNOME Keyring integration for storing sensitive variable values securely.
|
|
|
|
## Overview
|
|
|
|
Roster now supports marking variables as **sensitive**, which stores their values encrypted in GNOME Keyring instead of plain text in the JSON file.
|
|
|
|
**Use sensitive variables for:**
|
|
- API keys (`api_key`, `secret_key`)
|
|
- Authentication tokens (`bearer_token`, `oauth_token`)
|
|
- Passwords (`password`, `db_password`)
|
|
- Any secret credentials
|
|
|
|
**Use regular variables for:**
|
|
- Base URLs (`base_url`, `api_endpoint`)
|
|
- Environment names (`env`, `region`)
|
|
- Non-sensitive configuration values
|
|
|
|
## Storage
|
|
|
|
### Regular Variables
|
|
Stored in: `~/.local/share/cz.bugsy.roster/requests.json`
|
|
```json
|
|
{
|
|
"projects": [{
|
|
"variable_names": ["base_url", "api_key"],
|
|
"sensitive_variables": [], // empty = not sensitive
|
|
"environments": [{
|
|
"variables": {
|
|
"base_url": "https://api.example.com", // visible in JSON
|
|
"api_key": "secret-key-123" // visible (NOT SAFE!)
|
|
}
|
|
}]
|
|
}]
|
|
}
|
|
```
|
|
|
|
### Sensitive Variables
|
|
Stored in: GNOME Keyring (encrypted)
|
|
```json
|
|
{
|
|
"projects": [{
|
|
"variable_names": ["base_url", "api_key"],
|
|
"sensitive_variables": ["api_key"], // marked as sensitive
|
|
"environments": [{
|
|
"variables": {
|
|
"base_url": "https://api.example.com", // visible in JSON
|
|
"api_key": "" // empty placeholder
|
|
}
|
|
}]
|
|
}]
|
|
}
|
|
```
|
|
|
|
The actual value of `api_key` is stored encrypted in GNOME Keyring and automatically retrieved when needed.
|
|
|
|
## Code Examples
|
|
|
|
### Basic Usage
|
|
|
|
```python
|
|
from roster.project_manager import ProjectManager
|
|
|
|
pm = ProjectManager()
|
|
project_id = "your-project-id"
|
|
env_id = "your-environment-id"
|
|
|
|
# Mark a variable as sensitive (moves existing values to keyring)
|
|
pm.mark_variable_as_sensitive(project_id, "api_key")
|
|
|
|
# Set a sensitive variable value (goes to keyring)
|
|
pm.set_variable_value(project_id, env_id, "api_key", "my-secret-key-123")
|
|
|
|
# Get a sensitive variable value (retrieved from keyring)
|
|
value = pm.get_variable_value(project_id, env_id, "api_key")
|
|
# Returns: "my-secret-key-123"
|
|
|
|
# Check if variable is sensitive
|
|
is_sensitive = pm.is_variable_sensitive(project_id, "api_key")
|
|
# Returns: True
|
|
```
|
|
|
|
### Using in Request Substitution
|
|
|
|
```python
|
|
# Get environment with ALL values (including secrets from keyring)
|
|
environment = pm.get_environment_with_secrets(project_id, env_id)
|
|
|
|
# Now use this environment for variable substitution
|
|
from roster.variable_substitution import VariableSubstitution
|
|
|
|
vs = VariableSubstitution()
|
|
request = HttpRequest(
|
|
method="GET",
|
|
url="{{base_url}}/users",
|
|
headers={"Authorization": "Bearer {{api_key}}"},
|
|
body=""
|
|
)
|
|
|
|
substituted_request, undefined = vs.substitute_request(request, environment)
|
|
# Result:
|
|
# url = "https://api.example.com/users"
|
|
# headers = {"Authorization": "Bearer my-secret-key-123"}
|
|
```
|
|
|
|
### Mark Variable as Sensitive
|
|
|
|
```python
|
|
# Existing variable with values in all environments
|
|
pm.mark_variable_as_sensitive(project_id, "token")
|
|
|
|
# This will:
|
|
# 1. Add "token" to project.sensitive_variables list
|
|
# 2. Move all environment values to GNOME Keyring
|
|
# 3. Clear values in JSON (replaced with empty strings)
|
|
```
|
|
|
|
### Mark Variable as Non-Sensitive
|
|
|
|
```python
|
|
# Move back from keyring to JSON
|
|
pm.mark_variable_as_nonsensitive(project_id, "base_url")
|
|
|
|
# This will:
|
|
# 1. Remove "base_url" from project.sensitive_variables list
|
|
# 2. Move all environment values from keyring to JSON
|
|
# 3. Delete secrets from keyring
|
|
```
|
|
|
|
### Variable Operations (Automatic Secret Handling)
|
|
|
|
```python
|
|
# Rename a sensitive variable
|
|
pm.rename_variable(project_id, "old_token", "new_token")
|
|
# Automatically renames in both JSON AND keyring
|
|
|
|
# Delete a sensitive variable
|
|
pm.delete_variable(project_id, "api_key")
|
|
# Automatically deletes from both JSON AND keyring
|
|
|
|
# Delete an environment
|
|
pm.delete_environment(project_id, env_id)
|
|
# Automatically deletes all secrets for that environment
|
|
|
|
# Delete a project
|
|
pm.delete_project(project_id)
|
|
# Automatically deletes ALL secrets for that project
|
|
```
|
|
|
|
## Integration with UI
|
|
|
|
When implementing UI for sensitive variables:
|
|
|
|
### Variable List Display
|
|
|
|
```python
|
|
# Show lock icon for sensitive variables
|
|
for var_name in project.variable_names:
|
|
is_sensitive = var_name in project.sensitive_variables
|
|
icon = "🔒" if is_sensitive else ""
|
|
label = f"{icon} {var_name}"
|
|
```
|
|
|
|
### Variable Value Entry
|
|
|
|
```python
|
|
# Use password entry for sensitive variables
|
|
entry = Gtk.Entry()
|
|
if var_name in project.sensitive_variables:
|
|
entry.set_visibility(False) # Show bullets instead of text
|
|
entry.set_input_purpose(Gtk.InputPurpose.PASSWORD)
|
|
```
|
|
|
|
### Context Menu
|
|
|
|
```python
|
|
# Right-click menu on variable
|
|
menu = Gio.Menu()
|
|
|
|
if pm.is_variable_sensitive(project_id, var_name):
|
|
menu.append("Mark as Non-Sensitive", f"app.mark-nonsensitive::{var_name}")
|
|
else:
|
|
menu.append("Mark as Sensitive", f"app.mark-sensitive::{var_name}")
|
|
```
|
|
|
|
## How It Works
|
|
|
|
### GNOME Keyring Schema
|
|
|
|
Each secret is stored with these attributes:
|
|
```python
|
|
{
|
|
"project_id": "uuid-of-project",
|
|
"environment_id": "uuid-of-environment",
|
|
"variable_name": "api_key"
|
|
}
|
|
```
|
|
|
|
Label shown in Seahorse (Passwords and Keys app):
|
|
```
|
|
"Roster: ProjectName/EnvironmentName/variable_name"
|
|
```
|
|
|
|
### Viewing Secrets in Seahorse
|
|
|
|
1. Open "Passwords and Keys" application
|
|
2. Look under "Login" keyring
|
|
3. Find entries labeled "Roster: ..."
|
|
4. These are your sensitive variable values
|
|
|
|
### Security
|
|
|
|
- Encrypted at rest with your login password
|
|
- Automatically unlocked when you log in
|
|
- Protected by OS-level security
|
|
- Uses the same secure backend as browser passwords, WiFi passwords, SSH keys
|
|
|
|
## Migration Guide
|
|
|
|
If you have existing variables with sensitive data:
|
|
|
|
```python
|
|
project_id = "your-project-id"
|
|
sensitive_vars = ["api_key", "token", "password", "secret"]
|
|
|
|
for var_name in sensitive_vars:
|
|
pm.mark_variable_as_sensitive(project_id, var_name)
|
|
|
|
# Done! All values are now encrypted in GNOME Keyring
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
```python
|
|
from roster.secret_manager import get_secret_manager
|
|
|
|
sm = get_secret_manager()
|
|
|
|
# store_secret returns bool
|
|
success = sm.store_secret(project_id, env_id, "api_key", "value")
|
|
if not success:
|
|
# Handle error (check logs)
|
|
print("Failed to store secret")
|
|
|
|
# retrieve_secret returns None on failure
|
|
value = sm.retrieve_secret(project_id, env_id, "api_key")
|
|
if value is None:
|
|
# Secret not found or error occurred
|
|
print("Secret not found")
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
1. **Mark variables as sensitive BEFORE entering values**
|
|
- This ensures values never touch the JSON file
|
|
|
|
2. **Use descriptive variable names**
|
|
- `github_token` instead of `token`
|
|
- `stripe_api_key` instead of `key`
|
|
|
|
3. **Don't commit the JSON file with sensitive data**
|
|
- If you accidentally stored secrets in JSON, mark as sensitive to move them
|
|
|
|
4. **Regular variables are fine for most things**
|
|
- Only use sensitive variables for actual secrets
|
|
- Base URLs, regions, etc. don't need encryption
|
|
|
|
5. **Backup your GNOME Keyring**
|
|
- Sensitive variables are stored in your system keyring
|
|
- Backup `~/.local/share/keyrings/` if needed
|