1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-04-20 07:52:30 +02:00

Basic skeleton for api authentication

This commit is contained in:
Uwe L. Korn 2013-06-24 14:01:04 +02:00
parent 1bae47da51
commit 4cdfd81fb5
7 changed files with 407 additions and 2 deletions

View File

@ -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})

View File

@ -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 <QSslKey>
#include <QxtWeb/QxtWebPageEvent>
#include <QxtWeb/QxtWebSlotService>
@ -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<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 )
{
@ -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<Api2User> 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;
}

View File

@ -19,7 +19,11 @@
#ifndef API_V2_0_H
#define API_V2_0_H
#include <QMap>
#include <QObject>
#include <QSharedPointer>
#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

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