Beispiel #1
0
def registration_complete(user, credential_id, attestation_object, client_data, description):
    security_user = SecurityUser.from_user(user, current_app.private_userdb)
    server = get_webauthn_server(current_app.config['FIDO2_RP_ID'])
    att_obj = AttestationObject(urlsafe_b64decode(attestation_object))
    cdata_obj = ClientData(urlsafe_b64decode(client_data))
    state = session['_webauthn_state_']
    auth_data = server.register_complete(state, cdata_obj, att_obj)

    cred_data = auth_data.credential_data
    current_app.logger.debug('Proccessed Webauthn credential data: {}.'.format(cred_data))

    credential = Webauthn(
        keyhandle = credential_id,
        credential_data = base64.urlsafe_b64encode(cred_data).decode('ascii'),
        app_id = current_app.config['FIDO2_RP_ID'],
        attest_obj = base64.b64encode(attestation_object.encode('utf-8')).decode('ascii'),
        description = description,
        application = 'security'
        )

    security_user.credentials.add(credential)
    save_and_sync_user(security_user)
    current_app.stats.count(name='webauthn_register_complete')
    current_app.logger.info('User {} has completed registration of a webauthn token'.format(security_user))
    return {
        'message': 'security.webauthn_register_success',
        'credentials': compile_credential_list(security_user)
    }
Beispiel #2
0
    def test_fillup_attributes(self):
        for context in self.plugin_contexts:
            security_user = SecurityUser(data=self.user_data)
            context.private_db.save(security_user)

            self.assertDictEqual(
                attribute_fetcher(context, security_user.user_id),
                {
                    '$set': {
                        'passwords': [{
                            'credential_id': u'112345678901234567890123',
                            'salt': '$NDNv1H1$9c810d852430b62a9a7c6159d5d64c41c3831846f81b6799b54e1e8922f11545$32$32$',
                        }],
                        'nins': [{
                            'number': '123456781235',
                            'primary': True,
                            'verified': True
                        }],
                        'phone': [{
                            'number': '+46700011336',
                            'primary': True,
                            'verified': True
                        }]
                    },
                    '$unset': {
                        'terminated': None
                    }
                }
            )
Beispiel #3
0
def add_nin(user, nin):
    security_user = SecurityUser.from_user(user, current_app.private_userdb)
    current_app.logger.info('Removing NIN from user')
    current_app.logger.debug('NIN: {}'.format(nin))

    nin_obj = security_user.nins.find(nin)
    if nin_obj:
        current_app.logger.info('NIN already added.')
        return error_response(message=SecurityMsg.already_exists)

    try:
        nin_element = NinProofingElement.from_dict(
            dict(number=nin, created_by='security', verified=False))
        proofing_state = NinProofingState.from_dict({
            'eduPersonPrincipalName':
            security_user.eppn,
            'nin':
            nin_element.to_dict()
        })
        add_nin_to_user(user, proofing_state, user_class=SecurityUser)
        return success_response(
            payload=dict(nins=security_user.nins.to_list_of_dicts()),
            message=SecurityMsg.add_success)
    except AmTaskFailed as e:
        current_app.logger.error('Adding nin to user failed')
        current_app.logger.debug(f'NIN: {nin}')
        current_app.logger.error('{}'.format(e))
        return error_response(message=CommonMsg.temp_problem)
Beispiel #4
0
def bind(user, version, registration_data, client_data, description=''):
    security_user = SecurityUser.from_user(user, current_app.private_userdb)
    enrollment_data = session.pop('_u2f_enroll_', None)
    if not enrollment_data:
        current_app.logger.error('Found no U2F enrollment data in session.')
        return {'_error': True, 'message': 'security.u2f.missing_enrollment_data'}
    data = {
        'version': version,
        'registrationData': registration_data,
        'clientData': client_data
    }
    device, der_cert = complete_registration(enrollment_data, data, current_app.config['U2F_FACETS'])

    cert = x509.load_der_x509_certificate(der_cert, default_backend())
    pem_cert = crypto.dump_certificate(crypto.FILETYPE_PEM, cert)
    if not isinstance(pem_cert, six.string_types):
        pem_cert = pem_cert.decode('utf-8')

    u2f_token = U2F(version=device['version'], keyhandle=device['keyHandle'], app_id=device['appId'],
                    public_key=device['publicKey'], attest_cert=pem_cert, description=description,
                    application='eduid_security', created_ts=True)
    security_user.credentials.add(u2f_token)
    save_and_sync_user(security_user)
    current_app.stats.count(name='u2f_token_bind')
    return {
        'message': 'security.u2f_register_success',
        'credentials': compile_credential_list(security_user)
    }
