mirror of
https://github.com/warp-tech/warpgate.git
synced 2025-09-06 22:55:24 +08:00
parent
cea7acc918
commit
9841421211
7 changed files with 266 additions and 4 deletions
|
@ -10,9 +10,11 @@ use sea_orm::{
|
|||
};
|
||||
use tokio::sync::Mutex;
|
||||
use uuid::Uuid;
|
||||
use warpgate_common::{Role as RoleConfig, WarpgateError};
|
||||
use warpgate_common::{
|
||||
Role as RoleConfig, Target as TargetConfig, User as UserConfig, WarpgateError,
|
||||
};
|
||||
use warpgate_core::consts::BUILTIN_ADMIN_ROLE_NAME;
|
||||
use warpgate_db_entities::Role;
|
||||
use warpgate_db_entities::{Role, Target, User};
|
||||
|
||||
use super::AnySecurityScheme;
|
||||
|
||||
|
@ -118,6 +120,22 @@ enum DeleteRoleResponse {
|
|||
NotFound,
|
||||
}
|
||||
|
||||
#[derive(ApiResponse)]
|
||||
enum GetRoleTargetsResponse {
|
||||
#[oai(status = 200)]
|
||||
Ok(Json<Vec<TargetConfig>>),
|
||||
#[oai(status = 404)]
|
||||
NotFound,
|
||||
}
|
||||
|
||||
#[derive(ApiResponse)]
|
||||
enum GetRoleUsersResponse {
|
||||
#[oai(status = 200)]
|
||||
Ok(Json<Vec<UserConfig>>),
|
||||
#[oai(status = 404)]
|
||||
NotFound,
|
||||
}
|
||||
|
||||
pub struct DetailApi;
|
||||
|
||||
#[OpenApi]
|
||||
|
@ -185,4 +203,58 @@ impl DetailApi {
|
|||
role.delete(&*db).await?;
|
||||
Ok(DeleteRoleResponse::Deleted)
|
||||
}
|
||||
|
||||
#[oai(
|
||||
path = "/role/:id/targets",
|
||||
method = "get",
|
||||
operation_id = "get_role_targets"
|
||||
)]
|
||||
async fn api_get_role_targets(
|
||||
&self,
|
||||
db: Data<&Arc<Mutex<DatabaseConnection>>>,
|
||||
id: Path<Uuid>,
|
||||
_auth: AnySecurityScheme,
|
||||
) -> Result<GetRoleTargetsResponse, WarpgateError> {
|
||||
let db = db.lock().await;
|
||||
|
||||
let Some(role) = Role::Entity::find_by_id(id.0).one(&*db).await? else {
|
||||
return Ok(GetRoleTargetsResponse::NotFound);
|
||||
};
|
||||
|
||||
let targets = role.find_related(Target::Entity).all(&*db).await?;
|
||||
|
||||
Ok(GetRoleTargetsResponse::Ok(Json(
|
||||
targets
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<Vec<_>, serde_json::Error>>()?,
|
||||
)))
|
||||
}
|
||||
|
||||
#[oai(
|
||||
path = "/role/:id/users",
|
||||
method = "get",
|
||||
operation_id = "get_role_users"
|
||||
)]
|
||||
async fn api_get_role_users(
|
||||
&self,
|
||||
db: Data<&Arc<Mutex<DatabaseConnection>>>,
|
||||
id: Path<Uuid>,
|
||||
_auth: AnySecurityScheme,
|
||||
) -> Result<GetRoleUsersResponse, WarpgateError> {
|
||||
let db = db.lock().await;
|
||||
|
||||
let Some(role) = Role::Entity::find_by_id(id.0).one(&*db).await? else {
|
||||
return Ok(GetRoleUsersResponse::NotFound);
|
||||
};
|
||||
|
||||
let users = role.find_related(User::Entity).all(&*db).await?;
|
||||
|
||||
Ok(GetRoleUsersResponse::Ok(Json(
|
||||
users
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<Vec<_>, WarpgateError>>()?,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,26 @@ pub struct Model {
|
|||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {}
|
||||
|
||||
impl Related<super::Target::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
super::TargetRoleAssignment::Relation::Target.def()
|
||||
}
|
||||
|
||||
fn via() -> Option<RelationDef> {
|
||||
Some(super::TargetRoleAssignment::Relation::Role.def().rev())
|
||||
}
|
||||
}
|
||||
|
||||
impl Related<super::User::Entity> for Entity {
|
||||
fn to() -> RelationDef {
|
||||
super::UserRoleAssignment::Relation::User.def()
|
||||
}
|
||||
|
||||
fn via() -> Option<RelationDef> {
|
||||
Some(super::UserRoleAssignment::Relation::Role.def().rev())
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
impl From<Model> for Role {
|
||||
|
|
1
warpgate-web/package-lock.json
generated
1
warpgate-web/package-lock.json
generated
|
@ -40,6 +40,7 @@
|
|||
"format-duration": "^3.0.2",
|
||||
"otpauth": "^9.3.6",
|
||||
"qrcode": "^1.5.4",
|
||||
"rxjs": "^7.8.2",
|
||||
"sass": "1.78",
|
||||
"svelte": "^5.20.0",
|
||||
"svelte-check": "^4.1.4",
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
"format-duration": "^3.0.2",
|
||||
"otpauth": "^9.3.6",
|
||||
"qrcode": "^1.5.4",
|
||||
"rxjs": "^7.8.2",
|
||||
"sass": "1.78",
|
||||
"svelte": "^5.20.0",
|
||||
"svelte-check": "^4.1.4",
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
<script lang="ts">
|
||||
import { api, type Role } from 'admin/lib/api'
|
||||
import { api, type Role, type Target, type User } from 'admin/lib/api'
|
||||
import AsyncButton from 'common/AsyncButton.svelte'
|
||||
import { replace } from 'svelte-spa-router'
|
||||
import { link, replace } from 'svelte-spa-router'
|
||||
import { FormGroup, Input } from '@sveltestrap/sveltestrap'
|
||||
import { stringifyError } from 'common/errors'
|
||||
import Alert from 'common/sveltestrap-s5-ports/Alert.svelte'
|
||||
import Loadable from 'common/Loadable.svelte'
|
||||
import ItemList, { type PaginatedResponse } from 'common/ItemList.svelte';
|
||||
import * as rx from 'rxjs'
|
||||
import EmptyState from 'common/EmptyState.svelte';
|
||||
|
||||
interface Props {
|
||||
params: { id: string };
|
||||
|
@ -24,6 +27,30 @@
|
|||
disabled = role.name === 'warpgate:admin'
|
||||
}
|
||||
|
||||
function loadUsers (): rx.Observable<PaginatedResponse<User>> {
|
||||
return rx.from(api.getRoleUsers({
|
||||
id: params.id,
|
||||
})).pipe(
|
||||
rx.map(targets => ({
|
||||
items: targets,
|
||||
offset: 0,
|
||||
total: targets.length,
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
function loadTargets (): rx.Observable<PaginatedResponse<Target>> {
|
||||
return rx.from(api.getRoleTargets({
|
||||
id: params.id,
|
||||
})).pipe(
|
||||
rx.map(targets => ({
|
||||
items: targets,
|
||||
offset: 0,
|
||||
total: targets.length,
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
async function update () {
|
||||
try {
|
||||
role = await api.updateRole({
|
||||
|
@ -85,3 +112,50 @@
|
|||
click={remove}
|
||||
>Remove</AsyncButton>
|
||||
</div>
|
||||
|
||||
|
||||
<h4 class="mt-4">Assigned users</h4>
|
||||
|
||||
<ItemList load={loadUsers}>
|
||||
{#snippet item(user)}
|
||||
<a
|
||||
class="list-group-item list-group-item-action"
|
||||
href="/users/{user.id}"
|
||||
use:link>
|
||||
<div>
|
||||
<strong class="me-auto">
|
||||
{user.username}
|
||||
</strong>
|
||||
{#if user.description}
|
||||
<small class="d-block text-muted">{user.description}</small>
|
||||
{/if}
|
||||
</div>
|
||||
</a>
|
||||
{/snippet}
|
||||
{#snippet empty()}
|
||||
<Alert color="info">This role has no users assigned to it</Alert>
|
||||
{/snippet}
|
||||
</ItemList>
|
||||
|
||||
<h4 class="mt-4">Assigned targets</h4>
|
||||
|
||||
<ItemList load={loadTargets}>
|
||||
{#snippet item(target)}
|
||||
<a
|
||||
class="list-group-item list-group-item-action"
|
||||
href="/targets/{target.id}"
|
||||
use:link>
|
||||
<div class="me-auto">
|
||||
<strong>
|
||||
{target.name}
|
||||
</strong>
|
||||
{#if target.description}
|
||||
<small class="d-block text-muted">{target.description}</small>
|
||||
{/if}
|
||||
</div>
|
||||
</a>
|
||||
{/snippet}
|
||||
{#snippet empty()}
|
||||
<Alert color="info">This role has no targets assigned to it</Alert>
|
||||
{/snippet}
|
||||
</ItemList>
|
||||
|
|
|
@ -464,6 +464,94 @@
|
|||
"operationId": "delete_role"
|
||||
}
|
||||
},
|
||||
"/role/{id}/targets": {
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"deprecated": false,
|
||||
"explode": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json; charset=utf-8": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Target"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"TokenSecurityScheme": []
|
||||
},
|
||||
{
|
||||
"CookieSecurityScheme": []
|
||||
}
|
||||
],
|
||||
"operationId": "get_role_targets"
|
||||
}
|
||||
},
|
||||
"/role/{id}/users": {
|
||||
"get": {
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid"
|
||||
},
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"deprecated": false,
|
||||
"explode": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json; charset=utf-8": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/User"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"TokenSecurityScheme": []
|
||||
},
|
||||
{
|
||||
"CookieSecurityScheme": []
|
||||
}
|
||||
],
|
||||
"operationId": "get_role_users"
|
||||
}
|
||||
},
|
||||
"/tickets": {
|
||||
"get": {
|
||||
"responses": {
|
||||
|
|
|
@ -128,3 +128,9 @@
|
|||
<Pagination total={_total} bind:page={page} pageSize={pageSize} />
|
||||
{/if}
|
||||
{/await}
|
||||
|
||||
<style lang="scss">
|
||||
.list-group:empty {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Add table
Reference in a new issue