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 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 46x 46x 46x 46x 46x 46x 40x 40x 40x 40x 160x 160x 160x 104x 56x 56x 56x 56x 56x 8x 8x 48x 40x 40x 48x 48x 48x 48x 40x | import { HookContext, Application } from "@feathersjs/feathers"; import _isEqual from "lodash/isEqual"; import _pick from "lodash/pick"; import _isEmpty from "lodash/isEmpty"; import "@feathersjs/transport-commons"; import { subject } from "@casl/ability"; import { makeOptions, getAbility } from "./channels.utils"; import getModelName from "../utils/getModelName"; import hasRestrictingFields from "../utils/hasRestrictingFields"; import { Channel, RealTimeConnection } from "@feathersjs/transport-commons/lib/channels/channel/base"; import { ChannelOptions } from "../types"; interface ConnectionsPerField { fields: false | string[], connections: RealTimeConnection[] } export default (app: Application, data: Record<string, unknown>, context: HookContext, options?: Partial<ChannelOptions>): Channel|Channel[] => { options = makeOptions(app, options); const { channelOnError, activated } = options; const modelName = getModelName(options.modelName, context); Iif (!activated || !modelName) { return (!channelOnError) ? new Channel() : app.channel(channelOnError); } const { channels } = app; //return app.channel(channels); const allConnections = app.channel(channels).connections; const dataToTest = subject(modelName, data); Iif (!options.restrictFields) { // return all fields for allowed let connections = allConnections .filter(connection => { const ability = getAbility(app, data, connection, context, options); return ability && ability.can("get", dataToTest); }); connections = [...new Set(connections)]; return new Channel(connections, data); } else { // filter by restricted Fields const connectionsPerFields: ConnectionsPerField[] = []; for (let i = 0, n = allConnections.length; i < n; i++) { const connection = allConnections[i]; const { ability } = connection; if (!ability || !ability.can("get", dataToTest)) { // connection cannot read item -> don't send data continue; } const availableFields = (!options?.availableFields) ? undefined : (typeof options.availableFields === "function") ? options.availableFields(context) : options.availableFields; const fields = hasRestrictingFields(ability, "get", dataToTest, { availableFields }); // if fields is true or fields is empty array -> full restriction Iif (fields && (fields === true || fields.length === 0)) { continue; } const connField = connectionsPerFields.find(x => _isEqual(x.fields, fields)); if (connField) { Iif (connField.connections.indexOf(connection) !== -1) { // connection is already in array -> skip continue; } connField.connections.push(connection); } else { connectionsPerFields.push({ connections: [connection], fields: fields as string[] | false }); } } 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; Eif (!_isEmpty(restrictedData)) { channels.push(new Channel(connections, restrictedData)); } } return channels.length === 1 ? channels[0] : channels; } }; |