1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-03-13 20:39:57 +01:00

Move old code out of servent to acl registry; make ACL registry actually

do something.

Still needed: a page in settings to manage it
This commit is contained in:
Jeff Mitchell 2012-03-19 15:53:15 -04:00
parent e0f60931f7
commit 8ffd79d764
4 changed files with 152 additions and 115 deletions

View File

@ -18,10 +18,11 @@
#include "aclregistry.h"
#include <QMutexLocker>
#include <QThread>
#include <QVariant>
#include "tomahawksettings.h"
#include "tomahawkapp.h"
#include "utils/logger.h"
@ -53,17 +54,20 @@ ACLRegistry::~ACLRegistry()
ACLRegistry::ACL
ACLRegistry::isAuthorizedPeer( const QString& dbid, ACLRegistry::ACL globalType )
ACLRegistry::isAuthorizedPeer( const QString& dbid, ACLRegistry::ACL globalType, const QString &username )
{
// qDebug() << Q_FUNC_INFO;
QMutexLocker locker( &m_cacheMutex );
if ( QThread::currentThread() != TOMAHAWK_APPLICATION::instance()->thread() )
return globalType;
qDebug() << "Current cache keys =" << m_cache.keys();
// qDebug() << "Looking up dbid";
if( m_cache.contains( dbid ) )
{
QVariantHash peerHash = m_cache[ dbid ].toHash();
if( peerHash.contains( "global" ) )
{
registerAlias( dbid, username );
return ACLRegistry::ACL( peerHash[ "global" ].toInt() );
}
if ( globalType == ACLRegistry::NotFound )
return globalType;
@ -71,54 +75,54 @@ ACLRegistry::isAuthorizedPeer( const QString& dbid, ACLRegistry::ACL globalType
peerHash[ "global" ] = int( globalType );
m_cache[ dbid ] = peerHash;
save();
registerAlias( dbid, username );
return globalType;
}
//not found
if ( globalType == ACLRegistry::NotFound )
return globalType;
ACLRegistry::ACL acl = globalType;
tDebug( LOGVERBOSE ) << "ACL is intially" << acl;
#ifndef ENABLE_HEADLESS
acl = getUserDecision( username );
tDebug( LOGVERBOSE ) << "after getUserDecision acl is" << acl;
#endif
if ( acl == ACLRegistry::NotFound || acl == ACLRegistry::AllowOnce || acl == ACLRegistry::DenyOnce )
return acl;
QVariantHash peerHash;
peerHash[ "global" ] = int( globalType );
peerHash[ "global" ] = int( acl );
m_cache[ dbid ] = peerHash;
save();
return globalType;
registerAlias( dbid, username );
return acl;
}
void
ACLRegistry::registerPeer( const QString& dbid, ACLRegistry::ACL globalType, const QString &username )
{
// qDebug() << Q_FUNC_INFO;
if( globalType == ACLRegistry::NotFound )
if ( QThread::currentThread() != TOMAHAWK_APPLICATION::instance()->thread() )
return;
if ( globalType == ACLRegistry::NotFound || globalType == ACLRegistry::DenyOnce || globalType == ACLRegistry::AllowOnce )
return;
QMutexLocker locker( &m_cacheMutex );
QVariantHash peerHash;
if( m_cache.contains( dbid ) )
if ( m_cache.contains( dbid ) )
peerHash = m_cache[ dbid ].toHash();
peerHash[ "global" ] = int( globalType );
if ( !username.isEmpty() )
{
if ( peerHash.contains( "usernames" ) )
{
if ( !peerHash[ "usernames" ].toStringList().contains( username ) )
peerHash[ "usernames" ] = peerHash[ "usernames" ].toStringList() + QStringList( username );
}
else
peerHash[ "usernames" ] = QStringList( username );
}
m_cache[ dbid ] = peerHash;
save();
registerAlias( dbid, username );
}
QPair< QString, ACLRegistry::ACL >
ACLRegistry::isAuthorizedUser( const QString &username, ACLRegistry::ACL globalType )
{
// qDebug() << Q_FUNC_INFO;
QMutexLocker locker( &m_cacheMutex );
if ( QThread::currentThread() != TOMAHAWK_APPLICATION::instance()->thread() )
return QPair< QString, ACLRegistry::ACL >( QString(), ACLRegistry::NotFound );
qDebug() << "Current cache keys =" << m_cache.keys();
foreach ( QString dbid, m_cache.keys() )
{
@ -145,6 +149,31 @@ ACLRegistry::isAuthorizedUser( const QString &username, ACLRegistry::ACL globalT
}
void
ACLRegistry::registerAlias( const QString& dbid, const QString &username )
{
if ( QThread::currentThread() != TOMAHAWK_APPLICATION::instance()->thread() )
return;
if ( dbid.isEmpty() || username.isEmpty() )
return;
if ( !m_cache.contains( dbid ) )
return;
QVariantHash peerHash = m_cache[ dbid ].toHash();
if ( !peerHash.contains( "usernames" ) )
peerHash[ "usernames" ] = QStringList( username );
else if ( !peerHash[ "usernames" ].toStringList().contains( username ) )
peerHash[ "usernames" ] = peerHash[ "usernames" ].toStringList() + QStringList( username );
else
return;
m_cache[ dbid ] = peerHash;
save();
}
// ACLRegistry::ACL
// ACLRegistry::isAuthorizedPath( const QString& dbid, const QString& path )
// {
@ -181,6 +210,43 @@ ACLRegistry::isAuthorizedUser( const QString &username, ACLRegistry::ACL globalT
// m_cache[dbid] = peerHash;
// }
#ifndef ENABLE_HEADLESS
#include <QMessageBox>
ACLRegistry::ACL
ACLRegistry::getUserDecision( const QString &username )
{
QMessageBox msgBox;
msgBox.setIcon( QMessageBox::Question );
msgBox.setText( tr( "Connect to Peer?" ) );
msgBox.setInformativeText( tr( "Another Tomahawk instance that claims to be owned by %1 is attempting to connect to you. Select whether to allow or deny this connection.\n\nRemember: Only allow peers to connect if you trust who they are and if you have the legal right for them to stream music from you.").arg( username ) );
QPushButton *denyButton = msgBox.addButton( tr( "Deny" ), QMessageBox::HelpRole );
QPushButton *alwaysDenyButton = msgBox.addButton( tr( "Always Deny" ), QMessageBox::YesRole );
QPushButton *allowButton = msgBox.addButton( tr( "Allow" ), QMessageBox::NoRole );
QPushButton *alwaysAllowButton = msgBox.addButton( tr( "Always Allow" ), QMessageBox::ActionRole );
msgBox.setDefaultButton( allowButton );
msgBox.setEscapeButton( denyButton );
msgBox.exec();
if( msgBox.clickedButton() == denyButton )
return ACLRegistry::DenyOnce;
else if( msgBox.clickedButton() == alwaysDenyButton )
return ACLRegistry::Deny;
else if( msgBox.clickedButton() == allowButton )
return ACLRegistry::AllowOnce;
else if( msgBox.clickedButton() == alwaysAllowButton )
return ACLRegistry::Allow;
//How could we get here?
tDebug( LOGVERBOSE ) << "ERROR: returning NotFound";
Q_ASSERT( false );
return ACLRegistry::NotFound;
}
#endif
void
ACLRegistry::save()

View File

@ -26,6 +26,7 @@
#include <QMutex>
#include <QVariant>
#include "headlesscheck.h"
#include "dllmacro.h"
class DLLEXPORT ACLRegistry : public QObject
@ -39,7 +40,9 @@ public:
enum ACL {
Allow = 0,
Deny = 1,
NotFound = 2
NotFound = 2,
AllowOnce = 3,
DenyOnce = 4
};
ACLRegistry( QObject *parent = 0 );
@ -50,9 +53,10 @@ public:
*
* @param dbid DBID of peer
* @param globalType Global ACL to store if peer not found; if ACLRegistry::NotFound, does not store the peer Defaults to ACLRegistry::NotFound.
* @param username If not empty, will store the given username along with the new ACL value. Defaults to QString().
* @return ACLRegistry::ACL
**/
ACLRegistry::ACL isAuthorizedPeer( const QString &dbid, ACLRegistry::ACL globalType = ACLRegistry::NotFound );
ACLRegistry::ACL isAuthorizedPeer( const QString &dbid, ACLRegistry::ACL globalType = ACLRegistry::NotFound, const QString &username = QString() );
/**
* @brief Registers the global ACL value for this peer
@ -80,8 +84,12 @@ public:
* @param username Username of the peer to be added to the entry
* @return void
**/
void registerAlias( const QString &dbid, QString username );
void registerAlias( const QString &dbid, const QString &username );
#ifndef ENABLE_HEADLESS
ACLRegistry::ACL getUserDecision( const QString &username );
#endif
// ACLRegistry::ACL isAuthorizedPath( const QString &dbid, const QString &path );
// void authorizePath( const QString &dbid, const QString &path, ACLRegistry::ACL type );
@ -94,7 +102,6 @@ private:
void save();
QVariantHash m_cache;
QMutex m_cacheMutex;
static ACLRegistry* s_instance;
};

View File

@ -305,34 +305,34 @@ void
Servent::readyRead()
{
Q_ASSERT( this->thread() == QThread::currentThread() );
QTcpSocketExtra* sock = (QTcpSocketExtra*)sender();
QWeakPointer< QTcpSocketExtra > sock = (QTcpSocketExtra*)sender();
if( sock->_disowned )
if( sock.isNull() || sock.data()->_disowned )
{
return;
}
if( sock->_msg.isNull() )
if( sock.data()->_msg.isNull() )
{
char msgheader[ Msg::headerSize() ];
if( sock->bytesAvailable() < Msg::headerSize() )
if( sock.data()->bytesAvailable() < Msg::headerSize() )
return;
sock->read( (char*) &msgheader, Msg::headerSize() );
sock->_msg = Msg::begin( (char*) &msgheader );
sock.data()->read( (char*) &msgheader, Msg::headerSize() );
sock.data()->_msg = Msg::begin( (char*) &msgheader );
}
if( sock->bytesAvailable() < sock->_msg->length() )
if( sock.data()->bytesAvailable() < sock.data()->_msg->length() )
return;
QByteArray ba = sock->read( sock->_msg->length() );
sock->_msg->fill( ba );
Q_ASSERT( sock->_msg->is( Msg::JSON ) );
QByteArray ba = sock.data()->read( sock.data()->_msg->length() );
sock.data()->_msg->fill( ba );
Q_ASSERT( sock.data()->_msg->is( Msg::JSON ) );
ControlConnection* cc = 0;
bool ok;
QString key, conntype, nodeid, controlid;
QVariantMap m = parser.parse( sock->_msg->payload(), &ok ).toMap();
QVariantMap m = parser.parse( sock.data()->_msg->payload(), &ok ).toMap();
if( !ok )
{
tDebug() << "Invalid JSON on new connection, aborting";
@ -381,21 +381,31 @@ Servent::readyRead()
// they connected to us and want something we are offering
if ( conntype == "accept-offer" || conntype == "push-offer" )
{
sock->_msg.clear();
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << key << nodeid << "socket peer address = " << sock->peerAddress() << "socket peer name = " << sock->peerName();
Connection* conn = claimOffer( cc, nodeid, key, sock->peerAddress() );
if( !conn )
sock.data()->_msg.clear();
tDebug( LOGVERBOSE ) << Q_FUNC_INFO << key << nodeid << "socket peer address = " << sock.data()->peerAddress() << "socket peer name = " << sock.data()->peerName();
Connection* conn = claimOffer( cc, nodeid, key, sock.data()->peerAddress() );
if ( !conn )
{
tLog() << "claimOffer FAILED, key:" << key << nodeid;
goto closeconnection;
}
tDebug( LOGVERBOSE ) << "claimOffer OK:" << key << nodeid;
if ( sock.isNull() )
{
tLog() << "Socket has become null, possibly took too long to make an ACL decision, key:" << key << nodeid;
return;
}
else if ( !sock.data()->isValid() )
{
tLog() << "Socket has become invalid, possibly took too long to make an ACL decision, key:" << key << nodeid;
goto closeconnection;
}
tDebug( LOGVERBOSE ) << "claimOffer OK:" << key << nodeid;
m_connectedNodes << nodeid;
if( !nodeid.isEmpty() )
conn->setId( nodeid );
handoverSocket( conn, sock );
handoverSocket( conn, sock.data() );
return;
}
else
@ -406,8 +416,8 @@ Servent::readyRead()
// fallthru to cleanup:
closeconnection:
tLog() << "Closing incoming connection, something was wrong.";
sock->_msg.clear();
sock->disconnectFromHost();
sock.data()->_msg.clear();
sock.data()->disconnectFromHost();
}
@ -661,7 +671,7 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString
if( !nodeid.isEmpty() )
{
// If there isn't a nodeid it's not the first connection and will already have been stopped
if( !checkACL( conn.data(), nodeid, true ) )
if( !checkACL( conn, nodeid ) )
{
tLog() << "Connection not allowed due to ACL";
return NULL;
@ -694,64 +704,18 @@ Servent::claimOffer( ControlConnection* cc, const QString &nodeid, const QString
bool
Servent::checkACL( const Connection* conn, const QString &nodeid, bool showDialog ) const
Servent::checkACL( const QWeakPointer< Connection > conn, const QString &nodeid ) const
{
Q_UNUSED( conn );
Q_UNUSED( nodeid );
Q_UNUSED( showDialog );
/*
tDebug( LOGVERBOSE ) << "Checking ACLs";
ACLSystem* aclSystem = ACLSystem::instance();
ACLSystem::ACL peerStatus = aclSystem->isAuthorizedUser( nodeid );
if( peerStatus == ACLSystem::Deny )
if ( conn.isNull() )
return false;
tDebug( LOGVERBOSE ) << "Checking ACL for" << conn.data()->name();
ACLRegistry::ACL peerStatus = ACLRegistry::instance()->isAuthorizedPeer( nodeid, ACLRegistry::NotFound, conn.data()->name() );
tDebug( LOGVERBOSE ) << "ACL status is" << peerStatus;
if ( peerStatus == ACLRegistry::Allow || peerStatus == ACLRegistry::AllowOnce )
return true;
//FIXME: Actually enable it when it makes sense
//FIXME: needs refactoring because it depends on QtGui and the servent is part of libtomahawk-core
return true;
if( peerStatus == ACLSystem::NotFound )
{
if( !showDialog )
return false;
QMessageBox msgBox;
msgBox.setIcon( QMessageBox::Question );
msgBox.setText( tr( "Incoming Connection Attempt" ) );
msgBox.setInformativeText( tr( "Another Tomahawk instance is attempting to connect to you. Select whether to allow or deny this connection.\n\nPeer name: %1\nPeer ID: %2\n\nRemember: Only allow peers to connect if you have the legal right for them to stream music from you.").arg( conn->name(), nodeid ) );
QPushButton *denyButton = msgBox.addButton( tr( "Deny" ), QMessageBox::HelpRole );
QPushButton *alwaysDenyButton = msgBox.addButton( tr( "Always Deny" ), QMessageBox::YesRole );
QPushButton *allowButton = msgBox.addButton( tr( "Allow" ), QMessageBox::NoRole );
QPushButton *alwaysAllowButton = msgBox.addButton( tr( "Always Allow" ), QMessageBox::ActionRole );
msgBox.setDefaultButton( denyButton );
msgBox.setEscapeButton( denyButton );
msgBox.exec();
if( msgBox.clickedButton() == denyButton )
return false;
else if( msgBox.clickedButton() == alwaysDenyButton )
{
aclSystem->authorizeUser( nodeid, ACLSystem::Deny );
return false;
}
else if( msgBox.clickedButton() == alwaysAllowButton )
{
aclSystem->authorizeUser( nodeid, ACLSystem::Allow );
return true;
}
else if( msgBox.clickedButton() == allowButton )
return true;
//How could we get here?
Q_ASSERT( false );
return false;
}
*/
return true;
return false;
}

View File

@ -110,7 +110,7 @@ public:
QString externalAddress() const { return !m_externalHostname.isNull() ? m_externalHostname : m_externalAddress.toString(); }
int externalPort() const { return m_externalPort; }
QSharedPointer<QIODevice> remoteIODeviceFactory( const Tomahawk::result_ptr& );
QSharedPointer< QIODevice > remoteIODeviceFactory( const Tomahawk::result_ptr& );
static bool isIPWhitelisted( QHostAddress ip );
bool connectedToSession( const QString& session );
@ -118,10 +118,10 @@ public:
QList< StreamConnection* > streams() const { return m_scsessions; }
QSharedPointer<QIODevice> getIODeviceForUrl( const Tomahawk::result_ptr& result );
void registerIODeviceFactory( const QString &proto, boost::function<QSharedPointer<QIODevice>(Tomahawk::result_ptr)> fac );
QSharedPointer<QIODevice> localFileIODeviceFactory( const Tomahawk::result_ptr& result );
QSharedPointer<QIODevice> httpIODeviceFactory( const Tomahawk::result_ptr& result );
QSharedPointer< QIODevice > getIODeviceForUrl( const Tomahawk::result_ptr& result );
void registerIODeviceFactory( const QString &proto, boost::function< QSharedPointer< QIODevice >(Tomahawk::result_ptr) > fac );
QSharedPointer< QIODevice > localFileIODeviceFactory( const Tomahawk::result_ptr& result );
QSharedPointer< QIODevice > httpIODeviceFactory( const Tomahawk::result_ptr& result );
bool isReady() const { return m_ready; };
@ -154,12 +154,12 @@ private slots:
private:
bool isValidExternalIP( const QHostAddress& addr ) const;
void handoverSocket( Connection* conn, QTcpSocketExtra* sock );
bool checkACL( const Connection* conn, const QString &nodeid, bool showDialog ) const;
bool checkACL( const QWeakPointer< Connection > conn, const QString &nodeid ) const;
void printCurrentTransfers();
QJson::Parser parser;
QList< ControlConnection* > m_controlconnections; // canonical list of authed peers
QMap< QString, QWeakPointer<Connection> > m_offers;
QMap< QString, QWeakPointer< Connection > > m_offers;
QStringList m_connectedNodes;
int m_port, m_externalPort;
@ -172,7 +172,7 @@ private:
QList< StreamConnection* > m_scsessions;
QMutex m_ftsession_mut;
QMap< QString,boost::function<QSharedPointer<QIODevice>(Tomahawk::result_ptr)> > m_iofactories;
QMap< QString,boost::function< QSharedPointer< QIODevice >(Tomahawk::result_ptr) > > m_iofactories;
PortFwdThread* m_portfwd;
static Servent* s_instance;