Exemplo n.º 1
0
 def update_context_user(self):
     # XXX what is this context user???
     logger.notice('UPDATE CONTEXT USER CALLED')
     raise NotImplementedError('update_context_user UN-implemented')
     eppn = self.user.eppn
     self.user = self.request.userdb_new.get_user_by_eppn(eppn)
     retrieve_modified_ts(self.user, self.request.dashboard_userdb)
Exemplo n.º 2
0
def set_email_verified(request, user, new_mail):
    """
    Mark an e-mail address as verified on a user.

    This process also includes *removing* the e-mail address from any other user
    that had it as a verified e-mail address.

    :param request: The HTTP request
    :param user: The user
    :param new_mail: The e-mail address to mark as verified

    :type request: pyramid.request.Request
    :type user: User
    :type new_mail: str | unicode

    :return: Status message
    :rtype: str | unicode
    """
    log.info('Trying to verify mail address for user {!r}.'.format(user))
    log.debug('Mail address: {!s}.'.format(new_mail))
    # Start by removing the email address from any other user that currently has it (verified)
    old_user = request.userdb_new.get_user_by_mail(new_mail, raise_on_missing=False)
    steal_count = 0
    if old_user and old_user.user_id != user.user_id:
        retrieve_modified_ts(old_user, request.dashboard_userdb)
        old_user = _remove_mail_from_user(new_mail, old_user)
        request.context.save_dashboard_user(old_user)
        steal_count = 1
    # Add the verified mail address to the requesting user
    _add_mail_to_user(new_mail, user)
    log.info('Mail address verified for user {!r}.'.format(user))
    request.stats.count('verify_mail_stolen', steal_count)
    request.stats.count('verify_mail_completed')
    return _('Email {obj} verified')
Exemplo n.º 3
0
def set_phone_verified(request, user, new_number):
    """
    Mark a phone number as verified on a user.

    This process also includes *removing* the phone number from any other user
    that had it as a verified phone number.

    :param request: The HTTP request
    :param user: The user
    :param new_number: The phone number to mark as verified

    :type request: pyramid.request.Request
    :type user: User
    :type new_number: str | unicode

    :return: Status message
    :rtype: str | unicode
    """
    log.info('Trying to verify phone number for user {!r}.'.format(user))
    log.debug('Phone number: {!s}.'.format(new_number))
    # Start by removing mobile number from any other user
    old_user = request.userdb_new.get_user_by_phone(new_number, raise_on_missing=False)
    steal_count = 0
    if old_user and old_user.user_id != user.user_id:
        retrieve_modified_ts(old_user, request.dashboard_userdb)
        _remove_phone_from_user(new_number, old_user)
        request.context.save_dashboard_user(old_user)
        log.info('Removed phone number from user {!r}.'.format(old_user))
        steal_count = 1
    # Add the verified mobile number to the requesting user
    _add_phone_to_user(new_number, user)
    log.info('Phone number verified for user {!r}.'.format(user))
    request.stats.count('dashboard/verify_mobile_stolen', steal_count)
    request.stats.count('dashboard/verify_mobile_completed', 1)
    return _('Phone {obj} verified')
Exemplo n.º 4
0
def set_email_verified(request, user, new_mail):
    """
    Mark an e-mail address as verified on a user.

    This process also includes *removing* the e-mail address from any other user
    that had it as a verified e-mail address.

    :param request: The HTTP request
    :param user: The user
    :param new_mail: The e-mail address to mark as verified

    :type request: pyramid.request.Request
    :type user: User
    :type new_mail: str | unicode

    :return: Status message
    :rtype: str | unicode
    """
    log.info('Trying to verify mail address for user {!r}.'.format(user))
    log.debug('Mail address: {!s}.'.format(new_mail))
    # Start by removing the email address from any other user that currently has it (verified)
    old_user = request.userdb_new.get_user_by_mail(new_mail, raise_on_missing=False)
    steal_count = 0
    if old_user and old_user.user_id != user.user_id:
        retrieve_modified_ts(old_user, request.dashboard_userdb)
        old_user = _remove_mail_from_user(new_mail, old_user)
        request.context.save_dashboard_user(old_user)
        steal_count = 1
    # Add the verified mail address to the requesting user
    _add_mail_to_user(new_mail, user)
    log.info('Mail address verified for user {!r}.'.format(user))
    request.stats.count('dashboard/verify_mail_stolen', steal_count)
    request.stats.count('dashboard/verify_mail_completed', 1)
    return _('Email {obj} verified')
Exemplo n.º 5
0
 def update_context_user(self):
     # XXX what is this context user???
     logger.notice('UPDATE CONTEXT USER CALLED')
     raise NotImplementedError('update_context_user UN-implemented')
     eppn = self.user.eppn
     self.user = self.request.userdb_new.get_user_by_eppn(eppn)
     retrieve_modified_ts(self.user, self.request.dashboard_userdb)
Exemplo n.º 6
0
    def reset_success(self, passwordform):
        form_data = self.schema.serialize(passwordform)
        hash_code = self.request.matchdict['code']
        password_reset = self.request.db.reset_passwords.find_one(
            {'hash_code': hash_code})

        if password_reset.get('eppn'):
            user = self.request.userdb_new.get_user_by_eppn(
                password_reset['eppn'])

        # Legacy password reset codes were connected to the user by email
        elif password_reset.get('email'):
            user = self.request.userdb_new.get_user_by_mail(
                password_reset['email'])

        log.debug("Loaded user {!s} from {!s}".format(user,
                                                      self.request.userdb))

        if form_data.get('use_custom_password') == 'true':
            log.debug(
                "Password change for user {!r} (custom password).".format(
                    user))
            new_password = form_data.get('custom_password')
            self.request.stats.count('pwreset_custom_password')
        else:
            log.debug(
                "Password change for user {!r} (suggested password).".format(
                    user))
            new_password = self.get_suggested_password()
            self.request.stats.count('pwreset_generated_password')

        # Make user AL1 if password was reset by e-mail only
        if password_reset['mechanism'] == 'email':
            nin_count = user.nins.count
            if nin_count:
                retrieve_modified_ts(user, self.request.dashboard_userdb)
                unverify_user_nins(self.request, user)
                self.request.stats.count('pwreset_downgraded_NINs', nin_count)
            if len(user.phone_numbers.to_list()):
                retrieve_modified_ts(user, self.request.dashboard_userdb)
                # We need to unverify a users phone numbers to make sure that an attacker can not
                # verify the account again without control over the users phone number
                # This should be changed to only unverify the phone numbers instead of removing them.
                unverify_user_mobiles(self.request, user)

        # Save new password
        new_password = new_password.replace(' ', '')
        ok = change_password(self.request, user, '', new_password)

        if ok:
            self.request.db.reset_passwords.remove(
                {'_id': password_reset['_id']})
            self.request.stats.count('pwreset_changed_password')
            url = self.request.route_url('profile-editor')
            reset = True
        else:
            self.request.stats.count('pwreset_password_change_failed')
            url = self.request.route_url('reset-password')
            reset = False
        return {'url': url, 'reset': reset}
