commit
e647e535fa
|
@ -67,11 +67,12 @@
|
|||
<resource-file src="resources/android/icon/drawable-hdpi-smallicon.png" target="app/src/main/res/mipmap-hdpi/smallicon.png" />
|
||||
<resource-file src="resources/android/icon/drawable-xhdpi-smallicon.png" target="app/src/main/res/mipmap-xhdpi/smallicon.png" />
|
||||
<resource-file src="resources/android/xml/network_security_config.xml" target="app/src/main/res/xml/network_security_config.xml" />
|
||||
<resource-file src="resources/android/xml/backup_rules.xml" target="app/src/main/res/xml/backup_rules.xml" />
|
||||
<edit-config file="AndroidManifest.xml" mode="merge" target="/manifest/application/activity[@android:name='MainActivity']">
|
||||
<activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|screenLayout|smallestScreenSize" android:exported="true" />
|
||||
</edit-config>
|
||||
<edit-config file="AndroidManifest.xml" mode="merge" target="/manifest/application">
|
||||
<application android:largeHeap="true" android:networkSecurityConfig="@xml/network_security_config" />
|
||||
<application android:allowBackup="true" android:dataExtractionRules="@xml/backup_rules" android:largeHeap="true" android:networkSecurityConfig="@xml/network_security_config" />
|
||||
</edit-config>
|
||||
<config-file parent="/manifest/application" target="AndroidManifest.xml">
|
||||
<meta-data android:name="firebase_analytics_collection_deactivated" android:value="true" />
|
||||
|
|
|
@ -7,10 +7,20 @@
|
|||
</js-module>
|
||||
<platform name="android">
|
||||
<config-file target="res/xml/config.xml" parent="/*">
|
||||
<feature name="SystemUI">
|
||||
<param name="android-package" value="com.moodle.moodlemobile.SystemUI"/>
|
||||
<feature name="SecureStorage">
|
||||
<param name="android-package" value="com.moodle.moodlemobile.SecureStorage"/>
|
||||
</feature>
|
||||
</config-file>
|
||||
<source-file src="src/android/SystemUI.java" target-dir="src/com/moodle/moodlemobile" />
|
||||
<source-file src="src/android/SecureStorage.java" target-dir="src/com/moodle/moodlemobile" />
|
||||
</platform>
|
||||
<platform name="ios">
|
||||
<config-file target="config.xml" parent="/*">
|
||||
<feature name="SecureStorage">
|
||||
<param name="ios-package" value="SecureStorage" />
|
||||
</feature>
|
||||
</config-file>
|
||||
|
||||
<header-file src="src/ios/SecureStorage.h" />
|
||||
<source-file src="src/ios/SecureStorage.m" />
|
||||
</platform>
|
||||
</plugin>
|
||||
|
|
|
@ -22,10 +22,14 @@ const { resolve } = require('path');
|
|||
|
||||
const bundle = readFileSync(resolve(__dirname, '../www/index.js')).toString();
|
||||
const template = readFileSync(resolve(__dirname, './templates/cordova-plugin.js')).toString();
|
||||
const pluginsPath = resolve(__dirname, '../../plugins/');
|
||||
const platformsPath = resolve(__dirname, '../../platforms/');
|
||||
const filePaths = [
|
||||
resolve(pluginsPath, 'cordova-plugin-moodleapp/www/index.js'),
|
||||
resolve(platformsPath, 'android/app/src/main/assets/www/plugins/cordova-plugin-moodleapp/www/index.js'),
|
||||
resolve(platformsPath, 'android/platform_www/plugins/cordova-plugin-moodleapp/www/index.js'),
|
||||
resolve(platformsPath, 'ios/platform_www/plugins/cordova-plugin-moodleapp/www/index.js'),
|
||||
resolve(platformsPath, 'ios/www/plugins/cordova-plugin-moodleapp/www/index.js'),
|
||||
];
|
||||
const pluginIndex = template
|
||||
.replace('[[PLUGIN_NAME]]', 'cordova-plugin-moodleapp.moodleapp')
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
// (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.
|
||||
|
||||
package com.moodle.moodlemobile;
|
||||
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONException;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CallbackContext;
|
||||
import org.apache.cordova.PluginResult;
|
||||
|
||||
import com.adobe.phonegap.push.EncryptionHandler;
|
||||
|
||||
public class SecureStorage extends CordovaPlugin {
|
||||
|
||||
private static final String TAG = "SecureStorage";
|
||||
private static final String SHARED_PREFS_NAME = "moodlemobile_shared_prefs";
|
||||
|
||||
@Override
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
|
||||
try {
|
||||
switch (action) {
|
||||
case "get":
|
||||
callbackContext.success(this.get(args.getJSONArray(0), args.getString(1)));
|
||||
|
||||
return true;
|
||||
case "store":
|
||||
this.store(args.getJSONObject(0), args.getString(1));
|
||||
callbackContext.success();
|
||||
|
||||
return true;
|
||||
case "delete":
|
||||
this.delete(args.getJSONArray(0), args.getString(1));
|
||||
callbackContext.success();
|
||||
|
||||
return true;
|
||||
case "deleteCollection":
|
||||
this.deleteCollection(args.getString(0));
|
||||
callbackContext.success();
|
||||
|
||||
return true;
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "Failed executing action: " + action, e);
|
||||
callbackContext.error(e.getMessage());
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get several values from secure storage.
|
||||
*
|
||||
* @param names List of names to get.
|
||||
* @param collection The collection where the values are stored.
|
||||
* @return Values for each name.
|
||||
*/
|
||||
private JSONObject get(JSONArray names, String collection) throws GeneralSecurityException, IOException, JSONException {
|
||||
Context context = this.cordova.getActivity().getApplicationContext();
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(collection);
|
||||
JSONObject result = new JSONObject();
|
||||
|
||||
Log.d(TAG, "Get values with names " + names.toString());
|
||||
|
||||
for(int i = 0; i < names.length(); i++) {
|
||||
String name = names.optString(i);
|
||||
|
||||
if (name == null || name.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String rawValue = sharedPreferences.getString(name, null);
|
||||
if (rawValue == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result.put(name, EncryptionHandler.Companion.decrypt(context, rawValue));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store data in secure storage.
|
||||
*
|
||||
* @param data Data to store, using a name -> value format.
|
||||
* @param collection The collection where to store the values.
|
||||
*/
|
||||
private void store(JSONObject data, String collection) throws GeneralSecurityException, IOException, JSONException {
|
||||
Context context = this.cordova.getActivity().getApplicationContext();
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(collection);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
JSONArray names = data.names();
|
||||
|
||||
Log.d(TAG, "Store values with names " + names.toString());
|
||||
|
||||
for(int i = 0; i < names.length(); i++) {
|
||||
String name = names.optString(i);
|
||||
|
||||
if (name != null && !name.isEmpty()) {
|
||||
editor.putString(name, EncryptionHandler.Companion.encrypt(context, data.getString(name)));
|
||||
}
|
||||
}
|
||||
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete some values from secure storage.
|
||||
*
|
||||
* @param names Names to delete.
|
||||
* @param collection The collection where to delete the values.
|
||||
*/
|
||||
private void delete(JSONArray names, String collection) throws GeneralSecurityException, IOException {
|
||||
Log.d(TAG, "Delete value with names " + names.toString());
|
||||
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(collection);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
|
||||
for(int i = 0; i < names.length(); i++) {
|
||||
String name = names.optString(i);
|
||||
|
||||
if (name != null && !name.isEmpty()) {
|
||||
editor.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all values from a collection.
|
||||
*
|
||||
* @param collection The collection to delete.
|
||||
*/
|
||||
private void deleteCollection(String collection) throws GeneralSecurityException, IOException {
|
||||
Log.d(TAG, "Delete all values in collection " + collection);
|
||||
|
||||
SharedPreferences sharedPreferences = getSharedPreferences(collection);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
editor.clear();
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get shared preferences instance.
|
||||
*
|
||||
* @param collection The collection to use.
|
||||
* @return Shared preferences instance.
|
||||
*/
|
||||
private SharedPreferences getSharedPreferences(String collection) {
|
||||
return this.cordova.getActivity().getApplicationContext().getSharedPreferences(
|
||||
SHARED_PREFS_NAME + "_" + collection,
|
||||
Context.MODE_PRIVATE
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
// (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.
|
||||
|
||||
package com.moodle.moodlemobile;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.view.Window;
|
||||
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CallbackContext;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
||||
public class SystemUI extends CordovaPlugin {
|
||||
|
||||
private static final String TAG = "SystemUI";
|
||||
|
||||
@Override
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
|
||||
try {
|
||||
switch (action) {
|
||||
// No actions yet.
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, "Failed executing action: " + action, e);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import <Cordova/CDVPlugin.h>
|
||||
|
||||
@interface SecureStorage : CDVPlugin {}
|
||||
|
||||
- (void)get:(CDVInvokedUrlCommand*)command;
|
||||
- (void)store:(CDVInvokedUrlCommand*)command;
|
||||
- (void)delete:(CDVInvokedUrlCommand*)command;
|
||||
- (void)deleteCollection:(CDVInvokedUrlCommand*)command;
|
||||
|
||||
@end
|
|
@ -0,0 +1,218 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import <Cordova/CDVPlugin.h>
|
||||
#import <Cordova/CDVPluginResult.h>
|
||||
#import "SecureStorage.h"
|
||||
|
||||
@implementation SecureStorage
|
||||
|
||||
- (void)get:(CDVInvokedUrlCommand*)command {
|
||||
NSArray* names = [command argumentAtIndex:0];
|
||||
NSString* collection = [command argumentAtIndex:1 withDefault:@""];
|
||||
NSMutableDictionary* result = [NSMutableDictionary new];
|
||||
|
||||
NSLog(@"SecureStorage: Get values with names %@ in collection %@", names, collection);
|
||||
|
||||
for (NSString* name in names) {
|
||||
NSString* value = [self getValue:name inCollection:collection];
|
||||
if (value != nil) {
|
||||
result[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result];
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
- (NSString *)getValue:(NSString*)name inCollection:(NSString*)collection {
|
||||
if ([name length] == 0) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSDictionary* query = @{
|
||||
(id)kSecClass: (id)kSecClassGenericPassword,
|
||||
(id)kSecAttrAccount: name,
|
||||
(id)kSecAttrService: collection,
|
||||
(id)kSecReturnData: @YES,
|
||||
(id)kSecMatchLimit: (id)kSecMatchLimitOne
|
||||
};
|
||||
NSData* storedData = NULL;
|
||||
|
||||
OSStatus status = SecItemCopyMatching((CFDictionaryRef)query, (void *) &storedData);
|
||||
|
||||
if (status == errSecSuccess) {
|
||||
return [[NSString alloc] initWithData:storedData encoding:NSUTF8StringEncoding];
|
||||
} else if (status != errSecItemNotFound) {
|
||||
NSLog(@"Error getting value for %@ in collection %@. Status: %d", name, collection, (int) status);
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)store:(CDVInvokedUrlCommand*)command {
|
||||
|
||||
NSDictionary* data = [command argumentAtIndex:0];
|
||||
NSString* collection = [command argumentAtIndex:1 withDefault:@""];
|
||||
NSArray* names = [data allKeys];
|
||||
BOOL error = false;
|
||||
|
||||
// Variables to be able to rollback changes if something fails.
|
||||
NSMutableArray* insertedNames = [NSMutableArray new];
|
||||
NSMutableDictionary* previousValues = [NSMutableDictionary new];
|
||||
|
||||
NSLog(@"SecureStorage: Store values with names %@ in collection %@", names, collection);
|
||||
|
||||
for (NSString* name in data) {
|
||||
OSStatus status;
|
||||
NSString* storedValue = [self getValue:name inCollection:collection];
|
||||
|
||||
if (storedValue != nil) {
|
||||
status = [self updateName:name withValue:data[name] inCollection: collection];
|
||||
} else {
|
||||
status = [self addName:name withValue:data[name] inCollection: collection];
|
||||
}
|
||||
|
||||
if (status != errSecSuccess) {
|
||||
NSLog(@"Error storing value for %@ in collection %@. Status: %d", name, collection, (int) status);
|
||||
error = true;
|
||||
|
||||
// Rollback.
|
||||
for (NSString *name in insertedNames) {
|
||||
[self deleteName:name fromCollection:collection];
|
||||
}
|
||||
for(NSString *name in previousValues) {
|
||||
[self updateName:name withValue:previousValues[name] inCollection: collection];
|
||||
}
|
||||
|
||||
break;
|
||||
} else if (storedValue != nil) {
|
||||
previousValues[name] = storedValue;
|
||||
} else {
|
||||
[insertedNames addObject:name];
|
||||
}
|
||||
}
|
||||
|
||||
CDVPluginResult* pluginResult;
|
||||
if (error) {
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR
|
||||
messageAsString:@"Error storing one or more values in secure storage."];
|
||||
} else {
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
}
|
||||
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
- (OSStatus)addName:(NSString*)name withValue:(NSString*)value inCollection:(NSString*)collection {
|
||||
NSData* newValue = [value dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSMutableDictionary* query = [NSMutableDictionary new];
|
||||
query[(id)kSecClass] = (id)kSecClassGenericPassword;
|
||||
query[(id)kSecAttrAccount] = name;
|
||||
query[(id)kSecAttrService] = collection;
|
||||
query[(id)kSecAttrAccessible] = (id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly;
|
||||
query[(id)kSecValueData] = newValue;
|
||||
|
||||
return SecItemAdd((CFDictionaryRef)query, nil);
|
||||
}
|
||||
|
||||
- (OSStatus)updateName:(NSString*)name withValue:(NSString*)value inCollection:(NSString*)collection {
|
||||
NSData* newValue = [value dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSMutableDictionary* query = [NSMutableDictionary new];
|
||||
query[(id)kSecClass] = (id)kSecClassGenericPassword;
|
||||
query[(id)kSecAttrAccount] = name;
|
||||
query[(id)kSecAttrService] = collection;
|
||||
query[(id)kSecAttrAccessible] = (id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly;
|
||||
|
||||
return SecItemUpdate((CFDictionaryRef)query, (CFDictionaryRef)@{
|
||||
(id)kSecValueData: newValue
|
||||
});
|
||||
}
|
||||
|
||||
- (void)delete:(CDVInvokedUrlCommand*)command {
|
||||
NSArray* names = [command argumentAtIndex:0];
|
||||
NSString* collection = [command argumentAtIndex:1 withDefault:@""];
|
||||
BOOL error = false;
|
||||
|
||||
// Variable to be able to rollback changes if something fails.
|
||||
NSMutableDictionary* deletedValues = [NSMutableDictionary new];
|
||||
|
||||
NSLog(@"SecureStorage: Delete values with names %@ in collection %@", names, collection);
|
||||
|
||||
for (NSString* name in names) {
|
||||
if ([name length] == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NSString* storedValue = [self getValue:name inCollection:collection];
|
||||
if (storedValue == nil) {
|
||||
continue;
|
||||
}
|
||||
|
||||
OSStatus status = [self deleteName:name fromCollection:collection];
|
||||
|
||||
if (status != errSecSuccess && status != errSecItemNotFound) {
|
||||
NSLog(@"Error deleting entry with name %@ in collection %@. Status: %d", name, collection, (int) status);
|
||||
error = true;
|
||||
|
||||
// Rollback.
|
||||
for (NSString *name in deletedValues) {
|
||||
[self addName:name withValue:deletedValues[name] inCollection:collection];
|
||||
}
|
||||
} else {
|
||||
deletedValues[name] = storedValue;
|
||||
}
|
||||
}
|
||||
|
||||
CDVPluginResult* pluginResult;
|
||||
if (error) {
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR
|
||||
messageAsString:@"Error deleting one or more values from secure storage."];
|
||||
} else {
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
}
|
||||
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
- (OSStatus)deleteName:(NSString*)name fromCollection:(NSString*)collection {
|
||||
NSDictionary* query = @{
|
||||
(id)kSecClass: (id)kSecClassGenericPassword,
|
||||
(id)kSecAttrAccount: name,
|
||||
(id)kSecAttrService: collection,
|
||||
};
|
||||
|
||||
return SecItemDelete((CFDictionaryRef)query);
|
||||
}
|
||||
|
||||
- (void)deleteCollection:(CDVInvokedUrlCommand*)command {
|
||||
NSString* collection = [command argumentAtIndex:0 withDefault:@""];
|
||||
|
||||
if ([collection length] == 0) {
|
||||
NSLog(@"SecureStorage: Collection cannot be empty in deleteCollection");
|
||||
CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
NSLog(@"SecureStorage: Delete all values in collection %@", collection);
|
||||
|
||||
NSDictionary* query = @{
|
||||
(id)kSecClass: (id)kSecClassGenericPassword,
|
||||
(id)kSecAttrService: collection,
|
||||
};
|
||||
|
||||
OSStatus status = SecItemDelete((CFDictionaryRef)query);
|
||||
|
||||
CDVPluginResult* pluginResult;
|
||||
if (status == errSecSuccess || status == errSecItemNotFound) {
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||
} else {
|
||||
NSLog(@"Error deleting all values in collection %@. Status: %d", collection, (int) status);
|
||||
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION
|
||||
messageAsString:@"Error deleting values from secure storage."];
|
||||
}
|
||||
|
||||
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
||||
}
|
||||
|
||||
@end
|
|
@ -12,10 +12,10 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { SystemUI } from './plugins/SystemUI';
|
||||
import { SecureStorage } from './plugins/SecureStorage';
|
||||
|
||||
const api: MoodleAppPlugins = {
|
||||
systemUI: new SystemUI(),
|
||||
secureStorage: new SecureStorage(),
|
||||
};
|
||||
|
||||
// This is necessary to work around the default transpilation behavior,
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
// (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.
|
||||
|
||||
/**
|
||||
* Allows retrieving and storing items in a secure storage.
|
||||
*/
|
||||
export class SecureStorage {
|
||||
|
||||
/**
|
||||
* Get one or more values.
|
||||
*
|
||||
* @param names Names of the values to get.
|
||||
* @param collection The collection where the values are stored.
|
||||
* @returns Object with name -> value. If a name isn't found it won't be included in the result.
|
||||
*/
|
||||
async get(names: string | string[], collection: string): Promise<Record<string, string>> {
|
||||
if (typeof names === 'string') {
|
||||
names = [names];
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
cordova.exec(resolve, reject, 'SecureStorage', 'get', [names, collection]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set one or more values.
|
||||
*
|
||||
* @param data Object with values to store, in format name -> value. Null or undefined valid values will be ignored.
|
||||
* @param collection The collection where to store the values.
|
||||
*/
|
||||
async store(data: Record<string, string>, collection: string): Promise<void> {
|
||||
for (const name in data) {
|
||||
const value = data[name];
|
||||
if (value === undefined || value === null) {
|
||||
delete data[name];
|
||||
} else if (typeof value !== 'string') {
|
||||
throw new Error(`SecureStorage: Invalid value for ${name}. Expected string, received ${typeof value}`);
|
||||
}
|
||||
}
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
cordova.exec(resolve, reject, 'SecureStorage', 'store', [data, collection]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete one or more values.
|
||||
*
|
||||
* @param names Names to delete.
|
||||
* @param collection The collection where to delete the values.
|
||||
*/
|
||||
async delete(names: string | string[], collection: string): Promise<void> {
|
||||
if (typeof names === 'string') {
|
||||
names = [names];
|
||||
}
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
cordova.exec(resolve, reject, 'SecureStorage', 'delete', [names, collection]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all values for a certain collection.
|
||||
*
|
||||
* @param collection The collection to delete.
|
||||
*/
|
||||
async deleteCollection(collection: string): Promise<void> {
|
||||
await new Promise((resolve, reject) => {
|
||||
cordova.exec(resolve, reject, 'SecureStorage', 'deleteCollection', [collection]);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
// (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.
|
||||
|
||||
/**
|
||||
* Manages system UI settings.
|
||||
*/
|
||||
export class SystemUI {
|
||||
|
||||
/**
|
||||
* Set navigation bar color.
|
||||
*
|
||||
* @param color Color.
|
||||
*/
|
||||
async setNavigationBarColor(color: string): Promise<void> {
|
||||
await new Promise((resolve, reject) => {
|
||||
cordova.exec(resolve, reject, 'SystemUI', 'setNavigationBarColor', [color]);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -12,12 +12,12 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { SystemUI } from '../src/ts/plugins/SystemUI';
|
||||
import { SecureStorage as SecureStorageImpl } from '../src/ts/plugins/SecureStorage';
|
||||
|
||||
declare global {
|
||||
|
||||
interface MoodleAppPlugins {
|
||||
systemUI: SystemUI;
|
||||
secureStorage: SecureStorageImpl;
|
||||
}
|
||||
|
||||
interface Cordova {
|
||||
|
@ -25,3 +25,5 @@ declare global {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
export type SecureStorage = InstanceType<typeof SecureStorageImpl>;
|
||||
|
|
|
@ -4835,9 +4835,9 @@
|
|||
}
|
||||
},
|
||||
"@moodlehq/phonegap-plugin-push": {
|
||||
"version": "4.0.0-moodle.6",
|
||||
"resolved": "https://registry.npmjs.org/@moodlehq/phonegap-plugin-push/-/phonegap-plugin-push-4.0.0-moodle.6.tgz",
|
||||
"integrity": "sha512-0ddoef5tXsCRfv8MKNfsUZdO+63GK+s8dYON8E8hFhO0RxEp1mhqeTj8vnxBvjyjl2IlVGUXiOPwshgabxoBRA=="
|
||||
"version": "4.0.0-moodle.7",
|
||||
"resolved": "https://registry.npmjs.org/@moodlehq/phonegap-plugin-push/-/phonegap-plugin-push-4.0.0-moodle.7.tgz",
|
||||
"integrity": "sha512-Suzk1v4oLogcU/Xpm5Yl1KkzPwamGnzxculkYNIrqaAs6IZ6cSyZvIy0JoM98zOjGJBjKCSEYAAmtMZXLMGhjg=="
|
||||
},
|
||||
"@mrmlnc/readdir-enhanced": {
|
||||
"version": "2.2.1",
|
||||
|
@ -8617,6 +8617,7 @@
|
|||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"normalize-path": "^3.0.0",
|
||||
"picomatch": "^2.0.4"
|
||||
|
@ -9800,7 +9801,8 @@
|
|||
"binary-extensions": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="
|
||||
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
|
||||
"dev": true
|
||||
},
|
||||
"bindings": {
|
||||
"version": "1.5.0",
|
||||
|
@ -10963,6 +10965,7 @@
|
|||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
|
@ -10978,6 +10981,7 @@
|
|||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
|
@ -11144,6 +11148,7 @@
|
|||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
|
||||
"integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"string-width": "^3.1.0",
|
||||
"strip-ansi": "^5.2.0",
|
||||
|
@ -11153,12 +11158,14 @@
|
|||
"ansi-regex": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
|
||||
"integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
|
||||
"integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
|
@ -11166,17 +11173,20 @@
|
|||
"emoji-regex": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
|
||||
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
|
||||
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
|
||||
"dev": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
||||
"integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w=="
|
||||
"integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
|
||||
"dev": true
|
||||
},
|
||||
"string-width": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
|
||||
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"emoji-regex": "^7.0.1",
|
||||
"is-fullwidth-code-point": "^2.0.0",
|
||||
|
@ -11187,6 +11197,7 @@
|
|||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^4.1.0"
|
||||
}
|
||||
|
@ -11195,6 +11206,7 @@
|
|||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
|
||||
"integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.0",
|
||||
"string-width": "^3.0.0",
|
||||
|
@ -12182,6 +12194,115 @@
|
|||
"version": "file:cordova-plugin-moodleapp",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": {
|
||||
"version": "7.22.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz",
|
||||
"integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.11"
|
||||
}
|
||||
},
|
||||
"@esbuild/darwin-x64": {
|
||||
"version": "0.18.11",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.11.tgz",
|
||||
"integrity": "sha512-N15Vzy0YNHu6cfyDOjiyfJlRJCB/ngKOAvoBf1qybG3eOq0SL2Lutzz9N7DYUbb7Q23XtHPn6lMDF6uWbGv9Fw==",
|
||||
"optional": true
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
|
||||
"integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
|
||||
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
|
||||
"requires": {
|
||||
"color-convert": "^1.9.0"
|
||||
}
|
||||
},
|
||||
"anymatch": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||
"requires": {
|
||||
"normalize-path": "^3.0.0",
|
||||
"picomatch": "^2.0.4"
|
||||
}
|
||||
},
|
||||
"binary-extensions": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="
|
||||
},
|
||||
"braces": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
|
||||
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
|
||||
"requires": {
|
||||
"fill-range": "^7.0.1"
|
||||
}
|
||||
},
|
||||
"camelcase": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"chokidar": {
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
|
||||
"requires": {
|
||||
"anymatch": "~3.1.2",
|
||||
"braces": "~3.0.2",
|
||||
"fsevents": "~2.3.2",
|
||||
"glob-parent": "~5.1.2",
|
||||
"is-binary-path": "~2.1.0",
|
||||
"is-glob": "~4.0.1",
|
||||
"normalize-path": "~3.0.0",
|
||||
"readdirp": "~3.6.0"
|
||||
}
|
||||
},
|
||||
"chokidar-cli": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar-cli/-/chokidar-cli-3.0.0.tgz",
|
||||
|
@ -12189,29 +12310,451 @@
|
|||
"requires": {
|
||||
"chokidar": "^3.5.2",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"yargs": "^13.3.0"
|
||||
}
|
||||
},
|
||||
"cliui": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
|
||||
"integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
|
||||
"requires": {
|
||||
"string-width": "^3.1.0",
|
||||
"strip-ansi": "^5.2.0",
|
||||
"wrap-ansi": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
|
||||
},
|
||||
"concurrently": {
|
||||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.0.tgz",
|
||||
"integrity": "sha512-nnLMxO2LU492mTUj9qX/az/lESonSZu81UznYDoXtz1IQf996ixVqPAgHXwvHiHCAef/7S8HIK+fTFK7Ifk8YA==",
|
||||
"requires": {
|
||||
"chalk": "^4.1.2",
|
||||
"date-fns": "^2.30.0",
|
||||
"lodash": "^4.17.21",
|
||||
"rxjs": "^7.8.1",
|
||||
"shell-quote": "^1.8.1",
|
||||
"spawn-command": "0.0.2",
|
||||
"tree-kill": "^1.2.2"
|
||||
"supports-color": "^8.1.1",
|
||||
"tree-kill": "^1.2.2",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"cliui": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||
"requires": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
|
||||
},
|
||||
"string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"requires": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"requires": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"y18n": {
|
||||
"version": "5.0.8",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
|
||||
},
|
||||
"yargs": {
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||
"requires": {
|
||||
"cliui": "^8.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
"string-width": "^4.2.3",
|
||||
"y18n": "^5.0.5",
|
||||
"yargs-parser": "^21.1.1"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "21.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
||||
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"date-fns": {
|
||||
"version": "2.30.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
|
||||
"integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.21.0"
|
||||
}
|
||||
},
|
||||
"decamelize": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
|
||||
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
|
||||
},
|
||||
"esbuild": {
|
||||
"version": "0.18.11",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.11.tgz",
|
||||
"integrity": "sha512-i8u6mQF0JKJUlGR3OdFLKldJQMMs8OqM9Cc3UCi9XXziJ9WERM5bfkHaEAy0YAvPRMgqSW55W7xYn84XtEFTtA=="
|
||||
"integrity": "sha512-i8u6mQF0JKJUlGR3OdFLKldJQMMs8OqM9Cc3UCi9XXziJ9WERM5bfkHaEAy0YAvPRMgqSW55W7xYn84XtEFTtA==",
|
||||
"requires": {
|
||||
"@esbuild/darwin-x64": "0.18.11"
|
||||
}
|
||||
},
|
||||
"escalade": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
|
||||
"integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
|
||||
},
|
||||
"fill-range": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
|
||||
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
|
||||
"requires": {
|
||||
"to-regex-range": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"find-up": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
|
||||
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
|
||||
"requires": {
|
||||
"locate-path": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"optional": true
|
||||
},
|
||||
"get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
|
||||
},
|
||||
"glob-parent": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||
"requires": {
|
||||
"is-glob": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
|
||||
},
|
||||
"is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||
"requires": {
|
||||
"binary-extensions": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"is-extglob": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
|
||||
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
||||
"integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w=="
|
||||
},
|
||||
"is-glob": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
|
||||
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
|
||||
"requires": {
|
||||
"is-extglob": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"is-number": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
|
||||
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
|
||||
},
|
||||
"locate-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
|
||||
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
|
||||
"requires": {
|
||||
"p-locate": "^3.0.0",
|
||||
"path-exists": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||
},
|
||||
"lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
|
||||
},
|
||||
"lodash.throttle": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
|
||||
"integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ=="
|
||||
},
|
||||
"normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
|
||||
},
|
||||
"p-limit": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||
"requires": {
|
||||
"p-try": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"p-locate": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
|
||||
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
|
||||
"requires": {
|
||||
"p-limit": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"p-try": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
|
||||
},
|
||||
"path-exists": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
|
||||
"integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ=="
|
||||
},
|
||||
"picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
|
||||
},
|
||||
"readdirp": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"requires": {
|
||||
"picomatch": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.11",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
|
||||
},
|
||||
"require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
|
||||
},
|
||||
"require-main-filename": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
|
||||
},
|
||||
"rxjs": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||
"requires": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"set-blocking": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
|
||||
},
|
||||
"shell-quote": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz",
|
||||
"integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA=="
|
||||
},
|
||||
"spawn-command": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz",
|
||||
"integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ=="
|
||||
},
|
||||
"string-width": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
|
||||
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
|
||||
"requires": {
|
||||
"emoji-regex": "^7.0.1",
|
||||
"is-fullwidth-code-point": "^2.0.0",
|
||||
"strip-ansi": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
|
||||
"requires": {
|
||||
"ansi-regex": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
|
||||
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
|
||||
"requires": {
|
||||
"is-number": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"tree-kill": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
|
||||
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="
|
||||
},
|
||||
"tslib": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz",
|
||||
"integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA=="
|
||||
},
|
||||
"typescript": {
|
||||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
|
||||
"integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA=="
|
||||
},
|
||||
"which-module": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
|
||||
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
|
||||
"integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
|
||||
"requires": {
|
||||
"ansi-styles": "^3.2.0",
|
||||
"string-width": "^3.0.0",
|
||||
"strip-ansi": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"y18n": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
|
||||
},
|
||||
"yargs": {
|
||||
"version": "13.3.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
|
||||
"integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
|
||||
"requires": {
|
||||
"cliui": "^5.0.0",
|
||||
"find-up": "^3.0.0",
|
||||
"get-caller-file": "^2.0.1",
|
||||
"require-directory": "^2.1.1",
|
||||
"require-main-filename": "^2.0.0",
|
||||
"set-blocking": "^2.0.0",
|
||||
"string-width": "^3.0.0",
|
||||
"which-module": "^2.0.0",
|
||||
"y18n": "^4.0.0",
|
||||
"yargs-parser": "^13.1.2"
|
||||
}
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
|
||||
"integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
|
||||
"requires": {
|
||||
"camelcase": "^5.0.0",
|
||||
"decamelize": "^1.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -13034,6 +13577,7 @@
|
|||
"version": "2.30.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
|
||||
"integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.21.0"
|
||||
}
|
||||
|
@ -13070,7 +13614,8 @@
|
|||
"decamelize": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="
|
||||
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
|
||||
"dev": true
|
||||
},
|
||||
"decimal.js": {
|
||||
"version": "10.4.3",
|
||||
|
@ -16013,7 +16558,8 @@
|
|||
"get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
|
||||
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||
"dev": true
|
||||
},
|
||||
"get-intrinsic": {
|
||||
"version": "1.2.1",
|
||||
|
@ -18091,6 +18637,7 @@
|
|||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"binary-extensions": "^2.0.0"
|
||||
}
|
||||
|
@ -21704,7 +22251,8 @@
|
|||
"normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
|
||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||
"dev": true
|
||||
},
|
||||
"normalize-range": {
|
||||
"version": "0.1.2",
|
||||
|
@ -24677,6 +25225,7 @@
|
|||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"picomatch": "^2.2.1"
|
||||
}
|
||||
|
@ -25031,7 +25580,8 @@
|
|||
"require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
|
||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||
"dev": true
|
||||
},
|
||||
"require-from-string": {
|
||||
"version": "2.0.2",
|
||||
|
@ -25041,7 +25591,8 @@
|
|||
"require-main-filename": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
|
||||
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
|
||||
"dev": true
|
||||
},
|
||||
"requires-port": {
|
||||
"version": "1.0.0",
|
||||
|
@ -26572,7 +27123,8 @@
|
|||
"spawn-command": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz",
|
||||
"integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ=="
|
||||
"integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==",
|
||||
"dev": true
|
||||
},
|
||||
"spdx-correct": {
|
||||
"version": "3.2.0",
|
||||
|
@ -27906,7 +28458,8 @@
|
|||
"tree-kill": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
|
||||
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="
|
||||
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
|
||||
"dev": true
|
||||
},
|
||||
"ts-dedent": {
|
||||
"version": "2.2.0",
|
||||
|
@ -30072,7 +30625,8 @@
|
|||
"which-module": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
|
||||
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="
|
||||
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
|
||||
"dev": true
|
||||
},
|
||||
"which-typed-array": {
|
||||
"version": "1.1.9",
|
||||
|
@ -30381,7 +30935,8 @@
|
|||
"y18n": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
|
||||
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
|
@ -30398,6 +30953,7 @@
|
|||
"version": "13.3.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
|
||||
"integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cliui": "^5.0.0",
|
||||
"find-up": "^3.0.0",
|
||||
|
@ -30414,22 +30970,26 @@
|
|||
"ansi-regex": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
|
||||
"integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
|
||||
"integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
|
||||
"dev": true
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
|
||||
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
|
||||
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
|
||||
"dev": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
|
||||
"integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w=="
|
||||
"integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==",
|
||||
"dev": true
|
||||
},
|
||||
"string-width": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
|
||||
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"emoji-regex": "^7.0.1",
|
||||
"is-fullwidth-code-point": "^2.0.0",
|
||||
|
@ -30440,6 +31000,7 @@
|
|||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
|
||||
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^4.1.0"
|
||||
}
|
||||
|
@ -30450,6 +31011,7 @@
|
|||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
|
||||
"integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "^5.0.0",
|
||||
"decamelize": "^1.2.0"
|
||||
|
@ -30458,7 +31020,8 @@
|
|||
"camelcase": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
|
||||
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -87,7 +87,7 @@
|
|||
"@moodlehq/cordova-plugin-statusbar": "4.0.0-moodle.2",
|
||||
"@moodlehq/cordova-plugin-zip": "3.1.0-moodle.1",
|
||||
"@moodlehq/ionic-native-push": "5.36.0-moodle.2",
|
||||
"@moodlehq/phonegap-plugin-push": "4.0.0-moodle.6",
|
||||
"@moodlehq/phonegap-plugin-push": "4.0.0-moodle.7",
|
||||
"@ngx-translate/core": "^13.0.0",
|
||||
"@ngx-translate/http-loader": "^6.0.0",
|
||||
"@types/chart.js": "^2.9.31",
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<data-extraction-rules>
|
||||
<cloud-backup disableIfNoEncryptionCapabilities="false">
|
||||
<exclude domain="sharedpref" path="."/>
|
||||
</cloud-backup>
|
||||
</data-extraction-rules>
|
|
@ -0,0 +1,134 @@
|
|||
// (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 { SecureStorage } from 'cordova-plugin-moodleapp';
|
||||
|
||||
/**
|
||||
* Mock for SecureStorage plugin. It will store the data without being encrypted.
|
||||
*/
|
||||
export class SecureStorageMock implements SecureStorage {
|
||||
|
||||
/**
|
||||
* Get one or more values.
|
||||
*
|
||||
* @param names Names of the values to get.
|
||||
* @param collection The collection where the values are stored.
|
||||
* @returns Object with name -> value. If a name isn't found it won't be included in the result.
|
||||
*/
|
||||
async get(names: string | string[], collection: string): Promise<Record<string, string>> {
|
||||
if (typeof names === 'string') {
|
||||
names = [names];
|
||||
}
|
||||
|
||||
const result: Record<string, string> = {};
|
||||
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
const name = names[i];
|
||||
if (!name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const storedValue = localStorage.getItem(this.getPrefixedName(name, collection));
|
||||
if (storedValue !== null) {
|
||||
result[name] = storedValue;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the prefix to add to a name, including the collection.
|
||||
*
|
||||
* @param collection Collection name.
|
||||
* @returns Prefix.
|
||||
*/
|
||||
private getCollectionPrefix(collection: string): string {
|
||||
return `SecureStorage_${collection}_`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the full name to retrieve, store or delete an item.
|
||||
*
|
||||
* @param name Name inside collection.
|
||||
* @param collection Collection name.
|
||||
* @returns Full name.
|
||||
*/
|
||||
private getPrefixedName(name: string, collection: string): string {
|
||||
return this.getCollectionPrefix(collection) + name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set one or more values.
|
||||
*
|
||||
* @param data Object with values to store, in format name -> value. Null or undefined valid values will be ignored.
|
||||
* @param collection The collection where to store the values.
|
||||
*/
|
||||
async store(data: Record<string, string>, collection: string): Promise<void> {
|
||||
for (const name in data) {
|
||||
const value = data[name];
|
||||
if (value === undefined || value === null) {
|
||||
delete data[name];
|
||||
} else if (typeof value !== 'string') {
|
||||
throw new Error(`SecureStorage: Invalid value for ${name}. Expected string, received ${typeof value}`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const name in data) {
|
||||
if (!name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const value = data[name];
|
||||
localStorage.setItem(this.getPrefixedName(name, collection), value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete one or more values.
|
||||
*
|
||||
* @param names Names to delete.
|
||||
* @param collection The collection where to delete the values.
|
||||
*/
|
||||
async delete(names: string | string[], collection: string): Promise<void> {
|
||||
if (typeof names === 'string') {
|
||||
names = [names];
|
||||
}
|
||||
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
const name = names[i];
|
||||
if (!name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
localStorage.removeItem(this.getPrefixedName(name, collection));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all values for a certain collection.
|
||||
*
|
||||
* @param collection The collection to delete.
|
||||
*/
|
||||
async deleteCollection(collection: string): Promise<void> {
|
||||
const names = Object.keys(localStorage);
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
const name = names[i];
|
||||
if (name.startsWith(this.getCollectionPrefix(collection))) {
|
||||
localStorage.removeItem(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -42,6 +42,8 @@ import { MediaCaptureMock } from './services/media-capture';
|
|||
import { ZipMock } from './services/zip';
|
||||
import { CorePlatform } from '@services/platform';
|
||||
import { CoreLocalNotifications } from '@services/local-notifications';
|
||||
import { CoreNative } from '@features/native/services/native';
|
||||
import { SecureStorageMock } from '@features/emulator/classes/SecureStorage';
|
||||
|
||||
/**
|
||||
* This module handles the emulation of Cordova plugins in browser and desktop.
|
||||
|
@ -101,12 +103,14 @@ import { CoreLocalNotifications } from '@services/local-notifications';
|
|||
},
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: () => () => {
|
||||
useValue: async () => {
|
||||
if (CorePlatform.is('cordova')) {
|
||||
return;
|
||||
}
|
||||
|
||||
return CoreEmulatorHelper.load();
|
||||
CoreNative.registerBrowserMock('secureStorage', new SecureStorageMock());
|
||||
|
||||
await CoreEmulatorHelper.load();
|
||||
},
|
||||
multi: true,
|
||||
},
|
||||
|
|
|
@ -24,6 +24,7 @@ import { AsyncInstance, asyncInstance } from '@/core/utils/async-instance';
|
|||
export class CoreNativeService {
|
||||
|
||||
private plugins: Partial<Record<keyof MoodleAppPlugins, AsyncInstance>> = {};
|
||||
private mocks: Partial<Record<keyof MoodleAppPlugins, MoodleAppPlugins[keyof MoodleAppPlugins]>> = {};
|
||||
|
||||
/**
|
||||
* Get a native plugin instance.
|
||||
|
@ -31,22 +32,33 @@ export class CoreNativeService {
|
|||
* @param plugin Plugin name.
|
||||
* @returns Plugin instance.
|
||||
*/
|
||||
plugin<Plugin extends keyof MoodleAppPlugins>(plugin: Plugin): AsyncInstance<MoodleAppPlugins[Plugin]> | null {
|
||||
if (!CorePlatform.isAndroid()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
plugin<Plugin extends keyof MoodleAppPlugins>(plugin: Plugin): AsyncInstance<MoodleAppPlugins[Plugin]> {
|
||||
if (!(plugin in this.plugins)) {
|
||||
this.plugins[plugin] = asyncInstance(async () => {
|
||||
await CorePlatform.ready();
|
||||
|
||||
return window.cordova?.MoodleApp?.[plugin];
|
||||
const instance = CorePlatform.isMobile() ? window.cordova?.MoodleApp?.[plugin] : this.mocks[plugin];
|
||||
if (!instance) {
|
||||
throw new Error(`Plugin ${plugin} not found.`);
|
||||
}
|
||||
|
||||
return instance;
|
||||
});
|
||||
}
|
||||
|
||||
return this.plugins[plugin] as AsyncInstance<MoodleAppPlugins[Plugin]>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a mock to use in browser instead of the native plugin implementation.
|
||||
*
|
||||
* @param plugin Plugin name.
|
||||
* @param instance Instance to use.
|
||||
*/
|
||||
registerBrowserMock<Plugin extends keyof MoodleAppPlugins>(plugin: Plugin, instance: MoodleAppPlugins[Plugin]): void {
|
||||
this.mocks[plugin] = instance;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const CoreNative = makeSingleton(CoreNativeService);
|
||||
|
|
|
@ -63,6 +63,7 @@ import { CoreConfig } from './config';
|
|||
import { CoreNetwork } from '@services/network';
|
||||
import { CoreUserGuestSupportConfig } from '@features/user/classes/support/guest-support-config';
|
||||
import { CoreLang, CoreLangFormat } from '@services/lang';
|
||||
import { CoreNative } from '@features/native/services/native';
|
||||
|
||||
export const CORE_SITE_SCHEMAS = new InjectionToken<CoreSiteSchema[]>('CORE_SITE_SCHEMAS');
|
||||
export const CORE_SITE_CURRENT_SITE_ID_CONFIG = 'current_site_id';
|
||||
|
@ -821,16 +822,22 @@ export class CoreSitesProvider {
|
|||
config?: CoreSiteConfig,
|
||||
oauthId?: number,
|
||||
): Promise<void> {
|
||||
await this.sitesTable.insert({
|
||||
const promises: Promise<unknown>[] = [];
|
||||
const site: SiteDBEntry = {
|
||||
id,
|
||||
siteUrl,
|
||||
token,
|
||||
token: '',
|
||||
info: info ? JSON.stringify(info) : undefined,
|
||||
privateToken,
|
||||
privateToken: '',
|
||||
config: config ? JSON.stringify(config) : undefined,
|
||||
loggedOut: 0,
|
||||
oauthId,
|
||||
});
|
||||
};
|
||||
|
||||
promises.push(this.sitesTable.insert(site));
|
||||
promises.push(this.storeTokensInSecureStorage(id, token, privateToken));
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1058,15 +1065,14 @@ export class CoreSitesProvider {
|
|||
// Site DB deleted, now delete the app from the list of sites.
|
||||
delete this.sites[siteId];
|
||||
|
||||
try {
|
||||
await this.sitesTable.deleteByPrimaryKey({ id: siteId });
|
||||
} catch (err) {
|
||||
// DB remove shouldn't fail, but we'll go ahead even if it does.
|
||||
}
|
||||
// DB remove shouldn't fail, but we'll go ahead even if it does.
|
||||
await CoreUtils.ignoreErrors(this.sitesTable.deleteByPrimaryKey({ id: siteId }));
|
||||
|
||||
// Site deleted from sites list, now delete the folder.
|
||||
await site.deleteFolder();
|
||||
|
||||
await CoreUtils.ignoreErrors(CoreNative.plugin('secureStorage').deleteCollection(siteId));
|
||||
|
||||
CoreEvents.trigger(CoreEvents.SITE_DELETED, site, siteId);
|
||||
}
|
||||
|
||||
|
@ -1107,7 +1113,7 @@ export class CoreSitesProvider {
|
|||
// Retrieve and create the site.
|
||||
let record: SiteDBEntry;
|
||||
try {
|
||||
record = await this.sitesTable.getOneByPrimaryKey({ id: siteId });
|
||||
record = await this.loadSiteTokens(await this.sitesTable.getOneByPrimaryKey({ id: siteId }));
|
||||
} catch {
|
||||
throw new CoreError('SiteId not found.');
|
||||
}
|
||||
|
@ -1144,7 +1150,7 @@ export class CoreSitesProvider {
|
|||
* @returns Promise resolved with the site.
|
||||
*/
|
||||
async getSiteByUrl(siteUrl: string): Promise<CoreSite> {
|
||||
const data = await this.sitesTable.getOne({ siteUrl });
|
||||
const data = await this.loadSiteTokens(await this.sitesTable.getOne({ siteUrl }));
|
||||
|
||||
return this.addSiteFromSiteListEntry(data);
|
||||
}
|
||||
|
@ -1491,14 +1497,17 @@ export class CoreSitesProvider {
|
|||
site.privateToken = privateToken;
|
||||
site.setLoggedOut(false); // Token updated means the user authenticated again, not logged out anymore.
|
||||
|
||||
await this.sitesTable.update(
|
||||
{
|
||||
token,
|
||||
privateToken,
|
||||
loggedOut: 0,
|
||||
},
|
||||
{ id: siteId },
|
||||
);
|
||||
const promises: Promise<unknown>[] = [];
|
||||
const newData: Partial<SiteDBEntry> = {
|
||||
token: '',
|
||||
privateToken: '',
|
||||
loggedOut: 0,
|
||||
};
|
||||
|
||||
promises.push(this.sitesTable.update(newData, { id: siteId }));
|
||||
promises.push(this.storeTokensInSecureStorage(siteId, token, privateToken));
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1601,6 +1610,8 @@ export class CoreSitesProvider {
|
|||
const ids: string[] = [];
|
||||
|
||||
await Promise.all(siteEntries.map(async (site) => {
|
||||
site = await this.loadSiteTokens(site);
|
||||
|
||||
await this.addSiteFromSiteListEntry(site);
|
||||
|
||||
if (this.sites[site.id].containsUrl(url)) {
|
||||
|
@ -1962,6 +1973,80 @@ export class CoreSitesProvider {
|
|||
return this.schemasTables[siteId];
|
||||
}
|
||||
|
||||
/**
|
||||
* Move all tokens stored in DB to a secure storage.
|
||||
*/
|
||||
async moveTokensToSecureStorage(): Promise<void> {
|
||||
const sites = await this.sitesTable.getMany();
|
||||
|
||||
await Promise.all(sites.map(async site => {
|
||||
if (!site.token && !site.privateToken) {
|
||||
return; // Tokens are empty, no need to treat them.
|
||||
}
|
||||
|
||||
try {
|
||||
await this.storeTokensInSecureStorage(site.id, site.token, site.privateToken);
|
||||
} catch {
|
||||
this.logger.error('Error storing tokens in secure storage for site ' + site.id);
|
||||
}
|
||||
}));
|
||||
|
||||
// Remove tokens from DB even if they couldn't be stored in secure storage.
|
||||
await this.sitesTable.update({ token: '', privateToken: '' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tokens from secure storage.
|
||||
*
|
||||
* @param siteId Site ID.
|
||||
* @returns Stored tokens.
|
||||
*/
|
||||
protected async getTokensFromSecureStorage(siteId: string): Promise<{ token: string; privateToken?: string }> {
|
||||
const result = await CoreNative.plugin('secureStorage').get(['token', 'privateToken'], siteId);
|
||||
|
||||
return {
|
||||
token: result?.token ?? '',
|
||||
privateToken: result?.privateToken ?? undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Store tokens in secure storage.
|
||||
*
|
||||
* @param siteId Site ID.
|
||||
* @param token Site token.
|
||||
* @param privateToken Site private token.
|
||||
*/
|
||||
protected async storeTokensInSecureStorage(
|
||||
siteId: string,
|
||||
token: string,
|
||||
privateToken?: string,
|
||||
): Promise<void> {
|
||||
await CoreNative.plugin('secureStorage').store({
|
||||
token: token,
|
||||
privateToken: privateToken ?? '',
|
||||
}, siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a site, load its tokens if needed.
|
||||
*
|
||||
* @param site Site data.
|
||||
* @returns Site with tokens loaded.
|
||||
*/
|
||||
protected async loadSiteTokens(site: SiteDBEntry): Promise<SiteDBEntry> {
|
||||
if (site.token) {
|
||||
return site;
|
||||
}
|
||||
|
||||
const tokens = await this.getTokensFromSecureStorage(site.id);
|
||||
|
||||
return {
|
||||
...site,
|
||||
...tokens,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const CoreSites = makeSingleton(CoreSitesProvider);
|
||||
|
|
|
@ -89,6 +89,10 @@ export class CoreUpdateManagerProvider {
|
|||
promises.push(this.upgradeFontSizeNames());
|
||||
}
|
||||
|
||||
if (versionCode >= 43000 && versionApplied < 43000 && versionApplied > 0) {
|
||||
promises.push(CoreSites.moveTokensToSecureStorage());
|
||||
}
|
||||
|
||||
try {
|
||||
await Promise.all(promises);
|
||||
|
||||
|
|
Loading…
Reference in New Issue