Skip to content

Commit

Permalink
Merge pull request #7 from phasehq/feat--secrets
Browse files Browse the repository at this point in the history
Feat: secrets
  • Loading branch information
nimish-ks authored Aug 7, 2024
2 parents c2948b1 + cec10e8 commit 685f090
Show file tree
Hide file tree
Showing 19 changed files with 2,015 additions and 365 deletions.
10 changes: 7 additions & 3 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.x
python-version: '3.12'

- name: Install dependencies
run: pip install -r requirements.txt
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest
- name: Run pytest
run: python -m pytest
run: |
python -m pytest -v tests/
116 changes: 116 additions & 0 deletions CONTRIBUTING.md
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/
```
126 changes: 117 additions & 9 deletions README.md
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.
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"

[project]
name = "phase_dev"
version = "1.1.0"
description = "Python SDK for Phase"
version = "2.0.0"
description = "Python SDK for Phase secrets manager"
readme = "README.md"
requires-python = ">=3.10"
classifiers = [
Expand Down
20 changes: 18 additions & 2 deletions src/phase/__init__.py
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'
]
Loading

0 comments on commit 685f090

Please sign in to comment.