Skip to content

Commit cf826de

Browse files
committed
qtkeychain on sensitive data
1 parent 35ebb0b commit cf826de

File tree

2 files changed

+133
-83
lines changed

2 files changed

+133
-83
lines changed

core/merginuserauth.cpp

Lines changed: 123 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,42 @@ void MerginUserAuth::clear()
2424
mTokenExpiration.setTime( QTime() );
2525
mUserId = -1;
2626

27+
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
28+
deleteKey( "username" );
29+
deleteKey( "password" );
30+
deleteKey( "userId" );
31+
deleteKey( "token" );
32+
deleteKey( "expire" );
33+
#else
34+
QSettings settings;
35+
settings.beginGroup( "Input/" );
36+
settings.remove( "username" );
37+
settings.remove( "password" );
38+
settings.remove( "userId" );
39+
settings.remove( "token" );
40+
settings.remove( "expire" );
41+
settings.endGroup();
42+
#endif
43+
2744
emit authChanged();
2845
}
2946

3047
void MerginUserAuth::clearTokenData()
3148
{
3249
mTokenExpiration = QDateTime().currentDateTime().addDays( -42 ); // to make it expired arbitrary days ago
3350
mAuthToken.clear();
51+
52+
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
53+
deleteKey( QStringLiteral( "token" ) );
54+
deleteKey( QStringLiteral( "expire" ) );
55+
#else
56+
QSettings settings;
57+
settings.beginGroup( "Input/" );
58+
settings.remove( "token" );
59+
settings.remove( "expire" );
60+
settings.endGroup();
61+
#endif
62+
3463
emit authChanged();
3564
}
3665

