Beispiel #1
0
    def test_remove_orcid(self):
        for context in self.plugin_contexts:
            proofing_user = ProofingUser(data=self.user_data)
            proofing_user.orcid = None
            context.private_db.save(proofing_user)

            self.assertDictEqual(
                attribute_fetcher(context, proofing_user.user_id),
                {
                    '$unset': {
                        'orcid': None
                    }
                }
            )
Beispiel #2
0
def post_email(user, email, verified, primary):
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug('Trying to save unconfirmed email {!r} '
                             'for user {}'.format(email, proofing_user))

    new_mail = MailAddress.from_dict(
        dict(email=email, created_by='email', verified=False, primary=False))

    try:
        proofing_user.mail_addresses.add(new_mail)
    except DuplicateElementViolation:
        return error_response(message=EmailMsg.dupe)

    try:
        save_and_sync_user(proofing_user)
    except UserOutOfSync:
        current_app.logger.debug('Couldnt save email {} for user {}, '
                                 'data out of sync'.format(
                                     email, proofing_user))
        return error_response(message=CommonMsg.out_of_sync)
    current_app.logger.info('Saved unconfirmed email {!r} '
                            'for user {}'.format(email, proofing_user))
    current_app.stats.count(name='email_save_unconfirmed_email', value=1)

    sent = send_verification_code(email, proofing_user)
    emails = {'emails': proofing_user.mail_addresses.to_list_of_dicts()}
    email_list = EmailListPayload().dump(emails)

    if not sent:
        return success_response(payload=email_list,
                                message=EmailMsg.added_and_throttled)

    current_app.stats.count(name='email_send_verification_code', value=1)

    return success_response(payload=email_list, message=EmailMsg.saved)
Beispiel #3
0
def post_phone(user, number, verified, primary):
    """
    view to add a new phone to the user data of the currently
    logged in user.

    Returns a listing of  all phones for the logged in user.
    """
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug('Trying to save unconfirmed phone number {!r} '
                             'for user {}'.format(number, proofing_user))

    new_phone = PhoneNumber.from_dict(
        dict(number=number, created_by='phone', verified=False, primary=False))
    proofing_user.phone_numbers.add(new_phone)

    try:
        save_and_sync_user(proofing_user)
    except UserOutOfSync:
        current_app.logger.debug('Couldnt save phone number {!r} for user {}, '
                                 'data out of sync'.format(
                                     number, proofing_user))
        return error_response(message=CommonMsg.out_of_sync)

    current_app.logger.info('Saved unconfirmed phone number {!r} '
                            'for user {}'.format(number, proofing_user))
    current_app.stats.count(name='mobile_save_unconfirmed_mobile', value=1)

    send_verification_code(proofing_user, number)
    current_app.stats.count(name='mobile_send_verification_code', value=1)

    phones = {'phones': proofing_user.phone_numbers.to_list_of_dicts()}
    return success_response(payload=phones, message=PhoneMsg.save_success)
Beispiel #4
0
def post_email(user, email, verified, primary):
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug('Trying to save unconfirmed email {!r} '
                             'for user {}'.format(email, proofing_user))

    new_mail = MailAddress(email=email,
                           application='email',
                           verified=False,
                           primary=False)

    try:
        proofing_user.mail_addresses.add(new_mail)
    except DuplicateElementViolation:
        return {'_status': 'error', 'message': 'emails.duplicated'}

    try:
        save_and_sync_user(proofing_user)
    except UserOutOfSync:
        current_app.logger.debug('Couldnt save email {} for user {}, '
                                 'data out of sync'.format(
                                     email, proofing_user))
        return {'_status': 'error', 'message': 'user-out-of-sync'}
    current_app.logger.info('Saved unconfirmed email {!r} '
                            'for user {}'.format(email, proofing_user))
    current_app.stats.count(name='email_save_unconfirmed_email', value=1)

    send_verification_code(email, proofing_user)
    current_app.stats.count(name='email_send_verification_code', value=1)

    emails = {
        'emails': proofing_user.mail_addresses.to_list_of_dicts(),
        'message': 'emails.save-success'
    }
    return EmailListPayload().dump(emails).data
Beispiel #5
0
    def test_remove_orcid(self, mock_request_user_sync):
        mock_request_user_sync.side_effect = self.request_user_sync

        user = self.app.central_userdb.get_user_by_eppn(self.test_user_eppn)
        proofing_user = ProofingUser.from_user(user, self.app.private_userdb)
        proofing_user.orcid = self.orcid_element
        self.request_user_sync(proofing_user)

        with self.session_cookie(self.browser, self.test_user_eppn) as browser:
            response = browser.get('/')
        self.assertEqual(response.status_code, 200)
        response = json.loads(response.data)
        self.assertEqual(response['type'], 'GET_ORCID_SUCCESS')

        csrf_token = response['payload']['csrf_token']
        with self.session_cookie(self.browser, self.test_user_eppn) as browser:
            response = browser.post('/remove',
                                    data=json.dumps({'csrf_token':
                                                     csrf_token}),
                                    content_type=self.content_type_json)
        self.assertEqual(response.status_code, 200)
        response = json.loads(response.data)
        self.assertEqual(response['type'], 'POST_ORCID_REMOVE_SUCCESS')

        user = self.app.central_userdb.get_user_by_eppn(self.test_user_eppn)
        self.assertEqual(user.orcid, None)
Beispiel #6
0
def remove_orcid(user):
    current_app.logger.info('Removing ORCID data for user')
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    proofing_user.orcid = None
    save_and_sync_user(proofing_user)
    current_app.logger.info('ORCID data removed for user')
    return proofing_user.to_dict()
Beispiel #7
0
def remove_orcid(user):
    current_app.logger.info('Removing ORCID data for user')
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    proofing_user.orcid = None
    save_and_sync_user(proofing_user)
    current_app.logger.info('ORCID data removed for user')
    return proofing_user.to_dict()
Beispiel #8
0
def verify(user, code, number):
    """
    view to mark one of the (unverified) phone numbers of the logged in user
    as verified.

    Returns a listing of  all phones for the logged in user.
    """
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug('Trying to save phone number {} as verified'.format(number, proofing_user))

    db = current_app.proofing_statedb
    try:
        state = db.get_state_by_eppn_and_mobile(proofing_user.eppn, number)
        timeout = current_app.config['PHONE_VERIFICATION_TIMEOUT']
        if state.is_expired(timeout):
            current_app.logger.info("Proofing state is expired. Removing the state.")
            current_app.logger.debug("Proofing state: {!r}".format(state))
            current_app.proofing_statedb.remove_state(state)
            return {
                '_status': 'error',
                'message': 'phones.code_invalid_or_expired'
            }
    except DocumentDoesNotExist:
        current_app.logger.info("Could not find proofing state for number {}".format(number))
        return {
            '_status': 'error',
            'message': 'phones.unknown_phone'
        }

    if code == state.verification.verification_code:
        try:
            verify_phone_number(state, proofing_user)
            current_app.logger.info('Phone number successfully verified')
            current_app.logger.debug('Phone number: {}'.format(number))
            phones = {
                    'phones': proofing_user.phone_numbers.to_list_of_dicts(),
                    'message': 'phones.verification-success'
                    }
            return PhoneListPayload().dump(phones).data
        except UserOutOfSync:
            current_app.logger.info('Could not confirm phone number, data out of sync')
            current_app.logger.debug('Phone number: {}'.format(number))
            return {
                '_status': 'error',
                'message': 'user-out-of-sync'
            }
    current_app.logger.info("Invalid verification code")
    current_app.logger.debug("Proofing state: {!r}".format(state))
    return {
        '_status': 'error',
        'message': 'phones.code_invalid_or_expired'
    }
Beispiel #9
0
def post_remove(user, email):
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug('Trying to remove email address {!r} '
                             'from user {}'.format(email, proofing_user))

    emails = proofing_user.mail_addresses.to_list()
    verified_emails = proofing_user.mail_addresses.verified.to_list()

    # Do not let the user remove all mail addresses
    if len(emails) == 1:
        current_app.logger.debug('Cannot remove the last address: {}'.format(email))
        return {
            '_status': 'error',
            'message': 'emails.cannot_remove_unique'
        }

    # Do not let the user remove all verified mail addresses
    if len(verified_emails) == 1 and verified_emails[0].email == email:
        current_app.logger.debug('Cannot remove last verified address: {}'.format(email))
        return {
            '_status': 'error',
            'message': 'emails.cannot_remove_unique_verified'
        }

    try:
        proofing_user.mail_addresses.remove(email)
    except PrimaryElementViolation:
        # Trying to remove the primary mail address, set next verified mail address as primary
        other_verified = [address for address in verified_emails if address.email != email]
        proofing_user.mail_addresses.primary = other_verified[0].email
        # Now remove the unwanted and previous primary mail address
        proofing_user.mail_addresses.remove(email)

    try:
        save_and_sync_user(proofing_user)
    except UserOutOfSync:
        current_app.logger.debug('Could not remove email {} for user, data out of sync'.format(email))
        return {
            '_status': 'error',
            'message': 'user-out-of-sync'
        }

    current_app.logger.info('Email address {} removed'.format(email))
    current_app.stats.count(name='email_remove_success', value=1)

    emails = {
            'emails': proofing_user.mail_addresses.to_list_of_dicts(),
            'message': 'emails.removal-success'
            }
    return EmailListPayload().dump(emails).data
Beispiel #10
0
    def setUp(self):
        am_settings = {
            'WANT_MONGO_URI': True
        }
        super(AttributeFetcherPhoneProofingTests, self).setUp(init_am=True, am_settings=am_settings)
        self.user_data = deepcopy(USER_DATA)
        self.plugin_contexts = [
            phone_plugin_init(self.am_settings),
        ]
        for userdoc in self.amdb._get_all_docs():
            proofing_user = ProofingUser(data=userdoc)
            for context in self.plugin_contexts:
                context.private_db.save(proofing_user, check_sync=False)

        self.maxDiff = None
Beispiel #11
0
    def test_get_orcid(self):
        user = self.app.central_userdb.get_user_by_eppn(self.test_user_eppn)
        proofing_user = ProofingUser.from_user(user, self.app.private_userdb)
        proofing_user.orcid = self.orcid_element
        self.request_user_sync(proofing_user)

        with self.session_cookie(self.browser, self.test_user_eppn) as browser:
            response = browser.get('/')
        self.assertEqual(response.status_code, 200)
        response = json.loads(response.data)
        self.assertEqual(response['type'], 'GET_ORCID_SUCCESS')
        self.assertEqual(response['payload']['orcid']['id'], self.orcid_element.id)
        self.assertEqual(response['payload']['orcid']['name'], self.orcid_element.name)
        self.assertEqual(response['payload']['orcid']['given_name'], self.orcid_element.given_name)
        self.assertEqual(response['payload']['orcid']['family_name'], self.orcid_element.family_name)
Beispiel #12
0
def verify(user, code, email):
    """
    """
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug('Trying to save email address {} as verified'.format(email))

    db = current_app.proofing_statedb
    try:
        state = db.get_state_by_eppn_and_email(proofing_user.eppn, email)
        timeout = current_app.config['EMAIL_VERIFICATION_TIMEOUT']
        if state.is_expired(timeout):
            current_app.logger.info("Verification code is expired. Removing the state")
            current_app.logger.debug("Proofing state: {}".format(state))
            current_app.proofing_statedb.remove_state(state)
            return {
                '_status': 'error',
                'message': 'emails.code_invalid_or_expired'
            }
    except DocumentDoesNotExist:
        current_app.logger.info('Could not find proofing state for email {}'.format(email))
        return {
            '_status': 'error',
            'message': 'emails.unknown_email'
        }

    if code == state.verification.verification_code:
        try:
            verify_mail_address(state, proofing_user)
            current_app.logger.info('Email successfully verified')
            current_app.logger.debug('Email address: {}'.format(email))
            emails = {
                'emails': proofing_user.mail_addresses.to_list_of_dicts(),
                'message': 'emails.verification-success'
            }
            return EmailListPayload().dump(emails).data
        except UserOutOfSync:
            current_app.logger.info('Could not confirm email, data out of sync')
            current_app.logger.debug('Mail address: {}'.format(email))
            return {
                '_status': 'error',
                'message': 'user-out-of-sync'
            }
    current_app.logger.info("Invalid verification code")
    current_app.logger.debug("Email address: {}".format(state.verification.email))
    return {
        '_status': 'error',
        'message': 'emails.code_invalid_or_expired'
    }
Beispiel #13
0
def verify(user, code, number):
    """
    view to mark one of the (unverified) phone numbers of the logged in user
    as verified.

    Returns a listing of  all phones for the logged in user.
    """
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug(
        'Trying to save phone number {} as verified'.format(
            number, proofing_user))

    db = current_app.proofing_statedb
    try:
        state = db.get_state_by_eppn_and_mobile(proofing_user.eppn, number)
        timeout = current_app.config['PHONE_VERIFICATION_TIMEOUT']
        if state.is_expired(timeout):
            current_app.logger.info(
                "Proofing state is expired. Removing the state.")
            current_app.logger.debug("Proofing state: {!r}".format(state))
            current_app.proofing_statedb.remove_state(state)
            return {
                '_status': 'error',
                'message': 'phones.code_invalid_or_expired'
            }
    except DocumentDoesNotExist:
        current_app.logger.info(
            "Could not find proofing state for number {}".format(number))
        return {'_status': 'error', 'message': 'phones.unknown_phone'}

    if code == state.verification.verification_code:
        try:
            verify_phone_number(state, proofing_user)
            current_app.logger.info('Phone number successfully verified')
            current_app.logger.debug('Phone number: {}'.format(number))
            phones = {
                'phones': proofing_user.phone_numbers.to_list_of_dicts(),
                'message': 'phones.verification-success'
            }
            return PhoneListPayload().dump(phones).data
        except UserOutOfSync:
            current_app.logger.info(
                'Could not confirm phone number, data out of sync')
            current_app.logger.debug('Phone number: {}'.format(number))
            return {'_status': 'error', 'message': 'user-out-of-sync'}
    current_app.logger.info("Invalid verification code")
    current_app.logger.debug("Proofing state: {!r}".format(state))
    return {'_status': 'error', 'message': 'phones.code_invalid_or_expired'}
Beispiel #14
0
def verify_link(user):
    """
    Used for verifying an e-mail address when the user clicks the link in the verification mail.
    """
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    code = request.args.get('code')
    email = request.args.get('email')
    if code and email:
        current_app.logger.debug(
            'Trying to save email address {} as verified for user {}'.format(
                email, proofing_user))
        redirect_url = current_app.config.email_verify_redirect_url

        try:
            state = current_app.proofing_statedb.get_state_by_eppn_and_email(
                proofing_user.eppn, email)
            timeout = current_app.config.email_verification_timeout
            if state.is_expired(timeout):
                current_app.logger.info(
                    "Verification code is expired. Removing the state")
                current_app.logger.debug("Proofing state: {}".format(state))
                current_app.proofing_statedb.remove_state(state)
                return redirect_with_msg(redirect_url, EmailMsg.invalid_code)
        except DocumentDoesNotExist:
            current_app.logger.info(
                'Could not find proofing state for email {}'.format(email))
            return redirect_with_msg(redirect_url, EmailMsg.unknown_email)

        if code == state.verification.verification_code:
            try:
                verify_mail_address(state, proofing_user)
                current_app.logger.info('Email successfully verified')
                current_app.logger.debug('Email address: {}'.format(email))
                return redirect_with_msg(redirect_url,
                                         EmailMsg.verify_success,
                                         error=False)

            except UserOutOfSync:
                current_app.logger.info(
                    'Could not confirm email, data out of sync')
                current_app.logger.debug('Mail address: {}'.format(email))
                return redirect_with_msg(redirect_url, CommonMsg.out_of_sync)

        current_app.logger.info("Invalid verification code")
        current_app.logger.debug("Email address: {}".format(
            state.verification.email))
        return redirect_with_msg(redirect_url, EmailMsg.invalid_code)
    abort(400)
