migration changes
This commit is contained in:
parent
30432e216b
commit
4f50aa1362
|
@ -4,3 +4,4 @@ pub mod totp_authenticators;
|
||||||
pub mod verify_totp_authenticators;
|
pub mod verify_totp_authenticators;
|
||||||
pub mod networks;
|
pub mod networks;
|
||||||
pub mod organization;
|
pub mod organization;
|
||||||
|
pub mod roles;
|
|
@ -0,0 +1,165 @@
|
||||||
|
// trifid-api, an open source reimplementation of the Defined Networking nebula management server.
|
||||||
|
// Copyright (C) 2023 c0repwn3r
|
||||||
|
//
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
//#POST /v1/roles t+parity:full t+type:documented t+status:in_progress
|
||||||
|
// This endpoint has full parity with the original API. It has been recreated from the original API documentation.
|
||||||
|
// This endpoint is in-progress and should not be expected to work, sometimes at all.
|
||||||
|
|
||||||
|
use actix_web::{HttpRequest, HttpResponse, post};
|
||||||
|
use actix_web::web::{Data, Json};
|
||||||
|
use log::error;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use crate::AppState;
|
||||||
|
use crate::auth_tokens::{enforce_2fa, enforce_api_token, TokenInfo};
|
||||||
|
use crate::error::{APIError, APIErrorsResponse};
|
||||||
|
use trifid_api_entities::entity::organization;
|
||||||
|
use sea_orm::{EntityTrait, QueryFilter, ColumnTrait};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct CreateRoleRequest {
|
||||||
|
pub name: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub description: String,
|
||||||
|
#[serde(default, rename = "firewallRules")]
|
||||||
|
pub firewall_rules: Vec<RoleFirewallRule>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct RoleFirewallRule {
|
||||||
|
pub protocol: RoleProtocol,
|
||||||
|
#[serde(default)]
|
||||||
|
pub description: String,
|
||||||
|
#[serde(rename = "allowedRoleID")]
|
||||||
|
pub allowed_role_id: Option<String>, // Option is intentional here to prevent having to convert it anyway for SeaORM's types
|
||||||
|
#[serde(rename = "portRange")]
|
||||||
|
pub port_range: Option<RolePortRange>, // Option is intentional here, because we handle the null case in a way other than just "default 0"
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub enum RoleProtocol {
|
||||||
|
#[serde(rename = "ANY")]
|
||||||
|
Any,
|
||||||
|
#[serde(rename = "TCP")]
|
||||||
|
Tcp,
|
||||||
|
#[serde(rename = "UDP")]
|
||||||
|
Udp,
|
||||||
|
#[serde(rename = "ICMP")]
|
||||||
|
Icmp
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct RolePortRange {
|
||||||
|
pub from: u16,
|
||||||
|
pub to: u16
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct RoleCreateResponse {
|
||||||
|
pub data: RoleCreateResponseData,
|
||||||
|
pub metadata: RoleCreateResponseMetadata
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct RoleCreateResponseData {
|
||||||
|
pub id: Option<String>,
|
||||||
|
pub name: Option<String>,
|
||||||
|
pub description: Option<String>,
|
||||||
|
#[serde(rename = "firewallRules")]
|
||||||
|
pub firewall_rules: Vec<RoleFirewallRule>,
|
||||||
|
#[serde(rename = "createdAt")]
|
||||||
|
pub created_at: String,
|
||||||
|
#[serde(rename = "modifiedAt")]
|
||||||
|
pub modified_at: String
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct RoleCreateResponseMetadata {}
|
||||||
|
|
||||||
|
#[post("/v1/roles")]
|
||||||
|
pub async fn create_role_request(req: Json<CreateRoleRequest>, req_info: HttpRequest, db: Data<AppState>) -> HttpResponse {
|
||||||
|
// For this endpoint, you either need to be a fully authenticated user OR a token with roles:create
|
||||||
|
let session_info = enforce_2fa(&req_info, &db.conn).await.unwrap_or(TokenInfo::NotPresent);
|
||||||
|
let api_token_info = enforce_api_token(&req_info, &["roles:create"], &db.conn).await.unwrap_or(TokenInfo::NotPresent);
|
||||||
|
|
||||||
|
// If neither are present, throw an error
|
||||||
|
if matches!(session_info, TokenInfo::NotPresent) && matches!(api_token_info, TokenInfo::NotPresent) {
|
||||||
|
return HttpResponse::Unauthorized().json(APIErrorsResponse {
|
||||||
|
errors: vec![
|
||||||
|
APIError {
|
||||||
|
code: "ERR_UNAUTHORIZED".to_string(),
|
||||||
|
message: "This endpoint requires either a fully authenticated user or a token with the roles:create scope".to_string(),
|
||||||
|
path: None,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// If both are present, throw an error
|
||||||
|
if matches!(session_info, TokenInfo::AuthToken(_)) && matches!(api_token_info, TokenInfo::ApiToken(_)) {
|
||||||
|
return HttpResponse::BadRequest().json(APIErrorsResponse {
|
||||||
|
errors: vec![
|
||||||
|
APIError {
|
||||||
|
code: "ERR_AMBIGUOUS_AUTHENTICATION".to_string(),
|
||||||
|
message: "Both a user token and an API token with the proper scope was provided. Please only provide one.".to_string(),
|
||||||
|
path: None
|
||||||
|
}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
let org = match api_token_info {
|
||||||
|
TokenInfo::ApiToken(tkn) => tkn.organization,
|
||||||
|
_ => {
|
||||||
|
// we have a session token, which means we have to do a db request to get the organization that this user owns
|
||||||
|
let user = match session_info {
|
||||||
|
TokenInfo::AuthToken(tkn) => tkn.session_info.user,
|
||||||
|
_ => unreachable!()
|
||||||
|
};
|
||||||
|
|
||||||
|
let org = match organization::Entity::find().filter(organization::Column::Owner.eq(user.id)).one(&db.conn).await {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => {
|
||||||
|
error!("database error: {}", e);
|
||||||
|
return HttpResponse::InternalServerError().json(APIErrorsResponse {
|
||||||
|
errors: vec![
|
||||||
|
APIError {
|
||||||
|
code: "ERR_DB_ERROR".to_string(),
|
||||||
|
message: "There was an error performing the database request, please try again later.".to_string(),
|
||||||
|
path: None,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(org) = org {
|
||||||
|
org.id
|
||||||
|
} else {
|
||||||
|
return HttpResponse::Unauthorized().json(APIErrorsResponse {
|
||||||
|
errors: vec![
|
||||||
|
APIError {
|
||||||
|
code: "ERR_NO_ORG".to_string(),
|
||||||
|
message: "This user does not own any organizations. Try using an API token instead.".to_string(),
|
||||||
|
path: None
|
||||||
|
}
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
HttpResponse::Ok().finish()
|
||||||
|
}
|
|
@ -7,9 +7,12 @@ use sea_orm::entity::prelude::*;
|
||||||
pub struct Model {
|
pub struct Model {
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
pub id: String,
|
pub id: String,
|
||||||
|
#[sea_orm(unique)]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
pub organization: String,
|
pub organization: String,
|
||||||
|
pub created_at: i64,
|
||||||
|
pub modified_at: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||||
|
|
|
@ -11,9 +11,11 @@ impl MigrationTrait for Migration {
|
||||||
Table::create()
|
Table::create()
|
||||||
.table(Role::Table)
|
.table(Role::Table)
|
||||||
.col(ColumnDef::new(Role::Id).string().not_null().primary_key())
|
.col(ColumnDef::new(Role::Id).string().not_null().primary_key())
|
||||||
.col(ColumnDef::new(Role::Name).string().not_null())
|
.col(ColumnDef::new(Role::Name).string().not_null().unique_key())
|
||||||
.col(ColumnDef::new(Role::Description).string().not_null())
|
.col(ColumnDef::new(Role::Description).string().not_null())
|
||||||
.col(ColumnDef::new(Role::Organization).string().not_null())
|
.col(ColumnDef::new(Role::Organization).string().not_null())
|
||||||
|
.col(ColumnDef::new(Role::CreatedAt).big_integer().not_null())
|
||||||
|
.col(ColumnDef::new(Role::ModifiedAt).big_integer().not_null())
|
||||||
.foreign_key(
|
.foreign_key(
|
||||||
ForeignKey::create()
|
ForeignKey::create()
|
||||||
.from(Role::Table, Role::Organization)
|
.from(Role::Table, Role::Organization)
|
||||||
|
@ -36,5 +38,7 @@ pub enum Role {
|
||||||
Id,
|
Id,
|
||||||
Name,
|
Name,
|
||||||
Description,
|
Description,
|
||||||
Organization
|
Organization,
|
||||||
|
CreatedAt,
|
||||||
|
ModifiedAt
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue