Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 61 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ To successfully run Test Connectivity, you need at least one of these permission
**Mailbox Settings**

- `MailboxSettings.Read` - Out-of-office status, mail rules
- `MailboxSettings.ReadWrite` - Modify mail rules

#### **Add Permissions in Azure AD**

Expand Down Expand Up @@ -279,6 +280,11 @@ Test Connectivity needs at least one of these permissions:
- **Basic**: `User.Read.All` + `Group.Read.All`
- **Advanced**: Add `GroupMember.Read.All` for detailed group operations

#### **Mail Rule Management Set**

- **Read-only rule access**: `MailboxSettings.Read`
- **Rule management**: `MailboxSettings.ReadWrite` for `disable rule` and `delete rule`

### Detailed Action Permissions Table

| Action | Minimum Required (Del) | Full Functionality (App) | Notes |
Expand Down Expand Up @@ -312,6 +318,8 @@ Test Connectivity needs at least one of these permissions:
| oof check | `MailboxSettings.Read` | `MailboxSettings.Read` | Out-of-office status |
| get rule | `MailboxSettings.Read` | `MailboxSettings.Read` | Mail rules |
| list rules | `MailboxSettings.Read` | `MailboxSettings.Read` | Mail rules |
| disable rule | `MailboxSettings.ReadWrite` | `MailboxSettings.ReadWrite` | Modify mail rules |
| delete rule | `MailboxSettings.ReadWrite` | `MailboxSettings.ReadWrite` | Modify mail rules |
| **Polling** | | | |
| on poll | `Mail.ReadBasic` | `Mail.Read` | ReadBasic for basic polling |

