Skip to content

Commit

Permalink
Merge pull request #59 from 0x41424142/users
Browse files Browse the repository at this point in the history
  • Loading branch information
0x41424142 authored Aug 5, 2024
2 parents 3bbfcf9 + 0a40e65 commit 6cb1cdf
Show file tree
Hide file tree
Showing 37 changed files with 875 additions and 95 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ hosts = get_host_list(auth, details="All/AGs", show_tags=True, page_count=4)
|Module| Status |
|--|--|
| GAV (Global AssetView) ||
| VMDR | In Progress (```query_kb```, ```get_host_list```, ```get_hld```, ```get_ag_list```, ```add/edit/remove_ag```, ```get_ip_list```, ```add/update_ips```, ```get_scan_list```, ```pause_scan```, ```cancel_scan```, ```resume_scan```, ```delete_scan```, ```launch_scan```, ```fetch_scan```, ```get_scanner_list```, ```get_static_searchlists```, ```get_report_list```, ```launch/cancel/fetch/delete_report```, ```get_template_list```, ```get_scheduled_report_list```, ```launch_scheduled_report``` implemented) |
| VMDR | In Progress (```query_kb```, ```get_host_list```, ```get_hld```, ```get_ag_list```, ```add/edit/remove_ag```, ```get_ip_list```, ```add/update_ips```, ```get_scan_list```, ```pause_scan```, ```cancel_scan```, ```resume_scan```, ```delete_scan```, ```launch_scan```, ```fetch_scan```, ```get_scanner_list```, ```get_static_searchlists```, ```get_report_list```, ```launch/cancel/fetch/delete_report```, ```get_template_list```, ```get_scheduled_report_list```, ```launch_scheduled_report```, ```get_user_list```, ```add/edit_user``` implemented) |
| PM (Patch Management) | In Planning |
| WAS | Not Started |
| TC (TotalCloud) | Not Started |
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ hosts = get_host_list(auth, details="All/AGs", show_tags=True, page_count=4)
|Module| Status |
|--|--|
| GAV (Global AssetView) ||
| VMDR | In Progress (```query_kb```, ```get_host_list```, ```get_hld```, ```get_ag_list```, ```add/edit/remove_ag```, ```get_ip_list```, ```add/update_ips```, ```get_scan_list```, ```pause_scan```, ```cancel_scan```, ```resume_scan```, ```delete_scan```, ```launch_scan```, ```fetch_scan```, ```get_scanner_list```, ```get_static_searchlists```, ```get_report_list```, ```launch/cancel/fetch/delete_report```, ```get_template_list```, ```get_scheduled_report_list```, ```launch_scheduled_report``` implemented) |
| VMDR | In Progress (```query_kb```, ```get_host_list```, ```get_hld```, ```get_ag_list```, ```add/edit/remove_ag```, ```get_ip_list```, ```add/update_ips```, ```get_scan_list```, ```pause_scan```, ```cancel_scan```, ```resume_scan```, ```delete_scan```, ```launch_scan```, ```fetch_scan```, ```get_scanner_list```, ```get_static_searchlists```, ```get_report_list```, ```launch/cancel/fetch/delete_report```, ```get_template_list```, ```get_scheduled_report_list```, ```launch_scheduled_report```, ```get_user_list```, ```add/edit_user``` implemented) |
| PM (Patch Management) | In Planning |
| WAS | Not Started |
| TC (TotalCloud) | Not Started |
Expand Down
158 changes: 158 additions & 0 deletions docs/vmdr.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ You can use any of the VMDR endpoints currently supported:
|```get_scheduled_report_list```|Get a list of scheduled reports.|
|```launch_scheduled_report```|Launch a scheduled report.|
|```get_template_list```|Get a list of report templates.|
|```get_user_list```|Get a list of users in your subscription.|
|```add_user```|Add a new user to your subscription.|
|```edit_user```|Edit a user in your subscription.|

## Host List Detection

Expand Down Expand Up @@ -879,6 +882,161 @@ templates = get_template_list(auth)
>>>[ReportTemplate(ID=12345678, TYPE="Auto", ...)]
```

## User Management

This collection of APIs lets you work with user accounts in VMDR.

The APIs are as follows:

|API Call| Description|
|--|--|
|```get_user_list```| Get a ```BaseList``` of ```User``` objects.|
|```edit_user```|Edit a user account.|
|```add_user```|Add a new user account.|

### User Dataclass

The ```User``` dataclass is used to represent a single user account in VMDR. Attributes are as follows:

|Attribute|Type|Description|
|--|--|--|
|```USER_LOGIN```|```str```|The username of the user.|
|```USER_ID```|```int```|The ID number of the user.|
|```EXTERNAL_ID```|```str```|The external ID of the user.|
|```CONTACT_INFO```|```dict```|Contact information. Gets parsed out to below 14 fields.|
|```FIRSTNAME```|```str```|The first name of the user.|
|```LASTNAME```|```str```|The last name of the user.|
|```TITLE```|```str```|The title of the user.|
|```PHONE```|```str```|The phone number of the user.|
|```COUNTRY```|```str```|The country of the user.|
|```STATE```|```str```|The state of the user.|
|```CITY```|```str```|The city of the user.|
|```ZIP_CODE```|```str```|The ZIP code of the user.|
|```FAX```|```str```|The fax number of the user.|
|```EMAIL```|```str```|The email address of the user.|
|```COMPANY```|```str```|The company of the user.|
|```ADDRESS1```|```str```|The first line of the user's address.|
|```ADDRESS2```|```str```|The second line of the user's address.|
|```TIME_ZONE_CODE```|```str```|The time zone code of the user.|
|```USER_STATUS```|```str```|The status of the user.|
|```CREATION_DATE```|```datetime.datetime```|The date the user was created.|
|```USER_ROLE```|```dict```|The role of the user.|
|```LAST_LOGIN_DATE```|```datetime.datetime```|The last time the user logged in.|
|```BUSINESS_UNIT```|```str```|The business unit of the user.|
|```UNIT_MANAGER_POC```|```str```|The unit manager point of contact.|
|```MANAGER_POC```|```str```|The manager point of contact.|
|```UI_INTERFACE_STYLE```|```str```|The UI interface style of the user.|
|```PERMISSIONS```|```dict```|The permissions of the user. Gets parsed out to below 5 fields.|
|```CREATE_OPTION_PROFILES```|```bool```|If the user can create option profiles.|
|```PURGE_INFO```|```bool```|If the user can purge info.|
|```ADD_ASSETS```|```bool```|If the user can add assets.|
|```EDIT_REMEDIATION_POLICY```|```bool```|If the user can edit remediation policies.|
|```EDIT_AUTH_RECORDS```|```bool```|If the user can edit authentication records.|
|```CREATE_OPTION_PROFILES```|```bool```|If the user can create option profiles.|
|```NOTIFICATIONS```|```dict```|The notifications of the user. Gets parsed out to below 3 fields.|
|```LATEST_VULN```|```str```|How often the user gets vulnerability notifications.|
|```MAP```|```str```|How often the user gets map notifications.|
|```SCAN```|```str```|How often the user gets scan notifications.|
|```DAILY_TICKETS```|```int```|If the user gets daily ticket updates.|

### Get User List API

This API lets you pull a list of user accounts in your subscription, according to kwarg filters. Returns a ```BaseList``` of ```User``` objects.

Parameter| Possible Values |Description|Required|
|--|--|--|--|
|```auth```|```qualyspy.auth.BasicAuth```|The authentication object.||
|```external_id_contains```|```str```|Filter output to users with a specific external ID pattern.||
|```external_id_assigned```|```True/False```|Filter output to users with an external ID assigned.||

```py
from qualyspy.auth import BasicAuth
from qualyspy.vmdr import get_user_list

auth = BasicAuth(<username>, <password>, platform='qg1')

users = get_user_list(auth)
>>>[User(USER_ID=12345, USER_LOGIN='alice_123', ...), ...]
```

### Create User API

This API lets you create a new user account in VMDR. It returns a string with the Qualys response, or if the ```send_email``` kwarg is ```False```, the username and password of the new user.

Parameter| Possible Values |Description|Required|
|--|--|--|--|
|```auth```|```qualyspy.auth.BasicAuth```|The authentication object.||
|```user_role```|```Literal["manager", "unit_manager", "scanner", "reader", "contact", "administrator"]```|The role of the user.||
|```business_unit```|```Union[Literal["Unassigned"], str]```|The business unit of the user.||
|```first_name```|```str```|The first name of the user.||
|```last_name```|```str```|The last name of the user.||
|```title```|```str```|The title of the user.||
|```phone```|```str```|The phone number of the user.||
|```email```|```str```|The email address of the user.||
|```address1```|```str```|The first line of the user's address.|✅|
|```city```|```str```|The city of the user.||
|```state```|```str```|The state of the user. Must be the full state name, such as ```"Maryland"``` or ```"Pennsylvania"```.||
|```country```|```str```|The country of the user. Must be the full country name, such as ```"United States of America"```.||
|```send_email```|```True/False```|If ```True```, an email will be sent to the user with their login information. If ```False```, the username and password will be returned in the response. Defaults to ```True```.||
|```asset_groups```|```str```|A comma-separated string of asset groups to assign to the user.||
|```fax```|```str```|The fax number of the user - because fax is still very widely used nowadays. 😉||
|```address2```|```str```|The second line of the user's address.|❌|
|```zip_code```|```str```|The ZIP code of the user.||
|```external_id```|```str```|The external ID of the user.||

```py
from qualyspy.auth import BasicAuth
from qualyspy.vmdr import add_user

