Inserted web templates, readme, license. Updated logo and favicon
This commit is contained in:
@@ -20,7 +20,8 @@
|
||||
"tsConfig": "src/tsconfig.app.json",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
"src/assets",
|
||||
"src/logo.png"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.css"
|
||||
@@ -133,4 +134,4 @@
|
||||
}
|
||||
},
|
||||
"defaultProject": "tapit-frontend"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,9 @@ import { TextTemplateComponent } from './text-template/text-template.component';
|
||||
import { TextTemplateNewComponent } from './text-template-new/text-template-new.component';
|
||||
import { ProviderComponent } from './provider/provider.component';
|
||||
import { ProfileComponent } from './profile/profile.component';
|
||||
import { WebTemplateComponent } from './web-template/web-template.component';
|
||||
import { WebTemplateNewComponent } from './web-template-new/web-template-new.component';
|
||||
import { GlobalSettingsComponent } from './global-settings/global-settings.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', component: MainComponent },
|
||||
@@ -28,6 +31,10 @@ const routes: Routes = [
|
||||
{ path: 'text-template/new', component: TextTemplateNewComponent },
|
||||
{ path: 'text-template/:id/edit', component: TextTemplateNewComponent },
|
||||
{ path: 'provider', component: ProviderComponent },
|
||||
{ path: 'web-template', component: WebTemplateComponent },
|
||||
{ path: 'web-template/new', component: WebTemplateNewComponent },
|
||||
{ path: 'web-template/:id/edit', component: WebTemplateNewComponent },
|
||||
{ path: 'global-settings', component: GlobalSettingsComponent },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<a class="navbar-brand" routerLink="/">Tap It!</a>
|
||||
<a class="navbar-brand" routerLink="/"><img src="logo.png" height="30px" alt="Tap It!"></a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
@@ -19,7 +19,7 @@
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
|
||||
<a class="dropdown-item" routerLink="/profile">Profile</a>
|
||||
<a class="dropdown-item" routerLink="/provider">Providers</a>
|
||||
<a class="dropdown-item" routerLink="/globalsettings">Global Settings</a>
|
||||
<a class="dropdown-item" routerLink="/global-settings">Global Settings</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -18,6 +18,9 @@ import { RegisterComponent } from './register/register.component';
|
||||
import { ProviderComponent } from './provider/provider.component';
|
||||
import { ProfileComponent } from './profile/profile.component';
|
||||
import { CampaignViewComponent } from './campaign-view/campaign-view.component';
|
||||
import { WebTemplateComponent } from './web-template/web-template.component';
|
||||
import { WebTemplateNewComponent } from './web-template-new/web-template-new.component';
|
||||
import { GlobalSettingsComponent } from './global-settings/global-settings.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@@ -34,7 +37,10 @@ import { CampaignViewComponent } from './campaign-view/campaign-view.component';
|
||||
RegisterComponent,
|
||||
ProviderComponent,
|
||||
ProfileComponent,
|
||||
CampaignViewComponent
|
||||
CampaignViewComponent,
|
||||
WebTemplateComponent,
|
||||
WebTemplateNewComponent,
|
||||
GlobalSettingsComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
||||
@@ -23,7 +23,7 @@ export class UserNotification {
|
||||
})
|
||||
export class AuthService {
|
||||
currUser = new User();
|
||||
loggedin = false;
|
||||
loggedin = false; // change this for testing
|
||||
loginUrl = 'api/login';
|
||||
logoutUrl = 'api/logout';
|
||||
registerUrl = 'api/register';
|
||||
|
||||
@@ -34,6 +34,13 @@
|
||||
<option *ngFor="let textTemplate of textTemplateService.textTemplates" [ngValue]="textTemplate.id">{{textTemplate.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="web-template-select">Web Template</label>
|
||||
<select class="form-control" (change)="updatePreviews()" [(ngModel)]="newCampaign.webTemplateId" id="web-template-select">
|
||||
<option [ngValue]="0"></option>
|
||||
<option *ngFor="let webTemplate of webTemplateService.webTemplates" [ngValue]="webTemplate.id">{{webTemplate.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-12 d-flex">
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Router, ActivatedRoute, ParamMap } from '@angular/router';
|
||||
import { ProviderService } from '../provider.service';
|
||||
import { PhonebookService } from '../phonebook.service';
|
||||
import { TextTemplateService } from '../text-template.service';
|
||||
import { WebTemplateService } from '../web-template.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-campaign-new',
|
||||
@@ -17,7 +18,9 @@ export class CampaignNewComponent implements OnInit {
|
||||
private router: Router,
|
||||
private providerService: ProviderService,
|
||||
private phonebookService: PhonebookService,
|
||||
private textTemplateService: TextTemplateService) { }
|
||||
private textTemplateService: TextTemplateService,
|
||||
private webTemplateService: WebTemplateService,
|
||||
) { }
|
||||
|
||||
newCampaign: Campaign = new Campaign();
|
||||
|
||||
@@ -43,6 +46,7 @@ export class CampaignNewComponent implements OnInit {
|
||||
tempStr = tempStr.replace('{lastName}', phonebook.records[0].lastName);
|
||||
tempStr = tempStr.replace('{alias}', phonebook.records[0].alias);
|
||||
tempStr = tempStr.replace('{phoneNumber}', phonebook.records[0].phoneNumber);
|
||||
tempStr = tempStr.replace('{url}', 'https://www.example.com/eR2c1');
|
||||
|
||||
this.previewStr = tempStr;
|
||||
});
|
||||
@@ -55,6 +59,7 @@ export class CampaignNewComponent implements OnInit {
|
||||
|
||||
ngOnInit() {
|
||||
this.newCampaign.textTemplateId = 0;
|
||||
this.newCampaign.webTemplateId = 0;
|
||||
this.newCampaign.phonebookId = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
.campaign-details:read-only {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.download-visits {
|
||||
color: #007bff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.download-visits:hover {
|
||||
color: #007bff;
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
<th scope="col">From</th>
|
||||
<th scope="col">To</th>
|
||||
<th scope="col">Currrent Status</th>
|
||||
<th scope="col">Web Status</th>
|
||||
<th scope="col">Web Route URL</th>
|
||||
<th scope="col">Time Sent</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -47,6 +49,8 @@
|
||||
<td>{{ job.fromNum }}</td>
|
||||
<td>{{ job.toNum }}</td>
|
||||
<td>{{ job.currentStatus }}</td>
|
||||
<td><span class="download-visits" (click)="downloadVisits(job.id)">{{ job.webStatus }}</span></td>
|
||||
<td><a href="{{ job.fullUrl }}">{{ job.fullUrl }}</a></td>
|
||||
<td>{{ job.timeSent | date:'dd-MMM-yyyy'}}</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core';
|
||||
import { CampaignService, Campaign, Job, CampaignNotification } from '../campaign.service';
|
||||
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
|
||||
import { NotificationService } from '../notification.service';
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
|
||||
@Component({
|
||||
selector: 'app-campaign-view',
|
||||
@@ -18,9 +19,28 @@ export class CampaignViewComponent implements OnInit {
|
||||
private campaignService: CampaignService,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
private notificationService: NotificationService
|
||||
private notificationService: NotificationService,
|
||||
private http: HttpClient
|
||||
) { }
|
||||
|
||||
downloadVisits(id: string) {
|
||||
fetch('/api/jobs/' + id + '/visits')
|
||||
.then(resp => resp.blob())
|
||||
.then(blob => {
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.style.display = 'none';
|
||||
a.href = url;
|
||||
// the filename you want
|
||||
a.download = 'visits-' + id + '.csv';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
this.notificationService.addNotification('success', 'Successfully retrived web visits');
|
||||
})
|
||||
.catch(() => this.notificationService.addNotification('failure', 'Failed to download web visits'));
|
||||
}
|
||||
|
||||
startCampaign() {
|
||||
this.campaignService.startCampaign(this.currCampaign).subscribe(campaignNotification => {
|
||||
this.notificationService.addNotification(campaignNotification.resultType, campaignNotification.text);
|
||||
|
||||
@@ -21,9 +21,11 @@ export class Campaign {
|
||||
export class Job {
|
||||
id: number;
|
||||
currentStatus: string;
|
||||
webStatus: string;
|
||||
timeSent: Date;
|
||||
fromNum: string;
|
||||
toNum: string;
|
||||
fullUrl: string;
|
||||
}
|
||||
|
||||
export class CampaignNotification {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<tr>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Status</th>
|
||||
<th scope="col">Web Status</th>
|
||||
<th scope="col">Target Size</th>
|
||||
<th scope="col">Create Date</th>
|
||||
</tr>
|
||||
@@ -20,6 +21,7 @@
|
||||
<tr routerLink="/campaign/{{ campaign.id }}/view">
|
||||
<td>{{ campaign.name }}</td>
|
||||
<td>{{ campaign.currentStatus }}</td>
|
||||
<td>{{ campaign.webStatus }}</td>
|
||||
<td>{{ campaign.size }}</td>
|
||||
<td>{{ campaign.createDate | date:'dd-MMM-yyyy'}}</td>
|
||||
</tr>
|
||||
|
||||
12
tapit-frontend/src/app/global-settings.service.spec.ts
Normal file
12
tapit-frontend/src/app/global-settings.service.spec.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { GlobalSettingsService } from './global-settings.service';
|
||||
|
||||
describe('GlobalSettingsService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: GlobalSettingsService = TestBed.get(GlobalSettingsService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
60
tapit-frontend/src/app/global-settings.service.ts
Normal file
60
tapit-frontend/src/app/global-settings.service.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { NotificationService } from './notification.service';
|
||||
import { Observable, of } from 'rxjs';
|
||||
|
||||
export class GlobalSettings {
|
||||
secretRegistrationCode: string;
|
||||
threadsPerCampaign: number;
|
||||
bcryptCost: number;
|
||||
maxRequestRetries: number;
|
||||
waitBeforeRetry: number;
|
||||
webTemplatePrefix: string;
|
||||
webTemplateRoute: string;
|
||||
}
|
||||
|
||||
export class GlobalSettingsNotification {
|
||||
resultType: string;
|
||||
text: string;
|
||||
payload: GlobalSettings;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class GlobalSettingsService {
|
||||
globalSettings = new GlobalSettings();
|
||||
|
||||
globalSettingsUrl = 'api/globalsettings';
|
||||
|
||||
httpOptions = {
|
||||
headers: new HttpHeaders({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
};
|
||||
|
||||
getGlobalSettings() {
|
||||
this.http.get<GlobalSettings>(this.globalSettingsUrl, this.httpOptions).subscribe(globalSettings => {
|
||||
this.globalSettings = globalSettings;
|
||||
});
|
||||
}
|
||||
|
||||
getGlobalSettingsObs(): Observable<GlobalSettings> {
|
||||
return this.http.get<GlobalSettings>(this.globalSettingsUrl, this.httpOptions);
|
||||
}
|
||||
|
||||
updateGlobalSettings(globalSettings: GlobalSettings) {
|
||||
this.http.put<GlobalSettingsNotification>(this.globalSettingsUrl, globalSettings, this.httpOptions).subscribe(settingsMessage => {
|
||||
this.notificationService.addNotification(settingsMessage.resultType, settingsMessage.text);
|
||||
this.globalSettings = settingsMessage.payload;
|
||||
},
|
||||
err => {
|
||||
this.notificationService.addNotification('failure', 'Error in updating settings');
|
||||
});
|
||||
}
|
||||
|
||||
constructor(private http: HttpClient, private router: Router, private notificationService: NotificationService) {
|
||||
this.getGlobalSettings();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 d-flex">
|
||||
<h4>Global TapIt Settings</h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 d-flex">
|
||||
<label for="registration-code" class="pr-2 mt-auto mb-auto">Secret Registration Code</label>
|
||||
<input type="text" class="flex-grow-1" id="registration-code" [(ngModel)]="displaySettings.secretRegistrationCode" >
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 d-flex">
|
||||
<label for="threads" class="pr-2 mt-auto mb-auto">SMS Threads Per Campaign</label>
|
||||
<input type="text" class="flex-grow-1" id="threads" [(ngModel)]="displaySettings.threadsPerCampaign" >
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 d-flex">
|
||||
<label for="bcrypt-cost" class="pr-2 mt-auto mb-auto">BCrypt Cost</label>
|
||||
<input type="text" class="flex-grow-1" id="bcrypt-cost" [(ngModel)]="displaySettings.bcryptCost" >
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 d-flex">
|
||||
<label for="max-retries" class="pr-2 mt-auto mb-auto">Max Request Retries</label>
|
||||
<input type="text" class="flex-grow-1" id="max-retries" [(ngModel)]="displaySettings.maxRequestRetries" >
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 d-flex">
|
||||
<label for="wait-time" class="pr-2 mt-auto mb-auto">Wait Before Retry (ms) </label>
|
||||
<input type="text" class="flex-grow-1" id="wait-time" [(ngModel)]="displaySettings.waitBeforeRetry" >
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 d-flex">
|
||||
<label for="web-prefix" class="pr-2 mt-auto mb-auto">Web URL Prefix</label>
|
||||
<input type="text" class="flex-grow-1" id="web-prefix" [(ngModel)]="displaySettings.webTemplatePrefix" >
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 d-flex">
|
||||
<label for="web-route" class="pr-2 mt-auto mb-auto">Web Route</label>
|
||||
<input type="text" class="flex-grow-1" id="web-route" [(ngModel)]="displaySettings.webTemplateRoute" >
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 d-flex">
|
||||
<button type="button" (click)="updateGlobalSettings()" class="btn btn-primary">Submit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { GlobalSettingsComponent } from './global-settings.component';
|
||||
|
||||
describe('GlobalSettingsComponent', () => {
|
||||
let component: GlobalSettingsComponent;
|
||||
let fixture: ComponentFixture<GlobalSettingsComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ GlobalSettingsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(GlobalSettingsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { GlobalSettings, GlobalSettingsService } from '../global-settings.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-global-settings',
|
||||
templateUrl: './global-settings.component.html',
|
||||
styleUrls: ['./global-settings.component.css']
|
||||
})
|
||||
export class GlobalSettingsComponent implements OnInit {
|
||||
|
||||
tempSettings = new GlobalSettings();
|
||||
displaySettings;
|
||||
|
||||
updateGlobalSettings() {
|
||||
this.tempSettings.secretRegistrationCode = this.displaySettings.secretRegistrationCode;
|
||||
this.tempSettings.webTemplatePrefix = this.displaySettings.webTemplatePrefix;
|
||||
this.tempSettings.webTemplateRoute = this.displaySettings.webTemplateRoute;
|
||||
this.tempSettings.threadsPerCampaign = parseInt(this.displaySettings.threadsPerCampaign, 10) + 0;
|
||||
this.tempSettings.bcryptCost = parseInt(this.displaySettings.bcryptCost, 10);
|
||||
this.tempSettings.maxRequestRetries = parseInt(this.displaySettings.maxRequestRetries, 10);
|
||||
this.tempSettings.waitBeforeRetry = parseInt(this.displaySettings.waitBeforeRetry, 10);
|
||||
|
||||
this.globalSettingsService.updateGlobalSettings(this.tempSettings);
|
||||
}
|
||||
|
||||
constructor(private globalSettingsService: GlobalSettingsService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.globalSettingsService.getGlobalSettingsObs().subscribe(settings => {
|
||||
console.log(settings);
|
||||
this.displaySettings = settings;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,6 +28,7 @@
|
||||
<li>{{ '{' }}lastName{{ '}' }}</li>
|
||||
<li>{{ '{' }}alias{{ '}' }}</li>
|
||||
<li>{{ '{' }}phoneNumber{{ '}' }}</li>
|
||||
<li>{{ '{' }}url{{ '}' }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -41,6 +42,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="completeModal" tabindex="-1" role="dialog" aria-labelledby="completeModal" aria-hidden="true">
|
||||
|
||||
@@ -28,6 +28,7 @@ export class TextTemplateNewComponent implements OnInit {
|
||||
tempStr = tempStr.replace('{lastName}', 'Smith');
|
||||
tempStr = tempStr.replace('{alias}', 'Johnny');
|
||||
tempStr = tempStr.replace('{phoneNumber}', '+6598765432');
|
||||
tempStr = tempStr.replace('{url}', 'https://www.example.com/eR2c1');
|
||||
|
||||
this.previewStr = tempStr;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
<div class="row p-2">
|
||||
<div class="col-12">
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 d-flex">
|
||||
<label for="campaignName" class="pr-2 mt-auto mb-auto">Web Template Name</label>
|
||||
<input type="text" class="flex-grow-1" id="campaignName" [(ngModel)]="newWebTemplate.name" placeholder="Web Template Name">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 d-flex">
|
||||
<label for="web-template-type">Web Template Type</label>
|
||||
<select class="form-control" [(ngModel)]="newWebTemplate.templateType" id="web-template-type">
|
||||
<option></option>
|
||||
<option *ngFor="let templateEnum of webTemplateService.wTemplateEnum" [ngValue]="templateEnum.tag">{{templateEnum.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- for redirect -->
|
||||
<ng-container *ngIf="newWebTemplate.templateType === 'redirect'">
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 d-flex">
|
||||
<label for="redirect-url" class="pr-2 mt-auto mb-auto">Redirect URL</label>
|
||||
<input type="text" class="flex-grow-1" id="redirect-url" [(ngModel)]="newWebTemplate.redirectUrl" placeholder="https://www.attacker.com">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 d-flex">
|
||||
<label for="positive-redirect" class="pr-2 mt-auto mb-auto">Positive Redirect UA (Comma separated, leave blank if unused)</label>
|
||||
<input type="text" class="flex-grow-1" id="positive-redirect" [(ngModel)]="newWebTemplate.redirectAgent" placeholder="Windows NT">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 d-flex">
|
||||
<label for="negative-redirect" class="pr-2 mt-auto mb-auto">Negative Redirect UA (Comma Seperated, leave blank if unused)</label>
|
||||
<input type="text" class="flex-grow-1" id="negative-redirect" [(ngModel)]="newWebTemplate.redirectNegAgent" placeholder="Android,iPhone">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12 d-flex">
|
||||
<p><small><em>Use only either 'Positive' or 'Negative' redirect. DO NOT USE BOTH.</em></small></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 d-flex">
|
||||
<label for="placeholder-html" class="pr-2 mt-auto mb-auto">Placeholder HTML</label>
|
||||
<textarea class="form-control flex" [(ngModel)]="newWebTemplate.redirectPlaceholderHtml" id="placeholder-html" rows="6"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="newWebTemplate.templateType === 'harvester'">
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 d-flex">
|
||||
<label for="placeholder-harvest-html" class="pr-2 mt-auto mb-auto">Placeholder HTML</label>
|
||||
<textarea class="form-control flex" [(ngModel)]="newWebTemplate.harvesterBeforeHtml" id="placeholder-harvest-html" rows="6"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 d-flex">
|
||||
<label for="after-html" class="pr-2 mt-auto mb-auto">After HTML</label>
|
||||
<textarea class="form-control flex" [(ngModel)]="newWebTemplate.harvesterAfterHtml" id="after-html" rows="6"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div class="row mt-3">
|
||||
<div class="col-12 d-flex">
|
||||
<button type="button" (click)="submitNewWebTemplate()" class="btn btn-primary ml-2">Save Web Template</button>
|
||||
<button type="button" *ngIf="router.url !== '/web-template/new'" class="btn btn-danger ml-auto" data-toggle="modal" data-target="#completeModal">Delete</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="completeModal" tabindex="-1" role="dialog" aria-labelledby="completeModal" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="exampleModalLabel">{{ newWebTemplate.name }}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Are you sure you want to delete the text template?</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
<button type="button" class="btn btn-danger" (click)="deleteWebTemplate()" data-dismiss="modal">Delete Web Template</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { WebTemplateNewComponent } from './web-template-new.component';
|
||||
|
||||
describe('WebTemplateNewComponent', () => {
|
||||
let component: WebTemplateNewComponent;
|
||||
let fixture: ComponentFixture<WebTemplateNewComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ WebTemplateNewComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(WebTemplateNewComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { WebTemplateService, WebTemplate } from '../web-template.service';
|
||||
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-web-template-new',
|
||||
templateUrl: './web-template-new.component.html',
|
||||
styleUrls: ['./web-template-new.component.css']
|
||||
})
|
||||
export class WebTemplateNewComponent implements OnInit {
|
||||
|
||||
newWebTemplate: WebTemplate = new WebTemplate();
|
||||
id = 0;
|
||||
|
||||
submitNewWebTemplate() {
|
||||
if (this.router.url === '/web-template/new') {
|
||||
this.webTemplateService.addWebTemplate(this.newWebTemplate);
|
||||
} else {
|
||||
this.editWebTemplate();
|
||||
}
|
||||
}
|
||||
|
||||
deleteWebTemplate() {
|
||||
this.webTemplateService.deleteWebTemplate(this.newWebTemplate);
|
||||
}
|
||||
|
||||
editWebTemplate() {
|
||||
this.webTemplateService.editWebTemplate(this.newWebTemplate);
|
||||
}
|
||||
|
||||
constructor(private webTemplateService: WebTemplateService, private router: Router, private route: ActivatedRoute) { }
|
||||
|
||||
ngOnInit() {
|
||||
// if page is edit
|
||||
if (this.router.url !== '/web-template/new') {
|
||||
const idParam = 'id';
|
||||
this.route.params.subscribe( params => {
|
||||
this.id = parseInt(params[idParam], 10);
|
||||
this.webTemplateService.getWebTemplateObs(this.id).subscribe(currWT => {
|
||||
this.newWebTemplate = currWT;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
12
tapit-frontend/src/app/web-template.service.spec.ts
Normal file
12
tapit-frontend/src/app/web-template.service.spec.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { WebTemplateService } from './web-template.service';
|
||||
|
||||
describe('WebTemplateService', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({}));
|
||||
|
||||
it('should be created', () => {
|
||||
const service: WebTemplateService = TestBed.get(WebTemplateService);
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
||||
97
tapit-frontend/src/app/web-template.service.ts
Normal file
97
tapit-frontend/src/app/web-template.service.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { NotificationService } from './notification.service';
|
||||
|
||||
export class WebTemplate {
|
||||
id: number;
|
||||
name: string;
|
||||
templateType: string; // enum redirect, harvester
|
||||
redirectAgent: string;
|
||||
redirectNegAgent: string;
|
||||
redirectPlaceholderHtml: string;
|
||||
redirectUrl: string;
|
||||
harvesterBeforeHtml: string;
|
||||
harvesterAfterHtml: string;
|
||||
createDate: Date;
|
||||
}
|
||||
|
||||
export class WebTemplateNotification {
|
||||
resultType: string;
|
||||
text: string;
|
||||
payload: WebTemplate;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class WebTemplateService {
|
||||
|
||||
wTemplateEnum = [
|
||||
{name: 'Redirect Based On User Agent', tag: 'redirect'},
|
||||
{name: 'Credentials Harvesting', tag: 'harvester'},
|
||||
];
|
||||
|
||||
templateUrl = '/api/web-template';
|
||||
httpOptions = {
|
||||
headers: new HttpHeaders({
|
||||
'Content-Type': 'application/json',
|
||||
}),
|
||||
};
|
||||
|
||||
webTemplates: WebTemplate[] = [];
|
||||
|
||||
getWebTemplates() {
|
||||
this.http.get<WebTemplate[]>(this.templateUrl).subscribe(templates => {
|
||||
if (templates === null) {
|
||||
this.webTemplates = [];
|
||||
} else {
|
||||
this.webTemplates = templates;
|
||||
}
|
||||
});
|
||||
}
|
||||
addWebTemplate(newWebTemplate: WebTemplate) {
|
||||
this.http.post<WebTemplateNotification>(this.templateUrl, newWebTemplate, this.httpOptions).subscribe(templateNotification => {
|
||||
this.notificationService.addNotification(templateNotification.resultType, templateNotification.text);
|
||||
this.webTemplates.push(templateNotification.payload);
|
||||
if (templateNotification.payload !== null) {
|
||||
this.router.navigate(['/web-template']);
|
||||
}
|
||||
},
|
||||
err => {
|
||||
this.notificationService.addNotification('failure', 'Error in creating template');
|
||||
});
|
||||
}
|
||||
|
||||
deleteWebTemplate(webTemplate: WebTemplate) {
|
||||
this.http.delete<WebTemplateNotification>(this.templateUrl + '/' + webTemplate.id.toString(), this.httpOptions)
|
||||
.subscribe(templateNotification => {
|
||||
this.notificationService.addNotification(templateNotification.resultType, templateNotification.text);
|
||||
this.router.navigate(['/web-template']);
|
||||
},
|
||||
err => {
|
||||
this.notificationService.addNotification('failure', 'Error in deleting web template');
|
||||
});
|
||||
}
|
||||
|
||||
editWebTemplate(webTemplate: WebTemplate) {
|
||||
this.http.put<WebTemplateNotification>(this.templateUrl + '/' + webTemplate.id.toString(), webTemplate, this.httpOptions)
|
||||
.subscribe(templateNotification => {
|
||||
this.notificationService.addNotification(templateNotification.resultType, templateNotification.text);
|
||||
if (templateNotification.payload !== null) {
|
||||
this.router.navigate(['/web-template']);
|
||||
}
|
||||
},
|
||||
err => {
|
||||
this.notificationService.addNotification('failure', 'Error in editing web template');
|
||||
});
|
||||
}
|
||||
|
||||
getWebTemplateObs(id: number) {
|
||||
return this.http.get<WebTemplate>(this.templateUrl + '/' + id.toString());
|
||||
}
|
||||
|
||||
constructor(private http: HttpClient, private router: Router, private notificationService: NotificationService) {
|
||||
this.getWebTemplates();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<button class="btn btn-primary" routerLink="/web-template/new">New Web Template</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<div class="col-12">
|
||||
<table class="table table-hover">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Type</th>
|
||||
<th scope="col">Create Date</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<ng-container *ngFor="let webTemplate of webTemplateService.webTemplates">
|
||||
<tr routerLink="/web-template/{{ webTemplate.id }}/edit">
|
||||
<td>{{ webTemplate.name }}</td>
|
||||
<td>{{ webTemplate.templateType }}</td>
|
||||
<td>{{ webTemplate.createDate | date:'dd-MMM-yyyy'}}</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
<p *ngIf="webTemplateService.webTemplates.length === 0">No web template created yet. Create templates by clicking <a routerLink="/web-template/new">here</a></p>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { WebTemplateComponent } from './web-template.component';
|
||||
|
||||
describe('WebTemplateComponent', () => {
|
||||
let component: WebTemplateComponent;
|
||||
let fixture: ComponentFixture<WebTemplateComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ WebTemplateComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(WebTemplateComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { WebTemplateService } from '../web-template.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-web-template',
|
||||
templateUrl: './web-template.component.html',
|
||||
styleUrls: ['./web-template.component.css']
|
||||
})
|
||||
export class WebTemplateComponent implements OnInit {
|
||||
|
||||
constructor(private webTemplateService: WebTemplateService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.webTemplateService.getWebTemplates();
|
||||
}
|
||||
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 7.2 KiB |
BIN
tapit-frontend/src/logo.png
Normal file
BIN
tapit-frontend/src/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
Reference in New Issue
Block a user