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

Draft: enhance certification user experience for more security #637

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
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
20 changes: 18 additions & 2 deletions www/i18n/locale-fr-FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,24 @@
"SELECT_WALLET_MODAL": {
"TITLE": "Sélection du portefeuille"
},
"CERTIFICATION_MODAL": {
"CHECKLIST_TITLE": "Vérifications avant certification",
"INFOS": "La sécurité de la monnaie Ğ1 repose sur chaque membre. Avant de certifier l'identité de cette personne, vous devez avoir fait quelques vérifications à son propos. Veuillez répondre aux questions suivantes :",
"BTN_ALL_CHECK": "Certifier",
"CHECKLIST_CONDITIONS_NOT_MET": "La certification n'a pas été envoyée. Les vérifications semblent insuffisantes. Veuillez vérifier de nouveau chaque point auprès de la personne à certifier.",
"QUESTIONS": {
"WELL_KNOWN": "<b>Connaissez-vous bien</b> la personne que vous certifiez, et connaissez-vous des gens qui la connaissent bien également ?",
"REVOCATION": "A-t-elle téléchargé son <b>document de révocation</b> et sait-elle où le retrouver ?",
"CONTACT": "Avez-vous <b>contacté</b> cette personne par plusieurs moyens et vous a-t-elle répondu ?",
"DOUBLE_IDENTITY": "La personne possède-t-elle une <b>autre identité membre</b> active ?",
"MASTER_ACCOUNT": "Maîtrise-t-elle son compte, et a-t-elle déjà <b>effectué au moins un virement</b> depuis son compte ?",
"LICENSE": "A-t-elle <b>compris la licence</b>, et accepte-t-elle de s'y conformer pour la certification d'autres membres ?",
"CREDENTIALS": "L’identifiant et le mot de passe de son compte sont-ils <b>longs et complexes</b> (phrases de passe) ? A-t-elle compris que l’identifiant doit également rester secret ? Est-elle <b>certaine de s’en souvenir</b> ou de pouvoir les retrouver ?",
"PUBLIC_KEY_DIFFERENT": "La <b>clef publique</b> indiquée ici est-elle <b>différente</b> de celle que vous a communiqué cette personne ?"
},
"REMINDER_TITLE": "Rappel",
"SHORT_LICENSE_REMINDER": "<p>Vous pouvez rappeler à la personne certifiée les paramètres des certifications :</p><p>- Chaque membre peut avoir émis 100 certifications valides au maximum.</p><p>- Les certifications sont enregistrées à un intervalle de 5 jours.</p><p>- Une nouvelle identité membre doit réunir au minimum 5 certifications en moins de deux mois.</p><p>- Un membre doit renouveler son adhésion chaque année.</p><p>- Les certifications sont valides durant deux ans.</p>"
},
"WALLET_LIST": {
"TITLE": "Mes portefeuilles",
"BTN_NEW": "Ajouter un portefeuille",
Expand Down Expand Up @@ -808,8 +826,6 @@
"POPUP_TITLE": "<b>Confirmation</b>",
"POPUP_WARNING_TITLE": "<b>Avertissement</b>",
"POPUP_SECURITY_WARNING_TITLE": "<i class=\"icon ion-alert-circled\"></i> <b>Avertissement de sécurité</b>",
"CERTIFY_RULES_TITLE_UID": "Certifier {{uid}}",
"CERTIFY_RULES": "<b class=\"assertive\">Ne PAS certifier</b> un compte si vous pensez que :<br/><br/><ul><li>1.) il ne correspond pas à une personne <b>physique et vivante</b>.<li>2.) son propriétaire <b>possède un autre compte</b> déjà certifié.<li>3.) son propriétaire viole (volontairement ou non) la règle 1 ou 2 (par exemple en certifiant des comptes factices ou en double).</ul><br/><b>Êtes-vous sûr</b> de vouloir néanmoins certifier cette identité ?",
"FULLSCREEN": "Afficher l'application en plein écran ?",
"EXIT_APP": "Fermer l'application ?",
"TRANSFER": "<b>Récapitulatif du virement</b> :<br/><br/><ul><li> - De : {{from}}</li><li> - A : <b>{{to}}</b></li><li> - Montant : <b>{{amount}} {{unit}}</b></li><li> - Commentaire : <i>{{comment}}</i></li></ul><br/><b>Êtes-vous sûr de vouloir effectuer ce virement ?</b>",
Expand Down
155 changes: 135 additions & 20 deletions www/js/controllers/wot-controllers.js
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ angular.module('cesium.wot.controllers', ['cesium.services'])

.controller('WotCertificationsViewCtrl', WotCertificationsViewController)

.controller('WotCertificationChecklistCtrl', WotCertificationChecklistController)

.controller('WotSelectPubkeyIdentityModalCtrl', WotSelectPubkeyIdentityModalController)

;
Expand Down Expand Up @@ -783,15 +785,20 @@ function WotIdentityAbstractController($scope, $rootScope, $state, $translate, $
return;
}

UIUtils.alert.confirm('CONFIRM.CERTIFY_RULES', 'CONFIRM.POPUP_SECURITY_WARNING_TITLE', {
cssClass: 'warning',
okText: 'WOT.BTN_YES_CERTIFY',
okType: 'button-assertive'
})
.then(function(confirm){
if (!confirm) {
return;
// Certification checklist before confirmation
let answers_are_right = $q.defer();
answers_are_right.promise.then(function(){
UIUtils.alert.confirm(
'ACCOUNT.CERTIFICATION_MODAL.SHORT_LICENSE_REMINDER',
'ACCOUNT.CERTIFICATION_MODAL.REMINDER_TITLE',
{
cssClass: 'positive',
okText: 'COMMON.BTN_OK',
okType: 'button-positive'
}
)
.then(function(confirm){
if (! confirm) {return}
UIUtils.loading.show();
wallet.certify($scope.formData.uid,
$scope.formData.pubkey,
Expand All @@ -811,7 +818,18 @@ function WotIdentityAbstractController($scope, $rootScope, $state, $translate, $
}
})
.catch(UIUtils.onError('ERROR.SEND_CERTIFICATION_FAILED'));
});
})
})
.catch(
UIUtils.onError('ACCOUNT.CERTIFICATION_MODAL.CHECKLIST_CONDITIONS_NOT_MET')
);

// display certification checklist modal
return Modals.showCertificationCheckList({
answers_are_right: answers_are_right,
identity: $scope.formData,
});

})
.catch(function(err) {
if (err === 'CANCELLED') return;
Expand Down Expand Up @@ -889,17 +907,21 @@ function WotIdentityAbstractController($scope, $rootScope, $state, $translate, $
return;
}

// Ask confirmation
$translate('CONFIRM.CERTIFY_RULES_TITLE_UID', {uid: identity.uid})
.then(function (confirmTitle) {
return UIUtils.alert.confirm('CONFIRM.CERTIFY_RULES', confirmTitle);
})
.then(function (confirm) {
if (!confirm) {
return;
// Certification checklist before confirmation
let answers_are_right = $q.defer();
answers_are_right.promise.then(function(){
UIUtils.alert.confirm(
'ACCOUNT.CERTIFICATION_MODAL.SHORT_LICENSE_REMINDER',
'ACCOUNT.CERTIFICATION_MODAL.REMINDER_TITLE',
{
cssClass: 'positive',
okText: 'COMMON.BTN_OK',
okType: 'button-positive'
}
)
.then(function(confirm){
if (! confirm) {return}
UIUtils.loading.show();

// Send certification
wallet.certify(identity.uid,
identity.pubkey,
Expand All @@ -917,8 +939,19 @@ function WotIdentityAbstractController($scope, $rootScope, $state, $translate, $
$scope.doMotion();
});
})
.catch(UIUtils.onError('ERROR.SEND_CERTIFICATION_FAILED'));
});
.catch(UIUtils.onError('ERROR.SEND_CERTIFICATION_FAILED'));
})
})
.catch(
UIUtils.onError('ACCOUNT.CERTIFICATION_MODAL.CHECKLIST_CONDITIONS_NOT_MET')
);

// Display cert checklist modal
return Modals.showCertificationCheckList({
answers_are_right: answers_are_right,
identity: identity,
});

})
.catch(function (err) {
if (err === 'CANCELLED') return;
Expand Down Expand Up @@ -1429,6 +1462,88 @@ function WotCertificationsViewController($scope, $rootScope, $controller, csSett
};
}

/**
* Certification checklist controller
* @param $controller
*/
function WotCertificationChecklistController($scope, $controller, parameters){

// allow to display license
$controller('CurrencyViewCtrl', {$scope: $scope});

let answers_are_right = parameters.answers_are_right;
$scope.identity = parameters.identity;

$scope.prepare_cert_checklist = function() {
const original_cert_checklist = [
{
question: 'ACCOUNT.CERTIFICATION_MODAL.QUESTIONS.WELL_KNOWN',
expected_answer: true,
answer: false
},
{
question: 'ACCOUNT.CERTIFICATION_MODAL.QUESTIONS.REVOCATION',
expected_answer: true,
answer: false
},
{
question: 'ACCOUNT.CERTIFICATION_MODAL.QUESTIONS.CONTACT',
expected_answer: true,
answer: false
},
{
question: 'ACCOUNT.CERTIFICATION_MODAL.QUESTIONS.MASTER_ACCOUNT',
expected_answer: true,
answer: false
},
{
question: 'ACCOUNT.CERTIFICATION_MODAL.QUESTIONS.LICENSE',
expected_answer: true,
answer: false
},
{
question: 'ACCOUNT.CERTIFICATION_MODAL.QUESTIONS.CREDENTIALS',
expected_answer: true,
answer: false
},
// questions with negative answers
{
question: 'ACCOUNT.CERTIFICATION_MODAL.QUESTIONS.DOUBLE_IDENTITY',
expected_answer: false,
answer: false
},
{
question: 'ACCOUNT.CERTIFICATION_MODAL.QUESTIONS.PUBLIC_KEY_DIFFERENT',
expected_answer: false,
answer: false
},
];

// Fisher-Yates shuffle
function shuffle(array) {
for (let i = array.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1)); // random index from 0 to i
let t = array[i]; array[i] = array[j]; array[j] = t
}
return array;
}

return shuffle(original_cert_checklist).slice(0, 5);
}
$scope.cert_checklist = $scope.prepare_cert_checklist();

