Skip to content

Commit

Permalink
feat(Mattermost Plugin): Configure notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
Dschoordsch committed Feb 20, 2025
1 parent baf76ce commit 88d5e62
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 11 deletions.
29 changes: 29 additions & 0 deletions packages/server/graphql/private/mutations/loginMattermost.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import AuthToken from '../../../database/types/AuthToken'
import getKysely from '../../../postgres/getKysely'
import encodeAuthToken from '../../../utils/encodeAuthToken'
import {MutationResolvers} from '../resolverTypes'

const loginMattermost: MutationResolvers['loginMattermost'] = async (
_source,
{email}
) => {
const pg = getKysely()
const user = await pg
.selectFrom('User')
.selectAll()
.where('email', '=', email)
.executeTakeFirst()
if (!user) {
return {error: {message: 'Unknown user'}}
}
const {id: userId, tms} = user
const authToken = new AuthToken({sub: userId, tms})

return {
userId,
authToken: encodeAuthToken(authToken),
isNewUser: false
}
}

export default loginMattermost
10 changes: 10 additions & 0 deletions packages/server/graphql/private/typeDefs/Mutation.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,16 @@ type Mutation {
samlName: ID!
): UserLogInPayload!

"""
Log in with email from a trusted Mattermost
"""
loginMattermost(
"""
The email of the user
"""
email: Email!
): UserLogInPayload!

"""
Processes recurrence for meetings that should start and end.
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import {sql} from 'kysely'
import IntegrationProviderId from '~/shared/gqlIds/IntegrationProviderId'
import {OAuth2AuthorizeResponse} from '../../../integrations/OAuth2Manager'
import GcalOAuth2Manager from '../../../integrations/gcal/GcalOAuth2Manager'
import GitLabOAuth2Manager from '../../../integrations/gitlab/GitLabOAuth2Manager'
import JiraServerOAuth1Manager, {
OAuth1Auth
} from '../../../integrations/jiraServer/JiraServerOAuth1Manager'
import getKysely from '../../../postgres/getKysely'
import {IntegrationProviderAzureDevOps} from '../../../postgres/queries/getIntegrationProvidersByIds'
import AzureDevOpsServerManager from '../../../utils/AzureDevOpsServerManager'
import {analytics} from '../../../utils/analytics/analytics'
import {getUserId, isTeamMember} from '../../../utils/authorization'
import standardError from '../../../utils/standardError'
import updateRepoIntegrationsCacheByPerms from '../../queries/helpers/updateRepoIntegrationsCacheByPerms'
import {MutationResolvers} from '../resolverTypes'
import {link} from 'fs'

interface OAuth2Auth {
accessToken: string
refreshToken: string
scopes: string
expiresAt?: Date | null
}

const linkNotificationChannel: MutationResolvers['linkNotificationChannel'] = async (
_source,
{providerId, teamId, channelId},
context
) => {
const {authToken, dataLoader} = context
const viewerId = getUserId(authToken)
const pg = getKysely()

//AUTH
if (!isTeamMember(authToken, teamId)) {
return standardError(new Error('Attempted teamId spoof'), {userId: viewerId})
}

// VALIDATION
const providerDbId = IntegrationProviderId.split(providerId)
const integrationProvider = await dataLoader.get('integrationProviders').load(providerDbId)

if (!integrationProvider) {
return standardError(
new Error(`Unable to find appropriate integration provider for providerId ${providerId}`),
{
userId: viewerId
}
)
}

const {service} = integrationProvider
if (service !== 'msTeams' && service !== 'mattermost') {
return standardError(
new Error(`Unsupported notification service`), {userId: viewerId, tags: {service}}
)
}

// RESOLUTION
await pg
.insertInto('TeamNotificationSettings')
.columns(['providerId', 'teamId', 'events', 'channelId'])
.values(() => ({
providerId: providerDbId,
teamId,
events: sql`enum_range(NULL::"SlackNotificationEventEnum")`,
channelId
}))
.onConflict((oc) => oc.doNothing())
.execute()

const data = {userId: viewerId, teamId, service}
return data
}

export default linkNotificationChannel
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""
Return object for AddTeamMemberIntegrationAuthPayload
Return object for addTeamMemberIntegrationAuth
"""
union AddTeamMemberIntegrationAuthPayload = ErrorPayload | AddTeamMemberIntegrationAuthSuccess
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"""
Return object for linkNotificationChannel
"""
union LinkNotificationChannelPayload = ErrorPayload | LinkNotificationChannelSuccess
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
type LinkNotificationChannelSuccess {
}
17 changes: 17 additions & 0 deletions packages/server/graphql/public/typeDefs/Mutation.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -1454,6 +1454,23 @@ type Mutation {
redirectUri: URL
): AddTeamMemberIntegrationAuthPayload!

linkNotificationChannel(
"""
The ID of the auth to link the channel to
"""
providerId: ID!

"""
The team to link the channel to
"""
teamId: ID!

"""
The channel to link
"""
channelId: ID!
): LinkNotificationChannelPayload!

"""
Add the transcription bot to the Zoom meeting
"""
Expand Down
26 changes: 16 additions & 10 deletions packages/server/integrations/mattermost/mattermostWebhookHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,26 @@ import appOrigin from '../../appOrigin'
import AuthToken from '../../database/types/AuthToken'
import uWSAsyncHandler from '../../graphql/uWSAsyncHandler'
import parseBody from '../../parseBody'
import getKysely from '../../postgres/getKysely'
import encodeAuthToken from '../../utils/encodeAuthToken'
import publishWebhookGQL from '../../utils/publishWebhookGQL'

const MATTERMOST_SECRET = process.env.MATTERMOST_SECRET


const login = async (email: string) => {
const pg = getKysely()
const user = await pg
.selectFrom('User')
.selectAll()
.where('email', '=', email)
.executeTakeFirstOrThrow()
const authToken = new AuthToken({sub: user.id, tms: user.tms})
return encodeAuthToken(authToken)
const query = `
mutation LoginMattermost($email: String!) {
loginMattermost(email: $email) {
error {
message
}
authToken
}
}
`

const loginResult = await publishWebhookGQL<any>(query, {email})
const {error, authToken} = loginResult?.data?.loginMattermost ?? {}
return authToken
}

const mattermostWebhookHandler = uWSAsyncHandler(async (res: HttpResponse, req: HttpRequest) => {
Expand Down

0 comments on commit 88d5e62

Please sign in to comment.