Compare commits
8 Commits
main
...
invitation
Author | SHA1 | Date |
---|---|---|
Savanni D'Gerinel | 8133ebc8bf | |
Savanni D'Gerinel | deb3415c30 | |
Savanni D'Gerinel | 064b754786 | |
Savanni D'Gerinel | 95a597c8ea | |
Savanni D'Gerinel | 45df1233e5 | |
Savanni D'Gerinel | 2c1c19690f | |
Savanni D'Gerinel | 5d33f62be5 | |
Savanni D'Gerinel | 772d2c01f4 |
|
@ -6,6 +6,7 @@ edition = "2018"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
rusqlite = { version = "0.26" }
|
||||
serde = { version = "1" }
|
||||
serde_derive = { version = "1" }
|
||||
thiserror = { version = "1" }
|
||||
|
|
|
@ -5,6 +5,8 @@ use thiserror::Error;
|
|||
/// down and that the administrator fix a problem.
|
||||
#[derive(Debug, Error)]
|
||||
pub enum FatalError {
|
||||
#[error("database is inconsistent")]
|
||||
DatabaseInconsistency,
|
||||
#[error("disk is full")]
|
||||
DiskFull,
|
||||
#[error("io error: {0}")]
|
||||
|
@ -24,8 +26,3 @@ pub fn error<A, E>(err: E) -> AppResult<A, E> {
|
|||
pub fn fatal<A, E>(err: FatalError) -> AppResult<A, E> {
|
||||
Err(err)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn maybe_fail<E>(possible_errors: Vec<E>) -> AppResult<(), E> {
|
||||
ok(())
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
pub mod errors;
|
||||
pub mod types;
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
mod character;
|
||||
use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ValueRef};
|
||||
use std::{convert::Infallible, str::FromStr};
|
||||
use thiserror::Error;
|
||||
|
||||
mod character;
|
||||
pub use character::CharacterSheet;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
|
@ -12,3 +15,145 @@ pub struct Roll {
|
|||
dice: Vec<Die>,
|
||||
modifier: i8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Error, PartialEq)]
|
||||
pub enum AuthenticationError {
|
||||
#[error("username already exists")]
|
||||
DuplicateUsername,
|
||||
#[error("invitation is not valid")]
|
||||
InvalidInvitation,
|
||||
#[error("session token not found")]
|
||||
InvalidSession,
|
||||
#[error("user not found")]
|
||||
UserNotFound,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct SessionToken(String);
|
||||
|
||||
impl From<&str> for SessionToken {
|
||||
fn from(s: &str) -> Self {
|
||||
SessionToken(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for SessionToken {
|
||||
type Err = Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(SessionToken(s.to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SessionToken> for String {
|
||||
fn from(s: SessionToken) -> Self {
|
||||
s.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct Invitation(String);
|
||||
|
||||
impl From<&str> for Invitation {
|
||||
fn from(s: &str) -> Self {
|
||||
Invitation(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Invitation {
|
||||
type Err = Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Invitation(s.to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Invitation> for String {
|
||||
fn from(s: Invitation) -> Self {
|
||||
s.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct UserId(String);
|
||||
|
||||
impl From<&str> for UserId {
|
||||
fn from(s: &str) -> Self {
|
||||
UserId(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for UserId {
|
||||
type Err = Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(UserId(s.to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UserId> for String {
|
||||
fn from(s: UserId) -> Self {
|
||||
s.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql for UserId {
|
||||
fn column_result(val: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||
match val {
|
||||
ValueRef::Text(t) => Ok(UserId::from(
|
||||
String::from_utf8(Vec::from(t)).unwrap().as_ref(),
|
||||
)),
|
||||
_ => Err(FromSqlError::InvalidType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
pub struct Username(String);
|
||||
|
||||
impl From<&str> for Username {
|
||||
fn from(s: &str) -> Self {
|
||||
Username(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Username {
|
||||
type Err = Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Username(s.to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Username> for String {
|
||||
fn from(s: &Username) -> Self {
|
||||
s.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Username> for String {
|
||||
fn from(s: Username) -> Self {
|
||||
s.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql for Username {
|
||||
fn column_result(val: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||
match val {
|
||||
ValueRef::Text(t) => Ok(Username::from(
|
||||
String::from_utf8(Vec::from(t)).unwrap().as_ref(),
|
||||
)),
|
||||
_ => Err(FromSqlError::InvalidType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct AuthenticateParams {
|
||||
pub invitation: Invitation,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct AuthenticateResponse {
|
||||
pub session_token: SessionToken,
|
||||
}
|
||||
|
|
14
flake.lock
14
flake.lock
|
@ -33,11 +33,11 @@
|
|||
},
|
||||
"nixpkgs_22_05": {
|
||||
"locked": {
|
||||
"lastModified": 1668459637,
|
||||
"narHash": "sha256-HqnWCKujmtu8v0CjzOT0sr7m2AR7+vpbZJOp1R0rodY=",
|
||||
"lastModified": 1668766498,
|
||||
"narHash": "sha256-UjZlIrbHGlL3H3HZNPTxPSwJfr49jIfbPWCYxk0EQm4=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "16f4e04658c2ab10114545af2f39db17d51bd1bd",
|
||||
"rev": "f42a45c015f28ac3beeb0df360e50cdbf495d44b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -48,11 +48,11 @@
|
|||
},
|
||||
"nixpkgs_unstable": {
|
||||
"locked": {
|
||||
"lastModified": 1668505710,
|
||||
"narHash": "sha256-DulcfsGjpSXL9Ma0iQIsb3HRbARCDcA+CNH67pPyMQ0=",
|
||||
"lastModified": 1668765800,
|
||||
"narHash": "sha256-rC40+/W6Hio7b/RsY8SvQPKNx4WqNcTgfYv8cUMAvJk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "85d6b3990def7eef45f4502a82496de02a02b6e8",
|
||||
"rev": "52b2ac8ae18bbad4374ff0dd5aeee0fdf1aea739",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -67,7 +67,7 @@
|
|||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"narHash": "sha256-WjyKSpFY44ysIHSN3C0L5PKUJuwXDnSg6p5OcYwbZZ4=",
|
||||
"narHash": "sha256-F2ro05D6tGMwSaOYeIediJq6X0ATD7JgWEG2TgOs9Wo=",
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/oxalica/rust-overlay/archive/master.tar.gz"
|
||||
},
|
||||
|
|
|
@ -74,6 +74,16 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rusqlite",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.5"
|
||||
|
@ -716,6 +726,7 @@ name = "server"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"common",
|
||||
"rand",
|
||||
"rusqlite",
|
||||
"serde",
|
||||
|
|
|
@ -8,6 +8,7 @@ default-run = "server"
|
|||
|
||||
[dependencies]
|
||||
anyhow = { version = "1" }
|
||||
common = { path = "../common" }
|
||||
rand = { version = "0.8" }
|
||||
rusqlite = { version = "0.26" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
|
|
@ -1,149 +1,14 @@
|
|||
use crate::errors::{error, fatal, ok, AppResult};
|
||||
use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ValueRef};
|
||||
use common::{
|
||||
errors::{error, fatal, ok, AppResult, FatalError},
|
||||
types::{AuthenticationError, Invitation, SessionToken, UserId, Username},
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use std::{convert::Infallible, str::FromStr};
|
||||
use thiserror::Error;
|
||||
use uuid::{adapter::Hyphenated, Uuid};
|
||||
|
||||
#[cfg(test)]
|
||||
use crate::errors::maybe_fail;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum AuthenticationError {
|
||||
#[error("username already exists")]
|
||||
DuplicateUsername,
|
||||
#[error("invitation is not valid")]
|
||||
InvalidInvitation,
|
||||
#[error("session token not found")]
|
||||
InvalidSession,
|
||||
#[error("user not found")]
|
||||
UserNotFound,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct SessionToken(String);
|
||||
|
||||
impl From<&str> for SessionToken {
|
||||
fn from(s: &str) -> Self {
|
||||
SessionToken(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for SessionToken {
|
||||
type Err = Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(SessionToken(s.to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SessionToken> for String {
|
||||
fn from(s: SessionToken) -> Self {
|
||||
s.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Invitation(String);
|
||||
|
||||
impl From<&str> for Invitation {
|
||||
fn from(s: &str) -> Self {
|
||||
Invitation(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Invitation {
|
||||
type Err = Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Invitation(s.to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Invitation> for String {
|
||||
fn from(s: Invitation) -> Self {
|
||||
s.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct UserId(String);
|
||||
|
||||
impl From<&str> for UserId {
|
||||
fn from(s: &str) -> Self {
|
||||
UserId(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for UserId {
|
||||
type Err = Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(UserId(s.to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UserId> for String {
|
||||
fn from(s: UserId) -> Self {
|
||||
s.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql for UserId {
|
||||
fn column_result(val: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||
match val {
|
||||
ValueRef::Text(t) => Ok(UserId::from(
|
||||
String::from_utf8(Vec::from(t)).unwrap().as_ref(),
|
||||
)),
|
||||
_ => Err(FromSqlError::InvalidType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Username(String);
|
||||
|
||||
impl From<&str> for Username {
|
||||
fn from(s: &str) -> Self {
|
||||
Username(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Username {
|
||||
type Err = Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Username(s.to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Username> for String {
|
||||
fn from(s: &Username) -> Self {
|
||||
s.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Username> for String {
|
||||
fn from(s: Username) -> Self {
|
||||
s.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql for Username {
|
||||
fn column_result(val: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||
match val {
|
||||
ValueRef::Text(t) => Ok(Username::from(
|
||||
String::from_utf8(Vec::from(t)).unwrap().as_ref(),
|
||||
)),
|
||||
_ => Err(FromSqlError::InvalidType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AuthenticationDB: Send + Sync {
|
||||
fn create_user(&mut self, username: Username) -> AppResult<UserId, AuthenticationError>;
|
||||
|
||||
fn create_invitation(&mut self, user: UserId) -> AppResult<Invitation, AuthenticationError>;
|
||||
fn create_invitation(&mut self, user: &UserId) -> AppResult<Invitation, AuthenticationError>;
|
||||
|
||||
fn authenticate(
|
||||
&mut self,
|
||||
|
@ -156,11 +21,16 @@ pub trait AuthenticationDB: Send + Sync {
|
|||
|
||||
fn delete_user(&mut self, user: UserId) -> AppResult<(), AuthenticationError>;
|
||||
|
||||
fn validate_session(&self, session: SessionToken) -> AppResult<(), AuthenticationError>;
|
||||
fn validate_session(
|
||||
&self,
|
||||
session: SessionToken,
|
||||
) -> AppResult<(Username, UserId), AuthenticationError>;
|
||||
|
||||
fn get_user_id(&self, username: Username) -> AppResult<UserId, AuthenticationError>;
|
||||
fn get_userid(&self, username: &Username) -> AppResult<UserId, AuthenticationError>;
|
||||
|
||||
fn list_users(&self) -> AppResult<Vec<Username>, AuthenticationError>;
|
||||
fn get_username(&self, userid: &UserId) -> AppResult<Username, AuthenticationError>;
|
||||
|
||||
fn list_users(&self) -> AppResult<Vec<UserId>, AuthenticationError>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
|
@ -173,8 +43,9 @@ pub struct MemoryAuth {
|
|||
|
||||
impl AuthenticationDB for MemoryAuth {
|
||||
fn create_user(&mut self, username: Username) -> AppResult<UserId, AuthenticationError> {
|
||||
#[cfg(test)]
|
||||
let _ = maybe_fail::<AuthenticationError>(vec![])?;
|
||||
if self.users.contains_key(&username) {
|
||||
return Ok(Err(AuthenticationError::DuplicateUsername));
|
||||
}
|
||||
|
||||
let userid = UserId::from(format!("{}", Hyphenated::from_uuid(Uuid::new_v4())).as_str());
|
||||
self.users.insert(username.clone(), userid.clone());
|
||||
|
@ -183,17 +54,14 @@ impl AuthenticationDB for MemoryAuth {
|
|||
ok(userid)
|
||||
}
|
||||
|
||||
fn create_invitation(&mut self, user: UserId) -> AppResult<Invitation, AuthenticationError> {
|
||||
#[cfg(test)]
|
||||
let _ = maybe_fail::<AuthenticationError>(vec![])?;
|
||||
|
||||
fn create_invitation(&mut self, user: &UserId) -> AppResult<Invitation, AuthenticationError> {
|
||||
if !self.inverse_users.contains_key(&user) {
|
||||
return error::<Invitation, AuthenticationError>(AuthenticationError::UserNotFound);
|
||||
}
|
||||
|
||||
let invitation =
|
||||
Invitation::from(format!("{}", Hyphenated::from_uuid(Uuid::new_v4())).as_str());
|
||||
self.invitations.insert(invitation.clone(), user);
|
||||
self.invitations.insert(invitation.clone(), user.clone());
|
||||
ok(invitation)
|
||||
}
|
||||
|
||||
|
@ -201,9 +69,6 @@ impl AuthenticationDB for MemoryAuth {
|
|||
&mut self,
|
||||
invitation: Invitation,
|
||||
) -> AppResult<SessionToken, AuthenticationError> {
|
||||
#[cfg(test)]
|
||||
let _ = maybe_fail::<AuthenticationError>(vec![])?;
|
||||
|
||||
if let Some(user) = self.invitations.get(&invitation) {
|
||||
let session_token =
|
||||
SessionToken::from(format!("{}", Hyphenated::from_uuid(Uuid::new_v4())).as_str());
|
||||
|
@ -216,9 +81,6 @@ impl AuthenticationDB for MemoryAuth {
|
|||
}
|
||||
|
||||
fn delete_session(&mut self, session: SessionToken) -> AppResult<(), AuthenticationError> {
|
||||
#[cfg(test)]
|
||||
let _ = maybe_fail::<AuthenticationError>(vec![])?;
|
||||
|
||||
if let Some(_) = self.sessions.remove(&session) {
|
||||
ok(())
|
||||
} else {
|
||||
|
@ -227,9 +89,6 @@ impl AuthenticationDB for MemoryAuth {
|
|||
}
|
||||
|
||||
fn delete_invitation(&mut self, invitation: Invitation) -> AppResult<(), AuthenticationError> {
|
||||
#[cfg(test)]
|
||||
let _ = maybe_fail::<AuthenticationError>(vec![])?;
|
||||
|
||||
if let Some(_) = self.invitations.remove(&invitation) {
|
||||
ok(())
|
||||
} else {
|
||||
|
@ -238,9 +97,6 @@ impl AuthenticationDB for MemoryAuth {
|
|||
}
|
||||
|
||||
fn delete_user(&mut self, user: UserId) -> AppResult<(), AuthenticationError> {
|
||||
#[cfg(test)]
|
||||
let _ = maybe_fail::<AuthenticationError>(vec![])?;
|
||||
|
||||
if let Some(username) = self.inverse_users.remove(&user) {
|
||||
let _ = self.users.remove(&username);
|
||||
self.invitations = self
|
||||
|
@ -261,15 +117,22 @@ impl AuthenticationDB for MemoryAuth {
|
|||
}
|
||||
}
|
||||
|
||||
fn validate_session(&self, session: SessionToken) -> AppResult<(), AuthenticationError> {
|
||||
if self.sessions.contains_key(&session) {
|
||||
ok(())
|
||||
fn validate_session(
|
||||
&self,
|
||||
session: SessionToken,
|
||||
) -> AppResult<(Username, UserId), AuthenticationError> {
|
||||
if let Some(userid) = self.sessions.get(&session) {
|
||||
if let Some(username) = self.inverse_users.get(&userid) {
|
||||
ok((username.clone(), userid.clone()))
|
||||
} else {
|
||||
fatal(FatalError::DatabaseInconsistency)
|
||||
}
|
||||
} else {
|
||||
error::<(), AuthenticationError>(AuthenticationError::InvalidSession)
|
||||
error(AuthenticationError::InvalidSession)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_user_id(&self, username: Username) -> AppResult<UserId, AuthenticationError> {
|
||||
fn get_userid(&self, username: &Username) -> AppResult<UserId, AuthenticationError> {
|
||||
Ok(self
|
||||
.users
|
||||
.get(&username)
|
||||
|
@ -277,10 +140,255 @@ impl AuthenticationDB for MemoryAuth {
|
|||
.ok_or(AuthenticationError::UserNotFound))
|
||||
}
|
||||
|
||||
fn list_users(&self) -> AppResult<Vec<Username>, AuthenticationError> {
|
||||
ok(self.users.keys().cloned().collect::<Vec<Username>>())
|
||||
fn get_username(&self, userid: &UserId) -> AppResult<Username, AuthenticationError> {
|
||||
Ok(self
|
||||
.inverse_users
|
||||
.get(&userid)
|
||||
.map(|u| u.clone())
|
||||
.ok_or(AuthenticationError::UserNotFound))
|
||||
}
|
||||
|
||||
fn list_users(&self) -> AppResult<Vec<UserId>, AuthenticationError> {
|
||||
ok(self.inverse_users.keys().cloned().collect::<Vec<UserId>>())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {}
|
||||
mod test {
|
||||
use super::*;
|
||||
use std::panic;
|
||||
|
||||
fn with_memory_db<F>(test: F)
|
||||
where
|
||||
F: Fn(Box<dyn AuthenticationDB>) -> () + panic::UnwindSafe,
|
||||
{
|
||||
let authdb: Box<MemoryAuth> = Box::new(Default::default());
|
||||
test(authdb);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_create_a_user() {
|
||||
fn test_case(mut authdb: Box<dyn AuthenticationDB>) {
|
||||
let username = Username::from("shephard");
|
||||
let userid = authdb
|
||||
.create_user(username.clone())
|
||||
.expect("no fatal errors")
|
||||
.expect("creation should succeed");
|
||||
assert_eq!(authdb.get_userid(&username).unwrap(), Ok(userid.clone()));
|
||||
assert_eq!(authdb.get_username(&userid).unwrap(), Ok(username));
|
||||
assert!(authdb.list_users().unwrap().unwrap().contains(&userid));
|
||||
}
|
||||
|
||||
with_memory_db(test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_does_not_allow_duplicate_users() {
|
||||
fn test_case(mut authdb: Box<dyn AuthenticationDB>) {
|
||||
let username = Username::from("shephard");
|
||||
let _ = authdb
|
||||
.create_user(username.clone())
|
||||
.expect("no fatal errors")
|
||||
.expect("creation should succeed");
|
||||
assert_eq!(
|
||||
authdb.create_user(username.clone()).unwrap(),
|
||||
Err(AuthenticationError::DuplicateUsername)
|
||||
);
|
||||
}
|
||||
with_memory_db(test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_exchanges_an_invitation_for_a_session() {
|
||||
fn test_case(mut authdb: Box<dyn AuthenticationDB>) {
|
||||
let username = Username::from("shephard");
|
||||
let userid = authdb
|
||||
.create_user(username.clone())
|
||||
.expect("no fatal errors")
|
||||
.expect("user to be created");
|
||||
let invitation = authdb
|
||||
.create_invitation(&userid)
|
||||
.expect("no fatal errors")
|
||||
.expect("invitation to be created");
|
||||
let _ = authdb
|
||||
.authenticate(invitation)
|
||||
.expect("no fatal errors")
|
||||
.expect("to receive a session token");
|
||||
}
|
||||
with_memory_db(test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_identifies_a_user_by_session() {
|
||||
fn test_case(mut authdb: Box<dyn AuthenticationDB>) {
|
||||
let shephard = Username::from("shephard");
|
||||
let garrus = Username::from("garrus");
|
||||
|
||||
let (shephard_session, shephard_id) = {
|
||||
let userid = authdb.create_user(shephard.clone()).unwrap().unwrap();
|
||||
let invitation = authdb.create_invitation(&userid).unwrap().unwrap();
|
||||
(authdb.authenticate(invitation).unwrap().unwrap(), userid)
|
||||
};
|
||||
|
||||
let (garrus_session, garrus_id) = {
|
||||
let userid = authdb.create_user(garrus.clone()).unwrap().unwrap();
|
||||
let invitation = authdb.create_invitation(&userid).unwrap().unwrap();
|
||||
(authdb.authenticate(invitation).unwrap().unwrap(), userid)
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
authdb.validate_session(shephard_session).unwrap().unwrap(),
|
||||
(shephard, shephard_id)
|
||||
);
|
||||
assert_eq!(
|
||||
authdb.validate_session(garrus_session).unwrap().unwrap(),
|
||||
(garrus, garrus_id)
|
||||
);
|
||||
}
|
||||
with_memory_db(test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_allows_multiple_invitations_per_user() {
|
||||
fn test_case(mut authdb: Box<dyn AuthenticationDB>) {
|
||||
let username = Username::from("shephard");
|
||||
let userid = authdb.create_user(username).unwrap().unwrap();
|
||||
let invite1 = authdb.create_invitation(&userid).unwrap().unwrap();
|
||||
let invite2 = authdb.create_invitation(&userid).unwrap().unwrap();
|
||||
|
||||
assert_ne!(invite1, invite2);
|
||||
|
||||
authdb.authenticate(invite1).unwrap().unwrap();
|
||||
authdb.authenticate(invite2).unwrap().unwrap();
|
||||
}
|
||||
with_memory_db(test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_allows_multiple_sessions_per_user() {
|
||||
fn test_case(mut authdb: Box<dyn AuthenticationDB>) {
|
||||
let username = Username::from("shephard");
|
||||
let userid = authdb.create_user(username.clone()).unwrap().unwrap();
|
||||
let invite1 = authdb.create_invitation(&userid).unwrap().unwrap();
|
||||
let invite2 = authdb.create_invitation(&userid).unwrap().unwrap();
|
||||
|
||||
assert_ne!(invite1, invite2);
|
||||
|
||||
let session1 = authdb.authenticate(invite1).unwrap().unwrap();
|
||||
let session2 = authdb.authenticate(invite2).unwrap().unwrap();
|
||||
|
||||
assert_ne!(session1, session2);
|
||||
assert_eq!(
|
||||
authdb.validate_session(session1).unwrap().unwrap(),
|
||||
(username.clone(), userid.clone())
|
||||
);
|
||||
assert_eq!(
|
||||
authdb.validate_session(session2).unwrap().unwrap(),
|
||||
(username, userid)
|
||||
);
|
||||
}
|
||||
with_memory_db(test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_disallows_invitation_reuse() {
|
||||
fn test_case(mut authdb: Box<dyn AuthenticationDB>) {
|
||||
let username = Username::from("shephard");
|
||||
let userid = authdb
|
||||
.create_user(username.clone())
|
||||
.expect("no fatal errors")
|
||||
.expect("user to be created");
|
||||
let invitation = authdb
|
||||
.create_invitation(&userid)
|
||||
.expect("no fatal errors")
|
||||
.expect("invitation to be created");
|
||||
let _ = authdb
|
||||
.authenticate(invitation.clone())
|
||||
.expect("no fatal errors")
|
||||
.expect("to receive a session token");
|
||||
let reuse_result = authdb.authenticate(invitation).expect("no fatal errors");
|
||||
assert_eq!(reuse_result, Err(AuthenticationError::InvalidInvitation));
|
||||
}
|
||||
with_memory_db(test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_deletes_a_user_and_invalidates_tokens() {
|
||||
fn test_case(mut authdb: Box<dyn AuthenticationDB>) {
|
||||
let shephard = Username::from("shephard");
|
||||
let garrus = Username::from("garrus");
|
||||
|
||||
let (shephard_session, shephard_id) = {
|
||||
let userid = authdb.create_user(shephard.clone()).unwrap().unwrap();
|
||||
let invitation = authdb.create_invitation(&userid).unwrap().unwrap();
|
||||
(authdb.authenticate(invitation).unwrap().unwrap(), userid)
|
||||
};
|
||||
|
||||
let (garrus_invitation, garrus_id) = {
|
||||
let userid = authdb.create_user(garrus.clone()).unwrap().unwrap();
|
||||
(authdb.create_invitation(&userid).unwrap().unwrap(), userid)
|
||||
};
|
||||
|
||||
authdb.delete_user(shephard_id).unwrap().unwrap();
|
||||
assert_eq!(
|
||||
authdb.validate_session(shephard_session).unwrap(),
|
||||
Err(AuthenticationError::InvalidSession)
|
||||
);
|
||||
|
||||
authdb.delete_user(garrus_id).unwrap().unwrap();
|
||||
assert_eq!(
|
||||
authdb.authenticate(garrus_invitation).unwrap(),
|
||||
Err(AuthenticationError::InvalidInvitation)
|
||||
);
|
||||
}
|
||||
with_memory_db(test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_deletes_a_session() {
|
||||
fn test_case(mut authdb: Box<dyn AuthenticationDB>) {
|
||||
let username = Username::from("shephard");
|
||||
let userid = authdb
|
||||
.create_user(username.clone())
|
||||
.expect("no fatal errors")
|
||||
.expect("user to be created");
|
||||
let invitation = authdb
|
||||
.create_invitation(&userid)
|
||||
.expect("no fatal errors")
|
||||
.expect("invitation to be created");
|
||||
let session = authdb.authenticate(invitation).unwrap().unwrap();
|
||||
|
||||
authdb.delete_session(session.clone()).unwrap().unwrap();
|
||||
assert_eq!(
|
||||
authdb.validate_session(session).unwrap(),
|
||||
Err(AuthenticationError::InvalidSession)
|
||||
);
|
||||
}
|
||||
with_memory_db(test_case);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_deletes_an_invitation() {
|
||||
fn test_case(mut authdb: Box<dyn AuthenticationDB>) {
|
||||
let username = Username::from("shephard");
|
||||
let userid = authdb
|
||||
.create_user(username.clone())
|
||||
.expect("no fatal errors")
|
||||
.expect("user to be created");
|
||||
let invitation = authdb
|
||||
.create_invitation(&userid)
|
||||
.expect("no fatal errors")
|
||||
.expect("invitation to be created");
|
||||
|
||||
authdb
|
||||
.delete_invitation(invitation.clone())
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
authdb.authenticate(invitation).unwrap(),
|
||||
Err(AuthenticationError::InvalidInvitation)
|
||||
);
|
||||
}
|
||||
with_memory_db(test_case);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
use common::types::{
|
||||
AuthenticateParams, AuthenticateResponse, Invitation, SessionToken, UserId, Username,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
net::{IpAddr, Ipv4Addr, SocketAddr},
|
||||
|
@ -6,16 +9,15 @@ use std::{
|
|||
use warp::Filter;
|
||||
|
||||
mod authentication;
|
||||
use authentication::{AuthenticationDB, AuthenticationError, MemoryAuth, UserId, Username};
|
||||
use authentication::{AuthenticationDB, MemoryAuth};
|
||||
|
||||
mod database;
|
||||
mod errors;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct AuthenticationRefused;
|
||||
impl warp::reject::Reject for AuthenticationRefused {}
|
||||
|
||||
fn with_authentication(
|
||||
fn with_session(
|
||||
auth_ctx: Arc<RwLock<impl AuthenticationDB>>,
|
||||
) -> impl Filter<Extract = ((Username, UserId),), Error = warp::Rejection> + Clone {
|
||||
let auth_ctx = auth_ctx.clone();
|
||||
|
@ -25,13 +27,11 @@ fn with_authentication(
|
|||
let auth_ctx = auth_ctx.clone();
|
||||
async move {
|
||||
if auth_header.starts_with("Basic ") {
|
||||
let username = auth_header.split(" ").skip(1).collect::<String>();
|
||||
match auth_ctx
|
||||
.read()
|
||||
.unwrap()
|
||||
.get_user_id(Username::from(username.as_str()))
|
||||
{
|
||||
Ok(Ok(userid)) => Ok((Username::from(username.as_str()), userid)),
|
||||
let session_token = SessionToken::from(
|
||||
auth_header.split(" ").skip(1).collect::<String>().as_str(),
|
||||
);
|
||||
match auth_ctx.read().unwrap().validate_session(session_token) {
|
||||
Ok(Ok((username, userid))) => Ok((username, userid)),
|
||||
Ok(Err(_)) => Err(warp::reject::custom(AuthenticationRefused)),
|
||||
Err(err) => panic!("{}", err),
|
||||
}
|
||||
|
@ -49,13 +49,13 @@ struct ErrorResponse {
|
|||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct MakeUserParameters {
|
||||
username: String,
|
||||
struct MakeUserParams {
|
||||
username: Username,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct MakeUserResponse {
|
||||
userid: String,
|
||||
userid: UserId,
|
||||
}
|
||||
|
||||
fn make_user(
|
||||
|
@ -64,13 +64,57 @@ fn make_user(
|
|||
warp::path!("api" / "v1" / "users")
|
||||
.and(warp::put())
|
||||
.and(warp::body::json())
|
||||
.map(move |params: MakeUserParameters| {
|
||||
.map(move |params: MakeUserParams| {
|
||||
let mut auth_ctx = auth_ctx.write().unwrap();
|
||||
match (*auth_ctx).create_user(Username::from(params.username.as_str())) {
|
||||
Ok(Ok(userid)) => warp::reply::json(&MakeUserResponse {
|
||||
userid: String::from(userid),
|
||||
match (*auth_ctx).create_user(Username::from(params.username)) {
|
||||
Ok(Ok(userid)) => warp::reply::json(&MakeUserResponse { userid }),
|
||||
Ok(Err(auth_error)) => warp::reply::json(&ErrorResponse {
|
||||
error: format!("{:?}", auth_error),
|
||||
}),
|
||||
Ok(auth_error) => warp::reply::json(&ErrorResponse {
|
||||
Err(err) => panic!("{}", err),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct MakeInvitationParams {
|
||||
userid: UserId,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct MakeInvitationResponse {
|
||||
invitation: Invitation,
|
||||
}
|
||||
|
||||
fn make_invitation(
|
||||
auth_ctx: Arc<RwLock<impl AuthenticationDB>>,
|
||||
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
|
||||
warp::path!("api" / "v1" / "invitations")
|
||||
.and(warp::put())
|
||||
.and(warp::body::json())
|
||||
.map(move |params: MakeInvitationParams| {
|
||||
let mut auth_ctx = auth_ctx.write().unwrap();
|
||||
match (*auth_ctx).create_invitation(¶ms.userid) {
|
||||
Ok(Ok(invitation)) => warp::reply::json(&MakeInvitationResponse { invitation }),
|
||||
Ok(Err(auth_error)) => warp::reply::json(&ErrorResponse {
|
||||
error: format!("{:?}", auth_error),
|
||||
}),
|
||||
Err(err) => panic!("{}", err),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn authenticate(
|
||||
auth_ctx: Arc<RwLock<impl AuthenticationDB>>,
|
||||
) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
|
||||
warp::path!("api" / "v1" / "authenticate")
|
||||
.and(warp::put())
|
||||
.and(warp::body::json())
|
||||
.map(move |params: AuthenticateParams| {
|
||||
let mut auth_ctx = auth_ctx.write().unwrap();
|
||||
match (*auth_ctx).authenticate(params.invitation) {
|
||||
Ok(Ok(session_token)) => warp::reply::json(&AuthenticateResponse { session_token }),
|
||||
Ok(Err(auth_error)) => warp::reply::json(&ErrorResponse {
|
||||
error: format!("{:?}", auth_error),
|
||||
}),
|
||||
Err(err) => panic!("{}", err),
|
||||
|
@ -89,7 +133,7 @@ fn list_users(
|
|||
Ok(Ok(users)) => warp::reply::json(
|
||||
&users
|
||||
.iter()
|
||||
.map(|u| String::from(u))
|
||||
.map(|u| String::from(u.clone()))
|
||||
.collect::<Vec<String>>(),
|
||||
),
|
||||
Ok(auth_error) => warp::reply::json(&ErrorResponse {
|
||||
|
@ -104,31 +148,10 @@ fn list_users(
|
|||
pub async fn main() {
|
||||
let auth_ctx: Arc<RwLock<MemoryAuth>> = Arc::new(RwLock::new(Default::default()));
|
||||
|
||||
let echo_unauthenticated = warp::path!("api" / "v1" / "echo" / String).map(|param: String| {
|
||||
println!("param: {}", param);
|
||||
warp::reply::json(&vec!["unauthenticated", param.as_str()])
|
||||
});
|
||||
|
||||
/*
|
||||
let authenticate = warp::path!("api" / "v1" / "auth" / String).map(|param: String| {
|
||||
println!("param: {}", param);
|
||||
warp::reply::json(¶m)
|
||||
});
|
||||
*/
|
||||
|
||||
let echo_authenticated = warp::path!("api" / "v1" / "echo" / String)
|
||||
.and(with_authentication(auth_ctx.clone()))
|
||||
.map(|param: String, (username, userid)| {
|
||||
println!("param: {:?}", username);
|
||||
println!("param: {:?}", userid);
|
||||
println!("param: {}", param);
|
||||
warp::reply::json(&vec!["authed", param.as_str()])
|
||||
});
|
||||
|
||||
let filter = list_users(auth_ctx.clone())
|
||||
.or(make_user(auth_ctx.clone()))
|
||||
.or(echo_authenticated)
|
||||
.or(echo_unauthenticated);
|
||||
let filter = make_user(auth_ctx.clone())
|
||||
.or(make_invitation(auth_ctx.clone()))
|
||||
.or(authenticate(auth_ctx.clone()))
|
||||
.or(list_users(auth_ctx.clone()));
|
||||
|
||||
let server = warp::serve(filter);
|
||||
server
|
||||
|
|
|
@ -14,6 +14,17 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||
dependencies = [
|
||||
"getrandom 0.2.8",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.19"
|
||||
|
@ -48,12 +59,26 @@ checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
|
|||
name = "app"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"common",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.58"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e805d94e6b5001b651426cf4cd446b1ab5f319d27bab5c644f61de0a804360c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atk"
|
||||
version = "0.15.1"
|
||||
|
@ -308,6 +333,16 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "common"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"rusqlite",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
|
@ -587,6 +622,18 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fallible-iterator"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-streaming-iterator"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.8.0"
|
||||
|
@ -711,6 +758,12 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.25"
|
||||
|
@ -1017,12 +1070,49 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"http",
|
||||
"indexmap",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashlink"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf"
|
||||
dependencies = [
|
||||
"hashbrown 0.11.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.3.3"
|
||||
|
@ -1072,12 +1162,72 @@ dependencies = [
|
|||
"itoa 1.0.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-range"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573"
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
|
||||
|
||||
[[package]]
|
||||
name = "httpdate"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa 1.0.4",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"hyper",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ico"
|
||||
version = "0.1.0"
|
||||
|
@ -1142,7 +1292,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
"hashbrown 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1172,6 +1322,12 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f88c5561171189e69df9d98bcf18fd5f9558300f7ea7b801eb8a0fd748bd8745"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.8"
|
||||
|
@ -1280,6 +1436,16 @@ dependencies = [
|
|||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.23.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2cafc7c74096c336d9d27145f7ebd4f4b6f95ba16aa5a282387267e6925cb58"
|
||||
dependencies = [
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "line-wrap"
|
||||
version = "0.1.1"
|
||||
|
@ -1395,6 +1561,12 @@ dependencies = [
|
|||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.5.4"
|
||||
|
@ -1413,6 +1585,18 @@ dependencies = [
|
|||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.11"
|
||||
|
@ -2146,6 +2330,43 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"hyper-tls",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rfd"
|
||||
version = "0.10.0"
|
||||
|
@ -2170,6 +2391,21 @@ dependencies = [
|
|||
"windows 0.37.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.26.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ba4d3462c8b2e4d7f4fcfcf2b296dc6b65404fbbc7b63daa37fd485c149daf7"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"fallible-iterator",
|
||||
"fallible-streaming-iterator",
|
||||
"hashlink",
|
||||
"libsqlite3-sys",
|
||||
"memchr",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.3.3"
|
||||
|
@ -2466,6 +2702,16 @@ version = "1.10.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "soup2"
|
||||
version = "0.2.1"
|
||||
|
@ -2954,9 +3200,37 @@ checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099"
|
|||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
"libc",
|
||||
"memchr",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-native-tls"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2968,6 +3242,12 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.37"
|
||||
|
@ -3039,6 +3319,12 @@ dependencies = [
|
|||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.15.0"
|
||||
|
@ -3152,6 +3438,16 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"try-lock",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
|
@ -3618,6 +3914,15 @@ version = "0.42.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winres"
|
||||
version = "0.1.12"
|
||||
|
|
|
@ -15,9 +15,12 @@ rust-version = "1.57"
|
|||
tauri-build = { version = "1.0.4", features = [] }
|
||||
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tauri = { version = "1.0.5", features = ["api-all"] }
|
||||
async-trait = { version = "0.1" }
|
||||
common = { path = "../../common" }
|
||||
reqwest = { version = "0.11", features = [ "json" ] }
|
||||
serde_json = { verison = "1.0" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tauri = { version = "1.0.5", features = ["api-all"] }
|
||||
|
||||
[features]
|
||||
# by default Tauri runs in production mode
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
use async_trait::async_trait;
|
||||
use common::{
|
||||
errors::{AppResult, FatalError},
|
||||
types::{
|
||||
AuthenticateParams, AuthenticateResponse, AuthenticationError, Invitation, SessionToken,
|
||||
UserId, Username,
|
||||
},
|
||||
};
|
||||
use reqwest::{self, Url};
|
||||
use serde_json::json;
|
||||
|
||||
#[async_trait]
|
||||
trait Client {
|
||||
async fn create_user(&self, username: Username) -> AppResult<UserId, AuthenticationError>;
|
||||
async fn create_invitation(&self, userid: UserId)
|
||||
-> AppResult<Invitation, AuthenticationError>;
|
||||
async fn delete_user(&self, userid: UserId) -> AppResult<(), AuthenticationError>;
|
||||
async fn delete_invitation(&self, invitation: Invitation)
|
||||
-> AppResult<(), AuthenticationError>;
|
||||
async fn delete_session(&self, session: SessionToken) -> AppResult<(), AuthenticationError>;
|
||||
async fn authenticate(
|
||||
&self,
|
||||
invitation: Invitation,
|
||||
) -> AppResult<SessionToken, AuthenticationError>;
|
||||
async fn list_users(&self) -> AppResult<Vec<UserId>, AuthenticationError>;
|
||||
}
|
||||
|
||||
struct VisionsClient {
|
||||
client: reqwest::Client,
|
||||
url: Url,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Client for VisionsClient {
|
||||
async fn create_user(&self, username: Username) -> AppResult<UserId, AuthenticationError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
async fn create_invitation(
|
||||
&self,
|
||||
userid: UserId,
|
||||
) -> AppResult<Invitation, AuthenticationError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
async fn delete_user(&self, userid: UserId) -> AppResult<(), AuthenticationError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
async fn delete_invitation(
|
||||
&self,
|
||||
invitation: Invitation,
|
||||
) -> AppResult<(), AuthenticationError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
async fn delete_session(&self, session: SessionToken) -> AppResult<(), AuthenticationError> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
async fn authenticate(
|
||||
&self,
|
||||
invitation: Invitation,
|
||||
) -> AppResult<SessionToken, AuthenticationError> {
|
||||
let mut url = self.url.clone();
|
||||
url.set_path("api/v1/invitations");
|
||||
match self
|
||||
.client
|
||||
.put(url)
|
||||
.json(&AuthenticateParams { invitation })
|
||||
.send()
|
||||
.await
|
||||
{
|
||||
Ok(resp) => {
|
||||
let js = resp.json().await;
|
||||
println!("authenticate response: {:?}", js);
|
||||
match js {
|
||||
Ok(response) => Ok(Ok(response)),
|
||||
Err(err) => panic!("could not decode response {:?}", err),
|
||||
}
|
||||
}
|
||||
Err(err) => panic!("http client error {:?}", err),
|
||||
}
|
||||
}
|
||||
|
||||
async fn list_users(&self) -> AppResult<Vec<UserId>, AuthenticationError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
|
@ -1,10 +1,12 @@
|
|||
#![cfg_attr(
|
||||
all(not(debug_assertions), target_os = "windows"),
|
||||
windows_subsystem = "windows"
|
||||
all(not(debug_assertions), target_os = "windows"),
|
||||
windows_subsystem = "windows"
|
||||
)]
|
||||
|
||||
mod client;
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
tauri::Builder::default()
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue