1
0
mirror of https://github.com/tomahawk-player/tomahawk.git synced 2025-08-10 16:14:40 +02:00

Add auth support to Playdar procol handling. Needs graphic design BADLY!

add part one of auth
fix stage two of auth as well
Fix API.
This commit is contained in:
Leo Franchi
2011-02-23 01:21:31 -05:00
parent 763aad9539
commit b5f52f6c04
14 changed files with 456 additions and 8 deletions

64
data/www/auth.html Normal file
View File

@@ -0,0 +1,64 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Allow Tomahawk Access</title>
<style>
body {
margin: 0;
padding: 0;
font: normal 12px 'Verdana', sans-serif;
}
a img,
img {
border: 0;
}
a {
color: #598d0c;
}
a#head {
display: block;
width: 100%;
height: 68px;
background: #cbdab1;
}
div#content {
margin: 30px;
text-align: center;
}
div#content p {
margin: 0 0 25px 0;
}
input.button {
border: 1px solid #999;
padding: 4px 10px;
border-color: #999 #555 #555 #999;
background: #ddd;
}
input.confirm {
background: #81bd0e;
color: #fff;
border-color: #6ba318 #426c0b #426c0b #6ba318;
}
</style>
</head>
<body>
<a href="http://www.playdar.org/" title="Tomahawk - Powered by Playdar" id="head">
<img alt="Tomahawk - Powered by Playdar" src="/static/playdar_auth_logo.gif" width="233" height="68"/>
</a>
<div id="content">
<form method="post" action="/auth_2/" id="auth">
<p>Allow access to Tomahawk from <a href="<%WEBSITE%>"><%NAME%></a></p>
<p class="buttons">
<input type="button" value="Deny" class="button" onclick="window.close();" />
<input type="submit" value="Allow" class="confirm button" />
</p>
<input type="hidden" name="formtoken" value="<%FORMTOKEN%>" />
<input type="hidden" name="receiverurl" value="<%URL%>" />
<input type="hidden" name="website" value="<%WEBSITE%>" />
<input type="hidden" name="name" value="<%NAME%>" />
</form>
</div>
</body>
</html>

44
data/www/auth.na.html Normal file
View File

@@ -0,0 +1,44 @@
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Allow Tomahawk Access</title>
<style>
body {
margin: 0;
padding: 0;
font: normal 12px 'Verdana', sans-serif;
}
a img,
img {
border: 0;
}
a {
color: #598d0c;
}
a#head {
display: block;
width: 100%;
height: 68px;
background: #cbdab1;
}
div#content {
margin: 30px;
text-align: center;
}
div#content p {
margin: 0 0 25px 0;
}
</style>
</head>
<body>
<a href="http://www.playdar.org/" title="Tomahawk - Powered by Playdar" id="head">
<img alt="Tomahawk - Powered by Playdar" src="/static/playdar_auth_logo.gif" width="233" height="68"/>
</a>
<div id="content">
<p>You have allowed access to Tomahawk from <a href="<%WEBSITE%>"><%NAME%></a></p>
<p>Copy and paste this authentication <strong>token</strong> into the status bar then close this window.</p>
<p>Token: <input type="text" value="<%AUTHCODE%>" size="35" onclick="this.focus(); this.select();" />
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -80,5 +80,7 @@
<file>./data/icons/audio-x-generic-22x22.png</file>
<file>./data/icons/audio-x-generic-32x32.png</file>
<file>./data/icons/audio-x-generic-16x16.png</file>
<file>./data/www/auth.html</file>
<file>./data/www/auth.na.html</file>
</qresource>
</RCC>

View File

