
    Vh              	          d dl mZmZmZ eZdZdZdZd dl	Z	d dl
Z
d dlZd dlZd dlmZ d dlmZmZ d dlmZmZmZ d d	lmZ d d
lmZ d dlmZ d dlmZmZmZ d dl m!Z!m"Z"m#Z#m$Z$m%Z%m&Z&m'Z'm(Z(m)Z)m*Z* d dl+m,Z, e!re" e,d      k  rd dl-Z.ne!rd dl.Z.	 d dlm/Z/ dZ0dZ2ddiZ3dZ4 e5 e6d       e6d            Z7 e5ddddddd !      Z8g Z9g Z: G d" d#e;      Z< G d$ d%e;      Z=d& Z>d' Z?d( Z@d) ZAd* ZBd+ ZCd, ZDd- ZEd. ZFdGd/ZGd0 ZHd1 ZId2 ZJd3 ZKd4 ZLd5 ZMd6 ZNd7 ZOd8 ZPd9 ZQd: ZRd; ZSd< ZTd= ZUd> ZVd? ZWd@ ZXdA ZYdB ZZdC Z[dD Z\dE Z]e^dFk(  r e]        yy# e1$ r dZ0Y w xY w)H    )absolute_importdivisionprint_functionaw)  
---
module: postgresql_user
short_description: Create, alter, or remove a user (role) from a PostgreSQL server instance
description:
- Creates, alters, or removes a user (role) from a PostgreSQL server instance
  ("cluster" in PostgreSQL terminology) and, optionally,
  grants the user access to an existing database or tables.
- A user is a role with login privilege.
- You can also use it to grant or revoke user's privileges in a particular database.
- You cannot remove a user while it still has any privileges granted to it in any database.
- Set I(fail_on_user) to C(false) to make the module ignore failures when trying to remove a user.
  In this case, the module reports if changes happened as usual and separately reports
  whether the user has been removed or not.
- B(WARNING) The I(priv) option has been B(deprecated) and will be removed in community.postgresql 4.0.0. Please use the
  M(community.postgresql.postgresql_privs) module instead.
