Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Admin tool accessible without Cord Slack integration #4

Open
dmmiller opened this issue Jul 1, 2024 · 1 comment
Open

Make Admin tool accessible without Cord Slack integration #4

dmmiller opened this issue Jul 1, 2024 · 1 comment

Comments

@dmmiller
Copy link
Collaborator

dmmiller commented Jul 1, 2024

No description provided.

@jwatzman
Copy link

This hacky patch will make the SDK testbed "andrei" user an admin, and then log you into the admin console as him. This requires you to have loaded the SDK testbed at least once before loading the admin page. While Cord exposed the admin page at admin.cord.com and so needed a real login there, for anyone self-deploying this, it might be sufficient to just firewall the admin port so that you can only access it internally, in which case you don't need "real" auth, so this might be good enough for you. Then again, it very possibly is not, hence why I'm dumping it here as an example and not making a PR.

diff --git a/server/src/admin/routes/SlackLoginHandler.ts b/server/src/admin/routes/SlackLoginHandler.ts
index f0d1cdc..748254f 100644
--- a/server/src/admin/routes/SlackLoginHandler.ts
+++ b/server/src/admin/routes/SlackLoginHandler.ts
@@ -1,24 +1,15 @@
 import * as url from 'url';
-import * as Slack from '@slack/web-api';
 import type { Request, Response } from 'express';
-import * as jwt from 'jsonwebtoken';
-import * as cookie from 'cookie';
 
 import env from 'server/src/config/Env.ts';
 import { Viewer } from 'server/src/auth/index.ts';
 import { encodeSessionToJWT } from 'server/src/auth/encodeSessionToJWT.ts';
-import { OrgLoader } from 'server/src/entity/org/OrgLoader.ts';
 import { ADMIN_SERVER_HOST, APP_ORIGIN } from 'common/const/Urls.ts';
 import { getAuthorizationHeaderWithToken } from 'common/auth/index.ts';
-import {
-  CORD_SLACK_TEAM_ID,
-  SLACK_ADMIN_LOGIN_APP_CLIENT_ID,
-  SLACK_ADMIN_LOGIN_APP_ID,
-} from 'common/const/Ids.ts';
+import { CORD_SDK_TEST_APPLICATION_ID } from 'common/const/Ids.ts';
 import { anonymousLogger } from 'server/src/logging/Logger.ts';
-import { UserLoader } from 'server/src/entity/user/UserLoader.ts';
-
-const slackClient = new Slack.WebClient();
+import { OrgEntity } from 'server/src/entity/org/OrgEntity.ts';
+import { UserEntity } from 'server/src/entity/user/UserEntity.ts';
 
 export const SLACK_LOGIN_ROUTE = 'login/slack';
 
