2022-04-25 00:02:14 +00:00
|
|
|
use crate::{
|
2022-04-25 23:51:13 +00:00
|
|
|
auth,
|
|
|
|
db::{User, UserId},
|
|
|
|
AppState, Error, Result,
|
2022-04-25 00:02:14 +00:00
|
|
|
};
|
|
|
|
use anyhow::anyhow;
|
2022-04-24 17:08:25 +00:00
|
|
|
use axum::{
|
|
|
|
body::Body,
|
2022-04-25 23:51:13 +00:00
|
|
|
extract::{Path, Query},
|
|
|
|
http::StatusCode,
|
|
|
|
routing::{delete, get, post, put},
|
2022-04-25 00:02:14 +00:00
|
|
|
Json, Router,
|
2022-04-24 17:08:25 +00:00
|
|
|
};
|
2022-04-25 23:51:13 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
2021-12-19 16:43:13 +00:00
|
|
|
use std::sync::Arc;
|
2022-04-24 16:45:20 +00:00
|
|
|
|
2022-04-25 00:02:14 +00:00
|
|
|
pub fn add_routes(router: Router<Body>, app: Arc<AppState>) -> Router<Body> {
|
|
|
|
router
|
|
|
|
.route("/users", {
|
|
|
|
let app = app.clone();
|
2022-04-25 23:51:13 +00:00
|
|
|
get(move || get_users(app))
|
2022-04-25 00:02:14 +00:00
|
|
|
})
|
|
|
|
.route("/users", {
|
|
|
|
let app = app.clone();
|
2022-04-25 23:51:13 +00:00
|
|
|
post(move |params| create_user(params, app))
|
2022-04-25 00:02:14 +00:00
|
|
|
})
|
|
|
|
.route("/users/:id", {
|
|
|
|
let app = app.clone();
|
|
|
|
put(move |user_id, params| update_user(user_id, params, app))
|
|
|
|
})
|
2022-04-25 23:51:13 +00:00
|
|
|
.route("/users/:id", {
|
|
|
|
let app = app.clone();
|
|
|
|
delete(move |user_id| destroy_user(user_id, app))
|
|
|
|
})
|
|
|
|
.route("/users/:github_login", {
|
|
|
|
let app = app.clone();
|
|
|
|
get(move |github_login| get_user(github_login, app))
|
|
|
|
})
|
|
|
|
.route("/users/:github_login/access_tokens", {
|
|
|
|
let app = app.clone();
|
|
|
|
post(move |github_login, params| create_access_token(github_login, params, app))
|
|
|
|
})
|
2022-04-24 16:45:20 +00:00
|
|
|
}
|
|
|
|
|
2022-04-25 23:51:13 +00:00
|
|
|
async fn get_users(app: Arc<AppState>) -> Result<Json<Vec<User>>> {
|
2022-04-25 00:02:14 +00:00
|
|
|
let users = app.db.get_all_users().await?;
|
|
|
|
Ok(Json(users))
|
2022-04-24 17:08:25 +00:00
|
|
|
}
|
|
|
|
|
2022-04-25 00:02:14 +00:00
|
|
|
#[derive(Deserialize)]
|
2022-04-25 23:51:13 +00:00
|
|
|
struct CreateUserParams {
|
2022-04-25 00:02:14 +00:00
|
|
|
github_login: String,
|
|
|
|
admin: bool,
|
|
|
|
}
|
2022-04-22 19:33:19 +00:00
|
|
|
|
2022-04-25 23:51:13 +00:00
|
|
|
async fn create_user(
|
|
|
|
Json(params): Json<CreateUserParams>,
|
|
|
|
app: Arc<AppState>,
|
|
|
|
) -> Result<Json<User>> {
|
2022-04-25 00:02:14 +00:00
|
|
|
let user_id = app
|
|
|
|
.db
|
|
|
|
.create_user(¶ms.github_login, params.admin)
|
|
|
|
.await?;
|
2022-04-22 19:33:19 +00:00
|
|
|
|
2022-04-25 00:02:14 +00:00
|
|
|
let user = app
|
|
|
|
.db
|
|
|
|
.get_user_by_id(user_id)
|
|
|
|
.await?
|
|
|
|
.ok_or_else(|| anyhow!("couldn't find the user we just created"))?;
|
2022-04-22 19:33:19 +00:00
|
|
|
|
2022-04-25 00:02:14 +00:00
|
|
|
Ok(Json(user))
|
|
|
|
}
|
2022-04-22 19:33:19 +00:00
|
|
|
|
2022-04-25 00:02:14 +00:00
|
|
|
#[derive(Deserialize)]
|
2022-04-25 23:51:13 +00:00
|
|
|
struct UpdateUserParams {
|
2022-04-25 00:02:14 +00:00
|
|
|
admin: bool,
|
|
|
|
}
|
2022-04-22 19:33:19 +00:00
|
|
|
|
2022-04-25 00:02:14 +00:00
|
|
|
async fn update_user(
|
|
|
|
Path(user_id): Path<i32>,
|
2022-04-25 23:51:13 +00:00
|
|
|
Json(params): Json<UpdateUserParams>,
|
2022-04-25 00:02:14 +00:00
|
|
|
app: Arc<AppState>,
|
2022-04-25 23:51:13 +00:00
|
|
|
) -> Result<()> {
|
|
|
|
app.db
|
|
|
|
.set_user_is_admin(UserId(user_id), params.admin)
|
|
|
|
.await?;
|
2022-04-25 00:02:14 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
2022-04-22 19:33:19 +00:00
|
|
|
|
2022-04-25 23:51:13 +00:00
|
|
|
async fn destroy_user(Path(user_id): Path<i32>, app: Arc<AppState>) -> Result<()> {
|
|
|
|
app.db.destroy_user(UserId(user_id)).await?;
|
|
|
|
Ok(())
|
|
|
|
}
|
2022-04-22 19:33:19 +00:00
|
|
|
|
2022-04-25 23:51:13 +00:00
|
|
|
async fn get_user(Path(login): Path<String>, app: Arc<AppState>) -> Result<Json<User>> {
|
|
|
|
let user = app
|
|
|
|
.db
|
|
|
|
.get_user_by_github_login(&login)
|
|
|
|
.await?
|
|
|
|
.ok_or_else(|| anyhow!("user not found"))?;
|
|
|
|
Ok(Json(user))
|
|
|
|
}
|
2022-04-22 19:33:19 +00:00
|
|
|
|
2022-04-25 23:51:13 +00:00
|
|
|
#[derive(Deserialize)]
|
|
|
|
struct CreateAccessTokenQueryParams {
|
|
|
|
public_key: String,
|
|
|
|
impersonate: Option<String>,
|
|
|
|
}
|
2022-04-22 19:33:19 +00:00
|
|
|
|
2022-04-25 23:51:13 +00:00
|
|
|
#[derive(Serialize)]
|
|
|
|
struct CreateAccessTokenResponse {
|
|
|
|
user_id: UserId,
|
|
|
|
encrypted_access_token: String,
|
|
|
|
}
|
2022-04-22 19:33:19 +00:00
|
|
|
|
2022-04-25 23:51:13 +00:00
|
|
|
async fn create_access_token(
|
|
|
|
Path(login): Path<String>,
|
|
|
|
Query(params): Query<CreateAccessTokenQueryParams>,
|
|
|
|
app: Arc<AppState>,
|
|
|
|
) -> Result<Json<CreateAccessTokenResponse>> {
|
|
|
|
// request.require_token().await?;
|
2022-04-22 19:33:19 +00:00
|
|
|
|
2022-04-25 23:51:13 +00:00
|
|
|
let user = app
|
|
|
|
.db
|
|
|
|
.get_user_by_github_login(&login)
|
|
|
|
.await?
|
|
|
|
.ok_or_else(|| anyhow!("user not found"))?;
|
|
|
|
|
|
|
|
let mut user_id = user.id;
|
|
|
|
if let Some(impersonate) = params.impersonate {
|
|
|
|
if user.admin {
|
|
|
|
if let Some(impersonated_user) = app.db.get_user_by_github_login(&impersonate).await? {
|
|
|
|
user_id = impersonated_user.id;
|
|
|
|
} else {
|
|
|
|
return Err(Error::Http(
|
|
|
|
StatusCode::UNPROCESSABLE_ENTITY,
|
|
|
|
format!("user {impersonate} does not exist"),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return Err(Error::Http(
|
|
|
|
StatusCode::UNAUTHORIZED,
|
|
|
|
format!("you do not have permission to impersonate other users"),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let access_token = auth::create_access_token(app.db.as_ref(), user_id).await?;
|
|
|
|
let encrypted_access_token =
|
|
|
|
auth::encrypt_access_token(&access_token, params.public_key.clone())?;
|
|
|
|
|
|
|
|
Ok(Json(CreateAccessTokenResponse {
|
|
|
|
user_id,
|
|
|
|
encrypted_access_token,
|
|
|
|
}))
|
|
|
|
}
|
2022-04-22 19:33:19 +00:00
|
|
|
|
|
|
|
// #[async_trait]
|
|
|
|
// pub trait RequestExt {
|
|
|
|
// async fn require_token(&self) -> tide::Result<()>;
|
|
|
|
// }
|
|
|
|
|
|
|
|
// #[async_trait]
|
|
|
|
// impl RequestExt for Request {
|
|
|
|
// async fn require_token(&self) -> tide::Result<()> {
|
|
|
|
// let token = self
|
|
|
|
// .header("Authorization")
|
|
|
|
// .and_then(|header| header.get(0))
|
|
|
|
// .and_then(|header| header.as_str().strip_prefix("token "))
|
|
|
|
// .ok_or_else(|| surf::Error::from_str(403, "invalid authorization header"))?;
|
|
|
|
|
|
|
|
// if token == self.state().config.api_token {
|
|
|
|
// Ok(())
|
|
|
|
// } else {
|
|
|
|
// Err(tide::Error::from_str(403, "invalid authorization token"))
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|