Exemplo n.º 7
0
def set_phone_verified(request, user, new_number):
    """
    Mark a phone number as verified on a user.

    This process also includes *removing* the phone number from any other user
    that had it as a verified phone number.

    :param request: The HTTP request
    :param user: The user
    :param new_number: The phone number to mark as verified

    :type request: pyramid.request.Request
    :type user: User
    :type new_number: str | unicode

    :return: Status message
    :rtype: str | unicode
    """
    log.info('Trying to verify phone number for user {!r}.'.format(user))
    log.debug('Phone number: {!s}.'.format(new_number))
    # Start by removing mobile number from any other user
    old_user = request.userdb_new.get_user_by_phone(new_number, raise_on_missing=False)
    steal_count = 0
    if old_user and old_user.user_id != user.user_id:
        retrieve_modified_ts(old_user, request.dashboard_userdb)
        _remove_phone_from_user(new_number, old_user)
        request.context.save_dashboard_user(old_user)
        log.info('Removed phone number from user {!r}.'.format(old_user))
        steal_count = 1
    # Add the verified mobile number to the requesting user
    _add_phone_to_user(new_number, user)
    log.info('Phone number verified for user {!r}.'.format(user))
    request.stats.count('verify_mobile_stolen', steal_count)
    request.stats.count('verify_mobile_completed')
    return _('Phone {obj} verified')
Exemplo n.º 8
0
 def get_user(self):
     logger.debug("Looking for verification code {!r}".format(self.request.matchdict['code']))
     verification_code = self.request.db.verifications.find_one({
         'code': self.request.matchdict['code'],
     })
     if verification_code is None:
         raise HTTPNotFound()
     user = self.request.userdb_new.get_user_by_id(verification_code['user_oid'])
     retrieve_modified_ts(user, self.request.dashboard_userdb)
     return user
Exemplo n.º 9
0
    def save_success(self, passwordform):
        authn_ts = self.request.session.get('reauthn-for-chpass', None)
        if authn_ts is None:
            raise HTTPBadRequest(_('No authentication info'))
        now = datetime.utcnow()
        delta = now - datetime.fromtimestamp(authn_ts)
        # XXX put the reauthn timeout in the settings
        if int(delta.total_seconds()) > 600:
            msg = _('Stale authentication info. Please try again.')
            self.request.session.flash('error|' + msg)
            raise HTTPFound(self.context.route_url('profile-editor'))
        user = get_session_user(self.request)
        log.debug('Removing Authn ts for user {!r} before'
                  ' changing the password'.format(user))
        del self.request.session['reauthn-for-chpass']
        passwords_data = self.schema.serialize(passwordform)

        if passwords_data.get('use_custom_password') == 'true':
            # The user has entered his own password and it was verified by
            # validators
            log.debug(
                "Password change for user {!r} (custom password).".format(
                    user))
            new_password = passwords_data.get('custom_password')

        else:
            # If the user has selected the suggested password, then it should
            # be in session
            log.debug(
                "Password change for user {!r} (suggested password).".format(
                    user))
            new_password = self.get_suggested_password()

        new_password = new_password.replace(' ', '')

        old_password = passwords_data['old_password']

        # Load user from database to ensure we are working on an up-to-date set of credentials.
        # XXX this refresh is a bit redundant with the same thing being done in OldPasswordValidator.
        user = self.request.userdb_new.get_user_by_id(user.user_id)
        log.debug("Refreshed user {!s} from {!s}".format(
            user, self.request.userdb_new))
        retrieve_modified_ts(user, self.request.dashboard_userdb)

        self.changed = change_password(self.request, user, old_password,
                                       new_password)
        if self.changed:
            message = 'success|' + _(
                'Your password has been successfully updated')
        else:
            message = 'error|' + _(
                'An error has occured while updating your password, '
                'please try again or contact support if the problem persists.')
        self.request.session.flash(message)
        raise HTTPFound(self.context.route_url('profile-editor'))
Exemplo n.º 10
0
    def reset_success(self, passwordform):
        form_data = self.schema.serialize(passwordform)
        hash_code = self.request.matchdict['code']
        password_reset = self.request.db.reset_passwords.find_one({'hash_code': hash_code})

        if password_reset.get('eppn'):
            user = self.request.userdb_new.get_user_by_eppn(password_reset['eppn'])

        # Legacy password reset codes were connected to the user by email
        elif password_reset.get('email'):
            user = self.request.userdb_new.get_user_by_mail(password_reset['email'])

        log.debug("Loaded user {!s} from {!s}".format(user, self.request.userdb))

        if form_data.get('use_custom_password') == 'true':
            log.debug("Password change for user {!r} (custom password).".format(user))
            new_password = form_data.get('custom_password')
            self.request.stats.count('pwreset_custom_password')
        else:
            log.debug("Password change for user {!r} (suggested password).".format(user))
            new_password = self.get_suggested_password()
            self.request.stats.count('pwreset_generated_password')

        # Make user AL1 if password was reset by e-mail only
        if password_reset['mechanism'] == 'email':
            nin_count = user.nins.count
            if nin_count:
                retrieve_modified_ts(user, self.request.dashboard_userdb)
                unverify_user_nins(self.request, user)
                self.request.stats.count('pwreset_downgraded_NINs', nin_count)
            if len(user.phone_numbers.to_list()):
                retrieve_modified_ts(user, self.request.dashboard_userdb)
                # We need to unverify a users phone numbers to make sure that an attacker can not
                # verify the account again without control over the users phone number
                # This should be changed to only unverify the phone numbers instead of removing them.
                unverify_user_mobiles(self.request, user)

        # Save new password
        new_password = new_password.replace(' ', '')
        ok = change_password(self.request, user, '', new_password)

        if ok:
            self.request.db.reset_passwords.remove({'_id': password_reset['_id']})
            self.request.stats.count('pwreset_changed_password')
            url = self.request.route_url('profile-editor')
            reset = True
        else:
            self.request.stats.count('pwreset_password_change_failed')
            url = self.request.route_url('reset-password')
            reset = False
        return {
            'url': url,
            'reset': reset
        }
Exemplo n.º 11
0
    def reset_success(self, passwordform):
        form_data = self.schema.serialize(passwordform)
        hash_code = self.request.matchdict['code']
        password_reset = self.request.db.reset_passwords.find_one({'hash_code': hash_code})
        user = self.request.userdb_new.get_user_by_mail(password_reset['email'])

        log.debug("Loaded user {!s} from {!s}".format(user, self.request.userdb))

        if form_data.get('use_custom_password') == 'true':
            log.debug("Password change for user {!r} (custom password).".format(user))
            new_password = form_data.get('custom_password')
            self.request.stats.count('dashboard/pwreset_custom_password', 1)
        else:
            log.debug("Password change for user {!r} (suggested password).".format(user))
            new_password = self.get_suggested_password()
            self.request.stats.count('dashboard/pwreset_generated_password', 1)

        # Make user AL1 if password was reset by e-mail only
        if password_reset['mechanism'] == 'email':
            retrieve_modified_ts(user, self.request.dashboard_userdb)
            sync_user = False
            nin_count = len(user.nins.to_list())
            if nin_count:
                unverify_user_nins(self.request, user)
                sync_user = True
                self.request.stats.count('dashboard/pwreset_downgraded_NINs', nin_count)
            if len(user.phone_numbers.to_list()):
                # We need to unverify a users phone numbers to make sure that an attacker can not
                # verify the account again without control over the users phone number
                unverify_user_mobiles(self.request, user)
                sync_user = True
            if sync_user:
                # Do not perform a sync if no changes where made, there is a corner case
                # where the user has not been created yet
                self.request.amrelay.request_sync(user)

        # Save new password
        new_password = new_password.replace(' ', '')
        ok = change_password(self.request, user, '', new_password)

        if ok:
            self.request.db.reset_passwords.remove({'_id': password_reset['_id']})
            self.request.stats.count('dashboard/pwreset_changed_password', 1)
            url = self.request.route_url('profile-editor')
            reset = True
        else:
            self.request.stats.count('dashboard/pwreset_password_change_failed', 1)
            url = self.request.route_url('reset-password')
            reset = False
        return {
            'url': url,
            'reset': reset
        }
Exemplo n.º 12
0
def sync_user(request, context, old_user):
    user = request.userdb.get_user_by_id(old_user.user_id)

    if isinstance(user, DashboardUser):
        retrieve_modified_ts(user, request.dashboard_userdb)
    else:
        user.retrieve_modified_ts(request.db.profiles)

    if context.workmode == 'personal':
        store_session_user(request, user)
    else:
        store_session_user(request, user, edit_user = True)
    return user
Exemplo n.º 13
0
def sync_user(request, context, old_user):
    user = request.userdb.get_user_by_id(old_user.user_id)

    if isinstance(user, DashboardUser):
        retrieve_modified_ts(user, request.dashboard_userdb)
    else:
        user.retrieve_modified_ts(request.db.profiles)

    if context.workmode == 'personal':
        store_session_user(request, user)
    else:
        store_session_user(request, user, edit_user=True)
    return user
Exemplo n.º 14
0
 def get_user(self):
     logger.debug("Looking for verification code {!r}".format(
         self.request.matchdict['code']))
     verification_code = self.request.db.verifications.find_one({
         'code':
         self.request.matchdict['code'],
     })
     if verification_code is None:
         raise HTTPNotFound()
     user = self.request.userdb_new.get_user_by_id(
         verification_code['user_oid'])
     retrieve_modified_ts(user, self.request.dashboard_userdb)
     return user
Exemplo n.º 15
0
    def verify_mb_action(self, data, post_data):
        """
        Verify a users identity using their mobile phone subscriber records.

        Only the active (the last one) NIN can be verified
        """
        nin, index = data.split()
        index = int(index)
        nins = get_not_verified_nins_list(self.request, self.user)

        if len(nins) > index:
            new_nin = nins[index]
            if new_nin != nin:
                return self.sync_user()
        else:
            return self.sync_user()

#        if index != len(nins) - 1:
#            message = _("The provided nin can't be verified. You only "
#                        'can verify the last one')
#            return {
#                'result': 'bad',
#                'message': get_localizer(self.request).translate(message),
#            }

        validation = validate_nin_by_mobile(self.request, self.user, nin)
        result = validation['success'] and 'success' or 'error'
        model_name = 'norEduPersonNIN'
        if result == 'success':
            session_user = get_session_user(self.request, legacy_user = False)
            retrieve_modified_ts(session_user, self.request.dashboard_userdb)
            set_nin_verified(self.request, session_user, nin)
            try:
                #self.user.save(self.request)
                self.request.context.save_dashboard_user(session_user)
                logger.info("Verified by mobile, {!s} saved for user {!r}.".format(model_name, session_user))
                # Save the state in the verifications collection
                save_as_verified(self.request, 'norEduPersonNIN', session_user, nin)
            except UserOutOfSync:
                logger.info("Verified {!s} NOT saved for user {!r}. User out of sync.".format(model_name, session_user))
                raise
        settings = self.request.registry.settings
        msg = get_localizer(self.request).translate(validation['message'],
                mapping={
                'service_name': settings.get('mobile_service_name', 'Navet'),
                })
        return {
            'result': result,
            'message': msg,
            }
