3
 a                 @   s  d dl 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 d dlm	Z	 d dl
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 d dlmZ d dlmZmZ y d dlmZmZmZmZmZ W n ek
r   Y nX dZ dZ!dZ"dZ#dZ$dZ%dZ&dZ'G dd dej(Z)G dd dej*Z+G dd dZ,G dd dZ-G dd dZ.d d!d"d#Z/d$d%d&d'd(d)Z0ee1dd*d+d,Z2e.d d-d.d/d0Z3e-d d-d1d2d3Z4e1d-d4d5d6Z5e,d e,d7d8d9Z6d:d; Z7d<e8e8e1d=d>d?Z9e1ed@dAdBZ:e1ee;dCdDdEZ<edFdGe8e8dHdIdJdKZ=dLe1dMdNdOZ>ee1d-d dPddQdRdSZ?dd!dTdUZ@ee1e;dVdWdXZAee;dYdZd[ZBee1e;d\d]d^ZCe1ee;dCd_d`ZDee;dYdadbZEee;dYdcddZFedLe1e;dedfdgZGe1e1e;dhdidjZHdS )k    N)defaultdict)datetime)apt)UAConfig)CLOUD_TYPE_TO_TITLE
PRO_CLOUDSget_cloud_type)
exceptions)status)serviceclient)util)ENTITLEMENT_CLASS_BY_NAME)BASE_UA_URLPRINT_WRAP_WIDTH)AnyDictListOptionalTuplez=((CVE|cve)-\d{4}-\d{4,7}$|(USN|usn|LSN|lsn)-\d{1,5}-\d{1,2}$)z	cves.jsonzcves/{cve}.jsonznotices.jsonznotices/{notice}.jsonzUbuntu standard updateszUA InfrazUA Appsc                   s:   e Zd Z fddZdd Zd
ddZ fdd	Z  ZS )SecurityAPIErrorc                s*   t  j||j|j|j |jdd| _d S )Nmessage )super__init__codeheadersurlgetr   )selfeZerror_response)	__class__ 3/usr/lib/python3/dist-packages/uaclient/security.pyr   .   s    zSecurityAPIError.__init__c             C   s   t || jkS )N)boolr   )r   Z
error_coder!   r!   r"   __contains__2   s    zSecurityAPIError.__contains__Nc             C   s   || j kr| j S |S )N)r   )r   Z	error_strdefaultr!   r!   r"   __get__5   s    
zSecurityAPIError.__get__c                sD   t  j }| jg}|r2|d | j d dj| S |d | j d S )Nz: [z] z, ])r   __str__r   r   join)r   prefixdetails)r    r!   r"   r(   :   s
    
zSecurityAPIError.__str__)N)__name__
__module____qualname__r   r$   r&   r(   __classcell__r!   r!   )r    r"   r   -   s   
r   c                   s   e Zd ZdZdZeZdddddZej	e
jddd	gd
d! fdd	Zd"dddddddddd	ddZeddddZd#dddddddddZedddd Z  ZS )$UASecurityClient   Zsecurity_urlzDict[str, Any])query_paramsreturnc             C   s.   | j j jdi jdi }|r*|j| |S |S )zD
        Update query params with data from feature config.
        Zfeaturesextra_security_params)cfgr   update)r   r2   r4   r!   r!   r"   _get_query_paramsH   s    
z"UASecurityClient._get_query_params         )Zretry_sleepsNc                s    | j |}t j|||||dS )N)pathdatar   methodr2   )r7   r   request_url)r   r;   r<   r   r=   r2   )r    r!   r"   r>   X   s    
zUASecurityClient.request_urlzOptional[str]zOptional[int]zOptional[List[str]]z	List[CVE])	queryprioritypackagelimitoffset	componentversionr
   r3   c	          	      s:   ||||||||d}	 j t|	d\}
} fdd|
D S )znQuery to match multiple-CVEs.

        @return: List of CVE instances based on the the JSON response.
        )qr@   rA   rB   rC   rD   rE   r
   )r2   c                s   g | ]}t  |d qS ))clientresponse)CVE).0Zcve_md)r   r!   r"   
