1 /***************************************************************************
2 qgsauthmethodregistry.cpp
3 ---------------------
4 begin : September 1, 2015
5 copyright : (C) 2015 by Boundless Spatial, Inc. USA
6 author : Larry Shaffer
7 email : lshaffer at boundlessgeo dot com
8 ***************************************************************************
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 ***************************************************************************/
16
17 #include "qgsauthmethodregistry.h"
18
19 #include <QString>
20 #include <QDir>
21 #include <QLibrary>
22
23 #include "qgis.h"
24 #include "qgsauthconfig.h"
25 #include "qgsauthmethod.h"
26 #include "qgslogger.h"
27 #include "qgsmessageoutput.h"
28 #include "qgsmessagelog.h"
29 #include "qgsauthmethodmetadata.h"
30
31
32 // typedefs for auth method plugin functions of interest
33 typedef QString methodkey_t();
34 typedef QString description_t();
35 typedef bool isauthmethod_t();
36
37
instance(const QString & pluginPath)38 QgsAuthMethodRegistry *QgsAuthMethodRegistry::instance( const QString &pluginPath )
39 {
40 static QgsAuthMethodRegistry *sInstance( new QgsAuthMethodRegistry( pluginPath ) );
41 return sInstance;
42 }
43
QgsAuthMethodRegistry(const QString & pluginPath)44 QgsAuthMethodRegistry::QgsAuthMethodRegistry( const QString &pluginPath )
45 {
46 // At startup, examine the libs in the qgis/lib dir and store those that
47 // are an auth method shared lib
48 // check all libs in the current plugin directory and get name and descriptions
49 #if 0
50 char **argv = qApp->argv();
51 QString appDir = argv[0];
52 int bin = appDir.findRev( "/bin", -1, false );
53 QString baseDir = appDir.left( bin );
54 QString mLibraryDirectory = baseDir + "/lib";
55 #endif
56 mLibraryDirectory.setPath( pluginPath );
57 mLibraryDirectory.setSorting( QDir::Name | QDir::IgnoreCase );
58 mLibraryDirectory.setFilter( QDir::Files | QDir::NoSymLinks );
59
60 #if defined(Q_OS_WIN) || defined(__CYGWIN__)
61 mLibraryDirectory.setNameFilters( QStringList( "*authmethod.dll" ) );
62 #else
63 mLibraryDirectory.setNameFilters( QStringList( QStringLiteral( "*authmethod.so" ) ) );
64 #endif
65
66 QgsDebugMsgLevel( QStringLiteral( "Checking for auth method plugins in: %1" ).arg( mLibraryDirectory.path() ), 2 );
67
68 if ( mLibraryDirectory.count() == 0 )
69 {
70 QString msg = QObject::tr( "No QGIS auth method plugins found in:\n%1\n" ).arg( mLibraryDirectory.path() );
71 msg += QObject::tr( "No authentication methods can be used. Check your QGIS installation" );
72
73 QgsMessageOutput *output = QgsMessageOutput::createMessageOutput();
74 output->setTitle( QObject::tr( "No Authentication Methods" ) );
75 output->setMessage( msg, QgsMessageOutput::MessageText );
76 output->showMessage();
77 return;
78 }
79
80 // auth method file regex pattern, only files matching the pattern are loaded if the variable is defined
81 QString filePattern = getenv( "QGIS_AUTHMETHOD_FILE" );
82 QRegExp fileRegexp;
83 if ( !filePattern.isEmpty() )
84 {
85 fileRegexp.setPattern( filePattern );
86 }
87
88 QListIterator<QFileInfo> it( mLibraryDirectory.entryInfoList() );
89 while ( it.hasNext() )
90 {
91 QFileInfo fi( it.next() );
92
93 if ( !fileRegexp.isEmpty() )
94 {
95 if ( fileRegexp.indexIn( fi.fileName() ) == -1 )
96 {
97 QgsDebugMsg( "auth method " + fi.fileName() + " skipped because doesn't match pattern " + filePattern );
98 continue;
99 }
100 }
101
102 QLibrary myLib( fi.filePath() );
103 if ( !myLib.load() )
104 {
105 QgsDebugMsg( QStringLiteral( "Checking %1: ...invalid (lib not loadable): %2" ).arg( myLib.fileName(), myLib.errorString() ) );
106 continue;
107 }
108
109 // get the description and the key for the auth method plugin
110 isauthmethod_t *isAuthMethod = reinterpret_cast< isauthmethod_t * >( cast_to_fptr( myLib.resolve( "isAuthMethod" ) ) );
111 if ( !isAuthMethod )
112 {
113 QgsDebugMsg( QStringLiteral( "Checking %1: ...invalid (no isAuthMethod method)" ).arg( myLib.fileName() ) );
114 continue;
115 }
116
117 // check to see if this is an auth method plugin
118 if ( !isAuthMethod() )
119 {
120 QgsDebugMsg( QStringLiteral( "Checking %1: ...invalid (not an auth method)" ).arg( myLib.fileName() ) );
121 continue;
122 }
123
124 // looks like an auth method plugin. get the key and description
125 description_t *pDesc = reinterpret_cast< description_t * >( cast_to_fptr( myLib.resolve( "description" ) ) );
126 if ( !pDesc )
127 {
128 QgsDebugMsg( QStringLiteral( "Checking %1: ...invalid (no description method)" ).arg( myLib.fileName() ) );
129 continue;
130 }
131
132 methodkey_t *pKey = reinterpret_cast< methodkey_t * >( cast_to_fptr( myLib.resolve( "authMethodKey" ) ) );
133 if ( !pKey )
134 {
135 QgsDebugMsg( QStringLiteral( "Checking %1: ...invalid (no authMethodKey method)" ).arg( myLib.fileName() ) );
136 continue;
137 }
138
139 // add this auth method to the method map
140 mAuthMethods[pKey()] = new QgsAuthMethodMetadata( pKey(), pDesc(), myLib.fileName() );
141
142 }
143 }
144
145 // typedef for the unload auth method function
146 typedef void cleanupAuthMethod_t();
147
~QgsAuthMethodRegistry()148 QgsAuthMethodRegistry::~QgsAuthMethodRegistry()
149 {
150 AuthMethods::const_iterator it = mAuthMethods.begin();
151
152 while ( it != mAuthMethods.end() )
153 {
154 QgsDebugMsgLevel( QStringLiteral( "cleanup: %1" ).arg( it->first ), 5 );
155 QString lib = it->second->library();
156 QLibrary myLib( lib );
157 if ( myLib.isLoaded() )
158 {
159 cleanupAuthMethod_t *cleanupFunc = reinterpret_cast< cleanupAuthMethod_t * >( cast_to_fptr( myLib.resolve( "cleanupAuthMethod" ) ) );
160 if ( cleanupFunc )
161 cleanupFunc();
162 }
163 // clear cached QgsAuthMethodMetadata *
164 delete it->second;
165 ++it;
166 }
167 }
168
169
170 /**
171 * Convenience function for finding any existing auth methods that match "authMethodKey"
172
173 Necessary because [] map operator will create a QgsProviderMetadata
174 instance. Also you cannot use the map [] operator in const members for that
175 very reason. So there needs to be a convenient way to find an auth method
176 without accidentally adding a null meta data item to the metadata map.
177 */
findMetadata_(QgsAuthMethodRegistry::AuthMethods const & metaData,QString const & authMethodKey)178 static QgsAuthMethodMetadata *findMetadata_( QgsAuthMethodRegistry::AuthMethods const &metaData,
179 QString const &authMethodKey )
180 {
181 QgsAuthMethodRegistry::AuthMethods::const_iterator i =
182 metaData.find( authMethodKey );
183
184 if ( i != metaData.end() )
185 {
186 return i->second;
187 }
188
189 return nullptr;
190 } // findMetadata_
191
192
library(const QString & authMethodKey) const193 QString QgsAuthMethodRegistry::library( const QString &authMethodKey ) const
194 {
195 QgsAuthMethodMetadata *md = findMetadata_( mAuthMethods, authMethodKey );
196
197 if ( md )
198 {
199 return md->library();
200 }
201
202 return QString();
203 }
204
pluginList(bool asHtml) const205 QString QgsAuthMethodRegistry::pluginList( bool asHtml ) const
206 {
207 AuthMethods::const_iterator it = mAuthMethods.begin();
208
209 if ( mAuthMethods.empty() )
210 {
211 return QObject::tr( "No authentication method plugins are available." );
212 }
213
214 QString list;
215
216 if ( asHtml )
217 {
218 list += QLatin1String( "<ol>" );
219 }
220
221 while ( it != mAuthMethods.end() )
222 {
223 if ( asHtml )
224 {
225 list += QLatin1String( "<li>" );
226 }
227
228 list += it->second->description();
229
230 if ( asHtml )
231 {
232 list += QLatin1String( "<br></li>" );
233 }
234 else
235 {
236 list += '\n';
237 }
238
239 ++it;
240 }
241
242 if ( asHtml )
243 {
244 list += QLatin1String( "</ol>" );
245 }
246
247 return list;
248 }
249
libraryDirectory() const250 QDir QgsAuthMethodRegistry::libraryDirectory() const
251 {
252 return mLibraryDirectory;
253 }
254
setLibraryDirectory(const QDir & path)255 void QgsAuthMethodRegistry::setLibraryDirectory( const QDir &path )
256 {
257 mLibraryDirectory = path;
258 }
259
260
261 // typedef for the QgsDataProvider class factory
262 typedef QgsAuthMethod *classFactoryFunction_t();
263
authMethod(const QString & authMethodKey)264 std::unique_ptr<QgsAuthMethod> QgsAuthMethodRegistry::authMethod( const QString &authMethodKey )
265 {
266 // load the plugin
267 QString lib = library( authMethodKey );
268
269 #ifdef TESTAUTHMETHODLIB
270 const char *cLib = lib.toUtf8();
271
272 // test code to help debug auth method plugin loading problems
273 // void *handle = dlopen(cLib, RTLD_LAZY);
274 void *handle = dlopen( cOgrLib, RTLD_LAZY | RTLD_GLOBAL );
275 if ( !handle )
276 {
277 QgsLogger::warning( "Error in dlopen" );
278 }
279 else
280 {
281 QgsDebugMsg( QStringLiteral( "dlopen succeeded" ) );
282 dlclose( handle );
283 }
284
285 #endif
286 // load the auth method
287 QLibrary myLib( lib );
288
289 QgsDebugMsgLevel( "Auth method library name is " + myLib.fileName(), 2 );
290 if ( !myLib.load() )
291 {
292 QgsMessageLog::logMessage( QObject::tr( "Failed to load %1: %2" ).arg( lib, myLib.errorString() ) );
293 return nullptr;
294 }
295
296 classFactoryFunction_t *classFactory = reinterpret_cast< classFactoryFunction_t * >( cast_to_fptr( myLib.resolve( "classFactory" ) ) );
297 if ( !classFactory )
298 {
299 QgsDebugMsg( QStringLiteral( "Failed to load %1: no classFactory method" ).arg( lib ) );
300 return nullptr;
301 }
302
303 std::unique_ptr< QgsAuthMethod > authMethod( classFactory() );
304 if ( !authMethod )
305 {
306 QgsMessageLog::logMessage( QObject::tr( "Unable to instantiate the auth method plugin %1" ).arg( lib ) );
307 myLib.unload();
308 return nullptr;
309 }
310
311 QgsDebugMsgLevel( QStringLiteral( "Instantiated the auth method plugin: %1" ).arg( authMethod->key() ), 2 );
312 return authMethod;
313 }
314
315 typedef QWidget *editFactoryFunction_t( QWidget *parent );
316
editWidget(const QString & authMethodKey,QWidget * parent)317 QWidget *QgsAuthMethodRegistry::editWidget( const QString &authMethodKey, QWidget *parent )
318 {
319 editFactoryFunction_t *editFactory =
320 reinterpret_cast< editFactoryFunction_t * >( cast_to_fptr( function( authMethodKey, QStringLiteral( "editWidget" ) ) ) );
321
322 if ( !editFactory )
323 return nullptr;
324
325 return editFactory( parent );
326 }
327
function(QString const & authMethodKey,QString const & functionName)328 QFunctionPointer QgsAuthMethodRegistry::function( QString const &authMethodKey,
329 QString const &functionName )
330 {
331 QLibrary myLib( library( authMethodKey ) );
332
333 QgsDebugMsg( "Library name is " + myLib.fileName() );
334
335 if ( myLib.load() )
336 {
337 return myLib.resolve( functionName.toLatin1().data() );
338 }
339 else
340 {
341 QgsDebugMsg( "Cannot load library: " + myLib.errorString() );
342 return nullptr;
343 }
344 }
345
authMethodLibrary(const QString & authMethodKey) const346 std::unique_ptr<QLibrary> QgsAuthMethodRegistry::authMethodLibrary( const QString &authMethodKey ) const
347 {
348 std::unique_ptr< QLibrary > myLib( new QLibrary( library( authMethodKey ) ) );
349
350 QgsDebugMsg( "Library name is " + myLib->fileName() );
351
352 if ( myLib->load() )
353 return myLib;
354
355 QgsDebugMsg( "Cannot load library: " + myLib->errorString() );
356 return nullptr;
357 }
358
authMethodList() const359 QStringList QgsAuthMethodRegistry::authMethodList() const
360 {
361 QStringList lst;
362 for ( AuthMethods::const_iterator it = mAuthMethods.begin(); it != mAuthMethods.end(); ++it )
363 {
364 lst.append( it->first );
365 }
366 return lst;
367 }
368
authMethodMetadata(const QString & authMethodKey) const369 const QgsAuthMethodMetadata *QgsAuthMethodRegistry::authMethodMetadata( const QString &authMethodKey ) const
370 {
371 return findMetadata_( mAuthMethods, authMethodKey );
372 }
373