options:
  name:
    description:
    - Name of the user (role) to add or remove.
    type: str
    required: true
    aliases:
    - user
  password:
    description:
    - Set the user's password, before 1.4 this was required.
    - Password can be passed unhashed or hashed (MD5-hashed).
    - An unhashed password is automatically hashed when saved into the
      database if I(encrypted) is set, otherwise it is saved in
      plain text format.
    - When passing an MD5-hashed password, you must generate it with the format
      C('str["md5"] + md5[ password + username ]'), resulting in a total of
      35 characters. An easy way to do this is
      C(echo "md5`echo -n 'verysecretpasswordJOE' | md5sum | awk '{print $1}'`").
    - Note that if the provided password string is already in MD5-hashed
      format, then it is used as-is, regardless of I(encrypted) option.
    type: str
  login_db:
    description:
    - Name of database to connect to and where user's permissions are granted.
    - The V(db) alias is deprecated and will be removed in version 5.0.0.
    type: str
    default: ''
    aliases:
    - db
  fail_on_user:
    description:
    - If C(true), fails when the user (role) cannot be removed. Otherwise just log and continue.
    default: true
    type: bool
    aliases:
    - fail_on_role
  priv:
    description:
    - This option has been B(deprecated) and will be removed in
      community.postgresql 4.0.0. Please use the M(community.postgresql.postgresql_privs) module to
      GRANT/REVOKE permissions instead.
    - "Slash-separated PostgreSQL privileges string: C(priv1/priv2), where
      you can define the user's privileges for the database ( allowed options - 'CREATE',
      'CONNECT', 'TEMPORARY', 'TEMP', 'ALL'. For example C(CONNECT) ) or
      for table ( allowed options - 'SELECT', 'INSERT', 'UPDATE', 'DELETE',
      'TRUNCATE', 'REFERENCES', 'TRIGGER', 'ALL'. For example
      C(table:SELECT) ). Mixed example of this string:
      C(CONNECT/CREATE/table1:SELECT/table2:INSERT)."
    - When I(priv) contains tables, the module uses the schema C(public) by default.
      If you need to specify a different schema, use the C(schema_name.table_name) notation,
      for example, C(pg_catalog.pg_stat_database:SELECT).
    type: str
  role_attr_flags:
    description:
    - "PostgreSQL user attributes string in the format: CREATEDB,CREATEROLE,SUPERUSER."
    - Note that '[NO]CREATEUSER' is deprecated.
    - To create a simple role for using it like a group, use C(NOLOGIN) flag.
    - See the full list of supported flags in documentation for your PostgreSQL version.
    type: str
    default: ''
  session_role:
    description:
    - Switch to session role after connecting.
    - The specified session role must be a role that the current login_user is a member of.
    - Permissions checking for SQL commands is carried out as though the session role
      were the one that had logged in originally.
    type: str
  state:
    description:
    - The user (role) state.
    type: str
    default: present
    choices: [ absent, present ]
  encrypted:
    description:
    - Whether the password is stored hashed in the database.
    - You can specify an unhashed password, and PostgreSQL ensures
      the stored password is hashed when I(encrypted=true) is set.
      If you specify a hashed password, the module uses it as-is,
      regardless of the setting of I(encrypted).
    - "Note: Postgresql 10 and newer does not support unhashed passwords."
    - Previous to Ansible 2.6, this was C(false) by default.
    default: true
    type: bool
  expires:
    description:
    - The date at which the user's password is to expire.
    - If set to C('infinity'), user's password never expires.
    - Note that this value must be a valid SQL date and time type.
    type: str
  no_password_changes:
    description:
    - If C(true), does not inspect the database for password changes.
      If the user already exists, skips all password related checks.
      Useful when C(pg_authid) is not accessible (such as in AWS RDS).
      Otherwise, makes password changes as necessary.
    default: false
    type: bool
  conn_limit:
    description:
    - Specifies the user (role) connection limit.
    type: int
  ssl_mode:
    description:
      - Determines how an SSL session is negotiated with the server.
      - See U(https://www.postgresql.org/docs/current/static/libpq-ssl.html) for more information on the modes.
      - Default of C(prefer) matches libpq default.
    type: str
    default: prefer
    choices: [ allow, disable, prefer, require, verify-ca, verify-full ]
  ca_cert:
    description:
      - Specifies the name of a file containing SSL certificate authority (CA) certificate(s).
      - If the file exists, verifies that the server's certificate is signed by one of these authorities.
    type: str
    aliases: [ ssl_rootcert ]
  comment:
    description:
    - Adds a comment on the user (equivalent to the C(COMMENT ON ROLE) statement).
    - To reset the comment, pass an empty string.
    type: str
    version_added: '0.2.0'
  trust_input:
    description:
    - If C(false), checks whether values of options I(name), I(password), I(privs), I(expires),
      I(role_attr_flags), I(comment), I(session_role) are potentially dangerous.
    - It makes sense to use C(false) only when SQL injections through the options are possible.
    type: bool
    default: true
    version_added: '0.2.0'
  configuration:
    description:
      - Role-specific configuration parameters that would otherwise be set by C(ALTER ROLE user SET variable TO value;).
      - Takes a dict where the key is the name of the configuration parameter. If the key includes special characters
        like C(.) and C(-), it needs to be quoted to ensure the YAML is valid.
      - Sets or updates any parameter in the list that is not present or has the wrong value in the database.
      - Removes any parameter from the user that is not listed here.
      - Parameters that are present in the database but are not included in this list will only be reset, if
        O(reset_unspecified_configuration=true).
      - Inputs to O(user) as well as keys and values in this parameter are quoted by the module. If you require the
        user to contain a C("), you need to double it, otherwise the module will fail. C(") and C(') are not allowed in
        configuration keys and any C(') in the value of a configuration will be escaped by this module.
        Additionally, parameters and values are checked if O(trust_input) is C(false).
    type: dict
    default: {}
    version_added: '3.5.0'
  reset_unspecified_configuration:
    description:
      - If set to C(true), the user's default configuration parameters will be reset in case they are not included in
        O(configuration), otherwise existing parameters will not be modified if not included in O(configuration).
    type: bool
    default: false
    version_added: '3.5.0'
  quote_configuration_values:
    description:
      - Automatically quote the values of configuration variables added via I(configuration). The default is C(true)
        and setting this to C(false) leaves these options open to SQL-injections and makes the user responsible for
        properly quoting values.
      - This is required to be C(false) to modify settings like C(search_path), that need to be unquoted.
      - If this is C(false) you will also need to make sure that strings are properly quoted.
        For example C("'16MB'") for C(work_mem).
      - Set this only to C(false) if you know what you are doing!
    type: bool
    default: true
    version_added: '3.11.0'
notes:
- The module creates a user (role) with login privilege by default.
  Use C(NOLOGIN) I(role_attr_flags) to change this behaviour.
- If you specify C(PUBLIC) as the user (role), then the privilege changes apply to all users (roles).
  You may not specify password or role_attr_flags when the C(PUBLIC) user is specified.
- SCRAM-SHA-256-hashed passwords (SASL Authentication) require PostgreSQL version 10 or newer.
  On the previous versions the whole hashed string is used as a password.
- 'Working with SCRAM-SHA-256-hashed passwords, be sure you use the I(environment:) variable
  C(PGOPTIONS: "-c password_encryption=scram-sha-256") when it is not default
  for your PostgreSQL version (see the provided example).'
- On some systems (such as AWS RDS), C(pg_authid) is not accessible, thus, the module cannot compare
  the current and desired C(password). In this case, the module assumes that the passwords are
  different and changes it reporting that the state has been changed.
  To skip all password related checks for existing users, use I(no_password_changes=true).
- On some systems (such as AWS RDS), C(SUPERUSER) is unavailable. This means the C(SUPERUSER) and
  C(NOSUPERUSER) I(role_attr_flags) should not be specified to preserve idempotency and avoid
  InsufficientPrivilege errors.

attributes:
  check_mode:
    support: full

seealso:
- module: community.postgresql.postgresql_privs
- module: community.postgresql.postgresql_membership
- module: community.postgresql.postgresql_owner
- name: PostgreSQL database roles
  description: Complete reference of the PostgreSQL database roles documentation.
  link: https://www.postgresql.org/docs/current/user-manag.html
- name: PostgreSQL SASL Authentication
  description: Complete reference of the PostgreSQL SASL Authentication.
  link: https://www.postgresql.org/docs/current/sasl-authentication.html
author:
- Ansible Core Team
extends_documentation_fragment:
- community.postgresql.postgres
ak  
# This example uses the 'priv' argument which is deprecated.
# You should use the 'postgresql_privs' module instead.
- name: Connect to acme database, create django user, and grant access to database and products table
  community.postgresql.postgresql_user:
    login_db: acme
    name: django
    password: ceec4eif7ya
    priv: "CONNECT/products:ALL"
    expires: "Jan 31 2020"

- name: Add a comment on django user
  community.postgresql.postgresql_user:
    login_db: acme
    name: django
    comment: This is a test user

# Connect to default database, create rails user, set its password (MD5- or SHA256-hashed),
# and grant privilege to create other databases and demote rails from super user status if user exists
# the hash from the corresponding pg_authid entry.
- name: Create rails user, set MD5-hashed password, grant privs
  community.postgresql.postgresql_user:
    name: rails
    password: md59543f1d82624df2b31672ec0f7050460
    # password: SCRAM-SHA-256$4096:zFuajwIVdli9mK=NJkcv1Q++$JC4gWIrEHmF6sqRbEiZw5FFW45HUPrpVzNdoM72o730+;fqA4vLN3mCZGbhcbQyvNYY7anCrUTsem1eCh/4YA94=
    role_attr_flags: CREATEDB,NOSUPERUSER
  # When using sha256-hashed password:
  # environment:
  #   PGOPTIONS: "-c password_encryption=scram-sha-256"

# This example uses the 'priv' argument which is deprecated.
# You should use the 'postgresql_privs' module instead.
- name: Connect to acme database and remove test user privileges from there
  community.postgresql.postgresql_user:
    login_db: acme
    name: test
    priv: "ALL/products:ALL"
    state: absent
    fail_on_user: false

# This example uses the 'priv' argument which is deprecated.
# You should use the 'postgresql_privs' module instead.
- name: Connect to test database, remove test user from cluster
  community.postgresql.postgresql_user:
    login_db: test
    name: test
    priv: ALL
    state: absent

# This example uses the 'priv' argument which is deprecated.
# You should use the 'postgresql_privs' module instead.
- name: Connect to acme database and set user's password with no expire date
  community.postgresql.postgresql_user:
    login_db: acme
    name: django
    password: mysupersecretword
    priv: "CONNECT/products:ALL"
    expires: infinity

# Example privileges string format
# INSERT,UPDATE/table:SELECT/anothertable:ALL

- name: Connect to test database and remove an existing user's password
  community.postgresql.postgresql_user:
    login_db: test
    user: test
    password: ""

# Create user with a cleartext password if it does not exist or update its password.
# The password will be encrypted with SCRAM algorithm (available since PostgreSQL 10)
- name: Create appclient user with SCRAM-hashed password
  community.postgresql.postgresql_user:
    name: appclient
    password: "secret123"
  environment:
    PGOPTIONS: "-c password_encryption=scram-sha-256"

# This example uses the 'priv' argument which is deprecated.
# You should use the 'postgresql_privs' module instead.
- name: Create a user, grant SELECT on pg_catalog.pg_stat_database
  community.postgresql.postgresql_user:
    name: monitoring
    priv: 'pg_catalog.pg_stat_database:SELECT'

# Create a user and set a default-configuration that is active when they start a session
- name: Create a user with config-parameter
  community.postgresql.postgresql_user:
    name: appclient
    password: "secret123"
    configuration:
      work_mem: "16MB"

# Make sure user has only specified default configuration parameters
- name: Clear all configuration that is not explicitly defined for user
  community.postgresql.postgresql_user:
    name: appclient
    password: "secret123"
    configuration:
      work_mem: "16MB"
    reset_unspecified_configuration: true

- name: Set search_path for user
  community.postgresql.postgresql_user:
    name: postgres_exporter
    quote_configuration_values: false
    configuration:
      search_path: postgres_exporter, pg_catalog, public
z
queries:
  description: List of executed queries.
  returned: success
  type: list
  sample: ['CREATE USER "alice"', 'GRANT CONNECT ON DATABASE "acme" TO "alice"']
N)	b64decode)md5sha256)to_bytes	to_nativeto_text)AnsibleModule)	iteritems)saslprep)SQLParseErrorcheck_inputpg_quote_identifier)
HAS_PSYCOPGPSYCOPG_VERSIONconnect_to_dbensure_required_libsget_commentget_conn_paramsget_server_versionpg_cursor_argspostgres_common_argument_specset_comment)LooseVersionz3.0)pbkdf2_hmacTF)	SUPERUSER
CREATEROLECREATEDBINHERITLOGINREPLICATION	BYPASSRLSia zP^SCRAM-SHA-256\$(\d+):([A-Za-z0-9+\/=]+)\$([A-Za-z0-9+\/=]+):([A-Za-z0-9+\/=]+)$)SELECTINSERTUPDATEDELETETRUNCATE
REFERENCESTRIGGERALL)CREATECONNECT	TEMPORARYTEMPr,   tabledatabaserolsuperrolcreaterolerolcreatedb
rolinheritrolcanloginrolreplicationrolbypassrls)r   r   r    r!   r"   r#   r$   c                       e Zd Zy)InvalidFlagsErrorN__name__
__module____qualname__     x/home/dcms/DCMS/lib/python3.12/site-packages/ansible_collections/community/postgresql/plugins/modules/postgresql_user.pyr<   r<         rB   r<   c                       e Zd Zy)InvalidPrivsErrorNr=   rA   rB   rC   rF   rF     rD   rB   rF   c                 X    |dk(  ryd}| j                  |d|i       | j                  dkD  S )NPUBLICTz3SELECT rolname FROM pg_roles WHERE rolname=%(user)suserr   )executerowcount)cursorrI   querys      rC   user_existsrN     s3    xAE
NN564.)??QrB   c                    t        ||      }ddt        ||      iz  g}	|,|dk7  r'|	j                  dd|iz         |	j                  d       ||	j                  d       ||	j                  d	d
|iz         |	j                  |       dj                  |	      }	t        j                  |	       | j                  |	|       y)z"Create a new database user (role).passwordexpireszCREATE USER %(user)srI    WITH %(crypt)scryptPASSWORD %(password)sVALID UNTIL %(expires)sCONNECTION LIMIT %(conn_limit)s
conn_limit T)dict_pg_quote_userappendjoinexecuted_queriesrJ   )
rL   rI   rQ   role_attr_flags	encryptedrR   rY   modulequery_password_datarM   s
             rC   user_addrd     s     'B#nT6234 5EB%)(<<=,-./6,