<listcomp>   s    z-UASecurityClient.get_cves.<locals>.<listcomp>)r>   API_V1_CVES)r   r?   r@   rA   rB   rC   rD   rE   r
   r2   Zcves_response_headersr!   )r   r"   get_cvese   s    zUASecurityClient.get_cvesrI   )cve_idr3   c             C   s"   | j tj|d\}}t| |dS )zkQuery to match single-CVE.

        @return: CVE instance for JSON response from the Security API.
        )cve)rG   rH   )r>   API_V1_CVE_TMPLformatrI   )r   rO   cve_responserM   r!   r!   r"   get_cve   s    zUASecurityClient.get_cvez	List[USN])r+   releaserB   rC   orderr3   c       	         sJ    ||||d}j t|d\}}t fdd|jdg D dd dS )	zuQuery to match multiple-USNs.

        @return: Sorted list of USN instances based on the the JSON response.
        )r+   rU   rB   rC   rV   )r2   c                s0   g | ](} d ks  |j dg krt|dqS )Ncves_ids)rG   rH   )r   USN)rJ   Zusn_md)r+   r   r!   r"   rK      s   z0UASecurityClient.get_notices.<locals>.<listcomp>noticesc             S   s   | j S )N)id)xr!   r!   r"   <lambda>   s    z.UASecurityClient.get_notices.<locals>.<lambda>)key)r>   API_V1_NOTICESsortedr   )	r   r+   rU   rB   rC   rV   r2   Zusns_responserM   r!   )r+   r   r"   get_notices   s    zUASecurityClient.get_noticesrX   )	notice_idr3   c             C   s"   | j tj|d\}}t| |dS )zbQuery to match single-USN.

        @return: USN instance representing the JSON response.
        )notice)rG   rH   )r>   API_V1_NOTICE_TMPLrR   rX   )r   ra   Znotice_responserM   r!   r!   r"   