Beispiel #15
0
def verify_link(user):
    """
    Used for verifying an e-mail address when the user clicks the link in the verification mail.
    """
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    code = request.args.get('code')
    email = request.args.get('email')
    if code and email:
        current_app.logger.debug('Trying to save email address {} as verified for user {}'.format(email, proofing_user))
        url = urlappend(current_app.config['DASHBOARD_URL'], 'emails')
        scheme, netloc, path, query_string, fragment = urlsplit(url)

        try:
            state = current_app.proofing_statedb.get_state_by_eppn_and_email(proofing_user.eppn, email)
            timeout = current_app.config.get('EMAIL_VERIFICATION_TIMEOUT', 24)
            if state.is_expired(timeout):
                current_app.logger.info("Verification code is expired. Removing the state")
                current_app.logger.debug("Proofing state: {}".format(state))
                current_app.proofing_statedb.remove_state(state)
                new_query_string = urlencode({'msg': ':ERROR:emails.code_invalid_or_expired'})
                url = urlunsplit((scheme, netloc, path, new_query_string, fragment))
                return redirect(url)
        except DocumentDoesNotExist:
            current_app.logger.info('Could not find proofing state for email {}'.format(email))
            new_query_string = urlencode({'msg': ':ERROR:emails.unknown_email'})
            url = urlunsplit((scheme, netloc, path, new_query_string, fragment))
            return redirect(url)

        if code == state.verification.verification_code:
            try:
                verify_mail_address(state, proofing_user)
                current_app.logger.info('Email successfully verified')
                current_app.logger.debug('Email address: {}'.format(email))
                new_query_string = urlencode({'msg': 'emails.verification-success'})
                url = urlunsplit((scheme, netloc, path, new_query_string, fragment))
                return redirect(url)
            except UserOutOfSync:
                current_app.logger.info('Could not confirm email, data out of sync')
                current_app.logger.debug('Mail address: {}'.format(email))
                new_query_string = urlencode({'msg': ':ERROR:user-out-of-sync'})
                url = urlunsplit((scheme, netloc, path, new_query_string, fragment))
                return redirect(url)
        current_app.logger.info("Invalid verification code")
        current_app.logger.debug("Email address: {}".format(state.verification.email))
        new_query_string = urlencode({'msg': ':ERROR:emails.code_invalid_or_expired'})
        url = urlunsplit((scheme, netloc, path, new_query_string, fragment))
        return redirect(url)
    abort(400)
Beispiel #16
0
def post_remove(user, email):
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug('Trying to remove email address {!r} '
                             'from user {}'.format(email, proofing_user))

    emails = proofing_user.mail_addresses.to_list()
    verified_emails = proofing_user.mail_addresses.verified.to_list()

    # Do not let the user remove all mail addresses
    if len(emails) == 1:
        current_app.logger.debug(
            'Cannot remove the last address: {}'.format(email))
        return error_response(message=EmailMsg.cannot_remove_last)

    # Do not let the user remove all verified mail addresses
    if len(verified_emails) == 1 and verified_emails[0].email == email:
        current_app.logger.debug(
            'Cannot remove last verified address: {}'.format(email))
        return error_response(message=EmailMsg.cannot_remove_last_verified)

    try:
        proofing_user.mail_addresses.remove(email)
    except PrimaryElementViolation:
        # Trying to remove the primary mail address, set next verified mail address as primary
        other_verified = [
            address for address in verified_emails if address.email != email
        ]
        proofing_user.mail_addresses.primary = other_verified[0].email
        # Now remove the unwanted and previous primary mail address
        proofing_user.mail_addresses.remove(email)

    try:
        save_and_sync_user(proofing_user)
    except UserOutOfSync:
        current_app.logger.debug(
            'Could not remove email {} for user, data out of sync'.format(
                email))
        return error_response(message=CommonMsg.out_of_sync)

    current_app.logger.info('Email address {} removed'.format(email))
    current_app.stats.count(name='email_remove_success', value=1)

    emails = {'emails': proofing_user.mail_addresses.to_list_of_dicts()}
    email_list = EmailListPayload().dump(emails)
    return success_response(payload=email_list,
                            message=EmailMsg.removal_success)
Beispiel #17
0
    def test_fillup_attributes(self):
        for context in self.plugin_contexts:
            proofing_user = ProofingUser(data=self.user_data)
            context.private_db.save(proofing_user)

            self.assertDictEqual(
                attribute_fetcher(context, proofing_user.user_id),
                {
                    '$set': {
                        'phone': [{
                            'verified': True,
                            'number': '+46700011336',
                            'primary': True
                        }],
                    },
                }
            )
Beispiel #18
0
    def test_fillup_attributes(self):

        for context in self.plugin_contexts:
            proofing_user = ProofingUser(data=self.user_data)
            context.private_db.save(proofing_user)

            self.assertDictEqual(
                attribute_fetcher(context, proofing_user.user_id),
                {
                    '$set': {
                        "givenName": u"Testaren",
                        "surname": u"Testsson",
                        "displayName": u"John",
                        'nins': [{'number': '123456781235', 'verified': True, 'primary': True}],
                    },
                }
            )
Beispiel #19
0
def post_primary(user, number):
    """
    view to mark one of the (verified) phone numbers of the logged in user
    as the primary phone number.

    Returns a listing of  all phones for the logged in user.
    """
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug('Trying to save phone number {!r} as primary '
                             'for user {}'.format(number, proofing_user))

    try:
        phone_element = proofing_user.phone_numbers.find(number)
    except IndexError:
        current_app.logger.debug(
            'Couldnt save phone number {!r} as primary for user'
            ' {}, data out of sync'.format(number, proofing_user))
        return {'_status': 'error', 'message': 'user-out-of-sync'}

    if not phone_element.is_verified:
        current_app.logger.debug(
            'Couldnt save phone number {!r} as primary for user'
            ' {}, phone number unconfirmed'.format(number, proofing_user))
        return {
            '_status': 'error',
            'message': 'phones.unconfirmed_number_not_primary'
        }

    proofing_user.phone_numbers.primary = phone_element.number
    try:
        save_and_sync_user(proofing_user)
    except UserOutOfSync:
        current_app.logger.debug(
            'Couldnt save phone number {!r} as primary for user'
            ' {}, data out of sync'.format(number, proofing_user))
        return {'_status': 'error', 'message': 'user-out-of-sync'}
    current_app.logger.info('Phone number {!r} made primary '
                            'for user {}'.format(number, proofing_user))
    current_app.stats.count(name='mobile_set_primary', value=1)

    phones = {
        'phones': proofing_user.phone_numbers.to_list_of_dicts(),
        'message': 'phones.primary-success'
    }
    return PhoneListPayload().dump(phones).data
Beispiel #20
0
def post_remove(user, number):
    """
    view to remove one of the phone numbers of the logged in user.

    Returns a listing of  all phones for the logged in user.
    """
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug('Trying to remove phone number {!r} '
                             'from user {}'.format(number, proofing_user))

    try:
        proofing_user.phone_numbers.remove(number)
    except PrimaryElementViolation:
        current_app.logger.info('Removing primary phone number')
        current_app.logger.debug('Phone number: {}.'.format(number))
        verified = proofing_user.phone_numbers.verified.to_list()
        new_index = 1 if verified[0].number == number else 0
        proofing_user.phone_numbers.primary = verified[new_index].number
        proofing_user.phone_numbers.remove(number)
    except UserDBValueError:
        current_app.logger.info('Tried to remove a non existing phone number')
        current_app.logger.debug('Phone number: {}.'.format(number))
        return {
            '_status': 'error',
            'message': 'phones.unknown_phone'
        }

    try:
        save_and_sync_user(proofing_user)
    except UserOutOfSync:
        current_app.logger.debug('Couldnt remove phone number {!r} for user'
                                 ' {}, data out of sync'.format(number, proofing_user))
        return {
            '_status': 'error',
            'message': 'user-out-of-sync'
        }
    current_app.logger.info('Phone number {!r} removed '
                            'for user {}'.format(number, proofing_user))
    current_app.stats.count(name='mobile_remove_success', value=1)

    phones = {
            'phones': proofing_user.phone_numbers.to_list_of_dicts(),
            'message': 'phones.removal-success'
            }
    return PhoneListPayload().dump(phones).data
Beispiel #21
0
    def test_existing_user(self):

        for context in self.plugin_contexts:
            proofing_user = ProofingUser(data=self.user_data)
            context.private_db.save(proofing_user)

            self.assertDictEqual(
                attribute_fetcher(context, proofing_user.user_id),
                {
                    '$set': {
                        'mailAliases': [{
                            'email': '*****@*****.**',
                            'verified': True,
                            'primary': True,
                        }],
                    },
                }
            )
