diff --git a/jobConfig.example.yaml b/jobConfig.example.yaml index ab865be13..216ed9a18 100644 --- a/jobConfig.example.yaml +++ b/jobConfig.example.yaml @@ -21,7 +21,7 @@ jobs: from: from subject: "[SciCat] Your {{type}} job was submitted successfully" bodyTemplateFile: src/common/email-templates/job-template-simplified.html - statusUpdate: + update: auth: archivemanager actions: - actionType: rabbitmq @@ -39,5 +39,5 @@ jobs: required: - pid - files - statusUpdate: + update: auth: "#all" diff --git a/src/casl/action.enum.ts b/src/casl/action.enum.ts index 6eb84b3a6..c4c856ce5 100644 --- a/src/casl/action.enum.ts +++ b/src/casl/action.enum.ts @@ -5,7 +5,6 @@ export enum Action { ReadOwn = "readown", ReadAll = "readall", Update = "update", - StatusUpdate = "statusUpdate", Delete = "delete", ListOwn = "listown", ListAll = "listall", @@ -182,7 +181,7 @@ export enum Action { // endpoint authorization JobCreate = "jobs_create", JobRead = "jobs_read", - JobStatusUpdate = "job_update", + JobUpdate = "job_update", JobDelete = "job_delete", // data instance authorization JobCreateConfiguration = "job_create_configuration", @@ -190,9 +189,9 @@ export enum Action { JobCreateAny = "job_create_any", JobReadAccess = "job_read_access", JobReadAny = "job_read_any", - JobStatusUpdateConfiguration = "job_status_update_configuration", - JobStatusUpdateOwner = "job_status_update_owner", - JobStatusUpdateAny = "job_status_update_any", + JobUpdateConfiguration = "job_update_configuration", + JobUpdateOwner = "job_update_owner", + JobUpdateAny = "job_update_any", //JobDeleteAny = "job_delete_any", // ------------- diff --git a/src/casl/casl-ability.factory.ts b/src/casl/casl-ability.factory.ts index e0934b3e0..43b688ce5 100644 --- a/src/casl/casl-ability.factory.ts +++ b/src/casl/casl-ability.factory.ts @@ -27,11 +27,7 @@ import { User } from "src/users/schemas/user.schema"; import { Action } from "./action.enum"; import configuration from "src/config/configuration"; import { JobConfigService } from "src/config/job-config/jobconfig.service"; -import { - CreateJobAuth, - StatusUpdateJobAuth, -} from "src/jobs/types/jobs-auth.enum"; - +import { CreateJobAuth, UpdateJobAuth } from "src/jobs/types/jobs-auth.enum"; import { JobConfig } from "src/config/job-config/jobconfig.interface"; type Subjects = @@ -372,12 +368,12 @@ export class CaslAbilityFactory { cannot(Action.JobRead, JobClass); if ( Object.values(this.jobConfigService.allJobConfigs).some( - (j) => j.statusUpdate.auth == StatusUpdateJobAuth.All, + (j) => j.update.auth == UpdateJobAuth.All, ) ) { - can(Action.JobStatusUpdate, JobClass); + can(Action.JobUpdate, JobClass); } else { - cannot(Action.JobStatusUpdate, JobClass); + cannot(Action.JobUpdate, JobClass); } cannot(Action.JobDelete, JobClass); } else { @@ -393,7 +389,7 @@ export class CaslAbilityFactory { */ can(Action.JobRead, JobClass); can(Action.JobCreate, JobClass); - can(Action.JobStatusUpdate, JobClass); + can(Action.JobUpdate, JobClass); } else { const jobUserAuthorizationValues = [ ...user.currentGroups.map((g) => "@" + g), @@ -432,26 +428,26 @@ export class CaslAbilityFactory { } } const jobUpdateEndPointAuthorizationValues = [ - ...Object.values(StatusUpdateJobAuth), + ...Object.values(UpdateJobAuth), ...jobUserAuthorizationValues, ]; if ( user.currentGroups.some((g) => - configuration().statusUpdateJobGroups.includes(g), + configuration().updateJobGroups.includes(g), ) ) { - can(Action.JobStatusUpdate, JobClass); + can(Action.JobUpdate, JobClass); } else { if ( Object.values(this.jobConfigService.allJobConfigs).some( (j) => - j.statusUpdate.auth && + j.update.auth && jobUpdateEndPointAuthorizationValues.includes( - j.statusUpdate.auth as string, + j.update.auth as string, ), ) ) { - can(Action.JobStatusUpdate, JobClass); + can(Action.JobUpdate, JobClass); } } } @@ -1381,8 +1377,8 @@ export class CaslAbilityFactory { datasetsValidation: true, }); } - if (jobConfiguration.statusUpdate.auth === StatusUpdateJobAuth.All) { - can(Action.JobStatusUpdateConfiguration, JobClass, { + if (jobConfiguration.update.auth === UpdateJobAuth.All) { + can(Action.JobUpdateConfiguration, JobClass, { ownerGroup: undefined, }); } @@ -1399,7 +1395,7 @@ export class CaslAbilityFactory { */ can(Action.JobReadAny, JobClass); can(Action.JobCreateAny, JobClass); - can(Action.JobStatusUpdateAny, JobClass); + can(Action.JobUpdateAny, JobClass); } else { const jobUserAuthorizationValues = [ ...user.currentGroups.map((g) => "@" + g), @@ -1458,7 +1454,7 @@ export class CaslAbilityFactory { } } const jobUpdateInstanceAuthorizationValues = [ - ...Object.values(StatusUpdateJobAuth).filter( + ...Object.values(UpdateJobAuth).filter( (v) => !String(v).includes("#job"), ), ...jobUserAuthorizationValues, @@ -1466,37 +1462,37 @@ export class CaslAbilityFactory { if ( user.currentGroups.some((g) => - configuration().statusUpdateJobGroups.includes(g), + configuration().updateJobGroups.includes(g), ) ) { if ( jobUpdateInstanceAuthorizationValues.some( - (a) => jobConfiguration.statusUpdate.auth === a, + (a) => jobConfiguration.update.auth === a, ) ) { - can(Action.JobStatusUpdateConfiguration, JobClass); + can(Action.JobUpdateConfiguration, JobClass); } - can(Action.JobStatusUpdateOwner, JobClass, { + can(Action.JobUpdateOwner, JobClass, { ownerUser: user.username, }); - can(Action.JobStatusUpdateOwner, JobClass, { + can(Action.JobUpdateOwner, JobClass, { ownerGroup: { $in: user.currentGroups }, }); } else { if ( jobUpdateInstanceAuthorizationValues.some( - (a) => jobConfiguration.statusUpdate.auth === a, + (a) => jobConfiguration.update.auth === a, ) ) { - can(Action.JobStatusUpdateConfiguration, JobClass); + can(Action.JobUpdateConfiguration, JobClass); } - if (jobConfiguration.statusUpdate.auth === "#jobOwnerUser") { - can(Action.JobStatusUpdateConfiguration, JobClass, { + if (jobConfiguration.update.auth === "#jobOwnerUser") { + can(Action.JobUpdateConfiguration, JobClass, { ownerUser: user.username, }); } - if (jobConfiguration.statusUpdate.auth === "#jobOwnerGroup") { - can(Action.JobStatusUpdateConfiguration, JobClass, { + if (jobConfiguration.update.auth === "#jobOwnerGroup") { + can(Action.JobUpdateConfiguration, JobClass, { ownerGroup: { $in: user.currentGroups }, }); } diff --git a/src/config/configuration.ts b/src/config/configuration.ts index bb8df673c..9d1083607 100644 --- a/src/config/configuration.ts +++ b/src/config/configuration.ts @@ -21,7 +21,7 @@ const configuration = () => { process.env.DATASET_CREATION_VALIDATION_REGEX || ("" as string); const createJobGroups = process.env.CREATE_JOB_GROUPS || ("" as string); - const statusUpdateJobGroups = process.env.UPDATE_JOB_GROUPS || ("" as string); + const updateJobGroups = process.env.UPDATE_JOB_GROUPS || ("" as string); const deleteJobGroups = process.env.DELETE_JOB_GROUPS || ("" as string); const proposalGroups = process.env.PROPOSAL_GROUPS || ("" as string); @@ -95,7 +95,7 @@ const configuration = () => { datasetCreationValidationEnabled: datasetCreationValidationEnabled, datasetCreationValidationRegex: datasetCreationValidationRegex, createJobGroups: createJobGroups, - statusUpdateJobGroups: statusUpdateJobGroups, + updateJobGroups: updateJobGroups, deleteJobGroups: deleteJobGroups, logoutURL: process.env.LOGOUT_URL ?? "", // Example: http://localhost:3000/ accessGroupsGraphQlConfig: { diff --git a/src/config/job-config/jobconfig.interface.ts b/src/config/job-config/jobconfig.interface.ts index c32defa7c..0defe9b67 100644 --- a/src/config/job-config/jobconfig.interface.ts +++ b/src/config/job-config/jobconfig.interface.ts @@ -1,5 +1,5 @@ import { CreateJobDto } from "../../jobs/dto/create-job.dto"; -import { StatusUpdateJobDto } from "../../jobs/dto/status-update-job.dto"; +import { UpdateJobDto } from "../../jobs/dto/update-job.dto"; import { JobsAuth } from "../../jobs/types/jobs-auth.enum"; import { JobClass } from "../../jobs/schemas/job.schema"; @@ -20,18 +20,18 @@ export interface JobConfig { jobType: string; configVersion: string; create: JobOperation; - statusUpdate: JobOperation; + update: JobOperation; } export interface JobConfigOptions { jobType: string; configVersion: string; create: JobOperationOptions; - statusUpdate: JobOperationOptions; + update: JobOperationOptions; } -export type JobDto = CreateJobDto | StatusUpdateJobDto; +export type JobDto = CreateJobDto | UpdateJobDto; /** - * Encapsulates all information for a particular job operation (eg "create", "statusUpdate") + * Encapsulates all information for a particular job operation (eg "create", "update") */ export interface JobOperation { auth: JobsAuth | undefined; diff --git a/src/config/job-config/jobconfig.schema.ts b/src/config/job-config/jobconfig.schema.ts index c989d0bff..64e3a5980 100644 --- a/src/config/job-config/jobconfig.schema.ts +++ b/src/config/job-config/jobconfig.schema.ts @@ -28,7 +28,7 @@ export const JobConfigSchema = { }, additionalProperties: true, }, - statusUpdate: { + update: { type: "object", properties: { auth: { type: "string" }, diff --git a/src/config/job-config/jobconfig.service.ts b/src/config/job-config/jobconfig.service.ts index 0875736a1..aee5bb3a7 100644 --- a/src/config/job-config/jobconfig.service.ts +++ b/src/config/job-config/jobconfig.service.ts @@ -15,7 +15,7 @@ import { JobConfigSchema } from "./jobconfig.schema"; import { load } from "js-yaml"; import * as fs from "fs"; import { CreateJobDto } from "../../jobs/dto/create-job.dto"; -import { StatusUpdateJobDto } from "../../jobs/dto/status-update-job.dto"; +import { UpdateJobDto } from "../../jobs/dto/update-job.dto"; import { ConfigService } from "@nestjs/config"; /** @@ -30,10 +30,7 @@ export class JobConfigService { @Inject(CREATE_JOB_ACTION_CREATORS) private create_creators: Record>, @Inject(UPDATE_JOB_ACTION_CREATORS) - private update_creators: Record< - string, - JobActionCreator - >, + private update_creators: Record>, configService: ConfigService, ) { this.filePath = configService.get("jobConfigurationFile") || ""; @@ -104,8 +101,8 @@ export class JobConfigService { options.create, this.create_creators, ), - statusUpdate: this.parseJobOperation( - options.statusUpdate, + update: this.parseJobOperation( + options.update, this.update_creators, ), }; diff --git a/src/jobs/dto/status-update-job.dto.ts b/src/jobs/dto/update-job.dto.ts similarity index 95% rename from src/jobs/dto/status-update-job.dto.ts rename to src/jobs/dto/update-job.dto.ts index f1a3354e1..fc5e9f22c 100644 --- a/src/jobs/dto/status-update-job.dto.ts +++ b/src/jobs/dto/update-job.dto.ts @@ -2,7 +2,7 @@ import { ApiProperty, ApiTags } from "@nestjs/swagger"; import { IsOptional, IsString, IsObject } from "class-validator"; @ApiTags("jobs") -export class StatusUpdateJobDto { +export class UpdateJobDto { @ApiProperty({ type: String, required: true, diff --git a/src/jobs/jobs.controller.ts b/src/jobs/jobs.controller.ts index f0d8f6380..bd64728a0 100644 --- a/src/jobs/jobs.controller.ts +++ b/src/jobs/jobs.controller.ts @@ -17,7 +17,7 @@ import { Request } from "express"; import { FilterQuery } from "mongoose"; import { JobsService } from "./jobs.service"; import { CreateJobDto } from "./dto/create-job.dto"; -import { StatusUpdateJobDto } from "./dto/status-update-job.dto"; +import { UpdateJobDto } from "./dto/update-job.dto"; import { DatasetListDto } from "./dto/dataset-list.dto"; import { PoliciesGuard } from "src/casl/guards/policies.guard"; import { CheckPolicies } from "src/casl/decorators/check-policies.decorator"; @@ -631,7 +631,7 @@ export class JobsController { Job was created with configVersion ${jobInstance.configVersion}. Current configVersion is ${jobConfig.configVersion}. `, - "JobStatusUpdate", + "JobUpdate", ); } } @@ -680,21 +680,21 @@ export class JobsController { } /** - * Update job status + * Update job */ @UseGuards(PoliciesGuard) @CheckPolicies("jobs", (ability: AppAbility) => - ability.can(Action.JobStatusUpdate, JobClass), + ability.can(Action.JobUpdate, JobClass), ) @Patch(":id") @ApiOperation({ - summary: "It updates the status of an existing job.", - description: "It updates the status of an existing job.", + summary: "It updates an existing job.", + description: "It updates an existing job.", }) @ApiBody({ - description: "Status fields for the job to be updated", + description: "Fields for the job to be updated", required: true, - type: StatusUpdateJobDto, + type: UpdateJobDto, }) @ApiResponse({ status: HttpStatus.OK, @@ -704,7 +704,7 @@ export class JobsController { async update( @Req() request: Request, @Param("id") id: string, - @Body() statusUpdateJobDto: StatusUpdateJobDto, + @Body() updateJobDto: UpdateJobDto, ): Promise { Logger.log("updating job ", id); // Find existing job @@ -726,26 +726,23 @@ export class JobsController { jobConfig, ); // check if the user can update this job - const canUpdateStatus = - ability.can(Action.JobStatusUpdateAny, JobClass) || - ability.can(Action.JobStatusUpdateOwner, currentJobInstance) || - ability.can(Action.JobStatusUpdateConfiguration, currentJobInstance); - if (!canUpdateStatus) { + const canUpdate = + ability.can(Action.JobUpdateAny, JobClass) || + ability.can(Action.JobUpdateOwner, currentJobInstance) || + ability.can(Action.JobUpdateConfiguration, currentJobInstance); + if (!canUpdate) { throw new ForbiddenException("Unauthorized to update this job."); } // Allow actions to validate DTO - await this.validateDTO(jobConfig.statusUpdate.actions, statusUpdateJobDto); + await this.validateDTO(jobConfig.update.actions, updateJobDto); // Update job in database - const updatedJob = await this.jobsService.statusUpdate( - id, - statusUpdateJobDto, - ); + const updatedJob = await this.jobsService.update(id, updateJobDto); // Perform the action that is specified in the update portion of the job configuration if (updatedJob !== null) { await this.checkConfigVersion(jobConfig, updatedJob); - await this.performActions(jobConfig.statusUpdate.actions, updatedJob); + await this.performActions(jobConfig.update.actions, updatedJob); } return updatedJob; } diff --git a/src/jobs/jobs.service.ts b/src/jobs/jobs.service.ts index bce201ad3..069e0f132 100644 --- a/src/jobs/jobs.service.ts +++ b/src/jobs/jobs.service.ts @@ -20,7 +20,7 @@ import { parseLimitFilters, } from "src/common/utils"; import { CreateJobDto } from "./dto/create-job.dto"; -import { StatusUpdateJobDto } from "./dto/status-update-job.dto"; +import { UpdateJobDto } from "./dto/update-job.dto"; import { JobClass, JobDocument } from "./schemas/job.schema"; import { IJobFields } from "./interfaces/job-filters.interface"; @@ -94,9 +94,9 @@ export class JobsService { return this.jobModel.findOne(filter).exec(); } - async statusUpdate( + async update( id: string, - statusUpdateJobDto: StatusUpdateJobDto, + updateJobDto: UpdateJobDto, ): Promise { const existingJob = await this.jobModel.findOne({ id: id }).exec(); if (!existingJob) { @@ -108,13 +108,10 @@ export class JobsService { .findOneAndUpdate( { id: id }, addStatusFields( - addUpdatedByField( - statusUpdateJobDto as UpdateQuery, - username, - ), - statusUpdateJobDto.statusCode, - statusUpdateJobDto.statusMessage, - statusUpdateJobDto.jobResultObject, + addUpdatedByField(updateJobDto as UpdateQuery, username), + updateJobDto.statusCode, + updateJobDto.statusMessage, + updateJobDto.jobResultObject, ), { new: true }, ) diff --git a/src/jobs/schemas/job.schema.ts b/src/jobs/schemas/job.schema.ts index 465de79da..056a72b2a 100644 --- a/src/jobs/schemas/job.schema.ts +++ b/src/jobs/schemas/job.schema.ts @@ -131,7 +131,7 @@ export class JobClass extends OwnableClass { }) configVersion: string; - // initially empty, then provided during statusUpdate + // initially empty, then provided during update @ApiProperty({ type: Object, required: false, diff --git a/src/jobs/types/jobs-auth.enum.ts b/src/jobs/types/jobs-auth.enum.ts index 7d966aa5f..b807ec4f5 100644 --- a/src/jobs/types/jobs-auth.enum.ts +++ b/src/jobs/types/jobs-auth.enum.ts @@ -13,7 +13,7 @@ export enum CreateJobAuth { DatasetOwner = "#datasetOwner", } -export enum StatusUpdateJobAuth { +export enum UpdateJobAuth { // any user can update, no checks are performed // to be used carefully, mainly for testing All = "#all", @@ -25,4 +25,4 @@ export enum StatusUpdateJobAuth { JobOwnerGroup = "#jobOwnerGroup", } -export type JobsAuth = CreateJobAuth | StatusUpdateJobAuth; +export type JobsAuth = CreateJobAuth | UpdateJobAuth; diff --git a/test/config/jobconfig.yaml b/test/config/jobconfig.yaml index cd61bea70..1f0474782 100644 --- a/test/config/jobconfig.yaml +++ b/test/config/jobconfig.yaml @@ -5,52 +5,52 @@ jobs: auth: "#all" actions: - actionType: log - statusUpdate: + update: auth: "#all" - jobType: public_access create: auth: "#datasetPublic" - statusUpdate: + update: auth: "#all" - jobType: authenticated_access create: auth: "#authenticated" - statusUpdate: + update: auth: "#all" - jobType: dataset_access create: auth: "#datasetAccess" - statusUpdate: + update: auth: "#jobOwnerGroup" - jobType: owner_access create: auth: "#datasetOwner" - statusUpdate: + update: auth: "#jobOwnerUser" - jobType: user_access create: auth: user5.1 - statusUpdate: + update: auth: user5.1 - jobType: group_access create: auth: "@group5" - statusUpdate: + update: auth: "@group5" - jobType: archive create: auth: "#all" - statusUpdate: + update: auth: "#all" - jobType: retrieve create: auth: "#all" - statusUpdate: + update: auth: "#all" - jobType: public create: auth: "#all" - statusUpdate: + update: auth: "#all" - jobType: validate create: @@ -64,7 +64,7 @@ jobs: type: array items: type: string - statusUpdate: + update: auth: admin actions: - actionType: validate