
    Vh!9                         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mZ d dlmZmZ d dlmZ  G d	 d
e      Z	 	 	 	 ddZd ZddZd Zd Zedk(  r e        yy)    )absolute_importdivisionprint_functiona  
module: ipa_user
author: Thomas Krahn (@Nosmoht)
short_description: Manage FreeIPA users
description:
  - Add, modify and delete user within IPA server.
attributes:
  check_mode:
    support: full
  diff_mode:
    support: none
options:
  displayname:
    description: Display name.
    type: str
  update_password:
    description:
      - Set password for a user.
    type: str
    default: 'always'
    choices: [always, on_create]
  givenname:
    description:
      - First name.
      - If user does not exist and O(state=present), the usage of O(givenname) is required.
    type: str
  krbpasswordexpiration:
    description:
      - Date at which the user password will expire.
      - In the format YYYYMMddHHmmss.
      - For example V(20180121182022) will expire on 21 January 2018 at 18:20:22.
    type: str
  loginshell:
    description: Login shell.
    type: str
  mail:
    description:
      - List of mail addresses assigned to the user.
      - If an empty list is passed all assigned email addresses will be deleted.
      - If None is passed email addresses will not be checked or changed.
    type: list
    elements: str
  password:
    description:
      - Password for a user.
      - Will not be set for an existing user unless O(update_password=always), which is the default.
    type: str
  sn:
    description:
      - Surname.
      - If user does not exist and O(state=present), the usage of O(sn) is required.
    type: str
  sshpubkey:
    description:
      - List of public SSH key.
      - If an empty list is passed all assigned public keys will be deleted.
      - If None is passed SSH public keys will not be checked or changed.
    type: list
    elements: str
  state:
    description: State to ensure.
    default: "present"
    choices: ["absent", "disabled", "enabled", "present"]
    type: str
  telephonenumber:
    description:
      - List of telephone numbers assigned to the user.
      - If an empty list is passed all assigned telephone numbers will be deleted.
      - If None is passed telephone numbers will not be checked or changed.
    type: list
    elements: str
  title:
    description: Title.
    type: str
  uid:
    description: Uid of the user.
    required: true
    aliases: ["name"]
    type: str
  uidnumber:
    description:
      - Account Settings UID/Posix User ID number.
    type: str
  gidnumber:
    description:
      - Posix Group ID.
    type: str
  homedirectory:
    description:
      - Default home directory of the user.
    type: str
    version_added: '0.2.0'
  userauthtype:
    description:
      - The authentication type to use for the user.
      - To remove all authentication types from the user, use an empty list V([]).
      - The choice V(idp) and V(passkey) has been added in community.general 8.1.0.
    choices: ["password", "radius", "otp", "pkinit", "hardened", "idp", "passkey"]
    type: list
    elements: str
    version_added: '1.2.0'
extends_documentation_fragment:
  - community.general.ipa.documentation
  - community.general.attributes

requirements:
  - base64
  - hashlib
a  
- name: Ensure pinky is present and always reset password
  community.general.ipa_user:
    name: pinky
    state: present
    krbpasswordexpiration: 20200119235959
    givenname: Pinky
    sn: Acme
    mail:
      - pinky@acme.com
    telephonenumber:
      - '+555123456'
    sshpubkey:
      - ssh-rsa ....
      - ssh-dsa ....
    uidnumber: '1001'
    gidnumber: '100'
    homedirectory: /home/pinky
    ipa_host: ipa.example.com
    ipa_user: admin
    ipa_pass: topsecret

- name: Ensure brain is absent
  community.general.ipa_user:
    name: brain
    state: absent
    ipa_host: ipa.example.com
    ipa_user: admin
    ipa_pass: topsecret

- name: Ensure pinky is present but don't reset password if already exists
  community.general.ipa_user:
    name: pinky
    state: present
    givenname: Pinky
    sn: Acme
    password: zounds
    ipa_host: ipa.example.com
    ipa_user: admin
    ipa_pass: topsecret
    update_password: on_create

- name: Ensure pinky is present and using one time password and RADIUS authentication
  community.general.ipa_user:
    name: pinky
    state: present
    userauthtype:
      - otp
      - radius
    ipa_host: ipa.example.com
    ipa_user: admin
    ipa_pass: topsecret
zS
user:
  description: User as returned by IPA API.
  returned: always
  type: dict
N)AnsibleModule)	IPAClientipa_argument_spec)	to_nativec                   B     e Zd Z fdZd Zd Zd Zd Zd Zd Z	 xZ
S )UserIPAClientc                 2    t         t        |   ||||       y N)superr   __init__)selfmodulehostportprotocol	__class__s        n/home/dcms/DCMS/lib/python3.12/site-packages/ansible_collections/community/general/plugins/modules/ipa_user.pyr   zUserIPAClient.__init__   s    mT+FD$I    c                 0    | j                  dd d|d      S )N	user_findT)alluidmethodnameitem
_post_jsonr   r   s     r   r   zUserIPAClient.user_find   s    k4X\C]^^r   c                 *    | j                  d||      S )Nuser_addr   r    r   r   r   s      r   r$   zUserIPAClient.user_add       jt$GGr   c                 *    | j                  d||      S )Nuser_modr   r    r%   s      r   r(   zUserIPAClient.user_mod   r&   r   c                 (    | j                  d|      S )Nuser_delr   r   r    r"   s     r   r*   zUserIPAClient.user_del   s    jt<<r   c                 (    | j                  d|      S )Nuser_disabler+   r    r"   s     r   r-   zUserIPAClient.user_disable   s    n4@@r   c                 (    | j                  d|      S )Nuser_enabler+   r    r"   s     r   r/   zUserIPAClient.user_enable   s    m$??r   )__name__
__module____qualname__r   r   r$   r(   r*   r-   r/   __classcell__)r   s   @r   r   r      s,    J_HH=A@r   r   c                     i }| | |d<   ||dz   |d<   |||d<   |||d<   |||d<   ||d<   |||d<   |||d	<   |||d
<   |	|	|d<   |
|
|d<   |||d<   |||d<   |||d<   |||d<   |S )NdisplaynameZkrbpasswordexpiration	givenname
loginshellmailnsaccountlocksnipasshpubkeytelephonenumbertitleuserpassword	gidnumber	uidnumberhomedirectoryipauserauthtype )r5   r8   r7   r9   r:   r;   r<   	sshpubkeyr>   r?   r@   rA   rB   rC   userauthtypeusers                   r   get_user_dictrI      s     D)]((=(C$%%['\V)D	~T
(^""1W+^%[%[  -_".Kr   c                     d}d|v rLd}d|v r|d   d   dd j                         dk(  rd}|d   D cg c]  }t        ||       c}|d<   |d   }|d= | j                  ||	      }||d= ||d<   |S c c}w )
a)  
        Return the keys of each dict whereas values are different. Unfortunately the IPA
        API returns everything as a list even if only a single value is possible.
        Therefore some more complexity is needed.
        The method will check if the value type of module_user.attr is not a list and
        create a list with that element if the same attribute in ipa_user is list. In this way I hope that the method
        must not be changed if the returned API dict is changed.
    :param ipa_user:
    :param module_user:
    :return:
    Nr=   md5sshpubkeyfpr      zSHA256:sha256)ipa_datamodule_data)upperget_ssh_key_fingerprintget_diff)clientipa_usermodule_userrF   	hash_algopubkeyresults          r   get_user_diffrZ      s     I$	H$-)@)CBQ)G)M)M)OS\)\ I_jky_z%{U[&=fi&P%{M"/	'__hK_HF &&/N#M &|s   A2c           
         | j                         j                  dd      }t        |      dk(  ry|d   }t        j                  |d   j                  d            }|dk(  r`t        j                  |      j                         }dj                  d t        |ddd   |ddd         D              j                         }nk|d	k(  rft        j                  t        j                  |      j                               j                  d      j!                  d
      }dj#                  |      }t        |      dk  rd|dS |d   }d|d|dS )a  
    Return the public key fingerprint of a given public SSH key
    in format "[fp] [comment] (ssh-rsa)" where fp is of the format:
    FB:0C:AC:0A:07:94:5B:CE:75:6E:63:32:13:AD:AD:D7
    for md5 or
    SHA256:[base64]
    for sha256
    Comments are assumed to be all characters past the second
    whitespace character in the sshpubkey string.
    :param ssh_key:
    :param hash_algo:
    :return:
    N   r      asciirK   :c              3   ,   K   | ]  \  }}||z     y wr   rE   ).0abs      r   	<genexpr>z*get_ssh_key_fingerprint.<locals>.<genexpr>2  s     ODAq!a%Os   rN   =zSHA256:{fp})fp   z () )stripsplitlenbase64	b64decodeencodehashlibrK   	hexdigestjoinziprQ   	b64encoderN   digestdecoderstripformat)ssh_keyrW   partskey_typekeyfp_plainkey_fpcomments           r   rR   rR     s%    MMO!!$*E
5zQQxH


58??73
4CE;;s#--/OC1x1~,NOOUUW	h	##GNN3$7$>$>$@AHHQXXY\]%%%2
5zA~"H--(%w99r   c                 <   | j                   d   }| j                   d   }|dk(  }t        | j                   j                  d      | j                   j                  d      | j                   j                  d      | j                   d   | j                   d   | j                   d	   | j                   d
   || j                   d   | j                   d   | j                   d   | j                   j                  d      | j                   j                  d      | j                   j                  d      | j                   j                  d            }| j                   j                  d      }|j                  |      }d}|dv r~|s%d}| j                  sj|j                  ||      }||fS |dk(  r|j                  dd        t        |||      }	t        |	      dkD  r!d}| j                  s|j                  ||      }||fS |rd}| j                  s|j                  |       ||fS )Nstater   disabledr5   r7   r8   r9   r:   r<   rF   r>   r?   passwordrA   rB   rC   rG   )r5   r7   r8   r9   r:   r<   rF   r;   r>   r?   r@   rA   rB   rC   rG   update_password)r   F)presentenabledr   T)r   r   	on_creater@   r   )paramsrI   getr   
check_moder$   poprZ   rl   r(   r*   )
r   rT   r   r   r;   rV   r   rU   changeddiffs
             r   ensurer   =  s   MM'"E==DZ'MFMM,=,=m,L6<mm6G6GH_6`*0--*;*;K*H+1==+F%+]]6%:v}}T?R*0--*DTa06>O0PX^XeXefmXn-3]]:-F*0--*;*;K*HTZTaTaTeTefqTr.4mm.?.?.P-3]]->->~-N
