1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-09-06 20:20:41 +02:00

Compare commits

...

10 Commits

Author SHA1 Message Date
Uwe L. Korn
4cdfd81fb5 Basic skeleton for api authentication 2013-07-18 10:04:39 +02:00
Uwe L. Korn
1bae47da51 Add a storage class for API users 2013-07-18 10:04:39 +02:00
Uwe L. Korn
6bf471ff13 Allow the usage of self-signed certificates for accessing the API 2013-07-18 10:04:39 +02:00
Uwe L. Korn
65e51944c2 Fix next command spelling 2013-07-18 10:04:39 +02:00
Uwe L. Korn
571f5c7e7d Use SSL/TLS for API 2.0
* Permanent storage for the x509 certificate is still missing
2013-07-18 10:04:39 +02:00
Uwe L. Korn
5e0ae4fda1 Add authentication scaffold 2013-07-18 10:04:39 +02:00
Uwe L. Korn
aebc8bcb8a DRY on playback api commands 2013-07-18 10:04:39 +02:00
Uwe L. Korn
ea8fec15d3 Custom error messages. 2013-07-18 10:04:39 +02:00
Uwe L. Korn
33473d469a Add AudioEngine remote contolling 2013-07-18 10:04:39 +02:00
Uwe L. Korn
1529d2e5d4 Add API 2.0 / Control API skeleton 2013-07-18 10:04:39 +02:00
15 changed files with 1101 additions and 20 deletions

View File

@@ -107,7 +107,15 @@ INCLUDE_DIRECTORIES(
)
IF( QXTWEB_FOUND )
LIST(APPEND tomahawkSources web/Api_v1.cpp)
LIST(APPEND tomahawkSources
web/Api_v1.cpp
web/Api_v2.cpp
web/Api_v2_0.cpp
web/Api2HttpsServerConnector.cpp
web/Api2User.cpp
web/apiv2/ACLItemDelegate.cpp
web/apiv2/ACLJobStatusItem.cpp
)
LIST(APPEND LINK_LIBRARIES ${QXTWEB_LIBRARIES})
INCLUDE_DIRECTORIES(${QXTWEB_INCLUDE_DIRS})
ENDIF()

View File

