Merge pull request #5253 from boundlessgeo/auth_proxy

[auth][feature][needs-docs] Proxy authentication integration with QGIS authentication system
This commit is contained in:
Alessandro Pasotti 2017-09-27 17:41:53 +02:00 committed by GitHub
commit 829702bd24
14 changed files with 324 additions and 74 deletions

View File

@ -359,6 +359,17 @@ Get list of authentication ids from database
:rtype: bool
%End
bool updateNetworkProxy( QNetworkProxy &proxy /In,Out/, const QString &authcfg,
const QString &dataprovider = QString() );
%Docstring
Provider call to update a QNetworkProxy with an authentication config
\param proxy the QNetworkProxy
\param authcfg Associated authentication config id
\param dataprovider Provider key filter, offering logic branching in authentication method
:return: Whether operation succeeded
:rtype: bool
%End
bool storeAuthSetting( const QString &key, const QVariant &value, bool encrypt = false );
%Docstring

View File

@ -28,6 +28,7 @@ class QgsAuthMethod : QObject
NetworkReply,
DataSourceUri,
GenericDataSourceUri,
NetworkProxy,
All
};
typedef QFlags<QgsAuthMethod::Expansion> Expansions;
@ -110,6 +111,18 @@ Increment this if method is significantly updated, allow updater code to be writ
:rtype: bool
%End
virtual bool updateNetworkProxy( QNetworkProxy &proxy, const QString &authcfg,
const QString &dataprovider = QString() );
%Docstring
Update proxy settings with authentication components
\param proxy
\param authcfg Authentication configuration ID
\param dataprovider Textual key for a data provider, e.g. 'proxy', that allows
for custom updater code specific to the provider
:return: Whether the update succeeded
:rtype: bool
%End
virtual void clearCachedConfig( const QString &authcfg ) = 0;
%Docstring
Clear any cached configuration. Called when the QgsAuthManager deletes an authentication configuration (authcfg).

View File

@ -630,8 +630,6 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
connect( mUserProfileManager, &QgsUserProfileManager::profilesChanged, this, &QgisApp::refreshProfileMenu );
endProfile();
namSetup();
// load GUI: actions, menus, toolbars
profiler->beginGroup( QStringLiteral( "qgisapp" ) );
profiler->beginGroup( QStringLiteral( "startup" ) );
@ -656,6 +654,10 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
}
endProfile();
mTray = new QSystemTrayIcon();
mTray->setIcon( QIcon( QgsApplication::appIconPath() ) );
mTray->hide();
startProfile( QStringLiteral( "Initializing authentication" ) );
mSplash->showMessage( tr( "Initializing authentication" ), Qt::AlignHCenter | Qt::AlignBottom );
qApp->processEvents();
@ -666,6 +668,9 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
}
endProfile();
// Setup QgsNetworkAccessManager (this needs to happen after authentication, for proxy settings)
namSetup();
// Create the themes folder for the user
startProfile( QStringLiteral( "Creating theme folder" ) );
QgsApplication::createThemeFolder();
@ -1156,11 +1161,6 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, bool skipVersionCh
grabGesture( Qt::TapAndHoldGesture );
}
mTray = new QSystemTrayIcon();
mTray->setIcon( QIcon( QgsApplication::appIconPath() ) );
mTray->hide();
connect( QgsApplication::taskManager(), &QgsTaskManager::statusChanged, this, &QgisApp::onTaskCompleteShowNotify );
#ifdef Q_OS_WIN

View File

