diff --git a/deployment-apps/msadgraph/CONTRIBUTING.md b/deployment-apps/msadgraph/CONTRIBUTING.md new file mode 100644 index 00000000..b3cc5070 --- /dev/null +++ b/deployment-apps/msadgraph/CONTRIBUTING.md @@ -0,0 +1,2 @@ +# Contributing +For more information about contributing to Splunk SOAR Apps please take a look at our app [Contribution Guide](https://github.com/splunk-soar-connectors/.github/blob/main/.github/CONTRIBUTING.md)! diff --git a/deployment-apps/msadgraph/LICENSE b/deployment-apps/msadgraph/LICENSE new file mode 100644 index 00000000..573247f4 --- /dev/null +++ b/deployment-apps/msadgraph/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) 2022-2023 Splunk Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/deployment-apps/msadgraph/NOTICE b/deployment-apps/msadgraph/NOTICE new file mode 100644 index 00000000..c7723d77 --- /dev/null +++ b/deployment-apps/msadgraph/NOTICE @@ -0,0 +1,26 @@ +Splunk SOAR MS Graph for Active Directory +Copyright (c) 2022-2023 Splunk Inc. + +Third-party Software Attributions: + +Library: Django +Version: 3.2.13 +License: BSD 3 +0.9.0 thru 1.2 1991-1995 CWI yes +1.3 thru 1.5.2 1.2 1995-1999 CNRI yes +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Python Software Foundation; +Copyright 1991-1995 Stichting Mathematisch Centrum Amsterdam +Copyright 2001 Python Software Foundation; All Rights Reserved +Copyright Django Software Foundation and individual contributors + +Library: beautifulsoup4 +Version: 4.9.1 +License: MIT +Copyright 2004-2017 Leonard Richardson +Copyright 2004-2019 Leonard Richardson +Copyright 2018 Isaac Muse + +Library: requests +Version: 2.25.0 +License: Apache 2.0 +Kenneth Reitz diff --git a/deployment-apps/msadgraph/README.md b/deployment-apps/msadgraph/README.md new file mode 100644 index 00000000..8444168a --- /dev/null +++ b/deployment-apps/msadgraph/README.md @@ -0,0 +1,999 @@ +[comment]: # "Auto-generated SOAR connector documentation" +# MS Graph for Active Directory + +Publisher: Splunk +Connector Version: 1.4.0 +Product Vendor: Microsoft +Product Name: MS Graph for Active Directory +Product Version Supported (regex): ".\*" +Minimum Product Version: 6.1.0 + +Connects to Microsoft Active Directory using MS Graph REST API services to support various generic and investigative actions + +[comment]: # " File: README.md" +[comment]: # " Copyright (c) 2022-2023 Splunk Inc." +[comment]: # "" +[comment]: # "Licensed under the Apache License, Version 2.0 (the 'License');" +[comment]: # "you may not use this file except in compliance with the License." +[comment]: # "You may obtain a copy of the License at" +[comment]: # "" +[comment]: # " http://www.apache.org/licenses/LICENSE-2.0" +[comment]: # "" +[comment]: # "Unless required by applicable law or agreed to in writing, software distributed under" +[comment]: # "the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND," +[comment]: # "either express or implied. See the License for the specific language governing permissions" +[comment]: # "and limitations under the License." +[comment]: # "" +## Authentication + +### Microsoft Azure Application creation + +This app requires creating a Microsoft Azure Application. To do so, navigate to + in a browser and log in with a Microsoft account, then select **Azure +Active Directory** . + +1. Go to **App Registrations** and click on **+ New registration** . +2. Give the app an appropriate name. +3. Select a supported account type (configure the application to be multitenant). +4. Click on the **Register** . + - Under **Certificates & secrets** , add **New client secret** . Note this key somewhere + secure, as it cannot be retrieved after closing the window. + - Under **Redirect URIs** we will be updating the entry of https://phantom.local to reflect + the actual redirect URI. We will get this from the SOAR asset we create below in the section + titled "Configure the MS Graph for Active Directory SOAR app Asset" + +### Delegated Permissions configuration + +Use this procedure to provide non-admin permissions to the app. To do so, navigate to + in a browser and log in with a Microsoft account, then navigate to the +previously created app configuration. + +1. Under **API Permissions** , click on **Add a permission** . +2. Go to **Microsoft Graph Permissions** , the following **Delegated Permissions** need to be + added: + - User.ReadWrite.All + - Directory.ReadWrite.All + - Directory.AccessAsUser.All + - User.ManageIdentities.All + - Group.ReadWrite.All + - GroupMember.ReadWrite.All + - RoleManagement.ReadWrite.Directory + - offline_access +3. Click on the **Add permissions** . +4. After making these changes, click on **Grant admin consent** . + +### Application Permissions configuration + +Use this procedure to provide admin permissions to the app. To do so, navigate to + in a browser and log in with a Microsoft account, then navigate to the +previously created app configuration. + +1. Under **API Permissions** , click on **Add a permission** . +2. Go to **Microsoft Graph Permissions** , the following **Application Permissions** need to be + added: + - User.ReadWrite.All + - Directory.ReadWrite.All + - User.ManageIdentities.All + - Group.ReadWrite.All + - GroupMember.ReadWrite.All + - RoleManagement.ReadWrite.Directory +3. Click on the **Add permissions** . +4. After making these changes, click on **Grant admin consent** . + +#### Note: **reset password** action is not supported with Application permissions + +## Configure the MS Graph for Active Directory SOAR app Asset + +When creating an asset for the **MS Graph for Active Directory** app, place the **Application ID** +of the app created during the previous step in the **Client ID** field and place the password +generated during the app creation process in the **Client Secret** field. Then, after filling out +the **Tenant** field, click **SAVE** . + +After saving, a new field will appear in the **Asset Settings** tab. Take the URL found in the +**POST incoming for MS Graph to this location** field and place it in the **Redirect URIs** field of +the Azure Application configuration page. To this URL, add **/result** . After doing so the URL +should look something like: + +https://\/rest/handler/msgraphforactivedirectory_f2a239df-acb2-47d6-861c-726a435cfe76/\/result + + +Once again, click on Save. + +## Enable Application Permissions + +If you have received admin consent to use application permissions, make sure to check the **Admin +Access Required** and **Admin Consent Already Provided** checkboxes on the asset. + +## User Permissions + +To complete the authorization process, this app needs permission to view assets, which is not +granted by default. First, under **asset settings** , check which user is listed under **Select a +user on behalf of which automated actions can be executed** . By default, the user will be +**automation** , but this user can be changed by clicking **EDIT** at the bottom of the window. To +give this user permission to view assets, follow these steps: + +- In the main drop-down menu, select **Administration** , then select the **User Management** , + and under that tab, select **Roles** . Finally, click **+ ROLE** . +- In the **Add Role** wizard, give the role a name (e.g **Asset Viewer** ), and provide a + description. Subsequently, under **Available Users** , add the user assigned to the asset viewed + earlier. Then click the **Permissions** tab. +- On the permission tab, under **Available Privileges** , give the role the **View Assets** + privilege. Then click **SAVE** . + +## Method to Run Test Connectivity (for delegated permissions) + +After setting up the asset and user, click the **TEST CONNECTIVITY** button. A window should pop up +and display a URL. Navigate to this URL in a separate browser tab. This new tab will redirect to a +Microsoft login page. Log in to a Microsoft account with administrator privileges to the Microsoft +AD environment. After logging in, review the requested permissions listed, then click **Accept** . +Finally, close that tab. The test connectivity window should show success. + +The app should now be ready to use. + +## State File Permissions + +Please check the permissions for the state file as mentioned below. + +#### State Filepath + +- For Root Install Instance: + /opt/phantom/local_data/app_states/f2a239df-acb2-47d6-861c-726a435cfe76/{asset_id}\_state.json +- For Non-Root Install Instance: + /\/local_data/app_states/f2a239df-acb2-47d6-861c-726a435cfe76/{asset_id}\_state.json + +#### State File Permissions + +- File Rights: rw-rw-r-- (664) (The SOAR user should have read and write access for the state + file) +- File Owner: appropriate SOAR user + +## Port Details + +The app uses HTTP/ HTTPS protocol for communicating with the Microsoft Graph server. Below are the +default ports used by the Splunk SOAR Connector. + +| Service Name | Transport Protocol | Port | +|--------------|--------------------|------| +| https | tcp | 443 | + + +### Configuration Variables +The below configuration variables are required for this Connector to operate. These variables are specified when configuring a MS Graph for Active Directory asset in SOAR. + +VARIABLE | REQUIRED | TYPE | DESCRIPTION +-------- | -------- | ---- | ----------- +**tenant_id** | required | string | Tenant (Tenant ID or Tenant Name) +**client_id** | required | string | Application ID +**client_secret** | required | password | Client Secret +**region** | optional | string | Microsoft AD Region +**admin_access_required** | optional | boolean | Admin Access Required +**admin_access_granted** | optional | boolean | Admin Consent Already Provided + +### Supported Actions +[test connectivity](#action-test-connectivity) - Use supplied credentials to generate a token with MS Graph +[list users](#action-list-users) - Get a list of users +[reset password](#action-reset-password) - Reset or set a user's password in a Microsoft AD environment +[disable tokens](#action-disable-tokens) - Invalidate all active refresh tokens for a user in a Microsoft AD environment +[enable user](#action-enable-user) - Enable a user +[disable user](#action-disable-user) - Disable a user +[list user devices](#action-list-user-devices) - List devices for a specified user +[list user attributes](#action-list-user-attributes) - List attributes for all or a specified user +[set user attribute](#action-set-user-attribute) - Set an attribute for a user +[remove user](#action-remove-user) - Remove a user from a specified group +[add user](#action-add-user) - Add a user to a specified group +[list groups](#action-list-groups) - List groups in the organization +[get group](#action-get-group) - Get information about a group +[list group members](#action-list-group-members) - List the members in a group +[validate group](#action-validate-group) - Returns true if a user is in a group; otherwise, false +[list directory roles](#action-list-directory-roles) - List the directory roles that are activated in the tenant +[generate token](#action-generate-token) - Generate a token + +## action: 'test connectivity' +Use supplied credentials to generate a token with MS Graph + +Type: **test** +Read only: **True** + +#### Action Parameters +No parameters are required for this action + +#### Action Output +No Output + +## action: 'list users' +Get a list of users + +Type: **investigate** +Read only: **True** + +For more information on using the filter_string, select_string and expand_string parameters, refer to https://docs.microsoft.com/en-us/graph/query-parameters. By default, only a limited set of properties are returned, to return an alternative property set use $select query parameter. + +#### Action Parameters +PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS +--------- | -------- | ----------- | ---- | -------- +**filter_string** | optional | Filter string to apply to user listing | string | +**select_string** | optional | Select string to get additional user properties. Separate multiple values with commas | string | +**expand_string** | optional | Expand string to get a resource or collection referenced by a single relationship | string | +**use_advanced_query** | optional | Use advanced query capabilities | boolean | + +#### Action Output +DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES +--------- | ---- | -------- | -------------- +action_result.status | string | | success failed +action_result.parameter.expand_string | string | | manager +action_result.parameter.filter_string | string | | startswith(displayName,'User') +action_result.parameter.select_string | string | | displayName +action_result.parameter.use_advanced_query | boolean | | True False +action_result.data.\*.accountEnabled | boolean | | True False +action_result.data.\*.ageGroup | string | | +action_result.data.\*.assignedLicenses.\*.skuId | string | | 189a915c-fe4f-4ffa-bde4-85b9628d07a0 +action_result.data.\*.assignedPlans.\*.assignedTimestamp | string | | 2017-08-29T02:31:40Z +action_result.data.\*.assignedPlans.\*.capabilityStatus | string | | Enabled +action_result.data.\*.assignedPlans.\*.service | string | | OfficeForms +action_result.data.\*.assignedPlans.\*.servicePlanId | string | | e212cbc7-0961-4c40-9825-01117710dcb1 +action_result.data.\*.city | string | | Palo Alto +action_result.data.\*.companyName | string | | +action_result.data.\*.consentProvidedForMinor | string | | +action_result.data.\*.country | string | | US +action_result.data.\*.createdDateTime | string | | 2019-05-21T22:27:20Z +action_result.data.\*.creationType | string | | +action_result.data.\*.deletionTimestamp | string | | +action_result.data.\*.department | string | | Sales +action_result.data.\*.dirSyncEnabled | string | | +action_result.data.\*.displayName | string | | User +action_result.data.\*.employeeId | string | | +action_result.data.\*.facsimileTelephoneNumber | string | | +action_result.data.\*.givenName | string | | testuser +action_result.data.\*.id | string | `user id` | e4c722ac-3b83-478d-8f52-c388885dc30f +action_result.data.\*.immutableId | string | | +action_result.data.\*.isCompromised | string | | +action_result.data.\*.jobTitle | string | | Sales Manager +action_result.data.\*.lastDirSyncTime | string | | +action_result.data.\*.legalAgeGroupClassification | string | | +action_result.data.\*.mail | string | `email` | user@test.com +action_result.data.\*.mailNickname | string | | testmail +action_result.data.\*.mobile | string | | +1 5556378688 +action_result.data.\*.mobilePhone | string | | +action_result.data.\*.objectType | string | | User +action_result.data.\*.odata.type | string | | test.DirectoryServices.User +action_result.data.\*.officeLocation | string | | +action_result.data.\*.onPremisesDistinguishedName | string | | +action_result.data.\*.onPremisesSecurityIdentifier | string | | +action_result.data.\*.otherMails | string | `email` | user.test@outlook.com +action_result.data.\*.passwordPolicies | string | | None +action_result.data.\*.passwordProfile | string | | +action_result.data.\*.passwordProfile.enforceChangePasswordPolicy | boolean | | True False +action_result.data.\*.passwordProfile.forceChangePasswordNextLogin | boolean | | True False +action_result.data.\*.passwordProfile.password | string | | +action_result.data.\*.physicalDeliveryOfficeName | string | | +action_result.data.\*.postalCode | string | | 94303 +action_result.data.\*.preferredLanguage | string | | en-US +action_result.data.\*.provisionedPlans.\*.capabilityStatus | string | | Enabled +action_result.data.\*.provisionedPlans.\*.provisioningStatus | string | | Success +action_result.data.\*.provisionedPlans.\*.service | string | | exchange +action_result.data.\*.proxyAddresses | string | | SMTP:user1@test.com +action_result.data.\*.refreshTokensValidFromDateTime | string | | 2017-09-27T22:54:59Z +action_result.data.\*.showInAddressList | string | | +action_result.data.\*.sipProxyAddress | string | `email` | user@test.com +action_result.data.\*.state | string | | CA +action_result.data.\*.streetAddress | string | | 2479 E. Bayshore Rd. +action_result.data.\*.surname | string | | Test_surname +action_result.data.\*.telephoneNumber | string | | +action_result.data.\*.thumbnailPhoto@odata.mediaEditLink | string | | directoryObjects/6132ca31-7a09-434f-a269-abe836d0c01e/test.DirectoryServices.User/thumbnailPhoto +action_result.data.\*.usageLocation | string | | US +action_result.data.\*.userPrincipalName | string | `user id` | user@test.com +action_result.data.\*.userState | string | | +action_result.data.\*.userStateChangedOn | string | | +action_result.data.\*.userType | string | | Member +action_result.summary.num_users | numeric | | 8 +action_result.summary.result_found | boolean | | True False +action_result.summary.total_results | numeric | | 7 +action_result.message | string | | Successfully listed users +summary.total_objects | numeric | | 1 +summary.total_objects_successful | numeric | | 1 + +## action: 'reset password' +Reset or set a user's password in a Microsoft AD environment + +Type: **contain** +Read only: **False** + +#### Action Parameters +PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS +--------- | -------- | ----------- | ---- | -------- +**user_id** | required | User ID to change password - can be user principal name or object ID | string | `user id` +**force_change** | optional | Force user to change password on next login | boolean | +**temp_password** | required | Temporary password for user | string | + +#### Action Output +DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES +--------- | ---- | -------- | -------------- +action_result.status | string | | success failed +action_result.parameter.force_change | boolean | | True False +action_result.parameter.temp_password | string | | Temp_PA$$w0rd +action_result.parameter.user_id | string | `user id` | ee3dc4f2-70f9-446f-a19e-6b4e95ba030d user@test.com +action_result.data | string | | +action_result.summary.status | string | | Successfully reset user password +action_result.message | string | | Status: Successfully reset user password +summary.total_objects | numeric | | 1 +summary.total_objects_successful | numeric | | 1 + +## action: 'disable tokens' +Invalidate all active refresh tokens for a user in a Microsoft AD environment + +Type: **contain** +Read only: **False** + +#### Action Parameters +PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS +--------- | -------- | ----------- | ---- | -------- +**user_id** | required | User ID to disable tokens of - can be user principal name or object ID | string | `user id` + +#### Action Output +DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES +--------- | ---- | -------- | -------------- +action_result.status | string | | success failed +action_result.parameter.user_id | string | `user id` | ee3dc4f2-70f9-446f-a19e-6b4e95ba030d user@test.com +action_result.data | string | | +action_result.data.\*.@odata.context | string | | https://graph.test.com/v1.0/$metadata#Edm.Boolean +action_result.data.\*.odata.metadata | string | `url` | https://graph.windows.net/1t309est-db6c-4tes-t1d2-12bf3456d78d/$metadata#Edm.Null +action_result.data.\*.odata.null | boolean | | True False +action_result.data.\*.value | boolean | | True False +action_result.summary.status | string | | Successfully disabled tokens +action_result.message | string | | Successfully invalidated tokens Status: Successfully disabled tokens +summary.total_objects | numeric | | 1 +summary.total_objects_successful | numeric | | 1 + +## action: 'enable user' +Enable a user + +Type: **generic** +Read only: **False** + +#### Action Parameters +PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS +--------- | -------- | ----------- | ---- | -------- +**user_id** | required | User ID to enable - can be user principal name or object ID | string | `user id` + +#### Action Output +DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES +--------- | ---- | -------- | -------------- +action_result.status | string | | success failed +action_result.parameter.user_id | string | `user id` | user@test.com +action_result.data | string | | +action_result.summary.status | string | | Successfully enabled user user@test.com +action_result.message | string | | Status: Successfully enabled user user@test.com +summary.total_objects | numeric | | 1 +summary.total_objects_successful | numeric | | 1 + +## action: 'disable user' +Disable a user + +Type: **generic** +Read only: **False** + +#### Action Parameters +PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS +--------- | -------- | ----------- | ---- | -------- +**user_id** | required | User ID to disable - can be user principal name or object ID | string | `user id` + +#### Action Output +DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES +--------- | ---- | -------- | -------------- +action_result.status | string | | success failed +action_result.parameter.user_id | string | `user id` | user@test.com +action_result.data | string | | +action_result.summary.status | string | | Successfully disabled user user@test.com +action_result.message | string | | Status: Successfully disabled user user@test.com +summary.total_objects | numeric | | 1 +summary.total_objects_successful | numeric | | 1 + +## action: 'list user devices' +List devices for a specified user + +Type: **investigate** +Read only: **True** + +By default, only a limited set of properties are returned, to return an alternative property set use $select query parameter. For more information on using the select_string parameter, refer to this documentation. + +#### Action Parameters +PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS +--------- | -------- | ----------- | ---- | -------- +**user_id** | required | User ID - can be user principal name or object ID | string | `user id` +**select_string** | optional | Select string to get additional user properties. Separate multiple values with commas | string | + +#### Action Output +DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES +--------- | ---- | -------- | -------------- +action_result.status | string | | success failed +action_result.parameter.select_string | string | | displayName +action_result.parameter.user_id | string | `user id` | user@test.com +action_result.data.\*.@odata.type | string | | #test.graph.device +action_result.data.\*.accountEnabled | boolean | | True False +action_result.data.\*.alternativeSecurityIds.\*.identityProvider | string | | +action_result.data.\*.alternativeSecurityIds.\*.key | string | | WAA1ADAAOQA6ADwAUwBIAEEAMQAtAFQAUAAtAFAAVQBCAEsARQBZAD4AOQA5AEEARQAwADgAOABDAEUANAA1ADgAMABCADcAQgBGAEEARQA2ADEAQQBCADYANAA3ADYANgA5ADUAOAAzAEQANABFAEYARQA5ADYAOAAyAHkAcQBSAEIANwBrAGEAMQA4AEoATAByACsAegB4AE8AYwB6AE8AYgBNAFEANQBZAEgAbgB0AFQAdgBOAG0AbgA5AEQAZQA2AFgAVQBUAGgAcwBFAD0A +action_result.data.\*.alternativeSecurityIds.\*.type | numeric | | 2 +action_result.data.\*.approximateLastSignInDateTime | string | | 2019-09-26T03:42:15Z +action_result.data.\*.complianceExpirationDateTime | string | | +action_result.data.\*.createdDateTime | string | | 2019-09-26T03:42:15Z +action_result.data.\*.deletedDateTime | string | | +action_result.data.\*.deviceCategory | string | | +action_result.data.\*.deviceId | string | | +action_result.data.\*.deviceMetadata | string | | +action_result.data.\*.deviceOwnership | string | | +action_result.data.\*.deviceVersion | numeric | | +action_result.data.\*.displayName | string | | +action_result.data.\*.domainName | string | | +action_result.data.\*.enrollmentProfileName | string | | +action_result.data.\*.enrollmentType | string | | +action_result.data.\*.extensionAttributes.extensionAttribute1 | string | | +action_result.data.\*.extensionAttributes.extensionAttribute10 | string | | +action_result.data.\*.extensionAttributes.extensionAttribute11 | string | | +action_result.data.\*.extensionAttributes.extensionAttribute12 | string | | +action_result.data.\*.extensionAttributes.extensionAttribute13 | string | | +action_result.data.\*.extensionAttributes.extensionAttribute14 | string | | +action_result.data.\*.extensionAttributes.extensionAttribute15 | string | | +action_result.data.\*.extensionAttributes.extensionAttribute2 | string | | +action_result.data.\*.extensionAttributes.extensionAttribute3 | string | | +action_result.data.\*.extensionAttributes.extensionAttribute4 | string | | +action_result.data.\*.extensionAttributes.extensionAttribute5 | string | | +action_result.data.\*.extensionAttributes.extensionAttribute6 | string | | +action_result.data.\*.extensionAttributes.extensionAttribute7 | string | | +action_result.data.\*.extensionAttributes.extensionAttribute8 | string | | +action_result.data.\*.extensionAttributes.extensionAttribute9 | string | | +action_result.data.\*.externalSourceName | string | | +action_result.data.\*.id | string | | +action_result.data.\*.isCompliant | boolean | | +action_result.data.\*.isManaged | boolean | | +action_result.data.\*.isRooted | string | | +action_result.data.\*.managementType | string | | +action_result.data.\*.manufacturer | string | | +action_result.data.\*.mdmAppId | string | | +action_result.data.\*.model | string | | +action_result.data.\*.onPremisesLastSyncDateTime | string | | +action_result.data.\*.onPremisesSyncEnabled | boolean | | +action_result.data.\*.operatingSystem | string | | Windows +action_result.data.\*.operatingSystemVersion | string | | 10.0.18362.0 +action_result.data.\*.profileType | string | | RegisteredDevice +action_result.data.\*.registrationDateTime | string | | 2019-09-26T03:42:15Z +action_result.data.\*.sourceType | string | | +action_result.data.\*.trustType | string | | Workplace +action_result.summary | string | | +action_result.summary.status | string | | Successfully retrieved owned devices for user test@user.test.com +action_result.message | string | | Status: Successfully retrieved owned devices for user test@user.test.com +summary.total_objects | numeric | | 1 +summary.total_objects_successful | numeric | | 1 + +## action: 'list user attributes' +List attributes for all or a specified user + +Type: **investigate** +Read only: **True** + +By default, only a limited set of properties are returned, to return an alternative property set use $select query parameter. For more information on using the select_string and expand_string parameters, refer to https://docs.microsoft.com/en-us/graph/query-parameters. + +#### Action Parameters +PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS +--------- | -------- | ----------- | ---- | -------- +**user_id** | optional | User ID - can be user principal name or object ID | string | `user id` +**select_string** | optional | Select string to get additional user properties. Separate multiple values with commas | string | +**expand_string** | optional | Expand string to get a resource or collection referenced by a single relationship | string | +**use_advanced_query** | optional | Use advanced query capabilities | boolean | + +#### Action Output +DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES +--------- | ---- | -------- | -------------- +action_result.status | string | | success failed +action_result.parameter.expand_string | string | | manager +action_result.parameter.select_string | string | | displayName +action_result.parameter.use_advanced_query | boolean | | True False +action_result.parameter.user_id | string | `user id` | user@test.com +action_result.data.\*.@odata.context | string | | https://graph.test.com/v1.0/$metadata#users/$entity +action_result.data.\*.accountEnabled | boolean | | True False +action_result.data.\*.ageGroup | string | | +action_result.data.\*.assignedLicenses.\*.skuId | string | | f30db892-07e9-47e9-837c-80727f46fd3d +action_result.data.\*.assignedPlans.\*.assignedTimestamp | string | | 2019-04-26T07:21:18Z +action_result.data.\*.assignedPlans.\*.capabilityStatus | string | | Enabled +action_result.data.\*.assignedPlans.\*.service | string | | exchange +action_result.data.\*.assignedPlans.\*.servicePlanId | string | | 33c4f319-9bdd-48d6-9c4d-410b750a4a5a +action_result.data.\*.city | string | | +action_result.data.\*.companyName | string | | +action_result.data.\*.consentProvidedForMinor | string | | +action_result.data.\*.country | string | | +action_result.data.\*.createdDateTime | string | | 2019-05-02T20:27:59Z +action_result.data.\*.creationType | string | | +action_result.data.\*.deletionTimestamp | string | | +action_result.data.\*.department | string | | Sales +action_result.data.\*.dirSyncEnabled | string | | +action_result.data.\*.displayName | string | | Test User +action_result.data.\*.employeeId | string | | +action_result.data.\*.facsimileTelephoneNumber | string | | +action_result.data.\*.givenName | string | | +action_result.data.\*.id | string | `user id` | 7d55d7e6-cf5a-4dd2-a176-57a3c33b7fa9 +action_result.data.\*.identities.\*.issuer | string | | test.com +action_result.data.\*.identities.\*.issuerAssignedId | string | | test2@user.test.com +action_result.data.\*.identities.\*.signInType | string | | userPrincipalName +action_result.data.\*.immutableId | string | | +action_result.data.\*.isCompromised | string | | +action_result.data.\*.jobTitle | string | | +action_result.data.\*.lastDirSyncTime | string | | +action_result.data.\*.legalAgeGroupClassification | string | | +action_result.data.\*.mail | string | `email` | +action_result.data.\*.mailNickname | string | | test +action_result.data.\*.mobile | string | | +action_result.data.\*.mobilePhone | string | | +action_result.data.\*.objectId | string | | 59f51194-1998-4932-a8ac-468e59374edc +action_result.data.\*.objectType | string | | User +action_result.data.\*.odata.metadata | string | | https://graph.windows.net/1t309est-db6c-4tes-t1d2-12bf3456d78d/$metadata#directoryObjects/@Element +action_result.data.\*.odata.type | string | | test.DirectoryServices.User +action_result.data.\*.officeLocation | string | | +action_result.data.\*.onPremisesDistinguishedName | string | | +action_result.data.\*.onPremisesSecurityIdentifier | string | | +action_result.data.\*.otherMails | string | `email` | user@test.com +action_result.data.\*.passwordPolicies | string | | +action_result.data.\*.passwordProfile | string | | +action_result.data.\*.passwordProfile.enforceChangePasswordPolicy | boolean | | True False +action_result.data.\*.passwordProfile.forceChangePasswordNextLogin | boolean | | True False +action_result.data.\*.passwordProfile.password | string | | +action_result.data.\*.physicalDeliveryOfficeName | string | | +action_result.data.\*.postalCode | string | | +action_result.data.\*.preferredLanguage | string | | +action_result.data.\*.provisionedPlans.\*.capabilityStatus | string | | Enabled +action_result.data.\*.provisionedPlans.\*.provisioningStatus | string | | Success +action_result.data.\*.provisionedPlans.\*.service | string | | exchange +action_result.data.\*.proxyAddresses | string | | SMTP:test_shared_mailbox@test.com +action_result.data.\*.refreshTokensValidFromDateTime | string | | 2019-05-16T19:54:18Z +action_result.data.\*.showInAddressList | string | | +action_result.data.\*.sipProxyAddress | string | `email` | +action_result.data.\*.state | string | | +action_result.data.\*.streetAddress | string | | +action_result.data.\*.surname | string | | +action_result.data.\*.telephoneNumber | string | | +action_result.data.\*.thumbnailPhoto@odata.mediaEditLink | string | | directoryObjects/59f12345-1998-4932-a8ac-468e59374edc/test.DirectoryServices.User/thumbnailPhoto +action_result.data.\*.usageLocation | string | | US +action_result.data.\*.userPrincipalName | string | `user id` | user@test.com +action_result.data.\*.userState | string | | +action_result.data.\*.userStateChangedOn | string | | +action_result.data.\*.userType | string | | Member +action_result.summary.status | string | | Successfully retrieved user attributes Successfully retrieved attributes for user user@test.com +action_result.message | string | | Status: Successfully retrieved user attributes Status: Successfully retrieved attributes for user user@test.com, User enabled: False +summary.total_objects | numeric | | 1 +summary.total_objects_successful | numeric | | 1 + +## action: 'set user attribute' +Set an attribute for a user + +Type: **generic** +Read only: **False** + +#### Action Parameters +PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS +--------- | -------- | ----------- | ---- | -------- +**user_id** | required | User ID - can be user principal name or object ID | string | `user id` +**attribute** | required | Attribute to set | string | +**attribute_value** | required | Value of attribute to set | string | + +#### Action Output +DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES +--------- | ---- | -------- | -------------- +action_result.status | string | | success failed +action_result.parameter.attribute | string | | department +action_result.parameter.attribute_value | string | | Sales +action_result.parameter.user_id | string | `user id` | user@test.com +action_result.data | string | | +action_result.data.\*.classification | string | | +action_result.data.\*.createdDateTime | string | | 2021-03-25T18:40:53Z +action_result.data.\*.deletedDateTime | string | | +action_result.data.\*.deletionTimestamp | string | | +action_result.data.\*.description | string | | This is for testing purpose +action_result.data.\*.dirSyncEnabled | string | | +action_result.data.\*.displayName | string | | Test-site +action_result.data.\*.expirationDateTime | string | | +action_result.data.\*.id | string | `user id` | 2a201c95-101b-42d9-a7af-9a2fdf8193f1 +action_result.data.\*.isAssignableToRole | string | | +action_result.data.\*.lastDirSyncTime | string | | +action_result.data.\*.mail | string | `email` | Test-site@test.com +action_result.data.\*.mailEnabled | boolean | | True False +action_result.data.\*.mailNickname | string | | Test-site +action_result.data.\*.membershipRule | string | | +action_result.data.\*.membershipRuleProcessingState | string | | +action_result.data.\*.objectType | string | | Group +action_result.data.\*.odata.type | string | | test.DirectoryServices.Group +action_result.data.\*.onPremisesDomainName | string | `domain` | +action_result.data.\*.onPremisesLastSyncDateTime | string | | +action_result.data.\*.onPremisesNetBiosName | string | | +action_result.data.\*.onPremisesSamAccountName | string | | +action_result.data.\*.onPremisesSecurityIdentifier | string | | +action_result.data.\*.onPremisesSyncEnabled | string | | +action_result.data.\*.preferredDataLocation | string | | +action_result.data.\*.preferredLanguage | string | | +action_result.data.\*.proxyAddresses | string | | SMTP:test-h@test.com +action_result.data.\*.renewedDateTime | string | | 2021-03-25T18:40:53Z +action_result.data.\*.securityEnabled | boolean | | True False +action_result.data.\*.securityIdentifier | string | | S-1-12-1-294681889-1319597617-672379543-28952017 +action_result.data.\*.theme | string | | +action_result.data.\*.visibility | string | | Private +action_result.summary.status | string | | Successfully enabled user user@test.com +action_result.message | string | | Status: Successfully enabled user user@test.com +summary.total_objects | numeric | | 1 +summary.total_objects_successful | numeric | | 1 + +## action: 'remove user' +Remove a user from a specified group + +Type: **generic** +Read only: **False** + +#### Action Parameters +PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS +--------- | -------- | ----------- | ---- | -------- +**group_object_id** | required | Object ID of group | string | `group object id` +**user_id** | required | User ID to remove from group | string | `user id` + +#### Action Output +DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES +--------- | ---- | -------- | -------------- +action_result.status | string | | success failed +action_result.parameter.group_object_id | string | `group object id` | ddb876b3-603a-437b-9814-2d46a2219a1e +action_result.parameter.user_id | string | `user id` | 17be76d0-35ed-4881-ab62-d2eb73c2ebe3 +action_result.data | string | | +action_result.summary.status | string | | Successfully removed user from group User not in group +action_result.message | string | | Status: Successfully removed user from group Status: User not in group +summary.total_objects | numeric | | 1 +summary.total_objects_successful | numeric | | 1 + +## action: 'add user' +Add a user to a specified group + +Type: **generic** +Read only: **False** + +#### Action Parameters +PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS +--------- | -------- | ----------- | ---- | -------- +**group_object_id** | required | Object ID of group | string | `group object id` +**user_id** | required | User ID to add to group | string | `user id` + +#### Action Output +DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES +--------- | ---- | -------- | -------------- +action_result.status | string | | success failed +action_result.parameter.group_object_id | string | `group object id` | ddb876b3-603a-437b-9814-2d46a2219a1e +action_result.parameter.user_id | string | `user id` | 17be76d0-35ed-4881-ab62-d2eb73c2ebe3 +action_result.data | string | | +action_result.summary.status | string | | Successfully added user to group User already in group +action_result.message | string | | Status: Successfully added user to group Status: User already in group +summary.total_objects | numeric | | 1 +summary.total_objects_successful | numeric | | 1 + +## action: 'list groups' +List groups in the organization + +Type: **investigate** +Read only: **True** + +By default, only a limited set of properties are returned, to return an alternative property set use $select query parameter. For more information on using the select_string and expand_string parameters, refer to https://docs.microsoft.com/en-us/graph/query-parameters. + +#### Action Parameters +PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS +--------- | -------- | ----------- | ---- | -------- +**filter_string** | optional | Filter string to apply to group listing | string | +**select_string** | optional | Select string to get additional group properties. Separate multiple values with commas | string | +**expand_string** | optional | Expand string to get a resource or collection referenced by a single relationship | string | +**use_advanced_query** | optional | Use advanced query capabilities | boolean | + +#### Action Output +DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES +--------- | ---- | -------- | -------------- +action_result.status | string | | success failed +action_result.parameter.expand_string | string | | members +action_result.parameter.filter_string | string | | createdDateTime ge '2014-01-01T00:00:00Z' +action_result.parameter.select_string | string | | displayName +action_result.parameter.use_advanced_query | boolean | | True False +action_result.data.\*.classification | string | | +action_result.data.\*.createdDateTime | string | | 2021-03-25T18:40:53Z +action_result.data.\*.deletedDateTime | string | | +action_result.data.\*.deletionTimestamp | string | | +action_result.data.\*.description | string | | This is for testing purpose +action_result.data.\*.dirSyncEnabled | string | | +action_result.data.\*.displayName | string | | Test-site +action_result.data.\*.expirationDateTime | string | | +action_result.data.\*.id | string | `group object id` | 2a201c95-101b-42d9-a7af-9a2fdf8193f1 +action_result.data.\*.isAssignableToRole | string | | +action_result.data.\*.lastDirSyncTime | string | | +action_result.data.\*.mail | string | `email` | Test-site@test.com +action_result.data.\*.mailEnabled | boolean | | True False +action_result.data.\*.mailNickname | string | | Test-site +action_result.data.\*.membershipRule | string | | +action_result.data.\*.membershipRuleProcessingState | string | | +action_result.data.\*.objectType | string | | Group +action_result.data.\*.odata.type | string | | test.DirectoryServices.Group +action_result.data.\*.onPremisesDomainName | string | `domain` | +action_result.data.\*.onPremisesLastSyncDateTime | string | | +action_result.data.\*.onPremisesNetBiosName | string | | +action_result.data.\*.onPremisesSamAccountName | string | | +action_result.data.\*.onPremisesSecurityIdentifier | string | | +action_result.data.\*.onPremisesSyncEnabled | string | | +action_result.data.\*.preferredDataLocation | string | | +action_result.data.\*.preferredLanguage | string | | +action_result.data.\*.proxyAddresses | string | | SMTP:test-h@test.com +action_result.data.\*.renewedDateTime | string | | 2021-03-25T18:40:53Z +action_result.data.\*.securityEnabled | boolean | | True False +action_result.data.\*.securityIdentifier | string | | S-1-12-1-294681889-1319597617-672379543-28952017 +action_result.data.\*.theme | string | | +action_result.data.\*.visibility | string | | Private +action_result.summary.num_groups | numeric | | 7 +action_result.message | string | | Num groups: 7 +summary.total_objects | numeric | | 1 +summary.total_objects_successful | numeric | | 1 + +## action: 'get group' +Get information about a group + +Type: **investigate** +Read only: **True** + +By default, only a limited set of properties are returned, to return an alternative property set use $select query parameter. For more information on using the select_string and expand_string parameters, refer to https://docs.microsoft.com/en-us/graph/query-parameters. + +#### Action Parameters +PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS +--------- | -------- | ----------- | ---- | -------- +**object_id** | required | Object ID of group | string | `group object id` +**select_string** | optional | Select string to get additional group properties. Separate multiple values with commas | string | +**expand_string** | optional | Expand string to get a resource or collection referenced by a single relationship | string | +**use_advanced_query** | optional | Use advanced query capabilities | boolean | + +#### Action Output +DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES +--------- | ---- | -------- | -------------- +action_result.status | string | | success failed +action_result.parameter.expand_string | string | | members +action_result.parameter.object_id | string | `group object id` | ddb876b3-603a-437b-9814-2d46a2219a1e +action_result.parameter.select_string | string | | displayName +action_result.parameter.use_advanced_query | boolean | | True False +action_result.data.\*.@odata.context | string | | https://graph.test.com/v1.0/$metadata#groups(id,displayName)/$entity +action_result.data.\*.classification | string | | +action_result.data.\*.createdDateTime | string | | 2020-08-05T11:59:49Z +action_result.data.\*.deletedDateTime | string | | +action_result.data.\*.deletionTimestamp | string | | +action_result.data.\*.description | string | | This is the office 365 group +action_result.data.\*.dirSyncEnabled | string | | +action_result.data.\*.displayName | string | | o365group +action_result.data.\*.expirationDateTime | string | | +action_result.data.\*.id | string | `group object id` | ddb876b3-603a-437b-9814-2d46a2219a1e +action_result.data.\*.isAssignableToRole | string | | +action_result.data.\*.lastDirSyncTime | string | | +action_result.data.\*.mail | string | `email` | bc7f9cabe@test.com +action_result.data.\*.mailEnabled | boolean | | True False +action_result.data.\*.mailNickname | string | | bc7f9cabe +action_result.data.\*.membershipRule | string | | +action_result.data.\*.membershipRuleProcessingState | string | | +action_result.data.\*.objectType | string | | Group +action_result.data.\*.odata.metadata | string | | https://graph.windows.net/1t309est-db6c-4tes-t1d2-12bf3456d78d/$metadata#directoryObjects/@Element +action_result.data.\*.odata.type | string | | test.DirectoryServices.Group +action_result.data.\*.onPremisesDomainName | string | `domain` | +action_result.data.\*.onPremisesLastSyncDateTime | string | | +action_result.data.\*.onPremisesNetBiosName | string | | +action_result.data.\*.onPremisesSamAccountName | string | | +action_result.data.\*.onPremisesSecurityIdentifier | string | | +action_result.data.\*.onPremisesSyncEnabled | string | | +action_result.data.\*.preferredDataLocation | string | | +action_result.data.\*.preferredLanguage | string | | +action_result.data.\*.proxyAddresses | string | | SMTP:bc7f9cabe@test.com +action_result.data.\*.renewedDateTime | string | | 2020-08-05T11:59:49Z +action_result.data.\*.securityEnabled | boolean | | True False +action_result.data.\*.securityIdentifier | string | | S-1-12-1-909260723-1083662375-1952945031-2402852259 +action_result.data.\*.theme | string | | +action_result.data.\*.visibility | string | | +action_result.summary.display_name | string | | o365group +action_result.summary.status | string | | Successfully retrieved group 104d4576-1544-48b5-bb7e-9f8f871aa824 +action_result.message | string | | Display name: o365group +summary.total_objects | numeric | | 1 +summary.total_objects_successful | numeric | | 1 + +## action: 'list group members' +List the members in a group + +Type: **investigate** +Read only: **True** + +By default, only a limited set of properties are returned, to return an alternative property set use $select query parameter. For more information on using the select_string and expand_string parameters, refer to https://docs.microsoft.com/en-us/graph/query-parameters. + +#### Action Parameters +PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS +--------- | -------- | ----------- | ---- | -------- +**group_object_id** | required | Object ID of group | string | `group object id` +**select_string** | optional | Select string to get additional properties. Separate multiple values with commas | string | +**expand_string** | optional | Expand string to get a resource or collection referenced by a single relationship | string | +**use_advanced_query** | optional | Use advanced query capabilities | boolean | + +#### Action Output +DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES +--------- | ---- | -------- | -------------- +action_result.status | string | | success failed +action_result.parameter.expand_string | string | | manager +action_result.parameter.group_object_id | string | `group object id` | ebcd3130-55a1-4cbf-81b2-86408ff21203 +action_result.parameter.select_string | string | | displayName +action_result.parameter.use_advanced_query | boolean | | True False +action_result.data.\*.@odata.type | string | | #test.graph.user +action_result.data.\*.accountEnabled | boolean | | True +action_result.data.\*.ageGroup | string | | +action_result.data.\*.assignedLicenses.\*.skuId | string | | 189a915c-fe4f-4ffa-bde4-85b9628d07a0 +action_result.data.\*.assignedPlans.\*.assignedDateTime | string | | 2022-11-03T15:12:28Z +action_result.data.\*.assignedPlans.\*.capabilityStatus | string | | Deleted +action_result.data.\*.assignedPlans.\*.service | string | | AADPremiumService +action_result.data.\*.assignedPlans.\*.servicePlanId | string | | eec0eb4f-6444-4f95-aba0-50c24d67f998 +action_result.data.\*.city | string | | Palo Alto +action_result.data.\*.companyName | string | | +action_result.data.\*.consentProvidedForMinor | string | | +action_result.data.\*.country | string | | US +action_result.data.\*.createdDateTime | string | | 2016-06-09T18:33:27Z +action_result.data.\*.creationType | string | | +action_result.data.\*.deletedDateTime | string | | +action_result.data.\*.department | string | | +action_result.data.\*.displayName | string | | Firstname Lastname +action_result.data.\*.employeeHireDate | string | | +action_result.data.\*.employeeId | string | | +action_result.data.\*.employeeOrgData | string | | +action_result.data.\*.employeeType | string | | +action_result.data.\*.externalUserState | string | | +action_result.data.\*.externalUserStateChangeDateTime | string | | +action_result.data.\*.faxNumber | string | | +action_result.data.\*.givenName | string | | +action_result.data.\*.id | string | `user id` | 17be76d0-35ed-4881-ab62-d2eb73c2ebe3 +action_result.data.\*.identities.\*.issuer | string | | test.com +action_result.data.\*.identities.\*.issuerAssignedId | string | | test@user.test.com +action_result.data.\*.identities.\*.signInType | string | | userPrincipalName +action_result.data.\*.isResourceAccount | string | | +action_result.data.\*.jobTitle | string | | +action_result.data.\*.legalAgeGroupClassification | string | | +action_result.data.\*.mail | string | | +action_result.data.\*.mailNickname | string | | User +action_result.data.\*.mobilePhone | string | | +action_result.data.\*.officeLocation | string | | +action_result.data.\*.onPremisesDistinguishedName | string | | +action_result.data.\*.onPremisesDomainName | string | | +action_result.data.\*.onPremisesExtensionAttributes.extensionAttribute1 | string | | +action_result.data.\*.onPremisesExtensionAttributes.extensionAttribute10 | string | | +action_result.data.\*.onPremisesExtensionAttributes.extensionAttribute11 | string | | +action_result.data.\*.onPremisesExtensionAttributes.extensionAttribute12 | string | | +action_result.data.\*.onPremisesExtensionAttributes.extensionAttribute13 | string | | +action_result.data.\*.onPremisesExtensionAttributes.extensionAttribute14 | string | | +action_result.data.\*.onPremisesExtensionAttributes.extensionAttribute15 | string | | +action_result.data.\*.onPremisesExtensionAttributes.extensionAttribute2 | string | | +action_result.data.\*.onPremisesExtensionAttributes.extensionAttribute3 | string | | +action_result.data.\*.onPremisesExtensionAttributes.extensionAttribute4 | string | | +action_result.data.\*.onPremisesExtensionAttributes.extensionAttribute5 | string | | +action_result.data.\*.onPremisesExtensionAttributes.extensionAttribute6 | string | | +action_result.data.\*.onPremisesExtensionAttributes.extensionAttribute7 | string | | +action_result.data.\*.onPremisesExtensionAttributes.extensionAttribute8 | string | | +action_result.data.\*.onPremisesExtensionAttributes.extensionAttribute9 | string | | +action_result.data.\*.onPremisesImmutableId | string | | +action_result.data.\*.onPremisesLastSyncDateTime | string | | +action_result.data.\*.onPremisesSamAccountName | string | | +action_result.data.\*.onPremisesSecurityIdentifier | string | | +action_result.data.\*.onPremisesSyncEnabled | string | | +action_result.data.\*.onPremisesUserPrincipalName | string | | +action_result.data.\*.passwordPolicies | string | | +action_result.data.\*.passwordProfile | string | | +action_result.data.\*.postalCode | string | | 94303 +action_result.data.\*.preferredDataLocation | string | | +action_result.data.\*.preferredLanguage | string | | +action_result.data.\*.provisionedPlans.\*.capabilityStatus | string | | Enabled +action_result.data.\*.provisionedPlans.\*.provisioningStatus | string | | Success +action_result.data.\*.provisionedPlans.\*.service | string | | testCommunicationsOnline +action_result.data.\*.refreshTokensValidFromDateTime | string | | 2022-08-08T13:00:58Z +action_result.data.\*.showInAddressList | string | | +action_result.data.\*.signInSessionsValidFromDateTime | string | | 2022-08-08T13:00:58Z +action_result.data.\*.state | string | | CA +action_result.data.\*.streetAddress | string | | 2479 E. Bayshore Rd. +action_result.data.\*.surname | string | | +action_result.data.\*.usageLocation | string | | US +action_result.data.\*.userPrincipalName | string | | ews_retest@test.com +action_result.data.\*.userType | string | | Member +action_result.summary.num_members | numeric | `user id` | 3 +action_result.summary.num_users | numeric | | 3 +action_result.message | string | | Num members: 3 +summary.total_objects | numeric | | 1 +summary.total_objects_successful | numeric | | 1 + +## action: 'validate group' +Returns true if a user is in a group; otherwise, false + +Type: **investigate** +Read only: **True** + +#### Action Parameters +PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS +--------- | -------- | ----------- | ---- | -------- +**group_object_id** | required | Object ID of group | string | `group object id` +**user_id** | required | User ID to validate | string | `user id` + +#### Action Output +DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES +--------- | ---- | -------- | -------------- +action_result.status | string | | success failed +action_result.parameter.group_object_id | string | `group object id` | ebcd3130-55a1-4cbf-81b2-86408ff21203 +action_result.parameter.user_id | string | `user id` | user@test.com +action_result.data.\*.@odata.context | string | | https://graph.test.com/v1.0/$metadata#directoryObjects +action_result.data.\*.user_in_group | string | | +action_result.data.\*.value.\*.@odata.type | string | | #test.graph.group +action_result.data.\*.value.\*.classification | string | | +action_result.data.\*.value.\*.createdDateTime | string | | 2022-02-25T12:05:22Z +action_result.data.\*.value.\*.deletedDateTime | string | | +action_result.data.\*.value.\*.description | string | | Test group for MSGraph +action_result.data.\*.value.\*.displayName | string | | Test group for MSGraph +action_result.data.\*.value.\*.expirationDateTime | string | | +action_result.data.\*.value.\*.id | string | `user id` | 49233413-24c6-4516-a9e1-4d5f87fe34fd +action_result.data.\*.value.\*.isAssignableToRole | string | | +action_result.data.\*.value.\*.mail | string | | test@user.test.com +action_result.data.\*.value.\*.mailEnabled | boolean | | True +action_result.data.\*.value.\*.mailNickname | string | | TestgroupforMSGraph +action_result.data.\*.value.\*.membershipRule | string | | +action_result.data.\*.value.\*.membershipRuleProcessingState | string | | +action_result.data.\*.value.\*.onPremisesDomainName | string | | +action_result.data.\*.value.\*.onPremisesLastSyncDateTime | string | | +action_result.data.\*.value.\*.onPremisesNetBiosName | string | | +action_result.data.\*.value.\*.onPremisesSamAccountName | string | | +action_result.data.\*.value.\*.onPremisesSecurityIdentifier | string | | +action_result.data.\*.value.\*.onPremisesSyncEnabled | string | | +action_result.data.\*.value.\*.preferredDataLocation | string | | +action_result.data.\*.value.\*.preferredLanguage | string | | +action_result.data.\*.value.\*.renewedDateTime | string | | 2022-02-25T12:05:22Z +action_result.data.\*.value.\*.securityEnabled | boolean | | True +action_result.data.\*.value.\*.securityIdentifier | string | | S-1-12-1-1227043859-1159079110-1598939561-4248108679 +action_result.data.\*.value.\*.theme | string | | +action_result.data.\*.value.\*.visibility | string | | Private +action_result.summary.message | string | | User is member of group +action_result.summary.user_in_group | string | | +action_result.message | string | | User in group: True +summary.total_objects | numeric | | 1 +summary.total_objects_successful | numeric | | 1 + +## action: 'list directory roles' +List the directory roles that are activated in the tenant + +Type: **investigate** +Read only: **True** + +