@@ -4,6 +4,7 @@
* Copyright 2010-2011, Leo Franchi <lfranchi@kde.org>
* Copyright 2010-2012, Jeff Mitchell <jeff@tomahawk-player.org>
* Copyright 2013, Teo Mrnjavac <teo@kde.org>
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -38,6 +39,8 @@
#include "network/Servent.h"
#include "network/DbSyncConnection.h"
#include "web/Api_v1.h"
#include "web/Api_v2.h"
#include "web/Api2HttpsServerConnector.h"
#include "SourceList.h"
#include "ViewManager.h"
#include "ShortcutHandler.h"
@@ -112,6 +115,11 @@
#include <QFile>
#include <QFileInfo>
#include <QTranslator>
#include <QUuid>
#include <QtCrypto>
#include <QSslKey>
#include <QxtNetwork/QxtSslServer>
#include <iostream>
@@ -490,36 +498,98 @@ TomahawkApp::initHTTP()
delete m_httpv1_session.data();
if ( !m_httpv1_connector.isNull() )
delete m_httpv1_connector.data();
if ( !m_httpv2_session.isNull() )
delete m_httpv2_session.data();
if ( !m_httpv2_connector.isNull() )
delete m_httpv2_connector.data();
return;
}
if ( m_httpv1_session )
{
tLog() << "HTTPd session already exists, returning";
return;
tLog() << "HTTPd session for API v1.0 already exists, not starting again";
}
m_httpv1_session = QPointer< QxtHttpSessionManager >( new QxtHttpSessionManager() );
m_httpv1_connector = QPointer< QxtHttpServerConnector >( new QxtHttpServerConnector );
if ( m_httpv1_session.isNull() || m_httpv1_connector.isNull() )
else
{
if ( !m_httpv1_session.isNull() )
delete m_httpv1_session.data();
if ( !m_httpv1_connector.isNull() )
delete m_httpv1_connector.data();
tLog() << "Failed to start HTTPd, could not create object";
return;
m_httpv1_session = QPointer< QxtHttpSessionManager >( new QxtHttpSessionManager() );
m_httpv1_connector = QPointer< QxtHttpServerConnector >( new QxtHttpServerConnector );
if ( m_httpv1_session.isNull() || m_httpv1_connector.isNull() )
{
if ( !m_httpv1_session.isNull() )
delete m_httpv1_session.data();
if ( !m_httpv1_connector.isNull() )
delete m_httpv1_connector.data();
tLog() << "Failed to start HTTPd for API v1.0, could not create object";
}
else
{
m_httpv1_session.data()->setPort( 60210 ); //TODO config
m_httpv1_session.data()->setListenInterface( QHostAddress::LocalHost );
m_httpv1_session.data()->setConnector( m_httpv1_connector.data() );
Api_v1* api = new Api_v1( m_httpv1_session.data() );
m_httpv1_session.data()->setStaticContentService( api );
tLog() << "Starting HTTPd for API v1.0 on" << m_httpv1_session.data()->listenInterface().toString() << m_httpv1_session.data()->port();
m_httpv1_session.data()->start();
}
}
m_httpv1_session.data()->setPort( 60210 ); //TODO config
m_httpv1_session.data()->setListenInterface( QHostAddress::LocalHost );
m_httpv1_session.data()->setConnector( m_httpv1_connector.data() );
if ( !m_httpv2_session.isNull() )
{
tLog() << "HTTPd session for API v2.0 already exists, not starting again";
}
else
{
m_httpv2_session = QPointer< QxtHttpSessionManager >( new QxtHttpSessionManager( this ) );
m_httpv2_connector = QPointer< QxtHttpsServerConnector >( new Api2HttpsServerConnector( this ) );
if ( m_httpv2_session.isNull() || m_httpv2_connector.isNull() )
{
if ( !m_httpv2_session.isNull() )
delete m_httpv2_session.data();
if ( !m_httpv2_connector.isNull() )
delete m_httpv2_connector.data();
tLog() << "Failed to start HTTPd for API v2.0, could not create object";
}
else
{
m_httpv2_session->setPort( 60211 ); //TODO config
#if QT_VERSION >= QT_VERSION_CHECK( 5, 0, 0 )
m_httpv2_session.data()->setListenInterface( QHostAddress::Any );
#else
m_httpv2_session.data()->setListenInterface( QHostAddress::AnyIPv6 );
#endif
Api_v1* api = new Api_v1( m_httpv1_session.data() );
m_httpv1_session.data()->setStaticContentService( api );
m_httpv2_session->setConnector( m_httpv2_connector.data() );
tLog() << "Starting HTTPd on" << m_httpv1_session.data()->listenInterface().toString() << m_httpv1_session.data()->port();
m_httpv1_session.data()->start();
Api_v2* api = new Api_v2( m_httpv2_session.data() );
m_httpv2_session->setStaticContentService( api );
QCA::KeyGenerator generator;
QCA::PrivateKey key = generator.createRSA( 4096 );
// TODO: Store and load this key with qtkeychain
QCA::CertificateOptions certOpts;
QCA::CertificateInfo certInfo;
certInfo.insert( QCA::CommonName, QString( "tomahawk-%1" ).arg( Database::instance()->impl()->dbid() ) );
certInfo.insert( QCA::Organization, "Tomahawk Player" );
certInfo.insert( QCA::OrganizationalUnit, "HTTP API" );
certOpts.setInfo( certInfo );
certOpts.setSerialNumber( QCA::BigInteger( QCA::SecureArray( QUuid::createUuid().toByteArray() ) ) );
// TODO: Check for validity of ceritifcate and renew if needed
certOpts.setValidityPeriod( QDateTime::currentDateTime(), QDateTime::currentDateTime().addYears( 3 ) );
QCA::Certificate cert( certOpts, key );
QSslCertificate qcert( cert.toDER(), QSsl::Der);
QSslKey qkey( key.toDER().toByteArray(), QSsl::Rsa, QSsl::Der );
QxtSslServer* sslServer = m_httpv2_connector->tcpServer();
sslServer->setPrivateKey(qkey);
sslServer->setLocalCertificate(qcert);
tLog() << "Starting HTTPd for API v2.0 on" << m_httpv2_session->listenInterface().toString() << m_httpv2_session->port();
if ( !m_httpv2_session->start() )
{
tLog() << "Failed to start HTTPd for API v2.0, could not bind to interface/port";
}
}
}
}

