Added API endpoint to unlink last.fm account

This commit is contained in:
Antoine Gersant 2018-10-07 18:06:29 -07:00
parent a84f13214d
commit f905bc4f73
8 changed files with 172 additions and 103 deletions

View file

@ -1,4 +1,5 @@
use base64; use base64;
use crypto::scrypt;
use diesel::prelude::*; use diesel::prelude::*;
use iron::headers::{Authorization, Basic, Range}; use iron::headers::{Authorization, Basic, Range};
use iron::mime::Mime; use iron::mime::Mime;
@ -7,7 +8,6 @@ use iron::{status, AroundMiddleware, Handler};
use mount::Mount; use mount::Mount;
use params; use params;
use router::Router; use router::Router;
use crypto::scrypt;
use secure_session::middleware::{SessionConfig, SessionMiddleware}; use secure_session::middleware::{SessionConfig, SessionMiddleware};
use secure_session::session::ChaCha20Poly1305SessionManager; use secure_session::session::ChaCha20Poly1305SessionManager;
use serde_json; use serde_json;
@ -57,7 +57,12 @@ where
let params = scrypt::ScryptParams::new(12, 8, 1); let params = scrypt::ScryptParams::new(12, 8, 1);
let mut secret = [0; 32]; let mut secret = [0; 32];
scrypt::scrypt(misc.auth_secret.as_bytes(), b"polaris-salt-and-pepper-with-cheese", &params, &mut secret); scrypt::scrypt(
misc.auth_secret.as_bytes(),
b"polaris-salt-and-pepper-with-cheese",
&params,
&mut secret,
);
Ok(secret) Ok(secret)
} }
@ -66,8 +71,7 @@ pub fn get_handler(db: &Arc<DB>, index: &Arc<Mutex<Sender<index::Command>>>) ->
let mut api_chain = Chain::new(api_handler); let mut api_chain = Chain::new(api_handler);
let auth_secret = get_auth_secret(db.deref())?; let auth_secret = get_auth_secret(db.deref())?;
let session_manager = let session_manager = ChaCha20Poly1305SessionManager::<Session>::from_key(auth_secret);
ChaCha20Poly1305SessionManager::<Session>::from_key(auth_secret);
let session_config = SessionConfig::default(); let session_config = SessionConfig::default();
let session_middleware = SessionMiddleware::< let session_middleware = SessionMiddleware::<
Session, Session,
@ -221,22 +225,39 @@ fn get_endpoints(db: &Arc<DB>, index_channel: &Arc<Mutex<Sender<index::Command>>
auth_api_mount.mount("/playlist/", playlist_router); auth_api_mount.mount("/playlist/", playlist_router);
} }
{ {
let db = db.clone(); let mut lastfm_router = Router::new();
auth_api_mount.mount("/lastfm/auth/", move |request: &mut Request| { let now_playing_db = db.clone();
self::lastfm_auth(request, db.deref()) let link_db = db.clone();
}); let unlink_db = db.clone();
} let scrobble_db = db.clone();
{
let db = db.clone(); lastfm_router.put(
auth_api_mount.mount("/lastfm/now_playing/", move |request: &mut Request| { "/now_playing",
self::lastfm_now_playing(request, db.deref()) move |request: &mut Request| {
}); self::lastfm_now_playing(request, now_playing_db.deref())
} },
{ "now_playing",
let db = db.clone(); );
auth_api_mount.mount("/lastfm/scrobble/", move |request: &mut Request| {
self::lastfm_scrobble(request, db.deref()) lastfm_router.get(
}); "/link",
move |request: &mut Request| self::lastfm_link(request, link_db.deref()),
"link",
);
lastfm_router.delete(
"/link",
move |request: &mut Request| self::lastfm_unlink(request, unlink_db.deref()),
"unlink",
);
lastfm_router.get(
"/scrobble",
move |request: &mut Request| self::lastfm_scrobble(request, scrobble_db.deref()),
"auth",
);
auth_api_mount.mount("/lastfm/", lastfm_router);
} }
let mut auth_api_chain = Chain::new(auth_api_mount); let mut auth_api_chain = Chain::new(auth_api_mount);
@ -715,7 +736,7 @@ fn delete_playlist(request: &mut Request, db: &DB) -> IronResult<Response> {
Ok(Response::with(status::Ok)) Ok(Response::with(status::Ok))
} }
fn lastfm_auth(request: &mut Request, db: &DB) -> IronResult<Response> { fn lastfm_link(request: &mut Request, db: &DB) -> IronResult<Response> {
let input = request.get_ref::<params::Params>().unwrap(); let input = request.get_ref::<params::Params>().unwrap();
let username = match input.find(&["username"]) { let username = match input.find(&["username"]) {
Some(&params::Value::String(ref username)) => username.clone(), Some(&params::Value::String(ref username)) => username.clone(),
@ -726,7 +747,7 @@ fn lastfm_auth(request: &mut Request, db: &DB) -> IronResult<Response> {
_ => return Err(Error::from(ErrorKind::MissingPassword).into()), _ => return Err(Error::from(ErrorKind::MissingPassword).into()),
}; };
lastfm::auth(db, &username, &token)?; lastfm::link(db, &username, &token)?;
let url_encoded_content = match input.find(&["content"]) { let url_encoded_content = match input.find(&["content"]) {
Some(&params::Value::String(ref content)) => content.clone(), Some(&params::Value::String(ref content)) => content.clone(),
@ -748,6 +769,15 @@ fn lastfm_auth(request: &mut Request, db: &DB) -> IronResult<Response> {
Ok(Response::with((mime, status::Ok, popup_content))) Ok(Response::with((mime, status::Ok, popup_content)))
} }
fn lastfm_unlink(request: &mut Request, db: &DB) -> IronResult<Response> {
let username = match request.extensions.get::<SessionKey>() {
Some(s) => s.username.clone(),
None => return Err(Error::from(ErrorKind::AuthenticationRequired).into()),
};
lastfm::unlink(db, &username)?;
Ok(Response::with(status::Ok))
}
fn lastfm_now_playing(request: &mut Request, db: &DB) -> IronResult<Response> { fn lastfm_now_playing(request: &mut Request, db: &DB) -> IronResult<Response> {
let username = match request.extensions.get::<SessionKey>() { let username = match request.extensions.get::<SessionKey>() {
Some(s) => s.username.clone(), Some(s) => s.username.clone(),

View file

@ -26,7 +26,9 @@ pub struct MiscSettings {
} }
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Preferences {} pub struct Preferences {
pub lastfm_username: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ConfigUser { pub struct ConfigUser {
@ -252,11 +254,19 @@ where
Ok(()) Ok(())
} }
pub fn read_preferences<T>(_: &T, _: &str) -> Result<Preferences> pub fn read_preferences<T>(db: &T, username: &str) -> Result<Preferences>
where where
T: ConnectionSource, T: ConnectionSource,
{ {
Ok(Preferences {}) use self::users::dsl::*;
let connection = db.get_connection();
let read_lastfm_username = users
.select(lastfm_username)
.filter(name.eq(username))
.get_result(connection.deref())?;
Ok(Preferences {
lastfm_username: read_lastfm_username,
})
} }
pub fn write_preferences<T>(_: &T, _: &str, _: &Preferences) -> Result<()> pub fn write_preferences<T>(_: &T, _: &str, _: &Preferences) -> Result<()>

View file

@ -71,7 +71,10 @@ where
let full_url = format!("{}?host={}", DDNS_UPDATE_URL, &config.host); let full_url = format!("{}?host={}", DDNS_UPDATE_URL, &config.host);
let client = reqwest::ClientBuilder::new().build()?; let client = reqwest::ClientBuilder::new().build()?;
let res = client.get(full_url.as_str()).basic_auth(config.username, Some(config.password)).send()?; let res = client
.get(full_url.as_str())
.basic_auth(config.username, Some(config.password))
.send()?;
if !res.status().is_success() { if !res.status().is_success() {
return Err(DDNSError::Update(res.status())); return Err(DDNSError::Update(res.status()));
} }

View file

@ -2,8 +2,8 @@ use core::ops::Deref;
use diesel; use diesel;
use diesel::dsl::sql; use diesel::dsl::sql;
use diesel::prelude::*; use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
use diesel::sql_types; use diesel::sql_types;
use diesel::sqlite::SqliteConnection;
use regex::Regex; use regex::Regex;
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;

View file

@ -59,7 +59,7 @@ where
)) ))
} }
pub fn auth<T>(db: &T, username: &str, token: &str) -> Result<(), errors::Error> pub fn link<T>(db: &T, username: &str, token: &str) -> Result<(), errors::Error>
where where
T: ConnectionSource + VFSSource, T: ConnectionSource + VFSSource,
{ {
@ -82,7 +82,19 @@ where
Err(_) => bail!(errors::ErrorKind::LastFMDeserializationError), Err(_) => bail!(errors::ErrorKind::LastFMDeserializationError),
}; };
user::set_lastfm_session_key(db, username, &auth_response.session.key.body) user::lastfm_link(
db,
username,
&auth_response.session.name.body,
&auth_response.session.key.body,
)
}
pub fn unlink<T>(db: &T, username: &str) -> Result<(), errors::Error>
where
T: ConnectionSource + VFSSource,
{
user::lastfm_unlink(db, username)
} }
pub fn scrobble<T>(db: &T, username: &str, track: &Path) -> Result<(), errors::Error> pub fn scrobble<T>(db: &T, username: &str, track: &Path) -> Result<(), errors::Error>

