Compare commits
14 Commits
e3229f41a4
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 9fa66899aa | |||
| 8a48ca0ad6 | |||
| 26301ac344 | |||
| 81023f2042 | |||
| f5f35855ce | |||
| 7018adf98d | |||
| a60713a4b0 | |||
| 9ef4176091 | |||
| 6cd6b12630 | |||
| 2a9ac271a7 | |||
| 8a7ff047b6 | |||
| e28a8c2893 | |||
| 73f5edf77f | |||
| c12d7fb01c |
835
Cargo.lock
generated
835
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "BitAuth"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@ -16,10 +16,11 @@ jsonwebtoken = "9.2.0"
|
||||
rand = "0.8.5"
|
||||
rsa = "0.9.6"
|
||||
serde_json = "1.0.111"
|
||||
skytable = "0.8.6"
|
||||
skytable = "0.8.10"
|
||||
tokio = { version = "1.35.1", features = ["full"] }
|
||||
urlencoding = "2.1.3"
|
||||
uuid = { version = "1.6.1", features = ["v4", "v5"] }
|
||||
webhook = "2.1.2"
|
||||
|
||||
|
||||
[[bin]]
|
||||
|
||||
28
TODO
28
TODO
@ -1,6 +1,4 @@
|
||||
############################
|
||||
### MAKE USER UUID TABLE ###
|
||||
############################
|
||||
Forward user back when set `back_url`
|
||||
|
||||
|
||||
Auth using tokens
|
||||
@ -11,8 +9,26 @@ Auth using email
|
||||
|
||||
|
||||
|
||||
##### API v0
|
||||
##### API v0 (unsafe)
|
||||
# Auth link (device check url with this session and get data)
|
||||
/auth?session=<UNIQUE>
|
||||
/api/v0/auth?session=<UNIQUE>&back_url=<OPTIONAL>
|
||||
# Get auth data
|
||||
/auth_finish?session=<UNIQUE>
|
||||
/api/v0/auth_finish?session=<UNIQUE>
|
||||
|
||||
|
||||
##### API v1 (semi-safe)
|
||||
# Auth link
|
||||
/api/v1/auth?session=<UNIQUE>&back_url=<OPTIONAL>
|
||||
# Get token
|
||||
/api/v1/auth_finish?session=<UNIQUE>
|
||||
# Refresh token
|
||||
/api/v1/refresh?token=<REFRESH>
|
||||
|
||||
|
||||
##### API v2 (safe)
|
||||
# Auth link
|
||||
/api/v2/auth?site_id=<REQUIRED>&session=<UNIQUE>&back_url=<OPTIONAL>&webhook=<OPTIONAL>
|
||||
# Get token
|
||||
/api/v2/auth_finish?site_id=<REQUIRED>&session=<UNIQUE>
|
||||
# Refresh token
|
||||
/api/v2/refresh?site_id=<REQUIRED>&token=<REFRESH>
|
||||
|
||||
362
src/funcs.rs
362
src/funcs.rs
@ -7,10 +7,57 @@ use {
|
||||
UNIX_EPOCH,
|
||||
},
|
||||
},
|
||||
webhook::client::WebhookClient,
|
||||
urlencoding::decode as url_decode,
|
||||
uuid::Uuid,
|
||||
chrono::DateTime,
|
||||
serde_json::{
|
||||
Value as Json,
|
||||
Map as JsonMap,
|
||||
json,
|
||||
},
|
||||
rsa::{
|
||||
pkcs1::{
|
||||
EncodeRsaPublicKey,
|
||||
EncodeRsaPrivateKey,
|
||||
},
|
||||
RsaPrivateKey,
|
||||
RsaPublicKey,
|
||||
},
|
||||
jsonwebtoken as jwt,
|
||||
jwt::{
|
||||
Header,
|
||||
Algorithm,
|
||||
TokenData,
|
||||
Validation,
|
||||
EncodingKey,
|
||||
DecodingKey,
|
||||
encode as jwt_encode,
|
||||
decode as jwt_decode,
|
||||
},
|
||||
skytable::{
|
||||
query,
|
||||
error::Error as SkyError,
|
||||
},
|
||||
http_body_util::BodyExt,
|
||||
hyper::{
|
||||
Request,
|
||||
HeaderMap,
|
||||
body::Incoming,
|
||||
header::{
|
||||
HeaderValue,
|
||||
},
|
||||
},
|
||||
crate::{
|
||||
Res,
|
||||
Result,
|
||||
DBPool,
|
||||
REFRESH_LIFETIME,
|
||||
TOKEN_LIFETIME,
|
||||
html::*,
|
||||
Users,
|
||||
Sites,
|
||||
APIV0_LIFETIME,
|
||||
},
|
||||
};
|
||||
|
||||
@ -28,6 +75,64 @@ pub fn build_html(body: &str) -> String {
|
||||
format!("{}{}{}{}", HEADER_HTML, CSS3, body, FOOTER_HTML)
|
||||
}
|
||||
|
||||
pub async fn init_tables(pool: DBPool) -> Res<(), SkyError> {
|
||||
let mut con = pool.get().await.unwrap();
|
||||
|
||||
let _ = con.query_parse::<()>(&query!("CREATE SPACE IF NOT EXISTS bitauth")).await;
|
||||
let _ = con.query_parse::<()>(&query!(r#"
|
||||
CREATE MODEL IF NOT EXISTS bitauth.users(
|
||||
uuid: string,
|
||||
login: string,
|
||||
password: string,
|
||||
email: string,
|
||||
tokens: list {type: string}
|
||||
)
|
||||
"#)).await;
|
||||
let _ = con.query_parse::<()>(&query!(r#"
|
||||
CREATE MODEL IF NOT EXISTS bitauth.users_uuid(
|
||||
login: string,
|
||||
uuid: string
|
||||
)
|
||||
"#)).await;
|
||||
let _ = con.query_parse::<()>(&query!(r#"
|
||||
CREATE MODEL IF NOT EXISTS bitauth.sites(
|
||||
uuid: string,
|
||||
uid: string,
|
||||
domain: string,
|
||||
pkey: binary,
|
||||
skey: binary
|
||||
)
|
||||
"#)).await;
|
||||
let _ = con.query_parse::<()>(&query!(r#"
|
||||
CREATE MODEL IF NOT EXISTS bitauth.tokens(
|
||||
uuid: string,
|
||||
uid: string,
|
||||
sid: string,
|
||||
ref: string,
|
||||
refend: uint32
|
||||
)
|
||||
"#)).await;
|
||||
let _ = con.query_parse::<()>(&query!(r#"
|
||||
CREATE MODEL IF NOT EXISTS bitauth.v0(
|
||||
session: string,
|
||||
login: string,
|
||||
uuid: string,
|
||||
expire: uint32
|
||||
)
|
||||
"#)).await;
|
||||
|
||||
let q = con.query_parse::<Sites>(&query!("SELECT * FROM bitauth.sites WHERE uuid = 0")).await;
|
||||
if q.is_err() {
|
||||
let (skey, pkey) = rsa_gen();
|
||||
let _ = con.query_parse::<()>(&query!(
|
||||
"INSERT INTO bitauth.sites { uuid: ?, uid: ?, domain: ?, pkey: ?, skey: ? }",
|
||||
"0", "0", "", pkey, skey
|
||||
)).await;
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn double_split(body: String, first: &str, second: &str) -> HashMap<String, String> {
|
||||
body.split(first)
|
||||
.filter_map(|c| {
|
||||
@ -53,3 +158,260 @@ pub fn time_mcs() -> u128 {
|
||||
.unwrap()
|
||||
.as_micros()
|
||||
}
|
||||
|
||||
pub async fn discord_wh_send(text: String) {
|
||||
let url: &str = "https://discord.com/api/webhooks/1228566682902204537/RXaWpZplEGzU88O8c4mD4qzXp1PDBrrp93nGvdijaY7mBXp27xc0EsThHUeU0431PQOZ";
|
||||
let client: WebhookClient = WebhookClient::new(url);
|
||||
client.send(|message| message
|
||||
.username("Bit.Auth")
|
||||
// .avatar_url("")
|
||||
.embed(|embed| embed
|
||||
.title("Error")
|
||||
.description(&text)
|
||||
// .footer("Footer", Some(String::from(IMAGE_URL)))
|
||||
// .image(IMAGE_URL)
|
||||
// .thumbnail(IMAGE_URL)
|
||||
// .author("bitheaven", Some(String::from(IMAGE_URL)), Some(String::from(IMAGE_URL)))
|
||||
// .field("name", "value", false)
|
||||
)
|
||||
).await.unwrap();
|
||||
}
|
||||
|
||||
pub fn set_cookie(headers: &mut HeaderMap, key: &str, value: &str) {
|
||||
let time = DateTime::from_timestamp((time() + REFRESH_LIFETIME) as i64, 0)
|
||||
.expect("REASON")
|
||||
.to_rfc2822();
|
||||
let time = time.replace("+0000", "GMT");
|
||||
headers.append(
|
||||
hyper::header::SET_COOKIE,
|
||||
HeaderValue::from_str(format!("{}={}; HttpOnly; Expires={}", key, value, time).as_str())
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
pub fn set_location(headers: &mut HeaderMap, location: &str) {
|
||||
headers.append(
|
||||
hyper::header::LOCATION,
|
||||
HeaderValue::from_str(format!("{}", location).as_str()).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
pub async fn get_body_from_request(mut req: Request<Incoming>) -> Result<Vec<u8>> {
|
||||
let mut body: Vec<u8> = vec![];
|
||||
while let Some(next) = req.frame().await {
|
||||
let frame = next?;
|
||||
if let Some(chunk) = frame.data_ref() {
|
||||
body.extend_from_slice(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
pub async fn login_user(pool: DBPool, data: HashMap<String, String>) -> Res<(String, String), SkyError> {
|
||||
let mut ret: (String, String) = Default::default();
|
||||
let mut con = pool.get().await.unwrap();
|
||||
|
||||
let login = &data.get("login").unwrap().trim().to_lowercase();
|
||||
let pass = data.get("password").unwrap().trim();
|
||||
|
||||
let q = con
|
||||
.query_parse::<(String,)>(&query!("SELECT uuid FROM bitauth.users_uuid WHERE login = ?", login))
|
||||
.await;
|
||||
if q.is_err() { println!("{:?}", q.err()); return Ok(ret); };
|
||||
let (uuid,) = q.unwrap();
|
||||
|
||||
let q = con
|
||||
.query_parse::<Users>(&query!("SELECT * FROM bitauth.users WHERE uuid = ?", uuid.clone()))
|
||||
.await;
|
||||
if q.is_err() {
|
||||
discord_wh_send(
|
||||
format!("Пизда тут, ну тип, да, какая-то ебень с uuid, чел потерялся...\n`{}`\n**{}**", uuid, login)
|
||||
).await;
|
||||
return Ok(ret);
|
||||
}
|
||||
|
||||
// TODO: Say to user about trouble.
|
||||
|
||||
let q = q.unwrap();
|
||||
if bcrypt::verify(pass, q.password.as_str()).unwrap() {
|
||||
let token = format!("{}", uuid_v4().as_hyphenated());
|
||||
let reftoken = format!("{}", uuid_v4().as_hyphenated());
|
||||
let time = time();
|
||||
let uuid: String = q.uuid;
|
||||
let login: String = q.login;
|
||||
|
||||
let _ = con.query_parse::<()>(&query!(
|
||||
"INSERT INTO bitauth.tokens { uuid: ?, uid: ?, sid: ?, ref: ?, refend: ? }",
|
||||
token.clone(), uuid.clone(), "0", reftoken.clone(), time + REFRESH_LIFETIME
|
||||
)).await;
|
||||
let _ = con.query_parse::<()>(&query!(
|
||||
"UPDATE bitauth.users SET tokens += ? WHERE login = ?",
|
||||
token.clone(), login.clone()
|
||||
)).await;
|
||||
|
||||
ret = (
|
||||
jwt_sign(pool.clone(), json!({
|
||||
"login": login.clone(),
|
||||
"uuid": uuid.clone(),
|
||||
"iat": time,
|
||||
"exp": time + TOKEN_LIFETIME
|
||||
})).await.unwrap(),
|
||||
jwt_sign(pool.clone(), json!({
|
||||
"uuid": token.clone(),
|
||||
"iat": time,
|
||||
"ref": reftoken.clone(),
|
||||
"exp": time + REFRESH_LIFETIME
|
||||
})).await.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub async fn jwt_sign(pool: DBPool, data: Json) -> Result<String> {
|
||||
let mut con = pool.get().await.unwrap();
|
||||
|
||||
let q = con
|
||||
.query_parse::<Sites>(&query!("SELECT * FROM bitauth.sites WHERE uuid = ?", "0"))
|
||||
.await;
|
||||
if q.is_err() { return Ok("".to_owned()) };
|
||||
let skey = q.unwrap().skey;
|
||||
|
||||
let skey = EncodingKey::from_rsa_der(&skey);
|
||||
let header = Header::new(Algorithm::RS512);
|
||||
let token = jwt_encode(&header, &data, &skey)?;
|
||||
|
||||
Ok(token)
|
||||
}
|
||||
|
||||
pub fn rsa_gen() -> (Vec<u8>, Vec<u8>) {
|
||||
let mut rng = rand::thread_rng();
|
||||
let bits = 2048;
|
||||
let skey = RsaPrivateKey::new(&mut rng, bits).expect("RSA err");
|
||||
let pkey = RsaPublicKey::from(&skey);
|
||||
|
||||
(
|
||||
skey.to_pkcs1_der().unwrap().as_bytes().to_vec(),
|
||||
pkey.to_pkcs1_der().unwrap().as_bytes().to_vec()
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn jwt_verify(pool: DBPool, token: &str) -> Result<TokenData<Json>> {
|
||||
let mut con = pool.get().await.unwrap();
|
||||
let mut ret = TokenData { header: Header::new(Algorithm::RS512), claims: json![{}] };
|
||||
|
||||
let q = con
|
||||
.query_parse::<Sites>(&query!("SELECT * FROM bitauth.sites WHERE uuid = ?", "0"))
|
||||
.await;
|
||||
if q.is_err() { return Ok(ret) };
|
||||
let pkey = q.unwrap().pkey;
|
||||
|
||||
let pkey = DecodingKey::from_rsa_der(&pkey);
|
||||
let token = jwt_decode::<Json>(&token, &pkey, &Validation::new(Algorithm::RS512));
|
||||
|
||||
match token.is_ok() {
|
||||
true => ret = token.clone().unwrap(),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub async fn create_user(pool: DBPool, data: HashMap<String, String>) -> Res<bool, SkyError> {
|
||||
let mut ret = true;
|
||||
let mut con = pool.get().await.unwrap();
|
||||
|
||||
let login = &data.get("login").unwrap().trim().to_lowercase();
|
||||
let email = &data.get("email").unwrap().trim().to_lowercase();
|
||||
let pass = data.get("password").unwrap().trim();
|
||||
let pass2 = data.get("password2").unwrap().trim();
|
||||
|
||||
if pass != pass2 { ret = false };
|
||||
if pass.len() < 8 { ret = false };
|
||||
|
||||
let q = con
|
||||
.query_parse::<(String,)>(&query!("SELECT uuid FROM bitauth.users_uuid WHERE login = ?", login))
|
||||
.await;
|
||||
if q.is_ok() { ret = false };
|
||||
|
||||
if ret {
|
||||
let uuid = format!("{}", uuid_v4().as_hyphenated());
|
||||
let pass = bcrypt::hash(pass, 12).unwrap();
|
||||
|
||||
let q = con.query_parse::<()>(&query!(
|
||||
r#"INSERT INTO bitauth.users {
|
||||
uuid: ?,
|
||||
login: ?,
|
||||
password: ?,
|
||||
email: ?,
|
||||
tokens: []
|
||||
}"#,
|
||||
uuid.clone(),
|
||||
login.clone(),
|
||||
pass,
|
||||
email,
|
||||
)).await;
|
||||
if q.is_err() { ret = false }
|
||||
|
||||
let q = con.query_parse::<()>(&query!(
|
||||
r#"INSERT INTO bitauth.users_uuid {
|
||||
login: ?,
|
||||
uuid: ?
|
||||
}"#,
|
||||
login.clone(),
|
||||
uuid.clone()
|
||||
)).await;
|
||||
if q.is_err() { ret = false }
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub async fn get_user(pool: DBPool, login: String) -> Res<(String,), SkyError> {
|
||||
let mut con = pool.get().await.unwrap();
|
||||
|
||||
let q = con
|
||||
.query_parse::<(String,)>(&query!("SELECT uuid FROM bitauth.users_uuid WHERE login = ?", login))
|
||||
.await;
|
||||
|
||||
q
|
||||
}
|
||||
|
||||
pub fn get_cookies(headers: HeaderMap) -> HashMap<String, String> {
|
||||
let header = headers.get(hyper::header::COOKIE);
|
||||
let cookies = match header.is_none() {
|
||||
false => header.unwrap().to_str().unwrap(),
|
||||
_ => ""
|
||||
};
|
||||
|
||||
double_split(cookies.to_owned(), ";", "=")
|
||||
}
|
||||
|
||||
pub async fn authorize_user(pool: DBPool, token: String, session: String) {
|
||||
let mut con = pool.get().await.unwrap();
|
||||
|
||||
let data: JsonMap<String, Json> = jwt_verify(pool.clone(), &token)
|
||||
.await
|
||||
.unwrap()
|
||||
.claims
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
let login = data.get("login").unwrap().as_str();
|
||||
let uuid = data.get("uuid").unwrap().as_str();
|
||||
|
||||
let _ = con.query_parse::<()>(&query!(
|
||||
r#"INSERT INTO bitauth.v0 {
|
||||
session: ?,
|
||||
login: ?,
|
||||
uuid: ?,
|
||||
expire: ?
|
||||
}"#,
|
||||
session,
|
||||
login,
|
||||
uuid,
|
||||
time() + APIV0_LIFETIME
|
||||
)).await;
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ pub const HEADER_HTML: &str = r#"
|
||||
<a href="/">index</a>
|
||||
<a href="/login">login</a>
|
||||
<a href="/register">register</a>
|
||||
<span>{USER_STATUS}</span>
|
||||
<hr>
|
||||
</header>
|
||||
<main>
|
||||
@ -16,7 +17,7 @@ pub const FOOTER_HTML: &str = r#"
|
||||
</main>
|
||||
<footer>
|
||||
<hr>
|
||||
Page time: PAGE_TIMEµs.
|
||||
Page time: {PAGE_TIME}µs.
|
||||
Made by <a href="//bitheaven.ru/">BitHeaven</a>.
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
466
src/main.rs
466
src/main.rs
@ -1,7 +1,7 @@
|
||||
mod funcs;
|
||||
mod types;
|
||||
mod html;
|
||||
mod api;
|
||||
mod url;
|
||||
|
||||
use {
|
||||
std::{
|
||||
@ -10,18 +10,13 @@ use {
|
||||
collections::HashMap,
|
||||
process::exit,
|
||||
},
|
||||
chrono::{
|
||||
DateTime,
|
||||
},
|
||||
http_body_util::{
|
||||
Full,
|
||||
BodyExt,
|
||||
},
|
||||
hyper::{
|
||||
StatusCode,
|
||||
Request,
|
||||
Response,
|
||||
Method,
|
||||
HeaderMap,
|
||||
header::HeaderValue,
|
||||
body::{
|
||||
@ -37,28 +32,8 @@ use {
|
||||
tokio::{
|
||||
net::TcpListener,
|
||||
},
|
||||
rsa::{
|
||||
pkcs1::{
|
||||
EncodeRsaPublicKey,
|
||||
EncodeRsaPrivateKey,
|
||||
},
|
||||
RsaPrivateKey,
|
||||
RsaPublicKey,
|
||||
},
|
||||
jsonwebtoken as jwt,
|
||||
jwt::{
|
||||
Header,
|
||||
Algorithm,
|
||||
TokenData,
|
||||
Validation,
|
||||
EncodingKey,
|
||||
DecodingKey,
|
||||
encode as jwt_encode,
|
||||
decode as jwt_decode,
|
||||
},
|
||||
serde_json::{
|
||||
Value as Json,
|
||||
Map as JsonMap,
|
||||
json,
|
||||
},
|
||||
skytable::{
|
||||
@ -73,6 +48,9 @@ use {
|
||||
bb8::{
|
||||
Pool,
|
||||
},
|
||||
urlencoding::{
|
||||
encode as url_encode,
|
||||
},
|
||||
crate::{
|
||||
types::{
|
||||
users::Users,
|
||||
@ -89,17 +67,19 @@ type DBPool = Arc<Pool<ConnectionMgrTcp>>;
|
||||
type FullBytes = Result<Response<Full<Bytes>>>;
|
||||
|
||||
|
||||
const PORT: u16 = 8083;
|
||||
const PORT: u16 = 8051;
|
||||
|
||||
const DB_POOL: u32 = 32;
|
||||
const DB_ADDR: &str = "192.168.1.49";
|
||||
const DB_ADDR: &str = "127.0.0.1";
|
||||
const DB_PORT: u16 = 2003;
|
||||
const DB_USER: &str = "root";
|
||||
const DB_PASS: &str = "rootpass12345678";
|
||||
const DB_PASS: &str = "dBk6wUAynGRRLsSF";
|
||||
|
||||
const TOKEN_LIFETIME: u32 = 300;
|
||||
const REFRESH_LIFETIME: u32 = 2_678_400;
|
||||
|
||||
const APIV0_LIFETIME: u32 = 120;
|
||||
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
@ -175,13 +155,6 @@ async fn handle_connection(req: Request<Incoming>, pool: DBPool, ip: String) ->
|
||||
x if x.starts_with("/api/") => {},
|
||||
_ => 'jwt_check: {
|
||||
if token == "" { break 'jwt_check; }
|
||||
/* if token != "" {
|
||||
parts.status = StatusCode::FOUND;
|
||||
set_cookie(&mut headers, "token", "");
|
||||
set_location(&mut headers, "/login");
|
||||
parts.headers = headers;
|
||||
return Ok(Response::from_parts(parts, Full::new(Bytes::new())));
|
||||
}*/
|
||||
|
||||
let is_live = jwt_verify(pool.clone(), &token)
|
||||
.await?
|
||||
@ -217,15 +190,16 @@ async fn handle_connection(req: Request<Incoming>, pool: DBPool, ip: String) ->
|
||||
let newref = format!("{}", uuid_v4().as_hyphenated());
|
||||
let time = time();
|
||||
|
||||
// TODO: FIX ERROR IF TOKEN INVALID
|
||||
let (uuid,) = con.query_parse::<(String,)>(&query!(
|
||||
let q = con.query_parse::<(String,)>(&query!(
|
||||
r#"
|
||||
SELECT uid
|
||||
FROM bitauth.tokens
|
||||
WHERE uuid = ?
|
||||
"#,
|
||||
tokenid
|
||||
)).await?;
|
||||
)).await;
|
||||
if q.is_err() { break 'ref_check; }
|
||||
let (uuid,) = q.unwrap();
|
||||
|
||||
let (login,) = con.query_parse::<(String,)>(&query!(
|
||||
r#"
|
||||
@ -265,414 +239,40 @@ async fn handle_connection(req: Request<Incoming>, pool: DBPool, ip: String) ->
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if x == "/authorize" {
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
(body, parts.status, restype) = match req.uri().path().as_ref() {
|
||||
"/" => uri_index(),
|
||||
"/cabinet" => uri_login(req, pool.clone(), &mut headers).await?,
|
||||
"/login" => uri_login(req, pool.clone(), &mut headers).await?,
|
||||
x if x == "/authorize" && logged => uri_authorize(req, pool.clone(), token).await?,
|
||||
// "/authorize" => uri_authorize(req, pool.clone()).await?,
|
||||
"/register" => uri_register(req, pool.clone(), &mut headers).await?,
|
||||
"/recover" => uri_recover(),
|
||||
x if x.starts_with("/@") => uri_user(req, pool.clone()).await?,
|
||||
x if x.starts_with("/api/") => api::endpoint(req, pool.clone()).await,
|
||||
_ => uri_404()
|
||||
"/" => url::index::index(),
|
||||
"/cabinet" => url::login::login(req, pool.clone(), &mut headers).await?,
|
||||
"/login" => url::login::login(req, pool.clone(), &mut headers).await?,
|
||||
x if x == "/authorize" && logged => url::authorize::authorize(req, pool.clone(), token).await?,
|
||||
"/authorize" => uri_auth_required(req, &mut headers).await?,
|
||||
"/register" => url::register::register(req, pool.clone(), &mut headers).await?,
|
||||
"/recover" => url::recover::recover(),
|
||||
x if x.starts_with("/@") => url::user::user(req, pool.clone()).await?,
|
||||
x if x.starts_with("/api/") => url::api::endpoint(req, pool.clone()).await,
|
||||
_ => url::nf::nf()
|
||||
};
|
||||
|
||||
headers.insert(hyper::header::CONTENT_TYPE, restype);
|
||||
parts.headers = headers;
|
||||
|
||||
let body = body.replace("PAGE_TIME", &format!("{}", time_mcs() - t));
|
||||
let user_status = match logged {
|
||||
true => "AUTHORIZED",
|
||||
_ => "NOT AUTHORIZED"
|
||||
};
|
||||
let body = body.replace("{USER_STATUS}", user_status);
|
||||
let body = body.replace("{PAGE_TIME}", &format!("{}", time_mcs() - t));
|
||||
Ok(Response::from_parts(parts, Full::new(Bytes::from(body))))
|
||||
}
|
||||
|
||||
fn set_cookie(headers: &mut HeaderMap, key: &str, value: &str) {
|
||||
let time = DateTime::from_timestamp((time() + REFRESH_LIFETIME) as i64, 0)
|
||||
.expect("REASON")
|
||||
.to_rfc2822();
|
||||
let time = time.replace("+0000", "GMT");
|
||||
headers.append(
|
||||
hyper::header::SET_COOKIE,
|
||||
HeaderValue::from_str(format!("{}={}; HttpOnly; Expires={}", key, value, time).as_str())
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
fn get_cookies(headers: HeaderMap) -> HashMap<String, String> {
|
||||
let header = headers.get(hyper::header::COOKIE);
|
||||
let cookies = match header.is_none() {
|
||||
false => header.unwrap().to_str().unwrap(),
|
||||
_ => ""
|
||||
};
|
||||
async fn uri_auth_required(req: Request<Incoming>, headers: &mut HeaderMap) -> Result<(String, StatusCode, HeaderValue)> {
|
||||
let url = url_encode(req.uri().path_and_query().unwrap().as_str());
|
||||
|
||||
double_split(cookies.to_owned(), ";", "=")
|
||||
}
|
||||
|
||||
fn set_location(headers: &mut HeaderMap, location: &str) {
|
||||
headers.append(
|
||||
hyper::header::LOCATION,
|
||||
HeaderValue::from_str(format!("{}", location).as_str()).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
async fn uri_login(req: Request<Incoming>, pool: DBPool, headers: &mut HeaderMap) -> Result<(String, StatusCode, HeaderValue)> {
|
||||
if *req.method() == Method::POST {
|
||||
let body = get_body_from_request(req).await?;
|
||||
let body = String::from_utf8(body).unwrap();
|
||||
let body = double_split(body, "&", "=");
|
||||
|
||||
let (access, refresh) = login_user(pool.clone(), body).await?;
|
||||
|
||||
set_cookie(headers, "token", &access);
|
||||
set_cookie(headers, "refresh", &refresh);
|
||||
}
|
||||
set_location(headers, format!("/login?q={}", url).as_str());
|
||||
|
||||
let restype: HeaderValue = "text/html".parse().unwrap();
|
||||
Ok((build_html(LOGIN_HTML), StatusCode::OK, restype))
|
||||
Ok(("".to_owned(), StatusCode::FOUND, restype))
|
||||
}
|
||||
|
||||
async fn uri_authorize(req: Request<Incoming>, pool: DBPool, token: String) -> Result<(String, StatusCode, HeaderValue)> {
|
||||
if *req.method() == Method::POST {
|
||||
let r = double_split(req.uri().query().or(Some("")).unwrap().to_owned(), "&", "=");
|
||||
|
||||
let session = r.get("session");
|
||||
let session = match session.is_none() {
|
||||
false => session.unwrap().to_owned(),
|
||||
_ => "".to_owned()
|
||||
};
|
||||
|
||||
if session != "" {
|
||||
authorize_user(pool.clone(), token, session).await;
|
||||
}
|
||||
}
|
||||
|
||||
let restype: HeaderValue = "text/html".parse().unwrap();
|
||||
Ok((build_html(AUTHORIZE_HTML), StatusCode::OK, restype))
|
||||
}
|
||||
|
||||
async fn authorize_user(pool: DBPool, token: String, session: String) {
|
||||
let mut con = pool.get().await.unwrap();
|
||||
|
||||
let data: JsonMap<String, Json> = jwt_verify(pool.clone(), &token)
|
||||
.await
|
||||
.unwrap()
|
||||
.claims
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.clone();
|
||||
|
||||
let login = data.get("login").unwrap().as_str();
|
||||
let uuid = data.get("uuid").unwrap().as_str();
|
||||
|
||||
let _ = con.query_parse::<()>(&query!(
|
||||
r#"INSERT INTO bitauth.v0 {
|
||||
session: ?,
|
||||
login: ?,
|
||||
uuid: ?
|
||||
}"#,
|
||||
session,
|
||||
login,
|
||||
uuid
|
||||
)).await;
|
||||
}
|
||||
|
||||
fn uri_index() -> (String, StatusCode, HeaderValue) {
|
||||
let restype: HeaderValue = "text/html".parse().unwrap();
|
||||
(build_html(INDEX_HTML), StatusCode::OK, restype)
|
||||
}
|
||||
|
||||
async fn uri_register(req: Request<Incoming>, pool: DBPool, headers: &mut HeaderMap) -> Result<(String, StatusCode, HeaderValue)> {
|
||||
let mut body = "".to_owned();
|
||||
let mut status = StatusCode::OK;
|
||||
let restype: HeaderValue = "text/html".parse().unwrap();
|
||||
|
||||
if *req.method() == Method::POST {
|
||||
let request = get_body_from_request(req).await?;
|
||||
let request = String::from_utf8(request).unwrap();
|
||||
let request = double_split(request, "&", "=");
|
||||
|
||||
match create_user(pool.clone(), request).await? {
|
||||
true => {
|
||||
println!("Created");
|
||||
set_location(headers, "/login");
|
||||
},
|
||||
_ => {
|
||||
println!("Failed");
|
||||
set_location(headers, "/register");
|
||||
}
|
||||
}
|
||||
|
||||
status = StatusCode::FOUND;
|
||||
}
|
||||
else {
|
||||
body = build_html(REG_HTML);
|
||||
}
|
||||
|
||||
Ok((body, status, restype))
|
||||
}
|
||||
|
||||
fn uri_recover() -> (String, StatusCode, HeaderValue) {
|
||||
let restype: HeaderValue = "text/html".parse().unwrap();
|
||||
(build_html(RECOVER_HTML), StatusCode::OK, restype)
|
||||
}
|
||||
|
||||
async fn uri_user(req: Request<Incoming>, pool: DBPool) -> Res<(String, StatusCode, HeaderValue), SkyError> {
|
||||
let uri: &str = req.uri().path().as_ref();
|
||||
let login = &uri[2..uri.len()];
|
||||
let body: String;
|
||||
|
||||
let user = get_user(pool, login.to_string()).await;
|
||||
if user.is_ok() {
|
||||
body = format!("{}", user?.uuid);
|
||||
}
|
||||
else {
|
||||
body = "Not fond :(".to_owned();
|
||||
}
|
||||
|
||||
let restype: HeaderValue = "text/html".parse().unwrap();
|
||||
Ok((build_html(&body), StatusCode::OK, restype))
|
||||
}
|
||||
|
||||
fn uri_404() -> (String, StatusCode, HeaderValue) {
|
||||
let restype: HeaderValue = "text/html".parse().unwrap();
|
||||
(build_html(NF_HTML), StatusCode::NOT_FOUND, restype)
|
||||
}
|
||||
|
||||
async fn init_tables(pool: DBPool) -> Res<(), SkyError> {
|
||||
let mut con = pool.get().await.unwrap();
|
||||
|
||||
let _ = con.query_parse::<()>(&query!("CREATE SPACE IF NOT EXISTS bitauth")).await;
|
||||
let _ = con.query_parse::<()>(&query!(r#"
|
||||
CREATE MODEL IF NOT EXISTS bitauth.users(
|
||||
uuid: string,
|
||||
login: string,
|
||||
password: string,
|
||||
email: string,
|
||||
tokens: list {type: string}
|
||||
)
|
||||
"#)).await;
|
||||
let _ = con.query_parse::<()>(&query!(r#"
|
||||
CREATE MODEL IF NOT EXISTS bitauth.users_uuid(
|
||||
login: string,
|
||||
uuid: string
|
||||
)
|
||||
"#)).await;
|
||||
let _ = con.query_parse::<()>(&query!(r#"
|
||||
CREATE MODEL IF NOT EXISTS bitauth.sites(
|
||||
uuid: string,
|
||||
uid: string,
|
||||
domain: string,
|
||||
pkey: binary,
|
||||
skey: binary
|
||||
)
|
||||
"#)).await;
|
||||
let _ = con.query_parse::<()>(&query!(r#"
|
||||
CREATE MODEL IF NOT EXISTS bitauth.tokens(
|
||||
uuid: string,
|
||||
uid: string,
|
||||
sid: string,
|
||||
ref: string,
|
||||
refend: uint32
|
||||
)
|
||||
"#)).await;
|
||||
let _ = con.query_parse::<()>(&query!(r#"
|
||||
CREATE MODEL IF NOT EXISTS bitauth.v0(
|
||||
session: string,
|
||||
login: string,
|
||||
uuid: string
|
||||
)
|
||||
"#)).await;
|
||||
|
||||
let q = con.query_parse::<Sites>(&query!("SELECT * FROM bitauth.sites WHERE uuid = 0")).await;
|
||||
if q.is_err() {
|
||||
let (skey, pkey) = rsa_gen();
|
||||
let _ = con.query_parse::<()>(&query!(
|
||||
"INSERT INTO bitauth.sites { uuid: ?, uid: ?, domain: ?, pkey: ?, skey: ? }",
|
||||
"0", "0", "", pkey, skey
|
||||
)).await;
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_user(pool: DBPool, login: String) -> Res<Users, SkyError> {
|
||||
let mut con = pool.get().await.unwrap();
|
||||
|
||||
let q = con
|
||||
.query_parse::<Users>(&query!("SELECT * FROM bitauth.users WHERE login = ?", login))
|
||||
.await;
|
||||
|
||||
Ok(q?)
|
||||
}
|
||||
|
||||
async fn login_user(pool: DBPool, data: HashMap<String, String>) -> Res<(String, String), SkyError> {
|
||||
let mut ret: (String, String) = Default::default();
|
||||
let mut con = pool.get().await.unwrap();
|
||||
|
||||
let login = &data.get("login").unwrap().trim().to_lowercase();
|
||||
let pass = data.get("password").unwrap().trim();
|
||||
|
||||
let q = con
|
||||
.query_parse::<(String,)>(&query!("SELECT uuid FROM bitauth.users_uuid WHERE login = ?", login))
|
||||
.await;
|
||||
if q.is_err() { println!("{:?}", q.err()); return Ok(ret); };
|
||||
let (uuid,) = q.unwrap();
|
||||
|
||||
let q = con
|
||||
.query_parse::<Users>(&query!("SELECT * FROM bitauth.users WHERE uuid = ?", uuid.clone()))
|
||||
.await;
|
||||
// TODO: Send to admin notify about trouble!
|
||||
if q.is_err() { return Ok(ret); };
|
||||
|
||||
let q = q.unwrap();
|
||||
if bcrypt::verify(pass, q.password.as_str()).unwrap() {
|
||||
let token = format!("{}", uuid_v4().as_hyphenated());
|
||||
let reftoken = format!("{}", uuid_v4().as_hyphenated());
|
||||
let time = time();
|
||||
let uuid: String = q.uuid;
|
||||
let login: String = q.login;
|
||||
|
||||
let _ = con.query_parse::<()>(&query!(
|
||||
"INSERT INTO bitauth.tokens { uuid: ?, uid: ?, sid: ?, ref: ?, refend: ? }",
|
||||
token.clone(), uuid.clone(), "0", reftoken.clone(), time + REFRESH_LIFETIME
|
||||
)).await;
|
||||
let _ = con.query_parse::<()>(&query!(
|
||||
"UPDATE bitauth.users SET tokens += ? WHERE login = ?",
|
||||
token.clone(), login.clone()
|
||||
)).await;
|
||||
|
||||
ret = (
|
||||
jwt_sign(pool.clone(), json!({
|
||||
"login": login.clone(),
|
||||
"uuid": uuid.clone(),
|
||||
"iat": time,
|
||||
"exp": time + TOKEN_LIFETIME
|
||||
})).await.unwrap(),
|
||||
jwt_sign(pool.clone(), json!({
|
||||
"uuid": token.clone(),
|
||||
"iat": time,
|
||||
"ref": reftoken.clone(),
|
||||
"exp": time + REFRESH_LIFETIME
|
||||
})).await.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
async fn create_user(pool: DBPool, data: HashMap<String, String>) -> Res<bool, SkyError> {
|
||||
let mut ret = true;
|
||||
let mut con = pool.get().await.unwrap();
|
||||
|
||||
let login = &data.get("login").unwrap().trim().to_lowercase();
|
||||
let email = &data.get("email").unwrap().trim().to_lowercase();
|
||||
let pass = data.get("password").unwrap().trim();
|
||||
let pass2 = data.get("password2").unwrap().trim();
|
||||
|
||||
if pass != pass2 { ret = false };
|
||||
if pass.len() < 8 { ret = false };
|
||||
|
||||
let q = con
|
||||
.query_parse::<(String,)>(&query!("SELECT uuid FROM bitauth.users_uuid WHERE login = ?", login))
|
||||
.await;
|
||||
if q.is_ok() { ret = false };
|
||||
|
||||
if ret {
|
||||
let uuid = format!("{}", uuid_v4().as_hyphenated());
|
||||
let pass = bcrypt::hash(pass, 12).unwrap();
|
||||
|
||||
let q = con.query_parse::<()>(&query!(
|
||||
r#"INSERT INTO bitauth.users {
|
||||
uuid: ?,
|
||||
login: ?,
|
||||
password: ?,
|
||||
email: ?,
|
||||
tokens: []
|
||||
}"#,
|
||||
uuid.clone(),
|
||||
login.clone(),
|
||||
pass,
|
||||
email,
|
||||
)).await;
|
||||
if q.is_err() { ret = false }
|
||||
|
||||
let q = con.query_parse::<()>(&query!(
|
||||
r#"INSERT INTO bitauth.users_uuid {
|
||||
login: ?,
|
||||
uuid: ?
|
||||
}"#,
|
||||
login.clone(),
|
||||
uuid.clone()
|
||||
)).await;
|
||||
if q.is_err() { ret = false }
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
async fn get_body_from_request(mut req: Request<Incoming>) -> Result<Vec<u8>> {
|
||||
let mut body: Vec<u8> = vec![];
|
||||
while let Some(next) = req.frame().await {
|
||||
let frame = next?;
|
||||
if let Some(chunk) = frame.data_ref() {
|
||||
body.extend_from_slice(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
fn rsa_gen() -> (Vec<u8>, Vec<u8>) {
|
||||
let mut rng = rand::thread_rng();
|
||||
let bits = 2048;
|
||||
let skey = RsaPrivateKey::new(&mut rng, bits).expect("RSA err");
|
||||
let pkey = RsaPublicKey::from(&skey);
|
||||
|
||||
(
|
||||
skey.to_pkcs1_der().unwrap().as_bytes().to_vec(),
|
||||
pkey.to_pkcs1_der().unwrap().as_bytes().to_vec()
|
||||
)
|
||||
}
|
||||
|
||||
async fn jwt_sign(pool: DBPool, data: Json) -> Result<String> {
|
||||
let mut con = pool.get().await.unwrap();
|
||||
|
||||
let q = con
|
||||
.query_parse::<Sites>(&query!("SELECT * FROM bitauth.sites WHERE uuid = ?", "0"))
|
||||
.await;
|
||||
if q.is_err() { return Ok("".to_owned()) };
|
||||
let skey = q.unwrap().skey;
|
||||
|
||||
let skey = EncodingKey::from_rsa_der(&skey);
|
||||
let header = Header::new(Algorithm::RS512);
|
||||
let token = jwt_encode(&header, &data, &skey)?;
|
||||
|
||||
Ok(token)
|
||||
}
|
||||
|
||||
async fn jwt_verify(pool: DBPool, token: &str) -> Result<TokenData<Json>> {
|
||||
let mut con = pool.get().await.unwrap();
|
||||
let mut ret = TokenData { header: Header::new(Algorithm::RS512), claims: json![{}] };
|
||||
|
||||
let q = con
|
||||
.query_parse::<Sites>(&query!("SELECT * FROM bitauth.sites WHERE uuid = ?", "0"))
|
||||
.await;
|
||||
if q.is_err() { return Ok(ret) };
|
||||
let pkey = q.unwrap().pkey;
|
||||
|
||||
let pkey = DecodingKey::from_rsa_der(&pkey);
|
||||
let token = jwt_decode::<Json>(&token, &pkey, &Validation::new(Algorithm::RS512));
|
||||
|
||||
match token.is_ok() {
|
||||
true => ret = token.clone().unwrap(),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
|
||||
@ -1,31 +1,27 @@
|
||||
mod v0;
|
||||
mod v1;
|
||||
|
||||
use {
|
||||
hyper::{
|
||||
StatusCode,
|
||||
Request,
|
||||
header::HeaderValue,
|
||||
body::{
|
||||
Incoming,
|
||||
},
|
||||
body::Incoming,
|
||||
},
|
||||
serde_json::{
|
||||
Value as Json,
|
||||
json,
|
||||
},
|
||||
skytable::pool::ConnectionMgrTcp,
|
||||
bb8::Pool,
|
||||
std::sync::Arc,
|
||||
crate::DBPool,
|
||||
};
|
||||
|
||||
type DBPool = Arc<Pool<ConnectionMgrTcp>>;
|
||||
|
||||
|
||||
pub async fn endpoint(req: Request<Incoming>, pool: DBPool) -> (String, StatusCode, HeaderValue) {
|
||||
let uri: &str = req.uri().path().as_ref();
|
||||
let res: Json = match &uri[4..uri.len()] {
|
||||
"/test" => json!({"error": false, "msg": "test"}),
|
||||
x if x.starts_with("/v0/") => v0::api(req, pool.clone()).await,
|
||||
x if x.starts_with("/v1/") => v1::api(req, pool.clone()).await,
|
||||
_ => json!({"error": true, "msg": "No endpoint"})
|
||||
};
|
||||
|
||||
@ -1,27 +1,20 @@
|
||||
use {
|
||||
hyper::{
|
||||
Request,
|
||||
body::{
|
||||
Incoming,
|
||||
},
|
||||
body::Incoming,
|
||||
},
|
||||
serde_json::{
|
||||
Value as Json,
|
||||
json,
|
||||
},
|
||||
skytable::{
|
||||
pool::ConnectionMgrTcp,
|
||||
query,
|
||||
},
|
||||
bb8::Pool,
|
||||
std::sync::Arc,
|
||||
skytable::query,
|
||||
crate::{
|
||||
DBPool,
|
||||
time,
|
||||
double_split,
|
||||
},
|
||||
};
|
||||
|
||||
type DBPool = Arc<Pool<ConnectionMgrTcp>>;
|
||||
|
||||
|
||||
pub async fn api(req: Request<Incoming>, pool: DBPool) -> Json {
|
||||
let uri: &str = req.uri().path().as_ref();
|
||||
@ -40,10 +33,11 @@ async fn auth(req: Request<Incoming>, _pool: DBPool) -> Json {
|
||||
.or(Some(&"".to_string()))
|
||||
.unwrap());
|
||||
match sess.as_str() {
|
||||
"" => json!({"error": true, "msg": "No session in url"}),
|
||||
"" => json!({"error": true, "msg": "No session in request"}),
|
||||
x if x.len() > 128 => json!({"error": true, "msg": "Session len is too long"}),
|
||||
_ => json!({
|
||||
"error": false,
|
||||
"link": format!("https://auth.bitheaven.ru/authorize?session={}", sess)
|
||||
"link": format!("https://auth.bitheaven.ru/authorize?v=0&session={}", sess)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -58,8 +52,8 @@ async fn auth_get(req: Request<Incoming>, pool: DBPool) -> Json {
|
||||
_ => ""
|
||||
};
|
||||
|
||||
let res = con.query_parse::<(String, String)>(&query!(
|
||||
"SELECT login, uuid FROM bitauth.v0 WHERE session = ?",
|
||||
let res = con.query_parse::<(String, String, u32)>(&query!(
|
||||
"SELECT login, uuid, expire FROM bitauth.v0 WHERE session = ?",
|
||||
session
|
||||
)).await;
|
||||
let _ = con.query_parse::<()>(&query!(
|
||||
@ -67,13 +61,15 @@ async fn auth_get(req: Request<Incoming>, pool: DBPool) -> Json {
|
||||
session
|
||||
)).await;
|
||||
|
||||
let (login, uuid) = match res.is_ok() {
|
||||
false => ("".to_owned(), "".to_owned()),
|
||||
let (login, uuid, exp) = match res.is_ok() {
|
||||
false => ("".to_owned(), "".to_owned(), 0),
|
||||
_ => res.unwrap()
|
||||
};
|
||||
|
||||
match login {
|
||||
"" => json!({"error": true, "msg": "Not auth yet"}),
|
||||
_ => json!({"error": false, "login": login, "uuid": uuid})
|
||||
if login.as_str() == "" || exp < time() {
|
||||
json!({"error": true, "msg": "Not auth yet"})
|
||||
}
|
||||
else {
|
||||
json!({"error": false, "login": login, "uuid": uuid})
|
||||
}
|
||||
}
|
||||
75
src/url/api/v1.rs
Normal file
75
src/url/api/v1.rs
Normal file
@ -0,0 +1,75 @@
|
||||
use {
|
||||
hyper::{
|
||||
Request,
|
||||
body::Incoming,
|
||||
},
|
||||
serde_json::{
|
||||
Value as Json,
|
||||
json,
|
||||
},
|
||||
skytable::query,
|
||||
crate::{
|
||||
DBPool,
|
||||
time,
|
||||
double_split,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
pub async fn api(req: Request<Incoming>, pool: DBPool) -> Json {
|
||||
let uri: &str = req.uri().path().as_ref();
|
||||
match &uri[7..uri.len()] {
|
||||
"/auth" => auth(req, pool.clone()).await,
|
||||
"/auth_get" => auth_get(req, pool.clone()).await,
|
||||
_ => json!({"error": true, "msg": "No endpoint"})
|
||||
}
|
||||
}
|
||||
|
||||
async fn auth(req: Request<Incoming>, _pool: DBPool) -> Json {
|
||||
let query = req.uri().query().or(Some("")).unwrap();
|
||||
let query = double_split(query.to_string(), "&", "=");
|
||||
let sess = std::string::String::from(query
|
||||
.get("session")
|
||||
.or(Some(&"".to_string()))
|
||||
.unwrap());
|
||||
match sess.as_str() {
|
||||
"" => json!({"error": true, "msg": "No session in request"}),
|
||||
x if x.len() > 128 => json!({"error": true, "msg": "Session len is too long"}),
|
||||
_ => json!({
|
||||
"error": false,
|
||||
"link": format!("https://auth.bitheaven.ru/authorize?v=0&session={}", sess)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn auth_get(req: Request<Incoming>, pool: DBPool) -> Json {
|
||||
let mut con = pool.get().await.unwrap();
|
||||
|
||||
let query = req.uri().query().or(Some("")).unwrap();
|
||||
let query = double_split(query.to_string(), "&", "=");
|
||||
let session = match query.get("session").is_none() {
|
||||
false => query.get("session").unwrap(),
|
||||
_ => ""
|
||||
};
|
||||
|
||||
let res = con.query_parse::<(String, String, u32)>(&query!(
|
||||
"SELECT login, uuid, expire FROM bitauth.v0 WHERE session = ?",
|
||||
session
|
||||
)).await;
|
||||
let _ = con.query_parse::<()>(&query!(
|
||||
"DELETE FROM bitauth.v0 WHERE session = ?",
|
||||
session
|
||||
)).await;
|
||||
|
||||
let (login, uuid, exp) = match res.is_ok() {
|
||||
false => ("".to_owned(), "".to_owned(), 0),
|
||||
_ => res.unwrap()
|
||||
};
|
||||
|
||||
if login.as_str() == "" || exp < time() {
|
||||
json!({"error": true, "msg": "Not auth yet"})
|
||||
}
|
||||
else {
|
||||
json!({"error": false, "login": login, "uuid": uuid})
|
||||
}
|
||||
}
|
||||
38
src/url/authorize.rs
Normal file
38
src/url/authorize.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use {
|
||||
hyper::{
|
||||
Request,
|
||||
body::Incoming,
|
||||
Method,
|
||||
StatusCode,
|
||||
header::HeaderValue,
|
||||
},
|
||||
crate::{
|
||||
AUTHORIZE_HTML,
|
||||
DBPool,
|
||||
Result,
|
||||
build_html,
|
||||
double_split,
|
||||
authorize_user,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
pub async fn authorize(req: Request<Incoming>, pool: DBPool, token: String) -> Result<(String, StatusCode, HeaderValue)> {
|
||||
// TODO: Forward for versions.
|
||||
if *req.method() == Method::POST {
|
||||
let r = double_split(req.uri().query().or(Some("")).unwrap().to_owned(), "&", "=");
|
||||
|
||||
let session = r.get("session");
|
||||
let session = match session.is_none() {
|
||||
false => session.unwrap().to_owned(),
|
||||
_ => "".to_owned()
|
||||
};
|
||||
|
||||
if session != "" && session.len() <= 128 {
|
||||
authorize_user(pool.clone(), token, session).await;
|
||||
}
|
||||
}
|
||||
|
||||
let restype: HeaderValue = "text/html".parse().unwrap();
|
||||
Ok((build_html(AUTHORIZE_HTML), StatusCode::OK, restype))
|
||||
}
|
||||
16
src/url/index.rs
Normal file
16
src/url/index.rs
Normal file
@ -0,0 +1,16 @@
|
||||
use {
|
||||
hyper::{
|
||||
StatusCode,
|
||||
header::HeaderValue,
|
||||
},
|
||||
crate::{
|
||||
INDEX_HTML,
|
||||
build_html,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
pub fn index() -> (String, StatusCode, HeaderValue) {
|
||||
let restype: HeaderValue = "text/html".parse().unwrap();
|
||||
(build_html(INDEX_HTML), StatusCode::OK, restype)
|
||||
}
|
||||
53
src/url/login.rs
Normal file
53
src/url/login.rs
Normal file
@ -0,0 +1,53 @@
|
||||
use {
|
||||
hyper::{
|
||||
StatusCode,
|
||||
Request,
|
||||
Method,
|
||||
HeaderMap,
|
||||
header::HeaderValue,
|
||||
body::{
|
||||
Incoming,
|
||||
},
|
||||
},
|
||||
urlencoding::{
|
||||
decode as url_decode,
|
||||
},
|
||||
crate::{
|
||||
Result,
|
||||
DBPool,
|
||||
html::*,
|
||||
funcs::*,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
pub async fn login(req: Request<Incoming>, pool: DBPool, headers: &mut HeaderMap) -> Result<(String, StatusCode, HeaderValue)> {
|
||||
let mut body = build_html(LOGIN_HTML);
|
||||
let mut status = StatusCode::OK;
|
||||
let restype: HeaderValue = "text/html".parse().unwrap();
|
||||
|
||||
if *req.method() == Method::POST {
|
||||
let r = double_split(req.uri().query().or(Some("")).unwrap().to_owned(), "&", "=");
|
||||
|
||||
let post = get_body_from_request(req).await?;
|
||||
let post = String::from_utf8(post).unwrap();
|
||||
let post = double_split(post, "&", "=");
|
||||
|
||||
let (access, refresh) = login_user(pool.clone(), post).await?;
|
||||
|
||||
set_cookie(headers, "token", &access);
|
||||
set_cookie(headers, "refresh", &refresh);
|
||||
|
||||
let r = r.get("q");
|
||||
match r.is_some() {
|
||||
true => {
|
||||
status = StatusCode::FOUND;
|
||||
body = "".to_owned();
|
||||
set_location(headers, format!("{}", url_decode(r.unwrap())?).as_str());
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok((body, status, restype))
|
||||
}
|
||||
8
src/url/mod.rs
Normal file
8
src/url/mod.rs
Normal file
@ -0,0 +1,8 @@
|
||||
pub mod api;
|
||||
pub mod login;
|
||||
pub mod index;
|
||||
pub mod authorize;
|
||||
pub mod nf;
|
||||
pub mod register;
|
||||
pub mod user;
|
||||
pub mod recover;
|
||||
16
src/url/nf.rs
Normal file
16
src/url/nf.rs
Normal file
@ -0,0 +1,16 @@
|
||||
use {
|
||||
hyper::{
|
||||
StatusCode,
|
||||
header::HeaderValue,
|
||||
},
|
||||
crate::{
|
||||
NF_HTML,
|
||||
build_html,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
pub fn nf() -> (String, StatusCode, HeaderValue) {
|
||||
let restype: HeaderValue = "text/html".parse().unwrap();
|
||||
(build_html(NF_HTML), StatusCode::NOT_FOUND, restype)
|
||||
}
|
||||
16
src/url/recover.rs
Normal file
16
src/url/recover.rs
Normal file
@ -0,0 +1,16 @@
|
||||
use {
|
||||
hyper::{
|
||||
StatusCode,
|
||||
header::HeaderValue,
|
||||
},
|
||||
crate::{
|
||||
build_html,
|
||||
RECOVER_HTML,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
pub fn recover() -> (String, StatusCode, HeaderValue) {
|
||||
let restype: HeaderValue = "text/html".parse().unwrap();
|
||||
(build_html(RECOVER_HTML), StatusCode::OK, restype)
|
||||
}
|
||||
51
src/url/register.rs
Normal file
51
src/url/register.rs
Normal file
@ -0,0 +1,51 @@
|
||||
use {
|
||||
hyper::{
|
||||
body::Incoming,
|
||||
HeaderMap,
|
||||
Method,
|
||||
Request,
|
||||
header::HeaderValue,
|
||||
StatusCode,
|
||||
},
|
||||
crate::{
|
||||
Result,
|
||||
DBPool,
|
||||
REG_HTML,
|
||||
build_html,
|
||||
set_location,
|
||||
create_user,
|
||||
double_split,
|
||||
get_body_from_request,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
pub async fn register(req: Request<Incoming>, pool: DBPool, headers: &mut HeaderMap) -> Result<(String, StatusCode, HeaderValue)> {
|
||||
let mut body = "".to_owned();
|
||||
let mut status = StatusCode::OK;
|
||||
let restype: HeaderValue = "text/html".parse().unwrap();
|
||||
|
||||
if *req.method() == Method::POST {
|
||||
let request = get_body_from_request(req).await?;
|
||||
let request = String::from_utf8(request).unwrap();
|
||||
let request = double_split(request, "&", "=");
|
||||
|
||||
match create_user(pool.clone(), request).await? {
|
||||
true => {
|
||||
println!("Created");
|
||||
set_location(headers, "/login");
|
||||
},
|
||||
_ => {
|
||||
println!("Failed");
|
||||
set_location(headers, "/register");
|
||||
}
|
||||
}
|
||||
|
||||
status = StatusCode::FOUND;
|
||||
}
|
||||
else {
|
||||
body = build_html(REG_HTML);
|
||||
}
|
||||
|
||||
Ok((body, status, restype))
|
||||
}
|
||||
34
src/url/user.rs
Normal file
34
src/url/user.rs
Normal file
@ -0,0 +1,34 @@
|
||||
use {
|
||||
hyper::{
|
||||
Request,
|
||||
StatusCode,
|
||||
body::Incoming,
|
||||
header::HeaderValue,
|
||||
},
|
||||
crate::{
|
||||
Res,
|
||||
DBPool,
|
||||
SkyError,
|
||||
get_user,
|
||||
build_html,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
pub async fn user(req: Request<Incoming>, pool: DBPool) -> Res<(String, StatusCode, HeaderValue), SkyError> {
|
||||
let uri: &str = req.uri().path().as_ref();
|
||||
let login = &uri[2..uri.len()];
|
||||
let body: String;
|
||||
|
||||
let user = get_user(pool, login.to_string()).await;
|
||||
if user.is_ok() {
|
||||
let (uuid,) = user.unwrap();
|
||||
body = format!("{}", uuid);
|
||||
}
|
||||
else {
|
||||
body = "Not fond :(".to_owned();
|
||||
}
|
||||
|
||||
let restype: HeaderValue = "text/html".parse().unwrap();
|
||||
Ok((build_html(&body), StatusCode::OK, restype))
|
||||
}
|
||||
Reference in New Issue
Block a user