Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/core/backends/backends.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export class BackendsService extends AbstractQueueProcessor {
await this.identitiesService.model.findByIdAndUpdate(isSyncedJob?.concernedTo?.id, {
$set: {
state: IdentityState.SYNCED,
lastBackendSync: new Date(),
},
});
this.logger.warn(`Job already completed, syncing... [${job.id}::COMPLETED]`);
Expand Down
12 changes: 12 additions & 0 deletions src/management/identities/_dto/force-password-dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString } from 'class-validator';

export class ForcePasswordDto {
@IsString()
@ApiProperty({ example: '66d80ab41821baca9bf965b2', description: 'User object id', type: String })
public id: string;

@IsString()
@ApiProperty({ example: 'MyNewPassword', description: 'New password', type: String })
public newPassword: string;
}
9 changes: 9 additions & 0 deletions src/management/identities/_dto/need-change-password.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsString } from 'class-validator';

export class NeedChangePasswordDto {
@IsString()
@ApiProperty({ example: '66d80ab41821baca9bf965b2', description: 'User object id', type: String })
public id: string;

}
4 changes: 3 additions & 1 deletion src/management/identities/_enums/data-status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
//DELETED : soft delete
export enum DataStatusEnum {
ACTIVE = 1,
INACTIVE = 0,
NOTINITIALIZED = 0,
DELETED = -1,
PASSWORDNEEDTOBECHANGED=-2,
INACTIVE = -3,
}
2 changes: 1 addition & 1 deletion src/management/identities/_schemas/identities.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class Identities extends AbstractSchema {
@Prop({ type: Number, enum: IdentityLifecycle, default: IdentityLifecycle.INACTIVE })
public lifecycle: IdentityLifecycle;

@Prop({ type: Number, enum: DataStatusEnum, default: DataStatusEnum.INACTIVE })
@Prop({ type: Number, enum: DataStatusEnum, default: DataStatusEnum.NOTINITIALIZED })
public dataStatus: DataStatusEnum;

@Prop({ type: Boolean, default: false })
Expand Down
81 changes: 66 additions & 15 deletions src/management/identities/abstract-identities.service.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
import { BadRequestException, forwardRef, HttpException, Inject, Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Document, Model, ModifyResult, Query, Types } from 'mongoose';
import { AbstractServiceSchema } from '~/_common/abstracts/abstract.service.schema';
import { AbstractSchema } from '~/_common/abstracts/schemas/abstract.schema';
import { ValidationConfigException, ValidationSchemaException } from '~/_common/errors/ValidationException';
import { IdentitiesUpsertDto } from './_dto/identities.dto';
import { IdentityState } from './_enums/states.enum';
import { Identities } from './_schemas/identities.schema';
import { IdentitiesValidationService } from './validations/identities.validation.service';
import { FactorydriveService } from '@the-software-compagny/nestjs_module_factorydrive';
import { BackendsService } from '~/core/backends/backends.service';
import { construct, omit } from 'radash';
import { toPlainAndCrush } from '~/_common/functions/to-plain-and-crush';
import { createHash } from 'node:crypto';
import {BadRequestException, forwardRef, HttpException, Inject, Injectable} from '@nestjs/common';
import {InjectModel} from '@nestjs/mongoose';
import {Document, Model, ModifyResult, Query, Types} from 'mongoose';
import {AbstractServiceSchema} from '~/_common/abstracts/abstract.service.schema';
import {AbstractSchema} from '~/_common/abstracts/schemas/abstract.schema';
import {ValidationConfigException, ValidationSchemaException} from '~/_common/errors/ValidationException';
import {IdentitiesUpsertDto} from './_dto/identities.dto';
import {IdentityState} from './_enums/states.enum';
import {Identities} from './_schemas/identities.schema';
import {IdentitiesValidationService} from './validations/identities.validation.service';
import {FactorydriveService} from '@the-software-compagny/nestjs_module_factorydrive';
import {BackendsService} from '~/core/backends/backends.service';
import {construct, omit} from 'radash';
import {toPlainAndCrush} from '~/_common/functions/to-plain-and-crush';
import {createHash} from 'node:crypto';
import {PasswdadmService} from "~/settings/passwdadm.service";
import {DataStatusEnum} from "~/management/identities/_enums/data-status";
import {JobState} from "~/core/jobs/_enums/state.enum";

@Injectable()
export abstract class AbstractIdentitiesService extends AbstractServiceSchema {
public constructor(
@InjectModel(Identities.name) protected _model: Model<Identities>,
protected readonly _validation: IdentitiesValidationService,
protected readonly storage: FactorydriveService,
protected readonly passwdAdmService: PasswdadmService,
@Inject(forwardRef(() => BackendsService)) protected readonly backends: BackendsService,
) {
super();
Expand Down Expand Up @@ -138,4 +142,51 @@ export abstract class AbstractIdentitiesService extends AbstractServiceSchema {
hash.update(data);
return hash.digest('hex').toString();
}
public async activation(id: string, status: DataStatusEnum) {
//recherche de l'identité
let identity: Identities = null;
let statusChanged = false;
try {
identity = await this.findById<Identities>(id);
} catch (error) {
throw new HttpException('Id not found', 400);
}
if (identity.lastBackendSync === null) {
throw new HttpException('Identity has never been synced', 400);
}
if (identity.dataStatus !== DataStatusEnum.DELETED) {
identity.dataStatus = status;
statusChanged = true;
} else {
throw new BadRequestException('Identity is in status deleted');
}
//sauvegarde de l'identité
if (statusChanged) {
// le dataStaus à changé on envoye l info aux backend et on enregistre l identité
// Envoi du status au backend
let statusBackend=true
if (status == DataStatusEnum.INACTIVE || status == DataStatusEnum.PASSWORDNEEDTOBECHANGED){
statusBackend= false
}
const result = await this.backends.activationIdentity(identity._id.toString(),statusBackend);
if (result.state === JobState.COMPLETED) {
await super.update(identity._id, identity);
} else {
throw new HttpException('Backend failed', 400);
}
}
}
public async askToChangePassword(id: string){
try {
const identity = await this.findById<Identities>(id);
if (identity.dataStatus === DataStatusEnum.ACTIVE) {
identity.dataStatus = DataStatusEnum.PASSWORDNEEDTOBECHANGED
await super.update(identity._id, identity);
} else {
throw new BadRequestException('Identity not in active');
}
} catch (error) {
throw new HttpException('Id not found', 400);
}
}
}
19 changes: 12 additions & 7 deletions src/management/identities/identities-activation.controller.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { AbstractController } from '~/_common/abstracts/abstract.controller';
import { IdentitiesActivationService } from '~/management/identities/identities-activation.service';
import { ApiOperation, ApiResponse, ApiTags} from '@nestjs/swagger';
import { Body, Controller, HttpStatus, Post, Res} from '@nestjs/common';
import { Response } from 'express';
import { ActivationDto } from '~/management/identities/_dto/_parts/activation-dto';
import {AbstractController} from '~/_common/abstracts/abstract.controller';
import {IdentitiesActivationService} from '~/management/identities/identities-activation.service';
import {ApiOperation, ApiResponse, ApiTags} from '@nestjs/swagger';
import {Body, Controller, HttpStatus, Post, Res} from '@nestjs/common';
import {Response} from 'express';
import {ActivationDto} from '~/management/identities/_dto/_parts/activation-dto';
import {DataStatusEnum} from "~/management/identities/_enums/data-status";

@ApiTags('management/identities')
@Controller('identities')
Expand All @@ -17,7 +18,11 @@ export class IdentitiesActivationController extends AbstractController {
@ApiResponse({ status: HttpStatus.OK })
public async activation(@Res() res: Response, @Body() body: ActivationDto): Promise<Response> {
try {
const data = await this._service.activation(body.id, body.status);
let param = DataStatusEnum.INACTIVE
if ( body.status === true){
param=DataStatusEnum.ACTIVE
}
const data = await this._service.activation(body.id, param);
return res.status(HttpStatus.OK).json({
statusCode: HttpStatus.OK,
data,
Expand Down
44 changes: 2 additions & 42 deletions src/management/identities/identities-activation.service.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,6 @@
import { AbstractIdentitiesService } from '~/management/identities/abstract-identities.service';
import { Identities } from '~/management/identities/_schemas/identities.schema';
import { BadRequestException, HttpException } from '@nestjs/common';
import { DataStatusEnum } from '~/management/identities/_enums/data-status';
import { JobState } from '~/core/jobs/_enums/state.enum';


export class IdentitiesActivationService extends AbstractIdentitiesService {
public async activation(id: string, status: boolean) {
//recherche de l'identité
let identity: Identities = null;
let statusChanged = false;
try {
identity = await this.findById<Identities>(id);
} catch (error) {
throw new HttpException('Id not found', 400);
}
if (identity.lastBackendSync === null) {
throw new HttpException('Identity has never been synced', 400);
}
if (identity.dataStatus !== DataStatusEnum.DELETED) {
if (status) {
if (identity.dataStatus !== DataStatusEnum.ACTIVE) {
identity.dataStatus = DataStatusEnum.ACTIVE;
statusChanged = true;
}
} else {
if (identity.dataStatus !== DataStatusEnum.INACTIVE) {
identity.dataStatus = DataStatusEnum.INACTIVE;
statusChanged = true;
}
}
} else {
throw new BadRequestException('Identity is in status deleted');
}
//sauvegarde de l'identité
if (statusChanged) {
// le dataStaus à changé on envoye l info aux backend et on enregistre l identité
const result = await this.backends.activationIdentity(identity._id.toString(), status);
if (result.state === JobState.COMPLETED) {
await super.update(identity._id, identity);
} else {
throw new HttpException('Backend failed', 400);
}
}
}

}
51 changes: 51 additions & 0 deletions src/management/identities/identities-forcepassword.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { AbstractController } from '~/_common/abstracts/abstract.controller';
import { IdentitiesActivationService } from '~/management/identities/identities-activation.service';
import { ApiOperation, ApiResponse, ApiTags} from '@nestjs/swagger';
import { Body, Controller, HttpStatus, Post, Res} from '@nestjs/common';
import { Response } from 'express';
import { ActivationDto } from '~/management/identities/_dto/_parts/activation-dto';
import {ForcePasswordDto} from "~/management/identities/_dto/force-password-dto";
import {IdentitiesForcepasswordService} from "~/management/identities/identities-forcepassword.service";
import {NeedChangePasswordDto} from "~/management/identities/_dto/need-change-password.dto";

@ApiTags('management/identities')
@Controller('identities')
export class IdentitiesForcePasswordController extends AbstractController {
public constructor(protected readonly _service: IdentitiesForcepasswordService) {
super();
}
@Post('forcepassword')
@ApiOperation({ summary: 'force le mot de passe de l identite' })
@ApiResponse({ status: HttpStatus.OK })
public async forcePassword(@Res() res: Response, @Body() body: ForcePasswordDto): Promise<Response> {
try {
const data = await this._service.forcePassword(body.id, body.newPassword);
return res.status(HttpStatus.OK).json({
statusCode: HttpStatus.OK,
data,
});
} catch (error) {
return res.status(HttpStatus.BAD_REQUEST).json({
statusCode: HttpStatus.BAD_REQUEST,
message: error.message,
});
}
}
@Post('needtochangepassword')
@ApiOperation({ summary: "force l'utilisateur a changer son mot de passe" })
@ApiResponse({ status: HttpStatus.OK })
public async needToChangePassword(@Res() res: Response, @Body() body: NeedChangePasswordDto): Promise<Response> {
try {
const data = await this._service.needToChangePassword(body.id)
return res.status(HttpStatus.OK).json({
statusCode: HttpStatus.OK,
data,
});
} catch (error) {
return res.status(HttpStatus.BAD_REQUEST).json({
statusCode: HttpStatus.BAD_REQUEST,
message: error.message,
});
}
}
}
84 changes: 84 additions & 0 deletions src/management/identities/identities-forcepassword.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import {AbstractIdentitiesService} from '~/management/identities/abstract-identities.service';
import {Identities} from '~/management/identities/_schemas/identities.schema';
import {BadRequestException, HttpException, Injectable} from '@nestjs/common';
import {DataStatusEnum} from '~/management/identities/_enums/data-status';
import {ActionType} from "~/core/backends/_enum/action-type.enum";


@Injectable()
export class IdentitiesForcepasswordService extends AbstractIdentitiesService {

public async forcePassword(id: string, newPassword: string) {
//recherche de l'identité
let identity: Identities = null;
try {
identity = await this.findById<Identities>(id);
} catch (error) {
throw new HttpException('Id not found', 400);
}
if (identity.lastBackendSync === null) {
throw new HttpException('Identity has never been synced', 400);
}
if (identity.dataStatus === DataStatusEnum.DELETED) {
throw new BadRequestException('Identity is in status deleted');
}
//changement du password check de la policy
if ((await this.passwdAdmService.checkPolicies(newPassword)) === false) {
throw new BadRequestException({
message: 'Une erreur est survenue : Le mot de passe ne respecte pas la politique des mots de passe',
error: 'Bad Request',
statusCode: 400,
});
}
//ok on envoie le changement de mdp
try{
const [_, response] = await this.backends.executeJob(
ActionType.IDENTITY_PASSWORD_RESET,
identity._id,
{ uid: identity.inetOrgPerson.uid, newPassword: newPassword, ...identity.toJSON() },
{
async: false,
timeoutDiscard: true,
disableLogs: false,
switchToProcessing: false,
updateStatus: false,
},
);
if (response?.status === 0) {
//activation de l'identité
await this.activation(id,DataStatusEnum.ACTIVE)
return [_, response];
}
}catch (e) {
this.logger.error('Error while reseting password. ' + e + ` (uid=${identity.inetOrgPerson.uid})`);
throw new BadRequestException(
'Une erreur est survenue : Tentative de réinitialisation de mot de passe impossible',
);
}
}
public async needToChangePassword(id: string){
let identity: Identities = null;
try {
identity = await this.findById<Identities>(id);
} catch (error) {
throw new HttpException('Id not found', 400);
}
if (identity.lastBackendSync === null) {
throw new HttpException('Identity has never been synced', 400);
}
if (identity.dataStatus === DataStatusEnum.DELETED) {
throw new BadRequestException('Identity is in status deleted');
}
if (identity.dataStatus === DataStatusEnum.INACTIVE) {
throw new BadRequestException('Identity is in status disabled');
}
//desactivation du compte
try{
await this.activation(id,DataStatusEnum.PASSWORDNEEDTOBECHANGED)
}catch{
throw new BadRequestException('Error changing status');
}

}

}
Loading
Loading