auth = BasicAuth(<username>, <password>, platform='qg1')

# Add a new user to VMDR and send them an email:
result = add_user(auth, user_role='manager', business_unit='Unassigned', first_name='Alice', last_name='Smith', title='Manager', phone='555-555-5555', ...)
>>>User alice_123 created successfully.

# Add a new user to VMDR and return their username and password:
result = add_user(auth, user_role='manager', business_unit='Unassigned', first_name='Alice', last_name='Smith', title='Manager', phone='555-555-5555', ..., send_email=False)
>>>User alice_123 created. User:Pass is: alice_123, Password: 12345
```

### Edit User API

This API lets you edit an existing user account in VMDR. It returns a string with the Qualys response. Certain fields can not be edited. If you try to edit one of these, the SDK will raise a ```QualysAPIError``` Exception. You can also clear/"wipe" certain fields by specifiying an empty string in the kwarg.

Parameter| Possible Values |Description|Required|
|--|--|--|--|
|```auth```|```qualyspy.auth.BasicAuth```|The authentication object.||
|```login```|```str```|The username of the user to edit.||
|```asset_groups```|```str```|A comma-separated string of asset groups to assign to the user.||
|```first_name```|```str```|The first name of the user.||
|```last_name```|```str```|The last name of the user.||
|```title```|```str```|The title of the user.||
|```phone```|```str```|The phone number of the user.||
|```fax```|```str```|The fax number of the user.||
|```email```|```str```|The email address of the user.||
|```address1```|```str```|The first line of the user's address.|❌|
|```address2```|```str```|The second line of the user's address.|❌|
|```city```|```str```|The city of the user.||
|```state```|```str```|The state of the user. Must be the full state name, such as ```"Maryland"``` or ```"Pennsylvania"```.||
|```country```|```str```|The country of the user. Must be the full country name, such as ```"United States of America"```.||
|```zip_code```|```str```|The ZIP code of the user.||
|```external_id```|```str```|The external ID of the user.||

```py
from qualyspy.auth import BasicAuth
from qualyspy.vmdr import edit_user

auth = BasicAuth(<username>, <password>, platform='qg1')

# Edit Alice's phone number:
result = edit_user(auth, login='alice_123', phone='555-555-5555')
>>>User alice_123 has been successfully updated.

