diff --git a/packages/build/src/build/buildAuth/buildAuth.js b/packages/build/src/build/buildAuth/buildAuth.js index 75c8b3870..856a57cdb 100644 --- a/packages/build/src/build/buildAuth/buildAuth.js +++ b/packages/build/src/build/buildAuth/buildAuth.js @@ -16,43 +16,14 @@ limitations under the License. */ -import { type } from '@lowdefy/helpers'; -import getPageRoles from './getPageRoles.js'; -import getProtectedPages from './getProtectedPages.js'; +import buildAuthPlugins from './buildAuthPlugins.js'; +import buildPageAuth from './buildPageAuth.js'; import validateAuthConfig from './validateAuthConfig.js'; -function buildAuth({ components }) { +function buildAuth({ components, context }) { validateAuthConfig({ components }); - const protectedPages = getProtectedPages({ components }); - const pageRoles = getPageRoles({ components }); - let configPublicPages = []; - if (type.isArray(components.auth.pages.public)) { - configPublicPages = components.auth.pages.public; - } - - (components.pages || []).forEach((page) => { - if (pageRoles[page.id]) { - if (configPublicPages.includes(page.id)) { - throw new Error( - `Page "${page.id}" is both protected by roles ${JSON.stringify( - pageRoles[page.id] - )} and public.` - ); - } - page.auth = { - public: false, - roles: pageRoles[page.id], - }; - } else if (protectedPages.includes(page.id)) { - page.auth = { - public: false, - }; - } else { - page.auth = { - public: true, - }; - } - }); + buildPageAuth({ components }); + buildAuthPlugins({ components, context }); return components; } diff --git a/packages/build/src/build/buildAuth/buildAuthPlugins.js b/packages/build/src/build/buildAuth/buildAuthPlugins.js new file mode 100644 index 000000000..6f6285b73 --- /dev/null +++ b/packages/build/src/build/buildAuth/buildAuthPlugins.js @@ -0,0 +1,41 @@ +/* + Copyright 2020-2022 Lowdefy, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +import { type } from '@lowdefy/helpers'; + +function buildAuthPlugins({ components, context }) { + if (type.isArray(components.auth.providers)) { + components.auth.providers.forEach((provider) => { + if (type.isUndefined(provider.id)) { + throw new Error(`Connection id missing.`); + } + if (!type.isString(provider.id)) { + throw new Error( + `Auth provider id is not a string. Received ${JSON.stringify(provider.id)}.` + ); + } + if (!type.isString(provider.type)) { + throw new Error( + `Auth provider type is not a string at provider "${ + provider.id + }". Received ${JSON.stringify(provider.type)}.` + ); + } + context.typeCounters.auth.providers.increment(provider.type); + }); + } +} + +export default buildAuthPlugins; diff --git a/packages/build/src/build/buildAuth/buildPageAuth.js b/packages/build/src/build/buildAuth/buildPageAuth.js new file mode 100644 index 000000000..287440dca --- /dev/null +++ b/packages/build/src/build/buildAuth/buildPageAuth.js @@ -0,0 +1,58 @@ +/* eslint-disable no-param-reassign */ + +/* + Copyright 2020-2022 Lowdefy, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import { type } from '@lowdefy/helpers'; +import getPageRoles from './getPageRoles.js'; +import getProtectedPages from './getProtectedPages.js'; + +function buildPageAuth({ components }) { + const protectedPages = getProtectedPages({ components }); + const pageRoles = getPageRoles({ components }); + let configPublicPages = []; + if (type.isArray(components.auth.pages.public)) { + configPublicPages = components.auth.pages.public; + } + + (components.pages || []).forEach((page) => { + if (pageRoles[page.id]) { + if (configPublicPages.includes(page.id)) { + throw new Error( + `Page "${page.id}" is both protected by roles ${JSON.stringify( + pageRoles[page.id] + )} and public.` + ); + } + page.auth = { + public: false, + roles: pageRoles[page.id], + }; + } else if (protectedPages.includes(page.id)) { + page.auth = { + public: false, + }; + } else { + page.auth = { + public: true, + }; + } + }); + + return components; +} + +export default buildPageAuth; diff --git a/packages/build/src/build/buildAuth/validateAuthConfig.js b/packages/build/src/build/buildAuth/validateAuthConfig.js index a52bb3ea6..6c91b46e0 100644 --- a/packages/build/src/build/buildAuth/validateAuthConfig.js +++ b/packages/build/src/build/buildAuth/validateAuthConfig.js @@ -33,10 +33,17 @@ async function validateAuthConfig({ components }) { if (type.isNone(components.auth.pages.roles)) { components.auth.pages.roles = {}; } - validate({ + + const { valid } = validate({ schema: lowdefySchema.definitions.authConfig, data: components.auth, + returnErrors: true, }); + + if (!valid) { + throw new Error('lowdefy.auth does not match schema.'); // TODO: Better error message + } + if ( (components.auth.pages.protected === true && components.auth.pages.public === true) || (type.isArray(components.auth.pages.protected) && type.isArray(components.auth.pages.public)) diff --git a/packages/build/src/build/buildTypes.js b/packages/build/src/build/buildTypes.js index 8e39e0886..e648a5628 100644 --- a/packages/build/src/build/buildTypes.js +++ b/packages/build/src/build/buildTypes.js @@ -52,6 +52,9 @@ function buildTypes({ components, context }) { components.types = { actions: {}, + auth: { + providers: {}, + }, blocks: {}, connections: {}, requests: {}, @@ -68,6 +71,13 @@ function buildTypes({ components, context }) { typeClass: 'Action', }); + buildTypeClass(context, { + counter: typeCounters.auth.providers, + definitions: context.typesMap.auth.providers, + store: components.types.auth.providers, + typeClass: 'Auth provider', + }); + buildTypeClass(context, { counter: typeCounters.blocks, definitions: context.typesMap.blocks, diff --git a/packages/build/src/build/testSchema.js b/packages/build/src/build/testSchema.js index 1217fd103..ddc0a7c04 100644 --- a/packages/build/src/build/testSchema.js +++ b/packages/build/src/build/testSchema.js @@ -24,12 +24,12 @@ async function testSchema({ components, context }) { data: components, returnErrors: true, }); + if (!valid) { await context.logger.warn('Schema not valid.'); - const promises = errors.map((error) => - context.logger.warn(formatErrorMessage({ error, components })) + await Promise.all( + errors.map((error) => context.logger.warn(formatErrorMessage({ error, components }))) ); - await promises; } } diff --git a/packages/build/src/build/updateServerPackageJson.js b/packages/build/src/build/updateServerPackageJson.js index 7490ea0fb..50622c6d7 100644 --- a/packages/build/src/build/updateServerPackageJson.js +++ b/packages/build/src/build/updateServerPackageJson.js @@ -29,6 +29,7 @@ async function updateServerPackageJson({ components, context }) { }); } getPackages(components.types.actions); + getPackages(components.types.auth.providers); getPackages(components.types.blocks); getPackages(components.types.connections); getPackages(components.types.requests); diff --git a/packages/build/src/build/writePluginImports/writeAuthImports.js b/packages/build/src/build/writePluginImports/writeAuthImports.js new file mode 100644 index 000000000..7b39fb050 --- /dev/null +++ b/packages/build/src/build/writePluginImports/writeAuthImports.js @@ -0,0 +1,29 @@ +/* + Copyright 2020-2022 Lowdefy, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import generateImportFile from './generateImportFile.js'; + +async function writeAuthImports({ components, context }) { + await context.writeBuildArtifact( + 'plugins/auth/providers.js', + generateImportFile({ + types: components.types.auth.providers, + importPath: 'auth/providers', + }) + ); +} + +export default writeAuthImports; diff --git a/packages/build/src/build/writePluginImports/writeOperatorImports.js b/packages/build/src/build/writePluginImports/writeOperatorImports.js index 9aa2c3348..2a7179e81 100644 --- a/packages/build/src/build/writePluginImports/writeOperatorImports.js +++ b/packages/build/src/build/writePluginImports/writeOperatorImports.js @@ -18,14 +18,14 @@ import generateImportFile from './generateImportFile.js'; async function writeOperatorImports({ components, context }) { await context.writeBuildArtifact( - 'plugins/operatorsClient.js', + 'plugins/operators/client.js', generateImportFile({ types: components.types.operators.client, importPath: 'operators/client', }) ); await context.writeBuildArtifact( - 'plugins/operatorsServer.js', + 'plugins/operators/server.js', generateImportFile({ types: components.types.operators.server, importPath: 'operators/server', diff --git a/packages/build/src/build/writePluginImports/writePluginImports.js b/packages/build/src/build/writePluginImports/writePluginImports.js new file mode 100644 index 000000000..c43d6d96b --- /dev/null +++ b/packages/build/src/build/writePluginImports/writePluginImports.js @@ -0,0 +1,35 @@ +/* + Copyright 2020-2022 Lowdefy, Inc + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +import writeActionImports from './writeActionImports.js'; +import writeAuthImports from './writeAuthImports.js'; +import writeBlockImports from './writeBlockImports.js'; +import writeConnectionImports from './writeConnectionImports.js'; +import writeIconImports from './writeIconImports.js'; +import writeOperatorImports from './writeOperatorImports.js'; +import writeStyleImports from './writeStyleImports.js'; + +async function writePluginImports({ components, context }) { + await writeActionImports({ components, context }); + await writeAuthImports({ components, context }); + await writeBlockImports({ components, context }); + await writeConnectionImports({ components, context }); + await writeIconImports({ components, context }); + await writeOperatorImports({ components, context }); + await writeStyleImports({ components, context }); +} + +export default writePluginImports; diff --git a/packages/build/src/index.js b/packages/build/src/index.js index 62622ca83..d2483b60a 100644 --- a/packages/build/src/index.js +++ b/packages/build/src/index.js @@ -39,20 +39,15 @@ import testSchema from './build/testSchema.js'; import validateApp from './build/validateApp.js'; import validateConfig from './build/validateConfig.js'; import updateServerPackageJson from './build/updateServerPackageJson.js'; -import writeActionImports from './build/writePluginImports/writeActionImports.js'; import writeApp from './build/writeApp.js'; import writeAuth from './build/writeAuth.js'; -import writeBlockImports from './build/writePluginImports/writeBlockImports.js'; +import writePluginImports from './build/writePluginImports/writePluginImports.js'; import writeConfig from './build/writeConfig.js'; -import writeConnectionImports from './build/writePluginImports/writeConnectionImports.js'; import writeConnections from './build/writeConnections.js'; import writeGlobal from './build/writeGlobal.js'; -import writeIconImports from './build/writePluginImports/writeIconImports.js'; import writeMenus from './build/writeMenus.js'; -import writeOperatorImports from './build/writePluginImports/writeOperatorImports.js'; import writePages from './build/writePages.js'; import writeRequests from './build/writeRequests.js'; -import writeStyleImports from './build/writePluginImports/writeStyleImports.js'; import writeTypes from './build/writeTypes.js'; async function createContext({ customTypesMap, directories, logger, refResolver }) { @@ -67,6 +62,9 @@ async function createContext({ customTypesMap, directories, logger, refResolver refResolver, typeCounters: { actions: createCounter(), + auth: { + providers: createCounter(), + }, blocks: createCounter(), connections: createCounter(), requests: createCounter(), @@ -96,7 +94,6 @@ async function build(options) { await buildIcons({ components, context }); await buildStyles({ components, context }); await cleanBuildDirectory({ context }); - await writeActionImports({ components, context }); await writeApp({ components, context }); await writeAuth({ components, context }); await writeConnections({ components, context }); @@ -106,11 +103,7 @@ async function build(options) { await writeGlobal({ components, context }); await writeMenus({ components, context }); await writeTypes({ components, context }); - await writeBlockImports({ components, context }); - await writeConnectionImports({ components, context }); - await writeOperatorImports({ components, context }); - await writeStyleImports({ components, context }); - await writeIconImports({ components, context }); + await writePluginImports({ components, context }); await updateServerPackageJson({ components, context }); await copyPublicFolder({ components, context }); } diff --git a/packages/build/src/lowdefySchema.js b/packages/build/src/lowdefySchema.js index 120360305..c9d221ec1 100644 --- a/packages/build/src/lowdefySchema.js +++ b/packages/build/src/lowdefySchema.js @@ -185,6 +185,37 @@ export default { }, }, }, + providers: { + type: 'array', + items: { + type: 'object', + required: ['id', 'type'], + properties: { + id: { + type: 'string', + errorMessage: { + type: 'Auth provider "id" should be a string.', + }, + }, + type: { + type: 'string', + errorMessage: { + type: 'Auth provider "type" should be a string.', + }, + }, + properties: { + type: 'object', + }, + }, + errorMessage: { + type: 'Auth provider should be an object.', + required: { + id: 'Auth provider should have required property "id".', + type: 'Auth provider should have required property "type".', + }, + }, + }, + }, }, }, block: { @@ -615,6 +646,9 @@ export default { app: { $ref: '#/definitions/app', }, + auth: { + $ref: '#/definitions/authConfig', + }, cli: { type: 'object', errorMessage: { @@ -628,9 +662,6 @@ export default { }, additionalProperties: false, properties: { - auth: { - $ref: '#/definitions/authConfig', - }, basePath: { type: 'string', description: 'App base path to apply to all routes. Base path must start with "/".',