9SST	LL!HHUOEE"
NN5-.rB   c                 J    | j                  d       | j                         d   S )NzSHOW password_encryptionpassword_encryption)rJ   fetchone)rL   s    rC   get_passwd_encryptionrh     s"    
NN-.??233rB   c                    |yd}||d   }t        |t              r|j                  d      }|dk(  r|d}|S t        j                  t
        |      r	||k7  rd}|S |t        rt        j                  t
        |      rt        j                  t
        |      }	 t        |j                  d            }t        |j                  d            }	t        |j                  d            }
t        j                  t        |            }t        d	t        |      |	|      }t        j                  |t         
      }|j#                  d       |j%                         |
k7  rd}|S t)        |      s|dk(  r	||k7  rd}|S |dk(  r]t+        |       }|dk(  rFdj-                  t/        t        |      t        |      z         j1                               }||k7  rd}|S |dk(  rd}|S # t&        $ r d}Y |S w xY w)zCheck if we should change the user's password.

    Compare the proposed password with the existing one, comparing
    hashes if encrypted. If we can't access it assume yes.
    TFrolpasswordasciirS            r   )	digestmods
   Server KeyUNENCRYPTED	ENCRYPTEDr   zmd5{0}zscram-sha-256)
isinstancebytesdecoderematchSCRAM_SHA256_REGEXpbkdf2_foundintgroupr   r   r   r   r	   hmacnewr   updatedigest	Exceptionis_pg_passwd_md5rh   formatr   	hexdigest)rL   current_role_attrsrI   rQ   ra   
pwchangingcurrent_passwordritsalt
server_keynormalized_passwordsalted_passwordserver_key_verifierdefault_pw_encryptionhashed_passwords                   rC   user_should_we_change_passwordr     s    !  J-m<&./66w? r>+!
v s XX((3++!
n e ) HH/1AB+-=>A"_ ,&qwwqz2
 '/&7&78I&J#"-hAT8UW[]_"`&*hh&&Q##**=9&--/:=!%J6 ' h'9+E++!
" ! +%$9&$A!$-"*//#hx6H8TX>6Y2Z2d2d2f"g"&66!%J  '/9 "
5  " "
. 5"s   CG G-,G-c                 H    | j                  d      rt        |       dk(  rdS dS )Nr   #   TF)
startswithlen)rQ   s    rC   r   r   +  s'     &&u-#h-62I4TuTrB   c                    d }	 d}|j                  |d|i       |j                         }||S 	 d}|j                  |d|i       |j                         }|S # t        j                  $ r | j	                          Y Rw xY w# t        j                  $ r3}| j	                          |j                  d|d|       Y d }~|S d }~ww xY w)Nz.SELECT * FROM pg_authid where rolname=%(user)srI   z-SELECT * FROM pg_roles where rolname=%(user)sz,Failed to get role details for current user : msg)rJ   rg   psycopgProgrammingErrorrollback	fail_json)db_connectionrb   rL   rI   r   rM   es          rC   get_role_attrsr   1  s    
!@uvtn-#__.
 %!!_?uvtn-#__.
 # ## ! ! ## _ UY[\]^^	_s(   &A &B  #A=<A= C(CCc                     d}| roi }| j                  d      D ]/  }|j                  d      rd||j                  ddd      <   +d||<   1 |j                         D ]  \  }}|t        |      |k7  sd} |S )NFrZ   NOrS   rl   T)splitr   replaceitemsPRIV_TO_AUTHID_COLUMN)r`   r   role_attr_flags_changingrole_attr_flags_dictr   role_attr_namerole_attr_values          rC   need_to_change_role_attr_flagsr   O  s    
  %! &&s+ 	/A||D!?D$QYYtR%;<*.$Q'		/ 0D/I/I/K 	0+NO!"7"GHO[+/(	0 $#rB   c                     d}|:| j                  d|f       | j                         d   }||j                  d      k7  }|S )NFz$SELECT %s::timestamptz exp_timestampexp_timestamprolvaliduntil)rJ   rg   get)rL   rR   r   expires_changingexpires_with_tzs        rC   need_to_change_role_expirationr   d  sO    =zJ //+O< +.@.D.D_.UUrB   c                     | d uxr | |d   k7  S )NrolconnlimitrA   )rY   r   s     rC   need_to_change_conn_limitr   q  s    d"Wz5G5W'WXrB   c                 &   d}|i }	 |j                  ||       t        j                  |       d}|S # t        j                  $ ru}|j
                  j                  dk(  rBd}| j                  |j
                  j                  t        j                                |cY d }~S t        j                  |      d }~wt        j                  $ rD}| j                  |j
                  j                  t        j                                Y d }~|S d }~ww xY w)NFT25006r   	exception)rJ   r_   r]   r   InternalErrordiagsqlstater   message_primary	traceback
format_excNotSupportedError)rb   rL   	statementparamschangedr   s         rC   exec_alter_userr   u  s    G~Wy&)	* N    +66??g% G!7!79CWCWCYZN''**$$ WQVV33y?S?S?UVVNWs.   )3 DAB6D!B66D9DDc	                    d}	 | j                   di t        }
|dk(  r.||j                  d       n|dk7  r|j                  d       ny|s*|
|dk7  s||t        | ||
|      }t	        |
||||      }t        ||      }t        |
||      }t        ||      }|s|s|s|syddt        ||      iz  g}|rP|dk7  r(|j                  d	d
|iz         |j                  d       n|j                  d       |j                  |       n|r|j                  d|z         ||j                  d       ||j                  dd|iz         t        ||      }dj                  |      }t        ||
||      }	|	S |r{|dk7  rvt        | ||
|      }t        ||      }|syddt        ||      iz  g}|r|j                  d|z         dj                  |      }t        ||
|      }	t        | ||
|      }||k7  }	|	S )zPChange user password and/or attributes. Return True if changed, False otherwise.FrH   z*cannot change the password for PUBLIC userr   rS   z1cannot change the role_attr_flags for PUBLIC userzALTER USER %(user)srI   rT   rU   rV   zWITH PASSWORD NULLzWITH %srW   rX   rY   rP   rZ   rA   )rL   r   r   r   r   r   r   r   r\   r]   r[   r^   r   )r   rb   rI   rQ   r`   ra   rR   no_password_changesrY   r   rL   r   r   r   r   conn_limit_changingalterrc   r   new_role_attrss                       rC   
user_alterr     se   G!]!!3N3Fx!MN"!TU H$8Or<QU\Uhlv  mC+M664P 4F<NPTV^`ij
 $B/Se#f  :&'K]^ 8