Pagination is not implemented for this action as this endpoint does not support pagination. Here is the Documentation for the same.

+ +#### Action Parameters +No parameters are required for this action + +#### Action Output +DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES +--------- | ---- | -------- | -------------- +action_result.status | string | | success failed +action_result.data.\*.deletedDateTime | string | | +action_result.data.\*.deletionTimestamp | string | | +action_result.data.\*.description | string | | Can read basic directory information. For granting access to applications, not intended for users. +action_result.data.\*.displayName | string | | Directory Readers +action_result.data.\*.id | string | `directory object id` | 02b238cb-0d15-454b-aae6-0e94993a3207 +action_result.data.\*.isSystem | boolean | | True False +action_result.data.\*.objectType | string | | Role +action_result.data.\*.odata.type | string | | test.DirectoryServices.DirectoryRole +action_result.data.\*.roleTemplateId | string | `role template id` | 88d8e3e3-8f55-4a1e-953a-9b9898b8876b +action_result.summary.num_directory_roles | numeric | | 9 +action_result.message | string | | Num directory roles: 9 +summary.total_objects | numeric | | 1 +summary.total_objects_successful | numeric | | 1 + +## action: 'generate token' +Generate a token + +Type: **generic** +Read only: **False** + +#### Action Parameters +No parameters are required for this action + +#### Action Output +DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES +--------- | ---- | -------- | -------------- +action_result.status | string | | success failed +action_result.data | string | | +action_result.summary | string | | +action_result.message | string | | Token generated +summary.total_objects | numeric | | 1 +summary.total_objects_successful | numeric | | 1 \ No newline at end of file diff --git a/deployment-apps/msadgraph/__init__.py b/deployment-apps/msadgraph/__init__.py new file mode 100644 index 00000000..1cad73cc --- /dev/null +++ b/deployment-apps/msadgraph/__init__.py @@ -0,0 +1,14 @@ +# File: __init__.py +# +# Copyright (c) 2022-2023 Splunk Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under +# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific language governing permissions +# and limitations under the License. diff --git a/deployment-apps/msadgraph/logo_msadgraph.svg b/deployment-apps/msadgraph/logo_msadgraph.svg new file mode 100644 index 00000000..e21db8b7 --- /dev/null +++ b/deployment-apps/msadgraph/logo_msadgraph.svg @@ -0,0 +1 @@ +Microsoft Logo diff --git a/deployment-apps/msadgraph/logo_msadgraph_dark.svg b/deployment-apps/msadgraph/logo_msadgraph_dark.svg new file mode 100644 index 00000000..9c2c2dbf --- /dev/null +++ b/deployment-apps/msadgraph/logo_msadgraph_dark.svg @@ -0,0 +1 @@ +Artboard 1 diff --git a/deployment-apps/msadgraph/manual_readme_content.md b/deployment-apps/msadgraph/manual_readme_content.md new file mode 100644 index 00000000..2bf5e8b6 --- /dev/null +++ b/deployment-apps/msadgraph/manual_readme_content.md @@ -0,0 +1,145 @@ +[comment]: # " File: README.md" +[comment]: # " Copyright (c) 2022-2023 Splunk Inc." +[comment]: # "" +[comment]: # "Licensed under the Apache License, Version 2.0 (the 'License');" +[comment]: # "you may not use this file except in compliance with the License." +[comment]: # "You may obtain a copy of the License at" +[comment]: # "" +[comment]: # " http://www.apache.org/licenses/LICENSE-2.0" +[comment]: # "" +[comment]: # "Unless required by applicable law or agreed to in writing, software distributed under" +[comment]: # "the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND," +[comment]: # "either express or implied. See the License for the specific language governing permissions" +[comment]: # "and limitations under the License." +[comment]: # "" +## Authentication + +### Microsoft Azure Application creation + +This app requires creating a Microsoft Azure Application. To do so, navigate to + in a browser and log in with a Microsoft account, then select **Azure +Active Directory** . + +1. Go to **App Registrations** and click on **+ New registration** . +2. Give the app an appropriate name. +3. Select a supported account type (configure the application to be multitenant). +4. Click on the **Register** . + - Under **Certificates & secrets** , add **New client secret** . Note this key somewhere + secure, as it cannot be retrieved after closing the window. + - Under **Redirect URIs** we will be updating the entry of https://phantom.local to reflect + the actual redirect URI. We will get this from the SOAR asset we create below in the section + titled "Configure the MS Graph for Active Directory SOAR app Asset" + +### Delegated Permissions configuration + +Use this procedure to provide non-admin permissions to the app. To do so, navigate to + in a browser and log in with a Microsoft account, then navigate to the +previously created app configuration. + +1. Under **API Permissions** , click on **Add a permission** . +2. Go to **Microsoft Graph Permissions** , the following **Delegated Permissions** need to be + added: + - User.ReadWrite.All + - Directory.ReadWrite.All + - Directory.AccessAsUser.All + - User.ManageIdentities.All + - Group.ReadWrite.All + - GroupMember.ReadWrite.All + - RoleManagement.ReadWrite.Directory + - offline_access +3. Click on the **Add permissions** . +4. After making these changes, click on **Grant admin consent** . + +### Application Permissions configuration + +Use this procedure to provide admin permissions to the app. To do so, navigate to + in a browser and log in with a Microsoft account, then navigate to the +previously created app configuration. + +1. Under **API Permissions** , click on **Add a permission** . +2. Go to **Microsoft Graph Permissions** , the following **Application Permissions** need to be + added: + - User.ReadWrite.All + - Directory.ReadWrite.All + - User.ManageIdentities.All + - Group.ReadWrite.All + - GroupMember.ReadWrite.All + - RoleManagement.ReadWrite.Directory +3. Click on the **Add permissions** . +4. After making these changes, click on **Grant admin consent** . + +#### Note: **reset password** action is not supported with Application permissions + +## Configure the MS Graph for Active Directory SOAR app Asset + +When creating an asset for the **MS Graph for Active Directory** app, place the **Application ID** +of the app created during the previous step in the **Client ID** field and place the password +generated during the app creation process in the **Client Secret** field. Then, after filling out +the **Tenant** field, click **SAVE** . + +After saving, a new field will appear in the **Asset Settings** tab. Take the URL found in the +**POST incoming for MS Graph to this location** field and place it in the **Redirect URIs** field of +the Azure Application configuration page. To this URL, add **/result** . After doing so the URL +should look something like: + +https://\/rest/handler/msgraphforactivedirectory_f2a239df-acb2-47d6-861c-726a435cfe76/\/result + + +Once again, click on Save. + +## Enable Application Permissions + +If you have received admin consent to use application permissions, make sure to check the **Admin +Access Required** and **Admin Consent Already Provided** checkboxes on the asset. + +## User Permissions + +To complete the authorization process, this app needs permission to view assets, which is not +granted by default. First, under **asset settings** , check which user is listed under **Select a +user on behalf of which automated actions can be executed** . By default, the user will be +**automation** , but this user can be changed by clicking **EDIT** at the bottom of the window. To +give this user permission to view assets, follow these steps: + +- In the main drop-down menu, select **Administration** , then select the **User Management** , + and under that tab, select **Roles** . Finally, click **+ ROLE** . +- In the **Add Role** wizard, give the role a name (e.g **Asset Viewer** ), and provide a + description. Subsequently, under **Available Users** , add the user assigned to the asset viewed + earlier. Then click the **Permissions** tab. +- On the permission tab, under **Available Privileges** , give the role the **View Assets** + privilege. Then click **SAVE** . + +## Method to Run Test Connectivity (for delegated permissions) + +After setting up the asset and user, click the **TEST CONNECTIVITY** button. A window should pop up +and display a URL. Navigate to this URL in a separate browser tab. This new tab will redirect to a +Microsoft login page. Log in to a Microsoft account with administrator privileges to the Microsoft +AD environment. After logging in, review the requested permissions listed, then click **Accept** . +Finally, close that tab. The test connectivity window should show success. + +The app should now be ready to use. + +## State File Permissions + +Please check the permissions for the state file as mentioned below. + +#### State Filepath + +- For Root Install Instance: + /opt/phantom/local_data/app_states/f2a239df-acb2-47d6-861c-726a435cfe76/{asset_id}\_state.json +- For Non-Root Install Instance: + /\/local_data/app_states/f2a239df-acb2-47d6-861c-726a435cfe76/{asset_id}\_state.json + +#### State File Permissions + +- File Rights: rw-rw-r-- (664) (The SOAR user should have read and write access for the state + file) +- File Owner: appropriate SOAR user + +## Port Details + +The app uses HTTP/ HTTPS protocol for communicating with the Microsoft Graph server. Below are the +default ports used by the Splunk SOAR Connector. + +| Service Name | Transport Protocol | Port | +|--------------|--------------------|------| +| https | tcp | 443 | diff --git a/deployment-apps/msadgraph/msadgraph.json b/deployment-apps/msadgraph/msadgraph.json new file mode 100644 index 00000000..2acb359d --- /dev/null +++ b/deployment-apps/msadgraph/msadgraph.json @@ -0,0 +1,3871 @@ +{ + "appid": "f2a239df-acb2-47d6-861c-726a435cfe76", + "name": "MS Graph for Active Directory", + "description": "Connects to Microsoft Active Directory using MS Graph REST API services to support various generic and investigative actions", + "type": "identity management", + "product_vendor": "Microsoft", + "logo": "logo_msadgraph.svg", + "logo_dark": "logo_msadgraph_dark.svg", + "product_name": "MS Graph for Active Directory", + "product_version_regex": ".*", + "publisher": "Splunk", + "contributors": [ + { + "name": "Mathieu A. Cormier" + } + ], + "license": "Copyright (c) 2022-2023 Splunk Inc.", + "app_version": "1.4.0", + "python_version": "3", + "utctime_updated": "2022-04-18T12:59:04.000000Z", + "package_name": "phantom_msadgraph", + "main_module": "msadgraph_connector.py", + "min_phantom_version": "6.1.0", + "fips_compliant": true, + "latest_tested_versions": [ + "Cloud 2023-9-20, API Version: 1.0" + ], + "app_wizard_version": "1.0.0", + "rest_handler": "msadgraph_connector._handle_rest_request", + "configuration": { + "tenant_id": { + "description": "Tenant (Tenant ID or Tenant Name)", + "data_type": "string", + "required": true, + "order": 0 + }, + "client_id": { + "description": "Application ID", + "data_type": "string", + "required": true, + "order": 2 + }, + "client_secret": { + "description": "Client Secret", + "data_type": "password", + "required": true, + "order": 3 + }, + "region": { + "data_type": "string", + "description": "Microsoft AD Region", + "default": "Global", + "order": 1, + "value_list": [ + "Global", + "US Gov L4", + "US Gov L5 (DOD)", + "Germany", + "China (21Vianet)" + ] + }, + "admin_access_required": { + "description": "Admin Access Required", + "data_type": "boolean", + "default": false, + "order": 4 + }, + "admin_access_granted": { + "description": "Admin Consent Already Provided", + "data_type": "boolean", + "default": false, + "order": 5 + } + }, + "actions": [ + { + "action": "test connectivity", + "description": "Use supplied credentials to generate a token with MS Graph", + "type": "test", + "identifier": "test_connectivity", + "read_only": true, + "parameters": {}, + "output": [], + "versions": "EQ(*)" + }, + { + "action": "list users", + "description": "Get a list of users", + "verbose": "For more information on using the filter_string, select_string and expand_string parameters, refer to https://docs.microsoft.com/en-us/graph/query-parameters. By default, only a limited set of properties are returned, to return an alternative property set use $select query parameter.", + "type": "investigate", + "identifier": "list_users", + "read_only": true, + "parameters": { + "filter_string": { + "description": "Filter string to apply to user listing", + "data_type": "string", + "order": 0 + }, + "select_string": { + "description": "Select string to get additional user properties. Separate multiple values with commas", + "data_type": "string", + "order": 1 + }, + "expand_string": { + "description": "Expand string to get a resource or collection referenced by a single relationship", + "data_type": "string", + "order": 2 + }, + "use_advanced_query": { + "description": "Use advanced query capabilities", + "data_type": "boolean", + "default": false, + "order": 3 + } + }, + "output": [ + { + "data_path": "action_result.status", + "data_type": "string", + "example_values": [ + "success", + "failed" + ] + }, + { + "data_path": "action_result.parameter.expand_string", + "data_type": "string", + "example_values": [ + "manager" + ] + }, + { + "data_path": "action_result.parameter.filter_string", + "data_type": "string", + "example_values": [ + "startswith(displayName,'User')" + ] + }, + { + "data_path": "action_result.parameter.select_string", + "data_type": "string", + "example_values": [ + "displayName" + ] + }, + { + "data_path": "action_result.parameter.use_advanced_query", + "data_type": "boolean", + "example_values": [ + true, + false + ] + }, + { + "data_path": "action_result.data.*.accountEnabled", + "data_type": "boolean", + "example_values": [ + true, + false + ] + }, + { + "data_path": "action_result.data.*.ageGroup", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.assignedLicenses.*.skuId", + "data_type": "string", + "example_values": [ + "189a915c-fe4f-4ffa-bde4-85b9628d07a0" + ] + }, + { + "data_path": "action_result.data.*.assignedPlans.*.assignedTimestamp", + "data_type": "string", + "example_values": [ + "2017-08-29T02:31:40Z" + ] + }, + { + "data_path": "action_result.data.*.assignedPlans.*.capabilityStatus", + "data_type": "string", + "example_values": [ + "Enabled" + ] + }, + { + "data_path": "action_result.data.*.assignedPlans.*.service", + "data_type": "string", + "example_values": [ + "OfficeForms" + ] + }, + { + "data_path": "action_result.data.*.assignedPlans.*.servicePlanId", + "data_type": "string", + "example_values": [ + "e212cbc7-0961-4c40-9825-01117710dcb1" + ] + }, + { + "data_path": "action_result.data.*.city", + "data_type": "string", + "example_values": [ + "Palo Alto" + ] + }, + { + "data_path": "action_result.data.*.companyName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.consentProvidedForMinor", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.country", + "data_type": "string", + "example_values": [ + "US" + ] + }, + { + "data_path": "action_result.data.*.createdDateTime", + "data_type": "string", + "example_values": [ + "2019-05-21T22:27:20Z" + ] + }, + { + "data_path": "action_result.data.*.creationType", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.deletionTimestamp", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.department", + "data_type": "string", + "example_values": [ + "Sales" + ] + }, + { + "data_path": "action_result.data.*.dirSyncEnabled", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.displayName", + "data_type": "string", + "column_name": "Display Name", + "column_order": 0, + "example_values": [ + "User" + ] + }, + { + "data_path": "action_result.data.*.employeeId", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.facsimileTelephoneNumber", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.givenName", + "data_type": "string", + "example_values": [ + "testuser" + ] + }, + { + "data_path": "action_result.data.*.id", + "data_type": "string", + "column_name": "Object ID", + "column_order": 1, + "example_values": [ + "e4c722ac-3b83-478d-8f52-c388885dc30f" + ], + "contains": [ + "user id" + ] + }, + { + "data_path": "action_result.data.*.immutableId", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.isCompromised", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.jobTitle", + "data_type": "string", + "example_values": [ + "Sales Manager" + ] + }, + { + "data_path": "action_result.data.*.lastDirSyncTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.legalAgeGroupClassification", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.mail", + "data_type": "string", + "example_values": [ + "user@test.com" + ], + "contains": [ + "email" + ] + }, + { + "data_path": "action_result.data.*.mailNickname", + "data_type": "string", + "example_values": [ + "testmail" + ] + }, + { + "data_path": "action_result.data.*.mobile", + "data_type": "string", + "example_values": [ + "+1 5556378688" + ] + }, + { + "data_path": "action_result.data.*.mobilePhone", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.objectType", + "data_type": "string", + "example_values": [ + "User" + ] + }, + { + "data_path": "action_result.data.*.odata.type", + "data_type": "string", + "example_values": [ + "test.DirectoryServices.User" + ] + }, + { + "data_path": "action_result.data.*.officeLocation", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesDistinguishedName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesSecurityIdentifier", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.otherMails", + "data_type": "string", + "example_values": [ + "user.test@outlook.com" + ], + "contains": [ + "email" + ] + }, + { + "data_path": "action_result.data.*.passwordPolicies", + "data_type": "string", + "example_values": [ + "None" + ] + }, + { + "data_path": "action_result.data.*.passwordProfile", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.passwordProfile.enforceChangePasswordPolicy", + "data_type": "boolean", + "example_values": [ + true, + false + ] + }, + { + "data_path": "action_result.data.*.passwordProfile.forceChangePasswordNextLogin", + "data_type": "boolean", + "example_values": [ + true, + false + ] + }, + { + "data_path": "action_result.data.*.passwordProfile.password", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.physicalDeliveryOfficeName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.postalCode", + "data_type": "string", + "example_values": [ + "94303" + ] + }, + { + "data_path": "action_result.data.*.preferredLanguage", + "data_type": "string", + "example_values": [ + "en-US" + ] + }, + { + "data_path": "action_result.data.*.provisionedPlans.*.capabilityStatus", + "data_type": "string", + "example_values": [ + "Enabled" + ] + }, + { + "data_path": "action_result.data.*.provisionedPlans.*.provisioningStatus", + "data_type": "string", + "example_values": [ + "Success" + ] + }, + { + "data_path": "action_result.data.*.provisionedPlans.*.service", + "data_type": "string", + "example_values": [ + "exchange" + ] + }, + { + "data_path": "action_result.data.*.proxyAddresses", + "data_type": "string", + "example_values": [ + "SMTP:user1@test.com" + ] + }, + { + "data_path": "action_result.data.*.refreshTokensValidFromDateTime", + "data_type": "string", + "example_values": [ + "2017-09-27T22:54:59Z" + ] + }, + { + "data_path": "action_result.data.*.showInAddressList", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.sipProxyAddress", + "data_type": "string", + "example_values": [ + "user@test.com" + ], + "contains": [ + "email" + ] + }, + { + "data_path": "action_result.data.*.state", + "data_type": "string", + "example_values": [ + "CA" + ] + }, + { + "data_path": "action_result.data.*.streetAddress", + "data_type": "string", + "example_values": [ + "2479 E. Bayshore Rd." + ] + }, + { + "data_path": "action_result.data.*.surname", + "data_type": "string", + "example_values": [ + "Test_surname" + ] + }, + { + "data_path": "action_result.data.*.telephoneNumber", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.thumbnailPhoto@odata.mediaEditLink", + "data_type": "string", + "example_values": [ + "directoryObjects/6132ca31-7a09-434f-a269-abe836d0c01e/test.DirectoryServices.User/thumbnailPhoto" + ] + }, + { + "data_path": "action_result.data.*.usageLocation", + "data_type": "string", + "example_values": [ + "US" + ] + }, + { + "data_path": "action_result.data.*.userPrincipalName", + "data_type": "string", + "column_name": "User Principal Name", + "column_order": 2, + "example_values": [ + "user@test.com" + ], + "contains": [ + "user id" + ] + }, + { + "data_path": "action_result.data.*.userState", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.userStateChangedOn", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.userType", + "data_type": "string", + "example_values": [ + "Member" + ] + }, + { + "data_path": "action_result.summary.num_users", + "data_type": "numeric", + "example_values": [ + 8 + ] + }, + { + "data_path": "action_result.summary.result_found", + "data_type": "boolean", + "example_values": [ + true, + false + ] + }, + { + "data_path": "action_result.summary.total_results", + "data_type": "numeric", + "example_values": [ + 7 + ] + }, + { + "data_path": "action_result.message", + "data_type": "string", + "example_values": [ + "Successfully listed users" + ] + }, + { + "data_path": "summary.total_objects", + "data_type": "numeric", + "example_values": [ + 1 + ] + }, + { + "data_path": "summary.total_objects_successful", + "data_type": "numeric", + "example_values": [ + 1 + ] + } + ], + "render": { + "type": "custom", + "title": "List Users", + "view": "msadgraph_view.display_view" + }, + "versions": "EQ(*)" + }, + { + "action": "reset password", + "description": "Reset or set a user's password in a Microsoft AD environment", + "type": "contain", + "identifier": "reset_password", + "read_only": false, + "parameters": { + "user_id": { + "description": "User ID to change password - can be user principal name or object ID", + "data_type": "string", + "required": true, + "primary": true, + "order": 0, + "contains": [ + "user id" + ] + }, + "force_change": { + "description": "Force user to change password on next login", + "data_type": "boolean", + "default": true, + "order": 1 + }, + "temp_password": { + "description": "Temporary password for user", + "data_type": "string", + "required": true, + "order": 2 + } + }, + "output": [ + { + "data_path": "action_result.status", + "data_type": "string", + "example_values": [ + "success", + "failed" + ], + "column_name": "Status", + "column_order": 1 + }, + { + "data_path": "action_result.parameter.force_change", + "data_type": "boolean", + "example_values": [ + true, + false + ] + }, + { + "data_path": "action_result.parameter.temp_password", + "data_type": "string", + "example_values": [ + "Temp_PA$$w0rd" + ] + }, + { + "data_path": "action_result.parameter.user_id", + "data_type": "string", + "example_values": [ + "ee3dc4f2-70f9-446f-a19e-6b4e95ba030d", + "user@test.com" + ], + "contains": [ + "user id" + ], + "column_name": "User ID", + "column_order": 0 + }, + { + "data_path": "action_result.data", + "data_type": "string" + }, + { + "data_path": "action_result.summary.status", + "data_type": "string", + "example_values": [ + "Successfully reset user password" + ] + }, + { + "data_path": "action_result.message", + "data_type": "string", + "example_values": [ + "Status: Successfully reset user password" + ] + }, + { + "data_path": "summary.total_objects", + "data_type": "numeric", + "example_values": [ + 1 + ] + }, + { + "data_path": "summary.total_objects_successful", + "data_type": "numeric", + "example_values": [ + 1 + ] + } + ], + "render": { + "width": 12, + "title": "User Password Reset", + "type": "table", + "height": 5 + }, + "versions": "EQ(*)" + }, + { + "action": "disable tokens", + "description": "Invalidate all active refresh tokens for a user in a Microsoft AD environment", + "type": "contain", + "identifier": "invalidate_tokens", + "read_only": false, + "parameters": { + "user_id": { + "description": "User ID to disable tokens of - can be user principal name or object ID", + "data_type": "string", + "required": true, + "primary": true, + "order": 0, + "contains": [ + "user id" + ] + } + }, + "output": [ + { + "data_path": "action_result.status", + "data_type": "string", + "example_values": [ + "success", + "failed" + ], + "column_name": "Status", + "column_order": 1 + }, + { + "data_path": "action_result.parameter.user_id", + "data_type": "string", + "example_values": [ + "ee3dc4f2-70f9-446f-a19e-6b4e95ba030d", + "user@test.com" + ], + "contains": [ + "user id" + ], + "column_name": "User ID", + "column_order": 0 + }, + { + "data_path": "action_result.data", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.@odata.context", + "data_type": "string", + "example_values": [ + "https://graph.test.com/v1.0/$metadata#Edm.Boolean" + ] + }, + { + "data_path": "action_result.data.*.odata.metadata", + "data_type": "string", + "example_values": [ + "https://graph.windows.net/1t309est-db6c-4tes-t1d2-12bf3456d78d/$metadata#Edm.Null" + ], + "contains": [ + "url" + ] + }, + { + "data_path": "action_result.data.*.odata.null", + "data_type": "boolean", + "example_values": [ + true, + false + ] + }, + { + "data_path": "action_result.data.*.value", + "data_type": "boolean", + "example_values": [ + true, + false + ] + }, + { + "data_path": "action_result.summary.status", + "data_type": "string", + "example_values": [ + "Successfully disabled tokens" + ] + }, + { + "data_path": "action_result.message", + "data_type": "string", + "example_values": [ + "Successfully invalidated tokens", + "Status: Successfully disabled tokens" + ] + }, + { + "data_path": "summary.total_objects", + "data_type": "numeric", + "example_values": [ + 1 + ] + }, + { + "data_path": "summary.total_objects_successful", + "data_type": "numeric", + "example_values": [ + 1 + ] + } + ], + "render": { + "width": 12, + "title": "Invalidate Tokens", + "type": "table", + "height": 5 + }, + "versions": "EQ(*)" + }, + { + "action": "enable user", + "description": "Enable a user", + "type": "generic", + "identifier": "enable_user", + "read_only": false, + "undo": "disable user", + "parameters": { + "user_id": { + "description": "User ID to enable - can be user principal name or object ID", + "data_type": "string", + "required": true, + "primary": true, + "order": 0, + "contains": [ + "user id" + ] + } + }, + "output": [ + { + "data_path": "action_result.status", + "data_type": "string", + "example_values": [ + "success", + "failed" + ], + "column_name": "Status", + "column_order": 1 + }, + { + "data_path": "action_result.parameter.user_id", + "data_type": "string", + "example_values": [ + "user@test.com" + ], + "contains": [ + "user id" + ], + "column_name": "User ID", + "column_order": 0 + }, + { + "data_path": "action_result.data", + "data_type": "string" + }, + { + "data_path": "action_result.summary.status", + "data_type": "string", + "example_values": [ + "Successfully enabled user user@test.com" + ] + }, + { + "data_path": "action_result.message", + "data_type": "string", + "example_values": [ + "Status: Successfully enabled user user@test.com" + ] + }, + { + "data_path": "summary.total_objects", + "data_type": "numeric", + "example_values": [ + 1 + ] + }, + { + "data_path": "summary.total_objects_successful", + "data_type": "numeric", + "example_values": [ + 1 + ] + } + ], + "render": { + "type": "table" + }, + "versions": "EQ(*)" + }, + { + "action": "disable user", + "description": "Disable a user", + "type": "generic", + "identifier": "disable_user", + "read_only": false, + "undo": "enable user", + "parameters": { + "user_id": { + "description": "User ID to disable - can be user principal name or object ID", + "data_type": "string", + "required": true, + "primary": true, + "order": 0, + "contains": [ + "user id" + ] + } + }, + "output": [ + { + "data_path": "action_result.status", + "data_type": "string", + "example_values": [ + "success", + "failed" + ], + "column_name": "Status", + "column_order": 1 + }, + { + "data_path": "action_result.parameter.user_id", + "data_type": "string", + "example_values": [ + "user@test.com" + ], + "contains": [ + "user id" + ], + "column_name": "User ID", + "column_order": 0 + }, + { + "data_path": "action_result.data", + "data_type": "string" + }, + { + "data_path": "action_result.summary.status", + "data_type": "string", + "example_values": [ + "Successfully disabled user user@test.com" + ] + }, + { + "data_path": "action_result.message", + "data_type": "string", + "example_values": [ + "Status: Successfully disabled user user@test.com" + ] + }, + { + "data_path": "summary.total_objects", + "data_type": "numeric", + "example_values": [ + 1 + ] + }, + { + "data_path": "summary.total_objects_successful", + "data_type": "numeric", + "example_values": [ + 1 + ] + } + ], + "render": { + "type": "table" + }, + "versions": "EQ(*)" + }, + { + "action": "list user devices", + "description": "List devices for a specified user", + "verbose": "By default, only a limited set of properties are returned, to return an alternative property set use $select query parameter. For more information on using the select_string parameter, refer to this documentation.", + "type": "investigate", + "identifier": "list_user_devices", + "read_only": true, + "parameters": { + "user_id": { + "description": "User ID - can be user principal name or object ID", + "data_type": "string", + "primary": true, + "required": true, + "order": 0, + "contains": [ + "user id" + ] + }, + "select_string": { + "description": "Select string to get additional user properties. Separate multiple values with commas", + "data_type": "string", + "order": 1 + } + }, + "output": [ + { + "data_path": "action_result.status", + "data_type": "string", + "example_values": [ + "success", + "failed" + ] + }, + { + "data_path": "action_result.parameter.select_string", + "data_type": "string", + "example_values": [ + "displayName" + ] + }, + { + "data_path": "action_result.parameter.user_id", + "data_type": "string", + "example_values": [ + "user@test.com" + ], + "contains": [ + "user id" + ] + }, + { + "data_path": "action_result.data.*.@odata.type", + "data_type": "string", + "example_values": [ + "#test.graph.device" + ] + }, + { + "data_path": "action_result.data.*.accountEnabled", + "data_type": "boolean", + "example_values": [ + true, + false + ] + }, + { + "data_path": "action_result.data.*.alternativeSecurityIds.*.identityProvider", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.alternativeSecurityIds.*.key", + "data_type": "string", + "example_values": [ + "WAA1ADAAOQA6ADwAUwBIAEEAMQAtAFQAUAAtAFAAVQBCAEsARQBZAD4AOQA5AEEARQAwADgAOABDAEUANAA1ADgAMABCADcAQgBGAEEARQA2ADEAQQBCADYANAA3ADYANgA5ADUAOAAzAEQANABFAEYARQA5ADYAOAAyAHkAcQBSAEIANwBrAGEAMQA4AEoATAByACsAegB4AE8AYwB6AE8AYgBNAFEANQBZAEgAbgB0AFQAdgBOAG0AbgA5AEQAZQA2AFgAVQBUAGgAcwBFAD0A" + ] + }, + { + "data_path": "action_result.data.*.alternativeSecurityIds.*.type", + "data_type": "numeric", + "example_values": [ + 2 + ] + }, + { + "data_path": "action_result.data.*.approximateLastSignInDateTime", + "data_type": "string", + "example_values": [ + "2019-09-26T03:42:15Z" + ] + }, + { + "data_path": "action_result.data.*.complianceExpirationDateTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.createdDateTime", + "data_type": "string", + "example_values": [ + "2019-09-26T03:42:15Z" + ] + }, + { + "data_path": "action_result.data.*.deletedDateTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.deviceCategory", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.deviceId", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.deviceMetadata", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.deviceOwnership", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.deviceVersion", + "data_type": "numeric" + }, + { + "data_path": "action_result.data.*.displayName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.domainName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.enrollmentProfileName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.enrollmentType", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.extensionAttributes.extensionAttribute1", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.extensionAttributes.extensionAttribute10", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.extensionAttributes.extensionAttribute11", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.extensionAttributes.extensionAttribute12", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.extensionAttributes.extensionAttribute13", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.extensionAttributes.extensionAttribute14", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.extensionAttributes.extensionAttribute15", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.extensionAttributes.extensionAttribute2", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.extensionAttributes.extensionAttribute3", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.extensionAttributes.extensionAttribute4", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.extensionAttributes.extensionAttribute5", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.extensionAttributes.extensionAttribute6", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.extensionAttributes.extensionAttribute7", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.extensionAttributes.extensionAttribute8", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.extensionAttributes.extensionAttribute9", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.externalSourceName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.id", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.isCompliant", + "data_type": "boolean" + }, + { + "data_path": "action_result.data.*.isManaged", + "data_type": "boolean" + }, + { + "data_path": "action_result.data.*.isRooted", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.managementType", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.manufacturer", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.mdmAppId", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.model", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesLastSyncDateTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesSyncEnabled", + "data_type": "boolean" + }, + { + "data_path": "action_result.data.*.operatingSystem", + "data_type": "string", + "example_values": [ + "Windows" + ] + }, + { + "data_path": "action_result.data.*.operatingSystemVersion", + "data_type": "string", + "example_values": [ + "10.0.18362.0" + ] + }, + { + "data_path": "action_result.data.*.profileType", + "data_type": "string", + "example_values": [ + "RegisteredDevice" + ] + }, + { + "data_path": "action_result.data.*.registrationDateTime", + "data_type": "string", + "example_values": [ + "2019-09-26T03:42:15Z" + ] + }, + { + "data_path": "action_result.data.*.sourceType", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.trustType", + "data_type": "string", + "example_values": [ + "Workplace" + ] + }, + { + "data_path": "action_result.summary", + "data_type": "string" + }, + { + "data_path": "action_result.summary.status", + "data_type": "string", + "example_values": [ + "Successfully retrieved owned devices for user test@user.test.com" + ] + }, + { + "data_path": "action_result.message", + "data_type": "string", + "example_values": [ + "Status: Successfully retrieved owned devices for user test@user.test.com" + ] + }, + { + "data_path": "summary.total_objects", + "data_type": "numeric", + "example_values": [ + 1 + ] + }, + { + "data_path": "summary.total_objects_successful", + "data_type": "numeric", + "example_values": [ + 1 + ] + } + ], + "render": { + "type": "custom", + "title": "Owned Devices", + "view": "msadgraph_view.display_view" + }, + "versions": "EQ(*)" + }, + { + "action": "list user attributes", + "description": "List attributes for all or a specified user", + "verbose": "By default, only a limited set of properties are returned, to return an alternative property set use $select query parameter. For more information on using the select_string and expand_string parameters, refer to https://docs.microsoft.com/en-us/graph/query-parameters.", + "type": "investigate", + "identifier": "list_user_attributes", + "read_only": true, + "parameters": { + "user_id": { + "description": "User ID - can be user principal name or object ID", + "data_type": "string", + "primary": true, + "order": 0, + "contains": [ + "user id" + ] + }, + "select_string": { + "description": "Select string to get additional user properties. Separate multiple values with commas", + "data_type": "string", + "order": 1 + }, + "expand_string": { + "description": "Expand string to get a resource or collection referenced by a single relationship", + "data_type": "string", + "order": 2 + }, + "use_advanced_query": { + "description": "Use advanced query capabilities", + "data_type": "boolean", + "default": false, + "order": 3 + } + }, + "output": [ + { + "data_path": "action_result.status", + "data_type": "string", + "example_values": [ + "success", + "failed" + ] + }, + { + "data_path": "action_result.parameter.expand_string", + "data_type": "string", + "example_values": [ + "manager" + ] + }, + { + "data_path": "action_result.parameter.select_string", + "data_type": "string", + "example_values": [ + "displayName" + ] + }, + { + "data_path": "action_result.parameter.use_advanced_query", + "data_type": "boolean", + "example_values": [ + true, + false + ] + }, + { + "data_path": "action_result.parameter.user_id", + "data_type": "string", + "example_values": [ + "user@test.com" + ], + "contains": [ + "user id" + ] + }, + { + "data_path": "action_result.data.*.@odata.context", + "data_type": "string", + "example_values": [ + "https://graph.test.com/v1.0/$metadata#users/$entity" + ] + }, + { + "data_path": "action_result.data.*.accountEnabled", + "data_type": "boolean", + "example_values": [ + true, + false + ] + }, + { + "data_path": "action_result.data.*.ageGroup", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.assignedLicenses.*.skuId", + "data_type": "string", + "example_values": [ + "f30db892-07e9-47e9-837c-80727f46fd3d" + ] + }, + { + "data_path": "action_result.data.*.assignedPlans.*.assignedTimestamp", + "data_type": "string", + "example_values": [ + "2019-04-26T07:21:18Z" + ] + }, + { + "data_path": "action_result.data.*.assignedPlans.*.capabilityStatus", + "data_type": "string", + "example_values": [ + "Enabled" + ] + }, + { + "data_path": "action_result.data.*.assignedPlans.*.service", + "data_type": "string", + "example_values": [ + "exchange" + ] + }, + { + "data_path": "action_result.data.*.assignedPlans.*.servicePlanId", + "data_type": "string", + "example_values": [ + "33c4f319-9bdd-48d6-9c4d-410b750a4a5a" + ] + }, + { + "data_path": "action_result.data.*.city", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.companyName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.consentProvidedForMinor", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.country", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.createdDateTime", + "data_type": "string", + "example_values": [ + "2019-05-02T20:27:59Z" + ] + }, + { + "data_path": "action_result.data.*.creationType", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.deletionTimestamp", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.department", + "data_type": "string", + "example_values": [ + "Sales" + ] + }, + { + "data_path": "action_result.data.*.dirSyncEnabled", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.displayName", + "data_type": "string", + "example_values": [ + "Test User" + ] + }, + { + "data_path": "action_result.data.*.employeeId", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.facsimileTelephoneNumber", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.givenName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.id", + "data_type": "string", + "example_values": [ + "7d55d7e6-cf5a-4dd2-a176-57a3c33b7fa9" + ], + "contains": [ + "user id" + ] + }, + { + "data_path": "action_result.data.*.identities.*.issuer", + "data_type": "string", + "example_values": [ + "test.com" + ] + }, + { + "data_path": "action_result.data.*.identities.*.issuerAssignedId", + "data_type": "string", + "example_values": [ + "test2@user.test.com" + ] + }, + { + "data_path": "action_result.data.*.identities.*.signInType", + "data_type": "string", + "example_values": [ + "userPrincipalName" + ] + }, + { + "data_path": "action_result.data.*.immutableId", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.isCompromised", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.jobTitle", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.lastDirSyncTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.legalAgeGroupClassification", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.mail", + "data_type": "string", + "contains": [ + "email" + ] + }, + { + "data_path": "action_result.data.*.mailNickname", + "data_type": "string", + "example_values": [ + "test" + ] + }, + { + "data_path": "action_result.data.*.mobile", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.mobilePhone", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.objectId", + "data_type": "string", + "example_values": [ + "59f51194-1998-4932-a8ac-468e59374edc" + ] + }, + { + "data_path": "action_result.data.*.objectType", + "data_type": "string", + "example_values": [ + "User" + ] + }, + { + "data_path": "action_result.data.*.odata.metadata", + "data_type": "string", + "example_values": [ + "https://graph.windows.net/1t309est-db6c-4tes-t1d2-12bf3456d78d/$metadata#directoryObjects/@Element" + ] + }, + { + "data_path": "action_result.data.*.odata.type", + "data_type": "string", + "example_values": [ + "test.DirectoryServices.User" + ] + }, + { + "data_path": "action_result.data.*.officeLocation", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesDistinguishedName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesSecurityIdentifier", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.otherMails", + "data_type": "string", + "example_values": [ + "user@test.com" + ], + "contains": [ + "email" + ] + }, + { + "data_path": "action_result.data.*.passwordPolicies", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.passwordProfile", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.passwordProfile.enforceChangePasswordPolicy", + "data_type": "boolean", + "example_values": [ + true, + false + ] + }, + { + "data_path": "action_result.data.*.passwordProfile.forceChangePasswordNextLogin", + "data_type": "boolean", + "example_values": [ + true, + false + ] + }, + { + "data_path": "action_result.data.*.passwordProfile.password", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.physicalDeliveryOfficeName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.postalCode", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.preferredLanguage", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.provisionedPlans.*.capabilityStatus", + "data_type": "string", + "example_values": [ + "Enabled" + ] + }, + { + "data_path": "action_result.data.*.provisionedPlans.*.provisioningStatus", + "data_type": "string", + "example_values": [ + "Success" + ] + }, + { + "data_path": "action_result.data.*.provisionedPlans.*.service", + "data_type": "string", + "example_values": [ + "exchange" + ] + }, + { + "data_path": "action_result.data.*.proxyAddresses", + "data_type": "string", + "example_values": [ + "SMTP:test_shared_mailbox@test.com" + ] + }, + { + "data_path": "action_result.data.*.refreshTokensValidFromDateTime", + "data_type": "string", + "example_values": [ + "2019-05-16T19:54:18Z" + ] + }, + { + "data_path": "action_result.data.*.showInAddressList", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.sipProxyAddress", + "data_type": "string", + "contains": [ + "email" + ] + }, + { + "data_path": "action_result.data.*.state", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.streetAddress", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.surname", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.telephoneNumber", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.thumbnailPhoto@odata.mediaEditLink", + "data_type": "string", + "example_values": [ + "directoryObjects/59f12345-1998-4932-a8ac-468e59374edc/test.DirectoryServices.User/thumbnailPhoto" + ] + }, + { + "data_path": "action_result.data.*.usageLocation", + "data_type": "string", + "example_values": [ + "US" + ] + }, + { + "data_path": "action_result.data.*.userPrincipalName", + "data_type": "string", + "example_values": [ + "user@test.com" + ], + "contains": [ + "user id" + ] + }, + { + "data_path": "action_result.data.*.userState", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.userStateChangedOn", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.userType", + "data_type": "string", + "example_values": [ + "Member" + ] + }, + { + "data_path": "action_result.summary.status", + "data_type": "string", + "example_values": [ + "Successfully retrieved user attributes", + "Successfully retrieved attributes for user user@test.com" + ] + }, + { + "data_path": "action_result.message", + "data_type": "string", + "example_values": [ + "Status: Successfully retrieved user attributes", + "Status: Successfully retrieved attributes for user user@test.com, User enabled: False" + ] + }, + { + "data_path": "summary.total_objects", + "data_type": "numeric", + "example_values": [ + 1 + ] + }, + { + "data_path": "summary.total_objects_successful", + "data_type": "numeric", + "example_values": [ + 1 + ] + } + ], + "render": { + "type": "custom", + "title": "list user attributes", + "view": "msadgraph_view.display_view" + }, + "versions": "EQ(*)" + }, + { + "action": "set user attribute", + "description": "Set an attribute for a user", + "type": "generic", + "identifier": "set_user_attribute", + "read_only": false, + "parameters": { + "user_id": { + "description": "User ID - can be user principal name or object ID", + "data_type": "string", + "required": true, + "primary": true, + "order": 0, + "contains": [ + "user id" + ] + }, + "ph_0": { + "description": "Placeholder", + "data_type": "ph", + "order": 1 + }, + "attribute": { + "description": "Attribute to set", + "data_type": "string", + "required": true, + "order": 2 + }, + "attribute_value": { + "description": "Value of attribute to set", + "data_type": "string", + "required": true, + "order": 3 + } + }, + "output": [ + { + "data_path": "action_result.status", + "data_type": "string", + "example_values": [ + "success", + "failed" + ], + "column_name": "Status", + "column_order": 3 + }, + { + "data_path": "action_result.parameter.attribute", + "data_type": "string", + "example_values": [ + "department" + ], + "column_name": "Attribute Name", + "column_order": 1 + }, + { + "data_path": "action_result.parameter.attribute_value", + "data_type": "string", + "example_values": [ + "Sales" + ], + "column_name": "Attribute Value", + "column_order": 2 + }, + { + "data_path": "action_result.parameter.user_id", + "data_type": "string", + "example_values": [ + "user@test.com" + ], + "contains": [ + "user id" + ], + "column_name": "User ID", + "column_order": 0 + }, + { + "data_path": "action_result.data", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.classification", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.createdDateTime", + "data_type": "string", + "example_values": [ + "2021-03-25T18:40:53Z" + ] + }, + { + "data_path": "action_result.data.*.deletedDateTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.deletionTimestamp", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.description", + "data_type": "string", + "example_values": [ + "This is for testing purpose" + ] + }, + { + "data_path": "action_result.data.*.dirSyncEnabled", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.displayName", + "data_type": "string", + "example_values": [ + "Test-site" + ] + }, + { + "data_path": "action_result.data.*.expirationDateTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.id", + "data_type": "string", + "example_values": [ + "2a201c95-101b-42d9-a7af-9a2fdf8193f1" + ], + "contains": [ + "user id" + ] + }, + { + "data_path": "action_result.data.*.isAssignableToRole", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.lastDirSyncTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.mail", + "data_type": "string", + "example_values": [ + "Test-site@test.com" + ], + "contains": [ + "email" + ] + }, + { + "data_path": "action_result.data.*.mailEnabled", + "data_type": "boolean", + "example_values": [ + true, + false + ] + }, + { + "data_path": "action_result.data.*.mailNickname", + "data_type": "string", + "example_values": [ + "Test-site" + ] + }, + { + "data_path": "action_result.data.*.membershipRule", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.membershipRuleProcessingState", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.objectType", + "data_type": "string", + "example_values": [ + "Group" + ] + }, + { + "data_path": "action_result.data.*.odata.type", + "data_type": "string", + "example_values": [ + "test.DirectoryServices.Group" + ] + }, + { + "data_path": "action_result.data.*.onPremisesDomainName", + "data_type": "string", + "contains": [ + "domain" + ] + }, + { + "data_path": "action_result.data.*.onPremisesLastSyncDateTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesNetBiosName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesSamAccountName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesSecurityIdentifier", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesSyncEnabled", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.preferredDataLocation", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.preferredLanguage", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.proxyAddresses", + "data_type": "string", + "example_values": [ + "SMTP:test-h@test.com" + ] + }, + { + "data_path": "action_result.data.*.renewedDateTime", + "data_type": "string", + "example_values": [ + "2021-03-25T18:40:53Z" + ] + }, + { + "data_path": "action_result.data.*.securityEnabled", + "data_type": "boolean", + "example_values": [ + true, + false + ] + }, + { + "data_path": "action_result.data.*.securityIdentifier", + "data_type": "string", + "example_values": [ + "S-1-12-1-294681889-1319597617-672379543-28952017" + ] + }, + { + "data_path": "action_result.data.*.theme", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.visibility", + "data_type": "string", + "example_values": [ + "Private" + ] + }, + { + "data_path": "action_result.summary.status", + "data_type": "string", + "example_values": [ + "Successfully enabled user user@test.com" + ] + }, + { + "data_path": "action_result.message", + "data_type": "string", + "example_values": [ + "Status: Successfully enabled user user@test.com" + ] + }, + { + "data_path": "summary.total_objects", + "data_type": "numeric", + "example_values": [ + 1 + ] + }, + { + "data_path": "summary.total_objects_successful", + "data_type": "numeric", + "example_values": [ + 1 + ] + } + ], + "render": { + "type": "table" + }, + "versions": "EQ(*)" + }, + { + "action": "remove user", + "description": "Remove a user from a specified group", + "type": "generic", + "identifier": "remove_user", + "read_only": false, + "undo": "add user", + "parameters": { + "group_object_id": { + "description": "Object ID of group", + "data_type": "string", + "required": true, + "primary": true, + "order": 0, + "contains": [ + "group object id" + ] + }, + "user_id": { + "description": "User ID to remove from group", + "data_type": "string", + "required": true, + "primary": true, + "order": 1, + "contains": [ + "user id" + ] + } + }, + "output": [ + { + "data_path": "action_result.status", + "data_type": "string", + "example_values": [ + "success", + "failed" + ], + "column_name": "Status", + "column_order": 2 + }, + { + "data_path": "action_result.parameter.group_object_id", + "data_type": "string", + "example_values": [ + "ddb876b3-603a-437b-9814-2d46a2219a1e" + ], + "contains": [ + "group object id" + ], + "column_name": "Group Object ID", + "column_order": 0 + }, + { + "data_path": "action_result.parameter.user_id", + "data_type": "string", + "example_values": [ + "17be76d0-35ed-4881-ab62-d2eb73c2ebe3" + ], + "contains": [ + "user id" + ], + "column_name": "User ID", + "column_order": 1 + }, + { + "data_path": "action_result.data", + "data_type": "string" + }, + { + "data_path": "action_result.summary.status", + "data_type": "string", + "example_values": [ + "Successfully removed user from group", + "User not in group" + ] + }, + { + "data_path": "action_result.message", + "data_type": "string", + "example_values": [ + "Status: Successfully removed user from group", + "Status: User not in group" + ] + }, + { + "data_path": "summary.total_objects", + "data_type": "numeric", + "example_values": [ + 1 + ] + }, + { + "data_path": "summary.total_objects_successful", + "data_type": "numeric", + "example_values": [ + 1 + ] + } + ], + "render": { + "type": "table" + }, + "versions": "EQ(*)" + }, + { + "action": "add user", + "description": "Add a user to a specified group", + "type": "generic", + "identifier": "add_user", + "read_only": false, + "undo": "remove user", + "parameters": { + "group_object_id": { + "description": "Object ID of group", + "data_type": "string", + "required": true, + "primary": true, + "order": 0, + "contains": [ + "group object id" + ] + }, + "user_id": { + "description": "User ID to add to group", + "data_type": "string", + "required": true, + "primary": true, + "order": 1, + "contains": [ + "user id" + ] + } + }, + "output": [ + { + "data_path": "action_result.status", + "data_type": "string", + "example_values": [ + "success", + "failed" + ], + "column_name": "Status", + "column_order": 2 + }, + { + "data_path": "action_result.parameter.group_object_id", + "data_type": "string", + "example_values": [ + "ddb876b3-603a-437b-9814-2d46a2219a1e" + ], + "contains": [ + "group object id" + ], + "column_name": "Group Object ID", + "column_order": 0 + }, + { + "data_path": "action_result.parameter.user_id", + "data_type": "string", + "example_values": [ + "17be76d0-35ed-4881-ab62-d2eb73c2ebe3" + ], + "contains": [ + "user id" + ], + "column_name": "User ID", + "column_order": 1 + }, + { + "data_path": "action_result.data", + "data_type": "string" + }, + { + "data_path": "action_result.summary.status", + "data_type": "string", + "example_values": [ + "Successfully added user to group", + "User already in group" + ] + }, + { + "data_path": "action_result.message", + "data_type": "string", + "example_values": [ + "Status: Successfully added user to group", + "Status: User already in group" + ] + }, + { + "data_path": "summary.total_objects", + "data_type": "numeric", + "example_values": [ + 1 + ] + }, + { + "data_path": "summary.total_objects_successful", + "data_type": "numeric", + "example_values": [ + 1 + ] + } + ], + "render": { + "type": "table" + }, + "versions": "EQ(*)" + }, + { + "action": "list groups", + "description": "List groups in the organization", + "verbose": "By default, only a limited set of properties are returned, to return an alternative property set use $select query parameter. For more information on using the select_string and expand_string parameters, refer to https://docs.microsoft.com/en-us/graph/query-parameters.", + "type": "investigate", + "identifier": "list_groups", + "read_only": true, + "parameters": { + "filter_string": { + "description": "Filter string to apply to group listing", + "data_type": "string", + "order": 0 + }, + "select_string": { + "description": "Select string to get additional group properties. Separate multiple values with commas", + "data_type": "string", + "order": 1 + }, + "expand_string": { + "description": "Expand string to get a resource or collection referenced by a single relationship", + "data_type": "string", + "order": 2 + }, + "use_advanced_query": { + "description": "Use advanced query capabilities", + "data_type": "boolean", + "default": false, + "order": 3 + } + }, + "output": [ + { + "data_path": "action_result.status", + "data_type": "string", + "example_values": [ + "success", + "failed" + ] + }, + { + "data_path": "action_result.parameter.expand_string", + "data_type": "string", + "example_values": [ + "members" + ] + }, + { + "data_path": "action_result.parameter.filter_string", + "data_type": "string", + "example_values": [ + "createdDateTime ge '2014-01-01T00:00:00Z'" + ] + }, + { + "data_path": "action_result.parameter.select_string", + "data_type": "string", + "example_values": [ + "displayName" + ] + }, + { + "data_path": "action_result.parameter.use_advanced_query", + "data_type": "boolean", + "example_values": [ + true, + false + ] + }, + { + "data_path": "action_result.data.*.classification", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.createdDateTime", + "data_type": "string", + "example_values": [ + "2021-03-25T18:40:53Z" + ] + }, + { + "data_path": "action_result.data.*.deletedDateTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.deletionTimestamp", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.description", + "data_type": "string", + "example_values": [ + "This is for testing purpose" + ], + "column_name": "Description", + "column_order": 1 + }, + { + "data_path": "action_result.data.*.dirSyncEnabled", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.displayName", + "data_type": "string", + "example_values": [ + "Test-site" + ], + "column_name": "Display Name", + "column_order": 0 + }, + { + "data_path": "action_result.data.*.expirationDateTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.id", + "data_type": "string", + "example_values": [ + "2a201c95-101b-42d9-a7af-9a2fdf8193f1" + ], + "contains": [ + "group object id" + ] + }, + { + "data_path": "action_result.data.*.isAssignableToRole", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.lastDirSyncTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.mail", + "data_type": "string", + "example_values": [ + "Test-site@test.com" + ], + "contains": [ + "email" + ] + }, + { + "data_path": "action_result.data.*.mailEnabled", + "data_type": "boolean", + "example_values": [ + true, + false + ] + }, + { + "data_path": "action_result.data.*.mailNickname", + "data_type": "string", + "example_values": [ + "Test-site" + ] + }, + { + "data_path": "action_result.data.*.membershipRule", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.membershipRuleProcessingState", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.objectType", + "data_type": "string", + "example_values": [ + "Group" + ] + }, + { + "data_path": "action_result.data.*.odata.type", + "data_type": "string", + "example_values": [ + "test.DirectoryServices.Group" + ] + }, + { + "data_path": "action_result.data.*.onPremisesDomainName", + "data_type": "string", + "contains": [ + "domain" + ] + }, + { + "data_path": "action_result.data.*.onPremisesLastSyncDateTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesNetBiosName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesSamAccountName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesSecurityIdentifier", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesSyncEnabled", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.preferredDataLocation", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.preferredLanguage", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.proxyAddresses", + "data_type": "string", + "example_values": [ + "SMTP:test-h@test.com" + ] + }, + { + "data_path": "action_result.data.*.renewedDateTime", + "data_type": "string", + "example_values": [ + "2021-03-25T18:40:53Z" + ] + }, + { + "data_path": "action_result.data.*.securityEnabled", + "data_type": "boolean", + "example_values": [ + true, + false + ] + }, + { + "data_path": "action_result.data.*.securityIdentifier", + "data_type": "string", + "example_values": [ + "S-1-12-1-294681889-1319597617-672379543-28952017" + ] + }, + { + "data_path": "action_result.data.*.theme", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.visibility", + "data_type": "string", + "example_values": [ + "Private" + ] + }, + { + "data_path": "action_result.summary.num_groups", + "data_type": "numeric", + "example_values": [ + 7 + ] + }, + { + "data_path": "action_result.message", + "data_type": "string", + "example_values": [ + "Num groups: 7" + ] + }, + { + "data_path": "summary.total_objects", + "data_type": "numeric", + "example_values": [ + 1 + ] + }, + { + "data_path": "summary.total_objects_successful", + "data_type": "numeric", + "example_values": [ + 1 + ] + } + ], + "render": { + "type": "custom", + "title": "List Groups", + "view": "msadgraph_view.display_view" + }, + "versions": "EQ(*)" + }, + { + "action": "get group", + "description": "Get information about a group", + "verbose": "By default, only a limited set of properties are returned, to return an alternative property set use $select query parameter. For more information on using the select_string and expand_string parameters, refer to https://docs.microsoft.com/en-us/graph/query-parameters.", + "type": "investigate", + "identifier": "get_group", + "read_only": true, + "parameters": { + "object_id": { + "description": "Object ID of group", + "data_type": "string", + "required": true, + "primary": true, + "order": 0, + "contains": [ + "group object id" + ] + }, + "select_string": { + "description": "Select string to get additional group properties. Separate multiple values with commas", + "data_type": "string", + "order": 1 + }, + "expand_string": { + "description": "Expand string to get a resource or collection referenced by a single relationship", + "data_type": "string", + "order": 2 + }, + "use_advanced_query": { + "description": "Use advanced query capabilities", + "data_type": "boolean", + "default": false, + "order": 3 + } + }, + "output": [ + { + "data_path": "action_result.status", + "data_type": "string", + "example_values": [ + "success", + "failed" + ] + }, + { + "data_path": "action_result.parameter.expand_string", + "data_type": "string", + "example_values": [ + "members" + ] + }, + { + "data_path": "action_result.parameter.object_id", + "data_type": "string", + "example_values": [ + "ddb876b3-603a-437b-9814-2d46a2219a1e" + ], + "contains": [ + "group object id" + ] + }, + { + "data_path": "action_result.parameter.select_string", + "data_type": "string", + "example_values": [ + "displayName" + ] + }, + { + "data_path": "action_result.parameter.use_advanced_query", + "data_type": "boolean", + "example_values": [ + true, + false + ] + }, + { + "data_path": "action_result.data.*.@odata.context", + "data_type": "string", + "example_values": [ + "https://graph.test.com/v1.0/$metadata#groups(id,displayName)/$entity" + ] + }, + { + "data_path": "action_result.data.*.classification", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.createdDateTime", + "data_type": "string", + "example_values": [ + "2020-08-05T11:59:49Z" + ] + }, + { + "data_path": "action_result.data.*.deletedDateTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.deletionTimestamp", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.description", + "data_type": "string", + "example_values": [ + "This is the office 365 group" + ] + }, + { + "data_path": "action_result.data.*.dirSyncEnabled", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.displayName", + "data_type": "string", + "example_values": [ + "o365group" + ], + "column_name": "Display Name", + "column_order": 0 + }, + { + "data_path": "action_result.data.*.expirationDateTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.id", + "data_type": "string", + "example_values": [ + "ddb876b3-603a-437b-9814-2d46a2219a1e" + ], + "contains": [ + "group object id" + ], + "column_name": "Object ID", + "column_order": 1 + }, + { + "data_path": "action_result.data.*.isAssignableToRole", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.lastDirSyncTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.mail", + "data_type": "string", + "example_values": [ + "bc7f9cabe@test.com" + ], + "contains": [ + "email" + ] + }, + { + "data_path": "action_result.data.*.mailEnabled", + "data_type": "boolean", + "example_values": [ + true, + false + ], + "column_name": "Mail Enabled", + "column_order": 3 + }, + { + "data_path": "action_result.data.*.mailNickname", + "data_type": "string", + "example_values": [ + "bc7f9cabe" + ] + }, + { + "data_path": "action_result.data.*.membershipRule", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.membershipRuleProcessingState", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.objectType", + "data_type": "string", + "example_values": [ + "Group" + ] + }, + { + "data_path": "action_result.data.*.odata.metadata", + "data_type": "string", + "example_values": [ + "https://graph.windows.net/1t309est-db6c-4tes-t1d2-12bf3456d78d/$metadata#directoryObjects/@Element" + ] + }, + { + "data_path": "action_result.data.*.odata.type", + "data_type": "string", + "example_values": [ + "test.DirectoryServices.Group" + ] + }, + { + "data_path": "action_result.data.*.onPremisesDomainName", + "data_type": "string", + "contains": [ + "domain" + ] + }, + { + "data_path": "action_result.data.*.onPremisesLastSyncDateTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesNetBiosName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesSamAccountName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesSecurityIdentifier", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesSyncEnabled", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.preferredDataLocation", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.preferredLanguage", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.proxyAddresses", + "data_type": "string", + "example_values": [ + "SMTP:bc7f9cabe@test.com" + ] + }, + { + "data_path": "action_result.data.*.renewedDateTime", + "data_type": "string", + "example_values": [ + "2020-08-05T11:59:49Z" + ] + }, + { + "data_path": "action_result.data.*.securityEnabled", + "data_type": "boolean", + "example_values": [ + true, + false + ], + "column_name": "Security Enabled", + "column_order": 2 + }, + { + "data_path": "action_result.data.*.securityIdentifier", + "data_type": "string", + "example_values": [ + "S-1-12-1-909260723-1083662375-1952945031-2402852259" + ] + }, + { + "data_path": "action_result.data.*.theme", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.visibility", + "data_type": "string" + }, + { + "data_path": "action_result.summary.display_name", + "data_type": "string", + "example_values": [ + "o365group" + ] + }, + { + "data_path": "action_result.summary.status", + "data_type": "string", + "example_values": [ + "Successfully retrieved group 104d4576-1544-48b5-bb7e-9f8f871aa824" + ] + }, + { + "data_path": "action_result.message", + "data_type": "string", + "example_values": [ + "Display name: o365group" + ] + }, + { + "data_path": "summary.total_objects", + "data_type": "numeric", + "example_values": [ + 1 + ] + }, + { + "data_path": "summary.total_objects_successful", + "data_type": "numeric", + "example_values": [ + 1 + ] + } + ], + "render": { + "type": "custom", + "title": "List Groups", + "view": "msadgraph_view.display_view" + }, + "versions": "EQ(*)" + }, + { + "action": "list group members", + "description": "List the members in a group", + "verbose": "By default, only a limited set of properties are returned, to return an alternative property set use $select query parameter. For more information on using the select_string and expand_string parameters, refer to https://docs.microsoft.com/en-us/graph/query-parameters.", + "type": "investigate", + "identifier": "list_group_members", + "read_only": true, + "parameters": { + "group_object_id": { + "description": "Object ID of group", + "data_type": "string", + "required": true, + "primary": true, + "order": 0, + "contains": [ + "group object id" + ] + }, + "select_string": { + "description": "Select string to get additional properties. Separate multiple values with commas", + "data_type": "string", + "order": 1 + }, + "expand_string": { + "description": "Expand string to get a resource or collection referenced by a single relationship", + "data_type": "string", + "order": 2 + }, + "use_advanced_query": { + "description": "Use advanced query capabilities", + "data_type": "boolean", + "default": false, + "order": 3 + } + }, + "output": [ + { + "data_path": "action_result.status", + "data_type": "string", + "example_values": [ + "success", + "failed" + ] + }, + { + "data_path": "action_result.parameter.expand_string", + "data_type": "string", + "example_values": [ + "manager" + ] + }, + { + "data_path": "action_result.parameter.group_object_id", + "data_type": "string", + "example_values": [ + "ebcd3130-55a1-4cbf-81b2-86408ff21203" + ], + "contains": [ + "group object id" + ] + }, + { + "data_path": "action_result.parameter.select_string", + "data_type": "string", + "example_values": [ + "displayName" + ] + }, + { + "data_path": "action_result.parameter.use_advanced_query", + "data_type": "boolean", + "example_values": [ + true, + false + ] + }, + { + "data_path": "action_result.data.*.@odata.type", + "data_type": "string", + "example_values": [ + "#test.graph.user" + ] + }, + { + "data_path": "action_result.data.*.accountEnabled", + "data_type": "boolean", + "example_values": [ + true + ] + }, + { + "data_path": "action_result.data.*.ageGroup", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.assignedLicenses.*.skuId", + "data_type": "string", + "example_values": [ + "189a915c-fe4f-4ffa-bde4-85b9628d07a0" + ] + }, + { + "data_path": "action_result.data.*.assignedPlans.*.assignedDateTime", + "data_type": "string", + "example_values": [ + "2022-11-03T15:12:28Z" + ] + }, + { + "data_path": "action_result.data.*.assignedPlans.*.capabilityStatus", + "data_type": "string", + "example_values": [ + "Deleted" + ] + }, + { + "data_path": "action_result.data.*.assignedPlans.*.service", + "data_type": "string", + "example_values": [ + "AADPremiumService" + ] + }, + { + "data_path": "action_result.data.*.assignedPlans.*.servicePlanId", + "data_type": "string", + "example_values": [ + "eec0eb4f-6444-4f95-aba0-50c24d67f998" + ] + }, + { + "data_path": "action_result.data.*.city", + "data_type": "string", + "example_values": [ + "Palo Alto" + ] + }, + { + "data_path": "action_result.data.*.companyName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.consentProvidedForMinor", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.country", + "data_type": "string", + "example_values": [ + "US" + ] + }, + { + "data_path": "action_result.data.*.createdDateTime", + "data_type": "string", + "example_values": [ + "2016-06-09T18:33:27Z" + ] + }, + { + "data_path": "action_result.data.*.creationType", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.deletedDateTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.department", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.displayName", + "data_type": "string", + "example_values": [ + "Firstname Lastname" + ], + "column_name": "Display Name", + "column_order": 0 + }, + { + "data_path": "action_result.data.*.employeeHireDate", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.employeeId", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.employeeOrgData", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.employeeType", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.externalUserState", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.externalUserStateChangeDateTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.faxNumber", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.givenName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.id", + "data_type": "string", + "example_values": [ + "17be76d0-35ed-4881-ab62-d2eb73c2ebe3" + ], + "contains": [ + "user id" + ] + }, + { + "data_path": "action_result.data.*.identities.*.issuer", + "data_type": "string", + "example_values": [ + "test.com" + ] + }, + { + "data_path": "action_result.data.*.identities.*.issuerAssignedId", + "data_type": "string", + "example_values": [ + "test@user.test.com" + ] + }, + { + "data_path": "action_result.data.*.identities.*.signInType", + "data_type": "string", + "example_values": [ + "userPrincipalName" + ] + }, + { + "data_path": "action_result.data.*.isResourceAccount", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.jobTitle", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.legalAgeGroupClassification", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.mail", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.mailNickname", + "data_type": "string", + "example_values": [ + "User" + ] + }, + { + "data_path": "action_result.data.*.mobilePhone", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.officeLocation", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesDistinguishedName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesDomainName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesExtensionAttributes.extensionAttribute1", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesExtensionAttributes.extensionAttribute10", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesExtensionAttributes.extensionAttribute11", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesExtensionAttributes.extensionAttribute12", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesExtensionAttributes.extensionAttribute13", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesExtensionAttributes.extensionAttribute14", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesExtensionAttributes.extensionAttribute15", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesExtensionAttributes.extensionAttribute2", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesExtensionAttributes.extensionAttribute3", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesExtensionAttributes.extensionAttribute4", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesExtensionAttributes.extensionAttribute5", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesExtensionAttributes.extensionAttribute6", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesExtensionAttributes.extensionAttribute7", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesExtensionAttributes.extensionAttribute8", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesExtensionAttributes.extensionAttribute9", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesImmutableId", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesLastSyncDateTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesSamAccountName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesSecurityIdentifier", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesSyncEnabled", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.onPremisesUserPrincipalName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.passwordPolicies", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.passwordProfile", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.postalCode", + "data_type": "string", + "example_values": [ + "94303" + ] + }, + { + "data_path": "action_result.data.*.preferredDataLocation", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.preferredLanguage", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.provisionedPlans.*.capabilityStatus", + "data_type": "string", + "example_values": [ + "Enabled" + ] + }, + { + "data_path": "action_result.data.*.provisionedPlans.*.provisioningStatus", + "data_type": "string", + "example_values": [ + "Success" + ] + }, + { + "data_path": "action_result.data.*.provisionedPlans.*.service", + "data_type": "string", + "example_values": [ + "testCommunicationsOnline" + ] + }, + { + "data_path": "action_result.data.*.refreshTokensValidFromDateTime", + "data_type": "string", + "example_values": [ + "2022-08-08T13:00:58Z" + ] + }, + { + "data_path": "action_result.data.*.showInAddressList", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.signInSessionsValidFromDateTime", + "data_type": "string", + "example_values": [ + "2022-08-08T13:00:58Z" + ] + }, + { + "data_path": "action_result.data.*.state", + "data_type": "string", + "example_values": [ + "CA" + ] + }, + { + "data_path": "action_result.data.*.streetAddress", + "data_type": "string", + "example_values": [ + "2479 E. Bayshore Rd." + ] + }, + { + "data_path": "action_result.data.*.surname", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.usageLocation", + "data_type": "string", + "example_values": [ + "US" + ] + }, + { + "data_path": "action_result.data.*.userPrincipalName", + "data_type": "string", + "example_values": [ + "ews_retest@test.com" + ] + }, + { + "data_path": "action_result.data.*.userType", + "data_type": "string", + "example_values": [ + "Member" + ] + }, + { + "data_path": "action_result.summary.num_members", + "data_type": "numeric", + "example_values": [ + 3 + ], + "contains": [ + "user id" + ] + }, + { + "data_path": "action_result.summary.num_users", + "data_type": "numeric", + "example_values": [ + 3 + ] + }, + { + "data_path": "action_result.message", + "data_type": "string", + "example_values": [ + "Num members: 3" + ] + }, + { + "data_path": "summary.total_objects", + "data_type": "numeric", + "example_values": [ + 1 + ] + }, + { + "data_path": "summary.total_objects_successful", + "data_type": "numeric", + "example_values": [ + 1 + ] + } + ], + "render": { + "type": "custom", + "title": "list group members", + "view": "msadgraph_view.display_view" + }, + "versions": "EQ(*)" + }, + { + "action": "validate group", + "description": "Returns true if a user is in a group; otherwise, false", + "type": "investigate", + "identifier": "validate_group", + "read_only": true, + "parameters": { + "group_object_id": { + "description": "Object ID of group", + "data_type": "string", + "required": true, + "primary": true, + "order": 0, + "contains": [ + "group object id" + ] + }, + "user_id": { + "description": "User ID to validate", + "data_type": "string", + "required": true, + "primary": true, + "order": 1, + "contains": [ + "user id" + ] + } + }, + "output": [ + { + "data_path": "action_result.status", + "data_type": "string", + "example_values": [ + "success", + "failed" + ], + "column_name": "Status", + "column_order": 2 + }, + { + "data_path": "action_result.parameter.group_object_id", + "data_type": "string", + "example_values": [ + "ebcd3130-55a1-4cbf-81b2-86408ff21203" + ], + "contains": [ + "group object id" + ], + "column_name": "Group Object ID", + "column_order": 0 + }, + { + "data_path": "action_result.parameter.user_id", + "data_type": "string", + "example_values": [ + "user@test.com" + ], + "contains": [ + "user id" + ], + "column_name": "User ID", + "column_order": 1 + }, + { + "data_path": "action_result.data.*.@odata.context", + "data_type": "string", + "example_values": [ + "https://graph.test.com/v1.0/$metadata#directoryObjects" + ] + }, + { + "data_path": "action_result.data.*.user_in_group", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.value.*.@odata.type", + "data_type": "string", + "example_values": [ + "#test.graph.group" + ] + }, + { + "data_path": "action_result.data.*.value.*.classification", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.value.*.createdDateTime", + "data_type": "string", + "example_values": [ + "2022-02-25T12:05:22Z" + ] + }, + { + "data_path": "action_result.data.*.value.*.deletedDateTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.value.*.description", + "data_type": "string", + "example_values": [ + "Test group for MSGraph" + ] + }, + { + "data_path": "action_result.data.*.value.*.displayName", + "data_type": "string", + "example_values": [ + "Test group for MSGraph" + ] + }, + { + "data_path": "action_result.data.*.value.*.expirationDateTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.value.*.id", + "data_type": "string", + "example_values": [ + "49233413-24c6-4516-a9e1-4d5f87fe34fd" + ], + "contains": [ + "user id" + ] + }, + { + "data_path": "action_result.data.*.value.*.isAssignableToRole", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.value.*.mail", + "data_type": "string", + "example_values": [ + "test@user.test.com" + ] + }, + { + "data_path": "action_result.data.*.value.*.mailEnabled", + "data_type": "boolean", + "example_values": [ + true + ] + }, + { + "data_path": "action_result.data.*.value.*.mailNickname", + "data_type": "string", + "example_values": [ + "TestgroupforMSGraph" + ] + }, + { + "data_path": "action_result.data.*.value.*.membershipRule", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.value.*.membershipRuleProcessingState", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.value.*.onPremisesDomainName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.value.*.onPremisesLastSyncDateTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.value.*.onPremisesNetBiosName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.value.*.onPremisesSamAccountName", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.value.*.onPremisesSecurityIdentifier", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.value.*.onPremisesSyncEnabled", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.value.*.preferredDataLocation", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.value.*.preferredLanguage", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.value.*.renewedDateTime", + "data_type": "string", + "example_values": [ + "2022-02-25T12:05:22Z" + ] + }, + { + "data_path": "action_result.data.*.value.*.securityEnabled", + "data_type": "boolean", + "example_values": [ + true + ] + }, + { + "data_path": "action_result.data.*.value.*.securityIdentifier", + "data_type": "string", + "example_values": [ + "S-1-12-1-1227043859-1159079110-1598939561-4248108679" + ] + }, + { + "data_path": "action_result.data.*.value.*.theme", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.value.*.visibility", + "data_type": "string", + "example_values": [ + "Private" + ] + }, + { + "data_path": "action_result.summary.message", + "data_type": "string", + "example_values": [ + "User is member of group" + ] + }, + { + "data_path": "action_result.summary.user_in_group", + "data_type": "string" + }, + { + "data_path": "action_result.message", + "data_type": "string", + "example_values": [ + "User in group: True" + ] + }, + { + "data_path": "summary.total_objects", + "data_type": "numeric", + "example_values": [ + 1 + ] + }, + { + "data_path": "summary.total_objects_successful", + "data_type": "numeric", + "example_values": [ + 1 + ] + } + ], + "render": { + "type": "table" + }, + "versions": "EQ(*)" + }, + { + "action": "list directory roles", + "description": "List the directory roles that are activated in the tenant", + "verbose": "

