Aldonu aŭtentigo per pasvorto
Tio postulas kampojn por la pasvorto kaj la salo en la datumbazo.
This commit is contained in:
parent
823a701a2f
commit
f4164a62c4
|
@ -46,6 +46,15 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "buf_redux"
|
||||
version = "0.8.4"
|
||||
|
@ -92,6 +101,15 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d6b536309245c849479fba3da410962a43ed8e51c26b729208ec0ac2798d0"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.9.0"
|
||||
|
@ -101,6 +119,17 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b697d66081d42af4fba142d56918a3cb21dc8eb63372c6b85d14f44fb9c5979b"
|
||||
dependencies = [
|
||||
"block-buffer 0.10.0",
|
||||
"crypto-common",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fallible-iterator"
|
||||
version = "0.2.0"
|
||||
|
@ -403,7 +432,9 @@ name = "kampanja-kontrolada-servilo"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"rand 0.8.4",
|
||||
"rusqlite",
|
||||
"sha2",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
|
@ -833,13 +864,24 @@ version = "0.9.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99cd6713db3cf16b6c84e06321e049a9b9f699826e16096d23bbcc44d15d51a6"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"block-buffer 0.9.0",
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
"digest 0.9.0",
|
||||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.0"
|
||||
|
|
|
@ -7,7 +7,9 @@ edition = "2018"
|
|||
|
||||
[dependencies]
|
||||
anyhow = { version = "1" }
|
||||
rand = { version = "0.8" }
|
||||
rusqlite = { version = "0.26" }
|
||||
sha2 = { version = "0.10" }
|
||||
thiserror = { version = "1" }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
uuid = { version = "0.8", features = ["v4"] }
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
use crate::datumbazo::Datumbazo;
|
||||
use rusqlite::OptionalExtension;
|
||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||
use rusqlite::{
|
||||
params,
|
||||
types::{FromSql, FromSqlError, FromSqlResult, ToSqlOutput, ValueRef},
|
||||
OptionalExtension, ToSql,
|
||||
};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
convert::Infallible,
|
||||
|
@ -10,24 +16,24 @@ use uuid::{adapter::Hyphenated, Uuid};
|
|||
use warp::{reject, reject::Reject, Filter, Rejection};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Ĵetono(String);
|
||||
pub struct SesiaĴetono(String);
|
||||
|
||||
impl From<&str> for Ĵetono {
|
||||
impl From<&str> for SesiaĴetono {
|
||||
fn from(s: &str) -> Self {
|
||||
Ĵetono(s.to_owned())
|
||||
SesiaĴetono(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Ĵetono {
|
||||
impl FromStr for SesiaĴetono {
|
||||
type Err = Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Ĵetono(s.to_owned()))
|
||||
Ok(SesiaĴetono(s.to_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Ĵetono> for String {
|
||||
fn from(s: Ĵetono) -> Self {
|
||||
impl From<SesiaĴetono> for String {
|
||||
fn from(s: SesiaĴetono) -> Self {
|
||||
s.0.clone()
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +53,17 @@ impl From<UzantIdentigilo> for String {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromSql for UzantIdentigilo {
|
||||
fn column_result(val: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||
match val {
|
||||
ValueRef::Text(t) => Ok(UzantIdentigilo::from(
|
||||
String::from_utf8(Vec::from(t)).unwrap().as_ref(),
|
||||
)),
|
||||
_ => Err(FromSqlError::InvalidType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Uzantnomo(String);
|
||||
|
||||
|
@ -56,47 +73,177 @@ impl From<&str> for Uzantnomo {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&Uzantnomo> for String {
|
||||
fn from(s: &Uzantnomo) -> Self {
|
||||
s.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Uzantnomo> for String {
|
||||
fn from(s: Uzantnomo) -> Self {
|
||||
s.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AŭtentigoDB: Send {
|
||||
fn aŭtentigu(&self, ĵetono: Ĵetono) -> Option<(UzantIdentigilo, Uzantnomo)>;
|
||||
impl FromSql for Uzantnomo {
|
||||
fn column_result(val: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||
match val {
|
||||
ValueRef::Text(t) => Ok(Uzantnomo::from(
|
||||
String::from_utf8(Vec::from(t)).unwrap().as_ref(),
|
||||
)),
|
||||
_ => Err(FromSqlError::InvalidType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn kreu_ĵetono(&mut self, uzantnomo: Uzantnomo) -> Ĵetono;
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Pasvorto(String);
|
||||
|
||||
impl Pasvorto {
|
||||
fn haku(&self, salo: &Salo) -> HakitaPasvorto {
|
||||
let mut haketilo = Sha256::new();
|
||||
haketilo.update(String::from(self).as_bytes());
|
||||
haketilo.update(salo);
|
||||
HakitaPasvorto(Vec::from(haketilo.finalize().as_slice()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Pasvorto {
|
||||
fn from(s: &str) -> Self {
|
||||
Pasvorto(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Pasvorto> for String {
|
||||
fn from(s: &Pasvorto) -> Self {
|
||||
s.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Pasvorto> for String {
|
||||
fn from(s: Pasvorto) -> Self {
|
||||
s.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct HakitaPasvorto(Vec<u8>);
|
||||
|
||||
impl ToSql for HakitaPasvorto {
|
||||
fn to_sql(&self) -> Result<ToSqlOutput<'_>, rusqlite::Error> {
|
||||
Ok(ToSqlOutput::Borrowed(ValueRef::Blob(self.0.as_ref())))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql for HakitaPasvorto {
|
||||
fn column_result(val: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||
match val {
|
||||
ValueRef::Blob(t) => Ok(HakitaPasvorto(Vec::from(t))),
|
||||
_ => Err(FromSqlError::InvalidType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Salo(Vec<u8>);
|
||||
|
||||
impl AsRef<[u8]> for Salo {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToSql for Salo {
|
||||
fn to_sql(&self) -> Result<ToSqlOutput<'_>, rusqlite::Error> {
|
||||
Ok(ToSqlOutput::Borrowed(ValueRef::Blob(self.0.as_ref())))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromSql for Salo {
|
||||
fn column_result(val: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||
match val {
|
||||
ValueRef::Blob(t) => Ok(Salo(Vec::from(t))),
|
||||
_ => Err(FromSqlError::InvalidType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AŭtentigoDB: Send {
|
||||
fn kreu_uzanton(&mut self, uzantnomo: Uzantnomo, pasvorto: Pasvorto) -> UzantIdentigilo;
|
||||
|
||||
fn kreu_sesion(&mut self, uzantnomo: &Uzantnomo) -> SesiaĴetono;
|
||||
|
||||
fn ŝanĝu_pasvorton(&mut self, identigilo: &UzantIdentigilo, pasvorto: Pasvorto);
|
||||
|
||||
fn aŭtentigu_per_pasvorto(
|
||||
&self,
|
||||
uzantnomo: &Uzantnomo,
|
||||
pasvorto: &Pasvorto,
|
||||
) -> Option<UzantIdentigilo>;
|
||||
|
||||
fn aŭtentigu_per_sesio(&self, ĵetono: &SesiaĴetono) -> Option<(UzantIdentigilo, Uzantnomo)>;
|
||||
}
|
||||
|
||||
pub struct MemAŭtentigo {
|
||||
sesioj: HashMap<Ĵetono, UzantIdentigilo>,
|
||||
uzantoj: HashMap<UzantIdentigilo, Uzantnomo>,
|
||||
inversa_uzantoj: HashMap<Uzantnomo, UzantIdentigilo>,
|
||||
pasvortoj: HashMap<UzantIdentigilo, Pasvorto>,
|
||||
sesioj: HashMap<SesiaĴetono, UzantIdentigilo>,
|
||||
uzantoj: HashMap<UzantIdentigilo, Uzantnomo>,
|
||||
}
|
||||
|
||||
impl MemAŭtentigo {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inversa_uzantoj: HashMap::new(),
|
||||
pasvortoj: HashMap::new(),
|
||||
sesioj: HashMap::new(),
|
||||
uzantoj: HashMap::new(),
|
||||
inversa_uzantoj: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AŭtentigoDB for MemAŭtentigo {
|
||||
fn aŭtentigu(&self, ĵetono: Ĵetono) -> Option<(UzantIdentigilo, Uzantnomo)> {
|
||||
fn kreu_uzanton(&mut self, uzantnomo: Uzantnomo, pasvorto: Pasvorto) -> UzantIdentigilo {
|
||||
let uzant_id =
|
||||
UzantIdentigilo::from(format!("{}", Hyphenated::from_uuid(Uuid::new_v4())).as_str());
|
||||
self.uzantoj.insert(uzant_id.clone(), uzantnomo.clone());
|
||||
self.pasvortoj.insert(uzant_id.clone(), pasvorto);
|
||||
self.inversa_uzantoj.insert(uzantnomo, uzant_id.clone());
|
||||
uzant_id
|
||||
}
|
||||
|
||||
fn kreu_sesion(&mut self, uzantnomo: &Uzantnomo) -> SesiaĴetono {
|
||||
let identigilo = self.inversa_uzantoj.get(&uzantnomo).cloned().unwrap();
|
||||
let ĵetono =
|
||||
SesiaĴetono::from(format!("{}", Hyphenated::from_uuid(Uuid::new_v4())).as_str());
|
||||
self.sesioj.insert(ĵetono.clone(), identigilo);
|
||||
ĵetono
|
||||
}
|
||||
|
||||
fn ŝanĝu_pasvorton(&mut self, identigilo: &UzantIdentigilo, pasvorto: Pasvorto) {
|
||||
self.pasvortoj.insert(identigilo.clone(), pasvorto);
|
||||
}
|
||||
|
||||
fn aŭtentigu_per_pasvorto(
|
||||
&self,
|
||||
uzantnomo: &Uzantnomo,
|
||||
pasvorto: &Pasvorto,
|
||||
) -> Option<UzantIdentigilo> {
|
||||
self.inversa_uzantoj.get(&uzantnomo).and_then(|id| {
|
||||
self.pasvortoj.get(id).and_then(|kandidato| {
|
||||
if *pasvorto == *kandidato {
|
||||
Some(id.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn aŭtentigu_per_sesio(&self, ĵetono: &SesiaĴetono) -> Option<(UzantIdentigilo, Uzantnomo)> {
|
||||
let identigilo = self.sesioj.get(&ĵetono).cloned()?;
|
||||
let uzantnomo = self.uzantoj.get(&identigilo).cloned()?;
|
||||
Some((identigilo, uzantnomo))
|
||||
}
|
||||
|
||||
fn kreu_ĵetono(&mut self, uzantnomo: Uzantnomo) -> Ĵetono {
|
||||
let identigilo = self.inversa_uzantoj.get(&uzantnomo).cloned().unwrap();
|
||||
let ĵetono = Ĵetono::from(format!("{}", Hyphenated::from_uuid(Uuid::new_v4())).as_str());
|
||||
self.sesioj.insert(ĵetono.clone(), identigilo);
|
||||
ĵetono
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DBAŭtentigo {
|
||||
|
@ -110,26 +257,50 @@ impl DBAŭtentigo {
|
|||
}
|
||||
|
||||
impl AŭtentigoDB for DBAŭtentigo {
|
||||
fn aŭtentigu(&self, ĵetono: Ĵetono) -> Option<(UzantIdentigilo, Uzantnomo)> {
|
||||
let konekto = self.pool.konektu().unwrap();
|
||||
konekto
|
||||
fn kreu_uzanton(&mut self, uzantnomo: Uzantnomo, pasvorto: Pasvorto) -> UzantIdentigilo {
|
||||
let mut konekto = self.pool.konektu().unwrap();
|
||||
let tr = konekto.transaction().unwrap();
|
||||
|
||||
let identigilo =
|
||||
UzantIdentigilo::from(format!("{}", Hyphenated::from_uuid(Uuid::new_v4())).as_str());
|
||||
|
||||
let cnt: usize = tr
|
||||
.query_row(
|
||||
"SELECT uzantoj.id, uzantoj.nomo FROM sesioj INNER JOIN uzantoj on sesioj.uzanto == uzantoj.id WHERE sesioj.id = ?",
|
||||
[String::from(ĵetono)],
|
||||
|row| {
|
||||
let identigilo = row.get("id")
|
||||
.map(|s: String| UzantIdentigilo::from(s.as_str())).unwrap();
|
||||
let nomo = row.get("nomo")
|
||||
.map(|s: String| Uzantnomo::from(s.as_str())).unwrap();
|
||||
Ok((identigilo, nomo))
|
||||
},
|
||||
"SELECT count(*) from uzantoj WHERE nomo = ?",
|
||||
params![String::from(uzantnomo.clone())],
|
||||
|row| row.get("count(*)"),
|
||||
)
|
||||
.optional()
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
if cnt > 0 {
|
||||
panic!("uzanto jam ekzistas");
|
||||
} else {
|
||||
let salo = Salo(
|
||||
thread_rng()
|
||||
.sample_iter(&Alphanumeric)
|
||||
.take(10)
|
||||
.collect::<Vec<u8>>(),
|
||||
);
|
||||
let pasvorto = pasvorto.haku(&salo);
|
||||
tr.execute(
|
||||
"INSERT INTO uzantoj VALUES(?, ?, ?, ?)",
|
||||
params![
|
||||
String::from(identigilo.clone()),
|
||||
String::from(uzantnomo),
|
||||
pasvorto,
|
||||
salo
|
||||
],
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
tr.commit().unwrap();
|
||||
|
||||
identigilo
|
||||
}
|
||||
|
||||
fn kreu_ĵetono(&mut self, uzantnomo: Uzantnomo) -> Ĵetono {
|
||||
let ĵetono = Ĵetono::from(format!("{}", Hyphenated::from_uuid(Uuid::new_v4())).as_str());
|
||||
fn kreu_sesion(&mut self, uzantnomo: &Uzantnomo) -> SesiaĴetono {
|
||||
let ĵetono =
|
||||
SesiaĴetono::from(format!("{}", Hyphenated::from_uuid(Uuid::new_v4())).as_str());
|
||||
let mut konekto = self.pool.konektu().unwrap();
|
||||
let tr = konekto.transaction().unwrap();
|
||||
let uzanta_id: Option<String> = tr
|
||||
|
@ -153,6 +324,68 @@ impl AŭtentigoDB for DBAŭtentigo {
|
|||
tr.commit().unwrap();
|
||||
ĵetono
|
||||
}
|
||||
|
||||
fn ŝanĝu_pasvorton(&mut self, identigilo: &UzantIdentigilo, pasvorto: Pasvorto) {}
|
||||
|
||||
fn aŭtentigu_per_pasvorto(
|
||||
&self,
|
||||
uzantnomo: &Uzantnomo,
|
||||
pasvorto: &Pasvorto,
|
||||
) -> Option<UzantIdentigilo> {
|
||||
let konekto = self.pool.konektu().unwrap();
|
||||
let mut demando = konekto
|
||||
.prepare_cached("SELECT * FROM uzantoj WHERE nomo = ?")
|
||||
.unwrap();
|
||||
let rezultoj = demando.query_map(params![String::from(uzantnomo)], |row| {
|
||||
let id = row.get("id")?;
|
||||
let nomo = row.get("nomo")?;
|
||||
let pasvorto = row.get("pasvorto")?;
|
||||
let salo = row.get("pasvortsalo")?;
|
||||
Ok((id, nomo, pasvorto, salo))
|
||||
});
|
||||
|
||||
let rows: Vec<(UzantIdentigilo, Uzantnomo, HakitaPasvorto, Salo)> = match rezultoj {
|
||||
Ok(r) => {
|
||||
let mut rows = Vec::new();
|
||||
for row in r {
|
||||
let (id, nomo, pasvorto, salo) = row.unwrap();
|
||||
rows.push((id, nomo, pasvorto, salo));
|
||||
}
|
||||
rows
|
||||
}
|
||||
Err(_) => panic!("eraro en datumbazo"),
|
||||
};
|
||||
|
||||
match rows.len() {
|
||||
1 => {
|
||||
let (ref identigilo, _, ref hakita_pasvorto, ref salo) = rows[0];
|
||||
if pasvorto.haku(&salo) == *hakita_pasvorto {
|
||||
Some(identigilo.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
0 => None,
|
||||
_ => panic!("pli ol unu kongruo trovis por uzantnomo"),
|
||||
}
|
||||
}
|
||||
|
||||
fn aŭtentigu_per_sesio(&self, ĵetono: &SesiaĴetono) -> Option<(UzantIdentigilo, Uzantnomo)> {
|
||||
let konekto = self.pool.konektu().unwrap();
|
||||
konekto.query_row(
|
||||
"SELECT uzantoj.id, uzantoj.nomo FROM sesioj INNER JOIN uzantoj on sesioj.uzanto == uzantoj.id WHERE sesioj.id = ?",
|
||||
params![String::from(ĵetono.clone())],
|
||||
|row| {
|
||||
let identigilo = row.get("id")
|
||||
.map(|s: String| UzantIdentigilo::from(s.as_str())).unwrap();
|
||||
let nomo = row.get("nomo")
|
||||
.map(|s: String| Uzantnomo::from(s.as_str())).unwrap();
|
||||
Ok((identigilo, nomo))
|
||||
},
|
||||
)
|
||||
.optional()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -166,10 +399,10 @@ pub fn kun_aŭtentigo(
|
|||
let auth_ctx = auth_ctx.clone();
|
||||
warp::header("authentication").and_then({
|
||||
let auth_ctx = auth_ctx.clone();
|
||||
move |text| {
|
||||
move |text: SesiaĴetono| {
|
||||
let auth_ctx = auth_ctx.clone();
|
||||
async move {
|
||||
match auth_ctx.read().unwrap().aŭtentigu(text) {
|
||||
match auth_ctx.read().unwrap().aŭtentigu_per_sesio(&text) {
|
||||
Some(salutiloj) => Ok(salutiloj),
|
||||
None => Err(reject::custom(AŭtentigoPostulas)),
|
||||
}
|
||||
|
@ -177,3 +410,22 @@ pub fn kun_aŭtentigo(
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use tempfile::NamedTempFile;
|
||||
|
||||
#[test]
|
||||
fn ĝi_povas_krei_uzanto() {
|
||||
let vojo = NamedTempFile::new().unwrap().into_temp_path();
|
||||
let datumbazo = Datumbazo::kreu(vojo.to_path_buf()).unwrap();
|
||||
let mut aŭtentigo = DBAŭtentigo::kreu(datumbazo);
|
||||
let identigilo =
|
||||
aŭtentigo.kreu_uzanton(Uzantnomo::from("savanni"), Pasvorto::from("abcdefg"));
|
||||
|
||||
let aŭtentita_identigilo = aŭtentigo
|
||||
.aŭtentigu_per_pasvorto(&Uzantnomo::from("savanni"), &Pasvorto::from("abcdefg"));
|
||||
assert_eq!(aŭtentita_identigilo, Some(identigilo));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ impl Datumbazo {
|
|||
println!("versio: {}", versio);
|
||||
if versio == 0 {
|
||||
tx.execute_batch(
|
||||
"CREATE TABLE uzantoj (id string primary key, nomo text);
|
||||
"CREATE TABLE uzantoj (id string primary key, nomo text, pasvorto text, pasvortsalo text);
|
||||
CREATE TABLE rajtoj (uzanto string, rajto string, foreign key(uzanto) references uzanto(id));
|
||||
CREATE TABLE sesioj (id string primary key not null, uzanto string, foreign key(uzanto) references uzantoj(id));
|
||||
PRAGMA user_version = 1;",
|
||||
|
@ -97,20 +97,20 @@ mod test {
|
|||
let mut konekto = datumbazo.konektu().unwrap();
|
||||
let tr = konekto.transaction().unwrap();
|
||||
tr.execute(
|
||||
"INSERT INTO uzantoj VALUES(?, ?)",
|
||||
params![1, String::from("mia-uzantnomo")],
|
||||
"INSERT INTO uzantoj VALUES(?, ?, ?, ?)",
|
||||
params!["abcdfeg", "mia-uzantnomo", "pasvorto", "abcdefg"],
|
||||
)
|
||||
.unwrap();
|
||||
tr.commit().unwrap();
|
||||
|
||||
let konekto = datumbazo.konektu().unwrap();
|
||||
let id: Option<u32> = konekto
|
||||
let id: Option<String> = konekto
|
||||
.query_row(
|
||||
"SELECT id FROM uzantoj WHERE nomo = ?",
|
||||
[String::from("mia-uzantnomo")],
|
||||
|row| row.get("id"),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(id, Some(1));
|
||||
assert_eq!(id, Some(String::from("abcdfeg")));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ pub async fn main() {
|
|||
let ĵetono = auth_ctx
|
||||
.write()
|
||||
.unwrap()
|
||||
.kreu_ĵetono(Uzantnomo::from("savanni"));
|
||||
.kreu_sesion(&Uzantnomo::from("savanni"));
|
||||
println!("ĵetono: {}", String::from(ĵetono));
|
||||
let rajtigo = Arc::new(RwLock::new(DBRajtigo::kreu(db)));
|
||||
|
||||
|
|
Loading…
Reference in New Issue