Skip to content

Commit

Permalink
Merge pull request #275 from HEMPED/main
Browse files Browse the repository at this point in the history
test hem's version
  • Loading branch information
mensch72 authored Dec 28, 2024
2 parents 0ea6422 + d892ba4 commit 003a105
Show file tree
Hide file tree
Showing 15 changed files with 877 additions and 555 deletions.
6 changes: 3 additions & 3 deletions couchdb/vodle/_design/vodle/validate_doc_update.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ function (newDoc, savedDoc, userCtx) {
}
*/
} else {
// if doc already exists, let noone update or delete it:
if (savedDoc) {
throw ({forbidden: 'Noone may update or delete existing poll documents.'})
// if doc already exists, let noone update or delete it, unless its delegation map:
if (savedDoc && !_id.endsWith("inverse_indirect_map") && !_id.endsWith("direct_delegation_map")) {
throw ({forbidden: 'Noone may update or delete existing poll documents.'});
}
// let only the voters create it:
let doc_pid = _id.substring(pollprefix.length, _id.indexOf("§"));
Expand Down
93 changes: 72 additions & 21 deletions src/app/data.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,12 +214,12 @@ function myhash(what): string {

export type del_option_spec_t = {type: "+" | "-", oids: Array<string>};
export type del_request_t = {option_spec: del_option_spec_t, public_key: string};
export type del_response_t = {option_spec: del_option_spec_t};
export type del_response_t = {option_spec: del_option_spec_t, status: "agreed" | "declined" | "revoked", "decline_cycle", "decline_self"};
export type del_signed_response_t = string;
export type del_agreement_t = { // by pid, did
client_vid?: string,
delegate_vid?: string,
status?: "pending" | "agreed" | "declined" | "revoked",
status?: "pending" | "agreed" | "declined" | "revoked" | "declined_cycle" | "declined_self",
accepted_oids?: Set<string>, // oids accepted for delegation by delegate
active_oids?: Set<string> // among those, oids currently activated for delegation by client
};
Expand Down Expand Up @@ -312,13 +312,6 @@ export class DataService implements OnDestroy {

delegation_agreements_caches: Record<string, Map<string, del_agreement_t>>; // by pid, did

direct_delegation_map_caches: Record<string, Map<string, Map<string, string>>>; // redundant storage of direct delegation data, not stored in database
inv_direct_delegation_map_caches: Record<string, Map<string, Map<string, Set<string>>>>; // redundant storage of inverse direct delegation data, not stored in database
indirect_delegation_map_caches: Record<string, Map<string, Map<string, Set<string>>>>; // redundant storage of indirect delegation data, not stored in database
inv_indirect_delegation_map_caches: Record<string, Map<string, Map<string, Set<string>>>>; // redundant storage of inverse indirect delegation data, not stored in database
effective_delegation_map_caches: Record<string, Map<string, Map<string, string>>>; // redundant storage of effective delegation data, not stored in database
inv_effective_delegation_map_caches: Record<string, Map<string, Map<string, Set<string>>>>; // redundant storage of inverse effective delegation data, not stored in database

tally_caches: Record<string, {}>; // temporary storage of tally data, not stored in database

news_keys: Set<string>;
Expand Down Expand Up @@ -436,12 +429,6 @@ export class DataService implements OnDestroy {
this.outgoing_dids_caches = {};
this.incoming_dids_caches = {};
this.delegation_agreements_caches = {};
this.direct_delegation_map_caches = {};
this.inv_direct_delegation_map_caches = {};
this.indirect_delegation_map_caches = {};
this.inv_indirect_delegation_map_caches = {};
this.effective_delegation_map_caches = {};
this.inv_effective_delegation_map_caches = {};
this.proxy_ratings_map_caches = {};
this.max_proxy_ratings_map_caches = {};
this.argmax_proxy_ratings_map_caches = {};
Expand Down Expand Up @@ -1522,7 +1509,9 @@ export class DataService implements OnDestroy {
} else {
this.G.L.error("DataService.setp change option attempted for existing entry", pid, key, value);
}
} else {
} else if (key == 'inverse_indirect_map') {
return this._setp_in_polldb(pid, key, value);
}else {
this.G.L.error("DataService.setp non-local attempted for non-draft poll", pid, key, value);
}
}
Expand Down Expand Up @@ -1568,7 +1557,7 @@ export class DataService implements OnDestroy {
// other polls' data is stored in poll's own database.
// construct key for poll db:
const pkey = this.get_voter_key_prefix(pid, vid) + key;
// this.G.L.trace("getv", pid, key, vid, pkey)
// this.G.L.trace("getv", pid, key, vid, pkey)
this.ensure_poll_cache(pid);
value = this.poll_caches[pid][pkey] || '';
}
Expand All @@ -1591,8 +1580,51 @@ export class DataService implements OnDestroy {
}
}

delv(pid: string, key: string) {
// delete a voter data item
// inverse_indirect_map:- key: voterid, value: [voterid1, voterid2, voterid3] voterids that have effectively delegated to the voterid
set_inverse_indirect_map(pid: string, val: Map<string, string>) {
const mapKey = `poll.${pid}.inverse_indirect_map`;
this._setp_in_polldb(pid, mapKey, JSON.stringify(Array.from(val.entries())));
}

set_effective_delegation(pid: string, vid: string, val: string[]) {
const mapKey = `poll.${pid}.inverse_indirect_map`;
const currentMap = this.get_inverse_indirect_map(pid);

currentMap.set(vid, JSON.stringify(val)); // Add or update the key-value pair
this._setp_in_polldb(pid, mapKey, JSON.stringify(Array.from(currentMap.entries())));
}

get_inverse_indirect_map(pid: string): Map<string, string> {
const cache = this.poll_caches[pid]['poll.' + pid + '.inverse_indirect_map'] || '[]';
const ps = cache ? JSON.parse(cache) : {};
const mp = new Map<string, string>(ps);
return mp;
}

// direct_delegation_map:- key: voterid, value: [[voterid, rank, is_current_delegation], [voterid2, rank, is_current_delegate]] of the voter that the key has delegated to.
// When ranked delegation is not allowed, the rank is always 0 and the size of the value is 1.
set_direct_delegation_map(pid: string, val: Map<string, Array<[string, string, string]>>) {
const mapKey = `poll.${pid}.direct_delegation_map`;
this._setp_in_polldb(pid, mapKey, JSON.stringify(Array.from(val.entries())));
}

get_direct_delegation_map(pid: string): Map<string, Array<[string, string, string]>> {
const cache = this.poll_caches[pid]['poll.' + pid + '.direct_delegation_map'] || '[]';
const ps = cache ? JSON.parse(cache) : {};
const mp = new Map<string, Array<[string, string, string]>>(ps);
return mp;
}

clear_direct_delegation_map(pid: string) {
const mapKey = `poll.${pid}.direct_delegation_map`;
this._setp_in_polldb(pid, mapKey, JSON.stringify([]));
}

delv(pid: string, key: string, vid?: string) {
if(!this.getv(pid, key)) {
this.G.L.warn("DataService.delv nothing to delete", pid, key);
return;
}
if (this.pid_is_draft(pid)) {
const ukey = get_poll_key_prefix(pid) + this.get_voter_key_prefix(pid) + key;
delete this.user_cache[ukey];
Expand All @@ -1605,6 +1637,10 @@ export class DataService implements OnDestroy {
}
}

get_ranked_delegation_allowed(pid: string): boolean {
return this.poll_caches[pid]['allow_ranked'] == 'true';
}

// TODO: delv!

get_example_docs(): Promise<any> {
Expand Down Expand Up @@ -1654,6 +1690,18 @@ export class DataService implements OnDestroy {
return this.store_user_data(ukey, this.user_cache, ukey);
}

setv_global_for_poll(pid: string, key: string, value: string): boolean {
// set global voter data item in poll db:
value = value || '';
// construct key for poll db:
// return 'voter.' + (vid ? vid : this.getp(pid, 'myvid')) + "§";
const pkey = "§";
this.ensure_poll_cache(pid);
this.G.L.trace("DataService.setv_global_for_poll", pid, key, value);
this.poll_caches[pid][pkey] = value;
return this.store_poll_data(pid, pkey, this.poll_caches[pid], pkey, false);
}

setv_in_polldb(pid: string, key: string, value: string, vid?: string): boolean {
/** Set voter data item in poll db.
* If necessary, mark the database entry with poll's due date
Expand Down Expand Up @@ -1770,6 +1818,9 @@ export class DataService implements OnDestroy {
this.page.onDataChange();
}
}
if (change.docs.some((doc) => doc._id.endsWith('shared_set'))) {
console.log('shared_set has been updated', change.docs);
}
this.G.L.exit("DataService.handle_poll_db_change", pid, this.pending_changes);
}

Expand Down Expand Up @@ -2317,7 +2368,7 @@ export class DataService implements OnDestroy {
// key existed in poll db, check whether update is allowed.
const value = dict[dict_key];
const enc_value = encrypt(value, poll_pw);
if ((key != 'due') && (key != 'state') && (decrypt(doc.value, poll_pw) != value)) {
if ((key != 'due') && (key != 'state') && (decrypt(doc.value, poll_pw) != value) && (key.indexOf("inverse_indirect_map") == -1) && (key.indexOf("direct_delegation_map") == -1)) {
// this is not allowed for poll docs!
this.G.L.error("DataService.store_poll_data tried changing an existing poll data item", pid, key, value);
} else if ((key == 'due') && (doc.due != value)) {
Expand Down Expand Up @@ -2379,7 +2430,7 @@ export class DataService implements OnDestroy {
// check which voter's data this is:
const vid_prefix = key.slice(0, key.indexOf("§")),
vid = this.user_cache[get_poll_key_prefix(pid) + 'myvid'];
if (vid_prefix != 'voter.' + vid && this.poll_caches[pid]['is_test']!='true') {
if (vid_prefix != 'voter.' + vid && this.poll_caches[pid]['is_test']!='true' && key.indexOf("inverse_indirect_map") == -1) {
// it is not allowed to alter other voters' data!
this.G.L.error("DataService.store_poll_data tried changing another voter's data item", pid, key);

Expand Down
14 changes: 14 additions & 0 deletions src/app/delegation-dialog/delegation-dialog.page.html
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,20 @@ <h1 [innerHtml]="'delegation-request.header'|translate"></h1>
</ion-input>
</small>
</ion-item>

<ng-container *ngIf="this.G.D.get_ranked_delegation_allowed(this.parent.pid)">
<ion-item>
<small>
<ion-label position="floating" color="primary" [innerHtml]="'delegation-request.rank-label'|translate"></ion-label>
<ion-select aria-label="Rank" interface="popover" [placeholder]="rank"
(ionChange)="rank_changed($event)"
>
<ion-select-option *ngFor="let i of rank_options" [value]="i">{{i}}</ion-select-option>
</ion-select>
</small>
</ion-item>
</ng-container>

<ng-container *ngIf="can_share">
<ion-item lines="none">
<ion-col class="ion-no-padding ion-no-margin">
Expand Down
37 changes: 35 additions & 2 deletions src/app/delegation-dialog/delegation-dialog.page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ along with vodle. If not, see <https://www.gnu.org/licenses/>.
*/

import { Component, OnInit, Input, ViewChild } from '@angular/core';
import { Validators, UntypedFormBuilder, UntypedFormGroup, UntypedFormControl, ValidationErrors, AbstractControl } from '@angular/forms';
import { Validators, UntypedFormBuilder, UntypedFormGroup, UntypedFormControl, ValidationErrors, AbstractControl, Form } from '@angular/forms';
import { IonInput, PopoverController } from '@ionic/angular';
import { TranslateService } from '@ngx-translate/core';

Expand Down Expand Up @@ -59,6 +59,8 @@ export class DelegationDialogPage implements OnInit {
message_title: string;
message_body: string;
mailto_url: string;
rank: number;
rank_options: number[];

constructor(
private popover: PopoverController,
Expand All @@ -75,8 +77,14 @@ export class DelegationDialogPage implements OnInit {
this.can_share = Capacitor.isNativePlatform() || this.can_use_web_share;
this.formGroup = this.formBuilder.group({
delegate_nickname: new UntypedFormControl('', Validators.required),
from: new UntypedFormControl(this.G.S.email)
from: new UntypedFormControl(this.G.S.email),
});

// checks if ranked delegation is allowed and if so, initialises the values needed for the drop-down menu
if (this.G.D.get_ranked_delegation_allowed(this.parent.pid)) {
this.initialise_rank_values();
}

// TODO: what if already some delegation active or pending?
// prepare a new delegation:
[this.p, this.did, this.request, this.private_key, this.agreement] = this.G.Del.prepare_delegation(this.parent.pid);
Expand All @@ -92,6 +100,24 @@ export class DelegationDialogPage implements OnInit {
setTimeout(() => this.focus_element.setFocus(), 100);
}

initialise_rank_values() {
const uid = this.parent.p.myvid;
const dir_del_map = this.G.D.get_direct_delegation_map(this.parent.pid);
const dir_del = dir_del_map.get(uid) || [];
var ranks = Array.from({ length: environment.delegation.max_delegations }, (_, i) => i + 1);
for (const entry of dir_del) {
if (entry === undefined) {
continue;
}
const indexToRemove: number = ranks.indexOf(Number(entry[1]));
if (indexToRemove !== -1) {
ranks.splice(indexToRemove, 1);
}
}
this.rank = ranks[0];
this.rank_options = ranks;
}

delegate_nickname_changed() {
const delegate_nickname = this.formGroup.get('delegate_nickname').value;
this.G.D.setp(this.p.pid, "del_nickname." + this.did, delegate_nickname);
Expand All @@ -105,6 +131,10 @@ export class DelegationDialogPage implements OnInit {
this.update_request();
}

rank_changed(e) {
this.rank = e.detail.value;
}

update_request() {
this.G.L.entry("DelegationDialogPage.update_request");
this.mailto_url = "mailto:" + encodeURIComponent(this.formGroup.get('delegate_nickname').value) + "?subject=" + encodeURIComponent(this.message_title) + "&body=" + encodeURIComponent(this.message_body);
Expand Down Expand Up @@ -147,6 +177,7 @@ export class DelegationDialogPage implements OnInit {
}).then(res => {
this.G.L.info("DelegationDialogPage.share_button_clicked succeeded", res);
this.G.Del.after_request_was_sent(this.parent.pid, this.did, this.request, this.private_key, this.agreement);
this.G.Del.set_delegate_rank(this.parent.pid, this.did, this.rank);
this.popover.dismiss();
}).catch(err => {
this.G.L.error("DelegationDialogPage.share_button_clicked failed", err);
Expand All @@ -159,6 +190,7 @@ export class DelegationDialogPage implements OnInit {
this.from_changed();
window.navigator.clipboard.writeText(this.delegation_link);
this.G.Del.after_request_was_sent(this.parent.pid, this.did, this.request, this.private_key, this.agreement);
this.G.Del.set_delegate_rank(this.parent.pid, this.did, this.rank);
LocalNotifications.schedule({
notifications: [{
title: this.translate.instant("delegation-request.notification-copied-link-title"),
Expand All @@ -182,6 +214,7 @@ export class DelegationDialogPage implements OnInit {
this.delegate_nickname_changed();
this.from_changed();
this.G.Del.after_request_was_sent(this.parent.pid, this.did, this.request, this.private_key, this.agreement);
this.G.Del.set_delegate_rank(this.parent.pid, this.did, this.rank);
this.parent.update_delegation_info();
this.popover.dismiss();
this.G.L.exit("DelegationDialogPage.email_button_clicked");
Expand Down
Loading

0 comments on commit 003a105

Please sign in to comment.