Skip to content

Commit 89ae295

Browse files
authored
Merge pull request #3393 from dpalou/MOBILE-4134
MOBILE-4134 android: Add script to remove INSTALL_PACKAGES permission
2 parents 983e4ab + acd799c commit 89ae295

File tree

8 files changed

+113
-9
lines changed

8 files changed

+113
-9
lines changed

config.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
<resource-file src="resources/android/icon/drawable-hdpi-smallicon.png" target="app/src/main/res/mipmap-hdpi/smallicon.png" />
6464
<resource-file src="resources/android/icon/drawable-xhdpi-smallicon.png" target="app/src/main/res/mipmap-xhdpi/smallicon.png" />
6565
<resource-file src="resources/android/xml/network_security_config.xml" target="app/src/main/res/xml/network_security_config.xml" />
66+
<hook src="scripts/android/remove_permissions.js" type="after_prepare" />
6667
<edit-config file="AndroidManifest.xml" mode="merge" target="/manifest/application/activity[@android:name='MainActivity']">
6768
<activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|screenLayout|smallestScreenSize" android:exported="true" />
6869
</edit-config>

package-lock.json

Lines changed: 46 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,8 @@
172172
"terser-webpack-plugin": "4.2.3",
173173
"ts-jest": "26.4.1",
174174
"ts-node": "8.3.0",
175-
"typescript": "3.9.9"
175+
"typescript": "3.9.9",
176+
"xml2js": "0.4.23"
176177
},
177178
"engines": {
178179
"node": ">=14.15.0 <15"
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// (C) Copyright 2015 Moodle Pty Ltd.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
/**
16+
* Script to remove permissions for APK files. Implementation obtained from:
17+
* https://stackoverflow.com/a/67530993/5820052
18+
*/
19+
const fs = require('fs/promises');
20+
const xml2js = require('xml2js');
21+
22+
const REMOVE_PERMISSIONS = [
23+
'android.permission.REQUEST_INSTALL_PACKAGES',
24+
];
25+
26+
module.exports = async function(context) {
27+
const root = context.opts.projectRoot;
28+
const manifestPath = root + '/platforms/android/app/src/main/AndroidManifest.xml';
29+
30+
const manifestXml = await fs.readFile(manifestPath);
31+
const manifest = await xml2js.parseStringPromise(manifestXml);
32+
33+
const usesPermissions = manifest.manifest['uses-permission'];
34+
if (Array.isArray(usesPermissions)) {
35+
manifest.manifest['uses-permission'] = usesPermissions.filter(usesPermission => {
36+
const attrs = usesPermission.$ || {};
37+
const name = attrs['android:name'];
38+
39+
if (REMOVE_PERMISSIONS.includes(name)) {
40+
console.log(`Removing permission "${name}" from AndroidManifest.xml`);
41+
return false;
42+
} else {
43+
return true;
44+
}
45+
});
46+
}
47+
48+
const newManifest = (new xml2js.Builder()).buildObject(manifest);
49+
50+
await fs.writeFile(manifestPath, newManifest);
51+
}

scripts/langindex.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1471,6 +1471,7 @@
14711471
"core.cannotconnecttrouble": "local_moodlemobileapp",
14721472
"core.cannotconnectverify": "local_moodlemobileapp",
14731473
"core.cannotdownloadfiles": "local_moodlemobileapp",
1474+
"core.cannotinstallapk": "local_moodlemobileapp",
14741475
"core.cannotlogoutpageblocks": "local_moodlemobileapp",
14751476
"core.cannotopeninapp": "local_moodlemobileapp",
14761477
"core.cannotopeninappdownload": "local_moodlemobileapp",

src/core/lang.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"cannotconnecttrouble": "We're having trouble connecting to your site.",
1919
"cannotconnectverify": "<strong>Please check the address is correct.</strong>",
2020
"cannotdownloadfiles": "File downloading is disabled. Please contact your site administrator.",
21+
"cannotinstallapk": "For security reasons, it is not allowed to install unknown apps from the Moodle app. Please open the file using a browser.",
2122
"cannotlogoutpageblocks": "Please save or discard your changes before continuing.",
2223
"cannotopeninapp": "This file may not work as expected on this device. Would you like to open it anyway?",
2324
"cannotopeninappdownload": "This file may not work as expected on this device. Would you like to download it anyway?",

src/core/services/file-helper.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { CoreConstants } from '@/core/constants';
2727
import { CoreError } from '@classes/errors/error';
2828
import { makeSingleton, Translate } from '@singletons';
2929
import { CoreNetworkError } from '@classes/errors/network-error';
30+
import { CoreMimetypeUtils } from './utils/mimetype';
3031

3132
/**
3233
* Provider to provide some helper functions regarding files and packages.
@@ -303,18 +304,22 @@ export class CoreFileHelperProvider {
303304
}
304305

305306
/**
306-
* Whether the file has to be opened in browser (external repository).
307-
* The file must have a mimetype attribute.
307+
* Whether the file has to be opened in browser.
308308
*
309309
* @param file The file to check.
310310
* @return Whether the file should be opened in browser.
311311
*/
312312
shouldOpenInBrowser(file: CoreWSFile): boolean {
313-
if (!file || !('isexternalfile' in file) || !file.isexternalfile || !file.mimetype) {
313+
if (!file.mimetype) {
314314
return false;
315315
}
316316

317317
const mimetype = file.mimetype;
318+
if (!('isexternalfile' in file) || !file.isexternalfile) {
319+
return mimetype === 'application/vnd.android.package-archive' ||
320+
CoreMimetypeUtils.getFileExtension(file.filename ?? '') === 'apk';
321+
}
322+
318323
if (mimetype.indexOf('application/vnd.google-apps.') != -1) {
319324
// Google Docs file, always open in browser.
320325
return true;

src/core/services/utils/utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import { CoreFileEntry } from '@services/file-helper';
3434
import { CoreConstants } from '@/core/constants';
3535
import { CoreWindow } from '@singletons/window';
3636
import { CoreColors } from '@singletons/colors';
37+
import { CoreError } from '@classes/errors/error';
3738

3839
type TreeNode<T> = T & { children: TreeNode<T>[] };
3940

@@ -958,6 +959,8 @@ export class CoreUtilsProvider {
958959
this.openInApp(path);
959960

960961
return;
962+
} else if (extension === 'apk' && CoreApp.isAndroid()) {
963+
throw new CoreError(Translate.instant('core.cannotinstallapk'));
961964
}
962965

963966
// Path needs to be decoded, the file won't be opened if the path has %20 instead of spaces and so.

0 commit comments

Comments
 (0)