This is an automated email from the ASF dual-hosted git repository.

erisu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cordova-ios.git


The following commit(s) were added to refs/heads/master by this push:
     new c97845ac feat: add privacy-manifest config support (#1406)
c97845ac is described below

commit c97845acb7e4fe659c0f71103bef44700da37d5c
Author: knaito-asial <81949829+knaito-as...@users.noreply.github.com>
AuthorDate: Wed Mar 13 15:03:13 2024 +0900

    feat: add privacy-manifest config support (#1406)
    
    * feat: privacy manifest settings in config.xml
    * refac: remove unused code and tidy up
    * refac: update class name PlatformConfigParser
    * feat: add elementtree in package.json
    * dev: change privacy manifest tag name to privacy-manifest from 
privacy-manifest-ios
    * test: add test codes
    * refactor: Modified to match Linter.
    * refac: improve PlatformConfigParser
    * refac: remove unnecessary blank line
    
    ---------
    
    Co-authored-by: エリス <er...@users.noreply.github.com>
---
 lib/PlatformConfigParser.js                        | 32 +++++++++++++
 lib/prepare.js                                     | 40 ++++++++++++++--
 package-lock.json                                  |  1 +
 package.json                                       |  3 +-
 .../unit/fixtures/prepare/no-privacy-manifest.xml  | 23 +++++++++
 .../unit/fixtures/prepare/privacy-manifest.xml     | 56 ++++++++++++++++++++++
 tests/spec/unit/prepare.spec.js                    | 38 +++++++++++++++
 7 files changed, 189 insertions(+), 4 deletions(-)

diff --git a/lib/PlatformConfigParser.js b/lib/PlatformConfigParser.js
new file mode 100644
index 00000000..675287c6
--- /dev/null
+++ b/lib/PlatformConfigParser.js
@@ -0,0 +1,32 @@
+/**
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you 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.
+*/
+
+const ConfigParser = require('cordova-common').ConfigParser;
+
+class PlatformConfigParser extends ConfigParser {
+    /**
+     * Returns the privacy manifest node, if available.
+     * Otherwise `null` is returned.
+     */
+    getPrivacyManifest () {
+        return this.doc.find('./platform[@name="ios"]/privacy-manifest');
+    }
+}
+
+module.exports = PlatformConfigParser;
diff --git a/lib/prepare.js b/lib/prepare.js
index 38239e4c..cfaab02c 100644
--- a/lib/prepare.js
+++ b/lib/prepare.js
@@ -17,12 +17,11 @@
     under the License.
 */
 
-'use strict';
-
 const fs = require('fs-extra');
 const path = require('path');
 const unorm = require('unorm');
 const plist = require('plist');
+const et = require('elementtree');
 const URL = require('url');
 const events = require('cordova-common').events;
 const xmlHelpers = require('cordova-common').xmlHelpers;
@@ -35,6 +34,7 @@ const FileUpdater = require('cordova-common').FileUpdater;
 const projectFile = require('./projectFile');
 const Podfile = require('./Podfile').Podfile;
 const check_reqs = require('./check_reqs');
+const PlatformConfigParser = require('./PlatformConfigParser');
 
 // launch storyboard and related constants
 const IMAGESET_COMPACT_SIZE_CLASS = 'compact';
@@ -43,9 +43,16 @@ const CDV_ANY_SIZE_CLASS = 'any';
 module.exports.prepare = function (cordovaProject, options) {
     const platformJson = PlatformJson.load(this.locations.root, 'ios');
     const munger = new PlatformMunger('ios', this.locations.root, 
platformJson, new PluginInfoProvider());
-
     this._config = updateConfigFile(cordovaProject.projectConfig, munger, 
this.locations);
 
+    const parser = new PlatformConfigParser(cordovaProject.projectConfig.path);
+    try {
+        const manifest = parser.getPrivacyManifest();
+        overwritePrivacyManifest(manifest, this.locations);
+    } catch (err) {
+        return Promise.reject(new CordovaError(`Could not parse 
PrivacyManifest in config.xml: ${err}`));
+    }
+
     // Update own www dir with project's www assets and plugins' assets and 
js-files
     return updateWww(cordovaProject, this.locations)
         // update project according to config.xml changes.
@@ -87,6 +94,33 @@ module.exports.clean = function (options) {
     });
 };
 
+/**
+ * Overwrites the privacy manifest file with the provided manifest or sets the 
default manifest.
+ * @param {ElementTree} manifest - The manifest to be written to the privacy 
manifest file.
+ * @param {Object} locations - The locations object containing the path to the 
Xcode Cordova project.
+ */
+function overwritePrivacyManifest (manifest, locations) {
+    const privacyManifestDest = path.join(locations.xcodeCordovaProj, 
'PrivacyInfo.xcprivacy');
+    if (manifest != null) {
+        const XML_DECLARATION = '<?xml version="1.0" encoding="UTF-8"?>\n';
+        const DOCTYPE = '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" 
"http://www.apple.com/DTDs/PropertyList-1.0.dtd";>\n';
+        const plistElement = et.Element('plist');
+        plistElement.set('version', '1.0');
+        const dictElement = et.SubElement(plistElement, 'dict');
+        manifest.getchildren().forEach((child) => {
+            dictElement.append(child);
+        });
+        const etree = new et.ElementTree(plistElement);
+        const xmlString = XML_DECLARATION + DOCTYPE + etree.write({ 
xml_declaration: false });
+        fs.writeFileSync(privacyManifestDest, xmlString, 'utf-8');
+        return;
+    }
+    // Set default privacy manifest
+    const defaultPrivacyManifest = path.join(__dirname, '..', 'templates', 
'project', '__PROJECT_NAME__', 'PrivacyInfo.xcprivacy');
+    const xmlString = fs.readFileSync(defaultPrivacyManifest, 'utf8');
+    fs.writeFileSync(privacyManifestDest, xmlString, 'utf-8');
+}
+
 /**
  * Updates config files in project based on app's config.xml and config munge,
  *   generated by plugins.
diff --git a/package-lock.json b/package-lock.json
index 744d6aa1..862e2115 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,6 +10,7 @@
       "license": "Apache-2.0",
       "dependencies": {
         "cordova-common": "^5.0.0",
+        "elementtree": "^0.1.7",
         "execa": "^5.1.1",
         "fs-extra": "^11.1.1",
         "ios-sim": "^8.0.2",
diff --git a/package.json b/package.json
index 6082340a..2a305d22 100644
--- a/package.json
+++ b/package.json
@@ -51,7 +51,8 @@
     "unorm": "^1.6.0",
     "which": "^3.0.1",
     "xcode": "^3.0.1",
-    "xml-escape": "^1.1.0"
+    "xml-escape": "^1.1.0",
+    "elementtree": "^0.1.7"
   },
   "nyc": {
     "include": [
diff --git a/tests/spec/unit/fixtures/prepare/no-privacy-manifest.xml 
b/tests/spec/unit/fixtures/prepare/no-privacy-manifest.xml
new file mode 100644
index 00000000..98afac21
--- /dev/null
+++ b/tests/spec/unit/fixtures/prepare/no-privacy-manifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<widget id="io.cordova.hellocordova" 
ios-CFBundleIdentifier="io.cordova.hellocordova.ios" version="1.0.0" 
xmlns="http://www.w3.org/ns/widgets"; 
xmlns:cdv="http://cordova.apache.org/ns/1.0";>
+    <name>SampleApp</name>
+</widget>
diff --git a/tests/spec/unit/fixtures/prepare/privacy-manifest.xml 
b/tests/spec/unit/fixtures/prepare/privacy-manifest.xml
new file mode 100644
index 00000000..510464a5
--- /dev/null
+++ b/tests/spec/unit/fixtures/prepare/privacy-manifest.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you 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.
+-->
+
+<widget id="io.cordova.hellocordova" 
ios-CFBundleIdentifier="io.cordova.hellocordova.ios" version="1.0.0" 
xmlns="http://www.w3.org/ns/widgets"; 
xmlns:cdv="http://cordova.apache.org/ns/1.0";>
+    <name>SampleApp</name>
+    <platform name="ios">
+        <privacy-manifest>
+            <key>NSPrivacyTracking</key>
+            <true/>
+            <key>NSPrivacyAccessedAPITypes</key>
+            <array/>
+            <key>NSPrivacyTrackingDomains</key>
+            <array/>
+            <key>NSPrivacyCollectedDataTypes</key>
+            <array>
+                <dict>
+                    <!-- The value provided by Apple for 'Device ID' data type 
-->
+                    <key>NSPrivacyCollectedDataType</key>
+                    <string>NSPrivacyCollectedDataTypeDeviceID</string>
+
+                    <!-- Fingerprint Identification SDK does not link the 
'Device ID' with user's identity --> 
+                    <key>NSPrivacyCollectedDataTypeLinked</key>
+                    <false/>
+
+                    <!-- Fingerprint Identification SDK does not use 'Device 
ID' for tracking -->
+                    <key>NSPrivacyCollectedDataTypeTracking</key>
+                    <false/>
+
+                    <!-- Fingerprint Identification SDK uses 'Device ID' for 
App Functionality 
+                        (prevent fraud and implement security measures) -->
+                    <key>NSPrivacyCollectedDataTypePurposes</key>
+                    <array>
+                    
<string>NSPrivacyCollectedDataTypePurposeAppFunctionality</string>
+                    </array>
+                </dict>
+            </array>
+        </privacy-manifest>
+    </platform>
+</widget>
diff --git a/tests/spec/unit/prepare.spec.js b/tests/spec/unit/prepare.spec.js
index b51a9f6d..2ec83531 100644
--- a/tests/spec/unit/prepare.spec.js
+++ b/tests/spec/unit/prepare.spec.js
@@ -1504,6 +1504,44 @@ describe('prepare', () => {
                 
expect(plist.build.calls.mostRecent().args[0].CFBundleDisplayName).toEqual('MyApp');
             });
         });
+        it('Test#021 : <privacy-manifest> - should write out the privacy 
manifest ', () => {
+            plist.parse.and.callThrough();
+            writeFileSyncSpy.and.callThrough();
+            const projectRoot = iosProject;
+            const platformProjDir = path.join(projectRoot, 'platforms', 'ios', 
'SampleApp');
+            const PlatformConfigParser = 
require('../../../lib/PlatformConfigParser');
+            const my_config = new PlatformConfigParser(path.join(FIXTURES, 
'prepare', 'privacy-manifest.xml'));
+            const privacyManifest = my_config.getPrivacyManifest();
+            const overwritePrivacyManifest = 
prepare.__get__('overwritePrivacyManifest');
+            overwritePrivacyManifest(privacyManifest, p.locations);
+            const privacyManifestPathDest = path.join(platformProjDir, 
'PrivacyInfo.xcprivacy');
+            
expect(writeFileSyncSpy).toHaveBeenCalledWith(privacyManifestPathDest, 
jasmine.any(String), 'utf-8');
+            const xml = writeFileSyncSpy.calls.all()[0].args[1];
+            const json = plist.parse(xml);
+            expect(json.NSPrivacyTracking).toBeTrue();
+            expect(json.NSPrivacyAccessedAPITypes.length).toBe(0);
+            expect(json.NSPrivacyTrackingDomains.length).toBe(0);
+            expect(json.NSPrivacyCollectedDataTypes.length).toBe(1);
+        });
+        it('Test#022 : no <privacy-manifest> - should write out the privacy 
manifest ', () => {
+            plist.parse.and.callThrough();
+            writeFileSyncSpy.and.callThrough();
+            const projectRoot = iosProject;
+            const platformProjDir = path.join(projectRoot, 'platforms', 'ios', 
'SampleApp');
+            const PlatformConfigParser = 
require('../../../lib/PlatformConfigParser');
+            const my_config = new PlatformConfigParser(path.join(FIXTURES, 
'prepare', 'no-privacy-manifest.xml'));
+            const privacyManifest = my_config.getPrivacyManifest();
+            const overwritePrivacyManifest = 
prepare.__get__('overwritePrivacyManifest');
+            overwritePrivacyManifest(privacyManifest, p.locations);
+            const privacyManifestPathDest = path.join(platformProjDir, 
'PrivacyInfo.xcprivacy');
+            
expect(writeFileSyncSpy).toHaveBeenCalledWith(privacyManifestPathDest, 
jasmine.any(String), 'utf-8');
+            const xml = writeFileSyncSpy.calls.all()[0].args[1];
+            const json = plist.parse(xml);
+            expect(json.NSPrivacyTracking).toBeFalse();
+            expect(json.NSPrivacyAccessedAPITypes.length).toBe(0);
+            expect(json.NSPrivacyTrackingDomains.length).toBe(0);
+            expect(json.NSPrivacyCollectedDataTypes.length).toBe(0);
+        });
     });
 
     describe('<resource-file> tests', () => {


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@cordova.apache.org
For additional commands, e-mail: commits-h...@cordova.apache.org

Reply via email to