mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-06 17:44:11 +00:00
script insert and test cases with dynamic site key
This commit is contained in:

committed by
Sufiyan Shaikh

parent
4906516359
commit
0953806865
@@ -78,6 +78,7 @@
|
||||
"@nguniversal/express-engine": "^13.0.2",
|
||||
"@ngx-translate/core": "^13.0.0",
|
||||
"@nicky-lenaers/ngx-scroll-to": "^9.0.0",
|
||||
"@types/grecaptcha": "^3.0.4",
|
||||
"angular-idle-preload": "3.0.0",
|
||||
"angulartics2": "^12.0.0",
|
||||
"axios": "^0.27.2",
|
||||
@@ -110,7 +111,6 @@
|
||||
"moment": "^2.29.4",
|
||||
"morgan": "^1.10.0",
|
||||
"ng-mocks": "^13.1.1",
|
||||
"ng-recaptcha": "^9.0.0",
|
||||
"ng2-file-upload": "1.4.0",
|
||||
"ng2-nouislider": "^1.8.3",
|
||||
"ngx-infinite-scroll": "^10.0.1",
|
||||
|
@@ -178,8 +178,6 @@ import { OrcidHistoryDataService } from './orcid/orcid-history-data.service';
|
||||
import { OrcidQueue } from './orcid/model/orcid-queue.model';
|
||||
import { OrcidHistory } from './orcid/model/orcid-history.model';
|
||||
import { OrcidAuthService } from './orcid/orcid-auth.service';
|
||||
import { GoogleRecaptchaService } from './data/google-recaptcha.service';
|
||||
import { RecaptchaV3Module, RECAPTCHA_V3_SITE_KEY } from 'ng-recaptcha';
|
||||
|
||||
/**
|
||||
* When not in production, endpoint responses can be mocked for testing purposes
|
||||
@@ -195,7 +193,6 @@ export const restServiceFactory = (mocks: ResponseMapMock, http: HttpClient) =>
|
||||
|
||||
const IMPORTS = [
|
||||
CommonModule,
|
||||
RecaptchaV3Module,
|
||||
StoreModule.forFeature('core', coreReducers, storeModuleConfig as StoreConfig<CoreState, Action>),
|
||||
EffectsModule.forFeature(coreEffects)
|
||||
];
|
||||
@@ -312,8 +309,6 @@ const PROVIDERS = [
|
||||
OrcidAuthService,
|
||||
OrcidQueueService,
|
||||
OrcidHistoryDataService,
|
||||
GoogleRecaptchaService,
|
||||
{ provide: RECAPTCHA_V3_SITE_KEY, useValue: environment.recaptchaSiteKey }
|
||||
];
|
||||
|
||||
/**
|
||||
|
@@ -1,32 +0,0 @@
|
||||
import { GoogleRecaptchaService } from './google-recaptcha.service';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
|
||||
describe('GoogleRecaptchaService', () => {
|
||||
let service: GoogleRecaptchaService;
|
||||
|
||||
let reCaptchaV3Service;
|
||||
|
||||
function init() {
|
||||
reCaptchaV3Service = jasmine.createSpyObj('reCaptchaV3Service', {
|
||||
execute: observableOf('googleRecaptchaToken')
|
||||
});
|
||||
service = new GoogleRecaptchaService(reCaptchaV3Service);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
init();
|
||||
});
|
||||
|
||||
describe('getRecaptchaToken', () => {
|
||||
let result;
|
||||
|
||||
beforeEach(() => {
|
||||
result = service.getRecaptchaToken('test');
|
||||
});
|
||||
|
||||
it('should send a Request with action', () => {
|
||||
expect(reCaptchaV3Service.execute).toHaveBeenCalledWith('test');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
@@ -1,22 +0,0 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ReCaptchaV3Service } from 'ng-recaptcha';
|
||||
|
||||
/**
|
||||
* A GoogleRecaptchaService used to send action and get a token from REST
|
||||
*/
|
||||
@Injectable()
|
||||
export class GoogleRecaptchaService {
|
||||
|
||||
constructor(
|
||||
private recaptchaV3Service: ReCaptchaV3Service
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Returns an observable of string
|
||||
* @param action action is the process type in which used to protect multiple spam REST calls
|
||||
*/
|
||||
public getRecaptchaToken (action) {
|
||||
return this.recaptchaV3Service.execute(action);
|
||||
}
|
||||
|
||||
}
|
20
src/app/core/google-recaptcha/google-recaptcha.module.ts
Normal file
20
src/app/core/google-recaptcha/google-recaptcha.module.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { SharedModule } from '../../shared/shared.module';
|
||||
|
||||
import { GoogleRecaptchaService } from './google-recaptcha.service';
|
||||
|
||||
const PROVIDERS = [
|
||||
GoogleRecaptchaService
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [ SharedModule ],
|
||||
providers: [
|
||||
...PROVIDERS
|
||||
]
|
||||
})
|
||||
|
||||
/**
|
||||
* This module handles google recaptcha functionalities
|
||||
*/
|
||||
export class GoogleRecaptchaModule {}
|
@@ -0,0 +1,47 @@
|
||||
import { GoogleRecaptchaService } from './google-recaptcha.service';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../../shared/remote-data.utils';
|
||||
|
||||
describe('GoogleRecaptchaService', () => {
|
||||
let service: GoogleRecaptchaService;
|
||||
|
||||
let rendererFactory2;
|
||||
let configurationDataService;
|
||||
let spy: jasmine.Spy;
|
||||
let scriptElementMock: any;
|
||||
const innerHTMLTestValue = 'mock-script-inner-html';
|
||||
const document = { documentElement: { lang: 'en' } } as Document;
|
||||
scriptElementMock = {
|
||||
set innerHTML(newVal) { /* noop */ },
|
||||
get innerHTML() { return innerHTMLTestValue; }
|
||||
};
|
||||
|
||||
function init() {
|
||||
rendererFactory2 = jasmine.createSpyObj('rendererFactory2', {
|
||||
createRenderer: observableOf('googleRecaptchaToken'),
|
||||
createElement: scriptElementMock
|
||||
});
|
||||
configurationDataService = jasmine.createSpyObj('configurationDataService', {
|
||||
findByPropertyName: createSuccessfulRemoteDataObject$({ values: ['googleRecaptchaToken'] })
|
||||
});
|
||||
service = new GoogleRecaptchaService(document, rendererFactory2, configurationDataService);
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
init();
|
||||
});
|
||||
|
||||
describe('getRecaptchaToken', () => {
|
||||
let result;
|
||||
|
||||
beforeEach(() => {
|
||||
spy = spyOn(service, 'getRecaptchaToken').and.stub();
|
||||
});
|
||||
|
||||
it('should send a Request with action', () => {
|
||||
service.getRecaptchaToken('test');
|
||||
expect(spy).toHaveBeenCalledWith('test');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
70
src/app/core/google-recaptcha/google-recaptcha.service.ts
Normal file
70
src/app/core/google-recaptcha/google-recaptcha.service.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { Inject, Injectable, Renderer2, RendererFactory2 } from '@angular/core';
|
||||
import { getFirstCompletedRemoteData } from '../shared/operators';
|
||||
import { ConfigurationProperty } from '../shared/configuration-property.model';
|
||||
import { isNotEmpty } from '../../shared/empty.util';
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { ConfigurationDataService } from '../data/configuration-data.service';
|
||||
import { RemoteData } from '../data/remote-data';
|
||||
|
||||
/**
|
||||
* A GoogleRecaptchaService used to send action and get a token from REST
|
||||
*/
|
||||
@Injectable()
|
||||
export class GoogleRecaptchaService {
|
||||
|
||||
private renderer: Renderer2;
|
||||
captchaSiteKey: string;
|
||||
|
||||
constructor(
|
||||
@Inject(DOCUMENT) private _document: Document,
|
||||
rendererFactory: RendererFactory2,
|
||||
private configService: ConfigurationDataService,
|
||||
) {
|
||||
this.renderer = rendererFactory.createRenderer(null, null);
|
||||
this.configService.findByPropertyName('google.recaptcha.key').pipe(
|
||||
getFirstCompletedRemoteData(),
|
||||
).subscribe((res: RemoteData<ConfigurationProperty>) => {
|
||||
if (res.hasSucceeded && isNotEmpty(res?.payload?.values[0])) {
|
||||
this.captchaSiteKey = res?.payload?.values[0];
|
||||
this.loadScript(this.buildCaptchaUrl(res?.payload?.values[0]));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an observable of string
|
||||
* @param action action is the process type in which used to protect multiple spam REST calls
|
||||
*/
|
||||
public async getRecaptchaToken (action) {
|
||||
return await grecaptcha.execute(this.captchaSiteKey, {action: action});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the google captcha ur with google captchas api key
|
||||
*
|
||||
* @param key contains a secret key of a google captchas
|
||||
* @returns string which has google captcha url with google captchas key
|
||||
*/
|
||||
buildCaptchaUrl(key: string) {
|
||||
return `https://www.google.com/recaptcha/api.js?render=${key}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the google captchas script to the document
|
||||
*
|
||||
* @param url contains a script url which will be loaded into page
|
||||
* @returns A promise
|
||||
*/
|
||||
private loadScript(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const script = this.renderer.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.src = url;
|
||||
script.text = ``;
|
||||
script.onload = resolve;
|
||||
script.onerror = reject;
|
||||
this.renderer.appendChild(this._document.head, script);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { waitForAsync, ComponentFixture, TestBed, tick, fakeAsync } from '@angular/core/testing';
|
||||
import { of as observableOf } from 'rxjs';
|
||||
import { RestResponse } from '../core/cache/response.models';
|
||||
import { CommonModule } from '@angular/common';
|
||||
@@ -15,7 +15,7 @@ import { NotificationsServiceStub } from '../shared/testing/notifications-servic
|
||||
import { RegisterEmailFormComponent } from './register-email-form.component';
|
||||
import { createSuccessfulRemoteDataObject$ } from '../shared/remote-data.utils';
|
||||
import { ConfigurationDataService } from '../core/data/configuration-data.service';
|
||||
import { GoogleRecaptchaService } from '../core/data/google-recaptcha.service';
|
||||
import { GoogleRecaptchaService } from '../core/google-recaptcha/google-recaptcha.service';
|
||||
|
||||
describe('RegisterEmailComponent', () => {
|
||||
|
||||
@@ -31,7 +31,7 @@ describe('RegisterEmailComponent', () => {
|
||||
});
|
||||
|
||||
const googleRecaptchaService = jasmine.createSpyObj('googleRecaptchaService', {
|
||||
getRecaptchaToken: jasmine.createSpy('getRecaptchaToken')
|
||||
getRecaptchaToken: Promise.resolve('googleRecaptchaToken')
|
||||
});
|
||||
|
||||
const confResponse$ = createSuccessfulRemoteDataObject$({ values: ['true'] });
|
||||
@@ -64,7 +64,6 @@ describe('RegisterEmailComponent', () => {
|
||||
fixture = TestBed.createComponent(RegisterEmailFormComponent);
|
||||
comp = fixture.componentInstance;
|
||||
configurationDataService.findByPropertyName.and.returnValues(confResponseDisabled$, confResponseDisabled$, confResponseDisabled$, confResponseDisabled$, confResponseDisabled$, confResponseDisabled$, confResponseDisabled$, confResponseDisabled$, confResponseDisabled$, confResponseDisabled$);
|
||||
googleRecaptchaService.getRecaptchaToken.and.returnValue(observableOf('googleRecaptchaToken'));
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
@@ -108,29 +107,30 @@ describe('RegisterEmailComponent', () => {
|
||||
});
|
||||
});
|
||||
describe('register with google recaptcha', () => {
|
||||
beforeEach(waitForAsync(() => {
|
||||
beforeEach(fakeAsync(() => {
|
||||
configurationDataService.findByPropertyName.and.returnValues(confResponse$, confResponse$, confResponse$, confResponse$, confResponse$, confResponse$, confResponse$, confResponse$, confResponse$, confResponse$);
|
||||
comp.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
it('should send a registration to the service and on success display a message and return to home', () => {
|
||||
it('should send a registration to the service and on success display a message and return to home', fakeAsync(() => {
|
||||
comp.form.patchValue({email: 'valid@email.org'});
|
||||
|
||||
comp.register();
|
||||
tick();
|
||||
expect(epersonRegistrationService.registerEmail).toHaveBeenCalledWith('valid@email.org', 'googleRecaptchaToken');
|
||||
expect(notificationsService.success).toHaveBeenCalled();
|
||||
expect(router.navigate).toHaveBeenCalledWith(['/home']);
|
||||
});
|
||||
it('should send a registration to the service and on error display a message', () => {
|
||||
}));
|
||||
it('should send a registration to the service and on error display a message', fakeAsync(() => {
|
||||
(epersonRegistrationService.registerEmail as jasmine.Spy).and.returnValue(observableOf(new RestResponse(false, 400, 'Bad Request')));
|
||||
|
||||
comp.form.patchValue({email: 'valid@email.org'});
|
||||
|
||||
comp.register();
|
||||
tick();
|
||||
expect(epersonRegistrationService.registerEmail).toHaveBeenCalledWith('valid@email.org', 'googleRecaptchaToken');
|
||||
expect(notificationsService.error).toHaveBeenCalled();
|
||||
expect(router.navigate).not.toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
@@ -6,12 +6,12 @@ import { Router } from '@angular/router';
|
||||
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { Registration } from '../core/shared/registration.model';
|
||||
import { RemoteData } from '../core/data/remote-data';
|
||||
import { GoogleRecaptchaService } from '../core/data/google-recaptcha.service';
|
||||
import { ConfigurationDataService } from '../core/data/configuration-data.service';
|
||||
import { getFirstCompletedRemoteData } from '../core/shared/operators';
|
||||
import { ConfigurationProperty } from '../core/shared/configuration-property.model';
|
||||
import { isNotEmpty } from '../shared/empty.util';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { GoogleRecaptchaService } from '../core/google-recaptcha/google-recaptcha.service';
|
||||
|
||||
@Component({
|
||||
selector: 'ds-register-email-form',
|
||||
@@ -71,19 +71,18 @@ export class RegisterEmailFormComponent implements OnInit {
|
||||
/**
|
||||
* Register an email address
|
||||
*/
|
||||
register() {
|
||||
async register() {
|
||||
if (!this.form.invalid) {
|
||||
if (this.registrationVerification) {
|
||||
this.googleRecaptchaService.getRecaptchaToken('register_email').subscribe(captcha => {
|
||||
if (isNotEmpty(captcha)) {
|
||||
this.registeration(captcha);
|
||||
} else {
|
||||
this.notificationService.error(this.translateService.get(`${this.MESSAGE_PREFIX}.error.head`),
|
||||
this.translateService.get(`${this.MESSAGE_PREFIX}.error.recaptcha`, {email: this.email.value}));
|
||||
}
|
||||
});
|
||||
const token = await this.googleRecaptchaService.getRecaptchaToken('register_email');
|
||||
if (isNotEmpty(token)) {
|
||||
this.registeration(token);
|
||||
} else {
|
||||
this.notificationService.error(this.translateService.get(`${this.MESSAGE_PREFIX}.error.head`),
|
||||
this.translateService.get(`${this.MESSAGE_PREFIX}.error.recaptcha`, {email: this.email.value}));
|
||||
}
|
||||
} else {
|
||||
this.registeration(null);
|
||||
this.registeration();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,12 +90,14 @@ export class RegisterEmailFormComponent implements OnInit {
|
||||
/**
|
||||
* Register an email address
|
||||
*/
|
||||
registeration(captchaToken) {
|
||||
let a = this.epersonRegistrationService.registerEmail(this.email.value);
|
||||
registeration(captchaToken = null) {
|
||||
let registerEmail$;
|
||||
if (captchaToken) {
|
||||
a = this.epersonRegistrationService.registerEmail(this.email.value, captchaToken);
|
||||
registerEmail$ = this.epersonRegistrationService.registerEmail(this.email.value, captchaToken);
|
||||
} else {
|
||||
registerEmail$ = this.epersonRegistrationService.registerEmail(this.email.value);
|
||||
}
|
||||
a.subscribe((response: RemoteData<Registration>) => {
|
||||
registerEmail$.subscribe((response: RemoteData<Registration>) => {
|
||||
if (response.hasSucceeded) {
|
||||
this.notificationService.success(this.translateService.get(`${this.MESSAGE_PREFIX}.success.head`),
|
||||
this.translateService.get(`${this.MESSAGE_PREFIX}.success.content`, {email: this.email.value}));
|
||||
|
@@ -2,11 +2,13 @@ import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
import { RegisterEmailFormComponent } from './register-email-form.component';
|
||||
import { GoogleRecaptchaModule } from '../core/google-recaptcha/google-recaptcha.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
CommonModule,
|
||||
SharedModule,
|
||||
GoogleRecaptchaModule
|
||||
],
|
||||
declarations: [
|
||||
RegisterEmailFormComponent,
|
||||
|
@@ -2,7 +2,7 @@
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/app",
|
||||
"types": []
|
||||
"types": ["grecaptcha"]
|
||||
},
|
||||
"files": [
|
||||
"src/main.browser.ts",
|
||||
|
@@ -4,7 +4,8 @@
|
||||
"outDir": "./out-tsc/app-server",
|
||||
"target": "es2016",
|
||||
"types": [
|
||||
"node"
|
||||
"node",
|
||||
"grecaptcha"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
|
@@ -5,7 +5,8 @@
|
||||
"outDir": "./out-tsc/spec",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"node"
|
||||
"node",
|
||||
"grecaptcha"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
|
10
yarn.lock
10
yarn.lock
@@ -2247,7 +2247,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/file-saver/-/file-saver-2.0.5.tgz#9ee342a5d1314bb0928375424a2f162f97c310c7"
|
||||
integrity sha512-zv9kNf3keYegP5oThGLaPk8E081DFDuwfqjtiTzm6PoxChdJ1raSuADf2YGCVIyrSynLrgc8JWv296s7Q7pQSQ==
|
||||
|
||||
"@types/grecaptcha@^3.0.3":
|
||||
"@types/grecaptcha@^3.0.4":
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/grecaptcha/-/grecaptcha-3.0.4.tgz#3de601f3b0cd0298faf052dd5bd62aff64c2be2e"
|
||||
integrity sha512-7l1Y8DTGXkx/r4pwU1nMVAR+yD/QC+MCHKXAyEX/7JZhwcN1IED09aZ9vCjjkcGdhSQiu/eJqcXInpl6eEEEwg==
|
||||
@@ -8863,14 +8863,6 @@ ng-mocks@^13.1.1:
|
||||
resolved "https://registry.yarnpkg.com/ng-mocks/-/ng-mocks-13.1.1.tgz#e967dacc420c2ecec71826c5f24e0120186fad0b"
|
||||
integrity sha512-LYi/1ccDwHKLwi4/hvUsmxBDeQ+n8BTdg5f1ujCDCO7OM9OVnqkQcRlBHHK+5iFtEn/aqa2QG3xU/qwIhnaL4w==
|
||||
|
||||
ng-recaptcha@^9.0.0:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ng-recaptcha/-/ng-recaptcha-9.0.0.tgz#11b88f820cfca366d363fffd0f451490f2db2b04"
|
||||
integrity sha512-39YfJh1+p6gvfsGUhC8cmjhFZ1TtQ1OJES5SUgnanPL2aQuwrSX4WyTFh2liFn1dQqtGUVd5O4EhbcexB7AECQ==
|
||||
dependencies:
|
||||
"@types/grecaptcha" "^3.0.3"
|
||||
tslib "^2.2.0"
|
||||
|
||||
ng2-file-upload@1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/ng2-file-upload/-/ng2-file-upload-1.4.0.tgz#8dea28d573234c52af474ad2a4001b335271e5c4"
|
||||
|
Reference in New Issue
Block a user