postgresql - Postgres 使用 TypeORM SET 运行时变量,如何在调用之间的连接生命周期中保留变量

标签 postgresql typeorm

我有使用 GraphQL 的 NodeJS Web 服务器,使用 2 个连接。一个具有管理员访问权限,另一个具有增删改查访问权限。

底层 Postgres DB 有行级安全策略,即:

ALTER TABLE main.user ENABLE ROW LEVEL SECURITY;
CREATE POLICY user_isolation_policy ON main.user USING (id = current_setting('app.current_user_id')::UUID);

在登录用户之前,我需要从数据库获取他们的 id,然后在登录时在 Postgres session 中设置 current_user_id 变量。

但是,当我尝试取回用户时,我希望只取回登录的用户,而不是所有人 - 这就是使用 pgAdmin 的行为方式。但是,在这里我收到以下错误:

error: error: unrecognized configuration parameter "app.current_user_id"

以下是我如何登录用户:

@Resolver()
export class LoginResolver {
  @Mutation(() => LoginResponse)
  public async login(
    @Arg('email') email: string,
    @Arg('password') password: string,
    @Ctx() { res }: AppContext
  ): Promise<LoginResponse> {
    try {
      // get user from the admin repo so we can get their ID
      const userRepository = (await adminConnection).getRepository(User)
      const user = await userRepository.findOne({ where: { email } })
      if (!user) throw new Error('user not found')

      // using the api repo (not admin), set the variable
      User.getRepository().query(`SET app.current_user_id TO "${user.id}"`)
      
      const isValid = await bcrypt.compare(password, user.password)
      if (!isValid) throw new Error('incorrect password')
      if (!user.isConfirmed) throw new Error('email not confirmed')
      sendRefreshToken(res, user.createRefreshToken())
      return { token: user.createAccessToken() }
    } catch (error) {
      throw new Error(error)
    }
  }
}

以下是我尝试找回用户的方法:

@Resolver()
export class UsersResolver {
  @Authorized(UserRole.admin, UserRole.super)
  @Query(() => [User])
  public users(): Promise<User[]> {
    return User.find()
  }
}

请注意,如果我删除该策略,GraphQL 会正常运行,不会出现错误。

设置的变量不持久。如何在用户登录时保留变量?

最佳答案

这种方法对我有用:


import { EntityManager, getConnection, getConnectionManager, getManager } from "typeorm";
import { EventSubscriber, EntitySubscriberInterface, InsertEvent, UpdateEvent, RemoveEvent } from "typeorm";

@EventSubscriber()
export class CurrentUserSubscriber implements EntitySubscriberInterface {

    // get the userId from the current http request/headers/wherever you store it (currently I'm typeorm only, not as part of nest/express app)
    async setUserId(mng: EntityManager, userId: string) {
        await mng.query(`SELECT set_config('app.current_user_id', '${userId}', true);`)
    }

    async beforeInsert(event: InsertEvent<any>) {
        await this.setUserId(event.manager, 'myUserId');
    }
    async beforeUpdate(event: UpdateEvent<any>) {
        await this.setUserId(event.manager, 'myUserId');
    }
    async beforeRemove(event: RemoveEvent<any>) {
        await this.setUserId(event.manager, 'myUserId');
    }
}


不要忘记在 ormconfig.js 中配置 subscribers 属性,例如:

   "subscribers": [
      "src/subscribers/CurrentUserSubscriber.ts",
   ],

关于postgresql - Postgres 使用 TypeORM SET 运行时变量,如何在调用之间的连接生命周期中保留变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65944175/

相关文章:

python - SQLAlchemy 中表缺少 FROM 子句条目

typeorm - 在 Typeorm 中使用事务进行测试

node.js - 无法生成迁移

javascript - Nestjs/TypeORM无法连接到Docker MariaDB

node.js - TypeORM,如果存在值而不是空字符串,则在 `where` 中添加条件

postgresql - 使用 pg_stat_statements 可以聚合的查询高效地将可变数量的行插入 Postgres

php - Laravel:PHP Artisan Migrate 抛出 PDO 异常:找不到驱动程序 (Postgres)

mysql - SQL:返回具有匹配百分比计算列的用户表?

mongodb - typeorm mongo 全文搜索 - 按 $meta : "textScore" 排序

sql - 在rails或sql中克服 "ActiveRecord::StatementInvalid: PG::AmbiguousFunction"的方法。 (st_intersects 不是唯一的函数名称)