Skip to content

Commit d79900b

Browse files
committed
APP-5259: Added secure agent support to the OracleCrawler
1 parent 0ec575d commit d79900b

File tree

2 files changed

+123
-6
lines changed

2 files changed

+123
-6
lines changed

pyatlan/model/packages/base/crawler.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,13 @@ def build_flat_filter(raw_filter: Optional[list]) -> str:
127127
return dumps(to_include)
128128
except (AttributeError, TypeError):
129129
raise ErrorCode.UNABLE_TO_TRANSLATE_FILTERS.exception_with_parameters()
130+
131+
def _add_optional_params(self, params: Dict[str, Optional[Any]]) -> None:
132+
"""
133+
Helper method to add non-None params to `self._parameters`.
134+
135+
:param params: dict of param names and values.
136+
"""
137+
for name, value in params.items():
138+
if value is not None:
139+
self._parameters.append({"name": name, "value": value})

pyatlan/model/packages/oracle_crawler.py

Lines changed: 113 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from __future__ import annotations
22

3-
from typing import List, Optional
3+
from enum import Enum
4+
from json import dumps
5+
from typing import Any, Dict, List, Optional
46

57
from pyatlan.model.enums import AtlanConnectorType, WorkflowPackage
68
from pyatlan.model.packages.base.crawler import AbstractCrawler
@@ -34,6 +36,26 @@ class OracleCrawler(AbstractCrawler):
3436
"https://docs.oracle.com/sp_common/book-template/ohc-common/img/favicon.ico"
3537
)
3638

39+
class AuthType(str, Enum):
40+
BASIC = "basic"
41+
KERBEROS = "kerberos"
42+
43+
class AWS_AUTH_METHOD(str, Enum):
44+
IAM = "iam"
45+
IAM_ASSUME_ROLE = "iam-assume-role"
46+
ACCESS_KEY = "access-key"
47+
48+
class AZURE_AUTH_METHOD(str, Enum):
49+
MANAGED_IDENTITY = "managed_identity"
50+
SERVICE_PRINCIPAL = "service_principal"
51+
52+
class SecretStore(str, Enum):
53+
SECRET_INJECTION_ENV = "secretinjectionenvironment"
54+
AWS_SECRET_MANAGER = "awssecretmanager"
55+
AZURE_KEY_VAULT = "azurekeyvault"
56+
GCP_SECRET_MANAGER = "gcpsecretmanager"
57+
CUSTOM = "custom"
58+
3759
def __init__(
3860
self,
3961
connection_name: str,
@@ -45,6 +67,7 @@ def __init__(
4567
row_limit: int = 10000,
4668
):
4769
self._advanced_config = False
70+
self._agent_config = False
4871
super().__init__(
4972
connection_name=connection_name,
5073
connection_type=self._CONNECTOR_TYPE,
@@ -95,6 +118,75 @@ def direct(
95118
self._parameters.append(dict(name="extraction-method", value="direct"))
96119
return self
97120

121+
def agent_config(
122+
self,
123+
hostname: str,
124+
default_db_name: str,
125+
sid: str,
126+
agent_name: str,
127+
port: int = 1521,
128+
user_env_var: Optional[str] = None,
129+
password_env_var: Optional[str] = None,
130+
secret_store: SecretStore = SecretStore.CUSTOM,
131+
auth_type: AuthType = AuthType.BASIC,
132+
aws_region: str = "us-east-1",
133+
aws_auth_method: AWS_AUTH_METHOD = AWS_AUTH_METHOD.IAM,
134+
azure_auth_method: AZURE_AUTH_METHOD = AZURE_AUTH_METHOD.MANAGED_IDENTITY,
135+
secret_path: Optional[str] = None,
136+
principal: Optional[str] = None,
137+
azure_vault_name: Optional[str] = None,
138+
agent_custom_config: Optional[Dict[str, Any]] = None,
139+
) -> OracleCrawler:
140+
"""
141+
Configure the agent for Oracle extraction.
142+
143+
:param hostname: host address of the Oracle instance.
144+
:param default_db_name: default database name.
145+
:param sid: SID (system identifier) of the Oracle instance.
146+
:param agent_name: name of the agent.
147+
:param secret_store: secret store to use (e.g AWS, Azure, GCP, etc)
148+
:param port: port number for the Oracle instance. Defaults to `1521`.
149+
:param user_env_var: (optional) environment variable storing the username.
150+
:param password_env_var: (optional) environment variable storing the password.
151+
:param auth_type: authentication type (`basic` or `kerberos`). Defaults to `basic`.
152+
:param aws_region: AWS region where secrets are stored. Defaults to `us-east-1`.
153+
:param aws_auth_method: AWS authentication method (`iam`, `iam-assume-role`, `access-key`). Defaults to `iam`.
154+
:param azure_auth_method: Azure authentication method (`managed_identity` or `service_principal`). Defaults to `managed_identity`.
155+
:param secret_path: (optional) path to the secret in the secret manager.
156+
:param principal: (optional) Kerberos principal (required if using Kerberos authentication).
157+
:param azure_vault_name: (optional) Azure Key Vault name (required if using Azure secret store).
158+
:param agent_custom_config: (optional Custom JSON configuration for the agent.
159+
160+
:returns: crawler, set up to extraction from offline agent.
161+
"""
162+
self._agent_config = True
163+
_agent_dict = {
164+
"host": hostname,
165+
"port": port,
166+
"auth-type": auth_type,
167+
"database": default_db_name,
168+
"extra-service": sid,
169+
"agent-name": agent_name,
170+
"secret-manager": secret_store,
171+
"user-env": user_env_var,
172+
"password-env": password_env_var,
173+
"agent-config": agent_custom_config,
174+
"aws-auth-method": aws_auth_method,
175+
"aws-region": aws_region,
176+
"azure-auth-method": azure_auth_method,
177+
}
178+
if secret_path:
179+
_agent_dict["secret-path"] = secret_path
180+
if principal:
181+
_agent_dict["extra-principal"] = principal
182+
if agent_custom_config:
183+
_agent_dict["agent-config"] = agent_custom_config
184+
if azure_vault_name:
185+
_agent_dict["azure-vault-name"] = azure_vault_name
186+
self._parameters.append(dict(name="extraction-method", value="agent"))
187+
self._parameters.append(dict(name="agent-json", value=dumps(_agent_dict)))
188+
return self
189+
98190
def basic_auth(
99191
self,
100192
username: str,
@@ -133,7 +225,14 @@ def include(self, assets: dict) -> OracleCrawler:
133225
include_assets = assets or {}
134226
to_include = self.build_hierarchical_filter(include_assets)
135227
self._parameters.append(
136-
dict(dict(name="include-filter", value=to_include or "{}"))
228+
dict(
229+
dict(
230+
name="include-filter"
231+
if not self._agent_config
232+
else "include-filter-agent",
233+
value=to_include or "{}",
234+
)
235+
)
137236
)
138237
return self
139238

@@ -148,7 +247,14 @@ def exclude(self, assets: dict) -> OracleCrawler:
148247
"""
149248
exclude_assets = assets or {}
150249
to_exclude = self.build_hierarchical_filter(exclude_assets)
151-
self._parameters.append(dict(name="exclude-filter", value=to_exclude or "{}"))
250+
self._parameters.append(
251+
dict(
252+
name="exclude-filter"
253+
if not self._agent_config
254+
else "exclude-filter-agent",
255+
value=to_exclude or "{}",
256+
)
257+
)
152258
return self
153259

154260
def exclude_regex(self, regex: str) -> OracleCrawler:
@@ -199,9 +305,6 @@ def _set_required_metadata_params(self):
199305
self._parameters.append(
200306
{"name": "credentials-fetch-strategy", "value": "credential_guid"}
201307
)
202-
self._parameters.append(
203-
{"name": "credential-guid", "value": "{{credentialGuid}}"}
204-
)
205308
self._parameters.append(dict(name="publish-mode", value="production"))
206309
self._parameters.append(dict(name="atlas-auth-type", value="internal"))
207310
self._parameters.append(
@@ -218,6 +321,10 @@ def _set_required_metadata_params(self):
218321
),
219322
}
220323
)
324+
if not self._agent_config:
325+
self._parameters.append(
326+
{"name": "credential-guid", "value": "{{credentialGuid}}"}
327+
)
221328

222329
def _get_metadata(self) -> WorkflowMetadata:
223330
self._set_required_metadata_params()

0 commit comments

Comments
 (0)