// (C) Copyright 2015 Moodle Pty Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import { Injectable } from '@angular/core'; import { CoreWSError } from '@classes/errors/wserror'; import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; import { CoreUserSummary } from '@features/user/services/user'; import { CoreSites, CoreSitesCommonWSOptions } from '@services/sites'; import { CoreWSExternalWarning } from '@services/ws'; import { makeSingleton } from '@singletons'; /** * Service to handle data privacy. */ @Injectable({ providedIn: 'root' }) export class CoreDataPrivacyService { static readonly ROOT_CACHE_KEY = 'CoreDataPrivacy:'; /** * Check if data privacy is enabled on current site. * * @returns Whether data privacy is enabled. */ async isEnabled(): Promise { const site = CoreSites.getCurrentSite(); // Check if the privacy data WS are available in the site. if (!site?.wsAvailable('tool_dataprivacy_get_data_requests')) { return false; } // If the user can contact the DPO, then data privacy is enabled. const accessInformation = await this.getAccessInformation(); return accessInformation.cancontactdpo; } /** * Get cache key for data privacy access information WS calls. * * @returns Cache key. */ protected getAccessInformationCacheKey(): string { return CoreDataPrivacyService.ROOT_CACHE_KEY + 'accessInformation'; } /** * Retrieving privacy API access (permissions) information for the current user. * * @param options Request options. * @returns Promise resolved with object with access information. * @since 4.4 */ async getAccessInformation( options: CoreSitesCommonWSOptions = {}, ): Promise { const site = await CoreSites.getSite(options.siteId); const preSets: CoreSiteWSPreSets = { cacheKey: this.getAccessInformationCacheKey(), ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; return site.read('tool_dataprivacy_get_access_information', undefined, preSets); } /** * Invalidates access information. * * @param siteId Site ID. If not defined, current site. * @returns Promise resolved when the data is invalidated. */ protected async invalidateAccessInformation(siteId?: string): Promise { const site = await CoreSites.getSite(siteId); await site.invalidateWsCacheForKey(this.getAccessInformationCacheKey()); } /** * Contact the site Data Protection Officer(s). * * @param message Message to send to the DPO. * @param siteId Site ID. If not defined, current site. * @returns Promise resolved with boolean: whether the message was sent. * @since 4.4 */ async contactDPO(message: string, siteId?: string): Promise { const site = await CoreSites.getSite(siteId); const params: CoreDataPrivacyContactDPOWSParams = { message }; const response = await site.write('tool_dataprivacy_contact_dpo', params); if (response.warnings && response.warnings.length) { throw new CoreWSError(response.warnings[0]); } return response.result; } /** * Get cache key for data requests WS calls. * * @returns Cache key. */ protected getDataRequestsCacheKey(): string { return CoreDataPrivacyService.ROOT_CACHE_KEY + 'datarequests'; } /** * Fetch the details of a user's data request. * * @param options Request options. * @returns Promise resolved with the data requests. * @since 4.4 */ async getDataRequests( options: CoreSitesCommonWSOptions = {}, ): Promise { const site = await CoreSites.getSite(options.siteId); const preSets: CoreSiteWSPreSets = { cacheKey: this.getDataRequestsCacheKey(), ...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets. }; const params: CoreDataPrivacyGetDataRequestsWSParams = { userid: site.getUserId(), }; const response = await site.read('tool_dataprivacy_get_data_requests', params, preSets); return response.requests; } /** * Invalidate data requests. * * @param siteId Site ID. If not defined, current site. * @returns Promise resolved when the data is invalidated. */ async invalidateDataRequests(siteId?: string): Promise { const site = await CoreSites.getSite(siteId); await site.invalidateWsCacheForKey(this.getDataRequestsCacheKey()); } /** * Creates a data request. * * @param type Type of the request. * @param comments Comments for the data request. * @param siteId Site ID. If not defined, current site. * @returns Promise resolved when the request is created. * @since 4.4 */ async createDataRequest(type: CoreDataPrivacyDataRequestType, comments: string, siteId?: string): Promise { const site = await CoreSites.getSite(siteId); const params: CoreDataPrivacyCreateDataRequestWSParams = { type, comments, }; const response = await site.write('tool_dataprivacy_create_data_request', params); if (response.warnings && response.warnings.length) { throw new CoreWSError(response.warnings[0]); } return response.datarequestid; } /** * Cancel the data request made by the user. * * @param requestid ID of the request to cancel. * @param siteId Site ID. If not defined, current site. * @returns Promise resolved with boolean: whether the request was canceled. * @since 4.4 */ async cancelDataRequest(requestid: number, siteId?: string): Promise { const site = await CoreSites.getSite(siteId); const params: CoreDataPrivacyCancelDataRequestWSParams = { requestid }; const response = await site.write('tool_dataprivacy_cancel_data_request', params); if (response.warnings && response.warnings.length) { throw new CoreWSError(response.warnings[0]); } return response.result; } /** * Invalidate all the data related to data privacy. */ async invalidateAll(): Promise { await Promise.all([ this.invalidateAccessInformation(), this.invalidateDataRequests(), ]); } /** * Check if the user can cancel a request. * * @param request The request to check. * @returns Whether the user can cancel the request. */ canCancelRequest(request: CoreDataPrivacyRequest): boolean { const cannotCancelStatuses = [ CoreDataPrivacyDataRequestStatus.DATAREQUEST_STATUS_COMPLETE, CoreDataPrivacyDataRequestStatus.DATAREQUEST_STATUS_DOWNLOAD_READY, CoreDataPrivacyDataRequestStatus.DATAREQUEST_STATUS_DELETED, CoreDataPrivacyDataRequestStatus.DATAREQUEST_STATUS_EXPIRED, CoreDataPrivacyDataRequestStatus.DATAREQUEST_STATUS_CANCELLED, CoreDataPrivacyDataRequestStatus.DATAREQUEST_STATUS_REJECTED, ]; return !cannotCancelStatuses.includes(request.status); } } export const CoreDataPrivacy = makeSingleton(CoreDataPrivacyService); export enum CoreDataPrivacyDataRequestType { DATAREQUEST_TYPE_EXPORT = 1, // Data export request type. DATAREQUEST_TYPE_DELETE = 2, // Data deletion request type. DATAREQUEST_TYPE_OTHERS = 3, // Other request type. Usually of enquiries to the DPO. } export enum CoreDataPrivacyDataRequestStatus { DATAREQUEST_STATUS_PENDING = 0, // Newly submitted and we haven't yet started finding out where they have data. DATAREQUEST_STATUS_PREPROCESSING = 1, // Newly submitted and we have started to find the location of data. DATAREQUEST_STATUS_AWAITING_APPROVAL = 2, // Metadata ready and awaiting review and approval by the Data Protection officer. DATAREQUEST_STATUS_APPROVED = 3, // Request approved and will be processed soon. DATAREQUEST_STATUS_PROCESSING = 4, // The request is now being processed. DATAREQUEST_STATUS_COMPLETE = 5, // Information/other request completed. DATAREQUEST_STATUS_CANCELLED = 6, // Data request cancelled by the user. DATAREQUEST_STATUS_REJECTED = 7, // Data request rejected by the DPO. DATAREQUEST_STATUS_DOWNLOAD_READY = 8, // Data request download ready. DATAREQUEST_STATUS_EXPIRED = 9, // Data request expired. DATAREQUEST_STATUS_DELETED = 10, // Data delete request completed, account is removed. } /** * Data returned by tool_dataprivacy_get_access_information WS. */ export type CoreDataPrivacyGetAccessInformationWSResponse = { cancontactdpo: boolean; // Can contact dpo. canmanagedatarequests: boolean; // Can manage data requests. cancreatedatadownloadrequest: boolean; // Can create data download request for self. cancreatedatadeletionrequest: boolean; // Can create data deletion request for self. hasongoingdatadownloadrequest: boolean; // Has ongoing data download request. hasongoingdatadeletionrequest: boolean; // Has ongoing data deletion request. warnings?: CoreWSExternalWarning[]; }; /** * Params of tool_dataprivacy_contact_dpo WS. */ type CoreDataPrivacyContactDPOWSParams = { message: string; // The user's message to the Data Protection Officer(s). }; /** * Data returned by tool_dataprivacy_contact_dpo WS. */ type CoreDataPrivacyContactDPOWSResponse = { result: boolean; // The processing result warnings?: CoreWSExternalWarning[]; }; /** * Params of tool_dataprivacy_create_data_request WS. */ type CoreDataPrivacyCreateDataRequestWSParams = { type: CoreDataPrivacyDataRequestType; // The type of data request to create. 1 for export, 2 for data deletion. comments?: string; // Comments for the data request. foruserid?: number; // The id of the user to create the data request for. Empty for current user. }; /** * Data returned by tool_dataprivacy_create_data_request WS. */ type CoreDataPrivacyCreateDataRequestWSResponse = { datarequestid: number; // The id of the created data request. warnings?: CoreWSExternalWarning[]; }; /** * Params of tool_dataprivacy_cancel_data_request WS. */ type CoreDataPrivacyCancelDataRequestWSParams = { requestid: number; // The request ID }; /** * Data returned by tool_dataprivacy_cancel_data_request WS. */ type CoreDataPrivacyCancelDataRequestWSResponse = { result: boolean; // The processing result warnings?: CoreWSExternalWarning[]; }; /** * Params of tool_dataprivacy_get_data_requests WS. */ type CoreDataPrivacyGetDataRequestsWSParams = { userid?: number; // The id of the user to get the data requests for. Empty for all users. statuses?: CoreDataPrivacyDataRequestStatus[]; // The statuses of the data requests to get. // 0 for pending 1 preprocessing, 2 awaiting approval, 3 approved, // 4 processed, 5 completed, 6 cancelled, 7 rejected. types?: number[]; // The types of the data requests to get. 1 for export, 2 for data deletion. creationmethods?: number[]; // The creation methods of the data requests to get. 0 for manual, 1 for automatic. sort?: string; // The field to sort the data requests by. limitfrom?: number; // The number to start getting the data requests from. limitnum?: number; // The number of data requests to get. }; /** * Data returned by tool_dataprivacy_get_data_requests WS. */ type CoreDataPrivacyGetDataRequestsWSResponse = { requests: CoreDataPrivacyRequest[]; // The data requests. warnings?: CoreWSExternalWarning[]; }; /** * Data for the dataprivacy request. */ export type CoreDataPrivacyRequest = { type: CoreDataPrivacyDataRequestType; // Type. comments: string; // Comments. commentsformat: number; // Commentsformat. userid: number; // Userid. requestedby: number; // Requestedby. status: CoreDataPrivacyDataRequestStatus; // Status. dpo: number; // Dpo. dpocomment: string; // Dpocomment. dpocommentformat: number; // Dpocommentformat. systemapproved: boolean; // Systemapproved. creationmethod: number; // Creationmethod. id: number; // Id. timecreated: number; // Timecreated. timemodified: number; // Timemodified. usermodified: number; // Usermodified. foruser: CoreUserSummary; // The user the request is for. requestedbyuser: CoreUserSummary; // The user who requested the data. dpouser?: CoreUserSummary; // The user who processed the request. messagehtml?: string; // Messagehtml. typename: string; // Typename. typenameshort: string; // Typenameshort. statuslabel: string; // Statuslabel. statuslabelclass: string; // Statuslabelclass. canreview?: boolean; // Canreview. approvedeny?: boolean; // Approvedeny. allowfiltering?: boolean; // Allowfiltering. canmarkcomplete?: boolean; // Canmarkcomplete. downloadlink?: string; // Downloadlink. };