mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-13 07:50:22 +02:00
Merge branch 'aig' of github.com:fosrl/pangolin into aig
This commit is contained in:
+5
-5
@@ -1699,16 +1699,16 @@
|
||||
"virtualApiKeysFilterUnassigned": "Unassigned",
|
||||
"myVirtualApiKeysTitle": "Your API Keys",
|
||||
"myVirtualApiKeysDescription": "View your identity key and any virtual API keys attributed to you in this organization",
|
||||
"myVirtualApiKeysResourceTitle": "Your API Keys for This Resource",
|
||||
"myVirtualApiKeysResourceDescription": "View your identity key and virtual API keys attributed to you that can access this resource",
|
||||
"myVirtualApiKeysResourceTitle": "Your API Keys for {resourceName}",
|
||||
"myVirtualApiKeysResourceDescription": "View your identity key and virtual API keys attributed to you that can access {resourceName}",
|
||||
"myVirtualApiKeysIdentityTitle": "Identity Key",
|
||||
"myVirtualApiKeysIdentityHeadline": "Your Personal API Key",
|
||||
"myVirtualApiKeysIdentityDescription": "Your personal key for this organization. It is unique to your account and used to identify you when calling AI Gateway resources.",
|
||||
"myVirtualApiKeysIdentityResourceHeadline": "Your Personal API Key for This Resource",
|
||||
"myVirtualApiKeysIdentityResourceDescription": "Your personal key for this organization. Use it to call this AI Gateway resource.",
|
||||
"myVirtualApiKeysIdentityResourceHeadline": "Your Personal API Key for {resourceName}",
|
||||
"myVirtualApiKeysIdentityResourceDescription": "Your personal key for this organization. Use it to call {resourceName}.",
|
||||
"myVirtualApiKeysManualTitle": "Attributed Keys",
|
||||
"myVirtualApiKeysManualDescription": "Manual virtual API keys an admin associated with your account",
|
||||
"myVirtualApiKeysManualResourceDescription": "Manual virtual API keys associated with your account that can access this resource",
|
||||
"myVirtualApiKeysManualResourceDescription": "Manual virtual API keys associated with your account that can access {resourceName}",
|
||||
"myVirtualApiKeysManualEmpty": "No attributed keys yet",
|
||||
"myVirtualApiKeysKindUser": "Identity",
|
||||
"myVirtualApiKeysKindManual": "Manual",
|
||||
|
||||
@@ -263,28 +263,20 @@ export async function verifyResourceSession(
|
||||
);
|
||||
|
||||
if (action == "ACCEPT") {
|
||||
// Public inference still requires a virtual API key; do not
|
||||
// bypass that with an allow rule.
|
||||
if (mode === "inference") {
|
||||
logger.debug(
|
||||
"Rule ACCEPT ignored for inference; continuing to virtual API key check"
|
||||
);
|
||||
} else {
|
||||
logger.debug("Resource allowed by rule");
|
||||
logger.debug("Resource allowed by rule");
|
||||
|
||||
logRequestAudit(
|
||||
{
|
||||
action: true,
|
||||
reason: 100, // allowed by rule
|
||||
resourceId: resource.resourceId,
|
||||
orgId: resource.orgId,
|
||||
location: ipCC
|
||||
},
|
||||
parsedBody.data
|
||||
);
|
||||
logRequestAudit(
|
||||
{
|
||||
action: true,
|
||||
reason: 100, // allowed by rule
|
||||
resourceId: resource.resourceId,
|
||||
orgId: resource.orgId,
|
||||
location: ipCC
|
||||
},
|
||||
parsedBody.data
|
||||
);
|
||||
|
||||
return allowed(res, undefined, dontStripSession);
|
||||
}
|
||||
return allowed(res, undefined, dontStripSession);
|
||||
} else if (action == "DROP") {
|
||||
logger.debug("Resource denied by rule");
|
||||
|
||||
@@ -425,7 +417,7 @@ export async function verifyResourceSession(
|
||||
parsedBody.data
|
||||
);
|
||||
|
||||
return notAllowed(res, redirectPath);
|
||||
return notAllowed(res, redirectPath, resource.orgId);
|
||||
}
|
||||
|
||||
// check for access token in headers
|
||||
|
||||
@@ -42,6 +42,7 @@ export type GetResourceAuthInfoResponse = {
|
||||
skipToIdpId: number | null;
|
||||
orgId: string;
|
||||
postAuthPath: string | null;
|
||||
mode: string;
|
||||
};
|
||||
|
||||
export async function getResourceAuthInfo(
|
||||
@@ -227,7 +228,8 @@ export async function getResourceAuthInfo(
|
||||
whitelist: effectivePolicy?.emailWhitelistEnabled ?? false,
|
||||
skipToIdpId: effectivePolicy?.idpId ?? resource.skipToIdpId,
|
||||
orgId: resource.orgId,
|
||||
postAuthPath: resource.postAuthPath ?? null
|
||||
postAuthPath: resource.postAuthPath ?? null,
|
||||
mode: resource.mode
|
||||
},
|
||||
success: true,
|
||||
error: false,
|
||||
|
||||
@@ -123,10 +123,12 @@ export async function listMyVirtualApiKeys(
|
||||
}
|
||||
|
||||
let resourceId: number | undefined;
|
||||
let resourceName: string | undefined;
|
||||
if (resourceGuid) {
|
||||
const [resource] = await db
|
||||
.select({
|
||||
resourceId: resources.resourceId
|
||||
resourceId: resources.resourceId,
|
||||
name: resources.name
|
||||
})
|
||||
.from(resources)
|
||||
.where(
|
||||
@@ -147,6 +149,7 @@ export async function listMyVirtualApiKeys(
|
||||
}
|
||||
|
||||
resourceId = resource.resourceId;
|
||||
resourceName = resource.name;
|
||||
}
|
||||
|
||||
const { key: userKeyRow } = await getOrCreateUserVirtualApiKey({
|
||||
@@ -203,7 +206,8 @@ export async function listMyVirtualApiKeys(
|
||||
userKey: toKeyWithResources(userKeyRow, resourceIdsByKey),
|
||||
manualKeys: manualRows.map((row) =>
|
||||
toKeyWithResources(row, resourceIdsByKey)
|
||||
)
|
||||
),
|
||||
...(resourceName !== undefined ? { resourceName } : {})
|
||||
},
|
||||
success: true,
|
||||
error: false,
|
||||
|
||||
@@ -22,6 +22,7 @@ export type CreateOrEditVirtualApiKeyResponse = {
|
||||
export type ListMyVirtualApiKeysResponse = {
|
||||
userKey: VirtualApiKeyWithResources;
|
||||
manualKeys: VirtualApiKeyWithResources[];
|
||||
resourceName?: string | null;
|
||||
};
|
||||
|
||||
export type GetMyVirtualApiKeyResponse = {
|
||||
|
||||
@@ -18,7 +18,7 @@ import { cache } from "react";
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
const t = await getTranslations();
|
||||
return {
|
||||
title: t("myVirtualApiKeysResourceTitle")
|
||||
title: t("myVirtualApiKeysTitle")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -41,7 +41,9 @@ export default async function ResourceKeysPage(props: ResourceKeysPageProps) {
|
||||
const user = await getUser();
|
||||
|
||||
if (!user) {
|
||||
redirect("/");
|
||||
redirect(
|
||||
`/auth/resource/${encodeURIComponent(resourceGuid)}?redirect=${encodeURIComponent(`/${orgId}/resource/${resourceGuid}/keys`)}`
|
||||
);
|
||||
}
|
||||
|
||||
const cookieHeader = await authCookieHeader();
|
||||
@@ -111,11 +113,7 @@ export default async function ResourceKeysPage(props: ResourceKeysPageProps) {
|
||||
launcherMode
|
||||
showViewAsAdmin={isAdminOrOwner}
|
||||
>
|
||||
<UserVirtualApiKeys
|
||||
orgId={orgId}
|
||||
resourceGuid={resourceGuid}
|
||||
initialData={keysData}
|
||||
/>
|
||||
<UserVirtualApiKeys orgId={orgId} initialData={keysData} />
|
||||
</Layout>
|
||||
</UserProvider>
|
||||
);
|
||||
|
||||
@@ -71,6 +71,9 @@ export default async function ResourceAuthPage(props: {
|
||||
);
|
||||
}
|
||||
|
||||
const isInference = authInfo.mode === "inference";
|
||||
const keysPath = `/${authInfo.orgId}/resource/${authInfo.resourceGuid}/keys`;
|
||||
|
||||
const hasLoginPageDomain = await isOrgSubscribed(
|
||||
authInfo.orgId,
|
||||
tierMatrix.loginPageDomain
|
||||
@@ -159,7 +162,9 @@ export default async function ResourceAuthPage(props: {
|
||||
|
||||
if (user && !user.emailVerified && env.flags.emailVerificationRequired) {
|
||||
redirect(
|
||||
`/auth/verify-email?redirect=/auth/resource/${authInfo.resourceGuid}`
|
||||
`/auth/verify-email?redirect=${encodeURIComponent(
|
||||
`/auth/resource/${authInfo.resourceGuid}`
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -193,6 +198,16 @@ export default async function ResourceAuthPage(props: {
|
||||
);
|
||||
}
|
||||
|
||||
// Inference resources never establish a resource session on the inference
|
||||
// host. Authenticated users retrieve their virtual API key on the dashboard.
|
||||
if (isInference && user) {
|
||||
redirect(keysPath);
|
||||
}
|
||||
|
||||
// After password/pincode/SSO, do not send the browser back to the
|
||||
// inference host (session alone cannot pass Badger). Land on keys instead.
|
||||
const postAuthRedirect = isInference ? keysPath : redirectUrl;
|
||||
|
||||
if (!hasAuth) {
|
||||
// no authentication so always go straight to the resource
|
||||
redirect(redirectUrl);
|
||||
@@ -277,7 +292,7 @@ export default async function ResourceAuthPage(props: {
|
||||
<AutoLoginHandler
|
||||
resourceId={authInfo.resourceId}
|
||||
skipToIdpId={authInfo.skipToIdpId}
|
||||
redirectUrl={redirectUrl}
|
||||
redirectUrl={postAuthRedirect}
|
||||
orgId={build === "saas" ? authInfo.orgId : undefined}
|
||||
/>
|
||||
);
|
||||
@@ -315,7 +330,7 @@ export default async function ResourceAuthPage(props: {
|
||||
name: authInfo.resourceName,
|
||||
id: authInfo.resourceId
|
||||
}}
|
||||
redirect={redirectUrl}
|
||||
redirect={postAuthRedirect}
|
||||
idps={loginIdps}
|
||||
orgId={build === "saas" ? authInfo.orgId : undefined}
|
||||
branding={
|
||||
|
||||
@@ -4,11 +4,9 @@ import { useState } from "react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { AxiosResponse } from "axios";
|
||||
import moment from "moment";
|
||||
import { Badge } from "@app/components/ui/badge";
|
||||
import { Button } from "@app/components/ui/button";
|
||||
import CopyTextBox from "@app/components/CopyTextBox";
|
||||
import CopyToClipboard from "@app/components/CopyToClipboard";
|
||||
import SettingsSectionTitle from "@app/components/SettingsSectionTitle";
|
||||
import {
|
||||
SettingsContainer,
|
||||
SettingsFormCell,
|
||||
@@ -34,7 +32,6 @@ import {
|
||||
|
||||
type UserVirtualApiKeysProps = {
|
||||
orgId: string;
|
||||
resourceGuid?: string;
|
||||
initialData: ListMyVirtualApiKeysResponse;
|
||||
};
|
||||
|
||||
@@ -131,12 +128,12 @@ function IdentityKeyCenterpiece({
|
||||
orgId,
|
||||
virtualApiKeyId,
|
||||
lastChars,
|
||||
resourceGuid
|
||||
resourceName
|
||||
}: {
|
||||
orgId: string;
|
||||
virtualApiKeyId: string;
|
||||
lastChars: string;
|
||||
resourceGuid?: string;
|
||||
resourceName?: string | null;
|
||||
}) {
|
||||
const t = useTranslations();
|
||||
const preview = formatVirtualApiKeyPreview(virtualApiKeyId, lastChars);
|
||||
@@ -145,11 +142,11 @@ function IdentityKeyCenterpiece({
|
||||
virtualApiKeyId
|
||||
);
|
||||
const displayValue = credential ?? preview;
|
||||
const headline = resourceGuid
|
||||
? t("myVirtualApiKeysIdentityResourceHeadline")
|
||||
const headline = resourceName
|
||||
? t("myVirtualApiKeysIdentityResourceHeadline", { resourceName })
|
||||
: t("myVirtualApiKeysIdentityHeadline");
|
||||
const description = resourceGuid
|
||||
? t("myVirtualApiKeysIdentityResourceDescription")
|
||||
const description = resourceName
|
||||
? t("myVirtualApiKeysIdentityResourceDescription", { resourceName })
|
||||
: t("myVirtualApiKeysIdentityDescription");
|
||||
|
||||
return (
|
||||
@@ -220,17 +217,10 @@ function ManualKeyRow({
|
||||
|
||||
export default function UserVirtualApiKeys({
|
||||
orgId,
|
||||
resourceGuid,
|
||||
initialData
|
||||
}: UserVirtualApiKeysProps) {
|
||||
const t = useTranslations();
|
||||
|
||||
const title = resourceGuid
|
||||
? t("myVirtualApiKeysResourceTitle")
|
||||
: t("myVirtualApiKeysTitle");
|
||||
const description = resourceGuid
|
||||
? t("myVirtualApiKeysResourceDescription")
|
||||
: t("myVirtualApiKeysDescription");
|
||||
const resourceName = initialData.resourceName;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -239,7 +229,7 @@ export default function UserVirtualApiKeys({
|
||||
orgId={orgId}
|
||||
virtualApiKeyId={initialData.userKey.virtualApiKeyId}
|
||||
lastChars={initialData.userKey.lastChars}
|
||||
resourceGuid={resourceGuid}
|
||||
resourceName={resourceName}
|
||||
/>
|
||||
|
||||
{initialData.manualKeys.length > 0 ? (
|
||||
@@ -249,9 +239,10 @@ export default function UserVirtualApiKeys({
|
||||
{t("myVirtualApiKeysManualTitle")}
|
||||
</SectionTitle>
|
||||
<SettingsSectionDescription>
|
||||
{resourceGuid
|
||||
{resourceName
|
||||
? t(
|
||||
"myVirtualApiKeysManualResourceDescription"
|
||||
"myVirtualApiKeysManualResourceDescription",
|
||||
{ resourceName }
|
||||
)
|
||||
: t("myVirtualApiKeysManualDescription")}
|
||||
</SettingsSectionDescription>
|
||||
|
||||
@@ -202,7 +202,12 @@ export function LauncherInferenceApiKeysSection({
|
||||
</SettingsSubsectionTitle>
|
||||
<SettingsSubsectionDescription>
|
||||
{t(
|
||||
"myVirtualApiKeysManualResourceDescription"
|
||||
"myVirtualApiKeysManualResourceDescription",
|
||||
{
|
||||
resourceName:
|
||||
data.resourceName ??
|
||||
t("resource")
|
||||
}
|
||||
)}
|
||||
</SettingsSubsectionDescription>
|
||||
</SettingsSubsectionHeader>
|
||||
|
||||
Reference in New Issue
Block a user