Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions Common/Models/DatabaseModels/WorkspaceNotificationRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,43 @@ class WorkspaceNotificationRule extends BaseModel {
})
public workspaceType?: WorkspaceType = undefined;

@ColumnAccessControl({
create: [
Permission.ProjectAdmin,
Permission.ProjectOwner,
Permission.ProjectMember,
Permission.CreateWorkspaceNotificationRule,
],
read: [
Permission.ProjectAdmin,
Permission.ProjectOwner,
Permission.ProjectMember,
Permission.ReadWorkspaceNotificationRule,
Permission.ReadAllProjectResources,
],
update: [
Permission.ProjectAdmin,
Permission.ProjectOwner,
Permission.ProjectMember,
Permission.EditWorkspaceNotificationRule,
],
})
@Index()
@TableColumn({
type: TableColumnType.ObjectID,
required: true,
canReadOnRelationQuery: true,
title: "Workspace Project Auth Token ID",
description:
"Workspace project auth token ID for this rule (used when multiple workspaces are connected)",
})
@Column({
type: ColumnType.ObjectID,
nullable: false,
transformer: ObjectID.getDatabaseTransformer(),
})
public workspaceProjectAuthTokenId?: ObjectID = undefined;

@ColumnAccessControl({
create: [
Permission.ProjectAdmin,
Expand Down
24 changes: 24 additions & 0 deletions Common/Models/DatabaseModels/WorkspaceUserAuthToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface MiscData {

export interface SlackMiscData extends MiscData {
userId: string;
teamId?: string;
}

@TenantColumn("projectId")
Expand Down Expand Up @@ -153,6 +154,29 @@ class WorkspaceUserAuthToken extends BaseModel {
})
public workspaceType?: WorkspaceType = undefined;

@ColumnAccessControl({
create: [Permission.CurrentUser],
read: [Permission.CurrentUser],
update: [],
})
@TableColumn({
title: "Workspace Project ID",
description:
"Project ID in the Workspace (e.g., Slack team ID, Microsoft Teams team ID)",
required: false,
unique: false,
type: TableColumnType.LongText,
canReadOnRelationQuery: true,
})
@Column({
type: ColumnType.LongText,
length: ColumnLength.LongText,
unique: false,
nullable: true,
})
@Index()
public workspaceProjectId?: string = undefined;

@ColumnAccessControl({
create: [Permission.CurrentUser],
read: [Permission.CurrentUser],
Expand Down
51 changes: 43 additions & 8 deletions Common/Server/API/MicrosoftTeamsAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -422,7 +422,25 @@ export default class MicrosoftTeamsAPI {
logger.debug("User Profile: ");
logger.debug(userProfile);

await WorkspaceUserAuthTokenService.refreshAuthToken({
const existingProjectAuth: WorkspaceProjectAuthToken | null =
await WorkspaceProjectAuthTokenService.getProjectAuth({
projectId: new ObjectID(projectId),
workspaceType: WorkspaceType.MicrosoftTeams,
});

const userAuthData: {
projectId: ObjectID;
userId: ObjectID;
workspaceType: WorkspaceType;
authToken: string;
workspaceUserId: string;
miscData: {
userId: string;
displayName?: string;
email?: string;
};
workspaceProjectId?: string;
} = {
projectId: new ObjectID(projectId),
userId: new ObjectID(userId),
workspaceType: WorkspaceType.MicrosoftTeams,
Expand All @@ -435,15 +453,16 @@ export default class MicrosoftTeamsAPI {
(userProfile["mail"] as string) ||
(userProfile["userPrincipalName"] as string),
},
});
};

// Check if admin consent is already granted
const existingProjectAuth: WorkspaceProjectAuthToken | null =
await WorkspaceProjectAuthTokenService.getProjectAuth({
projectId: new ObjectID(projectId),
workspaceType: WorkspaceType.MicrosoftTeams,
});
if (existingProjectAuth?.workspaceProjectId) {
userAuthData.workspaceProjectId =
existingProjectAuth.workspaceProjectId;
}

await WorkspaceUserAuthTokenService.refreshAuthToken(userAuthData);

// Check if admin consent is already granted
if (
existingProjectAuth &&
(existingProjectAuth.miscData as any)?.adminConsentGranted
Expand Down Expand Up @@ -776,6 +795,22 @@ export default class MicrosoftTeamsAPI {
miscData: mergedMiscData,
});

await WorkspaceUserAuthTokenService.updateBy({
query: {
projectId: new ObjectID(projectId),
userId: new ObjectID(userId),
workspaceType: WorkspaceType.MicrosoftTeams,
},
data: {
workspaceProjectId: tenantId,
},
skip: 0,
limit: 1,
props: {
isRoot: true,
},
});

return Response.redirect(
req,
res,
Expand Down
119 changes: 95 additions & 24 deletions Common/Server/API/SlackAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -275,16 +275,39 @@ export default class SlackAPI {
},
});

await WorkspaceUserAuthTokenService.refreshAuthToken({
const userMiscData: {
userId: string;
teamId?: string;
} = {
userId: slackUserId || "",
};

if (slackTeamId) {
userMiscData.teamId = slackTeamId;
}

const userAuthData: {
projectId: ObjectID;
userId: ObjectID;
workspaceType: WorkspaceType;
authToken: string;
workspaceUserId: string;
miscData: typeof userMiscData;
workspaceProjectId?: string;
} = {
projectId: new ObjectID(projectId),
userId: new ObjectID(userId),
workspaceType: WorkspaceType.Slack,
authToken: slackUserAccessToken || "",
workspaceUserId: slackUserId || "",
miscData: {
userId: slackUserId || "",
},
});
miscData: userMiscData,
};

if (slackTeamId) {
userAuthData.workspaceProjectId = slackTeamId;
}

await WorkspaceUserAuthTokenService.refreshAuthToken(userAuthData);

// return back to dashboard after successful auth.
Response.redirect(req, res, slackIntegrationPageUrl);
Expand Down Expand Up @@ -441,15 +464,36 @@ export default class SlackAPI {
*/

/*
* check if the team id matches the project id.
* get project auth.
* check if the team id matches the project workspace.
* get project auth based on team id.
*/

const teamIdFromSlack: string | undefined =
idToken["https://slack.com/team_id"]?.toString();

// If state is provided, enforce workspace selection.
const expectedTeamId: string | undefined =
req.query["state"]?.toString();

if (expectedTeamId && teamIdFromSlack) {
if (expectedTeamId !== teamIdFromSlack) {
return Response.redirect(
req,
res,
slackIntegrationPageUrl.addQueryParam(
"error",
"Looks like you are trying to sign in to a different slack workspace. Please try again and sign in to the selected workspace.",
),
);
}
}

const projectAuth: WorkspaceProjectAuthToken | null =
await WorkspaceProjectAuthTokenService.findOneBy({
query: {
projectId: new ObjectID(projectId),
workspaceType: WorkspaceType.Slack,
workspaceProjectId: teamIdFromSlack,
},
select: {
workspaceProjectId: true,
Expand All @@ -460,19 +504,16 @@ export default class SlackAPI {
},
});

// cehck if the workspace project id is same as the team id.
// check if the workspace project id is same as the team id.
if (projectAuth) {
logger.debug("Project Auth: ");
logger.debug(projectAuth.workspaceProjectId);
logger.debug("Response Team ID: ");
logger.debug(idToken["https://slack.com/team_id"]);
logger.debug(teamIdFromSlack);
logger.debug("Response User ID: ");
logger.debug(idToken["https://slack.com/user_id"]);

if (
projectAuth.workspaceProjectId?.toString() !==
idToken["https://slack.com/team_id"]?.toString()
) {
if (projectAuth.workspaceProjectId?.toString() !== teamIdFromSlack) {
const teamName: string | undefined = (
projectAuth.miscData as SlackMiscData
)?.teamName;
Expand All @@ -495,7 +536,7 @@ export default class SlackAPI {
res,
slackIntegrationPageUrl.addQueryParam(
"error",
"Looks like this OneUptime project is not connected to any slack workspace. Please try again and sign in to the workspace",
"Looks like this OneUptime project is not connected to the selected slack workspace. Please connect the workspace first.",
),
);
}
Expand Down Expand Up @@ -524,7 +565,9 @@ export default class SlackAPI {
workspaceUserId: slackUserId || "",
miscData: {
userId: slackUserId || "",
teamId: teamIdFromSlack || "",
},
workspaceProjectId: teamIdFromSlack || "",
});

// return back to dashboard after successful auth.
Expand Down Expand Up @@ -714,12 +757,29 @@ export default class SlackAPI {
);
}

const workspaceProjectAuthTokenId: string | undefined =
req.query["workspaceProjectAuthTokenId"]?.toString();

// Get Slack project auth
const projectAuthQuery: {
projectId: ObjectID;
workspaceType: WorkspaceType;
workspaceProjectAuthTokenId?: ObjectID;
} = {
projectId: props.tenantId,
workspaceType: WorkspaceType.Slack,
};

if (workspaceProjectAuthTokenId) {
projectAuthQuery.workspaceProjectAuthTokenId = new ObjectID(
workspaceProjectAuthTokenId,
);
}

const projectAuth: WorkspaceProjectAuthToken | null =
await WorkspaceProjectAuthTokenService.getProjectAuth({
projectId: props.tenantId,
workspaceType: WorkspaceType.Slack,
});
await WorkspaceProjectAuthTokenService.getProjectAuth(
projectAuthQuery,
);

if (!projectAuth || !projectAuth.authToken) {
return Response.sendErrorResponse(
Expand All @@ -736,17 +796,28 @@ export default class SlackAPI {
let updatedProjectAuth: WorkspaceProjectAuthToken | null = projectAuth;

if (!(projectAuth.miscData as SlackMiscData)?.channelCache) {
await SlackUtil.getAllWorkspaceChannels({
const getChannelsData: {
authToken: string;
projectId: ObjectID;
workspaceProjectAuthTokenId?: ObjectID;
} = {
authToken: projectAuth.authToken,
projectId: props.tenantId,
});
};

if (workspaceProjectAuthTokenId) {
getChannelsData.workspaceProjectAuthTokenId = new ObjectID(
workspaceProjectAuthTokenId,
);
}

await SlackUtil.getAllWorkspaceChannels(getChannelsData);

// Re-fetch to return the latest cached object
updatedProjectAuth =
await WorkspaceProjectAuthTokenService.getProjectAuth({
projectId: props.tenantId,
workspaceType: WorkspaceType.Slack,
});
await WorkspaceProjectAuthTokenService.getProjectAuth(
projectAuthQuery,
);
}

const channelCache: {
Expand Down
Loading
Loading