diff --git a/packages/core b/packages/core index 804483d3a3..ab14ab9db1 160000 --- a/packages/core +++ b/packages/core @@ -1 +1 @@ -Subproject commit 804483d3a3210936743f3b57a032783296eab06c +Subproject commit ab14ab9db1698a82b1f98b692ecfa6b71c17752a diff --git a/packages/server/appPackages/master/appDefinition.json b/packages/server/appPackages/master/appDefinition.json index 7487ae21b3..02296255c6 100644 --- a/packages/server/appPackages/master/appDefinition.json +++ b/packages/server/appPackages/master/appDefinition.json @@ -273,6 +273,18 @@ "getInitialValue": "default", "getUndefinedValue": "default" }, + { + "name": "instanceVersion", + "type": "string", + "typeOptions": { + "maxLength": null, + "values": null, + "allowDeclaredValuesOnly": false + }, + "label": "Instance Version", + "getInitialValue": "default", + "getUndefinedValue": "default" + }, { "name": "username", "type": "string", diff --git a/packages/server/appPackages/master/main.js b/packages/server/appPackages/master/main.js index bc6f1b36d9..849b90c720 100644 --- a/packages/server/appPackages/master/main.js +++ b/packages/server/appPackages/master/main.js @@ -23,13 +23,6 @@ module.exports = (config) => { const application = await apis.recordApi.load(appKey); - const dbConfig = await createInstanceDb( - datastoreModule, - config.datastoreConfig, - application.id, - instance.id - ); - const versionId = $(instance.version.key, [ splitKey, last @@ -41,6 +34,15 @@ module.exports = (config) => { if(!await exists(runtimeDir)) await downloadAppPackage(apis, instance, application.name, versionId); + + const dbConfig = await createInstanceDb( + config, + datastoreModule, + config.datastoreConfig, + application, + instance + ); + instance.datastoreconfig = JSON.stringify(dbConfig); instance.isNew = false; diff --git a/packages/server/appPackages/testApp/access_levels.json b/packages/server/appPackages/testApp/access_levels.json index ebb7ca11a1..4e01e4e9a7 100644 --- a/packages/server/appPackages/testApp/access_levels.json +++ b/packages/server/appPackages/testApp/access_levels.json @@ -1 +1,75 @@ -{"levels":[{"name":"owner","permissions":[{"type":"create record","nodeKey":"/applications/1-{id}"},{"type":"update record","nodeKey":"/applications/1-{id}"},{"type":"delete record","nodeKey":"/applications/1-{id}"},{"type":"read record","nodeKey":"/applications/1-{id}"},{"type":"create record","nodeKey":"/applications/1-{id}/users/8-{id}"},{"type":"update record","nodeKey":"/applications/1-{id}/users/8-{id}"},{"type":"delete record","nodeKey":"/applications/1-{id}/users/8-{id}"},{"type":"read record","nodeKey":"/applications/1-{id}/users/8-{id}"},{"type":"create record","nodeKey":"/applications/1-{id}/instances/2-{id}"},{"type":"update record","nodeKey":"/applications/1-{id}/instances/2-{id}"},{"type":"delete record","nodeKey":"/applications/1-{id}/instances/2-{id}"},{"type":"read record","nodeKey":"/applications/1-{id}/instances/2-{id}"},{"type":"create record","nodeKey":"/applications/1-{id}/versions/3-{id}"},{"type":"update record","nodeKey":"/applications/1-{id}/versions/3-{id}"},{"type":"delete record","nodeKey":"/applications/1-{id}/versions/3-{id}"},{"type":"read record","nodeKey":"/applications/1-{id}/versions/3-{id}"},{"type":"create record","nodeKey":"/applications/1-{id}/sessions/16-{id}"},{"type":"update record","nodeKey":"/applications/1-{id}/sessions/16-{id}"},{"type":"delete record","nodeKey":"/applications/1-{id}/sessions/16-{id}"},{"type":"read record","nodeKey":"/applications/1-{id}/sessions/16-{id}"},{"type":"create record","nodeKey":"/sessions/17-{id}"},{"type":"update record","nodeKey":"/sessions/17-{id}"},{"type":"delete record","nodeKey":"/sessions/17-{id}"},{"type":"read record","nodeKey":"/sessions/17-{id}"},{"type":"read index","nodeKey":"/mastersessions_by_user"},{"type":"read index","nodeKey":"/all_applications"},{"type":"read index","nodeKey":"/applications/1-{id}/allinstances"},{"type":"read index","nodeKey":"/applications/1-{id}/sessions_by_user"},{"type":"read index","nodeKey":"/applications/1-{id}/user_name_lookup"},{"type":"read index","nodeKey":"/applications/1-{id}/all_versions"},{"type":"read index","nodeKey":"/applications/1-{id}/instances/2-{id}/users_on_this_instance"},{"type":"read index","nodeKey":"/applications/1-{id}/versions/3-{id}/instances_for_this_version"},{"type":"read index","nodeKey":"/applications/1-{id}/versions/3-{id}/instances_on_this_version"},{"type":"write templates"},{"type":"create user"},{"type":"set password"},{"type":"create temporary access"},{"type":"enable or disable user"},{"type":"write access levels"},{"type":"list users"},{"type":"list access levels"},{"type":"manage index"},{"type":"manage collection"},{"type":"set user access levels"}]}],"version":0} \ No newline at end of file +{ + "levels": [ + { + "name": "owner", + "permissions": [ + { + "type": "create record", + "nodeKey": "/customers/1-{id}" + }, + { + "type": "delete record", + "nodeKey": "/customers/1-{id}" + }, + { + "type": "update record", + "nodeKey": "/customers/1-{id}" + }, + { + "type": "read record", + "nodeKey": "/customers/1-{id}" + }, + { + "type": "create record", + "nodeKey": "/customers/1-{id}/invoices/2-{id}" + }, + { + "type": "update record", + "nodeKey": "/customers/1-{id}/invoices/2-{id}" + }, + { + "type": "delete record", + "nodeKey": "/customers/1-{id}/invoices/2-{id}" + }, + { + "type": "read record", + "nodeKey": "/customers/1-{id}/invoices/2-{id}" + }, + { + "type": "write templates" + }, + { + "type": "create user" + }, + { + "type": "set password" + }, + { + "type": "create temporary access" + }, + { + "type": "enable or disable user" + }, + { + "type": "write access levels" + }, + { + "type": "list users" + }, + { + "type": "list access levels" + }, + { + "type": "manage index" + }, + { + "type": "set user access levels" + }, + { + "type": "manage collection" + } + ] + } + ], + "version": 0 +} \ No newline at end of file diff --git a/packages/server/appPackages/testApp/appDefinition.json b/packages/server/appPackages/testApp/appDefinition.json index 6747e91e3b..17b992b184 100644 --- a/packages/server/appPackages/testApp/appDefinition.json +++ b/packages/server/appPackages/testApp/appDefinition.json @@ -4,148 +4,36 @@ "type": "root", "children": [ { - "name": "application", + "name": "customer", "type": "record", "fields": [ { "name": "name", "type": "string", "typeOptions": { - "maxLength": 500, + "maxLength": 1000, "values": null, "allowDeclaredValuesOnly": false }, - "label": "Name", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "domain", - "type": "string", - "typeOptions": { - "maxLength": 500, - "values": null, - "allowDeclaredValuesOnly": false - }, - "label": "domain", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "application_resolve_strategy", - "type": "string", - "typeOptions": { - "maxLength": 100, - "values": [ - "domain", - "path" - ], - "allowDeclaredValuesOnly": true - }, - "label": "Resolve Application By", + "label": "name", "getInitialValue": "default", "getUndefinedValue": "default" } ], "children": [ { - "name": "user", + "name": "invoice", "type": "record", "fields": [ { - "name": "unique_name", - "type": "string", + "name": "amount", + "type": "number", "typeOptions": { - "maxLength": 200, - "values": null, - "allowDeclaredValuesOnly": false + "minValue": 99999999999, + "maxValue": 99999999999, + "decimalPlaces": 2 }, - "label": "Name (unique)", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "active", - "type": "bool", - "typeOptions": { - "allowNulls": false - }, - "label": "Is Active", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "instance", - "type": "reference", - "typeOptions": { - "indexNodeKey": "/applications/1-{id}/allinstances", - "reverseIndexNodeKeys": [ - "/applications/1-{id}/instances/2-{id}/users_on_this_instance" - ], - "displayValue": "name" - }, - "label": "Instance", - "getInitialValue": "default", - "getUndefinedValue": "default" - } - ], - "children": [], - "validationRules": [], - "nodeId": 8, - "indexes": [], - "allidsShardFactor": "64", - "collectionName": "users", - "isSingle": false - }, - { - "name": "instance", - "type": "record", - "fields": [ - { - "name": "name", - "type": "string", - "typeOptions": { - "maxLength": 1000, - "values": null, - "allowDeclaredValuesOnly": false - }, - "label": "Name", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "active", - "type": "bool", - "typeOptions": { - "allowNulls": false - }, - "label": "Is Active", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "version", - "type": "reference", - "typeOptions": { - "indexNodeKey": "/applications/1-{id}/all_versions", - "reverseIndexNodeKeys": [ - "/applications/1-{id}/versions/3-{id}/instances_on_this_version" - ], - "displayValue": "name" - }, - "label": "Version", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "datastoreconfig", - "type": "string", - "typeOptions": { - "maxLength": 1000, - "values": null, - "allowDeclaredValuesOnly": false - }, - "label": "Datastore Config", + "label": "amount", "getInitialValue": "default", "getUndefinedValue": "default" } @@ -153,277 +41,22 @@ "children": [], "validationRules": [], "nodeId": 2, - "indexes": [ - { - "name": "users_on_this_instance", - "type": "index", - "map": "return {...record};", - "filter": "", - "indexType": "reference", - "getShardName": "", - "getSortKey": "record.id", - "aggregateGroups": [], - "allowedRecordNodeIds": [], - "nodeId": 15 - } - ], - "allidsShardFactor": 1, - "collectionName": "instances", - "isSingle": false - }, - { - "name": "version", - "type": "record", - "fields": [ - { - "name": "name", - "type": "string", - "typeOptions": { - "maxLength": 200, - "values": null, - "allowDeclaredValuesOnly": false - }, - "label": "Name", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "package", - "type": "file", - "typeOptions": {}, - "label": "Package", - "getInitialValue": "default", - "getUndefinedValue": "default" - } - ], - "children": [], - "validationRules": [], - "nodeId": 3, - "indexes": [ - { - "name": "instances_for_this_version", - "type": "index", - "map": "return {name:record.name};", - "filter": "", - "indexType": "ancestor", - "getShardName": "", - "getSortKey": "record.id", - "aggregateGroups": [], - "allowedRecordNodeIds": [], - "nodeId": 9 - }, - { - "name": "instances_on_this_version", - "type": "index", - "map": "return {...record};", - "filter": "", - "indexType": "reference", - "getShardName": "", - "getSortKey": "record.id", - "aggregateGroups": [], - "allowedRecordNodeIds": [], - "nodeId": 10 - } - ], - "allidsShardFactor": 1, - "collectionName": "versions", - "isSingle": false - }, - { - "name": "session", - "type": "record", - "fields": [ - { - "name": "created", - "type": "number", - "typeOptions": { - "minValue": 0, - "maxValue": 99999999999999, - "decimalPlaces": 0 - }, - "label": "Created", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "user_json", - "type": "string", - "typeOptions": { - "maxLength": null, - "values": null, - "allowDeclaredValuesOnly": false - }, - "label": "User Json", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "instanceDatastoreConfig", - "type": "string", - "typeOptions": { - "maxLength": null, - "values": null, - "allowDeclaredValuesOnly": false - }, - "label": "Instance Datastore Config", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "username", - "type": "string", - "typeOptions": { - "maxLength": null, - "values": null, - "allowDeclaredValuesOnly": false - }, - "label": "User", - "getInitialValue": "default", - "getUndefinedValue": "default" - } - ], - "children": [], - "validationRules": [], - "nodeId": 16, "indexes": [], "allidsShardFactor": 1, - "collectionName": "sessions", + "collectionName": "invoices", "isSingle": false } ], "validationRules": [], "nodeId": 1, - "indexes": [ - { - "name": "allinstances", - "type": "index", - "map": "return {...record};", - "filter": "", - "indexType": "ancestor", - "getShardName": "", - "getSortKey": "record.id", - "aggregateGroups": [], - "allowedRecordNodeIds": [ - 2 - ], - "nodeId": 23 - }, - { - "name": "sessions_by_user", - "type": "index", - "map": "return {username:record.username};", - "filter": "", - "indexType": "ancestor", - "getShardName": "return record.username.substring(0,2)", - "getSortKey": "record.id", - "aggregateGroups": [], - "allowedRecordNodeIds": [ - 16 - ], - "nodeId": 24 - }, - { - "name": "user_name_lookup", - "type": "index", - "map": "return {name:record.unique_name, instanceDatastoreConfig:instance.datastoreconfig};", - "filter": "", - "indexType": "ancestor", - "getShardName": "return record.unique_name.substring(0,2)", - "getSortKey": "record.id", - "aggregateGroups": [], - "allowedRecordNodeIds": [ - 8 - ], - "nodeId": 25 - }, - { - "name": "all_versions", - "type": "index", - "map": "return {...record};", - "filter": "", - "indexType": "ancestor", - "getShardName": "", - "getSortKey": "record.id", - "aggregateGroups": [], - "allowedRecordNodeIds": [ - 3 - ], - "nodeId": 26 - } - ], - "allidsShardFactor": 64, - "collectionName": "applications", - "isSingle": false - }, - { - "name": "mastersession", - "type": "record", - "fields": [ - { - "name": "user_json", - "type": "string", - "typeOptions": { - "maxLength": 10000, - "values": null, - "allowDeclaredValuesOnly": false - }, - "label": "User Json", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "username", - "type": "string", - "typeOptions": { - "maxLength": null, - "values": null, - "allowDeclaredValuesOnly": false - }, - "label": "User", - "getInitialValue": "default", - "getUndefinedValue": "default" - } - ], - "children": [], - "validationRules": [], - "nodeId": 17, "indexes": [], "allidsShardFactor": 64, - "collectionName": "sessions", + "collectionName": "customers", "isSingle": false } ], "pathMaps": [], - "indexes": [ - { - "name": "all_applications", - "type": "index", - "map": "return {...record};", - "filter": "", - "indexType": "ancestor", - "getShardName": "", - "getSortKey": "record.id", - "aggregateGroups": [], - "allowedRecordNodeIds": [ - 1 - ], - "nodeId": 22 - }, - { - "name": "mastersessions_by_user", - "type": "index", - "map": "return {username:record.username};", - "filter": "", - "indexType": "ancestor", - "getShardName": "return record.username.substring(0,2)", - "getSortKey": "record.id", - "aggregateGroups": [], - "allowedRecordNodeIds": [ - 17 - ], - "nodeId": 27 - } - ], + "indexes": [], "nodeId": 0 }, "actions": { @@ -439,7 +72,7 @@ "actionName": "output_to_file", "eventName": "authApi:createUser:onComplete", "optionsCreator": "return { filename:'tempaccess' + context.user.name, content:context.result.tempCode };", - "condition": "" + "condition": "!context.password" } ] } \ No newline at end of file diff --git a/packages/server/appPackages/testApp/dist/package.tar.gz b/packages/server/appPackages/testApp/dist/package.tar.gz index bd99d0227d..ee34a92d9f 100644 Binary files a/packages/server/appPackages/testApp/dist/package.tar.gz and b/packages/server/appPackages/testApp/dist/package.tar.gz differ diff --git a/packages/server/initialise/createInstanceDb.js b/packages/server/initialise/createInstanceDb.js index d0996c0a7e..e5273d0f3f 100644 --- a/packages/server/initialise/createInstanceDb.js +++ b/packages/server/initialise/createInstanceDb.js @@ -1,28 +1,44 @@ const { initialiseData, - setupDatastore + setupDatastore, + common } = require("budibase-core"); const constructHierarchy = require("../utilities/constructHierarchy"); const getDatabaseManager = require("../utilities/databaseManager"); const masterDbAppDefinition = require("../appPackages/master/appDefinition.json"); +const { applictionVersionPackage } = require("../utilities/createAppPackage"); +const { last } = require("lodash/fp"); +const {$,splitKey} = common; -module.exports = async (datastoreModule, rootDatastoreConfig, appId, instanceId) => { + +module.exports = async (config, datastoreModule, rootDatastoreConfig, app, instance) => { try { const databaseManager = getDatabaseManager( datastoreModule, rootDatastoreConfig); await databaseManager.createEmptyInstanceDb( - appId, instanceId); + app.id, instance.id); const dbConfig = databaseManager.getInstanceDatastoreConfig( - appId, instanceId); + app.id, instance.id); const datastore = setupDatastore( - datastoreModule.getDatastore(dbConfig)); + datastoreModule.getDatastore(dbConfig)); + + const versionId = $(instance.version.key, [ + splitKey, + last + ]); - await initialiseData(datastore, - constructHierarchy(masterDbAppDefinition)); + const appPackage = applictionVersionPackage( + config, app.name, versionId + ); + + await initialiseData( + datastore, + appPackage.appDefinition, + appPackage.accessLevels); return dbConfig; diff --git a/packages/server/middleware/routers.js b/packages/server/middleware/routers.js index 8498203dc2..5f75b69052 100644 --- a/packages/server/middleware/routers.js +++ b/packages/server/middleware/routers.js @@ -42,6 +42,11 @@ module.exports = (config, app) => { ctx.request.body.username ); + if(!instanceApi) { + ctx.request.status = StatusCodes.OK; + return; + } + await instanceApi.authApi.setPasswordFromTemporaryCode( ctx.request.body.tempCode, ctx.request.body.newPassword); @@ -54,6 +59,11 @@ module.exports = (config, app) => { ctx.request.body.username ); + if(!instanceApi) { + ctx.request.status = StatusCodes.OK; + return; + } + await instanceApi.authApi.createTemporaryAccess( ctx.request.body.username); diff --git a/packages/server/runtime_apps/testApp/3-7HHy_x966Th/access_levels.json b/packages/server/runtime_apps/testApp/3-7HHy_x966Th/access_levels.json deleted file mode 100644 index ebb7ca11a1..0000000000 --- a/packages/server/runtime_apps/testApp/3-7HHy_x966Th/access_levels.json +++ /dev/null @@ -1 +0,0 @@ -{"levels":[{"name":"owner","permissions":[{"type":"create record","nodeKey":"/applications/1-{id}"},{"type":"update record","nodeKey":"/applications/1-{id}"},{"type":"delete record","nodeKey":"/applications/1-{id}"},{"type":"read record","nodeKey":"/applications/1-{id}"},{"type":"create record","nodeKey":"/applications/1-{id}/users/8-{id}"},{"type":"update record","nodeKey":"/applications/1-{id}/users/8-{id}"},{"type":"delete record","nodeKey":"/applications/1-{id}/users/8-{id}"},{"type":"read record","nodeKey":"/applications/1-{id}/users/8-{id}"},{"type":"create record","nodeKey":"/applications/1-{id}/instances/2-{id}"},{"type":"update record","nodeKey":"/applications/1-{id}/instances/2-{id}"},{"type":"delete record","nodeKey":"/applications/1-{id}/instances/2-{id}"},{"type":"read record","nodeKey":"/applications/1-{id}/instances/2-{id}"},{"type":"create record","nodeKey":"/applications/1-{id}/versions/3-{id}"},{"type":"update record","nodeKey":"/applications/1-{id}/versions/3-{id}"},{"type":"delete record","nodeKey":"/applications/1-{id}/versions/3-{id}"},{"type":"read record","nodeKey":"/applications/1-{id}/versions/3-{id}"},{"type":"create record","nodeKey":"/applications/1-{id}/sessions/16-{id}"},{"type":"update record","nodeKey":"/applications/1-{id}/sessions/16-{id}"},{"type":"delete record","nodeKey":"/applications/1-{id}/sessions/16-{id}"},{"type":"read record","nodeKey":"/applications/1-{id}/sessions/16-{id}"},{"type":"create record","nodeKey":"/sessions/17-{id}"},{"type":"update record","nodeKey":"/sessions/17-{id}"},{"type":"delete record","nodeKey":"/sessions/17-{id}"},{"type":"read record","nodeKey":"/sessions/17-{id}"},{"type":"read index","nodeKey":"/mastersessions_by_user"},{"type":"read index","nodeKey":"/all_applications"},{"type":"read index","nodeKey":"/applications/1-{id}/allinstances"},{"type":"read index","nodeKey":"/applications/1-{id}/sessions_by_user"},{"type":"read index","nodeKey":"/applications/1-{id}/user_name_lookup"},{"type":"read index","nodeKey":"/applications/1-{id}/all_versions"},{"type":"read index","nodeKey":"/applications/1-{id}/instances/2-{id}/users_on_this_instance"},{"type":"read index","nodeKey":"/applications/1-{id}/versions/3-{id}/instances_for_this_version"},{"type":"read index","nodeKey":"/applications/1-{id}/versions/3-{id}/instances_on_this_version"},{"type":"write templates"},{"type":"create user"},{"type":"set password"},{"type":"create temporary access"},{"type":"enable or disable user"},{"type":"write access levels"},{"type":"list users"},{"type":"list access levels"},{"type":"manage index"},{"type":"manage collection"},{"type":"set user access levels"}]}],"version":0} \ No newline at end of file diff --git a/packages/server/runtime_apps/testApp/3-7HHy_x966Th/appDefinition.json b/packages/server/runtime_apps/testApp/3-7HHy_x966Th/appDefinition.json deleted file mode 100644 index 6747e91e3b..0000000000 --- a/packages/server/runtime_apps/testApp/3-7HHy_x966Th/appDefinition.json +++ /dev/null @@ -1,445 +0,0 @@ -{ - "hierarchy": { - "name": "root", - "type": "root", - "children": [ - { - "name": "application", - "type": "record", - "fields": [ - { - "name": "name", - "type": "string", - "typeOptions": { - "maxLength": 500, - "values": null, - "allowDeclaredValuesOnly": false - }, - "label": "Name", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "domain", - "type": "string", - "typeOptions": { - "maxLength": 500, - "values": null, - "allowDeclaredValuesOnly": false - }, - "label": "domain", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "application_resolve_strategy", - "type": "string", - "typeOptions": { - "maxLength": 100, - "values": [ - "domain", - "path" - ], - "allowDeclaredValuesOnly": true - }, - "label": "Resolve Application By", - "getInitialValue": "default", - "getUndefinedValue": "default" - } - ], - "children": [ - { - "name": "user", - "type": "record", - "fields": [ - { - "name": "unique_name", - "type": "string", - "typeOptions": { - "maxLength": 200, - "values": null, - "allowDeclaredValuesOnly": false - }, - "label": "Name (unique)", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "active", - "type": "bool", - "typeOptions": { - "allowNulls": false - }, - "label": "Is Active", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "instance", - "type": "reference", - "typeOptions": { - "indexNodeKey": "/applications/1-{id}/allinstances", - "reverseIndexNodeKeys": [ - "/applications/1-{id}/instances/2-{id}/users_on_this_instance" - ], - "displayValue": "name" - }, - "label": "Instance", - "getInitialValue": "default", - "getUndefinedValue": "default" - } - ], - "children": [], - "validationRules": [], - "nodeId": 8, - "indexes": [], - "allidsShardFactor": "64", - "collectionName": "users", - "isSingle": false - }, - { - "name": "instance", - "type": "record", - "fields": [ - { - "name": "name", - "type": "string", - "typeOptions": { - "maxLength": 1000, - "values": null, - "allowDeclaredValuesOnly": false - }, - "label": "Name", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "active", - "type": "bool", - "typeOptions": { - "allowNulls": false - }, - "label": "Is Active", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "version", - "type": "reference", - "typeOptions": { - "indexNodeKey": "/applications/1-{id}/all_versions", - "reverseIndexNodeKeys": [ - "/applications/1-{id}/versions/3-{id}/instances_on_this_version" - ], - "displayValue": "name" - }, - "label": "Version", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "datastoreconfig", - "type": "string", - "typeOptions": { - "maxLength": 1000, - "values": null, - "allowDeclaredValuesOnly": false - }, - "label": "Datastore Config", - "getInitialValue": "default", - "getUndefinedValue": "default" - } - ], - "children": [], - "validationRules": [], - "nodeId": 2, - "indexes": [ - { - "name": "users_on_this_instance", - "type": "index", - "map": "return {...record};", - "filter": "", - "indexType": "reference", - "getShardName": "", - "getSortKey": "record.id", - "aggregateGroups": [], - "allowedRecordNodeIds": [], - "nodeId": 15 - } - ], - "allidsShardFactor": 1, - "collectionName": "instances", - "isSingle": false - }, - { - "name": "version", - "type": "record", - "fields": [ - { - "name": "name", - "type": "string", - "typeOptions": { - "maxLength": 200, - "values": null, - "allowDeclaredValuesOnly": false - }, - "label": "Name", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "package", - "type": "file", - "typeOptions": {}, - "label": "Package", - "getInitialValue": "default", - "getUndefinedValue": "default" - } - ], - "children": [], - "validationRules": [], - "nodeId": 3, - "indexes": [ - { - "name": "instances_for_this_version", - "type": "index", - "map": "return {name:record.name};", - "filter": "", - "indexType": "ancestor", - "getShardName": "", - "getSortKey": "record.id", - "aggregateGroups": [], - "allowedRecordNodeIds": [], - "nodeId": 9 - }, - { - "name": "instances_on_this_version", - "type": "index", - "map": "return {...record};", - "filter": "", - "indexType": "reference", - "getShardName": "", - "getSortKey": "record.id", - "aggregateGroups": [], - "allowedRecordNodeIds": [], - "nodeId": 10 - } - ], - "allidsShardFactor": 1, - "collectionName": "versions", - "isSingle": false - }, - { - "name": "session", - "type": "record", - "fields": [ - { - "name": "created", - "type": "number", - "typeOptions": { - "minValue": 0, - "maxValue": 99999999999999, - "decimalPlaces": 0 - }, - "label": "Created", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "user_json", - "type": "string", - "typeOptions": { - "maxLength": null, - "values": null, - "allowDeclaredValuesOnly": false - }, - "label": "User Json", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "instanceDatastoreConfig", - "type": "string", - "typeOptions": { - "maxLength": null, - "values": null, - "allowDeclaredValuesOnly": false - }, - "label": "Instance Datastore Config", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "username", - "type": "string", - "typeOptions": { - "maxLength": null, - "values": null, - "allowDeclaredValuesOnly": false - }, - "label": "User", - "getInitialValue": "default", - "getUndefinedValue": "default" - } - ], - "children": [], - "validationRules": [], - "nodeId": 16, - "indexes": [], - "allidsShardFactor": 1, - "collectionName": "sessions", - "isSingle": false - } - ], - "validationRules": [], - "nodeId": 1, - "indexes": [ - { - "name": "allinstances", - "type": "index", - "map": "return {...record};", - "filter": "", - "indexType": "ancestor", - "getShardName": "", - "getSortKey": "record.id", - "aggregateGroups": [], - "allowedRecordNodeIds": [ - 2 - ], - "nodeId": 23 - }, - { - "name": "sessions_by_user", - "type": "index", - "map": "return {username:record.username};", - "filter": "", - "indexType": "ancestor", - "getShardName": "return record.username.substring(0,2)", - "getSortKey": "record.id", - "aggregateGroups": [], - "allowedRecordNodeIds": [ - 16 - ], - "nodeId": 24 - }, - { - "name": "user_name_lookup", - "type": "index", - "map": "return {name:record.unique_name, instanceDatastoreConfig:instance.datastoreconfig};", - "filter": "", - "indexType": "ancestor", - "getShardName": "return record.unique_name.substring(0,2)", - "getSortKey": "record.id", - "aggregateGroups": [], - "allowedRecordNodeIds": [ - 8 - ], - "nodeId": 25 - }, - { - "name": "all_versions", - "type": "index", - "map": "return {...record};", - "filter": "", - "indexType": "ancestor", - "getShardName": "", - "getSortKey": "record.id", - "aggregateGroups": [], - "allowedRecordNodeIds": [ - 3 - ], - "nodeId": 26 - } - ], - "allidsShardFactor": 64, - "collectionName": "applications", - "isSingle": false - }, - { - "name": "mastersession", - "type": "record", - "fields": [ - { - "name": "user_json", - "type": "string", - "typeOptions": { - "maxLength": 10000, - "values": null, - "allowDeclaredValuesOnly": false - }, - "label": "User Json", - "getInitialValue": "default", - "getUndefinedValue": "default" - }, - { - "name": "username", - "type": "string", - "typeOptions": { - "maxLength": null, - "values": null, - "allowDeclaredValuesOnly": false - }, - "label": "User", - "getInitialValue": "default", - "getUndefinedValue": "default" - } - ], - "children": [], - "validationRules": [], - "nodeId": 17, - "indexes": [], - "allidsShardFactor": 64, - "collectionName": "sessions", - "isSingle": false - } - ], - "pathMaps": [], - "indexes": [ - { - "name": "all_applications", - "type": "index", - "map": "return {...record};", - "filter": "", - "indexType": "ancestor", - "getShardName": "", - "getSortKey": "record.id", - "aggregateGroups": [], - "allowedRecordNodeIds": [ - 1 - ], - "nodeId": 22 - }, - { - "name": "mastersessions_by_user", - "type": "index", - "map": "return {username:record.username};", - "filter": "", - "indexType": "ancestor", - "getShardName": "return record.username.substring(0,2)", - "getSortKey": "record.id", - "aggregateGroups": [], - "allowedRecordNodeIds": [ - 17 - ], - "nodeId": 27 - } - ], - "nodeId": 0 - }, - "actions": { - "output_to_file": { - "name": "output_to_file", - "behaviourSource": "main", - "behaviourName": "outputToFile", - "initialOptions": {} - } - }, - "triggers": [ - { - "actionName": "output_to_file", - "eventName": "authApi:createUser:onComplete", - "optionsCreator": "return { filename:'tempaccess' + context.user.name, content:context.result.tempCode };", - "condition": "" - } - ] -} \ No newline at end of file diff --git a/packages/server/runtime_apps/testApp/3-Y5vsgF-ZEjh/access_levels.json b/packages/server/runtime_apps/testApp/3-Y5vsgF-ZEjh/access_levels.json new file mode 100644 index 0000000000..4e01e4e9a7 --- /dev/null +++ b/packages/server/runtime_apps/testApp/3-Y5vsgF-ZEjh/access_levels.json @@ -0,0 +1,75 @@ +{ + "levels": [ + { + "name": "owner", + "permissions": [ + { + "type": "create record", + "nodeKey": "/customers/1-{id}" + }, + { + "type": "delete record", + "nodeKey": "/customers/1-{id}" + }, + { + "type": "update record", + "nodeKey": "/customers/1-{id}" + }, + { + "type": "read record", + "nodeKey": "/customers/1-{id}" + }, + { + "type": "create record", + "nodeKey": "/customers/1-{id}/invoices/2-{id}" + }, + { + "type": "update record", + "nodeKey": "/customers/1-{id}/invoices/2-{id}" + }, + { + "type": "delete record", + "nodeKey": "/customers/1-{id}/invoices/2-{id}" + }, + { + "type": "read record", + "nodeKey": "/customers/1-{id}/invoices/2-{id}" + }, + { + "type": "write templates" + }, + { + "type": "create user" + }, + { + "type": "set password" + }, + { + "type": "create temporary access" + }, + { + "type": "enable or disable user" + }, + { + "type": "write access levels" + }, + { + "type": "list users" + }, + { + "type": "list access levels" + }, + { + "type": "manage index" + }, + { + "type": "set user access levels" + }, + { + "type": "manage collection" + } + ] + } + ], + "version": 0 +} \ No newline at end of file diff --git a/packages/server/runtime_apps/testApp/3-Y5vsgF-ZEjh/appDefinition.json b/packages/server/runtime_apps/testApp/3-Y5vsgF-ZEjh/appDefinition.json new file mode 100644 index 0000000000..17b992b184 --- /dev/null +++ b/packages/server/runtime_apps/testApp/3-Y5vsgF-ZEjh/appDefinition.json @@ -0,0 +1,78 @@ +{ + "hierarchy": { + "name": "root", + "type": "root", + "children": [ + { + "name": "customer", + "type": "record", + "fields": [ + { + "name": "name", + "type": "string", + "typeOptions": { + "maxLength": 1000, + "values": null, + "allowDeclaredValuesOnly": false + }, + "label": "name", + "getInitialValue": "default", + "getUndefinedValue": "default" + } + ], + "children": [ + { + "name": "invoice", + "type": "record", + "fields": [ + { + "name": "amount", + "type": "number", + "typeOptions": { + "minValue": 99999999999, + "maxValue": 99999999999, + "decimalPlaces": 2 + }, + "label": "amount", + "getInitialValue": "default", + "getUndefinedValue": "default" + } + ], + "children": [], + "validationRules": [], + "nodeId": 2, + "indexes": [], + "allidsShardFactor": 1, + "collectionName": "invoices", + "isSingle": false + } + ], + "validationRules": [], + "nodeId": 1, + "indexes": [], + "allidsShardFactor": 64, + "collectionName": "customers", + "isSingle": false + } + ], + "pathMaps": [], + "indexes": [], + "nodeId": 0 + }, + "actions": { + "output_to_file": { + "name": "output_to_file", + "behaviourSource": "main", + "behaviourName": "outputToFile", + "initialOptions": {} + } + }, + "triggers": [ + { + "actionName": "output_to_file", + "eventName": "authApi:createUser:onComplete", + "optionsCreator": "return { filename:'tempaccess' + context.user.name, content:context.result.tempCode };", + "condition": "!context.password" + } + ] +} \ No newline at end of file diff --git a/packages/server/runtime_apps/testApp/3-7HHy_x966Th/plugins.js b/packages/server/runtime_apps/testApp/3-Y5vsgF-ZEjh/plugins.js similarity index 100% rename from packages/server/runtime_apps/testApp/3-7HHy_x966Th/plugins.js rename to packages/server/runtime_apps/testApp/3-Y5vsgF-ZEjh/plugins.js diff --git a/packages/server/tests/all.spec.js b/packages/server/tests/all.spec.js index bdee953965..63968a9227 100644 --- a/packages/server/tests/all.spec.js +++ b/packages/server/tests/all.spec.js @@ -6,7 +6,8 @@ beforeAll(async () => await app.start()) afterAll(async () => await app.destroy()) -describe("authenticateMaster", () => authenticateMaster(app)); +describe("authenticateMaster", () => authenticateMaster(app, "_master", () => app.masterAuth)); describe("createNewApp", () => createNewApp(app)); +describe("authenticateTestApp", () => authenticateMaster(app, "testApp", () => app.user1_instance1)); diff --git a/packages/server/tests/authenticate.js b/packages/server/tests/authenticate.js index 9937de9e16..d5bf37d75f 100644 --- a/packages/server/tests/authenticate.js +++ b/packages/server/tests/authenticate.js @@ -2,27 +2,29 @@ const statusCodes = require("../utilities/statusCodes"); const { readFile } = require("../utilities/fsawait"); const { timeout } = require("./helpers"); -module.exports = (app) => { +module.exports = (app, appName) => { + + const credentials = app.credentials[appName]; it("should return unauthorized if username is incorrect", async () => { - await app.post("/_master/api/authenticate", { + await app.post(`/${appName}/api/authenticate`, { username: "unknownuser", - password: app.masterAuth.password + password: credentials.password }) .expect(statusCodes.UNAUTHORIZED); }) it("should return unauthorized if password is incorrect", async () => { - await app.post("/_master/api/authenticate", { - username: app.masterAuth.username, + await app.post(`/${appName}/api/authenticate`, { + username: credentials.username, password: "incorrect_password" }) .expect(statusCodes.UNAUTHORIZED); }) it("should not get cookie when unauthorized", async () => { - const response = await app.post("/_master/api/authenticate", { - username: app.masterAuth.username, + const response = await app.post(`/${appName}/api/authenticate`, { + username: credentials.username, password: "incorrect_password" }); @@ -32,20 +34,20 @@ module.exports = (app) => { it("should return ok correct username and password supplied", async () => { - const response = await app.post("/_master/api/authenticate", { - username: app.masterAuth.username, - password: app.masterAuth.password + const response = await app.post(`/${appName}/api/authenticate`, { + username: credentials.username, + password: credentials.password }) .expect(statusCodes.OK); - app.masterAuth.cookie = response.header['set-cookie']; + credentials.cookie = response.header['set-cookie']; }); - const testUserName = "test_user"; + const testUserName = appName + "_test_user"; let testPassword = "test_user_password"; it("should be able to create new user with authenticated cookie", async () => { - await app.post("/_master/api/createUser", { + await app.post(`/${appName}/api/createUser`, { user: { name: testUserName, accessLevels:["owner"], @@ -54,7 +56,7 @@ module.exports = (app) => { }, password: testPassword }) - .set("cookie", app.masterAuth.cookie) + .set("cookie", credentials.cookie) .expect(statusCodes.OK); @@ -63,7 +65,7 @@ module.exports = (app) => { let newUserCookie; it("should be able to authenticate with new user", async () => { - const responseNewUser = await app.post("/_master/api/authenticate", { + const responseNewUser = await app.post(`/${appName}/api/authenticate`, { username: testUserName, password: testPassword }) @@ -72,7 +74,7 @@ module.exports = (app) => { newUserCookie = responseNewUser.header['set-cookie']; expect(newUserCookie).toBeDefined(); - expect(newUserCookie).not.toEqual(app.masterAuth.cookie); + expect(newUserCookie).not.toEqual(credentials.cookie); app.get("/_master/api/users/") .set("cookie", newUserCookie) @@ -81,17 +83,18 @@ module.exports = (app) => { it("should not be able to perform requests when user is disabled", async () => { - await app.post("/_master/api/disableUser", { + //HERE + await app.post(`/${appName}/api/disableUser`, { username: testUserName }) - .set("cookie", app.masterAuth.cookie) + .set("cookie", credentials.cookie) .expect(statusCodes.OK); - await app.get("/_master/api/users/") + await app.get(`/${appName}/api/users`) .set("cookie", newUserCookie) .expect(statusCodes.UNAUTHORIZED); - await app.post("/_master/api/authenticate", { + await app.post(`/${appName}/api/authenticate`, { username: testUserName, password: testPassword }) @@ -100,7 +103,7 @@ module.exports = (app) => { }); it("should not be able to re-authenticate when user is disabled", async () => { - await app.post("/_master/api/authenticate", { + await app.post(`/${appName}/api/authenticate`, { username: testUserName, password: testPassword }) @@ -109,13 +112,13 @@ module.exports = (app) => { it("should be able with re-authenticate when user is enabled again", async () => { - await app.post("/_master/api/enableUser", { + await app.post(`/${appName}/api/enableUser`, { username: testUserName }) - .set("cookie", app.masterAuth.cookie) + .set("cookie", credentials.cookie) .expect(statusCodes.OK); - await app.post("/_master/api/authenticate", { + await app.post(`/${appName}/api/authenticate`, { username: testUserName, password: testPassword }) @@ -125,7 +128,7 @@ module.exports = (app) => { let testUserTempCode; it("should be able to reset password with temporary access", async () => { - await app.post("/_master/api/createTemporaryAccess", { + await app.post(`/${appName}/api/createTemporaryAccess`, { username: testUserName }) .expect(statusCodes.OK); @@ -138,14 +141,14 @@ module.exports = (app) => { const testUserTempCode = await readFile(`./tests/.data/tempaccess${testUserName}`, "utf8"); - await app.post("/_master/api/setPasswordFromTemporaryCode", { + await app.post(`/${appName}/api/setPasswordFromTemporaryCode`, { username: testUserName, tempCode:testUserTempCode, newPassword:testPassword }) .expect(statusCodes.OK); - await app.post("/_master/api/authenticate", { + await app.post(`/${appName}/api/authenticate`, { username: testUserName, password: testPassword }) @@ -157,20 +160,20 @@ module.exports = (app) => { it("should not be able to set password with used temp code", async () => { - await app.post("/_master/api/setPasswordFromTemporaryCode", { + await app.post(`/${appName}/api/setPasswordFromTemporaryCode`, { username: testUserName, tempCode:testUserTempCode, newPassword:"whatever" }) .expect(statusCodes.OK); - await app.post("/_master/api/authenticate", { + await app.post(`/${appName}/api/authenticate`, { username: testUserName, password: "whatever" }) .expect(statusCodes.UNAUTHORIZED); - await app.post("/_master/api/authenticate", { + await app.post(`/${appName}/api/authenticate`, { username: testUserName, password: testPassword }) diff --git a/packages/server/tests/createNewApp.js b/packages/server/tests/createNewApp.js index e29630127e..18dad0cafc 100644 --- a/packages/server/tests/createNewApp.js +++ b/packages/server/tests/createNewApp.js @@ -25,11 +25,11 @@ module.exports = (app) => { newAppKey = newApp.key; await app.post(`/_master/api/record/${newApp.key}`, newApp) - .set("cookie", app.masterAuth.cookie) + .set("cookie", app.credentials._master.cookie) .expect(statusCodes.OK); const response = await app.get(`/_master/api/record/${newApp.key}`) - .set("cookie", app.masterAuth.cookie) + .set("cookie", app.credentials._master.cookie) .expect(statusCodes.OK); expect(response.body.name).toBe(newApp.name); @@ -49,12 +49,12 @@ module.exports = (app) => { version1.package = { relativePath: "package.tar.gz", size}; await app.post(`/_master/api/record/${version1.key}`, version1) - .set("cookie", app.masterAuth.cookie) + .set("cookie", app.credentials._master.cookie) .expect(statusCodes.OK); await app.post(`/_master/api/files/${version1.key}`) .attach("file", path, "package.tar.gz") - .set("cookie", app.masterAuth.cookie) + .set("cookie", app.credentials._master.cookie) .expect(statusCodes.OK); }); @@ -69,11 +69,11 @@ module.exports = (app) => { instance1.version = {key:version1Key, name:"v1", defaultAccessLevel:"owner"}; await app.post(`/_master/api/record/${instance1.key}`, instance1) - .set("cookie", app.masterAuth.cookie) + .set("cookie", app.credentials._master.cookie) .expect(statusCodes.OK); const loadInstanceResponse = await app.get(`/_master/api/record/${instance1.key}`) - .set("cookie", app.masterAuth.cookie) + .set("cookie", app.credentials._master.cookie) .expect(statusCodes.OK); instance1 = loadInstanceResponse.body; @@ -85,36 +85,37 @@ module.exports = (app) => { const master = await getmaster(); user1_instance1 = master.recordApi .getNew(`${newAppKey}/users`, "user"); - user1_instance1.name = "testAppUser1"; + user1_instance1.name = app.credentials.testApp.username; /*const lookupResponse = await app.get(`/_master/api/lookup_field/${user1_instance1.key}?fields=instance`) - .set("cookie", app.masterAuth.cookie) + .set("cookie", app.credentials._master.cookie) .expect(statusCodes.OK); */ user1_instance1.instance = instance1; user1_instance1.active = true; //await timeout(100); await app.post(`/_master/api/record/${user1_instance1.key}`, user1_instance1) - .set("cookie", app.masterAuth.cookie) + .set("cookie", app.credentials._master.cookie) .expect(statusCodes.OK); }); it("should be able to set password for new user using temporary code", async () => { const testUserTempCode = await readFile(`./tests/.data/tempaccess${user1_instance1.name}`, "utf8"); - user1_instance1.password = "user1_instance1_password"; + user1_instance1.password = app.credentials.testApp.password; await app.post("/testApp/api/setPasswordFromTemporaryCode", { - username: user1_instance1.name, + username: app.credentials.testApp.username, tempCode:testUserTempCode, - newPassword:user1_instance1.password + newPassword:app.credentials.testApp.password }) .expect(statusCodes.OK); await app.post("/testApp/api/authenticate", { - username: user1_instance1.name, - password: user1_instance1.password + username: app.credentials.testApp.username, + password: app.credentials.testApp.password }) .expect(statusCodes.OK); + }) } \ No newline at end of file diff --git a/packages/server/tests/testApp.js b/packages/server/tests/testApp.js index 8fffaba32d..c221b2b174 100644 --- a/packages/server/tests/testApp.js +++ b/packages/server/tests/testApp.js @@ -68,11 +68,19 @@ module.exports = () => { server:() => server, post: (url, body) => postRequest(server,url,body), get: (url) => getRequest(server, url), - masterAuth: { - username: masterOwnerName, - password: masterOwnerPassword, - cookie: "" + credentials: { + _master: { + username: masterOwnerName, + password: masterOwnerPassword, + cookie: "" + }, + testApp: { + username: "testAppUser1", + password: "user1_instance1_password", + cookie: "" + } }, + testAppInfo: { name: "testApp" }, diff --git a/packages/server/utilities/createAppPackage.js b/packages/server/utilities/createAppPackage.js index bb0a5c3041..1582647b7e 100644 --- a/packages/server/utilities/createAppPackage.js +++ b/packages/server/utilities/createAppPackage.js @@ -9,11 +9,16 @@ const createAppPackage = (config, appPath) => { const pluginsModule = require( join(appPath, "plugins.js")); + + const accessLevels = require( + join(appPath, "access_levels.json") + ); return ({ appDefinition: appDefModule, behaviourSources: pluginsModule(config), - appPath + appPath, + accessLevels }) } diff --git a/packages/server/utilities/masterAppInternal.js b/packages/server/utilities/masterAppInternal.js index 74e3e42b42..e9d7e67c01 100644 --- a/packages/server/utilities/masterAppInternal.js +++ b/packages/server/utilities/masterAppInternal.js @@ -89,6 +89,7 @@ module.exports = async (config) => { const app = await getApplication(appname); const userInMaster = await getUser(bb, app.id, username); + if(!userInMaster) return null; const instance = await bb.recordApi.load( userInMaster.instanceKey); @@ -115,6 +116,7 @@ module.exports = async (config) => { session.user_json = JSON.stringify(authUser); session.instanceDatastoreConfig = instance.datastoreconfig; session.username = username; + session.instanceVersion = instance.version.key; await bb.recordApi.save(session); return session; }; @@ -140,9 +142,14 @@ module.exports = async (config) => { const session = await bb.recordApi.load(`/applications/${app.id}/sessions/${customId}`); const dsConfig = JSON.parse(session.instanceDatastoreConfig); const instanceDatastore = getInstanceDatastore(dsConfig) + const versionId = $(session.instanceVersion,[ + splitKey, + last + ]); + return await getApisForSession( instanceDatastore, - applictionVersionPackage(config, appname, session.instanceVersion), + applictionVersionPackage(config, appname, versionId), session); } catch(_) { return null; @@ -172,6 +179,8 @@ module.exports = async (config) => { const app = await getApplication(appname); const user = await getUser(bb, app.id, username); + if(!user) return null; + const dsConfig = JSON.parse(user.instanceDatastoreConfig); const instanceDatastore = getInstanceDatastore( dsConfig