File

src/iam/policy/policy.service.ts

Index

Properties
Methods

Constructor

constructor(prismaService: PrismaService, keycloakProvider: KeycloakProvider)
Parameters :
Name Type Optional
prismaService PrismaService No
keycloakProvider KeycloakProvider No

Methods

Async addResourceEntry
addResourceEntry(resourceType: string, resourceId: any)

this function adds entry to resource table

Parameters :
Name Type Optional Description
resourceType string No

eg: project, model, cost, etc.

resourceId any No

eg: 1, 2,etc.

Returns : Promise<object>

created resource object

checkPermission
checkPermission(permission, projectId, resource, action)

function to check permissions

Parameters :
Name Optional
permission No
projectId No
resource No
action No
Returns : any
Async createPolicy
createPolicy(policy: Policy)
Parameters :
Name Type Optional
policy Policy No
Returns : Promise<Policy>
Async deletePolicy
deletePolicy(where: Prisma.PolicyWhereUniqueInput)
Parameters :
Name Type Optional
where Prisma.PolicyWhereUniqueInput No
Returns : Promise<Policy>
Async getCredentials
getCredentials(username: string)
Parameters :
Name Type Optional
username string No
Returns : unknown
Async getOrCreateUser
getOrCreateUser(username: string, email: string)
Parameters :
Name Type Optional
username string No
email string No
Returns : unknown
Async getTuroPermissions
getTuroPermissions(userId: string)

this function gets turo permissions

Parameters :
Name Type Optional
userId string No
Returns : Promise<object>
Async getUserPermissions
getUserPermissions(userId: string)

this function gets permissions for user according to policies defined

Parameters :
Name Type Optional
userId string No
Returns : Promise<object>
Async getUserPolicies
getUserPolicies(userId: string)

this function to get policies applicable to user

Parameters :
Name Type Optional
userId string No
Returns : Promise<object>
Async removeResourceEntry
removeResourceEntry(resourceType: string, resourceId: any)

this function uses delete many to delete entry from resource table as prisma delete expects unique id in where clause

Parameters :
Name Type Optional Description
resourceType string No

eg: project, model, cost, etc.

resourceId any No

eg: 1, 2,etc.

Returns : Promise<object>

deleted resource entry object

Properties

policies
Type : object
Default value : {}
Private PROJECT_PUBLIC_ROLE_NAME
Default value : Roles.PROJECT_READONLY
Private TURO_ADMIN_ROLE_NAME
Default value : Roles.TURO_ADMIN
Private TURO_READONLY_ROLE_NAME
Default value : Roles.TURO_READONLY
import { Injectable, InternalServerErrorException } from "@nestjs/common";
import { Prisma } from "@prisma/client";
import { PrismaService } from "../../common/prisma/prisma.service";
import { Policy } from "./types/policy.interface";
import { KeycloakProvider } from "../../providers/keycloak/keycloak.provider";
import { Roles } from "../role/entity/role.entity";

@Injectable()
export class PolicyService {
  private PROJECT_PUBLIC_ROLE_NAME = Roles.PROJECT_READONLY;
  private TURO_READONLY_ROLE_NAME = Roles.TURO_READONLY;
  private TURO_ADMIN_ROLE_NAME = Roles.TURO_ADMIN;

  policies = {};

  constructor(
    private readonly prismaService: PrismaService,
    private readonly keycloakProvider: KeycloakProvider,
  ) {}

  async createPolicy(policy: Policy): Promise<Policy> {
    try {
      return (await this.prismaService.policy.create({
        data: policy,
      })) as unknown as Policy;
    } catch (error) {
      throw new InternalServerErrorException(error.message);
    }
  }

  async deletePolicy(where: Prisma.PolicyWhereUniqueInput): Promise<Policy> {
    try {
      return (await this.prismaService.policy.delete({
        where,
      })) as unknown as Policy;
    } catch (error) {
      throw new InternalServerErrorException(error.message);
    }
  }

  async getOrCreateUser(username: string, email: string) {
    return await this.keycloakProvider.getOrCreateUser(username, email);
  }

  async getCredentials(username: string) {
    return await this.keycloakProvider.getCredentials(
      KeycloakProvider.turoServicesClientId,
      username,
    );
  }

