Commit 843429e9 authored by Florent Poittevin's avatar Florent Poittevin
Browse files

feat(download): [DLCM-1743] apply download token for all authenticated download

parent fe090f11
......@@ -6,7 +6,9 @@ This project was generated with [Angular CLI](https://github.com/angular/angular
- Install [NodeJS LTS](https://nodejs.org/en/) : Use ALWAYS the "LTS" version.
- Install [Angular CLI](https://angular.io/cli) : version 10.0.4
- For user of Jetbrains IDE (IntelliJ, WebStorm), go to `File > Settings > Appaearance & Behavior > System Settings` and disable the option `Use "safe write" (save changes to a temporary file first)`.
- For user of Jetbrains IDE (IntelliJ, WebStorm), go to `File > Settings > Appaearance & Behavior > System Settings` and disable the
option `Use "safe write" (save changes to a temporary file first)`.
- To test download token in local, add the following config on dlcm backend `dlcm.security.downloadToken.cookie.secure=false`
### Apache
......@@ -19,7 +21,9 @@ For Linux, first install apache and then activate the following modules using a2
a2enmod headers
a2enmod rewrite
```
Add the following lines at the end of your apache2.conf:
```
<IfModule proxy_module>
ProxyPass "/dlcm-authorization/shiblogin" "http://localhost:16100/dlcm/shiblogin"
......@@ -51,34 +55,33 @@ N.B: Check and adapt accordingly your urls in case your context path is differen
## Development server
For a dev server, run command `npm start`. Navigate to `http://localhost:4200/`.
For a dev server, run command `npm start`. Navigate to `http://localhost:4200/`.
If you use config server in docker you need to change the ports used :
- in your environment.local.ts defined the property : `admin: "http://localhost:16105/dlcm/admin",`
- in the proxy.conf.local.js add the following lines :
`
...proxyConfVariable.oauth(16110),
...proxyConfVariable.shiblogin(16110),
`
The app will automatically reload if you change any of the source files.
This command run a proxy to contact backend services.
`
...proxyConfVariable.oauth(16110), ...proxyConfVariable.shiblogin(16110),
`
The app will automatically reload if you change any of the source files. This command run a proxy to contact backend services.
## Build
Run `npm run build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Generate client api
## Generate client api
If you want to update the DLCM Api :
- Pull the master branch of the project DLCM-Backend in a folder named `DLCM-Backend` at the same level of the frontend folder
- On the project DLCM-Backend run `mvn clean package`
- On this project run `npm run generate-models`
## Generate element
Run `ng generate component component-name` to generate a new component.
Run `ng generate component component-name` to generate a new component.
You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
......@@ -106,8 +109,7 @@ To get more help on the Angular CLI use `ng help` or go check out the [Angular C
## Grid
There is no grid used in the project.
We use pure [flex box](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) css rule instead.
There is no grid used in the project. We use pure [flex box](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) css rule instead.
If you want to manage specific css behavior depending of screen size, you should use the following mixins :
......@@ -122,17 +124,21 @@ If you want to manage specific css behavior depending of screen size, you should
## Lexical
### Types of components :
#### Concept
With solidify, we introduce a new convention of component :
- Routables : Component routable. Can communicate with the store (dispatch actions and retrieve datas from store)
- Containers : Component non routable. Can communicate with the store (dispatch actions and retrieve datas from store)
- Presentationals : Dumb component (just input / output, no connection with store)
- Dialogs : Component embedded in a material modal. Can communicate with the store (dispatch actions and retrieve datas from store)
#### Generation
You can use solidify schematics project to generate this types of component.
Please refer to the `README.md` of the project `Solidify-Frontend`.
As reminder, the shortcut to generate this new type of components are :
Please refer to the `README.md` of the project `Solidify-Frontend`. As reminder, the shortcut to generate this new type of components are :
ng g solidify-frontend:routable my-routable
ng g solidify-frontend:routable
......@@ -148,15 +154,16 @@ As reminder, the shortcut to generate this new type of components are :
ng g solidify-frontend:store-relation-2-tiers
ng g solidify-frontend:store-composition my-store-composition
ng g solidify-frontend:store-composition
#### Cypress E2E Tests
For now e2e test can be executed ba running the script :
npm run cypress
or by running the script :
npm run cypress-open
the first command will run simultaneously the portal and cypress console, the second command will just run the cypress console.
......@@ -32,9 +32,7 @@
"polyfills": "src/polyfills.ts",
"tsConfig": "src/tsconfig.app.json",
"assets": [
"src/mitm.html",
"src/ngsw-worker-custom.js",
"src/ngsw-worker-download.js",
{
"glob": "**/*",
"input": "./node_modules/jsmol/src",
......@@ -66,8 +64,7 @@
]
},
"scripts": [
"node_modules/blobjs/Blob.min.js",
"node_modules/streamsaver/StreamSaver.js"
"node_modules/blobjs/Blob.min.js"
],
"vendorChunk": true,
"extractLicenses": false,
......
......@@ -16232,9 +16232,9 @@
}
},
"solidify-frontend": {
"version": "2.1.3",
"resolved": "https://nexus.unige.ch/repository/npm-all/solidify-frontend/-/solidify-frontend-2.1.3.tgz",
"integrity": "sha512-nBrqL18XaZL5apuC7nc+VE03/DIN/IhMSmwLC5FyRdGQTykcnzm9amG75NPnVr+HtzRRg4Izwd3KD0OWG99oGA==",
"version": "2.2.0",
"resolved": "https://nexus.unige.ch/repository/npm-all/solidify-frontend/-/solidify-frontend-2.2.0.tgz",
"integrity": "sha512-Evew6wuXkHFQBLGGAPqnXE3sCbRYkOLyJBoxYukU/FwQz98riQ/P9o48bHlvAYDMIb6kkBfE6zG0PMB2x0sL+w==",
"requires": {
"tslib": "^2.3.0"
}
......@@ -16627,11 +16627,6 @@
}
}
},
"streamsaver": {
"version": "2.0.5",
"resolved": "https://nexus.unige.ch/repository/npm-all/streamsaver/-/streamsaver-2.0.5.tgz",
"integrity": "sha512-KIWtBvi8A6FiFZGNSyuIZRZM6C8AvnWTiCx/TYa7so420vC5sQwcBKkdqInuGWoWMfeWy/P+/cRqMtWVf4RW9w=="
},
"string-extended": {
"version": "0.0.8",
"resolved": "https://nexus.unige.ch/repository/npm-all/string-extended/-/string-extended-0.0.8.tgz",
......
......@@ -6,7 +6,7 @@ sonar.projectName=DLCM Angular Portal
sonar.projectVersion=1.0
sonar.sourceEncoding=UTF-8
sonar.sources=./src
sonar.exclusions=**/node_modules/**,**/*.spec.ts,**/assembly/**,src/ngsw-worker-original.js,src/ngsw-worker-custom.js,src/ngsw-worker-download.js
sonar.exclusions=**/node_modules/**,**/*.spec.ts,**/assembly/**,src/ngsw-worker-custom.js,src/ngsw-worker.js
sonar.tests=./src
sonar.test.inclusions=**/*.spec.ts
sonar.ts.tslintconfigpath=tslint.json
......
......@@ -121,7 +121,7 @@ export class AdminMetadataTypeState extends ResourceState<AdminMetadataTypeState
ext = "json";
}
const fileName = "metadata_type_" + StringUtil.convertToSnakeCase(action.metadataType.name) + "." + ext;
this.downloadService.download(false, `${this._urlResource}/${action.metadataType.resId}/${ApiResourceNameEnum.SCHEMA}`, fileName);
this.downloadService.download(`${this._urlResource}/${action.metadataType.resId}/${ApiResourceNameEnum.SCHEMA}`, fileName);
}
@Action(AdminMetadataTypeAction.TestFile)
......
......@@ -212,7 +212,7 @@ export class DepositDataFileState extends CompositionState<DepositDataFileStateM
@Action(DepositDataFileAction.Download)
download(ctx: StateContext<DepositDataFileStateModel>, action: DepositDataFileAction.Download): void {
const url = `${this._urlResource}/${action.parentId}/${ApiResourceNameEnum.DATAFILE}/${action.dataFile.resId}/${ApiActionNameEnum.DL}`;
this.downloadService.download(false, url, action.dataFile.fileName, action.dataFile.fileSize);
this.downloadService.download(url, action.dataFile.fileName, action.dataFile.fileSize);
}
@Action(DepositDataFileAction.ChangeCurrentFolder)
......
......@@ -789,7 +789,7 @@ export class DepositState extends ResourceLogoState<DepositStateModel, Deposit>
url = url + `?${this._FOLDER_QUERY_PARAM}=${action.fullFolderName}`;
fileName = fileName + action.fullFolderName;
}
this.downloadService.download(false, url, fileName + this._ZIP_EXTENSION);
this.downloadService.download(url, fileName + this._ZIP_EXTENSION);
}
@Action(DepositAction.ComputeModeTab)
......
......@@ -86,7 +86,7 @@ export class PreservationPlanningDepositState extends ResourceState<Preservation
url = url + `?${this._FOLDER_QUERY_PARAM}=${action.fullFolderName}`;
fileName = fileName + action.fullFolderName;
}
this.downloadService.download(false, url, fileName + this._ZIP_EXTENSION);
this.downloadService.download(url, fileName + this._ZIP_EXTENSION);
}
}
......@@ -88,7 +88,7 @@ export class PreservationPlanningDipDataFileState extends CompositionState<Prese
@Action(PreservationPlanningDipDataFileAction.Download)
download(ctx: StateContext<PreservationPlanningDipDataFileStateModel>, action: PreservationPlanningDipDataFileAction.Download): void {
const url = `${this._urlResource}/${action.parentId}/${this._resourceName}/${action.dataFile.resId}/${ApiActionNameEnum.DL}`;
this.downloadService.download(false, url, action.dataFile.fileName, action.dataFile.fileSize);
this.downloadService.download(url, action.dataFile.fileName, action.dataFile.fileSize);
}
@Action(PreservationPlanningDipDataFileAction.Resume)
......
......@@ -144,7 +144,7 @@ export class PreservationPlanningDipState extends ResourceState<PreservationPlan
@Action(PreservationPlanningDipAction.Download)
download(ctx: StateContext<PreservationPlanningDipStateModel>, action: PreservationPlanningDipAction.Download): void {
const fileName = "dip_" + action.id + ".zip";
this.downloadService.download(false, `${this._urlResource}/${action.id}/${ApiActionNameEnum.DL}`, fileName);
this.downloadService.download(`${this._urlResource}/${action.id}/${ApiActionNameEnum.DL}`, fileName);
}
@Action(PreservationPlanningDipAction.Resume)
......
......@@ -93,6 +93,6 @@ export class PreservationPlanningSipCollectionState extends CompositionState<Pre
downloadAip(ctx: StateContext<PreservationPlanningSipCollectionStateModel>, action: PreservationPlanningSipCollectionAction.DownloadAip): void {
// NOT WORK
const url = `${this._urlResource}/${action.parentId}/${this._resourceName}/${action.aip.resId}/${ApiActionNameEnum.DL}`;
this.downloadService.download(false, url, action.aip.info.name, action.aip.archiveSize);
this.downloadService.download(url, action.aip.info.name, action.aip.archiveSize);
}
}
......@@ -88,7 +88,7 @@ export class PreservationPlanningSipDataFileState extends CompositionState<Prese
@Action(PreservationPlanningSipDataFileAction.Download)
download(ctx: StateContext<PreservationPlanningSipDataFileStateModel>, action: PreservationPlanningSipDataFileAction.Download): void {
const url = `${this._urlResource}/${action.parentId}/${this._resourceName}/${action.dataFile.resId}/${ApiActionNameEnum.DL}`;
this.downloadService.download(false, url, action.dataFile.fileName, action.dataFile.fileSize);
this.downloadService.download(url, action.dataFile.fileName, action.dataFile.fileSize);
}
@Action(PreservationPlanningSipDataFileAction.Resume)
......
......@@ -116,7 +116,7 @@ export class PreservationPlanningSipState extends ResourceState<PreservationPlan
@Action(PreservationPlanningSipAction.Download)
download(ctx: StateContext<PreservationPlanningSipStateModel>, action: PreservationPlanningSipAction.Download): void {
const fileName = "sip_" + action.id + ".zip";
this.downloadService.download(false, `${this._urlResource}/${action.id}/${ApiActionNameEnum.DL}`, fileName);
this.downloadService.download(`${this._urlResource}/${action.id}/${ApiActionNameEnum.DL}`, fileName);
}
@Action(PreservationPlanningSipAction.Resume)
......
......@@ -31,7 +31,6 @@ enum ApiResourceNameExtendEnum {
INDEX_FIELD_ALIASES = "index-field-aliases",
USER = "users",
LANGUAGE = "languages",
DOWNLOAD_TOKEN = "download-token",
OAUTH2_CLIENT = "oauth2-clients",
ORCID = "orcid",
BASIC_AUTH_CLIENT = "basic-auth-clients",
......
......@@ -52,10 +52,6 @@ export class ApiEnum {
return ApiEnum.access + SEPARATOR + ApiResourceNameEnum.AIP;
}
static get accessDownloadToken(): string {
return ApiEnum.access + SEPARATOR + ApiResourceNameEnum.DOWNLOAD_TOKEN;
}
static get admin(): string {
return environment.admin;
}
......
......@@ -24,6 +24,10 @@ import {
} from "@shared/enums/routes.enum";
import {StateEnum} from "@shared/enums/state.enum";
import {SharedAipFormPresentational} from "@shared/features/aip/components/presentationals/aip-form/shared-aip-form.presentational";
import {
AipHelper,
AipMode,
} from "@shared/features/aip/helpers/aip.helper";
import {AipDataFile} from "@shared/features/aip/models/aip-data-file.model";
import {SharedAipCollectionAction} from "@shared/features/aip/stores/aip-collection/shared-aip-collection.action";
import {SharedAipCollectionState} from "@shared/features/aip/stores/aip-collection/shared-aip-collection.state";
......@@ -62,6 +66,7 @@ export class SharedAipCollectionRoutable extends AbstractDetailEditRoutable<Aip,
actions: DataTableActions<Aip>[];
readonly KEY_PARAM_NAME: keyof Aip & string = undefined;
mode: AipMode;
get labelTranslateEnum(): typeof LabelTranslateEnum {
return LabelTranslateEnum;
......@@ -133,7 +138,7 @@ export class SharedAipCollectionRoutable extends AbstractDetailEditRoutable<Aip,
logo: IconNameEnum.download,
callback: (aip: Aip) => this.downloadAipAip(this._resId, aip),
placeholder: current => LabelTranslateEnum.download,
displayOnCondition: (aip: Aip) => true,
displayOnCondition: (aip: Aip) => this.mode !== AipMode.DOWNLOADED_AIP,
},
{
logo: IconNameEnum.internalLink,
......@@ -146,6 +151,7 @@ export class SharedAipCollectionRoutable extends AbstractDetailEditRoutable<Aip,
ngOnInit(): void {
super.ngOnInit();
this.mode = AipHelper.determineMode(this._store);
this.getCurrentModelOnParent();
}
......
......@@ -3,6 +3,7 @@ import {
Injectable,
} from "@angular/core";
import {WINDOW} from "@app/app.module";
import {environment} from "@environments/environment";
import {Aip} from "@models";
import {
Action,
......@@ -30,6 +31,7 @@ import {
CompositionStateModel,
defaultCompositionStateInitValue,
DownloadService,
isNullOrUndefined,
NotificationService,
} from "solidify-frontend";
import {ApiActionNameEnum} from "../../../../enums/api-action-name.enum";
......@@ -79,8 +81,8 @@ export class SharedAipCollectionState extends CompositionState<SharedAipCollecti
@Action(SharedAipCollectionAction.DownloadAip)
downloadAip(ctx: StateContext<SharedAipCollectionStateModel>, action: SharedAipCollectionAction.DownloadAip): void {
const url = `${this._urlResource}/${action.parentId}/${this._resourceName}/${action.aip.resId}/${ApiActionNameEnum.DL}`;
this.downloadService.download(false, url, action.aip.info.name, action.aip.archiveSize);
const url = `${this._urlResource}/${action.aip.resId}/${ApiActionNameEnum.DL}`;
this.downloadService.download(url, action.aip.info.name, action.aip.archiveSize);
}
@Action(SharedAipCollectionAction.GoToAip)
......@@ -89,6 +91,6 @@ export class SharedAipCollectionState extends CompositionState<SharedAipCollecti
current: undefined,
list: undefined,
});
ctx.dispatch(new SharedAipAction.GoToAip(action.aip, action.storagion_number));
ctx.dispatch(new SharedAipAction.GoToAip(action.aip, isNullOrUndefined(action.storagion_number) ? "" + environment.defaultStorageIndex + 1 : action.storagion_number));
}
}
......@@ -86,7 +86,7 @@ export class SharedAipDataFileState extends CompositionState<SharedAipDataFileSt
@Action(SharedAipDataFileAction.Download)
download(ctx: StateContext<SharedAipDataFileStateModel>, action: SharedAipDataFileAction.Download): void {
const url = `${this._urlResource}/${action.parentId}/${this._resourceName}/${action.dataFile.resId}/${ApiActionNameEnum.DL}`;
this.downloadService.download(false, url, action.dataFile.fileName, action.dataFile.fileSize);
this.downloadService.download(url, action.dataFile.fileName, action.dataFile.fileSize);
}
@Action(SharedAipDataFileAction.Resume)
......
......@@ -139,7 +139,7 @@ export class SharedAipState extends ResourceState<SharedAipStateModel, Aip> {
@Action(SharedAipAction.Download)
download(ctx: StateContext<SharedAipStateModel>, action: SharedAipAction.Download): void {
const fileName = "aip_" + action.id + ".zip";
this.downloadService.download(false, `${this._urlResource}/${action.id}/${ApiActionNameEnum.DL}`, fileName);
this.downloadService.download(`${this._urlResource}/${action.id}/${ApiActionNameEnum.DL}`, fileName);
}
@OverrideDefaultAction()
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment