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 crypto::scrypt;
use diesel::prelude::*;
use iron::headers::{Authorization, Basic, Range};
use iron::mime::Mime;
@ -7,7 +8,6 @@ use iron::{status, AroundMiddleware, Handler};
use mount::Mount;
use params;
use router::Router;
use crypto::scrypt;
use secure_session::middleware::{SessionConfig, SessionMiddleware};
use secure_session::session::ChaCha20Poly1305SessionManager;
use serde_json;
@ -57,7 +57,12 @@ where
let params = scrypt::ScryptParams::new(12, 8, 1);
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)
}
@ -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 auth_secret = get_auth_secret(db.deref())?;
let session_manager =
ChaCha20Poly1305SessionManager::<Session>::from_key(auth_secret);
let session_manager = ChaCha20Poly1305SessionManager::<Session>::from_key(auth_secret);
let session_config = SessionConfig::default();
let session_middleware = SessionMiddleware::<
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);
}
{
let db = db.clone();
auth_api_mount.mount("/lastfm/auth/", move |request: &mut Request| {
self::lastfm_auth(request, db.deref())
});
}
{
let db = db.clone();
auth_api_mount.mount("/lastfm/now_playing/", move |request: &mut Request| {
self::lastfm_now_playing(request, db.deref())
});
}
{
let db = db.clone();
auth_api_mount.mount("/lastfm/scrobble/", move |request: &mut Request| {
self::lastfm_scrobble(request, db.deref())
});
let mut lastfm_router = Router::new();
let now_playing_db = db.clone();
let link_db = db.clone();
let unlink_db = db.clone();
let scrobble_db = db.clone();
lastfm_router.put(
"/now_playing",
move |request: &mut Request| {
self::lastfm_now_playing(request, now_playing_db.deref())
},
"now_playing",
);
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);
@ -715,7 +736,7 @@ fn delete_playlist(request: &mut Request, db: &DB) -> IronResult<Response> {
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 username = match input.find(&["username"]) {
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()),
};
lastfm::auth(db, &username, &token)?;
lastfm::link(db, &username, &token)?;
let url_encoded_content = match input.find(&["content"]) {
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)))
}
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> {
let username = match request.extensions.get::<SessionKey>() {
Some(s) => s.username.clone(),

View file

@ -26,7 +26,9 @@ pub struct MiscSettings {
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Preferences {}
pub struct Preferences {
pub lastfm_username: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ConfigUser {
@ -252,11 +254,19 @@ where
Ok(())
}
pub fn read_preferences<T>(_: &T, _: &str) -> Result<Preferences>
pub fn read_preferences<T>(db: &T, username: &str) -> Result<Preferences>
where
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<()>

View file

@ -1,99 +1,99 @@
table! {
ddns_config (id) {
id -> Integer,
host -> Text,
username -> Text,
password -> Text,
}
ddns_config (id) {
id -> Integer,
host -> Text,
username -> Text,
password -> Text,
}
}
table! {
directories (id) {
id -> Integer,
path -> Text,
parent -> Nullable<Text>,
artist -> Nullable<Text>,
year -> Nullable<Integer>,
album -> Nullable<Text>,
artwork -> Nullable<Text>,
date_added -> Integer,
}
directories (id) {
id -> Integer,
path -> Text,
parent -> Nullable<Text>,
artist -> Nullable<Text>,
year -> Nullable<Integer>,
album -> Nullable<Text>,
artwork -> Nullable<Text>,
date_added -> Integer,
}
}
table! {
misc_settings (id) {
id -> Integer,
auth_secret -> Text,
index_sleep_duration_seconds -> Integer,
index_album_art_pattern -> Text,
prefix_url -> Text,
}
misc_settings (id) {
id -> Integer,
auth_secret -> Text,
index_sleep_duration_seconds -> Integer,
index_album_art_pattern -> Text,
prefix_url -> Text,
}
}
table! {
mount_points (id) {
id -> Integer,
source -> Text,
name -> Text,
}
mount_points (id) {
id -> Integer,
source -> Text,
name -> Text,
}
}
table! {
playlist_songs (id) {
id -> Integer,
playlist -> Integer,
path -> Text,
ordering -> Integer,
}
playlist_songs (id) {
id -> Integer,
playlist -> Integer,
path -> Text,
ordering -> Integer,
}
}
table! {
playlists (id) {
id -> Integer,
owner -> Integer,
name -> Text,
}
playlists (id) {
id -> Integer,
owner -> Integer,
name -> Text,
}
}
table! {
songs (id) {
id -> Integer,
path -> Text,
parent -> Text,
track_number -> Nullable<Integer>,
disc_number -> Nullable<Integer>,
title -> Nullable<Text>,
artist -> Nullable<Text>,
album_artist -> Nullable<Text>,
year -> Nullable<Integer>,
album -> Nullable<Text>,
artwork -> Nullable<Text>,
duration -> Nullable<Integer>,
}
songs (id) {
id -> Integer,
path -> Text,
parent -> Text,
track_number -> Nullable<Integer>,
disc_number -> Nullable<Integer>,
title -> Nullable<Text>,
artist -> Nullable<Text>,
album_artist -> Nullable<Text>,
year -> Nullable<Integer>,
album -> Nullable<Text>,
artwork -> Nullable<Text>,
duration -> Nullable<Integer>,
}
}
table! {
users (id) {
id -> Integer,
name -> Text,
password_salt -> Binary,
password_hash -> Binary,
admin -> Integer,
lastfm_username -> Nullable<Text>,
lastfm_session_key -> Nullable<Text>,
}
users (id) {
id -> Integer,
name -> Text,
password_salt -> Binary,
password_hash -> Binary,
admin -> Integer,
lastfm_username -> Nullable<Text>,
lastfm_session_key -> Nullable<Text>,
}
}
joinable!(playlist_songs -> playlists (playlist));
joinable!(playlists -> users (owner));
allow_tables_to_appear_in_same_query!(
ddns_config,
directories,
misc_settings,
mount_points,
playlist_songs,
playlists,
songs,
users,
ddns_config,
directories,
misc_settings,
mount_points,
playlist_songs,
playlists,
songs,
users,
);

View file

@ -71,7 +71,10 @@ where
let full_url = format!("{}?host={}", DDNS_UPDATE_URL, &config.host);
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() {
return Err(DDNSError::Update(res.status()));
}

View file

@ -2,8 +2,8 @@ use core::ops::Deref;
use diesel;
use diesel::dsl::sql;
use diesel::prelude::*;
use diesel::sqlite::SqliteConnection;
use diesel::sql_types;
use diesel::sqlite::SqliteConnection;
use regex::Regex;
use std::fs;
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
T: ConnectionSource + VFSSource,
{
@ -82,7 +82,19 @@ where
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>

View file

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

View file

@ -101,15 +101,17 @@ where
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
T: ConnectionSource,
{
use db::users::dsl::*;
let connection = db.get_connection();
diesel::update(users.filter(name.eq(username)))
.set(lastfm_session_key.eq(token))
.execute(connection.deref())?;
.set((
lastfm_username.eq(lastfm_login),
lastfm_session_key.eq(session_key),
)).execute(connection.deref())?;
Ok(())
}
@ -128,3 +130,15 @@ where
_ => 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(())
}