예제 #1
0
    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())
예제 #2
0
 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('/')
예제 #3
0
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__'
예제 #4
0
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__'
예제 #5
0
    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
예제 #6
0
 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))
예제 #7
0
 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')
예제 #8
0
 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', '/'))
예제 #9
0
 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
예제 #10
0
    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'))
예제 #11
0
    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')
예제 #12
0
    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'))
예제 #13
0
    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
예제 #14
0
 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'),
                         '/')
예제 #15
0
    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'))
예제 #16
0
    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'))
예제 #17
0
    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}')
예제 #18
0
 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')
예제 #20
0
 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'))
예제 #21
0
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
            }