Skip to content

Commit

Permalink
Merge pull request #831 from Vizzuality/MARXAN-1237-apply-locks-scena…
Browse files Browse the repository at this point in the history
…rio-endpoints

MARXAN-1237-scenarioLocks-on-all-endpoints
  • Loading branch information
rubvalave authored Feb 17, 2022
2 parents abb0cd5 + 37b28ac commit 6a4b2e5
Show file tree
Hide file tree
Showing 12 changed files with 535 additions and 333 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
UserRoleInProjectDto,
UsersInProjectResult,
} from '@marxan-api/modules/access-control/projects-acl/dto/user-role-project.dto';
import { aclErrorHandler } from '@marxan-api/utils/acl.utils';
import { mapAclDomainToHttpError } from '@marxan-api/utils/acl.utils';
import { ImplementsAcl } from '@marxan-api/decorators/acl.decorator';

@ImplementsAcl()
Expand Down Expand Up @@ -84,7 +84,7 @@ export class ProjectAclController {
);

if (isLeft(result)) {
aclErrorHandler(result.left);
throw mapAclDomainToHttpError(result.left);
}
}

Expand All @@ -107,7 +107,7 @@ export class ProjectAclController {
);

if (isLeft(result)) {
aclErrorHandler(result.left);
throw mapAclDomainToHttpError(result.left);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export class ScenarioAccessControlMock implements ScenarioAccessControl {
async releaseLock(
userId: string,
scenarioId: string,
projectId: string,
_projectId: string,
): Promise<Either<typeof forbiddenError | typeof lockedByAnotherUser, void>> {
if (this.mock(userId, scenarioId)) {
return left(forbiddenError);
Expand All @@ -75,6 +75,7 @@ export class ScenarioAccessControlMock implements ScenarioAccessControl {
async findLock(
userId: string,
scenarioId: string,
_isDeletion?: boolean,
): Promise<Either<typeof forbiddenError, ScenarioLockResultSingular>> {
if (this.mock(userId, scenarioId)) {
return left(forbiddenError);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export abstract class ScenarioAccessControl {
abstract canEditScenarioAndOwnsLock(
userId: string,
scenarioId: string,
isDeletion?: boolean,
): Promise<
Either<
typeof forbiddenError | typeof lockedByAnotherUser | typeof noLockInPlace,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
UserRoleInScenarioDto,
UsersInScenarioResult,
} from '@marxan-api/modules/access-control/scenarios-acl/dto/user-role-scenario.dto';
import { aclErrorHandler } from '@marxan-api/utils/acl.utils';
import { mapAclDomainToHttpError } from '@marxan-api/utils/acl.utils';
import { ImplementsAcl } from '@marxan-api/decorators/acl.decorator';

@ImplementsAcl()
Expand Down Expand Up @@ -84,7 +84,7 @@ export class ScenarioAclController {
);

if (isLeft(result)) {
aclErrorHandler(result.left);
throw mapAclDomainToHttpError(result.left);
}
}

Expand All @@ -107,7 +107,7 @@ export class ScenarioAclController {
);

if (isLeft(result)) {
aclErrorHandler(result.left);
throw mapAclDomainToHttpError(result.left);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
} from '@marxan-api/modules/access-control/scenarios-acl/dto/user-role-scenario.dto';
import { UsersScenariosApiEntity } from '@marxan-api/modules/access-control/scenarios-acl/entity/users-scenarios.api.entity';
import { ScenarioAccessControl } from '@marxan-api/modules/access-control/scenarios-acl/scenario-access-control';
import { Either, left, right } from 'fp-ts/lib/Either';
import { Either, isRight, left, right } from 'fp-ts/lib/Either';
import { DbConnections } from '@marxan-api/ormconfig.connections';
import { assertDefined } from '@marxan/utils';
import {
Expand Down Expand Up @@ -229,19 +229,31 @@ export class ScenarioAclService implements ScenarioAccessControl {
async canEditScenarioAndOwnsLock(
userId: string,
scenarioId: string,
isDeletion?: boolean,
): Promise<
Either<
typeof forbiddenError | typeof lockedByAnotherUser | typeof noLockInPlace,
boolean
>
> {
const scenarioIsLocked = await this.lockService.isLocked(scenarioId);
const canEditScenario = await this.canEditScenario(userId, scenarioId);
const scenarioIsAlreadyLocked = await this.lockService.isLocked(scenarioId);
const canProcessActionInScenario = isDeletion
? await this.canDeleteScenario(userId, scenarioId)
: await this.canEditScenario(userId, scenarioId);
// LOFU (lock on first use): if scenario is not locked (maybe it's new and it
// was never locked, or any previous locks have expired or have been
// explicitly released), the current user can attempt to acquire a lock
// transparently.
const scenarioIsLocked =
canProcessActionInScenario && !scenarioIsAlreadyLocked
? isRight(await this.acquireLock(userId, scenarioId))
: scenarioIsAlreadyLocked;

const scenarioIsLockedByCurrentUser = await this.lockService.isLockedByUser(
scenarioId,
userId,
);
if (!canEditScenario) {
if (!canProcessActionInScenario) {
return left(forbiddenError);
}
if (!scenarioIsLocked) {
Expand All @@ -251,7 +263,9 @@ export class ScenarioAclService implements ScenarioAccessControl {
return left(lockedByAnotherUser);
}
return right(
scenarioIsLocked && canEditScenario && scenarioIsLockedByCurrentUser,
scenarioIsLocked &&
canProcessActionInScenario &&
scenarioIsLockedByCurrentUser,
);
}

Expand Down
Loading

0 comments on commit 6a4b2e5

Please sign in to comment.