@ -28,6 +28,7 @@
#include "qgstolerance.h"
#include "qgsscaleutils.h"
#include "qgsnetworkaccessmanager.h"
#include "qgsauthconfigselect.h"
#include "qgsproject.h"
#include "qgsdualview.h"
#include "qgsrasterlayer.h"
@ -307,6 +308,16 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl, const QList<QgsOpti
// WMS/WMS-C default max retry in case of tile request errors
mDefaultTileMaxRetrySpinBox->setValue( mSettings->value( QStringLiteral( "/qgis/defaultTileMaxRetry" ), "3" ).toInt() );
// Proxy stored authentication configurations
mProxyAuthConfigSelect = new QgsAuthConfigSelect( this, QStringLiteral( "proxy" ) );
tabAuth->insertTab( 1, mProxyAuthConfigSelect, tr( "Configurations" ) );
QString authcfg = mSettings->value( QStringLiteral( "proxy/authcfg" ) ).toString();
mProxyAuthConfigSelect->setConfigId( authcfg );
if ( !authcfg.isEmpty() )
{
tabAuth->setCurrentIndex( tabAuth->indexOf( mProxyAuthConfigSelect ) );
}
//Web proxy settings
grpProxy->setChecked( mSettings->value( QStringLiteral( "proxy/proxyEnabled" ), "0" ).toBool() );
leProxyHost->setText( mSettings->value( QStringLiteral( "proxy/proxyHost" ), "" ).toString() );
@ -1162,6 +1173,9 @@ void QgsOptions::saveOptions()
// WMS/WMS-C default max retry in case of tile request errors
mSettings->setValue( QStringLiteral( "/qgis/defaultTileMaxRetry" ), mDefaultTileMaxRetrySpinBox->value() );
// Proxy stored authentication configurations
mSettings->setValue( QStringLiteral( "proxy/authcfg" ), mProxyAuthConfigSelect->configId( ) );
//Web proxy settings
mSettings->setValue( QStringLiteral( "proxy/proxyEnabled" ), grpProxy->isChecked() );
mSettings->setValue( QStringLiteral( "proxy/proxyHost" ), leProxyHost->text() );

View File

@ -32,6 +32,7 @@
class QgsExpressionContext;
class QgsOptionsPageWidget;
class QgsLocatorOptionsWidget;
class QgsAuthConfigSelect;
/**
* \class QgsOptions
@ -255,6 +256,7 @@ class APP_EXPORT QgsOptions : public QgsOptionsDialogBase, private Ui::QgsOption
QList< QgsOptionsPageWidget * > mAdditionalOptionWidgets;
QgsLocatorOptionsWidget *mLocatorOptionsWidget = nullptr;
QgsAuthConfigSelect *mProxyAuthConfigSelect = nullptr;
};

View File

@ -20,6 +20,8 @@
#include "qgsauthmanager.h"
#include "qgslogger.h"
#include <QNetworkProxy>
static const QString AUTH_METHOD_KEY = QStringLiteral( "Basic" );
static const QString AUTH_METHOD_DESCRIPTION = QStringLiteral( "Basic authentication" );
@ -37,7 +39,8 @@ QgsAuthBasicMethod::QgsAuthBasicMethod()
<< QStringLiteral( "ows" )
<< QStringLiteral( "wfs" ) // convert to lowercase
<< QStringLiteral( "wcs" )
<< QStringLiteral( "wms" ) );
<< QStringLiteral( "wms" )
<< QStringLiteral( "proxy" ) );
}
QgsAuthBasicMethod::~QgsAuthBasicMethod()
@ -126,6 +129,28 @@ bool QgsAuthBasicMethod::updateDataSourceUriItems( QStringList &connectionItems,
return true;
}
bool QgsAuthBasicMethod::updateNetworkProxy( QNetworkProxy &proxy, const QString &authcfg, const QString &dataprovider )
{
Q_UNUSED( dataprovider )
QgsAuthMethodConfig mconfig = getMethodConfig( authcfg );
if ( !mconfig.isValid() )
{
QgsDebugMsg( QString( "Update proxy config FAILED for authcfg: %1: config invalid" ).arg( authcfg ) );
return false;
}
QString username = mconfig.config( QStringLiteral( "username" ) );
QString password = mconfig.config( QStringLiteral( "password" ) );
if ( !username.isEmpty() )
{
proxy.setUser( username );
proxy.setPassword( password );
}
return true;
}
void QgsAuthBasicMethod::updateMethodConfig( QgsAuthMethodConfig &mconfig )
{
if ( mconfig.hasConfig( QStringLiteral( "oldconfigstyle" ) ) )

View File

@ -44,6 +44,10 @@ class QgsAuthBasicMethod : public QgsAuthMethod
bool updateDataSourceUriItems( QStringList &connectionItems, const QString &authcfg,
const QString &dataprovider = QString() ) override;
bool updateNetworkProxy( QNetworkProxy &proxy, const QString &authcfg,
const QString &dataprovider = QString() ) override;
void clearCachedConfig( const QString &authcfg ) override;
void updateMethodConfig( QgsAuthMethodConfig &mconfig ) override;

View File

@ -1457,6 +1457,32 @@ bool QgsAuthManager::updateDataSourceUriItems( QStringList &connectionItems, con
return false;
}
bool QgsAuthManager::updateNetworkProxy( QNetworkProxy &proxy, const QString &authcfg, const QString &dataprovider )
{
if ( isDisabled() )
return false;
QgsAuthMethod *authmethod = configAuthMethod( authcfg );
if ( authmethod )
{
if ( !( authmethod->supportedExpansions() & QgsAuthMethod::NetworkProxy ) )
{
QgsDebugMsg( QStringLiteral( "Proxy updating not supported by authcfg: %1" ).arg( authcfg ) );
return true;
}
if ( !authmethod->updateNetworkProxy( proxy, authcfg, dataprovider.toLower() ) )
{
authmethod->clearCachedConfig( authcfg );
return false;
}
QgsDebugMsg( QStringLiteral( "Proxy updated successfully from authcfg: %1" ).arg( authcfg ) );
return true;
}
return false;
}
bool QgsAuthManager::storeAuthSetting( const QString &key, const QVariant &value, bool encrypt )
{
if ( key.isEmpty() )

View File

@ -332,6 +332,16 @@ class CORE_EXPORT QgsAuthManager : public QObject
bool updateDataSourceUriItems( QStringList &connectionItems SIP_INOUT, const QString &authcfg,
const QString &dataprovider = QString() );
/**
* Provider call to update a QNetworkProxy with an authentication config
* \param proxy the QNetworkProxy
* \param authcfg Associated authentication config id
* \param dataprovider Provider key filter, offering logic branching in authentication method
* \returns Whether operation succeeded
*/
bool updateNetworkProxy( QNetworkProxy &proxy SIP_INOUT, const QString &authcfg,
const QString &dataprovider = QString() );
////////////////// Generic settings ///////////////////////
//! Store an authentication setting (stored as string via QVariant( value ).toString() )

