All files / lib/channels getChannelsWithReadAbility.ts

0% Statements 0/38
0% Branches 0/22
0% Functions 0/3
0% Lines 0/33

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74                                                                                                                                                   
import { HookContext, Application } from "@feathersjs/feathers";
import _isEqual from "lodash/isEqual";
import _pick from "lodash/pick";
import "@feathersjs/transport-commons";
 
import makeDefaultOptions from "./makeDefaultOptions";
import hasRestrictingFields from "../utils/hasRestrictingFields";
 
import { FeathersCaslChannelOptions } from "../types";
import { Channel, RealTimeConnection } from "@feathersjs/transport-commons/lib/channels/channel/base";
 
export default (app: Application, data: unknown, context: HookContext, options?: FeathersCaslChannelOptions): Channel|Channel[] => {
  options = makeDefaultOptions(options);
  const { channelOnError, activated } = options;
  const modelName = options.getModelName(context);
 
  if (!activated || !modelName) {
    return (!channelOnError) ? new Channel() : app.channel(channelOnError);
  }
 
  const { channels } = app;
  //return app.channel(channels);
  const allConnections = app.channel(channels).connections;
 
  const dataToTest = options.subjectHelper(modelName, data as Record<string, unknown>);
 
  if (!options.restrictFields) {
    // return all fields for allowed 
    let connections = allConnections
      .filter(connection => {
        const ability = options.getAbility(app, connection, data, context);
        return ability && ability.can("read", dataToTest);
      });
    connections = [...new Set(connections)];
    return new Channel(connections, data);
  } else {
    // filter by restricted Fields
    const connectionsPerFields: { fields: string[]|false, connections: RealTimeConnection[]}[] = [];
    for (let i = 0, n = allConnections.length; i < n; i++) {
      const connection = allConnections[i];
      const { ability } = connection;
      if (!ability || !ability.can("read", dataToTest)) {
        // connection cannot read item -> don't send data
        continue; 
      }
      const fields = hasRestrictingFields(ability, "read", dataToTest);
      const connField = connectionsPerFields.find(x => _isEqual(x.fields, fields));
      if (connField) {
        if (connField.connections.indexOf(connection) !== -1) {
          // connection is already in array -> skip
          continue; 
        }
        connField.connections.push(connection);
      } else {
        connectionsPerFields.push({
          connections: [connection],
          fields: fields
        });
      }
    }
    const channels: Channel[] = [];
    for (let i = 0, n = connectionsPerFields.length; i < n; i++) {
      const { fields, connections } = connectionsPerFields[i];
      const restrictedData = 
      (fields) 
        ? _pick(data, fields)
        : data;
      channels.push(new Channel(connections, restrictedData));
    }
    return channels.length === 1 
      ? channels[0]
      : channels;
  }
};