View File

@@ -150,6 +150,8 @@ private:
QPointer< QxtHttpServerConnector > m_httpv1_connector;
QPointer< QxtHttpSessionManager > m_httpv1_session;
QPointer< QxtHttpsServerConnector > m_httpv2_connector;
QPointer< QxtHttpSessionManager > m_httpv2_session;
};
Q_DECLARE_METATYPE( PairList )

View File

@@ -0,0 +1,53 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk 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 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Api2HttpsServerConnector.h"
#include "utils/Logger.h"
#include <QSslSocket>
Api2HttpsServerConnector::Api2HttpsServerConnector( QObject *parent )
: QxtHttpsServerConnector( parent )
{
}
void
Api2HttpsServerConnector::sslErrors( const QList<QSslError>& errors )
{
QList<QSslError> toIgnore;
QSslSocket* socket = static_cast<QSslSocket*>( sender() );
foreach ( QSslError error, errors )
{
if ( error.error() == QSslError::SelfSignedCertificate )
{
toIgnore.append( error );
}
else if ( error.error() == QSslError::CertificateUntrusted )
{
toIgnore.append( error );
}
else
{
tLog() << Q_FUNC_INFO << "Not ignoring SSL error" << error << error.error();
}
}
socket->ignoreSslErrors( toIgnore );
}

View File

@@ -0,0 +1,42 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk 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 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef API2HTTPSSERVERCONNECTOR_H
#define API2HTTPSSERVERCONNECTOR_H
#include <QxtWeb/QxtHttpServerConnector>
class Api2HttpsServerConnector : public QxtHttpsServerConnector
{
Q_OBJECT
public:
Api2HttpsServerConnector( QObject* parent );
protected:
/**
* We want to handle ourselves which SSL certificates are trusted.
*
* Ignore errors about untrusted and self-signed certificates.
*
* Already a slot in QxtHttpsServerConnector
*/
void sslErrors( const QList<QSslError>& errors );
};
#endif // API2HTTPSSERVERCONNECTOR_H

View File

@@ -0,0 +1,81 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk 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 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Api2User.h"
Api2User::Api2User( const QString& name )
: QObject( 0 )
, m_aclDecision( None )
, m_name( name )
{
}
Api2User::ACLDecision
Api2User::aclDecision() const
{
return m_aclDecision;
}
QString
Api2User::name() const
{
return m_name;
}
QSslKey
Api2User::pubkey() const
{
return m_pubkey;
}
void
Api2User::setPubkey( const QSslKey &pubkey )
{
m_pubkey = pubkey;
}
QString
Api2User::clientDescription() const
{
return m_clientDescription;
}
void
Api2User::setClientDescription( const QString& clientDescription )
{
m_clientDescription = clientDescription;
}
QString
Api2User::clientName() const
{
return m_clientName;
}
void
Api2User::setClientName( const QString& clientName )
{
m_clientName = clientName;
}

View File

@@ -0,0 +1,62 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk 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 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef API2USER_H
#define API2USER_H
#include <QObject>
#include <QSslKey>
class Api2User : public QObject
{
Q_OBJECT
public:
Api2User( const QString& name );
enum ACLDecision {
None, Deny, FullAccess
};
ACLDecision aclDecision() const;
void setAclDecision( ACLDecision decision );
QString clientDescription() const;
void setClientDescription( const QString& clientDescription );
QString clientName() const;
void setClientName( const QString& clientName );
QString name() const;
QSslKey pubkey() const;
void setPubkey( const QSslKey& pubkey );
signals:
public slots:
private:
ACLDecision m_aclDecision;
QString m_clientDescription;
QString m_clientName;
QString m_name;
QSslKey m_pubkey;
};
#endif // API2USER_H

107
src/tomahawk/web/Api_v2.cpp Normal file
View File

@@ -0,0 +1,107 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk 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 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Api_v2.h"
#include <QxtWeb/QxtWebPageEvent>
Api_v2::Api_v2( QxtAbstractWebSessionManager* sm, QObject* parent )
: QxtWebSlotService( sm, parent )
, m_apiv2_0( this )
{
}
void
Api_v2::sendJsonOk( QxtWebRequestEvent* event )
{
QxtWebPageEvent * e = new QxtWebPageEvent( event->sessionID, event->requestID, "{ result: \"ok\" }" );
e->contentType = "application/json";
postEvent( e );
}
void
Api_v2::sendJsonError( QxtWebRequestEvent* event, const QString& message )
{
QxtWebPageEvent * e = new QxtWebPageEvent( event->sessionID, event->requestID, QString( "{ result: \"error\", error: \"%1\" }" ).arg( message ).toUtf8().constData() );
e->contentType = "application/json";
e->status = 500;
e->statusMessage = "Method call failed.";
postEvent( e );
}
void
Api_v2::apiCallFailed( QxtWebRequestEvent* event, const QString& method )
{
sendPlain404( event, QString( "Method \"%1\" for API 2.0 not found" ).arg( method ), "Method in API 2.0 not found" );
}
void
Api_v2::api( QxtWebRequestEvent* event, const QString& version, const QString& method, const QString& arg1, const QString& arg2, const QString& arg3 )
{
if ( version == "2.0" )
{
if ( !arg3.isEmpty() )
{
if ( !QMetaObject::invokeMethod( &m_apiv2_0, method.toAscii().constData(), Q_ARG( QxtWebRequestEvent*, event ), Q_ARG( QString, arg1 ), Q_ARG( QString, arg2 ), Q_ARG( QString, arg3 ) ) )
{
apiCallFailed(event, method);
}
}
else if ( !arg2.isEmpty() )
{
if ( !QMetaObject::invokeMethod( &m_apiv2_0, method.toAscii().constData(), Q_ARG( QxtWebRequestEvent*, event ), Q_ARG( QString, arg1 ), Q_ARG( QString, arg2 ) ) )
{
apiCallFailed(event, method);
}
}
else if ( !arg1.isEmpty() )
{
if ( !QMetaObject::invokeMethod( &m_apiv2_0, method.toAscii().constData(), Q_ARG( QxtWebRequestEvent*, event ), Q_ARG( QString, arg1 ) ) )
{
apiCallFailed(event, method);
}
}
else
{
if ( !QMetaObject::invokeMethod( &m_apiv2_0, method.toAscii().constData(), Q_ARG( QxtWebRequestEvent*, event ) ) )
{
apiCallFailed(event, method);
}
}
}
else
{
sendPlain404( event, QString( "Unknown API version %1" ).arg( version ), "API version not found" );
}
}
void
Api_v2::sendPlain404( QxtWebRequestEvent* event, const QString& message, const QString& statusmessage )
{
QxtWebPageEvent * e = new QxtWebPageEvent( event->sessionID, event->requestID, message.toUtf8() );
e->contentType = "text/plain";
e->status = 404;
e->statusMessage = statusmessage.toAscii().constData();
postEvent( e );
}

54
src/tomahawk/web/Api_v2.h Normal file
View File

@@ -0,0 +1,54 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk 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 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef API_V2_H
#define API_V2_H
#include "Api_v2_0.h"
#include <QObject>
#include <QxtWeb/QxtWebSlotService>
class Api_v2 : public QxtWebSlotService
{
Q_OBJECT
public:
Api_v2( QxtAbstractWebSessionManager* sm, QObject* parent = 0 );
void sendJsonOk( QxtWebRequestEvent* event );
void sendJsonError( QxtWebRequestEvent* event, const QString& message );
void sendPlain404( QxtWebRequestEvent* event, const QString& message, const QString& statusmessage );
signals:
public slots:
/**
* All api (non-UI) calls with go to /api/<version>/method
*/
void api( QxtWebRequestEvent* event, const QString& version, const QString& method, const QString& arg1 = QString(), const QString& arg2 = QString(), const QString& arg3 = QString() );
private:
/**
* Method call failed, report failure.
*/
void apiCallFailed( QxtWebRequestEvent* event, const QString& method);
Api_v2_0 m_apiv2_0;
};
#endif // API_V2_H

