-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from phasehq/feat--secrets
Feat: secrets
- Loading branch information
Showing
19 changed files
with
2,015 additions
and
365 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
## Local setup | ||
|
||
Clone the reposistory to your machine and install the required dependencies. | ||
|
||
### Create a virtual environment | ||
|
||
```fish | ||
python3 -m venv.venv | ||
``` | ||
|
||
### Install dependencies | ||
|
||
```fish | ||
pip install -r requirements.txt | ||
``` | ||
|
||
### Demo script | ||
|
||
This demo python script will create, read, update and delete secrets via the SDK. Just update the host, app, env and token constants at the top. | ||
|
||
```python | ||
from src.phase import Phase, CreateSecretsOptions, GetAllSecretsOptions, UpdateSecretOptions, DeleteSecretOptions | ||
|
||
CONSOLE_HOST = 'https://console.phase.dev' | ||
APP_NAME = '<app-name>' | ||
ENV_NAME = "<env-name>" | ||
TOKEN = '<service-token>' | ||
|
||
# Initialize the Phase object with host and service token | ||
phase = Phase(init=False, | ||
pss=TOKEN, | ||
host=CONSOLE_HOST) | ||
|
||
# Create secrets with references | ||
create_options = CreateSecretsOptions( | ||
env_name=ENV_NAME, | ||
app_name=APP_NAME, | ||
key_value_pairs=[ | ||
{"BASE_URL": "https://api.example.com"}, | ||
{"API_ENDPOINT": "${BASE_URL}/v1/data"}, | ||
{"NESTED_REF": "Nested ${API_ENDPOINT}"} | ||
] | ||
) | ||
create_result = phase.create_secrets(create_options) | ||
print(f"Create secrets result: {create_result}") | ||
|
||
# Read and resolve references | ||
get_options = GetAllSecretsOptions( | ||
env_name=ENV_NAME, | ||
app_name=APP_NAME | ||
) | ||
secrets = phase.get_all_secrets(get_options) | ||
|
||
resolved_secrets = phase.resolve_references(secrets, ENV_NAME, APP_NAME) | ||
|
||
print("\nResolved Secrets:") | ||
print("----------------") | ||
for secret in resolved_secrets: | ||
print(f"{secret.key}: {secret.value}") | ||
|
||
# Update secrets | ||
update_options = UpdateSecretOptions( | ||
env_name=ENV_NAME, | ||
app_name=APP_NAME, | ||
key="BASE_URL", | ||
value="https://api.acme.com", | ||
secret_path="/", | ||
destination_path="/", # Optional: move secret to a new path | ||
override=False, # Optional: create a personal override | ||
toggle_override=False # Optional: toggle personal override | ||
) | ||
update_result = phase.update_secret(update_options) | ||
|
||
print(f"\nUpdate secrets result: {update_result}") | ||
print("----------------") | ||
|
||
|
||
## Refetch secrets | ||
secrets = phase.get_all_secrets(get_options) | ||
|
||
resolved_secrets = phase.resolve_references(secrets, ENV_NAME, APP_NAME) | ||
|
||
print("\nResolved Secrets:") | ||
print("----------------") | ||
for secret in resolved_secrets: | ||
print(f"{secret.key}: {secret.value}") | ||
|
||
|
||
# Delete secrets | ||
delete_options = DeleteSecretOptions( | ||
env_name=ENV_NAME, | ||
app_name=APP_NAME, | ||
key_to_delete="BASE_URL", | ||
secret_path="/" | ||
) | ||
result = phase.delete_secret(delete_options) | ||
print(f"Delete result: {result}") | ||
|
||
## Refetch secrets | ||
secrets = phase.get_all_secrets(get_options) | ||
|
||
resolved_secrets = phase.resolve_references(secrets, ENV_NAME, APP_NAME) | ||
|
||
print("\nResolved Secrets:") | ||
print("----------------") | ||
for secret in resolved_secrets: | ||
print(f"{secret.key}: {secret.value}") | ||
``` | ||
|
||
## Running Tests | ||
|
||
Run the test suite with: | ||
|
||
```fish | ||
python -m pytest -v tests/ | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,143 @@ | ||
# Python SDK for Phase | ||
|
||
SDK to integrate Phase in server-side applications running Python | ||
SDK to integrate Phase in server-side applications running Python. This SDK allows you to manage secrets securely using the Phase platform. | ||
|
||
## Install | ||
|
||
`pip install phase-dev` | ||
``` | ||
pip install phase-dev | ||
``` | ||
|
||
## Import | ||
|
||
```python | ||
from phase import Phase; | ||
from phase import Phase, CreateSecretsOptions, GetAllSecretsOptions, GetSecretOptions, UpdateSecretOptions, DeleteSecretOptions | ||
``` | ||
|
||
## Initialize | ||
|
||
Initialize the SDK with your `APP_ID` and `APP_SECRET`: | ||
Initialize the SDK with your host and token: | ||
|
||
```python | ||
phase = Phase(APP_ID, APP_SECRET) | ||
phase = Phase( | ||
init=False, | ||
host='https://your-phase-host.com', | ||
pss=PHASE_SERVICE_TOKEN | ||
|
||
) | ||
``` | ||
|
||
## Usage | ||
|
||
### Encrypt | ||
### Create Secrets | ||
|
||
Create one or more secrets in a specified application and environment: | ||
|
||
```python | ||
create_options = CreateSecretsOptions( | ||
env_name="Development", | ||
app_name="Your App Name", | ||
key_value_pairs=[ | ||
{"API_KEY": "your-api-key"}, | ||
{"DB_PASSWORD": "your-db-password"} | ||
], | ||
secret_path="/api" | ||
) | ||
result = phase.create_secrets(create_options) | ||
print(f"Create secrets result: {result}") | ||
``` | ||
|
||
### Get Secrets | ||
|
||
Fetch one or more secrets from a specified application and environment: | ||
|
||
```python | ||
get_options = GetAllSecretsOptions( | ||
env_name="Development", | ||
app_name="Your App Name", | ||
tag="api", # Optional: filter by tag | ||
secret_path="/api" # Optional: specify path | ||
) | ||
secrets = phase.get_all_secrets(get_options) | ||
for secret in secrets: | ||
print(f"Key: {secret.key}, Value: {secret.value}") | ||
``` | ||
|
||
To get a specific secret: | ||
|
||
```python | ||
ciphertext = phase.encrypt("hello world"); | ||
get_options = GetSecretOptions( | ||
env_name="Development", | ||
app_name="Your App Name", | ||
key_to_find="API_KEY", | ||
secret_path="/api" | ||
) | ||
secret = phase.get_secret(get_options) | ||
if secret: | ||
print(f"Key: {secret.key}, Value: {secret.value}") | ||
``` | ||
|
||
### Decrypt | ||
### Update Secrets | ||
|
||
Update an existing secret in a specified application and environment: | ||
|
||
```python | ||
plaintext = phase.decrypt(ciphertext); | ||
update_options = UpdateSecretOptions( | ||
env_name="Development", | ||
app_name="Your App Name", | ||
key="API_KEY", | ||
value="new-api-key-value", | ||
secret_path="/api", | ||
destination_path="/new-api", # Optional: move secret to a new path | ||
override=False, # Optional: create a personal override | ||
toggle_override=False # Optional: toggle personal override | ||
) | ||
result = phase.update_secret(update_options) | ||
print(f"Update result: {result}") | ||
``` | ||
|
||
### Delete Secrets | ||
|
||
Delete a secret from a specified application and environment: | ||
|
||
```python | ||
delete_options = DeleteSecretOptions( | ||
env_name="Development", | ||
app_name="Your App Name", | ||
key_to_delete="API_KEY", | ||
secret_path="/api" | ||
) | ||
result = phase.delete_secret(delete_options) | ||
print(f"Delete result: {result}") | ||
``` | ||
|
||
### Resolve Secret References | ||
|
||
Resolve references in secret values: | ||
|
||
```python | ||
get_options = GetAllSecretsOptions( | ||
env_name="Development", | ||
app_name="Your App Name" | ||
) | ||
secrets = phase.get_all_secrets(get_options) | ||
resolved_secrets = phase.resolve_references(secrets, "Development", "Your App Name") | ||
for secret in resolved_secrets: | ||
print(f"Key: {secret.key}, Resolved Value: {secret.value}") | ||
``` | ||
|
||
## Error Handling | ||
|
||
The SDK methods may raise exceptions for various error conditions. It's recommended to wrap SDK calls in try-except blocks to handle potential errors: | ||
|
||
```python | ||
try: | ||
get_options = GetAllSecretsOptions(env_name="Development", app_name="Your App Name") | ||
secrets = phase.get_all_secrets(get_options) | ||
except ValueError as e: | ||
print(f"An error occurred: {e}") | ||
``` | ||
|
||
## Note on Security | ||
|
||
Never hard-code sensitive information like tokens or secrets directly in your code. Always use environment variables or secure configuration management to provide these values to your application. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,19 @@ | ||
from .phase import Phase | ||
from .phase import ( | ||
Phase, | ||
GetSecretOptions, | ||
GetAllSecretsOptions, | ||
CreateSecretsOptions, | ||
UpdateSecretOptions, | ||
DeleteSecretOptions, | ||
PhaseSecret | ||
) | ||
|
||
__all__ = ['Phase'] | ||
__all__ = [ | ||
'Phase', | ||
'GetSecretOptions', | ||
'GetAllSecretsOptions', | ||
'CreateSecretsOptions', | ||
'UpdateSecretOptions', | ||
'DeleteSecretOptions', | ||
'PhaseSecret' | ||
] |
Oops, something went wrong.