const getUserEditableOrgs = async (
  authInfo,
  scopes,
  validOrganisationTokenScopes = [ALL],
) => {
  let organisationIds = [];
  const tokenType = getTokenTypeFromAuthInfo(authInfo);
  if (tokenType === 'client') {
    if (includes(scopes, ALL)) {
      organisationIds.push(getOrgFromAuthInfo(authInfo));
    }
  } else if (tokenType === 'organisation') {
    const usersRoles = chain(get(authInfo, ['user', 'organisationSettings'], []))
      .map(({ roles }) => roles)
      .flatten()
      .value();

    const Role = getConnection().model('Role');
    const roles = await Role.find({
      _id: {
        $in: usersRoles
      },
      scopes: {
        $in: validOrganisationTokenScopes
      }
    }).select({
      organisation: 1
    }).lean();

    organisationIds = map(roles, ({ organisation }) => organisation);
  }

  return organisationIds;
};
        .then((dashboard) => {
          if (!dashboard) {
            return reject(new NotFoundError('dashboard', dashboardId));
          }
          const Visualisation = getConnection().model('Visualisation');
          const Organisation = getConnection().model('Organisation');
          const allVisualisationIds = map(dashboard.widgets, 'visualisation');
          const visualisationIds = filterNot(allVisualisationIds, isEmpty);

          return Promise.props({
            organisationId: dashboard.organisation,
            visualisations: Visualisation.find({
              _id: { $in: visualisationIds }
            }),
            organisations: Organisation.find({
              _id: dashboard.organisation
            }),
            shareable: dashboard && (find(dashboard.shareable, share => share._id.toString() === shareableId))
          });
        })
Beispiel #3
0
const checkNewUser = (user, next) => {
  if (user.isNew) {
    // check if email is taken
    const User = getConnection().model('User');
    User.findOne({ email: user.email }, (err, existingUser) => {
      if (existingUser) {
        // Create a unique merge of organisations from the existing user and the passed payload
        existingUser.organisations = _.unionBy(
          user.organisations,
          existingUser.organisations,
          _.toString
        );

        const existingSettings = existingUser.organisationSettings || [];
        // map across all the organisations on this user
        const newSettings = _.map(existingUser.organisations, (org) => {
          const orgId = org.toString();
          // if a setting exists for this organisation, return it
          const exists = _.find(existingSettings, setting =>
            setting.organisation.toString() === orgId
          );
          if (exists) {
            return exists;
          }

          // otherwise return a blank default
          return {
            organisation: orgId,
            scopes: []
          };
        });
        existingUser.organisationSettings = newSettings;
        next(existingUser);
      } else {
        user.ownerOrganisation = user.organisations[0];

        // send an email to new users
        user.createResetToken((token) => {
          // this method is used as part of the pre save hook, so no need to save the user at this time
          user.resetTokens.push(token);
          // send email to the new user
          sendNewUser(user, token);

          next(user);
        });
      }
    });
  } else {
    next(user);
  }
};
const getDashboardFilter = () => async ({ actionName, authInfo }) => {
  const scopes = getScopesFromAuthInfo(authInfo);
  const tokenType = getTokenTypeFromAuthInfo(authInfo);

  if (tokenType === 'dashboard') {
    if (actionName !== 'view' || !includes(scopes, VIEW_SHAREABLE_DASHBOARD)) {
      throw new NoAccessError();
    }

    const dashboardId = getDashboardFromAuthInfo(authInfo);

    const Dashboard = getConnection().model('Dashboard');
    const dashboards = await Dashboard.aggregate([
      { $match: { _id: objectId(dashboardId) } },
      { $unwind: '$widgets' },
      { $project: {
        _id: 0,
        visualisationId: '$widgets.visualisation'
      } }
    ]);

    return { _id: { $in: map(dashboards, item => objectId(item.visualisationId)) } };
  }
};
  },
  oldQuery: {
    type: String,
  },
  hasBeenMigrated: { type: Boolean, default: false },
  isPublic: { type: Boolean, default: false },
  fullDocument: { type: Boolean, default: false },
  sendAttachments: { type: Boolean, default: false }
});

schema.index({ organisation: 1, timestamp: -1, _id: 1 });

schema.plugin(scopeChecks);
schema.plugin(filterByOrg);
schema.plugin(addCRUDFunctions);
schema.plugin(timestamps);

