add way to store plain QByteArray instead of only QString and make naming more generic in the process

This commit is contained in:
Frank Osterfeld 2011-10-27 21:36:51 +02:00
parent 8d590e0729
commit 3751a18e71
5 changed files with 88 additions and 53 deletions

View File

@ -32,28 +32,37 @@ QString Keychain::errorString() const {
return d->errorString; return d->errorString;
} }
void Keychain::writePassword( const QString& account, const QString& password, OverwriteMode om ) { void Keychain::writePassword( const QString &key, const QString &password, OverwriteMode om ) {
writeEntry( key, password.toUtf8(), om );
}
void Keychain::writeEntry( const QString& key, const QByteArray& ba, OverwriteMode om ) {
QString err; QString err;
const Error ret = d->writePasswordImpl( account, password, om, &err ); const Error ret = d->writeEntryImpl( key, ba, om, &err );
d->error = ret; d->error = ret;
d->errorString = err; d->errorString = err;
} }
QString Keychain::readPassword( const QString& account ) { QString Keychain::readPassword( const QString& key ) {
const QByteArray ba = readEntry( key );
return QString::fromUtf8( ba.constData(), ba.size() );
}
QByteArray Keychain::readEntry( const QString& key ) {
QString err; QString err;
QString pw; QByteArray pw;
const Error ret = d->readPasswordImpl( &pw, account, &err ); const Error ret = d->readEntryImpl( &pw, key, &err );
d->error = ret; d->error = ret;
d->errorString = err; d->errorString = err;
if ( ret != NoError ) if ( ret != NoError )
return QString(); return QByteArray();
else else
return pw; return pw;
} }
void Keychain::deletePassword( const QString& account ) { void Keychain::deleteEntry( const QString& key ) {
QString err; QString err;
const Error ret = d->deletePasswordImpl( account, &err ); const Error ret = d->deleteEntryImpl( key, &err );
d->error = ret; d->error = ret;
d->errorString = err; d->errorString = err;
} }

View File

