Skip to content
Merged
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
2 changes: 1 addition & 1 deletion docker-compose.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ services:
- 80:80

traefik:
image: docker.io/traefik:v3.5
image: docker.io/traefik:v3.7
container_name: traefik
restart: unless-stopped
network_mode: service:gerbil
Expand Down
2 changes: 1 addition & 1 deletion install/config/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ services:
- 80:80

traefik:
image: docker.io/traefik:v3.5
image: docker.io/traefik:v3.7
container_name: traefik
restart: unless-stopped
network_mode: service:gerbil
Expand Down
72 changes: 58 additions & 14 deletions server/auth/sessions/resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,74 @@ export async function validateResourceSessionToken(
token: string,
resourceId: number
): Promise<ResourceSessionValidationResult> {
try {
const response = await axios.post(`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/resource/${resourceId}/session/validate`, {
try {
const response = await axios.post(
`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/resource/${resourceId}/session/validate`,
{
token: token
}, await tokenManager.getAuthHeader());
return response.data.data;
} catch (error) {
if (axios.isAxiosError(error)) {
logger.error("Error validating resource session token in hybrid mode:", {
},
await tokenManager.getAuthHeader()
);
return response.data.data;
} catch (error) {
if (axios.isAxiosError(error)) {
logger.error(
"Error validating resource session token in hybrid mode:",
{
message: error.message,
code: error.code,
status: error.response?.status,
statusText: error.response?.statusText,
url: error.config?.url,
method: error.config?.method
});
} else {
logger.error("Error validating resource session token in hybrid mode:", error);
}
return { resourceSession: null };
}
);
} else {
logger.error(
"Error validating resource session token in hybrid mode:",
error
);
}
return { resourceSession: null };
}
}

export async function createAccessTokenResourceSession(
resourceId: number,
accessTokenId: string
): Promise<string | null> {
try {
const response = await axios.post(
`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/resource/${resourceId}/session/create-access-token`,
{
accessTokenId
},
await tokenManager.getAuthHeader()
);
return response.data.data?.cookie ?? null;
} catch (error) {
if (axios.isAxiosError(error)) {
logger.error(
"Error creating access token resource session in hybrid mode:",
{
message: error.message,
code: error.code,
status: error.response?.status,
statusText: error.response?.statusText,
url: error.config?.url,
method: error.config?.method
}
);
} else {
logger.error(
"Error creating access token resource session in hybrid mode:",
error
);
}
return null;
}
}

export type ResourceSessionValidationResult = {
resourceSession: ResourceSession | null;
};


46 changes: 42 additions & 4 deletions server/auth/verifyResourceAccessToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ import { tokenManager } from "@server/lib/tokenManager";
import logger from "@server/logger";
import axios from "axios";
import config from "@server/lib/config";
import { Resource, ResourceAccessToken } from "@server/lib/types";
import {
AccessTokenUserData,
Resource,
ResourceAccessToken
} from "@server/lib/types";

export async function verifyResourceAccessToken({
accessToken,
Expand All @@ -17,10 +21,14 @@ export async function verifyResourceAccessToken({
error?: string;
tokenItem?: ResourceAccessToken;
resource?: Resource;
userData?: AccessTokenUserData;
}> {
try {
const path = resourceId
? `resource/${resourceId}/access-token/verify`
: `resource/access-token/verify`;
const response = await axios.post(
`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/resource/access-token/verify`,
`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/${path}`,
{
accessToken: accessToken,
accessTokenId: accessTokenId,
Expand All @@ -32,7 +40,7 @@ export async function verifyResourceAccessToken({
} catch (error) {
if (axios.isAxiosError(error)) {
logger.error(
"Error validating resource session token in hybrid mode:",
"Error validating resource access token in hybrid mode:",
{
message: error.message,
code: error.code,
Expand All @@ -44,10 +52,40 @@ export async function verifyResourceAccessToken({
);
} else {
logger.error(
"Error validating resource session token in hybrid mode:",
"Error validating resource access token in hybrid mode:",
error
);
}
return { valid: false };
}
}

export async function getResourceAccessToken(accessTokenId: string): Promise<{
tokenItem?: ResourceAccessToken;
userData?: AccessTokenUserData;
} | null> {
try {
const response = await axios.get(
`${config.getRawConfig().managed?.endpoint}/api/v1/hybrid/resource/access-token/${accessTokenId}`,
await tokenManager.getAuthHeader()
);
return response.data.data;
} catch (error) {
if (axios.isAxiosError(error)) {
logger.error("Error getting resource access token in hybrid mode:", {
message: error.message,
code: error.code,
status: error.response?.status,
statusText: error.response?.statusText,
url: error.config?.url,
method: error.config?.method
});
} else {
logger.error(
"Error getting resource access token in hybrid mode:",
error
);
}
return null;
}
}
53 changes: 53 additions & 0 deletions server/lib/logAccessAudit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import config from "@server/lib/config";
import { tokenManager } from "@server/lib/tokenManager";
import logger from "@server/logger";
import axios from "axios";

export async function logAccessAudit(data: {
action: boolean;
type: string;
orgId: string;
resourceId?: number;
siteResourceId?: number;
user?: { username: string; userId: string };
apiKey?: { name: string | null; apiKeyId: string };
metadata?: any;
userAgent?: string;
requestIp?: string;
}) {
if (!config.getRawConfig().app.audit_logging_enabled) {
return;
}

try {
const endpoint = config.getRawConfig().managed?.endpoint;
if (!endpoint) {
logger.warn(
"Cannot send access audit log: managed endpoint not configured"
);
return;
}

await axios.post(
`${endpoint}/api/v1/hybrid/logs/access`,
data,
await tokenManager.getAuthHeader()
);
} catch (error) {
if (axios.isAxiosError(error)) {
logger.error("logAccessAudit: Error sending access audit log:", {
message: error.message,
code: error.code,
status: error.response?.status,
statusText: error.response?.statusText,
url: error.config?.url,
method: error.config?.method
});
} else {
logger.error(
"logAccessAudit: Error sending access audit log:",
error
);
}
}
}
3 changes: 2 additions & 1 deletion server/lib/readConfigFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ export const configSchema = z.object({
log_level: z
.enum(["debug", "info", "warn", "error"])
.default("info"),
log_failed_attempts: z.boolean().optional().default(false)
log_failed_attempts: z.boolean().optional().default(false),
audit_logging_enabled: z.boolean().optional().default(true)
})
.prefault({}),
server: z
Expand Down
11 changes: 11 additions & 0 deletions server/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,21 @@ export type ResourceAccessToken = {
accessTokenId: string;
orgId: string;
resourceId: number;
userId: string | null;
path: string | null;
tokenHash: string;
sessionLength: number;
expiresAt: number | null;
title: string | null;
description: string | null;
persistSession: boolean;
createdAt: number;
};

export type AccessTokenUserData = {
userId: string;
username: string;
email: string | null;
name: string | null;
role: string | null;
};
4 changes: 4 additions & 0 deletions server/routers/badger/logRequestAudit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ export async function logRequestAudit(
requestIp?: string;
}
) {
if (!config.getRawConfig().app.audit_logging_enabled) {
return;
}

try {
if (data.orgId) {
const retentionDays = await getRetentionDays(data.orgId);
Expand Down
Loading