diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig index 592ceee..e8efba1 100644 --- a/ios/Flutter/Debug.xcconfig +++ b/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig index 592ceee..399e934 100644 --- a/ios/Flutter/Release.xcconfig +++ b/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 0000000..d077b08 --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,69 @@ +# Uncomment this line to define a global platform for your project +# platform :ios, '9.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def parse_KV_file(file, separator='=') + file_abs_path = File.expand_path(file) + if !File.exists? file_abs_path + return []; + end + pods_ary = [] + skip_line_start_symbols = ["#", "/"] + File.foreach(file_abs_path) { |line| + next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } + plugin = line.split(pattern=separator) + if plugin.length == 2 + podname = plugin[0].strip() + path = plugin[1].strip() + podpath = File.expand_path("#{path}", file_abs_path) + pods_ary.push({:name => podname, :path => podpath}); + else + puts "Invalid plugin specification: #{line}" + end + } + return pods_ary +end + +target 'Runner' do + # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock + # referring to absolute paths on developers' machines. + system('rm -rf .symlinks') + system('mkdir -p .symlinks/plugins') + + # Flutter Pods + generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') + if generated_xcode_build_settings.empty? + puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter packages get is executed first." + end + generated_xcode_build_settings.map { |p| + if p[:name] == 'FLUTTER_FRAMEWORK_DIR' + symlink = File.join('.symlinks', 'flutter') + File.symlink(File.dirname(p[:path]), symlink) + pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) + end + } + + # Plugin Pods + plugin_pods = parse_KV_file('../.flutter-plugins') + plugin_pods.map { |p| + symlink = File.join('.symlinks', 'plugins', p[:name]) + File.symlink(p[:path], symlink) + pod p[:name], :path => File.join(symlink, 'ios') + } +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['ENABLE_BITCODE'] = 'NO' + end + end +end diff --git a/ios/Podfile.lock b/ios/Podfile.lock new file mode 100644 index 0000000..557bbc7 --- /dev/null +++ b/ios/Podfile.lock @@ -0,0 +1,22 @@ +PODS: + - Flutter (1.0.0) + - flutter_secure_storage (3.2.0): + - Flutter + +DEPENDENCIES: + - Flutter (from `.symlinks/flutter/ios`) + - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) + +EXTERNAL SOURCES: + Flutter: + :path: ".symlinks/flutter/ios" + flutter_secure_storage: + :path: ".symlinks/plugins/flutter_secure_storage/ios" + +SPEC CHECKSUMS: + Flutter: 58dd7d1b27887414a370fcccb9e645c08ffd7a6a + flutter_secure_storage: 0c5779648ff644110e507909b77a57e620cbbf8b + +PODFILE CHECKSUM: aff02bfeed411c636180d6812254b2daeea14d09 + +COCOAPODS: 1.7.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index f202b9d..3b9aebf 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + F8EF57AFFF0C7C353E3604B3 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 097444538E941EBFED1976C8 /* libPods-Runner.a */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -37,13 +38,16 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 097444538E941EBFED1976C8 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; + 4674E99068360BD2489EC50C /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 880DAEE09FC4F07D8ED7F46C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; @@ -53,6 +57,7 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A4F4986515B0C16B78A73BD7 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -62,6 +67,7 @@ files = ( 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, + F8EF57AFFF0C7C353E3604B3 /* libPods-Runner.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -87,7 +93,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, - CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, + D52790F7E6421775A2150276 /* Pods */, + E491E94631566224F7EB2248 /* Frameworks */, ); sourceTree = ""; }; @@ -123,6 +130,24 @@ name = "Supporting Files"; sourceTree = ""; }; + D52790F7E6421775A2150276 /* Pods */ = { + isa = PBXGroup; + children = ( + 880DAEE09FC4F07D8ED7F46C /* Pods-Runner.debug.xcconfig */, + A4F4986515B0C16B78A73BD7 /* Pods-Runner.release.xcconfig */, + 4674E99068360BD2489EC50C /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + E491E94631566224F7EB2248 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 097444538E941EBFED1976C8 /* libPods-Runner.a */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -130,12 +155,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 0D0C54F13DCFB588B50287A7 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 273F25DC0E60392B1958115D /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -157,6 +184,8 @@ TargetAttributes = { 97C146ED1CF9000F007C117D = { CreatedOnToolsVersion = 7.3.1; + DevelopmentTeam = D8G6NQ28ZR; + ProvisioningStyle = Automatic; }; }; }; @@ -165,6 +194,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, ); @@ -194,6 +224,46 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 0D0C54F13DCFB588B50287A7 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 273F25DC0E60392B1958115D /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -310,8 +380,10 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - DEVELOPMENT_TEAM = S8QB4VV633; + DEVELOPMENT_TEAM = D8G6NQ28ZR; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -323,8 +395,9 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.selfpassMobile; + PRODUCT_BUNDLE_IDENTIFIER = com.mjfs.selfpass; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; VERSIONING_SYSTEM = "apple-generic"; }; name = Profile; @@ -436,7 +509,10 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = D8G6NQ28ZR; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -448,8 +524,9 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.selfpassMobile; + PRODUCT_BUNDLE_IDENTIFIER = com.mjfs.selfpass; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; @@ -459,7 +536,10 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = D8G6NQ28ZR; ENABLE_BITCODE = NO; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -471,8 +551,9 @@ "$(inherited)", "$(PROJECT_DIR)/Flutter", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.selfpassMobile; + PRODUCT_BUNDLE_IDENTIFIER = com.mjfs.selfpass; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; VERSIONING_SYSTEM = "apple-generic"; }; name = Release; diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a1..21a3cc1 100644 --- a/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index f1de609..3691648 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -4,6 +4,8 @@ CFBundleDevelopmentRegion en + CFBundleDisplayName + Selfpass CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier diff --git a/lib/main.dart b/lib/main.dart index d29212b..4e26e49 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,21 +1,53 @@ import 'package:flutter/cupertino.dart'; +import 'package:provider/provider.dart'; + +import 'repositories/credentials_client.dart'; +import 'repositories/config.dart'; import 'screens/authentication.dart'; -import 'screens/home.dart'; import 'screens/credentials.dart'; +import 'screens/home.dart'; + +import 'types/abstracts.dart'; void main() => runApp(Selfpass()); class Selfpass extends StatelessWidget { @override Widget build(BuildContext context) { - return CupertinoApp( - title: 'Selfpass', - routes: { - '/': (BuildContext context) => Authentication(), - '/home': (BuildContext context) => Home(), - '/credentials': (BuildContext context) => Credentials(), - }, + return Provider( + builder: (BuildContext context) => Config(), + child: CupertinoApp( + title: 'Selfpass', + onGenerateRoute: (RouteSettings settings) { + String title; + WidgetBuilder builder; + + switch (settings.name) { + case '/': + title = 'Authentication'; + builder = (BuildContext context) => Authentication(); + break; + + case '/home': + title = "Credential Hosts"; + builder = (BuildContext context) => Provider( + builder: (BuildContext context) => + CredentialsClient(settings.arguments), + child: Home(), + ); + break; + + case '/credentials': + title = "Credentials"; + builder = + (BuildContext context) => Credentials(settings.arguments); + break; + } + + return CupertinoPageRoute(builder: builder, title: title); + }, + ), ); } } diff --git a/lib/protobuf/google/protobuf/timestamp.pb.dart b/lib/protobuf/google/protobuf/timestamp.pb.dart new file mode 100644 index 0000000..be02171 --- /dev/null +++ b/lib/protobuf/google/protobuf/timestamp.pb.dart @@ -0,0 +1,61 @@ +/// +// Generated code. Do not modify. +// source: timestamp.proto +/// +// ignore_for_file: camel_case_types,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name + +import 'dart:core' as $core show bool, Deprecated, double, int, List, Map, override, pragma, String; + +import 'package:fixnum/fixnum.dart'; +import 'package:protobuf/protobuf.dart' as $pb; + +import 'dart:core' as $core show DateTime, Duration; +class Timestamp extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo('Timestamp', package: const $pb.PackageName('google.protobuf')) + ..aInt64(1, 'seconds') + ..a<$core.int>(2, 'nanos', $pb.PbFieldType.O3) + ..hasRequiredFields = false + ; + + Timestamp._() : super(); + factory Timestamp() => create(); + factory Timestamp.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory Timestamp.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + Timestamp clone() => Timestamp()..mergeFromMessage(this); + Timestamp copyWith(void Function(Timestamp) updates) => super.copyWith((message) => updates(message as Timestamp)); + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static Timestamp create() => Timestamp._(); + Timestamp createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + static Timestamp getDefault() => _defaultInstance ??= create()..freeze(); + static Timestamp _defaultInstance; + + Int64 get seconds => $_getI64(0); + set seconds(Int64 v) { $_setInt64(0, v); } + $core.bool hasSeconds() => $_has(0); + void clearSeconds() => clearField(1); + + $core.int get nanos => $_get(1, 0); + set nanos($core.int v) { $_setSignedInt32(1, v); } + $core.bool hasNanos() => $_has(1); + void clearNanos() => clearField(2); + /// Converts an instance to [DateTime]. + /// + /// The result is in UTC time zone and has microsecond precision, as + /// [DateTime] does not support nanosecond precision. + $core.DateTime toDateTime() => $core.DateTime.fromMicrosecondsSinceEpoch( + seconds.toInt() * $core.Duration.microsecondsPerSecond + nanos ~/ 1000, + isUtc: true); + + /// Creates a new instance from [dateTime]. + /// + /// Time zone information will not be preserved. + static Timestamp fromDateTime($core.DateTime dateTime) { + $core.int micros = dateTime.microsecondsSinceEpoch; + return Timestamp() + ..seconds = Int64(micros ~/ $core.Duration.microsecondsPerSecond) + ..nanos = (micros % $core.Duration.microsecondsPerSecond).toInt() * 1000; + } +} + diff --git a/lib/protobuf/google/protobuf/timestamp.pbenum.dart b/lib/protobuf/google/protobuf/timestamp.pbenum.dart new file mode 100644 index 0000000..fd4e632 --- /dev/null +++ b/lib/protobuf/google/protobuf/timestamp.pbenum.dart @@ -0,0 +1,6 @@ +/// +// Generated code. Do not modify. +// source: timestamp.proto +/// +// ignore_for_file: camel_case_types,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name + diff --git a/lib/protobuf/google/protobuf/timestamp.pbjson.dart b/lib/protobuf/google/protobuf/timestamp.pbjson.dart new file mode 100644 index 0000000..d7afdfb --- /dev/null +++ b/lib/protobuf/google/protobuf/timestamp.pbjson.dart @@ -0,0 +1,14 @@ +/// +// Generated code. Do not modify. +// source: timestamp.proto +/// +// ignore_for_file: camel_case_types,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name + +const Timestamp$json = const { + '1': 'Timestamp', + '2': const [ + const {'1': 'seconds', '3': 1, '4': 1, '5': 3, '10': 'seconds'}, + const {'1': 'nanos', '3': 2, '4': 1, '5': 5, '10': 'nanos'}, + ], +}; + diff --git a/lib/protobuf/service.pb.dart b/lib/protobuf/service.pb.dart new file mode 100644 index 0000000..6fcd5a1 --- /dev/null +++ b/lib/protobuf/service.pb.dart @@ -0,0 +1,384 @@ +/// +// Generated code. Do not modify. +// source: service.proto +/// +// ignore_for_file: camel_case_types,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name + +import 'dart:core' as $core show bool, Deprecated, double, int, List, Map, override, pragma, String; + +import 'package:protobuf/protobuf.dart' as $pb; + +import 'google/protobuf/timestamp.pb.dart' as $1; + +class DeleteResponse extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo('DeleteResponse', package: const $pb.PackageName('selfpass.credentials')) + ..aOB(1, 'success') + ..hasRequiredFields = false + ; + + DeleteResponse._() : super(); + factory DeleteResponse() => create(); + factory DeleteResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory DeleteResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + DeleteResponse clone() => DeleteResponse()..mergeFromMessage(this); + DeleteResponse copyWith(void Function(DeleteResponse) updates) => super.copyWith((message) => updates(message as DeleteResponse)); + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static DeleteResponse create() => DeleteResponse._(); + DeleteResponse createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + static DeleteResponse getDefault() => _defaultInstance ??= create()..freeze(); + static DeleteResponse _defaultInstance; + + $core.bool get success => $_get(0, false); + set success($core.bool v) { $_setBool(0, v); } + $core.bool hasSuccess() => $_has(0); + void clearSuccess() => clearField(1); +} + +class GetAllMetadataRequest extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo('GetAllMetadataRequest', package: const $pb.PackageName('selfpass.credentials')) + ..aOS(1, 'sourceHost') + ..hasRequiredFields = false + ; + + GetAllMetadataRequest._() : super(); + factory GetAllMetadataRequest() => create(); + factory GetAllMetadataRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory GetAllMetadataRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + GetAllMetadataRequest clone() => GetAllMetadataRequest()..mergeFromMessage(this); + GetAllMetadataRequest copyWith(void Function(GetAllMetadataRequest) updates) => super.copyWith((message) => updates(message as GetAllMetadataRequest)); + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static GetAllMetadataRequest create() => GetAllMetadataRequest._(); + GetAllMetadataRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + static GetAllMetadataRequest getDefault() => _defaultInstance ??= create()..freeze(); + static GetAllMetadataRequest _defaultInstance; + + $core.String get sourceHost => $_getS(0, ''); + set sourceHost($core.String v) { $_setString(0, v); } + $core.bool hasSourceHost() => $_has(0); + void clearSourceHost() => clearField(1); +} + +class IdRequest extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo('IdRequest', package: const $pb.PackageName('selfpass.credentials')) + ..aOS(1, 'id') + ..hasRequiredFields = false + ; + + IdRequest._() : super(); + factory IdRequest() => create(); + factory IdRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory IdRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + IdRequest clone() => IdRequest()..mergeFromMessage(this); + IdRequest copyWith(void Function(IdRequest) updates) => super.copyWith((message) => updates(message as IdRequest)); + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static IdRequest create() => IdRequest._(); + IdRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + static IdRequest getDefault() => _defaultInstance ??= create()..freeze(); + static IdRequest _defaultInstance; + + $core.String get id => $_getS(0, ''); + set id($core.String v) { $_setString(0, v); } + $core.bool hasId() => $_has(0); + void clearId() => clearField(1); +} + +class UpdateRequest extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo('UpdateRequest', package: const $pb.PackageName('selfpass.credentials')) + ..aOS(1, 'id') + ..a(2, 'credential', $pb.PbFieldType.OM, CredentialRequest.getDefault, CredentialRequest.create) + ..hasRequiredFields = false + ; + + UpdateRequest._() : super(); + factory UpdateRequest() => create(); + factory UpdateRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory UpdateRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + UpdateRequest clone() => UpdateRequest()..mergeFromMessage(this); + UpdateRequest copyWith(void Function(UpdateRequest) updates) => super.copyWith((message) => updates(message as UpdateRequest)); + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static UpdateRequest create() => UpdateRequest._(); + UpdateRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + static UpdateRequest getDefault() => _defaultInstance ??= create()..freeze(); + static UpdateRequest _defaultInstance; + + $core.String get id => $_getS(0, ''); + set id($core.String v) { $_setString(0, v); } + $core.bool hasId() => $_has(0); + void clearId() => clearField(1); + + CredentialRequest get credential => $_getN(1); + set credential(CredentialRequest v) { setField(2, v); } + $core.bool hasCredential() => $_has(1); + void clearCredential() => clearField(2); +} + +class DumpResponse extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo('DumpResponse', package: const $pb.PackageName('selfpass.credentials')) + ..a<$core.List<$core.int>>(1, 'contents', $pb.PbFieldType.OY) + ..hasRequiredFields = false + ; + + DumpResponse._() : super(); + factory DumpResponse() => create(); + factory DumpResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory DumpResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + DumpResponse clone() => DumpResponse()..mergeFromMessage(this); + DumpResponse copyWith(void Function(DumpResponse) updates) => super.copyWith((message) => updates(message as DumpResponse)); + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static DumpResponse create() => DumpResponse._(); + DumpResponse createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + static DumpResponse getDefault() => _defaultInstance ??= create()..freeze(); + static DumpResponse _defaultInstance; + + $core.List<$core.int> get contents => $_getN(0); + set contents($core.List<$core.int> v) { $_setBytes(0, v); } + $core.bool hasContents() => $_has(0); + void clearContents() => clearField(1); +} + +class EmptyRequest extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo('EmptyRequest', package: const $pb.PackageName('selfpass.credentials')) + ..hasRequiredFields = false + ; + + EmptyRequest._() : super(); + factory EmptyRequest() => create(); + factory EmptyRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory EmptyRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + EmptyRequest clone() => EmptyRequest()..mergeFromMessage(this); + EmptyRequest copyWith(void Function(EmptyRequest) updates) => super.copyWith((message) => updates(message as EmptyRequest)); + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static EmptyRequest create() => EmptyRequest._(); + EmptyRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + static EmptyRequest getDefault() => _defaultInstance ??= create()..freeze(); + static EmptyRequest _defaultInstance; +} + +class Metadata extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo('Metadata', package: const $pb.PackageName('selfpass.credentials')) + ..aOS(1, 'id') + ..a<$1.Timestamp>(2, 'createdAt', $pb.PbFieldType.OM, $1.Timestamp.getDefault, $1.Timestamp.create) + ..a<$1.Timestamp>(3, 'updatedAt', $pb.PbFieldType.OM, $1.Timestamp.getDefault, $1.Timestamp.create) + ..aOS(4, 'primary') + ..aOS(5, 'sourceHost') + ..aOS(6, 'loginUrl') + ..aOS(7, 'tag') + ..hasRequiredFields = false + ; + + Metadata._() : super(); + factory Metadata() => create(); + factory Metadata.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory Metadata.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + Metadata clone() => Metadata()..mergeFromMessage(this); + Metadata copyWith(void Function(Metadata) updates) => super.copyWith((message) => updates(message as Metadata)); + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static Metadata create() => Metadata._(); + Metadata createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + static Metadata getDefault() => _defaultInstance ??= create()..freeze(); + static Metadata _defaultInstance; + + $core.String get id => $_getS(0, ''); + set id($core.String v) { $_setString(0, v); } + $core.bool hasId() => $_has(0); + void clearId() => clearField(1); + + $1.Timestamp get createdAt => $_getN(1); + set createdAt($1.Timestamp v) { setField(2, v); } + $core.bool hasCreatedAt() => $_has(1); + void clearCreatedAt() => clearField(2); + + $1.Timestamp get updatedAt => $_getN(2); + set updatedAt($1.Timestamp v) { setField(3, v); } + $core.bool hasUpdatedAt() => $_has(2); + void clearUpdatedAt() => clearField(3); + + $core.String get primary => $_getS(3, ''); + set primary($core.String v) { $_setString(3, v); } + $core.bool hasPrimary() => $_has(3); + void clearPrimary() => clearField(4); + + $core.String get sourceHost => $_getS(4, ''); + set sourceHost($core.String v) { $_setString(4, v); } + $core.bool hasSourceHost() => $_has(4); + void clearSourceHost() => clearField(5); + + $core.String get loginUrl => $_getS(5, ''); + set loginUrl($core.String v) { $_setString(5, v); } + $core.bool hasLoginUrl() => $_has(5); + void clearLoginUrl() => clearField(6); + + $core.String get tag => $_getS(6, ''); + set tag($core.String v) { $_setString(6, v); } + $core.bool hasTag() => $_has(6); + void clearTag() => clearField(7); +} + +class Credential extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo('Credential', package: const $pb.PackageName('selfpass.credentials')) + ..aOS(1, 'id') + ..a<$1.Timestamp>(2, 'createdAt', $pb.PbFieldType.OM, $1.Timestamp.getDefault, $1.Timestamp.create) + ..a<$1.Timestamp>(3, 'updatedAt', $pb.PbFieldType.OM, $1.Timestamp.getDefault, $1.Timestamp.create) + ..aOS(4, 'primary') + ..aOS(5, 'username') + ..aOS(6, 'email') + ..aOS(7, 'password') + ..aOS(8, 'sourceHost') + ..aOS(9, 'loginUrl') + ..aOS(10, 'tag') + ..aOS(11, 'otpSecret') + ..hasRequiredFields = false + ; + + Credential._() : super(); + factory Credential() => create(); + factory Credential.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory Credential.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + Credential clone() => Credential()..mergeFromMessage(this); + Credential copyWith(void Function(Credential) updates) => super.copyWith((message) => updates(message as Credential)); + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static Credential create() => Credential._(); + Credential createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + static Credential getDefault() => _defaultInstance ??= create()..freeze(); + static Credential _defaultInstance; + + $core.String get id => $_getS(0, ''); + set id($core.String v) { $_setString(0, v); } + $core.bool hasId() => $_has(0); + void clearId() => clearField(1); + + $1.Timestamp get createdAt => $_getN(1); + set createdAt($1.Timestamp v) { setField(2, v); } + $core.bool hasCreatedAt() => $_has(1); + void clearCreatedAt() => clearField(2); + + $1.Timestamp get updatedAt => $_getN(2); + set updatedAt($1.Timestamp v) { setField(3, v); } + $core.bool hasUpdatedAt() => $_has(2); + void clearUpdatedAt() => clearField(3); + + $core.String get primary => $_getS(3, ''); + set primary($core.String v) { $_setString(3, v); } + $core.bool hasPrimary() => $_has(3); + void clearPrimary() => clearField(4); + + $core.String get username => $_getS(4, ''); + set username($core.String v) { $_setString(4, v); } + $core.bool hasUsername() => $_has(4); + void clearUsername() => clearField(5); + + $core.String get email => $_getS(5, ''); + set email($core.String v) { $_setString(5, v); } + $core.bool hasEmail() => $_has(5); + void clearEmail() => clearField(6); + + $core.String get password => $_getS(6, ''); + set password($core.String v) { $_setString(6, v); } + $core.bool hasPassword() => $_has(6); + void clearPassword() => clearField(7); + + $core.String get sourceHost => $_getS(7, ''); + set sourceHost($core.String v) { $_setString(7, v); } + $core.bool hasSourceHost() => $_has(7); + void clearSourceHost() => clearField(8); + + $core.String get loginUrl => $_getS(8, ''); + set loginUrl($core.String v) { $_setString(8, v); } + $core.bool hasLoginUrl() => $_has(8); + void clearLoginUrl() => clearField(9); + + $core.String get tag => $_getS(9, ''); + set tag($core.String v) { $_setString(9, v); } + $core.bool hasTag() => $_has(9); + void clearTag() => clearField(10); + + $core.String get otpSecret => $_getS(10, ''); + set otpSecret($core.String v) { $_setString(10, v); } + $core.bool hasOtpSecret() => $_has(10); + void clearOtpSecret() => clearField(11); +} + +class CredentialRequest extends $pb.GeneratedMessage { + static final $pb.BuilderInfo _i = $pb.BuilderInfo('CredentialRequest', package: const $pb.PackageName('selfpass.credentials')) + ..aOS(1, 'primary') + ..aOS(2, 'username') + ..aOS(3, 'email') + ..aOS(4, 'password') + ..aOS(5, 'sourceHost') + ..aOS(6, 'loginUrl') + ..aOS(7, 'tag') + ..aOS(8, 'otpSecret') + ..hasRequiredFields = false + ; + + CredentialRequest._() : super(); + factory CredentialRequest() => create(); + factory CredentialRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory CredentialRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + CredentialRequest clone() => CredentialRequest()..mergeFromMessage(this); + CredentialRequest copyWith(void Function(CredentialRequest) updates) => super.copyWith((message) => updates(message as CredentialRequest)); + $pb.BuilderInfo get info_ => _i; + @$core.pragma('dart2js:noInline') + static CredentialRequest create() => CredentialRequest._(); + CredentialRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + static CredentialRequest getDefault() => _defaultInstance ??= create()..freeze(); + static CredentialRequest _defaultInstance; + + $core.String get primary => $_getS(0, ''); + set primary($core.String v) { $_setString(0, v); } + $core.bool hasPrimary() => $_has(0); + void clearPrimary() => clearField(1); + + $core.String get username => $_getS(1, ''); + set username($core.String v) { $_setString(1, v); } + $core.bool hasUsername() => $_has(1); + void clearUsername() => clearField(2); + + $core.String get email => $_getS(2, ''); + set email($core.String v) { $_setString(2, v); } + $core.bool hasEmail() => $_has(2); + void clearEmail() => clearField(3); + + $core.String get password => $_getS(3, ''); + set password($core.String v) { $_setString(3, v); } + $core.bool hasPassword() => $_has(3); + void clearPassword() => clearField(4); + + $core.String get sourceHost => $_getS(4, ''); + set sourceHost($core.String v) { $_setString(4, v); } + $core.bool hasSourceHost() => $_has(4); + void clearSourceHost() => clearField(5); + + $core.String get loginUrl => $_getS(5, ''); + set loginUrl($core.String v) { $_setString(5, v); } + $core.bool hasLoginUrl() => $_has(5); + void clearLoginUrl() => clearField(6); + + $core.String get tag => $_getS(6, ''); + set tag($core.String v) { $_setString(6, v); } + $core.bool hasTag() => $_has(6); + void clearTag() => clearField(7); + + $core.String get otpSecret => $_getS(7, ''); + set otpSecret($core.String v) { $_setString(7, v); } + $core.bool hasOtpSecret() => $_has(7); + void clearOtpSecret() => clearField(8); +} + diff --git a/lib/protobuf/service.pbenum.dart b/lib/protobuf/service.pbenum.dart new file mode 100644 index 0000000..cf0e02d --- /dev/null +++ b/lib/protobuf/service.pbenum.dart @@ -0,0 +1,6 @@ +/// +// Generated code. Do not modify. +// source: service.proto +/// +// ignore_for_file: camel_case_types,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name + diff --git a/lib/protobuf/service.pbgrpc.dart b/lib/protobuf/service.pbgrpc.dart new file mode 100644 index 0000000..40a8c1f --- /dev/null +++ b/lib/protobuf/service.pbgrpc.dart @@ -0,0 +1,158 @@ +/// +// Generated code. Do not modify. +// source: service.proto +/// +// ignore_for_file: camel_case_types,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name + +import 'dart:async' as $async; + +import 'dart:core' as $core show int, String, List; + +import 'package:grpc/service_api.dart' as $grpc; +import 'service.pb.dart' as $0; +export 'service.pb.dart'; + +class CredentialServiceClient extends $grpc.Client { + static final _$getAllMetadata = + $grpc.ClientMethod<$0.GetAllMetadataRequest, $0.Metadata>( + '/selfpass.credentials.CredentialService/GetAllMetadata', + ($0.GetAllMetadataRequest value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.Metadata.fromBuffer(value)); + static final _$get = $grpc.ClientMethod<$0.IdRequest, $0.Credential>( + '/selfpass.credentials.CredentialService/Get', + ($0.IdRequest value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.Credential.fromBuffer(value)); + static final _$create = + $grpc.ClientMethod<$0.CredentialRequest, $0.Credential>( + '/selfpass.credentials.CredentialService/Create', + ($0.CredentialRequest value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.Credential.fromBuffer(value)); + static final _$update = $grpc.ClientMethod<$0.UpdateRequest, $0.Credential>( + '/selfpass.credentials.CredentialService/Update', + ($0.UpdateRequest value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.Credential.fromBuffer(value)); + static final _$delete = $grpc.ClientMethod<$0.IdRequest, $0.DeleteResponse>( + '/selfpass.credentials.CredentialService/Delete', + ($0.IdRequest value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.DeleteResponse.fromBuffer(value)); + + CredentialServiceClient($grpc.ClientChannel channel, + {$grpc.CallOptions options}) + : super(channel, options: options); + + $grpc.ResponseStream<$0.Metadata> getAllMetadata( + $0.GetAllMetadataRequest request, + {$grpc.CallOptions options}) { + final call = $createCall( + _$getAllMetadata, $async.Stream.fromIterable([request]), + options: options); + return $grpc.ResponseStream(call); + } + + $grpc.ResponseFuture<$0.Credential> get($0.IdRequest request, + {$grpc.CallOptions options}) { + final call = $createCall(_$get, $async.Stream.fromIterable([request]), + options: options); + return $grpc.ResponseFuture(call); + } + + $grpc.ResponseFuture<$0.Credential> create($0.CredentialRequest request, + {$grpc.CallOptions options}) { + final call = $createCall(_$create, $async.Stream.fromIterable([request]), + options: options); + return $grpc.ResponseFuture(call); + } + + $grpc.ResponseFuture<$0.Credential> update($0.UpdateRequest request, + {$grpc.CallOptions options}) { + final call = $createCall(_$update, $async.Stream.fromIterable([request]), + options: options); + return $grpc.ResponseFuture(call); + } + + $grpc.ResponseFuture<$0.DeleteResponse> delete($0.IdRequest request, + {$grpc.CallOptions options}) { + final call = $createCall(_$delete, $async.Stream.fromIterable([request]), + options: options); + return $grpc.ResponseFuture(call); + } +} + +abstract class CredentialServiceBase extends $grpc.Service { + $core.String get $name => 'selfpass.credentials.CredentialService'; + + CredentialServiceBase() { + $addMethod($grpc.ServiceMethod<$0.GetAllMetadataRequest, $0.Metadata>( + 'GetAllMetadata', + getAllMetadata_Pre, + false, + true, + ($core.List<$core.int> value) => + $0.GetAllMetadataRequest.fromBuffer(value), + ($0.Metadata value) => value.writeToBuffer())); + $addMethod($grpc.ServiceMethod<$0.IdRequest, $0.Credential>( + 'Get', + get_Pre, + false, + false, + ($core.List<$core.int> value) => $0.IdRequest.fromBuffer(value), + ($0.Credential value) => value.writeToBuffer())); + $addMethod($grpc.ServiceMethod<$0.CredentialRequest, $0.Credential>( + 'Create', + create_Pre, + false, + false, + ($core.List<$core.int> value) => $0.CredentialRequest.fromBuffer(value), + ($0.Credential value) => value.writeToBuffer())); + $addMethod($grpc.ServiceMethod<$0.UpdateRequest, $0.Credential>( + 'Update', + update_Pre, + false, + false, + ($core.List<$core.int> value) => $0.UpdateRequest.fromBuffer(value), + ($0.Credential value) => value.writeToBuffer())); + $addMethod($grpc.ServiceMethod<$0.IdRequest, $0.DeleteResponse>( + 'Delete', + delete_Pre, + false, + false, + ($core.List<$core.int> value) => $0.IdRequest.fromBuffer(value), + ($0.DeleteResponse value) => value.writeToBuffer())); + } + + $async.Stream<$0.Metadata> getAllMetadata_Pre( + $grpc.ServiceCall call, $async.Future request) async* { + yield* getAllMetadata(call, (await request) as $0.GetAllMetadataRequest); + } + + $async.Future<$0.Credential> get_Pre( + $grpc.ServiceCall call, $async.Future request) async { + return get(call, await request); + } + + $async.Future<$0.Credential> create_Pre( + $grpc.ServiceCall call, $async.Future request) async { + return create(call, await request); + } + + $async.Future<$0.Credential> update_Pre( + $grpc.ServiceCall call, $async.Future request) async { + return update(call, await request); + } + + $async.Future<$0.DeleteResponse> delete_Pre( + $grpc.ServiceCall call, $async.Future request) async { + return delete(call, await request); + } + + $async.Stream<$0.Metadata> getAllMetadata( + $grpc.ServiceCall call, $0.GetAllMetadataRequest request); + $async.Future<$0.Credential> get( + $grpc.ServiceCall call, $0.IdRequest request); + $async.Future<$0.Credential> create( + $grpc.ServiceCall call, $0.CredentialRequest request); + $async.Future<$0.Credential> update( + $grpc.ServiceCall call, $0.UpdateRequest request); + $async.Future<$0.DeleteResponse> delete( + $grpc.ServiceCall call, $0.IdRequest request); +} diff --git a/lib/protobuf/service.pbjson.dart b/lib/protobuf/service.pbjson.dart new file mode 100644 index 0000000..d97e434 --- /dev/null +++ b/lib/protobuf/service.pbjson.dart @@ -0,0 +1,90 @@ +/// +// Generated code. Do not modify. +// source: service.proto +/// +// ignore_for_file: camel_case_types,non_constant_identifier_names,library_prefixes,unused_import,unused_shown_name + +const DeleteResponse$json = const { + '1': 'DeleteResponse', + '2': const [ + const {'1': 'success', '3': 1, '4': 1, '5': 8, '10': 'success'}, + ], +}; + +const GetAllMetadataRequest$json = const { + '1': 'GetAllMetadataRequest', + '2': const [ + const {'1': 'source_host', '3': 1, '4': 1, '5': 9, '10': 'sourceHost'}, + ], +}; + +const IdRequest$json = const { + '1': 'IdRequest', + '2': const [ + const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, + ], +}; + +const UpdateRequest$json = const { + '1': 'UpdateRequest', + '2': const [ + const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, + const {'1': 'credential', '3': 2, '4': 1, '5': 11, '6': '.selfpass.credentials.CredentialRequest', '10': 'credential'}, + ], +}; + +const DumpResponse$json = const { + '1': 'DumpResponse', + '2': const [ + const {'1': 'contents', '3': 1, '4': 1, '5': 12, '10': 'contents'}, + ], +}; + +const EmptyRequest$json = const { + '1': 'EmptyRequest', +}; + +const Metadata$json = const { + '1': 'Metadata', + '2': const [ + const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, + const {'1': 'created_at', '3': 2, '4': 1, '5': 11, '6': '.google.protobuf.Timestamp', '10': 'createdAt'}, + const {'1': 'updated_at', '3': 3, '4': 1, '5': 11, '6': '.google.protobuf.Timestamp', '10': 'updatedAt'}, + const {'1': 'primary', '3': 4, '4': 1, '5': 9, '10': 'primary'}, + const {'1': 'source_host', '3': 5, '4': 1, '5': 9, '10': 'sourceHost'}, + const {'1': 'login_url', '3': 6, '4': 1, '5': 9, '10': 'loginUrl'}, + const {'1': 'tag', '3': 7, '4': 1, '5': 9, '10': 'tag'}, + ], +}; + +const Credential$json = const { + '1': 'Credential', + '2': const [ + const {'1': 'id', '3': 1, '4': 1, '5': 9, '10': 'id'}, + const {'1': 'created_at', '3': 2, '4': 1, '5': 11, '6': '.google.protobuf.Timestamp', '10': 'createdAt'}, + const {'1': 'updated_at', '3': 3, '4': 1, '5': 11, '6': '.google.protobuf.Timestamp', '10': 'updatedAt'}, + const {'1': 'primary', '3': 4, '4': 1, '5': 9, '10': 'primary'}, + const {'1': 'username', '3': 5, '4': 1, '5': 9, '10': 'username'}, + const {'1': 'email', '3': 6, '4': 1, '5': 9, '10': 'email'}, + const {'1': 'password', '3': 7, '4': 1, '5': 9, '10': 'password'}, + const {'1': 'source_host', '3': 8, '4': 1, '5': 9, '10': 'sourceHost'}, + const {'1': 'login_url', '3': 9, '4': 1, '5': 9, '10': 'loginUrl'}, + const {'1': 'tag', '3': 10, '4': 1, '5': 9, '10': 'tag'}, + const {'1': 'otp_secret', '3': 11, '4': 1, '5': 9, '10': 'otpSecret'}, + ], +}; + +const CredentialRequest$json = const { + '1': 'CredentialRequest', + '2': const [ + const {'1': 'primary', '3': 1, '4': 1, '5': 9, '10': 'primary'}, + const {'1': 'username', '3': 2, '4': 1, '5': 9, '10': 'username'}, + const {'1': 'email', '3': 3, '4': 1, '5': 9, '10': 'email'}, + const {'1': 'password', '3': 4, '4': 1, '5': 9, '10': 'password'}, + const {'1': 'source_host', '3': 5, '4': 1, '5': 9, '10': 'sourceHost'}, + const {'1': 'login_url', '3': 6, '4': 1, '5': 9, '10': 'loginUrl'}, + const {'1': 'tag', '3': 7, '4': 1, '5': 9, '10': 'tag'}, + const {'1': 'otp_secret', '3': 8, '4': 1, '5': 9, '10': 'otpSecret'}, + ], +}; + diff --git a/lib/protobuf/service.proto b/lib/protobuf/service.proto new file mode 100644 index 0000000..c9dfa49 --- /dev/null +++ b/lib/protobuf/service.proto @@ -0,0 +1,75 @@ +syntax = "proto3"; + +package selfpass.credentials; + +option go_package = "protobuf"; + +import "google/protobuf/timestamp.proto"; + +service CredentialService { + rpc GetAllMetadata (GetAllMetadataRequest) returns (stream Metadata); + rpc Get (IdRequest) returns (Credential); + rpc Create (CredentialRequest) returns (Credential); + rpc Update (UpdateRequest) returns (Credential); + rpc Delete (IdRequest) returns (DeleteResponse); + // rpc Dump (EmptyRequest) returns (DumpResponse); +} + +message DeleteResponse { + bool success = 1; +} + +message GetAllMetadataRequest { + string source_host = 1; +} + +message IdRequest { + string id = 1; +} + +message UpdateRequest { + string id = 1; + CredentialRequest credential = 2; +} + +message DumpResponse { + bytes contents = 1; +} + +message EmptyRequest { +} + +message Metadata { + string id = 1; + google.protobuf.Timestamp created_at = 2; + google.protobuf.Timestamp updated_at = 3; + string primary = 4; + string source_host = 5; + string login_url = 6; + string tag = 7; +} + +message Credential { + string id = 1; + google.protobuf.Timestamp created_at = 2; + google.protobuf.Timestamp updated_at = 3; + string primary = 4; + string username = 5; + string email = 6; + string password = 7; + string source_host = 8; + string login_url = 9; + string tag = 10; + string otp_secret = 11; +} + +message CredentialRequest { + string primary = 1; + string username = 2; + string email = 3; + string password = 4; + string source_host = 5; + string login_url = 6; + string tag = 7; + string otp_secret = 8; +} diff --git a/lib/repositories/config.dart b/lib/repositories/config.dart new file mode 100644 index 0000000..aab0b72 --- /dev/null +++ b/lib/repositories/config.dart @@ -0,0 +1,65 @@ +import 'dart:convert'; + +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; + +import '../types/abstracts.dart'; +import '../types/connection_config.dart'; + +import '../utils/crypto.dart' as crypto; + +class Config implements ConfigRepo { + static const _keyPrivateKey = "private_key"; + static const _keyConnectionConfig = "connection_config"; + static const _keyPassword = "password"; + final FlutterSecureStorage _storage = FlutterSecureStorage(); + var _passwordMatched = false; + + Future setPrivateKey(String key) { + _checkIfPasswordMatched(); + return _storage.write(key: _keyPrivateKey, value: key); + } + + Future get privateKey { + _checkIfPasswordMatched(); + return _storage.read(key: _keyPrivateKey); + } + + Future setPassword(String password) { + _checkIfPasswordMatched(); + return _storage.write( + key: _keyPassword, value: crypto.hashPassword(password)); + } + + Future get passwordSet async { + var passHash = await _storage.read(key: _keyPassword); + + if (passHash != null) { + return true; + } + + _passwordMatched = true; + + return false; + } + + Future matchesPasswordHash(String password) async => + _passwordMatched = crypto.matchHashedPassword( + await _storage.read(key: _keyPassword), password); + + Future setConnectionConfig(ConnectionConfig config) { + _checkIfPasswordMatched(); + return _storage.write( + key: _keyConnectionConfig, value: json.encode(config)); + } + + Future get connectionConfig async { + _checkIfPasswordMatched(); + return ConnectionConfig.fromJson( + json.decode(await _storage.read(key: _keyConnectionConfig))); + } + + void _checkIfPasswordMatched() { + if (_passwordMatched) return; + throw Exception('password not matched yet'); + } +} diff --git a/lib/repositories/credentials_client.dart b/lib/repositories/credentials_client.dart new file mode 100644 index 0000000..b84a757 --- /dev/null +++ b/lib/repositories/credentials_client.dart @@ -0,0 +1,79 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:grpc/grpc.dart'; + +import '../protobuf/service.pbgrpc.dart' as grpc; +import '../protobuf/service.pb.dart' as protobuf; + +import '../types/abstracts.dart'; +import '../types/connection_config.dart'; +import '../types/credential.dart'; + +class CredentialsClient implements CredentialsRepo { + grpc.CredentialServiceClient _client; + + CredentialsClient(ConnectionConfig config) { + final caCert = utf8.encode(config.caCertificate); + final cert = utf8.encode(config.certificate); + final privateCert = utf8.encode(config.privateCertificate); + + final splitHost = config.host.split(':'); + final hostname = splitHost[0]; + final port = int.parse(splitHost[1]); + + _client = grpc.CredentialServiceClient(ClientChannel( + hostname, + port: port, + options: ChannelOptions( + credentials: _ChannelCredentials(caCert, cert, privateCert), + ), + )); + } + + Stream getAllMetadata(String sourceHost) { + final request = grpc.GetAllMetadataRequest(); + request.sourceHost = sourceHost; + + return _client.getAllMetadata(request).map( + (protobuf.Metadata pbMetadata) => Metadata.fromProtobuf(pbMetadata)); + } + + Future get(String id) async { + final request = grpc.IdRequest(); + request.id = id; + + return Credential.fromProtobuf(await _client.get(request)); + } + + Future create(CredentialInput input) async { + return Credential(); + } + + Future update(String id, CredentialInput input) async { + return Credential(); + } + + Future delete(String id) { + final request = grpc.IdRequest(); + request.id = id; + + return _client.delete(request); + } +} + +class _ChannelCredentials extends ChannelCredentials { + final List _key; + final List _cert; + + const _ChannelCredentials(List caCert, this._cert, this._key) + : super.secure(certificates: caCert); + + @override + SecurityContext get securityContext { + return super.securityContext + ..usePrivateKeyBytes(_key) + ..useCertificateChainBytes(_cert); + } +} diff --git a/lib/screens/authentication.dart b/lib/screens/authentication.dart index 4f924d0..eb85109 100644 --- a/lib/screens/authentication.dart +++ b/lib/screens/authentication.dart @@ -1,4 +1,9 @@ import 'package:flutter/cupertino.dart'; +import 'package:provider/provider.dart'; + +import '../types/abstracts.dart'; + +import '../widgets/obfuscated_text_field.dart'; class Authentication extends StatefulWidget { @override @@ -6,42 +11,58 @@ class Authentication extends StatefulWidget { } class _AuthenticationState extends State { - static const String _masterpass = 'hunter#2'; bool _invalid = false; + bool _passesDontMatch = false; + String _masterpass; + ConfigRepo _config; + Future _passwordIsSet; + + @override + didChangeDependencies() { + super.didChangeDependencies(); + _config = Provider.of(context); + } @override Widget build(BuildContext context) { + if (_passwordIsSet == null) { + _passwordIsSet = _config.passwordSet; + } + return CupertinoPageScaffold( child: Container( margin: const EdgeInsets.symmetric(horizontal: 50.0), - child: Column( - children: _buildColumnChildren(context), + child: FutureBuilder( + future: _passwordIsSet, + builder: (BuildContext context, AsyncSnapshot snapshot) => + snapshot.connectionState == ConnectionState.done + ? Column(children: _buildColumnChildren(snapshot.data)) + : Container(), ), ), ); } - List _buildColumnChildren(BuildContext context) { - final children = [ + List _buildColumnChildren(bool passwordIsSet) { + List children = [ const Spacer(flex: 4), const Flexible(child: Text('Master password:')), Flexible( - child: Container( - padding: const EdgeInsets.symmetric(vertical: 5.0), - child: CupertinoTextField( - decoration: BoxDecoration( - border: Border.all(color: CupertinoColors.black), - borderRadius: const BorderRadius.all(Radius.circular(5.0)), - ), - clearButtonMode: OverlayVisibilityMode.editing, - textAlign: TextAlign.center, - onSubmitted: _makeTextFieldOnSubmittedHandler(context), - obscureText: true, - ), - ), + child: ObfuscatedTextField( + onSubmittedBuilder: + _buildMasterpassSubmittedBuilder(passwordIsSet)), ), ]; + if (!passwordIsSet) { + children.add(const Flexible(child: Text('Re-enter password:'))); + children.add(Flexible( + child: ObfuscatedTextField( + onSubmittedBuilder: + _buildConfirmPassSubmittedBuilder(passwordIsSet)), + )); + } + if (_invalid) { children.add(const Flexible( child: Text( @@ -49,6 +70,20 @@ class _AuthenticationState extends State { style: TextStyle(color: CupertinoColors.destructiveRed), ), )); + } + + if (_passesDontMatch) { + children.add(const Flexible( + child: Text( + 'passwords don\'t match', + style: TextStyle(color: CupertinoColors.destructiveRed), + ), + )); + } + + if (_passesDontMatch) { + children.add(const Spacer(flex: 1)); + } else if (_invalid || !passwordIsSet) { children.add(const Spacer(flex: 2)); } else { children.add(const Spacer(flex: 3)); @@ -57,16 +92,40 @@ class _AuthenticationState extends State { return children; } - ValueChanged _makeTextFieldOnSubmittedHandler(BuildContext context) { - return (String pass) { - if (pass != _masterpass) { - this.setState(() { - _invalid = true; - }); - return; - } + OnSubmittedBuilder _buildMasterpassSubmittedBuilder(bool passwordIsSet) { + return (BuildContext context) { + return (String pass) async { + if (passwordIsSet) { + if (await _config.matchesPasswordHash(pass)) { + Navigator.of(context).pushReplacementNamed('/home', + arguments: await _config.connectionConfig); + return; + } - Navigator.of(context).pushReplacementNamed('/home'); + this.setState(() => _invalid = true); + return; + } + + _masterpass = pass; + }; + }; + } + + OnSubmittedBuilder _buildConfirmPassSubmittedBuilder(bool passwordIsSet) { + return (BuildContext context) { + return (String pass) async { + if (pass != _masterpass) { + this.setState(() { + _passesDontMatch = true; + }); + return; + } + + _config.setPassword(_masterpass); + _passwordIsSet = Future.value(true); + Navigator.of(context).pushReplacementNamed('/home', + arguments: await _config.connectionConfig); + }; }; } } diff --git a/lib/screens/credentials.dart b/lib/screens/credentials.dart index cf8b109..1a6efa7 100644 --- a/lib/screens/credentials.dart +++ b/lib/screens/credentials.dart @@ -1,25 +1,29 @@ import 'package:flutter/cupertino.dart'; +import '../types/credential.dart'; + import '../widgets/tappable_text_list.dart'; class Credentials extends StatelessWidget { + final List metadatas; + + const Credentials(this.metadatas); + @override Widget build(BuildContext context) { return CupertinoPageScaffold( child: TappableTextList(tappableText: _buildTappableText(context)), - navigationBar: const CupertinoNavigationBar(middle: Text('Credentials')), + navigationBar: CupertinoNavigationBar(), ); } - static Map _buildTappableText( - BuildContext context) { + Map _buildTappableText(BuildContext context) { var handleOnTap = () {}; - Map tappableText = { - 'm@mjfs.us': handleOnTap, - 'm-mjfs': handleOnTap, - 'mitchelljfsimon@gmail.com-mitchelljfsimon': handleOnTap, - }; + Map tappableText = {}; + + metadatas.forEach( + (Metadata metadata) => tappableText[metadata.id] = handleOnTap); return tappableText; } diff --git a/lib/screens/home.dart b/lib/screens/home.dart index 53030d0..f7e6643 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -1,28 +1,71 @@ import 'package:flutter/cupertino.dart'; -// import 'package:provider/provider.dart'; +import 'package:provider/provider.dart'; + +import '../types/abstracts.dart'; +import '../types/credential.dart'; -// import '../types/abstracts.dart'; -// import '../types/credential.dart'; import '../widgets/tappable_text_list.dart'; -class Home extends StatelessWidget { +class Home extends StatefulWidget { + @override + State createState() => _HomeState(); +} + +class _HomeState extends State { + CredentialsRepo _client; + Future> _metadatas; + + @override + didChangeDependencies() { + super.didChangeDependencies(); + _client = Provider.of(context); + } + @override Widget build(BuildContext context) { + if (_metadatas == null) { + _metadatas = _client.getAllMetadata('').toList(); + } + return CupertinoPageScaffold( - child: TappableTextList(tappableText: _buildTappableText(context)), - navigationBar: - const CupertinoNavigationBar(middle: Text('Credentials Hosts')), + child: FutureBuilder>( + future: _metadatas, + builder: (BuildContext context, + AsyncSnapshot> snapshot) => + (snapshot.connectionState == ConnectionState.done) + ? TappableTextList( + tappableText: _buildTappableText(context, snapshot.data)) + : Container(), + ), + navigationBar: CupertinoNavigationBar(), ); } - static Map _buildTappableText( - BuildContext context) { - var handleOnTap = () => Navigator.of(context).pushNamed('/credentials'); - Map tappableText = { - "google.com": handleOnTap, - "amazon.com": handleOnTap, - "linkedin.com": handleOnTap, - }; + Map _buildTappableText( + BuildContext context, + List metadatas, + ) { + final Map> metaMap = {}; + + metadatas.sort((a, b) => a.id.compareTo(b.id)); + + for (var metadata in metadatas) { + final source = metadata.sourceHost; + + if (metaMap[source] == null) { + metaMap[source] = [metadata]; + } else { + metaMap[source].add(metadata); + } + } + + final handleOnTap = (List metadatas) => () => + Navigator.of(context).pushNamed('/credentials', arguments: metadatas); + + final Map tappableText = {}; + + metaMap.forEach((String key, List value) => + tappableText[key] = handleOnTap(value)); return tappableText; } diff --git a/lib/types/abstracts.dart b/lib/types/abstracts.dart new file mode 100644 index 0000000..5ac1041 --- /dev/null +++ b/lib/types/abstracts.dart @@ -0,0 +1,24 @@ +import 'dart:async'; + +import 'credential.dart'; +import 'connection_config.dart'; + +abstract class CredentialsRepo { + Stream getAllMetadata(String sourceHost); + Future get(String id); + Future create(CredentialInput input); + Future update(String id, CredentialInput input); + Future delete(String id); +} + +abstract class ConfigRepo { + Future setPrivateKey(String key); + Future get privateKey; + + Future setPassword(String password); + Future get passwordSet; + Future matchesPasswordHash(String password); + + Future setConnectionConfig(ConnectionConfig config); + Future get connectionConfig; +} diff --git a/lib/types/connection_config.dart b/lib/types/connection_config.dart new file mode 100644 index 0000000..a9e6689 --- /dev/null +++ b/lib/types/connection_config.dart @@ -0,0 +1,27 @@ +class ConnectionConfig { + String host; + String caCertificate; + String certificate; + String privateCertificate; + + ConnectionConfig({ + this.host, + this.caCertificate, + this.certificate, + this.privateCertificate, + }); + + ConnectionConfig.fromJson(Map json) { + host = json['host']; + caCertificate = json['caCertificate']; + certificate = json['certificate']; + privateCertificate = json['privateCertificate']; + } + + Map toJson() => { + 'host': host, + 'caCertificate': caCertificate, + 'certificate': certificate, + 'privateCertificate': privateCertificate, + }; +} diff --git a/lib/types/credential.dart b/lib/types/credential.dart new file mode 100644 index 0000000..2b0956e --- /dev/null +++ b/lib/types/credential.dart @@ -0,0 +1,94 @@ +import '../protobuf/service.pb.dart' as protobuf; + +class Metadata { + String id; + String sourceHost; + DateTime createdAt; + DateTime updatedAt; + String primary; + String loginUrl; + String tag; + + Metadata({ + this.id, + this.sourceHost, + this.createdAt, + this.updatedAt, + this.primary, + this.loginUrl, + this.tag, + }); + + Metadata.fromProtobuf(protobuf.Metadata metadata) { + id = metadata.id; + createdAt = metadata.createdAt.toDateTime(); + updatedAt = metadata.updatedAt.toDateTime(); + sourceHost = metadata.sourceHost; + primary = metadata.primary; + loginUrl = metadata.loginUrl; + tag = metadata.tag; + } + + @override + String toString() => "id: $id"; +} + +class MetadataInput { + String sourceHost; + String primary; + String loginUrl; + String tag; + + MetadataInput({this.sourceHost, this.primary, this.loginUrl, this.tag}); +} + +class Credential { + Metadata meta; + String username; + String email; + String password; + String otpSecret; + + Credential({ + this.meta, + this.username, + this.email, + this.password, + this.otpSecret, + }); + + Credential.fromProtobuf(protobuf.Credential credential) { + meta = Metadata( + id: credential.id, + createdAt: credential.createdAt.toDateTime(), + updatedAt: credential.updatedAt.toDateTime(), + sourceHost: credential.sourceHost, + primary: credential.primary, + loginUrl: credential.loginUrl, + tag: credential.tag, + ); + username = credential.username; + email = credential.email; + password = credential.password; + otpSecret = credential.otpSecret; + } + + @override + String toString() => "meta: $meta\n"; +} + +class CredentialInput { + MetadataInput meta; + String username; + String email; + String password; + String otpSecret; + + CredentialInput({ + this.meta, + this.username, + this.email, + this.password, + this.otpSecret, + }); +} diff --git a/lib/utils/crypto.dart b/lib/utils/crypto.dart new file mode 100644 index 0000000..fea1395 --- /dev/null +++ b/lib/utils/crypto.dart @@ -0,0 +1,15 @@ +import 'dart:math'; +import 'dart:convert'; + +import 'package:crypt/crypt.dart'; + +String hashPassword(String password) { + final random = Random.secure(); + final saltInts = List.generate(16, (_) => random.nextInt(256)); + final salt = base64.encode(saltInts); + + return Crypt.sha256(password, salt: salt).toString(); +} + +bool matchHashedPassword(String hashedPassword, String password) => + Crypt(hashedPassword).match(password); diff --git a/lib/widgets/obfuscated_text_field.dart b/lib/widgets/obfuscated_text_field.dart new file mode 100644 index 0000000..d72a047 --- /dev/null +++ b/lib/widgets/obfuscated_text_field.dart @@ -0,0 +1,28 @@ +import 'package:flutter/cupertino.dart'; + +typedef OnSubmittedBuilder = ValueChanged Function( + BuildContext context, +); + +class ObfuscatedTextField extends StatelessWidget { + final OnSubmittedBuilder onSubmittedBuilder; + + const ObfuscatedTextField({this.onSubmittedBuilder}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 5.0), + child: CupertinoTextField( + decoration: BoxDecoration( + border: Border.all(color: CupertinoColors.black), + borderRadius: const BorderRadius.all(Radius.circular(5.0)), + ), + clearButtonMode: OverlayVisibilityMode.editing, + textAlign: TextAlign.center, + onSubmitted: onSubmittedBuilder(context), + obscureText: true, + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock index 97dcf06..672b612 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -43,6 +43,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.1" + crypt: + dependency: "direct main" + description: + name: crypt + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.7" crypto: dependency: transitive description: @@ -69,6 +76,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_secure_storage: + dependency: "direct main" + description: + name: flutter_secure_storage + url: "https://pub.dartlang.org" + source: hosted + version: "3.2.1+1" flutter_test: dependency: "direct dev" description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index 47dd54e..b79df69 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -28,6 +28,9 @@ dependencies: provider: ^3.0.0 + crypt: ^1.0.7 + flutter_secure_storage: ^3.2.1 + dev_dependencies: flutter_test: sdk: flutter