Beispiel #22
0
def verify(user, code, email):
    """
    """
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug(
        'Trying to save email address {} as verified'.format(email))

    db = current_app.proofing_statedb
    try:
        state = db.get_state_by_eppn_and_email(proofing_user.eppn, email)
        timeout = current_app.config['EMAIL_VERIFICATION_TIMEOUT']
        if state.is_expired(timeout):
            current_app.logger.info(
                "Verification code is expired. Removing the state")
            current_app.logger.debug("Proofing state: {}".format(state))
            current_app.proofing_statedb.remove_state(state)
            return {
                '_status': 'error',
                'message': 'emails.code_invalid_or_expired'
            }
    except DocumentDoesNotExist:
        current_app.logger.info(
            'Could not find proofing state for email {}'.format(email))
        return {'_status': 'error', 'message': 'emails.unknown_email'}

    if code == state.verification.verification_code:
        try:
            verify_mail_address(state, proofing_user)
            current_app.logger.info('Email successfully verified')
            current_app.logger.debug('Email address: {}'.format(email))
            emails = {
                'emails': proofing_user.mail_addresses.to_list_of_dicts(),
                'message': 'emails.verification-success'
            }
            return EmailListPayload().dump(emails).data
        except UserOutOfSync:
            current_app.logger.info(
                'Could not confirm email, data out of sync')
            current_app.logger.debug('Mail address: {}'.format(email))
            return {'_status': 'error', 'message': 'user-out-of-sync'}
    current_app.logger.info("Invalid verification code")
    current_app.logger.debug("Email address: {}".format(
        state.verification.email))
    return {'_status': 'error', 'message': 'emails.code_invalid_or_expired'}
Beispiel #23
0
def verify(user, code, number):
    """
    view to mark one of the (unverified) phone numbers of the logged in user
    as verified.

    Returns a listing of  all phones for the logged in user.
    """
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug(
        'Trying to save phone number {} as verified'.format(number))

    db = current_app.proofing_statedb
    try:
        state = db.get_state_by_eppn_and_mobile(proofing_user.eppn, number)
        timeout = current_app.config.phone_verification_timeout
        if state.is_expired(timeout):
            current_app.logger.info(
                "Proofing state is expired. Removing the state.")
            current_app.logger.debug("Proofing state: {!r}".format(state))
            current_app.proofing_statedb.remove_state(state)
            return error_response(message=PhoneMsg.code_invalid)
    except DocumentDoesNotExist:
        current_app.logger.info(
            "Could not find proofing state for number {}".format(number))
        return error_response(message=PhoneMsg.unknown_phone)

    if code == state.verification.verification_code:
        try:
            verify_phone_number(state, proofing_user)
            current_app.logger.info('Phone number successfully verified')
            current_app.logger.debug('Phone number: {}'.format(number))
            phones = {
                'phones': proofing_user.phone_numbers.to_list_of_dicts(),
            }
            return success_response(payload=phones,
                                    message=PhoneMsg.verify_success)
        except UserOutOfSync:
            current_app.logger.info(
                'Could not confirm phone number, data out of sync')
            current_app.logger.debug('Phone number: {}'.format(number))
            return error_response(message=CommonMsg.out_of_sync)
    current_app.logger.info("Invalid verification code")
    current_app.logger.debug("Proofing state: {!r}".format(state))
    return error_response(message=PhoneMsg.code_invalid)
Beispiel #24
0
    def test_get_orcid(self):
        user = self.app.central_userdb.get_user_by_eppn(self.test_user_eppn)
        proofing_user = ProofingUser.from_user(user, self.app.private_userdb)
        proofing_user.orcid = self.orcid_element
        self.request_user_sync(proofing_user)

        with self.session_cookie(self.browser, self.test_user_eppn) as browser:
            response = browser.get('/')
        self.assertEqual(response.status_code, 200)
        response = json.loads(response.data)
        self.assertEqual(response['type'], 'GET_ORCID_SUCCESS')
        self.assertEqual(response['payload']['orcid']['id'],
                         self.orcid_element.id)
        self.assertEqual(response['payload']['orcid']['name'],
                         self.orcid_element.name)
        self.assertEqual(response['payload']['orcid']['given_name'],
                         self.orcid_element.given_name)
        self.assertEqual(response['payload']['orcid']['family_name'],
                         self.orcid_element.family_name)
Beispiel #25
0
def verify(user, code, email):
    """
    """
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug(
        'Trying to save email address {} as verified'.format(email))

    db = current_app.proofing_statedb
    try:
        state = db.get_state_by_eppn_and_email(proofing_user.eppn, email)
        timeout = current_app.config.email_verification_timeout
        if state.is_expired(timeout):
            current_app.logger.info(
                "Verification code is expired. Removing the state")
            current_app.logger.debug("Proofing state: {}".format(state))
            current_app.proofing_statedb.remove_state(state)
            return error_response(message=EmailMsg.invalid_code)
    except DocumentDoesNotExist:
        current_app.logger.info(
            'Could not find proofing state for email {}'.format(email))
        return error_response(message=EmailMsg.unknown_email)

    if code == state.verification.verification_code:
        try:
            verify_mail_address(state, proofing_user)
            current_app.logger.info('Email successfully verified')
            current_app.logger.debug('Email address: {}'.format(email))
            emails = {
                'emails': proofing_user.mail_addresses.to_list_of_dicts(),
            }
            email_list = EmailListPayload().dump(emails)
            return success_response(payload=email_list,
                                    message=EmailMsg.verify_success)
        except UserOutOfSync:
            current_app.logger.info(
                'Could not confirm email, data out of sync')
            current_app.logger.debug('Mail address: {}'.format(email))
            return error_response(message=CommonMsg.out_of_sync)
    current_app.logger.info("Invalid verification code")
    current_app.logger.debug("Email address: {}".format(
        state.verification.email))
    return error_response(message=EmailMsg.invalid_code)
Beispiel #26
0
def post_primary(user, number):
    """
    view to mark one of the (verified) phone numbers of the logged in user
    as the primary phone number.

    Returns a listing of all phones for the logged in user.
    """
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug('Trying to save phone number {} as primary'.format(number))

    phone_element = proofing_user.phone_numbers.find(number)
    if not phone_element:
        current_app.logger.debug('Could not save phone number {} as primary, data out of sync'.format(number))
        return {
            '_status': 'error',
            'message': 'user-out-of-sync'
        }

    if not phone_element.is_verified:
        current_app.logger.debug('Could not save phone number {} as primary, phone number unconfirmed'.format(number))
        return {
            '_status': 'error',
            'message': 'phones.unconfirmed_number_not_primary'
        }

    proofing_user.phone_numbers.primary = phone_element.number
    try:
        save_and_sync_user(proofing_user)
    except UserOutOfSync:
        current_app.logger.debug('Could not save phone number {} as primary, data out of sync'.format(number))
        return {
            '_status': 'error',
            'message': 'user-out-of-sync'
        }
    current_app.logger.info('Phone number {} made primary'.format(number))
    current_app.stats.count(name='mobile_set_primary', value=1)

    phones = {
            'phones': proofing_user.phone_numbers.to_list_of_dicts(),
            'message': 'phones.primary-success'
            }
    return PhoneListPayload().dump(phones).data
Beispiel #27
0
def post_primary(user, email):
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug('Trying to save email address {!r} as primary '
                             'for user {}'.format(email, proofing_user))

    try:
        mail = proofing_user.mail_addresses.find(email)
    except IndexError:
        current_app.logger.debug('Couldnt save email {!r} as primary for user'
                                 ' {}, data out of sync'.format(email, proofing_user))
        return {
            '_status': 'error',
            'message': 'user-out-of-sync'
        }

    if not mail.is_verified:
        current_app.logger.debug('Couldnt save email {!r} as primary for user'
                                 ' {}, email unconfirmed'.format(email, proofing_user))
        return {
            '_status': 'error',
            'message': 'emails.unconfirmed_address_not_primary'
        }

    proofing_user.mail_addresses.primary = mail.email
    try:
        save_and_sync_user(proofing_user)
    except UserOutOfSync:
        current_app.logger.debug('Couldnt save email {!r} as primary for user'
                                 ' {}, data out of sync'.format(email, proofing_user))
        return {
            '_status': 'error',
            'message': 'user-out-of-sync'
        }
    current_app.logger.info('Email address {!r} made primary '
                            'for user {}'.format(email, proofing_user))
    current_app.stats.count(name='email_set_primary', value=1)

    emails = {
            'emails': proofing_user.mail_addresses.to_list_of_dicts(),
            'message': 'emails.primary-success'
            }
    return EmailListPayload().dump(emails).data
Beispiel #28
0
    def convert_and_remove_norEduPersonNIN(self):
        self.user_data.update({'norEduPersonNIN': '123456781235'})
        del self.user_data['nins']
        for context in self.plugin_contexts:
            proofing_user = ProofingUser(data=self.user_data)
            context.private_db.save(proofing_user)

            actual_update = attribute_fetcher(context, proofing_user.user_id)
            expected_update = {
                '$set': {
                    'nins': [{'number': '123456781235', 'verified': True, 'primary': True}],
                },
                '$unset': {
                    'norEduPersonNIN': None
                }
            }

            self.assertDictEqual(
                actual_update,
                expected_update
            )
