change zone member management
This commit is contained in:
parent
2a3354456a
commit
76f222e1c4
4 changed files with 111 additions and 59 deletions
|
@ -26,7 +26,8 @@ async fn rocket() -> rocket::Rocket {
|
|||
.attach(DbConn::fairing())
|
||||
.mount("/api/v1", routes![
|
||||
get_zone_records,
|
||||
create_user_zone,
|
||||
get_zones,
|
||||
add_member_to_zone,
|
||||
create_auth_token,
|
||||
create_user
|
||||
])
|
||||
|
|
|
@ -21,7 +21,6 @@ use crate::schema::*;
|
|||
use crate::DbConn;
|
||||
use crate::config::Config;
|
||||
use crate::models::errors::{ErrorResponse, make_500};
|
||||
use crate::models::dns::AbsoluteName;
|
||||
|
||||
|
||||
const BEARER: &str = "Bearer ";
|
||||
|
@ -38,7 +37,6 @@ pub enum Role {
|
|||
}
|
||||
|
||||
// TODO: Store Uuid instead of string??
|
||||
// TODO: Store role as Role and not String.
|
||||
#[derive(Debug, Queryable, Identifiable, Insertable)]
|
||||
#[table_name = "user"]
|
||||
pub struct User {
|
||||
|
@ -63,9 +61,10 @@ pub struct UserZone {
|
|||
pub zone_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Queryable, Identifiable, Insertable)]
|
||||
#[derive(Debug, Serialize, Queryable, Identifiable, Insertable)]
|
||||
#[table_name = "zone"]
|
||||
pub struct Zone {
|
||||
#[serde(skip)]
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
@ -79,8 +78,8 @@ pub struct CreateUserRequest {
|
|||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CreateUserZoneRequest {
|
||||
pub zone: AbsoluteName,
|
||||
pub struct AddZoneMemberRequest {
|
||||
pub id: String,
|
||||
}
|
||||
|
||||
// pub struct LdapUserAssociation {
|
||||
|
@ -145,36 +144,16 @@ impl UserInfo {
|
|||
Ok(res_zone)
|
||||
}
|
||||
|
||||
pub fn add_zone(&self, conn: &diesel::SqliteConnection, request: CreateUserZoneRequest) -> Result<Zone, UserError> {
|
||||
pub fn get_zones(&self, conn: &diesel::SqliteConnection) -> Result<Vec<Zone>, UserError> {
|
||||
use crate::schema::user_zone::dsl::*;
|
||||
use crate::schema::zone::dsl::*;
|
||||
|
||||
let zone_name = request.zone.to_utf8();
|
||||
let current_zone: Zone = zone.filter(name.eq(zone_name))
|
||||
.get_result(conn)
|
||||
.map_err(|e| match e {
|
||||
DieselError::NotFound => UserError::ZoneNotFound,
|
||||
other => UserError::DbError(other)
|
||||
})?;
|
||||
let res: Vec<(Zone, UserZone)> = zone.inner_join(user_zone)
|
||||
.filter(user_id.eq(&self.id))
|
||||
.get_results(conn)
|
||||
.map_err(UserError::DbError)?;
|
||||
|
||||
let new_user_zone = UserZone {
|
||||
zone_id: current_zone.id.clone(),
|
||||
user_id: self.id.clone()
|
||||
};
|
||||
|
||||
let res = diesel::insert_into(user_zone)
|
||||
.values(new_user_zone)
|
||||
.execute(conn);
|
||||
|
||||
match res {
|
||||
// If user has already access to the zone, safely ignore the conflit
|
||||
// TODO: use 'on conflict do nothing' in postgres when we get there
|
||||
Err(DieselError::DatabaseError(diesel::result::DatabaseErrorKind::UniqueViolation, _)) => (),
|
||||
Err(e) => return Err(e.into()),
|
||||
Ok(_) => ()
|
||||
};
|
||||
|
||||
Ok(current_zone)
|
||||
Ok(res.into_iter().map(|(z, _)| z).collect())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -211,8 +190,8 @@ impl<'r> FromRequest<'r> for UserInfo {
|
|||
|
||||
let user_id = token_data.sub;
|
||||
|
||||
conn.run(|c| {
|
||||
match LocalUser::get_user_by_uuid(c, user_id) {
|
||||
conn.run(move |c| {
|
||||
match LocalUser::get_user_by_uuid(c, &user_id) {
|
||||
Err(UserError::NotFound) => ErrorResponse::from(UserError::NotFound).into(),
|
||||
Err(e) => ErrorResponse::from(e).into(),
|
||||
Ok(d) => Outcome::Success(d),
|
||||
|
@ -318,7 +297,7 @@ impl LocalUser {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn get_user_by_uuid(conn: &diesel::SqliteConnection, request_user_id: String) -> Result<UserInfo, UserError> {
|
||||
pub fn get_user_by_uuid(conn: &diesel::SqliteConnection, request_user_id: &str) -> Result<UserInfo, UserError> {
|
||||
use crate::schema::localuser::dsl::*;
|
||||
use crate::schema::user::dsl::*;
|
||||
|
||||
|
@ -365,3 +344,47 @@ impl AuthClaims {
|
|||
encode(&Header::default(), &self, &EncodingKey::from_secret(secret.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Should probably not be implemented here
|
||||
// also, "UserError" seems like a misleading name
|
||||
impl Zone {
|
||||
pub fn get_all(conn: &diesel::SqliteConnection) -> Result<Vec<Zone>, UserError> {
|
||||
use crate::schema::zone::dsl::*;
|
||||
|
||||
zone.get_results(conn)
|
||||
.map_err(UserError::DbError)
|
||||
}
|
||||
|
||||
pub fn get_by_name(conn: &diesel::SqliteConnection, zone_name: &str) -> Result<Zone, UserError> {
|
||||
use crate::schema::zone::dsl::*;
|
||||
|
||||
zone.filter(name.eq(zone_name))
|
||||
.get_result(conn)
|
||||
.map_err(|e| match e {
|
||||
DieselError::NotFound => UserError::ZoneNotFound,
|
||||
other => UserError::DbError(other)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn add_member(&self, conn: &diesel::SqliteConnection, new_member: &UserInfo) -> Result<(), UserError> {
|
||||
use crate::schema::user_zone::dsl::*;
|
||||
|
||||
let new_user_zone = UserZone {
|
||||
zone_id: self.id.clone(),
|
||||
user_id: new_member.id.clone()
|
||||
};
|
||||
|
||||
let res = diesel::insert_into(user_zone)
|
||||
.values(new_user_zone)
|
||||
.execute(conn);
|
||||
|
||||
match res {
|
||||
// If user has already access to the zone, safely ignore the conflit
|
||||
// TODO: use 'on conflict do nothing' in postgres when we get there
|
||||
Err(DieselError::DatabaseError(diesel::result::DatabaseErrorKind::UniqueViolation, _)) => (),
|
||||
Err(e) => return Err(e.into()),
|
||||
Ok(_) => ()
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,8 @@ use crate::config::Config;
|
|||
use crate::DbConn;
|
||||
use crate::models::errors::{ErrorResponse, make_500};
|
||||
use crate::models::users::{
|
||||
UserInfo,
|
||||
LocalUser,
|
||||
CreateUserRequest,
|
||||
CreateUserZoneRequest,
|
||||
AuthClaims,
|
||||
AuthTokenRequest,
|
||||
AuthTokenResponse
|
||||
|
@ -45,22 +43,3 @@ pub async fn create_user<'r>(conn: DbConn, user_request: Json<CreateUserRequest>
|
|||
.status(Status::Created)
|
||||
.ok()
|
||||
}
|
||||
|
||||
#[post("/users/<user_id>/zones", data = "<user_zone_request>")]
|
||||
pub async fn create_user_zone<'r>(
|
||||
conn: DbConn,
|
||||
user_info: Result<UserInfo, ErrorResponse>,
|
||||
user_id: String,
|
||||
user_zone_request: Json<CreateUserZoneRequest>
|
||||
) -> Result<Response<'r>, ErrorResponse>{
|
||||
user_info?.check_admin()?;
|
||||
|
||||
conn.run(move |c| {
|
||||
let user_info = LocalUser::get_user_by_uuid(&c, user_id)?;
|
||||
user_info.add_zone(&c, user_zone_request.into_inner())
|
||||
}).await?;
|
||||
|
||||
Response::build()
|
||||
.status(Status::Created)
|
||||
.ok()
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use rocket::Response;
|
||||
use rocket::http::Status;
|
||||
|
||||
use rocket_contrib::json::Json;
|
||||
|
@ -8,7 +9,7 @@ use trust_dns_client::rr::{DNSClass, RecordType};
|
|||
|
||||
use crate::{DbConn, models::dns};
|
||||
use crate::models::errors::{ErrorResponse, make_500};
|
||||
use crate::models::users::UserInfo;
|
||||
use crate::models::users::{LocalUser, UserInfo, Zone, AddZoneMemberRequest};
|
||||
|
||||
|
||||
#[get("/zones/<zone>/records")]
|
||||
|
@ -24,7 +25,7 @@ pub async fn get_zone_records(
|
|||
if !user_info.is_admin() {
|
||||
let zone_name = zone.clone().to_string();
|
||||
conn.run(move |c| {
|
||||
dbg!(user_info.get_zone(c, &zone_name))
|
||||
user_info.get_zone(c, &zone_name)
|
||||
}).await?;
|
||||
}
|
||||
|
||||
|
@ -33,10 +34,12 @@ pub async fn get_zone_records(
|
|||
query.await.map_err(make_500)?
|
||||
};
|
||||
|
||||
// TODO: Better error handling (ex. not authorized should be 500)
|
||||
if response.response_code() != ResponseCode::NoError {
|
||||
println!("Querrying of zone {} failed with code {}", *zone, response.response_code());
|
||||
return ErrorResponse::new(
|
||||
Status::NotFound,
|
||||
format!("zone {} could not be found", *zone)
|
||||
format!("Zone {} could not be found", *zone)
|
||||
).err()
|
||||
}
|
||||
|
||||
|
@ -51,3 +54,49 @@ pub async fn get_zone_records(
|
|||
|
||||
Ok(Json(records))
|
||||
}
|
||||
|
||||
// TODO: the post version of that
|
||||
#[get("/zones")]
|
||||
pub async fn get_zones(
|
||||
conn: DbConn,
|
||||
user_info: Result<UserInfo, ErrorResponse>,
|
||||
) -> Result<Json<Vec<Zone>>, ErrorResponse> {
|
||||
let user_info = user_info?;
|
||||
|
||||
let zones = conn.run(move |c| {
|
||||
if user_info.is_admin() {
|
||||
Zone::get_all(c)
|
||||
} else {
|
||||
user_info.get_zones(c)
|
||||
}
|
||||
}).await?;
|
||||
|
||||
Ok(Json(zones))
|
||||
}
|
||||
|
||||
|
||||
#[post("/zones/<zone>/members", data = "<zone_member_request>")]
|
||||
pub async fn add_member_to_zone<'r>(
|
||||
conn: DbConn,
|
||||
zone: dns::AbsoluteName,
|
||||
user_info: Result<UserInfo, ErrorResponse>,
|
||||
zone_member_request: Json<AddZoneMemberRequest>
|
||||
) -> Result<Response<'r>, ErrorResponse> {
|
||||
let user_info = user_info?;
|
||||
let zone_name = zone.to_utf8();
|
||||
|
||||
conn.run(move |c| {
|
||||
let zone = if user_info.is_admin() {
|
||||
Zone::get_by_name(c, &zone_name)
|
||||
} else {
|
||||
user_info.get_zone(c, &zone_name)
|
||||
}?;
|
||||
|
||||
let new_member = LocalUser::get_user_by_uuid(c, &zone_member_request.id)?;
|
||||
zone.add_member(&c, &new_member)
|
||||
}).await?;
|
||||
|
||||
Response::build()
|
||||
.status(Status::Created) // TODO: change this?
|
||||
.ok()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue