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

Various fixes #301

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@ import { Roles } from '../../../auth/decorators/roles.decorator';
import { RolesGuard } from '../../../auth/guards/role.guard';
import { ApiKeyStrategy } from '../../../auth/strategies/api-key.strategy';
import { JwtStrategy } from '../../../auth/strategies/jwt.strategy';
import { Port } from '../port/port.model';
import { PortService } from '../port/port.service';
import {
BatchEditHostsDto,
DeleteHostsDto,
GetHostPortDto,
HostsFilterDto,
SubmitHostsDto,
} from './host.dto';
Expand All @@ -31,7 +34,10 @@ import { HostService } from './host.service';

@Controller('hosts')
export class HostController {
constructor(private readonly hostsService: HostService) {}
constructor(
private readonly hostsService: HostService,
private readonly portsService: PortService,
) {}

@UseGuards(AuthGuard([JwtStrategy.name, ApiKeyStrategy.name]), RolesGuard)
@Roles(Role.User)
Expand Down Expand Up @@ -65,6 +71,13 @@ export class HostController {
return await this.hostsService.getHost(dto.id);
}

@UseGuards(AuthGuard([JwtStrategy.name, ApiKeyStrategy.name]), RolesGuard)
@Roles(Role.ReadOnly)
@Get(':id/ports/:portNumber')
async getPort(@Param() dto: GetHostPortDto): Promise<Port> {
return await this.portsService.getHostPort(dto.id, dto.portNumber);
}

@UseGuards(AuthGuard([JwtStrategy.name, ApiKeyStrategy.name]), RolesGuard)
@Roles(Role.User)
@Delete(':id')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
IsMongoId,
IsNotEmpty,
IsOptional,
IsPort,
Max,
Min,
} from 'class-validator';
Expand Down Expand Up @@ -83,3 +84,13 @@ export class BatchEditHostsDto {
@IsBoolean()
block: boolean;
}

export class GetHostPortDto {
@IsMongoId()
@IsNotEmpty()
id: string;

@IsPort()
@IsNotEmpty()
portNumber: number;
}
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,23 @@ describe('Host Controller (e2e)', () => {
});
});

describe('GET /hosts/:id/ports/:portNumber', () => {
it('Get host port - Not authorized - Should return 401', async () => {
const success = await checkAuthorizations(
testData,
Role.ReadOnly,
async (givenToken) => {
return await getReq(
app,
givenToken,
`/hosts/507f1f77bcf86cd799439011/ports/80`,
);
},
);
expect(success).toBe(true);
});
});

it('Should create a host (POST /hosts)', async () => {
// Arrange
const project = await createProject(app, testData, getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ describe('Port Service', () => {
expect(res.deletedCount).toStrictEqual(2);
});
});

describe('Get all', () => {
it.each([
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,14 @@ export class PortService {
return await this.portsModel.findById(portId);
}

public async getHostPort(hostId: string, portNumber: number) {
console.log(await this.portsModel.findOne());
return await this.portsModel.findOne({
'host.id': { $eq: new Types.ObjectId(hostId) },
port: { $eq: portNumber },
});
}

public async deleteAllForProject(projectId: string): Promise<DeleteResult> {
return await this.portsModel.deleteMany({
projectId: { $eq: new Types.ObjectId(projectId) },
Expand Down
2 changes: 1 addition & 1 deletion packages/frontend/stalker-app/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# StalkerUi
# App

This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 13.2.5.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Observable, firstValueFrom } from 'rxjs';
import { Host } from 'src/app/shared/types/host/host.interface';
import { Page } from 'src/app/shared/types/page.type';
import { environment } from 'src/environments/environment';
import { Port } from '../../shared/types/ports/port.interface';
import { filtersToParams } from '../../utils/filters-to-params';

@Injectable({
Expand Down Expand Up @@ -52,4 +53,8 @@ export class HostsService {
public async block(hostIds: string[], block: boolean) {
return await firstValueFrom(this.http.patch(`${environment.fmUrl}/hosts/`, { hostIds, block }));
}

public getPort(hostId: string, portNumber: number): Observable<Port> {
return this.http.get<Port>(`${environment.fmUrl}/hosts/${hostId}/ports/${portNumber}`);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,21 @@ import { Injectable } from '@angular/core';
import { firstValueFrom, map, Observable } from 'rxjs';
import { environment } from 'src/environments/environment';
import { CustomJob, CustomJobData } from '../../../shared/types/jobs/custom-job.type';
import { Page } from '../../../shared/types/page.type';
import { normalizeSearchString } from '../../../utils/normalize-search-string';

@Injectable({
providedIn: 'root',
})
export class CustomJobsService {
constructor(private http: HttpClient) {}

public getCustomJobs(): Observable<CustomJob[]> {
return <Observable<CustomJob[]>>this.http.get(`${environment.fmUrl}/custom-jobs/`);
public getCustomJobs(filters: string[], page: number, pageSize: number): Observable<Page<CustomJob>> {
return this.http.get<CustomJob[]>(`${environment.fmUrl}/custom-jobs/`).pipe(
map((jobs) => jobs.sort((a, b) => a._id.localeCompare(b._id))),
map((jobs) => jobs.filter((job) => !filters?.length || this.filterJob(job, filters))),
map((jobs) => ({ items: jobs.slice(page * pageSize, page * pageSize + pageSize), totalRecords: jobs.length }))
);
}

public get(id: string): Observable<CustomJob> {
Expand All @@ -37,4 +43,9 @@ export class CustomJobsService {
public async syncCache() {
return await firstValueFrom(this.http.post(`${environment.fmUrl}/custom-jobs/sync`, undefined));
}

private filterJob(job: CustomJob, filters: string[]) {
const parts = [job.name, job.findingHandlerLanguage, job.type];
return filters.some((filter) => normalizeSearchString(parts.join(' ')).includes(filter));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
EventSubscriptionData,
} from 'src/app/shared/types/subscriptions/subscription.type';
import { stringify } from 'yaml';
import { Page } from '../../../shared/types/page.type';
import { normalizeSearchString } from '../../../utils/normalize-search-string';
import { CronSubscriptionsService } from './cron-subscriptions.service';
import { EventSubscriptionsService } from './event-subscriptions.service';

Expand All @@ -21,15 +23,22 @@ export class SubscriptionService {
private cronSubscriptionsService: CronSubscriptionsService
) {}

getSubscriptions(): Observable<(CronSubscription | EventSubscription)[]> {
getSubscriptions(
filters: string[],
page: number,
pageSize: number
): Observable<Page<CronSubscription | EventSubscription>> {
return combineLatest([
this.eventsSubscriptionsService.getSubscriptions(),
this.cronSubscriptionsService.getSubscriptions(),
]).pipe(
map(([eventSubscriptions, cronSubscriptions]: [EventSubscription[], CronSubscription[]]) => [
...eventSubscriptions,
...cronSubscriptions,
])
]),
map((subs) => subs.sort((a, b) => a._id.localeCompare(b._id))),
map((subs) => subs.filter((sub) => !filters?.length || this.filterSubscription(sub, filters))),
map((subs) => ({ items: subs.slice(page * pageSize, page * pageSize + pageSize), totalRecords: subs.length }))
);
}

Expand Down Expand Up @@ -95,4 +104,18 @@ export class SubscriptionService {

return stringify(copy);
}

private filterSubscription(subscription: EventSubscription | CronSubscription, filters: string[]) {
const event = subscription as EventSubscription;
const cron = subscription as CronSubscription;
const parts = [
subscription.job?.name,
subscription.name,
cron.cronExpression,
event.finding,
cron.cronExpression ? 'cron' : 'event',
subscription.isEnabled === false ? 'disabled' : 'enabled',
];
return filters.some((filter) => normalizeSearchString(parts.join(' ')).includes(filter));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,26 @@ import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, firstValueFrom, from, map, mergeMap } from 'rxjs';
import { environment } from '../../../environments/environment';
import { Page } from '../../shared/types/page.type';
import { ProjectSummary } from '../../shared/types/project/project.summary';
import { Secret } from '../../shared/types/secret.type';
import { normalizeSearchString } from '../../utils/normalize-search-string';

@Injectable({
providedIn: 'root',
})
export class SecretService {
constructor(private http: HttpClient) {}

getSecrets(): Observable<Secret[]> {
return <Observable<Array<Secret>>>this.http.get(`${environment.fmUrl}/secrets/`);
getSecrets(filters: string[], page: number, pageSize: number, projects: ProjectSummary[]): Observable<Page<Secret>> {
return this.http.get<Secret[]>(`${environment.fmUrl}/secrets/`).pipe(
map((secrets) => secrets.sort((a, b) => a._id.localeCompare(b._id))),
map((secrets) => secrets.filter((secret) => !filters?.length || this.filterSecret(secret, filters, projects))),
map((secrets) => ({
items: secrets.slice(page * pageSize, page * pageSize + pageSize),
totalRecords: secrets.length,
}))
);
}

async create(secret: Omit<Secret, '_id'>) {
Expand All @@ -21,4 +31,9 @@ export class SecretService {
deleteMany(ids: string[]) {
return from(ids).pipe(mergeMap((id) => this.http.delete(`${environment.fmUrl}/secrets/${id}`).pipe(map(() => id))));
}

private filterSecret(secret: Secret, filters: string[], projects: ProjectSummary[]) {
const parts = [secret?.name, projects?.find((p) => p.id === secret._id)?.name, secret.description];
return filters.some((filter) => normalizeSearchString(parts.join(' ')).includes(filter));
}
}
25 changes: 21 additions & 4 deletions packages/frontend/stalker-app/src/app/api/tags/tags.service.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,41 @@
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { firstValueFrom, Observable } from 'rxjs';
import { firstValueFrom, map, Observable } from 'rxjs';
import { environment } from 'src/environments/environment';
import { Page } from '../../shared/types/page.type';
import { Tag } from '../../shared/types/tag.type';
import { normalizeSearchString } from '../../utils/normalize-search-string';

@Injectable({
providedIn: 'root',
})
export class TagsService {
constructor(private http: HttpClient) {}

public getTags(): Observable<any[]> {
return <Observable<Array<any>>>this.http.get(`${environment.fmUrl}/tags/`);
public getTags(filters?: string[], page?: number, pageSize?: number): Observable<Page<Tag>> {
return this.http.get<Tag[]>(`${environment.fmUrl}/tags/`).pipe(
map((tags) => tags.sort((a, b) => a._id.localeCompare(b._id))),
map((tags) => tags.filter((job) => !filters?.length || this.filterTags(job, filters))),
map((tags) => {
if (page == null || pageSize == null) return { items: tags, totalRecords: tags.length };
return { items: tags.slice(page * pageSize, page * pageSize + pageSize), totalRecords: tags.length };
})
);
}

public getAllTags(): Observable<Tag[]> {
return this.http.get<Tag[]>(`${environment.fmUrl}/tags/`);
}

public async createTag(text: string, color: string): Promise<Tag> {
return <Tag>await firstValueFrom(this.http.post(`${environment.fmUrl}/tags/`, { text: text, color: color }));
return await firstValueFrom(this.http.post<Tag>(`${environment.fmUrl}/tags/`, { text: text, color: color }));
}

public async delete(id: string) {
return await firstValueFrom(this.http.delete(`${environment.fmUrl}/tags/${id}`));
}
private filterTags(secret: Tag, filters: string[]) {
const parts = [secret?.text, secret.color];
return filters.some((filter) => normalizeSearchString(parts.join(' ')).includes(filter));
}
}
8 changes: 6 additions & 2 deletions packages/frontend/stalker-app/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Component } from '@angular/core';
import { PreviousRouteService } from './services/previous-route.service';
import { ThemeService } from './services/theme.service';

@Component({
Expand All @@ -7,7 +8,10 @@ import { ThemeService } from './services/theme.service';
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
title = 'Stalker';
title = 'Red Kite';

constructor(private theme: ThemeService) {}
constructor(
private theme: ThemeService,
private previousRouteService: PreviousRouteService
) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ import { Title } from '@angular/platform-browser';
})
export class NotFoundComponent {
constructor(private titleService: Title) {
this.titleService.setTitle($localize`:Not found page title|:Page not found · Stalker`);
this.titleService.setTitle($localize`:Not found page title|:Page not found · Red Kite`);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@

<mat-paginator
[length]="100"
[pageSize]="10"
[pageSizeOptions]="[10, 25, 50, 100]"
[pageSize]="25"
[pageSizeOptions]="[25, 50, 100]"
showFirstLastButtons
aria-label="Select page"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const roles: Role[] = [
];

export const rolesInfoDialogText = {
text: $localize`:Application role description|Short description of what a user's role represents in the application:The user role is a crucial part of any user. Their role will define what they can and cannot do in Stalker.`,
text: $localize`:Application role description|Short description of what a user's role represents in the application:The user role is a crucial part of any user. Their role will define what they can and cannot do in Red Kite.`,
title: $localize`:User roles|Title of the panel giving more info about roles:User roles`,
primaryButtonText: $localize`:Got it|Accepting and aknowledging information:Got it`,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export class FirstUserComponent {
try {
await this.usersService.createFirstUser(email, password, firstName, lastName);
this.toastr.success(
$localize`:Stalker initialized|The platform is properly intialized:Stalker is now initialized`
$localize`:Red Kite initialized|The platform is properly intialized:Red Kite is now initialized`
);
this.router.navigateByUrl('/auth/login');
} catch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class LoginComponent {
private fb: FormBuilder,
private cdr: ChangeDetectorRef
) {
this.titleService.setTitle($localize`:Sign in page title|:Sign in to Stalker`);
this.titleService.setTitle($localize`:Sign in page title|:Sign in to Red Kite`);
this.authService.checkServerSetup(true);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export class ResetPasswordComponent {
tap((isAuthorized) => {
if (!isAuthorized) this.router.navigate(['/auth', 'request-reset'], { queryParams: { invalidToken: true } });
}),
shareReplay()
shareReplay(1)
);

constructor(
Expand Down
Loading
Loading