Beispiel #29
0
def post_remove(user, number):
    """
    view to remove one of the phone numbers of the logged in user.

    Returns a listing of  all phones for the logged in user.
    """
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug('Trying to remove phone number {!r} '
                             'from user {}'.format(number, proofing_user))

    try:
        proofing_user.phone_numbers.remove(number)
    except PrimaryElementViolation:
        current_app.logger.info('Removing primary phone number')
        current_app.logger.debug('Phone number: {}.'.format(number))
        verified = proofing_user.phone_numbers.verified.to_list()
        new_index = 1 if verified[0].number == number else 0
        proofing_user.phone_numbers.primary = verified[new_index].number
        proofing_user.phone_numbers.remove(number)
    except UserDBValueError:
        current_app.logger.info('Tried to remove a non existing phone number')
        current_app.logger.debug('Phone number: {}.'.format(number))
        return {'_status': 'error', 'message': 'phones.unknown_phone'}

    try:
        save_and_sync_user(proofing_user)
    except UserOutOfSync:
        current_app.logger.debug('Couldnt remove phone number {!r} for user'
                                 ' {}, data out of sync'.format(
                                     number, proofing_user))
        return {'_status': 'error', 'message': 'user-out-of-sync'}
    current_app.logger.info('Phone number {!r} removed '
                            'for user {}'.format(number, proofing_user))
    current_app.stats.count(name='mobile_remove_success', value=1)

    phones = {
        'phones': proofing_user.phone_numbers.to_list_of_dicts(),
        'message': 'phones.removal-success'
    }
    return PhoneListPayload().dump(phones).data
Beispiel #30
0
    def test_fillup_attributes(self):
        self.user_data = {
            'givenName': 'Testaren',
            'surname': 'Testsson',
            'preferredLanguage': 'sv',
            'eduPersonPrincipalName': 'test-test',
            'displayName': 'John',
            'mailAliases': [{
                'email': '*****@*****.**',
                'verified': True,
                'primary': True
            }],
            'mobile': [{
                'verified': True,
                'mobile': '+46700011336',
                'primary': True
            }],
            'passwords': [{
                'id': bson.ObjectId('112345678901234567890123'),
                'salt': '$NDNv1H1$9c810d852430b62a9a7c6159d5d64c41c3831846f81b6799b54e1e8922f11545$32$32$',
            }],
            'nins': [{'number': '123456781235', 'verified': True, 'primary': True}],
        }

        for context in self.plugin_contexts:
            proofing_user = ProofingUser(data=self.user_data)
            context.private_db.save(proofing_user)

            self.assertDictEqual(
                attribute_fetcher(context, proofing_user.user_id),
                {
                    '$set': {
                        'mailAliases': [{
                            'email': '*****@*****.**',
                            'verified': True,
                            'primary': True
                        }],
                    },
                }
            )
Beispiel #31
0
    def test_existing_user(self):
        for context in self.plugin_contexts:
            proofing_user = ProofingUser(data=self.user_data)
            context.private_db.save(proofing_user)

            self.assertDictEqual(
                attribute_fetcher(context, proofing_user.user_id),
                {
                    '$set': {
                        'orcid': {
                            'oidc_authz': {
                                'token_type': 'bearer',
                                'refresh_token': 'a_refresh_token',
                                'access_token': 'an_access_token',
                                'id_token': {
                                        'nonce': 'a_nonce',
                                        'sub': 'sub_id',
                                        'iss': 'https://issuer.example.org',
                                        'created_by': 'orcid',
                                        'exp': 1526890816,
                                        'auth_time': 1526890214,
                                        'iat': 1526890216,
                                        'aud': [
                                                'APP-YIAD0N1L4B3Z3W9Q'
                                        ]
                                },
                                'expires_in': 631138518,
                                'created_by': 'orcid'
                            },
                            'given_name': 'Testaren',
                            'family_name': 'Testsson',
                            'name': None,
                            'verified': True,
                            'id': 'orcid_unique_id',
                            'created_by': 'orcid'
                        }
                    },
                }
            )
Beispiel #32
0
def post_primary(user, email):
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug('Trying to save email address {!r} as primary '
                             'for user {}'.format(email, proofing_user))

    try:
        mail = proofing_user.mail_addresses.find(email)
    except IndexError:
        current_app.logger.debug('Couldnt save email {!r} as primary for user'
                                 ' {}, data out of sync'.format(
                                     email, proofing_user))
        return {'_status': 'error', 'message': 'user-out-of-sync'}

    if not mail.is_verified:
        current_app.logger.debug('Couldnt save email {!r} as primary for user'
                                 ' {}, email unconfirmed'.format(
                                     email, proofing_user))
        return {
            '_status': 'error',
            'message': 'emails.unconfirmed_address_not_primary'
        }

    proofing_user.mail_addresses.primary = mail.email
    try:
        save_and_sync_user(proofing_user)
    except UserOutOfSync:
        current_app.logger.debug('Couldnt save email {!r} as primary for user'
                                 ' {}, data out of sync'.format(
                                     email, proofing_user))
        return {'_status': 'error', 'message': 'user-out-of-sync'}
    current_app.logger.info('Email address {!r} made primary '
                            'for user {}'.format(email, proofing_user))
    current_app.stats.count(name='email_set_primary', value=1)

    emails = {
        'emails': proofing_user.mail_addresses.to_list_of_dicts(),
        'message': 'emails.primary-success'
    }
    return EmailListPayload().dump(emails).data
Beispiel #33
0
def post_remove(user, email):
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug('Trying to remove email address {!r} '
                             'from user {}'.format(email, proofing_user))

    emails = proofing_user.mail_addresses.to_list()
    if len(emails) == 1:
        msg = "Cannot remove unique address: {!r}".format(email)
        current_app.logger.debug(msg)
        return {'_status': 'error', 'message': 'emails.cannot_remove_unique'}

    try:
        proofing_user.mail_addresses.remove(email)
    except PrimaryElementViolation:
        new_index = 1 if emails[0].email == email else 0
        proofing_user.mail_addresses.primary = emails[new_index].email
        proofing_user.mail_addresses.remove(email)

    try:
        save_and_sync_user(proofing_user)
    except UserOutOfSync:
        current_app.logger.debug('Couldnt remove email {!r} for user'
                                 ' {}, data out of sync'.format(
                                     email, proofing_user))
        return {'_status': 'error', 'message': 'user-out-of-sync'}

    except PrimaryElementViolation:
        return {'_status': 'error', 'message': 'emails.cannot_remove_primary'}

    current_app.logger.info('Email address {!r} removed '
                            'for user {}'.format(email, proofing_user))
    current_app.stats.count(name='email_remove_success', value=1)

    emails = {
        'emails': proofing_user.mail_addresses.to_list_of_dicts(),
        'message': 'emails.removal-success'
    }
    return EmailListPayload().dump(emails).data
Beispiel #34
0
    def test_remove_orcid(self, mock_request_user_sync):
        mock_request_user_sync.side_effect = self.request_user_sync

        user = self.app.central_userdb.get_user_by_eppn(self.test_user_eppn)
        proofing_user = ProofingUser.from_user(user, self.app.private_userdb)
        proofing_user.orcid = self.orcid_element
        self.request_user_sync(proofing_user)

        with self.session_cookie(self.browser, self.test_user_eppn) as browser:
            response = browser.get('/')
        self.assertEqual(response.status_code, 200)
        response = json.loads(response.data)
        self.assertEqual(response['type'], 'GET_ORCID_SUCCESS')

        csrf_token = response['payload']['csrf_token']
        with self.session_cookie(self.browser, self.test_user_eppn) as browser:
            response = browser.post('/remove', data={'csrf_token': csrf_token})
        self.assertEqual(response.status_code, 200)
        response = json.loads(response.data)
        self.assertEqual(response['type'], 'POST_ORCID_REMOVE_SUCCESS')

        user = self.app.central_userdb.get_user_by_eppn(self.test_user_eppn)
        self.assertEqual(user.orcid, None)
