Skip to content
Merged
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline

## next-release (YYYY-MM-DD)

### Client
#### Bug fixes
* **(statistics)** : suppression de l'utilisation du DeviceId pour le suivi des statistiques d'usage comme préconisé dans le RGPD. Utilisation d'une UID générée automatiquement par le client à la place.

### Backend
#### Bug fixes
* **(maps)** : suppression du tracking du fichier map-data.json et ajout d'un fichier .dist avec des POI factices
* **(statistics)** : prise en compte de la nouvelle uid de tracking à la place du Device Id pour la génération des statistiques d'usage

## 1.1.0 (2024-09-13)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
*/

export interface StatisticsUserActionDto {
uid: string | null;
uid: string;
userAgent: string;
xForwardedFor: string;
duid: string;
Expand All @@ -49,8 +49,8 @@ export interface StatisticsUserActionDto {
}

export interface StatisticsExternalApiUserActionDto {
uid: string | null;
duid: string | null;
uid: string;
duid: string;
action: string;
service: string;
platform: string | null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class StatisticsService {

const requestData: StatisticsExternalApiUserActionDto = {
uid: statData.uid,
duid: statData.platform != 'web' ? statData.duid : null,
duid: statData.duid && statData.duid !== '' ? statData.duid : 'unknown',
action: mappedAction,
service: statData.functionality,
platform: statData.platform,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright ou © ou Copr. Université de Lorraine, (2022)
*
* Direction du Numérique de l'Université de Lorraine - SIED
* (dn-mobile-dev@univ-lorraine.fr)
* JNESIS (contact@jnesis.com)
*
* Ce logiciel est un programme informatique servant à rendre accessible
* sur mobile divers services universitaires aux étudiants et aux personnels
* de l'université.
*
* Ce logiciel est régi par la licence CeCILL 2.1, soumise au droit français
* et respectant les principes de diffusion des logiciels libres. Vous pouvez
* utiliser, modifier et/ou redistribuer ce programme sous les conditions
* de la licence CeCILL telle que diffusée par le CEA, le CNRS et INRIA
* sur le site "http://cecill.info".
*
* En contrepartie de l'accessibilité au code source et des droits de copie,
* de modification et de redistribution accordés par cette licence, il n'est
* offert aux utilisateurs qu'une garantie limitée. Pour les mêmes raisons,
* seule une responsabilité restreinte pèse sur l'auteur du programme, le
* titulaire des droits patrimoniaux et les concédants successifs.
*
* À cet égard, l'attention de l'utilisateur est attirée sur les risques
* associés au chargement, à l'utilisation, à la modification et/ou au
* développement et à la reproduction du logiciel par l'utilisateur étant
* donné sa spécificité de logiciel libre, qui peut le rendre complexe à
* manipuler et qui le réserve donc à des développeurs et des professionnels
* avertis possédant des connaissances informatiques approfondies. Les
* utilisateurs sont donc invités à charger et à tester l'adéquation du
* logiciel à leurs besoins dans des conditions permettant d'assurer la
* sécurité de leurs systèmes et/ou de leurs données et, plus généralement,
* à l'utiliser et à l'exploiter dans les mêmes conditions de sécurité.
*
* Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
* pris connaissance de la licence CeCILL 2.1, et que vous en avez accepté les
* termes.
*/

import { createStore, select, withProps } from '@ngneat/elf';
import {
persistState,
localStorageStrategy
} from '@ngneat/elf-persist-state';

const STORE_NAME = 'stats-uid';

interface StatsUidProps {
uid: string;
}

const statsUidStore = createStore(
{ name: STORE_NAME },
withProps<StatsUidProps>({ uid: null })
);

export const persistStatsUid = persistState(statsUidStore, {
key: STORE_NAME,
storage: localStorageStrategy,
});

export const statsUid$ = statsUidStore.pipe(select((state) => state.uid));

export const updateStatsUid = (uid: StatsUidProps['uid']) => {
statsUidStore.update((state) => ({
...state,
uid,
}));
};

export const clearStatsUid = () => statsUidStore.reset();
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@

import { HttpClient } from '@angular/common/http';
import { Inject, Injectable } from '@angular/core';
import { Device } from '@capacitor/device';
import { combineLatest, from, Observable, of } from 'rxjs';
import { catchError, switchMap, take } from 'rxjs/operators';
import { getAuthToken } from '../auth/auth.repository';
import { NetworkService } from '../network/network.service';
import { Capacitor } from '@capacitor/core';
import { statsUid$, updateStatsUid } from './statistics.repository';

interface UserActionRequestData {
authToken: string;
Expand Down Expand Up @@ -91,13 +91,13 @@ export class StatisticsService {
private postUserActionStatistic(userActionDetails: UserActionDetails): Observable<void> {
const url = `${this.environment.apiEndpoint}/statistics/user-action`;

return combineLatest([getAuthToken(), from(Device.getId()), from(this.networkService.getConnectionStatus())]).pipe(
return combineLatest([getAuthToken(), statsUid$, from(this.networkService.getConnectionStatus())]).pipe(
take(1),
switchMap(([authToken, deviceId, connectionStatus]) => {
switchMap(([authToken, statsUid, connectionStatus]) => {
const data: UserActionRequestData = {
authToken,
data: {
duid: deviceId.identifier,
duid: statsUid,
action: userActionDetails.action,
functionality: userActionDetails.functionality,
platform: Capacitor.getPlatform(),
Expand All @@ -110,4 +110,24 @@ export class StatisticsService {
catchError(() => of(null))
);
}

// Generate a unique id for stats usage and store it in Local Storage
public async checkAndGenerateStatsUid() {
statsUid$.pipe(take(1)).subscribe((uid) => {
if (!uid) {
updateStatsUid(this.uuid4());
}
});
}

private uuid4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
/[xy]/g,
function (c) {
const r = (Math.random() * 16) | 0,
v = c === 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
},
);
}
}
7 changes: 5 additions & 2 deletions dev/user-frontend-ionic/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ import { ModalController, Platform, PopoverController } from '@ionic/angular';
import { TranslateService } from '@ngx-translate/core';
import {
currentLanguage$, features$, FeaturesService, isDarkTheme$, isFeatureStoreInitialized$, NavigationService,
NotificationsService, NetworkService, PageLayout, PageLayoutService, setIsDarkTheme, themeRepoInitialized$,
userHadSetThemeInApp, userHadSetThemeInApp$
NotificationsService, NetworkService, PageLayout, PageLayoutService, setIsDarkTheme, StatisticsService,
themeRepoInitialized$, userHadSetThemeInApp, userHadSetThemeInApp$
} from '@multi/shared';
import { initializeApp } from 'firebase/app';
import { combineLatest, Observable, of, Subscription } from 'rxjs';
Expand Down Expand Up @@ -88,6 +88,7 @@ export class AppComponent implements OnInit, OnDestroy {
private networkService: NetworkService,
private featuresService: FeaturesService,
private notificationsService: NotificationsService,
private statisticsService: StatisticsService,
private titleService: Title
) {
currentLanguage$.subscribe((language) => {
Expand Down Expand Up @@ -143,6 +144,8 @@ export class AppComponent implements OnInit, OnDestroy {

StatusBar.setStyle({ style: Style.Dark });
});

this.statisticsService.checkAndGenerateStatsUid();
}

ngOnInit() {
Expand Down