|
19 | 19 |
|
20 | 20 | from feast.errors import (
|
21 | 21 | FeastFeatureServerTypeInvalidError,
|
| 22 | + FeastInvalidAuthConfigClass, |
22 | 23 | FeastOfflineStoreInvalidName,
|
23 | 24 | FeastOnlineStoreInvalidName,
|
24 | 25 | FeastRegistryNotSetError,
|
|
86 | 87 | "local": "feast.infra.feature_servers.local_process.config.LocalFeatureServerConfig",
|
87 | 88 | }
|
88 | 89 |
|
| 90 | +AUTH_CONFIGS_CLASS_FOR_TYPE = { |
| 91 | + "no_auth": "feast.permissions.auth_model.NoAuthConfig", |
| 92 | + "kubernetes": "feast.permissions.auth_model.K8AuthConfig", |
| 93 | + "oidc": "feast.permissions.auth_model.OidcAuthConfig", |
| 94 | +} |
| 95 | + |
89 | 96 |
|
90 | 97 | class FeastBaseModel(BaseModel):
|
91 | 98 | """Feast Pydantic Configuration Class"""
|
@@ -167,6 +174,9 @@ class RepoConfig(FeastBaseModel):
|
167 | 174 | online_config: Any = Field(None, alias="online_store")
|
168 | 175 | """ OnlineStoreConfig: Online store configuration (optional depending on provider) """
|
169 | 176 |
|
| 177 | + auth: Any = Field(None, alias="auth") |
| 178 | + """ auth: Optional if the services needs the authentication against IDPs (optional depending on provider) """ |
| 179 | + |
170 | 180 | offline_config: Any = Field(None, alias="offline_store")
|
171 | 181 | """ OfflineStoreConfig: Offline store configuration (optional depending on provider) """
|
172 | 182 |
|
@@ -211,6 +221,13 @@ def __init__(self, **data: Any):
|
211 | 221 | self._online_store = None
|
212 | 222 | self.online_config = data.get("online_store", "sqlite")
|
213 | 223 |
|
| 224 | + self._auth = None |
| 225 | + if "auth" not in data: |
| 226 | + self.auth = dict() |
| 227 | + self.auth["type"] = "no_auth" |
| 228 | + else: |
| 229 | + self.auth = data.get("auth") |
| 230 | + |
214 | 231 | self._batch_engine = None
|
215 | 232 | if "batch_engine" in data:
|
216 | 233 | self.batch_engine_config = data["batch_engine"]
|
@@ -270,6 +287,20 @@ def offline_store(self):
|
270 | 287 | self._offline_store = self.offline_config
|
271 | 288 | return self._offline_store
|
272 | 289 |
|
| 290 | + @property |
| 291 | + def auth_config(self): |
| 292 | + if not self._auth: |
| 293 | + if isinstance(self.auth, Dict): |
| 294 | + self._auth = get_auth_config_from_type(self.auth.get("type"))( |
| 295 | + **self.auth |
| 296 | + ) |
| 297 | + elif isinstance(self.auth, str): |
| 298 | + self._auth = get_auth_config_from_type(self.auth.get("type"))() |
| 299 | + elif self.auth: |
| 300 | + self._auth = self.auth |
| 301 | + |
| 302 | + return self._auth |
| 303 | + |
273 | 304 | @property
|
274 | 305 | def online_store(self):
|
275 | 306 | if not self._online_store:
|
@@ -300,6 +331,21 @@ def batch_engine(self):
|
300 | 331 |
|
301 | 332 | return self._batch_engine
|
302 | 333 |
|
| 334 | + @model_validator(mode="before") |
| 335 | + def _validate_auth_config(cls, values: Any) -> Any: |
| 336 | + if "auth" in values: |
| 337 | + allowed_auth_types = AUTH_CONFIGS_CLASS_FOR_TYPE.keys() |
| 338 | + if values["auth"].get("type") is None: |
| 339 | + raise ValueError( |
| 340 | + f"auth configuration is not having authentication type. Possible values={allowed_auth_types}" |
| 341 | + ) |
| 342 | + elif values["auth"]["type"] not in allowed_auth_types: |
| 343 | + raise ValueError( |
| 344 | + f'auth configuration is having invalid authentication type={values["auth"]["type"]}. Possible ' |
| 345 | + f'values={allowed_auth_types}' |
| 346 | + ) |
| 347 | + return values |
| 348 | + |
303 | 349 | @model_validator(mode="before")
|
304 | 350 | def _validate_online_store_config(cls, values: Any) -> Any:
|
305 | 351 | # This method will validate whether the online store configurations are set correctly. This explicit validation
|
@@ -480,6 +526,17 @@ def get_online_config_from_type(online_store_type: str):
|
480 | 526 | return import_class(module_name, config_class_name, config_class_name)
|
481 | 527 |
|
482 | 528 |
|
| 529 | +def get_auth_config_from_type(auth_config_type: str): |
| 530 | + if auth_config_type in AUTH_CONFIGS_CLASS_FOR_TYPE: |
| 531 | + auth_config_type = AUTH_CONFIGS_CLASS_FOR_TYPE[auth_config_type] |
| 532 | + elif not auth_config_type.endswith("AuthConfig"): |
| 533 | + raise FeastInvalidAuthConfigClass(auth_config_type) |
| 534 | + module_name, online_store_class_type = auth_config_type.rsplit(".", 1) |
| 535 | + config_class_name = f"{online_store_class_type}" |
| 536 | + |
| 537 | + return import_class(module_name, config_class_name, config_class_name) |
| 538 | + |
| 539 | + |
483 | 540 | def get_offline_config_from_type(offline_store_type: str):
|
484 | 541 | if offline_store_type in OFFLINE_STORE_CLASS_FOR_TYPE:
|
485 | 542 | offline_store_type = OFFLINE_STORE_CLASS_FOR_TYPE[offline_store_type]
|
|
0 commit comments