Skip to content

Commit 5433584

Browse files
authored
feat: add support for TeamUsers and UserGroups APIs (#114)
Enable managing users with the new, experimental, TeamUsers and UserGroups API
1 parent 9b0022c commit 5433584

File tree

7 files changed

+329
-0
lines changed

7 files changed

+329
-0
lines changed

examples/example_team_users.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Example script showing how to use the LaceworkClient class.
4+
"""
5+
6+
import logging
7+
8+
from dotenv import load_dotenv
9+
from laceworksdk import LaceworkClient
10+
11+
logging.basicConfig(level=logging.DEBUG)
12+
13+
load_dotenv()
14+
15+
16+
def standard_user_example(client: LaceworkClient):
17+
"""
18+
Example of create/update/delete and group management for standard user
19+
"""
20+
21+
# Create user
22+
data = client.team_users.create("test user", "noreply@lacework.com", "test-company")
23+
guid = data["data"]["userGuid"]
24+
logging.debug(f'user guid created:\n{guid}')
25+
26+
# Get one user
27+
client.team_users.get(guid)
28+
29+
# Update user
30+
client.team_users.update(guid, user_enabled=0)
31+
32+
# Add user to group
33+
client.user_groups.add_users("LACEWORK_USER_GROUP_POWER_USER", [guid])
34+
35+
# Remove user from group
36+
client.user_groups.remove_users("LACEWORK_USER_GROUP_POWER_USER", [guid])
37+
38+
# Delete user
39+
client.team_users.delete(guid)
40+
41+
def service_user_example(client: LaceworkClient):
42+
"""
43+
Example of create/update/delete and group management for service user
44+
"""
45+
46+
# Create user
47+
data = client.team_users.create("test service user", description="test service user", type="ServiceUser")
48+
guid = data["data"]["userGuid"]
49+
logging.debug(f'user guid created:\n{guid}')
50+
51+
# Get one user
52+
client.team_users.get(guid)
53+
54+
# Update user
55+
client.team_users.update(guid, user_enabled=0)
56+
57+
# Add user to group
58+
client.user_groups.add_users("LACEWORK_USER_GROUP_POWER_USER", [guid])
59+
60+
# Remove user from group
61+
client.user_groups.remove_users("LACEWORK_USER_GROUP_POWER_USER", [guid])
62+
63+
# Delete user
64+
client.team_users.delete(guid)
65+
66+
67+
68+
if __name__ == "__main__":
69+
# Instantiate a LaceworkClient instance
70+
lacework_client = LaceworkClient()
71+
72+
# TeamUsers API
73+
74+
# Get users
75+
lacework_client.team_users.get()
76+
77+
standard_user_example(lacework_client)
78+
service_user_example(lacework_client)
79+

laceworksdk/api/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
from .v2.resource_groups import ResourceGroupsAPI
4848
from .v2.schemas import SchemasAPI
4949
from .v2.team_members import TeamMembersAPI
50+
from .v2.team_users import TeamUsersAPI
51+
from .v2.user_groups import UserGroupsAPI
5052
from .v2.user_profile import UserProfileAPI
5153
from .v2.vulnerabilities import VulnerabilitiesAPI
5254
from .v2.vulnerability_exceptions import VulnerabilityExceptionsAPI
@@ -178,7 +180,9 @@ def __init__(self,
178180
self.schemas = SchemasAPI(self._session)
179181
self.suppressions = SuppressionsAPI(self._session)
180182
self.team_members = TeamMembersAPI(self._session)
183+
self.team_users = TeamUsersAPI(self._session)
181184
self.tokens = TokenAPI(self._session)
185+
self.user_groups = UserGroupsAPI(self._session)
182186
self.user_profile = UserProfileAPI(self._session)
183187
self.vulnerabilities = VulnerabilitiesAPI(self._session)
184188
self.vulnerability_exceptions = VulnerabilityExceptionsAPI(self._session)

laceworksdk/api/v2/team_users.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Lacework TeamUsers API wrapper (Experimental).
4+
"""
5+
6+
from laceworksdk.api.crud_endpoint import CrudEndpoint
7+
import logging
8+
logger = logging.getLogger(__name__)
9+
10+
class TeamUsersAPI(CrudEndpoint):
11+
def __init__(self, session):
12+
super().__init__(session, "TeamUsers")
13+
14+
def get(self, guid=None):
15+
"""
16+
(Experimental API) A method to get TeamUsers objects.
17+
18+
:param guid: A string representing the object GUID.
19+
20+
:return response json
21+
"""
22+
23+
return super().get(id=guid)
24+
25+
def get_by_guid(self, guid):
26+
"""
27+
(Experimental API) A method to get a TeamUsers object by GUID.
28+
29+
:param guid: A string representing the object GUID.
30+
31+
:return response json
32+
"""
33+
34+
return self.get(guid=guid)
35+
36+
def create(self,
37+
name,
38+
email=None,
39+
company=None,
40+
description=None,
41+
type="StandardUser",
42+
**request_params):
43+
"""
44+
(Experimental API) A method to create a new TeamUsers standard user object.
45+
46+
:param name: A string representing the friendly name of the user.
47+
:param email: A string representing the email address of the user (valid only for StandardUser).
48+
:param company: A string representing the company of the user (valid only for StandardUser).
49+
:param description: A description text for describing service accounts (valid only for ServiceUser).
50+
:param type: A string representing the type of the user to create.
51+
(StandardUser or ServiceUser)
52+
:param request_params: Additional request parameters.
53+
(provides support for parameters that may be added in the future)
54+
55+
:return response json
56+
"""
57+
58+
return super().create(
59+
name=name,
60+
email=email,
61+
description=description,
62+
company=company,
63+
type=type,
64+
**request_params
65+
)
66+
67+
def update(self,
68+
guid,
69+
name=None,
70+
user_enabled=None,
71+
description=None,
72+
**request_params):
73+
"""
74+
(Experimental API) A method to update a TeamUsers object.
75+
76+
:param guid: A string representing the object GUID.
77+
:param name: A string representing the friendly name of the object.
78+
:param userEnabled: A boolean/integer representing whether the object is enabled.
79+
(0 or 1)
80+
:param description: A description text for describing service accounts (only valid for service accounts).
81+
82+
:return response json
83+
"""
84+
85+
if user_enabled is not None:
86+
user_enabled = int(bool(user_enabled))
87+
88+
return super().update(
89+
id=guid,
90+
name=name,
91+
user_enabled=user_enabled,
92+
description=description,
93+
**request_params
94+
)
95+
96+
def delete(self, guid):
97+
"""
98+
(Experimental API) A method to delete a TeamUsers object.
99+
100+
:param guid: A string representing the object GUID.
101+
102+
:return response json
103+
"""
104+
105+
return super().delete(id=guid)

laceworksdk/api/v2/user_groups.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Lacework UserGroups API wrapper (Experimental).
4+
"""
5+
6+
from laceworksdk.api.base_endpoint import BaseEndpoint
7+
8+
class UserGroupsAPI(BaseEndpoint):
9+
def __init__(self, session):
10+
super().__init__(session, "UserGroups")
11+
12+
def __modify_members(self, guid, user_guids, action):
13+
json = self.build_dict_from_items(
14+
user_guids=user_guids,
15+
)
16+
17+
response = self._session.post(self.build_url(resource=guid, action=action), json=json, params=None)
18+
19+
return response.json()
20+
21+
def add_users(self, guid, user_guids):
22+
"""
23+
(Experimental API) A method to add users to existing UserGroup object.
24+
25+
:param guid: A string representing the GUID of the UserGroup to modify.
26+
:param user_guids: An array of user guids to add to the UserGroup object.
27+
28+
:return response json
29+
"""
30+
return self.__modify_members(guid, user_guids, "addUsers")
31+
32+
def remove_users(self, guid, user_guids):
33+
"""
34+
(Experimental API) A method to remove users from an existing UserGroup object.
35+
36+
:param guid: A string representing the GUID of the UserGroup object to modify.
37+
:param user_guids: An array of user guids to add to the UserGroup object.
38+
39+
:return response json
40+
"""
41+
return self.__modify_members(guid, user_guids, "removeUsers")

laceworksdk/http_session.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,12 @@ def _request(self, method, uri, **kwargs):
229229
if "/api/v1/" in uri:
230230
logger.warning("Lacework's v1 APIs are scheduled to be deprecated and will not allow usage of after December 2022.")
231231

232+
if "/api/v2/TeamUsers" in uri:
233+
logger.warning("TeamUsers APIs is currently experimental and subject to change")
234+
235+
if "/api/v2/UserGroups" in uri:
236+
logger.warning("UserGroups API is currently experimental and subject to change")
237+
232238
# Check for 'org' - if True, make an organization-level API call
233239
# TODO: Remove this on v1.0 release - this is done for back compat
234240
org = kwargs.pop("org", None)

tests/api/v2/test_team_users.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
Test suite for the community-developed Python SDK for interacting with Lacework APIs.
4+
"""
5+
6+
import pytest
7+
8+
from laceworksdk.api.v2.team_users import TeamUsersAPI
9+
from tests.api.test_crud_endpoint import CrudEndpoint
10+
11+
12+
# Tests
13+
14+
@pytest.fixture(scope="module")
15+
def api_object(api):
16+
return api.team_users
17+
18+
19+
@pytest.fixture(scope="module")
20+
def api_object_create_body(random_text):
21+
return {
22+
"name": "John Doe",
23+
"email": f"{random_text.lower()}@lacework.net",
24+
"company": "Lacework",
25+
}
26+
27+
28+
@pytest.fixture(scope="module")
29+
def api_object_update_body():
30+
return {
31+
"user_enabled": 0
32+
}
33+
34+
35+
class TestTeamUsers(CrudEndpoint):
36+
37+
OBJECT_ID_NAME = "userGuid"
38+
OBJECT_TYPE = TeamUsersAPI
39+
40+
def test_api_search(self, api_object, request):
41+
"Not implemented"
42+
pass
43+
44+
def test_api_get_by_guid(self, api_object):
45+
self._get_object_classifier_test(api_object, "guid", self.OBJECT_ID_NAME)

tests/api/v2/test_user_groups.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
2+
# -*- coding: utf-8 -*-
3+
"""
4+
Test suite for the community-developed Python SDK for interacting with Lacework APIs.
5+
"""
6+
7+
import pytest
8+
9+
from laceworksdk.api.v2.user_groups import UserGroupsAPI
10+
from laceworksdk.exceptions import ApiError
11+
from tests.api.test_base_endpoint import BaseEndpoint
12+
from laceworksdk import LaceworkClient
13+
14+
15+
# Tests
16+
17+
@pytest.fixture(scope="module")
18+
def api_object(api):
19+
return api.user_groups
20+
21+
@pytest.fixture(scope="module")
22+
def test_user(api, random_text):
23+
res = api.team_users.create(f"test{random_text}", f"noreply+{random_text}@lacework.com", "test")
24+
guid = res["data"]["userGuid"]
25+
yield guid
26+
api.team_users.delete(guid)
27+
28+
29+
class TestUserProfile(BaseEndpoint):
30+
31+
OBJECT_TYPE = UserGroupsAPI
32+
33+
def test_add_user(self, api_object, test_user):
34+
response = api_object.add_users("LACEWORK_USER_GROUP_POWER_USER", [test_user])
35+
assert "data" in response.keys()
36+
37+
def test_remove_user(self, api_object, test_user):
38+
response = api_object.remove_users("LACEWORK_USER_GROUP_POWER_USER", [test_user])
39+
assert "data" in response.keys()
40+
41+
def test_add_user_should_fail_with_invalid_data(self, api_object):
42+
with pytest.raises(ApiError) as e:
43+
api_object.add_users("LACEWORK_USER_GROUP_POWER_USER", ["fake"])
44+
assert "400" in str(e.value)
45+
46+
def test_remove_user_should_fail_with_invalid_data(self, api_object):
47+
with pytest.raises(ApiError) as e:
48+
api_object.remove_users("LACEWORK_USER_GROUP_POWER_USER", ["fake"])
49+
assert "500" in str(e.value)

0 commit comments

Comments
 (0)