Skip to content

Commit abba187

Browse files
Chronicle Teamcopybara-github
authored andcommitted
Adds sample Python script for GetSubject BK API
PiperOrigin-RevId: 401606180
1 parent 58478ef commit abba187

3 files changed

Lines changed: 211 additions & 0 deletions

File tree

access_control/__init__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Copyright 2021 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#

access_control/get_subject.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#!/usr/bin/env python3
2+
3+
# Copyright 2021 Google LLC
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
"""Executable and reusable sample for retrieving information about a subject.
18+
19+
A subject can be an analyst or an Identity Provider (IdP) group.
20+
"""
21+
22+
import argparse
23+
import json
24+
import sys
25+
from typing import Any, Mapping
26+
from typing import Optional
27+
from typing import Sequence
28+
29+
from google.auth.transport import requests
30+
31+
from common import chronicle_auth
32+
from common import regions
33+
34+
CHRONICLE_API_BASE_URL = "https://backstory.googleapis.com"
35+
36+
37+
def initialize_command_line_args(
38+
args: Optional[Sequence[str]] = None) -> Optional[argparse.Namespace]:
39+
"""Initializes and checks all the command-line arguments."""
40+
parser = argparse.ArgumentParser()
41+
chronicle_auth.add_argument_credentials_file(parser)
42+
regions.add_argument_region(parser)
43+
parser.add_argument(
44+
"-n", "--name", type=str, required=True, help="subject ID")
45+
46+
# No sanity checks for the command-line arguments here. The subject name
47+
# argument converts the provide input into a string and accepts a wide range
48+
# of values. If the subject name isn't passed in, the error will be thrown
49+
# from the argparse library.
50+
51+
return parser.parse_args(args)
52+
53+
54+
def get_subject(http_session: requests.AuthorizedSession,
55+
name: str) -> Mapping[str, Sequence[Any]]:
56+
"""Retrieves information about a subject.
57+
58+
Args:
59+
http_session: Authorized session for HTTP requests.
60+
name: The ID of the subject to retrieve information about.
61+
62+
Returns:
63+
Information about the requested subject in the form:
64+
{
65+
"subject": {
66+
"name": "test@test.com",
67+
"type": "SUBJECT_TYPE_ANALYST",
68+
"roles": [
69+
{
70+
"name": "Test",
71+
"title": "Test role",
72+
"description": "The Test role",
73+
"createTime": "yyyy-mm-ddThh:mm:ss.ssssssZ",
74+
"isDefault": false,
75+
"permissions": [
76+
{
77+
"name": "Test",
78+
"title": "Test permission",
79+
"description": "The Test permission",
80+
"createTime": "yyyy-mm-ddThh:mm:ss.ssssssZ",
81+
},
82+
...
83+
]
84+
},
85+
...
86+
]
87+
}
88+
}
89+
90+
Raises:
91+
requests.exceptions.HTTPError: HTTP request resulted in an error
92+
(response.status_code >= 400).
93+
"""
94+
url = f"{CHRONICLE_API_BASE_URL}/v1/subjects/{name}"
95+
response = http_session.request("GET", url)
96+
97+
if response.status_code >= 400:
98+
print(response.text)
99+
response.raise_for_status()
100+
return response.json()
101+
102+
103+
if __name__ == "__main__":
104+
cli = initialize_command_line_args()
105+
if not cli:
106+
sys.exit(1) # A sanity check failed.
107+
108+
CHRONICLE_API_BASE_URL = regions.url(CHRONICLE_API_BASE_URL, cli.region)
109+
session = chronicle_auth.initialize_http_session(cli.credentials_file)
110+
print(json.dumps(get_subject(session, cli.name), indent=2))

access_control/get_subject_test.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Copyright 2021 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
"""Unit tests for the "get_subject" module."""
16+
17+
import unittest
18+
import argparse
19+
20+
from unittest import mock
21+
22+
from google.auth.transport import requests
23+
24+
from . import get_subject
25+
26+
27+
class GetSubjectTest(unittest.TestCase):
28+
29+
def test_initialize_command_line_args(self):
30+
actual = get_subject.initialize_command_line_args([
31+
"--name=test@test.com",
32+
])
33+
self.assertEqual(
34+
actual,
35+
argparse.Namespace(
36+
credentials_file=None, name="test@test.com", region="us"))
37+
38+
@mock.patch.object(requests, "AuthorizedSession", autospec=True)
39+
@mock.patch.object(requests.requests, "Response", autospec=True)
40+
def test_get_subject_error(self, mock_response, mock_session):
41+
mock_session.request.return_value = mock_response
42+
type(mock_response).status_code = mock.PropertyMock(return_value=400)
43+
mock_response.raise_for_status.side_effect = (
44+
requests.requests.exceptions.HTTPError())
45+
46+
with self.assertRaises(requests.requests.exceptions.HTTPError):
47+
get_subject.get_subject(mock_session, "")
48+
49+
@mock.patch.object(requests, "AuthorizedSession", autospec=True)
50+
@mock.patch.object(requests.requests, "Response", autospec=True)
51+
def test_get_subject(self, mock_response, mock_session):
52+
mock_session.request.return_value = mock_response
53+
type(mock_response).status_code = mock.PropertyMock(return_value=200)
54+
subject_id = "test@test.com"
55+
expected = {
56+
"subject": {
57+
"name":
58+
subject_id,
59+
"type":
60+
"SUBJECT_TYPE_ANALYST",
61+
"roles": [{
62+
"name":
63+
"Test",
64+
"title":
65+
"Test role",
66+
"description":
67+
"The Test role",
68+
"createTime":
69+
"2020-11-05T00:00:00Z",
70+
"isDefault":
71+
False,
72+
"permissions": [{
73+
"name": "Test",
74+
"title": "Test permission",
75+
"description": "The Test permission",
76+
"createTime": "2020-11-05T00:00:00Z",
77+
},]
78+
},]
79+
},
80+
}
81+
mock_response.json.return_value = expected
82+
actual = get_subject.get_subject(mock_session, subject_id)
83+
self.assertEqual(actual, expected)
84+
85+
86+
if __name__ == "__main__":
87+
unittest.main()

0 commit comments

Comments
 (0)