View File

@@ -0,0 +1,187 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk 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 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Api_v2_0.h"
#include "audio/AudioEngine.h"
#include "jobview/JobStatusModel.h"
#include "jobview/JobStatusView.h"
#include "utils/Logger.h"
#include "web/apiv2/ACLJobStatusItem.h"
#include "Api_v2.h"
#include "Query.h"
#include <QSslKey>
#include <QxtWeb/QxtWebPageEvent>
#include <QxtWeb/QxtWebSlotService>
// Assumptions: QxtWebRequestEvent instance is called event and result is true on success
#define JSON_REPLY( result, message ) jsonReply( event, Q_FUNC_INFO, message, !result )
Api_v2_0::Api_v2_0( Api_v2* parent )
: QObject( parent )
, m_service( parent )
{
}
void
Api_v2_0::ping( QxtWebRequestEvent* event )
{
QxtWebPageEvent * e = new QxtWebPageEvent( event->sessionID, event->requestID, "pong" );
e->contentType = "text/plain";
m_service->postEvent( e );
}
void
Api_v2_0::playback( QxtWebRequestEvent* event, const QString& command )
{
if ( !checkAuthentication( event ) )
{
return;
}
if ( command == "next")
{
JSON_REPLY( QMetaObject::invokeMethod( AudioEngine::instance(), "next", Qt::QueuedConnection ) , "Skipping to the next track failed." );
}
else if ( command == "previous" )
{
JSON_REPLY( QMetaObject::invokeMethod( AudioEngine::instance(), "previous", Qt::QueuedConnection ), "Rewinding to the previous track failed." );
}
else if ( command == "playpause" )
{
JSON_REPLY( QMetaObject::invokeMethod( AudioEngine::instance(), "playpause", Qt::QueuedConnection ), "Play/Pause failed." );
}
else if ( command == "play" )
{
JSON_REPLY( QMetaObject::invokeMethod( AudioEngine::instance(), "play", Qt::QueuedConnection ), "Starting the playback failed." );
}
else if ( command == "pause" )
{
JSON_REPLY( QMetaObject::invokeMethod( AudioEngine::instance(), "pause", Qt::QueuedConnection ), "Pausing the current track failed." );
}
else if ( command == "stop" )
{
JSON_REPLY( QMetaObject::invokeMethod( AudioEngine::instance(), "stop", Qt::QueuedConnection ), "Stopping the current track failed." );
}
else if ( command == "lowervolume" )
{
JSON_REPLY( QMetaObject::invokeMethod( AudioEngine::instance(), "lowerVolume", Qt::QueuedConnection ), "Lowering volume failed." );
}
else if ( command == "raisevolume" )
{
JSON_REPLY( QMetaObject::invokeMethod( AudioEngine::instance(), "raiseVolume", Qt::QueuedConnection ), "Raising volume failed." );
}
else
{
m_service->sendJsonError( event, "No such playback command." );
}
}
void
Api_v2_0::requestauth( QxtWebRequestEvent *event )
{
tLog() << Q_FUNC_INFO;
if ( checkAuthentication( event ) )
{
// A decision was already made.
m_service->sendJsonOk( event );
return;
}
if ( event->isSecure && !event->clientCertificate.isNull() )
{
tLog() << Q_FUNC_INFO;
QSslCertificate certificate = event->clientCertificate;
QString clientName = certificate.subjectInfo( QSslCertificate::CommonName );
if ( m_users.contains( clientName ) && m_users.value( clientName )->aclDecision() == Api2User::Deny )
{
// Access was already denied
jsonUnauthenticated( event );
return;
}
QSharedPointer<Api2User> user( new Api2User( clientName ) );
user->setClientDescription( certificate.subjectInfo( QSslCertificate::OrganizationalUnitName ) );
user->setClientName( certificate.subjectInfo( QSslCertificate::Organization ) );
user->setPubkey( certificate.publicKey() );
// TODO: Do not readd users
m_users[ clientName ] = user;
// TODO: ACL decision
Tomahawk::APIv2::ACLJobStatusItem* job = new Tomahawk::APIv2::ACLJobStatusItem( user );
JobStatusView::instance()->model()->addJob( job );
}
else
{
// TODO
}
}
void
Api_v2_0::jsonReply( QxtWebRequestEvent* event, const char* funcInfo, const QString& errorMessage, bool isError )
{
if ( isError )
{
tLog( LOGVERBOSE ) << funcInfo << errorMessage;
m_service->sendJsonError( event, errorMessage );
}
else
{
m_service->sendJsonOk( event );
}
}
void
Api_v2_0::jsonUnauthenticated( QxtWebRequestEvent *event )
{
QxtWebPageEvent * e = new QxtWebPageEvent( event->sessionID, event->requestID, "{ result: \"error\", error: \"Method call needs to be authenticated.\" }" );
e->contentType = "application/json";
e->status = 401;
e->statusMessage = "Method call needs to be authenticated.";
m_service->postEvent( e );
}
bool
Api_v2_0::checkAuthentication( QxtWebRequestEvent* event )
{
if ( event->isSecure && !event->clientCertificate.isNull() )
{
// Using SSL Certificate authentication
QString clientName = event->clientCertificate.subjectInfo( QSslCertificate::CommonName );
QSslKey pubkey = event->clientCertificate.publicKey();
if ( m_users.contains( clientName ) )
{
const QSharedPointer<Api2User> user = m_users.value( clientName );
if ( user->aclDecision() == Api2User::FullAccess && user->pubkey() == pubkey )
{
return true;
}
}
}
// TODO: Auth!
// * Shared secret between two clients when talking via SSL
// * sth else when connecting without httpS
// * a more secure version of digest auth
return false;
}

