Compare commits
	
		
			12 Commits
		
	
	
		
			73f5edf77f
			...
			main
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 9fa66899aa | |||
| 8a48ca0ad6 | |||
| 26301ac344 | |||
| 81023f2042 | |||
| f5f35855ce | |||
| 7018adf98d | |||
| a60713a4b0 | |||
| 9ef4176091 | |||
| 6cd6b12630 | |||
| 2a9ac271a7 | |||
| 8a7ff047b6 | |||
| e28a8c2893 | 
							
								
								
									
										835
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										835
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,6 +1,6 @@ | |||||||
| [package] | [package] | ||||||
| name = "BitAuth" | name = "BitAuth" | ||||||
| version = "0.1.0" | version = "0.1.1" | ||||||
| edition = "2021" | edition = "2021" | ||||||
|  |  | ||||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | # 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" | rand = "0.8.5" | ||||||
| rsa = "0.9.6" | rsa = "0.9.6" | ||||||
| serde_json = "1.0.111" | serde_json = "1.0.111" | ||||||
| skytable = "0.8.6" | skytable = "0.8.10" | ||||||
| tokio = { version = "1.35.1", features = ["full"] } | tokio = { version = "1.35.1", features = ["full"] } | ||||||
| urlencoding = "2.1.3" | urlencoding = "2.1.3" | ||||||
| uuid = { version = "1.6.1", features = ["v4", "v5"] } | uuid = { version = "1.6.1", features = ["v4", "v5"] } | ||||||
|  | webhook = "2.1.2" | ||||||
|  |  | ||||||
|  |  | ||||||
| [[bin]] | [[bin]] | ||||||
|  | |||||||
							
								
								
									
										28
									
								
								TODO
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								TODO
									
									
									
									
									
								
							| @ -1,6 +1,4 @@ | |||||||
| ############################ | Forward user back when set `back_url` | ||||||
| ### MAKE USER UUID TABLE ### |  | ||||||
| ############################ |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Auth using tokens | 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 link (device check url with this session and get data) | ||||||
| /auth?session=<UNIQUE> | /api/v0/auth?session=<UNIQUE>&back_url=<OPTIONAL> | ||||||
| # Get auth data | # 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, | 			UNIX_EPOCH, | ||||||
| 		}, | 		}, | ||||||
| 	}, | 	}, | ||||||
|  | 	webhook::client::WebhookClient, | ||||||
| 	urlencoding::decode as url_decode, | 	urlencoding::decode as url_decode, | ||||||
| 	uuid::Uuid, | 	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::{ | 	crate::{ | ||||||
|  | 		Res, | ||||||
|  | 		Result, | ||||||
|  | 		DBPool, | ||||||
|  | 		REFRESH_LIFETIME, | ||||||
|  | 		TOKEN_LIFETIME, | ||||||
| 		html::*, | 		html::*, | ||||||
|  | 		Users, | ||||||
|  | 		Sites, | ||||||
|  | 		APIV0_LIFETIME, | ||||||
| 	}, | 	}, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @ -28,6 +75,64 @@ pub fn build_html(body: &str) -> String { | |||||||
| 	format!("{}{}{}{}", HEADER_HTML, CSS3, body, FOOTER_HTML) | 	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> { | pub fn double_split(body: String, first: &str, second: &str) -> HashMap<String, String> { | ||||||
| 	body.split(first) | 	body.split(first) | ||||||
| 		.filter_map(|c| { | 		.filter_map(|c| { | ||||||
| @ -53,3 +158,260 @@ pub fn time_mcs() -> u128 { | |||||||
| 		.unwrap() | 		.unwrap() | ||||||
| 		.as_micros() | 		.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="/">index</a> | ||||||
| 			<a href="/login">login</a> | 			<a href="/login">login</a> | ||||||
| 			<a href="/register">register</a> | 			<a href="/register">register</a> | ||||||
|  | 			<span>{USER_STATUS}</span> | ||||||
| 			<hr> | 			<hr> | ||||||
| 		</header> | 		</header> | ||||||
| 		<main> | 		<main> | ||||||
| @ -16,7 +17,7 @@ pub const FOOTER_HTML: &str = r#" | |||||||
| 		</main> | 		</main> | ||||||
| 		<footer> | 		<footer> | ||||||
| 			<hr> | 			<hr> | ||||||
| 			Page time: PAGE_TIMEµs. | 			Page time: {PAGE_TIME}µs. | ||||||
| 			Made by <a href="//bitheaven.ru/">BitHeaven</a>. | 			Made by <a href="//bitheaven.ru/">BitHeaven</a>. | ||||||
| 		</footer> | 		</footer> | ||||||
| 	</body> | 	</body> | ||||||
|  | |||||||
							
								
								
									
										467
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										467
									
								
								src/main.rs
									
									
									
									
									
								
							| @ -1,7 +1,7 @@ | |||||||
| mod funcs; | mod funcs; | ||||||
| mod types; | mod types; | ||||||
| mod html; | mod html; | ||||||
| mod api; | mod url; | ||||||
|  |  | ||||||
| use { | use { | ||||||
| 	std::{ | 	std::{ | ||||||
| @ -10,18 +10,13 @@ use { | |||||||
| 		collections::HashMap, | 		collections::HashMap, | ||||||
| 		process::exit, | 		process::exit, | ||||||
| 	}, | 	}, | ||||||
| 	chrono::{ |  | ||||||
| 		DateTime, |  | ||||||
| 	}, |  | ||||||
| 	http_body_util::{ | 	http_body_util::{ | ||||||
| 		Full, | 		Full, | ||||||
| 		BodyExt, |  | ||||||
| 	}, | 	}, | ||||||
| 	hyper::{ | 	hyper::{ | ||||||
| 		StatusCode, | 		StatusCode, | ||||||
| 		Request, | 		Request, | ||||||
| 		Response, | 		Response, | ||||||
| 		Method, |  | ||||||
| 		HeaderMap, | 		HeaderMap, | ||||||
| 		header::HeaderValue, | 		header::HeaderValue, | ||||||
| 		body::{ | 		body::{ | ||||||
| @ -37,28 +32,8 @@ use { | |||||||
| 	tokio::{ | 	tokio::{ | ||||||
| 		net::TcpListener, | 		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::{ | 	serde_json::{ | ||||||
| 		Value as Json, | 		Value as Json, | ||||||
| 		Map as JsonMap, |  | ||||||
| 		json, | 		json, | ||||||
| 	}, | 	}, | ||||||
| 	skytable::{ | 	skytable::{ | ||||||
| @ -73,6 +48,9 @@ use { | |||||||
| 	bb8::{ | 	bb8::{ | ||||||
| 		Pool, | 		Pool, | ||||||
| 	}, | 	}, | ||||||
|  | 	urlencoding::{ | ||||||
|  | 		encode as url_encode, | ||||||
|  | 	}, | ||||||
| 	crate::{ | 	crate::{ | ||||||
| 		types::{ | 		types::{ | ||||||
| 			users::Users, | 			users::Users, | ||||||
| @ -89,13 +67,13 @@ type DBPool = Arc<Pool<ConnectionMgrTcp>>; | |||||||
| type FullBytes = Result<Response<Full<Bytes>>>; | type FullBytes = Result<Response<Full<Bytes>>>; | ||||||
|  |  | ||||||
|  |  | ||||||
| const PORT: u16 = 8083; | const PORT: u16 = 8051; | ||||||
|  |  | ||||||
| const DB_POOL: u32 = 32; | 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_PORT: u16 = 2003; | ||||||
| const DB_USER: &str = "root"; | const DB_USER: &str = "root"; | ||||||
| const DB_PASS: &str = "rootpass12345678"; | const DB_PASS: &str = "dBk6wUAynGRRLsSF"; | ||||||
|  |  | ||||||
| const TOKEN_LIFETIME: u32 = 300; | const TOKEN_LIFETIME: u32 = 300; | ||||||
| const REFRESH_LIFETIME: u32 = 2_678_400; | const REFRESH_LIFETIME: u32 = 2_678_400; | ||||||
| @ -177,13 +155,6 @@ async fn handle_connection(req: Request<Incoming>, pool: DBPool, ip: String) -> | |||||||
| 		x if x.starts_with("/api/") => {}, | 		x if x.starts_with("/api/") => {}, | ||||||
| 		_ => 'jwt_check: { | 		_ => 'jwt_check: { | ||||||
| 			if token == "" { break '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) | 			let is_live = jwt_verify(pool.clone(), &token) | ||||||
| 				.await? | 				.await? | ||||||
| @ -219,15 +190,16 @@ async fn handle_connection(req: Request<Incoming>, pool: DBPool, ip: String) -> | |||||||
| 						let newref = format!("{}", uuid_v4().as_hyphenated()); | 						let newref = format!("{}", uuid_v4().as_hyphenated()); | ||||||
| 						let time = time(); | 						let time = time(); | ||||||
|  |  | ||||||
| 						// TODO: FIX ERROR IF TOKEN INVALID | 						let q = con.query_parse::<(String,)>(&query!( | ||||||
| 						let (uuid,) = con.query_parse::<(String,)>(&query!( |  | ||||||
| 							r#" | 							r#" | ||||||
| 								SELECT uid | 								SELECT uid | ||||||
| 								FROM bitauth.tokens | 								FROM bitauth.tokens | ||||||
| 								WHERE uuid = ? | 								WHERE uuid = ? | ||||||
| 							"#, | 							"#, | ||||||
| 							tokenid | 							tokenid | ||||||
| 						)).await?; | 						)).await; | ||||||
|  | 						if q.is_err() { break 'ref_check; } | ||||||
|  | 						let (uuid,) = q.unwrap(); | ||||||
|  |  | ||||||
| 						let (login,) = con.query_parse::<(String,)>(&query!( | 						let (login,) = con.query_parse::<(String,)>(&query!( | ||||||
| 							r#" | 							r#" | ||||||
| @ -267,417 +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() { | 	(body, parts.status, restype) = match req.uri().path().as_ref() { | ||||||
| 		"/" => uri_index(), | 		"/" => url::index::index(), | ||||||
| 		"/cabinet" => uri_login(req, pool.clone(), &mut headers).await?, | 		"/cabinet" => url::login::login(req, pool.clone(), &mut headers).await?, | ||||||
| 		"/login" => uri_login(req, pool.clone(), &mut headers).await?, | 		"/login" => url::login::login(req, pool.clone(), &mut headers).await?, | ||||||
| 		x if x == "/authorize" && logged => uri_authorize(req, pool.clone(), token).await?, | 		x if x == "/authorize" && logged => url::authorize::authorize(req, pool.clone(), token).await?, | ||||||
| //		"/authorize" => uri_authorize(req, pool.clone()).await?, | 		"/authorize" => uri_auth_required(req, &mut headers).await?, | ||||||
| 		"/register" => uri_register(req, pool.clone(), &mut headers).await?, | 		"/register" => url::register::register(req, pool.clone(), &mut headers).await?, | ||||||
| 		"/recover" => uri_recover(), | 		"/recover" => url::recover::recover(), | ||||||
| 		x if x.starts_with("/@") => uri_user(req, pool.clone()).await?, | 		x if x.starts_with("/@") => url::user::user(req, pool.clone()).await?, | ||||||
| 		x if x.starts_with("/api/") => api::endpoint(req, pool.clone()).await, | 		x if x.starts_with("/api/") => url::api::endpoint(req, pool.clone()).await, | ||||||
| 		_ => uri_404() | 		_ => url::nf::nf() | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
| 	headers.insert(hyper::header::CONTENT_TYPE, restype); | 	headers.insert(hyper::header::CONTENT_TYPE, restype); | ||||||
| 	parts.headers = headers; | 	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)))) | 	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> { | async fn uri_auth_required(req: Request<Incoming>, headers: &mut HeaderMap) -> Result<(String, StatusCode, HeaderValue)> { | ||||||
| 	let header = headers.get(hyper::header::COOKIE); | 	let url = url_encode(req.uri().path_and_query().unwrap().as_str()); | ||||||
| 	let cookies = match header.is_none() { |  | ||||||
| 		false => header.unwrap().to_str().unwrap(), |  | ||||||
| 		_ => "" |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	double_split(cookies.to_owned(), ";", "=") | 	set_location(headers, format!("/login?q={}", url).as_str()); | ||||||
| } |  | ||||||
|  |  | ||||||
| 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); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	let restype: HeaderValue = "text/html".parse().unwrap(); | 	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: ?, |  | ||||||
| 			expire: ?, |  | ||||||
| 		}"#, |  | ||||||
| 		session, |  | ||||||
| 		login, |  | ||||||
| 		uuid, |  | ||||||
| 		time() + APIV0_LIFETIME |  | ||||||
| 	)).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, |  | ||||||
| 			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(()) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| 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 v0; | ||||||
|  | mod v1; | ||||||
| 
 | 
 | ||||||
| use { | use { | ||||||
| 	hyper::{ | 	hyper::{ | ||||||
| 		StatusCode, | 		StatusCode, | ||||||
| 		Request, | 		Request, | ||||||
| 		header::HeaderValue, | 		header::HeaderValue, | ||||||
| 		body::{ | 		body::Incoming, | ||||||
| 			Incoming, |  | ||||||
| 		}, |  | ||||||
| 	}, | 	}, | ||||||
| 	serde_json::{ | 	serde_json::{ | ||||||
| 		Value as Json, | 		Value as Json, | ||||||
| 		json, | 		json, | ||||||
| 	}, | 	}, | ||||||
| 	skytable::pool::ConnectionMgrTcp, | 	crate::DBPool, | ||||||
| 	bb8::Pool, |  | ||||||
| 	std::sync::Arc, |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| type DBPool = Arc<Pool<ConnectionMgrTcp>>; |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| pub async fn endpoint(req: Request<Incoming>, pool: DBPool) -> (String, StatusCode, HeaderValue) { | pub async fn endpoint(req: Request<Incoming>, pool: DBPool) -> (String, StatusCode, HeaderValue) { | ||||||
| 	let uri: &str = req.uri().path().as_ref(); | 	let uri: &str = req.uri().path().as_ref(); | ||||||
| 	let res: Json = match &uri[4..uri.len()] { | 	let res: Json = match &uri[4..uri.len()] { | ||||||
| 		"/test" => json!({"error": false, "msg": "test"}), | 		"/test" => json!({"error": false, "msg": "test"}), | ||||||
| 		x if x.starts_with("/v0/") => v0::api(req, pool.clone()).await, | 		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"}) | 		_ => json!({"error": true, "msg": "No endpoint"}) | ||||||
| 	}; | 	}; | ||||||
| 	
 | 	
 | ||||||
| @ -1,28 +1,20 @@ | |||||||
| use { | use { | ||||||
| 	hyper::{ | 	hyper::{ | ||||||
| 		Request, | 		Request, | ||||||
| 		body::{ | 		body::Incoming, | ||||||
| 			Incoming, |  | ||||||
| 		}, |  | ||||||
| 	}, | 	}, | ||||||
| 	serde_json::{ | 	serde_json::{ | ||||||
| 		Value as Json, | 		Value as Json, | ||||||
| 		json, | 		json, | ||||||
| 	}, | 	}, | ||||||
| 	skytable::{ | 	skytable::query, | ||||||
| 		pool::ConnectionMgrTcp, |  | ||||||
| 		query, |  | ||||||
| 	}, |  | ||||||
| 	bb8::Pool, |  | ||||||
| 	std::sync::Arc, |  | ||||||
| 	crate::{ | 	crate::{ | ||||||
|  | 		DBPool, | ||||||
| 		time, | 		time, | ||||||
| 		double_split, | 		double_split, | ||||||
| 	}, | 	}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| type DBPool = Arc<Pool<ConnectionMgrTcp>>; |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| pub async fn api(req: Request<Incoming>, pool: DBPool) -> Json { | pub async fn api(req: Request<Incoming>, pool: DBPool) -> Json { | ||||||
| 	let uri: &str = req.uri().path().as_ref(); | 	let uri: &str = req.uri().path().as_ref(); | ||||||
| @ -41,10 +33,11 @@ async fn auth(req: Request<Incoming>, _pool: DBPool) -> Json { | |||||||
| 		.or(Some(&"".to_string())) | 		.or(Some(&"".to_string())) | ||||||
| 		.unwrap()); | 		.unwrap()); | ||||||
| 	match sess.as_str() { | 	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!({ | 		_ => json!({ | ||||||
| 			"error": false, | 			"error": false, | ||||||
| 			"link": format!("https://auth.bitheaven.ru/authorize?session={}", sess) | 			"link": format!("https://auth.bitheaven.ru/authorize?v=0&session={}", sess) | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
							
								
								
									
										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
	