yes
This commit is contained in:
parent
7abcdcf8f1
commit
548d874221
@ -92,56 +92,32 @@ export const routes: Routes = [
|
|||||||
tenant: tenantResolver,
|
tenant: tenantResolver,
|
||||||
title: tenantNameResolver,
|
title: tenantNameResolver,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Subject TODO',
|
||||||
|
path: 'subject/:subjectId',
|
||||||
|
component: HomeComponent
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: ':id',
|
||||||
|
resolve: {
|
||||||
|
title: tenantNameResolver,
|
||||||
|
},
|
||||||
children: [
|
children: [
|
||||||
{
|
|
||||||
title: 'Subject TODO',
|
|
||||||
path: 'subject/:subjectId',
|
|
||||||
component: HomeComponent
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: 'Apps',
|
title: 'Apps',
|
||||||
path: 'apps',
|
path: 'app',
|
||||||
component: HomeComponent,
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
title: 'App TODO',
|
title: 'App TODO',
|
||||||
path: ':id',
|
path: ':appId',
|
||||||
component: HomeComponent,
|
component: HomeComponent
|
||||||
children: [
|
|
||||||
{
|
|
||||||
title: 'Groups',
|
|
||||||
path: 'groups',
|
|
||||||
component: HomeComponent,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Roles',
|
|
||||||
path: 'roles',
|
|
||||||
component: HomeComponent,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Authorities',
|
|
||||||
path: 'authorities',
|
|
||||||
component: HomeComponent,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
},
|
data: {
|
||||||
{
|
breadcrumbUrl: '?tab=apps'
|
||||||
title: 'Groups',
|
}
|
||||||
path: 'groups',
|
}
|
||||||
component: HomeComponent,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Roles',
|
|
||||||
path: 'roles',
|
|
||||||
component: HomeComponent,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Authorities',
|
|
||||||
path: 'authorities',
|
|
||||||
component: HomeComponent,
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@ -0,0 +1,12 @@
|
|||||||
|
import {Identifiable} from '../../../../components/list/list.component';
|
||||||
|
|
||||||
|
export interface AuthorityDto extends Identifiable {
|
||||||
|
id: string;
|
||||||
|
tenantId: string;
|
||||||
|
name: string;
|
||||||
|
categoryPath: string;
|
||||||
|
description: string;
|
||||||
|
type: string;
|
||||||
|
visibility: string;
|
||||||
|
appId?: string;
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
import {AuthorityDto} from './authority-dto';
|
||||||
|
|
||||||
|
export interface GroupDto extends AuthorityDto {}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
import {AuthorityDto} from './authority-dto';
|
||||||
|
|
||||||
|
export interface RoleDto extends AuthorityDto {}
|
||||||
@ -3,6 +3,9 @@ import {lastValueFrom, Observable, of} from "rxjs";
|
|||||||
import {PalantirService} from '../palantir.service';
|
import {PalantirService} from '../palantir.service';
|
||||||
import {AppListDto} from '../dtos/app-list-dto';
|
import {AppListDto} from '../dtos/app-list-dto';
|
||||||
import {TenantGridViewDto} from '../dtos/tenant-grid-view-dto';
|
import {TenantGridViewDto} from '../dtos/tenant-grid-view-dto';
|
||||||
|
import {AuthorityDto} from '../dtos/authority-dto';
|
||||||
|
import {RoleDto} from '../dtos/role-dto';
|
||||||
|
import {GroupDto} from '../dtos/group-dto';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -39,4 +42,28 @@ export class TenantService extends PalantirService {
|
|||||||
getTenantAppsAsync(id: string, onError?: (error: any) => void): Promise<AppListDto[]> {
|
getTenantAppsAsync(id: string, onError?: (error: any) => void): Promise<AppListDto[]> {
|
||||||
return lastValueFrom(this.getTenantApps$(id, onError));
|
return lastValueFrom(this.getTenantApps$(id, onError));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTenantAuthorities$(id: string, onError?: (error: any) => void): Observable<AuthorityDto[]> {
|
||||||
|
return this.handleRequest(this.http.get<AuthorityDto[]>(this.baseUrl + `/${id}/authorities`), onError);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTenantAuthoritiesAsync(id: string, onError?: (error: any) => void): Promise<AuthorityDto[]> {
|
||||||
|
return lastValueFrom(this.getTenantAuthorities$(id, onError));
|
||||||
|
}
|
||||||
|
|
||||||
|
getTenantRoles$(id: string, onError?: (error: any) => void): Observable<RoleDto[]> {
|
||||||
|
return this.handleRequest(this.http.get<RoleDto[]>(this.baseUrl + `/${id}/roles`), onError);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTenantRolesAsync(id: string, onError?: (error: any) => void): Promise<RoleDto[]> {
|
||||||
|
return lastValueFrom(this.getTenantRoles$(id, onError));
|
||||||
|
}
|
||||||
|
|
||||||
|
getTenantGroups$(id: string, onError?: (error: any) => void): Observable<GroupDto[]> {
|
||||||
|
return this.handleRequest(this.http.get<GroupDto[]>(this.baseUrl + `/${id}/groups`), onError);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTenantGroupsAsync(id: string, onError?: (error: any) => void): Promise<GroupDto[]> {
|
||||||
|
return lastValueFrom(this.getTenantGroups$(id, onError));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import {Component, inject, input} from '@angular/core';
|
import {Component, inject, input} from '@angular/core';
|
||||||
import {Router} from '@angular/router';
|
import {ActivatedRoute, Router} from '@angular/router';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-link',
|
selector: 'app-link',
|
||||||
@ -13,11 +13,39 @@ export class LinkComponent {
|
|||||||
public external = input<boolean>(false);
|
public external = input<boolean>(false);
|
||||||
|
|
||||||
private router = inject(Router);
|
private router = inject(Router);
|
||||||
|
private route = inject(ActivatedRoute);
|
||||||
|
|
||||||
click($event: PointerEvent) {
|
click($event: PointerEvent) {
|
||||||
|
|
||||||
if (!this.external()) {
|
if (!this.external()) {
|
||||||
$event.preventDefault();
|
$event.preventDefault();
|
||||||
this.router.navigate([this.href()]).then();
|
|
||||||
|
const link = this.href();
|
||||||
|
const split = link.split('?');
|
||||||
|
const path = split[0]
|
||||||
|
const queryParamsStrings = split[1]?.split('&');
|
||||||
|
|
||||||
|
const queryParams: {[key: string]: string} = {};
|
||||||
|
|
||||||
|
if (queryParamsStrings) {
|
||||||
|
for (const queryParamString of queryParamsStrings) {
|
||||||
|
const splitQueryParam = queryParamString.split('=');
|
||||||
|
const key = splitQueryParam[0] as string;
|
||||||
|
const value = splitQueryParam[1];
|
||||||
|
|
||||||
|
if (key && value) {
|
||||||
|
queryParams[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.router.navigate([path], {
|
||||||
|
relativeTo: this.route,
|
||||||
|
queryParams: queryParams,
|
||||||
|
queryParamsHandling: 'merge',
|
||||||
|
skipLocationChange: false,
|
||||||
|
replaceUrl: false
|
||||||
|
}).then();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,13 @@
|
|||||||
|
@if (loading()) {
|
||||||
|
Loading...
|
||||||
|
} @else {
|
||||||
|
|
||||||
|
@if (items().length === 0) {
|
||||||
|
<span>List is empty :(</span>
|
||||||
|
}
|
||||||
|
@else {
|
||||||
|
@for (item of items(); track item.id) {
|
||||||
|
<ng-container *ngTemplateOutlet="itemTemplate(); context: { $implicit: item, metadata: metadata() }"></ng-container>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
|
import { ListComponent } from './list.component';
|
||||||
|
|
||||||
|
describe('ListComponent', () => {
|
||||||
|
let component: ListComponent;
|
||||||
|
let fixture: ComponentFixture<ListComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
imports: [ListComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(ListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import {Component, input, TemplateRef} from '@angular/core';
|
||||||
|
import {PanelComponent} from '../panel/panel.component';
|
||||||
|
import {NgTemplateOutlet} from '@angular/common';
|
||||||
|
|
||||||
|
export interface Identifiable {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-list',
|
||||||
|
imports: [
|
||||||
|
PanelComponent,
|
||||||
|
NgTemplateOutlet
|
||||||
|
],
|
||||||
|
templateUrl: './list.component.html',
|
||||||
|
styleUrl: './list.component.scss',
|
||||||
|
})
|
||||||
|
export class ListComponent {
|
||||||
|
|
||||||
|
public items = input.required<Identifiable[]>();
|
||||||
|
public itemTemplate = input.required<TemplateRef<any>>();
|
||||||
|
public loading = input<boolean>(false);
|
||||||
|
public metadata = input<any>();
|
||||||
|
|
||||||
|
}
|
||||||
@ -21,7 +21,7 @@ export class LoginComponent {
|
|||||||
|
|
||||||
protected loginFormGroup = new FormGroup({
|
protected loginFormGroup = new FormGroup({
|
||||||
usernameOrEmail: new FormControl('housemaster'),
|
usernameOrEmail: new FormControl('housemaster'),
|
||||||
password: new FormControl('kR0pNCspBKx8lOzAIch5'),
|
password: new FormControl('W33PWO6IIRpkc6VSBIY0'),
|
||||||
keepSignedIn: new FormControl(false),
|
keepSignedIn: new FormControl(false),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import {Component, computed, OnInit, signal} from '@angular/core';
|
import {Component, computed, OnInit, signal} from '@angular/core';
|
||||||
import {NavProfileComponent} from './nav-profile/nav-profile.component';
|
import {NavProfileComponent} from './nav-profile/nav-profile.component';
|
||||||
import {ActivatedRoute, NavigationEnd, Route, Router, RouterLink} from '@angular/router';
|
import {ActivatedRoute, NavigationEnd, Route, Router} from '@angular/router';
|
||||||
import {IconComponent} from '../icon/icon.component';
|
import {IconComponent} from '../icon/icon.component';
|
||||||
import {DrawerComponent} from './drawer/drawer.component';
|
import {DrawerComponent} from './drawer/drawer.component';
|
||||||
import {filter} from 'rxjs';
|
import {filter} from 'rxjs';
|
||||||
@ -13,7 +13,6 @@ import {LinkComponent} from '../link/link.component';
|
|||||||
NavProfileComponent,
|
NavProfileComponent,
|
||||||
IconComponent,
|
IconComponent,
|
||||||
DrawerComponent,
|
DrawerComponent,
|
||||||
RouterLink,
|
|
||||||
NgClass,
|
NgClass,
|
||||||
LinkComponent
|
LinkComponent
|
||||||
],
|
],
|
||||||
@ -86,14 +85,27 @@ export class ShellComponent implements OnInit {
|
|||||||
const child = children[0];
|
const child = children[0];
|
||||||
|
|
||||||
const routeURL = child.snapshot.url.map(segment => segment.path).join('/');
|
const routeURL = child.snapshot.url.map(segment => segment.path).join('/');
|
||||||
if (routeURL) {
|
const overrideUrl = child.snapshot.data['breadcrumbUrl'];
|
||||||
|
|
||||||
|
if (overrideUrl) {
|
||||||
|
|
||||||
|
if (overrideUrl.startsWith('?')) {
|
||||||
|
url += overrideUrl;
|
||||||
|
} else {
|
||||||
|
url += !url.endsWith('/') ? `/${overrideUrl}` : `${overrideUrl}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (routeURL) {
|
||||||
url += !url.endsWith('/') ? `/${routeURL}` : `${routeURL}`;
|
url += !url.endsWith('/') ? `/${routeURL}` : `${routeURL}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const label =
|
const label =
|
||||||
child.snapshot.data['title'] ||
|
child.snapshot.routeConfig?.title
|
||||||
child.snapshot.routeConfig?.title ||
|
|| child.snapshot.data['title']
|
||||||
'';
|
|| '';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (label) {
|
if (label) {
|
||||||
breadcrumbs.push({ label, url });
|
breadcrumbs.push({ label, url });
|
||||||
|
|||||||
@ -9,15 +9,30 @@
|
|||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template tabId="groups" let-tab>
|
<ng-template tabId="groups" let-tab>
|
||||||
{{tab.name}} - content
|
<app-list [itemTemplate]="authorityItem" [items]="groups()" [loading]="groupsLoading()" metadata="group"></app-list>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template tabId="roles" let-tab>
|
<ng-template tabId="roles" let-tab>
|
||||||
{{tab.name}} - content
|
<app-list [itemTemplate]="authorityItem" [items]="roles()" [loading]="rolesLoading()" metadata="role"></app-list>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
<ng-template tabId="authorities" let-tab>
|
<ng-template tabId="authorities" let-tab>
|
||||||
{{tab.name}} - content
|
<app-list [itemTemplate]="authorityItem" [items]="authorities()" [loading]="authoritiesLoading()" metadata="authority"></app-list>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
|
|
||||||
</app-tab-group>
|
</app-tab-group>
|
||||||
|
|
||||||
|
<ng-template #authorityItem let-item let-metadata="metadata">
|
||||||
|
<app-panel class="neutral-80 authority-item">
|
||||||
|
<span class="title">{{ item.name }}</span>
|
||||||
|
|
||||||
|
@if (item.description) {
|
||||||
|
<span class="spacer">|</span>
|
||||||
|
<span class="description">{{ item.description }}</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
<span class="actions">
|
||||||
|
<button [routerLink]="metadata + '/' + item.id" class="primary outline">View Details</button>
|
||||||
|
</span>
|
||||||
|
</app-panel>
|
||||||
|
</ng-template>
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
:host {
|
||||||
|
.authority-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.description, .spacer {
|
||||||
|
color: var(--neutral-40);
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,9 +1,15 @@
|
|||||||
import {Component, inject, signal} from '@angular/core';
|
import {Component, inject, signal} from '@angular/core';
|
||||||
import {TabGroup, TabGroupComponent} from '../../tab-group/tab-group.component';
|
import {TabGroup, TabGroupComponent} from '../../tab-group/tab-group.component';
|
||||||
import {ActiveTabDirective} from '../../tab-group/active-tab.directive';
|
import {ActiveTabDirective} from '../../tab-group/active-tab.directive';
|
||||||
import {ActivatedRoute, Router} from '@angular/router';
|
import {ActivatedRoute, Router, RouterLink} from '@angular/router';
|
||||||
import {SubjectListComponent} from '../../subject/subject-list/subject-list.component';
|
import {SubjectListComponent} from '../../subject/subject-list/subject-list.component';
|
||||||
import {AppListComponent} from '../../app/app-list/app-list.component';
|
import {AppListComponent} from '../../app/app-list/app-list.component';
|
||||||
|
import {ListComponent} from '../../list/list.component';
|
||||||
|
import {PanelComponent} from '../../panel/panel.component';
|
||||||
|
import {GroupDto} from '../../../clients/gandalf/mithrandir/dtos/group-dto';
|
||||||
|
import {RoleDto} from '../../../clients/gandalf/mithrandir/dtos/role-dto';
|
||||||
|
import {AuthorityDto} from '../../../clients/gandalf/mithrandir/dtos/authority-dto';
|
||||||
|
import {TenantService} from '../../../clients/gandalf/mithrandir/tenant/tenant.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-tenant-detail',
|
selector: 'app-tenant-detail',
|
||||||
@ -11,7 +17,10 @@ import {AppListComponent} from '../../app/app-list/app-list.component';
|
|||||||
TabGroupComponent,
|
TabGroupComponent,
|
||||||
ActiveTabDirective,
|
ActiveTabDirective,
|
||||||
SubjectListComponent,
|
SubjectListComponent,
|
||||||
AppListComponent
|
AppListComponent,
|
||||||
|
ListComponent,
|
||||||
|
PanelComponent,
|
||||||
|
RouterLink
|
||||||
],
|
],
|
||||||
templateUrl: './tenant-detail.component.html',
|
templateUrl: './tenant-detail.component.html',
|
||||||
styleUrl: './tenant-detail.component.scss',
|
styleUrl: './tenant-detail.component.scss',
|
||||||
@ -44,8 +53,16 @@ export class TenantDetailComponent {
|
|||||||
protected activeTabId = signal<string | null>(null)
|
protected activeTabId = signal<string | null>(null)
|
||||||
protected tenantId = signal<string | null>(null)
|
protected tenantId = signal<string | null>(null)
|
||||||
|
|
||||||
|
protected groups = signal<GroupDto[]>([]);
|
||||||
|
protected groupsLoading = signal<boolean>(true);
|
||||||
|
protected roles = signal<RoleDto[]>([]);
|
||||||
|
protected rolesLoading = signal<boolean>(true);
|
||||||
|
protected authorities = signal<AuthorityDto[]>([]);
|
||||||
|
protected authoritiesLoading = signal<boolean>(true);
|
||||||
|
|
||||||
private route = inject(ActivatedRoute);
|
private route = inject(ActivatedRoute);
|
||||||
private router = inject(Router)
|
private router = inject(Router)
|
||||||
|
private tenantService = inject(TenantService);
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
const routeSnapshot = this.route.snapshot;
|
const routeSnapshot = this.route.snapshot;
|
||||||
@ -65,5 +82,35 @@ export class TenantDetailComponent {
|
|||||||
skipLocationChange: false,
|
skipLocationChange: false,
|
||||||
replaceUrl: true
|
replaceUrl: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const tenantId = this.tenantId();
|
||||||
|
|
||||||
|
if (!tenantId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (tab.id) {
|
||||||
|
case 'groups':
|
||||||
|
if (this.groupsLoading()) {
|
||||||
|
const groups = await this.tenantService.getTenantGroupsAsync(tenantId);
|
||||||
|
this.groups.set(groups);
|
||||||
|
this.groupsLoading.set(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'roles':
|
||||||
|
if (this.rolesLoading()) {
|
||||||
|
const roles = await this.tenantService.getTenantRolesAsync(tenantId);
|
||||||
|
this.roles.set(roles);
|
||||||
|
this.rolesLoading.set(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'authorities':
|
||||||
|
if (this.authoritiesLoading()) {
|
||||||
|
const authorities = await this.tenantService.getTenantAuthoritiesAsync(tenantId);
|
||||||
|
this.authorities.set(authorities);
|
||||||
|
this.authoritiesLoading.set(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
src/dotnet/.idea/.idea.Suspectus.Gandalf/.idea/statistic.xml
generated
Normal file
6
src/dotnet/.idea/.idea.Suspectus.Gandalf/.idea/statistic.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Statistic">
|
||||||
|
<option name="fileTypesIncluded" value="html;ts;cs;scss;css;" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@ -5,8 +5,11 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Suspectus.Gandalf.Palantir.Abstractions;
|
using Suspectus.Gandalf.Palantir.Abstractions;
|
||||||
using Suspectus.Gandalf.Palantir.Data.Database;
|
using Suspectus.Gandalf.Palantir.Data.Database;
|
||||||
using Suspectus.Gandalf.Palantir.Data.Dto.App;
|
using Suspectus.Gandalf.Palantir.Data.Dto.App;
|
||||||
|
using Suspectus.Gandalf.Palantir.Data.Dto.Group;
|
||||||
|
using Suspectus.Gandalf.Palantir.Data.Dto.Role;
|
||||||
using Suspectus.Gandalf.Palantir.Data.Dto.Tenant;
|
using Suspectus.Gandalf.Palantir.Data.Dto.Tenant;
|
||||||
using Suspectus.Gandalf.Palantir.Data.Entities.Base;
|
using Suspectus.Gandalf.Palantir.Data.Entities.Base;
|
||||||
|
using Suspectus.Gandalf.Palantir.Data.Entities.Security.Permission.Data;
|
||||||
|
|
||||||
namespace Suspectus.Gandalf.Palantir.Api.Controllers;
|
namespace Suspectus.Gandalf.Palantir.Api.Controllers;
|
||||||
|
|
||||||
@ -121,4 +124,130 @@ public class TenantController : ControllerBase
|
|||||||
|
|
||||||
return Ok(dtos);
|
return Ok(dtos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpGet("{tenantIdHash}/groups")]
|
||||||
|
public async Task<IActionResult> GetTenantGroups(InvokerContext invokerContext, string tenantIdHash,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (!_hashids.TryDecodeSingleLong(tenantIdHash, out var tenantId))
|
||||||
|
{
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
var tenantExists = await _context.Tenants.AnyAsync(x => x.Id == tenantId, cancellationToken);
|
||||||
|
|
||||||
|
if (!tenantExists)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
var relationExists = await _context.TenantSubjectRelations
|
||||||
|
.AnyAsync(x => x.SubjectId == invokerContext.Invoker!.SubjectId, cancellationToken);
|
||||||
|
|
||||||
|
if (!relationExists)
|
||||||
|
{
|
||||||
|
return Forbid();
|
||||||
|
}
|
||||||
|
|
||||||
|
var groups = await _context.Groups
|
||||||
|
.Where(x => x.TenantId == tenantId && x.Type == AuthorityType.Tenant)
|
||||||
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
|
var dtos = groups.Select(x => new GroupDto
|
||||||
|
{
|
||||||
|
Id = _hashids.EncodeLong(x.Id!.Value),
|
||||||
|
Name = x.Name,
|
||||||
|
Visibility = x.Visibility,
|
||||||
|
TenantId = _hashids.EncodeLong(x.TenantId),
|
||||||
|
CategoryPath = x.CategoryPath,
|
||||||
|
Description = x.Description,
|
||||||
|
Type = x.Type
|
||||||
|
});
|
||||||
|
|
||||||
|
return Ok(dtos);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{tenantIdHash}/roles")]
|
||||||
|
public async Task<IActionResult> GetTenantRoles(InvokerContext invokerContext, string tenantIdHash,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (!_hashids.TryDecodeSingleLong(tenantIdHash, out var tenantId))
|
||||||
|
{
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
var tenantExists = await _context.Tenants.AnyAsync(x => x.Id == tenantId, cancellationToken);
|
||||||
|
|
||||||
|
if (!tenantExists)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
var relationExists = await _context.TenantSubjectRelations
|
||||||
|
.AnyAsync(x => x.SubjectId == invokerContext.Invoker!.SubjectId, cancellationToken);
|
||||||
|
|
||||||
|
if (!relationExists)
|
||||||
|
{
|
||||||
|
return Forbid();
|
||||||
|
}
|
||||||
|
|
||||||
|
var groups = await _context.Roles
|
||||||
|
.Where(x => x.TenantId == tenantId && x.Type == AuthorityType.Tenant)
|
||||||
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
|
var dtos = groups.Select(x => new RoleDto
|
||||||
|
{
|
||||||
|
Id = _hashids.EncodeLong(x.Id!.Value),
|
||||||
|
Name = x.Name,
|
||||||
|
Visibility = x.Visibility,
|
||||||
|
TenantId = _hashids.EncodeLong(x.TenantId),
|
||||||
|
CategoryPath = x.CategoryPath,
|
||||||
|
Description = x.Description,
|
||||||
|
Type = x.Type
|
||||||
|
});
|
||||||
|
|
||||||
|
return Ok(dtos);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{tenantIdHash}/authorities")]
|
||||||
|
public async Task<IActionResult> GetTenantAuthorities(InvokerContext invokerContext, string tenantIdHash,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (!_hashids.TryDecodeSingleLong(tenantIdHash, out var tenantId))
|
||||||
|
{
|
||||||
|
return BadRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
var tenantExists = await _context.Tenants.AnyAsync(x => x.Id == tenantId, cancellationToken);
|
||||||
|
|
||||||
|
if (!tenantExists)
|
||||||
|
{
|
||||||
|
return NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
var relationExists = await _context.TenantSubjectRelations
|
||||||
|
.AnyAsync(x => x.SubjectId == invokerContext.Invoker!.SubjectId, cancellationToken);
|
||||||
|
|
||||||
|
if (!relationExists)
|
||||||
|
{
|
||||||
|
return Forbid();
|
||||||
|
}
|
||||||
|
|
||||||
|
var groups = await _context.Authorities
|
||||||
|
.Where(x => x.TenantId == tenantId && x.Type == AuthorityType.Tenant)
|
||||||
|
.ToListAsync(cancellationToken);
|
||||||
|
|
||||||
|
var dtos = groups.Select(x => new GroupDto
|
||||||
|
{
|
||||||
|
Id = _hashids.EncodeLong(x.Id!.Value),
|
||||||
|
Name = x.Name,
|
||||||
|
Visibility = x.Visibility,
|
||||||
|
TenantId = _hashids.EncodeLong(x.TenantId),
|
||||||
|
CategoryPath = x.CategoryPath,
|
||||||
|
Description = x.Description,
|
||||||
|
Type = x.Type
|
||||||
|
});
|
||||||
|
|
||||||
|
return Ok(dtos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
using Suspectus.Gandalf.Palantir.Data.Entities.Base;
|
||||||
|
using Suspectus.Gandalf.Palantir.Data.Entities.Security.Permission.Data;
|
||||||
|
|
||||||
|
namespace Suspectus.Gandalf.Palantir.Data.Dto.Authority;
|
||||||
|
|
||||||
|
public class AuthorityDto
|
||||||
|
{
|
||||||
|
public required string Id { get; set; }
|
||||||
|
public required string TenantId { get; set; }
|
||||||
|
public required string Name { get; set; }
|
||||||
|
public required string CategoryPath { get; set; }
|
||||||
|
public required string? Description { get; set; }
|
||||||
|
public required AuthorityType Type { get; set; }
|
||||||
|
public required EntityVisibility Visibility { get; set; }
|
||||||
|
public string? AppId { get; set; }
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
using Suspectus.Gandalf.Palantir.Data.Dto.Authority;
|
||||||
|
|
||||||
|
namespace Suspectus.Gandalf.Palantir.Data.Dto.Group;
|
||||||
|
|
||||||
|
public class GroupDto : AuthorityDto;
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
using Suspectus.Gandalf.Palantir.Data.Dto.Authority;
|
||||||
|
using Suspectus.Gandalf.Palantir.Data.Dto.Group;
|
||||||
|
|
||||||
|
namespace Suspectus.Gandalf.Palantir.Data.Dto.Role;
|
||||||
|
|
||||||
|
public class RoleDto : AuthorityDto;
|
||||||
Loading…
x
Reference in New Issue
Block a user