Beispiel #5
0
def registration_complete(user, credential_id, attestation_object, client_data, description):
    security_user = SecurityUser.from_user(user, current_app.private_userdb)
    server = get_webauthn_server(current_app.config.fido2_rp_id)
    att_obj = AttestationObject(urlsafe_b64decode(attestation_object))
    cdata_obj = ClientData(urlsafe_b64decode(client_data))
    state = session['_webauthn_state_']
    auth_data = server.register_complete(state, cdata_obj, att_obj)

    cred_data = auth_data.credential_data
    current_app.logger.debug('Proccessed Webauthn credential data: {}.'.format(cred_data))

    credential = Webauthn.from_dict(
        dict(
            keyhandle=credential_id,
            credential_data=base64.urlsafe_b64encode(cred_data).decode('ascii'),
            app_id=current_app.config.fido2_rp_id,
            attest_obj=base64.b64encode(attestation_object.encode('utf-8')).decode('ascii'),
            description=description,
            created_by='security',
        )
    )

    security_user.credentials.add(credential)
    save_and_sync_user(security_user)
    current_app.stats.count(name='webauthn_register_complete')
    current_app.logger.info('User {} has completed registration of a webauthn token'.format(security_user))
    credentials = compile_credential_list(security_user)
    return success_response(payload=dict(credentials=credentials), message=SecurityMsg.webauthn_success)
Beispiel #6
0
 def add_token_to_user(self, eppn):
     user = self.app.central_userdb.get_user_by_eppn(eppn)
     u2f_token = U2F(version='version', keyhandle='keyHandle', app_id='appId',
                     public_key='publicKey', attest_cert='cert', description='description',
                     application='eduid_security', created_ts=True)
     user.credentials.add(u2f_token)
     self.app.central_userdb.save(user)
     return SecurityUser.from_user(user, self.app.private_userdb)
Beispiel #7
0
def account_terminated(user):
    """
    The account termination action,
    removes all credentials for the terminated account
    from the VCCS service,
    flags the account as terminated,
    sends an email to the address in the terminated account,
    and logs out the session.

    :type user: eduid_userdb.user.User
    """
    security_user = SecurityUser.from_user(user, current_app.private_userdb)
    authn_ts = session.get('reauthn-for-termination', None)
    if authn_ts is None:
        abort(400)

    now = datetime.utcnow()
    delta = now - datetime.fromtimestamp(authn_ts)

    if int(delta.total_seconds()) > 600:
        return error_response(message=SecurityMsg.stale_reauthn)

    del session['reauthn-for-termination']

    # revoke all user passwords
    revoke_all_credentials(current_app.config.vccs_url, security_user)
    # Skip removing old passwords from the user at this point as a password reset will do that anyway.
    # This fixes the problem with loading users for a password reset as users without passwords triggers
    # the UserHasNotCompletedSignup check in eduid-userdb.
    # TODO: Needs a decision on how to handle unusable user passwords
    # for p in security_user.credentials.filter(Password).to_list():
    #    security_user.passwords.remove(p.key)

    # flag account as terminated
    security_user.terminated = True
    try:
        save_and_sync_user(security_user)
    except UserOutOfSync:
        return error_response(message=CommonMsg.out_of_sync)

    current_app.stats.count(name='security_account_terminated', value=1)
    current_app.logger.info('Terminated user account')

    # email the user
    try:
        send_termination_mail(security_user)
    except MsgTaskFailed as e:
        current_app.logger.error(
            f'Failed to send account termination mail: {e}')
        current_app.logger.error(
            'Account will be terminated successfully anyway.')

    current_app.logger.debug(f'Logging out (terminated) user {user}')
    return redirect(
        f'{current_app.config.logout_endpoint}?next={current_app.config.termination_redirect_url}'
    )
