From 4cdfd81fb5fb2cc7fcdaad0bb3e64dac6e2522f8 Mon Sep 17 00:00:00 2001 From: "Uwe L. Korn" Date: Mon, 24 Jun 2013 14:01:04 +0200 Subject: [PATCH] Basic skeleton for api authentication --- src/tomahawk/CMakeLists.txt | 3 + src/tomahawk/web/Api_v2_0.cpp | 62 ++++++++- src/tomahawk/web/Api_v2_0.h | 15 ++ src/tomahawk/web/apiv2/ACLItemDelegate.cpp | 146 ++++++++++++++++++++ src/tomahawk/web/apiv2/ACLItemDelegate.h | 60 ++++++++ src/tomahawk/web/apiv2/ACLJobStatusItem.cpp | 56 ++++++++ src/tomahawk/web/apiv2/ACLJobStatusItem.h | 67 +++++++++ 7 files changed, 407 insertions(+), 2 deletions(-) create mode 100644 src/tomahawk/web/apiv2/ACLItemDelegate.cpp create mode 100644 src/tomahawk/web/apiv2/ACLItemDelegate.h create mode 100644 src/tomahawk/web/apiv2/ACLJobStatusItem.cpp create mode 100644 src/tomahawk/web/apiv2/ACLJobStatusItem.h diff --git a/src/tomahawk/CMakeLists.txt b/src/tomahawk/CMakeLists.txt index c3029bbaa..9ad5c4718 100644 --- a/src/tomahawk/CMakeLists.txt +++ b/src/tomahawk/CMakeLists.txt @@ -112,6 +112,9 @@ IF( QXTWEB_FOUND ) 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}) diff --git a/src/tomahawk/web/Api_v2_0.cpp b/src/tomahawk/web/Api_v2_0.cpp index 8d863313c..f3894cc60 100644 --- a/src/tomahawk/web/Api_v2_0.cpp +++ b/src/tomahawk/web/Api_v2_0.cpp @@ -19,11 +19,15 @@ #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 #include #include @@ -90,6 +94,47 @@ Api_v2_0::playback( QxtWebRequestEvent* event, const QString& 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 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 ) { @@ -120,10 +165,23 @@ Api_v2_0::jsonUnauthenticated( QxtWebRequestEvent *event ) 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 user = m_users.value( clientName ); + if ( user->aclDecision() == Api2User::FullAccess && user->pubkey() == pubkey ) + { + return true; + } + } + } // TODO: Auth! - // * SSL client certificate // * Shared secret between two clients when talking via SSL // * sth else when connecting without httpS // * a more secure version of digest auth - return true; + return false; } diff --git a/src/tomahawk/web/Api_v2_0.h b/src/tomahawk/web/Api_v2_0.h index 44308137b..0a7d2442e 100644 --- a/src/tomahawk/web/Api_v2_0.h +++ b/src/tomahawk/web/Api_v2_0.h @@ -19,7 +19,11 @@ #ifndef API_V2_0_H #define API_V2_0_H +#include #include +#include + +#include "Api2User.h" class Api_v2; class QxtWebRequestEvent; @@ -47,6 +51,16 @@ public slots: * 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. @@ -66,6 +80,7 @@ private: void jsonUnauthenticated( QxtWebRequestEvent* event ); Api_v2* m_service; + QMap< QString, QSharedPointer< Api2User > > m_users; }; #endif // API_V2_0_H diff --git a/src/tomahawk/web/apiv2/ACLItemDelegate.cpp b/src/tomahawk/web/apiv2/ACLItemDelegate.cpp new file mode 100644 index 000000000..9f251b29c --- /dev/null +++ b/src/tomahawk/web/apiv2/ACLItemDelegate.cpp @@ -0,0 +1,146 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Uwe L. Korn + * + * 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 . + */ + +#include "ACLItemDelegate.h" + +#include "jobview/JobStatusModel.h" +#include "utils/TomahawkUtils.h" +#include "utils/TomahawkUtilsGui.h" +#include "ACLJobStatusItem.h" + +#include +#include +#include + +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 diff --git a/src/tomahawk/web/apiv2/ACLItemDelegate.h b/src/tomahawk/web/apiv2/ACLItemDelegate.h new file mode 100644 index 000000000..8b8728bf0 --- /dev/null +++ b/src/tomahawk/web/apiv2/ACLItemDelegate.h @@ -0,0 +1,60 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Uwe L. Korn + * + * 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 . + */ + + +#ifndef TOMAHAWK_APIV2_ACLITEMDELEGATE_H +#define TOMAHAWK_APIV2_ACLITEMDELEGATE_H + +#include + +#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 diff --git a/src/tomahawk/web/apiv2/ACLJobStatusItem.cpp b/src/tomahawk/web/apiv2/ACLJobStatusItem.cpp new file mode 100644 index 000000000..87e0fa8f3 --- /dev/null +++ b/src/tomahawk/web/apiv2/ACLJobStatusItem.cpp @@ -0,0 +1,56 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Uwe L. Korn + * + * 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 . + */ + + +#include "ACLJobStatusItem.h" + +#include "infosystem/InfoSystem.h" +#include "ACLItemDelegate.h" + +namespace Tomahawk { +namespace APIv2 { + +ACLJobStatusItem::ACLJobStatusItem( const QSharedPointer& 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 + diff --git a/src/tomahawk/web/apiv2/ACLJobStatusItem.h b/src/tomahawk/web/apiv2/ACLJobStatusItem.h new file mode 100644 index 000000000..1d8300e62 --- /dev/null +++ b/src/tomahawk/web/apiv2/ACLJobStatusItem.h @@ -0,0 +1,67 @@ +/* === This file is part of Tomahawk Player - === + * + * Copyright 2013, Uwe L. Korn + * + * 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 . + */ + +#ifndef TOMAHAWK_APIV2_ACLJOBSTATUSITEM_H +#define TOMAHAWK_APIV2_ACLJOBSTATUSITEM_H + +#include "jobview/JobStatusItem.h" +#include "web/Api2User.h" + +#include + +namespace Tomahawk { +namespace APIv2 { + +class ACLJobStatusItem : public JobStatusItem +{ + Q_OBJECT +public: + explicit ACLJobStatusItem( const QSharedPointer& 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 user() const { return m_user; } + virtual QString name() const { return m_user->name(); } + +signals: +// void userDecision( QSharedPointer user ); + +public slots: +// void aclResult( Api2User::ACLDecision ); + +private: + QStyledItemDelegate* m_delegate; + QSharedPointer m_user; +}; + +} // namespace APIv2 +} // namespace Tomahawk + +#endif // TOMAHAWK_APIV2_ACLJOBSTATUSITEM_H