get_notice   s    zUASecurityClient.get_notice)NNNN)NNNNNNNN)NNNNN)r,   r-   r.   Zurl_timeoutZcfg_url_base_attrr   Zapi_error_clsr7   r   ZretrysocketZtimeoutr>   rN   strrT   r`   rd   r/   r!   r!   )r    r"   r0   B   s0              r0   c               @   s   e Zd ZdZddddZedd Zedd	 Zed
d Zedd Z	edd Z
edd ZeedddZedd ZdS )CVEPackageStatuszAClass representing specific CVE PackageStatus on an Ubuntu serieszDict[str, Any])rS   c             C   s
   || _ d S )N)rH   )r   rS   r!   r!   r"   r      s    zCVEPackageStatus.__init__c             C   s
   | j d S )Ndescription)rH   )r   r!   r!   r"   rh      s    zCVEPackageStatus.descriptionc             C   s   | j S )N)rh   )r   r!   r!   r"   fixed_version   s    zCVEPackageStatus.fixed_versionc             C   s
   | j d S )Npocket)rH   )r   r!   r!   r"   rj      s    zCVEPackageStatus.pocketc             C   s
   | j d S )Nrelease_codename)rH   )r   r!   r!   r"   rk      s    z!CVEPackageStatus.release_codenamec             C   s
   | j d S )Nr
   )rH   )r   r!   r!   r"   r
      s    zCVEPackageStatus.statusc             C   sz   | j dkrdS | j dkrdS | j dkr*dS | j dkr8d	S | j d
krFdS | j dkrTdS | j dkrnt jj| jdS dj| j S )NZneededzSorry, no fix is available yet.zneeds-triagez7Ubuntu security engineers are investigating this issue.pendingz)A fix is coming soon. Try again tomorrow.ignoreddeferredzSorry, no fix is available.ZDNEz.Source package does not exist on this release.znot-affectedz/Source package is not affected on this release.released)Z
fix_streamzUNKNOWN: {})rm   rn   )r
   Z#MESSAGE_SECURITY_FIX_RELEASE_STREAMrR   pocket_source)r   r!   r!   r"   status_message   s     







zCVEPackageStatus.status_message)r3   c             C   s   t | jtkS )z>Return True if the package requires an active UA subscription.)r#   rp   UBUNTU_STANDARD_UPDATES_POCKET)r   r!   r!   r"   requires_ua   s    zCVEPackageStatus.requires_uac             C   sH   | j dkrt}n4| j dkr t}n$| j dkr0t}nd| jkr@t}nt}|S )z>Human-readable string representing where the fix is published.z	esm-infrazesm-appsupdatessecurityZesm)rt   ru   )rj   UA_INFRA_POCKETUA_APPS_POCKETrr   ri   )r   Z
fix_sourcer!   r!   r"   rp      s    



zCVEPackageStatus.pocket_sourceN)r,   r-   r.   __doc__r   propertyrh   ri   rj   rk   r
   rq   r#   rs   rp   r!   r!   r!   r"   rg      s   rg   c               @   s   e Zd ZdZeddddZedddZed	d
 Z	dd Z
eddddZeddddZedd ZeddddZdS )rI   z7Class representing CVE response from the SecurityClientzDict[str, Any])rG   rH   c             C   s   || _ || _d S )N)rH   rG   )r   rG   rH   r!   r!   r"   r      s    zCVE.__init__)r3   c             C   s   t |tsdS | j|jkS )NF)
isinstancerI   rH   )r   otherr!   r!   r"   __eq__  s    
z
CVE.__eq__c             C   s   | j jddj S )NrZ   ZUNKNOWN_CVE_ID)rH   r   upper)r   r!   r!   r"   rZ   	  s    zCVE.idc             C   sD   | j }x| jD ]}|j}P qW dj| j|ddj| jg}dj|S )z2Return a string representing the URL for this cve.z{issue}: {title})issuetitlezhttps://ubuntu.com/security/{}
)rh   rY   r   rR   rZ   r)   )r   r   rb   linesr!   r!   r"   get_url_header  s    zCVE.get_url_headerz	List[str]c             C   s   | j jdg S )Nnotices_ids)rH   r   )r   r!   r!   r"   r     s    zCVE.notices_idsz	List[USN]c                s<   t  ds6t fdd jjdg D dd dd _ jS )	zReturn a list of USN instances from API response 'notices'.

        Cache the value to avoid extra work on multiple calls.
        _noticesc                s   g | ]}t  j|qS r!   )rX   rG   )rJ   rb   )r   r!   r"   rK   '  s   zCVE.notices.<locals>.<listcomp>rY   c             S   s   | j S )N)rZ   )nr!   r!   r"   r\   *  s    zCVE.notices.<locals>.<lambda>T)r]   reverse)hasattrr_   rH   r   r   )r   r!   )r   r"   rY     s    


zCVE.noticesc             C   s   | j jdS )Nrh   )rH   r   )r   r!   r!   r"   rh   /  s    zCVE.descriptionzDict[str, CVEPackageStatus]c             C   sl   t | dr| jS i | _tj d }xB| jd D ]4}x.|d D ]"}|d |kr<t|| j|d < q<W q.W | jS )zDict of package status dicts for the current Ubuntu series.

        Top-level keys are source packages names and each value is a
        CVEPackageStatus object
        _packages_statusseriesZpackagesZstatusesrk   name)r   r   r   get_platform_inforH   rg   )r   r   rA   
pkg_statusr!   r!   r"   packages_status3  s    
zCVE.packages_statusN)r,   r-   r.   rx   r0   r   r#   r|   ry   rZ   r   r   rY   rh   r   r!   r!   r!   r"   rI      s   rI   c               @   s   e Zd ZdZeddddZedddZee	dd	d
Z
eddddZeddddZedd Zdd ZeddddZdS )rX   z7Class representing USN response from the SecurityClientzDict[str, Any])rG   rH   c             C   s   || _ || _d S )N)rH   rG   )r   rG   rH   r!   r!   r"   r   J  s    zUSN.__init__)r3   c             C   s   t |tsdS | j|jkS )NF)rz   rX   rH   )r   r{   r!   r!   r"   r|   N  s    
z
USN.__eq__c             C   s   | j jddj S )NrZ   ZUNKNOWN_USN_ID)rH   r   r}   )r   r!   r!   r"   rZ   S  s    zUSN.idz	List[str]c             C   s   | j jdg S )z$List of CVE IDs related to this USN.rW   )rH   r   )r   r!   r!   r"   rW   W  s    zUSN.cves_idsz	List[CVE]c                s<   t  ds6t fdd jjdg D dd dd _ jS )	zList of CVE instances based on API response 'cves' key.

        Cache the values to avoid extra work for multiple call-sites.
        _cvesc                s   g | ]}t  j|qS r!   )rI   rG   )rJ   rP   )r   r!   r"   rK   e  s   zUSN.cves.<locals>.<listcomp>cvesc             S   s   | j S )N)rZ   )r   r!   r!   r"   r\   h  s    zUSN.cves.<locals>.<lambda>T)r]   r   )r   r_   rH   r   r   )r   r!   )r   r"   r   \  s    


zUSN.cvesc             C   s   | j jdS )Nr   )rH   r   )r   r!   r!   r"   r   m  s    z	USN.titlec             C   s@   dj | j| jddg}x| jD ]}|jdj | qW dj|S )z5Return a string representing the URL for this notice.z{issue}: {title})r~   r   zFound CVEs:zhttps://ubuntu.com/security/{}r   )rR   rZ   r   rW   appendr)   )r   r   rP   r!   r!   r"   r   q  s
    zUSN.get_url_headerz$Dict[str, Dict[str, Dict[str, str]]]c             C   sX  t | dr| jS tj d }i | _x,| jjdi j|g D ]}|jdr|d | jkrd| j|d  krtjdj| j	|d d| j	d	|| j|d  d< nd|i| j|d < q<|jd
stjdj| j	|d d| j	d	n4d|d
 krtjdj| j	|d |d
 d| j	d	|d
 j
dd }|| jkr<i | j|< || j| |d < q<W | jS )aW  Binary package information available for this release.


        Reformat the USN.release_packages response to key it based on source
        package name and related binary package names.

        :return: Dict keyed by source package name. The second-level key will
            be binary package names generated from that source package and the
            values will be the dict response from USN.release_packages for
            that binary package. The binary metadata contains the following
            keys: name, version.
            Optional additional keys: pocket and component.
        _release_packagesr   release_packagesZ	is_sourcer   sourcez6{usn} metadata defines duplicate source packages {pkg})usnpkg)issue_idZsource_linkzL{issue} metadata does not define release_packages source_link for {bin_pkg}.)r~   bin_pkg/zX{issue} metadata has unexpected release_packages source_link value for {bin_pkg}: {link})r~   r   linkr8   )r   r   r   r   rH   r   r	   SecurityAPIMetadataErrorrR   rZ   split)r   r   r   source_pkg_namer!   r!   r"   r   {  s>    
 





zUSN.release_packagesN)r,   r-   r.   rx   r0   r   r#   r|   ry   rf   rZ   rW   r   r   r   r   r!   r!   r!   r"   rX   G  s   
rX   zDict[str, Dict[str, str]])r3   c        
      C   s   t j d } | dkrd}nd}t jdd| d dg\}}i }xV|j D ]J}|jd	\}}}}	|sf|}d
|	krpqH||kr||| |< qH||i||< qHW |S )zReturn a dict of all source packages installed on the system.

    The dict keys will be source package name: "krb5". The value will be a dict
    with keys binary_pkg and version.
    r   Ztrustyz	${Status}z${db:Status-Status}z
dpkg-queryz#-f=${Package},${Source},${Version},r   z-W,Z	installed)r   r   subp
splitlinesr   )
r   Zstatus_fieldoutZ_errinstalled_packagesZpkg_linepkg_namer   Zpkg_versionr
   r!   r!   r"   #query_installed_source_pkg_versions  s&    
r   z	List[USN]zDict[str, bool]z%Dict[str,  Dict[str, Dict[str, str]]])usnsbeta_pocketsr3   c                s   i }x| D ]}x|j j D ]\}} fdd|j D }||krN|rN|||< q||kr|| }xL|j D ]@\}}	||kr|	||< qh|| d }
|	d }t||
sh|	||< qhW qW q
W |S )a  Walk related USNs, merging the released binary package versions.

    For each USN, iterate over release_packages to collect released binary
        package names and required fix version. If multiple related USNs
        require differnt version fixes to the same binary package, track the
        maximum version required across all USNs.

    :param usns: List of USN response instances from which to calculate merge.
    :param beta_pockets: Dict keyed on service name: esm-infra, esm-apps
        the values of which will be true of USN response instances
        from which to calculate merge.

    :return: Dict keyed by source package name. Under each source package will
        be a dict with binary package name as keys and binary package metadata
        as the value.
    c                s.   i | ]&\}}d  j |j ddd kr||qS )Frj   None)r   )rJ   Zbin_pkg_nameZ
bin_pkg_md)r   r!   r"   
<dictcomp>  s   z>merge_usn_released_binary_package_versions.<locals>.<dictcomp>rE   )r   itemsversion_cmp_le)r   r   Zusn_pkg_versionsr   src_pkgZbinary_pkg_versionsZpublic_bin_pkg_versionsZusn_src_pkgr   Zbinary_pkg_mdZprev_versionZcurrent_versionr!   )r   r"   *merge_usn_released_binary_package_versions  s"    




r   )r5   r   r3   c             C   s
  |j  }t| d}t }td| td| d}d|kry|j|d}|j|d}W nN tk
r } z2t|}d|j krt	j
j|d	}tj|W Y d d }~X nX t||d
}	t|j  t||}
n,i }y`|j|d}x6|jD ],}x&|jD ]}||kr|j|d||< qW qW tt|j dd d}W nR tk
r~ } z4t|}d|j krdt	j
j|d	}tj|W Y d d }~X nX t||d}	t||}
t|j  ttjdd |D  }|stjdj||d	|jd stjdj||d	t| ||	||
d d S )N)r5   zesm-appsz	esm-infra)zesm-appsz	esm-infrarI   )rO   )r+   z	not found)r   )rP   r   )ra   c             S   s   | j S )N)rZ   )r[   r!   r!   r"   r\   1  s    z'fix_security_issue_id.<locals>.<lambda>)r]   )r   r   c             S   s   g | ]
}|j qS r!   )rW   )rJ   ur!   r!   r"   rK   @  s    z)fix_security_issue_id.<locals>.<listcomp>z${} metadata defines no related CVEs.r   z.{} metadata defines no fixed package versions.)r5   r   affected_pkg_statusr   usn_released_pkgs)r}   r0   r   _is_pocket_used_by_beta_servicerT   r`   r   rf   lowerr
   Z$MESSAGE_SECURITY_FIX_NOT_FOUND_ISSUErR   r	   ZUserFacingError'get_cve_affected_source_packages_statusprintr   r   rd   r   r   listr_   values get_usn_affected_packages_statusset	itertoolschainr   rH   prompt_for_affected_packages)r5   r   rG   r   r   rP   r   r   msgr   r   Zrelated_usnsr   Zrelated_usn_idZrelated_cvesr!   r!   r"   fix_security_issue_id
  sp    


