def sync_cognito(self, include_custom=False): cognito = cognito_client() user_data = self.__dict__ cognito_data = {} for data in user_data.keys(): # Remove default django stuff if data in [ '_state', 'id', 'password', 'last_login', 'date_joined', 'backend', '_password' ]: continue cognito_data[data] = user_data[data] cognito.username = cognito_data.pop('email') # Enable/disable cognito user based on 'is_active' if not cognito_data.pop('is_active'): cognito.admin_disable_user() else: cognito.admin_enable_user() # Sync custom data user_data_fields = self.sync_custom_data() cognito_data.update(user_data_fields) # Remove foreign key id from user data model cognito_data.pop('user_data_id') cognito_data.update(**get_custom_attrs_from_options(cognito_data)) cognito.admin_update_profile(cognito_data, attr_map=get_attr_map())
def form_valid(self, form): cognito = cognito_client() login_form_data = self.request.session.get('login_data') current_password = login_form_data.get('password') new_password = form.cleaned_data['password1'] try: # Username can drop off cognito session, if it does, add it back on if not cognito.username: cognito.username = login_form_data.get('username') cognito.new_password_challenge(current_password, new_password) except Exception as e: try: cognito.auth_error_handler(e) except CognitoInvalidPassword: error = e.response.get('Error') form.add_error(field='password1', error=error['Message']) else: form.add_error( error=f'Code: {e["Code"]} - Message: {e["Message"]}', field='password1') return super(ForceChangePassword, self).form_invalid(form) else: verify_user_email(cognito) user = authenticate(username=cognito.username, password=new_password) barrier_field_login(self.request, user) return redirect('/')
class AdditionalUserFields(forms.ModelForm): email_address_verified = forms.BooleanField(required=False) cognito = cognito_client() def clean_password(self): password = self.cleaned_data.get('password') validate_password(password) return password def get_initial_for_field(self, field, field_name): if self.instance and self.instance.pk: self.cognito.username = self.instance.email user = self.cognito.admin_get_user() if field_name == 'email_address_verified': return user.email_verified return super().get_initial_for_field(field, field_name) def save(self, commit=True): self.cognito.username = self.instance.email email_address_verified = self.cleaned_data.pop( 'email_address_verified', False) user = super(AdditionalUserFields, self).save(commit=commit) if not self.instance.pk: user.register_on_cognito() verify_user_email(self.cognito, set=email_address_verified) return super(AdditionalUserFields, self).save(commit=commit) class Meta: model = User fields = '__all__'
class AdditionalUserFields(forms.ModelForm): email_address_verified = forms.BooleanField(required=False) cognito = cognito_client() def get_initial_for_field(self, field, field_name): self.cognito.username = self.instance.username user = self.cognito.get_user() if field_name == 'email_address_verified': return user.email_verified return super().get_initial_for_field(field, field_name) def save(self, commit=True): self.cognito.username = self.instance.username email_address_verified = self.cleaned_data.pop( 'email_address_verified', False) verify_user_email(self.cognito, set=email_address_verified) # TODO: Add phone number verified support # if self.cleaned_data.get('phone_number_verified'): # phone_number_verified = self.cleaned_data.pop( # 'phone_number_verified', None # ) # verify_user_phone(cognito, set=phone_number_verified) return super(AdditionalUserFields, self).save(commit=commit) class Meta: model = User fields = '__all__'
def __call__(self, request): response = self.get_response(request) cognito = cognito_client() # Refresh local cognito session if (request.user.is_authenticated and request.session.get('cognito_auth') and not cognito.access_token): cognito_auth_session = request.session['cognito_auth'] cognito.access_token = cognito_auth_session['access_token'] cognito.refresh_token = cognito_auth_session['refresh_token'] cognito.token_type = cognito_auth_session['token_type'] cognito.id_token = cognito_auth_session['id_token'] # Clean up QR codes temp_path = Path(getattr(settings, 'QR_CODE_PATH', 'static/temp/QR')) if not temp_path.exists(): return response for file in listdir(temp_path): path_to_file = f'{temp_path}/{file}' file_stats = stat(path_to_file) date_modified = datetime.datetime.fromtimestamp( file_stats.st_mtime) time_diff = (datetime.datetime.now() - date_modified).seconds / 60 if time_diff > 30: remove(path_to_file) return response
def handle(self, *args, **options): cognito = cognito_client() users = cognito.get_users() display_users = [] for user in users: info = {'key': user.pk, 'username': user.email, 'data': user._data} display_users.append(info) self.stdout.write(json.dumps(display_users, indent=4))
def handle(self, *args, **options): cognito = cognito_client() username = options['username'] try: cognito.admin_confirm_sign_up(username) except Exception as e: self.stderr.write(f'Error: {e}') self.stdout.write(f'User {username} confirmed')
def form_valid(self, form): cognito = cognito_client() code = form.cleaned_data['mfa_code'] username = cognito.username if not username: username = self.request.session['login_data']['username'] response = cognito.respond_to_auth_challenge( 'SMS_MFA', code, username, self.request.session.pop('MFA_CHALLENGE').get('Session')) if response['ResponseMetadata']['HTTPStatusCode'] == 200: complete_login(self.request, response) return redirect(getattr(settings, 'LOGIN_REDIRECT_URL', '/'))
def get_context_data(self, **kwargs): cognito = cognito_client() context = super(SetSoftwareMFA, self).get_context_data(**kwargs) response = cognito.associate_software_token(self.request) secret_code = response['SecretCode'] OTP = f'otpauth://totp/Username:{self.request.user.username}' \ f'?secret={secret_code}&issuer=BoughtByMany' save_location = generate_and_save_qr_code(self.request, OTP) context['qr_code'] = save_location.replace('static/', '') context['token_code'] = secret_code context['mfa_type'] = 'SOFTWARE' return context
def form_valid(self, form): cognito = cognito_client() # Remove temp QR code qr_code_loc = self.request.session['qr_code_loc'] os.remove(qr_code_loc) code = form.cleaned_data['mfa_code'] response = cognito.verify_software_token(self.request, code) if response['Status'] == 'SUCCESS': cognito.update_software_mfa(self.request, enabled=True) messages.success(self.request, 'Software MFA Activated') return redirect(reverse('mfa-settings'))
def handle(self, *args, **options): cognito = cognito_client() username = options['username'] cognito.username = username key = options['key'] value = options['value'] if not key and value: raise Exception('Key and value required.') cognito.admin_update_profile( {key: value} ) self.stdout.write(f'User {username} unverified')
def form_valid(self, form): cognito = cognito_client() email_address = form.cleaned_data['email_address'] verification_code = form.cleaned_data['verification_code'] new_password = form.cleaned_data['password2'] cognito.username = email_address try: response = cognito.confirm_forgot_password(verification_code, new_password) except Exception as ex: form.add_error( field='verification_code', error=f'Something went wrong: {ex} -> Resp: {response}') return redirect(reverse('cognito-login'))
def get_initial(self): cognito = cognito_client() user = cognito.get_user_detailed() mfa_preferences = user.get('UserMFASettingList') initial = super(MFASettings, self).get_initial() if not mfa_preferences: return initial if 'SOFTWARE_TOKEN_MFA' in mfa_preferences: self.software_enabled = True initial['software_mfa'] = True if 'SMS_MFA' in mfa_preferences: self.sms_enabled = True initial['sms_mfa'] = True return initial
def form_valid(self, form): cognito = cognito_client() current_password = form.cleaned_data['current_password'] new_password = form.cleaned_data['new_password1'] try: cognito.change_password(current_password, new_password) except Exception as e: try: error = e.response['Error'] except AttributeError: raise AttributeError('Something went wrong there...') if error['Code'] == 'InvalidPasswordException': form.add_error(field='new_password1', error=error['Message']) return super(ChangePassword, self).form_invalid(form) else: return redirect(getattr(settings, 'PASSWORD_CHANGE_REDIRECT_URL'), '/')
def form_valid(self, form): cognito = cognito_client() email_address = form.cleaned_data['email_address'] verification_code = form.cleaned_data['verification_code'] new_password = form.cleaned_data['password2'] user_model = get_user_model() db_user = user_model.objects.get(email=email_address) cognito.username = email_address try: db_user.set_password(new_password) db_user.save() response = cognito.confirm_forgot_password(verification_code, new_password) except Exception as ex: form.add_error(field='verification_code', error=f'Something went wrong: {ex} -> Resp:') return redirect(reverse('cognito-login'))
def form_valid(self, form): cognito = cognito_client() sms = (form.cleaned_data['sms_mfa'], self.sms_enabled) if sms[0] and not sms[1]: cognito.update_sms_mfa(self.request, enabled=True) messages.success(self.request, 'SMS MFA Activated') if sms[1] and not sms[0]: cognito.update_sms_mfa(self.request, enabled=False) messages.success(self.request, 'SMS MFA Deactivated') software = (form.cleaned_data['software_mfa'], self.software_enabled) if software[0] and not software[1]: return redirect(reverse('associate-mfa')) if software[1] and not software[0]: cognito.update_software_mfa(self.request, enabled=False) messages.success(self.request, 'Software MFA Deactivated') return redirect(reverse('mfa-settings'))
def handle(self, *args, **options): # Validate and create user in cognito # Check username format cognito = cognito_client() username = options['username'] try: validate_email(username) except ValidationError: self.stderr.write('Username must be an email address') password = options.get('temporary_password') if not password: password = generate_temporary_password() base_attributes = {'email': username} # Check telephone format telephone = options.get('telephone') if telephone: if telephone[0] != '+': dial_code = getattr(settings, 'DEFAULT_DIAL_CODE', '+44') if dial_code == '+44' and telephone[0] == '0': telephone = dial_code + telephone[1:] else: telephone = dial_code + telephone # Add to base attributes base_attributes['phone_number'] = telephone # Create a dict of all basic user info to print out complete_user = {'username': username, 'temp_password': password} complete_user.update(base_attributes) custom_attributes = get_custom_attrs_from_options(options) cognito.add_base_attributes(**base_attributes) cognito.admin_create_user(username, password) # Update user with optional and custom attributes cognito.username = username base_attributes.update(custom_attributes) base_attributes['access_key'] = 'None' cognito.admin_update_profile(base_attributes, attr_map=get_attr_map()) self.stdout.write(f'User successfully created! {complete_user}')
def form_valid(self, form): cognito = cognito_client() code = form.cleaned_data['mfa_code'] username = cognito.username if not username: username = self.request.session['username'] try: response = cognito.respond_to_auth_challenge( 'SOFTWARE_TOKEN_MFA', code, username, self.request.session.get('AUTH_CHALLENGE').get('Session')) except Exception as e: try: cognito.auth_error_handler(e) except MFAMismatch: form.add_error(field='mfa_code', error='Incorrect one time passwod') return super(SoftwareMFA, self).form_invalid(form) if response['ResponseMetadata']['HTTPStatusCode'] == 200: complete_login(self.request, response, username) return redirect(getattr(settings, 'LOGIN_REDIRECT_URL', '/'))
def handle(self, *args, **options): cognito = cognito_client() username = options['username'] cognito.username = username cognito.admin_delete_user() self.stdout.write(f'User {username} deleted')
def form_valid(self, form): cognito = cognito_client() email_address = form.cleaned_data['email_address'] cognito.username = email_address cognito.initiate_forgot_password() return redirect(reverse('forgot-password-sent'))
class CognitoAuth: Users = get_user_model() cognito = cognito_client() cognito_mapping = get_attr_map() def get_user(self, request): return self.Users.objects.get(pk=request) def authenticate(self, request, username=None, password=None, cognito_auth=None): """ Authenticate with cognito. If authentication is success the cognito user will be sync'ed with local cache. :param request: :param username: :param password: :param cognito_auth: If cognito user has already been authorised, and you are completing authentication (for example, force changing password or completeing MFA), send the authorised cognito token :return: """ self.cognito.username = username if not is_enabled(): return None if not cognito_auth: # New user session authentication try: self.cognito.authenticate(password, request) except Exception as e: resp = self.cognito.auth_error_handler(e) return resp else: # Validate authentication self.cognito.verify_token( cognito_auth['AuthenticationResult']['IdToken'], 'id_token','id' ) self.cognito.verify_token( cognito_auth['AuthenticationResult']['AccessToken'], 'access_token', 'access' ) self.update_session(request) user = self.cognito.get_user(self.cognito_mapping) self.cognito.sync_cache(user) cache_user = self.Users.objects.get(username=user.pk) return cache_user def update_session(self, request): """ Add refresh token to the session so it can be accessed """ if getattr(request, 'session', False): request.session['cognito_auth'] = { 'access_token': self.cognito.access_token, 'refresh_token': self.cognito.refresh_token, 'token_type': self.cognito.token_type, 'id_token': self.cognito.id_token }