  /**
   * this function to get policies applicable to user
   * @param userId
   * @returns {Promise<object>}
   */
  async getUserPolicies(userId: string): Promise<object> {
    const _userId = userId.toLowerCase();
    const projectPolicyMap = {};

    const userTuroRoles = await this.prismaService.userTuroRole.findMany({
      where: {
        userId: _userId,
      },
    });

    const userBURoles = await this.prismaService.userBURole.findMany({
      where: {
        userId: _userId,
      },
      include: {
        bu: {
          include: {
            projects: true,
          },
        },
      },
    });

    const userProjectRoles = await this.prismaService.userProjectRole.findMany({
      where: {
        userId: _userId,
      },
    });

    const userProjects = userProjectRoles.map((role) => role.projectId);

    // get public projects ie not already in user projects
    const publicProjects = await this.prismaService.project.findMany({
      where: {
        id: {
          notIn: userProjects,
        },
        isPublic: true,
      },
    });

    const publicRole = await this.prismaService.role.findUnique({
      where: {
        name: this.PROJECT_PUBLIC_ROLE_NAME,
      },
    });

    publicProjects.forEach((project) => {
      userProjectRoles.push({
        id: null, //placeholder
        projectId: project.id,
        userId: _userId,
        roleId: publicRole.id,
      });
    });

    userBURoles.forEach((buRole) => {
      buRole.bu.projects.forEach((project) => {
        userProjectRoles.push({
          id: null, // placeholder
          roleId: buRole.roleId,
          projectId: project.id,
          userId: buRole.userId,
        });
      });
    });

    userTuroRoles.forEach((turoRole) => {
      userProjectRoles.push({
        id: null, // placeholder
        roleId: turoRole.roleId,
        projectId: 0,
        userId: turoRole.userId,
      });
    });

    const promises = userProjectRoles.map((userProjectRole) => {
      return new Promise<void>(async (resolve) => {
        const policies = await this.prismaService.policy.findMany({
          where: {
            roles: {
              some: {
                roleId: userProjectRole.roleId,
              },
            },
          },
        });
        if (userProjectRole.projectId in projectPolicyMap) {
          projectPolicyMap[userProjectRole.projectId].push(...policies);
        } else {
          projectPolicyMap[userProjectRole.projectId] = policies;
        }
        resolve();
      });
    });

    await Promise.all(promises);

    return projectPolicyMap;
  }

  /**
   * this function gets permissions for user
   * according to policies defined
   * @param userId
   * @returns {Promise<object>}
   */
  async getUserPermissions(userId: string): Promise<object> {
    const _userId = userId.toLowerCase();
    const projectPolicyMap = await this.getUserPolicies(_userId);
    const permissionsMap = {};

    for (const projectId in projectPolicyMap) {
      projectPolicyMap[projectId].forEach((policy) => {
        policy["statements"].forEach((statement) => {
          if (
            permissionsMap[projectId] &&
            statement["resource"] in permissionsMap[projectId]
          ) {
            permissionsMap[projectId][statement["resource"]][
              statement["action"]
            ] = true;
          } else if (permissionsMap[projectId]) {
            permissionsMap[projectId][statement["resource"]] = {
              [statement["action"]]: true,
            };
          } else {
            permissionsMap[projectId] = {
              [statement["resource"]]: {
                [statement["action"]]: true,
              },
            };
          }
        });
      });
    }

    return permissionsMap;
  }
  /**
   * this function adds entry to resource table
   * @param resourceType eg: project, model, cost, etc.
   * @param resourceId eg: 1, 2,etc.
   * @returns created resource object
   */
  async addResourceEntry(
    resourceType: string,
    resourceId: any,
  ): Promise<object> {
    return this.prismaService.resource.create({
      data: {
        name: `turo:${resourceType}:${resourceId}`,
      },
    });
  }

  /**
   * this function uses delete many to delete entry from resource
   * table as prisma delete expects unique id in where clause
   * @param resourceType eg: project, model, cost, etc.
   * @param resourceId eg: 1, 2,etc.
   * @returns deleted resource entry object
   */
  async removeResourceEntry(
    resourceType: string,
    resourceId: any,
  ): Promise<object> {
    return this.prismaService.resource.deleteMany({
      where: {
        name: `turo:${resourceType}:${resourceId}`,
      },
    });
  }

  /**
   * function to check permissions
   * @param permission
   * @param projectId
   * @param resource
   * @param action
   * @returns
   */
  checkPermission(permission, projectId, resource, action) {
    const projectPermission = permission[projectId];
    const globalPermission = permission["0"];

    const allowedPermissionList = [
      globalPermission?.["*"]?.["*"],
      globalPermission?.["*"]?.[action],
      globalPermission?.[resource]?.["*"],
      globalPermission?.[resource]?.[action],
      projectPermission?.["*"]?.["*"],
      projectPermission?.["*"]?.[action],
      projectPermission?.[resource]?.["*"],
      projectPermission?.[resource]?.[action],
    ];

    return allowedPermissionList.some((permission) => permission == true);
  }

  /**
   * this function gets turo permissions
   * @param userId
   * @returns {Promise<object>}
   */
  async getTuroPermissions(userId: string): Promise<object> {
    // fetch all user roles
    const _userId = userId.toLowerCase();
    const userRoles = await this.prismaService.userTuroRole.findMany({
      where: {
        userId: _userId,
      },
      include: {
        role: true,
      },
    });

    // check if user is turo admin
    const isTuroAdmin = userRoles.some(
      (role) => role.role.name === this.TURO_ADMIN_ROLE_NAME,
    );

    // check if user is turo readonly
    const isTuroReadOnly = userRoles.some(
      (role) => role.role.name === this.TURO_READONLY_ROLE_NAME,
    );

    return {
      isTuroAdmin,
      isTuroReadOnly,
    };
  }
}

results matching ""

    No results matching ""