DVW ":CS\o '&.v2N)OOP2~-)0DDE4512LL)LL_45LL23!LL:lJ=WWX"HgFHHUO	!&&)=PQ6 N3 
B!6+M664P $B/Se#f ' '.v678 9LL_45HHUO	!&&)< (vvtL %6NrB   c                    | j                  d       	 dt        ||      z  }t        j                  |       | j                  |       | j                  d       y# t        $ r% | j                  d       | j                  d       Y yw xY w)z@Try to remove a user. Returns True if successful otherwise Falsez#SAVEPOINT ansible_pgsql_user_deletezDROP USER %sz/ROLLBACK TO SAVEPOINT ansible_pgsql_user_deletez+RELEASE SAVEPOINT ansible_pgsql_user_deleteFT)rJ   r\   r_   r]   r   )rL   rI   rb   rM   s       rC   user_deleter     s    
NN89f!==&u NN@A  HIDEs   5A +BBc                     t        | ||      }|j                  |      }|j                  |      }|j                  |      }|||fS aD  
    Return the difference between the privileges that a user already has and
    the privileges that they desire to have.

    :returns: tuple of:
        * privileges that they have and were requested
        * privileges they currently hold but were not requested
        * privileges requested that they do not hold
    )get_table_privilegesintersection
difference)rL   rI   r2   privs	cur_privshave_currentlyother_currentdesireds           rC   has_table_privilegesr     sO     %VT59I++E2N((/My)GM733rB   c                     d|v r|j                  dd      \  }}nd}d}| j                  ||||d       t        | j                         D cg c]  }|d   	 c}      S c c}w )N.rl   publiczSELECT privilege_type FROM information_schema.role_table_grants WHERE grantee=%(user)s AND table_name=%(table)s AND table_schema=%(schema)s)rI   r2   schemaprivilege_type)r   rJ   	frozensetfetchall)rL   rI   r2   r   rM   xs         rC   r   r     se    
e|C+[E
NN54%6JK6??3DEaa()EFFEs   Ac                     dj                  |      }d|dt        |d      d|d}t        j                  |       | j	                  |       y )N, GRANT 
 ON TABLE r2    TO ""r^   r   r_   r]   rJ   rL   rI   r2   r   rM   s        rC   grant_table_privilegesr     @    IIeE"5'2D:EE"
NN5rB   c                     dj                  |      }d|dt        |d      d|d}t        j                  |       | j	                  |       y )Nr   REVOKE r   r2    FROM "r   r   r   s        rC   revoke_table_privilegesr     r   rB   c                 @   dddd}d}| j                  ||f       | j                         d   }|
t               S t        j                  d|z  |      }|
t               S t               }|j                  d      D ]  }|j                  ||           t        |d	      S )
Nr-   r/   r.   )CTcz7SELECT datacl::text FROM pg_database WHERE datname = %sdataclz%s\\?"?=(C?T?c?)/[^,]+,?rl   r3   )rJ   rg   setru   searchrz   addnormalize_privileges)	rL   rI   dbpriv_maprM   r   r   ovs	            rC   get_database_privilegesr   '  s    H
 FE