View File

@@ -0,0 +1,86 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk 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 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef API_V2_0_H
#define API_V2_0_H
#include <QMap>
#include <QObject>
#include <QSharedPointer>
#include "Api2User.h"
class Api_v2;
class QxtWebRequestEvent;
class QxtWebSlotService;
class Api_v2_0 : public QObject
{
Q_OBJECT
public:
Api_v2_0( Api_v2* parent = 0 );
signals:
public slots:
/**
* Simple test to check for API 2.0 support.
*
* This call needs no authentication.
*/
void ping( QxtWebRequestEvent* event );
/**
* Control playback.
*
* This call needs to be authenticated.
*/
void playback( QxtWebRequestEvent* event, const QString& command );
/**
* Request that the client should be authorized.
*
* The request can be made via 2 methods:
* - If using SSL, the client sends his certificate as a standard peercerificate and supplies a shared secret.
* - Without SSL, the client encrypts a shared secret with the certificate from the server and signs it with its secret.
* The client certificate is supplied as another parameter
*/
void requestauth( QxtWebRequestEvent* event );
private:
/**
* Check the current HTTP request is correctly authenticated via any of the possible authentication schemes.
*/
bool checkAuthentication( QxtWebRequestEvent* event );
/**
* Send a simple reply to a (write-only) method call.
*
* On failure send a custom error message.
*/
void jsonReply( QxtWebRequestEvent* event, const char* funcInfo, const QString& errorMessage, bool isError );
/**
* Send a reply that the made call lacks the needed authentication
*/
void jsonUnauthenticated( QxtWebRequestEvent* event );
Api_v2* m_service;
QMap< QString, QSharedPointer< Api2User > > m_users;
};
#endif // API_V2_0_H

View File