@ -13,7 +13,14 @@
namespace QKeychain { namespace QKeychain {
/** /**
* Provides access to platform-specific key stores for secure persistence of
* passwords and other sensitive user data.
* *
* On Windows, TODO
* On Mac OS X, the OS X keychain is used.
* On other Unixes, TODO
*
* TODO we don't guarantee anything
*/ */
class Keychain { class Keychain {
public: public:
@ -35,11 +42,11 @@ public:
*/ */
enum Error { enum Error {
NoError=0, /*< No error occurred, operation was successful */ NoError=0, /*< No error occurred, operation was successful */
PasswordNotFound, /*< For the given account no password was found */ EntryNotFound, /*< For the given key no data was found */
CouldNotDeletePassword, /*< Could not delete existing password */ CouldNotDeleteEntry, /*< Could not delete existing secret data */
AccessDeniedByUser, /*< User denied access to keychain */ AccessDeniedByUser, /*< User denied access to keychain */
AccessDenied, /*< Access denied for other reasons */ AccessDenied, /*< Access denied for other reasons */
EntryAlreadyExists, /*< There is already a password for the given account and overwriting was not enforced */ EntryAlreadyExists, /*< There is already an entry for the given key and overwriting was not enforced */
OtherError /*< Something else went wrong (errorString() might provide details) */ OtherError /*< Something else went wrong (errorString() might provide details) */
}; };
@ -48,7 +55,7 @@ public:
*/ */
enum OverwriteMode { enum OverwriteMode {
DoNotOverwrite, /*< Do not overwrite existing entries */ DoNotOverwrite, /*< Do not overwrite existing entries */
ForceOverwrite /*< Replace old passowrd by new one */ ForceOverwrite /*< Replace old data by new one */
}; };
/** /**
@ -67,32 +74,52 @@ public:
QString errorString() const; QString errorString() const;
/** /**
* Stores a @p password in the keychain, for a given @p account. * Stores a @p password in the keychain, for a given @p key.
* error() and errorString() hold the result of the write operation. * error() and errorString() hold the result of the write operation.
* *
* @param account the account to store a password for * @param key the key to store a password for
* @param the password to store * @param password the password to store
* @param om Whether to overwrite existing passwords * @param om Whether to overwrite existing passwords
*/ */
void writePassword( const QString& account, void writePassword( const QString& key,
const QString& password, const QString& password,
OverwriteMode om=DoNotOverwrite ); OverwriteMode om=DoNotOverwrite );
/** /**
* Reads the @p password for an @p account from the keychain. * Stores @p data in the keychain, for a given @p key.
* error() and errorString() hold the result of the read operation. * error() and errorString() hold the result of the write operation.
* *
* @param account the account ot read the password for * @param key the key to store a password for
* @param data the data to store
* @param om Whether to overwrite existing passwords
*/ */
QString readPassword( const QString& account ); void writeEntry( const QString& key,
const QByteArray& data,
OverwriteMode om=DoNotOverwrite );
/** /**
* Deletes the @p password for an @p account from the keychain. * Reads the password for a given @p key from the keychain.
* error() and errorString() hold the result of the read operation. * error() and errorString() hold the result of the read operation.
* *
* @param account The account to delete the password for * @param key the key ot read the password for
*/ */
void deletePassword( const QString& account ); QString readPassword( const QString& key );
/**
* Reads data for a given @p key from the keychain.
* error() and errorString() hold the result of the read operation.
*
* @param key the key ot read the password for
*/
QByteArray readEntry( const QString& key );
/**
* Deletes the data for a @p key from the keychain.
* error() and errorString() hold the result of the delete operation.
*
* @param key The key to delete the data for
*/
void deleteEntry( const QString& key );
private: private:
class Private; class Private;

View File

@ -32,7 +32,7 @@ static QString strForStatus( OSStatus os ) {
return QString::fromUtf8( buf, strlen( buf ) ); return QString::fromUtf8( buf, strlen( buf ) );
} }
static OSStatus readPw( QString* pw, static OSStatus readPw( QByteArray* pw,
const QString& service, const QString& service,
const QString& account, const QString& account,
SecKeychainItemRef* ref ) { SecKeychainItemRef* ref ) {
@ -53,7 +53,7 @@ static OSStatus readPw( QString* pw,
&data, &data,
ref ); ref );
if ( ret == noErr ) { if ( ret == noErr ) {
*pw = QString::fromUtf8( reinterpret_cast<const char*>( data ), len ); *pw = QByteArray( reinterpret_cast<const char*>( data ), len );
const OSStatus ret2 = SecKeychainItemFreeContent ( 0, data ); const OSStatus ret2 = SecKeychainItemFreeContent ( 0, data );
if ( ret2 != noErr ) if ( ret2 != noErr )
qWarning() << "Could not free item content: " << strForStatus( ret2 ); qWarning() << "Could not free item content: " << strForStatus( ret2 );
@ -61,9 +61,9 @@ static OSStatus readPw( QString* pw,
return ret; return ret;
} }
Keychain::Error Keychain::Private::readPasswordImpl( QString* pw, Keychain::Error Keychain::Private::readEntryImpl( QByteArray* pw,
const QString& account, const QString& account,
QString* err ) { QString* err ) {
Q_ASSERT( pw ); Q_ASSERT( pw );
Q_ASSERT( err ); Q_ASSERT( err );
err->clear(); err->clear();
@ -73,29 +73,28 @@ Keychain::Error Keychain::Private::readPasswordImpl( QString* pw,
return NoError; return NoError;
case errSecItemNotFound: case errSecItemNotFound:
*err = tr("Password not found"); *err = tr("Password not found");
return PasswordNotFound; return EntryNotFound;
default: default:
*err = strForStatus( ret ); *err = strForStatus( ret );
return OtherError; return OtherError;
} }
} }
Keychain::Error Keychain::Private::writePasswordImpl( const QString& account, Keychain::Error Keychain::Private::writeEntryImpl( const QString& account,
const QString& password, const QByteArray& data,
Keychain::OverwriteMode ov, Keychain::OverwriteMode ov,
QString* err ) { QString* err ) {
Q_ASSERT( err ); Q_ASSERT( err );
err->clear(); err->clear();
const QByteArray serviceData = service.toUtf8(); const QByteArray serviceData = service.toUtf8();
const QByteArray accountData = account.toUtf8(); const QByteArray accountData = account.toUtf8();
const QByteArray passwordData = password.toUtf8();
const OSStatus ret = SecKeychainAddGenericPassword( NULL, //default keychain const OSStatus ret = SecKeychainAddGenericPassword( NULL, //default keychain
serviceData.size(), serviceData.size(),
serviceData.constData(), serviceData.constData(),
accountData.size(), accountData.size(),
accountData.constData(), accountData.constData(),
passwordData.size(), data.size(),
passwordData.constData(), data.constData(),
NULL //item reference NULL //item reference
); );
if ( ret != noErr ) { if ( ret != noErr ) {
@ -106,11 +105,11 @@ Keychain::Error Keychain::Private::writePasswordImpl( const QString& account,
*err = tr("Entry already exists"); *err = tr("Entry already exists");
return EntryAlreadyExists; return EntryAlreadyExists;
} }
Error derr = deletePasswordImpl( account, err ); Error derr = deleteEntryImpl( account, err );
if ( derr != NoError ) if ( derr != NoError )
return CouldNotDeletePassword; return CouldNotDeleteEntry;
else else
return writePasswordImpl( account, password, ov, err ); return writeEntryImpl( account, data, ov, err );
} }
default: default:
*err = strForStatus( ret ); *err = strForStatus( ret );
@ -121,10 +120,10 @@ Keychain::Error Keychain::Private::writePasswordImpl( const QString& account,
return NoError; return NoError;
} }
Keychain::Error Keychain::Private::deletePasswordImpl( const QString& account, Keychain::Error Keychain::Private::deleteEntryImpl( const QString& account,
QString* err ) { QString* err ) {
SecKeychainItemRef ref; SecKeychainItemRef ref;
QString pw; QByteArray pw;
const OSStatus ret1 = readPw( &pw, service, account, &ref ); const OSStatus ret1 = readPw( &pw, service, account, &ref );
if ( ret1 == errSecItemNotFound ) if ( ret1 == errSecItemNotFound )
return NoError; // No item stored, we're done return NoError; // No item stored, we're done
@ -141,6 +140,6 @@ Keychain::Error Keychain::Private::deletePasswordImpl( const QString& account,
return NoError; return NoError;
//TODO map error code //TODO map error code
*err = strForStatus( ret2 ); *err = strForStatus( ret2 );
return CouldNotDeletePassword; return CouldNotDeleteEntry;
} }

View File

@ -19,15 +19,15 @@ class Keychain::Private {
public: public:
explicit Private( const QString& s ) : service( s ) {} explicit Private( const QString& s ) : service( s ) {}
Keychain::Error writePasswordImpl( const QString& account, Keychain::Error writeEntryImpl( const QString& account,
const QString& password, const QByteArray& data,
Keychain::OverwriteMode, Keychain::OverwriteMode,
QString* errorString ); QString* errorString );
Keychain::Error deletePasswordImpl( const QString& account, Keychain::Error deleteEntryImpl( const QString& account,
QString* errorString ); QString* errorString );
Keychain::Error readPasswordImpl( QString* password, Keychain::Error readEntryImpl( QByteArray* password,
const QString& account, const QString& account,
QString* errorString ); QString* errorString );
const QString service; const QString service;
Keychain::Error error; Keychain::Error error;

View File

@ -66,7 +66,7 @@ int main( int argc, char** argv ) {
if ( ++it != args.constEnd() ) if ( ++it != args.constEnd() )
return printUsage(); return printUsage();
Keychain k( QLatin1String("qtkeychain-testclient") ); Keychain k( QLatin1String("qtkeychain-testclient") );
k.deletePassword( acc ); k.deleteEntry( acc );
if ( k.error() ) { if ( k.error() ) {
std::cerr << "Deleting password failed: " << qPrintable(k.errorString()) << std::endl; std::cerr << "Deleting password failed: " << qPrintable(k.errorString()) << std::endl;
return 1; return 1;