Fix authorization in many places (use async)

Admin basket: Fix display
This commit is contained in:
phil 2024-03-26 12:06:22 +05:30
parent 339d8f2210
commit 909e250fe9
26 changed files with 121 additions and 229 deletions

View file

@ -134,4 +134,10 @@ export class AuthenticationService {
conf => conf.bsData?.user?.roles?.filter(value => -1 !== roles.indexOf(value.name)).length > 0
))
}
isNotAuthorized(roles: string[]): Observable<boolean> {
return this.isAuthorized(roles).pipe(map(
isAuthorized => !isAuthorized
))
}
}

View file

@ -59,7 +59,7 @@
<ng-container matColumnDef="time">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Import time</th>
<td mat-cell *matCellDef="let item">{{ isDate(item.time) ? (item.time | date:'dd/MM/yyyy, HH:mm:ss') : 'never' }}</td>
<td mat-cell *matCellDef="let item">{{ item.time | date:'dd/MM/yyyy, HH:mm:ss' }}</td>
</ng-container>
<ng-container matColumnDef="url">
@ -74,17 +74,17 @@
<ng-container matColumnDef="project">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Project</th>
<td mat-cell *matCellDef="let item">{{ item.project }}</td>
<td mat-cell *matCellDef="let item">{{ adminDataService.projectMap.get(item.project_id).name }}</td>
</ng-container>
<ng-container matColumnDef="surveyor">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Surveyor</th>
<td mat-cell *matCellDef="let item">{{ item.surveyor }}</td>
<td mat-cell *matCellDef="let item">{{ adminDataService.surveyorMap.get(item.surveyor_id).name }}</td>
</ng-container>
<ng-container matColumnDef="equipment">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Equipment</th>
<td mat-cell *matCellDef="let item">{{ item.equipment }}</td>
<td mat-cell *matCellDef="let item">{{ adminDataService.equipmentMap.get(item.equipment_id).name }}</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="getColumns();sticky:true"></tr>

View file

