export function updateOrgMemberRole()

in controlplane/src/core/bufservices/user/updateOrgMemberRole.ts [15:198]


export function updateOrgMemberRole(
  opts: RouterOptions,
  req: UpdateOrgMemberRoleRequest,
  ctx: HandlerContext,
): Promise<PlainMessage<UpdateOrgMemberRoleResponse>> {
  let logger = getLogger(ctx, opts.logger);

  return handleError<PlainMessage<UpdateOrgMemberRoleResponse>>(ctx, logger, async () => {
    const authContext = await opts.authenticator.authenticate(ctx.requestHeader);
    logger = enrichLogger(ctx, logger, authContext);

    const orgRepo = new OrganizationRepository(logger, opts.db, opts.billingDefaultPlanId);
    const oidcRepo = new OidcRepository(opts.db);
    const auditLogRepo = new AuditLogRepository(opts.db);

    const org = await orgRepo.byId(authContext.organizationId);
    if (!org) {
      return {
        response: {
          code: EnumStatusCode.ERR_NOT_FOUND,
          details: `Organization not found`,
        },
      };
    }

    // fetching the user who is updating the other member's role.
    const user = await orgRepo.getOrganizationMember({
      organizationID: authContext.organizationId,
      userID: authContext.userId || req.userID,
    });

    if (!user) {
      return {
        response: {
          code: EnumStatusCode.ERR,
          details: 'User is not a part of this organization.',
        },
      };
    }

    // non admins cannot update the role of an org member
    if (!user.roles.includes('admin')) {
      return {
        response: {
          code: EnumStatusCode.ERR,
          details: 'User does not have the permissions to update the role of an organization member.',
        },
      };
    }

    // fetching the user whose role is being updated.
    const orgMember = await orgRepo.getOrganizationMember({
      organizationID: authContext.organizationId,
      userID: req.orgMemberUserID,
    });

    if (!orgMember) {
      return {
        response: {
          code: EnumStatusCode.ERR,
          details: 'User is not a part of this organization.',
        },
      };
    }

    await opts.keycloakClient.authenticateClient();

    const users = await opts.keycloakClient.client.users.find({
      realm: opts.keycloakRealm,
      email: orgMember.email,
      exact: true,
    });

    if (users.length === 0) {
      return {
        response: {
          code: EnumStatusCode.ERR,
          details: 'User does not exist.',
        },
      };
    }

    const provider = await oidcRepo.getOidcProvider({ organizationId: authContext.organizationId });

    if (provider) {
      // checking if the user has logged in using the sso
      const ssoUser = await opts.keycloakClient.client.users.find({
        realm: opts.keycloakRealm,
        email: orgMember.email,
        exact: true,
        idpAlias: provider.alias,
      });

      if (ssoUser.length > 0) {
        return {
          response: {
            code: EnumStatusCode.ERR,
            details: 'User has logged in using the OIDC provider. Please update the role using the provider.',
          },
        };
      }
    }

    const organizationGroups = await opts.keycloakClient.client.groups.find({
      max: 1,
      search: org.slug,
      realm: opts.keycloakRealm,
      briefRepresentation: false,
    });

    if (organizationGroups.length === 0) {
      throw new Error(`Organization group '${org.slug}' not found`);
    }

    const userRoles = await orgRepo.getOrganizationMemberRoles({
      userID: orgMember.userID,
      organizationID: authContext.organizationId,
    });
    const highPriorityRole = getHighestPriorityRole({ userRoles });
    if (highPriorityRole === req.role) {
      return {
        response: {
          code: EnumStatusCode.OK,
        },
      };
    }

    const organizationSubGroups = await opts.keycloakClient.fetchAllSubGroups({
      realm: opts.keycloakRealm,
      kcGroupId: organizationGroups[0].id!,
    });

    const targetGroup = organizationSubGroups.find((group) => group.name === req.role);
    if (!targetGroup) {
      throw new Error(`Invalid role ${req.role}`);
    }

    // deleting current roles
    for (const role of userRoles) {
      const childGroup = organizationSubGroups.find((group) => group.name === role);
      if (!childGroup) {
        continue;
      }

      await opts.keycloakClient.client.users.delFromGroup({
        id: users[0].id!,
        realm: opts.keycloakRealm,
        groupId: childGroup.id!,
      });
    }

    await opts.keycloakClient.client.users.addToGroup({
      id: users[0].id!,
      realm: opts.keycloakRealm,
      groupId: targetGroup.id!,
    });

    await orgRepo.updateUserRole({
      orgMemberID: orgMember.orgMemberID,
      role: req.role as MemberRole,
    });

    await auditLogRepo.addAuditLog({
      organizationId: authContext.organizationId,
      auditAction: 'member_role.updated',
      action: 'updated',
      actorId: authContext.userId,
      auditableDisplayName: req.role,
      auditableType: 'member_role',
      actorDisplayName: authContext.userDisplayName,
      apiKeyName: authContext.apiKeyName,
      targetId: orgMember.userID,
      targetType: 'user',
      targetDisplayName: orgMember.email,
      actorType: authContext.auth === 'api_key' ? 'api_key' : 'user',
    });

    return {
      response: {
        code: EnumStatusCode.OK,
      },
    };
  });
}