schema.methods.getAuthHeaders = function schemeGetAuthHeaders() {
  return getAuthHeaders(this);
};

schema.methods.getHeaders = function getHeadersFromModel(statement) {
  return getHeaders(this, statement);
};

const StatementForwarding = getConnection().model(
  'StatementForwarding', schema, 'statementForwarding'
);

export default StatementForwarding;
import filterByOrg from 'lib/models/plugins/filterByOrg';
import scopeChecks from 'lib/models/plugins/scopeChecks';
import * as scopes from 'lib/constants/scopes';
import _ from 'lodash';
import addCRUDFunctions from 'lib/models/plugins/addCRUDFunctions';

const schema = new mongoose.Schema({
  organisation: { type: mongoose.Schema.ObjectId, ref: 'Organisation' },
  path: { type: String },
  hash: { type: String },
  value: { type: mongoose.Schema.Types.Mixed },
  display: { type: mongoose.Schema.Types.Mixed, default: null },
  valueType: { type: String },
  searchString: { type: String },
});

schema.readScopes = _.keys(scopes.USER_SCOPES);
schema.writeScopes = schema.readScopes;

schema.plugin(timestamps);
schema.plugin(scopeChecks);
schema.plugin(filterByOrg);

schema.plugin(addCRUDFunctions);
schema.index({ organisation: 1, path: 1, hash: 1 }, { unique: true });
schema.index({ organisation: 1, path: 1, updatedAt: -1, _id: 1 });

const QueryBuilderCache =
  getConnection().model('QueryBuilderCacheValue', schema, 'queryBuilderCacheValues');
export default QueryBuilderCache;
import logger from 'lib/logger';
import mongoose from 'mongoose';
import { getConnection } from 'lib/connections/mongoose';
import Dashboard, { schema } from 'lib/models/dashboard';

import { map } from 'lodash';

const objectId = mongoose.Types.ObjectId;

schema.set('strict', false);
const OldDashboardModel = getConnection().model('Dashboard', schema, 'dashboards');

const up = async () => {
  logger.info('Moving existing shared dashboards into new array format.');
  const dashboards = await Dashboard.find({}).lean().exec();

  const updatePromises = map(dashboards, async (dashboard) => {
    if (!dashboard.shareable) {
      dashboard.shareable = [];
    }

    const shareable = dashboard.shareable;
    shareable.unshift({
      title: '~ Shareable',
      filter: dashboard.filter,
      visibility: dashboard.visibility,
      validDomains: dashboard.validDomains,
      createdAt: new Date(),
    });

    return OldDashboardModel.updateOne(
const up = async () => {
  const connection = getConnection();
  createStatementIndexes(connection);
  createClientIndexes(connection);
  createActivityIndexes(connection);
};
Beispiel #9
0
    // If the user has no organisation settings, grab the owner org and attach its settings
    if (user.isNew) {
      Organisation.findOne({ _id: user.ownerOrganisation }, (err, org) => {
        if (err) {
          throw err;
        }

        // Set the settings into the user, the schema should trim any that are not allowed
        if (org) {
          user.ownerOrganisationSettings = org.settings;
        }

        // Carry on with password checks
        next();
      });
    }

    // Proceed with password checks
    next();
  });
});

schema.pre('save', function handlePostSave(next) {
  const user = this;
  preSavePasswordCheck(user, next);
});

const User = getConnection().model('User', schema, 'users');

export default User;
describe('asignIdentifierStatements', () => {
  const organisation = testId;
  const connection = getConnection();
  const personaService = getPersonaService();

  beforeEach('Set up people and statements for testing', async () => {
    await awaitReadyConnection(connection);
    await personaService.clearService();
  });

  afterEach('Clear db collections', async () => {
    await personaService.clearService();
  });

  it('should return true when identifier matches a statement', async () => {
    const ifi = {
      key: 'mbox',
      value: '*****@*****.**',
    };
    const { identifier } = await personaService.createUpdateIdentifierPersona({
      organisation: testId,
      personaName: 'Test person',
      ifi,
    });

    await connection.collection('statements').insert({
      organisation: new ObjectID(organisation),
      statement: {
        actor: { mbox: ifi.value }
      }
    });

    const hasStatement = await identifierHasStatements({
      organisation: testId,
      identifierId: identifier.id,
    });


    expect(hasStatement).to.equal(true);
  });

  it('should return false when identifier does not match a statement in the organisation', async () => {
    const otherOrg = new ObjectID();
    const ifi = {
      key: 'account',
      value: { homePage: 'http://test.com', name: 'testifi' },
    };
    const { identifier } = await personaService.createUpdateIdentifierPersona({
      organisation: testId,
      personaName: 'Test person',
      ifi,
    });

    await personaService.createUpdateIdentifierPersona({
      organisation: otherOrg,
      personaName: 'Test person',
      ifi,
    });

    await connection.collection('statements').insert({
      organisation: otherOrg,
      statement: {
        actor: { mbox: ifi.value }
      }
    });

    const hasStatement = await identifierHasStatements({
      organisation: testId,
      identifierId: identifier.id,
    });

    expect(hasStatement).to.equal(false);
  });
});
        message: 'Can only have one column as the name'
      }
    ]
    // {[columnName]: {
    //   columnName: { type: String },
    //   columnType: {
    //     type: String,
    //     enum: COLUMN_TYPES
    //   },
    //   relatedColumn: { // related column name, optional.
    //     type: String
    //   },
    //   primary: { // null or the order that we apply this as a primary key.
    //     type: Number
    //   }
    // }}
  }
});

schema.readScopes = keys(scopes.USER_SCOPES);
schema.writeScopes = schema.readScopes;

schema.plugin(timestamps);
schema.plugin(scopeChecks);
schema.plugin(filterByOrg);
schema.plugin(addCRUDFunctions);

schema.index({ organisation: 1, updatedAt: -1, _id: 1 });

export default getConnection().model('PersonasImport', schema, 'personasImports');
const up = async () => {
  const connection = getConnection();
  await connection.collection('personaidentifiers').drop();
};
  new Promise((resolve, reject) => {
    const dashboardId = getDashboardFromAuthInfo(authInfo);
    const shareableId = getShareableFromAuthInfo(authInfo);

    // if authentication has a dashboard scope, check that the query is in the dashboard's queries
    if (dashboardId) {
      const Dashboard = getConnection().model('Dashboard');
      return Dashboard.findById(dashboardId)
        .then((dashboard) => {
          if (!dashboard) {
            return reject(new NotFoundError('dashboard', dashboardId));
          }
          const Visualisation = getConnection().model('Visualisation');
          const Organisation = getConnection().model('Organisation');
          const allVisualisationIds = map(dashboard.widgets, 'visualisation');
          const visualisationIds = filterNot(allVisualisationIds, isEmpty);

          return Promise.props({
            organisationId: dashboard.organisation,
            visualisations: Visualisation.find({
              _id: { $in: visualisationIds }
            }),
            organisations: Organisation.find({
              _id: dashboard.organisation
            }),
            shareable: dashboard && (find(dashboard.shareable, share => share._id.toString() === shareableId))
          });
        })
        .then(({ organisationId, organisations, visualisations, shareable }) => {
          const organisation = organisations[0];
          const defaultTimezone = organisation.timezone || 'UTC';

          let immutPipeline = fromJS(pipeline); // pipeline to authenticate

          const immutPipelines = new List(visualisations) // pipelines from the visualisations
            .flatMap((visualisation) => {
              const axes2 = unflattenAxes(fromJS(visualisation.toObject()));
              const axes3 = deserialiseAxes(axes2, VISUALISE_AXES_PREFIX);

              const parsedQueries = new List(visualisation.filters).map((query) => {
                const parsedQuery = fromJS(JSON.parse(query));

                if (shareable) {
                  const and = immutPipeline.getIn([1, '$match', '$and']);

                  // filterMode === ANY
                  if (shareable.filterMode === ANY && and && and.size > 2) {
                    immutPipeline = immutPipeline.setIn([1, '$match', '$and'], and.slice(0, -1));
                  }

                  // filterMode === JWT
                  if (shareable.filterMode === JWT_SECURED && and && and.size > 2) {
                    immutPipeline = immutPipeline.setIn([1, '$match', '$and'], and.slice(0, -1));
                  }

                  return parsedQuery.set('$match', new Map({
                    $and: new List([
                      parsedQuery.get('$match', new Map()),
                      fromJS(JSON.parse(shareable.filter))
                    ])
                  }));
                }
                return fromJS(JSON.parse(query));
              });

              const timezone = visualisation.timezone || defaultTimezone;

              return pipelinesFromQueries(
                parsedQueries,
                axes3,
                visualisation.type,
                visualisation.previewPeriod,
                visualisation.id,
                timezone,
                visualisation.benchmarkingEnabled,
              );
            })
            .flatten(true);

          const pipelineMatch = immutPipelines.contains(immutPipeline);

          if (pipelineMatch) {
            return resolve(organisationId);
          }

          return reject(new UnauthorisedQueryError());
        });
    }
    const organisationId = getOrgFromAuthInfo(authInfo, pipeline);
    return resolve(organisationId);
  });