@@ -56,48 +85,20 @@ void MerginUserAuth::saveAuthData()
5685
{
5786
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
5887
// mobile => QtKeychain
59-
const QString group = QStringLiteral( "Input/" );
60-
61-
auto writeJob = [this, group]( const QString & key, const QVariant & value )
62-
{
63-
auto *job = new QKeychain::WritePasswordJob( QStringLiteral( "mergin_maps_auth" ), this );
64-
job->setAutoDelete( false );
65-
job->setKey( group + key );
66-
67-
if ( value.type() == QVariant::ByteArray )
68-
job->setTextData( QString::fromUtf8( value.toByteArray().toBase64() ) );
69-
else if ( value.type() == QVariant::DateTime )
70-
job->setTextData( value.toDateTime().toString( Qt::ISODate ) );
71-
else
72-
job->setTextData( value.toString() );
73-
74-
connect( job, &QKeychain::Job::finished, this, [this, job, key]()
75-
{
76-
if ( job->error() )
77-
{
78-
CoreUtils::log( "Auth", QString( "Keychain write error (%1): %2" ).arg( key, job->errorString() ) );
79-
}
80-
job->deleteLater();
81-
} );
82-
83-
job->start();
84-
};
85-
86-
writeJob( QStringLiteral( "username" ), mUsername );
87-
writeJob( QStringLiteral( "password" ), mPassword );
88-
writeJob( QStringLiteral( "userId" ), mUserId );
89-
writeJob( QStringLiteral( "token" ), mAuthToken );
90-
writeJob( QStringLiteral( "expire" ), mTokenExpiration );
91-
88+
writeKey( "username", mUsername );
89+
writeKey( "password", mPassword );
90+
writeKey( "userId", mUserId );
91+
writeKey( "token", mAuthToken );
92+
writeKey( "expire", mTokenExpiration );
9293
#else
9394
// desktop => QSettings
9495
QSettings settings;
95-
settings.beginGroup( QStringLiteral( "Input/" ) );
96-
settings.setValue( QStringLiteral( "username" ), mUsername );
97-
settings.setValue( QStringLiteral( "password" ), mPassword );
98-
settings.setValue( QStringLiteral( "userId" ), mUserId );
99-
settings.setValue( QStringLiteral( "token" ), mAuthToken );
100-
settings.setValue( QStringLiteral( "expire" ), mTokenExpiration );
96+
settings.beginGroup( "Input/" );
97+
settings.setValue( "username", mUsername );
98+
settings.setValue( "password", mPassword );
99+
settings.setValue( "userId", mUserId );
100+
settings.setValue( "token", mAuthToken );
101+
settings.setValue( "expire", mTokenExpiration );
101102
settings.endGroup();
102103
#endif
103104
}
@@ -106,53 +107,20 @@ void MerginUserAuth::loadAuthData()
106107
{
107108
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
108109
// mobile => QtKeychain
109-
const QString group = QStringLiteral( "Input/" );
110-
111-
auto readJob = [this, group]( const QString & key, auto & destination, std::function<typename std::decay<decltype( destination )>::type( const QString & )> converter )
112-
{
113-
auto *job = new QKeychain::ReadPasswordJob( QStringLiteral( "mergin_maps_auth" ), this );
114-
job->setAutoDelete( false );
115-
job->setKey( group + key );
116-
117-
connect( job, &QKeychain::Job::finished, this, [this, job, key, &destination, converter]()
118-
{
119-
if ( !job->error() && !job->textData().isEmpty() )
120-
{
121-
destination = converter( job->textData() );
122-
}
123-
else if ( job->error() )
124-
{
125-
CoreUtils::log( "Auth", QString( "Keychain read error (%1): %2" ).arg( key, job->errorString() ) );
126-
}
127-
job->deleteLater();
128-
} );
129-
130-
job->start();
131-
};
132-
133-
readJob( QStringLiteral( "username" ), mUsername, []( const QString & v ) { return v; } );
134-
readJob( QStringLiteral( "password" ), mPassword, []( const QString & v ) { return v; } );
135-
readJob( QStringLiteral( "userId" ), mUserId, []( const QString & v ) { return v.toInt(); } );
136-
137-
readJob( QStringLiteral( "token" ), mAuthToken, []( const QString & v )
138-
{
139-
return QByteArray::fromBase64( v.toUtf8() );
140-
} );
141-
142-
readJob( QStringLiteral( "expire" ), mTokenExpiration, []( const QString & v )
143-
{
144-
return QDateTime::fromString( v, Qt::ISODate );
145-
} );
146-
110+
readKey( "username", mUsername, []( const QString & v ) { return v; } );
111+
readKey( "password", mPassword, []( const QString & v ) { return v; } );
112+
readKey( "userId", mUserId, []( const QString & v ) { return v.toInt(); } );
113+
readKey( "token", mAuthToken, []( const QString & v ) { return QByteArray::fromBase64( v.toUtf8() ); } );
114+
readKey( "expire", mTokenExpiration, []( const QString & v ) { return QDateTime::fromString( v, Qt::ISODate ); } );
147115
#else
148116
// desktop => QSettings
149117
QSettings settings;
150-
settings.beginGroup( QStringLiteral( "Input/" ) );
151-
mUsername = settings.value( QStringLiteral( "username" ) ).toString();
152-
mPassword = settings.value( QStringLiteral( "password" ) ).toString();
153-
mUserId = settings.value( QStringLiteral( "userId" ) ).toInt();
154-
mTokenExpiration = settings.value( QStringLiteral( "expire" ) ).toDateTime();
155-
mAuthToken = settings.value( QStringLiteral( "token" ) ).toByteArray();
118+
settings.beginGroup( "Input/" );
119+
mUsername = settings.value( "username" ).toString();
120+
mPassword = settings.value( "password" ).toString();
121+
mUserId = settings.value( "userId" ).toInt();
122+
mTokenExpiration = settings.value( "expire" ).toDateTime();
123+
mAuthToken = settings.value( "token" ).toByteArray();
156124
settings.endGroup();
157125
#endif
158126
}
@@ -216,3 +184,75 @@ bool MerginUserAuth::hasValidToken() const
216184
{
217185
return !mAuthToken.isEmpty() && mTokenExpiration >= QDateTime().currentDateTimeUtc();
218186
}
187+
188+
void MerginUserAuth::deleteKey( const QString &key )
189+
{
190+
auto *job = new QKeychain::DeletePasswordJob( "mergin_maps_auth", this );
191+
job->setAutoDelete( false );
192+
job->setKey( "Input/" + key );
193+
194+
connect( job, &QKeychain::Job::finished, this, [this, job, key]()
195+
{
196+
if ( job->error() )
197+
{
198+
CoreUtils::log( "Auth", QString( "Keychain delete error (%1): %2" ).arg( key, job->errorString() ) );
199+
}
200+
job->deleteLater();
201+
} );
202+
203+
job->start();
204+
}
205+
206+
void MerginUserAuth::writeKey( const QString &key, const QVariant &value )
207+
{
208+
auto *job = new QKeychain::WritePasswordJob( "mergin_maps_auth", this );
209+
job->setAutoDelete( false );
210+
job->setKey( "Input/" + key );
211+
212+
if ( value.type() == QVariant::ByteArray )
213+
{
214+
job->setTextData( QString::fromUtf8( value.toByteArray().toBase64() ) );
215+
}
216+
else if ( value.type() == QVariant::DateTime )
217+
{
218+
job->setTextData( value.toDateTime().toString( Qt::ISODate ) );
219+
}
220+
else
221+
{
222+
job->setTextData( value.toString() );
223+
}
224+
225+
connect( job, &QKeychain::Job::finished, this, [this, job, key]()
226+
{
227+
if ( job->error() )
228+
{
229+
CoreUtils::log( "Auth", QString( "Keychain write error (%1): %2" ).arg( key, job->errorString() ) );
230+
}
231+
job->deleteLater();
232+
} );
233+
234+
job->start();
235+
}
236+
237+
template <typename T, typename Converter>
238+
void MerginUserAuth::readKey( const QString &key, T &destination, Converter converter )
239+
{
240+
auto *job = new QKeychain::ReadPasswordJob( "mergin_maps_auth", this );
241+
job->setAutoDelete( false );
242+
job->setKey( "Input/" + key );
243+
244+
connect( job, &QKeychain::Job::finished, this, [this, job, key, &destination, converter]()
245+
{
246+
if ( !job->error() && !job->textData().isEmpty() )
247+
{
248+
destination = converter( job->textData() );
249+
}
250+
else if ( job->error() )
251+
{
252+
CoreUtils::log( "Auth", QString( "Keychain read error (%1): %2" ).arg( key, job->errorString() ) );
253+
}
254+
job->deleteLater();
255+
} );
256+
257+
job->start();
258+
}

core/merginuserauth.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,16 @@ class MerginUserAuth: public QObject
6262
void setFromJson( QJsonObject docObj );
6363

6464
private:
65+
//! Delete a key in keychain
66+
void deleteKey( const QString &key );
67+
68+
//! Write a key/value in keychain
69+
void writeKey( const QString &key, const QVariant &value );
70+
71+
//! Read a key from keychain into a destination variable
72+
template <typename T, typename Converter>
73+
void readKey( const QString &key, T &destination, Converter converter );
74+
6575
QString mUsername;
6676
QString mPassword;
6777
int mUserId = -1;

0 commit comments

Comments
 (0)