attempted fix on group shares not appearing (GID not found) (3)
This commit is contained in:
@@ -150,10 +150,11 @@ Kerberos requires close time alignment.
|
|||||||
### Dynamic Group Shares
|
### Dynamic Group Shares
|
||||||
|
|
||||||
- AD groups: `FileShare_*` and `FS_*`
|
- AD groups: `FileShare_*` and `FS_*`
|
||||||
- Share name: prefix removed (`FileShare_Finance` -> `\\server\Finance`, `FS_Finance` -> `\\server\Finance`)
|
- Share name: group title from AD (`displayName` -> `name`/`cn` fallback). Prefix stripping from `sAMAccountName` is only a fallback when no title exists.
|
||||||
- Backing path: `/data/groups/<objectGUID>`
|
- Backing path: `/data/groups/<objectGUID>`
|
||||||
- Share exposure generated in `/etc/samba/generated/shares.conf`
|
- Share exposure generated in `/etc/samba/generated/shares.conf`
|
||||||
- Dynamic share names are validated for SMB compatibility and deduplicated case-insensitively.
|
- Dynamic share names are validated for SMB compatibility and deduplicated case-insensitively.
|
||||||
|
- Group membership changes are refreshed during each reconciliation cycle (winbind cache flush + Samba config reload).
|
||||||
|
|
||||||
## Useful Commands
|
## Useful Commands
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ LDAP_FILTER = (
|
|||||||
)
|
)
|
||||||
GROUP_PREFIXES = ("FileShare_", "FS_")
|
GROUP_PREFIXES = ("FileShare_", "FS_")
|
||||||
USER_STATUS_FILTER = "(&(objectClass=user)(!(objectClass=computer))(sAMAccountName=*))"
|
USER_STATUS_FILTER = "(&(objectClass=user)(!(objectClass=computer))(sAMAccountName=*))"
|
||||||
|
GROUP_TITLE_ATTRS = ("displayname", "name", "cn")
|
||||||
|
|
||||||
REQUIRED_ENV = ["REALM", "WORKGROUP", "DOMAIN"]
|
REQUIRED_ENV = ["REALM", "WORKGROUP", "DOMAIN"]
|
||||||
ATTR_RE = re.compile(r"^([^:]+)(::?)\s*(.*)$")
|
ATTR_RE = re.compile(r"^([^:]+)(::?)\s*(.*)$")
|
||||||
@@ -123,6 +124,15 @@ def derive_share_name(sam_account_name: str) -> Optional[str]:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def derive_group_title(entry: Dict[str, Tuple[str, bool]]) -> Optional[str]:
|
||||||
|
for attr in GROUP_TITLE_ATTRS:
|
||||||
|
if attr in entry:
|
||||||
|
value = entry[attr][0].strip()
|
||||||
|
if value:
|
||||||
|
return value
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def parse_groups_from_ldap_output(output: str) -> List[Dict[str, str]]:
|
def parse_groups_from_ldap_output(output: str) -> List[Dict[str, str]]:
|
||||||
entries = parse_ldap_entries(output)
|
entries = parse_ldap_entries(output)
|
||||||
|
|
||||||
@@ -133,7 +143,7 @@ def parse_groups_from_ldap_output(output: str) -> List[Dict[str, str]]:
|
|||||||
|
|
||||||
sam_value, _ = entry["samaccountname"]
|
sam_value, _ = entry["samaccountname"]
|
||||||
sam = sam_value.strip()
|
sam = sam_value.strip()
|
||||||
share_name = derive_share_name(sam)
|
share_name = derive_group_title(entry) or derive_share_name(sam)
|
||||||
if not share_name:
|
if not share_name:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -157,7 +167,18 @@ def parse_groups_from_ldap_output(output: str) -> List[Dict[str, str]]:
|
|||||||
|
|
||||||
def fetch_groups_via_net_ads() -> List[Dict[str, str]]:
|
def fetch_groups_via_net_ads() -> List[Dict[str, str]]:
|
||||||
result = run_command(
|
result = run_command(
|
||||||
["net", "ads", "search", "-P", LDAP_FILTER, "objectGUID", "sAMAccountName"],
|
[
|
||||||
|
"net",
|
||||||
|
"ads",
|
||||||
|
"search",
|
||||||
|
"-P",
|
||||||
|
LDAP_FILTER,
|
||||||
|
"objectGUID",
|
||||||
|
"sAMAccountName",
|
||||||
|
"displayName",
|
||||||
|
"name",
|
||||||
|
"cn",
|
||||||
|
],
|
||||||
check=False,
|
check=False,
|
||||||
)
|
)
|
||||||
if result.returncode != 0:
|
if result.returncode != 0:
|
||||||
@@ -204,6 +225,9 @@ def fetch_groups_via_ldap_bind() -> List[Dict[str, str]]:
|
|||||||
LDAP_FILTER,
|
LDAP_FILTER,
|
||||||
"objectGUID",
|
"objectGUID",
|
||||||
"sAMAccountName",
|
"sAMAccountName",
|
||||||
|
"displayName",
|
||||||
|
"name",
|
||||||
|
"cn",
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
return parse_groups_from_ldap_output(result.stdout)
|
return parse_groups_from_ldap_output(result.stdout)
|
||||||
@@ -447,6 +471,12 @@ def reload_samba() -> None:
|
|||||||
log("smbcontrol reload-config failed; will retry on next run")
|
log("smbcontrol reload-config failed; will retry on next run")
|
||||||
|
|
||||||
|
|
||||||
|
def refresh_winbind_cache() -> None:
|
||||||
|
result = run_command(["net", "cache", "flush"], check=False)
|
||||||
|
if result.returncode != 0:
|
||||||
|
log("net cache flush failed; group membership updates may be delayed")
|
||||||
|
|
||||||
|
|
||||||
def resolve_user_uid(qualified_user: str) -> Optional[int]:
|
def resolve_user_uid(qualified_user: str) -> Optional[int]:
|
||||||
try:
|
try:
|
||||||
return pwd.getpwnam(qualified_user).pw_uid
|
return pwd.getpwnam(qualified_user).pw_uid
|
||||||
@@ -701,6 +731,7 @@ def with_lock() -> bool:
|
|||||||
|
|
||||||
sync_public_directory()
|
sync_public_directory()
|
||||||
sync_private_directories()
|
sync_private_directories()
|
||||||
|
refresh_winbind_cache()
|
||||||
reload_samba()
|
reload_samba()
|
||||||
log("Reconciliation completed")
|
log("Reconciliation completed")
|
||||||
return True
|
return True
|
||||||
|
|||||||
Reference in New Issue
Block a user