Exemplo n.º 16
0
    def test_verify_existant_nin_by_mobile(self):
        email = self.no_nin_user_email
        self.set_logged(email)
        user = self.userdb_new.get_user_by_mail(email)
        dashboard_user = DashboardUser(data=user.to_dict())
        retrieve_modified_ts(dashboard_user, self.dashboard_db)

        self.assertEqual(dashboard_user.nins.count, 0)

        # Add a verified phone number to the user in the central userdb
        dashboard_user.phone_numbers.add(
            PhoneNumber(data={
                'number': '666666666',
                'primary': True,
                'verified': True
            }))
        self.dashboard_db.save(dashboard_user)

        # First we add a nin...
        nin = '200010100001'

        response_form = self.testapp.get('/profile/nins/')
        form = response_form.forms[self.formname]
        from eduiddashboard.msgrelay import MsgRelay
        with patch.multiple(MsgRelay,
                            nin_validator=return_true,
                            nin_reachable=return_true):
            form['norEduPersonNIN'].value = nin
            form.submit('add')

        # and then we verify it
        self.testapp.get('/profile/nins/')
        from eduiddashboard.views import nins
        with patch.object(nins, 'validate_nin_by_mobile', clear=True):
            nins.validate_nin_by_mobile.return_value = {
                'success': True,
                'message': u'Ok',
            }
            response = self.testapp.post('/profile/nins-actions/', {
                'identifier': nin + ' 0',
                'action': 'verify_mb'
            })
        response_json = json.loads(response.body)
        self.assertEqual(response_json['message'], 'Ok')
        user = self.dashboard_db.get_user_by_mail(email)
        self.assertEqual(user.nins.count, 1)
        self.assertEqual(user.nins.to_list_of_dicts()[0]['number'], nin)
Exemplo n.º 17
0
    def save_success(self, passwordform):
        authn_ts = self.request.session.get('reauthn-for-chpass', None)
        if authn_ts is None:
            raise HTTPBadRequest(_('No authentication info'))
        now = datetime.utcnow()
        delta = now - datetime.fromtimestamp(authn_ts)
        # XXX put the reauthn timeout in the settings
        if int(delta.total_seconds()) > 600:
            msg = _('Stale authentication info. Please try again.')
            self.request.session.flash('error|' + msg)
            raise HTTPFound(self.context.route_url('profile-editor'))
        user = get_session_user(self.request)
        log.debug('Removing Authn ts for user {!r} before'
                ' changing the password'.format(user))
        del self.request.session['reauthn-for-chpass']
        passwords_data = self.schema.serialize(passwordform)

        if passwords_data.get('use_custom_password') == 'true':
            # The user has entered his own password and it was verified by
            # validators
            log.debug("Password change for user {!r} (custom password).".format(user))
            new_password = passwords_data.get('custom_password')

        else:
            # If the user has selected the suggested password, then it should
            # be in session
            log.debug("Password change for user {!r} (suggested password).".format(user))
            new_password = self.get_suggested_password()

        new_password = new_password.replace(' ', '')

        old_password = passwords_data['old_password']

        # Load user from database to ensure we are working on an up-to-date set of credentials.
        # XXX this refresh is a bit redundant with the same thing being done in OldPasswordValidator.
        user = self.request.userdb_new.get_user_by_id(user.user_id)
        log.debug("Refreshed user {!s} from {!s}".format(user, self.request.userdb_new))
        retrieve_modified_ts(user, self.request.dashboard_userdb)

        self.changed = change_password(self.request, user, old_password, new_password)
        if self.changed:
            message = 'success|' + _('Your password has been successfully updated')
        else:
            message = 'error|' + _('An error has occured while updating your password, '
                        'please try again or contact support if the problem persists.')
        self.request.session.flash(message)
        raise HTTPFound(self.context.route_url('profile-editor'))
Exemplo n.º 18
0
    def test_verify_existant_nin_by_mobile(self):
        email = self.no_nin_user_email
        self.set_logged(email)
        user = self.userdb_new.get_user_by_mail(email)
        dashboard_user = DashboardUser(data=user.to_dict())
        retrieve_modified_ts(dashboard_user, self.dashboard_db)

        self.assertEqual(dashboard_user.nins.count, 0)

        # Add a verified phone number to the user in the central userdb
        dashboard_user.phone_numbers.add(PhoneNumber(data={
            'number': '666666666',
            'primary': True,
            'verified': True
            }))
        self.dashboard_db.save(dashboard_user)

        # First we add a nin...
        nin = '200010100001'

        response_form = self.testapp.get('/profile/nins/')
        form = response_form.forms[self.formname]
        from eduiddashboard.msgrelay import MsgRelay
        with patch.multiple(MsgRelay, nin_validator=return_true,
                            nin_reachable=return_true):
            form['norEduPersonNIN'].value = nin
            form.submit('add')

        # and then we verify it
        self.testapp.get('/profile/nins/')
        from eduiddashboard.views import nins
        with patch.object(nins, 'validate_nin_by_mobile', clear=True):
            nins.validate_nin_by_mobile.return_value = {
                'success': True,
                'message': u'Ok',
                }
            response = self.testapp.post(
                '/profile/nins-actions/',
                {'identifier': nin + ' 0', 'action': 'verify_mb'}
            )
        response_json = json.loads(response.body)
        self.assertEqual(response_json['message'], 'Ok')
        user = self.dashboard_db.get_user_by_mail(email)
        self.assertEqual(user.nins.count, 1)
        self.assertEqual(user.nins.to_list_of_dicts()[0]['number'], nin)
Exemplo n.º 19
0
    def _search_user(self, text):
        """
        Find a user matching email, mobile or NIN.

        @param text: search string
        @return: user object
        """
        if validate_email_format(text):
            user = self.request.userdb_new.get_user_by_mail(normalize_email(text), raise_on_missing=True)
        elif text.startswith(u'0') or text.startswith(u'+'):
            text = normalize_to_e_164(self.request, text)
            user = self.request.userdb_new.get_user_by_phone(text)
        else:
            user = self.request.userdb_new.get_user_by_nin(text)

        log.debug("Found user {!r} using input {!s}.".format(user, text))
        retrieve_modified_ts(user, self.request.dashboard_userdb)
        return user
Exemplo n.º 20
0
    def _search_user(self, text):
        """
        Find a user matching email, mobile or NIN.

        @param text: search string
        @return: user object
        """
        if validate_email_format(text):
            user = self.request.userdb_new.get_user_by_mail(
                normalize_email(text), raise_on_missing=True)
        elif text.startswith(u'0') or text.startswith(u'+'):
            text = normalize_to_e_164(self.request, text)
            user = self.request.userdb_new.get_user_by_phone(text)
        else:
            user = self.request.userdb_new.get_user_by_nin(text)

        log.debug("Found user {!r} using input {!s}.".format(user, text))
        retrieve_modified_ts(user, self.request.dashboard_userdb)
        return user
Exemplo n.º 21
0
    def test_verify_nin_by_mobile(self):
        email = self.no_nin_user_email
        self.set_logged(email)
        user = self.userdb_new.get_user_by_mail(email)
        dashboard_user = DashboardUser(data=user.to_dict())
        self.assertEqual(user.nins.count, 0)

        # Add a verified phone number to the user in the central userdb
        dashboard_user.phone_numbers.add(
            PhoneNumber(data={
                'number': '666666666',
                'primary': True,
                'verified': True
            }))
        dashboard_user.modified_ts = None
        self.userdb_new.save(dashboard_user)
        retrieve_modified_ts(dashboard_user, self.dashboard_db)
        self.dashboard_db.save(dashboard_user)

        # First we add a nin...
        new_nin = '200010100001'

        response_form = self.testapp.get('/profile/nins/')
        form = response_form.forms[self.formname]
        from eduiddashboard.lookuprelay import LookupMobileRelay
        with patch.object(LookupMobileRelay, 'find_NIN_by_mobile', clear=True):
            LookupMobileRelay.find_NIN_by_mobile.return_value = new_nin
            from eduiddashboard.msgrelay import MsgRelay
            with patch.object(MsgRelay, 'get_full_postal_address', clear=True):
                MsgRelay.get_full_postal_address.return_value = {
                    'Address2': u'StreetName 104',
                    'PostalCode': u'74142',
                    'City': u'STOCKHOLM',
                }
                form['norEduPersonNIN'].value = new_nin
                form.submit('add_by_mobile')

        user = self.dashboard_db.get_user_by_mail(email)
        self.assertEqual(user.nins.count, 1)
        self.assertEqual(user.nins.primary.number, new_nin)
Exemplo n.º 22
0
def _get_user_by_eppn(request, eppn, legacy_user):
    """
    Fetch a user in either legacy format or the new eduid_userdb.User format.

    :param request: Pyramid request object
    :param eppn: eduPersonPrincipalName
    :param legacy_user: Use old format or not

    :type eppn: str | unicode
    :type legacy_user: bool

    :return: DashboardUser
    :rtype: eduid_userdb.User | eduid_userdb.dashboard.DashboardLegacyUser
    """
    if legacy_user:
        user = request.userdb.get_user_by_eppn(eppn)
        logger.debug('Loading modified_ts from dashboard db (profiles) for user {!r}'.format(user))
        user.retrieve_modified_ts(request.db.profiles)
        return user
    user = request.userdb_new.get_user_by_eppn(eppn)
    retrieve_modified_ts(user, request.dashboard_userdb)
    return user
Exemplo n.º 23
0
def change_password(request, user, old_password, new_password):
    """
    Change the user password, deleting old credentials

    @param request: Request object
    @param user: User object
    @param old_password: Old password, if supplied (i.e. not a password reset)
    @param new_password: New password

    @type request: Request
    @type user: User
    @type old_password: str or unicode or None
    """
    vccs_url = request.registry.settings.get('vccs_url')
    log.debug("Changing password user {!s}\nTIMESTAMP 1 {!s}".format(user, user.modified_ts))
    added = add_credentials(vccs_url, old_password, new_password, user)
    log.debug("Changing password user {!s}\nTIMESTAMP 2 {!s}".format(user, user.modified_ts))
    if added:
        retrieve_modified_ts(user, request.dashboard_userdb)
        user.terminated = False
        request.context.save_dashboard_user(user)
    return added
Exemplo n.º 24
0
    def test_verify_nin_by_mobile(self):
        email = self.no_nin_user_email
        self.set_logged(email)
        user = self.userdb_new.get_user_by_mail(email)
        dashboard_user = DashboardUser(data=user.to_dict())
        self.assertEqual(user.nins.count, 0)

        # Add a verified phone number to the user in the central userdb
        dashboard_user.phone_numbers.add(PhoneNumber(data={
            'number': '666666666',
            'primary': True,
            'verified': True
            }))
        dashboard_user.modified_ts = None
        self.userdb_new.save(dashboard_user)
        retrieve_modified_ts(dashboard_user, self.dashboard_db)
        self.dashboard_db.save(dashboard_user)

        # First we add a nin...
        new_nin = '200010100001'

        response_form = self.testapp.get('/profile/nins/')
        form = response_form.forms[self.formname]
        from eduiddashboard.lookuprelay import LookupMobileRelay
        with patch.object(LookupMobileRelay, 'find_NIN_by_mobile', clear=True):
            LookupMobileRelay.find_NIN_by_mobile.return_value = new_nin
            from eduiddashboard.msgrelay import MsgRelay
            with patch.object(MsgRelay, 'get_full_postal_address', clear=True):
                MsgRelay.get_full_postal_address.return_value = {
                    'Address2': u'StreetName 104',
                    'PostalCode': u'74142',
                    'City': u'STOCKHOLM',
                }
                form['norEduPersonNIN'].value = new_nin
                form.submit('add_by_mobile')

        user = self.dashboard_db.get_user_by_mail(email)
        self.assertEqual(user.nins.count, 1)
        self.assertEqual(user.nins.primary.number, new_nin)