View file

@ -5,7 +5,7 @@ use image::GenericImageView;
use image::ImageBuffer; use image::ImageBuffer;
use std::cmp; use std::cmp;
use std::collections::hash_map::DefaultHasher; use std::collections::hash_map::DefaultHasher;
use std::fs::{DirBuilder}; use std::fs::DirBuilder;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::path::*; use std::path::*;

View file

@ -101,15 +101,17 @@ where
Ok(is_admin != 0) Ok(is_admin != 0)
} }
pub fn set_lastfm_session_key<T>(db: &T, username: &str, token: &str) -> Result<()> pub fn lastfm_link<T>(db: &T, username: &str, lastfm_login: &str, session_key: &str) -> Result<()>
where where
T: ConnectionSource, T: ConnectionSource,
{ {
use db::users::dsl::*; use db::users::dsl::*;
let connection = db.get_connection(); let connection = db.get_connection();
diesel::update(users.filter(name.eq(username))) diesel::update(users.filter(name.eq(username)))
.set(lastfm_session_key.eq(token)) .set((
.execute(connection.deref())?; lastfm_username.eq(lastfm_login),
lastfm_session_key.eq(session_key),
)).execute(connection.deref())?;
Ok(()) Ok(())
} }
@ -128,3 +130,15 @@ where
_ => bail!(ErrorKind::MissingLastFMCredentials), _ => bail!(ErrorKind::MissingLastFMCredentials),
} }
} }
pub fn lastfm_unlink<T>(db: &T, username: &str) -> Result<()>
where
T: ConnectionSource,
{
use db::users::dsl::*;
let connection = db.get_connection();
diesel::update(users.filter(name.eq(username)))
.set((lastfm_session_key.eq(""), lastfm_username.eq("")))
.execute(connection.deref())?;
Ok(())
}