parent
e43e5ce74a
commit
de2e34ac50
11 changed files with 134 additions and 156 deletions
|
@ -7,21 +7,21 @@ use reqwest::{StatusCode, Url};
|
||||||
use sodiumoxide::crypto::secretbox;
|
use sodiumoxide::crypto::secretbox;
|
||||||
|
|
||||||
use atuin_common::api::{
|
use atuin_common::api::{
|
||||||
AddHistoryRequest, CountResponse, LoginResponse, RegisterResponse, SyncHistoryResponse,
|
AddHistoryRequest, CountResponse, LoginRequest, LoginResponse, RegisterResponse,
|
||||||
|
SyncHistoryResponse,
|
||||||
};
|
};
|
||||||
use atuin_common::utils::hash_str;
|
use atuin_common::utils::hash_str;
|
||||||
|
|
||||||
use crate::encryption::{decode_key, decrypt};
|
use crate::encryption::{decode_key, decrypt, EncryptedHistory};
|
||||||
use crate::history::History;
|
use crate::history::History;
|
||||||
|
|
||||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
static APP_USER_AGENT: &str = concat!("atuin/", env!("CARGO_PKG_VERSION"),);
|
||||||
|
|
||||||
// TODO: remove all references to the encryption key from this
|
// TODO: remove all references to the encryption key from this
|
||||||
// It should be handled *elsewhere*
|
// It should be handled *elsewhere*
|
||||||
|
|
||||||
pub struct Client<'a> {
|
pub struct Client<'a> {
|
||||||
sync_addr: &'a str,
|
sync_addr: &'a str,
|
||||||
token: &'a str,
|
|
||||||
key: secretbox::Key,
|
key: secretbox::Key,
|
||||||
client: reqwest::Client,
|
client: reqwest::Client,
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ pub fn register(
|
||||||
username: &str,
|
username: &str,
|
||||||
email: &str,
|
email: &str,
|
||||||
password: &str,
|
password: &str,
|
||||||
) -> Result<RegisterResponse> {
|
) -> Result<RegisterResponse<'static>> {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
map.insert("username", username);
|
map.insert("username", username);
|
||||||
map.insert("email", email);
|
map.insert("email", email);
|
||||||
|
@ -48,7 +48,7 @@ pub fn register(
|
||||||
let client = reqwest::blocking::Client::new();
|
let client = reqwest::blocking::Client::new();
|
||||||
let resp = client
|
let resp = client
|
||||||
.post(url)
|
.post(url)
|
||||||
.header(USER_AGENT, format!("atuin/{}", VERSION))
|
.header(USER_AGENT, APP_USER_AGENT)
|
||||||
.json(&map)
|
.json(&map)
|
||||||
.send()?;
|
.send()?;
|
||||||
|
|
||||||
|
@ -60,18 +60,14 @@ pub fn register(
|
||||||
Ok(session)
|
Ok(session)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn login(address: &str, username: &str, password: &str) -> Result<LoginResponse> {
|
pub fn login(address: &str, req: LoginRequest) -> Result<LoginResponse<'static>> {
|
||||||
let mut map = HashMap::new();
|
|
||||||
map.insert("username", username);
|
|
||||||
map.insert("password", password);
|
|
||||||
|
|
||||||
let url = format!("{}/login", address);
|
let url = format!("{}/login", address);
|
||||||
let client = reqwest::blocking::Client::new();
|
let client = reqwest::blocking::Client::new();
|
||||||
|
|
||||||
let resp = client
|
let resp = client
|
||||||
.post(url)
|
.post(url)
|
||||||
.header(USER_AGENT, format!("atuin/{}", VERSION))
|
.header(USER_AGENT, APP_USER_AGENT)
|
||||||
.json(&map)
|
.json(&req)
|
||||||
.send()?;
|
.send()?;
|
||||||
|
|
||||||
if resp.status() != reqwest::StatusCode::OK {
|
if resp.status() != reqwest::StatusCode::OK {
|
||||||
|
@ -83,31 +79,25 @@ pub fn login(address: &str, username: &str, password: &str) -> Result<LoginRespo
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Client<'a> {
|
impl<'a> Client<'a> {
|
||||||
pub fn new(sync_addr: &'a str, token: &'a str, key: String) -> Result<Self> {
|
pub fn new(sync_addr: &'a str, session_token: &'a str, key: String) -> Result<Self> {
|
||||||
|
let mut headers = HeaderMap::new();
|
||||||
|
headers.insert(AUTHORIZATION, format!("Token {}", session_token).parse()?);
|
||||||
|
|
||||||
Ok(Client {
|
Ok(Client {
|
||||||
sync_addr,
|
sync_addr,
|
||||||
token,
|
|
||||||
key: decode_key(key)?,
|
key: decode_key(key)?,
|
||||||
client: reqwest::Client::new(),
|
client: reqwest::Client::builder()
|
||||||
|
.user_agent(APP_USER_AGENT)
|
||||||
|
.default_headers(headers)
|
||||||
|
.build()?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn count(&self) -> Result<i64> {
|
pub async fn count(&self) -> Result<i64> {
|
||||||
let url = format!("{}/sync/count", self.sync_addr);
|
let url = format!("{}/sync/count", self.sync_addr);
|
||||||
let url = Url::parse(url.as_str())?;
|
let url = Url::parse(url.as_str())?;
|
||||||
let token = format!("Token {}", self.token);
|
|
||||||
let token = token.parse()?;
|
|
||||||
|
|
||||||
let mut headers = HeaderMap::new();
|
let resp = self.client.get(url).send().await?;
|
||||||
headers.insert(AUTHORIZATION, token);
|
|
||||||
|
|
||||||
let resp = self
|
|
||||||
.client
|
|
||||||
.get(url)
|
|
||||||
.header(USER_AGENT, format!("atuin/{}", VERSION))
|
|
||||||
.headers(headers)
|
|
||||||
.send()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if resp.status() != StatusCode::OK {
|
if resp.status() != StatusCode::OK {
|
||||||
return Err(eyre!("failed to get count (are you logged in?)"));
|
return Err(eyre!("failed to get count (are you logged in?)"));
|
||||||
|
@ -137,13 +127,7 @@ impl<'a> Client<'a> {
|
||||||
host,
|
host,
|
||||||
);
|
);
|
||||||
|
|
||||||
let resp = self
|
let resp = self.client.get(url).send().await?;
|
||||||
.client
|
|
||||||
.get(url)
|
|
||||||
.header(AUTHORIZATION, format!("Token {}", self.token))
|
|
||||||
.header(USER_AGENT, format!("atuin/{}", VERSION))
|
|
||||||
.send()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let history = resp.json::<SyncHistoryResponse>().await?;
|
let history = resp.json::<SyncHistoryResponse>().await?;
|
||||||
let history = history
|
let history = history
|
||||||
|
@ -156,41 +140,15 @@ impl<'a> Client<'a> {
|
||||||
Ok(history)
|
Ok(history)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn post_history(&self, history: &[AddHistoryRequest]) -> Result<()> {
|
pub async fn post_history(
|
||||||
|
&self,
|
||||||
|
history: &[AddHistoryRequest<'_, EncryptedHistory>],
|
||||||
|
) -> Result<()> {
|
||||||
let url = format!("{}/history", self.sync_addr);
|
let url = format!("{}/history", self.sync_addr);
|
||||||
let url = Url::parse(url.as_str())?;
|
let url = Url::parse(url.as_str())?;
|
||||||
|
|
||||||
self.client
|
self.client.post(url).json(history).send().await?;
|
||||||
.post(url)
|
|
||||||
.json(history)
|
|
||||||
.header(AUTHORIZATION, format!("Token {}", self.token))
|
|
||||||
.header(USER_AGENT, format!("atuin/{}", VERSION))
|
|
||||||
.send()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn login(&self, username: &str, password: &str) -> Result<LoginResponse> {
|
|
||||||
let mut map = HashMap::new();
|
|
||||||
map.insert("username", username);
|
|
||||||
map.insert("password", password);
|
|
||||||
|
|
||||||
let url = format!("{}/login", self.sync_addr);
|
|
||||||
let resp = self
|
|
||||||
.client
|
|
||||||
.post(url)
|
|
||||||
.json(&map)
|
|
||||||
.header(USER_AGENT, format!("atuin/{}", VERSION))
|
|
||||||
.send()
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if resp.status() != reqwest::StatusCode::OK {
|
|
||||||
return Err(eyre!("invalid login details"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let session = resp.json::<LoginResponse>().await?;
|
|
||||||
|
|
||||||
Ok(session)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ async fn sync_upload(
|
||||||
|
|
||||||
while local_count > remote_count {
|
while local_count > remote_count {
|
||||||
let last = db.before(cursor, HISTORY_PAGE_SIZE).await?;
|
let last = db.before(cursor, HISTORY_PAGE_SIZE).await?;
|
||||||
let mut buffer = Vec::<AddHistoryRequest>::new();
|
let mut buffer = Vec::new();
|
||||||
|
|
||||||
if last.is_empty() {
|
if last.is_empty() {
|
||||||
break;
|
break;
|
||||||
|
@ -107,13 +107,11 @@ async fn sync_upload(
|
||||||
|
|
||||||
for i in last {
|
for i in last {
|
||||||
let data = encrypt(&i, &key)?;
|
let data = encrypt(&i, &key)?;
|
||||||
let data = serde_json::to_string(&data)?;
|
|
||||||
|
|
||||||
let add_hist = AddHistoryRequest {
|
let add_hist = AddHistoryRequest {
|
||||||
id: i.id,
|
id: i.id.into(),
|
||||||
timestamp: i.timestamp,
|
timestamp: i.timestamp,
|
||||||
data,
|
data,
|
||||||
hostname: hash_str(i.hostname.as_str()),
|
hostname: hash_str(&i.hostname).into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
buffer.push(add_hist);
|
buffer.push(add_hist);
|
||||||
|
@ -132,8 +130,8 @@ async fn sync_upload(
|
||||||
|
|
||||||
pub async fn sync(settings: &Settings, force: bool, db: &mut (impl Database + Send)) -> Result<()> {
|
pub async fn sync(settings: &Settings, force: bool, db: &mut (impl Database + Send)) -> Result<()> {
|
||||||
let client = api_client::Client::new(
|
let client = api_client::Client::new(
|
||||||
settings.sync_address.as_str(),
|
&settings.sync_address,
|
||||||
settings.session_token.as_str(),
|
&settings.session_token,
|
||||||
load_encoded_key(settings)?,
|
load_encoded_key(settings)?,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
|
@ -1,43 +1,43 @@
|
||||||
use std::convert::Infallible;
|
use std::{borrow::Cow, convert::Infallible};
|
||||||
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use serde::Serialize;
|
use serde::{Deserialize, Serialize};
|
||||||
use warp::{reply::Response, Reply};
|
use warp::{reply::Response, Reply};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct UserResponse {
|
pub struct UserResponse<'a> {
|
||||||
pub username: String,
|
pub username: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct RegisterRequest {
|
pub struct RegisterRequest<'a> {
|
||||||
pub email: String,
|
pub email: Cow<'a, str>,
|
||||||
pub username: String,
|
pub username: Cow<'a, str>,
|
||||||
pub password: String,
|
pub password: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct RegisterResponse {
|
pub struct RegisterResponse<'a> {
|
||||||
pub session: String,
|
pub session: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct LoginRequest {
|
pub struct LoginRequest<'a> {
|
||||||
pub username: String,
|
pub username: Cow<'a, str>,
|
||||||
pub password: String,
|
pub password: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct LoginResponse {
|
pub struct LoginResponse<'a> {
|
||||||
pub session: String,
|
pub session: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct AddHistoryRequest {
|
pub struct AddHistoryRequest<'a, D> {
|
||||||
pub id: String,
|
pub id: Cow<'a, str>,
|
||||||
pub timestamp: chrono::DateTime<Utc>,
|
pub timestamp: chrono::DateTime<Utc>,
|
||||||
pub data: String,
|
pub data: D,
|
||||||
pub hostname: String,
|
pub hostname: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
@ -46,10 +46,10 @@ pub struct CountResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct SyncHistoryRequest {
|
pub struct SyncHistoryRequest<'a> {
|
||||||
pub sync_ts: chrono::DateTime<chrono::FixedOffset>,
|
pub sync_ts: chrono::DateTime<chrono::FixedOffset>,
|
||||||
pub history_ts: chrono::DateTime<chrono::FixedOffset>,
|
pub history_ts: chrono::DateTime<chrono::FixedOffset>,
|
||||||
pub host: String,
|
pub host: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
@ -58,38 +58,38 @@ pub struct SyncHistoryResponse {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct ErrorResponse {
|
pub struct ErrorResponse<'a> {
|
||||||
pub reason: String,
|
pub reason: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Reply for ErrorResponse {
|
impl Reply for ErrorResponse<'_> {
|
||||||
fn into_response(self) -> Response {
|
fn into_response(self) -> Response {
|
||||||
warp::reply::json(&self).into_response()
|
warp::reply::json(&self).into_response()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ErrorResponseStatus {
|
pub struct ErrorResponseStatus<'a> {
|
||||||
pub error: ErrorResponse,
|
pub error: ErrorResponse<'a>,
|
||||||
pub status: warp::http::StatusCode,
|
pub status: warp::http::StatusCode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Reply for ErrorResponseStatus {
|
impl Reply for ErrorResponseStatus<'_> {
|
||||||
fn into_response(self) -> Response {
|
fn into_response(self) -> Response {
|
||||||
warp::reply::with_status(self.error, self.status).into_response()
|
warp::reply::with_status(self.error, self.status).into_response()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ErrorResponse {
|
impl<'a> ErrorResponse<'a> {
|
||||||
pub fn with_status(self, status: warp::http::StatusCode) -> ErrorResponseStatus {
|
pub fn with_status(self, status: warp::http::StatusCode) -> ErrorResponseStatus<'a> {
|
||||||
ErrorResponseStatus {
|
ErrorResponseStatus {
|
||||||
error: self,
|
error: self,
|
||||||
status,
|
status,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reply(reason: &str) -> ErrorResponse {
|
pub fn reply(reason: &'a str) -> ErrorResponse {
|
||||||
Self {
|
Self {
|
||||||
reason: reason.to_string(),
|
reason: reason.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
#[macro_use]
|
#![forbid(unsafe_code)]
|
||||||
extern crate serde_derive;
|
|
||||||
|
|
||||||
pub mod api;
|
pub mod api;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
|
@ -13,9 +13,9 @@ pub trait Database {
|
||||||
async fn get_session_user(&self, token: &str) -> Result<User>;
|
async fn get_session_user(&self, token: &str) -> Result<User>;
|
||||||
async fn add_session(&self, session: &NewSession) -> Result<()>;
|
async fn add_session(&self, session: &NewSession) -> Result<()>;
|
||||||
|
|
||||||
async fn get_user(&self, username: String) -> Result<User>;
|
async fn get_user(&self, username: &str) -> Result<User>;
|
||||||
async fn get_user_session(&self, u: &User) -> Result<Session>;
|
async fn get_user_session(&self, u: &User) -> Result<Session>;
|
||||||
async fn add_user(&self, user: NewUser) -> Result<i64>;
|
async fn add_user(&self, user: &NewUser) -> Result<i64>;
|
||||||
|
|
||||||
async fn count_history(&self, user: &User) -> Result<i64>;
|
async fn count_history(&self, user: &User) -> Result<i64>;
|
||||||
async fn list_history(
|
async fn list_history(
|
||||||
|
@ -23,7 +23,7 @@ pub trait Database {
|
||||||
user: &User,
|
user: &User,
|
||||||
created_since: chrono::NaiveDateTime,
|
created_since: chrono::NaiveDateTime,
|
||||||
since: chrono::NaiveDateTime,
|
since: chrono::NaiveDateTime,
|
||||||
host: String,
|
host: &str,
|
||||||
) -> Result<Vec<History>>;
|
) -> Result<Vec<History>>;
|
||||||
async fn add_history(&self, history: &[NewHistory]) -> Result<()>;
|
async fn add_history(&self, history: &[NewHistory]) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@ impl Database for Postgres {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_user(&self, username: String) -> Result<User> {
|
async fn get_user(&self, username: &str) -> Result<User> {
|
||||||
let res: Option<User> =
|
let res: Option<User> =
|
||||||
sqlx::query_as::<_, User>("select * from users where username = $1")
|
sqlx::query_as::<_, User>("select * from users where username = $1")
|
||||||
.bind(username)
|
.bind(username)
|
||||||
|
@ -111,7 +111,7 @@ impl Database for Postgres {
|
||||||
user: &User,
|
user: &User,
|
||||||
created_since: chrono::NaiveDateTime,
|
created_since: chrono::NaiveDateTime,
|
||||||
since: chrono::NaiveDateTime,
|
since: chrono::NaiveDateTime,
|
||||||
host: String,
|
host: &str,
|
||||||
) -> Result<Vec<History>> {
|
) -> Result<Vec<History>> {
|
||||||
let res = sqlx::query_as::<_, History>(
|
let res = sqlx::query_as::<_, History>(
|
||||||
"select * from history
|
"select * from history
|
||||||
|
@ -137,6 +137,10 @@ impl Database for Postgres {
|
||||||
let mut tx = self.pool.begin().await?;
|
let mut tx = self.pool.begin().await?;
|
||||||
|
|
||||||
for i in history {
|
for i in history {
|
||||||
|
let client_id: &str = &i.client_id;
|
||||||
|
let hostname: &str = &i.hostname;
|
||||||
|
let data: &str = &i.data;
|
||||||
|
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
"insert into history
|
"insert into history
|
||||||
(client_id, user_id, hostname, timestamp, data)
|
(client_id, user_id, hostname, timestamp, data)
|
||||||
|
@ -144,11 +148,11 @@ impl Database for Postgres {
|
||||||
on conflict do nothing
|
on conflict do nothing
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
.bind(i.client_id)
|
.bind(client_id)
|
||||||
.bind(i.user_id)
|
.bind(i.user_id)
|
||||||
.bind(i.hostname)
|
.bind(hostname)
|
||||||
.bind(i.timestamp)
|
.bind(i.timestamp)
|
||||||
.bind(i.data)
|
.bind(data)
|
||||||
.execute(&mut tx)
|
.execute(&mut tx)
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
@ -158,16 +162,20 @@ impl Database for Postgres {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn add_user(&self, user: NewUser) -> Result<i64> {
|
async fn add_user(&self, user: &NewUser) -> Result<i64> {
|
||||||
|
let email: &str = &user.email;
|
||||||
|
let username: &str = &user.username;
|
||||||
|
let password: &str = &user.password;
|
||||||
|
|
||||||
let res: (i64,) = sqlx::query_as(
|
let res: (i64,) = sqlx::query_as(
|
||||||
"insert into users
|
"insert into users
|
||||||
(username, email, password)
|
(username, email, password)
|
||||||
values($1, $2, $3)
|
values($1, $2, $3)
|
||||||
returning id",
|
returning id",
|
||||||
)
|
)
|
||||||
.bind(user.username.as_str())
|
.bind(username)
|
||||||
.bind(user.email.as_str())
|
.bind(email)
|
||||||
.bind(user.password)
|
.bind(password)
|
||||||
.fetch_one(&self.pool)
|
.fetch_one(&self.pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -175,13 +183,15 @@ impl Database for Postgres {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn add_session(&self, session: &NewSession) -> Result<()> {
|
async fn add_session(&self, session: &NewSession) -> Result<()> {
|
||||||
|
let token: &str = &session.token;
|
||||||
|
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
"insert into sessions
|
"insert into sessions
|
||||||
(user_id, token)
|
(user_id, token)
|
||||||
values($1, $2)",
|
values($1, $2)",
|
||||||
)
|
)
|
||||||
.bind(session.user_id)
|
.bind(session.user_id)
|
||||||
.bind(session.token)
|
.bind(token)
|
||||||
.execute(&self.pool)
|
.execute(&self.pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ use atuin_common::api::*;
|
||||||
pub async fn count(
|
pub async fn count(
|
||||||
user: User,
|
user: User,
|
||||||
db: impl Database + Clone + Send + Sync,
|
db: impl Database + Clone + Send + Sync,
|
||||||
) -> JSONResult<ErrorResponseStatus> {
|
) -> JSONResult<ErrorResponseStatus<'static>> {
|
||||||
db.count_history(&user).await.map_or(
|
db.count_history(&user).await.map_or(
|
||||||
reply_error(
|
reply_error(
|
||||||
ErrorResponse::reply("failed to query history count")
|
ErrorResponse::reply("failed to query history count")
|
||||||
|
@ -17,16 +17,16 @@ pub async fn count(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn list(
|
pub async fn list(
|
||||||
req: SyncHistoryRequest,
|
req: SyncHistoryRequest<'_>,
|
||||||
user: User,
|
user: User,
|
||||||
db: impl Database + Clone + Send + Sync,
|
db: impl Database + Clone + Send + Sync,
|
||||||
) -> JSONResult<ErrorResponseStatus> {
|
) -> JSONResult<ErrorResponseStatus<'static>> {
|
||||||
let history = db
|
let history = db
|
||||||
.list_history(
|
.list_history(
|
||||||
&user,
|
&user,
|
||||||
req.sync_ts.naive_utc(),
|
req.sync_ts.naive_utc(),
|
||||||
req.history_ts.naive_utc(),
|
req.history_ts.naive_utc(),
|
||||||
req.host,
|
&req.host,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
@ -54,20 +54,20 @@ pub async fn list(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add(
|
pub async fn add(
|
||||||
req: Vec<AddHistoryRequest>,
|
req: Vec<AddHistoryRequest<'_, String>>,
|
||||||
user: User,
|
user: User,
|
||||||
db: impl Database + Clone + Send + Sync,
|
db: impl Database + Clone + Send + Sync,
|
||||||
) -> ReplyResult<impl Reply, ErrorResponseStatus> {
|
) -> ReplyResult<impl Reply, ErrorResponseStatus<'_>> {
|
||||||
debug!("request to add {} history items", req.len());
|
debug!("request to add {} history items", req.len());
|
||||||
|
|
||||||
let history: Vec<NewHistory> = req
|
let history: Vec<NewHistory> = req
|
||||||
.iter()
|
.into_iter()
|
||||||
.map(|h| NewHistory {
|
.map(|h| NewHistory {
|
||||||
client_id: h.id.as_str(),
|
client_id: h.id,
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
hostname: h.hostname.as_str(),
|
hostname: h.hostname,
|
||||||
timestamp: h.timestamp.naive_utc(),
|
timestamp: h.timestamp.naive_utc(),
|
||||||
data: h.data.as_str(),
|
data: h.data.into(),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::borrow::Borrow;
|
||||||
|
|
||||||
use atuin_common::api::*;
|
use atuin_common::api::*;
|
||||||
use atuin_common::utils::hash_secret;
|
use atuin_common::utils::hash_secret;
|
||||||
use sodiumoxide::crypto::pwhash::argon2id13;
|
use sodiumoxide::crypto::pwhash::argon2id13;
|
||||||
|
@ -23,10 +25,10 @@ pub fn verify_str(secret: &str, verify: &str) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get(
|
pub async fn get(
|
||||||
username: String,
|
username: impl AsRef<str>,
|
||||||
db: impl Database + Clone + Send + Sync,
|
db: impl Database + Clone + Send + Sync,
|
||||||
) -> JSONResult<ErrorResponseStatus> {
|
) -> JSONResult<ErrorResponseStatus<'static>> {
|
||||||
let user = match db.get_user(username).await {
|
let user = match db.get_user(username.as_ref()).await {
|
||||||
Ok(user) => user,
|
Ok(user) => user,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug!("user not found: {}", e);
|
debug!("user not found: {}", e);
|
||||||
|
@ -37,15 +39,15 @@ pub async fn get(
|
||||||
};
|
};
|
||||||
|
|
||||||
reply_json(UserResponse {
|
reply_json(UserResponse {
|
||||||
username: user.username,
|
username: user.username.into(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn register(
|
pub async fn register(
|
||||||
register: RegisterRequest,
|
register: RegisterRequest<'_>,
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
db: impl Database + Clone + Send + Sync,
|
db: impl Database + Clone + Send + Sync,
|
||||||
) -> JSONResult<ErrorResponseStatus> {
|
) -> JSONResult<ErrorResponseStatus<'static>> {
|
||||||
if !settings.open_registration {
|
if !settings.open_registration {
|
||||||
return reply_error(
|
return reply_error(
|
||||||
ErrorResponse::reply("this server is not open for registrations")
|
ErrorResponse::reply("this server is not open for registrations")
|
||||||
|
@ -53,15 +55,15 @@ pub async fn register(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let hashed = hash_secret(register.password.as_str());
|
let hashed = hash_secret(®ister.password);
|
||||||
|
|
||||||
let new_user = NewUser {
|
let new_user = NewUser {
|
||||||
email: register.email,
|
email: register.email,
|
||||||
username: register.username,
|
username: register.username,
|
||||||
password: hashed,
|
password: hashed.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let user_id = match db.add_user(new_user).await {
|
let user_id = match db.add_user(&new_user).await {
|
||||||
Ok(id) => id,
|
Ok(id) => id,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("failed to add user: {}", e);
|
error!("failed to add user: {}", e);
|
||||||
|
@ -75,11 +77,13 @@ pub async fn register(
|
||||||
|
|
||||||
let new_session = NewSession {
|
let new_session = NewSession {
|
||||||
user_id,
|
user_id,
|
||||||
token: token.as_str(),
|
token: (&token).into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
match db.add_session(&new_session).await {
|
match db.add_session(&new_session).await {
|
||||||
Ok(_) => reply_json(RegisterResponse { session: token }),
|
Ok(_) => reply_json(RegisterResponse {
|
||||||
|
session: token.into(),
|
||||||
|
}),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("failed to add session: {}", e);
|
error!("failed to add session: {}", e);
|
||||||
reply_error(
|
reply_error(
|
||||||
|
@ -91,10 +95,10 @@ pub async fn register(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn login(
|
pub async fn login(
|
||||||
login: LoginRequest,
|
login: LoginRequest<'_>,
|
||||||
db: impl Database + Clone + Send + Sync,
|
db: impl Database + Clone + Send + Sync,
|
||||||
) -> JSONResult<ErrorResponseStatus> {
|
) -> JSONResult<ErrorResponseStatus<'_>> {
|
||||||
let user = match db.get_user(login.username.clone()).await {
|
let user = match db.get_user(login.username.borrow()).await {
|
||||||
Ok(u) => u,
|
Ok(u) => u,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("failed to get user {}: {}", login.username.clone(), e);
|
error!("failed to get user {}: {}", login.username.clone(), e);
|
||||||
|
@ -116,7 +120,7 @@ pub async fn login(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let verified = verify_str(user.password.as_str(), login.password.as_str());
|
let verified = verify_str(user.password.as_str(), login.password.borrow());
|
||||||
|
|
||||||
if !verified {
|
if !verified {
|
||||||
return reply_error(
|
return reply_error(
|
||||||
|
@ -125,6 +129,6 @@ pub async fn login(
|
||||||
}
|
}
|
||||||
|
|
||||||
reply_json(LoginResponse {
|
reply_json(LoginResponse {
|
||||||
session: session.token,
|
session: session.token.into(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
|
|
||||||
#[derive(sqlx::FromRow)]
|
#[derive(sqlx::FromRow)]
|
||||||
|
@ -14,12 +16,12 @@ pub struct History {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NewHistory<'a> {
|
pub struct NewHistory<'a> {
|
||||||
pub client_id: &'a str,
|
pub client_id: Cow<'a, str>,
|
||||||
pub user_id: i64,
|
pub user_id: i64,
|
||||||
pub hostname: &'a str,
|
pub hostname: Cow<'a, str>,
|
||||||
pub timestamp: chrono::NaiveDateTime,
|
pub timestamp: chrono::NaiveDateTime,
|
||||||
|
|
||||||
pub data: &'a str,
|
pub data: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(sqlx::FromRow)]
|
#[derive(sqlx::FromRow)]
|
||||||
|
@ -37,13 +39,13 @@ pub struct Session {
|
||||||
pub token: String,
|
pub token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NewUser {
|
pub struct NewUser<'a> {
|
||||||
pub username: String,
|
pub username: Cow<'a, str>,
|
||||||
pub email: String,
|
pub email: Cow<'a, str>,
|
||||||
pub password: String,
|
pub password: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct NewSession<'a> {
|
pub struct NewSession<'a> {
|
||||||
pub user_id: i64,
|
pub user_id: i64,
|
||||||
pub token: &'a str,
|
pub token: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::fs::File;
|
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
|
use std::{borrow::Cow, fs::File};
|
||||||
|
|
||||||
|
use atuin_common::api::LoginRequest;
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
@ -34,8 +35,10 @@ impl Cmd {
|
||||||
|
|
||||||
let session = api_client::login(
|
let session = api_client::login(
|
||||||
settings.sync_address.as_str(),
|
settings.sync_address.as_str(),
|
||||||
self.username.as_str(),
|
LoginRequest {
|
||||||
self.password.as_str(),
|
username: Cow::Borrowed(&self.username),
|
||||||
|
password: Cow::Borrowed(&self.password),
|
||||||
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let session_path = settings.session_path.as_str();
|
let session_path = settings.session_path.as_str();
|
||||||
|
|
Loading…
Reference in a new issue