View File

@ -51,7 +51,8 @@ class CORE_EXPORT QgsAuthMethod : public QObject
NetworkReply = 0x2,
DataSourceUri = 0x4,
GenericDataSourceUri = 0x8,
All = NetworkRequest | NetworkReply | DataSourceUri | GenericDataSourceUri
NetworkProxy = 0x16,
All = NetworkRequest | NetworkReply | DataSourceUri | GenericDataSourceUri | NetworkProxy
};
Q_DECLARE_FLAGS( Expansions, Expansion )
@ -126,6 +127,22 @@ class CORE_EXPORT QgsAuthMethod : public QObject
return true; // noop
}
/** Update proxy settings with authentication components
* \param proxy
* \param authcfg Authentication configuration ID
* \param dataprovider Textual key for a data provider, e.g. 'proxy', that allows
* for custom updater code specific to the provider
* \returns Whether the update succeeded
*/
virtual bool updateNetworkProxy( QNetworkProxy &proxy, const QString &authcfg,
const QString &dataprovider = QString() )
{
Q_UNUSED( proxy )
Q_UNUSED( authcfg )
Q_UNUSED( dataprovider )
return true; // noop
}
/** Clear any cached configuration. Called when the QgsAuthManager deletes an authentication configuration (authcfg).
* \note It is highly recommended that a cache of authentication components (per requested authcfg)
* be implemented, to avoid excessive queries on the auth database. Such a cache could be as

View File

@ -321,6 +321,7 @@ void QgsNetworkAccessManager::setupDefaultProxyAndCache()
//read type, host, port, user, passw from settings
QString proxyHost = settings.value( QStringLiteral( "proxy/proxyHost" ), "" ).toString();
int proxyPort = settings.value( QStringLiteral( "proxy/proxyPort" ), "" ).toString().toInt();
QString proxyUser = settings.value( QStringLiteral( "proxy/proxyUser" ), "" ).toString();
QString proxyPassword = settings.value( QStringLiteral( "proxy/proxyPassword" ), "" ).toString();
@ -356,7 +357,7 @@ void QgsNetworkAccessManager::setupDefaultProxyAndCache()
{
proxyType = QNetworkProxy::FtpCachingProxy;
}
QgsDebugMsg( QString( "setting proxy %1 %2:%3 %4/%5" )
QgsDebugMsg( QStringLiteral( "setting proxy %1 %2:%3 %4/%5" )
.arg( proxyType )
.arg( proxyHost ).arg( proxyPort )
.arg( proxyUser, proxyPassword )
@ -365,6 +366,14 @@ void QgsNetworkAccessManager::setupDefaultProxyAndCache()
}
}
// Setup network proxy authentication configuration
QString authcfg = settings.value( QStringLiteral( "proxy/authcfg" ), "" ).toString();
if ( !authcfg.isEmpty( ) )
{
QgsDebugMsg( QStringLiteral( "setting proxy from stored authentication configuration %1" ).arg( authcfg ) );
QgsAuthManager::instance()->updateNetworkProxy( proxy, authcfg );
}
setFallbackProxyAndExcludes( proxy, excludes );
QgsNetworkDiskCache *newcache = qobject_cast<QgsNetworkDiskCache *>( cache() );

View File

@ -320,7 +320,7 @@
<item>
<widget class="QStackedWidget" name="mOptionsStackedWidget">
<property name="currentIndex">
<number>0</number>
<number>13</number>
</property>
<widget class="QWidget" name="mOptionsPageGeneral">
<layout class="QVBoxLayout" name="verticalLayout_3">
@ -349,8 +349,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>857</width>
<height>678</height>
<width>846</width>
<height>672</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_28">
@ -998,8 +998,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>561</width>
<height>1079</height>
<width>537</width>
<height>1128</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_22">
@ -1528,8 +1528,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>511</width>
<height>719</height>
<width>499</width>
<height>754</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_27">
@ -1896,8 +1896,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>692</width>
<height>994</height>
<width>672</width>
<height>1043</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_22">
@ -2647,8 +2647,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>146</width>
<height>241</height>
<width>130</width>
<height>322</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_46">
@ -2798,8 +2798,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>514</width>
<height>334</height>
<width>500</width>
<height>340</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_25">
@ -3141,8 +3141,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>625</width>
<height>612</height>
<width>623</width>
<height>734</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_30">
@ -3585,8 +3585,8 @@ The bigger the number, the faster zooming with the mouse wheel will be.</string>
<rect>
<x>0</x>
<y>0</y>
<width>487</width>
<height>588</height>
<width>491</width>
<height>610</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_39">
@ -3854,8 +3854,8 @@ The bigger the number, the faster zooming with the mouse wheel will be.</string>
<rect>
<x>0</x>
<y>0</y>
<width>556</width>
<height>740</height>
<width>548</width>
<height>785</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_31">
@ -4432,8 +4432,8 @@ The bigger the number, the faster zooming with the mouse wheel will be.</string>
<rect>
<x>0</x>
<y>0</y>
<width>427</width>
<height>380</height>
<width>431</width>
<height>376</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
@ -4571,8 +4571,8 @@ The bigger the number, the faster zooming with the mouse wheel will be.</string>
<rect>
<x>0</x>
<y>0</y>
<width>429</width>
<height>549</height>
<width>418</width>
<height>544</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_15">
@ -4784,8 +4784,8 @@ The bigger the number, the faster zooming with the mouse wheel will be.</string>
<rect>
<x>0</x>
<y>0</y>
<width>282</width>
<height>234</height>
<width>268</width>
<height>232</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_32">
@ -4892,9 +4892,9 @@ The bigger the number, the faster zooming with the mouse wheel will be.</string>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>509</width>
<height>723</height>
<y>-44</y>
<width>846</width>
<height>830</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_33">
@ -5125,40 +5125,12 @@ The bigger the number, the faster zooming with the mouse wheel will be.</string>
<property name="rightMargin">
<number>0</number>
</property>
<item row="2" column="2">
<widget class="QLineEdit" name="leProxyUser">
<property name="toolTip">
<string>Leave this blank if no proxy username / password are required</string>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QgsPasswordLineEdit" name="leProxyPassword">
<property name="toolTip">
<string>Leave this blank if no proxy username / password are required</string>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLabel" name="lblUser">
<property name="text">
<string>User</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLabel" name="lblPassword">
<property name="text">
<string>Password</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="leProxyHost"/>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="leProxyPort"/>
</item>
<item row="1" column="1">
<widget class="QLabel" name="lblProxyPort">
<property name="text">
@ -5166,9 +5138,6 @@ The bigger the number, the faster zooming with the mouse wheel will be.</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLineEdit" name="leProxyPort"/>
</item>
<item row="0" column="1">
<widget class="QLabel" name="lblProxyHost">
<property name="text">
@ -5192,6 +5161,51 @@ The bigger the number, the faster zooming with the mouse wheel will be.</string>
</property>
</spacer>
</item>
<item row="4" column="1" colspan="2">
<widget class="QTabWidget" name="tabAuth">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="authLegacy">
<attribute name="title">
<string>Authentication</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_25">
<item row="0" column="0">
<widget class="QLabel" name="lblUser">
<property name="text">
<string>User name</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="lblPassword">
<property name="text">
<string>Password</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="leProxyUser">
<property name="toolTip">
<string>Leave this blank if no proxy username / password are required</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QgsPasswordLineEdit" name="leProxyPassword">
<property name="toolTip">
<string>Leave this blank if no proxy username / password are required</string>
</property>
<property name="echoMode">
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</item>
@ -5709,8 +5723,6 @@ The bigger the number, the faster zooming with the mouse wheel will be.</string>
<tabstop>mProxyTypeComboBox</tabstop>
<tabstop>leProxyHost</tabstop>
<tabstop>leProxyPort</tabstop>
<tabstop>leProxyUser</tabstop>
<tabstop>leProxyPassword</tabstop>
<tabstop>mAddUrlPushButton</tabstop>
<tabstop>mRemoveUrlPushButton</tabstop>
<tabstop>mExcludeUrlListWidget</tabstop>

View File

@ -181,6 +181,7 @@ ADD_PYTHON_TEST(PyQgsFileDownloader test_qgsfiledownloader.py)
ADD_PYTHON_TEST(PyQgsSettings test_qgssettings.py)
ADD_PYTHON_TEST(PyQgsZipUtils test_qgsziputils.py)
ADD_PYTHON_TEST(PyQgsSourceSelectProvider test_qgssourceselectprovider.py)
ADD_PYTHON_TEST(PyQgsAuthManagerProxy test_authmanager_proxy.py)
IF (NOT WIN32)
ADD_PYTHON_TEST(PyQgsLogger test_qgslogger.py)

View File

@ -0,0 +1,106 @@
# -*- coding: utf-8 -*-
"""
Tests for auth manager Basic configuration update proxy
From build dir, run from test directory:
LC_ALL=en_US.UTF-8 ctest -R PyQgsAuthManagerProxy -V
.. note:: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
"""
import os
import re
import string
import sys
from functools import partial
from shutil import rmtree
import tempfile
import random
from qgis.core import QgsAuthManager, QgsAuthMethodConfig, QgsNetworkAccessManager, QgsSettings
from qgis.testing import start_app, unittest
from utilities import unitTestDataPath, waitServer
__author__ = 'Alessandro Pasotti'
__date__ = '27/09/2017'
__copyright__ = 'Copyright 2017, The QGIS Project'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'
QGIS_AUTH_DB_DIR_PATH = tempfile.mkdtemp()
os.environ['QGIS_AUTH_DB_DIR_PATH'] = QGIS_AUTH_DB_DIR_PATH
qgis_app = start_app()
class TestAuthManager(unittest.TestCase):
@classmethod
def setUpClass(cls):
"""Run before all tests:
Creates an auth configuration"""
cls.testdata_path = unitTestDataPath('qgis_server') + '/'
# Enable auth
# os.environ['QGIS_AUTH_PASSWORD_FILE'] = QGIS_AUTH_PASSWORD_FILE
authm = QgsAuthManager.instance()
assert (authm.setMasterPassword('masterpassword', True))
cls.auth_config = QgsAuthMethodConfig('Basic')
cls.auth_config.setName('test_auth_config')
cls.username = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(6))
cls.password = cls.username[::-1] # reversed
cls.auth_config.setConfig('username', cls.username)
cls.auth_config.setConfig('password', cls.password)
assert (authm.storeAuthenticationConfig(cls.auth_config)[0])
@classmethod
def tearDownClass(cls):
"""Run after all tests"""
rmtree(QGIS_AUTH_DB_DIR_PATH)
def setUp(self):
"""Run before each test."""
pass
def tearDown(self):
"""Run after each test."""
pass
def testProxyIsUpdated(self):
"""
Test that proxy is updated
"""
authm = QgsAuthManager.instance()
nam = QgsNetworkAccessManager.instance()
proxy = nam.proxy()
self.assertEqual(proxy.password(), '')
self.assertEqual(proxy.user(), '')
self.assertTrue(authm.updateNetworkProxy(proxy, self.auth_config.id()))
self.assertEqual(proxy.user(), self.username)
self.assertEqual(proxy.password(), self.password)
def testProxyIsUpdatedByUserSettings(self):
"""
Test that proxy is updated
"""
nam = QgsNetworkAccessManager.instance()
nam.setupDefaultProxyAndCache()
proxy = nam.proxy()
self.assertEqual(proxy.password(), '')
self.assertEqual(proxy.user(), '')
settings = QgsSettings()
settings.setValue("proxy/authcfg", self.auth_config.id())
settings.setValue("proxy/proxyEnabled", True)
del(settings)
nam.setupDefaultProxyAndCache()
proxy = nam.fallbackProxy()
self.assertEqual(proxy.password(), self.password)
self.assertEqual(proxy.user(), self.username)
if __name__ == '__main__':
unittest.main()