use super::verify::{verify_creds_legacy, verify_db_creds, verify_ns_creds, verify_root_creds};
use super::{Actor, Level};
use crate::cnf::{INSECURE_FORWARD_SCOPE_ERRORS, SERVER_NAME};
use crate::dbs::Session;
use crate::err::Error;
use crate::iam::token::{Claims, HEADER};
use crate::iam::Auth;
use crate::kvs::{Datastore, LockType::*, TransactionType::*};
use crate::Object;
use crate::Value;
use chrono::{Duration, Utc};
use jsonwebtoken::{encode, EncodingKey};
use std::sync::Arc;
pub async fn signin(
kvs: &Datastore,
session: &mut Session,
vars: Object,
) -> Result<Option<String>, Error> {
let ns = vars.get("NS").or_else(|| vars.get("ns"));
let db = vars.get("DB").or_else(|| vars.get("db"));
let sc = vars.get("SC").or_else(|| vars.get("sc"));
match (ns, db, sc) {
(Some(ns), Some(db), Some(sc)) => {
let ns = ns.to_raw_string();
let db = db.to_raw_string();
let sc = sc.to_raw_string();
super::signin::sc(kvs, session, ns, db, sc, vars).await
}
(Some(ns), Some(db), None) => {
let user = vars.get("user");
let pass = vars.get("pass");
match (user, pass) {
(Some(user), Some(pass)) => {
let ns = ns.to_raw_string();
let db = db.to_raw_string();
let user = user.to_raw_string();
let pass = pass.to_raw_string();
super::signin::db(kvs, session, ns, db, user, pass).await
}
_ => Err(Error::MissingUserOrPass),
}
}
(Some(ns), None, None) => {
let user = vars.get("user");
let pass = vars.get("pass");
match (user, pass) {
(Some(user), Some(pass)) => {
let ns = ns.to_raw_string();
let user = user.to_raw_string();
let pass = pass.to_raw_string();
super::signin::ns(kvs, session, ns, user, pass).await
}
_ => Err(Error::MissingUserOrPass),
}
}
(None, None, None) => {
let user = vars.get("user");
let pass = vars.get("pass");
match (user, pass) {
(Some(user), Some(pass)) => {
let user = user.to_raw_string();
let pass = pass.to_raw_string();
super::signin::root(kvs, session, user, pass).await
}
_ => Err(Error::MissingUserOrPass),
}
}
_ => Err(Error::NoSigninTarget),
}
}
pub async fn sc(
kvs: &Datastore,
session: &mut Session,
ns: String,
db: String,
sc: String,
vars: Object,
) -> Result<Option<String>, Error> {
let mut tx = kvs.transaction(Read, Optimistic).await?;
let scope = tx.get_sc(&ns, &db, &sc).await;
tx.cancel().await?;
match scope {
Ok(sv) => {
match sv.signin {
Some(val) => {
let vars = Some(vars.0);
let mut sess = Session::editor().with_ns(&ns).with_db(&db);
sess.ip = session.ip.clone();
sess.or = session.or.clone();
match kvs.evaluate(val, &sess, vars).await {
Ok(val) => match val.record() {
Some(rid) => {
let key = EncodingKey::from_secret(sv.code.as_ref());
let val = Claims {
iss: Some(SERVER_NAME.to_owned()),
iat: Some(Utc::now().timestamp()),
nbf: Some(Utc::now().timestamp()),
exp: Some(
match sv.session {
Some(v) => {
Utc::now() + Duration::from_std(v.0).unwrap()
}
_ => Utc::now() + Duration::hours(1),
}
.timestamp(),
),
ns: Some(ns.to_owned()),
db: Some(db.to_owned()),
sc: Some(sc.to_owned()),
id: Some(rid.to_raw()),
..Claims::default()
};
trace!("Signing in to scope `{}`", sc);
let enc = encode(&HEADER, &val, &key);
session.tk = Some(val.into());
session.ns = Some(ns.to_owned());
session.db = Some(db.to_owned());
session.sc = Some(sc.to_owned());
session.sd = Some(Value::from(rid.to_owned()));
session.au = Arc::new(Auth::new(Actor::new(
rid.to_string(),
Default::default(),
Level::Scope(ns, db, sc),
)));
match enc {
Ok(tk) => Ok(Some(tk)),
_ => Err(Error::TokenMakingFailed),
}
}
_ => Err(Error::NoRecordFound),
},
Err(e) => match e {
Error::Thrown(_) => Err(e),
e if *INSECURE_FORWARD_SCOPE_ERRORS => Err(e),
_ => Err(Error::SigninQueryFailed),
},
}
}
_ => Err(Error::ScopeNoSignin),
}
}
_ => Err(Error::NoScopeFound),
}
}
pub async fn db(
kvs: &Datastore,
session: &mut Session,
ns: String,
db: String,
user: String,
pass: String,
) -> Result<Option<String>, Error> {
let verify_creds = if kvs.is_auth_level_enabled() {
verify_db_creds(kvs, &ns, &db, &user, &pass).await
} else {
match verify_creds_legacy(kvs, Some(&ns), Some(&db), &user, &pass).await {
Ok((_, u)) => Ok(u),
Err(e) => Err(e),
}
};
match verify_creds {
Ok(u) => {
let key = EncodingKey::from_secret(u.code.as_ref());
let val = Claims {
iss: Some(SERVER_NAME.to_owned()),
iat: Some(Utc::now().timestamp()),
nbf: Some(Utc::now().timestamp()),
exp: Some((Utc::now() + Duration::hours(1)).timestamp()),
ns: Some(ns.to_owned()),
db: Some(db.to_owned()),
id: Some(user),
..Claims::default()
};
trace!("Signing in to database `{}`", db);
let enc = encode(&HEADER, &val, &key);
session.tk = Some(val.into());
session.ns = Some(ns.to_owned());
session.db = Some(db.to_owned());
session.au = Arc::new((&u, Level::Database(ns.to_owned(), db.to_owned())).into());
match enc {
Ok(tk) => Ok(Some(tk)),
_ => Err(Error::TokenMakingFailed),
}
}
_ => Err(Error::InvalidAuth),
}
}
pub async fn ns(
kvs: &Datastore,
session: &mut Session,
ns: String,
user: String,
pass: String,
) -> Result<Option<String>, Error> {
let verify_creds = if kvs.is_auth_level_enabled() {
verify_ns_creds(kvs, &ns, &user, &pass).await
} else {
match verify_creds_legacy(kvs, Some(&ns), None, &user, &pass).await {
Ok((_, u)) => Ok(u),
Err(e) => Err(e),
}
};
match verify_creds {
Ok(u) => {
let key = EncodingKey::from_secret(u.code.as_ref());
let val = Claims {
iss: Some(SERVER_NAME.to_owned()),
iat: Some(Utc::now().timestamp()),
nbf: Some(Utc::now().timestamp()),
exp: Some((Utc::now() + Duration::hours(1)).timestamp()),
ns: Some(ns.to_owned()),
id: Some(user),
..Claims::default()
};
trace!("Signing in to namespace `{}`", ns);
let enc = encode(&HEADER, &val, &key);
session.tk = Some(val.into());
session.ns = Some(ns.to_owned());
session.au = Arc::new((&u, Level::Namespace(ns.to_owned())).into());
match enc {
Ok(tk) => Ok(Some(tk)),
_ => Err(Error::TokenMakingFailed),
}
}
_ => Err(Error::InvalidAuth),
}
}
pub async fn root(
kvs: &Datastore,
session: &mut Session,
user: String,
pass: String,
) -> Result<Option<String>, Error> {
let verify_creds = if kvs.is_auth_level_enabled() {
verify_root_creds(kvs, &user, &pass).await
} else {
match verify_creds_legacy(kvs, None, None, &user, &pass).await {
Ok((_, u)) => Ok(u),
Err(e) => Err(e),
}
};
match verify_creds {
Ok(u) => {
let key = EncodingKey::from_secret(u.code.as_ref());
let val = Claims {
iss: Some(SERVER_NAME.to_owned()),
iat: Some(Utc::now().timestamp()),
nbf: Some(Utc::now().timestamp()),
exp: Some((Utc::now() + Duration::hours(1)).timestamp()),
id: Some(user),
..Claims::default()
};
trace!("Signing in as root");
let enc = encode(&HEADER, &val, &key);
session.tk = Some(val.into());
session.au = Arc::new((&u, Level::Root).into());
match enc {
Ok(tk) => Ok(Some(tk)),
_ => Err(Error::TokenMakingFailed),
}
}
_ => Err(Error::InvalidAuth),
}
}