Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 03-25-2024 take 2 #683

Closed
wants to merge 14 commits into from
Closed
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
4 changes: 4 additions & 0 deletions api/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,7 @@ CORS_ORIGINS=["http://localhost:3000", "http://localhost:3001"]
CORS_REGEX=["test1", "test2"]
# controls the repetition of the temp file clearing cron job
TEMP_FILE_CLEAR_CRON_STRING=0 * * *
# how long we maintain our request time outs (60 * 60 * 1000 ms)
THROTTLE_TTL=3600000
# how many requests before we throttle
THROTTLE_LIMIT=100
3 changes: 2 additions & 1 deletion api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,12 @@
"@nestjs/platform-express": "^10.3.2",
"@nestjs/schedule": "^4.0.1",
"@nestjs/swagger": "~7.1.12",
"@nestjs/throttler": "^5.1.2",
"@prisma/client": "^5.0.0",
"@sendgrid/mail": "7.7.0",
"@turf/boolean-point-in-polygon": "6.5.0",
"@turf/buffer": "6.5.0",
"@turf/helpers": "6.5.0",
"@turf/boolean-point-in-polygon": "6.5.0",
"@turf/points-within-polygon": "6.5.0",
"@types/archiver": "^6.0.2",
"archiver": "^6.0.1",
Expand Down
5 changes: 4 additions & 1 deletion api/prisma/seed-dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ export const devSeeding = async (
jurisdictionName?: string,
) => {
const jurisdiction = await prismaClient.jurisdictions.create({
data: jurisdictionFactory(jurisdictionName),
data: {
...jurisdictionFactory(jurisdictionName),
allowSingleUseCodeLogin: false,
},
});
await prismaClient.userAccounts.create({
data: await userFactory({
Expand Down
26 changes: 12 additions & 14 deletions api/prisma/seed-helpers/map-layer-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,18 @@ export const simplifiedDCMap = {
geometry: {
coordinates: [
[
[
[-77.0392589333301, 38.79186072967565],
[-76.90981025809415, 38.89293952026222],
[-77.04122027689426, 38.996161202682146],
[-77.12000091005532, 38.93465307055658],
[-77.10561772391833, 38.91990351952725],
[-77.09123453778136, 38.90565966392609],
[-77.06802530560486, 38.9015894658674],
[-77.06181438431805, 38.889377471720564],
[-77.03697069917165, 38.870801038935525],
[-77.03043288729134, 38.850437727576235],
[-77.03435557441966, 38.80816525459605],
[-77.0392589333301, 38.79186072967565],
],
[-77.0392589333301, 38.79186072967565],
[-76.90981025809415, 38.89293952026222],
[-77.04122027689426, 38.996161202682146],
[-77.12000091005532, 38.93465307055658],
[-77.10561772391833, 38.91990351952725],
[-77.09123453778136, 38.90565966392609],
[-77.06802530560486, 38.9015894658674],
[-77.06181438431805, 38.889377471720564],
[-77.03697069917165, 38.870801038935525],
[-77.03043288729134, 38.850437727576235],
[-77.03435557441966, 38.80816525459605],
[-77.0392589333301, 38.79186072967565],
],
],
type: 'Polygon',
Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/ami-chart.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ import { SuccessDTO } from '../dtos/shared/success.dto';
import { PermissionTypeDecorator } from '../decorators/permission-type.decorator';
import { JwtAuthGuard } from '../guards/jwt.guard';
import { PermissionGuard } from '../guards/permission.guard';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('/amiCharts')
@ApiTags('amiCharts')
@UsePipes(new ValidationPipe(defaultValidationPipeOptions))
@PermissionTypeDecorator('amiChart')
@UseGuards(JwtAuthGuard, PermissionGuard)
@UseGuards(ThrottleGuard, JwtAuthGuard, PermissionGuard)
@ApiExtraModels(AmiChartQueryParams)
export class AmiChartController {
constructor(private readonly AmiChartService: AmiChartService) {}
Expand Down
2 changes: 2 additions & 0 deletions api/src/controllers/app.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ import { PermissionAction } from '../decorators/permission-action.decorator';
import { permissionActions } from '../enums/permissions/permission-actions-enum';
import { AdminOrJurisdictionalAdminGuard } from '../guards/admin-or-jurisdiction-admin.guard';
import { AppService } from '../services/app.service';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller()
@UseGuards(ThrottleGuard)
@ApiExtraModels(SuccessDTO)
@ApiTags('root')
export class AppController {
Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/application-flagged-set.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ import { mapTo } from '../utilities/mapTo';
import { OptionalAuthGuard } from '../guards/optional.guard';
import { PermissionGuard } from '../guards/permission.guard';
import { PermissionTypeDecorator } from '../decorators/permission-type.decorator';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('/applicationFlaggedSets')
@ApiExtraModels(SuccessDTO)
@ApiTags('applicationFlaggedSets')
@UseGuards(OptionalAuthGuard, PermissionGuard)
@UseGuards(ThrottleGuard, OptionalAuthGuard, PermissionGuard)
@PermissionTypeDecorator('applicationFlaggedSet')
@UsePipes(
new ValidationPipe({
Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/application.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import { ApplicationCsvExporterService } from '../services/application-csv-expor
import { ApplicationCsvQueryParams } from '../dtos/applications/application-csv-query-params.dto';
import { MostRecentApplicationQueryParams } from '../dtos/applications/most-recent-application-query-params.dto';
import { ExportLogInterceptor } from '../interceptors/export-log.interceptor';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('applications')
@ApiTags('applications')
Expand All @@ -59,7 +60,7 @@ import { ExportLogInterceptor } from '../interceptors/export-log.interceptor';
}),
)
@ApiExtraModels(IdDTO, AddressInput, BooleanInput, TextInput)
@UseGuards(OptionalAuthGuard)
@UseGuards(ThrottleGuard, OptionalAuthGuard)
@PermissionTypeDecorator('application')
@UseInterceptors(ActivityLogInterceptor)
export class ApplicationController {
Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/asset.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { CreatePresignedUploadMetadataResponse } from '../dtos/assets/create-pre
import { CreatePresignedUploadMetadata } from '../dtos/assets/create-presigned-upload-meta.dto';
import { AssetService } from '../services/asset.service';
import { defaultValidationPipeOptions } from '../utilities/default-validation-pipe-options';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('assets')
@ApiTags('assets')
Expand All @@ -28,7 +29,7 @@ import { defaultValidationPipeOptions } from '../utilities/default-validation-pi
CreatePresignedUploadMetadataResponse,
)
@PermissionTypeDecorator('asset')
@UseGuards(JwtAuthGuard, PermissionGuard)
@UseGuards(ThrottleGuard, JwtAuthGuard, PermissionGuard)
export class AssetController {
constructor(private readonly assetService: AssetService) {}

Expand Down
21 changes: 6 additions & 15 deletions api/src/controllers/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { User } from '../dtos/users/user.dto';
import { RequestSingleUseCode } from '../dtos/single-use-code/request-single-use-code.dto';
import { LoginViaSingleUseCode } from '../dtos/auth/login-single-use-code.dto';
import { SingleUseCodeAuthGuard } from '../guards/single-use-code.guard';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('auth')
@ApiTags('auth')
Expand All @@ -43,7 +44,7 @@ export class AuthController {
@ApiOperation({ summary: 'Login', operationId: 'login' })
@ApiOkResponse({ type: SuccessDTO })
@ApiBody({ type: Login })
@UseGuards(MfaAuthGuard)
@UseGuards(ThrottleGuard, MfaAuthGuard)
async login(
@Request() req: ExpressRequest,
@Response({ passthrough: true }) res: ExpressResponse,
Expand All @@ -63,7 +64,10 @@ export class AuthController {
@Request() req: ExpressRequest,
@Response({ passthrough: true }) res: ExpressResponse,
): Promise<SuccessDTO> {
return await this.authService.setCredentials(res, mapTo(User, req['user']));
return await this.authService.confirmAndSetCredentials(
mapTo(User, req['user']),
res,
);
}

@Get('logout')
Expand All @@ -89,19 +93,6 @@ export class AuthController {
return await this.authService.requestMfaCode(dto);
}

@Post('request-single-use-code')
@ApiOperation({
summary: 'Request single use code',
operationId: 'requestSingleUseCode',
})
@ApiOkResponse({ type: SuccessDTO })
async requestSingleUseCode(
@Request() req: ExpressRequest,
@Body() dto: RequestSingleUseCode,
): Promise<SuccessDTO> {
return await this.authService.requestSingleUseCode(dto, req);
}

@Get('requestNewToken')
@ApiOperation({
summary: 'Requests a new token given a refresh token',
Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/jurisdiction.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@ import { SuccessDTO } from '../dtos/shared/success.dto';
import { PermissionTypeDecorator } from '../decorators/permission-type.decorator';
import { OptionalAuthGuard } from '../guards/optional.guard';
import { PermissionGuard } from '../guards/permission.guard';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('jurisdictions')
@ApiTags('jurisdictions')
@UsePipes(new ValidationPipe(defaultValidationPipeOptions))
@ApiExtraModels(JurisdictionCreate, JurisdictionUpdate, IdDTO)
@PermissionTypeDecorator('jurisdiction')
@UseGuards(OptionalAuthGuard, PermissionGuard)
@UseGuards(ThrottleGuard, OptionalAuthGuard, PermissionGuard)
export class JurisdictionController {
constructor(private readonly jurisdictionService: JurisdictionService) {}

Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/listing.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { ListingCsvExporterService } from '../services/listing-csv-export.servic
import { ListingCsvQueryParams } from '../dtos/listings/listing-csv-query-params.dto';
import { PermissionGuard } from '../guards/permission.guard';
import { ExportLogInterceptor } from '../interceptors/export-log.interceptor';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('listings')
@ApiTags('listings')
Expand All @@ -63,7 +64,7 @@ import { ExportLogInterceptor } from '../interceptors/export-log.interceptor';
PaginationAllowsAllQueryParams,
IdDTO,
)
@UseGuards(OptionalAuthGuard)
@UseGuards(ThrottleGuard, OptionalAuthGuard)
@PermissionTypeDecorator('listing')
@ActivityLogMetadata([{ targetPropertyName: 'status', propertyPath: 'status' }])
@UseInterceptors(ActivityLogInterceptor)
Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/map-layer.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ import { defaultValidationPipeOptions } from '../utilities/default-validation-pi
import { OptionalAuthGuard } from '../guards/optional.guard';
import { PermissionGuard } from '../guards/permission.guard';
import { PermissionTypeDecorator } from '../decorators/permission-type.decorator';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('/mapLayers')
@ApiTags('mapLayers')
@UseGuards(OptionalAuthGuard, PermissionGuard)
@UseGuards(ThrottleGuard, OptionalAuthGuard, PermissionGuard)
@PermissionTypeDecorator('mapLayers')
@UsePipes(new ValidationPipe(defaultValidationPipeOptions))
export class MapLayersController {
Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/multiselect-question.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { OptionalAuthGuard } from '../guards/optional.guard';
import { PermissionGuard } from '../guards/permission.guard';
import { AdminOrJurisdictionalAdminGuard } from '../guards/admin-or-jurisdiction-admin.guard';
import { ActivityLogInterceptor } from '../interceptors/activity-log.interceptor';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('multiselectQuestions')
@ApiTags('multiselectQuestions')
Expand All @@ -46,7 +47,7 @@ import { ActivityLogInterceptor } from '../interceptors/activity-log.interceptor
IdDTO,
)
@PermissionTypeDecorator('multiselectQuestion')
@UseGuards(OptionalAuthGuard, PermissionGuard)
@UseGuards(ThrottleGuard, OptionalAuthGuard, PermissionGuard)
export class MultiselectQuestionController {
constructor(
private readonly multiselectQuestionService: MultiselectQuestionService,
Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/reserved-community-type.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@ import { SuccessDTO } from '../dtos/shared/success.dto';
import { PermissionTypeDecorator } from '../decorators/permission-type.decorator';
import { JwtAuthGuard } from '../guards/jwt.guard';
import { PermissionGuard } from '../guards/permission.guard';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('reservedCommunityTypes')
@ApiTags('reservedCommunityTypes')
@UsePipes(new ValidationPipe(defaultValidationPipeOptions))
@ApiExtraModels(ReservedCommunityTypeQueryParams)
@PermissionTypeDecorator('reservedCommunityType')
@UseGuards(JwtAuthGuard, PermissionGuard)
@UseGuards(ThrottleGuard, JwtAuthGuard, PermissionGuard)
export class ReservedCommunityTypeController {
constructor(
private readonly ReservedCommunityTypeService: ReservedCommunityTypeService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ import { SuccessDTO } from '../dtos/shared/success.dto';
import { PermissionTypeDecorator } from '../decorators/permission-type.decorator';
import { JwtAuthGuard } from '../guards/jwt.guard';
import { PermissionGuard } from '../guards/permission.guard';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('unitAccessibilityPriorityTypes')
@ApiTags('unitAccessibilityPriorityTypes')
@UsePipes(new ValidationPipe(defaultValidationPipeOptions))
@ApiExtraModels(IdDTO)
@PermissionTypeDecorator('unitAccessibilityPriorityType')
@UseGuards(JwtAuthGuard, PermissionGuard)
@UseGuards(ThrottleGuard, JwtAuthGuard, PermissionGuard)
export class UnitAccessibilityPriorityTypeController {
constructor(
private readonly unitAccessibilityPriorityTypeService: UnitAccessibilityPriorityTypeService,
Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/unit-rent-type.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ import { SuccessDTO } from '../dtos/shared/success.dto';
import { PermissionTypeDecorator } from '../decorators/permission-type.decorator';
import { JwtAuthGuard } from '../guards/jwt.guard';
import { PermissionGuard } from '../guards/permission.guard';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('unitRentTypes')
@ApiTags('unitRentTypes')
@UsePipes(new ValidationPipe(defaultValidationPipeOptions))
@ApiExtraModels(UnitRentTypeCreate, UnitRentTypeUpdate, IdDTO)
@PermissionTypeDecorator('unitRentType')
@UseGuards(JwtAuthGuard, PermissionGuard)
@UseGuards(ThrottleGuard, JwtAuthGuard, PermissionGuard)
export class UnitRentTypeController {
constructor(private readonly unitRentTypeService: UnitRentTypeService) {}

Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/unit-type.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ import { SuccessDTO } from '../dtos/shared/success.dto';
import { PermissionTypeDecorator } from '../decorators/permission-type.decorator';
import { JwtAuthGuard } from '../guards/jwt.guard';
import { PermissionGuard } from '../guards/permission.guard';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('unitTypes')
@ApiTags('unitTypes')
@UsePipes(new ValidationPipe(defaultValidationPipeOptions))
@PermissionTypeDecorator('unitType')
@UseGuards(JwtAuthGuard, PermissionGuard)
@UseGuards(ThrottleGuard, JwtAuthGuard, PermissionGuard)
export class UnitTypeController {
constructor(private readonly unitTypeService: UnitTypeService) {}

Expand Down
27 changes: 18 additions & 9 deletions api/src/controllers/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,11 @@ import { PermissionTypeDecorator } from '../decorators/permission-type.decorator
import { UserFilterParams } from '../dtos/users/user-filter-params.dto';
import { UserCsvExporterService } from '../services/user-csv-export.service';
import { ExportLogInterceptor } from '../interceptors/export-log.interceptor';
import { RequestSingleUseCode } from '../dtos/single-use-code/request-single-use-code.dto';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('user')
@UseGuards(ThrottleGuard)
@ApiTags('user')
@PermissionTypeDecorator('user')
@UsePipes(new ValidationPipe(defaultValidationPipeOptions))
Expand Down Expand Up @@ -168,13 +171,11 @@ export class UserController {
@Body() dto: UserCreate,
@Query() queryParams: UserCreateParams,
): Promise<User> {
const jurisdictionName = req.headers['jurisdictionname'] || '';
return await this.userService.create(
dto,
false,
queryParams.noWelcomeEmail !== true,
mapTo(User, req['user']),
jurisdictionName as string,
req,
);
}

Expand All @@ -187,12 +188,20 @@ export class UserController {
@Body() dto: UserInvite,
@Request() req: ExpressRequest,
): Promise<User> {
return await this.userService.create(
dto,
true,
undefined,
mapTo(User, req['user']),
);
return await this.userService.create(dto, true, undefined, req);
}

@Post('request-single-use-code')
@ApiOperation({
summary: 'Request single use code',
operationId: 'requestSingleUseCode',
})
@ApiOkResponse({ type: SuccessDTO })
async requestSingleUseCode(
@Request() req: ExpressRequest,
@Body() dto: RequestSingleUseCode,
): Promise<SuccessDTO> {
return await this.userService.requestSingleUseCode(dto, req);
}

@Post('resend-confirmation')
Expand Down
22 changes: 22 additions & 0 deletions api/src/guards/throttler.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ThrottlerGuard } from '@nestjs/throttler';
import { ExecutionContext, Injectable } from '@nestjs/common';
import { ThrottlerLimitDetail } from '@nestjs/throttler/dist/throttler.guard.interface';

@Injectable()
export class ThrottleGuard extends ThrottlerGuard {
protected async getTracker(req: Record<string, any>): Promise<string> {
if (req?.headers && req.headers['x-forwarded-for']) {
// if we are passing through the proxy use forwarded for
return req.headers['x-forwarded-for'].split(',')[0];
}
return req.ips.length ? req.ips[0] : req.ip;
}

protected async throwThrottlingException(
context: ExecutionContext,
throttlerLimitDetail: ThrottlerLimitDetail,
): Promise<void> {
console.error(`IP Address: ${throttlerLimitDetail.tracker} was throttled`);
await super.throwThrottlingException(context, throttlerLimitDetail);
}
}
Loading
Loading