From dc9b150304b76c80cc645a3dad0d67008275bca5 Mon Sep 17 00:00:00 2001 From: Kiran Date: Thu, 17 Feb 2022 12:27:50 +0530 Subject: [PATCH] Fix the api endpoints --- .env.example | 4 + lib/env.ts | 6 ++ package-lock.json | 145 ++++++++++++++++++++++++++-- package.json | 1 + pages/api/apps/metadata.ts | 60 ------------ pages/api/apps/metadata/download.ts | 37 +++++++ pages/api/apps/metadata/index.ts | 32 ++++++ pages/apps/[id].tsx | 5 +- pages/apps/demo.tsx | 13 --- pages/apps/index.tsx | 18 +--- 10 files changed, 222 insertions(+), 99 deletions(-) create mode 100644 .env.example create mode 100644 lib/env.ts delete mode 100644 pages/api/apps/metadata.ts create mode 100644 pages/api/apps/metadata/download.ts create mode 100644 pages/api/apps/metadata/index.ts delete mode 100644 pages/apps/demo.tsx diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..42ccf12 --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +NODE_ENV=dev +DATABASE_URL= +APP_URL=http://localhost:4000 +ENTITY_ID=http://saml.example.com \ No newline at end of file diff --git a/lib/env.ts b/lib/env.ts new file mode 100644 index 0000000..e13b9ed --- /dev/null +++ b/lib/env.ts @@ -0,0 +1,6 @@ +const config = { + appUrl: process.env.APP_URL || 'http://localhost:4000', + entityId: process.env.ENTITY_ID || 'http://saml.example.com', +} + +export default config; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b21b179..028d8d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "@prisma/client": "^3.7.0", "axios": "^0.24.0", "next": "12.0.7", + "node-fetch": "^3.2.0", "react": "17.0.2", "react-dom": "17.0.2", "rsuite": "^5.5.2", @@ -3247,6 +3248,28 @@ "reusify": "^1.0.4" } }, + "node_modules/fetch-blob": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.4.tgz", + "integrity": "sha512-Eq5Xv5+VlSrYWEqKrusxY1C3Hm/hjeAsCGVG3ft7pZahlUAChpGZT/Ms1WmSLnEAisEXszjzu/s+ce6HZB2VHA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -3341,6 +3364,17 @@ "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/fraction.js": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.2.tgz", @@ -4430,6 +4464,14 @@ } } }, + "node_modules/next/node_modules/node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "engines": { + "node": "4.x || >=6.0.0" + } + }, "node_modules/next/node_modules/postcss": { "version": "8.2.15", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.15.tgz", @@ -4455,12 +4497,47 @@ "node": ">=0.10.0" } }, - "node_modules/node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], "engines": { - "node": "4.x || >=6.0.0" + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.0.tgz", + "integrity": "sha512-8xeimMwMItMw8hRrOl3C9/xzU49HV/yE6ORew/l+dxWimO5A4Ra8ld2rerlJvc/O7et5Z1zrWsPX43v1QBjCxw==", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/node-fetch/node_modules/data-uri-to-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", + "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==", + "engines": { + "node": ">= 12" } }, "node_modules/node-html-parser": { @@ -6086,6 +6163,14 @@ "node": ">=10.13.0" } }, + "node_modules/web-streams-polyfill": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz", + "integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==", + "engines": { + "node": ">= 8" + } + }, "node_modules/webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", @@ -8655,6 +8740,15 @@ "reusify": "^1.0.4" } }, + "fetch-blob": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.1.4.tgz", + "integrity": "sha512-Eq5Xv5+VlSrYWEqKrusxY1C3Hm/hjeAsCGVG3ft7pZahlUAChpGZT/Ms1WmSLnEAisEXszjzu/s+ce6HZB2VHA==", + "requires": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + } + }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -8717,6 +8811,14 @@ "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" }, + "formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "requires": { + "fetch-blob": "^3.1.2" + } + }, "fraction.js": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.2.tgz", @@ -9502,6 +9604,11 @@ "watchpack": "2.3.0" }, "dependencies": { + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, "postcss": { "version": "8.2.15", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.2.15.tgz", @@ -9519,10 +9626,27 @@ } } }, + "node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==" + }, "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.0.tgz", + "integrity": "sha512-8xeimMwMItMw8hRrOl3C9/xzU49HV/yE6ORew/l+dxWimO5A4Ra8ld2rerlJvc/O7et5Z1zrWsPX43v1QBjCxw==", + "requires": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "dependencies": { + "data-uri-to-buffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.0.tgz", + "integrity": "sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==" + } + } }, "node-html-parser": { "version": "1.4.9", @@ -10721,6 +10845,11 @@ "graceful-fs": "^4.1.2" } }, + "web-streams-polyfill": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.0.tgz", + "integrity": "sha512-EqPmREeOzttaLRm5HS7io98goBgZ7IVz79aDvqjD0kYXLtFZTc0T/U6wHTPKyIjb+MdN7DFIIX6hgdBEpWmfPA==" + }, "webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", diff --git a/package.json b/package.json index 9385f30..fc41781 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "@prisma/client": "^3.7.0", "axios": "^0.24.0", "next": "12.0.7", + "node-fetch": "^3.2.0", "react": "17.0.2", "react-dom": "17.0.2", "rsuite": "^5.5.2", diff --git a/pages/api/apps/metadata.ts b/pages/api/apps/metadata.ts deleted file mode 100644 index 46edadc..0000000 --- a/pages/api/apps/metadata.ts +++ /dev/null @@ -1,60 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import { createCertificate, createIdPMetadataXML } from '../../../utils'; -import { IdPMetadata } from '../../../types'; - -const idpEntityId = 'http://saml.example.com'; -const baseUrl = 'http://localhost:4000'; // TODO: Read from .env - -// https://boxyhqdemo.onelogin.com/trust/saml2/http-post/sso/a810f17d-48a8-4ac2-ae0f-253c823b272c -// https://dev-8924093.okta.com/app/dev-8924093_jacksondemo_1/exk3u9pl6jx4P9AE15d7/sso/saml -// https://accounts.google.com/o/saml2/idp?idpid=C02frd9s1 - -export default async function handler( - req: NextApiRequest, - res: NextApiResponse -) { - - switch (req.method) { - case 'GET': - return await getMetadata(); - case 'POST': - return await downloadMetadata(); - default: - return res.status(405).end(`Method ${req.method} Not Allowed`); - } - - // Get metadata for an app - async function getMetadata() { - //const {id} = req.query; - const appId = '0480c44e-f200-4f72-8af0-a5a57611fd2d'; - - const metadata = { - certificate: await createCertificate(), - fingerprint: '', - sso_url: `${baseUrl}/saml2/app/${appId}`, - entity_id: idpEntityId, - } - - return res.json(metadata); - } - - // Download metadata for an app - async function downloadMetadata() { - const appId = '0480c44e-f200-4f72-8af0-a5a57611fd2d'; - - const certificate = await createCertificate(); - const idpEntityId = 'http://localhost:4000/sso'; - const idpSsoUrl = 'http://localhost:4000/sso'; - - const xml = await createIdPMetadataXML({ - idpEntityId, - idpSsoUrl, - certificate, - }); - - res.setHeader('Content-type', 'text/xml'); - res.setHeader('Content-Disposition', 'attachment; filename="metadata.xml"'); - - return res.send(xml); - } -} diff --git a/pages/api/apps/metadata/download.ts b/pages/api/apps/metadata/download.ts new file mode 100644 index 0000000..704698d --- /dev/null +++ b/pages/api/apps/metadata/download.ts @@ -0,0 +1,37 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { createCertificate, createIdPMetadataXML } from '../../../../utils'; +import { IdPMetadata } from '../../../../types'; +import stream from 'stream'; +import { promisify } from 'util'; +import config from '../../../../lib/env' + +const pipeline = promisify(stream.pipeline); + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + + switch (req.method) { + case 'GET': + return await downloadMetadata(); + default: + return res.status(405).end(`Method ${req.method} Not Allowed`); + } + + // Download metadata for an app + async function downloadMetadata() { + const appId = '0480c44e-f200-4f72-8af0-a5a57611fd2d'; + + const xml = await createIdPMetadataXML({ + idpEntityId: config.entityId, + idpSsoUrl: `${config.appUrl}/saml2/app/${appId}`, + certificate: await createCertificate(), + }); + + res.setHeader('Content-type', 'text/xml'); + res.setHeader('Content-Disposition', 'attachment; filename=metadata.xml'); + + await pipeline(xml, res); + } +} \ No newline at end of file diff --git a/pages/api/apps/metadata/index.ts b/pages/api/apps/metadata/index.ts new file mode 100644 index 0000000..f62e4a2 --- /dev/null +++ b/pages/api/apps/metadata/index.ts @@ -0,0 +1,32 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { createCertificate } from '../../../../utils'; +import { IdPMetadata } from '../../../../types'; +import config from '../../../../lib/env' + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + + switch (req.method) { + case 'GET': + return await getMetadata(); + default: + return res.status(405).end(`Method ${req.method} Not Allowed`); + } + + // Get metadata for an app + async function getMetadata() { + //const {id} = req.query; + const appId = '0480c44e-f200-4f72-8af0-a5a57611fd2d'; + + const metadata = { + certificate: await createCertificate(), + fingerprint: '', + sso_url: `${config.appUrl}/saml2/app/${appId}`, + entity_id: config.entityId, + } + + return res.json(metadata); + } +} \ No newline at end of file diff --git a/pages/apps/[id].tsx b/pages/apps/[id].tsx index 099758a..34cd6a2 100644 --- a/pages/apps/[id].tsx +++ b/pages/apps/[id].tsx @@ -1,10 +1,11 @@ import prisma from '../../lib/prisma'; import { GetServerSideProps } from 'next'; -import React from 'react'; import { App } from '../../types'; import axios from 'axios'; import { IdPMetadata } from '../../types'; +import React, { ChangeEvent, FormEvent, useState } from 'react'; +// TODO: Remove this export const getServerSideProps: GetServerSideProps = async ({ params }) => { const app = await prisma.app.findUnique({ where: { @@ -34,6 +35,8 @@ const ShowApp: React.FC<{app: App, metadata: IdPMetadata}> = ({app, metadata}) =

