Skip to content

Commit

Permalink
feat(api): add repository to save and find revoked user access tempor…
Browse files Browse the repository at this point in the history
…ary storage
  • Loading branch information
EmmanuelleBonnemay committed Jan 29, 2025
1 parent 936ae43 commit 50cece4
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 0 deletions.
6 changes: 6 additions & 0 deletions api/sample.env
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,12 @@ REFRESH_TOKEN_LIFESPAN=7d
# default: '7d'
# REFRESH_TOKEN_LIFESPAN_PIX_ADMIN=7d

# Revoked user access lifespan
# presence: optional
# type: String
# default: '2d'
# REVOKED_USER_ACCESS_LIFESPAN=2d

# Saml access token lifespan
# presence: optional
# type: String
Expand Down
14 changes: 14 additions & 0 deletions api/src/identity-access-management/domain/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ class UserCantBeCreatedError extends DomainError {
}
}

class RevokeDateMustBeAnInstanceOfDate extends DomainError {
constructor(message = 'Revoke date must be an instance of Date') {
super(message);
}
}

class UserIdIsRequiredError extends DomainError {
constructor(message = 'User Id is required') {
super(message);
}
}

class UserShouldChangePasswordError extends DomainError {
constructor(message = 'User password must be changed.', meta) {
super(message);
Expand All @@ -87,6 +99,8 @@ export {
OrganizationLearnerNotBelongToOrganizationIdentityError,
PasswordNotMatching,
PasswordResetDemandNotFoundError,
RevokeDateMustBeAnInstanceOfDate,
UserCantBeCreatedError,
UserIdIsRequiredError,
UserShouldChangePasswordError,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { config } from '../../../../src/shared/config.js';
import { temporaryStorage } from '../../../../src/shared/infrastructure/key-value-storages/index.js';
import { UserIdIsRequiredError } from '../../domain/errors.js';
import { RevokeDateMustBeAnInstanceOfDate } from '../../domain/errors.js';
import { RevokedUserAccess } from '../../domain/models/revoked-user-access.js';

const revokedUserAccessTemporaryStorage = temporaryStorage.withPrefix('revoked-user-access:');
const revokedUserAccessLifespanMs = config.authentication.revokedUserAccessLifespanMs;

export const saveForUser = async function (userId, revokeDate) {
if (!userId) {
throw new UserIdIsRequiredError();
}

if (!(revokeDate instanceof Date)) {
throw new RevokeDateMustBeAnInstanceOfDate();
}

await revokedUserAccessTemporaryStorage.save({
key: userId,
value: Math.floor(revokeDate.getTime() / 1000),
expirationDelaySeconds: revokedUserAccessLifespanMs / 1000,
});
};

export const findByUserId = async function (userId) {
const value = await revokedUserAccessTemporaryStorage.get(userId);
return new RevokedUserAccess(value);
};
1 change: 1 addition & 0 deletions api/src/shared/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ const configuration = (function () {
'pix-certif': ms(process.env.REFRESH_TOKEN_LIFESPAN_PIX_CERTIF || '7d'),
'pix-admin': ms(process.env.REFRESH_TOKEN_LIFESPAN_PIX_ADMIN || '7d'),
},
revokedUserAccessLifespanMs: ms(process.env.REVOKED_USER_ACCESS_LIFESPAN || '7d'),
tokenForCampaignResultLifespan: process.env.CAMPAIGN_RESULT_ACCESS_TOKEN_LIFESPAN || '1h',
tokenForStudentReconciliationLifespan: '1h',
passwordResetTokenLifespan: '1h',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { RevokedUserAccess } from '../../../../../src/identity-access-management/domain/models/revoked-user-access.js';
import * as revokedUserAccessRepository from '../../../../../src/identity-access-management/infrastructure/repositories/revoked-user-access.repository.js';
import { temporaryStorage } from '../../../../../src/shared/infrastructure/key-value-storages/index.js';
import { expect } from '../../../../test-helper.js';

const revokedUserAccessTemporaryStorage = temporaryStorage.withPrefix('revoked-user-access:');

describe('Integration | Identity Access Management | Infrastructure | Repository | revoked-user', function () {
beforeEach(async function () {
await revokedUserAccessTemporaryStorage.flushAll();
});

describe('#saveForUser', function () {
it('saves revoked user access in Redis', async function () {
// given
const revokeDate = new Date();
const revokedTimeStamp = Math.floor(revokeDate.getTime() / 1000);

// when
await revokedUserAccessRepository.saveForUser(12345, revokeDate);

// then
const result = await revokedUserAccessTemporaryStorage.get(12345);
expect(result).to.equal(revokedTimeStamp);
});
});

describe('#findByUserId', function () {
it('finds revoked user access by user id', async function () {
// given
const revokeDate = new Date();
const revokeTimeStamp = Math.floor(new Date().getTime() / 1000);
await revokedUserAccessRepository.saveForUser(12345, revokeDate);

// when
const result = await revokedUserAccessRepository.findByUserId(12345);

// then
expect(result).to.deep.equal({
revokeTimeStamp: revokeTimeStamp,
});
expect(result).to.be.instanceOf(RevokedUserAccess);
});
});
});

0 comments on commit 50cece4

Please sign in to comment.