Expand Down Expand Up @@ -628,7 +636,9 @@ VARIABLE | REQUIRED | TYPE | DESCRIPTION
[list rules](#action-list-rules) - Get all the messageRule objects defined for the user's inbox <br>
[resolve name](#action-resolve-name) - Resolve a name to email addresses <br>
[run query](#action-run-query) - Search emails in a mailbox <br>
[block sender](#action-block-sender) - Add a sender to the blocked senders list
[block sender](#action-block-sender) - Add a sender to the blocked senders list <br>
[delete rule](#action-delete-rule) - Delete inbox rule by ID <br>
[disable rule](#action-disable-rule) - Disable inbox rule by ID

## action: 'test connectivity'

Expand Down Expand Up @@ -1523,6 +1533,56 @@ action_result.data.\*.message | string | | |
summary.total_objects | numeric | | 1 |
summary.total_objects_successful | numeric | | 1 |

## action: 'delete rule'

Delete inbox rule by ID

Type: **contain** <br>
Read only: **True**

#### Action Parameters

PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS
--------- | -------- | ----------- | ---- | --------
**user_id** | required | User ID/Principal name | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` |
**rule_id** | required | Inbox rule ID | string | `msgoffice365 rule id` |

#### Action Output

DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES
--------- | ---- | -------- | --------------
action_result.status | string | | success failure |
action_result.message | string | | |
action_result.parameter.user_id | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | |
action_result.parameter.rule_id | string | `msgoffice365 rule id` | |
summary.total_objects | numeric | | 1 |
summary.total_objects_successful | numeric | | 1 |

## action: 'disable rule'

Disable inbox rule by ID

Type: **contain** <br>
Read only: **True**

#### Action Parameters

PARAMETER | REQUIRED | DESCRIPTION | TYPE | CONTAINS
--------- | -------- | ----------- | ---- | --------
**user_id** | required | User ID/Principal name | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` |
**rule_id** | required | Inbox rule ID | string | `msgoffice365 rule id` |

#### Action Output

DATA PATH | TYPE | CONTAINS | EXAMPLE VALUES
--------- | ---- | -------- | --------------
action_result.status | string | | success failure |
action_result.message | string | | |
action_result.parameter.user_id | string | `msgoffice365 user id` `msgoffice365 user principal name` `email` | |
action_result.parameter.rule_id | string | `msgoffice365 rule id` | |
summary.total_objects | numeric | | 1 |
summary.total_objects_successful | numeric | | 1 |

______________________________________________________________________

Auto-generated Splunk SOAR Connector documentation.
Expand Down
8 changes: 8 additions & 0 deletions manual_readme_content.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ To successfully run Test Connectivity, you need at least one of these permission
**Mailbox Settings**

- `MailboxSettings.Read` - Out-of-office status, mail rules
- `MailboxSettings.ReadWrite` - Modify mail rules

#### **Add Permissions in Azure AD**

Expand Down Expand Up @@ -269,6 +270,11 @@ Test Connectivity needs at least one of these permissions:
- **Basic**: `User.Read.All` + `Group.Read.All`
- **Advanced**: Add `GroupMember.Read.All` for detailed group operations

#### **Mail Rule Management Set**

- **Read-only rule access**: `MailboxSettings.Read`
- **Rule management**: `MailboxSettings.ReadWrite` for `disable rule` and `delete rule`

### Detailed Action Permissions Table

| Action | Minimum Required (Del) | Full Functionality (App) | Notes |
Expand Down Expand Up @@ -302,6 +308,8 @@ Test Connectivity needs at least one of these permissions:
| oof check | `MailboxSettings.Read` | `MailboxSettings.Read` | Out-of-office status |
| get rule | `MailboxSettings.Read` | `MailboxSettings.Read` | Mail rules |
| list rules | `MailboxSettings.Read` | `MailboxSettings.Read` | Mail rules |
| disable rule | `MailboxSettings.ReadWrite` | `MailboxSettings.ReadWrite` | Modify mail rules |
| delete rule | `MailboxSettings.ReadWrite` | `MailboxSettings.ReadWrite` | Modify mail rules |
| **Polling** | | | |
| on poll | `Mail.ReadBasic` | `Mail.Read` | ReadBasic for basic polling |

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ dependencies = [
"beautifulsoup4>=4.13.0,<4.14.0",
"msal>=1.35.1",
"requests>=2.33.1",
"splunk-soar-sdk[email]>=3.18.1",
"splunk-soar-sdk[email]>=3.20.0",
]

# [tool.uv.sources]
Expand Down
2 changes: 2 additions & 0 deletions release_notes/unreleased.md
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
**Unreleased**

* Added `delete rule` and `disable rule` inbox rule actions
2 changes: 2 additions & 0 deletions src/actions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
create_folder,
delete_email,
delete_event,
delete_rule,
disable_rule,
generate_token,
get_email,
get_email_properties,
Expand Down
47 changes: 47 additions & 0 deletions src/actions/delete_rule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Copyright (c) 2017-2026 Splunk Inc.
from typing import TYPE_CHECKING

from soar_sdk.abstract import SOARClient
from soar_sdk.action_results import ActionOutput
from soar_sdk.params import Param, Params


if TYPE_CHECKING:
from ..app import Asset
from ..app import app
from ..helper import MsGraphHelper


class DeleteRuleParams(Params):
user_id: str = Param(
description="User ID/Principal name",
required=True,
primary=True,
cef_types=["msgoffice365 user id", "msgoffice365 user principal name", "email"],
)
rule_id: str = Param(
description="Inbox rule ID",
required=True,
primary=True,
cef_types=["msgoffice365 rule id"],
)


class DeleteRuleOutput(ActionOutput):
pass


@app.action(description="Delete inbox rule by ID", action_type="contain")
def delete_rule(
params: DeleteRuleParams, soar: SOARClient, asset: "Asset"
) -> DeleteRuleOutput:
helper = MsGraphHelper(soar, asset)
helper.get_token()

endpoint = (
f"/users/{params.user_id}/mailFolders/inbox/messageRules/{params.rule_id}"
)
helper.make_rest_call_helper(endpoint, method="delete")

soar.set_message(f"Successfully deleted rule: {params.rule_id}")
return DeleteRuleOutput()
50 changes: 50 additions & 0 deletions src/actions/disable_rule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright (c) 2017-2026 Splunk Inc.
import json
from typing import TYPE_CHECKING

from soar_sdk.abstract import SOARClient
from soar_sdk.action_results import ActionOutput
from soar_sdk.params import Param, Params


if TYPE_CHECKING:
from ..app import Asset
from ..app import app
from ..helper import MsGraphHelper


class DisableRuleParams(Params):
user_id: str = Param(
description="User ID/Principal name",
required=True,
primary=True,
cef_types=["msgoffice365 user id", "msgoffice365 user principal name", "email"],
)
rule_id: str = Param(
description="Inbox rule ID",
required=True,
primary=True,
cef_types=["msgoffice365 rule id"],
)


class DisableRuleOutput(ActionOutput):
pass


@app.action(description="Disable inbox rule by ID", action_type="contain")
def disable_rule(
params: DisableRuleParams, soar: SOARClient, asset: "Asset"
) -> DisableRuleOutput:
helper = MsGraphHelper(soar, asset)
helper.get_token()

endpoint = (
f"/users/{params.user_id}/mailFolders/inbox/messageRules/{params.rule_id}"
)
helper.make_rest_call_helper(
endpoint, method="patch", data=json.dumps({"isEnabled": False})
)

soar.set_message(f"Successfully disabled rule: {params.rule_id}")
return DisableRuleOutput()
24 changes: 14 additions & 10 deletions src/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import requests
from soar_sdk.abstract import SOARClient
from soar_sdk.exceptions import ActionFailure
from soar_sdk.logging import getLogger

from .consts import (
Expand Down Expand Up @@ -69,15 +70,15 @@ def _generate_cba_access_token(self):
)
except ValueError as e:
if "private_key" in str(e).lower():
raise Exception(MSGOFFICE365_CBA_KEY_ERROR) from None
raise ActionFailure(MSGOFFICE365_CBA_KEY_ERROR) from None
raise

res_json = app_client.acquire_token_for_client(
scopes=[MSGOFFICE365_DEFAULT_SCOPE]
)
if error := res_json.get("error"):
error_message = f"{error}: {res_json.get('error_description')}"
raise Exception(error_message)
raise ActionFailure(error_message)
return res_json

def _generate_oauth_access_token(self):
Expand Down Expand Up @@ -177,15 +178,15 @@ def _make_rest_call(
if download:
if 200 <= resp.status_code < 399:
return resp.text
raise Exception(f"Error downloading: {resp.status_code}")
raise ActionFailure(f"Error downloading: {resp.status_code}")

if resp.status_code == 204:
return {}

if not resp.text:
if 200 <= resp.status_code < 399:
return {}
raise Exception(f"Empty response with status {resp.status_code}")
raise ActionFailure(f"Empty response with status {resp.status_code}")

if "json" in resp.headers.get("Content-Type", ""):
resp_json = resp.json()
Expand All @@ -197,10 +198,10 @@ def _make_rest_call(
if isinstance(error, dict)
else str(error)
)
raise Exception(f"API Error {resp.status_code}: {error_msg}")
raise ActionFailure(f"API Error {resp.status_code}: {error_msg}")

if resp.status_code >= 400:
raise Exception(f"Error {resp.status_code}: {resp.text[:500]}")
raise ActionFailure(f"Error {resp.status_code}: {resp.text[:500]}")
return {}

def make_rest_call_helper(
Expand All @@ -223,7 +224,7 @@ def make_rest_call_helper(
return self._make_rest_call(
url, method=method, params=params, data=data, download=download
)
except Exception as e:
except ActionFailure as e:
error_msg = str(e)
if any(msg in error_msg for msg in MSGOFFICE365_AUTH_FAILURE_MSG):
logger.info("Token expired, refreshing...")
Expand All @@ -235,9 +236,12 @@ def make_rest_call_helper(
state.pop(auth_key, None)
self._save_auth_state(state)
self.get_token()
return self._make_rest_call(
url, method=method, params=params, data=data, download=download
)
try:
return self._make_rest_call(
url, method=method, params=params, data=data, download=download
)
except ActionFailure as refresh_error:
raise ActionFailure(str(refresh_error)) from None
raise

def get_folder_id(self, folder_name: str, email_address: str) -> str | None:
Expand Down
Loading
Loading