sso_url: {metadata.sso_url}

entity_id: {metadata.entity_id}

certificate: {metadata.certificate}

+ + Download Metadata ); }; diff --git a/pages/apps/demo.tsx b/pages/apps/demo.tsx deleted file mode 100644 index 674feef..0000000 --- a/pages/apps/demo.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import type { NextPage } from 'next'; -import { useEffect, useRef, useState } from 'react'; -import { Sidenav, Nav, Dropdown } from 'rsuite'; - -const Demo: NextPage = (prop) => { - return ( -
-

Hello from demo

-
- ); -}; - -export default Demo; \ No newline at end of file diff --git a/pages/apps/index.tsx b/pages/apps/index.tsx index d2ba9e2..bb39f97 100644 --- a/pages/apps/index.tsx +++ b/pages/apps/index.tsx @@ -32,14 +32,6 @@ const Apps: NextPage = () => { setMetadata(data); }; - const downloadMetadata = async (e: ChangeEvent) => { - e.preventDefault(); - - const {data} = await axios.post('/api/apps/metadata', { - ...formData - }); - } - return (
@@ -64,16 +56,8 @@ const Apps: NextPage = () => {
- + - - - -
    -
  • SSO URL:

    {metadata.sso_url}
  • -
  • Entity ID:

    {metadata.entity_id}
  • -
  • Certificate:

    {metadata.certificate}
  • -
); };