Beispiel #35
0
def post_phone(user, number, verified, primary):
    """
    view to add a new phone to the user data of the currently
    logged in user.

    Returns a listing of  all phones for the logged in user.
    """
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug('Trying to save unconfirmed phone number {!r} '
                             'for user {}'.format(number, proofing_user))

    new_phone = PhoneNumber(number=number,
                            application='phone',
                            verified=False,
                            primary=False)
    proofing_user.phone_numbers.add(new_phone)

    try:
        save_and_sync_user(proofing_user)
    except UserOutOfSync:
        current_app.logger.debug('Couldnt save phone number {!r} for user {}, '
                                 'data out of sync'.format(
                                     number, proofing_user))
        return {'_status': 'error', 'message': 'user-out-of-sync'}

    current_app.logger.info('Saved unconfirmed phone number {!r} '
                            'for user {}'.format(number, proofing_user))
    current_app.stats.count(name='mobile_save_unconfirmed_mobile', value=1)

    send_verification_code(proofing_user, number)
    current_app.stats.count(name='mobile_send_verification_code', value=1)

    phones = {
        'phones': proofing_user.phone_numbers.to_list_of_dicts(),
        'message': 'phones.save-success'
    }
    return PhoneListPayload().dump(phones).data
Beispiel #36
0
def post_phone(user, number, verified, primary):
    """
    view to add a new phone to the user data of the currently
    logged in user.

    Returns a listing of  all phones for the logged in user.
    """
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug('Trying to save unconfirmed phone number {!r} '
                             'for user {}'.format(number, proofing_user))

    new_phone = PhoneNumber(number=number, application='phone',
                            verified=False, primary=False)
    proofing_user.phone_numbers.add(new_phone)

    try:
        save_and_sync_user(proofing_user)
    except UserOutOfSync:
        current_app.logger.debug('Couldnt save phone number {!r} for user {}, '
                                 'data out of sync'.format(number, proofing_user))
        return {
            '_status': 'error',
            'message': 'user-out-of-sync'
        }

    current_app.logger.info('Saved unconfirmed phone number {!r} '
                            'for user {}'.format(number, proofing_user))
    current_app.stats.count(name='mobile_save_unconfirmed_mobile', value=1)

    send_verification_code(proofing_user, number)
    current_app.stats.count(name='mobile_send_verification_code', value=1)

    phones = {
            'phones': proofing_user.phone_numbers.to_list_of_dicts(),
            'message': 'phones.save-success'
            }
    return PhoneListPayload().dump(phones).data
Beispiel #37
0
def post_email(user, email, verified, primary):
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug('Trying to save unconfirmed email {!r} '
                             'for user {}'.format(email, proofing_user))

    new_mail = MailAddress(email=email, application='email',
                           verified=False, primary=False)

    try:
        proofing_user.mail_addresses.add(new_mail)
    except DuplicateElementViolation:
        return {
            '_status': 'error',
            'message':  'emails.duplicated'
        }

    try:
        save_and_sync_user(proofing_user)
    except UserOutOfSync:
        current_app.logger.debug('Couldnt save email {} for user {}, '
                                 'data out of sync'.format(email, proofing_user))
        return {
            '_status': 'error',
            'message': 'user-out-of-sync'
        }
    current_app.logger.info('Saved unconfirmed email {!r} '
                            'for user {}'.format(email, proofing_user))
    current_app.stats.count(name='email_save_unconfirmed_email', value=1)

    send_verification_code(email, proofing_user)
    current_app.stats.count(name='email_send_verification_code', value=1)

    emails = {
            'emails': proofing_user.mail_addresses.to_list_of_dicts(),
            'message': 'emails.save-success'
            }
    return EmailListPayload().dump(emails).data
Beispiel #38
0
def post_primary(user, number):
    """
    view to mark one of the (verified) phone numbers of the logged in user
    as the primary phone number.

    Returns a listing of all phones for the logged in user.
    """
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug(
        'Trying to save phone number {} as primary'.format(number))

    phone_element = proofing_user.phone_numbers.find(number)
    if not phone_element:
        current_app.logger.debug(
            'Could not save phone number {} as primary, data out of sync'.
            format(number))
        return error_response(message=CommonMsg.out_of_sync)

    if not phone_element.is_verified:
        current_app.logger.debug(
            'Could not save phone number {} as primary, phone number unconfirmed'
            .format(number))
        return error_response(message=PhoneMsg.unconfirmed_primary)

    proofing_user.phone_numbers.primary = phone_element.number
    try:
        save_and_sync_user(proofing_user)
    except UserOutOfSync:
        current_app.logger.debug(
            'Could not save phone number {} as primary, data out of sync'.
            format(number))
        return error_response(message=CommonMsg.out_of_sync)
    current_app.logger.info('Phone number {} made primary'.format(number))
    current_app.stats.count(name='mobile_set_primary', value=1)

    phones = {'phones': proofing_user.phone_numbers.to_list_of_dicts()}
    return success_response(payload=phones, message=PhoneMsg.primary_success)
Beispiel #39
0
def post_primary(user, email):
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    current_app.logger.debug(
        'Trying to save email address {!r} as primary for user {}'.format(
            email, proofing_user))

    try:
        mail = proofing_user.mail_addresses.find(email)
    except IndexError:
        current_app.logger.debug(
            'Couldnt save email {!r} as primary for user {}, data out of sync'.
            format(email, proofing_user))
        return error_response(message=CommonMsg.out_of_sync)

    if not mail.is_verified:
        current_app.logger.debug(
            'Couldnt save email {!r} as primary for user {}, email unconfirmed'
            .format(email, proofing_user))
        return error_response(message=EmailMsg.unconfirmed_not_primary)

    proofing_user.mail_addresses.primary = mail.email
    try:
        save_and_sync_user(proofing_user)
    except UserOutOfSync:
        current_app.logger.debug('Couldnt save email {!r} as primary for user'
                                 ' {}, data out of sync'.format(
                                     email, proofing_user))
        return error_response(message=CommonMsg.out_of_sync)
    current_app.logger.info('Email address {!r} made primary '
                            'for user {}'.format(email, proofing_user))
    current_app.stats.count(name='email_set_primary', value=1)

    emails = {'emails': proofing_user.mail_addresses.to_list_of_dicts()}
    email_list = EmailListPayload().dump(emails)
    return success_response(payload=email_list,
                            message=EmailMsg.success_primary)
Beispiel #40
0
def authorization_response(user):
    # Redirect url for user feedback
    redirect_url = current_app.config.orcid_verify_redirect_url

    current_app.stats.count(name='authn_response')

    # parse authentication response
    query_string = request.query_string.decode('utf-8')
    current_app.logger.debug('query_string: {!s}'.format(query_string))

    authn_resp = current_app.oidc_client.parse_response(AuthorizationResponse, info=query_string, sformat='urlencoded')
    current_app.logger.debug('Authorization response received: {!s}'.format(authn_resp))

    if authn_resp.get('error'):
        current_app.logger.error(
            'AuthorizationError from {}: {} - {} ({})'.format(
                request.host, authn_resp['error'], authn_resp.get('error_message'), authn_resp.get('error_description')
            )
        )
        return redirect_with_msg(redirect_url, OrcidMsg.authz_error)

    user_oidc_state = authn_resp['state']
    proofing_state = current_app.proofing_statedb.get_state_by_oidc_state(user_oidc_state, raise_on_missing=False)
    if not proofing_state:
        current_app.logger.error('The \'state\' parameter ({!s}) does not match a user state.'.format(user_oidc_state))
        return redirect_with_msg(redirect_url, OrcidMsg.no_state)

    # do token request
    args = {
        'code': authn_resp['code'],
        'redirect_uri': url_for('orcid.authorization_response', _external=True),
    }
    current_app.logger.debug('Trying to do token request: {!s}'.format(args))
    token_resp = current_app.oidc_client.do_access_token_request(
        scope='openid', state=authn_resp['state'], request_args=args, authn_method='client_secret_basic'
    )
    current_app.logger.debug('token response received: {!s}'.format(token_resp))
    id_token = token_resp['id_token']
    if id_token['nonce'] != proofing_state.nonce:
        current_app.logger.error('The \'nonce\' parameter does not match for user')
        return redirect_with_msg(redirect_url, OrcidMsg.unknown_nonce)

    current_app.logger.info('ORCID authorized for user')

    # do userinfo request
    current_app.logger.debug('Trying to do userinfo request:')
    userinfo = current_app.oidc_client.do_user_info_request(
        method=current_app.config.userinfo_endpoint_method, state=authn_resp['state']
    )
    current_app.logger.debug('userinfo received: {!s}'.format(userinfo))
    if userinfo['sub'] != id_token['sub']:
        current_app.logger.error(
            'The \'sub\' of userinfo does not match \'sub\' of ID Token for user {!s}.'.format(proofing_state.eppn)
        )
        return redirect_with_msg(redirect_url, OrcidMsg.sub_mismatch)

    # Save orcid and oidc data to user
    current_app.logger.info('Saving ORCID data for user')
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    oidc_id_token = OidcIdToken.from_dict(
        dict(
            iss=id_token['iss'],
            sub=id_token['sub'],
            aud=id_token['aud'],
            exp=id_token['exp'],
            iat=id_token['iat'],
            nonce=id_token['nonce'],
            auth_time=id_token['auth_time'],
            created_by='orcid',
        )
    )
    oidc_authz = OidcAuthorization.from_dict(
        dict(
            access_token=token_resp['access_token'],
            token_type=token_resp['token_type'],
            id_token=oidc_id_token,
            expires_in=token_resp['expires_in'],
            refresh_token=token_resp['refresh_token'],
            created_by='orcid',
        )
    )
    orcid_element = Orcid.from_dict(
        dict(
            id=userinfo['id'],
            name=userinfo['name'],
            given_name=userinfo['given_name'],
            family_name=userinfo['family_name'],
            verified=True,
            oidc_authz=oidc_authz,
            created_by='orcid',
        )
    )
    orcid_proofing = OrcidProofing(
        proofing_user,
        created_by='orcid',
        orcid=orcid_element.id,
        issuer=orcid_element.oidc_authz.id_token.iss,
        audience=orcid_element.oidc_authz.id_token.aud,
        proofing_method='oidc',
        proofing_version='2018v1',
    )

    if current_app.proofing_log.save(orcid_proofing):
        current_app.logger.info('ORCID proofing data saved to log')
        proofing_user.orcid = orcid_element
        save_and_sync_user(proofing_user)
        current_app.logger.info('ORCID proofing data saved to user')
        message_args = dict(msg=OrcidMsg.authz_success, error=False)
    else:
        current_app.logger.info('ORCID proofing data NOT saved, failed to save proofing log')
        message_args = dict(msg=CommonMsg.temp_problem)

    # Clean up
    current_app.logger.info('Removing proofing state')
    current_app.proofing_statedb.remove_state(proofing_state)
    return redirect_with_msg(redirect_url, **message_args)