Beispiel #8
0
def remove(user, credential_key):
    security_user = SecurityUser.from_user(user, current_app.private_userdb)
    token_to_remove = security_user.credentials.filter(U2F).find(credential_key)
    if token_to_remove:
        security_user.credentials.remove(credential_key)
        save_and_sync_user(security_user)
        current_app.stats.count(name='u2f_token_remove')
    return {
        'message': 'security.u2f-token-removed',
        'credentials': compile_credential_list(security_user)
    }
Beispiel #9
0
def remove(user, credential_key):
    security_user = SecurityUser.from_user(user, current_app.private_userdb)
    token_to_remove = security_user.credentials.filter(U2F).find(
        credential_key)
    if token_to_remove:
        security_user.credentials.remove(credential_key)
        save_and_sync_user(security_user)
        current_app.stats.count(name='u2f_token_remove')

    credentials = compile_credential_list(security_user)
    return success_response(payload=dict(credentials=credentials),
                            message=SecurityMsg.rm_u2f_success)
Beispiel #10
0
def account_terminated(user):
    """
    The account termination action,
    removes all credentials for the terminated account
    from the VCCS service,
    flags the account as terminated,
    sends an email to the address in the terminated account,
    and logs out the session.

    :type user: eduid_userdb.user.User
    """
    security_user = SecurityUser.from_user(user, current_app.private_userdb)
    authn_ts = session.get('reauthn-for-termination', None)
    if authn_ts is None:
        abort(400)

    now = datetime.utcnow()
    delta = now - datetime.fromtimestamp(authn_ts)

    if int(delta.total_seconds()) > 600:
        return error_message('security.stale_authn_info')

    del session['reauthn-for-termination']

    # revoke all user passwords
    revoke_all_credentials(current_app.config.get('VCCS_URL'), security_user)
    # Skip removing old passwords from the user at this point as a password reset will do that anyway.
    # This fixes the problem with loading users for a password reset as users without passwords triggers
    # the UserHasNotCompletedSignup check in eduid-userdb.
    # TODO: Needs a decision on how to handle unusable user passwords
    #for p in security_user.credentials.filter(Password).to_list():
    #    security_user.passwords.remove(p.key)

    # flag account as terminated
    security_user.terminated = True
    try:
        save_and_sync_user(security_user)
    except UserOutOfSync:
        return error_message('user-out-of-sync')

    current_app.stats.count(name='security_account_terminated', value=1)
    current_app.logger.info('Terminated user account')

    # email the user
    send_termination_mail(security_user)

    session.invalidate()
    current_app.logger.info('Invalidated session for user')

    site_url = current_app.config.get("EDUID_SITE_URL")
    current_app.logger.info('Redirection user to user {}'.format(site_url))
    # TODO: Add a account termination completed view to redirect to
    return redirect(site_url)
Beispiel #11
0
def account_terminated(user):
    """
    The account termination action,
    removes all credentials for the terminated account
    from the VCCS service,
    flags the account as terminated,
    sends an email to the address in the terminated account,
    and logs out the session.

    :type user: eduid_userdb.user.User
    """
    security_user = SecurityUser.from_user(user, current_app.private_userdb)
    authn_ts = session.get('reauthn-for-termination', None)
    if authn_ts is None:
        abort(400)

    now = datetime.utcnow()
    delta = now - datetime.fromtimestamp(authn_ts)

    if int(delta.total_seconds()) > 600:
        return error_message('security.stale_authn_info')

    del session['reauthn-for-termination']

    # revoke all user passwords
    revoke_all_credentials(current_app.config.get('VCCS_URL'), security_user)
    # Skip removing old passwords from the user at this point as a password reset will do that anyway.
    # This fixes the problem with loading users for a password reset as users without passwords triggers
    # the UserHasNotCompletedSignup check in eduid-userdb.
    # TODO: Needs a decision on how to handle unusable user passwords
    #for p in security_user.credentials.filter(Password).to_list():
    #    security_user.passwords.remove(p.key)

    # flag account as terminated
    security_user.terminated = True
    try:
        save_and_sync_user(security_user)
    except UserOutOfSync:
        return error_message('user-out-of-sync')

    current_app.stats.count(name='security_account_terminated', value=1)
    current_app.logger.info('Terminated user account')

    # email the user
    send_termination_mail(security_user)

    session.invalidate()
    current_app.logger.info('Invalidated session for user')

    site_url = current_app.config.get("EDUID_SITE_URL")
    current_app.logger.info('Redirection user to user {}'.format(site_url))
    # TODO: Add a account termination completed view to redirect to
    return redirect(site_url)
