Mini Shell
"""
Management of PostgreSQL users (roles)
======================================
The postgres_users module is used to create and manage Postgres users.
.. code-block:: yaml
frank:
postgres_user.present
"""
import datetime
import logging
# Salt imports
from salt.modules import postgres
log = logging.getLogger(__name__)
def __virtual__():
"""
Only load if the postgres module is present
"""
if "postgres.user_exists" not in __salt__:
return (
False,
"Unable to load postgres module. Make sure `postgres.bins_dir` is set.",
)
return True
def present(
name,
createdb=None,
createroles=None,
encrypted=None,
superuser=None,
replication=None,
inherit=None,
login=None,
password=None,
default_password=None,
refresh_password=None,
valid_until=None,
groups=None,
user=None,
maintenance_db=None,
db_password=None,
db_host=None,
db_port=None,
db_user=None,
):
"""
Ensure that the named user is present with the specified privileges
Please note that the user/group notion in postgresql is just abstract, we
have roles, where users can be seen as roles with the LOGIN privilege
and groups the others.
name
The name of the system user to manage.
createdb
Is the user allowed to create databases?
createroles
Is the user allowed to create other users?
encrypted
How the password should be stored.
If encrypted is ``None``, ``True``, or ``md5``, it will use
PostgreSQL's MD5 algorithm.
If encrypted is ``False``, it will be stored in plaintext.
If encrypted is ``scram-sha-256``, it will use the algorithm described
in RFC 7677.
.. versionchanged:: 3003
Prior versions only supported ``True`` and ``False``
login
Should the group have login perm
inherit
Should the group inherit permissions
superuser
Should the new user be a "superuser"
replication
Should the new user be allowed to initiate streaming replication
password
The user's password.
It can be either a plain string or a pre-hashed password::
'md5{MD5OF({password}{role}}'
'SCRAM-SHA-256${iterations}:{salt}${stored_key}:{server_key}'
If encrypted is not ``False``, then the password will be converted
to the appropriate format above, if not already. As a consequence,
passwords that start with "md5" or "SCRAM-SHA-256" cannot be used.
default_password
The password used only when creating the user, unless password is set.
.. versionadded:: 2016.3.0
refresh_password
Password refresh flag
Boolean attribute to specify whether to password comparison check
should be performed.
If refresh_password is ``True``, the password will be automatically
updated without extra password change check.
This behaviour makes it possible to execute in environments without
superuser access available, e.g. Amazon RDS for PostgreSQL
valid_until
A date and time after which the role's password is no longer valid.
groups
A string of comma separated groups the user should be in
user
System user all operations should be performed on behalf of
.. versionadded:: 0.17.0
db_user
Postgres database username, if different from config or default.
db_password
Postgres user's password, if any password, for a specified db_user.
db_host
Postgres database host, if different from config or default.
db_port
Postgres database port, if different from config or default.
"""
ret = {
"name": name,
"changes": {},
"result": True,
"comment": f"User {name} is already present",
}
db_args = {
"maintenance_db": maintenance_db,
"runas": user,
"host": db_host,
"user": db_user,
"port": db_port,
"password": db_password,
}
# default to encrypted passwords
if encrypted is None:
encrypted = postgres._DEFAULT_PASSWORDS_ENCRYPTION
# check if user exists
mode = "create"
user_attr = __salt__["postgres.role_get"](
name, return_password=not refresh_password, **db_args
)
if user_attr is not None:
mode = "update"
if mode == "create" and password is None:
password = default_password
if password is not None:
if (
mode == "update"
and not refresh_password
and postgres._verify_password(
name, password, user_attr["password"], encrypted
)
):
# if password already matches then don't touch it
password = None
else:
# encrypt password if necessary
password = postgres._maybe_encrypt_password(
name, password, encrypted=encrypted
)
update = {}
if mode == "update":
user_groups = user_attr.get("groups", [])
if createdb is not None and user_attr["can create databases"] != createdb:
update["createdb"] = createdb
if inherit is not None and user_attr["inherits privileges"] != inherit:
update["inherit"] = inherit
if login is not None and user_attr["can login"] != login:
update["login"] = login
if createroles is not None and user_attr["can create roles"] != createroles:
update["createroles"] = createroles
if replication is not None and user_attr["replication"] != replication:
update["replication"] = replication
if superuser is not None and user_attr["superuser"] != superuser:
update["superuser"] = superuser
if password is not None:
update["password"] = True
if valid_until is not None:
valid_until_dt = __salt__["postgres.psql_query"](
"SELECT '{}'::timestamp(0) as dt;".format(
valid_until.replace("'", "''")
),
**db_args,
)[0]["dt"]
try:
valid_until_dt = datetime.datetime.strptime(
valid_until_dt, "%Y-%m-%d %H:%M:%S"
)
except ValueError:
valid_until_dt = None
if valid_until_dt != user_attr["expiry time"]:
update["valid_until"] = valid_until
if groups is not None:
lgroups = groups
if isinstance(groups, str):
lgroups = lgroups.split(",")
if isinstance(lgroups, list):
missing_groups = [a for a in lgroups if a not in user_groups]
if missing_groups:
update["groups"] = missing_groups
if mode == "create" or (mode == "update" and update):
if __opts__["test"]:
if update:
ret["changes"][name] = update
ret["result"] = None
ret["comment"] = f"User {name} is set to be {mode}d"
return ret
cret = __salt__[f"postgres.user_{mode}"](
username=name,
createdb=createdb,
createroles=createroles,
encrypted=encrypted,
superuser=superuser,
login=login,
inherit=inherit,
replication=replication,
rolepassword=password,
valid_until=valid_until,
groups=groups,
**db_args,
)
else:
cret = None
if cret:
ret["comment"] = f"The user {name} has been {mode}d"
if update:
ret["changes"][name] = update
else:
ret["changes"][name] = "Present"
elif cret is not None:
ret["comment"] = f"Failed to {mode} user {name}"
ret["result"] = False
else:
ret["result"] = True
return ret
def absent(
name,
user=None,
maintenance_db=None,
db_password=None,
db_host=None,
db_port=None,
db_user=None,
):
"""
Ensure that the named user is absent
name
The username of the user to remove
user
System user all operations should be performed on behalf of
.. versionadded:: 0.17.0
db_user
database username if different from config or default
db_password
user password if any password for a specified user
db_host
Database host if different from config or default
db_port
Database port if different from config or default
"""
ret = {"name": name, "changes": {}, "result": True, "comment": ""}
db_args = {
"maintenance_db": maintenance_db,
"runas": user,
"host": db_host,
"user": db_user,
"port": db_port,
"password": db_password,
}
# check if user exists and remove it
if __salt__["postgres.user_exists"](name, **db_args):
if __opts__["test"]:
ret["result"] = None
ret["comment"] = f"User {name} is set to be removed"
return ret
if __salt__["postgres.user_remove"](name, **db_args):
ret["comment"] = f"User {name} has been removed"
ret["changes"][name] = "Absent"
return ret
else:
ret["result"] = False
ret["comment"] = f"User {name} failed to be removed"
return ret
else:
ret["comment"] = f"User {name} is not present, so it cannot be removed"
return ret
Zerion Mini Shell 1.0