Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions src/openhound_jamf/models/account.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from dataclasses import dataclass
from typing import Optional

from openhound.core.asset import EdgeDef, NodeDef
from openhound.core.models.entries_dataclass import (
Expand All @@ -9,12 +8,13 @@
EdgeProperties,
PropertyMatch,
)
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, field_validator

from openhound_jamf.graph import JAMFAsset, JAMFNode, JAMFNodeProperties
from openhound_jamf.kinds import edges as ek
from openhound_jamf.kinds import nodes as nk
from openhound_jamf.main import app
from openhound_jamf.models.utils import NO_SITE_ID, normalize_site


@dataclass
Expand Down Expand Up @@ -230,10 +230,15 @@ class Account(JAMFAsset):
enabled: str
access_level: str
privilege_set: str
site: Optional[Site] | None = None
site: Site = Field(default_factory=lambda: Site(id=NO_SITE_ID))
privileges: Privilege | None = None
directory_user: bool

@field_validator("site", mode="before")
@classmethod
def _default_site(cls, v):
return normalize_site(v)

@property
def as_node(self):

Expand All @@ -249,7 +254,7 @@ def as_node(self):
full_name=self.full_name,
email=self.email,
enabled=self.enabled == "Enabled",
site_id=str(self.site.id) if self.site else "-1",
site_id=str(self.site.id),
access_level=self.access_level,
privilege_objects=self.privileges.jss_objects if self.privileges else [],
privilege_actions=self.privileges.jss_actions if self.privileges else [],
Expand Down Expand Up @@ -278,7 +283,7 @@ def _is_admin(self) -> bool:
)

def _target_computers(self):
if self.access_level == "Site Access" and self.site:
if self.access_level == "Site Access" and self.site.id != NO_SITE_ID:
return self._lookup.computers_by_site(str(self.site.id))
return self._lookup.all_computers()

Expand Down Expand Up @@ -307,7 +312,7 @@ def _admin_to_site_edges(self):
if (
self.access_level == "Site Access"
and self.privilege_set == "Administrator"
and self.site
and self.site.id != NO_SITE_ID
):
site_node_id = JAMFNode.guid(str(self.site.id), nk.SITE, self.tenant_id)
yield Edge(
Expand Down Expand Up @@ -418,7 +423,7 @@ def _scripts_non_traversable_edge(self):
self._has_privilege("Create Scripts")
or self._has_privilege("Update Scripts")
):
if self.access_level == "Site Access" and self.site:
if self.access_level == "Site Access" and self.site.id != NO_SITE_ID:
site_node_id = JAMFNode.guid(str(self.site.id), nk.SITE, self.tenant_id)
yield Edge(
kind=ek.SCRIPTS_NON_TRAVERSABLE,
Expand Down
5 changes: 3 additions & 2 deletions src/openhound_jamf/models/computer.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from openhound_jamf.kinds import edges as ek
from openhound_jamf.kinds import nodes as nk
from openhound_jamf.main import app
from openhound_jamf.models.utils import NO_SITE_ID_STR


@dataclass
Expand Down Expand Up @@ -235,7 +236,7 @@ def _node_id(self) -> str:

@property
def _contains_tenant_edge(self):
if self.site.id == "-1":
if self.site.id == NO_SITE_ID_STR:
yield Edge(
kind=ek.CONTAINS,
start=EdgePath(match_by="id", value=self.tenant_node_id),
Expand All @@ -245,7 +246,7 @@ def _contains_tenant_edge(self):

@property
def _contains_site_edge(self):
if self.site.id != "-1":
if self.site.id != NO_SITE_ID_STR:
site_node_id = JAMFNode.guid(self.site.id, nk.SITE, self.tenant_id)
yield Edge(
kind=ek.CONTAINS,
Expand Down
18 changes: 12 additions & 6 deletions src/openhound_jamf/models/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

from openhound.core.asset import EdgeDef, NodeDef
from openhound.core.models.entries_dataclass import Edge, EdgePath, EdgeProperties
from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, field_validator

from openhound_jamf.graph import JAMFAsset, JAMFNode, JAMFNodeProperties
from openhound_jamf.kinds import edges as ek
from openhound_jamf.kinds import nodes as nk
from openhound_jamf.main import app
from openhound_jamf.models.utils import NO_SITE_ID, normalize_site


class User(BaseModel):
Expand Down Expand Up @@ -213,10 +214,15 @@ class Group(JAMFAsset):
name: str
access_level: str
privilege_set: str
site: Site
site: Site = Field(default_factory=lambda: Site(id=NO_SITE_ID))
privileges: Privilege | None = None
members: list[User] = Field(default_factory=list)

@field_validator("site", mode="before")
@classmethod
def _default_site(cls, v):
return normalize_site(v)

@property
def as_node(self):
tier_eval = (
Expand All @@ -228,7 +234,7 @@ def as_node(self):
displayname=self.name,
tenant=self.tenant_id,
tier=0 if tier_eval else 1,
site_id=str(self.site.id) if self.site else "-1",
site_id=str(self.site.id),
access_level=self.access_level,
privilege_objects=self.privileges.jss_objects if self.privileges else [],
privilege_actions=self.privileges.jss_actions if self.privileges else [],
Expand Down Expand Up @@ -256,7 +262,7 @@ def _is_admin(self) -> bool:
)

def _target_computers(self):
if self.access_level == "Site Access":
if self.access_level == "Site Access" and self.site.id != NO_SITE_ID:
return self._lookup.computers_by_site(str(self.site.id))
return self._lookup.all_computers()

Expand Down Expand Up @@ -292,7 +298,7 @@ def _admin_to_edge(self):

@property
def _admin_to_site_edges(self):
if self.access_level == "Site Access" and self.privilege_set == "Administrator":
if self.access_level == "Site Access" and self.privilege_set == "Administrator" and self.site.id != NO_SITE_ID:
site_node_id = JAMFNode.guid(str(self.site.id), nk.SITE, self.tenant_id)
yield Edge(
kind=ek.ADMIN_TO_SITE,
Expand Down Expand Up @@ -393,7 +399,7 @@ def _scripts_non_traversable_edge(self):
self._has_privilege("Create Scripts")
or self._has_privilege("Update Scripts")
):
if self.access_level == "Site Access":
if self.access_level == "Site Access" and self.site.id != NO_SITE_ID:
site_node_id = JAMFNode.guid(str(self.site.id), nk.SITE, self.tenant_id)
yield Edge(
kind=ek.SCRIPTS_NON_TRAVERSABLE,
Expand Down
2 changes: 1 addition & 1 deletion src/openhound_jamf/models/site.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class SiteProperties(JAMFNodeProperties):
start=nk.TENANT,
end=nk.SITE,
kind=ek.CONTAINS,
description="Something something",
description="The tenant contains this site.",
),
],
)
Expand Down
20 changes: 20 additions & 0 deletions src/openhound_jamf/models/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""Shared utilities for JAMF model validators."""

NO_SITE_ID = -1
# The Jamf Pro API (v1) returns site.id as a string, unlike the Classic API which uses an int.
NO_SITE_ID_STR = str(NO_SITE_ID)
_SENTINEL_SITE = {"id": NO_SITE_ID}


def normalize_site(v):
"""Coerce a raw site value into a valid site dict.

Returns the sentinel ``{"id": -1}`` when the value is ``None`` or already
the sentinel, and passes all other values through unchanged so Pydantic can
validate them normally.
"""
if v is None:
return _SENTINEL_SITE
if isinstance(v, dict) and v.get("id") == NO_SITE_ID:
return _SENTINEL_SITE
return v
Loading
Loading