@@ -68,6 +68,8 @@ set( libSources
database/databasecommand_loaddynamicplaylist.cpp
database/databasecommand_loadalldynamicplaylists.cpp
database/databasecommand_deletedynamicplaylist.cpp
database/databasecommand_addclientauth.cpp
database/databasecommand_clientauthvalid.cpp
database/database.cpp
playlist/collectionmodel.cpp
@@ -207,6 +209,8 @@ set( libHeaders
database/databasecommand_loaddynamicplaylist.h
database/databasecommand_deletedynamicplaylist.h
database/databasecommand_loadalldynamicplaylists.h
database/databasecommand_addclientauth.h
database/databasecommand_clientauthvalid.h
network/bufferiodevice.h
network/msgprocessor.h

View File

@@ -0,0 +1,46 @@
/****************************************************************************************
* Copyright (c) 2011 Leo Franchi <lfranchi@kde.org> *
* *
* This program 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 2 of the License, or (at your option) any later *
* version. *
* *
* This program 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 *
* this program. If not, see <http://www.gnu.org/licenses/>. *
****************************************************************************************/
#include "databasecommand_addclientauth.h"
DatabaseCommand_AddClientAuth::DatabaseCommand_AddClientAuth( const QString& clientToken,
const QString& website,
const QString& name,
const QString& userAgent,
QObject* parent )
: DatabaseCommand( parent )
, m_clientToken( clientToken )
, m_website( website )
, m_name( name )
, m_userAgent( userAgent )
{
}
void DatabaseCommand_AddClientAuth::exec(DatabaseImpl* lib)
{
TomahawkSqlQuery q = lib->newquery();
q.prepare( "INSERT INTO http_client_auth (token, website, name, ua, mtime, permissions) VALUES (?, ?, ?, ?, ?, ?)" );
q.addBindValue( m_clientToken );
q.addBindValue( m_website );
q.addBindValue( m_name );
q.addBindValue( m_userAgent );
q.addBindValue( 0 );
q.addBindValue( "*" );
if( !q.exec() ) {
qWarning() << "Failed to insert http client into auth table!";
}
}

View File

@@ -0,0 +1,45 @@
/****************************************************************************************
* Copyright (c) 2011 Leo Franchi <lfranchi@kde.org> *
* *
* This program 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 2 of the License, or (at your option) any later *
* version. *
* *
* This program 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 *
* this program. If not, see <http://www.gnu.org/licenses/>. *
****************************************************************************************/
#ifndef DATABASECOMMAND_ADDCLIENTAUTH_H
#define DATABASECOMMAND_ADDCLIENTAUTH_H
#include "databaseimpl.h"
#include "databasecommand.h"
#include "dllmacro.h"
#include <QObject>
class DLLEXPORT DatabaseCommand_AddClientAuth : public DatabaseCommand
{
Q_OBJECT
public:
explicit DatabaseCommand_AddClientAuth( QObject* parent = 0 )
: DatabaseCommand( parent )
{}
explicit DatabaseCommand_AddClientAuth( const QString& clientToken, const QString& website, const QString& name, const QString& userAgent, QObject* parent = 0 );
QString commandname() const { return "addclientauth"; }
virtual void exec( DatabaseImpl* lib );
virtual bool doesMutates() const { return true; }
private:
QString m_clientToken, m_website, m_name, m_userAgent;
};
#endif // DATABASECOMMAND_ADDCLIENTAUTH_H

View File

@@ -0,0 +1,42 @@
/****************************************************************************************
* Copyright (c) 2011 Leo Franchi <lfranchi@kde.org> *
* *
* This program 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 2 of the License, or (at your option) any later *
* version. *
* *
* This program 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 *
* this program. If not, see <http://www.gnu.org/licenses/>. *
****************************************************************************************/
#include "databasecommand_clientauthvalid.h"
DatabaseCommand_ClientAuthValid::DatabaseCommand_ClientAuthValid( const QString& clientToken, QObject* parent )
: DatabaseCommand( parent )
, m_clientToken( clientToken )
{
}
void DatabaseCommand_ClientAuthValid::exec(DatabaseImpl* lib)
{
TomahawkSqlQuery q = lib->newquery();
q.prepare( "SELECT name FROM http_client_auth WHERE token = ?" );
q.addBindValue( m_clientToken );
if( q.exec() ) {
if( q.next() ) {
QString name = q.value( 0 ).toString();
emit authValid( m_clientToken, name, true );
} else {
emit authValid( m_clientToken, QString(), false );
}
} else {
qWarning() << "Failed to query http auth table for client:" << m_clientToken;
}
}

View File

@@ -0,0 +1,49 @@
/****************************************************************************************
* Copyright (c) 2011 Leo Franchi <lfranchi@kde.org> *
* *
* This program 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 2 of the License, or (at your option) any later *
* version. *
* *
* This program 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 *
* this program. If not, see <http://www.gnu.org/licenses/>. *
****************************************************************************************/
#ifndef DATABASECOMMAND_CLIENTAUTHVALID_H
#define DATABASECOMMAND_CLIENTAUTHVALID_H
#include "databaseimpl.h"
#include "databasecommand.h"
#include "dllmacro.h"
#include <QObject>
class DLLEXPORT DatabaseCommand_ClientAuthValid : public DatabaseCommand
{
Q_OBJECT
public:
explicit DatabaseCommand_ClientAuthValid( QObject* parent = 0 )
: DatabaseCommand( parent )
{}
explicit DatabaseCommand_ClientAuthValid( const QString& clientToken, QObject* parent = 0 );
QString commandname() const { return "clientauthvalid"; }
virtual void exec( DatabaseImpl* lib );
virtual bool doesMutates() const { return false; }
signals:
// if auth is invalid name is empty
void authValid( const QString& clientToken, const QString& name, bool valid );
private:
QString m_clientToken;
};
#endif // DATABASECOMMAND_CLIENTAUTHVALID_H

View File

@@ -16,8 +16,7 @@
*/
#include "schema.sql.h"
#define CURRENT_SCHEMA_VERSION 20
#define CURRENT_SCHEMA_VERSION 21
DatabaseImpl::DatabaseImpl( const QString& dbname, Database* parent )
: QObject( (QObject*) parent )

View File

@@ -241,6 +241,16 @@ CREATE TABLE IF NOT EXISTS playback_log (
CREATE INDEX playback_log_source ON playback_log(source);
CREATE INDEX playback_log_track ON playback_log(track);
-- auth information for http clients
CREATE TABLE IF NOT EXISTS http_client_auth (
token TEXT NOT NULL PRIMARY KEY,
website TEXT NOT NULL,
name TEXT NOT NULL,
ua TEXT,
mtime INTEGER,
permissions TEXT NOT NULL
);
-- Schema version, and misc tomahawk settings relating to the collection db
@@ -250,4 +260,4 @@ CREATE TABLE IF NOT EXISTS settings (
v TEXT NOT NULL DEFAULT ''
);
INSERT INTO settings(k,v) VALUES('schema_version', '20');
INSERT INTO settings(k,v) VALUES('schema_version', '21');

View File

@@ -1,5 +1,5 @@
/*
This file was automatically generated from ./schema.sql on Wed Feb 23 12:39:07 CET 2011.
This file was automatically generated from schema.sql on Thu Feb 24 19:05:46 EST 2011.
*/
static const char * tomahawk_schema_sql =
@@ -161,11 +161,19 @@ static const char * tomahawk_schema_sql =
");"
"CREATE INDEX playback_log_source ON playback_log(source);"
"CREATE INDEX playback_log_track ON playback_log(track);"
"CREATE TABLE IF NOT EXISTS http_client_auth ("
" token TEXT NOT NULL PRIMARY KEY,"
" website TEXT NOT NULL,"
" name TEXT NOT NULL,"
" ua TEXT,"
" mtime INTEGER,"
" permissions TEXT NOT NULL"
");"
"CREATE TABLE IF NOT EXISTS settings ("
" k TEXT NOT NULL PRIMARY KEY,"
" v TEXT NOT NULL DEFAULT ''"
");"
"INSERT INTO settings(k,v) VALUES('schema_version', '20');"
"INSERT INTO settings(k,v) VALUES('schema_version', '21');"
;
const char * get_tomahawk_sql()

12
src/tomahawk.protocol Normal file
View File

@@ -0,0 +1,12 @@
[Protocol]
exec=/home/leo/kde/tomahawk/build/tomahawk "%u"
protocol=tomahawk
input=none
output=none
helper=true
listing=
reading=false
writing=false
makedir=false
deleting=false

View File

@@ -17,8 +17,15 @@
#include <QFile>
#include <QSharedPointer>
#include <QStringList>
#include "network/servent.h"
#include "tomahawkutils.h"
#include "tomahawk/tomahawkapp.h"
#include <database/databasecommand_addclientauth.h>
#include <qxtwebcontent.h>
#include <database/database.h>
#include <database/databasecommand_clientauthvalid.h>
class Api_v1 : public QxtWebSlotService
{
@@ -32,7 +39,81 @@ public:
}
public slots:
// authenticating uses /auth_1
// we redirect to /auth_2 for the callback
void auth_1( QxtWebRequestEvent* event ) {
qDebug() << "AUTH_1 HTTP" << event->url.toString();
if( !event->url.hasQueryItem( "website" ) || !event->url.hasQueryItem( "name" ) ) {
qDebug() << "Malformed HTTP resolve request";
send404( event );
}
QString formToken = uuid();
if( event->url.hasQueryItem( "json" ) ) { // JSON response
QVariantMap m;
m[ "formtoken" ] = formToken;
sendJSON( m, event );
} else { // webpage request
QString authPage = RESPATH "www/auth.html";
QHash< QString, QString > args;
if( event->url.hasQueryItem( "receiverurl" ) )
args[ "url" ] = QUrl::fromPercentEncoding( event->url.queryItemValue( "receiverurl" ).toUtf8() );
args[ "formtoken" ] = formToken;
args[ "website" ] = QUrl::fromPercentEncoding( event->url.queryItemValue( "website" ).toUtf8() );
args[ "name" ] = QUrl::fromPercentEncoding( event->url.queryItemValue( "name" ).toUtf8() );
sendWebpageWithArgs( event, authPage, args );
}
}
void auth_2( QxtWebRequestEvent* event ) {
qDebug() << "AUTH_2 HTTP" << event->url.toString();
QUrl url = event->url;
url.setEncodedQuery( event->content->readAll() );
if( !url.hasQueryItem( "website" ) || !url.hasQueryItem( "name" ) || !url.hasQueryItem( "formtoken" ) ) {
qDebug() << "Malformed HTTP resolve request";
qDebug() << url.hasQueryItem( "website" ) << url.hasQueryItem( "name" ) << url.hasQueryItem( "formtoken" );
send404( event );
return;
}
QString website = QUrl::fromPercentEncoding( url.queryItemValue( "website" ).toUtf8() );
QString name = QUrl::fromPercentEncoding( url.queryItemValue( "name" ).toUtf8() );
QByteArray authtoken = uuid().toLatin1();
qDebug() << "HEADERS:" << event->headers;
if( !url.hasQueryItem( "receiverurl" ) && url.queryItemValue( "receiverurl" ).isEmpty() ) { //no receiver url, so do it ourselves
QString receiverUrl = QUrl::fromPercentEncoding( url.queryItemValue( "receiverurl" ).toUtf8() );
if( url.hasQueryItem( "json" ) ) {
QVariantMap m;
m[ "authtoken" ] = authtoken;
sendJSON( m, event );
} else {
QString authPage = RESPATH "www/auth.na.html";
QHash< QString, QString > args;
args[ "authcode" ] = authPage;
args[ "website" ] = QUrl::fromPercentEncoding( url.queryItemValue( "website" ).toUtf8() );
args[ "name" ] = QUrl::fromPercentEncoding( url.queryItemValue( "name" ).toUtf8() );
sendWebpageWithArgs( event, authPage, args );
}
} else { // do what the client wants
QUrl receiverurl = QUrl( url.queryItemValue( "receiverurl" ).toUtf8(), QUrl::TolerantMode );
receiverurl.addEncodedQueryItem( "authtoken", "#" + authtoken );
qDebug() << "Got receiver url:" << receiverurl.toString();
QxtWebRedirectEvent* e = new QxtWebRedirectEvent( event->sessionID, event->requestID, receiverurl.toString() );
postEvent( e );
// TODO validation of receiverurl?
}
DatabaseCommand_AddClientAuth* dbcmd = new DatabaseCommand_AddClientAuth( authtoken, website, name, event->headers.key( "ua" ) );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(dbcmd) );
}
// all v1 api calls go to /api/
void api(QxtWebRequestEvent* event)
{
@@ -80,18 +161,35 @@ public slots:
qDebug() << "404" << event->url.toString();
QxtWebPageEvent* wpe = new QxtWebPageEvent(event->sessionID, event->requestID, "<h1>Not Found</h1>");
wpe->status = 404;
wpe->statusMessage = "not found";
wpe->statusMessage = "not feventound";
postEvent( wpe );
}
void stat( QxtWebRequestEvent* event )
{
qDebug() << "Got Stat request:" << event->url.toString();
m_storedEvent = event;
if( !event->content.isNull() )
qDebug() << "BODY:" << event->content->readAll();
if( event->url.hasQueryItem( "auth" ) ) { // check for auth status
DatabaseCommand_ClientAuthValid* dbcmd = new DatabaseCommand_ClientAuthValid( event->url.queryItemValue( "auth" ), this );
connect( dbcmd, SIGNAL( authValid( QString, QString, bool ) ), this, SLOT( statResult( QString, QString, bool ) ) );
Database::instance()->enqueue( QSharedPointer<DatabaseCommand>(dbcmd) );
} else {
statResult( QString(), QString(), false );
}
}
void statResult( const QString& clientToken, const QString& name, bool valid ) {
QVariantMap m;
m.insert( "name", "playdar" );
m.insert( "version", "0.1.1" ); // TODO (needs to be >=0.1.1 for JS to work)
m.insert( "authenticated", true ); // TODO
m.insert( "authenticated", valid ); // TODO
m.insert( "capabilities", QVariantList() );
sendJSON( m, event );
sendJSON( m, m_storedEvent );
m_storedEvent = 0;
}
void resolve( QxtWebRequestEvent* event )
@@ -119,6 +217,12 @@ public slots:
sendJSON( r, event );
}
void staticdata( QxtWebRequestEvent* event ) {
if( event->url.path().contains( "playdar_auth_logo.gif" ) ) {
// TODO handle
}
}
void get_results( QxtWebRequestEvent* event )
{
if( !event->url.hasQueryItem("qid") )
@@ -174,6 +278,23 @@ public slots:
qDebug() << "JSON response" << event->url.toString() << body;
}
// load an html template from a file, replace args from map
// then serve
void sendWebpageWithArgs( QxtWebRequestEvent* event, const QString& filenameSource, const QHash< QString, QString >& args ) {
if( !QFile::exists( filenameSource ) )
qWarning() << "Passed invalid file for html source:" << filenameSource;
QFile f( filenameSource );
f.open( QIODevice::ReadOnly );
QByteArray html = f.readAll();
foreach( const QString& param, args.keys() ) {
html.replace( QString( "<%%1%>" ).arg( param.toUpper() ), args.value( param ).toUtf8() );
}
QxtWebPageEvent* e = new QxtWebPageEvent( event->sessionID, event->requestID, html );
postEvent( e );
}
void index(QxtWebRequestEvent* event)
{
@@ -182,6 +303,8 @@ public slots:
}
private:
QxtWebRequestEvent* m_storedEvent;
};
#endif