@@ -0,0 +1,146 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk 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 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ACLItemDelegate.h"
#include "jobview/JobStatusModel.h"
#include "utils/TomahawkUtils.h"
#include "utils/TomahawkUtilsGui.h"
#include "ACLJobStatusItem.h"
#include <QApplication>
#include <QMouseEvent>
#include <QPainter>
namespace Tomahawk {
namespace APIv2 {
#define PADDING 2
ACLItemDelegate::ACLItemDelegate( QObject *parent )
: QStyledItemDelegate( parent )
{
}
ACLItemDelegate::~ACLItemDelegate()
{
}
void ACLItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
ACLJobStatusItem* item = dynamic_cast< ACLJobStatusItem* >( index.data( JobStatusModel::JobDataRole ).value< JobStatusItem* >() );
if ( !item )
return;
QStyleOptionViewItemV4 opt = option;
initStyleOption( &opt, index );
QFontMetrics fm( opt.font );
opt.state &= ~QStyle::State_MouseOver;
QApplication::style()->drawPrimitive( QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget );
painter->setRenderHint( QPainter::Antialiasing );
painter->fillRect( opt.rect, Qt::lightGray );
QString mainText = QString( tr( "%An instance of\n%1\nwants to control this player:" ) ).arg( item->user()->clientName() );
const QRect rRect( opt.rect.left() + PADDING, opt.rect.top() + 4*PADDING, opt.rect.width() - 2*PADDING, opt.rect.height() - 2*PADDING );
painter->drawText( rRect, Qt::AlignHCenter, mainText );
int totalwidth = opt.rect.width();
int thirds = totalwidth/3;
QRect allowBtnRect;
painter->setPen( Qt::white );
QString allowBtnText = tr( "See Details & Decide" );
int allowBtnWidth = fm.width( allowBtnText ) + 2 * PADDING;
allowBtnRect = QRect( opt.rect.left() + thirds - allowBtnWidth / 2, opt.rect.bottom() - fm.height() - 4 * PADDING, allowBtnWidth + 2 * PADDING, fm.height() + 2 * PADDING );
drawRoundedButton( painter, allowBtnRect, allowBtnRect.contains( m_savedHoverPos ) );
painter->drawText( allowBtnRect, Qt::AlignCenter, allowBtnText );
m_savedAcceptRect = allowBtnRect;
}
QSize
ACLItemDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index) const
{
QSize size( QStyledItemDelegate::sizeHint( option, index ).width(), ( TomahawkUtils::defaultFontHeight() + 6 ) * 4.5 );
return size;
}
void
ACLItemDelegate::emitSizeHintChanged( const QModelIndex& index )
{
emit sizeHintChanged( index );
}
void
ACLItemDelegate::drawRoundedButton( QPainter *painter, const QRect& btnRect, bool red ) const
{
//FIXME const colors
if ( !red )
TomahawkUtils::drawRoundedButton( painter, btnRect, QColor( 54, 127, 211 ), QColor( 43, 104, 182 ), QColor( 34, 85, 159 ), QColor( 35, 79, 147 ) );
else
TomahawkUtils::drawRoundedButton( painter, btnRect, QColor( 206, 63, 63 ), QColor( 170, 52, 52 ), QColor( 150, 50, 50 ), QColor( 130, 40, 40 ) );
}
bool
ACLItemDelegate::editorEvent( QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem& option, const QModelIndex& index )
{
Q_UNUSED( option )
Q_UNUSED( model )
//tDebug( LOGVERBOSE ) << Q_FUNC_INFO;
if ( event->type() != QEvent::MouseButtonPress &&
event->type() != QEvent::MouseButtonRelease &&
event->type() != QEvent::MouseButtonDblClick &&
event->type() != QEvent::MouseMove )
return false;
if ( event->type() == QEvent::MouseMove )
{
QMouseEvent* me = static_cast< QMouseEvent* >( event );
m_savedHoverPos = me->pos();
//tDebug( LOGVERBOSE ) << Q_FUNC_INFO << "Setting position to " << m_savedHoverPos;
emit update( index );
return true;
}
/* TODO
if ( event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::MouseButtonDblClick )
{
QMouseEvent* me = static_cast< QMouseEvent* >( event );
if ( m_savedAcceptRect.contains( me->pos() ) )
emit aclResult( Tomahawk::ACLStatus::Stream );
else if ( m_savedDenyRect.contains( me->pos() ) )
emit aclResult( Tomahawk::ACLStatus::Deny );
return true;
}
*/
return false;
}
} // namespace APIv2
} // namespace Tomahawk

