Skip to content

Commit

Permalink
Refactor integrations logic and replace hardcoded client ids with envars
Browse files Browse the repository at this point in the history
  • Loading branch information
dangtony98 committed Dec 18, 2022
1 parent 5168195 commit 5475555
Show file tree
Hide file tree
Showing 17 changed files with 144 additions and 42 deletions.
7 changes: 6 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,12 @@ SMTP_PASSWORD=

# Integration
# Optional only if integration is used
OAUTH_CLIENT_SECRET_HEROKU=
CLIENT_ID_HEROKU=
CLIENT_ID_VERCEL=
CLIENT_ID_NETLIFY=
CLIENT_SECRET_HEROKU=
CLIENT_SECRET_VERCEL=
CLIENT_SECRET_NETLIFY=

# Sentry (optional) for monitoring errors
SENTRY_DSN=
Expand Down
8 changes: 6 additions & 2 deletions backend/environment.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,12 @@ declare global {
JWT_SIGNUP_SECRET: string;
MONGO_URL: string;
NODE_ENV: 'development' | 'staging' | 'testing' | 'production';
OAUTH_CLIENT_SECRET_HEROKU: string;
OAUTH_TOKEN_URL_HEROKU: string;
CLIENT_ID_HEROKU: string;
CLIENT_ID_VERCEL: string;
CLIENT_ID_NETLIFY: string;
CLIENT_SECRET_HEROKU: string;
CLIENT_SECRET_VERCEL: string;
CLIENT_SECRET_NETLIFY: string;
POSTHOG_HOST: string;
POSTHOG_PROJECT_API_KEY: string;
PRIVATE_KEY: string;
Expand Down
4 changes: 2 additions & 2 deletions backend/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ const JWT_SIGNUP_LIFETIME = process.env.JWT_SIGNUP_LIFETIME! || '15m';
const JWT_SIGNUP_SECRET = process.env.JWT_SIGNUP_SECRET!;
const MONGO_URL = process.env.MONGO_URL!;
const NODE_ENV = process.env.NODE_ENV! || 'production';
const OAUTH_CLIENT_SECRET_HEROKU = process.env.OAUTH_CLIENT_SECRET_HEROKU!;
const CLIENT_SECRET_HEROKU = process.env.CLIENT_SECRET_HEROKU!;
const CLIENT_ID_VERCEL = process.env.CLIENT_ID_VERCEL!;
const CLIENT_ID_NETLIFY = process.env.CLIENT_ID_NETLIFY!;
const CLIENT_SECRET_VERCEL = process.env.CLIENT_SECRET_VERCEL!;
Expand Down Expand Up @@ -49,9 +49,9 @@ export {
JWT_SIGNUP_SECRET,
MONGO_URL,
NODE_ENV,
OAUTH_CLIENT_SECRET_HEROKU,
CLIENT_ID_VERCEL,
CLIENT_ID_NETLIFY,
CLIENT_SECRET_HEROKU,
CLIENT_SECRET_VERCEL,
CLIENT_SECRET_NETLIFY,
POSTHOG_HOST,
Expand Down
4 changes: 2 additions & 2 deletions backend/src/integrations/exchange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
} from '../variables';
import {
SITE_URL,
OAUTH_CLIENT_SECRET_HEROKU,
CLIENT_SECRET_HEROKU,
CLIENT_ID_VERCEL,
CLIENT_ID_NETLIFY,
CLIENT_SECRET_VERCEL,
Expand Down Expand Up @@ -114,7 +114,7 @@ const exchangeCodeHeroku = async ({
new URLSearchParams({
grant_type: 'authorization_code',
code: code,
client_secret: OAUTH_CLIENT_SECRET_HEROKU
client_secret: CLIENT_SECRET_HEROKU
} as any)
)).data;

Expand Down
4 changes: 2 additions & 2 deletions backend/src/integrations/refresh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import axios from 'axios';
import * as Sentry from '@sentry/node';
import { INTEGRATION_HEROKU } from '../variables';
import {
OAUTH_CLIENT_SECRET_HEROKU
CLIENT_SECRET_HEROKU
} from '../config';
import {
INTEGRATION_HEROKU_TOKEN_URL
Expand Down Expand Up @@ -59,7 +59,7 @@ const exchangeRefreshHeroku = async ({
new URLSearchParams({
grant_type: 'refresh_token',
refresh_token: refreshToken,
client_secret: OAUTH_CLIENT_SECRET_HEROKU
client_secret: CLIENT_SECRET_HEROKU
} as any)
);

Expand Down
14 changes: 9 additions & 5 deletions backend/src/middleware/requireIntegrationAuthorizationAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ import { validateMembership } from '../helpers/membership';
* @param {Object} obj
* @param {String[]} obj.acceptedRoles - accepted workspace roles
* @param {String[]} obj.acceptedStatuses - accepted workspace statuses
* @param {Boolean} obj.attachRefresh - whether or not to decrypt and attach integration authorization refresh token onto request
* @param {Boolean} obj.attachAccessToken - whether or not to decrypt and attach integration authorization access token onto request
*/
const requireIntegrationAuthorizationAuth = ({
acceptedRoles,
acceptedStatuses
acceptedStatuses,
attachAccessToken = true
}: {
acceptedRoles: string[];
acceptedStatuses: string[];
attachAccessToken?: boolean;
}) => {
return async (req: Request, res: Response, next: NextFunction) => {
try {
Expand All @@ -41,9 +43,11 @@ const requireIntegrationAuthorizationAuth = ({
});

req.integrationAuth = integrationAuth;
req.accessToken = await IntegrationService.getIntegrationAuthAccess({
integrationAuthId: integrationAuth._id.toString()
});
if (attachAccessToken) {
req.accessToken = await IntegrationService.getIntegrationAuthAccess({
integrationAuthId: integrationAuth._id.toString()
});
}

return next();
} catch (err) {
Expand Down
3 changes: 2 additions & 1 deletion backend/src/routes/integrationAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ router.delete(
requireAuth,
requireIntegrationAuthorizationAuth({
acceptedRoles: [ADMIN, MEMBER],
acceptedStatuses: [GRANTED]
acceptedStatuses: [GRANTED],
attachAccessToken: false
}),
param('integrationAuthId'),
validateRequest,
Expand Down
4 changes: 4 additions & 0 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,12 @@ services:
env_file: .env
environment:
- NEXT_PUBLIC_ENV=development
- INFISICAL_TELEMETRY_ENABLED=${TELEMETRY_ENABLED}
- NEXT_PUBLIC_SITE_URL=${SITE_URL}
- NEXT_PUBLIC_STRIPE_PRODUCT_PRO=${STRIPE_PRODUCT_PRO}
- NEXT_PUBLIC_STRIPE_PRODUCT_STARTER=${STRIPE_PRODUCT_STARTER}
- NEXT_PUBLIC_CLIENT_ID_HEROKU=${CLIENT_ID_HEROKU}
- NEXT_PUBLIC_CLIENT_ID_NETLIFY=${CLIENT_ID_NETLIFY}
networks:
- infisical-dev

Expand Down
3 changes: 3 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ services:
- INFISICAL_TELEMETRY_ENABLED=${TELEMETRY_ENABLED}
- NEXT_PUBLIC_STRIPE_PRODUCT_PRO=${STRIPE_PRODUCT_PRO}
- NEXT_PUBLIC_STRIPE_PRODUCT_STARTER=${STRIPE_PRODUCT_STARTER}
- NEXT_PUBLIC_SITE_URL=${SITE_URL}
- NEXT_PUBLIC_CLIENT_ID_HEROKU=${CLIENT_ID_HEROKU}
- NEXT_PUBLIC_CLIENT_ID_NETLIFY=${CLIENT_ID_NETLIFY}
networks:
- infisical

Expand Down
7 changes: 5 additions & 2 deletions docs/self-hosting/configuration/envars.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ Configuring Infisical requires setting some environment variables. There is a fi
| `SMTP_USERNAME` | ❗️ Credential to connect to host (e.g. `[email protected]`) | `None` |
| `SMTP_PASSWORD` | ❗️ Credential to connect to host | `None` |
| `TELEMETRY_ENABLED` | `true` or `false`. [More](../overview). | `true` |
| `OAUTH_CLIENT_SECRET_HEROKU` | OAuth client secret for Heroku integration | `None` |
| `OAUTH_TOKEN_URL_HEROKU` | OAuth token URL for Heroku integration | `None` |
| `CLIENT_ID_VERCEL` | OAuth client id for Vercel integration | `None` |
| `CLIENT_ID_NETLIFY` | OAuth client id for Netlify integration | `None` |
| `CLIENT_SECRET_HEROKU` | OAuth client secret for Heroku integration | `None` |
| `CLIENT_SECRET_VERCEL` | OAuth client secret for Vercel integration | `None` |
| `CLIENT_SECRET_NETLIFY` | OAuth client secret for Netlify integration | `None` |
| `SENTRY_DSN` | DSN for error-monitoring with Sentry | `None` |
1 change: 1 addition & 0 deletions frontend/components/integrations/CloudIntegration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ const CloudIntegration = ({
)
.map((authorization) => authorization._id)[0],
});

router.reload();
}}
className="cursor-pointer w-max bg-red py-0.5 px-2 rounded-b-md text-xs flex flex-row items-center opacity-0 group-hover:opacity-100 duration-200"
Expand Down
2 changes: 0 additions & 2 deletions frontend/components/integrations/Integration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import getIntegrationApps from "../../pages/api/integrations/GetIntegrationApps"
import Button from "~/components/basic/buttons/Button";
import ListBox from "~/components/basic/Listbox";

// TODO: optimize laggy dropdown for app options

interface Integration {
app?: string;
environment: string;
Expand Down
8 changes: 7 additions & 1 deletion frontend/components/utilities/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@ const POSTHOG_HOST =
process.env.NEXT_PUBLIC_POSTHOG_HOST! || "https://app.posthog.com";
const STRIPE_PRODUCT_PRO = process.env.NEXT_PUBLIC_STRIPE_PRODUCT_PRO!;
const STRIPE_PRODUCT_STARTER = process.env.NEXT_PUBLIC_STRIPE_PRODUCT_STARTER!;
const SITE_URL = process.env.NEXT_PUBLIC_SITE_URL!;
const CLIENT_ID_HEROKU = process.env.NEXT_PUBLIC_CLIENT_ID_HEROKU!;
const CLIENT_ID_NETLIFY = process.env.NEXT_PUBLIC_CLIENT_ID_NETLIFY!;

export {
ENV,
POSTHOG_API_KEY,
POSTHOG_HOST,
STRIPE_PRODUCT_PRO,
STRIPE_PRODUCT_STARTER,
};
SITE_URL,
CLIENT_ID_HEROKU,
CLIENT_ID_NETLIFY
};
25 changes: 12 additions & 13 deletions frontend/pages/integrations/[id].js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import FrameworkIntegrationSection from "~/components/integrations/FrameworkInte
import CloudIntegrationSection from "~/components/integrations/CloudIntegrationSection";
import IntegrationSection from "~/components/integrations/IntegrationSection";
import frameworkIntegrationOptions from "../../public/json/frameworkIntegrations.json";
import cloudIntegrationOptions from "../../public/json/cloudIntegrations.json";
// import cloudIntegrationOptions from "../../public/json/cloudIntegrations.json";
import { cloudIntegrationOptions } from "../../public/data/cloudIntegrations";
import getWorkspaceAuthorizations from "../api/integrations/getWorkspaceAuthorizations";
import getWorkspaceIntegrations from "../api/integrations/getWorkspaceIntegrations";
import getBot from "../api/bot/getBot";
Expand All @@ -29,7 +30,7 @@ export default function Integrations() {
const [integrations, setIntegrations] = useState([]);
const [bot, setBot] = useState(null);
const [isActivateBotDialogOpen, setIsActivateBotDialogOpen] = useState(false);
const [isIntegrationAccessTokenDialogOpen, setIntegrationAccessTokenDialogOpen] = useState(true);
// const [isIntegrationAccessTokenDialogOpen, setIntegrationAccessTokenDialogOpen] = useState(true);
const [selectedIntegrationOption, setSelectedIntegrationOption] = useState(null);

const router = useRouter();
Expand Down Expand Up @@ -122,22 +123,20 @@ export default function Integrations() {
const state = crypto.randomBytes(16).toString("hex");
localStorage.setItem('latestCSRFToken', state);

// TODO: Add CircleCI, Render, Fly.io

switch (integrationOption.name) {
case 'Heroku':
window.location = `https://id.heroku.com/oauth/authorize?client_id=${integrationOption.clientId}&response_type=code&scope=write-protected&state=${state}`;
break;
case 'Vercel':
window.location = `https://vercel.com/integrations/infisical/new?state=${state}`;
window.location = `https://vercel.com/integrations/infisical-dev/new?state=${state}`;
break;
case 'Netlify':
window.location = `https://app.netlify.com/authorize?client_id=${integrationOption.clientId}&response_type=code&redirect_uri=${integrationOption.redirectURL}&state=${state}`;
break;
case 'Fly.io':
console.log('fly.io');
setIntegrationAccessTokenDialogOpen(true);
window.location = `https://app.netlify.com/authorize?client_id=${integrationOption.clientId}&response_type=code&state=${state}&redirect_uri=${integrationOption.redirectURL}`;
break;
// case 'Fly.io':
// console.log('fly.io');
// setIntegrationAccessTokenDialogOpen(true);
// break;
}
} catch (err) {
console.log(err);
Expand All @@ -163,7 +162,7 @@ export default function Integrations() {
}

// case: bot is not active -> open modal to activate bot
setIsActivateBotOpen(true);
setIsActivateBotDialogOpen(true);
} catch (err) {
console.error(err);
}
Expand Down Expand Up @@ -193,13 +192,13 @@ export default function Integrations() {
handleBotActivate={handleBotActivate}
handleIntegrationOption={handleIntegrationOption}
/>
<IntegrationAccessTokenDialog
{/* <IntegrationAccessTokenDialog
isOpen={isIntegrationAccessTokenDialogOpen}
closeModal={() => setIntegrationAccessTokenDialogOpen(false)}
selectedIntegrationOption={selectedIntegrationOption}
handleBotActivate={handleBotActivate}
handleIntegrationOption={handleIntegrationOption}
/>
/> */}
<IntegrationSection integrations={integrations} />
<CloudIntegrationSection
cloudIntegrationOptions={cloudIntegrationOptions}
Expand Down
83 changes: 83 additions & 0 deletions frontend/public/data/cloudIntegrations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import {
SITE_URL,
CLIENT_ID_HEROKU,
CLIENT_ID_NETLIFY
} from '../../components/utilities/config';

const cloudIntegrationOptions = [
{
name: 'Heroku',
slug: 'heroku',
image: 'Heroku',
isAvailable: true,
type: 'oauth2',
clientId: CLIENT_ID_HEROKU,
docsLink: ''
},
{
name: 'Vercel',
slug: 'vercel',
image: 'Vercel',
isAvailable: true,
type: 'vercel',
clientId: '',
docsLink: ''
},
{
name: 'Netlify',
slug: 'netlify',
image: 'Netlify',
isAvailable: true,
type: 'oauth2',
clientId: CLIENT_ID_NETLIFY,
redirectURL: `${SITE_URL}/netlify`,
docsLink: ''
},
{
name: 'Google Cloud Platform',
slug: 'gcp',
image: 'Google Cloud Platform',
isAvailable: false,
type: '',
clientId: '',
docsLink: ''
},
{
name: 'Amazon Web Services',
slug: 'aws',
image: 'Amazon Web Services',
isAvailable: false,
type: '',
clientId: '',
docsLink: ''
},
{
name: 'Microsoft Azure',
slug: 'azure',
image: 'Microsoft Azure',
isAvailable: false,
type: '',
clientId: '',
docsLink: ''
},
{
name: 'Travis CI',
slug: 'travisci',
image: 'Travis CI',
isAvailable: false,
type: '',
clientId: '',
docsLink: ''
},
{
name: 'Circle CI',
slug: 'circleci',
image: 'Circle CI',
isAvailable: false,
type: '',
clientId: '',
docsLink: ''
}
]

export { cloudIntegrationOptions };
Binary file added frontend/public/images/integrations/Vercel.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 0 additions & 9 deletions frontend/public/json/cloudIntegrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,6 @@
"redirectURL": "http://localhost:8080/netlify",
"docsLink": ""
},
{
"name": "Fly.io",
"slug": "flyio",
"image": "Google Cloud Platform",
"isAvailable": true,
"type": "accessToken",
"clientId": "",
"docsLink": ""
},
{
"name": "Google Cloud Platform",
"slug": "gcp",
Expand Down

0 comments on commit 5475555

Please sign in to comment.