Skip to content
This repository has been archived by the owner on Nov 7, 2023. It is now read-only.

Commit

Permalink
Merge pull request #5 from emeraldsanto/dev
Browse files Browse the repository at this point in the history
Version 2.0.0
  • Loading branch information
emeraldsanto authored Feb 15, 2020
2 parents 112ef9e + ee5f658 commit a2da117
Show file tree
Hide file tree
Showing 12 changed files with 4,708 additions and 59 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,6 @@ buck-out/
\.buckd/
*.keystore
.vscode/settings.json

# JEST
coverage/
39 changes: 20 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,16 @@ import EncryptedStorage from 'react-native-encrypted-storage';

```js
const storeUserSession = async () => {
const success = await EncryptedStorage.store('user_session', {
username : 'emeraldsanto',
age : 21,
languages : ['fr', 'en', 'de'],
token : 'ACCESS_TOKEN'
});

if (success === true) {
try {
await EncryptedStorage.setItem('user_session', {
username : 'emeraldsanto',
age : 21,
languages : ['fr', 'en', 'de'],
token : 'ACCESS_TOKEN'
});

// Congrats! You've just stored your first value!
}

else {
} catch (error) {
// There was an error on the native side
}
}
Expand All @@ -72,10 +70,14 @@ const storeUserSession = async () => {

```js
const retrieveUserSession = async () => {
const session = await EncryptedStorage.retrieve("user_session");
try {
const session = await EncryptedStorage.getItem("user_session");

if (session !== undefined) {
// Congrats! You've just retrieved your first value!
if (session !== null) {
// Congrats! You've just retrieved your first value!
}
} catch (error) {
// There was an error on the native side
}
}
```
Expand All @@ -84,12 +86,11 @@ const retrieveUserSession = async () => {

```js
const removeUserSession = async () => {
const success = await EncryptedStorage.remove('user_session');

if (success === true) {
try {
await EncryptedStorage.removeItem('user_session');
// Congrats! You've just removed your first value!
const previousValue = await EncryptedStorage.retrieve('user_session');
console.log(previousValue); // undefined
} catch (error) {
// There was an error on the native side
}
}
```
Expand Down
3 changes: 2 additions & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import groovy.json.JsonSlurper

def DEFAULT_COMPILE_SDK_VERSION = 28
def DEFAULT_BUILD_TOOLS_VERSION = '28.0.3'
def DEFAULT_MIN_SDK_VERSION = 16
def DEFAULT_MIN_SDK_VERSION = 23
def DEFAULT_TARGET_SDK_VERSION = 28

def safeExtGet(prop, fallback) {
Expand Down Expand Up @@ -86,6 +86,7 @@ dependencies {
//noinspection GradleDynamicVersion
implementation 'com.facebook.react:react-native:+'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" // From node_modules
implementation "androidx.security:security-crypto:1.0.0-alpha02"
}

def configureReactNativePom(def pom) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.emeraldsanto.encryptedstorage

import android.content.Context
import android.content.SharedPreferences
import android.content.res.Resources
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKeys
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
Expand All @@ -18,15 +19,21 @@ class RNEncryptedStorageModule(context : ReactApplicationContext) : ReactContext
private val sharedPreferences : SharedPreferences;

init {
this.sharedPreferences = context.getSharedPreferences(RNEncryptedStorageModule.SHARED_PREFERENCES_FILENAME, Context.MODE_PRIVATE)
this.sharedPreferences = EncryptedSharedPreferences.create(
RNEncryptedStorageModule.SHARED_PREFERENCES_FILENAME,
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);
}

override fun getName(): String {
return RNEncryptedStorageModule.NATIVE_MODULE_NAME
}

@ReactMethod
fun store(key : String, value : String, promise : Promise) {
fun setItem(key : String, value : String, promise : Promise) {
val editor = this.sharedPreferences.edit();
editor.putString(key, value);
val saved = editor.commit();
Expand All @@ -41,7 +48,7 @@ class RNEncryptedStorageModule(context : ReactApplicationContext) : ReactContext
}

@ReactMethod
fun retrieve(key : String, promise : Promise) {
fun getItem(key : String, promise : Promise) {
val value = this.sharedPreferences.getString(key, "");

if (value.isNullOrEmpty()) {
Expand All @@ -54,7 +61,7 @@ class RNEncryptedStorageModule(context : ReactApplicationContext) : ReactContext
}

@ReactMethod
fun remove(key : String, promise : Promise) {
fun removeItem(key : String, promise : Promise) {
val editor = this.sharedPreferences.edit();
editor.remove(key);
val saved = editor.commit();
Expand Down
6 changes: 3 additions & 3 deletions ios/RNEncryptedStorage.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,20 @@
@interface RCT_EXTERN_MODULE(RNEncryptedStorage, NSObject)

RCT_EXTERN_METHOD(
store: (NSString *)key
setItem: (NSString *)key
value: (NSString *)value
resolver: (RCTPromiseResolveBlock)resolve
rejecter: (RCTPromiseRejectBlock)reject
);

RCT_EXTERN_METHOD(
retrieve: (NSString *)key
getItem: (NSString *)key
resolver: (RCTPromiseResolveBlock)resolve
rejecter: (RCTPromiseRejectBlock)reject
);

RCT_EXTERN_METHOD(
remove: (NSString *)key
removeItem: (NSString *)key
resolver: (RCTPromiseResolveBlock)resolve
rejecter: (RCTPromiseRejectBlock)reject
);
Expand Down
6 changes: 3 additions & 3 deletions ios/RNEncryptedStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class RNEncryptedStorage: NSObject {
return false;
}

@objc func store(_ key : String, value : String, resolver resolve : RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) -> Void {
@objc func setItem(_ key : String, value : String, resolver resolve : RCTPromiseResolveBlock, rejecter reject: RCTPromiseRejectBlock) -> Void {
guard let dataFromValue = value.data(using: .utf8, allowLossyConversion: false) else {
return reject("Error parsing value for key \(key)", nil, nil);
}
Expand All @@ -44,7 +44,7 @@ class RNEncryptedStorage: NSObject {
}

@objc
func retrieve(_ key : String, resolver resolve : RCTPromiseResolveBlock, rejecter reject : RCTPromiseRejectBlock) {
func getItem(_ key : String, resolver resolve : RCTPromiseResolveBlock, rejecter reject : RCTPromiseRejectBlock) {
let retrieveQuery : [String : Any] = [
kSecClass as String : kSecClassGenericPassword,
kSecAttrAccount as String : key,
Expand All @@ -67,7 +67,7 @@ class RNEncryptedStorage: NSObject {
}

@objc
func remove(_ key : String, resolver resolve : RCTPromiseResolveBlock, rejecter reject : RCTPromiseRejectBlock) {
func removeItem(_ key : String, resolver resolve : RCTPromiseResolveBlock, rejecter reject : RCTPromiseRejectBlock) {
let removeQuery : [String : Any] = [
kSecClass as String : kSecClassGenericPassword,
kSecAttrAccount as String : key,
Expand Down
55 changes: 33 additions & 22 deletions lib/EncryptedStorage.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,40 +11,51 @@ const EncryptedStorage = {
* @param {string} key - A string that will be associated to the value for later retrieval.
* @param {Object} value - The data to store.
*/
store : async function(key, value) {
try {
await RNEncryptedStorage.store(key, JSON.stringify(value));
return true;
} catch (error) {
return false;
}
setItem: async function (key, value, cb) {
return new Promise(async (resolve, reject) => {
try {
await RNEncryptedStorage.setItem(key, value);
cb && cb(null);
resolve(null);
} catch (error) {
cb && cb(error);
reject(error);
}
});
},

/**
* Retrieves data from the disk, using SharedPreferences or KeyChain, depending on the platform and returns it as the specified type.
* @param {string} key - A string that is associated to a value.
*/
retrieve : async function(key) {
try {
const savedValue = await RNEncryptedStorage.retrieve(key);
return JSON.parse(savedValue);
} catch (error) {
return undefined;
}
getItem: async function (key, cb) {
return new Promise(async (resolve, reject) => {
try {
const value = await RNEncryptedStorage.getItem(key);
cb && cb(null, value);
resolve(value);
} catch (error) {
cb && cb(error, null);
reject(error);
}
});
},

/**
* Deletes data from the disk, using SharedPreferences or KeyChain, depending on the platform.
* @param {string} key - A string that is associated to a value.
*/
remove : async function(key) {
try {
await RNEncryptedStorage.remove(key);
return true;
} catch (error) {
return false;
}
removeItem: async function (key, cb) {
return new Promise(async (resolve, reject) => {
try {
await RNEncryptedStorage.removeItem(key);
cb && cb(null);
resolve(null);
} catch (error) {
reject(error);
}
});
}
}

export default EncryptedStorage;
module.exports = EncryptedStorage;
130 changes: 130 additions & 0 deletions lib/EncryptedStorage.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
const EncryptedStorage = require('./EncryptedStorage');
const { NativeModules } = require('react-native');
const { RNEncryptedStorage } = NativeModules;

describe('lib/EncryptedStorage', () => {
afterEach(() => {
jest.clearAllMocks();
});

describe('using Promises', () => {
describe('setItem(key, value)', () => {
it('should return no errors if it could store the value', async () => {
const storeError = await EncryptedStorage.setItem('key', 'value');
expect(storeError).toBe(null);
});

it('should reject with an error if it could not store the value', async () => {
RNEncryptedStorage.setItem.mockImplementationOnce(() => Promise.reject(new Error('Set error')));

try {
await EncryptedStorage.setItem('key', 'value');
} catch (error) {
expect(error.message).toBe('Set error');
}
});
});

describe('getItem(key)', () => {
it('should return the value if it could be retrieved succesfully', async () => {
const item = await EncryptedStorage.getItem('key');
expect(item).toEqual('{ "foo": 1 }');
});

it('should return null if no value was found for that key', async () => {
RNEncryptedStorage.getItem.mockImplementationOnce(() => Promise.resolve(undefined));

const item = await EncryptedStorage.getItem('key');
expect(item).toEqual(undefined);
});

it('should reject with an error if it could not retrieve the value', async () => {
RNEncryptedStorage.getItem.mockImplementationOnce(() => Promise.reject(new Error("Get error")));

try {
await EncryptedStorage.getItem('key');
} catch (error) {
expect(error.message).toBe('Get error');
}
});
});

describe('removeItem(key)', () => {
it('should return no error if it could removed the stored value', async () => {
const removeResult = await EncryptedStorage.removeItem('key');
expect(removeResult).toBe(null);
});

it('should throw an error if it could not retrieve the stored value', async () => {
RNEncryptedStorage.removeItem.mockImplementationOnce(() => Promise.reject(new Error("Remove error")));

try {
await EncryptedStorage.removeItem('key');
} catch (error) {
expect(error.message).toBe('Remove error');
}
});
});
});

describe('using callbacks', () => {
describe('setItem(key, value)', () => {
it('should return no errors if it could store the value', () => {
EncryptedStorage.setItem('key', 'value', err => {
expect(err).toBe(null);
});
});

it('should reject with an error if it could not store the value', () => {
RNEncryptedStorage.setItem.mockImplementationOnce(() => Promise.reject(new Error('Set error')));

EncryptedStorage.setItem('key', 'value', err => {
expect(err.message).toBe('Set error');
});
});
});

describe('getItem(key)', () => {
it('should return the value if it could be retrieved succesfully', () => {
EncryptedStorage.getItem('key', (err, value) => {
expect(err).toBe(null);
expect(value).toEqual('{ "foo": 1 }');
});
});

it('should return null if no value was found for that key', () => {
RNEncryptedStorage.getItem.mockImplementationOnce(() => Promise.resolve(undefined));

EncryptedStorage.getItem('key', (err, value) => {
expect(err).toBe(null);
expect(value).toBe(undefined);
});
});

it('should reject with an error if it could not retrieve the value', () => {
RNEncryptedStorage.getItem.mockImplementationOnce(() => Promise.reject(new Error("Get error")));

EncryptedStorage.getItem('key', (err, value) => {
expect(err.message).toEqual('Get error');
expect(value).toBe(undefined);
});
});
});

describe('removeItem(key)', () => {
it('should return no error if it could removed the stored value', () => {
EncryptedStorage.removeItem('key', err => {
expect(err).toBe(null);
});
});

it('should throw an error if it could not retrieve the stored value', () => {
RNEncryptedStorage.removeItem.mockImplementationOnce(() => Promise.reject(new Error("Remove error")));

EncryptedStorage.removeItem('key', err => {
expect(err.message).toBe('Remove error');
});
});
});
});
});
Loading

0 comments on commit a2da117

Please sign in to comment.