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

[RFC] Add GitLab auth #5594

Closed
wants to merge 29 commits into from
Closed

[RFC] Add GitLab auth #5594

wants to merge 29 commits into from

Conversation

jordanh
Copy link
Contributor

@jordanh jordanh commented Nov 1, 2021

No description provided.

@jordanh jordanh changed the title [WIP] Add gitlab auth [WIP] Add gitlab auth [base: add-mattermost] Nov 1, 2021
@jordanh jordanh changed the base branch from master to add-mattermost November 6, 2021 22:03
@jordanh jordanh changed the base branch from add-mattermost to master November 6, 2021 22:04
@jordanh jordanh changed the base branch from master to add-mattermost November 12, 2021 04:50
@@ -30,7 +30,15 @@ const useMenu = <T extends HTMLElement = HTMLButtonElement>(
if (originCoords) {
;(originRef as any).current = {getBoundingClientRect: () => originCoords} as RectElement
}
const {portal, closePortal, togglePortal, portalStatus, setPortalStatus, openPortal} = usePortal({
const {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes here are only to return terminatePortal so it may be used

Copy link
Contributor Author

@jordanh jordanh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self-review pass one: will make some small fixups

@@ -8,7 +8,8 @@ export default class DemoUser {
createdAt = new Date().toJSON()
email: string
featureFlags = {
jira: false
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I presume it is ok to remove this flag of yesteryear...

packages/client/types/ClientActionVarsT.ts Outdated Show resolved Hide resolved
packages/client/types/constEnums.ts Outdated Show resolved Hide resolved
packages/server/graphql/types/GitHubIntegration.ts Outdated Show resolved Hide resolved
scripts/toolboxSrc/getClientActionVars.ts Outdated Show resolved Hide resolved
@jordanh jordanh changed the title [WIP] Add gitlab auth [base: add-mattermost] [RFC] Add gitlab auth [base: add-mattermost] Nov 13, 2021
@jordanh jordanh changed the base branch from add-mattermost to master November 13, 2021 23:37
@jordanh
Copy link
Contributor Author

jordanh commented Nov 13, 2021

👀 **Watch the demo & overview 👇 **

image

Specific requests from reviewers:

  • Are these tables IntegrationProvider and IntegrationToken architected well for our needs?
  • Are the introduced GraphQL types/query patterns roughly correct? What might need to change now to make this safe to try?
  • Are the introduced mutations roughly correct? What might need to change now to make this safe to try?
  • Is the postDeploy utility function to populate global providers safe to try?
  • What would you change about the GitLab auth implementation? (Or, is it safe enough to try?)
  • What might you change about the Mattermost auth implementation? (Or, is it safe enough to try?)
  • Is the IntegrationServerManager pattern in the right direction? What might need to change now?
  • Are the minor refactoring (e.g. separating some dataLoaders into their own files) beneficial enough to include (or should these changes be reverted)?
  • Anything else you notice?

@BartoszJarocki
Copy link
Contributor

Great work @jordanh!

Regarding the database schema, I'd avoid having separate columns for things that are related only to a particular provider type. For example, when the provider type is different than OAuth we'll have all OAuth columns empty. It'd be better to keep provider data in a separate JSONB column. This way we can deserialize data into a proper type when querying and no matter what provider type we add, we won't need to change the DB schema. And we avoid having provider-specific columns in a generic table. Then, the table would look like

    CREATE TABLE IF NOT EXISTS "IntegrationProvider" (
      "id" INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
      "createdAt" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
      "updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
      "type" "IntegrationProviderTypesEnum" NOT NULL,
      "tokenType" "IntegrationProviderTokenTypeEnum" NOT NULL,
      "scope" "IntegrationProviderScopesEnum" NOT NULL,
      "orgId" VARCHAR(100),
      "teamId" VARCHAR(100),
      "isActive" BOOLEAN DEFAULT TRUE NOT NULL,
      "name" VARCHAR(250) NOT NULL,
      "metadata" JSONB NOT NULL DEFAULT '{}', // here we'd store all the oauth/whatever data
       ...
    );

Depending on what differences are in IntegrationToken table between providers I'd do the same there (if depending on provider type authentication is or could be different), so instead of columns, just use JSONB.

Copy link
Contributor

@tianrunhe tianrunhe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work! I second Bartosz's suggestion and made some minor suggestions

'Content-Type': 'application/json'
}

const uri = `${this.provider.serverBaseUri}/oauth/token?${stringify(queryParams)}`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does most of the services have OAuth token request URI like this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will almost certainly need to be parameterized

try {
const r = await getRethink()
await flushSocketConnections()
await storePersistedQueries()
await r.getPoolMaster().drain()

const pg = getPg()
await upsertGlobalIntegrationProvidersFromEnv()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would there be delay between inserting the rows and app starting?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pool is drained, so these row(s) will be updated before the app starts

}

const data = {userId: viewerId, teamId}
publish(SubscriptionChannel.TEAM, teamId, 'AddIntegrationProvider', data, subOptions)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the subscription level should be depend on the scope.

Copy link
Contributor Author

@jordanh jordanh Dec 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed. If/when we update the Org view to manage integration providers, this logic will need to be added here (and the subscriptions return types will need to be updated)

description: "The provider's unique identifier",
resolve: ({id}) => IntegrationProviderId.join(id)
},
type: {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have cases where we expect to have more than one token type per integration provider?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's certainly possible! For Mattermost we could support Oauth2, PATs, and Webhooks if we wanted to as Mattermost supports each of these modes

"oauthClientSecret" VARCHAR(2600),
"createdAt" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
"updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
UNIQUE("scopeGlobal", "type"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

neat

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:)

"expiresAt" TIMESTAMP WITH TIME ZONE,
"oauthRefreshToken" VARCHAR(2600),
"oauthScopes" VARCHAR(100)[],
"attributes" JSONB,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems rather vague, if we follow Bartosz's recommendation we don't need that. Is this meant to store integration specific settings like your last github search?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I agree with you and @BartoszJarocki, let's move these oauth* vars to the attributes column.

If an implementer wanted to, we could use attributes to store things like your last github search, although, personally I think these should likely belong on their own table. I mostly intended these abstracts to only handle auth. The fact we conflate auth and state is a bit hacky IMO.

// this table has a composite primary key (userId, teamId),
// which cannot use the index with a WHERE IN or JOIN on VALUES
// so if we want to query multiple userIds/teamIds, just call this multiple times
// if we want to query multiple userIds/teamIds, just call this multiple times
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is out of scope of this RFC, but you removed the answer to 'why?' from the comment, which is the important bit. If I would look at this in a month I wouldn't know why we wouldn't just implement it for multiple userId, teamId pairs.

Copy link
Contributor Author

@jordanh jordanh Dec 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment was originally written by @mattkrick. He tells me this is no longer true (or perhaps was never true) when we spoke sync about this

CREATE INDEX IF NOT EXISTS "idx_IntegrationProvider_orgId" ON "IntegrationProvider"("orgId");
CREATE INDEX IF NOT EXISTS "idx_IntegrationProvider_teamId" ON "IntegrationProvider"("teamId");
CREATE TABLE IF NOT EXISTS "IntegrationToken" (
"teamId" VARCHAR(100) NOT NULL,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some thoughts on whether or not we should have the IntegrationToken per User, Team, Provider (1, current solution) or just User, Provider and then a separate relation TeamMemberIntegration (2).

  1. Pro
  • easy, it's what we have now
  • no joins
    Con
  • updates to the token need to be propagated to all rows
  1. Pro
  • it would simplify refreshing a token and similar operations on the token itself, because it would not require to update multiple rows.
  • integration specific settings could be stored on TeamMemberIntegration and thus could also be stored without having a token
    Con
  • additional join for retrieving the token
  • the key User,IntegrationProvider might not work. While I think it is unlikely that someone wants to have access to Jira with 2 different accounts in 1 team, the likelyhood increases when it's spread over multiple teams. This is probably still not a use case, only wanted to mention it.

Before, when we did an "import someImage 'static/some.png'", file-loader
would give us a path relative to the root like 'dist/[hash].png'. Now,
we'll:

   - Use the CDN if it's configured
   - Serve up the assets via `static/` otherwise

Also:
   - makeMattermostAttachments fixed to server assets from CDN
@BartoszJarocki BartoszJarocki changed the title [RFC] Add gitlab auth [base: add-mattermost] [RFC] Add Gitlab auth Dec 13, 2021
@BartoszJarocki BartoszJarocki changed the title [RFC] Add Gitlab auth [RFC] Add GitLab auth Dec 13, 2021
BartoszJarocki and others added 3 commits December 13, 2021 18:49
Also:
   - Fixed bugs in (Add|Update)IntegrationProvier mutations where
     we were returning the wrong error type (and sometimes the
     incorrect error variable!)
   - Added comments and TODOs to enhance AddIntegrationProvider
     when an oauth token is specified to work like AddIntegrationToken and,
   - validate that new tokens are functional by loading the appropriate ServerManager
     and running a method that tests that ServerManager's connection
@BartoszJarocki
Copy link
Contributor

Closing in favor of #5805

@mattkrick mattkrick deleted the add-gitlab-auth branch June 3, 2022 23:03
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

Successfully merging this pull request may close these issues.

GitLab auth shipped behind feature flag, Mattermost to new Auth tables
5 participants