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)
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) }
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) }
def get_credentials(user): """ View to get credentials for the logged user. """ current_app.logger.debug('Trying to get the credentials ' 'for user {}'.format(user)) credentials = {'credentials': compile_credential_list(user)} return credentials
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) }
def get_credentials(user): """ View to get credentials for the logged user. """ current_app.logger.debug('Trying to get the credentials ' 'for user {}'.format(user)) credentials = { 'credentials': compile_credential_list(user) } return CredentialList().dump(credentials).data
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)
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) }
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)}
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) }
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
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}
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)
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