UNPKG

infrastructure-components

Version:

Infrastructure-Components configure the infrastructure of your React-App as part of your React-Components.

451 lines • 23.7 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result["default"] = mod; return result; }; Object.defineProperty(exports, "__esModule", { value: true }); const react_1 = __importDefault(require("react")); const types_1 = __importDefault(require("../types")); const middleware_component_1 = __importDefault(require("../middleware/middleware-component")); const webapp_component_1 = __importStar(require("../webapp/webapp-component")); const securedroute_component_1 = require("./securedroute-component"); const route_component_1 = __importStar(require("../route/route-component")); const libs_1 = require("../libs"); const iso_libs_1 = require("../libs/iso-libs"); const auth_middleware_1 = require("./auth-middleware"); const body_parser_1 = __importDefault(require("body-parser")); const securedservice_component_1 = require("./securedservice-component"); const service_component_1 = require("../service/service-component"); exports.AUTHENTICATION_INSTANCE_TYPE = "AuthenticationComponent"; exports.AuthenticationProvider = { EMAIL: "AUTH_EMAIL", GITHUB: "AUTH_GITHUB", // TODO Medium Auth only partly implemented!!! MEDIUM: "AUTH_MEDIUM" }; exports.AUTH_RESPONSE = { EMAIL_INVALID: "EMAIL_INVALID", NOT_IMPLEMENTED: "NOT_IMPLEMENTED" // the authCallback is not implemented for this provider }; exports.getProviderKey = (provider) => { return provider + SUFFIX_SECRET; }; exports.getClientSecret = (provider) => { return process.env[exports.getProviderKey(provider)]; }; /** * This creates a customized middleware that catches an error when the user is not logged in, i.e. it forwards her * to the provider's login-page * * @param clientId as defined in the app of the provider * @param callbackUrl as defined in the app of the provider * @param provider to create the redirect Url for */ exports.createRequestLoginMiddleware = (clientId, callbackUrl, provider, loginUrl) => (err, req, res, next) => { const path = require('path'); console.log("createRequestLoginMiddleware: ", err); if (provider === exports.AuthenticationProvider.EMAIL) { console.log("request login from: ", loginUrl); const page = err && req.query && req.query.page ? req.query.page : req.url; res.redirect(`${path.join(iso_libs_1.getBasename(), loginUrl)}?page=${page}${err ? `&message=${err}` : ""}`); } else if (provider === exports.AuthenticationProvider.GITHUB) { res.redirect(`https://github.com/login/oauth/authorize?scope=user:email&client_id=${clientId}&redirect_uri=${callbackUrl}?page=${req.url}`); } else if (provider === exports.AuthenticationProvider.MEDIUM) { res.redirect(`https://medium.com/m/oauth/authorize?client_id=${clientId}&scope=basicProfile,listPublications,publishPost&state=InteractiveMedium&response_type=code&redirect_uri=${callbackUrl}?page=${req.url}`); } return; }; /** * Create a function that creates a function that fetches the AccessToken of the provider */ exports.createFetchAccessTokenFunction = (clientId, callbackUrl, provider, senderEmail, getSubject, getHtmlText) => (req) => { if (provider === exports.AuthenticationProvider.EMAIL) { console.log("this is the EMAIL - createFetchAccessTokenFunction middleware"); const { email, password, page } = req.query; if (email !== undefined && password !== undefined) { // the function fFetch must call a service that responds with a json. this json is fed into createGetUserFunction return { redirectPage: page, fFetch: function () { return __awaiter(this, void 0, void 0, function* () { const uuidv4 = require('uuid/v4'); const access_token = uuidv4(); // TODO: here we call the service to send an email to the user // see: https://docs.aws.amazon.com/de_de/sdk-for-javascript/v2/developer-guide/ses-examples-sending-email.html const AWS = require('aws-sdk'); // Create sendEmail params var params = { Destination: { BccAddresses: [ senderEmail ], CcAddresses: [], ToAddresses: [ email ] }, Message: { Body: { Html: { Charset: "UTF-8", Data: getHtmlText(email, `${callbackUrl}?${auth_middleware_1.EMAIL_CONFIRMATION_PARAM}=${access_token}&${auth_middleware_1.EMAIL_PARAM}=${email}`) }, }, Subject: { Charset: 'UTF-8', Data: getSubject(email) } }, Source: senderEmail, ReplyToAddresses: [ senderEmail ], }; console.log("this is the fFetch of the mail-authentication"); // this is the response return new Promise(function (resolve, reject) { // Create the promise and SES service object var sendPromise = new AWS.SES({ apiVersion: '2010-12-01' }).sendEmail(params).promise(); // Handle promise's fulfilled/rejected states sendPromise.then(function (data) { console.log(data.MessageId); resolve({ id: email, name: "", username: "", imageUrl: "", access_token: access_token, email: email, encrypted_password: password, status: auth_middleware_1.AUTH_STATUS.PENDING }); }).catch(function (err) { console.error(err, err.stack); reject(err); }); }); }); } /*fetch(callbackUrl,{ method: 'POST', body: `code=${code}&client_id=${clientId}&client_secret=${getClientSecret(provider)}&grant_type=authorization_code&redirect_uri=${callbackUrl}`, headers: { "Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json", "Accept-Charset": "utf-8" } })*/ }; } } else if (provider === exports.AuthenticationProvider.GITHUB) { // TODO check https://octokit.github.io/rest.js/ !!! // see docs: https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/ //console.log("request: ", req); const { state, code, error, page } = req.query; // session_code = request.env['rack.request.query_hash']['code'] if (error !== undefined) { // TODO handle an error, e.g. user does not authorize the app console.log(error); return; } else if (code !== undefined) { console.log("found redirect page: ", page); return { redirectPage: page, fFetch: function () { return __awaiter(this, void 0, void 0, function* () { return yield fetch('https://github.com/login/oauth/access_token', { method: 'POST', body: `code=${code}&client_id=${clientId}&client_secret=${exports.getClientSecret(provider)}`, headers: { "Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json", "Accept-Charset": "utf-8" } }).then(function (response) { return response.json(); }); }); } }; } ; } else if (provider === exports.AuthenticationProvider.MEDIUM) { console.log("Medium callback: ", req.query); const { state, code, error, page } = req.query; if (error !== undefined) { // TODO handle an error, e.g. user does not authorize the app console.log(error); return; } else if (code !== undefined) { return { redirectPage: page, fFetch: fetch('https://api.medium.com/v1/tokens', { method: 'POST', body: `code=${code}&client_id=${clientId}&client_secret=${exports.getClientSecret(provider)}&grant_type=authorization_code&redirect_uri=${callbackUrl}`, headers: { "Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json", "Accept-Charset": "utf-8" } }).then(function (response) { return response.json(); }) }; } } }; exports.createGetUserFunction = (provider) => function (resJson) { return __awaiter(this, void 0, void 0, function* () { console.log("resJson: ", resJson); if (provider === exports.AuthenticationProvider.EMAIL) { // we just provide the response-json return new Promise(function (resolve, reject) { resolve(resJson); }); } else if (provider === exports.AuthenticationProvider.GITHUB) { const { token_type, access_token, } = resJson; // try the freshly acquired token and get the user's Medium.com id return yield fetch('https://api.github.com/user', { method: 'GET', headers: { "Content-Type": "application/json", "Accept": "application/json", "Accept-Charset": "utf-8", "Authorization": token_type + " " + access_token } }).then(function (response) { // now parse the json return response.json(); }).then(function (data) { /* "login":"frankzickert", "id":10414265, "node_id":"MDQ6VXNlcjEwNDE0MjY1", "avatar_url":"https://avatars0.githubusercontent.com/u/10414265?v=4", "gravatar_id":"", "url":"https://api.github.com/users/frankzickert", "html_url":"https://github.com/frankzickert", "followers_url":"https://api.github.com/users/frankzickert/followers", "following_url":"https://api.github.com/users/frankzickert/following{/other_user}", "gists_url":"https://api.github.com/users/frankzickert/gists{/gist_id}", "starred_url":"https://api.github.com/users/frankzickert/starred{/owner}{/repo}", "subscriptions_url":"https://api.github.com/users/frankzickert/subscriptions", "organizations_url":"https://api.github.com/users/frankzickert/orgs", "repos_url":"https://api.github.com/users/frankzickert/repos", "events_url":"https://api.github.com/users/frankzickert/events{/privacy}", "received_events_url":"https://api.github.com/users/frankzickert/received_events", "type":"User", "site_admin":false, "name":"Frank Zickert", "company":null, "blog":"https://www.lean-data-science.com/", "location":"Germany", "email":"frank.zickert@lean-data-science.awsapps.com", "hireable":null, "bio":"I have been working as an IT professional for 15 years...", "public_repos":3, "public_gists":5, "followers":1, "following":0, "created_at":"2015-01-06T07:23:33Z", "updated_at":"2019-05-08T20:37:43Z"} */ return { id: data.id, name: data.name, username: data.login, imageUrl: data.avatar_url, email: data.email, access_token: access_token, status: auth_middleware_1.AUTH_STATUS.ACTIVE }; }); } else if (provider === exports.AuthenticationProvider.MEDIUM) { const { token_type, access_token, refresh_token, scope, expires_at } = resJson; // try the freshly acquired token and get the user's Medium.com id return yield fetch('https://api.medium.com/v1/me', { method: 'GET', headers: { "Content-Type": "application/json", "Accept": "application/json", "Accept-Charset": "utf-8", "Authorization": token_type + " " + access_token } }).then(function (response) { // now parse the json return response.json(); }).then(function (data) { return { id: data.id, name: data.name, username: data.username, imageUrl: data.imageUrl, email: data.email, access_token: access_token, status: auth_middleware_1.AUTH_STATUS.ACTIVE }; }); } }); }; /** * suffix to the provider. specifies the -env-variable to hold the clientSecret */ const SUFFIX_SECRET = "_SECRET"; /** * The WebApp is a client that runs in the browser, SPA or SSR * * @param props */ exports.default = (props) => { //console.log ("route: ",props ); const componentProps = { infrastructureType: types_1.default.INFRASTRUCTURE_TYPE_COMPONENT, instanceType: exports.AUTHENTICATION_INSTANCE_TYPE, instanceId: props.id, }; const authenticationProps = { // set our storeData-Property setStoreIdentityData: (storeIdentityData) => { //console.log("setStoreIdentityData: ", storeIdentityData); props.storeAuthData = storeIdentityData; //console.log("auth-props: ", props) }, setGetIdentityData: (getIdentityData) => { props.getAuthData = getIdentityData; }, authCallback: (email, password, page, onResponse) => { console.log("this is the auth-Callback"); if (props.provider === exports.AuthenticationProvider.EMAIL) { // verify the format of the e-mail address if (!(/^([A-Za-z0-9_\-.])+@([A-Za-z0-9_\-.])+\.([A-Za-z]{2,})$/.test(email))) { onResponse(exports.AUTH_RESPONSE.EMAIL_INVALID); return; } // redirect to the provided url that is // TODO encrypt the password! window.location.href = `${props.callbackUrl}?${auth_middleware_1.EMAIL_PARAM}=${email}&${auth_middleware_1.PASSWORD_PARAM}=${password}&page=${page}`; return; } onResponse(exports.AUTH_RESPONSE.NOT_IMPLEMENTED); } }; /** * For this function is a callback on a middleware, it only sets the userId on the backend side! * @param userid */ const onAuthenticated = (userid) => { console.log("just authenticated, tell the securedEntries about it"); // we need to provide some data to the secured entries libs_1.findComponentRecursively(props.children, (c) => c.setUserId !== undefined).forEach(se => { //console.log("found secured entry: ", se); se.setUserId(userid); }); }; libs_1.findComponentRecursively(props.children, (c) => c.setMiddleware !== undefined).forEach(se => { //console.log("found secured entry: ", se); se.setMiddleware(middleware_component_1.default({ callback: auth_middleware_1.createAuthMiddleware(exports.getClientSecret(props.provider), onAuthenticated) })); }); // we need to provide some data to the secured routes libs_1.findComponentRecursively(props.children, securedroute_component_1.isSecuredRoute).forEach(sr => { //console.log("found secured route: ", sr); sr.middlewares = [ // this middleware checks whether the user is logged in middleware_component_1.default({ callback: auth_middleware_1.createAuthMiddleware(exports.getClientSecret(props.provider), onAuthenticated) }), // this middleware checks redirects the user to the login page, if she is not logged in middleware_component_1.default({ callback: exports.createRequestLoginMiddleware(props.clientId, props.callbackUrl, props.provider, props.loginUrl) }) ].concat(sr.middlewares); // now that we have added the authentication middlewares, the route can be handled as a normal one sr.instanceType = route_component_1.ROUTE_INSTANCE_TYPE; }); // we need to provide some data to the secured routes libs_1.findComponentRecursively(props.children, securedservice_component_1.isSecuredService).forEach(service => { //console.log("found secured route: ", sr); service.middlewares = [ // this middleware checks whether the user is logged in middleware_component_1.default({ callback: auth_middleware_1.createAuthMiddleware(exports.getClientSecret(props.provider), onAuthenticated) }), // this middleware checks redirects the user to the login page, if she is not logged in middleware_component_1.default({ callback: exports.createRequestLoginMiddleware(props.clientId, props.callbackUrl, props.provider, props.loginUrl) }) ].concat(service.middlewares); // now that we have added the authentication middlewares, the route can be handled as a normal one service.instanceType = service_component_1.SERVICE_INSTANCE_TYPE; }); // we need to provide the AuthenticationId to webApps, these may be anywhere in the tree, not // only direct children. So rather than mapping the children, we need to change them libs_1.findComponentRecursively(props.children, (child) => child.setAuthenticationId !== undefined).forEach(child => { child.setAuthenticationId(props.id); }); /** * The data-layer replaces the authentication component with its children */ const mappedChildren = { // we provide the middlewares that we require children: [ // we create a webapp to handle the callback webapp_component_1.default({ id: "WEBAPP_" + props.provider, path: props.callbackUrl.substring(props.callbackUrl.lastIndexOf("/")), method: "GET", children: [ // middleware required of parsing the json response middleware_component_1.default({ callback: body_parser_1.default.json() }), middleware_component_1.default({ callback: body_parser_1.default.urlencoded({ extended: true }) }), middleware_component_1.default({ callback: auth_middleware_1.createCallbackMiddleware(exports.getClientSecret(props.provider), exports.createFetchAccessTokenFunction(props.clientId, props.callbackUrl, props.provider, props.senderEmail, props.getSubject, props.getHtmlText), //fetchAccessToken exports.createGetUserFunction(props.provider), function (request, key, val, jsonData) { return __awaiter(this, void 0, void 0, function* () { return yield props.storeAuthData(request, key, val, jsonData); }); }, //props.storeAuthData function (request, matchBrowserIdentity, key, val) { return __awaiter(this, void 0, void 0, function* () { return yield props.getAuthData(request, matchBrowserIdentity, key, val); }); } //getAuthData ) }), middleware_component_1.default({ callback: exports.createRequestLoginMiddleware(props.clientId, props.callbackUrl, props.provider, props.loginUrl) }), // the render function should never be called for the Callback-Middleware redirects to the // page that was requested in the first request route_component_1.default({ path: props.callbackUrl.substring(props.callbackUrl.lastIndexOf("/")), name: "WEBAPP_" + props.provider, render: (rp) => react_1.default.createElement("div", null, "This is the callback route!"), }) ] }) //, //(browserId: string) => redirectMiddleware("/dashboard") ].concat(libs_1.getChildrenArray(props.children)) }; console.log("authentication: ", libs_1.findComponentRecursively(props.children, webapp_component_1.isWebApp)); return Object.assign(props, componentProps, authenticationProps, mappedChildren); }; exports.isAuthentication = (component) => { return component !== undefined && component.instanceType === exports.AUTHENTICATION_INSTANCE_TYPE; }; //# sourceMappingURL=authentication-component.js.map