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
7 changes: 5 additions & 2 deletions backend/src/authorization/auth-with-api.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import Sentry from '@sentry/minimal';
import { NextFunction, Request, Response } from 'express';
import { NextFunction, Response } from 'express';
import jwt from 'jsonwebtoken';
import { Repository } from 'typeorm';
import { JwtScopesEnum } from '../entities/user/enums/jwt-scopes.enum.js';
Expand Down Expand Up @@ -48,7 +48,7 @@ export class AuthWithApiMiddleware implements NestMiddleware {
}
}

private getTokenFromCookie(req: Request): string | undefined {
private getTokenFromCookie(req: IRequestWithCognitoInfo): string | undefined {
return req.cookies?.[Constants.JWT_COOKIE_KEY_NAME];
}

Expand All @@ -62,6 +62,9 @@ export class AuthWithApiMiddleware implements NestMiddleware {
private async authenticateWithToken(tokenFromCookie: string, req: IRequestWithCognitoInfo): Promise<void> {
try {
const jwtSecret = appConfig.auth.jwtSecret;
if (!jwtSecret) {
throw new UnauthorizedException('JWT verification failed');
}
const data = jwt.verify(tokenFromCookie, jwtSecret) as jwt.JwtPayload;
const userId = data.id;

Expand Down
5 changes: 4 additions & 1 deletion backend/src/authorization/auth.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export class AuthMiddleware implements NestMiddleware {
private readonly logOutRepository: Repository<LogOutEntity>,
) {}
async use(req: IRequestWithCognitoInfo, _res: Response, next: NextFunction): Promise<void> {
let token: string;
let token: string | undefined;
try {
token = req.cookies[Constants.JWT_COOKIE_KEY_NAME];
} catch (_e) {
Expand All @@ -50,6 +50,9 @@ export class AuthMiddleware implements NestMiddleware {

try {
const jwtSecret = appConfig.auth.jwtSecret;
if (!jwtSecret) {
throw new UnauthorizedException('JWT verification failed');
}
const data = jwt.verify(token, jwtSecret) as jwt.JwtPayload;
const userId = data.id;

Expand Down
5 changes: 4 additions & 1 deletion backend/src/authorization/non-scoped-auth.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class NonScopedAuthMiddleware implements NestMiddleware {
) {}
async use(req: IRequestWithCognitoInfo, _res: Response, next: NextFunction): Promise<void> {
console.log(`auth middleware triggered ->: ${new Date().toISOString()}`);
let token: string;
let token: string | undefined;
try {
token = req.cookies[Constants.JWT_COOKIE_KEY_NAME];
} catch (_e) {
Expand All @@ -46,6 +46,9 @@ export class NonScopedAuthMiddleware implements NestMiddleware {

try {
const jwtSecret = appConfig.auth.jwtSecret;
if (!jwtSecret) {
throw new UnauthorizedException('JWT verification failed');
}
const data = jwt.verify(token, jwtSecret) as jwt.JwtPayload;
const userId = data.id;
if (!userId) {
Expand Down
3 changes: 3 additions & 0 deletions backend/src/authorization/saas-auth.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ export class SaaSAuthMiddleware implements NestMiddleware {
}
try {
const jwtSecret = appConfig.auth.microserviceJwtSecret;
if (!jwtSecret) {
throw new UnauthorizedException(Messages.AUTHORIZATION_REJECTED);
}
const data = jwt.verify(token, jwtSecret) as jwt.JwtPayload;
const requestId = data.request_id;

Expand Down
5 changes: 4 additions & 1 deletion backend/src/authorization/temporary-auth.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class TemporaryAuthMiddleware implements NestMiddleware {
) {}
async use(req: IRequestWithCognitoInfo, _res: Response, next: NextFunction): Promise<void> {
console.log(`temporary auth middleware triggered ->: ${new Date().toISOString()}`);
let token: string;
let token: string | undefined;
try {
token = req.cookies[Constants.JWT_COOKIE_KEY_NAME];
} catch (_e) {
Expand All @@ -48,6 +48,9 @@ export class TemporaryAuthMiddleware implements NestMiddleware {

try {
const jwtSecret = appConfig.auth.temporaryJwtSecret;
if (!jwtSecret) {
throw new UnauthorizedException('JWT verification failed');
}
const data = jwt.verify(token, jwtSecret) as jwt.JwtPayload;
const userId = data.id;
if (!userId) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Request } from 'express';
export const extractTokenFromHeader = (request: Request): string | null => {
export const extractTokenFromHeader = (request: Pick<Request, 'headers'>): string | null => {
const [type, token] = request.headers.authorization?.split(' ') ?? [];
return type === 'Bearer' ? token : null;
};
15 changes: 11 additions & 4 deletions backend/src/common/abstract-use.case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,27 @@ abstract class AbstractUseCase<TInputData = void, TOutputData = void> {

protected abstract implementation(inputData: TInputData): Promise<TOutputData> | TOutputData;

private getDbContext(): IDatabaseContext {
if (!this._dbContext) {
throw new Error('Database context is not initialized for this use case.');
}
return this._dbContext;
}

private async startTransaction(): Promise<void> {
await this._dbContext.startTransaction();
await this.getDbContext().startTransaction();
}

private async commitTransaction(): Promise<void> {
await this._dbContext.commitTransaction();
await this.getDbContext().commitTransaction();
}

private async rollbackTransaction(): Promise<void> {
await this._dbContext.rollbackTransaction();
await this.getDbContext().rollbackTransaction();
}

private async releaseQueryRunner(): Promise<void> {
await this._dbContext.releaseQueryRunner();
await this.getDbContext().releaseQueryRunner();
}
}

Expand Down
5 changes: 2 additions & 3 deletions backend/src/common/application/global-database-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,10 +452,9 @@ export class GlobalDatabaseContext implements IGlobalDatabaseContext {
return this._schemaChangeChatMessageRepository;
}

public startTransaction(): Promise<void> {
public async startTransaction(): Promise<void> {
this._queryRunner = this.appDataSource.createQueryRunner();
this._queryRunner.startTransaction();
return;
await this._queryRunner.startTransaction();
}

public async commitTransaction(): Promise<void> {
Expand Down
2 changes: 1 addition & 1 deletion backend/src/decorators/gclid-decorator.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { IRequestWithCognitoInfo } from '../authorization/cognito-decoded.interface.js';

export const GCLlId = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {
export const GCLlId = createParamDecorator((_data: unknown, ctx: ExecutionContext): string | null => {
const request: IRequestWithCognitoInfo = ctx.switchToHttp().getRequest();
if (request.headers) {
return (request.headers.gclid as string | undefined) ?? null;
Expand Down
2 changes: 1 addition & 1 deletion backend/src/decorators/master-password.decorator.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { IRequestWithCognitoInfo } from '../authorization/cognito-decoded.interface.js';

export const MasterPassword = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {
export const MasterPassword = createParamDecorator((_data: unknown, ctx: ExecutionContext): string | null => {
const request: IRequestWithCognitoInfo = ctx.switchToHttp().getRequest();
const masterPwd = request.headers.masterpwd as string | undefined;
return masterPwd ? masterPwd : null;
Expand Down
5 changes: 3 additions & 2 deletions backend/src/decorators/query-table-name.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import { isObjectPropertyExists } from '../helpers/validators/is-object-property
export const QueryTableName = createParamDecorator((_data: unknown, ctx: ExecutionContext): string => {
const request: IRequestWithCognitoInfo = ctx.switchToHttp().getRequest();
const query = request.query;
if (isObjectPropertyExists(query, 'tableName') && query.tableName.length > 0) {
return query.tableName;
const tableName = query.tableName;
if (isObjectPropertyExists(query, 'tableName') && typeof tableName === 'string' && tableName.length > 0) {
return tableName;
}
throw new BadRequestException(Messages.TABLE_NAME_MISSING);
});
2 changes: 1 addition & 1 deletion backend/src/decorators/query-uuid.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const QueryUuid = createParamDecorator((paramName: string, ctx: Execution
if (isObjectPropertyExists(query, paramName)) {
// eslint-disable-next-line security/detect-object-injection
const uuId = query[paramName];
if (ValidationHelper.isValidUUID(uuId) || ValidationHelper.isValidNanoId(uuId)) {
if (typeof uuId === 'string' && (ValidationHelper.isValidUUID(uuId) || ValidationHelper.isValidNanoId(uuId))) {
return uuId;
}
throw new BadRequestException(Messages.UUID_INVALID);
Expand Down
4 changes: 2 additions & 2 deletions backend/src/decorators/slug-uuid.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ export const SlugUuid = createParamDecorator(
throw new BadRequestException(Messages.UUID_INVALID);
}
// eslint-disable-next-line security/detect-object-injection
const uuId: string = request.params?.[parameterName];
const uuId: string | undefined = request.params?.[parameterName];

if (ValidationHelper.isValidUUID(uuId) || ValidationHelper.isValidNanoId(uuId)) {
if (uuId && (ValidationHelper.isValidUUID(uuId) || ValidationHelper.isValidNanoId(uuId))) {
return uuId;
}
throw new BadRequestException(Messages.UUID_INVALID);
Expand Down
4 changes: 2 additions & 2 deletions backend/src/decorators/slug-verification.decorator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ export const VerificationString = createParamDecorator(
(paramName: SlugVerificationType, ctx: ExecutionContext): string => {
const request: IRequestWithCognitoInfo = ctx.switchToHttp().getRequest();
// eslint-disable-next-line security/detect-object-injection
const verificationString: string = request.params?.[paramName] || request.params?.slug;
const isValidString = ValidationHelper.isValidVerificationString(verificationString);
const verificationString: string | undefined = request.params?.[paramName] || request.params?.slug;
const isValidString = !!verificationString && ValidationHelper.isValidVerificationString(verificationString);
if (isValidString) {
return verificationString;
}
Expand Down
5 changes: 4 additions & 1 deletion backend/src/entities/ai/ai.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,10 @@ IMPORTANT:
}
}
settings.columns_view = filteredColumnsView;
settings.ordering = this.mapOrdering(tableSettings.ordering);
const mappedOrdering = this.mapOrdering(tableSettings.ordering);
if (mappedOrdering !== null) {
settings.ordering = mappedOrdering;
}
settings.ordering_field = validColumnNames.includes(tableSettings.ordering_field)
? tableSettings.ordering_field
: null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export class RequestInfoFromTableWithAIUseCaseV7
const systemPrompt = createDatabaseQuerySystemPrompt(
tableName,
foundConnection.type as ConnectionTypesEnum,
foundConnection.schema,
foundConnection.schema ?? undefined,
);

let chatIdForHeader: string | null = null;
Expand Down Expand Up @@ -362,9 +362,9 @@ export class RequestInfoFromTableWithAIUseCaseV7
throw new NotFoundException(Messages.CONNECTION_NOT_FOUND);
}

let userEmail: string;
let userEmail = '';
if (isConnectionTypeAgent(foundConnection.type)) {
userEmail = await this._dbContext.userRepository.getUserEmailOrReturnNull(user_id);
userEmail = (await this._dbContext.userRepository.getUserEmailOrReturnNull(user_id)) ?? '';
}

const connectionProperties =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,20 +44,24 @@ export class CedarAuthorizationService implements ICedarAuthorizationService, On

switch (actionPrefix) {
case 'connection':
if (!connectionId) return false;
resourceType = CedarResourceType.Connection;
resourceId = connectionId;
break;
case 'group':
if (!groupId) return false;
resourceType = CedarResourceType.Group;
connectionId = await this.getConnectionIdForGroup(groupId);
connectionId = (await this.getConnectionIdForGroup(groupId)) ?? undefined;
if (!connectionId) return false;
resourceId = groupId;
break;
case 'table':
if (!connectionId) return false;
resourceType = CedarResourceType.Table;
resourceId = `${connectionId}/${tableName}`;
break;
case 'actionEvent': {
if (!connectionId) return false;
if (!tableName || !actionEventId) return false;
resourceType = CedarResourceType.ActionEvent;
resourceId = `${connectionId}/${tableName}/${actionEventId}`;
Expand All @@ -74,6 +78,7 @@ export class CedarAuthorizationService implements ICedarAuthorizationService, On
);
}
case 'dashboard': {
if (!connectionId) return false;
resourceType = CedarResourceType.Dashboard;
const needsSentinel = action === CedarAction.DashboardCreate || !dashboardId;
const effectiveDashboardId = needsSentinel ? '__new__' : dashboardId;
Expand All @@ -90,6 +95,7 @@ export class CedarAuthorizationService implements ICedarAuthorizationService, On
);
}
case 'panel': {
if (!connectionId) return false;
resourceType = CedarResourceType.Panel;
const needsSentinel = action === CedarAction.PanelCreate || !panelId;
const effectivePanelId = needsSentinel ? '__new__' : panelId;
Expand All @@ -109,6 +115,7 @@ export class CedarAuthorizationService implements ICedarAuthorizationService, On
return false;
}

if (!connectionId) return false;
return this.evaluate(userId, connectionId, action, resourceType, resourceId, tableName, dashboardId, undefined);
}

Expand Down Expand Up @@ -257,7 +264,7 @@ export class CedarAuthorizationService implements ICedarAuthorizationService, On
}

private loadPoliciesPerGroup(userGroups: Array<GroupEntity>): string[] {
return userGroups.map((g) => g.cedarPolicy).filter(Boolean);
return userGroups.map((g) => g.cedarPolicy).filter((policy): policy is string => Boolean(policy));
}

private async assertUserNotSuspended(userId: string): Promise<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,12 @@ export class CedarPermissionsService implements IUserAccessRepository {
for (const group of allGroups) {
const connId = group.connection?.id;
if (!connId) continue;
if (!groupsByConnection.has(connId)) {
groupsByConnection.set(connId, []);
let connectionGroups = groupsByConnection.get(connId);
if (!connectionGroups) {
connectionGroups = [];
groupsByConnection.set(connId, connectionGroups);
}
groupsByConnection.get(connId).push(group);
connectionGroups.push(group);
}

for (const connectionId of connectionIds) {
Expand All @@ -90,7 +92,7 @@ export class CedarPermissionsService implements IUserAccessRepository {
continue;
}

const policies = userGroups.map((g) => g.cedarPolicy).filter(Boolean);
const policies = userGroups.map((g) => g.cedarPolicy).filter((policy): policy is string => Boolean(policy));
if (policies.length === 0) {
result.set(connectionId, AccessLevelEnum.none);
continue;
Expand Down Expand Up @@ -258,7 +260,7 @@ export class CedarPermissionsService implements IUserAccessRepository {
cognitoUserName: string,
connectionId: string,
tableName: string,
_masterPwd: string,
_masterPwd?: string,
): Promise<boolean> {
const ctx = await this.loadContext(connectionId, cognitoUserName);
if (!ctx) return false;
Expand Down Expand Up @@ -507,7 +509,7 @@ export class CedarPermissionsService implements IUserAccessRepository {
const userGroups = await this.globalDbContext.groupRepository.findAllUserGroupsInConnection(connectionId, userId);
if (userGroups.length === 0) return null;

const policies = userGroups.map((g) => g.cedarPolicy).filter(Boolean);
const policies = userGroups.map((g) => g.cedarPolicy).filter((policy): policy is string => Boolean(policy));
if (policies.length === 0) return null;

return { userGroups, policies };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,22 @@ export class FoundUserCompanyInfoDs {
is_payment_method_added?: boolean;

@ApiProperty({ required: false })
is2faEnabled: boolean;
is2faEnabled?: boolean;

@ApiProperty()
show_test_connections: boolean;

@ApiProperty({ required: false })
custom_domain: string | null;

@ApiProperty({ required: false, type: FoundCompanyImageInfo })
logo: FoundCompanyImageInfo;
@ApiProperty({ required: false, type: FoundCompanyImageInfo, nullable: true })
logo: FoundCompanyImageInfo | null;

@ApiProperty({ required: false, type: FoundCompanyImageInfo })
favicon: FoundCompanyImageInfo;
@ApiProperty({ required: false, type: FoundCompanyImageInfo, nullable: true })
favicon: FoundCompanyImageInfo | null;

@ApiProperty({ required: false })
tab_title: string;
@ApiProperty({ required: false, nullable: true })
tab_title: string | null;
}

export class FoundUserFullCompanyInfoDs extends FoundUserCompanyInfoDs {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ export class FoundCompanyImageInfo {
}

export class FoundCompanyLogoRO {
@ApiProperty({ type: FoundCompanyImageInfo })
logo: FoundCompanyImageInfo;
@ApiProperty({ type: FoundCompanyImageInfo, nullable: true })
logo: FoundCompanyImageInfo | null;
}

export class FoundCompanyFaviconRO {
@ApiProperty({ type: FoundCompanyImageInfo })
favicon: FoundCompanyImageInfo;
@ApiProperty({ type: FoundCompanyImageInfo, nullable: true })
favicon: FoundCompanyImageInfo | null;
}
Loading
Loading