Pagination is not implemented for this action as this endpoint does not support pagination. Here is the Documentation for the same.

", + "type": "investigate", + "identifier": "list_directory_roles", + "read_only": true, + "parameters": {}, + "output": [ + { + "data_path": "action_result.status", + "data_type": "string", + "example_values": [ + "success", + "failed" + ] + }, + { + "data_path": "action_result.data.*.deletedDateTime", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.deletionTimestamp", + "data_type": "string" + }, + { + "data_path": "action_result.data.*.description", + "data_type": "string", + "example_values": [ + "Can read basic directory information. For granting access to applications, not intended for users." + ] + }, + { + "data_path": "action_result.data.*.displayName", + "data_type": "string", + "example_values": [ + "Directory Readers" + ], + "column_name": "Display Name", + "column_order": 0 + }, + { + "data_path": "action_result.data.*.id", + "data_type": "string", + "example_values": [ + "02b238cb-0d15-454b-aae6-0e94993a3207" + ], + "contains": [ + "directory object id" + ], + "column_name": "Object ID", + "column_order": 1 + }, + { + "data_path": "action_result.data.*.isSystem", + "data_type": "boolean", + "example_values": [ + true, + false + ] + }, + { + "data_path": "action_result.data.*.objectType", + "data_type": "string", + "example_values": [ + "Role" + ] + }, + { + "data_path": "action_result.data.*.odata.type", + "data_type": "string", + "example_values": [ + "test.DirectoryServices.DirectoryRole" + ] + }, + { + "data_path": "action_result.data.*.roleTemplateId", + "data_type": "string", + "example_values": [ + "88d8e3e3-8f55-4a1e-953a-9b9898b8876b" + ], + "contains": [ + "role template id" + ] + }, + { + "data_path": "action_result.summary.num_directory_roles", + "data_type": "numeric", + "example_values": [ + 9 + ] + }, + { + "data_path": "action_result.message", + "data_type": "string", + "example_values": [ + "Num directory roles: 9" + ] + }, + { + "data_path": "summary.total_objects", + "data_type": "numeric", + "example_values": [ + 1 + ] + }, + { + "data_path": "summary.total_objects_successful", + "data_type": "numeric", + "example_values": [ + 1 + ] + } + ], + "render": { + "type": "table" + }, + "versions": "EQ(*)" + }, + { + "action": "generate token", + "identifier": "generate_token", + "description": "Generate a token", + "type": "generic", + "read_only": false, + "parameters": {}, + "output": [ + { + "data_path": "action_result.status", + "data_type": "string", + "example_values": [ + "success", + "failed" + ], + "column_name": "Status", + "column_order": 0 + }, + { + "data_path": "action_result.data", + "data_type": "string" + }, + { + "data_path": "action_result.summary", + "data_type": "string" + }, + { + "data_path": "action_result.message", + "data_type": "string", + "example_values": [ + "Token generated" + ], + "column_name": "Message", + "column_order": 1 + }, + { + "data_path": "summary.total_objects", + "data_type": "numeric", + "example_values": [ + 1 + ] + }, + { + "data_path": "summary.total_objects_successful", + "data_type": "numeric", + "example_values": [ + 1 + ] + } + ], + "render": { + "type": "table" + }, + "versions": "EQ(*)" + } + ], + "pip_dependencies": { + "wheel": [ + { + "module": "beautifulsoup4", + "input_file": "wheels/py3/beautifulsoup4-4.9.1-py3-none-any.whl" + }, + { + "module": "soupsieve", + "input_file": "wheels/py3/soupsieve-2.3.2.post1-py3-none-any.whl" + } + ] + }, + "pip39_dependencies": { + "wheel": [ + { + "module": "beautifulsoup4", + "input_file": "wheels/py3/beautifulsoup4-4.9.1-py3-none-any.whl" + }, + { + "module": "soupsieve", + "input_file": "wheels/py3/soupsieve-2.4.1-py3-none-any.whl" + } + ] + } +} diff --git a/deployment-apps/msadgraph/msadgraph_connector.py b/deployment-apps/msadgraph/msadgraph_connector.py new file mode 100644 index 00000000..5dea0e42 --- /dev/null +++ b/deployment-apps/msadgraph/msadgraph_connector.py @@ -0,0 +1,1541 @@ +# File: msadgraph_connector.py +# +# Copyright (c) 2022-2023 Splunk Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under +# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific language governing permissions +# and limitations under the License. +# +# +# Phantom App imports +import grp +import json +import os +import pathlib +import pwd +import sys +import time +import urllib.parse as urlparse + +import encryption_helper +import phantom.app as phantom +import requests +from bs4 import BeautifulSoup +from django.http import HttpResponse +from phantom.action_result import ActionResult +from phantom.base_connector import BaseConnector + +from msadgraph_consts import * + +MAX_END_OFFSET_VAL = 2147483646 + + +def _handle_login_redirect(request, key): + """ This function is used to redirect login request to microsoft login page. + + :param request: Data given to REST endpoint + :param key: Key to search in state file + :return: response authorization_url/admin_consent_url + """ + + asset_id = request.GET.get('asset_id') + if not asset_id: + return HttpResponse('ERROR: Asset ID not found in URL', content_type="text/plain", status=MS_AZURE_BAD_REQUEST_CODE) + state = _load_app_state(asset_id) + if not state: + return HttpResponse('ERROR: Invalid asset_id', content_type="text/plain", status=MS_AZURE_BAD_REQUEST_CODE) + url = state.get(key) + if not url: + return HttpResponse(f'App state is invalid, {key} not found.', content_type="text/plain", status=MS_AZURE_BAD_REQUEST_CODE) + response = HttpResponse(status=302) + response['Location'] = url + return response + + +def _is_valid_asset_id(asset_id): + """ This function validates an asset id. + Must be an alphanumeric string of less than 128 characters. + + :param asset_id: asset_id + :return: is_valid: Boolean True if valid, False if not. + """ + if not isinstance(asset_id, str): + return False + if not asset_id.isalnum(): + return False + if len(asset_id) > 128: + return False + return True + + +def _get_file_path(asset_id, is_state_file=True): + """ This function gets the path of the auth status file of an asset id. + + :param asset_id: asset_id + :param app_connector: Object of app_connector class + :param is_state_file: boolean parameter for state file + :return: file_path: Path object of the file + """ + current_file_path = pathlib.Path(__file__).resolve() + if is_state_file: + input_file = f'{asset_id}_state.json' + else: + input_file = f'{asset_id}_oauth_task.out' + output_file_path = current_file_path.with_name(input_file) + return output_file_path + + +def _decrypt_state(state, salt): + """ + Decrypts the state. + :param state: state dictionary + :param salt: salt used for decryption + :return: decrypted state + """ + + if not state.get("is_encrypted"): + return state + + access_token = state.get("token", {}).get("access_token") + if access_token: + state["token"]["access_token"] = encryption_helper.decrypt(access_token, salt) + + refresh_token = state.get("token", {}).get("refresh_token") + if refresh_token: + state["token"]["refresh_token"] = encryption_helper.decrypt(refresh_token, salt) + + code = state.get("code") + if code: + state["code"] = encryption_helper.decrypt(code, salt) + + return state + + +def _encrypt_state(state, salt): + """ + Encrypts the state. + :param state: state dictionary + :param salt: salt used for encryption + :return: encrypted state + """ + + access_token = state.get("token", {}).get("access_token") + if access_token: + state["token"]["access_token"] = encryption_helper.encrypt(access_token, salt) + + refresh_token = state.get("token", {}).get("refresh_token") + if refresh_token: + state["token"]["refresh_token"] = encryption_helper.encrypt(refresh_token, salt) + + code = state.get("code") + if code: + state["code"] = encryption_helper.encrypt(code, salt) + + state["is_encrypted"] = True + + return state + + +def _load_app_state(asset_id, app_connector=None): + """ This function is used to load the current state file. + + :param asset_id: asset_id + :param app_connector: Object of app_connector class + :return: state: Current state file as a dictionary + """ + + asset_id = str(asset_id) + if not _is_valid_asset_id(asset_id): + if app_connector: + app_connector.debug_print('In _load_app_state: Invalid asset_id') + return {} + + state_file_path = _get_file_path(asset_id) + + state = {} + try: + with open(state_file_path, 'r') as state_file: + state = json.load(state_file) + except Exception as e: + if app_connector: + app_connector.error_print(f'In _load_app_state: Exception: {str(e)}') + + if app_connector: + app_connector.debug_print('Loaded state: ', state) + + try: + state = _decrypt_state(state, asset_id) + except Exception as e: + if app_connector: + app_connector.error_print("{}: {}".format(MS_AZURE_DECRYPTION_ERROR, str(e))) + state = {} + + return state + + +def _save_app_state(state, asset_id, app_connector): + """ This function is used to save current state in file. + + :param state: Dictionary which contains data to write in state file + :param asset_id: asset_id + :param app_connector: Object of app_connector class + :return: status: phantom.APP_SUCCESS + """ + asset_id = str(asset_id) + if not _is_valid_asset_id(asset_id): + if app_connector: + app_connector.debug_print('In _save_app_state: Invalid asset_id') + return {} + + state_file_path = _get_file_path(asset_id) + + try: + state = _encrypt_state(state, asset_id) + except Exception as e: + if app_connector: + app_connector.error_print("{}: {}".format(MS_AZURE_ENCRYPTION_ERROR, str(e))) + return phantom.APP_ERROR + + if app_connector: + app_connector.debug_print('Saving state: ', state) + + try: + with open(state_file_path, 'w+') as state_file: + json.dump(state, state_file) + except Exception as e: + if app_connector: + app_connector.error_print(f'Unable to save state file: {str(e)}') + + return phantom.APP_SUCCESS + + +def _handle_login_response(request): + """ This function is used to get the login response of authorization request from microsoft login page. + + :param request: Data given to REST endpoint + :return: HttpResponse. The response displayed on authorization URL page + """ + + asset_id = request.GET.get('state') + if not asset_id: + return HttpResponse(f'ERROR: Asset ID not found in URL\n{json.dumps(request.GET)}', + content_type="text/plain", status=MS_AZURE_BAD_REQUEST_CODE) + + # Check for error in URL + error = request.GET.get('error') + error_description = request.GET.get('error_description') + + # If there is an error in response + if error: + message = f'Error: {error}' + if error_description: + message = f'{message} Details: {error_description}' + return HttpResponse(f'Server returned {message}', content_type="text/plain", status=MS_AZURE_BAD_REQUEST_CODE) + + code = request.GET.get('code') + admin_consent = request.GET.get('admin_consent') + + # If none of the code or admin_consent is available + if not (code or admin_consent): + return HttpResponse(f'Error while authenticating\n{json.dumps(request.GET)}', + content_type="text/plain", status=MS_AZURE_BAD_REQUEST_CODE) + + state = _load_app_state(asset_id) + + # If value of admin_consent is available + if admin_consent: + if admin_consent == 'True': + admin_consent = True + else: + admin_consent = False + + state['admin_consent'] = admin_consent + _save_app_state(state, asset_id, None) + + # If admin_consent is True + if admin_consent: + return HttpResponse('Admin Consent received. Please close this window.', content_type="text/plain") + return HttpResponse('Admin Consent declined. Please close this window and try again later.', + content_type="text/plain", status=MS_AZURE_BAD_REQUEST_CODE) + + # If value of admin_consent is not available, value of code is available + state['code'] = code + _save_app_state(state, asset_id, None) + + return HttpResponse('Code received. Please close this window, the action will continue to get new token.', content_type="text/plain") + + +def _handle_rest_request(request, path_parts): + """ Handle requests for authorization. + + :param request: Data given to REST endpoint + :param path_parts: parts of the URL passed + :return: dictionary containing response parameters + """ + + if len(path_parts) < 2: + return HttpResponse('error: True, message: Invalid REST endpoint request', content_type="text/plain", status=MS_AZURE_BAD_REQUEST_CODE) + + call_type = path_parts[1] + + # To handle authorize request in test connectivity action + if call_type == 'start_oauth': + return _handle_login_redirect(request, 'admin_consent_url') + + # To handle response from microsoft login page + if call_type == 'result': + return_val = _handle_login_response(request) + asset_id = request.GET.get('state') + if asset_id: + if not _is_valid_asset_id(asset_id): + return HttpResponse("Error: Invalid asset_id", content_type="text/plain", status=MS_AZURE_BAD_REQUEST_CODE) + auth_status_file_path = _get_file_path(asset_id, is_state_file=False) + auth_status_file_path.touch(mode=664, exist_ok=True) + try: + uid = pwd.getpwnam('apache').pw_uid + gid = grp.getgrnam('phantom').gr_gid + os.chown(auth_status_file_path, uid, gid) # nosemgrep file traversal risk is handled by blocking non-alphanum strings + except Exception: + pass + + return return_val + return HttpResponse('error: Invalid endpoint', content_type="text/plain", status=MS_AZURE_NOT_FOUND_CODE) + + +def _get_dir_name_from_app_name(app_name): + """ Get name of the directory for the app. + + :param app_name: Name of the application for which directory name is required + :return: app_name: Name of the directory for the application + """ + + app_name = ''.join([x for x in app_name if x.isalnum()]) + app_name = app_name.lower() + if not app_name: + app_name = 'app_for_phantom' + return app_name + + +class RetVal(tuple): + + def __new__(cls, val1, val2): + + return tuple.__new__(RetVal, (val1, val2)) + + +class MSADGraphConnector(BaseConnector): + + def __init__(self): + + # Call the BaseConnectors init first + super(MSADGraphConnector, self).__init__() + + self._state = None + self._tenant = None + self._client_id = None + self._client_secret = None + self._access_token = None + self._refresh_token = None + self._base_url = None + self._admin_access_required = None + self._admin_access_granted = None + + def load_state(self): + """ + Load the contents of the state file to the state dictionary and decrypt it. + :return: loaded state + """ + state = super().load_state() + if not isinstance(state, dict): + self.debug_print("Reseting the state file with the default format") + state = { + "app_version": self.get_app_json().get('app_version') + } + return state + try: + state = _decrypt_state(state, self.get_asset_id()) + except Exception as e: + error_message = self._get_error_message_from_exception(e) + self.error_print("{}: {}".format(MS_AZURE_DECRYPTION_ERROR, error_message)) + self.debug_print("Reseting the state file with the default format") + state = { + "app_version": self.get_app_json().get('app_version') + } + + return state + + def save_state(self, state): + """ + Encrypt and save the current state dictionary to the the state file. + :param state: state dictionary + :return: status + """ + try: + state = _encrypt_state(state, self.get_asset_id()) + except Exception as e: + error_message = self._get_error_message_from_exception(e) + self.error_print("{}: {}".format(MS_AZURE_ENCRYPTION_ERROR, error_message)) + + return super().save_state(state) + + def _dump_error_log(self, error, message="Exception occurred."): + self.error_print(message, dump_object=error) + + def _get_error_message_from_exception(self, e): + """ + Get appropriate error message from the exception. + :param e: Exception object + :return: error message + """ + error_code = None + error_message = MS_AZURE_ERROR_MESSAGE_UNKNOWN + + self._dump_error_log(e) + + try: + if hasattr(e, "args"): + if len(e.args) > 1: + error_code = e.args[0] + error_message = e.args[1] + elif len(e.args) == 1: + error_message = e.args[0] + except Exception: + self.error_print("Exception occurred while getting error code and message") + + if not error_code: + error_text = "Error Message: {}".format(error_message) + else: + error_text = "Error Code: {}. Error Message: {}".format(error_code, error_message) + + return error_text + + def _process_empty_response(self, response, action_result): + """ This function is used to process empty response. + + :param response: response data + :param action_result: object of Action Result + :return: status phantom.APP_ERROR/phantom.APP_SUCCESS(along with appropriate message) + """ + + if response.status_code == 200 or response.status_code == 202: + return RetVal(phantom.APP_SUCCESS, {}) + + return RetVal(action_result.set_status(phantom.APP_ERROR, "Empty response and no information in the header"), + None) + + def _process_html_response(self, response, action_result): + """ This function is used to process html response. + + :param response: response data + :param action_result: object of Action Result + :return: status phantom.APP_ERROR/phantom.APP_SUCCESS(along with appropriate message) + """ + + # An html response, treat it like an error + status_code = response.status_code + + try: + soup = BeautifulSoup(response.text, "html.parser") + # Remove the script, style, footer and navigation part from the HTML message + for element in soup(["script", "style", "footer", "nav"]): + element.extract() + error_text = soup.text + split_lines = error_text.split('\n') + split_lines = [x.strip() for x in split_lines if x.strip()] + error_text = '\n'.join(split_lines) + except Exception: + error_text = "Cannot parse error details" + + message = MS_AZURE_RESPONSE_ERROR_MESSAGE.format(status_code=status_code, error_text=error_text) + + message = message.replace('{', '{{').replace('}', '}}') + + if status_code == MS_AZURE_BAD_REQUEST_CODE: + message = MS_AZURE_RESPONSE_ERROR_MESSAGE.format(status_code=status_code, error_text=MS_AZURE_HTML_ERROR) + + return RetVal(action_result.set_status(phantom.APP_ERROR, message), None) + + def _process_json_response(self, response, action_result): + """ This function is used to process json response. + + :param response: response data + :param action_result: object of Action Result + :return: status phantom.APP_ERROR/phantom.APP_SUCCESS(along with appropriate message) + """ + + # Try a json parse + try: + resp_json = response.json() + except Exception as e: + error_message = self._get_error_message_from_exception(e) + return RetVal(action_result.set_status(phantom.APP_ERROR, "Unable to parse JSON response. Error: {0}". + format(error_message)), None) + + # Please specify the status codes here + if 200 <= response.status_code < 399: + return RetVal(phantom.APP_SUCCESS, resp_json) + + error_message = response.text.replace('{', '{{').replace('}', '}}') + message = MS_AZURE_RESPONSE_ERROR_MESSAGE.format(status_code=response.status_code, error_text=error_message) + + # Show only error message if available + if isinstance(resp_json.get('error', {}), dict): + if resp_json.get('error', {}).get('message'): + error_message = resp_json['error']['message'] + message = MS_AZURE_RESPONSE_ERROR_MESSAGE.format(status_code=response.status_code, error_text=error_message) + else: + error_message = resp_json['error'] + message = MS_AZURE_RESPONSE_ERROR_MESSAGE.format(status_code=response.status_code, error_text=error_message) + + return RetVal(action_result.set_status(phantom.APP_ERROR, message), None) + + def _process_response(self, response, action_result): + """ This function is used to process html response. + + :param response: response data + :param action_result: object of Action Result + :return: status phantom.APP_ERROR/phantom.APP_SUCCESS(along with appropriate message) + """ + + # store the r_text in debug data, it will get dumped in the logs if the action fails + if hasattr(action_result, 'add_debug_data'): + action_result.add_debug_data({'r_status_code': response.status_code}) + action_result.add_debug_data({'r_text': response.text}) + action_result.add_debug_data({'r_headers': response.headers}) + + # Process each 'Content-Type' of response separately + + # Process a json response + if 'json' in response.headers.get('Content-Type', ''): + return self._process_json_response(response, action_result) + + if 'text/javascript' in response.headers.get('Content-Type', ''): + return self._process_json_response(response, action_result) + + # Process an HTML response, Do this no matter what the API talks. + # There is a high chance of a PROXY in between SOAR and the rest of + # world, in case of errors, PROXY's return HTML, this function parses + # the error and adds it to the action_result. + if 'html' in response.headers.get('Content-Type', ''): + return self._process_html_response(response, action_result) + + # Reset_password returns empty body + if not response.text and 200 <= response.status_code < 399: + return RetVal(phantom.APP_SUCCESS, {}) + + # it's not content-type that is to be parsed, handle an empty response + if not response.text: + return self._process_empty_response(response, action_result) + + # everything else is actually an error at this point + response_content = response.text.replace('{', '{{').replace('}', '}}') + message = MS_AZURE_PROCESS_RESPONSE_ERROR_MESSAGE.format(status_code=response.status_code, content=response_content) + + return RetVal(action_result.set_status(phantom.APP_ERROR, message), None) + + def _get_asset_name(self, action_result): + """ Get name of the asset using SOAR URL. + + :param action_result: object of ActionResult class + :return: status phantom.APP_ERROR/phantom.APP_SUCCESS(along with appropriate message), asset name + """ + + url = urlparse.urljoin(self.get_phantom_base_url(), f'rest/asset/{self._asset_id}') + ret_val, resp_json = self._make_rest_call(action_result=action_result, endpoint=url, verify=False) # nosemgrep + + if phantom.is_fail(ret_val): + return ret_val, None + + asset_name = resp_json.get('name') + if not asset_name: + return action_result.set_status(phantom.APP_ERROR, f'Asset Name for id: {self._asset_id} not found.'), None + return phantom.APP_SUCCESS, asset_name + + def _get_external_phantom_base_url(self, action_result): + """ Get base url of SOAR. + + :param action_result: object of ActionResult class + :return: status phantom.APP_ERROR/phantom.APP_SUCCESS(along with appropriate message), + base url of SOAR + """ + + url = urlparse.urljoin(self.get_phantom_base_url(), 'rest/system_info') + ret_val, resp_json = self._make_rest_call(action_result=action_result, endpoint=url, verify=False) # nosemgrep + if phantom.is_fail(ret_val): + return ret_val, None + + phantom_base_url = resp_json.get('base_url').rstrip("/") + if not phantom_base_url: + return action_result.set_status(phantom.APP_ERROR, MS_AZURE_BASE_URL_NOT_FOUND_MESSAGE), None + return phantom.APP_SUCCESS, phantom_base_url + + def _get_app_rest_url(self, action_result): + """ Get URL for making rest calls. + + :param action_result: object of ActionResult class + :return: status phantom.APP_ERROR/phantom.APP_SUCCESS(along with appropriate message), + URL to make rest calls + """ + + ret_val, phantom_base_url = self._get_external_phantom_base_url(action_result) + if phantom.is_fail(ret_val): + return action_result.get_status(), None + + ret_val, asset_name = self._get_asset_name(action_result) + if phantom.is_fail(ret_val): + return action_result.get_status(), None + + self.save_progress(f'Using SOAR base URL: {phantom_base_url}') + app_json = self.get_app_json() + app_id = app_json['appid'] + app_name = app_json['name'] + + app_dir_name = _get_dir_name_from_app_name(app_name) + url_to_app_rest = f"{phantom_base_url}/rest/handler/{app_dir_name}_{app_id}/{asset_name}" + return phantom.APP_SUCCESS, url_to_app_rest + + def _make_rest_call(self, endpoint, action_result, verify=True, headers=None, params=None, data=None, json=None, method="get"): + """ Function that makes the REST call to the app. + + :param endpoint: REST endpoint that needs to appended to the service address + :param action_result: object of ActionResult class + :param headers: request headers + :param params: request parameters + :param data: request body + :param json: JSON object + :param method: GET/POST/PUT/DELETE/PATCH (Default will be GET) + :param verify: verify server certificate (Default True) + :return: status phantom.APP_ERROR/phantom.APP_SUCCESS(along with appropriate message), + response obtained by making an API call + """ + + resp_json = None + + try: + request_func = getattr(requests, method) + except AttributeError: + return RetVal(action_result.set_status(phantom.APP_ERROR, f"Invalid method: {method}"), resp_json) + + try: + resp_json = request_func(endpoint, json=json, data=data, headers=headers, verify=verify, params=params, timeout=DEFAULT_TIMEOUT) + except Exception as e: + error_message = f"Error connecting to server. Details: {self._get_error_message_from_exception(e)}" + return RetVal(action_result.set_status(phantom.APP_ERROR, error_message), resp_json) + + return self._process_response(resp_json, action_result) + + def _make_rest_call_helper(self, action_result, endpoint, verify=True, headers=None, params=None, data=None, json=None, method="get"): + """ Function that helps setting REST call to the app. + + :param endpoint: REST endpoint that needs to appended to the service address + :param action_result: object of ActionResult class + :param headers: request headers + :param params: request parameters + :param data: request body + :param json: JSON object + :param method: GET/POST/PUT/DELETE/PATCH (Default will be GET) + :param verify: verify server certificate (Default True) + :return: status phantom.APP_ERROR/phantom.APP_SUCCESS(along with appropriate message), + response obtained by making an API call + """ + + url = f"{self._base_url}/{self._tenant}{endpoint}" + if headers is None: + headers = {} + + token = self._state.get(MS_AZURE_TOKEN_STRING, {}) + if not token.get(MS_AZURE_ACCESS_TOKEN_STRING): + ret_val = self._get_token(action_result) + + if phantom.is_fail(ret_val): + return RetVal(action_result.get_status(), None) + headers.update({ + 'Authorization': f'Bearer {self._access_token}', + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }) + ret_val, resp_json = self._make_rest_call(url, action_result, verify, headers, params, data, json, method) + + # If token is expired, generate a new token + message = action_result.get_message() + self.debug_print(f"message: {message}") + if message and ('token' in message and 'expired' in message): + self.save_progress("Token is invalid/expired. Hence, generating a new token.") + ret_val = self._get_token(action_result) + if phantom.is_fail(ret_val): + return RetVal(ret_val, None) + + headers.update({'Authorization': f'Bearer {self._access_token}'}) + + ret_val, resp_json = self._make_rest_call(url, action_result, verify, headers, params, data, json, method) + + if phantom.is_fail(ret_val): + return RetVal(ret_val, resp_json) + + return RetVal(phantom.APP_SUCCESS, resp_json) + + def _handle_generate_token(self, param): + + self.save_progress(f"In action handler for: {self.get_action_identifier()}") + action_result = self.add_action_result(ActionResult(dict(param))) + ret_val = self._get_token(action_result) + if phantom.is_fail(ret_val): + return action_result.get_status() + + self._state['admin_consent'] = True + + self.save_progress(f"Completed action handler for: {self.get_action_identifier()}") + return action_result.set_status(phantom.APP_SUCCESS, "Token generated") + + def _handle_test_connectivity(self, param): + """ Function that handles the test connectivity action, it is much simpler than other action handlers.""" + + # Progress + # self.save_progress("Generating Authentication URL") + app_state = {} + action_result = self.add_action_result(ActionResult(param)) + + if not (self._admin_access_required and self._admin_access_granted): + + self.save_progress("Getting App REST endpoint URL") + # Get the URL to the app's REST Endpoint, this is the url that the TC dialog + # box will ask the user to connect to + ret_val, app_rest_url = self._get_app_rest_url(action_result) + + if phantom.is_fail(ret_val): + self.save_progress(MS_REST_URL_NOT_AVAILABLE_MESSAGE.format(error=self.get_status())) + return self.set_status(phantom.APP_ERROR) + + # create the url that the oauth server should re-direct to after the auth is completed + # (success and failure), this is added to the state so that the request handler will access + # it later on + redirect_uri = f"{app_rest_url}/result" + app_state['redirect_uri'] = redirect_uri + + self.save_progress(MS_OAUTH_URL_MESSAGE) + self.save_progress(redirect_uri) + + self._client_id = urlparse.quote(self._client_id) + self._tenant = urlparse.quote(self._tenant) + + query_params = { + 'client_id': self._client_id, + 'redirect_uri': redirect_uri, + 'state': self._asset_id, + } + + if self._admin_access_required: + # Create the url for fetching administrator consent + admin_consent_url_base = MS_AZURE_ADMIN_CONSENT_URL.format(tenant_id=self._tenant) + else: + # Create the url authorization, this is the one pointing to the oauth server side + admin_consent_url_base = MS_AZURE_AUTHORIZE_URL.format(tenant_id=self._tenant) + query_params['scope'] = MS_AZURE_CODE_GENERATION_SCOPE + query_params['response_type'] = 'code' + + query_string = '&'.join(f'{key}={value}' for key, value in query_params.items()) + + admin_consent_url = f'{admin_consent_url_base}?{query_string}' + + app_state['admin_consent_url'] = admin_consent_url + + # The URL that the user should open in a different tab. + # This is pointing to a REST endpoint that points to the app + url_to_show = f"{app_rest_url}/start_oauth?asset_id={self._asset_id}&" + + # Save the state, will be used by the request handler + _save_app_state(app_state, self._asset_id, self) + + self.save_progress('Please connect to the following URL from a different tab to continue the connectivity process') + self.save_progress(url_to_show) + self.save_progress(MS_AZURE_AUTHORIZE_TROUBLESHOOT_MESSAGE) + + time.sleep(MS_AZURE_WAIT_FOR_URL_SLEEP) + + completed = False + + if not _is_valid_asset_id(self._asset_id): + return action_result.set_status(phantom.APP_ERROR, "Invalid asset id") + + auth_status_file_path = _get_file_path(self._asset_id, is_state_file=False) + + self.save_progress('Waiting for authorization to complete') + + for i in range(0, 40): + + self.send_progress('{0}'.format('.' * (i % 10))) + + if auth_status_file_path.is_file(): + completed = True + auth_status_file_path.unlink() + break + + time.sleep(MS_TC_STATUS_SLEEP) + + if not completed: + self.save_progress("Authentication process does not seem to be completed. Timing out") + self.save_progress(MS_AZURE_TEST_CONNECTIVITY_FAILURE_MESSAGE) + return self.set_status(phantom.APP_ERROR) + + self.send_progress("") + + # Load the state again, since the http request handlers would have saved the result of the admin consent + self._state = _load_app_state(self._asset_id, self) + + # Deleting the local state file because of it replicates with actual state file while installing the app + current_file_path = pathlib.Path(__file__).resolve() + input_file = f'{self._asset_id}_state.json' + state_file_path = current_file_path.with_name(input_file) + state_file_path.unlink() + + if not self._state: + self.save_progress(MS_STATE_FILE_ERROR_MESSAGE) + self.save_progress(MS_AZURE_TEST_CONNECTIVITY_FAILURE_MESSAGE) + return action_result.set_status(phantom.APP_ERROR) + + self._state.setdefault('admin_consent', False) + + if self._admin_access_required and not self._state.get('admin_consent'): + self.save_progress(MS_ADMIN_CONSENT_ERROR_MESSAGE) + self.save_progress(MS_AZURE_TEST_CONNECTIVITY_FAILURE_MESSAGE) + return action_result.set_status(phantom.APP_ERROR) + + if not self._admin_access_required and not self._state.get('code'): + self.save_progress(MS_AUTHORIZATION_ERROR_MESSAGE) + self.save_progress(MS_AZURE_TEST_CONNECTIVITY_FAILURE_MESSAGE) + return action_result.set_status(phantom.APP_ERROR) + + if self._admin_access_required: + self.save_progress("Admin consent received") + self.save_progress( + "Waiting for 30 seconds before generating token. If action fails with '403: AccessDenied' error, " + "please check permissions and re-run the 'test connectivity' after some time.") + self.save_progress( + "Admin consent is already received. You can mark 'Admin Consent Already Provided' to True, " + "unless you make changes in the permissions.") + time.sleep(30) + + self.save_progress(MS_GENERATING_ACCESS_TOKEN_MESSAGE) + ret_val = self._get_token(action_result) + if phantom.is_fail(ret_val): + return action_result.get_status() + + self.save_progress("Getting info about a single user to verify token") + params = {'$top': '1'} + ret_val, response = self._make_rest_call_helper(action_result, "/users", params=params) + + if phantom.is_fail(ret_val): + self.save_progress("API to get users failed") + self.save_progress(MS_AZURE_TEST_CONNECTIVITY_FAILURE_MESSAGE) + return self.set_status(phantom.APP_ERROR) + + value = response.get('value') + + if value: + self.save_progress("Got user info") + + self.save_progress(MS_AZURE_TEST_CONNECTIVITY_PASSED) + + return self.set_status(phantom.APP_SUCCESS) + + def _handle_list_users(self, param): + + self.save_progress(f"In action handler for: {self.get_action_identifier()}") + action_result = self.add_action_result(ActionResult(dict(param))) + + filter_string = param.get('filter_string') + select_string = param.get('select_string') + expand_string = param.get('expand_string') + use_advanced_query = param.get('use_advanced_query') + + headers = {} + parameters = {} + + if filter_string: + parameters['$filter'] = filter_string + if select_string: + select_string = [param_value.strip() for param_value in select_string.split(",")] + select_string = list(filter(None, select_string)) + parameters['$select'] = ','.join(param_value for param_value in select_string) + if expand_string: + parameters['$expand'] = expand_string + if use_advanced_query: + headers['ConsistencyLevel'] = 'eventual' + parameters['$count'] = 'true' + + ret_val = self._handle_pagination(action_result, '/users', headers=headers, params=parameters) + + if phantom.is_fail(ret_val): + return action_result.get_status() + + summary = action_result.update_summary({}) + resp_data = action_result.get_data() + if resp_data and resp_data[action_result.get_data_size() - 1] == 'Empty response': + summary['num_users'] = (action_result.get_data_size()) - 1 + else: + summary['num_users'] = action_result.get_data_size() + + self.save_progress(f"Completed action handler for: {self.get_action_identifier()}") + return action_result.set_status(phantom.APP_SUCCESS) + + def _handle_reset_password(self, param): + + self.save_progress(f"In action handler for: {self.get_action_identifier()}") + action_result = self.add_action_result(ActionResult(dict(param))) + + user_id = param['user_id'] + temp_password = param.get('temp_password', '') + force_change = param.get('force_change', True) + + data = { + 'passwordProfile': { + 'forceChangePasswordNextSignIn': force_change, + 'password': temp_password + } + } + + endpoint = f'/users/{user_id}' + + ret_val, _ = self._make_rest_call_helper(action_result, endpoint, json=data, method='patch') + + if phantom.is_fail(ret_val): + return ret_val + + summary = action_result.update_summary({}) + summary['status'] = f"Successfully reset password for {user_id}" + + # An empty response indicates success. No response body is returned. + self.save_progress(f"Completed action handler for: {self.get_action_identifier()}") + return action_result.set_status(phantom.APP_SUCCESS) + + def _handle_enable_user(self, param): + + self.save_progress(f"In action handler for: {self.get_action_identifier()}") + action_result = self.add_action_result(ActionResult(dict(param))) + + user_id = param['user_id'] + + data = { + "accountEnabled": True + } + + endpoint = f'/users/{user_id}' + ret_val, _ = self._make_rest_call_helper(action_result, endpoint, json=data, method='patch') + + if phantom.is_fail(ret_val): + return action_result.get_status() + + summary = action_result.update_summary({}) + summary['status'] = f"Successfully enabled user {user_id}" + + # An empty response indicates success. No response body is returned. + self.save_progress(f"Completed action handler for: {self.get_action_identifier()}") + return action_result.set_status(phantom.APP_SUCCESS) + + def _handle_invalidate_tokens(self, param): + + self.save_progress(f"In action handler for: {self.get_action_identifier()}") + action_result = self.add_action_result(ActionResult(dict(param))) + + user_id = param['user_id'] + endpoint = f'/users/{user_id}/revokeSignInSessions' + + ret_val, _ = self._make_rest_call_helper(action_result, endpoint, method='post') + + if phantom.is_fail(ret_val): + return action_result.get_status() + + summary = action_result.update_summary({}) + summary['status'] = "Successfully disabled tokens" + + self.save_progress(f"Completed action handler for: {self.get_action_identifier()}") + return action_result.set_status(phantom.APP_SUCCESS) + + def _handle_disable_user(self, param): + + self.save_progress(f"In action handler for: {self.get_action_identifier()}") + action_result = self.add_action_result(ActionResult(dict(param))) + + user_id = param['user_id'] + + data = { + "accountEnabled": False + } + + endpoint = f'/users/{user_id}' + ret_val, _ = self._make_rest_call_helper(action_result, endpoint, json=data, method='patch') + + if phantom.is_fail(ret_val): + return action_result.get_status() + + summary = action_result.update_summary({}) + summary['status'] = f"Successfully disabled user {user_id}" + + self.save_progress(f"Completed action handler for: {self.get_action_identifier()}") + return action_result.set_status(phantom.APP_SUCCESS) + + def _handle_list_user_attributes(self, param): + + self.save_progress(f"In action handler for: {self.get_action_identifier()}") + action_result = self.add_action_result(ActionResult(dict(param))) + + user_id = param.get('user_id') + select_string = param.get('select_string') + expand_string = param.get('expand_string') + use_advanced_query = param.get('use_advanced_query') + + headers = {} + parameters = {} + + if select_string: + select_string = [param_value.strip() for param_value in select_string.split(",")] + select_string = list(filter(None, select_string)) + parameters['$select'] = ','.join(param_value for param_value in select_string) + if expand_string: + parameters['$expand'] = expand_string + if use_advanced_query: + headers['ConsistencyLevel'] = 'eventual' + parameters['$count'] = 'true' + + if user_id: + endpoint = f'/users/{user_id}' + else: + endpoint = '/users' + + ret_val = self._handle_pagination(action_result, endpoint, headers=headers, params=parameters) + + if phantom.is_fail(ret_val): + return action_result.get_status() + + summary = action_result.update_summary({}) + if user_id: + summary['status'] = f"Successfully retrieved attributes for user {user_id}" + else: + summary['status'] = "Successfully retrieved user attributes" + + self.save_progress(f"Completed action handler for: {self.get_action_identifier()}") + return action_result.set_status(phantom.APP_SUCCESS) + + def _handle_list_user_devices(self, param): + + self.save_progress("In action handler for: {0}".format(self.get_action_identifier())) + action_result = self.add_action_result(ActionResult(dict(param))) + + user_id = param['user_id'] + + parameters = {} + select_string = param.get('select_string') + if select_string: + select_string = [param_value.strip() for param_value in select_string.split(",")] + select_string = list(filter(None, select_string)) + parameters['$select'] = ','.join(param_value for param_value in select_string) + + endpoint = f'/users/{user_id}/ownedDevices' + + ret_val = self._handle_pagination(action_result, endpoint, params=parameters) + + if phantom.is_fail(ret_val): + return action_result.get_status() + + summary = action_result.update_summary({}) + summary['status'] = "Successfully retrieved owned devices for user {}".format(user_id) + + return action_result.set_status(phantom.APP_SUCCESS) + + def _handle_set_user_attribute(self, param): + + self.save_progress(f"In action handler for: {self.get_action_identifier()}") + action_result = self.add_action_result(ActionResult(dict(param))) + + user_id = param['user_id'] + attribute = param['attribute'] + attribute_value = param['attribute_value'] + + data = { + attribute: attribute_value + } + + endpoint = f'/users/{user_id}' + ret_val, _ = self._make_rest_call_helper(action_result, endpoint, json=data, method='patch') + + if phantom.is_fail(ret_val): + return action_result.get_status() + + summary = action_result.update_summary({}) + summary['status'] = "Successfully updated user attribute" + + self.save_progress(f"Completed action handler for: {self.get_action_identifier()}") + return action_result.set_status(phantom.APP_SUCCESS) + + def _handle_add_user(self, param): + + config = self.get_config() + self.save_progress(f"In action handler for: {self.get_action_identifier()}") + action_result = self.add_action_result(ActionResult(dict(param))) + + object_id = param['group_object_id'] + user_id = param['user_id'] + + data = { + '@odata.id': "https://{}/directoryObjects/{}".format(MSADGRAPH_API_REGION[config.get(MS_AZURE_URL, "Global")], user_id) + } + + endpoint = f'/groups/{object_id}/members/$ref' + ret_val, _ = self._make_rest_call_helper(action_result, endpoint, json=data, method='post') + + summary = action_result.update_summary({}) + if phantom.is_fail(ret_val): + message = action_result.get_message() + if 'references already exist for the following modified properties: \'members\'.' in message: + summary['status'] = "User already in group" + return action_result.get_status() + else: + return ret_val + else: + summary['status'] = "Successfully added user to group" + + self.save_progress(f"Completed action handler for: {self.get_action_identifier()}") + return action_result.set_status(phantom.APP_SUCCESS) + + def _handle_remove_user(self, param): + + self.save_progress(f"In action handler for: {self.get_action_identifier()}") + action_result = self.add_action_result(ActionResult(dict(param))) + + object_id = param['group_object_id'] + user_id = param['user_id'] + + endpoint = f'/groups/{object_id}/members/{user_id}/$ref' + ret_val, _ = self._make_rest_call_helper(action_result, endpoint, method='delete') + + summary = action_result.update_summary({}) + if phantom.is_fail(ret_val): + message = action_result.get_message() + if 'does not exist or one of its queried' in message: + summary['status'] = "User not in group" + return action_result.get_status() + else: + summary['status'] = "Successfully removed user from group" + + self.save_progress(f"Completed action handler for: {self.get_action_identifier()}") + return action_result.set_status(phantom.APP_SUCCESS) + + def _handle_list_groups(self, param): + + self.save_progress(f"In action handler for: {self.get_action_identifier()}") + action_result = self.add_action_result(ActionResult(dict(param))) + + filter_string = param.get('filter_string') + select_string = param.get('select_string') + expand_string = param.get('expand_string') + use_advanced_query = param.get('use_advanced_query') + + headers = {} + parameters = {} + + if filter_string: + parameters['$filter'] = filter_string + if select_string: + select_string = [param_value.strip() for param_value in select_string.split(",")] + select_string = list(filter(None, select_string)) + parameters['$select'] = ','.join(param_value for param_value in select_string) + if expand_string: + parameters['$expand'] = expand_string + if use_advanced_query: + headers['ConsistencyLevel'] = 'eventual' + parameters['$count'] = 'true' + + ret_val = self._handle_pagination(action_result, '/groups', headers=headers, params=parameters) + + if phantom.is_fail(ret_val): + return action_result.get_status() + + summary = action_result.update_summary({}) + resp_data = action_result.get_data() + if resp_data and resp_data[action_result.get_data_size() - 1] == 'Empty response': + summary['num_groups'] = (action_result.get_data_size()) - 1 + else: + summary['num_groups'] = action_result.get_data_size() + + self.save_progress(f"Completed action handler for: {self.get_action_identifier()}") + return action_result.set_status(phantom.APP_SUCCESS) + + def _handle_get_group(self, param): + + self.save_progress(f"In action handler for: {self.get_action_identifier()}") + action_result = self.add_action_result(ActionResult(dict(param))) + + select_string = param.get('select_string') + expand_string = param.get('expand_string') + use_advanced_query = param.get('use_advanced_query') + + headers = {} + parameters = {} + + if select_string: + select_string = [param_value.strip() for param_value in select_string.split(",")] + select_string = list(filter(None, select_string)) + parameters['$select'] = ','.join(param_value for param_value in select_string) + if expand_string: + parameters['$expand'] = expand_string + if use_advanced_query: + headers['ConsistencyLevel'] = 'eventual' + parameters['$count'] = 'true' + + object_id = param['object_id'] + + endpoint = f'/groups/{object_id}' + + ret_val, response = self._make_rest_call_helper(action_result, endpoint, method='get', headers=headers, params=parameters) + + if phantom.is_fail(ret_val): + return action_result.get_status() + + action_result.add_data(response) + + summary = action_result.update_summary({}) + summary['status'] = f"Successfully retrieved group {object_id}" + + self.save_progress(f"Completed action handler for: {self.get_action_identifier()}") + return action_result.set_status(phantom.APP_SUCCESS) + + def _handle_list_group_members(self, param): + + self.save_progress(f"In action handler for: {self.get_action_identifier()}") + action_result = self.add_action_result(ActionResult(dict(param))) + + object_id = param['group_object_id'] + + select_string = param.get('select_string') + expand_string = param.get('expand_string') + use_advanced_query = param.get('use_advanced_query') + + headers = {} + parameters = {} + + if select_string: + select_string = [param_value.strip() for param_value in select_string.split(",")] + select_string = list(filter(None, select_string)) + parameters['$select'] = ','.join(param_value for param_value in select_string) + if expand_string: + parameters['$expand'] = expand_string + if use_advanced_query: + headers['ConsistencyLevel'] = 'eventual' + parameters['$count'] = 'true' + + endpoint = f'/groups/{object_id}/members' + + ret_val = self._handle_pagination(action_result, endpoint, headers=headers, params=parameters) + + if phantom.is_fail(ret_val): + return action_result.get_status() + + summary = action_result.update_summary({}) + summary['num_users'] = action_result.get_data_size() + + self.save_progress(f"Completed action handler for: {self.get_action_identifier()}") + return action_result.set_status(phantom.APP_SUCCESS) + + def _handle_list_directory_roles(self, param): + + self.save_progress(f"In action handler for: {self.get_action_identifier()}") + action_result = self.add_action_result(ActionResult(dict(param))) + + endpoint = '/directoryRoles' + ret_val, response = self._make_rest_call_helper(action_result, endpoint, method='get') + + if phantom.is_fail(ret_val): + return action_result.get_status() + + value = response.get('value', []) + for item in value: + action_result.add_data(item) + + summary = action_result.update_summary({}) + summary['num_directory_roles'] = len(value) + + self.save_progress(f"Completed action handler for: {self.get_action_identifier()}") + return action_result.set_status(phantom.APP_SUCCESS) + + def _handle_validate_group(self, param): + + self.save_progress(f"In action handler for: {self.get_action_identifier()}") + action_result = self.add_action_result(ActionResult(dict(param))) + + object_id = param['group_object_id'] + user_id = param['user_id'] + + endpoint = f'/users/{user_id}/memberOf?$filter=id eq \'{object_id}\'' + ret_val, response = self._make_rest_call_helper(action_result, endpoint, method='get') + + if phantom.is_fail(ret_val): + return action_result.get_status() + + user_id_map = {} + + for user in response.get('value', []): + user_id_map[user['id']] = user['displayName'] + + self.save_progress(f"Completed action handler for: {self.get_action_identifier()}") + return action_result.set_status(phantom.APP_SUCCESS, f"User is member of group: {ret_val}") + + def _get_token(self, action_result): + """ This function is used to get a token via REST Call. + + :param action_result: Object of action result + :return: status(phantom.APP_SUCCESS/phantom.APP_ERROR) + """ + + data = { + 'client_id': self._client_id, + 'client_secret': self._client_secret, + } + + req_url = SERVER_TOKEN_URL.format(self._tenant) + headers = { + 'Content-Type': 'application/x-www-form-urlencoded' + } + + if not self._admin_access_required: + data['scope'] = MS_AZURE_CODE_GENERATION_SCOPE + data['redirect_uri'] = self._state.get('redirect_uri') + auth_code = self._state.get('code', None) + if self._state.get(MS_AZURE_TOKEN_STRING, {}).get(MS_AZURE_REFRESH_TOKEN_STRING, None): + data['refresh_token'] = self._refresh_token + data['grant_type'] = 'refresh_token' + elif auth_code: + data['code'] = auth_code + data['grant_type'] = 'authorization_code' + else: + return action_result.set_status(phantom.APP_ERROR, "Unexpected details retrieved from the state file.") + else: + data['scope'] = 'https://graph.microsoft.com/.default' + data['grant_type'] = 'client_credentials' + + ret_val, resp_json = self._make_rest_call(req_url, action_result, headers=headers, data=data, method='post') + + if phantom.is_fail(ret_val): + return action_result.get_status() + + if self._admin_access_required and self._admin_access_granted: + self._state['admin_consent'] = True + + self._state[MS_AZURE_TOKEN_STRING] = resp_json + self._access_token = resp_json.get(MS_AZURE_ACCESS_TOKEN_STRING, None) + self._refresh_token = resp_json.get(MS_AZURE_REFRESH_TOKEN_STRING, None) + + return phantom.APP_SUCCESS + + def _handle_pagination(self, action_result, endpoint, headers=None, params=None): + """ + This action is used to create an iterator that will paginate through responses from called methods. + + :param action_result: Object of ActionResult class + :param endpoint: REST endpoint that needs to appended to the service address + :param headers: Dictionary of headers for the rest API calls + :param params: Dictionary of params for the rest API calls + """ + # maximum page size + page_size = MS_AZURE_PAGE_SIZE + if isinstance(params, dict): + params.update({"$top": page_size}) + else: + params = {"$top": page_size} + + while True: + + # make rest call + ret_val, response = self._make_rest_call_helper(action_result, endpoint, headers=headers, params=params, method='get') + + if phantom.is_fail(ret_val): + return None + + if "value" in response: + for user in response.get('value', []): + action_result.add_data(user) + if len(response.get('value')) > 0 and response.get('value')[0] == {}: + action_result.add_data('Empty response') + else: + action_result.add_data(response) + + if response.get(MS_AZURE_NEXT_LINK_STRING): + parsed_url = urlparse.urlparse(response.get(MS_AZURE_NEXT_LINK_STRING)) + try: + params['$skiptoken'] = urlparse.parse_qs(parsed_url.query).get('$skiptoken')[0] + except Exception: + self.debug_print(f"odata.nextLink is {response.get(MS_AZURE_NEXT_LINK_STRING)}") + self.debug_print("Error occurred while extracting skiptoken from the odata.nextLink") + break + else: + break + + return phantom.APP_SUCCESS + + def handle_action(self, param): + + ret_val = phantom.APP_SUCCESS + + # Get the action that we are supposed to execute for this App Run + action_id = self.get_action_identifier() + + self.debug_print("action_id", self.get_action_identifier()) + + if action_id == 'test_connectivity': + ret_val = self._handle_test_connectivity(param) + + elif action_id == 'list_users': + ret_val = self._handle_list_users(param) + + elif action_id == 'reset_password': + ret_val = self._handle_reset_password(param) + + elif action_id == 'invalidate_tokens': + ret_val = self._handle_invalidate_tokens(param) + + elif action_id == 'enable_user': + ret_val = self._handle_enable_user(param) + + elif action_id == 'disable_user': + ret_val = self._handle_disable_user(param) + + elif action_id == 'list_user_attributes': + ret_val = self._handle_list_user_attributes(param) + + elif action_id == 'set_user_attribute': + ret_val = self._handle_set_user_attribute(param) + + elif action_id == 'remove_user': + ret_val = self._handle_remove_user(param) + + elif action_id == 'add_user': + ret_val = self._handle_add_user(param) + + elif action_id == 'list_groups': + ret_val = self._handle_list_groups(param) + + elif action_id == 'get_group': + ret_val = self._handle_get_group(param) + + elif action_id == 'list_group_members': + ret_val = self._handle_list_group_members(param) + + elif action_id == 'validate_group': + ret_val = self._handle_validate_group(param) + + elif action_id == 'list_directory_roles': + ret_val = self._handle_list_directory_roles(param) + + elif action_id == 'generate_token': + ret_val = self._handle_generate_token(param) + + elif action_id == 'list_user_devices': + ret_val = self._handle_list_user_devices(param) + + return ret_val + + def initialize(self): + """ This is an optional function that can be implemented by the AppConnector derived class. Since the + configuration dictionary is already validated by the time this function is called, it's a good place to do any + extra initialization of any internal modules. This function MUST return a value of either phantom.APP_SUCCESS or + phantom.APP_ERROR. If this function returns phantom.APP_ERROR, then AppConnector::handle_action will not get + called. + """ + + self._state = self.load_state() + + # get the asset config + config = self.get_config() + self._asset_id = self.get_asset_id() + + self._tenant = config[MS_AZURE_CONFIG_TENANT] + self._client_id = config[MS_AZURE_CONFIG_CLIENT_ID] + self._client_secret = config[MS_AZURE_CONFIG_CLIENT_SECRET] + self._admin_access_required = config.get(MS_AZURE_CONFIG_ADMIN_ACCESS_REQUIRED, False) + self._admin_access_granted = config.get(MS_AZURE_CONFIG_ADMIN_ACCESS_GRANTED, False) + self._access_token = self._state.get(MS_AZURE_TOKEN_STRING, {}).get(MS_AZURE_ACCESS_TOKEN_STRING) + self._refresh_token = self._state.get(MS_AZURE_TOKEN_STRING, {}).get(MS_AZURE_REFRESH_TOKEN_STRING) + self._base_url = MSADGRAPH_API_URLS[config.get(MS_AZURE_URL, "Global")] + + return phantom.APP_SUCCESS + + def finalize(self): + + # Save the state, this data is saved across actions and app upgrades + self.save_state(self._state) + return phantom.APP_SUCCESS + + +if __name__ == '__main__': + + import argparse + + import pudb + + pudb.set_trace() + + argparser = argparse.ArgumentParser() + + argparser.add_argument('input_test_json', help='Input Test JSON file') + argparser.add_argument('-u', '--username', help='username', required=False) + argparser.add_argument('-p', '--password', help='password', required=False) + argparser.add_argument('-v', '--verify', action='store_true', help='verify', required=False, default=False) + + args = argparser.parse_args() + session_id = None + + username = args.username + password = args.password + verify = args.verify + + if username is not None and password is None: + + # User specified a username but not a password, so ask + import getpass + password = getpass.getpass("Password: ") + + if username and password: + login_url = BaseConnector._get_phantom_base_url() + "login" + try: + print("Accessing the Login page") + r = requests.get(login_url, verify=verify, timeout=60) + csrftoken = r.cookies['csrftoken'] + + data = dict() + data['username'] = username + data['password'] = password + data['csrfmiddlewaretoken'] = csrftoken + + headers = dict() + headers['Cookie'] = 'csrftoken=' + csrftoken + headers['Referer'] = login_url + + print("Logging into Platform to get the session id") + r2 = requests.post(login_url, verify=verify, data=data, headers=headers, timeout=60) + session_id = r2.cookies['sessionid'] + except Exception as e: + print("Unable to get session id from the platform. Error: " + str(e)) + sys.exit(1) + + with open(args.input_test_json) as f: + in_json = f.read() + in_json = json.loads(in_json) + print(json.dumps(in_json, indent=4)) + + connector = MSADGraphConnector() + connector.print_progress_message = True + + if session_id is not None: + in_json['user_session_token'] = session_id + connector._set_csrf_info(csrftoken, headers['Referer']) + + ret_val = connector._handle_action(json.dumps(in_json), None) + print(json.dumps(json.loads(ret_val), indent=4)) + + sys.exit(0) diff --git a/deployment-apps/msadgraph/msadgraph_consts.py b/deployment-apps/msadgraph/msadgraph_consts.py new file mode 100644 index 00000000..1f05ba95 --- /dev/null +++ b/deployment-apps/msadgraph/msadgraph_consts.py @@ -0,0 +1,86 @@ +# File: msadgraph_consts.py +# +# Copyright (c) 2022-2023 Splunk Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under +# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific language governing permissions +# and limitations under the License. + +PHANTOM_SYS_INFO_URL = "{base_url}rest/system_info" +PHANTOM_ASSET_INFO_URL = "{base_url}rest/asset/{asset_id}" + +MSADGRAPH_API_URLS = { + "Global": "https://graph.microsoft.com/v1.0", + "US Gov L4": "https://graph.microsoft.us", + "US Gov L5 (DOD)": "https://dod-graph.microsoft.us", + "Germany": "https://graph.microsoft.de", + "China (21Vianet)": "https://microsoftgraph.chinacloudapi.cn" +} +MSADGRAPH_API_REGION = { + "Global": "graph.microsoft.com/v1.0", + "US Gov L4": "graph.microsoft.us", + "US Gov L5 (DOD)": "dod-graph.microsoft.us", + "Germany": "graph.microsoft.de", + "China (21Vianet)": "microsoftgraph.chinacloudapi.cn" +} +MS_AZURE_CONFIG_TENANT = 'tenant_id' +MS_AZURE_CONFIG_SUBSCRIPTION = 'subscription_id' +MS_AZURE_CONFIG_CLIENT_ID = 'client_id' +MS_AZURE_CONFIG_CLIENT_SECRET = 'client_secret' # pragma: allowlist secret +MS_AZURE_CONFIG_ADMIN_ACCESS_REQUIRED = 'admin_access_required' +MS_AZURE_CONFIG_ADMIN_ACCESS_GRANTED = 'admin_access_granted' +MS_AZURE_URL = "region" +MS_AZURE_CONFIG_ADMIN_ACCESS = 'admin_access' +MS_AZURE_TOKEN_STRING = 'token' +MS_AZURE_ACCESS_TOKEN_STRING = 'access_token' +MS_AZURE_REFRESH_TOKEN_STRING = 'refresh_token' +MS_AZURE_PHANTOM_BASE_URL = '{phantom_base_url}rest' +MS_AZURE_PHANTOM_SYS_INFO_URL = '/system_info' +MS_AZURE_PHANTOM_ASSET_INFO_URL = '/asset/{asset_id}' +MS_AZURE_BASE_URL_NOT_FOUND_MESSAGE = 'SOAR Base URL not found in System Settings. ' \ + 'Please specify this value in System Settings.' +MS_AZURE_HTML_ERROR = 'Bad Request Bad Request - Invalid URL HTTP Error 400. The request URL is invalid.' +MS_AZURE_NEXT_LINK_STRING = 'odata.nextLink' +MS_AZURE_PAGE_SIZE = 999 +MS_AZURE_ERROR_MESSAGE_UNKNOWN = "Unknown error occurred. Please check the asset configuration and|or action parameters." + +# status codes +MS_AZURE_BAD_REQUEST_CODE = 400 +MS_AZURE_NOT_FOUND_CODE = 404 + +# For authorization code +SERVER_TOKEN_URL = "https://login.microsoftonline.com/{0}/oauth2/v2.0/token" +MS_AZURE_ADMIN_CONSENT_URL = "https://login.microsoftonline.com/{tenant_id}/adminconsent" +MS_AZURE_AUTHORIZE_URL = "https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/authorize" +MS_REST_URL_NOT_AVAILABLE_MESSAGE = 'Rest URL not available. Error: {error}' +MS_OAUTH_URL_MESSAGE = 'Using OAuth URL:\n' +MS_AUTHORIZE_USER_MESSAGE = 'Please authorize user in a separate tab using URL:' +MS_GENERATING_ACCESS_TOKEN_MESSAGE = 'Generating access token' +MS_TC_STATUS_SLEEP = 3 +MS_AZURE_WAIT_FOR_URL_SLEEP = 5 +MS_AZURE_CODE_GENERATION_SCOPE = 'offline_access Group.ReadWrite.All User.Read.All User.ReadWrite.All Directory.ReadWrite.All \ +Directory.AccessAsUser.All User.ManageIdentities.All GroupMember.ReadWrite.All RoleManagement.ReadWrite.Directory' +MS_AZURE_AUTHORIZE_TROUBLESHOOT_MESSAGE = 'If authorization URL fails to communicate with your SOAR instance, check whether you have: '\ + ' 1. Specified the Web Redirect URL of your App -- The Redirect URL should be /result . '\ + ' 2. Configured the base URL of your SOAR Instance at Administration -> Company Settings -> Info' + +MS_AZURE_TEST_CONNECTIVITY_FAILURE_MESSAGE = "Test Connectivity Failed" +MS_AZURE_TEST_CONNECTIVITY_PASSED = "Test Connectivity Passed" +MS_AZURE_ENCRYPTION_ERROR = "Error occurred while encrypting the state file" +MS_AZURE_DECRYPTION_ERROR = "Error occurred while decrypting the state file" +MS_AZURE_STATE_FILE_CORRUPT_ERROR = "Error occurred while loading the state file due to it's unexpected format. " \ + "Resetting the state file with the default format. Please test the connectivity." +MS_AZURE_RESPONSE_ERROR_MESSAGE = "Error from server. Status Code: {status_code}. Data from server: \n{error_text}\n" +MS_AZURE_PROCESS_RESPONSE_ERROR_MESSAGE = "Can't process response from server. Status Code: {status_code} Data from server: {content}" +MS_ADMIN_CONSENT_ERROR_MESSAGE = "Admin consent not received" +MS_AUTHORIZATION_ERROR_MESSAGE = "Authorization code not received or not given" +MS_STATE_FILE_ERROR_MESSAGE = "Unable to load state file" + +DEFAULT_TIMEOUT = 30 diff --git a/deployment-apps/msadgraph/msadgraph_get_group.html b/deployment-apps/msadgraph/msadgraph_get_group.html new file mode 100644 index 00000000..5f48c5e0 --- /dev/null +++ b/deployment-apps/msadgraph/msadgraph_get_group.html @@ -0,0 +1,170 @@ +{% extends 'widgets/widget_template.html' %} +{% load custom_template %} + +{% block custom_title_prop %}{% if title_logo %}style="background-size: auto 60%; background-position: 50%; +background-repeat: no-repeat; background-image: url('/app_resource/{{ title_logo }}');"{% endif %}{% endblock %} +{% block title1 %}{{ title1 }}{% endblock %} +{% block title2 %}{{ title2 }}{% endblock %} +{% block custom_tools %} +{% endblock %} + +{% block widget_content %} + + + + + +
+ + {% for result in results %} + + + {% if not result.data %} +