r   zDict[str, CVEPackageStatus])r   r   r3   c             C   sd   i }xZ| j D ]P}xJt||j D ]8\}}||kr:|||< q || j}t||js |||< q W qW |S )zWalk CVEs related to a USN and return a dict of all affected packages.

    :return: Dict keyed on source package name, with active CVEPackageStatus
        for the current Ubuntu release.
    )r   r   r   ri   r   )r   r   Zaffected_pkgsrP   r   r   Zcurrent_verr!   r!   r"   r   X  s    

r   )rP   r   r3   c             C   s<   i }x2| j j D ]$\}}|jdkr$q||kr|||< qW |S )zGet a dict of any CVEPackageStatuses affecting this Ubuntu release.

    :return: Dict of active CVEPackageStatus keyed by source package names.
    znot-affected)r   r   r
   )rP   r   Zaffected_pkg_versionsZ
source_pkgZpackage_statusr!   r!   r"   r   n  s    
r   )r   r   c             C   s   t |}|dkr>ttjjdddd  ttjj| d dS |dkrLd	}nd}tjj||dd
 djt|j  }tt	j
|tdd dS )a	  Print header strings describing affected packages related to a CVE/USN.

    :param issue_id: String of USN or CVE issue id.
    :param affected_pkg_status: Dict keyed on source package name, with active
        CVEPackageStatus for the current Ubuntu release.
    r   ZNozs are)count
