From 0ebb3870ec471c732d13abc2a019508e748fb403 Mon Sep 17 00:00:00 2001 From: Alessio Franceschini Date: Wed, 21 Aug 2024 12:42:58 +0200 Subject: [PATCH 001/607] Added local file to gitignore --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 49ff745e1e..85775f5eeb 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,8 @@ backend/env/ backend/workingdir/ client/build client/node_modules/ +/docker-compose.yml +/Dockerfile +/**/client/package.json +/**/client/proxy.conf.json +/client/.angular From 787c4dc3aefdcece111c4cb540d0c3b11d47e262 Mon Sep 17 00:00:00 2001 From: Alessio Franceschini Date: Wed, 21 Aug 2024 15:18:42 +0200 Subject: [PATCH 002/607] misc: first commit in this branch --- client/app/css/main.css | 3 +- client/app/src/app-routing.module.ts | 27 +++ client/app/src/app.module.ts | 4 + .../models/accreditor/organization-data.ts | 30 +++ .../app/src/models/app/shared-public-model.ts | 11 ++ .../src/pages/accred/accred-routing.module.ts | 19 ++ client/app/src/pages/accred/accred.module.ts | 23 +++ .../pages/accred/accred/accred.component.html | 125 +++++++++++++ .../pages/accred/accred/accred.component.ts | 70 +++++++ .../accreditor/accreditor-routing.module.ts | 37 ++++ .../accreditor/accreditor.module.ts | 23 +++ .../accreditor/home/home.component.html | 3 + .../accreditor/home/home.component.ts | 25 +++ .../organization/organization.component.html | 67 +++++++ .../organization/organization.component.ts | 85 +++++++++ .../organizations.component.html | 172 ++++++++++++++++++ .../organizations/organizations.component.ts | 76 ++++++++ .../accreditor/sidebar/sidebar.component.html | 8 + .../accreditor/sidebar/sidebar.component.ts | 24 +++ .../users-tab1/users-tab1.component.html | 3 +- client/app/src/pages/app/app.component.html | 1 + .../services/helper/accreditor-org.service.ts | 19 ++ client/app/src/shared.module.ts | 19 +- .../app/src/shared/guards/accreditor.guard.ts | 28 +++ .../org-admin-info.component.html | 20 ++ .../org-admin-info.component.ts | 18 ++ .../partials/org-info/org-info.component.html | 35 ++++ .../partials/org-info/org-info.component.ts | 26 +++ .../org-overview/org-overview.component.html | 33 ++++ .../org-overview/org-overview.component.ts | 26 +++ .../org-recipient-info.component.html | 27 +++ .../org-recipient-info.component.ts | 17 ++ .../org-users-list.component.html | 104 +++++++++++ .../org-users-list.component.ts | 24 +++ .../accreditation-req-resolver.service.ts | 81 +++++++++ 35 files changed, 1309 insertions(+), 4 deletions(-) create mode 100644 client/app/src/models/accreditor/organization-data.ts create mode 100644 client/app/src/pages/accred/accred-routing.module.ts create mode 100644 client/app/src/pages/accred/accred.module.ts create mode 100644 client/app/src/pages/accred/accred/accred.component.html create mode 100644 client/app/src/pages/accred/accred/accred.component.ts create mode 100644 client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts create mode 100644 client/app/src/pages/accreditor/accreditor/accreditor.module.ts create mode 100644 client/app/src/pages/accreditor/accreditor/home/home.component.html create mode 100644 client/app/src/pages/accreditor/accreditor/home/home.component.ts create mode 100644 client/app/src/pages/accreditor/accreditor/organization/organization.component.html create mode 100644 client/app/src/pages/accreditor/accreditor/organization/organization.component.ts create mode 100644 client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html create mode 100644 client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts create mode 100644 client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html create mode 100644 client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts create mode 100644 client/app/src/services/helper/accreditor-org.service.ts create mode 100644 client/app/src/shared/guards/accreditor.guard.ts create mode 100644 client/app/src/shared/partials/org-admin-info/org-admin-info.component.html create mode 100644 client/app/src/shared/partials/org-admin-info/org-admin-info.component.ts create mode 100644 client/app/src/shared/partials/org-info/org-info.component.html create mode 100644 client/app/src/shared/partials/org-info/org-info.component.ts create mode 100644 client/app/src/shared/partials/org-overview/org-overview.component.html create mode 100644 client/app/src/shared/partials/org-overview/org-overview.component.ts create mode 100644 client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html create mode 100644 client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts create mode 100644 client/app/src/shared/partials/org-users-list/org-users-list.component.html create mode 100644 client/app/src/shared/partials/org-users-list/org-users-list.component.ts create mode 100644 client/app/src/shared/resolvers/accreditation-req-resolver.service.ts diff --git a/client/app/css/main.css b/client/app/css/main.css index 0284ab03cc..df31c63ec3 100644 --- a/client/app/css/main.css +++ b/client/app/css/main.css @@ -310,7 +310,8 @@ h6 { #FooterBox, #DemoBadge, #PrivacyBadge, -#operator-badge{ +#operator-badge, +#organization-button{ padding: 1em; } diff --git a/client/app/src/app-routing.module.ts b/client/app/src/app-routing.module.ts index 7068b44522..57f7bb5517 100644 --- a/client/app/src/app-routing.module.ts +++ b/client/app/src/app-routing.module.ts @@ -22,6 +22,8 @@ import {WbTipResolver} from "@app/shared/resolvers/wb-tip-resolver.service"; import {WhistleblowerLoginResolver} from "@app/shared/resolvers/whistleblower-login.resolver"; import {SubmissionComponent} from "@app/pages/whistleblower/submission/submission.component"; import {AuthRoutingModule} from "@app/pages/auth/auth-routing.module"; +import { AccreditorGuard } from "./shared/guards/accreditor.guard"; +import { OrganizationComponent } from "./pages/accreditor/organization/organization.component"; const routes: Routes = [ @@ -126,6 +128,15 @@ const routes: Routes = [ }, loadChildren: () => import("./pages/wizard/wizard-routing.module").then(m => m.WizardRoutingModule) }, + { + path: "accred", + data: {pageTitle: "Accreditation"}, + resolve: { + PreferenceResolver, + title: TitleResolver + }, + loadChildren: () => import("./pages/accred/accred-routing.module").then(m => m.AccredRoutingModule) + }, { path: "reports/:tip_id", data: {pageTitle: "Report"}, @@ -136,6 +147,22 @@ const routes: Routes = [ canActivate: [SessionGuard], pathMatch: "full", }, + { + path: 'accreditor', + canActivate: [AccreditorGuard], + data: { + sidebar: "acreditator-sidebar", + pageTitle: "Home", + }, + loadChildren: () => import('./pages/accreditor/accreditor-routing.module').then(m => m.AccreditorRoutingModule) + }, + { + path: "organizations/:org_id", + data: {pageTitle: "Organization"}, + component: OrganizationComponent, + canActivate: [AccreditorGuard], + pathMatch: "full", + }, {path: "**", redirectTo: ""} ]; diff --git a/client/app/src/app.module.ts b/client/app/src/app.module.ts index 7aa7e7563d..2a12503337 100644 --- a/client/app/src/app.module.ts +++ b/client/app/src/app.module.ts @@ -39,6 +39,8 @@ import {AnalystModule} from "@app/pages/analyst/analyst.module"; import {mockEngine} from './services/helper/mocks'; import {HttpService} from "./shared/services/http.service"; import {CryptoService} from "@app/shared/services/crypto.service"; +import { AccredModule } from "./pages/accred/accred.module"; +import { AccreditorModule } from "./pages/accreditor/accreditor.module"; export function createTranslateLoader(http: HttpClient) { return new TranslateHttpLoader(http, "l10n/", ""); } @@ -74,6 +76,8 @@ const translationModule = TranslateModule.forRoot({ HttpClientModule, BrowserModule, BrowserAnimationsModule, + AccredModule, + AccreditorModule, AuthModule, SignupModule, ActionModule, diff --git a/client/app/src/models/accreditor/organization-data.ts b/client/app/src/models/accreditor/organization-data.ts new file mode 100644 index 0000000000..d64e00078a --- /dev/null +++ b/client/app/src/models/accreditor/organization-data.ts @@ -0,0 +1,30 @@ +import { EOUser } from "../app/shared-public-model"; + +export class OrganizationData { + + id: string; + denomination: string; + type: string; + state: string; + accreditation_date: string; + num_user_profiled: number; + num_tip: number; + users: EOUser[]; + pec: string; + +} + + +export interface AccreditationRequestModel{ + + id: string; + denomination: string; + type: string; + state: string; + accreditation_date: string; + num_user_profiled: number; + num_tip: number; + +} + + diff --git a/client/app/src/models/app/shared-public-model.ts b/client/app/src/models/app/shared-public-model.ts index c5bd1d92c1..b1e335d046 100644 --- a/client/app/src/models/app/shared-public-model.ts +++ b/client/app/src/models/app/shared-public-model.ts @@ -198,3 +198,14 @@ export interface SubmissionStatus { status?: string; substatus?: string; } + +export interface EOUser { + id: string; + name: string; + surname: string; + creation_date: string; + last_access: string; + role: string; + tips: number; + closed_tips: number; +} diff --git a/client/app/src/pages/accred/accred-routing.module.ts b/client/app/src/pages/accred/accred-routing.module.ts new file mode 100644 index 0000000000..148e869b86 --- /dev/null +++ b/client/app/src/pages/accred/accred-routing.module.ts @@ -0,0 +1,19 @@ +import {NgModule} from "@angular/core"; +import {RouterModule, Routes} from "@angular/router"; +import {AccredComponent} from "@app/pages/accred/accred/accred.component"; + +const routes: Routes = [ + { + path: "", + component: AccredComponent, + pathMatch: "full", + } + +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class AccredRoutingModule { +} diff --git a/client/app/src/pages/accred/accred.module.ts b/client/app/src/pages/accred/accred.module.ts new file mode 100644 index 0000000000..f6fdaec6ad --- /dev/null +++ b/client/app/src/pages/accred/accred.module.ts @@ -0,0 +1,23 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { NgSelectModule } from '@ng-select/ng-select'; +import { TranslateModule } from '@ngx-translate/core'; +import { SharedModule } from '@app/shared.module'; +import { AccredComponent } from './accred/accred.component'; + + +@NgModule({ + declarations: [ + AccredComponent + ], + imports: [ + CommonModule, + TranslateModule, + FormsModule, + ReactiveFormsModule, + NgSelectModule, + SharedModule + ] +}) +export class AccredModule { } \ No newline at end of file diff --git a/client/app/src/pages/accred/accred/accred.component.html b/client/app/src/pages/accred/accred/accred.component.html new file mode 100644 index 0000000000..22b1674e60 --- /dev/null +++ b/client/app/src/pages/accred/accred/accred.component.html @@ -0,0 +1,125 @@ + + + + + + +
+
+
+ +
+
{{ 'Organization info' | translate }}
+
+
+ + +
+
+ + +
{{'Invalid PEC format' | translate}}
+
+
+ + +
{{'Invalid PEC format' | translate}}
+
{{'PEC and Confirm PEC must match' | translate}}
+
+
+ + +
+
+
+
+
+ +
+
{{ 'Admin info' | translate }}
+
+
+ + +
+
+ + +
{{'Invalid email format' | translate}}
+
+
+
+
+
+ +
+
{{ 'Primary recipient info' | translate }}
+
+
+ + +
+
+ + +
+
+ + +
{{'Invalid email format' | translate}}
+
+
+
+
+
+
+
+
+
{{ 'Privacy Policy' | translate }}
+
{{ privacyPolicy | translate }}
+
+ + {{ 'I have read and agree to the privacy policy.' | translate }} +
+
+
+
+
+
+ +
+
diff --git a/client/app/src/pages/accred/accred/accred.component.ts b/client/app/src/pages/accred/accred/accred.component.ts new file mode 100644 index 0000000000..c48e1a7433 --- /dev/null +++ b/client/app/src/pages/accred/accred/accred.component.ts @@ -0,0 +1,70 @@ +import { Component, OnInit, ViewChild, TemplateRef } from "@angular/core"; +import { AuthenticationService } from "@app/services/helper/authentication.service"; +import { Constants } from "@app/shared/constants/constants"; +import { ActivatedRoute, Router } from "@angular/router"; +import { AppDataService } from "@app/app-data.service"; +import { NgbModal } from "@ng-bootstrap/ng-bootstrap"; +import { NgForm } from "@angular/forms"; + +@Component({ + selector: "app-login", + templateUrl: "./accred.component.html" +}) +export class AccredComponent implements OnInit { + @ViewChild("accredForm") public accredForm: NgForm; + @ViewChild('content') content: TemplateRef; + modalMessage = 'A confirmation email will be sent to the organization\'s PEC.'; + + protected readonly location = location; + protected readonly Constants = Constants; + + organizationInfo = { + denomination: '', + pec: '', + confirmPec: '', + institutionalWebsite: '' + }; + adminInfo = { + name: '', + email: '', + fiscalCode: '' + }; + recipientInfo = { + name: '', + fiscalCode: '', + email: '' + }; + + pecsMatch = true; + privacyAccept = false; + privacyPolicy = 'Your privacy policy text here...'; + + constructor(private authentication: AuthenticationService, public router: Router, private route: ActivatedRoute, protected appDataService: AppDataService, private modalService: NgbModal) {} + + ngOnInit() { + this.adminInfo.fiscalCode = this.getFiscalCodeFromIdp(); + } + + private getFiscalCodeFromIdp(): string { + return 'sample-fiscal-code'; + } + + checkPecsMatch() { + this.pecsMatch = this.organizationInfo.pec === this.organizationInfo.confirmPec; + } + + closeModal(modal: any) { + modal.close('Close click'); + this.router.navigate(['/']); + } + + openConfirmModal() { + this.modalService.open(this.content); + } + + onSubmit() { + if (this.privacyAccept && this.pecsMatch) { + this.openConfirmModal(); + } + } +} diff --git a/client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts b/client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts new file mode 100644 index 0000000000..756d7b887a --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts @@ -0,0 +1,37 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { AccreditorHomeComponent } from './home/home.component'; +import { OrganizationsComponent } from './organizations/organizations.component'; +import { AccreditationReqResolver } from '@app/shared/resolvers/accreditation-req-resolver.service'; + +const routes: Routes = [ + { + path: "", + component: AccreditorHomeComponent, + pathMatch: "full", + data: {sidebar: "accreditor-sidebar", pageTitle: "Home"}, + }, + { + path: "home", + component: AccreditorHomeComponent, + pathMatch: "full", + data: {sidebar: "accreditor-sidebar", pageTitle: "Home"}, + }, + { + path: "organizations", + component: OrganizationsComponent, + resolve: { + AccreditationReqResolver + }, + pathMatch: "full", + data: {sidebar: "accreditor-sidebar", pageTitle: "Organizations"}, + } + + +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class AccreditorRoutingModule { } diff --git a/client/app/src/pages/accreditor/accreditor/accreditor.module.ts b/client/app/src/pages/accreditor/accreditor/accreditor.module.ts new file mode 100644 index 0000000000..558bd9d1c2 --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/accreditor.module.ts @@ -0,0 +1,23 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import {RouterModule} from "@angular/router"; +import {SharedModule} from "@app/shared.module"; +import {FormsModule} from "@angular/forms"; +import {NgbModule, NgbNavModule} from "@ng-bootstrap/ng-bootstrap"; +import { AccreditorHomeComponent } from './home/home.component'; +import { SidebarComponent } from './sidebar/sidebar.component'; +import { OrganizationsComponent } from './organizations/organizations.component'; +import { OrganizationComponent } from './organization/organization.component'; + + +@NgModule({ + declarations: [ + AccreditorHomeComponent, SidebarComponent, OrganizationsComponent, OrganizationComponent + ], + imports: [ + CommonModule, SharedModule, NgbNavModule, NgbModule, RouterModule, FormsModule + ], + exports: [SidebarComponent] +}) +export class AccreditorModule { } diff --git a/client/app/src/pages/accreditor/accreditor/home/home.component.html b/client/app/src/pages/accreditor/accreditor/home/home.component.html new file mode 100644 index 0000000000..aa5a211875 --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/home/home.component.html @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/client/app/src/pages/accreditor/accreditor/home/home.component.ts b/client/app/src/pages/accreditor/accreditor/home/home.component.ts new file mode 100644 index 0000000000..43cb036271 --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/home/home.component.ts @@ -0,0 +1,25 @@ +import {Component} from "@angular/core"; +import {AppDataService} from "@app/app-data.service"; +import {preferenceResolverModel} from "@app/models/resolvers/preference-resolver-model"; +import {PreferenceResolver} from "@app/shared/resolvers/preference.resolver"; +import {UtilsService} from "@app/shared/services/utils.service"; + + +@Component({ + selector: "src-accreditor-home", + templateUrl: "./home.component.html" +}) +export class AccreditorHomeComponent { + preferenceData: preferenceResolverModel; + constructor(private appDataService: AppDataService,private utilsService: UtilsService, private preference: PreferenceResolver) { + } + + ngOnInit(): void { + if (this.preference.dataModel) { + this.preferenceData = this.preference.dataModel; + } + if (this.appDataService.public.node.user_privacy_policy_text && this.preferenceData.accepted_privacy_policy === "1970-01-01T00:00:00Z") { + this.utilsService.acceptPrivacyPolicyDialog().subscribe(); + } + } +} diff --git a/client/app/src/pages/accreditor/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/accreditor/organization/organization.component.html new file mode 100644 index 0000000000..01252cc562 --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/organization/organization.component.html @@ -0,0 +1,67 @@ +
+
+
+ + + + + + + + + + + + + + + + + + + + +
+
+ + +
+
+ +
+ +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
diff --git a/client/app/src/pages/accreditor/accreditor/organization/organization.component.ts b/client/app/src/pages/accreditor/accreditor/organization/organization.component.ts new file mode 100644 index 0000000000..ab120d4cde --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/organization/organization.component.ts @@ -0,0 +1,85 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { OrganizationData } from '@app/models/accreditor/organization-data'; +import { EOUser } from '@app/models/app/shared-public-model'; +import { AccreditorOrgService } from '@app/services/helper/accreditor-org.service'; +import { HttpService } from '@app/shared/services/http.service'; +import { Observable } from 'rxjs'; + +@Component({ + selector: 'src-organization', + templateUrl: './organization.component.html' +}) +export class OrganizationComponent implements OnInit{ + + org_id: string | null; + loading: boolean = false; + organization: OrganizationData; + + constructor(private activatedRoute: ActivatedRoute, private httpService: HttpService, private orgService : AccreditorOrgService){ + + } + + + ngOnInit() { + this.loadOrganizationData(); + } + + loadOrganizationData(){ + this.org_id = this.activatedRoute.snapshot.paramMap.get("org_id"); + + // const requestObservable: Observable = this.httpService.receiverTip(this.org_id); + this.loading = true; + this.orgService.reset(); + + // setTimeout(()=>{ + this.organization = new OrganizationData(); + this.organization.id = "1" + this.organization.denomination = "denominazione Org 1" + this.organization.accreditation_date = "01-02-2024" + this.organization.num_tip = 10 + this.organization.num_user_profiled = 2 + this.organization.type = "NOT_AFFILIATED" + this.organization.state = "ACCREDITED" + + let users : EOUser[] = []; + users.push({ + id: "1", + name: "Utente 1", + surname: "Surname 1", + creation_date: "01-01-2024", + last_access: "01/02/2024 10:00:05", + role: "Admin", + tips: 10, + closed_tips: 1 + }) + + this.organization.users = users; + + + // }, 1000) + + // requestObservable.subscribe( + // { + // next: (response: OrganizationData) => { + // this.loading = false; + // this.organization = this.orgService.organization; + + // // this.activatedRoute.queryParams.subscribe((params: { [x: string]: string; }) => { + // // this.tip.tip_id = params["tip_id"]; + // // }) + + // //TODO: CHIAMATA PER RECUPERARE LISTA UTENTI DELL'ORGANIZZAZIONE + // // this.httpService.getOrgUsers(this.org_id) + + // } + // } + // ); + } + + + convertiInAffiliata(){ + console.log("CONVERTI IN AFFILIATA - TODO!!!") + } + +} diff --git a/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html b/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html new file mode 100644 index 0000000000..ade2b2c85a --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html @@ -0,0 +1,172 @@ + +
+
+ + + + + + + + + + + +
+ +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + {{'Denomination' | translate}} + + + + + + + + + {{'Type' | translate}} + + + + + + + + + {{'Accreditation Date' | translate}} + + + + + + + + + {{'Users' | translate}} + + + + + + + + + {{'Tips' | translate}} + + + + + + + + + {{'Closed' | translate}} + + + + + + + + + {{'State' | translate}} + + + + + +
+ {{req.id}} + + {{req.denomination}} + + {{req.type}} + + {{req.accreditation_date}} + + {{req.num_user_profiled}} + + {{req.num_tip}} + + {{req.num_tip}} + + {{req.state}} +
+
+ + < {{ 'Previous' | translate }} + + + {{ 'Next' | translate }} > + + << {{ 'First' | translate }} + + + {{ 'Last' | translate }} >> + + +
+ +
+
+
\ No newline at end of file diff --git a/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts b/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts new file mode 100644 index 0000000000..322819efd8 --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts @@ -0,0 +1,76 @@ +import { HttpClient } from '@angular/common/http'; +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { AppDataService } from '@app/app-data.service'; +import { AccreditationRequestModel } from '@app/models/accreditor/organization-data'; +import { AuthenticationService } from '@app/services/helper/authentication.service'; +import { AppConfigService } from '@app/services/root/app-config.service'; +import { AccreditationReqResolver } from '@app/shared/resolvers/accreditation-req-resolver.service'; +import { HttpService } from '@app/shared/services/http.service'; +import { UtilsService } from '@app/shared/services/utils.service'; +import { TranslateService } from '@ngx-translate/core'; +import { filter, orderBy } from 'lodash'; + +@Component({ + selector: 'src-organizations', + templateUrl: './organizations.component.html' +}) +export class OrganizationsComponent implements OnInit{ + + constructor(private http: HttpClient,protected authenticationService: AuthenticationService, protected httpService: HttpService, + private appConfigServices: AppConfigService, private router: Router, protected AReqs: AccreditationReqResolver, + protected utils: UtilsService, protected appDataService: AppDataService, private translateService: TranslateService) { + + } + + search: string | undefined; + selectedReqs: string[] = []; + filteredReqs: AccreditationRequestModel[]; + currentPage: number = 1; + itemsPerPage: number = 20; + sortKey: string = "accreditation_date"; + sortReverse: boolean = true; + + + + ngOnInit(): void { + if (!this.AReqs.dataModel) { + this.router.navigate(["/accreditor/home"]).then(); + } else { + this.filteredReqs = this.AReqs.dataModel; + } + } + + + reload() { + const reloadCallback = () => { + this.AReqs.reload(); + }; + + this.appConfigServices.localInitialization(true, reloadCallback); + } + + + onSearchChange(value: string | number | undefined) { + if (typeof value !== "undefined") { + this.currentPage = 1; + this.filteredReqs = this.AReqs.dataModel; + // this.processTips(); + + this.filteredReqs = orderBy(filter(this.filteredReqs, (req) => + Object.values(req).some((val) => { + if (typeof val === "string" || typeof val === "number") { + return String(val).toLowerCase().includes(String(value).toLowerCase()); + } + return false; + }) + ), "accreditation_date"); + } + } + + orderbyCast(data: AccreditationRequestModel[]): AccreditationRequestModel[] { + return data; + } + + +} diff --git a/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html b/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html new file mode 100644 index 0000000000..abfd855a2d --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html @@ -0,0 +1,8 @@ + + + {{'Home' | translate}} + + + + {{'Organizations' | translate}} + \ No newline at end of file diff --git a/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts b/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts new file mode 100644 index 0000000000..5a9aec12c6 --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts @@ -0,0 +1,24 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { Router } from '@angular/router'; +import { AuthenticationService } from '@app/services/helper/authentication.service'; +import { NodeResolver } from '@app/shared/resolvers/node.resolver'; + +@Component({ + selector: "src-accreditor-sidebar", + templateUrl: "./sidebar.component.html", + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class SidebarComponent { + + constructor(private router: Router, protected nodeResolver: NodeResolver, protected authenticationService: AuthenticationService) { + } + + isActive(route: string): boolean { + return this.router.isActive(route, { + paths: "subset", + queryParams: "subset", + fragment: "ignored", + matrixParams: "ignored" + }); + } +} \ No newline at end of file diff --git a/client/app/src/pages/admin/users/users-tab1/users-tab1.component.html b/client/app/src/pages/admin/users/users-tab1/users-tab1.component.html index 9af8d7dd6a..c05935ac83 100644 --- a/client/app/src/pages/admin/users/users-tab1/users-tab1.component.html +++ b/client/app/src/pages/admin/users/users-tab1/users-tab1.component.html @@ -17,6 +17,7 @@ + @@ -59,4 +60,4 @@ - \ No newline at end of file + diff --git a/client/app/src/pages/app/app.component.html b/client/app/src/pages/app/app.component.html index 584d9d9744..2b9870ecd0 100644 --- a/client/app/src/pages/app/app.component.html +++ b/client/app/src/pages/app/app.component.html @@ -52,6 +52,7 @@

Error!



+ diff --git a/client/app/src/services/helper/accreditor-org.service.ts b/client/app/src/services/helper/accreditor-org.service.ts new file mode 100644 index 0000000000..d0ce84049c --- /dev/null +++ b/client/app/src/services/helper/accreditor-org.service.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@angular/core'; +import { OrganizationData } from '@app/models/accreditor/organization-data'; +import { HttpService } from '@app/shared/services/http.service'; + +@Injectable({ + providedIn: 'root' +}) +export class AccreditorOrgService { + + organization: OrganizationData = new OrganizationData(); + + constructor(private httpService: HttpService) { + + } + + reset(){ + this.organization = new OrganizationData(); + } +} diff --git a/client/app/src/shared.module.ts b/client/app/src/shared.module.ts index 348a78a2ac..54636a605b 100644 --- a/client/app/src/shared.module.ts +++ b/client/app/src/shared.module.ts @@ -83,6 +83,11 @@ import {ReopenSubmissionComponent} from "@app/shared/modals/reopen-submission/re import {OtkcAccessComponent} from "@app/shared/modals/otkc-access/otkc-access.component"; import {OperationComponent} from "@app/shared/partials/operation/operation.component"; import {RedactInformationComponent} from "@app/shared/modals/redact-information/redact-information.component"; +import { OrgInfoComponent } from "./shared/partials/org-info/org-info.component"; +import { OrgAdminInfoComponent } from "./shared/partials/org-admin-info/org-admin-info.component"; +import { OrgRecipientInfoComponent } from "./shared/partials/org-recipient-info/org-recipient-info.component"; +import { OrgOverviewComponent } from "./shared/partials/org-overview/org-overview.component"; +import { OrgUsersListComponent } from "./shared/partials/org-users-list/org-users-list.component"; @NgModule({ imports: [ @@ -191,7 +196,12 @@ import {RedactInformationComponent} from "@app/shared/modals/redact-information/ SwitchComponent, OtkcAccessComponent, OperationComponent, - RedactInformationComponent + RedactInformationComponent, + OrgInfoComponent, + OrgRecipientInfoComponent, + OrgAdminInfoComponent, + OrgOverviewComponent, + OrgUsersListComponent ], exports: [ FooterComponent, @@ -251,7 +261,12 @@ import {RedactInformationComponent} from "@app/shared/modals/redact-information/ SwitchComponent, OtkcAccessComponent, OperationComponent, - RedactInformationComponent + RedactInformationComponent, + OrgInfoComponent, + OrgRecipientInfoComponent, + OrgAdminInfoComponent, + OrgOverviewComponent, + OrgUsersListComponent ] }) export class SharedModule { diff --git a/client/app/src/shared/guards/accreditor.guard.ts b/client/app/src/shared/guards/accreditor.guard.ts new file mode 100644 index 0000000000..0a570b4fdc --- /dev/null +++ b/client/app/src/shared/guards/accreditor.guard.ts @@ -0,0 +1,28 @@ +import {Injectable} from "@angular/core"; +import {Router, UrlTree} from "@angular/router"; +import {Observable} from "rxjs"; +import {AuthenticationService} from "@app/services/helper/authentication.service"; +import {AppConfigService} from "@app/services/root/app-config.service"; +import {UtilsService} from "@app/shared/services/utils.service"; + +@Injectable({ + providedIn: "root" +}) +export class AccreditorGuard { + constructor(private utilsService: UtilsService, private appConfigService: AppConfigService, private router: Router, public authenticationService: AuthenticationService) { + } + + canActivate(): Observable | Promise | boolean | UrlTree { + if (this.authenticationService.session) { + if(this.authenticationService.session.role === "accreditor"){ + this.appConfigService.setPage(this.router.url); + }else { + this.router.navigateByUrl("/login").then(); + } + return true; + } else { + this.utilsService.routeGuardRedirect(); + return false; + } + } +} diff --git a/client/app/src/shared/partials/org-admin-info/org-admin-info.component.html b/client/app/src/shared/partials/org-admin-info/org-admin-info.component.html new file mode 100644 index 0000000000..7630f67c19 --- /dev/null +++ b/client/app/src/shared/partials/org-admin-info/org-admin-info.component.html @@ -0,0 +1,20 @@ +
+
{{ 'Admin info' | translate }}
+
+
+ + +
+
+ + +
{{'Invalid email format' | translate}}
+
+
+
\ No newline at end of file diff --git a/client/app/src/shared/partials/org-admin-info/org-admin-info.component.ts b/client/app/src/shared/partials/org-admin-info/org-admin-info.component.ts new file mode 100644 index 0000000000..3da484c268 --- /dev/null +++ b/client/app/src/shared/partials/org-admin-info/org-admin-info.component.ts @@ -0,0 +1,18 @@ +import { Component } from '@angular/core'; +import { Constants } from '@app/shared/constants/constants'; + +@Component({ + selector: 'src-org-admin-info', + templateUrl: './org-admin-info.component.html' +}) +export class OrgAdminInfoComponent { + + adminInfo = { + name: '', + email: '', + fiscalCode: '' + }; + + protected readonly Constants = Constants; + +} diff --git a/client/app/src/shared/partials/org-info/org-info.component.html b/client/app/src/shared/partials/org-info/org-info.component.html new file mode 100644 index 0000000000..5661757db4 --- /dev/null +++ b/client/app/src/shared/partials/org-info/org-info.component.html @@ -0,0 +1,35 @@ +
+
{{ 'Organization info' | translate }}
+
+
+ + +
+
+ + +
{{'Invalid PEC format' | translate}}
+
+
+ + +
{{'Invalid PEC format' | translate}}
+
{{'PEC and Confirm PEC must match' | translate}}
+
+
+ + +
+
+
\ No newline at end of file diff --git a/client/app/src/shared/partials/org-info/org-info.component.ts b/client/app/src/shared/partials/org-info/org-info.component.ts new file mode 100644 index 0000000000..b1bdd83722 --- /dev/null +++ b/client/app/src/shared/partials/org-info/org-info.component.ts @@ -0,0 +1,26 @@ +import { Component } from '@angular/core'; +import { Constants } from "@app/shared/constants/constants"; +import {FormsModule} from '@angular/forms' + +@Component({ + selector: 'src-org-info', + templateUrl: './org-info.component.html' +}) +export class OrgInfoComponent { + + protected readonly Constants = Constants; + + organizationInfo = { + denomination: '', + pec: '', + confirmPec: '', + institutionalWebsite: '' + }; + + pecsMatch = true; + + checkPecsMatch() { + this.pecsMatch = this.organizationInfo.pec === this.organizationInfo.confirmPec; + } + +} diff --git a/client/app/src/shared/partials/org-overview/org-overview.component.html b/client/app/src/shared/partials/org-overview/org-overview.component.html new file mode 100644 index 0000000000..daaf25ac0c --- /dev/null +++ b/client/app/src/shared/partials/org-overview/org-overview.component.html @@ -0,0 +1,33 @@ +
+
+ {{'Overview'|translate}} + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + +
{{'Type'|translate}}{{'Accreditation date'|translate}}{{'Tips'|translate}}{{'Closed Tips'|translate}}{{'State'|translate}}
{{org.type}}{{org.accreditation_date}}{{org.num_tip}}{{org.num_tip}}{{org.state}}
+
+
+
diff --git a/client/app/src/shared/partials/org-overview/org-overview.component.ts b/client/app/src/shared/partials/org-overview/org-overview.component.ts new file mode 100644 index 0000000000..132eda04f4 --- /dev/null +++ b/client/app/src/shared/partials/org-overview/org-overview.component.ts @@ -0,0 +1,26 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { AccreditationRequestModel, OrganizationData } from '@app/models/accreditor/organization-data'; + +@Component({ + selector: 'src-org-overview', + templateUrl: './org-overview.component.html' +}) +export class OrgOverviewComponent implements OnInit { + + + @Input() org: OrganizationData + collapsed: boolean = false; + + + ngOnInit(): void { + + } + + + public toggleCollapse() { + this.collapsed = !this.collapsed; + } + + + +} diff --git a/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html b/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html new file mode 100644 index 0000000000..929dd7c1e5 --- /dev/null +++ b/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html @@ -0,0 +1,27 @@ +
+
{{ 'Primary recipient info' | translate }}
+
+
+ + +
+
+ + +
+
+ + +
{{'Invalid email format' | translate}}
+
+
+
\ No newline at end of file diff --git a/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts b/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts new file mode 100644 index 0000000000..640c300cae --- /dev/null +++ b/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts @@ -0,0 +1,17 @@ +import { Component } from '@angular/core'; +import { Constants } from '@app/shared/constants/constants'; + +@Component({ + selector: 'src-org-recipient-info', + templateUrl: './org-recipient-info.component.html' +}) +export class OrgRecipientInfoComponent { + + recipientInfo = { + name: '', + fiscalCode: '', + email: '' + }; + + protected readonly Constants = Constants; +} diff --git a/client/app/src/shared/partials/org-users-list/org-users-list.component.html b/client/app/src/shared/partials/org-users-list/org-users-list.component.html new file mode 100644 index 0000000000..d2a1274060 --- /dev/null +++ b/client/app/src/shared/partials/org-users-list/org-users-list.component.html @@ -0,0 +1,104 @@ +
+
+ {{'Users List'|translate}} + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + {{'Name' | translate}} + + + + + + + + + {{'Role' | translate}} + + + + + + + + + {{'Creation Date' | translate}} + + + + + + + + + {{'Last Access' | translate}} + + + + + + + + + {{'Tips' | translate}} + + + + + + + + + {{'Closed' | translate}} + + + + + +
{{user.id}}{{user.name}}{{user.role}}{{user.creation_date}}{{user.last_access}}{{user.tips}}{{user.closed_tips}}
+
+
+
\ No newline at end of file diff --git a/client/app/src/shared/partials/org-users-list/org-users-list.component.ts b/client/app/src/shared/partials/org-users-list/org-users-list.component.ts new file mode 100644 index 0000000000..e687dffaf8 --- /dev/null +++ b/client/app/src/shared/partials/org-users-list/org-users-list.component.ts @@ -0,0 +1,24 @@ +import { Component, OnInit } from '@angular/core'; +import { EOUser } from '@app/models/app/shared-public-model'; + +@Component({ + selector: 'src-org-users-list', + templateUrl: './org-users-list.component.html' +}) +export class OrgUsersListComponent implements OnInit { + + ngOnInit(): void { + + } + + collapsed: boolean = false; + users: EOUser[] = []; + sortKey: string = "creation_date"; + sortReverse: boolean = true; + + public toggleCollapse() { + this.collapsed = !this.collapsed; + } + + +} diff --git a/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts b/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts new file mode 100644 index 0000000000..19d4f86a15 --- /dev/null +++ b/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts @@ -0,0 +1,81 @@ +import { Injectable } from '@angular/core'; +import { AccreditationRequestModel } from '@app/models/accreditor/organization-data'; +import { UtilsService } from '../services/utils.service'; +import { HttpService } from '../services/http.service'; +import { AuthenticationService } from '@app/services/helper/authentication.service'; +import { map, Observable, of } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class AccreditationReqResolver { + + dataModel: AccreditationRequestModel[] = []; + + constructor(private utilsService: UtilsService, private httpService: HttpService, private authenticationService: AuthenticationService) { + } + + + + reload() { + // this.httpService.accreditorRequestResource().subscribe( + // (response) => { + // this.dataModel = response; + // this.utilsService.reloadComponent(); + // } + // ); + + //TODO MOCKUP + setTimeout(()=> { + this.utilsService.reloadComponent(); + }, 1000) + } + + resolve(): Observable { + if (this.authenticationService.session.role === "accreditor") { + // return this.httpService.accreditorRequestResource().pipe( + // map((response: AccreditationRequestModel[]) => { + // this.dataModel = response; + // return true; + // }) + // ); + + //TODO MOCKUP + setTimeout(() => { + this.dataModel = [ + { + id: "ID1", + denomination: "ORGANIZZAZIONE 1", + type: "NOT_AFFILIATED", + state: "REQUESTED", + accreditation_date: "10-08-2024", + num_user_profiled: 10, + num_tip: 5 + }, + { + id: "ID2", + denomination: "ORGANIZZAZIONE 2", + type: "AFFILIATED", + state: "INVITED", + accreditation_date: "12-08-2024", + num_user_profiled: 1, + num_tip: 0 + }, + { + id: "ID3", + denomination: "ORGANIZZAZIONE 3", + type: "NOT_AFFILIATED", + state: "REJECTED", + accreditation_date: "08-06-2024", + num_user_profiled: 2, + num_tip: 15 + } + + ] + return true; + }, 1000) + } + + return of(true); + } +} From 14169160a1a809db9a42f892ecc790a5ef821484 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Wed, 21 Aug 2024 16:41:52 +0200 Subject: [PATCH 003/607] Update __init__.py and enums.py --- backend/globaleaks/models/__init__.py | 7 ++++++- backend/globaleaks/models/enums.py | 13 +++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/backend/globaleaks/models/__init__.py b/backend/globaleaks/models/__init__.py index d62254a0ab..89cab8a8d0 100644 --- a/backend/globaleaks/models/__init__.py +++ b/backend/globaleaks/models/__init__.py @@ -590,6 +590,8 @@ class _InternalFile(Model): size = Column(JSON, default='', nullable=False) new = Column(Boolean, default=True, nullable=False) reference_id = Column(UnicodeText(36), default='', nullable=False) + verification_date = Column(DateTime, nullable=True) + state = Column(Enum(EnumStateFile), default='pending', nullable=False) @declared_attr def __table_args__(self): @@ -959,6 +961,7 @@ class _User(Model): readonly = Column(Boolean, default=False, nullable=False) two_factor_secret = Column(UnicodeText(32), default='', nullable=False) reminder_date = Column(DateTime, default=datetime_null, nullable=False) + status = Column(Enum(EnumUserStatus), default='active', nullable=False) # BEGIN of PGP key fields pgp_key_fingerprint = Column(UnicodeText, default='', nullable=False) @@ -974,7 +977,7 @@ class _User(Model): 'name', 'public_name', 'language', 'change_email_address', 'salt', - 'two_factor_secret'] + 'two_factor_secret', 'status'] localized_keys = ['description'] @@ -1022,9 +1025,11 @@ class _ReceiverFile(Model): size = Column(Integer, nullable=False) content_type = Column(UnicodeText, nullable=False) creation_date = Column(DateTime, default=datetime_now, nullable=False) + verification_date = Column(DateTime, nullable=True) access_date = Column(DateTime, default=datetime_null, nullable=False) description = Column(UnicodeText, default="", nullable=False) visibility = Column(Enum(EnumVisibility), default='public', nullable=False) + state = Column(Enum(EnumStateFile), default='pending', nullable=False) new = Column(Boolean, default=True, nullable=False) @declared_attr diff --git a/backend/globaleaks/models/enums.py b/backend/globaleaks/models/enums.py index db8a89fad3..eb94fcde3c 100644 --- a/backend/globaleaks/models/enums.py +++ b/backend/globaleaks/models/enums.py @@ -13,6 +13,12 @@ class EnumUserRole(_Enum): receiver = 1 custodian = 2 analyst = 3 + accreditor = 4 + + +class EnumUserStatus(_Enum): + active = 0 + suspend = 1 class EnumFieldInstance(_Enum): @@ -38,3 +44,10 @@ class EnumVisibility(_Enum): public = 0 internal = 1 personal = 2 + oe = 3 + + +class EnumStateFile(_Enum): + pending = 0 + verified = 1 + infected = 2 \ No newline at end of file From 794174614e417860576204da87d38919f33c7af4 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Thu, 22 Aug 2024 17:58:24 +0200 Subject: [PATCH 004/607] Update __init__.py and enums.py --- backend/globaleaks/models/__init__.py | 207 ++++++++++++++++++++++---- backend/globaleaks/models/enums.py | 11 +- 2 files changed, 185 insertions(+), 33 deletions(-) diff --git a/backend/globaleaks/models/__init__.py b/backend/globaleaks/models/__init__.py index 89cab8a8d0..4f74a9a89a 100644 --- a/backend/globaleaks/models/__init__.py +++ b/backend/globaleaks/models/__init__.py @@ -16,6 +16,7 @@ class LocalizationEngine(object): """ This Class can manage all the localized strings inside one ORM object """ + def __init__(self, keys): self._localized_strings = {} self._localized_keys = keys @@ -230,7 +231,8 @@ class _Comment(Model): @declared_attr def __table_args__(self): - return ForeignKeyConstraint(['internaltip_id'], ['internaltip.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), + return ForeignKeyConstraint(['internaltip_id'], ['internaltip.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED'), class _Config(Model): @@ -287,7 +289,8 @@ class _ConfigL10N(Model): @declared_attr def __table_args__(self): - return ForeignKeyConstraint(['tid', 'lang'], ['enabledlanguage.tid', 'enabledlanguage.name'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), + return ForeignKeyConstraint(['tid', 'lang'], ['enabledlanguage.tid', 'enabledlanguage.name'], + ondelete='CASCADE', deferrable=True, initially='DEFERRED'), def __init__(self, values=None): if values is None: @@ -422,10 +425,13 @@ class _Field(Model): @declared_attr def __table_args__(self): return (ForeignKeyConstraint(['tid'], ['tenant.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), - ForeignKeyConstraint(['step_id'], ['step.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), - ForeignKeyConstraint(['fieldgroup_id'], ['field.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), + ForeignKeyConstraint(['step_id'], ['step.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED'), + ForeignKeyConstraint(['fieldgroup_id'], ['field.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED'), ForeignKeyConstraint(['template_id'], ['field.id'], deferrable=True, initially='DEFERRED'), - ForeignKeyConstraint(['template_override_id'], ['field.id'], ondelete='SET NULL', deferrable=True, initially='DEFERRED'), + ForeignKeyConstraint(['template_override_id'], ['field.id'], ondelete='SET NULL', deferrable=True, + initially='DEFERRED'), CheckConstraint(self.instance.in_(EnumFieldInstance.keys()))) unicode_keys = ['type', 'instance', 'key'] @@ -448,7 +454,8 @@ class _FieldAttr(Model): @declared_attr def __table_args__(self): return (UniqueConstraint('field_id', 'name'), - ForeignKeyConstraint(['field_id'], ['field.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), + ForeignKeyConstraint(['field_id'], ['field.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED'), CheckConstraint(self.type.in_(EnumFieldAttrType.keys()))) def update(self, values=None): @@ -491,7 +498,8 @@ class _FieldOption(Model): @declared_attr def __table_args__(self): - return ForeignKeyConstraint(['field_id'], ['field.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), + return ForeignKeyConstraint(['field_id'], ['field.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED'), class _FieldOptionTriggerField(Model): @@ -503,8 +511,10 @@ class _FieldOptionTriggerField(Model): @declared_attr def __table_args__(self): - return (ForeignKeyConstraint(['option_id'], ['fieldoption.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), - ForeignKeyConstraint(['object_id'], ['field.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED')) + return (ForeignKeyConstraint(['option_id'], ['fieldoption.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED'), + ForeignKeyConstraint(['object_id'], ['field.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED')) class _FieldOptionTriggerStep(Model): @@ -516,8 +526,10 @@ class _FieldOptionTriggerStep(Model): @declared_attr def __table_args__(self): - return (ForeignKeyConstraint(['option_id'], ['fieldoption.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), - ForeignKeyConstraint(['object_id'], ['step.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED')) + return (ForeignKeyConstraint(['option_id'], ['fieldoption.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED'), + ForeignKeyConstraint(['object_id'], ['step.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED')) class _File(Model): @@ -557,7 +569,8 @@ class _IdentityAccessRequest(Model): @declared_attr def __table_args__(self): - return ForeignKeyConstraint(['internaltip_id'], ['internaltip.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), + return ForeignKeyConstraint(['internaltip_id'], ['internaltip.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED'), class _IdentityAccessRequestCustodian(Model): @@ -572,8 +585,48 @@ class _IdentityAccessRequestCustodian(Model): @declared_attr def __table_args__(self): - return (ForeignKeyConstraint(['identityaccessrequest_id'], ['identityaccessrequest.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), - ForeignKeyConstraint(['custodian_id'], ['user.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED')) + return (ForeignKeyConstraint(['identityaccessrequest_id'], ['identityaccessrequest.id'], ondelete='CASCADE', + deferrable=True, initially='DEFERRED'), + ForeignKeyConstraint(['custodian_id'], ['user.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED')) + + +class _InternalFileForwarding(Model): + """ + This model keeps track of submission files for the oe + """ + __tablename__ = 'internalfile_forwarding' + + id = Column(UnicodeText(36), primary_key=True, default=uuid4) + tid = Column(Integer, default=1, nullable=False) + internaltip_id = Column(UnicodeText(36), nullable=False, index=True) + internalfile_id = Column(UnicodeText(36), nullable=False, index=True) + + @declared_attr + def __table_args__(self): + return ( + ForeignKeyConstraint( + ['tid'], + ['tenant.id'], + ondelete='CASCADE', + deferrable=True, + initially='DEFERRED' + ), + ForeignKeyConstraint( + ['internaltip_id'], + ['internaltip.id'], + ondelete='CASCADE', + deferrable=True, + initially='DEFERRED' + ), + ForeignKeyConstraint( + ['internalfile_id'], + ['internalfile.id'], + ondelete='CASCADE', + deferrable=True, + initially='DEFERRED' + ) + ) class _InternalFile(Model): @@ -595,7 +648,8 @@ class _InternalFile(Model): @declared_attr def __table_args__(self): - return ForeignKeyConstraint(['internaltip_id'], ['internaltip.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), + return ForeignKeyConstraint(['internaltip_id'], ['internaltip.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED'), class _InternalTip(Model): @@ -636,7 +690,8 @@ def __table_args__(self): return (UniqueConstraint('tid', 'progressive'), UniqueConstraint('tid', 'receipt_hash'), ForeignKeyConstraint(['tid'], ['tenant.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), - ForeignKeyConstraint(['context_id'], ['context.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED')) + ForeignKeyConstraint(['context_id'], ['context.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED')) class _InternalTipAnswers(Model): @@ -652,7 +707,8 @@ class _InternalTipAnswers(Model): @declared_attr def __table_args__(self): - return ForeignKeyConstraint(['internaltip_id'], ['internaltip.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), + return ForeignKeyConstraint(['internaltip_id'], ['internaltip.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED'), class _InternalTipData(Model): @@ -666,7 +722,8 @@ class _InternalTipData(Model): @declared_attr def __table_args__(self): return (UniqueConstraint('internaltip_id', 'key'), - ForeignKeyConstraint(['internaltip_id'], ['internaltip.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED')) + ForeignKeyConstraint(['internaltip_id'], ['internaltip.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED')) class _Mail(Model): @@ -719,8 +776,11 @@ class _ReceiverContext(Model): @declared_attr def __table_args__(self): - return (ForeignKeyConstraint(['context_id'], ['context.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), - ForeignKeyConstraint(['receiver_id'], ['user.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED')) + return ( + ForeignKeyConstraint(['context_id'], ['context.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED'), + ForeignKeyConstraint(['receiver_id'], ['user.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED')) class _WhistleblowerFile(Model): @@ -737,8 +797,10 @@ class _WhistleblowerFile(Model): @declared_attr def __table_args__(self): - return (ForeignKeyConstraint(['internalfile_id'], ['internalfile.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), - ForeignKeyConstraint(['receivertip_id'], ['receivertip.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED')) + return (ForeignKeyConstraint(['internalfile_id'], ['internalfile.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED'), + ForeignKeyConstraint(['receivertip_id'], ['receivertip.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED')) class _ReceiverTip(Model): @@ -762,8 +824,11 @@ class _ReceiverTip(Model): @declared_attr def __table_args__(self): - return (ForeignKeyConstraint(['receiver_id'], ['user.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), - ForeignKeyConstraint(['internaltip_id'], ['internaltip.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED')) + return ( + ForeignKeyConstraint(['receiver_id'], ['user.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED'), + ForeignKeyConstraint(['internaltip_id'], ['internaltip.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED')) class _Redaction(Model): @@ -782,7 +847,8 @@ class _Redaction(Model): @declared_attr def __table_args__(self): - return ForeignKeyConstraint(['internaltip_id'], ['internaltip.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), + return ForeignKeyConstraint(['internaltip_id'], ['internaltip.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED'), class _Redirect(Model): @@ -819,7 +885,8 @@ class _Step(Model): @declared_attr def __table_args__(self): - return ForeignKeyConstraint(['questionnaire_id'], ['questionnaire.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), + return ForeignKeyConstraint(['questionnaire_id'], ['questionnaire.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED'), class _SubmissionStatus(Model): @@ -863,7 +930,8 @@ class _SubmissionSubStatus(Model): @declared_attr def __table_args__(self): - return ForeignKeyConstraint(['tid', 'submissionstatus_id'], ['submissionstatus.tid', 'submissionstatus.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), + return ForeignKeyConstraint(['tid', 'submissionstatus_id'], ['submissionstatus.tid', 'submissionstatus.id'], + ondelete='CASCADE', deferrable=True, initially='DEFERRED'), class _Subscriber(Model): @@ -887,10 +955,25 @@ class _Subscriber(Model): tos1 = Column(UnicodeText, default='', nullable=False) tos2 = Column(UnicodeText, default='', nullable=False) + creation_date = Column(DateTime, default=datetime_now, nullable=False) + state = Column(Enum(EnumSubscriberStatus), nullable=True) + organization_institutional_site = Column(UnicodeText, default='', nullable=False) + accreditation_date = Column(DateTime, nullable=True) + admin_name = Column(UnicodeText, nullable=True) + admin_surname = Column(UnicodeText, nullable=True) + admin_email = Column(UnicodeText, nullable=True) + admin_fiscal_code = Column(UnicodeText, nullable=True) + recipient_name = Column(UnicodeText, nullable=True) + recipient_surname = Column(UnicodeText, nullable=True) + recipient_email = Column(UnicodeText, nullable=True) + recipient_fiscal_code = Column(UnicodeText, nullable=True) + unicode_keys = ['subdomain', 'language', 'name', 'surname', 'phone', 'email', - 'organization_name', 'organization_tax_code', + 'organization_name', 'organization_tax_code', 'organization_vat_code', 'organization_location', - 'client_ip_address', 'client_user_agent'] + 'client_ip_address', 'client_user_agent', 'state', 'organization_institutional_site', + 'admin_name', 'admin_surname', 'admin_email', 'admin_fiscal_code', 'recipient_name', + 'recipient_surname', 'recipient_email', 'recipient_fiscal_code'] bool_keys = ['tos1', 'tos2'] @@ -908,14 +991,65 @@ class _Tenant(Model): __tablename__ = 'tenant' __table_args__ = {'sqlite_autoincrement': True} - id = Column(Integer, primary_key=True) creation_date = Column(DateTime, default=datetime_now, nullable=False) active = Column(Boolean, default=False, nullable=False) + affiliated = Column(Boolean, nullable=True) + external = Column(Boolean, default=False, nullable=False) bool_keys = ['active'] +class _InternalTipForwarding(Model): + """ + This model keeps track of forward tip. + """ + __tablename__ = 'internaltip_forwarding' + id = Column(UnicodeText(36), primary_key=True, default=uuid4) + internaltip_id = Column(UnicodeText(36), nullable=False, index=True) + oe_internaltip_id = Column(UnicodeText(36), nullable=False, index=True) + tid = Column(Integer, default=1, nullable=False) + creation_date = Column(DateTime, default=datetime_now, nullable=False) + update_date = Column(DateTime, default=datetime_now, nullable=False) + text = Column(UnicodeText, nullable=False) + comment = Column(UnicodeText, nullable=False) + data = Column(UnicodeText, nullable=False) + questionnaire_id = Column(UnicodeText(36), nullable=False, index=True) + + @declared_attr + def __table_args__(self): + return ( + ForeignKeyConstraint( + ['internaltip_id'], + ['internaltip.id'], + ondelete='CASCADE', + deferrable=True, + initially='DEFERRED' + ), + ForeignKeyConstraint( + ['oe_internaltip_id'], + ['internaltip.id'], + ondelete='CASCADE', + deferrable=True, + initially='DEFERRED' + ), + ForeignKeyConstraint( + ['questionnaire_id'], + ['questionnaire.id'], + ondelete='CASCADE', + deferrable=True, + initially='DEFERRED' + ), + ForeignKeyConstraint( + ['tid'], + ['tenant.id'], + ondelete='CASCADE', + deferrable=True, + initially='DEFERRED' + ) + ) + + class _User(Model): """ This model keeps track of users. @@ -1012,7 +1146,7 @@ def __table_args__(self): class _ReceiverFile(Model): """ - This models stores metadata of files uploaded by recipients intended to be + This models stores metadata of files uploaded by recipients intended to bes delivered to the whistleblower. This file is not encrypted and nor is it integrity checked in any meaningful way. """ @@ -1034,7 +1168,8 @@ class _ReceiverFile(Model): @declared_attr def __table_args__(self): - return (ForeignKeyConstraint(['internaltip_id'], ['internaltip.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), + return (ForeignKeyConstraint(['internaltip_id'], ['internaltip.id'], ondelete='CASCADE', deferrable=True, + initially='DEFERRED'), CheckConstraint(self.visibility.in_(EnumVisibility.keys()))) @@ -1172,3 +1307,11 @@ class User(_User, Base): class WhistleblowerFile(_WhistleblowerFile, Base): pass + + +class InternalTipForwarding(_InternalTipForwarding, Base): + pass + + +class InternalFileForwarding(_InternalFileForwarding, Base): + pass diff --git a/backend/globaleaks/models/enums.py b/backend/globaleaks/models/enums.py index eb94fcde3c..e8899517b2 100644 --- a/backend/globaleaks/models/enums.py +++ b/backend/globaleaks/models/enums.py @@ -50,4 +50,13 @@ class EnumVisibility(_Enum): class EnumStateFile(_Enum): pending = 0 verified = 1 - infected = 2 \ No newline at end of file + infected = 2 + + +class EnumSubscriberStatus(_Enum): + requested = 0 + accredited = 1 + rejected = 2 + instructor_request = 3 + invited = 4 + suspend = 5 From 0a50ea757b8874e9ab9fcef48df68307e36af9a1 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Fri, 23 Aug 2024 13:13:38 +0200 Subject: [PATCH 005/607] Update __init__.py, migration.py, and __init__.py --- backend/globaleaks/__init__.py | 2 +- backend/globaleaks/db/migration.py | 76 +++--- .../db/migrations/update_69/__init__.py | 258 ++++++++++++++++++ 3 files changed, 298 insertions(+), 38 deletions(-) create mode 100644 backend/globaleaks/db/migrations/update_69/__init__.py diff --git a/backend/globaleaks/__init__.py b/backend/globaleaks/__init__.py index e9d85b04fe..8b0b8f8ea2 100644 --- a/backend/globaleaks/__init__.py +++ b/backend/globaleaks/__init__.py @@ -10,7 +10,7 @@ __version__ = '5.0.0' __license__ = 'AGPL-3.0' -DATABASE_VERSION = 68 +DATABASE_VERSION = 69 FIRST_DATABASE_VERSION_SUPPORTED = 52 # Add new languages as they are supported here! To do this retrieve the name of diff --git a/backend/globaleaks/db/migration.py b/backend/globaleaks/db/migration.py index c1407cbf0c..d3967f6731 100644 --- a/backend/globaleaks/db/migration.py +++ b/backend/globaleaks/db/migration.py @@ -12,6 +12,8 @@ from globaleaks import __version__, models, \ DATABASE_VERSION, FIRST_DATABASE_VERSION_SUPPORTED, LANGUAGES_SUPPORTED_CODES from globaleaks.db.appdata import load_appdata, db_load_defaults +from globaleaks.db.migrations.update_69 import User_v_68, Tenant_v_68, Subscriber_v_68, InternalFile_v_68, \ + ReceiverFile_v_68 from globaleaks.orm import db_log from globaleaks.db.migrations.update_53 import FieldAttr_v_52, InternalTip_v_52, \ @@ -47,44 +49,44 @@ migration_mapping = OrderedDict([ - ('ArchivedSchema', [models._ArchivedSchema, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - ('AuditLog', [-1, -1, AuditLog_v_61, 0, 0, 0, 0, 0, 0, 0, models._AuditLog, 0, 0, 0, 0, 0, 0]), - ('Comment', [Comment_v_64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, models._Comment, 0, 0, 0]), - ('Config', [models._Config, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - ('ConfigL10N', [models._ConfigL10N, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - ('Context', [Context_v_61, 0, 0, 0, 0, 0, 0, 0, 0, 0, Context_v_63, 0, models._Context, 0, 0, 0, 0]), - ('CustomTexts', [models._CustomTexts, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - ('EnabledLanguage', [models._EnabledLanguage, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - ('Field', [models._Field, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ('ArchivedSchema', [models._ArchivedSchema, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ('AuditLog', [-1, -1, AuditLog_v_61, 0, 0, 0, 0, 0, 0, 0, models._AuditLog, 0, 0, 0, 0, 0, 0, 0]), + ('Comment', [Comment_v_64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, models._Comment, 0, 0, 0, 0]), + ('Config', [models._Config, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ('ConfigL10N', [models._ConfigL10N, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ('Context', [Context_v_61, 0, 0, 0, 0, 0, 0, 0, 0, 0, Context_v_63, 0, models._Context, 0, 0, 0, 0, 0]), + ('CustomTexts', [models._CustomTexts, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ('EnabledLanguage', [models._EnabledLanguage, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ('Field', [models._Field, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), ('FieldAttr', [FieldAttr_v_52, models._FieldAttr, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - ('FieldOption', [models._FieldOption, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - ('FieldOptionTriggerField', [models._FieldOptionTriggerField, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - ('FieldOptionTriggerStep', [models._FieldOptionTriggerStep, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - ('File', [File_v_53, 0, models._File, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - ('IdentityAccessRequest', [IdentityAccessRequest_v_64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, models._IdentityAccessRequest, 0, 0, 0]), - ('IdentityAccessRequestCustodian', [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, models._IdentityAccessRequestCustodian, 0, 0, 0]), - ('InternalFile', [InternalFile_v_64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, models._InternalFile, 0, 0, 0]), - ('InternalTip', [InternalTip_v_52, InternalTip_v_57, 0, 0, 0, 0, InternalTip_v_59, 0, InternalTip_v_63, 0, 0, 0, InternalTip_v_64, InternalTip_v_66, 0, models._InternalTip, 0]), - ('InternalTipAnswers', [models._InternalTipAnswers, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - ('InternalTipData', [models._InternalTipData, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - ('Mail', [models._Mail, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - ('Message', [Message_v_64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1]), - ('Questionnaire', [models._Questionnaire, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - ('ReceiverContext', [models._ReceiverContext, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - ('ReceiverFile', [ReceiverFile_v_57, 0, 0, 0, 0, 0, ReceiverFile_v_64, 0, 0, 0, 0, 0, 0, ReceiverFile_v_66, 0, models._ReceiverFile, 0]), - ('ReceiverTip', [ReceiverTip_v_52, ReceiverTip_v_57, 0, 0, 0, 0, ReceiverTip_v_58, ReceiverTip_v_59, ReceiverTip_v_61, 0, ReceiverTip_v_64, 0, 0, models._ReceiverTip, 0, 0, 0]), - ('Redaction', [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, Redaction_v_66, 0, models._Redaction, 0]), - ('Redirect', [models._Redirect, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - ('SubmissionStatus', [SubmissionStatus_v_64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, models._SubmissionStatus, 0, 0]), - ('SubmissionSubStatus', [SubmissionSubStatus_v_64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, SubmissionSubStatus_v_65, 0, models._SubmissionSubStatus, 0]), - ('SubmissionStatusChange', [SubmissionStatusChange_v_54, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]), - ('Step', [models._Step, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - ('Subscriber', [Subscriber_v_52, Subscriber_v_62, 0, 0, 0, 0, 0, 0, 0, 0, 0, Subscriber_v_67, 0, 0, 0, 0, models._Subscriber]), - ('Tenant', [Tenant_v_52, models._Tenant, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), - ('User', [User_v_52, User_v_54, 0, User_v_56, 0, User_v_61, 0, 0, 0, 0, User_v_64, 0, 0, User_v_66, 0, models._User, 0]), - ('WhistleblowerFile', [WhistleblowerFile_v_57, 0, 0, 0, 0, 0, WhistleblowerFile_v_64, 0, 0, 0, 0, 0, 0, WhistleblowerFile_v_66, 0, models._WhistleblowerFile, 0]), - - ('WhistleblowerTip', [WhistleblowerTip_v_59, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1]) + ('FieldOption', [models._FieldOption, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ('FieldOptionTriggerField', [models._FieldOptionTriggerField, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ('FieldOptionTriggerStep', [models._FieldOptionTriggerStep, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ('File', [File_v_53, 0, models._File, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ('IdentityAccessRequest', [IdentityAccessRequest_v_64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, models._IdentityAccessRequest, 0, 0, 0, 0]), + ('IdentityAccessRequestCustodian', [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, models._IdentityAccessRequestCustodian, 0, 0, 0, 0]), + ('InternalFile', [InternalFile_v_64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, InternalFile_v_68, 0, 0, 0, models._InternalFile]), + ('InternalTip', [InternalTip_v_52, InternalTip_v_57, 0, 0, 0, 0, InternalTip_v_59, 0, InternalTip_v_63, 0, 0, 0, InternalTip_v_64, InternalTip_v_66, 0, models._InternalTip, 0, 0]), + ('InternalTipAnswers', [models._InternalTipAnswers, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ('InternalTipData', [models._InternalTipData, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ('Mail', [models._Mail, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ('Message', [Message_v_64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1]), + ('Questionnaire', [models._Questionnaire, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ('ReceiverContext', [models._ReceiverContext, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ('ReceiverFile', [ReceiverFile_v_57, 0, 0, 0, 0, 0, ReceiverFile_v_64, 0, 0, 0, 0, 0, 0, ReceiverFile_v_66, 0, ReceiverFile_v_68, 0, models._ReceiverFile]), + ('ReceiverTip', [ReceiverTip_v_52, ReceiverTip_v_57, 0, 0, 0, 0, ReceiverTip_v_58, ReceiverTip_v_59, ReceiverTip_v_61, 0, ReceiverTip_v_64, 0, 0, models._ReceiverTip, 0, 0, 0, 0]), + ('Redaction', [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, Redaction_v_66, 0, models._Redaction, 0, 0]), + ('Redirect', [models._Redirect, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ('SubmissionStatus', [SubmissionStatus_v_64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, models._SubmissionStatus, 0, 0, 0]), + ('SubmissionSubStatus', [SubmissionSubStatus_v_64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, SubmissionSubStatus_v_65, 0, models._SubmissionSubStatus, 0, 0]), + ('SubmissionStatusChange', [SubmissionStatusChange_v_54, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]), + ('Step', [models._Step, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), + ('Subscriber', [Subscriber_v_52, Subscriber_v_62, 0, 0, 0, 0, 0, 0, 0, 0, 0, Subscriber_v_67, 0, 0, 0, 0, Subscriber_v_68, models._Subscriber]), + ('Tenant', [Tenant_v_52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Tenant_v_68, models._Tenant]), + ('User', [User_v_52, User_v_54, 0, User_v_56, 0, User_v_61, 0, 0, 0, 0, User_v_64, 0, 0, User_v_66, 0, User_v_68, 0, models._User]), + ('WhistleblowerFile', [WhistleblowerFile_v_57, 0, 0, 0, 0, 0, WhistleblowerFile_v_64, 0, 0, 0, 0, 0, 0, WhistleblowerFile_v_66, 0, models._WhistleblowerFile, 0, 0]), + ('InternalFileForwarding', [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, models._InternalTipForwarding]), + ('WhistleblowerTip', [WhistleblowerTip_v_59, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]) ]) diff --git a/backend/globaleaks/db/migrations/update_69/__init__.py b/backend/globaleaks/db/migrations/update_69/__init__.py new file mode 100644 index 0000000000..8dbbce0797 --- /dev/null +++ b/backend/globaleaks/db/migrations/update_69/__init__.py @@ -0,0 +1,258 @@ +from globaleaks.db.migrations.update import MigrationBase +from globaleaks.models import Model, EnumSubscriberStatus, EnumStateFile, EnumVisibility, EnumUserRole, EnumUserStatus +from globaleaks.models.properties import * +from globaleaks.utils.utility import datetime_now, datetime_null + + +class Subscriber_v_68(Model): + __tablename__ = 'subscriber' + + tid = Column(Integer, primary_key=True) + subdomain = Column(UnicodeText, unique=True, nullable=False) + language = Column(UnicodeText(12), nullable=False) + name = Column(UnicodeText, nullable=False) + surname = Column(UnicodeText, nullable=False) + phone = Column(UnicodeText, default='', nullable=False) + email = Column(UnicodeText, nullable=False) + organization_name = Column(UnicodeText, default='', nullable=False) + organization_tax_code = Column(UnicodeText, unique=True, nullable=True) + organization_vat_code = Column(UnicodeText, unique=True, nullable=True) + organization_location = Column(UnicodeText, default='', nullable=False) + activation_token = Column(UnicodeText, unique=True) + client_ip_address = Column(UnicodeText, nullable=False) + client_user_agent = Column(UnicodeText, nullable=False) + registration_date = Column(DateTime, default=datetime_now, nullable=False) + tos1 = Column(UnicodeText, default='', nullable=False) + tos2 = Column(UnicodeText, default='', nullable=False) + """ + creation_date = Column(DateTime, default=datetime_now, nullable=False) + state = Column(Enum(EnumSubscriberStatus), nullable=True) + organization_institutional_site = Column(UnicodeText, default='', nullable=False) + accreditation_date = Column(DateTime, nullable=True) + admin_name = Column(UnicodeText, nullable=True) + admin_surname = Column(UnicodeText, nullable=True) + admin_email = Column(UnicodeText, nullable=True) + admin_fiscal_code = Column(UnicodeText, nullable=True) + recipient_name = Column(UnicodeText, nullable=True) + recipient_surname = Column(UnicodeText, nullable=True) + recipient_email = Column(UnicodeText, nullable=True) + recipient_fiscal_code = Column(UnicodeText, nullable=True) + """ + + +class Tenant_v_68(Model): + __tablename__ = 'tenant' + + id = Column(Integer, primary_key=True) + creation_date = Column(DateTime, default=datetime_now, nullable=False) + active = Column(Boolean, default=False, nullable=False) + # affiliated = Column(Boolean, nullable=True) + # external = Column(Boolean, default=False, nullable=False) + + +""" +class InternalTipForwarding_v_68(Model): + + __tablename__ = 'internaltip_forwarding' + id = Column(UnicodeText(36), primary_key=True, default=uuid4) + internaltip_id = Column(UnicodeText(36), nullable=False, index=True) + oe_internaltip_id = Column(UnicodeText(36), nullable=False, index=True) + tid = Column(Integer, default=1, nullable=False) + creation_date = Column(DateTime, default=datetime_now, nullable=False) + update_date = Column(DateTime, default=datetime_now, nullable=False) + text = Column(UnicodeText, nullable=False) + comment = Column(UnicodeText, nullable=False) + data = Column(UnicodeText, nullable=False) + questionnaire_id = Column(UnicodeText(36), nullable=False, index=True) + + @declared_attr + def __table_args__(self): + return ( + ForeignKeyConstraint( + ['internaltip_id'], + ['internaltip.id'], + ondelete='CASCADE', + deferrable=True, + initially='DEFERRED' + ), + ForeignKeyConstraint( + ['oe_internaltip_id'], + ['internaltip.id'], + ondelete='CASCADE', + deferrable=True, + initially='DEFERRED' + ), + ForeignKeyConstraint( + ['questionnaire_id'], + ['questionnaire.id'], + ondelete='CASCADE', + deferrable=True, + initially='DEFERRED' + ), + ForeignKeyConstraint( + ['tid'], + ['tenant.id'], + ondelete='CASCADE', + deferrable=True, + initially='DEFERRED' + ) + ) +""" + +class InternalFile_v_68(Model): + """ + This model keeps track of submission files + """ + __tablename__ = 'internalfile' + + id = Column(UnicodeText(36), primary_key=True, default=uuid4) + creation_date = Column(DateTime, default=datetime_now, nullable=False) + # filename = Column(UnicodeText, default='', nullable=False) + internaltip_id = Column(UnicodeText(36), nullable=False, index=True) + name = Column(UnicodeText, nullable=False) + content_type = Column(JSON, default='', nullable=False) + size = Column(JSON, default='', nullable=False) + new = Column(Boolean, default=True, nullable=False) + reference_id = Column(UnicodeText(36), default='', nullable=False) + # verification_date = Column(DateTime, nullable=True) + # state = Column(Enum(EnumStateFile), default='pending', nullable=False) + + +class ReceiverFile_v_68(Model): + """ + This models stores metadata of files uploaded by recipients intended to bes + delivered to the whistleblower. This file is not encrypted and nor is it + integrity checked in any meaningful way. + """ + __tablename__ = 'receiverfile' + + id = Column(UnicodeText(36), primary_key=True, default=uuid4) + internaltip_id = Column(UnicodeText(36), nullable=False, index=True) + author_id = Column(UnicodeText(36)) + name = Column(UnicodeText, nullable=False) + size = Column(Integer, nullable=False) + content_type = Column(UnicodeText, nullable=False) + creation_date = Column(DateTime, default=datetime_now, nullable=False) + # verification_date = Column(DateTime, nullable=True) + access_date = Column(DateTime, default=datetime_null, nullable=False) + description = Column(UnicodeText, default="", nullable=False) + visibility = Column(Enum(EnumVisibility), default='public', nullable=False) + # state = Column(Enum(EnumStateFile), default='pending', nullable=False) + new = Column(Boolean, default=True, nullable=False) + + +class User_v_68(Model): + """ + This model keeps track of users. + """ + __tablename__ = 'user' + + id = Column(UnicodeText(36), primary_key=True, default=uuid4) + tid = Column(Integer, default=1, nullable=False) + creation_date = Column(DateTime, default=datetime_now, nullable=False) + username = Column(UnicodeText, default='', nullable=False) + salt = Column(UnicodeText(24), default='', nullable=False) + hash = Column(UnicodeText(44), default='', nullable=False) + name = Column(UnicodeText, default='', nullable=False) + description = Column(JSON, default=dict, nullable=False) + public_name = Column(UnicodeText, default='', nullable=False) + role = Column(Enum(EnumUserRole), default='receiver', nullable=False) + enabled = Column(Boolean, default=True, nullable=False) + last_login = Column(DateTime, default=datetime_null, nullable=False) + mail_address = Column(UnicodeText, default='', nullable=False) + language = Column(UnicodeText(12), nullable=False) + password_change_needed = Column(Boolean, default=True, nullable=False) + password_change_date = Column(DateTime, default=datetime_null, nullable=False) + crypto_prv_key = Column(UnicodeText(84), default='', nullable=False) + crypto_pub_key = Column(UnicodeText(56), default='', nullable=False) + crypto_rec_key = Column(UnicodeText(80), default='', nullable=False) + crypto_bkp_key = Column(UnicodeText(84), default='', nullable=False) + crypto_escrow_prv_key = Column(UnicodeText(84), default='', nullable=False) + crypto_escrow_bkp1_key = Column(UnicodeText(84), default='', nullable=False) + crypto_escrow_bkp2_key = Column(UnicodeText(84), default='', nullable=False) + change_email_address = Column(UnicodeText, default='', nullable=False) + change_email_token = Column(UnicodeText, unique=True) + change_email_date = Column(DateTime, default=datetime_null, nullable=False) + notification = Column(Boolean, default=True, nullable=False) + forcefully_selected = Column(Boolean, default=False, nullable=False) + can_delete_submission = Column(Boolean, default=False, nullable=False) + can_postpone_expiration = Column(Boolean, default=True, nullable=False) + can_grant_access_to_reports = Column(Boolean, default=False, nullable=False) + can_transfer_access_to_reports = Column(Boolean, default=False, nullable=False) + can_redact_information = Column(Boolean, default=False, nullable=False) + can_mask_information = Column(Boolean, default=True, nullable=False) + can_reopen_reports = Column(Boolean, default=True, nullable=False) + can_edit_general_settings = Column(Boolean, default=False, nullable=False) + readonly = Column(Boolean, default=False, nullable=False) + two_factor_secret = Column(UnicodeText(32), default='', nullable=False) + reminder_date = Column(DateTime, default=datetime_null, nullable=False) + # status = Column(Enum(EnumUserStatus), default='active', nullable=False) + + # BEGIN of PGP key fields + pgp_key_fingerprint = Column(UnicodeText, default='', nullable=False) + pgp_key_public = Column(UnicodeText, default='', nullable=False) + pgp_key_expiration = Column(DateTime, default=datetime_null, nullable=False) + # END of PGP key fields + + accepted_privacy_policy = Column(DateTime, default=datetime_null, nullable=False) + clicked_recovery_key = Column(Boolean, default=False, nullable=False) + +""" +class InternalFileForwarding_v_68(Model): + __tablename__ = 'internalfile_forwarding' + + id = Column(UnicodeText(36), primary_key=True, default=uuid4) + tid = Column(Integer, default=1, nullable=False) + internaltip_id = Column(UnicodeText(36), nullable=False, index=True) + internalfile_id = Column(UnicodeText(36), nullable=False, index=True) + + @declared_attr + def __table_args__(self): + return ( + ForeignKeyConstraint( + ['tid'], + ['tenant.id'], + ondelete='CASCADE', + deferrable=True, + initially='DEFERRED' + ), + ForeignKeyConstraint( + ['internaltip_id'], + ['internaltip.id'], + ondelete='CASCADE', + deferrable=True, + initially='DEFERRED' + ), + ForeignKeyConstraint( + ['internalfile_id'], + ['internalfile.id'], + ondelete='CASCADE', + deferrable=True, + initially='DEFERRED' + ) + ) +""" + +class MigrationScript(MigrationBase): + + def epilogue(self): + """for model in ['ReceiverFile', 'User', 'InternalFile', + 'Tenant']: + for old_obj in self.session_old.query(self.model_from[model]): + new_obj = self.model_to[model]() + for key in new_obj.__mapper__.column_attrs.keys(): + if model == 'User' and key == 'status': + setattr(new_obj, key, 'active') + elif model == 'InternalFile' and key == 'verification_date': + setattr(new_obj, key, None) + elif model == 'InternalFile' and key == 'state': + setattr(new_obj, key, 'pending') + elif model == 'Tenant' and key == 'affiliated': + setattr(new_obj, key, None) + elif model == 'Tenant' and key == 'external': + setattr(new_obj, key, False) + else: + setattr(new_obj, key, getattr(old_obj, key)) + self.session_new.add(new_obj) + """ + pass From 4b9b879bdb9e3dbabd056dd178bf885ff3173898 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Fri, 23 Aug 2024 18:46:53 +0200 Subject: [PATCH 006/607] Update migration.py and __init__.py --- backend/globaleaks/db/migration.py | 3 ++- backend/globaleaks/models/__init__.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/globaleaks/db/migration.py b/backend/globaleaks/db/migration.py index d3967f6731..c6c71a8bf5 100644 --- a/backend/globaleaks/db/migration.py +++ b/backend/globaleaks/db/migration.py @@ -85,7 +85,8 @@ ('Tenant', [Tenant_v_52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Tenant_v_68, models._Tenant]), ('User', [User_v_52, User_v_54, 0, User_v_56, 0, User_v_61, 0, 0, 0, 0, User_v_64, 0, 0, User_v_66, 0, User_v_68, 0, models._User]), ('WhistleblowerFile', [WhistleblowerFile_v_57, 0, 0, 0, 0, 0, WhistleblowerFile_v_64, 0, 0, 0, 0, 0, 0, WhistleblowerFile_v_66, 0, models._WhistleblowerFile, 0, 0]), - ('InternalFileForwarding', [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, models._InternalTipForwarding]), + ('InternalTipForwarding', [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, models._InternalTipForwarding]), + ('InternalFileForwarding', [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, models._InternalFileForwarding]), ('WhistleblowerTip', [WhistleblowerTip_v_59, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]) ]) diff --git a/backend/globaleaks/models/__init__.py b/backend/globaleaks/models/__init__.py index 4f74a9a89a..106ac9e8e1 100644 --- a/backend/globaleaks/models/__init__.py +++ b/backend/globaleaks/models/__init__.py @@ -956,7 +956,7 @@ class _Subscriber(Model): tos2 = Column(UnicodeText, default='', nullable=False) creation_date = Column(DateTime, default=datetime_now, nullable=False) - state = Column(Enum(EnumSubscriberStatus), nullable=True) + state = Column(Integer, default=None, nullable=True) organization_institutional_site = Column(UnicodeText, default='', nullable=False) accreditation_date = Column(DateTime, nullable=True) admin_name = Column(UnicodeText, nullable=True) From bdcc86ac20712783d3c1c198afdd4462d978d2e1 Mon Sep 17 00:00:00 2001 From: Filippo Leonelli Date: Mon, 2 Sep 2024 12:38:22 +0200 Subject: [PATCH 007/607] Fixed relative path --- client/app/src/app-routing.module.ts | 2 +- .../{accreditor => }/accreditor-routing.module.ts | 0 .../accreditor/{accreditor => }/accreditor.module.ts | 0 .../{accreditor => }/home/home.component.html | 0 .../accreditor/{accreditor => }/home/home.component.ts | 0 .../organization/organization.component.html | 0 .../organization/organization.component.ts | 0 .../organizations/organizations.component.html | 0 .../organizations/organizations.component.ts | 0 .../{accreditor => }/sidebar/sidebar.component.html | 0 .../{accreditor => }/sidebar/sidebar.component.ts | 0 client/app/src/shared.module.ts | 10 +++++----- .../org-recipient-info.component.html | 0 .../org-recipient-info.component.ts | 0 14 files changed, 6 insertions(+), 6 deletions(-) rename client/app/src/pages/accreditor/{accreditor => }/accreditor-routing.module.ts (100%) rename client/app/src/pages/accreditor/{accreditor => }/accreditor.module.ts (100%) rename client/app/src/pages/accreditor/{accreditor => }/home/home.component.html (100%) rename client/app/src/pages/accreditor/{accreditor => }/home/home.component.ts (100%) rename client/app/src/pages/accreditor/{accreditor => }/organization/organization.component.html (100%) rename client/app/src/pages/accreditor/{accreditor => }/organization/organization.component.ts (100%) rename client/app/src/pages/accreditor/{accreditor => }/organizations/organizations.component.html (100%) rename client/app/src/pages/accreditor/{accreditor => }/organizations/organizations.component.ts (100%) rename client/app/src/pages/accreditor/{accreditor => }/sidebar/sidebar.component.html (100%) rename client/app/src/pages/accreditor/{accreditor => }/sidebar/sidebar.component.ts (100%) rename client/app/src/shared/partials/{org-recipiant-info => org-recipient-info}/org-recipient-info.component.html (100%) rename client/app/src/shared/partials/{org-recipiant-info => org-recipient-info}/org-recipient-info.component.ts (100%) diff --git a/client/app/src/app-routing.module.ts b/client/app/src/app-routing.module.ts index 57f7bb5517..4f9191caf3 100644 --- a/client/app/src/app-routing.module.ts +++ b/client/app/src/app-routing.module.ts @@ -23,7 +23,7 @@ import {WhistleblowerLoginResolver} from "@app/shared/resolvers/whistleblower-lo import {SubmissionComponent} from "@app/pages/whistleblower/submission/submission.component"; import {AuthRoutingModule} from "@app/pages/auth/auth-routing.module"; import { AccreditorGuard } from "./shared/guards/accreditor.guard"; -import { OrganizationComponent } from "./pages/accreditor/organization/organization.component"; +import { OrganizationComponent } from "@app/pages/accreditor/organization/organization.component"; const routes: Routes = [ diff --git a/client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts b/client/app/src/pages/accreditor/accreditor-routing.module.ts similarity index 100% rename from client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts rename to client/app/src/pages/accreditor/accreditor-routing.module.ts diff --git a/client/app/src/pages/accreditor/accreditor/accreditor.module.ts b/client/app/src/pages/accreditor/accreditor.module.ts similarity index 100% rename from client/app/src/pages/accreditor/accreditor/accreditor.module.ts rename to client/app/src/pages/accreditor/accreditor.module.ts diff --git a/client/app/src/pages/accreditor/accreditor/home/home.component.html b/client/app/src/pages/accreditor/home/home.component.html similarity index 100% rename from client/app/src/pages/accreditor/accreditor/home/home.component.html rename to client/app/src/pages/accreditor/home/home.component.html diff --git a/client/app/src/pages/accreditor/accreditor/home/home.component.ts b/client/app/src/pages/accreditor/home/home.component.ts similarity index 100% rename from client/app/src/pages/accreditor/accreditor/home/home.component.ts rename to client/app/src/pages/accreditor/home/home.component.ts diff --git a/client/app/src/pages/accreditor/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/organization/organization.component.html similarity index 100% rename from client/app/src/pages/accreditor/accreditor/organization/organization.component.html rename to client/app/src/pages/accreditor/organization/organization.component.html diff --git a/client/app/src/pages/accreditor/accreditor/organization/organization.component.ts b/client/app/src/pages/accreditor/organization/organization.component.ts similarity index 100% rename from client/app/src/pages/accreditor/accreditor/organization/organization.component.ts rename to client/app/src/pages/accreditor/organization/organization.component.ts diff --git a/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html b/client/app/src/pages/accreditor/organizations/organizations.component.html similarity index 100% rename from client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html rename to client/app/src/pages/accreditor/organizations/organizations.component.html diff --git a/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts b/client/app/src/pages/accreditor/organizations/organizations.component.ts similarity index 100% rename from client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts rename to client/app/src/pages/accreditor/organizations/organizations.component.ts diff --git a/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html b/client/app/src/pages/accreditor/sidebar/sidebar.component.html similarity index 100% rename from client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html rename to client/app/src/pages/accreditor/sidebar/sidebar.component.html diff --git a/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts b/client/app/src/pages/accreditor/sidebar/sidebar.component.ts similarity index 100% rename from client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts rename to client/app/src/pages/accreditor/sidebar/sidebar.component.ts diff --git a/client/app/src/shared.module.ts b/client/app/src/shared.module.ts index 54636a605b..49fd67864d 100644 --- a/client/app/src/shared.module.ts +++ b/client/app/src/shared.module.ts @@ -83,11 +83,11 @@ import {ReopenSubmissionComponent} from "@app/shared/modals/reopen-submission/re import {OtkcAccessComponent} from "@app/shared/modals/otkc-access/otkc-access.component"; import {OperationComponent} from "@app/shared/partials/operation/operation.component"; import {RedactInformationComponent} from "@app/shared/modals/redact-information/redact-information.component"; -import { OrgInfoComponent } from "./shared/partials/org-info/org-info.component"; -import { OrgAdminInfoComponent } from "./shared/partials/org-admin-info/org-admin-info.component"; -import { OrgRecipientInfoComponent } from "./shared/partials/org-recipient-info/org-recipient-info.component"; -import { OrgOverviewComponent } from "./shared/partials/org-overview/org-overview.component"; -import { OrgUsersListComponent } from "./shared/partials/org-users-list/org-users-list.component"; +import { OrgInfoComponent } from "@app/shared/partials/org-info/org-info.component"; +import { OrgAdminInfoComponent } from "@app/shared/partials/org-admin-info/org-admin-info.component"; +import { OrgRecipientInfoComponent } from "@app/shared/partials/org-recipient-info/org-recipient-info.component"; +import { OrgOverviewComponent } from "@app/shared/partials/org-overview/org-overview.component"; +import { OrgUsersListComponent } from "@app/shared/partials/org-users-list/org-users-list.component"; @NgModule({ imports: [ diff --git a/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html b/client/app/src/shared/partials/org-recipient-info/org-recipient-info.component.html similarity index 100% rename from client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html rename to client/app/src/shared/partials/org-recipient-info/org-recipient-info.component.html diff --git a/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts b/client/app/src/shared/partials/org-recipient-info/org-recipient-info.component.ts similarity index 100% rename from client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts rename to client/app/src/shared/partials/org-recipient-info/org-recipient-info.component.ts From ba73aed31d5fed3ea93c39a6387ed8488be9ad77 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Mon, 2 Sep 2024 17:04:49 +0200 Subject: [PATCH 008/607] Update migration.py, __init__.py, and 4 more files... --- backend/globaleaks/db/migration.py | 2 +- .../db/migrations/update_69/__init__.py | 131 +----------------- backend/globaleaks/models/__init__.py | 17 +-- backend/globaleaks/models/config.py | 2 + backend/globaleaks/models/config_desc.py | 3 +- backend/globaleaks/models/enums.py | 6 + 6 files changed, 22 insertions(+), 139 deletions(-) diff --git a/backend/globaleaks/db/migration.py b/backend/globaleaks/db/migration.py index c6c71a8bf5..739ce958b6 100644 --- a/backend/globaleaks/db/migration.py +++ b/backend/globaleaks/db/migration.py @@ -86,7 +86,7 @@ ('User', [User_v_52, User_v_54, 0, User_v_56, 0, User_v_61, 0, 0, 0, 0, User_v_64, 0, 0, User_v_66, 0, User_v_68, 0, models._User]), ('WhistleblowerFile', [WhistleblowerFile_v_57, 0, 0, 0, 0, 0, WhistleblowerFile_v_64, 0, 0, 0, 0, 0, 0, WhistleblowerFile_v_66, 0, models._WhistleblowerFile, 0, 0]), ('InternalTipForwarding', [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, models._InternalTipForwarding]), - ('InternalFileForwarding', [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, models._InternalFileForwarding]), + ('FileForwarding', [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, models._FileForwarding]), ('WhistleblowerTip', [WhistleblowerTip_v_59, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]) ]) diff --git a/backend/globaleaks/db/migrations/update_69/__init__.py b/backend/globaleaks/db/migrations/update_69/__init__.py index 8dbbce0797..64d1412e16 100644 --- a/backend/globaleaks/db/migrations/update_69/__init__.py +++ b/backend/globaleaks/db/migrations/update_69/__init__.py @@ -24,20 +24,6 @@ class Subscriber_v_68(Model): registration_date = Column(DateTime, default=datetime_now, nullable=False) tos1 = Column(UnicodeText, default='', nullable=False) tos2 = Column(UnicodeText, default='', nullable=False) - """ - creation_date = Column(DateTime, default=datetime_now, nullable=False) - state = Column(Enum(EnumSubscriberStatus), nullable=True) - organization_institutional_site = Column(UnicodeText, default='', nullable=False) - accreditation_date = Column(DateTime, nullable=True) - admin_name = Column(UnicodeText, nullable=True) - admin_surname = Column(UnicodeText, nullable=True) - admin_email = Column(UnicodeText, nullable=True) - admin_fiscal_code = Column(UnicodeText, nullable=True) - recipient_name = Column(UnicodeText, nullable=True) - recipient_surname = Column(UnicodeText, nullable=True) - recipient_email = Column(UnicodeText, nullable=True) - recipient_fiscal_code = Column(UnicodeText, nullable=True) - """ class Tenant_v_68(Model): @@ -46,58 +32,7 @@ class Tenant_v_68(Model): id = Column(Integer, primary_key=True) creation_date = Column(DateTime, default=datetime_now, nullable=False) active = Column(Boolean, default=False, nullable=False) - # affiliated = Column(Boolean, nullable=True) - # external = Column(Boolean, default=False, nullable=False) - - -""" -class InternalTipForwarding_v_68(Model): - - __tablename__ = 'internaltip_forwarding' - id = Column(UnicodeText(36), primary_key=True, default=uuid4) - internaltip_id = Column(UnicodeText(36), nullable=False, index=True) - oe_internaltip_id = Column(UnicodeText(36), nullable=False, index=True) - tid = Column(Integer, default=1, nullable=False) - creation_date = Column(DateTime, default=datetime_now, nullable=False) - update_date = Column(DateTime, default=datetime_now, nullable=False) - text = Column(UnicodeText, nullable=False) - comment = Column(UnicodeText, nullable=False) - data = Column(UnicodeText, nullable=False) - questionnaire_id = Column(UnicodeText(36), nullable=False, index=True) - @declared_attr - def __table_args__(self): - return ( - ForeignKeyConstraint( - ['internaltip_id'], - ['internaltip.id'], - ondelete='CASCADE', - deferrable=True, - initially='DEFERRED' - ), - ForeignKeyConstraint( - ['oe_internaltip_id'], - ['internaltip.id'], - ondelete='CASCADE', - deferrable=True, - initially='DEFERRED' - ), - ForeignKeyConstraint( - ['questionnaire_id'], - ['questionnaire.id'], - ondelete='CASCADE', - deferrable=True, - initially='DEFERRED' - ), - ForeignKeyConstraint( - ['tid'], - ['tenant.id'], - ondelete='CASCADE', - deferrable=True, - initially='DEFERRED' - ) - ) -""" class InternalFile_v_68(Model): """ @@ -114,8 +49,6 @@ class InternalFile_v_68(Model): size = Column(JSON, default='', nullable=False) new = Column(Boolean, default=True, nullable=False) reference_id = Column(UnicodeText(36), default='', nullable=False) - # verification_date = Column(DateTime, nullable=True) - # state = Column(Enum(EnumStateFile), default='pending', nullable=False) class ReceiverFile_v_68(Model): @@ -133,11 +66,9 @@ class ReceiverFile_v_68(Model): size = Column(Integer, nullable=False) content_type = Column(UnicodeText, nullable=False) creation_date = Column(DateTime, default=datetime_now, nullable=False) - # verification_date = Column(DateTime, nullable=True) access_date = Column(DateTime, default=datetime_null, nullable=False) description = Column(UnicodeText, default="", nullable=False) visibility = Column(Enum(EnumVisibility), default='public', nullable=False) - # state = Column(Enum(EnumStateFile), default='pending', nullable=False) new = Column(Boolean, default=True, nullable=False) @@ -186,7 +117,6 @@ class User_v_68(Model): readonly = Column(Boolean, default=False, nullable=False) two_factor_secret = Column(UnicodeText(32), default='', nullable=False) reminder_date = Column(DateTime, default=datetime_null, nullable=False) - # status = Column(Enum(EnumUserStatus), default='active', nullable=False) # BEGIN of PGP key fields pgp_key_fingerprint = Column(UnicodeText, default='', nullable=False) @@ -197,62 +127,13 @@ class User_v_68(Model): accepted_privacy_policy = Column(DateTime, default=datetime_null, nullable=False) clicked_recovery_key = Column(Boolean, default=False, nullable=False) -""" -class InternalFileForwarding_v_68(Model): - __tablename__ = 'internalfile_forwarding' - - id = Column(UnicodeText(36), primary_key=True, default=uuid4) - tid = Column(Integer, default=1, nullable=False) - internaltip_id = Column(UnicodeText(36), nullable=False, index=True) - internalfile_id = Column(UnicodeText(36), nullable=False, index=True) - - @declared_attr - def __table_args__(self): - return ( - ForeignKeyConstraint( - ['tid'], - ['tenant.id'], - ondelete='CASCADE', - deferrable=True, - initially='DEFERRED' - ), - ForeignKeyConstraint( - ['internaltip_id'], - ['internaltip.id'], - ondelete='CASCADE', - deferrable=True, - initially='DEFERRED' - ), - ForeignKeyConstraint( - ['internalfile_id'], - ['internalfile.id'], - ondelete='CASCADE', - deferrable=True, - initially='DEFERRED' - ) - ) -""" class MigrationScript(MigrationBase): def epilogue(self): - """for model in ['ReceiverFile', 'User', 'InternalFile', - 'Tenant']: - for old_obj in self.session_old.query(self.model_from[model]): - new_obj = self.model_to[model]() - for key in new_obj.__mapper__.column_attrs.keys(): - if model == 'User' and key == 'status': - setattr(new_obj, key, 'active') - elif model == 'InternalFile' and key == 'verification_date': - setattr(new_obj, key, None) - elif model == 'InternalFile' and key == 'state': - setattr(new_obj, key, 'pending') - elif model == 'Tenant' and key == 'affiliated': - setattr(new_obj, key, None) - elif model == 'Tenant' and key == 'external': - setattr(new_obj, key, False) - else: - setattr(new_obj, key, getattr(old_obj, key)) - self.session_new.add(new_obj) - """ - pass + new_configuration = self.model_to['Config']() + new_configuration.var_name = 'url_file_analysis' + new_configuration.value = 'http://localhost/api/v1/scan' + self.session_new.add(new_configuration) + + self.entries_count['Config'] += 1 \ No newline at end of file diff --git a/backend/globaleaks/models/__init__.py b/backend/globaleaks/models/__init__.py index 106ac9e8e1..f3e9120b46 100644 --- a/backend/globaleaks/models/__init__.py +++ b/backend/globaleaks/models/__init__.py @@ -591,16 +591,17 @@ def __table_args__(self): initially='DEFERRED')) -class _InternalFileForwarding(Model): +class _FileForwarding(Model): """ This model keeps track of submission files for the oe """ - __tablename__ = 'internalfile_forwarding' + __tablename__ = 'file_forwarding' id = Column(UnicodeText(36), primary_key=True, default=uuid4) tid = Column(Integer, default=1, nullable=False) internaltip_id = Column(UnicodeText(36), nullable=False, index=True) - internalfile_id = Column(UnicodeText(36), nullable=False, index=True) + file_id = Column(UnicodeText(36), nullable=False, index=True) + file_origin = Column(Enum(EnumOriginFile), default='internal_file', nullable=False) @declared_attr def __table_args__(self): @@ -618,13 +619,6 @@ def __table_args__(self): ondelete='CASCADE', deferrable=True, initially='DEFERRED' - ), - ForeignKeyConstraint( - ['internalfile_id'], - ['internalfile.id'], - ondelete='CASCADE', - deferrable=True, - initially='DEFERRED' ) ) @@ -954,7 +948,6 @@ class _Subscriber(Model): registration_date = Column(DateTime, default=datetime_now, nullable=False) tos1 = Column(UnicodeText, default='', nullable=False) tos2 = Column(UnicodeText, default='', nullable=False) - creation_date = Column(DateTime, default=datetime_now, nullable=False) state = Column(Integer, default=None, nullable=True) organization_institutional_site = Column(UnicodeText, default='', nullable=False) @@ -1313,5 +1306,5 @@ class InternalTipForwarding(_InternalTipForwarding, Base): pass -class InternalFileForwarding(_InternalFileForwarding, Base): +class FileForwarding(_FileForwarding, Base): pass diff --git a/backend/globaleaks/models/config.py b/backend/globaleaks/models/config.py index ac04eb1358..9d4e7f1768 100644 --- a/backend/globaleaks/models/config.py +++ b/backend/globaleaks/models/config.py @@ -161,6 +161,8 @@ def initialize_config(session, tid, mode): for name in inherit_from_root_tenant: variables[name] = root_tenant_node[name] + variables['url_file_analysis'] = 'http://localhost/api/v1/scan' + for name, value in variables.items(): session.add(Config({'tid': tid, 'var_name': name, 'value': value})) diff --git a/backend/globaleaks/models/config_desc.py b/backend/globaleaks/models/config_desc.py index ad42c6bd1d..a3b9a03435 100644 --- a/backend/globaleaks/models/config_desc.py +++ b/backend/globaleaks/models/config_desc.py @@ -118,7 +118,8 @@ class Bool(Item): 'version': Unicode(default=str(__version__)), 'version_db': Int(default=DATABASE_VERSION), 'wizard_done': Bool(default=False), - 'uuid': Unicode(default=uuid4) + 'uuid': Unicode(default=uuid4), + 'url_file_analysis': Unicode(default='http://localhost/api/v1/scan') } diff --git a/backend/globaleaks/models/enums.py b/backend/globaleaks/models/enums.py index e8899517b2..55d9319922 100644 --- a/backend/globaleaks/models/enums.py +++ b/backend/globaleaks/models/enums.py @@ -60,3 +60,9 @@ class EnumSubscriberStatus(_Enum): instructor_request = 3 invited = 4 suspend = 5 + + +class EnumOriginFile(_Enum): + internal_file = 0 + receiver_file = 1 + whistleblower_file = 2 From b2e7188186c156f9dcfe919d4da3f09898b2ffaf Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Mon, 2 Sep 2024 18:16:30 +0200 Subject: [PATCH 009/607] actions on OE --- .../organization/organization.component.html | 2 ++ .../organization/organization.component.ts | 21 +++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/client/app/src/pages/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/organization/organization.component.html index 01252cc562..66cc04554c 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.html +++ b/client/app/src/pages/accreditor/organization/organization.component.html @@ -7,12 +7,14 @@ diff --git a/client/app/src/pages/accreditor/organization/organization.component.ts b/client/app/src/pages/accreditor/organization/organization.component.ts index ab120d4cde..dcdaa7a3b7 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.ts +++ b/client/app/src/pages/accreditor/organization/organization.component.ts @@ -16,6 +16,8 @@ export class OrganizationComponent implements OnInit{ loading: boolean = false; organization: OrganizationData; + + constructor(private activatedRoute: ActivatedRoute, private httpService: HttpService, private orgService : AccreditorOrgService){ } @@ -40,7 +42,13 @@ export class OrganizationComponent implements OnInit{ this.organization.num_tip = 10 this.organization.num_user_profiled = 2 this.organization.type = "NOT_AFFILIATED" - this.organization.state = "ACCREDITED" + // • 0 -> REQUESTED + // • 1 -> ACCREDITED + // • 2 -> REJECTED + // • 3 -> INSTRUCTOR_REQUEST + // • 4 -> INVITED + // • 5 -> SUSPEND + this.organization.state = 0 let users : EOUser[] = []; users.push({ @@ -81,5 +89,14 @@ export class OrganizationComponent implements OnInit{ convertiInAffiliata(){ console.log("CONVERTI IN AFFILIATA - TODO!!!") } - + + isSuspendable(){ + return this.organization.state == 1 + } + + isDeletable(){ + return this.organization.state == 1 + } + + } From c0135b8dfa891480a64cc982e6182e3ae4f8216a Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Mon, 2 Sep 2024 18:16:53 +0200 Subject: [PATCH 010/607] fix on organization data type --- client/app/src/models/accreditor/organization-data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/app/src/models/accreditor/organization-data.ts b/client/app/src/models/accreditor/organization-data.ts index d64e00078a..b492a282b2 100644 --- a/client/app/src/models/accreditor/organization-data.ts +++ b/client/app/src/models/accreditor/organization-data.ts @@ -5,7 +5,7 @@ export class OrganizationData { id: string; denomination: string; type: string; - state: string; + state: number; accreditation_date: string; num_user_profiled: number; num_tip: number; From 835e98462b38bc607716ecc5efc9e265b6cf3748 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Mon, 2 Sep 2024 18:26:40 +0200 Subject: [PATCH 011/607] check if organization is suspendable --- .../accreditor/organization/organization.component.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/client/app/src/pages/accreditor/organization/organization.component.ts b/client/app/src/pages/accreditor/organization/organization.component.ts index dcdaa7a3b7..7542ce03d4 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.ts +++ b/client/app/src/pages/accreditor/organization/organization.component.ts @@ -48,7 +48,7 @@ export class OrganizationComponent implements OnInit{ // • 3 -> INSTRUCTOR_REQUEST // • 4 -> INVITED // • 5 -> SUSPEND - this.organization.state = 0 + this.organization.state = 5 let users : EOUser[] = []; users.push({ @@ -90,8 +90,14 @@ export class OrganizationComponent implements OnInit{ console.log("CONVERTI IN AFFILIATA - TODO!!!") } + // • 0 -> REQUESTED + // • 1 -> ACCREDITED + // • 2 -> REJECTED + // • 3 -> INSTRUCTOR_REQUEST + // • 4 -> INVITED + // • 5 -> SUSPEND isSuspendable(){ - return this.organization.state == 1 + return [1, 5].includes(this.organization.state) } isDeletable(){ From e4964680ed4a5ec013689076eaf8da2534806fa0 Mon Sep 17 00:00:00 2001 From: Alessio Franceschini Date: Tue, 3 Sep 2024 12:08:30 +0200 Subject: [PATCH 012/607] Changed column name and foreign key --- backend/globaleaks/models/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/globaleaks/models/__init__.py b/backend/globaleaks/models/__init__.py index f3e9120b46..10ee951038 100644 --- a/backend/globaleaks/models/__init__.py +++ b/backend/globaleaks/models/__init__.py @@ -599,7 +599,7 @@ class _FileForwarding(Model): id = Column(UnicodeText(36), primary_key=True, default=uuid4) tid = Column(Integer, default=1, nullable=False) - internaltip_id = Column(UnicodeText(36), nullable=False, index=True) + internaltip_forwarding_id = Column(UnicodeText(36), nullable=False, index=True) file_id = Column(UnicodeText(36), nullable=False, index=True) file_origin = Column(Enum(EnumOriginFile), default='internal_file', nullable=False) @@ -614,8 +614,8 @@ def __table_args__(self): initially='DEFERRED' ), ForeignKeyConstraint( - ['internaltip_id'], - ['internaltip.id'], + ['internaltip_forwarding_id'], + ['internaltip_forwarding.id'], ondelete='CASCADE', deferrable=True, initially='DEFERRED' From 25c9c57a172a7038a941e5a81089536a1ec68efc Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Tue, 3 Sep 2024 15:17:59 +0200 Subject: [PATCH 013/607] OE detail for all status --- .../models/accreditor/organization-data.ts | 2 +- .../organization/organization.component.html | 43 +++++++++++++++---- .../organization/organization.component.ts | 38 +++++++++++++--- 3 files changed, 67 insertions(+), 16 deletions(-) diff --git a/client/app/src/models/accreditor/organization-data.ts b/client/app/src/models/accreditor/organization-data.ts index b492a282b2..d64e00078a 100644 --- a/client/app/src/models/accreditor/organization-data.ts +++ b/client/app/src/models/accreditor/organization-data.ts @@ -5,7 +5,7 @@ export class OrganizationData { id: string; denomination: string; type: string; - state: number; + state: string; accreditation_date: string; num_user_profiled: number; num_tip: number; diff --git a/client/app/src/pages/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/organization/organization.component.html index 66cc04554c..bb83c7bf48 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.html +++ b/client/app/src/pages/accreditor/organization/organization.component.html @@ -7,22 +7,24 @@ - - + - - + -
+
+
+
+ + {{'Enable email notifications'|translate}} +
+
+
@@ -66,4 +75,22 @@
+ +
+
+
+ +
+
+ +
+
+
diff --git a/client/app/src/pages/accreditor/organization/organization.component.ts b/client/app/src/pages/accreditor/organization/organization.component.ts index 7542ce03d4..f3d9821656 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.ts +++ b/client/app/src/pages/accreditor/organization/organization.component.ts @@ -4,7 +4,6 @@ import { OrganizationData } from '@app/models/accreditor/organization-data'; import { EOUser } from '@app/models/app/shared-public-model'; import { AccreditorOrgService } from '@app/services/helper/accreditor-org.service'; import { HttpService } from '@app/shared/services/http.service'; -import { Observable } from 'rxjs'; @Component({ selector: 'src-organization', @@ -15,7 +14,7 @@ export class OrganizationComponent implements OnInit{ org_id: string | null; loading: boolean = false; organization: OrganizationData; - + org_type: boolean = false; constructor(private activatedRoute: ActivatedRoute, private httpService: HttpService, private orgService : AccreditorOrgService){ @@ -48,7 +47,8 @@ export class OrganizationComponent implements OnInit{ // • 3 -> INSTRUCTOR_REQUEST // • 4 -> INVITED // • 5 -> SUSPEND - this.organization.state = 5 + // • 6 -> APPROVED + this.organization.state = "INSTRUCTOR_REQUEST" let users : EOUser[] = []; users.push({ @@ -90,19 +90,43 @@ export class OrganizationComponent implements OnInit{ console.log("CONVERTI IN AFFILIATA - TODO!!!") } + invia(){ + if(this.org_type) + this.organization.type = "AFFILIATED" + + console.log("INVIA / INVIA INVITO - TODO!!!") + } + + rifiuta(){ + console.log("REJECT - TODO!!!") + + } + // • 0 -> REQUESTED // • 1 -> ACCREDITED // • 2 -> REJECTED // • 3 -> INSTRUCTOR_REQUEST // • 4 -> INVITED // • 5 -> SUSPEND - isSuspendable(){ - return [1, 5].includes(this.organization.state) + + isRequested(){ + return this.organization.state === "REQUESTED" + } + + isInstructorRequest(){ + return this.organization.state === "INSTRUCTOR_REQUEST" } - isDeletable(){ - return this.organization.state == 1 + isAccredited(){ + return this.organization.state === "ACCREDITED" } + isSuspended(){ + return this.organization.state === "SUSPENDED" + } + + isInvited(){ + return this.organization.state === "INVITED" + } } From 1c0bc2d7b6ba4ca87dbfb42281baf64227cd4fac Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Tue, 3 Sep 2024 15:26:33 +0200 Subject: [PATCH 014/607] fix visibility admin and recipient info --- .../pages/accreditor/organization/organization.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/app/src/pages/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/organization/organization.component.html index bb83c7bf48..9737fbfcdc 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.html +++ b/client/app/src/pages/accreditor/organization/organization.component.html @@ -41,11 +41,11 @@ -
+
-
+
From efff7099a6beb605897c21d0ffbf4e0f049d9dfb Mon Sep 17 00:00:00 2001 From: Alessio Franceschini Date: Tue, 3 Sep 2024 15:35:57 +0200 Subject: [PATCH 015/607] Changed table's name and removed some fields --- backend/globaleaks/db/migration.py | 2 +- backend/globaleaks/models/__init__.py | 20 +++++--------------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/backend/globaleaks/db/migration.py b/backend/globaleaks/db/migration.py index 739ce958b6..d3367101ca 100644 --- a/backend/globaleaks/db/migration.py +++ b/backend/globaleaks/db/migration.py @@ -86,7 +86,7 @@ ('User', [User_v_52, User_v_54, 0, User_v_56, 0, User_v_61, 0, 0, 0, 0, User_v_64, 0, 0, User_v_66, 0, User_v_68, 0, models._User]), ('WhistleblowerFile', [WhistleblowerFile_v_57, 0, 0, 0, 0, 0, WhistleblowerFile_v_64, 0, 0, 0, 0, 0, 0, WhistleblowerFile_v_66, 0, models._WhistleblowerFile, 0, 0]), ('InternalTipForwarding', [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, models._InternalTipForwarding]), - ('FileForwarding', [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, models._FileForwarding]), + ('ContentForwarding', [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, models._ContentForwarding]), ('WhistleblowerTip', [WhistleblowerTip_v_59, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]) ]) diff --git a/backend/globaleaks/models/__init__.py b/backend/globaleaks/models/__init__.py index 10ee951038..073611a817 100644 --- a/backend/globaleaks/models/__init__.py +++ b/backend/globaleaks/models/__init__.py @@ -591,28 +591,20 @@ def __table_args__(self): initially='DEFERRED')) -class _FileForwarding(Model): +class _ContentForwarding(Model): """ This model keeps track of submission files for the oe """ - __tablename__ = 'file_forwarding' + __tablename__ = 'content_forwarding' id = Column(UnicodeText(36), primary_key=True, default=uuid4) - tid = Column(Integer, default=1, nullable=False) internaltip_forwarding_id = Column(UnicodeText(36), nullable=False, index=True) - file_id = Column(UnicodeText(36), nullable=False, index=True) - file_origin = Column(Enum(EnumOriginFile), default='internal_file', nullable=False) + content_id = Column(UnicodeText(36), nullable=False, index=True) + content_origin = Column(Enum(EnumOriginFile), default='receiver_file', nullable=False) @declared_attr def __table_args__(self): return ( - ForeignKeyConstraint( - ['tid'], - ['tenant.id'], - ondelete='CASCADE', - deferrable=True, - initially='DEFERRED' - ), ForeignKeyConstraint( ['internaltip_forwarding_id'], ['internaltip_forwarding.id'], @@ -1004,8 +996,6 @@ class _InternalTipForwarding(Model): tid = Column(Integer, default=1, nullable=False) creation_date = Column(DateTime, default=datetime_now, nullable=False) update_date = Column(DateTime, default=datetime_now, nullable=False) - text = Column(UnicodeText, nullable=False) - comment = Column(UnicodeText, nullable=False) data = Column(UnicodeText, nullable=False) questionnaire_id = Column(UnicodeText(36), nullable=False, index=True) @@ -1306,5 +1296,5 @@ class InternalTipForwarding(_InternalTipForwarding, Base): pass -class FileForwarding(_FileForwarding, Base): +class ContentForwarding(_ContentForwarding, Base): pass From 3ed3fc57b706ca26d7bd272b23a83fb621d84789 Mon Sep 17 00:00:00 2001 From: Alessio Franceschini Date: Tue, 3 Sep 2024 15:59:30 +0200 Subject: [PATCH 016/607] Fixed typo --- backend/globaleaks/models/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/globaleaks/models/__init__.py b/backend/globaleaks/models/__init__.py index 073611a817..fa19ce4c63 100644 --- a/backend/globaleaks/models/__init__.py +++ b/backend/globaleaks/models/__init__.py @@ -611,7 +611,7 @@ def __table_args__(self): ondelete='CASCADE', deferrable=True, initially='DEFERRED' - ) + ), ) From 210a6b4e1cdcb21e7536e59493805ddbe2fb6113 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Tue, 3 Sep 2024 16:39:17 +0200 Subject: [PATCH 017/607] fix for accreditation_req initialization --- .../app/src/pages/accreditor/accreditor-routing.module.ts | 6 ++++++ .../shared/resolvers/accreditation-req-resolver.service.ts | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/client/app/src/pages/accreditor/accreditor-routing.module.ts b/client/app/src/pages/accreditor/accreditor-routing.module.ts index 756d7b887a..5e19694983 100644 --- a/client/app/src/pages/accreditor/accreditor-routing.module.ts +++ b/client/app/src/pages/accreditor/accreditor-routing.module.ts @@ -9,12 +9,18 @@ const routes: Routes = [ path: "", component: AccreditorHomeComponent, pathMatch: "full", + resolve: { + AccreditationReqResolver + }, data: {sidebar: "accreditor-sidebar", pageTitle: "Home"}, }, { path: "home", component: AccreditorHomeComponent, pathMatch: "full", + resolve: { + AccreditationReqResolver + }, data: {sidebar: "accreditor-sidebar", pageTitle: "Home"}, }, { diff --git a/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts b/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts index 19d4f86a15..5d7e2a500d 100644 --- a/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts +++ b/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts @@ -4,6 +4,7 @@ import { UtilsService } from '../services/utils.service'; import { HttpService } from '../services/http.service'; import { AuthenticationService } from '@app/services/helper/authentication.service'; import { map, Observable, of } from 'rxjs'; +import { AppDataService } from '@app/app-data.service'; @Injectable({ providedIn: 'root' @@ -12,7 +13,9 @@ export class AccreditationReqResolver { dataModel: AccreditationRequestModel[] = []; - constructor(private utilsService: UtilsService, private httpService: HttpService, private authenticationService: AuthenticationService) { + constructor(private utilsService: UtilsService, private httpService: HttpService, + private authenticationService: AuthenticationService, + private appDataService: AppDataService) { } @@ -28,6 +31,7 @@ export class AccreditationReqResolver { //TODO MOCKUP setTimeout(()=> { this.utilsService.reloadComponent(); + this.appDataService.updateShowLoadingPanel(false) }, 1000) } @@ -72,6 +76,7 @@ export class AccreditationReqResolver { } ] + this.appDataService.updateShowLoadingPanel(false) return true; }, 1000) } From 5252b81be02baa2b8d046eade5b5aede9398e937 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Tue, 3 Sep 2024 16:43:34 +0200 Subject: [PATCH 018/607] label fix --- .../pages/accreditor/organization/organization.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/app/src/pages/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/organization/organization.component.html index 9737fbfcdc..fc41867973 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.html +++ b/client/app/src/pages/accreditor/organization/organization.component.html @@ -59,7 +59,7 @@
- {{'Enable email notifications'|translate}} + {{'Affiliata'|translate}}
From adb4d8b86cd5d1128ba4345ea0ac824cbdf81c8d Mon Sep 17 00:00:00 2001 From: Alessio Franceschini Date: Wed, 21 Aug 2024 15:18:42 +0200 Subject: [PATCH 019/607] misc: first commit in this branch --- client/app/css/main.css | 3 +- client/app/src/app-routing.module.ts | 27 +++ client/app/src/app.module.ts | 4 + .../models/accreditor/organization-data.ts | 30 +++ .../app/src/models/app/shared-public-model.ts | 11 ++ .../src/pages/accred/accred-routing.module.ts | 19 ++ client/app/src/pages/accred/accred.module.ts | 23 +++ .../pages/accred/accred/accred.component.html | 125 +++++++++++++ .../pages/accred/accred/accred.component.ts | 70 +++++++ .../accreditor/accreditor-routing.module.ts | 37 ++++ .../accreditor/accreditor.module.ts | 23 +++ .../accreditor/home/home.component.html | 3 + .../accreditor/home/home.component.ts | 25 +++ .../organization/organization.component.html | 67 +++++++ .../organization/organization.component.ts | 85 +++++++++ .../organizations.component.html | 172 ++++++++++++++++++ .../organizations/organizations.component.ts | 76 ++++++++ .../accreditor/sidebar/sidebar.component.html | 8 + .../accreditor/sidebar/sidebar.component.ts | 24 +++ .../users-tab1/users-tab1.component.html | 3 +- client/app/src/pages/app/app.component.html | 1 + .../services/helper/accreditor-org.service.ts | 19 ++ client/app/src/shared.module.ts | 19 +- .../app/src/shared/guards/accreditor.guard.ts | 28 +++ .../org-admin-info.component.html | 20 ++ .../org-admin-info.component.ts | 18 ++ .../partials/org-info/org-info.component.html | 35 ++++ .../partials/org-info/org-info.component.ts | 26 +++ .../org-overview/org-overview.component.html | 33 ++++ .../org-overview/org-overview.component.ts | 26 +++ .../org-recipient-info.component.html | 27 +++ .../org-recipient-info.component.ts | 17 ++ .../org-users-list.component.html | 104 +++++++++++ .../org-users-list.component.ts | 24 +++ .../accreditation-req-resolver.service.ts | 81 +++++++++ 35 files changed, 1309 insertions(+), 4 deletions(-) create mode 100644 client/app/src/models/accreditor/organization-data.ts create mode 100644 client/app/src/pages/accred/accred-routing.module.ts create mode 100644 client/app/src/pages/accred/accred.module.ts create mode 100644 client/app/src/pages/accred/accred/accred.component.html create mode 100644 client/app/src/pages/accred/accred/accred.component.ts create mode 100644 client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts create mode 100644 client/app/src/pages/accreditor/accreditor/accreditor.module.ts create mode 100644 client/app/src/pages/accreditor/accreditor/home/home.component.html create mode 100644 client/app/src/pages/accreditor/accreditor/home/home.component.ts create mode 100644 client/app/src/pages/accreditor/accreditor/organization/organization.component.html create mode 100644 client/app/src/pages/accreditor/accreditor/organization/organization.component.ts create mode 100644 client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html create mode 100644 client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts create mode 100644 client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html create mode 100644 client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts create mode 100644 client/app/src/services/helper/accreditor-org.service.ts create mode 100644 client/app/src/shared/guards/accreditor.guard.ts create mode 100644 client/app/src/shared/partials/org-admin-info/org-admin-info.component.html create mode 100644 client/app/src/shared/partials/org-admin-info/org-admin-info.component.ts create mode 100644 client/app/src/shared/partials/org-info/org-info.component.html create mode 100644 client/app/src/shared/partials/org-info/org-info.component.ts create mode 100644 client/app/src/shared/partials/org-overview/org-overview.component.html create mode 100644 client/app/src/shared/partials/org-overview/org-overview.component.ts create mode 100644 client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html create mode 100644 client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts create mode 100644 client/app/src/shared/partials/org-users-list/org-users-list.component.html create mode 100644 client/app/src/shared/partials/org-users-list/org-users-list.component.ts create mode 100644 client/app/src/shared/resolvers/accreditation-req-resolver.service.ts diff --git a/client/app/css/main.css b/client/app/css/main.css index 0284ab03cc..df31c63ec3 100644 --- a/client/app/css/main.css +++ b/client/app/css/main.css @@ -310,7 +310,8 @@ h6 { #FooterBox, #DemoBadge, #PrivacyBadge, -#operator-badge{ +#operator-badge, +#organization-button{ padding: 1em; } diff --git a/client/app/src/app-routing.module.ts b/client/app/src/app-routing.module.ts index 7068b44522..57f7bb5517 100644 --- a/client/app/src/app-routing.module.ts +++ b/client/app/src/app-routing.module.ts @@ -22,6 +22,8 @@ import {WbTipResolver} from "@app/shared/resolvers/wb-tip-resolver.service"; import {WhistleblowerLoginResolver} from "@app/shared/resolvers/whistleblower-login.resolver"; import {SubmissionComponent} from "@app/pages/whistleblower/submission/submission.component"; import {AuthRoutingModule} from "@app/pages/auth/auth-routing.module"; +import { AccreditorGuard } from "./shared/guards/accreditor.guard"; +import { OrganizationComponent } from "./pages/accreditor/organization/organization.component"; const routes: Routes = [ @@ -126,6 +128,15 @@ const routes: Routes = [ }, loadChildren: () => import("./pages/wizard/wizard-routing.module").then(m => m.WizardRoutingModule) }, + { + path: "accred", + data: {pageTitle: "Accreditation"}, + resolve: { + PreferenceResolver, + title: TitleResolver + }, + loadChildren: () => import("./pages/accred/accred-routing.module").then(m => m.AccredRoutingModule) + }, { path: "reports/:tip_id", data: {pageTitle: "Report"}, @@ -136,6 +147,22 @@ const routes: Routes = [ canActivate: [SessionGuard], pathMatch: "full", }, + { + path: 'accreditor', + canActivate: [AccreditorGuard], + data: { + sidebar: "acreditator-sidebar", + pageTitle: "Home", + }, + loadChildren: () => import('./pages/accreditor/accreditor-routing.module').then(m => m.AccreditorRoutingModule) + }, + { + path: "organizations/:org_id", + data: {pageTitle: "Organization"}, + component: OrganizationComponent, + canActivate: [AccreditorGuard], + pathMatch: "full", + }, {path: "**", redirectTo: ""} ]; diff --git a/client/app/src/app.module.ts b/client/app/src/app.module.ts index 7aa7e7563d..2a12503337 100644 --- a/client/app/src/app.module.ts +++ b/client/app/src/app.module.ts @@ -39,6 +39,8 @@ import {AnalystModule} from "@app/pages/analyst/analyst.module"; import {mockEngine} from './services/helper/mocks'; import {HttpService} from "./shared/services/http.service"; import {CryptoService} from "@app/shared/services/crypto.service"; +import { AccredModule } from "./pages/accred/accred.module"; +import { AccreditorModule } from "./pages/accreditor/accreditor.module"; export function createTranslateLoader(http: HttpClient) { return new TranslateHttpLoader(http, "l10n/", ""); } @@ -74,6 +76,8 @@ const translationModule = TranslateModule.forRoot({ HttpClientModule, BrowserModule, BrowserAnimationsModule, + AccredModule, + AccreditorModule, AuthModule, SignupModule, ActionModule, diff --git a/client/app/src/models/accreditor/organization-data.ts b/client/app/src/models/accreditor/organization-data.ts new file mode 100644 index 0000000000..d64e00078a --- /dev/null +++ b/client/app/src/models/accreditor/organization-data.ts @@ -0,0 +1,30 @@ +import { EOUser } from "../app/shared-public-model"; + +export class OrganizationData { + + id: string; + denomination: string; + type: string; + state: string; + accreditation_date: string; + num_user_profiled: number; + num_tip: number; + users: EOUser[]; + pec: string; + +} + + +export interface AccreditationRequestModel{ + + id: string; + denomination: string; + type: string; + state: string; + accreditation_date: string; + num_user_profiled: number; + num_tip: number; + +} + + diff --git a/client/app/src/models/app/shared-public-model.ts b/client/app/src/models/app/shared-public-model.ts index c5bd1d92c1..b1e335d046 100644 --- a/client/app/src/models/app/shared-public-model.ts +++ b/client/app/src/models/app/shared-public-model.ts @@ -198,3 +198,14 @@ export interface SubmissionStatus { status?: string; substatus?: string; } + +export interface EOUser { + id: string; + name: string; + surname: string; + creation_date: string; + last_access: string; + role: string; + tips: number; + closed_tips: number; +} diff --git a/client/app/src/pages/accred/accred-routing.module.ts b/client/app/src/pages/accred/accred-routing.module.ts new file mode 100644 index 0000000000..148e869b86 --- /dev/null +++ b/client/app/src/pages/accred/accred-routing.module.ts @@ -0,0 +1,19 @@ +import {NgModule} from "@angular/core"; +import {RouterModule, Routes} from "@angular/router"; +import {AccredComponent} from "@app/pages/accred/accred/accred.component"; + +const routes: Routes = [ + { + path: "", + component: AccredComponent, + pathMatch: "full", + } + +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class AccredRoutingModule { +} diff --git a/client/app/src/pages/accred/accred.module.ts b/client/app/src/pages/accred/accred.module.ts new file mode 100644 index 0000000000..f6fdaec6ad --- /dev/null +++ b/client/app/src/pages/accred/accred.module.ts @@ -0,0 +1,23 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { NgSelectModule } from '@ng-select/ng-select'; +import { TranslateModule } from '@ngx-translate/core'; +import { SharedModule } from '@app/shared.module'; +import { AccredComponent } from './accred/accred.component'; + + +@NgModule({ + declarations: [ + AccredComponent + ], + imports: [ + CommonModule, + TranslateModule, + FormsModule, + ReactiveFormsModule, + NgSelectModule, + SharedModule + ] +}) +export class AccredModule { } \ No newline at end of file diff --git a/client/app/src/pages/accred/accred/accred.component.html b/client/app/src/pages/accred/accred/accred.component.html new file mode 100644 index 0000000000..22b1674e60 --- /dev/null +++ b/client/app/src/pages/accred/accred/accred.component.html @@ -0,0 +1,125 @@ + + + + + + +
+
+
+ +
+
{{ 'Organization info' | translate }}
+
+
+ + +
+
+ + +
{{'Invalid PEC format' | translate}}
+
+
+ + +
{{'Invalid PEC format' | translate}}
+
{{'PEC and Confirm PEC must match' | translate}}
+
+
+ + +
+
+
+
+
+ +
+
{{ 'Admin info' | translate }}
+
+
+ + +
+
+ + +
{{'Invalid email format' | translate}}
+
+
+
+
+
+ +
+
{{ 'Primary recipient info' | translate }}
+
+
+ + +
+
+ + +
+
+ + +
{{'Invalid email format' | translate}}
+
+
+
+
+
+
+
+
+
{{ 'Privacy Policy' | translate }}
+
{{ privacyPolicy | translate }}
+
+ + {{ 'I have read and agree to the privacy policy.' | translate }} +
+
+
+
+
+
+ +
+
diff --git a/client/app/src/pages/accred/accred/accred.component.ts b/client/app/src/pages/accred/accred/accred.component.ts new file mode 100644 index 0000000000..c48e1a7433 --- /dev/null +++ b/client/app/src/pages/accred/accred/accred.component.ts @@ -0,0 +1,70 @@ +import { Component, OnInit, ViewChild, TemplateRef } from "@angular/core"; +import { AuthenticationService } from "@app/services/helper/authentication.service"; +import { Constants } from "@app/shared/constants/constants"; +import { ActivatedRoute, Router } from "@angular/router"; +import { AppDataService } from "@app/app-data.service"; +import { NgbModal } from "@ng-bootstrap/ng-bootstrap"; +import { NgForm } from "@angular/forms"; + +@Component({ + selector: "app-login", + templateUrl: "./accred.component.html" +}) +export class AccredComponent implements OnInit { + @ViewChild("accredForm") public accredForm: NgForm; + @ViewChild('content') content: TemplateRef; + modalMessage = 'A confirmation email will be sent to the organization\'s PEC.'; + + protected readonly location = location; + protected readonly Constants = Constants; + + organizationInfo = { + denomination: '', + pec: '', + confirmPec: '', + institutionalWebsite: '' + }; + adminInfo = { + name: '', + email: '', + fiscalCode: '' + }; + recipientInfo = { + name: '', + fiscalCode: '', + email: '' + }; + + pecsMatch = true; + privacyAccept = false; + privacyPolicy = 'Your privacy policy text here...'; + + constructor(private authentication: AuthenticationService, public router: Router, private route: ActivatedRoute, protected appDataService: AppDataService, private modalService: NgbModal) {} + + ngOnInit() { + this.adminInfo.fiscalCode = this.getFiscalCodeFromIdp(); + } + + private getFiscalCodeFromIdp(): string { + return 'sample-fiscal-code'; + } + + checkPecsMatch() { + this.pecsMatch = this.organizationInfo.pec === this.organizationInfo.confirmPec; + } + + closeModal(modal: any) { + modal.close('Close click'); + this.router.navigate(['/']); + } + + openConfirmModal() { + this.modalService.open(this.content); + } + + onSubmit() { + if (this.privacyAccept && this.pecsMatch) { + this.openConfirmModal(); + } + } +} diff --git a/client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts b/client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts new file mode 100644 index 0000000000..756d7b887a --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts @@ -0,0 +1,37 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { AccreditorHomeComponent } from './home/home.component'; +import { OrganizationsComponent } from './organizations/organizations.component'; +import { AccreditationReqResolver } from '@app/shared/resolvers/accreditation-req-resolver.service'; + +const routes: Routes = [ + { + path: "", + component: AccreditorHomeComponent, + pathMatch: "full", + data: {sidebar: "accreditor-sidebar", pageTitle: "Home"}, + }, + { + path: "home", + component: AccreditorHomeComponent, + pathMatch: "full", + data: {sidebar: "accreditor-sidebar", pageTitle: "Home"}, + }, + { + path: "organizations", + component: OrganizationsComponent, + resolve: { + AccreditationReqResolver + }, + pathMatch: "full", + data: {sidebar: "accreditor-sidebar", pageTitle: "Organizations"}, + } + + +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class AccreditorRoutingModule { } diff --git a/client/app/src/pages/accreditor/accreditor/accreditor.module.ts b/client/app/src/pages/accreditor/accreditor/accreditor.module.ts new file mode 100644 index 0000000000..558bd9d1c2 --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/accreditor.module.ts @@ -0,0 +1,23 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import {RouterModule} from "@angular/router"; +import {SharedModule} from "@app/shared.module"; +import {FormsModule} from "@angular/forms"; +import {NgbModule, NgbNavModule} from "@ng-bootstrap/ng-bootstrap"; +import { AccreditorHomeComponent } from './home/home.component'; +import { SidebarComponent } from './sidebar/sidebar.component'; +import { OrganizationsComponent } from './organizations/organizations.component'; +import { OrganizationComponent } from './organization/organization.component'; + + +@NgModule({ + declarations: [ + AccreditorHomeComponent, SidebarComponent, OrganizationsComponent, OrganizationComponent + ], + imports: [ + CommonModule, SharedModule, NgbNavModule, NgbModule, RouterModule, FormsModule + ], + exports: [SidebarComponent] +}) +export class AccreditorModule { } diff --git a/client/app/src/pages/accreditor/accreditor/home/home.component.html b/client/app/src/pages/accreditor/accreditor/home/home.component.html new file mode 100644 index 0000000000..aa5a211875 --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/home/home.component.html @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/client/app/src/pages/accreditor/accreditor/home/home.component.ts b/client/app/src/pages/accreditor/accreditor/home/home.component.ts new file mode 100644 index 0000000000..43cb036271 --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/home/home.component.ts @@ -0,0 +1,25 @@ +import {Component} from "@angular/core"; +import {AppDataService} from "@app/app-data.service"; +import {preferenceResolverModel} from "@app/models/resolvers/preference-resolver-model"; +import {PreferenceResolver} from "@app/shared/resolvers/preference.resolver"; +import {UtilsService} from "@app/shared/services/utils.service"; + + +@Component({ + selector: "src-accreditor-home", + templateUrl: "./home.component.html" +}) +export class AccreditorHomeComponent { + preferenceData: preferenceResolverModel; + constructor(private appDataService: AppDataService,private utilsService: UtilsService, private preference: PreferenceResolver) { + } + + ngOnInit(): void { + if (this.preference.dataModel) { + this.preferenceData = this.preference.dataModel; + } + if (this.appDataService.public.node.user_privacy_policy_text && this.preferenceData.accepted_privacy_policy === "1970-01-01T00:00:00Z") { + this.utilsService.acceptPrivacyPolicyDialog().subscribe(); + } + } +} diff --git a/client/app/src/pages/accreditor/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/accreditor/organization/organization.component.html new file mode 100644 index 0000000000..01252cc562 --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/organization/organization.component.html @@ -0,0 +1,67 @@ +
+
+
+ + + + + + + + + + + + + + + + + + + + +
+
+ + +
+
+ +
+ +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
diff --git a/client/app/src/pages/accreditor/accreditor/organization/organization.component.ts b/client/app/src/pages/accreditor/accreditor/organization/organization.component.ts new file mode 100644 index 0000000000..ab120d4cde --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/organization/organization.component.ts @@ -0,0 +1,85 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { OrganizationData } from '@app/models/accreditor/organization-data'; +import { EOUser } from '@app/models/app/shared-public-model'; +import { AccreditorOrgService } from '@app/services/helper/accreditor-org.service'; +import { HttpService } from '@app/shared/services/http.service'; +import { Observable } from 'rxjs'; + +@Component({ + selector: 'src-organization', + templateUrl: './organization.component.html' +}) +export class OrganizationComponent implements OnInit{ + + org_id: string | null; + loading: boolean = false; + organization: OrganizationData; + + constructor(private activatedRoute: ActivatedRoute, private httpService: HttpService, private orgService : AccreditorOrgService){ + + } + + + ngOnInit() { + this.loadOrganizationData(); + } + + loadOrganizationData(){ + this.org_id = this.activatedRoute.snapshot.paramMap.get("org_id"); + + // const requestObservable: Observable = this.httpService.receiverTip(this.org_id); + this.loading = true; + this.orgService.reset(); + + // setTimeout(()=>{ + this.organization = new OrganizationData(); + this.organization.id = "1" + this.organization.denomination = "denominazione Org 1" + this.organization.accreditation_date = "01-02-2024" + this.organization.num_tip = 10 + this.organization.num_user_profiled = 2 + this.organization.type = "NOT_AFFILIATED" + this.organization.state = "ACCREDITED" + + let users : EOUser[] = []; + users.push({ + id: "1", + name: "Utente 1", + surname: "Surname 1", + creation_date: "01-01-2024", + last_access: "01/02/2024 10:00:05", + role: "Admin", + tips: 10, + closed_tips: 1 + }) + + this.organization.users = users; + + + // }, 1000) + + // requestObservable.subscribe( + // { + // next: (response: OrganizationData) => { + // this.loading = false; + // this.organization = this.orgService.organization; + + // // this.activatedRoute.queryParams.subscribe((params: { [x: string]: string; }) => { + // // this.tip.tip_id = params["tip_id"]; + // // }) + + // //TODO: CHIAMATA PER RECUPERARE LISTA UTENTI DELL'ORGANIZZAZIONE + // // this.httpService.getOrgUsers(this.org_id) + + // } + // } + // ); + } + + + convertiInAffiliata(){ + console.log("CONVERTI IN AFFILIATA - TODO!!!") + } + +} diff --git a/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html b/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html new file mode 100644 index 0000000000..ade2b2c85a --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html @@ -0,0 +1,172 @@ + +
+
+ + + + + + + + + + + +
+ +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + {{'Denomination' | translate}} + + + + + + + + + {{'Type' | translate}} + + + + + + + + + {{'Accreditation Date' | translate}} + + + + + + + + + {{'Users' | translate}} + + + + + + + + + {{'Tips' | translate}} + + + + + + + + + {{'Closed' | translate}} + + + + + + + + + {{'State' | translate}} + + + + + +
+ {{req.id}} + + {{req.denomination}} + + {{req.type}} + + {{req.accreditation_date}} + + {{req.num_user_profiled}} + + {{req.num_tip}} + + {{req.num_tip}} + + {{req.state}} +
+
+ + < {{ 'Previous' | translate }} + + + {{ 'Next' | translate }} > + + << {{ 'First' | translate }} + + + {{ 'Last' | translate }} >> + + +
+ +
+
+
\ No newline at end of file diff --git a/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts b/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts new file mode 100644 index 0000000000..322819efd8 --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts @@ -0,0 +1,76 @@ +import { HttpClient } from '@angular/common/http'; +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { AppDataService } from '@app/app-data.service'; +import { AccreditationRequestModel } from '@app/models/accreditor/organization-data'; +import { AuthenticationService } from '@app/services/helper/authentication.service'; +import { AppConfigService } from '@app/services/root/app-config.service'; +import { AccreditationReqResolver } from '@app/shared/resolvers/accreditation-req-resolver.service'; +import { HttpService } from '@app/shared/services/http.service'; +import { UtilsService } from '@app/shared/services/utils.service'; +import { TranslateService } from '@ngx-translate/core'; +import { filter, orderBy } from 'lodash'; + +@Component({ + selector: 'src-organizations', + templateUrl: './organizations.component.html' +}) +export class OrganizationsComponent implements OnInit{ + + constructor(private http: HttpClient,protected authenticationService: AuthenticationService, protected httpService: HttpService, + private appConfigServices: AppConfigService, private router: Router, protected AReqs: AccreditationReqResolver, + protected utils: UtilsService, protected appDataService: AppDataService, private translateService: TranslateService) { + + } + + search: string | undefined; + selectedReqs: string[] = []; + filteredReqs: AccreditationRequestModel[]; + currentPage: number = 1; + itemsPerPage: number = 20; + sortKey: string = "accreditation_date"; + sortReverse: boolean = true; + + + + ngOnInit(): void { + if (!this.AReqs.dataModel) { + this.router.navigate(["/accreditor/home"]).then(); + } else { + this.filteredReqs = this.AReqs.dataModel; + } + } + + + reload() { + const reloadCallback = () => { + this.AReqs.reload(); + }; + + this.appConfigServices.localInitialization(true, reloadCallback); + } + + + onSearchChange(value: string | number | undefined) { + if (typeof value !== "undefined") { + this.currentPage = 1; + this.filteredReqs = this.AReqs.dataModel; + // this.processTips(); + + this.filteredReqs = orderBy(filter(this.filteredReqs, (req) => + Object.values(req).some((val) => { + if (typeof val === "string" || typeof val === "number") { + return String(val).toLowerCase().includes(String(value).toLowerCase()); + } + return false; + }) + ), "accreditation_date"); + } + } + + orderbyCast(data: AccreditationRequestModel[]): AccreditationRequestModel[] { + return data; + } + + +} diff --git a/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html b/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html new file mode 100644 index 0000000000..abfd855a2d --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html @@ -0,0 +1,8 @@ + + + {{'Home' | translate}} + + + + {{'Organizations' | translate}} + \ No newline at end of file diff --git a/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts b/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts new file mode 100644 index 0000000000..5a9aec12c6 --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts @@ -0,0 +1,24 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { Router } from '@angular/router'; +import { AuthenticationService } from '@app/services/helper/authentication.service'; +import { NodeResolver } from '@app/shared/resolvers/node.resolver'; + +@Component({ + selector: "src-accreditor-sidebar", + templateUrl: "./sidebar.component.html", + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class SidebarComponent { + + constructor(private router: Router, protected nodeResolver: NodeResolver, protected authenticationService: AuthenticationService) { + } + + isActive(route: string): boolean { + return this.router.isActive(route, { + paths: "subset", + queryParams: "subset", + fragment: "ignored", + matrixParams: "ignored" + }); + } +} \ No newline at end of file diff --git a/client/app/src/pages/admin/users/users-tab1/users-tab1.component.html b/client/app/src/pages/admin/users/users-tab1/users-tab1.component.html index 9af8d7dd6a..c05935ac83 100644 --- a/client/app/src/pages/admin/users/users-tab1/users-tab1.component.html +++ b/client/app/src/pages/admin/users/users-tab1/users-tab1.component.html @@ -17,6 +17,7 @@ +
@@ -59,4 +60,4 @@ - \ No newline at end of file + diff --git a/client/app/src/pages/app/app.component.html b/client/app/src/pages/app/app.component.html index 584d9d9744..2b9870ecd0 100644 --- a/client/app/src/pages/app/app.component.html +++ b/client/app/src/pages/app/app.component.html @@ -52,6 +52,7 @@

Error!



+ diff --git a/client/app/src/services/helper/accreditor-org.service.ts b/client/app/src/services/helper/accreditor-org.service.ts new file mode 100644 index 0000000000..d0ce84049c --- /dev/null +++ b/client/app/src/services/helper/accreditor-org.service.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@angular/core'; +import { OrganizationData } from '@app/models/accreditor/organization-data'; +import { HttpService } from '@app/shared/services/http.service'; + +@Injectable({ + providedIn: 'root' +}) +export class AccreditorOrgService { + + organization: OrganizationData = new OrganizationData(); + + constructor(private httpService: HttpService) { + + } + + reset(){ + this.organization = new OrganizationData(); + } +} diff --git a/client/app/src/shared.module.ts b/client/app/src/shared.module.ts index 348a78a2ac..54636a605b 100644 --- a/client/app/src/shared.module.ts +++ b/client/app/src/shared.module.ts @@ -83,6 +83,11 @@ import {ReopenSubmissionComponent} from "@app/shared/modals/reopen-submission/re import {OtkcAccessComponent} from "@app/shared/modals/otkc-access/otkc-access.component"; import {OperationComponent} from "@app/shared/partials/operation/operation.component"; import {RedactInformationComponent} from "@app/shared/modals/redact-information/redact-information.component"; +import { OrgInfoComponent } from "./shared/partials/org-info/org-info.component"; +import { OrgAdminInfoComponent } from "./shared/partials/org-admin-info/org-admin-info.component"; +import { OrgRecipientInfoComponent } from "./shared/partials/org-recipient-info/org-recipient-info.component"; +import { OrgOverviewComponent } from "./shared/partials/org-overview/org-overview.component"; +import { OrgUsersListComponent } from "./shared/partials/org-users-list/org-users-list.component"; @NgModule({ imports: [ @@ -191,7 +196,12 @@ import {RedactInformationComponent} from "@app/shared/modals/redact-information/ SwitchComponent, OtkcAccessComponent, OperationComponent, - RedactInformationComponent + RedactInformationComponent, + OrgInfoComponent, + OrgRecipientInfoComponent, + OrgAdminInfoComponent, + OrgOverviewComponent, + OrgUsersListComponent ], exports: [ FooterComponent, @@ -251,7 +261,12 @@ import {RedactInformationComponent} from "@app/shared/modals/redact-information/ SwitchComponent, OtkcAccessComponent, OperationComponent, - RedactInformationComponent + RedactInformationComponent, + OrgInfoComponent, + OrgRecipientInfoComponent, + OrgAdminInfoComponent, + OrgOverviewComponent, + OrgUsersListComponent ] }) export class SharedModule { diff --git a/client/app/src/shared/guards/accreditor.guard.ts b/client/app/src/shared/guards/accreditor.guard.ts new file mode 100644 index 0000000000..0a570b4fdc --- /dev/null +++ b/client/app/src/shared/guards/accreditor.guard.ts @@ -0,0 +1,28 @@ +import {Injectable} from "@angular/core"; +import {Router, UrlTree} from "@angular/router"; +import {Observable} from "rxjs"; +import {AuthenticationService} from "@app/services/helper/authentication.service"; +import {AppConfigService} from "@app/services/root/app-config.service"; +import {UtilsService} from "@app/shared/services/utils.service"; + +@Injectable({ + providedIn: "root" +}) +export class AccreditorGuard { + constructor(private utilsService: UtilsService, private appConfigService: AppConfigService, private router: Router, public authenticationService: AuthenticationService) { + } + + canActivate(): Observable | Promise | boolean | UrlTree { + if (this.authenticationService.session) { + if(this.authenticationService.session.role === "accreditor"){ + this.appConfigService.setPage(this.router.url); + }else { + this.router.navigateByUrl("/login").then(); + } + return true; + } else { + this.utilsService.routeGuardRedirect(); + return false; + } + } +} diff --git a/client/app/src/shared/partials/org-admin-info/org-admin-info.component.html b/client/app/src/shared/partials/org-admin-info/org-admin-info.component.html new file mode 100644 index 0000000000..7630f67c19 --- /dev/null +++ b/client/app/src/shared/partials/org-admin-info/org-admin-info.component.html @@ -0,0 +1,20 @@ +
+
{{ 'Admin info' | translate }}
+
+
+ + +
+
+ + +
{{'Invalid email format' | translate}}
+
+
+
\ No newline at end of file diff --git a/client/app/src/shared/partials/org-admin-info/org-admin-info.component.ts b/client/app/src/shared/partials/org-admin-info/org-admin-info.component.ts new file mode 100644 index 0000000000..3da484c268 --- /dev/null +++ b/client/app/src/shared/partials/org-admin-info/org-admin-info.component.ts @@ -0,0 +1,18 @@ +import { Component } from '@angular/core'; +import { Constants } from '@app/shared/constants/constants'; + +@Component({ + selector: 'src-org-admin-info', + templateUrl: './org-admin-info.component.html' +}) +export class OrgAdminInfoComponent { + + adminInfo = { + name: '', + email: '', + fiscalCode: '' + }; + + protected readonly Constants = Constants; + +} diff --git a/client/app/src/shared/partials/org-info/org-info.component.html b/client/app/src/shared/partials/org-info/org-info.component.html new file mode 100644 index 0000000000..5661757db4 --- /dev/null +++ b/client/app/src/shared/partials/org-info/org-info.component.html @@ -0,0 +1,35 @@ +
+
{{ 'Organization info' | translate }}
+
+
+ + +
+
+ + +
{{'Invalid PEC format' | translate}}
+
+
+ + +
{{'Invalid PEC format' | translate}}
+
{{'PEC and Confirm PEC must match' | translate}}
+
+
+ + +
+
+
\ No newline at end of file diff --git a/client/app/src/shared/partials/org-info/org-info.component.ts b/client/app/src/shared/partials/org-info/org-info.component.ts new file mode 100644 index 0000000000..b1bdd83722 --- /dev/null +++ b/client/app/src/shared/partials/org-info/org-info.component.ts @@ -0,0 +1,26 @@ +import { Component } from '@angular/core'; +import { Constants } from "@app/shared/constants/constants"; +import {FormsModule} from '@angular/forms' + +@Component({ + selector: 'src-org-info', + templateUrl: './org-info.component.html' +}) +export class OrgInfoComponent { + + protected readonly Constants = Constants; + + organizationInfo = { + denomination: '', + pec: '', + confirmPec: '', + institutionalWebsite: '' + }; + + pecsMatch = true; + + checkPecsMatch() { + this.pecsMatch = this.organizationInfo.pec === this.organizationInfo.confirmPec; + } + +} diff --git a/client/app/src/shared/partials/org-overview/org-overview.component.html b/client/app/src/shared/partials/org-overview/org-overview.component.html new file mode 100644 index 0000000000..daaf25ac0c --- /dev/null +++ b/client/app/src/shared/partials/org-overview/org-overview.component.html @@ -0,0 +1,33 @@ +
+
+ {{'Overview'|translate}} + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + +
{{'Type'|translate}}{{'Accreditation date'|translate}}{{'Tips'|translate}}{{'Closed Tips'|translate}}{{'State'|translate}}
{{org.type}}{{org.accreditation_date}}{{org.num_tip}}{{org.num_tip}}{{org.state}}
+
+
+
diff --git a/client/app/src/shared/partials/org-overview/org-overview.component.ts b/client/app/src/shared/partials/org-overview/org-overview.component.ts new file mode 100644 index 0000000000..132eda04f4 --- /dev/null +++ b/client/app/src/shared/partials/org-overview/org-overview.component.ts @@ -0,0 +1,26 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { AccreditationRequestModel, OrganizationData } from '@app/models/accreditor/organization-data'; + +@Component({ + selector: 'src-org-overview', + templateUrl: './org-overview.component.html' +}) +export class OrgOverviewComponent implements OnInit { + + + @Input() org: OrganizationData + collapsed: boolean = false; + + + ngOnInit(): void { + + } + + + public toggleCollapse() { + this.collapsed = !this.collapsed; + } + + + +} diff --git a/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html b/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html new file mode 100644 index 0000000000..929dd7c1e5 --- /dev/null +++ b/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html @@ -0,0 +1,27 @@ +
+
{{ 'Primary recipient info' | translate }}
+
+
+ + +
+
+ + +
+
+ + +
{{'Invalid email format' | translate}}
+
+
+
\ No newline at end of file diff --git a/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts b/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts new file mode 100644 index 0000000000..640c300cae --- /dev/null +++ b/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts @@ -0,0 +1,17 @@ +import { Component } from '@angular/core'; +import { Constants } from '@app/shared/constants/constants'; + +@Component({ + selector: 'src-org-recipient-info', + templateUrl: './org-recipient-info.component.html' +}) +export class OrgRecipientInfoComponent { + + recipientInfo = { + name: '', + fiscalCode: '', + email: '' + }; + + protected readonly Constants = Constants; +} diff --git a/client/app/src/shared/partials/org-users-list/org-users-list.component.html b/client/app/src/shared/partials/org-users-list/org-users-list.component.html new file mode 100644 index 0000000000..d2a1274060 --- /dev/null +++ b/client/app/src/shared/partials/org-users-list/org-users-list.component.html @@ -0,0 +1,104 @@ +
+
+ {{'Users List'|translate}} + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + {{'Name' | translate}} + + + + + + + + + {{'Role' | translate}} + + + + + + + + + {{'Creation Date' | translate}} + + + + + + + + + {{'Last Access' | translate}} + + + + + + + + + {{'Tips' | translate}} + + + + + + + + + {{'Closed' | translate}} + + + + + +
{{user.id}}{{user.name}}{{user.role}}{{user.creation_date}}{{user.last_access}}{{user.tips}}{{user.closed_tips}}
+
+
+
\ No newline at end of file diff --git a/client/app/src/shared/partials/org-users-list/org-users-list.component.ts b/client/app/src/shared/partials/org-users-list/org-users-list.component.ts new file mode 100644 index 0000000000..e687dffaf8 --- /dev/null +++ b/client/app/src/shared/partials/org-users-list/org-users-list.component.ts @@ -0,0 +1,24 @@ +import { Component, OnInit } from '@angular/core'; +import { EOUser } from '@app/models/app/shared-public-model'; + +@Component({ + selector: 'src-org-users-list', + templateUrl: './org-users-list.component.html' +}) +export class OrgUsersListComponent implements OnInit { + + ngOnInit(): void { + + } + + collapsed: boolean = false; + users: EOUser[] = []; + sortKey: string = "creation_date"; + sortReverse: boolean = true; + + public toggleCollapse() { + this.collapsed = !this.collapsed; + } + + +} diff --git a/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts b/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts new file mode 100644 index 0000000000..19d4f86a15 --- /dev/null +++ b/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts @@ -0,0 +1,81 @@ +import { Injectable } from '@angular/core'; +import { AccreditationRequestModel } from '@app/models/accreditor/organization-data'; +import { UtilsService } from '../services/utils.service'; +import { HttpService } from '../services/http.service'; +import { AuthenticationService } from '@app/services/helper/authentication.service'; +import { map, Observable, of } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class AccreditationReqResolver { + + dataModel: AccreditationRequestModel[] = []; + + constructor(private utilsService: UtilsService, private httpService: HttpService, private authenticationService: AuthenticationService) { + } + + + + reload() { + // this.httpService.accreditorRequestResource().subscribe( + // (response) => { + // this.dataModel = response; + // this.utilsService.reloadComponent(); + // } + // ); + + //TODO MOCKUP + setTimeout(()=> { + this.utilsService.reloadComponent(); + }, 1000) + } + + resolve(): Observable { + if (this.authenticationService.session.role === "accreditor") { + // return this.httpService.accreditorRequestResource().pipe( + // map((response: AccreditationRequestModel[]) => { + // this.dataModel = response; + // return true; + // }) + // ); + + //TODO MOCKUP + setTimeout(() => { + this.dataModel = [ + { + id: "ID1", + denomination: "ORGANIZZAZIONE 1", + type: "NOT_AFFILIATED", + state: "REQUESTED", + accreditation_date: "10-08-2024", + num_user_profiled: 10, + num_tip: 5 + }, + { + id: "ID2", + denomination: "ORGANIZZAZIONE 2", + type: "AFFILIATED", + state: "INVITED", + accreditation_date: "12-08-2024", + num_user_profiled: 1, + num_tip: 0 + }, + { + id: "ID3", + denomination: "ORGANIZZAZIONE 3", + type: "NOT_AFFILIATED", + state: "REJECTED", + accreditation_date: "08-06-2024", + num_user_profiled: 2, + num_tip: 15 + } + + ] + return true; + }, 1000) + } + + return of(true); + } +} From 864434c3298637a670f8e14bafd77e8fe6262cd4 Mon Sep 17 00:00:00 2001 From: Filippo Leonelli Date: Mon, 2 Sep 2024 12:38:22 +0200 Subject: [PATCH 020/607] Fixed relative path --- client/app/src/app-routing.module.ts | 2 +- .../{accreditor => }/accreditor-routing.module.ts | 0 .../accreditor/{accreditor => }/accreditor.module.ts | 0 .../{accreditor => }/home/home.component.html | 0 .../accreditor/{accreditor => }/home/home.component.ts | 0 .../organization/organization.component.html | 0 .../organization/organization.component.ts | 0 .../organizations/organizations.component.html | 0 .../organizations/organizations.component.ts | 0 .../{accreditor => }/sidebar/sidebar.component.html | 0 .../{accreditor => }/sidebar/sidebar.component.ts | 0 client/app/src/shared.module.ts | 10 +++++----- .../org-recipient-info.component.html | 0 .../org-recipient-info.component.ts | 0 14 files changed, 6 insertions(+), 6 deletions(-) rename client/app/src/pages/accreditor/{accreditor => }/accreditor-routing.module.ts (100%) rename client/app/src/pages/accreditor/{accreditor => }/accreditor.module.ts (100%) rename client/app/src/pages/accreditor/{accreditor => }/home/home.component.html (100%) rename client/app/src/pages/accreditor/{accreditor => }/home/home.component.ts (100%) rename client/app/src/pages/accreditor/{accreditor => }/organization/organization.component.html (100%) rename client/app/src/pages/accreditor/{accreditor => }/organization/organization.component.ts (100%) rename client/app/src/pages/accreditor/{accreditor => }/organizations/organizations.component.html (100%) rename client/app/src/pages/accreditor/{accreditor => }/organizations/organizations.component.ts (100%) rename client/app/src/pages/accreditor/{accreditor => }/sidebar/sidebar.component.html (100%) rename client/app/src/pages/accreditor/{accreditor => }/sidebar/sidebar.component.ts (100%) rename client/app/src/shared/partials/{org-recipiant-info => org-recipient-info}/org-recipient-info.component.html (100%) rename client/app/src/shared/partials/{org-recipiant-info => org-recipient-info}/org-recipient-info.component.ts (100%) diff --git a/client/app/src/app-routing.module.ts b/client/app/src/app-routing.module.ts index 57f7bb5517..4f9191caf3 100644 --- a/client/app/src/app-routing.module.ts +++ b/client/app/src/app-routing.module.ts @@ -23,7 +23,7 @@ import {WhistleblowerLoginResolver} from "@app/shared/resolvers/whistleblower-lo import {SubmissionComponent} from "@app/pages/whistleblower/submission/submission.component"; import {AuthRoutingModule} from "@app/pages/auth/auth-routing.module"; import { AccreditorGuard } from "./shared/guards/accreditor.guard"; -import { OrganizationComponent } from "./pages/accreditor/organization/organization.component"; +import { OrganizationComponent } from "@app/pages/accreditor/organization/organization.component"; const routes: Routes = [ diff --git a/client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts b/client/app/src/pages/accreditor/accreditor-routing.module.ts similarity index 100% rename from client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts rename to client/app/src/pages/accreditor/accreditor-routing.module.ts diff --git a/client/app/src/pages/accreditor/accreditor/accreditor.module.ts b/client/app/src/pages/accreditor/accreditor.module.ts similarity index 100% rename from client/app/src/pages/accreditor/accreditor/accreditor.module.ts rename to client/app/src/pages/accreditor/accreditor.module.ts diff --git a/client/app/src/pages/accreditor/accreditor/home/home.component.html b/client/app/src/pages/accreditor/home/home.component.html similarity index 100% rename from client/app/src/pages/accreditor/accreditor/home/home.component.html rename to client/app/src/pages/accreditor/home/home.component.html diff --git a/client/app/src/pages/accreditor/accreditor/home/home.component.ts b/client/app/src/pages/accreditor/home/home.component.ts similarity index 100% rename from client/app/src/pages/accreditor/accreditor/home/home.component.ts rename to client/app/src/pages/accreditor/home/home.component.ts diff --git a/client/app/src/pages/accreditor/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/organization/organization.component.html similarity index 100% rename from client/app/src/pages/accreditor/accreditor/organization/organization.component.html rename to client/app/src/pages/accreditor/organization/organization.component.html diff --git a/client/app/src/pages/accreditor/accreditor/organization/organization.component.ts b/client/app/src/pages/accreditor/organization/organization.component.ts similarity index 100% rename from client/app/src/pages/accreditor/accreditor/organization/organization.component.ts rename to client/app/src/pages/accreditor/organization/organization.component.ts diff --git a/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html b/client/app/src/pages/accreditor/organizations/organizations.component.html similarity index 100% rename from client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html rename to client/app/src/pages/accreditor/organizations/organizations.component.html diff --git a/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts b/client/app/src/pages/accreditor/organizations/organizations.component.ts similarity index 100% rename from client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts rename to client/app/src/pages/accreditor/organizations/organizations.component.ts diff --git a/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html b/client/app/src/pages/accreditor/sidebar/sidebar.component.html similarity index 100% rename from client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html rename to client/app/src/pages/accreditor/sidebar/sidebar.component.html diff --git a/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts b/client/app/src/pages/accreditor/sidebar/sidebar.component.ts similarity index 100% rename from client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts rename to client/app/src/pages/accreditor/sidebar/sidebar.component.ts diff --git a/client/app/src/shared.module.ts b/client/app/src/shared.module.ts index 54636a605b..49fd67864d 100644 --- a/client/app/src/shared.module.ts +++ b/client/app/src/shared.module.ts @@ -83,11 +83,11 @@ import {ReopenSubmissionComponent} from "@app/shared/modals/reopen-submission/re import {OtkcAccessComponent} from "@app/shared/modals/otkc-access/otkc-access.component"; import {OperationComponent} from "@app/shared/partials/operation/operation.component"; import {RedactInformationComponent} from "@app/shared/modals/redact-information/redact-information.component"; -import { OrgInfoComponent } from "./shared/partials/org-info/org-info.component"; -import { OrgAdminInfoComponent } from "./shared/partials/org-admin-info/org-admin-info.component"; -import { OrgRecipientInfoComponent } from "./shared/partials/org-recipient-info/org-recipient-info.component"; -import { OrgOverviewComponent } from "./shared/partials/org-overview/org-overview.component"; -import { OrgUsersListComponent } from "./shared/partials/org-users-list/org-users-list.component"; +import { OrgInfoComponent } from "@app/shared/partials/org-info/org-info.component"; +import { OrgAdminInfoComponent } from "@app/shared/partials/org-admin-info/org-admin-info.component"; +import { OrgRecipientInfoComponent } from "@app/shared/partials/org-recipient-info/org-recipient-info.component"; +import { OrgOverviewComponent } from "@app/shared/partials/org-overview/org-overview.component"; +import { OrgUsersListComponent } from "@app/shared/partials/org-users-list/org-users-list.component"; @NgModule({ imports: [ diff --git a/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html b/client/app/src/shared/partials/org-recipient-info/org-recipient-info.component.html similarity index 100% rename from client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html rename to client/app/src/shared/partials/org-recipient-info/org-recipient-info.component.html diff --git a/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts b/client/app/src/shared/partials/org-recipient-info/org-recipient-info.component.ts similarity index 100% rename from client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts rename to client/app/src/shared/partials/org-recipient-info/org-recipient-info.component.ts From df98391f1029d500d1e5f708320daa2cc8f8a9df Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Mon, 2 Sep 2024 18:16:30 +0200 Subject: [PATCH 021/607] actions on OE --- .../organization/organization.component.html | 2 ++ .../organization/organization.component.ts | 21 +++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/client/app/src/pages/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/organization/organization.component.html index 01252cc562..66cc04554c 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.html +++ b/client/app/src/pages/accreditor/organization/organization.component.html @@ -7,12 +7,14 @@ diff --git a/client/app/src/pages/accreditor/organization/organization.component.ts b/client/app/src/pages/accreditor/organization/organization.component.ts index ab120d4cde..dcdaa7a3b7 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.ts +++ b/client/app/src/pages/accreditor/organization/organization.component.ts @@ -16,6 +16,8 @@ export class OrganizationComponent implements OnInit{ loading: boolean = false; organization: OrganizationData; + + constructor(private activatedRoute: ActivatedRoute, private httpService: HttpService, private orgService : AccreditorOrgService){ } @@ -40,7 +42,13 @@ export class OrganizationComponent implements OnInit{ this.organization.num_tip = 10 this.organization.num_user_profiled = 2 this.organization.type = "NOT_AFFILIATED" - this.organization.state = "ACCREDITED" + // • 0 -> REQUESTED + // • 1 -> ACCREDITED + // • 2 -> REJECTED + // • 3 -> INSTRUCTOR_REQUEST + // • 4 -> INVITED + // • 5 -> SUSPEND + this.organization.state = 0 let users : EOUser[] = []; users.push({ @@ -81,5 +89,14 @@ export class OrganizationComponent implements OnInit{ convertiInAffiliata(){ console.log("CONVERTI IN AFFILIATA - TODO!!!") } - + + isSuspendable(){ + return this.organization.state == 1 + } + + isDeletable(){ + return this.organization.state == 1 + } + + } From 8444569a1da272692cbb8bc447ca13d3c1bf157f Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Mon, 2 Sep 2024 18:16:53 +0200 Subject: [PATCH 022/607] fix on organization data type --- client/app/src/models/accreditor/organization-data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/app/src/models/accreditor/organization-data.ts b/client/app/src/models/accreditor/organization-data.ts index d64e00078a..b492a282b2 100644 --- a/client/app/src/models/accreditor/organization-data.ts +++ b/client/app/src/models/accreditor/organization-data.ts @@ -5,7 +5,7 @@ export class OrganizationData { id: string; denomination: string; type: string; - state: string; + state: number; accreditation_date: string; num_user_profiled: number; num_tip: number; From 947bd9b3b839d5e9aa29aa3285779da744c1441d Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Mon, 2 Sep 2024 18:26:40 +0200 Subject: [PATCH 023/607] check if organization is suspendable --- .../accreditor/organization/organization.component.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/client/app/src/pages/accreditor/organization/organization.component.ts b/client/app/src/pages/accreditor/organization/organization.component.ts index dcdaa7a3b7..7542ce03d4 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.ts +++ b/client/app/src/pages/accreditor/organization/organization.component.ts @@ -48,7 +48,7 @@ export class OrganizationComponent implements OnInit{ // • 3 -> INSTRUCTOR_REQUEST // • 4 -> INVITED // • 5 -> SUSPEND - this.organization.state = 0 + this.organization.state = 5 let users : EOUser[] = []; users.push({ @@ -90,8 +90,14 @@ export class OrganizationComponent implements OnInit{ console.log("CONVERTI IN AFFILIATA - TODO!!!") } + // • 0 -> REQUESTED + // • 1 -> ACCREDITED + // • 2 -> REJECTED + // • 3 -> INSTRUCTOR_REQUEST + // • 4 -> INVITED + // • 5 -> SUSPEND isSuspendable(){ - return this.organization.state == 1 + return [1, 5].includes(this.organization.state) } isDeletable(){ From 1a962e778ac537b8c1ae9900356803fd4df08191 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Tue, 3 Sep 2024 15:17:59 +0200 Subject: [PATCH 024/607] OE detail for all status --- .../models/accreditor/organization-data.ts | 2 +- .../organization/organization.component.html | 43 +++++++++++++++---- .../organization/organization.component.ts | 38 +++++++++++++--- 3 files changed, 67 insertions(+), 16 deletions(-) diff --git a/client/app/src/models/accreditor/organization-data.ts b/client/app/src/models/accreditor/organization-data.ts index b492a282b2..d64e00078a 100644 --- a/client/app/src/models/accreditor/organization-data.ts +++ b/client/app/src/models/accreditor/organization-data.ts @@ -5,7 +5,7 @@ export class OrganizationData { id: string; denomination: string; type: string; - state: number; + state: string; accreditation_date: string; num_user_profiled: number; num_tip: number; diff --git a/client/app/src/pages/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/organization/organization.component.html index 66cc04554c..bb83c7bf48 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.html +++ b/client/app/src/pages/accreditor/organization/organization.component.html @@ -7,22 +7,24 @@ - - + - - + -
+
+
+
+ + {{'Enable email notifications'|translate}} +
+
+
@@ -66,4 +75,22 @@
+ +
+
+
+ +
+
+ +
+
+
diff --git a/client/app/src/pages/accreditor/organization/organization.component.ts b/client/app/src/pages/accreditor/organization/organization.component.ts index 7542ce03d4..f3d9821656 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.ts +++ b/client/app/src/pages/accreditor/organization/organization.component.ts @@ -4,7 +4,6 @@ import { OrganizationData } from '@app/models/accreditor/organization-data'; import { EOUser } from '@app/models/app/shared-public-model'; import { AccreditorOrgService } from '@app/services/helper/accreditor-org.service'; import { HttpService } from '@app/shared/services/http.service'; -import { Observable } from 'rxjs'; @Component({ selector: 'src-organization', @@ -15,7 +14,7 @@ export class OrganizationComponent implements OnInit{ org_id: string | null; loading: boolean = false; organization: OrganizationData; - + org_type: boolean = false; constructor(private activatedRoute: ActivatedRoute, private httpService: HttpService, private orgService : AccreditorOrgService){ @@ -48,7 +47,8 @@ export class OrganizationComponent implements OnInit{ // • 3 -> INSTRUCTOR_REQUEST // • 4 -> INVITED // • 5 -> SUSPEND - this.organization.state = 5 + // • 6 -> APPROVED + this.organization.state = "INSTRUCTOR_REQUEST" let users : EOUser[] = []; users.push({ @@ -90,19 +90,43 @@ export class OrganizationComponent implements OnInit{ console.log("CONVERTI IN AFFILIATA - TODO!!!") } + invia(){ + if(this.org_type) + this.organization.type = "AFFILIATED" + + console.log("INVIA / INVIA INVITO - TODO!!!") + } + + rifiuta(){ + console.log("REJECT - TODO!!!") + + } + // • 0 -> REQUESTED // • 1 -> ACCREDITED // • 2 -> REJECTED // • 3 -> INSTRUCTOR_REQUEST // • 4 -> INVITED // • 5 -> SUSPEND - isSuspendable(){ - return [1, 5].includes(this.organization.state) + + isRequested(){ + return this.organization.state === "REQUESTED" + } + + isInstructorRequest(){ + return this.organization.state === "INSTRUCTOR_REQUEST" } - isDeletable(){ - return this.organization.state == 1 + isAccredited(){ + return this.organization.state === "ACCREDITED" } + isSuspended(){ + return this.organization.state === "SUSPENDED" + } + + isInvited(){ + return this.organization.state === "INVITED" + } } From 2c2d259c084d9469ddadf9a39c0fc01dc5d1e705 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Tue, 3 Sep 2024 15:26:33 +0200 Subject: [PATCH 025/607] fix visibility admin and recipient info --- .../pages/accreditor/organization/organization.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/app/src/pages/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/organization/organization.component.html index bb83c7bf48..9737fbfcdc 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.html +++ b/client/app/src/pages/accreditor/organization/organization.component.html @@ -41,11 +41,11 @@ -
+
-
+
From 0b836222916f5ff7b9d43a257b3bbce1a200add6 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Tue, 3 Sep 2024 16:39:17 +0200 Subject: [PATCH 026/607] fix for accreditation_req initialization --- .../app/src/pages/accreditor/accreditor-routing.module.ts | 6 ++++++ .../shared/resolvers/accreditation-req-resolver.service.ts | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/client/app/src/pages/accreditor/accreditor-routing.module.ts b/client/app/src/pages/accreditor/accreditor-routing.module.ts index 756d7b887a..5e19694983 100644 --- a/client/app/src/pages/accreditor/accreditor-routing.module.ts +++ b/client/app/src/pages/accreditor/accreditor-routing.module.ts @@ -9,12 +9,18 @@ const routes: Routes = [ path: "", component: AccreditorHomeComponent, pathMatch: "full", + resolve: { + AccreditationReqResolver + }, data: {sidebar: "accreditor-sidebar", pageTitle: "Home"}, }, { path: "home", component: AccreditorHomeComponent, pathMatch: "full", + resolve: { + AccreditationReqResolver + }, data: {sidebar: "accreditor-sidebar", pageTitle: "Home"}, }, { diff --git a/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts b/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts index 19d4f86a15..5d7e2a500d 100644 --- a/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts +++ b/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts @@ -4,6 +4,7 @@ import { UtilsService } from '../services/utils.service'; import { HttpService } from '../services/http.service'; import { AuthenticationService } from '@app/services/helper/authentication.service'; import { map, Observable, of } from 'rxjs'; +import { AppDataService } from '@app/app-data.service'; @Injectable({ providedIn: 'root' @@ -12,7 +13,9 @@ export class AccreditationReqResolver { dataModel: AccreditationRequestModel[] = []; - constructor(private utilsService: UtilsService, private httpService: HttpService, private authenticationService: AuthenticationService) { + constructor(private utilsService: UtilsService, private httpService: HttpService, + private authenticationService: AuthenticationService, + private appDataService: AppDataService) { } @@ -28,6 +31,7 @@ export class AccreditationReqResolver { //TODO MOCKUP setTimeout(()=> { this.utilsService.reloadComponent(); + this.appDataService.updateShowLoadingPanel(false) }, 1000) } @@ -72,6 +76,7 @@ export class AccreditationReqResolver { } ] + this.appDataService.updateShowLoadingPanel(false) return true; }, 1000) } From 396e6ea5918ac77ac96d3feea451329ea5a91076 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Tue, 3 Sep 2024 16:43:34 +0200 Subject: [PATCH 027/607] label fix --- .../pages/accreditor/organization/organization.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/app/src/pages/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/organization/organization.component.html index 9737fbfcdc..fc41867973 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.html +++ b/client/app/src/pages/accreditor/organization/organization.component.html @@ -59,7 +59,7 @@
- {{'Enable email notifications'|translate}} + {{'Affiliata'|translate}}
From 562c9ee6017f6ade4a57b39e01b5b6278f7a2621 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Thu, 5 Sep 2024 15:14:00 +0200 Subject: [PATCH 028/607] Update accreditation.py, api.py, and requests.py --- backend/globaleaks/handlers/accreditation.py | 18 ++++++++ backend/globaleaks/rest/api.py | 43 +++++++++++--------- backend/globaleaks/rest/requests.py | 18 ++++++++ 3 files changed, 59 insertions(+), 20 deletions(-) create mode 100644 backend/globaleaks/handlers/accreditation.py diff --git a/backend/globaleaks/handlers/accreditation.py b/backend/globaleaks/handlers/accreditation.py new file mode 100644 index 0000000000..598ba49b26 --- /dev/null +++ b/backend/globaleaks/handlers/accreditation.py @@ -0,0 +1,18 @@ +from globaleaks.handlers.base import BaseHandler +from globaleaks.rest import requests + + +class SubmitAccreditationHandler(BaseHandler): + """ + This manager is responsible for receiving accreditation requests and forwarding them to the accreditation manager + """ + check_roles = 'any' + root_tenant_only = True + invalidate_cache = True + + def post(self): + self.request.headers.get(b'x-fiscalcode') + request = self.validate_request( + self.request.content.read(), + requests.SubmitAccreditation) + diff --git a/backend/globaleaks/rest/api.py b/backend/globaleaks/rest/api.py index bed8b19a46..20d6f94312 100644 --- a/backend/globaleaks/rest/api.py +++ b/backend/globaleaks/rest/api.py @@ -16,26 +16,26 @@ from globaleaks import LANGUAGES_SUPPORTED_CODES from globaleaks.handlers import admin, \ - analyst, \ - auth, \ - custodian, \ - file, \ - health, \ - l10n, \ - public, \ - recipient, \ - redirect, \ - robots, \ - security, \ - signup, \ - sitemap, \ - support, \ - staticfile, \ - support, \ - user, \ - viewer, \ - wizard, \ - whistleblower + analyst, \ + auth, \ + custodian, \ + file, \ + health, \ + l10n, \ + public, \ + recipient, \ + redirect, \ + robots, \ + security, \ + signup, \ + sitemap, \ + support, \ + staticfile, \ + support, \ + user, \ + viewer, \ + wizard, \ + whistleblower, accreditation from globaleaks.rest import decorators, errors from globaleaks.state import State, extract_exception_traceback_and_schedule_email @@ -141,6 +141,9 @@ (r'/api/admin/statuses/' + r'(closed)' + r'/substatuses/' + uuid_regexp, admin.submission_statuses.SubmissionSubStatusInstance), (r'/api/admin/statuses/' + uuid_regexp + r'/substatuses/' + uuid_regexp, admin.submission_statuses.SubmissionSubStatusInstance), + # Accreditation + (r'/accreditation', accreditation.SubmitAccreditationHandler), + # Services (r'/api/support', support.SupportHandler), (r'/api/signup', signup.Signup), diff --git a/backend/globaleaks/rest/requests.py b/backend/globaleaks/rest/requests.py index c03baea348..5261fd0ba4 100644 --- a/backend/globaleaks/rest/requests.py +++ b/backend/globaleaks/rest/requests.py @@ -525,6 +525,24 @@ def get_multilang_request_format(request_format, localized_strings): 'tos2': bool } +SubmitAccreditation = { + 'organization_name': str +} +""" + 'organization_email': email_regexp, + 'organization_site': hostname_regexp_or_empty, + 'admin_name': alphanumeric_str_regexp, + 'admin_surname': alphanumeric_str_regexp, + 'admin_email': email_regexp, + 'receiver_name': alphanumeric_str_regexp, + 'receiver_surname': alphanumeric_str_regexp, + 'receiver_tax_code': alphanumeric_str_regexp, + 'receiver_email': email_regexp, + + 'tos1': bool, + 'tos2': bool +""" + SupportDesc = { 'mail_address': email_regexp, 'url': url_regexp_or_empty, From cfa4d67dbc44b95266916c3a8867905c5ad7c673 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Mon, 9 Sep 2024 13:11:41 +0200 Subject: [PATCH 029/607] privacy in accretitation request module --- client/app/src/app-routing.module.ts | 2 +- client/app/src/pages/accred/accred/accred.component.html | 7 +++++-- client/app/src/pages/accred/accred/accred.component.ts | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/client/app/src/app-routing.module.ts b/client/app/src/app-routing.module.ts index 4f9191caf3..dd7ec1a85c 100644 --- a/client/app/src/app-routing.module.ts +++ b/client/app/src/app-routing.module.ts @@ -129,7 +129,7 @@ const routes: Routes = [ loadChildren: () => import("./pages/wizard/wizard-routing.module").then(m => m.WizardRoutingModule) }, { - path: "accred", + path: "accreditation-request", data: {pageTitle: "Accreditation"}, resolve: { PreferenceResolver, diff --git a/client/app/src/pages/accred/accred/accred.component.html b/client/app/src/pages/accred/accred/accred.component.html index 22b1674e60..7ff854db0b 100644 --- a/client/app/src/pages/accred/accred/accred.component.html +++ b/client/app/src/pages/accred/accred/accred.component.html @@ -107,13 +107,16 @@
-
{{ 'Privacy Policy' | translate }}
-
{{ privacyPolicy | translate }}
+ {{'Privacy Policy'}}
{{ 'I have read and agree to the privacy policy.' | translate }} +
+ {{ 'Una volta inoltrata la richiesta di accreditamento riceverai una notifica via email.' | translate }}
+ +

diff --git a/client/app/src/pages/accred/accred/accred.component.ts b/client/app/src/pages/accred/accred/accred.component.ts index c48e1a7433..d8a0478ddc 100644 --- a/client/app/src/pages/accred/accred/accred.component.ts +++ b/client/app/src/pages/accred/accred/accred.component.ts @@ -39,7 +39,7 @@ export class AccredComponent implements OnInit { privacyAccept = false; privacyPolicy = 'Your privacy policy text here...'; - constructor(private authentication: AuthenticationService, public router: Router, private route: ActivatedRoute, protected appDataService: AppDataService, private modalService: NgbModal) {} + constructor(public router: Router, private route: ActivatedRoute, protected appDataService: AppDataService, private modalService: NgbModal) {} ngOnInit() { this.adminInfo.fiscalCode = this.getFiscalCodeFromIdp(); From 98f07459826071fa495d428e73a253412ef97ecf Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Mon, 9 Sep 2024 14:39:41 +0200 Subject: [PATCH 030/607] margin bottom to privacy policy link --- client/app/css/main.css | 5 +++++ .../src/pages/accred/accred/accred.component.html | 12 +++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/client/app/css/main.css b/client/app/css/main.css index df31c63ec3..532bd3bf93 100644 --- a/client/app/css/main.css +++ b/client/app/css/main.css @@ -1134,3 +1134,8 @@ i.fa-solid.fa-chevron-right { .underline { text-decoration: underline; } + + +#AccreditationRequestPrivacy div { + margin-bottom: 0.5em; +} \ No newline at end of file diff --git a/client/app/src/pages/accred/accred/accred.component.html b/client/app/src/pages/accred/accred/accred.component.html index 7ff854db0b..00f637d1fb 100644 --- a/client/app/src/pages/accred/accred/accred.component.html +++ b/client/app/src/pages/accred/accred/accred.component.html @@ -13,7 +13,6 @@
-
{{ 'Organization info' | translate }}
@@ -106,14 +105,17 @@
-
- {{'Privacy Policy'}} +
+ +
{{ 'I have read and agree to the privacy policy.' | translate }} -
- {{ 'Una volta inoltrata la richiesta di accreditamento riceverai una notifica via email.' | translate }} +
+ {{ 'Una volta inoltrata la richiesta di accreditamento riceverai una notifica via email.' | translate }}
From 5c1a03298e95cf3df1b6855841c7d9bb1eb8613a Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Mon, 9 Sep 2024 14:41:06 +0200 Subject: [PATCH 031/607] index on feature/accreditamento: 562c9ee Update accreditation.py, api.py, and requests.py From 255d65a0b4ca943da5051dbc678ba8073546d486 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Mon, 9 Sep 2024 15:32:25 +0200 Subject: [PATCH 032/607] Merge commit 'e9863dee413c03ce131e9f2f87f9d87f0865e9e5' --- backend/globaleaks/settings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/globaleaks/settings.py b/backend/globaleaks/settings.py index 232bcff93f..c6c6bb094a 100644 --- a/backend/globaleaks/settings.py +++ b/backend/globaleaks/settings.py @@ -8,8 +8,8 @@ this_directory = os.path.dirname(__file__) possible_client_paths = [ - os.path.abspath(os.path.join(this_directory, '../../client/build/')), - '/usr/share/globaleaks/client/' + '/usr/share/globaleaks/client/', + os.path.abspath(os.path.join(this_directory, '../../client/build/')) ] From bba536ed835b978124ad3cf79ec66370e1c80808 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Mon, 9 Sep 2024 16:06:09 +0200 Subject: [PATCH 033/607] Update token.py --- backend/globaleaks/utils/token.py | 41 +------------------------------ 1 file changed, 1 insertion(+), 40 deletions(-) diff --git a/backend/globaleaks/utils/token.py b/backend/globaleaks/utils/token.py index 2ac4f4d375..dd81a19897 100644 --- a/backend/globaleaks/utils/token.py +++ b/backend/globaleaks/utils/token.py @@ -16,9 +16,7 @@ def __init__(self, tid): self.creation_date = datetime_now() def serialize(self): - otp = CryptoOTP() return { - # 'id': f"{self.id.decode()}:{otp.exec(self.id.decode())}", 'id': self.id.decode(), 'creation_date': self.creation_date, 'complexity': 4 @@ -60,41 +58,4 @@ def validate(self, token_answer): except: raise errors.InternalServerError("TokenFailure: Invalid token") - return token - -import hashlib -import asyncio -class CryptoOTP: - def __init__(self): - self.data = "" - self.counter = 0 - - def calculate_hash(self, hash_bytes: bytes) -> int: - # Verifica se l'ultimo byte dell'hash è 0 - if hash_bytes[-1] == 0: - return self.counter - else: - self.counter += 1 - return None # Indica che è necessario continuare a lavorare - - @staticmethod - def convert_string(string: str) -> bytes: - # Converte una stringa in un array di byte - return string.encode('utf-8') - - def search_collision(self) -> int: - # Loop asincrono per trovare il valore che soddisfa la condizione - while True: - to_hash = self.convert_string(self.data + str(self.counter)) - hash_result = hashlib.sha256(to_hash).digest() - - result = self.calculate_hash(hash_result) - if result is not None: - return result # Hash valido trovato - - def exec(self, data: str) -> int: - # Inizializza i dati e il contatore - self.data = data - self.counter = 0 - - return self.search_collision() + return token \ No newline at end of file From 21c80f8f2458af282c28ae7f9b3e9b51dfc63957 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Tue, 10 Sep 2024 11:47:29 +0200 Subject: [PATCH 034/607] accreditation model + fiscal code validation in form --- .../app/src/models/app/shared-public-model.ts | 21 ++++++++++++++++ .../pages/accred/accred/accred.component.html | 8 +++--- .../pages/accred/accred/accred.component.ts | 25 ++++++++++--------- client/app/src/shared/constants/constants.ts | 1 + 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/client/app/src/models/app/shared-public-model.ts b/client/app/src/models/app/shared-public-model.ts index b1e335d046..36c95d199f 100644 --- a/client/app/src/models/app/shared-public-model.ts +++ b/client/app/src/models/app/shared-public-model.ts @@ -209,3 +209,24 @@ export interface EOUser { tips: number; closed_tips: number; } + + +export interface ExternalOrganization { + denomination: string; + pec: string; + institutional_site: string; +} + + +export interface EOAdmin { + name: string; + surname: string; + email: string; +} + +export interface EOPrimaryReceiver { + name: string; + surname: string; + email: string; + fiscal_code: string; +} \ No newline at end of file diff --git a/client/app/src/pages/accred/accred/accred.component.html b/client/app/src/pages/accred/accred/accred.component.html index 00f637d1fb..1699f70a3d 100644 --- a/client/app/src/pages/accred/accred/accred.component.html +++ b/client/app/src/pages/accred/accred/accred.component.html @@ -36,7 +36,7 @@ {{'Confirm PEC'|translate}} * - +
{{'Invalid PEC format' | translate}}
{{'PEC and Confirm PEC must match' | translate}}
@@ -44,13 +44,12 @@ - +
-
{{ 'Admin info' | translate }}
@@ -89,7 +88,8 @@ {{'Fiscal Code'|translate}} * - + +
{{'Invalid fiscal code format' | translate}}
diff --git a/client/app/src/services/helper/accreditor-org.service.ts b/client/app/src/services/helper/accreditor-org.service.ts new file mode 100644 index 0000000000..d0ce84049c --- /dev/null +++ b/client/app/src/services/helper/accreditor-org.service.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@angular/core'; +import { OrganizationData } from '@app/models/accreditor/organization-data'; +import { HttpService } from '@app/shared/services/http.service'; + +@Injectable({ + providedIn: 'root' +}) +export class AccreditorOrgService { + + organization: OrganizationData = new OrganizationData(); + + constructor(private httpService: HttpService) { + + } + + reset(){ + this.organization = new OrganizationData(); + } +} diff --git a/client/app/src/shared.module.ts b/client/app/src/shared.module.ts index 348a78a2ac..54636a605b 100644 --- a/client/app/src/shared.module.ts +++ b/client/app/src/shared.module.ts @@ -83,6 +83,11 @@ import {ReopenSubmissionComponent} from "@app/shared/modals/reopen-submission/re import {OtkcAccessComponent} from "@app/shared/modals/otkc-access/otkc-access.component"; import {OperationComponent} from "@app/shared/partials/operation/operation.component"; import {RedactInformationComponent} from "@app/shared/modals/redact-information/redact-information.component"; +import { OrgInfoComponent } from "./shared/partials/org-info/org-info.component"; +import { OrgAdminInfoComponent } from "./shared/partials/org-admin-info/org-admin-info.component"; +import { OrgRecipientInfoComponent } from "./shared/partials/org-recipient-info/org-recipient-info.component"; +import { OrgOverviewComponent } from "./shared/partials/org-overview/org-overview.component"; +import { OrgUsersListComponent } from "./shared/partials/org-users-list/org-users-list.component"; @NgModule({ imports: [ @@ -191,7 +196,12 @@ import {RedactInformationComponent} from "@app/shared/modals/redact-information/ SwitchComponent, OtkcAccessComponent, OperationComponent, - RedactInformationComponent + RedactInformationComponent, + OrgInfoComponent, + OrgRecipientInfoComponent, + OrgAdminInfoComponent, + OrgOverviewComponent, + OrgUsersListComponent ], exports: [ FooterComponent, @@ -251,7 +261,12 @@ import {RedactInformationComponent} from "@app/shared/modals/redact-information/ SwitchComponent, OtkcAccessComponent, OperationComponent, - RedactInformationComponent + RedactInformationComponent, + OrgInfoComponent, + OrgRecipientInfoComponent, + OrgAdminInfoComponent, + OrgOverviewComponent, + OrgUsersListComponent ] }) export class SharedModule { diff --git a/client/app/src/shared/guards/accreditor.guard.ts b/client/app/src/shared/guards/accreditor.guard.ts new file mode 100644 index 0000000000..0a570b4fdc --- /dev/null +++ b/client/app/src/shared/guards/accreditor.guard.ts @@ -0,0 +1,28 @@ +import {Injectable} from "@angular/core"; +import {Router, UrlTree} from "@angular/router"; +import {Observable} from "rxjs"; +import {AuthenticationService} from "@app/services/helper/authentication.service"; +import {AppConfigService} from "@app/services/root/app-config.service"; +import {UtilsService} from "@app/shared/services/utils.service"; + +@Injectable({ + providedIn: "root" +}) +export class AccreditorGuard { + constructor(private utilsService: UtilsService, private appConfigService: AppConfigService, private router: Router, public authenticationService: AuthenticationService) { + } + + canActivate(): Observable | Promise | boolean | UrlTree { + if (this.authenticationService.session) { + if(this.authenticationService.session.role === "accreditor"){ + this.appConfigService.setPage(this.router.url); + }else { + this.router.navigateByUrl("/login").then(); + } + return true; + } else { + this.utilsService.routeGuardRedirect(); + return false; + } + } +} diff --git a/client/app/src/shared/partials/org-admin-info/org-admin-info.component.html b/client/app/src/shared/partials/org-admin-info/org-admin-info.component.html new file mode 100644 index 0000000000..7630f67c19 --- /dev/null +++ b/client/app/src/shared/partials/org-admin-info/org-admin-info.component.html @@ -0,0 +1,20 @@ +
+
{{ 'Admin info' | translate }}
+
+
+ + +
+
+ + +
{{'Invalid email format' | translate}}
+
+
+
\ No newline at end of file diff --git a/client/app/src/shared/partials/org-admin-info/org-admin-info.component.ts b/client/app/src/shared/partials/org-admin-info/org-admin-info.component.ts new file mode 100644 index 0000000000..3da484c268 --- /dev/null +++ b/client/app/src/shared/partials/org-admin-info/org-admin-info.component.ts @@ -0,0 +1,18 @@ +import { Component } from '@angular/core'; +import { Constants } from '@app/shared/constants/constants'; + +@Component({ + selector: 'src-org-admin-info', + templateUrl: './org-admin-info.component.html' +}) +export class OrgAdminInfoComponent { + + adminInfo = { + name: '', + email: '', + fiscalCode: '' + }; + + protected readonly Constants = Constants; + +} diff --git a/client/app/src/shared/partials/org-info/org-info.component.html b/client/app/src/shared/partials/org-info/org-info.component.html new file mode 100644 index 0000000000..5661757db4 --- /dev/null +++ b/client/app/src/shared/partials/org-info/org-info.component.html @@ -0,0 +1,35 @@ +
+
{{ 'Organization info' | translate }}
+
+
+ + +
+
+ + +
{{'Invalid PEC format' | translate}}
+
+
+ + +
{{'Invalid PEC format' | translate}}
+
{{'PEC and Confirm PEC must match' | translate}}
+
+
+ + +
+
+
\ No newline at end of file diff --git a/client/app/src/shared/partials/org-info/org-info.component.ts b/client/app/src/shared/partials/org-info/org-info.component.ts new file mode 100644 index 0000000000..b1bdd83722 --- /dev/null +++ b/client/app/src/shared/partials/org-info/org-info.component.ts @@ -0,0 +1,26 @@ +import { Component } from '@angular/core'; +import { Constants } from "@app/shared/constants/constants"; +import {FormsModule} from '@angular/forms' + +@Component({ + selector: 'src-org-info', + templateUrl: './org-info.component.html' +}) +export class OrgInfoComponent { + + protected readonly Constants = Constants; + + organizationInfo = { + denomination: '', + pec: '', + confirmPec: '', + institutionalWebsite: '' + }; + + pecsMatch = true; + + checkPecsMatch() { + this.pecsMatch = this.organizationInfo.pec === this.organizationInfo.confirmPec; + } + +} diff --git a/client/app/src/shared/partials/org-overview/org-overview.component.html b/client/app/src/shared/partials/org-overview/org-overview.component.html new file mode 100644 index 0000000000..daaf25ac0c --- /dev/null +++ b/client/app/src/shared/partials/org-overview/org-overview.component.html @@ -0,0 +1,33 @@ +
+
+ {{'Overview'|translate}} + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + +
{{'Type'|translate}}{{'Accreditation date'|translate}}{{'Tips'|translate}}{{'Closed Tips'|translate}}{{'State'|translate}}
{{org.type}}{{org.accreditation_date}}{{org.num_tip}}{{org.num_tip}}{{org.state}}
+
+
+
diff --git a/client/app/src/shared/partials/org-overview/org-overview.component.ts b/client/app/src/shared/partials/org-overview/org-overview.component.ts new file mode 100644 index 0000000000..132eda04f4 --- /dev/null +++ b/client/app/src/shared/partials/org-overview/org-overview.component.ts @@ -0,0 +1,26 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { AccreditationRequestModel, OrganizationData } from '@app/models/accreditor/organization-data'; + +@Component({ + selector: 'src-org-overview', + templateUrl: './org-overview.component.html' +}) +export class OrgOverviewComponent implements OnInit { + + + @Input() org: OrganizationData + collapsed: boolean = false; + + + ngOnInit(): void { + + } + + + public toggleCollapse() { + this.collapsed = !this.collapsed; + } + + + +} diff --git a/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html b/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html new file mode 100644 index 0000000000..929dd7c1e5 --- /dev/null +++ b/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html @@ -0,0 +1,27 @@ +
+
{{ 'Primary recipient info' | translate }}
+
+
+ + +
+
+ + +
+
+ + +
{{'Invalid email format' | translate}}
+
+
+
\ No newline at end of file diff --git a/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts b/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts new file mode 100644 index 0000000000..640c300cae --- /dev/null +++ b/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts @@ -0,0 +1,17 @@ +import { Component } from '@angular/core'; +import { Constants } from '@app/shared/constants/constants'; + +@Component({ + selector: 'src-org-recipient-info', + templateUrl: './org-recipient-info.component.html' +}) +export class OrgRecipientInfoComponent { + + recipientInfo = { + name: '', + fiscalCode: '', + email: '' + }; + + protected readonly Constants = Constants; +} diff --git a/client/app/src/shared/partials/org-users-list/org-users-list.component.html b/client/app/src/shared/partials/org-users-list/org-users-list.component.html new file mode 100644 index 0000000000..d2a1274060 --- /dev/null +++ b/client/app/src/shared/partials/org-users-list/org-users-list.component.html @@ -0,0 +1,104 @@ +
+
+ {{'Users List'|translate}} + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + {{'Name' | translate}} + + + + + + + + + {{'Role' | translate}} + + + + + + + + + {{'Creation Date' | translate}} + + + + + + + + + {{'Last Access' | translate}} + + + + + + + + + {{'Tips' | translate}} + + + + + + + + + {{'Closed' | translate}} + + + + + +
{{user.id}}{{user.name}}{{user.role}}{{user.creation_date}}{{user.last_access}}{{user.tips}}{{user.closed_tips}}
+
+
+
\ No newline at end of file diff --git a/client/app/src/shared/partials/org-users-list/org-users-list.component.ts b/client/app/src/shared/partials/org-users-list/org-users-list.component.ts new file mode 100644 index 0000000000..e687dffaf8 --- /dev/null +++ b/client/app/src/shared/partials/org-users-list/org-users-list.component.ts @@ -0,0 +1,24 @@ +import { Component, OnInit } from '@angular/core'; +import { EOUser } from '@app/models/app/shared-public-model'; + +@Component({ + selector: 'src-org-users-list', + templateUrl: './org-users-list.component.html' +}) +export class OrgUsersListComponent implements OnInit { + + ngOnInit(): void { + + } + + collapsed: boolean = false; + users: EOUser[] = []; + sortKey: string = "creation_date"; + sortReverse: boolean = true; + + public toggleCollapse() { + this.collapsed = !this.collapsed; + } + + +} diff --git a/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts b/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts new file mode 100644 index 0000000000..19d4f86a15 --- /dev/null +++ b/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts @@ -0,0 +1,81 @@ +import { Injectable } from '@angular/core'; +import { AccreditationRequestModel } from '@app/models/accreditor/organization-data'; +import { UtilsService } from '../services/utils.service'; +import { HttpService } from '../services/http.service'; +import { AuthenticationService } from '@app/services/helper/authentication.service'; +import { map, Observable, of } from 'rxjs'; + +@Injectable({ + providedIn: 'root' +}) +export class AccreditationReqResolver { + + dataModel: AccreditationRequestModel[] = []; + + constructor(private utilsService: UtilsService, private httpService: HttpService, private authenticationService: AuthenticationService) { + } + + + + reload() { + // this.httpService.accreditorRequestResource().subscribe( + // (response) => { + // this.dataModel = response; + // this.utilsService.reloadComponent(); + // } + // ); + + //TODO MOCKUP + setTimeout(()=> { + this.utilsService.reloadComponent(); + }, 1000) + } + + resolve(): Observable { + if (this.authenticationService.session.role === "accreditor") { + // return this.httpService.accreditorRequestResource().pipe( + // map((response: AccreditationRequestModel[]) => { + // this.dataModel = response; + // return true; + // }) + // ); + + //TODO MOCKUP + setTimeout(() => { + this.dataModel = [ + { + id: "ID1", + denomination: "ORGANIZZAZIONE 1", + type: "NOT_AFFILIATED", + state: "REQUESTED", + accreditation_date: "10-08-2024", + num_user_profiled: 10, + num_tip: 5 + }, + { + id: "ID2", + denomination: "ORGANIZZAZIONE 2", + type: "AFFILIATED", + state: "INVITED", + accreditation_date: "12-08-2024", + num_user_profiled: 1, + num_tip: 0 + }, + { + id: "ID3", + denomination: "ORGANIZZAZIONE 3", + type: "NOT_AFFILIATED", + state: "REJECTED", + accreditation_date: "08-06-2024", + num_user_profiled: 2, + num_tip: 15 + } + + ] + return true; + }, 1000) + } + + return of(true); + } +} From 9d12afd14b50c3d1da2264c3f2e43345f7b4668d Mon Sep 17 00:00:00 2001 From: Filippo Leonelli Date: Mon, 2 Sep 2024 12:38:22 +0200 Subject: [PATCH 111/607] Fixed relative path --- client/app/src/app-routing.module.ts | 2 +- .../{accreditor => }/accreditor-routing.module.ts | 0 .../accreditor/{accreditor => }/accreditor.module.ts | 0 .../{accreditor => }/home/home.component.html | 0 .../accreditor/{accreditor => }/home/home.component.ts | 0 .../organization/organization.component.html | 0 .../organization/organization.component.ts | 0 .../organizations/organizations.component.html | 0 .../organizations/organizations.component.ts | 0 .../{accreditor => }/sidebar/sidebar.component.html | 0 .../{accreditor => }/sidebar/sidebar.component.ts | 0 client/app/src/shared.module.ts | 10 +++++----- .../org-recipient-info.component.html | 0 .../org-recipient-info.component.ts | 0 14 files changed, 6 insertions(+), 6 deletions(-) rename client/app/src/pages/accreditor/{accreditor => }/accreditor-routing.module.ts (100%) rename client/app/src/pages/accreditor/{accreditor => }/accreditor.module.ts (100%) rename client/app/src/pages/accreditor/{accreditor => }/home/home.component.html (100%) rename client/app/src/pages/accreditor/{accreditor => }/home/home.component.ts (100%) rename client/app/src/pages/accreditor/{accreditor => }/organization/organization.component.html (100%) rename client/app/src/pages/accreditor/{accreditor => }/organization/organization.component.ts (100%) rename client/app/src/pages/accreditor/{accreditor => }/organizations/organizations.component.html (100%) rename client/app/src/pages/accreditor/{accreditor => }/organizations/organizations.component.ts (100%) rename client/app/src/pages/accreditor/{accreditor => }/sidebar/sidebar.component.html (100%) rename client/app/src/pages/accreditor/{accreditor => }/sidebar/sidebar.component.ts (100%) rename client/app/src/shared/partials/{org-recipiant-info => org-recipient-info}/org-recipient-info.component.html (100%) rename client/app/src/shared/partials/{org-recipiant-info => org-recipient-info}/org-recipient-info.component.ts (100%) diff --git a/client/app/src/app-routing.module.ts b/client/app/src/app-routing.module.ts index 57f7bb5517..4f9191caf3 100644 --- a/client/app/src/app-routing.module.ts +++ b/client/app/src/app-routing.module.ts @@ -23,7 +23,7 @@ import {WhistleblowerLoginResolver} from "@app/shared/resolvers/whistleblower-lo import {SubmissionComponent} from "@app/pages/whistleblower/submission/submission.component"; import {AuthRoutingModule} from "@app/pages/auth/auth-routing.module"; import { AccreditorGuard } from "./shared/guards/accreditor.guard"; -import { OrganizationComponent } from "./pages/accreditor/organization/organization.component"; +import { OrganizationComponent } from "@app/pages/accreditor/organization/organization.component"; const routes: Routes = [ diff --git a/client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts b/client/app/src/pages/accreditor/accreditor-routing.module.ts similarity index 100% rename from client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts rename to client/app/src/pages/accreditor/accreditor-routing.module.ts diff --git a/client/app/src/pages/accreditor/accreditor/accreditor.module.ts b/client/app/src/pages/accreditor/accreditor.module.ts similarity index 100% rename from client/app/src/pages/accreditor/accreditor/accreditor.module.ts rename to client/app/src/pages/accreditor/accreditor.module.ts diff --git a/client/app/src/pages/accreditor/accreditor/home/home.component.html b/client/app/src/pages/accreditor/home/home.component.html similarity index 100% rename from client/app/src/pages/accreditor/accreditor/home/home.component.html rename to client/app/src/pages/accreditor/home/home.component.html diff --git a/client/app/src/pages/accreditor/accreditor/home/home.component.ts b/client/app/src/pages/accreditor/home/home.component.ts similarity index 100% rename from client/app/src/pages/accreditor/accreditor/home/home.component.ts rename to client/app/src/pages/accreditor/home/home.component.ts diff --git a/client/app/src/pages/accreditor/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/organization/organization.component.html similarity index 100% rename from client/app/src/pages/accreditor/accreditor/organization/organization.component.html rename to client/app/src/pages/accreditor/organization/organization.component.html diff --git a/client/app/src/pages/accreditor/accreditor/organization/organization.component.ts b/client/app/src/pages/accreditor/organization/organization.component.ts similarity index 100% rename from client/app/src/pages/accreditor/accreditor/organization/organization.component.ts rename to client/app/src/pages/accreditor/organization/organization.component.ts diff --git a/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html b/client/app/src/pages/accreditor/organizations/organizations.component.html similarity index 100% rename from client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html rename to client/app/src/pages/accreditor/organizations/organizations.component.html diff --git a/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts b/client/app/src/pages/accreditor/organizations/organizations.component.ts similarity index 100% rename from client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts rename to client/app/src/pages/accreditor/organizations/organizations.component.ts diff --git a/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html b/client/app/src/pages/accreditor/sidebar/sidebar.component.html similarity index 100% rename from client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html rename to client/app/src/pages/accreditor/sidebar/sidebar.component.html diff --git a/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts b/client/app/src/pages/accreditor/sidebar/sidebar.component.ts similarity index 100% rename from client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts rename to client/app/src/pages/accreditor/sidebar/sidebar.component.ts diff --git a/client/app/src/shared.module.ts b/client/app/src/shared.module.ts index 54636a605b..49fd67864d 100644 --- a/client/app/src/shared.module.ts +++ b/client/app/src/shared.module.ts @@ -83,11 +83,11 @@ import {ReopenSubmissionComponent} from "@app/shared/modals/reopen-submission/re import {OtkcAccessComponent} from "@app/shared/modals/otkc-access/otkc-access.component"; import {OperationComponent} from "@app/shared/partials/operation/operation.component"; import {RedactInformationComponent} from "@app/shared/modals/redact-information/redact-information.component"; -import { OrgInfoComponent } from "./shared/partials/org-info/org-info.component"; -import { OrgAdminInfoComponent } from "./shared/partials/org-admin-info/org-admin-info.component"; -import { OrgRecipientInfoComponent } from "./shared/partials/org-recipient-info/org-recipient-info.component"; -import { OrgOverviewComponent } from "./shared/partials/org-overview/org-overview.component"; -import { OrgUsersListComponent } from "./shared/partials/org-users-list/org-users-list.component"; +import { OrgInfoComponent } from "@app/shared/partials/org-info/org-info.component"; +import { OrgAdminInfoComponent } from "@app/shared/partials/org-admin-info/org-admin-info.component"; +import { OrgRecipientInfoComponent } from "@app/shared/partials/org-recipient-info/org-recipient-info.component"; +import { OrgOverviewComponent } from "@app/shared/partials/org-overview/org-overview.component"; +import { OrgUsersListComponent } from "@app/shared/partials/org-users-list/org-users-list.component"; @NgModule({ imports: [ diff --git a/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html b/client/app/src/shared/partials/org-recipient-info/org-recipient-info.component.html similarity index 100% rename from client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html rename to client/app/src/shared/partials/org-recipient-info/org-recipient-info.component.html diff --git a/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts b/client/app/src/shared/partials/org-recipient-info/org-recipient-info.component.ts similarity index 100% rename from client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts rename to client/app/src/shared/partials/org-recipient-info/org-recipient-info.component.ts From 326f73e0d36e8b6b96f66aeb8c4b0ace51cdee53 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Mon, 2 Sep 2024 18:16:30 +0200 Subject: [PATCH 112/607] actions on OE --- .../organization/organization.component.html | 2 ++ .../organization/organization.component.ts | 21 +++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/client/app/src/pages/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/organization/organization.component.html index 01252cc562..66cc04554c 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.html +++ b/client/app/src/pages/accreditor/organization/organization.component.html @@ -7,12 +7,14 @@ diff --git a/client/app/src/pages/accreditor/organization/organization.component.ts b/client/app/src/pages/accreditor/organization/organization.component.ts index ab120d4cde..dcdaa7a3b7 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.ts +++ b/client/app/src/pages/accreditor/organization/organization.component.ts @@ -16,6 +16,8 @@ export class OrganizationComponent implements OnInit{ loading: boolean = false; organization: OrganizationData; + + constructor(private activatedRoute: ActivatedRoute, private httpService: HttpService, private orgService : AccreditorOrgService){ } @@ -40,7 +42,13 @@ export class OrganizationComponent implements OnInit{ this.organization.num_tip = 10 this.organization.num_user_profiled = 2 this.organization.type = "NOT_AFFILIATED" - this.organization.state = "ACCREDITED" + // • 0 -> REQUESTED + // • 1 -> ACCREDITED + // • 2 -> REJECTED + // • 3 -> INSTRUCTOR_REQUEST + // • 4 -> INVITED + // • 5 -> SUSPEND + this.organization.state = 0 let users : EOUser[] = []; users.push({ @@ -81,5 +89,14 @@ export class OrganizationComponent implements OnInit{ convertiInAffiliata(){ console.log("CONVERTI IN AFFILIATA - TODO!!!") } - + + isSuspendable(){ + return this.organization.state == 1 + } + + isDeletable(){ + return this.organization.state == 1 + } + + } From 38d2c50092d48d1299139633a946a59c4d0afbc5 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Mon, 2 Sep 2024 18:16:53 +0200 Subject: [PATCH 113/607] fix on organization data type --- client/app/src/models/accreditor/organization-data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/app/src/models/accreditor/organization-data.ts b/client/app/src/models/accreditor/organization-data.ts index d64e00078a..b492a282b2 100644 --- a/client/app/src/models/accreditor/organization-data.ts +++ b/client/app/src/models/accreditor/organization-data.ts @@ -5,7 +5,7 @@ export class OrganizationData { id: string; denomination: string; type: string; - state: string; + state: number; accreditation_date: string; num_user_profiled: number; num_tip: number; From 0e086591083e9d92002dd3070ce240483640ff7c Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Mon, 2 Sep 2024 18:26:40 +0200 Subject: [PATCH 114/607] check if organization is suspendable --- .../accreditor/organization/organization.component.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/client/app/src/pages/accreditor/organization/organization.component.ts b/client/app/src/pages/accreditor/organization/organization.component.ts index dcdaa7a3b7..7542ce03d4 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.ts +++ b/client/app/src/pages/accreditor/organization/organization.component.ts @@ -48,7 +48,7 @@ export class OrganizationComponent implements OnInit{ // • 3 -> INSTRUCTOR_REQUEST // • 4 -> INVITED // • 5 -> SUSPEND - this.organization.state = 0 + this.organization.state = 5 let users : EOUser[] = []; users.push({ @@ -90,8 +90,14 @@ export class OrganizationComponent implements OnInit{ console.log("CONVERTI IN AFFILIATA - TODO!!!") } + // • 0 -> REQUESTED + // • 1 -> ACCREDITED + // • 2 -> REJECTED + // • 3 -> INSTRUCTOR_REQUEST + // • 4 -> INVITED + // • 5 -> SUSPEND isSuspendable(){ - return this.organization.state == 1 + return [1, 5].includes(this.organization.state) } isDeletable(){ From 9acb78f0d3138379209a5123764fbe1f33f04d97 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Tue, 3 Sep 2024 15:17:59 +0200 Subject: [PATCH 115/607] OE detail for all status --- .../models/accreditor/organization-data.ts | 2 +- .../organization/organization.component.html | 43 +++++++++++++++---- .../organization/organization.component.ts | 38 +++++++++++++--- 3 files changed, 67 insertions(+), 16 deletions(-) diff --git a/client/app/src/models/accreditor/organization-data.ts b/client/app/src/models/accreditor/organization-data.ts index b492a282b2..d64e00078a 100644 --- a/client/app/src/models/accreditor/organization-data.ts +++ b/client/app/src/models/accreditor/organization-data.ts @@ -5,7 +5,7 @@ export class OrganizationData { id: string; denomination: string; type: string; - state: number; + state: string; accreditation_date: string; num_user_profiled: number; num_tip: number; diff --git a/client/app/src/pages/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/organization/organization.component.html index 66cc04554c..bb83c7bf48 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.html +++ b/client/app/src/pages/accreditor/organization/organization.component.html @@ -7,22 +7,24 @@ - - + - - +
-
+
+
+
+ + {{'Enable email notifications'|translate}} +
+
+
@@ -66,4 +75,22 @@
+ +
+
+
+ +
+
+ +
+
+
diff --git a/client/app/src/pages/accreditor/organization/organization.component.ts b/client/app/src/pages/accreditor/organization/organization.component.ts index 7542ce03d4..f3d9821656 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.ts +++ b/client/app/src/pages/accreditor/organization/organization.component.ts @@ -4,7 +4,6 @@ import { OrganizationData } from '@app/models/accreditor/organization-data'; import { EOUser } from '@app/models/app/shared-public-model'; import { AccreditorOrgService } from '@app/services/helper/accreditor-org.service'; import { HttpService } from '@app/shared/services/http.service'; -import { Observable } from 'rxjs'; @Component({ selector: 'src-organization', @@ -15,7 +14,7 @@ export class OrganizationComponent implements OnInit{ org_id: string | null; loading: boolean = false; organization: OrganizationData; - + org_type: boolean = false; constructor(private activatedRoute: ActivatedRoute, private httpService: HttpService, private orgService : AccreditorOrgService){ @@ -48,7 +47,8 @@ export class OrganizationComponent implements OnInit{ // • 3 -> INSTRUCTOR_REQUEST // • 4 -> INVITED // • 5 -> SUSPEND - this.organization.state = 5 + // • 6 -> APPROVED + this.organization.state = "INSTRUCTOR_REQUEST" let users : EOUser[] = []; users.push({ @@ -90,19 +90,43 @@ export class OrganizationComponent implements OnInit{ console.log("CONVERTI IN AFFILIATA - TODO!!!") } + invia(){ + if(this.org_type) + this.organization.type = "AFFILIATED" + + console.log("INVIA / INVIA INVITO - TODO!!!") + } + + rifiuta(){ + console.log("REJECT - TODO!!!") + + } + // • 0 -> REQUESTED // • 1 -> ACCREDITED // • 2 -> REJECTED // • 3 -> INSTRUCTOR_REQUEST // • 4 -> INVITED // • 5 -> SUSPEND - isSuspendable(){ - return [1, 5].includes(this.organization.state) + + isRequested(){ + return this.organization.state === "REQUESTED" + } + + isInstructorRequest(){ + return this.organization.state === "INSTRUCTOR_REQUEST" } - isDeletable(){ - return this.organization.state == 1 + isAccredited(){ + return this.organization.state === "ACCREDITED" } + isSuspended(){ + return this.organization.state === "SUSPENDED" + } + + isInvited(){ + return this.organization.state === "INVITED" + } } From 548aeb3ca17142f9bc1416f4ff0eef60122786e9 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Tue, 3 Sep 2024 15:26:33 +0200 Subject: [PATCH 116/607] fix visibility admin and recipient info --- .../pages/accreditor/organization/organization.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/app/src/pages/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/organization/organization.component.html index bb83c7bf48..9737fbfcdc 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.html +++ b/client/app/src/pages/accreditor/organization/organization.component.html @@ -41,11 +41,11 @@
-
+
-
+
From bddd1f0dcdcccbacebd91ec2efa64aa6933733b1 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Tue, 3 Sep 2024 16:39:17 +0200 Subject: [PATCH 117/607] fix for accreditation_req initialization --- .../app/src/pages/accreditor/accreditor-routing.module.ts | 6 ++++++ .../shared/resolvers/accreditation-req-resolver.service.ts | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/client/app/src/pages/accreditor/accreditor-routing.module.ts b/client/app/src/pages/accreditor/accreditor-routing.module.ts index 756d7b887a..5e19694983 100644 --- a/client/app/src/pages/accreditor/accreditor-routing.module.ts +++ b/client/app/src/pages/accreditor/accreditor-routing.module.ts @@ -9,12 +9,18 @@ const routes: Routes = [ path: "", component: AccreditorHomeComponent, pathMatch: "full", + resolve: { + AccreditationReqResolver + }, data: {sidebar: "accreditor-sidebar", pageTitle: "Home"}, }, { path: "home", component: AccreditorHomeComponent, pathMatch: "full", + resolve: { + AccreditationReqResolver + }, data: {sidebar: "accreditor-sidebar", pageTitle: "Home"}, }, { diff --git a/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts b/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts index 19d4f86a15..5d7e2a500d 100644 --- a/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts +++ b/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts @@ -4,6 +4,7 @@ import { UtilsService } from '../services/utils.service'; import { HttpService } from '../services/http.service'; import { AuthenticationService } from '@app/services/helper/authentication.service'; import { map, Observable, of } from 'rxjs'; +import { AppDataService } from '@app/app-data.service'; @Injectable({ providedIn: 'root' @@ -12,7 +13,9 @@ export class AccreditationReqResolver { dataModel: AccreditationRequestModel[] = []; - constructor(private utilsService: UtilsService, private httpService: HttpService, private authenticationService: AuthenticationService) { + constructor(private utilsService: UtilsService, private httpService: HttpService, + private authenticationService: AuthenticationService, + private appDataService: AppDataService) { } @@ -28,6 +31,7 @@ export class AccreditationReqResolver { //TODO MOCKUP setTimeout(()=> { this.utilsService.reloadComponent(); + this.appDataService.updateShowLoadingPanel(false) }, 1000) } @@ -72,6 +76,7 @@ export class AccreditationReqResolver { } ] + this.appDataService.updateShowLoadingPanel(false) return true; }, 1000) } From 44723f0cefb933a6520e57c84ad5ecacc05c9c9e Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Tue, 3 Sep 2024 16:43:34 +0200 Subject: [PATCH 118/607] label fix --- .../pages/accreditor/organization/organization.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/app/src/pages/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/organization/organization.component.html index 9737fbfcdc..fc41867973 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.html +++ b/client/app/src/pages/accreditor/organization/organization.component.html @@ -59,7 +59,7 @@
- {{'Enable email notifications'|translate}} + {{'Affiliata'|translate}}
From 620f9ee4d4d299191c13688015f818e5775d017b Mon Sep 17 00:00:00 2001 From: Alessio Franceschini Date: Wed, 21 Aug 2024 15:18:42 +0200 Subject: [PATCH 119/607] misc: first commit in this branch --- .../accreditor/accreditor-routing.module.ts | 37 ++++ .../accreditor/accreditor.module.ts | 23 +++ .../accreditor/home/home.component.html | 3 + .../accreditor/home/home.component.ts | 25 +++ .../organization/organization.component.html | 67 +++++++ .../organization/organization.component.ts | 85 +++++++++ .../organizations.component.html | 172 ++++++++++++++++++ .../organizations/organizations.component.ts | 76 ++++++++ .../accreditor/sidebar/sidebar.component.html | 8 + .../accreditor/sidebar/sidebar.component.ts | 24 +++ .../org-recipient-info.component.html | 27 +++ .../org-recipient-info.component.ts | 17 ++ .../accreditation-req-resolver.service.ts | 7 +- 13 files changed, 565 insertions(+), 6 deletions(-) create mode 100644 client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts create mode 100644 client/app/src/pages/accreditor/accreditor/accreditor.module.ts create mode 100644 client/app/src/pages/accreditor/accreditor/home/home.component.html create mode 100644 client/app/src/pages/accreditor/accreditor/home/home.component.ts create mode 100644 client/app/src/pages/accreditor/accreditor/organization/organization.component.html create mode 100644 client/app/src/pages/accreditor/accreditor/organization/organization.component.ts create mode 100644 client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html create mode 100644 client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts create mode 100644 client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html create mode 100644 client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts create mode 100644 client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html create mode 100644 client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts diff --git a/client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts b/client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts new file mode 100644 index 0000000000..756d7b887a --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts @@ -0,0 +1,37 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { AccreditorHomeComponent } from './home/home.component'; +import { OrganizationsComponent } from './organizations/organizations.component'; +import { AccreditationReqResolver } from '@app/shared/resolvers/accreditation-req-resolver.service'; + +const routes: Routes = [ + { + path: "", + component: AccreditorHomeComponent, + pathMatch: "full", + data: {sidebar: "accreditor-sidebar", pageTitle: "Home"}, + }, + { + path: "home", + component: AccreditorHomeComponent, + pathMatch: "full", + data: {sidebar: "accreditor-sidebar", pageTitle: "Home"}, + }, + { + path: "organizations", + component: OrganizationsComponent, + resolve: { + AccreditationReqResolver + }, + pathMatch: "full", + data: {sidebar: "accreditor-sidebar", pageTitle: "Organizations"}, + } + + +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class AccreditorRoutingModule { } diff --git a/client/app/src/pages/accreditor/accreditor/accreditor.module.ts b/client/app/src/pages/accreditor/accreditor/accreditor.module.ts new file mode 100644 index 0000000000..558bd9d1c2 --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/accreditor.module.ts @@ -0,0 +1,23 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; + +import {RouterModule} from "@angular/router"; +import {SharedModule} from "@app/shared.module"; +import {FormsModule} from "@angular/forms"; +import {NgbModule, NgbNavModule} from "@ng-bootstrap/ng-bootstrap"; +import { AccreditorHomeComponent } from './home/home.component'; +import { SidebarComponent } from './sidebar/sidebar.component'; +import { OrganizationsComponent } from './organizations/organizations.component'; +import { OrganizationComponent } from './organization/organization.component'; + + +@NgModule({ + declarations: [ + AccreditorHomeComponent, SidebarComponent, OrganizationsComponent, OrganizationComponent + ], + imports: [ + CommonModule, SharedModule, NgbNavModule, NgbModule, RouterModule, FormsModule + ], + exports: [SidebarComponent] +}) +export class AccreditorModule { } diff --git a/client/app/src/pages/accreditor/accreditor/home/home.component.html b/client/app/src/pages/accreditor/accreditor/home/home.component.html new file mode 100644 index 0000000000..aa5a211875 --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/home/home.component.html @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/client/app/src/pages/accreditor/accreditor/home/home.component.ts b/client/app/src/pages/accreditor/accreditor/home/home.component.ts new file mode 100644 index 0000000000..43cb036271 --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/home/home.component.ts @@ -0,0 +1,25 @@ +import {Component} from "@angular/core"; +import {AppDataService} from "@app/app-data.service"; +import {preferenceResolverModel} from "@app/models/resolvers/preference-resolver-model"; +import {PreferenceResolver} from "@app/shared/resolvers/preference.resolver"; +import {UtilsService} from "@app/shared/services/utils.service"; + + +@Component({ + selector: "src-accreditor-home", + templateUrl: "./home.component.html" +}) +export class AccreditorHomeComponent { + preferenceData: preferenceResolverModel; + constructor(private appDataService: AppDataService,private utilsService: UtilsService, private preference: PreferenceResolver) { + } + + ngOnInit(): void { + if (this.preference.dataModel) { + this.preferenceData = this.preference.dataModel; + } + if (this.appDataService.public.node.user_privacy_policy_text && this.preferenceData.accepted_privacy_policy === "1970-01-01T00:00:00Z") { + this.utilsService.acceptPrivacyPolicyDialog().subscribe(); + } + } +} diff --git a/client/app/src/pages/accreditor/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/accreditor/organization/organization.component.html new file mode 100644 index 0000000000..01252cc562 --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/organization/organization.component.html @@ -0,0 +1,67 @@ +
+
+
+ + + + + + + + + + + + + + + + + + + + +
+
+ + +
+
+ +
+ +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
diff --git a/client/app/src/pages/accreditor/accreditor/organization/organization.component.ts b/client/app/src/pages/accreditor/accreditor/organization/organization.component.ts new file mode 100644 index 0000000000..ab120d4cde --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/organization/organization.component.ts @@ -0,0 +1,85 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { OrganizationData } from '@app/models/accreditor/organization-data'; +import { EOUser } from '@app/models/app/shared-public-model'; +import { AccreditorOrgService } from '@app/services/helper/accreditor-org.service'; +import { HttpService } from '@app/shared/services/http.service'; +import { Observable } from 'rxjs'; + +@Component({ + selector: 'src-organization', + templateUrl: './organization.component.html' +}) +export class OrganizationComponent implements OnInit{ + + org_id: string | null; + loading: boolean = false; + organization: OrganizationData; + + constructor(private activatedRoute: ActivatedRoute, private httpService: HttpService, private orgService : AccreditorOrgService){ + + } + + + ngOnInit() { + this.loadOrganizationData(); + } + + loadOrganizationData(){ + this.org_id = this.activatedRoute.snapshot.paramMap.get("org_id"); + + // const requestObservable: Observable = this.httpService.receiverTip(this.org_id); + this.loading = true; + this.orgService.reset(); + + // setTimeout(()=>{ + this.organization = new OrganizationData(); + this.organization.id = "1" + this.organization.denomination = "denominazione Org 1" + this.organization.accreditation_date = "01-02-2024" + this.organization.num_tip = 10 + this.organization.num_user_profiled = 2 + this.organization.type = "NOT_AFFILIATED" + this.organization.state = "ACCREDITED" + + let users : EOUser[] = []; + users.push({ + id: "1", + name: "Utente 1", + surname: "Surname 1", + creation_date: "01-01-2024", + last_access: "01/02/2024 10:00:05", + role: "Admin", + tips: 10, + closed_tips: 1 + }) + + this.organization.users = users; + + + // }, 1000) + + // requestObservable.subscribe( + // { + // next: (response: OrganizationData) => { + // this.loading = false; + // this.organization = this.orgService.organization; + + // // this.activatedRoute.queryParams.subscribe((params: { [x: string]: string; }) => { + // // this.tip.tip_id = params["tip_id"]; + // // }) + + // //TODO: CHIAMATA PER RECUPERARE LISTA UTENTI DELL'ORGANIZZAZIONE + // // this.httpService.getOrgUsers(this.org_id) + + // } + // } + // ); + } + + + convertiInAffiliata(){ + console.log("CONVERTI IN AFFILIATA - TODO!!!") + } + +} diff --git a/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html b/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html new file mode 100644 index 0000000000..ade2b2c85a --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html @@ -0,0 +1,172 @@ + +
+
+ + + + + + + + + + + +
+ +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + {{'Denomination' | translate}} + + + + + + + + + {{'Type' | translate}} + + + + + + + + + {{'Accreditation Date' | translate}} + + + + + + + + + {{'Users' | translate}} + + + + + + + + + {{'Tips' | translate}} + + + + + + + + + {{'Closed' | translate}} + + + + + + + + + {{'State' | translate}} + + + + + +
+ {{req.id}} + + {{req.denomination}} + + {{req.type}} + + {{req.accreditation_date}} + + {{req.num_user_profiled}} + + {{req.num_tip}} + + {{req.num_tip}} + + {{req.state}} +
+
+ + < {{ 'Previous' | translate }} + + + {{ 'Next' | translate }} > + + << {{ 'First' | translate }} + + + {{ 'Last' | translate }} >> + + +
+ +
+
+
\ No newline at end of file diff --git a/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts b/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts new file mode 100644 index 0000000000..322819efd8 --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts @@ -0,0 +1,76 @@ +import { HttpClient } from '@angular/common/http'; +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { AppDataService } from '@app/app-data.service'; +import { AccreditationRequestModel } from '@app/models/accreditor/organization-data'; +import { AuthenticationService } from '@app/services/helper/authentication.service'; +import { AppConfigService } from '@app/services/root/app-config.service'; +import { AccreditationReqResolver } from '@app/shared/resolvers/accreditation-req-resolver.service'; +import { HttpService } from '@app/shared/services/http.service'; +import { UtilsService } from '@app/shared/services/utils.service'; +import { TranslateService } from '@ngx-translate/core'; +import { filter, orderBy } from 'lodash'; + +@Component({ + selector: 'src-organizations', + templateUrl: './organizations.component.html' +}) +export class OrganizationsComponent implements OnInit{ + + constructor(private http: HttpClient,protected authenticationService: AuthenticationService, protected httpService: HttpService, + private appConfigServices: AppConfigService, private router: Router, protected AReqs: AccreditationReqResolver, + protected utils: UtilsService, protected appDataService: AppDataService, private translateService: TranslateService) { + + } + + search: string | undefined; + selectedReqs: string[] = []; + filteredReqs: AccreditationRequestModel[]; + currentPage: number = 1; + itemsPerPage: number = 20; + sortKey: string = "accreditation_date"; + sortReverse: boolean = true; + + + + ngOnInit(): void { + if (!this.AReqs.dataModel) { + this.router.navigate(["/accreditor/home"]).then(); + } else { + this.filteredReqs = this.AReqs.dataModel; + } + } + + + reload() { + const reloadCallback = () => { + this.AReqs.reload(); + }; + + this.appConfigServices.localInitialization(true, reloadCallback); + } + + + onSearchChange(value: string | number | undefined) { + if (typeof value !== "undefined") { + this.currentPage = 1; + this.filteredReqs = this.AReqs.dataModel; + // this.processTips(); + + this.filteredReqs = orderBy(filter(this.filteredReqs, (req) => + Object.values(req).some((val) => { + if (typeof val === "string" || typeof val === "number") { + return String(val).toLowerCase().includes(String(value).toLowerCase()); + } + return false; + }) + ), "accreditation_date"); + } + } + + orderbyCast(data: AccreditationRequestModel[]): AccreditationRequestModel[] { + return data; + } + + +} diff --git a/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html b/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html new file mode 100644 index 0000000000..abfd855a2d --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html @@ -0,0 +1,8 @@ + + + {{'Home' | translate}} + + + + {{'Organizations' | translate}} + \ No newline at end of file diff --git a/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts b/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts new file mode 100644 index 0000000000..5a9aec12c6 --- /dev/null +++ b/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts @@ -0,0 +1,24 @@ +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { Router } from '@angular/router'; +import { AuthenticationService } from '@app/services/helper/authentication.service'; +import { NodeResolver } from '@app/shared/resolvers/node.resolver'; + +@Component({ + selector: "src-accreditor-sidebar", + templateUrl: "./sidebar.component.html", + changeDetection: ChangeDetectionStrategy.OnPush +}) +export class SidebarComponent { + + constructor(private router: Router, protected nodeResolver: NodeResolver, protected authenticationService: AuthenticationService) { + } + + isActive(route: string): boolean { + return this.router.isActive(route, { + paths: "subset", + queryParams: "subset", + fragment: "ignored", + matrixParams: "ignored" + }); + } +} \ No newline at end of file diff --git a/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html b/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html new file mode 100644 index 0000000000..929dd7c1e5 --- /dev/null +++ b/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html @@ -0,0 +1,27 @@ +
+
{{ 'Primary recipient info' | translate }}
+
+
+ + +
+
+ + +
+
+ + +
{{'Invalid email format' | translate}}
+
+
+
\ No newline at end of file diff --git a/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts b/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts new file mode 100644 index 0000000000..640c300cae --- /dev/null +++ b/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts @@ -0,0 +1,17 @@ +import { Component } from '@angular/core'; +import { Constants } from '@app/shared/constants/constants'; + +@Component({ + selector: 'src-org-recipient-info', + templateUrl: './org-recipient-info.component.html' +}) +export class OrgRecipientInfoComponent { + + recipientInfo = { + name: '', + fiscalCode: '', + email: '' + }; + + protected readonly Constants = Constants; +} diff --git a/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts b/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts index 5d7e2a500d..19d4f86a15 100644 --- a/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts +++ b/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts @@ -4,7 +4,6 @@ import { UtilsService } from '../services/utils.service'; import { HttpService } from '../services/http.service'; import { AuthenticationService } from '@app/services/helper/authentication.service'; import { map, Observable, of } from 'rxjs'; -import { AppDataService } from '@app/app-data.service'; @Injectable({ providedIn: 'root' @@ -13,9 +12,7 @@ export class AccreditationReqResolver { dataModel: AccreditationRequestModel[] = []; - constructor(private utilsService: UtilsService, private httpService: HttpService, - private authenticationService: AuthenticationService, - private appDataService: AppDataService) { + constructor(private utilsService: UtilsService, private httpService: HttpService, private authenticationService: AuthenticationService) { } @@ -31,7 +28,6 @@ export class AccreditationReqResolver { //TODO MOCKUP setTimeout(()=> { this.utilsService.reloadComponent(); - this.appDataService.updateShowLoadingPanel(false) }, 1000) } @@ -76,7 +72,6 @@ export class AccreditationReqResolver { } ] - this.appDataService.updateShowLoadingPanel(false) return true; }, 1000) } From 3e772b7e54eff7bddf115a6b4c1403405d876cde Mon Sep 17 00:00:00 2001 From: Filippo Leonelli Date: Mon, 2 Sep 2024 12:38:22 +0200 Subject: [PATCH 120/607] Fixed relative path --- .../accreditor/accreditor-routing.module.ts | 6 - .../accreditor/accreditor-routing.module.ts | 37 ---- .../accreditor/accreditor.module.ts | 23 --- .../accreditor/home/home.component.html | 3 - .../accreditor/home/home.component.ts | 25 --- .../organization/organization.component.html | 67 ------- .../organization/organization.component.ts | 85 --------- .../organizations.component.html | 172 ------------------ .../organizations/organizations.component.ts | 76 -------- .../accreditor/sidebar/sidebar.component.html | 8 - .../accreditor/sidebar/sidebar.component.ts | 24 --- .../organization/organization.component.ts | 49 +---- .../org-recipient-info.component.html | 27 --- .../org-recipient-info.component.ts | 17 -- 14 files changed, 2 insertions(+), 617 deletions(-) delete mode 100644 client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts delete mode 100644 client/app/src/pages/accreditor/accreditor/accreditor.module.ts delete mode 100644 client/app/src/pages/accreditor/accreditor/home/home.component.html delete mode 100644 client/app/src/pages/accreditor/accreditor/home/home.component.ts delete mode 100644 client/app/src/pages/accreditor/accreditor/organization/organization.component.html delete mode 100644 client/app/src/pages/accreditor/accreditor/organization/organization.component.ts delete mode 100644 client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html delete mode 100644 client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts delete mode 100644 client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html delete mode 100644 client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts delete mode 100644 client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html delete mode 100644 client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts diff --git a/client/app/src/pages/accreditor/accreditor-routing.module.ts b/client/app/src/pages/accreditor/accreditor-routing.module.ts index 5e19694983..756d7b887a 100644 --- a/client/app/src/pages/accreditor/accreditor-routing.module.ts +++ b/client/app/src/pages/accreditor/accreditor-routing.module.ts @@ -9,18 +9,12 @@ const routes: Routes = [ path: "", component: AccreditorHomeComponent, pathMatch: "full", - resolve: { - AccreditationReqResolver - }, data: {sidebar: "accreditor-sidebar", pageTitle: "Home"}, }, { path: "home", component: AccreditorHomeComponent, pathMatch: "full", - resolve: { - AccreditationReqResolver - }, data: {sidebar: "accreditor-sidebar", pageTitle: "Home"}, }, { diff --git a/client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts b/client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts deleted file mode 100644 index 756d7b887a..0000000000 --- a/client/app/src/pages/accreditor/accreditor/accreditor-routing.module.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; -import { AccreditorHomeComponent } from './home/home.component'; -import { OrganizationsComponent } from './organizations/organizations.component'; -import { AccreditationReqResolver } from '@app/shared/resolvers/accreditation-req-resolver.service'; - -const routes: Routes = [ - { - path: "", - component: AccreditorHomeComponent, - pathMatch: "full", - data: {sidebar: "accreditor-sidebar", pageTitle: "Home"}, - }, - { - path: "home", - component: AccreditorHomeComponent, - pathMatch: "full", - data: {sidebar: "accreditor-sidebar", pageTitle: "Home"}, - }, - { - path: "organizations", - component: OrganizationsComponent, - resolve: { - AccreditationReqResolver - }, - pathMatch: "full", - data: {sidebar: "accreditor-sidebar", pageTitle: "Organizations"}, - } - - -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class AccreditorRoutingModule { } diff --git a/client/app/src/pages/accreditor/accreditor/accreditor.module.ts b/client/app/src/pages/accreditor/accreditor/accreditor.module.ts deleted file mode 100644 index 558bd9d1c2..0000000000 --- a/client/app/src/pages/accreditor/accreditor/accreditor.module.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; - -import {RouterModule} from "@angular/router"; -import {SharedModule} from "@app/shared.module"; -import {FormsModule} from "@angular/forms"; -import {NgbModule, NgbNavModule} from "@ng-bootstrap/ng-bootstrap"; -import { AccreditorHomeComponent } from './home/home.component'; -import { SidebarComponent } from './sidebar/sidebar.component'; -import { OrganizationsComponent } from './organizations/organizations.component'; -import { OrganizationComponent } from './organization/organization.component'; - - -@NgModule({ - declarations: [ - AccreditorHomeComponent, SidebarComponent, OrganizationsComponent, OrganizationComponent - ], - imports: [ - CommonModule, SharedModule, NgbNavModule, NgbModule, RouterModule, FormsModule - ], - exports: [SidebarComponent] -}) -export class AccreditorModule { } diff --git a/client/app/src/pages/accreditor/accreditor/home/home.component.html b/client/app/src/pages/accreditor/accreditor/home/home.component.html deleted file mode 100644 index aa5a211875..0000000000 --- a/client/app/src/pages/accreditor/accreditor/home/home.component.html +++ /dev/null @@ -1,3 +0,0 @@ -
- -
\ No newline at end of file diff --git a/client/app/src/pages/accreditor/accreditor/home/home.component.ts b/client/app/src/pages/accreditor/accreditor/home/home.component.ts deleted file mode 100644 index 43cb036271..0000000000 --- a/client/app/src/pages/accreditor/accreditor/home/home.component.ts +++ /dev/null @@ -1,25 +0,0 @@ -import {Component} from "@angular/core"; -import {AppDataService} from "@app/app-data.service"; -import {preferenceResolverModel} from "@app/models/resolvers/preference-resolver-model"; -import {PreferenceResolver} from "@app/shared/resolvers/preference.resolver"; -import {UtilsService} from "@app/shared/services/utils.service"; - - -@Component({ - selector: "src-accreditor-home", - templateUrl: "./home.component.html" -}) -export class AccreditorHomeComponent { - preferenceData: preferenceResolverModel; - constructor(private appDataService: AppDataService,private utilsService: UtilsService, private preference: PreferenceResolver) { - } - - ngOnInit(): void { - if (this.preference.dataModel) { - this.preferenceData = this.preference.dataModel; - } - if (this.appDataService.public.node.user_privacy_policy_text && this.preferenceData.accepted_privacy_policy === "1970-01-01T00:00:00Z") { - this.utilsService.acceptPrivacyPolicyDialog().subscribe(); - } - } -} diff --git a/client/app/src/pages/accreditor/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/accreditor/organization/organization.component.html deleted file mode 100644 index 01252cc562..0000000000 --- a/client/app/src/pages/accreditor/accreditor/organization/organization.component.html +++ /dev/null @@ -1,67 +0,0 @@ -
-
-
- - - - - - - - - - - - - - - - - - - - -
-
- - -
-
- -
- -
- -
- -
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
diff --git a/client/app/src/pages/accreditor/accreditor/organization/organization.component.ts b/client/app/src/pages/accreditor/accreditor/organization/organization.component.ts deleted file mode 100644 index ab120d4cde..0000000000 --- a/client/app/src/pages/accreditor/accreditor/organization/organization.component.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { OrganizationData } from '@app/models/accreditor/organization-data'; -import { EOUser } from '@app/models/app/shared-public-model'; -import { AccreditorOrgService } from '@app/services/helper/accreditor-org.service'; -import { HttpService } from '@app/shared/services/http.service'; -import { Observable } from 'rxjs'; - -@Component({ - selector: 'src-organization', - templateUrl: './organization.component.html' -}) -export class OrganizationComponent implements OnInit{ - - org_id: string | null; - loading: boolean = false; - organization: OrganizationData; - - constructor(private activatedRoute: ActivatedRoute, private httpService: HttpService, private orgService : AccreditorOrgService){ - - } - - - ngOnInit() { - this.loadOrganizationData(); - } - - loadOrganizationData(){ - this.org_id = this.activatedRoute.snapshot.paramMap.get("org_id"); - - // const requestObservable: Observable = this.httpService.receiverTip(this.org_id); - this.loading = true; - this.orgService.reset(); - - // setTimeout(()=>{ - this.organization = new OrganizationData(); - this.organization.id = "1" - this.organization.denomination = "denominazione Org 1" - this.organization.accreditation_date = "01-02-2024" - this.organization.num_tip = 10 - this.organization.num_user_profiled = 2 - this.organization.type = "NOT_AFFILIATED" - this.organization.state = "ACCREDITED" - - let users : EOUser[] = []; - users.push({ - id: "1", - name: "Utente 1", - surname: "Surname 1", - creation_date: "01-01-2024", - last_access: "01/02/2024 10:00:05", - role: "Admin", - tips: 10, - closed_tips: 1 - }) - - this.organization.users = users; - - - // }, 1000) - - // requestObservable.subscribe( - // { - // next: (response: OrganizationData) => { - // this.loading = false; - // this.organization = this.orgService.organization; - - // // this.activatedRoute.queryParams.subscribe((params: { [x: string]: string; }) => { - // // this.tip.tip_id = params["tip_id"]; - // // }) - - // //TODO: CHIAMATA PER RECUPERARE LISTA UTENTI DELL'ORGANIZZAZIONE - // // this.httpService.getOrgUsers(this.org_id) - - // } - // } - // ); - } - - - convertiInAffiliata(){ - console.log("CONVERTI IN AFFILIATA - TODO!!!") - } - -} diff --git a/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html b/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html deleted file mode 100644 index ade2b2c85a..0000000000 --- a/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.html +++ /dev/null @@ -1,172 +0,0 @@ - -
-
- - - - - - - - - - - -
- -
-
-
-
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - {{'Denomination' | translate}} - - - - - - - - - {{'Type' | translate}} - - - - - - - - - {{'Accreditation Date' | translate}} - - - - - - - - - {{'Users' | translate}} - - - - - - - - - {{'Tips' | translate}} - - - - - - - - - {{'Closed' | translate}} - - - - - - - - - {{'State' | translate}} - - - - - -
- {{req.id}} - - {{req.denomination}} - - {{req.type}} - - {{req.accreditation_date}} - - {{req.num_user_profiled}} - - {{req.num_tip}} - - {{req.num_tip}} - - {{req.state}} -
-
- - < {{ 'Previous' | translate }} - - - {{ 'Next' | translate }} > - - << {{ 'First' | translate }} - - - {{ 'Last' | translate }} >> - - -
- -
-
-
\ No newline at end of file diff --git a/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts b/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts deleted file mode 100644 index 322819efd8..0000000000 --- a/client/app/src/pages/accreditor/accreditor/organizations/organizations.component.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { HttpClient } from '@angular/common/http'; -import { Component, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; -import { AppDataService } from '@app/app-data.service'; -import { AccreditationRequestModel } from '@app/models/accreditor/organization-data'; -import { AuthenticationService } from '@app/services/helper/authentication.service'; -import { AppConfigService } from '@app/services/root/app-config.service'; -import { AccreditationReqResolver } from '@app/shared/resolvers/accreditation-req-resolver.service'; -import { HttpService } from '@app/shared/services/http.service'; -import { UtilsService } from '@app/shared/services/utils.service'; -import { TranslateService } from '@ngx-translate/core'; -import { filter, orderBy } from 'lodash'; - -@Component({ - selector: 'src-organizations', - templateUrl: './organizations.component.html' -}) -export class OrganizationsComponent implements OnInit{ - - constructor(private http: HttpClient,protected authenticationService: AuthenticationService, protected httpService: HttpService, - private appConfigServices: AppConfigService, private router: Router, protected AReqs: AccreditationReqResolver, - protected utils: UtilsService, protected appDataService: AppDataService, private translateService: TranslateService) { - - } - - search: string | undefined; - selectedReqs: string[] = []; - filteredReqs: AccreditationRequestModel[]; - currentPage: number = 1; - itemsPerPage: number = 20; - sortKey: string = "accreditation_date"; - sortReverse: boolean = true; - - - - ngOnInit(): void { - if (!this.AReqs.dataModel) { - this.router.navigate(["/accreditor/home"]).then(); - } else { - this.filteredReqs = this.AReqs.dataModel; - } - } - - - reload() { - const reloadCallback = () => { - this.AReqs.reload(); - }; - - this.appConfigServices.localInitialization(true, reloadCallback); - } - - - onSearchChange(value: string | number | undefined) { - if (typeof value !== "undefined") { - this.currentPage = 1; - this.filteredReqs = this.AReqs.dataModel; - // this.processTips(); - - this.filteredReqs = orderBy(filter(this.filteredReqs, (req) => - Object.values(req).some((val) => { - if (typeof val === "string" || typeof val === "number") { - return String(val).toLowerCase().includes(String(value).toLowerCase()); - } - return false; - }) - ), "accreditation_date"); - } - } - - orderbyCast(data: AccreditationRequestModel[]): AccreditationRequestModel[] { - return data; - } - - -} diff --git a/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html b/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html deleted file mode 100644 index abfd855a2d..0000000000 --- a/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.html +++ /dev/null @@ -1,8 +0,0 @@ - - - {{'Home' | translate}} - - - - {{'Organizations' | translate}} - \ No newline at end of file diff --git a/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts b/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts deleted file mode 100644 index 5a9aec12c6..0000000000 --- a/client/app/src/pages/accreditor/accreditor/sidebar/sidebar.component.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { Router } from '@angular/router'; -import { AuthenticationService } from '@app/services/helper/authentication.service'; -import { NodeResolver } from '@app/shared/resolvers/node.resolver'; - -@Component({ - selector: "src-accreditor-sidebar", - templateUrl: "./sidebar.component.html", - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class SidebarComponent { - - constructor(private router: Router, protected nodeResolver: NodeResolver, protected authenticationService: AuthenticationService) { - } - - isActive(route: string): boolean { - return this.router.isActive(route, { - paths: "subset", - queryParams: "subset", - fragment: "ignored", - matrixParams: "ignored" - }); - } -} \ No newline at end of file diff --git a/client/app/src/pages/accreditor/organization/organization.component.ts b/client/app/src/pages/accreditor/organization/organization.component.ts index f3d9821656..2569ec2178 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.ts +++ b/client/app/src/pages/accreditor/organization/organization.component.ts @@ -4,6 +4,7 @@ import { OrganizationData } from '@app/models/accreditor/organization-data'; import { EOUser } from '@app/models/app/shared-public-model'; import { AccreditorOrgService } from '@app/services/helper/accreditor-org.service'; import { HttpService } from '@app/shared/services/http.service'; +import { Observable } from 'rxjs'; @Component({ selector: 'src-organization', @@ -41,14 +42,7 @@ export class OrganizationComponent implements OnInit{ this.organization.num_tip = 10 this.organization.num_user_profiled = 2 this.organization.type = "NOT_AFFILIATED" - // • 0 -> REQUESTED - // • 1 -> ACCREDITED - // • 2 -> REJECTED - // • 3 -> INSTRUCTOR_REQUEST - // • 4 -> INVITED - // • 5 -> SUSPEND - // • 6 -> APPROVED - this.organization.state = "INSTRUCTOR_REQUEST" + this.organization.state = "ACCREDITED" let users : EOUser[] = []; users.push({ @@ -90,43 +84,4 @@ export class OrganizationComponent implements OnInit{ console.log("CONVERTI IN AFFILIATA - TODO!!!") } - invia(){ - if(this.org_type) - this.organization.type = "AFFILIATED" - - console.log("INVIA / INVIA INVITO - TODO!!!") - } - - rifiuta(){ - console.log("REJECT - TODO!!!") - - } - - // • 0 -> REQUESTED - // • 1 -> ACCREDITED - // • 2 -> REJECTED - // • 3 -> INSTRUCTOR_REQUEST - // • 4 -> INVITED - // • 5 -> SUSPEND - - isRequested(){ - return this.organization.state === "REQUESTED" - } - - isInstructorRequest(){ - return this.organization.state === "INSTRUCTOR_REQUEST" - } - - isAccredited(){ - return this.organization.state === "ACCREDITED" - } - - isSuspended(){ - return this.organization.state === "SUSPENDED" - } - - isInvited(){ - return this.organization.state === "INVITED" - } - } diff --git a/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html b/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html deleted file mode 100644 index 929dd7c1e5..0000000000 --- a/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.html +++ /dev/null @@ -1,27 +0,0 @@ -
-
{{ 'Primary recipient info' | translate }}
-
-
- - -
-
- - -
-
- - -
{{'Invalid email format' | translate}}
-
-
-
\ No newline at end of file diff --git a/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts b/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts deleted file mode 100644 index 640c300cae..0000000000 --- a/client/app/src/shared/partials/org-recipiant-info/org-recipient-info.component.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Component } from '@angular/core'; -import { Constants } from '@app/shared/constants/constants'; - -@Component({ - selector: 'src-org-recipient-info', - templateUrl: './org-recipient-info.component.html' -}) -export class OrgRecipientInfoComponent { - - recipientInfo = { - name: '', - fiscalCode: '', - email: '' - }; - - protected readonly Constants = Constants; -} From d343c478d4d7bda531627fade1c938b1c166775b Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Mon, 2 Sep 2024 18:16:30 +0200 Subject: [PATCH 121/607] actions on OE --- .../organization/organization.component.html | 47 ++++--------------- .../organization/organization.component.ts | 19 +++++++- 2 files changed, 27 insertions(+), 39 deletions(-) diff --git a/client/app/src/pages/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/organization/organization.component.html index fc41867973..66cc04554c 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.html +++ b/client/app/src/pages/accreditor/organization/organization.component.html @@ -7,26 +7,24 @@ - - - - + + + + @@ -41,28 +39,21 @@
-
+
-
+
-
+
-
-
- - {{'Affiliata'|translate}} -
-
-
@@ -75,22 +66,4 @@
- -
-
-
- -
-
- -
-
-
diff --git a/client/app/src/pages/accreditor/organization/organization.component.ts b/client/app/src/pages/accreditor/organization/organization.component.ts index 2569ec2178..6733ccf6c2 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.ts +++ b/client/app/src/pages/accreditor/organization/organization.component.ts @@ -15,7 +15,7 @@ export class OrganizationComponent implements OnInit{ org_id: string | null; loading: boolean = false; organization: OrganizationData; - org_type: boolean = false; + constructor(private activatedRoute: ActivatedRoute, private httpService: HttpService, private orgService : AccreditorOrgService){ @@ -42,7 +42,13 @@ export class OrganizationComponent implements OnInit{ this.organization.num_tip = 10 this.organization.num_user_profiled = 2 this.organization.type = "NOT_AFFILIATED" - this.organization.state = "ACCREDITED" + // • 0 -> REQUESTED + // • 1 -> ACCREDITED + // • 2 -> REJECTED + // • 3 -> INSTRUCTOR_REQUEST + // • 4 -> INVITED + // • 5 -> SUSPEND + this.organization.state = 0 let users : EOUser[] = []; users.push({ @@ -84,4 +90,13 @@ export class OrganizationComponent implements OnInit{ console.log("CONVERTI IN AFFILIATA - TODO!!!") } + isSuspendable(){ + return this.organization.state == 1 + } + + isDeletable(){ + return this.organization.state == 1 + } + + } From 0372b3f46aac3bc46a0e21cf471297fcfff81297 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Mon, 2 Sep 2024 18:16:53 +0200 Subject: [PATCH 122/607] fix on organization data type --- client/app/src/models/accreditor/organization-data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/app/src/models/accreditor/organization-data.ts b/client/app/src/models/accreditor/organization-data.ts index d64e00078a..b492a282b2 100644 --- a/client/app/src/models/accreditor/organization-data.ts +++ b/client/app/src/models/accreditor/organization-data.ts @@ -5,7 +5,7 @@ export class OrganizationData { id: string; denomination: string; type: string; - state: string; + state: number; accreditation_date: string; num_user_profiled: number; num_tip: number; From 00aafed71ab25e31ca1d62cb671b1f136a53b612 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Mon, 2 Sep 2024 18:26:40 +0200 Subject: [PATCH 123/607] check if organization is suspendable --- .../accreditor/organization/organization.component.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/client/app/src/pages/accreditor/organization/organization.component.ts b/client/app/src/pages/accreditor/organization/organization.component.ts index 6733ccf6c2..87913548bd 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.ts +++ b/client/app/src/pages/accreditor/organization/organization.component.ts @@ -48,7 +48,7 @@ export class OrganizationComponent implements OnInit{ // • 3 -> INSTRUCTOR_REQUEST // • 4 -> INVITED // • 5 -> SUSPEND - this.organization.state = 0 + this.organization.state = 5 let users : EOUser[] = []; users.push({ @@ -90,8 +90,14 @@ export class OrganizationComponent implements OnInit{ console.log("CONVERTI IN AFFILIATA - TODO!!!") } + // • 0 -> REQUESTED + // • 1 -> ACCREDITED + // • 2 -> REJECTED + // • 3 -> INSTRUCTOR_REQUEST + // • 4 -> INVITED + // • 5 -> SUSPEND isSuspendable(){ - return this.organization.state == 1 + return [1, 5].includes(this.organization.state) } isDeletable(){ From f3663283a9f982ff05d5db17749b87c75438084d Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Tue, 3 Sep 2024 15:17:59 +0200 Subject: [PATCH 124/607] OE detail for all status --- .../models/accreditor/organization-data.ts | 2 +- .../organization/organization.component.html | 43 +++++++++++++++---- .../organization/organization.component.ts | 40 +++++++++++++---- 3 files changed, 68 insertions(+), 17 deletions(-) diff --git a/client/app/src/models/accreditor/organization-data.ts b/client/app/src/models/accreditor/organization-data.ts index b492a282b2..d64e00078a 100644 --- a/client/app/src/models/accreditor/organization-data.ts +++ b/client/app/src/models/accreditor/organization-data.ts @@ -5,7 +5,7 @@ export class OrganizationData { id: string; denomination: string; type: string; - state: number; + state: string; accreditation_date: string; num_user_profiled: number; num_tip: number; diff --git a/client/app/src/pages/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/organization/organization.component.html index 66cc04554c..bb83c7bf48 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.html +++ b/client/app/src/pages/accreditor/organization/organization.component.html @@ -7,22 +7,24 @@ - - + - - +
-
+
+
+
+ + {{'Enable email notifications'|translate}} +
+
+
@@ -66,4 +75,22 @@
+ +
+
+
+ +
+
+ +
+
+
diff --git a/client/app/src/pages/accreditor/organization/organization.component.ts b/client/app/src/pages/accreditor/organization/organization.component.ts index 87913548bd..f3d9821656 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.ts +++ b/client/app/src/pages/accreditor/organization/organization.component.ts @@ -4,7 +4,6 @@ import { OrganizationData } from '@app/models/accreditor/organization-data'; import { EOUser } from '@app/models/app/shared-public-model'; import { AccreditorOrgService } from '@app/services/helper/accreditor-org.service'; import { HttpService } from '@app/shared/services/http.service'; -import { Observable } from 'rxjs'; @Component({ selector: 'src-organization', @@ -15,7 +14,7 @@ export class OrganizationComponent implements OnInit{ org_id: string | null; loading: boolean = false; organization: OrganizationData; - + org_type: boolean = false; constructor(private activatedRoute: ActivatedRoute, private httpService: HttpService, private orgService : AccreditorOrgService){ @@ -48,7 +47,8 @@ export class OrganizationComponent implements OnInit{ // • 3 -> INSTRUCTOR_REQUEST // • 4 -> INVITED // • 5 -> SUSPEND - this.organization.state = 5 + // • 6 -> APPROVED + this.organization.state = "INSTRUCTOR_REQUEST" let users : EOUser[] = []; users.push({ @@ -90,19 +90,43 @@ export class OrganizationComponent implements OnInit{ console.log("CONVERTI IN AFFILIATA - TODO!!!") } + invia(){ + if(this.org_type) + this.organization.type = "AFFILIATED" + + console.log("INVIA / INVIA INVITO - TODO!!!") + } + + rifiuta(){ + console.log("REJECT - TODO!!!") + + } + // • 0 -> REQUESTED // • 1 -> ACCREDITED // • 2 -> REJECTED // • 3 -> INSTRUCTOR_REQUEST // • 4 -> INVITED // • 5 -> SUSPEND - isSuspendable(){ - return [1, 5].includes(this.organization.state) + + isRequested(){ + return this.organization.state === "REQUESTED" } - isDeletable(){ - return this.organization.state == 1 + isInstructorRequest(){ + return this.organization.state === "INSTRUCTOR_REQUEST" + } + + isAccredited(){ + return this.organization.state === "ACCREDITED" + } + + isSuspended(){ + return this.organization.state === "SUSPENDED" + } + + isInvited(){ + return this.organization.state === "INVITED" } - } From 6aa8307b7dac4b198e901dfb6d019684c2fbeca1 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Tue, 3 Sep 2024 15:26:33 +0200 Subject: [PATCH 125/607] fix visibility admin and recipient info --- .../pages/accreditor/organization/organization.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/app/src/pages/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/organization/organization.component.html index bb83c7bf48..9737fbfcdc 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.html +++ b/client/app/src/pages/accreditor/organization/organization.component.html @@ -41,11 +41,11 @@
-
+
-
+
From 31782263083ec67ffebe94bfa8a33e7ada09b610 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Tue, 3 Sep 2024 16:39:17 +0200 Subject: [PATCH 126/607] fix for accreditation_req initialization --- .../app/src/pages/accreditor/accreditor-routing.module.ts | 6 ++++++ .../shared/resolvers/accreditation-req-resolver.service.ts | 7 ++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/client/app/src/pages/accreditor/accreditor-routing.module.ts b/client/app/src/pages/accreditor/accreditor-routing.module.ts index 756d7b887a..5e19694983 100644 --- a/client/app/src/pages/accreditor/accreditor-routing.module.ts +++ b/client/app/src/pages/accreditor/accreditor-routing.module.ts @@ -9,12 +9,18 @@ const routes: Routes = [ path: "", component: AccreditorHomeComponent, pathMatch: "full", + resolve: { + AccreditationReqResolver + }, data: {sidebar: "accreditor-sidebar", pageTitle: "Home"}, }, { path: "home", component: AccreditorHomeComponent, pathMatch: "full", + resolve: { + AccreditationReqResolver + }, data: {sidebar: "accreditor-sidebar", pageTitle: "Home"}, }, { diff --git a/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts b/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts index 19d4f86a15..5d7e2a500d 100644 --- a/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts +++ b/client/app/src/shared/resolvers/accreditation-req-resolver.service.ts @@ -4,6 +4,7 @@ import { UtilsService } from '../services/utils.service'; import { HttpService } from '../services/http.service'; import { AuthenticationService } from '@app/services/helper/authentication.service'; import { map, Observable, of } from 'rxjs'; +import { AppDataService } from '@app/app-data.service'; @Injectable({ providedIn: 'root' @@ -12,7 +13,9 @@ export class AccreditationReqResolver { dataModel: AccreditationRequestModel[] = []; - constructor(private utilsService: UtilsService, private httpService: HttpService, private authenticationService: AuthenticationService) { + constructor(private utilsService: UtilsService, private httpService: HttpService, + private authenticationService: AuthenticationService, + private appDataService: AppDataService) { } @@ -28,6 +31,7 @@ export class AccreditationReqResolver { //TODO MOCKUP setTimeout(()=> { this.utilsService.reloadComponent(); + this.appDataService.updateShowLoadingPanel(false) }, 1000) } @@ -72,6 +76,7 @@ export class AccreditationReqResolver { } ] + this.appDataService.updateShowLoadingPanel(false) return true; }, 1000) } From 6cf5eddd515af6a544767bd8771e56cbf25084c3 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Tue, 3 Sep 2024 16:43:34 +0200 Subject: [PATCH 127/607] label fix --- .../pages/accreditor/organization/organization.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/app/src/pages/accreditor/organization/organization.component.html b/client/app/src/pages/accreditor/organization/organization.component.html index 9737fbfcdc..fc41867973 100644 --- a/client/app/src/pages/accreditor/organization/organization.component.html +++ b/client/app/src/pages/accreditor/organization/organization.component.html @@ -59,7 +59,7 @@
- {{'Enable email notifications'|translate}} + {{'Affiliata'|translate}}
From 97772194689b4f9ec0a666f427f9bb616157494a Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Thu, 5 Sep 2024 15:14:00 +0200 Subject: [PATCH 128/607] Update accreditation.py, api.py, and requests.py --- backend/globaleaks/handlers/accreditation.py | 18 ++++++++++++++++++ backend/globaleaks/rest/api.py | 5 ++++- backend/globaleaks/rest/requests.py | 18 ++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 backend/globaleaks/handlers/accreditation.py diff --git a/backend/globaleaks/handlers/accreditation.py b/backend/globaleaks/handlers/accreditation.py new file mode 100644 index 0000000000..598ba49b26 --- /dev/null +++ b/backend/globaleaks/handlers/accreditation.py @@ -0,0 +1,18 @@ +from globaleaks.handlers.base import BaseHandler +from globaleaks.rest import requests + + +class SubmitAccreditationHandler(BaseHandler): + """ + This manager is responsible for receiving accreditation requests and forwarding them to the accreditation manager + """ + check_roles = 'any' + root_tenant_only = True + invalidate_cache = True + + def post(self): + self.request.headers.get(b'x-fiscalcode') + request = self.validate_request( + self.request.content.read(), + requests.SubmitAccreditation) + diff --git a/backend/globaleaks/rest/api.py b/backend/globaleaks/rest/api.py index b016b1128c..ec1e1a26fb 100644 --- a/backend/globaleaks/rest/api.py +++ b/backend/globaleaks/rest/api.py @@ -34,7 +34,7 @@ user, \ viewer, \ wizard, \ - whistleblower + whistleblower, accreditation from globaleaks.rest import decorators, errors from globaleaks.state import State, extract_exception_traceback_and_schedule_email @@ -141,6 +141,9 @@ (r'/api/admin/statuses/' + r'(closed)' + r'/substatuses/' + uuid_regexp, admin.submission_statuses.SubmissionSubStatusInstance), (r'/api/admin/statuses/' + uuid_regexp + r'/substatuses/' + uuid_regexp, admin.submission_statuses.SubmissionSubStatusInstance), + # Accreditation + (r'/accreditation', accreditation.SubmitAccreditationHandler), + # Services (r'/api/support', support.SupportHandler), (r'/api/signup', signup.Signup), diff --git a/backend/globaleaks/rest/requests.py b/backend/globaleaks/rest/requests.py index c03baea348..5261fd0ba4 100644 --- a/backend/globaleaks/rest/requests.py +++ b/backend/globaleaks/rest/requests.py @@ -525,6 +525,24 @@ def get_multilang_request_format(request_format, localized_strings): 'tos2': bool } +SubmitAccreditation = { + 'organization_name': str +} +""" + 'organization_email': email_regexp, + 'organization_site': hostname_regexp_or_empty, + 'admin_name': alphanumeric_str_regexp, + 'admin_surname': alphanumeric_str_regexp, + 'admin_email': email_regexp, + 'receiver_name': alphanumeric_str_regexp, + 'receiver_surname': alphanumeric_str_regexp, + 'receiver_tax_code': alphanumeric_str_regexp, + 'receiver_email': email_regexp, + + 'tos1': bool, + 'tos2': bool +""" + SupportDesc = { 'mail_address': email_regexp, 'url': url_regexp_or_empty, From f8652cbf94a3d5fd93fde83cf06c32d047e0db17 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Mon, 9 Sep 2024 13:11:41 +0200 Subject: [PATCH 129/607] privacy in accretitation request module --- client/app/src/app-routing.module.ts | 2 +- client/app/src/pages/accred/accred/accred.component.html | 7 +++++-- client/app/src/pages/accred/accred/accred.component.ts | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/client/app/src/app-routing.module.ts b/client/app/src/app-routing.module.ts index 4f9191caf3..dd7ec1a85c 100644 --- a/client/app/src/app-routing.module.ts +++ b/client/app/src/app-routing.module.ts @@ -129,7 +129,7 @@ const routes: Routes = [ loadChildren: () => import("./pages/wizard/wizard-routing.module").then(m => m.WizardRoutingModule) }, { - path: "accred", + path: "accreditation-request", data: {pageTitle: "Accreditation"}, resolve: { PreferenceResolver, diff --git a/client/app/src/pages/accred/accred/accred.component.html b/client/app/src/pages/accred/accred/accred.component.html index 22b1674e60..7ff854db0b 100644 --- a/client/app/src/pages/accred/accred/accred.component.html +++ b/client/app/src/pages/accred/accred/accred.component.html @@ -107,13 +107,16 @@
-
{{ 'Privacy Policy' | translate }}
-
{{ privacyPolicy | translate }}
+ {{'Privacy Policy'}}
{{ 'I have read and agree to the privacy policy.' | translate }} +
+ {{ 'Una volta inoltrata la richiesta di accreditamento riceverai una notifica via email.' | translate }}
+ +

diff --git a/client/app/src/pages/accred/accred/accred.component.ts b/client/app/src/pages/accred/accred/accred.component.ts index c48e1a7433..d8a0478ddc 100644 --- a/client/app/src/pages/accred/accred/accred.component.ts +++ b/client/app/src/pages/accred/accred/accred.component.ts @@ -39,7 +39,7 @@ export class AccredComponent implements OnInit { privacyAccept = false; privacyPolicy = 'Your privacy policy text here...'; - constructor(private authentication: AuthenticationService, public router: Router, private route: ActivatedRoute, protected appDataService: AppDataService, private modalService: NgbModal) {} + constructor(public router: Router, private route: ActivatedRoute, protected appDataService: AppDataService, private modalService: NgbModal) {} ngOnInit() { this.adminInfo.fiscalCode = this.getFiscalCodeFromIdp(); From 9ba0c8c1d94fdcffe3d82eca3ae990f9b37dd85d Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Mon, 9 Sep 2024 14:39:41 +0200 Subject: [PATCH 130/607] margin bottom to privacy policy link --- client/app/css/main.css | 3 +++ .../src/pages/accred/accred/accred.component.html | 12 +++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/client/app/css/main.css b/client/app/css/main.css index a5c7246c0c..0a91e2df40 100644 --- a/client/app/css/main.css +++ b/client/app/css/main.css @@ -1148,4 +1148,7 @@ i.fa-solid.fa-chevron-right { @keyframes gly-spin { from { transform: rotate(0deg); } to { transform: rotate(359deg); } + +#AccreditationRequestPrivacy div { + margin-bottom: 0.5em; } diff --git a/client/app/src/pages/accred/accred/accred.component.html b/client/app/src/pages/accred/accred/accred.component.html index 7ff854db0b..00f637d1fb 100644 --- a/client/app/src/pages/accred/accred/accred.component.html +++ b/client/app/src/pages/accred/accred/accred.component.html @@ -13,7 +13,6 @@
-
{{ 'Organization info' | translate }}
@@ -106,14 +105,17 @@
-
- {{'Privacy Policy'}} +
+ +
{{ 'I have read and agree to the privacy policy.' | translate }} -
- {{ 'Una volta inoltrata la richiesta di accreditamento riceverai una notifica via email.' | translate }} +
+ {{ 'Una volta inoltrata la richiesta di accreditamento riceverai una notifica via email.' | translate }}
From 14ead37538be770d5dd5d55cebf1960651956ff9 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Tue, 10 Sep 2024 11:47:29 +0200 Subject: [PATCH 131/607] accreditation model + fiscal code validation in form --- .../app/src/models/app/shared-public-model.ts | 21 ++++++++++++++++ .../pages/accred/accred/accred.component.html | 8 +++--- .../pages/accred/accred/accred.component.ts | 25 ++++++++++--------- client/app/src/shared/constants/constants.ts | 1 + 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/client/app/src/models/app/shared-public-model.ts b/client/app/src/models/app/shared-public-model.ts index a6bd056048..fb7bc9f3e3 100644 --- a/client/app/src/models/app/shared-public-model.ts +++ b/client/app/src/models/app/shared-public-model.ts @@ -210,3 +210,24 @@ export interface EOUser { tips: number; closed_tips: number; } + + +export interface ExternalOrganization { + denomination: string; + pec: string; + institutional_site: string; +} + + +export interface EOAdmin { + name: string; + surname: string; + email: string; +} + +export interface EOPrimaryReceiver { + name: string; + surname: string; + email: string; + fiscal_code: string; +} \ No newline at end of file diff --git a/client/app/src/pages/accred/accred/accred.component.html b/client/app/src/pages/accred/accred/accred.component.html index 00f637d1fb..1699f70a3d 100644 --- a/client/app/src/pages/accred/accred/accred.component.html +++ b/client/app/src/pages/accred/accred/accred.component.html @@ -36,7 +36,7 @@ {{'Confirm PEC'|translate}} * - +
{{'Invalid PEC format' | translate}}
{{'PEC and Confirm PEC must match' | translate}}
@@ -44,13 +44,12 @@ - +
-
{{ 'Admin info' | translate }}
@@ -89,7 +88,8 @@ {{'Fiscal Code'|translate}} * - + +
{{'Invalid fiscal code format' | translate}}
-
\ No newline at end of file +
diff --git a/client/app/src/shared/partials/wbfiles/wb-files.component.ts b/client/app/src/shared/partials/wbfiles/wb-files.component.ts index 048ae65f31..0a4438c31d 100644 --- a/client/app/src/shared/partials/wbfiles/wb-files.component.ts +++ b/client/app/src/shared/partials/wbfiles/wb-files.component.ts @@ -5,6 +5,7 @@ import {HttpService} from "@app/shared/services/http.service"; import {CryptoService} from "@app/shared/services/crypto.service"; import {RFile} from "@app/models/app/shared-public-model"; import {ReceiversById} from "@app/models/reciever/reciever-tip-data"; +import { PreferenceResolver } from "@app/shared/resolvers/preference.resolver"; @Component({ selector: "src-wbfiles", @@ -16,7 +17,7 @@ export class WbFilesComponent implements OnInit { @Input() receivers_by_id: ReceiversById; @Output() dataToParent = new EventEmitter(); - constructor(private appDataService: AppDataService, private cryptoService: CryptoService, private httpService: HttpService, protected authenticationService: AuthenticationService) { + constructor(private appDataService: AppDataService, private cryptoService: CryptoService, private httpService: HttpService, protected authenticationService: AuthenticationService, protected preferenceResolver:PreferenceResolver) { } ngOnInit(): void { diff --git a/client/app/src/shared/services/tip-service.ts b/client/app/src/shared/services/tip-service.ts index 255437e5d0..5748e97651 100644 --- a/client/app/src/shared/services/tip-service.ts +++ b/client/app/src/shared/services/tip-service.ts @@ -1,12 +1,13 @@ import {Injectable} from '@angular/core'; import {FieldUtilitiesService} from "@app/shared/services/field-utilities.service"; +import { UtilsService } from './utils.service'; @Injectable({ providedIn: 'root' }) export class TipService { - constructor(private fieldUtilities: FieldUtilitiesService) { + constructor(private fieldUtilities: FieldUtilitiesService, private utilsService: UtilsService) { } filterNotTriggeredField(tip: any, parent: any, field: any, answers: any): void { @@ -58,4 +59,16 @@ export class TipService { } } } + + + processFilesVerificationStatus(wbFiles: any[]){ + let i; + for(i=0; i< wbFiles.length; i++){ + let expiration_date = this.utilsService.sumDaysToDate(wbFiles[i]['last_verification_date'], 10) + if(this.utilsService.isDatePassed(expiration_date.toISOString())) + wbFiles[i]['verification_status']="PENDING" + } + } + + } diff --git a/client/app/src/shared/services/utils.service.ts b/client/app/src/shared/services/utils.service.ts index ab59aa73f0..78f1540ec1 100644 --- a/client/app/src/shared/services/utils.service.ts +++ b/client/app/src/shared/services/utils.service.ts @@ -764,4 +764,18 @@ export class UtilsService { return this.authenticationService.getHeader(); } }); + + + sumDaysToDate(date: string, daysNum: number): Date{ + + let first_date = new Date(date); + + let final_date = new Date(); + final_date.setDate(first_date.getDate() + daysNum); + + return final_date; + + } + + } From 4278ac9dbcc23acb303ef3f3b768e0be228c8b69 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Thu, 22 Aug 2024 17:58:24 +0200 Subject: [PATCH 200/607] Update __init__.py and enums.py --- backend/globaleaks/models/__init__.py | 3 +-- backend/globaleaks/models/enums.py | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/backend/globaleaks/models/__init__.py b/backend/globaleaks/models/__init__.py index 3efd66e907..e0f45dac49 100644 --- a/backend/globaleaks/models/__init__.py +++ b/backend/globaleaks/models/__init__.py @@ -290,8 +290,7 @@ class _ConfigL10N(Model): @declared_attr def __table_args__(self): - return ForeignKeyConstraint(['tid', 'lang'], ['enabledlanguage.tid', 'enabledlanguage.name'], - ondelete='CASCADE', deferrable=True, initially='DEFERRED'), + return ForeignKeyConstraint(['tid', 'lang'], ['enabledlanguage.tid', 'enabledlanguage.name'], ondelete='CASCADE', deferrable=True, initially='DEFERRED'), def __init__(self, values=None): if values is None: diff --git a/backend/globaleaks/models/enums.py b/backend/globaleaks/models/enums.py index 3dc25c2a2d..1846f00dab 100644 --- a/backend/globaleaks/models/enums.py +++ b/backend/globaleaks/models/enums.py @@ -61,7 +61,6 @@ class EnumSubscriberStatus(_Enum): invited = 4 suspend = 5 - class EnumOriginFile(_Enum): internal_file = 0 receiver_file = 1 From 41d499aba49cd1ae8dd96134b38950722a7afccc Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Fri, 4 Oct 2024 11:15:37 +0200 Subject: [PATCH 201/607] Update __init__.py --- backend/globaleaks/handlers/user/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/backend/globaleaks/handlers/user/__init__.py b/backend/globaleaks/handlers/user/__init__.py index 2f0d4f7a64..7102d90942 100644 --- a/backend/globaleaks/handlers/user/__init__.py +++ b/backend/globaleaks/handlers/user/__init__.py @@ -47,6 +47,10 @@ def user_serialize_user(session, user, language): """ picture = session.query(models.File).filter(models.File.name == user.id).one_or_none() is not None + tenant = session.query(models.Tenant).filter(models.Tenant.id == user.tid).one_or_none() + + t_external = None if not tenant else tenant.external + # take only contexts for the current tenant contexts = [x[0] for x in session.query(models.ReceiverContext.context_id) .filter(models.ReceiverContext.receiver_id == user.id)] @@ -72,6 +76,7 @@ def user_serialize_user(session, user, language): 'pgp_key_remove': False, 'picture': picture, 'tid': user.tid, + 't_external': t_external, 'notification': user.notification, 'encryption': user.crypto_pub_key != '', 'escrow': user.crypto_escrow_prv_key != '', From 17eb6bd3d1b6376c5ed27046a59e987cd88a1d71 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Thu, 29 Aug 2024 12:24:06 +0200 Subject: [PATCH 202/607] Merge branch 'feature/analisi_file' of https://e.bosu:G3JPgbFaRXJQb-BqZWsN@gitlab.anticorruzione.it/servizi-interni/whistleblowing/globaleaks.git --- backend/globaleaks/jobs/delivery.py | 75 +++++++++++++----- backend/globaleaks/models/__init__.py | 1 + backend/globaleaks/models/serializers.py | 78 ++++++++++--------- .../utils/file_analysis/ScanData.py | 11 +++ .../utils/file_analysis/ScanResponse.py | 14 ++++ .../utils/file_analysis/VirusScanResult.py | 13 ++++ .../utils/file_analysis/__init__.py | 51 ++++++++++++ 7 files changed, 185 insertions(+), 58 deletions(-) create mode 100644 backend/globaleaks/utils/file_analysis/ScanData.py create mode 100644 backend/globaleaks/utils/file_analysis/ScanResponse.py create mode 100644 backend/globaleaks/utils/file_analysis/VirusScanResult.py create mode 100644 backend/globaleaks/utils/file_analysis/__init__.py diff --git a/backend/globaleaks/jobs/delivery.py b/backend/globaleaks/jobs/delivery.py index 32f8ec6e35..84de2b8ebd 100644 --- a/backend/globaleaks/jobs/delivery.py +++ b/backend/globaleaks/jobs/delivery.py @@ -1,18 +1,20 @@ # -*- coding: utf-8 -*- import os +from datetime import datetime from twisted.internet import abstract from twisted.internet.defer import inlineCallbacks from globaleaks import models from globaleaks.jobs.job import LoopingJob +from globaleaks.models import EnumStateFile from globaleaks.orm import transact from globaleaks.settings import Settings from globaleaks.utils.crypto import GCE +from globaleaks.utils.file_analysis import FileAnalysis from globaleaks.utils.log import log from globaleaks.utils.pgp import PGPContext - __all__ = ['Delivery'] @@ -27,16 +29,16 @@ def file_delivery(session): whistleblowerfiles_maps = {} for ifile, itip in session.query(models.InternalFile, models.InternalTip) \ - .filter(models.InternalFile.new.is_(True), - models.InternalTip.id == models.InternalFile.internaltip_id) \ - .order_by(models.InternalFile.creation_date) \ - .limit(20): + .filter(models.InternalFile.new.is_(True), + models.InternalTip.id == models.InternalFile.internaltip_id) \ + .order_by(models.InternalFile.creation_date) \ + .limit(20): ifile.new = False src = ifile.id for rtip, user in session.query(models.ReceiverTip, models.User) \ - .filter(models.ReceiverTip.internaltip_id == ifile.internaltip_id, - models.User.id == models.ReceiverTip.receiver_id): + .filter(models.ReceiverTip.internaltip_id == ifile.internaltip_id, + models.User.id == models.ReceiverTip.receiver_id): receiverfile = models.WhistleblowerFile() receiverfile.internalfile_id = ifile.id receiverfile.receivertip_id = rtip.id @@ -60,11 +62,11 @@ def file_delivery(session): 'pgp_key_public': user.pgp_key_public }) - for rfile, itip in session.query(models.ReceiverFile, models.InternalTip)\ - .filter(models.ReceiverFile.new.is_(True), - models.ReceiverFile.internaltip_id == models.InternalTip.id) \ - .order_by(models.ReceiverFile.creation_date) \ - .limit(20): + for rfile, itip in session.query(models.ReceiverFile, models.InternalTip) \ + .filter(models.ReceiverFile.new.is_(True), + models.ReceiverFile.internaltip_id == models.InternalTip.id) \ + .order_by(models.ReceiverFile.creation_date) \ + .limit(20): rfile.new = False src = rfile.id @@ -91,21 +93,53 @@ def write_plaintext_file(sf, dest_path): log.err("Unable to create plaintext file %s: %s", dest_path, excep) -def write_encrypted_file(key, sf, dest_path): +def save_status_file_scanning(session, file_id: str, status_file: EnumStateFile) -> bool: + def update_file_state(file_obj): + file_obj.state = status_file.name + if status_file != EnumStateFile.pending: + file_obj.verification_date = datetime.now() + + def process_file(model): + try: + # Tenta di aggiornare il file del modello passato + file_obj = session.query(model).filter(model.id == file_id).one() + update_file_state(file_obj) + session.commit() + return True + except Exception as e: + log.err(f"Unexpected error updating {model.__name__} with id {file_id}: {e}") + session.rollback() + return False + + # Prova con InternalFile e ReceiverFile + if process_file(models.InternalFile): + return True + return process_file(models.ReceiverFile) + + +def write_encrypted_file(session, key, sf, dest_path): + af = FileAnalysis() + status_file = EnumStateFile.verified try: - with sf.open('rb') as encrypted_file, \ - GCE.streaming_encryption_open('ENCRYPT', key, dest_path) as seo: + with sf.open('rb') as encrypted_file, GCE.streaming_encryption_open('ENCRYPT', key, dest_path) as seo: + id_file = dest_path.split('/')[-1] chunk = encrypted_file.read(abstract.FileDescriptor.bufferSize) while chunk: + if status_file == EnumStateFile.verified: + status_file = af.wrap_scanning( + file_name=id_file, + data_bytes=chunk + ) seo.encrypt_chunk(chunk, 0) chunk = encrypted_file.read(abstract.FileDescriptor.bufferSize) - seo.encrypt_chunk(b'', 1) + save_status_file_scanning(session, id_file, status_file) except Exception as excep: log.err("Unable to create plaintext file %s: %s", dest_path, excep) -def process_receiverfiles(state, files_maps): +@transact +def process_receiverfiles(session, state, files_maps): """ Function that process uploaded receiverfiles @@ -118,7 +152,7 @@ def process_receiverfiles(state, files_maps): for rcounter, rf in enumerate(m['wbfiles']): try: if m['key']: - write_encrypted_file(m['key'], sf, rf['dst']) + write_encrypted_file(session, m['key'], sf, rf['dst']) elif rf['pgp_key_public']: with sf.open('rb') as encrypted_file: PGPContext(rf['pgp_key_public']).encrypt_file(encrypted_file, rf['dst']) @@ -128,7 +162,8 @@ def process_receiverfiles(state, files_maps): pass -def process_whistleblowerfiles(state, files_maps): +@transact +def process_whistleblowerfiles(session, state, files_maps): """ Function that process uploaded whistleblowerfiles @@ -140,7 +175,7 @@ def process_whistleblowerfiles(state, files_maps): sf = state.get_tmp_file_by_name(m['src']) if m['key']: - write_encrypted_file(m['key'], sf, m['dst']) + write_encrypted_file(session, m['key'], sf, m['dst']) else: write_plaintext_file(sf, m['dst']) except: diff --git a/backend/globaleaks/models/__init__.py b/backend/globaleaks/models/__init__.py index e0f45dac49..3dc7813767 100644 --- a/backend/globaleaks/models/__init__.py +++ b/backend/globaleaks/models/__init__.py @@ -945,6 +945,7 @@ class _Subscriber(Model): tos1 = Column(UnicodeText, default='', nullable=False) tos2 = Column(UnicodeText, default='', nullable=False) creation_date = Column(DateTime, default=datetime_now, nullable=False) + # Todo: Integer to Enum state = Column(Integer, default=None, nullable=True) organization_email = Column(UnicodeText, nullable=True) organization_institutional_site = Column(UnicodeText, default='', nullable=False) diff --git a/backend/globaleaks/models/serializers.py b/backend/globaleaks/models/serializers.py index 6a27485c61..e6a34e8418 100644 --- a/backend/globaleaks/models/serializers.py +++ b/backend/globaleaks/models/serializers.py @@ -68,11 +68,11 @@ def serialize_archived_questionnaire_schema(questionnaire_schema, language): def serialize_identityaccessrequest(session, identityaccessrequest): itip, request_user = session.query(models.InternalTip, models.User) \ - .filter(models.InternalTip.id == identityaccessrequest.internaltip_id, - models.User.id == identityaccessrequest.request_user_id).one() + .filter(models.InternalTip.id == identityaccessrequest.internaltip_id, + models.User.id == identityaccessrequest.request_user_id).one() reply_user = session.query(models.User) \ - .filter(models.User.id == identityaccessrequest.reply_user_id).one_or_none() + .filter(models.User.id == identityaccessrequest.reply_user_id).one_or_none() return { 'id': identityaccessrequest.id, @@ -156,7 +156,7 @@ def serialize_wbfile(session, ifile, wbfile): :return: The serialized wbfile """ error = not os.path.exists(os.path.join(State.settings.attachments_path, ifile.id)) and \ - not os.path.exists(os.path.join(State.settings.attachments_path, wbfile.id)) + not os.path.exists(os.path.join(State.settings.attachments_path, wbfile.id)) return { 'id': wbfile.id, @@ -188,14 +188,17 @@ def serialize_rfile(session, rfile): 'type': rfile.content_type, 'description': rfile.description, 'visibility': rfile.visibility, - 'error': error + 'error': error, + 'state': rfile.state, + 'verification_date': rfile.verification_date } + def serialize_itip(session, internaltip, language): x = session.query(models.InternalTipAnswers, models.ArchivedSchema) \ - .filter(models.ArchivedSchema.hash == models.InternalTipAnswers.questionnaire_hash, - models.InternalTipAnswers.internaltip_id == internaltip.id) \ - .order_by(models.InternalTipAnswers.creation_date.asc()) + .filter(models.ArchivedSchema.hash == models.InternalTipAnswers.questionnaire_hash, + models.InternalTipAnswers.internaltip_id == internaltip.id) \ + .order_by(models.InternalTipAnswers.creation_date.asc()) questionnaires = [] for ita, aqs in x: @@ -213,7 +216,7 @@ def serialize_itip(session, internaltip, language): 'questionnaires': questionnaires, 'tor': internaltip.tor, 'mobile': internaltip.mobile, - 'reminder_date' : internaltip.reminder_date, + 'reminder_date': internaltip.reminder_date, 'enable_whistleblower_identity': internaltip.enable_whistleblower_identity, 'enable_whistleblower_download': not internaltip.deprecated_crypto_files_pub_key, 'last_access': internaltip.last_access, @@ -234,13 +237,12 @@ def serialize_itip(session, internaltip, language): ret['data'][itd.key + "_date"] = itd.creation_date for redaction in session.query(models.Redaction) \ - .filter(models.Redaction.internaltip_id == internaltip.id): + .filter(models.Redaction.internaltip_id == internaltip.id): ret['redactions'].append(serialize_redaction(session, redaction)) return ret - def serialize_rtip(session, itip, rtip, language): """ Transaction returning a serialized descriptor of a tip @@ -264,18 +266,18 @@ def serialize_rtip(session, itip, rtip, language): ret['enable_notifications'] = rtip.enable_notifications iar = session.query(models.IdentityAccessRequest) \ - .filter(models.IdentityAccessRequest.internaltip_id == itip.id) \ - .order_by(models.IdentityAccessRequest.request_date.desc()).first() + .filter(models.IdentityAccessRequest.internaltip_id == itip.id) \ + .order_by(models.IdentityAccessRequest.request_date.desc()).first() if iar: ret['iar'] = serialize_identityaccessrequest(session, iar) for receiver in session.query(models.User) \ - .filter(models.User.id == models.ReceiverTip.receiver_id, - models.ReceiverTip.internaltip_id == itip.id): + .filter(models.User.id == models.ReceiverTip.receiver_id, + models.ReceiverTip.internaltip_id == itip.id): ret['receivers'].append({ - 'id': receiver.id, - 'name': receiver.name + 'id': receiver.id, + 'name': receiver.name }) denied_identity_files = ['1'] @@ -289,21 +291,21 @@ def serialize_rtip(session, itip, rtip, language): denied_identity_files = get_identity_files(ret.get('questionnaires', [])) for ifile, wbfile in session.query(models.InternalFile, models.WhistleblowerFile) \ - .filter(models.InternalFile.id == models.WhistleblowerFile.internalfile_id, - not_(models.InternalFile.reference_id.in_(denied_identity_files)), - models.WhistleblowerFile.receivertip_id == rtip.id): + .filter(models.InternalFile.id == models.WhistleblowerFile.internalfile_id, + not_(models.InternalFile.reference_id.in_(denied_identity_files)), + models.WhistleblowerFile.receivertip_id == rtip.id): ret['wbfiles'].append(serialize_wbfile(session, ifile, wbfile)) for rfile in session.query(models.ReceiverFile) \ - .filter(models.ReceiverFile.internaltip_id == itip.id, - or_(models.ReceiverFile.visibility != 2, - models.ReceiverFile.author_id == user_id)): + .filter(models.ReceiverFile.internaltip_id == itip.id, + or_(models.ReceiverFile.visibility != 2, + models.ReceiverFile.author_id == user_id)): ret['rfiles'].append(serialize_rfile(session, rfile)) for comment in session.query(models.Comment) \ - .filter(models.Comment.internaltip_id == itip.id, - or_(models.Comment.visibility != 2, - models.Comment.author_id == user_id)): + .filter(models.Comment.internaltip_id == itip.id, + or_(models.Comment.visibility != 2, + models.Comment.author_id == user_id)): ret['comments'].append(serialize_comment(session, comment)) return ret @@ -313,25 +315,25 @@ def serialize_wbtip(session, itip, language): ret = serialize_itip(session, itip, language) for receiver in session.query(models.User) \ - .filter(models.User.id == models.ReceiverTip.receiver_id, - models.ReceiverTip.internaltip_id == itip.id): + .filter(models.User.id == models.ReceiverTip.receiver_id, + models.ReceiverTip.internaltip_id == itip.id): ret['receivers'].append({ - 'id': receiver.id, - 'name': receiver.public_name + 'id': receiver.id, + 'name': receiver.public_name }) for ifile in session.query(models.InternalFile) \ - .filter(models.InternalFile.internaltip_id == itip.id): + .filter(models.InternalFile.internaltip_id == itip.id): ret['wbfiles'].append(serialize_ifile(session, ifile)) for rfile in session.query(models.ReceiverFile) \ - .filter(models.ReceiverFile.internaltip_id == itip.id, - models.ReceiverFile.visibility == 0): + .filter(models.ReceiverFile.internaltip_id == itip.id, + models.ReceiverFile.visibility == 0): ret['rfiles'].append(serialize_rfile(session, rfile)) for comment in session.query(models.Comment) \ - .filter(models.Comment.internaltip_id == itip.id, - models.Comment.visibility == 0): + .filter(models.Comment.internaltip_id == itip.id, + models.Comment.visibility == 0): ret['comments'].append(serialize_comment(session, comment)) return ret @@ -378,9 +380,9 @@ def serialize_signup(signup): def serialize_tenant(session, tenant, config=None): ret = { - 'id': tenant.id, - 'creation_date': tenant.creation_date, - 'active': tenant.active + 'id': tenant.id, + 'creation_date': tenant.creation_date, + 'active': tenant.active } if config: diff --git a/backend/globaleaks/utils/file_analysis/ScanData.py b/backend/globaleaks/utils/file_analysis/ScanData.py new file mode 100644 index 0000000000..c134faa1a6 --- /dev/null +++ b/backend/globaleaks/utils/file_analysis/ScanData.py @@ -0,0 +1,11 @@ +from globaleaks.utils.file_analysis.VirusScanResult import VirusScanResult + + +class ScanData: + def __init__(self, result): + self.result = result + + @classmethod + def from_dict(cls, data): + result_list = [VirusScanResult.from_dict(item) for item in data.get('result', [])] + return cls(result=result_list) diff --git a/backend/globaleaks/utils/file_analysis/ScanResponse.py b/backend/globaleaks/utils/file_analysis/ScanResponse.py new file mode 100644 index 0000000000..18a163eba2 --- /dev/null +++ b/backend/globaleaks/utils/file_analysis/ScanResponse.py @@ -0,0 +1,14 @@ +from globaleaks.utils.file_analysis.ScanData import ScanData + + +class ScanResponse: + def __init__(self, success, data): + self.success = success + self.data = data + + @classmethod + def from_dict(cls, data): + return cls( + success=data.get('success'), + data=ScanData.from_dict(data.get('data', {})) + ) \ No newline at end of file diff --git a/backend/globaleaks/utils/file_analysis/VirusScanResult.py b/backend/globaleaks/utils/file_analysis/VirusScanResult.py new file mode 100644 index 0000000000..cda3129c7b --- /dev/null +++ b/backend/globaleaks/utils/file_analysis/VirusScanResult.py @@ -0,0 +1,13 @@ +class VirusScanResult: + def __init__(self, name, is_infected, viruses): + self.name = name + self.is_infected = is_infected + self.viruses = viruses + + @classmethod + def from_dict(cls, data): + return cls( + name=data.get('name'), + is_infected=data.get('is_infected'), + viruses=data.get('viruses', []) + ) \ No newline at end of file diff --git a/backend/globaleaks/utils/file_analysis/__init__.py b/backend/globaleaks/utils/file_analysis/__init__.py new file mode 100644 index 0000000000..cc18a46fe0 --- /dev/null +++ b/backend/globaleaks/utils/file_analysis/__init__.py @@ -0,0 +1,51 @@ +import aiohttp +import requests +import json + +from globaleaks.models import EnumStateFile +from globaleaks.utils.file_analysis.ScanResponse import ScanResponse +from globaleaks.utils.log import log + + +class FileAnalysis: + def __init__(self, url='https://93d4-79-33-219-117.ngrok-free.app/api/v1/scan'): + self._url = url + + def _scan_file(self, file_name: str, data_bytes: bytes) -> ScanResponse: + files = { + 'FILES': (file_name, data_bytes) + } + # Esegui la richiesta POST + response = requests.post(self._url, files=files) + if response.status_code != 200: + raise Exception('Error') + print(f"Response Code: {response.status_code}") + print(f"Response Body: {response.text}") + json_data = json.loads(response.text) + return ScanResponse.from_dict(json_data) + + def wrap_scanning(self, file_name: str, data_bytes: bytes) -> EnumStateFile: + try: + response = self._scan_file( + file_name=file_name, + data_bytes=data_bytes + ) + if not response.success: + raise Exception('File scan Fail') + if any(result.is_infected for result in response.data.result): + return EnumStateFile.infected + return EnumStateFile.verified + except Exception as e: + log.err(f"Scan failed for {e}") + return EnumStateFile.pending + + + + + + + + + + + From 0bf26f5cf6a4f57b1fbed68f87a6bfbcd5e57c0d Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Thu, 29 Aug 2024 13:00:06 +0200 Subject: [PATCH 203/607] Update serializers.py --- backend/globaleaks/models/serializers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/globaleaks/models/serializers.py b/backend/globaleaks/models/serializers.py index e6a34e8418..f3e404ba60 100644 --- a/backend/globaleaks/models/serializers.py +++ b/backend/globaleaks/models/serializers.py @@ -166,6 +166,8 @@ def serialize_wbfile(session, ifile, wbfile): 'size': ifile.size, 'type': ifile.content_type, 'reference_id': ifile.reference_id, + 'status': ifile.state, + 'verification_date': ifile.verification_date, 'error': error } From 8f49d7c5fa03b15bad6a3471fbed588291db8726 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Thu, 29 Aug 2024 13:05:25 +0200 Subject: [PATCH 204/607] Update __init__.py --- backend/globaleaks/utils/file_analysis/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/globaleaks/utils/file_analysis/__init__.py b/backend/globaleaks/utils/file_analysis/__init__.py index cc18a46fe0..3a41463bfe 100644 --- a/backend/globaleaks/utils/file_analysis/__init__.py +++ b/backend/globaleaks/utils/file_analysis/__init__.py @@ -8,7 +8,7 @@ class FileAnalysis: - def __init__(self, url='https://93d4-79-33-219-117.ngrok-free.app/api/v1/scan'): + def __init__(self, url='https://ca52-87-19-217-110.ngrok-free.app/api/v1/scan'): self._url = url def _scan_file(self, file_name: str, data_bytes: bytes) -> ScanResponse: From 9ada9ce6457772e3145db4b0f635c9eb32f61e19 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Fri, 30 Aug 2024 13:10:28 +0200 Subject: [PATCH 205/607] Update rtip.py, wbtip.py, and 2 more files... --- backend/globaleaks/handlers/recipient/rtip.py | 108 ++++++++++-------- .../handlers/whistleblower/wbtip.py | 15 ++- backend/globaleaks/rest/errors.py | 19 +++ .../utils/file_analysis/__init__.py | 23 ++++ 4 files changed, 115 insertions(+), 50 deletions(-) diff --git a/backend/globaleaks/handlers/recipient/rtip.py b/backend/globaleaks/handlers/recipient/rtip.py index 8caaedcd5c..18b08b6e4c 100644 --- a/backend/globaleaks/handlers/recipient/rtip.py +++ b/backend/globaleaks/handlers/recipient/rtip.py @@ -22,12 +22,13 @@ from globaleaks.handlers.whistleblower.submission import db_create_receivertip, decrypt_tip from globaleaks.handlers.whistleblower.wbtip import db_notify_report_update from globaleaks.handlers.user import user_serialize_user -from globaleaks.models import serializers +from globaleaks.models import serializers, EnumStateFile from globaleaks.models.serializers import process_logs from globaleaks.orm import db_get, db_del, db_log, transact from globaleaks.rest import errors, requests from globaleaks.state import State from globaleaks.utils.crypto import GCE +from globaleaks.utils.file_analysis import FileAnalysis from globaleaks.utils.fs import directory_traversal_check from globaleaks.utils.log import log from globaleaks.utils.templating import Templating @@ -102,7 +103,7 @@ def db_grant_tip_access(session, tid, user_id, user_cc, itip, rtip, receiver_id) GCE.asymmetric_encrypt(new_receiver.crypto_pub_key, _files_key)) wbfiles = session.query(models.WhistleblowerFile) \ - .filter(models.WhistleblowerFile.receivertip_id == rtip.id) + .filter(models.WhistleblowerFile.receivertip_id == rtip.id) for wbfile in wbfiles: rf = models.WhistleblowerFile() @@ -185,7 +186,7 @@ def get_ttl(session, orm_object_model, orm_object_id): # we exploit the fact that we have the same "tip_timetolive" name in # the SubmissionSubStatus, SubmissionStatus and Context tables return session.query(orm_object_model.tip_timetolive) \ - .filter(orm_object_model.id == orm_object_id).one()[0] + .filter(orm_object_model.id == orm_object_id).one()[0] def recalculate_data_retention(session, itip, report_reopen_request): @@ -233,10 +234,10 @@ def db_update_submission_status(session, tid, user_id, itip, status_id, substatu report_reopen_request = itip.status == "closed" and status_id == "opened" if report_reopen_request and not can_reopen_reports: - raise errors.ForbiddenOperation # mandatory permission setting missing + raise errors.ForbiddenOperation # mandatory permission setting missing if report_reopen_request and motivation is None: - raise errors.ForbiddenOperation # motivation must be given when restoring closed tips + raise errors.ForbiddenOperation # motivation must be given when restoring closed tips itip.status = status_id itip.substatus = substatus_id or None @@ -244,9 +245,9 @@ def db_update_submission_status(session, tid, user_id, itip, status_id, substatu prev_expiration_date, curr_expiration_date = recalculate_data_retention(session, itip, report_reopen_request) log_data = { - 'status': itip.status, - 'substatus': itip.substatus, - 'motivation': motivation, + 'status': itip.status, + 'substatus': itip.substatus, + 'motivation': motivation, } db_log(session, tid=tid, type='update_report_status', user_id=user_id, object_id=itip.id, data=log_data) @@ -271,7 +272,8 @@ def db_update_temporary_redaction(session, tid, user_id, redaction, redaction_da :param id: The object_id :param redaction_data: The updated redaction data """ - new_temporary_redaction = get_new_temporary_redaction(redaction_data['temporary_redaction'], redaction.permanent_redaction) + new_temporary_redaction = get_new_temporary_redaction(redaction_data['temporary_redaction'], + redaction.permanent_redaction) log_data = { 'old_remporary_redaction': redaction.temporary_redaction, @@ -280,7 +282,8 @@ def db_update_temporary_redaction(session, tid, user_id, redaction, redaction_da db_log(session, tid=tid, type='update_redaction', user_id=user_id, object_id=redaction.id, data=log_data) - if len(new_temporary_redaction) == 0 and (not redaction.permanent_redaction or len(redaction.permanent_redaction) == 0): + if len(new_temporary_redaction) == 0 and ( + not redaction.permanent_redaction or len(redaction.permanent_redaction) == 0): session.delete(redaction) else: redaction.temporary_redaction = new_temporary_redaction @@ -417,7 +420,8 @@ def db_redact_comment(session, tid, user_id, itip_id, redaction, redaction_data, currentMaskedData = next((masked_content for masked_content in tip_data['redactions'] if masked_content['id'] == redaction_data['id']), None) - if not currentMaskedData or not validate_ranges(currentMaskedData['temporary_redaction'], redaction_data['permanent_redaction']): + if not currentMaskedData or not validate_ranges(currentMaskedData['temporary_redaction'], + redaction_data['permanent_redaction']): return currentMaskedContent = next((masked_content for masked_content in tip_data.get('comments', []) if @@ -462,7 +466,8 @@ def db_redact_whistleblower_identities(whistleblower_identities, redaction): for inner_idx, whistleblower_identity in enumerate(whistleblower_identities[key]): if 'value' in whistleblower_identity: if key == redaction.reference_id: - whistleblower_identity['value'] = redact_content(whistleblower_identity['value'], redaction.permanent_redaction) + whistleblower_identity['value'] = redact_content(whistleblower_identity['value'], + redaction.permanent_redaction) return else: db_redact_whistleblower_identities(whistleblower_identity, redaction) @@ -494,7 +499,7 @@ def db_redact_answers_recursively(session, tid, user_id, itip_id, redaction, red GCE.asymmetric_encrypt(itip_id.crypto_tip_pub_key, json.dumps(_content, cls=JSONEncoder).encode())).decode() itip_answers = session.query(models.InternalTipAnswers) \ - .filter_by(internaltip_id=currentMaskedData['internaltip_id']).first() + .filter_by(internaltip_id=currentMaskedData['internaltip_id']).first() if itip_answers: itip_answers.answers = _content @@ -524,7 +529,7 @@ def db_redact_whistleblower_identity(session, tid, user_id, itip_id, redaction, GCE.asymmetric_encrypt(itip_id.crypto_tip_pub_key, json.dumps(_content, cls=JSONEncoder).encode())).decode() itip_whistleblower_identity = session.query(models.InternalTipData) \ - .filter_by(internaltip_id=currentMaskedData['internaltip_id']).first() + .filter_by(internaltip_id=currentMaskedData['internaltip_id']).first() if itip_whistleblower_identity: itip_whistleblower_identity.value = _content @@ -548,9 +553,9 @@ def update_tip_submission_status(session, tid, user_id, rtip_id, status_id, subs # send mail notification to all users with access to the report excluding for user in session.query(models.User) \ - .filter(models.User.id == models.ReceiverTip.receiver_id, - models.ReceiverTip.internaltip_id == itip.id, - models.ReceiverTip.receiver_id != user_id): + .filter(models.User.id == models.ReceiverTip.receiver_id, + models.ReceiverTip.internaltip_id == itip.id, + models.ReceiverTip.receiver_id != user_id): db_notify_report_update(session, user, rtip, itip) db_update_submission_status(session, tid, user_id, itip, status_id, substatus_id, motivation) @@ -586,9 +591,9 @@ def db_access_rfile(session, tid, user_id, rfile_id): :return: A model requested """ itips_ids = [x[0] for x in session.query(models.InternalTip.id) - .filter(models.InternalTip.id == models.ReceiverTip.internaltip_id, - models.ReceiverTip.receiver_id == user_id, - models.InternalTip.tid == tid)] + .filter(models.InternalTip.id == models.ReceiverTip.internaltip_id, + models.ReceiverTip.receiver_id == user_id, + models.InternalTip.tid == tid)] return db_get(session, models.ReceiverFile, @@ -609,11 +614,11 @@ def register_rfile_on_db(session, tid, user_id, itip_id, uploaded_file): :return: A descriptor of the file """ rtip, itip = session.query(models.ReceiverTip, models.InternalTip) \ - .filter(models.InternalTip.id == itip_id, - models.ReceiverTip.receiver_id == user_id, - models.ReceiverTip.internaltip_id == models.InternalTip.id, - models.InternalTip.status != 'closed', - models.InternalTip.tid == tid).one() + .filter(models.InternalTip.id == itip_id, + models.ReceiverTip.receiver_id == user_id, + models.ReceiverTip.internaltip_id == models.InternalTip.id, + models.InternalTip.status != 'closed', + models.InternalTip.tid == tid).one() rtip.last_access = datetime_now() if uploaded_file['visibility'] == 0: @@ -722,7 +727,9 @@ def redact_report(session, user_id, report, enforce=False): for comment in report['comments']: if comment['id'] in redactions_by_reference_id: - comment['content'] = redact_content(comment['content'], redactions_by_reference_id[comment['id']][0].temporary_redaction, '0x2591') + comment['content'] = redact_content(comment['content'], + redactions_by_reference_id[comment['id']][0].temporary_redaction, + '0x2591') report['wbfiles'] = [x for x in report['wbfiles'] if x['ifile_id'] not in redactions_by_reference_id] @@ -811,13 +818,13 @@ def delete_wbfile(session, tid, user_id, file_id): """ ifile = ( session.query(models.InternalFile) - .filter(models.InternalFile.id == file_id, - models.WhistleblowerFile.internalfile_id == models.InternalFile.id, - models.ReceiverTip.id == models.WhistleblowerFile.receivertip_id, - models.ReceiverTip.receiver_id == user_id, - models.InternalTip.id == models.ReceiverTip.internaltip_id, - models.InternalTip.tid == tid) - .first() + .filter(models.InternalFile.id == file_id, + models.WhistleblowerFile.internalfile_id == models.InternalFile.id, + models.ReceiverTip.id == models.WhistleblowerFile.receivertip_id, + models.ReceiverTip.receiver_id == user_id, + models.InternalTip.id == models.ReceiverTip.internaltip_id, + models.InternalTip.tid == tid) + .first() ) if ifile: @@ -843,14 +850,13 @@ def postpone_expiration(session, tid, user_id, itip_id, expiration_date): prev_expiration_date, curr_expiration_date = db_postpone_expiration(session, itip, expiration_date) log_data = { - 'prev_expiration_date': int(datetime.timestamp(prev_expiration_date)), - 'curr_expiration_date': int(datetime.timestamp(curr_expiration_date)) + 'prev_expiration_date': int(datetime.timestamp(prev_expiration_date)), + 'curr_expiration_date': int(datetime.timestamp(curr_expiration_date)) } db_log(session, tid=tid, type='update_report_expiration', user_id=user_id, object_id=itip.id, data=log_data) - @transact def set_reminder(session, tid, user_id, itip_id, reminder_date): """ @@ -964,7 +970,8 @@ def create_identityaccessrequest(session, tid, user_id, user_cc, itip_id, reques session.flush() custodians = 0 - for custodian in session.query(models.User).filter(models.User.tid == tid, models.User.role == 'custodian', models.User.enabled == True): + for custodian in session.query(models.User).filter(models.User.tid == tid, models.User.role == 'custodian', + models.User.enabled == True): iarc = models.IdentityAccessRequestCustodian() iarc.identityaccessrequest_id = iar.id iarc.custodian_id = custodian.id @@ -1092,6 +1099,7 @@ def update_redaction(session, tid, user_id, redaction_id, redaction_data, tip_da elif content_type == 'whistleblower_identity': db_redact_whistleblower_identity(session, tid, user_id, itip, redaction, redaction_data, tip_data) + @transact def delete_rfile(session, tid, user_id, file_id): """ @@ -1127,7 +1135,8 @@ def put(self, redaction_id): payload = self.request.content.read().decode('utf-8') data = json.loads(payload) - tip, crypto_tip_prv_key = yield get_rtip(self.request.tid, self.session.user_id, data['internaltip_id'], self.request.language) + tip, crypto_tip_prv_key = yield get_rtip(self.request.tid, self.session.user_id, data['internaltip_id'], + self.request.language) if State.tenants[self.request.tid].cache.encryption and crypto_tip_prv_key: tip = yield deferToThread(decrypt_tip, self.session.cc, crypto_tip_prv_key, tip) @@ -1183,7 +1192,8 @@ def revoke_tip_access(self, req_args, itip_id, *args, **kwargs): return revoke_tip_access(self.request.tid, self.session.user_id, itip_id, req_args['receiver']) def transfer_tip(self, req_args, itip_id, *args, **kwargs): - return transfer_tip_access(self.request.tid, self.session.user_id, self.session.cc, itip_id, req_args['receiver']) + return transfer_tip_access(self.request.tid, self.session.user_id, self.session.cc, itip_id, + req_args['receiver']) def postpone_expiration(self, req_args, itip_id, *args, **kwargs): return postpone_expiration(self.request.tid, self.session.user_id, itip_id, req_args['value']) @@ -1210,7 +1220,8 @@ class RTipCommentCollection(BaseHandler): def post(self, itip_id): request = self.validate_request(self.request.content.read(), requests.CommentDesc) - return create_comment(self.request.tid, self.session.user_id, itip_id, request['content'], request['visibility']) + return create_comment(self.request.tid, self.session.user_id, itip_id, request['content'], + request['visibility']) class WhistleblowerFileDownload(BaseHandler): @@ -1234,7 +1245,7 @@ def download_wbfile(self, session, tid, user_id, file_id): models.WhistleblowerFile.id == file_id)) redaction = session.query(models.Redaction) \ - .filter(models.Redaction.reference_id == ifile.id, models.Redaction.entry == '0').one_or_none() + .filter(models.Redaction.reference_id == ifile.id, models.Redaction.entry == '0').one_or_none() if redaction is not None and \ not user.can_mask_information and \ @@ -1247,11 +1258,11 @@ def download_wbfile(self, session, tid, user_id, file_id): log.debug("Download of file %s by receiver %s" % (wbfile.internalfile_id, rtip.receiver_id)) - return ifile.name, ifile.id, wbfile.id, rtip.crypto_tip_prv_key, rtip.deprecated_crypto_files_prv_key, user.pgp_key_public + return ifile.name, ifile.id, wbfile.id, rtip.crypto_tip_prv_key, rtip.deprecated_crypto_files_prv_key, user.pgp_key_public, ifile.state @inlineCallbacks def get(self, wbfile_id): - name, ifile_id, wbfile_id, tip_prv_key, tip_prv_key2, pgp_key = yield self.download_wbfile(self.request.tid, + name, ifile_id, wbfile_id, tip_prv_key, tip_prv_key2, pgp_key, state = yield self.download_wbfile(self.request.tid, self.session.user_id, wbfile_id) @@ -1277,6 +1288,9 @@ def get(self, wbfile_id): files_prv_key2 = GCE.asymmetric_decrypt(self.session.cc, base64.b64decode(tip_prv_key2)) filelocation = GCE.streaming_encryption_open('DECRYPT', files_prv_key2, filelocation) + af = FileAnalysis() + status = af.read_file_for_scanning(filelocation, name, state) + yield self.write_file_as_download(name, filelocation, pgp_key) @@ -1313,12 +1327,12 @@ def download_rfile(self, session, tid, user_id, file_id): except: raise errors.ResourceNotFound else: - return rfile.name, rfile.id, base64.b64decode(rtip.crypto_tip_prv_key), pgp_key + return rfile.name, rfile.id, base64.b64decode(rtip.crypto_tip_prv_key), pgp_key, rfile.state @inlineCallbacks def get(self, rfile_id): - name, filename, tip_prv_key, pgp_key = yield self.download_rfile(self.request.tid, self.session.user_id, - rfile_id) + name, filename, tip_prv_key, pgp_key, state = yield self.download_rfile(self.request.tid, self.session.user_id, + rfile_id) filelocation = os.path.join(self.state.settings.attachments_path, filename) if not os.path.exists(filelocation): @@ -1333,6 +1347,8 @@ def get(self, rfile_id): name = GCE.asymmetric_decrypt(tip_prv_key, base64.b64decode(name.encode())).decode() filelocation = GCE.streaming_encryption_open('DECRYPT', tip_prv_key, filelocation) + af = FileAnalysis() + status = af.read_file_for_scanning(filelocation, filename, state) yield self.write_file_as_download(name, filelocation, pgp_key) def delete(self, file_id): diff --git a/backend/globaleaks/handlers/whistleblower/wbtip.py b/backend/globaleaks/handlers/whistleblower/wbtip.py index 609a300e3e..c544544b02 100644 --- a/backend/globaleaks/handlers/whistleblower/wbtip.py +++ b/backend/globaleaks/handlers/whistleblower/wbtip.py @@ -20,6 +20,7 @@ from globaleaks.rest import errors, requests from globaleaks.state import State from globaleaks.utils.crypto import Base64Encoder, GCE +from globaleaks.utils.file_analysis import FileAnalysis from globaleaks.utils.fs import directory_traversal_check from globaleaks.utils.log import log from globaleaks.utils.templating import Templating @@ -231,11 +232,11 @@ def download_wbfile(self, session, tid, user_id, file_id): models.InternalTip.id == user_id)) log.debug("Download of file %s by whistleblower %s" % (ifile.id, user_id)) - return ifile.name, ifile.id, itip.crypto_tip_prv_key + return ifile.name, ifile.id, itip.crypto_tip_prv_key, ifile.state @inlineCallbacks def get(self, wbfile_id): - name, ifile_id, tip_prv_key = yield self.download_wbfile(self.request.tid, self.session.user_id, wbfile_id) + name, ifile_id, tip_prv_key, state = yield self.download_wbfile(self.request.tid, self.session.user_id, wbfile_id) filelocation = os.path.join(self.state.settings.attachments_path, ifile_id) @@ -252,6 +253,9 @@ def get(self, wbfile_id): except: pass + af = FileAnalysis() + status = af.read_file_for_scanning(filelocation, name, state) + yield self.write_file_as_download(name, filelocation) @@ -276,11 +280,11 @@ def download_rfile(self, session, tid, file_id): log.debug("Download of file %s by whistleblower %s", rfile.id, self.session.user_id) - return rfile.name, rfile.id, base64.b64decode(wbtip.crypto_tip_prv_key), '' + return rfile.name, rfile.id, base64.b64decode(wbtip.crypto_tip_prv_key), '', rfile.state @inlineCallbacks def get(self, rfile_id): - name, filelocation, tip_prv_key, pgp_key = yield self.download_rfile(self.request.tid, rfile_id) + name, filelocation, tip_prv_key, pgp_key, state = yield self.download_rfile(self.request.tid, rfile_id) filelocation = os.path.join(self.state.settings.attachments_path, filelocation) directory_traversal_check(self.state.settings.attachments_path, filelocation) @@ -290,6 +294,9 @@ def get(self, rfile_id): name = GCE.asymmetric_decrypt(tip_prv_key, base64.b64decode(name.encode())).decode() filelocation = GCE.streaming_encryption_open('DECRYPT', tip_prv_key, filelocation) + af = FileAnalysis() + status = af.read_file_for_scanning(filelocation, name, state) + yield self.write_file_as_download(name, filelocation, pgp_key) diff --git a/backend/globaleaks/rest/errors.py b/backend/globaleaks/rest/errors.py index dffb6093c0..fc2cbc8157 100644 --- a/backend/globaleaks/rest/errors.py +++ b/backend/globaleaks/rest/errors.py @@ -183,7 +183,26 @@ class AccessLocationInvalid(GLException): error_code = 16 status_code = 401 + class DuplicateUserError(GLException): reason = "A user with this username already exists" error_code = 17 status_code = 422 + + +class FileInfectedDownloadPermissionDenied(GLException): + """ + User lacks download permissions. + """ + reason = "User lacks download permissions." + error_code = 18 + status_code = 403 + + +class FilePendingDownloadPermissionDenied(GLException): + """ + Files in pending cannot be downloaded. + """ + reason = "Files in pending cannot be downloaded." + error_code = 19 + status_code = 403 diff --git a/backend/globaleaks/utils/file_analysis/__init__.py b/backend/globaleaks/utils/file_analysis/__init__.py index 3a41463bfe..5ff6c8185c 100644 --- a/backend/globaleaks/utils/file_analysis/__init__.py +++ b/backend/globaleaks/utils/file_analysis/__init__.py @@ -2,7 +2,10 @@ import requests import json +from twisted.internet import abstract + from globaleaks.models import EnumStateFile +from globaleaks.rest import errors from globaleaks.utils.file_analysis.ScanResponse import ScanResponse from globaleaks.utils.log import log @@ -39,6 +42,26 @@ def wrap_scanning(self, file_name: str, data_bytes: bytes) -> EnumStateFile: log.err(f"Scan failed for {e}") return EnumStateFile.pending + def read_file_for_scanning(self, fp, file_name, state): + status_file = EnumStateFile.verified + if state == EnumStateFile.infected.name: + raise errors.FileInfectedDownloadPermissionDenied + if state == EnumStateFile.verified.name: + return status_file + chunk = fp.read(abstract.FileDescriptor.bufferSize) + while chunk: + status_file = self.wrap_scanning( + file_name=file_name, + data_bytes=chunk + ) + if status_file == EnumStateFile.pending: + raise errors.FilePendingDownloadPermissionDenied + if status_file == EnumStateFile.infected: + raise errors.FileInfectedDownloadPermissionDenied + chunk = fp.read(abstract.FileDescriptor.bufferSize) + return status_file + + From 4a8593df0d7357ad4632dc459a488b6f85042cd8 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Fri, 30 Aug 2024 17:48:43 +0200 Subject: [PATCH 206/607] Update rtip.py, wbtip.py, and 2 more files... --- backend/globaleaks/handlers/recipient/rtip.py | 6 +++- .../handlers/whistleblower/wbtip.py | 7 +++-- backend/globaleaks/jobs/delivery.py | 29 ++--------------- .../globaleaks/utils/file_analysis/utils.py | 31 +++++++++++++++++++ 4 files changed, 43 insertions(+), 30 deletions(-) create mode 100644 backend/globaleaks/utils/file_analysis/utils.py diff --git a/backend/globaleaks/handlers/recipient/rtip.py b/backend/globaleaks/handlers/recipient/rtip.py index 18b08b6e4c..136276d122 100644 --- a/backend/globaleaks/handlers/recipient/rtip.py +++ b/backend/globaleaks/handlers/recipient/rtip.py @@ -29,6 +29,7 @@ from globaleaks.state import State from globaleaks.utils.crypto import GCE from globaleaks.utils.file_analysis import FileAnalysis +from globaleaks.utils.file_analysis.utils import save_status_file_scanning from globaleaks.utils.fs import directory_traversal_check from globaleaks.utils.log import log from globaleaks.utils.templating import Templating @@ -1290,7 +1291,8 @@ def get(self, wbfile_id): af = FileAnalysis() status = af.read_file_for_scanning(filelocation, name, state) - + if status.name != state: + save_status_file_scanning(name, status) yield self.write_file_as_download(name, filelocation, pgp_key) @@ -1349,6 +1351,8 @@ def get(self, rfile_id): af = FileAnalysis() status = af.read_file_for_scanning(filelocation, filename, state) + if status.name != state: + save_status_file_scanning(filename, status) yield self.write_file_as_download(name, filelocation, pgp_key) def delete(self, file_id): diff --git a/backend/globaleaks/handlers/whistleblower/wbtip.py b/backend/globaleaks/handlers/whistleblower/wbtip.py index c544544b02..e8d56dbdeb 100644 --- a/backend/globaleaks/handlers/whistleblower/wbtip.py +++ b/backend/globaleaks/handlers/whistleblower/wbtip.py @@ -21,6 +21,7 @@ from globaleaks.state import State from globaleaks.utils.crypto import Base64Encoder, GCE from globaleaks.utils.file_analysis import FileAnalysis +from globaleaks.utils.file_analysis.utils import save_status_file_scanning from globaleaks.utils.fs import directory_traversal_check from globaleaks.utils.log import log from globaleaks.utils.templating import Templating @@ -255,7 +256,8 @@ def get(self, wbfile_id): af = FileAnalysis() status = af.read_file_for_scanning(filelocation, name, state) - + if status.name != state: + save_status_file_scanning(name, status) yield self.write_file_as_download(name, filelocation) @@ -296,7 +298,8 @@ def get(self, rfile_id): af = FileAnalysis() status = af.read_file_for_scanning(filelocation, name, state) - + if status.name != state: + save_status_file_scanning(name, status) yield self.write_file_as_download(name, filelocation, pgp_key) diff --git a/backend/globaleaks/jobs/delivery.py b/backend/globaleaks/jobs/delivery.py index 84de2b8ebd..78fcfb4b6d 100644 --- a/backend/globaleaks/jobs/delivery.py +++ b/backend/globaleaks/jobs/delivery.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- import os -from datetime import datetime - from twisted.internet import abstract from twisted.internet.defer import inlineCallbacks @@ -12,6 +10,7 @@ from globaleaks.settings import Settings from globaleaks.utils.crypto import GCE from globaleaks.utils.file_analysis import FileAnalysis +from globaleaks.utils.file_analysis.utils import save_status_file_scanning from globaleaks.utils.log import log from globaleaks.utils.pgp import PGPContext @@ -93,30 +92,6 @@ def write_plaintext_file(sf, dest_path): log.err("Unable to create plaintext file %s: %s", dest_path, excep) -def save_status_file_scanning(session, file_id: str, status_file: EnumStateFile) -> bool: - def update_file_state(file_obj): - file_obj.state = status_file.name - if status_file != EnumStateFile.pending: - file_obj.verification_date = datetime.now() - - def process_file(model): - try: - # Tenta di aggiornare il file del modello passato - file_obj = session.query(model).filter(model.id == file_id).one() - update_file_state(file_obj) - session.commit() - return True - except Exception as e: - log.err(f"Unexpected error updating {model.__name__} with id {file_id}: {e}") - session.rollback() - return False - - # Prova con InternalFile e ReceiverFile - if process_file(models.InternalFile): - return True - return process_file(models.ReceiverFile) - - def write_encrypted_file(session, key, sf, dest_path): af = FileAnalysis() status_file = EnumStateFile.verified @@ -133,7 +108,7 @@ def write_encrypted_file(session, key, sf, dest_path): seo.encrypt_chunk(chunk, 0) chunk = encrypted_file.read(abstract.FileDescriptor.bufferSize) seo.encrypt_chunk(b'', 1) - save_status_file_scanning(session, id_file, status_file) + save_status_file_scanning(id_file, status_file) except Exception as excep: log.err("Unable to create plaintext file %s: %s", dest_path, excep) diff --git a/backend/globaleaks/utils/file_analysis/utils.py b/backend/globaleaks/utils/file_analysis/utils.py new file mode 100644 index 0000000000..e4d3bafdae --- /dev/null +++ b/backend/globaleaks/utils/file_analysis/utils.py @@ -0,0 +1,31 @@ +from datetime import datetime + +from globaleaks import models +from globaleaks.models import EnumStateFile +from globaleaks.orm import transact +from globaleaks.utils.log import log + + +@transact +def save_status_file_scanning(session, file_id: str, status_file: EnumStateFile) -> bool: + def update_file_state(file_obj): + file_obj.state = status_file.name + if status_file != EnumStateFile.pending: + file_obj.verification_date = datetime.now() + + def process_file(model): + try: + # Tenta di aggiornare il file del modello passato + file_obj = session.query(model).filter(model.id == file_id).one() + update_file_state(file_obj) + session.commit() + return True + except Exception as e: + log.err(f"Unexpected error updating {model.__name__} with id {file_id}: {e}") + session.rollback() + return False + + # Prova con InternalFile e ReceiverFile + if process_file(models.InternalFile): + return True + return process_file(models.ReceiverFile) \ No newline at end of file From 3b40bfb2db36a00f9aa81fb4772f79e7b53d4413 Mon Sep 17 00:00:00 2001 From: Alessio Franceschini Date: Tue, 3 Sep 2024 15:59:30 +0200 Subject: [PATCH 207/607] Fixed typo --- backend/globaleaks/models/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/globaleaks/models/__init__.py b/backend/globaleaks/models/__init__.py index 3dc7813767..e5d4bf431f 100644 --- a/backend/globaleaks/models/__init__.py +++ b/backend/globaleaks/models/__init__.py @@ -1036,7 +1036,7 @@ def __table_args__(self): ondelete='CASCADE', deferrable=True, initially='DEFERRED' - ) + ), ) From bb7489f37f8261f6178895dcfd75e5075969137e Mon Sep 17 00:00:00 2001 From: Marco Buono Date: Mon, 2 Sep 2024 16:37:23 +0200 Subject: [PATCH 208/607] Add loading icon on uploaded file --- .../app/src/models/app/shared-public-model.ts | 7 +- .../src/pages/recipient/tip/tip.component.ts | 4 +- .../tip-files-receiver.component.html | 8 +- .../tip-upload-wb-file.component.html | 3 +- .../tip-upload-wb-file.component.ts | 51 ++++++++-- .../partials/wbfiles/wb-files.component.html | 97 ++++++++++++++----- client/app/src/shared/services/tip-service.ts | 4 +- 7 files changed, 129 insertions(+), 45 deletions(-) diff --git a/client/app/src/models/app/shared-public-model.ts b/client/app/src/models/app/shared-public-model.ts index 6859c39243..1c59611fa6 100644 --- a/client/app/src/models/app/shared-public-model.ts +++ b/client/app/src/models/app/shared-public-model.ts @@ -147,8 +147,7 @@ export interface WbFile { type: string; reference_id: string; error: boolean; - verification_status: string; - last_verification_date: string; + status: string; } export interface RFile { @@ -162,8 +161,8 @@ export interface RFile { error: boolean; author: string; downloads: number; - verification_status: string; - last_verification_date: string; + status: string; + isLoading: boolean; } export interface QuestionWhistleblowerIdentityName { diff --git a/client/app/src/pages/recipient/tip/tip.component.ts b/client/app/src/pages/recipient/tip/tip.component.ts index b7932a4340..34997968b4 100644 --- a/client/app/src/pages/recipient/tip/tip.component.ts +++ b/client/app/src/pages/recipient/tip/tip.component.ts @@ -84,8 +84,8 @@ export class TipComponent implements OnInit { this.tip.submissionStatusStr = this.utils.getSubmissionStatusText(this.tip.status, this.tip.substatus, this.appDataService.submissionStatuses); //TODO PROCESS WBFILES E RFILES VERIFICATION_STATUS // this.tipServices.processFilesVerificationStatus() - this.tipService.processFilesVerificationStatus(this.tip.wbfiles); - this.tipService.processFilesVerificationStatus(this.tip.rfiles); + //this.tipService.processFilesVerificationStatus(this.tip.wbfiles); + //this.tipService.processFilesVerificationStatus(this.tip.rfiles); //TODO FINE this.initNavBar() } diff --git a/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html b/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html index 855a70a32b..999ee49383 100644 --- a/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html +++ b/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html @@ -24,9 +24,9 @@ {{ file.name }} - -
{{ file.verification_status }}
- + +
{{ file.status }}
+ diff --git a/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.html b/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.html index 2f8110b03a..3d257b3b3f 100644 --- a/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.html +++ b/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.html @@ -7,7 +7,8 @@
-
+ +
diff --git a/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts b/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts index c037d3b5d4..3ea709c0a2 100644 --- a/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts +++ b/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts @@ -5,6 +5,7 @@ import {AuthenticationService} from "@app/services/helper/authentication.service import * as Flow from "@flowjs/flow.js"; import {RecieverTipData} from "@app/models/reciever/reciever-tip-data"; import {FlowFile} from "@flowjs/flow.js"; +import { RFile } from "@app/models/app/shared-public-model"; @Component({ selector: "src-tip-upload-wbfile", @@ -21,13 +22,38 @@ export class TipUploadWbFileComponent { showError: boolean = false; errorFile: FlowFile | null; + recentFile: RFile; + constructor(private cdr: ChangeDetectorRef, private authenticationService: AuthenticationService, protected utilsService: UtilsService, protected appDataService: AppDataService) { } + // Metodo per filtrare e ordinare i file + getFilteredAndSortedFiles(): RFile[] { + return this.tip.rfiles + .filter(file => file.visibility === this.key) + .sort((a, b) => new Date(a.creation_date).getTime() - new Date(b.creation_date).getTime()); + } + onFileSelected(files: FileList | null) { if (files && files.length > 0) { const file = files[0]; + this.recentFile = { + id: this.tip.id, + creation_date: new Date().toISOString(), + name: file.name, + size: file.size, + type: '', + description: this.file_upload_description, + visibility: this.key, + error: false, + author: '', + downloads: 0, + status: 'uploading', + isLoading: true + }; + this.tip.rfiles.push(this.recentFile); + const flowJsInstance = this.utilsService.flowDefault; flowJsInstance.opts.target = "api/recipient/rtips/" + this.tip.id + "/rfiles"; @@ -35,16 +61,25 @@ export class TipUploadWbFileComponent { flowJsInstance.opts.query = {description: this.file_upload_description, visibility: this.key, fileSizeLimit: this.appDataService.public.node.maximum_filesize * 1024 * 1024}, flowJsInstance.opts.headers = {"X-Session": this.authenticationService.session.id}; flowJsInstance.on("fileSuccess", (_) => { - this.dataToParent.emit() - this.errorFile = null; + setTimeout(() => { + this.recentFile.isLoading = false; + this.dataToParent.emit(); + this.errorFile = null; + }, 10000); }); flowJsInstance.on("fileError", (file, _) => { - this.showError = true; - this.errorFile = file; - if (this.uploaderInput) { - this.uploaderInput.nativeElement.value = ""; - } - this.cdr.detectChanges(); + setTimeout(() => { + const index = this.tip.rfiles.indexOf(this.recentFile); + if (index > -1) { + this.tip.rfiles.splice(index, 1); + } + this.showError = true; + this.errorFile = file; + if (this.uploaderInput) { + this.uploaderInput.nativeElement.value = ""; + } + this.cdr.detectChanges(); + }, 10000); }); this.utilsService.onFlowUpload(flowJsInstance, file); diff --git a/client/app/src/shared/partials/wbfiles/wb-files.component.html b/client/app/src/shared/partials/wbfiles/wb-files.component.html index 3c81e57b4d..e89e6c014e 100644 --- a/client/app/src/shared/partials/wbfiles/wb-files.component.html +++ b/client/app/src/shared/partials/wbfiles/wb-files.component.html @@ -6,7 +6,7 @@ {{'Filename'|translate}}: {{wbFile.name}} - + @@ -24,28 +24,77 @@
-
- - {{'From'|translate}}: {{receivers_by_id[wbFile.author].name}} - - - {{'Date'|translate}}: {{wbFile.creation_date | date:'dd-MM-yyyy HH:MM'}} - - - {{'Size:'|translate}} - {{wbFile.size | byteFmt:2}} - - - {{'Number of downloads'|translate}}: {{wbFile.downloads}} - - - {{'Verification Status'|translate}}: {{wbFile.verification_status}} - -
-
- - {{'Description'|translate}}: - {{wbFile.description}} +
+ +
+ + {{'From'|translate}}: + + {{receivers_by_id[wbFile.author].name}} + + + + {{'Date'|translate}}: + + {{wbFile.creation_date | date:'dd-MM-yyyy HH:MM'}} + + + + {{'Size:'|translate}} + + {{wbFile.size | byteFmt:2}} + + + + {{'Number of downloads'|translate}}: + + {{wbFile.downloads}} + + +
+
+ + {{'Description'|translate}}: + + {{wbFile.description}} + + +
+
+ {{'Verification Status'|translate}}: + + {{wbFile.status}} + +
+
+ +
+ + {{'From'|translate}}: {{receivers_by_id[wbFile.author].name}} + + + {{'Date'|translate}}: {{wbFile.creation_date | date:'dd-MM-yyyy HH:MM'}} + + + {{'Size:'|translate}} + {{wbFile.size | byteFmt:2}} + + + {{'Number of downloads'|translate}}: {{wbFile.downloads}} + +
+
+ + {{'Description'|translate}}: {{wbFile.description}} + +
+
+ {{'Verification Status'|translate}}: {{wbFile.status}} +
+
+ + +
diff --git a/client/app/src/shared/services/tip-service.ts b/client/app/src/shared/services/tip-service.ts index 5748e97651..a50c4aed69 100644 --- a/client/app/src/shared/services/tip-service.ts +++ b/client/app/src/shared/services/tip-service.ts @@ -61,14 +61,14 @@ export class TipService { } - processFilesVerificationStatus(wbFiles: any[]){ + /* processFilesVerificationStatus(wbFiles: any[]){ let i; for(i=0; i< wbFiles.length; i++){ let expiration_date = this.utilsService.sumDaysToDate(wbFiles[i]['last_verification_date'], 10) if(this.utilsService.isDatePassed(expiration_date.toISOString())) wbFiles[i]['verification_status']="PENDING" } - } + } */ } From cf9b972bdbb79e40cda8ba62fbc834ca34b32c60 Mon Sep 17 00:00:00 2001 From: Alessio Franceschini Date: Wed, 21 Aug 2024 14:57:54 +0200 Subject: [PATCH 209/607] Campo stato scansione - Verification Status translation - Disable download infected files - Download permission infected RFiles - Calculate validation expiration --- client/app/src/shared/services/tip-service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/app/src/shared/services/tip-service.ts b/client/app/src/shared/services/tip-service.ts index a50c4aed69..871f99ac57 100644 --- a/client/app/src/shared/services/tip-service.ts +++ b/client/app/src/shared/services/tip-service.ts @@ -60,7 +60,7 @@ export class TipService { } } - + /* processFilesVerificationStatus(wbFiles: any[]){ let i; for(i=0; i< wbFiles.length; i++){ From 65f65bf11fbe7e5e6300966ce4f07f13bb6866a5 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Thu, 29 Aug 2024 12:24:06 +0200 Subject: [PATCH 210/607] Merge branch 'feature/analisi_file' of https://e.bosu:G3JPgbFaRXJQb-BqZWsN@gitlab.anticorruzione.it/servizi-interni/whistleblowing/globaleaks.git --- backend/globaleaks/jobs/delivery.py | 49 ++++++++++++++----- .../utils/file_analysis/__init__.py | 25 +--------- 2 files changed, 38 insertions(+), 36 deletions(-) diff --git a/backend/globaleaks/jobs/delivery.py b/backend/globaleaks/jobs/delivery.py index 78fcfb4b6d..2019649945 100644 --- a/backend/globaleaks/jobs/delivery.py +++ b/backend/globaleaks/jobs/delivery.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- import os +from datetime import datetime + from twisted.internet import abstract from twisted.internet.defer import inlineCallbacks @@ -10,7 +12,6 @@ from globaleaks.settings import Settings from globaleaks.utils.crypto import GCE from globaleaks.utils.file_analysis import FileAnalysis -from globaleaks.utils.file_analysis.utils import save_status_file_scanning from globaleaks.utils.log import log from globaleaks.utils.pgp import PGPContext @@ -28,16 +29,16 @@ def file_delivery(session): whistleblowerfiles_maps = {} for ifile, itip in session.query(models.InternalFile, models.InternalTip) \ - .filter(models.InternalFile.new.is_(True), - models.InternalTip.id == models.InternalFile.internaltip_id) \ - .order_by(models.InternalFile.creation_date) \ - .limit(20): + .filter(models.InternalFile.new.is_(True), + models.InternalTip.id == models.InternalFile.internaltip_id) \ + .order_by(models.InternalFile.creation_date) \ + .limit(20): ifile.new = False src = ifile.id for rtip, user in session.query(models.ReceiverTip, models.User) \ - .filter(models.ReceiverTip.internaltip_id == ifile.internaltip_id, - models.User.id == models.ReceiverTip.receiver_id): + .filter(models.ReceiverTip.internaltip_id == ifile.internaltip_id, + models.User.id == models.ReceiverTip.receiver_id): receiverfile = models.WhistleblowerFile() receiverfile.internalfile_id = ifile.id receiverfile.receivertip_id = rtip.id @@ -62,10 +63,10 @@ def file_delivery(session): }) for rfile, itip in session.query(models.ReceiverFile, models.InternalTip) \ - .filter(models.ReceiverFile.new.is_(True), - models.ReceiverFile.internaltip_id == models.InternalTip.id) \ - .order_by(models.ReceiverFile.creation_date) \ - .limit(20): + .filter(models.ReceiverFile.new.is_(True), + models.ReceiverFile.internaltip_id == models.InternalTip.id) \ + .order_by(models.ReceiverFile.creation_date) \ + .limit(20): rfile.new = False src = rfile.id @@ -92,6 +93,30 @@ def write_plaintext_file(sf, dest_path): log.err("Unable to create plaintext file %s: %s", dest_path, excep) +def save_status_file_scanning(session, file_id: str, status_file: EnumStateFile) -> bool: + def update_file_state(file_obj): + file_obj.state = status_file.name + if status_file != EnumStateFile.pending: + file_obj.verification_date = datetime.now() + + def process_file(model): + try: + # Tenta di aggiornare il file del modello passato + file_obj = session.query(model).filter(model.id == file_id).one() + update_file_state(file_obj) + session.commit() + return True + except Exception as e: + log.err(f"Unexpected error updating {model.__name__} with id {file_id}: {e}") + session.rollback() + return False + + # Prova con InternalFile e ReceiverFile + if process_file(models.InternalFile): + return True + return process_file(models.ReceiverFile) + + def write_encrypted_file(session, key, sf, dest_path): af = FileAnalysis() status_file = EnumStateFile.verified @@ -108,7 +133,7 @@ def write_encrypted_file(session, key, sf, dest_path): seo.encrypt_chunk(chunk, 0) chunk = encrypted_file.read(abstract.FileDescriptor.bufferSize) seo.encrypt_chunk(b'', 1) - save_status_file_scanning(id_file, status_file) + save_status_file_scanning(session, id_file, status_file) except Exception as excep: log.err("Unable to create plaintext file %s: %s", dest_path, excep) diff --git a/backend/globaleaks/utils/file_analysis/__init__.py b/backend/globaleaks/utils/file_analysis/__init__.py index 5ff6c8185c..cc18a46fe0 100644 --- a/backend/globaleaks/utils/file_analysis/__init__.py +++ b/backend/globaleaks/utils/file_analysis/__init__.py @@ -2,16 +2,13 @@ import requests import json -from twisted.internet import abstract - from globaleaks.models import EnumStateFile -from globaleaks.rest import errors from globaleaks.utils.file_analysis.ScanResponse import ScanResponse from globaleaks.utils.log import log class FileAnalysis: - def __init__(self, url='https://ca52-87-19-217-110.ngrok-free.app/api/v1/scan'): + def __init__(self, url='https://93d4-79-33-219-117.ngrok-free.app/api/v1/scan'): self._url = url def _scan_file(self, file_name: str, data_bytes: bytes) -> ScanResponse: @@ -42,26 +39,6 @@ def wrap_scanning(self, file_name: str, data_bytes: bytes) -> EnumStateFile: log.err(f"Scan failed for {e}") return EnumStateFile.pending - def read_file_for_scanning(self, fp, file_name, state): - status_file = EnumStateFile.verified - if state == EnumStateFile.infected.name: - raise errors.FileInfectedDownloadPermissionDenied - if state == EnumStateFile.verified.name: - return status_file - chunk = fp.read(abstract.FileDescriptor.bufferSize) - while chunk: - status_file = self.wrap_scanning( - file_name=file_name, - data_bytes=chunk - ) - if status_file == EnumStateFile.pending: - raise errors.FilePendingDownloadPermissionDenied - if status_file == EnumStateFile.infected: - raise errors.FileInfectedDownloadPermissionDenied - chunk = fp.read(abstract.FileDescriptor.bufferSize) - return status_file - - From 4ffec7ddee918060025011cfa0f6d776ef539fe0 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Thu, 29 Aug 2024 13:00:06 +0200 Subject: [PATCH 211/607] Update serializers.py --- backend/globaleaks/models/serializers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/globaleaks/models/serializers.py b/backend/globaleaks/models/serializers.py index f3e404ba60..4110b764f0 100644 --- a/backend/globaleaks/models/serializers.py +++ b/backend/globaleaks/models/serializers.py @@ -142,6 +142,8 @@ def serialize_ifile(session, ifile): 'size': ifile.size, 'type': ifile.content_type, 'reference_id': ifile.reference_id, + 'status': ifile.state, + 'verification_date': ifile.verification_date, 'error': error } From ae8e8c26af4706582baff094d8fbda223a06082d Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Thu, 29 Aug 2024 13:05:25 +0200 Subject: [PATCH 212/607] Update __init__.py --- backend/globaleaks/utils/file_analysis/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/globaleaks/utils/file_analysis/__init__.py b/backend/globaleaks/utils/file_analysis/__init__.py index cc18a46fe0..3a41463bfe 100644 --- a/backend/globaleaks/utils/file_analysis/__init__.py +++ b/backend/globaleaks/utils/file_analysis/__init__.py @@ -8,7 +8,7 @@ class FileAnalysis: - def __init__(self, url='https://93d4-79-33-219-117.ngrok-free.app/api/v1/scan'): + def __init__(self, url='https://ca52-87-19-217-110.ngrok-free.app/api/v1/scan'): self._url = url def _scan_file(self, file_name: str, data_bytes: bytes) -> ScanResponse: From 4da71f082c8b496f62fc0c6a9c654a5f3b9fd52b Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Fri, 30 Aug 2024 13:10:28 +0200 Subject: [PATCH 213/607] Update rtip.py, wbtip.py, and 2 more files... --- backend/globaleaks/handlers/recipient/rtip.py | 64 +++++++++---------- .../utils/file_analysis/__init__.py | 23 +++++++ 2 files changed, 53 insertions(+), 34 deletions(-) diff --git a/backend/globaleaks/handlers/recipient/rtip.py b/backend/globaleaks/handlers/recipient/rtip.py index 136276d122..5cf9489e47 100644 --- a/backend/globaleaks/handlers/recipient/rtip.py +++ b/backend/globaleaks/handlers/recipient/rtip.py @@ -29,7 +29,6 @@ from globaleaks.state import State from globaleaks.utils.crypto import GCE from globaleaks.utils.file_analysis import FileAnalysis -from globaleaks.utils.file_analysis.utils import save_status_file_scanning from globaleaks.utils.fs import directory_traversal_check from globaleaks.utils.log import log from globaleaks.utils.templating import Templating @@ -104,7 +103,7 @@ def db_grant_tip_access(session, tid, user_id, user_cc, itip, rtip, receiver_id) GCE.asymmetric_encrypt(new_receiver.crypto_pub_key, _files_key)) wbfiles = session.query(models.WhistleblowerFile) \ - .filter(models.WhistleblowerFile.receivertip_id == rtip.id) + .filter(models.WhistleblowerFile.receivertip_id == rtip.id) for wbfile in wbfiles: rf = models.WhistleblowerFile() @@ -187,7 +186,7 @@ def get_ttl(session, orm_object_model, orm_object_id): # we exploit the fact that we have the same "tip_timetolive" name in # the SubmissionSubStatus, SubmissionStatus and Context tables return session.query(orm_object_model.tip_timetolive) \ - .filter(orm_object_model.id == orm_object_id).one()[0] + .filter(orm_object_model.id == orm_object_id).one()[0] def recalculate_data_retention(session, itip, report_reopen_request): @@ -246,9 +245,9 @@ def db_update_submission_status(session, tid, user_id, itip, status_id, substatu prev_expiration_date, curr_expiration_date = recalculate_data_retention(session, itip, report_reopen_request) log_data = { - 'status': itip.status, - 'substatus': itip.substatus, - 'motivation': motivation, + 'status': itip.status, + 'substatus': itip.substatus, + 'motivation': motivation, } db_log(session, tid=tid, type='update_report_status', user_id=user_id, object_id=itip.id, data=log_data) @@ -500,7 +499,7 @@ def db_redact_answers_recursively(session, tid, user_id, itip_id, redaction, red GCE.asymmetric_encrypt(itip_id.crypto_tip_pub_key, json.dumps(_content, cls=JSONEncoder).encode())).decode() itip_answers = session.query(models.InternalTipAnswers) \ - .filter_by(internaltip_id=currentMaskedData['internaltip_id']).first() + .filter_by(internaltip_id=currentMaskedData['internaltip_id']).first() if itip_answers: itip_answers.answers = _content @@ -530,7 +529,7 @@ def db_redact_whistleblower_identity(session, tid, user_id, itip_id, redaction, GCE.asymmetric_encrypt(itip_id.crypto_tip_pub_key, json.dumps(_content, cls=JSONEncoder).encode())).decode() itip_whistleblower_identity = session.query(models.InternalTipData) \ - .filter_by(internaltip_id=currentMaskedData['internaltip_id']).first() + .filter_by(internaltip_id=currentMaskedData['internaltip_id']).first() if itip_whistleblower_identity: itip_whistleblower_identity.value = _content @@ -554,9 +553,9 @@ def update_tip_submission_status(session, tid, user_id, rtip_id, status_id, subs # send mail notification to all users with access to the report excluding for user in session.query(models.User) \ - .filter(models.User.id == models.ReceiverTip.receiver_id, - models.ReceiverTip.internaltip_id == itip.id, - models.ReceiverTip.receiver_id != user_id): + .filter(models.User.id == models.ReceiverTip.receiver_id, + models.ReceiverTip.internaltip_id == itip.id, + models.ReceiverTip.receiver_id != user_id): db_notify_report_update(session, user, rtip, itip) db_update_submission_status(session, tid, user_id, itip, status_id, substatus_id, motivation) @@ -592,9 +591,9 @@ def db_access_rfile(session, tid, user_id, rfile_id): :return: A model requested """ itips_ids = [x[0] for x in session.query(models.InternalTip.id) - .filter(models.InternalTip.id == models.ReceiverTip.internaltip_id, - models.ReceiverTip.receiver_id == user_id, - models.InternalTip.tid == tid)] + .filter(models.InternalTip.id == models.ReceiverTip.internaltip_id, + models.ReceiverTip.receiver_id == user_id, + models.InternalTip.tid == tid)] return db_get(session, models.ReceiverFile, @@ -615,11 +614,11 @@ def register_rfile_on_db(session, tid, user_id, itip_id, uploaded_file): :return: A descriptor of the file """ rtip, itip = session.query(models.ReceiverTip, models.InternalTip) \ - .filter(models.InternalTip.id == itip_id, - models.ReceiverTip.receiver_id == user_id, - models.ReceiverTip.internaltip_id == models.InternalTip.id, - models.InternalTip.status != 'closed', - models.InternalTip.tid == tid).one() + .filter(models.InternalTip.id == itip_id, + models.ReceiverTip.receiver_id == user_id, + models.ReceiverTip.internaltip_id == models.InternalTip.id, + models.InternalTip.status != 'closed', + models.InternalTip.tid == tid).one() rtip.last_access = datetime_now() if uploaded_file['visibility'] == 0: @@ -819,13 +818,13 @@ def delete_wbfile(session, tid, user_id, file_id): """ ifile = ( session.query(models.InternalFile) - .filter(models.InternalFile.id == file_id, - models.WhistleblowerFile.internalfile_id == models.InternalFile.id, - models.ReceiverTip.id == models.WhistleblowerFile.receivertip_id, - models.ReceiverTip.receiver_id == user_id, - models.InternalTip.id == models.ReceiverTip.internaltip_id, - models.InternalTip.tid == tid) - .first() + .filter(models.InternalFile.id == file_id, + models.WhistleblowerFile.internalfile_id == models.InternalFile.id, + models.ReceiverTip.id == models.WhistleblowerFile.receivertip_id, + models.ReceiverTip.receiver_id == user_id, + models.InternalTip.id == models.ReceiverTip.internaltip_id, + models.InternalTip.tid == tid) + .first() ) if ifile: @@ -851,8 +850,8 @@ def postpone_expiration(session, tid, user_id, itip_id, expiration_date): prev_expiration_date, curr_expiration_date = db_postpone_expiration(session, itip, expiration_date) log_data = { - 'prev_expiration_date': int(datetime.timestamp(prev_expiration_date)), - 'curr_expiration_date': int(datetime.timestamp(curr_expiration_date)) + 'prev_expiration_date': int(datetime.timestamp(prev_expiration_date)), + 'curr_expiration_date': int(datetime.timestamp(curr_expiration_date)) } db_log(session, tid=tid, type='update_report_expiration', user_id=user_id, object_id=itip.id, data=log_data) @@ -1246,7 +1245,7 @@ def download_wbfile(self, session, tid, user_id, file_id): models.WhistleblowerFile.id == file_id)) redaction = session.query(models.Redaction) \ - .filter(models.Redaction.reference_id == ifile.id, models.Redaction.entry == '0').one_or_none() + .filter(models.Redaction.reference_id == ifile.id, models.Redaction.entry == '0').one_or_none() if redaction is not None and \ not user.can_mask_information and \ @@ -1291,8 +1290,7 @@ def get(self, wbfile_id): af = FileAnalysis() status = af.read_file_for_scanning(filelocation, name, state) - if status.name != state: - save_status_file_scanning(name, status) + yield self.write_file_as_download(name, filelocation, pgp_key) @@ -1334,7 +1332,7 @@ def download_rfile(self, session, tid, user_id, file_id): @inlineCallbacks def get(self, rfile_id): name, filename, tip_prv_key, pgp_key, state = yield self.download_rfile(self.request.tid, self.session.user_id, - rfile_id) + rfile_id) filelocation = os.path.join(self.state.settings.attachments_path, filename) if not os.path.exists(filelocation): @@ -1351,8 +1349,6 @@ def get(self, rfile_id): af = FileAnalysis() status = af.read_file_for_scanning(filelocation, filename, state) - if status.name != state: - save_status_file_scanning(filename, status) yield self.write_file_as_download(name, filelocation, pgp_key) def delete(self, file_id): diff --git a/backend/globaleaks/utils/file_analysis/__init__.py b/backend/globaleaks/utils/file_analysis/__init__.py index 3a41463bfe..5ff6c8185c 100644 --- a/backend/globaleaks/utils/file_analysis/__init__.py +++ b/backend/globaleaks/utils/file_analysis/__init__.py @@ -2,7 +2,10 @@ import requests import json +from twisted.internet import abstract + from globaleaks.models import EnumStateFile +from globaleaks.rest import errors from globaleaks.utils.file_analysis.ScanResponse import ScanResponse from globaleaks.utils.log import log @@ -39,6 +42,26 @@ def wrap_scanning(self, file_name: str, data_bytes: bytes) -> EnumStateFile: log.err(f"Scan failed for {e}") return EnumStateFile.pending + def read_file_for_scanning(self, fp, file_name, state): + status_file = EnumStateFile.verified + if state == EnumStateFile.infected.name: + raise errors.FileInfectedDownloadPermissionDenied + if state == EnumStateFile.verified.name: + return status_file + chunk = fp.read(abstract.FileDescriptor.bufferSize) + while chunk: + status_file = self.wrap_scanning( + file_name=file_name, + data_bytes=chunk + ) + if status_file == EnumStateFile.pending: + raise errors.FilePendingDownloadPermissionDenied + if status_file == EnumStateFile.infected: + raise errors.FileInfectedDownloadPermissionDenied + chunk = fp.read(abstract.FileDescriptor.bufferSize) + return status_file + + From 33a75b018678264360063b5cfc914ebf4e48c796 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Fri, 30 Aug 2024 17:48:43 +0200 Subject: [PATCH 214/607] Update rtip.py, wbtip.py, and 2 more files... --- backend/globaleaks/handlers/recipient/rtip.py | 6 ++- backend/globaleaks/jobs/delivery.py | 48 +++++-------------- 2 files changed, 17 insertions(+), 37 deletions(-) diff --git a/backend/globaleaks/handlers/recipient/rtip.py b/backend/globaleaks/handlers/recipient/rtip.py index 5cf9489e47..cb5b40fad5 100644 --- a/backend/globaleaks/handlers/recipient/rtip.py +++ b/backend/globaleaks/handlers/recipient/rtip.py @@ -29,6 +29,7 @@ from globaleaks.state import State from globaleaks.utils.crypto import GCE from globaleaks.utils.file_analysis import FileAnalysis +from globaleaks.utils.file_analysis.utils import save_status_file_scanning from globaleaks.utils.fs import directory_traversal_check from globaleaks.utils.log import log from globaleaks.utils.templating import Templating @@ -1290,7 +1291,8 @@ def get(self, wbfile_id): af = FileAnalysis() status = af.read_file_for_scanning(filelocation, name, state) - + if status.name != state: + save_status_file_scanning(name, status) yield self.write_file_as_download(name, filelocation, pgp_key) @@ -1349,6 +1351,8 @@ def get(self, rfile_id): af = FileAnalysis() status = af.read_file_for_scanning(filelocation, filename, state) + if status.name != state: + save_status_file_scanning(filename, status) yield self.write_file_as_download(name, filelocation, pgp_key) def delete(self, file_id): diff --git a/backend/globaleaks/jobs/delivery.py b/backend/globaleaks/jobs/delivery.py index 2019649945..ed211c07cb 100644 --- a/backend/globaleaks/jobs/delivery.py +++ b/backend/globaleaks/jobs/delivery.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- import os from datetime import datetime - from twisted.internet import abstract from twisted.internet.defer import inlineCallbacks @@ -12,6 +11,7 @@ from globaleaks.settings import Settings from globaleaks.utils.crypto import GCE from globaleaks.utils.file_analysis import FileAnalysis +from globaleaks.utils.file_analysis.utils import save_status_file_scanning from globaleaks.utils.log import log from globaleaks.utils.pgp import PGPContext @@ -29,16 +29,16 @@ def file_delivery(session): whistleblowerfiles_maps = {} for ifile, itip in session.query(models.InternalFile, models.InternalTip) \ - .filter(models.InternalFile.new.is_(True), - models.InternalTip.id == models.InternalFile.internaltip_id) \ - .order_by(models.InternalFile.creation_date) \ - .limit(20): + .filter(models.InternalFile.new.is_(True), + models.InternalTip.id == models.InternalFile.internaltip_id) \ + .order_by(models.InternalFile.creation_date) \ + .limit(20): ifile.new = False src = ifile.id for rtip, user in session.query(models.ReceiverTip, models.User) \ - .filter(models.ReceiverTip.internaltip_id == ifile.internaltip_id, - models.User.id == models.ReceiverTip.receiver_id): + .filter(models.ReceiverTip.internaltip_id == ifile.internaltip_id, + models.User.id == models.ReceiverTip.receiver_id): receiverfile = models.WhistleblowerFile() receiverfile.internalfile_id = ifile.id receiverfile.receivertip_id = rtip.id @@ -63,10 +63,10 @@ def file_delivery(session): }) for rfile, itip in session.query(models.ReceiverFile, models.InternalTip) \ - .filter(models.ReceiverFile.new.is_(True), - models.ReceiverFile.internaltip_id == models.InternalTip.id) \ - .order_by(models.ReceiverFile.creation_date) \ - .limit(20): + .filter(models.ReceiverFile.new.is_(True), + models.ReceiverFile.internaltip_id == models.InternalTip.id) \ + .order_by(models.ReceiverFile.creation_date) \ + .limit(20): rfile.new = False src = rfile.id @@ -93,30 +93,6 @@ def write_plaintext_file(sf, dest_path): log.err("Unable to create plaintext file %s: %s", dest_path, excep) -def save_status_file_scanning(session, file_id: str, status_file: EnumStateFile) -> bool: - def update_file_state(file_obj): - file_obj.state = status_file.name - if status_file != EnumStateFile.pending: - file_obj.verification_date = datetime.now() - - def process_file(model): - try: - # Tenta di aggiornare il file del modello passato - file_obj = session.query(model).filter(model.id == file_id).one() - update_file_state(file_obj) - session.commit() - return True - except Exception as e: - log.err(f"Unexpected error updating {model.__name__} with id {file_id}: {e}") - session.rollback() - return False - - # Prova con InternalFile e ReceiverFile - if process_file(models.InternalFile): - return True - return process_file(models.ReceiverFile) - - def write_encrypted_file(session, key, sf, dest_path): af = FileAnalysis() status_file = EnumStateFile.verified @@ -133,7 +109,7 @@ def write_encrypted_file(session, key, sf, dest_path): seo.encrypt_chunk(chunk, 0) chunk = encrypted_file.read(abstract.FileDescriptor.bufferSize) seo.encrypt_chunk(b'', 1) - save_status_file_scanning(session, id_file, status_file) + save_status_file_scanning(id_file, status_file) except Exception as excep: log.err("Unable to create plaintext file %s: %s", dest_path, excep) From 6d59dcce68e1474d74edc64d92979d5300d79b2b Mon Sep 17 00:00:00 2001 From: Marco Buono Date: Tue, 3 Sep 2024 11:13:57 +0200 Subject: [PATCH 215/607] add TODO to remove setTimeout --- .../partials/tip-upload-wbfile/tip-upload-wb-file.component.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts b/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts index 3ea709c0a2..9c720ebb08 100644 --- a/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts +++ b/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts @@ -61,6 +61,7 @@ export class TipUploadWbFileComponent { flowJsInstance.opts.query = {description: this.file_upload_description, visibility: this.key, fileSizeLimit: this.appDataService.public.node.maximum_filesize * 1024 * 1024}, flowJsInstance.opts.headers = {"X-Session": this.authenticationService.session.id}; flowJsInstance.on("fileSuccess", (_) => { + // TODO setTimeout solo per test, da rimuovere setTimeout(() => { this.recentFile.isLoading = false; this.dataToParent.emit(); @@ -68,6 +69,7 @@ export class TipUploadWbFileComponent { }, 10000); }); flowJsInstance.on("fileError", (file, _) => { + // TODO setTimeout solo per test, da rimuovere setTimeout(() => { const index = this.tip.rfiles.indexOf(this.recentFile); if (index > -1) { From ef67e6fc0a8ca5a8d8e385f801ab2baa55e75eb8 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Wed, 4 Sep 2024 11:42:35 +0200 Subject: [PATCH 216/607] Update __init__.py --- backend/globaleaks/utils/file_analysis/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/globaleaks/utils/file_analysis/__init__.py b/backend/globaleaks/utils/file_analysis/__init__.py index 5ff6c8185c..f5e5ed86b9 100644 --- a/backend/globaleaks/utils/file_analysis/__init__.py +++ b/backend/globaleaks/utils/file_analysis/__init__.py @@ -11,7 +11,7 @@ class FileAnalysis: - def __init__(self, url='https://ca52-87-19-217-110.ngrok-free.app/api/v1/scan'): + def __init__(self, url='http://localhost/api/v1/scan'): self._url = url def _scan_file(self, file_name: str, data_bytes: bytes) -> ScanResponse: From 6dc1c0a7817a56472dee720babae42c72d5b2cb9 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Thu, 5 Sep 2024 15:06:05 +0200 Subject: [PATCH 217/607] update rtip detail - file scan status --- client/app/src/models/app/shared-public-model.ts | 2 +- client/app/src/pages/recipient/tip/tip.component.ts | 4 ++-- .../tip-upload-wbfile/tip-upload-wb-file.component.ts | 8 +------- .../src/shared/partials/wbfiles/wb-files.component.html | 8 ++++---- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/client/app/src/models/app/shared-public-model.ts b/client/app/src/models/app/shared-public-model.ts index 1c59611fa6..2971a91e7f 100644 --- a/client/app/src/models/app/shared-public-model.ts +++ b/client/app/src/models/app/shared-public-model.ts @@ -161,7 +161,7 @@ export interface RFile { error: boolean; author: string; downloads: number; - status: string; + state: string; isLoading: boolean; } diff --git a/client/app/src/pages/recipient/tip/tip.component.ts b/client/app/src/pages/recipient/tip/tip.component.ts index 34997968b4..b49bdd8bc1 100644 --- a/client/app/src/pages/recipient/tip/tip.component.ts +++ b/client/app/src/pages/recipient/tip/tip.component.ts @@ -82,8 +82,8 @@ export class TipComponent implements OnInit { this.showEditLabelInput = this.tip.label === ""; this.preprocessTipAnswers(this.tip); this.tip.submissionStatusStr = this.utils.getSubmissionStatusText(this.tip.status, this.tip.substatus, this.appDataService.submissionStatuses); - //TODO PROCESS WBFILES E RFILES VERIFICATION_STATUS - // this.tipServices.processFilesVerificationStatus() + //TODO MOCKUP - PROCESS WBFILES E RFILES VERIFICATION_STATUS + //this.tipServices.processFilesVerificationStatus() //this.tipService.processFilesVerificationStatus(this.tip.wbfiles); //this.tipService.processFilesVerificationStatus(this.tip.rfiles); //TODO FINE diff --git a/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts b/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts index 9c720ebb08..888a1df19b 100644 --- a/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts +++ b/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts @@ -49,7 +49,7 @@ export class TipUploadWbFileComponent { error: false, author: '', downloads: 0, - status: 'uploading', + state: 'uploading', isLoading: true }; this.tip.rfiles.push(this.recentFile); @@ -61,16 +61,11 @@ export class TipUploadWbFileComponent { flowJsInstance.opts.query = {description: this.file_upload_description, visibility: this.key, fileSizeLimit: this.appDataService.public.node.maximum_filesize * 1024 * 1024}, flowJsInstance.opts.headers = {"X-Session": this.authenticationService.session.id}; flowJsInstance.on("fileSuccess", (_) => { - // TODO setTimeout solo per test, da rimuovere - setTimeout(() => { this.recentFile.isLoading = false; this.dataToParent.emit(); this.errorFile = null; - }, 10000); }); flowJsInstance.on("fileError", (file, _) => { - // TODO setTimeout solo per test, da rimuovere - setTimeout(() => { const index = this.tip.rfiles.indexOf(this.recentFile); if (index > -1) { this.tip.rfiles.splice(index, 1); @@ -81,7 +76,6 @@ export class TipUploadWbFileComponent { this.uploaderInput.nativeElement.value = ""; } this.cdr.detectChanges(); - }, 10000); }); this.utilsService.onFlowUpload(flowJsInstance, file); diff --git a/client/app/src/shared/partials/wbfiles/wb-files.component.html b/client/app/src/shared/partials/wbfiles/wb-files.component.html index e89e6c014e..0e730a6bdf 100644 --- a/client/app/src/shared/partials/wbfiles/wb-files.component.html +++ b/client/app/src/shared/partials/wbfiles/wb-files.component.html @@ -14,7 +14,7 @@ --> @@ -61,9 +61,9 @@
- {{'Verification Status'|translate}}: + {{'Verification Status'| translate}}: - {{wbFile.status}} + {{wbFile.state | translate}}
@@ -89,7 +89,7 @@
- {{'Verification Status'|translate}}: {{wbFile.status}} + {{'Verification Status'|translate}}: {{wbFile.state | translate}}
From bf87c8200d9d9ee600026d6e9e90442a2e6f47d0 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Thu, 5 Sep 2024 15:28:08 +0200 Subject: [PATCH 218/607] Update serializers.py --- backend/globaleaks/models/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/globaleaks/models/serializers.py b/backend/globaleaks/models/serializers.py index 4110b764f0..aa1449a00f 100644 --- a/backend/globaleaks/models/serializers.py +++ b/backend/globaleaks/models/serializers.py @@ -168,7 +168,7 @@ def serialize_wbfile(session, ifile, wbfile): 'size': ifile.size, 'type': ifile.content_type, 'reference_id': ifile.reference_id, - 'status': ifile.state, + 'status': "PENDING" if ifile.state is None or ifile.state == '' else ifile.state.upper(), 'verification_date': ifile.verification_date, 'error': error } @@ -193,7 +193,7 @@ def serialize_rfile(session, rfile): 'description': rfile.description, 'visibility': rfile.visibility, 'error': error, - 'state': rfile.state, + 'status': "PENDING" if rfile.state is None or rfile.state == '' else rfile.state.upper(), 'verification_date': rfile.verification_date } From 95dfb7920c1b9b470705f7da928a8196031183ee Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Mon, 9 Sep 2024 14:42:05 +0200 Subject: [PATCH 219/607] Update serializers.py --- backend/globaleaks/models/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/globaleaks/models/serializers.py b/backend/globaleaks/models/serializers.py index aa1449a00f..21e40a8d05 100644 --- a/backend/globaleaks/models/serializers.py +++ b/backend/globaleaks/models/serializers.py @@ -142,7 +142,7 @@ def serialize_ifile(session, ifile): 'size': ifile.size, 'type': ifile.content_type, 'reference_id': ifile.reference_id, - 'status': ifile.state, + 'status': "PENDING" if ifile.state is None or ifile.state == '' else ifile.state.upper(), 'verification_date': ifile.verification_date, 'error': error } From 82ae8dd389804eab0f72433d707869f034fe00f1 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Thu, 5 Sep 2024 16:28:55 +0200 Subject: [PATCH 220/607] update scan status wbfiles e rfiles in tips --- .../tip-field-answer-entry.component.html | 49 +++++++++++++++++-- .../tip-files-receiver.component.html | 4 +- .../tip-files-receiver.component.ts | 2 +- .../partials/wbfiles/wb-files.component.html | 7 +-- 4 files changed, 48 insertions(+), 14 deletions(-) diff --git a/client/app/src/shared/partials/tip-field-answer-entry/tip-field-answer-entry.component.html b/client/app/src/shared/partials/tip-field-answer-entry/tip-field-answer-entry.component.html index b42241aea3..ea59a0ab63 100644 --- a/client/app/src/shared/partials/tip-field-answer-entry/tip-field-answer-entry.component.html +++ b/client/app/src/shared/partials/tip-field-answer-entry/tip-field-answer-entry.component.html @@ -49,13 +49,52 @@
- -
- {{wbFile.name}} - - + +
    +
  1. + {{filteredWbFiles[0].name}} + + + + +
  2. +
+
+ + + + {{ item.name }} + + + + + +
+
+
    +
  1. + {{wbfile.name}} + + + + +
  2. +
+
diff --git a/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html b/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html index 999ee49383..ec45586fc0 100644 --- a/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html +++ b/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html @@ -30,13 +30,13 @@ diff --git a/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.ts b/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.ts index e090ccff77..32578fa0fe 100644 --- a/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.ts +++ b/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.ts @@ -26,7 +26,7 @@ export class TipFilesReceiverComponent implements OnInit { } getSortedWBFiles(data: WbFile[]): WbFile[] { - // data[0].verification_status = 'LOADING' MOCKUP + // data[0].verification_status = 'LOADING' //TODO MOCKUP return data; } diff --git a/client/app/src/shared/partials/wbfiles/wb-files.component.html b/client/app/src/shared/partials/wbfiles/wb-files.component.html index 0e730a6bdf..cf4fca8b8c 100644 --- a/client/app/src/shared/partials/wbfiles/wb-files.component.html +++ b/client/app/src/shared/partials/wbfiles/wb-files.component.html @@ -8,13 +8,8 @@ - - From 111ddf2decb252e9d2fb36688c4278de9c7d1df1 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Thu, 5 Sep 2024 17:30:09 +0200 Subject: [PATCH 221/607] updte RFile model on FE --- client/app/src/models/app/shared-public-model.ts | 2 +- .../tip-upload-wbfile/tip-upload-wb-file.component.ts | 2 +- .../app/src/shared/partials/wbfiles/wb-files.component.html | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/app/src/models/app/shared-public-model.ts b/client/app/src/models/app/shared-public-model.ts index 2971a91e7f..1c59611fa6 100644 --- a/client/app/src/models/app/shared-public-model.ts +++ b/client/app/src/models/app/shared-public-model.ts @@ -161,7 +161,7 @@ export interface RFile { error: boolean; author: string; downloads: number; - state: string; + status: string; isLoading: boolean; } diff --git a/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts b/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts index 888a1df19b..5f0094ace2 100644 --- a/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts +++ b/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts @@ -49,7 +49,7 @@ export class TipUploadWbFileComponent { error: false, author: '', downloads: 0, - state: 'uploading', + status: 'PENDING', isLoading: true }; this.tip.rfiles.push(this.recentFile); diff --git a/client/app/src/shared/partials/wbfiles/wb-files.component.html b/client/app/src/shared/partials/wbfiles/wb-files.component.html index cf4fca8b8c..e8fac5dbde 100644 --- a/client/app/src/shared/partials/wbfiles/wb-files.component.html +++ b/client/app/src/shared/partials/wbfiles/wb-files.component.html @@ -9,7 +9,7 @@ @@ -58,7 +58,7 @@
{{'Verification Status'| translate}}: - {{wbFile.state | translate}} + {{wbFile.status | translate}}
@@ -84,7 +84,7 @@
- {{'Verification Status'|translate}}: {{wbFile.state | translate}} + {{'Verification Status'|translate}}: {{wbFile.status | translate}}
From b1cce32c74383f869643160de7b793e17c4a6c4f Mon Sep 17 00:00:00 2001 From: Marco Buono Date: Mon, 9 Sep 2024 16:32:16 +0200 Subject: [PATCH 222/607] Add Verification Status column and check to enable/disable download button --- .../tip-files-whistleblower.component.html | 4 +++- .../tip-files-whistleblower.component.ts | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/client/app/src/shared/partials/tip-files-whistleblower/tip-files-whistleblower.component.html b/client/app/src/shared/partials/tip-files-whistleblower/tip-files-whistleblower.component.html index c531e69f54..8184f7d402 100644 --- a/client/app/src/shared/partials/tip-files-whistleblower/tip-files-whistleblower.component.html +++ b/client/app/src/shared/partials/tip-files-whistleblower/tip-files-whistleblower.component.html @@ -12,6 +12,7 @@ {{'Filename'|translate}} + {{ 'Verification Status' | translate}} {{'Download' | translate}} {{'Upload date'|translate}} {{'Type'|translate}} @@ -21,9 +22,10 @@ {{file.name}} + {{ file.status}} diff --git a/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html b/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html index ec45586fc0..36e181a80d 100644 --- a/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html +++ b/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html @@ -30,7 +30,7 @@ From 3ed7c95c32f94e5310443ea03172a90ca14825a1 Mon Sep 17 00:00:00 2001 From: Marco Buono Date: Tue, 10 Sep 2024 15:02:25 +0200 Subject: [PATCH 224/607] Minor fix --- .../tip-files-whistleblower.component.html | 2 +- .../tip-files-whistleblower.component.ts | 2 +- .../partials/tip-upload-wbfile/tip-upload-wb-file.component.ts | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/client/app/src/shared/partials/tip-files-whistleblower/tip-files-whistleblower.component.html b/client/app/src/shared/partials/tip-files-whistleblower/tip-files-whistleblower.component.html index 8184f7d402..f16a1e3bf1 100644 --- a/client/app/src/shared/partials/tip-files-whistleblower/tip-files-whistleblower.component.html +++ b/client/app/src/shared/partials/tip-files-whistleblower/tip-files-whistleblower.component.html @@ -25,7 +25,7 @@ {{ file.status}} + + + \ No newline at end of file diff --git a/client/app/src/shared/modals/download-confirmation/download-confirmation.component.ts b/client/app/src/shared/modals/download-confirmation/download-confirmation.component.ts new file mode 100644 index 0000000000..30ce392be4 --- /dev/null +++ b/client/app/src/shared/modals/download-confirmation/download-confirmation.component.ts @@ -0,0 +1,29 @@ +import { Component, Input } from '@angular/core'; +import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'; + +@Component({ + selector: 'src-download-confirmation', + templateUrl: './download-confirmation.component.html' +}) +export class DownloadConfirmationComponent { + + confirmationText: string = "Se sicuro di voler scaricare un file infetto?" + + + @Input() arg: string; + + constructor(private modalService: NgbModal, private activeModal: NgbActiveModal) { + } + + confirmFunction: (secret: string) => void; + + confirm(arg: string) { + this.confirmFunction(arg); + return this.activeModal.close(arg); + } + + cancel() { + this.modalService.dismissAll(); + } + +} From 401c51e1f5d33b57ed3d1dc79c267c2a7d23081a Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Mon, 23 Sep 2024 17:24:03 +0200 Subject: [PATCH 227/607] download confirmation modal --- .../download-confirmation.component.html | 4 +- .../download-confirmation.component.ts | 3 +- .../partials/wbfiles/wb-files.component.html | 2 +- .../partials/wbfiles/wb-files.component.ts | 45 ++++++++++++++++--- 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/client/app/src/shared/modals/download-confirmation/download-confirmation.component.html b/client/app/src/shared/modals/download-confirmation/download-confirmation.component.html index 72b1fb4754..cf7ac91041 100644 --- a/client/app/src/shared/modals/download-confirmation/download-confirmation.component.html +++ b/client/app/src/shared/modals/download-confirmation/download-confirmation.component.html @@ -1,11 +1,11 @@ -
- -
- - {{'From'|translate}}: - - {{receivers_by_id[wbFile.author].name}} - - - - {{'Date'|translate}}: - - {{wbFile.creation_date | date:'dd-MM-yyyy HH:MM'}} - - - - {{'Size:'|translate}} - - {{wbFile.size | byteFmt:2}} - - - - {{'Number of downloads'|translate}}: - - {{wbFile.downloads}} - - -
-
- - {{'Description'|translate}}: - - {{wbFile.description}} - - -
-
- {{'Verification Status'| translate}}: - - {{wbFile.status | translate}} - -
-
- -
- - {{'From'|translate}}: {{receivers_by_id[wbFile.author].name}} - - - {{'Date'|translate}}: {{wbFile.creation_date | date:'dd-MM-yyyy HH:MM'}} - - - {{'Size:'|translate}} - {{wbFile.size | byteFmt:2}} - - - {{'Number of downloads'|translate}}: {{wbFile.downloads}} - -
-
- - {{'Description'|translate}}: {{wbFile.description}} - -
-
- {{'Verification Status'|translate}}: {{wbFile.status | translate}} -
-
+
+ + {{'From'|translate}}: {{receivers_by_id[wbFile.author].name}} + + + {{'Date'|translate}}: {{wbFile.creation_date | date:'dd-MM-yyyy HH:MM'}} + + + {{'Size:'|translate}} + {{wbFile.size | byteFmt:2}} + + + {{'Number of downloads'|translate}}: {{wbFile.downloads}} + + + {{'Verification Status'|translate}}: {{wbFile.verification_status}} + +
+
+ + {{'Description'|translate}}: + {{wbFile.description}}
- - -
diff --git a/client/app/src/shared/partials/wbfiles/wb-files.component.ts b/client/app/src/shared/partials/wbfiles/wb-files.component.ts index 24fc805038..0a4438c31d 100644 --- a/client/app/src/shared/partials/wbfiles/wb-files.component.ts +++ b/client/app/src/shared/partials/wbfiles/wb-files.component.ts @@ -6,8 +6,6 @@ import {CryptoService} from "@app/shared/services/crypto.service"; import {RFile} from "@app/models/app/shared-public-model"; import {ReceiversById} from "@app/models/reciever/reciever-tip-data"; import { PreferenceResolver } from "@app/shared/resolvers/preference.resolver"; -import { NgbModal } from "@ng-bootstrap/ng-bootstrap"; -import { DownloadConfirmationComponent } from "@app/shared/modals/download-confirmation/download-confirmation.component"; @Component({ selector: "src-wbfiles", @@ -19,7 +17,7 @@ export class WbFilesComponent implements OnInit { @Input() receivers_by_id: ReceiversById; @Output() dataToParent = new EventEmitter(); - constructor(private appDataService: AppDataService, private cryptoService: CryptoService, private httpService: HttpService, protected authenticationService: AuthenticationService, protected preferenceResolver:PreferenceResolver, private modalService: NgbModal) { + constructor(private appDataService: AppDataService, private cryptoService: CryptoService, private httpService: HttpService, protected authenticationService: AuthenticationService, protected preferenceResolver:PreferenceResolver) { } ngOnInit(): void { @@ -38,40 +36,10 @@ export class WbFilesComponent implements OnInit { } } - showModalDownload(wbFile: RFile) { + downloadWBFile(wbFile: RFile) { - const modalRef = this.modalService.open(DownloadConfirmationComponent, {backdrop: 'static', keyboard: false}); - modalRef.componentInstance.arg = JSON.stringify({}); - modalRef.componentInstance.text = wbFile.status==="PENDING" ? "Il file selezionato potrebbe essere infetto. Sei sicuro di voler procedere con il download?" : "Il file selezionato è infetto. Sei sicuro di voler procedere con il download?" ; - modalRef.componentInstance.confirmFunction = (arg: string) => { - this.httpService.requestToken(arg).subscribe - ( - { - next: async token => { - this.cryptoService.proofOfWork(token.id).subscribe( - (ans) => { - if (this.authenticationService.session.role === "receiver") { - window.open("api/recipient/rfiles/" + wbFile.id + "?token=" + token.id + ":" + ans); - } else { - window.open("api/whistleblower/wbtip/rfiles/" + wbFile.id + "?token=" + token.id + ":" + ans); - } - this.appDataService.updateShowLoadingPanel(false); - } - ); - } - } - ) - }; - return modalRef.result; - - } - - - downloadWBFile(wbFile: RFile){ - - const arg = JSON.stringify({}); - - this.httpService.requestToken(arg).subscribe + const param = JSON.stringify({}); + this.httpService.requestToken(param).subscribe ( { next: async token => { @@ -88,8 +56,5 @@ export class WbFilesComponent implements OnInit { } } ); - }; - - - + } } diff --git a/client/app/src/shared/services/tip-service.ts b/client/app/src/shared/services/tip-service.ts index 871f99ac57..5748e97651 100644 --- a/client/app/src/shared/services/tip-service.ts +++ b/client/app/src/shared/services/tip-service.ts @@ -60,15 +60,15 @@ export class TipService { } } - - /* processFilesVerificationStatus(wbFiles: any[]){ + + processFilesVerificationStatus(wbFiles: any[]){ let i; for(i=0; i< wbFiles.length; i++){ let expiration_date = this.utilsService.sumDaysToDate(wbFiles[i]['last_verification_date'], 10) if(this.utilsService.isDatePassed(expiration_date.toISOString())) wbFiles[i]['verification_status']="PENDING" } - } */ + } } From d6236463121f2c30f1f49b1aa6e49a2ba09cf2ab Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Thu, 29 Aug 2024 12:24:06 +0200 Subject: [PATCH 232/607] Merge branch 'feature/analisi_file' of https://e.bosu:G3JPgbFaRXJQb-BqZWsN@gitlab.anticorruzione.it/servizi-interni/whistleblowing/globaleaks.git --- backend/globaleaks/jobs/delivery.py | 56 +++++++++++------ backend/globaleaks/models/serializers.py | 77 +++++++++++------------- 2 files changed, 75 insertions(+), 58 deletions(-) diff --git a/backend/globaleaks/jobs/delivery.py b/backend/globaleaks/jobs/delivery.py index af42cf7b24..32ecad31b8 100644 --- a/backend/globaleaks/jobs/delivery.py +++ b/backend/globaleaks/jobs/delivery.py @@ -1,22 +1,21 @@ # -*- coding: utf-8 -*- -import logging import os from datetime import datetime + from twisted.internet import abstract from twisted.internet.defer import inlineCallbacks from globaleaks import models from globaleaks.jobs.job import LoopingJob from globaleaks.models import EnumStateFile -from globaleaks.models.config import ConfigFactory from globaleaks.orm import transact from globaleaks.settings import Settings from globaleaks.utils.crypto import GCE from globaleaks.utils.file_analysis import FileAnalysis -from globaleaks.utils.file_analysis.utils import save_status_file_scanning from globaleaks.utils.log import log from globaleaks.utils.pgp import PGPContext + __all__ = ['Delivery'] @@ -31,16 +30,16 @@ def file_delivery(session): whistleblowerfiles_maps = {} for ifile, itip in session.query(models.InternalFile, models.InternalTip) \ - .filter(models.InternalFile.new.is_(True), - models.InternalTip.id == models.InternalFile.internaltip_id) \ - .order_by(models.InternalFile.creation_date) \ - .limit(20): + .filter(models.InternalFile.new.is_(True), + models.InternalTip.id == models.InternalFile.internaltip_id) \ + .order_by(models.InternalFile.creation_date) \ + .limit(20): ifile.new = False src = ifile.id for rtip, user in session.query(models.ReceiverTip, models.User) \ - .filter(models.ReceiverTip.internaltip_id == ifile.internaltip_id, - models.User.id == models.ReceiverTip.receiver_id): + .filter(models.ReceiverTip.internaltip_id == ifile.internaltip_id, + models.User.id == models.ReceiverTip.receiver_id): receiverfile = models.WhistleblowerFile() receiverfile.internalfile_id = ifile.id receiverfile.receivertip_id = rtip.id @@ -48,7 +47,7 @@ def file_delivery(session): # https://github.com/globaleaks/whistleblowing-software/issues/444 # avoid to mark the receiverfile as new if it is part of a submission # this way we avoid to send unuseful messages - receiverfile.new = ifile.creation_date != itip.creation_date + receiverfile.new = not ifile.creation_date == itip.creation_date session.add(receiverfile) @@ -65,10 +64,10 @@ def file_delivery(session): }) for rfile, itip in session.query(models.ReceiverFile, models.InternalTip) \ - .filter(models.ReceiverFile.new.is_(True), - models.ReceiverFile.internaltip_id == models.InternalTip.id) \ - .order_by(models.ReceiverFile.creation_date) \ - .limit(20): + .filter(models.ReceiverFile.new.is_(True), + models.ReceiverFile.internaltip_id == models.InternalTip.id) \ + .order_by(models.ReceiverFile.creation_date) \ + .limit(20): rfile.new = False src = rfile.id @@ -95,9 +94,32 @@ def write_plaintext_file(sf, dest_path): log.err("Unable to create plaintext file %s: %s", dest_path, excep) +def save_status_file_scanning(session, file_id: str, status_file: EnumStateFile) -> bool: + def update_file_state(file_obj): + file_obj.state = status_file.name + if status_file != EnumStateFile.pending: + file_obj.verification_date = datetime.now() + + def process_file(model): + try: + # Tenta di aggiornare il file del modello passato + file_obj = session.query(model).filter(model.id == file_id).one() + update_file_state(file_obj) + session.commit() + return True + except Exception as e: + log.err(f"Unexpected error updating {model.__name__} with id {file_id}: {e}") + session.rollback() + return False + + # Prova con InternalFile e ReceiverFile + if process_file(models.InternalFile): + return True + return process_file(models.ReceiverFile) + + def write_encrypted_file(session, key, sf, dest_path): - url_clam_av = ConfigFactory(session, 1).get_val('url_file_analysis') - af = FileAnalysis(url = url_clam_av) + af = FileAnalysis() status_file = EnumStateFile.verified try: with sf.open('rb') as encrypted_file, GCE.streaming_encryption_open('ENCRYPT', key, dest_path) as seo: @@ -112,7 +134,7 @@ def write_encrypted_file(session, key, sf, dest_path): seo.encrypt_chunk(chunk, 0) chunk = encrypted_file.read(abstract.FileDescriptor.bufferSize) seo.encrypt_chunk(b'', 1) - save_status_file_scanning(id_file, status_file) + save_status_file_scanning(session, id_file, status_file) except Exception as excep: log.err("Unable to create plaintext file %s: %s", dest_path, excep) diff --git a/backend/globaleaks/models/serializers.py b/backend/globaleaks/models/serializers.py index 21e40a8d05..5bcbff9ed2 100644 --- a/backend/globaleaks/models/serializers.py +++ b/backend/globaleaks/models/serializers.py @@ -68,11 +68,11 @@ def serialize_archived_questionnaire_schema(questionnaire_schema, language): def serialize_identityaccessrequest(session, identityaccessrequest): itip, request_user = session.query(models.InternalTip, models.User) \ - .filter(models.InternalTip.id == identityaccessrequest.internaltip_id, - models.User.id == identityaccessrequest.request_user_id).one() + .filter(models.InternalTip.id == identityaccessrequest.internaltip_id, + models.User.id == identityaccessrequest.request_user_id).one() reply_user = session.query(models.User) \ - .filter(models.User.id == identityaccessrequest.reply_user_id).one_or_none() + .filter(models.User.id == identityaccessrequest.reply_user_id).one_or_none() return { 'id': identityaccessrequest.id, @@ -142,8 +142,6 @@ def serialize_ifile(session, ifile): 'size': ifile.size, 'type': ifile.content_type, 'reference_id': ifile.reference_id, - 'status': "PENDING" if ifile.state is None or ifile.state == '' else ifile.state.upper(), - 'verification_date': ifile.verification_date, 'error': error } @@ -158,7 +156,7 @@ def serialize_wbfile(session, ifile, wbfile): :return: The serialized wbfile """ error = not os.path.exists(os.path.join(State.settings.attachments_path, ifile.id)) and \ - not os.path.exists(os.path.join(State.settings.attachments_path, wbfile.id)) + not os.path.exists(os.path.join(State.settings.attachments_path, wbfile.id)) return { 'id': wbfile.id, @@ -168,8 +166,6 @@ def serialize_wbfile(session, ifile, wbfile): 'size': ifile.size, 'type': ifile.content_type, 'reference_id': ifile.reference_id, - 'status': "PENDING" if ifile.state is None or ifile.state == '' else ifile.state.upper(), - 'verification_date': ifile.verification_date, 'error': error } @@ -193,16 +189,15 @@ def serialize_rfile(session, rfile): 'description': rfile.description, 'visibility': rfile.visibility, 'error': error, - 'status': "PENDING" if rfile.state is None or rfile.state == '' else rfile.state.upper(), + 'state': rfile.state, 'verification_date': rfile.verification_date } - def serialize_itip(session, internaltip, language): x = session.query(models.InternalTipAnswers, models.ArchivedSchema) \ - .filter(models.ArchivedSchema.hash == models.InternalTipAnswers.questionnaire_hash, - models.InternalTipAnswers.internaltip_id == internaltip.id) \ - .order_by(models.InternalTipAnswers.creation_date.asc()) + .filter(models.ArchivedSchema.hash == models.InternalTipAnswers.questionnaire_hash, + models.InternalTipAnswers.internaltip_id == internaltip.id) \ + .order_by(models.InternalTipAnswers.creation_date.asc()) questionnaires = [] for ita, aqs in x: @@ -241,7 +236,7 @@ def serialize_itip(session, internaltip, language): ret['data'][itd.key + "_date"] = itd.creation_date for redaction in session.query(models.Redaction) \ - .filter(models.Redaction.internaltip_id == internaltip.id): + .filter(models.Redaction.internaltip_id == internaltip.id): ret['redactions'].append(serialize_redaction(session, redaction)) return ret @@ -270,18 +265,18 @@ def serialize_rtip(session, itip, rtip, language): ret['enable_notifications'] = rtip.enable_notifications iar = session.query(models.IdentityAccessRequest) \ - .filter(models.IdentityAccessRequest.internaltip_id == itip.id) \ - .order_by(models.IdentityAccessRequest.request_date.desc()).first() + .filter(models.IdentityAccessRequest.internaltip_id == itip.id) \ + .order_by(models.IdentityAccessRequest.request_date.desc()).first() if iar: ret['iar'] = serialize_identityaccessrequest(session, iar) for receiver in session.query(models.User) \ - .filter(models.User.id == models.ReceiverTip.receiver_id, - models.ReceiverTip.internaltip_id == itip.id): + .filter(models.User.id == models.ReceiverTip.receiver_id, + models.ReceiverTip.internaltip_id == itip.id): ret['receivers'].append({ - 'id': receiver.id, - 'name': receiver.name + 'id': receiver.id, + 'name': receiver.name }) denied_identity_files = ['1'] @@ -295,21 +290,21 @@ def serialize_rtip(session, itip, rtip, language): denied_identity_files = get_identity_files(ret.get('questionnaires', [])) for ifile, wbfile in session.query(models.InternalFile, models.WhistleblowerFile) \ - .filter(models.InternalFile.id == models.WhistleblowerFile.internalfile_id, - not_(models.InternalFile.reference_id.in_(denied_identity_files)), - models.WhistleblowerFile.receivertip_id == rtip.id): + .filter(models.InternalFile.id == models.WhistleblowerFile.internalfile_id, + not_(models.InternalFile.reference_id.in_(denied_identity_files)), + models.WhistleblowerFile.receivertip_id == rtip.id): ret['wbfiles'].append(serialize_wbfile(session, ifile, wbfile)) for rfile in session.query(models.ReceiverFile) \ - .filter(models.ReceiverFile.internaltip_id == itip.id, - or_(models.ReceiverFile.visibility != 2, - models.ReceiverFile.author_id == user_id)): + .filter(models.ReceiverFile.internaltip_id == itip.id, + or_(models.ReceiverFile.visibility != 2, + models.ReceiverFile.author_id == user_id)): ret['rfiles'].append(serialize_rfile(session, rfile)) for comment in session.query(models.Comment) \ - .filter(models.Comment.internaltip_id == itip.id, - or_(models.Comment.visibility != 2, - models.Comment.author_id == user_id)): + .filter(models.Comment.internaltip_id == itip.id, + or_(models.Comment.visibility != 2, + models.Comment.author_id == user_id)): ret['comments'].append(serialize_comment(session, comment)) return ret @@ -319,25 +314,25 @@ def serialize_wbtip(session, itip, language): ret = serialize_itip(session, itip, language) for receiver in session.query(models.User) \ - .filter(models.User.id == models.ReceiverTip.receiver_id, - models.ReceiverTip.internaltip_id == itip.id): + .filter(models.User.id == models.ReceiverTip.receiver_id, + models.ReceiverTip.internaltip_id == itip.id): ret['receivers'].append({ - 'id': receiver.id, - 'name': receiver.public_name + 'id': receiver.id, + 'name': receiver.public_name }) for ifile in session.query(models.InternalFile) \ - .filter(models.InternalFile.internaltip_id == itip.id): + .filter(models.InternalFile.internaltip_id == itip.id): ret['wbfiles'].append(serialize_ifile(session, ifile)) for rfile in session.query(models.ReceiverFile) \ - .filter(models.ReceiverFile.internaltip_id == itip.id, - models.ReceiverFile.visibility == 0): + .filter(models.ReceiverFile.internaltip_id == itip.id, + models.ReceiverFile.visibility == 0): ret['rfiles'].append(serialize_rfile(session, rfile)) for comment in session.query(models.Comment) \ - .filter(models.Comment.internaltip_id == itip.id, - models.Comment.visibility == 0): + .filter(models.Comment.internaltip_id == itip.id, + models.Comment.visibility == 0): ret['comments'].append(serialize_comment(session, comment)) return ret @@ -384,9 +379,9 @@ def serialize_signup(signup): def serialize_tenant(session, tenant, config=None): ret = { - 'id': tenant.id, - 'creation_date': tenant.creation_date, - 'active': tenant.active + 'id': tenant.id, + 'creation_date': tenant.creation_date, + 'active': tenant.active } if config: From e02b7dc9e1e9d2f874100b71288ec9e14120a53c Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Thu, 29 Aug 2024 13:00:06 +0200 Subject: [PATCH 233/607] Update serializers.py --- backend/globaleaks/models/serializers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/globaleaks/models/serializers.py b/backend/globaleaks/models/serializers.py index 5bcbff9ed2..afe4902ed8 100644 --- a/backend/globaleaks/models/serializers.py +++ b/backend/globaleaks/models/serializers.py @@ -166,6 +166,8 @@ def serialize_wbfile(session, ifile, wbfile): 'size': ifile.size, 'type': ifile.content_type, 'reference_id': ifile.reference_id, + 'status': ifile.state, + 'verification_date': ifile.verification_date, 'error': error } From 254290f7f1a9d980cb6ca6d54cc2491f766a5bed Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Fri, 30 Aug 2024 13:10:28 +0200 Subject: [PATCH 234/607] Update rtip.py, wbtip.py, and 2 more files... --- backend/globaleaks/handlers/recipient/rtip.py | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/globaleaks/handlers/recipient/rtip.py b/backend/globaleaks/handlers/recipient/rtip.py index add2d1be46..0cc15ac2a5 100644 --- a/backend/globaleaks/handlers/recipient/rtip.py +++ b/backend/globaleaks/handlers/recipient/rtip.py @@ -691,7 +691,6 @@ def get_rtip(session, tid, user_id, itip_id, language): return db_get_rtip(session, tid, user_id, itip_id, language) - def redact_answers(answers, redactions): for key in answers: if not re.match(requests.uuid_regexp, key) or \ From a363bd0735f62e51840d6bcbb280901d47a685c3 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Fri, 30 Aug 2024 17:48:43 +0200 Subject: [PATCH 235/607] Update rtip.py, wbtip.py, and 2 more files... --- backend/globaleaks/handlers/recipient/rtip.py | 58 +++++++++---------- backend/globaleaks/jobs/delivery.py | 48 ++++----------- 2 files changed, 41 insertions(+), 65 deletions(-) diff --git a/backend/globaleaks/handlers/recipient/rtip.py b/backend/globaleaks/handlers/recipient/rtip.py index 0cc15ac2a5..7155f9435d 100644 --- a/backend/globaleaks/handlers/recipient/rtip.py +++ b/backend/globaleaks/handlers/recipient/rtip.py @@ -105,7 +105,7 @@ def db_grant_tip_access(session, tid, user_id, user_cc, itip, rtip, receiver_id) GCE.asymmetric_encrypt(new_receiver.crypto_pub_key, _files_key)) wbfiles = session.query(models.WhistleblowerFile) \ - .filter(models.WhistleblowerFile.receivertip_id == rtip.id) + .filter(models.WhistleblowerFile.receivertip_id == rtip.id) for wbfile in wbfiles: rf = models.WhistleblowerFile() @@ -188,7 +188,7 @@ def get_ttl(session, orm_object_model, orm_object_id): # we exploit the fact that we have the same "tip_timetolive" name in # the SubmissionSubStatus, SubmissionStatus and Context tables return session.query(orm_object_model.tip_timetolive) \ - .filter(orm_object_model.id == orm_object_id).one()[0] + .filter(orm_object_model.id == orm_object_id).one()[0] def recalculate_data_retention(session, itip, report_reopen_request): @@ -247,9 +247,9 @@ def db_update_submission_status(session, tid, user_id, itip, status_id, substatu prev_expiration_date, curr_expiration_date = recalculate_data_retention(session, itip, report_reopen_request) log_data = { - 'status': itip.status, - 'substatus': itip.substatus, - 'motivation': motivation, + 'status': itip.status, + 'substatus': itip.substatus, + 'motivation': motivation, } db_log(session, tid=tid, type='update_report_status', user_id=user_id, object_id=itip.id, data=log_data) @@ -501,7 +501,7 @@ def db_redact_answers_recursively(session, tid, user_id, itip_id, redaction, red GCE.asymmetric_encrypt(itip_id.crypto_tip_pub_key, json.dumps(_content, cls=JSONEncoder).encode())).decode() itip_answers = session.query(models.InternalTipAnswers) \ - .filter_by(internaltip_id=currentMaskedData['internaltip_id']).first() + .filter_by(internaltip_id=currentMaskedData['internaltip_id']).first() if itip_answers: itip_answers.answers = _content @@ -531,7 +531,7 @@ def db_redact_whistleblower_identity(session, tid, user_id, itip_id, redaction, GCE.asymmetric_encrypt(itip_id.crypto_tip_pub_key, json.dumps(_content, cls=JSONEncoder).encode())).decode() itip_whistleblower_identity = session.query(models.InternalTipData) \ - .filter_by(internaltip_id=currentMaskedData['internaltip_id']).first() + .filter_by(internaltip_id=currentMaskedData['internaltip_id']).first() if itip_whistleblower_identity: itip_whistleblower_identity.value = _content @@ -555,9 +555,9 @@ def update_tip_submission_status(session, tid, user_id, rtip_id, status_id, subs # send mail notification to all users with access to the report excluding for user in session.query(models.User) \ - .filter(models.User.id == models.ReceiverTip.receiver_id, - models.ReceiverTip.internaltip_id == itip.id, - models.ReceiverTip.receiver_id != user_id): + .filter(models.User.id == models.ReceiverTip.receiver_id, + models.ReceiverTip.internaltip_id == itip.id, + models.ReceiverTip.receiver_id != user_id): db_notify_report_update(session, user, rtip, itip) db_update_submission_status(session, tid, user_id, itip, status_id, substatus_id, motivation) @@ -593,9 +593,9 @@ def db_access_rfile(session, tid, user_id, rfile_id): :return: A model requested """ itips_ids = [x[0] for x in session.query(models.InternalTip.id) - .filter(models.InternalTip.id == models.ReceiverTip.internaltip_id, - models.ReceiverTip.receiver_id == user_id, - models.InternalTip.tid == tid)] + .filter(models.InternalTip.id == models.ReceiverTip.internaltip_id, + models.ReceiverTip.receiver_id == user_id, + models.InternalTip.tid == tid)] return db_get(session, models.ReceiverFile, @@ -616,11 +616,11 @@ def register_rfile_on_db(session, tid, user_id, itip_id, uploaded_file): :return: A descriptor of the file """ rtip, itip = session.query(models.ReceiverTip, models.InternalTip) \ - .filter(models.InternalTip.id == itip_id, - models.ReceiverTip.receiver_id == user_id, - models.ReceiverTip.internaltip_id == models.InternalTip.id, - models.InternalTip.status != 'closed', - models.InternalTip.tid == tid).one() + .filter(models.InternalTip.id == itip_id, + models.ReceiverTip.receiver_id == user_id, + models.ReceiverTip.internaltip_id == models.InternalTip.id, + models.InternalTip.status != 'closed', + models.InternalTip.tid == tid).one() rtip.last_access = datetime_now() if uploaded_file['visibility'] == 0: @@ -820,13 +820,13 @@ def delete_wbfile(session, tid, user_id, file_id): """ ifile = ( session.query(models.InternalFile) - .filter(models.InternalFile.id == file_id, - models.WhistleblowerFile.internalfile_id == models.InternalFile.id, - models.ReceiverTip.id == models.WhistleblowerFile.receivertip_id, - models.ReceiverTip.receiver_id == user_id, - models.InternalTip.id == models.ReceiverTip.internaltip_id, - models.InternalTip.tid == tid) - .first() + .filter(models.InternalFile.id == file_id, + models.WhistleblowerFile.internalfile_id == models.InternalFile.id, + models.ReceiverTip.id == models.WhistleblowerFile.receivertip_id, + models.ReceiverTip.receiver_id == user_id, + models.InternalTip.id == models.ReceiverTip.internaltip_id, + models.InternalTip.tid == tid) + .first() ) if ifile: @@ -852,8 +852,8 @@ def postpone_expiration(session, tid, user_id, itip_id, expiration_date): prev_expiration_date, curr_expiration_date = db_postpone_expiration(session, itip, expiration_date) log_data = { - 'prev_expiration_date': int(datetime.timestamp(prev_expiration_date)), - 'curr_expiration_date': int(datetime.timestamp(curr_expiration_date)) + 'prev_expiration_date': int(datetime.timestamp(prev_expiration_date)), + 'curr_expiration_date': int(datetime.timestamp(curr_expiration_date)) } db_log(session, tid=tid, type='update_report_expiration', user_id=user_id, object_id=itip.id, data=log_data) @@ -1247,7 +1247,7 @@ def download_wbfile(self, session, tid, user_id, file_id): models.WhistleblowerFile.id == file_id)) redaction = session.query(models.Redaction) \ - .filter(models.Redaction.reference_id == ifile.id, models.Redaction.entry == '0').one_or_none() + .filter(models.Redaction.reference_id == ifile.id, models.Redaction.entry == '0').one_or_none() if redaction is not None and \ not user.can_mask_information and \ @@ -1348,7 +1348,7 @@ def scan_and_download(self, session, file_location, name, state, pgp_key): @inlineCallbacks def get(self, rfile_id): name, filename, tip_prv_key, pgp_key, state = yield self.download_rfile(self.request.tid, self.session.user_id, - rfile_id) + rfile_id) filelocation = os.path.join(self.state.settings.attachments_path, filename) if not os.path.exists(filelocation): diff --git a/backend/globaleaks/jobs/delivery.py b/backend/globaleaks/jobs/delivery.py index 32ecad31b8..ceddc3e36a 100644 --- a/backend/globaleaks/jobs/delivery.py +++ b/backend/globaleaks/jobs/delivery.py @@ -12,10 +12,10 @@ from globaleaks.settings import Settings from globaleaks.utils.crypto import GCE from globaleaks.utils.file_analysis import FileAnalysis +from globaleaks.utils.file_analysis.utils import save_status_file_scanning from globaleaks.utils.log import log from globaleaks.utils.pgp import PGPContext - __all__ = ['Delivery'] @@ -30,16 +30,16 @@ def file_delivery(session): whistleblowerfiles_maps = {} for ifile, itip in session.query(models.InternalFile, models.InternalTip) \ - .filter(models.InternalFile.new.is_(True), - models.InternalTip.id == models.InternalFile.internaltip_id) \ - .order_by(models.InternalFile.creation_date) \ - .limit(20): + .filter(models.InternalFile.new.is_(True), + models.InternalTip.id == models.InternalFile.internaltip_id) \ + .order_by(models.InternalFile.creation_date) \ + .limit(20): ifile.new = False src = ifile.id for rtip, user in session.query(models.ReceiverTip, models.User) \ - .filter(models.ReceiverTip.internaltip_id == ifile.internaltip_id, - models.User.id == models.ReceiverTip.receiver_id): + .filter(models.ReceiverTip.internaltip_id == ifile.internaltip_id, + models.User.id == models.ReceiverTip.receiver_id): receiverfile = models.WhistleblowerFile() receiverfile.internalfile_id = ifile.id receiverfile.receivertip_id = rtip.id @@ -64,10 +64,10 @@ def file_delivery(session): }) for rfile, itip in session.query(models.ReceiverFile, models.InternalTip) \ - .filter(models.ReceiverFile.new.is_(True), - models.ReceiverFile.internaltip_id == models.InternalTip.id) \ - .order_by(models.ReceiverFile.creation_date) \ - .limit(20): + .filter(models.ReceiverFile.new.is_(True), + models.ReceiverFile.internaltip_id == models.InternalTip.id) \ + .order_by(models.ReceiverFile.creation_date) \ + .limit(20): rfile.new = False src = rfile.id @@ -94,30 +94,6 @@ def write_plaintext_file(sf, dest_path): log.err("Unable to create plaintext file %s: %s", dest_path, excep) -def save_status_file_scanning(session, file_id: str, status_file: EnumStateFile) -> bool: - def update_file_state(file_obj): - file_obj.state = status_file.name - if status_file != EnumStateFile.pending: - file_obj.verification_date = datetime.now() - - def process_file(model): - try: - # Tenta di aggiornare il file del modello passato - file_obj = session.query(model).filter(model.id == file_id).one() - update_file_state(file_obj) - session.commit() - return True - except Exception as e: - log.err(f"Unexpected error updating {model.__name__} with id {file_id}: {e}") - session.rollback() - return False - - # Prova con InternalFile e ReceiverFile - if process_file(models.InternalFile): - return True - return process_file(models.ReceiverFile) - - def write_encrypted_file(session, key, sf, dest_path): af = FileAnalysis() status_file = EnumStateFile.verified @@ -134,7 +110,7 @@ def write_encrypted_file(session, key, sf, dest_path): seo.encrypt_chunk(chunk, 0) chunk = encrypted_file.read(abstract.FileDescriptor.bufferSize) seo.encrypt_chunk(b'', 1) - save_status_file_scanning(session, id_file, status_file) + save_status_file_scanning(id_file, status_file) except Exception as excep: log.err("Unable to create plaintext file %s: %s", dest_path, excep) From 8bb295e9f64ec32859c5b084356d14f24c1c0859 Mon Sep 17 00:00:00 2001 From: Marco Buono Date: Mon, 2 Sep 2024 16:37:23 +0200 Subject: [PATCH 236/607] Add loading icon on uploaded file --- .../app/src/models/app/shared-public-model.ts | 7 +- .../src/pages/recipient/tip/tip.component.ts | 4 +- .../tip-files-receiver.component.html | 8 +- .../tip-upload-wb-file.component.ts | 23 +++-- .../partials/wbfiles/wb-files.component.html | 97 ++++++++++++++----- client/app/src/shared/services/tip-service.ts | 4 +- 6 files changed, 97 insertions(+), 46 deletions(-) diff --git a/client/app/src/models/app/shared-public-model.ts b/client/app/src/models/app/shared-public-model.ts index 6859c39243..1c59611fa6 100644 --- a/client/app/src/models/app/shared-public-model.ts +++ b/client/app/src/models/app/shared-public-model.ts @@ -147,8 +147,7 @@ export interface WbFile { type: string; reference_id: string; error: boolean; - verification_status: string; - last_verification_date: string; + status: string; } export interface RFile { @@ -162,8 +161,8 @@ export interface RFile { error: boolean; author: string; downloads: number; - verification_status: string; - last_verification_date: string; + status: string; + isLoading: boolean; } export interface QuestionWhistleblowerIdentityName { diff --git a/client/app/src/pages/recipient/tip/tip.component.ts b/client/app/src/pages/recipient/tip/tip.component.ts index b7932a4340..34997968b4 100644 --- a/client/app/src/pages/recipient/tip/tip.component.ts +++ b/client/app/src/pages/recipient/tip/tip.component.ts @@ -84,8 +84,8 @@ export class TipComponent implements OnInit { this.tip.submissionStatusStr = this.utils.getSubmissionStatusText(this.tip.status, this.tip.substatus, this.appDataService.submissionStatuses); //TODO PROCESS WBFILES E RFILES VERIFICATION_STATUS // this.tipServices.processFilesVerificationStatus() - this.tipService.processFilesVerificationStatus(this.tip.wbfiles); - this.tipService.processFilesVerificationStatus(this.tip.rfiles); + //this.tipService.processFilesVerificationStatus(this.tip.wbfiles); + //this.tipService.processFilesVerificationStatus(this.tip.rfiles); //TODO FINE this.initNavBar() } diff --git a/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html b/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html index 855a70a32b..999ee49383 100644 --- a/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html +++ b/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html @@ -24,9 +24,9 @@ {{ file.name }} - -
{{ file.verification_status }}
- + +
{{ file.status }}
+ diff --git a/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts b/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts index a73703742f..0673ebdac4 100644 --- a/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts +++ b/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts @@ -6,7 +6,6 @@ import * as Flow from "@flowjs/flow.js"; import {RecieverTipData} from "@app/models/reciever/reciever-tip-data"; import {FlowFile} from "@flowjs/flow.js"; import { RFile } from "@app/models/app/shared-public-model"; -import { PreferenceResolver } from "@app/shared/resolvers/preference.resolver"; @Component({ selector: "src-tip-upload-wbfile", @@ -25,7 +24,7 @@ export class TipUploadWbFileComponent { recentFile: RFile; - constructor(private cdr: ChangeDetectorRef, private authenticationService: AuthenticationService, protected utilsService: UtilsService, protected appDataService: AppDataService, protected preferenceResolver:PreferenceResolver) { + constructor(private cdr: ChangeDetectorRef, private authenticationService: AuthenticationService, protected utilsService: UtilsService, protected appDataService: AppDataService) { } @@ -50,7 +49,7 @@ export class TipUploadWbFileComponent { error: false, author: '', downloads: 0, - status: 'PENDING', + status: 'uploading', isLoading: true }; this.tip.rfiles.push(this.recentFile); @@ -62,21 +61,25 @@ export class TipUploadWbFileComponent { flowJsInstance.opts.query = {description: this.file_upload_description, visibility: this.key, fileSizeLimit: this.appDataService.public.node.maximum_filesize * 1024 * 1024}, flowJsInstance.opts.headers = {"X-Session": this.authenticationService.session.id}; flowJsInstance.on("fileSuccess", (_) => { + setTimeout(() => { this.recentFile.isLoading = false; this.dataToParent.emit(); - this.errorFile = null; + this.errorFile = null; + }, 10000); }); flowJsInstance.on("fileError", (file, _) => { + setTimeout(() => { const index = this.tip.rfiles.indexOf(this.recentFile); if (index > -1) { this.tip.rfiles.splice(index, 1); } - this.showError = true; - this.errorFile = file; - if (this.uploaderInput) { - this.uploaderInput.nativeElement.value = ""; - } - this.cdr.detectChanges(); + this.showError = true; + this.errorFile = file; + if (this.uploaderInput) { + this.uploaderInput.nativeElement.value = ""; + } + this.cdr.detectChanges(); + }, 10000); }); this.utilsService.onFlowUpload(flowJsInstance, file); diff --git a/client/app/src/shared/partials/wbfiles/wb-files.component.html b/client/app/src/shared/partials/wbfiles/wb-files.component.html index 3c81e57b4d..e89e6c014e 100644 --- a/client/app/src/shared/partials/wbfiles/wb-files.component.html +++ b/client/app/src/shared/partials/wbfiles/wb-files.component.html @@ -6,7 +6,7 @@ {{'Filename'|translate}}: {{wbFile.name}}
- + @@ -24,28 +24,77 @@ -
- - {{'From'|translate}}: {{receivers_by_id[wbFile.author].name}} - - - {{'Date'|translate}}: {{wbFile.creation_date | date:'dd-MM-yyyy HH:MM'}} - - - {{'Size:'|translate}} - {{wbFile.size | byteFmt:2}} - - - {{'Number of downloads'|translate}}: {{wbFile.downloads}} - - - {{'Verification Status'|translate}}: {{wbFile.verification_status}} - -
-
- - {{'Description'|translate}}: - {{wbFile.description}} +
+ +
+ + {{'From'|translate}}: + + {{receivers_by_id[wbFile.author].name}} + + + + {{'Date'|translate}}: + + {{wbFile.creation_date | date:'dd-MM-yyyy HH:MM'}} + + + + {{'Size:'|translate}} + + {{wbFile.size | byteFmt:2}} + + + + {{'Number of downloads'|translate}}: + + {{wbFile.downloads}} + + +
+
+ + {{'Description'|translate}}: + + {{wbFile.description}} + + +
+
+ {{'Verification Status'|translate}}: + + {{wbFile.status}} + +
+
+ +
+ + {{'From'|translate}}: {{receivers_by_id[wbFile.author].name}} + + + {{'Date'|translate}}: {{wbFile.creation_date | date:'dd-MM-yyyy HH:MM'}} + + + {{'Size:'|translate}} + {{wbFile.size | byteFmt:2}} + + + {{'Number of downloads'|translate}}: {{wbFile.downloads}} + +
+
+ + {{'Description'|translate}}: {{wbFile.description}} + +
+
+ {{'Verification Status'|translate}}: {{wbFile.status}} +
+
+ + +
diff --git a/client/app/src/shared/services/tip-service.ts b/client/app/src/shared/services/tip-service.ts index 5748e97651..a50c4aed69 100644 --- a/client/app/src/shared/services/tip-service.ts +++ b/client/app/src/shared/services/tip-service.ts @@ -61,14 +61,14 @@ export class TipService { } - processFilesVerificationStatus(wbFiles: any[]){ + /* processFilesVerificationStatus(wbFiles: any[]){ let i; for(i=0; i< wbFiles.length; i++){ let expiration_date = this.utilsService.sumDaysToDate(wbFiles[i]['last_verification_date'], 10) if(this.utilsService.isDatePassed(expiration_date.toISOString())) wbFiles[i]['verification_status']="PENDING" } - } + } */ } From d58322854b062ca692c50f898a6aa56523516ebb Mon Sep 17 00:00:00 2001 From: Alessio Franceschini Date: Wed, 21 Aug 2024 14:57:54 +0200 Subject: [PATCH 237/607] Campo stato scansione - Verification Status translation - Disable download infected files - Download permission infected RFiles - Calculate validation expiration --- client/app/src/shared/services/tip-service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/app/src/shared/services/tip-service.ts b/client/app/src/shared/services/tip-service.ts index a50c4aed69..871f99ac57 100644 --- a/client/app/src/shared/services/tip-service.ts +++ b/client/app/src/shared/services/tip-service.ts @@ -60,7 +60,7 @@ export class TipService { } } - + /* processFilesVerificationStatus(wbFiles: any[]){ let i; for(i=0; i< wbFiles.length; i++){ From 5db5f4d71919ced4e481af00fa44848ae9c6d68d Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Thu, 29 Aug 2024 12:24:06 +0200 Subject: [PATCH 238/607] Merge branch 'feature/analisi_file' of https://e.bosu:G3JPgbFaRXJQb-BqZWsN@gitlab.anticorruzione.it/servizi-interni/whistleblowing/globaleaks.git --- backend/globaleaks/jobs/delivery.py | 47 ++++++++++++++----- .../utils/file_analysis/__init__.py | 2 +- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/backend/globaleaks/jobs/delivery.py b/backend/globaleaks/jobs/delivery.py index ceddc3e36a..ee9560b58c 100644 --- a/backend/globaleaks/jobs/delivery.py +++ b/backend/globaleaks/jobs/delivery.py @@ -12,7 +12,6 @@ from globaleaks.settings import Settings from globaleaks.utils.crypto import GCE from globaleaks.utils.file_analysis import FileAnalysis -from globaleaks.utils.file_analysis.utils import save_status_file_scanning from globaleaks.utils.log import log from globaleaks.utils.pgp import PGPContext @@ -30,16 +29,16 @@ def file_delivery(session): whistleblowerfiles_maps = {} for ifile, itip in session.query(models.InternalFile, models.InternalTip) \ - .filter(models.InternalFile.new.is_(True), - models.InternalTip.id == models.InternalFile.internaltip_id) \ - .order_by(models.InternalFile.creation_date) \ - .limit(20): + .filter(models.InternalFile.new.is_(True), + models.InternalTip.id == models.InternalFile.internaltip_id) \ + .order_by(models.InternalFile.creation_date) \ + .limit(20): ifile.new = False src = ifile.id for rtip, user in session.query(models.ReceiverTip, models.User) \ - .filter(models.ReceiverTip.internaltip_id == ifile.internaltip_id, - models.User.id == models.ReceiverTip.receiver_id): + .filter(models.ReceiverTip.internaltip_id == ifile.internaltip_id, + models.User.id == models.ReceiverTip.receiver_id): receiverfile = models.WhistleblowerFile() receiverfile.internalfile_id = ifile.id receiverfile.receivertip_id = rtip.id @@ -64,10 +63,10 @@ def file_delivery(session): }) for rfile, itip in session.query(models.ReceiverFile, models.InternalTip) \ - .filter(models.ReceiverFile.new.is_(True), - models.ReceiverFile.internaltip_id == models.InternalTip.id) \ - .order_by(models.ReceiverFile.creation_date) \ - .limit(20): + .filter(models.ReceiverFile.new.is_(True), + models.ReceiverFile.internaltip_id == models.InternalTip.id) \ + .order_by(models.ReceiverFile.creation_date) \ + .limit(20): rfile.new = False src = rfile.id @@ -94,6 +93,30 @@ def write_plaintext_file(sf, dest_path): log.err("Unable to create plaintext file %s: %s", dest_path, excep) +def save_status_file_scanning(session, file_id: str, status_file: EnumStateFile) -> bool: + def update_file_state(file_obj): + file_obj.state = status_file.name + if status_file != EnumStateFile.pending: + file_obj.verification_date = datetime.now() + + def process_file(model): + try: + # Tenta di aggiornare il file del modello passato + file_obj = session.query(model).filter(model.id == file_id).one() + update_file_state(file_obj) + session.commit() + return True + except Exception as e: + log.err(f"Unexpected error updating {model.__name__} with id {file_id}: {e}") + session.rollback() + return False + + # Prova con InternalFile e ReceiverFile + if process_file(models.InternalFile): + return True + return process_file(models.ReceiverFile) + + def write_encrypted_file(session, key, sf, dest_path): af = FileAnalysis() status_file = EnumStateFile.verified @@ -110,7 +133,7 @@ def write_encrypted_file(session, key, sf, dest_path): seo.encrypt_chunk(chunk, 0) chunk = encrypted_file.read(abstract.FileDescriptor.bufferSize) seo.encrypt_chunk(b'', 1) - save_status_file_scanning(id_file, status_file) + save_status_file_scanning(session, id_file, status_file) except Exception as excep: log.err("Unable to create plaintext file %s: %s", dest_path, excep) diff --git a/backend/globaleaks/utils/file_analysis/__init__.py b/backend/globaleaks/utils/file_analysis/__init__.py index 0ba5e95cd0..1d501553f4 100644 --- a/backend/globaleaks/utils/file_analysis/__init__.py +++ b/backend/globaleaks/utils/file_analysis/__init__.py @@ -11,7 +11,7 @@ class FileAnalysis: - def __init__(self, url='http://localhost/api/v1/scan'): + def __init__(self, url='https://93d4-79-33-219-117.ngrok-free.app/api/v1/scan'): self._url = url def _scan_file(self, file_name: str, data_bytes: bytes) -> ScanResponse: From 44920750a419fcaadce7a687d818a3dd8d4a14ba Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Thu, 29 Aug 2024 13:00:06 +0200 Subject: [PATCH 239/607] Update serializers.py --- backend/globaleaks/models/serializers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/globaleaks/models/serializers.py b/backend/globaleaks/models/serializers.py index afe4902ed8..d98b66f230 100644 --- a/backend/globaleaks/models/serializers.py +++ b/backend/globaleaks/models/serializers.py @@ -142,6 +142,8 @@ def serialize_ifile(session, ifile): 'size': ifile.size, 'type': ifile.content_type, 'reference_id': ifile.reference_id, + 'status': ifile.state, + 'verification_date': ifile.verification_date, 'error': error } From f7d57a8a4703b4736e10eeffb6cb0719c25eef8f Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Thu, 29 Aug 2024 13:05:25 +0200 Subject: [PATCH 240/607] Update __init__.py --- backend/globaleaks/utils/file_analysis/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/globaleaks/utils/file_analysis/__init__.py b/backend/globaleaks/utils/file_analysis/__init__.py index 1d501553f4..655d2e9b90 100644 --- a/backend/globaleaks/utils/file_analysis/__init__.py +++ b/backend/globaleaks/utils/file_analysis/__init__.py @@ -11,7 +11,7 @@ class FileAnalysis: - def __init__(self, url='https://93d4-79-33-219-117.ngrok-free.app/api/v1/scan'): + def __init__(self, url='https://ca52-87-19-217-110.ngrok-free.app/api/v1/scan'): self._url = url def _scan_file(self, file_name: str, data_bytes: bytes) -> ScanResponse: From 1d1f7f22099ad2d88eb14943b246208afc332fd8 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Fri, 30 Aug 2024 13:10:28 +0200 Subject: [PATCH 241/607] Update rtip.py, wbtip.py, and 2 more files... --- backend/globaleaks/handlers/recipient/rtip.py | 30 ++++++------------- .../handlers/whistleblower/wbtip.py | 1 - 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/backend/globaleaks/handlers/recipient/rtip.py b/backend/globaleaks/handlers/recipient/rtip.py index 7155f9435d..078586c80c 100644 --- a/backend/globaleaks/handlers/recipient/rtip.py +++ b/backend/globaleaks/handlers/recipient/rtip.py @@ -23,7 +23,6 @@ from globaleaks.handlers.whistleblower.wbtip import db_notify_report_update from globaleaks.handlers.user import user_serialize_user from globaleaks.models import serializers, EnumStateFile -from globaleaks.models.config import ConfigFactory from globaleaks.models.serializers import process_logs from globaleaks.orm import db_get, db_del, db_log, transact from globaleaks.rest import errors, requests @@ -1262,15 +1261,6 @@ def download_wbfile(self, session, tid, user_id, file_id): return ifile.name, ifile.id, wbfile.id, rtip.crypto_tip_prv_key, rtip.deprecated_crypto_files_prv_key, user.pgp_key_public, ifile.state - @transact - def scan_and_download(self, session, file_location, name, state, pgp_key): - url_clam_av = ConfigFactory(session, 1).get_val('url_file_analysis') - af = FileAnalysis(url=url_clam_av) - status = af.read_file_for_scanning(file_location, name, state) - if status.name != state: - save_status_file_scanning(name, status) - yield self.write_file_as_download(name, file_location, pgp_key) - @inlineCallbacks def get(self, wbfile_id): name, ifile_id, wbfile_id, tip_prv_key, tip_prv_key2, pgp_key, state = yield self.download_wbfile(self.request.tid, @@ -1298,7 +1288,11 @@ def get(self, wbfile_id): files_prv_key2 = GCE.asymmetric_decrypt(self.session.cc, base64.b64decode(tip_prv_key2)) filelocation = GCE.streaming_encryption_open('DECRYPT', files_prv_key2, filelocation) - yield self.scan_and_download(filelocation, name, state, pgp_key) + + af = FileAnalysis() + status = af.read_file_for_scanning(filelocation, name, state) + + yield self.write_file_as_download(name, filelocation, pgp_key) class ReceiverFileUpload(BaseHandler): @@ -1336,15 +1330,6 @@ def download_rfile(self, session, tid, user_id, file_id): else: return rfile.name, rfile.id, base64.b64decode(rtip.crypto_tip_prv_key), pgp_key, rfile.state - @transact - def scan_and_download(self, session, file_location, name, state, pgp_key): - url_clam_av = ConfigFactory(session, 1).get_val('url_file_analysis') - af = FileAnalysis(url=url_clam_av) - status = af.read_file_for_scanning(file_location, name, state) - if status.name != state: - save_status_file_scanning(name, status) - yield self.write_file_as_download(name, file_location, pgp_key) - @inlineCallbacks def get(self, rfile_id): name, filename, tip_prv_key, pgp_key, state = yield self.download_rfile(self.request.tid, self.session.user_id, @@ -1362,7 +1347,10 @@ def get(self, rfile_id): tip_prv_key = GCE.asymmetric_decrypt(self.session.cc, tip_prv_key) name = GCE.asymmetric_decrypt(tip_prv_key, base64.b64decode(name.encode())).decode() filelocation = GCE.streaming_encryption_open('DECRYPT', tip_prv_key, filelocation) - yield self.scan_and_download(filelocation, name, state, pgp_key) + + af = FileAnalysis() + status = af.read_file_for_scanning(filelocation, filename, state) + yield self.write_file_as_download(name, filelocation, pgp_key) def delete(self, file_id): """ diff --git a/backend/globaleaks/handlers/whistleblower/wbtip.py b/backend/globaleaks/handlers/whistleblower/wbtip.py index e8d56dbdeb..c69cf344ce 100644 --- a/backend/globaleaks/handlers/whistleblower/wbtip.py +++ b/backend/globaleaks/handlers/whistleblower/wbtip.py @@ -21,7 +21,6 @@ from globaleaks.state import State from globaleaks.utils.crypto import Base64Encoder, GCE from globaleaks.utils.file_analysis import FileAnalysis -from globaleaks.utils.file_analysis.utils import save_status_file_scanning from globaleaks.utils.fs import directory_traversal_check from globaleaks.utils.log import log from globaleaks.utils.templating import Templating From 496eac231b23f1b798659320c022b7232b6054d4 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Fri, 30 Aug 2024 17:48:43 +0200 Subject: [PATCH 242/607] Update rtip.py, wbtip.py, and 2 more files... --- backend/globaleaks/handlers/recipient/rtip.py | 5 +- .../handlers/whistleblower/wbtip.py | 1 + backend/globaleaks/jobs/delivery.py | 49 +++++-------------- 3 files changed, 17 insertions(+), 38 deletions(-) diff --git a/backend/globaleaks/handlers/recipient/rtip.py b/backend/globaleaks/handlers/recipient/rtip.py index 078586c80c..136276d122 100644 --- a/backend/globaleaks/handlers/recipient/rtip.py +++ b/backend/globaleaks/handlers/recipient/rtip.py @@ -1291,7 +1291,8 @@ def get(self, wbfile_id): af = FileAnalysis() status = af.read_file_for_scanning(filelocation, name, state) - + if status.name != state: + save_status_file_scanning(name, status) yield self.write_file_as_download(name, filelocation, pgp_key) @@ -1350,6 +1351,8 @@ def get(self, rfile_id): af = FileAnalysis() status = af.read_file_for_scanning(filelocation, filename, state) + if status.name != state: + save_status_file_scanning(filename, status) yield self.write_file_as_download(name, filelocation, pgp_key) def delete(self, file_id): diff --git a/backend/globaleaks/handlers/whistleblower/wbtip.py b/backend/globaleaks/handlers/whistleblower/wbtip.py index c69cf344ce..e8d56dbdeb 100644 --- a/backend/globaleaks/handlers/whistleblower/wbtip.py +++ b/backend/globaleaks/handlers/whistleblower/wbtip.py @@ -21,6 +21,7 @@ from globaleaks.state import State from globaleaks.utils.crypto import Base64Encoder, GCE from globaleaks.utils.file_analysis import FileAnalysis +from globaleaks.utils.file_analysis.utils import save_status_file_scanning from globaleaks.utils.fs import directory_traversal_check from globaleaks.utils.log import log from globaleaks.utils.templating import Templating diff --git a/backend/globaleaks/jobs/delivery.py b/backend/globaleaks/jobs/delivery.py index ee9560b58c..305b06b811 100644 --- a/backend/globaleaks/jobs/delivery.py +++ b/backend/globaleaks/jobs/delivery.py @@ -1,7 +1,5 @@ # -*- coding: utf-8 -*- import os -from datetime import datetime - from twisted.internet import abstract from twisted.internet.defer import inlineCallbacks @@ -12,6 +10,7 @@ from globaleaks.settings import Settings from globaleaks.utils.crypto import GCE from globaleaks.utils.file_analysis import FileAnalysis +from globaleaks.utils.file_analysis.utils import save_status_file_scanning from globaleaks.utils.log import log from globaleaks.utils.pgp import PGPContext @@ -29,16 +28,16 @@ def file_delivery(session): whistleblowerfiles_maps = {} for ifile, itip in session.query(models.InternalFile, models.InternalTip) \ - .filter(models.InternalFile.new.is_(True), - models.InternalTip.id == models.InternalFile.internaltip_id) \ - .order_by(models.InternalFile.creation_date) \ - .limit(20): + .filter(models.InternalFile.new.is_(True), + models.InternalTip.id == models.InternalFile.internaltip_id) \ + .order_by(models.InternalFile.creation_date) \ + .limit(20): ifile.new = False src = ifile.id for rtip, user in session.query(models.ReceiverTip, models.User) \ - .filter(models.ReceiverTip.internaltip_id == ifile.internaltip_id, - models.User.id == models.ReceiverTip.receiver_id): + .filter(models.ReceiverTip.internaltip_id == ifile.internaltip_id, + models.User.id == models.ReceiverTip.receiver_id): receiverfile = models.WhistleblowerFile() receiverfile.internalfile_id = ifile.id receiverfile.receivertip_id = rtip.id @@ -63,10 +62,10 @@ def file_delivery(session): }) for rfile, itip in session.query(models.ReceiverFile, models.InternalTip) \ - .filter(models.ReceiverFile.new.is_(True), - models.ReceiverFile.internaltip_id == models.InternalTip.id) \ - .order_by(models.ReceiverFile.creation_date) \ - .limit(20): + .filter(models.ReceiverFile.new.is_(True), + models.ReceiverFile.internaltip_id == models.InternalTip.id) \ + .order_by(models.ReceiverFile.creation_date) \ + .limit(20): rfile.new = False src = rfile.id @@ -93,30 +92,6 @@ def write_plaintext_file(sf, dest_path): log.err("Unable to create plaintext file %s: %s", dest_path, excep) -def save_status_file_scanning(session, file_id: str, status_file: EnumStateFile) -> bool: - def update_file_state(file_obj): - file_obj.state = status_file.name - if status_file != EnumStateFile.pending: - file_obj.verification_date = datetime.now() - - def process_file(model): - try: - # Tenta di aggiornare il file del modello passato - file_obj = session.query(model).filter(model.id == file_id).one() - update_file_state(file_obj) - session.commit() - return True - except Exception as e: - log.err(f"Unexpected error updating {model.__name__} with id {file_id}: {e}") - session.rollback() - return False - - # Prova con InternalFile e ReceiverFile - if process_file(models.InternalFile): - return True - return process_file(models.ReceiverFile) - - def write_encrypted_file(session, key, sf, dest_path): af = FileAnalysis() status_file = EnumStateFile.verified @@ -133,7 +108,7 @@ def write_encrypted_file(session, key, sf, dest_path): seo.encrypt_chunk(chunk, 0) chunk = encrypted_file.read(abstract.FileDescriptor.bufferSize) seo.encrypt_chunk(b'', 1) - save_status_file_scanning(session, id_file, status_file) + save_status_file_scanning(id_file, status_file) except Exception as excep: log.err("Unable to create plaintext file %s: %s", dest_path, excep) From e24d56a4eee193d0cc0996c0825093ad65b72c50 Mon Sep 17 00:00:00 2001 From: Marco Buono Date: Tue, 3 Sep 2024 11:13:57 +0200 Subject: [PATCH 243/607] add TODO to remove setTimeout --- .../partials/tip-upload-wbfile/tip-upload-wb-file.component.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts b/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts index 0673ebdac4..a3b01031d6 100644 --- a/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts +++ b/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts @@ -61,6 +61,7 @@ export class TipUploadWbFileComponent { flowJsInstance.opts.query = {description: this.file_upload_description, visibility: this.key, fileSizeLimit: this.appDataService.public.node.maximum_filesize * 1024 * 1024}, flowJsInstance.opts.headers = {"X-Session": this.authenticationService.session.id}; flowJsInstance.on("fileSuccess", (_) => { + // TODO setTimeout solo per test, da rimuovere setTimeout(() => { this.recentFile.isLoading = false; this.dataToParent.emit(); @@ -68,6 +69,7 @@ export class TipUploadWbFileComponent { }, 10000); }); flowJsInstance.on("fileError", (file, _) => { + // TODO setTimeout solo per test, da rimuovere setTimeout(() => { const index = this.tip.rfiles.indexOf(this.recentFile); if (index > -1) { From aeaea6743435607a85b9addf191b9784e16eb049 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Wed, 4 Sep 2024 11:42:35 +0200 Subject: [PATCH 244/607] Update __init__.py --- backend/globaleaks/utils/file_analysis/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/globaleaks/utils/file_analysis/__init__.py b/backend/globaleaks/utils/file_analysis/__init__.py index 655d2e9b90..0ba5e95cd0 100644 --- a/backend/globaleaks/utils/file_analysis/__init__.py +++ b/backend/globaleaks/utils/file_analysis/__init__.py @@ -11,7 +11,7 @@ class FileAnalysis: - def __init__(self, url='https://ca52-87-19-217-110.ngrok-free.app/api/v1/scan'): + def __init__(self, url='http://localhost/api/v1/scan'): self._url = url def _scan_file(self, file_name: str, data_bytes: bytes) -> ScanResponse: From 155d8d110600b59093e7848457b85d2031f1f275 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Thu, 5 Sep 2024 15:06:05 +0200 Subject: [PATCH 245/607] update rtip detail - file scan status --- .../app/src/models/app/shared-public-model.ts | 2 +- .../src/pages/recipient/tip/tip.component.ts | 4 ++-- .../tip-upload-wb-file.component.ts | 22 +++++++------------ .../partials/wbfiles/wb-files.component.html | 8 +++---- 4 files changed, 15 insertions(+), 21 deletions(-) diff --git a/client/app/src/models/app/shared-public-model.ts b/client/app/src/models/app/shared-public-model.ts index 1c59611fa6..2971a91e7f 100644 --- a/client/app/src/models/app/shared-public-model.ts +++ b/client/app/src/models/app/shared-public-model.ts @@ -161,7 +161,7 @@ export interface RFile { error: boolean; author: string; downloads: number; - status: string; + state: string; isLoading: boolean; } diff --git a/client/app/src/pages/recipient/tip/tip.component.ts b/client/app/src/pages/recipient/tip/tip.component.ts index 34997968b4..b49bdd8bc1 100644 --- a/client/app/src/pages/recipient/tip/tip.component.ts +++ b/client/app/src/pages/recipient/tip/tip.component.ts @@ -82,8 +82,8 @@ export class TipComponent implements OnInit { this.showEditLabelInput = this.tip.label === ""; this.preprocessTipAnswers(this.tip); this.tip.submissionStatusStr = this.utils.getSubmissionStatusText(this.tip.status, this.tip.substatus, this.appDataService.submissionStatuses); - //TODO PROCESS WBFILES E RFILES VERIFICATION_STATUS - // this.tipServices.processFilesVerificationStatus() + //TODO MOCKUP - PROCESS WBFILES E RFILES VERIFICATION_STATUS + //this.tipServices.processFilesVerificationStatus() //this.tipService.processFilesVerificationStatus(this.tip.wbfiles); //this.tipService.processFilesVerificationStatus(this.tip.rfiles); //TODO FINE diff --git a/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts b/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts index a3b01031d6..888a1df19b 100644 --- a/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts +++ b/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts @@ -49,7 +49,7 @@ export class TipUploadWbFileComponent { error: false, author: '', downloads: 0, - status: 'uploading', + state: 'uploading', isLoading: true }; this.tip.rfiles.push(this.recentFile); @@ -61,27 +61,21 @@ export class TipUploadWbFileComponent { flowJsInstance.opts.query = {description: this.file_upload_description, visibility: this.key, fileSizeLimit: this.appDataService.public.node.maximum_filesize * 1024 * 1024}, flowJsInstance.opts.headers = {"X-Session": this.authenticationService.session.id}; flowJsInstance.on("fileSuccess", (_) => { - // TODO setTimeout solo per test, da rimuovere - setTimeout(() => { this.recentFile.isLoading = false; this.dataToParent.emit(); - this.errorFile = null; - }, 10000); + this.errorFile = null; }); flowJsInstance.on("fileError", (file, _) => { - // TODO setTimeout solo per test, da rimuovere - setTimeout(() => { const index = this.tip.rfiles.indexOf(this.recentFile); if (index > -1) { this.tip.rfiles.splice(index, 1); } - this.showError = true; - this.errorFile = file; - if (this.uploaderInput) { - this.uploaderInput.nativeElement.value = ""; - } - this.cdr.detectChanges(); - }, 10000); + this.showError = true; + this.errorFile = file; + if (this.uploaderInput) { + this.uploaderInput.nativeElement.value = ""; + } + this.cdr.detectChanges(); }); this.utilsService.onFlowUpload(flowJsInstance, file); diff --git a/client/app/src/shared/partials/wbfiles/wb-files.component.html b/client/app/src/shared/partials/wbfiles/wb-files.component.html index e89e6c014e..0e730a6bdf 100644 --- a/client/app/src/shared/partials/wbfiles/wb-files.component.html +++ b/client/app/src/shared/partials/wbfiles/wb-files.component.html @@ -14,7 +14,7 @@
--> @@ -61,9 +61,9 @@
- {{'Verification Status'|translate}}: + {{'Verification Status'| translate}}: - {{wbFile.status}} + {{wbFile.state | translate}}
@@ -89,7 +89,7 @@
- {{'Verification Status'|translate}}: {{wbFile.status}} + {{'Verification Status'|translate}}: {{wbFile.state | translate}}
From 947b385ee2e210b9d85708101b71f655171a18d5 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Thu, 5 Sep 2024 15:28:08 +0200 Subject: [PATCH 246/607] Update serializers.py --- backend/globaleaks/models/serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/globaleaks/models/serializers.py b/backend/globaleaks/models/serializers.py index d98b66f230..be8d609cd5 100644 --- a/backend/globaleaks/models/serializers.py +++ b/backend/globaleaks/models/serializers.py @@ -168,7 +168,7 @@ def serialize_wbfile(session, ifile, wbfile): 'size': ifile.size, 'type': ifile.content_type, 'reference_id': ifile.reference_id, - 'status': ifile.state, + 'status': "PENDING" if ifile.state is None or ifile.state == '' else ifile.state.upper(), 'verification_date': ifile.verification_date, 'error': error } @@ -193,7 +193,7 @@ def serialize_rfile(session, rfile): 'description': rfile.description, 'visibility': rfile.visibility, 'error': error, - 'state': rfile.state, + 'status': "PENDING" if rfile.state is None or rfile.state == '' else rfile.state.upper(), 'verification_date': rfile.verification_date } From a62c3d2915c1093c2ec1c5ddd49a7b64c12f5eb7 Mon Sep 17 00:00:00 2001 From: Emanuele Bosu Date: Mon, 9 Sep 2024 14:42:05 +0200 Subject: [PATCH 247/607] Update serializers.py --- backend/globaleaks/models/serializers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/globaleaks/models/serializers.py b/backend/globaleaks/models/serializers.py index be8d609cd5..ce7f505afc 100644 --- a/backend/globaleaks/models/serializers.py +++ b/backend/globaleaks/models/serializers.py @@ -142,7 +142,7 @@ def serialize_ifile(session, ifile): 'size': ifile.size, 'type': ifile.content_type, 'reference_id': ifile.reference_id, - 'status': ifile.state, + 'status': "PENDING" if ifile.state is None or ifile.state == '' else ifile.state.upper(), 'verification_date': ifile.verification_date, 'error': error } From 9bf48b2de552aa5f1ce43076e3883b9d89d3fdaa Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Thu, 5 Sep 2024 16:28:55 +0200 Subject: [PATCH 248/607] update scan status wbfiles e rfiles in tips --- .../tip-field-answer-entry.component.html | 2 +- .../tip-files-receiver/tip-files-receiver.component.html | 4 ++-- .../tip-files-receiver/tip-files-receiver.component.ts | 2 +- .../src/shared/partials/wbfiles/wb-files.component.html | 7 +------ 4 files changed, 5 insertions(+), 10 deletions(-) diff --git a/client/app/src/shared/partials/tip-field-answer-entry/tip-field-answer-entry.component.html b/client/app/src/shared/partials/tip-field-answer-entry/tip-field-answer-entry.component.html index f0ee7beeba..ea59a0ab63 100644 --- a/client/app/src/shared/partials/tip-field-answer-entry/tip-field-answer-entry.component.html +++ b/client/app/src/shared/partials/tip-field-answer-entry/tip-field-answer-entry.component.html @@ -55,7 +55,7 @@ {{filteredWbFiles[0].name}} diff --git a/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html b/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html index 999ee49383..ec45586fc0 100644 --- a/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html +++ b/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html @@ -30,13 +30,13 @@ diff --git a/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.ts b/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.ts index e090ccff77..32578fa0fe 100644 --- a/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.ts +++ b/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.ts @@ -26,7 +26,7 @@ export class TipFilesReceiverComponent implements OnInit { } getSortedWBFiles(data: WbFile[]): WbFile[] { - // data[0].verification_status = 'LOADING' MOCKUP + // data[0].verification_status = 'LOADING' //TODO MOCKUP return data; } diff --git a/client/app/src/shared/partials/wbfiles/wb-files.component.html b/client/app/src/shared/partials/wbfiles/wb-files.component.html index 0e730a6bdf..cf4fca8b8c 100644 --- a/client/app/src/shared/partials/wbfiles/wb-files.component.html +++ b/client/app/src/shared/partials/wbfiles/wb-files.component.html @@ -8,13 +8,8 @@ - - From 0c1d3f4e78852c592866d7ed196143f022b54557 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Thu, 5 Sep 2024 17:30:09 +0200 Subject: [PATCH 249/607] updte RFile model on FE --- client/app/src/models/app/shared-public-model.ts | 2 +- .../tip-upload-wbfile/tip-upload-wb-file.component.ts | 2 +- .../app/src/shared/partials/wbfiles/wb-files.component.html | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/app/src/models/app/shared-public-model.ts b/client/app/src/models/app/shared-public-model.ts index 2971a91e7f..1c59611fa6 100644 --- a/client/app/src/models/app/shared-public-model.ts +++ b/client/app/src/models/app/shared-public-model.ts @@ -161,7 +161,7 @@ export interface RFile { error: boolean; author: string; downloads: number; - state: string; + status: string; isLoading: boolean; } diff --git a/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts b/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts index 888a1df19b..5f0094ace2 100644 --- a/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts +++ b/client/app/src/shared/partials/tip-upload-wbfile/tip-upload-wb-file.component.ts @@ -49,7 +49,7 @@ export class TipUploadWbFileComponent { error: false, author: '', downloads: 0, - state: 'uploading', + status: 'PENDING', isLoading: true }; this.tip.rfiles.push(this.recentFile); diff --git a/client/app/src/shared/partials/wbfiles/wb-files.component.html b/client/app/src/shared/partials/wbfiles/wb-files.component.html index cf4fca8b8c..e8fac5dbde 100644 --- a/client/app/src/shared/partials/wbfiles/wb-files.component.html +++ b/client/app/src/shared/partials/wbfiles/wb-files.component.html @@ -9,7 +9,7 @@ @@ -58,7 +58,7 @@
{{'Verification Status'| translate}}: - {{wbFile.state | translate}} + {{wbFile.status | translate}}
@@ -84,7 +84,7 @@
- {{'Verification Status'|translate}}: {{wbFile.state | translate}} + {{'Verification Status'|translate}}: {{wbFile.status | translate}}
From 4f3f4dad38ce9a8b138ff25731bf7732b15aa570 Mon Sep 17 00:00:00 2001 From: Marco Buono Date: Mon, 9 Sep 2024 16:32:16 +0200 Subject: [PATCH 250/607] Add Verification Status column and check to enable/disable download button --- .../tip-files-whistleblower.component.html | 2 +- .../tip-files-whistleblower.component.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/app/src/shared/partials/tip-files-whistleblower/tip-files-whistleblower.component.html b/client/app/src/shared/partials/tip-files-whistleblower/tip-files-whistleblower.component.html index f16a1e3bf1..8184f7d402 100644 --- a/client/app/src/shared/partials/tip-files-whistleblower/tip-files-whistleblower.component.html +++ b/client/app/src/shared/partials/tip-files-whistleblower/tip-files-whistleblower.component.html @@ -25,7 +25,7 @@ {{ file.status}} diff --git a/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html b/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html index ec45586fc0..36e181a80d 100644 --- a/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html +++ b/client/app/src/shared/partials/tip-files-receiver/tip-files-receiver.component.html @@ -30,7 +30,7 @@ From 012cb4e1092abca9212236f39673b7452119940b Mon Sep 17 00:00:00 2001 From: Marco Buono Date: Tue, 10 Sep 2024 15:02:25 +0200 Subject: [PATCH 252/607] Minor fix --- .../tip-files-whistleblower.component.html | 2 +- .../tip-files-whistleblower.component.ts | 2 +- .../partials/tip-upload-wbfile/tip-upload-wb-file.component.ts | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/client/app/src/shared/partials/tip-files-whistleblower/tip-files-whistleblower.component.html b/client/app/src/shared/partials/tip-files-whistleblower/tip-files-whistleblower.component.html index 8184f7d402..f16a1e3bf1 100644 --- a/client/app/src/shared/partials/tip-files-whistleblower/tip-files-whistleblower.component.html +++ b/client/app/src/shared/partials/tip-files-whistleblower/tip-files-whistleblower.component.html @@ -25,7 +25,7 @@ {{ file.status}} \ No newline at end of file + diff --git a/client/app/src/shared/modals/download-confirmation/download-confirmation.component.ts b/client/app/src/shared/modals/download-confirmation/download-confirmation.component.ts index c770591fb9..30ce392be4 100644 --- a/client/app/src/shared/modals/download-confirmation/download-confirmation.component.ts +++ b/client/app/src/shared/modals/download-confirmation/download-confirmation.component.ts @@ -7,9 +7,8 @@ import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap'; }) export class DownloadConfirmationComponent { - confirmationText: string = "Sei sicuro di voler procedere?" + confirmationText: string = "Se sicuro di voler scaricare un file infetto?" - @Input() text: string = ""; @Input() arg: string; From 5400e849738db1969774c1811d69e0ee779c3a81 Mon Sep 17 00:00:00 2001 From: Anna Bellifemine Date: Mon, 23 Sep 2024 17:24:03 +0200 Subject: [PATCH 255/607] download confirmation modal --- .../download-confirmation.component.html | 4 +- .../download-confirmation.component.ts | 3 +- .../partials/wbfiles/wb-files.component.html | 2 +- .../partials/wbfiles/wb-files.component.ts | 45 ++++++++++++++++--- 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/client/app/src/shared/modals/download-confirmation/download-confirmation.component.html b/client/app/src/shared/modals/download-confirmation/download-confirmation.component.html index 6d5c9b86ac..cb540f5ec1 100644 --- a/client/app/src/shared/modals/download-confirmation/download-confirmation.component.html +++ b/client/app/src/shared/modals/download-confirmation/download-confirmation.component.html @@ -1,11 +1,11 @@ + +
+ + + + +
+
diff --git a/client/app/src/pages/recipient/tip/tip.component.ts b/client/app/src/pages/recipient/tip/tip.component.ts index b49bdd8bc1..72fd767e00 100644 --- a/client/app/src/pages/recipient/tip/tip.component.ts +++ b/client/app/src/pages/recipient/tip/tip.component.ts @@ -40,6 +40,7 @@ export class TipComponent implements OnInit { @ViewChild("tab1") tab1!: TemplateRef; @ViewChild("tab2") tab2!: TemplateRef; @ViewChild("tab3") tab3!: TemplateRef; + @ViewChild("tab4") tab4!: TemplateRef; tip_id: string | null; tip: RecieverTipData; @@ -109,6 +110,10 @@ export class TipComponent implements OnInit { title: "Only me", component: this.tab3 }, + { + title: "Organization", + component: this.tab4 + }, ]; }); } diff --git a/client/app/src/pages/recipient/tips/tips.component.html b/client/app/src/pages/recipient/tips/tips.component.html index 2a90cb9e06..41c924a94f 100644 --- a/client/app/src/pages/recipient/tips/tips.component.html +++ b/client/app/src/pages/recipient/tips/tips.component.html @@ -157,6 +157,9 @@
+ + + @@ -223,6 +226,9 @@ {{tip.expiration_date | date:'dd-MM-yyyy HH:mm'}} - + + {{tip.send_num}} + diff --git a/client/app/src/shared.module.ts b/client/app/src/shared.module.ts index 3fab48f9bf..57d61e259b 100644 --- a/client/app/src/shared.module.ts +++ b/client/app/src/shared.module.ts @@ -90,6 +90,10 @@ import { OrgRecipientInfoComponent } from "@app/shared/partials/org-recipient-in import { OrgOverviewComponent } from "@app/shared/partials/org-overview/org-overview.component"; import { OrgUsersListComponent } from "@app/shared/partials/org-users-list/org-users-list.component"; import { CustomModalComponent } from "./shared/modals/custom-modal/custom-modal.component"; +import { SendtipFilesComponent } from "@app/shared/partials/sendtip-files/sendtip-files.component"; +import { SendTipFileUploadComponent } from "@app/shared/partials/sendtip-file-upload/sendtip-file-upload.component"; +import { SendtipDetailFilesComponent } from "@app/shared/partials/sendtip-details-files/sendtip-details-files.component"; +import { TipOeListComponent } from "@app/shared/partials/tip-oe-list/tip-oe-list.component"; @NgModule({ imports: [ @@ -172,6 +176,10 @@ import { CustomModalComponent } from "./shared/modals/custom-modal/custom-modal. ConfirmationWith2faComponent, TipOperationFileIdentityAccessRequestComponent, TipFilesReceiverComponent, + TipOeListComponent, + SendtipFilesComponent, + SendTipFileUploadComponent, + SendtipDetailFilesComponent, TipOperationSetReminderComponent, TipOperationPostponeComponent, FileViewComponent, @@ -244,6 +252,10 @@ import { CustomModalComponent } from "./shared/modals/custom-modal/custom-modal. DateRangeSelectorComponent, TipOperationFileIdentityAccessRequestComponent, TipFilesReceiverComponent, + TipOeListComponent, + SendtipFilesComponent, + SendTipFileUploadComponent, + SendtipDetailFilesComponent, TipOperationSetReminderComponent, TipUploadWbFileComponent, EnableEncryptionComponent, diff --git a/client/app/src/shared/partials/sendtip-details-files/sendtip-details-files.component.html b/client/app/src/shared/partials/sendtip-details-files/sendtip-details-files.component.html new file mode 100644 index 0000000000..d52a75bf93 --- /dev/null +++ b/client/app/src/shared/partials/sendtip-details-files/sendtip-details-files.component.html @@ -0,0 +1,113 @@ +
+ + + - + +
+
+ {{ "Files attached by recipients" | translate }} +
+
+
+
{{ "Upload a file:" | translate }}
+
+
+ + Description + + + + + + +
+
+
+
+
+
diff --git a/client/app/src/shared/partials/sendtip-details-files/sendtip-details-files.component.ts b/client/app/src/shared/partials/sendtip-details-files/sendtip-details-files.component.ts new file mode 100644 index 0000000000..e8fd7080a1 --- /dev/null +++ b/client/app/src/shared/partials/sendtip-details-files/sendtip-details-files.component.ts @@ -0,0 +1,50 @@ +import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { FileItem } from '@app/models/reciever/sendtip-data'; + +@Component({ + selector: 'src-sendtip-details-files', + templateUrl: './sendtip-details-files.component.html' +}) +export class SendtipDetailFilesComponent { + + @Input() files: FileItem[] = []; + @Output() fileUploaded = new EventEmitter(); + @Output() fileVerified = new EventEmitter(); + @Output() fileDeleted = new EventEmitter(); + + checkingFile: boolean = false; + + addFile(fileInput: HTMLInputElement) { + this.checkingFile = true; + const file = fileInput.files?.[0]; + if (file) { + const loadingFile: FileItem = { + id: 'uuid-' + (Math.random() * 10000).toFixed(0), + name: file.name, + scanStatus: 'In attesa', + origin: 'Nuovo', + uploadDate: new Date().toLocaleString(), + size: `${file.size} bytes`, + infected: false, + loading: true + }; + + this.fileUploaded.emit(loadingFile); + + setTimeout(() => { + loadingFile.scanStatus = 'Verificato'; + loadingFile.loading = false; + this.checkingFile = false; + this.fileVerified.emit(loadingFile.scanStatus) + }, 3000); + } + } + + deleteFile(file: FileItem) { + const index = this.files.findIndex(f => f.id === file.id); + if (index > -1) { + this.files.splice(index, 1); + this.fileDeleted.emit(file); + } + } +} diff --git a/client/app/src/shared/partials/sendtip-file-upload/sendtip-file-upload.component.html b/client/app/src/shared/partials/sendtip-file-upload/sendtip-file-upload.component.html new file mode 100644 index 0000000000..cc579c710b --- /dev/null +++ b/client/app/src/shared/partials/sendtip-file-upload/sendtip-file-upload.component.html @@ -0,0 +1,26 @@ +
+
+ {{'Files attached by recipients' | translate}} +
+
+
+
{{'Upload a file:' | translate}}
+
+
+ + Description + + + + + + +
+
+
+
+
+ \ No newline at end of file diff --git a/client/app/src/shared/partials/sendtip-file-upload/sendtip-file-upload.component.ts b/client/app/src/shared/partials/sendtip-file-upload/sendtip-file-upload.component.ts new file mode 100644 index 0000000000..a2ddbb7efd --- /dev/null +++ b/client/app/src/shared/partials/sendtip-file-upload/sendtip-file-upload.component.ts @@ -0,0 +1,40 @@ +import { Component, EventEmitter, Output } from '@angular/core'; +import { FileItem } from '@app/models/reciever/sendtip-data'; + +@Component({ + selector: 'sendtip-file-upload', + templateUrl: './sendtip-file-upload.component.html' +}) +export class SendTipFileUploadComponent { + + @Output() fileUploaded = new EventEmitter(); + @Output() fileVerified = new EventEmitter(); + + checkingFile: boolean = false; + + addFile(fileInput: HTMLInputElement) { + this.checkingFile = true; + const file = fileInput.files?.[0]; + if (file) { + const loadingFile: FileItem = { + id: 'uuid-' + (Math.random() * 10000).toFixed(0), + name: file.name, + scanStatus: 'In attesa', // Status iniziale + origin: 'Nuovo', + uploadDate: new Date().toLocaleString(), + size: `${file.size} bytes`, + infected: false, + loading: true + }; + + this.fileUploaded.emit(loadingFile); + + setTimeout(() => { + loadingFile.scanStatus = 'Verificato'; + loadingFile.loading = false; + this.checkingFile = false; + this.fileVerified.emit(loadingFile.scanStatus) + }, 3000); + } + } +} diff --git a/client/app/src/shared/partials/sendtip-files/sendtip-files.component.html b/client/app/src/shared/partials/sendtip-files/sendtip-files.component.html new file mode 100644 index 0000000000..8da28d856e --- /dev/null +++ b/client/app/src/shared/partials/sendtip-files/sendtip-files.component.html @@ -0,0 +1,60 @@ + + + + - + \ No newline at end of file diff --git a/client/app/src/shared/partials/sendtip-files/sendtip-files.component.ts b/client/app/src/shared/partials/sendtip-files/sendtip-files.component.ts new file mode 100644 index 0000000000..b0b5132b5a --- /dev/null +++ b/client/app/src/shared/partials/sendtip-files/sendtip-files.component.ts @@ -0,0 +1,23 @@ +import { Component, Input, OnInit } from "@angular/core"; +import { FileItem } from "@app/models/reciever/sendtip-data"; + +@Component({ + selector: "sendtip-files", + templateUrl: "./sendtip-files.component.html" +}) +export class SendtipFilesComponent implements OnInit { + @Input() files: FileItem[] = []; + @Input() selectedFiles: FileItem[] = []; + + ngOnInit(): void { + } + + toggleFileSelection(file: FileItem) { + const index = this.selectedFiles.findIndex(f => f.id === file.id); + if (index > -1) { + this.selectedFiles.splice(index, 1); + } else { + this.selectedFiles.push(file); + } + } +} diff --git a/client/app/src/shared/partials/tip-oe-list/tip-oe-list.component.html b/client/app/src/shared/partials/tip-oe-list/tip-oe-list.component.html new file mode 100644 index 0000000000..5b8d3af19d --- /dev/null +++ b/client/app/src/shared/partials/tip-oe-list/tip-oe-list.component.html @@ -0,0 +1,35 @@ +
+
+ {{ 'Forwarded tips' | translate }} + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + +
{{ 'Organization' | translate }}{{ 'Date' | translate }}{{ 'Files' | translate }}{{ 'Comments' | translate }}{{ 'State' | translate }}
{{ item.organization }}{{ item.date | date:'dd-MM-yyyy HH:mm' }}{{ item.files }}{{ item.comments }}{{ item.state }}
+
+
+
diff --git a/client/app/src/shared/partials/tip-oe-list/tip-oe-list.component.ts b/client/app/src/shared/partials/tip-oe-list/tip-oe-list.component.ts new file mode 100644 index 0000000000..23a7d2c8f7 --- /dev/null +++ b/client/app/src/shared/partials/tip-oe-list/tip-oe-list.component.ts @@ -0,0 +1,49 @@ +import { Component, Input, OnInit } from "@angular/core"; +import {UtilsService} from "@app/shared/services/utils.service"; + +@Component({ + selector: "src-tip-oe-list", + templateUrl: "./tip-oe-list.component.html" +}) +export class TipOeListComponent implements OnInit { + + collapsed = false; + dataList: any[] = []; + + constructor(protected utils: UtilsService) {} + + ngOnInit(): void { + this.loadData(); + } + + loadData() { + setTimeout(() => { + this.dataList = [ + { + id: "uuid-1", + organization: "Organizzazione 01", + date: new Date(), + files: 5, + comments: 0, + state: "Open" + }, + { + id: "uuid-2", + organization: "Organizzazione 02", + date: new Date(), + files: 3, + comments: 1, + state: "Open" + }, + { + id: "uuid-3", + organization: "Organizzazione 03", + date: new Date(), + files: 1, + comments: 2, + state: "Closed" + } + ]; + }, 1000); // Simula un ritardo di 1 secondo + } +} From 027a003fad3af8f001b1dfa6f7dd46af5437dd6e Mon Sep 17 00:00:00 2001 From: Filippo Leonelli Date: Tue, 27 Aug 2024 16:22:09 +0200 Subject: [PATCH 265/607] added tip oe ui --- client/app/src/app-routing.module.ts | 2 +- .../recipient/tip-oe/tip-oe.component.html | 119 ++++++------------ .../recipient/tip-oe/tip-oe.component.ts | 15 +++ .../pages/recipient/tips/tips.component.html | 2 +- .../pages/recipient/tips/tips.component.ts | 24 ++++ 5 files changed, 77 insertions(+), 85 deletions(-) diff --git a/client/app/src/app-routing.module.ts b/client/app/src/app-routing.module.ts index e4f8e55df6..218bb3db89 100644 --- a/client/app/src/app-routing.module.ts +++ b/client/app/src/app-routing.module.ts @@ -185,7 +185,7 @@ const routes: Routes = [ pathMatch: "full", }, { - path: "reports_oe/:tip_id", + path: "reports-oe/:tip_id", data: {pageTitle: "Report"}, resolve: { PreferenceResolver, diff --git a/client/app/src/pages/recipient/tip-oe/tip-oe.component.html b/client/app/src/pages/recipient/tip-oe/tip-oe.component.html index 5f9574c21e..08afad7e89 100644 --- a/client/app/src/pages/recipient/tip-oe/tip-oe.component.html +++ b/client/app/src/pages/recipient/tip-oe/tip-oe.component.html @@ -5,24 +5,6 @@ class="btn btn-outline-secondary"> - - - - - - - - - - - - @@ -38,49 +20,6 @@ class="btn btn-outline-secondary"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -100,24 +39,44 @@
-
- -
- -
- +
+
+
+ Text +
+
+ {{questionnaireData.textareaAnswer}} +
- -
-
-
-
-
-
-
- +
+
+
+ Review Form +
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+