No data found

+ {% else %} + {% if result.param.select_string %} +

Group Details

+ {% for curr_data in result.data %} + + + + {% for key, value in curr_data.items %} + + {% endfor %} + + + + {% for key, value in curr_data.items %} + + {% endfor %} + +
{{ key }}
{{ value }}
+
+ {% endfor %} +
+ + {% else %} +

Group Details

+ + + + + + + + {% for curr_data in result.data %} + + + + + + {% endfor %} + +
Display NameGroup Object IdDescription
+ {{ curr_data.displayName }} + + + {{ curr_data.id }} +   + + + + {{ curr_data.description }} +
+
+ {% endif %} + {% endif %} +
+ {% endfor %} + + +
+ + + +{% endblock %} + diff --git a/deployment-apps/msadgraph/msadgraph_list_group_members.html b/deployment-apps/msadgraph/msadgraph_list_group_members.html new file mode 100644 index 00000000..092ae770 --- /dev/null +++ b/deployment-apps/msadgraph/msadgraph_list_group_members.html @@ -0,0 +1,174 @@ +{% extends 'widgets/widget_template.html' %} +{% load custom_template %} + +{% block custom_title_prop %}{% if title_logo %}style="background-size: auto 60%; background-position: 50%; +background-repeat: no-repeat; background-image: url('/app_resource/{{ title_logo }}');"{% endif %}{% endblock %} +{% block title1 %}{{ title1 }}{% endblock %} +{% block title2 %}{{ title2 }}{% endblock %} +{% block custom_tools %} +{% endblock %} + +{% block widget_content %} + + + + + +
+ + {% for result in results %} + + + {% if not result.data %} +