import mongoose from 'mongoose';
import { getConnection } from 'lib/connections/mongoose';

const schema = new mongoose.Schema({
  clientId: { type: mongoose.Schema.ObjectId, ref: 'Client' },
  accessToken: { type: String },
  createdAt: { type: Date },
  expireAt: { type: Date }
});

const OAuthToken = getConnection().model('OAuthToken', schema, 'oAuthTokens');
export default OAuthToken;
Beispiel #15
0
  organisation: {
    type: mongoose.Schema.ObjectId,
    ref: 'Organisation',
    index: true
  },
  name: { type: String },
  owner: { type: mongoose.Schema.Types.ObjectId, ref: 'User', index: true },
  projections: {
    type: [{ type: String }],
    default: [defaultProjection]
    // set doesn't work here
    // due to a dot notation issue with express-restify-mongoose and mixed type values
    // the value must be cast as a string on the client side before it is sent
    // get doesn't work here either
  },
  type: { type: String },
  rawMode: { type: Boolean, default: false },
  downloads: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Download' }],
  isPublic: { type: Boolean, default: false }
});

schema.readScopes = _.keys(scopes.USER_SCOPES);
schema.writeScopes = schema.readScopes;

schema.plugin(scopeChecks);
schema.plugin(filterByOrg);
schema.plugin(addCRUDFunctions);

const Export = getConnection().model('Export', schema, 'exports');
export default Export;
import mongoose from 'mongoose';
import { getConnection } from 'lib/connections/mongoose';
import timestamps from 'mongoose-timestamp';

const schema = new mongoose.Schema({
  dontShowRegistration: { type: Boolean, default: false },
  batchDeleteWindowUTCHour: { type: Number, default: null }, // UTC hour that the window should start at
  batchDeleteWindowUTCMinutes: { type: Number, default: null }, // UTC minute that the window should start at
  batchDeleteWindowDurationSeconds: { type: Number, default: 60 * 60 } // How long the window is open for in secs
});
schema.plugin(timestamps);

export default getConnection().model('SiteSettings', schema, 'siteSettings');
/* eslint-disable import/no-mutable-exports */
import { getConnection } from 'lib/connections/mongoose';
import { schema } from './statement';

const StatementSample = getConnection().model('StatementSample', schema, 'statementSamples');
export default StatementSample;
  isPublic: { type: Boolean, default: false },
  hasBeenMigrated: { type: Boolean, default: false },
});

schema.readScopes = keys(scopes.USER_SCOPES);
schema.writeScopes = schema.readScopes;

schema.plugin(timestamps);
schema.plugin(scopeChecks);
schema.plugin(filterByOrg);
schema.plugin(addCRUDFunctions);

const getDashboardFilter = (dashboardId, baseFilter = {}) => ({
  ...baseFilter,
  _id: objectId(dashboardId)
});

schema.statics.filterByAuth = async function filterByAuth(authInfo, filter) {
  const authScopes = getScopesFromAuthInfo(authInfo);
  const organisationId = getOrgFromAuthInfo(authInfo);
  const dashboardId = getDashboardFromAuthInfo(authInfo);
  const isSiteAdmin = includes(authScopes, scopes.SITE_ADMIN);
  if (isSiteAdmin && !organisationId && !dashboardId) return filter;
  if (organisationId) return await this.getOrgFilterFromAuth(authInfo, filter);
  if (dashboardId) return getDashboardFilter(dashboardId, filter);
  throw new Error('Could not validate auth');
};

const Dashboard = getConnection().model('Dashboard', schema, 'dashboards');
export default Dashboard;