Skip to content

Commit

Permalink
fix: kandji list unencrypted devices
Browse files Browse the repository at this point in the history
  • Loading branch information
danielgrittner committed Nov 20, 2024
1 parent f932b89 commit 275621a
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 206 deletions.
2 changes: 0 additions & 2 deletions admyral/actions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,6 @@
list_github_issue_comments,
list_kandji_devices,
get_kandji_device_details,
list_kandji_unencrypted_devices,
get_kandji_device_apps,
list_zendesk_users,
get_kandji_application_firewall,
Expand Down Expand Up @@ -178,7 +177,6 @@
"list_github_issue_comments",
"list_kandji_devices",
"get_kandji_device_details",
"list_kandji_unencrypted_devices",
"get_kandji_device_apps",
"aws_list_iam_users",
"list_zendesk_users",
Expand Down
2 changes: 0 additions & 2 deletions admyral/actions/integrations/compliance/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
from admyral.actions.integrations.compliance.kandji import (
list_kandji_devices,
get_kandji_device_details,
list_kandji_unencrypted_devices,
get_kandji_device_apps,
get_kandji_application_firewall,
get_kandji_desktop_and_screensaver,
Expand All @@ -51,7 +50,6 @@
"list_github_issue_comments",
"list_kandji_devices",
"get_kandji_device_details",
"list_kandji_unencrypted_devices",
"get_kandji_device_apps",
"list_zendesk_users",
"get_kandji_application_firewall",
Expand Down
114 changes: 84 additions & 30 deletions admyral/actions/integrations/compliance/kandji.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
from typing import Annotated
from typing import Annotated, Literal
from httpx import Client
from pydantic import BaseModel
from dateutil.parser import parse
from datetime import timedelta

from admyral.action import action, ArgumentMetadata
from admyral.context import ctx
from admyral.typings import JsonValue
from admyral.secret.secret import register_secret
from admyral.utils.time import utc_now


@register_secret(secret_type="Kandji")
Expand All @@ -25,21 +28,25 @@ def get_kandji_client(secret: KandjiSecret) -> Client:
)


def _kandji_get_api_with_pagination(url: str, data_access_key: str | None = None):
def _kandji_get_api_with_pagination(
url: str,
params: dict[str, JsonValue] | None = None,
data_access_key: str | None = None,
):
secret = ctx.get().secrets.get("KANDJI_SECRET")
secret = KandjiSecret.model_validate(secret)

with get_kandji_client(secret) as client:
offset = 0
max_limit_per_page = 300

params = params or {}
params["limit"] = max_limit_per_page
params["offset"] = 0

out = []

while True:
response = client.get(
url=url,
params={"limit": max_limit_per_page, "offset": offset},
)
response = client.get(url=url, params=params)
response.raise_for_status()

result = response.json()
Expand All @@ -49,7 +56,7 @@ def _kandji_get_api_with_pagination(url: str, data_access_key: str | None = None
new_data = result[data_access_key]
out.extend(new_data)

offset += len(new_data)
params["offset"] += len(new_data)
if len(new_data) < max_limit_per_page:
break

Expand All @@ -72,9 +79,76 @@ def _kandji_get_api(url: str) -> dict[str, JsonValue]:
description="List devices managed by Kandji",
secrets_placeholders=["KANDJI_SECRET"],
)
def list_kandji_devices() -> list[dict[str, JsonValue]]:
def list_kandji_devices(
last_checkin_within_days: Annotated[
int | None,
ArgumentMetadata(
display_name="Last Checkin Within Days",
description="Only keep devices that have checked in within the last defined days.",
),
] = None,
platform: Annotated[
Literal["Mac", "iPad", "iPhone", "AppleTV"] | None,
ArgumentMetadata(
display_name="Platform",
description="Only keep devices that match the defined platform. Possible values: Mac, iPad, iPhone, AppleTV",
),
] = None,
blueprints: Annotated[
list[str] | None,
ArgumentMetadata(
display_name="Blueprints",
description="Only keep devices that match the defined blueprints.",
),
] = None,
filevault_enabled: Annotated[
bool | None,
ArgumentMetadata(
display_name="FileVault Enabled",
description="Only keep devices that have FileVault enabled. Only for Mac devices.",
),
] = None,
) -> list[dict[str, JsonValue]]:
# https://api-docs.kandji.io/#78209960-31a7-4e3b-a2c0-95c7e65bb5f9
return _kandji_get_api_with_pagination(url="/devices")
params = {}
if platform is not None:
if platform not in ["Mac", "iPad", "iPhone", "AppleTV"]:
raise ValueError(f"Invalid platform: {platform}")
params["platform"] = platform

if filevault_enabled is not None:
params["filevault_enabled"] = filevault_enabled

devices = _kandji_get_api_with_pagination(url="/devices", params=params)

# filter based on last checkin and blueprints
if last_checkin_within_days is not None or blueprints is not None:
filtered_devices = []

if last_checkin_within_days is not None:
last_checkin_within_days = utc_now() - timedelta(
days=last_checkin_within_days
)

for device in devices:
if blueprints is not None and device["blueprint_name"] not in blueprints:
continue

if last_checkin_within_days is not None:
device_details = _kandji_get_api(
url=f"/devices/{device["device_id"]}/details"
)
if (
parse(device_details["mdm"]["last_check_in"])
< last_checkin_within_days
):
continue

filtered_devices.append(device)

devices = filtered_devices

return devices


@action(
Expand All @@ -93,26 +167,6 @@ def get_kandji_device_details(
return _kandji_get_api(url=f"/devices/{device_id}/details")


@action(
display_name="List Unencrypted Devices",
display_namespace="Kandji",
description="List devices managed by Kandji which don't have disk encryption enabled on all volumes",
secrets_placeholders=["KANDJI_SECRET"],
)
def list_kandji_unencrypted_devices() -> list[dict[str, JsonValue]]:
# https://api-docs.kandji.io/#78209960-31a7-4e3b-a2c0-95c7e65bb5f9

devices = _kandji_get_api_with_pagination(url="/devices")

unencrypted_devices = []
for device in devices:
device_details = _kandji_get_api(url=f"/devices/{device["device_id"]}/details")
if any(volume["encrypted"] == "No" for volume in device_details["volumes"]):
unencrypted_devices.append(device)

return unencrypted_devices


@action(
display_name="Get Device Apps",
display_namespace="Kandji",
Expand Down
13 changes: 11 additions & 2 deletions docs/pages/integrations/kandji/apis/list_devices.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Get a list of all devices in an organization from Kandji.
**Required permissions:**

- `Device list`
- `Device details`

**SDK Import:**

Expand All @@ -21,8 +22,12 @@ from admyral.actions import list_kandji_devices

## Arguments:

| Argument Name | Description | Required |
| ------------- | ----------- | :------: |
| Argument Name | Description | Required |
| ------------------------- | ---------------------------------------------------------------------------------------------- | :------: |
| Last Check In Within Days | Only keep devices that have checked in within the last defined days. | No |
| Platform | Only keep devices that match the defined platform. Possible values: Mac, iPad, iPhone, AppleTV | No |
| Blueprints | Only keep devices that match the defined blueprints. | No |
| FileVault Enabled | Only keep devices that have FileVault enabled. Only for Mac devices. | No |

## Returns

Expand All @@ -38,6 +43,10 @@ A JSON array of managed devices.

```python
devices = list_kandji_devices(
last_checkin_within_days=90,
blueprints=["Default Blueprint"],
platform="Mac",
filevault_enabled=False,
secrets={"KANDJI_SECRET": "my_kandji_secret"}
)
```
Expand Down
111 changes: 0 additions & 111 deletions docs/pages/integrations/kandji/apis/list_unencrypted_devices.mdx

This file was deleted.

Loading

0 comments on commit 275621a

Please sign in to comment.