Exemplo n.º 25
0
def set_nin_verified(request, user, new_nin, reference=None):
    """
    Mark a National Identity Number (NIN) as verified on a user.

    This process also includes *removing* the NIN from any other user
    that had it as a verified NIN.

    :param request: The HTTP request
    :param user: The user
    :param new_nin: The National Identity Number to mark as verified
    :param reference: A reference to the verification code - used for audit logging

    :type request: pyramid.request.Request
    :type user: User
    :type new_nin: str | unicode

    :return: Status message
    :rtype: str | unicode
    """
    log.info('Trying to verify NIN for user {!r}.'.format(user))
    log.debug('NIN: {!s}.'.format(new_nin))
    # Start by removing nin from any other user
    old_user = request.userdb_new.get_user_by_nin(new_nin, raise_on_missing=False)
    log.debug('Searched for NIN {!r} in {!s}: {!r}'.format(new_nin, request.userdb_new, old_user))
    steal_count = 0
    if old_user and old_user.user_id != user.user_id:
        retrieve_modified_ts(old_user, request.dashboard_userdb)
        _remove_nin_from_user(new_nin, old_user)
        request.context.save_dashboard_user(old_user)
        log.info('Removed NIN and associated addresses from user {!r}.'.format(old_user))
        steal_count = 1
    # Add the verified nin to the requesting user
    _add_nin_to_user(new_nin, user)
    _nin_verified_transaction_audit(request, reference)
    log.info('NIN verified for user {!r}.'.format(user))
    request.stats.count('dashboard/verify_nin_stolen', steal_count)
    request.stats.count('dashboard/verify_nin_completed', 1)
    return _('National identity number {obj} verified')
Exemplo n.º 26
0
def set_nin_verified(request, user, new_nin, reference=None):
    """
    Mark a National Identity Number (NIN) as verified on a user.

    This process also includes *removing* the NIN from any other user
    that had it as a verified NIN.

    :param request: The HTTP request
    :param user: The user
    :param new_nin: The National Identity Number to mark as verified
    :param reference: A reference to the verification code - used for audit logging

    :type request: pyramid.request.Request
    :type user: User
    :type new_nin: str | unicode

    :return: Status message
    :rtype: str | unicode
    """
    log.info('Trying to verify NIN for user {!r}.'.format(user))
    log.debug('NIN: {!s}.'.format(new_nin))
    # Start by removing nin from any other user
    old_user = request.userdb_new.get_user_by_nin(new_nin, raise_on_missing=False)
    log.debug('Searched for NIN {!r} in {!s}: {!r}'.format(new_nin, request.userdb_new, old_user))
    steal_count = 0
    if old_user and old_user.user_id != user.user_id:
        retrieve_modified_ts(old_user, request.dashboard_userdb)
        _remove_nin_from_user(new_nin, old_user)
        request.context.save_dashboard_user(old_user)
        log.info('Removed NIN and associated addresses from user {!r}.'.format(old_user))
        steal_count = 1
    # Add the verified nin to the requesting user
    _add_nin_to_user(new_nin, user)
    _nin_verified_transaction_audit(request, reference)
    log.info('NIN verified for user {!r}.'.format(user))
    request.stats.count('verify_nin_stolen', steal_count)
    request.stats.count('verify_nin_completed')
    return _('National identity number {obj} verified')
Exemplo n.º 27
0
def change_password(request, user, old_password, new_password):
    """
    Change the user password, deleting old credentials

    @param request: Request object
    @param user: User object
    @param old_password: Old password, if supplied (i.e. not a password reset)
    @param new_password: New password

    @type request: Request
    @type user: User
    @type old_password: str or unicode or None
    """
    vccs_url = request.registry.settings.get('vccs_url')
    log.debug("Changing password user {!s}\nTIMESTAMP 1 {!s}".format(
        user, user.modified_ts))
    added = add_credentials(vccs_url, old_password, new_password, user)
    log.debug("Changing password user {!s}\nTIMESTAMP 2 {!s}".format(
        user, user.modified_ts))
    if added:
        retrieve_modified_ts(user, request.dashboard_userdb)
        user.terminated = False
        request.context.save_dashboard_user(user)
    return added
Exemplo n.º 28
0
def _get_user_by_eppn(request, eppn, legacy_user):
    """
    Fetch a user in either legacy format or the new eduid_userdb.User format.

    :param request: Pyramid request object
    :param eppn: eduPersonPrincipalName
    :param legacy_user: Use old format or not

    :type eppn: str | unicode
    :type legacy_user: bool

    :return: DashboardUser
    :rtype: eduid_userdb.User | eduid_userdb.dashboard.DashboardLegacyUser
    """
    if legacy_user:
        user = request.userdb.get_user_by_eppn(eppn)
        logger.debug(
            'Loading modified_ts from dashboard db (profiles) for user {!r}'.
            format(user))
        user.retrieve_modified_ts(request.db.profiles)
        return user
    user = request.userdb_new.get_user_by_eppn(eppn)
    retrieve_modified_ts(user, request.dashboard_userdb)
    return user
Exemplo n.º 29
0
    def add_success_other(self, ninform):
        newnin = self.schema.serialize(ninform)
        newnin = newnin['norEduPersonNIN']

        newnin = normalize_nin(newnin)

        old_user = self.request.db.profiles.find_one({
            'norEduPersonNIN': newnin
            })

        if old_user:
            old_user = DashboardUser(data=old_user)
            retrieve_modified_ts(old_user, self.request.dashboard_userdb)
            old_user.nins.remove(newnin)
            self.context.save_dashboard_user(old_user)

        primary = False
        if self.user.nins.count == 0:
            primary = True
        newnin_obj = Nin(number=newnin, application='dashboard',
                verified=True, primary=primary)
        self.user.nins.add(newnin_obj)

        try:
            self.context.save_dashboard_user(self.user)
        except UserOutOfSync:
            message = _('Your user profile is out of sync. Please '
                        'reload the page and try again.')
        else:
            message = _('Your national identity number has been confirmed')
        # Save the state in the verifications collection
        save_as_verified(self.request, 'norEduPersonNIN', self.user, newnin)
        self.request.session.flash(
                get_localizer(self.request).translate(message),
                queue='forms')
        self.request.stats.count('nin_add_other')
Exemplo n.º 30
0
    def test_reset_password_unterminates_account(self):
        email = '*****@*****.**'
        # Set up a bunch of faked passwords to make sure they are all revoked
        user = self.userdb_new.get_user_by_mail(email)
        dashboard_user = DashboardUser(data=user.to_dict())
        retrieve_modified_ts(dashboard_user, self.dashboard_db)
        for i in range(7):
            pw = Password(
                credential_id=ObjectId(),
                salt=str(i) * 64,
                application='dashboard_unittest',
            )
            dashboard_user.passwords.add(pw)
        self.dashboard_db.save(dashboard_user)

        logging.log(logging.DEBUG,
                    "Fetching /profile/security\n\n" + ('-=-' * 30) + "\n\n")

        request = self.set_logged(email=email)
        response = self.testapp.get('/profile/security/')
        form = response.forms['terminate-account-form']

        # Verify the user has eight passwords
        user = self.dashboard_db.get_user_by_mail(email)
        self.assertEqual(
            len(user.credentials.filter(Password).to_list_of_dicts()), 8)

        logging.log(
            logging.DEBUG,
            "Submitting termination request\n\n" + ('-=-' * 30) + "\n\n")

        form_response = form.submit('submit')
        self.assertEqual(form_response.status, '302 Found')
        self.assertIn('authn.example.com', form_response.location)
        with patch('eduiddashboard.vccs.get_vccs_client'):
            from eduiddashboard.vccs import get_vccs_client
            get_vccs_client.return_value = FakeVCCSClient(fake_response={
                'revoke_creds_response': {
                    'version': 1,
                    'success': True,
                },
            })
            with patch('eduiddashboard.views.portal.send_termination_mail'):
                from eduiddashboard.views.portal import send_termination_mail
                send_termination_mail.return_value = None
                store_session_user(request, self.user)
                self.set_logged(email=self.user.mail_addresses.primary.email,
                                extra_session_data={
                                    'reauthn-for-termination': int(time.time())
                                })
                response = self.testapp.get('/profile/account-terminated/')

        # Verify the user doesn't have ANY passwords and IS terminated at this point
        user = self.dashboard_db.get_user_by_mail(email)
        self.assertEqual(user.passwords.count, 0)
        self.assertTrue(user.terminated)

        logging.log(
            logging.DEBUG,
            "Password reset of terminated user\n\n" + ('-=-' * 30) + "\n\n")

        # Do a password reset, which should resurrect the terminated user
        hash_code = '123456'
        date = datetime.now(pytz.utc)
        self.db.reset_passwords.insert({
            'email': email,
            'hash_code': hash_code,
            'mechanism': 'email',
            'created_at': date
        })
        response = self.testapp.get(
            '/profile/reset-password/{0}/'.format(hash_code))
        self.assertIn('Please choose a new password for your eduID account',
                      response.text)
        form = response.forms['resetpasswordstep2view-form']
        with patch('eduiddashboard.vccs.get_vccs_client'):
            from eduiddashboard.vccs import get_vccs_client
            get_vccs_client.return_value = FakeVCCSClient()
            form.submit('reset')

        # Verify the user has a password and is NOT terminated again
        user = self.dashboard_db.get_user_by_mail(email)
        self.assertEqual(
            len(user.credentials.filter(Password).to_list_of_dicts()), 1)
        self.assertFalse(user.terminated)
Exemplo n.º 31
0
def verify_code(request, model_name, code):
    """
    Verify a code and act accordingly to the model_name ('norEduPersonNIN', 'mobile', or 'mailAliases').

    This is what turns an unconfirmed NIN/mobile/e-mail into a confirmed one.

    :param request: The HTTP request
    :param model_name: 'norEduPersonNIN', 'mobile', or 'mailAliases'
    :param code: The user supplied code
    :type request: pyramid.request.Request
    :return: string of verified data
    """
    assert model_name in ['norEduPersonNIN', 'phone', 'mailAliases']

    this_verification = request.db.verifications.find_one(
        {
            "model_name": model_name,
            "code": code,
        })

    if not this_verification:
        log.error("Could not find verification record for code {!r}, model {!r}".format(code, model_name))
        return

    reference = unicode(this_verification['_id'])
    obj_id = this_verification['obj_id']

    if not obj_id:
        return None

    user = get_session_user(request, legacy_user=False)
    retrieve_modified_ts(user, request.dashboard_userdb)

    assert_error_msg = 'Requesting users ID does not match verifications user ID'
    assert user.user_id == this_verification['user_oid'], assert_error_msg

    if model_name == 'norEduPersonNIN':
        msg = set_nin_verified(request, user, obj_id, reference)
    elif model_name == 'phone':
        msg = set_phone_verified(request, user, obj_id)
    elif model_name == 'mailAliases':
        msg = set_email_verified(request, user, obj_id)
    else:
        raise NotImplementedError('Unknown validation model_name: {!r}'.format(model_name))

    try:
        request.context.save_dashboard_user(user)
        log.info("Verified {!s} saved for user {!r}.".format(model_name, user))
        verified = {
            'verified': True,
            'verified_timestamp': datetime.utcnow()
        }
        this_verification.update(verified)
        request.db.verifications.update({'_id': this_verification['_id']}, this_verification)
        log.info("Code {!r} ({!s}) marked as verified".format(code, obj_id))
    except UserOutOfSync:
        log.info("Verified {!s} NOT saved for user {!r}. User out of sync.".format(model_name, user))
        raise
    else:
        msg = get_localizer(request).translate(msg)
        request.session.flash(msg.format(obj=obj_id), queue='forms')
        request.stats.count('dashboard/verify_code_completed', 1)
    return obj_id
Exemplo n.º 32
0
    def test_reset_password_unterminates_account(self):
        email = '*****@*****.**'
        # Set up a bunch of faked passwords to make sure they are all revoked
        user = self.userdb_new.get_user_by_mail(email)
        dashboard_user = DashboardUser(data=user.to_dict())
        retrieve_modified_ts(dashboard_user, self.dashboard_db)
        for i in range(7):
            pw = Password(credential_id=ObjectId(),
                          salt=str(i) * 64,
                          application='dashboard_unittest',
                          )
            dashboard_user.passwords.add(pw)
        self.dashboard_db.save(dashboard_user)

        logging.log(logging.DEBUG, "Fetching /profile/security\n\n" + ('-=-' * 30) + "\n\n")

        request = self.set_logged(email=email)
        response = self.testapp.get('/profile/security/')
        form = response.forms['terminate-account-form']

        # Verify the user has eight passwords
        user = self.dashboard_db.get_user_by_mail(email)
        self.assertEqual(len(user.credentials.filter(Password).to_list_of_dicts()), 8)

        logging.log(logging.DEBUG, "Submitting termination request\n\n" + ('-=-' * 30) + "\n\n")

        form_response = form.submit('submit')
        self.assertEqual(form_response.status, '302 Found')
        self.assertIn('authn.example.com', form_response.location)
        with patch('eduiddashboard.vccs.get_vccs_client'):
            from eduiddashboard.vccs import get_vccs_client
            get_vccs_client.return_value = FakeVCCSClient(fake_response={
                'revoke_creds_response': {
                    'version': 1,
                    'success': True,
                },
            })
            with patch('eduiddashboard.views.portal.send_termination_mail'):
                from eduiddashboard.views.portal import send_termination_mail
                send_termination_mail.return_value = None
                store_session_user(request, self.user)
                self.set_logged(email = self.user.mail_addresses.primary.email,
                                extra_session_data = {'reauthn-for-termination': int(time.time())})
                response = self.testapp.get('/profile/account-terminated/')

        # Verify the user doesn't have ANY passwords and IS terminated at this point
        user = self.dashboard_db.get_user_by_mail(email)
        self.assertEqual(user.passwords.count, 0)
        self.assertTrue(user.terminated)

        logging.log(logging.DEBUG, "Password reset of terminated user\n\n" + ('-=-' * 30) + "\n\n")

        # Do a password reset, which should resurrect the terminated user
        hash_code = '123456'
        date = datetime.now(pytz.utc)
        self.db.reset_passwords.insert({
            'email': email,
            'hash_code': hash_code,
            'mechanism': 'email',
            'created_at': date
        })
        response = self.testapp.get('/profile/reset-password/{0}/'.format(hash_code))
        self.assertIn('Please choose a new password for your eduID account', response.text)
        form = response.forms['resetpasswordstep2view-form']
        with patch('eduiddashboard.vccs.get_vccs_client'):
            from eduiddashboard.vccs import get_vccs_client
            get_vccs_client.return_value = FakeVCCSClient()
            form.submit('reset')

        # Verify the user has a password and is NOT terminated again
        user = self.dashboard_db.get_user_by_mail(email)
        self.assertEqual(len(user.credentials.filter(Password).to_list_of_dicts()), 1)
        self.assertFalse(user.terminated)
Exemplo n.º 33
0
def verify_code(request, model_name, code):
    """
    Verify a code and act accordingly to the model_name ('norEduPersonNIN', 'phone', or 'mailAliases').

    This is what turns an unconfirmed NIN/mobile/e-mail into a confirmed one.

    :param request: The HTTP request
    :param model_name: 'norEduPersonNIN', 'phone', or 'mailAliases'
    :param code: The user supplied code
    :type request: pyramid.request.Request
    :return: string of verified data
    """
    assert model_name in ['norEduPersonNIN', 'phone', 'mailAliases']

    this_verification = request.db.verifications.find_one(
        {
            "model_name": model_name,
            "code": code,
        })

    if not this_verification:
        log.error("Could not find verification record for code {!r}, model {!r}".format(code, model_name))
        return

    reference = unicode(this_verification['_id'])
    obj_id = this_verification['obj_id']

    if not obj_id:
        return None

    user = get_session_user(request, legacy_user=False)
    retrieve_modified_ts(user, request.dashboard_userdb)

    assert_error_msg = 'Requesting users ID does not match verifications user ID'
    assert user.user_id == this_verification['user_oid'], assert_error_msg

    if model_name == 'norEduPersonNIN':
        msg = set_nin_verified(request, user, obj_id, reference)
    elif model_name == 'phone':
        msg = set_phone_verified(request, user, obj_id)
    elif model_name == 'mailAliases':
        msg = set_email_verified(request, user, obj_id)
    else:
        raise NotImplementedError('Unknown validation model_name: {!r}'.format(model_name))

    try:
        request.context.save_dashboard_user(user)
        log.info("Verified {!s} saved for user {!r}.".format(model_name, user))
        verified = {
            'verified': True,
            'verified_timestamp': datetime.utcnow()
        }
        this_verification.update(verified)
        request.db.verifications.update({'_id': this_verification['_id']}, this_verification)
        log.info("Code {!r} ({!s}) marked as verified".format(code, obj_id))
    except UserOutOfSync:
        log.info("Verified {!s} NOT saved for user {!r}. User out of sync.".format(model_name, user))
        raise
    else:
        msg = get_localizer(request).translate(msg)
        request.session.flash(msg.format(obj=obj_id), queue='forms')
        request.stats.count('verify_code_completed')
    return obj_id