Beispiel #12
0
def reset_user_password(state, password):
    """
    :param state: Password reset state
    :type state: PasswordResetState
    :param password: Plain text password
    :type password: six.string_types
    :return: None
    :rtype: None
    """
    vccs_url = current_app.config.vccs_url

    user = current_app.central_userdb.get_user_by_eppn(state.eppn,
                                                       raise_on_missing=False)
    security_user = SecurityUser.from_user(
        user, private_userdb=current_app.private_userdb)

    # If no extra security is all verified information (except email addresses) is set to not verified
    if not extra_security_used(state):
        current_app.logger.info('No extra security used by user {}'.format(
            state.eppn))
        # Phone numbers
        verified_phone_numbers = security_user.phone_numbers.verified.to_list()
        if verified_phone_numbers:
            current_app.logger.info(
                'Unverifying phone numbers for user {}'.format(state.eppn))
            security_user.phone_numbers.primary.is_primary = False
            for phone_number in verified_phone_numbers:
                phone_number.is_verified = False
                current_app.logger.debug('Phone number {} unverified'.format(
                    phone_number.number))
        # NINs
        verified_nins = security_user.nins.verified.to_list()
        if verified_nins:
            current_app.logger.info('Unverifying nins for user {}'.format(
                state.eppn))
            security_user.nins.primary.is_primary = False
            for nin in verified_nins:
                nin.is_verified = False
                current_app.logger.debug('NIN {} unverified'.format(
                    nin.number))

    security_user = reset_password(security_user,
                                   new_password=password,
                                   application='security',
                                   vccs_url=vccs_url)
    security_user.terminated = False
    save_and_sync_user(security_user)
    current_app.stats.count(name='security_password_reset', value=1)
    current_app.logger.info('Reset password successful for user {}'.format(
        security_user.eppn))
Beispiel #13
0
def modify(user, credential_key, description):
    security_user = SecurityUser.from_user(user, current_app.private_userdb)
    token_to_modify = security_user.credentials.filter(U2F).find(credential_key)
    if not token_to_modify:
        current_app.logger.error('Did not find requested U2F token for user.')
        return {'_error': True, 'message': 'security.u2f.missing_token'}
    if len(description) > current_app.config['U2F_MAX_DESCRIPTION_LENGTH']:
        current_app.logger.error('User tried to set a U2F token description longer than {}.'.format(
            current_app.config['U2F_MAX_DESCRIPTION_LENGTH']))
        return {'_error': True, 'message': 'security.u2f.description_to_long'}
    token_to_modify.description = description
    save_and_sync_user(security_user)
    current_app.stats.count(name='u2f_token_modify')
    return {
        'credentials': compile_credential_list(security_user)
    }
Beispiel #14
0
def modify(user, credential_key, description):
    security_user = SecurityUser.from_user(user, current_app.private_userdb)
    token_to_modify = security_user.credentials.filter(U2F).find(
        credential_key)
    if not token_to_modify:
        current_app.logger.error('Did not find requested U2F token for user.')
        return error_response(message=SecurityMsg.no_token)

    if len(description) > current_app.config.u2f_max_description_length:
        current_app.logger.error(
            'User tried to set a U2F token description longer than {}.'.format(
                current_app.config.u2f_max_description_length))
        return error_response(message=SecurityMsg.long_desc)

    token_to_modify.description = description
    save_and_sync_user(security_user)
    current_app.stats.count(name='u2f_token_modify')
    return {'credentials': compile_credential_list(security_user)}
