def mutate(_, info, auth_token, provider, push_token): authorized = False session_jwt = '' success = False if provider == 'google': try: user_info = id_token.verify_oauth2_token( auth_token, requests.Request()) if user_info['iss'] not in [ 'accounts.google.com', 'https://accounts.google.com' ]: rollbar.report_message('Error: Invalid oauth2 issuer', 'error', info.context, user_info['iss']) raise GraphQLError('INVALID_AUTH_TOKEN') if user_info['aud'] not in [ FI_GOOGLE_OAUTH2_KEY_ANDROID, FI_GOOGLE_OAUTH2_KEY_IOS ]: rollbar.report_message('Error: Invalid oauth2 audience', 'error', info.context, user_info['aud']) raise GraphQLError('INVALID_AUTH_TOKEN') email = user_info['email'] authorized = user_domain.is_registered(email) if push_token: user_dal.update(email, {'devices_to_notify': set(push_token)}) session_jwt = jwt.encode( { 'user_email': email, 'user_role': user_domain.get_data(email, 'role'), 'company': user_domain.get_data(email, 'company'), 'first_name': user_info['given_name'], 'last_name': user_info['family_name'], 'exp': datetime.utcnow() + timedelta(seconds=settings.SESSION_COOKIE_AGE) }, algorithm='HS512', key=settings.JWT_SECRET, ) success = True except ValueError: util.cloudwatch_log( info.context, 'Security: Sign in attempt using invalid Google token') raise GraphQLError('INVALID_AUTH_TOKEN') else: rollbar.report_message('Error: Unknown auth provider' + provider, 'error') raise GraphQLError('UNKNOWN_AUTH_PROVIDER') return SignIn(authorized, session_jwt, success)
def __init__(self, project_name: str, user_email: str, role: str = None): self.email: str = user_email self.role: str = '' self.responsibility: str = '' self.phone_number: str = '' self.organization: str = '' self.first_login: str = '-' self.last_login: _List[int] = [-1, -1] self.list_projects: _List[int] = [] if not project_name: projs_active = \ ['{proj}: {description} - Active'.format( proj=proj, description=project_domain.get_description(proj)) for proj in user_domain.get_projects(self.email)] projs_suspended = \ ['{proj}: {description} - Suspended'.format( proj=proj, description=project_domain.get_description(proj)) for proj in user_domain.get_projects( self.email, active=False)] self.list_projects = projs_active + projs_suspended last_login = user_domain.get_data(user_email, 'last_login') if last_login == '1111-1-1 11:11:11' or not last_login: self.last_login = [-1, -1] else: dates_difference = \ datetime.now() - datetime.strptime(last_login, '%Y-%m-%d %H:%M:%S') diff_last_login = [dates_difference.days, dates_difference.seconds] self.last_login = diff_last_login self.first_login = user_domain.get_data(user_email, 'date_joined') organization = user_domain.get_data(user_email, 'company') self.organization = organization.title() self.responsibility = has_responsibility( project_name, user_email) if project_name else '' self.phone_number = has_phone_number(user_email) user_role = user_domain.get_data(user_email, 'role') if project_name and is_customeradmin(project_name, user_email): self.role = 'customer_admin' elif user_role == 'customeradmin': self.role = 'customer' else: self.role = user_role if project_name and role: if role == 'admin': has_access = has_access_to_project(user_email, project_name, self.role) else: has_access = user_domain.get_project_access( user_email, project_name) if not user_domain.get_data(user_email, 'email') or \ not has_access: raise UserNotFound()
def update_treatment_in_vuln(finding_id: str, updated_values: Dict[str, str]) -> bool: new_values = cast( Dict[str, FindingType], { 'treatment': updated_values.get('treatment', ''), 'treatment_justification': updated_values.get('justification'), 'acceptance_date': updated_values.get('acceptance_date'), }) if new_values['treatment'] == 'NEW': new_values['treatment_manager'] = None vulns = get_vulnerabilities(finding_id) resp = True for vuln in vulns: if 'treatment_manager' not in [vuln, new_values]: new_values[ 'treatment_manager'] = vuln_domain.set_treatment_manager( str(new_values.get('treatment', '')), str(updated_values.get('user', '')), finding_dal.get_finding(finding_id), user_domain.get_data(str(updated_values.get('user')), 'role') == 'customeradmin', str(updated_values.get('user', ''))) result_update_treatment = \ vuln_dal.update(finding_id, str(vuln.get('UUID', '')), new_values) if not result_update_treatment: resp = False return resp
def set_treatment_manager(treatment: str, treatment_manager: str, finding: Dict[str, FindingType], is_customer_admin: bool, user_mail: str) -> str: if treatment == 'IN PROGRESS': if not is_customer_admin: treatment_manager = user_mail if treatment_manager: project_users = project_dal.get_users( str(finding.get('project_name'))) customer_roles = ['customer', 'customeradmin'] customer_users = \ [user for user in project_users if user_domain.get_data(user, 'role') in customer_roles] if treatment_manager not in customer_users: raise GraphQLError('Invalid treatment manager') else: raise GraphQLError('Invalid treatment manager') elif treatment == 'ACCEPTED': treatment_manager = user_mail elif treatment == 'ACCEPTED_UNDEFINED': last_state = cast(List[Dict[str, str]], finding.get('historic_treatment'))[-1] if last_state['acceptance_status'] == 'SUBMITTED': treatment_manager = user_mail else: treatment_manager = last_state['user'] return treatment_manager
def custom_data(self, user): """ Send extra data to Intercom. """ email = user.get_username() company = user_domain.get_data(email, 'company') return { 'Company': company, }
async def _resolve_fields(info, email, project_name): """Async resolve of fields.""" email_dict: dict = await _get_email(email) role_dict: dict = await _get_role(email, project_name) email: str = email_dict['email'] role: str = role_dict['role'] if project_name and role: if role == 'admin': has_access = has_access_to_project( email, project_name, role) else: has_access = user_domain.get_project_access( email, project_name) if not user_domain.get_data(email, 'email') or \ not has_access: raise UserNotFound() result = dict() tasks = list() for requested_field in info.field_nodes[0].selection_set.selections: snake_field = convert_camel_case_to_snake(requested_field.name.value) if snake_field.startswith('_'): continue resolver_func = getattr( sys.modules[__name__], f'_get_{snake_field}' ) future = asyncio.ensure_future(resolver_func(email, project_name)) tasks.append(future) tasks_result = await asyncio.gather(*tasks) for dict_result in tasks_result: result.update(dict_result) return result
def create_user(strategy, details, backend, user=None, *args, **kwargs): del args del kwargs del backend first_name = details['first_name'][:29] last_name = details['last_name'][:29] email = details['email'].lower() # Put details on session. strategy.session_set('first_name', first_name) strategy.session_set('last_name', last_name) today = user_domain.get_current_date() data_dict = { 'first_name': first_name, 'last_login': today, 'last_name': last_name, 'date_joined': today } if user: if user_domain.get_data(str(user), 'first_name'): user_domain.update_last_login(user) else: user_domain.update_multiple_user_attributes(str(user), data_dict) else: mail_to = [FI_MAIL_CONTINUOUS, FI_MAIL_PROJECTS] name = first_name + ' ' + last_name context = { 'name_user': name, 'mail_user': email, } send_mail_new_user(mail_to, context) user_domain.update_multiple_user_attributes(email, data_dict)
def create_new_user(context: Dict[str, Any], new_user_data: Dict[str, Any], project_name: str) -> bool: analizable_list = list(new_user_data.values())[1:-1] if (all(validate_alphanumeric_field(field) for field in analizable_list) and validate_phone_field(new_user_data['phone_number']) and validate_email_address(new_user_data['email'])): email = new_user_data['email'] organization = new_user_data['organization'] responsibility = new_user_data['responsibility'] role = new_user_data['role'] phone_number = new_user_data['phone_number'] else: return False success = False if not user_domain.get_data(email, 'email'): user_domain.create(email.lower(), { 'company': organization.lower(), 'phone': phone_number }) if not user_domain.is_registered(email): user_domain.register(email) user_domain.assign_role(email, role) user_domain.update(email, organization.lower(), 'company') elif user_domain.is_registered(email): user_domain.assign_role(email, role) if project_name and responsibility and len(responsibility) <= 50: project_domain.add_access(email, project_name, 'responsibility', responsibility) else: util.cloudwatch_log( context, 'Security: {email} Attempted to add responsibility to project \ {project} without validation'.format(email=email, project=project_name)) if phone_number and phone_number[1:].isdigit(): user_domain.add_phone_to_user(email, phone_number) if project_name and role == 'customeradmin': project_domain.add_user(project_name.lower(), email.lower(), role) if project_name and user_domain.update_project_access( email, project_name, True): description = project_domain.get_description(project_name.lower()) project_url = \ 'https://fluidattacks.com/integrates/dashboard#!/project/' \ + project_name.lower() + '/indicators' mail_to = [email] context = { 'admin': email, 'project': project_name, 'project_description': description, 'project_url': project_url, } email_send_thread = \ threading.Thread(name='Access granted email thread', target=send_mail_access_granted, args=(mail_to, context,)) email_send_thread.start() success = True return success
def resolve_remember(self, info): """ Resolve remember preference """ jwt_content = util.get_jwt_content(info.context) user_email = jwt_content.get('user_email') user_info = user_domain.get_data(user_email, 'legal_remember') self.remember = user_info if user_info else False return self.remember
def _get_recipient_first_name(email: str) -> str: first_name = user_domain.get_data(email, 'first_name') if not first_name: first_name = email.split('@')[0] else: # First name exists in database pass return str(first_name)
def _send_new_event_mail(analyst: str, event_id: str, project: str, subscription: str, event_type: str): recipients = project_dal.list_project_managers(project) recipients.append(analyst) if subscription == 'oneshot': recipients.append(FI_MAIL_PROJECTS) elif subscription == 'continuous': recipients += [FI_MAIL_CONTINUOUS, FI_MAIL_PROJECTS] if event_type in [ 'CLIENT_APPROVES_CHANGE_TOE', 'CLIENT_CANCELS_PROJECT_MILESTONE', 'CLIENT_EXPLICITLY_SUSPENDS_PROJECT' ]: recipients.append(FI_MAIL_PRODUCTION) recipients += FI_MAIL_REVIEWERS.split(',') email_context = { 'analyst_email': analyst, 'event_id': event_id, 'event_url': ('https://fluidattacks.com/integrates/dashboard#!/' f'project/{project}/events/{event_id}'), 'project': project } recipients_customers = [ recipient for recipient in recipients if user_domain.get_data(recipient, 'role') == 'customeradmin' ] recipients_not_customers = [ recipient for recipient in recipients if user_domain.get_data(recipient, 'role') != 'customeradmin' ] email_context_customers = email_context.copy() email_context_customers['analyst_email'] = \ 'Hacker at ' + str(user_domain.get_data(analyst, 'company')).capitalize() email_send_thread = threading.Thread( name='New event email thread', target=send_mail_new_event, args=([recipients_not_customers, recipients_customers], [email_context, email_context_customers])) email_send_thread.start()
def get_user_role(user_data: UserType) -> str: if user_data.get('jti'): role = str( user_domain.get_data(str(user_data.get('user_email', '')), 'role')) if role == 'customeradmin': role = 'customer' else: role = str(user_data.get('user_role', '')) return role
def resolve_users(self, info): """ Resolve project users """ init_email_list = project_domain.get_users(self.name) user_email_list = util.user_email_filter( init_email_list, util.get_jwt_content(info.context)['user_email']) self.users = [User(self.name, user_email) for user_email in user_email_list if user_domain.get_data(user_email, 'role') in ['customer', 'customeradmin']] return self.users
def _get_role(email, project_name): """Get role.""" user_role = user_domain.get_data(email, 'role') if project_name and is_customeradmin(project_name, email): role = 'customer_admin' elif user_role == 'customeradmin': role = 'customer' else: role = user_role return dict(role=role)
def _get_access_token(jwt_content): """Get access token.""" user_email = jwt_content.get('user_email') access_token = user_domain.get_data(user_email, 'access_token') access_token_dict = { 'hasAccessToken': bool(access_token), 'issuedAt': str(access_token.get('iat', '')) if bool(access_token) else '' } return dict(access_token=json.dumps(access_token_dict))
def _get_last_login(email, _=None): """Get last_login.""" last_login = user_domain.get_data(email, 'last_login') if last_login == '1111-1-1 11:11:11' or not last_login: last_login = [-1, -1] else: dates_difference = \ datetime.now() - datetime.strptime(last_login, '%Y-%m-%d %H:%M:%S') diff_last_login = [dates_difference.days, dates_difference.seconds] last_login = diff_last_login return dict(last_login=str(last_login))
def check_registered(strategy, details, backend, *args, **kwargs): del args del kwargs del backend email = details['email'].lower() is_registered = user_domain.is_registered(email) last_login = user_domain.get_data(email, 'last_login') role = user_domain.get_data(email, 'role') company = user_domain.get_data(email, 'company') strategy.session_set('username', email) strategy.session_set('registered', is_registered) if role == 'customeradmin': role = 'customer' else: # different role pass strategy.session_set('role', role) strategy.session_set('company', company) strategy.session_set('last_login', last_login) strategy.session_set('projects', {})
def has_valid_access_token(email: str, context: Dict[str, str], jti: str) -> bool: """ Verify if has active access token and match. """ access_token = cast(Dict[str, str], user_domain.get_data(email, 'access_token')) resp = False if context and access_token: resp = util.verificate_hash_token(access_token, jti) else: # authorization header not present or user without access_token pass return resp
def resolve_access_token(self, info): jwt_content = util.get_jwt_content(info.context) user_email = jwt_content.get('user_email') access_token = user_domain.get_data(user_email, 'access_token') access_token_dict = { 'hasAccessToken': bool(access_token), 'issuedAt': str(access_token.get('iat', '')) if bool(access_token) else '' } self.access_token = json.dumps(access_token_dict) return self.access_token
def create_project(user_email: str, user_role: str, **kwargs: Dict[str, Union[bool, str, List[str]]]) -> bool: is_user_admin = user_role == 'admin' if is_user_admin or \ cast(List[str], kwargs.get('companies', [])): companies = [ company.lower() for company in kwargs.get('companies', []) ] else: companies = [str(user_domain.get_data(user_email, 'company'))] description = str(kwargs.get('description', '')) has_forces = kwargs.get('has_forces', False) project_name = str(kwargs.get('project_name', '')).lower() if kwargs.get('subscription'): subscription = str(kwargs.get('subscription')) else: subscription = 'continuous' resp = False if not (not description.strip() or not project_name.strip() or not all([company.strip() for company in companies]) or not companies): if has_forces and subscription != 'continuous': # Forces is only available in projects of type continuous raise InvalidProjectForcesSubscriptionType() if not project_dal.exists(project_name): project: ProjectType = { 'project_name': project_name, 'description': description, 'has_forces': has_forces, 'companies': companies, 'type': subscription, 'project_status': 'ACTIVE' } resp = project_dal.create(project) if resp: if not is_user_admin: add_user_access = user_domain.update_project_access( user_email, project_name, True) add_user_manager = add_user(project_name.lower(), user_email.lower(), 'customeradmin') resp = all([add_user_access, add_user_manager]) else: raise InvalidProjectName() else: raise InvalidParameter() return resp
def get_email_recipients(project_name: str, comment_type: Union[str, bool]) -> List[str]: project_users = project_dal.get_users(project_name) recipients: List[str] = [] approvers = FI_MAIL_REVIEWERS.split(',') recipients += approvers if comment_type == 'observation': analysts = [ user for user in project_users if user_domain.get_data(user, 'role') == 'analyst' ] recipients += analysts else: recipients += project_users return recipients
def resolve_update_access_token(_, info, expiration_time): """Resolve update_access_token mutation.""" user_info = util.get_jwt_content(info.context) email = user_info['user_email'] token_data = util.calculate_hash_token() session_jwt = '' success = False if util.is_valid_expiration_time(expiration_time): session_jwt = jwt.encode( { 'user_email': email, 'company': user_domain.get_data( email, 'company'), 'first_name': user_info['first_name'], 'last_name': user_info['last_name'], 'jti': token_data['jti'], 'iat': datetime.utcnow().timestamp(), 'exp': expiration_time }, algorithm='HS512', key=settings.JWT_SECRET_API ) success = user_domain.update_access_token(email, token_data) if success: util.cloudwatch_log( info.context, '{email} update access token'.format( email=user_info['user_email'])) else: util.cloudwatch_log( info.context, '{email} attempted to update access token' .format(email=user_info['user_email'])) else: util.cloudwatch_log( info.context, '{email} attempted to use expiration time \ greater than six months or minor than current time' .format(email=user_info['user_email'])) raise InvalidExpirationTime() return dict(success=success, session_jwt=session_jwt)
def _get_organization(email, _=None): """Get organization.""" org = user_domain.get_data(email, 'company') return dict(organization=org.title())
def _get_first_login(email, _=None): """Get first login.""" result = user_domain.get_data(email, 'date_joined') return dict(first_login=result)
def has_phone_number(email: str) -> str: user_info = str(user_domain.get_data(email, 'phone')) user_phone = user_info if user_info else '-' return user_phone
def send_comment_mail(comment_data: CommentType, entity_name: str, user_mail: str, comment_type: str = '', entity: Union[str, Dict[str, FindingType], EventType, ProjectType] = ''): parent = comment_data['parent'] base_url = 'https://fluidattacks.com/integrates/dashboard#!' email_context = { 'user_email': user_mail, 'comment': str(comment_data['content']).replace('\n', ' '), 'comment_type': comment_type, 'parent': parent, } if entity_name == 'finding': finding: Dict[str, FindingType] = cast(Dict[str, FindingType], entity) project_name = str(finding.get('projectName', '')) recipients = get_email_recipients(project_name, comment_type) is_draft = 'releaseDate' in finding email_context['finding_id'] = str(finding.get('findingId', '')) email_context['finding_name'] = str(finding.get('finding', '')) comment_url = ( base_url + '/project/{project}/{finding_type}/{id}/{comment_type}s'.format( comment_type=comment_type, finding_type='findings' if is_draft else 'drafts', id=finding.get('findingId'), project=project_name)) elif entity_name == 'event': event = cast(EventType, entity) event_id = str(event.get('event_id', '')) project_name = str(event.get('project_name', '')) recipients = project_dal.get_users(project_name, True) email_context['finding_id'] = event_id email_context['finding_name'] = f'Event #{event_id}' comment_url = ('https://fluidattacks.com/integrates/dashboard#!/' f'project/{project_name}/events/{event_id}/comments') elif entity_name == 'project': project_name = str(entity) recipients = get_email_recipients(project_name, True) comment_url = ( base_url + '/project/{project!s}/comments'.format(project=project_name)) email_context['comment_url'] = comment_url email_context['project'] = project_name recipients_customers = [ recipient for recipient in recipients if user_domain.get_data( recipient, 'role') in ['customer', 'customeradmin'] ] recipients_not_customers = [ recipient for recipient in recipients if user_domain.get_data( recipient, 'role') not in ['customer', 'customeradmin'] ] email_context_customers = email_context.copy() if user_domain.get_data(user_mail, 'role') not in ['customer', 'customeradmin']: email_context_customers['user_email'] = \ 'Hacker at ' + str(user_domain.get_data(user_mail, 'company')).capitalize() email_send_thread = threading.Thread( name='New {} email thread'.format(entity_name), target=send_mail_comment, args=([recipients_not_customers, recipients_customers], [email_context, email_context_customers])) email_send_thread.start()
def _get_remember(jwt_content): """Get remember preference.""" user_email = jwt_content.get('user_email') remember = user_domain.get_data(user_email, 'legal_remember') result = remember if remember else False return dict(remember=result)