View File

@@ -0,0 +1,60 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk 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 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TOMAHAWK_APIV2_ACLITEMDELEGATE_H
#define TOMAHAWK_APIV2_ACLITEMDELEGATE_H
#include <QStyledItemDelegate>
#include "web/Api2User.h"
namespace Tomahawk {
namespace APIv2 {
class ACLItemDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit ACLItemDelegate ( QObject* parent = 0 );
virtual ~ACLItemDelegate();
virtual void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const;
virtual QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const;
virtual void emitSizeHintChanged( const QModelIndex &index );
signals:
void update( const QModelIndex& idx );
void aclResult( Api2User::ACLDecision aclDecision );
protected:
virtual bool editorEvent( QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index );
private:
void drawRoundedButton( QPainter* painter, const QRect& btnRect, bool red = false ) const;
QPoint m_savedHoverPos;
mutable QRect m_savedAcceptRect;
};
} // namespace APIv2
} // namespace Tomahawk
#endif // TOMAHAWK_APIV2_ACLITEMDELEGATE_H

View File

@@ -0,0 +1,56 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk 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 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ACLJobStatusItem.h"
#include "infosystem/InfoSystem.h"
#include "ACLItemDelegate.h"
namespace Tomahawk {
namespace APIv2 {
ACLJobStatusItem::ACLJobStatusItem( const QSharedPointer<Api2User>& user )
: JobStatusItem()
{
m_user = user;
}
ACLJobStatusItem::~ACLJobStatusItem()
{
}
void
ACLJobStatusItem::createDelegate( QObject *parent )
{
if ( m_delegate )
return;
m_delegate = new ACLItemDelegate( parent );
// Display a system notification so that the user sees that he has to make a decision.
Tomahawk::InfoSystem::InfoPushData pushData( "ACLJobStatusItem", Tomahawk::InfoSystem::InfoNotifyUser, tr( "Tomahawk needs you to decide whether an app is allowed to connect via the HTTP API." ), Tomahawk::InfoSystem::PushNoFlag );
Tomahawk::InfoSystem::InfoSystem::instance()->pushInfo( pushData );
}
} // namespace APIv2
} // namespace Tomahawk

View File

@@ -0,0 +1,67 @@
/* === This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2013, Uwe L. Korn <uwelk@xhochy.com>
*
* Tomahawk 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 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TOMAHAWK_APIV2_ACLJOBSTATUSITEM_H
#define TOMAHAWK_APIV2_ACLJOBSTATUSITEM_H
#include "jobview/JobStatusItem.h"
#include "web/Api2User.h"
#include <QPixmap>
namespace Tomahawk {
namespace APIv2 {
class ACLJobStatusItem : public JobStatusItem
{
Q_OBJECT
public:
explicit ACLJobStatusItem( const QSharedPointer<Api2User>& user );
virtual ~ACLJobStatusItem();
virtual int weight() const { return 99; }
virtual QString rightColumnText() const { return QString(); }
virtual QString mainText() const { return QString(); }
virtual QPixmap icon() const { return QPixmap(); }
virtual QString type() const { return "apiv2-acljob"; }
virtual int concurrentJobLimit() const { return 3; }
virtual bool hasCustomDelegate() const { return true; }
virtual void createDelegate( QObject* parent = 0 );
virtual QStyledItemDelegate* customDelegate() const { return m_delegate; }
virtual QSharedPointer<Api2User> user() const { return m_user; }
virtual QString name() const { return m_user->name(); }
signals:
// void userDecision( QSharedPointer<Api2User> user );
public slots:
// void aclResult( Api2User::ACLDecision );
private:
QStyledItemDelegate* m_delegate;
QSharedPointer<Api2User> m_user;
};
} // namespace APIv2
} // namespace Tomahawk
#endif // TOMAHAWK_APIV2_ACLJOBSTATUSITEM_H