Mini Shell
"""
Module for Solaris' Role-Based Access Control
"""
import logging
import salt.utils.files
import salt.utils.path
log = logging.getLogger(__name__)
# Define the module's virtual name
__virtualname__ = "rbac"
def __virtual__():
"""
Provides rbac if we are running on a solaris like system
"""
if __grains__["kernel"] == "SunOS" and salt.utils.path.which("profiles"):
return __virtualname__
return (
False,
f"{__virtualname__} module can only be loaded on a solaris like system",
)
def profile_list(default_only=False):
"""
List all available profiles
default_only : boolean
return only default profile
CLI Example:
.. code-block:: bash
salt '*' rbac.profile_list
"""
profiles = {}
default_profiles = ["All"]
## lookup default profile(s)
with salt.utils.files.fopen("/etc/security/policy.conf", "r") as policy_conf:
for policy in policy_conf:
policy = salt.utils.stringutils.to_unicode(policy)
policy = policy.split("=")
if policy[0].strip() == "PROFS_GRANTED":
default_profiles.extend(policy[1].strip().split(","))
## read prof_attr file (profname:res1:res2:desc:attr)
with salt.utils.files.fopen("/etc/security/prof_attr", "r") as prof_attr:
for profile in prof_attr:
profile = salt.utils.stringutils.to_unicode(profile)
profile = profile.split(":")
# skip comments and non complaint lines
if len(profile) != 5:
continue
# add profile info to dict
profiles[profile[0]] = profile[3]
## filtered profiles
if default_only:
for p in [p for p in profiles if p not in default_profiles]:
del profiles[p]
return profiles
def profile_get(user, default_hidden=True):
"""
List profiles for user
user : string
username
default_hidden : boolean
hide default profiles
CLI Example:
.. code-block:: bash
salt '*' rbac.profile_get leo
salt '*' rbac.profile_get leo default_hidden=False
"""
user_profiles = []
## read user_attr file (user:qualifier:res1:res2:attr)
with salt.utils.files.fopen("/etc/user_attr", "r") as user_attr:
for profile in user_attr:
profile = salt.utils.stringutils.to_unicode(profile)
profile = profile.strip().split(":")
# skip comments and non complaint lines
if len(profile) != 5:
continue
# skip other users
if profile[0] != user:
continue
# parse attr
attrs = {}
for attr in profile[4].strip().split(";"):
attr_key, attr_val = attr.strip().split("=")
if attr_key in ["auths", "profiles", "roles"]:
attrs[attr_key] = attr_val.strip().split(",")
else:
attrs[attr_key] = attr_val
if "profiles" in attrs:
user_profiles.extend(attrs["profiles"])
## remove default profiles
if default_hidden:
for profile in profile_list(default_only=True):
if profile in user_profiles:
user_profiles.remove(profile)
return list(set(user_profiles))
def profile_add(user, profile):
"""
Add profile to user
user : string
username
profile : string
profile name
CLI Example:
.. code-block:: bash
salt '*' rbac.profile_add martine 'Primary Administrator'
salt '*' rbac.profile_add martine 'User Management,User Security'
"""
ret = {}
## validate profiles
profiles = profile.split(",")
known_profiles = profile_list().keys()
valid_profiles = [p for p in profiles if p in known_profiles]
log.debug(
"rbac.profile_add - profiles=%s, known_profiles=%s, valid_profiles=%s",
profiles,
known_profiles,
valid_profiles,
)
## update user profiles
if valid_profiles:
res = __salt__["cmd.run_all"](
'usermod -P "{profiles}" {login}'.format(
login=user,
profiles=",".join(set(profile_get(user) + valid_profiles)),
)
)
if res["retcode"] > 0:
ret["Error"] = {
"retcode": res["retcode"],
"message": res["stderr"] if "stderr" in res else res["stdout"],
}
return ret
## update return value
active_profiles = profile_get(user, False)
for p in profiles:
if p not in valid_profiles:
ret[p] = "Unknown"
elif p in active_profiles:
ret[p] = "Added"
else:
ret[p] = "Failed"
return ret
def profile_rm(user, profile):
"""
Remove profile from user
user : string
username
profile : string
profile name
CLI Example:
.. code-block:: bash
salt '*' rbac.profile_rm jorge 'Primary Administrator'
salt '*' rbac.profile_rm jorge 'User Management,User Security'
"""
ret = {}
## validate profiles
profiles = profile.split(",")
known_profiles = profile_list().keys()
valid_profiles = [p for p in profiles if p in known_profiles]
log.debug(
"rbac.profile_rm - profiles=%s, known_profiles=%s, valid_profiles=%s",
profiles,
known_profiles,
valid_profiles,
)
## update user profiles
if valid_profiles:
res = __salt__["cmd.run_all"](
'usermod -P "{profiles}" {login}'.format(
login=user,
profiles=",".join(
[p for p in profile_get(user) if p not in valid_profiles]
),
)
)
if res["retcode"] > 0:
ret["Error"] = {
"retcode": res["retcode"],
"message": res["stderr"] if "stderr" in res else res["stdout"],
}
return ret
## update return value
active_profiles = profile_get(user, False)
for p in profiles:
if p not in valid_profiles:
ret[p] = "Unknown"
elif p in active_profiles:
ret[p] = "Failed"
else:
ret[p] = "Remove"
return ret
def role_list():
"""
List all available roles
CLI Example:
.. code-block:: bash
salt '*' rbac.role_list
"""
roles = {}
## read user_attr file (user:qualifier:res1:res2:attr)
with salt.utils.files.fopen("/etc/user_attr", "r") as user_attr:
for role in user_attr:
role = salt.utils.stringutils.to_unicode(role)
role = role.split(":")
# skip comments and non complaint lines
if len(role) != 5:
continue
# parse attr
attrs = {}
for attr in role[4].split(";"):
attr_key, attr_val = attr.split("=")
if attr_key in ["auths", "profiles", "roles"]:
attrs[attr_key] = attr_val.split(",")
else:
attrs[attr_key] = attr_val
role[4] = attrs
# add role info to dict
if "type" in role[4] and role[4]["type"] == "role":
del role[4]["type"]
roles[role[0]] = role[4]
return roles
def role_get(user):
"""
List roles for user
user : string
username
CLI Example:
.. code-block:: bash
salt '*' rbac.role_get leo
"""
user_roles = []
## read user_attr file (user:qualifier:res1:res2:attr)
with salt.utils.files.fopen("/etc/user_attr", "r") as user_attr:
for role in user_attr:
role = salt.utils.stringutils.to_unicode(role)
role = role.strip().strip().split(":")
# skip comments and non complaint lines
if len(role) != 5:
continue
# skip other users
if role[0] != user:
continue
# parse attr
attrs = {}
for attr in role[4].strip().split(";"):
attr_key, attr_val = attr.strip().split("=")
if attr_key in ["auths", "profiles", "roles"]:
attrs[attr_key] = attr_val.strip().split(",")
else:
attrs[attr_key] = attr_val
if "roles" in attrs:
user_roles.extend(attrs["roles"])
return list(set(user_roles))
def role_add(user, role):
"""
Add role to user
user : string
username
role : string
role name
CLI Example:
.. code-block:: bash
salt '*' rbac.role_add martine netcfg
salt '*' rbac.role_add martine netcfg,zfssnap
"""
ret = {}
## validate roles
roles = role.split(",")
known_roles = role_list().keys()
valid_roles = [r for r in roles if r in known_roles]
log.debug(
"rbac.role_add - roles=%s, known_roles=%s, valid_roles=%s",
roles,
known_roles,
valid_roles,
)
## update user roles
if valid_roles:
res = __salt__["cmd.run_all"](
'usermod -R "{roles}" {login}'.format(
login=user,
roles=",".join(set(role_get(user) + valid_roles)),
)
)
if res["retcode"] > 0:
ret["Error"] = {
"retcode": res["retcode"],
"message": res["stderr"] if "stderr" in res else res["stdout"],
}
return ret
## update return value
active_roles = role_get(user)
for r in roles:
if r not in valid_roles:
ret[r] = "Unknown"
elif r in active_roles:
ret[r] = "Added"
else:
ret[r] = "Failed"
return ret
def role_rm(user, role):
"""
Remove role from user
user : string
username
role : string
role name
CLI Example:
.. code-block:: bash
salt '*' rbac.role_rm jorge netcfg
salt '*' rbac.role_rm jorge netcfg,zfssnap
"""
ret = {}
## validate roles
roles = role.split(",")
known_roles = role_list().keys()
valid_roles = [r for r in roles if r in known_roles]
log.debug(
"rbac.role_rm - roles=%s, known_roles=%s, valid_roles=%s",
roles,
known_roles,
valid_roles,
)
## update user roles
if valid_roles:
res = __salt__["cmd.run_all"](
'usermod -R "{roles}" {login}'.format(
login=user,
roles=",".join([r for r in role_get(user) if r not in valid_roles]),
)
)
if res["retcode"] > 0:
ret["Error"] = {
"retcode": res["retcode"],
"message": res["stderr"] if "stderr" in res else res["stdout"],
}
return ret
## update return value
active_roles = role_get(user)
for r in roles:
if r not in valid_roles:
ret[r] = "Unknown"
elif r in active_roles:
ret[r] = "Failed"
else:
ret[r] = "Remove"
return ret
def auth_list():
"""
List all available authorization
CLI Example:
.. code-block:: bash
salt '*' rbac.auth_list
"""
auths = {}
## read auth_attr file (name:res1:res2:short_desc:long_desc:attr)
with salt.utils.files.fopen("/etc/security/auth_attr", "r") as auth_attr:
for auth in auth_attr:
auth = salt.utils.stringutils.to_unicode(auth)
auth = auth.split(":")
# skip comments and non complaint lines
if len(auth) != 6:
continue
# add auth info to dict
if auth[0][-1:] == ".":
auth[0] = f"{auth[0]}*"
auths[auth[0]] = auth[3]
return auths
def auth_get(user, computed=True):
"""
List authorization for user
user : string
username
computed : boolean
merge results from `auths` command into data from user_attr
CLI Example:
.. code-block:: bash
salt '*' rbac.auth_get leo
"""
user_auths = []
## read user_attr file (user:qualifier:res1:res2:attr)
with salt.utils.files.fopen("/etc/user_attr", "r") as user_attr:
for auth in user_attr:
auth = salt.utils.stringutils.to_unicode(auth)
auth = auth.strip().split(":")
# skip comments and non complaint lines
if len(auth) != 5:
continue
# skip other users
if auth[0] != user:
continue
# parse attr
attrs = {}
for attr in auth[4].strip().split(";"):
attr_key, attr_val = attr.strip().split("=")
if attr_key in ["auths", "profiles", "roles"]:
attrs[attr_key] = attr_val.strip().split(",")
else:
attrs[attr_key] = attr_val
if "auths" in attrs:
user_auths.extend(attrs["auths"])
## also parse auths command
if computed:
res = __salt__["cmd.run_all"](f"auths {user}")
if res["retcode"] == 0:
for auth in res["stdout"].splitlines():
if "," in auth:
user_auths.extend(auth.strip().split(","))
else:
user_auths.append(auth.strip())
return list(set(user_auths))
def auth_add(user, auth):
"""
Add authorization to user
user : string
username
auth : string
authorization name
CLI Example:
.. code-block:: bash
salt '*' rbac.auth_add martine solaris.zone.manage
salt '*' rbac.auth_add martine solaris.zone.manage,solaris.mail.mailq
"""
ret = {}
## validate auths
auths = auth.split(",")
known_auths = auth_list().keys()
valid_auths = [r for r in auths if r in known_auths]
log.debug(
"rbac.auth_add - auths=%s, known_auths=%s, valid_auths=%s",
auths,
known_auths,
valid_auths,
)
## update user auths
if valid_auths:
res = __salt__["cmd.run_all"](
'usermod -A "{auths}" {login}'.format(
login=user,
auths=",".join(set(auth_get(user, False) + valid_auths)),
)
)
if res["retcode"] > 0:
ret["Error"] = {
"retcode": res["retcode"],
"message": res["stderr"] if "stderr" in res else res["stdout"],
}
return ret
## update return value
active_auths = auth_get(user, False)
for a in auths:
if a not in valid_auths:
ret[a] = "Unknown"
elif a in active_auths:
ret[a] = "Added"
else:
ret[a] = "Failed"
return ret
def auth_rm(user, auth):
"""
Remove authorization from user
user : string
username
auth : string
authorization name
CLI Example:
.. code-block:: bash
salt '*' rbac.auth_rm jorge solaris.zone.manage
salt '*' rbac.auth_rm jorge solaris.zone.manage,solaris.mail.mailq
"""
ret = {}
## validate auths
auths = auth.split(",")
known_auths = auth_list().keys()
valid_auths = [a for a in auths if a in known_auths]
log.debug(
"rbac.auth_rm - auths=%s, known_auths=%s, valid_auths=%s",
auths,
known_auths,
valid_auths,
)
## update user auths
if valid_auths:
res = __salt__["cmd.run_all"](
'usermod -A "{auths}" {login}'.format(
login=user,
auths=",".join(
[a for a in auth_get(user, False) if a not in valid_auths]
),
)
)
if res["retcode"] > 0:
ret["Error"] = {
"retcode": res["retcode"],
"message": res["stderr"] if "stderr" in res else res["stdout"],
}
return ret
## update return value
active_auths = auth_get(user, False)
for a in auths:
if a not in valid_auths:
ret[a] = "Unknown"
elif a in active_auths:
ret[a] = "Failed"
else:
ret[a] = "Remove"
return ret
Zerion Mini Shell 1.0