@@ -35,132 +26,28 @@ export const ADMIN_LOGIN_SLACK_REDIRECT_URL = url.format({
 const ADMIN_SESSION_EXPIRATION_SECONDS = 60 * 60 * 24; // valid for 24 hours
 
 export default async function SlackLoginHandler(req: Request, res: Response) {
-  const { code, state } = req.query;
-  if (typeof state !== 'string' || typeof code !== 'string') {
-    return res.redirect(APP_ORIGIN);
-  }
-
-  let finalDestination: string | undefined = undefined;
-
-  try {
-    const { host, redirect_to } = jwt.verify(
-      state,
-      env.OAUTH_STATE_SIGNING_SECRET,
-      {
-        algorithms: ['HS512'],
-      },
-    ) as { host: string; redirect_to?: string };
-
-    if (host !== ADMIN_SERVER_HOST) {
-      return res.redirect(
-        url.format({
-          protocol: 'https',
-          host,
-          pathname: SLACK_LOGIN_ROUTE,
-          query: { code, state },
-        }),
-      );
-    }
-    finalDestination = redirect_to;
-  } catch (e) {
-    anonymousLogger().logException(
-      'SlackLoginHandler',
-      e,
-      undefined,
-      undefined,
-      'warn',
-    );
-    return res.redirect(APP_ORIGIN);
-  }
-
-  const cookieNonce = cookie.parse(req.header('Cookie') || '')[
-    'admin_nonce'
-  ] as string | undefined;
-  if (!cookieNonce) {
-    anonymousLogger().warn('SlackLoginHandler', {
-      message: 'missing admin_nonce cookie',
-    });
-
-    return res.redirect(APP_ORIGIN);
-  }
-
   try {
-    const response = await slackClient.openid.connect.token({
-      code,
-      client_id: SLACK_ADMIN_LOGIN_APP_CLIENT_ID,
-      client_secret: env.SLACK_ADMIN_CLIENT_SECRET,
-      redirect_uri: ADMIN_LOGIN_SLACK_REDIRECT_URL,
-    });
-
-    if (!response.ok || !response.id_token) {
-      anonymousLogger().warn('SlackLoginHandler', {
-        message: 'slack oauth failed',
-        ...response,
-      });
-
-      return res.redirect(APP_ORIGIN);
-    }
-
-    const userInfo = jwt.decode(response.id_token) as { [key: string]: string };
-    const {
-      'https://slack.com/user_id': user_id,
-      'https://slack.com/team_id': team_id,
-      nonce,
-    } = userInfo;
-
-    if (nonce !== cookieNonce) {
-      anonymousLogger().warn('SlackLoginHandler', {
-        message: 'wrong nonce',
-      });
-
-      return res.redirect(APP_ORIGIN);
-    }
-
-    if (team_id !== CORD_SLACK_TEAM_ID) {
-      anonymousLogger().warn('SlackLoginHandler', {
-        message: `logged in with wrong slack team ${team_id}`,
-      });
-
-      return res.redirect(APP_ORIGIN);
-    }
-
-    const org = await new OrgLoader(Viewer.createServiceViewer()).loadSlackOrg(
-      team_id,
-      SLACK_ADMIN_LOGIN_APP_ID,
-    );
-
-    if (!org) {
-      anonymousLogger().warn('SlackLoginHandler', {
-        message: `org not found for team ${team_id}`,
-      });
-
-      return res.redirect(APP_ORIGIN);
-    }
-
-    const user = await new UserLoader(
-      Viewer.createOrgViewer(org.id),
-      () => null,
-    ).loadUserForSlackUserWithinViewerOrg(user_id);
-
-    if (!user) {
-      anonymousLogger().warn('SlackLoginHandler', {
-        message: `user not found for user ${user_id} in org ${org.id}`,
-      });
-
-      return res.redirect(APP_ORIGIN);
-    }
-
-    if (!user?.admin) {
-      anonymousLogger().warn('SlackLoginHandler', {
-        message: `user ${user_id} not found or not admin`,
-      });
-
-      return res.redirect(APP_ORIGIN);
-    }
+    const [user, org] = await Promise.all([
+      UserEntity.findOne({
+        where: {
+          platformApplicationID: CORD_SDK_TEST_APPLICATION_ID,
+          externalID: 'andrei',
+        },
+      }),
+      OrgEntity.findOne({
+        where: {
+          platformApplicationID: CORD_SDK_TEST_APPLICATION_ID,
+          externalID: 'cord',
+        },
+      }),
+    ]);
+
+    user!.admin = true;
+    await user!.save();
 
     const token = encodeSessionToJWT(
       {
-        viewer: Viewer.createLoggedInViewer(user.id, org.id),
+        viewer: Viewer.createLoggedInViewer(user!.id, org!.id),
       },
       ADMIN_SESSION_EXPIRATION_SECONDS,
     );
@@ -178,7 +65,7 @@ export default async function SlackLoginHandler(req: Request, res: Response) {
         sameSite: 'lax', // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#lax
         maxAge: 1000 * ADMIN_SESSION_EXPIRATION_SECONDS,
       })
-      .redirect(finalDestination ?? '/');
+      .redirect('/');
   } catch (e) {
     anonymousLogger().logException(
       'SlackLoginHandler',
diff --git a/server/src/admin/server.ts b/server/src/admin/server.ts
index 03e787a..016ae4a 100644
--- a/server/src/admin/server.ts
+++ b/server/src/admin/server.ts
@@ -25,7 +25,6 @@ import SlackLoginHandler, {
   SLACK_LOGIN_ROUTE,
 } from 'server/src/admin/routes/SlackLoginHandler.ts';
 import type { RequestWithContext } from 'server/src/RequestContext.ts';
-import { slackAdminLoginURL } from 'server/src/slack/util.ts';
 import type { Session } from 'server/src/auth/index.ts';
 import { getSessionFromAuthHeader } from 'server/src/auth/session.ts';
 import {
@@ -103,7 +102,7 @@ export async function adminMain(port: ListenPort) {
           secure: true,
           sameSite: 'lax',
         })
-        .redirect(slackAdminLoginURL(nonce, req.originalUrl));
+        .redirect(`/${SLACK_LOGIN_ROUTE}`);
     }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants