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
1 change: 1 addition & 0 deletions src/mas/devops/mas/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@
getMasChannel,
updateIBMEntitlementKey,
getMasPublicClusterIssuer,
getPermissionMode,
)
90 changes: 90 additions & 0 deletions src/mas/devops/mas/suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -357,3 +357,93 @@ def getMasPublicClusterIssuer(dynClient: DynamicClient, instanceId: str) -> str
except UnauthorizedError as e:
logger.error(f"Error: Unable to retrieve MAS instance due to failed authorization: {e}")
return None


def getPermissionMode(dynClient: DynamicClient, instanceId: str) -> str | None:
"""
Detect the current RBAC permission mode for a MAS instance.

This function determines whether MAS is installed with cluster-level permissions,
namespace-scoped permissions (essential + non-essential), or minimal essential-only
permissions by checking for the existence of RBAC resources in the cluster.

RBAC Resource Distribution:
- Cluster mode: ClusterRoles + Essential Roles
- Namespaced mode: Essential Roles + Non-essential Roles
- Minimal mode: Essential Roles ONLY

Detection Logic:
1. Check for ClusterRoles → cluster mode
2. Check for non-essential openshift-marketplace Role → namespaced mode
3. No ClusterRole and no openshift-marketplace Role → minimal mode

Args:
dynClient (DynamicClient): OpenShift dynamic client for cluster API interactions.
instanceId (str): The MAS instance identifier.

Returns:
str: Permission mode - "cluster", "namespaced", or "minimal"
Returns None if unable to determine (e.g., no RBAC resources found)
"""
try:
# Step 1: Check for ClusterRoles (indicates cluster mode)
clusterRoleAPI = dynClient.resources.get(api_version="rbac.authorization.k8s.io/v1", kind="ClusterRole")

# Look for MAS ClusterRoles with the instance ID pattern
clusterRoleName = f"mas:{instanceId}:core:coreapi"
try:
clusterRoleAPI.get(name=clusterRoleName)
logger.info(f"Found ClusterRole '{clusterRoleName}' - permission mode is 'cluster'")
return "cluster"
except NotFoundError:
logger.debug(f"ClusterRole '{clusterRoleName}' not found, checking for non-essential Roles")

# Step 2: Check for non-essential openshift-marketplace Role (only exists in namespaced mode)
roleAPI = dynClient.resources.get(api_version="rbac.authorization.k8s.io/v1", kind="Role")

# This role only exists in namespaced mode (applied via role-non-essential-core-coreapi-openshift-marketplace.yaml)
marketplaceRoleName = f"mas:{instanceId}:core:coreapi:openshift-marketplace"
marketplaceNamespace = "openshift-marketplace"

try:
roleAPI.get(name=marketplaceRoleName, namespace=marketplaceNamespace)
logger.info(f"Found non-essential Role '{marketplaceRoleName}' in namespace '{marketplaceNamespace}' - permission mode is 'namespaced'")
return "namespaced"
except NotFoundError:
logger.debug("Non-essential openshift-marketplace Role not found, checking for essential roles")

# Step 3: Verify minimal mode by checking for essential roles in mas-{instanceId}-core namespace
# Essential roles have pattern: mas:{instanceId}:core:suite:{app}:essential
coreNamespace = f"mas-{instanceId}-core"

# Try to find at least one essential role to confirm minimal mode
# Check common apps that might be installed
essentialRolePatterns = [
f"mas:{instanceId}:core:suite:manage:essential",
f"mas:{instanceId}:core:suite:iot:essential",
f"mas:{instanceId}:core:suite:monitor:essential",
f"mas:{instanceId}:core:suite:predict:essential",
f"mas:{instanceId}:core:suite:arcgis:essential",
f"mas:{instanceId}:core:suite:facilities:essential",
f"mas:{instanceId}:core:suite:optimizer:essential",
f"mas:{instanceId}:core:suite:visualinspection:essential"
]

for essentialRoleName in essentialRolePatterns:
try:
roleAPI.get(name=essentialRoleName, namespace=coreNamespace)
logger.info(f"Found essential Role '{essentialRoleName}' in namespace '{coreNamespace}' with no non-essential roles - permission mode is 'minimal'")
return "minimal"
except NotFoundError:
continue

# If we couldn't find any RBAC resources, return None
logger.warning(f"Unable to determine permission mode for instance '{instanceId}' ")
return None

except ResourceNotFoundError:
logger.warning("Required API resources not found in the cluster")
return None
except UnauthorizedError as e:
logger.error(f"Error: Unable to check permissions due to failed authorization: {e}")
return None
Loading