Skip to content

Commit

Permalink
chore(core): Split storage interfaces (#62)
Browse files Browse the repository at this point in the history
Create two variations of a new `Storage` interface: `LocalStorage` and
`SecureStorage`. This is necessary in Auth to properly store
information, since platform keychains have different persistence
characteristics (e.g. iOS/macOS persist after uninstall), and some
platforms cannot support secure storage (e.g. Web).
  • Loading branch information
dnys1 authored Mar 7, 2024
1 parent eeae84b commit ce97ec3
Show file tree
Hide file tree
Showing 22 changed files with 151 additions and 69 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/celest_core.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ jobs:
echo "Booted simulator"
- name: Test (iOS)
working-directory: packages/celest_core/example
run: flutter test -d ios integration_test/secure_storage_test.dart
run: flutter test -d ios integration_test/storage_test.dart
- name: Test (macOS)
working-directory: packages/celest_core/example
run: flutter test -d macos integration_test/secure_storage_test.dart
run: flutter test -d macos integration_test/storage_test.dart
test_android:
needs: analyze_and_format
runs-on:
Expand Down Expand Up @@ -93,7 +93,7 @@ jobs:
# https://github.com/dart-lang/native/blob/001910c9f40d637cb25c19bb500fb89cebdf7450/pkgs/jni/android/build.gradle#L57C23-L57C25
api-level: 31
arch: x86_64
script: cd packages/celest_core/example && flutter test -d emulator integration_test/secure_storage_test.dart
script: cd packages/celest_core/example && flutter test -d emulator integration_test/storage_test.dart
test_linux:
needs: analyze_and_format
runs-on: ubuntu-latest
Expand Down Expand Up @@ -125,7 +125,7 @@ jobs:
# Headless tests require virtual display for the linux tests to run.
export DISPLAY=:99
sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 &
flutter test -d linux integration_test/secure_storage_test.dart
flutter test -d linux integration_test/storage_test.dart
# TODO: Re-enable
# Need to fix this: Git error. Command: `git clone --mirror https://github.com/dart-lang/native /c/Users/runneradmin/.pub-cache\git\cache\native-647c69ed8027da6d6def6bc40efa87cf1a2f76aa`
# test_windows:
Expand All @@ -150,7 +150,7 @@ jobs:
# run: flutter pub get
# - name: Test (Windows)
# working-directory: packages/celest_core/example
# run: flutter test -d windows integration_test/secure_storage_test.dart
# run: flutter test -d windows integration_test/storage_test.dart
test_web:
needs: analyze_and_format
runs-on: ubuntu-latest
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import 'dart:math';

import 'package:celest_core/src/secure_storage/secure_storage.dart';
import 'package:celest_core/src/storage/storage.dart';
import 'package:test/test.dart';

void sharedTests() {
void sharedTests(String name, Storage Function({String? scope}) factory) {
group('SecureStorage', () {
late String key;
final storage = SecureStorage(scope: 'test');
final storage = factory(scope: 'test');

setUp(() {
storage.clear();
Expand Down
11 changes: 11 additions & 0 deletions packages/celest_core/example/integration_test/storage_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import 'package:celest_core/src/storage/local/local_storage.dart';
import 'package:celest_core/src/storage/secure/secure_storage.dart';
import 'package:integration_test/integration_test.dart';

import 'storage_shared.dart';

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
sharedTests('SecureStorage', SecureStorage.new);
sharedTests('LocalStorage', LocalStorage.new);
}
33 changes: 0 additions & 33 deletions packages/celest_core/lib/src/secure_storage/secure_storage.dart

This file was deleted.

7 changes: 7 additions & 0 deletions packages/celest_core/lib/src/storage/local/local_storage.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import 'package:celest_core/src/storage/local/local_storage_platform.vm.dart'
if (dart.library.js_interop) 'package:celest_core/src/storage/local/local_storage_platform.web.dart';
import 'package:celest_core/src/storage/storage.dart';

abstract interface class LocalStorage implements Storage {
factory LocalStorage({String? scope}) = LocalStoragePlatform;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import 'package:celest_core/src/storage/local/local_storage.dart';
import 'package:celest_core/src/storage/secure/secure_storage_platform.vm.dart';

extension type LocalStoragePlatform._(LocalStorage _impl)
implements LocalStorage {
LocalStoragePlatform({String? scope})
: _impl = SecureStoragePlatform(scope: scope ?? _defaultScope);

static const _defaultScope = 'dev.celest.celest';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import 'package:celest_core/src/storage/local/local_storage.dart';
import 'package:web/web.dart' as web;

final class LocalStoragePlatform implements LocalStorage {
LocalStoragePlatform({String? scope}) : scope = scope ?? _defaultScope;

static const _defaultScope = 'dev.celest.celest';

final String scope;
final web.Storage _storage = web.window.localStorage;

@override
void clear() {
for (final key in _storage.keys) {
if (key.startsWith('$scope/')) {
_storage.removeItem(key);
}
}
}

@override
String? delete(String key) {
final value = read(key);
if (value != null) {
_storage.removeItem('$scope/$key');
}
return null;
}

@override
String? read(String key) => _storage['$scope/$key'];

@override
String write(String key, String value) {
_storage.setItem('$scope/$key', value);
return value;
}
}

extension on web.Storage {
List<String> get keys => [for (var i = 0; i < length; i++) key(i)!];
}
24 changes: 24 additions & 0 deletions packages/celest_core/lib/src/storage/memory_storage.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import 'package:celest_core/src/storage/secure/secure_storage.dart';
import 'package:celest_core/src/storage/storage.dart';

/// An in-memory implementation of [Storage] and [SecureStorage].
final class MemoryStorage implements Storage, SecureStorage {
MemoryStorage({
required this.scope,
});

final _storage = <String, String>{};
final String scope;

@override
void clear() => _storage.removeWhere((key, _) => key.startsWith('$scope/'));

@override
String? delete(String key) => _storage.remove('$scope/$key');

@override
String? read(String key) => _storage['$scope/$key'];

@override
String write(String key, String value) => _storage['$scope/$key'] = value;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:celest_core/src/native/android/jni_bindings.ffi.dart';
import 'package:celest_core/src/secure_storage/secure_storage_platform.vm.dart';
import 'package:celest_core/src/storage/secure/secure_storage_platform.vm.dart';
import 'package:jni/jni.dart';

final class SecureStoragePlatformAndroid extends SecureStoragePlatform {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import 'package:celest_core/src/storage/secure/secure_storage_platform.vm.dart'
if (dart.library.js_interop) 'package:celest_core/src/storage/secure/secure_storage_platform.web.dart';
import 'package:celest_core/src/storage/storage.dart';

abstract interface class SecureStorage implements Storage {
factory SecureStorage({String? scope}) = SecureStoragePlatform;
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import 'dart:io';
import 'package:celest_core/src/native/darwin/core_foundation.ffi.dart';
import 'package:celest_core/src/native/darwin/darwin_ffi_helpers.dart';
import 'package:celest_core/src/native/darwin/security.ffi.dart';
import 'package:celest_core/src/secure_storage/secure_storage_exception.dart';
import 'package:celest_core/src/secure_storage/secure_storage_platform.vm.dart';
import 'package:celest_core/src/storage/secure/secure_storage_exception.dart';
import 'package:celest_core/src/storage/secure/secure_storage_platform.vm.dart';
import 'package:celest_core/src/util/globals.dart';
import 'package:ffi/ffi.dart';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import 'dart:io';

import 'package:celest_core/src/native/linux/glib.ffi.dart';
import 'package:celest_core/src/native/linux/libsecret.ffi.dart';
import 'package:celest_core/src/secure_storage/secure_storage_exception.dart';
import 'package:celest_core/src/secure_storage/secure_storage_platform.vm.dart';
import 'package:celest_core/src/storage/secure/secure_storage_exception.dart';
import 'package:celest_core/src/storage/secure/secure_storage_platform.vm.dart';
import 'package:ffi/ffi.dart';

final class SecureStoragePlatformLinux extends SecureStoragePlatform {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import 'dart:io';
import 'dart:typed_data';

import 'package:celest_core/src/native/windows/windows_paths.dart';
import 'package:celest_core/src/secure_storage/secure_storage_exception.dart';
import 'package:celest_core/src/secure_storage/secure_storage_platform.vm.dart';
import 'package:path/path.dart' as p;
import 'package:celest_core/src/storage/secure/secure_storage_exception.dart';
import 'package:celest_core/src/storage/secure/secure_storage_platform.vm.dart';
import 'package:ffi/ffi.dart';
import 'package:path/path.dart' as p;
import 'package:win32/win32.dart';

final class SecureStoragePlatformWindows extends SecureStoragePlatform {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import 'dart:io';

import 'package:celest_core/src/secure_storage/secure_storage.android.dart';
import 'package:celest_core/src/secure_storage/secure_storage.dart';
import 'package:celest_core/src/secure_storage/secure_storage.darwin.dart';
import 'package:celest_core/src/secure_storage/secure_storage.linux.dart';
import 'package:celest_core/src/secure_storage/secure_storage.windows.dart';
import 'package:celest_core/src/storage/local/local_storage.dart';
import 'package:celest_core/src/storage/secure/secure_storage.android.dart';
import 'package:celest_core/src/storage/secure/secure_storage.dart';
import 'package:celest_core/src/storage/secure/secure_storage.darwin.dart';
import 'package:celest_core/src/storage/secure/secure_storage.linux.dart';
import 'package:celest_core/src/storage/secure/secure_storage.windows.dart';
import 'package:meta/meta.dart';

abstract base class SecureStoragePlatform implements SecureStorage {
abstract base class SecureStoragePlatform
implements SecureStorage, LocalStorage {
factory SecureStoragePlatform({
String? scope,
}) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import 'package:celest_core/src/secure_storage/secure_storage.dart';
import 'package:celest_core/src/storage/memory_storage.dart';
import 'package:celest_core/src/storage/secure/secure_storage.dart';

extension type SecureStoragePlatform._(SecureStorage _impl)
implements SecureStorage {
SecureStoragePlatform({String? scope})
: _impl = MemorySecureStorage(scope: scope ?? _defaultScope);
: _impl = MemoryStorage(scope: scope ?? _defaultScope);

static const _defaultScope = 'dev.celest.celest';
}
12 changes: 12 additions & 0 deletions packages/celest_core/lib/src/storage/storage.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import 'package:celest_core/src/storage/local/local_storage.dart';
import 'package:celest_core/src/storage/secure/secure_storage.dart';

abstract interface class Storage {
factory Storage.local({String? scope}) = LocalStorage;
factory Storage.secure({String? scope}) = SecureStorage;

String? read(String key);
String write(String key, String value);
String? delete(String key);
void clear();
}
1 change: 1 addition & 0 deletions packages/celest_core/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ dependencies:
meta: ^1.10.0
os_detect: ^2.0.1
path: ^1.9.0
web: ^0.5.1
win32: ^5.2.0

dev_dependencies:
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@TestOn('mac-os')

import 'package:celest_core/src/native/darwin/security.ffi.dart';
import 'package:celest_core/src/secure_storage/secure_storage.darwin.dart';
import 'package:celest_core/src/storage/secure/secure_storage.darwin.dart';
import 'package:test/test.dart';

void main() {
Expand Down
9 changes: 9 additions & 0 deletions packages/celest_core/test/storage/storage_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'package:celest_core/src/storage/local/local_storage.dart';
import 'package:celest_core/src/storage/secure/secure_storage.dart';

import '../../example/integration_test/storage_shared.dart';

void main() {
sharedTests('SecureStorage', SecureStorage.new);
sharedTests('LocalStorage', LocalStorage.new);
}

0 comments on commit ce97ec3

Please sign in to comment.