Commit a9bfc40d authored by FORESTIER Fabien's avatar FORESTIER Fabien
Browse files

Add front encryption

parent 21c19c54
......@@ -2315,7 +2315,7 @@
},
"@types/q": {
"version": "0.0.32",
"resolved": "http://registry.npmjs.org/@types/q/-/q-0.0.32.tgz",
"resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz",
"integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=",
"dev": true
},
......@@ -2674,7 +2674,7 @@
},
"readable-stream": {
"version": "1.1.14",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
"dev": true,
"optional": true,
......@@ -2694,7 +2694,7 @@
},
"string_decoder": {
"version": "0.10.31",
"resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
"dev": true,
"optional": true
......@@ -3018,7 +3018,7 @@
},
"axios": {
"version": "0.15.3",
"resolved": "http://registry.npmjs.org/axios/-/axios-0.15.3.tgz",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.15.3.tgz",
"integrity": "sha1-LJ1jiy4ZGgjqHWzJiOrda6W9wFM=",
"dev": true,
"optional": true,
......@@ -3320,7 +3320,7 @@
},
"bl": {
"version": "1.1.2",
"resolved": "http://registry.npmjs.org/bl/-/bl-1.1.2.tgz",
"resolved": "https://registry.npmjs.org/bl/-/bl-1.1.2.tgz",
"integrity": "sha1-/cqHGplxOqANGeO7ukHER4emU5g=",
"dev": true,
"optional": true,
......@@ -3337,7 +3337,7 @@
},
"readable-stream": {
"version": "2.0.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
"integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
"dev": true,
"optional": true,
......@@ -3352,7 +3352,7 @@
},
"string_decoder": {
"version": "0.10.31",
"resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
"dev": true,
"optional": true
......@@ -3386,7 +3386,7 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
}
......@@ -4846,7 +4846,7 @@
},
"doctrine": {
"version": "0.7.2",
"resolved": "http://registry.npmjs.org/doctrine/-/doctrine-0.7.2.tgz",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-0.7.2.tgz",
"integrity": "sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM=",
"dev": true,
"requires": {
......@@ -5052,7 +5052,7 @@
},
"engine.io": {
"version": "3.1.5",
"resolved": "http://registry.npmjs.org/engine.io/-/engine.io-3.1.5.tgz",
"resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.1.5.tgz",
"integrity": "sha512-D06ivJkYxyRrcEe0bTpNnBQNgP9d3xog+qZlLbui8EsMr/DouQpf5o9FzJnWYHEYE0YsFHllUv2R1dkgYZXHcA==",
"dev": true,
"requires": {
......@@ -5078,7 +5078,7 @@
},
"engine.io-client": {
"version": "3.1.6",
"resolved": "http://registry.npmjs.org/engine.io-client/-/engine.io-client-3.1.6.tgz",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.1.6.tgz",
"integrity": "sha512-hnuHsFluXnsKOndS4Hv6SvUrgdYx1pk2NqfaDMW+GWdgfU3+/V25Cj7I8a0x92idSpa5PIhJRKxPvp9mnoLsfg==",
"dev": true,
"requires": {
......@@ -5214,7 +5214,7 @@
},
"es6-promisify": {
"version": "5.0.0",
"resolved": "http://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
"integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
"dev": true,
"requires": {
......@@ -5394,7 +5394,7 @@
},
"expand-range": {
"version": "0.1.1",
"resolved": "http://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz",
"resolved": "https://registry.npmjs.org/expand-range/-/expand-range-0.1.1.tgz",
"integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=",
"dev": true,
"requires": {
......@@ -5694,7 +5694,7 @@
},
"follow-redirects": {
"version": "1.0.0",
"resolved": "http://registry.npmjs.org/follow-redirects/-/follow-redirects-1.0.0.tgz",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.0.0.tgz",
"integrity": "sha1-jjQpjL0uF28lTv/sdaHHjMhJ/Tc=",
"dev": true,
"optional": true,
......@@ -5843,14 +5843,12 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
......@@ -5865,20 +5863,17 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"core-util-is": {
"version": "1.0.2",
......@@ -5995,8 +5990,7 @@
"inherits": {
"version": "2.0.3",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"ini": {
"version": "1.3.5",
......@@ -6008,7 +6002,6 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
......@@ -6023,7 +6016,6 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
......@@ -6135,8 +6127,7 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true,
"optional": true
"dev": true
},
"object-assign": {
"version": "4.1.1",
......@@ -6269,7 +6260,6 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
......@@ -6373,7 +6363,7 @@
},
"readable-stream": {
"version": "1.1.14",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
"integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
"dev": true,
"optional": true,
......@@ -6386,7 +6376,7 @@
},
"string_decoder": {
"version": "0.10.31",
"resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
"dev": true,
"optional": true
......@@ -8451,13 +8441,13 @@
"dependencies": {
"core-js": {
"version": "2.3.0",
"resolved": "http://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz",
"integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU=",
"dev": true
},
"es6-promise": {
"version": "3.0.2",
"resolved": "http://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz",
"integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=",
"dev": true
},
......@@ -8469,7 +8459,7 @@
},
"readable-stream": {
"version": "2.0.6",
"resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
"integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
"dev": true,
"requires": {
......@@ -8483,7 +8473,7 @@
},
"string_decoder": {
"version": "0.10.31",
"resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
"dev": true
}
......@@ -9063,7 +9053,7 @@
"dependencies": {
"iconv-lite": {
"version": "0.4.15",
"resolved": "http://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz",
"integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es=",
"dev": true
}
......@@ -9252,7 +9242,7 @@
},
"chalk": {
"version": "1.1.3",
"resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"optional": true,
......@@ -9266,7 +9256,7 @@
},
"form-data": {
"version": "2.0.0",
"resolved": "http://registry.npmjs.org/form-data/-/form-data-2.0.0.tgz",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.0.0.tgz",
"integrity": "sha1-bwrrrcxdoWwT4ezBETfYX5uIOyU=",
"dev": true,
"optional": true,
......@@ -9278,7 +9268,7 @@
},
"har-validator": {
"version": "2.0.6",
"resolved": "http://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz",
"integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=",
"dev": true,
"optional": true,
......@@ -9305,7 +9295,7 @@
},
"request": {
"version": "2.75.0",
"resolved": "http://registry.npmjs.org/request/-/request-2.75.0.tgz",
"resolved": "https://registry.npmjs.org/request/-/request-2.75.0.tgz",
"integrity": "sha1-0rgmiihtoT6qXQGt9dGMyQ9lfZM=",
"dev": true,
"optional": true,
......@@ -10080,6 +10070,24 @@
"vm-browserify": "0.0.4"
}
},
"node-rsa": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/node-rsa/-/node-rsa-1.0.3.tgz",
"integrity": "sha512-gQowjnOunjmojrpO+d8x1ubL9X2Zpj4MRmY2J2hPtVF8g1VgOX1yNWUeCCoyzkRHunJf1/3orLzit5PiRtDz1A==",
"requires": {
"asn1": "^0.2.4"
},
"dependencies": {
"asn1": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
"integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
"requires": {
"safer-buffer": "~2.1.0"
}
}
}
},
"node-sass": {
"version": "4.9.0",
"resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.9.0.tgz",
......@@ -10223,7 +10231,7 @@
},
"socks": {
"version": "1.1.9",
"resolved": "http://registry.npmjs.org/socks/-/socks-1.1.9.tgz",
"resolved": "https://registry.npmjs.org/socks/-/socks-1.1.9.tgz",
"integrity": "sha1-Yo1+TQSRJDVEWsC25Fk3bLPm1pE=",
"dev": true,
"optional": true,
......@@ -11239,7 +11247,7 @@
},
"chalk": {
"version": "1.1.3",
"resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
"dev": true,
"requires": {
......@@ -11267,7 +11275,7 @@
},
"globby": {
"version": "5.0.0",
"resolved": "http://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
"resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
"integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=",
"dev": true,
"requires": {
......@@ -11281,13 +11289,13 @@
},
"minimist": {
"version": "1.2.0",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
},
"pify": {
"version": "2.3.0",
"resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
"dev": true
},
......@@ -11753,13 +11761,13 @@
},
"regjsgen": {
"version": "0.2.0",
"resolved": "http://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz",
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz",
"integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=",
"dev": true
},
"regjsparser": {
"version": "0.1.5",
"resolved": "http://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz",
"resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz",
"integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=",
"dev": true,
"requires": {
......@@ -11768,7 +11776,7 @@
"dependencies": {
"jsesc": {
"version": "0.5.0",
"resolved": "http://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
"integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
"dev": true
}
......@@ -12109,6 +12117,11 @@
"ret": "~0.1.10"
}
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"sass-graph": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz",
......@@ -12653,7 +12666,7 @@
},
"socket.io-parser": {
"version": "3.1.3",
"resolved": "http://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.1.3.tgz",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.1.3.tgz",
"integrity": "sha512-g0a2HPqLguqAczs3dMECuA1RgoGFPyvDqcbaDEdCWY9g59kdUAz3YRmaJBNKXflrHNwB7Q12Gkf/0CZXfdHR7g==",
"dev": true,
"requires": {
......@@ -14071,7 +14084,7 @@
"dependencies": {
"lru-cache": {
"version": "2.2.4",
"resolved": "http://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz",
"integrity": "sha1-bGWGGb7PFAMdDQtZSxYELOTcBj0=",
"dev": true
}
......
......@@ -13,11 +13,17 @@ import { UserModule } from './user/user.module';
import { UserService } from './user/services';
// Function used by APP_INITIALIZER before the app start: init user info / statut (expect a promise)
export function loadUser(authService: UserService) {
export function initUserService(authService: UserService) {
return (): Promise<any> => {
return new Promise((resolve, reject) => {
authService.setUserInfo();
resolve();
authService.initializeService().subscribe(
(res) => {
resolve();
},
(err) => {
resolve();
},
);
});
};
}
......@@ -39,7 +45,7 @@ export function loadUser(authService: UserService) {
providers: [
{
provide: APP_INITIALIZER,
useFactory: loadUser,
useFactory: initUserService,
deps: [UserService],
multi: true,
},
......
......@@ -257,7 +257,7 @@
<div class="modal-background" (click)="closeDeleteAccountModal()"></div>
<div class="modal-content">
<header class="modal-card-head">
<p class="modal-card-title has-text-weight-bold has-text-centered" i18n="@@userProfil.userAccountDeletionConfirmationQuestion">Are
<p class="custom-modal-card-title has-text-weight-bold" i18n="@@userProfil.userAccountDeletionConfirmationQuestion">Are
you sure you want to delete your account?</p>
</header>
<footer class="modal-card-foot has-text-right">
......
......@@ -31,7 +31,11 @@ h1 {
padding: $size-6;
}
.modal-card-title {
font-size: 1.1rem;
padding: $size-6;
}
\ No newline at end of file
.modal-card-head {
justify-content: center;
.custom-modal-card-title {
font-size: 1.1rem;
padding: $size-6;
}
}
......@@ -23,7 +23,7 @@
<span class="column-name" i18n="@@userService.validity">Validity</span>
</div>
<div class="column is-1 has-text-centered">
<span class="column-name" i18n="@@userService.actions">Actions</span>
<span class="column-name">Actions</span>
</div>
<div class="column is-1 has-text-centered">
<span class="column-name" i18n="@@userService.delete">Delete</span>
......
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { Observable } from 'rxjs';
import { IUserInfo, PasswordUpdateForm, User, ILoginResponse, LegacyAccount, IPasswordForgottenForm } from '../models';
import { environment } from '../../../environments/environment';
import { map } from 'rxjs/operators';
import * as JwtDecode from 'jwt-decode';
import { Token } from '@angular/compiler';
import * as NodeRSA from 'node-rsa';
@Injectable()
export class UserService {
private _user: User = null;
private _publicKey: string = null;
constructor(
private _http: HttpClient,
) { }
) {
}
initializeService() {
this.setUserInfo();
return this.getPublicKey().pipe(
map((publicKey) => {
this._publicKey = publicKey;
}),
);
}
// Function and helpers allowing the management of the user session (jwt), info...
setSession(authResult): boolean {
......@@ -54,7 +66,10 @@ export class UserService {
// HTTP Calls
login(loginForm): Observable<boolean> {
return this._http.post<ILoginResponse>(`${environment.serviceAuth}login/legacy`, loginForm).pipe(
// Make sure not to mofidy the object passed in parameter (object reference) when encrypting the password
const form = Object.assign({}, loginForm);
form.password = this.encrypt(form.password);
return this._http.post<ILoginResponse>(`${environment.serviceAuth}login/legacy`, form).pipe(
map(
(res) => {
return this.setSession(res);
......@@ -67,7 +82,10 @@ export class UserService {
}
createAccount(legacyAccount: LegacyAccount): Observable<boolean> {
return this._http.post<any>(`${environment.middlewareLegacyAuth}user/`, legacyAccount).pipe(
// Make sure not to mofidy the object passed in parameter (object reference) when encrypting the password
const form = Object.assign({}, legacyAccount);
form.password = this.encrypt(form.password);
return this._http.post<any>(`${environment.middlewareLegacyAuth}user/`, form).pipe(
map(
(res) => {
return true;
......@@ -92,8 +110,8 @@ export class UserService {
);
}
updateUserInfo(info: IUserInfo): Observable<{token: string}> {
return this._http.put<{token: string}>(`${environment.serviceAuth}user/update`, info).pipe(
updateUserInfo(info: IUserInfo): Observable<{ token: string }> {
return this._http.put<{ token: string }>(`${environment.serviceAuth}user/update`, info).pipe(
map(
(res) => {
return res;
......@@ -105,7 +123,11 @@ export class UserService {
);
}
updateUserPassword(form: PasswordUpdateForm): Observable<void> {
updateUserPassword(passwordUpdateform: PasswordUpdateForm): Observable<void> {
// Make sure not to mofidy the object passed in parameter (object reference) when encrypting the password
const form = Object.assign({}, passwordUpdateform);
form.newPassword = this.encrypt(form.newPassword);
form.oldPassword = this.encrypt(form.oldPassword);
return this._http.put<void>(`${environment.middlewareLegacyAuth}user/updatePassword`, form).pipe(
map(
(res) => {
......@@ -145,7 +167,9 @@ export class UserService {
}
resetPassword(token: string, password: string): Observable<void> {
return this._http.put<void>(`${environment.serviceAuth}resetPassword`, { token, password }).pipe(
// Make sure not to mofidy the object passed in parameter (object reference) when encrypting the password
const encryptedPassword = this.encrypt(password);
return this._http.put<void>(`${environment.serviceAuth}resetPassword`, { token, password: encryptedPassword }).pipe(
map(
(res) => {
return res;
......@@ -169,4 +193,27 @@ export class UserService {
),
);
}
getPublicKey(): Observable<any> {
return this._http.get<any>(`${environment.middlewareLegacyAuth}publicKey`).pipe(
map(
(res) => {
return res.publicKey;
},
(err) => {
throw err;
},
),
);
}
encrypt(unencrypted: string): string {
if (!this._publicKey) {
throw new Error('Can\'t encrypt without public key');
} else {
const key = new NodeRSA(this._publicKey);
const encrypted = key.encrypt(unencrypted, 'base64');
return encrypted;
}
}
}
......@@ -685,14 +685,14 @@
<source>Your password must at least contain <x id="INTERPOLATION"/> characters</source>
<target>Your password must at least contain <x id="INTERPOLATION"/> characters</target>
</trans-unit>
<trans-unit id="form.errors.invalidLastname" datatype="html">
<source>You must enter your last name.</source>
<target>You must enter your last name.</target>
</trans-unit>
<trans-unit id="form.errors.invalidCharacters" datatype="html">
<source>Your password contains some forbidden characters (€, ô, é, è, …).</source>
<target>Your password contains some forbidden characters (€, ô, é, è, …).</target>
</trans-unit>
<trans-unit id="form.errors.missingSpecialCharacters" datatype="html">
<source>Your password must contain at least one special character.</source>
<target>Your password must contain at least one special character.</target>
</trans-unit>
<trans-unit id="form.errors.missingUppercasedLetter" datatype="html">
<source>Your password must contain at least one uppercased character.</source>
<target>Your password must contain at least one uppercased character.</target>
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment