diff --git a/sld-api-backend/src/aws/api/container/create.py b/sld-api-backend/src/aws/api/container/create.py index d33358ec..5a2f2e00 100644 --- a/sld-api-backend/src/aws/api/container/create.py +++ b/sld-api-backend/src/aws/api/container/create.py @@ -22,13 +22,16 @@ async def create_new_aws_profile( status_code=409, detail="The squad or environment field must have a value that is not a string.", ) - db_aws_account = crud_aws.get_squad_aws_profile( - db=db, squad=aws.squad, environment=aws.environment + filters = schemas_aws.AwsAccountFilter() + filters.squad = aws.squad + filters.environment = aws.environment + db_aws_account = crud_aws.get_all_aws_profile( + db=db, filters=filters ) if db_aws_account: raise HTTPException(status_code=409, detail="Account already exists") try: - result = crud_aws.create_aws_profile(db=db, aws=aws) + crud_aws.create_aws_profile(db=db, aws=aws) crud_activity.create_activity_log( db=db, username=current_user.username, diff --git a/sld-api-backend/src/aws/api/container/get.py b/sld-api-backend/src/aws/api/container/get.py index 10bf4462..0986bf58 100644 --- a/sld-api-backend/src/aws/api/container/get.py +++ b/sld-api-backend/src/aws/api/container/get.py @@ -2,18 +2,20 @@ from sqlalchemy.orm import Session from src.aws.infrastructure import repositories as crud_aws +from src.aws.domain.entities import aws as schemas_aws from src.shared.security import deps from src.users.domain.entities import users as schemas_users from src.users.infrastructure import repositories as crud_users async def get_all_aws_accounts( - current_user: schemas_users.User = Depends(deps.get_current_active_user), + skip: int = 0, + limit: int = 100, db: Session = Depends(deps.get_db), + current_user: schemas_users.User = Depends(deps.get_current_active_user), + filters: schemas_aws.AwsAccountFilter = Depends(schemas_aws.AwsAccountFilter), + ): - # Check if the user has privileges if not crud_users.is_master(db, current_user): - return crud_aws.get_squad_aws_profile( - db=db, squad=current_user.squad, environment=None - ) - return crud_aws.get_all_aws_profile(db=db) + filters.squad = current_user.squad + return crud_aws.get_all_aws_profile(db=db, filters=filters, skip=skip, limit=limit) diff --git a/sld-api-backend/src/aws/api/v1/aws.py b/sld-api-backend/src/aws/api/v1/aws.py index 649c03f4..0c15f0f1 100644 --- a/sld-api-backend/src/aws/api/v1/aws.py +++ b/sld-api-backend/src/aws/api/v1/aws.py @@ -17,7 +17,7 @@ async def create_new_aws_profile( @router.get("/", status_code=200, response_model=list[schemas_aws.AwsAccountResponse]) async def get_all_aws_accounts( - get_aws_profile: schemas_aws.AwsAsumeProfile = Depends(get.get_all_aws_accounts), + get_aws_profile: schemas_aws.AwsAccountResponse = Depends(get.get_all_aws_accounts), ): return get_aws_profile diff --git a/sld-api-backend/src/aws/domain/entities/aws.py b/sld-api-backend/src/aws/domain/entities/aws.py index 6cbfe477..6b486ba0 100644 --- a/sld-api-backend/src/aws/domain/entities/aws.py +++ b/sld-api-backend/src/aws/domain/entities/aws.py @@ -1,23 +1,19 @@ -from typing import Optional +from typing import Optional, Dict, Any -from pydantic import BaseModel, Field, constr +from pydantic import BaseModel, constr class AwsBase(BaseModel): squad: constr(strip_whitespace=True) environment: constr(strip_whitespace=True) access_key_id: constr(strip_whitespace=True) - secret_access_key: Optional[constr(strip_whitespace=True)] = Field( - None, example="string" - ) + secret_access_key: constr(strip_whitespace=True) default_region: constr(strip_whitespace=True) + extra_variables: Optional[Dict[str, Any]] = None - default_region: constr(strip_whitespace=True) class AwsAsumeProfile(AwsBase): - profile_name: Optional[constr(strip_whitespace=True)] = None role_arn: Optional[constr(strip_whitespace=True)] = None - source_profile: Optional[constr(strip_whitespace=True)] = None class Aws(AwsBase): @@ -26,10 +22,31 @@ class Aws(AwsBase): class Config: from_attributes = True + class AwsAccountResponse(BaseModel): id: int - squad: constr(strip_whitespace=True) - environment: constr(strip_whitespace=True) - profile_name: Optional[constr(strip_whitespace=True)] = None - role_arn: Optional[constr(strip_whitespace=True)] = None - source_profile: Optional[constr(strip_whitespace=True)] = None \ No newline at end of file + squad: str + environment: str + default_region: Optional[str] + role_arn: Optional[str] + extra_variables: Optional[Dict[str, Any]] + + class Config: + from_attributes = True + + +class AwsAccountResponseRepo(AwsAccountResponse): + access_key_id: str + secret_access_key: str + + class Config: + from_attributes = True + + +class AwsAccountFilter(BaseModel): + id: Optional[int] = None + squad: Optional[str] = None + access_key_id: Optional[str] = None + environment: Optional[str] = None + default_region: Optional[str] = None + role_arn: Optional[str] = None diff --git a/sld-api-backend/src/aws/infrastructure/models.py b/sld-api-backend/src/aws/infrastructure/models.py index a84abb38..6c34e3d5 100644 --- a/sld-api-backend/src/aws/infrastructure/models.py +++ b/sld-api-backend/src/aws/infrastructure/models.py @@ -1,7 +1,7 @@ import datetime from config.database import Base -from sqlalchemy import Column, DateTime, Integer, String, UniqueConstraint +from sqlalchemy import Column, DateTime, Integer, String, UniqueConstraint, JSON class Aws_provider(Base): @@ -12,8 +12,9 @@ class Aws_provider(Base): access_key_id = Column(String(200), nullable=False) secret_access_key = Column(String(200), nullable=False) default_region = Column(String(200)) - profile_name = Column(String(200), nullable=False) + profile_name = Column(String(200), nullable=True) role_arn = Column(String(200), nullable=True) source_profile = Column(String(200), nullable=True) + extra_variables = Column(JSON, nullable=True) created_at = Column(DateTime, default=datetime.datetime.now()) __table_args__ = (UniqueConstraint("squad", "environment"),) diff --git a/sld-api-backend/src/aws/infrastructure/repositories.py b/sld-api-backend/src/aws/infrastructure/repositories.py index 22497605..ea83e6e7 100644 --- a/sld-api-backend/src/aws/infrastructure/repositories.py +++ b/sld-api-backend/src/aws/infrastructure/repositories.py @@ -1,10 +1,13 @@ import datetime +from typing import List from sqlalchemy.orm import Session +from sqlalchemy import desc, or_ + import src.aws.infrastructure.models as models from src.aws.domain.entities import aws as schemas_aws -from src.shared.security.vault import vault_decrypt, vault_encrypt +from src.shared.security.vault import vault_encrypt @vault_encrypt @@ -15,35 +18,21 @@ def encrypt(secreto): raise err -@vault_decrypt -def decrypt(secreto): - try: - return secreto - except Exception as err: - raise err - - def create_aws_profile(db: Session, aws: schemas_aws.AwsAsumeProfile): encrypt_access_key_id = encrypt(aws.access_key_id) encrypt_secret_access_key = encrypt(aws.secret_access_key) + encrypted_extra_variables = {key: encrypt(val) for key, val in aws.extra_variables.items()} if aws.extra_variables else None + db_aws = models.Aws_provider( access_key_id=encrypt_access_key_id, secret_access_key=encrypt_secret_access_key, environment=aws.environment, default_region=aws.default_region, - profile_name=aws.profile_name, role_arn=aws.role_arn, - source_profile=aws.source_profile, + extra_variables=encrypted_extra_variables, created_at=datetime.datetime.now(), squad=aws.squad, ) - check_None = [None, "string"] - if db_aws.role_arn in check_None: - db_aws.role_arn = "" - if db_aws.profile_name in check_None: - db_aws.profile_name = "" - if db_aws.source_profile in check_None: - db_aws.source_profile = "" try: db.add(db_aws) db.commit() @@ -53,84 +42,89 @@ def create_aws_profile(db: Session, aws: schemas_aws.AwsAsumeProfile): raise err -def get_credentials_aws_profile(db: Session, environment: str, squad: str): - get_access_key = ( - db.query(models.Aws_provider.access_key_id) - .filter(models.Aws_provider.environment == environment) - .filter(models.Aws_provider.squad == squad) - .first() - ) - get_secret_access_key = ( - db.query(models.Aws_provider.secret_access_key) - .filter(models.Aws_provider.environment == environment) - .filter(models.Aws_provider.squad == squad) - .first() - ) - default_region = ( - db.query(models.Aws_provider.default_region) - .filter(models.Aws_provider.environment == environment) - .filter(models.Aws_provider.squad == squad) - .first() - ) - profile_name = ( - db.query(models.Aws_provider.profile_name) +def get_credentials_aws_profile(db: Session, environment: str, squad: str) -> schemas_aws.AwsAccountResponseRepo: + aws_provider_data = ( + db.query(models.Aws_provider) .filter(models.Aws_provider.environment == environment) .filter(models.Aws_provider.squad == squad) .first() ) - role_arn = ( - db.query(models.Aws_provider.role_arn) - .filter(models.Aws_provider.environment == environment) - .filter(models.Aws_provider.squad == squad) - .first() - ) - source_profile = ( - db.query(models.Aws_provider.source_profile) - .filter(models.Aws_provider.environment == environment) - .filter(models.Aws_provider.squad == squad) - .first() + return schemas_aws.AwsAccountResponseRepo( + id=aws_provider_data.id, + squad=aws_provider_data.squad, + environment=aws_provider_data.environment, + access_key_id=aws_provider_data.access_key_id, + secret_access_key=aws_provider_data.secret_access_key, + role_arn=aws_provider_data.role_arn, + default_region=aws_provider_data.default_region, + extra_variables=aws_provider_data.extra_variables, ) - try: - return { - "access_key": decrypt(get_access_key[0]), - "secret_access_key": decrypt(get_secret_access_key[0]), - "default_region": default_region[0], - "profile_name": profile_name[0], - "role_arn": role_arn[0], - "source_profile": source_profile[0], - } - except Exception as err: - raise err -def get_squad_aws_profile(db: Session, squad: str, environment: str): +def get_squad_aws_profile( + db: Session, squad: str, filters: schemas_aws.AwsAccountFilter, skip: int = 0, limit: int = 100 +) -> List[schemas_aws.AwsAccountResponse]: try: - if environment != None: - return ( - db.query(models.Aws_provider) - .filter(models.Aws_provider.squad == squad) - .filter(models.Aws_provider.environment == environment) - .first() - ) - result = [] - for i in squad: - result.extend( - db.query(models.Aws_provider) - .filter(models.Aws_provider.squad == i) - .all() + query = ( + db.query(models.Aws_provider) + .filter(models.Aws_provider.squad == squad) + ) + + for field, value in filters.model_dump().items(): + if value is not None: + query = query.filter(getattr(models.Aws_provider, field) == value) + + results = query.order_by(desc(models.Aws_provider.id)).offset(skip).limit(limit).all() + + aws_profiles = [] + for result in results: + aws_profile = schemas_aws.AwsAccountResponse( + id=result.id, + squad=result.squad, + environment=result.environment, + default_region=result.default_region, + role_arn=result.role_arn, + extra_variables=result.extra_variables, ) - return set(result) + aws_profiles.append(aws_profile) + return aws_profiles except Exception as err: raise err -def get_all_aws_profile(db: Session): +def get_all_aws_profile( + db: Session, filters: schemas_aws.AwsAccountFilter, skip: int = 0, limit: int = 100 +) -> List[schemas_aws.AwsAccountResponse]: try: - return db.query(models.Aws_provider).all() + query = db.query(models.Aws_provider) + + for field, value in filters.model_dump().items(): + if value is not None: + if field == 'squad' and isinstance(value, list): + or_conditions = [getattr(models.Aws_provider, field).like(f"%{v}%") for v in value] + query = query.filter(or_(*or_conditions)) + else: + query = query.filter(getattr(models.Aws_provider, field) == value) + + results = query.order_by(desc(models.Aws_provider.id)).offset(skip).limit(limit).all() + + aws_profiles = [] + for result in results: + aws_profile = schemas_aws.AwsAccountResponse( + id=result.id, + squad=result.squad, + environment=result.environment, + default_region=result.default_region, + role_arn=result.role_arn, + extra_variables=result.extra_variables, + ) + aws_profiles.append(aws_profile) + return aws_profiles except Exception as err: raise err + def delete_aws_profile_by_id(db: Session, aws_profile_id: int): try: db.query(models.Aws_provider).filter( diff --git a/sld-api-backend/src/azure/infrastructure/models.py b/sld-api-backend/src/azure/infrastructure/models.py index 4dfde48a..87ed2e50 100644 --- a/sld-api-backend/src/azure/infrastructure/models.py +++ b/sld-api-backend/src/azure/infrastructure/models.py @@ -1,7 +1,7 @@ import datetime from config.database import Base -from sqlalchemy import Column, DateTime, Integer, String, UniqueConstraint +from sqlalchemy import Column, DateTime, Integer, String, UniqueConstraint, JSON class Azure_provider(Base): @@ -13,5 +13,6 @@ class Azure_provider(Base): client_secret = Column(String(200), nullable=False) subscription_id = Column(String(200), nullable=False) tenant_id = Column(String(200), nullable=False) + extra_variables = Column(JSON, nullable=True) created_at = Column(DateTime, default=datetime.datetime.now()) __table_args__ = (UniqueConstraint("squad", "environment"),) diff --git a/sld-api-backend/src/deploy/api/container/deploy/get.py b/sld-api-backend/src/deploy/api/container/deploy/get.py index c6393921..ced0f3b0 100644 --- a/sld-api-backend/src/deploy/api/container/deploy/get.py +++ b/sld-api-backend/src/deploy/api/container/deploy/get.py @@ -22,7 +22,6 @@ async def get_all_deploys( filters: DeployFilter = Depends(DeployFilter), ) -> List[DeployFilterResponse]: try: - # Si el usuario no es un maestro, aplicar el filtro de escuadrĂ³n if not crud_users.is_master(db, current_user): filters.squad = current_user.squad diff --git a/sld-api-backend/src/deploy/infrastructure/repositories.py b/sld-api-backend/src/deploy/infrastructure/repositories.py index ee991fd9..7f803f70 100644 --- a/sld-api-backend/src/deploy/infrastructure/repositories.py +++ b/sld-api-backend/src/deploy/infrastructure/repositories.py @@ -210,17 +210,15 @@ def get_deploys(db: Session, filters: DeployFilter, skip: int = 0, limit: int = results = query.order_by(desc(models.Deploy.id)).offset(skip).limit(limit).all() - # Crear una lista de DeployFilterResponse a partir de los resultados deploy_responses = [] for deploy, icon_path in results: deploy_dict = deploy.__dict__ - deploy_dict['icon_path'] = icon_path # Agregar el icon_path al diccionario + deploy_dict['icon_path'] = icon_path deploy_responses.append(DeployFilterResponse(**deploy_dict)) return deploy_responses - class MetricsFetcher: def __init__(self, db: Session): self.db = db diff --git a/sld-api-backend/src/gcp/infrastructure/models.py b/sld-api-backend/src/gcp/infrastructure/models.py index 2dbbaca0..138c9939 100644 --- a/sld-api-backend/src/gcp/infrastructure/models.py +++ b/sld-api-backend/src/gcp/infrastructure/models.py @@ -1,7 +1,7 @@ import datetime from config.database import Base -from sqlalchemy import Column, DateTime, Integer, String, UniqueConstraint +from sqlalchemy import Column, DateTime, Integer, String, UniqueConstraint, JSON class Gcloud_provider(Base): @@ -10,5 +10,6 @@ class Gcloud_provider(Base): environment = Column(String(200), nullable=False) squad = Column(String(200), nullable=False) gcloud_keyfile_json = Column(String(5000), nullable=False) + extra_variables = Column(JSON, nullable=True) created_at = Column(DateTime, default=datetime.datetime.now()) __table_args__ = (UniqueConstraint("squad", "environment"),) diff --git a/sld-api-backend/src/worker/security/providers_credentials.py b/sld-api-backend/src/worker/security/providers_credentials.py index a9ee9bff..f3536eeb 100644 --- a/sld-api-backend/src/worker/security/providers_credentials.py +++ b/sld-api-backend/src/worker/security/providers_credentials.py @@ -5,8 +5,17 @@ import os from config.api import settings +from src.shared.security.vault import vault_decrypt +@vault_decrypt +def decrypt(secreto): + try: + return secreto + except Exception as err: + raise err + + class SecretsProviders: def __init__(self, secret_provider: dict) -> None: self.secret_provider = secret_provider @@ -25,74 +34,6 @@ def createLocalFolder(dir_path: str): raise -def aws_config(secreto): - try: - config = configparser.ConfigParser(strict=False) - # Check if pass me profile - if secreto.get("profile_name"): - # Create folder in home user - createLocalFolder(settings.AWS_CONGIG_DEFAULT_FOLDER) - # Read config - config.read(settings.AWS_SHARED_CONFIG_FILE) - profile_name = secreto.get("profile_name") - if not config.has_section(f"profile {profile_name}"): - config.add_section(f"profile {profile_name}") - config.set(f"profile {profile_name}", "role_arn", secreto.get("role_arn")) - config.set( - f"profile {profile_name}", - "region", - secreto.get("default_region"), - ) - config.set( - f"profile {profile_name}", - "source_profile", - secreto.get("source_profile"), - ) - with open(settings.AWS_SHARED_CONFIG_FILE, "w") as configfile: - config.write(configfile) - logging.info( - "created config done" - ) - del secreto - del profile_name - del configfile - del config - return True - except Exception as err: - return False - logging.warning(err) - - -def aws_credentials(secreto): - try: - config = configparser.ConfigParser(strict=False) - if secreto.get("source_profile"): - config.read(settings.AWS_SHARED_CREDENTIALS_FILE) - source_profile = secreto.get("source_profile") - if not config.has_section(source_profile): - config.add_section(source_profile) - config.set(source_profile, "region", secreto.get("default_region")) - config.set(source_profile, "aws_access_key_id", secreto.get("access_key")) - config.set( - source_profile, - "aws_secret_access_key", - secreto.get("secret_access_key"), - ) - with open(settings.AWS_SHARED_CREDENTIALS_FILE, "w") as credentialsfile: - config.write(credentialsfile) - logging.info( - "created credentials done" - ) - del secreto - del source_profile - del credentialsfile - del config - return True - except Exception as err: - return False - logging.warning(err) - - def secret( stack_name, environment, @@ -102,12 +43,16 @@ def secret( ): if any(i in stack_name.lower() for i in settings.AWS_PREFIX): try: - if not aws_config(secreto) or not aws_credentials(secreto): - os.environ["AWS_ACCESS_KEY_ID"] = secreto.get("access_key") - os.environ["AWS_SECRET_ACCESS_KEY"] = secreto.get("secret_access_key") - logging.info( - f"Set aws account without asume role {squad}, {environment}, {stack_name}, {name}" - ) + os.environ["AWS_ACCESS_KEY_ID"] = decrypt(secreto.get("access_key_id")) + os.environ["AWS_SECRET_ACCESS_KEY"] = decrypt(secreto.get("secret_access_key")) + os.environ["AWS_DEFAULT_REGION"] = secreto.get("default_region") + if secreto.get("role_arn"): + logging.info("Set role_arn for assume role") + os.environ["TF_VAR_role_arn"] = secreto.get("role_arn") + logging.info(f"TF_VAR_role_arn = {secreto.get('role_arn')}") + logging.info( + f'Set aws account {squad}, {environment}, {stack_name}, {secreto.get("default_region")}, {name}' + ) except Exception as err: logging.warning(err)