diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 5b29e8cbf..b7963b7bd 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -11,4 +11,24 @@ ### Relates - +### Security +Please answer the questions below briefly where applicable, or write `N/A`. Based on +[OWASP 10](https://owasp.org/Top10/en/). + +- Does this PR introduce or modify any input fields or queries - this includes +fetching data from storage outside the application (e.g. a database, an S3 bucket)? + - Is the input sanitized? + - What precautions are you taking before deserializing the data you consume? + - Is injection prevented by parametrizing queries? + - Have you ensured no `eval` or similar functions are used? +- Does this PR introduce any functionality or component that requires authorization? + - How have you ensured it respects the existing AuthN/AuthZ mechanisms? + - Are you logging failed auth attempts? +- Are you using or adding any cryptographic features? + - Do you use a standard proven implementations? + - Are the used keys controlled by the customer? Where are they stored? +- Are you introducing any new policies/roles/users? + - Have you used the least-privilege principle? How? + + By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. diff --git a/UserGuide.pdf b/UserGuide.pdf index 2992ef52e..49dda4beb 100644 Binary files a/UserGuide.pdf and b/UserGuide.pdf differ diff --git a/backend/cdkproxymain.py b/backend/cdkproxymain.py index 1a2ff2de9..343d5692b 100644 --- a/backend/cdkproxymain.py +++ b/backend/cdkproxymain.py @@ -52,7 +52,12 @@ def up(response: Response): def check_creds(response: Response): logger.info('GET /awscreds') try: - sts = boto3.client('sts', region_name=os.getenv('AWS_REGION', 'eu-west-1')) + region = os.getenv('AWS_REGION', 'eu-west-1') + sts = boto3.client( + 'sts', + region_name=region, + endpoint_url=f"https://sts.{region}.amazonaws.com" + ) data = sts.get_caller_identity() return { 'DH_DOCKER_VERSION': os.environ.get('DH_DOCKER_VERSION'), @@ -84,7 +89,7 @@ def check_connect(response: Response): return { 'DH_DOCKER_VERSION': os.environ.get('DH_DOCKER_VERSION'), '_ts': datetime.now().isoformat(), - 'message': f"Connected to database for environment {ENVNAME}({engine.dbconfig.params['host']}:{engine.dbconfig.params['port']})", + 'message': f"Connected to database for environment {ENVNAME}({engine.dbconfig.host})", } except Exception as e: logger.exception('DBCONNECTIONERROR') diff --git a/backend/dataall/api/Objects/Dataset/input_types.py b/backend/dataall/api/Objects/Dataset/input_types.py index 16609814d..79d140bda 100644 --- a/backend/dataall/api/Objects/Dataset/input_types.py +++ b/backend/dataall/api/Objects/Dataset/input_types.py @@ -42,6 +42,7 @@ class DatasetSortField(GraphQLEnumMapper): gql.Argument('language', gql.Ref('Language')), gql.Argument('confidentiality', gql.Ref('ConfidentialityClassification')), gql.Argument(name='stewards', type=gql.String), + gql.Argument('KmsAlias', gql.NonNullableType(gql.String)), ], ) @@ -94,7 +95,7 @@ class DatasetSortField(GraphQLEnumMapper): gql.Argument('description', gql.String), gql.Argument('bucketName', gql.NonNullableType(gql.String)), gql.Argument('glueDatabaseName', gql.String), - gql.Argument('KmsKeyId', gql.String), + gql.Argument('KmsKeyAlias', gql.NonNullableType(gql.String)), gql.Argument('adminRoleName', gql.String), gql.Argument('tags', gql.ArrayType(gql.String)), gql.Argument('owner', gql.NonNullableType(gql.String)), diff --git a/backend/dataall/api/Objects/Dataset/resolvers.py b/backend/dataall/api/Objects/Dataset/resolvers.py index 606f5396d..39758bbd1 100644 --- a/backend/dataall/api/Objects/Dataset/resolvers.py +++ b/backend/dataall/api/Objects/Dataset/resolvers.py @@ -13,7 +13,8 @@ from ....aws.handlers.glue import Glue from ....aws.handlers.service_handlers import Worker from ....aws.handlers.sts import SessionHelper -from ....aws.handlers.sns import Sns +from ....aws.handlers.kms import KMS + from ....aws.handlers.quicksight import Quicksight from ....db import paginate, exceptions, permissions, models from ....db.api import Dataset, Environment, ShareObject, ResourcePolicy @@ -32,6 +33,21 @@ def check_dataset_account(environment): return True +def check_imported_resources(environment, kmsAlias): + if kmsAlias not in ["Undefined", "", "SSE-S3"]: + key_id = KMS.get_key_id( + account_id=environment.AwsAccountId, + region=environment.region, + key_alias=f"alias/{kmsAlias}" + ) + if not key_id: + raise exceptions.AWSResourceNotFound( + action=permissions.IMPORT_DATASET, + message=f'KMS key with alias={kmsAlias} cannot be found', + ) + return True + + def create_dataset(context: Context, source, input=None): with context.engine.scoped_session() as session: environment = Environment.get_environment_by_uri(session, input.get('environmentUri')) @@ -71,6 +87,7 @@ def import_dataset(context: Context, source, input=None): with context.engine.scoped_session() as session: environment = Environment.get_environment_by_uri(session, input.get('environmentUri')) check_dataset_account(environment=environment) + check_imported_resources(environment=environment, kmsAlias=input.get('KmsKeyAlias', "")) dataset = Dataset.create_dataset( session=session, @@ -83,9 +100,9 @@ def import_dataset(context: Context, source, input=None): dataset.imported = True dataset.importedS3Bucket = True if input['bucketName'] else False dataset.importedGlueDatabase = True if input.get('glueDatabaseName') else False - dataset.importedKmsKey = True if input.get('KmsKeyId') else False + dataset.importedKmsKey = True if input.get('KmsKeyAlias') else False dataset.importedAdminRole = True if input.get('adminRoleName') else False - + dataset.KmsAlias = "SSE-S3" if input.get('KmsKeyAlias') == "" else input.get('KmsKeyAlias') Dataset.create_dataset_stack(session, dataset) indexers.upsert_dataset( @@ -231,6 +248,7 @@ def update_dataset(context, source, datasetUri: str = None, input: dict = None): dataset = Dataset.get_dataset_by_uri(session, datasetUri) environment = Environment.get_environment_by_uri(session, dataset.environmentUri) check_dataset_account(environment=environment) + check_imported_resources(environment=environment, kmsAlias=input.get('KmsAlias', "")) updated_dataset = Dataset.update_dataset( session=session, username=context.username, diff --git a/backend/dataall/api/Objects/DatasetProfiling/mutations.py b/backend/dataall/api/Objects/DatasetProfiling/mutations.py index 5876c81a7..559129dc8 100644 --- a/backend/dataall/api/Objects/DatasetProfiling/mutations.py +++ b/backend/dataall/api/Objects/DatasetProfiling/mutations.py @@ -7,13 +7,3 @@ type=gql.Ref('DatasetProfilingRun'), resolver=start_profiling_run, ) - -updateDatasetProfilingRunResults = gql.MutationField( - name='updateDatasetProfilingRunResults', - args=[ - gql.Argument(name='profilingRunUri', type=gql.NonNullableType(gql.String)), - gql.Argument(name='results', type=gql.NonNullableType(gql.String)), - ], - type=gql.Ref('DatasetProfilingRun'), - resolver=update_profiling_run_results, -) diff --git a/backend/dataall/api/Objects/DatasetProfiling/queries.py b/backend/dataall/api/Objects/DatasetProfiling/queries.py index 9ab3eb2bb..1cbe06764 100644 --- a/backend/dataall/api/Objects/DatasetProfiling/queries.py +++ b/backend/dataall/api/Objects/DatasetProfiling/queries.py @@ -2,24 +2,6 @@ from .resolvers import * -getDatasetProfilingRun = gql.QueryField( - name='getDatasetProfilingRun', - args=[gql.Argument(name='profilingRunUri', type=gql.NonNullableType(gql.String))], - type=gql.Ref('DatasetProfilingRun'), - resolver=get_profiling_run, -) - - -listDatasetProfilingRuns = gql.QueryField( - name='listDatasetProfilingRuns', - args=[ - gql.Argument(name='datasetUri', type=gql.NonNullableType(gql.String)), - gql.Argument(name='filter', type=gql.Ref('DatasetProfilingRunFilter')), - ], - type=gql.Ref('DatasetProfilingRunSearchResults'), - resolver=list_profiling_runs, -) - listDatasetTableProfilingRuns = gql.QueryField( name='listDatasetTableProfilingRuns', args=[gql.Argument(name='tableUri', type=gql.NonNullableType(gql.String))], @@ -31,5 +13,5 @@ name='getDatasetTableProfilingRun', args=[gql.Argument(name='tableUri', type=gql.NonNullableType(gql.String))], type=gql.Ref('DatasetProfilingRun'), - resolver=get_last_table_profiling_run, + resolver=get_dataset_table_profiling_run, ) diff --git a/backend/dataall/api/Objects/DatasetProfiling/resolvers.py b/backend/dataall/api/Objects/DatasetProfiling/resolvers.py index 678a8cba6..11c19b888 100644 --- a/backend/dataall/api/Objects/DatasetProfiling/resolvers.py +++ b/backend/dataall/api/Objects/DatasetProfiling/resolvers.py @@ -1,6 +1,7 @@ import json import logging +from .... import db from ....api.context import Context from ....aws.handlers.service_handlers import Worker from ....aws.handlers.sts import SessionHelper @@ -19,7 +20,30 @@ def resolve_dataset(context, source: models.DatasetProfilingRun): ) +def resolve_profiling_run_status(context: Context, source: models.DatasetProfilingRun): + if not source: + return None + with context.engine.scoped_session() as session: + task = models.Task( + targetUri=source.profilingRunUri, action='glue.job.profiling_run_status' + ) + session.add(task) + Worker.queue(engine=context.engine, task_ids=[task.taskUri]) + return source.status + + +def resolve_profiling_results(context: Context, source: models.DatasetProfilingRun): + if not source or source.results == {}: + return None + else: + return json.dumps(source.results) + + def start_profiling_run(context: Context, source, input: dict = None): + """ + Triggers profiling jobs on a Table. + Only Dataset owners with PROFILE_DATASET_TABLE can perform this action + """ with context.engine.scoped_session() as session: ResourcePolicy.check_user_resource_permission( @@ -48,47 +72,14 @@ def start_profiling_run(context: Context, source, input: dict = None): return run -def get_profiling_run_status(context: Context, source: models.DatasetProfilingRun): - if not source: - return None - with context.engine.scoped_session() as session: - task = models.Task( - targetUri=source.profilingRunUri, action='glue.job.profiling_run_status' - ) - session.add(task) - Worker.queue(engine=context.engine, task_ids=[task.taskUri]) - return source.status - - -def get_profiling_results(context: Context, source: models.DatasetProfilingRun): - if not source or source.results == {}: - return None - else: - return json.dumps(source.results) - - -def update_profiling_run_results(context: Context, source, profilingRunUri, results): - with context.engine.scoped_session() as session: - run = api.DatasetProfilingRun.update_run( - session=session, profilingRunUri=profilingRunUri, results=results - ) - return run - - -def list_profiling_runs(context: Context, source, datasetUri=None): - with context.engine.scoped_session() as session: - return api.DatasetProfilingRun.list_profiling_runs(session, datasetUri) - - -def get_profiling_run(context: Context, source, profilingRunUri=None): - with context.engine.scoped_session() as session: - return api.DatasetProfilingRun.get_profiling_run( - session=session, profilingRunUri=profilingRunUri - ) - - -def get_last_table_profiling_run(context: Context, source, tableUri=None): +def get_dataset_table_profiling_run(context: Context, source, tableUri=None): + """ + Shows the results of the last profiling job on a Table. + For datasets "Unclassified" all users can perform this action. + For datasets "Secret" or "Official", only users with PREVIEW_DATASET_TABLE permissions can perform this action. + """ with context.engine.scoped_session() as session: + _check_preview_permissions_if_needed(context=context, session=session, tableUri=tableUri) run: models.DatasetProfilingRun = ( api.DatasetProfilingRun.get_table_last_profiling_run( session=session, tableUri=tableUri @@ -102,7 +93,7 @@ def get_last_table_profiling_run(context: Context, source, tableUri=None): environment = api.Environment.get_environment_by_uri( session, dataset.environmentUri ) - content = get_profiling_results_from_s3( + content = _get_profiling_results_from_s3( environment, dataset, table, run ) if content: @@ -121,7 +112,7 @@ def get_last_table_profiling_run(context: Context, source, tableUri=None): return run -def get_profiling_results_from_s3(environment, dataset, table, run): +def _get_profiling_results_from_s3(environment, dataset, table, run): s3 = SessionHelper.remote_session(environment.AwsAccountId).client( 's3', region_name=environment.region ) @@ -141,7 +132,32 @@ def get_profiling_results_from_s3(environment, dataset, table, run): def list_table_profiling_runs(context: Context, source, tableUri=None): + """ + Lists the runs of a profiling job on a Table. + For datasets "Unclassified" all users can perform this action. + For datasets "Secret" or "Official", only users with PREVIEW_DATASET_TABLE permissions can perform this action. + """ with context.engine.scoped_session() as session: + _check_preview_permissions_if_needed(context=context, session=session, tableUri=tableUri) return api.DatasetProfilingRun.list_table_profiling_runs( session=session, tableUri=tableUri, filter={} ) + + +def _check_preview_permissions_if_needed(context, session, tableUri): + table: models.DatasetTable = db.api.DatasetTable.get_dataset_table_by_uri( + session, tableUri + ) + dataset = db.api.Dataset.get_dataset_by_uri(session, table.datasetUri) + if ( + dataset.confidentiality + != models.ConfidentialityClassification.Unclassified.value + ): + ResourcePolicy.check_user_resource_permission( + session=session, + username=context.username, + groups=context.groups, + resource_uri=table.tableUri, + permission_name=permissions.PREVIEW_DATASET_TABLE, + ) + return True diff --git a/backend/dataall/api/Objects/DatasetProfiling/schema.py b/backend/dataall/api/Objects/DatasetProfiling/schema.py index f6fe9c575..88edbc403 100644 --- a/backend/dataall/api/Objects/DatasetProfiling/schema.py +++ b/backend/dataall/api/Objects/DatasetProfiling/schema.py @@ -1,8 +1,8 @@ from ... import gql from .resolvers import ( resolve_dataset, - get_profiling_run_status, - get_profiling_results, + resolve_profiling_run_status, + resolve_profiling_results, ) DatasetProfilingRun = gql.ObjectType( @@ -16,11 +16,11 @@ gql.Field(name='GlueTriggerName', type=gql.String), gql.Field(name='GlueTableName', type=gql.String), gql.Field(name='AwsAccountId', type=gql.String), - gql.Field(name='results', type=gql.String, resolver=get_profiling_results), + gql.Field(name='results', type=gql.String, resolver=resolve_profiling_results), gql.Field(name='created', type=gql.String), gql.Field(name='updated', type=gql.String), gql.Field(name='owner', type=gql.String), - gql.Field('status', type=gql.String, resolver=get_profiling_run_status), + gql.Field('status', type=gql.String, resolver=resolve_profiling_run_status), gql.Field(name='dataset', type=gql.Ref('Dataset'), resolver=resolve_dataset), ], ) diff --git a/backend/dataall/api/Objects/Environment/queries.py b/backend/dataall/api/Objects/Environment/queries.py index 34b397748..0d593ea09 100644 --- a/backend/dataall/api/Objects/Environment/queries.py +++ b/backend/dataall/api/Objects/Environment/queries.py @@ -175,6 +175,14 @@ test_scope='Environment', ) +getCDKExecPolicyPresignedUrl = gql.QueryField( + name='getCDKExecPolicyPresignedUrl', + args=[gql.Argument(name='organizationUri', type=gql.NonNullableType(gql.String))], + type=gql.String, + resolver=get_cdk_exec_policy_template, + test_scope='Environment', +) + getPivotRoleExternalId = gql.QueryField( name='getPivotRoleExternalId', diff --git a/backend/dataall/api/Objects/Environment/resolvers.py b/backend/dataall/api/Objects/Environment/resolvers.py index 6df801118..03ce2d964 100644 --- a/backend/dataall/api/Objects/Environment/resolvers.py +++ b/backend/dataall/api/Objects/Environment/resolvers.py @@ -700,6 +700,52 @@ def get_pivot_role_template(context: Context, source, organizationUri=None): raise e +def get_cdk_exec_policy_template(context: Context, source, organizationUri=None): + from ....utils import Parameter + + with context.engine.scoped_session() as session: + ResourcePolicy.check_user_resource_permission( + session=session, + username=context.username, + groups=context.groups, + resource_uri=organizationUri, + permission_name=permissions.GET_ORGANIZATION, + ) + cdk_exec_policy_bucket = Parameter().get_parameter( + env=os.getenv('envname', 'local'), path='s3/resources_bucket_name' + ) + cdk_exec_policy_bucket_key = Parameter().get_parameter( + env=os.getenv('envname', 'local'), path='s3/cdk_exec_policy_prefix' + ) + if not cdk_exec_policy_bucket or not cdk_exec_policy_bucket_key: + raise exceptions.AWSResourceNotFound( + action='GET_CDK_EXEC_POLICY_TEMPLATE', + message='CDK Exec Yaml template file could not be found on Amazon S3 bucket', + ) + try: + s3_client = boto3.client( + 's3', + region_name=os.getenv('AWS_REGION', 'eu-central-1'), + config=Config( + signature_version='s3v4', s3={'addressing_style': 'virtual'} + ), + ) + presigned_url = s3_client.generate_presigned_url( + 'get_object', + Params=dict( + Bucket=cdk_exec_policy_bucket, + Key=cdk_exec_policy_bucket_key, + ), + ExpiresIn=15 * 60, + ) + return presigned_url + except ClientError as e: + log.error( + f'Failed to get presigned URL for CDK Exec role template due to: {e}' + ) + raise e + + def get_external_id(context: Context, source, organizationUri=None): with context.engine.scoped_session() as session: ResourcePolicy.check_user_resource_permission( @@ -731,6 +777,6 @@ def get_pivot_role_name(context: Context, source, organizationUri=None): if not pivot_role_name: raise exceptions.AWSResourceNotFound( action='GET_PIVOT_ROLE_NAME', - message='Pivot role name could not be found on AWS Secretsmanager', + message='Pivot role name could not be found on AWS Systems Manager - Parameter Store', ) return pivot_role_name diff --git a/backend/dataall/api/Objects/ShareObject/input_types.py b/backend/dataall/api/Objects/ShareObject/input_types.py index 04f7269ec..0b7828825 100644 --- a/backend/dataall/api/Objects/ShareObject/input_types.py +++ b/backend/dataall/api/Objects/ShareObject/input_types.py @@ -8,6 +8,7 @@ gql.Argument(name='groupUri', type=gql.NonNullableType(gql.String)), gql.Argument(name='principalId', type=gql.NonNullableType(gql.String)), gql.Argument(name='principalType', type=gql.NonNullableType(gql.String)), + gql.Argument(name='requestPurpose', type=gql.String), ], ) diff --git a/backend/dataall/api/Objects/ShareObject/mutations.py b/backend/dataall/api/Objects/ShareObject/mutations.py index d8247837d..68e7d18d8 100644 --- a/backend/dataall/api/Objects/ShareObject/mutations.py +++ b/backend/dataall/api/Objects/ShareObject/mutations.py @@ -56,7 +56,10 @@ rejectShareObject = gql.MutationField( name='rejectShareObject', - args=[gql.Argument(name='shareUri', type=gql.NonNullableType(gql.String))], + args=[ + gql.Argument(name='shareUri', type=gql.NonNullableType(gql.String)), + gql.Argument(name='rejectPurpose', type=gql.String), + ], type=gql.Ref('ShareObject'), resolver=reject_share_object, ) @@ -67,3 +70,24 @@ type=gql.Ref('ShareObject'), resolver=revoke_items_share_object, ) + +updateShareRejectReason = gql.MutationField( + name='updateShareRejectReason', + args=[ + gql.Argument(name='shareUri', type=gql.NonNullableType(gql.String)), + gql.Argument(name='rejectPurpose', type=gql.String) + ], + type=gql.Boolean, + resolver=update_share_reject_purpose, +) + + +updateShareRequestReason = gql.MutationField( + name='updateShareRequestReason', + args=[ + gql.Argument(name='shareUri', type=gql.NonNullableType(gql.String)), + gql.Argument(name='requestPurpose', type=gql.String) + ], + type=gql.Boolean, + resolver=update_share_request_purpose, +) diff --git a/backend/dataall/api/Objects/ShareObject/resolvers.py b/backend/dataall/api/Objects/ShareObject/resolvers.py index f14642aeb..bdc03545d 100644 --- a/backend/dataall/api/Objects/ShareObject/resolvers.py +++ b/backend/dataall/api/Objects/ShareObject/resolvers.py @@ -85,14 +85,14 @@ def approve_share_object(context: Context, source, shareUri: str = None): return share -def reject_share_object(context: Context, source, shareUri: str = None): +def reject_share_object(context: Context, source, shareUri: str = None, rejectPurpose: str = None,): with context.engine.scoped_session() as session: return db.api.ShareObject.reject_share_object( session=session, username=context.username, groups=context.groups, uri=shareUri, - data=None, + data={"rejectPurpose": rejectPurpose}, check_perm=True, ) @@ -222,25 +222,19 @@ def resolve_user_role(context: Context, source: models.ShareObject, **kwargs): return None with context.engine.scoped_session() as session: dataset: models.Dataset = db.api.Dataset.get_dataset_by_uri(session, source.datasetUri) - if dataset and dataset.stewards in context.groups: + if ( + dataset and ( + dataset.stewards in context.groups + or dataset.SamlAdminGroupName in context.groups + or dataset.owner == context.username + ) + ): return ShareObjectPermission.Approvers.value if ( source.owner == context.username - or source.principalId in context.groups - or dataset.owner == context.username - or dataset.SamlAdminGroupName in context.groups + or source.groupUri in context.groups ): return ShareObjectPermission.Requesters.value - if ( - dataset and dataset.stewards in context.groups - and ( - source.owner == context.username - or source.principalId in context.groups - or dataset.owner == context.username - or dataset.SamlAdminGroupName in context.groups - ) - ): - return ShareObjectPermission.ApproversAndRequesters.value else: return ShareObjectPermission.NoPermission.value @@ -375,3 +369,27 @@ def list_shares_in_my_outbox(context: Context, source, filter: dict = None): data=filter, check_perm=None, ) + + +def update_share_request_purpose(context: Context, source, shareUri: str = None, requestPurpose: str = None): + with context.engine.scoped_session() as session: + return db.api.ShareObject.update_share_request_purpose( + session=session, + username=context.username, + groups=context.groups, + uri=shareUri, + data={"requestPurpose": requestPurpose}, + check_perm=True, + ) + + +def update_share_reject_purpose(context: Context, source, shareUri: str = None, rejectPurpose: str = None): + with context.engine.scoped_session() as session: + return db.api.ShareObject.update_share_reject_purpose( + session=session, + username=context.username, + groups=context.groups, + uri=shareUri, + data={"rejectPurpose": rejectPurpose}, + check_perm=True, + ) diff --git a/backend/dataall/api/Objects/ShareObject/schema.py b/backend/dataall/api/Objects/ShareObject/schema.py index b045d6072..4bcf806a9 100644 --- a/backend/dataall/api/Objects/ShareObject/schema.py +++ b/backend/dataall/api/Objects/ShareObject/schema.py @@ -115,6 +115,8 @@ gql.Field(name='deleted', type=gql.String), gql.Field(name='updated', type=gql.String), gql.Field(name='datasetUri', type=gql.String), + gql.Field(name='requestPurpose', type=gql.String), + gql.Field(name='rejectPurpose', type=gql.String), gql.Field(name='dataset', type=DatasetLink, resolver=resolve_dataset), gql.Field(name='consumptionData', type=gql.Ref('ConsumptionData'), resolver=resolve_consumption_data), gql.Field(name='existingSharedItems', type=gql.Boolean, resolver=resolve_existing_shared_items), diff --git a/backend/dataall/aws/handlers/cloudformation.py b/backend/dataall/aws/handlers/cloudformation.py index 94bf54d8a..460e525ae 100644 --- a/backend/dataall/aws/handlers/cloudformation.py +++ b/backend/dataall/aws/handlers/cloudformation.py @@ -46,7 +46,6 @@ def delete_stack(engine, task: models.Task): try: data = { 'accountid': task.payload['accountid'], - 'cdk_role_arn': task.payload['cdk_role_arn'], 'region': task.payload['region'], 'stack_name': task.payload['stack_name'], } @@ -61,13 +60,11 @@ def delete_cloudformation_stack(**data): accountid = data['accountid'] region = data['region'] stack_name = data['stack_name'] - cdk_role_arn = data['cdk_role_arn'] try: aws_session = SessionHelper.remote_session(accountid=accountid) cfnclient = aws_session.client('cloudformation', region_name=region) response = cfnclient.delete_stack( StackName=stack_name, - RoleARN=cdk_role_arn, ClientRequestToken=str(uuid.uuid4()), ) log.info(f'Stack {stack_name} deleted: {response}') diff --git a/backend/dataall/aws/handlers/ecs.py b/backend/dataall/aws/handlers/ecs.py index 84235f494..3c247b4af 100644 --- a/backend/dataall/aws/handlers/ecs.py +++ b/backend/dataall/aws/handlers/ecs.py @@ -51,7 +51,7 @@ def run_share_management_ecs_task(envname, share_uri, handler): cluster_name = Parameter().get_parameter(env=envname, path='ecs/cluster/name') subnets = Parameter().get_parameter(env=envname, path='ecs/private_subnets') security_groups = Parameter().get_parameter( - env=envname, path='ecs/security_groups' + env=envname, path='ecs/sharemanager_security_groups' ) try: diff --git a/backend/dataall/aws/handlers/glue.py b/backend/dataall/aws/handlers/glue.py index bcfecbb8c..51929540a 100644 --- a/backend/dataall/aws/handlers/glue.py +++ b/backend/dataall/aws/handlers/glue.py @@ -398,6 +398,7 @@ def create_crawler(engine, task: models.Task): 'region': dataset.region, 'accountid': dataset.AwsAccountId, 'database': dataset.GlueDatabaseName, + 'dataset_role': dataset.IAMDatasetAdminRoleArn, 'location': location or f's3://{dataset.S3BucketName}', } ) @@ -407,6 +408,7 @@ def create_glue_crawler(**data): try: accountid = data['accountid'] database = data.get('database') + dataset_role = data['dataset_role'] session = SessionHelper.remote_session(accountid=accountid) glue = session.client('glue', region_name=data.get('region', 'eu-west-1')) crawler_name = data.get('crawler_name') @@ -414,12 +416,12 @@ def create_glue_crawler(**data): crawler = Glue._get_crawler(glue, crawler_name) if crawler: Glue._update_existing_crawler( - glue, accountid, crawler_name, targets, database + glue, dataset_role, crawler_name, targets, database ) else: crawler = glue.create_crawler( Name=crawler_name, - Role=SessionHelper.get_delegation_role_arn(accountid=accountid), + Role=dataset_role, DatabaseName=database, Targets=targets, Tags=data.get('tags', {'Application': 'dataall'}), @@ -458,6 +460,7 @@ def start_crawler(engine, task: models.Task): 'region': dataset.region, 'accountid': dataset.AwsAccountId, 'database': dataset.GlueDatabaseName, + 'dataset_role': dataset.IAMDatasetAdminRoleArn, 'location': location, } ) @@ -468,12 +471,13 @@ def start_glue_crawler(data): accountid = data['accountid'] crawler_name = data['crawler_name'] database = data['database'] + dataset_role = data['dataset_role'] targets = {'S3Targets': [{'Path': data.get('location')}]} session = SessionHelper.remote_session(accountid=accountid) glue = session.client('glue', region_name=data.get('region', 'eu-west-1')) if data.get('location'): Glue._update_existing_crawler( - glue, accountid, crawler_name, targets, database + glue, dataset_role, crawler_name, targets, database ) crawler = Glue._get_crawler(glue, crawler_name) glue.start_crawler(Name=crawler_name) @@ -496,7 +500,7 @@ def _get_crawler(glue, crawler_name): return crawler.get('Crawler') if crawler else None @staticmethod - def _update_existing_crawler(glue, accountid, crawler_name, targets, database): + def _update_existing_crawler(glue, dataset_role, crawler_name, targets, database): try: glue.stop_crawler(Name=crawler_name) except ClientError as e: @@ -508,7 +512,7 @@ def _update_existing_crawler(glue, accountid, crawler_name, targets, database): try: glue.update_crawler( Name=crawler_name, - Role=SessionHelper.get_delegation_role_arn(accountid=accountid), + Role=dataset_role, DatabaseName=database, Targets=targets, ) diff --git a/backend/dataall/aws/handlers/lakeformation.py b/backend/dataall/aws/handlers/lakeformation.py index ba91a6e47..d06515f01 100644 --- a/backend/dataall/aws/handlers/lakeformation.py +++ b/backend/dataall/aws/handlers/lakeformation.py @@ -14,7 +14,7 @@ def __init__(self): pass @staticmethod - def check_existing_lf_registered_location(resource_arn, accountid, region): + def check_existing_lf_registered_location(resource_arn: str, role_arn: str, accountid: str, region: str): """ Checks if there is a non-dataall-created registered location for the Dataset Returns False is already existing location else return the resource info @@ -25,7 +25,7 @@ def check_existing_lf_registered_location(resource_arn, accountid, region): response = lf_client.describe_resource(ResourceArn=resource_arn) registered_role_name = response['ResourceInfo']['RoleArn'].lstrip(f"arn:aws:iam::{accountid}:role/") log.info(f'LF data location already registered: {response}, registered with role {registered_role_name}') - if registered_role_name.startswith(PIVOT_ROLE_NAME_PREFIX): + if registered_role_name.startswith(PIVOT_ROLE_NAME_PREFIX) or response['ResourceInfo']['RoleArn'] == role_arn: log.info('The existing data location was created as part of the dataset stack. There was no pre-existing data location.') return False return response['ResourceInfo'] diff --git a/backend/dataall/aws/handlers/quicksight.py b/backend/dataall/aws/handlers/quicksight.py index f82ea87b6..67790486a 100644 --- a/backend/dataall/aws/handlers/quicksight.py +++ b/backend/dataall/aws/handlers/quicksight.py @@ -420,7 +420,7 @@ def create_data_source_vpc(AwsAccountId, region, UserName, vpcConnectionId): DataSourceParameters={ 'AuroraPostgreSqlParameters': { 'Host': aurora_params_dict["host"], - 'Port': aurora_params_dict["port"], + 'Port': "5432", 'Database': aurora_params_dict["dbname"] } }, diff --git a/backend/dataall/aws/handlers/sts.py b/backend/dataall/aws/handlers/sts.py index d55e23a06..dbfd414d0 100644 --- a/backend/dataall/aws/handlers/sts.py +++ b/backend/dataall/aws/handlers/sts.py @@ -23,10 +23,6 @@ class SessionHelper: """SessionHelpers is a class simplifying common aws boto3 session tasks and helpers""" - @classmethod - def get_root_account_session(cls): - ENVNAME = os.environ.get('envname', 'local') - @classmethod def get_session(cls, base_session=None, role_arn=None): """Returns a boto3 session fo the given role @@ -52,9 +48,12 @@ def get_session(cls, base_session=None, role_arn=None): RoleSessionName=role_arn.split('/')[1], ) try: + region = os.getenv('AWS_REGION', 'eu-west-1') sts = base_session.client( 'sts', config=Config(user_agent_extra=f'{__pkg_name__}/{__version__}'), + region_name=region, + endpoint_url=f"https://sts.{region}.amazonaws.com" ) response = sts.assume_role(**assume_role_dict) return boto3.Session( @@ -70,22 +69,24 @@ def get_session(cls, base_session=None, role_arn=None): return boto3.Session() @classmethod - def get_secret(cls, secret_name): + def _get_parameter_value(cls, parameter_path=None): """ - Method to get secret_string from secrets manager + Method to get parameter from System Manager Parameter Store :return: :rtype: """ - secret_string = None + parameter_value = None region = os.getenv('AWS_REGION', 'eu-west-1') + if not parameter_path: + raise Exception('Parameter name is None') try: session = SessionHelper.get_session() - client = session.client('secretsmanager', region_name=region) - secret_string = client.get_secret_value(SecretId=secret_name).get('SecretString') - log.debug(f'Found Secret {secret_name}|{secret_string}') + client = session.client('ssm', region_name=region) + parameter_value = client.get_parameter(Name=parameter_path)['Parameter']['Value'] + log.debug(f'Found Parameter {parameter_path}|{parameter_value}') except ClientError as e: - log.warning(f'Secret {secret_name} not found: {e}') - return secret_string + log.warning(f'Parameter {parameter_path} not found: {e}') + return parameter_value @classmethod def get_external_id_secret(cls): @@ -95,7 +96,8 @@ def get_external_id_secret(cls): :return: :rtype: """ - return SessionHelper.get_secret(secret_name=f'dataall-externalId-{os.getenv("envname", "local")}') + return SessionHelper._get_parameter_value( + parameter_path=f'/dataall/{os.getenv("envname", "local")}/pivotRole/externalId') @classmethod def get_delegation_role_name(cls): @@ -103,7 +105,8 @@ def get_delegation_role_name(cls): Returns: string: name of the assumed role """ - return SessionHelper.get_secret(secret_name=f'dataall-pivot-role-name-{os.getenv("envname", "local")}') + return SessionHelper._get_parameter_value( + parameter_path=f'/dataall/{os.getenv("envname", "local")}/pivotRole/pivotRoleName') @classmethod def get_console_access_url(cls, boto3_session, region='eu-west-1', bucket=None, redshiftcluster=None): @@ -168,6 +171,17 @@ def get_cdk_look_up_role_arn(cls, accountid, region): log.info(f"Getting CDK look up role: arn:aws:iam::{accountid}:role/cdk-hnb659fds-lookup-role-{accountid}-{region}") return 'arn:aws:iam::{}:role/cdk-hnb659fds-lookup-role-{}-{}'.format(accountid, accountid, region) + @classmethod + def get_cdk_exec_role_arn(cls, accountid, region): + """Returns the name that will be assumed to perform IAM actions on a given AWS accountid using CDK Toolkit role + Args: + accountid(string) : aws account id + Returns: + string : arn of the CDKToolkit role on the target aws account id + """ + log.info(f"Getting CDK exec role: arn:aws:iam::{accountid}:role/cdk-hnb659fds-cfn-exec-role-{accountid}-{region}") + return 'arn:aws:iam::{}:role/cdk-hnb659fds-cfn-exec-role-{}-{}'.format(accountid, accountid, region) + @classmethod def get_delegation_role_id(cls, accountid): """Returns the name that will be assumed to perform IAM actions on a given AWS accountid @@ -211,7 +225,12 @@ def get_account(cls, session=None): """ if not session: session = cls.get_session() - client = session.client('sts') + region = os.getenv('AWS_REGION', 'eu-west-1') + client = session.client( + 'sts', + region_name=region, + endpoint_url=f"https://sts.{region}.amazonaws.com" + ) response = client.get_caller_identity() return response['Account'] diff --git a/backend/dataall/cdkproxy/assets/datalakelocationcustomresource/__init__.py b/backend/dataall/cdkproxy/assets/datalakelocationcustomresource/__init__.py deleted file mode 100644 index 6c32d76cc..000000000 --- a/backend/dataall/cdkproxy/assets/datalakelocationcustomresource/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .index import * diff --git a/backend/dataall/cdkproxy/assets/datalakelocationcustomresource/index.py b/backend/dataall/cdkproxy/assets/datalakelocationcustomresource/index.py deleted file mode 100644 index 216c8b23f..000000000 --- a/backend/dataall/cdkproxy/assets/datalakelocationcustomresource/index.py +++ /dev/null @@ -1,89 +0,0 @@ -import logging -import os -import json -import boto3 -from botocore.exceptions import ClientError - -logger = logging.getLogger() -logger.setLevel(os.environ.get("LOG_LEVEL", "INFO")) -log = logging.getLogger(__name__) - -lf_client = boto3.client("lakeformation", region_name=os.environ.get("AWS_REGION")) - - -def on_event(event, context): - request_type = event["RequestType"] - if request_type == "Create": - return on_create(event) - if request_type == "Update": - return on_update(event) - if request_type == "Delete": - return on_delete(event) - raise Exception(f"Invalid request type: {request_type}") - - -def on_create(event): - """ Checks if the S3 location is already registered in Lake Formation. - If already registered it updated the roleArn for the location. - If not registered, it registers the location. - """ - props = event["ResourceProperties"] - if not _is_resource_registered(props["ResourceArn"]): - register(props) - else: - update(props) - - -def _is_resource_registered(resource_arn: str): - try: - lf_client.describe_resource(ResourceArn=resource_arn) - log.info(f"LakeFormation Resource: {resource_arn} already registered") - return True - except ClientError as client_error: - if client_error.response["Error"]["Code"] == "EntityNotFoundException": - log.info(f"LakeFormation Resource: {resource_arn} not found") - return False - else: - raise client_error - - -def register(props): - resource_arn = props["ResourceArn"] - role_arn = props["RoleArn"] - log.info(f"Registering LakeFormation Resource: {resource_arn} and roleArn: {role_arn}") - try: - lf_client.register_resource( - ResourceArn=resource_arn, - UseServiceLinkedRole=props["UseServiceLinkedRole"] == "True", - RoleArn=role_arn, - ) - except ClientError as e: - log.exception(f"Could not register LakeFormation resource: {resource_arn}") - raise Exception(f"Could not register LakeFormation resource: {resource_arn} , received {str(e)}") - - -def on_update(event): - on_create(event) - - -def update(props): - resource_arn = props["ResourceArn"] - role_arn = props["RoleArn"] - log.info(f"Updating LakeFormation Resource: {resource_arn} with roleArn: {role_arn}") - try: - lf_client.update_resource(RoleArn=role_arn, ResourceArn=resource_arn) - except ClientError as e: - log.exception(f"Could not update LakeFormation resource: {resource_arn}") - raise Exception(f"Could not update LakeFormation resource: {resource_arn}, received {str(e)}") - - -def on_delete(event): - """ Deregisters the S3 location from Lake Formation - """ - resource_arn = event["ResourceProperties"]["ResourceArn"] - log.info(f"Unregistering LakeFormation Resource: {resource_arn}") - try: - lf_client.deregister_resource(ResourceArn=resource_arn) - except ClientError as e: - log.exception(f"Could not unregister LakeFormation resource: {resource_arn}") - raise Exception(f"Could not unregister LakeFormation Resource: {resource_arn}, received {str(e)}") diff --git a/backend/dataall/cdkproxy/assets/gluedatabasecustomresource/index.py b/backend/dataall/cdkproxy/assets/gluedatabasecustomresource/index.py index 6c83b8e73..ce01c0f9a 100644 --- a/backend/dataall/cdkproxy/assets/gluedatabasecustomresource/index.py +++ b/backend/dataall/cdkproxy/assets/gluedatabasecustomresource/index.py @@ -49,18 +49,28 @@ def on_create(event): except ClientError as e: pass + default_db_exists = False + try: + glue_client.get_database(Name="default") + default_db_exists = True + except ClientError as e: + pass + if not exists: try: + db_input = props.get('DatabaseInput').copy() + if "Imported" in db_input: + del db_input["Imported"] response = glue_client.create_database( CatalogId=props.get('CatalogId'), - DatabaseInput=props.get('DatabaseInput'), + DatabaseInput=db_input, ) except ClientError as e: log.exception(f"Could not create Glue Database {props['DatabaseInput']['Name']} in aws://{AWS_ACCOUNT}/{AWS_REGION}, received {str(e)}") raise Exception(f"Could not create Glue Database {props['DatabaseInput']['Name']} in aws://{AWS_ACCOUNT}/{AWS_REGION}, received {str(e)}") Entries = [] - for i, role_arn in enumerate(props.get('DatabaseAdministrators')): + for i, role_arn in enumerate(props.get('DatabaseAdministrators', [])): Entries.append( { 'Id': str(uuid.uuid4()), @@ -100,8 +110,22 @@ def on_create(event): 'PermissionsWithGrantOption': ['SELECT', 'ALTER', 'DESCRIBE'], } ) + if default_db_exists: + Entries.append( + { + 'Id': str(uuid.uuid4()), + 'Principal': {'DataLakePrincipalIdentifier': role_arn}, + 'Resource': { + 'Database': { + 'Name': 'default' + } + }, + 'Permissions': ['Describe'.upper()], + } + ) + lf_client.batch_grant_permissions(CatalogId=props['CatalogId'], Entries=Entries) - physical_id = props['DatabaseInput']['Name'] + physical_id = props['DatabaseInput']['Imported'] + props['DatabaseInput']['Name'] return {'PhysicalResourceId': physical_id} @@ -113,18 +137,25 @@ def on_update(event): def on_delete(event): """ Deletes the created Glue database. With this action, Lake Formation permissions are also deleted. + Imported databases are not deleted """ physical_id = event['PhysicalResourceId'] - log.info('delete resource %s' % physical_id) - try: - glue_client.get_database(Name=physical_id) - except ClientError as e: - log.exception(f'Resource {physical_id} does not exists') - raise Exception(f'Resource {physical_id} does not exists') + if physical_id.startswith('IMPORTED'): + log.info(f'Imported database {physical_id} will not be deleted (it was not created by dataa.all)') + elif physical_id.startswith('CREATED'): + database_name = physical_id.replace('CREATED-', '') + log.info('delete resource %s' % database_name) + try: + glue_client.get_database(Name=database_name) + except ClientError as e: + log.exception(f'Resource {database_name} does not exists') + raise Exception(f'Resource {database_name} does not exists') - try: - response = glue_client.delete_database(CatalogId=AWS_ACCOUNT, Name=physical_id) - log.info(f'Successfully deleted database {physical_id} in aws://{AWS_ACCOUNT}/{AWS_REGION}') - except ClientError as e: - log.exception(f'Could not delete databse {physical_id} in aws://{AWS_ACCOUNT}/{AWS_REGION}') - raise Exception(f'Could not delete databse {physical_id} in aws://{AWS_ACCOUNT}/{AWS_REGION}') + try: + response = glue_client.delete_database(CatalogId=AWS_ACCOUNT, Name=database_name) + log.info(f'Successfully deleted database {database_name} in aws://{AWS_ACCOUNT}/{AWS_REGION}') + except ClientError as e: + log.exception(f'Could not delete database {database_name} in aws://{AWS_ACCOUNT}/{AWS_REGION}') + raise Exception(f'Could not delete database {database_name} in aws://{AWS_ACCOUNT}/{AWS_REGION}') + else: + log.info('Old PhysicalID, do not delete anything') diff --git a/backend/dataall/cdkproxy/assets/gluedatabasecustomresource_nodelete/__init__.py b/backend/dataall/cdkproxy/assets/gluedatabasecustomresource_nodelete/__init__.py deleted file mode 100644 index 6c32d76cc..000000000 --- a/backend/dataall/cdkproxy/assets/gluedatabasecustomresource_nodelete/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .index import * diff --git a/backend/dataall/cdkproxy/assets/gluedatabasecustomresource_nodelete/index.py b/backend/dataall/cdkproxy/assets/gluedatabasecustomresource_nodelete/index.py deleted file mode 100644 index 69abcd85c..000000000 --- a/backend/dataall/cdkproxy/assets/gluedatabasecustomresource_nodelete/index.py +++ /dev/null @@ -1,118 +0,0 @@ -import os -import json -import boto3 -from botocore.exceptions import ClientError -import uuid -import logging - -logger = logging.getLogger() -logger.setLevel(os.environ.get("LOG_LEVEL", "INFO")) -log = logging.getLogger(__name__) - -AWS_ACCOUNT = os.environ.get('AWS_ACCOUNT') -AWS_REGION = os.environ.get('AWS_REGION') -DEFAULT_ENV_ROLE_ARN = os.environ.get('DEFAULT_ENV_ROLE_ARN') -DEFAULT_CDK_ROLE_ARN = os.environ.get('DEFAULT_CDK_ROLE_ARN') - -glue_client = boto3.client('glue', region_name=AWS_REGION) -lf_client = boto3.client('lakeformation', region_name=AWS_REGION) - - -def clean_props(**props): - data = {k: props[k] for k in props.keys() if k != 'ServiceToken'} - return data - - -def on_event(event, context): - - request_type = event['RequestType'] - if request_type == 'Create': - return on_create(event) - if request_type == 'Update': - return on_update(event) - if request_type == 'Delete': - return on_delete(event) - raise Exception('Invalid request type: %s' % request_type) - - -def on_create(event): - """Creates if it does not exist Glue database for the data.all Dataset - Grants permissions to Database Administrators = dataset Admin team IAM role, pivotRole, dataset IAM role - """ - props = clean_props(**event['ResourceProperties']) - log.info('Create new resource with props %s' % props) - - exists = False - try: - glue_client.get_database(Name=props['DatabaseInput']['Name']) - exists = True - except ClientError as e: - pass - - if not exists: - try: - response = glue_client.create_database( - CatalogId=props.get('CatalogId'), - DatabaseInput=props.get('DatabaseInput'), - ) - except ClientError as e: - log.exception(f"Could not create Glue Database {props['DatabaseInput']['Name']} in aws://{AWS_ACCOUNT}/{AWS_REGION}, received {str(e)}") - raise Exception(f"Could not create Glue Database {props['DatabaseInput']['Name']} in aws://{AWS_ACCOUNT}/{AWS_REGION}, received {str(e)}") - - Entries = [] - for i, role_arn in enumerate(props.get('DatabaseAdministrators')): - Entries.append( - { - 'Id': str(uuid.uuid4()), - 'Principal': {'DataLakePrincipalIdentifier': role_arn}, - 'Resource': { - 'Database': { - # 'CatalogId': AWS_ACCOUNT, - 'Name': props['DatabaseInput']['Name'] - } - }, - 'Permissions': [ - 'Alter'.upper(), - 'Create_table'.upper(), - 'Drop'.upper(), - 'Describe'.upper(), - ], - 'PermissionsWithGrantOption': [ - 'Alter'.upper(), - 'Create_table'.upper(), - 'Drop'.upper(), - 'Describe'.upper(), - ], - } - ) - Entries.append( - { - 'Id': str(uuid.uuid4()), - 'Principal': {'DataLakePrincipalIdentifier': role_arn}, - 'Resource': { - 'Table': { - 'DatabaseName': props['DatabaseInput']['Name'], - 'TableWildcard': {}, - 'CatalogId': props.get('CatalogId'), - } - }, - 'Permissions': ['SELECT', 'ALTER', 'DESCRIBE'], - 'PermissionsWithGrantOption': ['SELECT', 'ALTER', 'DESCRIBE'], - } - ) - lf_client.batch_grant_permissions(CatalogId=props['CatalogId'], Entries=Entries) - physical_id = props['DatabaseInput']['Name'] - - return {'PhysicalResourceId': physical_id} - - -def on_update(event): - return on_create(event) - - -def on_delete(event): - """ Does not Delete the created Glue database. - This is a risky action which would be done manually by customers - """ - physical_id = event['PhysicalResourceId'] - log.info('Keeping resources %s' % physical_id) diff --git a/backend/dataall/cdkproxy/assets/glueprofilingjob/glue_script.py b/backend/dataall/cdkproxy/assets/glueprofilingjob/glue_script.py index 8279bc11c..e974c6bf9 100644 --- a/backend/dataall/cdkproxy/assets/glueprofilingjob/glue_script.py +++ b/backend/dataall/cdkproxy/assets/glueprofilingjob/glue_script.py @@ -1,4 +1,5 @@ import json +import os import logging import pprint import sys @@ -8,7 +9,6 @@ from awsglue.transforms import * from awsglue.utils import getResolvedOptions from pyspark.context import SparkContext -from pydeequ.profiles import * sc = SparkContext.getOrCreate() sc._jsc.hadoopConfiguration().set('fs.s3.canned.acl', 'BucketOwnerFullControl') @@ -32,6 +32,7 @@ 'environmentBucket', 'dataallRegion', 'table', + "SPARK_VERSION" ] try: args = getResolvedOptions(sys.argv, list_args) @@ -43,6 +44,10 @@ list_args.remove('table') args = getResolvedOptions(sys.argv, list_args) +os.environ["SPARK_VERSION"] = args.get("SPARK_VERSION", "3.1") + +from pydeequ.profiles import * + logger.info('Parsed Retrieved parameters') logger.info('Parsed Args = %s', pprint.pformat(args)) diff --git a/backend/dataall/cdkproxy/blueprints/cookiecutter_config.yaml b/backend/dataall/cdkproxy/blueprints/cookiecutter_config.yaml new file mode 100644 index 000000000..7b0c8b2e6 --- /dev/null +++ b/backend/dataall/cdkproxy/blueprints/cookiecutter_config.yaml @@ -0,0 +1,2 @@ +cookiecutters_dir: "/dataall" +replay_dir: "/dataall" \ No newline at end of file diff --git a/backend/blueprints/data_pipeline_blueprint/app_multiaccount.py b/backend/dataall/cdkproxy/blueprints/data_pipeline_blueprint/app_multiaccount.py similarity index 100% rename from backend/blueprints/data_pipeline_blueprint/app_multiaccount.py rename to backend/dataall/cdkproxy/blueprints/data_pipeline_blueprint/app_multiaccount.py diff --git a/backend/blueprints/data_pipeline_blueprint/ddk_app/__init__.py b/backend/dataall/cdkproxy/blueprints/data_pipeline_blueprint/ddk_app/__init__.py similarity index 100% rename from backend/blueprints/data_pipeline_blueprint/ddk_app/__init__.py rename to backend/dataall/cdkproxy/blueprints/data_pipeline_blueprint/ddk_app/__init__.py diff --git a/backend/blueprints/data_pipeline_blueprint/ddk_app/ddk_app_stack_multiaccount.py b/backend/dataall/cdkproxy/blueprints/data_pipeline_blueprint/ddk_app/ddk_app_stack_multiaccount.py similarity index 100% rename from backend/blueprints/data_pipeline_blueprint/ddk_app/ddk_app_stack_multiaccount.py rename to backend/dataall/cdkproxy/blueprints/data_pipeline_blueprint/ddk_app/ddk_app_stack_multiaccount.py diff --git a/backend/blueprints/data_pipeline_blueprint/utils/config.py b/backend/dataall/cdkproxy/blueprints/data_pipeline_blueprint/utils/config.py similarity index 100% rename from backend/blueprints/data_pipeline_blueprint/utils/config.py rename to backend/dataall/cdkproxy/blueprints/data_pipeline_blueprint/utils/config.py diff --git a/backend/dataall/cdkproxy/cdk_cli_wrapper.py b/backend/dataall/cdkproxy/cdk_cli_wrapper.py index d8fc98f8c..6c8932e03 100644 --- a/backend/dataall/cdkproxy/cdk_cli_wrapper.py +++ b/backend/dataall/cdkproxy/cdk_cli_wrapper.py @@ -55,7 +55,12 @@ def update_stack_output(session, stack): def deploy_cdk_stack(engine: Engine, stackid: str, app_path: str = None, path: str = None): logger.warning(f'Starting new stack from stackid {stackid}') - sts = boto3.client('sts') + region = os.getenv('AWS_REGION', 'eu-west-1') + sts = boto3.client( + 'sts', + region_name=region, + endpoint_url=f"https://sts.{region}.amazonaws.com" + ) idnty = sts.get_caller_identity() this_aws_account = idnty['Account'] creds = None diff --git a/backend/dataall/cdkproxy/cdkpipeline/cdk_pipeline.py b/backend/dataall/cdkproxy/cdkpipeline/cdk_pipeline.py index abb5fc036..66829b6fc 100644 --- a/backend/dataall/cdkproxy/cdkpipeline/cdk_pipeline.py +++ b/backend/dataall/cdkproxy/cdkpipeline/cdk_pipeline.py @@ -54,7 +54,7 @@ def __init__(self, target_uri): self.code_dir_path = os.path.realpath( os.path.abspath( os.path.join( - __file__, "..", "..", "..", "..", "blueprints", "data_pipeline_blueprint" + __file__, "..", "..", "blueprints", "data_pipeline_blueprint" ) ) ) @@ -269,6 +269,7 @@ def _set_env_vars(pipeline_environment): 'PYTHONPATH': python_path, 'PATH': python_path, 'envname': os.environ.get('envname', 'local'), + 'COOKIECUTTER_CONFIG': "/dataall/cdkproxy/blueprints/cookiecutter_config.yaml", } if env_creds: env.update( diff --git a/backend/dataall/cdkproxy/main.py b/backend/dataall/cdkproxy/main.py index 173c36496..6bbcbf22b 100644 --- a/backend/dataall/cdkproxy/main.py +++ b/backend/dataall/cdkproxy/main.py @@ -56,7 +56,12 @@ def up(response: Response): def check_creds(response: Response): logger.info('GET /awscreds') try: - sts = boto3.client('sts', region_name=os.getenv('AWS_REGION', 'eu-west-1')) + region = os.getenv('AWS_REGION', 'eu-west-1') + sts = boto3.client( + 'sts', + region_name=region, + endpoint_url=f"https://sts.{region}.amazonaws.com" + ) data = sts.get_caller_identity() return { '_ts': datetime.now().isoformat(), @@ -85,7 +90,7 @@ def check_connect(response: Response): engine = connect() return { '_ts': datetime.now().isoformat(), - 'message': f"Connected to database for environment {ENVNAME}({engine.dbconfig.params['host']}:{engine.dbconfig.params['port']})", + 'message': f"Connected to database for environment {ENVNAME}({engine.dbconfig.host})", } except Exception as e: logger.exception('DBCONNECTIONERROR') diff --git a/backend/dataall/cdkproxy/requirements.txt b/backend/dataall/cdkproxy/requirements.txt index f85335573..a7b91a402 100644 --- a/backend/dataall/cdkproxy/requirements.txt +++ b/backend/dataall/cdkproxy/requirements.txt @@ -1,4 +1,4 @@ -aws-cdk-lib==2.77.0 +aws-cdk-lib==2.78.0 aws_cdk.aws_redshift_alpha==2.14.0a0 boto3==1.24.85 boto3-stubs==1.24.85 diff --git a/backend/dataall/cdkproxy/stacks/dataset.py b/backend/dataall/cdkproxy/stacks/dataset.py index d5306f5f0..3e75633d3 100644 --- a/backend/dataall/cdkproxy/stacks/dataset.py +++ b/backend/dataall/cdkproxy/stacks/dataset.py @@ -1,12 +1,9 @@ import logging import os -import typing from aws_cdk import ( - custom_resources as cr, aws_s3 as s3, aws_kms as kms, - aws_lambda as _lambda, aws_iam as iam, aws_ssm as ssm, aws_glue as glue, @@ -98,14 +95,18 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): quicksight_default_group_arn = None if env.dashboardsEnabled: - quicksight_default_group = Quicksight.create_quicksight_group(AwsAccountId=env.AwsAccountId) - quicksight_default_group_arn = quicksight_default_group['Group']['Arn'] + quicksight_default_group_arn = f"arn:aws:quicksight:{dataset.region}:{dataset.AwsAccountId}:group/default/{Quicksight._DEFAULT_GROUP_NAME}" # Dataset S3 Bucket and KMS key + dataset_key = False if dataset.imported and dataset.importedS3Bucket: dataset_bucket = s3.Bucket.from_bucket_name( self, f'ImportedBucket{dataset.datasetUri}', dataset.S3BucketName ) + if dataset.importedKmsKey: + dataset_key = kms.Key.from_lookup( + self, f'ImportedKey{dataset.datasetUri}', alias_name=f"alias/{dataset.KmsAlias}" + ) else: dataset_key = kms.Key( self, @@ -113,21 +114,51 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): alias=dataset.KmsAlias, enable_key_rotation=True, policy=iam.PolicyDocument( - assign_sids=True, statements=[ iam.PolicyStatement( + sid="EnableDatasetOwnerKeyUsage", resources=['*'], effect=iam.Effect.ALLOW, principals=[ - iam.AccountPrincipal(account_id=dataset.AwsAccountId), - iam.ArnPrincipal( - f'arn:aws:iam::{env.AwsAccountId}:role/{self.pivot_role_name}' - ), + iam.ArnPrincipal(env_group.environmentIAMRoleArn), + ], + actions=[ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:DescribeKey", + "kms:List*", + "kms:GetKeyPolicy", + ], + ), + iam.PolicyStatement( + sid='KMSPivotRolePermissions', + effect=iam.Effect.ALLOW, + actions=[ + 'kms:Decrypt', + 'kms:Encrypt', + 'kms:GenerateDataKey*', + 'kms:PutKeyPolicy', + "kms:GetKeyPolicy", + 'kms:ReEncrypt*', + 'kms:TagResource', + 'kms:UntagResource', + 'kms:DeleteAlias', + 'kms:DescribeKey', + 'kms:CreateAlias', + 'kms:List*', + ], + resources=['*'], + principals=[ + iam.ArnPrincipal(f'arn:aws:iam::{env.AwsAccountId}:role/{self.pivot_role_name}') ], - actions=['kms:*'], ) - ], + ] ), + admins=[ + iam.ArnPrincipal(env.CDKRoleArn), + ] ) dataset_bucket = s3.Bucket( @@ -194,95 +225,141 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): policy_name=dataset.S3BucketName, statements=[ iam.PolicyStatement( - actions=['s3:List*'], resources=['*'], effect=iam.Effect.ALLOW - ), - iam.PolicyStatement( - actions=['logs:*'], resources=['*'], effect=iam.Effect.ALLOW - ), - iam.PolicyStatement( - actions=['tag:*'], resources=['*'], effect=iam.Effect.ALLOW + sid="ListAll", + actions=[ + "s3:ListAllMyBuckets", + "s3:ListAccessPoints", + ], + resources=["*"], + effect=iam.Effect.ALLOW ), iam.PolicyStatement( - actions=['s3:List*', 's3:Get*'], + sid="ListDatasetBucket", + actions=[ + "s3:ListBucket", + "s3:GetBucketLocation" + ], resources=[dataset_bucket.bucket_arn], effect=iam.Effect.ALLOW, ), iam.PolicyStatement( - actions=['s3:*'], + sid="ReadWriteDatasetBucket", + actions=[ + "s3:PutObject", + "s3:PutObjectAcl", + "s3:GetObject", + "s3:GetObjectAcl", + "s3:GetObjectVersion", + "s3:DeleteObject" + ], effect=iam.Effect.ALLOW, resources=[dataset_bucket.bucket_arn + '/*'], ), iam.PolicyStatement( + sid="ReadAccessPointsDatasetBucket", actions=[ 's3:GetAccessPoint', 's3:GetAccessPointPolicy', - 's3:ListAccessPoints', - 's3:CreateAccessPoint', - 's3:DeleteAccessPoint', 's3:GetAccessPointPolicyStatus', - 's3:DeleteAccessPointPolicy', - 's3:PutAccessPointPolicy', ], effect=iam.Effect.ALLOW, resources=[ - f'arn:aws:s3:{dataset.region}:{dataset.AwsAccountId}:accesspoint/*', + f'arn:aws:s3:{dataset.region}:{dataset.AwsAccountId}:accesspoint/{dataset.datasetUri}*', ], ), iam.PolicyStatement( - actions=['s3:List*', 's3:Get*'], - resources=[f'arn:aws:s3:::{env.EnvironmentDefaultBucketName}'], - effect=iam.Effect.ALLOW, - ), - iam.PolicyStatement( - actions=['s3:*'], - effect=iam.Effect.ALLOW, - resources=[f'arn:aws:s3:::{env.EnvironmentDefaultBucketName}/*'], - ), - iam.PolicyStatement( + sid="GlueAccessCrawler", + actions=[ + "glue:Get*", + "glue:BatchGet*", + "glue:CreateTable", + "glue:UpdateTable", + "glue:DeleteTableVersion", + "glue:DeleteTable", + ], effect=iam.Effect.ALLOW, - resources=['arn:aws:s3:::aws-glue-*'], - actions=['s3:CreateBucket'], + resources=[ + f"arn:aws:glue:*:{dataset.AwsAccountId}:catalog", + f"arn:aws:glue:{dataset.region}:{dataset.AwsAccountId}:database/{dataset.GlueDatabaseName}", + f"arn:aws:glue:{dataset.region}:{dataset.AwsAccountId}:table/{dataset.GlueDatabaseName}/*" + ] ), iam.PolicyStatement( - actions=['s3:GetObject', 's3:PutObject', 's3:DeleteObject'], + sid="GlueAccessDefault", + actions=[ + "glue:GetDatabase", + ], effect=iam.Effect.ALLOW, resources=[ - 'arn:aws:s3:::aws-glue-*/*', - 'arn:aws:s3:::*/*aws-glue-*/*', - ], + f"arn:aws:glue:{dataset.region}:{dataset.AwsAccountId}:database/default", + ] ), iam.PolicyStatement( - actions=['s3:GetObject'], + sid="CreateLoggingGlue", + actions=[ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + ], effect=iam.Effect.ALLOW, resources=[ - 'arn:aws:s3:::crawler-public*', - 'arn:aws:s3:::aws-glue-*', + f'arn:aws:logs:{dataset.region}:{dataset.AwsAccountId}:log-group:/aws-glue/crawlers*', + f'arn:aws:logs:{dataset.region}:{dataset.AwsAccountId}:log-group:/aws-glue/jobs/*', ], ), iam.PolicyStatement( + sid="LoggingGlue", actions=[ - 'logs:CreateLogGroup', - 'logs:CreateLogStream', 'logs:PutLogEvents', ], effect=iam.Effect.ALLOW, - resources=['arn:aws:logs:*:*:/aws-glue/*'], + resources=[ + f'arn:aws:logs:{dataset.region}:{dataset.AwsAccountId}:log-group:/aws-glue/crawlers:log-stream:{dataset.GlueCrawlerName}', + f'arn:aws:logs:{dataset.region}:{dataset.AwsAccountId}:log-group:/aws-glue/jobs/*', + ], ), iam.PolicyStatement( - actions=['kms:*'], effect=iam.Effect.ALLOW, resources=['*'] + actions=['s3:ListBucket'], + resources=[f'arn:aws:s3:::{env.EnvironmentDefaultBucketName}'], + effect=iam.Effect.ALLOW ), iam.PolicyStatement( - actions=['glue:*', 'athena:*', 'lakeformation:*'], - resources=['*'], + sid="ReadEnvironmentBucketProfiling", + actions=[ + "s3:GetObject", + "s3:GetObjectAcl", + "s3:GetObjectVersion" + ], effect=iam.Effect.ALLOW, + resources=[f'arn:aws:s3:::{env.EnvironmentDefaultBucketName}/profiling/code/*'], ), iam.PolicyStatement( - actions=['cloudformation:*'], - resources=['*'], + sid="ReadWriteEnvironmentBucketProfiling", + actions=[ + "s3:PutObject", + "s3:PutObjectAcl", + "s3:GetObject", + "s3:GetObjectAcl", + "s3:GetObjectVersion", + "s3:DeleteObject" + ], + resources=[f'arn:aws:s3:::{env.EnvironmentDefaultBucketName}/profiling/results/{dataset.datasetUri}/*'], effect=iam.Effect.ALLOW, ), ], ) + if dataset_key: + dataset_admin_policy.add_statements( + iam.PolicyStatement( + sid="KMSAccess", + actions=[ + "kms:Decrypt", + "kms:Encrypt", + "kms:GenerateDataKey" + ], + effect=iam.Effect.ALLOW, + resources=[dataset_key.key_arn], + ) + ) dataset_admin_policy.node.add_dependency(dataset_bucket) dataset_admin_role = iam.Role( @@ -290,24 +367,36 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): 'DatasetAdminRole', role_name=dataset.IAMDatasetAdminRoleArn.split('/')[-1], assumed_by=iam.CompositePrincipal( - iam.ServicePrincipal('glue.amazonaws.com'), - iam.ServicePrincipal('lakeformation.amazonaws.com'), - iam.ServicePrincipal('athena.amazonaws.com'), - iam.ServicePrincipal('sagemaker.amazonaws.com'), - iam.ServicePrincipal('lambda.amazonaws.com'), - iam.ServicePrincipal('ec2.amazonaws.com'), - iam.AccountPrincipal(str(os.environ.get('CURRENT_AWS_ACCOUNT'))), - iam.AccountPrincipal(dataset.AwsAccountId), iam.ArnPrincipal( f'arn:aws:iam::{dataset.AwsAccountId}:role/{self.pivot_role_name}' ), + iam.ServicePrincipal('glue.amazonaws.com'), ), ) dataset_admin_policy.attach_to_role(dataset_admin_role) + # Add Key Policy For Users + if not dataset.imported: + dataset_key.add_to_resource_policy( + iam.PolicyStatement( + sid="EnableDatasetIAMRoleKeyUsage", + resources=['*'], + effect=iam.Effect.ALLOW, + principals=[dataset_admin_role], + actions=[ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:DescribeKey" + ], + ) + ) + # Datalake location custom resource: registers the S3 location in LakeFormation registered_location = LakeFormation.check_existing_lf_registered_location( resource_arn=f'arn:aws:s3:::{dataset.S3BucketName}', + role_arn=dataset.IAMDatasetAdminRoleArn, accountid=env.AwsAccountId, region=env.region ) @@ -333,45 +422,6 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): if quicksight_default_group_arn: dataset_admins.append(quicksight_default_group_arn) - # Glue Database custom resource: creates the Glue database and grants the default permissions (dataset role, admin, pivotrole, QS group) - # Old provider, to be deleted in future release - glue_db_handler_arn = ssm.StringParameter.from_string_parameter_name( - self, - 'GlueDbCRArnParameter', - string_parameter_name=f'/dataall/{dataset.environmentUri}/cfn/custom-resources/lambda/arn', - ) - - glue_db_handler = _lambda.Function.from_function_attributes( - self, - 'CustomGlueDatabaseHandler', - function_arn=glue_db_handler_arn.string_value, - same_environment=True, - ) - - GlueDatabase = cr.Provider( - self, - f'{env.resourcePrefix}GlueDbCustomResourceProvider', - on_event_handler=glue_db_handler, - ) - old_glue_db = CustomResource( - self, - f'{env.resourcePrefix}DatasetDatabase', - service_token=GlueDatabase.service_token, - resource_type='Custom::GlueDatabase', - properties={ - 'CatalogId': dataset.AwsAccountId, - 'DatabaseInput': { - 'Description': 'dataall database {} '.format( - dataset.GlueDatabaseName - ), - 'LocationUri': f's3://{dataset.S3BucketName}/', - 'Name': f'{dataset.GlueDatabaseName}', - 'CreateTableDefaultPermissions': [], - }, - 'DatabaseAdministrators': dataset_admins, - }, - ) - # Get the Provider service token from SSM, the Lambda and Provider are created as part of the environment stack glue_db_provider_service_token = ssm.StringParameter.from_string_parameter_name( self, @@ -393,8 +443,10 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): 'LocationUri': f's3://{dataset.S3BucketName}/', 'Name': f'{dataset.GlueDatabaseName}', 'CreateTableDefaultPermissions': [], + 'Imported': 'IMPORTED-' if dataset.imported else 'CREATED-' }, - 'DatabaseAdministrators': dataset_admins + 'DatabaseAdministrators': dataset_admins, + 'TriggerUpdate': True }, ) @@ -402,7 +454,7 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): crawler = glue.CfnCrawler( self, dataset.GlueCrawlerName, - description=f'datall Glue Crawler for bucket {dataset.S3BucketName}', + description=f'datall Glue Crawler for S3 Bucket {dataset.S3BucketName}', name=dataset.GlueCrawlerName, database_name=dataset.GlueDatabaseName, schedule={'scheduleExpression': f'{dataset.GlueCrawlerSchedule}'} @@ -418,7 +470,7 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): crawler.node.add_dependency(dataset_bucket) job_args = { - '--additional-python-modules': 'pydeequ,great_expectations,requests', + '--additional-python-modules': 'urllib3<2,pydeequ', '--datasetUri': dataset.datasetUri, '--database': dataset.GlueDatabaseName, '--datasetRegion': dataset.region, @@ -435,15 +487,15 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): '--enable-metrics': 'true', '--enable-continuous-cloudwatch-log': 'true', '--enable-glue-datacatalog': 'true', + '--SPARK_VERSION': '3.1', } job = glue.CfnJob( self, 'DatasetGlueProfilingJob', name=dataset.GlueProfilingJobName, - role=iam.ArnPrincipal( - f'arn:aws:iam::{env.AwsAccountId}:role/{self.pivot_role_name}' - ).arn, + description=f'datall Glue Profiling job for dataset {dataset.label}', + role=dataset_admin_role.role_arn, allocated_capacity=10, execution_property=glue.CfnJob.ExecutionPropertyProperty( max_concurrent_runs=100 @@ -465,6 +517,7 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): self, 'DatasetGlueProfilingTrigger', name=dataset.GlueProfilingTriggerName, + description=f'datall Glue Profiling trigger schedule for dataset {dataset.label}', type='SCHEDULED', schedule=dataset.GlueProfilingTriggerSchedule, start_on_creation=True, diff --git a/backend/dataall/cdkproxy/stacks/environment.py b/backend/dataall/cdkproxy/stacks/environment.py index e004c1f6e..0368d5dcf 100644 --- a/backend/dataall/cdkproxy/stacks/environment.py +++ b/backend/dataall/cdkproxy/stacks/environment.py @@ -147,6 +147,7 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): self.pivot_role_name = SessionHelper.get_delegation_role_name() self.external_id = SessionHelper.get_external_id_secret() self.dataall_central_account = SessionHelper.get_account() + pivot_role_as_part_of_environment_stack = ParameterStoreManager.get_parameter_value( region=os.getenv('AWS_REGION', 'eu-west-1'), parameter_path=f"/dataall/{os.getenv('envname', 'local')}/pivotRole/enablePivotRoleAutoCreate" @@ -166,6 +167,27 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): self.all_environment_datasets = self.get_all_environment_datasets(self.engine, self._environment) + # Create or import Pivot role + if self.create_pivot_role is True: + config = { + 'roleName': self.pivot_role_name, + 'accountId': self.dataall_central_account, + 'externalId': self.external_id, + 'resourcePrefix': self._environment.resourcePrefix, + } + pivot_role_stack = PivotRole(self, 'PivotRoleStack', config) + self.pivot_role = iam.Role.from_role_arn( + self, + f'PivotRole{self._environment.environmentUri}', + pivot_role_stack.pivot_role.role_arn, + ) + else: + self.pivot_role = iam.Role.from_role_arn( + self, + f'PivotRole{self._environment.environmentUri}', + f'arn:aws:iam::{self._environment.AwsAccountId}:role/{self.pivot_role_name}', + ) + # Environment S3 Bucket default_environment_bucket = s3.Bucket( self, @@ -177,17 +199,6 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): versioned=True, enforce_ssl=True, ) - default_environment_bucket.add_to_resource_policy( - iam.PolicyStatement( - sid='RedshiftLogging', - actions=['s3:PutObject', 's3:GetBucketAcl'], - resources=[ - f'{default_environment_bucket.bucket_arn}/*', - default_environment_bucket.bucket_arn, - ], - principals=[iam.ServicePrincipal('redshift.amazonaws.com')], - ) - ) default_environment_bucket.add_to_resource_policy( iam.PolicyStatement( @@ -237,7 +248,7 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): ) # Create or import team IAM roles - default_role = self.create_or_import_environment_default_role() + default_role = self.create_or_import_environment_admin_group_role() group_roles = self.create_or_import_environment_groups_roles() self.create_default_athena_workgroup( @@ -246,22 +257,7 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): ) self.create_athena_workgroups(self.environment_groups, default_environment_bucket) - # Create or import Pivot role - if self.create_pivot_role is True: - config = { - 'roleName': self.pivot_role_name, - 'accountId': self.dataall_central_account, - 'externalId': self.external_id, - 'resourcePrefix': self._environment.resourcePrefix, - } - pivot_role_stack = PivotRole(self, 'PivotRoleStack', config) - self.pivot_role = pivot_role_stack.pivot_role - else: - self.pivot_role = iam.Role.from_role_arn( - self, - f'PivotRole{self._environment.environmentUri}', - f'arn:aws:iam::{self._environment.AwsAccountId}:role/{self.pivot_role_name}', - ) + kms_key = self.set_cr_kms_key(group_roles, default_role) # Lakeformation default settings custom resource # Set PivotRole as Lake Formation data lake admin @@ -270,7 +266,8 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): ) lakeformation_cr_dlq = self.set_dlq( - f'{self._environment.resourcePrefix}-lfcr-{self._environment.environmentUri}' + f'{self._environment.resourcePrefix}-lfcr-{self._environment.environmentUri}', + kms_key ) lf_default_settings_custom_resource = _lambda.Function( self, @@ -325,55 +322,17 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): string_value=lf_default_settings_custom_resource.function_name, parameter_name=f'/dataall/{self._environment.environmentUri}/cfn/lf/defaultsettings/lambda/name', ) - # Glue database custom resource - Old, to be deleted in future release - entry_point = str( - pathlib.PosixPath(os.path.dirname(__file__), '../assets/gluedatabasecustomresource_nodelete').resolve() - ) - gluedb_cr_dlq = self.set_dlq(f'{self._environment.resourcePrefix}-gluedbcr-{self._environment.environmentUri}') - gluedb_custom_resource = _lambda.Function( - self, - 'GlueDatabaseCustomResourceHandler', - function_name=f'{self._environment.resourcePrefix}-gluedb-handler-{self._environment.environmentUri}', - role=self.pivot_role, - handler='index.on_event', - code=_lambda.Code.from_asset(entry_point), - memory_size=1664, - description='This Lambda function is a cloudformation custom resource provider for Glue database ' - 'as Cfn currently does not support the CreateTableDefaultPermissions parameter', - timeout=Duration.seconds(5 * 60), - environment={ - 'envname': self._environment.name, - 'LOG_LEVEL': 'DEBUG', - 'AWS_ACCOUNT': self._environment.AwsAccountId, - 'DEFAULT_ENV_ROLE_ARN': self._environment.EnvironmentDefaultIAMRoleArn, - 'DEFAULT_CDK_ROLE_ARN': self._environment.CDKRoleArn, - }, - dead_letter_queue_enabled=True, - dead_letter_queue=gluedb_cr_dlq, - on_failure=lambda_destination.SqsDestination(gluedb_cr_dlq), - tracing=_lambda.Tracing.ACTIVE, - runtime=_lambda.Runtime.PYTHON_3_9, - ) - ssm.StringParameter( - self, - 'GlueCustomResourceFunctionArn', - string_value=gluedb_custom_resource.function_arn, - parameter_name=f'/dataall/{self._environment.environmentUri}/cfn/custom-resources/lambda/arn', - ) - ssm.StringParameter( - self, - 'GlueCustomResourceFunctionName', - string_value=gluedb_custom_resource.function_name, - parameter_name=f'/dataall/{self._environment.environmentUri}/cfn/custom-resources/lambda/name', - ) # Glue database custom resource - New # This Lambda is triggered with the creation of each dataset, it is not executed when the environment is created entry_point = str( pathlib.PosixPath(os.path.dirname(__file__), '../assets/gluedatabasecustomresource').resolve() ) - gluedb_lf_cr_dlq = self.set_dlq(f'{self._environment.resourcePrefix}-gluedb-lf-cr-{self._environment.environmentUri}') + gluedb_lf_cr_dlq = self.set_dlq( + f'{self._environment.resourcePrefix}-gluedb-lf-cr-{self._environment.environmentUri}', + kms_key + ) gluedb_lf_custom_resource = _lambda.Function( self, 'GlueDatabaseLFCustomResourceHandler', @@ -425,84 +384,61 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): parameter_name=f'/dataall/{self._environment.environmentUri}/cfn/custom-resources/gluehandler/provider/servicetoken', ) - # Data lake location custom resource - entry_point = str( - pathlib.PosixPath( - os.path.dirname(__file__), "../assets/datalakelocationcustomresource" - ).resolve() - ) - - datalakelocation_cr_dlq = self.set_dlq( - f'{self._environment.resourcePrefix}-datalakelocationcr-{self._environment.environmentUri}' - ) - datalake_location_custom_resource = _lambda.Function( - self, - "DatalakeLocationCustomResourceHandler", - function_name=f'{self._environment.resourcePrefix}-datalakelocation-handler-{self._environment.environmentUri}', - role=self.pivot_role, - handler="index.on_event", - code=_lambda.Code.from_asset(entry_point), - memory_size=1664, - description='This Lambda function is a cloudformation custom resource provider for LakeFormation Storage Locations ' - 'as the Cfn resource cannot handle pivotRole updates', - timeout=Duration.seconds(5 * 60), - environment={ - 'envname': self._environment.name, - 'LOG_LEVEL': 'DEBUG', - 'AWS_ACCOUNT': self._environment.AwsAccountId, - 'DEFAULT_ENV_ROLE_ARN': self._environment.EnvironmentDefaultIAMRoleArn, - 'DEFAULT_CDK_ROLE_ARN': self._environment.CDKRoleArn, - }, - dead_letter_queue_enabled=True, - dead_letter_queue=datalakelocation_cr_dlq, - on_failure=lambda_destination.SqsDestination(datalakelocation_cr_dlq), - tracing=_lambda.Tracing.ACTIVE, - runtime=_lambda.Runtime.PYTHON_3_9, - ) - - datalake_location_provider = cr.Provider( - self, - f"{self._environment.resourcePrefix}DatalakeLocationProvider", - on_event_handler=datalake_location_custom_resource - ) - - ssm.StringParameter( - self, - "DatalakeLocationCustomResourceFunctionArn", - string_value=datalake_location_custom_resource.function_arn, - parameter_name=f"/dataall/{self._environment.environmentUri}/cfn/custom-resources/datalocationhandler/lambda/arn", - ) - - ssm.StringParameter( - self, - "DatalakeLocationCustomResourceFunctionName", - string_value=datalake_location_custom_resource.function_name, - parameter_name=f"/dataall/{self._environment.environmentUri}/cfn/custom-resources/datalocationhandler/lambda/name", - ) - - ssm.StringParameter( - self, - 'DataLocationCustomResourceProviderServiceToken', - string_value=datalake_location_provider.service_token, - parameter_name=f'/dataall/{self._environment.environmentUri}/cfn/custom-resources/datalocationhandler/provider/servicetoken', - ) - # Create SNS topics for subscriptions if self._environment.subscriptionsEnabled: - queue_key = kms.Key( + subscription_key_policy = iam.PolicyDocument( + assign_sids=True, + statements=[ + iam.PolicyStatement( + actions=[ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + ], + effect=iam.Effect.ALLOW, + principals=[default_role] + group_roles, + resources=["*"], + conditions={ + "StringEquals": { + "kms:ViaService": [ + f"sqs.{self._environment.region}.amazonaws.com", + f"sns.{self._environment.region}.amazonaws.com", + ] + } + } + ), + iam.PolicyStatement( + actions=[ + "kms:DescribeKey", + "kms:List*", + "kms:GetKeyPolicy", + ], + effect=iam.Effect.ALLOW, + principals=[default_role] + group_roles, + resources=["*"], + ) + ] + ) + subscription_key = kms.Key( self, - f'{self._environment.resourcePrefix}-producers-queue-key', + f'dataall-env-{self._environment.environmentUri}-subscription-key', removal_policy=RemovalPolicy.DESTROY, - alias=f'{self._environment.resourcePrefix}-producers-queue-key', + alias=f'dataall-env-{self._environment.environmentUri}-subscription-key', enable_key_rotation=True, + admins=[ + iam.ArnPrincipal(self._environment.CDKRoleArn), + ], + policy=subscription_key_policy ) + dlq_queue = sqs.Queue( self, f'ProducersSubscriptionsQueue-{self._environment.environmentUri}-dlq', queue_name=f'{self._environment.resourcePrefix}-producers-dlq-{self._environment.environmentUri}', retention_period=Duration.days(14), encryption=sqs.QueueEncryption.KMS, - encryption_master_key=queue_key, + encryption_master_key=subscription_key, ) dlq_queue.add_to_resource_policy( iam.PolicyStatement( @@ -527,7 +463,7 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): queue_name=f'{self._environment.resourcePrefix}-producers-queue-{self._environment.environmentUri}', dead_letter_queue=self.dlq, encryption=sqs.QueueEncryption.KMS, - encryption_master_key=queue_key, + encryption_master_key=subscription_key, ) if self._environment.subscriptionsProducersTopicImported: @@ -541,6 +477,7 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): self._environment.subscriptionsProducersTopicName, self.dataall_central_account, self._environment, + subscription_key ) topic.add_subscription(sns_subs.SqsSubscription(queue)) @@ -592,6 +529,7 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): self._environment.subscriptionsConsumersTopicName, self.dataall_central_account, self._environment, + subscription_key ) # Create or import SageMaker Studio domain if ML Studio enabled @@ -615,16 +553,16 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): # print the IAM role arn for this service account CfnOutput( self, - 'pivotRoleName', - export_name='pivotRoleName', + f'pivotRoleName-{self._environment.environmentUri}', + export_name=f'pivotRoleName-{self._environment.environmentUri}', value=self.pivot_role_name, - description='pivotRoleName', + description='pivotRole name, helps us to distinguish between auto-created pivot roles (dataallPivotRole-cdk) and manually created pivot roles (dataallPivotRole)', ) TagsUtil.add_tags(self) CDKNagUtil.check_rules(self) - def create_or_import_environment_default_role(self): + def create_or_import_environment_admin_group_role(self): if self._environment.EnvironmentDefaultIAMRoleImported: default_role = iam.Role.from_role_arn( self, @@ -632,65 +570,15 @@ def create_or_import_environment_default_role(self): self._environment.EnvironmentDefaultIAMRoleArn, ) else: - services_policies = ServicePolicy( - stack=self, - tag_key='Team', - tag_value=self._environment.SamlGroupName, - resource_prefix=self._environment.resourcePrefix, - name=f'{self._environment.resourcePrefix}-{self._environment.SamlGroupName}-{self._environment.environmentUri}-default-services-policy', - id=f'{self._environment.resourcePrefix}-{self._environment.SamlGroupName}-{self._environment.environmentUri}-default-services-policy', - account=self._environment.AwsAccountId, - region=self._environment.region, - role_name=self._environment.EnvironmentDefaultIAMRoleName, - permissions=self.get_environment_group_permissions( - self.engine, - self._environment.environmentUri, - self._environment.SamlGroupName, - ), - ).generate_policies() - - data_policy = DataPolicy( - stack=self, - tag_key='Team', - tag_value=self._environment.SamlGroupName, - resource_prefix=self._environment.resourcePrefix, - name=f'{self._environment.resourcePrefix}-{self._environment.SamlGroupName}-default-data-policy', - id=f'{self._environment.resourcePrefix}-{self._environment.SamlGroupName}-default-data-policy', - account=self._environment.AwsAccountId, - region=self._environment.region, - environment=self._environment, - team=self.environment_admins_group, - datasets=self.all_environment_datasets, - ).generate_admins_data_access_policy() - - default_role = iam.Role( - self, - 'DefaultEnvironmentRole', - role_name=self._environment.EnvironmentDefaultIAMRoleName, - inline_policies={ - f'DataPolicy{self._environment.environmentUri}': data_policy.document, - }, - managed_policies=services_policies, - assumed_by=iam.CompositePrincipal( - iam.ServicePrincipal('glue.amazonaws.com'), - iam.ServicePrincipal('lambda.amazonaws.com'), - iam.ServicePrincipal('lakeformation.amazonaws.com'), - iam.ServicePrincipal('athena.amazonaws.com'), - iam.ServicePrincipal('states.amazonaws.com'), - iam.ServicePrincipal('sagemaker.amazonaws.com'), - iam.ServicePrincipal('redshift.amazonaws.com'), - iam.ServicePrincipal('databrew.amazonaws.com'), - iam.AccountPrincipal(self._environment.AwsAccountId), - ), - ) - return default_role + environment_admin_group_role = self.create_group_environment_role(group=self.environment_admins_group, id='DefaultEnvironmentRole') + return environment_admin_group_role def create_or_import_environment_groups_roles(self): group: models.EnvironmentGroup group_roles = [] for group in self.environment_groups: if not group.environmentIAMRoleImported: - group_role = self.create_group_environment_role(group) + group_role = self.create_group_environment_role(group=group, id=f'{group.environmentIAMRoleName}') group_roles.append(group_role) else: iam.Role.from_role_arn( @@ -700,7 +588,7 @@ def create_or_import_environment_groups_roles(self): ) return group_roles - def create_group_environment_role(self, group): + def create_group_environment_role(self, group: models.EnvironmentGroup, id: str): group_permissions = self.get_environment_group_permissions( self.engine, self._environment.environmentUri, group.groupUri @@ -715,6 +603,8 @@ def create_group_environment_role(self, group): role_name=group.environmentIAMRoleName, account=self._environment.AwsAccountId, region=self._environment.region, + environment=self._environment, + team=group, permissions=group_permissions, ).generate_policies() @@ -734,7 +624,7 @@ def create_group_environment_role(self, group): group_role = iam.Role( self, - f'{group.environmentIAMRoleName}', + id, role_name=group.environmentIAMRoleName, inline_policies={ f'{group.environmentIAMRoleName}DataPolicy': data_policy.document, @@ -743,12 +633,12 @@ def create_group_environment_role(self, group): assumed_by=iam.CompositePrincipal( iam.ServicePrincipal('glue.amazonaws.com'), iam.ServicePrincipal('lambda.amazonaws.com'), - iam.ServicePrincipal('lakeformation.amazonaws.com'), - iam.ServicePrincipal('athena.amazonaws.com'), - iam.ServicePrincipal('states.amazonaws.com'), iam.ServicePrincipal('sagemaker.amazonaws.com'), - iam.ServicePrincipal('redshift.amazonaws.com'), - iam.AccountPrincipal(self._environment.AwsAccountId), + iam.ServicePrincipal('states.amazonaws.com'), + iam.ServicePrincipal('databrew.amazonaws.com'), + iam.ServicePrincipal('codebuild.amazonaws.com'), + iam.ServicePrincipal('codepipeline.amazonaws.com'), + self.pivot_role, ), ) Tags.of(group_role).add('group', group.groupUri) @@ -788,7 +678,7 @@ def create_athena_workgroup(self, output_bucket, workgroup_name): ) return athena_workgroup - def create_topic(self, construct_id, central_account, environment): + def create_topic(self, construct_id, central_account, environment, kms_key): actions = [ 'SNS:GetTopicAttributes', 'SNS:SetTopicAttributes', @@ -800,14 +690,13 @@ def create_topic(self, construct_id, central_account, environment): 'SNS:Publish', 'SNS:Receive', ] - topic_key = kms.Key( + topic = sns.Topic( self, - f'{construct_id}-topic-key', - removal_policy=RemovalPolicy.DESTROY, - alias=f'{construct_id}-topic-key', - enable_key_rotation=True, + f'{construct_id}', + topic_name=f'{construct_id}', + master_key=kms_key ) - topic = sns.Topic(self, f'{construct_id}', topic_name=f'{construct_id}', master_key=topic_key) + topic.add_to_resource_policy( iam.PolicyStatement( principals=[iam.AccountPrincipal(central_account)], @@ -832,22 +721,62 @@ def zip_code(assetspath, s3_key='profiler'): shutil.make_archive(base_name=f'{assetspath}/{s3_key}', format='zip', root_dir=f'{assetspath}') return assetspath - def set_dlq(self, queue_name) -> sqs.Queue: - queue_key = kms.Key( + def set_cr_kms_key(self, group_roles, default_role) -> kms.Key: + key_policy = iam.PolicyDocument( + assign_sids=True, + statements=[ + iam.PolicyStatement( + actions=[ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + ], + effect=iam.Effect.ALLOW, + principals=[ + default_role, + ] + group_roles, + resources=["*"], + conditions={ + "StringEquals": {"kms:ViaService": f"sqs.{self._environment.region}.amazonaws.com"} + } + ), + iam.PolicyStatement( + actions=[ + "kms:DescribeKey", + "kms:List*", + "kms:GetKeyPolicy", + ], + effect=iam.Effect.ALLOW, + principals=[ + default_role, + ] + group_roles, + resources=["*"], + ) + ] + ) + + kms_key = kms.Key( self, - f'{queue_name}-key', + f'dataall-environment-{self._environment.environmentUri}-cr-key', removal_policy=RemovalPolicy.DESTROY, - alias=f'{queue_name}-key', + alias=f'dataall-environment-{self._environment.environmentUri}-cr-key', enable_key_rotation=True, + admins=[ + iam.ArnPrincipal(self._environment.CDKRoleArn), + ], + policy=key_policy ) + return kms_key + def set_dlq(self, queue_name, kms_key) -> sqs.Queue: dlq = sqs.Queue( self, f'{queue_name}-queue', queue_name=f'{queue_name}', retention_period=Duration.days(14), encryption=sqs.QueueEncryption.KMS, - encryption_master_key=queue_key, + encryption_master_key=kms_key, data_key_reuse=Duration.days(1), removal_policy=RemovalPolicy.DESTROY, ) diff --git a/backend/dataall/cdkproxy/stacks/notebook.py b/backend/dataall/cdkproxy/stacks/notebook.py index dd80de060..de50484eb 100644 --- a/backend/dataall/cdkproxy/stacks/notebook.py +++ b/backend/dataall/cdkproxy/stacks/notebook.py @@ -15,6 +15,7 @@ from ...db import models from ...db.api import Environment from ...utils.cdk_nag_utils import CDKNagUtil +from ...aws.handlers.sts import SessionHelper from ...utils.runtime_stacks_tagging import TagsUtil logger = logging.getLogger(__name__) @@ -62,11 +63,16 @@ def __init__(self, scope, id: str, target_uri: str = None, **kwargs) -> None: env_group = self.get_env_group(notebook) + cdk_exec_role = SessionHelper.get_cdk_exec_role_arn(notebook.AWSAccountId, notebook.region) + notebook_key = kms.Key( self, 'NotebookKmsKey', alias=notebook.NotebookInstanceName, enable_key_rotation=True, + admins=[ + iam.ArnPrincipal(cdk_exec_role), + ], policy=iam.PolicyDocument( assign_sids=True, statements=[ @@ -74,14 +80,30 @@ def __init__(self, scope, id: str, target_uri: str = None, **kwargs) -> None: resources=['*'], effect=iam.Effect.ALLOW, principals=[ - iam.AccountPrincipal(account_id=notebook.AWSAccountId), - iam.Role.from_role_arn( - self, - 'NotebookRole', - role_arn=notebook.RoleArn, - ), + iam.ArnPrincipal(notebook.RoleArn) + ], + actions=[ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:DescribeKey" + ], + conditions={ + "StringEquals": {"kms:ViaService": f"sagemaker.{notebook.region}.amazonaws.com"} + } + ), + iam.PolicyStatement( + resources=['*'], + effect=iam.Effect.ALLOW, + principals=[ + iam.ArnPrincipal(notebook.RoleArn) ], - actions=['kms:*'], + actions=[ + "kms:DescribeKey", + "kms:List*", + "kms:GetKeyPolicy", + ] ) ], ), @@ -116,7 +138,7 @@ def __init__(self, scope, id: str, target_uri: str = None, **kwargs) -> None: self, f'Notebook{target_uri}', instance_type=notebook.InstanceType, - role_arn=env_group.environmentIAMRoleArn, + role_arn=notebook.RoleArn, direct_internet_access='Disabled', subnet_id=notebook.SubnetId, security_group_ids=[security_group.security_group_id], diff --git a/backend/dataall/cdkproxy/stacks/pipeline.py b/backend/dataall/cdkproxy/stacks/pipeline.py index 995422283..f132809c2 100644 --- a/backend/dataall/cdkproxy/stacks/pipeline.py +++ b/backend/dataall/cdkproxy/stacks/pipeline.py @@ -141,24 +141,37 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): removal_policy=RemovalPolicy.DESTROY, alias=f"{pipeline.name}-codebuild-key", enable_key_rotation=True, + admins=[ + iam.ArnPrincipal(pipeline_environment.CDKRoleArn), + ], policy=iam.PolicyDocument( statements=[ iam.PolicyStatement( resources=["*"], effect=iam.Effect.ALLOW, principals=[ - iam.AccountPrincipal(account_id=self.account), + build_project_role + ], + actions=[ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", ], - actions=["kms:*"], ), iam.PolicyStatement( resources=["*"], effect=iam.Effect.ALLOW, principals=[ - iam.ServicePrincipal(service="codebuild.amazonaws.com"), + iam.ArnPrincipal(pipeline_env_team.environmentIAMRoleArn), + build_project_role ], - actions=["kms:GenerateDataKey*", "kms:Decrypt"], - ), + actions=[ + "kms:DescribeKey", + "kms:List*", + "kms:GetKeyPolicy", + ], + ) ], ), ) @@ -167,15 +180,14 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): code_dir_path = os.path.realpath( os.path.abspath( os.path.join( - __file__, "..", "..", "..", "..", "blueprints", "data_pipeline_blueprint" + __file__, "..", "..", "blueprints", "data_pipeline_blueprint" ) ) ) - + logger.info(f"code directory path = {code_dir_path}") + env_vars, aws = PipelineStack._set_env_vars(pipeline_environment) try: - env_vars, aws = PipelineStack._set_env_vars(pipeline_environment) - codecommit_client = aws.client('codecommit', region_name=pipeline_environment.region) - repository = PipelineStack._check_repository(codecommit_client, pipeline.repo) + repository = PipelineStack._check_repository(aws, pipeline_environment.region, pipeline.repo) if repository: PipelineStack.write_ddk_json_multienvironment(path=code_dir_path, output_file="ddk.json", pipeline_environment=pipeline_environment, development_environments=development_environments) @@ -197,7 +209,7 @@ def __init__(self, scope, id, target_uri: str = None, **kwargs): else: raise Exception except Exception as e: - PipelineStack.initialize_repo(pipeline, code_dir_path) + PipelineStack.initialize_repo(pipeline, code_dir_path, env_vars) PipelineStack.write_deploy_buildspec(path=code_dir_path, output_file=f"{pipeline.repo}/deploy_buildspec.yaml") @@ -454,9 +466,6 @@ def make_codebuild_policy_statements( iam.PolicyStatement( actions=[ "ec2:DescribeAvailabilityZones", - "kms:Decrypt", - "kms:Encrypt", - "kms:GenerateDataKey", "secretsmanager:GetSecretValue", "secretsmanager:DescribeSecret", "ssm:GetParametersByPath", @@ -510,7 +519,7 @@ def write_ddk_json_multienvironment(path, output_file, pipeline_environment, dev with open(f'{path}/{output_file}', 'w') as text_file: print(json, file=text_file) - def initialize_repo(pipeline, code_dir_path): + def initialize_repo(pipeline, code_dir_path, env_vars): venv_name = ".venv" @@ -529,7 +538,8 @@ def initialize_repo(pipeline, code_dir_path): text=True, shell=True, # nosec encoding='utf-8', - cwd=code_dir_path + cwd=code_dir_path, + env=env_vars ) if process.returncode == 0: logger.info("Successfully Initialized New CDK/DDK App") @@ -545,6 +555,7 @@ def _set_env_vars(pipeline_environment): 'AWS_DEFAULT_REGION': pipeline_environment.region, 'CURRENT_AWS_ACCOUNT': pipeline_environment.AwsAccountId, 'envname': os.environ.get('envname', 'local'), + 'COOKIECUTTER_CONFIG': "/dataall/cdkproxy/blueprints/cookiecutter_config.yaml", } if env_creds: env.update( @@ -557,7 +568,8 @@ def _set_env_vars(pipeline_environment): return env, aws @staticmethod - def _check_repository(codecommit_client, repo_name): + def _check_repository(aws, region, repo_name): + codecommit_client = aws.client('codecommit', region_name=region) repository = None logger.info(f"Checking Repository Exists: {repo_name}") try: diff --git a/backend/dataall/cdkproxy/stacks/pivot_role.py b/backend/dataall/cdkproxy/stacks/pivot_role.py index b4c340d31..c0b157f4a 100644 --- a/backend/dataall/cdkproxy/stacks/pivot_role.py +++ b/backend/dataall/cdkproxy/stacks/pivot_role.py @@ -34,8 +34,8 @@ def create_pivot_role(self, name: str, principal_id: str, external_id: str, env_ 'DataAllPivotRole-cdk', role_name=name, assumed_by=iam.CompositePrincipal( - iam.ServicePrincipal('glue.amazonaws.com'), iam.ServicePrincipal('lakeformation.amazonaws.com'), + iam.ServicePrincipal('glue.amazonaws.com'), iam.ServicePrincipal('lambda.amazonaws.com'), ), path='/', @@ -53,7 +53,14 @@ def create_pivot_role(self, name: str, principal_id: str, external_id: str, env_ effect=iam.Effect.ALLOW, principals=[iam.AccountPrincipal(account_id=principal_id)], actions=['sts:AssumeRole'], - conditions={'StringEquals': {'sts:ExternalId': external_id}}, + conditions={ + 'StringEquals': {'sts:ExternalId': external_id}, + 'StringLike': {"aws:PrincipalArn": [ + f"arn:aws:iam::{principal_id}:role/*graphql-role", + f"arn:aws:iam::{principal_id}:role/*awsworker-role", + f"arn:aws:iam::{principal_id}:role/*ecs-tasks-role" + ]} + }, ) ) @@ -72,54 +79,16 @@ def _create_dataall_policy0(self, env_resource_prefix: str) -> iam.ManagedPolicy 'PivotRolePolicy0', managed_policy_name=f'{env_resource_prefix}-pivotrole-cdk-policy-0', statements=[ - # Athena permissions - iam.PolicyStatement( - sid='Athena', - effect=iam.Effect.ALLOW, - resources=['*'], - actions=[ - 'athena:GetQuery*', - 'athena:StartQueryExecution', - 'athena:ListWorkGroups' - ], - ), - # Athena Workgroups permissions - iam.PolicyStatement( - sid='AthenaWorkgroups', - effect=iam.Effect.ALLOW, - actions=[ - 'athena:GetWorkGroup', - 'athena:CreateWorkGroup', - 'athena:UpdateWorkGroup', - 'athena:DeleteWorkGroup', - 'athena:TagResource', - 'athena:UntagResource', - 'athena:ListTagsForResource', - ], - resources=[f'arn:aws:athena:*:{self.account}:workgroup/{env_resource_prefix}*'], - ), - # AWS Glue Crawler Bucket - iam.PolicyStatement( - sid='AwsGlueCrawlerBucket', - effect=iam.Effect.ALLOW, - actions=['s3:GetObject'], - resources=['arn:aws:s3:::crawler-public*'], - ), - # S3 Access points + # Read Buckets iam.PolicyStatement( - sid='ManagedAccessPoints', + sid='ReadBuckets', effect=iam.Effect.ALLOW, actions=[ - 's3:GetAccessPoint', - 's3:GetAccessPointPolicy', - 's3:ListAccessPoints', - 's3:CreateAccessPoint', - 's3:DeleteAccessPoint', - 's3:GetAccessPointPolicyStatus', - 's3:DeleteAccessPointPolicy', - 's3:PutAccessPointPolicy', + 's3:ListAllMyBuckets', + 's3:GetBucketLocation', + 's3:PutBucketTagging' ], - resources=[f'arn:aws:s3:*:{self.account}:accesspoint/*'], + resources=['*'], ), # S3 Managed Buckets iam.PolicyStatement( @@ -133,7 +102,7 @@ def _create_dataall_policy0(self, env_resource_prefix: str) -> iam.ManagedPolicy ], resources=[f'arn:aws:s3:::{env_resource_prefix}*'], ), - # S3 Imported Buckets + # S3 Imported Buckets - restrict resources via bucket policies iam.PolicyStatement( sid='ImportedBuckets', effect=iam.Effect.ALLOW, @@ -150,155 +119,104 @@ def _create_dataall_policy0(self, env_resource_prefix: str) -> iam.ManagedPolicy ], resources=['arn:aws:s3:::*'], ), - # AWS Logging Buckets - iam.PolicyStatement( - sid='AWSLoggingBuckets', - effect=iam.Effect.ALLOW, - actions=[ - 's3:PutBucketAcl', - 's3:PutBucketNotification' - ], - resources=[f'arn:aws:s3:::{env_resource_prefix}-logging-*'], - ), - # Read Buckets + # KMS - needed for imported buckets iam.PolicyStatement( - sid='ReadBuckets', + sid='KMS', effect=iam.Effect.ALLOW, actions=[ - 's3:ListAllMyBuckets', - 's3:GetBucketLocation', - 's3:PutBucketTagging' + 'kms:Decrypt', + 'kms:Encrypt', + 'kms:GenerateDataKey*', + 'kms:PutKeyPolicy', + 'kms:ReEncrypt*', + 'kms:TagResource', + 'kms:UntagResource', ], resources=['*'], ), - # CloudWatch Metrics iam.PolicyStatement( - sid='CWMetrics', + sid='KMSList', effect=iam.Effect.ALLOW, actions=[ - 'cloudwatch:PutMetricData', - 'cloudwatch:GetMetricData', - 'cloudwatch:GetMetricStatistics' + 'kms:List*', + 'kms:DescribeKey', ], resources=['*'], ), - # Logs + # Athena - needed for Worksheets feature iam.PolicyStatement( - sid='Logs', + sid='AthenaWorkgroups', effect=iam.Effect.ALLOW, actions=[ - 'logs:CreateLogGroup', - 'logs:CreateLogStream', - 'logs:PutLogEvents' - ], - resources=[ - f'arn:aws:logs:*:{self.account}:log-group:/aws-glue/*', - f'arn:aws:logs:*:{self.account}:log-group:/aws/lambda/*', - f'arn:aws:logs:*:{self.account}:log-group:/{env_resource_prefix}*', + "athena:GetQueryExecution", + "athena:GetQueryResults", + "athena:GetWorkGroup", + "athena:StartQueryExecution" ], + resources=[f'arn:aws:athena:*:{self.account}:workgroup/{env_resource_prefix}*'], ), - # Logging - iam.PolicyStatement( - sid='Logging', effect=iam.Effect.ALLOW, actions=['logs:PutLogEvents'], resources=['*'] - ), - # EventBridge (CloudWatch Events) + # S3 Access points - needed for access points sharing iam.PolicyStatement( - sid='CWEvents', + sid='ManagedAccessPoints', effect=iam.Effect.ALLOW, actions=[ - 'events:DeleteRule', - 'events:List*', - 'events:PutRule', - 'events:PutTargets', - 'events:RemoveTargets', + 's3:GetAccessPoint', + 's3:GetAccessPointPolicy', + 's3:ListAccessPoints', + 's3:CreateAccessPoint', + 's3:DeleteAccessPoint', + 's3:GetAccessPointPolicyStatus', + 's3:DeleteAccessPointPolicy', + 's3:PutAccessPointPolicy', ], - resources=['*'], + resources=[f'arn:aws:s3:*:{self.account}:accesspoint/*'], ), - # Glue + # Glue - needed to handle databases and tables and cross-account shares iam.PolicyStatement( - sid='Glue', + sid='GlueCatalog', effect=iam.Effect.ALLOW, actions=[ 'glue:BatchCreatePartition', 'glue:BatchDeletePartition', 'glue:BatchDeleteTable', - 'glue:CreateCrawler', 'glue:CreateDatabase', 'glue:CreatePartition', 'glue:CreateTable', - 'glue:DeleteCrawler', 'glue:DeleteDatabase', - 'glue:DeleteJob', 'glue:DeletePartition', 'glue:DeleteTable', - 'glue:DeleteTrigger', 'glue:BatchGet*', 'glue:Get*', 'glue:List*', - 'glue:StartCrawler', - 'glue:StartJobRun', - 'glue:StartTrigger', 'glue:SearchTables', 'glue:UpdateDatabase', 'glue:UpdatePartition', 'glue:UpdateTable', - 'glue:UpdateTrigger', - 'glue:UpdateJob', 'glue:TagResource', - 'glue:UpdateCrawler', - ], - resources=['*'], - ), - # KMS - iam.PolicyStatement( - sid='KMS', - effect=iam.Effect.ALLOW, - actions=[ - 'kms:Decrypt', - 'kms:Encrypt', - 'kms:GenerateDataKey*', - 'kms:PutKeyPolicy', - 'kms:ReEncrypt*', - 'kms:TagResource', - 'kms:UntagResource', + 'glue:DeleteResourcePolicy', + 'glue:PutResourcePolicy', ], resources=['*'], ), + # Glue ETL - needed to start crawler and profiling jobs iam.PolicyStatement( - sid='KMSAlias', - effect=iam.Effect.ALLOW, - actions=['kms:DeleteAlias'], - resources=[f'arn:aws:kms:*:{self.account}:alias/{env_resource_prefix}*'], - ), - iam.PolicyStatement( - sid='KMSCreate', + sid='GlueETL', effect=iam.Effect.ALLOW, actions=[ - 'kms:List*', - 'kms:DescribeKey', - 'kms:CreateAlias', - 'kms:CreateKey' + 'glue:StartCrawler', + 'glue:StartJobRun', + 'glue:StartTrigger', + 'glue:UpdateTrigger', + 'glue:UpdateJob', + 'glue:UpdateCrawler', ], - resources=['*'], - ), - # AWS Organizations - iam.PolicyStatement( - sid='Organizations', - effect=iam.Effect.ALLOW, - actions=['organizations:DescribeOrganization'], - resources=['*'], - ), - # Resource Tags - iam.PolicyStatement( - sid='ResourceGroupTags', - effect=iam.Effect.ALLOW, - actions=[ - 'tag:*', - 'resource-groups:*' + resources=[ + f'arn:aws:glue:*:{self.account}:crawler/{env_resource_prefix}*', + f'arn:aws:glue:*:{self.account}:job/{env_resource_prefix}*', + f'arn:aws:glue:*:{self.account}:trigger/{env_resource_prefix}*', ], - resources=['*'], ), - # SNS + # SNS - For subscriptions iam.PolicyStatement( sid='SNSPublish', effect=iam.Effect.ALLOW, @@ -317,7 +235,7 @@ def _create_dataall_policy0(self, env_resource_prefix: str) -> iam.ManagedPolicy iam.PolicyStatement( sid='SNSList', effect=iam.Effect.ALLOW, actions=['sns:ListTopics'], resources=['*'] ), - # SQS + # SQS - support SQS queues iam.PolicyStatement( sid='SQSList', effect=iam.Effect.ALLOW, actions=['sqs:ListQueues'], resources=['*'] ), @@ -330,104 +248,73 @@ def _create_dataall_policy0(self, env_resource_prefix: str) -> iam.ManagedPolicy ], resources=[f'arn:aws:sqs:*:{self.account}:{env_resource_prefix}*'], ), - ], - ) - - def _create_dataall_policy1(self, env_resource_prefix: str) -> iam.ManagedPolicy: - """ - Creates the second managed IAM Policy required for the Pivot Role used by data.all - - :param str env_resource_prefix: Environment Resource Prefix provided by data.all - :returns: Created IAM Policy - :rtype: iam.ManagedPolicy - """ - return iam.ManagedPolicy( - self, - 'PivotRolePolicy1', - managed_policy_name=f'{env_resource_prefix}-pivotrole-cdk-policy-1', - statements=[ - # Redshift + # AWS Logging Buckets iam.PolicyStatement( - sid='Redshift', + sid='AWSLoggingBuckets', effect=iam.Effect.ALLOW, actions=[ - 'redshift:DeleteTags', - 'redshift:ModifyClusterIamRoles', - 'redshift:DescribeClusterSecurityGroups', - 'redshift:DescribeClusterSubnetGroups', - 'redshift:pauseCluster', - 'redshift:resumeCluster', + 's3:PutBucketAcl', + 's3:PutBucketNotification' ], - resources=['*'], - conditions={'StringEquals': {'aws:ResourceTag/dataall': 'true'}}, + resources=[f'arn:aws:s3:::{env_resource_prefix}-logging-*'], ), + # CloudWatch Metrics iam.PolicyStatement( - sid='RedshiftRead', + sid='CWMetrics', effect=iam.Effect.ALLOW, actions=[ - 'redshift:DescribeClusters', - 'redshift:CreateTags', - 'redshift:DescribeClusterSubnetGroups', + 'cloudwatch:PutMetricData', + 'cloudwatch:GetMetricData', + 'cloudwatch:GetMetricStatistics' ], resources=['*'], ), + # Logs iam.PolicyStatement( - sid='RedshiftCreds', + sid='Logs', effect=iam.Effect.ALLOW, - actions=['redshift:GetClusterCredentials'], + actions=[ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + ], resources=[ - f'arn:aws:redshift:*:{self.account}:dbgroup:*/*', - f'arn:aws:redshift:*:{self.account}:dbname:*/*', - f'arn:aws:redshift:*:{self.account}:dbuser:*/*', + f'arn:aws:logs:*:{self.account}:log-group:/aws/lambda/*', + f'arn:aws:logs:*:{self.account}:log-group:/{env_resource_prefix}*', ], ), + # Logging iam.PolicyStatement( - sid='AllowRedshiftSubnet', - effect=iam.Effect.ALLOW, - actions=['redshift:CreateClusterSubnetGroup'], - resources=['*'], - ), - iam.PolicyStatement( - sid='AllowRedshiftDataApi', - effect=iam.Effect.ALLOW, - actions=[ - 'redshift-data:ListTables', - 'redshift-data:GetStatementResult', - 'redshift-data:CancelStatement', - 'redshift-data:ListSchemas', - 'redshift-data:ExecuteStatement', - 'redshift-data:ListStatements', - 'redshift-data:ListDatabases', - 'redshift-data:DescribeStatement', - ], - resources=['*'], + sid='Logging', effect=iam.Effect.ALLOW, actions=['logs:PutLogEvents'], resources=['*'] ), - # EC2 + ], + ) + + def _create_dataall_policy1(self, env_resource_prefix: str) -> iam.ManagedPolicy: + """ + Creates the second managed IAM Policy required for the Pivot Role used by data.all + + :param str env_resource_prefix: Environment Resource Prefix provided by data.all + :returns: Created IAM Policy + :rtype: iam.ManagedPolicy + """ + return iam.ManagedPolicy( + self, + 'PivotRolePolicy1', + managed_policy_name=f'{env_resource_prefix}-pivotrole-cdk-policy-1', + statements=[ + # EC2 describe needed for SageMaker iam.PolicyStatement( sid='EC2SG', effect=iam.Effect.ALLOW, actions=[ - 'ec2:CreateSecurityGroup', - 'ec2:CreateNetworkInterface', - 'ec2:Describe*' + 'ec2:DescribeSubnets', + 'ec2:DescribeSecurityGroups', + 'ec2:DescribeVpcs', + 'ec2:DescribeInstances', + 'ec2:DescribeNetworkInterfaces', ], resources=['*'], ), - iam.PolicyStatement( - sid='TagsforENI', - effect=iam.Effect.ALLOW, - actions=[ - 'ec2:CreateTags', - 'ec2:DeleteTags' - ], - resources=[f'arn:aws:ec2:*:{self.account}:network-interface/*'], - ), - iam.PolicyStatement( - sid='DeleteENI', - effect=iam.Effect.ALLOW, - actions=['ec2:DeleteNetworkInterface'], - resources=[f'arn:aws:ec2:*:{self.account}:network-interface/*'], - ), # SageMaker iam.PolicyStatement( sid='SageMakerNotebookActions', @@ -435,7 +322,6 @@ def _create_dataall_policy1(self, env_resource_prefix: str) -> iam.ManagedPolicy actions=[ 'sagemaker:ListTags', 'sagemaker:DescribeUserProfile', - 'sagemaker:DeleteNotebookInstance', 'sagemaker:StopNotebookInstance', 'sagemaker:CreatePresignedNotebookInstanceUrl', 'sagemaker:DescribeNotebookInstance', @@ -486,7 +372,6 @@ def _create_dataall_policy1(self, env_resource_prefix: str) -> iam.ManagedPolicy actions=['ram:UpdateResourceShare'], resources=[f'arn:aws:ram:*:{self.account}:resource-share/*'], conditions={ - 'StringEquals': {'aws:ResourceTag/dataall': 'true'}, 'ForAllValues:StringLike': {'ram:ResourceShareName': ['LakeFormation*']}, }, ), @@ -504,7 +389,7 @@ def _create_dataall_policy1(self, env_resource_prefix: str) -> iam.ManagedPolicy sid='RamDeleteResource', effect=iam.Effect.ALLOW, actions=['ram:DeleteResourceShare'], - resources=[f'arn:aws:ram:*:{self.account}:resource-share/*'], + resources=[f'arn:aws:ram:*:{self.account}:resource-share/*'] ), iam.PolicyStatement( sid='RamInvitations', @@ -512,105 +397,44 @@ def _create_dataall_policy1(self, env_resource_prefix: str) -> iam.ManagedPolicy actions=[ 'ram:AcceptResourceShareInvitation', 'ram:RejectResourceShareInvitation', - 'ec2:DescribeAvailabilityZones', 'ram:EnableSharingWithAwsOrganization', ], resources=['*'], ), iam.PolicyStatement( - sid='RamReadGlue', + sid='RamRead', effect=iam.Effect.ALLOW, actions=[ - 'glue:PutResourcePolicy', - 'glue:DeleteResourcePolicy', 'ram:Get*', 'ram:List*' ], resources=['*'], ), - # Security Groups - iam.PolicyStatement( - sid='SGCreateTag', - effect=iam.Effect.ALLOW, - actions=['ec2:CreateTags'], - resources=[f'arn:aws:ec2:*:{self.account}:security-group/*'], - conditions={'StringEquals': {'aws:RequestTag/dataall': 'true'}}, - ), + # CloudFormation iam.PolicyStatement( - sid='SGandRedshift', - effect=iam.Effect.ALLOW, - actions=[ - 'ec2:DeleteTags', - 'ec2:DeleteSecurityGroup', - 'redshift:DeleteClusterSubnetGroup' - ], - resources=['*'], - conditions={'ForAnyValue:StringEqualsIfExists': {'aws:ResourceTag/dataall': 'true'}}, - ), - # Redshift - iam.PolicyStatement( - sid='RedshiftDataApi', - effect=iam.Effect.ALLOW, - actions=[ - 'redshift-data:ListTables', - 'redshift-data:GetStatementResult', - 'redshift-data:CancelStatement', - 'redshift-data:ListSchemas', - 'redshift-data:ExecuteStatement', - 'redshift-data:ListStatements', - 'redshift-data:ListDatabases', - 'redshift-data:DescribeStatement', - ], - resources=['*'], - conditions={'StringEqualsIfExists': {'aws:ResourceTag/dataall': 'true'}}, - ), - # Dev Tools - iam.PolicyStatement( - sid='DevTools0', - effect=iam.Effect.ALLOW, - actions=['cloudformation:ValidateTemplate'], - resources=['*'], - ), - iam.PolicyStatement( - sid='DevTools1', + sid='CloudFormation', effect=iam.Effect.ALLOW, actions=[ - 'secretsmanager:CreateSecret', - 'secretsmanager:DeleteSecret', - 'secretsmanager:TagResource', - 'codebuild:DeleteProject', + "cloudformation:DeleteStack", + "cloudformation:DescribeStacks", + "cloudformation:DescribeStackEvents", + "cloudformation:DescribeStackResources" ], - resources=['*'], - conditions={'StringEquals': {'aws:ResourceTag/dataall': 'true'}}, - ), - iam.PolicyStatement( - sid='DevTools2', - effect=iam.Effect.ALLOW, - actions=[ - 'codebuild:CreateProject', - 'ecr:CreateRepository', - 'ssm:PutParameter', - 'ssm:AddTagsToResource', + resources=[ + f'arn:aws:cloudformation:*:{self.account}:stack/{env_resource_prefix}*/*', + f'arn:aws:cloudformation:*:{self.account}:stack/CDKToolkit/*', ], - resources=['*'], - conditions={'StringEquals': {'aws:RequestTag/dataall': 'true'}}, ), iam.PolicyStatement( - sid='CloudFormation', + sid='CloudFormationDataPipeliens', effect=iam.Effect.ALLOW, actions=[ - 'cloudformation:DescribeStacks', - 'cloudformation:DescribeStackResources', - 'cloudformation:DescribeStackEvents', - 'cloudformation:DeleteStack', - 'cloudformation:CreateStack', - 'cloudformation:GetTemplate', - 'cloudformation:ListStackResources', - 'cloudformation:DescribeStackResource', + "cloudformation:DeleteStack", + "cloudformation:DescribeStacks", + "cloudformation:DescribeStackEvents", + "cloudformation:DescribeStackResources" ], resources=[ - f'arn:aws:cloudformation:*:{self.account}:stack/{env_resource_prefix}*/*', - f'arn:aws:cloudformation:*:{self.account}:stack/CDKToolkit/*', f'arn:aws:cloudformation:*:{self.account}:stack/*/*', ], ), @@ -635,8 +459,6 @@ def _create_dataall_policy2(self, env_resource_prefix: str) -> iam.ManagedPolicy sid='LakeFormation', effect=iam.Effect.ALLOW, actions=[ - 'lakeformation:RegisterResource', - 'lakeformation:DeregisterResource', 'lakeformation:UpdateResource', 'lakeformation:DescribeResource', 'lakeformation:AddLFTagsToResource', @@ -663,70 +485,12 @@ def _create_dataall_policy2(self, env_resource_prefix: str) -> iam.ManagedPolicy 'lakeformation:GetWorkUnitResults', 'lakeformation:GetQueryState', 'lakeformation:GetQueryStatistics', - 'lakeformation:StartTransaction', - 'lakeformation:CommitTransaction', - 'lakeformation:CancelTransaction', - 'lakeformation:ExtendTransaction', - 'lakeformation:DescribeTransaction', - 'lakeformation:ListTransactions', 'lakeformation:GetTableObjects', 'lakeformation:UpdateTableObjects', 'lakeformation:DeleteObjectsOnCancel', ], resources=['*'], ), - # Compute - iam.PolicyStatement( - sid='Compute', - effect=iam.Effect.ALLOW, - actions=[ - 'lambda:CreateFunction', - 'lambda:AddPermission', - 'lambda:InvokeFunction', - 'lambda:RemovePermission', - 'lambda:GetFunction', - 'lambda:GetFunctionConfiguration', - 'lambda:DeleteFunction', - 'ecr:CreateRepository', - 'ecr:SetRepositoryPolicy', - 'ecr:DeleteRepository', - 'ecr:DescribeImages', - 'ecr:BatchDeleteImage', - 'codepipeline:GetPipelineState', - 'codepipeline:DeletePipeline', - 'codepipeline:GetPipeline', - 'codepipeline:CreatePipeline', - 'codepipeline:TagResource', - 'codepipeline:UntagResource', - ], - resources=[ - f'arn:aws:lambda:*:{self.account}:function:{env_resource_prefix}*', - f'arn:aws:s3:::{env_resource_prefix}*', - f'arn:aws:codepipeline:*:{self.account}:{env_resource_prefix}*', - f'arn:aws:ecr:*:{self.account}:repository/{env_resource_prefix}*', - ], - ), - # Databrew - iam.PolicyStatement( - sid='DatabrewList', effect=iam.Effect.ALLOW, actions=['databrew:List*'], resources=['*'] - ), - iam.PolicyStatement( - sid='DatabrewPermissions', - effect=iam.Effect.ALLOW, - actions=[ - 'databrew:BatchDeleteRecipeVersion', - 'databrew:Delete*', - 'databrew:Describe*', - 'databrew:PublishRecipe', - 'databrew:SendProjectSessionAction', - 'databrew:Start*', - 'databrew:Stop*', - 'databrew:TagResource', - 'databrew:UntagResource', - 'databrew:Update*', - ], - resources=[f'arn:aws:databrew:*:{self.account}:*/{env_resource_prefix}*'], - ), # QuickSight iam.PolicyStatement( sid='QuickSight', @@ -795,26 +559,7 @@ def _create_dataall_policy3(self, env_resource_prefix: str, role_name: str) -> i f'arn:aws:ssm:*:{self.account}:parameter/ddk/*', ], ), - # Secrets Manager - iam.PolicyStatement( - sid='SecretsManager', - effect=iam.Effect.ALLOW, - actions=[ - 'secretsmanager:DescribeSecret', - 'secretsmanager:GetSecretValue' - ], - resources=[ - f'arn:aws:secretsmanager:*:{self.account}:secret:{env_resource_prefix}*', - f'arn:aws:secretsmanager:*:{self.account}:secret:dataall*', - ], - ), - iam.PolicyStatement( - sid='SecretsManagerList', - effect=iam.Effect.ALLOW, - actions=['secretsmanager:ListSecrets'], - resources=['*'], - ), - # IAM + # IAM - needed for consumption roles and for S3 sharing iam.PolicyStatement( sid='IAMListGet', effect=iam.Effect.ALLOW, @@ -833,15 +578,30 @@ def _create_dataall_policy3(self, env_resource_prefix: str, role_name: str) -> i resources=['*'], ), iam.PolicyStatement( - sid='IAMPassRole', - effect=iam.Effect.ALLOW, - actions=['iam:PassRole'], + sid="PassRole", + actions=[ + 'iam:PassRole', + ], resources=[ - f'arn:aws:iam::{self.account}:role/{env_resource_prefix}*', f'arn:aws:iam::{self.account}:role/{role_name}', - f'arn:aws:iam::{self.account}:role/cdk-*', ], ), + iam.PolicyStatement( + sid="PassRoleGlue", + actions=[ + 'iam:PassRole', + ], + resources=[ + f'arn:aws:iam::{self.account}:role/{env_resource_prefix}*', + ], + conditions={ + "StringEquals": { + "iam:PassedToService": [ + "glue.amazonaws.com", + ] + } + } + ), # STS iam.PolicyStatement( sid='STS', @@ -852,18 +612,7 @@ def _create_dataall_policy3(self, env_resource_prefix: str, role_name: str) -> i f'arn:aws:iam::{self.account}:role/ddk-*', ], ), - # Step Functions - iam.PolicyStatement( - sid='StepFunctions', - effect=iam.Effect.ALLOW, - actions=[ - 'states:DescribeStateMachine', - 'states:ListExecutions', - 'states:StartExecution' - ], - resources=[f'arn:aws:states:*:{self.account}:stateMachine:{env_resource_prefix}*'], - ), - # CodeCommit + # CodeCommit - used in Pipelines iam.PolicyStatement( sid='CodeCommit', effect=iam.Effect.ALLOW, diff --git a/backend/dataall/cdkproxy/stacks/policies/_lambda.py b/backend/dataall/cdkproxy/stacks/policies/_lambda.py index 2aa76257b..8da645153 100644 --- a/backend/dataall/cdkproxy/stacks/policies/_lambda.py +++ b/backend/dataall/cdkproxy/stacks/policies/_lambda.py @@ -3,32 +3,96 @@ class Lambda(ServicePolicy): + """ + Class including all permissions needed to work with AWS Lambda. + It allows data.all users to: + - List Lambda resources + - Create and manage team Lambda resources + - Log Lambda executions + """ def get_statements(self): statements = [ iam.PolicyStatement( + # sid="ListLambda", actions=[ - 'lambda:ListFunctions', - 'lambda:ListEventSourceMappings', - 'lambda:ListLayerVersions', - 'lambda:ListLayers', + 'lambda:List*', + 'lambda:GetLayer*', 'lambda:GetAccountSettings', + 'lambda:GetEventSourceMapping', 'lambda:CreateEventSourceMapping', - 'lambda:ListCodeSigningConfigs', + 'lambda:CreateCodeSigningConfig', ], resources=['*'], ), iam.PolicyStatement( + # sid="GenericLambdaFunctions", actions=[ - 'lambda:*', + 'lambda:UpdateFunctionCodeSigningConfig', + 'lambda:UpdateEventSourceMapping', ], resources=[ + f'arn:aws:lambda:{self.region}:{self.account}:function:{self.resource_prefix}*', + f'arn:aws:lambda:{self.region}:{self.account}:function:{self.resource_prefix}*:*', f'arn:aws:lambda:{self.region}:{self.account}:code-signing-config:*', f'arn:aws:lambda:{self.region}:{self.account}:event-source-mapping:*', + ], + ), + iam.PolicyStatement( + # sid="CreateTeamLambda", + actions=[ + 'lambda:CreateFunction', + 'lambda:TagResource', + ], + resources=[ f'arn:aws:lambda:{self.region}:{self.account}:function:{self.resource_prefix}*', f'arn:aws:lambda:{self.region}:{self.account}:function:{self.resource_prefix}*:*', + ], + conditions={ + 'StringEquals': { + f'aws:RequestTag/{self.tag_key}': [self.tag_value] + } + }, + ), + iam.PolicyStatement( + # sid="ManageTeamLambda", + not_actions=[ + 'lambda:CreateFunction', + 'lambda:TagResource', + 'lambda:UntagResource', + ], + resources=[ + f'arn:aws:lambda:{self.region}:{self.account}:function:{self.resource_prefix}*', + f'arn:aws:lambda:{self.region}:{self.account}:function:{self.resource_prefix}*:*' + ], + conditions={ + 'StringEquals': { + f'aws:ResourceTag/{self.tag_key}': [self.tag_value] + } + }, + ), + iam.PolicyStatement( + # sid="ManageLambdaLayers", + actions=[ + 'lambda:PublishLayerVersion', + 'lambda:DeleteLayerVersion', + ], + resources=[ f'arn:aws:lambda:{self.region}:{self.account}:layer:{self.resource_prefix}*', f'arn:aws:lambda:{self.region}:{self.account}:layer:{self.resource_prefix}*:*', - ], + ] ), + iam.PolicyStatement( + # sid="LoggingLambda", + actions=[ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents', + ], + effect=iam.Effect.ALLOW, + resources=[ + f'arn:aws:logs:{self.region}:{self.account}:log-group:/aws/lambda/*', + f'arn:aws:logs:{self.region}:{self.account}:log-group:/aws/lambda/*:log-stream:*', + ], + ) ] return statements diff --git a/backend/dataall/cdkproxy/stacks/policies/athena.py b/backend/dataall/cdkproxy/stacks/policies/athena.py new file mode 100644 index 000000000..938029262 --- /dev/null +++ b/backend/dataall/cdkproxy/stacks/policies/athena.py @@ -0,0 +1,64 @@ +from .service_policy import ServicePolicy +from aws_cdk import aws_iam as iam + + +class Athena(ServicePolicy): + """ + Class including all permissions needed to work with Amazon Athena. + It allows data.all users to: + - Work with team workgroup + - Store query results in environment S3 Bucket location for the team workgroup (access to other S3 locations is restricted) + """ + def get_statements(self): + statements = [ + iam.PolicyStatement( + # sid="ListAthena", + actions=[ + "athena:ListWorkGroups", + "athena:ListTagsForResource", + "athena:GetWorkgroup" + ], + effect=iam.Effect.ALLOW, + resources=['*'], + ), + iam.PolicyStatement( + # sid="AthenaWorkgroup", + actions=[ + "athena:Get*", + "athena:BatchGet*", + "athena:List*", + "athena:StartQueryExecution", + "athena:StopQueryExecution", + "athena:CreateNamedQuery", + "athena:DeleteNamedQuery", + "athena:CreatePreparedStatement", + "athena:UpdatePreparedStatement", + "athena:DeletePreparedStatement" + ], + resources=[f'arn:aws:athena:{self.region}:{self.account}:workgroup/{self.team.environmentAthenaWorkGroup}'], + ), + iam.PolicyStatement( + # sid="ListBucketAthena", + actions=[ + "s3:ListBucket", + ], + effect=iam.Effect.ALLOW, + resources=[f'arn:aws:s3:::{self.environment.EnvironmentDefaultBucketName}'], + conditions={"StringEquals": {"s3:prefix": ["", "athenaqueries/", f"athenaqueries/{self.team.environmentIAMRoleName}/"], "s3:delimiter": ["/"]}} + ), + iam.PolicyStatement( + # sid="ReadWriteEnvironmentBucketAthenaQueries", + actions=[ + "s3:PutObject", + "s3:PutObjectAcl", + "s3:GetObject", + "s3:GetObjectAcl", + "s3:GetObjectVersion", + "s3:DeleteObject" + ], + resources=[ + f'arn:aws:s3:::{self.environment.EnvironmentDefaultBucketName}/athenaqueries/{self.team.environmentIAMRoleName}/*'], + effect=iam.Effect.ALLOW, + ), + ] + return statements diff --git a/backend/dataall/cdkproxy/stacks/policies/codestar.py b/backend/dataall/cdkproxy/stacks/policies/aws_cicd.py similarity index 53% rename from backend/dataall/cdkproxy/stacks/policies/codestar.py rename to backend/dataall/cdkproxy/stacks/policies/aws_cicd.py index 021409d61..1f6f5dae0 100644 --- a/backend/dataall/cdkproxy/stacks/policies/codestar.py +++ b/backend/dataall/cdkproxy/stacks/policies/aws_cicd.py @@ -2,26 +2,52 @@ from aws_cdk import aws_iam as iam -class CodeStar(ServicePolicy): +class AwsCICD(ServicePolicy): + """ + Class including all permissions needed to work with AWS CICD services: CodeCommit, CodePipeline and CodeBuild. + It allows data.all users to: + - Create and manage CodeBuild, CodeCommit and CodePipeline resources for the team + - Create an S3 Bucket for codepipeline prefixed by "codepipeline-" + - Read/Write to and from S3 Buckets prefixed by "codepipeline-" + """ def get_statements(self): statements = [ iam.PolicyStatement( + # sid="GenericCodeCommit", actions=[ - 'codecommit:ListRepositoriesForApprovalRuleTemplate', + 'codecommit:List*', 'codecommit:CreateApprovalRuleTemplate', 'codecommit:UpdateApprovalRuleTemplateName', 'codecommit:GetApprovalRuleTemplate', - 'codecommit:ListApprovalRuleTemplates', 'codecommit:DeleteApprovalRuleTemplate', - 'codecommit:ListRepositories', 'codecommit:UpdateApprovalRuleTemplateContent', 'codecommit:UpdateApprovalRuleTemplateDescription', ], resources=['*'], ), iam.PolicyStatement( + # sid="TagCICD", actions=[ - 'codecommit:*', + "codecommit:TagResource", + "codepipeline:TagResource" + ], + resources=[ + f'arn:aws:codecommit:{self.region}:{self.account}:{self.resource_prefix}*', + f'arn:aws:codepipeline:{self.region}:{self.account}:{self.resource_prefix}*', + f'arn:aws:codepipeline:{self.region}:{self.account}:actiontype:/*/*/*', + f'arn:aws:codepipeline:{self.region}:{self.account}:webhook:{self.resource_prefix}', + ], + conditions={ + 'StringEquals': { + f'aws:RequestTag/{self.tag_key}': [self.tag_value], + }, + }, + ), + iam.PolicyStatement( + # sid="AllCodecommitTeamRepo", + not_actions=[ + "codecommit:TagResource", + "codecommit:UntagResource", ], resources=[ f'arn:aws:codecommit:{self.region}:{self.account}:{self.resource_prefix}*' @@ -33,24 +59,29 @@ def get_statements(self): }, ), iam.PolicyStatement( + # sid="GenericCodePipeline", actions=[ - 'codepipeline:PutThirdPartyJobSuccessResult', - 'codepipeline:PutThirdPartyJobFailureResult', - 'codepipeline:PollForThirdPartyJobs', - 'codepipeline:PutJobFailureResult', - 'codepipeline:PutJobSuccessResult', - 'codepipeline:ListPipelines', 'codepipeline:AcknowledgeJob', 'codepipeline:AcknowledgeThirdPartyJob', 'codepipeline:GetThirdPartyJobDetails', 'codepipeline:GetJobDetails', 'codepipeline:GetActionType', 'codepipeline:ListActionTypes', + 'codepipeline:ListPipelines', + 'codepipeline:PollForThirdPartyJobs', + 'codepipeline:PutThirdPartyJobSuccessResult', + 'codepipeline:PutThirdPartyJobFailureResult', + 'codepipeline:PutJobFailureResult', + 'codepipeline:PutJobSuccessResult', ], resources=['*'], ), iam.PolicyStatement( - actions=['codepipeline:*'], + # sid="AllCodepipelineTeamRepo", + not_actions=[ + "codepipeline:TagResource", + "codepipeline:UntagResource", + ], resources=[ f'arn:aws:codepipeline:{self.region}:{self.account}:{self.resource_prefix}*/*/*', f'arn:aws:codepipeline:{self.region}:{self.account}:actiontype:/*/*/*', @@ -65,18 +96,23 @@ def get_statements(self): }, ), iam.PolicyStatement( - actions=['codebuild:*'], + # sid="CodePipelineCreateS3Bucket", + effect=iam.Effect.ALLOW, + actions=[ + 's3:CreateBucket', + 's3:ListBucket', + 's3:PutBucketPublicAccessBlock', + 's3:GetObject', + 's3:PutObject', + 's3:DeleteObject' + ], resources=[ - f'arn:aws:codebuild:{self.region}:{self.account}:project/{self.resource_prefix}*', - f'arn:aws:codebuild:{self.region}:{self.account}:report-group/{self.resource_prefix}*', + f"arn:aws:s3:::codepipeline-{self.region}-{self.account}", + f"arn:aws:s3:::codepipeline-{self.region}-{self.account}/{self.resource_prefix}*" ], - conditions={ - 'StringEquals': { - f'aws:ResourceTag/{self.tag_key}': [self.tag_value] - } - }, ), iam.PolicyStatement( + # sid="GenericCodeBuild", actions=[ 'codebuild:ListCuratedEnvironmentImages', 'codebuild:ListReportGroups', @@ -96,5 +132,43 @@ def get_statements(self): ], resources=['*'], ), + iam.PolicyStatement( + # sid="TagCodebuildTeamRepo", + actions=[ + 'codebuild:CreateProject', + 'codebuild:UpdateProject', + 'codebuild:UpdateProjectVisibility', + 'codebuild:CreateReportGroup', + 'codebuild:UpdateReportGroup', + ], + resources=[ + f'arn:aws:codebuild:{self.region}:{self.account}:project/{self.resource_prefix}*', + f'arn:aws:codebuild:{self.region}:{self.account}:report-group/{self.resource_prefix}*', + ], + conditions={ + 'StringEquals': { + f'aws:RequestTag/{self.tag_key}': [self.tag_value] + } + }, + ), + iam.PolicyStatement( + # sid="AllCodebuildTeamRepo", + not_actions=[ + 'codebuild:CreateProject', + 'codebuild:UpdateProject', + 'codebuild:UpdateProjectVisibility', + 'codebuild:CreateReportGroup', + 'codebuild:UpdateReportGroup', + ], + resources=[ + f'arn:aws:codebuild:{self.region}:{self.account}:project/{self.resource_prefix}*', + f'arn:aws:codebuild:{self.region}:{self.account}:report-group/{self.resource_prefix}*', + ], + conditions={ + 'StringEquals': { + f'aws:ResourceTag/{self.tag_key}': [self.tag_value] + } + }, + ) ] return statements diff --git a/backend/dataall/cdkproxy/stacks/policies/cloudformation.py b/backend/dataall/cdkproxy/stacks/policies/cloudformation.py index 12eb8297a..468efb531 100644 --- a/backend/dataall/cdkproxy/stacks/policies/cloudformation.py +++ b/backend/dataall/cdkproxy/stacks/policies/cloudformation.py @@ -3,9 +3,17 @@ class Cloudformation(ServicePolicy): + """ + Class including all permissions needed to work with AWS CloudFormation. + It allows data.all users to: + - Create/Delete CloudFormation team stacks + - Create an S3 Bucket for codepipeline prefixed by "cf-templates-" + - Read/Write to and from S3 Buckets prefixed by "cf-templates-" + """ def get_statements(self): statements = [ iam.PolicyStatement( + # sid="GenericCloudFormation", actions=[ 'cloudformation:EstimateTemplateCost', 'cloudformation:ListStacks', @@ -23,23 +31,13 @@ def get_statements(self): 'cloudformation:Get*', 'cloudformation:Describe*', 'cloudformation:List*', + 'cloudformation:CreateUploadBucket', ], resources=['*'], ), iam.PolicyStatement( + # sid="DeleteTeamCloudFormation", actions=[ - 'cloudformation:CreateStack', - ], - resources=[ - f'arn:aws:cloudformation:{self.region}:{self.account}:*/{self.resource_prefix}*' - ], - conditions={ - 'StringEquals': {f'aws:RequestTag/{self.tag_key}': [self.tag_value]} - }, - ), - iam.PolicyStatement( - actions=[ - 'cloudformation:UpdateStack', 'cloudformation:DeleteStack', ], resources=[ diff --git a/backend/dataall/cdkproxy/stacks/policies/data_policy.py b/backend/dataall/cdkproxy/stacks/policies/data_policy.py index be926b4ce..3508c8106 100644 --- a/backend/dataall/cdkproxy/stacks/policies/data_policy.py +++ b/backend/dataall/cdkproxy/stacks/policies/data_policy.py @@ -2,6 +2,7 @@ from typing import List from aws_cdk import aws_iam as iam +from ....aws.handlers.kms import KMS from ....db import models @@ -9,6 +10,11 @@ class DataPolicy: + """ + Class including all permissions needed to work with AWS Lambda. + It allows data.all users to: + - + """ def __init__( self, stack, @@ -35,57 +41,6 @@ def __init__( self.team = team self.datasets = datasets - def generate_admins_data_access_policy(self) -> iam.Policy: - """ - Creates an open ws_iam.Policy for environment admins - """ - - policy: iam.Policy = iam.Policy( - self.stack, - self.id, - policy_name=self.name, - statements=[ - iam.PolicyStatement( - actions=[ - 's3:List*', - 's3:Get*', - 's3:PutAccountPublicAccessBlock', - 's3:PutAccessPointPublicAccessBlock', - 's3:PutStorageLensConfiguration', - 's3:GetAccessPoint', - 's3:GetAccessPointPolicy', - 's3:ListAccessPoints', - 's3:CreateAccessPoint', - 's3:DeleteAccessPoint', - 's3:GetAccessPointPolicyStatus', - 's3:DeleteAccessPointPolicy', - 's3:PutAccessPointPolicy', - 's3:CreateJob', - ], - resources=['*'], - ), - iam.PolicyStatement( - actions=['s3:*'], - resources=[ - f'arn:aws:s3-object-lambda:{self.region}:{self.account}:accesspoint/*', - f'arn:aws:s3:{self.region}:{self.account}:job/*', - f'arn:aws:s3:{self.region}:{self.account}:storage-lens/*', - f'arn:aws:s3:us-west-2:{self.account}:async-request/mrap/*/*', - f'arn:aws:s3:{self.region}:{self.account}:accesspoint/*', - f'arn:aws:s3:::{self.resource_prefix}*/*', - f'arn:aws:s3:::{self.resource_prefix}*', - ], - ), - iam.PolicyStatement( - actions=['athena:*', 'lakeformation:*', 'glue:*', 'kms:*'], - resources=['*'], - ), - ], - ) - logger.debug(f'Final generated policy {policy.document.to_json()}') - - return policy - def generate_data_access_policy(self) -> iam.Policy: """ Creates aws_iam.Policy based on team datasets @@ -105,70 +60,97 @@ def generate_data_access_policy(self) -> iam.Policy: def get_statements(self): statements = [ iam.PolicyStatement( + sid="ListAll", actions=[ - 's3:List*', - 's3:Get*', - 's3:PutAccountPublicAccessBlock', - 's3:PutAccessPointPublicAccessBlock', - 's3:PutStorageLensConfiguration', - 's3:CreateJob', - 's3:GetAccessPoint', - 's3:GetAccessPointPolicy', - 's3:ListAccessPoints', - 's3:CreateAccessPoint', - 's3:DeleteAccessPoint', - 's3:GetAccessPointPolicyStatus', - 's3:DeleteAccessPointPolicy', - 's3:PutAccessPointPolicy', + "s3:ListAllMyBuckets", + "s3:ListAccessPoints", + "s3:GetBucketLocation", + 'kms:ListAliases', + 'kms:ListKeys', ], - resources=['*'], - ), - iam.PolicyStatement( - actions=['s3:*'], - resources=[ - f'arn:aws:s3-object-lambda:{self.region}:{self.account}:accesspoint/*', - f'arn:aws:s3:{self.region}:{self.account}:job/*', - f'arn:aws:s3:{self.region}:{self.account}:storage-lens/*', - f'arn:aws:s3:us-west-2:{self.account}:async-request/mrap/*/*', - f'arn:aws:s3:{self.region}:{self.account}:accesspoint/*', - ], - ), + resources=["*"], + effect=iam.Effect.ALLOW + ) ] self.set_allowed_s3_buckets_statements(statements) - - self.set_athena_statements(statements) + self.set_allowed_kms_keys_statements(statements) return statements def set_allowed_s3_buckets_statements(self, statements): - allowed_buckets = [ - f'arn:aws:s3:::{self.environment.EnvironmentDefaultBucketName}', - f'arn:aws:s3:::{self.environment.EnvironmentDefaultBucketName}/*', - ] + allowed_buckets = [] + allowed_access_points = [] if self.datasets: dataset: models.Dataset for dataset in self.datasets: - allowed_buckets.append(f'arn:aws:s3:::{dataset.S3BucketName}/*') allowed_buckets.append(f'arn:aws:s3:::{dataset.S3BucketName}') - statements.extend( - [ - iam.PolicyStatement( - actions=['s3:*'], - resources=allowed_buckets, - ) - ] - ) - - def set_athena_statements(self, statements): - statements.extend( - [ - iam.PolicyStatement( - actions=['athena:*'], - resources=[ - f'arn:aws:athena:{self.region}:{self.account}:workgroup/{self.team.environmentAthenaWorkGroup}', - f'arn:aws:athena:{self.region}:{self.account}:datacatalog/*', - ], + allowed_access_points.append(f'arn:aws:s3:{dataset.region}:{dataset.AwsAccountId}:accesspoint/{dataset.datasetUri}*') + allowed_buckets_content = [f"{bucket}/*" for bucket in allowed_buckets] + statements.extend( + [ + iam.PolicyStatement( + sid="ListDatasetsBuckets", + actions=[ + "s3:ListBucket", + "s3:GetBucketLocation" + ], + resources=allowed_buckets, + effect=iam.Effect.ALLOW, + ), + iam.PolicyStatement( + sid="ReadWriteDatasetsBuckets", + actions=[ + "s3:PutObject", + "s3:PutObjectAcl", + "s3:GetObject", + "s3:GetObjectAcl", + "s3:GetObjectVersion", + "s3:DeleteObject" + ], + effect=iam.Effect.ALLOW, + resources=allowed_buckets_content, + ), + iam.PolicyStatement( + sid="ReadAccessPointsDatasetBucket", + actions=[ + 's3:GetAccessPoint', + 's3:GetAccessPointPolicy', + 's3:GetAccessPointPolicyStatus', + ], + effect=iam.Effect.ALLOW, + resources=allowed_access_points, + ) + ] + ) + + def set_allowed_kms_keys_statements(self, statements): + allowed_buckets_kms_keys = [] + if self.datasets: + dataset: models.Dataset + for dataset in self.datasets: + if dataset.imported and dataset.importedKmsKey: + key_id = KMS.get_key_id( + account_id=dataset.AwsAccountId, + region=dataset.region, + key_alias=f"alias/{dataset.KmsAlias}" + ) + if key_id: + allowed_buckets_kms_keys.append(f"arn:aws:kms:{dataset.region}:{dataset.AwsAccountId}:key/{key_id}") + if len(allowed_buckets_kms_keys): + statements.extend( + [ + iam.PolicyStatement( + sid="KMSImportedDatasetAccess", + actions=[ + "kms:Decrypt", + "kms:Encrypt", + "kms:ReEncrypt*", + "kms:DescribeKey", + "kms:GenerateDataKey" + ], + effect=iam.Effect.ALLOW, + resources=allowed_buckets_kms_keys + ) + ] ) - ] - ) diff --git a/backend/dataall/cdkproxy/stacks/policies/databrew.py b/backend/dataall/cdkproxy/stacks/policies/databrew.py index 19aa41293..0c6c81878 100644 --- a/backend/dataall/cdkproxy/stacks/policies/databrew.py +++ b/backend/dataall/cdkproxy/stacks/policies/databrew.py @@ -3,20 +3,32 @@ class Databrew(ServicePolicy): + """ + Class including all permissions needed to work with AWS DataBrew. + """ def get_statements(self): statements = [ - iam.PolicyStatement(actions=['databrew:List*'], resources=['*']), iam.PolicyStatement( + # sid="DataBrewGeneric", + actions=['databrew:List*'], + resources=['*'] + ), + iam.PolicyStatement( + # sid="DataBrewRecipes", actions=[ - 'databrew:Delete*', - 'databrew:Describe*', - 'databrew:PublishRecipe', - 'databrew:SendProjectSessionAction', - 'databrew:Start*', - 'databrew:Stop*', + 'databrew:BatchDeleteRecipeVersion', + 'databrew:*Recipe', + ], + resources=[ + f'arn:aws:databrew:{self.region}:{self.account}:recipe/{self.resource_prefix}*' + ], + ), + iam.PolicyStatement( + # sid="DataBrewManageTeamResources", + not_actions=[ + 'databrew:Create*', 'databrew:TagResource', 'databrew:UntagResource', - 'databrew:Update*', ], resources=[ f'arn:aws:databrew:{self.region}:{self.account}:*/{self.resource_prefix}*' @@ -28,8 +40,14 @@ def get_statements(self): }, ), iam.PolicyStatement( - actions=['databrew:Create*'], - resources=['*'], + # sid="DataBrewCreateTeamResources", + actions=[ + 'databrew:Create*', + 'databrew:TagResource', + ], + resources=[ + f'arn:aws:databrew:{self.region}:{self.account}:*/{self.resource_prefix}*' + ], conditions={ 'StringEquals': {f'aws:RequestTag/{self.tag_key}': [self.tag_value]} }, diff --git a/backend/dataall/cdkproxy/stacks/policies/glue.py b/backend/dataall/cdkproxy/stacks/policies/glue.py index 896622cfe..899ca92a6 100644 --- a/backend/dataall/cdkproxy/stacks/policies/glue.py +++ b/backend/dataall/cdkproxy/stacks/policies/glue.py @@ -2,38 +2,33 @@ from aws_cdk import aws_iam as iam -class Glue(ServicePolicy): +class GlueCatalog(ServicePolicy): + """ + Class including all permissions needed to work with AWS Glue Catalog. + """ def get_statements(self): statements = [ iam.PolicyStatement( + # sid="GlueLFReadData", + effect=iam.Effect.ALLOW, actions=[ - 'glue:Get*', - 'glue:List*', - 'glue:BatchGet*', - 'glue:CreateClassifier', - 'glue:CreateScript', - 'glue:CreateSecurityConfiguration', - 'glue:DeleteClassifier', - 'glue:DeleteResourcePolicy', - 'glue:DeleteSecurityConfiguration', - 'glue:ResetJobBookmark', - 'glue:PutDataCatalogEncryptionSettings', - 'glue:PutResourcePolicy', - 'glue:StartCrawlerSchedule', - 'glue:StartJobRun', - 'glue:StopCrawlerSchedule', - 'glue:TagResource', - 'glue:UntagResource', - 'glue:UpdateClassifier', - 'glue:UpdateCrawlerSchedule', - 'glue:BatchStopJobRun', - 'glue:SearchTables', - ], - resources=[ - '*', + "lakeformation:GetDataAccess", + "glue:GetTable", + "glue:GetTables", + "glue:SearchTables", + "glue:GetDatabase", + "glue:GetDatabases", + "glue:GetPartitions", + "lakeformation:GetResourceLFTags", + "lakeformation:ListLFTags", + "lakeformation:GetLFTag", + "lakeformation:SearchTablesByLFTags", + "lakeformation:SearchDatabasesByLFTags" ], + resources=["*"], ), iam.PolicyStatement( + # sid="GlueManageCatalog", actions=[ 'glue:CreateConnection', 'glue:CreateDatabase', @@ -64,13 +59,81 @@ def get_statements(self): f'arn:aws:glue:{self.region}:{self.account}:table/{self.resource_prefix}*/*', f'arn:aws:glue:{self.region}:{self.account}:connection/{self.resource_prefix}*', ], + ) + ] + return statements + + +class Glue(ServicePolicy): + """ + Class including all permissions needed to work with AWS Glue ETL. + """ + def get_statements(self): + statements = [ + iam.PolicyStatement( + # sid="ListBucketProfilingGlue", + actions=[ + "s3:ListBucket", + ], + effect=iam.Effect.ALLOW, + resources=[f'arn:aws:s3:::{self.environment.EnvironmentDefaultBucketName}'], + conditions={"StringEquals": { + "s3:prefix": ["", "profiling/", "profiling/code/"], + "s3:delimiter": ["/"]}} + ), + iam.PolicyStatement( + # sid="ReadEnvironmentBucketProfilingGlue", + actions=[ + "s3:GetObject", + "s3:GetObjectAcl", + "s3:GetObjectVersion", + ], + resources=[ + f'arn:aws:s3:::{self.environment.EnvironmentDefaultBucketName}/profiling/code/*'], + effect=iam.Effect.ALLOW, + ), + iam.PolicyStatement( + # sid="GlueList", + effect=iam.Effect.ALLOW, + actions=[ + 'glue:Get*', + 'glue:List*', + 'glue:BatchGet*', + ], + resources=["*"], + ), + iam.PolicyStatement( + # sid="GlueCreateS3Bucket", + effect=iam.Effect.ALLOW, + actions=[ + 's3:CreateBucket', + 's3:ListBucket', + 's3:PutBucketPublicAccessBlock' + ], + resources=[f'arn:aws:s3:::aws-glue-assets-{self.account}-{self.region}'], ), iam.PolicyStatement( + # sid="GlueReadWriteS3Bucket", + actions=[ + 's3:GetObject', + 's3:PutObject', + 's3:DeleteObject' + ], + effect=iam.Effect.ALLOW, + resources=[ + f'arn:aws:s3:::aws-glue-assets-{self.account}-{self.region}/{self.resource_prefix}/{self.team.groupUri}/', + f'arn:aws:s3:::aws-glue-assets-{self.account}-{self.region}/{self.resource_prefix}/{self.team.groupUri}/*', + ], + ), + iam.PolicyStatement( + # sid="GlueCreate", + effect=iam.Effect.ALLOW, actions=[ 'glue:CreateDevEndpoint', 'glue:CreateCrawler', 'glue:CreateJob', 'glue:CreateTrigger', + 'glue:TagResource' ], resources=[ f'arn:aws:glue:{self.region}:{self.account}:crawler/{self.resource_prefix}*', @@ -82,30 +145,22 @@ def get_statements(self): ], conditions={ 'StringEquals': {f'aws:RequestTag/{self.tag_key}': [self.tag_value]} - }, + } ), iam.PolicyStatement( - actions=[ - 'glue:DeleteDevEndpoint', - 'glue:DeleteCrawler', - 'glue:DeleteJob', - 'glue:DeleteTrigger', - 'glue:StartCrawler', - 'glue:StartTrigger', - 'glue:StopCrawler', - 'glue:StopTrigger', - 'glue:UpdateCrawler', - 'glue:UpdateDevEndpoint', - 'glue:UpdateJob', - 'glue:UpdateTrigger', + # sid="GlueManageGlueResources", + effect=iam.Effect.ALLOW, + not_actions=[ + 'glue:CreateDevEndpoint', + 'glue:CreateTrigger', + 'glue:CreateJob', + 'glue:CreateCrawler', ], resources=[ - f'arn:aws:glue:{self.region}:{self.account}:crawler/{self.resource_prefix}*', - f'arn:aws:glue:{self.region}:{self.account}:job/{self.resource_prefix}*', f'arn:aws:glue:{self.region}:{self.account}:devEndpoint/{self.resource_prefix}*', - f'arn:aws:glue:{self.region}:{self.account}:catalog', f'arn:aws:glue:{self.region}:{self.account}:trigger/{self.resource_prefix}*', - f'arn:aws:glue:{self.region}:{self.account}:table/{self.resource_prefix}*/*', + f'arn:aws:glue:{self.region}:{self.account}:job/{self.resource_prefix}*', + f'arn:aws:glue:{self.region}:{self.account}:crawler/{self.resource_prefix}*' ], conditions={ 'StringEquals': { @@ -113,5 +168,27 @@ def get_statements(self): } }, ), + iam.PolicyStatement( + # sid="SupportGluePermissions", + effect=iam.Effect.ALLOW, + actions=[ + 'glue:*Classifier', + 'glue:CreateScript', + ], + resources=['*'], + ), + iam.PolicyStatement( + # sid="LoggingGlue", + actions=[ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents', + ], + effect=iam.Effect.ALLOW, + resources=[ + f'arn:aws:logs:{self.region}:{self.account}:log-group:/aws-glue/*', + f'arn:aws:logs:{self.region}:{self.account}:log-group:/aws-glue/*:log-stream:*', + ], + ) ] return statements diff --git a/backend/dataall/cdkproxy/stacks/policies/lakeformation.py b/backend/dataall/cdkproxy/stacks/policies/lakeformation.py deleted file mode 100644 index e495a1da2..000000000 --- a/backend/dataall/cdkproxy/stacks/policies/lakeformation.py +++ /dev/null @@ -1,35 +0,0 @@ -from aws_cdk import aws_iam as iam - -from .service_policy import ServicePolicy - - -class LakeFormation(ServicePolicy): - def get_statements(self): - return [ - iam.PolicyStatement( - actions=[ - 'lakeformation:GetDataAccess', - 'lakeformation:GetResourceLFTags', - 'lakeformation:ListLFTags', - 'lakeformation:GetLFTag', - 'lakeformation:SearchTablesByLFTags', - 'lakeformation:SearchDatabasesByLFTags', - 'lakeformation:GetWorkUnits', - 'lakeformation:StartQueryPlanning', - 'lakeformation:GetWorkUnitResults', - 'lakeformation:GetQueryState', - 'lakeformation:GetQueryStatistics', - 'lakeformation:StartTransaction', - 'lakeformation:CommitTransaction', - 'lakeformation:CancelTransaction', - 'lakeformation:ExtendTransaction', - 'lakeformation:DescribeTransaction', - 'lakeformation:ListTransactions', - 'lakeformation:GetTableObjects', - 'lakeformation:UpdateTableObjects', - 'lakeformation:DeleteObjectsOnCancel', - ], - resources=['*'], - effect=iam.Effect.ALLOW, - ) - ] diff --git a/backend/dataall/cdkproxy/stacks/policies/quicksight.py b/backend/dataall/cdkproxy/stacks/policies/quicksight.py index 487ddb429..3690b0f95 100644 --- a/backend/dataall/cdkproxy/stacks/policies/quicksight.py +++ b/backend/dataall/cdkproxy/stacks/policies/quicksight.py @@ -4,38 +4,25 @@ class QuickSight(ServicePolicy): + """ + Class including all permissions needed to work with Amazon Quicksight. + It allows data.all users to: + - + """ def get_statements(self): return [ iam.PolicyStatement( - actions=[ - 'quicksight:ListDataSets', - 'quicksight:CreateDataSource', - 'quicksight:SetGroupMapping', - 'quicksight:SearchDirectoryGroups', - 'quicksight:ListIngestions', - 'quicksight:GetAnonymousUserEmbedUrl', - 'quicksight:ListDataSources', - 'quicksight:GetSessionEmbedUrl', - 'quicksight:GetGroupMapping', - 'quicksight:ListNamespaces', - ], - resources=['*'], + # sid="QuicksightList", effect=iam.Effect.ALLOW, + actions=['quicksight:List*'], + resources=['*'], ), iam.PolicyStatement( - actions=[ - 'quicksight:*', - ], + # sid="QuicksightManageTeamResources", + effect=iam.Effect.ALLOW, + actions=['quicksight:*'], resources=[ - f'arn:aws:quicksight:{self.region}:{self.account}:analysis/{self.resource_prefix}*', - f'arn:aws:quicksight:{self.region}:{self.account}:folder/{self.resource_prefix}*', - f'arn:aws:quicksight:{self.region}:{self.account}:dataset/{self.resource_prefix}*/ingestion/*', - f'arn:aws:quicksight:{self.region}:{self.account}:customization/{self.resource_prefix}*', - f'arn:aws:quicksight:{self.region}:{self.account}:dashboard/{self.resource_prefix}*', - f'arn:aws:quicksight:{self.region}:{self.account}:datasource/{self.resource_prefix}*', - f'arn:aws:quicksight:{self.region}:{self.account}:template/{self.resource_prefix}*', - f'arn:aws:quicksight:{self.region}:{self.account}:theme/{self.resource_prefix}*', + f'arn:aws:quicksight:{self.region}:{self.account}:*/{self.resource_prefix}-{self.team.groupUri}*' ], - effect=iam.Effect.ALLOW, ), ] diff --git a/backend/dataall/cdkproxy/stacks/policies/redshift.py b/backend/dataall/cdkproxy/stacks/policies/redshift.py deleted file mode 100644 index 1c02dee66..000000000 --- a/backend/dataall/cdkproxy/stacks/policies/redshift.py +++ /dev/null @@ -1,68 +0,0 @@ -from aws_cdk import aws_iam as iam - -from .service_policy import ServicePolicy - - -class Redshift(ServicePolicy): - def get_statements(self): - return [ - iam.PolicyStatement( - actions=[ - 'redshift:List*', - 'redshift:ModifySavedQuery', - 'redshift:CreateSavedQuery', - 'redshift:FetchResults', - 'redshift:ViewQueriesFromConsole', - 'redshift:CancelQuery', - 'redshift:Describe*', - 'redshift:ExecuteQuery', - 'redshift:DeleteSavedQueries', - 'redshift-data:ListTables', - 'redshift-data:ListTables', - 'redshift-data:GetStatementResult', - 'redshift-data:CancelStatement', - 'redshift-data:ListSchemas', - 'redshift-data:ExecuteStatement', - 'redshift-data:ListStatements', - 'redshift-data:ListDatabases', - 'redshift-data:DescribeStatement', - ], - resources=['*'], - effect=iam.Effect.ALLOW, - ), - iam.PolicyStatement( - actions=[ - 'redshift:DeleteCluster', - 'redshift:RejectDataShare', - 'redshift:CancelResize', - 'redshift:ModifyClusterIamRoles', - 'redshift:PauseCluster', - 'redshift:ResumeCluster', - 'redshift:CreateEventSubscription', - 'redshift:RebootCluster', - 'redshift:CreateClusterSnapshot', - 'redshift:DeleteClusterSnapshot', - 'redshift:AuthorizeDataShare', - 'redshift:CopyClusterSnapshot', - 'redshift:CreateCluster', - 'redshift:GetClusterCredentials', - 'redshift:JoinGroup', - 'redshift:ModifyCluster', - 'redshift:AssociateDataShareConsumer', - 'redshift:DeleteEventSubscription', - 'redshift:DeauthorizeDataShare', - 'redshift:ModifyEventSubscription', - 'redshift:DisassociateDataShareConsumer', - ], - resources=[ - f'arn:aws:redshift:{self.region}:{self.account}:dbgroup:{self.resource_prefix}*/*', - f'arn:aws:redshift:{self.region}:{self.account}:datashare:{self.resource_prefix}*/*', - f'arn:aws:redshift:{self.region}:{self.account}:dbuser:{self.resource_prefix}*/*', - f'arn:aws:redshift:{self.region}:{self.account}:snapshot:{self.resource_prefix}*/*', - f'arn:aws:redshift:{self.region}:{self.account}:cluster:{self.resource_prefix}*', - f'arn:aws:redshift:{self.region}:{self.account}:eventsubscription:{self.resource_prefix}*', - f'arn:aws:redshift:{self.region}:{self.account}:dbname:{self.resource_prefix}*/*', - ], - effect=iam.Effect.ALLOW, - ), - ] diff --git a/backend/dataall/cdkproxy/stacks/policies/sagemaker.py b/backend/dataall/cdkproxy/stacks/policies/sagemaker.py index fee698989..8d6a08d2b 100644 --- a/backend/dataall/cdkproxy/stacks/policies/sagemaker.py +++ b/backend/dataall/cdkproxy/stacks/policies/sagemaker.py @@ -3,9 +3,30 @@ class Sagemaker(ServicePolicy): + """ + Class including all permissions needed to work with Amazon SageMaker. + - Allow creation and management of SageMaker Notebooks only if tagged with team tag + - DO NOT allow creation of domain because this is handled in the environment stack + - DO NOT allow creation of user-profiles because this is handled in the ML Studio stack + - Allow management of domains and user-profiles tagged with team tag + - Allow any action besides the above listed ones on resources that are not notebooks, domains, apps and user-profiles + - Allow support permissions on ECR, Service Catalog and logging + """ def get_statements(self): statements = [ iam.PolicyStatement( + effect=iam.Effect.ALLOW, + actions=['sagemaker:AddTags'], + resources=['*'], + conditions={ + 'StringEquals': { + f'aws:ResourceTag/{self.tag_key}': [self.tag_value], + f'aws:RequestTag/{self.tag_key}': [self.tag_value], + }, + }, + ), + iam.PolicyStatement( + effect=iam.Effect.ALLOW, actions=[ 'sagemaker:List*', 'sagemaker:Describe*', @@ -15,66 +36,69 @@ def get_statements(self): 'sagemaker:RenderUiTemplate', 'sagemaker:GetSearchSuggestions', 'sagemaker:QueryLineage', + 'sagemaker:GetSagemakerServicecatalogPortfolioStatus', 'sagemaker:CreateNotebookInstanceLifecycleConfig', 'sagemaker:DeleteNotebookInstanceLifecycleConfig', - 'sagemaker:CreatePresignedDomainUrl' ], resources=['*'], ), + # SageMaker Notebooks permissions iam.PolicyStatement( - actions=['sagemaker:AddTags'], - resources=['*'], + # sid="SageMakerCreateTaggedResourcesNotebooks", + effect=iam.Effect.ALLOW, + actions=['sagemaker:CreateNotebookInstance'], + resources=[ + f'arn:aws:sagemaker:{self.region}:{self.account}:notebook-instance/{self.resource_prefix}*', + + ], conditions={ 'StringEquals': { + f'aws:RequestTag/{self.tag_key}': [self.tag_value], f'aws:ResourceTag/{self.tag_key}': [self.tag_value] - } + }, }, ), iam.PolicyStatement( - actions=['sagemaker:Delete*'], + # sid="SageMakerCreatePresignedNotebookInstanceUrl", + effect=iam.Effect.ALLOW, + actions=['sagemaker:CreatePresignedNotebookInstanceUrl'], resources=[ - f'arn:aws:sagemaker:{self.region}:{self.account}:notebook-instance/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:algorithm/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:model/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:endpoint/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:endpoint-config/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:experiment/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:experiment-trial/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:experiment-group/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:model-bias-job-definition/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:model-package/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:model-package-group/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:model-quality-job-definition/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:monitoring-schedule/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:pipeline/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:project/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:app/*' + f'arn:aws:sagemaker:{self.region}:{self.account}:notebook-instance/{self.resource_prefix}*', ], conditions={ 'StringEquals': { - f'aws:ResourceTag/{self.tag_key}': [self.tag_value] - } + f'sagemaker:ResourceTag/{self.tag_key}': [self.tag_value] + }, }, ), iam.PolicyStatement( - actions=['sagemaker:CreateApp'], - resources=['*'] - ), - iam.PolicyStatement( - actions=['sagemaker:Create*'], - resources=['*'], + # sid="SageMakerManageResourcesNotebooks", + effect=iam.Effect.ALLOW, + actions=[ + 'sagemaker:*NotebookInstance', + ], + resources=[ + f'arn:aws:sagemaker:{self.region}:{self.account}:notebook-instance/{self.resource_prefix}*', + ], + conditions={ + 'StringEquals': { + f'aws:ResourceTag/{self.tag_key}': [self.tag_value] + }, + }, ), + # SageMaker Studio permissions iam.PolicyStatement( - actions=['sagemaker:Start*', 'sagemaker:Stop*'], + # sid="SageMakerManageTeamResourcesMLStudio", + effect=iam.Effect.ALLOW, + actions=[ + 'sagemaker:DeleteDomain', + 'sagemaker:DeleteUserProfile', + 'sagemaker:UpdateDomain', + 'sagemaker:UpdateUserProfile', + ], resources=[ - f'arn:aws:sagemaker:{self.region}:{self.account}:notebook-instance/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:monitoring-schedule/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:pipeline/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:training-job/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:processing-job/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:hyper-parameter-tuning-job/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:transform-job/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:automl-job/*' + f'arn:aws:sagemaker:{self.region}:{self.account}:domain/*', + f'arn:aws:sagemaker:{self.region}:{self.account}:user-profile/*/*', ], conditions={ 'StringEquals': { @@ -82,59 +106,100 @@ def get_statements(self): } }, ), + # For everything that is not domains and user-profiles we allow permissions if the resource is tagged + # Deny on creation of domains and users, generic allow for prefixed and tagged resources + # allow for apps (cannot be tagged) and special tag needed for CreatePresignedDomainUrl iam.PolicyStatement( - actions=['sagemaker:Update*'], + # sid="SageMakerDenyCreateDomainsUsers", + effect=iam.Effect.DENY, + actions=['sagemaker:Create*'], resources=[ - f'arn:aws:sagemaker:{self.region}:{self.account}:notebook-instance/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:notebook-instance-lifecycle-config/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:studio-lifecycle-config/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:endpoint/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:pipeline/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:pipeline-execution/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:monitoring-schedule/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:experiment/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:experiment-trial/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:experiment-trial-component/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:model-package/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:training-job/*', - f'arn:aws:sagemaker:{self.region}:{self.account}:project/*' + f'arn:aws:sagemaker:{self.region}:{self.account}:domain/*', + f'arn:aws:sagemaker:{self.region}:{self.account}:user-profile/*/*', + ], + ), + iam.PolicyStatement( + # sid="SageMakerCreateGenericResources", + effect=iam.Effect.ALLOW, + actions=['sagemaker:Create*'], + not_resources=[ + f'arn:aws:sagemaker:{self.region}:{self.account}:*/{self.resource_prefix}*', + f'arn:aws:sagemaker:{self.region}:{self.account}:*/{self.resource_prefix}*/*', ], conditions={ 'StringEquals': { - f'aws:ResourceTag/{self.tag_key}': [self.tag_value] - } + f'aws:ResourceTag/{self.tag_key}': [self.tag_value], + f'aws:RequestTag/{self.tag_key}': [self.tag_value], + }, }, ), iam.PolicyStatement( - actions=['sagemaker:InvokeEndpoint', 'sagemaker:InvokeEndpointAsync'], + # sid="SageMakerApps", + effect=iam.Effect.ALLOW, + actions=[ + 'sagemaker:CreateApp', + 'sagemaker:DeleteApp' + ], + resources=[f'arn:aws:sagemaker:{self.region}:{self.account}:app/*/*'] + ), + iam.PolicyStatement( + # sid="SageMakerCreatePresignedDomainUrl", + effect=iam.Effect.ALLOW, + actions=['sagemaker:CreatePresignedDomainUrl'], + resources=[f'arn:aws:sagemaker:{self.region}:{self.account}:user-profile/*/*'], + conditions={ + 'StringEquals': { + f'sagemaker:ResourceTag/{self.tag_key}': [self.tag_value] + }, + }, + ), + iam.PolicyStatement( + # sid="SageMakerManageGenericResources", + effect=iam.Effect.ALLOW, + actions=[ + 'sagemaker:Delete*', + 'sagemaker:Update*', + 'sagemaker:Start*', + 'sagemaker:Stop*', + 'sagemaker:InvokeEndpoint', + 'sagemaker:InvokeEndpointAsync' + ], resources=[ - f'arn:aws:sagemaker:{self.region}:{self.account}:endpoint/*' + f'arn:aws:sagemaker:{self.region}:{self.account}:*/{self.resource_prefix}*', + f'arn:aws:sagemaker:{self.region}:{self.account}:*/{self.resource_prefix}*/*', ], conditions={ 'StringEquals': { - f'aws:ResourceTag/{self.tag_key}': [self.tag_value] - } + f'aws:ResourceTag/{self.tag_key}': [self.tag_value], + }, }, ), + # Logging and support permissions iam.PolicyStatement( + # sid="SageMakerLogging", + effect=iam.Effect.ALLOW, actions=[ 'logs:CreateLogGroup', 'logs:CreateLogStream', - 'logs:PutLogEvents'], + 'logs:PutLogEvents' + ], resources=[ f'arn:aws:logs:{self.region}:{self.account}:log-group:/aws/sagemaker/*', f'arn:aws:logs:{self.region}:{self.account}:log-group:/aws/sagemaker/*:log-stream:*', ] ), iam.PolicyStatement( + # sid="SageMakerSupport", + effect=iam.Effect.ALLOW, actions=[ 'ecr:GetAuthorizationToken', 'ecr:BatchCheckLayerAvailability', 'ecr:GetDownloadUrlForLayer', - 'ecr:BatchGetImage'], - resources=[ - '*' - ] + 'ecr:BatchGetImage', + 'servicecatalog:ListAcceptedPortfolioShares', + 'servicecatalog:ListPrincipalsForPortfolio', + ], + resources=['*'] ) ] return statements diff --git a/backend/dataall/cdkproxy/stacks/policies/secretsmanager.py b/backend/dataall/cdkproxy/stacks/policies/secretsmanager.py new file mode 100644 index 000000000..54604542a --- /dev/null +++ b/backend/dataall/cdkproxy/stacks/policies/secretsmanager.py @@ -0,0 +1,51 @@ +from .service_policy import ServicePolicy +from aws_cdk import aws_iam + + +class SecretsManager(ServicePolicy): + """ + Class including all permissions needed to work with AWS Secrets Manager. + It allows data.all users to: + - + """ + + def get_statements(self): + statements = [ + aws_iam.PolicyStatement( + # sid="SecretsReadAll", + effect=aws_iam.Effect.ALLOW, + actions=["secretsmanager:ListSecrets"], + resources=["*"], + ), + aws_iam.PolicyStatement( + # sid='CreateTeamSecrets', + effect=aws_iam.Effect.ALLOW, + actions=[ + "secretsmanager:CreateSecret", + "secretsmanager:TagResource", + ], + resources=[f"arn:aws:secretsmanager:*:{self.account}:secret:{self.resource_prefix}*"], + conditions={ + 'StringEquals': { + f'aws:RequestTag/{self.tag_key}': [self.tag_value] + } + }, + ), + aws_iam.PolicyStatement( + # sid='ManageTeamSecrets', + effect=aws_iam.Effect.ALLOW, + actions=[ + "secretsmanager:GetSecretValue", + "secretsmanager:DescribeSecret", + "secretsmanager:DeleteSecret", + "secretsmanager:UpdateSecret" + ], + resources=[f"arn:aws:secretsmanager:*:{self.account}:secret:{self.resource_prefix}*"], + conditions={ + 'StringEquals': { + f'aws:ResourceTag/{self.tag_key}': [self.tag_value] + } + }, + ) + ] + return statements diff --git a/backend/dataall/cdkproxy/stacks/policies/service_policy.py b/backend/dataall/cdkproxy/stacks/policies/service_policy.py index 007ee0044..abdc36ddf 100644 --- a/backend/dataall/cdkproxy/stacks/policies/service_policy.py +++ b/backend/dataall/cdkproxy/stacks/policies/service_policy.py @@ -4,11 +4,15 @@ from aws_cdk import aws_iam from ....db import permissions +from ....db import models logger = logging.getLogger() class ServicePolicy(object): + """ + Generic Class to define AWS-services policies added to an IAM role + """ def __init__( self, stack, @@ -20,6 +24,8 @@ def __init__( tag_key, tag_value, resource_prefix, + environment: models.Environment, + team: models.EnvironmentGroup, permissions, ): self.stack = stack @@ -30,6 +36,8 @@ def __init__( self.tag_key = tag_key self.tag_value = tag_value self.resource_prefix = resource_prefix + self.environment = environment + self.team = team self.permissions = permissions self.role_name = role_name @@ -37,69 +45,77 @@ def generate_policies(self) -> [aws_iam.ManagedPolicy]: """ Creates aws_iam.Policy based on declared subclasses of Policy object """ - from .redshift import Redshift + from ._lambda import Lambda + from .athena import Athena + from .cloudformation import Cloudformation + from .aws_cicd import AwsCICD from .databrew import Databrew - from .lakeformation import LakeFormation + from .glue import Glue, GlueCatalog + from .quicksight import QuickSight from .sagemaker import Sagemaker - from ._lambda import Lambda - from .codestar import CodeStar - from .glue import Glue + from .secretsmanager import SecretsManager + from .sqs import SQS + from .ssm import SSM from .stepfunctions import StepFunctions - from .quicksight import QuickSight - from .cloudformation import Cloudformation policies: [aws_iam.ManagedPolicy] = [ - # This policy covers the minumum actions required independent - # of the service permissions given to the group. - # The 'glue:GetTable', 'glue:GetPartitions' and - # 'lakeformation:GetDataAccess' actions are additionally - # required for the Worksheet/Athena feature. + # This policy adds some minimum actions required independent from the services enabled for the group aws_iam.ManagedPolicy( self.stack, self.id, managed_policy_name=f'{self.id}-0', statements=[ aws_iam.PolicyStatement( + sid="ListActions", + effect=aws_iam.Effect.ALLOW, actions=[ - 'athena:ListEngineVersions', - 'athena:ListDataCatalogs', - 'athena:ListWorkGroups', - 'glue:GetTable', - 'glue:GetPartitions', - 'lakeformation:GetDataAccess', - 'kms:Decrypt', - 'kms:DescribeKey', - 'kms:Encrypt', - 'kms:ReEncrypt*', - 'kms:GenerateDataKey*', - 'kms:CreateGrant', - 'secretsmanager:GetSecretValue', - 'secretsmanager:DescribeSecret', - 'secretsmanager:ListSecrets', - 'ssm:GetParametersByPath', - 'ssm:GetParameters', - 'ssm:GetParameter', 'ec2:Describe*', 'logs:Describe*', 'logs:Get*', 'logs:List*', + 'cloudwatch:GetMetricData', + 'events:ListRuleNamesByTarget', 'iam:list*', 'iam:Get*', + 'iam:CreatePolicy', + 'iam:CreateServiceLinkedRole', 'tag:GetResources', - 'tag:TagResources', - 'tag:UntagResources', 'tag:GetTagValues', 'tag:GetTagKeys', ], resources=['*'], ), aws_iam.PolicyStatement( + sid="CreateServiceRole", + actions=[ + 'iam:CreateRole', + ], + resources=[ + f'arn:aws:iam::{self.account}:role/service-role/*' + ] + ), + aws_iam.PolicyStatement( + sid="PassRole", actions=[ 'iam:PassRole', ], resources=[ f'arn:aws:iam::{self.account}:role/{self.role_name}' ], + conditions={ + "StringEquals": { + "iam:PassedToService": [ + "glue.amazonaws.com", + "lambda.amazonaws.com", + "sagemaker.amazonaws.com", + "states.amazonaws.com", + "sagemaker.amazonaws.com", + "databrew.amazonaws.com", + "codebuild.amazonaws.com", + "codepipeline.amazonaws.com" + ] + } + } ), ], ) @@ -107,11 +123,8 @@ def generate_policies(self) -> [aws_iam.ManagedPolicy]: services = ServicePolicy.__subclasses__() - if permissions.CREATE_REDSHIFT_CLUSTER not in self.permissions: - services.remove(Redshift) if permissions.CREATE_DATASET not in self.permissions: services.remove(Databrew) - services.remove(LakeFormation) services.remove(Glue) if ( permissions.CREATE_NOTEBOOK not in self.permissions @@ -120,7 +133,7 @@ def generate_policies(self) -> [aws_iam.ManagedPolicy]: services.remove(Sagemaker) if permissions.CREATE_PIPELINE not in self.permissions: services.remove(Lambda) - services.remove(CodeStar) + services.remove(AwsCICD) services.remove(StepFunctions) if permissions.CREATE_DASHBOARD not in self.permissions: services.remove(QuickSight) @@ -130,7 +143,7 @@ def generate_policies(self) -> [aws_iam.ManagedPolicy]: statements.extend(service.get_statements(self)) statements_chunks: list = [ - statements[i : i + 8] for i in range(0, len(statements), 8) + statements[i : i + 10] for i in range(0, len(statements), 10) ] for index, chunk in enumerate(statements_chunks): diff --git a/backend/dataall/cdkproxy/stacks/policies/sqs.py b/backend/dataall/cdkproxy/stacks/policies/sqs.py new file mode 100644 index 000000000..fcfe43dda --- /dev/null +++ b/backend/dataall/cdkproxy/stacks/policies/sqs.py @@ -0,0 +1,57 @@ +from .service_policy import ServicePolicy +from aws_cdk import aws_iam + + +class SQS(ServicePolicy): + """ + Class including all permissions needed to work with AWS SQS queues. + """ + def get_statements(self): + + statements = [ + aws_iam.PolicyStatement( + # sid='SQSRead', + effect=aws_iam.Effect.ALLOW, + actions=[ + "sqs:ListQueues", + ], + resources=["*"] + ), + aws_iam.PolicyStatement( + # sid='SQSCreate', + effect=aws_iam.Effect.ALLOW, + actions=[ + "sqs:CreateQueue", + "sqs:TagQueue", + ], + resources=[f"arn:aws:sqs:*:{self.account}:{self.resource_prefix}*"], + conditions={ + 'StringEquals': { + f'aws:RequestTag/{self.tag_key}': [self.tag_value] + } + } + ), + aws_iam.PolicyStatement( + # sid='SQSManageTeamQueue', + effect=aws_iam.Effect.ALLOW, + actions=[ + "sqs:GetQueueUrl", + "sqs:DeleteQueue", + "sqs:GetQueueAttributes", + "sqs:SetQueueAttributes", + "sqs:ListQueueTags", + "sqs:ListDeadLetterSourceQueues", + "sqs:SendMessage", + "sqs:ReceiveMessage", + "sqs:DeleteMessage", + "sqs:ChangeMessageVisibility", + ], + resources=[f"arn:aws:sqs:*:{self.account}:{self.resource_prefix}*"], + conditions={ + 'StringEquals': { + f'aws:ResourceTag/{self.tag_key}': [self.tag_value] + } + } + ) + ] + return statements diff --git a/backend/dataall/cdkproxy/stacks/policies/ssm.py b/backend/dataall/cdkproxy/stacks/policies/ssm.py new file mode 100644 index 000000000..8d86de43a --- /dev/null +++ b/backend/dataall/cdkproxy/stacks/policies/ssm.py @@ -0,0 +1,54 @@ +from .service_policy import ServicePolicy +from aws_cdk import aws_iam + + +class SSM(ServicePolicy): + """ + Class including all permissions needed to work with AWS SSM Parameter Store. + """ + + def get_statements(self): + statements = [ + aws_iam.PolicyStatement( + # sid="SSMReadAll", + effect=aws_iam.Effect.ALLOW, + actions=[ + "ssm:DescribeParameters", + ], + resources=["*"], + ), + aws_iam.PolicyStatement( + # sid='CreateTeamParameters', + effect=aws_iam.Effect.ALLOW, + actions=[ + 'ssm:AddTagsToResource' + ], + resources=[f"arn:aws:ssm:*:{self.account}:parameter/{self.resource_prefix}*"], + conditions={ + 'StringEquals': { + f'aws:RequestTag/{self.tag_key}': [self.tag_value] + } + }, + ), + aws_iam.PolicyStatement( + # sid='ManageTeamParameters', + effect=aws_iam.Effect.ALLOW, + actions=[ + 'ssm:PutParameter', + 'ssm:DeleteParameter', + 'ssm:GetParameterHistory', + 'ssm:GetParametersByPath', + 'ssm:GetParameters', + 'ssm:GetParameter', + 'ssm:DeleteParameters', + 'ssm:ListTagsForResource', + ], + resources=[f"arn:aws:ssm:*:{self.account}:parameter/{self.resource_prefix}*"], + conditions={ + 'StringEquals': { + f'aws:ResourceTag/{self.tag_key}': [self.tag_value] + } + }, + ) + ] + return statements diff --git a/backend/dataall/cdkproxy/stacks/policies/stepfunctions.py b/backend/dataall/cdkproxy/stacks/policies/stepfunctions.py index d8611b001..a80c29f46 100644 --- a/backend/dataall/cdkproxy/stacks/policies/stepfunctions.py +++ b/backend/dataall/cdkproxy/stacks/policies/stepfunctions.py @@ -1,30 +1,61 @@ -from aws_cdk import aws_iam as iam +from aws_cdk import aws_iam as aws_iam from .service_policy import ServicePolicy class StepFunctions(ServicePolicy): + """ + Class including all permissions needed to work with AWS Step Functions. + """ def get_statements(self): return [ - iam.PolicyStatement( + aws_iam.PolicyStatement( + # sid='ListMonitorStepFunctions', + effect=aws_iam.Effect.ALLOW, actions=[ - 'states:SendTaskSuccess', 'states:ListStateMachines', - 'states:SendTaskFailure', 'states:ListActivities', + 'states:SendTaskFailure', + 'states:SendTaskSuccess', 'states:SendTaskHeartbeat', ], resources=['*'], - effect=iam.Effect.ALLOW, ), - iam.PolicyStatement( - actions=['states:*'], + aws_iam.PolicyStatement( + # sid='CreateTeamStepFunctions', + effect=aws_iam.Effect.ALLOW, + actions=[ + 'states:CreateStateMachine', + 'states:UpdateStateMachine', + 'states:CreateActivity', + 'states:TagResource' + ], + resources=[ + f'arn:aws:states:{self.region}:{self.account}:stateMachine:{self.resource_prefix}*', + f'arn:aws:states:{self.region}:{self.account}:activity:{self.resource_prefix}*', + ], + conditions={ + 'StringEquals': { + f'aws:RequestTag/{self.tag_key}': [self.tag_value] + } + }, + ), + aws_iam.PolicyStatement( + # sid='ManageTeamStepFunctions', + effect=aws_iam.Effect.ALLOW, + actions=[ + 'states:Delete*', + 'states:Describe*', + 'states:Get*', + 'states:List*', + 'states:Start*', + 'states:StopExecution' + ], resources=[ - f'arn:aws:states:{self.region}:{self.account}:execution:{self.resource_prefix}:*', - f'arn:aws:states:{self.region}:{self.account}:activity:{self.resource_prefix}', - f'arn:aws:states:{self.region}:{self.account}:stateMachine:{self.resource_prefix}', + f'arn:aws:states:{self.region}:{self.account}:execution:{self.resource_prefix}*:*', + f'arn:aws:states:{self.region}:{self.account}:activity:{self.resource_prefix}*', + f'arn:aws:states:{self.region}:{self.account}:stateMachine:{self.resource_prefix}*' ], - effect=iam.Effect.ALLOW, conditions={ 'StringEquals': { f'aws:ResourceTag/{self.tag_key}': [self.tag_value] diff --git a/backend/dataall/cdkproxy/stacks/sagemakerstudio.py b/backend/dataall/cdkproxy/stacks/sagemakerstudio.py index 491a5359a..e1767b5cd 100644 --- a/backend/dataall/cdkproxy/stacks/sagemakerstudio.py +++ b/backend/dataall/cdkproxy/stacks/sagemakerstudio.py @@ -31,7 +31,7 @@ def __init__( self, stack, id, - environment: models.Environment + environment: models.Environment, ): self.stack = stack self.id = id @@ -146,14 +146,48 @@ def create_sagemaker_domain_resources(self, sagemaker_principals): 'SagemakerDomainKmsKey', alias='SagemakerStudioDomain', enable_key_rotation=True, + admins=[ + iam.ArnPrincipal(self.environment.CDKRoleArn) + ], policy=iam.PolicyDocument( assign_sids=True, statements=[ iam.PolicyStatement( - resources=['*'], + actions=[ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:CreateGrant" + ], + effect=iam.Effect.ALLOW, + principals=[ + sagemaker_domain_role, + iam.ArnPrincipal(self.environment.CDKRoleArn) + ] + sagemaker_principals, + resources=["*"], + conditions={ + "StringEquals": { + "kms:ViaService": [ + f"sagemaker.{self.environment.region}.amazonaws.com", + f"elasticfilesystem.{self.environment.region}.amazonaws.com", + f"ec2.{self.environment.region}.amazonaws.com", + f"s3.{self.environment.region}.amazonaws.com" + ] + } + } + ), + iam.PolicyStatement( + actions=[ + "kms:DescribeKey", + "kms:List*", + "kms:GetKeyPolicy", + ], effect=iam.Effect.ALLOW, - principals=[iam.AccountPrincipal(account_id=self.environment.AwsAccountId), sagemaker_domain_role] + sagemaker_principals, - actions=['kms:*'], + principals=[ + sagemaker_domain_role, + ] + sagemaker_principals, + resources=["*"], ) ], ), diff --git a/backend/dataall/db/api/dataset.py b/backend/dataall/db/api/dataset.py index a42b676ec..f913f7e3e 100644 --- a/backend/dataall/db/api/dataset.py +++ b/backend/dataall/db/api/dataset.py @@ -149,8 +149,7 @@ def _set_dataset_aws_resources(dataset: models.Dataset, data, environment): ).build_compliant_name() dataset.GlueDatabaseName = data.get('glueDatabaseName') or glue_db_name - kms_alias = bucket_name - dataset.KmsAlias = data.get('KmsKeyId') or kms_alias + dataset.KmsAlias = bucket_name iam_role_name = NamingConventionService( target_uri=dataset.datasetUri, @@ -170,13 +169,20 @@ def _set_dataset_aws_resources(dataset: models.Dataset, data, environment): dataset.IAMDatasetAdminRoleArn = iam_role_arn dataset.IAMDatasetAdminUserArn = iam_role_arn - dataset.GlueCrawlerName = f'{dataset.S3BucketName}-{dataset.datasetUri}-crawler' - dataset.GlueProfilingJobName = f'{dataset.S3BucketName}-{dataset.datasetUri}-profiler' + glue_etl_basename = NamingConventionService( + target_uri=dataset.datasetUri, + target_label=dataset.label, + pattern=NamingConventionPattern.GLUE_ETL, + resource_prefix=environment.resourcePrefix, + ).build_compliant_name() + + dataset.GlueCrawlerName = f"{glue_etl_basename}-crawler" + dataset.GlueProfilingJobName = f"{glue_etl_basename}-profiler" dataset.GlueProfilingTriggerSchedule = None - dataset.GlueProfilingTriggerName = f'{dataset.S3BucketName}-{dataset.datasetUri}-trigger' - dataset.GlueDataQualityJobName = f'{dataset.S3BucketName}-{dataset.datasetUri}-dataquality' + dataset.GlueProfilingTriggerName = f"{glue_etl_basename}-trigger" + dataset.GlueDataQualityJobName = f"{glue_etl_basename}-dataquality" dataset.GlueDataQualitySchedule = None - dataset.GlueDataQualityTriggerName = f'{dataset.S3BucketName}-{dataset.datasetUri}-dqtrigger' + dataset.GlueDataQualityTriggerName = f"{glue_etl_basename}-dqtrigger" return dataset @staticmethod @@ -326,6 +332,9 @@ def update_dataset( for k in data.keys(): if k != 'stewards': setattr(dataset, k, data.get(k)) + if data.get('KmsAlias') not in ["Undefined"]: + dataset.KmsAlias = "SSE-S3" if data.get('KmsAlias') == "" else data.get('KmsAlias') + dataset.importedKmsKey = False if data.get('KmsAlias') == "" else True if data.get('stewards') and data.get('stewards') != dataset.stewards: if data.get('stewards') != dataset.SamlAdminGroupName: Dataset.transfer_stewardship_to_new_stewards( @@ -358,6 +367,26 @@ def update_dataset( @staticmethod def transfer_stewardship_to_owners(session, dataset): + # Remove Steward Resource Policy on Dataset + env = Environment.get_environment_by_uri(session, dataset.environmentUri) + if dataset.stewards != env.SamlGroupName: + ResourcePolicy.delete_resource_policy( + session=session, + group=dataset.stewards, + resource_uri=dataset.datasetUri, + ) + + # Remove Steward Resource Policy on Dataset Tables + dataset_tables = [t.tableUri for t in Dataset.get_dataset_tables(session, dataset.datasetUri)] + for tableUri in dataset_tables: + if dataset.stewards != env.SamlGroupName: + ResourcePolicy.delete_resource_policy( + session=session, + group=dataset.stewards, + resource_uri=tableUri, + ) + + # Remove Steward Resource Policy on Dataset Share Objects dataset_shares = ( session.query(models.ShareObject) .filter(models.ShareObject.datasetUri == dataset.datasetUri) @@ -365,19 +394,17 @@ def transfer_stewardship_to_owners(session, dataset): ) if dataset_shares: for share in dataset_shares: - ResourcePolicy.attach_resource_policy( + ResourcePolicy.delete_resource_policy( session=session, - group=dataset.SamlAdminGroupName, - permissions=permissions.SHARE_OBJECT_APPROVER, + group=dataset.stewards, resource_uri=share.shareUri, - resource_type=models.ShareObject.__name__, ) return dataset @staticmethod def transfer_stewardship_to_new_stewards(session, dataset, new_stewards): env = Environment.get_environment_by_uri(session, dataset.environmentUri) - if dataset.stewards != env.SamlGroupName: + if dataset.stewards != dataset.SamlAdminGroupName: ResourcePolicy.delete_resource_policy( session=session, group=dataset.stewards, @@ -393,7 +420,7 @@ def transfer_stewardship_to_new_stewards(session, dataset, new_stewards): dataset_tables = [t.tableUri for t in Dataset.get_dataset_tables(session, dataset.datasetUri)] for tableUri in dataset_tables: - if dataset.stewards != env.SamlGroupName: + if dataset.stewards != dataset.SamlAdminGroupName: ResourcePolicy.delete_resource_policy( session=session, group=dataset.stewards, @@ -421,11 +448,12 @@ def transfer_stewardship_to_new_stewards(session, dataset, new_stewards): resource_uri=share.shareUri, resource_type=models.ShareObject.__name__, ) - ResourcePolicy.delete_resource_policy( - session=session, - group=dataset.stewards, - resource_uri=share.shareUri, - ) + if dataset.stewards != dataset.SamlAdminGroupName: + ResourcePolicy.delete_resource_policy( + session=session, + group=dataset.stewards, + resource_uri=share.shareUri, + ) return dataset @staticmethod @@ -520,11 +548,19 @@ def list_dataset_shares(session, dataset_uri) -> [models.ShareObject]: @staticmethod def list_dataset_shares_with_existing_shared_items(session, dataset_uri) -> [models.ShareObject]: - query = session.query(models.ShareObject).filter( - and_( - models.ShareObject.datasetUri == dataset_uri, - models.ShareObject.deleted.is_(None), - models.ShareObject.existingSharedItems.is_(True), + share_item_shared_states = api.ShareItemSM.get_share_item_shared_states() + query = ( + session.query(models.ShareObject) + .outerjoin( + models.ShareObjectItem, + models.ShareObjectItem.shareUri == models.ShareObject.shareUri + ) + .filter( + and_( + models.ShareObject.datasetUri == dataset_uri, + models.ShareObject.deleted.is_(None), + models.ShareObjectItem.status.in_(share_item_shared_states), + ) ) ) return query.all() @@ -568,23 +604,36 @@ def delete_dataset( @staticmethod def _delete_dataset_shares_with_no_shared_items(session, dataset_uri): - share_objects = ( + share_item_shared_states = api.ShareItemSM.get_share_item_shared_states() + shares = ( session.query(models.ShareObject) + .outerjoin( + models.ShareObjectItem, + models.ShareObjectItem.shareUri == models.ShareObject.shareUri + ) .filter( and_( models.ShareObject.datasetUri == dataset_uri, - models.ShareObject.existingSharedItems.is_(False), + models.ShareObjectItem.status.notin_(share_item_shared_states), ) ) .all() ) - for share in share_objects: - ( + for share in shares: + share_items = ( session.query(models.ShareObjectItem) .filter(models.ShareObjectItem.shareUri == share.shareUri) - .delete() + .all() + ) + for item in share_items: + session.delete(item) + + share_obj = ( + session.query(models.ShareObject) + .filter(models.ShareObject.shareUri == share.shareUri) + .first() ) - session.delete(share) + session.delete(share_obj) @staticmethod def _delete_dataset_term_links(session, uri): diff --git a/backend/dataall/db/api/share_object.py b/backend/dataall/db/api/share_object.py index ff1c426d7..b41548a62 100644 --- a/backend/dataall/db/api/share_object.py +++ b/backend/dataall/db/api/share_object.py @@ -343,6 +343,7 @@ def create_share_object( groupUri = data['groupUri'] itemUri = data.get('itemUri') itemType = data.get('itemType') + requestPurpose = data.get('requestPurpose') dataset: models.Dataset = data.get( 'dataset', api.Dataset.get_dataset_by_uri(session, datasetUri) @@ -411,6 +412,7 @@ def create_share_object( principalType=principalType, principalIAMRoleName=principalIAMRoleName, status=ShareObjectStatus.Draft.value, + requestPurpose=requestPurpose ) session.add(share) session.commit() @@ -470,8 +472,7 @@ def create_share_object( # Attaching REQUESTER permissions to: # requester group (groupUri) - # dataset.SamlAdminGroupName - # environment.SamlGroupName + # environment.SamlGroupName (if not dataset admins) ResourcePolicy.attach_resource_policy( session=session, group=groupUri, @@ -479,30 +480,24 @@ def create_share_object( resource_uri=share.shareUri, resource_type=models.ShareObject.__name__, ) + + # Attaching APPROVER permissions to: + # dataset.stewards (includes the dataset Admins) ResourcePolicy.attach_resource_policy( session=session, group=dataset.SamlAdminGroupName, - permissions=permissions.SHARE_OBJECT_REQUESTER, + permissions=permissions.SHARE_OBJECT_APPROVER, resource_uri=share.shareUri, resource_type=models.ShareObject.__name__, ) - if dataset.SamlAdminGroupName != environment.SamlGroupName: + if dataset.stewards != dataset.SamlAdminGroupName: ResourcePolicy.attach_resource_policy( session=session, - group=environment.SamlGroupName, - permissions=permissions.SHARE_OBJECT_REQUESTER, + group=dataset.stewards, + permissions=permissions.SHARE_OBJECT_APPROVER, resource_uri=share.shareUri, resource_type=models.ShareObject.__name__, ) - # Attaching REQUESTER permissions to: - # dataset.stewards (includes the dataset Admins) - ResourcePolicy.attach_resource_policy( - session=session, - group=dataset.stewards, - permissions=permissions.SHARE_OBJECT_APPROVER, - resource_uri=share.shareUri, - resource_type=models.ShareObject.__name__, - ) return share @staticmethod @@ -607,9 +602,42 @@ def approve_share_object( resource_type=models.DatasetTable.__name__, ) + share.rejectPurpose = "" + session.commit() + api.Notification.notify_share_object_approval(session, username, dataset, share) return share + @staticmethod + @has_resource_perm(permissions.SUBMIT_SHARE_OBJECT) + def update_share_request_purpose( + session, + username: str, + groups: [str], + uri: str, + data: dict = None, + check_perm: bool = False, + ) -> models.ShareObject: + share = ShareObject.get_share_by_uri(session, uri) + share.requestPurpose = data.get("requestPurpose") + session.commit() + return True + + @staticmethod + @has_resource_perm(permissions.REJECT_SHARE_OBJECT) + def update_share_reject_purpose( + session, + username: str, + groups: [str], + uri: str, + data: dict = None, + check_perm: bool = False, + ) -> models.ShareObject: + share = ShareObject.get_share_by_uri(session, uri) + share.rejectPurpose = data.get("rejectPurpose") + session.commit() + return True + @staticmethod @has_resource_perm(permissions.REJECT_SHARE_OBJECT) def reject_share_object( @@ -624,7 +652,6 @@ def reject_share_object( share = ShareObject.get_share_by_uri(session, uri) dataset = api.Dataset.get_dataset_by_uri(session, share.datasetUri) share_items_states = ShareObject.get_share_items_states(session, uri) - Share_SM = ShareObjectSM(share.status) new_share_state = Share_SM.run_transition(ShareObjectActions.Reject.value) @@ -640,6 +667,11 @@ def reject_share_object( group=share.groupUri, resource_uri=dataset.datasetUri, ) + + # Update Reject Purpose + share.rejectPurpose = data.get("rejectPurpose") + session.commit() + api.Notification.notify_share_object_rejection(session, username, dataset, share) return share @@ -1031,6 +1063,7 @@ def list_user_received_share_requests( f'{{{username}}}' ), models.Dataset.stewards.in_(groups), + models.Dataset.SamlAdminGroupName.in_(groups), ) ) ) diff --git a/backend/dataall/db/connection.py b/backend/dataall/db/connection.py index 77a031461..8b5016b50 100644 --- a/backend/dataall/db/connection.py +++ b/backend/dataall/db/connection.py @@ -29,19 +29,19 @@ def __init__(self, dbconfig: DbConfig): dbconfig.url, echo=False, pool_size=1, - connect_args={'options': f"-csearch_path={dbconfig.params['schema']}"}, + connect_args={'options': f"-csearch_path={dbconfig.schema}"}, ) try: if not self.engine.dialect.has_schema( - self.engine, dbconfig.params['schema'] + self.engine, dbconfig.schema ): log.info( - f"Schema not found - init the schema {dbconfig.params['schema']}" + f"Schema not found - init the schema {dbconfig.schema}" ) self.engine.execute( - sqlalchemy.schema.CreateSchema(dbconfig.params['schema']) + sqlalchemy.schema.CreateSchema(dbconfig.schema) ) - log.info('-- Using schema: %s --', dbconfig.params['schema']) + log.info('-- Using schema: %s --', dbconfig.schema) except Exception as e: log.error(f'Could not create schema: {e}') @@ -124,10 +124,12 @@ def get_engine(envname=ENVNAME): creds = json.loads(db_credentials_string['SecretString']) user = creds['username'] pwd = creds['password'] + host = param_store.get_parameter(env=envname, path='aurora/hostname') + database = param_store.get_parameter(env=envname, path='aurora/db') + db_params = { - 'host': param_store.get_parameter(env=envname, path='aurora/hostname'), - 'port': param_store.get_parameter(env=envname, path='aurora/port'), - 'db': param_store.get_parameter(env=envname, path='aurora/db'), + 'host': host, + 'db': database, 'user': user, 'pwd': pwd, 'schema': schema, @@ -136,7 +138,6 @@ def get_engine(envname=ENVNAME): hostname = 'db' if envname == 'dkrcompose' else 'localhost' db_params = { 'host': hostname, - 'port': '5432', 'db': 'dataall', 'user': 'postgres', 'pwd': 'docker', diff --git a/backend/dataall/db/dbconfig.py b/backend/dataall/db/dbconfig.py index ca20268e9..667ec9513 100644 --- a/backend/dataall/db/dbconfig.py +++ b/backend/dataall/db/dbconfig.py @@ -1,23 +1,59 @@ +import os +import re + +_SANITIZE_WORD_REGEX = r"[^\w]" # A-Za-z0-9_ +_SANITIZE_HOST_REGEX = r"[^\w.-]" +_SANITIZE_PWD_REGEX = r"[\"\s%+~`#$&*()|\[\]{}:;<>?!'/]+" +_AURORA_HOST_SUFFIX = "rds.amazonaws.com" +_POSTGRES_MAX_LEN = 63 +_MAX_HOST_LENGTH = 253 + +_envname = os.getenv('envname', 'local') + + class DbConfig: - def __init__(self, **kwargs): - self.params = kwargs - self.url = f"postgresql+pygresql://{self.params['user']}:{self.params['pwd']}@{self.params['host']}/{self.params['db']}" + def __init__(self, user: str, pwd: str, host: str, db: str, schema: str): + for param in (user, db, schema): + if len(param) > _POSTGRES_MAX_LEN: + raise ValueError( + f"PostgreSQL doesn't allow values more than 63 characters" + f" parameters {user}, {db}, {schema}" + ) + + if len(host) > _MAX_HOST_LENGTH: + raise ValueError(f"Hostname is too long: {host}") + + if _envname not in ['local', 'pytest', 'dkrcompose'] and not host.lower().endswith(_AURORA_HOST_SUFFIX): + raise ValueError(f"Unknown host {host} for the rds") + + self.user = self._sanitize_and_compare(_SANITIZE_WORD_REGEX, user, "username") + self.host = self._sanitize_and_compare(_SANITIZE_HOST_REGEX, host, "host") + self.db = self._sanitize_and_compare(_SANITIZE_WORD_REGEX, db, "database name") + self.schema = self._sanitize_and_compare(_SANITIZE_WORD_REGEX, schema, "schema") + pwd = self._sanitize_and_compare(_SANITIZE_PWD_REGEX, pwd, "password") + self.url = f"postgresql+pygresql://{self.user}:{pwd}@{self.host}/{self.db}" def __str__(self): - lines = [] - lines.append(' DbConfig >') + lines = [' DbConfig >'] hr = ' '.join(['+', ''.ljust(10, '-'), '+', ''.ljust(65, '-'), '+']) lines.append(hr) header = ' '.join(['+', 'Db Param'.ljust(10), ' ', 'Value'.ljust(65), '+']) lines.append(header) hr = ' '.join(['+', ''.ljust(10, '-'), '+', ''.ljust(65, '-'), '+']) lines.append(hr) - for k in self.params: - v = self.params[k] - if k == 'pwd': - v = '*' * len(self.params[k]) - lines.append(' '.join(['|', k.ljust(10), '|', v.ljust(65), '|'])) + lines.append(' '.join(['|', "host".ljust(10), '|', self.host.ljust(65), '|'])) + lines.append(' '.join(['|', "db".ljust(10), '|', self.db.ljust(65), '|'])) + lines.append(' '.join(['|', "user".ljust(10), '|', self.user.ljust(65), '|'])) + lines.append(' '.join(['|', "pwd".ljust(10), '|', "*****".ljust(65), '|'])) hr = ' '.join(['+', ''.ljust(10, '-'), '+', ''.ljust(65, '-'), '+']) lines.append(hr) return '\n'.join(lines) + + @staticmethod + def _sanitize_and_compare(regex, string: str, param_name) -> str: + sanitized = re.sub(regex, "", string) + if sanitized != string: + raise ValueError(f"Can't create a database connection. The {param_name} parameter has invalid symbols." + f" The sanitized string length: {len(sanitized)} < original : {len(string)}") + return sanitized diff --git a/backend/dataall/db/models/ShareObject.py b/backend/dataall/db/models/ShareObject.py index 220099fd5..403de15a7 100644 --- a/backend/dataall/db/models/ShareObject.py +++ b/backend/dataall/db/models/ShareObject.py @@ -33,5 +33,7 @@ class ShareObject(Base): updated = Column(DateTime, onupdate=datetime.now) deleted = Column(DateTime) confirmed = Column(Boolean, default=False) + requestPurpose = Column(String, nullable=True) + rejectPurpose = Column(String, nullable=True) userRoleForShareObject = query_expression() existingSharedItems = query_expression() diff --git a/backend/dataall/db/permissions.py b/backend/dataall/db/permissions.py index cf921a30c..1f79445ea 100644 --- a/backend/dataall/db/permissions.py +++ b/backend/dataall/db/permissions.py @@ -172,7 +172,6 @@ SHARE_OBJECT_APPROVER = [ ADD_ITEM, REMOVE_ITEM, - SUBMIT_SHARE_OBJECT, APPROVE_SHARE_OBJECT, REJECT_SHARE_OBJECT, DELETE_SHARE_OBJECT, diff --git a/backend/dataall/tasks/data_sharing/share_managers/s3_share_manager.py b/backend/dataall/tasks/data_sharing/share_managers/s3_share_manager.py index 1323770a4..0b1a15e87 100644 --- a/backend/dataall/tasks/data_sharing/share_managers/s3_share_manager.py +++ b/backend/dataall/tasks/data_sharing/share_managers/s3_share_manager.py @@ -349,7 +349,7 @@ def delete_target_role_access_policy( ) access_point_name = S3ShareManager.build_access_point_name(share) existing_policy = IAM.get_role_policy( - dataset.AwsAccountId, + target_environment.AwsAccountId, share.principalIAMRoleName, "targetDatasetAccessControlPolicy", ) @@ -388,7 +388,7 @@ def delete_dataset_bucket_key_policy( target_requester_id = SessionHelper.get_role_id(target_environment.AwsAccountId, share.principalIAMRoleName) if existing_policy and f'{target_requester_id}:*' in existing_policy: policy = json.loads(existing_policy) - policy["Statement"] = [item for item in policy["Statement"] if item["Sid"] != f"{target_requester_id}"] + policy["Statement"] = [item for item in policy["Statement"] if item.get("Sid", None) != f"{target_requester_id}"] KMS.put_key_policy( dataset.AwsAccountId, dataset.region, diff --git a/backend/dataall/tasks/subscriptions/sqs_poller.py b/backend/dataall/tasks/subscriptions/sqs_poller.py index a6415d903..47b38abbe 100644 --- a/backend/dataall/tasks/subscriptions/sqs_poller.py +++ b/backend/dataall/tasks/subscriptions/sqs_poller.py @@ -23,7 +23,11 @@ def poll_queues(queues): for queue in queues: - sqs = boto3.client('sqs', region_name=queue['region']) + sqs = boto3.client( + 'sqs', + region_name=queue['region'], + endpoint_url=f"https://sqs.{queue['region']}.amazonaws.com" + ) try: response = sqs.receive_message( diff --git a/backend/dataall/tasks/tables_syncer.py b/backend/dataall/tasks/tables_syncer.py index 04bafdfa5..a441ff197 100644 --- a/backend/dataall/tasks/tables_syncer.py +++ b/backend/dataall/tasks/tables_syncer.py @@ -80,7 +80,6 @@ def sync_tables(engine, es=None): table, principals=[ SessionHelper.get_delegation_role_arn(env.AwsAccountId), - env.EnvironmentDefaultIAMRoleArn, env_group.environmentIAMRoleArn, ], ) diff --git a/backend/dataall/utils/naming_convention.py b/backend/dataall/utils/naming_convention.py index def67e40c..abe55c649 100644 --- a/backend/dataall/utils/naming_convention.py +++ b/backend/dataall/utils/naming_convention.py @@ -10,6 +10,7 @@ def __str__(self): S3 = {'regex': '[^a-zA-Z0-9-]', 'separator': '-', 'max_length': 63} IAM = {'regex': '[^a-zA-Z0-9-_]', 'separator': '-', 'max_length': 63} GLUE = {'regex': '[^a-zA-Z0-9_]', 'separator': '_', 'max_length': 63} + GLUE_ETL = {'regex': '[^a-zA-Z0-9-]', 'separator': '-', 'max_length': 52} NOTEBOOK = {'regex': '[^a-zA-Z0-9-]', 'separator': '-', 'max_length': 63} DEFAULT = {'regex': '[^a-zA-Z0-9-_]', 'separator': '-', 'max_length': 63} @@ -40,6 +41,9 @@ def build_compliant_name(self) -> str: elif self.service == NamingConventionPattern.GLUE: regex = self.service.GLUE.value['regex'] return self.build_glue_compliant_name(regex) + elif self.service == NamingConventionPattern.GLUE_ETL: + regex = self.service.GLUE_ETL.value['regex'] + return self.build_glue_etl_compliant_name(regex) elif self.service == NamingConventionPattern.NOTEBOOK: regex = self.service.NOTEBOOK.value['regex'] return self.build_notebook_compliant_name(regex) @@ -56,6 +60,9 @@ def build_iam_compliant_name(self, regex) -> str: def build_glue_compliant_name(self, regex) -> str: return f"{slugify(self.resource_prefix + '-' + self.target_label[:(self.service.GLUE.value['max_length'] - len(self.resource_prefix + self.target_uri))] + '-' + self.target_uri, regex_pattern=fr'{regex}', separator=self.service.GLUE.value['separator'], lowercase=True)}" + def build_glue_etl_compliant_name(self, regex) -> str: + return f"{slugify(self.resource_prefix + '-' + self.target_label[:(self.service.GLUE_ETL.value['max_length'] - len(self.resource_prefix + self.target_uri))] + '-' + self.target_uri, regex_pattern=fr'{regex}', separator=self.service.GLUE_ETL.value['separator'], lowercase=True)}" + def build_notebook_compliant_name(self, regex) -> str: return f"{slugify(self.resource_prefix + '-' + self.target_label[:(self.service.NOTEBOOK.value['max_length'] - len(self.resource_prefix +self.target_uri))] + '-' + self.target_uri, regex_pattern=fr'{regex}', separator=self.service.NOTEBOOK.value['separator'], lowercase=True)}" diff --git a/backend/docker/prod/ecs/Dockerfile b/backend/docker/prod/ecs/Dockerfile index b272902af..334fc043a 100644 --- a/backend/docker/prod/ecs/Dockerfile +++ b/backend/docker/prod/ecs/Dockerfile @@ -5,57 +5,58 @@ ARG NVM_VERSION=v0.37.2 ARG DEEQU_VERSION=2.0.0-spark-3.1 ARG PYTHON_VERSION=python3.8 -RUN yum upgrade -y;\ - find /var/tmp -name "*.rpm" -print -delete ;\ - find /tmp -name "*.rpm" -print -delete ;\ - yum autoremove -y; \ - yum clean packages; yum clean headers; yum clean metadata; yum clean all; rm -rfv /var/cache/yum - -RUN yum -y install shadow-utils wget -RUN yum -y install openssl-devel bzip2-devel libffi-devel postgresql-devel gcc unzip tar gzip -RUN amazon-linux-extras install $PYTHON_VERSION -RUN yum -y install python38-devel -RUN yum -y install git - -RUN /bin/bash -c "ln -s /usr/bin/${PYTHON_VERSION} /usr/bin/python3" - -RUN curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" -RUN unzip awscliv2.zip -RUN ./aws/install - +# Installing libraries +RUN yum upgrade -y \ + && find /var/tmp -name "*.rpm" -print -delete \ + && find /tmp -name "*.rpm" -print -delete \ + && yum autoremove -y \ + && yum clean all \ + && rm -rfv /var/cache/yum \ + && yum install -y \ + shadow-utils wget openssl-devel bzip2-devel libffi-devel \ + postgresql-devel gcc unzip tar gzip \ + && amazon-linux-extras install $PYTHON_VERSION \ + && yum install -y python38-devel git \ + && /bin/bash -c "ln -s /usr/bin/${PYTHON_VERSION} /usr/bin/python3" \ + && curl https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip -o /tmp/awscliv2.zip \ + && unzip -q /tmp/awscliv2.zip -d /opt \ + && /opt/aws/install --update -i /usr/local/aws-cli -b /usr/local/bin \ + && rm /tmp/awscliv2.zip \ + && rm -rf /opt/aws \ + && aws --version + +# Configuring path RUN touch ~/.bashrc -RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/$NVM_VERSION/install.sh | bash - -RUN /bin/bash -c ". ~/.nvm/nvm.sh && \ - nvm install $NODE_VERSION && nvm use $NODE_VERSION && \ - npm install -g aws-cdk && \ - nvm alias default node && nvm cache clear" - -RUN echo export PATH="\ -/root/.nvm/versions/node/${NODE_VERSION}/bin:\ -$(${PYTHON_VERSION} -m site --user-base)/bin:\ -$(python3 -m site --user-base)/bin:\ -$PATH" >> ~/.bashrc && \ - echo "nvm use ${NODE_VERSION} 1> /dev/null" >> ~/.bashrc -RUN /bin/bash -c ". ~/.nvm/nvm.sh && cdk --version" +# Configuring Node and CDK +RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/$NVM_VERSION/install.sh | bash \ + && /bin/bash -c ". ~/.nvm/nvm.sh && \ + nvm install $NODE_VERSION && nvm use $NODE_VERSION && \ + npm install -g aws-cdk && \ + nvm alias default node && nvm cache clear" \ + && echo export PATH="\ + /root/.nvm/versions/node/${NODE_VERSION}/bin:\ + $(${PYTHON_VERSION} -m site --user-base)/bin:\ + $(python3 -m site --user-base)/bin:\ + $PATH" >> ~/.bashrc && \ + echo "nvm use ${NODE_VERSION} 1> /dev/null" >> ~/.bashrc \ + && /bin/bash -c ". ~/.nvm/nvm.sh && cdk --version" RUN $PYTHON_VERSION -m pip install -U pip +# App specific ADD backend/requirements.txt /dh.requirements.txt ADD backend/dataall/cdkproxy/requirements.txt /cdk.requirements.txt -RUN /bin/bash -c "pip3.8 install -r /dh.requirements.txt" -RUN /bin/bash -c "pip3.8 install -r /cdk.requirements.txt" +RUN /bin/bash -c "pip3.8 install -r /dh.requirements.txt" \ + && /bin/bash -c "pip3.8 install -r /cdk.requirements.txt" ADD backend/dataall /dataall -ADD backend/blueprints /blueprints +VOLUME ["/dataall"] ADD backend/cdkproxymain.py /cdkproxymain.py RUN mkdir -p dataall/cdkproxy/assets/glueprofilingjob/jars -RUN mkdir -p blueprints/ml_data_pipeline/engine/glue/jars -RUN curl https://repo1.maven.org/maven2/com/amazon/deequ/deequ/$DEEQU_VERSION/deequ-$DEEQU_VERSION.jar --output dataall/cdkproxy/assets/glueprofilingjob/jars/deequ-$DEEQU_VERSION.jar -RUN cp -f dataall/cdkproxy/assets/glueprofilingjob/jars/deequ-$DEEQU_VERSION.jar blueprints/ml_data_pipeline/engine/glue/jars/deequ-$DEEQU_VERSION.jar +ADD https://repo1.maven.org/maven2/com/amazon/deequ/deequ/$DEEQU_VERSION/deequ-$DEEQU_VERSION.jar /dataall/cdkproxy/assets/glueprofilingjob/jars/ WORKDIR / diff --git a/backend/migrations/versions/72b8a90b6ee8__share_request_purpose.py b/backend/migrations/versions/72b8a90b6ee8__share_request_purpose.py new file mode 100644 index 000000000..1b358b11d --- /dev/null +++ b/backend/migrations/versions/72b8a90b6ee8__share_request_purpose.py @@ -0,0 +1,136 @@ +"""_share_request_purpose + +Revision ID: 72b8a90b6ee8 +Revises: 509997f0a51e +Create Date: 2023-06-05 12:28:56.221364 + +""" +from alembic import op +from sqlalchemy import orm, Column, String, and_ +from sqlalchemy.ext.declarative import declarative_base + +from dataall.db import api, models, permissions + +# revision identifiers, used by Alembic. +revision = '72b8a90b6ee8' +down_revision = '509997f0a51e' +branch_labels = None +depends_on = None + +Base = declarative_base() + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('share_object', Column('requestPurpose', String(), nullable=True)) + op.add_column('share_object', Column('rejectPurpose', String(), nullable=True)) + + # ### Fix Permissions Set for Share Object Approvers + Requesters + try: + bind = op.get_bind() + session = orm.Session(bind=bind) + + print('Getting all Share Objects...') + shares: [models.ShareObject] = session.query(models.ShareObject).all() + for share in shares: + dataset = api.Dataset.get_dataset_by_uri(session, share.datasetUri) + environment = api.Environment.get_environment_by_uri(session, share.environmentUri) + + # Env Admins + # Delete Share Object Permissions on Share Env Admin if Not Share Requester Group + if share.groupUri != environment.SamlGroupName: + api.ResourcePolicy.delete_resource_policy( + session=session, + group=environment.SamlGroupName, + resource_uri=share.shareUri, + ) + print(f"Delete SHARE_OBJECT Permissions for Env Owner {environment.SamlGroupName} on Share {share.shareUri}") + + # Dataset Admins + # Delete and Recreate Dataset Share Object Permissions to be Share Object Approver Permission Set + api.ResourcePolicy.delete_resource_policy( + session=session, + group=dataset.SamlAdminGroupName, + resource_uri=share.shareUri, + ) + api.ResourcePolicy.attach_resource_policy( + session=session, + group=dataset.SamlAdminGroupName, + permissions=permissions.SHARE_OBJECT_APPROVER, + resource_uri=share.shareUri, + resource_type=models.ShareObject.__name__, + ) + print(f"Recreated SHARE_OBJECT_APPROVER Permissions for Dataset Owner {dataset.SamlAdminGroupName} on Share {share.shareUri}") + + # Dataset Stewards + # Delete and Recreate Dataset Share Object Permissions to be Share Object Approver Permission Set + if dataset.SamlAdminGroupName != dataset.stewards: + api.ResourcePolicy.delete_resource_policy( + session=session, + group=dataset.stewards, + resource_uri=share.shareUri, + ) + api.ResourcePolicy.attach_resource_policy( + session=session, + group=dataset.stewards, + permissions=permissions.SHARE_OBJECT_APPROVER, + resource_uri=share.shareUri, + resource_type=models.ShareObject.__name__, + ) + print(f"Recreated SHARE_OBJECT_APPROVER Permissions for Dataset Steward {dataset.stewards} on Share {share.shareUri}") + + except Exception as e: + print(e) + print(f'Failed to update share object approver permissions due to: {e}') + raise e + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + try: + bind = op.get_bind() + session = orm.Session(bind=bind) + + print('Getting all Share Objects...') + shares: [models.ShareObject] = session.query(models.ShareObject).all() + for share in shares: + dataset = api.Dataset.get_dataset_by_uri(session, share.datasetUri) + environment = api.Environment.get_environment_by_uri(session, share.environmentUri) + + # Env Admins + # Add SHARE_OBJECT_REQUESTER to Env Admin Group + api.ResourcePolicy.attach_resource_policy( + session=session, + group=environment.SamlGroupName, + permissions=permissions.SHARE_OBJECT_REQUESTER, + resource_uri=share.shareUri, + resource_type=models.ShareObject.__name__, + ) + print(f"Adding SHARE_OBJECT_REQUESTER Permissions for Share Env Admin {environment.SamlGroupName} on Share {share.shareUri}") + + # Dataset Admins + # Remove SHARE_OBJECT_APPROVER Permissions if Exists Separate from Stewards(i.e. if steward != owner) + # Add SHARE_OBJECT_REQUESTER Permissions to Dataset Admin Group + if dataset.SamlAdminGroupName != dataset.stewards: + api.ResourcePolicy.delete_resource_policy( + session=session, + group=dataset.SamlAdminGroupName, + resource_uri=share.shareUri, + ) + api.ResourcePolicy.attach_resource_policy( + session=session, + group=dataset.SamlAdminGroupName, + permissions=permissions.SHARE_OBJECT_REQUESTER, + resource_uri=share.shareUri, + resource_type=models.ShareObject.__name__, + ) + print(f"Adding SHARE_OBJECT_REQUESTER Permissions for Dataset Owner {dataset.SamlAdminGroupName} on Share {share.shareUri}") + except Exception as e: + print(e) + print(f'Failed to update share object approver permissions due to: {e}') + raise e + + op.drop_column('share_object', 'requestPurpose') + op.drop_column('share_object', 'rejectPurpose') + # ### end Alembic commands ### diff --git a/backend/migrations/versions/b1cdc0dc987a_fix_template_column_in_table.py b/backend/migrations/versions/b1cdc0dc987a_fix_template_column_in_table.py index 5c92d6ca5..7c134012e 100644 --- a/backend/migrations/versions/b1cdc0dc987a_fix_template_column_in_table.py +++ b/backend/migrations/versions/b1cdc0dc987a_fix_template_column_in_table.py @@ -7,7 +7,9 @@ """ from alembic import op import sqlalchemy as sa +from sqlalchemy import orm, Column, String from sqlalchemy.dialects import postgresql +from sqlalchemy.ext.declarative import declarative_base # revision identifiers, used by Alembic. revision = 'b1cdc0dc987a' @@ -15,13 +17,51 @@ branch_labels = None depends_on = None +Base = declarative_base() + + +class DataPipeline(Base): + __tablename__ = 'datapipeline' + DataPipelineUri = Column( + String, nullable=False, primary_key=True + ) + devStrategy = Column(String, nullable=True) + devStages = Column(postgresql.ARRAY(String), nullable=True) + def upgrade(): # ### commands auto generated by Alembic - please adjust! ### + # Modify column types + print("Upgrade devStages and devStrategy column types. Updating nullable to True...") op.add_column( 'datapipeline', sa.Column('template', sa.String(), nullable=True) ) + op.alter_column( + 'datapipeline', + 'devStages', + existing_type=postgresql.ARRAY(sa.VARCHAR()), + nullable=True + ) + op.alter_column( + 'datapipeline', + 'devStrategy', + existing_type=sa.VARCHAR(), + nullable=True + ) + print("Backfilling values for devStages and devStrategy...") + # Backfill values + bind = op.get_bind() + session = orm.Session(bind=bind) + session.query(DataPipeline).filter(DataPipeline.devStrategy is None).update( + {DataPipeline.devStrategy: 'gitflowBlueprint'}, synchronize_session=False) + + session.query(DataPipeline).filter(DataPipeline.devStages is None).update( + {DataPipeline.devStages: ['dev', 'test', 'prod']}, synchronize_session=False) + session.commit() + + print("Backfilling values for devStages and devStrategy is done. Updating nullable to False...") + # Force nullable = False op.alter_column( 'datapipeline', 'devStages', diff --git a/backend/migrations/versions/e1cd4927482b_rename_imported_dataset_aws_resources.py b/backend/migrations/versions/e1cd4927482b_rename_imported_dataset_aws_resources.py new file mode 100644 index 000000000..9f77df9cc --- /dev/null +++ b/backend/migrations/versions/e1cd4927482b_rename_imported_dataset_aws_resources.py @@ -0,0 +1,132 @@ +"""rename_imported_dataset_aws_resources + +Revision ID: e1cd4927482b +Revises: 72b8a90b6ee8 +Create Date: 2023-07-13 09:20:20.091639 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy import orm, Column, String, Boolean +from sqlalchemy.dialects import postgresql +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import query_expression + +from dataall.db import utils, Resource +from dataall.utils.naming_convention import ( + NamingConventionService, + NamingConventionPattern, +) + +# revision identifiers, used by Alembic. +revision = 'e1cd4927482b' +down_revision = '72b8a90b6ee8' +branch_labels = None +depends_on = None + +Base = declarative_base() + + +class Environment(Resource, Base): + __tablename__ = 'environment' + organizationUri = Column(String, nullable=False) + environmentUri = Column(String, primary_key=True, default=utils.uuid('environment')) + AwsAccountId = Column(String, nullable=False) + resourcePrefix = Column(String, nullable=False, default='dataall') + + +class Dataset(Resource, Base): + __tablename__ = 'dataset' + environmentUri = Column(String, nullable=False) + organizationUri = Column(String, nullable=False) + datasetUri = Column(String, primary_key=True, default=utils.uuid('dataset')) + region = Column(String, default='eu-west-1') + AwsAccountId = Column(String, nullable=False) + S3BucketName = Column(String, nullable=False) + GlueDatabaseName = Column(String, nullable=False) + GlueCrawlerName = Column(String) + GlueCrawlerSchedule = Column(String) + GlueProfilingJobName = Column(String) + GlueProfilingTriggerSchedule = Column(String) + GlueProfilingTriggerName = Column(String) + GlueDataQualityJobName = Column(String) + GlueDataQualitySchedule = Column(String) + GlueDataQualityTriggerName = Column(String) + IAMDatasetAdminRoleArn = Column(String, nullable=False) + IAMDatasetAdminUserArn = Column(String, nullable=False) + KmsAlias = Column(String, nullable=False) + language = Column(String, nullable=False, default='English') + topics = Column(postgresql.ARRAY(String), nullable=True) + confidentiality = Column(String, nullable=False, default='Unclassified') + tags = Column(postgresql.ARRAY(String)) + + bucketCreated = Column(Boolean, default=False) + glueDatabaseCreated = Column(Boolean, default=False) + iamAdminRoleCreated = Column(Boolean, default=False) + iamAdminUserCreated = Column(Boolean, default=False) + kmsAliasCreated = Column(Boolean, default=False) + lakeformationLocationCreated = Column(Boolean, default=False) + bucketPolicyCreated = Column(Boolean, default=False) + + businessOwnerEmail = Column(String, nullable=True) + businessOwnerDelegationEmails = Column(postgresql.ARRAY(String), nullable=True) + stewards = Column(String, nullable=True) + + SamlAdminGroupName = Column(String, nullable=True) + + importedS3Bucket = Column(Boolean, default=False) + importedGlueDatabase = Column(Boolean, default=False) + importedKmsKey = Column(Boolean, default=False) + importedAdminRole = Column(Boolean, default=False) + imported = Column(Boolean, default=False) + + +def upgrade(): + try: + bind = op.get_bind() + session = orm.Session(bind=bind) + print('Updating imported dataset aws resources names...') + imported_datasets: [Dataset] = session.query(Dataset).filter(Dataset.imported.is_(True)) + for dataset in imported_datasets: + print(f"Updating dataset {dataset.datasetUri}") + environment: [Environment] = session.query(Environment).filter( + Environment.environmentUri == dataset.environmentUri + ).first() + glue_etl_basename = NamingConventionService( + target_uri=dataset.datasetUri, + target_label=dataset.label, + pattern=NamingConventionPattern.GLUE_ETL, + resource_prefix=environment.resourcePrefix, + ).build_compliant_name() + dataset.GlueCrawlerName = f"{glue_etl_basename}-crawler" + dataset.GlueProfilingJobName = f"{glue_etl_basename}-profiler" + dataset.GlueProfilingTriggerName = f"{glue_etl_basename}-trigger" + dataset.GlueDataQualityJobName = f"{glue_etl_basename}-dataquality" + dataset.GlueDataQualityTriggerName = f"{glue_etl_basename}-dqtrigger" + if not dataset.importedKmsKey: + # Not adding downgrade for this line because this is a fix not an upgrade + dataset.KmsAlias = "Undefined" + session.commit() + print('imported Datasets resources updated successfully') + except Exception as e: + print(f'Failed to update imported dataset aws resources names due to: {e}') + + +def downgrade(): + try: + bind = op.get_bind() + session = orm.Session(bind=bind) + print('Updating imported dataset aws resources names to previous...') + imported_datasets: [Dataset] = session.query(Dataset).filter(Dataset.imported.is_(True)) + for dataset in imported_datasets: + print(f"Updating dataset {dataset.datasetUri}") + glue_etl_basename = f"{dataset.S3BucketName}-{dataset.datasetUri}" + dataset.GlueCrawlerName = f"{glue_etl_basename}-crawler" + dataset.GlueProfilingJobName = f"{glue_etl_basename}-profiler" + dataset.GlueProfilingTriggerName = f"{glue_etl_basename}-trigger" + dataset.GlueDataQualityJobName = f"{glue_etl_basename}-dataquality" + dataset.GlueDataQualityTriggerName = f"{glue_etl_basename}-dqtrigger" + session.commit() + print('imported Datasets resources updated successfully') + except Exception as e: + print(f'Failed to update imported dataset aws resources to previous names due to: {e}') diff --git a/deploy/cdk_exec_policy/cdkExecPolicy.yaml b/deploy/cdk_exec_policy/cdkExecPolicy.yaml new file mode 100644 index 000000000..cc98cf0ac --- /dev/null +++ b/deploy/cdk_exec_policy/cdkExecPolicy.yaml @@ -0,0 +1,278 @@ +AWSTemplateFormatVersion: 2010-09-09 +Description: Custom least privilege IAM policy for linking environments to dataall +Parameters: + PolicyName: + Description: IAM policy name (The same name must be used during CDK bootstrapping. Default is DataAllCustomCDKPolicy.) + Type: String + Default: 'DataAllCustomCDKPolicy' + EnvironmentResourcePrefix: + Description: The resource prefix value of the dataall environment. It MUST match the resource prefix that we use when we create the environment. + Type: String +Resources: + CDKCustomExecutionPolicy0: + Type: 'AWS::IAM::ManagedPolicy' + Properties: + ManagedPolicyName: !Ref PolicyName + PolicyDocument: + Version: 2012-10-17 + Statement: + - Sid: KMS + Action: + - 'kms:CreateKey' + Effect: Allow + Resource: '*' + - Sid: ec2 + Action: + - 'ec2:DescribeSecurityGroups' + - 'ec2:CreateSecurityGroup' + - 'ec2:createTags' + - 'ec2:DeleteSecurityGroup' + - 'ec2:RevokeSecurityGroupEgress' + - 'ec2:AuthorizeSecurityGroupIngress' + - 'ec2:AuthorizeSecurityGroupEgress' + - 'ec2:RevokeSecurityGroupIngress' + - 'ec2:AttachInternetGateway' + - 'ec2:DetachInternetGateway' + - 'ec2:DescribeInternetGateways' + - 'ec2:DescribeRouteTables' + - 'ec2:AssociateRouteTable' + - 'ec2:DisassociateRouteTable' + - 'ec2:CreateRoute' + - 'ec2:DeleteRoute' + Effect: Allow + Resource: '*' + - Sid: Athena + Effect: Allow + Action: 'athena:CreateWorkGroup' + Resource: + - !Sub 'arn:${AWS::Partition}:athena:*:${AWS::AccountId}:workgroup/*' + - Sid: IAM + Action: + - 'iam:CreatePolicy' + - 'iam:GetPolicy' + Effect: Allow + Resource: + - !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/*' + - Sid: IAMRole + Action: + - 'iam:AttachRolePolicy' + - 'iam:CreateRole' + - 'iam:CreateServiceLinkedRole' + - 'iam:GetRole' + - 'iam:GetRolePolicy' + - 'iam:PutRolePolicy' + - 'iam:TagRole' + - 'iam:UnTagRole' + - 'iam:DeleteRole' + - 'iam:DetachRolePolicy' + - 'iam:DeleteRolePolicy' + - 'iam:PassRole' + - 'iam:UpdateAssumeRolePolicy' + - 'iam:DeletePolicy' + - 'iam:List*' + - 'iam:GetServiceLastAccessedDetails' + Effect: Allow + Resource: '*' + - Sid: IAMQuickSight + Effect: Allow + Action: + - 'iam:CreatePolicyVersion' + - 'iam:DeletePolicyVersion' + Resource: + - !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/service-role/AWSQuickSight*' + - Sid: QuickSight + Effect: Allow + Action: + - 'ds:AuthorizeApplication' + - 'ds:UnauthorizeApplication' + - 'ds:CheckAlias' + - 'ds:CreateAlias' + - 'ds:DescribeDirectories' + - 'ds:DescribeTrusts' + - 'ds:DeleteDirectory' + - 'ds:CreateIdentityPoolDirectory' + - 'quicksight:CreateAdmin' + - 'quicksight:CreateUser' + - 'quicksight:Subscribe' + - 'quicksight:GetGroupMapping' + - 'quicksight:SearchDirectoryGroups' + - 'quicksight:SetGroupMapping' + - 'quicksight:RegisterUser' + - 'quicksight:GetDashboardEmbedUrl' + - 'quicksight:DescribeDashboardPermissions' + Resource: '*' + - Sid: QuickSightDeny + Effect: Deny + Action: + - 'quicksight:Unsubscribe' + Resource: '*' + - Sid: KMSCreateAlias + Action: + - 'kms:CreateAlias' + Effect: Allow + Resource: + - !Sub 'arn:${AWS::Partition}:kms:*:${AWS::AccountId}:alias/*' + - Sid: KMSKey + Action: + - 's3:PutBucketAcl' + - 's3:PutBucketNotification' + Effect: Allow + Resource: + - !Sub 'arn:${AWS::Partition}:s3:::${EnvironmentResourcePrefix}-logging-*' + - Sid: ReadBuckets + Action: + - 'kms:CreateAlias' + - 'kms:CreateGrant' + - 'kms:Decrypt' + - 'kms:DescribeKey' + - 'kms:EnableKeyRotation' + - 'kms:Encrypt' + - 'kms:GetKeyPolicy' + - 'kms:GetKeyRotationStatus' + - 'kms:ListResourceTags' + - 'kms:PutKeyPolicy' + - 'kms:TagResource' + Effect: Allow + Resource: !Sub 'arn:${AWS::Partition}:kms:*:${AWS::AccountId}:key/*' + - Sid: Lambda + Action: + - 'lambda:AddPermission' + - 'lambda:CreateFunction' + - 'lambda:GetFunction' + - 'lambda:GetFunctionCodeSigningConfig' + - 'lambda:GetRuntimeManagementConfig' + - 'lambda:PutFunctionEventInvokeConfig' + - 'lambda:InvokeFunction' + - 'lambda:RemovePermission' + Effect: Allow + Resource: '*' + - Sid: LambdaPublishLayer + Effect: Allow + Action: + - 'lambda:PublishLayerVersion' + Resource: + - !Sub 'arn:${AWS::Partition}:lambda:*:${AWS::AccountId}:layer:*' + - Sid: S3 + Action: + - 's3:CreateBucket' + - 's3:GetBucketPolicy' + - 's3:PutBucketCORS' + - 's3:PutBucketLogging' + - 's3:PutBucketPolicy' + - 's3:PutBucketPublicAccessBlock' + - 's3:PutBucketTagging' + - 's3:PutBucketVersioning' + - 's3:PutEncryptionConfiguration' + - 's3:PutLifecycleConfiguration' + - 's3:DeleteBucketPolicy' + - 's3:DeleteBucket' + Effect: Allow + Resource: !Sub 'arn:${AWS::Partition}:s3:::*' + - Sid: SQS + Effect: Allow + Action: + - 'sqs:CreateQueue' + - 'sqs:SetQueueAttributes' + Resource: !Sub 'arn:${AWS::Partition}:sqs:*:${AWS::AccountId}:*' + - Sid: SSM + Effect: Allow + Action: + - 'ssm:GetParameters' + - 'ssm:PutParameter' + Resource: '*' + - Sid: Logs + Effect: Allow + Action: + - 'logs:CreateLogGroup' + - 'logs:CreateLogStream' + - 'logs:PutLogEvents' + - 'logs:DescribeLogStreams' + Resource: !Sub 'arn:${AWS::Partition}:logs:*:*:*' + - Sid: STS + Effect: Allow + Action: + - 'sts:AssumeRole' + - 'iam:*Role*' + Resource: !Sub 'arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-*' + - Sid: CloudFormation + Effect: Allow + Action: + - 'cloudformation:*' + Resource: !Sub 'arn:${AWS::Partition}:cloudformation:*:${AWS::AccountId}:stack/CDKToolkit/*' + - Sid: ECR + Effect: Allow + Action: + - 'ecr:SetRepositoryPolicy' + - 'ecr:GetLifecyclePolicy' + - 'ecr:PutImageScanningConfiguration' + - 'ecr:DescribeRepositories' + - 'ecr:CreateRepository' + - 'ecr:DeleteRepository' + Resource: !Sub 'arn:${AWS::Partition}:ecr:*:${AWS::AccountId}:repository/cdk-*' + - Sid: SSMTwo + Effect: Allow + Action: + - 'ssm:GetParameter' + - 'ssm:PutParameter' + - 'ssm:DeleteParameter' + Resource: !Sub 'arn:${AWS::Partition}:ssm:*:${AWS::AccountId}:parameter/cdk-bootstrap/*' + - Sid: CloudformationTwo + Effect: Allow + Action: + - '*' + Resource: '*' + Condition: + 'ForAnyValue:StringEquals': + 'aws:CalledVia': 'cloudformation.amazonaws.com' + - Sid: S3Staging + Effect: Allow + Action: + - 's3:*' + Resource: + - !Sub 'arn:${AWS::Partition}:s3:::cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}*' + - Sid: Pipelines + Effect: Allow + Action: + - 'codepipeline:TagResource' + - 'codepipeline:UntagResource' + - 'codepipeline:CreatePipeline' + - 'codepipeline:UpdatePipeline' + - 'codepipeline:StartPipelineExecution' + - 'codepipeline:GetPipeline' + - 'codepipeline:GetPipelineState' + - 'codepipeline:GetPipelineExecution' + - 'codepipeline:ListPipelineExecutions' + - 'codepipeline:ListActionExecutions' + - 'codepipeline:ListActionTypes' + - 'codepipeline:ListPipelines' + - 'codepipeline:ListTagsForResource' + - 'codepipeline:DeletePipeline' + - 'codestar-notifications:ListNotificationRules' + - 'codestar-notifications:ListEventTypes' + - 'codestar-notifications:ListTargets' + Resource: '*' + - Sid: PipelinesS3 + Effect: Allow + Action: + - 's3:GetObject' + - 's3:ListBucket' + - 's3:GetBucketPolicy' + Resource: + - !Sub 'arn:${AWS::Partition}:s3::*:codepipeline-*' + - Sid: CodeStarNotificationsReadOnly + Effect: Allow + Action: + - 'codestar-notifications:DescribeNotificationRule' + Resource: '*' + Condition: + 'StringLike': + 'codestar-notifications:NotificationsForResource': !Sub 'arn:${AWS::Partition}:codepipeline:*' + - Sid: Eventrules + Effect: Allow + Action: + - 'events:PutRule' + - 'events:DescribeRule' + - 'events:PutTargets' + - 'events:RemoveTargets' + - 'events:DeleteRule' + Resource: '*' diff --git a/deploy/pivot_role/pivotRole.yaml b/deploy/pivot_role/pivotRole.yaml index 3721ea81f..3a4ce3243 100644 --- a/deploy/pivot_role/pivotRole.yaml +++ b/deploy/pivot_role/pivotRole.yaml @@ -26,9 +26,9 @@ Resources: - Effect: Allow Principal: Service: - - glue.amazonaws.com - lakeformation.amazonaws.com - lambda.amazonaws.com + - glue.amazonaws.com Action: - 'sts:AssumeRole' - Effect: Allow @@ -40,48 +40,25 @@ Resources: Condition: StringEquals: 'sts:ExternalId': !Ref ExternalId + StringLike: + 'aws:PrincipalArn': [ + !Sub "arn:aws:iam::${AwsAccountId}:role/*graphql-role", + !Sub "arn:aws:iam::${AwsAccountId}:role/*awsworker-role", + !Sub "arn:aws:iam::${AwsAccountId}:role/*ecs-tasks-role" + ] PivotRolePolicy0: Type: 'AWS::IAM::ManagedPolicy' Properties: PolicyDocument: Version: 2012-10-17 Statement: - - Sid: Athena + - Sid: ReadBuckets Action: - - 'athena:GetQuery*' - - 'athena:StartQueryExecution' - - 'athena:ListWorkGroups' + - 's3:ListAllMyBuckets' + - 's3:GetBucketLocation' + - 's3:PutBucketTagging' Effect: Allow Resource: '*' - - Sid: AthenaWorkgroups - Action: - - 'athena:GetWorkGroup' - - 'athena:CreateWorkGroup' - - 'athena:UpdateWorkGroup' - - 'athena:DeleteWorkGroup' - - 'athena:TagResource' - - 'athena:UntagResource' - - 'athena:ListTagsForResource' - Effect: Allow - Resource: !Sub 'arn:aws:athena:*:${AWS::AccountId}:workgroup/${EnvironmentResourcePrefix}*' - - Sid: AwsGlueCrawlerBucket - Effect: Allow - Action: 's3:GetObject' - Resource: - - 'arn:aws:s3:::crawler-public*' - - Sid: ManagedAccessPoints - Action: - - 's3:GetAccessPoint' - - 's3:GetAccessPointPolicy' - - 's3:ListAccessPoints' - - 's3:CreateAccessPoint' - - 's3:DeleteAccessPoint' - - 's3:GetAccessPointPolicyStatus' - - 's3:DeleteAccessPointPolicy' - - 's3:PutAccessPointPolicy' - Effect: Allow - Resource: - - !Sub 'arn:aws:s3:*:${AWS::AccountId}:accesspoint/*' - Sid: ManagedBuckets Action: - 's3:List*' @@ -105,118 +82,81 @@ Resources: Effect: Allow Resource: - 'arn:aws:s3:::*' - - Sid: AWSLoggingBuckets + - Sid: KMS Action: - - 's3:PutBucketAcl' - - 's3:PutBucketNotification' + - 'kms:Decrypt' + - 'kms:Encrypt' + - 'kms:GenerateDataKey*' + - 'kms:PutKeyPolicy' + - 'kms:ReEncrypt*' + - 'kms:TagResource' + - 'kms:UntagResource' Effect: Allow Resource: - - !Sub 'arn:aws:s3:::${EnvironmentResourcePrefix}-logging-*' - - Sid: ReadBuckets - Action: - - 's3:ListAllMyBuckets' - - 's3:GetBucketLocation' - - 's3:PutBucketTagging' - Effect: Allow - Resource: '*' - - Sid: CWMetrics + - '*' + - Sid: KMSList Action: - - 'cloudwatch:PutMetricData' - - 'cloudwatch:GetMetricData' - - 'cloudwatch:GetMetricStatistics' + - 'kms:List*' + - 'kms:DescribeKey' Effect: Allow Resource: '*' - - Sid: Logs - Effect: Allow - Action: - - 'logs:CreateLogGroup' - - 'logs:CreateLogStream' - - 'logs:PutLogEvents' - Resource: - - !Sub 'arn:aws:logs:*:${AWS::AccountId}:log-group:/aws-glue/*' - - !Sub 'arn:aws:logs:*:${AWS::AccountId}:log-group:/aws/lambda/*' - - !Sub 'arn:aws:logs:*:${AWS::AccountId}:log-group:/${EnvironmentResourcePrefix}*' - - Sid: Logging + - Sid: AthenaWorkgroups Action: - - 'logs:PutLogEvents' + - 'athena:GetWorkGroup' + - 'athena:GetQueryExecution' + - 'athena:GetQueryResults' + - 'athena:StartQueryExecution' Effect: Allow - Resource: '*' - - Sid: CWEvents + Resource: !Sub 'arn:aws:athena:*:${AWS::AccountId}:workgroup/${EnvironmentResourcePrefix}*' + - Sid: ManagedAccessPoints Action: - - 'events:DeleteRule' - - 'events:List*' - - 'events:PutRule' - - 'events:PutTargets' - - 'events:RemoveTargets' + - 's3:GetAccessPoint' + - 's3:GetAccessPointPolicy' + - 's3:ListAccessPoints' + - 's3:CreateAccessPoint' + - 's3:DeleteAccessPoint' + - 's3:GetAccessPointPolicyStatus' + - 's3:DeleteAccessPointPolicy' + - 's3:PutAccessPointPolicy' Effect: Allow - Resource: '*' - - Sid: Glue + Resource: + - !Sub 'arn:aws:s3:*:${AWS::AccountId}:accesspoint/*' + - Sid: GlueCatalog Action: - 'glue:BatchCreatePartition' - 'glue:BatchDeletePartition' - 'glue:BatchDeleteTable' - - 'glue:CreateCrawler' - 'glue:CreateDatabase' - 'glue:CreatePartition' - 'glue:CreateTable' - - 'glue:DeleteCrawler' - 'glue:DeleteDatabase' - - 'glue:DeleteJob' - 'glue:DeletePartition' - 'glue:DeleteTable' - - 'glue:DeleteTrigger' - 'glue:BatchGet*' - 'glue:Get*' - 'glue:List*' - - 'glue:StartCrawler' - - 'glue:StartJobRun' - - 'glue:StartTrigger' - 'glue:SearchTables' - 'glue:UpdateDatabase' - 'glue:UpdatePartition' - 'glue:UpdateTable' - - 'glue:UpdateTrigger' - - 'glue:UpdateJob' - 'glue:TagResource' - - 'glue:UpdateCrawler' + - 'glue:DeleteResourcePolicy' + - 'glue:PutResourcePolicy' Effect: Allow Resource: '*' - - Sid: KMS + - Sid: GlueETL Action: - - 'kms:Decrypt' - - 'kms:Encrypt' - - 'kms:GenerateDataKey*' - - 'kms:PutKeyPolicy' - - 'kms:ReEncrypt*' - - 'kms:TagResource' - - 'kms:UntagResource' - Effect: Allow - Resource: - - '*' - - Sid: KMSAlias - Action: - - 'kms:DeleteAlias' + - 'glue:StartCrawler' + - 'glue:StartJobRun' + - 'glue:StartTrigger' + - 'glue:UpdateTrigger' + - 'glue:UpdateJob' + - 'glue:UpdateCrawler' Effect: Allow Resource: - - !Sub 'arn:aws:kms:*:${AWS::AccountId}:alias/${EnvironmentResourcePrefix}*' - - Sid: KMSCreate - Action: - - 'kms:List*' - - 'kms:DescribeKey' - - 'kms:CreateAlias' - - 'kms:CreateKey' - Effect: Allow - Resource: '*' - - Sid: Organizations - Action: 'organizations:DescribeOrganization' - Effect: Allow - Resource: '*' - - Sid: ResourcesGroupTags - Action: - - 'tag:*' - - 'resource-groups:*' - Effect: Allow - Resource: '*' + - !Sub 'arn:aws:glue:*:${AWS::AccountId}:crawler/${EnvironmentResourcePrefix}*' + - !Sub 'arn:aws:glue:*:${AWS::AccountId}:job/${EnvironmentResourcePrefix}*' + - !Sub 'arn:aws:glue:*:${AWS::AccountId}:trigger/${EnvironmentResourcePrefix}*' - Sid: SNSPublish Action: - 'sns:Publish' @@ -245,6 +185,33 @@ Resources: - 'sqs:SendMessage' Effect: Allow Resource: !Sub 'arn:aws:sqs:*:${AWS::AccountId}:${EnvironmentResourcePrefix}*' + - Sid: AWSLoggingBuckets + Action: + - 's3:PutBucketAcl' + - 's3:PutBucketNotification' + Effect: Allow + Resource: + - !Sub 'arn:aws:s3:::${EnvironmentResourcePrefix}-logging-*' + - Sid: CWMetrics + Action: + - 'cloudwatch:PutMetricData' + - 'cloudwatch:GetMetricData' + - 'cloudwatch:GetMetricStatistics' + Effect: Allow + Resource: '*' + - Sid: Logs + Effect: Allow + Action: + - 'logs:CreateLogGroup' + - 'logs:CreateLogStream' + Resource: + - !Sub 'arn:aws:logs:*:${AWS::AccountId}:log-group:/aws/lambda/*' + - !Sub 'arn:aws:logs:*:${AWS::AccountId}:log-group:/${EnvironmentResourcePrefix}*' + - Sid: Logging + Action: + - 'logs:PutLogEvents' + Effect: Allow + Resource: '*' ManagedPolicyName: !Sub ${EnvironmentResourcePrefix}-pivotrole-policy-0 Roles: - !Ref PivotRoleName @@ -256,75 +223,20 @@ Resources: PolicyDocument: Version: 2012-10-17 Statement: - - Sid: Redshift - Effect: Allow - Action: - - 'redshift:DeleteTags' - - 'redshift:ModifyClusterIamRoles' - - 'redshift:DescribeClusterSecurityGroups' - - 'redshift:DescribeClusterSubnetGroups' - - 'redshift:pauseCluster' - - 'redshift:resumeCluster' - Resource: '*' - Condition: - StringEquals: - 'aws:ResourceTag/dataall': 'true' - - Sid: RedshiftRead - Effect: Allow - Action: - - 'redshift:DescribeClusters' - - 'redshift:CreateTags' - - 'redshift:DescribeClusterSubnetGroups' - Resource: '*' - - Sid: RedshiftCreds - Effect: Allow - Action: - - 'redshift:GetClusterCredentials' - Resource: - - !Sub 'arn:aws:redshift:*:${AWS::AccountId}:dbgroup:*/*' - - !Sub 'arn:aws:redshift:*:${AWS::AccountId}:dbname:*/*' - - !Sub 'arn:aws:redshift:*:${AWS::AccountId}:dbuser:*/*' - - Sid: AllowRedshiftSubnet - Effect: Allow - Action: - - 'redshift:CreateClusterSubnetGroup' - Resource: '*' - - Sid: AllowRedshiftDataApi - Effect: Allow - Action: - - 'redshift-data:ListTables' - - 'redshift-data:GetStatementResult' - - 'redshift-data:CancelStatement' - - 'redshift-data:ListSchemas' - - 'redshift-data:ExecuteStatement' - - 'redshift-data:ListStatements' - - 'redshift-data:ListDatabases' - - 'redshift-data:DescribeStatement' - Resource: '*' - Sid: EC2SG Effect: Allow Action: - - 'ec2:CreateSecurityGroup' - - 'ec2:CreateNetworkInterface' - - 'ec2:Describe*' + - 'ec2:DescribeSubnets' + - 'ec2:DescribeSecurityGroups' + - 'ec2:DescribeVpcs' + - 'ec2:DescribeInstances' + - 'ec2:DescribeNetworkInterfaces' Resource: '*' - - Sid: TagsforENI - Effect: Allow - Action: - - 'ec2:DeleteTags' - - 'ec2:CreateTags' - Resource: !Sub 'arn:aws:ec2:*:${AWS::AccountId}:network-interface/*' - - Sid: DeleteENI - Effect: Allow - Action: - - 'ec2:DeleteNetworkInterface' - Resource: !Sub 'arn:aws:ec2:*:${AWS::AccountId}:network-interface/*' - Sid: SageMakerNotebookActions Effect: Allow Action: - 'sagemaker:ListTags' - 'sagemaker:DescribeUserProfile' - - 'sagemaker:DeleteNotebookInstance' - 'sagemaker:StopNotebookInstance' - 'sagemaker:CreatePresignedNotebookInstanceUrl' - 'sagemaker:DescribeNotebookInstance' @@ -370,8 +282,6 @@ Resources: - 'ram:UpdateResourceShare' Resource: !Sub 'arn:aws:ram:*:${AWS::AccountId}:resource-share/*' Condition: - StringEquals: - 'aws:ResourceTag/dataall': 'true' 'ForAllValues:StringLike': 'ram:ResourceShareName': - LakeFormation* @@ -390,106 +300,37 @@ Resources: Action: - 'ram:DeleteResourceShare' Resource: !Sub 'arn:aws:ram:*:${AWS::AccountId}:resource-share/*' - Condition: - StringEqualsIfExists: - 'aws:ResourceTag/dataall': 'true' - Sid: RamInvitations Effect: Allow Action: - "ram:AcceptResourceShareInvitation" - "ram:RejectResourceShareInvitation" - - "ec2:DescribeAvailabilityZones" - "ram:EnableSharingWithAwsOrganization" Resource: '*' - - Sid: RamReadGlue + - Sid: RamRead Effect: Allow Action: - - 'glue:PutResourcePolicy' - - 'glue:DeleteResourcePolicy' - 'ram:Get*' - 'ram:List*' Resource: '*' - - Sid: SGCreateTag - Effect: Allow - Action: - - 'ec2:CreateTags' - Resource: - - !Sub 'arn:aws:ec2:*:${AWS::AccountId}:security-group/*' - Condition: - StringEquals: - 'aws:RequestTag/dataall': 'true' - - Sid: SGandRedshift - Effect: Allow - Action: - - 'ec2:DeleteTags' - - 'ec2:DeleteSecurityGroup' - - 'redshift:DeleteClusterSubnetGroup' - Resource: - - '*' - Condition: - 'ForAnyValue:StringEqualsIfExists': - 'aws:ResourceTag/dataall': 'true' - - Sid: RedshiftDataApi - Effect: Allow - Action: - - 'redshift-data:ListTables' - - 'redshift-data:GetStatementResult' - - 'redshift-data:CancelStatement' - - 'redshift-data:ListSchemas' - - 'redshift-data:ExecuteStatement' - - 'redshift-data:ListStatements' - - 'redshift-data:ListDatabases' - - 'redshift-data:DescribeStatement' - Resource: '*' - Condition: - StringEqualsIfExists: - 'aws:ResourceTag/dataall': 'true' - - Sid: DevTools0 - Effect: Allow - Action: 'cloudformation:ValidateTemplate' - Resource: '*' - - Sid: DevTools1 - Effect: Allow - Action: - - 'secretsmanager:CreateSecret' - - 'secretsmanager:DeleteSecret' - - 'secretsmanager:TagResource' - - 'codebuild:DeleteProject' - Resource: '*' - Condition: - StringEquals: - 'aws:ResourceTag/dataall': 'true' - - Sid: DevTools2 - Effect: Allow - Action: - - 'codebuild:CreateProject' - - 'ecr:CreateRepository' - - 'ssm:PutParameter' - - 'ssm:AddTagsToResource' - Resource: '*' - Condition: - StringEquals: - 'aws:RequestTag/dataall': 'true' - Sid: CloudFormation Effect: Allow Action: - - 'cloudformation:CreateStack' + - 'cloudformation:DescribeStacks' + - 'cloudformation:DescribeStackResources' + - 'cloudformation:DescribeStackEvents' + - 'cloudformation:DeleteStack' Resource: - !Sub 'arn:aws:cloudformation:*:${AWS::AccountId}:stack/${EnvironmentResourcePrefix}*/*' - !Sub 'arn:aws:cloudformation:*:${AWS::AccountId}:stack/CDKToolkit/*' - - Sid: CloudFormation2 + - Sid: CloudFormationDataPipelines Effect: Allow Action: - 'cloudformation:DescribeStacks' - 'cloudformation:DescribeStackResources' - 'cloudformation:DescribeStackEvents' - 'cloudformation:DeleteStack' - - 'cloudformation:GetTemplate' - - 'cloudformation:ListStackResources' - - 'cloudformation:DescribeStackResource' Resource: - - !Sub 'arn:aws:cloudformation:*:${AWS::AccountId}:stack/${EnvironmentResourcePrefix}*/*' - - !Sub 'arn:aws:cloudformation:*:${AWS::AccountId}:stack/CDKToolkit/*' - !Sub 'arn:aws:cloudformation:*:${AWS::AccountId}:stack/*/*' ManagedPolicyName: !Sub ${EnvironmentResourcePrefix}-pivotrole-policy-1 Roles: @@ -505,8 +346,6 @@ Resources: - Sid: LakeFormation Effect: Allow Action: - - "lakeformation:RegisterResource" - - "lakeformation:DeregisterResource" - "lakeformation:UpdateResource" - "lakeformation:DescribeResource" - "lakeformation:AddLFTagsToResource" @@ -533,61 +372,10 @@ Resources: - 'lakeformation:GetWorkUnitResults' - 'lakeformation:GetQueryState' - 'lakeformation:GetQueryStatistics' - - 'lakeformation:StartTransaction' - - 'lakeformation:CommitTransaction' - - 'lakeformation:CancelTransaction' - - 'lakeformation:ExtendTransaction' - - 'lakeformation:DescribeTransaction' - - 'lakeformation:ListTransactions' - 'lakeformation:GetTableObjects' - 'lakeformation:UpdateTableObjects' - 'lakeformation:DeleteObjectsOnCancel' Resource: '*' - - Sid: Compute - Effect: Allow - Action: - - 'lambda:CreateFunction' - - 'lambda:AddPermission' - - 'lambda:InvokeFunction' - - 'lambda:RemovePermission' - - 'lambda:GetFunction' - - 'lambda:GetFunctionConfiguration' - - 'lambda:DeleteFunction' - - 'ecr:CreateRepository' - - 'ecr:SetRepositoryPolicy' - - 'ecr:DeleteRepository' - - 'ecr:DescribeImages' - - 'ecr:BatchDeleteImage' - - 'codepipeline:GetPipelineState' - - 'codepipeline:DeletePipeline' - - 'codepipeline:GetPipeline' - - 'codepipeline:CreatePipeline' - - 'codepipeline:TagResource' - - 'codepipeline:UntagResource' - Resource: - - !Sub 'arn:aws:lambda:*:${AWS::AccountId}:function:${EnvironmentResourcePrefix}*' - - !Sub 'arn:aws:s3:::${EnvironmentResourcePrefix}*' - - !Sub 'arn:aws:codepipeline:*:${AWS::AccountId}:${EnvironmentResourcePrefix}*' - - !Sub 'arn:aws:ecr:*:${AWS::AccountId}:repository/${EnvironmentResourcePrefix}*' - - Sid: DatabrewList - Effect: Allow - Action: - - 'databrew:List*' - Resource: '*' - - Sid: DatabrewPermissions - Effect: Allow - Action: - - 'databrew:BatchDeleteRecipeVersion' - - 'databrew:Delete*' - - 'databrew:Describe*' - - 'databrew:PublishRecipe' - - 'databrew:SendProjectSessionAction' - - 'databrew:Start*' - - 'databrew:Stop*' - - 'databrew:TagResource' - - 'databrew:UntagResource' - - 'databrew:Update*' - Resource: !Sub 'arn:aws:databrew:*:${AWS::AccountId}:*/${EnvironmentResourcePrefix}*' - Sid: QuickSight Effect: Allow Action: @@ -641,19 +429,6 @@ Resources: - !Sub 'arn:aws:ssm:*:${AWS::AccountId}:parameter/${EnvironmentResourcePrefix}/*' - !Sub 'arn:aws:ssm:*:${AWS::AccountId}:parameter/dataall/*' - !Sub 'arn:aws:ssm:*:${AWS::AccountId}:parameter/ddk/*' - - Sid: Secretsmanager - Effect: Allow - Action: - - "secretsmanager:DescribeSecret" - - "secretsmanager:GetSecretValue" - Resource: - - !Sub 'arn:aws:secretsmanager:*:${AWS::AccountId}:secret:${EnvironmentResourcePrefix}*' - - !Sub 'arn:aws:secretsmanager:*:${AWS::AccountId}:secret:dataall*' - - Sid: SecretsmanagerList - Effect: Allow - Action: - - "secretsmanager:ListSecrets" - Resource: '*' - Sid: IAMListGet Action: - 'iam:Get*' @@ -671,24 +446,23 @@ Resources: - 'iam:PassRole' Effect: Allow Resource: - - !Sub 'arn:aws:iam::${AWS::AccountId}:role/${EnvironmentResourcePrefix}*' - !Sub 'arn:aws:iam::${AWS::AccountId}:role/${PivotRoleName}' - - !Sub 'arn:aws:iam::${AWS::AccountId}:role/cdk-*' - - Sid: STS + - Sid: IAMPassRoleGlue Action: - - 'sts:AssumeRole' + - 'iam:PassRole' Effect: Allow Resource: - !Sub 'arn:aws:iam::${AWS::AccountId}:role/${EnvironmentResourcePrefix}*' - - !Sub 'arn:aws:iam::${AWS::AccountId}:role/ddk-*' - - Sid: StepFunctions + Condition: + StringEquals: + 'iam:PassedToService': [ "glue.amazonaws.com" ] + - Sid: STS Action: - - 'states:DescribeStateMachine' - - 'states:ListExecutions' - - 'states:StartExecution' + - 'sts:AssumeRole' Effect: Allow Resource: - - !Sub 'arn:aws:states:*:${AWS::AccountId}:stateMachine:${EnvironmentResourcePrefix}*' + - !Sub 'arn:aws:iam::${AWS::AccountId}:role/${EnvironmentResourcePrefix}*' + - !Sub 'arn:aws:iam::${AWS::AccountId}:role/ddk-*' - Sid: CodeCommit Action: - 'codecommit:GetFile' diff --git a/deploy/requirements.txt b/deploy/requirements.txt index 4490b9d4a..fdc8a53e5 100644 --- a/deploy/requirements.txt +++ b/deploy/requirements.txt @@ -1,4 +1,4 @@ -aws-cdk-lib==2.77.0 +aws-cdk-lib==2.78.0 boto3-stubs==1.20.20 boto3==1.24.85 botocore==1.27.85 diff --git a/deploy/stacks/aurora.py b/deploy/stacks/aurora.py index 6fe1975b0..d1b5d36a8 100644 --- a/deploy/stacks/aurora.py +++ b/deploy/stacks/aurora.py @@ -29,6 +29,7 @@ def __init__( ): super().__init__(scope, id, **kwargs) + # if exclude_characters property is set make sure that the pwd regex in DbConfig is changed accordingly db_credentials = rds.DatabaseSecret( self, f'{resource_prefix}-{envname}-aurora-db', username='dtaadmin' ) @@ -52,60 +53,9 @@ def __init__( security_group_name=f'{resource_prefix}-{envname}-aurora-sg', vpc=vpc, allow_all_outbound=False, + disable_inline_rules=True ) - if lambdas: - l: _lambda.Function - for l in lambdas: - sgs = l.connections.security_groups - for i, sg in enumerate(sgs): - db_security_group.add_ingress_rule( - peer=sg, - connection=ec2.Port.tcp(5432), - description=f'Allow dataall lambda {l.function_name}', - ) - - if ecs_security_groups: - for sg in ecs_security_groups: - db_security_group.add_ingress_rule( - peer=sg, - connection=ec2.Port.tcp(5432), - description=f'Allow dataall ECS cluster tasks', - ) - - if codebuild_dbmigration_sg: - db_security_group.add_ingress_rule( - peer=codebuild_dbmigration_sg, - connection=ec2.Port.tcp(5432), - description=f'Allow dataall ECS codebuild alembic migration', - ) - - if quicksight_monitoring_sg: - db_security_group.add_ingress_rule( - peer=quicksight_monitoring_sg, - connection=ec2.Port.tcp(5432), - description=f'Allow Quicksight connection from Quicksight to RDS port', - ) - - db_security_group.add_egress_rule( - peer=quicksight_monitoring_sg, - connection=ec2.Port.all_tcp(), - description=f'Allow Quicksight connection from RDS to Quicksight', - ) - - quicksight_monitoring_sg.add_ingress_rule( - peer=db_security_group, - connection=ec2.Port.all_tcp(), - description=f'Allow RDS from RDS to Quicksight', - ) - - quicksight_monitoring_sg.add_egress_rule( - peer=db_security_group, - connection=ec2.Port.tcp(5432), - description=f'Allow RDS from Quicksight to RDS', - ) - - key = aws_kms.Key( self, f'AuroraKMSKey', @@ -146,18 +96,52 @@ def __init__( storage_encryption_key=key, ) database.add_rotation_single_user(automatically_after=Duration.days(90)) + + # Allow Lambda Connections + if lambdas: + l: _lambda.Function + for l in lambdas: + database.connections.allow_from( + l.connections, + ec2.Port.tcp(5432), + f'Allow dataall lambda {l.function_name}', + ) + + # Allow ECS Connections + if ecs_security_groups: + for sg in ecs_security_groups: + database.connections.allow_from( + ec2.Connections(security_groups=[sg]), + ec2.Port.tcp(5432), + f'Allow dataall ecs to db connection', + ) + + # Allow CodeBuild DB Migration Connections + if codebuild_dbmigration_sg: + database.connections.allow_from( + ec2.Connections(security_groups=[codebuild_dbmigration_sg]), + ec2.Port.tcp(5432), + 'Allow dataall ECS codebuild alembic migration', + ) + + if quicksight_monitoring_sg: + database.connections.allow_from( + ec2.Connections(security_groups=[quicksight_monitoring_sg]), + ec2.Port.tcp(5432), + 'Allow Quicksight connection from Quicksight to RDS port', + ) + database.connections.allow_to( + ec2.Connections(security_groups=[quicksight_monitoring_sg]), + ec2.Port.all_tcp(), + 'Allow Quicksight connection from RDS to Quicksight', + ) + ssm.StringParameter( self, 'DatabaseHostParameter', parameter_name=f'/dataall/{envname}/aurora/hostname', string_value=str(database.cluster_endpoint.hostname), ) - ssm.StringParameter( - self, - 'DatabasePortParameter', - parameter_name=f'/dataall/{envname}/aurora/port', - string_value=str(database.cluster_endpoint.port), - ) ssm.StringParameter( self, diff --git a/deploy/stacks/backend_stack.py b/deploy/stacks/backend_stack.py index 78b920482..502f9fa55 100644 --- a/deploy/stacks/backend_stack.py +++ b/deploy/stacks/backend_stack.py @@ -1,4 +1,5 @@ from builtins import super +import boto3 from aws_cdk import aws_ecr as ecr from aws_cdk import aws_iam as iam @@ -34,6 +35,7 @@ def __init__( image_tag=None, pipeline_bucket=None, vpc_id=None, + vpc_restricted_nacls=False, vpc_endpoints_sg=None, internet_facing=True, custom_domain=None, @@ -46,6 +48,8 @@ def __init__( shared_dashboard_sessions='anonymous', enable_pivot_role_auto_create=False, enable_opensearch_serverless=False, + codeartifact_domain_name=None, + codeartifact_pip_repo_name=None, **kwargs, ): super().__init__(scope, id, **kwargs) @@ -61,10 +65,13 @@ def __init__( resource_prefix=resource_prefix, vpc_endpoints_sg=vpc_endpoints_sg, vpc_id=vpc_id, + restricted_nacl=vpc_restricted_nacls, **kwargs, ) vpc = self.vpc_stack.vpc vpc_endpoints_sg = self.vpc_stack.vpce_security_group + vpce_connection = ec2.Connections(security_groups=[vpc_endpoints_sg]) + self.s3_prefix_list = self.get_s3_prefix_list() self.pivot_role_name = f"dataallPivotRole{'-cdk' if enable_pivot_role_auto_create else ''}" @@ -78,18 +85,17 @@ def __init__( quicksight_enabled=quicksight_enabled, shared_dashboard_sessions=shared_dashboard_sessions, enable_pivot_role_auto_create=enable_pivot_role_auto_create, - **kwargs, - ) - - SecretsManagerStack( - self, - f'Secrets', - envname=envname, - resource_prefix=resource_prefix, - enable_cw_canaries=enable_cw_canaries, pivot_role_name=self.pivot_role_name, **kwargs, ) + if enable_cw_canaries: + SecretsManagerStack( + self, + f'Secrets', + envname=envname, + resource_prefix=resource_prefix, + **kwargs, + ) s3_resources_stack = S3ResourcesStack( self, @@ -129,6 +135,7 @@ def __init__( envname=envname, resource_prefix=resource_prefix, vpc=vpc, + vpce_connection=vpce_connection, sqs_queue=sqs_stack.queue, image_tag=image_tag, ecr_repository=repo, @@ -147,12 +154,18 @@ def __init__( envname=envname, resource_prefix=resource_prefix, vpc=vpc, - vpc_endpoints_sg=vpc_endpoints_sg, + vpce_connection=vpce_connection, ecr_repository=repo, image_tag=image_tag, prod_sizing=prod_sizing, pivot_role_name=self.pivot_role_name, tooling_account_id=tooling_account_id, + s3_prefix_list=self.s3_prefix_list, + lambdas=[ + self.lambda_api_stack.aws_handler, + self.lambda_api_stack.api_handler, + self.lambda_api_stack.elasticsearch_proxy_handler, + ], **kwargs, ) @@ -162,8 +175,12 @@ def __init__( envname=envname, resource_prefix=resource_prefix, vpc=vpc, + s3_prefix_list=self.s3_prefix_list, tooling_account_id=tooling_account_id, pipeline_bucket=pipeline_bucket, + vpce_connection=vpce_connection, + codeartifact_domain_name=codeartifact_domain_name, + codeartifact_pip_repo_name=codeartifact_pip_repo_name, **kwargs, ) @@ -243,6 +260,7 @@ def __init__( security_group_name=f'{resource_prefix}-{envname}-quicksight-monitoring-sg', vpc=vpc, allow_all_outbound=False, + disable_inline_rules=True, ) else: @@ -277,7 +295,7 @@ def __init__( ], database=aurora_stack.cluster.cluster_identifier, ecs_cluster=self.ecs_stack.ecs_cluster, - ecs_task_definitions=self.ecs_stack.ecs_task_definitions, + ecs_task_definitions_families=self.ecs_stack.ecs_task_definitions_families, backend_api=self.lambda_api_stack.backend_api_name, queue_name=sqs_stack.queue.queue_name, **kwargs, @@ -342,3 +360,15 @@ def create_opensearch_serverless_stack(self): collection_id=aoss_stack.collection_id, collection_name=aoss_stack.collection_name, ) + + def get_s3_prefix_list(self): + ec2_client = boto3.client("ec2", region_name=self.region) + response = ec2_client.describe_prefix_lists( + Filters=[ + { + 'Name': 'prefix-list-name', + 'Values': [f'com.amazonaws.{self.region}.s3'] + }, + ] + ) + return response['PrefixLists'][0].get("PrefixListId") diff --git a/deploy/stacks/backend_stage.py b/deploy/stacks/backend_stage.py index 86a21ba86..9a9d12dc1 100644 --- a/deploy/stacks/backend_stage.py +++ b/deploy/stacks/backend_stage.py @@ -17,6 +17,7 @@ def __init__( tooling_account_id=None, pipeline_bucket=None, vpc_id=None, + vpc_restricted_nacls=False, vpc_endpoints_sg=None, internet_facing=True, custom_domain=None, @@ -29,6 +30,8 @@ def __init__( shared_dashboard_sessions='anonymous', enable_opensearch_serverless=False, enable_pivot_role_auto_create=False, + codeartifact_domain_name=None, + codeartifact_pip_repo_name=None, **kwargs, ): super().__init__(scope, id, **kwargs) @@ -43,6 +46,7 @@ def __init__( pipeline_bucket=pipeline_bucket, image_tag=commit_id, vpc_id=vpc_id, + vpc_restricted_nacls=vpc_restricted_nacls, vpc_endpoints_sg=vpc_endpoints_sg, internet_facing=internet_facing, custom_domain=custom_domain, @@ -55,6 +59,8 @@ def __init__( shared_dashboard_sessions=shared_dashboard_sessions, enable_opensearch_serverless=enable_opensearch_serverless, enable_pivot_role_auto_create=enable_pivot_role_auto_create, + codeartifact_domain_name=codeartifact_domain_name, + codeartifact_pip_repo_name=codeartifact_pip_repo_name, **kwargs, ) diff --git a/deploy/stacks/cloudfront.py b/deploy/stacks/cloudfront.py index 382b91d8e..ebcc51569 100644 --- a/deploy/stacks/cloudfront.py +++ b/deploy/stacks/cloudfront.py @@ -5,6 +5,7 @@ aws_ssm as ssm, aws_s3 as s3, aws_cloudfront as cloudfront, + aws_cloudfront_origins as origins, aws_certificatemanager as acm, aws_route53 as route53, aws_route53_targets as route53_targets, @@ -231,8 +232,13 @@ def __init__( frontend_alternate_domain = None userguide_alternate_domain = None - frontend_alias_configuration = None - userguide_alias_configuration = None + + frontend_domain_names = None + userguide_domain_names = None + + certificate = None + ssl_support_method = None + security_policy = None cloudfront_bucket = s3.Bucket( self, @@ -276,52 +282,32 @@ def __init__( validation=acm.CertificateValidation.from_dns(hosted_zone=hosted_zone), ) - frontend_alias_configuration = ( - cloudfront.ViewerCertificate.from_acm_certificate( - aliases=[frontend_alternate_domain], - certificate=certificate, - ssl_method=cloudfront.SSLMethod.SNI, - security_policy=cloudfront.SecurityPolicyProtocol.TLS_V1_2_2021, - ) - ) + frontend_domain_names = [frontend_alternate_domain] + userguide_domain_names = [userguide_alternate_domain] + ssl_support_method = cloudfront.SSLMethod.SNI + security_policy = cloudfront.SecurityPolicyProtocol.TLS_V1_2_2021 - cloudfront_distribution = cloudfront.CloudFrontWebDistribution( + cloudfront_distribution = cloudfront.Distribution( self, 'CloudFrontDistribution', - viewer_certificate=frontend_alias_configuration, - origin_configs=[ - cloudfront.SourceConfiguration( - s3_origin_source=cloudfront.S3OriginConfig( - s3_bucket_source=cloudfront_bucket, - origin_access_identity=origin_access_identity, - ), - behaviors=[ - cloudfront.Behavior( - is_default_behavior=True, default_ttl=Duration.hours(1) - ) - ], - ) - ], - viewer_protocol_policy=cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, - default_root_object='index.html', - error_configurations=[ - cloudfront.CfnDistribution.CustomErrorResponseProperty( - error_code=404, - response_code=404, - error_caching_min_ttl=0, - response_page_path='/index.html', + certificate=certificate, + domain_names=frontend_domain_names, + ssl_support_method=ssl_support_method, + minimum_protocol_version=security_policy, + default_behavior=cloudfront.BehaviorOptions( + origin=origins.S3Origin( + bucket=cloudfront_bucket, + origin_access_identity=origin_access_identity ), - cloudfront.CfnDistribution.CustomErrorResponseProperty( - error_code=403, - response_code=403, - error_caching_min_ttl=0, - response_page_path='/index.html', - ), - ], - web_acl_id=acl.get_att('Arn').to_string(), - logging_config=cloudfront.LoggingConfiguration( - bucket=logging_bucket, prefix='cloudfront-logs/frontend' + viewer_protocol_policy=cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, + response_headers_policy=cloudfront.ResponseHeadersPolicy.SECURITY_HEADERS, + cache_policy=cloudfront.CachePolicy.CACHING_OPTIMIZED, ), + default_root_object='index.html', + error_responses=self.error_responses(), + web_acl_id=acl.get_att('Arn').to_string(), + log_bucket=logging_bucket, + log_file_prefix='cloudfront-logs/frontend', ) ssm_distribution_id = ssm.StringParameter( @@ -357,24 +343,16 @@ def __init__( self.http_header_func_version, ) = self.build_docs_http_headers(docs_http_headers, envname, resource_prefix) - - if userguide_alternate_domain: - userguide_alias_configuration = ( - cloudfront.ViewerCertificate.from_acm_certificate( - aliases=[userguide_alternate_domain], - certificate=certificate, - ssl_method=cloudfront.SSLMethod.SNI, - security_policy=cloudfront.SecurityPolicyProtocol.TLS_V1_2_2021, - ) - ) - userguide_docs_distribution, user_docs_bucket = self.build_static_site( f'userguide', acl, auth_at_edge, envname, resource_prefix, - userguide_alias_configuration, + userguide_domain_names, + certificate, + ssl_support_method, + security_policy, logging_bucket, ) if frontend_alternate_domain: @@ -523,7 +501,10 @@ def build_static_site( auth_at_edge, envname, resource_prefix, - alias_configuration, + domain_names, + certificate, + ssl_support_method, + security_policy, logging_bucket, ): @@ -558,120 +539,50 @@ def build_static_site( cloudfront_bucket.grant_read(origin_access_identity) - cloudfront_distribution = cloudfront.CloudFrontWebDistribution( + cloudfront_distribution = cloudfront.Distribution( self, f'{construct_id}Distribution', - viewer_certificate=alias_configuration, - origin_configs=[ - cloudfront.SourceConfiguration( - custom_origin_source=cloudfront.CustomOriginConfig( - domain_name='example.org', - origin_protocol_policy=cloudfront.OriginProtocolPolicy.MATCH_VIEWER, - ), - behaviors=[ - cloudfront.Behavior( - path_pattern='/parseauth', - compress=True, - forwarded_values=cloudfront.CfnDistribution.ForwardedValuesProperty( - query_string=True - ), - lambda_function_associations=[ - cloudfront.LambdaFunctionAssociation( - event_type=cloudfront.LambdaEdgeEventType.VIEWER_REQUEST, - lambda_function=_lambda.Version.from_version_arn( - self, - f'{construct_id}ParserV', - version_arn=parse, - ), - ) - ], - ), - cloudfront.Behavior( - path_pattern='/refreshauth', - compress=True, - forwarded_values=cloudfront.CfnDistribution.ForwardedValuesProperty( - query_string=True - ), - lambda_function_associations=[ - cloudfront.LambdaFunctionAssociation( - event_type=cloudfront.LambdaEdgeEventType.VIEWER_REQUEST, - lambda_function=_lambda.Version.from_version_arn( - self, - f'{construct_id}RefresherV', - version_arn=refresh, - ), - ) - ], - ), - cloudfront.Behavior( - path_pattern='/signout', - compress=True, - forwarded_values=cloudfront.CfnDistribution.ForwardedValuesProperty( - query_string=True - ), - lambda_function_associations=[ - cloudfront.LambdaFunctionAssociation( - event_type=cloudfront.LambdaEdgeEventType.VIEWER_REQUEST, - lambda_function=_lambda.Version.from_version_arn( - self, - f'{construct_id}SingouterV', - version_arn=signout, - ), - ) - ], - ), - ], + certificate=certificate, + domain_names=domain_names, + ssl_support_method=ssl_support_method, + minimum_protocol_version=security_policy, + default_behavior=cloudfront.BehaviorOptions( + viewer_protocol_policy=cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, + response_headers_policy=cloudfront.ResponseHeadersPolicy.SECURITY_HEADERS, + cache_policy=cloudfront.CachePolicy.CACHING_OPTIMIZED, + compress=True, + origin=origins.S3Origin( + bucket=cloudfront_bucket, + origin_access_identity=origin_access_identity ), - cloudfront.SourceConfiguration( - s3_origin_source=cloudfront.S3OriginConfig( - s3_bucket_source=cloudfront_bucket, - origin_access_identity=origin_access_identity, + + edge_lambdas=[ + cloudfront.EdgeLambda( + event_type=cloudfront.LambdaEdgeEventType.VIEWER_REQUEST, + function_version=self.func_version(f'{construct_id}CheckerV', check) ), - behaviors=[ - cloudfront.Behavior( - is_default_behavior=True, - compress=True, - forwarded_values=cloudfront.CfnDistribution.ForwardedValuesProperty( - query_string=True - ), - lambda_function_associations=[ - cloudfront.LambdaFunctionAssociation( - event_type=cloudfront.LambdaEdgeEventType.VIEWER_REQUEST, - lambda_function=_lambda.Version.from_version_arn( - self, - f'{construct_id}CheckerV', - version_arn=check, - ), - ), - cloudfront.LambdaFunctionAssociation( - event_type=cloudfront.LambdaEdgeEventType.ORIGIN_RESPONSE, - lambda_function=self.http_header_func.current_version, - ), - ], - ), - ], + cloudfront.EdgeLambda( + event_type=cloudfront.LambdaEdgeEventType.VIEWER_RESPONSE, + function_version=self.http_header_func.current_version, + ), + ], + ), + additional_behaviors={ + '/parseauth': self.additional_documentation_behavior( + self.func_version(f'{construct_id}ParserV', parse) ), - ], - viewer_protocol_policy=cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, - default_root_object='index.html', - error_configurations=[ - cloudfront.CfnDistribution.CustomErrorResponseProperty( - error_code=404, - response_code=404, - error_caching_min_ttl=0, - response_page_path='/index.html', + '/refreshauth': self.additional_documentation_behavior( + self.func_version(f'{construct_id}RefresherV', refresh) ), - cloudfront.CfnDistribution.CustomErrorResponseProperty( - error_code=403, - response_code=403, - error_caching_min_ttl=0, - response_page_path='/index.html', + '/signout': self.additional_documentation_behavior( + self.func_version(f'{construct_id}SingouterV', signout) ), - ], + }, + default_root_object='index.html', + error_responses=self.error_responses(), web_acl_id=acl.get_att('Arn').to_string(), - logging_config=cloudfront.LoggingConfiguration( - bucket=logging_bucket, prefix=f'cloudfront-logs/{construct_id}' - ), + log_bucket=logging_bucket, + log_file_prefix=f'cloudfront-logs/{construct_id}' ) param_path = f'/dataall/{envname}/cloudfront/docs/user' @@ -702,3 +613,40 @@ def store_distribution_params( parameter_name=f'{param_path}/CloudfrontDistributionBucket', string_value=cloudfront_bucket.bucket_name, ) + + @staticmethod + def additional_documentation_behavior(func) -> cloudfront.BehaviorOptions: + return cloudfront.BehaviorOptions( + origin=origins.HttpOrigin('example.org'), + viewer_protocol_policy=cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS, + compress=True, + cache_policy=cloudfront.CachePolicy.CACHING_OPTIMIZED, + response_headers_policy=cloudfront.ResponseHeadersPolicy.SECURITY_HEADERS, + + edge_lambdas=[ + cloudfront.EdgeLambda( + event_type=cloudfront.LambdaEdgeEventType.VIEWER_REQUEST, + function_version=func + ) + ], + ) + + def func_version(self, name, arn): + return _lambda.Version.from_version_arn(self, name, version_arn=arn) + + @staticmethod + def error_responses(): + return [ + cloudfront.ErrorResponse( + http_status=404, + response_http_status=404, + ttl=Duration.seconds(0), + response_page_path='/index.html', + ), + cloudfront.ErrorResponse( + http_status=403, + response_http_status=403, + ttl=Duration.seconds(0), + response_page_path='/index.html', + ), + ] \ No newline at end of file diff --git a/deploy/stacks/codeartifact.py b/deploy/stacks/codeartifact.py index 542057ca8..b28c6aaa2 100644 --- a/deploy/stacks/codeartifact.py +++ b/deploy/stacks/codeartifact.py @@ -36,6 +36,27 @@ def __init__( ) domain_dict['permissions_policy_document'] = domain_policy + pip_repo_policy = iam.PolicyDocument( + statements=[ + iam.PolicyStatement( + effect=iam.Effect.ALLOW, + actions=[ + "codeartifact:DescribePackageVersion", + "codeartifact:DescribeRepository", + "codeartifact:GetPackageVersionReadme", + "codeartifact:GetRepositoryEndpoint", + "codeartifact:ListPackageVersionAssets", + "codeartifact:ListPackageVersionDependencies", + "codeartifact:ListPackageVersions", + "codeartifact:ListPackages", + "codeartifact:ReadFromRepository" + ], + principals=principals, + resources=['*'], + ) + ] + ) + domain = codeartifact.CfnDomain(self, 'CodeArtifactDomain', **domain_dict) npm_repo = codeartifact.CfnRepository( self, @@ -57,6 +78,7 @@ def __init__( external_connections=[ 'public:pypi', ], + permissions_policy_document=pip_repo_policy ) pip_repo.add_override('Properties.DomainName', domain.domain_name) pip_repo.add_depends_on(domain) @@ -64,3 +86,15 @@ def __init__( self.domain = domain self.npm_repo = npm_repo self.pip_repo = pip_repo + + @property + def codeartifact_domain_name(self) -> str: + return self.domain.domain_name + + @property + def codeartifact_pip_repo_name(self) -> str: + return self.pip_repo.repository_name + + @property + def codeartifact_npm_repo_name(self) -> str: + return self.npm_repo.repository_name \ No newline at end of file diff --git a/deploy/stacks/container.py b/deploy/stacks/container.py index 997ad5d76..be002fd30 100644 --- a/deploy/stacks/container.py +++ b/deploy/stacks/container.py @@ -19,7 +19,7 @@ def __init__( scope, id, vpc: ec2.Vpc = None, - vpc_endpoints_sg: ec2.SecurityGroup = None, + vpce_connection: ec2.Connections = None, envname='dev', resource_prefix='dataall', ecr_repository=None, @@ -27,6 +27,8 @@ def __init__( prod_sizing=False, pivot_role_name=None, tooling_account_id=None, + s3_prefix_list=None, + lambdas=None, **kwargs, ): super().__init__(scope, id, **kwargs) @@ -35,8 +37,16 @@ def __init__( image_tag = self.node.try_get_context('image_tag') cdkproxy_image_tag = f'cdkproxy-{image_tag}' - - self.ecs_security_groups: [aws_ec2.SecurityGroup] = [] + + (self.scheduled_tasks_sg, self.share_manager_sg) = self.create_ecs_security_groups( + envname, + resource_prefix, + vpc, + vpce_connection, + s3_prefix_list, + lambdas + ) + self.ecs_security_groups: [aws_ec2.SecurityGroup] = [self.scheduled_tasks_sg, self.share_manager_sg] cluster = ecs.Cluster( self, @@ -49,52 +59,88 @@ def __init__( self.task_role = self.create_task_role(envname, resource_prefix, pivot_role_name) self.cicd_stacks_updater_role = self.create_cicd_stacks_updater_role(envname, resource_prefix, tooling_account_id) - cdkproxy_task_definition = ecs.FargateTaskDefinition( + cdkproxy_container_name = f'container' + cdkproxy_log_group = self.create_log_group( + envname, resource_prefix, log_group_name='cdkproxy' + ) + cdkproxy_image = ecs.ContainerImage.from_ecr_repository( + repository=ecr_repository, + tag=cdkproxy_image_tag + ) + + cdkproxy_task_definition = ecs.CfnTaskDefinition( self, f'{resource_prefix}-{envname}-cdkproxy', - cpu=1024, - memory_limit_mib=2048, - task_role=self.task_role, - execution_role=self.task_role, + container_definitions=[ecs.CfnTaskDefinition.ContainerDefinitionProperty( + image=cdkproxy_image.image_name, + name=cdkproxy_container_name, + command=['python3.8', '-m', 'dataall.tasks.cdkproxy'], + environment=[ + ecs.CfnTaskDefinition.KeyValuePairProperty( + name="AWS_REGION", + value=self.region + ), + ecs.CfnTaskDefinition.KeyValuePairProperty( + name="envname", + value=envname + ), + ecs.CfnTaskDefinition.KeyValuePairProperty( + name="LOGLEVEL", + value="DEBUG" + ), + ], + essential=True, + log_configuration=ecs.CfnTaskDefinition.LogConfigurationProperty( + log_driver="awslogs", + options={ + "awslogs-group": cdkproxy_log_group.log_group_name, + "awslogs-region": self.region, + "awslogs-stream-prefix": "task" + }, + ), + mount_points=[ + ecs.CfnTaskDefinition.MountPointProperty( + container_path="/dataall", + read_only=False, + source_volume="dataall_scratch" + ), + ecs.CfnTaskDefinition.MountPointProperty( + container_path="/tmp", + read_only=False, + source_volume="dataall_tmp_scratch" + ) + ], + readonly_root_filesystem=True, + )], + cpu="1024", + memory="2048", + execution_role_arn=self.task_role.role_arn, family=f'{resource_prefix}-{envname}-cdkproxy', - ) - - cdkproxy_container = cdkproxy_task_definition.add_container( - f'ShareManagementTaskContainer{envname}', - container_name=f'container', - image=ecs.ContainerImage.from_ecr_repository( - repository=ecr_repository, tag=cdkproxy_image_tag - ), - environment={ - 'AWS_REGION': self.region, - 'envname': envname, - 'LOGLEVEL': 'DEBUG', - }, - command=['python3.8', '-m', 'dataall.tasks.cdkproxy'], - logging=ecs.LogDriver.aws_logs( - stream_prefix='task', - log_group=self.create_log_group( - envname, resource_prefix, log_group_name='cdkproxy' + requires_compatibilities=[ecs.Compatibility.FARGATE.name], + task_role_arn=self.task_role.role_arn, + network_mode="awsvpc", + volumes=[ + ecs.CfnTaskDefinition.VolumeProperty( + name="dataall_scratch" ), - ), + ecs.CfnTaskDefinition.VolumeProperty( + name="dataall_tmp_scratch" + ) + ] ) ssm.StringParameter( self, f'CDKProxyTaskDefParam{envname}', parameter_name=f'/dataall/{envname}/ecs/task_def_arn/cdkproxy', - string_value=cdkproxy_task_definition.task_definition_arn, + string_value=cdkproxy_task_definition.attr_task_definition_arn, ) ssm.StringParameter( self, f'CDKProxyContainerParam{envname}', parameter_name=f'/dataall/{envname}/ecs/container/cdkproxy', - string_value=cdkproxy_container.container_name, - ) - - scheduled_tasks_sg = self.create_task_sg( - envname, resource_prefix, vpc, vpc_endpoints_sg + string_value=cdkproxy_container_name, ) sync_tables_task, sync_tables_task_def = self.set_scheduled_task( @@ -116,10 +162,9 @@ def __init__( task_id=f'{resource_prefix}-{envname}-tables-syncer', task_role=self.task_role, vpc=vpc, - security_group=scheduled_tasks_sg, + security_group=self.scheduled_tasks_sg, prod_sizing=prod_sizing, ) - self.ecs_security_groups.extend(sync_tables_task.task.security_groups) catalog_indexer_task, catalog_indexer_task_def = self.set_scheduled_task( cluster=cluster, @@ -140,10 +185,9 @@ def __init__( task_id=f'{resource_prefix}-{envname}-catalog-indexer', task_role=self.task_role, vpc=vpc, - security_group=scheduled_tasks_sg, + security_group=self.scheduled_tasks_sg, prod_sizing=prod_sizing, ) - self.ecs_security_groups.extend(catalog_indexer_task.task.security_groups) stacks_updater, stacks_updater_task_def = self.set_scheduled_task( cluster=cluster, @@ -164,10 +208,9 @@ def __init__( task_id=f'{resource_prefix}-{envname}-stacks-updater', task_role=self.task_role, vpc=vpc, - security_group=scheduled_tasks_sg, + security_group=self.scheduled_tasks_sg, prod_sizing=prod_sizing, ) - self.ecs_security_groups.extend(stacks_updater.task.security_groups) ssm.StringParameter( self, @@ -195,12 +238,9 @@ def __init__( task_id=f'{resource_prefix}-{envname}-policies-updater', task_role=self.task_role, vpc=vpc, - security_group=scheduled_tasks_sg, + security_group=self.scheduled_tasks_sg, prod_sizing=prod_sizing, ) - self.ecs_security_groups.extend( - update_bucket_policies_task.task.security_groups - ) subscriptions_task, subscription_task_def = self.set_scheduled_task( cluster=cluster, @@ -225,10 +265,9 @@ def __init__( task_id=f'{resource_prefix}-{envname}-subscriptions', task_role=self.task_role, vpc=vpc, - security_group=scheduled_tasks_sg, + security_group=self.scheduled_tasks_sg, prod_sizing=prod_sizing, ) - self.ecs_security_groups.extend(subscriptions_task.task.security_groups) share_management_task_definition = ecs.FargateTaskDefinition( self, @@ -258,6 +297,7 @@ def __init__( envname, resource_prefix, log_group_name='share-manager' ), ), + readonly_root_filesystem=True, ) ssm.StringParameter( @@ -292,22 +332,89 @@ def __init__( ), ) + self.ecs_cluster = cluster + self.ecs_task_definitions_families = [ + cdkproxy_task_definition.family, + sync_tables_task.task_definition.family, + update_bucket_policies_task.task_definition.family, + catalog_indexer_task.task_definition.family, + share_management_task_definition.family, + subscriptions_task.task_definition.family, + ] + + def create_ecs_security_groups(self, envname, resource_prefix, vpc, vpce_connection, s3_prefix_list, lambdas): + scheduled_tasks_sg = ec2.SecurityGroup( + self, + f'ScheduledTasksSG{envname}', + security_group_name=f'{resource_prefix}-{envname}-ecs-tasks-sg', + vpc=vpc, + allow_all_outbound=False, + disable_inline_rules=True, + ) + + # Requires RAM Access via NAT + share_manager_sg = ec2.SecurityGroup( + self, + f'ShareManagerSG{envname}', + security_group_name=f'{resource_prefix}-{envname}-ecs-share-manager-tasks-sg', + vpc=vpc, + allow_all_outbound=False, + disable_inline_rules=True, + ) + + for sg in [scheduled_tasks_sg,share_manager_sg]: + sg_connection = ec2.Connections(security_groups=[sg]) + # Add ECS to VPC Endpoint Connection + if vpce_connection: + sg_connection.allow_to( + vpce_connection, + ec2.Port.tcp(443), + 'Allow ECS to VPC Endpoint SG' + ) + sg_connection.allow_from( + vpce_connection, + ec2.Port.tcp_range(start_port=1024, end_port=65535), + 'Allow ECS from VPC Endpoint SG' + ) + # Add S3 Prefix List Connection + if s3_prefix_list: + sg_connection.allow_to( + ec2.Connections(peer=ec2.Peer.prefix_list(s3_prefix_list)), + ec2.Port.tcp(443), + 'Allow ECS Task to S3 Prefix List' + ) + + # Add Lambda to ECS Connection + if lambdas: + for l in lambdas: + sg_connection.connections.allow_from( + l.connections, + ec2.Port.tcp(443), + 'Allow Lambda to ECS Connection' + ) + + # Add NAT Gateway Access for Cross-region requests in same region the more specific rules apply + sg_connection.allow_to( + ec2.Peer.any_ipv4(), + ec2.Port.tcp(443), + 'Allow NAT Internet Access SG Egress' + ) + + # Create SSM of Security Group IDs ssm.StringParameter( self, f'SecurityGroup{envname}', parameter_name=f'/dataall/{envname}/ecs/security_groups', - string_value=','.join([s.security_group_id for s in sync_tables_task.task.security_groups]), + string_value=scheduled_tasks_sg.security_group_id, + ) + ssm.StringParameter( + self, + f'SecurityGroupShareManager{envname}', + parameter_name=f'/dataall/{envname}/ecs/sharemanager_security_groups', + string_value=share_manager_sg.security_group_id, ) - self.ecs_cluster = cluster - self.ecs_task_definitions = [ - cdkproxy_task_definition, - sync_tables_task.task_definition, - update_bucket_policies_task.task_definition, - catalog_indexer_task.task_definition, - share_management_task_definition, - subscriptions_task.task_definition, - ] + return scheduled_tasks_sg, share_manager_sg def create_cicd_stacks_updater_role(self, envname, resource_prefix, tooling_account_id): cicd_stacks_updater_role = iam.Role( @@ -446,31 +553,6 @@ def create_task_role(self, envname, resource_prefix, pivot_role_name): task_role.grant_pass_role(task_role) return task_role - def create_task_sg(self, envname, resource_prefix, vpc, vpc_endpoints_sg): - if vpc_endpoints_sg: - scheduled_tasks_sg = ec2.SecurityGroup( - self, - f'ScheduledTasksSG{envname}', - security_group_name=f'{resource_prefix}-{envname}-ecs-tasks-sg', - vpc=vpc, - allow_all_outbound=False, - ) - - scheduled_tasks_sg.add_egress_rule( - peer=vpc_endpoints_sg, - connection=ec2.Port.tcp(443), - description='Allow VPC Endpoint SG Egress', - ) - else: - scheduled_tasks_sg = ec2.SecurityGroup( - self, - f'ScheduledTasksSG{envname}', - security_group_name=f'{resource_prefix}-{envname}-ecs-tasks-sg', - vpc=vpc, - allow_all_outbound=True, - ) - return scheduled_tasks_sg - def create_log_group(self, envname, resource_prefix, log_group_name): log_group = logs.LogGroup( self, @@ -544,6 +626,7 @@ def set_scheduled_task( environment=environment, command=command, logging=ecs.LogDriver.aws_logs(stream_prefix='task', log_group=log_group), + readonly_root_filesystem=True, ) scheduled_task = ecs_patterns.ScheduledFargateTask( self, @@ -559,8 +642,8 @@ def set_scheduled_task( subnet_type=ec2.SubnetType.PRIVATE_WITH_NAT ).subnets ), - rule_name=scheduled_task_id - # security_groups=[security_group], + rule_name=scheduled_task_id, + security_groups=[security_group], ) return scheduled_task, task diff --git a/deploy/stacks/dbmigration.py b/deploy/stacks/dbmigration.py index f48712795..d71320ebe 100644 --- a/deploy/stacks/dbmigration.py +++ b/deploy/stacks/dbmigration.py @@ -11,10 +11,14 @@ def __init__( scope, id, vpc, + s3_prefix_list=None, envname='dev', resource_prefix='dataall', pipeline_bucket: str = None, tooling_account_id=None, + codeartifact_domain_name=None, + codeartifact_pip_repo_name=None, + vpce_connection=None, **kwargs, ): super().__init__(scope, id, **kwargs) @@ -82,13 +86,56 @@ def __init__( ], ), ) + self.build_project_role.add_to_policy( + iam.PolicyStatement( + actions=[ + "codeartifact:GetAuthorizationToken", + "codeartifact:ReadFromRepository", + "codeartifact:GetRepositoryEndpoint", + "codeartifact:GetRepositoryPermissionsPolicy" + ], + resources=[ + f"arn:aws:codeartifact:*:{tooling_account_id}:repository/{codeartifact_domain_name}/{codeartifact_pip_repo_name}", + f"arn:aws:codeartifact:*:{tooling_account_id}:domain/{codeartifact_domain_name}", + ], + ), + ) + self.build_project_role.add_to_policy( + iam.PolicyStatement( + actions=[ + 'sts:GetServiceBearerToken' + ], + resources=['*'], + conditions={ + 'StringEquals': {'sts:AWSServiceName': 'codeartifact.amazonaws.com'} + }, + ), + ) self.codebuild_sg = ec2.SecurityGroup( self, f'DBMigrationCBSG{envname}', security_group_name=f'{resource_prefix}-{envname}-cb-dbmigration-sg', vpc=vpc, - allow_all_outbound=True, + allow_all_outbound=False, + disable_inline_rules=True ) + sg_connection = ec2.Connections(security_groups=[self.codebuild_sg]) + sg_connection.allow_to( + vpce_connection, + ec2.Port.tcp(443), + 'Allow DB Migration CodeBuild to VPC Endpoint SG' + ) + sg_connection.allow_from( + vpce_connection, + ec2.Port.tcp_range(start_port=1024, end_port=65535), + 'Allow DB Migration CodeBuild from VPC Endpoint' + ) + sg_connection.allow_to( + ec2.Connections(peer=ec2.Peer.prefix_list(s3_prefix_list)), + ec2.Port.tcp(443), + 'Allow DB Migration CodeBuild to S3 Prefix List' + ) + self.db_migration_project = codebuild.Project( scope=self, id=f'DBMigrationCBProject{envname}', @@ -107,6 +154,7 @@ def __init__( 'unzip source_build.zip', 'python -m venv env', '. env/bin/activate', + f'aws codeartifact login --tool pip --domain {codeartifact_domain_name} --domain-owner {tooling_account_id} --repository {codeartifact_pip_repo_name}', 'pip install -r backend/requirements.txt', 'pip install alembic', 'export PYTHONPATH=backend', diff --git a/deploy/stacks/lambda_api.py b/deploy/stacks/lambda_api.py index 95c670f78..f4ff3bb5c 100644 --- a/deploy/stacks/lambda_api.py +++ b/deploy/stacks/lambda_api.py @@ -36,6 +36,7 @@ def __init__( envname='dev', resource_prefix='dataall', vpc=None, + vpce_connection=None, sqs_queue: sqs.Queue = None, ecr_repository=None, image_tag=None, @@ -55,7 +56,9 @@ def __init__( image_tag = f'lambdas-{image_tag}' + self.esproxy_dlq = self.set_dlq(f'{resource_prefix}-{envname}-esproxy-dlq') + esproxy_sg = self.create_lambda_sgs(envname, "esproxy", resource_prefix, vpc) self.elasticsearch_proxy_handler = _lambda.DockerImageFunction( self, 'ElasticSearchProxyHandler', @@ -66,6 +69,7 @@ def __init__( repository=ecr_repository, tag=image_tag, cmd=['search_handler.handler'] ), vpc=vpc, + security_groups=[esproxy_sg], memory_size=1664 if prod_sizing else 256, timeout=Duration.minutes(15), environment={'envname': envname, 'LOG_LEVEL': 'INFO'}, @@ -76,6 +80,7 @@ def __init__( ) self.api_handler_dlq = self.set_dlq(f'{resource_prefix}-{envname}-graphql-dlq') + api_handler_sg = self.create_lambda_sgs(envname, "apihandler", resource_prefix, vpc) self.api_handler = _lambda.DockerImageFunction( self, 'LambdaGraphQL', @@ -86,6 +91,7 @@ def __init__( repository=ecr_repository, tag=image_tag, cmd=['api_handler.handler'] ), vpc=vpc, + security_groups=[api_handler_sg], memory_size=3008 if prod_sizing else 1024, timeout=Duration.minutes(15), environment={'envname': envname, 'LOG_LEVEL': 'INFO'}, @@ -96,6 +102,7 @@ def __init__( ) self.aws_handler_dlq = self.set_dlq(f'{resource_prefix}-{envname}-awsworker-dlq') + awsworker_sg = self.create_lambda_sgs(envname, "awsworker", resource_prefix, vpc) self.aws_handler = _lambda.DockerImageFunction( self, 'AWSWorker', @@ -109,6 +116,7 @@ def __init__( memory_size=1664 if prod_sizing else 256, timeout=Duration.minutes(15), vpc=vpc, + security_groups=[awsworker_sg], dead_letter_queue_enabled=True, dead_letter_queue=self.aws_handler_dlq, on_failure=lambda_destination.SqsDestination(self.aws_handler_dlq), @@ -121,6 +129,36 @@ def __init__( ) ) + # Add VPC Endpoint Connectivity + if vpce_connection: + for lmbda in [ + self.aws_handler, + self.api_handler, + self.elasticsearch_proxy_handler, + ]: + lmbda.connections.allow_from( + vpce_connection, + ec2.Port.tcp_range(start_port=1024, end_port=65535), + 'Allow Lambda from VPC Endpoint' + ) + lmbda.connections.allow_to( + vpce_connection, + ec2.Port.tcp(443), + 'Allow Lambda to VPC Endpoint' + ) + + # Add NAT Connectivity For API Handler + self.api_handler.connections.allow_to( + ec2.Peer.any_ipv4(), + ec2.Port.tcp(443), + 'Allow NAT Internet Access SG Egress' + ) + self.aws_handler.connections.allow_to( + ec2.Peer.any_ipv4(), + ec2.Port.tcp(443), + 'Allow NAT Internet Access SG Egress' + ) + self.backend_api_name = f'{resource_prefix}-{envname}-api' self.graphql_api, self.acl = self.create_api_gateway( @@ -141,6 +179,17 @@ def __init__( param_name='backend_sns_topic_arn', topic_name=f'{resource_prefix}-{envname}-backend-topic', ) + + def create_lambda_sgs(self, envname, name, resource_prefix, vpc): + lambda_sg = ec2.SecurityGroup( + self, + f'{name}SG{envname}', + security_group_name=f'{resource_prefix}-{envname}-{name}-sg', + vpc=vpc, + allow_all_outbound=False, + disable_inline_rules=True, + ) + return lambda_sg def create_function_role(self, envname, resource_prefix, fn_name, pivot_role_name): diff --git a/deploy/stacks/monitoring.py b/deploy/stacks/monitoring.py index f6cd4f9f7..001e4b361 100644 --- a/deploy/stacks/monitoring.py +++ b/deploy/stacks/monitoring.py @@ -27,7 +27,7 @@ def __init__( lambdas: [_lambda.Function] = None, database='dataalldevdb', ecs_cluster: ecs.Cluster = None, - ecs_task_definitions: [ecs.FargateTaskDefinition] = None, + ecs_task_definitions_families = None, backend_api=None, queue_name: str = None, **kwargs, @@ -51,7 +51,7 @@ def __init__( backend_api, database, ecs_cluster, - ecs_task_definitions, + ecs_task_definitions_families, envname, lambdas, resource_prefix, @@ -136,7 +136,7 @@ def create_cw_dashboard( backend_api, database, ecs_cluster, - ecs_task_definitions, + ecs_task_definitions_families, envname, lambdas, resource_prefix, @@ -173,19 +173,18 @@ def create_cw_dashboard( cf_ecs.build_ecs_cluster_task_count_widget(cluster_name), ) - if ecs_task_definitions: + if ecs_task_definitions_families: dashboard.add_widgets(cw.TextWidget(width=24, markdown='# ECS Tasks')) - task: ecs.FargateTaskDefinition - for task in ecs_task_definitions: + for task_family in ecs_task_definitions_families: dashboard.add_widgets( cf_ecs.build_ecs_task_container_insight_cpu_widget( - cluster_name, task.family + cluster_name, task_family ), cf_ecs.build_ecs_task_container_insight_memory_widget( - cluster_name, task.family + cluster_name, task_family ), cf_ecs.build_ecs_task_container_insight_storage_widget( - cluster_name, task.family + cluster_name, task_family ), ) if database: diff --git a/deploy/stacks/opensearch.py b/deploy/stacks/opensearch.py index 51ebbbaf2..118aff944 100644 --- a/deploy/stacks/opensearch.py +++ b/deploy/stacks/opensearch.py @@ -33,27 +33,9 @@ def __init__( security_group_name=f'{resource_prefix}-{envname}-elasticsearch-sg', vpc=vpc, allow_all_outbound=False, + disable_inline_rules=True, ) - if lambdas: - l: _lambda.Function - for l in lambdas: - sgs = l.connections.security_groups - for i, sg in enumerate(sgs): - db_security_group.add_ingress_rule( - peer=sg, - connection=ec2.Port.tcp(443), - description=f'Allow dataall lambda {l.function_name}', - ) - - if ecs_security_groups: - for sg in ecs_security_groups: - db_security_group.add_ingress_rule( - peer=sg, - connection=ec2.Port.tcp(443), - description=f'Allow dataall ECS cluster tasks', - ) - key = aws_kms.Key( self, f'ESKMSKey', @@ -115,6 +97,24 @@ def __init__( ], ) + if lambdas: + l: _lambda.Function + for l in lambdas: + self.domain.connections.allow_from( + l.connections, + ec2.Port.tcp(443), + f'Allow dataall opensearch to lambda {l.function_name}', + ) + + if ecs_security_groups: + for sg in ecs_security_groups: + sg_connection = ec2.Connections(security_groups=[sg]) + self.domain.connections.allow_from( + sg_connection, + ec2.Port.tcp(443), + f'Allow dataall opensearch to ecs sg', + ) + ssm.StringParameter( self, 'ElasticSearchEndpointParameter', diff --git a/deploy/stacks/param_store_stack.py b/deploy/stacks/param_store_stack.py index 2419d1f66..b2991495b 100644 --- a/deploy/stacks/param_store_stack.py +++ b/deploy/stacks/param_store_stack.py @@ -1,5 +1,10 @@ +import random +import string + +import boto3 from aws_cdk import ( aws_ssm, + SecretValue ) from .pyNestedStack import pyNestedClass @@ -17,6 +22,7 @@ def __init__( quicksight_enabled=False, shared_dashboard_sessions='anonymous', enable_pivot_role_auto_create=False, + pivot_role_name='dataallPivotRole', **kwargs, ): super().__init__(scope, id, **kwargs) @@ -87,4 +93,59 @@ def __init__( f'dataallCreationPivotRole{envname}', parameter_name=f"/dataall/{envname}/pivotRole/enablePivotRoleAutoCreate", string_value=str(enable_pivot_role_auto_create), - ) \ No newline at end of file + ) + + aws_ssm.StringParameter( + self, + f'dataallPivotRoleName{envname}', + parameter_name=f"/dataall/{envname}/pivotRole/pivotRoleName", + string_value=str(pivot_role_name), + description=f"Stores dataall pivot role name for environment {envname}", + ) + + existing_external_id = _get_external_id_value(envname=envname, account_id=self.account, region=self.region) + external_id_value = existing_external_id if existing_external_id else _generate_external_id() + + aws_ssm.StringParameter( + self, + f'dataallExternalId{envname}', + parameter_name=f"/dataall/{envname}/pivotRole/externalId", + string_value=str(external_id_value), + description=f"Stores dataall external id for environment {envname}", + ) + +def _get_external_id_value(envname, account_id, region): + """ + For first deployments and upgrades from <=V1.5.6 to >=v1.6 - returns False and a new ssm parameter created, + For existing >=v1.6 deployments - returns the ssm parameter value generated in the first deployment + """ + cdk_look_up_role = 'arn:aws:iam::{}:role/cdk-hnb659fds-lookup-role-{}-{}'.format(account_id, account_id, region) + base_session = boto3.Session() + assume_role_dict = dict( + RoleArn=cdk_look_up_role, + RoleSessionName=cdk_look_up_role.split('/')[1], + ) + sts = base_session.client( + 'sts', + region_name=region, + endpoint_url=f"https://sts.{region}.amazonaws.com" + ) + parameter_path = f"/dataall/{envname}/pivotRole/externalId" + + try: + response = sts.assume_role(**assume_role_dict) + session = boto3.Session( + aws_access_key_id=response['Credentials']['AccessKeyId'], + aws_secret_access_key=response['Credentials']['SecretAccessKey'], + aws_session_token=response['Credentials']['SessionToken'], + ) + ssm_client = session.client('ssm', region_name=region) + parameter_value = ssm_client.get_parameter(Name=parameter_path)['Parameter']['Value'] + return parameter_value + except: + return False + + +def _generate_external_id(): + allowed_chars = string.ascii_uppercase + string.ascii_lowercase + string.digits + return ''.join(random.choice(allowed_chars) for i in range(32)) \ No newline at end of file diff --git a/deploy/stacks/pipeline.py b/deploy/stacks/pipeline.py index ea5b2128a..e6159055c 100644 --- a/deploy/stacks/pipeline.py +++ b/deploy/stacks/pipeline.py @@ -32,7 +32,6 @@ def __init__( **kwargs, ): super().__init__(id, scope, **kwargs) - self.validate_deployment_params(git_branch, resource_prefix, target_envs) self.git_branch = git_branch self.source = source @@ -82,67 +81,8 @@ def __init__( resource_prefix=self.resource_prefix, ) - self.codebuild_policy = [ - iam.PolicyStatement( - actions=[ - 'sts:GetServiceBearerToken', - ], - resources=['*'], - conditions={ - 'StringEquals': {'sts:AWSServiceName': 'codeartifact.amazonaws.com'} - }, - ), - iam.PolicyStatement( - actions=[ - 'ecr:GetAuthorizationToken', - ], - resources=['*'], - ), - iam.PolicyStatement( - actions=[ - 'codeartifact:GetAuthorizationToken', - 'codeartifact:GetRepositoryEndpoint', - 'codeartifact:ReadFromRepository', - 'ecr:GetDownloadUrlForLayer', - 'ecr:BatchGetImage', - 'ecr:BatchCheckLayerAvailability', - 'ecr:PutImage', - 'ecr:InitiateLayerUpload', - 'ecr:UploadLayerPart', - 'ecr:CompleteLayerUpload', - 'ecr:GetDownloadUrlForLayer', - 'kms:Decrypt', - 'kms:Encrypt', - 'kms:GenerateDataKey', - 'secretsmanager:GetSecretValue', - 'secretsmanager:DescribeSecret', - 'ssm:GetParametersByPath', - 'ssm:GetParameters', - 'ssm:GetParameter', - 's3:Get*', - 's3:Put*', - 's3:List*', - 'codebuild:CreateReportGroup', - 'codebuild:CreateReport', - 'codebuild:UpdateReport', - 'codebuild:BatchPutTestCases', - 'codebuild:BatchPutCodeCoverages', - ], - resources=[ - f'arn:aws:s3:::{self.resource_prefix}*', - f'arn:aws:s3:::{self.resource_prefix}*/*', - f'arn:aws:codebuild:{self.region}:{self.account}:project/*{self.resource_prefix}*', - f'arn:aws:secretsmanager:{self.region}:{self.account}:secret:*{resource_prefix}*', - f'arn:aws:secretsmanager:{self.region}:{self.account}:secret:*dataall*', - f'arn:aws:kms:{self.region}:{self.account}:key/*', - f'arn:aws:ssm:*:{self.account}:parameter/*dataall*', - f'arn:aws:ssm:*:{self.account}:parameter/*{resource_prefix}*', - f'arn:aws:ecr:{self.region}:{self.account}:repository/{resource_prefix}*', - f'arn:aws:codeartifact:{self.region}:{self.account}:repository/{resource_prefix}*', - f'arn:aws:codeartifact:{self.region}:{self.account}:domain/{resource_prefix}*', - ], - ), - ] + self.set_codebuild_iam_roles() + self.pipeline_bucket_name = f'{self.resource_prefix}-{self.git_branch}-code-{self.account}-{self.region}' self.pipeline_bucket = s3.Bucket( self, @@ -171,19 +111,6 @@ def __init__( ) self.pipeline_bucket.grant_read_write(iam.AccountPrincipal(self.account)) - self.pipeline_iam_role = iam.Role( - self, - id=f'CDKPipelinesRole{self.git_branch}', - role_name=f'{self.resource_prefix}-{self.git_branch}-cdkpipelines-role', - assumed_by=iam.CompositePrincipal( - iam.ServicePrincipal('codebuild.amazonaws.com'), - iam.ServicePrincipal('codepipeline.amazonaws.com'), - iam.AccountPrincipal(self.account), - ), - ) - for policy in self.codebuild_policy: - self.pipeline_iam_role.add_to_policy(policy) - if self.source == 'github': source = CodePipelineSource.git_hub( repo_string='awslabs/aws-dataall', @@ -209,14 +136,14 @@ def __init__( build_image=codebuild.LinuxBuildImage.AMAZON_LINUX_2_4, ), commands=[ - f'aws codeartifact login --tool npm --repository {self.codeartifact.npm_repo.attr_name} --domain {self.codeartifact.domain.attr_name} --domain-owner {self.codeartifact.domain.attr_owner}', + f'aws codeartifact login --tool npm --repository {self.codeartifact.codeartifact_npm_repo_name} --domain {self.codeartifact.codeartifact_domain_name} --domain-owner {self.codeartifact.domain.attr_owner}', 'npm install -g aws-cdk', - f'aws codeartifact login --tool pip --repository {self.codeartifact.pip_repo.attr_name} --domain {self.codeartifact.domain.attr_name} --domain-owner {self.codeartifact.domain.attr_owner}', + f'aws codeartifact login --tool pip --repository {self.codeartifact.codeartifact_pip_repo_name} --domain {self.codeartifact.codeartifact_domain_name} --domain-owner {self.codeartifact.domain.attr_owner}', 'pip install -r deploy/requirements.txt', 'cdk synth', 'echo ${CODEBUILD_SOURCE_VERSION}' ], - role_policy_statements=self.codebuild_policy, + role=self.baseline_codebuild_role, vpc=self.vpc, ), cross_account_keys=True, @@ -257,28 +184,6 @@ def __init__( comment=f'Approve deployment for environment {target_env["envname"]}', ) ) - self.codebuild_policy.append( - iam.PolicyStatement( - actions=[ - 'cloudfront:CreateInvalidation', - 'ssm:GetParametersByPath', - 'ssm:GetParameters', - 'ssm:GetParameter', - 's3:Get*', - 's3:Put*', - 's3:List*', - 'sts:AssumeRole', - ], - resources=[ - f'arn:aws:s3:::{self.resource_prefix}-*', - f'arn:aws:s3:::{self.resource_prefix}*/*', - f'arn:aws:ssm:*:{self.account}:parameter/*dataall*', - f'arn:aws:ssm:*:{self.account}:parameter/*{resource_prefix}*', - f'arn:aws:iam::*:role/{resource_prefix}*', - f'arn:aws:cloudfront::*:distribution/*', - ], - ), - ) self.set_db_migration_stage( target_env, @@ -288,7 +193,7 @@ def __init__( self.set_stacks_updater_stage( target_env ) - + if target_env.get('internet_facing', True): self.set_cloudfront_stage( target_env, @@ -301,6 +206,158 @@ def __init__( Tags.of(self).add('Application', f'{resource_prefix}-{git_branch}') + def set_codebuild_iam_roles(self): + # IAM Role Creation + self.baseline_codebuild_role = iam.Role( + self, + id=f'CodeBuildBaselineRole{self.git_branch}', + role_name=f'{self.resource_prefix}-{self.git_branch}-baseline-codebuild-role', + assumed_by=iam.CompositePrincipal( + iam.ServicePrincipal('codebuild.amazonaws.com'), + iam.ServicePrincipal('codepipeline.amazonaws.com'), + iam.AccountPrincipal(self.account), + ), + ) + self.expanded_codebuild_role = iam.Role( + self, + id=f'CodeBuildExpandedRole{self.git_branch}', + role_name=f'{self.resource_prefix}-{self.git_branch}-expanded-codebuild-role', + assumed_by=iam.ServicePrincipal('codebuild.amazonaws.com'), + ) + + self.baseline_codebuild_policy = iam.Policy( + self, + 'BaselineCodeBuildPolicy', + policy_name=f'{self.resource_prefix}-{self.git_branch}-baseline-codebuild-policy', + roles=[self.baseline_codebuild_role, self.expanded_codebuild_role], + statements= [ + iam.PolicyStatement( + actions=[ + 'sts:AssumeRole', + ], + resources=[ + 'arn:aws:iam::*:role/cdk-hnb659fds-lookup-role*' + ], + ), + iam.PolicyStatement( + actions=[ + 'sts:GetServiceBearerToken', + ], + resources=['*'], + conditions={ + 'StringEquals': {'sts:AWSServiceName': 'codeartifact.amazonaws.com'} + }, + ), + iam.PolicyStatement( + actions=[ + 'ecr:GetAuthorizationToken', + 'ec2:DescribePrefixLists', + 'ec2:DescribeManagedPrefixLists' + ], + resources=['*'], + ), + iam.PolicyStatement( + actions=[ + 'codeartifact:GetAuthorizationToken', + 'codeartifact:GetRepositoryEndpoint', + 'codeartifact:ReadFromRepository', + 'ecr:GetDownloadUrlForLayer', + 'ecr:BatchGetImage', + 'ecr:BatchCheckLayerAvailability', + 'ecr:PutImage', + 'ecr:InitiateLayerUpload', + 'ecr:UploadLayerPart', + 'ecr:CompleteLayerUpload', + 'ecr:GetDownloadUrlForLayer', + 'kms:Decrypt', + 'kms:Encrypt', + 'kms:GenerateDataKey', + 'secretsmanager:GetSecretValue', + 'secretsmanager:DescribeSecret', + 'ssm:GetParametersByPath', + 'ssm:GetParameters', + 'ssm:GetParameter', + 's3:Get*', + 's3:Put*', + 's3:List*', + 'codebuild:CreateReportGroup', + 'codebuild:CreateReport', + 'codebuild:UpdateReport', + 'codebuild:BatchPutTestCases', + 'codebuild:BatchPutCodeCoverages', + 'ec2:GetManagedPrefixListEntries' + ], + resources=[ + f'arn:aws:s3:::{self.resource_prefix}*', + f'arn:aws:s3:::{self.resource_prefix}*/*', + f'arn:aws:codebuild:{self.region}:{self.account}:project/*{self.resource_prefix}*', + f'arn:aws:secretsmanager:{self.region}:{self.account}:secret:*{self.resource_prefix}*', + f'arn:aws:secretsmanager:{self.region}:{self.account}:secret:*dataall*', + f'arn:aws:kms:{self.region}:{self.account}:key/*', + f'arn:aws:ssm:*:{self.account}:parameter/*dataall*', + f'arn:aws:ssm:*:{self.account}:parameter/*{self.resource_prefix}*', + f'arn:aws:ecr:{self.region}:{self.account}:repository/{self.resource_prefix}*', + f'arn:aws:codeartifact:{self.region}:{self.account}:repository/{self.resource_prefix}*', + f'arn:aws:codeartifact:{self.region}:{self.account}:domain/{self.resource_prefix}*', + f'arn:aws:ec2:{self.region}:{self.account}:prefix-list/*', + ], + ), + ], + ) + self.expanded_codebuild_policy = iam.Policy( + self, + 'ExpandedCodeBuildPolicy', + policy_name=f'{self.resource_prefix}-{self.git_branch}-expanded-codebuild-policy', + roles=[self.expanded_codebuild_role], + statements= [ + iam.PolicyStatement( + actions=[ + 'cloudfront:CreateInvalidation', + 'sts:AssumeRole', + ], + resources=[ + f'arn:aws:iam::*:role/{self.resource_prefix}*', + f'arn:aws:cloudfront::*:distribution/*', + ], + ) + ], + ) + if self.node.try_get_context('git_release'): + self.git_project_role = iam.Role( + self, + id=f'GitReleaseCBRole{self.git_branch}', + role_name=f'{self.resource_prefix}-{self.git_branch}-git-release-role', + assumed_by=iam.CompositePrincipal( + iam.ServicePrincipal('codebuild.amazonaws.com'), + iam.AccountPrincipal(self.account), + ), + ) + self.expanded_codebuild_policy.attach_to_role(self.git_project_role) + self.baseline_codebuild_policy.attach_to_role(self.git_project_role) + self.git_release_policy = iam.Policy( + self, + 'GitReleasePolicy', + policy_name=f'{self.resource_prefix}-{self.git_branch}-git-release-policy', + roles=[self.git_project_role], + statements= [ + iam.PolicyStatement( + actions=[ + 'codecommit:CreateBranch', + 'codecommit:GetCommit', + 'codecommit:ListBranches', + 'codecommit:GetRepository', + 'codecommit:GetBranch', + 'codecommit:GitPull', + 'codecommit:PutFile', + 'codecommit:CreateCommit', + 'codecommit:GitPush', + 'codecommit:ListTagsForResource', + ], + resources=[f'arn:aws:codecommit:{self.region}:{self.account}:dataall'], + ) + ], + ) + def validate_deployment_params(self, git_branch, resource_prefix, target_envs): if not bool(re.match(r'^[a-zA-Z0-9-_]+$', git_branch)): raise ValueError( @@ -343,17 +400,6 @@ def validate_deployment_params(self, git_branch, resource_prefix, target_envs): def set_quality_gate_stage(self): quality_gate_param = self.node.try_get_context('quality_gate') if quality_gate_param is not False: - it_project_role = iam.Role( - self, - id=f'ItCobdeBuildRole{self.git_branch}', - role_name=f'{self.resource_prefix}-{self.git_branch}-integration-tests-role', - assumed_by=iam.CompositePrincipal( - iam.ServicePrincipal('codebuild.amazonaws.com'), - iam.AccountPrincipal(self.account), - ), - ) - for policy in self.codebuild_policy: - it_project_role.add_to_policy(policy) gate_quality_wave = self.pipeline.add_wave('QualityGate') gate_quality_wave.add_pre( pipelines.CodeBuildStep( @@ -362,7 +408,7 @@ def set_quality_gate_stage(self): build_image=codebuild.LinuxBuildImage.AMAZON_LINUX_2_4, ), commands=[ - f'aws codeartifact login --tool pip --repository {self.codeartifact.pip_repo.attr_name} --domain {self.codeartifact.domain.attr_name} --domain-owner {self.codeartifact.domain.attr_owner}', + f'aws codeartifact login --tool pip --repository {self.codeartifact.codeartifact_pip_repo_name} --domain {self.codeartifact.codeartifact_domain_name} --domain-owner {self.codeartifact.domain.attr_owner}', f'export envname={self.git_branch}', f'export schema_name=validation', 'python -m venv env', @@ -370,7 +416,7 @@ def set_quality_gate_stage(self): 'make drop-tables', 'make upgrade-db', ], - role_policy_statements=self.codebuild_policy, + role=self.baseline_codebuild_role, vpc=self.vpc, security_groups=[self.codebuild_sg], ), @@ -380,13 +426,13 @@ def set_quality_gate_stage(self): build_image=codebuild.LinuxBuildImage.AMAZON_LINUX_2_4, ), commands=[ - f'aws codeartifact login --tool pip --repository {self.codeartifact.pip_repo.attr_name} --domain {self.codeartifact.domain.attr_name} --domain-owner {self.codeartifact.domain.attr_owner}', + f'aws codeartifact login --tool pip --repository {self.codeartifact.codeartifact_pip_repo_name} --domain {self.codeartifact.codeartifact_domain_name} --domain-owner {self.codeartifact.domain.attr_owner}', 'pip install --upgrade pip', 'python -m venv env', '. env/bin/activate', 'make check-security', ], - role_policy_statements=self.codebuild_policy, + role=self.baseline_codebuild_role, vpc=self.vpc, ), pipelines.CodeBuildStep( @@ -395,17 +441,17 @@ def set_quality_gate_stage(self): build_image=codebuild.LinuxBuildImage.AMAZON_LINUX_2_4, ), commands=[ - f'aws codeartifact login --tool pip --repository {self.codeartifact.pip_repo.attr_name} --domain {self.codeartifact.domain.attr_name} --domain-owner {self.codeartifact.domain.attr_owner}', + f'aws codeartifact login --tool pip --repository {self.codeartifact.codeartifact_pip_repo_name} --domain {self.codeartifact.codeartifact_domain_name} --domain-owner {self.codeartifact.domain.attr_owner}', 'pip install --upgrade pip', 'python -m venv env', '. env/bin/activate', 'make lint', 'cd frontend', - f'aws codeartifact login --tool npm --repository {self.codeartifact.npm_repo.attr_name} --domain {self.codeartifact.domain.attr_name} --domain-owner {self.codeartifact.domain.attr_owner}', + f'aws codeartifact login --tool npm --repository {self.codeartifact.codeartifact_npm_repo_name} --domain {self.codeartifact.codeartifact_domain_name} --domain-owner {self.codeartifact.domain.attr_owner}', 'npm install', 'npm run lint', ], - role_policy_statements=self.codebuild_policy, + role=self.baseline_codebuild_role, vpc=self.vpc, ), ) @@ -422,7 +468,7 @@ def set_quality_gate_stage(self): 'build': { 'commands': [ 'set -eu', - f'aws codeartifact login --tool pip --repository {self.codeartifact.pip_repo.attr_name} --domain {self.codeartifact.domain.attr_name} --domain-owner {self.codeartifact.domain.attr_owner}', + f'aws codeartifact login --tool pip --repository {self.codeartifact.codeartifact_pip_repo_name} --domain {self.codeartifact.codeartifact_domain_name} --domain-owner {self.codeartifact.domain.attr_owner}', f'export envname={self.git_branch}', 'python -m venv env', '. env/bin/activate', @@ -440,7 +486,7 @@ def set_quality_gate_stage(self): ) ), commands=[], - role=it_project_role, + role=self.baseline_codebuild_role, vpc=self.vpc, security_groups=[self.codebuild_sg], ), @@ -455,24 +501,12 @@ def set_quality_gate_stage(self): 'cd source_build/ && zip -r ../source_build/source_build.zip *', f'aws s3api put-object --bucket {self.pipeline_bucket.bucket_name} --key source_build.zip --body source_build.zip', ], - role_policy_statements=self.codebuild_policy, + role=self.baseline_codebuild_role, vpc=self.vpc, security_groups=[self.codebuild_sg], ), ) else: - it_project_role = iam.Role( - self, - id=f'ItCobdeBuildRole{self.git_branch}', - role_name=f'{self.resource_prefix}-{self.git_branch}-integration-tests-role', - assumed_by=iam.CompositePrincipal( - iam.ServicePrincipal('codebuild.amazonaws.com'), - iam.AccountPrincipal(self.account), - ), - ) - for policy in self.codebuild_policy: - it_project_role.add_to_policy(policy) - gate_quality_wave = self.pipeline.add_wave('UploadCodeToS3') gate_quality_wave.add_pre( pipelines.CodeBuildStep( @@ -486,7 +520,7 @@ def set_quality_gate_stage(self): 'cd source_build/ && zip -r ../source_build/source_build.zip *', f'aws s3api put-object --bucket {self.pipeline_bucket.bucket_name} --key source_build.zip --body source_build.zip', ], - role_policy_statements=self.codebuild_policy, + role=self.baseline_codebuild_role, vpc=self.vpc, security_groups=[self.codebuild_sg], ), @@ -527,7 +561,7 @@ def set_ecr_stage( commands=[ f"make deploy-image type=lambda image-tag=$IMAGE_TAG account={target_env['account']} region={target_env['region']} repo={repository_name}", ], - role_policy_statements=self.codebuild_policy, + role=self.baseline_codebuild_role, vpc=self.vpc, ), pipelines.CodeBuildStep( @@ -545,7 +579,7 @@ def set_ecr_stage( commands=[ f"make deploy-image type=ecs image-tag=$IMAGE_TAG account={target_env['account']} region={target_env['region']} repo={repository_name}", ], - role_policy_statements=self.codebuild_policy, + role=self.baseline_codebuild_role, vpc=self.vpc, ), ) @@ -568,6 +602,7 @@ def set_backend_stage(self, target_env, repository_name): commit_id=self.image_tag, vpc_id=target_env.get('vpc_id'), vpc_endpoints_sg=target_env.get('vpc_endpoints_sg'), + vpc_restricted_nacls=target_env.get('vpc_restricted_nacl', False), internet_facing=target_env.get('internet_facing', True), custom_domain=target_env.get('custom_domain'), ip_ranges=target_env.get('ip_ranges'), @@ -579,6 +614,8 @@ def set_backend_stage(self, target_env, repository_name): shared_dashboard_sessions=target_env.get('shared_dashboard_sessions', 'anonymous'), enable_opensearch_serverless=target_env.get('enable_opensearch_serverless', False), enable_pivot_role_auto_create=target_env.get('enable_pivot_role_auto_create', False), + codeartifact_domain_name=self.codeartifact.codeartifact_domain_name, + codeartifact_pip_repo_name=self.codeartifact.codeartifact_pip_repo_name, ) ) return backend_stage @@ -608,7 +645,7 @@ def set_db_migration_stage( 'if [ "$(jq -r .builds[0].buildStatus codebuild-output.json)" = "FAILED" ]; then echo "Failed"; cat codebuild-output.json; exit -1; fi', 'cat codebuild-output.json ', ], - role_policy_statements=self.codebuild_policy, + role=self.expanded_codebuild_role, vpc=self.vpc, ), ) @@ -640,7 +677,7 @@ def set_stacks_updater_stage( f'cluster_arn="arn:aws:ecs:{target_env["region"]}:{target_env["account"]}:cluster/$cluster_name"', f'aws --profile buildprofile ecs run-task --task-definition $task_definition --cluster "$cluster_arn" --launch-type "FARGATE" --network-configuration "$network_config" --launch-type FARGATE --propagate-tags TASK_DEFINITION', ], - role_policy_statements=self.codebuild_policy, + role=self.expanded_codebuild_role, vpc=self.vpc, ), ) @@ -695,7 +732,7 @@ def set_cloudfront_stage(self, target_env): 'aws s3 sync build/ s3://$bucket --profile buildprofile', "aws cloudfront create-invalidation --distribution-id $distributionId --paths '/*' --profile buildprofile", ], - role_policy_statements=self.codebuild_policy, + role=self.expanded_codebuild_role, vpc=self.vpc, ), self.cognito_config_action(target_env), @@ -717,7 +754,7 @@ def set_cloudfront_stage(self, target_env): build_image=codebuild.LinuxBuildImage.AMAZON_LINUX_2_4, ), commands=[ - f'aws codeartifact login --tool pip --repository {self.codeartifact.pip_repo.attr_name} --domain {self.codeartifact.domain.attr_name} --domain-owner {self.codeartifact.domain.attr_owner}', + f'aws codeartifact login --tool pip --repository {self.codeartifact.codeartifact_pip_repo_name} --domain {self.codeartifact.codeartifact_domain_name} --domain-owner {self.codeartifact.domain.attr_owner}', f"make assume-role REMOTE_ACCOUNT_ID={target_env['account']} REMOTE_ROLE={self.resource_prefix}-{target_env['envname']}-S3DeploymentRole", '. ./.env.assumed_role', 'aws sts get-caller-identity', @@ -730,7 +767,7 @@ def set_cloudfront_stage(self, target_env): 'aws s3 sync site/ s3://$bucket', "aws cloudfront create-invalidation --distribution-id $distributionId --paths '/*'", ], - role_policy_statements=self.codebuild_policy, + role=self.expanded_codebuild_role, vpc=self.vpc, ), ) @@ -757,7 +794,7 @@ def cw_rum_config_action(self, target_env): 'pip install boto3==1.20.46', 'python deploy/configs/rum_config.py', ], - role_policy_statements=self.codebuild_policy, + role=self.expanded_codebuild_role, vpc=self.vpc, ) @@ -784,7 +821,7 @@ def cognito_config_action(self, target_env): 'pip install boto3==1.20.46', 'python deploy/configs/cognito_urls_config.py', ], - role_policy_statements=self.codebuild_policy, + role=self.expanded_codebuild_role, vpc=self.vpc, ) @@ -818,7 +855,7 @@ def set_albfront_stage(self, target_env, repository_name): }, ), commands=[ - f'aws codeartifact login --tool pip --repository {self.codeartifact.pip_repo.attr_name} --domain {self.codeartifact.domain.attr_name} --domain-owner {self.codeartifact.domain.attr_owner}', + f'aws codeartifact login --tool pip --repository {self.codeartifact.codeartifact_pip_repo_name} --domain {self.codeartifact.codeartifact_domain_name} --domain-owner {self.codeartifact.domain.attr_owner}', f'export REACT_APP_STAGE={target_env["envname"]}', f'export envname={target_env["envname"]}', f'export internet_facing={target_env.get("internet_facing", False)}', @@ -842,7 +879,7 @@ def set_albfront_stage(self, target_env, repository_name): 'docker tag $IMAGE_TAG:$IMAGE_TAG $REPOSITORY_URI:$IMAGE_TAG', 'docker push $REPOSITORY_URI:$IMAGE_TAG', ], - role_policy_statements=self.codebuild_policy, + role=self.expanded_codebuild_role, vpc=self.vpc, ), pipelines.CodeBuildStep( @@ -859,14 +896,14 @@ def set_albfront_stage(self, target_env, repository_name): }, ), commands=[ - f'aws codeartifact login --tool pip --repository {self.codeartifact.pip_repo.attr_name} --domain {self.codeartifact.domain.attr_name} --domain-owner {self.codeartifact.domain.attr_owner}', + f'aws codeartifact login --tool pip --repository {self.codeartifact.codeartifact_pip_repo_name} --domain {self.codeartifact.codeartifact_domain_name} --domain-owner {self.codeartifact.domain.attr_owner}', 'cd documentation/userguide', 'docker build -f docker/prod/Dockerfile -t $IMAGE_TAG:$IMAGE_TAG .', f'aws ecr get-login-password --region {self.region} | docker login --username AWS --password-stdin {self.account}.dkr.ecr.{self.region}.amazonaws.com', 'docker tag $IMAGE_TAG:$IMAGE_TAG $REPOSITORY_URI:$IMAGE_TAG', 'docker push $REPOSITORY_URI:$IMAGE_TAG', ], - role_policy_statements=self.codebuild_policy, + role=self.expanded_codebuild_role, vpc=self.vpc, ), ], @@ -888,35 +925,6 @@ def evaluate_post_albfront_stage(self, target_env): def set_release_stage( self, ): - git_project_role = iam.Role( - self, - id=f'GitReleaseCBRole{self.git_branch}', - role_name=f'{self.resource_prefix}-{self.git_branch}-git-release-role', - assumed_by=iam.CompositePrincipal( - iam.ServicePrincipal('codebuild.amazonaws.com'), - iam.AccountPrincipal(self.account), - ), - ) - for policy in self.codebuild_policy: - git_project_role.add_to_policy(policy) - - git_project_role.add_to_policy( - iam.PolicyStatement( - actions=[ - 'codecommit:CreateBranch', - 'codecommit:GetCommit', - 'codecommit:ListBranches', - 'codecommit:GetRepository', - 'codecommit:GetBranch', - 'codecommit:GitPull', - 'codecommit:PutFile', - 'codecommit:CreateCommit', - 'codecommit:GitPush', - 'codecommit:ListTagsForResource', - ], - resources=[f'arn:aws:codecommit:{self.region}:{self.account}:dataall'], - ), - ) self.pipeline.add_wave( f'{self.resource_prefix}-{self.git_branch}-release-stage' ).add_post( @@ -932,7 +940,7 @@ def set_release_stage( 'build': { 'commands': [ 'set -eu', - f'aws codeartifact login --tool pip --repository {self.codeartifact.pip_repo.attr_name} --domain {self.codeartifact.domain.attr_name} --domain-owner {self.codeartifact.domain.attr_owner}', + f'aws codeartifact login --tool pip --repository {self.codeartifact.codeartifact_pip_repo_name} --domain {self.codeartifact.codeartifact_domain_name} --domain-owner {self.codeartifact.domain.attr_owner}', 'python -m venv env', '. env/bin/activate', 'pip install git-remote-codecommit', @@ -946,7 +954,7 @@ def set_release_stage( }, ) ), - role=git_project_role, + role=self.git_project_role, vpc=self.vpc, security_groups=[self.codebuild_sg], commands=[], diff --git a/deploy/stacks/s3_resources.py b/deploy/stacks/s3_resources.py index 699549736..1d19cb4e0 100644 --- a/deploy/stacks/s3_resources.py +++ b/deploy/stacks/s3_resources.py @@ -55,6 +55,10 @@ def __init__(self, scope, id, envname='dev', resource_prefix='dataall', **kwargs os.path.abspath(os.path.join(__file__, '..', '..', 'pivot_role')) ) + cdk_exec_policy = os.path.realpath( + os.path.abspath(os.path.join(__file__, '..', '..', 'cdk_exec_policy')) + ) + s3d.BucketDeployment( self, f'PivotRoleDeployment{envname}', @@ -70,6 +74,21 @@ def __init__(self, scope, id, envname='dev', resource_prefix='dataall', **kwargs string_value='roles/pivotRole.yaml', ) + s3d.BucketDeployment( + self, + f'CDKExecutionPolicyDeployment{envname}', + sources=[s3d.Source.asset(cdk_exec_policy)], + destination_bucket=self.bucket, + destination_key_prefix='policies', + ) + + ssm.StringParameter( + self, + f'S3ResourcesBucketKeyParamCDK{envname}', + parameter_name=f'/dataall/{envname}/s3/cdk_exec_policy_prefix', + string_value='policies/cdkExecPolicy.yaml', + ) + CfnOutput( self, f'{resource_prefix}-{envname}-resources-bucket-output', diff --git a/deploy/stacks/secrets_stack.py b/deploy/stacks/secrets_stack.py index 8d71129e1..56a9d2b87 100644 --- a/deploy/stacks/secrets_stack.py +++ b/deploy/stacks/secrets_stack.py @@ -17,71 +17,34 @@ def __init__( id, envname='dev', resource_prefix='dataall', - enable_cw_canaries=False, - pivot_role_name=None, **kwargs, ): super().__init__(scope, id, **kwargs) - self.external_id_key = kms.Key( + self.secrets_manager_key = kms.Key( self, - f'ExternalIdSecretKey{envname}', - alias=f'{resource_prefix}-{envname}-externalId-key', + f'SecretsManagerKey{envname}', + alias=f'{resource_prefix}-{envname}-secrets-manager-key', enable_key_rotation=True, removal_policy=RemovalPolicy.DESTROY, ) - self.external_id_secret = sm.Secret( + self.canary_user = sm.Secret( self, - f'ExternalIdSecret{envname}', - secret_name=f'dataall-externalId-{envname}', - generate_secret_string=sm.SecretStringGenerator(exclude_punctuation=True), - encryption_key=self.external_id_key, - description=f'Stores dataall external id for environment {envname}', + f'canary-user', + secret_name=f'{resource_prefix}-{envname}-cognito-canary-user', + generate_secret_string=sm.SecretStringGenerator( + secret_string_template=json.dumps({'username': f'cwcanary-{self.account}'}), + generate_string_key='password', + include_space=False, + password_length=12, + ), + encryption_key=self.secrets_manager_key, + description=f'Stores dataall Cognito canary user', removal_policy=RemovalPolicy.DESTROY, ) - - self.pivot_role_name_key = kms.Key( - self, - f'PivotRoleNameSecretKey{envname}', - alias=f'{resource_prefix}-{envname}-pivotrolename-key', - enable_key_rotation=True, - ) - - self.pivot_role_name_secret = sm.CfnSecret( - self, - f'PivotRoleNameSecret{envname}', - name=f'dataall-pivot-role-name-{envname}', - secret_string=pivot_role_name, - kms_key_id=self.pivot_role_name_key.key_id, - description=f'Stores dataall pivot role name for environment {envname}', + self.canary_user.add_rotation_schedule( + id=envname[:2], + automatically_after=Duration.days(90), + hosted_rotation=sm.HostedRotation.postgre_sql_single_user(), ) - - self.cognito_default_user = kms.Key( - self, - f'{resource_prefix}-{envname}-cognito-defaultuser-key', - alias=f'{resource_prefix}-{envname}-cognito-defaultuser-key', - enable_key_rotation=True, - removal_policy=RemovalPolicy.DESTROY, - ) - - if enable_cw_canaries: - self.canary_user = sm.Secret( - self, - f'canary-user', - secret_name=f'{resource_prefix}-{envname}-cognito-canary-user', - generate_secret_string=sm.SecretStringGenerator( - secret_string_template=json.dumps({'username': f'cwcanary-{self.account}'}), - generate_string_key='password', - include_space=False, - password_length=12, - ), - encryption_key=self.external_id_key, - description=f'Stores dataall Cognito canary user', - removal_policy=RemovalPolicy.DESTROY, - ) - self.canary_user.add_rotation_schedule( - id=envname[:2], - automatically_after=Duration.days(90), - hosted_rotation=sm.HostedRotation.postgre_sql_single_user(), - ) diff --git a/deploy/stacks/vpc.py b/deploy/stacks/vpc.py index 42f04b0fb..45c7ac685 100644 --- a/deploy/stacks/vpc.py +++ b/deploy/stacks/vpc.py @@ -11,7 +11,6 @@ from .pyNestedStack import pyNestedClass - class VpcStack(pyNestedClass): def __init__( self, @@ -38,11 +37,12 @@ def __init__( ) else: self.vpce_security_group = ec2.SecurityGroup( - self, 'vpc-sg', vpc=cast(ec2.IVpc, self.vpc), allow_all_outbound=False - ) - self.vpce_security_group.add_ingress_rule( - peer=ec2.Peer.ipv4(self.vpc.vpc_cidr_block), - connection=ec2.Port.all_tcp(), + self, + 'vpc-sg', + security_group_name=f'{resource_prefix}-{envname}-vpce-sg', + vpc=cast(ec2.IVpc, self.vpc), + allow_all_outbound=False, + disable_inline_rules=True ) self._create_vpc_endpoints() @@ -126,6 +126,7 @@ def create_new_vpc(self, cidr, envname, resource_prefix, restricted_nacl): ], nat_gateways=1, ) + if restricted_nacl: nacl = ec2.NetworkAcl( self, "RestrictedNACL", @@ -240,6 +241,12 @@ def _create_vpc_endpoints(self) -> None: 'cloudwatch_logs_endpoint': ec2.InterfaceVpcEndpointAwsService.CLOUDWATCH_LOGS, 'rds_endpoint': ec2.InterfaceVpcEndpointAwsService.RDS, 'rds_data_endpoint': ec2.InterfaceVpcEndpointAwsService.RDS_DATA, + 'sagemaker_api': ec2.InterfaceVpcEndpointAwsService.SAGEMAKER_API, + 'glue': ec2.InterfaceVpcEndpointAwsService.GLUE, + 'lakeformation': ec2.InterfaceVpcEndpointAwsService.LAKE_FORMATION, + 'athena': ec2.InterfaceVpcEndpointAwsService.ATHENA, + 'codecommit': ec2.InterfaceVpcEndpointAwsService.CODECOMMIT, + 'git-codecommit': ec2.InterfaceVpcEndpointAwsService.CODECOMMIT_GIT, } for name, gateway_vpc_endpoint_service in vpc_gateway_endpoints.items(): diff --git a/documentation/userguide/docs/datasets.md b/documentation/userguide/docs/datasets.md index 9f304ed70..09605d3f3 100644 --- a/documentation/userguide/docs/datasets.md +++ b/documentation/userguide/docs/datasets.md @@ -1,6 +1,6 @@ # **Datasets** ## **Datasets** -In data.all, a Dataset is a representation of multiple AWS resources that helps +In *data.all*, a Dataset is a representation of multiple AWS resources that helps users store data and establish the basis to make this data discoverable and shareable with other teams. When data owners create a dataset the following resources are @@ -8,13 +8,32 @@ deployed on the selected environment and its linked AWS account: 1. Amazon S3 Bucket to store the data on AWS. 2. AWS KMS key to encrypt the data on AWS. -3. AWS IAM role that gives access to the data on Amazon S3. +3. AWS IAM role that gives access to the data on Amazon S3 (Dataset IAM role, see below) 4. AWS Glue database that is the representation of the structured data on AWS. -!!!abstract "AWS Champion" - data.all does all the infrastructure heavy lifting for data owners using - **AWS CDK** and **AWS CloudFormation** service while following AWS - deployment and security best practices. +### Dataset IAM role + +**Usage** + +- Assumed by Dataset owners from *data.all* UI to quickly ingest or access Dataset data +- Assumed by Dataset Glue crawler +- Assumed by the Dataset Glue profiling job + +**IAM Permissions** + +- read and write permissions to the Dataset S3 Bucket (ONLY this bucket) +- encrypt/decrypt data with the Dataset KMS key (ONLY this key) +- read and write permissions to the Dataset Glue database and tables (ONLY this database) +- read permissions to profiling/code folder in the Environment S3 Bucket (ONLY this folder) +- read and write permissions to profiling/results/datasetUri folder in the Environment S3 Bucket (ONLY this folder) +- put logs permissions to log crawler and profiling jobs results + +**Data Governance with Lake Formation** + +In addition to restricting the access via IAM policies, Dataset Glue database and tables are +protected using AWS Lake Formation. With Lake Formation, the Dataset IAM role gets granted +access to the Dataset Glue database only. + ### Tables and Folders @@ -76,13 +95,22 @@ On left pane choose **Datasets**, then click on the **Create** button. Fill the ## :material-import: **Import a dataset** -If you already have data stored on Amazon S3 buckets, data.all got you covered with the import feature. In addition to -the fields of a newly created dataset you have to specify the S3 bucket and optionally a Glue database: -| Field | Description | Required | Editable |Example -|-------------------|----------------------------------------------------------------------------|----------|----------|------------- -| Amazon S3 bucket name | Name of the S3 bucket you want to import | Yes | No |importedBucket -| AWS Glue database name | Name of the Glue database tht you want to import | No | No |anyDatabase +If you already have data stored on Amazon S3 buckets in your data.all environment, data.all got you covered with the import feature. In addition to +the fields of a newly created dataset you have to specify the S3 bucket and optionally a Glue database and a KMS key Alias. If the Glue database +is left empty, data.all will create a Glue database pointing at the S3 Bucket. As for the KMS key Alias, data.all assumes that if nothing is specified +the S3 Bucket is encrypted with SSE-S3 encryption. + +| Field | Description | Required | Editable |Example +|------------------------|-------------------------------------------------------------------------------------------------|----------|----------|------------- +| Amazon S3 bucket name | Name of the S3 bucket you want to import | Yes | No |DOC-EXAMPLE-BUCKET +| Amazon KMS key Alias | Alias of the KMS key used to encrypt the S3 Bucket (do not include alias/, just ) | Yes | No |somealias +| AWS Glue database name | Name of the Glue database tht you want to import | No | No |anyDatabase + +!!!success "Update imported Datasets" + Imported keys is an addition of V1.6.0 release. Any previously imported bucket will have a KMS Key Alias set to `Undefined`. + If that is the case and you want to update the Dataset and import a KMS key Alias, data.all let's you edit the Dataset on the + **Edit** window. ![import_dataset](pictures/datasets/import_dataset.png#zoom#shadow) diff --git a/documentation/userguide/docs/environments.md b/documentation/userguide/docs/environments.md index 233d90ef3..864ddb57c 100644 --- a/documentation/userguide/docs/environments.md +++ b/documentation/userguide/docs/environments.md @@ -11,7 +11,8 @@ users store data and work with data.** ## :material-hammer-screwdriver: **AWS account Pre-requisites** *data.all* does not create AWS accounts. You need to provide an AWS account and complete the following bootstraping -steps. +steps. Only the first step, CDK bootstrap, is mandatory; the rest are needed depending on your deployment configuration +or on the features enabled in the environment. ### 1. CDK Bootstrap @@ -44,6 +45,12 @@ cdk bootstrap --trust DATA.ALL_AWS_ACCOUNT_NUMBER -c @aws-cdk/core:newStyleStac ````bash cdk bootstrap --trust 222222222222 -c @aws-cdk/core:newStyleStackSynthesis=true --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess aws://333333333333/eu-west-1 ```` +#### Restricted CDK Execution role +In the above command we define the `--cloudformation-execution-policies` to use the AdministratorAccess policy `arn:aws:iam::aws:policy/AdministratorAccess`. +This is the default policy that CDK uses to deploy resources, nevertheless it is possible to restrict it to any IAM policy created in the account. + +In V1.6.0 a more restricted policy is provided and directly downloadable from the UI. This more restrictive policy can be optionally used as +`--cloudformation-execution-policies` for the CDK Execution role. ### 2. (For manual) Pivot role @@ -61,7 +68,7 @@ In this case, the AWS CloudFormation stack of the role can be downloaded from *data.all* makes it easier to manage access to your AWS accounts. How? remember when we assigned granular AWS permissions -to invited groups and this created an IAM role? (If not, check the "Manage teams" section). From the **Teams** tab of +### :material-aws: **AWS access - Environment IAM roles** +For the environment admin team and for each team invited to the environment *data.all* +creates an IAM role. From the **Teams** tab of the environment we can assume our team's IAM role to get access to the AWS Console or copy the credentials to the clipboard. Both options are under the "Actions" column in the Teams table. + +![](pictures/environments/env_teams_3.png#zoom#shadow) + + +**Usage** + +- Assumed by Team members from *data.all* UI to explore and work with data +- Credentials can be copied in *data.all* UI to explore and work with data +- Assumed by *data.all* Worksheets to query data using Athena +- Credentials can be copied in *data.all* Pipelines to develop the pipeline + +**IAM Permissions** + +Default permissions + +- read permissions to profiling/code folder in the Environment S3 Bucket +- Athena permissions to use the Team's workgroup +- CloudFormation permissions to resources tagged with Team tag and prefixed with environment `resource_prefix` +- SSM Parameter Store permissions to resources tagged with team tag and prefixed with environment `resource_prefix` +- Secrets Manager permissions to resources tagged with team tag and prefixed with environment `resource_prefix` +- read permissions on Logs and IAM +- PassRole permissions for itself to Glue, Lambda, SageMaker, StepFunctions and DataBrew + +Data permissions + +- read and write permissions to the Team-owned Dataset S3 Buckets +- encrypt/decrypt data with the Team-owned Dataset KMS keys +- read and write permissions Dataset Glue databases - governed with Lake Formation + +Feature permissions + +Depending on the features enabled in the environment and granted to the Team, additional AWS permissions +are given to the role. Permissions for any AWS service need to be defined to allow access onlt to resources tagged +with team tag and prefixed with environment `resource_prefix` + +!!! warning "Access denied? You need to tag resources when you create them" + Since permissions to AWS services are restricted to team-tagged resources, you need to tag any new + resource that you create at creation time. + +Let's say you are using the "Engineers" IAM role in an environment that prefixes all resources with +the `resource_prefix` = "dataall" as in the following picture. + +![](pictures/environments/env_teams_5.png#zoom#shadow) + + +Assuming the IAM role you will be able to create parameters prefixed by "dataall" and tagged +with a tag Team=Engineers, otherwise you will get AccessDenied errors. + + +![](pictures/environments/env_teams_6.png#zoom#shadow) + +All the resources created in the environment stack are tagged with the tag `Team=EnvAdminTeam`, which means that +environment admins can access and manage the environment baseline AWS resources. + +**Data Governance with Lake Formation** + +We use AWS Lake Formation to govern Glue databases and tables. Using Lake Formation, we grant permissions to the +Environment teams IAM roles to read and write the Glue databases and tables that the Team owns. +In other words, each environment team IAM role can only access the Glue databases and tables of the Datasets +that the team owns. + + + ## :material-account-plus-outline: **Manage Consumption Roles** -Data.all creates or imports one IAM role per Cognito/IdP group that we invite to the environment. With these IAM roles data producers and consumers -can ingest and consume data, but sometimes we want to consume data from an application such as SageMaker pipelines, -Glue Jobs or any other downstream application. To increase the flexibility in the data consumption patterns, data.all introduces Consumption Roles. +*data.all* creates or imports one IAM role per Cognito/IdP group that we invite to the environment. With these IAM roles data producers and consumers +can ingest and consume data, but sometimes we want to consume data from an application that already has an execution role. To increase the flexibility in the data consumption patterns, data.all introduces Consumption Roles. -Any IAM role that exists in the Environment AWS Account can be added to data.all. In the **Teams** tab click on *Add Consumption Role* +Any IAM role that exists in the Environment AWS Account can be added to *data.all*. In the **Teams** tab click on *Add Consumption Role* ![](pictures/environments/env_consumption_roles_1.png#zoom#shadow) A window like the following will appear for you to introduce the arn of the IAM role and the Team that owns the consumption role. -Only members of this team and tenants of data.all can remove the consumption role. +Only members of this team and tenants of *data.all* can remove the consumption role. ![](pictures/environments/env_consumption_roles_2.png#zoom#shadow) !!! success "Existing roles only" - Data.all checks whether that IAM role exists in the AWS account of the environment before adding it as a consumption role. + *data.all* checks whether that IAM role exists in the AWS account of the environment before adding it as a consumption role. **Data Access** -- By default, a new consumption role does NOT have access to any data in data.all. +- By default, a new consumption role does NOT have access to any data in *data.all*. - The team that owns the consumption role needs to open a share request for the consumption role as shown in the picture below. diff --git a/documentation/userguide/docs/pictures/environments/env_teams_3.png b/documentation/userguide/docs/pictures/environments/env_teams_3.png new file mode 100644 index 000000000..05f1c859a Binary files /dev/null and b/documentation/userguide/docs/pictures/environments/env_teams_3.png differ diff --git a/documentation/userguide/docs/pictures/environments/env_teams_5.png b/documentation/userguide/docs/pictures/environments/env_teams_5.png new file mode 100644 index 000000000..c589e1cb3 Binary files /dev/null and b/documentation/userguide/docs/pictures/environments/env_teams_5.png differ diff --git a/documentation/userguide/docs/pictures/environments/env_teams_6.png b/documentation/userguide/docs/pictures/environments/env_teams_6.png new file mode 100644 index 000000000..0b5c4c2a8 Binary files /dev/null and b/documentation/userguide/docs/pictures/environments/env_teams_6.png differ diff --git a/documentation/userguide/docs/tables.md b/documentation/userguide/docs/tables.md index 192757b51..cb8396a28 100644 --- a/documentation/userguide/docs/tables.md +++ b/documentation/userguide/docs/tables.md @@ -70,6 +70,9 @@ By selecting the **Metrics** tab of your data table you can run a profiling job ![](pictures/tables/table_metrics.png#zoom#shadow) +!!! warning "Profiling Job Prerequisite" + Before running the profiling job you will need to ensure that the **default** Glue Database exists in the AWS Account where the data exists (by default this database exists for new accounts). This is required to enable the Glue profiling job to use the metadata stored in the Glue Catalog. + ### :material-trash-can-outline: **Delete a table** Deleting a table means deleting it from the data.all Catalog, but it will be still available on the AWS Glue Catalog. Moreover, when data owners diff --git a/frontend/src/api/Dataset/getDataset.js b/frontend/src/api/Dataset/getDataset.js index 4b9e7189f..aeb71fb74 100644 --- a/frontend/src/api/Dataset/getDataset.js +++ b/frontend/src/api/Dataset/getDataset.js @@ -14,6 +14,7 @@ const getDataset = (datasetUri) => ({ name region created + imported userRoleForDataset SamlAdminGroupName AwsAccountId diff --git a/frontend/src/api/Environment/getCDKExecPolicyPresignedUrl.js b/frontend/src/api/Environment/getCDKExecPolicyPresignedUrl.js new file mode 100644 index 000000000..026f6668a --- /dev/null +++ b/frontend/src/api/Environment/getCDKExecPolicyPresignedUrl.js @@ -0,0 +1,14 @@ +import { gql } from 'apollo-boost'; + +const getCDKExecPolicyPresignedUrl = (organizationUri) => ({ + variables: { + organizationUri + }, + query: gql` + query getCDKExecPolicyPresignedUrl($organizationUri: String!) { + getCDKExecPolicyPresignedUrl(organizationUri: $organizationUri) + } + ` +}); + +export default getCDKExecPolicyPresignedUrl; diff --git a/frontend/src/api/ShareObject/getShareObject.js b/frontend/src/api/ShareObject/getShareObject.js index 3444fa26b..e7da50f4e 100644 --- a/frontend/src/api/ShareObject/getShareObject.js +++ b/frontend/src/api/ShareObject/getShareObject.js @@ -12,6 +12,8 @@ const getShareObject = ({ shareUri, filter }) => ({ created owner status + requestPurpose + rejectPurpose userRoleForShareObject consumptionData { s3AccessPointName diff --git a/frontend/src/api/ShareObject/rejectShareObject.js b/frontend/src/api/ShareObject/rejectShareObject.js index 315330a16..5fb45dfee 100644 --- a/frontend/src/api/ShareObject/rejectShareObject.js +++ b/frontend/src/api/ShareObject/rejectShareObject.js @@ -1,12 +1,13 @@ import { gql } from 'apollo-boost'; -const rejectShareObject = ({ shareUri }) => ({ +const rejectShareObject = ({ shareUri, rejectPurpose }) => ({ variables: { - shareUri + shareUri, + rejectPurpose }, mutation: gql` - mutation RejectShareObject($shareUri: String!) { - rejectShareObject(shareUri: $shareUri) { + mutation RejectShareObject($shareUri: String!, $rejectPurpose: String!) { + rejectShareObject(shareUri: $shareUri,rejectPurpose: $rejectPurpose) { shareUri status } diff --git a/frontend/src/api/ShareObject/updateShareRejectReason.js b/frontend/src/api/ShareObject/updateShareRejectReason.js new file mode 100644 index 000000000..32df0c67e --- /dev/null +++ b/frontend/src/api/ShareObject/updateShareRejectReason.js @@ -0,0 +1,15 @@ +import { gql } from 'apollo-boost'; + +const updateShareRejectReason = ({ shareUri, rejectPurpose }) => ({ + variables: { + shareUri, + rejectPurpose + }, + mutation: gql` + mutation updateShareRejectReason($shareUri: String!,$rejectPurpose: String!) { + updateShareRejectReason(shareUri: $shareUri, rejectPurpose: $rejectPurpose) + } + ` +}); + +export default updateShareRejectReason; diff --git a/frontend/src/api/ShareObject/updateShareRequestReason.js b/frontend/src/api/ShareObject/updateShareRequestReason.js new file mode 100644 index 000000000..6bf4dd0dc --- /dev/null +++ b/frontend/src/api/ShareObject/updateShareRequestReason.js @@ -0,0 +1,15 @@ +import { gql } from 'apollo-boost'; + +const updateShareRequestReason = ({ shareUri, requestPurpose }) => ({ + variables: { + shareUri, + requestPurpose + }, + mutation: gql` + mutation updateShareRequestReason($shareUri: String!, $requestPurpose: String!) { + updateShareRequestReason(shareUri: $shareUri, requestPurpose: $requestPurpose) + } + ` +}); + +export default updateShareRequestReason; diff --git a/frontend/src/views/Catalog/RequestAccessModal.js b/frontend/src/views/Catalog/RequestAccessModal.js index 360c9246f..970d500aa 100644 --- a/frontend/src/views/Catalog/RequestAccessModal.js +++ b/frontend/src/views/Catalog/RequestAccessModal.js @@ -136,7 +136,8 @@ const RequestAccessModal = (props) => { environmentUri: values.environment.environmentUri, groupUri: values.groupUri, principalId: principal, - principalType: type + principalType: type, + requestPurpose: values.comment } }) ); @@ -151,7 +152,8 @@ const RequestAccessModal = (props) => { environmentUri: values.environment.environmentUri, groupUri: values.groupUri, principalId: principal, - principalType: type + principalType: type, + requestPurpose: values.comment } }) ); @@ -166,7 +168,8 @@ const RequestAccessModal = (props) => { environmentUri: values.environment.environmentUri, groupUri: values.groupUri, principalId: principal, - principalType: type + principalType: type, + requestPurpose: values.comment } }) ); diff --git a/frontend/src/views/Datasets/DatasetConsoleAccess.js b/frontend/src/views/Datasets/DatasetConsoleAccess.js index ebd2c18a5..5e469148d 100644 --- a/frontend/src/views/Datasets/DatasetConsoleAccess.js +++ b/frontend/src/views/Datasets/DatasetConsoleAccess.js @@ -1,10 +1,11 @@ import PropTypes from 'prop-types'; import { - Card, - CardContent, - CardHeader, - Divider, - Typography + Box, + Card, + CardContent, + CardHeader, + Divider, + Typography } from '@mui/material'; const DatasetConsoleAccess = (props) => { @@ -47,16 +48,26 @@ const DatasetConsoleAccess = (props) => { {dataset.IAMDatasetAdminRoleArn} - {!dataset.imported && ( + { dataset.KmsAlias === "SSE-S3" || dataset.KmsAlias === "Undefined" ? - KMS alias + S3 Encryption - {`arn:aws:kms:${dataset.region}:${dataset.AwsAccountId}/alias:${dataset.KmsAlias}`} + {`${dataset.KmsAlias}`} - )} + : + + + S3 Encryption SSE-KMS + + + {`arn:aws:kms:${dataset.region}:${dataset.AwsAccountId}/alias:${dataset.KmsAlias}`} + + + + } ); }; diff --git a/frontend/src/views/Datasets/DatasetEditForm.js b/frontend/src/views/Datasets/DatasetEditForm.js index ffa1464c6..eeb7cdfb5 100644 --- a/frontend/src/views/Datasets/DatasetEditForm.js +++ b/frontend/src/views/Datasets/DatasetEditForm.js @@ -149,7 +149,8 @@ const DatasetEditForm = (props) => { terms: values.terms.nodes ? values.terms.nodes.map((t) => t.nodeUri) : values.terms.map((t) => t.nodeUri), - confidentiality: values.confidentiality + confidentiality: values.confidentiality, + KmsAlias: values.KmsAlias } }) ); @@ -254,13 +255,15 @@ const DatasetEditForm = (props) => { tags: dataset.tags, terms: dataset.terms || [], stewards: dataset.stewards, - confidentiality: dataset.confidentiality + confidentiality: dataset.confidentiality, + KmsAlias: dataset.KmsAlias }} validationSchema={Yup.object().shape({ label: Yup.string() .max(255) .required('*Dataset name is required'), description: Yup.string().max(5000), + KmsAlias: Yup.string().max(255), topics: Yup.array().min(1).required('*Topics are required'), tags: Yup.array().min(1).required('*Tags are required'), confidentiality: Yup.string().required( @@ -480,6 +483,23 @@ const DatasetEditForm = (props) => { variant="outlined" /> + { dataset.imported && dataset.KmsAlias === 'Undefined' && + + + + } diff --git a/frontend/src/views/Datasets/DatasetImportForm.js b/frontend/src/views/Datasets/DatasetImportForm.js index 1cdd53045..eead249dc 100644 --- a/frontend/src/views/Datasets/DatasetImportForm.js +++ b/frontend/src/views/Datasets/DatasetImportForm.js @@ -113,7 +113,8 @@ const DatasetImportForm = (props) => { tags: values.tags, description: values.description, topics: values.topics ? values.topics.map((t) => t.value) : [], - bucketName: values.bucketName, + bucketName: values.bucketName, + KmsKeyAlias: values.KmsKeyAlias, glueDatabaseName: values.glueDatabaseName, stewards: values.stewards, confidentiality: values.confidentiality @@ -215,6 +216,7 @@ const DatasetImportForm = (props) => { topics: [], glueDatabaseName: '', bucketName: '', + KmsKeyAlias: '', confidentiality: '' }} validationSchema={Yup.object().shape({ @@ -229,6 +231,7 @@ const DatasetImportForm = (props) => { environment: Yup.object().required('*Environment is required'), tags: Yup.array().min(1).required('*Tags are required'), glueDatabaseName: Yup.string().max(255), + KmsKeyAlias: Yup.string().max(255), bucketName: Yup.string() .max(255) .required('*S3 bucket name is required'), @@ -454,6 +457,21 @@ const DatasetImportForm = (props) => { variant="outlined" /> + + + { dispatch({ type: SET_ERROR, error: response.errors[0].message }); } }; + + const getCDKExecPolicyUrl = async () => { + const response = await client.query(getCDKExecPolicyPresignedUrl(params.uri)); + if (!response.errors) { + window.open(response.data.getCDKExecPolicyPresignedUrl, '_blank'); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + }; + const getExternalId = async () => { const response = await client.query(getPivotRoleExternalId(params.uri)); if (!response.errors) { @@ -259,43 +270,100 @@ const EnvironmentCreateForm = (props) => { - + - Bootstrap your AWS account with AWS CDK + 1. (OPTIONAL) As part of setting up your AWS Environment with CDK you need to specify a IAM Policy that gives permission for CDK to create AWS Resources via CloudFormation (i.e. CDK Execution Policy). + You optionally can use the below CloudFormation template to create the custom IAM policy that is more restrictive than the default AdministratorAccess policy. - - copyNotification()} - text={`cdk bootstrap --trust ${trustedAccount} -c @aws-cdk/core:newStyleStackSynthesis=true --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess aws://ACCOUNT_ID/REGION`} - > - - - - - {`cdk bootstrap --trust ${trustedAccount} -c @aws-cdk/core:newStyleStackSynthesis=true --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess aws://ACCOUNT_ID/REGION`} + + + + + 2. Bootstrap your AWS account with AWS CDK + + + + + + If Using Default CDK Execution Policy: + + + copyNotification()} + text={`cdk bootstrap --trust ${trustedAccount} -c @aws-cdk/core:newStyleStackSynthesis=true --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess aws://ACCOUNT_ID/REGION`} + > + + + + + {`cdk bootstrap --trust ${trustedAccount} -c @aws-cdk/core:newStyleStackSynthesis=true --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess aws://ACCOUNT_ID/REGION`} + + + + + + + + + If Using Custom CDK Execution Policy (From Step 1): + + + copyNotification()} + text={`cdk bootstrap --trust ${trustedAccount} -c @aws-cdk/core:newStyleStackSynthesis=true --cloudformation-execution-policies arn:aws:iam::ACCOUNT_ID:policy/DataAllCustomCDKPolicy aws://ACCOUNT_ID/REGION`} + > + + + + + {`cdk bootstrap --trust ${trustedAccount} -c @aws-cdk/core:newStyleStackSynthesis=true --cloudformation-execution-policies arn:aws:iam::ACCOUNT_ID:policy/DataAllCustomCDKPolicy aws://ACCOUNT_ID/REGION`} + + + + + {process.env.REACT_APP_ENABLE_PIVOT_ROLE_AUTO_CREATE == 'True' ? ( - As part of the environment CloudFormation stack data.all will create an IAM role (Pivot Role) to manage AWS operations in the environment AWS Account. + 3. As part of the environment CloudFormation stack data.all will create an IAM role (Pivot Role) to manage AWS operations in the environment AWS Account. ): ( - Create an IAM role named {pivotRoleName} using the AWS + 3. Create an IAM role named {pivotRoleName} using the AWS CloudFormation stack below diff --git a/frontend/src/views/Shares/ShareRejectModal.js b/frontend/src/views/Shares/ShareRejectModal.js new file mode 100644 index 000000000..2011ee8c9 --- /dev/null +++ b/frontend/src/views/Shares/ShareRejectModal.js @@ -0,0 +1,119 @@ +import PropTypes from 'prop-types'; +import * as Yup from 'yup'; +import { Formik } from 'formik'; +import { + Box, + CardContent, + Dialog, + TextField, + FormHelperText, + Typography +} from '@mui/material'; +import { LoadingButton } from '@mui/lab'; +import SendIcon from '@mui/icons-material/Send'; +import React, { useState } from 'react'; + +const ShareRejectModal = (props) => { + const { share, onApply, onClose, open, rejectFunction, ...other } = props; + + return ( + + + + Reject Share + + + + (Optional) Provide a reason for rejecting this share in the text + input field below: + + + + { + await rejectFunction(values.comment); + }} + > + {({ + errors, + handleBlur, + handleChange, + handleSubmit, + isSubmitting, + setFieldValue, + touched, + values + }) => ( +
+ + + + {touched.comment && errors.comment && ( + + {errors.comment} + + )} + + + + } + color="primary" + disabled={isSubmitting} + type="submit" + variant="contained" + > + Reject Share + + +
+ )} +
+
+
+
+ ); +}; + +ShareRejectModal.propTypes = { + share: PropTypes.object.isRequired, + onApply: PropTypes.func, + onClose: PropTypes.func, + rejectFunction: PropTypes.func.isRequired, + open: PropTypes.bool.isRequired, +}; + +export default ShareRejectModal; diff --git a/frontend/src/views/Shares/ShareUpdateReject.js b/frontend/src/views/Shares/ShareUpdateReject.js new file mode 100644 index 000000000..d40ceed40 --- /dev/null +++ b/frontend/src/views/Shares/ShareUpdateReject.js @@ -0,0 +1,163 @@ +import PropTypes from 'prop-types'; +import * as Yup from 'yup'; +import { Formik } from 'formik'; +import { + Box, + CardContent, + Dialog, + TextField, + FormHelperText, + Typography +} from '@mui/material'; +import { LoadingButton } from '@mui/lab'; +import { SET_ERROR } from '../../store/errorReducer'; +import SendIcon from '@mui/icons-material/Send'; +import React, { useState } from 'react'; +import updateShareRejectReason from '../../api/ShareObject/updateShareRejectReason'; + +const UpdateRejectReason = (props) => { + const { share, client, dispatch, enqueueSnackbar, fetchItem, ...other } = props; + const [isUpdateRejectModalOpen, setIsUpdateRejectModalOpen] = useState(false); + const [updating, setUpdating] = useState(false); + + const handleUpdateRejectModalOpen = () => {setIsUpdateRejectModalOpen(true);}; + const handleUpdateRejectModalClose = () => {setIsUpdateRejectModalOpen(false);}; + const update = async (comment) => { + setUpdating(true); + const response = await client.mutate( + updateShareRejectReason( + { + shareUri: share.shareUri, + rejectPurpose: comment + } + ) + ); + if (!response.errors) { + handleUpdateRejectModalClose() + enqueueSnackbar('Share reject reason updated', { + anchorOrigin: { + horizontal: 'right', + vertical: 'top' + }, + variant: 'success' + }); + await fetchItem(); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + setUpdating(false); + }; + + return ( + <> + } + sx={{ m: 2 }} + onClick={handleUpdateRejectModalOpen} + type="button" + variant="outlined" + > + Edit + + + + + Update Share Reject Reason + + + + Update a reason to reject the share request: + + + + { + await update(values.comment); + }} + > + {({ + errors, + handleBlur, + handleChange, + handleSubmit, + isSubmitting, + setFieldValue, + touched, + values + }) => ( +
+ + + + {touched.comment && errors.comment && ( + + {errors.comment} + + )} + + + + } + color="primary" + disabled={isSubmitting} + type="submit" + variant="contained" + > + Update Share Reject Reason + + +
+ )} +
+
+
+
+ + ); +}; + +UpdateRejectReason.propTypes = { + share: PropTypes.any, + client: PropTypes.any, + dispatch: PropTypes.any, + enqueueSnackbar: PropTypes.any, + fetchItem: PropTypes.func, +}; + +export default UpdateRejectReason; diff --git a/frontend/src/views/Shares/ShareUpdateRequest.js b/frontend/src/views/Shares/ShareUpdateRequest.js new file mode 100644 index 000000000..bc2c003f1 --- /dev/null +++ b/frontend/src/views/Shares/ShareUpdateRequest.js @@ -0,0 +1,164 @@ +import PropTypes from 'prop-types'; +import * as Yup from 'yup'; +import { Formik } from 'formik'; +import { + Box, + CardContent, + Dialog, + TextField, + FormHelperText, + Typography +} from '@mui/material'; +import { LoadingButton } from '@mui/lab'; +import { SET_ERROR } from '../../store/errorReducer'; +import SendIcon from '@mui/icons-material/Send'; +import React, { useState } from 'react'; +import updateShareRequestReason from '../../api/ShareObject/updateShareRequestReason'; + + +const UpdateRequestReason = (props) => { + const { share, client, dispatch, enqueueSnackbar, fetchItem, ...other } = props; + const [isUpdateRequestModalOpen, setIsUpdateRequestModalOpen] = useState(false); + const [updating, setUpdating] = useState(false); + + const handleUpdateRequestModalOpen = () => {setIsUpdateRequestModalOpen(true);}; + const handleUpdateRequestModalClose = () => {setIsUpdateRequestModalOpen(false);}; + const update = async (comment) => { + setUpdating(true); + const response = await client.mutate( + updateShareRequestReason( + { + shareUri: share.shareUri, + requestPurpose: comment + } + ) + ); + if (!response.errors) { + handleUpdateRequestModalClose() + enqueueSnackbar('Share request reason updated', { + anchorOrigin: { + horizontal: 'right', + vertical: 'top' + }, + variant: 'success' + }); + await fetchItem(); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + setUpdating(false); + }; + + return ( + <> + } + sx={{ m: 2 }} + onClick={handleUpdateRequestModalOpen} + type="button" + variant="outlined" + > + Edit + + + + + Update Share Request + + + + Update a reason for your share request: + + + + { + await update(values.comment); + }} + > + {({ + errors, + handleBlur, + handleChange, + handleSubmit, + isSubmitting, + setFieldValue, + touched, + values + }) => ( +
+ + + + {touched.comment && errors.comment && ( + + {errors.comment} + + )} + + + + } + color="primary" + disabled={isSubmitting} + type="submit" + variant="contained" + > + Update Share Request + + +
+ )} +
+
+
+
+ + ); +}; + +UpdateRequestReason.propTypes = { + share: PropTypes.any, + client: PropTypes.any, + dispatch: PropTypes.any, + enqueueSnackbar: PropTypes.any, + fetchItem: PropTypes.func, +}; + +export default UpdateRequestReason; diff --git a/frontend/src/views/Shares/ShareView.js b/frontend/src/views/Shares/ShareView.js index e26e05d4e..b3aff9afd 100644 --- a/frontend/src/views/Shares/ShareView.js +++ b/frontend/src/views/Shares/ShareView.js @@ -59,6 +59,9 @@ import rejectShareObject from '../../api/ShareObject/rejectShareObject'; import deleteShareObject from '../../api/ShareObject/deleteShareObject.js'; import submitApproval from '../../api/ShareObject/submitApproval'; import removeSharedItem from '../../api/ShareObject/removeSharedItem'; +import ShareRejectModal from './ShareRejectModal'; +import UpdateRejectReason from './ShareUpdateReject'; +import UpdateRequestReason from './ShareUpdateRequest'; function ShareViewHeader(props) { @@ -76,6 +79,7 @@ function ShareViewHeader(props) { const [rejecting, setRejecting] = useState(false); const [submitting, setSubmitting] = useState(false); const [removing, setRemoving] = useState(false); + const [isRejectShareModalOpen, setIsRejectShareModalOpen] = useState(false); const submit = async () => { setSubmitting(true); const response = await client.mutate( @@ -118,6 +122,15 @@ function ShareViewHeader(props) { } setRemoving(false); }; + + const handleRejectShareModalOpen = () => { + setIsRejectShareModalOpen(true); + }; + + const handleRejectShareModalClose = () => { + setIsRejectShareModalOpen(false); + }; + const accept = async () => { setAccepting(true); const response = await client.mutate( @@ -140,14 +153,16 @@ function ShareViewHeader(props) { } setAccepting(false); }; - const reject = async () => { + const reject = async (rejectPurpose) => { setRejecting(true); const response = await client.mutate( rejectShareObject({ - shareUri: share.shareUri + shareUri: share.shareUri, + rejectPurpose: rejectPurpose }) ); if (!response.errors) { + handleRejectShareModalClose() enqueueSnackbar('Share request rejected', { anchorOrigin: { horizontal: 'right', @@ -163,6 +178,7 @@ function ShareViewHeader(props) { setRejecting(false); }; return ( + <> @@ -236,7 +252,7 @@ function ShareViewHeader(props) { color="error" sx={{ m: 1 }} startIcon={} - onClick={reject} + onClick={handleRejectShareModalOpen} type="button" variant="outlined" > @@ -275,6 +291,16 @@ function ShareViewHeader(props) { )} + {isRejectShareModalOpen && ( + + )} + ); } @@ -686,6 +712,68 @@ const ShareView = () => {
+ + + + + + + + + + Request Purpose + {share.userRoleForShareObject === 'Requesters' && ( + + )} + + + + {share.requestPurpose || '-'} + + + + + + Reject Purpose + {share.userRoleForShareObject === 'Approvers' && ( + + )} + + + + {share.rejectPurpose || '-'} + + + + + + diff --git a/frontend/yarn.lock b/frontend/yarn.lock new file mode 100644 index 000000000..1c2e67c95 --- /dev/null +++ b/frontend/yarn.lock @@ -0,0 +1,11616 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + +"@adobe/css-tools@^4.0.1": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.2.0.tgz#e1a84fca468f4b337816fcb7f0964beb620ba855" + +"@alloc/quick-lru@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz#7bf68b20c0a350f936915fcae06f58e32007ce30" + +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@apideck/better-ajv-errors@^0.3.1": + version "0.3.6" + resolved "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.6.tgz" + dependencies: + json-schema "^0.4.0" + jsonpointer "^5.0.0" + leven "^3.1.0" + +"@apollo/client@^3.3.19": + version "3.7.17" + resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.7.17.tgz#1d2538729fd8ef138aa301a7cf62704474e57b72" + dependencies: + "@graphql-typed-document-node/core" "^3.1.1" + "@wry/context" "^0.7.0" + "@wry/equality" "^0.5.0" + "@wry/trie" "^0.4.0" + graphql-tag "^2.12.6" + hoist-non-react-statics "^3.3.2" + optimism "^0.16.2" + prop-types "^15.7.2" + response-iterator "^0.2.6" + symbol-observable "^4.0.0" + ts-invariant "^0.10.3" + tslib "^2.3.0" + zen-observable-ts "^1.2.5" + +"@appbaseio/analytics@^1.1.1": + version "1.2.0" + resolved "https://registry.npmjs.org/@appbaseio/analytics/-/analytics-1.2.0.tgz" + dependencies: + cross-fetch "^3.0.4" + +"@appbaseio/reactivecore@9.14.40": + version "9.14.40" + resolved "https://registry.npmjs.org/@appbaseio/reactivecore/-/reactivecore-9.14.40.tgz" + dependencies: + cross-fetch "^3.0.4" + dayjs "^1.11.7" + prop-types "^15.6.0" + redux "^4.0.0" + redux-thunk "^2.3.0" + +"@appbaseio/reactivesearch@^3.43.10": + version "3.43.10" + resolved "https://registry.npmjs.org/@appbaseio/reactivesearch/-/reactivesearch-3.43.10.tgz" + dependencies: + "@appbaseio/analytics" "^1.1.1" + "@appbaseio/reactivecore" "9.14.40" + "@appbaseio/rheostat" "^1.0.0-alpha.15" + "@emotion/core" "^10.0.28" + "@emotion/styled" "^10.0.27" + appbase-js "^5.2.0" + cross-env "^5.2.0" + dayjs "^1.11.7" + downshift "^1.31.2" + echarts "^5.3.3" + echarts-for-react "^3.0.2" + emotion-theming "^10.0.27" + hoist-non-react-statics "^3.2.1" + hotkeys-js "^3.8.7" + polished "^1.9.3" + prop-types "^15.6.0" + react-day-picker "^7.0.5" + react-redux "^6.0.1" + url-search-params-polyfill "^7.0.0" + xss "^1.0.11" + +"@appbaseio/rheostat@^1.0.0-alpha.15": + version "1.0.0-alpha.15" + resolved "https://registry.npmjs.org/@appbaseio/rheostat/-/rheostat-1.0.0-alpha.15.tgz" + dependencies: + object.assign "^4.0.4" + prop-types "^15.5.10" + +"@aws-amplify/analytics@6.2.0": + version "6.2.0" + resolved "https://registry.npmjs.org/@aws-amplify/analytics/-/analytics-6.2.0.tgz" + dependencies: + "@aws-amplify/cache" "5.1.0" + "@aws-amplify/core" "5.4.0" + "@aws-sdk/client-firehose" "3.6.1" + "@aws-sdk/client-kinesis" "3.6.1" + "@aws-sdk/client-personalize-events" "3.6.1" + "@aws-sdk/util-utf8-browser" "3.6.1" + lodash "^4.17.20" + tslib "^1.8.0" + uuid "^3.2.1" + +"@aws-amplify/api-graphql@3.3.1": + version "3.3.1" + resolved "https://registry.npmjs.org/@aws-amplify/api-graphql/-/api-graphql-3.3.1.tgz" + dependencies: + "@aws-amplify/api-rest" "3.2.1" + "@aws-amplify/auth" "5.4.1" + "@aws-amplify/cache" "5.1.0" + "@aws-amplify/core" "5.4.0" + "@aws-amplify/pubsub" "5.2.1" + graphql "15.8.0" + tslib "^1.8.0" + uuid "^3.2.1" + zen-observable-ts "0.8.19" + +"@aws-amplify/api-rest@3.2.1": + version "3.2.1" + resolved "https://registry.npmjs.org/@aws-amplify/api-rest/-/api-rest-3.2.1.tgz" + dependencies: + "@aws-amplify/core" "5.4.0" + axios "0.26.0" + tslib "^1.8.0" + url "0.11.0" + +"@aws-amplify/api@5.2.1": + version "5.2.1" + resolved "https://registry.npmjs.org/@aws-amplify/api/-/api-5.2.1.tgz" + dependencies: + "@aws-amplify/api-graphql" "3.3.1" + "@aws-amplify/api-rest" "3.2.1" + tslib "^1.8.0" + +"@aws-amplify/auth@5.4.1": + version "5.4.1" + resolved "https://registry.npmjs.org/@aws-amplify/auth/-/auth-5.4.1.tgz" + dependencies: + "@aws-amplify/core" "5.4.0" + amazon-cognito-identity-js "6.2.0" + tslib "^1.8.0" + url "0.11.0" + +"@aws-amplify/cache@5.1.0": + version "5.1.0" + resolved "https://registry.npmjs.org/@aws-amplify/cache/-/cache-5.1.0.tgz" + dependencies: + "@aws-amplify/core" "5.4.0" + tslib "^1.8.0" + +"@aws-amplify/core@5.4.0": + version "5.4.0" + resolved "https://registry.npmjs.org/@aws-amplify/core/-/core-5.4.0.tgz" + dependencies: + "@aws-crypto/sha256-js" "1.2.2" + "@aws-sdk/client-cloudwatch-logs" "3.6.1" + "@aws-sdk/types" "3.6.1" + "@aws-sdk/util-hex-encoding" "3.6.1" + isomorphic-unfetch "^3.0.0" + react-native-url-polyfill "^1.3.0" + tslib "^1.8.0" + universal-cookie "^4.0.4" + zen-observable-ts "0.8.19" + +"@aws-amplify/datastore@4.5.1": + version "4.5.1" + resolved "https://registry.npmjs.org/@aws-amplify/datastore/-/datastore-4.5.1.tgz" + dependencies: + "@aws-amplify/api" "5.2.1" + "@aws-amplify/auth" "5.4.1" + "@aws-amplify/core" "5.4.0" + "@aws-amplify/pubsub" "5.2.1" + amazon-cognito-identity-js "6.2.0" + idb "5.0.6" + immer "9.0.6" + ulid "2.3.0" + uuid "3.4.0" + zen-observable-ts "0.8.19" + zen-push "0.2.1" + +"@aws-amplify/geo@2.0.35": + version "2.0.35" + resolved "https://registry.npmjs.org/@aws-amplify/geo/-/geo-2.0.35.tgz" + dependencies: + "@aws-amplify/core" "5.4.0" + "@aws-sdk/client-location" "3.186.2" + "@turf/boolean-clockwise" "6.5.0" + camelcase-keys "6.2.2" + tslib "^1.8.0" + +"@aws-amplify/interactions@5.1.1": + version "5.1.1" + resolved "https://registry.npmjs.org/@aws-amplify/interactions/-/interactions-5.1.1.tgz" + dependencies: + "@aws-amplify/core" "5.4.0" + "@aws-sdk/client-lex-runtime-service" "3.186.2" + "@aws-sdk/client-lex-runtime-v2" "3.186.2" + base-64 "1.0.0" + fflate "0.7.3" + pako "2.0.4" + tslib "^1.8.0" + +"@aws-amplify/notifications@1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@aws-amplify/notifications/-/notifications-1.2.0.tgz" + dependencies: + "@aws-amplify/cache" "5.1.0" + "@aws-amplify/core" "5.4.0" + "@aws-amplify/rtn-push-notification" "1.1.1" + lodash "^4.17.21" + uuid "^3.2.1" + +"@aws-amplify/predictions@5.2.3": + version "5.2.3" + resolved "https://registry.npmjs.org/@aws-amplify/predictions/-/predictions-5.2.3.tgz" + dependencies: + "@aws-amplify/core" "5.4.0" + "@aws-amplify/storage" "5.4.1" + "@aws-sdk/client-comprehend" "3.6.1" + "@aws-sdk/client-polly" "3.6.1" + "@aws-sdk/client-rekognition" "3.6.1" + "@aws-sdk/client-textract" "3.6.1" + "@aws-sdk/client-translate" "3.6.1" + "@aws-sdk/eventstream-marshaller" "3.6.1" + "@aws-sdk/util-utf8-node" "3.6.1" + buffer "4.9.2" + tslib "^1.8.0" + uuid "^3.2.1" + +"@aws-amplify/pubsub@5.2.1": + version "5.2.1" + resolved "https://registry.npmjs.org/@aws-amplify/pubsub/-/pubsub-5.2.1.tgz" + dependencies: + "@aws-amplify/auth" "5.4.1" + "@aws-amplify/cache" "5.1.0" + "@aws-amplify/core" "5.4.0" + graphql "15.8.0" + tslib "^1.8.0" + url "0.11.0" + uuid "^3.2.1" + zen-observable-ts "0.8.19" + +"@aws-amplify/rtn-push-notification@1.1.1": + version "1.1.1" + resolved "https://registry.npmjs.org/@aws-amplify/rtn-push-notification/-/rtn-push-notification-1.1.1.tgz" + +"@aws-amplify/storage@5.4.1": + version "5.4.1" + resolved "https://registry.npmjs.org/@aws-amplify/storage/-/storage-5.4.1.tgz" + dependencies: + "@aws-amplify/core" "5.4.0" + "@aws-sdk/client-s3" "3.6.3" + "@aws-sdk/s3-request-presigner" "3.6.1" + "@aws-sdk/util-create-request" "3.6.1" + "@aws-sdk/util-format-url" "3.6.1" + axios "0.26.0" + events "^3.1.0" + tslib "^1.8.0" + +"@aws-crypto/crc32@2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-2.0.0.tgz" + dependencies: + "@aws-crypto/util" "^2.0.0" + "@aws-sdk/types" "^3.1.0" + tslib "^1.11.1" + +"@aws-crypto/crc32@^1.0.0": + version "1.2.2" + resolved "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-1.2.2.tgz" + dependencies: + "@aws-crypto/util" "^1.2.2" + "@aws-sdk/types" "^3.1.0" + tslib "^1.11.1" + +"@aws-crypto/ie11-detection@^1.0.0": + version "1.0.0" + resolved "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-1.0.0.tgz" + dependencies: + tslib "^1.11.1" + +"@aws-crypto/ie11-detection@^2.0.0": + version "2.0.2" + resolved "https://registry.npmjs.org/@aws-crypto/ie11-detection/-/ie11-detection-2.0.2.tgz" + dependencies: + tslib "^1.11.1" + +"@aws-crypto/sha256-browser@2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-2.0.0.tgz" + dependencies: + "@aws-crypto/ie11-detection" "^2.0.0" + "@aws-crypto/sha256-js" "^2.0.0" + "@aws-crypto/supports-web-crypto" "^2.0.0" + "@aws-crypto/util" "^2.0.0" + "@aws-sdk/types" "^3.1.0" + "@aws-sdk/util-locate-window" "^3.0.0" + "@aws-sdk/util-utf8-browser" "^3.0.0" + tslib "^1.11.1" + +"@aws-crypto/sha256-browser@^1.0.0": + version "1.2.2" + resolved "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-1.2.2.tgz" + dependencies: + "@aws-crypto/ie11-detection" "^1.0.0" + "@aws-crypto/sha256-js" "^1.2.2" + "@aws-crypto/supports-web-crypto" "^1.0.0" + "@aws-crypto/util" "^1.2.2" + "@aws-sdk/types" "^3.1.0" + "@aws-sdk/util-locate-window" "^3.0.0" + tslib "^1.11.1" + +"@aws-crypto/sha256-js@1.2.2", "@aws-crypto/sha256-js@^1.0.0", "@aws-crypto/sha256-js@^1.2.2": + version "1.2.2" + resolved "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-1.2.2.tgz" + dependencies: + "@aws-crypto/util" "^1.2.2" + "@aws-sdk/types" "^3.1.0" + tslib "^1.11.1" + +"@aws-crypto/sha256-js@2.0.0", "@aws-crypto/sha256-js@^2.0.0": + version "2.0.0" + resolved "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-2.0.0.tgz" + dependencies: + "@aws-crypto/util" "^2.0.0" + "@aws-sdk/types" "^3.1.0" + tslib "^1.11.1" + +"@aws-crypto/supports-web-crypto@^1.0.0": + version "1.0.0" + resolved "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-1.0.0.tgz" + dependencies: + tslib "^1.11.1" + +"@aws-crypto/supports-web-crypto@^2.0.0": + version "2.0.2" + resolved "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-2.0.2.tgz" + dependencies: + tslib "^1.11.1" + +"@aws-crypto/util@^1.2.2": + version "1.2.2" + resolved "https://registry.npmjs.org/@aws-crypto/util/-/util-1.2.2.tgz" + dependencies: + "@aws-sdk/types" "^3.1.0" + "@aws-sdk/util-utf8-browser" "^3.0.0" + tslib "^1.11.1" + +"@aws-crypto/util@^2.0.0": + version "2.0.2" + resolved "https://registry.npmjs.org/@aws-crypto/util/-/util-2.0.2.tgz" + dependencies: + "@aws-sdk/types" "^3.110.0" + "@aws-sdk/util-utf8-browser" "^3.0.0" + tslib "^1.11.1" + +"@aws-sdk/abort-controller@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.186.0.tgz" + dependencies: + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/abort-controller@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/abort-controller/-/abort-controller-3.6.1.tgz" + dependencies: + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/chunked-blob-reader-native@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/chunked-blob-reader-native/-/chunked-blob-reader-native-3.6.1.tgz" + dependencies: + "@aws-sdk/util-base64-browser" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/chunked-blob-reader@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/chunked-blob-reader/-/chunked-blob-reader-3.6.1.tgz" + dependencies: + tslib "^1.8.0" + +"@aws-sdk/client-cloudwatch-logs@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/client-cloudwatch-logs/-/client-cloudwatch-logs-3.6.1.tgz" + dependencies: + "@aws-crypto/sha256-browser" "^1.0.0" + "@aws-crypto/sha256-js" "^1.0.0" + "@aws-sdk/config-resolver" "3.6.1" + "@aws-sdk/credential-provider-node" "3.6.1" + "@aws-sdk/fetch-http-handler" "3.6.1" + "@aws-sdk/hash-node" "3.6.1" + "@aws-sdk/invalid-dependency" "3.6.1" + "@aws-sdk/middleware-content-length" "3.6.1" + "@aws-sdk/middleware-host-header" "3.6.1" + "@aws-sdk/middleware-logger" "3.6.1" + "@aws-sdk/middleware-retry" "3.6.1" + "@aws-sdk/middleware-serde" "3.6.1" + "@aws-sdk/middleware-signing" "3.6.1" + "@aws-sdk/middleware-stack" "3.6.1" + "@aws-sdk/middleware-user-agent" "3.6.1" + "@aws-sdk/node-config-provider" "3.6.1" + "@aws-sdk/node-http-handler" "3.6.1" + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/smithy-client" "3.6.1" + "@aws-sdk/types" "3.6.1" + "@aws-sdk/url-parser" "3.6.1" + "@aws-sdk/url-parser-native" "3.6.1" + "@aws-sdk/util-base64-browser" "3.6.1" + "@aws-sdk/util-base64-node" "3.6.1" + "@aws-sdk/util-body-length-browser" "3.6.1" + "@aws-sdk/util-body-length-node" "3.6.1" + "@aws-sdk/util-user-agent-browser" "3.6.1" + "@aws-sdk/util-user-agent-node" "3.6.1" + "@aws-sdk/util-utf8-browser" "3.6.1" + "@aws-sdk/util-utf8-node" "3.6.1" + tslib "^2.0.0" + +"@aws-sdk/client-comprehend@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/client-comprehend/-/client-comprehend-3.6.1.tgz" + dependencies: + "@aws-crypto/sha256-browser" "^1.0.0" + "@aws-crypto/sha256-js" "^1.0.0" + "@aws-sdk/config-resolver" "3.6.1" + "@aws-sdk/credential-provider-node" "3.6.1" + "@aws-sdk/fetch-http-handler" "3.6.1" + "@aws-sdk/hash-node" "3.6.1" + "@aws-sdk/invalid-dependency" "3.6.1" + "@aws-sdk/middleware-content-length" "3.6.1" + "@aws-sdk/middleware-host-header" "3.6.1" + "@aws-sdk/middleware-logger" "3.6.1" + "@aws-sdk/middleware-retry" "3.6.1" + "@aws-sdk/middleware-serde" "3.6.1" + "@aws-sdk/middleware-signing" "3.6.1" + "@aws-sdk/middleware-stack" "3.6.1" + "@aws-sdk/middleware-user-agent" "3.6.1" + "@aws-sdk/node-config-provider" "3.6.1" + "@aws-sdk/node-http-handler" "3.6.1" + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/smithy-client" "3.6.1" + "@aws-sdk/types" "3.6.1" + "@aws-sdk/url-parser" "3.6.1" + "@aws-sdk/url-parser-native" "3.6.1" + "@aws-sdk/util-base64-browser" "3.6.1" + "@aws-sdk/util-base64-node" "3.6.1" + "@aws-sdk/util-body-length-browser" "3.6.1" + "@aws-sdk/util-body-length-node" "3.6.1" + "@aws-sdk/util-user-agent-browser" "3.6.1" + "@aws-sdk/util-user-agent-node" "3.6.1" + "@aws-sdk/util-utf8-browser" "3.6.1" + "@aws-sdk/util-utf8-node" "3.6.1" + tslib "^2.0.0" + uuid "^3.0.0" + +"@aws-sdk/client-firehose@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/client-firehose/-/client-firehose-3.6.1.tgz" + dependencies: + "@aws-crypto/sha256-browser" "^1.0.0" + "@aws-crypto/sha256-js" "^1.0.0" + "@aws-sdk/config-resolver" "3.6.1" + "@aws-sdk/credential-provider-node" "3.6.1" + "@aws-sdk/fetch-http-handler" "3.6.1" + "@aws-sdk/hash-node" "3.6.1" + "@aws-sdk/invalid-dependency" "3.6.1" + "@aws-sdk/middleware-content-length" "3.6.1" + "@aws-sdk/middleware-host-header" "3.6.1" + "@aws-sdk/middleware-logger" "3.6.1" + "@aws-sdk/middleware-retry" "3.6.1" + "@aws-sdk/middleware-serde" "3.6.1" + "@aws-sdk/middleware-signing" "3.6.1" + "@aws-sdk/middleware-stack" "3.6.1" + "@aws-sdk/middleware-user-agent" "3.6.1" + "@aws-sdk/node-config-provider" "3.6.1" + "@aws-sdk/node-http-handler" "3.6.1" + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/smithy-client" "3.6.1" + "@aws-sdk/types" "3.6.1" + "@aws-sdk/url-parser" "3.6.1" + "@aws-sdk/url-parser-native" "3.6.1" + "@aws-sdk/util-base64-browser" "3.6.1" + "@aws-sdk/util-base64-node" "3.6.1" + "@aws-sdk/util-body-length-browser" "3.6.1" + "@aws-sdk/util-body-length-node" "3.6.1" + "@aws-sdk/util-user-agent-browser" "3.6.1" + "@aws-sdk/util-user-agent-node" "3.6.1" + "@aws-sdk/util-utf8-browser" "3.6.1" + "@aws-sdk/util-utf8-node" "3.6.1" + tslib "^2.0.0" + +"@aws-sdk/client-kinesis@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/client-kinesis/-/client-kinesis-3.6.1.tgz" + dependencies: + "@aws-crypto/sha256-browser" "^1.0.0" + "@aws-crypto/sha256-js" "^1.0.0" + "@aws-sdk/config-resolver" "3.6.1" + "@aws-sdk/credential-provider-node" "3.6.1" + "@aws-sdk/eventstream-serde-browser" "3.6.1" + "@aws-sdk/eventstream-serde-config-resolver" "3.6.1" + "@aws-sdk/eventstream-serde-node" "3.6.1" + "@aws-sdk/fetch-http-handler" "3.6.1" + "@aws-sdk/hash-node" "3.6.1" + "@aws-sdk/invalid-dependency" "3.6.1" + "@aws-sdk/middleware-content-length" "3.6.1" + "@aws-sdk/middleware-host-header" "3.6.1" + "@aws-sdk/middleware-logger" "3.6.1" + "@aws-sdk/middleware-retry" "3.6.1" + "@aws-sdk/middleware-serde" "3.6.1" + "@aws-sdk/middleware-signing" "3.6.1" + "@aws-sdk/middleware-stack" "3.6.1" + "@aws-sdk/middleware-user-agent" "3.6.1" + "@aws-sdk/node-config-provider" "3.6.1" + "@aws-sdk/node-http-handler" "3.6.1" + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/smithy-client" "3.6.1" + "@aws-sdk/types" "3.6.1" + "@aws-sdk/url-parser" "3.6.1" + "@aws-sdk/url-parser-native" "3.6.1" + "@aws-sdk/util-base64-browser" "3.6.1" + "@aws-sdk/util-base64-node" "3.6.1" + "@aws-sdk/util-body-length-browser" "3.6.1" + "@aws-sdk/util-body-length-node" "3.6.1" + "@aws-sdk/util-user-agent-browser" "3.6.1" + "@aws-sdk/util-user-agent-node" "3.6.1" + "@aws-sdk/util-utf8-browser" "3.6.1" + "@aws-sdk/util-utf8-node" "3.6.1" + "@aws-sdk/util-waiter" "3.6.1" + tslib "^2.0.0" + +"@aws-sdk/client-lex-runtime-service@3.186.2": + version "3.186.2" + resolved "https://registry.npmjs.org/@aws-sdk/client-lex-runtime-service/-/client-lex-runtime-service-3.186.2.tgz" + dependencies: + "@aws-crypto/sha256-browser" "2.0.0" + "@aws-crypto/sha256-js" "2.0.0" + "@aws-sdk/client-sts" "3.186.2" + "@aws-sdk/config-resolver" "3.186.0" + "@aws-sdk/credential-provider-node" "3.186.0" + "@aws-sdk/fetch-http-handler" "3.186.0" + "@aws-sdk/hash-node" "3.186.0" + "@aws-sdk/invalid-dependency" "3.186.0" + "@aws-sdk/middleware-content-length" "3.186.0" + "@aws-sdk/middleware-host-header" "3.186.0" + "@aws-sdk/middleware-logger" "3.186.0" + "@aws-sdk/middleware-recursion-detection" "3.186.0" + "@aws-sdk/middleware-retry" "3.186.0" + "@aws-sdk/middleware-serde" "3.186.0" + "@aws-sdk/middleware-signing" "3.186.0" + "@aws-sdk/middleware-stack" "3.186.0" + "@aws-sdk/middleware-user-agent" "3.186.0" + "@aws-sdk/node-config-provider" "3.186.0" + "@aws-sdk/node-http-handler" "3.186.0" + "@aws-sdk/protocol-http" "3.186.0" + "@aws-sdk/smithy-client" "3.186.0" + "@aws-sdk/types" "3.186.0" + "@aws-sdk/url-parser" "3.186.0" + "@aws-sdk/util-base64-browser" "3.186.0" + "@aws-sdk/util-base64-node" "3.186.0" + "@aws-sdk/util-body-length-browser" "3.186.0" + "@aws-sdk/util-body-length-node" "3.186.0" + "@aws-sdk/util-defaults-mode-browser" "3.186.0" + "@aws-sdk/util-defaults-mode-node" "3.186.0" + "@aws-sdk/util-user-agent-browser" "3.186.0" + "@aws-sdk/util-user-agent-node" "3.186.0" + "@aws-sdk/util-utf8-browser" "3.186.0" + "@aws-sdk/util-utf8-node" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/client-lex-runtime-v2@3.186.2": + version "3.186.2" + resolved "https://registry.npmjs.org/@aws-sdk/client-lex-runtime-v2/-/client-lex-runtime-v2-3.186.2.tgz" + dependencies: + "@aws-crypto/sha256-browser" "2.0.0" + "@aws-crypto/sha256-js" "2.0.0" + "@aws-sdk/client-sts" "3.186.2" + "@aws-sdk/config-resolver" "3.186.0" + "@aws-sdk/credential-provider-node" "3.186.0" + "@aws-sdk/eventstream-handler-node" "3.186.0" + "@aws-sdk/eventstream-serde-browser" "3.186.0" + "@aws-sdk/eventstream-serde-config-resolver" "3.186.0" + "@aws-sdk/eventstream-serde-node" "3.186.0" + "@aws-sdk/fetch-http-handler" "3.186.0" + "@aws-sdk/hash-node" "3.186.0" + "@aws-sdk/invalid-dependency" "3.186.0" + "@aws-sdk/middleware-content-length" "3.186.0" + "@aws-sdk/middleware-eventstream" "3.186.0" + "@aws-sdk/middleware-host-header" "3.186.0" + "@aws-sdk/middleware-logger" "3.186.0" + "@aws-sdk/middleware-recursion-detection" "3.186.0" + "@aws-sdk/middleware-retry" "3.186.0" + "@aws-sdk/middleware-serde" "3.186.0" + "@aws-sdk/middleware-signing" "3.186.0" + "@aws-sdk/middleware-stack" "3.186.0" + "@aws-sdk/middleware-user-agent" "3.186.0" + "@aws-sdk/node-config-provider" "3.186.0" + "@aws-sdk/node-http-handler" "3.186.0" + "@aws-sdk/protocol-http" "3.186.0" + "@aws-sdk/smithy-client" "3.186.0" + "@aws-sdk/types" "3.186.0" + "@aws-sdk/url-parser" "3.186.0" + "@aws-sdk/util-base64-browser" "3.186.0" + "@aws-sdk/util-base64-node" "3.186.0" + "@aws-sdk/util-body-length-browser" "3.186.0" + "@aws-sdk/util-body-length-node" "3.186.0" + "@aws-sdk/util-defaults-mode-browser" "3.186.0" + "@aws-sdk/util-defaults-mode-node" "3.186.0" + "@aws-sdk/util-user-agent-browser" "3.186.0" + "@aws-sdk/util-user-agent-node" "3.186.0" + "@aws-sdk/util-utf8-browser" "3.186.0" + "@aws-sdk/util-utf8-node" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/client-location@3.186.2": + version "3.186.2" + resolved "https://registry.npmjs.org/@aws-sdk/client-location/-/client-location-3.186.2.tgz" + dependencies: + "@aws-crypto/sha256-browser" "2.0.0" + "@aws-crypto/sha256-js" "2.0.0" + "@aws-sdk/client-sts" "3.186.2" + "@aws-sdk/config-resolver" "3.186.0" + "@aws-sdk/credential-provider-node" "3.186.0" + "@aws-sdk/fetch-http-handler" "3.186.0" + "@aws-sdk/hash-node" "3.186.0" + "@aws-sdk/invalid-dependency" "3.186.0" + "@aws-sdk/middleware-content-length" "3.186.0" + "@aws-sdk/middleware-host-header" "3.186.0" + "@aws-sdk/middleware-logger" "3.186.0" + "@aws-sdk/middleware-recursion-detection" "3.186.0" + "@aws-sdk/middleware-retry" "3.186.0" + "@aws-sdk/middleware-serde" "3.186.0" + "@aws-sdk/middleware-signing" "3.186.0" + "@aws-sdk/middleware-stack" "3.186.0" + "@aws-sdk/middleware-user-agent" "3.186.0" + "@aws-sdk/node-config-provider" "3.186.0" + "@aws-sdk/node-http-handler" "3.186.0" + "@aws-sdk/protocol-http" "3.186.0" + "@aws-sdk/smithy-client" "3.186.0" + "@aws-sdk/types" "3.186.0" + "@aws-sdk/url-parser" "3.186.0" + "@aws-sdk/util-base64-browser" "3.186.0" + "@aws-sdk/util-base64-node" "3.186.0" + "@aws-sdk/util-body-length-browser" "3.186.0" + "@aws-sdk/util-body-length-node" "3.186.0" + "@aws-sdk/util-defaults-mode-browser" "3.186.0" + "@aws-sdk/util-defaults-mode-node" "3.186.0" + "@aws-sdk/util-user-agent-browser" "3.186.0" + "@aws-sdk/util-user-agent-node" "3.186.0" + "@aws-sdk/util-utf8-browser" "3.186.0" + "@aws-sdk/util-utf8-node" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/client-personalize-events@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/client-personalize-events/-/client-personalize-events-3.6.1.tgz" + dependencies: + "@aws-crypto/sha256-browser" "^1.0.0" + "@aws-crypto/sha256-js" "^1.0.0" + "@aws-sdk/config-resolver" "3.6.1" + "@aws-sdk/credential-provider-node" "3.6.1" + "@aws-sdk/fetch-http-handler" "3.6.1" + "@aws-sdk/hash-node" "3.6.1" + "@aws-sdk/invalid-dependency" "3.6.1" + "@aws-sdk/middleware-content-length" "3.6.1" + "@aws-sdk/middleware-host-header" "3.6.1" + "@aws-sdk/middleware-logger" "3.6.1" + "@aws-sdk/middleware-retry" "3.6.1" + "@aws-sdk/middleware-serde" "3.6.1" + "@aws-sdk/middleware-signing" "3.6.1" + "@aws-sdk/middleware-stack" "3.6.1" + "@aws-sdk/middleware-user-agent" "3.6.1" + "@aws-sdk/node-config-provider" "3.6.1" + "@aws-sdk/node-http-handler" "3.6.1" + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/smithy-client" "3.6.1" + "@aws-sdk/types" "3.6.1" + "@aws-sdk/url-parser" "3.6.1" + "@aws-sdk/url-parser-native" "3.6.1" + "@aws-sdk/util-base64-browser" "3.6.1" + "@aws-sdk/util-base64-node" "3.6.1" + "@aws-sdk/util-body-length-browser" "3.6.1" + "@aws-sdk/util-body-length-node" "3.6.1" + "@aws-sdk/util-user-agent-browser" "3.6.1" + "@aws-sdk/util-user-agent-node" "3.6.1" + "@aws-sdk/util-utf8-browser" "3.6.1" + "@aws-sdk/util-utf8-node" "3.6.1" + tslib "^2.0.0" + +"@aws-sdk/client-polly@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/client-polly/-/client-polly-3.6.1.tgz" + dependencies: + "@aws-crypto/sha256-browser" "^1.0.0" + "@aws-crypto/sha256-js" "^1.0.0" + "@aws-sdk/config-resolver" "3.6.1" + "@aws-sdk/credential-provider-node" "3.6.1" + "@aws-sdk/fetch-http-handler" "3.6.1" + "@aws-sdk/hash-node" "3.6.1" + "@aws-sdk/invalid-dependency" "3.6.1" + "@aws-sdk/middleware-content-length" "3.6.1" + "@aws-sdk/middleware-host-header" "3.6.1" + "@aws-sdk/middleware-logger" "3.6.1" + "@aws-sdk/middleware-retry" "3.6.1" + "@aws-sdk/middleware-serde" "3.6.1" + "@aws-sdk/middleware-signing" "3.6.1" + "@aws-sdk/middleware-stack" "3.6.1" + "@aws-sdk/middleware-user-agent" "3.6.1" + "@aws-sdk/node-config-provider" "3.6.1" + "@aws-sdk/node-http-handler" "3.6.1" + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/smithy-client" "3.6.1" + "@aws-sdk/types" "3.6.1" + "@aws-sdk/url-parser" "3.6.1" + "@aws-sdk/url-parser-native" "3.6.1" + "@aws-sdk/util-base64-browser" "3.6.1" + "@aws-sdk/util-base64-node" "3.6.1" + "@aws-sdk/util-body-length-browser" "3.6.1" + "@aws-sdk/util-body-length-node" "3.6.1" + "@aws-sdk/util-user-agent-browser" "3.6.1" + "@aws-sdk/util-user-agent-node" "3.6.1" + "@aws-sdk/util-utf8-browser" "3.6.1" + "@aws-sdk/util-utf8-node" "3.6.1" + tslib "^2.0.0" + +"@aws-sdk/client-rekognition@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/client-rekognition/-/client-rekognition-3.6.1.tgz" + dependencies: + "@aws-crypto/sha256-browser" "^1.0.0" + "@aws-crypto/sha256-js" "^1.0.0" + "@aws-sdk/config-resolver" "3.6.1" + "@aws-sdk/credential-provider-node" "3.6.1" + "@aws-sdk/fetch-http-handler" "3.6.1" + "@aws-sdk/hash-node" "3.6.1" + "@aws-sdk/invalid-dependency" "3.6.1" + "@aws-sdk/middleware-content-length" "3.6.1" + "@aws-sdk/middleware-host-header" "3.6.1" + "@aws-sdk/middleware-logger" "3.6.1" + "@aws-sdk/middleware-retry" "3.6.1" + "@aws-sdk/middleware-serde" "3.6.1" + "@aws-sdk/middleware-signing" "3.6.1" + "@aws-sdk/middleware-stack" "3.6.1" + "@aws-sdk/middleware-user-agent" "3.6.1" + "@aws-sdk/node-config-provider" "3.6.1" + "@aws-sdk/node-http-handler" "3.6.1" + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/smithy-client" "3.6.1" + "@aws-sdk/types" "3.6.1" + "@aws-sdk/url-parser" "3.6.1" + "@aws-sdk/url-parser-native" "3.6.1" + "@aws-sdk/util-base64-browser" "3.6.1" + "@aws-sdk/util-base64-node" "3.6.1" + "@aws-sdk/util-body-length-browser" "3.6.1" + "@aws-sdk/util-body-length-node" "3.6.1" + "@aws-sdk/util-user-agent-browser" "3.6.1" + "@aws-sdk/util-user-agent-node" "3.6.1" + "@aws-sdk/util-utf8-browser" "3.6.1" + "@aws-sdk/util-utf8-node" "3.6.1" + "@aws-sdk/util-waiter" "3.6.1" + tslib "^2.0.0" + +"@aws-sdk/client-s3@3.6.3": + version "3.6.3" + resolved "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.6.3.tgz" + dependencies: + "@aws-crypto/sha256-browser" "^1.0.0" + "@aws-crypto/sha256-js" "^1.0.0" + "@aws-sdk/config-resolver" "3.6.1" + "@aws-sdk/credential-provider-node" "3.6.1" + "@aws-sdk/eventstream-serde-browser" "3.6.1" + "@aws-sdk/eventstream-serde-config-resolver" "3.6.1" + "@aws-sdk/eventstream-serde-node" "3.6.1" + "@aws-sdk/fetch-http-handler" "3.6.1" + "@aws-sdk/hash-blob-browser" "3.6.1" + "@aws-sdk/hash-node" "3.6.1" + "@aws-sdk/hash-stream-node" "3.6.1" + "@aws-sdk/invalid-dependency" "3.6.1" + "@aws-sdk/md5-js" "3.6.1" + "@aws-sdk/middleware-apply-body-checksum" "3.6.1" + "@aws-sdk/middleware-bucket-endpoint" "3.6.1" + "@aws-sdk/middleware-content-length" "3.6.1" + "@aws-sdk/middleware-expect-continue" "3.6.1" + "@aws-sdk/middleware-host-header" "3.6.1" + "@aws-sdk/middleware-location-constraint" "3.6.1" + "@aws-sdk/middleware-logger" "3.6.1" + "@aws-sdk/middleware-retry" "3.6.1" + "@aws-sdk/middleware-sdk-s3" "3.6.1" + "@aws-sdk/middleware-serde" "3.6.1" + "@aws-sdk/middleware-signing" "3.6.1" + "@aws-sdk/middleware-ssec" "3.6.1" + "@aws-sdk/middleware-stack" "3.6.1" + "@aws-sdk/middleware-user-agent" "3.6.1" + "@aws-sdk/node-config-provider" "3.6.1" + "@aws-sdk/node-http-handler" "3.6.1" + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/smithy-client" "3.6.1" + "@aws-sdk/types" "3.6.1" + "@aws-sdk/url-parser" "3.6.1" + "@aws-sdk/url-parser-native" "3.6.1" + "@aws-sdk/util-base64-browser" "3.6.1" + "@aws-sdk/util-base64-node" "3.6.1" + "@aws-sdk/util-body-length-browser" "3.6.1" + "@aws-sdk/util-body-length-node" "3.6.1" + "@aws-sdk/util-user-agent-browser" "3.6.1" + "@aws-sdk/util-user-agent-node" "3.6.1" + "@aws-sdk/util-utf8-browser" "3.6.1" + "@aws-sdk/util-utf8-node" "3.6.1" + "@aws-sdk/util-waiter" "3.6.1" + "@aws-sdk/xml-builder" "3.6.1" + fast-xml-parser "4.2.4" + tslib "^2.0.0" + +"@aws-sdk/client-sso@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.186.0.tgz" + dependencies: + "@aws-crypto/sha256-browser" "2.0.0" + "@aws-crypto/sha256-js" "2.0.0" + "@aws-sdk/config-resolver" "3.186.0" + "@aws-sdk/fetch-http-handler" "3.186.0" + "@aws-sdk/hash-node" "3.186.0" + "@aws-sdk/invalid-dependency" "3.186.0" + "@aws-sdk/middleware-content-length" "3.186.0" + "@aws-sdk/middleware-host-header" "3.186.0" + "@aws-sdk/middleware-logger" "3.186.0" + "@aws-sdk/middleware-recursion-detection" "3.186.0" + "@aws-sdk/middleware-retry" "3.186.0" + "@aws-sdk/middleware-serde" "3.186.0" + "@aws-sdk/middleware-stack" "3.186.0" + "@aws-sdk/middleware-user-agent" "3.186.0" + "@aws-sdk/node-config-provider" "3.186.0" + "@aws-sdk/node-http-handler" "3.186.0" + "@aws-sdk/protocol-http" "3.186.0" + "@aws-sdk/smithy-client" "3.186.0" + "@aws-sdk/types" "3.186.0" + "@aws-sdk/url-parser" "3.186.0" + "@aws-sdk/util-base64-browser" "3.186.0" + "@aws-sdk/util-base64-node" "3.186.0" + "@aws-sdk/util-body-length-browser" "3.186.0" + "@aws-sdk/util-body-length-node" "3.186.0" + "@aws-sdk/util-defaults-mode-browser" "3.186.0" + "@aws-sdk/util-defaults-mode-node" "3.186.0" + "@aws-sdk/util-user-agent-browser" "3.186.0" + "@aws-sdk/util-user-agent-node" "3.186.0" + "@aws-sdk/util-utf8-browser" "3.186.0" + "@aws-sdk/util-utf8-node" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/client-sts@3.186.2": + version "3.186.2" + resolved "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.186.2.tgz" + dependencies: + "@aws-crypto/sha256-browser" "2.0.0" + "@aws-crypto/sha256-js" "2.0.0" + "@aws-sdk/config-resolver" "3.186.0" + "@aws-sdk/credential-provider-node" "3.186.0" + "@aws-sdk/fetch-http-handler" "3.186.0" + "@aws-sdk/hash-node" "3.186.0" + "@aws-sdk/invalid-dependency" "3.186.0" + "@aws-sdk/middleware-content-length" "3.186.0" + "@aws-sdk/middleware-host-header" "3.186.0" + "@aws-sdk/middleware-logger" "3.186.0" + "@aws-sdk/middleware-recursion-detection" "3.186.0" + "@aws-sdk/middleware-retry" "3.186.0" + "@aws-sdk/middleware-sdk-sts" "3.186.0" + "@aws-sdk/middleware-serde" "3.186.0" + "@aws-sdk/middleware-signing" "3.186.0" + "@aws-sdk/middleware-stack" "3.186.0" + "@aws-sdk/middleware-user-agent" "3.186.0" + "@aws-sdk/node-config-provider" "3.186.0" + "@aws-sdk/node-http-handler" "3.186.0" + "@aws-sdk/protocol-http" "3.186.0" + "@aws-sdk/smithy-client" "3.186.0" + "@aws-sdk/types" "3.186.0" + "@aws-sdk/url-parser" "3.186.0" + "@aws-sdk/util-base64-browser" "3.186.0" + "@aws-sdk/util-base64-node" "3.186.0" + "@aws-sdk/util-body-length-browser" "3.186.0" + "@aws-sdk/util-body-length-node" "3.186.0" + "@aws-sdk/util-defaults-mode-browser" "3.186.0" + "@aws-sdk/util-defaults-mode-node" "3.186.0" + "@aws-sdk/util-user-agent-browser" "3.186.0" + "@aws-sdk/util-user-agent-node" "3.186.0" + "@aws-sdk/util-utf8-browser" "3.186.0" + "@aws-sdk/util-utf8-node" "3.186.0" + entities "2.2.0" + fast-xml-parser "4.2.4" + tslib "^2.3.1" + +"@aws-sdk/client-textract@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/client-textract/-/client-textract-3.6.1.tgz" + dependencies: + "@aws-crypto/sha256-browser" "^1.0.0" + "@aws-crypto/sha256-js" "^1.0.0" + "@aws-sdk/config-resolver" "3.6.1" + "@aws-sdk/credential-provider-node" "3.6.1" + "@aws-sdk/fetch-http-handler" "3.6.1" + "@aws-sdk/hash-node" "3.6.1" + "@aws-sdk/invalid-dependency" "3.6.1" + "@aws-sdk/middleware-content-length" "3.6.1" + "@aws-sdk/middleware-host-header" "3.6.1" + "@aws-sdk/middleware-logger" "3.6.1" + "@aws-sdk/middleware-retry" "3.6.1" + "@aws-sdk/middleware-serde" "3.6.1" + "@aws-sdk/middleware-signing" "3.6.1" + "@aws-sdk/middleware-stack" "3.6.1" + "@aws-sdk/middleware-user-agent" "3.6.1" + "@aws-sdk/node-config-provider" "3.6.1" + "@aws-sdk/node-http-handler" "3.6.1" + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/smithy-client" "3.6.1" + "@aws-sdk/types" "3.6.1" + "@aws-sdk/url-parser" "3.6.1" + "@aws-sdk/url-parser-native" "3.6.1" + "@aws-sdk/util-base64-browser" "3.6.1" + "@aws-sdk/util-base64-node" "3.6.1" + "@aws-sdk/util-body-length-browser" "3.6.1" + "@aws-sdk/util-body-length-node" "3.6.1" + "@aws-sdk/util-user-agent-browser" "3.6.1" + "@aws-sdk/util-user-agent-node" "3.6.1" + "@aws-sdk/util-utf8-browser" "3.6.1" + "@aws-sdk/util-utf8-node" "3.6.1" + tslib "^2.0.0" + +"@aws-sdk/client-translate@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/client-translate/-/client-translate-3.6.1.tgz" + dependencies: + "@aws-crypto/sha256-browser" "^1.0.0" + "@aws-crypto/sha256-js" "^1.0.0" + "@aws-sdk/config-resolver" "3.6.1" + "@aws-sdk/credential-provider-node" "3.6.1" + "@aws-sdk/fetch-http-handler" "3.6.1" + "@aws-sdk/hash-node" "3.6.1" + "@aws-sdk/invalid-dependency" "3.6.1" + "@aws-sdk/middleware-content-length" "3.6.1" + "@aws-sdk/middleware-host-header" "3.6.1" + "@aws-sdk/middleware-logger" "3.6.1" + "@aws-sdk/middleware-retry" "3.6.1" + "@aws-sdk/middleware-serde" "3.6.1" + "@aws-sdk/middleware-signing" "3.6.1" + "@aws-sdk/middleware-stack" "3.6.1" + "@aws-sdk/middleware-user-agent" "3.6.1" + "@aws-sdk/node-config-provider" "3.6.1" + "@aws-sdk/node-http-handler" "3.6.1" + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/smithy-client" "3.6.1" + "@aws-sdk/types" "3.6.1" + "@aws-sdk/url-parser" "3.6.1" + "@aws-sdk/url-parser-native" "3.6.1" + "@aws-sdk/util-base64-browser" "3.6.1" + "@aws-sdk/util-base64-node" "3.6.1" + "@aws-sdk/util-body-length-browser" "3.6.1" + "@aws-sdk/util-body-length-node" "3.6.1" + "@aws-sdk/util-user-agent-browser" "3.6.1" + "@aws-sdk/util-user-agent-node" "3.6.1" + "@aws-sdk/util-utf8-browser" "3.6.1" + "@aws-sdk/util-utf8-node" "3.6.1" + tslib "^2.0.0" + uuid "^3.0.0" + +"@aws-sdk/config-resolver@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.186.0.tgz" + dependencies: + "@aws-sdk/signature-v4" "3.186.0" + "@aws-sdk/types" "3.186.0" + "@aws-sdk/util-config-provider" "3.186.0" + "@aws-sdk/util-middleware" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/config-resolver@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/config-resolver/-/config-resolver-3.6.1.tgz" + dependencies: + "@aws-sdk/signature-v4" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/credential-provider-env@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.186.0.tgz" + dependencies: + "@aws-sdk/property-provider" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-env@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.6.1.tgz" + dependencies: + "@aws-sdk/property-provider" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/credential-provider-imds@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.186.0.tgz" + dependencies: + "@aws-sdk/node-config-provider" "3.186.0" + "@aws-sdk/property-provider" "3.186.0" + "@aws-sdk/types" "3.186.0" + "@aws-sdk/url-parser" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-imds@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.6.1.tgz" + dependencies: + "@aws-sdk/property-provider" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/credential-provider-ini@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.186.0.tgz" + dependencies: + "@aws-sdk/credential-provider-env" "3.186.0" + "@aws-sdk/credential-provider-imds" "3.186.0" + "@aws-sdk/credential-provider-sso" "3.186.0" + "@aws-sdk/credential-provider-web-identity" "3.186.0" + "@aws-sdk/property-provider" "3.186.0" + "@aws-sdk/shared-ini-file-loader" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-ini@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.6.1.tgz" + dependencies: + "@aws-sdk/property-provider" "3.6.1" + "@aws-sdk/shared-ini-file-loader" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/credential-provider-node@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.186.0.tgz" + dependencies: + "@aws-sdk/credential-provider-env" "3.186.0" + "@aws-sdk/credential-provider-imds" "3.186.0" + "@aws-sdk/credential-provider-ini" "3.186.0" + "@aws-sdk/credential-provider-process" "3.186.0" + "@aws-sdk/credential-provider-sso" "3.186.0" + "@aws-sdk/credential-provider-web-identity" "3.186.0" + "@aws-sdk/property-provider" "3.186.0" + "@aws-sdk/shared-ini-file-loader" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-node@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.6.1.tgz" + dependencies: + "@aws-sdk/credential-provider-env" "3.6.1" + "@aws-sdk/credential-provider-imds" "3.6.1" + "@aws-sdk/credential-provider-ini" "3.6.1" + "@aws-sdk/credential-provider-process" "3.6.1" + "@aws-sdk/property-provider" "3.6.1" + "@aws-sdk/shared-ini-file-loader" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/credential-provider-process@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.186.0.tgz" + dependencies: + "@aws-sdk/property-provider" "3.186.0" + "@aws-sdk/shared-ini-file-loader" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-process@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.6.1.tgz" + dependencies: + "@aws-sdk/credential-provider-ini" "3.6.1" + "@aws-sdk/property-provider" "3.6.1" + "@aws-sdk/shared-ini-file-loader" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/credential-provider-sso@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.186.0.tgz" + dependencies: + "@aws-sdk/client-sso" "3.186.0" + "@aws-sdk/property-provider" "3.186.0" + "@aws-sdk/shared-ini-file-loader" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-web-identity@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.186.0.tgz" + dependencies: + "@aws-sdk/property-provider" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/eventstream-codec@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/eventstream-codec/-/eventstream-codec-3.186.0.tgz" + dependencies: + "@aws-crypto/crc32" "2.0.0" + "@aws-sdk/types" "3.186.0" + "@aws-sdk/util-hex-encoding" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/eventstream-handler-node@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.186.0.tgz" + dependencies: + "@aws-sdk/eventstream-codec" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/eventstream-marshaller@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/eventstream-marshaller/-/eventstream-marshaller-3.6.1.tgz" + dependencies: + "@aws-crypto/crc32" "^1.0.0" + "@aws-sdk/types" "3.6.1" + "@aws-sdk/util-hex-encoding" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/eventstream-serde-browser@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/eventstream-serde-browser/-/eventstream-serde-browser-3.186.0.tgz" + dependencies: + "@aws-sdk/eventstream-serde-universal" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/eventstream-serde-browser@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/eventstream-serde-browser/-/eventstream-serde-browser-3.6.1.tgz" + dependencies: + "@aws-sdk/eventstream-marshaller" "3.6.1" + "@aws-sdk/eventstream-serde-universal" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/eventstream-serde-config-resolver@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.186.0.tgz" + dependencies: + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/eventstream-serde-config-resolver@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.6.1.tgz" + dependencies: + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/eventstream-serde-node@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/eventstream-serde-node/-/eventstream-serde-node-3.186.0.tgz" + dependencies: + "@aws-sdk/eventstream-serde-universal" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/eventstream-serde-node@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/eventstream-serde-node/-/eventstream-serde-node-3.6.1.tgz" + dependencies: + "@aws-sdk/eventstream-marshaller" "3.6.1" + "@aws-sdk/eventstream-serde-universal" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/eventstream-serde-universal@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/eventstream-serde-universal/-/eventstream-serde-universal-3.186.0.tgz" + dependencies: + "@aws-sdk/eventstream-codec" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/eventstream-serde-universal@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/eventstream-serde-universal/-/eventstream-serde-universal-3.6.1.tgz" + dependencies: + "@aws-sdk/eventstream-marshaller" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/fetch-http-handler@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.186.0.tgz" + dependencies: + "@aws-sdk/protocol-http" "3.186.0" + "@aws-sdk/querystring-builder" "3.186.0" + "@aws-sdk/types" "3.186.0" + "@aws-sdk/util-base64-browser" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/fetch-http-handler@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.6.1.tgz" + dependencies: + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/querystring-builder" "3.6.1" + "@aws-sdk/types" "3.6.1" + "@aws-sdk/util-base64-browser" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/hash-blob-browser@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/hash-blob-browser/-/hash-blob-browser-3.6.1.tgz" + dependencies: + "@aws-sdk/chunked-blob-reader" "3.6.1" + "@aws-sdk/chunked-blob-reader-native" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/hash-node@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.186.0.tgz" + dependencies: + "@aws-sdk/types" "3.186.0" + "@aws-sdk/util-buffer-from" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/hash-node@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/hash-node/-/hash-node-3.6.1.tgz" + dependencies: + "@aws-sdk/types" "3.6.1" + "@aws-sdk/util-buffer-from" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/hash-stream-node@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/hash-stream-node/-/hash-stream-node-3.6.1.tgz" + dependencies: + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/invalid-dependency@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.186.0.tgz" + dependencies: + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/invalid-dependency@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/invalid-dependency/-/invalid-dependency-3.6.1.tgz" + dependencies: + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/is-array-buffer@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.186.0.tgz" + dependencies: + tslib "^2.3.1" + +"@aws-sdk/is-array-buffer@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/is-array-buffer/-/is-array-buffer-3.6.1.tgz" + dependencies: + tslib "^1.8.0" + +"@aws-sdk/md5-js@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/md5-js/-/md5-js-3.6.1.tgz" + dependencies: + "@aws-sdk/types" "3.6.1" + "@aws-sdk/util-utf8-browser" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/middleware-apply-body-checksum@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-apply-body-checksum/-/middleware-apply-body-checksum-3.6.1.tgz" + dependencies: + "@aws-sdk/is-array-buffer" "3.6.1" + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/middleware-bucket-endpoint@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.6.1.tgz" + dependencies: + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/types" "3.6.1" + "@aws-sdk/util-arn-parser" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/middleware-content-length@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.186.0.tgz" + dependencies: + "@aws-sdk/protocol-http" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-content-length@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-content-length/-/middleware-content-length-3.6.1.tgz" + dependencies: + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/middleware-eventstream@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.186.0.tgz" + dependencies: + "@aws-sdk/protocol-http" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-expect-continue@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.6.1.tgz" + dependencies: + "@aws-sdk/middleware-header-default" "3.6.1" + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/middleware-header-default@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-header-default/-/middleware-header-default-3.6.1.tgz" + dependencies: + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/middleware-host-header@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.186.0.tgz" + dependencies: + "@aws-sdk/protocol-http" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-host-header@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.6.1.tgz" + dependencies: + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/middleware-location-constraint@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.6.1.tgz" + dependencies: + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/middleware-logger@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.186.0.tgz" + dependencies: + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-logger@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.6.1.tgz" + dependencies: + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/middleware-recursion-detection@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.186.0.tgz" + dependencies: + "@aws-sdk/protocol-http" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-retry@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.186.0.tgz" + dependencies: + "@aws-sdk/protocol-http" "3.186.0" + "@aws-sdk/service-error-classification" "3.186.0" + "@aws-sdk/types" "3.186.0" + "@aws-sdk/util-middleware" "3.186.0" + tslib "^2.3.1" + uuid "^8.3.2" + +"@aws-sdk/middleware-retry@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-retry/-/middleware-retry-3.6.1.tgz" + dependencies: + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/service-error-classification" "3.6.1" + "@aws-sdk/types" "3.6.1" + react-native-get-random-values "^1.4.0" + tslib "^1.8.0" + uuid "^3.0.0" + +"@aws-sdk/middleware-sdk-s3@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.6.1.tgz" + dependencies: + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/types" "3.6.1" + "@aws-sdk/util-arn-parser" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/middleware-sdk-sts@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.186.0.tgz" + dependencies: + "@aws-sdk/middleware-signing" "3.186.0" + "@aws-sdk/property-provider" "3.186.0" + "@aws-sdk/protocol-http" "3.186.0" + "@aws-sdk/signature-v4" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-serde@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.186.0.tgz" + dependencies: + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-serde@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-serde/-/middleware-serde-3.6.1.tgz" + dependencies: + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/middleware-signing@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.186.0.tgz" + dependencies: + "@aws-sdk/property-provider" "3.186.0" + "@aws-sdk/protocol-http" "3.186.0" + "@aws-sdk/signature-v4" "3.186.0" + "@aws-sdk/types" "3.186.0" + "@aws-sdk/util-middleware" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-signing@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-signing/-/middleware-signing-3.6.1.tgz" + dependencies: + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/signature-v4" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/middleware-ssec@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.6.1.tgz" + dependencies: + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/middleware-stack@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.186.0.tgz" + dependencies: + tslib "^2.3.1" + +"@aws-sdk/middleware-stack@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-stack/-/middleware-stack-3.6.1.tgz" + dependencies: + tslib "^1.8.0" + +"@aws-sdk/middleware-user-agent@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.186.0.tgz" + dependencies: + "@aws-sdk/protocol-http" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-user-agent@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.6.1.tgz" + dependencies: + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/node-config-provider@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.186.0.tgz" + dependencies: + "@aws-sdk/property-provider" "3.186.0" + "@aws-sdk/shared-ini-file-loader" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/node-config-provider@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/node-config-provider/-/node-config-provider-3.6.1.tgz" + dependencies: + "@aws-sdk/property-provider" "3.6.1" + "@aws-sdk/shared-ini-file-loader" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/node-http-handler@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.186.0.tgz" + dependencies: + "@aws-sdk/abort-controller" "3.186.0" + "@aws-sdk/protocol-http" "3.186.0" + "@aws-sdk/querystring-builder" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/node-http-handler@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/node-http-handler/-/node-http-handler-3.6.1.tgz" + dependencies: + "@aws-sdk/abort-controller" "3.6.1" + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/querystring-builder" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/property-provider@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.186.0.tgz" + dependencies: + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/property-provider@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/property-provider/-/property-provider-3.6.1.tgz" + dependencies: + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/protocol-http@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.186.0.tgz" + dependencies: + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/protocol-http@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.6.1.tgz" + dependencies: + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/querystring-builder@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.186.0.tgz" + dependencies: + "@aws-sdk/types" "3.186.0" + "@aws-sdk/util-uri-escape" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/querystring-builder@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/querystring-builder/-/querystring-builder-3.6.1.tgz" + dependencies: + "@aws-sdk/types" "3.6.1" + "@aws-sdk/util-uri-escape" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/querystring-parser@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.186.0.tgz" + dependencies: + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/querystring-parser@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/querystring-parser/-/querystring-parser-3.6.1.tgz" + dependencies: + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/s3-request-presigner@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.6.1.tgz" + dependencies: + "@aws-sdk/protocol-http" "3.6.1" + "@aws-sdk/signature-v4" "3.6.1" + "@aws-sdk/smithy-client" "3.6.1" + "@aws-sdk/types" "3.6.1" + "@aws-sdk/util-create-request" "3.6.1" + "@aws-sdk/util-format-url" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/service-error-classification@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.186.0.tgz" + +"@aws-sdk/service-error-classification@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/service-error-classification/-/service-error-classification-3.6.1.tgz" + +"@aws-sdk/shared-ini-file-loader@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.186.0.tgz" + dependencies: + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/shared-ini-file-loader@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.6.1.tgz" + dependencies: + tslib "^1.8.0" + +"@aws-sdk/signature-v4@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.186.0.tgz" + dependencies: + "@aws-sdk/is-array-buffer" "3.186.0" + "@aws-sdk/types" "3.186.0" + "@aws-sdk/util-hex-encoding" "3.186.0" + "@aws-sdk/util-middleware" "3.186.0" + "@aws-sdk/util-uri-escape" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/signature-v4@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.6.1.tgz" + dependencies: + "@aws-sdk/is-array-buffer" "3.6.1" + "@aws-sdk/types" "3.6.1" + "@aws-sdk/util-hex-encoding" "3.6.1" + "@aws-sdk/util-uri-escape" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/smithy-client@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.186.0.tgz" + dependencies: + "@aws-sdk/middleware-stack" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/smithy-client@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/smithy-client/-/smithy-client-3.6.1.tgz" + dependencies: + "@aws-sdk/middleware-stack" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/types@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/types/-/types-3.186.0.tgz" + +"@aws-sdk/types@3.6.1", "@aws-sdk/types@^3.1.0": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/types/-/types-3.6.1.tgz" + +"@aws-sdk/types@^3.110.0": + version "3.347.0" + resolved "https://registry.npmjs.org/@aws-sdk/types/-/types-3.347.0.tgz" + dependencies: + tslib "^2.5.0" + +"@aws-sdk/url-parser-native@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/url-parser-native/-/url-parser-native-3.6.1.tgz" + dependencies: + "@aws-sdk/querystring-parser" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + url "^0.11.0" + +"@aws-sdk/url-parser@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.186.0.tgz" + dependencies: + "@aws-sdk/querystring-parser" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/url-parser@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/url-parser/-/url-parser-3.6.1.tgz" + dependencies: + "@aws-sdk/querystring-parser" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/util-arn-parser@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.6.1.tgz" + dependencies: + tslib "^1.8.0" + +"@aws-sdk/util-base64-browser@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-base64-browser/-/util-base64-browser-3.186.0.tgz" + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-base64-browser@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/util-base64-browser/-/util-base64-browser-3.6.1.tgz" + dependencies: + tslib "^1.8.0" + +"@aws-sdk/util-base64-node@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-base64-node/-/util-base64-node-3.186.0.tgz" + dependencies: + "@aws-sdk/util-buffer-from" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/util-base64-node@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/util-base64-node/-/util-base64-node-3.6.1.tgz" + dependencies: + "@aws-sdk/util-buffer-from" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/util-body-length-browser@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.186.0.tgz" + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-body-length-browser@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.6.1.tgz" + dependencies: + tslib "^1.8.0" + +"@aws-sdk/util-body-length-node@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.186.0.tgz" + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-body-length-node@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/util-body-length-node/-/util-body-length-node-3.6.1.tgz" + dependencies: + tslib "^1.8.0" + +"@aws-sdk/util-buffer-from@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.186.0.tgz" + dependencies: + "@aws-sdk/is-array-buffer" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/util-buffer-from@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/util-buffer-from/-/util-buffer-from-3.6.1.tgz" + dependencies: + "@aws-sdk/is-array-buffer" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/util-config-provider@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-config-provider/-/util-config-provider-3.186.0.tgz" + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-create-request@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/util-create-request/-/util-create-request-3.6.1.tgz" + dependencies: + "@aws-sdk/middleware-stack" "3.6.1" + "@aws-sdk/smithy-client" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/util-defaults-mode-browser@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.186.0.tgz" + dependencies: + "@aws-sdk/property-provider" "3.186.0" + "@aws-sdk/types" "3.186.0" + bowser "^2.11.0" + tslib "^2.3.1" + +"@aws-sdk/util-defaults-mode-node@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.186.0.tgz" + dependencies: + "@aws-sdk/config-resolver" "3.186.0" + "@aws-sdk/credential-provider-imds" "3.186.0" + "@aws-sdk/node-config-provider" "3.186.0" + "@aws-sdk/property-provider" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/util-format-url@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.6.1.tgz" + dependencies: + "@aws-sdk/querystring-builder" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/util-hex-encoding@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.186.0.tgz" + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-hex-encoding@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.6.1.tgz" + dependencies: + tslib "^1.8.0" + +"@aws-sdk/util-locate-window@^3.0.0": + version "3.310.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.310.0.tgz" + dependencies: + tslib "^2.5.0" + +"@aws-sdk/util-middleware@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-middleware/-/util-middleware-3.186.0.tgz" + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-uri-escape@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.186.0.tgz" + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-uri-escape@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/util-uri-escape/-/util-uri-escape-3.6.1.tgz" + dependencies: + tslib "^1.8.0" + +"@aws-sdk/util-user-agent-browser@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.186.0.tgz" + dependencies: + "@aws-sdk/types" "3.186.0" + bowser "^2.11.0" + tslib "^2.3.1" + +"@aws-sdk/util-user-agent-browser@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.6.1.tgz" + dependencies: + "@aws-sdk/types" "3.6.1" + bowser "^2.11.0" + tslib "^1.8.0" + +"@aws-sdk/util-user-agent-node@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.186.0.tgz" + dependencies: + "@aws-sdk/node-config-provider" "3.186.0" + "@aws-sdk/types" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/util-user-agent-node@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.6.1.tgz" + dependencies: + "@aws-sdk/node-config-provider" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/util-utf8-browser@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.186.0.tgz" + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-utf8-browser@3.6.1", "@aws-sdk/util-utf8-browser@^3.0.0": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.6.1.tgz" + dependencies: + tslib "^1.8.0" + +"@aws-sdk/util-utf8-node@3.186.0": + version "3.186.0" + resolved "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-3.186.0.tgz" + dependencies: + "@aws-sdk/util-buffer-from" "3.186.0" + tslib "^2.3.1" + +"@aws-sdk/util-utf8-node@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/util-utf8-node/-/util-utf8-node-3.6.1.tgz" + dependencies: + "@aws-sdk/util-buffer-from" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/util-waiter@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/util-waiter/-/util-waiter-3.6.1.tgz" + dependencies: + "@aws-sdk/abort-controller" "3.6.1" + "@aws-sdk/types" "3.6.1" + tslib "^1.8.0" + +"@aws-sdk/xml-builder@3.6.1": + version "3.6.1" + resolved "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.6.1.tgz" + dependencies: + tslib "^1.8.0" + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.22.5", "@babel/code-frame@^7.8.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" + dependencies: + "@babel/highlight" "^7.22.5" + +"@babel/compat-data@^7.22.5", "@babel/compat-data@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.6.tgz#15606a20341de59ba02cd2fcc5086fcbe73bf544" + +"@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.7.2", "@babel/core@^7.8.0": + version "7.22.8" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.8.tgz#386470abe884302db9c82e8e5e87be9e46c86785" + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.7" + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helpers" "^7.22.6" + "@babel/parser" "^7.22.7" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.8" + "@babel/types" "^7.22.5" + "@nicolo-ribaudo/semver-v6" "^6.3.3" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + +"@babel/eslint-parser@^7.16.3": + version "7.22.7" + resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.22.7.tgz#d2807fbd1fa4376162716da63dfd3c69a2249fed" + dependencies: + "@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1" + "@nicolo-ribaudo/semver-v6" "^6.3.3" + eslint-visitor-keys "^2.1.0" + +"@babel/generator@^7.22.7", "@babel/generator@^7.7.2": + version "7.22.7" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.7.tgz#a6b8152d5a621893f2c9dacf9a4e286d520633d5" + dependencies: + "@babel/types" "^7.22.5" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + +"@babel/helper-annotate-as-pure@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz#a3f4758efdd0190d8927fcffd261755937c71878" + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-compilation-targets@^7.22.5", "@babel/helper-compilation-targets@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.6.tgz#e30d61abe9480aa5a83232eb31c111be922d2e52" + dependencies: + "@babel/compat-data" "^7.22.6" + "@babel/helper-validator-option" "^7.22.5" + "@nicolo-ribaudo/semver-v6" "^6.3.3" + browserslist "^4.21.9" + lru-cache "^5.1.1" + +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.22.5", "@babel/helper-create-class-features-plugin@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.6.tgz#58564873c889a6fea05a538e23f9f6d201f10950" + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@nicolo-ribaudo/semver-v6" "^6.3.3" + +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.5": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.6.tgz#87afd63012688ad792de430ceb3b6dc28e4e7a40" + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@nicolo-ribaudo/semver-v6" "^6.3.3" + regexpu-core "^5.3.1" + +"@babel/helper-define-polyfill-provider@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.1.tgz#af1429c4a83ac316a6a8c2cc8ff45cb5d2998d3a" + dependencies: + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-plugin-utils" "^7.22.5" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + +"@babel/helper-environment-visitor@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" + +"@babel/helper-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" + dependencies: + "@babel/template" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helper-hoist-variables@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-member-expression-to-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz#0a7c56117cad3372fbf8d2fb4bf8f8d64a1e76b2" + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.16.7", "@babel/helper-module-imports@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-module-transforms@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz#0f65daa0716961b6e96b164034e737f60a80d2ef" + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helper-optimise-call-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz" + +"@babel/helper-remap-async-to-generator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.5.tgz#14a38141a7bf2165ad38da61d61cf27b43015da2" + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-wrap-function" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helper-replace-supers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.5.tgz#71bc5fb348856dea9fdc4eafd7e2e49f585145dc" + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-member-expression-to-functions" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helper-simple-access@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-skip-transparent-expression-wrappers@^7.20.0", "@babel/helper-skip-transparent-expression-wrappers@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-split-export-declaration@^7.22.5", "@babel/helper-split-export-declaration@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" + dependencies: + "@babel/types" "^7.22.5" + +"@babel/helper-string-parser@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" + +"@babel/helper-validator-identifier@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" + +"@babel/helper-validator-option@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz" + +"@babel/helper-wrap-function@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.5.tgz#44d205af19ed8d872b4eefb0d2fa65f45eb34f06" + dependencies: + "@babel/helper-function-name" "^7.22.5" + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/helpers@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.6.tgz#8e61d3395a4f0c5a8060f309fb008200969b5ecd" + dependencies: + "@babel/template" "^7.22.5" + "@babel/traverse" "^7.22.6" + "@babel/types" "^7.22.5" + +"@babel/highlight@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" + dependencies: + "@babel/helper-validator-identifier" "^7.22.5" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.5", "@babel/parser@^7.22.7": + version "7.22.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae" + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz#87245a21cd69a73b0b81bcda98d443d6df08f05e" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz#fef09f9499b1f1c930da8a0c419db42167d792ca" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.22.5" + +"@babel/plugin-proposal-class-properties@^7.16.0": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz" + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-proposal-decorators@^7.16.4": + version "7.22.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.22.7.tgz#9b5b73c2e404f0869ef8a8a53765f8203c5467a7" + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.6" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/plugin-syntax-decorators" "^7.22.5" + +"@babel/plugin-proposal-nullish-coalescing-operator@^7.16.0": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-proposal-numeric-separator@^7.16.0": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-proposal-optional-chaining@^7.16.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-proposal-private-methods@^7.16.0": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz" + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": + version "7.21.0-placeholder-for-preset-env.2" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" + +"@babel/plugin-proposal-unicode-property-regex@^7.4.4": + version "7.18.6" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz" + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-syntax-async-generators@^7.8.4": + version "7.8.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3": + version "7.12.13" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.12.13" + +"@babel/plugin-syntax-class-static-block@^7.14.5": + version "7.14.5" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-decorators@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.22.5.tgz#329fe2907c73de184033775637dbbc507f09116a" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-dynamic-import@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-export-namespace-from@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-flow@^7.22.5": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.22.5.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-import-assertions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz#07d252e2aa0bc6125567f742cd58619cb14dce98" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-import-attributes@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz#ab840248d834410b829f569f5262b9e517555ecb" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-json-strings@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-jsx@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz#a6b68e84fb76e759fc3b93e901876ffabbe1d918" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3": + version "7.10.4" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.10.4" + +"@babel/plugin-syntax-object-rest-spread@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-catch-binding@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-optional-chaining@^7.8.3": + version "7.8.3" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-private-property-in-object@^7.14.5": + version "7.14.5" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3": + version "7.14.5" + resolved "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.14.5" + +"@babel/plugin-syntax-typescript@^7.22.5", "@babel/plugin-syntax-typescript@^7.7.2": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz#aac8d383b062c5072c647a31ef990c1d0af90272" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-syntax-unicode-sets-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/plugin-transform-arrow-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz#e5ba566d0c58a5b2ba2a8b795450641950b71958" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-async-generator-functions@^7.22.7": + version "7.22.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.7.tgz#053e76c0a903b72b573cb1ab7d6882174d460a1b" + dependencies: + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.5" + "@babel/plugin-syntax-async-generators" "^7.8.4" + +"@babel/plugin-transform-async-to-generator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz#c7a85f44e46f8952f6d27fe57c2ed3cc084c3775" + dependencies: + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.5" + +"@babel/plugin-transform-block-scoped-functions@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz#27978075bfaeb9fa586d3cb63a3d30c1de580024" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-block-scoping@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz#8bfc793b3a4b2742c0983fadc1480d843ecea31b" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-class-properties@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz#97a56e31ad8c9dc06a0b3710ce7803d5a48cca77" + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-class-static-block@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz#3e40c46f048403472d6f4183116d5e46b1bff5ba" + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + +"@babel/plugin-transform-classes@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz#e04d7d804ed5b8501311293d1a0e6d43e94c3363" + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-optimise-call-expression" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + globals "^11.1.0" + +"@babel/plugin-transform-computed-properties@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz#cd1e994bf9f316bd1c2dafcd02063ec261bb3869" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/template" "^7.22.5" + +"@babel/plugin-transform-destructuring@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz#d3aca7438f6c26c78cdd0b0ba920a336001b27cc" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-dotall-regex@^7.22.5", "@babel/plugin-transform-dotall-regex@^7.4.4": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz#dbb4f0e45766eb544e193fb00e65a1dd3b2a4165" + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-duplicate-keys@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz#b6e6428d9416f5f0bba19c70d1e6e7e0b88ab285" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-dynamic-import@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz#d6908a8916a810468c4edff73b5b75bda6ad393e" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + +"@babel/plugin-transform-exponentiation-operator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz#402432ad544a1f9a480da865fda26be653e48f6a" + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-export-namespace-from@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz#57c41cb1d0613d22f548fddd8b288eedb9973a5b" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + +"@babel/plugin-transform-flow-strip-types@^7.16.0": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.22.5.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-flow" "^7.22.5" + +"@babel/plugin-transform-for-of@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz#ab1b8a200a8f990137aff9a084f8de4099ab173f" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-function-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz#935189af68b01898e0d6d99658db6b164205c143" + dependencies: + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-json-strings@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz#14b64352fdf7e1f737eed68de1a1468bd2a77ec0" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-json-strings" "^7.8.3" + +"@babel/plugin-transform-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz#e9341f4b5a167952576e23db8d435849b1dd7920" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-logical-assignment-operators@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz#66ae5f068fd5a9a5dc570df16f56c2a8462a9d6c" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + +"@babel/plugin-transform-member-expression-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz#4fcc9050eded981a468347dd374539ed3e058def" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-modules-amd@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz#4e045f55dcf98afd00f85691a68fc0780704f526" + dependencies: + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-modules-commonjs@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz#7d9875908d19b8c0536085af7b053fd5bd651bfa" + dependencies: + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-simple-access" "^7.22.5" + +"@babel/plugin-transform-modules-systemjs@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz#18c31410b5e579a0092638f95c896c2a98a5d496" + dependencies: + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" + +"@babel/plugin-transform-modules-umd@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz#4694ae40a87b1745e3775b6a7fe96400315d4f98" + dependencies: + "@babel/helper-module-transforms" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-named-capturing-groups-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz#67fe18ee8ce02d57c855185e27e3dc959b2e991f" + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-new-target@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz#1b248acea54ce44ea06dfd37247ba089fcf9758d" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-nullish-coalescing-operator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz#f8872c65776e0b552e0849d7596cddd416c3e381" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + +"@babel/plugin-transform-numeric-separator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz#57226a2ed9e512b9b446517ab6fa2d17abb83f58" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + +"@babel/plugin-transform-object-rest-spread@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz#9686dc3447df4753b0b2a2fae7e8bc33cdc1f2e1" + dependencies: + "@babel/compat-data" "^7.22.5" + "@babel/helper-compilation-targets" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.22.5" + +"@babel/plugin-transform-object-super@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz#794a8d2fcb5d0835af722173c1a9d704f44e218c" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-replace-supers" "^7.22.5" + +"@babel/plugin-transform-optional-catch-binding@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz#842080be3076703be0eaf32ead6ac8174edee333" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + +"@babel/plugin-transform-optional-chaining@^7.22.5", "@babel/plugin-transform-optional-chaining@^7.22.6": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.6.tgz#4bacfe37001fe1901117672875e931d439811564" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +"@babel/plugin-transform-parameters@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz#c3542dd3c39b42c8069936e48717a8d179d63a18" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-private-methods@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz#21c8af791f76674420a147ae62e9935d790f8722" + dependencies: + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-private-property-in-object@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz#07a77f28cbb251546a43d175a1dda4cf3ef83e32" + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + +"@babel/plugin-transform-property-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz#b5ddabd73a4f7f26cd0e20f5db48290b88732766" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-react-constant-elements@^7.12.1": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-constant-elements/-/plugin-transform-react-constant-elements-7.22.5.tgz#6dfa7c1c37f7d7279e417ceddf5a04abb8bb9c29" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-react-display-name@^7.16.0", "@babel/plugin-transform-react-display-name@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.22.5.tgz#3c4326f9fce31c7968d6cb9debcaf32d9e279a2b" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-react-jsx-development@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz#e716b6edbef972a92165cd69d92f1255f7e73e87" + dependencies: + "@babel/plugin-transform-react-jsx" "^7.22.5" + +"@babel/plugin-transform-react-jsx@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.5.tgz#932c291eb6dd1153359e2a90cb5e557dcf068416" + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-jsx" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/plugin-transform-react-pure-annotations@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.22.5.tgz#1f58363eef6626d6fa517b95ac66fe94685e32c0" + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-regenerator@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz#cd8a68b228a5f75fa01420e8cc2fc400f0fc32aa" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + regenerator-transform "^0.15.1" + +"@babel/plugin-transform-reserved-words@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz#832cd35b81c287c4bcd09ce03e22199641f964fb" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-runtime@^7.16.4": + version "7.22.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.7.tgz#eb9094b5fb756cc2d98d398b2c88aeefa9205de9" + dependencies: + "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@nicolo-ribaudo/semver-v6" "^6.3.3" + babel-plugin-polyfill-corejs2 "^0.4.4" + babel-plugin-polyfill-corejs3 "^0.8.2" + babel-plugin-polyfill-regenerator "^0.5.1" + +"@babel/plugin-transform-shorthand-properties@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz#6e277654be82b5559fc4b9f58088507c24f0c624" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-spread@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz#6487fd29f229c95e284ba6c98d65eafb893fea6b" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" + +"@babel/plugin-transform-sticky-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz#295aba1595bfc8197abd02eae5fc288c0deb26aa" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-template-literals@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz#8f38cf291e5f7a8e60e9f733193f0bcc10909bff" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-typeof-symbol@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz#5e2ba478da4b603af8673ff7c54f75a97b716b34" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-typescript@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.5.tgz#5c0f7adfc1b5f38c4dbc8f79b1f0f8074134bd7d" + dependencies: + "@babel/helper-annotate-as-pure" "^7.22.5" + "@babel/helper-create-class-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/plugin-syntax-typescript" "^7.22.5" + +"@babel/plugin-transform-unicode-escapes@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz#ce0c248522b1cb22c7c992d88301a5ead70e806c" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-property-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz#098898f74d5c1e86660dc112057b2d11227f1c81" + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz#ce7e7bb3ef208c4ff67e02a22816656256d7a183" + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/plugin-transform-unicode-sets-regex@^7.22.5": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz#77788060e511b708ffc7d42fdfbc5b37c3004e91" + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.22.5" + "@babel/helper-plugin-utils" "^7.22.5" + +"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.1", "@babel/preset-env@^7.16.4": + version "7.22.7" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.7.tgz#a1ef34b64a80653c22ce4d9c25603cfa76fc168a" + dependencies: + "@babel/compat-data" "^7.22.6" + "@babel/helper-compilation-targets" "^7.22.6" + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.22.5" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.22.5" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.22.5" + "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.22.5" + "@babel/plugin-syntax-import-attributes" "^7.22.5" + "@babel/plugin-syntax-import-meta" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" + "@babel/plugin-transform-arrow-functions" "^7.22.5" + "@babel/plugin-transform-async-generator-functions" "^7.22.7" + "@babel/plugin-transform-async-to-generator" "^7.22.5" + "@babel/plugin-transform-block-scoped-functions" "^7.22.5" + "@babel/plugin-transform-block-scoping" "^7.22.5" + "@babel/plugin-transform-class-properties" "^7.22.5" + "@babel/plugin-transform-class-static-block" "^7.22.5" + "@babel/plugin-transform-classes" "^7.22.6" + "@babel/plugin-transform-computed-properties" "^7.22.5" + "@babel/plugin-transform-destructuring" "^7.22.5" + "@babel/plugin-transform-dotall-regex" "^7.22.5" + "@babel/plugin-transform-duplicate-keys" "^7.22.5" + "@babel/plugin-transform-dynamic-import" "^7.22.5" + "@babel/plugin-transform-exponentiation-operator" "^7.22.5" + "@babel/plugin-transform-export-namespace-from" "^7.22.5" + "@babel/plugin-transform-for-of" "^7.22.5" + "@babel/plugin-transform-function-name" "^7.22.5" + "@babel/plugin-transform-json-strings" "^7.22.5" + "@babel/plugin-transform-literals" "^7.22.5" + "@babel/plugin-transform-logical-assignment-operators" "^7.22.5" + "@babel/plugin-transform-member-expression-literals" "^7.22.5" + "@babel/plugin-transform-modules-amd" "^7.22.5" + "@babel/plugin-transform-modules-commonjs" "^7.22.5" + "@babel/plugin-transform-modules-systemjs" "^7.22.5" + "@babel/plugin-transform-modules-umd" "^7.22.5" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" + "@babel/plugin-transform-new-target" "^7.22.5" + "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.5" + "@babel/plugin-transform-numeric-separator" "^7.22.5" + "@babel/plugin-transform-object-rest-spread" "^7.22.5" + "@babel/plugin-transform-object-super" "^7.22.5" + "@babel/plugin-transform-optional-catch-binding" "^7.22.5" + "@babel/plugin-transform-optional-chaining" "^7.22.6" + "@babel/plugin-transform-parameters" "^7.22.5" + "@babel/plugin-transform-private-methods" "^7.22.5" + "@babel/plugin-transform-private-property-in-object" "^7.22.5" + "@babel/plugin-transform-property-literals" "^7.22.5" + "@babel/plugin-transform-regenerator" "^7.22.5" + "@babel/plugin-transform-reserved-words" "^7.22.5" + "@babel/plugin-transform-shorthand-properties" "^7.22.5" + "@babel/plugin-transform-spread" "^7.22.5" + "@babel/plugin-transform-sticky-regex" "^7.22.5" + "@babel/plugin-transform-template-literals" "^7.22.5" + "@babel/plugin-transform-typeof-symbol" "^7.22.5" + "@babel/plugin-transform-unicode-escapes" "^7.22.5" + "@babel/plugin-transform-unicode-property-regex" "^7.22.5" + "@babel/plugin-transform-unicode-regex" "^7.22.5" + "@babel/plugin-transform-unicode-sets-regex" "^7.22.5" + "@babel/preset-modules" "^0.1.5" + "@babel/types" "^7.22.5" + "@nicolo-ribaudo/semver-v6" "^6.3.3" + babel-plugin-polyfill-corejs2 "^0.4.4" + babel-plugin-polyfill-corejs3 "^0.8.2" + babel-plugin-polyfill-regenerator "^0.5.1" + core-js-compat "^3.31.0" + +"@babel/preset-modules@^0.1.5": + version "0.1.5" + resolved "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.5.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" + "@babel/plugin-transform-dotall-regex" "^7.4.4" + "@babel/types" "^7.4.4" + esutils "^2.0.2" + +"@babel/preset-react@^7.12.5", "@babel/preset-react@^7.16.0": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.22.5.tgz#c4d6058fbf80bccad02dd8c313a9aaa67e3c3dd6" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.22.5" + "@babel/plugin-transform-react-display-name" "^7.22.5" + "@babel/plugin-transform-react-jsx" "^7.22.5" + "@babel/plugin-transform-react-jsx-development" "^7.22.5" + "@babel/plugin-transform-react-pure-annotations" "^7.22.5" + +"@babel/preset-typescript@^7.16.0": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.22.5.tgz#16367d8b01d640e9a507577ed4ee54e0101e51c8" + dependencies: + "@babel/helper-plugin-utils" "^7.22.5" + "@babel/helper-validator-option" "^7.22.5" + "@babel/plugin-syntax-jsx" "^7.22.5" + "@babel/plugin-transform-modules-commonjs" "^7.22.5" + "@babel/plugin-transform-typescript" "^7.22.5" + +"@babel/regjsgen@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" + +"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.9", "@babel/runtime@^7.21.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": + version "7.22.5" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.5.tgz" + dependencies: + regenerator-runtime "^0.13.11" + +"@babel/runtime@^7.20.7", "@babel/runtime@^7.22.5": + version "7.22.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438" + dependencies: + regenerator-runtime "^0.13.11" + +"@babel/template@^7.22.5", "@babel/template@^7.3.3": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/parser" "^7.22.5" + "@babel/types" "^7.22.5" + +"@babel/traverse@^7.22.5", "@babel/traverse@^7.22.6", "@babel/traverse@^7.22.8", "@babel/traverse@^7.7.2": + version "7.22.8" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.8.tgz#4d4451d31bc34efeae01eac222b514a77aa4000e" + dependencies: + "@babel/code-frame" "^7.22.5" + "@babel/generator" "^7.22.7" + "@babel/helper-environment-visitor" "^7.22.5" + "@babel/helper-function-name" "^7.22.5" + "@babel/helper-hoist-variables" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/parser" "^7.22.7" + "@babel/types" "^7.22.5" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.12.6", "@babel/types@^7.20.7", "@babel/types@^7.22.5", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.22.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" + dependencies: + "@babel/helper-string-parser" "^7.22.5" + "@babel/helper-validator-identifier" "^7.22.5" + to-fast-properties "^2.0.0" + +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" + +"@csstools/normalize.css@*": + version "12.0.0" + resolved "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.0.0.tgz" + +"@csstools/postcss-cascade-layers@^1.1.1": + version "1.1.1" + resolved "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz" + dependencies: + "@csstools/selector-specificity" "^2.0.2" + postcss-selector-parser "^6.0.10" + +"@csstools/postcss-color-function@^1.1.1": + version "1.1.1" + resolved "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz" + dependencies: + "@csstools/postcss-progressive-custom-properties" "^1.1.0" + postcss-value-parser "^4.2.0" + +"@csstools/postcss-font-format-keywords@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-hwb-function@^1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-ic-unit@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz" + dependencies: + "@csstools/postcss-progressive-custom-properties" "^1.1.0" + postcss-value-parser "^4.2.0" + +"@csstools/postcss-is-pseudo-class@^2.0.7": + version "2.0.7" + resolved "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz" + dependencies: + "@csstools/selector-specificity" "^2.0.0" + postcss-selector-parser "^6.0.10" + +"@csstools/postcss-nested-calc@^1.0.0": + version "1.0.0" + resolved "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-normalize-display-values@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-oklab-function@^1.1.1": + version "1.1.1" + resolved "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz" + dependencies: + "@csstools/postcss-progressive-custom-properties" "^1.1.0" + postcss-value-parser "^4.2.0" + +"@csstools/postcss-progressive-custom-properties@^1.1.0", "@csstools/postcss-progressive-custom-properties@^1.3.0": + version "1.3.0" + resolved "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-stepped-value-functions@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-text-decoration-shorthand@^1.0.0": + version "1.0.0" + resolved "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-trigonometric-functions@^1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +"@csstools/postcss-unset-value@^1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz" + +"@csstools/selector-specificity@^2.0.0", "@csstools/selector-specificity@^2.0.2": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz#2cbcf822bf3764c9658c4d2e568bd0c0cb748016" + +"@date-io/core@^2.15.0", "@date-io/core@^2.16.0": + version "2.16.0" + resolved "https://registry.npmjs.org/@date-io/core/-/core-2.16.0.tgz" + +"@date-io/date-fns@^2.15.0": + version "2.16.0" + resolved "https://registry.npmjs.org/@date-io/date-fns/-/date-fns-2.16.0.tgz" + dependencies: + "@date-io/core" "^2.16.0" + +"@date-io/dayjs@^2.15.0": + version "2.16.0" + resolved "https://registry.npmjs.org/@date-io/dayjs/-/dayjs-2.16.0.tgz" + dependencies: + "@date-io/core" "^2.16.0" + +"@date-io/luxon@^2.15.0": + version "2.16.1" + resolved "https://registry.npmjs.org/@date-io/luxon/-/luxon-2.16.1.tgz" + dependencies: + "@date-io/core" "^2.16.0" + +"@date-io/moment@^2.15.0": + version "2.16.1" + resolved "https://registry.npmjs.org/@date-io/moment/-/moment-2.16.1.tgz" + dependencies: + "@date-io/core" "^2.16.0" + +"@emotion/babel-plugin@^11.11.0": + version "11.11.0" + resolved "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz" + dependencies: + "@babel/helper-module-imports" "^7.16.7" + "@babel/runtime" "^7.18.3" + "@emotion/hash" "^0.9.1" + "@emotion/memoize" "^0.8.1" + "@emotion/serialize" "^1.1.2" + babel-plugin-macros "^3.1.0" + convert-source-map "^1.5.0" + escape-string-regexp "^4.0.0" + find-root "^1.1.0" + source-map "^0.5.7" + stylis "4.2.0" + +"@emotion/cache@^10.0.27": + version "10.0.29" + resolved "https://registry.npmjs.org/@emotion/cache/-/cache-10.0.29.tgz" + dependencies: + "@emotion/sheet" "0.9.4" + "@emotion/stylis" "0.8.5" + "@emotion/utils" "0.11.3" + "@emotion/weak-memoize" "0.2.5" + +"@emotion/cache@^11.11.0": + version "11.11.0" + resolved "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz" + dependencies: + "@emotion/memoize" "^0.8.1" + "@emotion/sheet" "^1.2.2" + "@emotion/utils" "^1.2.1" + "@emotion/weak-memoize" "^0.3.1" + stylis "4.2.0" + +"@emotion/core@^10.0.28": + version "10.3.1" + resolved "https://registry.npmjs.org/@emotion/core/-/core-10.3.1.tgz" + dependencies: + "@babel/runtime" "^7.5.5" + "@emotion/cache" "^10.0.27" + "@emotion/css" "^10.0.27" + "@emotion/serialize" "^0.11.15" + "@emotion/sheet" "0.9.4" + "@emotion/utils" "0.11.3" + +"@emotion/css@^10.0.27": + version "10.0.27" + resolved "https://registry.npmjs.org/@emotion/css/-/css-10.0.27.tgz" + dependencies: + "@emotion/serialize" "^0.11.15" + "@emotion/utils" "0.11.3" + babel-plugin-emotion "^10.0.27" + +"@emotion/hash@0.8.0": + version "0.8.0" + resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz" + +"@emotion/hash@^0.9.1": + version "0.9.1" + resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz" + +"@emotion/is-prop-valid@0.8.8": + version "0.8.8" + resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz" + dependencies: + "@emotion/memoize" "0.7.4" + +"@emotion/is-prop-valid@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz#23116cf1ed18bfeac910ec6436561ecb1a3885cc" + dependencies: + "@emotion/memoize" "^0.8.1" + +"@emotion/memoize@0.7.4": + version "0.7.4" + resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz" + +"@emotion/memoize@^0.8.1": + version "0.8.1" + resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz" + +"@emotion/react@^11.8.2": + version "11.11.1" + resolved "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz" + dependencies: + "@babel/runtime" "^7.18.3" + "@emotion/babel-plugin" "^11.11.0" + "@emotion/cache" "^11.11.0" + "@emotion/serialize" "^1.1.2" + "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1" + "@emotion/utils" "^1.2.1" + "@emotion/weak-memoize" "^0.3.1" + hoist-non-react-statics "^3.3.1" + +"@emotion/serialize@^0.11.15", "@emotion/serialize@^0.11.16": + version "0.11.16" + resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.11.16.tgz" + dependencies: + "@emotion/hash" "0.8.0" + "@emotion/memoize" "0.7.4" + "@emotion/unitless" "0.7.5" + "@emotion/utils" "0.11.3" + csstype "^2.5.7" + +"@emotion/serialize@^1.1.2": + version "1.1.2" + resolved "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz" + dependencies: + "@emotion/hash" "^0.9.1" + "@emotion/memoize" "^0.8.1" + "@emotion/unitless" "^0.8.1" + "@emotion/utils" "^1.2.1" + csstype "^3.0.2" + +"@emotion/sheet@0.9.4": + version "0.9.4" + resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-0.9.4.tgz" + +"@emotion/sheet@^1.2.2": + version "1.2.2" + resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz" + +"@emotion/styled-base@^10.3.0": + version "10.3.0" + resolved "https://registry.npmjs.org/@emotion/styled-base/-/styled-base-10.3.0.tgz" + dependencies: + "@babel/runtime" "^7.5.5" + "@emotion/is-prop-valid" "0.8.8" + "@emotion/serialize" "^0.11.15" + "@emotion/utils" "0.11.3" + +"@emotion/styled@^10.0.27": + version "10.3.0" + resolved "https://registry.npmjs.org/@emotion/styled/-/styled-10.3.0.tgz" + dependencies: + "@emotion/styled-base" "^10.3.0" + babel-plugin-emotion "^10.0.27" + +"@emotion/styled@^11.8.1": + version "11.11.0" + resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.11.0.tgz#26b75e1b5a1b7a629d7c0a8b708fbf5a9cdce346" + dependencies: + "@babel/runtime" "^7.18.3" + "@emotion/babel-plugin" "^11.11.0" + "@emotion/is-prop-valid" "^1.2.1" + "@emotion/serialize" "^1.1.2" + "@emotion/use-insertion-effect-with-fallbacks" "^1.0.1" + "@emotion/utils" "^1.2.1" + +"@emotion/stylis@0.8.5": + version "0.8.5" + resolved "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz" + +"@emotion/unitless@0.7.5": + version "0.7.5" + resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz" + +"@emotion/unitless@^0.8.1": + version "0.8.1" + resolved "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz" + +"@emotion/use-insertion-effect-with-fallbacks@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz" + +"@emotion/utils@0.11.3": + version "0.11.3" + resolved "https://registry.npmjs.org/@emotion/utils/-/utils-0.11.3.tgz" + +"@emotion/utils@^1.2.1": + version "1.2.1" + resolved "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz" + +"@emotion/weak-memoize@0.2.5": + version "0.2.5" + resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz" + +"@emotion/weak-memoize@^0.3.1": + version "0.3.1" + resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz" + +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.4.0": + version "4.5.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.1.tgz#cdd35dce4fa1a89a4fd42b1599eb35b3af408884" + +"@eslint/eslintrc@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.0.tgz#82256f164cc9e0b59669efc19d57f8092706841d" + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.44.0": + version "8.44.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.44.0.tgz#961a5903c74139390478bdc808bcde3fc45ab7af" + +"@graphql-typed-document-node/core@^3.1.1": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861" + +"@humanwhocodes/config-array@^0.11.10": + version "0.11.10" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz" + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz" + +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.1.0" + resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz" + dependencies: + camelcase "^5.3.1" + find-up "^4.1.0" + get-package-type "^0.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" + +"@istanbuljs/schema@^0.1.2": + version "0.1.3" + resolved "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz" + +"@jest/console@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz" + dependencies: + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^27.5.1" + jest-util "^27.5.1" + slash "^3.0.0" + +"@jest/console@^28.1.3": + version "28.1.3" + resolved "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz" + dependencies: + "@jest/types" "^28.1.3" + "@types/node" "*" + chalk "^4.0.0" + jest-message-util "^28.1.3" + jest-util "^28.1.3" + slash "^3.0.0" + +"@jest/core@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz" + dependencies: + "@jest/console" "^27.5.1" + "@jest/reporters" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.8.1" + exit "^0.1.2" + graceful-fs "^4.2.9" + jest-changed-files "^27.5.1" + jest-config "^27.5.1" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-resolve-dependencies "^27.5.1" + jest-runner "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + jest-watcher "^27.5.1" + micromatch "^4.0.4" + rimraf "^3.0.0" + slash "^3.0.0" + strip-ansi "^6.0.0" + +"@jest/environment@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz" + dependencies: + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + jest-mock "^27.5.1" + +"@jest/expect-utils@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.6.1.tgz#ab83b27a15cdd203fe5f68230ea22767d5c3acc5" + dependencies: + jest-get-type "^29.4.3" + +"@jest/fake-timers@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz" + dependencies: + "@jest/types" "^27.5.1" + "@sinonjs/fake-timers" "^8.0.1" + "@types/node" "*" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-util "^27.5.1" + +"@jest/globals@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz" + dependencies: + "@jest/environment" "^27.5.1" + "@jest/types" "^27.5.1" + expect "^27.5.1" + +"@jest/reporters@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz" + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + collect-v8-coverage "^1.0.0" + exit "^0.1.2" + glob "^7.1.2" + graceful-fs "^4.2.9" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^5.1.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.1.3" + jest-haste-map "^27.5.1" + jest-resolve "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" + slash "^3.0.0" + source-map "^0.6.0" + string-length "^4.0.1" + terminal-link "^2.0.0" + v8-to-istanbul "^8.1.0" + +"@jest/schemas@^28.1.3": + version "28.1.3" + resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz" + dependencies: + "@sinclair/typebox" "^0.24.1" + +"@jest/schemas@^29.6.0": + version "29.6.0" + resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.0.tgz#0f4cb2c8e3dca80c135507ba5635a4fd755b0040" + dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jest/source-map@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz" + dependencies: + callsites "^3.0.0" + graceful-fs "^4.2.9" + source-map "^0.6.0" + +"@jest/test-result@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz" + dependencies: + "@jest/console" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-result@^28.1.3": + version "28.1.3" + resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz" + dependencies: + "@jest/console" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" + +"@jest/test-sequencer@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz" + dependencies: + "@jest/test-result" "^27.5.1" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-runtime "^27.5.1" + +"@jest/transform@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz" + dependencies: + "@babel/core" "^7.1.0" + "@jest/types" "^27.5.1" + babel-plugin-istanbul "^6.1.1" + chalk "^4.0.0" + convert-source-map "^1.4.0" + fast-json-stable-stringify "^2.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-regex-util "^27.5.1" + jest-util "^27.5.1" + micromatch "^4.0.4" + pirates "^4.0.4" + slash "^3.0.0" + source-map "^0.6.1" + write-file-atomic "^3.0.0" + +"@jest/types@^27.5.1": + version "27.5.1" + resolved "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz" + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^16.0.0" + chalk "^4.0.0" + +"@jest/types@^28.1.3": + version "28.1.3" + resolved "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz" + dependencies: + "@jest/schemas" "^28.1.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jest/types@^29.6.1": + version "29.6.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.1.tgz#ae79080278acff0a6af5eb49d063385aaa897bf2" + dependencies: + "@jest/schemas" "^29.6.0" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@3.1.0": + version "3.1.0" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz" + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz" + +"@jridgewell/source-map@^0.3.3": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.14" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" + +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + +"@juggle/resize-observer@^3.3.1": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60" + +"@leichtgewicht/ip-codec@^2.0.1": + version "2.0.4" + resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz" + +"@monaco-editor/loader@^1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.3.3.tgz#7f1742bd3cc21c0362a46a4056317f6e5215cfca" + dependencies: + state-local "^1.0.6" + +"@monaco-editor/react@^4.3.1": + version "4.5.1" + resolved "https://registry.yarnpkg.com/@monaco-editor/react/-/react-4.5.1.tgz#fbc76c692aee9a33b9ab24ae0c5f219b8f002fdb" + dependencies: + "@monaco-editor/loader" "^1.3.3" + +"@mui/base@5.0.0-beta.6": + version "5.0.0-beta.6" + resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.6.tgz#c4537231619f4642ebda714c2cfd0e598aa9f511" + dependencies: + "@babel/runtime" "^7.22.5" + "@emotion/is-prop-valid" "^1.2.1" + "@mui/types" "^7.2.4" + "@mui/utils" "^5.13.7" + "@popperjs/core" "^2.11.8" + clsx "^1.2.1" + prop-types "^15.8.1" + react-is "^18.2.0" + +"@mui/core-downloads-tracker@^5.13.7": + version "5.13.7" + resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.13.7.tgz#f4d9af5fe113b80b98b2cb158263d7b8f77e61c7" + +"@mui/icons-material@^5.5.1": + version "5.13.7" + resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.13.7.tgz#d83532363196b49d8716987e9a2c12f55b233cc1" + dependencies: + "@babel/runtime" "^7.22.5" + +"@mui/lab@^5.0.0-alpha.74": + version "5.0.0-alpha.135" + resolved "https://registry.yarnpkg.com/@mui/lab/-/lab-5.0.0-alpha.135.tgz#d4c4145b63d5839cc201136fc4e152fd31690b44" + dependencies: + "@babel/runtime" "^7.22.5" + "@mui/base" "5.0.0-beta.6" + "@mui/system" "^5.13.7" + "@mui/types" "^7.2.4" + "@mui/utils" "^5.13.7" + clsx "^1.2.1" + prop-types "^15.8.1" + react-is "^18.2.0" + +"@mui/material@^5.5.2": + version "5.13.7" + resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.13.7.tgz#0a4cef14d2a647eb6b049557a795744ff35df755" + dependencies: + "@babel/runtime" "^7.22.5" + "@mui/base" "5.0.0-beta.6" + "@mui/core-downloads-tracker" "^5.13.7" + "@mui/system" "^5.13.7" + "@mui/types" "^7.2.4" + "@mui/utils" "^5.13.7" + "@types/react-transition-group" "^4.4.6" + clsx "^1.2.1" + csstype "^3.1.2" + prop-types "^15.8.1" + react-is "^18.2.0" + react-transition-group "^4.4.5" + +"@mui/private-theming@^5.13.7": + version "5.13.7" + resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.13.7.tgz#2f8ef5da066f3c6c6423bd4260d003a28d10b099" + dependencies: + "@babel/runtime" "^7.22.5" + "@mui/utils" "^5.13.7" + prop-types "^15.8.1" + +"@mui/styled-engine@^5.13.2": + version "5.13.2" + resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.13.2.tgz#c87bd61c0ab8086d34828b6defe97c02bcd642ef" + dependencies: + "@babel/runtime" "^7.21.0" + "@emotion/cache" "^11.11.0" + csstype "^3.1.2" + prop-types "^15.8.1" + +"@mui/styles@^5.5.1": + version "5.13.7" + resolved "https://registry.yarnpkg.com/@mui/styles/-/styles-5.13.7.tgz#b3d98741ccfaad32c6341c9b1dc5072578327d66" + dependencies: + "@babel/runtime" "^7.22.5" + "@emotion/hash" "^0.9.1" + "@mui/private-theming" "^5.13.7" + "@mui/types" "^7.2.4" + "@mui/utils" "^5.13.7" + clsx "^1.2.1" + csstype "^3.1.2" + hoist-non-react-statics "^3.3.2" + jss "^10.10.0" + jss-plugin-camel-case "^10.10.0" + jss-plugin-default-unit "^10.10.0" + jss-plugin-global "^10.10.0" + jss-plugin-nested "^10.10.0" + jss-plugin-props-sort "^10.10.0" + jss-plugin-rule-value-function "^10.10.0" + jss-plugin-vendor-prefixer "^10.10.0" + prop-types "^15.8.1" + +"@mui/system@^5.13.7": + version "5.13.7" + resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.13.7.tgz#b02e6284bbaab4201b142546ebbb2012ec0fa63d" + dependencies: + "@babel/runtime" "^7.22.5" + "@mui/private-theming" "^5.13.7" + "@mui/styled-engine" "^5.13.2" + "@mui/types" "^7.2.4" + "@mui/utils" "^5.13.7" + clsx "^1.2.1" + csstype "^3.1.2" + prop-types "^15.8.1" + +"@mui/types@^7.2.4": + version "7.2.4" + resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.4.tgz#b6fade19323b754c5c6de679a38f068fd50b9328" + +"@mui/utils@^5.10.3": + version "5.13.1" + resolved "https://registry.npmjs.org/@mui/utils/-/utils-5.13.1.tgz" + dependencies: + "@babel/runtime" "^7.21.0" + "@types/prop-types" "^15.7.5" + "@types/react-is" "^18.2.0" + prop-types "^15.8.1" + react-is "^18.2.0" + +"@mui/utils@^5.13.7": + version "5.13.7" + resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.13.7.tgz#7e6a8336e05eb2642667a5c02eb605351e27ec20" + dependencies: + "@babel/runtime" "^7.22.5" + "@types/prop-types" "^15.7.5" + "@types/react-is" "^18.2.1" + prop-types "^15.8.1" + react-is "^18.2.0" + +"@mui/x-data-grid@^5.7.0": + version "5.17.26" + resolved "https://registry.yarnpkg.com/@mui/x-data-grid/-/x-data-grid-5.17.26.tgz#1f7fa73dd3986cf052e2fd2cb56eb4678a7bd913" + dependencies: + "@babel/runtime" "^7.18.9" + "@mui/utils" "^5.10.3" + clsx "^1.2.1" + prop-types "^15.8.1" + reselect "^4.1.6" + +"@mui/x-date-pickers@^5.0.0": + version "5.0.20" + resolved "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-5.0.20.tgz" + dependencies: + "@babel/runtime" "^7.18.9" + "@date-io/core" "^2.15.0" + "@date-io/date-fns" "^2.15.0" + "@date-io/dayjs" "^2.15.0" + "@date-io/luxon" "^2.15.0" + "@date-io/moment" "^2.15.0" + "@mui/utils" "^5.10.3" + "@types/react-transition-group" "^4.4.5" + clsx "^1.2.1" + prop-types "^15.7.2" + react-transition-group "^4.4.5" + rifm "^0.12.1" + +"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1": + version "5.1.1-v1" + resolved "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz" + dependencies: + eslint-scope "5.1.1" + +"@nicolo-ribaudo/semver-v6@^6.3.3": + version "6.3.3" + resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz#ea6d23ade78a325f7a52750aab1526b02b628c29" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@pmmmwh/react-refresh-webpack-plugin@^0.5.3": + version "0.5.10" + resolved "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.10.tgz" + dependencies: + ansi-html-community "^0.0.8" + common-path-prefix "^3.0.0" + core-js-pure "^3.23.3" + error-stack-parser "^2.0.6" + find-up "^5.0.0" + html-entities "^2.1.0" + loader-utils "^2.0.4" + schema-utils "^3.0.0" + source-map "^0.7.3" + +"@popperjs/core@^2.11.8": + version "2.11.8" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" + +"@reduxjs/toolkit@^1.8.0": + version "1.9.5" + resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.9.5.tgz#d3987849c24189ca483baa7aa59386c8e52077c4" + dependencies: + immer "^9.0.21" + redux "^4.2.1" + redux-thunk "^2.4.2" + reselect "^4.1.8" + +"@rollup/plugin-babel@^5.2.0": + version "5.3.1" + resolved "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz" + dependencies: + "@babel/helper-module-imports" "^7.10.4" + "@rollup/pluginutils" "^3.1.0" + +"@rollup/plugin-node-resolve@^11.2.1": + version "11.2.1" + resolved "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.1.tgz" + dependencies: + "@rollup/pluginutils" "^3.1.0" + "@types/resolve" "1.17.1" + builtin-modules "^3.1.0" + deepmerge "^4.2.2" + is-module "^1.0.0" + resolve "^1.19.0" + +"@rollup/plugin-replace@^2.4.1": + version "2.4.2" + resolved "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.4.2.tgz" + dependencies: + "@rollup/pluginutils" "^3.1.0" + magic-string "^0.25.7" + +"@rollup/pluginutils@^3.1.0": + version "3.1.0" + resolved "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz" + dependencies: + "@types/estree" "0.0.39" + estree-walker "^1.0.1" + picomatch "^2.2.2" + +"@rushstack/eslint-patch@^1.1.0": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.3.2.tgz#31b9c510d8cada9683549e1dbb4284cca5001faf" + +"@sinclair/typebox@^0.24.1": + version "0.24.51" + resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz" + +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + +"@sinonjs/commons@^1.7.0": + version "1.8.6" + resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz" + dependencies: + type-detect "4.0.8" + +"@sinonjs/fake-timers@^8.0.1": + version "8.1.0" + resolved "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz" + dependencies: + "@sinonjs/commons" "^1.7.0" + +"@surma/rollup-plugin-off-main-thread@^2.2.3": + version "2.2.3" + resolved "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz" + dependencies: + ejs "^3.1.6" + json5 "^2.2.0" + magic-string "^0.25.0" + string.prototype.matchall "^4.0.6" + +"@svgr/babel-plugin-add-jsx-attribute@^5.4.0": + version "5.4.0" + resolved "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz" + +"@svgr/babel-plugin-remove-jsx-attribute@^5.4.0": + version "5.4.0" + resolved "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz" + +"@svgr/babel-plugin-remove-jsx-empty-expression@^5.0.1": + version "5.0.1" + resolved "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz" + +"@svgr/babel-plugin-replace-jsx-attribute-value@^5.0.1": + version "5.0.1" + resolved "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz" + +"@svgr/babel-plugin-svg-dynamic-title@^5.4.0": + version "5.4.0" + resolved "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz" + +"@svgr/babel-plugin-svg-em-dimensions@^5.4.0": + version "5.4.0" + resolved "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz" + +"@svgr/babel-plugin-transform-react-native-svg@^5.4.0": + version "5.4.0" + resolved "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz" + +"@svgr/babel-plugin-transform-svg-component@^5.5.0": + version "5.5.0" + resolved "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz" + +"@svgr/babel-preset@^5.5.0": + version "5.5.0" + resolved "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz" + dependencies: + "@svgr/babel-plugin-add-jsx-attribute" "^5.4.0" + "@svgr/babel-plugin-remove-jsx-attribute" "^5.4.0" + "@svgr/babel-plugin-remove-jsx-empty-expression" "^5.0.1" + "@svgr/babel-plugin-replace-jsx-attribute-value" "^5.0.1" + "@svgr/babel-plugin-svg-dynamic-title" "^5.4.0" + "@svgr/babel-plugin-svg-em-dimensions" "^5.4.0" + "@svgr/babel-plugin-transform-react-native-svg" "^5.4.0" + "@svgr/babel-plugin-transform-svg-component" "^5.5.0" + +"@svgr/core@^5.5.0": + version "5.5.0" + resolved "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz" + dependencies: + "@svgr/plugin-jsx" "^5.5.0" + camelcase "^6.2.0" + cosmiconfig "^7.0.0" + +"@svgr/hast-util-to-babel-ast@^5.5.0": + version "5.5.0" + resolved "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-5.5.0.tgz" + dependencies: + "@babel/types" "^7.12.6" + +"@svgr/plugin-jsx@^5.5.0": + version "5.5.0" + resolved "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz" + dependencies: + "@babel/core" "^7.12.3" + "@svgr/babel-preset" "^5.5.0" + "@svgr/hast-util-to-babel-ast" "^5.5.0" + svg-parser "^2.0.2" + +"@svgr/plugin-svgo@^5.5.0": + version "5.5.0" + resolved "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.5.0.tgz" + dependencies: + cosmiconfig "^7.0.0" + deepmerge "^4.2.2" + svgo "^1.2.2" + +"@svgr/webpack@^5.5.0": + version "5.5.0" + resolved "https://registry.npmjs.org/@svgr/webpack/-/webpack-5.5.0.tgz" + dependencies: + "@babel/core" "^7.12.3" + "@babel/plugin-transform-react-constant-elements" "^7.12.1" + "@babel/preset-env" "^7.12.1" + "@babel/preset-react" "^7.12.5" + "@svgr/core" "^5.5.0" + "@svgr/plugin-jsx" "^5.5.0" + "@svgr/plugin-svgo" "^5.5.0" + loader-utils "^2.0.0" + +"@testing-library/dom@^8.0.0": + version "8.20.1" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.20.1.tgz#2e52a32e46fc88369eef7eef634ac2a192decd9f" + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^5.0.1" + aria-query "5.1.3" + chalk "^4.1.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.5.0" + pretty-format "^27.0.2" + +"@testing-library/jest-dom@^5.16.2": + version "5.16.5" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz#3912846af19a29b2dbf32a6ae9c31ef52580074e" + dependencies: + "@adobe/css-tools" "^4.0.1" + "@babel/runtime" "^7.9.2" + "@types/testing-library__jest-dom" "^5.9.1" + aria-query "^5.0.0" + chalk "^3.0.0" + css.escape "^1.5.1" + dom-accessibility-api "^0.5.6" + lodash "^4.17.15" + redent "^3.0.0" + +"@testing-library/react@^12.1.4": + version "12.1.5" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.5.tgz#bb248f72f02a5ac9d949dea07279095fa577963b" + dependencies: + "@babel/runtime" "^7.12.5" + "@testing-library/dom" "^8.0.0" + "@types/react-dom" "<18.0.0" + +"@testing-library/user-event@^13.5.0": + version "13.5.0" + resolved "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz" + dependencies: + "@babel/runtime" "^7.12.5" + +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz" + +"@trysound/sax@0.2.0": + version "0.2.0" + resolved "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz" + +"@turf/boolean-clockwise@6.5.0": + version "6.5.0" + resolved "https://registry.npmjs.org/@turf/boolean-clockwise/-/boolean-clockwise-6.5.0.tgz" + dependencies: + "@turf/helpers" "^6.5.0" + "@turf/invariant" "^6.5.0" + +"@turf/helpers@^6.5.0": + version "6.5.0" + resolved "https://registry.npmjs.org/@turf/helpers/-/helpers-6.5.0.tgz" + +"@turf/invariant@^6.5.0": + version "6.5.0" + resolved "https://registry.npmjs.org/@turf/invariant/-/invariant-6.5.0.tgz" + dependencies: + "@turf/helpers" "^6.5.0" + +"@types/aria-query@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.1.tgz#3286741fb8f1e1580ac28784add4c7a1d49bdfbc" + +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.1.tgz#916ecea274b0c776fec721e333e55762d3a9614b" + dependencies: + "@babel/parser" "^7.20.7" + "@babel/types" "^7.20.7" + "@types/babel__generator" "*" + "@types/babel__template" "*" + "@types/babel__traverse" "*" + +"@types/babel__generator@*": + version "7.6.4" + resolved "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz" + dependencies: + "@babel/types" "^7.0.0" + +"@types/babel__template@*": + version "7.4.1" + resolved "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz" + dependencies: + "@babel/parser" "^7.1.0" + "@babel/types" "^7.0.0" + +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": + version "7.20.1" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.1.tgz#dd6f1d2411ae677dcb2db008c962598be31d6acf" + dependencies: + "@babel/types" "^7.20.7" + +"@types/body-parser@*": + version "1.19.2" + resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz" + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/bonjour@^3.5.9": + version "3.5.10" + resolved "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz" + dependencies: + "@types/node" "*" + +"@types/connect-history-api-fallback@^1.3.5": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz#9fd20b3974bdc2bcd4ac6567e2e0f6885cb2cf41" + dependencies: + "@types/express-serve-static-core" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.35" + resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz" + dependencies: + "@types/node" "*" + +"@types/cookie@^0.3.3": + version "0.3.3" + resolved "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz" + +"@types/eslint-scope@^3.7.3": + version "3.7.4" + resolved "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz" + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*", "@types/eslint@^7.29.0 || ^8.4.1": + version "8.44.0" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.44.0.tgz#55818eabb376e2272f77fbf5c96c43137c3c1e53" + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" + +"@types/estree@0.0.39": + version "0.0.39" + resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz" + +"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33": + version "4.17.35" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz#c95dd4424f0d32e525d23812aa8ab8e4d3906c4f" + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@*", "@types/express@^4.17.13": + version "4.17.17" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.17.tgz#01d5437f6ef9cfa8668e616e13c2f2ac9a491ae4" + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/graceful-fs@^4.1.2": + version "4.1.6" + resolved "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz" + dependencies: + "@types/node" "*" + +"@types/hoist-non-react-statics@^3.3.0": + version "3.3.1" + resolved "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz" + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + +"@types/html-minifier-terser@^6.0.0": + version "6.1.0" + resolved "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz" + +"@types/http-errors@*": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.1.tgz#20172f9578b225f6c7da63446f56d4ce108d5a65" + +"@types/http-proxy@^1.17.8": + version "1.17.11" + resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.11.tgz#0ca21949a5588d55ac2b659b69035c84bd5da293" + dependencies: + "@types/node" "*" + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": + version "2.0.4" + resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz" + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.1" + resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz" + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/jest@*": + version "29.5.2" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.2.tgz#86b4afc86e3a8f3005b297ed8a72494f89e6395b" + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + +"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.12" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" + +"@types/lodash@^4.14.175": + version "4.14.195" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.195.tgz#bafc975b252eb6cea78882ce8a7b6bf22a6de632" + +"@types/mime@*": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" + +"@types/mime@^1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + +"@types/node@*", "@types/node@>=6": + version "20.4.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.1.tgz#a6033a8718653c50ac4962977e14d0f984d9527d" + +"@types/parse-json@^4.0.0": + version "4.0.0" + resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz" + +"@types/prettier@^2.1.5": + version "2.7.3" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" + +"@types/prop-types@*", "@types/prop-types@^15.7.5": + version "15.7.5" + resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz" + +"@types/q@^1.5.1": + version "1.5.5" + resolved "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz" + +"@types/qs@*": + version "6.9.7" + resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz" + +"@types/range-parser@*": + version "1.2.4" + resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz" + +"@types/react-dom@<18.0.0": + version "17.0.20" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.20.tgz#e0c8901469d732b36d8473b40b679ad899da1b53" + dependencies: + "@types/react" "^17" + +"@types/react-is@^18.2.0": + version "18.2.0" + resolved "https://registry.npmjs.org/@types/react-is/-/react-is-18.2.0.tgz" + dependencies: + "@types/react" "*" + +"@types/react-is@^18.2.1": + version "18.2.1" + resolved "https://registry.yarnpkg.com/@types/react-is/-/react-is-18.2.1.tgz#61d01c2a6fc089a53520c0b66996d458fdc46863" + dependencies: + "@types/react" "*" + +"@types/react-redux@^7.1.20": + version "7.1.25" + resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.25.tgz#de841631205b24f9dfb4967dd4a7901e048f9a88" + dependencies: + "@types/hoist-non-react-statics" "^3.3.0" + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + redux "^4.0.0" + +"@types/react-transition-group@^4.4.5", "@types/react-transition-group@^4.4.6": + version "4.4.6" + resolved "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz" + dependencies: + "@types/react" "*" + +"@types/react@*": + version "18.2.14" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.14.tgz#fa7a6fecf1ce35ca94e74874f70c56ce88f7a127" + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/react@^17": + version "17.0.62" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.62.tgz#2efe8ddf8533500ec44b1334dd1a97caa2f860e3" + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + +"@types/resolve@1.17.1": + version "1.17.1" + resolved "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz" + dependencies: + "@types/node" "*" + +"@types/retry@0.12.0": + version "0.12.0" + resolved "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz" + +"@types/scheduler@*": + version "0.16.3" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5" + +"@types/semver@^7.3.12": + version "7.5.0" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.0.tgz#591c1ce3a702c45ee15f47a42ade72c2fd78978a" + +"@types/send@*": + version "0.17.1" + resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.1.tgz#ed4932b8a2a805f1fe362a70f4e62d0ac994e301" + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-index@^1.9.1": + version "1.9.1" + resolved "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz" + dependencies: + "@types/express" "*" + +"@types/serve-static@*", "@types/serve-static@^1.13.10": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.2.tgz#3e5419ecd1e40e7405d34093f10befb43f63381a" + dependencies: + "@types/http-errors" "*" + "@types/mime" "*" + "@types/node" "*" + +"@types/sockjs@^0.3.33": + version "0.3.33" + resolved "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz" + dependencies: + "@types/node" "*" + +"@types/stack-utils@^2.0.0": + version "2.0.1" + resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz" + +"@types/testing-library__jest-dom@^5.9.1": + version "5.14.7" + resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.7.tgz#fff92bed2a32c58a9224a85603e731519c0a9037" + dependencies: + "@types/jest" "*" + +"@types/trusted-types@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.3.tgz#a136f83b0758698df454e328759dbd3d44555311" + +"@types/ws@^8.5.5": + version "8.5.5" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.5.tgz#af587964aa06682702ee6dcbc7be41a80e4b28eb" + dependencies: + "@types/node" "*" + +"@types/yargs-parser@*": + version "21.0.0" + resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz" + +"@types/yargs@^16.0.0": + version "16.0.5" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.5.tgz" + dependencies: + "@types/yargs-parser" "*" + +"@types/yargs@^17.0.8": + version "17.0.24" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz" + dependencies: + "@types/yargs-parser" "*" + +"@types/zen-observable@^0.8.0": + version "0.8.3" + resolved "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.3.tgz" + +"@typescript-eslint/eslint-plugin@^5.5.0": + version "5.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.61.0.tgz#a1a5290cf33863b4db3fb79350b3c5275a7b1223" + dependencies: + "@eslint-community/regexpp" "^4.4.0" + "@typescript-eslint/scope-manager" "5.61.0" + "@typescript-eslint/type-utils" "5.61.0" + "@typescript-eslint/utils" "5.61.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/experimental-utils@^5.0.0": + version "5.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.61.0.tgz#5ab9f8f1f7e7a43c68a48c450d972c7e400a2be4" + dependencies: + "@typescript-eslint/utils" "5.61.0" + +"@typescript-eslint/parser@^5.5.0": + version "5.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.61.0.tgz#7fbe3e2951904bb843f8932ebedd6e0635bffb70" + dependencies: + "@typescript-eslint/scope-manager" "5.61.0" + "@typescript-eslint/types" "5.61.0" + "@typescript-eslint/typescript-estree" "5.61.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.61.0": + version "5.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.61.0.tgz#b670006d069c9abe6415c41f754b1b5d949ef2b2" + dependencies: + "@typescript-eslint/types" "5.61.0" + "@typescript-eslint/visitor-keys" "5.61.0" + +"@typescript-eslint/type-utils@5.61.0": + version "5.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.61.0.tgz#e90799eb2045c4435ea8378cb31cd8a9fddca47a" + dependencies: + "@typescript-eslint/typescript-estree" "5.61.0" + "@typescript-eslint/utils" "5.61.0" + debug "^4.3.4" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.61.0": + version "5.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.61.0.tgz#e99ff11b5792d791554abab0f0370936d8ca50c0" + +"@typescript-eslint/typescript-estree@5.61.0": + version "5.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.61.0.tgz#4c7caca84ce95bb41aa585d46a764bcc050b92f3" + dependencies: + "@typescript-eslint/types" "5.61.0" + "@typescript-eslint/visitor-keys" "5.61.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.61.0", "@typescript-eslint/utils@^5.58.0": + version "5.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.61.0.tgz#5064838a53e91c754fffbddd306adcca3fe0af36" + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.61.0" + "@typescript-eslint/types" "5.61.0" + "@typescript-eslint/typescript-estree" "5.61.0" + eslint-scope "^5.1.1" + semver "^7.3.7" + +"@typescript-eslint/visitor-keys@5.61.0": + version "5.61.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.61.0.tgz#c79414fa42158fd23bd2bb70952dc5cdbb298140" + dependencies: + "@typescript-eslint/types" "5.61.0" + eslint-visitor-keys "^3.3.0" + +"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" + dependencies: + "@webassemblyjs/helper-numbers" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + +"@webassemblyjs/floating-point-hex-parser@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431" + +"@webassemblyjs/helper-api-error@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" + +"@webassemblyjs/helper-buffer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093" + +"@webassemblyjs/helper-numbers@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5" + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" + +"@webassemblyjs/helper-wasm-section@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577" + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + +"@webassemblyjs/ieee754@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a" + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7" + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" + +"@webassemblyjs/wasm-edit@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab" + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-opt" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + "@webassemblyjs/wast-printer" "1.11.6" + +"@webassemblyjs/wasm-gen@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268" + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wasm-opt@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2" + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + +"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1" + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wast-printer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20" + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@xtuc/long" "4.2.2" + +"@wry/context@^0.4.0": + version "0.4.4" + resolved "https://registry.npmjs.org/@wry/context/-/context-0.4.4.tgz" + dependencies: + "@types/node" ">=6" + tslib "^1.9.3" + +"@wry/context@^0.7.0": + version "0.7.3" + resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.7.3.tgz#240f6dfd4db5ef54f81f6597f6714e58d4f476a1" + dependencies: + tslib "^2.3.0" + +"@wry/equality@^0.1.2": + version "0.1.11" + resolved "https://registry.npmjs.org/@wry/equality/-/equality-0.1.11.tgz" + dependencies: + tslib "^1.9.3" + +"@wry/equality@^0.5.0": + version "0.5.6" + resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.6.tgz#cd4a533c72c3752993ab8cbf682d3d20e3cb601e" + dependencies: + tslib "^2.3.0" + +"@wry/trie@^0.3.0": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.3.2.tgz#a06f235dc184bd26396ba456711f69f8c35097e6" + dependencies: + tslib "^2.3.0" + +"@wry/trie@^0.4.0": + version "0.4.3" + resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.4.3.tgz#077d52c22365871bf3ffcbab8e95cb8bc5689af4" + dependencies: + tslib "^2.3.0" + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz" + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz" + +abab@^2.0.3, abab@^2.0.5: + version "2.0.6" + resolved "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz" + +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: + version "1.3.8" + resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz" + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-globals@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz" + dependencies: + acorn "^7.1.1" + acorn-walk "^7.1.1" + +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" + +acorn-walk@^7.1.1: + version "7.2.0" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz" + +acorn@^7.1.1: + version "7.4.1" + resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" + +acorn@^8.2.4, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + +address@^1.0.1, address@^1.1.2: + version "1.2.2" + resolved "https://registry.npmjs.org/address/-/address-1.2.2.tgz" + +adjust-sourcemap-loader@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-4.0.0.tgz" + dependencies: + loader-utils "^2.0.0" + regex-parser "^2.2.11" + +agent-base@6: + version "6.0.2" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" + dependencies: + debug "4" + +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz" + dependencies: + ajv "^8.0.0" + +ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz" + +ajv-keywords@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + dependencies: + fast-deep-equal "^3.1.3" + +ajv@^6.10.0, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.0.0, ajv@^8.6.0, ajv@^8.9.0: + version "8.12.0" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +amazon-cognito-identity-js@6.2.0: + version "6.2.0" + resolved "https://registry.npmjs.org/amazon-cognito-identity-js/-/amazon-cognito-identity-js-6.2.0.tgz" + dependencies: + "@aws-crypto/sha256-js" "1.2.2" + buffer "4.9.2" + fast-base64-decode "^1.0.0" + isomorphic-unfetch "^3.0.0" + js-cookie "^2.2.1" + +amazon-quicksight-embedding-sdk@^1.18.1: + version "1.20.1" + resolved "https://registry.yarnpkg.com/amazon-quicksight-embedding-sdk/-/amazon-quicksight-embedding-sdk-1.20.1.tgz#bb0f574365b36bc790ac4c43c80ffd6cc2cc0af1" + +ansi-escapes@^4.2.1, ansi-escapes@^4.3.1: + version "4.3.2" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" + dependencies: + type-fest "^0.21.3" + +ansi-html-community@^0.0.8: + version "0.0.8" + resolved "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz" + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz" + +any-promise@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + +anymatch@^3.0.3, anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +apexcharts@^3.33.2: + version "3.41.0" + resolved "https://registry.yarnpkg.com/apexcharts/-/apexcharts-3.41.0.tgz#7aef77275c19dfb925552d6fc8e027443a6d1337" + dependencies: + svg.draggable.js "^2.2.2" + svg.easing.js "^2.0.0" + svg.filter.js "^2.0.2" + svg.pathmorphing.js "^0.1.3" + svg.resize.js "^1.4.3" + svg.select.js "^3.0.1" + +apollo-boost@^0.4.9: + version "0.4.9" + resolved "https://registry.npmjs.org/apollo-boost/-/apollo-boost-0.4.9.tgz" + dependencies: + apollo-cache "^1.3.5" + apollo-cache-inmemory "^1.6.6" + apollo-client "^2.6.10" + apollo-link "^1.0.6" + apollo-link-error "^1.0.3" + apollo-link-http "^1.3.1" + graphql-tag "^2.4.2" + ts-invariant "^0.4.0" + tslib "^1.10.0" + +apollo-cache-inmemory@^1.6.6: + version "1.6.6" + resolved "https://registry.npmjs.org/apollo-cache-inmemory/-/apollo-cache-inmemory-1.6.6.tgz" + dependencies: + apollo-cache "^1.3.5" + apollo-utilities "^1.3.4" + optimism "^0.10.0" + ts-invariant "^0.4.0" + tslib "^1.10.0" + +apollo-cache@1.3.5, apollo-cache@^1.3.5: + version "1.3.5" + resolved "https://registry.npmjs.org/apollo-cache/-/apollo-cache-1.3.5.tgz" + dependencies: + apollo-utilities "^1.3.4" + tslib "^1.10.0" + +apollo-client@^2.6.10: + version "2.6.10" + resolved "https://registry.npmjs.org/apollo-client/-/apollo-client-2.6.10.tgz" + dependencies: + "@types/zen-observable" "^0.8.0" + apollo-cache "1.3.5" + apollo-link "^1.0.0" + apollo-utilities "1.3.4" + symbol-observable "^1.0.2" + ts-invariant "^0.4.0" + tslib "^1.10.0" + zen-observable "^0.8.0" + +apollo-link-error@^1.0.3: + version "1.1.13" + resolved "https://registry.npmjs.org/apollo-link-error/-/apollo-link-error-1.1.13.tgz" + dependencies: + apollo-link "^1.2.14" + apollo-link-http-common "^0.2.16" + tslib "^1.9.3" + +apollo-link-http-common@^0.2.16: + version "0.2.16" + resolved "https://registry.npmjs.org/apollo-link-http-common/-/apollo-link-http-common-0.2.16.tgz" + dependencies: + apollo-link "^1.2.14" + ts-invariant "^0.4.0" + tslib "^1.9.3" + +apollo-link-http@^1.3.1: + version "1.5.17" + resolved "https://registry.npmjs.org/apollo-link-http/-/apollo-link-http-1.5.17.tgz" + dependencies: + apollo-link "^1.2.14" + apollo-link-http-common "^0.2.16" + tslib "^1.9.3" + +apollo-link@^1.0.0, apollo-link@^1.0.6, apollo-link@^1.2.14: + version "1.2.14" + resolved "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.14.tgz" + dependencies: + apollo-utilities "^1.3.0" + ts-invariant "^0.4.0" + tslib "^1.9.3" + zen-observable-ts "^0.8.21" + +apollo-utilities@1.3.4, apollo-utilities@^1.3.0, apollo-utilities@^1.3.4: + version "1.3.4" + resolved "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.3.4.tgz" + dependencies: + "@wry/equality" "^0.1.2" + fast-json-stable-stringify "^2.0.0" + ts-invariant "^0.4.0" + tslib "^1.10.0" + +appbase-js@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/appbase-js/-/appbase-js-5.2.0.tgz" + dependencies: + cross-fetch "^3.1.5" + querystring "^0.2.0" + url-parser-lite "^0.1.0" + +arg@^5.0.2: + version "5.0.2" + resolved "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" + dependencies: + sprintf-js "~1.0.2" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + +aria-query@5.1.3: + version "5.1.3" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" + dependencies: + deep-equal "^2.0.5" + +aria-query@^5.0.0, aria-query@^5.1.3: + version "5.3.0" + resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.3.0.tgz#650c569e41ad90b51b3d7df5e5eed1c7549c103e" + dependencies: + dequal "^2.0.3" + +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" + +array-flatten@^2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz" + +array-includes@^3.1.5, array-includes@^3.1.6: + version "3.1.6" + resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz" + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" + is-string "^1.0.7" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" + +array.prototype.flat@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz" + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + +array.prototype.reduce@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz" + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-array-method-boxes-properly "^1.0.0" + is-string "^1.0.7" + +array.prototype.tosorted@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz" + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.1.3" + +asap@~2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz" + +ast-types-flow@^0.0.7: + version "0.0.7" + resolved "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz" + +async@^3.2.3: + version "3.2.4" + resolved "https://registry.npmjs.org/async/-/async-3.2.4.tgz" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + +at-least-node@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz" + +attr-accept@^2.2.2: + version "2.2.2" + resolved "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz" + +autoprefixer@^10.4.13: + version "10.4.14" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.14.tgz#e28d49902f8e759dd25b153264e862df2705f79d" + dependencies: + browserslist "^4.21.5" + caniuse-lite "^1.0.30001464" + fraction.js "^4.2.0" + normalize-range "^0.1.2" + picocolors "^1.0.0" + postcss-value-parser "^4.2.0" + +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz" + +aws-amplify@^5.2.6: + version "5.2.6" + resolved "https://registry.npmjs.org/aws-amplify/-/aws-amplify-5.2.6.tgz" + dependencies: + "@aws-amplify/analytics" "6.2.0" + "@aws-amplify/api" "5.2.1" + "@aws-amplify/auth" "5.4.1" + "@aws-amplify/cache" "5.1.0" + "@aws-amplify/core" "5.4.0" + "@aws-amplify/datastore" "4.5.1" + "@aws-amplify/geo" "2.0.35" + "@aws-amplify/interactions" "5.1.1" + "@aws-amplify/notifications" "1.2.0" + "@aws-amplify/predictions" "5.2.3" + "@aws-amplify/pubsub" "5.2.1" + "@aws-amplify/storage" "5.4.1" + tslib "^2.0.0" + +axe-core@^4.6.2: + version "4.7.2" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.2.tgz#040a7342b20765cb18bb50b628394c21bccc17a0" + +axios@0.26.0: + version "0.26.0" + resolved "https://registry.npmjs.org/axios/-/axios-0.26.0.tgz" + dependencies: + follow-redirects "^1.14.8" + +axios@^0.26.1: + version "0.26.1" + resolved "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz" + dependencies: + follow-redirects "^1.14.8" + +axobject-query@^3.1.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.2.1.tgz#39c378a6e3b06ca679f29138151e45b2b32da62a" + dependencies: + dequal "^2.0.3" + +babel-jest@^27.4.2, babel-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz" + dependencies: + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/babel__core" "^7.1.14" + babel-plugin-istanbul "^6.1.1" + babel-preset-jest "^27.5.1" + chalk "^4.0.0" + graceful-fs "^4.2.9" + slash "^3.0.0" + +babel-loader@^8.2.3: + version "8.3.0" + resolved "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz" + dependencies: + find-cache-dir "^3.3.1" + loader-utils "^2.0.0" + make-dir "^3.1.0" + schema-utils "^2.6.5" + +babel-plugin-emotion@^10.0.27: + version "10.2.2" + resolved "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-10.2.2.tgz" + dependencies: + "@babel/helper-module-imports" "^7.0.0" + "@emotion/hash" "0.8.0" + "@emotion/memoize" "0.7.4" + "@emotion/serialize" "^0.11.16" + babel-plugin-macros "^2.0.0" + babel-plugin-syntax-jsx "^6.18.0" + convert-source-map "^1.5.0" + escape-string-regexp "^1.0.5" + find-root "^1.1.0" + source-map "^0.5.7" + +babel-plugin-istanbul@^6.1.1: + version "6.1.1" + resolved "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz" + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^5.0.4" + test-exclude "^6.0.0" + +babel-plugin-jest-hoist@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz" + dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" + "@types/babel__core" "^7.0.0" + "@types/babel__traverse" "^7.0.6" + +babel-plugin-macros@^2.0.0: + version "2.8.0" + resolved "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz" + dependencies: + "@babel/runtime" "^7.7.2" + cosmiconfig "^6.0.0" + resolve "^1.12.0" + +babel-plugin-macros@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz" + dependencies: + "@babel/runtime" "^7.12.5" + cosmiconfig "^7.0.0" + resolve "^1.19.0" + +babel-plugin-named-asset-import@^0.3.8: + version "0.3.8" + resolved "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz" + +babel-plugin-polyfill-corejs2@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.4.tgz#9f9a0e1cd9d645cc246a5e094db5c3aa913ccd2b" + dependencies: + "@babel/compat-data" "^7.22.6" + "@babel/helper-define-polyfill-provider" "^0.4.1" + "@nicolo-ribaudo/semver-v6" "^6.3.3" + +babel-plugin-polyfill-corejs3@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.2.tgz#d406c5738d298cd9c66f64a94cf8d5904ce4cc5e" + dependencies: + "@babel/helper-define-polyfill-provider" "^0.4.1" + core-js-compat "^3.31.0" + +babel-plugin-polyfill-regenerator@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.1.tgz#ace7a5eced6dff7d5060c335c52064778216afd3" + dependencies: + "@babel/helper-define-polyfill-provider" "^0.4.1" + +babel-plugin-syntax-jsx@^6.18.0: + version "6.18.0" + resolved "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz" + +babel-plugin-transform-react-remove-prop-types@^0.4.24: + version "0.4.24" + resolved "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz" + +babel-preset-current-node-syntax@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz" + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-import-meta" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-top-level-await" "^7.8.3" + +babel-preset-jest@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz" + dependencies: + babel-plugin-jest-hoist "^27.5.1" + babel-preset-current-node-syntax "^1.0.0" + +babel-preset-react-app@^10.0.1: + version "10.0.1" + resolved "https://registry.npmjs.org/babel-preset-react-app/-/babel-preset-react-app-10.0.1.tgz" + dependencies: + "@babel/core" "^7.16.0" + "@babel/plugin-proposal-class-properties" "^7.16.0" + "@babel/plugin-proposal-decorators" "^7.16.4" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.16.0" + "@babel/plugin-proposal-numeric-separator" "^7.16.0" + "@babel/plugin-proposal-optional-chaining" "^7.16.0" + "@babel/plugin-proposal-private-methods" "^7.16.0" + "@babel/plugin-transform-flow-strip-types" "^7.16.0" + "@babel/plugin-transform-react-display-name" "^7.16.0" + "@babel/plugin-transform-runtime" "^7.16.4" + "@babel/preset-env" "^7.16.4" + "@babel/preset-react" "^7.16.0" + "@babel/preset-typescript" "^7.16.0" + "@babel/runtime" "^7.16.3" + babel-plugin-macros "^3.1.0" + babel-plugin-transform-react-remove-prop-types "^0.4.24" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + +base-64@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz" + +base64-js@^1.0.2, base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" + +batch@0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz" + +bfj@^7.0.2: + version "7.0.2" + resolved "https://registry.npmjs.org/bfj/-/bfj-7.0.2.tgz" + dependencies: + bluebird "^3.5.5" + check-types "^11.1.1" + hoopy "^0.1.4" + tryer "^1.0.1" + +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz" + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" + +bluebird@^3.5.5: + version "3.7.2" + resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" + +body-parser@1.20.1: + version "1.20.1" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz" + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" + +bonjour-service@^1.0.11: + version "1.1.1" + resolved "https://registry.yarnpkg.com/bonjour-service/-/bonjour-service-1.1.1.tgz#960948fa0e0153f5d26743ab15baf8e33752c135" + dependencies: + array-flatten "^2.1.2" + dns-equal "^1.0.0" + fast-deep-equal "^3.1.3" + multicast-dns "^7.2.5" + +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" + +bowser@^2.11.0: + version "2.11.0" + resolved "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz" + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + dependencies: + fill-range "^7.0.1" + +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz" + +browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.18.1, browserslist@^4.21.4, browserslist@^4.21.5, browserslist@^4.21.9: + version "4.21.9" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.9.tgz#e11bdd3c313d7e2a9e87e8b4b0c7872b13897635" + dependencies: + caniuse-lite "^1.0.30001503" + electron-to-chromium "^1.4.431" + node-releases "^2.0.12" + update-browserslist-db "^1.0.11" + +bser@2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz" + dependencies: + node-int64 "^0.4.0" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" + +buffer@4.9.2: + version "4.9.2" + resolved "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +buffer@^5.4.3: + version "5.7.1" + resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +builtin-modules@^3.1.0: + version "3.3.0" + resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz" + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz" + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + +camel-case@^4.1.2: + version "4.1.2" + resolved "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz" + dependencies: + pascal-case "^3.1.2" + tslib "^2.0.3" + +camelcase-css@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz" + +camelcase-keys@6.2.2: + version "6.2.2" + resolved "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz" + dependencies: + camelcase "^5.3.1" + map-obj "^4.0.0" + quick-lru "^4.0.1" + +camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" + +camelcase@^6.2.0, camelcase@^6.2.1: + version "6.3.0" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" + +can-use-dom@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/can-use-dom/-/can-use-dom-0.1.0.tgz" + +caniuse-api@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz" + dependencies: + browserslist "^4.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001464, caniuse-lite@^1.0.30001503: + version "1.0.30001514" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001514.tgz#e2a7e184a23affc9367b7c8d734e7ec4628c1309" + +case-sensitive-paths-webpack-plugin@^2.4.0: + version "2.4.0" + resolved "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz" + +chalk@^2.0.0, chalk@^2.4.1: + version "2.4.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz" + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz" + +char-regex@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz" + +check-types@^11.1.1: + version "11.2.2" + resolved "https://registry.npmjs.org/check-types/-/check-types-11.2.2.tgz" + +chokidar@^3.4.2, chokidar@^3.5.3: + version "3.5.3" + resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz" + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz" + +ci-info@^3.2.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" + +cjs-module-lexer@^1.0.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + +classnames@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" + +clean-css@^5.2.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.2.tgz#70ecc7d4d4114921f5d298349ff86a31a9975224" + dependencies: + source-map "~0.6.0" + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz" + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +clsx@^1.1.0, clsx@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.npmjs.org/co/-/co-4.6.0.tgz" + +coa@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz" + dependencies: + "@types/q" "^1.5.1" + chalk "^2.4.1" + q "^1.1.2" + +collect-v8-coverage@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + +colord@^2.9.1: + version "2.9.3" + resolved "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz" + +colorette@^2.0.10: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" + dependencies: + delayed-stream "~1.0.0" + +commander@^2.20.0, commander@^2.20.3: + version "2.20.3" + resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" + +commander@^4.0.0: + version "4.1.1" + resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz" + +commander@^7.2.0: + version "7.2.0" + resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz" + +commander@^8.3.0: + version "8.3.0" + resolved "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz" + +common-path-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz" + +common-tags@^1.8.0: + version "1.8.2" + resolved "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz" + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz" + +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz" + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz" + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + +confusing-browser-globals@^1.0.11: + version "1.0.11" + resolved "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz" + +connect-history-api-fallback@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz" + +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + +convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: + version "1.9.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz" + +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" + +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz" + +cookie@^0.4.0: + version "0.4.2" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz" + +copy-to-clipboard@^3.3.1: + version "3.3.3" + resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" + dependencies: + toggle-selection "^1.0.6" + +core-js-compat@^3.31.0: + version "3.31.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.31.1.tgz#5084ad1a46858df50ff89ace152441a63ba7aae0" + dependencies: + browserslist "^4.21.9" + +core-js-pure@^3.23.3: + version "3.31.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.31.1.tgz#73d154958881873bc19381df80bddb20c8d0cdb5" + +core-js@^3.0.1, core-js@^3.19.2: + version "3.31.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.31.1.tgz#f2b0eea9be9da0def2c5fece71064a7e5d687653" + +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" + +cosmiconfig@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz" + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.1.0" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.7.2" + +cosmiconfig@^7.0.0: + version "7.1.0" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz" + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + +cross-env@^5.2.0: + version "5.2.1" + resolved "https://registry.npmjs.org/cross-env/-/cross-env-5.2.1.tgz" + dependencies: + cross-spawn "^6.0.5" + +cross-fetch@^3.0.4, cross-fetch@^3.1.5: + version "3.1.6" + resolved "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.6.tgz" + dependencies: + node-fetch "^2.6.11" + +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz" + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +crypto-random-string@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz" + +css-blank-pseudo@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz" + dependencies: + postcss-selector-parser "^6.0.9" + +css-declaration-sorter@^6.3.1: + version "6.4.1" + resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz#28beac7c20bad7f1775be3a7129d7eae409a3a71" + +css-has-pseudo@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz" + dependencies: + postcss-selector-parser "^6.0.9" + +css-loader@^6.5.1: + version "6.8.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.8.1.tgz#0f8f52699f60f5e679eab4ec0fcd68b8e8a50a88" + dependencies: + icss-utils "^5.1.0" + postcss "^8.4.21" + postcss-modules-extract-imports "^3.0.0" + postcss-modules-local-by-default "^4.0.3" + postcss-modules-scope "^3.0.0" + postcss-modules-values "^4.0.0" + postcss-value-parser "^4.2.0" + semver "^7.3.8" + +css-minimizer-webpack-plugin@^3.2.0: + version "3.4.1" + resolved "https://registry.npmjs.org/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.4.1.tgz" + dependencies: + cssnano "^5.0.6" + jest-worker "^27.0.2" + postcss "^8.3.5" + schema-utils "^4.0.0" + serialize-javascript "^6.0.0" + source-map "^0.6.1" + +css-prefers-color-scheme@^6.0.3: + version "6.0.3" + resolved "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz" + +css-select-base-adapter@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz" + +css-select@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz" + dependencies: + boolbase "^1.0.0" + css-what "^3.2.1" + domutils "^1.7.0" + nth-check "^1.0.2" + +css-select@^4.1.3: + version "4.3.0" + resolved "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz" + dependencies: + boolbase "^1.0.0" + css-what "^6.0.1" + domhandler "^4.3.1" + domutils "^2.8.0" + nth-check "^2.0.1" + +css-tree@1.0.0-alpha.37: + version "1.0.0-alpha.37" + resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz" + dependencies: + mdn-data "2.0.4" + source-map "^0.6.1" + +css-tree@^1.1.2, css-tree@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz" + dependencies: + mdn-data "2.0.14" + source-map "^0.6.1" + +css-vendor@^2.0.8: + version "2.0.8" + resolved "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz" + dependencies: + "@babel/runtime" "^7.8.3" + is-in-browser "^1.0.2" + +css-what@^3.2.1: + version "3.4.2" + resolved "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz" + +css-what@^6.0.1: + version "6.1.0" + resolved "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz" + +css.escape@^1.5.1: + version "1.5.1" + resolved "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz" + +cssdb@^7.1.0: + version "7.6.0" + resolved "https://registry.yarnpkg.com/cssdb/-/cssdb-7.6.0.tgz#beac8f7a5f676db62d3c33da517ef4c9eb008f8b" + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" + +cssfilter@0.0.10: + version "0.0.10" + resolved "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz" + +cssnano-preset-default@^5.2.14: + version "5.2.14" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.2.14.tgz#309def4f7b7e16d71ab2438052093330d9ab45d8" + dependencies: + css-declaration-sorter "^6.3.1" + cssnano-utils "^3.1.0" + postcss-calc "^8.2.3" + postcss-colormin "^5.3.1" + postcss-convert-values "^5.1.3" + postcss-discard-comments "^5.1.2" + postcss-discard-duplicates "^5.1.0" + postcss-discard-empty "^5.1.1" + postcss-discard-overridden "^5.1.0" + postcss-merge-longhand "^5.1.7" + postcss-merge-rules "^5.1.4" + postcss-minify-font-values "^5.1.0" + postcss-minify-gradients "^5.1.1" + postcss-minify-params "^5.1.4" + postcss-minify-selectors "^5.2.1" + postcss-normalize-charset "^5.1.0" + postcss-normalize-display-values "^5.1.0" + postcss-normalize-positions "^5.1.1" + postcss-normalize-repeat-style "^5.1.1" + postcss-normalize-string "^5.1.0" + postcss-normalize-timing-functions "^5.1.0" + postcss-normalize-unicode "^5.1.1" + postcss-normalize-url "^5.1.0" + postcss-normalize-whitespace "^5.1.1" + postcss-ordered-values "^5.1.3" + postcss-reduce-initial "^5.1.2" + postcss-reduce-transforms "^5.1.0" + postcss-svgo "^5.1.0" + postcss-unique-selectors "^5.1.1" + +cssnano-utils@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz" + +cssnano@^5.0.6: + version "5.1.15" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.1.15.tgz#ded66b5480d5127fcb44dac12ea5a983755136bf" + dependencies: + cssnano-preset-default "^5.2.14" + lilconfig "^2.0.3" + yaml "^1.10.2" + +csso@^4.0.2, csso@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz" + dependencies: + css-tree "^1.1.2" + +cssom@^0.4.4: + version "0.4.4" + resolved "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz" + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz" + +cssstyle@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz" + dependencies: + cssom "~0.3.6" + +csstype@^2.5.7: + version "2.6.21" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.21.tgz#2efb85b7cc55c80017c66a5ad7cbd931fda3a90e" + +csstype@^3.0.2, csstype@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" + +damerau-levenshtein@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz" + +data-urls@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz" + dependencies: + abab "^2.0.3" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.0.0" + +date-fns@^2.28.0: + version "2.30.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" + dependencies: + "@babel/runtime" "^7.21.0" + +dayjs@^1.11.0, dayjs@^1.11.7: + version "1.11.8" + resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.8.tgz" + +debug@2.6.9, debug@^2.6.0: + version "2.6.9" + resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" + dependencies: + ms "2.0.0" + +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + dependencies: + ms "2.1.2" + +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + dependencies: + ms "^2.1.1" + +decimal.js@^10.2.1: + version "10.4.3" + resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz" + +dedent@^0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz" + +deep-equal@^2.0.5: + version "2.2.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.2.1.tgz#c72ab22f3a7d3503a4ca87dde976fe9978816739" + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + es-get-iterator "^1.1.3" + get-intrinsic "^1.2.0" + is-arguments "^1.1.1" + is-array-buffer "^3.0.2" + is-date-object "^1.0.5" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + isarray "^2.0.5" + object-is "^1.1.5" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.0" + side-channel "^1.0.4" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.9" + +deep-is@^0.1.3, deep-is@~0.1.3: + version "0.1.4" + resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" + +deepmerge@^2.1.1: + version "2.2.1" + resolved "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz" + +deepmerge@^4.2.2: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + +default-gateway@^6.0.3: + version "6.0.3" + resolved "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz" + dependencies: + execa "^5.0.0" + +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz" + +define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" + dependencies: + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" + +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" + +dequal@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" + +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz" + +detect-node@^2.0.4: + version "2.1.0" + resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz" + +detect-port-alt@^1.1.6: + version "1.1.6" + resolved "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz" + dependencies: + address "^1.0.1" + debug "^2.6.0" + +didyoumean@^1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz" + +diff-sequences@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz" + +diff-sequences@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" + dependencies: + path-type "^4.0.0" + +dlv@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz" + +dns-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz" + +dns-packet@^5.2.2: + version "5.6.0" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-5.6.0.tgz#2202c947845c7a63c23ece58f2f70ff6ab4c2f7d" + dependencies: + "@leichtgewicht/ip-codec" "^2.0.1" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz" + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" + dependencies: + esutils "^2.0.2" + +dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: + version "0.5.16" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" + +dom-converter@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz" + dependencies: + utila "~0.4" + +dom-helpers@^5.0.1: + version "5.2.1" + resolved "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz" + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + +dom-serializer@0: + version "0.2.2" + resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz" + dependencies: + domelementtype "^2.0.1" + entities "^2.0.0" + +dom-serializer@^1.0.1: + version "1.4.1" + resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz" + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +domelementtype@1: + version "1.3.1" + resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz" + +domelementtype@^2.0.1, domelementtype@^2.2.0: + version "2.3.0" + resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz" + +domexception@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz" + dependencies: + webidl-conversions "^5.0.0" + +domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: + version "4.3.1" + resolved "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz" + dependencies: + domelementtype "^2.2.0" + +domutils@^1.7.0: + version "1.7.0" + resolved "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz" + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^2.5.2, domutils@^2.8.0: + version "2.8.0" + resolved "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz" + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz" + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +dotenv-expand@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz" + +dotenv@^10.0.0: + version "10.0.0" + resolved "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz" + +downshift@^1.31.2: + version "1.31.16" + resolved "https://registry.npmjs.org/downshift/-/downshift-1.31.16.tgz" + +duplexer@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz" + +echarts-for-react@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/echarts-for-react/-/echarts-for-react-3.0.2.tgz" + dependencies: + fast-deep-equal "^3.1.3" + size-sensor "^1.0.1" + +echarts@^5.3.3: + version "5.4.2" + resolved "https://registry.npmjs.org/echarts/-/echarts-5.4.2.tgz" + dependencies: + tslib "2.3.0" + zrender "5.4.3" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" + +ejs@^3.1.6: + version "3.1.9" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361" + dependencies: + jake "^10.8.5" + +electron-to-chromium@^1.4.431: + version "1.4.454" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.454.tgz#774dc7cb5e58576d0125939ec34a4182f3ccc87d" + +emittery@^0.10.2: + version "0.10.2" + resolved "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz" + +emittery@^0.8.1: + version "0.8.1" + resolved "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz" + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" + +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz" + +emotion-theming@^10.0.27: + version "10.3.0" + resolved "https://registry.npmjs.org/emotion-theming/-/emotion-theming-10.3.0.tgz" + dependencies: + "@babel/runtime" "^7.5.5" + "@emotion/weak-memoize" "0.2.5" + hoist-non-react-statics "^3.3.0" + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz" + +enhanced-resolve@^5.15.0: + version "5.15.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +entities@2.2.0, entities@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz" + +env-cmd@^10.1.0: + version "10.1.0" + resolved "https://registry.npmjs.org/env-cmd/-/env-cmd-10.1.0.tgz" + dependencies: + commander "^4.0.0" + cross-spawn "^7.0.0" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" + dependencies: + is-arrayish "^0.2.1" + +error-stack-parser@^2.0.6: + version "2.1.4" + resolved "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz" + dependencies: + stackframe "^1.3.4" + +es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.21.2: + version "1.21.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" + dependencies: + array-buffer-byte-length "^1.0.0" + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.5" + get-intrinsic "^1.2.0" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.10" + is-weakref "^1.0.2" + object-inspect "^1.12.3" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.4.3" + safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.7" + string.prototype.trimend "^1.0.6" + string.prototype.trimstart "^1.0.6" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.9" + +es-array-method-boxes-properly@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz" + +es-get-iterator@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.3.tgz#3ef87523c5d464d41084b2c3c9c214f1199763d6" + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + is-arguments "^1.1.1" + is-map "^2.0.2" + is-set "^2.0.2" + is-string "^1.0.7" + isarray "^2.0.5" + stop-iteration-iterator "^1.0.0" + +es-module-lexer@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.3.0.tgz#6be9c9e0b4543a60cd166ff6f8b4e9dae0b0c16f" + +es-set-tostringtag@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + has-tostringtag "^1.0.0" + +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz" + dependencies: + has "^1.0.3" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" + +escodegen@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz" + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +eslint-config-react-app@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz" + dependencies: + "@babel/core" "^7.16.0" + "@babel/eslint-parser" "^7.16.3" + "@rushstack/eslint-patch" "^1.1.0" + "@typescript-eslint/eslint-plugin" "^5.5.0" + "@typescript-eslint/parser" "^5.5.0" + babel-preset-react-app "^10.0.1" + confusing-browser-globals "^1.0.11" + eslint-plugin-flowtype "^8.0.3" + eslint-plugin-import "^2.25.3" + eslint-plugin-jest "^25.3.0" + eslint-plugin-jsx-a11y "^6.5.1" + eslint-plugin-react "^7.27.1" + eslint-plugin-react-hooks "^4.3.0" + eslint-plugin-testing-library "^5.0.1" + +eslint-import-resolver-node@^0.3.7: + version "0.3.7" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" + dependencies: + debug "^3.2.7" + is-core-module "^2.11.0" + resolve "^1.22.1" + +eslint-module-utils@^2.7.4: + version "2.8.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" + dependencies: + debug "^3.2.7" + +eslint-plugin-flowtype@^8.0.3: + version "8.0.3" + resolved "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz" + dependencies: + lodash "^4.17.21" + string-natural-compare "^3.0.1" + +eslint-plugin-import@^2.25.3: + version "2.27.5" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65" + dependencies: + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + array.prototype.flatmap "^1.3.1" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.7" + eslint-module-utils "^2.7.4" + has "^1.0.3" + is-core-module "^2.11.0" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.values "^1.1.6" + resolve "^1.22.1" + semver "^6.3.0" + tsconfig-paths "^3.14.1" + +eslint-plugin-jest@^25.3.0: + version "25.7.0" + resolved "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-25.7.0.tgz" + dependencies: + "@typescript-eslint/experimental-utils" "^5.0.0" + +eslint-plugin-jsx-a11y@^6.5.1: + version "6.7.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz#fca5e02d115f48c9a597a6894d5bcec2f7a76976" + dependencies: + "@babel/runtime" "^7.20.7" + aria-query "^5.1.3" + array-includes "^3.1.6" + array.prototype.flatmap "^1.3.1" + ast-types-flow "^0.0.7" + axe-core "^4.6.2" + axobject-query "^3.1.1" + damerau-levenshtein "^1.0.8" + emoji-regex "^9.2.2" + has "^1.0.3" + jsx-ast-utils "^3.3.3" + language-tags "=1.0.5" + minimatch "^3.1.2" + object.entries "^1.1.6" + object.fromentries "^2.0.6" + semver "^6.3.0" + +eslint-plugin-react-hooks@^4.3.0: + version "4.6.0" + resolved "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz" + +eslint-plugin-react@^7.27.1: + version "7.32.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz#e71f21c7c265ebce01bcbc9d0955170c55571f10" + dependencies: + array-includes "^3.1.6" + array.prototype.flatmap "^1.3.1" + array.prototype.tosorted "^1.1.1" + doctrine "^2.1.0" + estraverse "^5.3.0" + jsx-ast-utils "^2.4.1 || ^3.0.0" + minimatch "^3.1.2" + object.entries "^1.1.6" + object.fromentries "^2.0.6" + object.hasown "^1.1.2" + object.values "^1.1.6" + prop-types "^15.8.1" + resolve "^2.0.0-next.4" + semver "^6.3.0" + string.prototype.matchall "^4.0.8" + +eslint-plugin-testing-library@^5.0.1: + version "5.11.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-testing-library/-/eslint-plugin-testing-library-5.11.0.tgz#0bad7668e216e20dd12f8c3652ca353009163121" + dependencies: + "@typescript-eslint/utils" "^5.58.0" + +eslint-scope@5.1.1, eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994" + +eslint-webpack-plugin@^3.1.1: + version "3.2.0" + resolved "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz" + dependencies: + "@types/eslint" "^7.29.0 || ^8.4.1" + jest-worker "^28.0.2" + micromatch "^4.0.5" + normalize-path "^3.0.0" + schema-utils "^4.0.0" + +eslint@^8.3.0: + version "8.44.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.44.0.tgz#51246e3889b259bbcd1d7d736a0c10add4f0e500" + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.4.0" + "@eslint/eslintrc" "^2.1.0" + "@eslint/js" "8.44.0" + "@humanwhocodes/config-array" "^0.11.10" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.0" + eslint-visitor-keys "^3.4.1" + espree "^9.6.0" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + +espree@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.0.tgz#80869754b1c6560f32e3b6929194a3fe07c5b82f" + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esprima@^4.0.0, esprima@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" + +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz" + +estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: + version "5.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + +estree-walker@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz" + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" + +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" + +events@^3.1.0, events@^3.2.0: + version "3.3.0" + resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz" + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" + +expect@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz" + dependencies: + "@jest/types" "^27.5.1" + jest-get-type "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + +expect@^29.0.0: + version "29.6.1" + resolved "https://registry.yarnpkg.com/expect/-/expect-29.6.1.tgz#64dd1c8f75e2c0b209418f2b8d36a07921adfdf1" + dependencies: + "@jest/expect-utils" "^29.6.1" + "@types/node" "*" + jest-get-type "^29.4.3" + jest-matcher-utils "^29.6.1" + jest-message-util "^29.6.1" + jest-util "^29.6.1" + +express@^4.17.3: + version "4.18.2" + resolved "https://registry.npmjs.org/express/-/express-4.18.2.tgz" + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.1" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.5.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + +fast-base64-decode@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + +fast-glob@^3.2.12, fast-glob@^3.2.9: + version "3.2.12" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz" + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" + +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" + +fast-xml-parser@4.2.4: + version "4.2.4" + resolved "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.4.tgz" + dependencies: + strnum "^1.0.5" + +fastq@^1.6.0: + version "1.15.0" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz" + dependencies: + reusify "^1.0.4" + +faye-websocket@^0.11.3: + version "0.11.4" + resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz" + dependencies: + websocket-driver ">=0.5.1" + +fb-watchman@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz" + dependencies: + bser "2.1.1" + +fflate@0.7.3: + version "0.7.3" + resolved "https://registry.npmjs.org/fflate/-/fflate-0.7.3.tgz" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" + dependencies: + flat-cache "^3.0.4" + +file-loader@^6.2.0: + version "6.2.0" + resolved "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz" + dependencies: + loader-utils "^2.0.0" + schema-utils "^3.0.0" + +file-selector@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.5.0.tgz#21c7126dc9728b31a2742d91cab20d55e67e4fb4" + dependencies: + tslib "^2.0.3" + +filelist@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" + dependencies: + minimatch "^5.0.1" + +filesize@^8.0.6: + version "8.0.7" + resolved "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz" + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + +find-cache-dir@^3.3.1: + version "3.3.2" + resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz" + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + +find-root@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz" + dependencies: + locate-path "^3.0.0" + +find-up@^4.0.0, find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz" + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + +flatted@^3.1.0: + version "3.2.7" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz" + +follow-redirects@^1.0.0, follow-redirects@^1.14.8: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz" + dependencies: + is-callable "^1.1.3" + +fork-ts-checker-webpack-plugin@^6.5.0: + version "6.5.3" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz#eda2eff6e22476a2688d10661688c47f611b37f3" + dependencies: + "@babel/code-frame" "^7.8.3" + "@types/json-schema" "^7.0.5" + chalk "^4.1.0" + chokidar "^3.4.2" + cosmiconfig "^6.0.0" + deepmerge "^4.2.2" + fs-extra "^9.0.0" + glob "^7.1.6" + memfs "^3.1.2" + minimatch "^3.0.4" + schema-utils "2.7.0" + semver "^7.3.2" + tapable "^1.0.0" + +form-data@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +formik@^2.2.9: + version "2.4.2" + resolved "https://registry.yarnpkg.com/formik/-/formik-2.4.2.tgz#a1115457cfb012a5c782cea3ad4b40b2fe36fa18" + dependencies: + deepmerge "^2.1.1" + hoist-non-react-statics "^3.3.0" + lodash "^4.17.21" + lodash-es "^4.17.21" + react-fast-compare "^2.0.1" + tiny-warning "^1.0.2" + tslib "^2.0.0" + +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" + +fraction.js@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz" + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" + +fs-extra@^10.0.0: + version "10.1.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz" + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@^9.0.0, fs-extra@^9.0.1: + version "9.1.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz" + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-monkey@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.4.tgz#ee8c1b53d3fe8bb7e5d2c5c5dfc0168afdd2f747" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + +fsevents@^2.3.2, fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" + +function.prototype.name@^1.1.5: + version "1.1.5" + resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz" + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.19.0" + functions-have-names "^1.2.2" + +functions-have-names@^1.2.2, functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" + +gensync@^1.0.0-beta.2: + version "1.0.0-beta.2" + resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz" + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-proto "^1.0.1" + has-symbols "^1.0.3" + +get-own-enumerable-property-symbols@^3.0.0: + version "3.0.2" + resolved "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz" + +get-package-type@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz" + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" + +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz" + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +glob-parent@^5.1.2, glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + dependencies: + is-glob "^4.0.3" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" + +glob@7.1.6: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: + version "7.2.3" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global-modules@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz" + dependencies: + global-prefix "^3.0.0" + +global-prefix@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz" + dependencies: + ini "^1.3.5" + kind-of "^6.0.2" + which "^1.3.1" + +globals@^11.1.0: + version "11.12.0" + resolved "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz" + +globals@^13.19.0: + version "13.20.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" + dependencies: + type-fest "^0.20.2" + +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz" + dependencies: + define-properties "^1.1.3" + +globby@^11.0.4, globby@^11.1.0: + version "11.1.0" + resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz" + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + +graphql-tag@^2.12.6, graphql-tag@^2.4.2: + version "2.12.6" + resolved "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz" + dependencies: + tslib "^2.1.0" + +graphql@15.8.0: + version "15.8.0" + resolved "https://registry.npmjs.org/graphql/-/graphql-15.8.0.tgz" + +gzip-size@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz" + dependencies: + duplexer "^0.1.2" + +handle-thing@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz" + +harmony-reflect@^1.4.6: + version "1.6.2" + resolved "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz" + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz" + dependencies: + get-intrinsic "^1.1.1" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz" + +has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz" + dependencies: + has-symbols "^1.0.2" + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" + dependencies: + function-bind "^1.1.1" + +he@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" + +history@^5.0.3: + version "5.3.0" + resolved "https://registry.npmjs.org/history/-/history-5.3.0.tgz" + dependencies: + "@babel/runtime" "^7.7.6" + +hoist-non-react-statics@^3.2.1, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2: + version "3.3.2" + resolved "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz" + dependencies: + react-is "^16.7.0" + +hoopy@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/hoopy/-/hoopy-0.1.4.tgz" + +hotkeys-js@^3.8.7: + version "3.11.1" + resolved "https://registry.yarnpkg.com/hotkeys-js/-/hotkeys-js-3.11.1.tgz#63d374faaddd1a28b8c3013e3a486d5cf78e968a" + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz" + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +html-encoding-sniffer@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz" + dependencies: + whatwg-encoding "^1.0.5" + +html-entities@^2.1.0, html-entities@^2.3.2: + version "2.4.0" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.4.0.tgz#edd0cee70402584c8c76cc2c0556db09d1f45061" + +html-escaper@^2.0.0: + version "2.0.2" + resolved "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz" + +html-minifier-terser@^6.0.2: + version "6.1.0" + resolved "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz" + dependencies: + camel-case "^4.1.2" + clean-css "^5.2.2" + commander "^8.3.0" + he "^1.2.0" + param-case "^3.0.4" + relateurl "^0.2.7" + terser "^5.10.0" + +html-webpack-plugin@^5.5.0: + version "5.5.3" + resolved "https://registry.yarnpkg.com/html-webpack-plugin/-/html-webpack-plugin-5.5.3.tgz#72270f4a78e222b5825b296e5e3e1328ad525a3e" + dependencies: + "@types/html-minifier-terser" "^6.0.0" + html-minifier-terser "^6.0.2" + lodash "^4.17.21" + pretty-error "^4.0.0" + tapable "^2.0.0" + +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz" + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz" + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz" + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz" + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-parser-js@>=0.5.1: + version "0.5.8" + resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz" + +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz" + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + +http-proxy-middleware@^2.0.3: + version "2.0.6" + resolved "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz" + dependencies: + "@types/http-proxy" "^1.17.8" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz" + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz" + dependencies: + agent-base "6" + debug "4" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" + +hyphenate-style-name@^1.0.3: + version "1.0.4" + resolved "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz" + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +icss-utils@^5.0.0, icss-utils@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz" + +idb@5.0.6: + version "5.0.6" + resolved "https://registry.npmjs.org/idb/-/idb-5.0.6.tgz" + +idb@^7.0.1: + version "7.1.1" + resolved "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz" + +identity-obj-proxy@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz" + dependencies: + harmony-reflect "^1.4.6" + +ieee754@^1.1.13, ieee754@^1.1.4: + version "1.2.1" + resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" + +ignore@^5.2.0: + version "5.2.4" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz" + +immer@9.0.6: + version "9.0.6" + resolved "https://registry.npmjs.org/immer/-/immer-9.0.6.tgz" + +immer@^9.0.21, immer@^9.0.7: + version "9.0.21" + resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176" + +import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz" + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz" + +ini@^1.3.5: + version "1.3.8" + resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" + +internal-slot@^1.0.3, internal-slot@^1.0.4, internal-slot@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" + dependencies: + get-intrinsic "^1.2.0" + has "^1.0.3" + side-channel "^1.0.4" + +invariant@^2.2.4: + version "2.2.4" + resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz" + dependencies: + loose-envify "^1.0.0" + +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" + +ipaddr.js@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.1.0.tgz#2119bc447ff8c257753b196fc5f1ce08a4cdf39f" + +is-arguments@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + is-typed-array "^1.1.10" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz" + dependencies: + has-bigints "^1.0.1" + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" + dependencies: + binary-extensions "^2.0.0" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz" + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" + +is-core-module@^2.11.0, is-core-module@^2.9.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" + dependencies: + has "^1.0.3" + +is-date-object@^1.0.1, is-date-object@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" + dependencies: + has-tostringtag "^1.0.0" + +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" + +is-generator-fn@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz" + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + dependencies: + is-extglob "^2.1.1" + +is-in-browser@^1.0.2, is-in-browser@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz" + +is-map@^2.0.1, is-map@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" + +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz" + +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz" + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz" + dependencies: + has-tostringtag "^1.0.0" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + +is-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz" + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" + +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz" + +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz" + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz" + +is-root@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz" + +is-set@^2.0.1, is-set@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" + +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz" + dependencies: + call-bind "^1.0.2" + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz" + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz" + dependencies: + has-symbols "^1.0.2" + +is-typed-array@^1.1.10, is-typed-array@^1.1.9: + version "1.1.10" + resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz" + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz" + +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz" + dependencies: + call-bind "^1.0.2" + +is-weakset@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d" + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz" + dependencies: + is-docker "^2.0.0" + +isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + +isomorphic-unfetch@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz" + dependencies: + node-fetch "^2.6.1" + unfetch "^4.2.0" + +istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: + version "3.2.0" + resolved "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz" + +istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: + version "5.2.1" + resolved "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz" + dependencies: + "@babel/core" "^7.12.3" + "@babel/parser" "^7.14.7" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.2.0" + semver "^6.3.0" + +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz" + dependencies: + debug "^4.1.1" + istanbul-lib-coverage "^3.0.0" + source-map "^0.6.1" + +istanbul-reports@^3.1.3: + version "3.1.5" + resolved "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz" + dependencies: + html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" + +jake@^10.8.5: + version "10.8.7" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.7.tgz#63a32821177940c33f356e0ba44ff9d34e1c7d8f" + dependencies: + async "^3.2.3" + chalk "^4.0.2" + filelist "^1.0.4" + minimatch "^3.1.2" + +jest-changed-files@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz" + dependencies: + "@jest/types" "^27.5.1" + execa "^5.0.0" + throat "^6.0.1" + +jest-circus@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz" + dependencies: + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + dedent "^0.7.0" + expect "^27.5.1" + is-generator-fn "^2.0.0" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + slash "^3.0.0" + stack-utils "^2.0.3" + throat "^6.0.1" + +jest-cli@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz" + dependencies: + "@jest/core" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + chalk "^4.0.0" + exit "^0.1.2" + graceful-fs "^4.2.9" + import-local "^3.0.2" + jest-config "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + prompts "^2.0.1" + yargs "^16.2.0" + +jest-config@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz" + dependencies: + "@babel/core" "^7.8.0" + "@jest/test-sequencer" "^27.5.1" + "@jest/types" "^27.5.1" + babel-jest "^27.5.1" + chalk "^4.0.0" + ci-info "^3.2.0" + deepmerge "^4.2.2" + glob "^7.1.1" + graceful-fs "^4.2.9" + jest-circus "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-get-type "^27.5.1" + jest-jasmine2 "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runner "^27.5.1" + jest-util "^27.5.1" + jest-validate "^27.5.1" + micromatch "^4.0.4" + parse-json "^5.2.0" + pretty-format "^27.5.1" + slash "^3.0.0" + strip-json-comments "^3.1.1" + +jest-diff@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz" + dependencies: + chalk "^4.0.0" + diff-sequences "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + +jest-diff@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.6.1.tgz#13df6db0a89ee6ad93c747c75c85c70ba941e545" + dependencies: + chalk "^4.0.0" + diff-sequences "^29.4.3" + jest-get-type "^29.4.3" + pretty-format "^29.6.1" + +jest-docblock@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz" + dependencies: + detect-newline "^3.0.0" + +jest-each@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz" + dependencies: + "@jest/types" "^27.5.1" + chalk "^4.0.0" + jest-get-type "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + +jest-environment-jsdom@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz" + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + jest-mock "^27.5.1" + jest-util "^27.5.1" + jsdom "^16.6.0" + +jest-environment-node@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz" + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + jest-mock "^27.5.1" + jest-util "^27.5.1" + +jest-get-type@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz" + +jest-get-type@^29.4.3: + version "29.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" + +jest-haste-map@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz" + dependencies: + "@jest/types" "^27.5.1" + "@types/graceful-fs" "^4.1.2" + "@types/node" "*" + anymatch "^3.0.3" + fb-watchman "^2.0.0" + graceful-fs "^4.2.9" + jest-regex-util "^27.5.1" + jest-serializer "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" + micromatch "^4.0.4" + walker "^1.0.7" + optionalDependencies: + fsevents "^2.3.2" + +jest-jasmine2@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz" + dependencies: + "@jest/environment" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + co "^4.6.0" + expect "^27.5.1" + is-generator-fn "^2.0.0" + jest-each "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-runtime "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + pretty-format "^27.5.1" + throat "^6.0.1" + +jest-leak-detector@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz" + dependencies: + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + +jest-matcher-utils@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz" + dependencies: + chalk "^4.0.0" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + pretty-format "^27.5.1" + +jest-matcher-utils@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.6.1.tgz#6c60075d84655d6300c5d5128f46531848160b53" + dependencies: + chalk "^4.0.0" + jest-diff "^29.6.1" + jest-get-type "^29.4.3" + pretty-format "^29.6.1" + +jest-message-util@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz" + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^27.5.1" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^27.5.1" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-message-util@^28.1.3: + version "28.1.3" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz" + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^28.1.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^28.1.3" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-message-util@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.6.1.tgz#d0b21d87f117e1b9e165e24f245befd2ff34ff8d" + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.1" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.6.1" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-mock@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz" + dependencies: + "@jest/types" "^27.5.1" + "@types/node" "*" + +jest-pnp-resolver@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz" + +jest-regex-util@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz" + +jest-regex-util@^28.0.0: + version "28.0.2" + resolved "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz" + +jest-resolve-dependencies@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz" + dependencies: + "@jest/types" "^27.5.1" + jest-regex-util "^27.5.1" + jest-snapshot "^27.5.1" + +jest-resolve@^27.4.2, jest-resolve@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz" + dependencies: + "@jest/types" "^27.5.1" + chalk "^4.0.0" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-pnp-resolver "^1.2.2" + jest-util "^27.5.1" + jest-validate "^27.5.1" + resolve "^1.20.0" + resolve.exports "^1.1.0" + slash "^3.0.0" + +jest-runner@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz" + dependencies: + "@jest/console" "^27.5.1" + "@jest/environment" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + emittery "^0.8.1" + graceful-fs "^4.2.9" + jest-docblock "^27.5.1" + jest-environment-jsdom "^27.5.1" + jest-environment-node "^27.5.1" + jest-haste-map "^27.5.1" + jest-leak-detector "^27.5.1" + jest-message-util "^27.5.1" + jest-resolve "^27.5.1" + jest-runtime "^27.5.1" + jest-util "^27.5.1" + jest-worker "^27.5.1" + source-map-support "^0.5.6" + throat "^6.0.1" + +jest-runtime@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz" + dependencies: + "@jest/environment" "^27.5.1" + "@jest/fake-timers" "^27.5.1" + "@jest/globals" "^27.5.1" + "@jest/source-map" "^27.5.1" + "@jest/test-result" "^27.5.1" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + chalk "^4.0.0" + cjs-module-lexer "^1.0.0" + collect-v8-coverage "^1.0.0" + execa "^5.0.0" + glob "^7.1.3" + graceful-fs "^4.2.9" + jest-haste-map "^27.5.1" + jest-message-util "^27.5.1" + jest-mock "^27.5.1" + jest-regex-util "^27.5.1" + jest-resolve "^27.5.1" + jest-snapshot "^27.5.1" + jest-util "^27.5.1" + slash "^3.0.0" + strip-bom "^4.0.0" + +jest-serializer@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz" + dependencies: + "@types/node" "*" + graceful-fs "^4.2.9" + +jest-snapshot@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz" + dependencies: + "@babel/core" "^7.7.2" + "@babel/generator" "^7.7.2" + "@babel/plugin-syntax-typescript" "^7.7.2" + "@babel/traverse" "^7.7.2" + "@babel/types" "^7.0.0" + "@jest/transform" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/babel__traverse" "^7.0.4" + "@types/prettier" "^2.1.5" + babel-preset-current-node-syntax "^1.0.0" + chalk "^4.0.0" + expect "^27.5.1" + graceful-fs "^4.2.9" + jest-diff "^27.5.1" + jest-get-type "^27.5.1" + jest-haste-map "^27.5.1" + jest-matcher-utils "^27.5.1" + jest-message-util "^27.5.1" + jest-util "^27.5.1" + natural-compare "^1.4.0" + pretty-format "^27.5.1" + semver "^7.3.2" + +jest-util@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz" + dependencies: + "@jest/types" "^27.5.1" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-util@^28.1.3: + version "28.1.3" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz" + dependencies: + "@jest/types" "^28.1.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-util@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.6.1.tgz#c9e29a87a6edbf1e39e6dee2b4689b8a146679cb" + dependencies: + "@jest/types" "^29.6.1" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +jest-validate@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz" + dependencies: + "@jest/types" "^27.5.1" + camelcase "^6.2.0" + chalk "^4.0.0" + jest-get-type "^27.5.1" + leven "^3.1.0" + pretty-format "^27.5.1" + +jest-watch-typeahead@^1.0.0: + version "1.1.0" + resolved "https://registry.npmjs.org/jest-watch-typeahead/-/jest-watch-typeahead-1.1.0.tgz" + dependencies: + ansi-escapes "^4.3.1" + chalk "^4.0.0" + jest-regex-util "^28.0.0" + jest-watcher "^28.0.0" + slash "^4.0.0" + string-length "^5.0.1" + strip-ansi "^7.0.1" + +jest-watcher@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz" + dependencies: + "@jest/test-result" "^27.5.1" + "@jest/types" "^27.5.1" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + jest-util "^27.5.1" + string-length "^4.0.1" + +jest-watcher@^28.0.0: + version "28.1.3" + resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz" + dependencies: + "@jest/test-result" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/node" "*" + ansi-escapes "^4.2.1" + chalk "^4.0.0" + emittery "^0.10.2" + jest-util "^28.1.3" + string-length "^4.0.1" + +jest-worker@^26.2.1: + version "26.6.2" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz" + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + +jest-worker@^27.0.2, jest-worker@^27.4.5, jest-worker@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz" + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest-worker@^28.0.2: + version "28.1.3" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz" + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +jest@^27.4.3: + version "27.5.1" + resolved "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz" + dependencies: + "@jest/core" "^27.5.1" + import-local "^3.0.2" + jest-cli "^27.5.1" + +jiti@^1.18.2: + version "1.19.1" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.19.1.tgz#fa99e4b76a23053e0e7cde098efe1704a14c16f1" + +js-cookie@^2.2.1: + version "2.2.1" + resolved "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz" + +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + dependencies: + argparse "^2.0.1" + +jsdom@^16.6.0: + version "16.7.0" + resolved "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz" + dependencies: + abab "^2.0.5" + acorn "^8.2.4" + acorn-globals "^6.0.0" + cssom "^0.4.4" + cssstyle "^2.3.0" + data-urls "^2.0.0" + decimal.js "^10.2.1" + domexception "^2.0.1" + escodegen "^2.0.0" + form-data "^3.0.0" + html-encoding-sniffer "^2.0.1" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.0" + parse5 "6.0.1" + saxes "^5.0.1" + symbol-tree "^3.2.4" + tough-cookie "^4.0.0" + w3c-hr-time "^1.0.2" + w3c-xmlserializer "^2.0.0" + webidl-conversions "^6.1.0" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.5.0" + ws "^7.4.6" + xml-name-validator "^3.0.0" + +jsesc@^2.5.1: + version "2.5.2" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz" + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz" + +json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" + +json-schema@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz" + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" + +json5@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" + dependencies: + minimist "^1.2.0" + +json5@^2.1.2, json5@^2.2.0, json5@^2.2.2: + version "2.2.3" + resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonpointer@^5.0.0: + version "5.0.1" + resolved "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz" + +jss-plugin-camel-case@^10.10.0: + version "10.10.0" + resolved "https://registry.yarnpkg.com/jss-plugin-camel-case/-/jss-plugin-camel-case-10.10.0.tgz#27ea159bab67eb4837fa0260204eb7925d4daa1c" + dependencies: + "@babel/runtime" "^7.3.1" + hyphenate-style-name "^1.0.3" + jss "10.10.0" + +jss-plugin-default-unit@^10.10.0: + version "10.10.0" + resolved "https://registry.yarnpkg.com/jss-plugin-default-unit/-/jss-plugin-default-unit-10.10.0.tgz#db3925cf6a07f8e1dd459549d9c8aadff9804293" + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.10.0" + +jss-plugin-global@^10.10.0: + version "10.10.0" + resolved "https://registry.yarnpkg.com/jss-plugin-global/-/jss-plugin-global-10.10.0.tgz#1c55d3c35821fab67a538a38918292fc9c567efd" + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.10.0" + +jss-plugin-nested@^10.10.0: + version "10.10.0" + resolved "https://registry.yarnpkg.com/jss-plugin-nested/-/jss-plugin-nested-10.10.0.tgz#db872ed8925688806e77f1fc87f6e62264513219" + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.10.0" + tiny-warning "^1.0.2" + +jss-plugin-props-sort@^10.10.0: + version "10.10.0" + resolved "https://registry.yarnpkg.com/jss-plugin-props-sort/-/jss-plugin-props-sort-10.10.0.tgz#67f4dd4c70830c126f4ec49b4b37ccddb680a5d7" + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.10.0" + +jss-plugin-rule-value-function@^10.10.0: + version "10.10.0" + resolved "https://registry.yarnpkg.com/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.10.0.tgz#7d99e3229e78a3712f78ba50ab342e881d26a24b" + dependencies: + "@babel/runtime" "^7.3.1" + jss "10.10.0" + tiny-warning "^1.0.2" + +jss-plugin-vendor-prefixer@^10.10.0: + version "10.10.0" + resolved "https://registry.yarnpkg.com/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.10.0.tgz#c01428ef5a89f2b128ec0af87a314d0c767931c7" + dependencies: + "@babel/runtime" "^7.3.1" + css-vendor "^2.0.8" + jss "10.10.0" + +jss@10.10.0, jss@^10.10.0: + version "10.10.0" + resolved "https://registry.yarnpkg.com/jss/-/jss-10.10.0.tgz#a75cc85b0108c7ac8c7b7d296c520a3e4fbc6ccc" + dependencies: + "@babel/runtime" "^7.3.1" + csstype "^3.0.2" + is-in-browser "^1.1.3" + tiny-warning "^1.0.2" + +"jsx-ast-utils@^2.4.1 || ^3.0.0": + version "3.3.3" + resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz" + dependencies: + array-includes "^3.1.5" + object.assign "^4.1.3" + +jsx-ast-utils@^3.3.3: + version "3.3.4" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.4.tgz#b896535fed5b867650acce5a9bd4135ffc7b3bf9" + dependencies: + array-includes "^3.1.6" + array.prototype.flat "^1.3.1" + object.assign "^4.1.4" + object.values "^1.1.6" + +jwt-decode@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" + +kleur@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz" + +klona@^2.0.4, klona@^2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.6.tgz#85bffbf819c03b2f53270412420a4555ef882e22" + +language-subtag-registry@~0.3.2: + version "0.3.22" + resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d" + +language-tags@=1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.5.tgz#d321dbc4da30ba8bf3024e040fa5c14661f9193a" + dependencies: + language-subtag-registry "~0.3.2" + +launch-editor@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/launch-editor/-/launch-editor-2.6.0.tgz#4c0c1a6ac126c572bd9ff9a30da1d2cae66defd7" + dependencies: + picocolors "^1.0.0" + shell-quote "^1.7.3" + +leven@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lilconfig@^2.0.3, lilconfig@^2.0.5, lilconfig@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz" + +loader-utils@^2.0.0, loader-utils@^2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz" + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + +loader-utils@^3.2.0: + version "3.2.1" + resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz" + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" + dependencies: + p-locate "^5.0.0" + +lodash-es@^4.17.21: + version "4.17.21" + resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz" + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz" + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" + +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz" + +lodash.throttle@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz" + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz" + +lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0: + version "4.17.21" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz" + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz" + dependencies: + tslib "^2.0.3" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz" + dependencies: + yallist "^3.0.2" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" + dependencies: + yallist "^4.0.0" + +lz-string@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" + +magic-string@^0.25.0, magic-string@^0.25.7: + version "0.25.9" + resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz" + dependencies: + sourcemap-codec "^1.4.8" + +make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz" + dependencies: + semver "^6.0.0" + +makeerror@1.0.12: + version "1.0.12" + resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz" + dependencies: + tmpl "1.0.5" + +map-obj@^4.0.0: + version "4.3.0" + resolved "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz" + +mdn-data@2.0.14: + version "2.0.14" + resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz" + +mdn-data@2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" + +memfs@^3.1.2, memfs@^3.4.3: + version "3.6.0" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.6.0.tgz#d7a2110f86f79dd950a8b6df6d57bc984aa185f6" + dependencies: + fs-monkey "^1.0.4" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz" + +micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: + version "4.0.5" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": + version "1.52.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + dependencies: + mime-db "1.52.0" + +mime@1.6.0: + version "1.6.0" + resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz" + +mini-css-extract-plugin@^2.4.5: + version "2.7.6" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz#282a3d38863fddcd2e0c220aaed5b90bc156564d" + dependencies: + schema-utils "^4.0.0" + +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz" + +minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.0, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + +mkdirp@~0.5.1: + version "0.5.6" + resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz" + dependencies: + minimist "^1.2.6" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + +ms@2.1.3, ms@^2.1.1: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + +multicast-dns@^7.2.5: + version "7.2.5" + resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz" + dependencies: + dns-packet "^5.2.2" + thunky "^1.0.2" + +mz@^2.7.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" + dependencies: + any-promise "^1.0.0" + object-assign "^4.0.1" + thenify-all "^1.0.0" + +nanoclone@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz" + +nanoid@^3.3.6: + version "3.3.6" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" + +natural-compare-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" + +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz" + +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz" + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +node-fetch@^2.6.1, node-fetch@^2.6.11: + version "2.6.11" + resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz" + dependencies: + whatwg-url "^5.0.0" + +node-forge@^1: + version "1.3.1" + resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz" + +node-int64@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz" + +node-releases@^2.0.12: + version "2.0.13" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz" + +normalize-url@^6.0.1: + version "6.1.0" + resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz" + +notistack@^2.0.3: + version "2.0.8" + resolved "https://registry.yarnpkg.com/notistack/-/notistack-2.0.8.tgz#78cdf34c64e311bf1d1d71c2123396bcdea5e95b" + dependencies: + clsx "^1.1.0" + hoist-non-react-statics "^3.3.0" + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" + dependencies: + path-key "^3.0.0" + +nprogress@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/nprogress/-/nprogress-0.2.0.tgz" + +nth-check@^1.0.2, nth-check@^2.0.1: + version "2.1.1" + resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz" + dependencies: + boolbase "^1.0.0" + +nwsapi@^2.2.0: + version "2.2.7" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.7.tgz#738e0707d3128cb750dddcfe90e4610482df0f30" + +object-assign@^4.0.1, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" + +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz" + +object-inspect@^1.12.3, object-inspect@^1.9.0: + version "1.12.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" + +object-is@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" + +object.assign@^4.0.4, object.assign@^4.1.3, object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz" + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.entries@^1.1.6: + version "1.1.6" + resolved "https://registry.npmjs.org/object.entries/-/object.entries-1.1.6.tgz" + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +object.fromentries@^2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.6.tgz" + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +object.getownpropertydescriptors@^2.1.0: + version "2.1.6" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.6.tgz#5e5c384dd209fa4efffead39e3a0512770ccc312" + dependencies: + array.prototype.reduce "^1.0.5" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.21.2" + safe-array-concat "^1.0.0" + +object.hasown@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.2.tgz" + dependencies: + define-properties "^1.1.4" + es-abstract "^1.20.4" + +object.values@^1.1.0, object.values@^1.1.6: + version "1.1.6" + resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz" + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz" + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + dependencies: + wrappy "1" + +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" + dependencies: + mimic-fn "^2.1.0" + +open@^8.0.9, open@^8.4.0: + version "8.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + +optimism@^0.10.0: + version "0.10.3" + resolved "https://registry.npmjs.org/optimism/-/optimism-0.10.3.tgz" + dependencies: + "@wry/context" "^0.4.0" + +optimism@^0.16.2: + version "0.16.2" + resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.16.2.tgz#519b0c78b3b30954baed0defe5143de7776bf081" + dependencies: + "@wry/context" "^0.7.0" + "@wry/trie" "^0.3.0" + +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + +p-limit@^2.0.0, p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" + dependencies: + yocto-queue "^0.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz" + dependencies: + p-limit "^2.0.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" + dependencies: + p-limit "^3.0.2" + +p-retry@^4.5.0: + version "4.6.2" + resolved "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz" + dependencies: + "@types/retry" "0.12.0" + retry "^0.13.1" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" + +pako@2.0.4: + version "2.0.4" + resolved "https://registry.npmjs.org/pako/-/pako-2.0.4.tgz" + +param-case@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz" + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" + dependencies: + callsites "^3.0.0" + +parse-json@^5.0.0, parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +parse5@6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz" + +parseurl@~1.3.2, parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" + +pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz" + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + +path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz" + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" + +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz" + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz" + +picocolors@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz" + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" + +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + +pify@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" + +pirates@^4.0.1: + version "4.0.6" + resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" + +pirates@^4.0.4: + version "4.0.5" + resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz" + +pkg-dir@^4.1.0, pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz" + dependencies: + find-up "^4.0.0" + +pkg-up@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz" + dependencies: + find-up "^3.0.0" + +polished@^1.9.3: + version "1.9.3" + resolved "https://registry.npmjs.org/polished/-/polished-1.9.3.tgz" + +postcss-attribute-case-insensitive@^5.0.2: + version "5.0.2" + resolved "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz" + dependencies: + postcss-selector-parser "^6.0.10" + +postcss-browser-comments@^4: + version "4.0.0" + resolved "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz" + +postcss-calc@^8.2.3: + version "8.2.4" + resolved "https://registry.npmjs.org/postcss-calc/-/postcss-calc-8.2.4.tgz" + dependencies: + postcss-selector-parser "^6.0.9" + postcss-value-parser "^4.2.0" + +postcss-clamp@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +postcss-color-functional-notation@^4.2.4: + version "4.2.4" + resolved "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +postcss-color-hex-alpha@^8.0.4: + version "8.0.4" + resolved "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +postcss-color-rebeccapurple@^7.1.1: + version "7.1.1" + resolved "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +postcss-colormin@^5.3.1: + version "5.3.1" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.3.1.tgz#86c27c26ed6ba00d96c79e08f3ffb418d1d1988f" + dependencies: + browserslist "^4.21.4" + caniuse-api "^3.0.0" + colord "^2.9.1" + postcss-value-parser "^4.2.0" + +postcss-convert-values@^5.1.3: + version "5.1.3" + resolved "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-5.1.3.tgz" + dependencies: + browserslist "^4.21.4" + postcss-value-parser "^4.2.0" + +postcss-custom-media@^8.0.2: + version "8.0.2" + resolved "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +postcss-custom-properties@^12.1.10: + version "12.1.11" + resolved "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.11.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +postcss-custom-selectors@^6.0.3: + version "6.0.3" + resolved "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz" + dependencies: + postcss-selector-parser "^6.0.4" + +postcss-dir-pseudo-class@^6.0.5: + version "6.0.5" + resolved "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz" + dependencies: + postcss-selector-parser "^6.0.10" + +postcss-discard-comments@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz" + +postcss-discard-duplicates@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz" + +postcss-discard-empty@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz" + +postcss-discard-overridden@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz" + +postcss-double-position-gradients@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz" + dependencies: + "@csstools/postcss-progressive-custom-properties" "^1.1.0" + postcss-value-parser "^4.2.0" + +postcss-env-function@^4.0.6: + version "4.0.6" + resolved "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +postcss-flexbugs-fixes@^5.0.2: + version "5.0.2" + resolved "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz" + +postcss-focus-visible@^6.0.4: + version "6.0.4" + resolved "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz" + dependencies: + postcss-selector-parser "^6.0.9" + +postcss-focus-within@^5.0.4: + version "5.0.4" + resolved "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz" + dependencies: + postcss-selector-parser "^6.0.9" + +postcss-font-variant@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz" + +postcss-gap-properties@^3.0.5: + version "3.0.5" + resolved "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz" + +postcss-image-set-function@^4.0.7: + version "4.0.7" + resolved "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +postcss-import@^15.1.0: + version "15.1.0" + resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-15.1.0.tgz#41c64ed8cc0e23735a9698b3249ffdbf704adc70" + dependencies: + postcss-value-parser "^4.0.0" + read-cache "^1.0.0" + resolve "^1.1.7" + +postcss-initial@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz" + +postcss-js@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.1.tgz#61598186f3703bab052f1c4f7d805f3991bee9d2" + dependencies: + camelcase-css "^2.0.1" + +postcss-lab-function@^4.2.1: + version "4.2.1" + resolved "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz" + dependencies: + "@csstools/postcss-progressive-custom-properties" "^1.1.0" + postcss-value-parser "^4.2.0" + +postcss-load-config@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.1.tgz#152383f481c2758274404e4962743191d73875bd" + dependencies: + lilconfig "^2.0.5" + yaml "^2.1.1" + +postcss-loader@^6.2.1: + version "6.2.1" + resolved "https://registry.npmjs.org/postcss-loader/-/postcss-loader-6.2.1.tgz" + dependencies: + cosmiconfig "^7.0.0" + klona "^2.0.5" + semver "^7.3.5" + +postcss-logical@^5.0.4: + version "5.0.4" + resolved "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz" + +postcss-media-minmax@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz" + +postcss-merge-longhand@^5.1.7: + version "5.1.7" + resolved "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-5.1.7.tgz" + dependencies: + postcss-value-parser "^4.2.0" + stylehacks "^5.1.1" + +postcss-merge-rules@^5.1.4: + version "5.1.4" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.1.4.tgz#2f26fa5cacb75b1402e213789f6766ae5e40313c" + dependencies: + browserslist "^4.21.4" + caniuse-api "^3.0.0" + cssnano-utils "^3.1.0" + postcss-selector-parser "^6.0.5" + +postcss-minify-font-values@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-5.1.0.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +postcss-minify-gradients@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-5.1.1.tgz" + dependencies: + colord "^2.9.1" + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-minify-params@^5.1.4: + version "5.1.4" + resolved "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-5.1.4.tgz" + dependencies: + browserslist "^4.21.4" + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-minify-selectors@^5.2.1: + version "5.2.1" + resolved "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-5.2.1.tgz" + dependencies: + postcss-selector-parser "^6.0.5" + +postcss-modules-extract-imports@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz" + +postcss-modules-local-by-default@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz#b08eb4f083050708998ba2c6061b50c2870ca524" + dependencies: + icss-utils "^5.0.0" + postcss-selector-parser "^6.0.2" + postcss-value-parser "^4.1.0" + +postcss-modules-scope@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz" + dependencies: + postcss-selector-parser "^6.0.4" + +postcss-modules-values@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz" + dependencies: + icss-utils "^5.0.0" + +postcss-nested@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.0.1.tgz#f83dc9846ca16d2f4fa864f16e9d9f7d0961662c" + dependencies: + postcss-selector-parser "^6.0.11" + +postcss-nesting@^10.2.0: + version "10.2.0" + resolved "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.2.0.tgz" + dependencies: + "@csstools/selector-specificity" "^2.0.0" + postcss-selector-parser "^6.0.10" + +postcss-normalize-charset@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz" + +postcss-normalize-display-values@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-5.1.0.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-positions@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-5.1.1.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-repeat-style@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-5.1.1.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-string@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-5.1.0.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-timing-functions@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-5.1.0.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize-unicode@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-5.1.1.tgz" + dependencies: + browserslist "^4.21.4" + postcss-value-parser "^4.2.0" + +postcss-normalize-url@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-5.1.0.tgz" + dependencies: + normalize-url "^6.0.1" + postcss-value-parser "^4.2.0" + +postcss-normalize-whitespace@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-5.1.1.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +postcss-normalize@^10.0.1: + version "10.0.1" + resolved "https://registry.npmjs.org/postcss-normalize/-/postcss-normalize-10.0.1.tgz" + dependencies: + "@csstools/normalize.css" "*" + postcss-browser-comments "^4" + sanitize.css "*" + +postcss-opacity-percentage@^1.1.2: + version "1.1.3" + resolved "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz" + +postcss-ordered-values@^5.1.3: + version "5.1.3" + resolved "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-5.1.3.tgz" + dependencies: + cssnano-utils "^3.1.0" + postcss-value-parser "^4.2.0" + +postcss-overflow-shorthand@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +postcss-page-break@^3.0.4: + version "3.0.4" + resolved "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz" + +postcss-place@^7.0.5: + version "7.0.5" + resolved "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +postcss-preset-env@^7.0.1: + version "7.8.3" + resolved "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.3.tgz" + dependencies: + "@csstools/postcss-cascade-layers" "^1.1.1" + "@csstools/postcss-color-function" "^1.1.1" + "@csstools/postcss-font-format-keywords" "^1.0.1" + "@csstools/postcss-hwb-function" "^1.0.2" + "@csstools/postcss-ic-unit" "^1.0.1" + "@csstools/postcss-is-pseudo-class" "^2.0.7" + "@csstools/postcss-nested-calc" "^1.0.0" + "@csstools/postcss-normalize-display-values" "^1.0.1" + "@csstools/postcss-oklab-function" "^1.1.1" + "@csstools/postcss-progressive-custom-properties" "^1.3.0" + "@csstools/postcss-stepped-value-functions" "^1.0.1" + "@csstools/postcss-text-decoration-shorthand" "^1.0.0" + "@csstools/postcss-trigonometric-functions" "^1.0.2" + "@csstools/postcss-unset-value" "^1.0.2" + autoprefixer "^10.4.13" + browserslist "^4.21.4" + css-blank-pseudo "^3.0.3" + css-has-pseudo "^3.0.4" + css-prefers-color-scheme "^6.0.3" + cssdb "^7.1.0" + postcss-attribute-case-insensitive "^5.0.2" + postcss-clamp "^4.1.0" + postcss-color-functional-notation "^4.2.4" + postcss-color-hex-alpha "^8.0.4" + postcss-color-rebeccapurple "^7.1.1" + postcss-custom-media "^8.0.2" + postcss-custom-properties "^12.1.10" + postcss-custom-selectors "^6.0.3" + postcss-dir-pseudo-class "^6.0.5" + postcss-double-position-gradients "^3.1.2" + postcss-env-function "^4.0.6" + postcss-focus-visible "^6.0.4" + postcss-focus-within "^5.0.4" + postcss-font-variant "^5.0.0" + postcss-gap-properties "^3.0.5" + postcss-image-set-function "^4.0.7" + postcss-initial "^4.0.1" + postcss-lab-function "^4.2.1" + postcss-logical "^5.0.4" + postcss-media-minmax "^5.0.0" + postcss-nesting "^10.2.0" + postcss-opacity-percentage "^1.1.2" + postcss-overflow-shorthand "^3.0.4" + postcss-page-break "^3.0.4" + postcss-place "^7.0.5" + postcss-pseudo-class-any-link "^7.1.6" + postcss-replace-overflow-wrap "^4.0.0" + postcss-selector-not "^6.0.1" + postcss-value-parser "^4.2.0" + +postcss-pseudo-class-any-link@^7.1.6: + version "7.1.6" + resolved "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz" + dependencies: + postcss-selector-parser "^6.0.10" + +postcss-reduce-initial@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.1.2.tgz#798cd77b3e033eae7105c18c9d371d989e1382d6" + dependencies: + browserslist "^4.21.4" + caniuse-api "^3.0.0" + +postcss-reduce-transforms@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-5.1.0.tgz" + dependencies: + postcss-value-parser "^4.2.0" + +postcss-replace-overflow-wrap@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz" + +postcss-selector-not@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz" + dependencies: + postcss-selector-parser "^6.0.10" + +postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector-parser@^6.0.5, postcss-selector-parser@^6.0.9: + version "6.0.13" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz#d05d8d76b1e8e173257ef9d60b706a8e5e99bf1b" + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss-svgo@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-5.1.0.tgz" + dependencies: + postcss-value-parser "^4.2.0" + svgo "^2.7.0" + +postcss-unique-selectors@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-5.1.1.tgz" + dependencies: + postcss-selector-parser "^6.0.5" + +postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" + +postcss@^7.0.35: + version "7.0.39" + resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz" + dependencies: + picocolors "^0.2.1" + source-map "^0.6.1" + +postcss@^8.3.5, postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.4: + version "8.4.25" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.25.tgz#4a133f5e379eda7f61e906c3b1aaa9b81292726f" + dependencies: + nanoid "^3.3.6" + picocolors "^1.0.0" + source-map-js "^1.0.2" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz" + +prettier@^2.6.1: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + +pretty-bytes@^5.3.0, pretty-bytes@^5.4.1: + version "5.6.0" + resolved "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz" + +pretty-error@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz" + dependencies: + lodash "^4.17.20" + renderkid "^3.0.0" + +pretty-format@^27.0.2, pretty-format@^27.5.1: + version "27.5.1" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz" + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + +pretty-format@^28.1.3: + version "28.1.3" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz" + dependencies: + "@jest/schemas" "^28.1.3" + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^18.0.0" + +pretty-format@^29.0.0, pretty-format@^29.6.1: + version "29.6.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.6.1.tgz#ec838c288850b7c4f9090b867c2d4f4edbfb0f3e" + dependencies: + "@jest/schemas" "^29.6.0" + ansi-styles "^5.0.0" + react-is "^18.0.0" + +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" + +promise@^8.1.0: + version "8.3.0" + resolved "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz" + dependencies: + asap "~2.0.6" + +prompts@^2.0.1, prompts@^2.4.2: + version "2.4.2" + resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz" + dependencies: + kleur "^3.0.3" + sisteransi "^1.0.5" + +prop-types@^15.5.10, prop-types@^15.5.7, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: + version "15.8.1" + resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +property-expr@^2.0.4: + version "2.0.5" + resolved "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz" + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz" + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + +psl@^1.1.33: + version "1.9.0" + resolved "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz" + +punycode@^2.1.0, punycode@^2.1.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" + +q@^1.1.2: + version "1.5.1" + resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz" + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz" + dependencies: + side-channel "^1.0.4" + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz" + +querystring@^0.2.0: + version "0.2.1" + resolved "https://registry.npmjs.org/querystring/-/querystring-0.2.1.tgz" + +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz" + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + +quick-lru@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz" + +raf@^3.4.1: + version "3.4.1" + resolved "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz" + dependencies: + performance-now "^2.1.0" + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" + dependencies: + safe-buffer "^5.1.0" + +range-parser@^1.2.1, range-parser@~1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" + +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz" + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +react-apexcharts@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/react-apexcharts/-/react-apexcharts-1.4.0.tgz" + dependencies: + prop-types "^15.5.7" + +react-app-polyfill@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/react-app-polyfill/-/react-app-polyfill-3.0.0.tgz" + dependencies: + core-js "^3.19.2" + object-assign "^4.1.1" + promise "^8.1.0" + raf "^3.4.1" + regenerator-runtime "^0.13.9" + whatwg-fetch "^3.6.2" + +react-copy-to-clipboard@^5.0.4: + version "5.1.0" + resolved "https://registry.yarnpkg.com/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz#09aae5ec4c62750ccb2e6421a58725eabc41255c" + dependencies: + copy-to-clipboard "^3.3.1" + prop-types "^15.8.1" + +react-day-picker@^7.0.5: + version "7.4.10" + resolved "https://registry.npmjs.org/react-day-picker/-/react-day-picker-7.4.10.tgz" + dependencies: + prop-types "^15.6.2" + +react-dev-utils@^12.0.1: + version "12.0.1" + resolved "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz" + dependencies: + "@babel/code-frame" "^7.16.0" + address "^1.1.2" + browserslist "^4.18.1" + chalk "^4.1.2" + cross-spawn "^7.0.3" + detect-port-alt "^1.1.6" + escape-string-regexp "^4.0.0" + filesize "^8.0.6" + find-up "^5.0.0" + fork-ts-checker-webpack-plugin "^6.5.0" + global-modules "^2.0.0" + globby "^11.0.4" + gzip-size "^6.0.0" + immer "^9.0.7" + is-root "^2.1.0" + loader-utils "^3.2.0" + open "^8.4.0" + pkg-up "^3.1.0" + prompts "^2.4.2" + react-error-overlay "^6.0.11" + recursive-readdir "^2.2.2" + shell-quote "^1.7.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +react-dom@^17.0.2: + version "17.0.2" + resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz" + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + scheduler "^0.20.2" + +react-dropzone@^12.0.4: + version "12.1.0" + resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-12.1.0.tgz#e097b37e9da6f9e324efc757b7434ebc6f3dc2cb" + dependencies: + attr-accept "^2.2.2" + file-selector "^0.5.0" + prop-types "^15.8.1" + +react-error-overlay@^6.0.11: + version "6.0.11" + resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz" + +react-fast-compare@^2.0.1: + version "2.0.4" + resolved "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz" + +react-fast-compare@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.2.tgz#929a97a532304ce9fee4bcae44234f1ce2c21d49" + +react-helmet-async@^1.2.3: + version "1.3.0" + resolved "https://registry.yarnpkg.com/react-helmet-async/-/react-helmet-async-1.3.0.tgz#7bd5bf8c5c69ea9f02f6083f14ce33ef545c222e" + dependencies: + "@babel/runtime" "^7.12.5" + invariant "^2.2.4" + prop-types "^15.7.2" + react-fast-compare "^3.2.0" + shallowequal "^1.1.0" + +react-icons@^4.3.1: + version "4.10.1" + resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.10.1.tgz#3f3b5eec1f63c1796f6a26174a1091ca6437a500" + +react-if@^4.1.1: + version "4.1.5" + resolved "https://registry.yarnpkg.com/react-if/-/react-if-4.1.5.tgz#f23f49277779e07240c61bdc7ab12671ff3fc20f" + +react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.2: + version "16.13.1" + resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" + +react-is@^17.0.1, react-is@^17.0.2: + version "17.0.2" + resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" + +react-is@^18.0.0, react-is@^18.2.0: + version "18.2.0" + resolved "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz" + +react-native-get-random-values@^1.4.0: + version "1.9.0" + resolved "https://registry.npmjs.org/react-native-get-random-values/-/react-native-get-random-values-1.9.0.tgz" + dependencies: + fast-base64-decode "^1.0.0" + +react-native-url-polyfill@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-1.3.0.tgz" + dependencies: + whatwg-url-without-unicode "8.0.0-3" + +react-redux@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-6.0.1.tgz#0d423e2c1cb10ada87293d47e7de7c329623ba4d" + dependencies: + "@babel/runtime" "^7.3.1" + hoist-non-react-statics "^3.3.0" + invariant "^2.2.4" + loose-envify "^1.4.0" + prop-types "^15.7.2" + react-is "^16.8.2" + +react-redux@^7.2.6: + version "7.2.9" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.9.tgz#09488fbb9416a4efe3735b7235055442b042481d" + dependencies: + "@babel/runtime" "^7.15.4" + "@types/react-redux" "^7.1.20" + hoist-non-react-statics "^3.3.2" + loose-envify "^1.4.0" + prop-types "^15.7.2" + react-is "^17.0.2" + +react-refresh@^0.11.0: + version "0.11.0" + resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz" + +react-router-dom@6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.0.0.tgz" + dependencies: + react-router "6.0.0" + +react-router@6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/react-router/-/react-router-6.0.0.tgz" + dependencies: + history "^5.0.3" + +react-scripts@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz" + dependencies: + "@babel/core" "^7.16.0" + "@pmmmwh/react-refresh-webpack-plugin" "^0.5.3" + "@svgr/webpack" "^5.5.0" + babel-jest "^27.4.2" + babel-loader "^8.2.3" + babel-plugin-named-asset-import "^0.3.8" + babel-preset-react-app "^10.0.1" + bfj "^7.0.2" + browserslist "^4.18.1" + camelcase "^6.2.1" + case-sensitive-paths-webpack-plugin "^2.4.0" + css-loader "^6.5.1" + css-minimizer-webpack-plugin "^3.2.0" + dotenv "^10.0.0" + dotenv-expand "^5.1.0" + eslint "^8.3.0" + eslint-config-react-app "^7.0.1" + eslint-webpack-plugin "^3.1.1" + file-loader "^6.2.0" + fs-extra "^10.0.0" + html-webpack-plugin "^5.5.0" + identity-obj-proxy "^3.0.0" + jest "^27.4.3" + jest-resolve "^27.4.2" + jest-watch-typeahead "^1.0.0" + mini-css-extract-plugin "^2.4.5" + postcss "^8.4.4" + postcss-flexbugs-fixes "^5.0.2" + postcss-loader "^6.2.1" + postcss-normalize "^10.0.1" + postcss-preset-env "^7.0.1" + prompts "^2.4.2" + react-app-polyfill "^3.0.0" + react-dev-utils "^12.0.1" + react-refresh "^0.11.0" + resolve "^1.20.0" + resolve-url-loader "^4.0.0" + sass-loader "^12.3.0" + semver "^7.3.5" + source-map-loader "^3.0.0" + style-loader "^3.3.1" + tailwindcss "^3.0.2" + terser-webpack-plugin "^5.2.5" + webpack "^5.64.4" + webpack-dev-server "^4.6.0" + webpack-manifest-plugin "^4.0.2" + workbox-webpack-plugin "^6.4.1" + optionalDependencies: + fsevents "^2.3.2" + +react-transition-group@^4.4.5: + version "4.4.5" + resolved "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz" + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + +react@^17.0.2: + version "17.0.2" + resolved "https://registry.npmjs.org/react/-/react-17.0.2.tgz" + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz" + dependencies: + pify "^2.3.0" + +readable-stream@^2.0.1: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" + dependencies: + picomatch "^2.2.1" + +recursive-readdir@^2.2.2: + version "2.2.3" + resolved "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz" + dependencies: + minimatch "^3.0.5" + +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz" + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + +redux-thunk@^2.3.0, redux-thunk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.2.tgz#b9d05d11994b99f7a91ea223e8b04cf0afa5ef3b" + +redux@^4.0.0, redux@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197" + dependencies: + "@babel/runtime" "^7.9.2" + +regenerate-unicode-properties@^10.1.0: + version "10.1.0" + resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz" + dependencies: + regenerate "^1.4.2" + +regenerate@^1.4.2: + version "1.4.2" + resolved "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz" + +regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.9: + version "0.13.11" + resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz" + +regenerator-transform@^0.15.1: + version "0.15.1" + resolved "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz" + dependencies: + "@babel/runtime" "^7.8.4" + +regex-parser@^2.2.11: + version "2.2.11" + resolved "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz" + +regexp.prototype.flags@^1.4.3, regexp.prototype.flags@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + functions-have-names "^1.2.3" + +regexpu-core@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" + dependencies: + "@babel/regjsgen" "^0.8.0" + regenerate "^1.4.2" + regenerate-unicode-properties "^10.1.0" + regjsparser "^0.9.1" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" + +regjsparser@^0.9.1: + version "0.9.1" + resolved "https://registry.npmjs.org/regjsparser/-/regjsparser-0.9.1.tgz" + dependencies: + jsesc "~0.5.0" + +relateurl@^0.2.7: + version "0.2.7" + resolved "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz" + +renderkid@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz" + dependencies: + css-select "^4.1.3" + dom-converter "^0.2.0" + htmlparser2 "^6.1.0" + lodash "^4.17.21" + strip-ansi "^6.0.1" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" + +reselect@^4.1.6, reselect@^4.1.8: + version "4.1.8" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.8.tgz#3f5dc671ea168dccdeb3e141236f69f02eaec524" + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" + dependencies: + resolve-from "^5.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" + +resolve-url-loader@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz" + dependencies: + adjust-sourcemap-loader "^4.0.0" + convert-source-map "^1.7.0" + loader-utils "^2.0.0" + postcss "^7.0.35" + source-map "0.6.1" + +resolve.exports@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.1.tgz#05cfd5b3edf641571fd46fa608b610dda9ead999" + +resolve@^1.1.7, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.22.2: + version "1.22.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" + dependencies: + is-core-module "^2.11.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^2.0.0-next.4: + version "2.0.0-next.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +response-iterator@^0.2.6: + version "0.2.6" + resolved "https://registry.yarnpkg.com/response-iterator/-/response-iterator-0.2.6.tgz#249005fb14d2e4eeb478a3f735a28fd8b4c9f3da" + +retry@^0.13.1: + version "0.13.1" + resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" + +rifm@^0.12.1: + version "0.12.1" + resolved "https://registry.yarnpkg.com/rifm/-/rifm-0.12.1.tgz#8fa77f45b7f1cda2a0068787ac821f0593967ac4" + +rimraf@^3.0.0, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" + dependencies: + glob "^7.1.3" + +rollup-plugin-terser@^7.0.0: + version "7.0.2" + resolved "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz" + dependencies: + "@babel/code-frame" "^7.10.4" + jest-worker "^26.2.1" + serialize-javascript "^4.0.0" + terser "^5.0.0" + +rollup@^2.43.1: + version "2.79.1" + resolved "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz" + optionalDependencies: + fsevents "~2.3.2" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + dependencies: + queue-microtask "^1.2.2" + +safe-array-concat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.0.tgz#2064223cba3c08d2ee05148eedbc563cd6d84060" + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + has-symbols "^1.0.3" + isarray "^2.0.5" + +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" + +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz" + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" + +sanitize.css@*: + version "13.0.0" + resolved "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz" + +sass-loader@^12.3.0: + version "12.6.0" + resolved "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz" + dependencies: + klona "^2.0.4" + neo-async "^2.6.2" + +sax@~1.2.4: + version "1.2.4" + resolved "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz" + +saxes@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz" + dependencies: + xmlchars "^2.2.0" + +scheduler@^0.20.2: + version "0.20.2" + resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz" + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + +schema-utils@2.7.0: + version "2.7.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz" + dependencies: + "@types/json-schema" "^7.0.4" + ajv "^6.12.2" + ajv-keywords "^3.4.1" + +schema-utils@^2.6.5: + version "2.7.1" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz" + dependencies: + "@types/json-schema" "^7.0.5" + ajv "^6.12.4" + ajv-keywords "^3.5.2" + +schema-utils@^3.0.0, schema-utils@^3.1.1, schema-utils@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +schema-utils@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz" + +selfsigned@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz" + dependencies: + node-forge "^1" + +semver@^5.5.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + +semver@^6.0.0, semver@^6.3.0: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + +semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + dependencies: + lru-cache "^6.0.0" + +send@0.18.0: + version "0.18.0" + resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz" + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + +serialize-javascript@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz" + dependencies: + randombytes "^2.1.0" + +serialize-javascript@^6.0.0, serialize-javascript@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" + dependencies: + randombytes "^2.1.0" + +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz" + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz" + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz" + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" + +shallowequal@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz" + dependencies: + shebang-regex "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + +shell-quote@^1.7.3: + version "1.8.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.2, signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" + +simplebar-react@^2.3.6: + version "2.4.3" + resolved "https://registry.yarnpkg.com/simplebar-react/-/simplebar-react-2.4.3.tgz#79c830711c23a5ae457ef73420f5752d4a1b3133" + dependencies: + prop-types "^15.6.1" + simplebar "^5.3.9" + +simplebar@^5.3.6, simplebar@^5.3.9: + version "5.3.9" + resolved "https://registry.yarnpkg.com/simplebar/-/simplebar-5.3.9.tgz#168ea0eb6d52f29f03960e40d9b69a1b28cf6318" + dependencies: + "@juggle/resize-observer" "^3.3.1" + can-use-dom "^0.1.0" + core-js "^3.0.1" + lodash.debounce "^4.0.8" + lodash.memoize "^4.1.2" + lodash.throttle "^4.1.1" + +sisteransi@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz" + +size-sensor@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/size-sensor/-/size-sensor-1.0.1.tgz" + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" + +slash@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz" + +sockjs@^0.3.24: + version "0.3.24" + resolved "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz" + dependencies: + faye-websocket "^0.11.3" + uuid "^8.3.2" + websocket-driver "^0.7.4" + +source-list-map@^2.0.0, source-list-map@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz" + +source-map-js@^1.0.1, source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz" + +source-map-loader@^3.0.0: + version "3.0.2" + resolved "https://registry.npmjs.org/source-map-loader/-/source-map-loader-3.0.2.tgz" + dependencies: + abab "^2.0.5" + iconv-lite "^0.6.3" + source-map-js "^1.0.1" + +source-map-support@^0.5.6, source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + +source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" + +source-map@^0.7.3: + version "0.7.4" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz" + +source-map@^0.8.0-beta.0: + version "0.8.0-beta.0" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz" + dependencies: + whatwg-url "^7.0.0" + +sourcemap-codec@^1.4.8: + version "1.4.8" + resolved "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz" + +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz" + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz" + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" + +stable@^0.1.8: + version "0.1.8" + resolved "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz" + +stack-utils@^2.0.3: + version "2.0.6" + resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz" + dependencies: + escape-string-regexp "^2.0.0" + +stackframe@^1.3.4: + version "1.3.4" + resolved "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz" + +state-local@^1.0.6: + version "1.0.7" + resolved "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz" + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz" + +"statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" + +stop-iteration-iterator@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz#6a60be0b4ee757d1ed5254858ec66b10c49285e4" + dependencies: + internal-slot "^1.0.4" + +string-length@^4.0.1: + version "4.0.2" + resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" + dependencies: + char-regex "^1.0.2" + strip-ansi "^6.0.0" + +string-length@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz" + dependencies: + char-regex "^2.0.0" + strip-ansi "^7.0.1" + +string-natural-compare@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/string-natural-compare/-/string-natural-compare-3.0.1.tgz" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string.prototype.matchall@^4.0.6, string.prototype.matchall@^4.0.8: + version "4.0.8" + resolved "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz" + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + get-intrinsic "^1.1.3" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + regexp.prototype.flags "^1.4.3" + side-channel "^1.0.4" + +string.prototype.trim@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +string.prototype.trimend@^1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz" + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +string.prototype.trimstart@^1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz" + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + es-abstract "^1.20.4" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + dependencies: + safe-buffer "~5.1.0" + +stringify-object@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz" + dependencies: + get-own-enumerable-property-symbols "^3.0.0" + is-obj "^1.0.1" + is-regexp "^1.0.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + dependencies: + ansi-regex "^6.0.1" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" + +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz" + +strip-comments@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz" + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" + +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz" + dependencies: + min-indent "^1.0.0" + +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" + +strnum@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz" + +style-loader@^3.3.1: + version "3.3.3" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.3.tgz#bba8daac19930169c0c9c96706749a597ae3acff" + +stylehacks@^5.1.1: + version "5.1.1" + resolved "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz" + dependencies: + browserslist "^4.21.4" + postcss-selector-parser "^6.0.4" + +stylis@4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz" + +sucrase@^3.32.0: + version "3.32.0" + resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.32.0.tgz#c4a95e0f1e18b6847127258a75cf360bc568d4a7" + dependencies: + "@jridgewell/gen-mapping" "^0.3.2" + commander "^4.0.0" + glob "7.1.6" + lines-and-columns "^1.1.6" + mz "^2.7.0" + pirates "^4.0.1" + ts-interface-checker "^0.1.9" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + dependencies: + has-flag "^4.0.0" + +supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" + dependencies: + has-flag "^4.0.0" + +supports-hyperlinks@^2.0.0: + version "2.3.0" + resolved "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz" + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + +svg-parser@^2.0.2: + version "2.0.4" + resolved "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz" + +svg.draggable.js@^2.2.2: + version "2.2.2" + resolved "https://registry.npmjs.org/svg.draggable.js/-/svg.draggable.js-2.2.2.tgz" + dependencies: + svg.js "^2.0.1" + +svg.easing.js@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/svg.easing.js/-/svg.easing.js-2.0.0.tgz" + dependencies: + svg.js ">=2.3.x" + +svg.filter.js@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/svg.filter.js/-/svg.filter.js-2.0.2.tgz" + dependencies: + svg.js "^2.2.5" + +svg.js@>=2.3.x, svg.js@^2.0.1, svg.js@^2.2.5, svg.js@^2.4.0, svg.js@^2.6.5: + version "2.7.1" + resolved "https://registry.npmjs.org/svg.js/-/svg.js-2.7.1.tgz" + +svg.pathmorphing.js@^0.1.3: + version "0.1.3" + resolved "https://registry.npmjs.org/svg.pathmorphing.js/-/svg.pathmorphing.js-0.1.3.tgz" + dependencies: + svg.js "^2.4.0" + +svg.resize.js@^1.4.3: + version "1.4.3" + resolved "https://registry.npmjs.org/svg.resize.js/-/svg.resize.js-1.4.3.tgz" + dependencies: + svg.js "^2.6.5" + svg.select.js "^2.1.2" + +svg.select.js@^2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/svg.select.js/-/svg.select.js-2.1.2.tgz" + dependencies: + svg.js "^2.2.5" + +svg.select.js@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/svg.select.js/-/svg.select.js-3.0.1.tgz" + dependencies: + svg.js "^2.6.5" + +svgo@^1.2.2: + version "1.3.2" + resolved "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz" + dependencies: + chalk "^2.4.1" + coa "^2.0.2" + css-select "^2.0.0" + css-select-base-adapter "^0.1.1" + css-tree "1.0.0-alpha.37" + csso "^4.0.2" + js-yaml "^3.13.1" + mkdirp "~0.5.1" + object.values "^1.1.0" + sax "~1.2.4" + stable "^0.1.8" + unquote "~1.1.1" + util.promisify "~1.0.0" + +svgo@^2.7.0: + version "2.8.0" + resolved "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz" + dependencies: + "@trysound/sax" "0.2.0" + commander "^7.2.0" + css-select "^4.1.3" + css-tree "^1.1.3" + csso "^4.2.0" + picocolors "^1.0.0" + stable "^0.1.8" + +symbol-observable@^1.0.2: + version "1.2.0" + resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz" + +symbol-observable@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz" + +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz" + +tailwindcss@^3.0.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.3.2.tgz#2f9e35d715fdf0bbf674d90147a0684d7054a2d3" + dependencies: + "@alloc/quick-lru" "^5.2.0" + arg "^5.0.2" + chokidar "^3.5.3" + didyoumean "^1.2.2" + dlv "^1.1.3" + fast-glob "^3.2.12" + glob-parent "^6.0.2" + is-glob "^4.0.3" + jiti "^1.18.2" + lilconfig "^2.1.0" + micromatch "^4.0.5" + normalize-path "^3.0.0" + object-hash "^3.0.0" + picocolors "^1.0.0" + postcss "^8.4.23" + postcss-import "^15.1.0" + postcss-js "^4.0.1" + postcss-load-config "^4.0.1" + postcss-nested "^6.0.1" + postcss-selector-parser "^6.0.11" + postcss-value-parser "^4.2.0" + resolve "^1.22.2" + sucrase "^3.32.0" + +tapable@^1.0.0: + version "1.1.3" + resolved "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz" + +tapable@^2.0.0, tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz" + +temp-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz" + +tempy@^0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz" + dependencies: + is-stream "^2.0.0" + temp-dir "^2.0.0" + type-fest "^0.16.0" + unique-string "^2.0.0" + +terminal-link@^2.0.0: + version "2.1.1" + resolved "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz" + dependencies: + ansi-escapes "^4.2.1" + supports-hyperlinks "^2.0.0" + +terser-webpack-plugin@^5.2.5, terser-webpack-plugin@^5.3.7: + version "5.3.9" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz#832536999c51b46d468067f9e37662a3b96adfe1" + dependencies: + "@jridgewell/trace-mapping" "^0.3.17" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.16.8" + +terser@^5.0.0, terser@^5.10.0, terser@^5.16.8: + version "5.18.2" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.18.2.tgz#ff3072a0faf21ffd38f99acc9a0ddf7b5f07b948" + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz" + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" + +thenify-all@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726" + dependencies: + thenify ">= 3.1.0 < 4" + +"thenify@>= 3.1.0 < 4": + version "3.3.1" + resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f" + dependencies: + any-promise "^1.0.0" + +throat@^6.0.1: + version "6.0.2" + resolved "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz" + +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz" + +tiny-warning@^1.0.2: + version "1.0.3" + resolved "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz" + +tmpl@1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz" + +to-fast-properties@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + dependencies: + is-number "^7.0.0" + +toggle-selection@^1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" + +toposort@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz" + +tough-cookie@^4.0.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf" + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.2.0" + url-parse "^1.5.3" + +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz" + dependencies: + punycode "^2.1.0" + +tr46@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz" + dependencies: + punycode "^2.1.1" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz" + +tryer@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz" + +ts-interface-checker@^0.1.9: + version "0.1.13" + resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" + +ts-invariant@^0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.10.3.tgz#3e048ff96e91459ffca01304dbc7f61c1f642f6c" + dependencies: + tslib "^2.1.0" + +ts-invariant@^0.4.0: + version "0.4.4" + resolved "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.4.4.tgz" + dependencies: + tslib "^1.9.3" + +tsconfig-paths@^3.14.1: + version "3.14.2" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz" + +tslib@^1.10.0, tslib@^1.11.1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.3: + version "1.14.1" + resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz" + +tslib@^2.0.0, tslib@^2.3.1, tslib@^2.5.0: + version "2.5.3" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz" + +tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3" + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz" + dependencies: + tslib "^1.8.1" + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" + dependencies: + prelude-ls "^1.2.1" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz" + dependencies: + prelude-ls "~1.1.2" + +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" + +type-fest@^0.16.0: + version "0.16.0" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" + +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz" + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz" + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz" + dependencies: + is-typedarray "^1.0.0" + +ulid@2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/ulid/-/ulid-2.3.0.tgz" + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz" + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +unfetch@^4.2.0: + version "4.2.0" + resolved "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz" + +unicode-canonical-property-names-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz" + +unicode-match-property-ecmascript@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz" + dependencies: + unicode-canonical-property-names-ecmascript "^2.0.0" + unicode-property-aliases-ecmascript "^2.0.0" + +unicode-match-property-value-ecmascript@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz" + +unicode-property-aliases-ecmascript@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz" + +unique-string@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz" + dependencies: + crypto-random-string "^2.0.0" + +universal-cookie@^4.0.4: + version "4.0.4" + resolved "https://registry.npmjs.org/universal-cookie/-/universal-cookie-4.0.4.tgz" + dependencies: + "@types/cookie" "^0.3.3" + cookie "^0.4.0" + +universalify@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz" + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" + +unquote@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz" + +upath@^1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz" + +update-browserslist-db@^1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + dependencies: + punycode "^2.1.0" + +url-parse@^1.5.3: + version "1.5.10" + resolved "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz" + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +url-parser-lite@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/url-parser-lite/-/url-parser-lite-0.1.0.tgz" + +url-search-params-polyfill@^7.0.0: + version "7.0.1" + resolved "https://registry.npmjs.org/url-search-params-polyfill/-/url-search-params-polyfill-7.0.1.tgz" + +url@0.11.0, url@^0.11.0: + version "0.11.0" + resolved "https://registry.npmjs.org/url/-/url-0.11.0.tgz" + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + +util.promisify@~1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz" + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.2" + has-symbols "^1.0.1" + object.getownpropertydescriptors "^2.1.0" + +utila@~0.4: + version "0.4.0" + resolved "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" + +uuid@3.4.0, uuid@^3.0.0, uuid@^3.2.1: + version "3.4.0" + resolved "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz" + +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz" + +v8-to-istanbul@^8.1.0: + version "8.1.1" + resolved "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz" + dependencies: + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + source-map "^0.7.3" + +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" + +w3c-hr-time@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz" + dependencies: + browser-process-hrtime "^1.0.0" + +w3c-xmlserializer@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz" + dependencies: + xml-name-validator "^3.0.0" + +walker@^1.0.7: + version "1.0.8" + resolved "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz" + dependencies: + makeerror "1.0.12" + +watchpack@^2.4.0: + version "2.4.0" + resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz" + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz" + dependencies: + minimalistic-assert "^1.0.0" + +web-vitals@^2.1.4: + version "2.1.4" + resolved "https://registry.npmjs.org/web-vitals/-/web-vitals-2.1.4.tgz" + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" + +webidl-conversions@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz" + +webidl-conversions@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz" + +webidl-conversions@^6.1.0: + version "6.1.0" + resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz" + +webpack-dev-middleware@^5.3.1: + version "5.3.3" + resolved "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz" + dependencies: + colorette "^2.0.10" + memfs "^3.4.3" + mime-types "^2.1.31" + range-parser "^1.2.1" + schema-utils "^4.0.0" + +webpack-dev-server@^4.6.0: + version "4.15.1" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.15.1.tgz#8944b29c12760b3a45bdaa70799b17cb91b03df7" + dependencies: + "@types/bonjour" "^3.5.9" + "@types/connect-history-api-fallback" "^1.3.5" + "@types/express" "^4.17.13" + "@types/serve-index" "^1.9.1" + "@types/serve-static" "^1.13.10" + "@types/sockjs" "^0.3.33" + "@types/ws" "^8.5.5" + ansi-html-community "^0.0.8" + bonjour-service "^1.0.11" + chokidar "^3.5.3" + colorette "^2.0.10" + compression "^1.7.4" + connect-history-api-fallback "^2.0.0" + default-gateway "^6.0.3" + express "^4.17.3" + graceful-fs "^4.2.6" + html-entities "^2.3.2" + http-proxy-middleware "^2.0.3" + ipaddr.js "^2.0.1" + launch-editor "^2.6.0" + open "^8.0.9" + p-retry "^4.5.0" + rimraf "^3.0.2" + schema-utils "^4.0.0" + selfsigned "^2.1.1" + serve-index "^1.9.1" + sockjs "^0.3.24" + spdy "^4.0.2" + webpack-dev-middleware "^5.3.1" + ws "^8.13.0" + +webpack-manifest-plugin@^4.0.2: + version "4.1.1" + resolved "https://registry.npmjs.org/webpack-manifest-plugin/-/webpack-manifest-plugin-4.1.1.tgz" + dependencies: + tapable "^2.0.0" + webpack-sources "^2.2.0" + +webpack-sources@^1.4.3: + version "1.4.3" + resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz" + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack-sources@^2.2.0: + version "2.3.1" + resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-2.3.1.tgz" + dependencies: + source-list-map "^2.0.1" + source-map "^0.6.1" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" + +webpack@^5.64.4: + version "5.88.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.88.1.tgz#21eba01e81bd5edff1968aea726e2fbfd557d3f8" + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^1.0.0" + "@webassemblyjs/ast" "^1.11.5" + "@webassemblyjs/wasm-edit" "^1.11.5" + "@webassemblyjs/wasm-parser" "^1.11.5" + acorn "^8.7.1" + acorn-import-assertions "^1.9.0" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.15.0" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.2.0" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.7" + watchpack "^2.4.0" + webpack-sources "^3.2.3" + +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: + version "0.7.4" + resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz" + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz" + +whatwg-encoding@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz" + dependencies: + iconv-lite "0.4.24" + +whatwg-fetch@^3.6.2: + version "3.6.2" + resolved "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz" + +whatwg-mimetype@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz" + +whatwg-url-without-unicode@8.0.0-3: + version "8.0.0-3" + resolved "https://registry.npmjs.org/whatwg-url-without-unicode/-/whatwg-url-without-unicode-8.0.0-3.tgz" + dependencies: + buffer "^5.4.3" + punycode "^2.1.1" + webidl-conversions "^5.0.0" + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz" + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +whatwg-url@^7.0.0: + version "7.1.0" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz" + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + +whatwg-url@^8.0.0, whatwg-url@^8.5.0: + version "8.7.0" + resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz" + dependencies: + lodash "^4.7.0" + tr46 "^2.1.0" + webidl-conversions "^6.1.0" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-collection@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" + dependencies: + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" + +which-typed-array@^1.1.9: + version "1.1.9" + resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz" + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.10" + +which@^1.2.9, which@^1.3.1: + version "1.3.1" + resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + dependencies: + isexe "^2.0.0" + +word-wrap@~1.2.3: + version "1.2.3" + resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz" + +workbox-background-sync@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-6.6.1.tgz#08d603a33717ce663e718c30cc336f74909aff2f" + dependencies: + idb "^7.0.1" + workbox-core "6.6.1" + +workbox-broadcast-update@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-6.6.1.tgz#0fad9454cf8e4ace0c293e5617c64c75d8a8c61e" + dependencies: + workbox-core "6.6.1" + +workbox-build@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-6.6.1.tgz#6010e9ce550910156761448f2dbea8cfcf759cb0" + dependencies: + "@apideck/better-ajv-errors" "^0.3.1" + "@babel/core" "^7.11.1" + "@babel/preset-env" "^7.11.0" + "@babel/runtime" "^7.11.2" + "@rollup/plugin-babel" "^5.2.0" + "@rollup/plugin-node-resolve" "^11.2.1" + "@rollup/plugin-replace" "^2.4.1" + "@surma/rollup-plugin-off-main-thread" "^2.2.3" + ajv "^8.6.0" + common-tags "^1.8.0" + fast-json-stable-stringify "^2.1.0" + fs-extra "^9.0.1" + glob "^7.1.6" + lodash "^4.17.20" + pretty-bytes "^5.3.0" + rollup "^2.43.1" + rollup-plugin-terser "^7.0.0" + source-map "^0.8.0-beta.0" + stringify-object "^3.3.0" + strip-comments "^2.0.1" + tempy "^0.6.0" + upath "^1.2.0" + workbox-background-sync "6.6.1" + workbox-broadcast-update "6.6.1" + workbox-cacheable-response "6.6.1" + workbox-core "6.6.1" + workbox-expiration "6.6.1" + workbox-google-analytics "6.6.1" + workbox-navigation-preload "6.6.1" + workbox-precaching "6.6.1" + workbox-range-requests "6.6.1" + workbox-recipes "6.6.1" + workbox-routing "6.6.1" + workbox-strategies "6.6.1" + workbox-streams "6.6.1" + workbox-sw "6.6.1" + workbox-window "6.6.1" + +workbox-cacheable-response@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-6.6.1.tgz#284c2b86be3f4fd191970ace8c8e99797bcf58e9" + dependencies: + workbox-core "6.6.1" + +workbox-core@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-6.6.1.tgz#7184776d4134c5ed2f086878c882728fc9084265" + +workbox-expiration@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-6.6.1.tgz#a841fa36676104426dbfb9da1ef6a630b4f93739" + dependencies: + idb "^7.0.1" + workbox-core "6.6.1" + +workbox-google-analytics@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-6.6.1.tgz#a07a6655ab33d89d1b0b0a935ffa5dea88618c5d" + dependencies: + workbox-background-sync "6.6.1" + workbox-core "6.6.1" + workbox-routing "6.6.1" + workbox-strategies "6.6.1" + +workbox-navigation-preload@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-6.6.1.tgz#61a34fe125558dd88cf09237f11bd966504ea059" + dependencies: + workbox-core "6.6.1" + +workbox-precaching@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-6.6.1.tgz#dedeeba10a2d163d990bf99f1c2066ac0d1a19e2" + dependencies: + workbox-core "6.6.1" + workbox-routing "6.6.1" + workbox-strategies "6.6.1" + +workbox-range-requests@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-6.6.1.tgz#ddaf7e73af11d362fbb2f136a9063a4c7f507a39" + dependencies: + workbox-core "6.6.1" + +workbox-recipes@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-recipes/-/workbox-recipes-6.6.1.tgz#ea70d2b2b0b0bce8de0a9d94f274d4a688e69fae" + dependencies: + workbox-cacheable-response "6.6.1" + workbox-core "6.6.1" + workbox-expiration "6.6.1" + workbox-precaching "6.6.1" + workbox-routing "6.6.1" + workbox-strategies "6.6.1" + +workbox-routing@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-6.6.1.tgz#cba9a1c7e0d1ea11e24b6f8c518840efdc94f581" + dependencies: + workbox-core "6.6.1" + +workbox-strategies@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-6.6.1.tgz#38d0f0fbdddba97bd92e0c6418d0b1a2ccd5b8bf" + dependencies: + workbox-core "6.6.1" + +workbox-streams@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-6.6.1.tgz#b2f7ba7b315c27a6e3a96a476593f99c5d227d26" + dependencies: + workbox-core "6.6.1" + workbox-routing "6.6.1" + +workbox-sw@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-6.6.1.tgz#d4c4ca3125088e8b9fd7a748ed537fa0247bd72c" + +workbox-webpack-plugin@^6.4.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-webpack-plugin/-/workbox-webpack-plugin-6.6.1.tgz#4f81cc1ad4e5d2cd7477a86ba83c84ee2d187531" + dependencies: + fast-json-stable-stringify "^2.1.0" + pretty-bytes "^5.4.1" + upath "^1.2.0" + webpack-sources "^1.4.3" + workbox-build "6.6.1" + +workbox-window@6.6.1: + version "6.6.1" + resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-6.6.1.tgz#f22a394cbac36240d0dadcbdebc35f711bb7b89e" + dependencies: + "@types/trusted-types" "^2.0.2" + workbox-core "6.6.1" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz" + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + +ws@^7.4.6: + version "7.5.9" + resolved "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz" + +ws@^8.13.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + +xml-name-validator@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz" + +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz" + +xss@^1.0.11: + version "1.0.14" + resolved "https://registry.npmjs.org/xss/-/xss-1.0.14.tgz" + dependencies: + commander "^2.20.3" + cssfilter "0.0.10" + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" + +yallist@^3.0.2: + version "3.1.1" + resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz" + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" + +yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: + version "1.10.2" + resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz" + +yaml@^2.1.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b" + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" + +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz" + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" + +yup@^0.32.11: + version "0.32.11" + resolved "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz" + dependencies: + "@babel/runtime" "^7.15.4" + "@types/lodash" "^4.14.175" + lodash "^4.17.21" + lodash-es "^4.17.21" + nanoclone "^0.2.1" + property-expr "^2.0.4" + toposort "^2.0.2" + +zen-observable-ts@0.8.19: + version "0.8.19" + resolved "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.19.tgz" + dependencies: + tslib "^1.9.3" + zen-observable "^0.8.0" + +zen-observable-ts@^0.8.21: + version "0.8.21" + resolved "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.21.tgz" + dependencies: + tslib "^1.9.3" + zen-observable "^0.8.0" + +zen-observable-ts@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz#6c6d9ea3d3a842812c6e9519209365a122ba8b58" + dependencies: + zen-observable "0.8.15" + +zen-observable@0.8.15, zen-observable@^0.8.0: + version "0.8.15" + resolved "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz" + +zen-observable@^0.7.0: + version "0.7.1" + resolved "https://registry.npmjs.org/zen-observable/-/zen-observable-0.7.1.tgz" + +zen-push@0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/zen-push/-/zen-push-0.2.1.tgz" + dependencies: + zen-observable "^0.7.0" + +zrender@5.4.3: + version "5.4.3" + resolved "https://registry.npmjs.org/zrender/-/zrender-5.4.3.tgz" + dependencies: + tslib "2.3.0" diff --git a/template_cdk.json b/template_cdk.json index 75a36cfbd..9cbd28eb2 100644 --- a/template_cdk.json +++ b/template_cdk.json @@ -21,6 +21,7 @@ "with_approval": "boolean_ADD_CODEPIPELINE_APPROVAL_STEP|DEFAULT=false", "vpc_id": "string_DEPLOY_WITHIN_AN_EXISTING_VPC|DEFAULT=None", "vpc_endpoints_sg": "string_DEPLOY_WITHIN_EXISTING_VPC_SG|DEFAULT=None", + "vpc_restricted_nacl": "boolean_CREATE_CUSTOM_NACL|DEFAULT=false", "internet_facing": "boolean_CLOUDFRONT_IF_TRUE_ELSE_ECS_BEHIND_INTERNAL_ALB|DEFAULT=true", "custom_domain": { "hosted_zone_name": "string_ROUTE_53_EXISTING_DOMAIN_NAME|DEFAULT=None, REQUIRED if internet_facing=false", diff --git a/tests/api/conftest.py b/tests/api/conftest.py index f3666d850..cde8a5a25 100644 --- a/tests/api/conftest.py +++ b/tests/api/conftest.py @@ -187,6 +187,7 @@ def factory( name: str, owner: str, group: str, + confidentiality: str = None ) -> models.Dataset: key = f'{org.organizationUri}-{env.environmentUri}-{name}-{group}' if cache.get(key): @@ -290,6 +291,7 @@ def factory( 'environmentUri': env.environmentUri, 'SamlAdminGroupName': group or random_group(), 'organizationUri': org.organizationUri, + 'confidentiality': confidentiality or dataall.api.constants.ConfidentialityClassification.Unclassified.value }, ) print('==>', response) @@ -466,7 +468,7 @@ def factory( dataall.db.api.ResourcePolicy.attach_resource_policy( session=session, group=dataset.SamlAdminGroupName, - permissions=dataall.db.permissions.SHARE_OBJECT_REQUESTER, + permissions=dataall.db.permissions.SHARE_OBJECT_APPROVER, resource_uri=share.shareUri, resource_type=dataall.db.models.ShareObject.__name__, ) @@ -477,14 +479,6 @@ def factory( resource_uri=share.shareUri, resource_type=dataall.db.models.ShareObject.__name__, ) - if dataset.SamlAdminGroupName != environment.SamlGroupName: - dataall.db.api.ResourcePolicy.attach_resource_policy( - session=session, - group=environment.SamlGroupName, - permissions=dataall.db.permissions.SHARE_OBJECT_REQUESTER, - resource_uri=share.shareUri, - resource_type=dataall.db.models.ShareObject.__name__, - ) session.commit() return share @@ -566,6 +560,49 @@ def factory(dataset: models.Dataset, name, username) -> models.DatasetTable: yield factory +@pytest.fixture(scope='module', autouse=True) +def table_with_permission(client, patch_es): + cache = {} + + def factory( + dataset: models.Dataset, + name: str, + owner: str, + group: str, + ) -> models.DatasetTable: + key = f'{dataset.datasetUri}-{name}' + if cache.get(key): + print('found in cache ', cache[key]) + return cache.get(key) + response = client.query( + """ + mutation CreateDatasetTable( + $datasetUri: String + $input: NewDatasetTableInput + ) { + createDatasetTable(datasetUri: $datasetUri, input: $input) { + tableUri + name + } + } + """, + username=owner, + groups=[group], + datasetUri=dataset.datasetUri, + input={ + 'label': f'{name}', + 'name': name, + 'description': f'test table {name}', + 'tags': random_tags(), + 'region': dataset.region + }, + ) + print('==>', response) + return response.data.createDatasetTable + + yield factory + + @pytest.fixture(scope='module', autouse=True) def org(client): cache = {} diff --git a/tests/api/test_dataset.py b/tests/api/test_dataset.py index dd1d81a86..0cb824fd1 100644 --- a/tests/api/test_dataset.py +++ b/tests/api/test_dataset.py @@ -100,7 +100,11 @@ def test_list_datasets(client, dataset1, group): assert response.data.listDatasets.nodes[0].datasetUri == dataset1.datasetUri -def test_update_dataset(dataset1, client, group, group2): +def test_update_dataset(dataset1, client, group, group2, module_mocker): + module_mocker.patch( + 'dataall.aws.handlers.kms.KMS.get_key_id', + return_value={"some_key"}, + ) response = client.query( """ mutation UpdateDataset($datasetUri:String!,$input:ModifyDatasetInput){ @@ -119,6 +123,7 @@ def test_update_dataset(dataset1, client, group, group2): 'label': 'dataset1updated', 'stewards': group2.name, 'confidentiality': 'Secret', + 'KmsAlias': '' }, groups=[group.name], ) @@ -164,6 +169,7 @@ def test_update_dataset(dataset1, client, group, group2): 'label': 'dataset1updated2', 'stewards': dataset1.SamlAdminGroupName, 'confidentiality': 'Official', + 'KmsAlias': '' }, groups=[group.name], ) @@ -212,7 +218,10 @@ def test_update_dataset_unauthorized(dataset1, client, group): """, username='anonymoususer', datasetUri=dataset1.datasetUri, - input={'label': 'dataset1updated'}, + input={ + 'label': 'dataset1updated', + 'KmsAlias': '' + }, ) assert 'UnauthorizedOperation' in response.errors[0].message @@ -443,7 +452,7 @@ def test_import_dataset(org1, env1, dataset1, client, group): 'bucketName': 'dhimportedbucket', 'glueDatabaseName': 'dhimportedGlueDB', 'adminRoleName': 'dhimportedRole', - 'KmsKeyId': '1234-YYEY', + 'KmsKeyAlias': '1234-YYEY', 'owner': dataset1.owner, 'SamlAdminGroupName': group.name, }, diff --git a/tests/api/test_dataset_profiling.py b/tests/api/test_dataset_profiling.py index ece463008..bcab1deb5 100644 --- a/tests/api/test_dataset_profiling.py +++ b/tests/api/test_dataset_profiling.py @@ -15,34 +15,32 @@ def env1(env, org1, user, group, tenant): env1 = env(org1, 'dev', user.userName, group.name, '111111111111', 'eu-west-1') yield env1 +@pytest.fixture(scope='module', autouse=True) +def org2(org, user2, group2, tenant): + org2 = org('testorg2', user2.userName, group2.name) + yield org2 -@pytest.fixture(scope='module') -def dataset1(env1, org1, dataset, group, user) -> dataall.db.models.Dataset: - yield dataset( - org=org1, env=env1, name='dataset1', owner=user.userName, group=group.name - ) +@pytest.fixture(scope='module', autouse=True) +def env2(env, org2, user2, group2, tenant): + env2 = env(org2, 'dev2', user2.userName, group2.name, '2222222222', 'eu-west-1') + yield env2 -def test_add_tables(table, dataset1, db): - for i in range(0, 10): - table(dataset=dataset1, name=f'table{i+1}', username=dataset1.owner) - - with db.scoped_session() as session: - nb = session.query(dataall.db.models.DatasetTable).count() - assert nb == 10 +@pytest.fixture(scope='module') +def dataset1(env1, org1, dataset, group, user) -> dataall.db.models.Dataset: + dataset1 = dataset( + org=org1, env=env1, name='dataset1', owner=user.userName, group=group.name, + confidentiality=dataall.api.constants.ConfidentialityClassification.Secret.value + ) + yield dataset1 -def update_runs(db, runs): - with db.scoped_session() as session: - for run in runs: - run = session.query(dataall.db.models.DatasetProfilingRun).get( - run['profilingRunUri'] - ) - run.status = 'SUCCEEDED' - session.commit() +@pytest.fixture(scope='module') +def table1(dataset1, table_with_permission, group, user): + yield table_with_permission(dataset=dataset1, name="table1", owner=user.userName, group=group.name) -def test_start_profiling(org1, env1, dataset1, client, module_mocker, db, user, group): +def test_start_profiling_run_authorized(org1, env1, dataset1, table1, client, module_mocker, db, user, group): module_mocker.patch('requests.post', return_value=True) module_mocker.patch( 'dataall.aws.handlers.service_handlers.Worker.process', return_value=True @@ -60,7 +58,7 @@ def test_start_profiling(org1, env1, dataset1, client, module_mocker, db, user, } """, username=user.userName, - input={'datasetUri': dataset1.datasetUri, 'GlueTableName': 'table1'}, + input={'datasetUri': dataset1.datasetUri, 'GlueTableName': table1.name}, groups=[group.name], ) profiling = response.data.startDatasetProfilingRun @@ -73,73 +71,64 @@ def test_start_profiling(org1, env1, dataset1, client, module_mocker, db, user, session.commit() -def test_list_runs(client, dataset1, env1, group): - runs = list_profiling_runs(client, dataset1, group) - assert len(runs) == 1 - - -def list_profiling_runs(client, dataset1, group): +def test_start_profiling_run_unauthorized(org2, env2, dataset1, table1, client, module_mocker, db, user2, group2): + module_mocker.patch('requests.post', return_value=True) + module_mocker.patch( + 'dataall.aws.handlers.service_handlers.Worker.process', return_value=True + ) + dataset1.GlueProfilingJobName = ('profile-job',) + dataset1.GlueProfilingTriggerSchedule = ('cron(* 2 * * ? *)',) + dataset1.GlueProfilingTriggerName = ('profile-job',) response = client.query( """ - query listDatasetProfilingRuns($datasetUri:String!){ - listDatasetProfilingRuns(datasetUri:$datasetUri){ - count - nodes{ + mutation startDatasetProfilingRun($input:StartDatasetProfilingRunInput){ + startDatasetProfilingRun(input:$input) + { profilingRunUri } } - } """, - datasetUri=dataset1.datasetUri, - groups=[group.name], + username=user2.userName, + input={'datasetUri': dataset1.datasetUri, 'GlueTableName': table1.name}, + groups=[group2.name], ) - return response.data.listDatasetProfilingRuns['nodes'] + assert 'UnauthorizedOperation' in response.errors[0].message -def test_get_profiling_run(client, dataset1, env1, module_mocker, db, group): - runs = list_profiling_runs(client, dataset1, group) +def test_get_table_profiling_run_authorized( + client, dataset1, table1, module_mocker, db, user, group +): module_mocker.patch( - 'dataall.aws.handlers.service_handlers.Worker.queue', - return_value=update_runs(db, runs), + 'dataall.api.Objects.DatasetProfiling.resolvers._get_profiling_results_from_s3', + return_value='{"results": "yes"}', ) + response = client.query( """ - query getDatasetProfilingRun($profilingRunUri:String!){ - getDatasetProfilingRun(profilingRunUri:$profilingRunUri){ + query getDatasetTableProfilingRun($tableUri:String!){ + getDatasetTableProfilingRun(tableUri:$tableUri){ profilingRunUri status + GlueTableName } } """, - profilingRunUri=runs[0]['profilingRunUri'], + tableUri=table1.tableUri, groups=[group.name], + username=user.userName, ) - assert ( - response.data.getDatasetProfilingRun['profilingRunUri'] - == runs[0]['profilingRunUri'] - ) - assert response.data.getDatasetProfilingRun['status'] == 'SUCCEEDED' - + assert response.data.getDatasetTableProfilingRun['profilingRunUri'] + assert response.data.getDatasetTableProfilingRun['status'] == 'RUNNING' + assert response.data.getDatasetTableProfilingRun['GlueTableName'] == 'table1' -def test_get_table_profiling_run( - client, dataset1, env1, module_mocker, table, db, group +def test_get_table_profiling_run_unauthorized( + client, dataset1, module_mocker, table1, db, user2, group2 ): module_mocker.patch( - 'dataall.api.Objects.DatasetProfiling.resolvers.get_profiling_results_from_s3', + 'dataall.api.Objects.DatasetProfiling.resolvers._get_profiling_results_from_s3', return_value='{"results": "yes"}', ) - runs = list_profiling_runs(client, dataset1, group) - module_mocker.patch( - 'dataall.aws.handlers.service_handlers.Worker.queue', - return_value=update_runs(db, runs), - ) - table = table(dataset=dataset1, name='table1', username=dataset1.owner) - with db.scoped_session() as session: - table = ( - session.query(dataall.db.models.DatasetTable) - .filter(dataall.db.models.DatasetTable.GlueTableName == 'table1') - .first() - ) + response = client.query( """ query getDatasetTableProfilingRun($tableUri:String!){ @@ -150,37 +139,22 @@ def test_get_table_profiling_run( } } """, - tableUri=table.tableUri, - groups=[group.name], - ) - assert ( - response.data.getDatasetTableProfilingRun['profilingRunUri'] - == runs[0]['profilingRunUri'] + tableUri=table1.tableUri, + groups=[group2.name], + username=user2.userName, ) - assert response.data.getDatasetTableProfilingRun['status'] == 'SUCCEEDED' - assert response.data.getDatasetTableProfilingRun['GlueTableName'] == 'table1' + assert 'UnauthorizedOperation' in response.errors[0].message -def test_list_table_profiling_runs( - client, dataset1, env1, module_mocker, table, db, group +def test_list_table_profiling_runs_authorized( + client, dataset1, module_mocker, table1, db, user, group ): module_mocker.patch( - 'dataall.api.Objects.DatasetProfiling.resolvers.get_profiling_results_from_s3', + 'dataall.api.Objects.DatasetProfiling.resolvers._get_profiling_results_from_s3', return_value='{"results": "yes"}', ) module_mocker.patch('requests.post', return_value=True) - runs = list_profiling_runs(client, dataset1, group) - table1000 = table(dataset=dataset1, name='table1000', username=dataset1.owner) - with db.scoped_session() as session: - table = ( - session.query(dataall.db.models.DatasetTable) - .filter(dataall.db.models.DatasetTable.GlueTableName == 'table1') - .first() - ) - module_mocker.patch( - 'dataall.aws.handlers.service_handlers.Worker.queue', - return_value=update_runs(db, runs), - ) + response = client.query( """ query listDatasetTableProfilingRuns($tableUri:String!){ @@ -195,25 +169,29 @@ def test_list_table_profiling_runs( } } """, - tableUri=table.tableUri, + tableUri=table1.tableUri, groups=[group.name], + username=user.userName, ) + assert response.data.listDatasetTableProfilingRuns['count'] == 1 + assert response.data.listDatasetTableProfilingRuns['nodes'][0]['profilingRunUri'] assert ( - response.data.listDatasetTableProfilingRuns['nodes'][0]['profilingRunUri'] - == runs[0]['profilingRunUri'] - ) - assert ( - response.data.listDatasetTableProfilingRuns['nodes'][0]['status'] == 'SUCCEEDED' + response.data.listDatasetTableProfilingRuns['nodes'][0]['status'] == 'RUNNING' ) assert ( response.data.listDatasetTableProfilingRuns['nodes'][0]['GlueTableName'] == 'table1' ) +def test_list_table_profiling_runs_unauthorized( + client, dataset1, module_mocker, table1, db, user2, group2 +): module_mocker.patch( - 'dataall.aws.handlers.service_handlers.Worker.queue', - return_value=update_runs(db, runs), + 'dataall.api.Objects.DatasetProfiling.resolvers._get_profiling_results_from_s3', + return_value='{"results": "yes"}', ) + module_mocker.patch('requests.post', return_value=True) + response = client.query( """ query listDatasetTableProfilingRuns($tableUri:String!){ @@ -228,39 +206,8 @@ def test_list_table_profiling_runs( } } """, - tableUri=table1000.tableUri, - groups=[group.name], - ) - assert response.data.listDatasetTableProfilingRuns['count'] == 0 - - response = client.query( - """ - query getDatasetTableProfilingRun($tableUri:String!){ - getDatasetTableProfilingRun(tableUri:$tableUri){ - profilingRunUri - status - GlueTableName - } - } - """, - tableUri=table.tableUri, - groups=[group.name], - ) - assert ( - response.data.getDatasetTableProfilingRun['profilingRunUri'] - == runs[0]['profilingRunUri'] - ) - - response = client.query( - """ - query getDatasetTableProfilingRun($tableUri:String!){ - getDatasetTableProfilingRun(tableUri:$tableUri){ - profilingRunUri - status - GlueTableName - } - } - """, - tableUri=table1000.tableUri, + tableUri=table1.tableUri, + groups=[group2.name], + username=user2.userName, ) - assert not response.data.getDatasetTableProfilingRun + assert 'UnauthorizedOperation' in response.errors[0].message diff --git a/tests/api/test_glossary.py b/tests/api/test_glossary.py index 8821aadd0..d295fe068 100644 --- a/tests/api/test_glossary.py +++ b/tests/api/test_glossary.py @@ -316,7 +316,10 @@ def test_dataset_term_link_approval(db, client, t1, _dataset, user, group): username='alice', groups=[group.name], datasetUri=_dataset.datasetUri, - input={'terms': [t1.nodeUri]}, + input={ + 'terms': [t1.nodeUri], + 'KmsAlias': '' + }, ) with db.scoped_session() as session: link: models.TermLink = ( diff --git a/tests/api/test_share.py b/tests/api/test_share.py index 58309aa01..71af1c5c5 100644 --- a/tests/api/test_share.py +++ b/tests/api/test_share.py @@ -146,6 +146,7 @@ def share1_draft( owner=user2.userName, status=dataall.api.constants.ShareObjectStatus.Draft.value ) + yield share1 # Cleanup share @@ -395,6 +396,8 @@ def create_share_object(client, userName, group, groupUri, environmentUri, datas created status userRoleForShareObject + requestPurpose + rejectPurpose } } """ @@ -411,6 +414,7 @@ def create_share_object(client, userName, group, groupUri, environmentUri, datas 'groupUri': groupUri, 'principalId': groupUri, 'principalType': dataall.api.constants.PrincipalType.Group.value, + 'requestPurpose': 'testShare' }, ) @@ -427,6 +431,8 @@ def get_share_object(client, user, group, shareUri, filter): created owner status + requestPurpose + rejectPurpose userRoleForShareObject principal { principalId @@ -479,6 +485,43 @@ def get_share_object(client, user, group, shareUri, filter): return response +def update_share_request_purpose(client, user, group, shareUri, requestPurpose): + q = """ + mutation updateShareRequestReason($shareUri: String!,$requestPurpose: String!) { + updateShareRequestReason(shareUri: $shareUri, requestPurpose: $requestPurpose) + } + """ + + response = client.query( + q, + username=user.userName, + groups=[group.name], + shareUri=shareUri, + requestPurpose=requestPurpose, + ) + # Print response + print('Update share request purpose response: ', response) + return response + + +def update_share_reject_purpose(client, user, group, shareUri, rejectPurpose): + q = """ + mutation updateShareRejectReason($shareUri: String!, $rejectPurpose: String!) { + updateShareRejectReason(shareUri: $shareUri, rejectPurpose: $rejectPurpose) + } + """ + + response = client.query( + q, + username=user.userName, + groups=[group.name], + shareUri=shareUri, + rejectPurpose=rejectPurpose, + ) + # Print response + print('Update share reject purpose response: ', response) + return response + def list_dataset_share_objects(client, user, group, datasetUri): q = """ query ListDatasetShareObjects( @@ -683,10 +726,11 @@ def approve_share_object(client, user, group, shareUri): def reject_share_object(client, user, group, shareUri): q = """ - mutation RejectShareObject($shareUri: String!) { - rejectShareObject(shareUri: $shareUri) { + mutation RejectShareObject($shareUri: String!, $rejectPurpose: String!) { + rejectShareObject(shareUri: $shareUri, rejectPurpose: $rejectPurpose) { shareUri status + rejectPurpose } } """ @@ -696,6 +740,7 @@ def reject_share_object(client, user, group, shareUri): username=user.userName, groups=[group.name], shareUri=shareUri, + rejectPurpose="testRejectShare" ) print('Response from rejectShareObject: ', response) @@ -811,7 +856,7 @@ def test_create_share_object_authorized(client, user2, group2, env2group, env2, assert create_share_object_response.data.createShareObject.shareUri assert create_share_object_response.data.createShareObject.status == dataall.api.constants.ShareObjectStatus.Draft.value assert create_share_object_response.data.createShareObject.userRoleForShareObject == 'Requesters' - + assert create_share_object_response.data.createShareObject.requestPurpose == 'testShare' def test_create_share_object_with_item_authorized(client, user2, group2, env2group, env2, dataset1, table1): # Given @@ -831,6 +876,7 @@ def test_create_share_object_with_item_authorized(client, user2, group2, env2gro assert create_share_object_response.data.createShareObject.shareUri assert create_share_object_response.data.createShareObject.status == dataall.api.constants.ShareObjectStatus.Draft.value assert create_share_object_response.data.createShareObject.userRoleForShareObject == 'Requesters' + assert create_share_object_response.data.createShareObject.requestPurpose == 'testShare' # And item has been added to the share request get_share_object_response = get_share_object( @@ -866,6 +912,47 @@ def test_get_share_object(client, share1_draft, user, group): assert get_share_object_response.data.getShareObject.get('principal').region +def test_update_share_request_purpose(client, share1_draft, user2, group2): + # Given + # Existing share object in status Draft (->fixture share1_draft) + # When a user from the requesters group updates + update_share_request_purpose_response = update_share_request_purpose( + client=client, + user=user2, + group=group2, + shareUri=share1_draft.shareUri, + requestPurpose="NewRequestPurpose" + ) + + # Then the requestPurpose of the Share is Updated + get_share_object_response = get_share_object( + client=client, + user=user2, + group=group2, + shareUri=share1_draft.shareUri, + filter={} + ) + + assert get_share_object_response.data.getShareObject.requestPurpose == "NewRequestPurpose" + assert get_share_object_response.data.getShareObject.userRoleForShareObject == 'Requesters' + + +def test_update_share_request_purpose_unauthorized(client, share1_draft, user, group): + # Given + # Existing share object in status Draft (->fixture share1_draft) + # When a user from the approvers group attempts to update the request purpose + update_share_request_purpose_response = update_share_request_purpose( + client=client, + user=user, + group=group, + shareUri=share1_draft.shareUri, + requestPurpose="NewRequestPurpose" + ) + + # Then we get an error of the type + assert 'UnauthorizedOperation' in update_share_request_purpose_response.errors[0].message + + def test_list_dataset_share_objects_approvers( client, user, group, share1_draft, dataset1 ): @@ -1085,6 +1172,47 @@ def test_submit_share_request( assert status == dataall.api.constants.ShareItemStatus.PendingApproval.name +def test_update_share_reject_purpose(client, share2_submitted, user, group): + # Given + # Existing share object in status Submitted (-> fixture share2_submitted) + # When a user from the approvers group updates the reject purpose + update_share_reject_purpose_response = update_share_reject_purpose( + client=client, + user=user, + group=group, + shareUri=share2_submitted.shareUri, + rejectPurpose="NewRejectPurpose" + ) + + # Then the rejectPurpose of the Share is Updated + get_share_object_response = get_share_object( + client=client, + user=user, + group=group, + shareUri=share2_submitted.shareUri, + filter={} + ) + + assert get_share_object_response.data.getShareObject.rejectPurpose == "NewRejectPurpose" + assert get_share_object_response.data.getShareObject.userRoleForShareObject == 'Approvers' + + +def test_update_share_reject_purpose_unauthorized(client, share2_submitted, user2, group2): + # Given + # Existing share object in status Submitted (-> fixture share2_submitted) + # When a user from the requester group attempts to update the reject purpose + update_share_reject_purpose_response = update_share_reject_purpose( + client=client, + user=user2, + group=group2, + shareUri=share2_submitted.shareUri, + rejectPurpose="NewRejectPurpose" + ) + + # Then we get an error of the type + assert 'UnauthorizedOperation' in update_share_reject_purpose_response.errors[0].message + + def test_approve_share_request( db, client, user, group, share2_submitted, share2_item_pa ): @@ -1182,7 +1310,7 @@ def test_reject_share_request( # Then share object status is changed to Rejected assert reject_share_object_response.data.rejectShareObject.status == \ dataall.api.constants.ShareObjectStatus.Rejected.name - + assert reject_share_object_response.data.rejectShareObject.rejectPurpose == "testRejectShare" # and share item status is changed to Share_Rejected get_share_object_response = get_share_object( client=client, diff --git a/tests/cdkproxy/test_pipeline_stack.py b/tests/cdkproxy/test_pipeline_stack.py index fb1976839..ed1d7d0c0 100644 --- a/tests/cdkproxy/test_pipeline_stack.py +++ b/tests/cdkproxy/test_pipeline_stack.py @@ -1,5 +1,5 @@ import json - +import os import pytest from aws_cdk import App @@ -28,6 +28,14 @@ def patch_methods(mocker, db, pipeline2, env, pip_envs, org): 'dataall.cdkproxy.stacks.pipeline.PipelineStack.get_pipeline_environments', return_value=pip_envs, ) + mocker.patch( + 'dataall.cdkproxy.stacks.pipeline.PipelineStack._set_env_vars', + return_value=(os.environ, True) + ) + mocker.patch( + 'dataall.cdkproxy.stacks.pipeline.PipelineStack._check_repository', + return_value=False + ) mocker.patch( 'dataall.utils.runtime_stacks_tagging.TagsUtil.get_engine', return_value=db ) diff --git a/tests/db/test_connect.py b/tests/db/test_connect.py index 74be044fd..32333d4cc 100644 --- a/tests/db/test_connect.py +++ b/tests/db/test_connect.py @@ -6,9 +6,8 @@ def test(db: dataall.db.Engine): if os.getenv('local') or os.getenv('pytest'): config: dataall.db.DbConfig = db.dbconfig print(config) - assert config.params.get('host') == 'localhost' - assert config.params.get('port') == '5432' - assert config.params.get('schema') == 'pytest' + assert config.host == 'localhost' + assert config.schema == 'pytest' with db.scoped_session() as session: models = [] models = models + dataall.db.Base.__subclasses__() diff --git a/tests/db/test_dbconfig.py b/tests/db/test_dbconfig.py new file mode 100644 index 000000000..67476d514 --- /dev/null +++ b/tests/db/test_dbconfig.py @@ -0,0 +1,58 @@ +import pytest + +from dataall.db import DbConfig + + +def test_incorrect_database(): + with pytest.raises(ValueError): + DbConfig( + user='dataall', + pwd='123456789', + host="dataall.eu-west-1.rds.amazonaws.com", + db='dataall\'; DROP TABLE users;', + schema='dev' + ) + + +def test_incorrect_user(): + with pytest.raises(ValueError): + DbConfig( + user='dataall2;^&*end', + pwd='qwsufn3i20d-_s3qaSW3d2', + host="dataall.eu-west-1.rds.amazonaws.com", + db='dataall', + schema='dev' + ) + + +def test_incorrect_pwd(): + with pytest.raises(ValueError): + DbConfig( + user='dataall', + pwd='qazxsVFRTGBdfrew-332_c2@dataall.eu-west-1.rds.amazonaws.com/dataall\'; drop table dataset; # ', + host="dataall.eu-west-1.rds.amazonaws.com", + db='dataall', + schema='dev' + ) + + +def test_incorrect_host(): + with pytest.raises(ValueError): + DbConfig( + user='dataall', + pwd='q68rjdmwiosoxahGDYJWIdi-9eu93_9dJJ_', + host="dataall.eu-west-1$%#&@*#)$#.rds.amazonaws.com", + db='dataall', + schema='dev' + ) + + +def test_correct_config(): + # no exception is raised + DbConfig( + user='dataall', + pwd='q68rjdm_aX', + host="dataall.eu-west-1.rds.amazonaws.com", + db='dataall', + schema='dev' + )