No data found

+ {% else %} + {% if result.param.select_string %} +

List of Group Members

+ {% for curr_data in result.data %} + + + + {% for key, value in curr_data.items %} + + {% endfor %} + + + + {% for key, value in curr_data.items %} + + {% endfor %} + +
{{ key }}
{{ value }}
+
+ {% endfor %} +
+ + {% else %} +

List of Group Members

+ + + + + + + + {% for curr_data in result.data %} + + + + + + {% endfor %} + +
Display NameUser Object IdUser Princicple Name
+ {{ curr_data.displayName }} + + + {{ curr_data.id }} +   + + + + + {{ curr_data.userPrincipalName }} +   + + +
+
+ {% endif %} + {% endif %} +
+ {% endfor %} + + +
+ + +{% endblock %} + diff --git a/deployment-apps/msadgraph/msadgraph_list_groups.html b/deployment-apps/msadgraph/msadgraph_list_groups.html new file mode 100644 index 00000000..ee345ef1 --- /dev/null +++ b/deployment-apps/msadgraph/msadgraph_list_groups.html @@ -0,0 +1,174 @@ +{% extends 'widgets/widget_template.html' %} +{% load custom_template %} + +{% block custom_title_prop %}{% if title_logo %}style="background-size: auto 60%; background-position: 50%; +background-repeat: no-repeat; background-image: url('/app_resource/{{ title_logo }}');"{% endif %}{% endblock %} +{% block title1 %}{{ title1 }}{% endblock %} +{% block title2 %}{{ title2 }}{% endblock %} +{% block custom_tools %} +{% endblock %} + +{% block widget_content %} + + + + + +
+ + {% for result in results %} + + + {% if not result.data %} +

No data found

+ {% else %} + {% if result.param.select_string %} + {% for curr_data in result.data %} + {% if curr_data == 'Empty response' %} +

Empty response. Please add valid property name

+ {% else %} +

Group

+ + + + {% for key, value in curr_data.items %} + + {% endfor %} + + + + {% for key, value in curr_data.items %} + + {% endfor %} + +
+
{{ key }}
{{ value }}
+ {% endif %} + {% endfor %} +
+ + {% else %} +

List of Groups

+ + + + + + + + {% for curr_data in result.data %} + + + + + + {% endfor %} + +
Group Object IdDisplay NameDescription
+ + {{ curr_data.id }} +   + + + + {{ curr_data.displayName }} + + {{ curr_data.description }} +
+
+ {% endif %} + {% endif %} +
+ {% endfor %} + + +
+ + + +{% endblock %} + diff --git a/deployment-apps/msadgraph/msadgraph_list_user_attributes.html b/deployment-apps/msadgraph/msadgraph_list_user_attributes.html new file mode 100644 index 00000000..c70548c4 --- /dev/null +++ b/deployment-apps/msadgraph/msadgraph_list_user_attributes.html @@ -0,0 +1,175 @@ +{% extends 'widgets/widget_template.html' %} +{% load custom_template %} + +{% block custom_title_prop %}{% if title_logo %}style="background-size: auto 60%; background-position: 50%; +background-repeat: no-repeat; background-image: url('/app_resource/{{ title_logo }}');"{% endif %}{% endblock %} +{% block title1 %}{{ title1 }}{% endblock %} +{% block title2 %}{{ title2 }}{% endblock %} +{% block custom_tools %} +{% endblock %} + +{% block widget_content %} + + + + + +
+ + {% for result in results %} + + + {% if not result.data %} +