Beispiel #15
0
def remove_nin(user, nin):
    security_user = SecurityUser.from_user(user, current_app.private_userdb)
    current_app.logger.info('Removing NIN from user')
    current_app.logger.debug('NIN: {}'.format(nin))

    nin_obj = security_user.nins.find(nin)
    if nin_obj and nin_obj.is_verified:
        current_app.logger.info('NIN verified. Will not remove it.')
        return {'_status': 'error', 'success': False, 'message': 'nins.verified_no_rm'}

    try:
        remove_nin_from_user(security_user, nin)
        return {'success': True,
                'message': 'nins.success_removal',
                'nins': security_user.nins.to_list_of_dicts()}
    except AmTaskFailed as e:
        current_app.logger.error('Removing nin from user failed'.format(nin, security_user))
        current_app.logger.error('{}'.format(e))
        return {'_status': 'error', 'message': 'Temporary technical problems'}
Beispiel #16
0
def remove_nin(user, nin):
    security_user = SecurityUser.from_user(user, current_app.private_userdb)
    current_app.logger.info('Removing NIN from user')
    current_app.logger.debug('NIN: {}'.format(nin))

    nin_obj = security_user.nins.find(nin)
    if nin_obj and nin_obj.is_verified:
        current_app.logger.info('NIN verified. Will not remove it.')
        return {'_status': 'error', 'success': False, 'message': 'nins.verified_no_rm'}

    try:
        remove_nin_from_user(security_user, nin)
        return {'success': True,
                'message': 'nins.success_removal',
                'nins': security_user.nins.to_list_of_dicts()}
    except AmTaskFailed as e:
        current_app.logger.error('Removing nin from user failed'.format(nin, security_user))
        current_app.logger.error('{}'.format(e))
        return {'_status': 'error', 'message': 'Temporary technical problems'}
Beispiel #17
0
def remove_nin(user, nin):
    security_user = SecurityUser.from_user(user, current_app.private_userdb)
    current_app.logger.info('Removing NIN from user')
    current_app.logger.debug('NIN: {}'.format(nin))

    nin_obj = security_user.nins.find(nin)
    if nin_obj and nin_obj.is_verified:
        current_app.logger.info('NIN verified. Will not remove it.')
        return error_response(message=SecurityMsg.rm_verified)

    try:
        remove_nin_from_user(security_user, nin)
        return success_response(
            payload=dict(nins=security_user.nins.to_list_of_dicts()),
            message=SecurityMsg.rm_success)
    except AmTaskFailed as e:
        current_app.logger.error('Removing nin from user failed')
        current_app.logger.debug(f'NIN: {nin}')
        current_app.logger.error('{}'.format(e))
        return error_response(message=CommonMsg.temp_problem)
Beispiel #18
0
def remove(user, credential_key):
    security_user = SecurityUser.from_user(user, current_app.private_userdb)
    tokens = security_user.credentials.filter(FidoCredential)
    if tokens.count <= 1:
        return {'_error': True, 'message': 'security.webauthn-noremove-last'}
    token_to_remove = security_user.credentials.find(credential_key)
    if token_to_remove:
        security_user.credentials.remove(credential_key)
        save_and_sync_user(security_user)
        current_app.stats.count(name='webauthn_token_remove')
        current_app.logger.info(f'User {security_user} has removed a security token: {credential_key}')
        message = 'security.webauthn-token-removed'
    else:
        current_app.logger.info(f'User {security_user} has tried to remove a'
                                f' missing security token: {credential_key}')
        message = 'security.webauthn-token-notfound'
    return {
        'message': message,
        'credentials': compile_credential_list(security_user)
    }
