Advertisement
Guest User

Untitled

a guest
Jul 18th, 2022
101
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import { head, isEmpty, keys, mapKeys, omit, pick, uniqBy } from 'lodash';
  2. import {
  3.   actionsColumns,
  4.   actionsTable,
  5.   roleActionsColumns,
  6.   roleActionsTable,
  7.   rolesColumns,
  8.   rolesTable,
  9.   userRolesColumns,
  10.   userRolesTable,
  11. } from './tables';
  12.  
  13. export const tableName = userRolesTable;
  14. export const columnsByProperties = userRolesColumns;
  15.  
  16. const addRolePrefix = (string) => `role_${string}`;
  17. const removeRolePrefix = (string) => string.substring('role_'.length);
  18.  
  19. const prefixedRolesColumnsByProperties = mapKeys(rolesColumns, (v, k) =>
  20.   addRolePrefix(k)
  21. );
  22. const roleSelectColumns = selectColumnsAsProperties(
  23.   prefixedRolesColumnsByProperties,
  24.   rolesTable
  25. );
  26. const actionSelectColumns = selectColumnsAsProperties(
  27.   actionsColumns,
  28.   actionsTable
  29. );
  30.  
  31. function rowToRoleAssignment(row) {
  32.   if (!row) {
  33.     return undefined;
  34.   }
  35.  
  36.   return {
  37.     id: row.id,
  38.     scope: row.scope,
  39.     userId: row.userId,
  40.     role: mapKeys(pick(row, keys(prefixedRolesColumnsByProperties)), (v, k) =>
  41.       removeRolePrefix(k)
  42.     ),
  43.   };
  44. }
  45.  
  46. class UserRoles extends DAO {
  47.   // returns query builder prepared for selecting assignments with roles. Note
  48.   // that the .select() must still be called for the final query to work.
  49.   withRole(filter) {
  50.     // since we're joining we need to use the fully qualified column name.
  51.     const whereClause = mapKeys(
  52.       this.prepare(omit(filter, ['scope'])),
  53.       (v, k) => `${tableName}.${k}`
  54.     );
  55.     const query = this.tx(this.tableName)
  56.       .leftJoin(rolesTable, columnsByProperties.roleId, `${rolesTable}.id`)
  57.       .where(whereClause);
  58.  
  59.     if (filter.scope) {
  60.       query.where('scope', '@>', filter.scope);
  61.     }
  62.     return query;
  63.   }
  64.  
  65.   actionsJoinQuery(filter) {
  66.     // since we're joining we need to use the fully qualified column name.
  67.     const whereClause = mapKeys(
  68.       this.prepare(filter),
  69.       (v, k) => `${tableName}.${k}`
  70.     );
  71.     return this.tx(this.tableName)
  72.       .distinct()
  73.       .leftJoin(
  74.         roleActionsTable,
  75.         `${tableName}.${columnsByProperties.roleId}`,
  76.         `${roleActionsTable}.${roleActionsColumns.roleId}`
  77.       )
  78.       .leftJoin(
  79.         actionsTable,
  80.         `${roleActionsTable}.${roleActionsColumns.actionId}`,
  81.         `${actionsTable}.id`
  82.       )
  83.       .where(whereClause);
  84.   }
  85.  
  86.   /**
  87.    * Returns the users roles with the full role already resolved.
  88.    *
  89.    * @param {object} filter
  90.    */
  91.   selectWithRole(filter = {}) {
  92.     return this.withRole(filter)
  93.       .select([
  94.         ...roleSelectColumns,
  95.         'scope',
  96.         'user_id as userId',
  97.         'user_roles.id as id',
  98.         'user_roles.role_id as roleId',
  99.       ])
  100.       .then((results) => results.map(rowToRoleAssignment));
  101.   }
  102.  
  103.   /**
  104.    * Count the number of user roles matching the given filter.
  105.    *
  106.    * @param {object} filter
  107.    */
  108.   count(filter = {}) {
  109.     return this.withRole(filter)
  110.       .count('*')
  111.       .then(head)
  112.       .then((row) => Number(row.count));
  113.   }
  114.  
  115.   /**
  116.    * Count the number of user matching the given filter.
  117.    *
  118.    * @param {object} filter
  119.    */
  120.   countUniqueUsers(filter = {}) {
  121.     const subQuery = this.withRole(filter);
  122.     return this.tx
  123.       .count('*')
  124.       .from(subQuery.distinct(columnsByProperties.userId).as('users'))
  125.       .then(head)
  126.       .then((row) => Number(row.count));
  127.   }
  128.  
  129.   /**
  130.    * Returns the first role assignment matching the given filter.
  131.    *
  132.    * @param {object} filter
  133.    */
  134.   selectFirstWithRole(filter = {}) {
  135.     return this.withRole(filter)
  136.       .first([...roleSelectColumns, 'user_id as userId', 'user_roles.id as id'])
  137.       .then(rowToRoleAssignment);
  138.   }
  139.  
  140.   /**
  141.    * Select all actions in any role matching the filter.
  142.    */
  143.   selectActions(filter = {}) {
  144.     return this.actionsJoinQuery(filter)
  145.       .select(actionSelectColumns)
  146.       .orderBy('name');
  147.   }
  148.  
  149.   studyUsers({ filter, pagination = {} }) {
  150.     const builder = this.tx('user_roles').select(['user_id AS id']);
  151.  
  152.     const { studyId, siteId, roleIds, roleNames, userIds } = filter;
  153.     if (studyId) {
  154.       switch (filter.studyIdRestriction) {
  155.         // Only one ID allowed. Users with more ids or * are excluded.
  156.         case 'SINGLE':
  157.           builder.whereRaw("scope ->> 'studies' = ?", [`["${studyId}"]`]);
  158.           break;
  159.  
  160.         // Only one ID or * allowed. Users with more ids are excluded.
  161.         case 'SINGLE_OR_WILDCARD':
  162.           builder.where((qbStudy) =>
  163.             qbStudy
  164.               .whereRaw("scope ->> 'studies' = ?", [`["${studyId}"]`])
  165.               .orWhereRaw(`scope ->> 'studies' = '["*"]'`)
  166.           );
  167.           break;
  168.  
  169.         // Wildcard * is not allowed. However users with multiple IDs are included.
  170.         case 'NO_WILDCARD':
  171.           builder.where('scope', '@>', { studies: Array.of(studyId) });
  172.           break;
  173.  
  174.         // No restrictions (= single id, multiple ids, wildcard included)
  175.         case 'NONE':
  176.         default:
  177.           builder.where((qbStudy) =>
  178.             qbStudy
  179.               .where('scope', '@>', { studies: Array.of(studyId) })
  180.               .orWhere('scope', '@>', { studies: Array.of('*') })
  181.           );
  182.           break;
  183.       }
  184.     }
  185.     if (siteId) {
  186.       switch (filter.siteIdRestriction) {
  187.         // Only one ID allowed. Users with more ids or * are excluded.
  188.         case 'SINGLE':
  189.           builder.whereRaw("scope ->> 'sites' = ?", [`["${siteId}"]`]);
  190.           break;
  191.  
  192.         // Only one ID or * allowed. Users with more ids are excluded.
  193.         case 'SINGLE_OR_WILDCARD':
  194.           builder.where((qbStudy) =>
  195.             qbStudy
  196.               .whereRaw("scope ->> 'sites' = ?", [`["${siteId}"]`])
  197.               .orWhereRaw(`scope ->> 'sites' = '["*"]'`)
  198.           );
  199.           break;
  200.  
  201.         // Wildcard * is not allowed. However users with multiple IDs are included.
  202.         case 'NO_WILDCARD':
  203.           builder.where('scope', '@>', { sites: Array.of(siteId) });
  204.           break;
  205.  
  206.         // No restrictions (= single id, multiple ids, wildcard included)
  207.         case 'NONE':
  208.         default:
  209.           builder.where((qbStudy) =>
  210.             qbStudy
  211.               .where('scope', '@>', { sites: Array.of(siteId) })
  212.               .orWhere('scope', '@>', { sites: Array.of('*') })
  213.           );
  214.           break;
  215.       }
  216.     }
  217.     if (!isEmpty(roleIds)) {
  218.       builder.whereIn(columnsByProperties.roleId, roleIds);
  219.     }
  220.     if (!isEmpty(userIds)) {
  221.       builder.whereIn(columnsByProperties.userId, userIds);
  222.     }
  223.     if (!isEmpty(roleNames)) {
  224.       builder
  225.         .leftJoin(rolesTable, columnsByProperties.roleId, `${rolesTable}.id`)
  226.         .whereIn(`${rolesTable}.${rolesColumns.name}`, roleNames);
  227.     }
  228.  
  229.     const { skip, limit } = pagination;
  230.     if (skip) builder.offset(skip);
  231.     if (limit) builder.limit(limit);
  232.  
  233.     return builder.then((x) => uniqBy(x, 'id'));
  234.   }
  235.  
  236.   /**
  237.    * Select all actions in any role matching the filter.
  238.    */
  239.   countActions(filter = {}) {
  240.     return this.actionsJoinQuery(filter)
  241.       .countDistinct('actions.id')
  242.       .then(head)
  243.       .then((row) => Number(row.count));
  244.   }
  245.  
  246.   insertAll({ userId, assignments }) {
  247.     return this.tx(tableName)
  248.       .returning(this.selectColumns)
  249.       .insert(
  250.         assignments.map((assignment) => this.prepare({ userId, ...assignment }))
  251.       );
  252.   }
  253. }
  254.  
  255. export default function userRoles(tx) {
  256.   return new UserRoles({ tx, tableName, columnsByProperties });
  257. }
  258.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement