diff --git a/.nycrc b/.nycrc index 147cb57c0..819bc23f1 100644 --- a/.nycrc +++ b/.nycrc @@ -75,12 +75,14 @@ "!**/gpii/node_modules/ontologyHandler/src/ontologyHandler.js", "!**/gpii/node_modules/ontologyHandler/src/ontologyHandlerUtilities.js", "!**/gpii/node_modules/preferencesServer/index.js", + "!**/gpii/node_modules/preferencesServer/src/cloudSafeCredHandlers.js", "!**/gpii/node_modules/preferencesServer/src/preferencesGetHandler.js", "!**/gpii/node_modules/preferencesServer/src/preferencesPostHandler.js", "!**/gpii/node_modules/preferencesServer/src/preferencesPutHandler.js", "!**/gpii/node_modules/preferencesServer/src/preferencesServer.js", "!**/gpii/node_modules/preferencesServer/src/preferencesServerConst.js", "!**/gpii/node_modules/preferencesServer/src/preferencesService.js", + "!**/gpii/node_modules/preferencesServer/src/prefsSafesHandlers.js", "!**/gpii/node_modules/preferencesServer/src/readyGetHandler.js", "!**/gpii/node_modules/processReporter/index.js", "!**/gpii/node_modules/processReporter/src/ProcessReporter.js", diff --git a/documentation/AuthorizationService.md b/documentation/AuthorizationService.md index 4709486c5..112f0fc29 100644 --- a/documentation/AuthorizationService.md +++ b/documentation/AuthorizationService.md @@ -21,7 +21,7 @@ Grant](https://wiki.gpii.net/w/GPII_OAuth_2_Guide#Resource_Owner_GPII_Key_Grant) "accessToken": "gpii-app-installation-accessToken-1", "clientCredential": { "type": "clientCredential", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": "gpiiAppInstallationClient-1", "oauth2ClientId": "net.gpii.ajc.bakersfield", "oauth2ClientSecret": "client_secret_ajc_bakersfield", @@ -37,7 +37,7 @@ Grant](https://wiki.gpii.net/w/GPII_OAuth_2_Guide#Resource_Owner_GPII_Key_Grant) }, "authorization": { "type": "gpiiAppInstallationAuthorization", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": "gpiiAppInstallationClient-1", "gpiiKey": "chrome_high_contrast", "accessToken": "gpii-app-installation-accessToken-1", diff --git a/documentation/DataModel.md b/documentation/DataModel.md index 455ea3bc8..3ed8e0228 100644 --- a/documentation/DataModel.md +++ b/documentation/DataModel.md @@ -4,3 +4,124 @@ GPII uses CouchDB to store data in JSON documents. The two GPII components that storage are the Preferences Server and the Authorization Server. The details of the GPII data model can be found [here](https://wiki.gpii.net/w/Keys,_KeyTokens,_and_Preferences). + +## Preference Safes Overview + +In this section we discuss the CouchDB documents for all the data associated with a users preferences safe. This +includes their preference sets, keys, full login credentials, and a minimal amount of metadata for the user, +such as name and email. Currently, this consists of three document types: `prefsSafe`, `gpiiKey`, +`gpiiCloudSafeCredential`. + +### Preference Safes + +Preference Safes consist of a single primary document of type `prefsSafe`. These contain some optional metadata such +as `name` and `email`, and the `preferences` section which contains the users preference sets. This is the central +document for a safe, any documents relating to a safe should have a property `prefsSafeId` which contains the id of +the preferences safe. + +An example document: + +```json + { + "_id": "prefsSafe-7", + "type": "prefsSafe", + "schemaVersion": "0.3", + "prefsSafeType": "user", + "name": null, + "email": null, + "preferences": { + "flat": { + "name": "bit of stuff", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/common/onScreenKeyboard/enabled": true + }, + "metadata": [] + } + }, + "metadata": [] + } + }, + "timestampCreated": "2017-12-14T19:55:11.641Z", + "timestampUpdated": null + } +``` + +### Key-in Documents + +When users key-in to the GPII using a USB stick, NFC card, or other mechanism, the unique `gpiiKey` on the device will +be matched to the CouchDB `_id` on a document with type `gpiiKey`. This document contains the important fields `prefsSafeId` +and `prefsSetId` linking it to the safe, and to a specific preference set within that safe to key in with. + +An example document: + +```json + { + "_id": "np_tiny", + "type": "gpiiKey", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-7", + "prefsSetId": "gpii-default", + "revoked": false, + "revokedReason": null, + "timestampCreated": "2017-12-14T19:55:11.641Z", + "timestampUpdated": null, + "timestampRevoked": null + } +``` + +### Login Documents + +In order to have full permissions to edit all aspects of their preferences safe, users must login to their safe using a +username and password. The current implementation of this is backed by the `gpii-express-user` library which creates +records in the same format as native CouchDB accounts and manages password hashing, unlocking, etc. In order to avoid +making changes to this external library, we introduce a document type 'gpiiCloudSafeCredential' which tracks the native +record that is created by `gpii-express-user`. Note that the `gpiiExpressuserId` entries are prefixed with `org.couch.db.user:` +which is the convention for both internal CouchDB users and users created with gpii-express-user. + +An example document: + +```json + { + "_id": "8f3085a7-b65b-4648-9a78-8ac7de766997", + "type": "gpiiCloudSafeCredential", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-7", + "gpiiExpressUserId": "org.couch.db.user:prefs7user" + } +``` + +For reference, the internal account record for the above looks as follows: + +```json + { + "_id": "org.couch.db.user:prefs7user", + "name": "prefs7user", + "type": "user", + "email": null, + "roles": [], + "username": "prefs7user", + "verified": true, + "iterations": 10, + "password_scheme": "pbkdf2", + "salt": "7cf6961e6ded3bd25732e5466512d116bf9908ba9629d4ed060a03a965e5341d", + "derived_key": "e8bd265e7d82fd0f662e9ddaaf2e75acb294da1b", + "verification_code": "618fa72aa62af282704b556e34957a79" + } +``` + +`gpii-express-user` handles the lookup of unique names when attempting to unlock a user with a password. +This means that in the above example, the login username for preferences safe `prefsSafe-7` would be `prefs7user`. +This also means that it could be possible for a preferences safe to have more than one `gpiiCloudSafeCredential` +similar to how it can have more than one `gpiiKey`. This allows flexibility for the addition, management, and revokation +of key-in and log-in methods. + +### Future Document Types + +In the future we may add additional document types, or sub-types for other features such as CAS or single sign-on +authentication. For any documents that add information to a preferences safe, the most important thing is that they +have a `prefsSafeId` attribute. In general, we want to avoid having documents several linkages away from the primary +preferences safe document. In the case of `gpii-express-user` above we have 1 extra hop to avoid modifying the external +library, but in general, the document relations should be kept as simple as possible. diff --git a/documentation/PreferencesServer.md b/documentation/PreferencesServer.md index 00a94a8a1..4491ac07f 100644 --- a/documentation/PreferencesServer.md +++ b/documentation/PreferencesServer.md @@ -341,6 +341,434 @@ There are two important things to note here: to `http://registry.gpii.net/common/fontSize`. Since we do not allow the same setting to be present multiple times in the NP set, the fontSize has been stored in the flat ontology and removed from the ISO24751 block. +### GET /prefsSafe/:prefsSafeId + +This end point will return the entire preferences safe, including the embedded preference sets. The URL param is the +`id` of the preferences safe. This will not return any attached keys, credentials, or other records. + +#### Example GET + +URL: `http://preferences.gpii.net/prefsSafe/prefsSafe-alice` + +Example Success Payload: + +```json +{ + "id": "prefsSafe-alice", + "type": "prefsSafe", + "schemaVersion": "0.3", + "prefsSafeType": "user", + "name": "alice", + "email": null, + "preferences": { + "flat": { + "name": "Alice", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/applications/com.microsoft.windows.onscreenKeyboard": {} + } + } + } + } + }, + "timestampCreated": "2019-01-14T23:48:03.221Z", + "timestampUpdated": null +} +``` + +If the preferences safe with the `prefsSafeId` does not exist, the following error payload will be +returned: + +```json +{ + "isError": true, + "errorCode": "GPII_ERR_NO_PREFSSAFE", + "message": "Missing prefsSafe" +} +``` + +### GET /prefsSafe-with-keys/:prefsSafeId + +This endpoint will return the entire preferences safe structure, along with records directory related to it, such +as keys and credentials. A top level object will contain the preferences safe under key `prefsSafe`, and the related +documents under key `keys`. + +#### Example GET + +URL: `http://preferences.gpii.net/prefsSafe-with-keys/prefsSafe-alice` + +Example returned item: + +```json +{ + "prefsSafe": { + "id": "prefsSafe-alice", + "type": "prefsSafe", + "schemaVersion": "0.3", + "prefsSafeType": "user", + "name": "alice", + "email": null, + "preferences": { + "flat": { + "name": "Alice", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/applications/com.microsoft.windows.onscreenKeyboard": {} + } + } + } + } + }, + "timestampCreated": "2019-01-14T23:48:03.221Z", + "timestampUpdated": null + }, + "keys": [ + { + "id": "8f3085a7-b65b-4648-9a78-8ac7de766997", + "type": "gpiiCloudSafeCredential", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-alice", + "gpiiExpressUserId": "org.couch.db.user:alice" + }, + { + "id": "57A68E84-03A9-4ADD-9365-11C75E4F1B0E", + "type": "gpiiKey", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-alice", + "prefsSetId": "gpii-default", + "revoked": false, + "revokedReason": null, + "timestampCreated": "2017-12-14T19:55:11.640Z", + "timestampUpdated": null + } + ] +} +``` + +If the safe with the specified `prefsSafeId` does not exist, the following error payload will be returned: + +```json +{ + "isError": true, + "errorCode": "GPII_ERR_NO_PREFSSAFE", + "message": "Missing prefsSafe" +} +``` + +### POST /prefsSafe + +Creates a new preferences safe in the system. Takes a full preferences safe JSON payload, albiet without a `id`. +The returned payload will include the entire preferences safe, including the updated `timestampCreated` field, +and a newly provisioned `id`. + +#### Example POST + +URL: `http://preferences.gpii.net/prefsSafe` + +The POST payload should be a full preferences safe without an `id`: + +```json +{ + "type": "prefsSafe", + "schemaVersion": "0.3", + "prefsSafeType": "user", + "name": "Steve", + "email": null, + "preferences": { + "flat": { + "name": "bit of stuff", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/common/onScreenKeyboard/enabled": false + }, + "metadata": [] + } + }, + "metadata": [] + } + } +} +``` + +A successful return payload will consist of the same payload with the addition of timestamps and a +freshly generated `id` field. In the event of an error, a payload similar to the following will be returned +with the contents of the system error: + +```json +{ + "isError": true, + "errorCode": "GPII_ERR_APPLICABLE_MESSAGE", + "message": "System error described here." +} +``` + +### PUT /prefsSafe/:prefsSafeId + +Updates an existing preferences safe in the database, using the full preferences safe format. Will return the +updated safe, which should include an updated `timestampUpdated` field. + +#### Example PUT + +URL: `http://preferences.gpii.net/prefsSafe/prefsSafe-alice` + +Example PUT body: + +```json +{ + "id": "prefsSafe-alice", + "type": "prefsSafe", + "schemaVersion": "0.3", + "prefsSafeType": "user", + "name": "alice", + "email": null, + "preferences": { + "flat": { + "name": "Alice", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/applications/com.microsoft.windows.onscreenKeyboard": {} + } + } + } + } + }, + "timestampCreated": "2019-01-14T23:48:03.221Z", + "timestampUpdated": null +} +``` + +On a successfull save, the same payload from the PUT operation will be returned, with an updated +`timestampUpdated` field with the time of save. In the event of a failed save an error payload with a +suitable message will be returned. + +```json +{ + "isError": true, + "errorCode": "APPROPRIATE_ERROR_CODE", + "message": "System error described here." +} +``` + +### GET /prefsSafes + +Returns a list of preferences safes, including only basic information about each one. Ideal for building a table +or listing of preferences safes. Each item in the list representing a preferences safe will include `id`, `name`, +`email`, `created`, and `updated`. This endpoint will likely have more options in the future, such as sorting, +paging, etc. + +#### Example GET + +URL: `http://preferences.gpii.net/prefsSafes` + +Example return payload: + +```json +{ + "total_rows": 2, + "offset": 0, + "rows": [ + { + "name": "Alice", + "email": "alice@gpii.net", + "created": "2017-12-14T19:55:11.640Z", + "updated": null, + "id": "prefsSafe-1" + }, + { + "name": null, + "email": null, + "created": "2017-12-14T19:55:11.640Z", + "updated": null, + "id": "prefsSafe-2" + } + ] +} +``` + +There should never be an error payload for this endpoint. In the situation where there are no preferences +safes stored in the system it will merely contain zero rows. + +### GET /prefsSafe-keys/:prefsSafeId + +This will return the associated keys and credentials documents for a given preferences safe, in a `rows` field. +Also included is a `total_rows` and `offset` field, but do note that the `total_rows` field is not accurate as +of the time of writing, and should be ignored. + +#### Example GET + +URL: `http://preferences.gpii.net/prefsSafe-keys/prefsSafe-alice` + +An example payload for a particlar safe may be: + +```json +{ + "total_rows": 2, + "offset": 0, + "rows": [ + { + "id": "8f3085a7-b65b-4648-9a78-8ac7de766997", + "type": "gpiiCloudSafeCredential", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-alice", + "gpiiExpressUserId": "org.couch.db.user:alice" + }, + { + "id": "57A68E84-03A9-4ADD-9365-11C75E4F1B0E", + "type": "gpiiKey", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-alice", + "prefsSetId": "gpii-default", + "revoked": false, + "revokedReason": null, + "timestampCreated": "2017-12-14T19:55:11.640Z", + "timestampUpdated": null + } + ] +} +``` + +### POST /prefsSafe-key-create + +This endpoint will add a new `gpiiKey` document to the system. There are no URL parameters, but the JSON body takes +3 fields, one of them optional. Required are the `prefsSafeId` and `prefsSetId`. These indicate the preferences safe, +and the respective preferences set that this key will operate upon. Optionally you can pass in a unique +unused `gpiiKey` to use, otherwise a new one will be generated as part of the process. The new document is returned +upon success. In event of a failure, a standard error document is returned with `isError` true, and a message +detailing the failure. + +#### Example POST: + +URL: `http://preferences.gpii.net/prefsSafe-key-create` + +Example POST Body: + +```json +{ + "prefsSafeId": "prefsSafe-alice", + "prefsSetId": "gpii-lowlight" +} +``` + +Example successful return payload: + +```json +{ + "id": "3B3D3003-9F5F-4B66-98C1-1380EC86DDB1", + "type": "gpiiKey", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-alice", + "prefsSetId": "gpii-lowlight", + "revoked": false, + "revokedReason": null, + "timestampCreated": "2017-12-14T19:55:11.640Z", + "timestampUpdated": null +} +``` + +In the event of any system error, an error payload with a suitable `message` is returned. + +```json +{ + "isError": true, + "errorCode": "GPII_ERR_APPLICABLE_MESSAGE", + "message": "System error described here." +} +``` + +### PUT /add-cloud-credentials/:prefsSafeId + +This endpoint will add a new username and password credential set to the preferences safe whose id is specified +in the URL parameter `prefsSafeId`. The body is a JSON payload containing the username and password for the new +credential set. + +#### Example PUT + +URL: `http://preferences.gpii.net/add-cloud-credentials/prefsSafe-1` + +The above indicates that the credentials will be added to preferences safe with `id` `prefsSafe-1`. + +putBody: + +```json +{ + "username": "NewLoginUsername", + "password": "v3ry$ecu4e" +} +``` + +Example successful return payload: + +```json +{ + "_id": "8f3085a7-b65b-4648-9a78-8ac7de766997", + "type": "gpiiCloudSafeCredential", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-1", + "gpiiExpressUserId": "org.couch.db.user:prefs1user" +} +``` + +In the event of any system error, an error payload with a suitable `message` is returned. + +```json +{ + "isError": true, + "errorCode": "GPII_ERR_APPLICABLE_MESSAGE", + "message": "System error described here." +} +``` + +### POST /unlock-cloud-safe + +This endpoint allows you to verify if a username and password combination will unclock a preferences safe that +has a matching cloud credential. It requires a username and password, but not a preferences safe ID. If the +unique combination unlocks a particular safe, that preferences safes `prefsSafe` document will be returned. + +#### Example post + +Using the `http://preferences.gpii.net/unlock-cloud-safe` with no parameters we can pass in the username and +password via the body. + +Example POST body: + +```json +{ + "username": "NewLoginUsername", + "password": "v3ry$ecu4e" +} +``` + +If successful this will return the preferences safe: + +```json +{ + "id": "prefsSafe-1", + "type": "prefsSafe", + "etc etc": "..." +} +``` + +If the username and password do not match a record the following error is returned: + +```json +{ + "isError": true, + "errorCode": "GPII_ERR_UNLOCKING_PREFSSAFE_CREDENTIALS", + "message": "Unable to unlock a Preferences Safe with the supplied credentials." +} +``` + +Any other system errors will return a similar `isError` payload with a suitable message for the failure +condition. + ## Other relevant documentation: * [The Preferences Server Framework](PreferencesServerFramework.md) diff --git a/gpii/node_modules/gpii-db-operation/src/DbConst.js b/gpii/node_modules/gpii-db-operation/src/DbConst.js index b00ce58a5..563cb73c8 100644 --- a/gpii/node_modules/gpii-db-operation/src/DbConst.js +++ b/gpii/node_modules/gpii-db-operation/src/DbConst.js @@ -15,13 +15,15 @@ var gpii = fluid.registerNamespace("gpii"); fluid.registerNamespace("gpii.dbOperation"); // The current version of schemas that document structures correspond with. -gpii.dbOperation.schemaVersion = "0.2"; +gpii.dbOperation.schemaVersion = "0.3"; // All doc types used for saving different documents into CouchDB // See [GPII Data Model](https://wiki.gpii.net/w/Keys,_KeyTokens,_and_Preferences#Data_Model) // regarding accepted fields for each document type. gpii.dbOperation.docTypes = fluid.freezeRecursive({ gpiiKey: "gpiiKey", + user: "user", // This is the CouchDB compatible record type gpii-express-user creates + gpiiCloudSafeCredential: "gpiiCloudSafeCredential", prefsSafe: "prefsSafe", clientCredential: "clientCredential", gpiiAppInstallationClient: "gpiiAppInstallationClient", diff --git a/gpii/node_modules/gpii-db-operation/src/DbDataStore.js b/gpii/node_modules/gpii-db-operation/src/DbDataStore.js index 5888e4700..07c38f549 100644 --- a/gpii/node_modules/gpii-db-operation/src/DbDataStore.js +++ b/gpii/node_modules/gpii-db-operation/src/DbDataStore.js @@ -97,6 +97,45 @@ fluid.defaults("gpii.dbOperation.dbDataStore", { } } }, + expressUserSafeLoginLookup: { + type: "gpii.dbOperation.dbDataSource", + options: { + requestUrl: "/_design/views/_view/expressUserSafeLoginLookup?include_docs=true&key=%22%gpiiExpressUserId%22", + termMap: { + gpiiExpressUserId: "%gpiiExpressUserId" + }, + rules: { + readPayload: { + "": "rows.0.doc" + } + } + } + }, + listPrefsSafesDataSource: { + type: "gpii.dbOperation.dbDataSource", + options: { + requestUrl: "/_design/views/_view/listPrefsSafes", + rules: { + readPayload: { + "": "" + } + } + } + }, + findRelatedDocsForPrefsSafeDataSource: { + type: "gpii.dbOperation.dbDataSource", + options: { + requestUrl: "/_design/views/_view/findRelatedDocsForPrefsSafe?include_docs=true&key=%22%prefsSafeId%22", + termMap: { + prefsSafeId: "%prefsSafeId" + }, + rules: { + readPayload: { + "": "" + } + } + } + }, findPrefsSafeByGpiiKeyDataSource: { type: "gpii.dbOperation.dbDataSource", options: { @@ -167,6 +206,22 @@ fluid.defaults("gpii.dbOperation.dbDataStore", { ] // id }, + findPrefsSafeList: { + funcName: "gpii.dbOperation.dbDataStore.findRecordList", + args: [ + "{that}.listPrefsSafesDataSource", + {} + ] + }, + findRelatedDocsForPrefsSafe: { + funcName: "gpii.dbOperation.dbDataStore.findRecordList", + args: [ + "{that}.findRelatedDocsForPrefsSafeDataSource", + { + prefsSafeId: "{arguments}.0" + } + ] + }, findGpiiKey: { func: "{that}.findById" // gpiiKey @@ -191,6 +246,16 @@ fluid.defaults("gpii.dbOperation.dbDataStore", { ] // gpiiKey }, + findSafeByExpressUserLookup: { + funcName: "gpii.dbOperation.dbDataStore.findRecord", + args: [ + "{that}.expressUserSafeLoginLookup", + { + gpiiExpressUserId: "{arguments}.0" + }, + "gpiiExpressUserId" + ] + }, findClientByOauth2ClientId: { funcName: "gpii.dbOperation.dbDataStore.findRecord", args: [ diff --git a/gpii/node_modules/gpii-db-operation/src/DbDataStoreUtils.js b/gpii/node_modules/gpii-db-operation/src/DbDataStoreUtils.js index 1fb7fd042..b725ae721 100644 --- a/gpii/node_modules/gpii-db-operation/src/DbDataStoreUtils.js +++ b/gpii/node_modules/gpii-db-operation/src/DbDataStoreUtils.js @@ -74,6 +74,37 @@ gpii.dbOperation.dbDataStore.findRecord = function (dataSource, directModel, val return promiseTogo; }; +/** + * Similar to `gpii.dbOperation.dbDataStore.findRecord` but for CouchDB dataSources meant to return + * a list of records. + * + * Eventually this method, and the related CouchDB dataSources will take a standard set of options + * for paging and sorting. Because the dataProcessFunc can be operating on each record, ideally this + * should always return a reasonable amount of records you'd want to display say, on a page. + * + * @param {Component} dataSource - An instance of gpii.dbOperation.dbDataSource. + * @param {Object} directModel - The direct model expressing the "coordinates" of the model to be fetched. + * @param {Function} dataProcessFunc - The function to further process the retrieved record when the returned + * record is not empty. + * @return {Promise} A promise for the retrieved record. + */ +gpii.dbOperation.dbDataStore.findRecordList = function (dataSource, directModel, dataProcessFunc) { + dataProcessFunc = dataProcessFunc || gpii.dbOperation.dbDataStore.cleanUpDocList; + var promiseTogo = fluid.promise(); + + var finalDirectModel = fluid.extend(true, {}, dataSource.options.directModel, directModel); + var promise = dataSource.get(finalDirectModel); + promise.then(function (data) { + dataProcessFunc(data); + promiseTogo.resolve(data); + }, function (error) { + fluid.log("gpii-db-operation, findRecordList(), error occurs: ", error); + promiseTogo.reject(error); + }); + + return promiseTogo; +}; + /** * Filter the given array valueNotEmpty to return elements that satisfy: * 1. the element isn't used as a path name in the object; @@ -113,6 +144,39 @@ gpii.dbOperation.dbDataStore.cleanUpDoc = function (data) { return data; }; +/** + * Modifies a list of records from CouchDB, such that the rows entry will now + * contain a list of the regular documents, rather than a key/id/doc entry. + * This will look for both options from a view, using first the `value` entry + * if it's available, and then checking for the `doc` entry on each item. The + * `doc` is what is available when your map view emits a `null` reference but + * the query is run to retrieve the document references. + * + * @param {Object} data - A CouchDB record list to transform. + * @return {Object} An object with CouchDB/PouchDB specific internal fields being transformed. + */ +gpii.dbOperation.dbDataStore.cleanUpDocList = function (data) { + if (data.rows) { + fluid.each(data.rows, function (item, idx) { + var toProcess; + if (item.value && item.value !== null) { + toProcess = item.value; + } + else if (item.doc) { + toProcess = item.doc; + } + else { + fluid.fail("Missing both the value or doc for record: ", item.key); + } + toProcess.id = item.id; + delete toProcess._id; + delete toProcess._rev; + data.rows[idx] = toProcess; + }); + } + return data; +}; + /** Use the kettle dataSource `set` method to create a new record. Before sending the input data to * CouchDB, it is modified by adding an unique _id field and a proper document type. * @param {Component} dataSource - An instance of gpii.dbOperation.dbDataSource that handles the record creation. @@ -287,7 +351,6 @@ gpii.dbOperation.dbDataStore.updateGpiiKey = function (saveDataSource, gpiiKey, * type: {String}, * schemaVersion: {String}, * name: {String}, - * password: {String}, * email: {String}, * preferences: {Object}, * timestampCreated: {Date}, @@ -324,7 +387,6 @@ gpii.dbOperation.dbDataStore.findPrefsSafeByGpiiKeyPostProcess = function (data) * { * prefsSafeType: {String}, * name: {String}, - * password: {String}, * email: {String}, * preferences: {Object} * } @@ -344,7 +406,6 @@ gpii.dbOperation.dbDataStore.addPrefsSafe = function (saveDataSource, prefsSafeD schemaVersion: gpii.dbOperation.schemaVersion, prefsSafeType: prefsSafeData.prefsSafeType, name: prefsSafeData.name, - password: prefsSafeData.password, email: prefsSafeData.email, preferences: prefsSafeData.preferences, timestampCreated: gpii.dbOperation.getCurrentTimestamp(), @@ -366,7 +427,6 @@ gpii.dbOperation.dbDataStore.addPrefsSafe = function (saveDataSource, prefsSafeD * schemaVersion: {String}, * prefsSafeType: {String}, * name: {String}, - * password: {String}, * email: {String}, * preferences: {Object}, * timestampCreated: {Date} @@ -391,7 +451,6 @@ gpii.dbOperation.dbDataStore.updatePrefsSafe = function (saveDataSource, prefsSa schemaVersion: prefsSafeData.schemaVersion, prefsSafeType: prefsSafeData.prefsSafeType, name: prefsSafeData.name || null, - password: prefsSafeData.password || null, email: prefsSafeData.email || null, preferences: prefsSafeData.preferences, timestampCreated: prefsSafeData.timestampCreated, diff --git a/gpii/node_modules/gpii-db-operation/src/DbUtils.js b/gpii/node_modules/gpii-db-operation/src/DbUtils.js index 4113d1bc7..13dcea0b4 100644 --- a/gpii/node_modules/gpii-db-operation/src/DbUtils.js +++ b/gpii/node_modules/gpii-db-operation/src/DbUtils.js @@ -36,3 +36,40 @@ gpii.dbOperation.composeError = function (error, termMap) { gpii.dbOperation.getCurrentTimestamp = function () { return new Date().toISOString(); }; + +/** + * This function looks at an item to make sure it exists properly, useful for + * looking at the results of a `{dataStore}.findById` or similar operation. If + * the item exists as it should, a promise resolved with the item is + * returned, otherwise a rejected promise is returned with the supplied `errorMessage`. + * + * The current criteria for validity is that the item is not undefined and has + * the `type` that is supplied in the arguments. + * + * @param {Object} item - Item, mostly a return from another API or CouchDB call + * that we are checking to make sure it's of the appropriate type. + * @param {String} type - The `type` that the item should be of. This is to + * follow the convention we have for CouchDB records where each record has a `type` + * member. + * @param {String} errorMessage - If this isn't the appropriate type, this message will be + * included in the promise rejection payload. + * @param {String} errorCode - If this isn't the appropriate type, this error code will be + * included in the promise rejection payload. + * @return {fluid.promise} Promise, when the item is valid, it resolves with the + * original item. When the item is not valid, it rejects with an error payload including + * the supplied `errorMessage`. + */ +gpii.dbOperation.verifyExists = function (item, type, errorMessage, errorCode) { + var togo = fluid.promise(); + if (item && item.type === type) { + togo.resolve(item); + } + else { + togo.reject({ + isError: true, + errorCode: errorCode, + message: errorMessage + }); + } + return togo; +}; diff --git a/gpii/node_modules/gpii-db-operation/test/DbDataStoreTests.js b/gpii/node_modules/gpii-db-operation/test/DbDataStoreTests.js index 466f6a9f7..b1d2eef2e 100644 --- a/gpii/node_modules/gpii-db-operation/test/DbDataStoreTests.js +++ b/gpii/node_modules/gpii-db-operation/test/DbDataStoreTests.js @@ -510,6 +510,54 @@ fluid.defaults("gpii.tests.dbDataStore.addAuthorization", { }] }); +fluid.defaults("gpii.tests.dbDataStore.findPrefsSafeList", { + gradeNames: ["gpii.tests.dbDataStore.environment"], + rawModules: [{ + name: "Test findPrefsSafeList()", + tests: [{ + name: "List prefs safes", + sequence: [{ + task: "{dbDataStore}.findPrefsSafeList", + args: [undefined], + resolve: "jqUnit.assertDeepEq", + resolveArgs: ["Expecing a list of all the safes.", gpii.tests.dbDataStore.testData.findPrefsSafeList_listing, "{arguments}.0"] + }] + }] + }] +}); + +fluid.defaults("gpii.tests.dbDataStore.findRelatedDocsForPrefsSafe", { + gradeNames: ["gpii.tests.dbDataStore.environment"], + rawModules: [{ + name: "Test findRelatedDocsForPrefsSafe()", + tests: [{ + name: "List prefs safes", + sequence: [{ + task: "{dbDataStore}.findRelatedDocsForPrefsSafe", + args: ["prefsSafe-1"], + resolve: "jqUnit.assertLeftHand", + resolveArgs: ["Expecting a list of keys for prefsSafe-1.", gpii.tests.dbDataStore.testData.findRelatedDocsForPrefsSafe_prefsSafe1, "{arguments}.0"] + }] + }] + }] +}); + +fluid.defaults("gpii.tests.dbDataStore.findSafeByExpressUserLookup", { + gradeNames: ["gpii.tests.dbDataStore.environment"], + rawModules: [{ + name: "Test findSafeByExpressUserLookup()", + tests: [{ + name: "List prefs safes", + sequence: [{ + task: "{dbDataStore}.findSafeByExpressUserLookup", + args: ["org.couch.db.user:prefs1user"], + resolve: "jqUnit.assertDeepEq", + resolveArgs: ["Expecting a safe from the express lookup.", gpii.tests.dbDataStore.testData.findSafeByExpressUserLookup_credential, "{arguments}.0"] + }] + }] + }] +}); + fluid.test.runTests([ "gpii.tests.dbDataStore.findGpiiKey", "gpii.tests.dbDataStore.findClientById", @@ -522,5 +570,8 @@ fluid.test.runTests([ "gpii.tests.dbDataStore.updateGpiiKey", "gpii.tests.dbDataStore.addPrefsSafe", "gpii.tests.dbDataStore.updatePrefsSafe", - "gpii.tests.dbDataStore.addAuthorization" + "gpii.tests.dbDataStore.addAuthorization", + "gpii.tests.dbDataStore.findPrefsSafeList", + "gpii.tests.dbDataStore.findRelatedDocsForPrefsSafe", + "gpii.tests.dbDataStore.findSafeByExpressUserLookup" ]); diff --git a/gpii/node_modules/gpii-db-operation/test/DbDataStoreTestsUtils.js b/gpii/node_modules/gpii-db-operation/test/DbDataStoreTestsUtils.js index 318770bf3..ba019ad0c 100644 --- a/gpii/node_modules/gpii-db-operation/test/DbDataStoreTestsUtils.js +++ b/gpii/node_modules/gpii-db-operation/test/DbDataStoreTestsUtils.js @@ -27,6 +27,7 @@ fluid.defaults("gpii.tests.dbDataStore.environment", { "%gpii-db-operation/test/data/clientCredentials.json", "%gpii-db-operation/test/data/gpiiAppInstallationAuthorizations.json", "%gpii-db-operation/test/data/gpiiAppInstallationClients.json", + "%gpii-db-operation/test/data/gpiiCloudSafeCred.json", "%gpii-db-operation/test/data/gpiiKeys.json", "%gpii-db-operation/test/data/prefsSafes.json", "%gpii-universal/testData/dbData/views.json" @@ -134,7 +135,7 @@ gpii.tests.dbDataStore.testData = { gpiiKeyChromehcDefault: { "id": "chrome_high_contrast", "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": "prefsSafe-1", "prefsSetId": "gpii-default", "revoked": false, @@ -145,7 +146,7 @@ gpii.tests.dbDataStore.testData = { client1: { "id": "gpiiAppInstallationClient-1", "type": "gpiiAppInstallationClient", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "name": "AJC-Bakersfield", "computerType": "public", "timestampCreated": "2017-11-21T18:11:22.101Z", @@ -154,7 +155,7 @@ gpii.tests.dbDataStore.testData = { clientCredential1: { "id": "clientCredential-1", "type": "clientCredential", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": "gpiiAppInstallationClient-1", "oauth2ClientId": "net.gpii.ajc.bakersfield", "oauth2ClientSecret": "client_secret_ajc_bakersfield", @@ -168,7 +169,7 @@ gpii.tests.dbDataStore.testData = { "gpiiKeyDetails": { "id": "chrome_high_contrast", "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": "prefsSafe-1", "prefsSetId": "gpii-default", "revoked": false, @@ -179,10 +180,9 @@ gpii.tests.dbDataStore.testData = { "prefsSafe": { "id": "prefsSafe-1", "type": "prefsSafe", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeType": "user", "name": null, - "password": null, "email": null, "preferences": { "flat": { @@ -213,7 +213,7 @@ gpii.tests.dbDataStore.testData = { "gpiiKeyDetails": { "id": "chrome_high_contrast_dark", "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": null, "prefsSetId": null, "revoked": false, @@ -227,7 +227,7 @@ gpii.tests.dbDataStore.testData = { "oauth2ClientId": "net.gpii.ajc.bakersfield", "client": { "type": "gpiiAppInstallationClient", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "name": "AJC-Bakersfield", "computerType": "public", "timestampCreated": "2017-11-21T18:11:22.101Z", @@ -236,7 +236,7 @@ gpii.tests.dbDataStore.testData = { }, "clientCredential": { "type": "clientCredential", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": "gpiiAppInstallationClient-1", "oauth2ClientId": "net.gpii.ajc.bakersfield", "oauth2ClientSecret": "client_secret_ajc_bakersfield", @@ -258,7 +258,7 @@ gpii.tests.dbDataStore.testData = { }, gpiiKeyToUpdate: { "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": "prefsSafe-1", "prefsSetId": "updated_name", "revoked": false, @@ -269,7 +269,6 @@ gpii.tests.dbDataStore.testData = { prefsSafeToCreate: { "prefsSafeType": "user", "name": null, - "password": null, "email": null, "preferences": { "test": "test" @@ -277,10 +276,9 @@ gpii.tests.dbDataStore.testData = { }, prefsSafeToUpdate: { "type": "prefsSafe", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeType": "snapset", "name": "updated_name", - "password": "updated_password", "email": "updated_email", "preferences": { "test": "test" @@ -298,7 +296,7 @@ gpii.tests.dbDataStore.testData = { "accessToken": "gpii-app-installation-accessToken-1", "clientCredential": { "type": "clientCredential", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": "gpiiAppInstallationClient-1", "oauth2ClientId": "net.gpii.ajc.bakersfield", "oauth2ClientSecret": "client_secret_ajc_bakersfield", @@ -310,7 +308,7 @@ gpii.tests.dbDataStore.testData = { }, "authorization": { "type": "gpiiAppInstallationAuthorization", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": "gpiiAppInstallationClient-1", "gpiiKey": "chrome_high_contrast", "clientCredentialId": "clientCredential-1", @@ -322,5 +320,47 @@ gpii.tests.dbDataStore.testData = { "timestampExpires": "3020-05-30T17:54:00.000Z", "id": "gpiiAppInstallationAuthorization-1" } + }, + findPrefsSafeList_listing: { + "total_rows": 1, + "offset": 0, + "rows": [ + { + "name": null, + "email": null, + "created": "2017-12-01T18:43:32.889Z", + "updated": null, + "id": "prefsSafe-1" + } + ] + }, + findRelatedDocsForPrefsSafe_prefsSafe1: { + "rows": [ + { + "type": "gpiiCloudSafeCredential", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-1", + "gpiiExpressUserId": "org.couch.db.user:prefs1user", + "id": "8f3085a7-b65b-4648-9a78-8ac7de766997" + }, + { + "type": "gpiiKey", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-1", + "prefsSetId": "gpii-default", + "revoked": false, + "revokedReason": null, + "timestampCreated": "2017-11-21T18:11:22.101Z", + "timestampUpdated": null, + "id": "chrome_high_contrast" + } + ] + }, + findSafeByExpressUserLookup_credential: { + "type": "gpiiCloudSafeCredential", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-1", + "gpiiExpressUserId": "org.couch.db.user:prefs1user", + "id": "8f3085a7-b65b-4648-9a78-8ac7de766997" } }; diff --git a/gpii/node_modules/gpii-db-operation/test/data/clientCredentials.json b/gpii/node_modules/gpii-db-operation/test/data/clientCredentials.json index 312503480..642176f6d 100644 --- a/gpii/node_modules/gpii-db-operation/test/data/clientCredentials.json +++ b/gpii/node_modules/gpii-db-operation/test/data/clientCredentials.json @@ -2,7 +2,7 @@ { "_id": "clientCredential-1", "type": "clientCredential", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": "gpiiAppInstallationClient-1", "oauth2ClientId": "net.gpii.ajc.bakersfield", "oauth2ClientSecret": "client_secret_ajc_bakersfield", @@ -14,7 +14,7 @@ { "_id": "clientCredential-2", "type": "clientCredential", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": "gpiiAppInstallationClient-2", "oauth2ClientId": "net.gpii.ajc.richmond", "oauth2ClientSecret": "client_secret_ajc_richmond", diff --git a/gpii/node_modules/gpii-db-operation/test/data/gpiiAppInstallationAuthorizations.json b/gpii/node_modules/gpii-db-operation/test/data/gpiiAppInstallationAuthorizations.json index 2a2453459..b9ce8228e 100644 --- a/gpii/node_modules/gpii-db-operation/test/data/gpiiAppInstallationAuthorizations.json +++ b/gpii/node_modules/gpii-db-operation/test/data/gpiiAppInstallationAuthorizations.json @@ -2,7 +2,7 @@ { "_id": "gpiiAppInstallationAuthorization-1", "type": "gpiiAppInstallationAuthorization", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": "gpiiAppInstallationClient-1", "gpiiKey": "chrome_high_contrast", "clientCredentialId": "clientCredential-1", @@ -16,7 +16,7 @@ { "_id": "gpiiAppInstallationAuthorization-2", "type": "gpiiAppInstallationAuthorization", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": "gpiiAppInstallationClient-2", "gpiiKey": "chrome_high_contrast", "clientCredentialId": "clientCredential-2", @@ -30,7 +30,7 @@ { "_id": "gpiiAppInstallationAuthorization-3", "type": "gpiiAppInstallationAuthorization", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": "gpiiAppInstallationClient-2", "gpiiKey": "chrome_high_contrast", "clientCredentialId": "clientCredential-2", diff --git a/gpii/node_modules/gpii-db-operation/test/data/gpiiAppInstallationClients.json b/gpii/node_modules/gpii-db-operation/test/data/gpiiAppInstallationClients.json index adf2efc56..18024d6c4 100644 --- a/gpii/node_modules/gpii-db-operation/test/data/gpiiAppInstallationClients.json +++ b/gpii/node_modules/gpii-db-operation/test/data/gpiiAppInstallationClients.json @@ -2,7 +2,7 @@ { "_id": "gpiiAppInstallationClient-1", "type": "gpiiAppInstallationClient", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "name": "AJC-Bakersfield", "computerType": "public", "timestampCreated": "2017-11-21T18:11:22.101Z", @@ -11,7 +11,7 @@ { "_id": "gpiiAppInstallationClient-2", "type": "gpiiAppInstallationClient", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "name": "AJC-Richmond", "computerType": "public", "timestampCreated": "2017-11-21T18:11:22.101Z", diff --git a/gpii/node_modules/gpii-db-operation/test/data/gpiiCloudSafeCred.json b/gpii/node_modules/gpii-db-operation/test/data/gpiiCloudSafeCred.json new file mode 100644 index 000000000..826148920 --- /dev/null +++ b/gpii/node_modules/gpii-db-operation/test/data/gpiiCloudSafeCred.json @@ -0,0 +1,23 @@ +[ + { + "name": "prefs1user", + "type": "user", + "email": "prefs1user@gpii.org", + "roles": [], + "username": "prefs1user", + "verified": true, + "iterations": 10, + "password_scheme": "pbkdf2", + "salt": "7cf6961e6ded3bd25732e5466512d116bf9908ba9629d4ed060a03a965e5341d", + "derived_key": "e8bd265e7d82fd0f662e9ddaaf2e75acb294da1b", + "verification_code": "618fa72aa62af282704b556e34957a79", + "_id": "org.couch.db.user:prefs7user" + }, + { + "type": "gpiiCloudSafeCredential", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-1", + "gpiiExpressUserId": "org.couch.db.user:prefs1user", + "_id": "8f3085a7-b65b-4648-9a78-8ac7de766997" + } +] diff --git a/gpii/node_modules/gpii-db-operation/test/data/gpiiKeys.json b/gpii/node_modules/gpii-db-operation/test/data/gpiiKeys.json index 3b318f739..50cbba643 100644 --- a/gpii/node_modules/gpii-db-operation/test/data/gpiiKeys.json +++ b/gpii/node_modules/gpii-db-operation/test/data/gpiiKeys.json @@ -2,7 +2,7 @@ { "_id": "chrome_high_contrast", "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": "prefsSafe-1", "prefsSetId": "gpii-default", "revoked": false, @@ -13,7 +13,7 @@ { "_id": "chrome_high_contrast_dark", "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": null, "prefsSetId": null, "revoked": false, diff --git a/gpii/node_modules/gpii-db-operation/test/data/prefsSafes.json b/gpii/node_modules/gpii-db-operation/test/data/prefsSafes.json index 9fd2dfc47..e82a56b67 100644 --- a/gpii/node_modules/gpii-db-operation/test/data/prefsSafes.json +++ b/gpii/node_modules/gpii-db-operation/test/data/prefsSafes.json @@ -2,10 +2,9 @@ { "_id": "prefsSafe-1", "type": "prefsSafe", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeType": "user", "name": null, - "password": null, "email": null, "preferences": { "flat": { diff --git a/gpii/node_modules/gpii-oauth2/gpii-oauth2-authz-server/test/data/authorizationServiceTests-data.json b/gpii/node_modules/gpii-oauth2/gpii-oauth2-authz-server/test/data/authorizationServiceTests-data.json index 476d31065..202d605b1 100644 --- a/gpii/node_modules/gpii-oauth2/gpii-oauth2-authz-server/test/data/authorizationServiceTests-data.json +++ b/gpii/node_modules/gpii-oauth2/gpii-oauth2-authz-server/test/data/authorizationServiceTests-data.json @@ -2,7 +2,7 @@ { "_id": "alice_gpii_key", "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": "prefsSafe-1", "prefsSetId": "gpii-default", "revoked": false, @@ -13,7 +13,7 @@ { "_id": "gpiiAppInstallationClient-1", "type": "gpiiAppInstallationClient", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "name": "AJC1", "computerType": "public", "timestampCreated": "2017-11-21T18:11:22.101Z", @@ -22,7 +22,7 @@ { "_id": "clientCredential-1", "type": "clientCredential", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": "gpiiAppInstallationClient-1", "oauth2ClientId": "client_id_AJC1", "oauth2ClientSecret": "client_secret_AJC1", @@ -34,7 +34,7 @@ { "_id": "gpiiAppInstallationClient-2", "type": "unknownClient", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "name": "test", "computerType": "public", "timestampCreated": "2017-11-21T18:11:22.101Z", @@ -43,7 +43,7 @@ { "_id": "clientCredential-2", "type": "clientCredential", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": "gpiiAppInstallationClient-2", "oauth2ClientId": "client_id_test", "oauth2ClientSecret": "client_secret_test", @@ -55,7 +55,7 @@ { "_id": "clientCredential-3", "type": "unknownClientCredential", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": "gpiiAppInstallationClient-2", "oauth2ClientId": "client_id_test-3", "oauth2ClientSecret": "client_secret_test-3", diff --git a/gpii/node_modules/lifecycleManager/test/html/LifecycleManagerUtilsTest.html b/gpii/node_modules/lifecycleManager/test/html/LifecycleManagerUtilsTest.html deleted file mode 100644 index bb79b1b11..000000000 --- a/gpii/node_modules/lifecycleManager/test/html/LifecycleManagerUtilsTest.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - GPII Lifecycle Manager Utilities Tests - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

GPII Lifecycle Manager Utilities Tests

-

-
-

-
    - - - diff --git a/gpii/node_modules/preferencesServer/index.js b/gpii/node_modules/preferencesServer/index.js index 968d4b0ff..b9126f2f6 100644 --- a/gpii/node_modules/preferencesServer/index.js +++ b/gpii/node_modules/preferencesServer/index.js @@ -10,6 +10,8 @@ require("./src/preferencesPostHandler.js"); require("./src/preferencesPutHandler.js"); require("./src/readyGetHandler.js"); require("../flowManager/src/HealthGetHandler.js"); -require("./src/preferencesService.js"); require("./src/preferencesServerConst.js"); +require("./src/preferencesService.js"); require("./src/preferencesServer.js"); +require("./src/prefsSafesHandlers.js"); +require("./src/cloudSafeCredHandlers.js"); diff --git a/gpii/node_modules/preferencesServer/src/cloudSafeCredHandlers.js b/gpii/node_modules/preferencesServer/src/cloudSafeCredHandlers.js new file mode 100644 index 000000000..474782cc2 --- /dev/null +++ b/gpii/node_modules/preferencesServer/src/cloudSafeCredHandlers.js @@ -0,0 +1,76 @@ +/*! +GPII Preferences Server Cloud Safe Credentials Handler + +Copyright 2014 Raising the Floor - International +Copyright 2018 OCAD University + +Licensed under the New BSD license. You may not use this file except in +compliance with this License. + +The research leading to these results has received funding from the European Union's +Seventh Framework Programme (FP7/2007-2013) under grant agreement no. 289016. + +You may obtain a copy of the License at +https://github.com/GPII/universal/blob/master/LICENSE.txt +*/ + +"use strict"; + +var fluid = require("infusion"), + gpii = fluid.registerNamespace("gpii"); + +fluid.defaults("gpii.preferencesServer.cloudSafeCredCreate.handler", { + gradeNames: ["kettle.request.http"], + invokers: { + handleRequest: { + funcName: "gpii.preferencesServer.cloudSafeCredCreate.handler.put", + args: ["{gpii.preferencesServer.preferencesService}", "{preferencesServer}", "{that}", "{that}.req.params", "{that}.req.body"] + } + } +}); + +gpii.preferencesServer.cloudSafeCredCreate.handler.put = function (prefsService, prefsServer, request, params, body) { + prefsService.cloudSafeCredCreate({ + prefsSafeId: params.prefsSafeId, + username: body.username, + password: body.password + }).then(request.events.onSuccess.fire, + function (err) { + request.events.onError.fire({ + isError: true, + errorCode: err.errorCode, + message: err.message, + statusCode: 404 + }); + } + ); +}; + +fluid.defaults("gpii.preferencesServer.cloudSafeUnlockPost.handler", { + gradeNames: ["kettle.request.http"], + invokers: { + handleRequest: { + funcName: "gpii.preferencesServer.cloudSafeUnlockPost.handler.post", + args: ["{gpii.preferencesServer.preferencesService}", "{preferencesServer}", "{that}", "{that}.req.params.prefsSafeId", "{that}.req.body"] + } + } +}); + +gpii.preferencesServer.cloudSafeUnlockPost.handler.post = function (prefsService, prefsServer, request, prefsSafeId, body) { + prefsService.cloudSafeUnlock({ + username: body.username, + password: body.password + }).then( + function (data) { + request.events.onSuccess.fire(data); + }, + function (err) { + request.events.onError.fire({ + isError: true, + errorCode: err.errorCode, + message: err.message, + statusCode: 401 + }); + } + ); +}; diff --git a/gpii/node_modules/preferencesServer/src/preferencesServer.js b/gpii/node_modules/preferencesServer/src/preferencesServer.js index 5668d9168..658dadaed 100644 --- a/gpii/node_modules/preferencesServer/src/preferencesServer.js +++ b/gpii/node_modules/preferencesServer/src/preferencesServer.js @@ -51,6 +51,52 @@ fluid.defaults("gpii.preferencesServer", { route: "/preferences/:gpiiKey", method: "put", type: "gpii.preferencesServer.put.handler" + }, + // GPII-2966 + prefsSafeGet: { + route: "/prefsSafe/:prefsSafeId", + method: "get", + type: "gpii.preferencesServer.prefsSafeGet.handler" + }, + prefsSafeWithKeysGet: { + route: "/prefsSafe-with-keys/:prefsSafeId", + method: "get", + type: "gpii.preferencesServer.prefsSafeWithKeysGet.handler" + }, + createPrefsSafe: { + route: "/prefsSafe", + method: "post", + type: "gpii.preferencesServer.prefsSafePost.handler" + }, + updatePrefsSafe: { + route: "/prefsSafe/:prefsSafeId", + method: "put", + type: "gpii.preferencesServer.prefsSafePut.handler" + }, + listPrefsSafes: { + route: "/prefsSafes", + method: "get", + type: "gpii.preferencesServer.prefsSafeList.handler" + }, + findRelatedDocsForPrefsSafe: { + route: "/prefsSafe-keys/:prefsSafeId", + method: "get", + type: "gpii.preferencesServer.findRelatedDocsForPrefsSafe.handler" + }, + prefsSafeKeyCreate: { + route: "/prefsSafe-key-create", + method: "post", + type: "gpii.preferencesServer.prefsSafeKeyCreate.handler" + }, + cloudSafeCredCreate: { + route: "/add-cloud-credentials/:prefsSafeId", + method: "put", + type: "gpii.preferencesServer.cloudSafeCredCreate.handler" + }, + cloudSafeUnlockPost: { + route: "/unlock-cloud-safe", + method: "post", + type: "gpii.preferencesServer.cloudSafeUnlockPost.handler" } }, invokers: { diff --git a/gpii/node_modules/preferencesServer/src/preferencesServerConst.js b/gpii/node_modules/preferencesServer/src/preferencesServerConst.js index 33146c600..019abda6e 100644 --- a/gpii/node_modules/preferencesServer/src/preferencesServerConst.js +++ b/gpii/node_modules/preferencesServer/src/preferencesServerConst.js @@ -39,5 +39,43 @@ gpii.preferencesServer.errors = fluid.freezeRecursive({ missingPreferences: { errorCode: "GPII_ERR_NO_PREFS", message: "Missing preferences" + }, + missingPrefsSafe: { + errorCode: "GPII_ERR_NO_PREFSSAFE", + message: "Missing prefsSafe" + }, + // Catch all error for unaccepted errors + errorAccessingPrefsSafe: { + errorCode: "GPII_ERR_ACCESSING_PREFSSAFE", + message: "Error accessing prefsSafe" + }, + errorNotPrefsSafe: { + errorCode: "GPII_ERR_NOT_PREFSSAFE", + message: "Payload is not of type `prefsSafe`" + }, + errorNotPrefsSafeMissingId: { + errorCode: "GPII_ERR_NOT_PREFSSAFE_MISSING_ID", + message: "Payload requires an `id` and type of `prefsSafe`." + }, + errorUrlPayloadIdMismatch: { + errorCode: "GPII_ERR_URL_PAYLOAD_ID_MISMATCH", + message: "The ID parameter in the URL does not match the ID in the payload." + }, + errorMissingPrefsSafeAndSetId: { + errorCode: "GPII_ERR_MISSING_PREFSAFE_PREFSET_IDS", + message: "Unable to process. `prefsSafeId` and `prefsSetId` are required fields." + }, + errorCreateCredBadUsernamePassword: { + errorCode: "GPII_ERR_CREATE_CRED_BAD_USERNAME_PASSWORD", + message: "Unable to create credentials with this username and password combination" + }, + errorUnlockingPrefsSafeCredentials: { + errorCode: "GPII_ERR_UNLOCKING_PREFSSAFE_CREDENTIALS", + message: "Unable to unlock Preferences Safe with the supplied credentials." + }, + // Catch all error for unknown issues adding keys + errorAddingGpiiKey: { + errorCode: "GPII_ERR_ADDING_GPIIKEY", + message: "Unable to add GPII Key to Preferences Safe." } }); diff --git a/gpii/node_modules/preferencesServer/src/preferencesService.js b/gpii/node_modules/preferencesServer/src/preferencesService.js index 62e1240c5..8e65001ec 100644 --- a/gpii/node_modules/preferencesServer/src/preferencesService.js +++ b/gpii/node_modules/preferencesServer/src/preferencesService.js @@ -16,22 +16,58 @@ var fluid = fluid || require("infusion"); (function () { "use strict"; - var gpii = fluid.registerNamespace("gpii"); + var gpii = fluid.registerNamespace("gpii"), + $ = fluid.registerNamespace("jQuery"); + require("gpii-express-user"); fluid.defaults("gpii.preferencesServer.preferencesService", { gradeNames: ["fluid.component"], components: { dataStore: { type: "gpii.dbOperation.dataStore" + }, + expressUserUtils: { + type: "gpii.express.user.utils", + options: { + couch: { + userDbUrl: { + expander: { + funcName: "fluid.stringTemplate", + args: ["%baseUrl:%port/%dbName", { + baseUrl: "{dataStore}.options.dataSourceConfig.baseUrl", + port: "{dataStore}.options.dataSourceConfig.port", + dbName: "{dataStore}.options.dataSourceConfig.dbName" + }] + } + } + } + } } }, invokers: { + getPrefsSafe: { + funcName: "gpii.preferencesServer.preferencesService.getPrefsSafe", + args: ["{dataStore}", "{arguments}.0"] + }, + getPrefsSafeList: { + func: "{dataStore}.findPrefsSafeList", + args: ["{arguments}.0"] + }, getPrefsSafeByGpiiKey: "{dataStore}.findPrefsSafeByGpiiKey", + getKeysForPrefsSafe: "{dataStore}.findRelatedDocsForPrefsSafe", getPreferencesByGpiiKey: { funcName: "gpii.preferencesServer.preferencesService.getPreferencesByGpiiKey", args: ["{dataStore}.findPrefsSafeByGpiiKey", "{arguments}.0"] // gpiiKey }, + addPrefsSafe: { + funcName: "gpii.preferencesServer.preferencesService.addPrefsSafe", + args: ["{that}", "{dataStore}", "{arguments}.0"] + }, + updatePrefsSafe: { + funcName: "gpii.preferencesServer.preferencesService.updatePrefsSafe", + args: ["{that}", "{dataStore}", "{arguments}.0"] + }, createPreferences: { funcName: "gpii.preferencesServer.preferencesService.createPreferences", args: ["{that}", "{arguments}.0", "{arguments}.1"] @@ -42,6 +78,19 @@ var fluid = fluid || require("infusion"); args: ["{that}", "{arguments}.0", "{arguments}.1", "{arguments}.2"] // preferences, gpiiKey, merge }, + // Add a GPII Key to an existing preferences safe + addGpiiKey: { + funcName: "gpii.preferencesServer.preferencesService.addGpiiKey", + args: ["{that}", "{dataStore}", "{arguments}.0"] + }, + cloudSafeCredCreate: { + funcName: "gpii.preferencesServer.preferencesService.cloudSafeCredCreate", + args: ["{that}", "{arguments}.0"] + }, + cloudSafeUnlock: { + funcName: "gpii.preferencesServer.preferencesService.cloudSafeUnlock", + args: ["{that}", "{arguments}.0"] + }, isLive: { funcName: "gpii.preferencesServer.preferencesService.isLive", args: ["{dataStore}.findAllViews"] @@ -49,7 +98,10 @@ var fluid = fluid || require("infusion"); }, events: { onCreatePreferences: null, - onAssociatePreferences: null + onAssociatePreferences: null, + onCloudSafeCredCreate: null, + onCloudSafeUnlock: null, + onUpdatePrefsSafe: null }, listeners: { onCreatePreferences: [{ @@ -73,12 +125,273 @@ var fluid = fluid || require("infusion"); listener: "gpii.preferencesServer.preferencesService.updateGpiiKey", args: ["{dataStore}.updateGpiiKey", "{arguments}.0"], namespace: "updateGpiiKey" - }] + }], + onCloudSafeCredCreate: [{ + func: "{dataStore}.findById", + args: ["{arguments}.0"], + namespace: "findById" + }, { + func: "gpii.dbOperation.verifyExists", + args: ["{arguments}.0", "prefsSafe", gpii.preferencesServer.errors.missingPrefsSafe.message, + gpii.preferencesServer.errors.missingPrefsSafe.errorCode] + }, { + func: "{expressUserUtils}.createNewUser", + args: [{username: "{arguments}.1.username", password: "{arguments}.1.password"}], + namespace: "createNewUser" + }, { + funcName: "gpii.dbOperation.dbDataStore.addRecord", + args: ["{dbDataStore}.saveDataSource", + "gpiiCloudSafeCredential", + "id", { + type: "gpiiCloudSafeCredential", + schemaVersion: "0.1", + prefsSafeId: "{arguments}.1.prefsSafeId", + gpiiExpressUserId: "{arguments}.0._id" + }], + namespace: "addRecord" + }, { + funcName: "{dataStore}.findById", + args: ["{arguments}.0.id"] + }], + onCloudSafeUnlock: [{ + func: "{expressUserUtils}.unlockUser", + args: ["{arguments}.0.username", "{arguments}.0.password"], // username, password + namespace: "unlockUser" + }, { + func: "{dataStore}.findSafeByExpressUserLookup", + args: ["{arguments}.0._id"], + namespace: "findSafeByExpressUserLookup" + }, { + func: "{dataStore}.findById", + args: ["{arguments}.0.prefsSafeId"], + namespace: "findById" + }], + // onUpdatePrefsSafe is a pseudoevent for a promise chain. The primary method call is + // `dataStore.updatePrefsSafe` which is flanked by calls to `getPrefsSafe` in order to confirm + // existence of the safe, and then to return the final contents + "onUpdatePrefsSafe.checkPrefsSetExist": { + func: "{that}.getPrefsSafe", + args: "{arguments}.1.id" + }, + "onUpdatePrefsSafe.updatePrefsSafe": { + func: "{dataStore}.updatePrefsSafe", + args: ["{arguments}.1.id", "{arguments}.1"], + priority: "after:checkPrefsSetExist" + }, + "onUpdatePrefsSafe.getPrefsSafe": { + func: "{that}.getPrefsSafe", + args: "{arguments}.1.id", + priority: "after:updatePrefsSafe" + } } }); // APIs for Preferences and Preferences Safes + gpii.preferencesServer.preferencesService.errors = fluid.freezeRecursive({ + gpiiKeyMissing: "GPII key \"%gpiiKey\" does not exist", + gpiiKeyExisted: "GPII key \"%gpiiKey\" already exists", + noUpdateOnSnapset: "Cannot update: GPII key \"%gpiiKey\" is a snapset" + }); + + /** + * Returns a preferences safe using it's id. If the safe is not found an error object + * is returned. + * + * @param {Object} dataStore - Instance of `gpii.dbOperation.dataStore` + * @param {String} prefsSafeId - Prefs Safe ID + * @return {Promise} Returns a promise resolving with the preferences safe, + * or rejecting with an error payload containing an error `message`. + */ + gpii.preferencesServer.preferencesService.getPrefsSafe = function (dataStore, prefsSafeId) { + var promiseTogo = fluid.promise(); + dataStore.findById(prefsSafeId).then( + function (data) { + if ($.isEmptyObject(data) || data.type !== "prefsSafe") { + promiseTogo.reject({ + isError: true, + errorCode: gpii.preferencesServer.errors.missingPrefsSafe.errorCode, + message: gpii.preferencesServer.errors.missingPrefsSafe.message + }); + } + else { + promiseTogo.resolve(data); + } + }, + function (err) { + fluid.log("Error looking up prefsSafe: ", err); + promiseTogo.reject({ + isError: true, + errorCode: gpii.preferencesServer.errors.errorAccessingPrefsSafe.errorCode, + message: gpii.preferencesServer.errors.errorAccessingPrefsSafe.message + }); + } + ); + return promiseTogo; + }; + + /** + * Adds a new preferences safe to CouchDB using the preferences safe format. + * + * @param {Object} that - Instance of `gpii.preferencesServer.preferencesService`. + * @param {Object} dataStore - Instance of `gpii.dbOperation.dataStore` + * @param {Object} prefsSafeData - Preferences safe to add to system. The following fields + * are not required as part of this payload, and will be automatically created upon add: + * `id`, `timestampCreated`, and `timestampUpdated` properties. + * @return {Promise} Returns a promise resolving with the newly created + * preferences safe, or rejecting with an error payload containing an error `message`. + */ + gpii.preferencesServer.preferencesService.addPrefsSafe = function (that, dataStore, prefsSafeData) { + var promiseTogo = fluid.promise(); + if (prefsSafeData.type !== "prefsSafe") { + promiseTogo.reject({ + isError: true, + errorCode: gpii.preferencesServer.errors.errorNotPrefsSafe.errorCode, + message: gpii.preferencesServer.errors.errorNotPrefsSafe.message + }); + } + else { + // TODO This will have json schema validation when the scaffolding is all in. + dataStore.addPrefsSafe(prefsSafeData).then( + function (updatedReturn) { + that.getPrefsSafe(updatedReturn.id).then(promiseTogo.resolve, promiseTogo.reject); + }, + promiseTogo.reject); + }; + return promiseTogo; + }; + + /** + * Updates a preferences safe in CouchDB. + * + * @param {Object} that - Instance of `gpii.preferencesServer.preferencesService`. + * @param {Object} dataStore - Instance of `gpii.dbOperation.dataStore` + * @param {Object} prefsSafeData - Preferences safe to add to system. + * @return {Promise} Returns a promise resolving with the newly created + * preferences safe, or rejecting with an error payload containing an error `message`. + */ + gpii.preferencesServer.preferencesService.updatePrefsSafe = function (that, dataStore, prefsSafeData) { + var promiseTogo = fluid.promise(); + if (!prefsSafeData.id || prefsSafeData.type !== "prefsSafe") { + promiseTogo.reject({ + isError: true, + errorCode: gpii.preferencesServer.errors.errorNotPrefsSafeMissingId.errorCode, + message: gpii.preferencesServer.errors.errorNotPrefsSafeMissingId.message + }); + } + else { + fluid.promise.fireTransformEvent(that.events.onUpdatePrefsSafe, {}, prefsSafeData).then( + function (data) { + promiseTogo.resolve(data); + }, + function (err) { + fluid.log(err); + promiseTogo.reject({ + isError: true, + errorCode: gpii.preferencesServer.errors.missingPrefsSafe.errorCode, + message: gpii.preferencesServer.errors.missingPrefsSafe.message + }); + } + ); + } + return promiseTogo; + }; + + /** + * Handler for creating cloud safe credentials. + * + * This handler will first fetch the preferences safe to make sure it + * exists and is active. It will then take the username and password and + * create a new gpii-express-user entry. With that we will then create + * the cloudsafe credential document that will contain the ID's of both + * the prefsSafe and the user record. + * + * Currently this method does only a small amount of validation on the username + * and password, requiring that both fields are included and of length greater than + * length zero. + * + * @param {Object} that - Instance of `gpii.preferencesServer.preferencesService`. + * @param {Object} options - Data used to create the new credentials. + * @param {String} options.prefsSafeId - Id for the preferences safe we are adding + * credentials to. + * @param {String} options.username - Login username for these credentials. + * @param {String} options.password - Password for these new credentials. + * @return {Promise} Promise containing the new cloud credentials document. + */ + gpii.preferencesServer.preferencesService.cloudSafeCredCreate = function (that, options) { + var promiseTogo = fluid.promise(); + + if (!options.username || !options.password) { + promiseTogo.reject({ + isError: true, + errorCode: gpii.preferencesServer.errors.errorCreateCredBadUsernamePassword.errorCode, + message: gpii.preferencesServer.errors.errorCreateCredBadUsernamePassword.message + }); + return promiseTogo; + } + + fluid.promise.fireTransformEvent(that.events.onCloudSafeCredCreate, options.prefsSafeId, options).then( + promiseTogo.resolve, + function (err) { + // Create a slightly more meaningful error message for the gpii-express-user reject when + // the username is already used. + // { + // "isError": true, + // "message": { + // "error": "conflict", + // "reason": "Document update conflict" + // } + // } + if (err.message && err.message.error === "conflict") { + fluid.log("Error adding cloud safe credential", err); + promiseTogo.reject({ + isError: true, + errorCode: gpii.preferencesServer.errors.errorCreateCredBadUsernamePassword.errorCode, + message: gpii.preferencesServer.errors.errorCreateCredBadUsernamePassword.message + }); + } + else { + promiseTogo.reject(err); + } + }); + return promiseTogo; + }; + + /** + * Handler for unlocking a prefs safe. + * + * Steps: + * 1. Try to unlock a gpii-express-user acct using the username/password. + * 2. If successful, take the gpii-express-user.id and look up a cloud credentials doc for it. + * 3. Fetch the Safe for that and return it. + * + * TODO: In the future the number of couch calls may by optimized. + * + * @param {Object} that - Instance of `gpii.preferencesServer.preferencesService`. + * @param {Object} options - Data used to unlock the safe. + * @param {String} options.username - Username to unlock the Preferences Safe. + * @param {String} options.password - Password to unlock the Preferences Safe. + * @return {Promise} Returns a promise containing either the unlocked Preferences Safe + * record, or an error payload with a message. + */ + gpii.preferencesServer.preferencesService.cloudSafeUnlock = function (that, options) { + var promiseTogo = fluid.promise(); + fluid.promise.fireTransformEvent(that.events.onCloudSafeUnlock, { + username: options.username, + password: options.password + }).then(promiseTogo.resolve, + function (err) { + fluid.log(err); + promiseTogo.reject({ + isError: true, + errorCode: gpii.preferencesServer.errors.errorUnlockingPrefsSafeCredentials.errorCode, + message: gpii.preferencesServer.errors.errorUnlockingPrefsSafeCredentials.message + }); + } + ); + return promiseTogo; + }; + /** * Grant an authorization for the give GPII app installation. The gpii key will be verified before the access token is returned. * @param {Component} findPrefsSafeByGpiiKey - An instance of gpii.preferencesServer.preferencesService. @@ -114,7 +427,6 @@ var fluid = fluid || require("infusion"); * { * prefsSafeType: {String} * name: {String} - * password: {String} * email: {String} * preferences: {Object} // must be provided * } @@ -140,7 +452,6 @@ var fluid = fluid || require("infusion"); * prefsSafeData: { * prefsSafeType: {String} * name: {String} - * password: {String} * email: {String} * preferences: {Object} // must be provided * } @@ -187,7 +498,6 @@ var fluid = fluid || require("infusion"); * prefsSafeData: { * prefsSafeType: {String}, // optional * name: {String}, // optional - * password: {String}, // optional * email: {String}, // optional * preferences: {Object} // must be provided * } @@ -200,7 +510,6 @@ var fluid = fluid || require("infusion"); var prefsSafeDataAdded = { prefsSafeType: prefsSafeData.prefsSafeType || gpii.preferencesServer.prefsSafeType.user, name: prefsSafeData.name || null, - password: prefsSafeData.password || null, email: prefsSafeData.email || null, preferences: prefsSafeData.preferences }; @@ -219,6 +528,63 @@ var fluid = fluid || require("infusion"); return promiseTogo; }; + /** + * Stand alone method to add a `gpiiKey` document in the system. `prefsSafeId` and `prefSetId` are + * required fields. Optionally you can specify the `gpiiKey` otherwise a new one will be generated. + * This key must not exist yet. + * @param {Object} that - Instance of `gpii.preferencesServer.preferencesService`. + * @param {Object} dataStore - Instance of `gpii.dbOperation.dataStore` + * @param {Object} keyData - Data for the new `gpiiKey` document. + * @return {Promise} A promise object that resolves to the new saved document, or rejects with a + * standard error payload. + */ + gpii.preferencesServer.preferencesService.addGpiiKey = function (that, dataStore, keyData) { + var promTogo = fluid.promise(); + + if (!keyData.prefsSafeId || !keyData.prefsSetId) { + promTogo.reject({ + isError: true, + errorCode: gpii.preferencesServer.errors.errorMissingPrefsSafeAndSetId.errorCode, + message: gpii.preferencesServer.errors.errorMissingPrefsSafeAndSetId.message + }); + } + else { + // Preferences Safe must exist to add this key + that.getPrefsSafe(keyData.prefsSafeId).then(function () { + // TODO This will have json schema validation when the scaffolding is all in. + // Add the new key... + dataStore.addGpiiKey({ + gpiiKey: keyData.gpiiKey, // Can be undefined + prefsSafeId: keyData.prefsSafeId, + prefsSetId: keyData.prefsSetId + }).then( + // ...and then fetch the newly added key and return it. + function (newKeyData) { + dataStore.findById(newKeyData.id).then(promTogo.resolve, promTogo.reject); + }, + function (error) { + fluid.log("Error adding key to preferences safe: ", keyData, error); + promTogo.reject({ + isError: true, + errorCode: gpii.preferencesServer.errors.errorAddingGpiiKey.errorCode, + message: gpii.preferencesServer.errors.errorAddingGpiiKey.message + }); + } + ); + }, + function (error) { + fluid.log("Error in addGpiiKey prefSafe Lookup", error); + promTogo.reject({ + isError: true, + errorCode: gpii.preferencesServer.errors.missingPrefsSafe.errorCode, + message: gpii.preferencesServer.errors.missingPrefsSafe.message + }); + }); + } + + return promTogo; + }; + /** * The last step in the promise transforming chain for implementing createPreferences() API. * Create a gpiiKey record with the provided input. @@ -230,7 +596,6 @@ var fluid = fluid || require("infusion"); * prefsSafeDataAdded: { * prefsSafeType: {String} * name: {String} - * password: {String} * email: {String} * preferences: {Object} // must be provided * } @@ -331,7 +696,6 @@ var fluid = fluid || require("infusion"); * prefsSafeDataAdded: { * prefsSafeType: {String} * name: {String} - * password: {String} * email: {String} * preferences: {Object} // must be provided * } diff --git a/gpii/node_modules/preferencesServer/src/prefsSafesHandlers.js b/gpii/node_modules/preferencesServer/src/prefsSafesHandlers.js new file mode 100644 index 000000000..48e8d3c9c --- /dev/null +++ b/gpii/node_modules/preferencesServer/src/prefsSafesHandlers.js @@ -0,0 +1,205 @@ +/*! +GPII Preferences Server GET Handler + +Copyright 2014 Raising the Floor - International +Copyright 2018 OCAD University + +Licensed under the New BSD license. You may not use this file except in +compliance with this License. + +The research leading to these results has received funding from the European Union's +Seventh Framework Programme (FP7/2007-2013) under grant agreement no. 289016. + +You may obtain a copy of the License at +https://github.com/GPII/universal/blob/master/LICENSE.txt +*/ + +"use strict"; + +var fluid = require("infusion"), + gpii = fluid.registerNamespace("gpii"); + +fluid.defaults("gpii.preferencesServer.prefsSafeGet.handler", { + gradeNames: ["kettle.request.http"], + invokers: { + handleRequest: { + funcName: "gpii.preferencesServer.prefsSafeGet.handler.getPrefsSafe", + args: ["{gpii.preferencesServer.preferencesService}", "{preferencesServer}", "{that}", "{that}.req.params.prefsSafeId"] + } + } +}); + +gpii.preferencesServer.prefsSafeGet.handler.getPrefsSafe = function (prefsService, preferencesServer, request, gpiiKey) { + prefsService.getPrefsSafe(gpiiKey).then(request.events.onSuccess.fire, + function (err) { + request.events.onError.fire({ + isError: true, + message: err.message, + errorCode: err.errorCode, + statusCode: 404 + }); + } + ); +}; + +fluid.defaults("gpii.preferencesServer.prefsSafeList.handler", { + gradeNames: ["kettle.request.http"], + invokers: { + handleRequest: { + funcName: "gpii.preferencesServer.prefsSafeList.handler.getPrefsSafeList", + args: ["{gpii.preferencesServer.preferencesService}", "{preferencesServer}", "{that}"] + } + } +}); + +gpii.preferencesServer.prefsSafeList.handler.getPrefsSafeList = function (prefsService, preferencesServer, request) { + prefsService.getPrefsSafeList().then(request.events.onSuccess.fire, + function (err) { + request.events.onError.fire({ + isError: true, + message: err.message, + errorCode: err.errorCode, + statusCode: 404 + }); + } + ); +}; + +fluid.defaults("gpii.preferencesServer.findRelatedDocsForPrefsSafe.handler", { + gradeNames: ["kettle.request.http"], + invokers: { + handleRequest: { + funcName: "gpii.preferencesServer.findRelatedDocsForPrefsSafe.handler.getKeysForPrefsSafe", + args: ["{gpii.preferencesServer.preferencesService}", "{preferencesServer}", "{that}", "{that}.req.params.prefsSafeId"] + } + } +}); + +gpii.preferencesServer.findRelatedDocsForPrefsSafe.handler.getKeysForPrefsSafe = function (prefsService, preferencesServer, request, prefsSafeId) { + prefsService.getPrefsSafe(prefsSafeId).then( + function (/* prefsSafeData */) { + prefsService.getKeysForPrefsSafe(prefsSafeId).then(request.events.onSuccess.fire, request.events.onError.fire); + }, + function (err) { + request.events.onError.fire({ + isError: true, + message: err.message, + errorCode: err.errorCode, + statusCode: 404 + }); + } + ); +}; + +fluid.defaults("gpii.preferencesServer.prefsSafeWithKeysGet.handler", { + gradeNames: ["kettle.request.http"], + invokers: { + handleRequest: { + funcName: "gpii.preferencesServer.prefsSafeWithKeysGet.handler.get", + args: ["{gpii.preferencesServer.preferencesService}", "{preferencesServer}", "{that}", "{that}.req.params.prefsSafeId"] + } + } +}); + +gpii.preferencesServer.prefsSafeWithKeysGet.handler.get = function (prefsService, preferencesServer, request, prefsSafeId) { + prefsService.getPrefsSafe(prefsSafeId).then( + function (prefsSafeData) { + prefsService.getKeysForPrefsSafe(prefsSafeId).then( + function (keysData) { + request.events.onSuccess.fire({ + prefsSafe: prefsSafeData, + keys: keysData.rows + }); + }, + request.events.onError.fire); + }, + function (err) { + request.events.onError.fire({ + isError: true, + message: err.message, + errorCode: err.errorCode, + statusCode: 404 + }); + } + ); +}; + +fluid.defaults("gpii.preferencesServer.prefsSafePost.handler", { + gradeNames: ["kettle.request.http"], + invokers: { + handleRequest: { + funcName: "gpii.preferencesServer.prefsSafePost.handler.updatePrefsSafe", + args: ["{preferencesService}", "{that}", "{that}.req.body"] + } + } +}); + +gpii.preferencesServer.prefsSafePost.handler.updatePrefsSafe = function (preferencesService, request, prefsSafe) { + preferencesService.addPrefsSafe(prefsSafe).then( + request.events.onSuccess.fire, + function (err) { + request.events.onError.fire({ + isError: true, + message: err.message, + errorCode: err.errorCode, + statusCode: 404 + }); + } + ); +}; + +fluid.defaults("gpii.preferencesServer.prefsSafePut.handler", { + gradeNames: ["kettle.request.http"], + invokers: { + handleRequest: { + funcName: "gpii.preferencesServer.prefsSafePut.handler.updatePrefsSafe", + args: ["{preferencesService}", "{that}", "{that}.req.params.prefsSafeId", "{that}.req.body"] + } + } +}); + +gpii.preferencesServer.prefsSafePut.handler.updatePrefsSafe = function (preferencesService, request, prefsSafeId, prefsSafe) { + if (prefsSafeId !== prefsSafe.id) { + request.events.onError.fire({ + isError: true, + message: gpii.preferencesServer.errors.errorUrlPayloadIdMismatch.message, + errorCode: gpii.preferencesServer.errors.errorUrlPayloadIdMismatch.errorCode, + statusCode: 404 + }); + }; + + preferencesService.updatePrefsSafe(prefsSafe).then( + request.events.onSuccess.fire, + function (err) { + request.events.onError.fire({ + isError: true, + message: err.message, + errorCode: err.errorCode, + statusCode: 404 + }); + } + ); +}; + +fluid.defaults("gpii.preferencesServer.prefsSafeKeyCreate.handler", { + gradeNames: ["kettle.request.http"], + invokers: { + handleRequest: { + funcName: "gpii.preferencesServer.prefsSafeKeyCreate.handler.addGpiiKey", + args: ["{preferencesService}", "{that}.req.body", "{that}"] + } + } +}); + +gpii.preferencesServer.prefsSafeKeyCreate.handler.addGpiiKey = function (preferencesService, body, request) { + preferencesService.addGpiiKey(body).then(request.events.onSuccess.fire, + function (err) { + request.events.onError.fire({ + isError: true, + message: err.message, + errorCode: err.errorCode, + statusCode: 404 + }); + } + ); +}; diff --git a/gpii/node_modules/preferencesServer/test/cloudSafeCredTests.js b/gpii/node_modules/preferencesServer/test/cloudSafeCredTests.js new file mode 100644 index 000000000..7106f3b30 --- /dev/null +++ b/gpii/node_modules/preferencesServer/test/cloudSafeCredTests.js @@ -0,0 +1,434 @@ +/** +GPII Preferences Server Tests + +Copyright 2018 Raising the Floor - International + +Licensed under the New BSD license. You may not use this file except in +compliance with this License. + +The research leading to these results has received funding from the European Union's +Seventh Framework Programme (FP7/2007-2013) under grant agreement no. 289016. + +You may obtain a copy of the License at +https://github.com/GPII/universal/blob/master/LICENSE.txt +*/ + +"use strict"; + +var fluid = require("infusion"), + jqUnit = fluid.require("node-jqunit", require, "jqUnit"), + gpii = fluid.registerNamespace("gpii"); + +require("./preferencesServerTestsUtils.js"); +require("./preferencesServerTests.js"); + +fluid.require("%gpii-universal"); +gpii.loadTestingSupport(); + +fluid.registerNamespace("gpii.tests.preferencesServer.cloudSafeCred"); + +gpii.tests.preferencesServer.config = { + configName: "gpii.tests.preferencesServer.config", + configPath: "%preferencesServer/test/configs" +}; + +/////////////// TEST Creating a Cloud Safe Login ///////////////// +// +// Steps 1: Send the POST to create a cloudSafeCred for an existing prefset +// Step 2: Verify that the Cloud Safe Cred Document is correct +// Step 3: Verify that the gpii-express-user document is correct +// Step 4: Verify that this is included in the list keys endpoint +// Step 5: Verify that this is included in the prefsSafe with keys endpoint +fluid.registerNamespace("gpii.tests.preferencesServer.cloudSafeCred.put"); + +/* + * Test response payload against a subset of a gpiiCloudSafeCred document. + */ +gpii.tests.preferencesServer.cloudSafeCred.testCloudSafeCreatePut = function (cloudCredSubset, response) { + var data = JSON.parse(response); + jqUnit.assertLeftHand("Returned cred document should have the following: ", cloudCredSubset, data); +}; + +/* + * Check and see if the prefs safe that came back has our new credentials doc. + */ +gpii.tests.preferencesServer.cloudSafeCred.testCloudSafeCred = function (response /*, expected, receivedStatusCode, expectedStatusCode */) { + var data = JSON.parse(response); + jqUnit.assertEquals("There should be a prefsSafe", data.prefsSafe.type, "prefsSafe"); + var keyTypes = fluid.getMembers(data.keys, "type"); + jqUnit.assertTrue("There should be a cloud cred", fluid.contains(keyTypes, "gpiiCloudSafeCredential")); + jqUnit.assertTrue("There should be a gpii key", fluid.contains(keyTypes, "gpiiKey")); +}; + +gpii.tests.preferencesServer.cloudSafeCred.put.buildSuccessTestDef = function (fixture) { + var expectedStatusCode = fixture.expectedStatusCode || 200; + return { + name: fixture.name, + config: gpii.tests.preferencesServer.config, + components: { + putCloudCred: { + type: "kettle.test.request.http", + options: { + path: "/add-cloud-credentials/" + fixture.url, + method: "PUT", + port: 8081, + termMap: { + prefsSafeId: fixture.prefsSafeId + } + } + }, + getFullSafe: { + type: "kettle.test.request.http", + options: { + path: "/prefsSafe-with-keys/" + fixture.url, + method: "GET", + port: 8081, + termMap: { + prefsSafeId: fixture.prefsSafeId + } + } + }, + unlockCloudSafe: { + type: "kettle.test.request.http", + options: { + path: "/unlock-cloud-safe", + method: "POST", + port: 8081 + } + } + }, + sequence: [{ + func: "{putCloudCred}.send", + args: fixture.putCloudCredBody + }, { + event: "{putCloudCred}.events.onComplete", + listener: "gpii.tests.preferencesServer.cloudSafeCred.testCloudSafeCreatePut", + args: [fixture.cloudCredSubset, "{arguments}.0"] + }, { + func: "{getFullSafe}.send" + }, { + event: "{getFullSafe}.events.onComplete", + listener: "gpii.tests.preferencesServer.cloudSafeCred.testCloudSafeCred", + args: ["{arguments}.0", fixture.getCloudCredFullSafeExpected, "{getFullSafe}.nativeResponse.statusCode", expectedStatusCode] + }, { + func: "{unlockCloudSafe}.send", + args: fixture.unlockCloudSafePost + }, { + event: "{unlockCloudSafe}.events.onComplete", + listener: "gpii.tests.preferencesServer.testResponse", + args: ["{arguments}.0", fixture.unlockCloudSafeExpected, "{unlockCloudSafe}.nativeResponse.statusCode", expectedStatusCode] + }] + }; +}; + +gpii.tests.preferencesServer.cloudSafeCred.put.successFixtures = [ + { + name: "PUT: Add a cloudsafe credential to an existing prefsSafe", + prefsSafeId: "prefsSafe-1", + url: "%prefsSafeId", + putCloudCredBody: { + username: "prefsTestUsername", + password: "testPassword" + }, + cloudCredSubset: { + type: "gpiiCloudSafeCredential", + prefsSafeId: "prefsSafe-1", + gpiiExpressUserId: "org.couch.db.user:prefsTestUsername" + }, + unlockCloudSafePost: { + username: "prefsTestUsername", + password: "testPassword" + }, + unlockCloudSafeExpected: { + "type": "prefsSafe", + "schemaVersion": "0.3", + "prefsSafeType": "user", + "name": null, + "email": null, + "preferences": { + "ISO24751": { + "name": "ISO24751 set", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "control": { + "onscreenKeyboard": true, + "mouseEmulation": { + "-provisional-initDelay": 120, + "cursorSpeed": 0.850, + "-provisional-mouseEmulation/enabled": true + } + }, + "unknown": true, + "applications": { + "org.alsa-project": { + "id": "org.alsa-project", + "parameters": { + "masterVolume": 14 + } + } + } + }, + "metadata": [ + { + "type": "provenance", + "scope": ["applications.org\\.alsa-project.parameters"], + "source": "snapshotter" + } + ] + } + }, + "metadata": [ + { + "type": "doNotShare", + "scope": [ "display.screenEnhancement.fontSize"] + } + ] + } + }, + "timestampCreated": "2017-12-14T19:55:11.640Z", + "timestampUpdated": null, + "id": "prefsSafe-1" + } + } +]; + +gpii.tests.preferencesServer.cloudSafeCred.put.buildFailureTestDef = function (fixture) { + return { + name: fixture.name, + config: gpii.tests.preferencesServer.config, + components: { + putCloudCred: { + type: "kettle.test.request.http", + options: { + path: "/add-cloud-credentials/" + fixture.url, + method: "PUT", + port: 8081, + termMap: { + prefsSafeId: fixture.prefsSafeId + } + } + } + }, + sequence: [{ + func: "{putCloudCred}.send", + args: fixture.putCloudCredBody + }, { + event: "{putCloudCred}.events.onComplete", + listener: "gpii.tests.preferencesServer.testResponse", + args: ["{arguments}.0", fixture.failedCreateExpected, "{putCloudCred}.nativeResponse.statusCode", fixture.expectedStatusCode] + }] + }; +}; + +gpii.tests.preferencesServer.cloudSafeCred.put.failureFixtures = [ + { + name: "Unsuccessful PUT: Cannot add a cloudSafe credential to a safe that doesn't exist", + expectedStatusCode: 404, + prefsSafeId: "prefsSafe-1000", + url: "%prefsSafeId", + putCloudCredBody: { + username: "prefsTestUsername", + password: "testPassword" + }, + failedCreateExpected: { + isError: true, + errorCode: "GPII_ERR_NO_PREFSSAFE", + message: "Missing prefsSafe" + } + }, + { + name: "Unsuccessful PUT: Cannot add a cloudSafe credential with an existing name", + expectedStatusCode: 404, + prefsSafeId: "prefsSafe-1", + url: "%prefsSafeId", + putCloudCredBody: { + username: "prefs7user", + password: "testPassword" + }, + failedCreateExpected: { + "isError": true, + "message": "Unable to create credentials with this username and password combination", + "errorCode": "GPII_ERR_CREATE_CRED_BAD_USERNAME_PASSWORD" + } + }, + { + name: "Unsuccessful PUT: Missing username", + expectedStatusCode: 404, + prefsSafeId: "prefsSafe-1", + url: "%prefsSafeId", + putCloudCredBody: { + password: "testPassword" + }, + failedCreateExpected: { + "isError": true, + "message": "Unable to create credentials with this username and password combination", + "errorCode": "GPII_ERR_CREATE_CRED_BAD_USERNAME_PASSWORD" + } + }, + { + name: "Unsuccessful PUT: Missing password", + expectedStatusCode: 404, + prefsSafeId: "prefsSafe-1", + url: "%prefsSafeId", + putCloudCredBody: { + username: "spectacularUsername" + }, + failedCreateExpected: { + "isError": true, + "message": "Unable to create credentials with this username and password combination", + "errorCode": "GPII_ERR_CREATE_CRED_BAD_USERNAME_PASSWORD" + } + }, + { + name: "Unsuccessful PUT: Empty password", + expectedStatusCode: 404, + prefsSafeId: "prefsSafe-1", + url: "%prefsSafeId", + putCloudCredBody: { + username: "spectacularUsername", + password: "" + }, + failedCreateExpected: { + "isError": true, + "message": "Unable to create credentials with this username and password combination", + "errorCode": "GPII_ERR_CREATE_CRED_BAD_USERNAME_PASSWORD" + } + }, + { + name: "Unsuccessful PUT: Empty username", + expectedStatusCode: 404, + prefsSafeId: "prefsSafe-1", + url: "%prefsSafeId", + putCloudCredBody: { + username: "", + password: "s3cret" + }, + failedCreateExpected: { + "isError": true, + "message": "Unable to create credentials with this username and password combination", + "errorCode": "GPII_ERR_CREATE_CRED_BAD_USERNAME_PASSWORD" + } + } +]; + +/////////////// TEST Unlocking a Prefs Safe using a Cloud Safe Login ///////////// +fluid.registerNamespace("gpii.tests.preferencesServer.cloudSafeCred.unlock"); + +gpii.tests.preferencesServer.cloudSafeCred.unlock.buildSuccessTestDef = function (fixture) { + var expectedStatusCode = fixture.expectedStatusCode || 200; + return { + name: fixture.name, + expect: 2, + config: gpii.tests.preferencesServer.config, + components: { + unlockCloudSafe: { + type: "kettle.test.request.http", + options: { + path: "/unlock-cloud-safe", + method: "POST", + port: 8081 + } + } + }, + sequence: [{ + func: "{unlockCloudSafe}.send", + args: fixture.unlockCloudSafePost + }, { + event: "{unlockCloudSafe}.events.onComplete", + listener: "gpii.tests.preferencesServer.testResponse", + args: ["{arguments}.0", fixture.unlockCloudSafeExpected, "{unlockCloudSafe}.nativeResponse.statusCode", expectedStatusCode] + }] + }; +}; + +gpii.tests.preferencesServer.cloudSafeCred.unlock.successFixtures = [ + { + name: "POST: Unlock Prefs Safe 7 using it's attached credentials", + unlockCloudSafePost: { + username: "prefs7user", + password: "testPassword" + }, + unlockCloudSafeExpected: { + "id": "prefsSafe-7", + "type": "prefsSafe", + "schemaVersion": "0.3", + "prefsSafeType": "user", + "name": null, + "email": null, + "preferences": { + "flat": { + "name": "bit of stuff", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/common/onScreenKeyboard/enabled": true + }, + "metadata": [] + } + }, + "metadata": [] + } + }, + "timestampCreated": "2017-12-14T19:55:11.641Z", + "timestampUpdated": null + } + }, + { + name: "POST: Fail to Unlock Prefs Safe 7 using bad credentials", + expectedStatusCode: 401, + unlockCloudSafePost: { + username: "prefs7user", + password: "notThePassword" + }, + unlockCloudSafeExpected: { + "isError": true, + "message": "Unable to unlock Preferences Safe with the supplied credentials.", + "errorCode": "GPII_ERR_UNLOCKING_PREFSSAFE_CREDENTIALS" + } + } +]; + +/////////////// Test Aggregation and Setup below /////////////// +gpii.tests.preferencesServer.cloudSafeCred.testMap = [ { + build: gpii.tests.preferencesServer.cloudSafeCred.put.buildSuccessTestDef, + fixtures: gpii.tests.preferencesServer.cloudSafeCred.put.successFixtures +}, { + build: gpii.tests.preferencesServer.cloudSafeCred.unlock.buildSuccessTestDef, + fixtures: gpii.tests.preferencesServer.cloudSafeCred.unlock.successFixtures +}, { + build: gpii.tests.preferencesServer.cloudSafeCred.put.buildFailureTestDef, + fixtures: gpii.tests.preferencesServer.cloudSafeCred.put.failureFixtures +}]; + +fluid.defaults("gpii.tests.preferencesServer.cloudSafeCred.testEnvironment", { + gradeNames: ["gpii.test.couchdb.environment.base", "gpii.test.serverEnvironment"], + databases: { + gpii: { + data: [ + "%gpii-universal/gpii/node_modules/preferencesServer/test/data/gpiiKeys.json", + "%gpii-universal/gpii/node_modules/preferencesServer/test/data/prefsSafes.json", + "%gpii-universal/gpii/node_modules/preferencesServer/test/data/gpiiCloudSafeCred.json", + "%gpii-universal/testData/dbData/views.json" + ] + } + } +}); + +gpii.tests.preferencesServer.cloudSafeCred.testDefs = fluid.flatten(fluid.transform(gpii.tests.preferencesServer.cloudSafeCred.testMap, function (mapEl) { + return fluid.transform(mapEl.fixtures, mapEl.build, function (fixture) { + var common = { + config: gpii.tests.preferencesServer.config + }; + + return fluid.extend({}, common, fixture); + }); +})); + +gpii.tests.preferencesServer.cloudSafeCred.testDefToEnvironment = function (testDef) { + return gpii.test.testDefToEnvironment(testDef, "gpii.tests.preferencesServer.cloudSafeCred.testEnvironment", "gpii.test.couchSequenceGrade"); +}; + +gpii.test.runTestDefs(gpii.tests.preferencesServer.cloudSafeCred.testDefs, gpii.tests.preferencesServer.cloudSafeCred.testDefToEnvironment); diff --git a/gpii/node_modules/preferencesServer/test/data/gpiiCloudSafeCred.json b/gpii/node_modules/preferencesServer/test/data/gpiiCloudSafeCred.json new file mode 100644 index 000000000..a69d680fa --- /dev/null +++ b/gpii/node_modules/preferencesServer/test/data/gpiiCloudSafeCred.json @@ -0,0 +1,23 @@ +[ + { + "name": "prefs7user", + "type": "user", + "email": "prefs7user@gpii.org", + "roles": [], + "username": "prefs7user", + "verified": true, + "iterations": 10, + "password_scheme": "pbkdf2", + "salt": "7cf6961e6ded3bd25732e5466512d116bf9908ba9629d4ed060a03a965e5341d", + "derived_key": "e8bd265e7d82fd0f662e9ddaaf2e75acb294da1b", + "verification_code": "618fa72aa62af282704b556e34957a79", + "_id": "org.couch.db.user:prefs7user" + }, + { + "type": "gpiiCloudSafeCredential", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-7", + "gpiiExpressUserId": "org.couch.db.user:prefs7user", + "_id": "8f3085a7-b65b-4648-9a78-8ac7de766997" + } +] diff --git a/gpii/node_modules/preferencesServer/test/data/gpiiKeys-preferenceServiceTests.json b/gpii/node_modules/preferencesServer/test/data/gpiiKeys-preferenceServiceTests.json index 0160df644..33bbb46e6 100644 --- a/gpii/node_modules/preferencesServer/test/data/gpiiKeys-preferenceServiceTests.json +++ b/gpii/node_modules/preferencesServer/test/data/gpiiKeys-preferenceServiceTests.json @@ -2,7 +2,7 @@ { "_id": "alice_gpii_key", "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": "prefsSafe-1", "prefsSetId": "gpii-default", "revoked": false, @@ -13,7 +13,7 @@ { "_id": "bob_gpii_key", "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": null, "prefsSetId": null, "revoked": false, @@ -24,7 +24,7 @@ { "_id": "snapset1", "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": "prefsSafe-snapset1", "prefsSetId": "gpii-default", "revoked": false, diff --git a/gpii/node_modules/preferencesServer/test/data/gpiiKeys.json b/gpii/node_modules/preferencesServer/test/data/gpiiKeys.json index 99182ad45..66efcc87f 100644 --- a/gpii/node_modules/preferencesServer/test/data/gpiiKeys.json +++ b/gpii/node_modules/preferencesServer/test/data/gpiiKeys.json @@ -2,7 +2,7 @@ { "_id": "alice_gpii_key", "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": "prefsSafe-1", "prefsSetId": "gpii-default", "revoked": false, @@ -13,7 +13,7 @@ { "_id": "bob_gpii_key", "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": null, "prefsSetId": null, "revoked": false, @@ -24,7 +24,7 @@ { "_id": "np_ISO24751Only_singleContext", "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": "prefsSafe-1", "prefsSetId": "gpii-default", "revoked": false, @@ -35,7 +35,7 @@ { "_id": "np_ISO24751Only_singleContext_wildcardMetadata", "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": "prefsSafe-2", "prefsSetId": "gpii-default", "revoked": false, @@ -46,7 +46,7 @@ { "_id": "np_flatOnly_multiContext", "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": "prefsSafe-3", "prefsSetId": "gpii-default", "revoked": false, @@ -57,7 +57,7 @@ { "_id": "np_flatOnly_singleContext", "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": "prefsSafe-4", "prefsSetId": "gpii-default", "revoked": false, @@ -68,7 +68,7 @@ { "_id": "np_mixed_multiContext", "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": "prefsSafe-5", "prefsSetId": "gpii-default", "revoked": false, @@ -79,7 +79,7 @@ { "_id": "np_mixed_singleContext", "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": "prefsSafe-6", "prefsSetId": "gpii-default", "revoked": false, @@ -90,7 +90,7 @@ { "_id": "np_tiny", "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": "prefsSafe-7", "prefsSetId": "gpii-default", "revoked": false, @@ -101,7 +101,7 @@ { "_id": "undefined_preferences", "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": null, "prefsSetId": null, "revoked": false, diff --git a/gpii/node_modules/preferencesServer/test/data/preferencesServiceUsers.json b/gpii/node_modules/preferencesServer/test/data/preferencesServiceUsers.json new file mode 100644 index 000000000..f522af6db --- /dev/null +++ b/gpii/node_modules/preferencesServer/test/data/preferencesServiceUsers.json @@ -0,0 +1,103 @@ +[ + { + "_id": "alice_gpii_key", + "type": "gpiiKey", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-1", + "prefsSetId": "gpii-default", + "revoked": false, + "revokedReason": null, + "timestampCreated": "2017-11-21T18:11:22.101Z", + "timestampUpdated": null + }, { + "_id": "bob_gpii_key", + "type": "gpiiKey", + "schemaVersion": "0.3", + "prefsSafeId": null, + "prefsSetId": null, + "revoked": false, + "revokedReason": null, + "timestampCreated": "2017-11-21T18:11:22.101Z", + "timestampUpdated": null + }, { + "_id": "snapset1", + "type": "gpiiKey", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-snapset1", + "prefsSetId": "gpii-default", + "revoked": false, + "revokedReason": null, + "timestampCreated": "2017-11-21T18:11:22.101Z", + "timestampUpdated": null + }, { + "_id": "prefsSafe-1", + "type": "prefsSafe", + "schemaVersion": "0.3", + "prefsSafeType": "user", + "name": null, + "email": null, + "preferences": { + "flat": { + "name": "Default context", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/common/matchMakerType": "ruleBased", + "http://registry.gpii.net/common/fontSize": 24, + "http://registry.gpii.net/common/foregroundColor": "white" + } + } + } + } + }, + "timestampCreated": "2017-12-01T18:43:32.889Z", + "timestampUpdated": null + }, { + "_id": "prefsSafe-snapset1", + "type": "prefsSafe", + "schemaVersion": "0.3", + "prefsSafeType": "snapset", + "name": null, + "email": null, + "preferences": { + "flat": { + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/common/foregroundColor": "white" + } + } + } + } + }, + "timestampCreated": "2017-12-01T18:43:32.889Z", + "timestampUpdated": null + }, + { + "_id": "prefsSafe-7", + "type": "prefsSafe", + "schemaVersion": "0.3", + "prefsSafeType": "user", + "name": null, + "email": null, + "preferences": { + "flat": { + "name": "bit of stuff", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/common/onScreenKeyboard/enabled": true + }, + "metadata": [] + } + }, + "metadata": [] + } + }, + "timestampCreated": "2017-12-14T19:55:11.641Z", + "timestampUpdated": null + } +] diff --git a/gpii/node_modules/preferencesServer/test/data/prefsSafes-preferencesServiceTests.json b/gpii/node_modules/preferencesServer/test/data/prefsSafes-preferencesServiceTests.json index 6ac1fe892..111b13616 100644 --- a/gpii/node_modules/preferencesServer/test/data/prefsSafes-preferencesServiceTests.json +++ b/gpii/node_modules/preferencesServer/test/data/prefsSafes-preferencesServiceTests.json @@ -2,10 +2,9 @@ { "_id": "prefsSafe-1", "type": "prefsSafe", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeType": "user", "name": null, - "password": null, "email": null, "preferences": { "flat": { @@ -28,10 +27,9 @@ { "_id": "prefsSafe-snapset1", "type": "prefsSafe", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeType": "snapset", "name": null, - "password": null, "email": null, "preferences": { "flat": { diff --git a/gpii/node_modules/preferencesServer/test/data/prefsSafes.json b/gpii/node_modules/preferencesServer/test/data/prefsSafes.json index 04f7efcfa..58e315406 100644 --- a/gpii/node_modules/preferencesServer/test/data/prefsSafes.json +++ b/gpii/node_modules/preferencesServer/test/data/prefsSafes.json @@ -2,10 +2,9 @@ { "_id": "prefsSafe-1", "type": "prefsSafe", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeType": "user", "name": null, - "password": null, "email": null, "preferences": { "ISO24751": { @@ -55,10 +54,9 @@ { "_id": "prefsSafe-2", "type": "prefsSafe", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeType": "user", "name": null, - "password": null, "email": null, "preferences": { "ISO24751": { @@ -108,10 +106,9 @@ { "_id": "prefsSafe-3", "type": "prefsSafe", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeType": "user", "name": null, - "password": null, "email": null, "preferences": { "flat": { @@ -157,10 +154,9 @@ { "_id": "prefsSafe-4", "type": "prefsSafe", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeType": "user", "name": null, - "password": null, "email": null, "preferences": { "flat": { @@ -204,10 +200,9 @@ { "_id": "prefsSafe-5", "type": "prefsSafe", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeType": "user", "name": null, - "password": null, "email": null, "preferences": { "flat": { @@ -291,10 +286,9 @@ { "_id": "prefsSafe-6", "type": "prefsSafe", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeType": "user", "name": null, - "password": null, "email": null, "preferences": { "flat": { @@ -354,10 +348,9 @@ { "_id": "prefsSafe-7", "type": "prefsSafe", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeType": "user", "name": null, - "password": null, "email": null, "preferences": { "flat": { @@ -380,10 +373,9 @@ { "_id": "prefsSafe-snapset1", "type": "prefsSafe", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeType": "snapset", "name": null, - "password": null, "email": null, "preferences": { "flat": { diff --git a/gpii/node_modules/preferencesServer/test/preferencesServerTests.js b/gpii/node_modules/preferencesServer/test/preferencesServerTests.js index 18fbc3307..299137431 100644 --- a/gpii/node_modules/preferencesServer/test/preferencesServerTests.js +++ b/gpii/node_modules/preferencesServer/test/preferencesServerTests.js @@ -37,6 +37,12 @@ gpii.tests.preferencesServer.testResponse = function (response, expected, receiv jqUnit.assertLeftHand("Response is correct", expected, retrievedData); }; +gpii.tests.preferencesServer.testResponseLeftHand = function (response, expected, receivedStatusCode, expectedStatusCode) { + var retrievedData = JSON.parse(response); + jqUnit.assertEquals("Response status code is expected", expectedStatusCode, receivedStatusCode); + jqUnit.assertLeftHand("Response is correct", expected, retrievedData); +}; + // TESTING preferencesServer GET functionality fluid.registerNamespace("gpii.tests.preferencesServer.get"); @@ -687,7 +693,7 @@ gpii.tests.preferencesServer.put.wrapPrefs = function (fixture) { gpii.tests.preferencesServer.put.testSuccessResponse = function (response, prefs, gpiiKey) { var retrievedData = JSON.parse(response); - // expect preferences without ontology info - any generated user token is fine + // expect preferences without ontology info - any generated GPII key is fine var expected = { preferences: prefs, gpiiKey: gpiiKey diff --git a/gpii/node_modules/preferencesServer/test/preferencesServiceTests.js b/gpii/node_modules/preferencesServer/test/preferencesServiceTests.js index 6c1a00a6f..2e45d876c 100644 --- a/gpii/node_modules/preferencesServer/test/preferencesServiceTests.js +++ b/gpii/node_modules/preferencesServer/test/preferencesServiceTests.js @@ -110,6 +110,8 @@ fluid.defaults("gpii.tests.preferencesServer.preferencesService.baseEnvironment" data: [ "%gpii-universal/gpii/node_modules/preferencesServer/test/data/gpiiKeys-preferenceServiceTests.json", "%gpii-universal/gpii/node_modules/preferencesServer/test/data/prefsSafes-preferencesServiceTests.json", + "%gpii-universal/gpii/node_modules/preferencesServer/test/data/gpiiCloudSafeCred.json", + "%gpii-universal/gpii/node_modules/preferencesServer/test/data/prefsSafes.json", "%gpii-universal/testData/dbData/views.json" ] } @@ -201,7 +203,6 @@ gpii.tests.preferencesServer.preferencesService.expected = { preferencesToCreate_prefsOnly: { prefsSafeType: "user", name: null, - password: null, email: null, preferences: { "http://registry.gpii.net/common/cursorSize": 24 @@ -429,8 +430,260 @@ gpii.tests.preferencesServer.preferencesService.verifyNewPrefsSafeForUpdate = fu jqUnit.assertDeepEq("Other GPII key values are unchanged", unchangedOrigGpiiKeyFields, unchangedGpiiKeyFields); }; +fluid.defaults("gpii.tests.preferencesServer.preferencesService.addGpiiKey", { + gradeNames: ["gpii.tests.preferencesServer.preferencesService.baseEnvironment"], + components: { + caseHolder: { + options: { + modules: [{ + name: "Test addGpiiKey()", + tests: [ + { + name: "addGpiiKey: Successful create", + sequenceGrade: "gpii.tests.preferencesServer.preferencesService.sequenceGrade", + sequence: [{ + task: "{preferencesService}.addGpiiKey", + args: [{ + "type": "gpiiKey", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-1", + "prefsSetId": "gpii-default", + "revoked": false, + "revokedReason": null + }], + resolve: "jqUnit.assertLeftHand", + resolveArgs: ["Newly created gpiiKey", { + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-1", + "prefsSetId": "gpii-default", + "revoked": false, + "revokedReason": null, + "timestampUpdated": null, + "type": "gpiiKey" + }, "{arguments}.0"] + }] + }, + { + name: "addGpiiKey: Error: Prefs Safe does not exist", + sequenceGrade: "gpii.tests.preferencesServer.preferencesService.sequenceGrade", + sequence: [{ + task: "{preferencesService}.addGpiiKey", + args: [{ + "type": "gpiiKey", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-2020", + "prefsSetId": "gpii-default", + "revoked": false, + "revokedReason": null + }], + reject: "jqUnit.assertLeftHand", + rejectArgs: ["Preference safe doesn't exist, can't create key", { + "isError": true, + "message": "Missing prefsSafe", + "errorCode": "GPII_ERR_NO_PREFSSAFE" + }, "{arguments}.0"] + }] + } + ] + }] + } + } + } +}); + +// Tests for cloudSafeCredCreate +fluid.defaults("gpii.tests.preferencesServer.preferencesService.cloudSafeCredCreate", { + gradeNames: ["gpii.tests.preferencesServer.preferencesService.baseEnvironment"], + components: { + caseHolder: { + options: { + modules: [{ + name: "Test cloudSafeCredCreate()", + tests: [ + { + name: "cloudSafeCredCreate: Successful create", + sequenceGrade: "gpii.tests.preferencesServer.preferencesService.sequenceGrade", + sequence: [{ + task: "{preferencesService}.cloudSafeCredCreate", + args: [{prefsSafeId: "prefsSafe-1", username: "myUserName", password: "supersecret"}], + resolve: "jqUnit.assertLeftHand", + resolveArgs: ["Newly created credentials document", { + prefsSafeId: "prefsSafe-1", + gpiiExpressUserId: "org.couch.db.user:myUserName", + type: "gpiiCloudSafeCredential" + }, "{arguments}.0"] + }, { + task: "{preferencesService}.cloudSafeUnlock", + args: [{username: "myUserName", password: "supersecret"}], + resolve: "jqUnit.assertDeepEq", + resolveArgs: ["Should have returned prefsSafe-1: ", "prefsSafe-1", "{arguments}.0.id"] + }] + }, + { + name: "cloudSafeCredCreate: Error: Prefs Safe doesn't exist", + sequenceGrade: "gpii.tests.preferencesServer.preferencesService.sequenceGrade", + sequence: [{ + task: "{preferencesService}.cloudSafeCredCreate", + args: [{prefsSafeId: "prefsSafe-1999", username: "myUserName", password: "supersecret"}], + reject: "jqUnit.assertLeftHand", + rejectArgs: ["Error message saying the prefs safe does not exist.", { + "isError": true, + "message": "Missing prefsSafe", + "errorCode": "GPII_ERR_NO_PREFSSAFE" + }, "{arguments}.0"] + }] + }, + { + name: "cloudSafeUnlock: Error: Name already used", + sequence: [{ + task: "{preferencesService}.cloudSafeCredCreate", + args: [{prefsSafeId: "prefsSafe-1", username: "prefs7user", password: "supersecret"}], + reject: "jqUnit.assertLeftHand", + rejectArgs: ["Error indicating bad username and password for new credentials.", { + "isError": true, + "errorCode": "GPII_ERR_CREATE_CRED_BAD_USERNAME_PASSWORD", + "message": "Unable to create credentials with this username and password combination" + }, "{arguments}.0"] + }] + } + ] + }] + } + } + } +}); + +//Tests for cloudSafeUnlock +fluid.defaults("gpii.tests.preferencesServer.preferencesService.cloudSafeUnlock", { + gradeNames: ["gpii.tests.preferencesServer.preferencesService.baseEnvironment"], + components: { + caseHolder: { + options: { + modules: [{ + name: "Test cloudSafeUnlock()", + tests: [ + { + name: "cloudSafeUnlock: Unlock a safe successfully", + sequenceGrade: "gpii.tests.preferencesServer.preferencesService.sequenceGrade", + sequence: [{ + task: "{preferencesService}.cloudSafeUnlock", + args: [{username: "prefs7user", password: "testPassword"}], + resolve: "jqUnit.assertDeepEq", + resolveArgs: ["Should have returned prefsSafe-7: ", "prefsSafe-7", "{arguments}.0.id"] + }] + }, + { + name: "cloudSafeUnlock: Fail to unlock with a bad username/password", + sequenceGrade: "gpii.tests.preferencesServer.preferencesService.sequenceGrade", + sequence: [{ + task: "{preferencesService}.cloudSafeUnlock", + args: [{username: "prefs7user", password: "wrongPassword"}], + reject: "jqUnit.assertDeepEq", + rejectArgs: ["Should fail with message", "Unable to unlock Preferences Safe with the supplied credentials.", "{arguments}.0.message"] + }] + } + ] + }] + } + } + } +}); + +//Tests for creating and updating prefs safes +gpii.tests.preferencesServer.preferencesService.createUpdatePrefsSafeData = { + newPrefsSafe: { + "type": "prefsSafe", + "schemaVersion": "0.3", + "prefsSafeType": "user", + "name": "User 200", + "preferences": { + "flat": { + "name": "200 bits of stuff", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/common/onScreenKeyboard/enabled": true + }, + "metadata": [] + } + }, + "metadata": [] + } + } + }, + updatedPrefsSafe7: { + "id": "prefsSafe-7", + "type": "prefsSafe", + "schemaVersion": "0.3", + "prefsSafeType": "user", + "name": "Prefs Seven", + "email": null, + "preferences": { + "flat": { + "name": "bit of stuff", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/common/onScreenKeyboard/enabled": true + }, + "metadata": [] + } + }, + "metadata": [] + } + }, + "timestampCreated": "2017-12-14T19:55:11.641Z" + // This value will be different "timestampUpdated": null + } +}; + +fluid.defaults("gpii.tests.preferencesServer.preferencesService.createUpdatesPrefs", { + gradeNames: ["gpii.tests.preferencesServer.preferencesService.baseEnvironment"], + components: { + caseHolder: { + options: { + modules: [{ + name: "Test cloudSafeUnlock()", + tests: [ + { + name: "createSavePrefsSafe: Add a new safe.", + sequenceGrade: "gpii.tests.preferencesServer.preferencesService.sequenceGrade", + sequence: [{ + task: "{preferencesService}.addPrefsSafe", + args: [gpii.tests.preferencesServer.preferencesService.createUpdatePrefsSafeData.newPrefsSafe], + resolve: "jqUnit.assertLeftHand", + resolveArgs: ["Should have same structure as created: ", + gpii.tests.preferencesServer.preferencesService.createUpdatePrefsSafeData.newPrefsSafe, + "{arguments}.0"] + }] + }, + { + name: "updateExistingSafe: Change the name of prefsSafe 7", + sequenceGrade: "gpii.tests.preferencesServer.preferencesService.sequenceGrade", + sequence: [{ + task: "{preferencesService}.updatePrefsSafe", + args: [gpii.tests.preferencesServer.preferencesService.createUpdatePrefsSafeData.updatedPrefsSafe7], + resolve: "jqUnit.assertLeftHand", + resolveArgs: ["Should have same structure as created: ", + gpii.tests.preferencesServer.preferencesService.createUpdatePrefsSafeData.updatedPrefsSafe7, + "{arguments}.0"] + }] + } + ] + }] + } + } + } +}); + fluid.test.runTests([ "gpii.tests.preferencesServer.preferencesService.getPreferencesByGpiiKey", "gpii.tests.preferencesServer.preferencesService.createPreferences", - "gpii.tests.preferencesServer.preferencesService.updatePreferences" + "gpii.tests.preferencesServer.preferencesService.updatePreferences", + "gpii.tests.preferencesServer.preferencesService.addGpiiKey", + "gpii.tests.preferencesServer.preferencesService.cloudSafeCredCreate", + "gpii.tests.preferencesServer.preferencesService.cloudSafeUnlock", + "gpii.tests.preferencesServer.preferencesService.createUpdatesPrefs" ]); diff --git a/gpii/node_modules/preferencesServer/test/prefsSafesTests.js b/gpii/node_modules/preferencesServer/test/prefsSafesTests.js new file mode 100644 index 000000000..21db0ea22 --- /dev/null +++ b/gpii/node_modules/preferencesServer/test/prefsSafesTests.js @@ -0,0 +1,797 @@ +/** +GPII Preferences Server Tests + +Copyright 2018 Raising the Floor - International + +Licensed under the New BSD license. You may not use this file except in +compliance with this License. + +The research leading to these results has received funding from the European Union's +Seventh Framework Programme (FP7/2007-2013) under grant agreement no. 289016. + +You may obtain a copy of the License at +https://github.com/GPII/universal/blob/master/LICENSE.txt +*/ + +"use strict"; + +var fluid = require("infusion"), + gpii = fluid.registerNamespace("gpii"); + +require("./preferencesServerTestsUtils.js"); +require("./preferencesServerTests.js"); + +fluid.require("%gpii-universal"); +gpii.loadTestingSupport(); + +fluid.registerNamespace("gpii.tests.preferencesServer.prefsSafes"); + +gpii.tests.preferencesServer.config = { + configName: "gpii.tests.preferencesServer.config", + configPath: "%preferencesServer/test/configs" +}; + +/////////////////////// TESTING prefsSafes GET /prefsafe/:prefsSafeId //////////////////////////// +fluid.registerNamespace("gpii.tests.preferencesServer.prefsSafes.get"); + +gpii.tests.preferencesServer.prefsSafes.get.buildTestDef = function (fixture) { + var expectedStatusCode = fixture.expectedStatusCode || 200; + return { + name: fixture.name, + expect: 2, + components: { + getRequest: { + type: "kettle.test.request.http", + options: { + path: "/prefsSafe/" + fixture.url, + method: "GET", + port: 8081, + termMap: { + prefsSafeId: fixture.prefsSafeId + } + } + } + }, + sequence: [{ + func: "{getRequest}.send" + }, { + event: "{getRequest}.events.onComplete", + listener: "gpii.tests.preferencesServer.testResponse", + args: ["{arguments}.0", fixture.expected, "{getRequest}.nativeResponse.statusCode", expectedStatusCode] + }] + }; +}; + +gpii.tests.preferencesServer.prefsSafes.get.successFixtures = [ + { + name: "GET: Basic retrieval of a prefs safe", + prefsSafeId: "prefsSafe-7", + url: "%prefsSafeId", + expected: { + "id": "prefsSafe-7", + "type": "prefsSafe", + "schemaVersion": "0.3", + "prefsSafeType": "user", + "name": null, + "email": null, + "preferences": { + "flat": { + "name": "bit of stuff", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/common/onScreenKeyboard/enabled": true + }, + "metadata": [] + } + }, + "metadata": [] + } + }, + "timestampCreated": "2017-12-14T19:55:11.641Z", + "timestampUpdated": null + } + } +]; + +gpii.tests.preferencesServer.prefsSafes.get.failFixtures = [ + { + name: "GET error: Fetching a safe that does not exist returns an error payload.", + prefsSafeId: "prefsSafe-2999", + url: "%prefsSafeId", + expectedStatusCode: 404, + expected: { + "isError": true, + "errorCode": "GPII_ERR_NO_PREFSSAFE", + "message": "Missing prefsSafe" + } + } +]; + +/////////////////////// TESTING prefsSafes with keys GET //////////////////////////// +fluid.registerNamespace("gpii.tests.preferencesServer.prefsSafes.getSafeWithKeys"); + +gpii.tests.preferencesServer.prefsSafes.getSafeWithKeys.buildTestDef = function (fixture) { + var expectedStatusCode = fixture.expectedStatusCode || 200; + return { + name: fixture.name, + expect: 2, + components: { + getRequest: { + type: "kettle.test.request.http", + options: { + path: "/prefsSafe-with-keys/" + fixture.url, + method: "GET", + port: 8081, + termMap: { + prefsSafeId: fixture.prefsSafeId + } + } + } + }, + sequence: [{ + func: "{getRequest}.send" + }, { + event: "{getRequest}.events.onComplete", + listener: "gpii.tests.preferencesServer.testResponse", + args: ["{arguments}.0", fixture.expected, "{getRequest}.nativeResponse.statusCode", expectedStatusCode] + }] + }; +}; + +gpii.tests.preferencesServer.prefsSafes.getSafeWithKeys.successFixtures = [ + { + name: "GET: Retieve a payload containing the prefs-safes and all it's associated key/credential records", + prefsSafeId: "prefsSafe-7", + url: "%prefsSafeId", + expected: { + prefsSafe: { + "id": "prefsSafe-7", + "type": "prefsSafe", + "schemaVersion": "0.3", + "prefsSafeType": "user", + "name": null, + "email": null, + "preferences": { + "flat": { + "name": "bit of stuff", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/common/onScreenKeyboard/enabled": true + }, + "metadata": [] + } + }, + "metadata": [] + } + }, + "timestampCreated": "2017-12-14T19:55:11.641Z", + "timestampUpdated": null + }, + keys: [ + { + "type": "gpiiCloudSafeCredential", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-7", + "gpiiExpressUserId": "org.couch.db.user:prefs7user", + "id": "8f3085a7-b65b-4648-9a78-8ac7de766997" + }, + { + "id": "np_tiny", + "type": "gpiiKey", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-7", + "prefsSetId": "gpii-default", + "revoked": false, + "revokedReason": null, + "timestampCreated": "2017-12-14T19:55:11.641Z", + "timestampUpdated": null + } + ] + } + } +]; + +gpii.tests.preferencesServer.prefsSafes.getSafeWithKeys.failFixtures = [ + { + name: "GET: Failure message for a prefs safe that does not exist.", + prefsSafeId: "prefsSafe-3999", + url: "%prefsSafeId", + expectedStatusCode: 404, + expected: { + "isError": true, + "errorCode": "GPII_ERR_NO_PREFSSAFE", + "message": "Missing prefsSafe" + } + } +]; + +/////////////////////// TESTING prefsSafes PUT (Save) //////////////////////////// +fluid.registerNamespace("gpii.tests.preferencesServer.prefsSafes.putSafe"); + +gpii.tests.preferencesServer.prefsSafes.putSafe.buildTestDef = function (fixture) { + var expectedStatusCode = fixture.expectedStatusCode || 200; + return { + name: fixture.name, + expect: 2, + components: { + getRequest: { + type: "kettle.test.request.http", + options: { + path: "/prefsSafe/" + fixture.url, + method: "PUT", + port: 8081, + termMap: { + prefsSafeId: fixture.prefsSafeId + } + } + } + }, + sequence: [{ + func: "{getRequest}.send", + args: fixture.putBody + }, { + event: "{getRequest}.events.onComplete", + listener: "gpii.tests.preferencesServer.testResponseLeftHand", + args: ["{arguments}.0", fixture.expected, "{getRequest}.nativeResponse.statusCode", expectedStatusCode] + }] + }; +}; + +gpii.tests.preferencesServer.prefsSafes.putSafe.successFixtures = [ + { + name: "PUT: Basic update of an existing safe.", + prefsSafeId: "prefsSafe-7", + url: "%prefsSafeId", + putBody: { + "id": "prefsSafe-7", + "type": "prefsSafe", + "schemaVersion": "0.3", + "prefsSafeType": "user", + "name": "Prefs Seven", + "email": null, + "preferences": { + "flat": { + "name": "bit of stuff", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/common/onScreenKeyboard/enabled": false + }, + "metadata": [] + } + }, + "metadata": [] + } + }, + "timestampCreated": "2017-12-14T19:55:11.641Z", + "timestampUpdated": null + }, + expected: { + "id": "prefsSafe-7", + "type": "prefsSafe", + "schemaVersion": "0.3", + "prefsSafeType": "user", + "name": "Prefs Seven", + "email": null, + "preferences": { + "flat": { + "name": "bit of stuff", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/common/onScreenKeyboard/enabled": false + }, + "metadata": [] + } + }, + "metadata": [] + } + }, + "timestampCreated": "2017-12-14T19:55:11.641Z" + // This will be updated "timestampUpdated": null + } + } +]; + +gpii.tests.preferencesServer.prefsSafes.putSafe.failFixtures = [ + { + name: "PUT: Failure to put a safe that doesn't exist yet.", + expectedStatusCode: 404, + prefsSafeId: "prefsSafe-4000", + url: "%prefsSafeId", + putBody: { + "id": "prefsSafe-4000", + "type": "prefsSafe", + "schemaVersion": "0.3", + "prefsSafeType": "user", + "name": "Prefs Seven", + "email": null, + "preferences": { + "flat": { + "name": "bit of stuff", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/common/onScreenKeyboard/enabled": false + }, + "metadata": [] + } + }, + "metadata": [] + } + }, + "timestampCreated": "2017-12-14T19:55:11.641Z", + "timestampUpdated": null + }, + expected: { + isError: true, + errorCode: "GPII_ERR_NO_PREFSSAFE", + message: "Missing prefsSafe" + } + }, + { + name: "PUT: Failure to put a safe whose URL ID does not match the payload ID", + expectedStatusCode: 404, + prefsSafeId: "prefsSafe-1", + url: "%prefsSafeId", + putBody: { + "id": "prefsSafe-7", + "type": "prefsSafe", + "schemaVersion": "0.3", + "prefsSafeType": "user", + "name": "Prefs Seven", + "email": null, + "preferences": { + "flat": { + "name": "bit of stuff", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/common/onScreenKeyboard/enabled": false + }, + "metadata": [] + } + }, + "metadata": [] + } + }, + "timestampCreated": "2017-12-14T19:55:11.641Z", + "timestampUpdated": null + }, + expected: { + isError: true, + errorCode: "GPII_ERR_URL_PAYLOAD_ID_MISMATCH", + message: "The ID parameter in the URL does not match the ID in the payload." + } + } +]; + +/////////////////////// TESTING prefsSafes Create (POST) //////////////////////////// +fluid.registerNamespace("gpii.tests.preferencesServer.prefsSafes.postSafe"); + +gpii.tests.preferencesServer.prefsSafes.postSafe.buildTestDef = function (fixture) { + var expectedStatusCode = fixture.expectedStatusCode || 200; + return { + name: fixture.name, + expect: 2, + components: { + getRequest: { + type: "kettle.test.request.http", + options: { + path: "/prefsSafe", + method: "POST", + port: 8081 + } + } + }, + sequence: [{ + func: "{getRequest}.send", + args: fixture.postBody + }, { + event: "{getRequest}.events.onComplete", + listener: "gpii.tests.preferencesServer.testResponseLeftHand", + args: ["{arguments}.0", fixture.expected, "{getRequest}.nativeResponse.statusCode", expectedStatusCode] + }] + }; +}; + +gpii.tests.preferencesServer.prefsSafes.postSafe.successFixtures = [ + { + name: "POST: Create a new preferences safe.", + url: "%prefsSafeId", + postBody: { + "type": "prefsSafe", + "schemaVersion": "0.3", + "prefsSafeType": "user", + "name": "Prefs Three Thousand", + "email": null, + "preferences": { + "flat": { + "name": "bit of stuff", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/common/onScreenKeyboard/enabled": false + }, + "metadata": [] + } + }, + "metadata": [] + } + } + }, + expected: { + "type": "prefsSafe", + "schemaVersion": "0.3", + "prefsSafeType": "user", + "name": "Prefs Three Thousand", + "email": null, + "preferences": { + "flat": { + "name": "bit of stuff", + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/common/onScreenKeyboard/enabled": false + }, + "metadata": [] + } + }, + "metadata": [] + } + } + } + } +]; + +/////////////////////// TESTING prefsSafes List (GET) //////////////////////////// +fluid.registerNamespace("gpii.tests.preferencesServer.prefsSafes.list"); + +gpii.tests.preferencesServer.prefsSafes.list.buildTestDef = function (fixture) { + var expectedStatusCode = fixture.expectedStatusCode || 200; + return { + name: fixture.name, + expect: 2, + components: { + getRequest: { + type: "kettle.test.request.http", + options: { + path: "/prefsSafes", + method: "GET", + port: 8081 + } + } + }, + sequence: [{ + func: "{getRequest}.send" + }, { + event: "{getRequest}.events.onComplete", + listener: "gpii.tests.preferencesServer.testResponse", + args: ["{arguments}.0", fixture.expected, "{getRequest}.nativeResponse.statusCode", expectedStatusCode] + }] + }; +}; + +gpii.tests.preferencesServer.prefsSafes.list.successFixtures = [ + { + name: "GET: List Preference Safes", + expected: { + total_rows: 8, + offset: 0, + rows: [ + { + "name": null, + "email": null, + "created": "2017-12-14T19:55:11.640Z", + "updated": null, + "id": "prefsSafe-1" + }, + { + "name": null, + "email": null, + "created": "2017-12-14T19:55:11.640Z", + "updated": null, + "id": "prefsSafe-2" + }, + { + "name": null, + "email": null, + "created": "2017-12-14T19:55:11.641Z", + "updated": null, + "id": "prefsSafe-3" + }, + { + "name": null, + "email": null, + "created": "2017-12-14T19:55:11.641Z", + "updated": null, + "id": "prefsSafe-4" + }, + { + "name": null, + "email": null, + "created": "2017-12-14T19:55:11.641Z", + "updated": null, + "id": "prefsSafe-5" + }, + { + "name": null, + "email": null, + "created": "2017-12-14T19:55:11.641Z", + "updated": null, + "id": "prefsSafe-6" + }, + { + "name": null, + "email": null, + "created": "2017-12-14T19:55:11.641Z", + "updated": null, + "id": "prefsSafe-7" + }, + { + "name": null, + "email": null, + "created": "2017-12-01T18:43:32.889Z", + "updated": null, + "id": "prefsSafe-snapset1" + } + ] + } + } +]; + +// There aren't really any failure scenerios yet for the bare bones prefsSafe list, +// but we can test the case where there are zero safes. +gpii.tests.preferencesServer.prefsSafes.list.emptyDatabase = [ + { + config: { + configName: "gpii.tests.preferencesServer.config", + configPath: "%preferencesServer/test/configs" + }, + pouchTestCaseHolder: "gpii.tests.preferencesServer.preferencesService.emptySet.pouchTestCaseHolder", + name: "List the empty set of preference safes", + components: { + getRequest: { + type: "kettle.test.request.http", + options: { + path: "/prefsSafes", + method: "GET", + port: 8081 + } + } + }, + sequence: [{ + func: "{getRequest}.send" + }, { + event: "{getRequest}.events.onComplete", + listener: "gpii.tests.preferencesServer.testResponse", + args: ["{arguments}.0", { + "total_rows": 0, + "offset": 0, + "rows": [] + }, "{getRequest}.nativeResponse.statusCode", 200] + }] + } +]; + +/////////////////////// TESTING prefsSafes List Keys for Safe (GET) //////////////////////////// +fluid.registerNamespace("gpii.tests.preferencesServer.prefsSafes.listKeysForSafe"); + +gpii.tests.preferencesServer.prefsSafes.listKeysForSafe.buildTestDef = function (fixture) { + var expectedStatusCode = fixture.expectedStatusCode || 200; + return { + name: fixture.name, + expect: 2, + components: { + getRequest: { + type: "kettle.test.request.http", + options: { + path: "/prefsSafe-keys/" + fixture.url, + method: "GET", + port: 8081, + termMap: { + prefsSafeId: fixture.prefsSafeId + } + } + } + }, + sequence: [{ + func: "{getRequest}.send" + }, { + event: "{getRequest}.events.onComplete", + listener: "gpii.tests.preferencesServer.testResponse", + args: ["{arguments}.0", fixture.expected, "{getRequest}.nativeResponse.statusCode", expectedStatusCode] + }] + }; +}; + +gpii.tests.preferencesServer.prefsSafes.listKeysForSafe.successFixtures = [ + { + name: "GET: Basic listing of keys for a prefsSafe", + prefsSafeId: "prefsSafe-7", + url: "%prefsSafeId", + expected: { + // TODO sgithens, this CouchDB query needs to be fixed so that the total_rows + // is the actual number returned from the query, in this case 2 + "total_rows": 11, + "offset": 9, + rows: [{ + "type": "gpiiCloudSafeCredential", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-7", + "gpiiExpressUserId": "org.couch.db.user:prefs7user", + "id": "8f3085a7-b65b-4648-9a78-8ac7de766997" + }, + { + "id": "np_tiny", + "type": "gpiiKey", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-7", + "prefsSetId": "gpii-default", + "revoked": false, + "revokedReason": null, + "timestampCreated": "2017-12-14T19:55:11.641Z", + "timestampUpdated": null + }] + } + } +]; + +gpii.tests.preferencesServer.prefsSafes.listKeysForSafe.failFixtures = [ + { + name: "GET: Cannot list keys for a safe that doesn't exist.", + prefsSafeId: "prefsSafe-5999", + url: "%prefsSafeId", + expectedStatusCode: 404, + expected: { + "isError": true, + "errorCode": "GPII_ERR_NO_PREFSSAFE", + "message": "Missing prefsSafe" + } + } +]; + +/////////////////////// TESTING prefsSafesKeyCreate (POST) //////////////////////////// +fluid.registerNamespace("gpii.tests.preferencesServer.prefsSafes.prefsSafeKeyCreate"); + +gpii.tests.preferencesServer.prefsSafes.prefsSafeKeyCreate.buildTestDef = function (fixture) { + var expectedStatusCode = fixture.expectedStatusCode || 200; + return { + name: fixture.name, + expect: 2, + components: { + getRequest: { + type: "kettle.test.request.http", + options: { + path: "/prefsSafe-key-create", + method: "POST", + port: 8081 + } + } + }, + sequence: [{ + func: "{getRequest}.send", + args: fixture.postBody + }, { + event: "{getRequest}.events.onComplete", + listener: "gpii.tests.preferencesServer.testResponseLeftHand", + args: ["{arguments}.0", fixture.expected, "{getRequest}.nativeResponse.statusCode", expectedStatusCode] + }] + }; +}; + +gpii.tests.preferencesServer.prefsSafes.prefsSafeKeyCreate.successFixtures = [ + { + name: "POST: Add a new GPII Key to a preferences set", + postBody: { + "prefsSafeId": "prefsSafe-1", + "prefsSetId": "gpii-lowlight" + }, + expected: { + "type": "gpiiKey", + "schemaVersion": "0.3", + "prefsSafeId": "prefsSafe-1", + "prefsSetId": "gpii-lowlight", + "revoked": false, + "revokedReason": null, + // "timestampCreated": "2017-12-14T19:55:11.640Z", + "timestampUpdated": null + } + } +]; + +gpii.tests.preferencesServer.prefsSafes.prefsSafeKeyCreate.failFixtures = [ + { + name: "POST: Failure expected when not including the safe id and prefset id", + expectedStatusCode: 404, + postBody: { + // "prefsSafeId": "prefsSafe-1", + "prefsSetId": "gpii-lowlight" + }, + expected: { + isError: true, + errorCode: "GPII_ERR_MISSING_PREFSAFE_PREFSET_IDS", + message: "Unable to process. `prefsSafeId` and `prefsSetId` are required fields." + } + } +]; + +//////////////////////// Prefs Test Map /////////////////////////// + +gpii.tests.preferencesServer.prefsSafes.testMap = [ { + build: gpii.tests.preferencesServer.prefsSafes.get.buildTestDef, + fixtures: gpii.tests.preferencesServer.prefsSafes.get.successFixtures +}, { + build: gpii.tests.preferencesServer.prefsSafes.get.buildTestDef, + fixtures: gpii.tests.preferencesServer.prefsSafes.get.failFixtures +}, { + build: gpii.tests.preferencesServer.prefsSafes.putSafe.buildTestDef, + fixtures: gpii.tests.preferencesServer.prefsSafes.putSafe.successFixtures +}, { + build: gpii.tests.preferencesServer.prefsSafes.putSafe.buildTestDef, + fixtures: gpii.tests.preferencesServer.prefsSafes.putSafe.failFixtures +}, { + build: gpii.tests.preferencesServer.prefsSafes.postSafe.buildTestDef, + fixtures: gpii.tests.preferencesServer.prefsSafes.postSafe.successFixtures +}, { + build: gpii.tests.preferencesServer.prefsSafes.list.buildTestDef, + fixtures: gpii.tests.preferencesServer.prefsSafes.list.successFixtures +}, { + build: gpii.tests.preferencesServer.prefsSafes.listKeysForSafe.buildTestDef, + fixtures: gpii.tests.preferencesServer.prefsSafes.listKeysForSafe.successFixtures +}, { + build: gpii.tests.preferencesServer.prefsSafes.listKeysForSafe.buildTestDef, + fixtures: gpii.tests.preferencesServer.prefsSafes.listKeysForSafe.failFixtures +}, { + build: gpii.tests.preferencesServer.prefsSafes.getSafeWithKeys.buildTestDef, + fixtures: gpii.tests.preferencesServer.prefsSafes.getSafeWithKeys.successFixtures +}, { + build: gpii.tests.preferencesServer.prefsSafes.getSafeWithKeys.buildTestDef, + fixtures: gpii.tests.preferencesServer.prefsSafes.getSafeWithKeys.failFixtures +}, { + build: gpii.tests.preferencesServer.prefsSafes.prefsSafeKeyCreate.buildTestDef, + fixtures: gpii.tests.preferencesServer.prefsSafes.prefsSafeKeyCreate.successFixtures +}, { + build: gpii.tests.preferencesServer.prefsSafes.prefsSafeKeyCreate.buildTestDef, + fixtures: gpii.tests.preferencesServer.prefsSafes.prefsSafeKeyCreate.failFixtures +}]; + +fluid.defaults("gpii.tests.preferencesServer.prefsSafes.testEnvironment", { + gradeNames: ["gpii.test.couchdb.environment.base", "gpii.test.serverEnvironment"], + databases: { + gpii: { + data: [ + "%gpii-universal/gpii/node_modules/preferencesServer/test/data/gpiiKeys.json", + "%gpii-universal/gpii/node_modules/preferencesServer/test/data/prefsSafes.json", + "%gpii-universal/gpii/node_modules/preferencesServer/test/data/gpiiCloudSafeCred.json", + "%gpii-universal/testData/dbData/views.json" + ] + } + } +}); + +gpii.tests.preferencesServer.prefsSafes.testDefs = fluid.flatten(fluid.transform(gpii.tests.preferencesServer.prefsSafes.testMap, function (mapEl) { + return fluid.transform(mapEl.fixtures, mapEl.build, function (fixture) { + var common = { + config: gpii.tests.preferencesServer.config + }; + + return fluid.extend({}, common, fixture); + }); +})); + +gpii.tests.preferencesServer.prefsSafes.testDefToEnvironment = function (testDef) { + return gpii.test.testDefToEnvironment(testDef, "gpii.tests.preferencesServer.prefsSafes.testEnvironment", "gpii.test.couchSequenceGrade"); +}; + +gpii.test.runTestDefs(gpii.tests.preferencesServer.prefsSafes.testDefs, gpii.tests.preferencesServer.prefsSafes.testDefToEnvironment); diff --git a/package.json b/package.json index ee2f67c66..9849e6f85 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "express-session": "1.15.6", "fluid-resolve": "1.3.0", "glob": "7.1.3", + "gpii-express-user": "1.0.2-dev.20190627T120852Z.015bb40.GPII-3921", "@google-cloud/trace-agent": "3.4.0", "gpii-couchdb-test-harness": "1.0.1", "gpii-json-schema": "2.1.2-dev.20190704T103720Z.3c177e5.GPII-4009", diff --git a/scripts/generateCredentials.js b/scripts/generateCredentials.js index e67de274f..3f4a472fa 100644 --- a/scripts/generateCredentials.js +++ b/scripts/generateCredentials.js @@ -30,7 +30,7 @@ These credentials consist in: { "_id": "UUID-clientCredentials", "type": "clientCredential", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": "UUID-gpiiAppInstallationClient", "oauth2ClientId": "UUID-pilot-computer", "oauth2ClientSecret": "UUID-pilot-computer-secret", @@ -49,7 +49,7 @@ These credentials consist in: { "_id": "UUID-gpiiAppInstallationClient", "type": "gpiiAppInstallationClient", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "name": "Pilot Computers", "computerType": "public", "timestampCreated": "2017-11-21T18:11:22.101Z", @@ -118,7 +118,7 @@ var generateCredentials = function () { var clientCredential = { "_id": clientCredentialsId, "type": "clientCredential", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": gpiiAppInstallationClientId, "oauth2ClientId": clientId, "oauth2ClientSecret": clientSecret, @@ -135,7 +135,7 @@ var generateCredentials = function () { var gpiiAppInstallationClient = { "_id": gpiiAppInstallationClientId, "type": "gpiiAppInstallationClient", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "name": credentialsName, "computerType": "public", "timestampCreated": currentTime, diff --git a/scripts/migration/schema-0.3-GPII-2966/README.md b/scripts/migration/schema-0.3-GPII-2966/README.md new file mode 100644 index 000000000..7ac2b3e47 --- /dev/null +++ b/scripts/migration/schema-0.3-GPII-2966/README.md @@ -0,0 +1,46 @@ +# Data Migration and Verification for GPII-2966 Deployment + +This directory contains the data migration for GPII-2966. This migration is not required +for operation, but removes a now unused field. (for current and future use). That field is +the `password` field on `prefsSafe` document types. At the time of writing, this field +should most likely be `null` throughout the database given it was never exercised by any +code. + +## Scripts for testing + +1. createSimulatedGpiiKeys.js + + This script creates simulated GPII keys and their corresponding prefs safes in schema version 0.1 format. It + creates documents in batches of a given number. For example, for creating 1000 GPII keys in batches of 100, this + script will generate 100 new GPII keys and their corresponding prefs safes each time to send to CouchDB /_bulk_docs + to create. This is to avoid the potential memory overflow at creating a large number of GPII keys in one shot. An example to create 50K GPII keys in batches of 50: + ``` + node scripts/migration/schema-0.3-GPII-2966/createSimulatedGpiiKeys.js http://localhost:25984 50000 50 + ``` + +## Scripts for deployment + +1. migration-step1.js + + Run this script **before** deploying the new universal docker image from the universal root directory. Removes the + `password` field from preference safes.. An example: + ``` + node scripts/migration/schema-0.3-GPII-2966/migration-step1.js http://localhost:25984 + ``` + +2. migration-step2.js + + Run this script **after** deploying the new universal docker image from the universal root directory. + Updates the `schemaVersion` of all documents in the database to 0.3 and touches the `timestampUpdated` + value. + ``` + node scripts/migration/schema-0.3-GPII-2966/migration-step2.js http://localhost:25984 5000 + ``` + +3. verify.js + + Run this script **after** the data migration completes. It checks if all documents have been migrated. An example of verifying + in batches of 5000 documents in one batch: + ``` + node scripts/migration/schema-0.3-GPII-2966/verify.js http://localhost:25984 5000 + ``` diff --git a/scripts/migration/schema-0.3-GPII-2966/createSimulatedGpiiKeys.js b/scripts/migration/schema-0.3-GPII-2966/createSimulatedGpiiKeys.js new file mode 100644 index 000000000..4f02d879b --- /dev/null +++ b/scripts/migration/schema-0.3-GPII-2966/createSimulatedGpiiKeys.js @@ -0,0 +1,186 @@ +/*! +Copyright 2019 OCAD University + +Licensed under the New BSD license. You may not use this file except in +compliance with this License. + +You may obtain a copy of the License at +https://github.com/GPII/universal/blob/master/LICENSE.txt +*/ + +// This script creates simulated GPII keys and their corresponding prefs safes in schema version 0.2 format. +// It creates new documents in batches of a given number. Note that creating number n of GPII keys will end up +// with creating n * 2 documents in the database since each key has a corresponding prefs safe document created. + +// Usage: node scripts/migration/schema-0.3-GPII-2966/createSimulatedGpiiKeys.js CouchDB-url numOfKeysToCreate maxDocsInBatchPerRequest +// @param {String} CouchDB-url - The url to the CouchDB where docoments should be created. +// @param {Number} numOfKeysToCreate - The number of GPII keys to be created. +// @param {Number} maxDocsInBatchPerRequest - [optional] Limit the number of documents to be created in a batch. +// Default to 100 if not provided. + +// A sample command that runs this script in the universal root directory: +// node scripts/migration/schema-0.3-GPII-2966/createSimulatedGpiiKeys.js http://localhost:25984 100000 300 + +"use strict"; + +var fluid = require("infusion"), + gpii = fluid.registerNamespace("gpii"), + url = require("url"), + uuid = uuid || require("node-uuid"); + +fluid.registerNamespace("gpii.migration.GPII2966"); + +require("./shared/migratedValues.js"); +require("../../shared/dbRequestUtils.js"); +require("../../../gpii/node_modules/gpii-db-operation/src/DbUtils.js"); + +// Handle command line +if (process.argv.length < 4) { + console.log("Usage: node createSimulatedGpiiKeys.js COUCHDB_URL numOfKeysToCreate [maxDocsInBatchPerRequest]"); + process.exit(1); +} + +gpii.migration.GPII2966.defaultMaxDocsInBatchPerRequest = 100; + +/** + * Create a set of options for this script. + * The options are based on the command line parameters and a set of database constants. + * @param {Array} processArgv - The command line arguments. + * @return {Object} - The options. + */ +gpii.migration.GPII2966.initOptions = function (processArgv) { + var options = {}; + options.couchDbUrl = processArgv[2] + "/gpii"; + options.numOfKeysToCreate = Number(processArgv[3]); + options.maxDocsInBatchPerRequest = Number(processArgv[4]) || gpii.migration.GPII2966.defaultMaxDocsInBatchPerRequest; + + // Set up database specific options + options.newDocs = []; + options.numOfCreatedKeys = 0; + options.parsedCouchDbUrl = url.parse(options.couchDbUrl); + options.postOptions = { + hostname: options.parsedCouchDbUrl.hostname, + port: options.parsedCouchDbUrl.port, + path: "/gpii/_bulk_docs", + auth: options.parsedCouchDbUrl.auth, + method: "POST", + headers: { + "Accept": "application/json", + "Content-Length": 0, // IMPORTANT: FILL IN PER REQUEST + "Content-Type": "application/json" + } + }; + console.log("COUCHDB_URL: '" + + options.parsedCouchDbUrl.protocol + "//" + + options.parsedCouchDbUrl.hostname + ":" + + options.parsedCouchDbUrl.port + + options.parsedCouchDbUrl.pathname + "'" + ); + return options; +}; + +/** + * Generate an array of new documents to create. + * @param {Number} numOfKeys - The number of new GPII keys to create + * @return {Array} - An array of GPII keys and corresponding prefs safe documents to create. + */ +gpii.migration.GPII2966.generateKeyData = function (numOfKeys) { + var newDocs = []; + for (var i = 0; i < numOfKeys; i++) { + var currentTime = new Date().toISOString(); + var gpiiKeyId = uuid.v4(); + var prefsSafeId = "prefsSafe-" + gpiiKeyId; + var prefs = { + "flat": { + "contexts": { + "gpii-default": { + "name": "Default preferences", + "preferences": { + "http://registry.gpii.net/common/language": "en-US" + } + } + } + } + }; + + var newGpiiKey = { + "_id": gpiiKeyId, + "type": "gpiiKey", + "schemaVersion": "0.2", + "prefsSafeId": prefsSafeId, + "prefsSetId": "gpii-default", + "revoked": false, + "revokedReason": null, + "timestampCreated": currentTime, + "timestampUpdated": null + }; + + var newPrefsSafe = { + "_id": prefsSafeId, + "type": "prefsSafe", + "schemaVersion": "0.2", + "prefsSafeType": "user", + "name": gpiiKeyId, + "password": null, + "email": null, + "preferences": prefs, + "timestampCreated": currentTime, + "timestampUpdated": null + }; + newDocs.push(newGpiiKey); + newDocs.push(newPrefsSafe); + }; + return newDocs; +}; + +/** + * Log how many GPII documents were updated. + * @param {String} responseString - Response from the database (ignored) + * @param {Object} options - Object containing the set of documents: + * @param {Number} options.numOfKeysToCreate - Total number of GPII keys to create. + * @param {Number} options.numOfCreatedKeys - Total number of GPII keys that have been created. + * @return {Promise} - The resolved value is options.numOfCreatedKeys, or 0 when all has been created. + */ +gpii.migration.GPII2966.logUpdateDB = function (responseString, options) { + var togo = fluid.promise(); + options.numOfCreatedKeys = options.numOfCreatedKeys + options.numToCreateInThisBatch; + console.log("Created " + options.numOfCreatedKeys + " of requested " + options.numOfKeysToCreate + " GPII keys."); + togo.resolve(options.numOfCreatedKeys < options.numOfKeysToCreate ? options.numOfCreatedKeys : 0); + return togo; +}; + +/** + * Configure one batch of creation. + * @param {Object} options - Object containing the set of documents: + * @param {Array} options.newDocs - The documents to create. + * @param {Number} options.numOfKeysToCreate - Total number of GPII keys to create. + * @param {Number} options.numOfCreatedKeys - Total number of GPII keys that have been created. This option will be + * written by a call to this function. + * @return {Promise} - A promise whose resolved value is the number of created documents, or 0 when all has been created. + */ +gpii.migration.GPII2966.createOneBatch = function (options) { + var numOfNeeded = options.numOfKeysToCreate - options.numOfCreatedKeys; + options.numToCreateInThisBatch = numOfNeeded > options.maxDocsInBatchPerRequest ? options.maxDocsInBatchPerRequest : numOfNeeded; + + options.newDocs = gpii.migration.GPII2966.generateKeyData(options.numToCreateInThisBatch); + + var details = { + dataToPost: options.newDocs, + responseDataHandler: gpii.migration.GPII2966.logUpdateDB + }; + return gpii.dbRequest.configureStep(details, options); +}; + +/** + * Create and execute the steps to create GPII keys. + */ +gpii.migration.GPII2966.createKeys = function () { + var options = gpii.migration.GPII2966.initOptions(process.argv); + var finalPromise = gpii.dbRequest.processRecursive(options, gpii.migration.GPII2966.createOneBatch); + + finalPromise.then(function () { + console.log("Done: " + options.numOfCreatedKeys + " GPII keys have been created."); + }, console.log); +}; + +gpii.migration.GPII2966.createKeys(); diff --git a/scripts/migration/schema-0.3-GPII-2966/migration-step1.js b/scripts/migration/schema-0.3-GPII-2966/migration-step1.js new file mode 100644 index 000000000..b2d4573ca --- /dev/null +++ b/scripts/migration/schema-0.3-GPII-2966/migration-step1.js @@ -0,0 +1,163 @@ +/*! +Copyright 2019 OCAD University + +Licensed under the New BSD license. You may not use this file except in +compliance with this License. + +You may obtain a copy of the License at +https://github.com/GPII/universal/blob/master/LICENSE.txt +*/ + +// This script performs the first step of the data migration for GPII-2966 deployment. +// It removes the `password` field from all `prefsSafe` documents. + +// Usage: node scripts/migration/schema-0.3-GPII-2966/migration-step1.js CouchDB-url clientCredentialId-for-NOVA ... +// @param {String} CouchDB-url - The url to the CouchDB where docoments should be migrated. +// @param {Strings} clientCredentialIds-for-NOVA - The "_id" value of the NOVA client credential. There could be any +// number of client credential parameters from here onwards. + +// A sample command that runs this script in the universal root directory: +// node scripts/migration/schema-0.3-GPII-2966/migration-step1.js http://localhost:25984 + +"use strict"; + +var fluid = require("infusion"), + gpii = fluid.registerNamespace("gpii"), + url = require("url"); + +fluid.registerNamespace("gpii.migration.GPII2966"); + +require("./shared/migratedValues.js"); +require("../../shared/dbRequestUtils.js"); +require("../../../gpii/node_modules/gpii-db-operation/src/DbUtils.js"); + +/** + * Create a set of options for this script. + * The options are based on the command line parameters and a set of database constants. + * @param {Array} processArgv - The command line arguments. + * @return {Object} - The options. + */ +gpii.migration.GPII2966.initOptions = function (processArgv) { + var options = {}; + options.couchDbUrl = processArgv[2] + "/gpii"; + options.novaClientCredentials = process.argv.splice(3); + + // Set up database specific options + options.allDocsUrl = options.couchDbUrl + "/_all_docs?include_docs=true"; + options.clientCredentials = []; + options.parsedCouchDbUrl = url.parse(options.couchDbUrl); + options.postOptions = { + hostname: options.parsedCouchDbUrl.hostname, + port: options.parsedCouchDbUrl.port, + path: "/gpii/_bulk_docs", + auth: options.parsedCouchDbUrl.auth, + method: "POST", + headers: { + "Accept": "application/json", + "Content-Length": 0, // IMPORTANT: FILL IN PER REQUEST + "Content-Type": "application/json" + } + }; + console.log("COUCHDB_URL: '" + + options.parsedCouchDbUrl.protocol + "//" + + options.parsedCouchDbUrl.hostname + ":" + + options.parsedCouchDbUrl.port + + options.parsedCouchDbUrl.pathname + "'" + ); + return options; +}; + +/** + * Create the step that retrieves all documents from the database + * @param {Object} options - All docs URL and whether to filter: + * @param {Array} options.allDocsUrl - The url for retrieving all documents in the database. + * @return {Promise} - A promise that resolves retrieved documents. + */ +gpii.migration.GPII2966.retrieveAllDocs = function (options) { + var details = { + requestUrl: options.allDocsUrl, + requestErrMsg: "Error retrieving documents from the database: ", + responseDataHandler: gpii.migration.GPII2966.updateDocsData, + responseErrMsg: "Error retrieving documents from database: " + }; + + return gpii.dbRequest.configureStep(details, options); +}; + +/** + * Given all the documents from the database, update their data according to the new data model. + * @param {String} responseString - the response from the request to get all documents. + * @param {Object} options - Where to store the to-be-updated documents: + * @return {Array} - The updated documents in the new data structure with the new schema version. + */ +gpii.migration.GPII2966.updateDocsData = function (responseString, options) { + var allDocs = JSON.parse(responseString); + var updatedDocs = []; + options.totalNumOfDocs = allDocs.total_rows; + if (allDocs.rows) { + fluid.each(allDocs.rows, function (aRow) { + var aDoc = aRow.doc; + // To filter out the "_design/views" doc that doesn't have the "schemaVersion" field + if (aDoc.type === "prefsSafe") { + aDoc.schemaVersion = gpii.migration.GPII2966.newSchemaVersion; + aDoc.timestampUpdated = gpii.dbOperation.getCurrentTimestamp(); + if (aDoc.password !== undefined) { + delete aDoc.password; + } + console.log("Updating the prefsSafe ID: ", aDoc._id); + updatedDocs.push(aDoc); + } + }); + options.updatedDocs = updatedDocs; + } + return updatedDocs; +}; + +/** + * Log how many GPII documents were updated. + * @param {String} responseString - Response from the database (ignored) + * @param {Object} options - Object containing the set of documents: + * @param {Array} options.updatedDocs - The documents to update. + * @return {Number} - the number of documents updated. + */ +gpii.migration.GPII2966.logUpdateDB = function (responseString, options) { + console.log("Updated ", options.updatedDocs.length, " of ", options.totalNumOfDocs, " GPII documents."); + return options.updatedDocs.length; +}; + +/** + * Configure update, in batch, of the documents. + * @param {Object} options - The documents to be updated: + * @param {Array} options.updatedDocs - The documents to update. + * @return {Promise} - The promise that resolves the update. + */ +gpii.migration.GPII2966.updateDB = function (options) { + var details = { + dataToPost: options.updatedDocs, + responseDataHandler: gpii.migration.GPII2966.logUpdateDB + }; + return gpii.dbRequest.configureStep(details, options); +}; + +/** + * Create and execute the steps to migrate documents. + */ +gpii.migration.GPII2966.migrateStep1 = function () { + var options = gpii.migration.GPII2966.initOptions(process.argv); + var sequence = [ + gpii.migration.GPII2966.retrieveAllDocs, + gpii.migration.GPII2966.updateDB + ]; + fluid.promise.sequence(sequence, options).then( + function (/*result*/) { + console.log("Done."); + process.exit(0); + }, + function (error) { + console.log(error); + process.exit(1); + } + ); +}; + +gpii.migration.GPII2966.migrateStep1(); diff --git a/scripts/migration/schema-0.3-GPII-2966/migration-step2.js b/scripts/migration/schema-0.3-GPII-2966/migration-step2.js new file mode 100644 index 000000000..b587c69e5 --- /dev/null +++ b/scripts/migration/schema-0.3-GPII-2966/migration-step2.js @@ -0,0 +1,183 @@ +/*! +Copyright 2019 OCAD University + +Licensed under the New BSD license. You may not use this file except in +compliance with this License. + +You may obtain a copy of the License at +https://github.com/GPII/universal/blob/master/LICENSE.txt +*/ + +// This script performs the second step of the data migration for GPII-2966 deployment. It includes: +// 1. Bump schemaVersion value from 0.2 to 0.3 for all documents that have schemaVersion field. +// Note that "_design/views" doc doesn't have this field. +// 2. if the document has "timestampUpdated" field, set it to the time that the migration runs. +// Note that this script migrates documents in batches. Each batch will migrate a limited number of documents +// to avoid hitting the out-of-memory issue when migrating a large number of documents. + +// Usage: node scripts/migration/schema-0.3-GPII-2966/migration-step2.js CouchDB-url [maxDocsInBatchPerRequest] +// @param {String} CouchDB-url - The url to the CouchDB where docoments should be migrated. +// @param {Number} maxDocsInBatchPerRequest - [optional] Limit the number of documents to be migrated in a batch. +// Default to 100 if not provided. + +// A sample command that runs this script in the universal root directory: +// node scripts/migration/schema-0.3-GPII-2966/migration-step2.js http://localhost:25984 10 + +"use strict"; + +var fluid = require("infusion"), + gpii = fluid.registerNamespace("gpii"), + url = require("url"); + +fluid.registerNamespace("gpii.migration.GPII2966"); + +require("./shared/migratedValues.js"); +require("../../shared/dbRequestUtils.js"); +require("../../../gpii/node_modules/gpii-db-operation/src/DbUtils.js"); + +gpii.migration.GPII2966.defaultMaxDocsInBatchPerRequest = 100; + +/** + * Create a set of options for this script. + * The options are based on the command line parameters and a set of database constants. + * @param {Array} processArgv - The command line arguments. + * @return {Object} - The options. + */ +gpii.migration.GPII2966.initOptions = function (processArgv) { + var options = {}; + options.couchDbUrl = processArgv[2] + "/gpii"; + options.maxDocsInBatchPerRequest = Number(processArgv[3]) || gpii.migration.GPII2966.defaultMaxDocsInBatchPerRequest; + options.numOfUpdated = 0; + + // Set up database specific options + options.allDocsUrl = options.couchDbUrl + "/_design/views/_view/findDocsBySchemaVersion?key=%22" + gpii.migration.GPII2966.oldSchemaVersion + "%22&limit=" + options.maxDocsInBatchPerRequest; + options.allDocs = []; + options.parsedCouchDbUrl = url.parse(options.couchDbUrl); + options.postOptions = { + hostname: options.parsedCouchDbUrl.hostname, + port: options.parsedCouchDbUrl.port, + path: "/gpii/_bulk_docs", + auth: options.parsedCouchDbUrl.auth, + method: "POST", + headers: { + "Accept": "application/json", + "Content-Length": 0, // IMPORTANT: FILL IN PER REQUEST + "Content-Type": "application/json" + } + }; + console.log("COUCHDB_URL: '" + + options.parsedCouchDbUrl.protocol + "//" + + options.parsedCouchDbUrl.hostname + ":" + + options.parsedCouchDbUrl.port + + options.parsedCouchDbUrl.pathname + "'" + ); + return options; +}; + +/** + * Create the step that retrieves all documents from the database + * @param {Object} options - All docs URL and whether to filter: + * @param {String} options.allDocsUrl - The url for retrieving all documents in the database. + * @return {Promise} - A promise that resolves retrieved documents. + */ +gpii.migration.GPII2966.retrieveAllDocs = function (options) { + var details = { + requestUrl: options.allDocsUrl, + requestErrMsg: "Error retrieving documents from the database: ", + responseDataHandler: gpii.migration.GPII2966.updateDocsData, + responseErrMsg: "Error retrieving documents from database: " + }; + return gpii.dbRequest.configureStep(details, options); +}; + +/** + * Given all the documents from the database, update their data according to the new data model. + * @param {String} responseString - the response from the request to get all documents. + * @param {Object} options - Where to store the to-be-updated documents: + * @param {Array} options.allDocs - Accumulated documents. + * @return {Promise} - The resolved value contains an array of documents to be migrated, or 0 + * when no more document to migrate. + */ +gpii.migration.GPII2966.updateDocsData = function (responseString, options) { + var allDocs = JSON.parse(responseString); + var updatedDocs = []; + var togo = fluid.promise(); + + options.totalNumOfDocs = allDocs.total_rows; + + if (allDocs.rows.length === 0) { + options.updatedDocs = 0; + } else { + fluid.each(allDocs.rows, function (aRow) { + var aDoc = aRow.value; + // To filter out the "_design/views" doc that doesn't have the "schemaVersion" field + if (aDoc.schemaVersion) { + aDoc.schemaVersion = gpii.migration.GPII2966.newSchemaVersion; + if (aDoc.timestampUpdated === null) { + aDoc.timestampUpdated = gpii.dbOperation.getCurrentTimestamp(); + } + updatedDocs.push(aDoc); + } + }); + options.updatedDocs = updatedDocs; + } + togo.resolve(options.updatedDocs); + + return togo; +}; + +/** + * Log how many GPII documents were updated. + * @param {String} responseString - Response from the database (ignored) + * @param {Object} options - Object containing the set of documents: + * @param {Array} options.updatedDocs - The documents to update. + * @param {Number} options.numOfUpdated - The total number of migrated documents. + * @return {Number} - the number of documents updated. + */ +gpii.migration.GPII2966.logUpdateDB = function (responseString, options) { + options.numOfUpdated = options.numOfUpdated + options.updatedDocs.length; + console.log("Updated ", options.numOfUpdated, " of ", options.totalNumOfDocs, " GPII documents."); + return options.updatedDocs.length; +}; + +/** + * Configure update, in batch, of the documents. + * @param {Object} options - The documents to be updated: + * @param {Array} options.updatedDocs - The documents to update. + * @return {Promise} - A promise whose resolved value is the number of migrated documents, + * or 0 when all has been migrated. + */ +gpii.migration.GPII2966.updateDB = function (options) { + var togo = fluid.promise(); + if (options.updatedDocs === 0) { + togo.resolve(0); + } else { + var details = { + dataToPost: options.updatedDocs, + responseDataHandler: gpii.migration.GPII2966.logUpdateDB + }; + togo = gpii.dbRequest.configureStep(details, options); + } + return togo; +}; + +/** + * Create and execute the steps to migrate documents. + */ +gpii.migration.GPII2966.migrateStep2 = function () { + var options = gpii.migration.GPII2966.initOptions(process.argv); + var actionFunc = function (options) { + return fluid.promise.sequence([ + gpii.migration.GPII2966.retrieveAllDocs, + gpii.migration.GPII2966.updateDB + ], options); + }; + + var finalPromise = gpii.dbRequest.processRecursive(options, actionFunc); + + finalPromise.then(function () { + console.log("Done: Migrated " + options.numOfUpdated + " documents in total."); + }, console.log); +}; + +gpii.migration.GPII2966.migrateStep2(); diff --git a/scripts/migration/schema-0.3-GPII-2966/shared/migratedValues.js b/scripts/migration/schema-0.3-GPII-2966/shared/migratedValues.js new file mode 100644 index 000000000..b8cbb8790 --- /dev/null +++ b/scripts/migration/schema-0.3-GPII-2966/shared/migratedValues.js @@ -0,0 +1,24 @@ +/*! +Copyright 2019 Raising the Floor - US + +Licensed under the New BSD license. You may not use this file except in +compliance with this License. + +You may obtain a copy of the License at +https://github.com/GPII/universal/blob/master/LICENSE.txt +*/ + +// This shared script defines migrated values for the GPII-4014 deployment. + +"use strict"; + +var fluid = require("infusion"), + gpii = fluid.registerNamespace("gpii"); + +fluid.registerNamespace("gpii.migration.GPII2966"); + +// Migrated values +gpii.migration.GPII2966.oldSchemaVersion = "0.2"; +gpii.migration.GPII2966.newSchemaVersion = "0.3"; + +gpii.migration.GPII2966.passwordFieldName = "password"; diff --git a/scripts/migration/schema-0.3-GPII-2966/verify.js b/scripts/migration/schema-0.3-GPII-2966/verify.js new file mode 100644 index 000000000..d78a5d4da --- /dev/null +++ b/scripts/migration/schema-0.3-GPII-2966/verify.js @@ -0,0 +1,152 @@ +/*! +Copyright 2019 OCAD University + +Licensed under the New BSD license. You may not use this file except in +compliance with this License. + +You may obtain a copy of the License at +https://github.com/GPII/universal/blob/master/LICENSE.txt +*/ + +// This script verifies updated CouchDB documents after the GPII-2966 deployment. It includes: +// 1. Password field has been removed from `prefsSafe` documents. +// 2. All "schemaVersion" in documents have been set to 0.3. Note that "_design/views" doc doesn't have this field. +// 3. All "timestampUpdated" have been set. + +// Usage: node scripts/migration/schema-0.3-GPII-2966/verify.js CouchDB-url maxDocsInBatchPerRequest clientCredentialId-for-NOVA ... +// @param {String} CouchDB-url - The url to the CouchDB where docoments should be verified. +// @param {Number} maxDocsInBatchPerRequest - Limit the number of documents to be verified in a batch. + +// A sample command that runs this script in the universal root directory: +// node scripts/migration/schema-0.3-GPII-2966/verify.js http://localhost:25984 100 + +"use strict"; + +var fluid = require("infusion"), + gpii = fluid.registerNamespace("gpii"), + url = require("url"); + +fluid.registerNamespace("gpii.migration.GPII2966"); + +require("./shared/migratedValues.js"); +require("../../shared/dbRequestUtils.js"); +require("../../../gpii/node_modules/gpii-db-operation/src/DbUtils.js"); + +/** + * Create a set of options for this script. + * The options are based on the command line parameters and a set of database constants. + * @param {Array} processArgv - The command line arguments. + * @return {Object} - The options. + */ +gpii.migration.GPII2966.initOptions = function (processArgv) { + var options = {}; + options.couchDbUrl = processArgv[2] + "/gpii"; + options.maxDocsInBatchPerRequest = Number(processArgv[3]); + options.numOfVerified = 0; + options.numOfErrorDocs = 0; + + // Set up database specific options + options.allDocsUrl = options.couchDbUrl + "/_all_docs?include_docs=true&descending=true&limit=" + options.maxDocsInBatchPerRequest; + options.allDocs = []; + options.parsedCouchDbUrl = url.parse(options.couchDbUrl); + options.postOptions = { + hostname: options.parsedCouchDbUrl.hostname, + port: options.parsedCouchDbUrl.port, + path: "/gpii/_bulk_docs", + auth: options.parsedCouchDbUrl.auth, + method: "POST", + headers: { + "Accept": "application/json", + "Content-Length": 0, // IMPORTANT: FILL IN PER REQUEST + "Content-Type": "application/json" + } + }; + console.log("COUCHDB_URL: '" + + options.parsedCouchDbUrl.protocol + "//" + + options.parsedCouchDbUrl.hostname + ":" + + options.parsedCouchDbUrl.port + + options.parsedCouchDbUrl.pathname + "'" + ); + return options; +}; + +/** + * Create the step that retrieves all documents from the database + * @param {Object} options - All docs URL and whether to filter: + * @param {String} options.allDocsUrl - The url for retrieving all documents in the database. + * @return {Promise} - A promise whose resolved value is the verification result. + */ +gpii.migration.GPII2966.verifyDocsInBatch = function (options) { + var details = { + requestUrl: options.allDocsUrl, + requestErrMsg: "Error retrieving documents from the database: ", + responseDataHandler: gpii.migration.GPII2966.verifyDocsData, + responseErrMsg: "Error verifying documents from database: " + }; + return gpii.dbRequest.configureStep(details, options); +}; + +/** + * Given all the documents from the database, update their data according to the new data model. + * @param {String} responseString - the response from the request to get all documents. + * @param {Object} options - Where to store the to-be-updated documents: + * @param {Number} options.numOfVerified - The number of verified documents. + * @param {Number} options.numOfErrorDocs - The number of verified documents that have verification errors. + * @return {Promise} - A promise whose resolved value is the number of verified documents, or 0 when all has been verified. + */ +gpii.migration.GPII2966.verifyDocsData = function (responseString, options) { + var allDocs = JSON.parse(responseString); + options.totalNumOfDocs = allDocs.total_rows; + var togo = fluid.promise(); + + if (allDocs.rows.length === 0) { + togo.resolve(0); + } else { + fluid.each(allDocs.rows, function (aRow) { + var hasError = false; + var aDoc = aRow.doc; + // To filter out the "_design/views" doc that doesn't have the "schemaVersion" field + if (aDoc.schemaVersion) { + if (aDoc.schemaVersion !== gpii.migration.GPII2966.newSchemaVersion) { + console.log("Error with the document _id \"" + aDoc._id + "\": schema version is ", aDoc.schemaVersion, ", not ", gpii.migration.GPII2966.newSchemaVersion); + hasError = true; + } + if (aDoc.timestampUpdated === null) { + console.log("Error with the document _id \"" + aDoc._id + "\": the value of timestampUpdated is empty"); + hasError = true; + } + if (aDoc.type === "prefsSafe" && aDoc.password !== undefined) { + console.log("Error with the document _id \"" + aDoc._id + "\": password field not removed from prefsSafe."); + hasError = true; + } + } + if (hasError) { + options.numOfErrorDocs++; + } + }); + options.numOfVerified = options.numOfVerified + allDocs.rows.length; + console.log("Verified " + options.numOfVerified + " of " + options.totalNumOfDocs + " documents."); + options.allDocsUrl = options.couchDbUrl + "/_all_docs?include_docs=true&descending=true&skip=" + options.numOfVerified + "&limit=" + options.maxDocsInBatchPerRequest; + togo.resolve(allDocs.rows.length); + } + return togo; +}; + +/** + * Create and execute the steps to verify documents. + */ +gpii.migration.GPII2966.verify = function () { + var options = gpii.migration.GPII2966.initOptions(process.argv); + var finalPromise = gpii.dbRequest.processRecursive(options, gpii.migration.GPII2966.verifyDocsInBatch); + + finalPromise.then(function () { + if (options.numOfErrorDocs > 0) { + console.log("Fail: " + options.numOfErrorDocs + " documents have errors."); + } else { + console.log("All passed."); + } + console.log("Done: Verified " + options.totalNumOfDocs + " documents in total."); + }, console.log); +}; + +gpii.migration.GPII2966.verify(); diff --git a/scripts/shared/prefsSetsDbUtils.js b/scripts/shared/prefsSetsDbUtils.js index 3e868f5a2..3bff76947 100644 --- a/scripts/shared/prefsSetsDbUtils.js +++ b/scripts/shared/prefsSetsDbUtils.js @@ -55,7 +55,7 @@ gpii.prefsSetsDbUtils.generateKeyData = function (gpiiKeyId, preferences, prefsS var newGpiiKey = { "_id": gpiiKeyId, "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": prefsSafeId, "prefsSetId": "gpii-default", "revoked": false, @@ -67,10 +67,9 @@ gpii.prefsSetsDbUtils.generateKeyData = function (gpiiKeyId, preferences, prefsS var newPrefsSafe = { "_id": prefsSafeId, "type": "prefsSafe", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeType": prefsSafeType || "user", "name": gpiiKeyId, - "password": null, "email": null, "preferences": preferences || gpii.prefsSetsDbUtils.emptyPreferencesBlock, "timestampCreated": currentTime, diff --git a/testData/dbData/clientCredentials.json b/testData/dbData/clientCredentials.json index f66e408de..c5910dc28 100644 --- a/testData/dbData/clientCredentials.json +++ b/testData/dbData/clientCredentials.json @@ -2,7 +2,7 @@ { "_id": "clientCredential-1", "type": "clientCredential", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": "gpiiAppInstallationClient-1", "oauth2ClientId": "pilot-computer", "oauth2ClientSecret": "pilot-computer-secret", diff --git a/testData/dbData/gpiiAppInstallationClients.json b/testData/dbData/gpiiAppInstallationClients.json index 4c6e76f4b..3ba6ae80d 100644 --- a/testData/dbData/gpiiAppInstallationClients.json +++ b/testData/dbData/gpiiAppInstallationClients.json @@ -2,7 +2,7 @@ { "_id": "gpiiAppInstallationClient-1", "type": "gpiiAppInstallationClient", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "name": "Pilot Computers", "computerType": "public", "timestampCreated": "2017-11-21T18:11:22.101Z", diff --git a/testData/dbData/views.json b/testData/dbData/views.json index 6c189697f..96bd380a8 100644 --- a/testData/dbData/views.json +++ b/testData/dbData/views.json @@ -11,6 +11,15 @@ "findInfoByAccessToken": { "map": "function(doc) {\n if (doc.type === 'gpiiAppInstallationAuthorization' && doc.revoked === false) {\n var record = { authorization: doc };\n if (doc.clientCredentialId) {\n record._id = doc.clientCredentialId;\n } \n emit(doc.accessToken, record);\n }\n }" }, + "findRelatedDocsForPrefsSafe": { + "map": "function(doc) {if (doc.type === 'gpiiKey' || doc.type === 'gpiiCloudSafeCredential') emit(doc.prefsSafeId, null)}" + }, + "listPrefsSafes": { + "map": "function(doc) {if (doc.type === 'prefsSafe') emit(doc._id, {'name': doc.name, 'email': doc.email, 'created': doc.timestampCreated, 'updated': doc.timestampUpdated})}" + }, + "expressUserSafeLoginLookup": { + "map": "function(doc) {if (doc.type === 'gpiiCloudSafeCredential') {emit(doc.gpiiExpressUserId, null);}}" + }, "findSnapsetPrefsSafes": { "map": "function(doc) {if (doc.type === 'prefsSafe' && doc.prefsSafeType === 'snapset') { emit(doc._id, doc); }}" }, @@ -24,5 +33,19 @@ "map": "function(doc) {emit(doc.schemaVersion, doc); }" } } + }, + { + "_id": "_design/lookup", + "views": { + "byUsernameOrEmail": { + "map": "function (doc) {\n if (doc.type === 'user') { emit(doc.username, doc); \n emit(doc.email, doc); \n } \n}" + }, + "byVerificationCode": { + "map": "function (doc) {\n if (doc.type === 'user' && doc.verification_code) { emit(doc.verification_code, doc); \n } \n}" + }, + "byResetCode": { + "map": "function (doc) {\n if (doc.type === 'user' && doc.reset_code) { emit(doc.reset_code, doc); \n } \n}" + } + } } ] diff --git a/tests/all-tests.js b/tests/all-tests.js index f2bf8c474..ef4e95ed6 100644 --- a/tests/all-tests.js +++ b/tests/all-tests.js @@ -68,10 +68,15 @@ var testIncludes = [ "../gpii/node_modules/gpii-oauth2/gpii-oauth2-authz-server/test/authGrantFinderTests.js", "../gpii/node_modules/gpii-oauth2/gpii-oauth2-authz-server/test/authorizationServiceTests.js", "../gpii/node_modules/gpii-oauth2/gpii-oauth2-utilities/test/OAuth2UtilitiesTests.js", + "../gpii/node_modules/lifecycleManager/test/js/LifeCycleManagerUtilsTests.js", "../gpii/node_modules/matchMakerFramework/test/MatchMakerFrameworkTests.js", "../gpii/node_modules/ontologyHandler/test/node/OntologyHandlerTests.js", "../gpii/node_modules/preferencesServer/test/preferencesServerTests.js", + "../gpii/node_modules/preferencesServer/test/cloudSafeCredTests.js", + "../gpii/node_modules/preferencesServer/test/prefsSafesTests.js", "../gpii/node_modules/preferencesServer/test/preferencesServiceTests.js", + "../gpii/node_modules/settingsHandlers/test/JSONSettingsHandlerTests.js", + "../gpii/node_modules/settingsHandlers/test/XMLSettingsHandlerTests.js", "../gpii/node_modules/settingsHandlers/test/INISettingsHandlerTests.js", "../gpii/node_modules/settingsHandlers/test/JSONSettingsHandlerTests.js", "../gpii/node_modules/settingsHandlers/test/NoSettingsHandlerTests.js", diff --git a/tests/data/dbData/clientCredentials.json b/tests/data/dbData/clientCredentials.json index eab0c69ae..e428d06c8 100644 --- a/tests/data/dbData/clientCredentials.json +++ b/tests/data/dbData/clientCredentials.json @@ -2,7 +2,7 @@ { "_id": "clientCredential-1", "type": "clientCredential", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": "gpiiAppInstallationClient-1", "oauth2ClientId": "pilot-computer", "oauth2ClientSecret": "pilot-computer-secret", @@ -18,7 +18,7 @@ { "_id": "clientCredential-schemaV0.1", "type": "clientCredential", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": "gpiiAppInstallationClient-schemaV0.1", "oauth2ClientId": "pilot-computer-schemaV0.1", "oauth2ClientSecret": "pilot-computer-secret-schemaV0.1", @@ -30,7 +30,7 @@ { "_id": "clientCredential-nova", "type": "clientCredential", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": "gpiiAppInstallationClient-nova", "oauth2ClientId": "nova-computer", "oauth2ClientSecret": "nova-computer-secret", @@ -53,7 +53,7 @@ { "_id": "clientCredential-failInIpVerification", "type": "clientCredential", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": "gpiiAppInstallationClient-nova-failInIpVerification", "oauth2ClientId": "nova-computer-failInIpVerification", "oauth2ClientSecret": "nova-computer-secret-failInIpVerification", @@ -69,7 +69,7 @@ { "_id": "clientCredential-failInAllowedPrefsToWrite", "type": "clientCredential", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "clientId": "gpiiAppInstallationClient-nova-failInAllowedPrefsToWrite", "oauth2ClientId": "nova-computer-failInAllowedPrefsToWrite", "oauth2ClientSecret": "nova-computer-secret-failInAllowedPrefsToWrite", diff --git a/tests/data/dbData/gpiiAppInstallationClients.json b/tests/data/dbData/gpiiAppInstallationClients.json index d7db029ca..5465019b6 100644 --- a/tests/data/dbData/gpiiAppInstallationClients.json +++ b/tests/data/dbData/gpiiAppInstallationClients.json @@ -2,7 +2,7 @@ { "_id": "gpiiAppInstallationClient-1", "type": "gpiiAppInstallationClient", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "name": "Pilot Computers", "computerType": "public", "timestampCreated": "2017-11-21T18:11:22.101Z", @@ -11,7 +11,7 @@ { "_id": "gpiiAppInstallationClient-schemaV0.1", "type": "gpiiAppInstallationClient", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "name": "To match the client credential data structure that is in the old schema version 0.1", "computerType": "public", "timestampCreated": "2017-11-21T18:11:22.101Z", @@ -20,7 +20,7 @@ { "_id": "gpiiAppInstallationClient-nova", "type": "gpiiAppInstallationClient", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "name": "NOVA - success workflow tests", "computerType": "public", "timestampCreated": "2019-05-28T18:11:22.101Z", @@ -29,7 +29,7 @@ { "_id": "gpiiAppInstallationClient-nova-failInIpVerification", "type": "gpiiAppInstallationClient", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "name": "NOVA - erroneous workflow test for the ip verification", "computerType": "public", "timestampCreated": "2019-05-28T18:11:22.101Z", @@ -38,7 +38,7 @@ { "_id": "gpiiAppInstallationClient-nova-failInAllowedPrefsToWrite", "type": "gpiiAppInstallationClient", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "name": "NOVA - erroneous workflow test for the allowed preferences to write", "computerType": "public", "timestampCreated": "2019-05-28T18:11:22.101Z", diff --git a/tests/data/dbData/gpiiKeys.json b/tests/data/dbData/gpiiKeys.json index 7fe1d60ab..52d20280a 100644 --- a/tests/data/dbData/gpiiKeys.json +++ b/tests/data/dbData/gpiiKeys.json @@ -2,7 +2,7 @@ { "_id": "gpii_key_no_prefs_safe", "type": "gpiiKey", - "schemaVersion": "0.2", + "schemaVersion": "0.3", "prefsSafeId": null, "prefsSetId": null, "revoked": false, diff --git a/tests/web/html/all-tests.html b/tests/web/html/all-tests.html index 9bf6ffaf6..700b8d4ab 100644 --- a/tests/web/html/all-tests.html +++ b/tests/web/html/all-tests.html @@ -15,7 +15,6 @@ "/gpii/node_modules/gpii-user-errors/test/html/UserErrorsTest.html", "/gpii/node_modules/journal/test/html/JournalIdParserTests.html", "/gpii/node_modules/lifecycleManager/test/html/LifecycleManagerResolverTest.html", - "/gpii/node_modules/lifecycleManager/test/html/LifecycleManagerUtilsTest.html", "/gpii/node_modules/lifecycleManager/test/html/LifecycleManagerTest.html", "/gpii/node_modules/matchMakerFramework/test/html/MatchMakerUtilitiesTest.html", "/gpii/node_modules/ontologyHandler/test/html/OntologyHandlerUtilitiesTest.html",