Beispiel #19
0
def change_password(user, old_password, new_password):
    """
    View to change the password
    """
    security_user = SecurityUser.from_user(user, current_app.private_userdb)
    authn_ts = session.get('reauthn-for-chpass', None)
    if authn_ts is None:
        return error_message('chpass.no_reauthn')

    now = datetime.utcnow()
    delta = now - datetime.fromtimestamp(authn_ts)
    timeout = current_app.config.get('CHPASS_TIMEOUT', 600)
    if int(delta.total_seconds()) > timeout:
        return error_message('chpass.stale_reauthn')

    vccs_url = current_app.config.get('VCCS_URL')
    added = add_credentials(vccs_url, old_password, new_password, security_user, source='security')

    if not added:
        current_app.logger.debug('Problem verifying the old credentials for {}'.format(user))
        return error_message('chpass.unable-to-verify-old-password')

    security_user.terminated = False
    try:
        save_and_sync_user(security_user)
    except UserOutOfSync:
        return error_message('user-out-of-sync')

    del session['reauthn-for-chpass']

    current_app.stats.count(name='security_password_changed', value=1)
    current_app.logger.info('Changed password for user {}'.format(security_user.eppn))

    next_url = current_app.config.get('DASHBOARD_URL', '/profile')
    credentials = {
        'next_url': next_url,
        'credentials': compile_credential_list(security_user),
        'message': 'chpass.password-changed'
        }

    return CredentialList().dump(credentials).data
Beispiel #20
0
def change_password(user, old_password, new_password):
    """
    View to change the password
    """
    security_user = SecurityUser.from_user(user, current_app.private_userdb)
    authn_ts = session.get('reauthn-for-chpass', None)
    if authn_ts is None:
        return error_message('chpass.no_reauthn')

    now = datetime.utcnow()
    delta = now - datetime.fromtimestamp(authn_ts)
    timeout = current_app.config.get('CHPASS_TIMEOUT', 600)
    if int(delta.total_seconds()) > timeout:
        return error_message('chpass.stale_reauthn')

    vccs_url = current_app.config.get('VCCS_URL')
    added = add_credentials(vccs_url, old_password, new_password, security_user, source='security')

    if not added:
        current_app.logger.debug('Problem verifying the old credentials for {}'.format(user))
        return error_message('chpass.unable-to-verify-old-password')

    security_user.terminated = False
    try:
        save_and_sync_user(security_user)
    except UserOutOfSync:
        return error_message('user-out-of-sync')

    del session['reauthn-for-chpass']

    current_app.stats.count(name='security_password_changed', value=1)
    current_app.logger.info('Changed password for user {}'.format(security_user.eppn))

    next_url = current_app.config.get('DASHBOARD_URL', '/profile')
    credentials = {
        'next_url': next_url,
        'credentials': compile_credential_list(security_user),
        'message': 'chpass.password-changed'
        }

    return CredentialList().dump(credentials).data
Beispiel #21
0
def remove(user, credential_key):
    security_user = SecurityUser.from_user(user, current_app.private_userdb)
    tokens = security_user.credentials.filter(FidoCredential)
    if tokens.count <= 1:
        return {'_error': True, 'message': SecurityMsg.no_last.value}

    token_to_remove = security_user.credentials.find(credential_key)
    if token_to_remove:
        security_user.credentials.remove(credential_key)
        save_and_sync_user(security_user)
        current_app.stats.count(name='webauthn_token_remove')
        current_app.logger.info(f'User {security_user} has removed a security token: {credential_key}')
        message = SecurityMsg.rm_webauthn
    else:
        current_app.logger.info(
            f'User {security_user} has tried to remove a' f' missing security token: {credential_key}'
        )
        message = SecurityMsg.no_webauthn

    credentials = compile_credential_list(security_user)
    return {'message': message, 'credentials': credentials}
Beispiel #22
0
def reset_user_password(state, password):
    """
    :param state: Password reset state
    :type state: PasswordResetState
    :param password: Plain text password
    :type password: six.string_types
    :return: None
    :rtype: None
    """
    vccs_url = current_app.config.get('VCCS_URL')

    user = current_app.central_userdb.get_user_by_eppn(state.eppn, raise_on_missing=False)
    security_user = SecurityUser.from_user(user, private_userdb=current_app.private_userdb)

    # If no extra security is all verified information (except email addresses) is set to not verified
    if not extra_security_used(state):
        current_app.logger.info('No extra security used by user {}'.format(state.eppn))
        # Phone numbers
        verified_phone_numbers = security_user.phone_numbers.verified.to_list()
        if verified_phone_numbers:
            current_app.logger.info('Unverifying phone numbers for user {}'.format(state.eppn))
            security_user.phone_numbers.primary.is_primary = False
            for phone_number in verified_phone_numbers:
                phone_number.is_verified = False
                current_app.logger.debug('Phone number {} unverified'.format(phone_number.number))
        # NINs
        verified_nins = security_user.nins.verified.to_list()
        if verified_nins:
            current_app.logger.info('Unverifying nins for user {}'.format(state.eppn))
            security_user.nins.primary.is_primary = False
            for nin in verified_nins:
                nin.is_verified = False
                current_app.logger.debug('NIN {} unverified'.format(nin.number))

    security_user = reset_password(security_user, new_password=password, application='security', vccs_url=vccs_url)
    security_user.terminated = False
    save_and_sync_user(security_user)
    current_app.stats.count(name='security_password_reset', value=1)
    current_app.logger.info('Reset password successful for user {}'.format(security_user.eppn))
