pryv 2.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/.jsdoc-conf.json +29 -0
  2. package/.mocharc.js +13 -0
  3. package/LICENSE.md +27 -0
  4. package/README.md +723 -0
  5. package/package.json +57 -0
  6. package/scripts/setup-environment-dev.sh +28 -0
  7. package/scripts/upload.sh +15 -0
  8. package/src/Auth/AuthController.js +276 -0
  9. package/src/Auth/AuthStates.js +20 -0
  10. package/src/Auth/LoginMessages.js +29 -0
  11. package/src/Auth/index.js +43 -0
  12. package/src/Browser/CookieUtils.js +51 -0
  13. package/src/Browser/LoginButton.js +199 -0
  14. package/src/Browser/index.js +55 -0
  15. package/src/Connection.js +331 -0
  16. package/src/Pryv.js +19 -0
  17. package/src/Service.js +197 -0
  18. package/src/ServiceAssets.js +162 -0
  19. package/src/index-socket.io-monitor.js +4 -0
  20. package/src/index.html +17 -0
  21. package/src/index.js +3 -0
  22. package/src/lib/browser-getEventStreamed.js +80 -0
  23. package/src/lib/json-parser.js +156 -0
  24. package/src/utils.js +136 -0
  25. package/test/Browser.AuthController.test.js +97 -0
  26. package/test/Browser.test.js +79 -0
  27. package/test/Connection.test.js +455 -0
  28. package/test/Service.test.js +89 -0
  29. package/test/ServiceAssets.test.js +79 -0
  30. package/test/Y.png +0 -0
  31. package/test/browser-index.js +11 -0
  32. package/test/browser-tests.html +31 -0
  33. package/test/helpers.js +8 -0
  34. package/test/load-test-account.js +108 -0
  35. package/test/test-data.js +92 -0
  36. package/test/utils.test.js +68 -0
  37. package/web-demos/auth-with-redirection.html +72 -0
  38. package/web-demos/auth.html +77 -0
  39. package/web-demos/custom-login-button.html +158 -0
  40. package/web-demos/index.html +186 -0
  41. package/web-demos/service-info.json +13 -0
  42. package/web-demos/stream-examples.html +80 -0
  43. package/webpack.config.js +71 -0
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "pryv",
3
+ "version": "2.1.8",
4
+ "homepage": "https://github.com/pryv/lib-js",
5
+ "description": "Pryv Javascript Library",
6
+ "keywords": [
7
+ "Pryv"
8
+ ],
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git://github.com/pryv/lib-js"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/pryv/lib-js/issues"
15
+ },
16
+ "author": {
17
+ "name": "Pryv S.A."
18
+ },
19
+ "main": "src/index.js",
20
+ "license": "BSD-3-Clause",
21
+ "scripts": {
22
+ "setup": "./scripts/setup-environment-dev.sh",
23
+ "build": "webpack --config webpack.config.js",
24
+ "doc": "node node_modules/jsdoc/jsdoc.js -c .jsdoc-conf.json",
25
+ "test": "mocha --reporter spec test/**/*.test.js --timeout 3000",
26
+ "test-debug": "mocha --inpect-brk=40000 --reporter spec test/**/*.test.js ",
27
+ "test:browser": "npm run build; ",
28
+ "cover": "nyc npm run test && npm run cover:report",
29
+ "cover:report": "nyc report --reporter=lcov --reporter=html",
30
+ "webserver": "./node_modules/.bin/rec-la ./dist 9443",
31
+ "gh-pages": "./scripts/upload.sh",
32
+ "clear": "rm -r dist/ && npm run setup"
33
+ },
34
+ "contributors": [
35
+ {
36
+ "name": "Pierre-Mikael Legris",
37
+ "email": "perki@pryv.com"
38
+ }
39
+ ],
40
+ "dependencies": {
41
+ "superagent": "^5.2.2"
42
+ },
43
+ "devDependencies": {
44
+ "@pryv/lib-js-common": "git+https://github.com/pryv/lib-js-common.git#1.0.4",
45
+ "@pryv/monitor": "^1.0.5",
46
+ "@pryv/socket.io": "^1.0.5",
47
+ "chai": "^4.3.4",
48
+ "chai-as-promised": "^7.1.1",
49
+ "cuid": "^2.1.8",
50
+ "mocha": "^6.2.3",
51
+ "node-fetch": "^2.6.0",
52
+ "nyc": "^14.1.1",
53
+ "rec-la": "latest",
54
+ "universal-url": "^2.0.0",
55
+ "zombie": "^6.1.4"
56
+ }
57
+ }
@@ -0,0 +1,28 @@
1
+ #!/bin/sh
2
+
3
+ # working dir fix
4
+ scriptsFolder=$(cd $(dirname "$0"); pwd)
5
+ cd $scriptsFolder/..
6
+
7
+ # check for basic prerequisites
8
+ hash git 2>&- || { echo >&2 "I require git."; exit 1; }
9
+ hash npm 2>&- || { echo >&2 "I require Node and NPM."; exit 1; }
10
+
11
+ echo "
12
+ Installing Node modules if necessary...
13
+ "
14
+ npm install
15
+
16
+ distFolder=dist
17
+ if [ ! -d $distFolder ]
18
+ then
19
+ echo "
20
+ Setting up '$distFolder' folder for publishing to GitHub pages...
21
+ "
22
+ git clone -b gh-pages git@github.com:pryv/lib-js.git $distFolder
23
+ fi
24
+
25
+ echo "
26
+
27
+ OK!
28
+ "
@@ -0,0 +1,15 @@
1
+ #!/bin/sh
2
+
3
+ set -e
4
+
5
+ # working dir fix
6
+ scriptsFolder=$(cd $(dirname "$0"); pwd)
7
+ cd $scriptsFolder/..
8
+
9
+ if [ $# -eq 0 ]
10
+ then
11
+ echo "No commit message was provided, please provide a message as argument for readability";
12
+ exit 1;
13
+ fi
14
+
15
+ cd dist && git add . && git add -u . && git commit -m "$1" && git push
@@ -0,0 +1,276 @@
1
+ const utils = require('../utils');
2
+ const Service = require('../Service');
3
+ const AuthStates = require('./AuthStates');
4
+ const Messages = require('./LoginMessages');
5
+
6
+ /**
7
+ * @private
8
+ */
9
+ class AuthController {
10
+
11
+ /**
12
+ *
13
+ * @param {*} settings
14
+ * @param {*} service
15
+ */
16
+ constructor (settings, service, loginButton) {
17
+ this.settings = settings;
18
+ validateSettings.call(this, settings);
19
+
20
+ this.stateChangeListeners = [];
21
+ if (this.settings.onStateChange) {
22
+ this.stateChangeListeners.push(this.settings.onStateChange);
23
+ }
24
+ this.service = service;
25
+
26
+ // probably remove
27
+ this.languageCode = this.settings.authRequest.languageCode || 'en';
28
+ this.messages = Messages(this.languageCode);
29
+
30
+ this.loginButton = loginButton;
31
+
32
+ function validateSettings (settings) {
33
+ if (!settings) { throw new Error('settings cannot be null'); }
34
+ // -- settings
35
+ if (!settings.authRequest) { throw new Error('Missing settings.authRequest'); }
36
+
37
+ // -- Extract returnURL
38
+ settings.authRequest.returnURL =
39
+ this.getReturnURL(settings.authRequest.returnURL);
40
+
41
+ if (!settings.authRequest.requestingAppId) {
42
+ throw new Error('Missing settings.authRequest.requestingAppId');
43
+ }
44
+ if (!settings.authRequest.requestedPermissions) {
45
+ throw new Error('Missing settings.authRequest.requestedPermissions');
46
+ }
47
+ }
48
+ }
49
+
50
+ /**
51
+ * async function to call right after instanciating object
52
+ *
53
+ * @returns {PryvService}
54
+ */
55
+ async init () {
56
+ this.serviceInfo = this.service.infoSync();
57
+ this.state = { status: AuthStates.LOADING };
58
+ this.assets = await loadAssets(this);
59
+
60
+ const loginButton = this.loginButton;
61
+ // initialize human interaction interface
62
+ if (loginButton != null) {
63
+ this.stateChangeListeners.push(loginButton.onStateChange.bind(loginButton));
64
+ // autologin needs cookies/storage implemented in human interaction interface
65
+ await checkAutoLogin(this);
66
+ }
67
+
68
+ // if auto login is not prompted
69
+ if (this.state.status != AuthStates.AUTHORIZED) {
70
+ this.state = { status: AuthStates.INITIALIZED, serviceInfo: this.serviceInfo};
71
+ }
72
+
73
+ if (loginButton != null && loginButton.finishAuthProcessAfterRedirection != null) {
74
+ await loginButton.finishAuthProcessAfterRedirection(this);
75
+ }
76
+
77
+ return this.service;
78
+ }
79
+
80
+ /**
81
+ * Stops poll for auth request
82
+ */
83
+ stopAuthRequest (msg) {
84
+ this.state = { status: AuthStates.ERROR, message: msg };
85
+ }
86
+
87
+ /**
88
+ * Triggered when button is pressed
89
+ */
90
+ async handleClick () {
91
+ if (isAuthorized.call(this)) {
92
+ this.state = { status: AuthStates.SIGNOUT };
93
+ } else if (isInitialized.call(this)) {
94
+ this.startAuthRequest();
95
+ } else if (isNeedSignIn.call(this)) {
96
+ // reopen popup
97
+ this.state = this.state;
98
+ } else {
99
+ console.log('Unhandled action in "handleClick()" for status:', this.state.status);
100
+ }
101
+
102
+ function isAuthorized () {
103
+ return this.state.status == AuthStates.AUTHORIZED;
104
+ }
105
+ function isInitialized () {
106
+ return this.state.status === AuthStates.INITIALIZED;
107
+ }
108
+ function isNeedSignIn () {
109
+ return this.state.status === AuthStates.NEED_SIGNIN;
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Used only to retrieve returnUrl in browser environments
115
+ *
116
+ * @param {*} returnURL
117
+ * @param {*} windowLocationForTest
118
+ * @param {*} navigatorForTests
119
+ */
120
+ getReturnURL (
121
+ returnURL,
122
+ windowLocationForTest,
123
+ navigatorForTests
124
+ ) {
125
+ const RETURN_URL_AUTO = 'auto';
126
+
127
+ returnURL = returnURL || RETURN_URL_AUTO + '#';
128
+
129
+ // check the trailer
130
+ let trailer = returnURL.slice(-1);
131
+ if ('#&?'.indexOf(trailer) < 0) {
132
+ throw new Error('Pryv access: Last character of --returnURL setting-- is not ' +
133
+ '"?", "&" or "#": ' + returnURL);
134
+ }
135
+ // auto mode for desktop
136
+ if (
137
+ returnUrlIsAuto(returnURL) &&
138
+ !utils.browserIsMobileOrTablet(navigatorForTests)
139
+ ) {
140
+ return false;
141
+ } else if (
142
+ // auto mode for mobile or self
143
+ (returnUrlIsAuto(returnURL) &&
144
+ utils.browserIsMobileOrTablet(navigatorForTests)
145
+ )
146
+ || returnURL.indexOf('self') === 0
147
+ ) {
148
+ // set self as return url?
149
+ // eventually clean-up current url from previous pryv returnURL
150
+ const locationHref = windowLocationForTest || window.location.href;
151
+ returnURL = locationHref + returnURL.substring(4);
152
+ }
153
+ return utils.cleanURLFromPrYvParams(returnURL);
154
+
155
+ function returnUrlIsAuto (returnURL) {
156
+ return returnURL.indexOf(RETURN_URL_AUTO) === 0;
157
+ }
158
+ }
159
+
160
+ async startAuthRequest () {
161
+ this.state = await postAccess.call(this);
162
+
163
+ await doPolling.call(this);
164
+
165
+ async function postAccess () {
166
+ try {
167
+ const res = await utils.superagent
168
+ .post(this.serviceInfo.access)
169
+ .send(this.settings.authRequest);
170
+ return res.body;
171
+ } catch (e) {
172
+ this.state = {
173
+ status: AuthStates.ERROR,
174
+ message: 'Requesting access',
175
+ error: e
176
+ };
177
+ throw e; // forward error
178
+ }
179
+ }
180
+
181
+ async function doPolling() {
182
+ if (this.state.status !== AuthStates.NEED_SIGNIN) {
183
+ return;
184
+ }
185
+ const pollResponse = await pollAccess(this.state.poll);
186
+
187
+ if (pollResponse.status === AuthStates.NEED_SIGNIN) {
188
+ setTimeout(await doPolling.bind(this), this.state.poll_rate_ms);
189
+ } else {
190
+ this.state = pollResponse;
191
+ }
192
+
193
+ async function pollAccess(pollUrl) {
194
+ try {
195
+ const res = await utils.superagent
196
+ .get(pollUrl)
197
+ return res.body;
198
+ } catch (e) {
199
+ if (e.response &&
200
+ e.response.status === 403 &&
201
+ e.response.body &&
202
+ e.response.body.status === 'REFUSED') {
203
+ return { status: AuthStates.INITIALIZED };
204
+ } else {
205
+ return { status: AuthStates.ERROR, message: 'Error while polling for auth request', error: e };
206
+ }
207
+ }
208
+ }
209
+ }
210
+
211
+
212
+ }
213
+
214
+ // -------------- state listeners ---------------------
215
+ set state (newState) {
216
+
217
+ // retro-compatibility for lib-js < 2.0.9
218
+ newState.id = newState.status;
219
+
220
+ this._state = newState;
221
+
222
+ this.stateChangeListeners.map((listener) => {
223
+ try {
224
+ listener(this.state)
225
+ } catch (e) {
226
+ console.log('Error during set state ()', e);
227
+ }
228
+ });
229
+ }
230
+
231
+ get state () {
232
+ return this._state;
233
+ }
234
+ }
235
+
236
+ // ----------- private methods -------------
237
+
238
+ async function checkAutoLogin (authController) {
239
+ const loginButton = authController.loginButton;
240
+ if (loginButton == null) {
241
+ return;
242
+ }
243
+
244
+ const storedCredentials = await loginButton.getAuthorizationData();
245
+ if (storedCredentials != null) {
246
+ authController.state = Object.assign({}, {status: AuthStates.AUTHORIZED}, storedCredentials);
247
+ }
248
+ }
249
+
250
+ // ------------------ ACTIONS ----------- //
251
+
252
+ async function loadAssets(authController) {
253
+ let loadedAssets = {};
254
+ try {
255
+ loadedAssets = await authController.service.assets();
256
+ if (typeof location !== 'undefined') {
257
+ await loadedAssets.loginButtonLoadCSS();
258
+ const thisMessages = await loadedAssets.loginButtonGetMessages();
259
+ if (thisMessages.LOADING) {
260
+ authController.messages = Messages(authController.languageCode, thisMessages);
261
+ } else {
262
+ console.log("WARNING Messages cannot be loaded using defaults: ", thisMessages)
263
+ }
264
+ }
265
+ } catch (e) {
266
+ authController.state = {
267
+ status: AuthStates.ERROR,
268
+ message: 'Cannot fetch button visuals',
269
+ error: e
270
+ };
271
+ throw e; // forward error
272
+ }
273
+ return loadedAssets;
274
+ }
275
+
276
+ module.exports = AuthController;
@@ -0,0 +1,20 @@
1
+
2
+
3
+ /**
4
+ * Enum Possible states: ERROR, LOADING, INITIALIZED, AUTHORIZED, SIGNOUT
5
+ * @readonly
6
+ * @enum {string}
7
+ * @memberof Pryv.Browser
8
+ */
9
+ const AuthState = {
10
+ ERROR : 'ERROR',
11
+ LOADING : 'LOADING',
12
+ INITIALIZED: 'INITIALIZED',
13
+ NEED_SIGNIN: 'NEED_SIGNIN',
14
+ AUTHORIZED: 'ACCEPTED',
15
+ SIGNOUT: 'SIGNOUT',
16
+ REFUSED: 'REFUSED',
17
+ }
18
+
19
+
20
+ module.exports = AuthState
@@ -0,0 +1,29 @@
1
+ const Messages = {
2
+ LOADING: {
3
+ 'en': '...'
4
+ },
5
+ ERROR: {
6
+ 'en': 'Error',
7
+ 'fr': 'Erreur',
8
+ },
9
+ LOGIN: {
10
+ 'en': 'Signin',
11
+ 'fr': 'Login'
12
+ },
13
+ SIGNOUT_CONFIRM: {
14
+ 'en': 'Logout ?',
15
+ 'fr': 'Se deconnecter ?'
16
+ }
17
+ }
18
+
19
+ function get(languageCode, definitions) {
20
+ const myMessages = definitions || Messages;
21
+ const res = {};
22
+ Object.keys(myMessages).map((key) => {
23
+ res[key] = myMessages[key][languageCode] || myMessages[key]['en'];
24
+ });
25
+ return res;
26
+ }
27
+
28
+
29
+ module.exports = get;
@@ -0,0 +1,43 @@
1
+ const AuthController = require('./AuthController');
2
+ const AuthStates = require('./AuthStates');
3
+ const LoginButton = require('../Browser/LoginButton');
4
+ const Service = require('../Service');
5
+
6
+ /**
7
+ * @memberof Pryv
8
+ * @namespace Pryv.Auth
9
+ */
10
+
11
+ /**
12
+ * Start an authentication process
13
+ *
14
+ * @memberof Pryv.Auth
15
+ * @param {Object} settings
16
+ * @param {Object} settings.authRequest See https://api.pryv.com/reference/#data-structure-access
17
+ * @param {string} [settings.authRequest.languageCode] Language code, as per LoginButton Messages: 'en', 'fr
18
+ * @param {string} settings.authRequest.requestingAppId Application id, ex: 'my-app'
19
+ * @param {Object} settings.authRequest.requestedPermissions
20
+ * @param {string | boolean} settings.authRequest.returnURL : false, // set this if you don't want a popup
21
+ * @param {string} [settings.authRequest.referer] To track registration source
22
+ * @param {string} settings.spanButtonID set and <span> id in DOM to insert default login button or null for custom
23
+ * @param {Browser.AuthStateChangeHandler} settings.onStateChange
24
+ * @param {string} [settings.returnURL=auto#] Set to "self#" to disable popup and force using the same page. Set a custom url when process is finished (specific use cases). Should always end by # ? or &
25
+ * @param {string} serviceInfoUrl
26
+ * @param {Object} [serviceCustomizations] override properties of serviceInfoUrl
27
+ * @returns {Pryv.Service}
28
+ */
29
+ async function setupAuth (settings, serviceInfoUrl, serviceCustomizations, HumanInteraction = LoginButton) {
30
+ const service = new Service(serviceInfoUrl, serviceCustomizations);
31
+ await service.info()
32
+
33
+ const humanInteraction = new HumanInteraction(settings, service);
34
+ await humanInteraction.init();
35
+
36
+ return service;
37
+ }
38
+
39
+ module.exports = {
40
+ setupAuth: setupAuth,
41
+ AuthStates: AuthStates,
42
+ AuthController: AuthController,
43
+ }
@@ -0,0 +1,51 @@
1
+
2
+ const utils = require('../utils');
3
+ /**
4
+ * @memberof Pryv.Browser
5
+ * @namespace Pryv.Browser.CookieUtils
6
+ */
7
+
8
+ /**
9
+ * Set a Local cookier
10
+ * @memberof Pryv.Browser.CookieUtils
11
+ * @param {string} cookieKey - The key for the cookie
12
+ * @param {mixed} value - The Value
13
+ * @param {number} expireInDays - Expiration date in days from now
14
+ */
15
+ function set(cookieKey, value, expireInDays) {
16
+ if (! utils.isBrowser()) return;
17
+ expireInDays = expireInDays || 365;
18
+ var myDate = new Date();
19
+ var hostName = window.location.hostname;
20
+ var path = window.location.pathname;
21
+ myDate.setDate(myDate.getDate() + expireInDays);
22
+ var cookieStr = encodeURIComponent(cookieKey) + '=' + encodeURIComponent(JSON.stringify(value))
23
+ + ';expires=' + myDate.toGMTString()
24
+ + ';domain=.' + hostName + ';path=' + path;
25
+ // do not add SameSite when removing a cookie
26
+ if (expireInDays >= 0) cookieStr += ';SameSite=Strict';
27
+ document.cookie = cookieStr;
28
+ }
29
+ exports.set = set;
30
+
31
+ /**
32
+ * returns the value of a local cookie
33
+ * @memberof Pryv.Browser.CookieUtils
34
+ * @param cookieKey - The key
35
+ */
36
+ exports.get = function get(cookieKey) {
37
+ const name = encodeURIComponent(cookieKey);
38
+ if (! utils.isBrowser()) return;
39
+ var value = "; " + document.cookie;
40
+ var parts = value.split("; " + name + "=");
41
+ if (parts.length == 2) return JSON.parse(decodeURIComponent(parts.pop().split(";").shift()));
42
+ }
43
+
44
+ /**
45
+ * delete a local cookie
46
+ * @memberof Pryv.Browser.CookieUtils
47
+ * @param cookieKey - The key
48
+ */
49
+ exports.del = function del(cookieKey) {
50
+ set(cookieKey, {deleted: true}, -1);
51
+ }