From: Rob van der Linde Date: Thu, 22 Feb 2024 02:47:30 +0000 (+1300) Subject: netcmd: models: gmsa move GroupManagedServiceAccount model to gmsa.py X-Git-Tag: tdb-1.4.11~1588 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=1cd7cf6680738e619f7515c5477bf6742d1eb573;p=thirdparty%2Fsamba.git netcmd: models: gmsa move GroupManagedServiceAccount model to gmsa.py It needs to inherit from the Computer model, the Computer model also inherits from User. First, moving it to its own file from user.py to gmsa.py Signed-off-by: Rob van der Linde Reviewed-by: Andrew Bartlett Reviewed-by: Douglas Bagnall --- diff --git a/python/samba/netcmd/domain/models/__init__.py b/python/samba/netcmd/domain/models/__init__.py index 575a108a312..8051a5c3648 100644 --- a/python/samba/netcmd/domain/models/__init__.py +++ b/python/samba/netcmd/domain/models/__init__.py @@ -25,6 +25,7 @@ from .auth_policy import (AuthenticationPolicy, StrongNTLMPolicy, from .auth_silo import AuthenticationSilo from .claim_type import ClaimType from .computer import Computer +from .gmsa import GroupManagedServiceAccount from .group import Group from .model import MODELS from .schema import AttributeSchema, ClassSchema @@ -32,5 +33,5 @@ from .site import Site from .subnet import Subnet from .types import (AccountType, GroupType, SupportedEncryptionTypes, SystemFlags, UserAccountControl) -from .user import User, GroupManagedServiceAccount +from .user import User from .value_type import ValueType diff --git a/python/samba/netcmd/domain/models/gmsa.py b/python/samba/netcmd/domain/models/gmsa.py new file mode 100644 index 00000000000..dde1c0c2427 --- /dev/null +++ b/python/samba/netcmd/domain/models/gmsa.py @@ -0,0 +1,95 @@ +# Unix SMB/CIFS implementation. +# +# GroupManagedServiceAccount model. +# +# Copyright (C) Catalyst.Net Ltd. 2024 +# +# Written by Rob van der Linde +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +from ldb import Dn + +from samba.dcerpc import security +from samba.dsdb import DS_GUID_MANAGED_SERVICE_ACCOUNTS_CONTAINER + +from .exceptions import FieldError +from .fields import BinaryField, EnumField, IntegerField, SDDLField, StringField +from .types import SupportedEncryptionTypes +from .user import User + + +class GroupManagedServiceAccount(User): + """A GroupManagedServiceAccount is a type of User with additional fields.""" + managed_password_interval = IntegerField("msDS-ManagedPasswordInterval") + dns_host_name = StringField("dNSHostName") + group_msa_membership = SDDLField("msDS-GroupMSAMembership", + default="O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;LA)") + managed_password_id = BinaryField("msDS-ManagedPasswordId", + readonly=True, hidden=True) + managed_password_previous_id = BinaryField("msDS-ManagedPasswordPreviousId", + readonly=True, hidden=True) + supported_encryption_types = EnumField("msDS-SupportedEncryptionTypes", + SupportedEncryptionTypes) + + @staticmethod + def get_base_dn(ldb): + """Return base Dn for Managed Service Accounts. + + :param ldb: Ldb connection + :return: Dn to use for searching + """ + return ldb.get_wellknown_dn(ldb.get_default_basedn(), + DS_GUID_MANAGED_SERVICE_ACCOUNTS_CONTAINER) + + @staticmethod + def get_object_class(): + return "msDS-GroupManagedServiceAccount" + + @property + def trustees(self): + """Returns list of trustees from the msDS-GroupMSAMembership field. + + :return: list of SID strings + """ + allowed = [] + + # Make sure to exclude DENY aces. + for ace in self.group_msa_membership.dacl.aces: + if ((ace.access_mask & security.SEC_ADS_READ_PROP) + and ace.type == security.SEC_ACE_TYPE_ACCESS_ALLOWED): + allowed.append(str(ace.trustee)) + else: + raise FieldError( + "Cannot be represented as a simple list (try viewing as SDDL)", + field=GroupManagedServiceAccount.group_msa_membership) + + return allowed + + @classmethod + def find(cls, ldb, name): + """Helper function to find a service account first by Dn then username. + + If the Dn can't be parsed use sAMAccountName, automatically add the $. + """ + try: + query = {"dn": Dn(ldb, name)} + except ValueError: + if name.endswith("$"): + query = {"username": name} + else: + query = {"username": name + "$"} + + return cls.get(ldb, **query) diff --git a/python/samba/netcmd/domain/models/user.py b/python/samba/netcmd/domain/models/user.py index ebd45b7491a..f934f7b3335 100644 --- a/python/samba/netcmd/domain/models/user.py +++ b/python/samba/netcmd/domain/models/user.py @@ -22,15 +22,12 @@ from ldb import Dn -from samba.dcerpc import security -from samba.dsdb import (DS_GUID_MANAGED_SERVICE_ACCOUNTS_CONTAINER, - DS_GUID_USERS_CONTAINER) +from samba.dsdb import DS_GUID_USERS_CONTAINER -from .exceptions import FieldError -from .fields import (BinaryField, DnField, EnumField, IntegerField, SDDLField, - SIDField, StringField, NtTimeField) +from .fields import (DnField, EnumField, IntegerField, SIDField, StringField, + NtTimeField) from .model import Model -from .types import AccountType, SupportedEncryptionTypes, UserAccountControl +from .types import AccountType, UserAccountControl class User(Model): @@ -93,67 +90,3 @@ class User(Model): query = {"username": name} return cls.get(ldb, **query) - - -class GroupManagedServiceAccount(User): - """A GroupManagedServiceAccount is a type of User with additional fields.""" - managed_password_interval = IntegerField("msDS-ManagedPasswordInterval") - dns_host_name = StringField("dNSHostName") - group_msa_membership = SDDLField("msDS-GroupMSAMembership", - default="O:BAD:(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;LA)") - managed_password_id = BinaryField("msDS-ManagedPasswordId", - readonly=True, hidden=True) - managed_password_previous_id = BinaryField("msDS-ManagedPasswordPreviousId", - readonly=True, hidden=True) - supported_encryption_types = EnumField("msDS-SupportedEncryptionTypes", - SupportedEncryptionTypes) - - @staticmethod - def get_base_dn(ldb): - """Return base Dn for Managed Service Accounts. - - :param ldb: Ldb connection - :return: Dn to use for searching - """ - return ldb.get_wellknown_dn(ldb.get_default_basedn(), - DS_GUID_MANAGED_SERVICE_ACCOUNTS_CONTAINER) - - @staticmethod - def get_object_class(): - return "msDS-GroupManagedServiceAccount" - - @property - def trustees(self): - """Returns list of trustees from the msDS-GroupMSAMembership field. - - :return: list of SID strings - """ - allowed = [] - - # Make sure to exclude DENY aces. - for ace in self.group_msa_membership.dacl.aces: - if ((ace.access_mask & security.SEC_ADS_READ_PROP) - and ace.type == security.SEC_ACE_TYPE_ACCESS_ALLOWED): - allowed.append(str(ace.trustee)) - else: - raise FieldError( - "Cannot be represented as a simple list (try viewing as SDDL)", - field=GroupManagedServiceAccount.group_msa_membership) - - return allowed - - @classmethod - def find(cls, ldb, name): - """Helper function to find a service account first by Dn then username. - - If the Dn can't be parsed use sAMAccountName, automatically add the $. - """ - try: - query = {"dn": Dn(ldb, name)} - except ValueError: - if name.endswith("$"): - query = {"username": name} - else: - query = {"username": name + "$"} - - return cls.get(ldb, **query)