Beispiel #23
0
def bind(user, version, registration_data, client_data, description=''):
    security_user = SecurityUser.from_user(user, current_app.private_userdb)
    enrollment_data = session.pop('_u2f_enroll_', None)
    if not enrollment_data:
        current_app.logger.error('Found no U2F enrollment data in session.')
        return error_response(message=SecurityMsg.missing_data)

    data = {
        'version': version,
        'registrationData': registration_data,
        'clientData': client_data
    }
    device, der_cert = complete_registration(enrollment_data, data,
                                             current_app.config.u2f_facets)

    cert = x509.load_der_x509_certificate(der_cert, default_backend())
    pem_cert = crypto.dump_certificate(crypto.FILETYPE_PEM, cert)
    if not isinstance(pem_cert, six.string_types):
        pem_cert = pem_cert.decode('utf-8')

    u2f_token = U2F.from_dict(
        dict(
            version=device['version'],
            keyhandle=device['keyHandle'],
            app_id=device['appId'],
            public_key=device['publicKey'],
            attest_cert=pem_cert,
            description=description,
            created_by='eduid_security',
            created_ts=True,
        ))
    security_user.credentials.add(u2f_token)
    save_and_sync_user(security_user)
    current_app.stats.count(name='u2f_token_bind')
    credentials = compile_credential_list(security_user)
    return success_response(payload=dict(credentials=credentials),
                            message=SecurityMsg.u2f_registered)
Beispiel #24
0
def change_password(user):
    """
    View to change the password
    """
    security_user = SecurityUser.from_user(user, current_app.private_userdb)
    min_entropy = current_app.config.password_entropy
    schema = ChangePasswordSchema(zxcvbn_terms=get_zxcvbn_terms(
        security_user.eppn),
                                  min_entropy=int(min_entropy))

    if not request.data:
        return error_response(message='chpass.no-data')

    try:
        form = schema.load(json.loads(request.data))
        current_app.logger.debug(form)
    except ValidationError as e:
        current_app.logger.error(e)
        return error_response(message='chpass.weak-password')
    else:
        old_password = form.get('old_password')
        new_password = form.get('new_password')

    if session.get_csrf_token() != form['csrf_token']:
        return error_response(message='csrf.try_again')

    authn_ts = session.get('reauthn-for-chpass', None)
    if authn_ts is None:
        return error_response(message='chpass.no_reauthn')

    now = datetime.utcnow()
    delta = now - datetime.fromtimestamp(authn_ts)
    timeout = current_app.config.chpass_timeout
    if int(delta.total_seconds()) > timeout:
        return error_response(message='chpass.stale_reauthn')

    vccs_url = current_app.config.vccs_url
    added = add_credentials(vccs_url,
                            old_password,
                            new_password,
                            security_user,
                            source='security')

    if not added:
        current_app.logger.debug(
            'Problem verifying the old credentials for {}'.format(user))
        return error_response(message='chpass.unable-to-verify-old-password')

    security_user.terminated = False
    try:
        save_and_sync_user(security_user)
    except UserOutOfSync:
        return error_response(message='user-out-of-sync')

    del session['reauthn-for-chpass']

    current_app.stats.count(name='security_password_changed', value=1)
    current_app.logger.info('Changed password for user {}'.format(
        security_user.eppn))

    next_url = current_app.config.dashboard_url
    credentials = {
        'next_url': next_url,
        'credentials': compile_credential_list(security_user),
        'message': 'chpass.password-changed',
    }

    return credentials