plural_str.)r~   Nr8   z isz: z, z    )widthsubsequent_indent)lenr   r
   ZMESSAGE_SECURITY_AFFECTED_PKGSrR   Z!MESSAGE_SECURITY_ISSUE_UNAFFECTEDr)   r_   keystextwrapfillr   )r   r   r   r   r   r!   r!   r"   print_affected_packages_header~  s    	&r   )r   usn_src_released_pkgsr3   c             C   sh   t j| }|rd|jdrdd|jd< |d d |jd< x.|j D ]"\}}|jd}|r>||jd< P q>W |S )a  Parse release status based on both pkg_status and USN.release_packages.

    Since some source packages in universe are not represented in
    CVEPackageStatus, rely on presence of such source packages in
    usn_src_released_pkgs to represent package as a "released" status.

    :param pkg_status: the CVEPackageStatus for this source package.
    :param usn_src_released_pkgs: The USN.release_packages representing only
       this source package. Normally, release_packages would have data on
       multiple source packages.

    :return: Tuple of:
        human-readable status message, boolean whether released,
        boolean whether the fix requires access to UA
    r   ro   r
   rE   rh   rj   )copydeepcopyr   rH   r   )r   r   usn_pkg_statusr   Zusn_released_pkgrj   r!   r!   r"   #override_usn_release_package_status  s    



r   c             C   sh   i }x^t | j D ]N\}}|j|i }t||}|jjdd}||krNg ||< || j||f qW |S )Nrm   rn   )r_   r   r   r   r
   replacer   )r   r   Zstatus_groupsr   r   usn_released_srcr   Zstatus_groupr!   r!   r"   group_by_usn_package_status  s    r   z"List[Tuple[str, CVEPackageStatus]])pkg_status_list	pkg_indexnum_pkgsr3   c             C   s   | sdS g }g }x4| D ],\}}|d7 }|j dj|| |j | qW tjdjddj| d djt|tdd	}d
j||jS )z;Format the packages and status to an user friendly message.r   r8   z{}/{}z{} {}:(z, )z    )r   r   z{}
{})r   rR   r   r   r)   r_   r   rq   )r   r   r   Z	msg_indexZsrc_pkgsr   r   Z
msg_headerr!   r!   r"   _format_packages_message  s    r   )rj   r5   c             C   s8   d}| t krd}n| tkrd}tj|}|r4||S d S )Nzno-service-neededz	esm-infrazesm-apps)rv   rw   r   r   )rj   r5   Zservice_to_checkZent_clsr!   r!   r"   _get_service_for_pocket  s    
r   )rj   r5   r3   c             C   s4   t | |}|r0|j \}}|tjjkr*dS |jS dS )zBCheck if the pocket where the fix is at belongs to a beta service.F)r   user_facing_statusr
   UserFacingStatusACTIVEZvalid_service)rj   r5   ent