No data found

+ {% else %} + {% if result.param.select_string %} +

User Attributes

+ {% for curr_data in result.data %} + + + + {% for key, value in curr_data.items %} + + {% endfor %} + + + + {% for key, value in curr_data.items %} + + {% endfor %} + +
{{ key }}
{{ value }}
+
+{% endfor %} +
+ + {% else %} +

User Attributes

+ + + + + + + + {% for curr_data in result.data %} + + + + + + {% endfor %} + +
Display NameUser Object IdUser Princicple Name
+ {{ curr_data.displayName }} + + + {{ curr_data.id }} +   + + + + + {{ curr_data.userPrincipalName }} +   + + +
+
+ {% endif %} + {% endif %} +
+ {% endfor %} + + +
+ + + +{% endblock %} + diff --git a/deployment-apps/msadgraph/msadgraph_list_user_devices.html b/deployment-apps/msadgraph/msadgraph_list_user_devices.html new file mode 100644 index 00000000..1f92afba --- /dev/null +++ b/deployment-apps/msadgraph/msadgraph_list_user_devices.html @@ -0,0 +1,163 @@ +{% extends 'widgets/widget_template.html' %} +{% load custom_template %} + +{% block custom_title_prop %}{% if title_logo %}style="background-size: auto 60%; background-position: 50%; +background-repeat: no-repeat; background-image: url('/app_resource/{{ title_logo }}');"{% endif %}{% endblock %} +{% block title1 %}{{ title1 }}{% endblock %} +{% block title2 %}{{ title2 }}{% endblock %} +{% block custom_tools %} +{% endblock %} + +{% block widget_content %} + + + + + +
+ + {% for result in results %} + + + {% if not result.data %} +

No data found

+ {% else %} + {% if result.param.select_string %} + {% for curr_data in result.data %} + {% if curr_data == 'Empty response' %} +

Empty response. Please add valid property name

+ {% else %} +

Device

+ + + + {% for key, value in curr_data.items %} + + {% endfor %} + + + + {% for key, value in curr_data.items %} + + {% endfor %} + +
+
{{ key }}
{{ value }}
+ {% endif %} + {% endfor %} +
+ + {% else %} +

List of User Devices

+ + + + + + + {% for curr_data in result.data %} + + + + + {% endfor %} + +
Display NameDevice ID
+ {{ curr_data.displayName }} + + {{ curr_data.deviceId }} +
+
+ {% endif %} + {% endif %} +
+ {% endfor %} + +
+ + + +{% endblock %} + diff --git a/deployment-apps/msadgraph/msadgraph_list_users.html b/deployment-apps/msadgraph/msadgraph_list_users.html new file mode 100644 index 00000000..a468d038 --- /dev/null +++ b/deployment-apps/msadgraph/msadgraph_list_users.html @@ -0,0 +1,177 @@ +{% extends 'widgets/widget_template.html' %} +{% load custom_template %} + +{% block custom_title_prop %}{% if title_logo %}style="background-size: auto 60%; background-position: 50%; +background-repeat: no-repeat; background-image: url('/app_resource/{{ title_logo }}');"{% endif %}{% endblock %} +{% block title1 %}{{ title1 }}{% endblock %} +{% block title2 %}{{ title2 }}{% endblock %} +{% block custom_tools %} +{% endblock %} + +{% block widget_content %} + + + + + +
+ + {% for result in results %} + + + {% if not result.data %} +

No data found

+ {% else %} + {% if result.param.select_string %} + {% for curr_data in result.data %} + {% if curr_data == 'Empty response' %} +

Empty response. Please add valid property name

+ {% else %} +

User

+ + + + {% for key, value in curr_data.items %} + + {% endfor %} + + + + {% for key, value in curr_data.items %} + + {% endfor %} + +
+
{{ key }}
{{ value }}
+ {% endif %} + {% endfor %} +
+ + {% else %} +

List of Users

+ + + + + + + + {% for curr_data in result.data %} + + + + + + {% endfor %} + +
Display NameUser Object IdUser Princicple Name
+ {{ curr_data.displayName }} + + + {{ curr_data.id }} +   + + + + + {{ curr_data.userPrincipalName }} +   + + +
+
+ {% endif %} + {% endif %} +
+ {% endfor %} + +
+ + + +{% endblock %} + diff --git a/deployment-apps/msadgraph/msadgraph_view.py b/deployment-apps/msadgraph/msadgraph_view.py new file mode 100644 index 00000000..83f9f9df --- /dev/null +++ b/deployment-apps/msadgraph/msadgraph_view.py @@ -0,0 +1,58 @@ +# File: msadgraph_view.py +# +# Copyright (c) 2022-2023 Splunk Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software distributed under +# the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the License for the specific language governing permissions +# and limitations under the License. +def get_ctx_result(provides, result): + + ctx_result = {} + param = result.get_param() + summary = result.get_summary() + data = result.get_data() + + ctx_result['param'] = param + ctx_result['action'] = provides + + if data: + ctx_result['data'] = data + + if summary: + ctx_result['summary'] = summary + + return ctx_result + + +def display_view(provides, all_app_runs, context): + + context['results'] = results = [] + for summary, action_results in all_app_runs: + for result in action_results: + + ctx_result = get_ctx_result(provides, result) + if (not ctx_result): + continue + results.append(ctx_result) + + if provides == "list users": + return_page = "msadgraph_list_users.html" + if provides == "list user attributes": + return_page = "msadgraph_list_user_attributes.html" + if provides == "list groups": + return_page = "msadgraph_list_groups.html" + if provides == "get group": + return_page = "msadgraph_get_group.html" + if provides == "list group members": + return_page = "msadgraph_list_group_members.html" + if provides == "list user devices": + return_page = "msadgraph_list_user_devices.html" + + return return_page diff --git a/deployment-apps/msadgraph/release_notes/1.0.5.md b/deployment-apps/msadgraph/release_notes/1.0.5.md new file mode 100644 index 00000000..f07690d4 --- /dev/null +++ b/deployment-apps/msadgraph/release_notes/1.0.5.md @@ -0,0 +1,2 @@ +* Initial release with Python3 support +* Note - This app is a replacement for the Azure AD app with Microsoft Graph API as Azure AD API is going to deprecate in near future \ No newline at end of file diff --git a/deployment-apps/msadgraph/release_notes/1.1.0.md b/deployment-apps/msadgraph/release_notes/1.1.0.md new file mode 100644 index 00000000..aa6dedd6 --- /dev/null +++ b/deployment-apps/msadgraph/release_notes/1.1.0.md @@ -0,0 +1 @@ +* Added 'list user devices' action \ No newline at end of file diff --git a/deployment-apps/msadgraph/release_notes/1.2.0.md b/deployment-apps/msadgraph/release_notes/1.2.0.md new file mode 100644 index 00000000..0a6c863b --- /dev/null +++ b/deployment-apps/msadgraph/release_notes/1.2.0.md @@ -0,0 +1,8 @@ +* Added option to use admin granted permissions +* Updated the 'test connectivity' action to allow non-interactive tests when admin consent is already provided +* Added 'expand string' and 'use advanced query' parameters in below five actions + * list users + * list groups + * list user attributes + * get group + * list group members \ No newline at end of file diff --git a/deployment-apps/msadgraph/release_notes/1.3.0.md b/deployment-apps/msadgraph/release_notes/1.3.0.md new file mode 100644 index 00000000..6a23a79b --- /dev/null +++ b/deployment-apps/msadgraph/release_notes/1.3.0.md @@ -0,0 +1,2 @@ +* Fixed the state file reseting logic [PAPP-30760] +* Removed django and requests dependencies in order to use platform packages [PAPP-31087, PAPP-31082, PAPP-31096, PAPP-30822] \ No newline at end of file diff --git a/deployment-apps/msadgraph/release_notes/1.4.0.md b/deployment-apps/msadgraph/release_notes/1.4.0.md new file mode 100644 index 00000000..88391987 --- /dev/null +++ b/deployment-apps/msadgraph/release_notes/1.4.0.md @@ -0,0 +1 @@ +* Fixed the token expiration issue [PAPP-31400] \ No newline at end of file diff --git a/deployment-apps/msadgraph/release_notes/unreleased.md b/deployment-apps/msadgraph/release_notes/unreleased.md new file mode 100644 index 00000000..fbcb2fd9 --- /dev/null +++ b/deployment-apps/msadgraph/release_notes/unreleased.md @@ -0,0 +1 @@ +**Unreleased** diff --git a/deployment-apps/msadgraph/wheels/py3/beautifulsoup4-4.9.1-py3-none-any.whl b/deployment-apps/msadgraph/wheels/py3/beautifulsoup4-4.9.1-py3-none-any.whl new file mode 100644 index 00000000..080b2f8e Binary files /dev/null and b/deployment-apps/msadgraph/wheels/py3/beautifulsoup4-4.9.1-py3-none-any.whl differ diff --git a/deployment-apps/msadgraph/wheels/py3/soupsieve-2.3.2.post1-py3-none-any.whl b/deployment-apps/msadgraph/wheels/py3/soupsieve-2.3.2.post1-py3-none-any.whl new file mode 100644 index 00000000..b363a9bc Binary files /dev/null and b/deployment-apps/msadgraph/wheels/py3/soupsieve-2.3.2.post1-py3-none-any.whl differ diff --git a/deployment-apps/msadgraph/wheels/py3/soupsieve-2.4.1-py3-none-any.whl b/deployment-apps/msadgraph/wheels/py3/soupsieve-2.4.1-py3-none-any.whl new file mode 100644 index 00000000..26a486cb Binary files /dev/null and b/deployment-apps/msadgraph/wheels/py3/soupsieve-2.4.1-py3-none-any.whl differ