NN52% __x(F~u
		-4f=AyuAWWQZ 	hqk:..rB   c                     t        | ||      }|j                  |      }|j                  |      }|j                  |      }|||fS r   )r   r   r   )rL   rI   r   r   r   r   r   r   s           rC   has_database_privilegesr   <  sO     (b9I++E2N((/My)GM733rB   c                     dj                  |      }|dk(  rd|dt        |d      d}nd|dt        |d      d|d}t        j                  |       | j	                  |       y )	Nr   rH   r    ON DATABASE r3   z
 TO PUBLICr   r   r   rL   rI   r   r   rM   s        rC   grant_database_privilegesr   N  f    IIeEx*2z:< 
 *2z:DB E"
NN5rB   c                     dj                  |      }|dk(  rd|dt        |d      d}nd|dt        |d      d|d}t        j                  |       | j	                  |       y )	Nr   rH   r   r   r3   z FROM PUBLICr   r   r   r   s        rC   revoke_database_privilegesr  ]  r   rB   c                     |yt        t        t              }t        t        t              }d}|D ]<  }t        ||         D ])  \  }} ||   | |||      }	|	d   s ||   | |||       d}+ > |S )NFr1   r   T)r[   r   r  r   r   r   )
rL   rI   r   revoke_funcscheck_funcsr   type_name
privilegesdifferencess
             rC   revoke_privilegesr
  l  s    }5!;=L1 79K G  )%, 7 	D* -+e,VT4LK1~#U#FD$
C	 NrB   c                     |yt        t        t              }t        t        t              }d}|D ]<  }t        ||         D ])  \  }} ||   | |||      }	|	d   s ||   | |||       d}+ > |S )NFr1   rm   T)r[   r   r   r   r   r   )
rL   rI   r   grant_funcsr  r   r  r  r  r	  s
             rC   grant_privilegesr    s    }3 9;K1 79K G  )%, 7 	D* -+e,VT4LK1~"E"64zB	 NrB   c                    t        d | j                  d      D              }t        t        j                  t        t        |                  }t        t        j                  |d |D                    }|j                  |      s,t        ddj                  |j                  |            z        dj                  |      S )a  
    Parse role attributes string for user creation.
    Format:

        attributes[,attributes,...]

    Where:

        attributes := CREATEDB,CREATEROLE,NOSUPERUSER,...
        [ "[NO]SUPERUSER","[NO]CREATEROLE", "[NO]CREATEDB",
                            "[NO]INHERIT", "[NO]LOGIN", "[NO]REPLICATION",
                            "[NO]BYPASSRLS" ]

    Note: "[NO]BYPASSRLS" role attribute introduced in 9.5
    Note: "[NO]CREATEUSER" role attribute is deprecated.

    c              3   B   K   | ]  }|s|j                           y wN)upper).0roles     rC   	<genexpr>z#parse_role_attrs.<locals>.<genexpr>  s     RtTdjjlRs   ,c              3   &   K   | ]	  }d |z    yw)zNO%sNrA   )r  flags     rC   r  z#parse_role_attrs.<locals>.<genexpr>  s     9`D&4-9`s   z%Invalid role_attr_flags specified: %srZ   )
