Skip to content

Commit 5607a8c

Browse files
committed
Merge branch 'master' into issue-522-persona-imports
2 parents 82c7b40 + 0c1350c commit 5607a8c

File tree

92 files changed

+2054
-31049
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+2054
-31049
lines changed

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ FS_REPO=local
191191
########
192192
# MISC #
193193
########
194+
#RESTRICT_CREATE_ORGANISATION=true
194195

195196
# Location of virus scanning binary (ClamAV - https://www.clamav.net/)
196197
#CLAMDSCAN_BINARY=/usr/bin/clamdscan

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ storage/**/*.csv
1212
api/logs/*
1313
dist/
1414
dump.rdb
15-
node_modules/
1615
node_modules
1716
npm-debug.log*
1817
pids/

CHANGELOG.md

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,41 @@ Format based on [Keep a Changelog](http://keepachangelog.com/)
99
### Security
1010
### Migrations
1111

12-
## [2.2.5]
12+
## [2.3.0]
13+
### Added
14+
- Multiple shareable links per dashboard (#1096)
15+
- Requires migration to be run - `yarn migrate`
16+
- Aggregations now can read from secondary members on replica set (#1095)
17+
- Sentinel Redis support (#1119)
18+
- New role to allow organisation creation (via site admin) (#1110)
19+
- Widgets now auto pick visualisation name when populated (#1126)
20+
### Security
21+
- Passwords can only be changed for the user logged in or by site admins (#1112)
22+
### Fixes
23+
- Unicode data now pulled from dependency (#1125)
24+
- Ensure order on personaIdentifier IFI values (fixes issue with multiple personaIdents for the same actor) (#1120)
25+
- Fix for personaIdentifier migration
26+
- Client can select more than 10 xAPI stores (#1130)
27+
- Server side validation of Statement Forward queries (#1138)
28+
- Statement forwards decode `&46;` in statement keys (#1134)
29+
- Fixed issue with hanging file imports on persona data (#1141)
30+
- Switch to `clamdscan` as primary AV scanner (#1141)
31+
- Requires updated .env settings - refer to .env.example
32+
### Performance and build
33+
- Webpack 3 - improved build speed (#1094)
34+
### Migrations
35+
**This update requires a migration which can be run using `yarn migrate`.**
1336

1437
## [2.2.4]
1538
### Fixes
16-
- Ensure statementForwarding query is valid json. (#1138)
39+
- Server side validation of Statement Forward queries (#1138)
1740
- Persona import errors if there are no iris. (#1140)
18-
- Workers handle errors on missing personas
19-
- Workers handle errors on invalid JSON in statement forward callbacks
41+
- Workers handle errors on missing personas (#1137)
42+
- Workers handle errors on invalid JSON in statement forward callbacks (#1137)
2043

2144
## [2.2.3]
2245
### Fixes
23-
- Parse persona ident and attribute queries
46+
- Persona Attribute and Identifier APIs now parse $oid values for `persona` filters (#1133)
2447

2548
## [2.2.2]
2649
### Fixes

UnicodeData.txt

Lines changed: 0 additions & 30592 deletions
This file was deleted.

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.2.5
1+
2.3.0

api/src/auth/jwt.js

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@ const payloadDefaults = ({
2828
provider = 'native',
2929
scopes = [],
3030
extensions = {},
31+
organisation = null,
3132
...others
3233
}) => ({
3334
provider,
3435
scopes,
3536
extensions,
37+
organisation,
3638
...others
3739
});
3840

@@ -43,11 +45,13 @@ const createJWT = ({
4345
filter,
4446
tokenType,
4547
tokenId,
46-
extensions
48+
shareableId = null,
49+
extensions,
50+
organisation = null
4751
}, opts = {
4852
expiresIn: '7d'
4953
}) => sign(
50-
{ userId, provider, scopes, tokenType, tokenId, extensions, filter },
54+
{ userId, provider, scopes, tokenType, tokenId, shareableId, extensions, filter, organisation },
5155
process.env.APP_SECRET,
5256
opts
5357
);
@@ -87,22 +91,28 @@ const createOrgJWT = async (user, organisationId, provider) => {
8791
return createJWT(orgTokenPayload);
8892
};
8993

90-
const createDashboardTokenPayload = async (dashboard, provider) => {
94+
const createDashboardTokenPayload = async (dashboard, shareableId, provider) => {
95+
if (!shareableId && dashboard.shareable.length > 0) {
96+
shareableId = dashboard.shareable[0]._id;
97+
}
98+
9199
const visualisationIds = getVisualisationIdsFromDashboard(dashboard);
92100
return payloadDefaults({
93101
provider,
94102
scopes: getDashboardScopes(dashboard),
95103
tokenType: 'dashboard',
96104
tokenId: String(dashboard._id),
97-
filter: JSON.parse(dashboard.filter),
105+
shareableId,
106+
organisation: String(dashboard.organisation),
107+
filter: {},
98108
extensions: {
99109
visualisationIds,
100110
}
101111
});
102112
};
103113

104-
const createDashboardJWT = async (dashboard, provider) => {
105-
const dashboardPayload = await createDashboardTokenPayload(dashboard, provider);
114+
const createDashboardJWT = async (dashboard, shareableId, provider) => {
115+
const dashboardPayload = await createDashboardTokenPayload(dashboard, shareableId, provider);
106116
return createJWT(dashboardPayload);
107117
};
108118

api/src/auth/passport.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import Promise from 'bluebird';
77
import CustomStrategy from 'passport-custom';
88
import bcrypt from 'bcrypt';
99
import jwt from 'jsonwebtoken';
10-
import { find, get, omit } from 'lodash';
10+
import { find, get, omit, omitBy } from 'lodash';
1111
import { fromJS } from 'immutable';
1212
import assert from 'assert';
1313
import Client from 'lib/models/client';
@@ -49,8 +49,13 @@ const createPayloadFromPayload = (payload) => {
4949
return { expectedToken, user };
5050
}
5151
case 'dashboard': {
52+
const shareable = find(dashboard.shareable, share =>
53+
share._id.toString() === payload.shareableId
54+
);
55+
5256
const expectedToken = await createDashboardTokenPayload(
5357
dashboard,
58+
(shareable ? shareable._id.toString() : null),
5459
provider
5560
);
5661
return { expectedToken };
@@ -69,10 +74,12 @@ async function verifyToken(token, done) {
6974
const decodedToken = await verifyPromise(token, process.env.APP_SECRET);
7075

7176
// Recreates the token's payload to make sure that all scopes are still valid.
77+
7278
const { expectedToken, user } = await createPayloadFromPayload(
7379
decodedToken
7480
);
75-
const tokenToVerify = omit(decodedToken, ['iat', 'exp']);
81+
const tokenToVerify1 = omit(decodedToken, ['iat', 'exp']);
82+
const tokenToVerify = omitBy(tokenToVerify1, (v, k) => k === 'shareableId' && !v);
7683

7784
const iExpectedToken = fromJS(expectedToken);
7885
const iTokenToVerify = fromJS(tokenToVerify);

api/src/controllers/StatementController.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const aggregate = (req) => {
1616
const maxTimeMS = Number(req.query.maxTimeMS) || MAX_TIME_MS;
1717
const maxScan = Number(req.query.maxScan) || MAX_SCAN;
1818
const pipeline = JSON.parse(req.query.pipeline);
19-
return statementsService.aggregate({
19+
const out = statementsService.aggregate({
2020
authInfo,
2121
limit,
2222
skip,
@@ -25,6 +25,7 @@ const aggregate = (req) => {
2525
maxScan,
2626
pipeline
2727
});
28+
return out;
2829
};
2930

3031
const aggregateStatements = catchErrors(async (req, res) => {

api/src/routes/HttpRoutes.js

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ import express from 'express';
33
import restify from 'express-restify-mongoose';
44
import git from 'git-rev';
55
import Promise from 'bluebird';
6+
import { omit, findIndex } from 'lodash';
7+
import getAuthFromRequest from 'lib/helpers/getAuthFromRequest';
8+
import getScopesFromRequest from 'lib/services/auth/authInfoSelectors/getScopesFromAuthInfo';
9+
import getUserIdFromAuthInfo from 'lib/services/auth/authInfoSelectors/getUserIdFromAuthInfo';
10+
import { SITE_ADMIN } from 'lib/constants/scopes';
611
import { jsonSuccess, serverError } from 'api/utils/responses';
712
import passport from 'api/auth/passport';
813
import {
@@ -193,13 +198,40 @@ router.get(
193198
* REST APIS
194199
*/
195200
restify.defaults(RESTIFY_DEFAULTS);
196-
restify.serve(router, Organisation);
201+
restify.serve(router, Organisation, {
202+
preUpdate: (req, res, next) => {
203+
const authInfo = getAuthFromRequest(req);
204+
const scopes = getScopesFromRequest(authInfo);
205+
if (
206+
findIndex(scopes, item => item === SITE_ADMIN) < 0
207+
) {
208+
req.body = omit(req.body, 'expiration');
209+
}
210+
next();
211+
}
212+
});
197213
restify.serve(router, Stream);
198214
restify.serve(router, Export);
199215
restify.serve(router, Download);
200216
restify.serve(router, Query);
201217
restify.serve(router, ImportCsv);
202-
restify.serve(router, User);
218+
restify.serve(router, User, {
219+
preUpdate: (req, res, next) => {
220+
const authInfo = getAuthFromRequest(req);
221+
const scopes = getScopesFromRequest(authInfo);
222+
223+
if (findIndex(scopes, item => item === SITE_ADMIN) < 0) {
224+
// remove scope changes
225+
req.body = omit(req.body, 'scopes');
226+
if (req.body._id !== getUserIdFromAuthInfo(authInfo).toString()){
227+
// Don't allow changing of passwords
228+
req.body = omit(req.body, 'password');
229+
}
230+
}
231+
232+
next();
233+
}
234+
});
203235
restify.serve(router, Client);
204236
restify.serve(router, Visualisation);
205237
restify.serve(router, Dashboard);

api/src/routes/tests/http-test.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,11 @@ let jwtToken;
1818
let orgJwtToken;
1919
const provider = 'native';
2020

21-
describe('API HTTP Route tests', () => {
21+
describe('API HTTP Route tests', function describeTest() {
22+
this.timeout(10000);
2223
before((done) => {
23-
console.log('readyState', connection.readyState);
2424
if (connection.readyState !== 1) {
2525
connection.on('connected', () => {
26-
console.log('connected');
2726
done();
2827
});
2928
} else {

0 commit comments

Comments
 (0)