# Clear Alice's phone number:
result = edit_user(auth, login='alice_123', phone='')
>>>User alice_123 has been successfully updated.
```

## Querying the KB
The Qualys KnowledgeBase (KB) is a collection of vulnerabilities that Qualys has identified. You can query the KB using the ```query_kb()``` function:

Expand Down
2 changes: 0 additions & 2 deletions qualyspy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@

from .vmdr import query_kb, get_host_list, get_hld

from .base.sql import make_engine, sql_upload


# surprise!
__surprise__ = b"\xe2\x9c\xa8\xe2\x9c\xa8\xe2\x9c\xa8 Have a great day!".decode("utf-8")
1 change: 0 additions & 1 deletion qualyspy/base/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,3 @@
from .call_api import call_api
from .call_schema import CALL_SCHEMA
from .xml_parser import xml_parser
from .sql import make_engine, sql_upload
66 changes: 66 additions & 0 deletions qualyspy/base/call_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,72 @@
"pagination": False,
"auth_type": "basic",
},
"get_user_list": {
"endpoint": "/msp/user_list.php",
"method": ["GET", "POST"],
"valid_params": ["external_id_contains", "external_id_assigned"],
"valid_POST_data": [],
"use_requests_json_data": False,
"return_type": "xml",
"pagination": False,
"auth_type": "basic",
},
"add_user": {
"endpoint": "/msp/user.php",
"method": ["GET", "POST"],
"valid_params": [
"action",
"user_role",
"business_unit",
"first_name",
"last_name",
"title",
"phone",
"email",
"address1",
"city",
"country",
"state",
"send_email",
"asset_groups",
"fax",
"address2",
"zip_code",
"external_id",
],
"valid_POST_data": [],
"use_requests_json_data": False,
"return_type": "xml",
"pagination": False,
"auth_type": "basic",
},
"edit_user": {
"endpoint": "/msp/user.php",
"method": ["GET", "POST"],
"valid_params": [
"action",
"login",
"asset_groups",
"first_name",
"last_name",
"title",
"phone",
"email",
"address1",
"address2",
"city",
"country",
"state",
"fax",
"zip_code",
"external_id",
],
"valid_POST_data": [],
"use_requests_json_data": False,
"return_type": "xml",
"pagination": False,
"auth_type": "basic",
},
},
}
)
5 changes: 3 additions & 2 deletions qualyspy/gav/count_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@
from ..exceptions.Exceptions import *


def count_assets(auth: TokenAuth, **kwargs):
def count_assets(auth: TokenAuth, **kwargs) -> dict:
"""
Count assets in the Global AssetView API based on a QQL filter.
Params:
auth (TokenAuth): The authentication object.
API params (kwargs):
:Kwargs:
filter (str): The Qualys QQL filter to use.
lastSeenAssetId (int): The last seen asset ID.
lastModifiedDate (str): The last modified date.
Expand Down
5 changes: 3 additions & 2 deletions qualyspy/gav/get_all_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@

def get_all_assets(
auth: TokenAuth, page_count: Union[int, "all"] = "all", **kwargs
) -> list:
) -> list[Host]:
"""
Get all assets in the Global AssetView API.
Params:
auth (TokenAuth): The authentication object.
page_count (Union[int, "all"]): The number of pages to get. If "all", get all pages. Defaults to "all".
API params (kwargs):
:Kwargs:
excludeFields (str): The fields to exclude.
includeFields (str): The fields to include.
lastSeenAssetId (int): The last seen asset ID. Used for automatic pagination.
Expand Down
7 changes: 4 additions & 3 deletions qualyspy/gav/get_asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,20 @@
from .hosts import Host


def get_asset(auth: TokenAuth, **kwargs):
def get_asset(auth: TokenAuth, **kwargs) -> Host:
"""
Get a specific host from the Global AssetView API.
Params:
auth (TokenAuth): The authentication object.
API params (kwargs):
:Kwargs:
assetId (int): The asset ID to get.
lastSeenAssetId (int): The last seen asset ID.
lastModifiedDate (str): The last modified date.
Returns:
dict: The response from the API.
Host: The Host object.
"""
# despite the fact that this is a POST request, we still need to send stuff as a parameter
# because Qualys is Qualys.
Expand Down
9 changes: 7 additions & 2 deletions qualyspy/gav/query_assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,26 @@
from .hosts import Host


def query_assets(auth: TokenAuth, page_count: Union["all", int] = "all", **kwargs):
def query_assets(
auth: TokenAuth, page_count: Union["all", int] = "all", **kwargs
) -> list[Host]:
"""
Queries GAV inventory for assets that satisfy a Qualys Query Language (QQL) filter.
Params:
auth (TokenAuth): The authentication object.
page_count (int): The number of pages to get. Defaults to 'all'.
API params (kwargs):
:Kwargs:
filter (str): The Qualys QQL filter to use.
excludeFields (str): The fields to exclude.
includeFields (str): The fields to include.
lastSeenAssetId (int): The last seen asset ID. Used for automatic pagination.
lastModifiedDate (str): The last modified date.
pageSize (int): The number of assets to get per page.
Returns:
List[Host]: List of Host objects.
"""

responses = [] # list to hold all the responses
Expand Down
3 changes: 3 additions & 0 deletions qualyspy/gav/uber.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ def get(
Params:
endpoint (str): The endpoint to call. Must be one of the keys in CALL_SCHEMA["gav"].
**kwargs: The keyword arguments to pass to the endpoint.
Returns:
The response from the API call.
"""

match endpoint:
Expand Down
3 changes: 3 additions & 0 deletions qualyspy/vmdr/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
VMDRReport,
ReportTemplate,
VMDRScheduledReport,
User,
)

from .data_classes.lists import BaseList
Expand Down Expand Up @@ -54,3 +55,5 @@
get_scheduled_report_list,
launch_scheduled_report,
)

from .users import get_user_list, add_user, edit_user
Loading

0 comments on commit 6cb1cdf

Please sign in to comment.