110 lines
2.9 KiB
TypeScript
110 lines
2.9 KiB
TypeScript
/**
|
|
* Copyright 2025 LY Corporation
|
|
*
|
|
* LY Corporation licenses this file to you under the Apache License,
|
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at:
|
|
*
|
|
* https://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
import type { NextApiRequest } from 'next';
|
|
|
|
import { supportExternalUrl } from './support-external';
|
|
|
|
type StoredJwt = { accessToken?: string };
|
|
|
|
export interface SupportPrincipal {
|
|
user_id: string;
|
|
tenant_id: string;
|
|
email?: string | null;
|
|
name?: string | null;
|
|
department?: string | null;
|
|
phone_number?: string | null;
|
|
is_admin?: boolean;
|
|
is_system_admin?: boolean;
|
|
roles?: string[];
|
|
}
|
|
|
|
const SUPPORT_MANAGER_ROLES = new Set([
|
|
'SYSTEM_ADMIN',
|
|
'SUPER_ADMIN',
|
|
'PROJECT_MANAGER',
|
|
]);
|
|
|
|
export const isSupportAdmin = (principal: SupportPrincipal) =>
|
|
principal.is_admin === true ||
|
|
principal.is_system_admin === true ||
|
|
(Array.isArray(principal.roles) &&
|
|
principal.roles.some((role) => SUPPORT_MANAGER_ROLES.has(role)));
|
|
|
|
export const applySupportCommentPermissions = <
|
|
T extends {
|
|
author_id: string;
|
|
author_tenant_id: string;
|
|
},
|
|
>(comment: T, principal: SupportPrincipal) => {
|
|
const isAuthor =
|
|
comment.author_id === principal.user_id &&
|
|
comment.author_tenant_id === principal.tenant_id;
|
|
const canManage = isSupportAdmin(principal);
|
|
|
|
return {
|
|
...comment,
|
|
can_edit: canManage || isAuthor,
|
|
can_delete: canManage || isAuthor,
|
|
};
|
|
};
|
|
|
|
export const getSupportAuthHeaders = (
|
|
req: NextApiRequest,
|
|
): Record<string, string> => {
|
|
const authorization = req.headers.authorization;
|
|
if (authorization) return { Authorization: authorization };
|
|
|
|
const rawCookie = req.cookies.jwt;
|
|
if (!rawCookie) return {};
|
|
|
|
try {
|
|
const parsed = JSON.parse(decodeURIComponent(rawCookie)) as StoredJwt;
|
|
return parsed.accessToken ?
|
|
{ Authorization: 'Bearer ' + parsed.accessToken }
|
|
: {};
|
|
} catch {
|
|
return {};
|
|
}
|
|
};
|
|
|
|
export const getSupportPrincipal = async (
|
|
req: NextApiRequest,
|
|
): Promise<SupportPrincipal | null> => {
|
|
const headers = getSupportAuthHeaders(req);
|
|
if (!headers.Authorization) return null;
|
|
|
|
try {
|
|
const response = await fetch(supportExternalUrl('/access'), {
|
|
headers,
|
|
});
|
|
if (!response.ok) return null;
|
|
|
|
const data = (await response.json()) as Partial<SupportPrincipal>;
|
|
if (
|
|
typeof data.user_id !== 'string' ||
|
|
typeof data.tenant_id !== 'string' ||
|
|
!data.user_id ||
|
|
!data.tenant_id
|
|
) {
|
|
return null;
|
|
}
|
|
|
|
return data as SupportPrincipal;
|
|
} catch {
|
|
return null;
|
|
}
|
|
};
|