r   r   	itertoolschainFLAGSget_valid_flags_by_versionissubsetr<   r^   r   )r`   srv_versionflagsvalid_flagss       rC   parse_role_attrsr     s    $ R/D/DS/IRREIOOE3Mk3Z[\KIOOK9`T_9`abK>>+& G #)9)9+)F G!H I 	I 88E?rB   c                     t        |       }d|v r)|j                  t        |          |j                  d       d|v r"|j	                  d       |j                  d       |S )Nr,   r0   r/   )r   r}   VALID_PRIVSremover   )r   r  	new_privss      rC   r   r     s\    E
I	U+,k" rB   c                    | | S i i d}| j                  d      D ]  }d|vr&d}|}t        d |j                  d      D              }n8d}|j                  dd      \  }}t        d	 |j                  d      D              }|j                  t        |         s6t	        d
|ddj                  |j                  t        |                     t        ||      }|||   |<    |S )a  
    Parse privilege string to determine permissions for database db.
    Format:

        privileges[/privileges/...]

    Where:

        privileges := DATABASE_PRIVILEGES[,DATABASE_PRIVILEGES,...] |
            TABLE_NAME:TABLE_PRIVILEGES[,TABLE_PRIVILEGES,...]
    )r3   r2   /:r3   c              3   x   K   | ]2  }|j                         r |j                         j                          4 y wr  stripr  r  r   s     rC   r  zparse_privs.<locals>.<genexpr>  s/      !I%&aggi "#!2 !I   8:r  r2   rl   c              3   x   K   | ]2  }|j                         r |j                         j                          4 y wr  r)  r+  s     rC   r  zparse_privs.<locals>.<genexpr>  s/      !N%&1779 "#!2 !Nr,  zInvalid privs specified for r   rZ   )r   r   r  r"  rF   r^   r   r   )r   r   o_privstokenr  r  priv_setr  s           rC   parse_privsr1    s    } G S! (eED  !I*/++c*:!I IH E${{32D*  !N*4*:*:3*?!N NH   U!34#%*CHHX5H5HUZI[5\,]%_ ` ` (%8't#(& NrB   c                 f    t         j                         D cg c]  \  }}| |k\  r| c}}S c c}}w )z
    Some role attributes were introduced after certain versions. We want to
    compile a list of valid flags against the current Postgres version.
    )FLAGS_BY_VERSIONr   )r  r  version_introduceds      rC   r  r    s;     )9(>(>(@$D$,, 	  s   -c                 ^    t        | d|      }||nd}||k7  rt        | |d||t               yy)zAdd comment on user.r  rS   TF)r   r   r_   )rL   rI   comment
check_modecurrent_comments        rC   add_commentr9    s=    !&&$7O)8)Do"O/!FGVT:?OPrB   c                     g }|j                         }| j                         D ].  \  }}||v r|||   k(  r||= ||vs|s|j                  |       0 ||dS )zlCompares two configurations and returns a list of values to reset as well as a dict of parameters to update.)resetr}   )copyr   r]   )currentr   reset_unspec_configr;  r}   keyvalues          rC   compare_user_configurationsrA    sj    E\\^F mmo 
U'>ews|3s$7LL f--rB   c                     |#	 t        d |      D ci c]  }|d   |d    c}S i S c c}w # t        $ r | j                  d       Y yw xY w)zbParses configuration from a list of 'key=value' strings like returned from the database to a dict.Nc                 &    | j                  dd      S )N=rl   )r   )ss    rC   <lambda>z*parse_user_configuration.<locals>.<lambda>  s    aggc1o rB   r   rl   zIExpecting a list of strings where each string has the format 'key=value'.r   )map
IndexErrorr   )rb   configsts      rC   parse_user_configurationrK    sh    	a(+,Ew(OP1AaD!A$JPP
 	 Q 	a_  a	as   - (- - A
Ac                    d}| j                  |d|i       | j                         }d}||j                  dd|iz         t        ||d         }	t	        |	||      }
	 |
d   D ]<  }d	t        ||      |d
z  }t        j                  |       | j                  |       d}> |
d   j                         D ]V  \  }}|rdt        ||      ||dz  }ndt        ||      ||dz  }t        j                  |       | j                  |       d}X 	 |S # t        j                  $ r"}|j                  d||dz         Y d}~|S d}~ww xY w)z9Updates the user's configuration parameters if necessary.z8SELECT rolconfig FROM pg_roles WHERE rolname = %(user)s;rI   FNz&Can't find user %(user)s in 'pg_roles'r   	rolconfigr;  z$ALTER ROLE %(user)s RESET "%(key)s";)rI   r?  Tr}   z1ALTER ROLE %(user)s SET "%(key)s" TO '%(value)s';)rI   r?  r@  z/ALTER ROLE %(user)s SET "%(key)s" TO %(value)s;zCUnable to update configuration for '%(user)s' due to: %(exception)s)rI   r   )rJ   rg   r   rK  rA  r\   r_   r]   r   r   r   )rL   rb   rI   configurationr>  quote_valuescurrent_config_querycurrent_configr   current_config_dictconfig_updatesitemrM   r?  r@  r   s                   rC   user_configurationrU  %  s   U
NN'&$8__&NGEQUVW26>+;VW01DmUhiN9 #7+ 	D:nUY[aFbko=ppE##E*NN5!G		
 )288: 		JCN"0v">sUZ[\ K"0v">sUZ[\##E*NN5!G		 N ## 9^"&Q78 	9 	9N9s   B0D E%EEc                     | d   dk7  r| d   dk7  rt        d| z  d      S | d   dk(  r| d   dk7  s| d   dk7  r| d   dk(  r|j                  d       yt        | d      S )zgcorrectly escape users, pg_quote_identifiers will fail if the user contains a dot but is not pre-quotedr   r   z"%s"r  zpThe value of the user-field can't contain a double-quote in the end if it doesn't start with one and vice-versa.N)r   r   )rI   rb   s     rC   r\   r\   J  sv    Aw#~$r(c/"6D=&99
q'S.T"X_$q'S.T"XQT_ H 	I
 #400rB   c                     t               } | j                  t        dddg      t        dd d      t        ddddg      t        dd d	d
      t        dddgdd	ddg      t        dddg      t        dd      t        dd      t        ddd      t        dd       t        dd       t        d      t        dd       t        dd      t        di       t        dd      t        dd             t        | d      }|j                  d   }|j                  d   }|j                  d   }|j                  d   }|j                  d   dk(  r!|j                  d    |j                  d!"       t        |j                  d    |j                  d         }|j                  d#   }|j                  d$   rd%}nd&}|j                  d'   }	|j                  d(   }
|j                  d)   }|j                  d*   }|j                  d+   }|j                  d,   }|j                  d-   }|j                  d.   }|j                  d/   }|st        |||||	|||||
       t        |       t        ||j                  d0      }t        ||      \  }} |j                  d>i t        }t        |      }|rF|j                         D ]3  \  }}d1|v sd2|v r|j                  d3       |j                  d2d4      ||<   5 	 t!        ||      }t        |6      }d}d}|dk(  rzt+        ||      r	 t-        |||||||	||
	      }n	 t1        ||||||	|
|      }	 t7        |||      xs |}|	 t9        ||||j:                        xs |}t?        ||||||      xs |}n^t+        ||      rR|j:                  rd}d|d9<   n>	 tA        |||      }tC        |||      }|xs |}|r|sd:}|j                  |"       ||d9<   |j:                  r|jE                          n|jG                          |jI                          |jI                          ||d;<   tJ        |d<<   tL        r	tL        |d=<    |jN                  d>i | y # t"        $ r9}|j                  t%        |      t'        j(                         5       Y d }~d }~ww xY w# t.        $ r9}|j                  t%        |      t'        j(                         5       Y d }~d }~ww xY w# t2        j4                  $ r<}|j                  d7t%        |      z  t'        j(                         5       Y d }~d }~wt.        $ r9}|j                  t%        |      t'        j(                         5       Y d }~5d }~ww xY w# t.        $ r9}|j                  t%        |      t'        j(                         5       Y d }~hd }~ww xY w# t<        $ r<}|j                  d8t%        |      z  t'        j(                         5       Y d }~d }~ww xY w# t.        $ r9}|j                  t%        |      t'        j(                         5       Y d }~d }~ww xY w)?NstrTr  )typerequiredaliases)rZ  defaultno_logpresentabsent)rZ  r]  choicesz4.0.0zcommunity.postgreql)rZ  r]  removed_in_versionremoved_from_collectionrS   r   zcommunity.postgresql)r  versioncollection_name)rZ  r]  r\  deprecated_aliasesboolfail_on_role)rZ  r]  r\  )rZ  r]  Fry   )rZ  r[   )rI   rQ   stateprivlogin_dbfail_on_userr`   ra   r   rR   rY   session_roler6  trust_inputrN  reset_unspecified_configurationquote_configuration_values)argument_specsupports_check_moderI   rQ   ri  rl  rk  rj  z-privileges require a database to be specifiedr   r   ra   rq   rp   rR   rY   r`   r6  rm  rN  ro  rp  rn  )warn_db_defaultr   'zBThe key of a configuration may not contain single or double quotesz''r   )rI   z5Unable to add user with given requirement due to : %sz!Unable to add comment on role: %suser_removedzUnable to remove userr   queries
debug_inforA   )(r   r}   r[   r   r   r   r1  r   r   r   r   rL   r   r   r   r   r   r<   r
   r   r   rN   r   r   rd   r   r   r  r9  r7  r   rU  r
  r   r   commitcloser_   rw  	exit_json)rq  rb   rI   rQ   ri  rl  r   r   ra   rR   rY   r`   r6  rm  rN  r>  rp  rn  conn_paramsr   dummyrL   r  r?  r@  r   kwr   ru  r   s                                 rC   mainr~  \  sh   13MutfX>5$t<y8Y:OPudw`uv5"tf"#9R 
 vtn=MN%4FD1 feEJ%.UD1u%%.fd33(,&%(H#'VT#B/  2 # F
 == D}}Z(HMM'"E==0L}}Z B&6==+@+LLMf-v}}Z/HIE --(=>}}[!	!	mmI&G|,Jmm$56OmmI&G==0LMM/2M --(IJ!'/K!L--.KFD(E7#WlG]	T  !&&--OK(=M5!]!!3N3F$]3K "'--/ 	:JCczTS[  !ef!&sD!9M#	:
M*?KH 
4BGL	vt$U$]FD(%4iJ]_ik
U"64#2Iw
TZ\	Q&vtU;FwG C%fdGV=N=NOZSZ %VVT=J]%?A LDK 	 vt$  %)>"Y/eDG#.vtV#DL "1\1C$$$-%1>" 
LLNByM$ByM%<FrO  MYq\Y5I5I5KLLM ! U  Yq\Y=Q=Q=S TTU ++ C   &35>q\&B+4+?+?+A ! C C ! U  Yq\Y=Q=Q=S TTU
  	Q19M9M9OPP	Q  C  %H9UV<%W+4+?+?+A ! C CC$ % Y$$1AUAUAW$XXYs   8Q. &R3 ;S8 V
 "W 4X .	R07.R++R03	S5<.S00S58V1UV.VV
	W.WW	X1XX	Y .YY__main__r  )_
__future__r   r   r   rZ  __metaclass__DOCUMENTATIONEXAMPLESRETURNr{   r  ru   r   base64r   hashlibr   r   ansible.module_utils._textr	   r
   r   ansible.module_utils.basicr   ansible.module_utils.sixr   =ansible_collections.community.postgresql.plugins.module_utilsr   Fansible_collections.community.postgresql.plugins.module_utils.databaser   r   r   Fansible_collections.community.postgresql.plugins.module_utils.postgresr   r   r   r   r   r   r   r   r   r   Eansible_collections.community.postgresql.plugins.module_utils.versionr   psycopg2r   r   rx   ImportErrorr  r3  rw   r[   r   r"  r   r_   rw  r   r<   rF   rN   rd   rh   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r
  r  r   r   r1  r  r9  rA  rK  rU  r\   r~  r>   rA   rB   rC   <module>r     s   A @ZxkZ
   	    C C 4 . 
   ?\%%88 $L
 	S' h  #wx%HJ zo&3\Q^)9^U    
		 			 	*4
TnU<$*
Y6Qh"4$G/*4$,*>	&R		.(
"J1$Up zF ]  Ls   E EE