ent_status_r!   r!   r"   r     s    
r   z-Dict[str, List[Tuple[str, CVEPackageStatus]]]zDict[str, List[str]]zTuple[bool, List[str], bool])r5   src_pocket_pkgsbinary_pocket_pkgsr   r   r3   c             C   s   d}d}g }|rxt ttgD ]z}|| }	|| }
|rt|	||d}|rdt| |
s`ttj qnd}|t|	7 }|t| |
|M }|s|dd |	D 7 }qW |||fS )a%  Handle the packages that could be fixed and have a released status.

    :returns: Tuple of
        boolean whether all packages were successfully upgraded,
        list of strings containing the packages that were not upgraded,
        boolean whether all packages were already installed
    T)r   r   r   Fc             S   s   g | ]\}}|qS r!   r!   )rJ   r   r   r!   r!   r"   rK   9  s    z2_handle_released_package_fixes.<locals>.<listcomp>)	rr   rv   rw   r   r   r
   Z!MESSAGE_SECURITY_UPDATE_INSTALLEDr   upgrade_packages_and_attach)r5   r   r   r   r   all_already_installedZupgrade_statusunfixed_pkgsrj   Zpkg_src_groupZbinary_pkgsr   r!   r!   r"   _handle_released_package_fixes  s6    

r   z	List[str])r   r3   c             C   sF   t | }tjdj||dkrdnd|dkr,dnddjt| tdd	S )
zFormat the list of unfixed packages into an message.

    :returns: A string containing the message output for the unfixed
              packages.
    z"{} package{} {} still affected: {}r8   sr   Zareisz, z    )r   r   )r   r   r   rR   r)   r_   r   )r   Znum_pkgs_unfixedr!   r!   r"   _format_unfixed_packages_msg>  s    r   z$Dict[str, Dict[str, Dict[str, str]]])r5   r   r   r   r   r3   c             C   s  t |}t|| |dkrdS tjj|d}tt}tt}d}	t||}
g }xt|
j	 D ]\}}|dkrtj
j|d}tt||	|d |	t |7 }	|dd |D 7 }q^x|D ]\}}||j j||f x|| j	 D ]\}}|j|i }||kr6|dd |D 7 }d	j||d
}|t|7 }tj|||| }|d }t||s||j j| qW qW q^W t| |||	|d\}}}||7 }|rtt| |r|rt| nHtj rtjjdd}t| | jd| ttj
j|d nt| nttj
j|d dS )zProcess security CVE dict returning a CVEStatus object.

    Since CVEs point to a USN if active, get_notice may be called to fill in
    CVE title details.
    r   N)r~   ro   )r   r   r   c             S   s   g | ]\}}|qS r!   r!   )rJ   r   r   r!   r!   r"   rK   x  s    z0prompt_for_affected_packages.<locals>.<listcomp>c             S   s   g | ]\}}|qS r!   r!   )rJ   r   r   r!   r!   r"   rK     s    z5{issue} metadata defines no fixed version for {pkg}.
)r   r~   rE   )r5   r   r   r   r   zfix operation)Z	operationr   )r   r   r
   ZMESSAGE_SECURITY_ISSUE_RESOLVEDrR   r   r   r   r_   r   Z#MESSAGE_SECURITY_ISSUE_NOT_RESOLVEDr   r   rp   r   r   r   r	   r   r   r   r   Zshould_rebootZ#MESSAGE_ENABLE_REBOOT_REQUIRED_TMPLZ
add_notice)r5   r   r   r   r   r   Zfix_messager   r   r   Zpkg_status_groupsr   Zstatus_valueZpkg_status_groupr   r   Z
binary_pkgrE   r   r   Z	fixed_pkgri   Z
fix_statusZunfixed_pkgs_releasedr   Z
reboot_msgr!   r!   r"   r   Q  s~    










r   c              C   s,   t  } | tkr(ttjjtj| | d dS )z9Alert the user when running UA on cloud with PRO support.)r   ZcloudN)r   r   r   r
   ZMESSAGE_SECURITY_USE_PRO_TMPLrR   r   r   )Z
cloud_typer!   r!   r"   *_inform_ubuntu_pro_existence_if_applicable  s
    r   )r5   tokenr3   c             C   sH   ddl }ddlm} ttjdd|gg td|j|j|dd| kS )zkAttach to a UA subscription with a given token.

    :return: True if attach performed without errors.
    r   N)cliuaZattachT)r   Zauto_enable)	argparseuaclientr   r   r
   colorize_commandsr#   Zaction_attach	Namespace)r5   r   r   r   r!   r!   r"   _run_ua_attach  s    r   )r5   r3   c             C   sr   t   ttj tjddddgd}|dkr0dS |dkrJttj td |d
krnttj td}t	| |S d	S )zZPrompt for attach to a subscription or token.

    :return: True if attach performed.
    zBChoose: [S]ubscribe at ubuntu.com [A]ttach existing token [C]ancelr   ac)valid_choicesFz*Hit [Enter] when subscription is complete.z> T)r   r   )
r   r   r
   Z2MESSAGE_SECURITY_UPDATE_NOT_INSTALLED_SUBSCRIPTIONr   prompt_choicesZPROMPT_UA_SUBSCRIPTION_URLinputZPROMPT_ENTER_TOKENr   )r5   choicer   r!   r!   r"   _prompt_for_attach  s    



r   )r5   servicer3   c             C   s   ddl }ddlm} ttjj|d tjdj|ddgd}|dkr~ttj	d	d
|gg t
d|j|j|gddd| kS dS )zLPrompt for enable a ua service.

    :return: True if enable performed.
    r   N)r   )r   zChoose: [E]nable {} [C]ancelr   r   )r   r   enableTF)r   
assume_yesZbeta)r   r   r   r   r
   Z!MESSAGE_SECURITY_SERVICE_DISABLEDrR   r   r   r   r#   Zaction_enabler   )r5   r   r   r   r   r!   r!   r"   _prompt_for_enable  s    
r   c             C   s   t | |}|r||j \}}|tjjkr*dS |j \}}|tjjkrht||j	rRdS t
tjj|j	d nt
tjj|j	d dS )z?Verify if the ua subscription has the required service enabled.T)r   F)r   r   r
   r   r   applicability_statusZApplicabilityStatusZ
APPLICABLEr   r   r   Z'MESSAGE_SECURITY_UA_SERVICE_NOT_ENABLEDrR   Z(MESSAGE_SECURITY_UA_SERVICE_NOT_ENTITLED)rj   r5   r   r   r   r   r!   r!   r"   (_check_subscription_for_required_service  s     
r   c             C   s   ddl }ddlm} t  ttj tjdj	t
ddgd}|dkrttj td}ttjd	d
gg |j|jdd|  t| |S dS )zdPrompt for attach a new subscription token to the user.

    :return: True if attach performed.
    r   N)r   z2Choose: [R]enew your subscription (at {}) [C]ancelrr   )r   z> r   detachT)r   F)r   r   r   r   r   r
   Z-MESSAGE_SECURITY_UPDATE_NOT_INSTALLED_EXPIREDr   r   rR   r   ZPROMPT_EXPIRED_ENTER_TOKENr   r   Zaction_detachr   r   )r5   r   r   r   r   r!   r!   r"   _prompt_for_new_token7  s    


r   c             C   s(   | j }|j}|tj|k r$t|  S dS )znCheck if user UA subscription is expired.

    :returns: True if subscription is expired and not renewed.
    F)contract_expiry_datetimetzinfor   Znowr   )r5   r   r   r!   r!   r"   _check_subscription_is_expiredQ  s
    
r   )r5   upgrade_packagesrj   r3   c             C   s   |sdS t j dkr"ttj dS |tkrX| js>t| sJdS nt| rJdS t	|| sXdS ttj
dddgdddd	g | g tjd
dgtjd tjd
ddd	g| tjddid dS )a   Upgrade available packages to fix a CVE.

    Upgrade all packages in upgrades_packages and, if necessary,
    prompt regarding system attach prior to upgrading UA packages.

    :return: True if package upgrade completed or unneeded, False otherwise.
    Tr   Fr   r6   z&&Zinstallz--only-upgradez-yzapt-get)cmd	error_msgZDEBIAN_FRONTENDZnoninteractive)r  r  env)osgetuidr   r
   ZMESSAGE_SECURITY_APT_NON_ROOTrr   Zis_attachedr   r   r   r   r   Zrun_apt_commandZMESSAGE_APT_UPDATE_FAILEDZMESSAGE_APT_INSTALL_FAILED)r5   r  rj   r!   r!   r"   r   ^  s.    


 r   )version1version2r3   c             C   s4   yt jdd| d|g dS  t jk
r.   dS X dS )z<Return True when version1 is less than or equal to version2.Zdpkgz--compare-versionsleTFN)r   r   ZProcessExecutionError)r  r  r!   r!   r"   r     s
    r   )Ir   r   r  re   r   collectionsr   r   r   r   Zuaclient.configr   Zuaclient.clouds.identityr   r   r   r	   r
   r   r   Zuaclient.entitlementsr   Zuaclient.defaultsr   r   Ztypingr   r   r   r   r   ImportErrorZCVE_OR_USN_REGEXrL   rQ   r^   rc   rr   rv   rw   ZUrlErrorr   ZUAServiceClientr0   rg   rI   rX   r   r   rf   r   r   r   r   r   r   intr   r   r#   r   r   r   r   r   r   r   r   r   r   r   r   r   r!   r!   r!   r"   <module>   s    vEJs"-O" 2n0