Skip to content

Commit d4b912a

Browse files
Live Stream API DRM Key Publisher Samples (GoogleCloudPlatform#9923)
* Key publisher sample code for Live Stream API DRM feature * change main_test from unittest to pytest * simplify mocking logic for secret manager * fix lint errors, add code owners detail for live stream api * fix license header issue, move live stream API owner to no owner * specify live stream API ownership * change http to https for license link * remove live stream API from no owner section * add live stream api blunderbuss config * change cloud-media-livestream codeowner to cloud-media-team * fix import order and lint issues * import List for type hint * reorder import in main.py, remove unneeded requirement * import List for type hint in fake_client * requirement different cryptography package version based on python version * Remove support for python 2.7 * fix nox test opt out versions format, exclude 3.6 as well * Apply suggestions from code review Co-authored-by: Dan Lee <71398022+dandhlee@users.noreply.github.com> * address code review comments to improve README * add type hints * fix quotation styles * add type hint to main_test and enforce type hint * update package script to remove unneeded thrid-party specific script, add instruction on how to use the script * Apply suggestions from code review Co-authored-by: Dan Lee <71398022+dandhlee@users.noreply.github.com> * fix docstring indentation, simplify import --------- Co-authored-by: Dan Lee <71398022+dandhlee@users.noreply.github.com>
1 parent b003718 commit d4b912a

File tree

14 files changed

+1013
-0
lines changed

14 files changed

+1013
-0
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
/asset/**/* @GoogleCloudPlatform/cloud-asset-analysis-team @GoogleCloudPlatform/cloud-asset-platform-team @GoogleCloudPlatform/python-samples-reviewers
9595
/bigquery/**/* @chalmerlowe @GoogleCloudPlatform/python-samples-reviewers
9696
/bigquery/remote_function/**/* @autoerr @GoogleCloudPlatform/python-samples-reviewers
97+
/cloud-media-livestream/**/* @GoogleCloudPlatform/cloud-media-team @GoogleCloudPlatform/python-samples-reviewers
9798
/data-science-onramp/ @leahecole @bradmiro @GoogleCloudPlatform/python-samples-reviewers
9899
/dlp/**/* @GoogleCloudPlatform/googleapis-dlp @GoogleCloudPlatform/python-samples-reviewers
99100
/functions/spanner/* @GoogleCloudPlatform/api-spanner-python @GoogleCloudPlatform/functions-framework-google @GoogleCloudPlatform/python-samples-reviewers

.github/auto-label.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ path:
2525
bigquery: "bigquery"
2626
bigquery_storage: "bigquerystorage"
2727
billing: "cloudbilling"
28+
cloud-media-livestream: "cloudmedia"
2829
cloud_tasks: "tasks"
2930
cloud-sql: "cloudsql"
3031
cloudbuild: "cloudbuild"

.github/blunderbuss.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,10 @@ assign_issues_by:
147147
- "api: retail"
148148
to:
149149
- GoogleCloudPlatform/cloud-retail-team
150+
- labels:
151+
- "api: cloudmedia"
152+
to:
153+
- GoogleCloudPlatform/cloud-media-team
150154

151155
# Self-service individuals
152156
- labels:
@@ -291,6 +295,10 @@ assign_prs_by:
291295
- "api: retail"
292296
to:
293297
- GoogleCloudPlatform/cloud-retail-team
298+
- labels:
299+
- "api: cloudmedia"
300+
to:
301+
- GoogleCloudPlatform/cloud-media-team
294302

295303
# Self-service individuals
296304
- labels:
Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
# User-managed encryption key publisher
2+
3+
Code intended to be run by users of the Live Stream API. This code utilizes DRM
4+
provider CPIX APIs to fetch encryption keys and store them in Google Secret
5+
Manager for use with the Live Stream API's encryption feature.
6+
7+
To create a zip archive of all the files needed to run key publisher, run the
8+
script `./package.sh`.
9+
10+
## 1. Setup
11+
12+
### Install and configure gcloud
13+
14+
Follow [standard install instructions](http://cloud/sdk/docs/install). Use
15+
`gcloud config` to set your project and preferred region/zone.
16+
17+
## 2. Configure Cloud Functions and API Gateway
18+
19+
### Enable APIs
20+
21+
Enable the following Google APIs to host your key publisher and write key
22+
information to Secret Manager.
23+
24+
```shell
25+
gcloud services enable apigateway.googleapis.com
26+
gcloud services enable cloudbuild.googleapis.com
27+
gcloud services enable cloudfunctions.googleapis.com
28+
gcloud services enable secretmanager.googleapis.com
29+
gcloud services enable servicecontrol.googleapis.com
30+
gcloud services enable servicemanagement.googleapis.com
31+
```
32+
33+
### Create service accounts
34+
35+
Two service accounts are needed for your key publisher. The first allows your
36+
API Gateway to invoke your cloud function (with the IAM role of
37+
`roles/cloudfunctions.invoker`), and the second allows Cloud Functions to read
38+
and write to Secret Manager (with the IAM role of `roles/secretmanager.admin`).
39+
40+
Create the service account for invoking Cloud Functions:
41+
42+
```shell
43+
gcloud iam service-accounts create livestream-cf-invoker \
44+
--display-name="Cloud Function invoker for the API Gateway" \
45+
--description="Service Account to be used by the API Gateway to invoke Cloud Functions"
46+
```
47+
48+
Then configure the IAM policy binding using your `project-id`:
49+
50+
```shell
51+
gcloud projects add-iam-policy-binding [MY-PROJEC-ID] \
52+
--member serviceAccount:"livestream-cf-invoker@[MY-PROJECT-ID].iam.gserviceaccount.com" \
53+
--role "roles/cloudfunctions.invoker" \
54+
--no-user-output-enabled \
55+
--quiet
56+
```
57+
58+
Next, create another service account for interacting with Secret Manager:
59+
60+
```shell
61+
gcloud iam service-accounts create livestream-cloud-functions \
62+
--display-name="Service account for Cloud Functions" \
63+
--description="Service Account used for the Live Stream API key publisher functions"
64+
```
65+
66+
Then configure the IAM policy binding:
67+
68+
```shell
69+
gcloud projects add-iam-policy-binding [MY-PROJECT-ID] \
70+
--member serviceAccount:"livestream-cloud-functions@[MY-PROJECT-ID].iam.gserviceaccount.com" \
71+
--role "roles/secretmanager.admin" \
72+
--no-user-output-enabled \
73+
--quiet
74+
```
75+
76+
### Deploy the cloud function
77+
78+
Create a file `.env.yaml`. Take a look at the example template file:
79+
[`env.template.yml`](./templates/env.template.yml). Copy the template file and
80+
replace any needed values. This file will store environment variables to be used
81+
by your function. This file should have the following contents (also included in
82+
the example file):
83+
84+
```yaml
85+
# Your project ID.
86+
PROJECT: [MY-PROJECT-ID]
87+
# The endpoint of the CPIX API the key publisher will be calling.
88+
CPIX_ENDPOINT: https://cpix-api.url
89+
# The ID of the secret containing the provider's public certificate.
90+
CPIX_PUBLIC_CERT_PROVIDER_SECRET: cpix-public-cert-provider
91+
# The ID of the secret containing your private key.
92+
CPIX_PRIVATE_KEY_USER_SECRET: cpix-private-key-user
93+
# The ID of the secret containing your public certificate.
94+
CPIX_PUBLIC_CERT_USER_SECRET: cpix-public-cert-user
95+
# (Optional) The ID of the secret containing your enduser private key.
96+
CPIX_PRIVATE_KEY_ENDUSER_SECRET: cpix-private-key-enduser
97+
# (Optional) The ID of the secret containing your enduser public certificate.
98+
CPIX_PUBLIC_CERT_ENDUSER_SECRET: cpix-public-cert-enduser
99+
```
100+
101+
Then deploy your function:
102+
103+
```shell
104+
gcloud functions deploy livestream-key-publisher \
105+
--entry-point=keys \
106+
--runtime=python310 \
107+
--trigger-http \
108+
--memory=256MB \
109+
--security-level=secure-always \
110+
--timeout=60s \
111+
--env-vars-file=.env.yaml \
112+
--service-account="livestream-cloud-functions@[MY-PROJECT-ID].iam.gserviceaccount.com"
113+
```
114+
115+
After your function is deployed, the response you get will contain the
116+
`httpsTrigger.url` field. It will show up in the following format:
117+
118+
```shell
119+
httpsTrigger:
120+
url: https://<REGION>-<MY-PROJECT_ID>.cloudfunctions.net/livestream-key-publisher
121+
```
122+
123+
Save this value for later when you configure your API Gateway.
124+
125+
### Create the API and API Gateway
126+
127+
Create the API:
128+
129+
```shell
130+
gcloud api-gateway apis create livestream-key-publisher-api --display-name="Live Stream Key Publisher API"
131+
```
132+
133+
Create a file `api-config.yml`. Take a look at the example template file:
134+
[`api-config.template.yml`](./templates/api-config.template.yml). Copy the
135+
template file and replace any needed values. In particular `CLOUD_FUNCTION_URL`
136+
must be replaced with the URL you received when you deployed your function.
137+
138+
Once the file has been created, create your API config:
139+
140+
```shell
141+
gcloud api-gateway api-configs create livestream-key-publisher-api-config-1 \
142+
--display-name="Live Stream Key Publisher API Config v1" \
143+
--api=livestream-key-publisher-api \
144+
--openapi-spec="api-config.yml" \
145+
--backend-auth-service-account="livestream-cf-invoker@[MY-PROJECT-ID].iam.gserviceaccount.com"
146+
```
147+
148+
Next, create the API Gateway. The following example deploys to GCP Region
149+
`us-central1`. You can choose a different location from supported GCP Regions
150+
[listed here](https://cloud.google.com/api-gateway/docs/deploying-api#deploy_an_api_config_to_a_gateway):
151+
152+
```shell
153+
gcloud api-gateway gateways create livestream-key-publisher-api-gateway \
154+
--display-name="Live Stream Key Publisher API Gateway" \
155+
--api=livestream-key-publisher-api \
156+
--api-config=livestream-key-publisher-api-config-1 \
157+
--location=us-central1
158+
```
159+
160+
Once created, your API must be enabled. Run a command to describe your API:
161+
162+
```shell
163+
gcloud api-gateway apis describe livestream-key-publisher-api
164+
```
165+
166+
You will get the `managedService` field in the response. This is the service you
167+
need to enable. For example,
168+
169+
```shell
170+
gcloud services enable livestream-key-publisher-api-144h6p0e7bzc1.apigateway.[MY-PROJECT-ID].cloud.goog
171+
```
172+
173+
Now you need to locate the endpoint URL of your API. Run a command to describe
174+
your gateway:
175+
176+
```shell
177+
gcloud api-gateway gateways describe livestream-key-publisher-api-gateway --location=us-central1
178+
```
179+
180+
You will get the `defaultHostname` field in the response. For example,
181+
182+
```
183+
defaultHostname: livestream-key-publisher-api-gateway-48mwfke9.uc.gateway.dev
184+
```
185+
186+
You will send API requests to the URL received from your response.
187+
188+
Create an API key to use for making queries to your API, and restrict the API
189+
key such that only your newly created `livestream-key-publisher-api` can be
190+
called with the API key. Specify the API restriction with the `managedService`
191+
field from earlier:
192+
193+
```shell
194+
gcloud alpha services api-keys create --api-target=service=[managedService]
195+
```
196+
197+
For example
198+
199+
```shell
200+
gcloud alpha services api-keys create --api-target=service=livestream-key-publisher-api-144h6p0e7bzc1.apigateway.[MY-PROJECT-ID].cloud.goog
201+
```
202+
203+
> **Warning**: You will get the `keyString` field in the response. This is your
204+
> API key. Save this key, as you will not be able to retrieve it again.
205+
206+
### Test your API
207+
208+
Using the above information, use `curl` to make a `GET` query to your API:
209+
210+
```shell
211+
curl --location --request POST \
212+
'https://livestream-key-publisher-api-gateway-48mwfke9.uc.gateway.dev/keys?api_key=YOUR_API_KEY' \
213+
--header 'Content-Type: application/json' \
214+
--data '{"mediaId": "my-asset", "provider": "FakeProvider", "keyIds": ["abcd1234", "efgi5678"]}'
215+
```
216+
217+
Arguments in the request body are:
218+
219+
* `mediaId`: arbitrary identifier for the media being encrypted.
220+
* `provider`: the DRM provider to use. Omit this argument to see a list of
221+
supported providers.
222+
* `keyIds`: a list of key IDs to prepare for the given media ID.
223+
224+
This example query uses `FakeProvider`. This is a provider used as a reference
225+
example, which does not make any actual CPIX queries, but generates random hex
226+
strings in place of a real key. These keys will not work for a real encryption
227+
setup. `keyIds` should be unique identifiers for the keys provided by your
228+
third-party DRM provider. Please replace `provider` and `keyIds` accordingly.
229+
For `mediaId`, you can change it to any identifier to label the encrypted media.
230+
231+
If the query is successful, you will see a response like:
232+
233+
```
234+
projects/PROJECT_NUMBER/secrets/MEDIA_ID/versions/1
235+
```
236+
237+
This is the name of the secret version that was created to hold your encryption
238+
keys.
239+
240+
To verify this, you can use `gcloud` to access the content of that secret:
241+
242+
```shell
243+
gcloud secrets versions access projects/PROJECT_NUMBER/secrets/MEDIA_ID/versions/1
244+
```
245+
246+
## 3. Update code
247+
248+
Use `gcloud functions deploy` to update your Cloud Function code. Ensure
249+
`.env.yaml` still exists (example file:
250+
[env.template.yml](./templates/env.template.yml)).
251+
252+
```shell
253+
gcloud functions deploy livestream-key-publisher \
254+
--entry-point=keys \
255+
--runtime=python310 \
256+
--trigger-http \
257+
--memory=256MB \
258+
--security-level=secure-always \
259+
--timeout=60s \
260+
--env-vars-file=.env.yaml \
261+
--service-account="livestream-cloud-functions@[MY-PROJECT-ID].iam.gserviceaccount.com"
262+
```
263+
264+
If request paths or parameters have changed, you may also need to update
265+
`api-config.yml` to match. To do this, update `api-config.yml` as needed, then
266+
create a new API config:
267+
268+
```shell
269+
gcloud api-gateway api-configs create livestream-key-publisher-api-config-2 \
270+
--display-name="Live Stream Key Publisher API Config v2" \
271+
--api=livestream-key-publisher-api \
272+
--openapi-spec="api-config.yml" \
273+
--backend-auth-service-account="livestream-cf-invoker@[MY-PROJECT-ID].iam.gserviceaccount.com"
274+
```
275+
276+
Once the config has been created, update the API Gateway to use your new config:
277+
278+
```shell
279+
gcloud api-gateway gateways update livestream-key-publisher-api-gateway \
280+
--api=livestream-key-publisher-api \
281+
--api-config=livestream-key-publisher-api-config-2 \
282+
--location=us-central1
283+
```
284+
285+
## 4. Supported Python Versions
286+
287+
The code samples for key publisher are compatible with all current
288+
[active](https://devguide.python.org/developer-workflow/development-cycle/index.html#in-development-main-branch)
289+
and
290+
[maintenance](https://devguide.python.org/developer-workflow/development-cycle/index.html#maintenance-branches)
291+
versions of Python.
292+
293+
## 5. Testing
294+
295+
You can run unit tests for this code in a local environment. These tests do not
296+
utilize external services (e.g. Google Cloud Functions, Google Secret Manager).
297+
298+
Install python virtual environment:
299+
300+
```shell
301+
sudo apt-get install python3-venv
302+
```
303+
304+
Create a local virtual environment for running tests:
305+
306+
```shell
307+
python3 -m venv venv-key-publisher-test
308+
```
309+
310+
And activate the virtual environment:
311+
312+
```shell
313+
. venv-key-publisher-test/bin/activate
314+
```
315+
316+
Install dependencies needed for testing:
317+
318+
```shell
319+
pip install -r requirements.txt
320+
```
321+
322+
Execute the tests:
323+
324+
```shell
325+
pytest -k _test.py -v
326+
```

cloud-media-livestream/keypublisher/clients/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)