PK mm''(9:OT*HG22G$$!??;?G H +-5 ;?D4y1}((%D{KH H G$$%Hr   c                  $   t               } | j                  t        d      t        d      t        ddddgd      t        dd      t        d      t        dd	      t        d      t        dd
dg      t        d      t        d      t        dd
      t        dd	      t        ddg d      t        dd	      t        d      t        d      t        ddg d             t        | d
      }t	        ||j
                  d   |j
                  d   |j
                  d         }|j
                  d   ?t        |j
                  d         dk(  r$|j
                  d   d   dk(  rd |j
                  d<   	 |j                  |j
                  d   |j
                  d          t        ||      \  }}|j                  ||       y # t        $ r8}|j                  t        |      t        j                                 Y d }~y d }~ww xY w)!Nstr)typealwaysr   F)r   defaultchoicesno_log)r   r   list)r   elementsTr   )r   requiredaliasesr   )r   absentr   r   )r   r   r   )r   radiusotppkinithardenedidppasskey)r   r   r   )r5   r8   r   r7   r9   r:   r<   r   rA   rB   r   rF   r   r>   r?   rC   rG   )argument_specsupports_check_modeipa_hostipa_portipa_prot)r   r   r   r   rF   r]   r    rU   ipa_pass)usernamer   )r   rH   )msg	exception)r   updatedictr   r   r   rl   loginr   	exit_json	Exception	fail_jsonr	   	traceback
format_exc)r   r   rT   r   rH   es         r   mainr   h  s   %'MTu%5#'U#3)-5(7?6M5:*< 04u/M$(e$4"? e,!utfXN#'U#3#'U#3"&E$"?#'Ve#D#	,X Z)-6E)J#/'+'7&*3x'z'  {, /35F & &j 9 &j 9$*MM*$=?F }}[!-v}}[)*a/FMM+4Nq4QUW4W)-FMM+&MfmmJ7$mmJ7 	 	9vv.t4 MYq\Y5I5I5KLLMs   >AG 	H.H

H__main__)NNNNNFNNNNNNNNN)rN   )
__future__r   r   r   r   __metaclass__DOCUMENTATIONEXAMPLESRETURNrm   rp   r   ansible.module_utils.basicr   >ansible_collections.community.general.plugins.module_utils.ipar   r   +ansible.module_utils.common.text.convertersr	   r   rI   rZ   rR   r   r   r0   rE   r   r   <module>r      s    A @l\4l
    4 g A@I @. \`[__c##LB:B(V-M` zF r   