$scope.verifyAnswers = function() {
$scope.cert_checklist.map( question => {
if (question.answer !== question.expected_answer) {
// TODO message should be changed.
answers_are_right.reject();
}
});
answers_are_right.resolve(true);

$scope.closeModal();
}
}

/**
* Select identities from a pubkey (useful when many self on the same pubkey)
Expand Down
5 changes: 5 additions & 0 deletions www/js/services/modal-services.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ angular.module('cesium.modal.services', ['cesium.utils.services'])
parameters);
}

function showCertificationCheckList(parameters) {
return ModalUtils.show('templates/wot/modal_certification_checklist.html','WotCertificationChecklistCtrl', parameters);
}

function showSelectPubkeyIdentity(parameters) {
return ModalUtils.show('templates/wot/modal_select_pubkey_identity.html', 'WotSelectPubkeyIdentityModalCtrl',
parameters);
Expand Down Expand Up @@ -294,6 +298,7 @@ angular.module('cesium.modal.services', ['cesium.utils.services'])
showHelp: showHelp,
showAccountSecurity: showAccountSecurity,
showLicense: showLicense,
showCertificationCheckList: showCertificationCheckList,
showSelectPubkeyIdentity: showSelectPubkeyIdentity,
showSelectWallet: showSelectWallet,
showPassword: showPassword
Expand Down
17 changes: 17 additions & 0 deletions www/templates/wot/item_checklist_certification.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<div class="col col-80">
<div class="ion-text text-wrap" ng-bind-html="check.question | translate"></div>
</div>
<div class="col text-right">
<div class="ion-text" style="text-transform: uppercase;">
<b ng-if="check.answer" translate>COMMON.BTN_YES</b>
<b ng-if="!check.answer" translate>COMMON.BTN_NO</b>
</div>
</div>
<div class="col float-left">
<label class="toggle toggle-royal">
<input type="checkbox" ng-model="check.answer">
<div class="track">
<div class="handle"></div>
</div>
</label>
</div>
80 changes: 80 additions & 0 deletions www/templates/wot/modal_certification_checklist.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<ion-modal-view class="modal-full-height">

<!-- TOP BAR -->
<ion-header-bar class="bar-positive">

<button class="button button-clear visible-xs"
ng-if="!slides.slider.activeIndex"
ng-click="closeModal()" translate>COMMON.BTN_CANCEL
</button>
<button class="button button-icon button-clear icon ion-ios-arrow-back buttons header-item"
ng-click="doPrev()"
ng-if="slides.slider.activeIndex && slideBehavior.hasPreviousButton">
</button>
<button class="button button-icon button-clear icon ion-ios-help-outline visible-xs"
ng-if="slideBehavior.helpAnchor"
ng-click="showHelpModal(slideBehavior.helpAnchor)">
</button>

<h1 class="title" translate>ACCOUNT.CERTIFICATION_MODAL.CHECKLIST_TITLE</h1>

<!-- next -->
<button class="button button-clear icon-right visible-xs"
ng-if="slideBehavior.hasNextButton"
ng-click="doNext()">
<span translate>COMMON.BTN_NEXT</span>
<i class="icon ion-ios-arrow-right"></i>
</button>
<!-- accept -->
<button class="button button-clear icon-right visible-xs"
ng-class="{'button-text-stable': !isLicenseRead}"
ng-if="slideBehavior.hasAcceptButton"
ng-click="isLicenseRead ? doNext() : undefined">
<span translate>ACCOUNT.NEW.BTN_ACCEPT</span>
<i class="icon ion-ios-arrow-right"></i>
</button>
<!-- send -->
<button class="button button-clear icon-right visible-xs"
ng-if="slideBehavior.hasSendButton"
ng-click="doNewAccount()">
<i class="icon ion-android-send"></i>
</button>
</ion-header-bar>


<!-- CONTENT -->
<ion-content class="has-header" scroll="true">

<div class="padding text-center" ><b>{{ identity.uid }} - {{ identity.pubkey }}</b></div>

<div class="padding" translate>ACCOUNT.CERTIFICATION_MODAL.INFOS</div>

<div class="card padding">
<div class="row nowrap row-center"
ng-repeat="check in cert_checklist"
ng-include="::'templates/wot/item_checklist_certification.html'">
</div>
</div>


<div class="padding text-right">

<button class="button button-clear button-dark ink hidden-xs" ng-click="closeModal()" type="button" translate>
COMMON.BTN_CANCEL
</button>

<button class="button button-stable icon-right ink"
ng-if="formData.licenseUrl"
ng-click="showLicenseModal()">
<i class="icon ion-document-text"></i>&nbsp;
{{'CURRENCY.VIEW.BTN_SHOW_LICENSE' | translate}}
</button>

<button class="button button-positive icon-right ion-chevron-right ink" ng-click="verifyAnswers()"
type="button" translate>
ACCOUNT.CERTIFICATION_MODAL.BTN_ALL_CHECK
</button>

</div>
</ion-content>
</ion-modal-view>