commit 1800ffb071b0d9b3f0f7c1f08ee678ddb0f2437c
parent 6942ba3bcdb4a19f296d4d6d36153d5327906bce
Author: quantumish <freifeld.david@gmail.com>
Date: Thu, 22 Sep 2022 19:11:19 -0700
Attempt authorization, add barebones API
Diffstat:
2 files changed, 147 insertions(+), 23 deletions(-)
diff --git a/scuttlebutt/Cargo.toml b/scuttlebutt/Cargo.toml
@@ -7,7 +7,13 @@ edition = "2021"
[dependencies]
ctor = "0.1.23"
+hmac = "0.12.1"
+jwt = "0.16.0"
+log = "0.4.17"
poem = { version = "1.3.42", features = ["test"] }
poem-openapi = { version = "2.0.12", features = ["swagger-ui"] }
+rs-snowflake = "0.6.0"
+serde = "1.0.144"
+sha2 = "0.10.6"
tokio = { version = "1", features = ["full"] }
tracing-subscriber = "0.3.15"
diff --git a/scuttlebutt/src/main.rs b/scuttlebutt/src/main.rs
@@ -1,46 +1,156 @@
-use poem::{listener::TcpListener, Route, Server, Result };
-use poem_openapi::{param::Query, payload::{PlainText, Json}, OpenApi, OpenApiService, Object, ApiResponse};
+use poem::{listener::TcpListener, Route, Server, Result, Request, web::Data};
+use poem_openapi::{param::Query, payload::{PlainText, Json}, *, auth::ApiKey};
+use hmac::{Hmac, Mac};
+use log::{warn, info};
+use jwt::{SignWithKey, VerifyWithKey};
+use serde::{Serialize, Deserialize};
+use sha2::Sha256;
struct Api;
-#[derive(Object)]
+#[derive(Object, Serialize, Deserialize)]
struct User {
- id: u64,
+ id: i64,
username: String,
+ email: String,
+}
+
+#[derive(Object)]
+struct Group {
+ id: i64,
+ name: String,
+ members: Vec<i64>,
+ channels: Vec<i64>,
+}
+
+#[derive(Object)]
+struct Channel {
+ id: i64,
+ name: String,
+ members: Vec<i64>,
}
+// type ServerKey = Hmac<Sha256>;
+
+// /// ApiKey authorization
+// #[derive(SecurityScheme)]
+// #[oai(
+// type = "api_key",
+// key_name = "X-API-Key",
+// in = "header",
+// checker = "api_checker"
+// )]
+// struct MyApiKeyAuthorization(User);
+
+// async fn api_checker(req: &Request, api_key: ApiKey) -> Option<User> {
+// let server_key = req.data::<ServerKey>().unwrap();
+// VerifyWithKey::<User>::verify_with_key(api_key.key.as_str(), server_key).ok()
+// }
+
#[derive(ApiResponse)]
enum UserResponse {
- /// Returns the user requested
+ /// Returns the user requested.
#[oai(status = 200)]
User(Json<User>),
/// Returns when there is no user associated with the ID
#[oai(status = 404)]
- NotFound(PlainText<String>)
+ NotFound,
+ /// Recieved a bad argument when specifying the user. Returns error type, such as:
+ /// - found empty string for any of the arguments
+ /// - invalid email
+ #[oai(status = 400)]
+ BadRequest,
}
#[OpenApi]
impl Api {
- #[oai(path = "/hello", method = "get")]
- async fn hi(&self) -> PlainText<String> {
- PlainText(String::from("whee"))
- }
-
#[oai(path = "/user", method = "get")]
/// Gets the user with the given ID
///
/// # Example
///
- /// Call `/user/1234` to get the user with id 1234
- async fn get_user(&self, id: Query<u64>) -> UserResponse {
+ /// Call `/user?id=1234` to get the user with id 1234
+ async fn get_user(&self, id: Query<i64>) -> UserResponse {
todo!()
}
#[oai(path = "/user", method = "post")]
- /// Makes a user
+ /// Creates a new user
async fn make_user(&self, name: Query<String>, email: Query<String>, password: Query<String>) -> Result<Json<User>> {
todo!()
}
+
+ #[oai(path = "/user", method = "put")]
+ /// Updates a user's name and email
+ async fn update_user(&self, id: Query<i64>, name: Query<String>, email: Query<String>) -> UserResponse {
+ todo!()
+ }
+
+ #[oai(path = "/user/groups", method = "get")]
+ /// Gets all groups accessible to a user
+ async fn get_groups(&self, id: Query<i64>) -> Json<Vec<Group>> {
+ todo!()
+ }
+
+ #[oai(path = "/group", method = "get")]
+ /// Gets the group with the given ID
+ async fn get_group(&self, id: Query<i64>) -> Json<Group> {
+ todo!()
+ }
+
+ #[oai(path = "/group", method = "post")]
+ /// Creates a new group
+ async fn make_group(&self, name: Query<String>) -> Json<Group> {
+ todo!()
+ }
+
+ #[oai(path = "/group", method = "put")]
+ /// Updates the name of an existing group
+ async fn update_group(&self, id: Query<i64>, name: Query<String>) -> Json<Group> {
+ todo!()
+ }
+
+ #[oai(path = "/group/members", method = "get")]
+ /// Gets the members of the specified group
+ async fn get_group_members(&self, id: Query<i64>) -> Json<Vec<User>> {
+ todo!()
+ }
+
+ #[oai(path = "/group/members", method = "put")]
+ /// Adds a member to an existing group
+ async fn add_group_member(&self, gid: Query<i64>, uid: Query<i64>) -> Json<Vec<User>> {
+ todo!()
+ }
+
+ #[oai(path = "/group/channels", method = "get")]
+ /// Gets all channels in a group that are accessible to a user
+ async fn get_channels(&self, gid: Query<i64>, uid: Query<i64>) -> Json<Vec<Channel>> {
+ todo!()
+ }
+
+ #[oai(path = "/group/channels", method = "post")]
+ /// Creates a channel in a group
+ async fn make_channel(&self, gid: Query<i64>, name: Query<String>) -> Json<Channel> {
+ todo!()
+ }
+
+ #[oai(path = "/channel", method = "put")]
+ /// Updates the name of a channel
+ async fn update_channel(&self, id: Query<i64>, name: Query<String>) -> Json<Channel> {
+ todo!()
+ }
+
+ #[oai(path = "/channel/members", method = "get")]
+ /// Gets the members that can access a channel
+ async fn get_channel_members(&self, id: Query<i64>) -> Json<Vec<User>> {
+ todo!()
+ }
+
+ #[oai(path = "/channel/members", method = "put")]
+ /// Adds a member to a channel
+ async fn add_channel_member(&self, cid: Query<i64>, uid: Query<i64>) -> Json<Channel> {
+ todo!()
+ }
}
#[tokio::main]
@@ -51,9 +161,17 @@ async fn main() -> Result<(), std::io::Error> {
tracing_subscriber::fmt::init();
let api_service =
- OpenApiService::new(Api, "Hello World", "1.0").server("http://localhost:3000/api");
+ OpenApiService::new(Api, "Scuttlebutt", "1.0")
+ .description("Scuttlebutt is the REST API for managing everything but sending/receiving messages \
+ - which means creating/updating/deleting all of your users/groups/channels.")
+ .server("http://localhost:3000/api");
+
let ui = api_service.swagger_ui();
+ // let wat = b"whee";
+ // let server_key = Hmac::<Sha256>::new_from_slice(wat).expect("valid server key");
+ // let server_key2 = Hmac::<Sha256>::new_from_hex
+ // println!("{:?}", server_key.);
Server::new(TcpListener::bind("127.0.0.1:3000"))
.run(Route::new().nest("/api", api_service).nest("/", ui))
.await
@@ -65,15 +183,15 @@ mod tests {
use poem::test::TestClient;
fn setup() -> TestClient<Route> {
- let app = OpenApiService::new(Api, "Hello World", "1.0").server("http://localhost:3000/api");
+ let app = OpenApiService::new(Api, "Scuttlebutt", "1.0").server("http://localhost:3000/api");
TestClient::new(Route::new().nest("/api", app))
}
- #[tokio::test]
- async fn sanity() {
- let cli = setup();
- let resp = cli.get("/api/hello").send().await;
- resp.assert_status_is_ok();
- resp.assert_text("whee").await;
- }
+ // #[tokio::test]
+ // async fn sanity() {
+ // let cli = setup();
+ // let resp = cli.get("/api/hello").send().await;
+ // resp.assert_status_is_ok();
+ // resp.assert_text("whee").await;
+ // }
}