Skip to content

Commit

Permalink
Merge branch 'main' into v2m1m0
Browse files Browse the repository at this point in the history
# Conflicts:
#	deploy/stacks/backend_stack.py
#	deploy/stacks/backend_stage.py
#	deploy/stacks/lambda_api.py
#	deploy/stacks/pipeline.py
#	template_cdk.json
  • Loading branch information
dlpzx committed Oct 27, 2023
2 parents 6d727e9 + 85fc349 commit 8ad760b
Show file tree
Hide file tree
Showing 22 changed files with 418 additions and 34 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,6 @@ If you discover a potential security issue in this project we ask that you notif

## Licensing

See the [LICENSE](https://github.com/awslabs/aws-dataall/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
See the [LICENSE](./LICENCE.txt) file for our project's licensing. We will ask you to confirm the licensing of your contribution.

We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes.
53 changes: 51 additions & 2 deletions backend/api_handler.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
import logging
import os
import datetime
from argparse import Namespace
from time import perf_counter

Expand All @@ -12,6 +13,7 @@
from dataall.base.api import bootstrap as bootstrap_schema, get_executable_schema
from dataall.core.tasks.service_handlers import Worker
from dataall.base.aws.sqs import SqsQueue
from dataall.base.aws.parameter_store import ParameterStoreManager
from dataall.base.context import set_context, dispose_context, RequestContext
from dataall.core.permissions.db import save_permissions_with_tenant
from dataall.core.permissions.db.tenant_policy_repositories import TenantPolicy
Expand All @@ -30,6 +32,7 @@
load_modules(modes={ImportMode.API})
SCHEMA = bootstrap_schema()
TYPE_DEFS = gql(SCHEMA.gql(with_directives=False))
REAUTH_TTL = int(os.environ.get('REAUTH_TTL', '5'))
ENVNAME = os.getenv('envname', 'local')
ENGINE = get_engine(envname=ENVNAME)
Worker.queue = SqsQueue.send
Expand Down Expand Up @@ -114,9 +117,15 @@ def handler(event, context):
}

if 'authorizer' in event['requestContext']:
username = event['requestContext']['authorizer']['claims']['email']
if 'claims' not in event['requestContext']['authorizer']:
claims = event['requestContext']['authorizer']
else:
claims = event['requestContext']['authorizer']['claims']
username = claims['email']
log.debug('username is %s', username)
try:
groups = get_groups(event['requestContext']['authorizer']['claims'])
groups = get_groups(claims)
log.debug('groups are %s', ",".join(groups))
with ENGINE.scoped_session() as session:
for group in groups:
policy = TenantPolicy.find_tenant_policy(
Expand Down Expand Up @@ -146,10 +155,50 @@ def handler(event, context):
'schema': SCHEMA
}

# Determine if there are any Operations that Require ReAuth From SSM Parameter
try:
reauth_apis = ParameterStoreManager.get_parameter_value(region=os.getenv('AWS_REGION', 'eu-west-1'), parameter_path=f"/dataall/{ENVNAME}/reauth/apis").split(',')
except Exception as e:
log.info("No ReAuth APIs Found in SSM")
reauth_apis = None
else:
raise Exception(f'Could not initialize user context from event {event}')

query = json.loads(event.get('body'))

# If The Operation is a ReAuth Operation - Ensure A Non-Expired Session or Return Error
if reauth_apis and query.get('operationName', None) in reauth_apis:
now = datetime.datetime.now(datetime.timezone.utc)
try:
auth_time_datetime = datetime.datetime.fromtimestamp(int(claims["auth_time"]), tz=datetime.timezone.utc)
if auth_time_datetime + datetime.timedelta(minutes=REAUTH_TTL) < now:
raise Exception("ReAuth")
except Exception as e:
log.info(f'ReAuth Required for User {username} on Operation {query.get("operationName", "")}, Error: {e}')
response = {
"data": {query.get('operationName', 'operation') : None},
"errors": [
{
"message": f"ReAuth Required To Perform This Action {query.get('operationName', '')}",
"locations": None,
"path": [query.get('operationName', '')],
"extensions": {
"code": "REAUTH"
}
}
]
}
return {
'statusCode': 401,
'headers': {
'content-type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Headers': '*',
'Access-Control-Allow-Methods': '*',
},
'body': json.dumps(response)
}

success, response = graphql_sync(
schema=executable_schema, data=query, context_value=app_context
)
Expand Down
2 changes: 1 addition & 1 deletion backend/dataall/base/cdkproxy/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ requests==2.31.0
tabulate==0.8.9
uvicorn==0.15.0
jinja2==3.1.2
werkzeug==2.3.3
werkzeug==3.0.1
constructs>=10.0.0,<11.0.0
git-remote-codecommit==1.16
aws-ddk==0.5.1
Expand Down
7 changes: 5 additions & 2 deletions deploy/configs/frontend_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ def create_react_env_file(
resource_prefix,
internet_facing='True',
custom_domain='False',
cw_rum_enabled='False'
cw_rum_enabled='False',
reauth_ttl='5'
):
ssm = boto3.client('ssm', region_name=region)
user_pool_id = ssm.get_parameter(Name=f'/dataall/{envname}/cognito/userpool')[
Expand Down Expand Up @@ -69,6 +70,7 @@ def create_react_env_file(
REACT_APP_COGNITO_REDIRECT_SIGNOUT=https://{signin_singout_link}
REACT_APP_USERGUIDE_LINK=https://{user_guide_link}
REACT_APP_ENABLE_PIVOT_ROLE_AUTO_CREATE={pivot_role_auto_create}
REACT_APP_REAUTH_TTL={reauth_ttl}
"""
print('.env content: \n', file_content)
f.write(file_content)
Expand Down Expand Up @@ -125,13 +127,14 @@ def create_react_env_file(
custom_domain = os.environ.get('custom_domain', 'False')
region = os.environ.get('deployment_region', 'eu-west-1')
enable_cw_rum = os.environ.get('enable_cw_rum', 'False')
reauth_ttl = os.environ.get('reauth_ttl', '5')
print(
f'Creating React .env file with params: '
f'(region={region},envname={envname},resource_prefix={resource_prefix}'
f'internet_facing={internet_facing},custom_domain={custom_domain},'
f'cw_rum_enabled={enable_cw_rum})'
)
create_react_env_file(
region, envname, resource_prefix, internet_facing, custom_domain, enable_cw_rum
region, envname, resource_prefix, internet_facing, custom_domain, enable_cw_rum, reauth_ttl
)
print(f'React .env created successfully')
4 changes: 4 additions & 0 deletions deploy/stacks/backend_stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def __init__(
enable_opensearch_serverless=False,
codeartifact_domain_name=None,
codeartifact_pip_repo_name=None,
reauth_config=None,
cognito_user_session_timeout_inmins=43200,
**kwargs,
):
Expand Down Expand Up @@ -91,6 +92,7 @@ def __init__(
shared_dashboard_sessions=shared_dashboard_sessions,
enable_pivot_role_auto_create=enable_pivot_role_auto_create,
pivot_role_name=self.pivot_role_name,
reauth_apis=reauth_config.get("reauth_apis", None) if reauth_config else None,
**kwargs,
)
if enable_cw_canaries:
Expand Down Expand Up @@ -118,6 +120,7 @@ def __init__(
internet_facing=internet_facing,
tooling_account_id=tooling_account_id,
enable_cw_rum=enable_cw_rum,
vpc=vpc,
cognito_user_session_timeout_inmins=cognito_user_session_timeout_inmins,
**kwargs,
)
Expand Down Expand Up @@ -158,6 +161,7 @@ def __init__(
prod_sizing=prod_sizing,
user_pool=cognito_stack.user_pool,
pivot_role_name=self.pivot_role_name,
reauth_ttl=reauth_config.get("ttl", 5) if reauth_config else 5,
email_notification_sender_email_id=email_sender,
email_custom_domain = ses_stack.ses_identity.email_identity_name if ses_stack != None else None,
ses_configuration_set = ses_stack.configuration_set.configuration_set_name if ses_stack != None else None,
Expand Down
2 changes: 2 additions & 0 deletions deploy/stacks/backend_stage.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def __init__(
enable_pivot_role_auto_create=False,
codeartifact_domain_name=None,
codeartifact_pip_repo_name=None,
reauth_config=None,
cognito_user_session_timeout_inmins=43200,
**kwargs,
):
Expand Down Expand Up @@ -62,6 +63,7 @@ def __init__(
enable_pivot_role_auto_create=enable_pivot_role_auto_create,
codeartifact_domain_name=codeartifact_domain_name,
codeartifact_pip_repo_name=codeartifact_pip_repo_name,
reauth_config=reauth_config,
cognito_user_session_timeout_inmins=cognito_user_session_timeout_inmins,
**kwargs,
)
Expand Down
4 changes: 3 additions & 1 deletion deploy/stacks/lambda_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ def __init__(
prod_sizing=False,
user_pool=None,
pivot_role_name=None,
reauth_ttl=5,
email_notification_sender_email_id=None,
email_custom_domain=None,
ses_configuration_set=None,
Expand Down Expand Up @@ -97,7 +98,7 @@ def __init__(
security_groups=[api_handler_sg],
memory_size=3008 if prod_sizing else 1024,
timeout=Duration.minutes(15),
environment={'envname': envname, 'LOG_LEVEL': 'INFO'},
environment={'envname': envname, 'LOG_LEVEL': 'INFO', 'REAUTH_TTL': str(reauth_ttl)},
dead_letter_queue_enabled=True,
dead_letter_queue=self.api_handler_dlq,
on_failure=lambda_destination.SqsDestination(self.api_handler_dlq),
Expand Down Expand Up @@ -544,6 +545,7 @@ def set_up_graphql_api_gateway(
request_validator=request_validator,
request_models={'application/json': graphql_validation_model},
)

search_integration = apigw.LambdaIntegration(elasticsearch_proxy_handler)
search = gw.root.add_resource(path_part='search')
search_validation_model = apigw.Model(
Expand Down
10 changes: 9 additions & 1 deletion deploy/stacks/param_store_stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def __init__(
shared_dashboard_sessions='anonymous',
enable_pivot_role_auto_create=False,
pivot_role_name='dataallPivotRole',
reauth_apis=None,
**kwargs,
):
super().__init__(scope, id, **kwargs)
Expand Down Expand Up @@ -80,6 +81,13 @@ def __init__(
parameter_name=f'/dataall/{envname}/quicksightmonitoring/DashboardId',
string_value='updateme',
)
if reauth_apis:
aws_ssm.StringParameter(
self,
f'ReAuthAPIs{envname}',
parameter_name=f'/dataall/{envname}/reauth/apis',
string_value=','.join(reauth_apis),
)

aws_ssm.StringParameter(
self,
Expand Down Expand Up @@ -148,4 +156,4 @@ def _get_external_id_value(envname, account_id, region):

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))
return ''.join(random.choice(allowed_chars) for i in range(32))
2 changes: 2 additions & 0 deletions deploy/stacks/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,7 @@ def set_backend_stage(self, target_env, repository_name):
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,
reauth_config = target_env.get('reauth_config', None),
cognito_user_session_timeout_inmins=target_env.get('cognito_user_session_timeout_inmins', 43200)
)
)
Expand Down Expand Up @@ -732,6 +733,7 @@ def set_cloudfront_stage(self, target_env):
f'export deployment_region={target_env.get("region", self.region)}',
f'export enable_cw_rum={target_env.get("enable_cw_rum", False)}',
f'export resource_prefix={self.resource_prefix}',
f'export reauth_ttl={str(target_env.get("reauth_config", {}).get("ttl", 5))}',
'mkdir ~/.aws/ && touch ~/.aws/config',
'echo "[profile buildprofile]" > ~/.aws/config',
f'echo "role_arn = arn:aws:iam::{target_env["account"]}:role/{self.resource_prefix}-{target_env["envname"]}-S3DeploymentRole" >> ~/.aws/config',
Expand Down
4 changes: 4 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@
"authentication",
"./src/authentication"
],
[
"reauthentication",
"./src/reauthentication"
],
[
"design",
"./src/design"
Expand Down
7 changes: 2 additions & 5 deletions frontend/src/App.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { ThemeProvider } from '@mui/material';
import { SnackbarProvider } from 'notistack';
import { useRoutes } from 'react-router-dom';
import { useAuth } from './authentication';
import {
Expand All @@ -26,10 +25,8 @@ export const App = () => {

return (
<ThemeProvider theme={theme}>
<SnackbarProvider dense maxSnack={3} hideIconVariant>
<GlobalStyles />
{auth.isInitialized ? content : <SplashScreen />}
</SnackbarProvider>
<GlobalStyles />
{auth.isInitialized ? content : <SplashScreen />}
</ThemeProvider>
);
};
37 changes: 32 additions & 5 deletions frontend/src/authentication/contexts/CognitoAuthContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ Auth.configure({
const initialState = {
isAuthenticated: false,
isInitialized: false,
user: null
user: null,
reAuthStatus: false,
requestInfo: null
};

const handlers = {
Expand All @@ -37,7 +39,8 @@ const handlers = {
...state,
isAuthenticated,
isInitialized: true,
user
user,
reAuthStatus: false
};
},
LOGIN: (state, action) => {
Expand All @@ -53,7 +56,16 @@ const handlers = {
...state,
isAuthenticated: false,
user: null
})
}),
REAUTH: (state, action) => {
const { reAuthStatus, requestInfo } = action.payload;

return {
...state,
reAuthStatus,
requestInfo
};
}
};

const reducer = (state, action) =>
Expand All @@ -63,7 +75,8 @@ export const CognitoAuthContext = createContext({
...initialState,
platform: 'Amplify',
login: () => Promise.resolve(),
logout: () => Promise.resolve()
logout: () => Promise.resolve(),
reauth: () => Promise.resolve()
});

export const CognitoAuthProvider = (props) => {
Expand Down Expand Up @@ -118,6 +131,19 @@ export const CognitoAuthProvider = (props) => {
});
};

const reauth = async () => {
await Auth.signOut();
dispatch({
type: 'REAUTH',
payload: {
reAuthStatus: false,
requestInfo: null
}
}).catch((e) => {
console.error('Failed to reauth user', e);
});
};

const logout = async () => {
await Auth.signOut();
dispatch({
Expand All @@ -132,7 +158,8 @@ export const CognitoAuthProvider = (props) => {
dispatch,
platform: 'Amplify',
login,
logout
logout,
reauth
}}
>
{children}
Expand Down
12 changes: 9 additions & 3 deletions frontend/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { SettingsProvider } from './design';
import { store } from './globalErrors';
import { reportWebVitals } from './reportWebVitals';
import * as serviceWorker from './serviceWorker';
import { SnackbarProvider } from 'notistack';
import { RequestContextProvider } from './reauthentication';

ReactDOM.render(
<StrictMode>
Expand All @@ -22,9 +24,13 @@ ReactDOM.render(
<LocalizationProvider dateAdapter={AdapterDateFns}>
<SettingsProvider>
<BrowserRouter>
<AuthProvider>
<App />
</AuthProvider>
<SnackbarProvider dense maxSnack={3} hideIconVariant>
<RequestContextProvider>
<AuthProvider>
<App />
</AuthProvider>
</RequestContextProvider>
</SnackbarProvider>
</BrowserRouter>
</SettingsProvider>
</LocalizationProvider>
Expand Down
Loading

0 comments on commit 8ad760b

Please sign in to comment.