Beispiel #41
0
def authorization_response():
    # parse authentication response
    query_string = request.query_string.decode('utf-8')
    current_app.logger.debug('query_string: {!s}'.format(query_string))
    authn_resp = current_app.oidc_client.parse_response(AuthorizationResponse,
                                                        info=query_string,
                                                        sformat='urlencoded')
    current_app.logger.debug(
        'Authorization response received: {!s}'.format(authn_resp))

    if authn_resp.get('error'):
        current_app.logger.error(
            'AuthorizationError from {}: {} - {} ({})'.format(
                request.host, authn_resp['error'],
                authn_resp.get('error_message'), authn_resp.get('error_uri')))
        current_app.stats.count(name='authn_response_op_error')
        return make_response('OK', 200)

    user_oidc_state = authn_resp['state']
    proofing_state = current_app.proofing_statedb.get_state_by_oidc_state(
        user_oidc_state)
    if not proofing_state:
        msg = 'The \'state\' parameter ({!s}) does not match a user state.'.format(
            user_oidc_state)
        current_app.logger.error(msg)
        current_app.stats.count(name='authn_response_proofing_state_missing')
        return make_response('OK', 200)
    current_app.logger.debug('Proofing state {!s} for user {!s} found'.format(
        proofing_state.state, proofing_state.eppn))

    # Check if the token from the authn response matches the token we created when making the auth request
    authorization_header = request.headers.get('Authorization')
    if authorization_header != 'Bearer {}'.format(proofing_state.token):
        current_app.logger.error(
            'The authorization token ({!s}) did not match the expected'.format(
                authorization_header))
        current_app.stats.count(name='authn_response_authn_failure')
        return make_response('FORBIDDEN', 403)

    # TODO: We should save the auth response code to the proofing state to be able to continue a failed attempt
    # do token request
    args = {
        'code':
        authn_resp['code'],
        'redirect_uri':
        url_for('oidc_proofing.authorization_response', _external=True)
    }
    current_app.logger.debug('Trying to do token request: {!s}'.format(args))
    # TODO: What should be saved from the token response and where?
    token_resp = current_app.oidc_client.do_access_token_request(
        scope='openid',
        state=authn_resp['state'],
        request_args=args,
        authn_method='client_secret_basic')
    current_app.logger.debug(
        'token response received: {!s}'.format(token_resp))
    id_token = token_resp['id_token']
    if id_token['nonce'] != proofing_state.nonce:
        current_app.logger.error(
            'The \'nonce\' parameter does not match for user {!s}.'.format(
                proofing_state.eppn))
        current_app.stats.count(name='authn_response_token_request_failure')
        return make_response('OK', 200)
    current_app.stats.count(name='authn_response_token_request_success')

    # do userinfo request
    current_app.logger.debug('Trying to do userinfo request:')
    # TODO: Do we need to save anything else from the userinfo response
    userinfo = current_app.oidc_client.do_user_info_request(
        method=current_app.config.userinfo_endpoint_method,
        state=authn_resp['state'])
    current_app.logger.debug('userinfo received: {!s}'.format(userinfo))
    if userinfo['sub'] != id_token['sub']:
        current_app.logger.error(
            'The \'sub\' of userinfo does not match \'sub\' of ID Token for user {!s}.'
            .format(proofing_state.eppn))
        current_app.stats.count(name='authn_response_userinfo_request_failure')
        return make_response('OK', 200)
    current_app.stats.count(name='authn_response_userinfo_request_success')

    # TODO: Break out in parts to be able to continue the proofing process after a successful authorization response
    # TODO: even if the token request, userinfo request or something internal fails
    am_user = current_app.central_userdb.get_user_by_eppn(proofing_state.eppn)
    user = ProofingUser.from_user(am_user, current_app.private_userdb)

    try:
        # Handle userinfo differently depending on data in userinfo
        if userinfo.get('identity'):
            current_app.logger.info(
                'Handling userinfo as generic seleg vetting for user {}'.
                format(user))
            current_app.stats.count(name='seleg.authn_response_received')
            helpers.handle_seleg_userinfo(user, proofing_state, userinfo)
        elif userinfo.get('results'):
            current_app.logger.info(
                'Handling userinfo as freja vetting for user {}'.format(user))
            current_app.stats.count(name='freja.authn_response_received')
            helpers.handle_freja_eid_userinfo(user, proofing_state, userinfo)
    except (TaskFailed, KeyError) as e:
        current_app.logger.error(
            'Failed to handle userinfo for user {}'.format(user))
        current_app.logger.error('Exception: {}'.format(e))
        current_app.stats.count(name='authn_response_handling_failure')
    finally:
        # Remove users proofing state
        current_app.proofing_statedb.remove_state(proofing_state)
    return make_response('OK', 200)
Beispiel #42
0
def authorization_response(user):
    # Redirect url for user feedback
    url = urlappend(current_app.config['DASHBOARD_URL'], 'accountlinking')
    scheme, netloc, path, query_string, fragment = urlsplit(url)

    current_app.stats.count(name='authn_response')

    # parse authentication response
    query_string = request.query_string.decode('utf-8')
    current_app.logger.debug('query_string: {!s}'.format(query_string))

    authn_resp = current_app.oidc_client.parse_response(AuthorizationResponse, info=query_string,
                                                        sformat='urlencoded')
    current_app.logger.debug('Authorization response received: {!s}'.format(authn_resp))

    if authn_resp.get('error'):
        current_app.logger.error('AuthorizationError {!s} - {!s} ({!s})'.format(request.host, authn_resp['error'],
                                                                                authn_resp.get('error_message'),
                                                                                authn_resp.get('error_description')))
        new_query_string = urlencode({'msg': ':ERROR:orc.authorization_fail'})
        url = urlunsplit((scheme, netloc, path, new_query_string, fragment))
        return redirect(url)

    user_oidc_state = authn_resp['state']
    proofing_state = current_app.proofing_statedb.get_state_by_oidc_state(user_oidc_state, raise_on_missing=False)
    if not proofing_state:
        current_app.logger.error('The \'state\' parameter ({!s}) does not match a user state.'.format(user_oidc_state))
        new_query_string = urlencode({'msg': ':ERROR:orc.unknown_state'})
        url = urlunsplit((scheme, netloc, path, new_query_string, fragment))
        return redirect(url)

    # do token request
    args = {
        'code': authn_resp['code'],
        'redirect_uri': url_for('orcid.authorization_response', _external=True),
    }
    current_app.logger.debug('Trying to do token request: {!s}'.format(args))
    token_resp = current_app.oidc_client.do_access_token_request(scope='openid', state=authn_resp['state'],
                                                                 request_args=args,
                                                                 authn_method='client_secret_basic')
    current_app.logger.debug('token response received: {!s}'.format(token_resp))
    id_token = token_resp['id_token']
    if id_token['nonce'] != proofing_state.nonce:
        current_app.logger.error('The \'nonce\' parameter does not match for user')
        new_query_string = urlencode({'msg': ':ERROR:orc.unknown_nonce'})
        url = urlunsplit((scheme, netloc, path, new_query_string, fragment))
        return redirect(url)
    current_app.logger.info('ORCID authorized for user')

    # do userinfo request
    current_app.logger.debug('Trying to do userinfo request:')
    userinfo = current_app.oidc_client.do_user_info_request(method=current_app.config['USERINFO_ENDPOINT_METHOD'],
                                                            state=authn_resp['state'])
    current_app.logger.debug('userinfo received: {!s}'.format(userinfo))
    if userinfo['sub'] != id_token['sub']:
        current_app.logger.error('The \'sub\' of userinfo does not match \'sub\' of ID Token for user {!s}.'.format(
            proofing_state.eppn))
        new_query_string = urlencode({'msg': ':ERROR:orc.sub_mismatch'})
        url = urlunsplit((scheme, netloc, path, new_query_string, fragment))
        return redirect(url)

    # Save orcid and oidc data to user
    current_app.logger.info('Saving ORCID data for user')
    proofing_user = ProofingUser.from_user(user, current_app.private_userdb)
    oidc_id_token = OidcIdToken(iss=id_token['iss'], sub=id_token['sub'], aud=id_token['aud'], exp=id_token['exp'],
                                iat=id_token['iat'], nonce=id_token['nonce'], auth_time=id_token['auth_time'],
                                application='orcid')
    oidc_authz = OidcAuthorization(access_token=token_resp['access_token'], token_type=token_resp['token_type'],
                                   id_token=oidc_id_token, expires_in=token_resp['expires_in'],
                                   refresh_token=token_resp['refresh_token'], application='orcid')
    orcid_element = Orcid(id=userinfo['id'], name=userinfo['name'], given_name=userinfo['given_name'],
                          family_name=userinfo['family_name'], verified=True, oidc_authz=oidc_authz,
                          application='orcid')
    orcid_proofing = OrcidProofing(proofing_user, created_by='orcid', orcid=orcid_element.id,
                                   issuer=orcid_element.oidc_authz.id_token.iss,
                                   audience=orcid_element.oidc_authz.id_token.aud, proofing_method='oidc',
                                   proofing_version='2018v1')

    if current_app.proofing_log.save(orcid_proofing):
        current_app.logger.info('ORCID proofing data saved to log')
        proofing_user.orcid = orcid_element
        save_and_sync_user(proofing_user)
        current_app.logger.info('ORCID proofing data saved to user')
        new_query_string = urlencode({'msg': 'orc.authorization_success'})
    else:
        current_app.logger.info('ORCID proofing data NOT saved, failed to save proofing log')
        new_query_string = urlencode({'msg': ':ERROR:Temporary technical problems'})

    # Clean up
    current_app.logger.info('Removing proofing state')
    current_app.proofing_statedb.remove_state(proofing_state)
    url = urlunsplit((scheme, netloc, path, new_query_string, fragment))
    return redirect(url)
Beispiel #43
0
def authorization_response():
    # parse authentication response
    query_string = request.query_string.decode('utf-8')
    current_app.logger.debug('query_string: {!s}'.format(query_string))
    authn_resp = current_app.oidc_client.parse_response(AuthorizationResponse,
                                                        info=query_string,
                                                        sformat='urlencoded')
    current_app.logger.debug(
        'Authorization response received: {!s}'.format(authn_resp))

    if authn_resp.get('error'):
        current_app.logger.error(
            'AuthorizationError {!s} - {!s} ({!s})'.format(
                request.host, authn_resp['error'],
                authn_resp.get('error_message'), authn_resp.get('error_uri')))
        return make_response('OK', 200)

    user_oidc_state = authn_resp['state']
    proofing_state = current_app.proofing_statedb.get_state_by_oidc_state(
        user_oidc_state)
    if not proofing_state:
        msg = 'The \'state\' parameter ({!s}) does not match a user state.'.format(
            user_oidc_state)
        current_app.logger.error(msg)
        return make_response('OK', 200)
    current_app.logger.debug('Proofing state {!s} for user {!s} found'.format(
        proofing_state.state, proofing_state.eppn))

    # Check if the token from the QR code matches the token we created when making the auth request
    authorization_header = request.headers.get('Authorization')
    if authorization_header != 'Bearer {}'.format(proofing_state.token):
        current_app.logger.error(
            'The authorization token ({!s}) did not match the expected'.format(
                authorization_header))
        return make_response('FORBIDDEN', 403)

    # TODO: We should save the auth response code to the proofing state to be able to continue a failed attempt
    # do token request
    args = {
        'code':
        authn_resp['code'],
        'redirect_uri':
        url_for('oidc_proofing.authorization_response', _external=True)
    }
    current_app.logger.debug('Trying to do token request: {!s}'.format(args))
    # TODO: What and where should be save from the token response
    token_resp = current_app.oidc_client.do_access_token_request(
        scope='openid',
        state=authn_resp['state'],
        request_args=args,
        authn_method='client_secret_basic')
    current_app.logger.debug(
        'token response received: {!s}'.format(token_resp))
    id_token = token_resp['id_token']
    if id_token['nonce'] != proofing_state.nonce:
        current_app.logger.error(
            'The \'nonce\' parameter does not match for user {!s}.'.format(
                proofing_state.eppn))
        return make_response('OK', 200)

    # do userinfo request
    current_app.logger.debug('Trying to do userinfo request:')
    # TODO: Do we need to save anything else from the userinfo response
    userinfo = current_app.oidc_client.do_user_info_request(
        method=current_app.config['USERINFO_ENDPOINT_METHOD'],
        state=authn_resp['state'])
    current_app.logger.debug('userinfo received: {!s}'.format(userinfo))
    if userinfo['sub'] != id_token['sub']:
        current_app.logger.error(
            'The \'sub\' of userinfo does not match \'sub\' of ID Token for user {!s}.'
            .format(proofing_state.eppn))
        return make_response('OK', 200)

    # Check proofed nin against self proclaimed OidcProofingState.nin.number
    number = userinfo['identity']
    if proofing_state.nin.number == number:
        nin = Nin(data=proofing_state.nin.to_dict())
        nin.verified = True

        # TODO: Break out in parts to be able to continue the proofing process after a successful authorization response
        # TODO: even if the token request, userinfo request or something internal fails
        am_user = current_app.central_userdb.get_user_by_eppn(
            proofing_state.eppn)
        user = ProofingUser(data=am_user.to_dict())

        # Check if the user has more than one verified nin
        if user.nins.primary is None:
            # No primary NIN found, make the only verified NIN primary
            nin.is_primary = True
        user.nins.add(nin)

        # XXX: Send proofing data to some kind of proofing log

        # User from central db is as up to date as it can be no need to check for modified time
        user.modified_ts = True
        # Save user to private db
        current_app.proofing_userdb.save(user, check_sync=False)

        # TODO: Need to decide where to "steal" NIN if multiple users have the NIN verified
        # Ask am to sync user to central db
        try:
            current_app.logger.info('Request sync for user {!s}'.format(user))
            result = current_app.am_relay.request_user_sync(user)
            current_app.logger.info('Sync result for user {!s}: {!s}'.format(
                user, result))
        except Exception as e:
            current_app.logger.error(
                'Sync request failed for user {!s}'.format(user))
            current_app.logger.error('Exception: {!s}'.format(e))
            # TODO: Need to able to retry this
            return make_response('OK', 200)

        # TODO: Remove saving of proof
        # Save proof for demo purposes
        proof_data = {
            'eduPersonPrincipalName': proofing_state.eppn,
            'authn_resp': authn_resp.to_dict(),
            'token_resp': token_resp.to_dict(),
            'userinfo': userinfo.to_dict()
        }
        current_app.proofdb.save(Proof(data=proof_data))

    # Remove users proofing state
    current_app.proofing_statedb.remove_state(proofing_state)
    return make_response('OK', 200)