diff --git a/CHANGELOG.md b/CHANGELOG.md index 77eaadc3d..e089321b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,16 +8,17 @@ x.x.x Release notes (yyyy-MM-dd) ### Enhancements * Added a property `Configuration.disableFormatUpgrade`. When set to `true`, opening a Realm with an older file format will throw an exception to avoid automatically upgrading it. ([#310](https://github.com/realm/realm-dart/pull/310)) -* Support result value from write transaction callbacks. ([#294](https://github.com/realm/realm-dart/pull/294/)) +* Support result value from write transaction callbacks. ([#294](https://github.com/realm/realm-dart/pull/294)) * Added a property `Realm.isInTransaction` that indicates whether the Realm instance has an open write transaction associated with it. -* Support anonymous application credentials. ([#443](https://github.com/realm/realm-dart/pull/443/)) +* Support anonymous application credentials. ([#443](https://github.com/realm/realm-dart/pull/443)) * Added a property `Configuration.initialDataCallback`. This is a callback executed when a Realm file is first created and allows you to populate some initial data necessary for your application. ([#298](https://github.com/realm/realm-dart/issues/298)) -* Support application configuration. ([#306](https://github.com/realm/realm-dart/pull/306/)) -* Support application class. ([#446](https://github.com/realm/realm-dart/pull/446/)) +* Support application configuration. ([#306](https://github.com/realm/realm-dart/pull/306)) +* Support application class. ([#446](https://github.com/realm/realm-dart/pull/446)) * Support should realm compact on open callback `Configuration.shouldCompactCallback` as option when configuring a Realm to determine if it should be compacted before being returned. ([#466](https://github.com/realm/realm-dart/pull/466/)) * Support ObjectId type. ([#468](https://github.com/realm/realm-dart/pull/468)) * Support Uuid type. ([#470](https://github.com/realm/realm-dart/pull/470)) -* Support application login. ([#469](https://github.com/realm/realm-dart/pull/469/)) +* Support application login. ([#469](https://github.com/realm/realm-dart/pull/469)) +* Support user registration with EmailPassword authentication provider. ([#452](https://github.com/realm/realm-dart/pull/452)) ### Fixed * Fixed an issue that would result in the wrong transaction being rolled back if you start a write transaction inside a write transaction. ([#442](https://github.com/realm/realm-dart/issues/442)) diff --git a/flutter/realm_flutter/tests/test_driver/realm_test.dart b/flutter/realm_flutter/tests/test_driver/realm_test.dart index 80ac1f1e4..e81ed6d18 100644 --- a/flutter/realm_flutter/tests/test_driver/realm_test.dart +++ b/flutter/realm_flutter/tests/test_driver/realm_test.dart @@ -13,6 +13,7 @@ import '../test/list_test.dart' as list_tests; import '../test/results_test.dart' as results_tests; import '../test/credentials_test.dart' as credentials_tests; import '../test/application_test.dart' as application_tests; +import '../test/email_password_provider_test.dart' as email_password_provider_test; Future main(List args) async { final Completer completer = Completer(); @@ -25,6 +26,7 @@ Future main(List args) async { await results_tests.main(args); await credentials_tests.main(args); await application_tests.main(args); + await email_password_provider_test.main(args); tearDown(() { if (Invoker.current?.liveTest.state.result == test_api.Result.error || Invoker.current?.liveTest.state.result == test_api.Result.failure) { diff --git a/lib/src/application.dart b/lib/src/application.dart index 180872607..20134c1ff 100644 --- a/lib/src/application.dart +++ b/lib/src/application.dart @@ -24,6 +24,7 @@ import 'user.dart'; import 'configuration.dart'; /// Specify if and how to persists user objects. +/// {@category Application} enum MetadataPersistenceMode { /// Persist [User] objects, but do not encrypt them. plaintext, diff --git a/lib/src/credentials.dart b/lib/src/credentials.dart index d8b892b22..98eec4bc0 100644 --- a/lib/src/credentials.dart +++ b/lib/src/credentials.dart @@ -15,16 +15,18 @@ // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// + import 'native/realm_core.dart'; +import 'application.dart'; /// An enum containing all authentication providers. These have to be enabled manually for the application before they can be used. /// [Authentication Providers Docs](https://docs.mongodb.com/realm/authentication/providers/) /// {@category Application} -enum AuthProvider { - /// Mechanism for authenticating without credentials. +enum AuthProviderType { + /// For authenticating without credentials. anonymous, - /// Mechanism for authenticating with an email and a password. + /// For authenticating with an email and a password. emailPassword, } @@ -33,13 +35,13 @@ enum AuthProvider { class Credentials { late final RealmAppCredentialsHandle _handle; - final AuthProvider provider; + final AuthProviderType provider; /// Returns a [Credentials] object that can be used to authenticate an anonymous user. /// [Anonymous Authentication Docs](https://docs.mongodb.com/realm/authentication/anonymous) Credentials.anonymous() : _handle = realmCore.createAppCredentialsAnonymous(), - provider = AuthProvider.anonymous; + provider = AuthProviderType.anonymous; /// Returns a [Credentials] object that can be used to authenticate a user with their email and password. /// A user can login with email and password only after they have registered their account and verified their @@ -47,10 +49,30 @@ class Credentials { /// [Email/Password Authentication Docs](https://docs.mongodb.com/realm/authentication/email-password) Credentials.emailPassword(String email, String password) : _handle = realmCore.createAppCredentialsEmailPassword(email, password), - provider = AuthProvider.emailPassword; + provider = AuthProviderType.emailPassword; } /// @nodoc extension CredentialsInternal on Credentials { RealmAppCredentialsHandle get handle => _handle; } + +/// A class, encapsulating functionality for users, logged in with [Credentials.emailPassword()]. +/// It is always scoped to a particular app. +/// {@category Application} +class EmailPasswordAuthProvider { + final Application application; + + /// Create a new EmailPasswordAuthProvider for the [application] + EmailPasswordAuthProvider(this.application); + + /// Registers a new user with the given email and password. + /// The [email] to register with. This will be the user's username and, if user confirmation is enabled, this will be the address for + /// the confirmation email. + /// The [password] to associate with the email. The password must be between 6 and 128 characters long. + /// + /// Successful completion indicates that the user has been created on the server and can now be logged in with [Credentials.emailPassword()]. + Future registerUser(String email, String password) async { + return realmCore.appEmailPasswordRegisterUser(application, email, password); + } +} diff --git a/lib/src/native/realm_core.dart b/lib/src/native/realm_core.dart index 41fa77fdd..f0ca6f373 100644 --- a/lib/src/native/realm_core.dart +++ b/lib/src/native/realm_core.dart @@ -56,13 +56,12 @@ class _RealmCore { static _RealmCore? _instance; late final int isolateKey; - late final Pointer)>> _deletePersistentHandlePtr; + late final Pointer)>> _deletePersistentHandleFuncPtr; _RealmCore._() { final lib = initRealm(); _realmLib = RealmLibrary(lib); - - _deletePersistentHandlePtr = lib.lookup)>>('delete_persistent_handle'); + _deletePersistentHandleFuncPtr = lib.lookup)>>('delete_persistent_handle'); } factory _RealmCore() { @@ -886,11 +885,41 @@ class _RealmCore { credentials.handle._pointer, Pointer.fromFunction(_logInCallback), completer.toPersistentHandle(), - _deletePersistentHandlePtr, + _deletePersistentHandleFuncPtr, ), "Login failed"); return completer.future; } + + static void void_completion_callback(Pointer userdata, Pointer error) { + final Completer? completer = userdata.toObject(); + if (completer == null) { + return; + } + + if (error != nullptr) { + final message = error.ref.message.cast().toDartString(); + completer.completeError(RealmException(message)); + return; + } + + completer.complete(); + } + + Future appEmailPasswordRegisterUser(Application application, String email, String password) { + final completer = Completer(); + using((arena) { + _realmLib.invokeGetBool(() => _realmLib.realm_app_email_password_provider_client_register_email( + application.handle._pointer, + email.toUtf8Ptr(arena), + password.toRealmString(arena).ref, + Pointer.fromFunction(void_completion_callback), + completer.toPersistentHandle(), + _deletePersistentHandleFuncPtr, + )); + }); + return completer.future; + } } class LastError { diff --git a/lib/src/realm_class.dart b/lib/src/realm_class.dart index 0d524693b..eee931283 100644 --- a/lib/src/realm_class.dart +++ b/lib/src/realm_class.dart @@ -50,7 +50,7 @@ export 'list.dart' show RealmList, RealmListOfObject, RealmListChanges; export 'realm_object.dart' show RealmEntity, RealmException, RealmObject, RealmObjectChanges; export 'realm_property.dart'; export 'results.dart' show RealmResults, RealmResultsChanges; -export 'credentials.dart' show Credentials, AuthProvider; +export 'credentials.dart' show Credentials, AuthProviderType, EmailPasswordAuthProvider; export 'user.dart' show User; /// A [Realm] instance represents a `Realm` database. diff --git a/test/credentials_test.dart b/test/credentials_test.dart index dc7c98323..48c0d1c2c 100644 --- a/test/credentials_test.dart +++ b/test/credentials_test.dart @@ -29,11 +29,11 @@ Future main([List? args]) async { test('ApplicationCredentials anonymous', () { final credentials = Credentials.anonymous(); - expect(credentials.provider, AuthProvider.anonymous); + expect(credentials.provider, AuthProviderType.anonymous); }); test('ApplicationCredentials email/password', () { final credentials = Credentials.emailPassword("test@email.com", "000000"); - expect(credentials.provider, AuthProvider.emailPassword); + expect(credentials.provider, AuthProviderType.emailPassword); }); } diff --git a/test/email_password_provider_test.dart b/test/email_password_provider_test.dart new file mode 100644 index 000000000..7e9c85c30 --- /dev/null +++ b/test/email_password_provider_test.dart @@ -0,0 +1,28 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2022 Realm Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +//////////////////////////////////////////////////////////////////////////////// + +import 'dart:io'; +import '../lib/realm.dart'; +import 'test.dart'; + +Future main([List? args]) async { + print("Current PID $pid"); + + await setupTests(args); + +}