@ -5,8 +5,7 @@ import { map } from 'rxjs/operators'
// import { Apollo, gql } from 'apollo-angular'
import { Project } from '../admin-data.service'
import { AdminService, AdminBasket, BasketNameOnly } from '../../openapi'
import { AdminService, AdminBasket, BasketNameOnly, Project } from '../../openapi'
export class AdminBasketFile {
constructor(

View file

@ -3,67 +3,7 @@ import { Observable, of as observableOf } from 'rxjs'
import { map } from 'rxjs/operators'
import { Store } from './admin-manage/data.service'
import { ApiService, SurveyMeta } from '../openapi'
export class Project {
constructor(
public id: number,
public name: string,
) {}
}
export class Surveyor {
constructor(
public id: number,
public name: string,
) {}
}
export class Equipment {
constructor(
public id: number,
public name: string,
) {}
}
// export class SurveyMeta {
// constructor(
// public projects: Project[],
// public surveyors: Surveyor[],
// public equipments: Equipment[],
// public statuses: string[],
// public stores_misc: Store[],
// public stores_line_work: Store[],
// public defaults: Object
// ) {}
// }
// const getSurveyMeta = gql`
// query survey_meta {
// survey_meta {
// projects {
// id
// name
// }
// surveyors {
// id
// name
// }
// equipments {
// id
// name
// }
// stores_misc {
// name
// }
// stores_line_work {
// name
// }
// statuses
// default
// }
// }
// `
import { ApiService, SurveyMeta, Project, Surveyor, Equipment } from '../openapi'
// const admin_models_menu_bar_query = gql`
// query admin_models_menu_bar {
@ -79,6 +19,9 @@ export class Equipment {
@Injectable()
export class AdminDataService {
surveyMeta: SurveyMeta
projectMap: Map<number, Project>
surveyorMap: Map<number, Surveyor>
equipmentMap: Map<number, Equipment>
constructor(
// private apollo: Apollo,
@ -97,36 +40,13 @@ export class AdminDataService {
getSurveyMeta(): Observable<SurveyMeta> {
return this.apiService.getSurveyMetaApiSurveyMetaGet().pipe(map(
surveyMeta => this.surveyMeta = surveyMeta
surveyMeta => {
this.surveyMeta = surveyMeta
this.projectMap = new Map(surveyMeta.projects.map(i => [i.id, i]))
this.surveyorMap = new Map(surveyMeta.surveyors.map(i => [i.id, i]))
this.equipmentMap = new Map(surveyMeta.equipments.map(i => [i.id, i]))
return surveyMeta
}
))
console.warn('Migrate Graphql')
return observableOf()
// return this.apollo.query({
// query: getSurveyMeta,
// }).pipe(map(
// resp => {
// let data = resp['data']['survey_meta']
// this.surveyMeta = new SurveyMeta(
// data['projects'].map(
// item => new Project(item['id'], item['name'])
// ).sort((i, j) => i.name > j.name ? 1 : -1),
// data['surveyors'].map(
// item => new Surveyor(item['id'], item['name'])
// ).sort((i, j) => i.name > j.name ? 1 : -1),
// data['equipments'].map(
// item => new Equipment(item['id'], item['name'])
// ).sort((i, j) => i.name > j.name ? 1 : -1),
// data['statuses'],
// data['stores_misc'].map(
// item => new Store(item['name'])
// ).sort((i, j) => i.name > j.name ? 1 : -1),
// data['stores_line_work'].map(
// item => new Store(item['name'])
// ).sort((i, j) => i.name > j.name ? 1 : -1),
// JSON.parse(data['default']),
// )
// return this.surveyMeta
// }
// ))
}
}

View file

@ -1,19 +1,17 @@
import { Injectable } from '@angular/core'
import { Router, RouterStateSnapshot, ActivatedRouteSnapshot } from '@angular/router'
import { Observable } from 'rxjs'
import { map } from 'rxjs/operators'
import { AdminManageDataService, Category } from '../data.service'
import { ApiService, CategoryRead } from '../../../openapi'
@Injectable()
export class CategoryResolver {
constructor(
private adminManageDataService: AdminManageDataService,
public apiService: ApiService,
private router: Router
) {}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Category[]> {
return this.adminManageDataService.getCategories()
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<CategoryRead[]> {
return this.apiService.getCategoriesApiCategoriesGet()
}
}

View file

@ -15,7 +15,7 @@
</button>
</mat-form-field>
<button mat-raised-button aria-label="New"
*ngIf="authenticationService.isAuthorized(edit_roles)"
*ngIf="authenticationService.isAuthorized(edit_roles) | async"
(click)="editItem()">
<mat-icon>create</mat-icon>
Add new category
@ -37,7 +37,7 @@
</ng-container>
<ng-container matColumnDef="delete"
*ngIf="authenticationService.isAuthorized(edit_roles)"
*ngIf="authenticationService.isAuthorized(edit_roles) | async"
>
<th mat-header-cell *matHeaderCellDef>
<form [formGroup]="unlockDeleteFormGroup" class='unlockCheckbox'>

View file

@ -10,9 +10,10 @@ import { MatPaginator } from '@angular/material/paginator'
import { MatDialog } from '@angular/material/dialog'
import { GisafAdminCategoryDialogComponent } from './dialog.component'
import { Category, AdminManageDataService } from '../data.service'
import { AdminManageDataService } from '../data.service'
import { AuthenticationService } from '../../../_services/authentication.service'
import { CategoryField, categoryFields } from './fields'
import { CategoryRead } from '../../../openapi'
@Component({
selector: 'gisaf-admin-category',
@ -22,7 +23,7 @@ import { CategoryField, categoryFields } from './fields'
})
export class CategoryComponent implements OnInit {
edit_roles = ['category manager']
categories: Category[] = []
categories: CategoryRead[] = []
fields: string[] = categoryFields.map(f=>f.name)
actions: string[] = [
'edit',
@ -58,24 +59,28 @@ export class CategoryComponent implements OnInit {
'canDelete': new UntypedFormControl(),
})
this.columns = categoryFields.filter(f=>f.inTable).map(f=>f.name)
if (this.authenticationService.isAuthorized(this.edit_roles)) {
this.columns = this.columns.concat(this.actions)
}
this.authenticationService.isAuthorized(this.edit_roles).subscribe(
isAuthorized => {
if (isAuthorized) {
this.columns = this.columns.concat(this.actions)
}
}
)
}
applyFilter() {
this.dataSource.filter = this.filterText.trim().toLowerCase();
}
editItem(data?: Category) {
editItem(data?: CategoryRead) {
const dialogRef = this.dialog.open(GisafAdminCategoryDialogComponent, {
width: '75%',
data: {
'category': data || new Category(''),
'category': data
}
})
dialogRef.afterClosed().subscribe(
(category: Category) => category &&
(category: CategoryRead) => category &&
this.adminManageDataService.saveCategory(
category
).subscribe({
@ -86,7 +91,7 @@ export class CategoryComponent implements OnInit {
this.dataSource.data[row] = category
}
else {
this.dataSource.data.push(category as Category)
this.dataSource.data.push(category as CategoryRead)
}
let msg = 'Category ' + category.name + ' ' + result['_mode']
if (result['message']) {
@ -105,7 +110,7 @@ export class CategoryComponent implements OnInit {
)
}
deleteItem(category: Category) {
deleteItem(category: CategoryRead) {
this.adminManageDataService.deleteCategory(category).subscribe({
next: (message: string) => {
this.snackBar.open(message, 'Close')

View file

@ -4,11 +4,11 @@ import { UntypedFormGroup, UntypedFormControl, Validators } from '@angular/forms
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'
import { Category } from '../data.service'
import { categoryFields } from './fields'
import { CategoryRead } from '../../../openapi'
export interface DialogData {
category: Category
category: CategoryRead
}
@ -26,7 +26,7 @@ export class GisafAdminCategoryDialogComponent implements OnInit {
) {}
ngOnInit() {
let category: Category = this.data['category']
let category: CategoryRead = this.data['category']
this.formGroup = new UntypedFormGroup({})
//this.formGroup.addControl('name', new FormControl(category.name, [Validators.required]))
for (let categoryField of categoryFields) {

View file

@ -18,7 +18,7 @@ export const categoryFields: CategoryField[] = [
new CategoryField('minor_group_2', [Validators.required, Validators.maxLength(4), Validators.minLength(4)], 'string', '----'),
new CategoryField('status', [Validators.required, Validators.maxLength(1), Validators.minLength(1)], 'string', 'E'),
//new CategoryField('auto_import', [], 'boolean'),
new CategoryField('model_type', [Validators.required, Validators.pattern('^(Point|Line|Polygon)$')], 'string', 'Point'),
new CategoryField('gis_type', [Validators.required, Validators.pattern('^(Point|Line|Polygon)$')], 'string', 'Point'),
new CategoryField('long_name'),
new CategoryField('symbol', [Validators.maxLength(1), Validators.minLength(1)], 'string'),
new CategoryField('mapbox_type_custom', [], 'string', '', false),

View file

@ -5,6 +5,8 @@ import { map } from 'rxjs/operators'
// import { Apollo, gql } from 'apollo-angular'
import { ApiService, Project, CategoryRead } from '../../openapi'
// const getSurveyStores = gql`
// query geo_survey_stores {
// geo_survey_stores {
@ -217,39 +219,39 @@ export class Store {
) {}
}
export class Category {
constructor(
public name: string,
public description?: string,
public group?: string,
public minor_group_1?: string,
public minor_group_2?: string,
public status?: string,
public auto_import?: string,
public model_type?: string,
public long_name?: string,
public symbol?: string,
public mapbox_type_custom?: string,
public mapbox_paint?: string,
public mapbox_layout?: string,
public viewable_role?: string,
public extra?: string,
) {}
}
// export class Category {
// constructor(
// public name: string,
// public description?: string,
// public group?: string,
// public minor_group_1?: string,
// public minor_group_2?: string,
// public status?: string,
// public auto_import?: string,
// public model_type?: string,
// public long_name?: string,
// public symbol?: string,
// public mapbox_type_custom?: string,
// public mapbox_paint?: string,
// public mapbox_layout?: string,
// public viewable_role?: string,
// public extra?: string,
// ) {}
// }
export class Project {
constructor(
public id: number,
public name: string,
public contact_person?: string,
public site?: string,
public date_approved?: Date,
public start_date_planned?: Date,
public start_date_effective?: Date,
public end_date_planned?: Date,
public end_date_effective?: Date,
) {}
}
// export class Project {
// constructor(
// public id: number,
// public name: string,
// public contact_person?: string,
// public site?: string,
// public date_approved?: Date,
// public start_date_planned?: Date,
// public start_date_effective?: Date,
// public end_date_planned?: Date,
// public end_date_effective?: Date,
// ) {}
// }
export class RawSurveyPoint {
constructor(
@ -302,6 +304,7 @@ export class IntegrityCheck {
export class AdminManageDataService {
constructor(
// private apollo: Apollo,
public apiService: ApiService,
) {}
getRawSurveyStores(): Observable<Store[]> {
@ -397,47 +400,15 @@ export class AdminManageDataService {
// ))
}
getCategories(): Observable<Category[]> {
console.warn('Migrate Graphql')
return observableOf()
// return this.apollo.query({
// query: categoriesQuery,
// }).pipe(map(
// res => res['data']['categories'].map(
// (category: object) => new Category(
// category['name'],
// category['description'],
// category['group'],
// category['minor_group_1'],
// category['minor_group_2'],
// category['status'],
// category['auto_import'],
// category['model_type'],
// category['long_name'],
// category['symbol'],
// category['mapbox_type_custom'],
// category['mapbox_paint'],
// category['mapbox_layout'],
// category['viewable_role'],
// category['extra'],
// )
// )
// ))
getCategories(): Observable<CategoryRead[]> {
return this.apiService.getCategoriesApiCategoriesGet()
}
getProjects(): Observable<Project[]> {
console.warn('Migrate Graphql')
return observableOf()
// return this.apollo.query({
// query: projectsQuery,
// }).pipe(map(
// res => res['data']['projects'].map(
// (project: object) => project as Project
// )
// ))
return this.apiService.getProjectsApiProjectsGet()
}
saveCategory(category: Category): Observable<object> {
saveCategory(category: CategoryRead): Observable<object> {
console.warn('Migrate Graphql')
return observableOf()
// return this.apollo.mutate({
@ -450,7 +421,7 @@ export class AdminManageDataService {
// ))
}
deleteCategory(category: Category): Observable<string> {
deleteCategory(category: CategoryRead): Observable<string> {
console.warn('Migrate Graphql')
return observableOf()
// return this.apollo.mutate({

View file

@ -6,7 +6,8 @@ import { UntypedFormGroup, UntypedFormControl } from '@angular/forms'
import { map, startWith } from 'rxjs/operators'
import { Observable } from 'rxjs'
import { AdminManageDataService, Category, RawSurveyPoint } from '../data.service'
import { AdminManageDataService, RawSurveyPoint } from '../data.service'
import { CategoryRead } from '../../../openapi'
@Component({
selector: 'gisaf-admin-points-by-orig-id',
@ -16,9 +17,9 @@ import { AdminManageDataService, Category, RawSurveyPoint } from '../data.servic
})
export class AdminPointsByOrigIdComponent implements OnInit {
formGroup: UntypedFormGroup
categories: Category[]
categories: CategoryRead[]
rawSurveyPoints: RawSurveyPoint[]
filteredCategories: Observable<Category[]>
filteredCategories: Observable<CategoryRead[]>
destinationControl: UntypedFormControl = new UntypedFormControl()
constructor(
@ -43,7 +44,7 @@ export class AdminPointsByOrigIdComponent implements OnInit {
)
}
private _filter(value: string): Category[] {
private _filter(value: string): CategoryRead[] {
const filterValue = value.toUpperCase()
return this.categories.filter(
category => category.name.indexOf(filterValue) != -1

View file

@ -4,8 +4,8 @@ import { UntypedFormGroup, UntypedFormControl, Validators } from '@angular/forms
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'
import { Project } from '../data.service'
import { projectFields, ProjectField } from './fields'
import { Project } from '../../../openapi'
export interface DialogData {
project: Project

View file

@ -3,7 +3,8 @@ import { Router, RouterStateSnapshot, ActivatedRouteSnapshot } from '@angular/ro
import { Observable } from 'rxjs'
import { map } from 'rxjs/operators'
import { AdminManageDataService, Project } from '../data.service'
import { AdminManageDataService } from '../data.service'
import { Project } from '../../../openapi'
@Injectable()

View file

@ -15,7 +15,7 @@
</button>
</mat-form-field>
<button mat-raised-button aria-label="New"
*ngIf="authenticationService.isAuthorized(edit_roles)"
*ngIf="authenticationService.isAuthorized(edit_roles) | async"
(click)="editItem()">
<mat-icon>create</mat-icon>
Add new project
@ -29,7 +29,7 @@
</ng-container>
<ng-container matColumnDef="delete"
*ngIf="authenticationService.isAuthorized(edit_roles)"
*ngIf="authenticationService.isAuthorized(edit_roles) | async"
>
<th mat-header-cell *matHeaderCellDef>
<form [formGroup]="unlockDeleteFormGroup" class='unlockCheckbox'>

View file

@ -10,10 +10,11 @@ import { MatPaginator } from '@angular/material/paginator'
import { MatDialog } from '@angular/material/dialog'
import { AdminDataService } from '../../admin-data.service'
import { Project, AdminManageDataService } from '../data.service'
import { AdminManageDataService } from '../data.service'
import { GisafAdminProjectDialogComponent } from './dialog.component'
import { AuthenticationService } from '../../../_services/authentication.service'
import { ProjectField, projectFields } from './fields'
import { Project } from '../../../openapi'
@Component({
@ -61,9 +62,13 @@ export class ProjectComponent implements OnInit {
'canDelete': new UntypedFormControl(),
})
this.columns = projectFields.filter(f=>f.inTable).map(f=>f.name)
if (this.authenticationService.isAuthorized(this.edit_roles)) {
this.columns = this.columns.concat(this.actions)
}
this.authenticationService.isAuthorized(this.edit_roles).subscribe(
isAuthorized => {
if (isAuthorized) {
this.columns = this.columns.concat(this.actions)
}
}
)
}
applyFilter() {
@ -74,7 +79,7 @@ export class ProjectComponent implements OnInit {
const dialogRef = this.dialog.open(GisafAdminProjectDialogComponent, {
width: '75%',
data: {
'project': data || new Project(undefined, ''),
'project': data || {},
}
})
dialogRef.afterClosed().subscribe(

View file

@ -3,7 +3,8 @@ import { Component, OnInit, Input,
import { MatSnackBar } from '@angular/material/snack-bar'
import { AdminManageDataService, RawSurveyPoint, Category } from '../data.service'
import { AdminManageDataService, RawSurveyPoint } from '../data.service'
import { CategoryRead } from '../../../openapi'
@Component({

View file

@ -66,7 +66,7 @@
</mat-expansion-panel>
<mat-expansion-panel
*ngIf="authenticationService.isAuthorized(['manager'])"
*ngIf="authenticationService.isAuthorized(['manager']) | async"
>
<mat-expansion-panel-header>
<mat-panel-title>
@ -87,7 +87,7 @@
</mat-list-item>
<mat-list-item class='root'
*ngIf="authenticationService.isAuthorized(['manager'])"
*ngIf="authenticationService.isAuthorized(['manager']) | async"
>
<button mat-button
routerLink="/admin/manage/access"

View file

@ -12,7 +12,7 @@
*ngFor="let item of menuItem.pages"
>
<button mat-button
[hidden]="isHidden(item) | async"
[hidden]="authenticationService.isNotAuthorized([item.viewable_role]) | async"
matTooltip="{{ item.description }}"
matTooltipPosition="right"
routerLink="/dashboard/{{ item.group }}/{{ item.name }}"

View file

@ -29,10 +29,4 @@ export class DashboardMenuComponent implements OnInit {
}
)
}
isHidden(dashboard: DashboardPageMetaData): Observable<boolean> {
return this.authenticationService.isAuthorized([dashboard.viewable_role]).pipe(map(
r => !r
))
}
}

View file

@ -1,4 +1,5 @@
<button mat-raised-button color='primary' [hidden]='!isAuthorized'
<button mat-raised-button color='primary'
[hidden]='authenticationService.isNotAuthorized(action.roles) | async'
(click)="execute()">
<mat-icon aria-label="submit">done</mat-icon>
{{ action.name }}

View file

@ -22,16 +22,11 @@ export class TagActionComponent {
private actionsService: ActionsService,
private infoDataService: InfoDataService,
private snackBar: MatSnackBar,
private authenticationService: AuthenticationService,
public authenticationService: AuthenticationService,
protected mapControlService: MapControlService,
private cdr: ChangeDetectorRef,
) {}
get isAuthorized() {
// At least one role of the user must match the roles of the action
return this.authenticationService.roles.filter(r => this.action.roles.indexOf(r.name) != -1).length > 0
}
execute() {
this.actionsService.execute(
[this.source.modelInfo.store],

View file

@ -102,6 +102,7 @@ export class TagFormComponent implements OnInit {
)) {
for (let tagKey of store.tagActions) {
if (tagKey.actions.map(
// FIXME: isAuthorized is async!!
action => this.authenticationService.isAuthorized(action.roles)
).filter(i => i).length > 0) {
this.pluginsFormGroup.addControl(

View file

@ -30,7 +30,7 @@ export class ToolsComponent {
private infoDataService: InfoDataService,
private actionsService: ActionsService,
private snackBar: MatSnackBar,
private authenticationService: AuthenticationService
public authenticationService: AuthenticationService
) {
}
@ -65,10 +65,6 @@ export class ToolsComponent {
return formFields
}
get isAuthorized() {
return this.authenticationService.user.value
}
executeAction(action: ModelAction, fullInfo: FullInfo) {
this.actionsService.execute(
[fullInfo.modelInfo.store],

View file

@ -162,7 +162,7 @@
Download Shapefiles of selected layers (EPSG {{ (configService.conf | async).bsData?.geo.srid }}) [<i><b>Deprecated</b>: use Geopackage</i>]
</button>
</mat-menu>
<button mat-raised-button [disabled]="!(authenticationService.isAuthorized(['base_map_creator']) | async)"
<button mat-raised-button [disabled]="authenticationService.isNotAuthorized(['base_map_creator']) | async"
matTooltip="Create a base map with the selected layers"
(click)="createBaseMap()">
Create a base map...

View file

@ -24,7 +24,7 @@ export class TreeLayerItemComponent implements OnInit {
constructor(
protected mapControlService: MapControlService,
private cdr: ChangeDetectorRef,
private authenticationService: AuthenticationService,
public authenticationService: AuthenticationService,
) {}
ngOnInit() {
@ -42,9 +42,7 @@ export class TreeLayerItemComponent implements OnInit {
return of(false)
}
else {
return this.authenticationService.isAuthorized([role]).pipe(map(
a => !a
))
return this.authenticationService.isNotAuthorized([role])
}
}

View file

@ -21,7 +21,7 @@ export type OpenAPIConfig = {
export const OpenAPI: OpenAPIConfig = {
BASE: '',
VERSION: '2023.4.dev53+gd539a72.d20240320',
VERSION: '2023.4.dev56+g775030d.d20240325',
WITH_CREDENTIALS: false,
CREDENTIALS: 'include',
TOKEN: undefined,