feat: endpoint for deleting oAuth users & oAuth users returned data (#12912)
* feat: delete oAuth users * check if access token matches userId in parameter * driveby: return only user id and email in oauth users endpoints
This commit is contained in:
parent
d4c946a0d6
commit
2df15d1d0a
|
@ -2,7 +2,10 @@ import { bootstrap } from "@/app";
|
|||
import { AppModule } from "@/app.module";
|
||||
import { HttpExceptionFilter } from "@/filters/http-exception.filter";
|
||||
import { PrismaExceptionFilter } from "@/filters/prisma-exception.filter";
|
||||
import { CreateUserResponse } from "@/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller";
|
||||
import {
|
||||
CreateUserResponse,
|
||||
UserReturned,
|
||||
} from "@/modules/oauth-clients/controllers/oauth-client-users/oauth-client-users.controller";
|
||||
import { CreateUserInput } from "@/modules/users/inputs/create-user.input";
|
||||
import { UpdateUserInput } from "@/modules/users/inputs/update-user.input";
|
||||
import { UsersModule } from "@/modules/users/users.module";
|
||||
|
@ -49,6 +52,9 @@ describe("OAuth Client Users Endpoints", () => {
|
|||
it(`/PUT/:id`, () => {
|
||||
return request(app.getHttpServer()).put("/api/v2/oauth-clients/100/users/200").expect(401);
|
||||
});
|
||||
it(`/DELETE/:id`, () => {
|
||||
return request(app.getHttpServer()).delete("/api/v2/oauth-clients/100/users/200").expect(401);
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
@ -146,7 +152,7 @@ describe("OAuth Client Users Endpoints", () => {
|
|||
.set("Authorization", `Bearer ${postResponseData.accessToken}`)
|
||||
.expect(200);
|
||||
|
||||
const responseBody: ApiSuccessResponse<Omit<User, "password">> = response.body;
|
||||
const responseBody: ApiSuccessResponse<UserReturned> = response.body;
|
||||
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
|
@ -170,16 +176,16 @@ describe("OAuth Client Users Endpoints", () => {
|
|||
expect(responseBody.data.email).toEqual(userUpdatedEmail);
|
||||
});
|
||||
|
||||
it(`/DELETE/:id`, () => {
|
||||
return request(app.getHttpServer())
|
||||
.delete(`/api/v2/oauth-clients/${oAuthClient.id}/users/${postResponseData.user.id}`)
|
||||
.set("Authorization", `Bearer ${postResponseData.accessToken}`)
|
||||
.expect(200);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
if (postResponseData?.user.id) {
|
||||
await userRepositoryFixture.delete(postResponseData.user.id);
|
||||
}
|
||||
if (oAuthClient) {
|
||||
await oauthClientRepositoryFixture.delete(oAuthClient.id);
|
||||
}
|
||||
if (organization) {
|
||||
await teamRepositoryFixture.delete(organization.id);
|
||||
}
|
||||
|
||||
await app.close();
|
||||
});
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
Param,
|
||||
Put,
|
||||
BadRequestException,
|
||||
Delete,
|
||||
} from "@nestjs/common";
|
||||
import { User } from "@prisma/client";
|
||||
|
||||
|
@ -61,7 +62,10 @@ export class OAuthClientUsersController {
|
|||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
data: {
|
||||
user,
|
||||
user: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
},
|
||||
accessToken: accessToken,
|
||||
refreshToken: refreshToken,
|
||||
},
|
||||
|
@ -74,9 +78,9 @@ export class OAuthClientUsersController {
|
|||
async getUserById(
|
||||
@GetUser("id") accessTokenUserId: number,
|
||||
@Param("userId") userId: number
|
||||
): Promise<ApiResponse<Partial<User>>> {
|
||||
): Promise<ApiResponse<UserReturned>> {
|
||||
if (accessTokenUserId !== userId) {
|
||||
throw new BadRequestException("You can only access your own user data.");
|
||||
throw new BadRequestException("userId parameter does not match access token");
|
||||
}
|
||||
|
||||
const user = await this.userRepository.findById(userId);
|
||||
|
@ -84,7 +88,13 @@ export class OAuthClientUsersController {
|
|||
throw new NotFoundException(`User with ID ${userId} not found.`);
|
||||
}
|
||||
|
||||
return { status: SUCCESS_STATUS, data: user };
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
data: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@Put("/:userId")
|
||||
|
@ -94,16 +104,59 @@ export class OAuthClientUsersController {
|
|||
@GetUser("id") accessTokenUserId: number,
|
||||
@Param("userId") userId: number,
|
||||
@Body() body: UpdateUserInput
|
||||
): Promise<ApiResponse<Partial<User>>> {
|
||||
): Promise<ApiResponse<UserReturned>> {
|
||||
if (accessTokenUserId !== userId) {
|
||||
throw new BadRequestException("You can only update your own user data.");
|
||||
throw new BadRequestException("userId parameter does not match access token");
|
||||
}
|
||||
|
||||
this.logger.log(`Updating user with ID ${userId}: ${JSON.stringify(body, null, 2)}`);
|
||||
|
||||
const user = await this.userRepository.update(userId, body);
|
||||
return { status: SUCCESS_STATUS, data: user };
|
||||
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
data: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@Delete("/:userId")
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@UseGuards(AccessTokenGuard)
|
||||
async deleteUser(
|
||||
@GetUser("id") accessTokenUserId: number,
|
||||
@Param("userId") userId: number
|
||||
): Promise<ApiResponse<UserReturned>> {
|
||||
if (accessTokenUserId !== userId) {
|
||||
throw new BadRequestException("userId parameter does not match access token");
|
||||
}
|
||||
|
||||
this.logger.log(`Deleting user with ID: ${userId}`);
|
||||
|
||||
const existingUser = await this.userRepository.findById(userId);
|
||||
|
||||
if (!existingUser) {
|
||||
throw new NotFoundException(`User with ${userId} does not exist`);
|
||||
}
|
||||
|
||||
if (existingUser.username) {
|
||||
throw new BadRequestException("Cannot delete a non manually-managed user");
|
||||
}
|
||||
|
||||
const user = await this.userRepository.delete(userId);
|
||||
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
data: {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export type CreateUserResponse = { user: Partial<User>; accessToken: string; refreshToken: string };
|
||||
export type UserReturned = Pick<User, "id" | "email">;
|
||||
|
||||
export type CreateUserResponse = { user: UserReturned; accessToken: string; refreshToken: string };
|
||||
|
|
Loading…
Reference in New Issue
Block a user